]> xmof Git - DeDRM.git/commitdiff
tools v1.0
authorApprentice Alf <apprenticealf@gmail.com>
Fri, 13 Feb 2009 20:59:59 +0000 (20:59 +0000)
committerApprentice Alf <apprenticealf@gmail.com>
Sat, 28 Feb 2015 14:14:39 +0000 (14:14 +0000)
(With some additions)
Lots of authors, brought together by Apprentice Alf.

26 files changed:
Kindle_Mobi_Tools/lib/mobidedrm.py
Topaz_Tools/TopazExtract_Kindle4PC.pyw [new file with mode: 0644]
Topaz_Tools/TopazExtract_Kindle_iPhone.pyw [new file with mode: 0644]
Topaz_Tools/TopazFiles2HTML.pyw [new file with mode: 0644]
Topaz_Tools/TopazFiles2SVG.pyw [new file with mode: 0644]
Topaz_Tools/TopazFiles2XML.pyw [new file with mode: 0644]
Topaz_Tools/lib/cmbtc_dump.py
Topaz_Tools/lib/cmbtc_dump_nonK4PC.py
Topaz_Tools/lib/convert2xml.py
Topaz_Tools/lib/decode_meta.py
Topaz_Tools/lib/flatxml2html.py
Topaz_Tools/lib/genhtml.py
Topaz_Tools/lib/gensvg.py
Topaz_Tools/lib/genxml.py
Topaz_Tools/lib/getpagedim.py
Topaz_Tools/lib/scrolltextwidget.py [new file with mode: 0644]
Topaz_Tools/lib/stylexml2css.py
Topaz_Tools/lib/subasyncio.py [new file with mode: 0644]
Topaz_Tools/lib/topaz-changes.txt [new file with mode: 0644]
Topaz_Tools/lib/topaz-readme.txt [moved from Topaz_Tools/lib/readme.txt with 100% similarity]
eReader_Tools/Pml2HTML.pyw [new file with mode: 0644]
eReader_Tools/eReaderPDB2PML.pyw [new file with mode: 0644]
eReader_Tools/lib/erdr2pml.py
eReader_Tools/lib/scrolltextwidget.py [new file with mode: 0644]
eReader_Tools/lib/subasyncio.py [new file with mode: 0644]
eReader_Tools/lib/xpml2xhtml.py

index 4ec5b75981ade8c7ff26abf24e7fbbe476bada5a..178b046f8f4f121846fa397a547d156a56e42ced 100644 (file)
 #  0.07 - The extra data flags aren't present in MOBI header < 0xE8 in size
 #  0.08 - ...and also not in Mobi header version < 6
 #  0.09 - ...but they are there with Mobi header version 6, header size 0xE4!
-#  0.10 - use autoflushed stdout and proper return values
+#  0.10 - Outputs unencrypted files as-is, so that when run as a Calibre
+#         import filter it works when importing unencrypted files.
+#         Also now handles encrypted files that don't need a specific PID.
+#  0.11 - use autoflushed stdout and proper return values
 
 class Unbuffered:
     def __init__(self, stream):
@@ -143,6 +146,17 @@ class DrmStripper:
                        if verification == ver and cksum == temp_key_sum and (flags & 0x1F) == 1:
                                found_key = finalkey
                                break
+               if not found_key:
+                       # Then try the default encoding that doesn't require a PID
+                       temp_key = keyvec1
+                       temp_key_sum = sum(map(ord,temp_key)) & 0xff
+                       for i in xrange(count):
+                               verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
+                               cookie = PC1(temp_key, cookie)
+                               ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie)
+                               if verification == ver and cksum == temp_key_sum:
+                                       found_key = finalkey
+                                       break
                return found_key                
 
 
@@ -178,34 +192,35 @@ class DrmStripper:
 
                crypto_type, = struct.unpack('>H', sect[0xC:0xC+2])
                if crypto_type == 0:
-                       raise DrmException("it seems that this book isn't encrypted")
-               if crypto_type == 1:
-                       raise DrmException("cannot decode Mobipocket encryption type 1")
-               if crypto_type != 2:
-                       raise DrmException("unknown encryption type: %d" % crypto_type)
-
-               # calculate the keys
-               drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', sect[0xA8:0xA8+16])
-               if drm_count == 0:
-                       raise DrmException("no PIDs found in this file")
-               found_key = self.parseDRM(sect[drm_ptr:drm_ptr+drm_size], drm_count, pid)
-               if not found_key:
-                       raise DrmException("no key found. maybe the PID is incorrect")
-
-               # kill the drm keys
-               self.patchSection(0, "\0" * drm_size, drm_ptr)
-               # kill the drm pointers
-               self.patchSection(0, "\xff" * 4 + "\0" * 12, 0xA8)
-               # clear the crypto type
-               self.patchSection(0, "\0" * 2, 0xC)
-
-               # decrypt sections
-               print "Decrypting. Please wait...",
-               for i in xrange(1, records+1):
-                       data = self.loadSection(i)
-                       extra_size = getSizeOfTrailingDataEntries(data, len(data), extra_data_flags)
-                       # print "record %d, extra_size %d" %(i,extra_size)
-                       self.patchSection(i, PC1(found_key, data[0:len(data) - extra_size]))
+                       print "This book is not encrypted."
+               else:
+                       if crypto_type == 1:
+                               raise DrmException("cannot decode Mobipocket encryption type 1")
+                       if crypto_type != 2:
+                               raise DrmException("unknown encryption type: %d" % crypto_type)
+       
+                       # calculate the keys
+                       drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', sect[0xA8:0xA8+16])
+                       if drm_count == 0:
+                               raise DrmException("no PIDs found in this file")
+                       found_key = self.parseDRM(sect[drm_ptr:drm_ptr+drm_size], drm_count, pid)
+                       if not found_key:
+                               raise DrmException("no key found. maybe the PID is incorrect")
+       
+                       # kill the drm keys
+                       self.patchSection(0, "\0" * drm_size, drm_ptr)
+                       # kill the drm pointers
+                       self.patchSection(0, "\xff" * 4 + "\0" * 12, 0xA8)
+                       # clear the crypto type
+                       self.patchSection(0, "\0" * 2, 0xC)
+       
+                       # decrypt sections
+                       print "Decrypting. Please wait...",
+                       for i in xrange(1, records+1):
+                               data = self.loadSection(i)
+                               extra_size = getSizeOfTrailingDataEntries(data, len(data), extra_data_flags)
+                               # print "record %d, extra_size %d" %(i,extra_size)
+                               self.patchSection(i, PC1(found_key, data[0:len(data) - extra_size]))
                print "done"
        def getResult(self):
                return self.data_file
@@ -219,7 +234,7 @@ if not __name__ == "__main__":
                description         = 'Removes DRM from secure Mobi files'
                supported_platforms = ['linux', 'osx', 'windows'] # Platforms this plugin will run on
                author              = 'The Dark Reverser' # The author of this plugin
-               version             = (0, 0, 10)   # The version number of this plugin
+               version             = (0, 1, 0)   # The version number of this plugin
                file_types          = set(['prc','mobi','azw']) # The file types that this plugin will be applied to
                on_import           = True # Run this plugin during the import
 
@@ -245,25 +260,22 @@ if not __name__ == "__main__":
                def customization_help(self, gui=False):
                        return 'Enter PID (separate multiple PIDs with comma)'
 
-def main(argv=sys.argv):
-       print "MobiDeDrm v0.10. Copyright (c) 2008 The Dark Reverser"
+if __name__ == "__main__":
+       print "MobiDeDrm v0.11. Copyright (c) 2008 The Dark Reverser"
        if len(sys.argv)<4:
                print "Removes protection from Mobipocket books"
                print "Usage:"
-               print "  mobidedrm infile.mobi outfile.mobi PID"
-                return 1
+               print "  mobidedrm infile.mobi outfile.mobi (PID)"
+               sys.exit(1)
        else:  
                infile = sys.argv[1]
                outfile = sys.argv[2]
                pid = sys.argv[3]
                data_file = file(infile, 'rb').read()
                try:
-                       file(outfile, 'wb').write(DrmStripper(data_file, pid).getResult())
+                       strippedFile = DrmStripper(data_file, pid)
+                       file(outfile, 'wb').write(strippedFile.getResult())
                except DrmException, e:
                        print "Error: %s" % e
