mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
sync to trunk
This commit is contained in:
commit
f64ef65e7f
@ -25,6 +25,17 @@ every time you add an HTML file to the library.\
|
|||||||
html2oeb(htmlfile, of)
|
html2oeb(htmlfile, of)
|
||||||
return of.name
|
return of.name
|
||||||
|
|
||||||
|
class OPFMetadataReader(MetadataReaderPlugin):
|
||||||
|
|
||||||
|
name = 'Read OPF metadata'
|
||||||
|
file_types = set(['opf'])
|
||||||
|
description = _('Read metadata from %s files')%'OPF'
|
||||||
|
|
||||||
|
def get_metadata(self, stream, ftype):
|
||||||
|
from calibre.ebooks.metadata.opf2 import OPF
|
||||||
|
from calibre.ebooks.metadata import MetaInformation
|
||||||
|
return MetaInformation(OPF(stream, os.getcwd()))
|
||||||
|
|
||||||
class RTFMetadataReader(MetadataReaderPlugin):
|
class RTFMetadataReader(MetadataReaderPlugin):
|
||||||
|
|
||||||
name = 'Read RTF metadata'
|
name = 'Read RTF metadata'
|
||||||
@ -168,6 +179,16 @@ class ComicMetadataReader(MetadataReaderPlugin):
|
|||||||
mi.cover_data = (ext.lower(), data)
|
mi.cover_data = (ext.lower(), data)
|
||||||
return mi
|
return mi
|
||||||
|
|
||||||
|
class ZipMetadataReader(MetadataReaderPlugin):
|
||||||
|
|
||||||
|
name = 'Read ZIP metadata'
|
||||||
|
file_types = set(['zip', 'oebzip'])
|
||||||
|
description = _('Read metadata from ebooks in ZIP archives')
|
||||||
|
|
||||||
|
def get_metadata(self, stream, ftype):
|
||||||
|
from calibre.ebooks.metadata.zip import get_metadata
|
||||||
|
return get_metadata(stream)
|
||||||
|
|
||||||
class EPUBMetadataWriter(MetadataWriterPlugin):
|
class EPUBMetadataWriter(MetadataWriterPlugin):
|
||||||
|
|
||||||
name = 'Set EPUB metadata'
|
name = 'Set EPUB metadata'
|
||||||
|
@ -127,6 +127,7 @@ def get_file_type_metadata(stream, ftype):
|
|||||||
mi = plugin.get_metadata(stream, ftype.lower().strip())
|
mi = plugin.get_metadata(stream, ftype.lower().strip())
|
||||||
break
|
break
|
||||||
except:
|
except:
|
||||||
|
traceback.print_exc()
|
||||||
continue
|
continue
|
||||||
return mi
|
return mi
|
||||||
|
|
||||||
|
@ -20,7 +20,9 @@ class Device(object):
|
|||||||
FORMATS = ["lrf", "rtf", "pdf", "txt"]
|
FORMATS = ["lrf", "rtf", "pdf", "txt"]
|
||||||
VENDOR_ID = 0x0000
|
VENDOR_ID = 0x0000
|
||||||
PRODUCT_ID = 0x0000
|
PRODUCT_ID = 0x0000
|
||||||
BCD = 0x0000
|
# BCD can be either None to not distinguish between devices based on BCD, or
|
||||||
|
# it can be a list of the BCD numbers of all devices supported by this driver.
|
||||||
|
BCD = None
|
||||||
THUMBNAIL_HEIGHT = 68 # Height for thumbnails on device
|
THUMBNAIL_HEIGHT = 68 # Height for thumbnails on device
|
||||||
|
|
||||||
def __init__(self, key='-1', log_packets=False, report_progress=None) :
|
def __init__(self, key='-1', log_packets=False, report_progress=None) :
|
||||||
|
@ -85,7 +85,7 @@ class PRS500(Device):
|
|||||||
|
|
||||||
VENDOR_ID = 0x054c #: SONY Vendor Id
|
VENDOR_ID = 0x054c #: SONY Vendor Id
|
||||||
PRODUCT_ID = 0x029b #: Product Id for the PRS-500
|
PRODUCT_ID = 0x029b #: Product Id for the PRS-500
|
||||||
BCD = 0x100
|
BCD = [0x100]
|
||||||
PRODUCT_NAME = 'PRS-500'
|
PRODUCT_NAME = 'PRS-500'
|
||||||
VENDOR_NAME = 'SONY'
|
VENDOR_NAME = 'SONY'
|
||||||
INTERFACE_ID = 0 #: The interface we use to talk to the device
|
INTERFACE_ID = 0 #: The interface we use to talk to the device
|
||||||
|
@ -29,7 +29,7 @@ class File(object):
|
|||||||
class PRS505(Device):
|
class PRS505(Device):
|
||||||
VENDOR_ID = 0x054c #: SONY Vendor Id
|
VENDOR_ID = 0x054c #: SONY Vendor Id
|
||||||
PRODUCT_ID = 0x031e #: Product Id for the PRS-505
|
PRODUCT_ID = 0x031e #: Product Id for the PRS-505
|
||||||
BCD = 0x229 #: Needed to disambiguate 505 and 700 on linux
|
BCD = [0x229] #: Needed to disambiguate 505 and 700 on linux
|
||||||
PRODUCT_NAME = 'PRS-505'
|
PRODUCT_NAME = 'PRS-505'
|
||||||
VENDOR_NAME = 'SONY'
|
VENDOR_NAME = 'SONY'
|
||||||
FORMATS = ['lrf', 'epub', "rtf", "pdf", "txt"]
|
FORMATS = ['lrf', 'epub', "rtf", "pdf", "txt"]
|
||||||
@ -86,7 +86,7 @@ class PRS505(Device):
|
|||||||
deviceclass=cls.__name__,
|
deviceclass=cls.__name__,
|
||||||
vendor_id=hex(cls.VENDOR_ID),
|
vendor_id=hex(cls.VENDOR_ID),
|
||||||
product_id=hex(cls.PRODUCT_ID),
|
product_id=hex(cls.PRODUCT_ID),
|
||||||
bcd=hex(cls.BCD),
|
bcd=hex(cls.BCD[0]),
|
||||||
main_memory=cls.MAIN_MEMORY_VOLUME_LABEL,
|
main_memory=cls.MAIN_MEMORY_VOLUME_LABEL,
|
||||||
storage_card=cls.STORAGE_CARD_VOLUME_LABEL,
|
storage_card=cls.STORAGE_CARD_VOLUME_LABEL,
|
||||||
)
|
)
|
||||||
|
@ -9,7 +9,7 @@ from calibre.devices.prs505.driver import PRS505
|
|||||||
|
|
||||||
class PRS700(PRS505):
|
class PRS700(PRS505):
|
||||||
|
|
||||||
BCD = 0x31a
|
BCD = [0x31a]
|
||||||
PRODUCT_NAME = 'PRS-700'
|
PRODUCT_NAME = 'PRS-700'
|
||||||
OSX_NAME = 'Sony PRS-700'
|
OSX_NAME = 'Sony PRS-700'
|
||||||
|
|
||||||
|
@ -39,18 +39,35 @@ class DeviceScanner(object):
|
|||||||
'''Fetch list of connected USB devices from operating system'''
|
'''Fetch list of connected USB devices from operating system'''
|
||||||
self.devices = self.scanner()
|
self.devices = self.scanner()
|
||||||
|
|
||||||
def is_device_connected(self, device):
|
def test_bcd_windows(self, device_id, bcd):
|
||||||
if iswindows:
|
if bcd is None or len(bcd) == 0:
|
||||||
for device_id in self.devices:
|
return True
|
||||||
vid, pid = 'vid_%4.4x'%device.VENDOR_ID, 'pid_%4.4x'%device.PRODUCT_ID
|
for c in bcd:
|
||||||
rev = ('rev_%4.4x'%device.BCD).replace('a', ':') # Bug in winutil.get_usb_devices converts a to :
|
# Bug in winutil.get_usb_devices converts a to :
|
||||||
if vid in device_id and pid in device_id and rev in device_id:
|
rev = ('rev_%4.4x'%c).replace('a', ':')
|
||||||
|
if rev in device_id:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def test_bcd(self, bcdDevice, bcd):
|
||||||
|
if bcd is None or len(bcd) == 0:
|
||||||
|
return True
|
||||||
|
for c in bcd:
|
||||||
|
if c == bcdDevice:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_device_connected(self, device):
|
||||||
|
if iswindows:
|
||||||
|
vid, pid = 'vid_%4.4x'%device.VENDOR_ID, 'pid_%4.4x'%device.PRODUCT_ID
|
||||||
|
for device_id in self.devices:
|
||||||
|
if vid in device_id and pid in device_id:
|
||||||
|
if self.test_bcd_windows(device_id, getattr(device, 'BCD', None)):
|
||||||
|
return True
|
||||||
else:
|
else:
|
||||||
for vendor, product, bcdDevice in self.devices:
|
for vendor, product, bcdDevice in self.devices:
|
||||||
if device.VENDOR_ID == vendor and device.PRODUCT_ID == product:
|
if device.VENDOR_ID == vendor and device.PRODUCT_ID == product:
|
||||||
if hasattr(device, 'BCD') and device.BCD == bcdDevice:
|
if self.test_bcd(bcdDevice, getattr(device, 'BCD', None)):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ class Device(_Device):
|
|||||||
|
|
||||||
VENDOR_ID = 0x0
|
VENDOR_ID = 0x0
|
||||||
PRODUCT_ID = 0x0
|
PRODUCT_ID = 0x0
|
||||||
BCD = 0x0
|
BCD = None
|
||||||
|
|
||||||
VENDOR_NAME = ''
|
VENDOR_NAME = ''
|
||||||
PRODUCT_NAME = ''
|
PRODUCT_NAME = ''
|
||||||
@ -38,7 +38,6 @@ class Device(_Device):
|
|||||||
<match key="info.category" string="volume">
|
<match key="info.category" string="volume">
|
||||||
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.vendor_id" int="%(vendor_id)s">
|
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.vendor_id" int="%(vendor_id)s">
|
||||||
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.product_id" int="%(product_id)s">
|
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.product_id" int="%(product_id)s">
|
||||||
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.device_revision_bcd" int="%(bcd)s">
|
|
||||||
<match key="volume.is_partition" bool="false">
|
<match key="volume.is_partition" bool="false">
|
||||||
<merge key="volume.label" type="string">%(main_memory)s</merge>
|
<merge key="volume.label" type="string">%(main_memory)s</merge>
|
||||||
<merge key="%(app)s.mainvolume" type="string">%(deviceclass)s</merge>
|
<merge key="%(app)s.mainvolume" type="string">%(deviceclass)s</merge>
|
||||||
@ -46,13 +45,11 @@ class Device(_Device):
|
|||||||
</match>
|
</match>
|
||||||
</match>
|
</match>
|
||||||
</match>
|
</match>
|
||||||
</match>
|
|
||||||
</device>
|
</device>
|
||||||
<device>
|
<device>
|
||||||
<match key="info.category" string="volume">
|
<match key="info.category" string="volume">
|
||||||
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.vendor_id" int="%(vendor_id)s">
|
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.vendor_id" int="%(vendor_id)s">
|
||||||
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.product_id" int="%(product_id)s">
|
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.product_id" int="%(product_id)s">
|
||||||
<match key="@info.parent:@info.parent:@info.parent:@info.parent:usb.device_revision_bcd" int="%(bcd)s">
|
|
||||||
<match key="volume.is_partition" bool="true">
|
<match key="volume.is_partition" bool="true">
|
||||||
<merge key="volume.label" type="string">%(storage_card)s</merge>
|
<merge key="volume.label" type="string">%(storage_card)s</merge>
|
||||||
<merge key="%(app)s.cardvolume" type="string">%(deviceclass)s</merge>
|
<merge key="%(app)s.cardvolume" type="string">%(deviceclass)s</merge>
|
||||||
@ -60,7 +57,6 @@ class Device(_Device):
|
|||||||
</match>
|
</match>
|
||||||
</match>
|
</match>
|
||||||
</match>
|
</match>
|
||||||
</match>
|
|
||||||
</device>
|
</device>
|
||||||
'''
|
'''
|
||||||
|
|
||||||
@ -68,17 +64,22 @@ class Device(_Device):
|
|||||||
self._main_prefix = self._card_prefix = None
|
self._main_prefix = self._card_prefix = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_fdi(cls):
|
def get_bcd_less_fdi(cls):
|
||||||
return cls.FDI_TEMPLATE%dict(
|
return cls.FDI_TEMPLATE%dict(
|
||||||
app=__appname__,
|
app=__appname__,
|
||||||
deviceclass=cls.__name__,
|
deviceclass=cls.__name__,
|
||||||
vendor_id=hex(cls.VENDOR_ID),
|
vendor_id=hex(cls.VENDOR_ID),
|
||||||
product_id=hex(cls.PRODUCT_ID),
|
product_id=hex(cls.PRODUCT_ID),
|
||||||
bcd=hex(cls.BCD),
|
|
||||||
main_memory=cls.MAIN_MEMORY_VOLUME_LABEL,
|
main_memory=cls.MAIN_MEMORY_VOLUME_LABEL,
|
||||||
storage_card=cls.STORAGE_CARD_VOLUME_LABEL,
|
storage_card=cls.STORAGE_CARD_VOLUME_LABEL,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_fdi(cls):
|
||||||
|
if cls.BCD is None:
|
||||||
|
return cls.get_bcd_less_fdi()
|
||||||
|
raise NotImplementedError('TODO:')
|
||||||
|
|
||||||
def set_progress_reporter(self, report_progress):
|
def set_progress_reporter(self, report_progress):
|
||||||
self.report_progress = report_progress
|
self.report_progress = report_progress
|
||||||
|
|
||||||
|
24
src/calibre/ebooks/metadata/zip.py
Normal file
24
src/calibre/ebooks/metadata/zip.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
from __future__ import with_statement
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
|
||||||
|
import os
|
||||||
|
from zipfile import ZipFile
|
||||||
|
from cStringIO import StringIO
|
||||||
|
|
||||||
|
|
||||||
|
def get_metadata(stream):
|
||||||
|
stream_type = None
|
||||||
|
zf = ZipFile(stream, 'r')
|
||||||
|
for f in zf.namelist():
|
||||||
|
stream_type = os.path.splitext(f)[1].lower()
|
||||||
|
if stream_type:
|
||||||
|
stream_type = stream_type[1:]
|
||||||
|
if stream_type in ('lit', 'opf', 'prc', 'mobi', 'fb2', 'epub',
|
||||||
|
'rb', 'imp', 'pdf', 'lrf'):
|
||||||
|
from calibre.ebooks.metadata.meta import get_metadata
|
||||||
|
stream = StringIO(zf.read(f))
|
||||||
|
return get_metadata(stream, stream_type)
|
||||||
|
raise ValueError('No ebook found in ZIP archive')
|
||||||
|
|
||||||
|
|
81
src/calibre/ebooks/pdf/pdftrim.py
Normal file
81
src/calibre/ebooks/pdf/pdftrim.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
from __future__ import with_statement
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2009, James Beal, james_@catbus.co.uk'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
'crop a pdf file'
|
||||||
|
|
||||||
|
import os, sys, re
|
||||||
|
from calibre.utils.config import Config, StringConfig
|
||||||
|
from pyPdf import PdfFileWriter, PdfFileReader
|
||||||
|
|
||||||
|
def config(defaults=None):
|
||||||
|
desc = _('Options to control the transformation of pdf')
|
||||||
|
default_crop=10
|
||||||
|
if defaults is None:
|
||||||
|
c = Config('trimpdf', desc)
|
||||||
|
else:
|
||||||
|
c = StringConfig(defaults, desc)
|
||||||
|
c.add_opt('verbose', ['-v', '--verbose'], default=0, action='count',
|
||||||
|
help=_('Be verbose, useful for debugging. Can be specified multiple times for greater verbosity.'))
|
||||||
|
c.add_opt('output', ['-o', '--output'],default='cropped.pdf',
|
||||||
|
help=_('Path to output file. By default a file is created in the current directory.'))
|
||||||
|
c.add_opt('bottom_left_x', [ '-x', '--leftx'], default=default_crop,
|
||||||
|
help=_('Number of pixels to crop from the left most x (default is %d) ')%default_crop )
|
||||||
|
c.add_opt('bottom_left_y', [ '-y', '--lefty'], default=default_crop,
|
||||||
|
help=_('Number of pixels to crop from the left most y (default is %d) ')%default_crop )
|
||||||
|
c.add_opt('top_right_x', [ '-v', '--rightx'], default=default_crop,
|
||||||
|
help=_('Number of pixels to crop from the right most x (default is %d) ')%default_crop )
|
||||||
|
c.add_opt('top_right_y', [ '-w', '--righty'], default=default_crop,
|
||||||
|
help=_('Number of pixels to crop from the right most y (default is %d)')%default_crop )
|
||||||
|
c.add_opt('bounding', ['-b', '--bounding'],
|
||||||
|
help=_('A file generated by ghostscript which allows each page to be individually cropped'))
|
||||||
|
return c
|
||||||
|
|
||||||
|
|
||||||
|
def option_parser():
|
||||||
|
c = config()
|
||||||
|
return c.option_parser(usage=_('''\
|
||||||
|
%prog [options] file.pdf
|
||||||
|
|
||||||
|
Crop a pdf.
|
||||||
|
'''))
|
||||||
|
|
||||||
|
def main(args=sys.argv):
|
||||||
|
parser = option_parser()
|
||||||
|
opts, args = parser.parse_args(args)
|
||||||
|
source = os.path.abspath(args[1])
|
||||||
|
input_pdf = PdfFileReader(file(source, "rb"))
|
||||||
|
if opts.bounding != None:
|
||||||
|
try:
|
||||||
|
bounding = open( opts.bounding , 'r' )
|
||||||
|
bounding_regex= re.compile('%%BoundingBox: (?P<bottom_x>[0-9]+) (?P<bottom_y>[0-9]+) (?P<top_x>[0-9]+) (?P<top_y>[0-9]+)')
|
||||||
|
except:
|
||||||
|
print 'Error opening %s' % opts.bounding
|
||||||
|
return 1
|
||||||
|
output_pdf = PdfFileWriter()
|
||||||
|
for page_number in range (0, input_pdf.getNumPages() ):
|
||||||
|
page = input_pdf.getPage(page_number)
|
||||||
|
if opts.bounding != None:
|
||||||
|
while True:
|
||||||
|
line=bounding.readline()
|
||||||
|
match=bounding_regex.search(line)
|
||||||
|
if match !=None:
|
||||||
|
break
|
||||||
|
page.mediaBox.upperRight = (match.group('top_x'),match.group('top_y'))
|
||||||
|
page.mediaBox.lowerLeft = (match.group('bottom_x'),match.group('bottom_y'))
|
||||||
|
else:
|
||||||
|
page.mediaBox.upperRight = (page.bleedBox.getUpperRight_x()-opts.top_right_x,page.bleedBox.getUpperRight_y()-opts.top_right_y)
|
||||||
|
page.mediaBox.lowerLeft = (page.bleedBox.getLowerLeft_x()+opts.bottom_left_x,page.bleedBox.getLowerLeft_y()+opts.bottom_left_y)
|
||||||
|
output_pdf.addPage(page)
|
||||||
|
if opts.bounding != None:
|
||||||
|
bounding.close()
|
||||||
|
output_file = file(opts.output, "wb")
|
||||||
|
output_pdf.write(output_file)
|
||||||
|
output_file.close()
|
||||||
|
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
@ -314,6 +314,8 @@ def main(args=sys.argv, logger=None):
|
|||||||
sys.excepthook = main.unhandled_exception
|
sys.excepthook = main.unhandled_exception
|
||||||
main.show()
|
main.show()
|
||||||
main.render()
|
main.render()
|
||||||
|
main.activateWindow()
|
||||||
|
main.raise_()
|
||||||
return app.exec_()
|
return app.exec_()
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
HTTP server for remote access to the calibre database.
|
HTTP server for remote access to the calibre database.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import sys, textwrap, cStringIO, mimetypes, operator, os, re, logging
|
import sys, textwrap, mimetypes, operator, os, re, logging
|
||||||
from itertools import repeat
|
from itertools import repeat
|
||||||
from logging.handlers import RotatingFileHandler
|
from logging.handlers import RotatingFileHandler
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@ -285,7 +285,8 @@ class LibraryServer(object):
|
|||||||
updated=updated, id='urn:calibre:main').render('xml')
|
updated=updated, id='urn:calibre:main').render('xml')
|
||||||
|
|
||||||
@expose
|
@expose
|
||||||
def library(self, start='0', num='50', sort=None, search=None, _=None, order='ascending'):
|
def library(self, start='0', num='50', sort=None, search=None,
|
||||||
|
_=None, order='ascending'):
|
||||||
'''
|
'''
|
||||||
Serves metadata from the calibre database as XML.
|
Serves metadata from the calibre database as XML.
|
||||||
|
|
||||||
@ -321,7 +322,7 @@ class LibraryServer(object):
|
|||||||
total=len(ids)).render('xml')
|
total=len(ids)).render('xml')
|
||||||
|
|
||||||
@expose
|
@expose
|
||||||
def index(self):
|
def index(self, **kwargs):
|
||||||
'The / URL'
|
'The / URL'
|
||||||
return self.static('index.html')
|
return self.static('index.html')
|
||||||
|
|
||||||
@ -357,7 +358,8 @@ class LibraryServer(object):
|
|||||||
'' : 'application/octet-stream',
|
'' : 'application/octet-stream',
|
||||||
}[name.rpartition('.')[-1].lower()]
|
}[name.rpartition('.')[-1].lower()]
|
||||||
cherrypy.response.headers['Last-Modified'] = self.last_modified(build_time)
|
cherrypy.response.headers['Last-Modified'] = self.last_modified(build_time)
|
||||||
if self.opts.develop and name in ('gui.js', 'gui.css', 'index.html'):
|
if self.opts.develop and not getattr(sys, 'frozen', False) and \
|
||||||
|
name in ('gui.js', 'gui.css', 'index.html'):
|
||||||
path = os.path.join(os.path.dirname(__file__), 'static', name)
|
path = os.path.join(os.path.dirname(__file__), 'static', name)
|
||||||
lm = datetime.fromtimestamp(os.stat(path).st_mtime)
|
lm = datetime.fromtimestamp(os.stat(path).st_mtime)
|
||||||
cherrypy.response.headers['Last-Modified'] = self.last_modified(lm)
|
cherrypy.response.headers['Last-Modified'] = self.last_modified(lm)
|
||||||
|
@ -65,6 +65,7 @@ entry_points = {
|
|||||||
'calibre-fontconfig = calibre.utils.fontconfig:main',
|
'calibre-fontconfig = calibre.utils.fontconfig:main',
|
||||||
'calibre-parallel = calibre.parallel:main',
|
'calibre-parallel = calibre.parallel:main',
|
||||||
'calibre-customize = calibre.customize.ui:main',
|
'calibre-customize = calibre.customize.ui:main',
|
||||||
|
'pdftrim = calibre.ebooks.pdf.pdftrim:main' ,
|
||||||
],
|
],
|
||||||
'gui_scripts' : [
|
'gui_scripts' : [
|
||||||
__appname__+' = calibre.gui2.main:main',
|
__appname__+' = calibre.gui2.main:main',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user