added missing files
authorMatthias Kramm <kramm@quiss.org>
Thu, 27 May 2010 00:59:54 +0000 (17:59 -0700)
committerMatthias Kramm <kramm@quiss.org>
Thu, 27 May 2010 00:59:54 +0000 (17:59 -0700)
wx/gui/options/viewer.py
wx/lib/__init__.py [new file with mode: 0644]
wx/lib/app.py [new file with mode: 0644]
wx/lib/document.py [new file with mode: 0644]
wx/lib/embeddedimage.py [new file with mode: 0644]
wx/lib/images.py [new file with mode: 0644]
wx/lib/utils.py [new file with mode: 0644]
wx/lib/wordwrap.py [new file with mode: 0644]

index b0e69ac..e6693e6 100644 (file)
@@ -30,6 +30,7 @@ from lib.wordwrap import wordwrap
 from gui.boldstatictext import BoldStaticText
 import viewers
 import gui.fields
+import gui.plugin
 
 import viewers.raw
 import viewers.simple
diff --git a/wx/lib/__init__.py b/wx/lib/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/wx/lib/app.py b/wx/lib/app.py
new file mode 100644 (file)
index 0000000..072b79d
--- /dev/null
@@ -0,0 +1,508 @@
+#!/usr/bin/env python
+# -*- coding: UTF-8 -*-
+#
+# gpdf2swf.py
+# graphical user interface for pdf2swf
+#
+# Part of the swftools package.
+# 
+# Copyright (c) 2008,2009 Matthias Kramm <kramm@quiss.org> 
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+from __future__ import division
+import os
+import wx
+import time
+import pickle
+
+from lib.wordwrap import wordwrap
+from wx.lib.pubsub import Publisher
+
+from document import Document
+from gui.dialogs import (ProgressDialog, OptionsDialog, AboutDialog, InfoDialog)
+from gui.gmain import (PdfFrame,
+                      ID_INVERT_SELECTION, ID_SELECT_ODD,
+                      ID_ONE_PAGE_PER_FILE,
+                      ID_SELECT_EVEN, ID_DOC_INFO,
+                     )
+
+
+def GetDataDir():
+    """
+    Return the standard location on this platform for application data
+    """
+    sp = wx.StandardPaths.Get()
+    return sp.GetUserDataDir()
+
+def GetConfig():
+    if not os.path.exists(GetDataDir()):
+        os.makedirs(GetDataDir())
+
+    config = wx.FileConfig(
+        localFilename=os.path.join(GetDataDir(), "options"))
+    return config
+
+
+class Pdf2Swf:
+    def __init__(self):
+        self.__doc = Document()
+
+        self.__threads = {}
+
+        self.__busy = None
+        self.__progress = None
+
+        self.__can_save = False
+        self.__can_viewinfo = False
+
+        self.view = PdfFrame()
+        wx.GetApp().SetTopWindow(self.view)
+        # Call Show after the current and pending event
+        # handlers have been completed. Otherwise on MSW
+        # we see the frame been draw and after that we saw
+        # the menubar appear
+        wx.CallAfter(self.view.Show)
+
+        self.options = OptionsDialog(self.view)
+        self.__ReadConfigurationFile()
+
+        self.view.toolbar_preview_type.SetSelection(0)
+
+        Publisher.subscribe(self.OnPageChanged, "PAGE_CHANGED")
+        Publisher.subscribe(self.OnFileLoaded, "FILE_LOADED")
+        Publisher.subscribe(self.OnFileNotLoaded, "FILE_NOT_LOADED")
+        Publisher.subscribe(self.OnDiffSizes, "DIFF_SIZES")
+        Publisher.subscribe(self.OnThumbnailAdded, "THUMBNAIL_ADDED")
+        Publisher.subscribe(self.OnThumbnailDone, "THUMBNAIL_DONE")
+        Publisher.subscribe(self.OnProgressBegin, "SWF_BEGIN_SAVE")
+        Publisher.subscribe(self.OnProgressUpdate, "SWF_PAGE_SAVED")
+        Publisher.subscribe(self.OnProgressDone, "SWF_FILE_SAVED")
+        Publisher.subscribe(self.OnCombineError, "SWF_COMBINE_ERROR")
+        Publisher.subscribe(self.OnFileDroped, "FILE_DROPED")
+        Publisher.subscribe(self.OnFilesDroped, "FILES_DROPED")
+        Publisher.subscribe(self.OnPluginOnePagePerFileNotSupported,
+                                    "PLUGIN_ONE_PAGE_PER_FILE_NOT_SUPPORTED")
+        Publisher.subscribe(self.OnPluginError, "PLUGIN_ERROR")
+
+        self.view.Bind(wx.EVT_MENU, self.OnMenuOpen, id=wx.ID_OPEN)
+        self.view.Bind(wx.EVT_MENU, self.OnMenuSave, id=wx.ID_SAVE)
+        self.view.Bind(wx.EVT_MENU, self.OnMenuSaveSelected, id=wx.ID_SAVEAS)
+        self.view.Bind(wx.EVT_MENU, self.OnMenuExit, id=wx.ID_EXIT)
+        self.view.Bind(wx.EVT_MENU_RANGE, self.OnFileHistory,
+                       id=wx.ID_FILE1, id2=wx.ID_FILE9)
+
+        self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=wx.ID_SAVE)
+        self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=wx.ID_SAVEAS)
+
+        self.view.Bind(wx.EVT_MENU, self.OnMenuSelectAll, id=wx.ID_SELECTALL)
+        self.view.Bind(wx.EVT_MENU,
+                       self.OnMenuInvertSelection, id=ID_INVERT_SELECTION)
+        self.view.Bind(wx.EVT_MENU, self.OnMenuSelectOdd, id=ID_SELECT_ODD)
+        self.view.Bind(wx.EVT_MENU, self.OnMenuSelectEven, id=ID_SELECT_EVEN)
+        self.view.Bind(wx.EVT_MENU, self.OnMenuOptions, id=wx.ID_PREFERENCES)
+
+        self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=wx.ID_SELECTALL)
+        self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=ID_INVERT_SELECTION)
+        self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=ID_SELECT_ODD)
+        self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=ID_SELECT_EVEN)
+
+        self.view.Bind(wx.EVT_MENU, self.OnAbout, id=wx.ID_ABOUT)
+
+        self.view.Bind(wx.EVT_MENU, self.OnZoom, id=wx.ID_ZOOM_IN)
+        self.view.Bind(wx.EVT_MENU, self.OnZoom, id=wx.ID_ZOOM_OUT)
+        self.view.Bind(wx.EVT_MENU, self.OnZoom, id=wx.ID_ZOOM_100)
+        self.view.Bind(wx.EVT_MENU, self.OnFit, id=wx.ID_ZOOM_FIT)
+        self.view.Bind(wx.EVT_MENU, self.OnShowDocInfo, id=ID_DOC_INFO)
+
+        self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=wx.ID_ZOOM_IN)
+        self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=wx.ID_ZOOM_OUT)
+        self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=wx.ID_ZOOM_100)
+        self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI, id=wx.ID_ZOOM_FIT)
+        self.view.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUIInfo, id=ID_DOC_INFO)
+
+        self.view.page_list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnSelectItem)
+        self.view.Bind(wx.EVT_CLOSE, self.OnMenuExit)
+
+        self.view.toolbar_preview_type.Bind(wx.EVT_CHOICE,
+                                            self.OnPreviewType)
+
+        # statusbar cancel thumbanails generation button
+        self.view.statusbar.btn_cancel.Bind(wx.EVT_BUTTON,
+                                            self.OnThumbnailCancel)
+
+    def OnFilesDroped(self, evt):
+        dlg = wx.MessageDialog(self.view,
+                      u"You must drop only one file.",
+                      u"Notice",
+                      style=wx.OK, pos=wx.DefaultPosition)
+        dlg.ShowModal()
+        dlg.Destroy()
+
+    def OnFileDroped(self, message):
+        self.__Load(message.data["filename"])
+
+    def OnPluginOnePagePerFileNotSupported(self, message):
+        self.Message(u"Selected viewer does not support "
+                     u"one page per file. ")
+
+    def OnPluginError(self, message):
+        self.Message(u"Error applying selected viewer")
+
+    def OnFileHistory(self, evt):
+        # get the file based on the menu ID
+        fileNum = evt.GetId() - wx.ID_FILE1
+        filename = self.view.filehistory.GetHistoryFile(fileNum)
+
+        self.__Load(filename)
+
+    def OnProgressBegin(self, message):
+        pages = message.data["pages"]
+        style = (
+                 wx.PD_APP_MODAL|wx.PD_ELAPSED_TIME|
+                 wx.PD_REMAINING_TIME|wx.PD_CAN_ABORT|
+                 wx.PD_AUTO_HIDE
+                )
+        self.__progress = ProgressDialog(u"Saving...",
+                                       u"Start saving SWF pages",
+                                       maximum=pages,
+                                       parent=self.view, style=style)
+        self.__progress.Show()
+        self.view.SetStatusText(u"Saving document...")
+
+    def OnProgressUpdate(self, message):
+        pagenr = message.data["pagenr"]
+        pages = message.data["pages"]
+
+        keep_running = self.__progress.Update(
+                                 pagenr,
+                                 u"Saving SWF page %d of %d" % (pagenr, pages)
+                             )
+
+        if not keep_running and self.__threads.has_key("progress"):
+            self.view.SetStatusText(u"Cancelling...")
+            self.__threads.pop("progress").Stop()
+
+
+    def OnProgressDone(self, message):
+        if self.__threads.has_key("progress"): # it goes all the way?
+            self.__threads.pop("progress")
+            self.view.SetStatusText(u"SWF document saved successfully.")
+        else:
+            self.view.SetStatusText(u"")
+
+        self.__progress.Destroy()
+        self.__progress = None
+
+    def OnCombineError(self, message):
+        from wx.lib.dialogs import ScrolledMessageDialog
+        ScrolledMessageDialog(self.view, message.data, u"Notice").ShowModal()
+
+
+    def OnThumbnailAdded(self, message):
+        self.view.statusbar.SetGaugeValue(message.data['pagenr'])
+        tot = self.view.page_list.GetItemCount()
+        self.view.SetStatusText(u"Generating thumbnails %s/%d" %
+                                (message.data['pagenr'], tot), 0)
+
+    def OnThumbnailDone(self, message):
+        self.view.statusbar.SetGaugeValue(0)
+        self.view.SetStatusText(u"", 0)
+        if self.__threads.has_key("thumbnails"):
+            self.__threads.pop("thumbnails")
+        self.view.SendSizeEvent()
+
+    def OnThumbnailCancel(self, event):
+        if self.__threads.has_key("thumbnails"):
+            self.__threads["thumbnails"].Stop()
+
+    def OnSelectItem(self, event):
+        self.__doc.ChangePage(event.GetIndex() + 1)
+
+    def OnPreviewType(self, event):
+        filename = self.__doc.filename
+        if filename:
+            self.__Load(filename)
+
+    def OnPageChanged(self, message):
+        # ignore if we have more than one item selected
+        if self.view.page_list.GetSelectedItemCount() > 1:
+            return
+
+        self.view.page_preview.DisplayPage(message.data)
+
+    def SetTitle(self):
+        name = wx.GetApp().GetAppName()
+        filename = os.path.basename(self.__doc.filename)
+        if self.__doc.title != "n/a":
+            t = "%s - %s (%s)" % (name, filename, self.__doc.title)
+        else:
+            t = "%s - %s" % (name, filename)
+        self.view.SetTitle(t)
+
+    def OnFileLoaded(self, message):
+        if self.__progress:
+            self.__progress.Destroy()
+            self.__progress = None
+
+        self.__can_viewinfo = True
+        del self.__busy
+
+        self.SetTitle()
+
+        if self.__doc.oktocopy == 'no':
+            self.__can_save = False
+            self.view.page_list.DisplayEmptyThumbnails(0)
+            self.view.page_preview.Clear()
+            self.view.SetStatusText(u"")
+            self.Message(
+                    u"This PDF disallows copying, cannot be converted."
+                    )
+            return
+
+        #if not self.__doc.oktoprint:
+        self.view.SetStatusText(u"Document loaded successfully.")
+
+        self.view.page_list.DisplayEmptyThumbnails(message.data["pages"])
+        thumbs = self.__doc.GetThumbnails()
+        t = self.view.page_list.DisplayThumbnails(thumbs)
+        self.__threads["thumbnails"] = t
+        self.view.statusbar.SetGaugeRange(message.data["pages"])
+        #del self.__busy
+
+    def OnFileNotLoaded(self, message):
+        self.__can_save = False
+        self.__can_viewinfo = False
+        del self.__busy
+        self.view.SetStatusText(u"")
+        self.Message(
+                    u"Could not open file %s" % message.data['filename']
+                    )
+        
+    def OnDiffSizes(self, message):
+        # just let the user know- for now, we can't handle this properly
+        self.Message(
+                    u"In this PDF, width or height are not the same for "
+                    u"each page. This might cause problems if you export "
+                    u"pages of different dimensions into the same SWF file."
+                    )
+
+    def OnMenuOpen(self, event):
+        dlg = wx.FileDialog(self.view, u"Choose PDF File:",
+                     style=wx.OPEN|wx.CHANGE_DIR,
+                     wildcard = u"PDF files (*.pdf)|*.pdf|all files (*.*)|*.*")
+
+        if dlg.ShowModal() == wx.ID_OK:
+            filename = dlg.GetPath()
+            self.__Load(filename)
+
+    def OnMenuSave(self, event, pages=None):
+        defaultFile = self.__doc.lastsavefile
+        if "wxMSW" in wx.PlatformInfo:
+            allFiles = "*.*"
+        else:
+            allFiles = "*"
+        self.view.SetStatusText(u"")
+        dlg = wx.FileDialog(self.view, u"Choose Save Filename:",
+                       style = wx.SAVE | wx.OVERWRITE_PROMPT,
+                       defaultFile=os.path.basename(defaultFile),
+                       wildcard=u"SWF files (*.swf)|*.swf"
+                                 "|all files (%s)|%s" % (allFiles, allFiles))
+
+
+        if dlg.ShowModal() == wx.ID_OK:
+            menubar = self.view.GetMenuBar()
+            one_file_per_page = menubar.IsChecked(ID_ONE_PAGE_PER_FILE)
+
+            self.__threads["progress"] = self.__doc.SaveSWF(dlg.GetPath(),
+                                                         one_file_per_page,
+                                                         pages, self.options)
+
+    def OnUpdateUI(self, event):
+        menubar = self.view.GetMenuBar()
+        menubar.Enable(event.GetId(), self.__can_save)
+
+        self.view.GetToolBar().EnableTool(event.GetId(), self.__can_save)
+
+    def OnUpdateUIInfo(self, event):
+        menubar = self.view.GetMenuBar()
+        menubar.Enable(event.GetId(), self.__can_viewinfo)
+
+        self.view.GetToolBar().EnableTool(event.GetId(), self.__can_viewinfo)
+
+    def OnMenuSaveSelected(self, event):
+        pages = []
+        page = self.view.page_list.GetFirstSelected()
+        pages.append(page+1)
+
+        while True:
+            page = self.view.page_list.GetNextSelected(page)
+            if page == -1:
+                break
+            pages.append(page+1)
+
+        self.OnMenuSave(event, pages)
+
+    def OnMenuExit(self, event):
+        self.view.SetStatusText(u"Cleaning up...")
+
+        # Stop any running thread
+        self.__StopThreads()
+
+        config = GetConfig()
+        self.view.filehistory.Save(config)
+        config.Flush()
+        # A little extra cleanup is required for the FileHistory control
+        del self.view.filehistory
+
+        # Save quality options
+        dirpath = GetDataDir()
+        data = self.options.quality_panel.pickle()
+        try:
+            f = file(os.path.join(dirpath, 'quality.pkl'), 'wb')
+            pickle.dump(data, f)
+            f.close()
+        except Exception:
+            pass
+
+        # Save viewer options
+        try:
+            f = file(os.path.join(dirpath, 'viewers.pkl'), 'wb')
+            data = self.options.viewers_panel.pickle()
+            pickle.dump(data, f)
+            f.close()
+        except Exception:
+            pass
+
+        self.view.Destroy()
+
+    def OnMenuSelectAll(self, event):
+        for i in range(0, self.view.page_list.GetItemCount()):
+            self.view.page_list.Select(i, True)
+
+    def OnMenuInvertSelection(self, event):
+        for i in range(0, self.view.page_list.GetItemCount()):
+            self.view.page_list.Select(i, not self.view.page_list.IsSelected(i))
+
+    def OnMenuSelectOdd(self, event):
+        for i in range(0, self.view.page_list.GetItemCount()):
+            self.view.page_list.Select(i, not bool(i%2))
+
+    def OnMenuSelectEven(self, event):
+        for i in range(0, self.view.page_list.GetItemCount()):
+            self.view.page_list.Select(i, bool(i%2))
+
+    def OnMenuOptions(self, event):
+        self.options.ShowModal()
+
+    def OnFit(self, event):
+        self.__doc.Fit(self.view.page_preview.GetClientSize())
+
+    def OnZoom(self, event):
+        zoom = {
+              wx.ID_ZOOM_IN: .1,
+              wx.ID_ZOOM_OUT: -.1,
+              wx.ID_ZOOM_100: 1,
+        }
+        self.__doc.Zoom(zoom[event.GetId()])
+
+    def OnShowDocInfo(self, event):
+        info = InfoDialog(self.view)
+        info.info.display(self.__doc)
+        info.Show()
+
+    def OnAbout(self, evt):
+        AboutDialog(self.view)
+
+    def __Load(self, filename):
+        self.__can_save = True
+        self.__StopThreads()
+        self.view.SetStatusText(u"Loading document...")
+        self.__busy = wx.BusyInfo(u"One moment please, "
+                                  u"opening pdf document...")
+
+        self.view.filehistory.AddFileToHistory(filename)
+        try:
+            # I dont care if this, for some reason,
+            # give some error. I just swallow it
+            os.chdir(os.path.dirname(filename))
+        except:
+            pass
+
+        # Need to delay the file load a little bit
+        # for the BusyInfo get a change to repaint itself
+        #wx.FutureCall(150, self.__doc.Load, filename)
+        sel = self.view.toolbar_preview_type.GetSelection()
+        #print sel
+        PREV_TYPE = {
+            0 : [('bitmap', '1'), ('poly2bitmap', '0'), ('bitmapfonts', '1'),],
+            1 : [('bitmap', '0'), ('poly2bitmap', '1'), ('bitmapfonts', '0'),],
+            2 : [('bitmap', '0'), ('poly2bitmap', '0'), ('bitmapfonts', '0'),],
+        }
+        self.__doc.preview_parameters = PREV_TYPE[sel]
+        wx.CallAfter(self.__doc.Load, filename)
+
+    def __StopThreads(self):
+        for n, t in self.__threads.items():
+            t.Stop()
+
+        running = True
+        while running:
+            running = False
+            for n, t in self.__threads.items():
+                running = running + t.IsRunning()
+            time.sleep(0.1)
+
+    def __ReadConfigurationFile(self):
+        config = GetConfig()
+        self.view.filehistory.Load(config)
+
+        dirpath = GetDataDir()
+        try:
+            f = file(os.path.join(dirpath, 'quality.pkl'), 'rb')
+            #try:
+            if 1:
+                data = pickle.load(f)
+                self.options.quality_panel.unpickle(data)
+            #except:
+            #    self.Message(
+            #          u"Error loading quality settings. "
+            #          u"They will be reset to defaults. ")
+            f.close()
+        except Exception:
+            pass
+
+        try:
+            f = file(os.path.join(dirpath, 'viewers.pkl'), 'rb')
+            #try:
+            if 1:
+                data = pickle.load(f)
+                self.options.viewers_panel.unpickle(data)
+            #except:
+            #    self.Message(
+            #          u"Error loading viewers settings. "
+            #          u"They will be reset to defaults. ")
+            f.close()
+        except Exception:
+            pass
+        #d = pickle.load(f)
+
+    def Message(self, message):
+        dlg = wx.MessageDialog(self.view,
+                      message,
+                      style=wx.OK, pos=wx.DefaultPosition)
+        dlg.ShowModal()
+        dlg.Destroy()
+
diff --git a/wx/lib/document.py b/wx/lib/document.py
new file mode 100644 (file)
index 0000000..cb9b00d
--- /dev/null
@@ -0,0 +1,247 @@
+#!/usr/bin/env python
+# -*- coding: UTF-8 -*-
+#
+# gpdf2swf.py
+# graphical user interface for pdf2swf
+#
+# Part of the swftools package.
+# 
+# Copyright (c) 2008,2009 Matthias Kramm <kramm@quiss.org> 
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+from __future__ import division
+import os
+import time
+import thread
+import gfx
+import wx
+from wx.lib.pubsub import Publisher
+from subprocess import Popen, PIPE
+
+class _SaveSWFThread:
+    def __init__(self, pdffilename, filename, one_page_per_file, doc, pages, options):
+        #self.__doc = doc
+        self.__filename = filename
+        self.__pdffilename = pdffilename
+        self.__pages = pages or range(1, doc.pages+1)
+        self.__options = options
+        self.__one_page_per_file = one_page_per_file
+
+    def Start(self):
+        self.__keep_running = self.__running = True
+        thread.start_new_thread(self.Run, ())
+        #self.Run()
+
+    def Stop(self):
+        self.__keep_running = False
+
+    def IsRunning(self):
+        return self.__running
+
+    def Run(self):
+        swf = gfx.SWF()
+
+        try:
+            plugin = self.__options.viewers.init(swf, self.__filename)
+        except Exception, e:
+            wx.CallAfter(Publisher.sendMessage, "PLUGIN_ERROR")
+            self.__running = False
+            return
+
+        if self.__one_page_per_file and not plugin.one_page_per_file:
+            wx.CallAfter(Publisher.sendMessage,
+                         "PLUGIN_ONE_PAGE_PER_FILE_NOT_SUPPORTED",
+                         {'plugin': plugin,})
+            self.__running = False
+            return
+
+        pages = len(self.__pages)
+        wx.CallAfter(Publisher.sendMessage, "SWF_BEGIN_SAVE",
+                                            {'pages': pages,})
+
+        time.sleep(0.05)
+
+        self.setparameters(gfx)
+        self.__doc = gfx.open("pdf", self.__pdffilename)
+
+        self.setparameters(swf)
+
+        try:
+            plugin.before_render()
+        except Exception, e:
+            wx.CallAfter(Publisher.sendMessage, "PLUGIN_ERROR")
+            self.__running = False
+            return
+
+        for pagenr in self.__pages:
+            page = self.__doc.getPage(pagenr)
+            swf.startpage(page.width, page.height)
+            page.render(swf)
+            swf.endpage()
+            wx.CallAfter(Publisher.sendMessage, "SWF_PAGE_SAVED",
+                                                {'pagenr': pagenr,
+                                                 'pages': pages,})
+            time.sleep(0.05)
+
+            if self.__one_page_per_file:
+                form = '.%%0%dd.swf' % len(str(len(self.__pages)))
+                filename = self.__filename.replace('.swf', form % pagenr)
+                swf.save(filename)
+                swf = gfx.SWF()
+                self.setparameters(swf)
+
+            if not self.__keep_running:
+                break
+        else:
+            # This will not run if we break the for loop
+            if not self.__one_page_per_file:
+                try:
+                    plugin.before_save(page)
+                except Exception, e:
+                    wx.CallAfter(Publisher.sendMessage, "PLUGIN_ERROR")
+                    self.__running = False
+                    return
+                swf.save(self.__filename)
+                try:
+                    plugin.after_save(page)
+                except Exception, e:
+                    wx.CallAfter(Publisher.sendMessage, "PLUGIN_ERROR")
+                    self.__running = False
+                    return
+
+        # No need. But to be sure that it's clean up
+        # as soon as possible
+        del swf
+
+        wx.CallAfter(Publisher.sendMessage, "SWF_FILE_SAVED")
+        time.sleep(0.05)
+
+        self.__running = False
+
+    def setparameters(self, swf):
+        #print "driver", swf
+        for opt in self.__options.quality:
+            if type(opt.name) in (tuple, list):
+                for name, value in (
+                    # Example to better understand the list comprehension:
+                    # opt.name = ['a', 'b', 'c']
+                    # opt.value = [1, 2, 3]
+                    # zip them = [('a',1), ('b', 2), ('c', 3)]
+                    # pair will be in this example ('a', 1) due to
+                    # the if pair[1] condition
+                    pair for pair in zip(opt.name, opt.value) #if pair[1] == 1
+                ):
+                    #print "1.swf.setparameter(%s, %s)" % (name, value)
+                    swf.setparameter(str(name), str(value))
+            else:
+                #print "2.swf.setparameter(%s, %s)" % (opt.name, str(opt.value))
+                swf.setparameter(opt.name, str(opt.value))
+
+        #swf.setparameter('noclips', '1')
+        #swf.setparameter('reordertags', '1')
+        #swf.setparameter('animate', '1')
+
+PDF_INFO = [
+    "title", "subject", "keywords", "author",
+    "creator", "producer", "creationdate", "moddate",
+    "linearized", "tagged", "encrypted", "oktoprint",
+    "oktocopy", "oktochange", "oktoaddnotes", "version",
+]
+
+class Document(object):
+    def __init__(self):
+        self.__page = None
+        self.__zoom = 1
+        self.__lastsavefile = "output.swf"
+        self.__pdffilename = None
+        self.__preview_parameters = []
+
+    def __getattr__(self, name):
+        if name in PDF_INFO:
+            return self.__pdf.getInfo(name) or "n/a"
+        raise AttributeError, name
+
+    def filename(self):
+        return self.__pdffilename
+    filename = property(filename)
+
+    def __get_lastsavefile(self):
+        return self.__lastsavefile
+    def __set_lastsavefile(self, lastsavefile):
+        self.__lastsavefile = lastsavefile
+    lastsavefile = property(__get_lastsavefile, __set_lastsavefile)
+
+    def __SwapExtension(self, filename, newext):
+        basename, ext = os.path.splitext(filename)
+        return "%s.%s" % (basename, newext)
+
+    def __Reload(self):
+        Publisher.sendMessage("PAGE_CHANGED",
+                              {'page': self.__page,
+                               'width': int(self.__page.width * self.__zoom),
+                               'height': int(self.__page.height * self.__zoom)})
+
+    def __get_preview_parameters(self):
+        return self.__preview_parameters
+    def __set_preview_parameters(self, parameters):
+        self.__preview_parameters = parameters
+    preview_parameters = property(__get_preview_parameters,
+                                  __set_preview_parameters)
+
+    def Load(self, filename):
+        self.__lastsavefile = self.__SwapExtension(filename, "swf")
+        self.__pdffilename = filename
+
+        #print 'Load',self.__preview_parameters
+        for parameter, value in self.__preview_parameters:
+            gfx.setparameter(parameter, value)
+
+        try:
+            self.__pdf = gfx.open("pdf", filename)
+        except:
+            Publisher.sendMessage("FILE_NOT_LOADED", {'filename': filename})
+        else:
+            Publisher.sendMessage("FILE_LOADED", {'pages': self.__pdf.pages})
+
+    def ChangePage(self, pagenr=1, size=None):
+        self.__page = page = self.__pdf.getPage(pagenr)
+        self.__Reload()
+
+    def Fit(self, size):
+        w = size[0] / self.__page.width
+        h = size[1] / self.__page.height
+        self.__zoom = min(w, h)
+        self.__Reload()
+
+    def Zoom(self, zoom):
+        if zoom == 1:
+            self.__zoom = 1
+        else:
+            self.__zoom += zoom
+        self.__Reload()
+
+    def GetThumbnails(self):
+        for pagenr in range(1, self.__pdf.pages + 1):
+            page = self.__pdf.getPage(pagenr)
+            yield page
+
+    def SaveSWF(self, filename, one_page_per_file, pages, options):
+        self.__lastsavefile = filename
+        t = _SaveSWFThread(self.__pdffilename, filename, one_page_per_file, self.__pdf, pages, options)
+        t.Start()
+        return t
+        
+
diff --git a/wx/lib/embeddedimage.py b/wx/lib/embeddedimage.py
new file mode 100644 (file)
index 0000000..5b3cad4
--- /dev/null
@@ -0,0 +1,74 @@
+#----------------------------------------------------------------------
+# Name:        wx.lib.embeddedimage
+# Purpose:     Defines a class used for embedding PNG images in Python
+#              code. The primary method of using this module is via
+#              the code generator in wx.tools.img2py.
+#
+# Author:      Anthony Tuininga
+#
+# Created:     26-Nov-2007
+# RCS-ID:      $Id: embeddedimage.py 59672 2009-03-20 20:59:42Z RD $
+# Copyright:   (c) 2007 by Anthony Tuininga
+# Licence:     wxWindows license
+#----------------------------------------------------------------------
+
+import base64
+import cStringIO
+import wx
+
+try:
+    b64decode = base64.b64decode
+except AttributeError:
+    b64decode = base64.decodestring
+    
+
+class PyEmbeddedImage(object):
+    """
+    PyEmbeddedImage is primarily intended to be used by code generated
+    by img2py as a means of embedding image data in a python module so
+    the image can be used at runtime without needing to access the
+    image from an image file.  This makes distributing icons and such
+    that an application uses simpler since tools like py2exe will
+    automatically bundle modules that are imported, and the
+    application doesn't have to worry about how to locate the image
+    files on the user's filesystem.
+
+    The class can also be used for image data that may be acquired
+    from some other source at runtime, such as over the network or
+    from a database.  In this case pass False for isBase64 (unless the
+    data actually is base64 encoded.)  Any image type that
+    wx.ImageFromStream can handle should be okay.
+    """
+
+    def __init__(self, data, isBase64=True):
+        self.data = data
+        self.isBase64 = isBase64
+
+    def GetBitmap(self):
+        return wx.BitmapFromImage(self.GetImage())
+
+    def GetData(self):
+        if self.isBase64:
+            data = b64decode(self.data)
+        return data
+
+    def GetIcon(self):
+        icon = wx.EmptyIcon()
+        icon.CopyFromBitmap(self.GetBitmap())
+        return icon
+
+    def GetImage(self):
+        stream = cStringIO.StringIO(self.GetData())
+        return wx.ImageFromStream(stream)
+
+    # added for backwards compatibility
+    getBitmap = GetBitmap
+    getData = GetData
+    getIcon = GetIcon
+    getImage = GetImage
+
+    # define properties, for convenience
+    Bitmap = property(GetBitmap)
+    Icon = property(GetIcon)
+    Image = property(GetImage)
+
diff --git a/wx/lib/images.py b/wx/lib/images.py
new file mode 100644 (file)
index 0000000..7b34b74
--- /dev/null
@@ -0,0 +1,107 @@
+#----------------------------------------------------------------------
+# This file was generated by /usr/bin/img2py
+#
+from embeddedimage import PyEmbeddedImage
+
+blank = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAAZiS0dE"
+    "AP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kHDAscKjCK/4UAAABo"
+    "SURBVHja7dABAcBAEAIgXf/Ofo8dRKDblqPa5stxAgQIECBAgAABAgQIECBAgAABAgQIECBA"
+    "gAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIECBAgAABAgQIEPALTbLLAQ8OIAV9"
+    "8WNeKwAAAABJRU5ErkJggg==")
+getblankData = blank.GetData
+getblankImage = blank.GetImage
+getblankBitmap = blank.GetBitmap
+getblankIcon = blank.GetIcon
+
+#----------------------------------------------------------------------
+stop = PyEmbeddedImage(
+    "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAAXNSR0IArs4c6QAAAAZiS0dE"
+    "AP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9kHGAEINtjJMYEAAADV"
+    "SURBVBjTPc8xTgJBAEbh988sLFkDXEAvgB338AhauI3EUqOVVhR6gq0s9Do2WpDVipaOLCZL"
+    "LGZ+G+IJ3ve0KssBUo3UWAJANtgL7JcCqXZVNcxmKXVdBBMn06S2bdT3xOvh8D3P50nPTyF2"
+    "O3xyjG9vpI/PrM3mrHAI/PZ9PHp79Xi5VAZ+7u7t7TY6BAI2g/2ehDAgBXSwABTKmWIyTr44"
+    "D93Do0FQX6Kv76T1Omo1Gl25qhqdzlLqdtE5E6fTpLaN6vuFVmU5kFQTQmMdMjn/b/4BTeBh"
+    "NrAp1ecAAAAASUVORK5CYII=")
+getstopData = stop.GetData
+getstopImage = stop.GetImage
+getstopBitmap = stop.GetBitmap
+getstopIcon = stop.GetIcon
+
+#----------------------------------------------------------------------
+gpdf2swf = PyEmbeddedImage(
+    "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"
+    "fQAAAABJRU5ErkJggg==")
+getgpdf2swfData = gpdf2swf.GetData
+getgpdf2swfImage = gpdf2swf.GetImage
+getgpdf2swfBitmap = gpdf2swf.GetBitmap
+getgpdf2swfIcon = gpdf2swf.GetIcon
+
diff --git a/wx/lib/utils.py b/wx/lib/utils.py
new file mode 100644 (file)
index 0000000..75e80ff
--- /dev/null
@@ -0,0 +1,55 @@
+import wx
+
+def blank_bmp(w, h, color="white"):
+    bmp = wx.EmptyBitmap(max(w,1) , max(h,1))
+    clear_bmp(bmp, color)
+    return bmp
+
+def clear_bmp(bmp, color):
+    dc = wx.MemoryDC()
+    dc.SelectObject(bmp)
+    dc.SetBackground(wx.Brush(color))
+    dc.Clear()
+
+
+# Taken and adapt from django.utils.encoding
+class GPdf2SwfUnicodeDecodeError(UnicodeDecodeError):
+    def __init__(self, obj, *args):
+        self.obj = obj
+        UnicodeDecodeError.__init__(self, *args)
+
+    def __str__(self):
+        original = UnicodeDecodeError.__str__(self)
+        return '%s. You passed in %r (%s)' % (original, self.obj,
+                type(self.obj))
+
+def force_unicode(s, encoding='utf-8', strings_only=False, errors='strict'):
+    try:
+        if not isinstance(s, basestring,):
+            if hasattr(s, '__unicode__'):
+                s = unicode(s)
+            else:
+                try:
+                    s = unicode(str(s), encoding, errors)
+                except UnicodeEncodeError:
+                    if not isinstance(s, Exception):
+                        raise
+                    # If we get to here, the caller has passed in an Exception
+                    # subclass populated with non-ASCII data without special
+                    # handling to display as a string. We need to handle this
+                    # without raising a further exception. We do an
+                    # approximation to what the Exception's standard str()
+                    # output should be.
+                    s = ' '.join([force_unicode(arg, encoding, strings_only,
+                            errors) for arg in s])
+        elif not isinstance(s, unicode):
+            # Note: We use .decode() here, instead of unicode(s, encoding,
+            # errors), so that if s is a SafeString, it ends up being a
+            # SafeUnicode at the end.
+            s = s.decode(encoding, errors)
+    except UnicodeDecodeError, e:
+        raise GPdf2SwfUnicodeDecodeError(s, *e.args)
+        #raise UnicodeDecodeError(*e.args)
+    return s
+
+
diff --git a/wx/lib/wordwrap.py b/wx/lib/wordwrap.py
new file mode 100644 (file)
index 0000000..4b1a5f8
--- /dev/null
@@ -0,0 +1,97 @@
+#----------------------------------------------------------------------
+# Name:        wx.lib.wordwrap
+# Purpose:     Contains a function to aid in word-wrapping some text
+#
+# Author:      Robin Dunn
+#
+# Created:     15-Oct-2006
+# RCS-ID:      $Id: wordwrap.py 54718 2008-07-19 18:57:26Z RD $
+# Copyright:   (c) 2006 by Total Control Software
+# Licence:     wxWindows license
+#----------------------------------------------------------------------
+
+def wordwrap(text, width, dc, breakLongWords=True, margin=0):
+    """
+    Returns a copy of text with newline characters inserted where long
+    lines should be broken such that they will fit within the given
+    width, with the given margin left and right, on the given `wx.DC`
+    using its current font settings.  By default words that are wider
+    than the margin-adjusted width will be broken at the nearest
+    character boundary, but this can be disabled by passing ``False``
+    for the ``breakLongWords`` parameter.
+    """
+
+    wrapped_lines = []
+    text = text.split('\n')
+    for line in text:
+        pte = dc.GetPartialTextExtents(line)        
+        wid = ( width - (2*margin+1)*dc.GetTextExtent(' ')[0] 
+              - max([0] + [pte[i]-pte[i-1] for i in range(1,len(pte))]) )
+        idx = 0
+        start = 0
+        startIdx = 0
+        spcIdx = -1
+        while idx < len(pte):
+            # remember the last seen space
+            if line[idx] == ' ':
+                spcIdx = idx
+
+            # have we reached the max width?
+            if pte[idx] - start > wid and (spcIdx != -1 or breakLongWords):
+                if spcIdx != -1:
+                    idx = spcIdx + 1
+                wrapped_lines.append(' '*margin + line[startIdx : idx] + ' '*margin)
+                start = pte[idx]
+                startIdx = idx
+                spcIdx = -1
+
+            idx += 1
+
+        wrapped_lines.append(' '*margin + line[startIdx : idx] + ' '*margin)
+
+    return '\n'.join(wrapped_lines)
+
+
+
+if __name__ == '__main__':
+    import wx
+    class TestPanel(wx.Panel):
+        def __init__(self, parent):
+            wx.Panel.__init__(self, parent)
+
+            self.tc = wx.TextCtrl(self, -1, "", (20,20), (150,150), wx.TE_MULTILINE)
+            self.Bind(wx.EVT_TEXT, self.OnDoUpdate, self.tc)
+            self.Bind(wx.EVT_SIZE, self.OnSize)
+            
+
+        def OnSize(self, evt):
+            wx.CallAfter(self.OnDoUpdate, None)
+            
+            
+        def OnDoUpdate(self, evt):
+            WIDTH = self.GetSize().width - 220
+            HEIGHT = 200
+            bmp = wx.EmptyBitmap(WIDTH, HEIGHT)
+            mdc = wx.MemoryDC(bmp)
+            mdc.SetBackground(wx.Brush("white"))
+            mdc.Clear()
+            mdc.SetPen(wx.Pen("black"))
+            mdc.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.NORMAL))
+            mdc.DrawRectangle(0,0, WIDTH, HEIGHT)
+
+            text = wordwrap(self.tc.GetValue(), WIDTH-2, mdc, False)
+            #print repr(text)
+            mdc.DrawLabel(text, (1,1, WIDTH-2, HEIGHT-2))
+
+            del mdc
+            dc = wx.ClientDC(self)
+            dc.DrawBitmap(bmp, 200, 20)
+
+
+    app = wx.App(False)
+    frm = wx.Frame(None, title="Test wordWrap")
+    pnl = TestPanel(frm)
+    frm.Show()
+    app.MainLoop()
+
+