-                       return 1
-        return 0
-
-if __name__ == "__main__":
-    sys.exit(main())
-
+                       sys.exit(1)
+       sys.exit(0)
diff --git a/Topaz_Tools/TopazExtract_Kindle4PC.pyw b/Topaz_Tools/TopazExtract_Kindle4PC.pyw
new file mode 100644 (file)
index 0000000..c18fc3f
--- /dev/null
@@ -0,0 +1,193 @@
+#!/usr/bin/env python
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+import sys
+sys.path.append('lib')
+
+import os, os.path, urllib
+import subprocess
+from subprocess import Popen, PIPE, STDOUT
+import Tkinter
+import Tkconstants
+import tkFileDialog
+import tkMessageBox
+import subasyncio
+from subasyncio import Process
+from scrolltextwidget import ScrolledText
+
+class MainDialog(Tkinter.Frame):
+    def __init__(self, root):
+        Tkinter.Frame.__init__(self, root, border=5)
+        self.root = root
+        self.interval = 2000
+        self.p2 = None
+        self.status = Tkinter.Label(self, text='Extract Contents of Topaz eBook to a Directory')
+        self.status.pack(fill=Tkconstants.X, expand=1)
+        body = Tkinter.Frame(self)
+        body.pack(fill=Tkconstants.X, expand=1)
+        sticky = Tkconstants.E + Tkconstants.W
+        body.grid_columnconfigure(1, weight=2)
+
+        Tkinter.Label(body, text='Topaz eBook input file').grid(row=0, sticky=Tkconstants.E)
+        self.tpzpath = Tkinter.Entry(body, width=50)
+        self.tpzpath.grid(row=0, column=1, sticky=sticky)
+        self.tpzpath.insert(0, os.getcwd())
+        button = Tkinter.Button(body, text="...", command=self.get_tpzpath)
+        button.grid(row=0, column=2)
+
+        Tkinter.Label(body, text='Output Directory').grid(row=1, sticky=Tkconstants.E)
+        self.outpath = Tkinter.Entry(body, width=50)
+        self.outpath.grid(row=1, column=1, sticky=sticky)
+        self.outpath.insert(0, os.getcwd())
+        button = Tkinter.Button(body, text="...", command=self.get_outpath)
+        button.grid(row=1, column=2)
+
+        Tkinter.Label(body, text='First 8 char of PID (optional)').grid(row=3, sticky=Tkconstants.E)
+        self.pidnum = Tkinter.StringVar()
+        self.ccinfo = Tkinter.Entry(body, width=10, textvariable=self.pidnum)
+        self.ccinfo.grid(row=3, column=1, sticky=sticky)
+
+        msg1 = 'Conversion Log \n\n'
+        self.stext = ScrolledText(body, bd=5, relief=Tkconstants.RIDGE, height=15, width=60, wrap=Tkconstants.WORD)
+        self.stext.grid(row=4, column=0, columnspan=2,sticky=sticky)
+        self.stext.insert(Tkconstants.END,msg1)
+
+        buttons = Tkinter.Frame(self)
+        buttons.pack()
+        self.sbotton = Tkinter.Button(
+            buttons, text="Start", width=10, command=self.convertit)
+        self.sbotton.pack(side=Tkconstants.LEFT)
+
+        Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
+        self.qbutton = Tkinter.Button(
+            buttons, text="Quit", width=10, command=self.quitting)
+        self.qbutton.pack(side=Tkconstants.RIGHT)
+
+    # read from subprocess pipe without blocking
+    # invoked every interval via the widget "after"
+    # option being used, so need to reset it for the next time
+    def processPipe(self):
+        poll = self.p2.wait('nowait')
+        if poll != None: 
+            text = self.p2.readerr()
+            text += self.p2.read()
+            msg = text + '\n\n' + 'Files successfully extracted\n'
+            if poll != 0:
+                msg = text + '\n\n' + 'Error: File Extraction Failed\n'
+            self.showCmdOutput(msg)
+            self.p2 = None
+            self.sbotton.configure(state='normal')
+            return
+        text = self.p2.readerr()
+        text += self.p2.read()
+        self.showCmdOutput(text)
+        # make sure we get invoked again by event loop after interval 
+        self.stext.after(self.interval,self.processPipe)
+        return
+
+    # post output from subprocess in scrolled text widget
+    def showCmdOutput(self, msg):
+        if msg and msg !='':
+            self.stext.insert(Tkconstants.END,msg)
+            self.stext.yview_pickplace(Tkconstants.END)
+        return
+
+    # run as a subprocess via pipes and collect stdout
+    def topazrdr(self, infile, outdir, pidnum):
+        # os.putenv('PYTHONUNBUFFERED', '1')
+        pidoption = ''
+        if pidnum and pidnum != '':
+            pidoption = ' -p "' + pidnum + '" '
+        outoption = ' -o "' + outdir + '" '
+        cmdline = 'python ./lib/cmbtc_dump.py -v -d ' + pidoption + outoption + '"' + infile + '"'
+        if sys.platform[0:3] == 'win':
+            search_path = os.environ['PATH']
+            search_path = search_path.lower()
+            if search_path.find('python') >= 0: 
+                cmdline = 'python lib\cmbtc_dump.py -v -d ' + pidoption + outoption + '"' + infile + '"'
+            else :
+                cmdline = 'lib\cmbtc_dump.py -v -d ' + pidoption + outoption + '"' + infile + '"'
+
+        p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=PIPE, stderr=PIPE, close_fds=False)
+        return p2
+
+
+    def get_tpzpath(self):
+        tpzpath = tkFileDialog.askopenfilename(
+            parent=None, title='Select Topaz File',
+            defaultextension='.prc', filetypes=[('Topaz azw1', '.azw1'), ('Topaz prc', '.prc'),
+                                                ('All Files', '.*')])
+        if tpzpath:
+            tpzpath = os.path.normpath(tpzpath)
+            self.tpzpath.delete(0, Tkconstants.END)
+            self.tpzpath.insert(0, tpzpath)
+        return
+
+    def get_outpath(self):
+        outpath = tkFileDialog.askdirectory(
+            parent=None, title='Directory to Extract Files into',
+            initialdir=os.getcwd(), initialfile=None)
+        if outpath:
+            outpath = os.path.normpath(outpath)
+            self.outpath.delete(0, Tkconstants.END)
+            self.outpath.insert(0, outpath)
+        return
+
+    def quitting(self):
+        # kill any still running subprocess
+        if self.p2 != None:
+            if (self.p2.wait('nowait') == None):
+                self.p2.terminate()
+        self.root.destroy()
+
+    # actually ready to run the subprocess and get its output
+    def convertit(self):
+        # now disable the button to prevent multiple launches
+        self.sbotton.configure(state='disabled')
+        tpzpath = self.tpzpath.get()
+        outpath = self.outpath.get()
+        if not tpzpath or not os.path.exists(tpzpath):
+            self.status['text'] = 'Specified Topaz eBook file does not exist'
+            self.sbotton.configure(state='normal')
+            return
+        if not outpath:
+            self.status['text'] = 'No output directory specified'
+            self.sbotton.configure(state='normal')
+            return
+        if not os.path.exists(outpath):
+            os.makedirs(outpath)
+        pidnum = self.pidnum.get()
+        # if not pidnum or pidnum == '':
+        #     self.status['text'] = 'You have not entered a PID '
+        #     self.sbotton.configure(state='normal')
+        #     return
+
+        log = 'Command = "python cmbtc_dump.py"\n'
+        log += 'Topaz Path Path = "'+ tpzpath + '"\n'
+        log += 'Output Directory = "' + outpath + '"\n'
+        log += 'First 8 chars of PID = "' + pidnum + '"\n'
+        log += '\n\n'
+        log += 'Please Wait ...\n'
+        self.stext.insert(Tkconstants.END,log)
+        self.p2 = self.topazrdr(tpzpath, outpath, pidnum)
+
+        # python does not seem to allow you to create
+        # your own eventloop which every other gui does - strange 
+        # so need to use the widget "after" command to force
+        # event loop to run non-gui events every interval
+        self.stext.after(self.interval,self.processPipe)
+        return
+
+
+def main(argv=None):
+    root = Tkinter.Tk()
+    root.title('Topaz eBook File Extraction')
+    root.resizable(True, False)
+    root.minsize(300, 0)
+    MainDialog(root).pack(fill=Tkconstants.X, expand=1)
+    root.mainloop()
+    return 0
+    
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/Topaz_Tools/TopazExtract_Kindle_iPhone.pyw b/Topaz_Tools/TopazExtract_Kindle_iPhone.pyw
new file mode 100644 (file)
index 0000000..30133ec
--- /dev/null
@@ -0,0 +1,191 @@
+#!/usr/bin/env python
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+import sys
+sys.path.append('lib')
+
+import os, os.path, urllib
+import subprocess
+from subprocess import Popen, PIPE, STDOUT
+import Tkinter
+import Tkconstants
+import tkFileDialog
+import tkMessageBox
+import subasyncio
+from subasyncio import Process
+from scrolltextwidget import ScrolledText
+
+class MainDialog(Tkinter.Frame):
+    def __init__(self, root):
+        Tkinter.Frame.__init__(self, root, border=5)
+        self.root = root
+        self.interval = 2000
+        self.p2 = None
+        self.status = Tkinter.Label(self, text='Extract Contents of Topaz eBook to a Directory')
+        self.status.pack(fill=Tkconstants.X, expand=1)
+        body = Tkinter.Frame(self)
+        body.pack(fill=Tkconstants.X, expand=1)
+        sticky = Tkconstants.E + Tkconstants.W
+        body.grid_columnconfigure(1, weight=2)
+
+        Tkinter.Label(body, text='Topaz eBook input file').grid(row=0, sticky=Tkconstants.E)
+        self.tpzpath = Tkinter.Entry(body, width=50)
+        self.tpzpath.grid(row=0, column=1, sticky=sticky)
+        self.tpzpath.insert(0, os.getcwd())
+        button = Tkinter.Button(body, text="...", command=self.get_tpzpath)
+        button.grid(row=0, column=2)
+
+        Tkinter.Label(body, text='Output Directory').grid(row=1, sticky=Tkconstants.E)
+        self.outpath = Tkinter.Entry(body, width=50)
+        self.outpath.grid(row=1, column=1, sticky=sticky)
+        self.outpath.insert(0, os.getcwd())
+        button = Tkinter.Button(body, text="...", command=self.get_outpath)
+        button.grid(row=1, column=2)
+
+        Tkinter.Label(body, text='First 8 characters of PID').grid(row=3, sticky=Tkconstants.E)
+        self.pidnum = Tkinter.StringVar()
+        self.ccinfo = Tkinter.Entry(body, width=10, textvariable=self.pidnum)
+        self.ccinfo.grid(row=3, column=1, sticky=sticky)
+
+        msg1 = 'Conversion Log \n\n'
+        self.stext = ScrolledText(body, bd=5, relief=Tkconstants.RIDGE, height=15, width=60, wrap=Tkconstants.WORD)
+        self.stext.grid(row=4, column=0, columnspan=2,sticky=sticky)
+        self.stext.insert(Tkconstants.END,msg1)
+
+        buttons = Tkinter.Frame(self)
+        buttons.pack()
+        self.sbotton = Tkinter.Button(
+            buttons, text="Start", width=10, command=self.convertit)
+        self.sbotton.pack(side=Tkconstants.LEFT)
+
+        Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
+        self.qbutton = Tkinter.Button(
+            buttons, text="Quit", width=10, command=self.quitting)
+        self.qbutton.pack(side=Tkconstants.RIGHT)
+
+    # read from subprocess pipe without blocking
+    # invoked every interval via the widget "after"
+    # option being used, so need to reset it for the next time
+    def processPipe(self):
+        poll = self.p2.wait('nowait')
+        if poll != None: 
+            text = self.p2.readerr()
+            text += self.p2.read()
+            msg = text + '\n\n' + 'Files successfully extracted\n'
+            if poll != 0:
+                msg = text + '\n\n' + 'Error: File Extraction Failed\n'
+            self.showCmdOutput(msg)
+            self.p2 = None
+            self.sbotton.configure(state='normal')
+            return
+        text = self.p2.readerr()
+        text += self.p2.read()
+        self.showCmdOutput(text)
+        # make sure we get invoked again by event loop after interval 
+        self.stext.after(self.interval,self.processPipe)
+        return
+
+    # post output from subprocess in scrolled text widget
+    def showCmdOutput(self, msg):
+        if msg and msg !='':
+            self.stext.insert(Tkconstants.END,msg)
+            self.stext.yview_pickplace(Tkconstants.END)
+        return
+
+    # run as a subprocess via pipes and collect stdout
+    def topazrdr(self, infile, outdir, pidnum):
+        # os.putenv('PYTHONUNBUFFERED', '1')
+        pidoption = ' -p "' + pidnum + '" '
+        outoption = ' -o "' + outdir + '" '
+        cmdline = 'python ./lib/cmbtc_dump_nonK4PC.py -v -d ' + pidoption + outoption + '"' + infile + '"'
+        if sys.platform[0:3] == 'win':
+            search_path = os.environ['PATH']
+            search_path = search_path.lower()
+            if search_path.find('python') >= 0: 
+                cmdline = 'python lib\cmbtc_dump_nonK4PC.py -v -d ' + pidoption + outoption + '"' + infile + '"'
+            else :
+                cmdline = 'lib\cmbtc_dump_nonK4PC.py -v -d ' + pidoption + outoption + '"' + infile + '"'
+
+        p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=PIPE, stderr=PIPE, close_fds=False)
+        return p2
+
+
+    def get_tpzpath(self):
+        tpzpath = tkFileDialog.askopenfilename(
+            parent=None, title='Select Topaz File',
+            defaultextension='.prc', filetypes=[('Topaz azw1', '.azw1'), ('Topaz prc', '.prc'),
+                                                ('All Files', '.*')])
+        if tpzpath:
+            tpzpath = os.path.normpath(tpzpath)
+            self.tpzpath.delete(0, Tkconstants.END)
+            self.tpzpath.insert(0, tpzpath)
+        return
+
+    def get_outpath(self):
+        outpath = tkFileDialog.askdirectory(
+            parent=None, title='Directory to Extract Files into',
+            initialdir=os.getcwd(), initialfile=None)
+        if outpath:
+            outpath = os.path.normpath(outpath)
+            self.outpath.delete(0, Tkconstants.END)
+            self.outpath.insert(0, outpath)
+        return
+
+    def quitting(self):
+        # kill any still running subprocess
+        if self.p2 != None:
+            if (self.p2.wait('nowait') == None):
+                self.p2.terminate()
+        self.root.destroy()
+
+    # actually ready to run the subprocess and get its output
+    def convertit(self):
+        # now disable the button to prevent multiple launches
+        self.sbotton.configure(state='disabled')
+        tpzpath = self.tpzpath.get()
+        outpath = self.outpath.get()
+        if not tpzpath or not os.path.exists(tpzpath):
+            self.status['text'] = 'Specified Topaz eBook file does not exist'
+            self.sbotton.configure(state='normal')
+            return
+        if not outpath:
+            self.status['text'] = 'No output directory specified'
+            self.sbotton.configure(state='normal')
+            return
+        if not os.path.exists(outpath):
+            os.makedirs(outpath)
+        pidnum = self.pidnum.get()
+        if not pidnum or pidnum == '':
+            self.status['text'] = 'You have not entered a PID '
+            self.sbotton.configure(state='normal')
+            return
+
+        log = 'Command = "python cmbtc_dump_nonK4PC.py"\n'
+        log += 'Topaz Path Path = "'+ tpzpath + '"\n'
+        log += 'Output Directory = "' + outpath + '"\n'
+        log += 'First 8 chars of PID = "' + pidnum + '"\n'
+        log += '\n\n'
+        log += 'Please Wait ...\n'
+        self.stext.insert(Tkconstants.END,log)
+        self.p2 = self.topazrdr(tpzpath, outpath, pidnum)
+
+        # python does not seem to allow you to create
+        # your own eventloop which every other gui does - strange 
+        # so need to use the widget "after" command to force
+        # event loop to run non-gui events every interval
+        self.stext.after(self.interval,self.processPipe)
+        return
+
+
+def main(argv=None):
+    root = Tkinter.Tk()
+    root.title('Topaz eBook File Extraction')
+    root.resizable(True, False)
+    root.minsize(300, 0)
+    MainDialog(root).pack(fill=Tkconstants.X, expand=1)
+    root.mainloop()
+    return 0
+    
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/Topaz_Tools/TopazFiles2HTML.pyw b/Topaz_Tools/TopazFiles2HTML.pyw
new file mode 100644 (file)
index 0000000..1e094a5
--- /dev/null
@@ -0,0 +1,152 @@
+#!/usr/bin/env python
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+import sys
+sys.path.append('lib')
+
+import os, os.path, urllib
+import subprocess
+from subprocess import Popen, PIPE, STDOUT
+import Tkinter
+import Tkconstants
+import tkFileDialog
+import tkMessageBox
+import subasyncio
+from subasyncio import Process
+from scrolltextwidget import ScrolledText
+
+class MainDialog(Tkinter.Frame):
+    def __init__(self, root):
+        Tkinter.Frame.__init__(self, root, border=5)
+        self.root = root
+        self.interval = 2000
+        self.p2 = None
+        self.status = Tkinter.Label(self, text='Convert Files From Topaz eBook to HTML')
+        self.status.pack(fill=Tkconstants.X, expand=1)
+        body = Tkinter.Frame(self)
+        body.pack(fill=Tkconstants.X, expand=1)
+        sticky = Tkconstants.E + Tkconstants.W
+        body.grid_columnconfigure(1, weight=2)
+
+        Tkinter.Label(body, text='Directory you Extracted Topaz Files into').grid(row=0, sticky=Tkconstants.E)
+        self.bookdir = Tkinter.Entry(body, width=50)
+        self.bookdir.grid(row=0, column=1, sticky=sticky)
+        self.bookdir.insert(0, os.getcwd())
+        button = Tkinter.Button(body, text="...", command=self.get_bookdir)
+        button.grid(row=0, column=2)
+
+        msg1 = 'Conversion Log \n\n'
+        self.stext = ScrolledText(body, bd=5, relief=Tkconstants.RIDGE, height=15, width=60, wrap=Tkconstants.WORD)
+        self.stext.grid(row=4, column=0, columnspan=2,sticky=sticky)
+        self.stext.insert(Tkconstants.END,msg1)
+
+        buttons = Tkinter.Frame(self)
+        buttons.pack()
+        self.sbotton = Tkinter.Button(
+            buttons, text="Start", width=10, command=self.convertit)
+        self.sbotton.pack(side=Tkconstants.LEFT)
+
+        Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
+        self.qbutton = Tkinter.Button(
+            buttons, text="Quit", width=10, command=self.quitting)
+        self.qbutton.pack(side=Tkconstants.RIGHT)
+
+    # read from subprocess pipe without blocking
+    # invoked every interval via the widget "after"
+    # option being used, so need to reset it for the next time
+    def processPipe(self):
+        poll = self.p2.wait('nowait')
+        if poll != None: 
+            text = self.p2.readerr()
+            text += self.p2.read()
+            msg = text + '\n\n' + 'book.html successfully created in ' + self.bookdir.get() + '\n'
+            if poll != 0:
+                msg = text + '\n\n' + 'Error: HTML conversion Failed\n'
+            self.showCmdOutput(msg)
+            self.p2 = None
+            self.sbotton.configure(state='normal')
+            return
+        text = self.p2.readerr()
+        text += self.p2.read()
+        self.showCmdOutput(text)
+        # make sure we get invoked again by event loop after interval 
+        self.stext.after(self.interval,self.processPipe)
+        return
+
+    # post output from subprocess in scrolled text widget
+    def showCmdOutput(self, msg):
+        if msg and msg !='':
+            self.stext.insert(Tkconstants.END,msg)
+            self.stext.yview_pickplace(Tkconstants.END)
+        return
+
+    # run as a subprocess via pipes and collect stdout
+    def topazrdr(self, bookdir):
+        # os.putenv('PYTHONUNBUFFERED', '1')
+        cmdline = 'python ./lib/genhtml.py "' + bookdir + '"'
+        if sys.platform[0:3] == 'win':
+            search_path = os.environ['PATH']
+            search_path = search_path.lower()
+            if search_path.find('python') >= 0: 
+                cmdline = 'python lib\genhtml.py "' + bookdir + '"'
+            else :
+                cmdline = 'lib\genhtml.py "' + bookdir + '"'
+
+        p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=PIPE, stderr=PIPE, close_fds=False)
+        return p2
+
+
+    def get_bookdir(self):
+        bookdir = tkFileDialog.askdirectory(
+            parent=None, title='Select the Directory you Extracted Topaz Files into',
+            initialdir=os.getcwd(), initialfile=None)
+        if bookdir:
+            bookdir = os.path.normpath(bookdir)
+            self.bookdir.delete(0, Tkconstants.END)
+            self.bookdir.insert(0, bookdir)
+        return
+
+    def quitting(self):
+        # kill any still running subprocess
+        if self.p2 != None:
+            if (self.p2.wait('nowait') == None):
+                self.p2.terminate()
+        self.root.destroy()
+
+    # actually ready to run the subprocess and get its output
+    def convertit(self):
+        # now disable the button to prevent multiple launches
+        self.sbotton.configure(state='disabled')
+        bookdir = self.bookdir.get()
+        if not bookdir:
+            self.status['text'] = 'No directory specified'
+            self.sbotton.configure(state='normal')
+            return
+
+        log = 'Command = "python genhtml.py"\n'
+        log += 'Book Directory = "' + bookdir + '"\n'
+        log += '\n\n'
+        log += 'Please Wait ...\n'
+        self.stext.insert(Tkconstants.END,log)
+        self.p2 = self.topazrdr(bookdir)
+
+        # python does not seem to allow you to create
+        # your own eventloop which every other gui does - strange 
+        # so need to use the widget "after" command to force
+        # event loop to run non-gui events every interval
+        self.stext.after(self.interval,self.processPipe)
+        return
+
+
+def main(argv=None):
+    root = Tkinter.Tk()
+    root.title('Convert Topaz Files to SVG Files')
+    root.resizable(True, False)
+    root.minsize(300, 0)
+    MainDialog(root).pack(fill=Tkconstants.X, expand=1)
+    root.mainloop()
+    return 0
+    
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/Topaz_Tools/TopazFiles2SVG.pyw b/Topaz_Tools/TopazFiles2SVG.pyw
new file mode 100644 (file)
index 0000000..a93acc2
--- /dev/null
@@ -0,0 +1,152 @@
+#!/usr/bin/env python
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+import sys
+sys.path.append('lib')
+
+import os, os.path, urllib
+import subprocess
+from subprocess import Popen, PIPE, STDOUT
+import Tkinter
+import Tkconstants
+import tkFileDialog
+import tkMessageBox
+import subasyncio
+from subasyncio import Process
+from scrolltextwidget import ScrolledText
+
+class MainDialog(Tkinter.Frame):
+    def __init__(self, root):
+        Tkinter.Frame.__init__(self, root, border=5)
+        self.root = root
+        self.interval = 2000
+        self.p2 = None
+        self.status = Tkinter.Label(self, text='Convert Files From Topaz eBook to SVG')
+        self.status.pack(fill=Tkconstants.X, expand=1)
+        body = Tkinter.Frame(self)
+        body.pack(fill=Tkconstants.X, expand=1)
+        sticky = Tkconstants.E + Tkconstants.W
+        body.grid_columnconfigure(1, weight=2)
+
+        Tkinter.Label(body, text='Directory you Extracted Topaz Files into').grid(row=0, sticky=Tkconstants.E)
+        self.bookdir = Tkinter.Entry(body, width=50)
+        self.bookdir.grid(row=0, column=1, sticky=sticky)
+        self.bookdir.insert(0, os.getcwd())
+        button = Tkinter.Button(body, text="...", command=self.get_bookdir)
+        button.grid(row=0, column=2)
+
+        msg1 = 'Conversion Log \n\n'
+        self.stext = ScrolledText(body, bd=5, relief=Tkconstants.RIDGE, height=15, width=60, wrap=Tkconstants.WORD)
+        self.stext.grid(row=4, column=0, columnspan=2,sticky=sticky)
+        self.stext.insert(Tkconstants.END,msg1)
+
+        buttons = Tkinter.Frame(self)
+        buttons.pack()
+        self.sbotton = Tkinter.Button(
+            buttons, text="Start", width=10, command=self.convertit)
+        self.sbotton.pack(side=Tkconstants.LEFT)
+
+        Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
+        self.qbutton = Tkinter.Button(
+            buttons, text="Quit", width=10, command=self.quitting)
+        self.qbutton.pack(side=Tkconstants.RIGHT)
+
+    # read from subprocess pipe without blocking
+    # invoked every interval via the widget "after"
+    # option being used, so need to reset it for the next time
+    def processPipe(self):
+        poll = self.p2.wait('nowait')
+        if poll != None: 
+            text = self.p2.readerr()
+            text += self.p2.read()
+            msg = text + '\n\n' + 'SVG embedded in XHTML files successfully created in the svg directory in ' + self.bookdir.get() + '\n'
+            if poll != 0:
+                msg = text + '\n\n' + 'Error: SVG conversion Failed\n'
+            self.showCmdOutput(msg)
+            self.p2 = None
+            self.sbotton.configure(state='normal')
+            return
+        text = self.p2.readerr()
+        text += self.p2.read()
+        self.showCmdOutput(text)
+        # make sure we get invoked again by event loop after interval 
+        self.stext.after(self.interval,self.processPipe)
+        return
+
+    # post output from subprocess in scrolled text widget
+    def showCmdOutput(self, msg):
+        if msg and msg !='':
+            self.stext.insert(Tkconstants.END,msg)
+            self.stext.yview_pickplace(Tkconstants.END)
+        return
+
+    # run as a subprocess via pipes and collect stdout
+    def topazrdr(self, bookdir):
+        # os.putenv('PYTHONUNBUFFERED', '1')
+        cmdline = 'python ./lib/gensvg.py "' + bookdir + '"'
+        if sys.platform[0:3] == 'win':
+            search_path = os.environ['PATH']
+            search_path = search_path.lower()
+            if search_path.find('python') >= 0: 
+                cmdline = 'python lib\gensvg.py "' + bookdir + '"'
+            else :
+                cmdline = 'lib\gensvg.py "' + bookdir + '"'
+
+        p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=PIPE, stderr=PIPE, close_fds=False)
+        return p2
+
+
+    def get_bookdir(self):
+        bookdir = tkFileDialog.askdirectory(
+            parent=None, title='Select the Directory you Extracted Topaz Files into',
+            initialdir=os.getcwd(), initialfile=None)
+        if bookdir:
+            bookdir = os.path.normpath(bookdir)
+            self.bookdir.delete(0, Tkconstants.END)
+            self.bookdir.insert(0, bookdir)
+        return
+
+    def quitting(self):
+        # kill any still running subprocess
+        if self.p2 != None:
+            if (self.p2.wait('nowait') == None):
+                self.p2.terminate()
+        self.root.destroy()
+
+    # actually ready to run the subprocess and get its output
+    def convertit(self):
+        # now disable the button to prevent multiple launches
+        self.sbotton.configure(state='disabled')
+        bookdir = self.bookdir.get()
+        if not bookdir:
+            self.status['text'] = 'No directory specified'
+            self.sbotton.configure(state='normal')
+            return
+
+        log = 'Command = "python gensvg.py"\n'
+        log += 'Book Directory = "' + bookdir + '"\n'
+        log += '\n\n'
+        log += 'Please Wait ...\n'
+        self.stext.insert(Tkconstants.END,log)
+        self.p2 = self.topazrdr(bookdir)
+
+        # python does not seem to allow you to create
+        # your own eventloop which every other gui does - strange 
+        # so need to use the widget "after" command to force
+        # event loop to run non-gui events every interval
+        self.stext.after(self.interval,self.processPipe)
+        return
+
+
+def main(argv=None):
+    root = Tkinter.Tk()
+    root.title('Convert Topaz Files to SVG Files')
+    root.resizable(True, False)
+    root.minsize(300, 0)
+    MainDialog(root).pack(fill=Tkconstants.X, expand=1)
+    root.mainloop()
+    return 0
+    
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/Topaz_Tools/TopazFiles2XML.pyw b/Topaz_Tools/TopazFiles2XML.pyw
new file mode 100644 (file)
index 0000000..1d2bd7c
--- /dev/null
@@ -0,0 +1,152 @@
+#!/usr/bin/env python
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+import sys
+sys.path.append('lib')
+
+import os, os.path, urllib
+import subprocess
+from subprocess import Popen, PIPE, STDOUT
+import Tkinter
+import Tkconstants
+import tkFileDialog
+import tkMessageBox
+import subasyncio
+from subasyncio import Process
+from scrolltextwidget import ScrolledText
+
+class MainDialog(Tkinter.Frame):
+    def __init__(self, root):
+        Tkinter.Frame.__init__(self, root, border=5)
+        self.root = root
+        self.interval = 2000
+        self.p2 = None
+        self.status = Tkinter.Label(self, text='Convert Files From Topaz eBook to XML')
+        self.status.pack(fill=Tkconstants.X, expand=1)
+        body = Tkinter.Frame(self)
+        body.pack(fill=Tkconstants.X, expand=1)
+        sticky = Tkconstants.E + Tkconstants.W
+        body.grid_columnconfigure(1, weight=2)
+
+        Tkinter.Label(body, text='Directory you Extracted Topaz Files into').grid(row=0, sticky=Tkconstants.E)
+        self.bookdir = Tkinter.Entry(body, width=50)
+        self.bookdir.grid(row=0, column=1, sticky=sticky)
+        self.bookdir.insert(0, os.getcwd())
+        button = Tkinter.Button(body, text="...", command=self.get_bookdir)
+        button.grid(row=0, column=2)
+
+        msg1 = 'Conversion Log \n\n'
+        self.stext = ScrolledText(body, bd=5, relief=Tkconstants.RIDGE, height=15, width=60, wrap=Tkconstants.WORD)
+        self.stext.grid(row=4, column=0, columnspan=2,sticky=sticky)
+        self.stext.insert(Tkconstants.END,msg1)
+
+        buttons = Tkinter.Frame(self)
+        buttons.pack()
+        self.sbotton = Tkinter.Button(
+            buttons, text="Start", width=10, command=self.convertit)
+        self.sbotton.pack(side=Tkconstants.LEFT)
+
+        Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
+        self.qbutton = Tkinter.Button(
+            buttons, text="Quit", width=10, command=self.quitting)
+        self.qbutton.pack(side=Tkconstants.RIGHT)
+
+    # read from subprocess pipe without blocking
+    # invoked every interval via the widget "after"
+    # option being used, so need to reset it for the next time
+    def processPipe(self):
+        poll = self.p2.wait('nowait')
+        if poll != None: 
+            text = self.p2.readerr()
+            text += self.p2.read()
+            msg = text + '\n\n' + 'XML files successfully created in the xml directory in ' + self.bookdir.get() + '\n'
+            if poll != 0:
+                msg = text + '\n\n' + 'Error: XML conversion Failed\n'
+            self.showCmdOutput(msg)
+            self.p2 = None
+            self.sbotton.configure(state='normal')
+            return
+        text = self.p2.readerr()
+        text += self.p2.read()
+        self.showCmdOutput(text)
+        # make sure we get invoked again by event loop after interval 
+        self.stext.after(self.interval,self.processPipe)
+        return
+
+    # post output from subprocess in scrolled text widget
+    def showCmdOutput(self, msg):
+        if msg and msg !='':
+            self.stext.insert(Tkconstants.END,msg)
+            self.stext.yview_pickplace(Tkconstants.END)
+        return
+
+    # run as a subprocess via pipes and collect stdout
+    def topazrdr(self, bookdir):
+        # os.putenv('PYTHONUNBUFFERED', '1')
+        cmdline = 'python ./lib/genxml.py "' + bookdir + '"'
+        if sys.platform[0:3] == 'win':
+            search_path = os.environ['PATH']
+            search_path = search_path.lower()
+            if search_path.find('python') >= 0: 
+                cmdline = 'python lib\genxml.py "' + bookdir + '"'
+            else :
+                cmdline = 'lib\genxml.py "' + bookdir + '"'
+
+        p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=PIPE, stderr=PIPE, close_fds=False)
+        return p2
+
+
+    def get_bookdir(self):
+        bookdir = tkFileDialog.askdirectory(
+            parent=None, title='Select the Directory you Extracted Topaz Files into',
+            initialdir=os.getcwd(), initialfile=None)
+        if bookdir:
+            bookdir = os.path.normpath(bookdir)
+            self.bookdir.delete(0, Tkconstants.END)
+            self.bookdir.insert(0, bookdir)
+        return
+
+    def quitting(self):
+        # kill any still running subprocess
+        if self.p2 != None:
+            if (self.p2.wait('nowait') == None):
+                self.p2.terminate()
+        self.root.destroy()
+
+    # actually ready to run the subprocess and get its output
+    def convertit(self):
+        # now disable the button to prevent multiple launches
+        self.sbotton.configure(state='disabled')
+        bookdir = self.bookdir.get()
+        if not bookdir:
+            self.status['text'] = 'No directory specified'
+            self.sbotton.configure(state='normal')
+            return
+
+        log = 'Command = "python genxml.py"\n'
+        log += 'Book Directory = "' + bookdir + '"\n'
+        log += '\n\n'
+        log += 'Please Wait ...\n'
+        self.stext.insert(Tkconstants.END,log)
+        self.p2 = self.topazrdr(bookdir)
+
+        # python does not seem to allow you to create
+        # your own eventloop which every other gui does - strange 
+        # so need to use the widget "after" command to force
+        # event loop to run non-gui events every interval
+        self.stext.after(self.interval,self.processPipe)
+        return
+
+
+def main(argv=None):
+    root = Tkinter.Tk()
+    root.title('Convert Topaz Files to XML Files')
+    root.resizable(True, False)
+    root.minsize(300, 0)
+    MainDialog(root).pack(fill=Tkconstants.X, expand=1)
+    root.mainloop()
+    return 0
+    
+
+if __name__ == "__main__":
+    sys.exit(main())
index de0ecf7abda93dd8a98fb73dfd7488a979d8d97a..c7b08f84499c09b706a730e44acdd259137e28e9 100644 (file)
@@ -1,5 +1,5 @@
 #! /usr/bin/python
