Pull from driver-dev

This commit is contained in:
Kovid Goyal 2009-05-01 16:16:41 -07:00
commit 43199d9dd2
28 changed files with 455 additions and 102 deletions

View File

@ -221,5 +221,4 @@ class MetadataWriterPlugin(Plugin):
'''
pass

View File

@ -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 \

View File

@ -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)

View File

@ -5,19 +5,6 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
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
from calibre.devices.jetbook.driver import JETBOOK
return (PRS500, PRS505, PRS700, CYBOOKG3, KINDLE, KINDLE2,
BLACKBERRY, EB600, JETBOOK)
import time
DAY_MAP = dict(Sun=0, Mon=1, Tue=2, Wed=3, Thu=4, Fri=5, Sat=6)

View File

@ -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

View File

@ -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']

View File

@ -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']

View File

@ -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

View File

@ -11,6 +11,12 @@ from calibre.devices.usbms.driver import USBMS, metadata_from_formats
from calibre import sanitize_file_name as sanitize
class JETBOOK(USBMS):
name = 'Ectaco JetBook Device Interface'
description = _('Communicate with the JetBook eBook reader.')
author = _('James Ralston')
supported_platforms = ['windows', 'osx', 'linux']
# Ordered list of supported formats
# Be sure these have an entry in calibre.devices.mime
FORMATS = ['epub', 'mobi', 'prc', 'txt', 'rtf', 'pdf']

View File

@ -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]

View File

@ -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

View File

@ -1,5 +1,6 @@
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net> ' \
'2009, John Schember <john at nachtimwald.com>'
'''
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

View File

@ -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]

View File

@ -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 = '<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.device_revision_bcd" int="%(bcd)s">'
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

View File

@ -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:
@ -190,10 +196,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)

View File

@ -95,6 +95,18 @@ class HTMLPreProcessor(object):
# Fix pdftohtml markup
PDFTOHTML = [
# Fix umlauts
(re.compile(u'¨\s*(<br.*?>)*\s*o', re.UNICODE), lambda match: u'ö'),
(re.compile(u'¨\s*(<br.*?>)*\s*O', re.UNICODE), lambda match: u'Ö'),
(re.compile(u'¨\s*(<br.*?>)*\s*u', re.UNICODE), lambda match: u'ü'),
(re.compile(u'¨\s*(<br.*?>)*\s*U', re.UNICODE), lambda match: u'Ü'),
(re.compile(u'¨\s*(<br.*?>)*\s*e', re.UNICODE), lambda match: u'ë'),
(re.compile(u'¨\s*(<br.*?>)*\s*E', re.UNICODE), lambda match: u'Ë'),
(re.compile(u'¨\s*(<br.*?>)*\s*i', re.UNICODE), lambda match: u'ï'),
(re.compile(u'¨\s*(<br.*?>)*\s*I', re.UNICODE), lambda match: u'Ï'),
(re.compile(u'¨\s*(<br.*?>)*\s*a', re.UNICODE), lambda match: u'ä'),
(re.compile(u'¨\s*(<br.*?>)*\s*A', re.UNICODE), lambda match: u'Ä'),
# Remove page links
(re.compile(r'<a name=\d+></a>', re.IGNORECASE), lambda match: ''),
# Remove <hr> tags

View File

@ -0,0 +1,90 @@
# -*- coding: utf-8 -*-
'''
Inspect the header of ereader files. This is primarily used for debugging.
'''
__license__ = 'GPL v3'
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
__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]
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):
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())

View File

@ -27,11 +27,11 @@ 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]
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):
'''
@ -82,14 +82,47 @@ 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
image_data_offset = last_data_offset
record = u''
record = ''
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) # [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
record += struct.pack('>H', version)
record = record.ljust(12, '\x00')
@ -112,6 +145,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

View File

@ -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,
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,105 @@
# -*- coding: utf-8 -*-
__license__ = 'GPL v3'
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
__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())

View File

@ -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

View File

@ -16,8 +16,9 @@ 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, PDFMetadata
from calibre.ebooks.pdf.writer import PDFWriter, ImagePDFWriter, PDFMetadata
from calibre.ebooks.pdf.pageoptions import UNITS, PAPER_SIZES, \
ORIENTATIONS
@ -28,23 +29,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,28 +49,43 @@ 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
self.output_path = output_path
self.metadata = oeb_book.metadata
if input_plugin.is_image_collection:
self.convert_images(input_plugin.get_images())
else:
self.convert_text(oeb_book)
def convert_images(self, images):
self.write(ImagePDFWriter, images)
def convert_text(self, oeb_book):
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]
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])
writer = PDFWriter(log, opts)
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()

View File

@ -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, \
@ -37,7 +36,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)
@ -63,19 +62,42 @@ class PDFWriter(QObject):
self.custom_size = None
self.opts = opts
self.size = self._size()
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
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:
@ -87,7 +109,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))
@ -98,16 +120,8 @@ 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.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.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)
printer.setOutputFileName(item_path)
@ -120,7 +134,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)
@ -132,3 +146,38 @@ class PDFWriter(QObject):
finally:
self._delete_tmpdir()
self.loop.exit(0)
class ImagePDFWriter(PDFWriter):
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)
height = 'height: %fcm;' % (self.size[1] * 1.3)
html = '<html><body style="margin: 0;"><img src="%s" style="%s display: block; margin-left: auto; margin-right: auto; padding: 0px;" /></body></html>' % (item, height)
self.view.setHtml(html)
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

View File

@ -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()