From 602ff30b3af38ff3ad2ecb80010afe2aff131f1d Mon Sep 17 00:00:00 2001 From: Apprentice Alf Date: Wed, 26 Dec 2012 23:17:56 +0000 Subject: [PATCH] tools v5.5.3 --- .../K4MobiDeDRM_plugin/__init__.py | 3 ++- .../K4MobiDeDRM_plugin/convert2xml.py | 24 ++++++++++-------- .../K4MobiDeDRM_plugin/stylexml2css.py | 15 ++++++++++- .../K4MobiDeDRM_plugin/topazextract.py | 13 +++++++--- Calibre_Plugins/k4mobidedrm_plugin.zip | Bin 230640 -> 230654 bytes DeDRM_Macintosh_Application/DeDRM ReadMe.rtf | 2 +- .../DeDRM.app/Contents/Info.plist | 6 ++--- .../Contents/Resources/convert2xml.py | 24 ++++++++++-------- .../Contents/Resources/getk4pcpids.py | 12 ++++++--- .../Contents/Resources/stylexml2css.py | 15 ++++++++++- .../Contents/Resources/topazextract.py | 13 +++++++--- .../DeDRM_App/DeDRM_lib/DeDRM_app.pyw | 6 ++--- .../DeDRM_App/DeDRM_lib/lib/convert2xml.py | 24 ++++++++++-------- .../DeDRM_App/DeDRM_lib/lib/getk4pcpids.py | 12 ++++++--- .../DeDRM_App/DeDRM_lib/lib/stylexml2css.py | 15 ++++++++++- .../DeDRM_App/DeDRM_lib/lib/topazextract.py | 13 +++++++--- DeDRM_Windows_Application/DeDRM_ReadMe.txt | 8 +++--- 17 files changed, 142 insertions(+), 63 deletions(-) diff --git a/Calibre_Plugins/K4MobiDeDRM_plugin/__init__.py b/Calibre_Plugins/K4MobiDeDRM_plugin/__init__.py index e5a1702..46b57c9 100644 --- a/Calibre_Plugins/K4MobiDeDRM_plugin/__init__.py +++ b/Calibre_Plugins/K4MobiDeDRM_plugin/__init__.py @@ -21,13 +21,14 @@ __docformat__ = 'restructuredtext en' # 0.4.10 - Another Topaz Fix (class added to page and group and region) # 0.4.11 - Fixed Linux support of K4PC # 0.4.12 - More Linux Wine fixes +# 0.4.13 - Ancient Mobipocket files fix """ Decrypt Amazon Kindle and Mobipocket encrypted ebooks. """ PLUGIN_NAME = u"Kindle and Mobipocket DeDRM" -PLUGIN_VERSION_TUPLE = (0, 4, 12) +PLUGIN_VERSION_TUPLE = (0, 4, 13) PLUGIN_VERSION = '.'.join([str(x) for x in PLUGIN_VERSION_TUPLE]) import sys, os, re diff --git a/Calibre_Plugins/K4MobiDeDRM_plugin/convert2xml.py b/Calibre_Plugins/K4MobiDeDRM_plugin/convert2xml.py index 0f64a1b..6c8fa83 100644 --- a/Calibre_Plugins/K4MobiDeDRM_plugin/convert2xml.py +++ b/Calibre_Plugins/K4MobiDeDRM_plugin/convert2xml.py @@ -277,6 +277,7 @@ class PageParser(object): 'word_semantic' : (1, 'snippets', 1, 1), 'word_semantic.type' : (1, 'scalar_text', 0, 0), + 'word_semantic.class' : (1, 'scalar_text', 0, 0), 'word_semantic.firstWord' : (1, 'scalar_number', 0, 0), 'word_semantic.lastWord' : (1, 'scalar_number', 0, 0), @@ -287,6 +288,7 @@ class PageParser(object): 'word.lastGlyph' : (1, 'scalar_number', 0, 0), '_span' : (1, 'snippets', 1, 0), + '_span.class' : (1, 'scalar_text', 0, 0), '_span.firstWord' : (1, 'scalar_number', 0, 0), '_span.lastWord' : (1, 'scalar_number', 0, 0), '_span.gridSize' : (1, 'scalar_number', 0, 0), @@ -350,16 +352,18 @@ class PageParser(object): 'version.paragraph_continuation' : (1, 'scalar_text', 0, 0), 'version.toc' : (1, 'scalar_text', 0, 0), - 'stylesheet' : (1, 'snippets', 1, 0), - 'style' : (1, 'snippets', 1, 0), - 'style._tag' : (1, 'scalar_text', 0, 0), - 'style.type' : (1, 'scalar_text', 0, 0), - 'style._parent_type' : (1, 'scalar_text', 0, 0), - 'style.class' : (1, 'scalar_text', 0, 0), - 'style._after_class' : (1, 'scalar_text', 0, 0), - 'rule' : (1, 'snippets', 1, 0), - 'rule.attr' : (1, 'scalar_text', 0, 0), - 'rule.value' : (1, 'scalar_text', 0, 0), + 'stylesheet' : (1, 'snippets', 1, 0), + 'style' : (1, 'snippets', 1, 0), + 'style._tag' : (1, 'scalar_text', 0, 0), + 'style.type' : (1, 'scalar_text', 0, 0), + 'style._after_type' : (1, 'scalar_text', 0, 0), + 'style._parent_type' : (1, 'scalar_text', 0, 0), + 'style._after_parent_type' : (1, 'scalar_text', 0, 0), + 'style.class' : (1, 'scalar_text', 0, 0), + 'style._after_class' : (1, 'scalar_text', 0, 0), + 'rule' : (1, 'snippets', 1, 0), + 'rule.attr' : (1, 'scalar_text', 0, 0), + 'rule.value' : (1, 'scalar_text', 0, 0), 'original' : (0, 'number', 1, 1), 'original.pnum' : (1, 'number', 0, 0), diff --git a/Calibre_Plugins/K4MobiDeDRM_plugin/stylexml2css.py b/Calibre_Plugins/K4MobiDeDRM_plugin/stylexml2css.py index 2347f6a..c111850 100644 --- a/Calibre_Plugins/K4MobiDeDRM_plugin/stylexml2css.py +++ b/Calibre_Plugins/K4MobiDeDRM_plugin/stylexml2css.py @@ -10,6 +10,7 @@ import re from struct import pack from struct import unpack +debug = False class DocParser(object): def __init__(self, flatxml, fontsize, ph, pw): @@ -113,7 +114,9 @@ class DocParser(object): # process each style converting what you can + if debug: print ' ', 'Processing styles.' for j in xrange(stylecnt): + if debug: print ' ', 'Processing style %d' %(j) start = styleList[j] end = styleList[j+1] @@ -132,6 +135,8 @@ class DocParser(object): else : sclass = '' + if debug: print 'sclass', sclass + # check for any "after class" specifiers (pos, aftclass) = self.findinDoc('style._after_class',start,end) if aftclass != None: @@ -140,6 +145,8 @@ class DocParser(object): else : aftclass = '' + if debug: print 'aftclass', aftclass + cssargs = {} while True : @@ -147,6 +154,9 @@ class DocParser(object): (pos1, attr) = self.findinDoc('style.rule.attr', start, end) (pos2, val) = self.findinDoc('style.rule.value', start, end) + if debug: print 'attr', attr + if debug: print 'val', val + if attr == None : break if (attr == 'display') or (attr == 'pos') or (attr == 'align'): @@ -164,7 +174,7 @@ class DocParser(object): scale = self.pw elif attr == 'line-space': scale = self.fontsize * 2.0 - + if val == "": val = 0 @@ -179,6 +189,7 @@ class DocParser(object): if aftclass != "" : keep = False if keep : + if debug: print 'keeping style' # make sure line-space does not go below 100% or above 300% since # it can be wacky in some styles if 'line-space' in cssargs: @@ -256,7 +267,9 @@ def convert2CSS(flatxml, fontsize, ph, pw): # create a document parser dp = DocParser(flatxml, fontsize, ph, pw) + if debug: print ' ', 'Created DocParser.' csspage = dp.process() + if debug: print ' ', 'Processed DocParser.' return csspage diff --git a/Calibre_Plugins/K4MobiDeDRM_plugin/topazextract.py b/Calibre_Plugins/K4MobiDeDRM_plugin/topazextract.py index a343922..3e4db39 100644 --- a/Calibre_Plugins/K4MobiDeDRM_plugin/topazextract.py +++ b/Calibre_Plugins/K4MobiDeDRM_plugin/topazextract.py @@ -69,6 +69,9 @@ def unicode_argv(): argvencoding = 'utf-8' return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv] +#global switch +debug = False + if 'calibre' in sys.modules: inCalibre = True from calibre_plugins.k4mobidedrm import kgenpids @@ -206,6 +209,7 @@ class TopazBook: # Read and return the data of one header record at the current book file position # [[offset,decompressedLength,compressedLength],...] nbValues = bookReadEncodedNumber(self.fo) + if debug: print "%d records in header " % nbValues, values = [] for i in range (0,nbValues): values.append([bookReadEncodedNumber(self.fo),bookReadEncodedNumber(self.fo),bookReadEncodedNumber(self.fo)]) @@ -219,9 +223,10 @@ class TopazBook: record = bookReadHeaderRecordData() return [tag,record] nbRecords = bookReadEncodedNumber(self.fo) + if debug: print "Headers: %d" % nbRecords for i in range (0,nbRecords): result = parseTopazHeaderRecord() - # print result[0], result[1] + if debug: print result[0], ": ", result[1] self.bookHeaderRecords[result[0]] = result[1] if ord(self.fo.read(1)) != 0x64 : raise DrmException(u"Parse Error : Invalid Header") @@ -235,12 +240,12 @@ class TopazBook: raise DrmException(u"Parse Error : Record Names Don't Match") flags = ord(self.fo.read(1)) nbRecords = ord(self.fo.read(1)) - # print nbRecords + if debug: print "Metadata Records: %d" % nbRecords for i in range (0,nbRecords) : keyval = bookReadString(self.fo) content = bookReadString(self.fo) - # print keyval - # print content + if debug: print keyval + if debug: print content self.bookMetadata[keyval] = content return self.bookMetadata diff --git a/Calibre_Plugins/k4mobidedrm_plugin.zip b/Calibre_Plugins/k4mobidedrm_plugin.zip index 9c00bfea26f49c6fceef326db423a46237d0bc45..37436f659be8f7074febd5354355d436f7191492 100644 GIT binary patch delta 24740 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 droplet CFBundleGetInfoString - DeDRM 5.5. AppleScript written 2010–2012 by Apprentice Alf and others. + DeDRM 5.5.3. AppleScript written 2010–2012 by Apprentice Alf and others. CFBundleIconFile DeDRM CFBundleInfoDictionaryVersion 6.0 CFBundleName - DeDRM 5.5 + DeDRM 5.5.3 CFBundlePackageType APPL CFBundleShortVersionString - 5.5 + 5.5.3 CFBundleSignature dplt LSRequiresCarbon diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/convert2xml.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/convert2xml.py index 0f64a1b..6c8fa83 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/convert2xml.py +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/convert2xml.py @@ -277,6 +277,7 @@ class PageParser(object): 'word_semantic' : (1, 'snippets', 1, 1), 'word_semantic.type' : (1, 'scalar_text', 0, 0), + 'word_semantic.class' : (1, 'scalar_text', 0, 0), 'word_semantic.firstWord' : (1, 'scalar_number', 0, 0), 'word_semantic.lastWord' : (1, 'scalar_number', 0, 0), @@ -287,6 +288,7 @@ class PageParser(object): 'word.lastGlyph' : (1, 'scalar_number', 0, 0), '_span' : (1, 'snippets', 1, 0), + '_span.class' : (1, 'scalar_text', 0, 0), '_span.firstWord' : (1, 'scalar_number', 0, 0), '_span.lastWord' : (1, 'scalar_number', 0, 0), '_span.gridSize' : (1, 'scalar_number', 0, 0), @@ -350,16 +352,18 @@ class PageParser(object): 'version.paragraph_continuation' : (1, 'scalar_text', 0, 0), 'version.toc' : (1, 'scalar_text', 0, 0), - 'stylesheet' : (1, 'snippets', 1, 0), - 'style' : (1, 'snippets', 1, 0), - 'style._tag' : (1, 'scalar_text', 0, 0), - 'style.type' : (1, 'scalar_text', 0, 0), - 'style._parent_type' : (1, 'scalar_text', 0, 0), - 'style.class' : (1, 'scalar_text', 0, 0), - 'style._after_class' : (1, 'scalar_text', 0, 0), - 'rule' : (1, 'snippets', 1, 0), - 'rule.attr' : (1, 'scalar_text', 0, 0), - 'rule.value' : (1, 'scalar_text', 0, 0), + 'stylesheet' : (1, 'snippets', 1, 0), + 'style' : (1, 'snippets', 1, 0), + 'style._tag' : (1, 'scalar_text', 0, 0), + 'style.type' : (1, 'scalar_text', 0, 0), + 'style._after_type' : (1, 'scalar_text', 0, 0), + 'style._parent_type' : (1, 'scalar_text', 0, 0), + 'style._after_parent_type' : (1, 'scalar_text', 0, 0), + 'style.class' : (1, 'scalar_text', 0, 0), + 'style._after_class' : (1, 'scalar_text', 0, 0), + 'rule' : (1, 'snippets', 1, 0), + 'rule.attr' : (1, 'scalar_text', 0, 0), + 'rule.value' : (1, 'scalar_text', 0, 0), 'original' : (0, 'number', 1, 1), 'original.pnum' : (1, 'number', 0, 0), diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/getk4pcpids.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/getk4pcpids.py index cc8bcd4..1614a53 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/getk4pcpids.py +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/getk4pcpids.py @@ -11,15 +11,21 @@ __version__ = '1.01' import sys -class Unbuffered: +class SafeUnbuffered: def __init__(self, stream): self.stream = stream + self.encoding = stream.encoding + if self.encoding == None: + self.encoding = "utf-8" def write(self, data): + if isinstance(data,unicode): + data = data.encode(self.encoding,"replace") self.stream.write(data) self.stream.flush() def __getattr__(self, attr): return getattr(self.stream, attr) -sys.stdout=Unbuffered(sys.stdout) +sys.stdout=SafeUnbuffered(sys.stdout) +sys.stderr=SafeUnbuffered(sys.stderr) import os import struct @@ -41,7 +47,7 @@ def getK4PCpids(path_to_ebook): mobi = False if mobi: - mb = mobidedrm.MobiBook(path_to_ebook,False) + mb = mobidedrm.MobiBook(path_to_ebook) else: mb = topazextract.TopazBook(path_to_ebook) diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/stylexml2css.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/stylexml2css.py index 2347f6a..c111850 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/stylexml2css.py +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/stylexml2css.py @@ -10,6 +10,7 @@ import re from struct import pack from struct import unpack +debug = False class DocParser(object): def __init__(self, flatxml, fontsize, ph, pw): @@ -113,7 +114,9 @@ class DocParser(object): # process each style converting what you can + if debug: print ' ', 'Processing styles.' for j in xrange(stylecnt): + if debug: print ' ', 'Processing style %d' %(j) start = styleList[j] end = styleList[j+1] @@ -132,6 +135,8 @@ class DocParser(object): else : sclass = '' + if debug: print 'sclass', sclass + # check for any "after class" specifiers (pos, aftclass) = self.findinDoc('style._after_class',start,end) if aftclass != None: @@ -140,6 +145,8 @@ class DocParser(object): else : aftclass = '' + if debug: print 'aftclass', aftclass + cssargs = {} while True : @@ -147,6 +154,9 @@ class DocParser(object): (pos1, attr) = self.findinDoc('style.rule.attr', start, end) (pos2, val) = self.findinDoc('style.rule.value', start, end) + if debug: print 'attr', attr + if debug: print 'val', val + if attr == None : break if (attr == 'display') or (attr == 'pos') or (attr == 'align'): @@ -164,7 +174,7 @@ class DocParser(object): scale = self.pw elif attr == 'line-space': scale = self.fontsize * 2.0 - + if val == "": val = 0 @@ -179,6 +189,7 @@ class DocParser(object): if aftclass != "" : keep = False if keep : + if debug: print 'keeping style' # make sure line-space does not go below 100% or above 300% since # it can be wacky in some styles if 'line-space' in cssargs: @@ -256,7 +267,9 @@ def convert2CSS(flatxml, fontsize, ph, pw): # create a document parser dp = DocParser(flatxml, fontsize, ph, pw) + if debug: print ' ', 'Created DocParser.' csspage = dp.process() + if debug: print ' ', 'Processed DocParser.' return csspage diff --git a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/topazextract.py b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/topazextract.py index a343922..3e4db39 100644 --- a/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/topazextract.py +++ b/DeDRM_Macintosh_Application/DeDRM.app/Contents/Resources/topazextract.py @@ -69,6 +69,9 @@ def unicode_argv(): argvencoding = 'utf-8' return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv] +#global switch +debug = False + if 'calibre' in sys.modules: inCalibre = True from calibre_plugins.k4mobidedrm import kgenpids @@ -206,6 +209,7 @@ class TopazBook: # Read and return the data of one header record at the current book file position # [[offset,decompressedLength,compressedLength],...] nbValues = bookReadEncodedNumber(self.fo) + if debug: print "%d records in header " % nbValues, values = [] for i in range (0,nbValues): values.append([bookReadEncodedNumber(self.fo),bookReadEncodedNumber(self.fo),bookReadEncodedNumber(self.fo)]) @@ -219,9 +223,10 @@ class TopazBook: record = bookReadHeaderRecordData() return [tag,record] nbRecords = bookReadEncodedNumber(self.fo) + if debug: print "Headers: %d" % nbRecords for i in range (0,nbRecords): result = parseTopazHeaderRecord() - # print result[0], result[1] + if debug: print result[0], ": ", result[1] self.bookHeaderRecords[result[0]] = result[1] if ord(self.fo.read(1)) != 0x64 : raise DrmException(u"Parse Error : Invalid Header") @@ -235,12 +240,12 @@ class TopazBook: raise DrmException(u"Parse Error : Record Names Don't Match") flags = ord(self.fo.read(1)) nbRecords = ord(self.fo.read(1)) - # print nbRecords + if debug: print "Metadata Records: %d" % nbRecords for i in range (0,nbRecords) : keyval = bookReadString(self.fo) content = bookReadString(self.fo) - # print keyval - # print content + if debug: print keyval + if debug: print content self.bookMetadata[keyval] = content return self.bookMetadata diff --git a/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/DeDRM_app.pyw b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/DeDRM_app.pyw index d0a2bea..23cc30a 100644 --- a/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/DeDRM_app.pyw +++ b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/DeDRM_app.pyw @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# DeDRM.pyw, version 5.5 +# DeDRM.pyw, version 5.5.3 # By some_updates and Apprentice Alf import sys @@ -24,7 +24,7 @@ import re import simpleprefs -__version__ = '5.5' +__version__ = '5.5.3' class DrmException(Exception): pass @@ -441,10 +441,10 @@ class ConvDialog(Toplevel): self.log += text self.log += msg else: + msg = u"\nFailed\n" text = self.p2.read().decode('utf8') text += self.p2.readerr().decode('utf8') msg += text - msg += u"\nFailed\n" self.numbad += 1 self.log += msg self.showCmdOutput(msg) diff --git a/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/convert2xml.py b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/convert2xml.py index 0f64a1b..6c8fa83 100644 --- a/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/convert2xml.py +++ b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/convert2xml.py @@ -277,6 +277,7 @@ class PageParser(object): 'word_semantic' : (1, 'snippets', 1, 1), 'word_semantic.type' : (1, 'scalar_text', 0, 0), + 'word_semantic.class' : (1, 'scalar_text', 0, 0), 'word_semantic.firstWord' : (1, 'scalar_number', 0, 0), 'word_semantic.lastWord' : (1, 'scalar_number', 0, 0), @@ -287,6 +288,7 @@ class PageParser(object): 'word.lastGlyph' : (1, 'scalar_number', 0, 0), '_span' : (1, 'snippets', 1, 0), + '_span.class' : (1, 'scalar_text', 0, 0), '_span.firstWord' : (1, 'scalar_number', 0, 0), '_span.lastWord' : (1, 'scalar_number', 0, 0), '_span.gridSize' : (1, 'scalar_number', 0, 0), @@ -350,16 +352,18 @@ class PageParser(object): 'version.paragraph_continuation' : (1, 'scalar_text', 0, 0), 'version.toc' : (1, 'scalar_text', 0, 0), - 'stylesheet' : (1, 'snippets', 1, 0), - 'style' : (1, 'snippets', 1, 0), - 'style._tag' : (1, 'scalar_text', 0, 0), - 'style.type' : (1, 'scalar_text', 0, 0), - 'style._parent_type' : (1, 'scalar_text', 0, 0), - 'style.class' : (1, 'scalar_text', 0, 0), - 'style._after_class' : (1, 'scalar_text', 0, 0), - 'rule' : (1, 'snippets', 1, 0), - 'rule.attr' : (1, 'scalar_text', 0, 0), - 'rule.value' : (1, 'scalar_text', 0, 0), + 'stylesheet' : (1, 'snippets', 1, 0), + 'style' : (1, 'snippets', 1, 0), + 'style._tag' : (1, 'scalar_text', 0, 0), + 'style.type' : (1, 'scalar_text', 0, 0), + 'style._after_type' : (1, 'scalar_text', 0, 0), + 'style._parent_type' : (1, 'scalar_text', 0, 0), + 'style._after_parent_type' : (1, 'scalar_text', 0, 0), + 'style.class' : (1, 'scalar_text', 0, 0), + 'style._after_class' : (1, 'scalar_text', 0, 0), + 'rule' : (1, 'snippets', 1, 0), + 'rule.attr' : (1, 'scalar_text', 0, 0), + 'rule.value' : (1, 'scalar_text', 0, 0), 'original' : (0, 'number', 1, 1), 'original.pnum' : (1, 'number', 0, 0), diff --git a/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/getk4pcpids.py b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/getk4pcpids.py index cc8bcd4..1614a53 100644 --- a/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/getk4pcpids.py +++ b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/getk4pcpids.py @@ -11,15 +11,21 @@ __version__ = '1.01' import sys -class Unbuffered: +class SafeUnbuffered: def __init__(self, stream): self.stream = stream + self.encoding = stream.encoding + if self.encoding == None: + self.encoding = "utf-8" def write(self, data): + if isinstance(data,unicode): + data = data.encode(self.encoding,"replace") self.stream.write(data) self.stream.flush() def __getattr__(self, attr): return getattr(self.stream, attr) -sys.stdout=Unbuffered(sys.stdout) +sys.stdout=SafeUnbuffered(sys.stdout) +sys.stderr=SafeUnbuffered(sys.stderr) import os import struct @@ -41,7 +47,7 @@ def getK4PCpids(path_to_ebook): mobi = False if mobi: - mb = mobidedrm.MobiBook(path_to_ebook,False) + mb = mobidedrm.MobiBook(path_to_ebook) else: mb = topazextract.TopazBook(path_to_ebook) diff --git a/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/stylexml2css.py b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/stylexml2css.py index 2347f6a..c111850 100644 --- a/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/stylexml2css.py +++ b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/stylexml2css.py @@ -10,6 +10,7 @@ import re from struct import pack from struct import unpack +debug = False class DocParser(object): def __init__(self, flatxml, fontsize, ph, pw): @@ -113,7 +114,9 @@ class DocParser(object): # process each style converting what you can + if debug: print ' ', 'Processing styles.' for j in xrange(stylecnt): + if debug: print ' ', 'Processing style %d' %(j) start = styleList[j] end = styleList[j+1] @@ -132,6 +135,8 @@ class DocParser(object): else : sclass = '' + if debug: print 'sclass', sclass + # check for any "after class" specifiers (pos, aftclass) = self.findinDoc('style._after_class',start,end) if aftclass != None: @@ -140,6 +145,8 @@ class DocParser(object): else : aftclass = '' + if debug: print 'aftclass', aftclass + cssargs = {} while True : @@ -147,6 +154,9 @@ class DocParser(object): (pos1, attr) = self.findinDoc('style.rule.attr', start, end) (pos2, val) = self.findinDoc('style.rule.value', start, end) + if debug: print 'attr', attr + if debug: print 'val', val + if attr == None : break if (attr == 'display') or (attr == 'pos') or (attr == 'align'): @@ -164,7 +174,7 @@ class DocParser(object): scale = self.pw elif attr == 'line-space': scale = self.fontsize * 2.0 - + if val == "": val = 0 @@ -179,6 +189,7 @@ class DocParser(object): if aftclass != "" : keep = False if keep : + if debug: print 'keeping style' # make sure line-space does not go below 100% or above 300% since # it can be wacky in some styles if 'line-space' in cssargs: @@ -256,7 +267,9 @@ def convert2CSS(flatxml, fontsize, ph, pw): # create a document parser dp = DocParser(flatxml, fontsize, ph, pw) + if debug: print ' ', 'Created DocParser.' csspage = dp.process() + if debug: print ' ', 'Processed DocParser.' return csspage diff --git a/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/topazextract.py b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/topazextract.py index a343922..3e4db39 100644 --- a/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/topazextract.py +++ b/DeDRM_Windows_Application/DeDRM_App/DeDRM_lib/lib/topazextract.py @@ -69,6 +69,9 @@ def unicode_argv(): argvencoding = 'utf-8' return [arg if (type(arg) == unicode) else unicode(arg,argvencoding) for arg in sys.argv] +#global switch +debug = False + if 'calibre' in sys.modules: inCalibre = True from calibre_plugins.k4mobidedrm import kgenpids @@ -206,6 +209,7 @@ class TopazBook: # Read and return the data of one header record at the current book file position # [[offset,decompressedLength,compressedLength],...] nbValues = bookReadEncodedNumber(self.fo) + if debug: print "%d records in header " % nbValues, values = [] for i in range (0,nbValues): values.append([bookReadEncodedNumber(self.fo),bookReadEncodedNumber(self.fo),bookReadEncodedNumber(self.fo)]) @@ -219,9 +223,10 @@ class TopazBook: record = bookReadHeaderRecordData() return [tag,record] nbRecords = bookReadEncodedNumber(self.fo) + if debug: print "Headers: %d" % nbRecords for i in range (0,nbRecords): result = parseTopazHeaderRecord() - # print result[0], result[1] + if debug: print result[0], ": ", result[1] self.bookHeaderRecords[result[0]] = result[1] if ord(self.fo.read(1)) != 0x64 : raise DrmException(u"Parse Error : Invalid Header") @@ -235,12 +240,12 @@ class TopazBook: raise DrmException(u"Parse Error : Record Names Don't Match") flags = ord(self.fo.read(1)) nbRecords = ord(self.fo.read(1)) - # print nbRecords + if debug: print "Metadata Records: %d" % nbRecords for i in range (0,nbRecords) : keyval = bookReadString(self.fo) content = bookReadString(self.fo) - # print keyval - # print content + if debug: print keyval + if debug: print content self.bookMetadata[keyval] = content return self.bookMetadata diff --git a/DeDRM_Windows_Application/DeDRM_ReadMe.txt b/DeDRM_Windows_Application/DeDRM_ReadMe.txt index df13eb5..ad52d33 100644 --- a/DeDRM_Windows_Application/DeDRM_ReadMe.txt +++ b/DeDRM_Windows_Application/DeDRM_ReadMe.txt @@ -1,7 +1,7 @@ -ReadMe_DeDRM_v5.5_WinApp +ReadMe_DeDRM_v5.5.3_WinApp ======================== -DeDRM_v5.5_WinApp is a pure python drag and drop application that allows users to drag and drop ebooks or folders of ebooks onto the DeDRM_Drop_Target to have the DRM removed. It repackages all the "tools" python software in one easy to use program that remembers preferences and settings. +DeDRM_v5.5.3_WinApp is a pure python drag and drop application that allows users to drag and drop ebooks or folders of ebooks onto the DeDRM_Drop_Target to have the DRM removed. It repackages all the "tools" python software in one easy to use program that remembers preferences and settings. It will work without manual configuration for Kindle for PC ebooks and Adobe Adept epub and pdf ebooks. @@ -23,9 +23,9 @@ Installation 0. If you don't already have a correct version of Python and PyCrypto installed, follow the "Installing Python on Windows" and "Installing PyCrypto on Windows" sections below before continuing. -1. Drag the DeDRM_5.5 folder from tools_v5.5/DeDRM_Applications/Windows to your "My Documents" folder. +1. Drag the DeDRM_5.5.3 folder from tools_v5.5.3/DeDRM_Applications/Windows to your "My Documents" folder. -2. Open the DeDRM_5.5 folder you've just dragged, and make a short-cut of the DeDRM_Drop_Target.bat file (right-click/Create Shortcut). Drag the shortcut file onto your Desktop. +2. Open the DeDRM_5.5.3 folder you've just dragged, and make a short-cut of the DeDRM_Drop_Target.bat file (right-click/Create Shortcut). Drag the shortcut file onto your Desktop. 3. To set the preferences simply double-click on your just created short-cut.