-# For use in Topaz Scripts version 2.0
+# For use in Topaz Scripts version 2.2
 
 """
 
@@ -13,11 +13,22 @@ y2/pHuYme7U1TsgSjwIDAQAB
 -----END PUBLIC KEY-----
 
 """
-
 from __future__ import with_statement
 
-import csv
+class Unbuffered:
+    def __init__(self, stream):
+        self.stream = stream
+    def write(self, data):
+        self.stream.write(data)
+        self.stream.flush()
+    def __getattr__(self, attr):
+        return getattr(self.stream, attr)
+
 import sys
+sys.stdout=Unbuffered(sys.stdout)
+
+
+import csv
 import os
 import getopt
 import zlib
@@ -305,7 +316,10 @@ def encodeNumber(number):
        byte += flag
        result += chr(byte)
        flag = 0x80
-       if number == 0 : break
+       if number == 0 :
+           if (byte == 0xFF and negative == False) :
+               result += chr(0x80)
+           break
    
    if negative:
        result += chr(0xFF)
@@ -841,13 +855,12 @@ def main(argv=sys.argv):
         if len(bookKeys) == 0 :
             if verbose > 0 :
                 print ("Book key could not be found. Maybe this book is not registered with this device.")
+                return 1
         else :
             bookKey = bookKeys[0]
             if verbose > 0:
                 print("Book key: " + bookKey.encode('hex'))
                 
