From ccdb99299247d83cf1a540cd15e41d68a6d6a7c1 Mon Sep 17 00:00:00 2001 From: John Schember Date: Sun, 26 Apr 2009 17:09:23 -0400 Subject: [PATCH 1/9] Fix bug 2342. ereader inspector script to aid in implementing writer. ereader writer tweaks. --- src/calibre/ebooks/conversion/preprocess.py | 12 +++ src/calibre/ebooks/pdb/ereader/inspector.py | 87 +++++++++++++++++++++ src/calibre/ebooks/pdb/ereader/writer.py | 35 ++++++++- 3 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 src/calibre/ebooks/pdb/ereader/inspector.py diff --git a/src/calibre/ebooks/conversion/preprocess.py b/src/calibre/ebooks/conversion/preprocess.py index dad77ea3aa..9bfe6d4255 100644 --- a/src/calibre/ebooks/conversion/preprocess.py +++ b/src/calibre/ebooks/conversion/preprocess.py @@ -95,6 +95,18 @@ class HTMLPreProcessor(object): # Fix pdftohtml markup PDFTOHTML = [ + # Fix umlauts + (re.compile(u'¨\s*()*\s*o', re.UNICODE), lambda match: u'ö'), + (re.compile(u'¨\s*()*\s*O', re.UNICODE), lambda match: u'Ö'), + (re.compile(u'¨\s*()*\s*u', re.UNICODE), lambda match: u'ü'), + (re.compile(u'¨\s*()*\s*U', re.UNICODE), lambda match: u'Ü'), + (re.compile(u'¨\s*()*\s*e', re.UNICODE), lambda match: u'ë'), + (re.compile(u'¨\s*()*\s*E', re.UNICODE), lambda match: u'Ë'), + (re.compile(u'¨\s*()*\s*i', re.UNICODE), lambda match: u'ï'), + (re.compile(u'¨\s*()*\s*I', re.UNICODE), lambda match: u'Ï'), + (re.compile(u'¨\s*()*\s*a', re.UNICODE), lambda match: u'ä'), + (re.compile(u'¨\s*()*\s*A', re.UNICODE), lambda match: u'Ä'), + # Remove page links (re.compile(r'', re.IGNORECASE), lambda match: ''), # Remove
tags diff --git a/src/calibre/ebooks/pdb/ereader/inspector.py b/src/calibre/ebooks/pdb/ereader/inspector.py new file mode 100644 index 0000000000..a3875daad4 --- /dev/null +++ b/src/calibre/ebooks/pdb/ereader/inspector.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +''' +Inspect the header of ereader files. This is primarily used for debugging. +''' + +__license__ = 'GPL v3' +__copyright__ = '2009, John Schember ' +__docformat__ = 'restructuredtext en' + +import struct, sys + +from calibre.ebooks.pdb.header import PdbHeaderReader +from calibre.ebooks.pdb.ereader.reader import HeaderRecord + +def pdb_header_info(header): + print 'PDB Header Info:' + print '' + print 'Identity: %s' % header.ident + print 'Total Sectons: %s' % header.num_sections + print 'Title: %s' % header.title + print '' + +def ereader_header_info(header): + h0 = header.section_data(0) + + print 'Ereader Record 0 (Header) Info:' + print '' + print '0-2 Version: %i' % struct.unpack('>H', h0[0:2])[0] + print '2-4: %i' % struct.unpack('>H', h0[2:4])[0] + print '4-6: %i' % struct.unpack('>H', h0[4:6])[0] + print '6-8: %i' % struct.unpack('>H', h0[6:8])[0] + print '8-10: %i' % struct.unpack('>H', h0[8:10])[0] + print '10-12: %i' % struct.unpack('>H', h0[10:12])[0] + print '12-14 Non-Text: %i' % struct.unpack('>H', h0[12:14])[0] + print '14-16: %i' % struct.unpack('>H', h0[14:16])[0] + print '16-18: %i' % struct.unpack('>H', h0[16:18])[0] + print '18-20: %i' % struct.unpack('>H', h0[18:20])[0] + print '20-22: %i' % struct.unpack('>H', h0[20:22])[0] + print '22-24: %i' % struct.unpack('>H', h0[22:24])[0] + print '24-26: %i' % struct.unpack('>H', h0[24:26])[0] + print '26-28: %i' % struct.unpack('>H', h0[26:28])[0] + print '28-30 footnote_rec: %i' % struct.unpack('>H', h0[28:30])[0] + print '30-32 sidebar_rec: %i' % struct.unpack('>H', h0[30:32])[0] + print '32-34 bookmark_offset: %i' % struct.unpack('>H', h0[32:34])[0] + print '34-36: %i' % struct.unpack('>H', h0[34:36])[0] + print '36-38: %i' % struct.unpack('>H', h0[36:38])[0] + print '38-40: %i' % struct.unpack('>H', h0[38:40])[0] + print '40-42 image_data_offset: %i' % struct.unpack('>H', h0[40:42])[0] + print '42-44: %i' % struct.unpack('>H', h0[42:44])[0] + print '44-46 metadata_offset: %i' % struct.unpack('>H', h0[44:46])[0] + print '46-48: %i' % struct.unpack('>H', h0[46:48])[0] + print '48-50 footnote_offset: %i' % struct.unpack('>H', h0[48:50])[0] + print '50-52 sidebar_offset: %i' % struct.unpack('>H', h0[50:52])[0] + print '52-54 last_data_offset: %i' % struct.unpack('>H', h0[52:54])[0] + + print '' + +def section_lengths(header): + print 'Section Sizes' + print '' + + for i in range(0, header.section_count()): + size = len(header.section_data(i)) + if size > 65505: + message = '<--- Over!' + else: + message = '' + + print 'Section %i: %i %s' % (i, size, message) + +def main(args=sys.argv): + if len(args) < 2: + print 'Error: requires input file.' + return 1 + + f = open(sys.argv[1], 'rb') + + pheader = PdbHeaderReader(f) + + pdb_header_info(pheader) + ereader_header_info(pheader) + section_lengths(pheader) + + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/calibre/ebooks/pdb/ereader/writer.py b/src/calibre/ebooks/pdb/ereader/writer.py index 65eb35157e..b831849488 100644 --- a/src/calibre/ebooks/pdb/ereader/writer.py +++ b/src/calibre/ebooks/pdb/ereader/writer.py @@ -27,7 +27,7 @@ class Writer(object): hr = [self._header_record(len(text), len(images))] - sections = hr+text+images+metadata + sections = hr+text+images+metadata+['MeTaInFo\x00'] lengths = [len(i) for i in sections] @@ -82,7 +82,7 @@ class Writer(object): if image_items > 0: image_data_offset = text_items + 1 meta_data_offset = image_data_offset + image_items - last_data_offset = meta_data_offset + 1 + last_data_offset = meta_data_offset + 2 else: meta_data_offset = text_items + 1 last_data_offset = meta_data_offset + 1 @@ -90,6 +90,35 @@ class Writer(object): record = u'' + record += struct.pack('>H', version) # [0:2] + record += struct.pack('>H', 0) # [2:4] + record += struct.pack('>H', 0) # [4:6] + record += struct.pack('>H', 25152) # [6:8] # 25152 is MAGIC + record += struct.pack('>H', last_data_offset) # [8:10] + record += struct.pack('>H', last_data_offset) # [10:12] + record += struct.pack('>H', non_text_offset) # [12:14] # non_text_offset + record += struct.pack('>H', non_text_offset) # [14:16] + record += struct.pack('>H', 1) # [16:18] + record += struct.pack('>H', 1) # [18:20] + record += struct.pack('>H', 0) # [20:22] + record += struct.pack('>H', 1) # [22:24] + record += struct.pack('>H', 1) # [24:26] + record += struct.pack('>H', 0) # [26:28] + record += struct.pack('>H', 0) # [28:30] # footnote_rec + record += struct.pack('>H', 0) # [30:32] # sidebar_rec + record += struct.pack('>H', last_data_offset) # [32:34] # bookmark_offset + record += struct.pack('>H', 2560) # [34:36] # 2560 is MAGIC + record += struct.pack('>H', non_text_offset) # [36:38] + record += struct.pack('>H', non_text_offset + 1) # [38:40] + record += struct.pack('>H', image_data_offset) # [40:42] + record += struct.pack('>H', image_data_offset) # [42:44] + record += struct.pack('>H', meta_data_offset) # [44:46] + record += struct.pack('>H', meta_data_offset) # [46:48] + record += struct.pack('>H', last_data_offset) # [48:50] # footnote_offset + record += struct.pack('>H', last_data_offset) # [52:54] # sidebar_offset + record += struct.pack('>H', last_data_offset) # [54:56] # last_data_offset + + ''' # Version record += struct.pack('>H', version) record = record.ljust(12, '\x00') @@ -112,6 +141,6 @@ class Writer(object): record += struct.pack('>H', last_data_offset) record = record.ljust(52, '\x00') record += struct.pack('>H', last_data_offset) - + ''' return record From a9a18aa565c0681b494bf957e9bc486ec2443c66 Mon Sep 17 00:00:00 2001 From: John Schember Date: Mon, 27 Apr 2009 18:48:15 -0400 Subject: [PATCH 2/9] Ereader writer still not working --- src/calibre/ebooks/pdb/ereader/inspector.py | 3 +++ src/calibre/ebooks/pdb/ereader/writer.py | 14 +++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/calibre/ebooks/pdb/ereader/inspector.py b/src/calibre/ebooks/pdb/ereader/inspector.py index a3875daad4..b5f2341cb5 100644 --- a/src/calibre/ebooks/pdb/ereader/inspector.py +++ b/src/calibre/ebooks/pdb/ereader/inspector.py @@ -53,6 +53,9 @@ def ereader_header_info(header): print '50-52 sidebar_offset: %i' % struct.unpack('>H', h0[50:52])[0] print '52-54 last_data_offset: %i' % struct.unpack('>H', h0[52:54])[0] + for i in range(54, 131, 2): + print '%i-%i: %i' % (i, i+2, struct.unpack('>H', h0[i:i+2])[0]) + print '' def section_lengths(header): diff --git a/src/calibre/ebooks/pdb/ereader/writer.py b/src/calibre/ebooks/pdb/ereader/writer.py index b831849488..7e3fdc30ea 100644 --- a/src/calibre/ebooks/pdb/ereader/writer.py +++ b/src/calibre/ebooks/pdb/ereader/writer.py @@ -31,7 +31,7 @@ class Writer(object): lengths = [len(i) for i in sections] - pdbHeaderBuilder = PdbHeaderBuilder(IDENTITY, '') + pdbHeaderBuilder = PdbHeaderBuilder(IDENTITY, 'test book') pdbHeaderBuilder.build_header(lengths, out_stream) for item in sections: @@ -69,7 +69,7 @@ class Writer(object): return images def _metadata(self, metadata): - return '\x00\x00\x00\x00\x00' + return 'test\x00\x00\x00\x00\x00' def _header_record(self, text_items, image_items): ''' @@ -88,7 +88,7 @@ class Writer(object): last_data_offset = meta_data_offset + 1 image_data_offset = last_data_offset - record = u'' + record = '' record += struct.pack('>H', version) # [0:2] record += struct.pack('>H', 0) # [2:4] @@ -115,8 +115,12 @@ class Writer(object): record += struct.pack('>H', meta_data_offset) # [44:46] record += struct.pack('>H', meta_data_offset) # [46:48] record += struct.pack('>H', last_data_offset) # [48:50] # footnote_offset - record += struct.pack('>H', last_data_offset) # [52:54] # sidebar_offset - record += struct.pack('>H', last_data_offset) # [54:56] # last_data_offset + record += struct.pack('>H', last_data_offset) # [50:52] # sidebar_offset + record += struct.pack('>H', last_data_offset) # [52:54] # last_data_offset + + record += struct.pack('>H', 1) # [54:56] + for i in range(56, 132, 2): + record += struct.pack('>H', 0) ''' # Version From ed13e58801098860ad9c765f2494d25604dd7591 Mon Sep 17 00:00:00 2001 From: John Schember Date: Mon, 27 Apr 2009 19:04:20 -0400 Subject: [PATCH 3/9] PDF output: Remove redundant margin options, start image conversion. --- src/calibre/ebooks/pdf/output.py | 33 +++++++++++++++----------------- src/calibre/ebooks/pdf/writer.py | 11 +++++++++-- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/calibre/ebooks/pdf/output.py b/src/calibre/ebooks/pdf/output.py index 7b8b0323ab..a2674b83eb 100644 --- a/src/calibre/ebooks/pdf/output.py +++ b/src/calibre/ebooks/pdf/output.py @@ -28,23 +28,11 @@ class PDFOutput(OutputFormatPlugin): file_type = 'pdf' options = set([ - OptionRecommendation(name='margin_top', recommended_value='1', - level=OptionRecommendation.LOW, - help=_('The top margin around the document.')), - OptionRecommendation(name='margin_bottom', recommended_value='1', - level=OptionRecommendation.LOW, - help=_('The bottom margin around the document.')), - OptionRecommendation(name='margin_left', recommended_value='1', - level=OptionRecommendation.LOW, - help=_('The left margin around the document.')), - OptionRecommendation(name='margin_right', recommended_value='1', - level=OptionRecommendation.LOW, - help=_('The right margin around the document.')), - OptionRecommendation(name='unit', recommended_value='inch', level=OptionRecommendation.LOW, short_switch='u', choices=UNITS.keys(), help=_('The unit of measure. Default is inch. Choices ' - 'are %s' % UNITS.keys())), + 'are %s ' + 'Note: This does not override the unit for margins!' % UNITS.keys())), OptionRecommendation(name='paper_size', recommended_value='letter', level=OptionRecommendation.LOW, choices=PAPER_SIZES.keys(), help=_('The size of the paper. Default is letter. Choices ' @@ -60,15 +48,23 @@ class PDFOutput(OutputFormatPlugin): ]) def convert(self, oeb_book, output_path, input_plugin, opts, log): - self.opts, self.log = opts, log + self.input_plugin, self.opts, self.log = input_plugin, opts, log + if input_plugin.is_image_collection: - self.convert_images(input_plugin.get_images()) + self.convert_images(input_plugin.get_images(), output_path) + else: + self.convert_text(oeb_book, output_path) + + def convert_images(self, images, output_path): + raise NotImplementedError() + + def convert_text(self, oeb_book, output_path): with TemporaryDirectory('_pdf_out') as oebdir: - OEBOutput(None).convert(oeb_book, oebdir, input_plugin, opts, log) + OEBOutput(None).convert(oeb_book, oebdir, self.input_plugin, self.opts, self.log) opf = glob.glob(os.path.join(oebdir, '*.opf'))[0] - writer = PDFWriter(log, opts) + writer = PDFWriter(self.opts, self.log) close = False if not hasattr(output_path, 'write'): @@ -85,3 +81,4 @@ class PDFOutput(OutputFormatPlugin): if close: out_stream.close() + diff --git a/src/calibre/ebooks/pdf/writer.py b/src/calibre/ebooks/pdf/writer.py index 7a9973c6d7..e82c6bd257 100644 --- a/src/calibre/ebooks/pdf/writer.py +++ b/src/calibre/ebooks/pdf/writer.py @@ -37,7 +37,7 @@ class PDFMetadata(object): class PDFWriter(QObject): - def __init__(self, log, opts): + def __init__(self, opts, log): if QApplication.instance() is None: QApplication([]) QObject.__init__(self) @@ -107,7 +107,7 @@ class PDFWriter(QObject): else: printer.setPaperSize(QSizeF(self.opts.output_profile.width / self.opts.output_profile.dpi, self.opts.output_profile.height / self.opts.output_profile.dpi), QPrinter.Inch) - printer.setPageMargins(size(self.opts.margin_left), size(self.opts.margin_top), size(self.opts.margin_right), size(self.opts.margin_bottom), unit(self.opts.unit)) + printer.setPageMargins(0, 0, 0, 0, QPrinter.Point) printer.setOrientation(orientation(self.opts.orientation)) printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(item_path) @@ -132,3 +132,10 @@ class PDFWriter(QObject): finally: self._delete_tmpdir() self.loop.exit(0) + + +class ImagePDFWriter(object): + + def __init__(self, opts, log): + self.opts, self.log = opts, log + From ee777c539aa14030530258a91e477a7f5bb5b732 Mon Sep 17 00:00:00 2001 From: John Schember Date: Tue, 28 Apr 2009 21:33:35 -0400 Subject: [PATCH 4/9] beginnings of working comic to pdf output. --- src/calibre/ebooks/pdf/output.py | 46 ++++++++++++++++++-------------- src/calibre/ebooks/pdf/writer.py | 25 ++++++++++------- 2 files changed, 42 insertions(+), 29 deletions(-) diff --git a/src/calibre/ebooks/pdf/output.py b/src/calibre/ebooks/pdf/output.py index a2674b83eb..f4d9cb4631 100644 --- a/src/calibre/ebooks/pdf/output.py +++ b/src/calibre/ebooks/pdf/output.py @@ -17,7 +17,7 @@ from calibre.customize.conversion import OutputFormatPlugin, \ OptionRecommendation from calibre.ebooks.oeb.output import OEBOutput from calibre.ptempfile import TemporaryDirectory -from calibre.ebooks.pdf.writer import PDFWriter, PDFMetadata +from calibre.ebooks.pdf.writer import PDFWriter, ImagePDFWriter, PDFMetadata from calibre.ebooks.pdf.pageoptions import UNITS, PAPER_SIZES, \ ORIENTATIONS @@ -49,36 +49,42 @@ class PDFOutput(OutputFormatPlugin): def convert(self, oeb_book, output_path, input_plugin, opts, log): self.input_plugin, self.opts, self.log = input_plugin, opts, log + self.output_path = output_path + self.metadata = oeb_book.metadata if input_plugin.is_image_collection: - self.convert_images(input_plugin.get_images(), output_path) + self.convert_images(input_plugin.get_images()) else: - self.convert_text(oeb_book, output_path) + self.convert_text(oeb_book) - def convert_images(self, images, output_path): - raise NotImplementedError() + def convert_images(self, images): + # process images to document size + self.write(ImagePDFWriter, images) - def convert_text(self, oeb_book, output_path): + def convert_text(self, oeb_book): with TemporaryDirectory('_pdf_out') as oebdir: OEBOutput(None).convert(oeb_book, oebdir, self.input_plugin, self.opts, self.log) opf = glob.glob(os.path.join(oebdir, '*.opf'))[0] + + self.write(PDFWriter, [s.path for s in opf.spine]) - writer = PDFWriter(self.opts, self.log) + def write(self, Writer, items): + writer = Writer(self.opts, self.log) - close = False - if not hasattr(output_path, 'write'): - close = True - if not os.path.exists(os.path.dirname(output_path)) and os.path.dirname(output_path) != '': - os.makedirs(os.path.dirname(output_path)) - out_stream = open(output_path, 'wb') - else: - out_stream = output_path + close = False + if not hasattr(self.output_path, 'write'): + close = True + if not os.path.exists(os.path.dirname(self.output_path)) and os.path.dirname(self.output_path) != '': + os.makedirs(os.path.dirname(self.output_path)) + out_stream = open(self.output_path, 'wb') + else: + out_stream = self.output_path - out_stream.seek(0) - out_stream.truncate() - writer.dump(opf, out_stream, PDFMetadata(oeb_book.metadata)) + out_stream.seek(0) + out_stream.truncate() + writer.dump(items, out_stream, PDFMetadata(self.metadata)) - if close: - out_stream.close() + if close: + out_stream.close() diff --git a/src/calibre/ebooks/pdf/writer.py b/src/calibre/ebooks/pdf/writer.py index e82c6bd257..adaae10d16 100644 --- a/src/calibre/ebooks/pdf/writer.py +++ b/src/calibre/ebooks/pdf/writer.py @@ -64,12 +64,11 @@ class PDFWriter(QObject): self.opts = opts - def dump(self, opfpath, out_stream, pdf_metadata): + def dump(self, items, out_stream, pdf_metadata): self.metadata = pdf_metadata self._delete_tmpdir() - opf = OPF(opfpath, os.path.dirname(opfpath)) - self.render_queue = [i.path for i in opf.spine] + self.render_queue = items self.combine_queue = [] self.out_stream = out_stream @@ -87,7 +86,7 @@ class PDFWriter(QObject): item = str(self.render_queue.pop(0)) self.combine_queue.append(os.path.join(self.tmp_path, '%i.pdf' % (len(self.combine_queue) + 1))) - self.logger.info('Processing %s...' % item) + self.logger.debug('Processing %s...' % item) self.view.load(QUrl(item)) @@ -120,7 +119,7 @@ class PDFWriter(QObject): self.tmp_path = PersistentTemporaryDirectory('_pdf_output_parts') def _write(self): - self.logger.info('Combining individual PDF parts...') + self.logger.debug('Combining individual PDF parts...') try: outPDF = PdfFileWriter(title=self.metadata.title, author=self.metadata.author) @@ -134,8 +133,16 @@ class PDFWriter(QObject): self.loop.exit(0) -class ImagePDFWriter(object): +class ImagePDFWriter(PDFWriter): - def __init__(self, opts, log): - self.opts, self.log = opts, log - + def _render_next(self): + item = str(self.render_queue.pop(0)) + self.combine_queue.append(os.path.join(self.tmp_path, '%i.pdf' % (len(self.combine_queue) + 1))) + + self.logger.debug('Processing %s...' % item) + + html = '' % item + + self.view.setHtml(html) + + From 49aec87b59093f6ddacee3282914a58cebc4b11c Mon Sep 17 00:00:00 2001 From: John Schember Date: Wed, 29 Apr 2009 06:47:34 -0400 Subject: [PATCH 5/9] pdf image output tweaks --- src/calibre/ebooks/pdf/writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/ebooks/pdf/writer.py b/src/calibre/ebooks/pdf/writer.py index adaae10d16..80bcacde2c 100644 --- a/src/calibre/ebooks/pdf/writer.py +++ b/src/calibre/ebooks/pdf/writer.py @@ -141,7 +141,7 @@ class ImagePDFWriter(PDFWriter): self.logger.debug('Processing %s...' % item) - html = '' % item + html = '' % item self.view.setHtml(html) From 6c54eca8b3267139708facbb28c5048e3fc85052 Mon Sep 17 00:00:00 2001 From: John Schember Date: Wed, 29 Apr 2009 07:00:01 -0400 Subject: [PATCH 6/9] pdfmanipulate: rotate command. --- src/calibre/ebooks/pdf/manipulate/cli.py | 3 ++- src/calibre/ebooks/pdf/manipulate/crop.py | 2 +- src/calibre/ebooks/pdf/manipulate/info.py | 8 ++++---- src/calibre/ebooks/pdf/manipulate/merge.py | 2 +- src/calibre/ebooks/pdf/manipulate/reverse.py | 2 +- src/calibre/ebooks/pdf/manipulate/split.py | 2 +- 6 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/calibre/ebooks/pdf/manipulate/cli.py b/src/calibre/ebooks/pdf/manipulate/cli.py index edbba54a8d..4876cbd8f5 100644 --- a/src/calibre/ebooks/pdf/manipulate/cli.py +++ b/src/calibre/ebooks/pdf/manipulate/cli.py @@ -16,7 +16,7 @@ from calibre.utils.logging import Log from calibre.constants import preferred_encoding from calibre.customize.conversion import OptionRecommendation from calibre.ebooks.pdf.manipulate import crop, decrypt, encrypt, \ - info, merge, reverse, split + info, merge, reverse, rotate, split COMMANDS = { 'crop' : crop, @@ -25,6 +25,7 @@ COMMANDS = { 'info' : info, 'merge' : merge, 'reverse' : reverse, + 'rotate' : rotate, 'split' : split, } diff --git a/src/calibre/ebooks/pdf/manipulate/crop.py b/src/calibre/ebooks/pdf/manipulate/crop.py index 7627823a89..0f24f04638 100644 --- a/src/calibre/ebooks/pdf/manipulate/crop.py +++ b/src/calibre/ebooks/pdf/manipulate/crop.py @@ -19,7 +19,7 @@ from calibre.utils.config import OptionParser from calibre.utils.logging import Log from calibre.constants import preferred_encoding from calibre.customize.conversion import OptionRecommendation -from calibre.ebooks.pdf.verify import is_valid_pdf +from calibre.ebooks.pdf.verify import is_valid_pdf, is_encrypted from pyPdf import PdfFileWriter, PdfFileReader diff --git a/src/calibre/ebooks/pdf/manipulate/info.py b/src/calibre/ebooks/pdf/manipulate/info.py index d1b52a602c..13a39d10f6 100644 --- a/src/calibre/ebooks/pdf/manipulate/info.py +++ b/src/calibre/ebooks/pdf/manipulate/info.py @@ -16,7 +16,7 @@ from calibre.utils.config import OptionParser from calibre.utils.logging import Log from calibre.constants import preferred_encoding from calibre.customize.conversion import OptionRecommendation -from calibre.ebooks.pdf.verify import is_valid_pdfs, is_encrypted +from calibre.ebooks.pdf.verify import is_valid_pdfs, is_encrypted, is_encrypted from pyPdf import PdfFileWriter, PdfFileReader @@ -42,10 +42,10 @@ def print_info(pdf_path): print _('Subject: %s' % pdf.documentInfo.subject) print _('Creator: %s' % pdf.documentInfo.creator) print _('Producer: %s' % pdf.documentInfo.producer) - print _('Creation Date: %s' % time.strftime('%a %b %d %H:%M:%S %Y', time.gmtime(os.path.getctime(pdf_path)))) - print _('Modification Date: %s' % time.strftime('%a %b %d %H:%M:%S %Y', time.gmtime(os.path.getmtime(pdf_path)))) + #print _('Creation Date: %s' % time.strftime('%a %b %d %H:%M:%S %Y', time.gmtime(os.path.getctime(pdf_path)))) + #print _('Modification Date: %s' % time.strftime('%a %b %d %H:%M:%S %Y', time.gmtime(os.path.getmtime(pdf_path)))) print _('Pages: %s' % pdf.numPages) - print _('Encrypted: %s' % pdf.isEncrypted) + #print _('Encrypted: %s' % pdf.isEncrypted) try: print _('File Size: %s bytes' % os.path.getsize(pdf_path)) except: pass diff --git a/src/calibre/ebooks/pdf/manipulate/merge.py b/src/calibre/ebooks/pdf/manipulate/merge.py index fce7076e85..cb89368f31 100644 --- a/src/calibre/ebooks/pdf/manipulate/merge.py +++ b/src/calibre/ebooks/pdf/manipulate/merge.py @@ -18,7 +18,7 @@ from calibre.utils.config import OptionParser from calibre.utils.logging import Log from calibre.constants import preferred_encoding from calibre.customize.conversion import OptionRecommendation -from calibre.ebooks.pdf.verify import is_valid_pdfs +from calibre.ebooks.pdf.verify import is_valid_pdfs, is_encrypted from pyPdf import PdfFileWriter, PdfFileReader diff --git a/src/calibre/ebooks/pdf/manipulate/reverse.py b/src/calibre/ebooks/pdf/manipulate/reverse.py index f2f3fa16da..b4bbe27a40 100644 --- a/src/calibre/ebooks/pdf/manipulate/reverse.py +++ b/src/calibre/ebooks/pdf/manipulate/reverse.py @@ -18,7 +18,7 @@ from calibre.utils.config import OptionParser from calibre.utils.logging import Log from calibre.constants import preferred_encoding from calibre.customize.conversion import OptionRecommendation -from calibre.ebooks.pdf.verify import is_valid_pdf +from calibre.ebooks.pdf.verify import is_valid_pdf, is_encrypted from pyPdf import PdfFileWriter, PdfFileReader diff --git a/src/calibre/ebooks/pdf/manipulate/split.py b/src/calibre/ebooks/pdf/manipulate/split.py index 19012797ae..957b78f1e6 100644 --- a/src/calibre/ebooks/pdf/manipulate/split.py +++ b/src/calibre/ebooks/pdf/manipulate/split.py @@ -18,7 +18,7 @@ from calibre.utils.config import OptionParser from calibre.utils.logging import Log from calibre.constants import preferred_encoding from calibre.customize.conversion import OptionRecommendation -from calibre.ebooks.pdf.verify import is_valid_pdf +from calibre.ebooks.pdf.verify import is_valid_pdf, is_encrypted from pyPdf import PdfFileWriter, PdfFileReader From 73d203fa862b0bf2059e631c8922d3488d91e6a9 Mon Sep 17 00:00:00 2001 From: John Schember Date: Wed, 29 Apr 2009 19:04:18 -0400 Subject: [PATCH 7/9] pdfmanipulage rotate command. image to pdf output working. --- src/calibre/ebooks/pdf/manipulate/rotate.py | 105 ++++++++++++++++++++ src/calibre/ebooks/pdf/writer.py | 63 +++++++++++- 2 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 src/calibre/ebooks/pdf/manipulate/rotate.py diff --git a/src/calibre/ebooks/pdf/manipulate/rotate.py b/src/calibre/ebooks/pdf/manipulate/rotate.py new file mode 100644 index 0000000000..ac46a8e0c8 --- /dev/null +++ b/src/calibre/ebooks/pdf/manipulate/rotate.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- + +__license__ = 'GPL v3' +__copyright__ = '2009, John Schember ' +__docformat__ = 'restructuredtext en' + +''' +Rotate pages of a PDF. +''' + +import os, sys +from optparse import OptionGroup, Option + +from calibre.ebooks.metadata.meta import metadata_from_formats +from calibre.ebooks.metadata import authors_to_string +from calibre.utils.config import OptionParser +from calibre.utils.logging import Log +from calibre.constants import preferred_encoding +from calibre.customize.conversion import OptionRecommendation +from calibre.ebooks.pdf.verify import is_valid_pdf, is_encrypted + +from pyPdf import PdfFileWriter, PdfFileReader + +USAGE = '\n%prog %%name ' + _('''\ +file.pdf degrees + +Rotate pages of a PDF clockwise. +''') + +OPTIONS = set([ + OptionRecommendation(name='output', recommended_value='rotated.pdf', + level=OptionRecommendation.HIGH, long_switch='output', short_switch='o', + help=_('Path to output file. By default a file is created in the current directory.')), +]) + +def print_help(parser, log): + help = parser.format_help().encode(preferred_encoding, 'replace') + log(help) + +def option_parser(name): + usage = USAGE.replace('%%name', name) + return OptionParser(usage=usage) + +def option_recommendation_to_cli_option(add_option, rec): + opt = rec.option + switches = ['-'+opt.short_switch] if opt.short_switch else [] + switches.append('--'+opt.long_switch) + attrs = dict(dest=opt.name, help=opt.help, + choices=opt.choices, default=rec.recommended_value) + add_option(Option(*switches, **attrs)) + +def add_options(parser): + group = OptionGroup(parser, _('Rotate Options:'), _('Options to control the transformation of pdf')) + parser.add_option_group(group) + add_option = group.add_option + + for rec in OPTIONS: + option_recommendation_to_cli_option(add_option, rec) + +def rotate(pdf_path, out_path, degrees, metadata=None): + if metadata == None: + title = _('Unknown') + author = _('Unknown') + else: + title = metadata.title + author = authors_to_string(metadata.authors) + + out_pdf = PdfFileWriter(title=title, author=author) + + pdf = PdfFileReader(open(os.path.abspath(pdf_path), 'rb')) + for page in pdf.pages: + out_pdf.addPage(page.rotateClockwise(int(degrees))) + + with open(out_path, 'wb') as out_file: + out_pdf.write(out_file) + +def main(args=sys.argv, name=''): + log = Log() + parser = option_parser(name) + add_options(parser) + + opts, args = parser.parse_args(args) + args = args[1:] + + if len(args) < 2: + print 'Error: A PDF file and how many degrees to rotate is required.\n' + print_help(parser, log) + return 1 + + if not is_valid_pdf(args[0]): + print 'Error: Could not read file `%s`.' % args[0] + return 1 + + if is_encrypted(args[0]): + print 'Error: file `%s` is encrypted.' % args[0] + return 1 + + mi = metadata_from_formats([args[0]]) + + rotate(args[0], opts.output, args[1], mi) + + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/calibre/ebooks/pdf/writer.py b/src/calibre/ebooks/pdf/writer.py index 80bcacde2c..1700d58025 100644 --- a/src/calibre/ebooks/pdf/writer.py +++ b/src/calibre/ebooks/pdf/writer.py @@ -140,9 +140,70 @@ class ImagePDFWriter(PDFWriter): self.combine_queue.append(os.path.join(self.tmp_path, '%i.pdf' % (len(self.combine_queue) + 1))) self.logger.debug('Processing %s...' % item) + + import Image + + size = self._size() + #height = 'height: %ipx;' % size[1] if Image.open(item).size[1] > size[1] else '' + #height = 'height: %icm;' % size[1] + #height = 'height: %ipx;' % self.opts.output_profile.comic_screen_size[1] +### height = 'height: 594px;' # self.opts.output_profile.comic_screen_size[1] * .78 + + #print + #print size[1] + #print Image.open(item).size[1] + #print Image.open(item).size[1] / self.opts.output_profile.dpi + #print height - html = '' % item + + #height = 'height: %ipx;' % (self.opts.output_profile.comic_screen_size[1] - 160) + #height = 'height: %ipx;' % (self.opts.output_profile.comic_screen_size[1] * .78) + height = 'height: %fcm;' % (size[1] * 1.3) + #print height + + html = '' % (item, height) self.view.setHtml(html) + def _render_html(self, ok): + if ok: + item_path = os.path.join(self.tmp_path, '%i.pdf' % len(self.combine_queue)) + + self.logger.debug('\tRendering item %s as %i' % (os.path.basename(str(self.view.url().toLocalFile())), len(self.combine_queue))) + + printer = QPrinter(QPrinter.HighResolution) + + if self.opts.output_profile.short_name == 'default': + if self.custom_size == None: + printer.setPaperSize(paper_size(self.opts.paper_size)) + else: + printer.setPaperSize(QSizeF(self.custom_size[0], self.custom_size[1]), unit(self.opts.unit)) + else: + printer.setPaperSize(QSizeF(self.opts.output_profile.comic_screen_size[0] / self.opts.output_profile.dpi, self.opts.output_profile.comic_screen_size[1] / self.opts.output_profile.dpi), QPrinter.Inch) + + printer.setPageMargins(0, 0, 0, 0, QPrinter.Point) + printer.setOrientation(orientation(self.opts.orientation)) + printer.setOutputFormat(QPrinter.PdfFormat) + printer.setOutputFileName(item_path) + self.view.print_(printer) + self._render_book() + + def _size(self): + printer = QPrinter(QPrinter.HighResolution) + + if self.opts.output_profile.short_name == 'default': + if self.custom_size == None: + printer.setPaperSize(paper_size(self.opts.paper_size)) + else: + printer.setPaperSize(QSizeF(self.custom_size[0], self.custom_size[1]), unit(self.opts.unit)) + else: + printer.setPaperSize(QSizeF(self.opts.output_profile.comic_screen_size[0] / self.opts.output_profile.dpi, self.opts.output_profile.comic_screen_size[1] / self.opts.output_profile.dpi), QPrinter.Inch) + + printer.setPageMargins(0, 0, 0, 0, QPrinter.Point) + printer.setOrientation(orientation(self.opts.orientation)) + printer.setOutputFormat(QPrinter.PdfFormat) + + size = printer.paperSize(QPrinter.Millimeter) + + return size.width() / 10, size.height() / 10 From 3a2da56ccc2401af43fb3d203c8e3fbcab0cca72 Mon Sep 17 00:00:00 2001 From: John Schember Date: Wed, 29 Apr 2009 19:19:24 -0400 Subject: [PATCH 8/9] image to pdf output complete. --- src/calibre/ebooks/pdf/output.py | 5 +- src/calibre/ebooks/pdf/writer.py | 78 +++++++++++--------------------- 2 files changed, 29 insertions(+), 54 deletions(-) diff --git a/src/calibre/ebooks/pdf/output.py b/src/calibre/ebooks/pdf/output.py index f4d9cb4631..3f1e2db907 100644 --- a/src/calibre/ebooks/pdf/output.py +++ b/src/calibre/ebooks/pdf/output.py @@ -16,6 +16,7 @@ import os, glob from calibre.customize.conversion import OutputFormatPlugin, \ OptionRecommendation from calibre.ebooks.oeb.output import OEBOutput +from calibre.ebooks.metadata.opf2 import OPF from calibre.ptempfile import TemporaryDirectory from calibre.ebooks.pdf.writer import PDFWriter, ImagePDFWriter, PDFMetadata from calibre.ebooks.pdf.pageoptions import UNITS, PAPER_SIZES, \ @@ -58,14 +59,14 @@ class PDFOutput(OutputFormatPlugin): self.convert_text(oeb_book) def convert_images(self, images): - # process images to document size self.write(ImagePDFWriter, images) def convert_text(self, oeb_book): with TemporaryDirectory('_pdf_out') as oebdir: OEBOutput(None).convert(oeb_book, oebdir, self.input_plugin, self.opts, self.log) - opf = glob.glob(os.path.join(oebdir, '*.opf'))[0] + opfpath = glob.glob(os.path.join(oebdir, '*.opf'))[0] + opf = OPF(opfpath, os.path.dirname(opfpath)) self.write(PDFWriter, [s.path for s in opf.spine]) diff --git a/src/calibre/ebooks/pdf/writer.py b/src/calibre/ebooks/pdf/writer.py index 1700d58025..97eaeb9244 100644 --- a/src/calibre/ebooks/pdf/writer.py +++ b/src/calibre/ebooks/pdf/writer.py @@ -15,7 +15,6 @@ from calibre.ptempfile import PersistentTemporaryDirectory from calibre.ebooks.pdf.pageoptions import unit, paper_size, \ orientation, size from calibre.ebooks.metadata import authors_to_string -from calibre.ebooks.metadata.opf2 import OPF from PyQt4 import QtCore from PyQt4.Qt import QUrl, QEventLoop, SIGNAL, QObject, \ @@ -63,6 +62,8 @@ class PDFWriter(QObject): self.custom_size = None self.opts = opts + + self.size = self._size() def dump(self, items, out_stream, pdf_metadata): self.metadata = pdf_metadata @@ -75,6 +76,28 @@ class PDFWriter(QObject): QMetaObject.invokeMethod(self, "_render_book", Qt.QueuedConnection) self.loop.exec_() + def _size(self): + ''' + The size of a pdf page in cm. + ''' + printer = QPrinter(QPrinter.HighResolution) + + if self.opts.output_profile.short_name == 'default': + if self.custom_size == None: + printer.setPaperSize(paper_size(self.opts.paper_size)) + else: + printer.setPaperSize(QSizeF(self.custom_size[0], self.custom_size[1]), unit(self.opts.unit)) + else: + printer.setPaperSize(QSizeF(self.opts.output_profile.width / self.opts.output_profile.dpi, self.opts.output_profile.height / self.opts.output_profile.dpi), QPrinter.Inch) + + printer.setPageMargins(0, 0, 0, 0, QPrinter.Point) + printer.setOrientation(orientation(self.opts.orientation)) + printer.setOutputFormat(QPrinter.PdfFormat) + + size = printer.paperSize(QPrinter.Millimeter) + + return size.width() / 10, size.height() / 10 + @QtCore.pyqtSignature('_render_book()') def _render_book(self): if len(self.render_queue) == 0: @@ -97,15 +120,7 @@ class PDFWriter(QObject): self.logger.debug('\tRendering item %s as %i' % (os.path.basename(str(self.view.url().toLocalFile())), len(self.combine_queue))) printer = QPrinter(QPrinter.HighResolution) - - if self.opts.output_profile.short_name == 'default': - if self.custom_size == None: - printer.setPaperSize(paper_size(self.opts.paper_size)) - else: - printer.setPaperSize(QSizeF(self.custom_size[0], self.custom_size[1]), unit(self.opts.unit)) - else: - printer.setPaperSize(QSizeF(self.opts.output_profile.width / self.opts.output_profile.dpi, self.opts.output_profile.height / self.opts.output_profile.dpi), QPrinter.Inch) - + printer.setPaperSize(QSizeF(self.size[0] * 10, self.size[1] * 10), QPrinter.Millimeter) printer.setPageMargins(0, 0, 0, 0, QPrinter.Point) printer.setOrientation(orientation(self.opts.orientation)) printer.setOutputFormat(QPrinter.PdfFormat) @@ -141,53 +156,12 @@ class ImagePDFWriter(PDFWriter): self.logger.debug('Processing %s...' % item) - import Image + height = 'height: %fcm;' % (self.size[1] * 1.3) - size = self._size() - #height = 'height: %ipx;' % size[1] if Image.open(item).size[1] > size[1] else '' - #height = 'height: %icm;' % size[1] - #height = 'height: %ipx;' % self.opts.output_profile.comic_screen_size[1] -### height = 'height: 594px;' # self.opts.output_profile.comic_screen_size[1] * .78 - - #print - #print size[1] - #print Image.open(item).size[1] - #print Image.open(item).size[1] / self.opts.output_profile.dpi - #print height - - - #height = 'height: %ipx;' % (self.opts.output_profile.comic_screen_size[1] - 160) - #height = 'height: %ipx;' % (self.opts.output_profile.comic_screen_size[1] * .78) - height = 'height: %fcm;' % (size[1] * 1.3) - #print height - html = '' % (item, height) self.view.setHtml(html) - def _render_html(self, ok): - if ok: - item_path = os.path.join(self.tmp_path, '%i.pdf' % len(self.combine_queue)) - - self.logger.debug('\tRendering item %s as %i' % (os.path.basename(str(self.view.url().toLocalFile())), len(self.combine_queue))) - - printer = QPrinter(QPrinter.HighResolution) - - if self.opts.output_profile.short_name == 'default': - if self.custom_size == None: - printer.setPaperSize(paper_size(self.opts.paper_size)) - else: - printer.setPaperSize(QSizeF(self.custom_size[0], self.custom_size[1]), unit(self.opts.unit)) - else: - printer.setPaperSize(QSizeF(self.opts.output_profile.comic_screen_size[0] / self.opts.output_profile.dpi, self.opts.output_profile.comic_screen_size[1] / self.opts.output_profile.dpi), QPrinter.Inch) - - printer.setPageMargins(0, 0, 0, 0, QPrinter.Point) - printer.setOrientation(orientation(self.opts.orientation)) - printer.setOutputFormat(QPrinter.PdfFormat) - printer.setOutputFileName(item_path) - self.view.print_(printer) - self._render_book() - def _size(self): printer = QPrinter(QPrinter.HighResolution) From d794904319a34818a2a907c3ecbccdb6156fa00c Mon Sep 17 00:00:00 2001 From: John Schember Date: Thu, 30 Apr 2009 17:17:56 -0400 Subject: [PATCH 9/9] Pluginize device drivers. --- src/calibre/customize/__init__.py | 3 +-- src/calibre/customize/builtins.py | 10 ++++++++++ src/calibre/customize/ui.py | 7 +++++++ src/calibre/devices/__init__.py | 12 ------------ src/calibre/devices/blackberry/driver.py | 10 ++++++---- src/calibre/devices/cybookg3/driver.py | 6 ++++++ src/calibre/devices/eb600/driver.py | 5 +++++ src/calibre/devices/interface.py | 9 ++++++--- src/calibre/devices/kindle/driver.py | 12 +++++++++++- src/calibre/devices/prs500/driver.py | 10 +++++++--- src/calibre/devices/prs505/driver.py | 9 ++++++++- src/calibre/devices/prs700/driver.py | 5 +++++ src/calibre/devices/usbms/device.py | 6 +++--- src/calibre/devices/usbms/driver.py | 14 +++++++++++--- src/calibre/gui2/device.py | 10 +++++----- 15 files changed, 91 insertions(+), 37 deletions(-) diff --git a/src/calibre/customize/__init__.py b/src/calibre/customize/__init__.py index b43b242fd8..0e6bad8d2e 100644 --- a/src/calibre/customize/__init__.py +++ b/src/calibre/customize/__init__.py @@ -221,5 +221,4 @@ class MetadataWriterPlugin(Plugin): ''' pass - - \ No newline at end of file + diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index dcbffade92..0b5a0295ea 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -295,9 +295,19 @@ from calibre.ebooks.pdf.output import PDFOutput from calibre.ebooks.pdb.ereader.output import EREADEROutput from calibre.customize.profiles import input_profiles, output_profiles +from calibre.devices.prs500.driver import PRS500 +from calibre.devices.prs505.driver import PRS505 +from calibre.devices.prs700.driver import PRS700 +from calibre.devices.cybookg3.driver import CYBOOKG3 +from calibre.devices.kindle.driver import KINDLE +from calibre.devices.kindle.driver import KINDLE2 +from calibre.devices.blackberry.driver import BLACKBERRY +from calibre.devices.eb600.driver import EB600 + plugins = [HTML2ZIP, EPUBInput, MOBIInput, PDBInput, PDFInput, HTMLInput, TXTInput, OEBOutput, TXTOutput, PDFOutput, LITInput, ComicInput, FB2Input, ODTInput, RTFInput, EPUBOutput, EREADEROutput, RecipeInput] +plugins += [PRS505, PRS700, CYBOOKG3, KINDLE, KINDLE2, BLACKBERRY, EB600] plugins += [x for x in list(locals().values()) if isinstance(x, type) and \ x.__name__.endswith('MetadataReader')] plugins += [x for x in list(locals().values()) if isinstance(x, type) and \ diff --git a/src/calibre/customize/ui.py b/src/calibre/customize/ui.py index 4bd5c9b284..ad321a1f83 100644 --- a/src/calibre/customize/ui.py +++ b/src/calibre/customize/ui.py @@ -10,6 +10,7 @@ from calibre.customize.conversion import InputFormatPlugin, OutputFormatPlugin from calibre.customize.profiles import InputProfile, OutputProfile from calibre.customize.builtins import plugins as builtin_plugins from calibre.constants import __version__, iswindows, isosx +from calibre.devices.interface import DevicePlugin from calibre.ebooks.metadata import MetaInformation from calibre.utils.config import make_config_dir, Config, ConfigProxy, \ plugin_dir, OptionParser @@ -286,6 +287,12 @@ def available_output_formats(): formats.add(plugin.file_type) return formats +def device_plugins(): + for plugin in _initialized_plugins: + if isinstance(plugin, DevicePlugin): + if not is_disabled(plugin): + yield plugin + def disable_plugin(plugin_or_name): x = getattr(plugin_or_name, 'name', plugin_or_name) plugin = find_plugin(x) diff --git a/src/calibre/devices/__init__.py b/src/calibre/devices/__init__.py index 9c515c07fd..874de7c070 100644 --- a/src/calibre/devices/__init__.py +++ b/src/calibre/devices/__init__.py @@ -5,18 +5,6 @@ __copyright__ = '2008, Kovid Goyal ' Device drivers. ''' -def devices(): - from calibre.devices.prs500.driver import PRS500 - from calibre.devices.prs505.driver import PRS505 - from calibre.devices.prs700.driver import PRS700 - from calibre.devices.cybookg3.driver import CYBOOKG3 - from calibre.devices.kindle.driver import KINDLE - from calibre.devices.kindle.driver import KINDLE2 - from calibre.devices.blackberry.driver import BLACKBERRY - from calibre.devices.eb600.driver import EB600 - return (PRS500, PRS505, PRS700, CYBOOKG3, KINDLE, KINDLE2, - BLACKBERRY, EB600) - import time DAY_MAP = dict(Sun=0, Mon=1, Tue=2, Wed=3, Thu=4, Fri=5, Sat=6) diff --git a/src/calibre/devices/blackberry/driver.py b/src/calibre/devices/blackberry/driver.py index f6c615b0de..da2328419a 100644 --- a/src/calibre/devices/blackberry/driver.py +++ b/src/calibre/devices/blackberry/driver.py @@ -7,6 +7,12 @@ __docformat__ = 'restructuredtext en' from calibre.devices.usbms.driver import USBMS class BLACKBERRY(USBMS): + + name = 'Blackberry Device Interface' + description = _('Communicate with the Blackberry smart phone.') + author = _('Kovid Goyal') + supported_platforms = ['windows', 'linux'] + # Ordered list of supported formats FORMATS = ['mobi', 'prc'] @@ -16,15 +22,11 @@ class BLACKBERRY(USBMS): VENDOR_NAME = 'RIM' WINDOWS_MAIN_MEM = 'BLACKBERRY_SD' - #WINDOWS_CARD_MEM = 'CARD_STORAGE' #OSX_MAIN_MEM = 'Kindle Internal Storage Media' - #OSX_CARD_MEM = 'Kindle Card Storage Media' MAIN_MEMORY_VOLUME_LABEL = 'Blackberry Main Memory' - #STORAGE_CARD_VOLUME_LABEL = 'Kindle Storage Card' EBOOK_DIR_MAIN = 'ebooks' - #EBOOK_DIR_CARD = "documents" SUPPORTS_SUB_DIRS = True diff --git a/src/calibre/devices/cybookg3/driver.py b/src/calibre/devices/cybookg3/driver.py index 1cdf9863b4..7f3e5a82f4 100644 --- a/src/calibre/devices/cybookg3/driver.py +++ b/src/calibre/devices/cybookg3/driver.py @@ -12,6 +12,12 @@ from calibre.devices.usbms.driver import USBMS import calibre.devices.cybookg3.t2b as t2b class CYBOOKG3(USBMS): + name = 'Cybook Gen 3 Device Interface' + description = _('Communicate with the Cybook eBook reader.') + author = _('John Schember') + supported_platforms = ['windows', 'osx', 'linux'] + + # Ordered list of supported formats # Be sure these have an entry in calibre.devices.mime FORMATS = ['mobi', 'prc', 'html', 'pdf', 'rtf', 'txt'] diff --git a/src/calibre/devices/eb600/driver.py b/src/calibre/devices/eb600/driver.py index cb2f25d2f9..638dea42ba 100644 --- a/src/calibre/devices/eb600/driver.py +++ b/src/calibre/devices/eb600/driver.py @@ -7,6 +7,11 @@ Device driver for the Netronix EB600 from calibre.devices.usbms.driver import USBMS class EB600(USBMS): + name = 'Netronix EB600 Device Interface' + description = _('Communicate with the EB600 eBook reader.') + author = _('Kovid Goyal') + supported_platforms = ['windows', 'osx', 'linux'] + # Ordered list of supported formats FORMATS = ['epub', 'prc', 'chm', 'djvu', 'html', 'rtf', 'txt', 'pdf'] DRM_FORMATS = ['prc', 'mobi', 'html', 'pdf', 'txt'] diff --git a/src/calibre/devices/interface.py b/src/calibre/devices/interface.py index 0ad01e7493..94cecd65b6 100644 --- a/src/calibre/devices/interface.py +++ b/src/calibre/devices/interface.py @@ -6,8 +6,9 @@ the GUI. A device backend must subclass the L{Device} class. See prs500.py for a backend that implement the Device interface for the SONY PRS500 Reader. """ +from calibre.customize import Plugin -class Device(object): +class DevicePlugin(Plugin): """ Defines the interface that should be implemented by backends that communicate with an ebook reader. @@ -16,6 +17,8 @@ class Device(object): the front-end needs to call several methods one after another, in which case the USB session should not be closed after each method call. """ + type = _('Device Interface') + # Ordered list of supported formats FORMATS = ["lrf", "rtf", "pdf", "txt"] VENDOR_ID = 0x0000 @@ -27,8 +30,8 @@ class Device(object): # Whether the metadata on books can be set via the GUI. CAN_SET_METADATA = True - def __init__(self, key='-1', log_packets=False, report_progress=None) : - """ + def reset(self, key='-1', log_packets=False, report_progress=None) : + """ @param key: The key to unlock the device @param log_packets: If true the packet stream to/from the device is logged @param report_progress: Function that is called with a % progress diff --git a/src/calibre/devices/kindle/driver.py b/src/calibre/devices/kindle/driver.py index d598e2a503..6f17cd335a 100644 --- a/src/calibre/devices/kindle/driver.py +++ b/src/calibre/devices/kindle/driver.py @@ -6,9 +6,14 @@ Device driver for Amazon's Kindle import os, re, sys -from calibre.devices.usbms.driver import USBMS, metadata_from_formats +from calibre.devices.usbms.driver import USBMS class KINDLE(USBMS): + name = 'Kindle Device Interface' + description = _('Communicate with the Kindle eBook reader.') + author = _('John Schember') + supported_platforms = ['windows', 'osx', 'linux'] + # Ordered list of supported formats FORMATS = ['azw', 'mobi', 'prc', 'azw1', 'tpz', 'txt'] @@ -46,6 +51,7 @@ class KINDLE(USBMS): @classmethod def metadata_from_path(cls, path): + from calibre.devices.usbms.driver import metadata_from_formats mi = metadata_from_formats([path]) if mi.title == _('Unknown') or ('-asin' in mi.title and '-type' in mi.title): match = cls.WIRELESS_FILE_NAME_PATTERN.match(os.path.basename(path)) @@ -58,6 +64,10 @@ class KINDLE(USBMS): class KINDLE2(KINDLE): + name = 'Kindle 2 Device Interface' + description = _('Communicate with the Kindle 2 eBook reader.') + author = _('John Schember') + supported_platforms = ['windows', 'osx', 'linux'] PRODUCT_ID = [0x0002] BCD = [0x0100] diff --git a/src/calibre/devices/prs500/driver.py b/src/calibre/devices/prs500/driver.py index a905a314ae..874ed494e4 100644 --- a/src/calibre/devices/prs500/driver.py +++ b/src/calibre/devices/prs500/driver.py @@ -40,7 +40,7 @@ from array import array from functools import wraps from StringIO import StringIO -from calibre.devices.interface import Device +from calibre.devices.interface import DevicePlugin from calibre.devices.libusb import Error as USBError from calibre.devices.libusb import get_device_by_id from calibre.devices.prs500.prstypes import * @@ -76,12 +76,16 @@ class File(object): return self.name -class PRS500(Device): +class PRS500(DevicePlugin): """ Implements the backend for communication with the SONY Reader. Each method decorated by C{safe} performs a task. """ + name = 'PRS-500 Device Interface' + description = _('Communicate with the Sony PRS-500 eBook reader.') + author = _('Kovid Goyal') + supported_platforms = ['windows', 'osx', 'linux'] VENDOR_ID = 0x054c #: SONY Vendor Id PRODUCT_ID = 0x029b #: Product Id for the PRS-500 @@ -181,7 +185,7 @@ class PRS500(Device): return run_session - def __init__(self, key='-1', log_packets=False, report_progress=None) : + def reset(self, key='-1', log_packets=False, report_progress=None) : """ @param key: The key to unlock the device @param log_packets: If true the packet stream to/from the device is logged diff --git a/src/calibre/devices/prs505/driver.py b/src/calibre/devices/prs505/driver.py index a704eb1ec3..f569667ba1 100644 --- a/src/calibre/devices/prs505/driver.py +++ b/src/calibre/devices/prs505/driver.py @@ -1,5 +1,6 @@ __license__ = 'GPL v3' -__copyright__ = '2008, Kovid Goyal ' +__copyright__ = '2008, Kovid Goyal ' \ + '2009, John Schember ' ''' Device driver for the SONY PRS-505 ''' @@ -14,6 +15,12 @@ from calibre import iswindows, islinux, isosx, __appname__ from calibre.devices.errors import PathError class PRS505(CLI, Device): + + name = 'PRS-505 Device Interface' + description = _('Communicate with the Sony PRS-505 eBook reader.') + author = _('Kovid Goyal and John Schember') + supported_platforms = ['windows', 'osx', 'linux'] + FORMATS = ['epub', 'lrf', 'lrx', 'rtf', 'pdf', 'txt'] VENDOR_ID = [0x054c] #: SONY Vendor Id diff --git a/src/calibre/devices/prs700/driver.py b/src/calibre/devices/prs700/driver.py index 2b82eb3e34..a79902fe10 100644 --- a/src/calibre/devices/prs700/driver.py +++ b/src/calibre/devices/prs700/driver.py @@ -8,6 +8,11 @@ Device driver for the SONY PRS-700 from calibre.devices.prs505.driver import PRS505 class PRS700(PRS505): + + name = 'PRS-700 Device Interface' + description = _('Communicate with the Sony PRS-700 eBook reader.') + author = _('Kovid Goyal and John Schember') + supported_platforms = ['windows', 'osx', 'linux'] BCD = [0x31a] diff --git a/src/calibre/devices/usbms/device.py b/src/calibre/devices/usbms/device.py index 8f2755d3fa..c4bbe7839f 100644 --- a/src/calibre/devices/usbms/device.py +++ b/src/calibre/devices/usbms/device.py @@ -8,11 +8,11 @@ device. This class handles device detection. import os, subprocess, time, re -from calibre.devices.interface import Device as _Device +from calibre.devices.interface import DevicePlugin as Device from calibre.devices.errors import DeviceError from calibre import iswindows, islinux, isosx, __appname__ -class Device(_Device): +class Device(Device): ''' This class provides logic common to all drivers for devices that export themselves as USB Mass Storage devices. If you are writing such a driver, inherit from this @@ -83,7 +83,7 @@ class Device(_Device): FDI_BCD_TEMPLATE = '' - def __init__(self, key='-1', log_packets=False, report_progress=None) : + def reset(self, key='-1', log_packets=False, report_progress=None) : self._main_prefix = self._card_a_prefix = self._card_b_prefix = None @classmethod diff --git a/src/calibre/devices/usbms/driver.py b/src/calibre/devices/usbms/driver.py index aa40f90c25..156e3a5eb5 100644 --- a/src/calibre/devices/usbms/driver.py +++ b/src/calibre/devices/usbms/driver.py @@ -10,7 +10,6 @@ for a particular device. import os, fnmatch, shutil from itertools import cycle -from calibre.ebooks.metadata.meta import metadata_from_formats, path_to_ext from calibre.ebooks.metadata import authors_to_string from calibre.devices.usbms.cli import CLI from calibre.devices.usbms.device import Device @@ -21,6 +20,12 @@ from calibre.devices.mime import mime_type_ext # CLI must come before Device as it implments the CLI functions that # are inherited from the device interface in Device. class USBMS(CLI, Device): + + name = 'USBMS Base Device Interface' + description = _('Communicate with an eBook reader.') + author = _('John Schember') + supported_platforms = ['windows', 'osx', 'linux'] + FORMATS = [] EBOOK_DIR_MAIN = '' EBOOK_DIR_CARD_A = '' @@ -28,8 +33,8 @@ class USBMS(CLI, Device): SUPPORTS_SUB_DIRS = False CAN_SET_METADATA = False - def __init__(self, key='-1', log_packets=False, report_progress=None): - Device.__init__(self, key=key, log_packets=log_packets, + def reset(self, key='-1', log_packets=False, report_progress=None): + Device.reset(self, key=key, log_packets=log_packets, report_progress=report_progress) def get_device_information(self, end_session=True): @@ -40,6 +45,7 @@ class USBMS(CLI, Device): return (self.__class__.__name__, '', '', '') def books(self, oncard=None, end_session=True): + from calibre.ebooks.metadata.meta import path_to_ext bl = BookList() if oncard == 'carda' and not self._card_a_prefix: @@ -185,10 +191,12 @@ class USBMS(CLI, Device): @classmethod def metadata_from_path(cls, path): + from calibre.ebooks.metadata.meta import metadata_from_formats return metadata_from_formats([path]) @classmethod def book_from_path(cls, path): + from calibre.ebooks.metadata.meta import path_to_ext fileext = path_to_ext(path) mi = cls.metadata_from_path(path) mime = mime_type_ext(fileext) diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 239fd4d37d..bbdc62746f 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -10,8 +10,8 @@ from binascii import unhexlify from PyQt4.Qt import QMenu, QAction, QActionGroup, QIcon, SIGNAL, QPixmap, \ Qt -from calibre.customize.ui import available_input_formats, available_output_formats -from calibre.devices import devices +from calibre.customize.ui import available_input_formats, available_output_formats, \ + device_plugins from calibre.constants import iswindows from calibre.gui2.dialogs.choose_format import ChooseFormatDialog from calibre.parallel import Job @@ -21,7 +21,6 @@ from calibre.gui2 import config, error_dialog, Dispatcher, dynamic, \ info_dialog from calibre.ebooks.metadata import authors_to_string from calibre.gui2.dialogs.conversion_error import ConversionErrorDialog -from calibre.devices.interface import Device from calibre import sanitize_file_name, preferred_encoding from calibre.utils.filenames import ascii_filename from calibre.devices.errors import FreeSpaceError @@ -54,7 +53,7 @@ class DeviceManager(Thread): ''' Thread.__init__(self) self.setDaemon(True) - self.devices = [[d, False] for d in devices()] + self.devices = [[d, False] for d in device_plugins()] self.device = None self.device_class = None self.sleep_time = sleep_time @@ -71,7 +70,8 @@ class DeviceManager(Thread): connected = self.scanner.is_device_connected(device[0]) if connected and not device[1]: try: - dev = device[0]() + dev = device[0] + dev.reset() if iswindows: import pythoncom pythoncom.CoInitialize()