-            
-                  
             if command == "printRecord" :
                 extractBookPayloadRecord(recordName,int(recordIndex),outputFile)
                 if outputFile != "" and verbose>0 :
@@ -859,6 +872,7 @@ def main(argv=sys.argv):
                         print ("Decrypted book saved. Don't pirate!")
                 elif verbose > 0:
                     print("Output directory name was not supplied.")
+                    return 1
     
     return 0
 
index 5e43ae6b0ec1d261280bb43ff2d44d798e7d1721..253079f1c1786ba7f00c17944a62679d45bb5990 100644 (file)
@@ -1,10 +1,19 @@
-#! /usr/bin/python
-# For use with Topaz Scripts Version 2.0
+#!/usr/bin/python
+# For use with Topaz Scripts Version 2.2
+
+class Unbuffered:
+    def __init__(self, stream):
+        self.stream = stream
+    def write(self, data):
+        self.stream.write(data)
+        self.stream.flush()
+    def __getattr__(self, attr):
+        return getattr(self.stream, attr)
 
-from __future__ import with_statement
+import sys
+sys.stdout=Unbuffered(sys.stdout)
 
 import csv
-import sys
 import os
 import getopt
 import zlib
@@ -90,7 +99,10 @@ def encodeNumber(number):
        byte += flag
        result += chr(byte)
        flag = 0x80
-       if number == 0 : break
+       if number == 0 :
+           if (byte == 0xFF and negative == False) :
+               result += chr(0x80)
+           break
    
    if negative:
        result += chr(0xFF)
@@ -480,12 +492,11 @@ def main(argv=sys.argv):
         if len(bookKeys) == 0 :
             if verbose > 0 :
                 print ("Book key could not be found. Maybe this book is not registered with this device.")
+                return 1
         else :
             bookKey = bookKeys[0]
             if verbose > 0:
                 print("Book key: " + bookKey.encode('hex'))
-                
-            
                   
             if command == "printRecord" :
                 extractBookPayloadRecord(recordName,int(recordIndex),outputFile)
@@ -498,6 +509,7 @@ def main(argv=sys.argv):
                         print ("Decrypted book saved. Don't pirate!")
                 elif verbose > 0:
                     print("Output directory name was not supplied.")
+                    return 1
     
     return 0
 
index 4e841847fa477de0318ce7ffeffd97dc8138ec21..caf503922b6e06fff032ed6371a7136a2278b651 100644 (file)
@@ -1,10 +1,20 @@
 #! /usr/bin/python
 # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
-# For use with Topaz Scripts Version 2.0
+# For use with Topaz Scripts Version 2.2
+
+class Unbuffered:
+    def __init__(self, stream):
+        self.stream = stream
+    def write(self, data):
+        self.stream.write(data)
+        self.stream.flush()
+    def __getattr__(self, attr):
+        return getattr(self.stream, attr)
 
-from __future__ import with_statement
-import csv
 import sys
+sys.stdout=Unbuffered(sys.stdout)
+
+import csv
 import os
 import getopt
 from struct import pack
@@ -61,7 +71,10 @@ def encodeNumber(number):
        byte += flag
        result += chr(byte)
        flag = 0x80
-       if number == 0 : break
+       if number == 0 :
+           if (byte == 0xFF and negative == False) :
+               result += chr(0x80)
+           break
    
    if negative:
        result += chr(0xFF)
@@ -729,8 +742,6 @@ def main(argv):
     if len(argv) == 0:
         printOutput = True
         argv = sys.argv
-    else :
-        argv = argv.split()
 
     try:
         opts, args = getopt.getopt(argv[1:], "hd", ["flat-xml"])
index 9f58a53277b03e359f10279bc6641507a5aee525..01d8cc942cb7628b604d9f718c891323f2f15086 100644 (file)
@@ -1,8 +1,7 @@
 #! /usr/bin/python
 # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
-# For use with Topaz Scripts Version 2.0
+# For use with Topaz Scripts Version 2.2
 
-from __future__ import with_statement
 import csv
 import sys
 import os
@@ -61,8 +60,11 @@ def encodeNumber(number):
        byte += flag
        result += chr(byte)
        flag = 0x80
-       if number == 0 : break
-   
+       if number == 0 :
+           if (byte == 0xFF and negative == False) :
+               result += chr(0x80)
+           break
+
    if negative:
        result += chr(0xFF)
    
index eaeeabe1cfdcfd7b6e68b82a128d07431f2b1f4c..9e3080bf0332b7de296631ea450f38c06f50619d 100644 (file)
@@ -1,10 +1,9 @@
 #! /usr/bin/python
 # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
-# For use with Topaz Scripts Version 2.0
+# For use with Topaz Scripts Version 2.2
 
-from __future__ import with_statement
-import csv
 import sys
+import csv
 import os
 import math
 import getopt
index 5fcdd3851bd3aa19c9d6ee4a212102cbd2b0b010..8df7b52cfd3a51d3282795968d8466e8d592b8fb 100644 (file)
@@ -1,8 +1,21 @@
 #! /usr/bin/python
 # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
-# For use with Topaz Scripts Version 2.0
+# For use with Topaz Scripts Version 2.2
 
-import os, sys, getopt
+class Unbuffered:
+    def __init__(self, stream):
+        self.stream = stream
+    def write(self, data):
+        self.stream.write(data)
+        self.stream.flush()
+    def __getattr__(self, attr):
+        return getattr(self.stream, attr)
+
+import sys
+sys.stdout=Unbuffered(sys.stdout)
+
+
+import os, getopt
 
 # local routines
 import convert2xml
@@ -27,8 +40,6 @@ def main(argv):
 
     if len(argv) == 0:
         argv = sys.argv
-    else :
-        argv = argv.split()
 
     try:
         opts, args = getopt.getopt(argv[1:], "h:",["fixed-image"])
@@ -36,11 +47,11 @@ def main(argv):
     except getopt.GetoptError, err:
         print str(err)
         usage()
-        sys.exit(2)
+        sys.exit(1)
     
     if len(opts) == 0 and len(args) == 0 :
         usage()
-        sys.exit(2
+        sys.exit(1
        
     for o, a in opts:
         if o =="-h":
@@ -53,39 +64,39 @@ def main(argv):
 
     if not os.path.exists(bookDir) :
         print "Can not find directory with unencrypted book"
-        sys.exit(-1)
+        sys.exit(1)
 
     dictFile = os.path.join(bookDir,'dict0000.dat')
 
     if not os.path.exists(dictFile) :
         print "Can not find dict0000.dat file"
-        sys.exit(-1)
+        sys.exit(1)
 
     pageDir = os.path.join(bookDir,'page')
     if not os.path.exists(pageDir) :
         print "Can not find page directory in unencrypted book"
-        sys.exit(-1)
+        sys.exit(1)
 
     imgDir = os.path.join(bookDir,'img')
     if not os.path.exists(imgDir) :
         print "Can not find image directory in unencrypted book"
-        sys.exit(-1)
+        sys.exit(1)
 
     svgDir = os.path.join(bookDir,'svg')
     if not os.path.exists(svgDir) :
         print "Can not find svg directory in unencrypted book"
         print "please run gensvg.py before running genhtml.py"
-        sys.exit(-1)
+        sys.exit(1)
 
     otherFile = os.path.join(bookDir,'other0000.dat')
     if not os.path.exists(otherFile) :
         print "Can not find other0000.dat in unencrypted book"
-        sys.exit(-1)
+        sys.exit(1)
 
     metaFile = os.path.join(bookDir,'metadata0000.dat')
     if not os.path.exists(metaFile) :
         print "Can not find metadata0000.dat in unencrypted book"
-        sys.exit(-1)
+        sys.exit(1)
 
     htmlFileName = "book.html"
     htmlstr = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n'
@@ -123,18 +134,28 @@ def main(argv):
     pnum = int(spage)
 
     # get page height and width from first text page for use in stylesheet scaling
-    pname = 'page%04d.dat' % pnum
+    pname = 'page%04d.dat' % (pnum + 1)
     fname = os.path.join(pageDir,pname)
-    flat_xml = convert2xml.main('convert2xml.py --flat-xml ' + dictFile + ' ' + fname)
+    pargv=[]
+    pargv.append('convert2xml.py')
+    pargv.append('--flat-xml')
+    pargv.append(dictFile)
+    pargv.append(fname)
+    flat_xml = convert2xml.main(pargv)
     (ph, pw) = getpagedim.getPageDim(flat_xml)
-    if (ph == '-1') : ph = 11000
-    if (pw == '-1') : pw = 8500
+    if (ph == '-1') or (ph == '0') : ph = '11000'
+    if (pw == '-1') or (pw == '0') : pw = '8500'
 
     # now build up the style sheet
     print '     ', 'other0000.dat'
     fname = os.path.join(bookDir,'other0000.dat')
     xname = os.path.join(bookDir, 'style.css')
-    xmlstr = convert2xml.main('convert2xml.py --flat-xml ' + dictFile + ' ' + fname)
+    pargv=[]
+    pargv.append('convert2xml.py')
+    pargv.append('--flat-xml')
+    pargv.append(dictFile)
+    pargv.append(fname)
+    xmlstr = convert2xml.main(pargv)
     cssstr , classlst = stylexml2css.convert2CSS(xmlstr, fontsize, ph, pw)
     file(xname, 'wb').write(cssstr)
     htmlstr += '<link href="style.css" rel="stylesheet" type="text/css" />\n'
@@ -143,7 +164,12 @@ def main(argv):
     for filename in filenames:
         print '     ', filename
         fname = os.path.join(pageDir,filename)
-        flat_xml = convert2xml.main('convert2xml.py --flat-xml ' + dictFile + ' ' + fname) 
+        pargv=[]
+        pargv.append('convert2xml.py')
+        pargv.append('--flat-xml')
+        pargv.append(dictFile)
+        pargv.append(fname)
+        flat_xml = convert2xml.main(pargv) 
         htmlstr += flatxml2html.convert2HTML(flat_xml, classlst, fname, bookDir, fixedimage)
 
     htmlstr += '</body>\n</html>\n'
index ea198e4683f344e389bb40ac291bbce26eb07e58..e8dfe0665423269739a341240d630b9fe0427633 100644 (file)
@@ -1,8 +1,20 @@
 #! /usr/bin/python
 # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
-# For use with Topaz Scripts Version 2.0
+# For use with Topaz Scripts Version 2.2
 
-import os, sys, getopt
+class Unbuffered:
+    def __init__(self, stream):
+        self.stream = stream
+    def write(self, data):
+        self.stream.write(data)
+        self.stream.flush()
+    def __getattr__(self, attr):
+        return getattr(self.stream, attr)
+
+import sys
+sys.stdout=Unbuffered(sys.stdout)
+
+import os, getopt
 
 # local routines
 import convert2xml
@@ -190,8 +202,6 @@ def main(argv):
 
  if len(argv) == 0:
      argv = sys.argv
- else :
-     argv = argv.split()
 
  try:
      opts, args = getopt.getopt(argv[1:], "xrh")
@@ -199,11 +209,11 @@ def main(argv):
  except getopt.GetoptError, err:
      print str(err)
      usage()
-     sys.exit(2)
+     sys.exit(1)
 
  if len(opts) == 0 and len(args) == 0 :
      usage()
-     sys.exit(2
+     sys.exit(1
 
  raw = 0
  for o, a in opts:
@@ -219,33 +229,33 @@ def main(argv):
 
  if not os.path.exists(bookDir) :
      print "Can not find directory with unencrypted book"
-     sys.exit(-1)
+     sys.exit(1)
 
  dictFile = os.path.join(bookDir,'dict0000.dat')
 
  if not os.path.exists(dictFile) :
      print "Can not find dict0000.dat file"
-     sys.exit(-1)
+     sys.exit(1)
 
  pageDir = os.path.join(bookDir,'page')
  if not os.path.exists(pageDir) :
      print "Can not find page directory in unencrypted book"
-     sys.exit(-1)
+     sys.exit(1)
 
  imgDir = os.path.join(bookDir,'img')
  if not os.path.exists(imgDir) :
      print "Can not find image directory in unencrypted book"
-     sys.exit(-1)
+     sys.exit(1)
 
  glyphsDir = os.path.join(bookDir,'glyphs')
  if not os.path.exists(glyphsDir) :
      print "Can not find glyphs directory in unencrypted book"
-     sys.exit(-1)
+     sys.exit(1)
 
  metaFile = os.path.join(bookDir,'metadata0000.dat')
  if not os.path.exists(metaFile) :
      print "Can not find metadata0000.dat in unencrypted book"
-     sys.exit(-1)
+     sys.exit(1)
 
  svgDir = os.path.join(bookDir,'svg')
  if not os.path.exists(svgDir) :
@@ -274,7 +284,12 @@ def main(argv):
  for filename in filenames:
      print '     ', filename
      fname = os.path.join(glyphsDir,filename)
-     flat_xml = convert2xml.main('convert2xml.py --flat-xml ' + dictFile + ' ' + fname) 
+     pargv=[]
+     pargv.append('convert2xml.py')
+     pargv.append('--flat-xml')
+     pargv.append(dictFile)
+     pargv.append(fname)
+     flat_xml = convert2xml.main(pargv)
      gp = GParser(flat_xml)
      for i in xrange(0, gp.count):
          path = gp.getPath(i)
@@ -297,7 +312,12 @@ def main(argv):
  for filename in filenames:
      print '     ', filename
      fname = os.path.join(pageDir,filename)
-     flat_xml = convert2xml.main('convert2xml.py --flat-xml ' + dictFile + ' ' + fname) 
+     pargv=[]
+     pargv.append('convert2xml.py')
+     pargv.append('--flat-xml')
+     pargv.append(dictFile)
+     pargv.append(fname)
+     flat_xml = convert2xml.main(pargv)
      pp = PParser(flat_xml)
      if (raw) :
          pfile = open(os.path.join(svgDir,filename.replace('.dat','.svg')), 'w')
index 299dde2ffc203c7e5976773d4f751c616b355a92..868fef1c5f4ad479427f14e165d96234484c60c3 100644 (file)
@@ -1,8 +1,21 @@
 #! /usr/bin/python
 # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
-# For use with Topaz Scripts Version 2.0
+# For use with Topaz Scripts Version 2.2
 
-import os, sys, getopt
+class Unbuffered:
+    def __init__(self, stream):
+        self.stream = stream
+    def write(self, data):
+        self.stream.write(data)
+        self.stream.flush()
+    def __getattr__(self, attr):
+        return getattr(self.stream, attr)
+
+import sys
+sys.stdout=Unbuffered(sys.stdout)
+
+
+import os, getopt
 
 # local routines
 import convert2xml
@@ -23,8 +36,6 @@ def main(argv):
 
     if len(argv) == 0:
         argv = sys.argv
-    else :
-        argv = argv.split()
 
     try:
         opts, args = getopt.getopt(argv[1:], "h:")
@@ -32,11 +43,11 @@ def main(argv):
     except getopt.GetoptError, err:
         print str(err)
         usage()
-        sys.exit(2)
+        sys.exit(1)
     
     if len(opts) == 0 and len(args) == 0 :
         usage()
-        sys.exit(2
+        sys.exit(1
        
     for o, a in opts:
         if o =="-h":
@@ -47,32 +58,32 @@ def main(argv):
 
     if not os.path.exists(bookDir) :
         print "Can not find directory with unencrypted book"
-        sys.exit(-1)
+        sys.exit(1)
 
     dictFile = os.path.join(bookDir,'dict0000.dat')
     if not os.path.exists(dictFile) :
         print "Can not find dict0000.dat file"
-        sys.exit(-1)
+        sys.exit(1)
 
     pageDir = os.path.join(bookDir,'page')
     if not os.path.exists(pageDir) :
         print "Can not find page directory in unencrypted book"
-        sys.exit(-1)
+        sys.exit(1)
 
     glyphsDir = os.path.join(bookDir,'glyphs')
     if not os.path.exists(glyphsDir) :
         print "Can not find glyphs directory in unencrypted book"
-        sys.exit(-1)
+        sys.exit(1)
 
     otherFile = os.path.join(bookDir,'other0000.dat')
     if not os.path.exists(otherFile) :
         print "Can not find other0000.dat in unencrypted book"
-        sys.exit(-1)
+        sys.exit(1)
 
     metaFile = os.path.join(bookDir,'metadata0000.dat')
     if not os.path.exists(metaFile) :
         print "Can not find metadata0000.dat in unencrypted book"
-        sys.exit(-1)
+        sys.exit(1)
 
     xmlDir = os.path.join(bookDir,'xml')
     if not os.path.exists(xmlDir):
@@ -90,7 +101,11 @@ def main(argv):
     print '     ', 'other0000.dat'
     fname = os.path.join(bookDir,'other0000.dat')
     xname = os.path.join(xmlDir, 'stylesheet.xml')
-    xmlstr = convert2xml.main('convert2xml.py ' + dictFile + ' ' + fname)
+    pargv=[]
+    pargv.append('convert2xml.py')
+    pargv.append(dictFile)
+    pargv.append(fname)
+    xmlstr = convert2xml.main(pargv)
     file(xname, 'wb').write(xmlstr)
     
     filenames = os.listdir(pageDir)
@@ -100,7 +115,11 @@ def main(argv):
         print '     ', filename
         fname = os.path.join(pageDir,filename)
         xname = os.path.join(xmlDir, filename.replace('.dat','.xml'))
-        xmlstr = convert2xml.main('convert2xml.py ' + dictFile + ' ' + fname)
+        pargv=[]
+        pargv.append('convert2xml.py')
+        pargv.append(dictFile)
+        pargv.append(fname)
+        xmlstr = convert2xml.main(pargv)
         file(xname, 'wb').write(xmlstr)
 
     filenames = os.listdir(glyphsDir)
@@ -110,7 +129,11 @@ def main(argv):
         print '     ', filename
         fname = os.path.join(glyphsDir,filename)
         xname = os.path.join(xmlDir, filename.replace('.dat','.xml'))
-        xmlstr = convert2xml.main('convert2xml.py ' + dictFile + ' ' + fname)
+        pargv=[]
+        pargv.append('convert2xml.py')
+        pargv.append(dictFile)
+        pargv.append(fname)
+        xmlstr = convert2xml.main(pargv)
         file(xname, 'wb').write(xmlstr)
  
 
index b2f66c00194deb7f4df2a01efbc27cb38d3346c3..e59e83fa503fc261a41dbe9d4f946836b6eb63cd 100644 (file)
@@ -1,8 +1,7 @@
 #! /usr/bin/python
 # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
-# For use with Topaz Scripts Version 2.0
+# For use with Topaz Scripts Version 2.2
 
-from __future__ import with_statement
 import csv
 import sys
 import os
diff --git a/Topaz_Tools/lib/scrolltextwidget.py b/Topaz_Tools/lib/scrolltextwidget.py
new file mode 100644 (file)
index 0000000..98b4147
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+import Tkinter
+import Tkconstants
+
+# basic scrolled text widget
+class ScrolledText(Tkinter.Text):
+    def __init__(self, master=None, **kw):
+        self.frame = Tkinter.Frame(master)
+        self.vbar = Tkinter.Scrollbar(self.frame)
+        self.vbar.pack(side=Tkconstants.RIGHT, fill=Tkconstants.Y)
+        kw.update({'yscrollcommand': self.vbar.set})
+        Tkinter.Text.__init__(self, self.frame, **kw)
+        self.pack(side=Tkconstants.LEFT, fill=Tkconstants.BOTH, expand=True)
+        self.vbar['command'] = self.yview
+        # Copy geometry methods of self.frame without overriding Text
+        # methods = hack!
+        text_meths = vars(Tkinter.Text).keys()
+        methods = vars(Tkinter.Pack).keys() + vars(Tkinter.Grid).keys() + vars(Tkinter.Place).keys()
+        methods = set(methods).difference(text_meths)
+        for m in methods:
+            if m[0] != '_' and m != 'config' and m != 'configure':
+                setattr(self, m, getattr(self.frame, m))
+
+    def __str__(self):
+        return str(self.frame)
index f0d07d77b717245b9449eb91a7ef1d1ba2ae59f6..a540e9a96a976c85d1a034a404d87c519b69350a 100644 (file)
@@ -1,8 +1,7 @@
 #! /usr/bin/python
 # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
-# For use with Topaz Scripts Version 2.0
+# For use with Topaz Scripts Version 2.2
 
-from __future__ import with_statement
 import csv
 import sys
 import os
diff --git a/Topaz_Tools/lib/subasyncio.py b/Topaz_Tools/lib/subasyncio.py
new file mode 100644 (file)
index 0000000..ed13aa1
--- /dev/null
@@ -0,0 +1,149 @@
+#!/usr/bin/env python
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+import os, sys
+import signal
+import threading
+import subprocess
+from subprocess import Popen, PIPE, STDOUT
+
+# **heavily** chopped up and modfied version of asyncproc.py
+# to make it actually work on Windows as well as Mac/Linux
+# For the original see:
+# "http://www.lysator.liu.se/~bellman/download/"
+# author is  "Thomas Bellman <bellman@lysator.liu.se>"
+# available under GPL version 3 or Later
+
+# create an asynchronous subprocess whose output can be collected in
+# a non-blocking manner
+
+# What a mess!  Have to use threads just to get non-blocking io
+# in a cross-platform manner
+
+# luckily all thread use is hidden within this class
+
+class Process(object):
+    def __init__(self, *params, **kwparams):
+        if len(params) <= 3:
+            kwparams.setdefault('stdin', subprocess.PIPE)
+        if len(params) <= 4:
+            kwparams.setdefault('stdout', subprocess.PIPE)
+        if len(params) <= 5:
+            kwparams.setdefault('stderr', subprocess.PIPE)
+        self.__pending_input = []
+        self.__collected_outdata = []
+        self.__collected_errdata = []
+        self.__exitstatus = None
+        self.__lock = threading.Lock()
+        self.__inputsem = threading.Semaphore(0)
+        self.__quit = False
+
+        self.__process = subprocess.Popen(*params, **kwparams)
+
+        if self.__process.stdin:
+            self.__stdin_thread = threading.Thread(
+                name="stdin-thread",
+                target=self.__feeder, args=(self.__pending_input,
+                                            self.__process.stdin))
+            self.__stdin_thread.setDaemon(True)
+            self.__stdin_thread.start()
+
+        if self.__process.stdout:
+            self.__stdout_thread = threading.Thread(
+                name="stdout-thread",
+                target=self.__reader, args=(self.__collected_outdata,
+                                           self.__process.stdout))
+            self.__stdout_thread.setDaemon(True)
+            self.__stdout_thread.start()
+
+        if self.__process.stderr:
+            self.__stderr_thread = threading.Thread(
+                name="stderr-thread",
+                target=self.__reader, args=(self.__collected_errdata,
+                                           self.__process.stderr))
+            self.__stderr_thread.setDaemon(True)
+            self.__stderr_thread.start()
+
+    def pid(self):
+        return self.__process.pid
+
+    def kill(self, signal):
+        self.__process.send_signal(signal)
+
+    # check on subprocess (pass in 'nowait') to act like poll
+    def wait(self, flag):
+        if flag.lower() == 'nowait':
+            rc = self.__process.poll()
+        else:
+            rc = self.__process.wait()
+        if rc != None:
+            if self.__process.stdin:
+                self.closeinput()
+            if self.__process.stdout:
+                self.__stdout_thread.join()
+            if self.__process.stderr:
+                self.__stderr_thread.join()
+        return self.__process.returncode
+
+    def terminate(self):
+        if self.__process.stdin:
+            self.closeinput()
+        self.__process.terminate()
+
+    # thread gets data from subprocess stdout
+    def __reader(self, collector, source):
+        while True:
+            data = os.read(source.fileno(), 65536)
+            self.__lock.acquire()
+            collector.append(data)
+            self.__lock.release()
+            if data == "":
+                source.close()
+                break
+        return
+
+    # thread feeds data to subprocess stdin
+    def __feeder(self, pending, drain):
+        while True:
+            self.__inputsem.acquire()
+            self.__lock.acquire()
+            if not pending  and self.__quit:
+                drain.close()
+                self.__lock.release()
+                break
+            data = pending.pop(0)
+            self.__lock.release()
+            drain.write(data)
+
+    # non-blocking read of data from subprocess stdout
+    def read(self):
+        self.__lock.acquire()
+        outdata = "".join(self.__collected_outdata)
+        del self.__collected_outdata[:]
+        self.__lock.release()
+        return outdata
+
+    # non-blocking read of data from subprocess stderr
+    def readerr(self):
+        self.__lock.acquire()
+        errdata = "".join(self.__collected_errdata)
+        del self.__collected_errdata[:]
+        self.__lock.release()
+        return errdata
+
+    # non-blocking write to stdin of subprocess
+    def write(self, data):
+        if self.__process.stdin is None:
+            raise ValueError("Writing to process with stdin not a pipe")
+        self.__lock.acquire()
+        self.__pending_input.append(data)
+        self.__inputsem.release()
+        self.__lock.release()
+
+    # close stdinput of subprocess
+    def closeinput(self):
+        self.__lock.acquire()
+        self.__quit = True
+        self.__inputsem.release()
+        self.__lock.release()
+
diff --git a/Topaz_Tools/lib/topaz-changes.txt b/Topaz_Tools/lib/topaz-changes.txt
new file mode 100644 (file)
index 0000000..a80ef5f
--- /dev/null
@@ -0,0 +1,75 @@
+Changes in 2.2
+       - fix for minor bug in encode_Number from clark nova
+       - more fixes to handle paths with spaces in them
+       - updates to work better with the gui front end
+
+
+Changes in 2.1
+       - extremely minor changes to support a gui frontend
+       - no changes to functionality
+
+
+Changes in version 2.0
+
+       - gensvg.py now accepts two options
+             -x : output browseable XHTML+SVG pages (default)
+            -r : output raw SVG images (useful for later conversion to pdf)
+         
+       - flatxml2html.py now understands page.groups of type graphic
+            and handles vertical regions as svg images
+
+       - genhtml.py now accepts an option
+            --fixed-image : which will force the conversion
+                            of all fixed regions to svg images
+
+       - minor bug fixes and html conversion improvements
+
+
+Changes in version 1.8
+       - gensvg.py now builds wonderful xhtml pages with embedded svg 
+           that can be easily paged through as if reading a book!
+           (tested in Safari for Mac and Win and Firefox)
+           (requires javascript to be enabled)
+       - genhtml.py now REQUIRES that gensvg.py be run FIRST
+            this allows create of images on the fly from glyphs
+       - genhtml.py now automatically makes tables of words into svg
+            based images and will handle glyph based ornate first 
+            letters of words
+       - cmbtc_dump_mac_linux.py has been renamed to be
+            cmbtc_dump_nonK4PC.py to make it clearer
+            when it needs to be used
+       
+
+Changes in version 1.7
+       - gensvg.py has been improved so that the glyphs render exactly (ClarkNova)
+       - gensvg.py has fixed a render order "bug" that allowed some images to cover or hide text. (ClarkNova)
+       - change generated html to use external stylesheet via a link to "style.css"
+       - add missing <title> tag
+       - make xhtml compliant doctype and minor changes to write correct xhtml
+       - make divs that act as anchors be hidden visually and to take up 0 height and 0 width to prevent any impact on layout
+
+Changes in version 1.6
+       - support for books whose paragraphs have no styles
+       - support to run cmbtc_dump on Linux and Mac OSX provided you know your PID of your ipod or standalone Kindle
+        (contributed by DiapDealer)
+
+Changes in version 1.5
+       - completely reworked generation of styles to use actual page heights and widths
+       - added new script getpagedim.py to support the above
+       - style names with underscores in them are now properly paired with their base class
+       - fixed hanging indents that did not ever set a left margin
+       - added support for a number of not previously known region types
+       - added support for a previously unknown snippet - <empty></empty>
+       - corrected a bug that caused unknown regions to abort the program
+       - added code to make the handling of unknown regions better in general
+       - corrected a bug that caused the last link on a page to be missing (if it was the last thing on the page)
+
+Changes in version 1.3
+       - font generation by gensvg.py is now greatly improved with support for contour points added
+       - support for more region types
+       - support for inline images in paragraphs or text fields (ie. initial graphics for the first letter of a word)
+       - greatly improved dtd information used for the xml to prevent parsing mistakes
+
+Version 1.0
+       - initial release
+
diff --git a/eReader_Tools/Pml2HTML.pyw b/eReader_Tools/Pml2HTML.pyw
new file mode 100644 (file)
index 0000000..7800463
--- /dev/null
@@ -0,0 +1,181 @@
+#!/usr/bin/env python
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+import sys
+sys.path.append('lib')
+import os, os.path, urllib
+import subprocess
+from subprocess import Popen, PIPE, STDOUT
+import subasyncio
+from subasyncio import Process
+import Tkinter
+import Tkconstants
+import tkFileDialog
+import tkMessageBox
+from scrolltextwidget import ScrolledText
+
+class MainDialog(Tkinter.Frame):
+    def __init__(self, root):
+        Tkinter.Frame.__init__(self, root, border=5)
+        self.root = root
+        self.interval = 2000
+        self.p2 = None
+        self.status = Tkinter.Label(self, text='Pml to HTML Conversion')
+        self.status.pack(fill=Tkconstants.X, expand=1)
+        body = Tkinter.Frame(self)
+        body.pack(fill=Tkconstants.X, expand=1)
+        sticky = Tkconstants.E + Tkconstants.W
+        body.grid_columnconfigure(1, weight=2)
+
+        Tkinter.Label(body, text='eBook Pml input file').grid(row=0, sticky=Tkconstants.E)
+        self.pmlpath = Tkinter.Entry(body, width=50)
+        self.pmlpath.grid(row=0, column=1, sticky=sticky)
+        self.pmlpath.insert(0, os.getcwd())
+        button = Tkinter.Button(body, text="...", command=self.get_pmlpath)
+        button.grid(row=0, column=2)
+
+        Tkinter.Label(body, text='Name for HTML Output File').grid(row=1, sticky=Tkconstants.E)
+        self.outpath = Tkinter.Entry(body, width=50)
+        self.outpath.grid(row=1, column=1, sticky=sticky)
+        self.outpath.insert(0, '')
+        button = Tkinter.Button(body, text="...", command=self.get_outpath)
+        button.grid(row=1, column=2)
+
+        msg1 = 'Conversion Log \n\n'
+        self.stext = ScrolledText(body, bd=5, relief=Tkconstants.RIDGE, height=15, width=60, wrap=Tkconstants.WORD)
+        self.stext.grid(row=2, column=0, columnspan=2,sticky=sticky)
+        self.stext.insert(Tkconstants.END,msg1)
+
+        buttons = Tkinter.Frame(self)
+        buttons.pack()
+        self.sbotton = Tkinter.Button(
+            buttons, text="Start", width=10, command=self.convertit)
+        self.sbotton.pack(side=Tkconstants.LEFT)
+
+        Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
+        self.qbutton = Tkinter.Button(
+            buttons, text="Quit", width=10, command=self.quitting)
+        self.qbutton.pack(side=Tkconstants.RIGHT)
+
+    # read from subprocess pipe without blocking
+    # invoked every interval via the widget "after"
+    # option being used, so need to reset it for the next time
+    def processPipe(self):
+        poll = self.p2.wait('nowait')
+        if poll != None: 
+            text = self.p2.readerr()
+            text += self.p2.read()
+            msg = text + '\n\n' + 'File successfully converted\n'
+            if poll != 0:
+                msg = text + '\n\n' + 'Error: Conversion Failed\n'
+            self.showCmdOutput(msg)
+            self.p2 = None
+            self.sbotton.configure(state='normal')
+            return
+        text = self.p2.readerr()
+        text += self.p2.read()
+        self.showCmdOutput(text)
+        # make sure we get invoked again by event loop after interval 
+        self.stext.after(self.interval,self.processPipe)
+        return
+
+    # post output from subprocess in scrolled text widget
+    def showCmdOutput(self, msg):
+        if msg and msg !='':
+            self.stext.insert(Tkconstants.END,msg)
+            self.stext.yview_pickplace(Tkconstants.END)
+        return
+
+    # run xpml2hxtml.py as a subprocess via pipes and collect stdout
+    def pmlhtml(self, infile, outfile):
+        # os.putenv('PYTHONUNBUFFERED', '1')
+        cmdline = 'python ./lib/xpml2xhtml.py "' + infile + '" "' + outfile + '"' 
+        if sys.platform[0:3] == 'win':
+            search_path = os.environ['PATH']
+            search_path = search_path.lower()
+            if search_path.find('python') >= 0: 
+                cmdline = 'python lib\\xpml2xhtml.py "' + infile + '" "' + outfile + '"'
+            else :
+                cmdline = 'lib\\xpml2xhtml.py "' + infile + '" "' + outfile + '"'
+
+        p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=PIPE, stderr=PIPE, close_fds=False)
+        return p2
+
+
+    def get_pmlpath(self):
+        pmlpath = tkFileDialog.askopenfilename(
+            parent=None, title='Select eBook Pml File',
+            defaultextension='.pml', filetypes=[('eBook Pml File', '.pml'),
+                                                ('All Files', '.*')])
+        if pmlpath:
+            pmlpath = os.path.normpath(pmlpath)
+            self.pmlpath.delete(0, Tkconstants.END)
+            self.pmlpath.insert(0, pmlpath)
+        return
+
+    def get_outpath(self):
+        pmlpath = self.pmlpath.get()
+        initname = os.path.basename(pmlpath)
+        p = initname.find('.')
+        if p >= 0: initname = initname[0:p]
+        initname += '.html' 
+        outpath = tkFileDialog.asksaveasfilename(
+            parent=None, title='Select HTML file to produce',
+            defaultextension='.html', initialfile=initname,
+            filetypes=[('HTML files', '.html'), ('All files', '.*')])
+        if outpath:
+            outpath = os.path.normpath(outpath)
+            self.outpath.delete(0, Tkconstants.END)
+            self.outpath.insert(0, outpath)
+        return
+
+    def quitting(self):
+        # kill any still running subprocess
+        if self.p2 != None:
+            if (self.p2.wait('nowait') == None):
+                self.p2.terminate()
+        self.root.destroy()
+
+    # actually ready to run the subprocess and get its output
+    def convertit(self):
+        # now disable the button to prevent multiple launches
+        self.sbotton.configure(state='disabled')
+        pmlpath = self.pmlpath.get()
+        outpath = self.outpath.get()
+        if not pmlpath or not os.path.exists(pmlpath):
+            self.status['text'] = 'Specified eBook pml file does not exist'
+            self.sbotton.configure(state='normal')
+            return
+        if not outpath:
+            self.status['text'] = 'No output file specified'
+            self.sbotton.configure(state='normal')
+            return
+
+        log = 'Command = "python xpml2xhtml.py"\n'
+        log += 'PDB Path = "'+ pmlpath + '"\n'
+        log += 'HTML Output File = "' + outpath + '"\n'
+        log += '\n\n'
+        log += 'Please Wait ...\n\n'
+        self.stext.insert(Tkconstants.END,log)
+        self.p2 = self.pmlhtml(pmlpath, outpath)
+
+        # python does not seem to allow you to create
+        # your own eventloop which every other gui does - strange 
+        # so need to use the widget "after" command to force
+        # event loop to run non-gui events every interval
+        self.stext.after(self.interval,self.processPipe)
+        return
+
+
+def main(argv=None):
+    root = Tkinter.Tk()
+    root.title('eBook Pml to HTML Conversion')
+    root.resizable(True, False)
+    root.minsize(300, 0)
+    MainDialog(root).pack(fill=Tkconstants.X, expand=1)
+    root.mainloop()
+    return 0
+    
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/eReader_Tools/eReaderPDB2PML.pyw b/eReader_Tools/eReaderPDB2PML.pyw
new file mode 100644 (file)
index 0000000..cb1569c
--- /dev/null
@@ -0,0 +1,200 @@
+#!/usr/bin/env python
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+import sys
+sys.path.append('lib')
+
+import os, os.path, urllib
+import subprocess
+from subprocess import Popen, PIPE, STDOUT
+import Tkinter
+import Tkconstants
+import tkFileDialog
+import tkMessageBox
+import subasyncio
+from subasyncio import Process
+from scrolltextwidget import ScrolledText
+
+class MainDialog(Tkinter.Frame):
+    def __init__(self, root):
+        Tkinter.Frame.__init__(self, root, border=5)
+        self.root = root
+        self.interval = 2000
+        self.p2 = None
+        self.status = Tkinter.Label(self, text='eReader eBook Conversion')
+        self.status.pack(fill=Tkconstants.X, expand=1)
+        body = Tkinter.Frame(self)
+        body.pack(fill=Tkconstants.X, expand=1)
+        sticky = Tkconstants.E + Tkconstants.W
+        body.grid_columnconfigure(1, weight=2)
+
+        Tkinter.Label(body, text='eBook PDB input file').grid(row=0, sticky=Tkconstants.E)
+        self.pdbpath = Tkinter.Entry(body, width=50)
+        self.pdbpath.grid(row=0, column=1, sticky=sticky)
+        self.pdbpath.insert(0, os.getcwd())
+        button = Tkinter.Button(body, text="...", command=self.get_pdbpath)
+        button.grid(row=0, column=2)
+
+        Tkinter.Label(body, text='Output Directory').grid(row=1, sticky=Tkconstants.E)
+        self.outpath = Tkinter.Entry(body, width=50)
+        self.outpath.grid(row=1, column=1, sticky=sticky)
+        self.outpath.insert(0, os.getcwd())
+        button = Tkinter.Button(body, text="...", command=self.get_outpath)
+        button.grid(row=1, column=2)
+
+        Tkinter.Label(body, text='Name on CC').grid(row=2, sticky=Tkconstants.E)
+        self.name = Tkinter.StringVar()
+        self.nameinfo = Tkinter.Entry(body, width=40, textvariable=self.name)
+        self.nameinfo.grid(row=2, column=1, sticky=sticky)
+
+        Tkinter.Label(body, text='Last 8 digits of CC Number').grid(row=3, sticky=Tkconstants.E)
+        self.ccnum = Tkinter.StringVar()
+        self.ccinfo = Tkinter.Entry(body, width=10, textvariable=self.ccnum)
+        self.ccinfo.grid(row=3, column=1, sticky=sticky)
+
+        msg1 = 'Conversion Log \n\n'
+        self.stext = ScrolledText(body, bd=5, relief=Tkconstants.RIDGE, height=15, width=60, wrap=Tkconstants.WORD)
+        self.stext.grid(row=4, column=0, columnspan=2,sticky=sticky)
+        self.stext.insert(Tkconstants.END,msg1)
+
+        buttons = Tkinter.Frame(self)
+        buttons.pack()
+        self.sbotton = Tkinter.Button(
+            buttons, text="Start", width=10, command=self.convertit)
+        self.sbotton.pack(side=Tkconstants.LEFT)
+
+        Tkinter.Frame(buttons, width=10).pack(side=Tkconstants.LEFT)
+        self.qbutton = Tkinter.Button(
+            buttons, text="Quit", width=10, command=self.quitting)
+        self.qbutton.pack(side=Tkconstants.RIGHT)
+
+    # read from subprocess pipe without blocking
+    # invoked every interval via the widget "after"
+    # option being used, so need to reset it for the next time
+    def processPipe(self):
+        poll = self.p2.wait('nowait')
+        if poll != None: 
+            text = self.p2.readerr()
+            text += self.p2.read()
+            msg = text + '\n\n' + 'File successfully converted\n'
+            if poll != 0:
+                msg = text + '\n\n' + 'Error: Conversion Failed\n'
+            self.showCmdOutput(msg)
+            self.p2 = None
+            self.sbotton.configure(state='normal')
+            return
+        text = self.p2.readerr()
+        text += self.p2.read()
+        self.showCmdOutput(text)
+        # make sure we get invoked again by event loop after interval 
+        self.stext.after(self.interval,self.processPipe)
+        return
+
+    # post output from subprocess in scrolled text widget
+    def showCmdOutput(self, msg):
+        if msg and msg !='':
+            self.stext.insert(Tkconstants.END,msg)
+            self.stext.yview_pickplace(Tkconstants.END)
+        return
+
+    # run erdr2pml.py as a subprocess via pipes and collect stdout
+    def erdr(self, infile, outdir, name, ccnum):
+        # os.putenv('PYTHONUNBUFFERED', '1')
+        cmdline = 'python ./lib/erdr2pml.py "' + infile + '" "' + outdir + '" "' + name + '" ' + ccnum 
+        if sys.platform[0:3] == 'win':
+            search_path = os.environ['PATH']
+            search_path = search_path.lower()
+            if search_path.find('python') >= 0: 
+                cmdline = 'python lib\erdr2pml.py "' + infile + '" "' + outdir + '" "' + name + '" ' + ccnum
+            else :
+                cmdline = 'lib\erdr2pml.py "' + infile + '" "' + outdir + '" "' + name + '" ' + ccnum
+
+        p2 = Process(cmdline, shell=True, bufsize=1, stdin=None, stdout=PIPE, stderr=PIPE, close_fds=False)
+        return p2
+
+
+    def get_pdbpath(self):
+        pdbpath = tkFileDialog.askopenfilename(
+            parent=None, title='Select eReader PDB File',
+            defaultextension='.pdb', filetypes=[('eReader eBooks', '.pdb'),
+                                                ('All Files', '.*')])
+        if pdbpath:
+            pdbpath = os.path.normpath(pdbpath)
+            self.pdbpath.delete(0, Tkconstants.END)
+            self.pdbpath.insert(0, pdbpath)
+        return
+
+    def get_outpath(self):
+        outpath = tkFileDialog.askdirectory(
+            parent=None, title='Directory to Store Output into',
+            initialdir=os.getcwd(), initialfile=None)
+        if outpath:
+            outpath = os.path.normpath(outpath)
+            self.outpath.delete(0, Tkconstants.END)
+            self.outpath.insert(0, outpath)
+        return
+
+    def quitting(self):
+        # kill any still running subprocess
+        if self.p2 != None:
+            if (self.p2.wait('nowait') == None):
+                self.p2.terminate()
+        self.root.destroy()
+
+    # actually ready to run the subprocess and get its output
+    def convertit(self):
+        # now disable the button to prevent multiple launches
+        self.sbotton.configure(state='disabled')
+        pdbpath = self.pdbpath.get()
+        outpath = self.outpath.get()
+        if not pdbpath or not os.path.exists(pdbpath):
+            self.status['text'] = 'Specified eBook file does not exist'
+            self.sbotton.configure(state='normal')
+            return
+        if not outpath:
+            self.status['text'] = 'No output directory specified'
+            self.sbotton.configure(state='normal')
+            return
+        if not os.path.exists(outpath):
+            os.makedirs(outpath)
+        name = self.name.get()
+        if not name or name == '':
+            self.status['text'] = 'Your forgot to enter the Name on the CC'
+            self.sbotton.configure(state='normal')
+            return
+        ccnum = self.ccnum.get()
+        if not ccnum or ccnum == '':
+            self.status['text'] = 'Your forgot to enter the last 8 digits on the CC'
+            self.sbotton.configure(state='normal')
+            return
+
+        log = 'Command = "python erdr2pml.py"\n'
+        log += 'PDB Path = "'+ pdbpath + '"\n'
+        log += 'Output Directory = "' + outpath + '"\n'
+        log += 'Name = "' + name + '"\n'
+        log += 'Last 8 of CC = "' + ccnum + '"\n'
+        log += '\n\n'
+        log += 'Please Wait ...\n'
+        self.stext.insert(Tkconstants.END,log)
+        self.p2 = self.erdr(pdbpath, outpath, name, ccnum)
+
+        # python does not seem to allow you to create
+        # your own eventloop which every other gui does - strange 
+        # so need to use the widget "after" command to force
+        # event loop to run non-gui events every interval
+        self.stext.after(self.interval,self.processPipe)
+        return
+
+
+def main(argv=None):
+    root = Tkinter.Tk()
+    root.title('eReader PDB to PML Conversion')
+    root.resizable(True, False)
+    root.minsize(300, 0)
+    MainDialog(root).pack(fill=Tkconstants.X, expand=1)
+    root.mainloop()
+    return 0
+    
+
+if __name__ == "__main__":
+    sys.exit(main())
index 6bae3f55d5d7f61b430db7387b09cc4f1bf3e35c..3e9133a57c98570ec5083d3df1f76fd3ad6c6972 100644 (file)
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 # vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
 #
-# eRdr2Pml.py
+# erdr2pml.py
 #
 # This is a python script. You need a Python interpreter to run it.
 # For example, ActiveState Python, which exists for windows.
 #  0.09 - fixed typos in first_pages to first_page to again support older formats
 #  0.10 - minor cleanups
 #  0.11 - fixups for using correct xml for footnotes and sidebars for use with Dropbook
-#  0.12 - fixup for file name cleaning - no longer converts to lower case
+#  0.12 - Fix added to prevent lowercasing of image names when the pml code itself uses a different case in the link name.
+#  0.13 - change to unbuffered stdout for use with gui front ends
 
-__version__='0.12'
+__version__='0.13'
 
 # Import Psyco if available
 try:
@@ -72,7 +73,20 @@ try:
 except ImportError:
     pass
 
-import struct, binascii, zlib, os, sys, os.path, urllib
+class Unbuffered:
+    def __init__(self, stream):
+        self.stream = stream
+    def write(self, data):
+        self.stream.write(data)
+        self.stream.flush()
+    def __getattr__(self, attr):
+        return getattr(self.stream, attr)
+
+import sys
+sys.stdout=Unbuffered(sys.stdout)
+
+import struct, binascii, zlib, os, os.path, urllib
+
 try:
     from hashlib import sha1
 except ImportError:
@@ -85,7 +99,6 @@ import logging
 logging.basicConfig()
 #logging.basicConfig(level=logging.DEBUG)
 
-
 ECB =  0
 CBC =  1
 class Des(object):
@@ -593,6 +606,7 @@ def main(argv=None):
         print "Note:"
         print "  if ommitted, outdir defaults based on 'infile.pdb'"
         print "  It's enough to enter the last 8 digits of the credit card number"
+        return 1
     else:
         if len(argv)==4:
             infile, name, cc = argv[1], argv[2], argv[3]
@@ -613,6 +627,8 @@ def main(argv=None):
             print "done"
         except ValueError, e:
             print "Error: %s" % e
+            return 1
+    return 0
 
 if __name__ == "__main__":
     #import cProfile
diff --git a/eReader_Tools/lib/scrolltextwidget.py b/eReader_Tools/lib/scrolltextwidget.py
new file mode 100644 (file)
index 0000000..98b4147
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+import Tkinter
+import Tkconstants
+
+# basic scrolled text widget
+class ScrolledText(Tkinter.Text):
+    def __init__(self, master=None, **kw):
+        self.frame = Tkinter.Frame(master)
+        self.vbar = Tkinter.Scrollbar(self.frame)
+        self.vbar.pack(side=Tkconstants.RIGHT, fill=Tkconstants.Y)
+        kw.update({'yscrollcommand': self.vbar.set})
+        Tkinter.Text.__init__(self, self.frame, **kw)
+        self.pack(side=Tkconstants.LEFT, fill=Tkconstants.BOTH, expand=True)
+        self.vbar['command'] = self.yview
+        # Copy geometry methods of self.frame without overriding Text
+        # methods = hack!
+        text_meths = vars(Tkinter.Text).keys()
+        methods = vars(Tkinter.Pack).keys() + vars(Tkinter.Grid).keys() + vars(Tkinter.Place).keys()
+        methods = set(methods).difference(text_meths)
+        for m in methods:
+            if m[0] != '_' and m != 'config' and m != 'configure':
+                setattr(self, m, getattr(self.frame, m))
+
+    def __str__(self):
+        return str(self.frame)
diff --git a/eReader_Tools/lib/subasyncio.py b/eReader_Tools/lib/subasyncio.py
new file mode 100644 (file)
index 0000000..ed13aa1
--- /dev/null
@@ -0,0 +1,149 @@
+#!/usr/bin/env python
+# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
+
+import os, sys
+import signal
+import threading
+import subprocess
+from subprocess import Popen, PIPE, STDOUT
+
+# **heavily** chopped up and modfied version of asyncproc.py
+# to make it actually work on Windows as well as Mac/Linux
+# For the original see:
+# "http://www.lysator.liu.se/~bellman/download/"
+# author is  "Thomas Bellman <bellman@lysator.liu.se>"
+# available under GPL version 3 or Later
+
+# create an asynchronous subprocess whose output can be collected in
+# a non-blocking manner
+
+# What a mess!  Have to use threads just to get non-blocking io
+# in a cross-platform manner
+
+# luckily all thread use is hidden within this class
+
+class Process(object):
+    def __init__(self, *params, **kwparams):
+        if len(params) <= 3:
+            kwparams.setdefault('stdin', subprocess.PIPE)
+        if len(params) <= 4:
+            kwparams.setdefault('stdout', subprocess.PIPE)
+        if len(params) <= 5:
+            kwparams.setdefault('stderr', subprocess.PIPE)
+        self.__pending_input = []
+        self.__collected_outdata = []
+        self.__collected_errdata = []
+        self.__exitstatus = None
+        self.__lock = threading.Lock()
+        self.__inputsem = threading.Semaphore(0)
+        self.__quit = False
+
+        self.__process = subprocess.Popen(*params, **kwparams)
+
+        if self.__process.stdin:
+            self.__stdin_thread = threading.Thread(
+                name="stdin-thread",
+                target=self.__feeder, args=(self.__pending_input,
+                                            self.__process.stdin))
+            self.__stdin_thread.setDaemon(True)
+            self.__stdin_thread.start()
+
+        if self.__process.stdout:
+            self.__stdout_thread = threading.Thread(
+                name="stdout-thread",
+                target=self.__reader, args=(self.__collected_outdata,
+                                           self.__process.stdout))
+            self.__stdout_thread.setDaemon(True)
+            self.__stdout_thread.start()
+
+        if self.__process.stderr:
+            self.__stderr_thread = threading.Thread(
+                name="stderr-thread",
+                target=self.__reader, args=(self.__collected_errdata,
+                                           self.__process.stderr))
+            self.__stderr_thread.setDaemon(True)
+            self.__stderr_thread.start()
+
+    def pid(self):
+        return self.__process.pid
+
+    def kill(self, signal):
+        self.__process.send_signal(signal)
+
+    # check on subprocess (pass in 'nowait') to act like poll
+    def wait(self, flag):
+        if flag.lower() == 'nowait':
+            rc = self.__process.poll()
+        else:
+            rc = self.__process.wait()
+        if rc != None:
+            if self.__process.stdin:
+                self.closeinput()
+            if self.__process.stdout:
+                self.__stdout_thread.join()
+            if self.__process.stderr:
+                self.__stderr_thread.join()
+        return self.__process.returncode
+
+    def terminate(self):
+        if self.__process.stdin:
+            self.closeinput()
+        self.__process.terminate()
+
+    # thread gets data from subprocess stdout
+    def __reader(self, collector, source):
+        while True:
+            data = os.read(source.fileno(), 65536)
+            self.__lock.acquire()
+            collector.append(data)
+            self.__lock.release()
+            if data == "":
+                source.close()
+                break
+        return
+
+    # thread feeds data to subprocess stdin
+    def __feeder(self, pending, drain):
+        while True:
+            self.__inputsem.acquire()
+            self.__lock.acquire()
+            if not pending  and self.__quit:
+                drain.close()
+                self.__lock.release()
+                break
+            data = pending.pop(0)
+            self.__lock.release()
+            drain.write(data)
+
+    # non-blocking read of data from subprocess stdout
+    def read(self):
+        self.__lock.acquire()
+        outdata = "".join(self.__collected_outdata)
+        del self.__collected_outdata[:]
+        self.__lock.release()
+        return outdata
+
+    # non-blocking read of data from subprocess stderr
+    def readerr(self):
+        self.__lock.acquire()
+        errdata = "".join(self.__collected_errdata)
+        del self.__collected_errdata[:]
+        self.__lock.release()
+        return errdata
+
+    # non-blocking write to stdin of subprocess
+    def write(self, data):
+        if self.__process.stdin is None:
+            raise ValueError("Writing to process with stdin not a pipe")
+        self.__lock.acquire()
+        self.__pending_input.append(data)
+        self.__inputsem.release()
+        self.__lock.release()
+
+    # close stdinput of subprocess
+    def closeinput(self):
+        self.__lock.acquire()
+        self.__quit = True
+        self.__inputsem.release()
+        self.__lock.release()
+
index f4ceef4f6396e48dfdab6386f5ff4decf0851883..c449f2f9fb2d27bac1d35463ec5885fdf8cd4c4e 100644 (file)
 #  0.16 - use proper and safe temporary file when passing things to tidy
 #  0.17 - add support for tidy.exe under windows
 #  0.18 - fix corner case of lines that start with \axxx or \Uxxxx tags
+#  0.19 - change to use auto flushed stdout, and use proper return values
 
-__version__='0.18'
+__version__='0.19'
 
-import struct, binascii, zlib, os, getopt, sys, os.path, urllib, re, tempfile
+class Unbuffered:
+    def __init__(self, stream):
+        self.stream = stream
+    def write(self, data):
+        self.stream.write(data)
+        self.stream.flush()
+    def __getattr__(self, attr):
+        return getattr(self.stream, attr)
+
+import sys
+sys.stdout=Unbuffered(sys.stdout)
+
+import struct, binascii, zlib, os, getopt, os.path, urllib, re, tempfile
 import logging
 from subprocess import Popen, PIPE, STDOUT
 
@@ -790,10 +803,10 @@ def main(argv=None):
     except getopt.GetoptError, err:
         print str(err)
         usage()
-        return 2
+        return 1
     if len(args) != 2:
         usage()
-        return 2
+        return 1
     sigil_breaks = False
     use_tidy = False
     for o, a in opts:
@@ -832,7 +845,7 @@ def main(argv=None):
         print "Finished Processing"
     except ValueError, e:
         print "Error: %s" % e
-        return 2
+        return 1
     return 0
 
 if __name__ == "__main__":