mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 18:24:30 -04:00
Merge upstream changes.
This commit is contained in:
commit
28064174fc
@ -5,9 +5,5 @@
|
|||||||
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.5</pydev_property>
|
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.5</pydev_property>
|
||||||
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
|
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
|
||||||
<path>/calibre/src</path>
|
<path>/calibre/src</path>
|
||||||
<path>/calibre/devices</path>
|
|
||||||
<path>/calibre/libprs500.devices.prs500</path>
|
|
||||||
<path>/calibre/prs500</path>
|
|
||||||
<path>/calibre/gui2</path>
|
|
||||||
</pydev_pathproperty>
|
</pydev_pathproperty>
|
||||||
</pydev_project>
|
</pydev_project>
|
||||||
|
@ -122,6 +122,8 @@ def freeze():
|
|||||||
elif exe not in executables:
|
elif exe not in executables:
|
||||||
print >>sys.stderr, 'Invalid invocation of calibre loader. CALIBRE_CX_EXE=%%s is unknown'%%exe
|
print >>sys.stderr, 'Invalid invocation of calibre loader. CALIBRE_CX_EXE=%%s is unknown'%%exe
|
||||||
else:
|
else:
|
||||||
|
from PyQt4.QtCore import QCoreApplication
|
||||||
|
QCoreApplication.setLibraryPaths([sys.frozen_path, os.path.join(sys.frozen_path, "qtplugins")])
|
||||||
sys.argv[0] = exe
|
sys.argv[0] = exe
|
||||||
module, func = executables[exe]
|
module, func = executables[exe]
|
||||||
module = __import__(module, fromlist=[1])
|
module = __import__(module, fromlist=[1])
|
||||||
@ -179,7 +181,7 @@ def freeze():
|
|||||||
if not f.endswith('.so') or 'designer' in dirpath or 'codecs' in dirpath or 'sqldrivers' in dirpath:
|
if not f.endswith('.so') or 'designer' in dirpath or 'codecs' in dirpath or 'sqldrivers' in dirpath:
|
||||||
continue
|
continue
|
||||||
f = os.path.join(dirpath, f)
|
f = os.path.join(dirpath, f)
|
||||||
dest_dir = dirpath.replace(plugdir, os.path.join(FREEZE_DIR, 'qtlugins'))
|
dest_dir = dirpath.replace(plugdir, os.path.join(FREEZE_DIR, 'qtplugins'))
|
||||||
copy_binary(f, dest_dir)
|
copy_binary(f, dest_dir)
|
||||||
|
|
||||||
print 'Creating launchers'
|
print 'Creating launchers'
|
||||||
|
@ -2,7 +2,7 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
__appname__ = 'calibre'
|
__appname__ = 'calibre'
|
||||||
__version__ = '0.4.113'
|
__version__ = '0.4.115'
|
||||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||||
'''
|
'''
|
||||||
Various run time constants.
|
Various run time constants.
|
||||||
|
@ -88,10 +88,10 @@ def initialize_container(path_to_container, opf_name='metadata.opf'):
|
|||||||
zf.writestr('META-INF/container.xml', CONTAINER)
|
zf.writestr('META-INF/container.xml', CONTAINER)
|
||||||
return zf
|
return zf
|
||||||
|
|
||||||
def config(defaults=None):
|
def config(defaults=None, name='epub'):
|
||||||
desc = _('Options to control the conversion to EPUB')
|
desc = _('Options to control the conversion to EPUB')
|
||||||
if defaults is None:
|
if defaults is None:
|
||||||
c = Config('epub', desc)
|
c = Config(name, desc)
|
||||||
else:
|
else:
|
||||||
c = StringConfig(defaults, desc)
|
c = StringConfig(defaults, desc)
|
||||||
|
|
||||||
|
@ -116,7 +116,8 @@ def unarchive(path, tdir):
|
|||||||
return f, ext
|
return f, ext
|
||||||
return find_html_index(files)
|
return find_html_index(files)
|
||||||
|
|
||||||
def any2epub(opts, path, notification=None):
|
def any2epub(opts, path, notification=None, create_epub=True,
|
||||||
|
oeb_cover=False, extract_to=None):
|
||||||
ext = os.path.splitext(path)[1]
|
ext = os.path.splitext(path)[1]
|
||||||
if not ext:
|
if not ext:
|
||||||
raise ValueError('Unknown file type: '+path)
|
raise ValueError('Unknown file type: '+path)
|
||||||
@ -139,7 +140,9 @@ def any2epub(opts, path, notification=None):
|
|||||||
raise ValueError('Conversion from %s is not supported'%ext.upper())
|
raise ValueError('Conversion from %s is not supported'%ext.upper())
|
||||||
|
|
||||||
print 'Creating EPUB file...'
|
print 'Creating EPUB file...'
|
||||||
html2epub(path, opts, notification=notification)
|
html2epub(path, opts, notification=notification,
|
||||||
|
create_epub=create_epub, oeb_cover=oeb_cover,
|
||||||
|
extract_to=extract_to)
|
||||||
|
|
||||||
def config(defaults=None):
|
def config(defaults=None):
|
||||||
return common_config(defaults=defaults)
|
return common_config(defaults=defaults)
|
||||||
@ -148,14 +151,14 @@ def config(defaults=None):
|
|||||||
def formats():
|
def formats():
|
||||||
return ['html', 'rar', 'zip', 'oebzip']+list(MAP.keys())
|
return ['html', 'rar', 'zip', 'oebzip']+list(MAP.keys())
|
||||||
|
|
||||||
def option_parser():
|
USAGE = _('''\
|
||||||
|
|
||||||
return config().option_parser(usage=_('''\
|
|
||||||
%%prog [options] filename
|
%%prog [options] filename
|
||||||
|
|
||||||
Convert any of a large number of ebook formats to an epub file. Supported formats are: %s
|
Convert any of a large number of ebook formats to a %s file. Supported formats are: %s
|
||||||
''')%formats()
|
''')
|
||||||
)
|
|
||||||
|
def option_parser(usage=USAGE):
|
||||||
|
return config().option_parser(usage=usage%('EPUB', formats()))
|
||||||
|
|
||||||
def main(args=sys.argv):
|
def main(args=sys.argv):
|
||||||
parser = option_parser()
|
parser = option_parser()
|
||||||
|
@ -32,14 +32,14 @@ Conversion of HTML/OPF files follows several stages:
|
|||||||
* The EPUB container is created.
|
* The EPUB container is created.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import os, sys, cStringIO, logging, re, functools
|
import os, sys, cStringIO, logging, re, functools, shutil
|
||||||
|
|
||||||
from lxml.etree import XPath
|
from lxml.etree import XPath
|
||||||
from lxml import html
|
from lxml import html
|
||||||
from PyQt4.Qt import QApplication, QPixmap
|
from PyQt4.Qt import QApplication, QPixmap
|
||||||
|
|
||||||
from calibre.ebooks.html import Processor, merge_metadata, get_filelist,\
|
from calibre.ebooks.html import Processor, merge_metadata, get_filelist,\
|
||||||
opf_traverse, create_metadata, rebase_toc, Link
|
opf_traverse, create_metadata, rebase_toc, Link, parser
|
||||||
from calibre.ebooks.epub import config as common_config, tostring
|
from calibre.ebooks.epub import config as common_config, tostring
|
||||||
from calibre.ptempfile import TemporaryDirectory
|
from calibre.ptempfile import TemporaryDirectory
|
||||||
from calibre.ebooks.metadata.toc import TOC
|
from calibre.ebooks.metadata.toc import TOC
|
||||||
@ -62,9 +62,10 @@ def remove_bad_link(element, attribute, link, pos):
|
|||||||
|
|
||||||
def check(opf_path, pretty_print):
|
def check(opf_path, pretty_print):
|
||||||
'''
|
'''
|
||||||
Find a remove all invalid links in the HTML files
|
Find and remove all invalid links in the HTML files
|
||||||
'''
|
'''
|
||||||
print '\tChecking files for bad links...'
|
logger = logging.getLogger('html2epub')
|
||||||
|
logger.info('\tChecking files for bad links...')
|
||||||
pathtoopf = os.path.abspath(opf_path)
|
pathtoopf = os.path.abspath(opf_path)
|
||||||
with CurrentDir(os.path.dirname(pathtoopf)):
|
with CurrentDir(os.path.dirname(pathtoopf)):
|
||||||
opf = OPF(open(pathtoopf, 'rb'), os.path.dirname(pathtoopf))
|
opf = OPF(open(pathtoopf, 'rb'), os.path.dirname(pathtoopf))
|
||||||
@ -76,7 +77,7 @@ def check(opf_path, pretty_print):
|
|||||||
|
|
||||||
for path in html_files:
|
for path in html_files:
|
||||||
base = os.path.dirname(path)
|
base = os.path.dirname(path)
|
||||||
root = html.fromstring(open(content(path), 'rb').read())
|
root = html.fromstring(open(content(path), 'rb').read(), parser=parser)
|
||||||
for element, attribute, link, pos in list(root.iterlinks()):
|
for element, attribute, link, pos in list(root.iterlinks()):
|
||||||
link = to_unicode(link)
|
link = to_unicode(link)
|
||||||
plink = Link(link, base)
|
plink = Link(link, base)
|
||||||
@ -209,17 +210,16 @@ TITLEPAGE = '''\
|
|||||||
</html>
|
</html>
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def create_cover_image(src, dest, screen_size):
|
def create_cover_image(src, dest, screen_size, rescale_cover=True):
|
||||||
from PyQt4.Qt import QApplication, QImage, Qt
|
|
||||||
if QApplication.instance() is None:
|
|
||||||
app = QApplication([])
|
|
||||||
app
|
|
||||||
im = QImage()
|
|
||||||
try:
|
try:
|
||||||
|
from PyQt4.Qt import QImage, Qt
|
||||||
|
if QApplication.instance() is None:
|
||||||
|
QApplication([])
|
||||||
|
im = QImage()
|
||||||
im.load(src)
|
im.load(src)
|
||||||
if im.isNull():
|
if im.isNull():
|
||||||
raise ValueError
|
raise ValueError('Invalid cover image')
|
||||||
if screen_size is not None:
|
if rescale_cover and screen_size is not None:
|
||||||
width, height = im.width(), im.height()
|
width, height = im.width(), im.height()
|
||||||
dw, dh = (screen_size[0]-width)/float(width), (screen_size[1]-height)/float(height)
|
dw, dh = (screen_size[0]-width)/float(width), (screen_size[1]-height)/float(height)
|
||||||
delta = min(dw, dh)
|
delta = min(dw, dh)
|
||||||
@ -227,7 +227,7 @@ def create_cover_image(src, dest, screen_size):
|
|||||||
nwidth = int(width + delta*(width))
|
nwidth = int(width + delta*(width))
|
||||||
nheight = int(height + delta*(height))
|
nheight = int(height + delta*(height))
|
||||||
im = im.scaled(int(nwidth), int(nheight), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
|
im = im.scaled(int(nwidth), int(nheight), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
|
||||||
im.save(dest)
|
im.save(dest)
|
||||||
except:
|
except:
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
@ -240,7 +240,6 @@ def process_title_page(mi, filelist, htmlfilemap, opts, tdir):
|
|||||||
if mi.cover:
|
if mi.cover:
|
||||||
if f(filelist[0].path) == f(mi.cover):
|
if f(filelist[0].path) == f(mi.cover):
|
||||||
old_title_page = htmlfilemap[filelist[0].path]
|
old_title_page = htmlfilemap[filelist[0].path]
|
||||||
|
|
||||||
#logger = logging.getLogger('html2epub')
|
#logger = logging.getLogger('html2epub')
|
||||||
metadata_cover = mi.cover
|
metadata_cover = mi.cover
|
||||||
if metadata_cover and not os.path.exists(metadata_cover):
|
if metadata_cover and not os.path.exists(metadata_cover):
|
||||||
@ -249,14 +248,15 @@ def process_title_page(mi, filelist, htmlfilemap, opts, tdir):
|
|||||||
cpath = '/'.join(('resources', '_cover_.jpg'))
|
cpath = '/'.join(('resources', '_cover_.jpg'))
|
||||||
cover_dest = os.path.join(tdir, 'content', *cpath.split('/'))
|
cover_dest = os.path.join(tdir, 'content', *cpath.split('/'))
|
||||||
if metadata_cover is not None:
|
if metadata_cover is not None:
|
||||||
if not create_cover_image(metadata_cover, cover_dest, opts.profile.screen_size):
|
if not create_cover_image(metadata_cover, cover_dest,
|
||||||
|
opts.profile.screen_size):
|
||||||
metadata_cover = None
|
metadata_cover = None
|
||||||
|
|
||||||
specified_cover = opts.cover
|
specified_cover = opts.cover
|
||||||
if specified_cover and not os.path.exists(specified_cover):
|
if specified_cover and not os.path.exists(specified_cover):
|
||||||
specified_cover = None
|
specified_cover = None
|
||||||
if specified_cover is not None:
|
if specified_cover is not None:
|
||||||
if not create_cover_image(specified_cover, cover_dest, opts.profile.screen_size):
|
if not create_cover_image(specified_cover, cover_dest,
|
||||||
|
opts.profile.screen_size):
|
||||||
specified_cover = None
|
specified_cover = None
|
||||||
|
|
||||||
cover = metadata_cover if specified_cover is None or (opts.prefer_metadata_cover and metadata_cover is not None) else specified_cover
|
cover = metadata_cover if specified_cover is None or (opts.prefer_metadata_cover and metadata_cover is not None) else specified_cover
|
||||||
@ -271,9 +271,16 @@ def process_title_page(mi, filelist, htmlfilemap, opts, tdir):
|
|||||||
elif os.path.exists(cover_dest):
|
elif os.path.exists(cover_dest):
|
||||||
os.remove(cover_dest)
|
os.remove(cover_dest)
|
||||||
return None, old_title_page is not None
|
return None, old_title_page is not None
|
||||||
|
|
||||||
|
|
||||||
def convert(htmlfile, opts, notification=None):
|
def find_oeb_cover(htmlfile):
|
||||||
|
if os.stat(htmlfile).st_size > 2048:
|
||||||
|
return None
|
||||||
|
match = re.search(r'(?i)<img[^<>]+src\s*=\s*[\'"](.+?)[\'"]', open(htmlfile, 'rb').read())
|
||||||
|
if match:
|
||||||
|
return match.group(1)
|
||||||
|
|
||||||
|
def convert(htmlfile, opts, notification=None, create_epub=True,
|
||||||
|
oeb_cover=False, extract_to=None):
|
||||||
htmlfile = os.path.abspath(htmlfile)
|
htmlfile = os.path.abspath(htmlfile)
|
||||||
if opts.output is None:
|
if opts.output is None:
|
||||||
opts.output = os.path.splitext(os.path.basename(htmlfile))[0] + '.epub'
|
opts.output = os.path.splitext(os.path.basename(htmlfile))[0] + '.epub'
|
||||||
@ -325,7 +332,7 @@ def convert(htmlfile, opts, notification=None):
|
|||||||
|
|
||||||
title_page, has_title_page = process_title_page(mi, filelist, htmlfile_map, opts, tdir)
|
title_page, has_title_page = process_title_page(mi, filelist, htmlfile_map, opts, tdir)
|
||||||
spine = [htmlfile_map[f.path] for f in filelist]
|
spine = [htmlfile_map[f.path] for f in filelist]
|
||||||
if title_page is not None:
|
if not oeb_cover and title_page is not None:
|
||||||
spine = [title_page] + spine
|
spine = [title_page] + spine
|
||||||
mi.cover = None
|
mi.cover = None
|
||||||
mi.cover_data = (None, None)
|
mi.cover_data = (None, None)
|
||||||
@ -357,24 +364,43 @@ def convert(htmlfile, opts, notification=None):
|
|||||||
check(opf_path, opts.pretty_print)
|
check(opf_path, opts.pretty_print)
|
||||||
opf = OPF(opf_path, tdir)
|
opf = OPF(opf_path, tdir)
|
||||||
opf.remove_guide()
|
opf.remove_guide()
|
||||||
if has_title_page:
|
oeb_cover_file = None
|
||||||
|
if oeb_cover and title_page is not None:
|
||||||
|
oeb_cover_file = find_oeb_cover(os.path.join(tdir, 'content', title_page))
|
||||||
|
if has_title_page or (oeb_cover and oeb_cover_file):
|
||||||
opf.create_guide_element()
|
opf.create_guide_element()
|
||||||
opf.add_guide_item('cover', 'Cover', 'content/'+spine[0])
|
if has_title_page and not oeb_cover:
|
||||||
|
opf.add_guide_item('cover', 'Cover', 'content/'+spine[0])
|
||||||
|
if oeb_cover and oeb_cover_file:
|
||||||
|
opf.add_guide_item('cover', 'Cover', 'content/'+oeb_cover_file)
|
||||||
|
|
||||||
opf.add_path_to_manifest(os.path.join(tdir, 'content', 'resources', '_cover_.jpg'), 'image/jpeg')
|
cpath = os.path.join(tdir, 'content', 'resources', '_cover_.jpg')
|
||||||
|
if os.path.exists(cpath):
|
||||||
|
opf.add_path_to_manifest(cpath, 'image/jpeg')
|
||||||
with open(opf_path, 'wb') as f:
|
with open(opf_path, 'wb') as f:
|
||||||
raw = opf.render()
|
raw = opf.render()
|
||||||
if not raw.startswith('<?xml '):
|
if not raw.startswith('<?xml '):
|
||||||
raw = '<?xml version="1.0" encoding="UTF-8"?>\n'+raw
|
raw = '<?xml version="1.0" encoding="UTF-8"?>\n'+raw
|
||||||
f.write(raw)
|
f.write(raw)
|
||||||
epub = initialize_container(opts.output)
|
if create_epub:
|
||||||
epub.add_dir(tdir)
|
epub = initialize_container(opts.output)
|
||||||
|
epub.add_dir(tdir)
|
||||||
|
epub.close()
|
||||||
|
logger.info(_('Output written to ')+opts.output)
|
||||||
|
|
||||||
if opts.show_opf:
|
if opts.show_opf:
|
||||||
print open(os.path.join(tdir, 'metadata.opf')).read()
|
print open(os.path.join(tdir, 'metadata.opf')).read()
|
||||||
logger.info('Output written to %s'%opts.output)
|
|
||||||
if opts.extract_to is not None:
|
if opts.extract_to is not None:
|
||||||
epub.extractall(opts.extract_to)
|
if os.path.exists(opts.extract_to):
|
||||||
epub.close()
|
shutil.rmtree(opts.extract_to)
|
||||||
|
shutil.copytree(tdir, opts.extract_to)
|
||||||
|
|
||||||
|
if extract_to is not None:
|
||||||
|
if os.path.exists(extract_to):
|
||||||
|
shutil.rmtree(extract_to)
|
||||||
|
shutil.copytree(tdir, extract_to)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def main(args=sys.argv):
|
def main(args=sys.argv):
|
||||||
|
@ -170,7 +170,10 @@ class EbookIterator(object):
|
|||||||
dat = self.serialize_bookmarks(bookmarks)
|
dat = self.serialize_bookmarks(bookmarks)
|
||||||
if os.path.splitext(self.pathtoebook)[1].lower() == '.epub' and \
|
if os.path.splitext(self.pathtoebook)[1].lower() == '.epub' and \
|
||||||
os.access(self.pathtoebook, os.R_OK):
|
os.access(self.pathtoebook, os.R_OK):
|
||||||
zf = open(self.pathtoebook, 'r+b')
|
try:
|
||||||
|
zf = open(self.pathtoebook, 'r+b')
|
||||||
|
except IOError:
|
||||||
|
return
|
||||||
zipf = ZipFile(zf, mode='a')
|
zipf = ZipFile(zf, mode='a')
|
||||||
for name in zipf.namelist():
|
for name in zipf.namelist():
|
||||||
if name == 'META-INF/calibre_bookmarks.txt':
|
if name == 'META-INF/calibre_bookmarks.txt':
|
||||||
|
59
src/calibre/ebooks/lit/from_any.py
Normal file
59
src/calibre/ebooks/lit/from_any.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
from __future__ import with_statement
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
'''
|
||||||
|
Convert any ebook format to LIT.
|
||||||
|
'''
|
||||||
|
|
||||||
|
import sys, os, glob, logging
|
||||||
|
|
||||||
|
from calibre.ebooks.epub.from_any import any2epub, formats, USAGE
|
||||||
|
from calibre.ebooks.epub import config as common_config
|
||||||
|
from calibre.ptempfile import TemporaryDirectory
|
||||||
|
from calibre.ebooks.lit.writer import oeb2lit
|
||||||
|
|
||||||
|
def config(defaults=None):
|
||||||
|
c = common_config(defaults=defaults, name='lit')
|
||||||
|
return c
|
||||||
|
|
||||||
|
def option_parser(usage=USAGE):
|
||||||
|
return config().option_parser(usage=usage%('LIT', formats()))
|
||||||
|
|
||||||
|
def any2lit(opts, path):
|
||||||
|
ext = os.path.splitext(path)[1]
|
||||||
|
if not ext:
|
||||||
|
raise ValueError('Unknown file type: '+path)
|
||||||
|
ext = ext.lower()[1:]
|
||||||
|
|
||||||
|
if opts.output is None:
|
||||||
|
opts.output = os.path.splitext(os.path.basename(path))[0]+'.lit'
|
||||||
|
|
||||||
|
opts.output = os.path.abspath(opts.output)
|
||||||
|
orig_output = opts.output
|
||||||
|
|
||||||
|
with TemporaryDirectory('_any2lit') as tdir:
|
||||||
|
oebdir = os.path.join(tdir, 'oeb')
|
||||||
|
os.mkdir(oebdir)
|
||||||
|
opts.output = os.path.join(tdir, 'dummy.epub')
|
||||||
|
opts.profile = 'None'
|
||||||
|
any2epub(opts, path, create_epub=False, oeb_cover=True, extract_to=oebdir)
|
||||||
|
opf = glob.glob(os.path.join(oebdir, '*.opf'))[0]
|
||||||
|
opts.output = orig_output
|
||||||
|
logging.getLogger('html2epub').info(_('Creating LIT file from EPUB...'))
|
||||||
|
oeb2lit(opts, opf)
|
||||||
|
|
||||||
|
|
||||||
|
def main(args=sys.argv):
|
||||||
|
parser = option_parser()
|
||||||
|
opts, args = parser.parse_args(args)
|
||||||
|
if len(args) < 2:
|
||||||
|
parser.print_help()
|
||||||
|
print 'No input file specified.'
|
||||||
|
return 1
|
||||||
|
any2lit(opts, args[1])
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
@ -13,7 +13,9 @@ from types import StringTypes
|
|||||||
from itertools import izip, count
|
from itertools import izip, count
|
||||||
from urlparse import urldefrag, urlparse, urlunparse
|
from urlparse import urldefrag, urlparse, urlunparse
|
||||||
from urllib import unquote as urlunquote
|
from urllib import unquote as urlunquote
|
||||||
|
import logging
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
from calibre import LoggingInterface
|
||||||
|
|
||||||
XML_PARSER = etree.XMLParser(recover=True, resolve_entities=False)
|
XML_PARSER = etree.XMLParser(recover=True, resolve_entities=False)
|
||||||
XML_NS = 'http://www.w3.org/XML/1998/namespace'
|
XML_NS = 'http://www.w3.org/XML/1998/namespace'
|
||||||
@ -88,6 +90,13 @@ def urlnormalize(href):
|
|||||||
return urlunparse(parts)
|
return urlunparse(parts)
|
||||||
|
|
||||||
|
|
||||||
|
class FauxLogger(object):
|
||||||
|
def __getattr__(self, name):
|
||||||
|
return self
|
||||||
|
def __call__(self, message):
|
||||||
|
print message
|
||||||
|
|
||||||
|
|
||||||
class AbstractContainer(object):
|
class AbstractContainer(object):
|
||||||
def read_xml(self, path):
|
def read_xml(self, path):
|
||||||
return etree.fromstring(
|
return etree.fromstring(
|
||||||
@ -108,6 +117,10 @@ class DirContainer(AbstractContainer):
|
|||||||
with open(urlunquote(path), 'wb') as f:
|
with open(urlunquote(path), 'wb') as f:
|
||||||
return f.write(data)
|
return f.write(data)
|
||||||
|
|
||||||
|
def exists(self, path):
|
||||||
|
path = os.path.join(self.rootdir, path)
|
||||||
|
return os.path.isfile(path)
|
||||||
|
|
||||||
|
|
||||||
class Metadata(object):
|
class Metadata(object):
|
||||||
TERMS = set(['contributor', 'coverage', 'creator', 'date', 'description',
|
TERMS = set(['contributor', 'coverage', 'creator', 'date', 'description',
|
||||||
@ -530,13 +543,14 @@ class TOC(object):
|
|||||||
node.to_ncx(point, playorder, depth+1)
|
node.to_ncx(point, playorder, depth+1)
|
||||||
return parent
|
return parent
|
||||||
|
|
||||||
|
|
||||||
class OEBBook(object):
|
class OEBBook(object):
|
||||||
def __init__(self, opfpath=None, container=None):
|
def __init__(self, opfpath=None, container=None, logger=FauxLogger()):
|
||||||
if not container:
|
if not container:
|
||||||
container = DirContainer(os.path.dirname(opfpath))
|
container = DirContainer(os.path.dirname(opfpath))
|
||||||
opfpath = os.path.basename(opfpath)
|
opfpath = os.path.basename(opfpath)
|
||||||
self.container = container
|
self.container = container
|
||||||
|
self.logger = logger
|
||||||
opf = self._read_opf(opfpath)
|
opf = self._read_opf(opfpath)
|
||||||
self._all_from_opf(opf)
|
self._all_from_opf(opf)
|
||||||
|
|
||||||
@ -590,17 +604,28 @@ class OEBBook(object):
|
|||||||
if item.id == uid:
|
if item.id == uid:
|
||||||
self.uid = item
|
self.uid = item
|
||||||
break
|
break
|
||||||
|
else:
|
||||||
|
self.logger.log_warn(u'Unique-identifier %r not found.' % uid)
|
||||||
|
self.uid = metadata.identifier[0]
|
||||||
|
|
||||||
def _manifest_from_opf(self, opf):
|
def _manifest_from_opf(self, opf):
|
||||||
self.manifest = manifest = Manifest(self)
|
self.manifest = manifest = Manifest(self)
|
||||||
for elem in xpath(opf, '/o2:package/o2:manifest/o2:item'):
|
for elem in xpath(opf, '/o2:package/o2:manifest/o2:item'):
|
||||||
manifest.add(elem.get('id'), elem.get('href'),
|
href = elem.get('href')
|
||||||
elem.get('media-type'), elem.get('fallback'))
|
if not self.container.exists(href):
|
||||||
|
self.logger.log_warn(u'Manifest item %r not found.' % href)
|
||||||
|
continue
|
||||||
|
manifest.add(elem.get('id'), href, elem.get('media-type'),
|
||||||
|
elem.get('fallback'))
|
||||||
|
|
||||||
def _spine_from_opf(self, opf):
|
def _spine_from_opf(self, opf):
|
||||||
self.spine = spine = Spine(self)
|
self.spine = spine = Spine(self)
|
||||||
for elem in xpath(opf, '/o2:package/o2:spine/o2:itemref'):
|
for elem in xpath(opf, '/o2:package/o2:spine/o2:itemref'):
|
||||||
item = self.manifest[elem.get('idref')]
|
idref = elem.get('idref')
|
||||||
|
if idref not in self.manifest:
|
||||||
|
self.logger.log_warn(u'Spine item %r not found.' % idref)
|
||||||
|
continue
|
||||||
|
item = self.manifest[idref]
|
||||||
spine.add(item, elem.get('linear'))
|
spine.add(item, elem.get('linear'))
|
||||||
extras = []
|
extras = []
|
||||||
for item in self.manifest.values():
|
for item in self.manifest.values():
|
||||||
@ -614,7 +639,11 @@ class OEBBook(object):
|
|||||||
def _guide_from_opf(self, opf):
|
def _guide_from_opf(self, opf):
|
||||||
self.guide = guide = Guide(self)
|
self.guide = guide = Guide(self)
|
||||||
for elem in xpath(opf, '/o2:package/o2:guide/o2:reference'):
|
for elem in xpath(opf, '/o2:package/o2:guide/o2:reference'):
|
||||||
guide.add(elem.get('type'), elem.get('title'), elem.get('href'))
|
href = elem.get('href')
|
||||||
|
if href not in self.manifest.hrefs:
|
||||||
|
self.logger.log_warn(u'Guide reference %r not found' % href)
|
||||||
|
continue
|
||||||
|
guide.add(elem.get('type'), elem.get('title'), href)
|
||||||
|
|
||||||
def _toc_from_navpoint(self, toc, navpoint):
|
def _toc_from_navpoint(self, toc, navpoint):
|
||||||
children = xpath(navpoint, 'ncx:navPoint')
|
children = xpath(navpoint, 'ncx:navPoint')
|
||||||
|
@ -9,7 +9,7 @@ __copyright__ = '2008, Marshall T. Vandegrift <llasram@gmail.com>'
|
|||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
from struct import pack, unpack
|
from struct import pack
|
||||||
from itertools import izip, count, chain
|
from itertools import izip, count, chain
|
||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
@ -17,19 +17,20 @@ import re
|
|||||||
import copy
|
import copy
|
||||||
import uuid
|
import uuid
|
||||||
import functools
|
import functools
|
||||||
|
import logging
|
||||||
from urlparse import urldefrag
|
from urlparse import urldefrag
|
||||||
from urllib import unquote as urlunquote
|
from urllib import unquote as urlunquote
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
from calibre.ebooks.lit import LitError
|
from calibre.ebooks.lit.reader import DirectoryEntry
|
||||||
from calibre.ebooks.lit.reader import msguid, DirectoryEntry
|
|
||||||
import calibre.ebooks.lit.maps as maps
|
import calibre.ebooks.lit.maps as maps
|
||||||
from calibre.ebooks.lit.oeb import OEB_DOCS, OEB_STYLES, OEB_CSS_MIME, \
|
from calibre.ebooks.lit.oeb import OEB_DOCS, OEB_STYLES, OEB_CSS_MIME, \
|
||||||
CSS_MIME, XHTML_MIME, OPF_MIME, XML_NS, XML
|
CSS_MIME, OPF_MIME, XML_NS, XML
|
||||||
from calibre.ebooks.lit.oeb import namespace, barename, urlnormalize, xpath
|
from calibre.ebooks.lit.oeb import namespace, barename, urlnormalize, xpath
|
||||||
from calibre.ebooks.lit.oeb import OEBBook
|
from calibre.ebooks.lit.oeb import FauxLogger, OEBBook
|
||||||
from calibre.ebooks.lit.stylizer import Stylizer
|
from calibre.ebooks.lit.stylizer import Stylizer
|
||||||
from calibre.ebooks.lit.lzx import Compressor
|
from calibre.ebooks.lit.lzx import Compressor
|
||||||
import calibre
|
import calibre
|
||||||
|
from calibre import LoggingInterface
|
||||||
from calibre import plugins
|
from calibre import plugins
|
||||||
msdes, msdeserror = plugins['msdes']
|
msdes, msdeserror = plugins['msdes']
|
||||||
import calibre.ebooks.lit.mssha1 as mssha1
|
import calibre.ebooks.lit.mssha1 as mssha1
|
||||||
@ -135,11 +136,15 @@ def decint(value):
|
|||||||
def randbytes(n):
|
def randbytes(n):
|
||||||
return ''.join(chr(random.randint(0, 255)) for x in xrange(n))
|
return ''.join(chr(random.randint(0, 255)) for x in xrange(n))
|
||||||
|
|
||||||
|
def warn(x):
|
||||||
|
print x
|
||||||
|
|
||||||
class ReBinary(object):
|
class ReBinary(object):
|
||||||
NSRMAP = {'': None, XML_NS: 'xml'}
|
NSRMAP = {'': None, XML_NS: 'xml'}
|
||||||
|
|
||||||
def __init__(self, root, path, oeb, map=HTML_MAP):
|
def __init__(self, root, path, oeb, map=HTML_MAP, logger=FauxLogger()):
|
||||||
self.path = path
|
self.path = path
|
||||||
|
self.logger = logger
|
||||||
self.dir = os.path.dirname(path)
|
self.dir = os.path.dirname(path)
|
||||||
self.manifest = oeb.manifest
|
self.manifest = oeb.manifest
|
||||||
self.tags, self.tattrs = map
|
self.tags, self.tattrs = map
|
||||||
@ -268,8 +273,8 @@ class ReBinary(object):
|
|||||||
|
|
||||||
def build_ahc(self):
|
def build_ahc(self):
|
||||||
if len(self.anchors) > 6:
|
if len(self.anchors) > 6:
|
||||||
print "calibre: warning: More than six anchors in file %r. " \
|
self.logger.log_warn("More than six anchors in file %r. " \
|
||||||
"Some links may not work properly." % self.path
|
"Some links may not work properly." % self.path)
|
||||||
data = StringIO()
|
data = StringIO()
|
||||||
data.write(unichr(len(self.anchors)).encode('utf-8'))
|
data.write(unichr(len(self.anchors)).encode('utf-8'))
|
||||||
for anchor, offset in self.anchors:
|
for anchor, offset in self.anchors:
|
||||||
@ -293,8 +298,9 @@ def preserve(function):
|
|||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
class LitWriter(object):
|
class LitWriter(object):
|
||||||
def __init__(self, oeb):
|
def __init__(self, oeb, logger=FauxLogger()):
|
||||||
self._oeb = oeb
|
self._oeb = oeb
|
||||||
|
self._logger = logger
|
||||||
self._litize_oeb()
|
self._litize_oeb()
|
||||||
|
|
||||||
def _litize_oeb(self):
|
def _litize_oeb(self):
|
||||||
@ -307,6 +313,9 @@ class LitWriter(object):
|
|||||||
elif MS_COVER_TYPE in oeb.guide:
|
elif MS_COVER_TYPE in oeb.guide:
|
||||||
href = oeb.guide[MS_COVER_TYPE].href
|
href = oeb.guide[MS_COVER_TYPE].href
|
||||||
cover = oeb.manifest.hrefs[href]
|
cover = oeb.manifest.hrefs[href]
|
||||||
|
elif 'cover' in oeb.guide:
|
||||||
|
href = oeb.guide['cover'].href
|
||||||
|
cover = oeb.manifest.hrefs[href]
|
||||||
else:
|
else:
|
||||||
html = oeb.spine[0].data
|
html = oeb.spine[0].data
|
||||||
imgs = xpath(html, '//img[position()=1]')
|
imgs = xpath(html, '//img[position()=1]')
|
||||||
@ -319,7 +328,7 @@ class LitWriter(object):
|
|||||||
if type not in oeb.guide:
|
if type not in oeb.guide:
|
||||||
oeb.guide.add(type, title, cover.href)
|
oeb.guide.add(type, title, cover.href)
|
||||||
else:
|
else:
|
||||||
print "calibre: warning: No suitable cover image found."
|
self._logger.log_warn('No suitable cover image found.')
|
||||||
|
|
||||||
def dump(self, stream):
|
def dump(self, stream):
|
||||||
self._stream = stream
|
self._stream = stream
|
||||||
@ -461,15 +470,16 @@ class LitWriter(object):
|
|||||||
self._add_folder('/data')
|
self._add_folder('/data')
|
||||||
for item in self._oeb.manifest.values():
|
for item in self._oeb.manifest.values():
|
||||||
if item.media_type not in LIT_MIMES:
|
if item.media_type not in LIT_MIMES:
|
||||||
print "calibre: warning: File %r of unknown media-type %r " \
|
self._logger.log_warn("File %r of unknown media-type %r " \
|
||||||
"excluded from output." % (item.href, item.media_type)
|
"excluded from output." % (item.href, item.media_type))
|
||||||
continue
|
continue
|
||||||
name = '/data/' + item.id
|
name = '/data/' + item.id
|
||||||
data = item.data
|
data = item.data
|
||||||
secnum = 0
|
secnum = 0
|
||||||
if not isinstance(data, basestring):
|
if not isinstance(data, basestring):
|
||||||
self._add_folder(name)
|
self._add_folder(name)
|
||||||
rebin = ReBinary(data, item.href, self._oeb)
|
rebin = ReBinary(data, item.href, self._oeb, map=HTML_MAP,
|
||||||
|
logger=self._logger)
|
||||||
self._add_file(name + '/ahc', rebin.ahc, 0)
|
self._add_file(name + '/ahc', rebin.ahc, 0)
|
||||||
self._add_file(name + '/aht', rebin.aht, 0)
|
self._add_file(name + '/aht', rebin.aht, 0)
|
||||||
item.page_breaks = rebin.page_breaks
|
item.page_breaks = rebin.page_breaks
|
||||||
@ -548,7 +558,8 @@ class LitWriter(object):
|
|||||||
meta.attrib['ms--minimum_level'] = '0'
|
meta.attrib['ms--minimum_level'] = '0'
|
||||||
meta.attrib['ms--attr5'] = '1'
|
meta.attrib['ms--attr5'] = '1'
|
||||||
meta.attrib['ms--guid'] = '{%s}' % str(uuid.uuid4()).upper()
|
meta.attrib['ms--guid'] = '{%s}' % str(uuid.uuid4()).upper()
|
||||||
rebin = ReBinary(meta, 'content.opf', self._oeb, OPF_MAP)
|
rebin = ReBinary(meta, 'content.opf', self._oeb, map=OPF_MAP,
|
||||||
|
logger=self._logger)
|
||||||
meta = rebin.content
|
meta = rebin.content
|
||||||
self._meta = meta
|
self._meta = meta
|
||||||
self._add_file('/meta', meta)
|
self._add_file('/meta', meta)
|
||||||
@ -707,8 +718,25 @@ def option_parser():
|
|||||||
parser.add_option(
|
parser.add_option(
|
||||||
'-o', '--output', default=None,
|
'-o', '--output', default=None,
|
||||||
help=_('Output file. Default is derived from input filename.'))
|
help=_('Output file. Default is derived from input filename.'))
|
||||||
|
parser.add_option(
|
||||||
|
'--verbose', default=False, action='store_true',
|
||||||
|
help=_('Useful for debugging.'))
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
|
def oeb2lit(opts, opfpath):
|
||||||
|
logger = LoggingInterface(logging.getLogger('oeb2lit'))
|
||||||
|
logger.setup_cli_handler(opts.verbose)
|
||||||
|
litpath = opts.output
|
||||||
|
if litpath is None:
|
||||||
|
litpath = os.path.basename(opfpath)
|
||||||
|
litpath = os.path.splitext(litpath)[0] + '.lit'
|
||||||
|
litpath = os.path.abspath(litpath)
|
||||||
|
lit = LitWriter(OEBBook(opfpath))
|
||||||
|
with open(litpath, 'wb') as f:
|
||||||
|
lit.dump(f)
|
||||||
|
logger.log_info(_('Output written to ')+litpath)
|
||||||
|
|
||||||
|
|
||||||
def main(argv=sys.argv):
|
def main(argv=sys.argv):
|
||||||
parser = option_parser()
|
parser = option_parser()
|
||||||
opts, args = parser.parse_args(argv[1:])
|
opts, args = parser.parse_args(argv[1:])
|
||||||
@ -716,14 +744,7 @@ def main(argv=sys.argv):
|
|||||||
parser.print_help()
|
parser.print_help()
|
||||||
return 1
|
return 1
|
||||||
opfpath = args[0]
|
opfpath = args[0]
|
||||||
litpath = opts.output
|
oeb2lit(opts, opfpath)
|
||||||
if litpath is None:
|
|
||||||
litpath = os.path.basename(opfpath)
|
|
||||||
litpath = os.path.splitext(litpath)[0] + '.lit'
|
|
||||||
lit = LitWriter(OEBBook(opfpath))
|
|
||||||
with open(litpath, 'wb') as f:
|
|
||||||
lit.dump(f)
|
|
||||||
print _('LIT ebook created at'), litpath
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -1920,7 +1920,7 @@ def process_file(path, options, logger=None):
|
|||||||
options.anchor_ids = True
|
options.anchor_ids = True
|
||||||
files = options.spine if (options.use_spine and hasattr(options, 'spine')) else [path]
|
files = options.spine if (options.use_spine and hasattr(options, 'spine')) else [path]
|
||||||
conv = HTMLConverter(book, fonts, options, logger, files)
|
conv = HTMLConverter(book, fonts, options, logger, files)
|
||||||
if options.use_spine and hasattr(options, 'toc'):
|
if options.use_spine and hasattr(options, 'toc') and options.toc is not None:
|
||||||
conv.create_toc(options.toc)
|
conv.create_toc(options.toc)
|
||||||
oname = options.output
|
oname = options.output
|
||||||
if not oname:
|
if not oname:
|
||||||
|
@ -101,7 +101,6 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
|||||||
for item in items:
|
for item in items:
|
||||||
self.language.addItem(item[1], QVariant(item[0]))
|
self.language.addItem(item[1], QVariant(item[0]))
|
||||||
|
|
||||||
self.output_format.setCurrentIndex(0 if prefs['output_format'] == 'LRF' else 1)
|
|
||||||
self.pdf_metadata.setChecked(prefs['read_file_metadata'])
|
self.pdf_metadata.setChecked(prefs['read_file_metadata'])
|
||||||
|
|
||||||
added_html = False
|
added_html = False
|
||||||
@ -255,16 +254,11 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
|||||||
sc.set('max_cover', mcs)
|
sc.set('max_cover', mcs)
|
||||||
config['delete_news_from_library_on_upload'] = self.delete_news.isChecked()
|
config['delete_news_from_library_on_upload'] = self.delete_news.isChecked()
|
||||||
config['upload_news_to_device'] = self.sync_news.isChecked()
|
config['upload_news_to_device'] = self.sync_news.isChecked()
|
||||||
of = str(self.output_format.currentText())
|
|
||||||
fmts = []
|
fmts = []
|
||||||
for i in range(self.viewer.count()):
|
for i in range(self.viewer.count()):
|
||||||
if self.viewer.item(i).checkState() == Qt.Checked:
|
if self.viewer.item(i).checkState() == Qt.Checked:
|
||||||
fmts.append(str(self.viewer.item(i).text()))
|
fmts.append(str(self.viewer.item(i).text()))
|
||||||
config['internally_viewed_formats'] = fmts
|
config['internally_viewed_formats'] = fmts
|
||||||
if of != prefs['output_format'] and 'epub' in of.lower():
|
|
||||||
warning_dialog(self, 'Warning',
|
|
||||||
'<p>EPUB support is still in beta. If you find bugs, please report them by opening a <a href="http://calibre.kovidgoyal.net">ticket</a>.').exec_()
|
|
||||||
prefs['output_format'] = of
|
|
||||||
|
|
||||||
if not path or not os.path.exists(path) or not os.path.isdir(path):
|
if not path or not os.path.exists(path) or not os.path.isdir(path):
|
||||||
d = error_dialog(self, _('Invalid database location'),
|
d = error_dialog(self, _('Invalid database location'),
|
||||||
|
@ -149,7 +149,7 @@
|
|||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QGridLayout" name="gridLayout_2" >
|
<layout class="QGridLayout" name="gridLayout_2" >
|
||||||
<item row="1" column="0" >
|
<item row="0" column="0" >
|
||||||
<widget class="QLabel" name="label_5" >
|
<widget class="QLabel" name="label_5" >
|
||||||
<property name="text" >
|
<property name="text" >
|
||||||
<string>Format for &single file save:</string>
|
<string>Format for &single file save:</string>
|
||||||
@ -159,10 +159,10 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1" >
|
<item row="0" column="1" >
|
||||||
<widget class="QComboBox" name="single_format" />
|
<widget class="QComboBox" name="single_format" />
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0" >
|
<item row="1" column="0" >
|
||||||
<widget class="QLabel" name="label_2" >
|
<widget class="QLabel" name="label_2" >
|
||||||
<property name="text" >
|
<property name="text" >
|
||||||
<string>Default network &timeout:</string>
|
<string>Default network &timeout:</string>
|
||||||
@ -172,7 +172,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1" >
|
<item row="1" column="1" >
|
||||||
<widget class="QSpinBox" name="timeout" >
|
<widget class="QSpinBox" name="timeout" >
|
||||||
<property name="toolTip" >
|
<property name="toolTip" >
|
||||||
<string>Set the default timeout for network fetches (i.e. anytime we go out to the internet to get information)</string>
|
<string>Set the default timeout for network fetches (i.e. anytime we go out to the internet to get information)</string>
|
||||||
@ -191,10 +191,10 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="1" >
|
<item row="2" column="1" >
|
||||||
<widget class="QComboBox" name="language" />
|
<widget class="QComboBox" name="language" />
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0" >
|
<item row="2" column="0" >
|
||||||
<widget class="QLabel" name="label_7" >
|
<widget class="QLabel" name="label_7" >
|
||||||
<property name="text" >
|
<property name="text" >
|
||||||
<string>Choose &language (requires restart):</string>
|
<string>Choose &language (requires restart):</string>
|
||||||
@ -204,34 +204,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1" >
|
<item row="3" column="1" >
|
||||||
<widget class="QComboBox" name="output_format" >
|
|
||||||
<property name="toolTip" >
|
|
||||||
<string>The default output format for ebook conversions.</string>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<property name="text" >
|
|
||||||
<string>LRF</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text" >
|
|
||||||
<string>EPUB</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0" >
|
|
||||||
<widget class="QLabel" name="label_8" >
|
|
||||||
<property name="text" >
|
|
||||||
<string>&Output format:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy" >
|
|
||||||
<cstring>output_format</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="1" >
|
|
||||||
<widget class="QComboBox" name="priority" >
|
<widget class="QComboBox" name="priority" >
|
||||||
<item>
|
<item>
|
||||||
<property name="text" >
|
<property name="text" >
|
||||||
@ -250,7 +223,7 @@
|
|||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0" >
|
<item row="3" column="0" >
|
||||||
<widget class="QLabel" name="priority_label" >
|
<widget class="QLabel" name="priority_label" >
|
||||||
<property name="text" >
|
<property name="text" >
|
||||||
<string>Job &priority:</string>
|
<string>Job &priority:</string>
|
||||||
|
581
src/calibre/gui2/images/books_in_series.svg
Normal file
581
src/calibre/gui2/images/books_in_series.svg
Normal file
@ -0,0 +1,581 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 12.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
|
||||||
|
<svg
|
||||||
|
xmlns:ns="http://ns.adobe.com/SaveForWeb/1.0/"
|
||||||
|
xmlns:i="http://ns.adobe.com/AdobeIllustrator/10.0/"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://web.resource.org/cc/"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.1"
|
||||||
|
id="Layer_1"
|
||||||
|
width="128"
|
||||||
|
height="128"
|
||||||
|
viewBox="0 0 128 128"
|
||||||
|
overflow="visible"
|
||||||
|
enable-background="new 0 0 128 128"
|
||||||
|
xml:space="preserve"
|
||||||
|
sodipodi:version="0.32"
|
||||||
|
inkscape:version="0.45.1"
|
||||||
|
sodipodi:docname="edit-copy.svg"
|
||||||
|
sodipodi:docbase="/home/david/sandbox"
|
||||||
|
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||||
|
inkscape:export-filename="/home/david/sandbox/edit-copy.png"
|
||||||
|
inkscape:export-xdpi="22.5"
|
||||||
|
inkscape:export-ydpi="22.5"><defs
|
||||||
|
id="defs105"><linearGradient
|
||||||
|
id="linearGradient3291"><stop
|
||||||
|
style="stop-color:black;stop-opacity:1"
|
||||||
|
offset="0"
|
||||||
|
id="stop3293" /><stop
|
||||||
|
style="stop-color:black;stop-opacity:0"
|
||||||
|
offset="1"
|
||||||
|
id="stop3295" /></linearGradient><linearGradient
|
||||||
|
y2="0"
|
||||||
|
x2="28"
|
||||||
|
y1="57.5"
|
||||||
|
x1="28"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="linearGradient18668">
|
||||||
|
<stop
|
||||||
|
id="stop18670"
|
||||||
|
style="stop-color:#fffccf;stop-opacity:1;"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop18672"
|
||||||
|
style="stop-color:white;stop-opacity:0;"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient><linearGradient
|
||||||
|
y2="0"
|
||||||
|
x2="28"
|
||||||
|
y1="57.5"
|
||||||
|
x1="28"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
id="linearGradient15967">
|
||||||
|
<stop
|
||||||
|
id="stop15969"
|
||||||
|
style="stop-color:white;stop-opacity:1;"
|
||||||
|
offset="0" />
|
||||||
|
<stop
|
||||||
|
id="stop15971"
|
||||||
|
style="stop-color:white;stop-opacity:0;"
|
||||||
|
offset="1" />
|
||||||
|
</linearGradient><linearGradient
|
||||||
|
id="XMLID_2_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="28"
|
||||||
|
y1="57.5"
|
||||||
|
x2="28"
|
||||||
|
y2="0">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#FFEA00"
|
||||||
|
id="stop12" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#FFCC00"
|
||||||
|
id="stop14" />
|
||||||
|
</linearGradient><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_9_"
|
||||||
|
id="linearGradient2391"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="94.3438"
|
||||||
|
y1="102.3447"
|
||||||
|
x2="86.5356"
|
||||||
|
y2="94.5366" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_10_"
|
||||||
|
id="linearGradient2393"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="95"
|
||||||
|
y1="103"
|
||||||
|
x2="86.5865"
|
||||||
|
y2="94.5865" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_11_"
|
||||||
|
id="linearGradient2395"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="95"
|
||||||
|
y1="103"
|
||||||
|
x2="87.293"
|
||||||
|
y2="95.293" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_12_"
|
||||||
|
id="linearGradient2397"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="96"
|
||||||
|
y1="104"
|
||||||
|
x2="88.0002"
|
||||||
|
y2="96.0002" /><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_7_"
|
||||||
|
id="radialGradient2465"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
cx="102"
|
||||||
|
cy="112.3047"
|
||||||
|
r="139.5585" /><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_8_"
|
||||||
|
id="radialGradient2467"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
cx="102"
|
||||||
|
cy="112.3047"
|
||||||
|
r="139.55859" />
|
||||||
|
<foreignObject
|
||||||
|
requiredExtensions="http://ns.adobe.com/AdobeIllustrator/10.0/"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
width="1"
|
||||||
|
height="1"
|
||||||
|
id="foreignObject7">
|
||||||
|
<i:pgfRef
|
||||||
|
xlink:href="#adobe_illustrator_pgf">
|
||||||
|
</i:pgfRef>
|
||||||
|
</foreignObject>
|
||||||
|
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_2_"
|
||||||
|
id="linearGradient12378"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="28"
|
||||||
|
y1="57.5"
|
||||||
|
x2="28"
|
||||||
|
y2="0" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2309"
|
||||||
|
id="linearGradient14180"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="-74.820707"
|
||||||
|
y1="100.82378"
|
||||||
|
x2="-18.121965"
|
||||||
|
y2="100.82378" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2309"
|
||||||
|
id="linearGradient14189"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="-74.820707"
|
||||||
|
y1="100.82378"
|
||||||
|
x2="-18.121965"
|
||||||
|
y2="100.82378" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient15967"
|
||||||
|
id="linearGradient15973"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="27.719746"
|
||||||
|
y1="7.881104"
|
||||||
|
x2="27.719746"
|
||||||
|
y2="30.441185"
|
||||||
|
gradientTransform="translate(1.470416e-5,0)" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient2309"
|
||||||
|
id="linearGradient15977"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="-74.820707"
|
||||||
|
y1="100.82378"
|
||||||
|
x2="-18.121965"
|
||||||
|
y2="100.82378" /><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient15967"
|
||||||
|
id="radialGradient15986"
|
||||||
|
cx="26.954102"
|
||||||
|
cy="31.045055"
|
||||||
|
fx="26.954102"
|
||||||
|
fy="31.045055"
|
||||||
|
r="8.968153"
|
||||||
|
gradientTransform="matrix(0.754978,-2.959381e-2,0,0.905772,7.650275,10.87807)"
|
||||||
|
gradientUnits="userSpaceOnUse" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_2_"
|
||||||
|
id="linearGradient18657"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="28"
|
||||||
|
y1="57.5"
|
||||||
|
x2="28"
|
||||||
|
y2="0" />
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient18649"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="28"
|
||||||
|
y1="57.5"
|
||||||
|
x2="28"
|
||||||
|
y2="0">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#FFEA00"
|
||||||
|
id="stop18651" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#FFCC00"
|
||||||
|
id="stop18653" />
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient18668"
|
||||||
|
id="linearGradient18674"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="-39.53125"
|
||||||
|
y1="78"
|
||||||
|
x2="-39.53125"
|
||||||
|
y2="51.1875" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#linearGradient18668"
|
||||||
|
id="linearGradient18746"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="-39.53125"
|
||||||
|
y1="78"
|
||||||
|
x2="-39.53125"
|
||||||
|
y2="51.1875" /><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_8_"
|
||||||
|
id="radialGradient2311"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.856383,0,0,0.8415585,11.191492,18.14026)"
|
||||||
|
cx="99.080742"
|
||||||
|
cy="109.33402"
|
||||||
|
r="139.55859"
|
||||||
|
fx="99.080742"
|
||||||
|
fy="109.33402" /><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_7_"
|
||||||
|
id="radialGradient2314"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.8749999,0,0,0.8571428,10.000003,17.142857)"
|
||||||
|
cx="102"
|
||||||
|
cy="112.3047"
|
||||||
|
r="139.5585" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_12_"
|
||||||
|
id="linearGradient2339"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="96"
|
||||||
|
y1="104"
|
||||||
|
x2="86.571632"
|
||||||
|
y2="94.104362"
|
||||||
|
gradientTransform="matrix(0.8749999,0,0,0.8571428,10.000003,17.142857)" /><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="filter6241"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="1.2065414"
|
||||||
|
id="feGaussianBlur6243" /></filter><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_7_"
|
||||||
|
id="radialGradient6272"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.8749999,0,0,0.8571428,10.000003,17.142857)"
|
||||||
|
cx="102"
|
||||||
|
cy="112.3047"
|
||||||
|
r="139.5585" /><radialGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_8_"
|
||||||
|
id="radialGradient6274"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.856383,0,0,0.8415585,11.191492,18.14026)"
|
||||||
|
cx="99.080742"
|
||||||
|
cy="109.33402"
|
||||||
|
fx="99.080742"
|
||||||
|
fy="109.33402"
|
||||||
|
r="139.55859" /><linearGradient
|
||||||
|
inkscape:collect="always"
|
||||||
|
xlink:href="#XMLID_12_"
|
||||||
|
id="linearGradient6276"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
gradientTransform="matrix(0.8749999,0,0,0.8571428,10.000003,17.142857)"
|
||||||
|
x1="96"
|
||||||
|
y1="104"
|
||||||
|
x2="86.571632"
|
||||||
|
y2="94.104362" /><filter
|
||||||
|
inkscape:collect="always"
|
||||||
|
id="filter3217"><feGaussianBlur
|
||||||
|
inkscape:collect="always"
|
||||||
|
stdDeviation="0.89955545"
|
||||||
|
id="feGaussianBlur3219" /></filter></defs><sodipodi:namedview
|
||||||
|
inkscape:window-height="670"
|
||||||
|
inkscape:window-width="1022"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
guidetolerance="10.0"
|
||||||
|
gridtolerance="10.0"
|
||||||
|
objecttolerance="10.0"
|
||||||
|
borderopacity="1.0"
|
||||||
|
bordercolor="#666666"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
id="base"
|
||||||
|
inkscape:zoom="4"
|
||||||
|
inkscape:cx="92.02737"
|
||||||
|
inkscape:cy="54.798944"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:current-layer="g6245"
|
||||||
|
showgrid="true"
|
||||||
|
gridspacingx="4px"
|
||||||
|
gridspacingy="4px"
|
||||||
|
gridempspacing="0"
|
||||||
|
inkscape:grid-points="false"
|
||||||
|
inkscape:grid-bbox="true"
|
||||||
|
inkscape:object-points="true" />
|
||||||
|
<metadata
|
||||||
|
id="metadata3">
|
||||||
|
<ns:sfw>
|
||||||
|
<ns:slices>
|
||||||
|
<ns:slice
|
||||||
|
y="0"
|
||||||
|
x="0"
|
||||||
|
height="128"
|
||||||
|
width="128"
|
||||||
|
sliceID="1316743234" />
|
||||||
|
</ns:slices>
|
||||||
|
<ns:sliceSourceBounds
|
||||||
|
y="0"
|
||||||
|
x="0"
|
||||||
|
height="128"
|
||||||
|
width="128"
|
||||||
|
bottomLeftOrigin="true" />
|
||||||
|
<ns:optimizationSettings>
|
||||||
|
<ns:targetSettings
|
||||||
|
targetSettingsID="0"
|
||||||
|
fileFormat="PNG24Format">
|
||||||
|
<ns:PNG24Format
|
||||||
|
transparency="true"
|
||||||
|
filtered="false"
|
||||||
|
matteColor="#FFFFFF"
|
||||||
|
noMatteColor="false"
|
||||||
|
interlaced="false">
|
||||||
|
</ns:PNG24Format>
|
||||||
|
</ns:targetSettings>
|
||||||
|
<ns:targetSettings
|
||||||
|
targetSettingsID="1696735251"
|
||||||
|
fileFormat="PNG24Format">
|
||||||
|
<ns:PNG24Format
|
||||||
|
transparency="true"
|
||||||
|
filtered="false"
|
||||||
|
matteColor="#FFFFFF"
|
||||||
|
noMatteColor="false"
|
||||||
|
interlaced="false">
|
||||||
|
</ns:PNG24Format>
|
||||||
|
</ns:targetSettings>
|
||||||
|
</ns:optimizationSettings>
|
||||||
|
</ns:sfw>
|
||||||
|
<rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata>
|
||||||
|
|
||||||
|
<radialGradient
|
||||||
|
id="XMLID_7_"
|
||||||
|
cx="102"
|
||||||
|
cy="112.3047"
|
||||||
|
r="139.5585"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#00537D"
|
||||||
|
id="stop16" />
|
||||||
|
<stop
|
||||||
|
offset="0.0151"
|
||||||
|
style="stop-color:#186389"
|
||||||
|
id="stop18" />
|
||||||
|
<stop
|
||||||
|
offset="0.0558"
|
||||||
|
style="stop-color:#558CA8"
|
||||||
|
id="stop20" />
|
||||||
|
<stop
|
||||||
|
offset="0.0964"
|
||||||
|
style="stop-color:#89AFC3"
|
||||||
|
id="stop22" />
|
||||||
|
<stop
|
||||||
|
offset="0.1357"
|
||||||
|
style="stop-color:#B3CCD8"
|
||||||
|
id="stop24" />
|
||||||
|
<stop
|
||||||
|
offset="0.1737"
|
||||||
|
style="stop-color:#D4E2E9"
|
||||||
|
id="stop26" />
|
||||||
|
<stop
|
||||||
|
offset="0.2099"
|
||||||
|
style="stop-color:#ECF2F5"
|
||||||
|
id="stop28" />
|
||||||
|
<stop
|
||||||
|
offset="0.2435"
|
||||||
|
style="stop-color:#FAFCFD"
|
||||||
|
id="stop30" />
|
||||||
|
<stop
|
||||||
|
offset="0.2722"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
id="stop32" />
|
||||||
|
</radialGradient>
|
||||||
|
|
||||||
|
<radialGradient
|
||||||
|
id="XMLID_8_"
|
||||||
|
cx="102"
|
||||||
|
cy="112.3047"
|
||||||
|
r="139.55859"
|
||||||
|
gradientUnits="userSpaceOnUse">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#7a7d80;stop-opacity:1;"
|
||||||
|
id="stop37" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<stop
|
||||||
|
offset="0.12617962"
|
||||||
|
style="stop-color:#c2c2c2;stop-opacity:1;"
|
||||||
|
id="stop47" />
|
||||||
|
<stop
|
||||||
|
offset="0.23250513"
|
||||||
|
style="stop-color:#FAFAFA"
|
||||||
|
id="stop49" />
|
||||||
|
<stop
|
||||||
|
offset="0.2722"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
id="stop51" />
|
||||||
|
<stop
|
||||||
|
offset="0.5313"
|
||||||
|
style="stop-color:#FAFAFA"
|
||||||
|
id="stop53" />
|
||||||
|
<stop
|
||||||
|
offset="0.8449"
|
||||||
|
style="stop-color:#EBECEC"
|
||||||
|
id="stop55" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#E1E2E3"
|
||||||
|
id="stop57" />
|
||||||
|
</radialGradient>
|
||||||
|
|
||||||
|
<linearGradient
|
||||||
|
id="XMLID_9_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="94.3438"
|
||||||
|
y1="102.3447"
|
||||||
|
x2="86.5356"
|
||||||
|
y2="94.5366">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
id="stop62" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#555753"
|
||||||
|
id="stop64" />
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<linearGradient
|
||||||
|
id="XMLID_10_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="95"
|
||||||
|
y1="103"
|
||||||
|
x2="86.5865"
|
||||||
|
y2="94.5865">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
id="stop69" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#555753"
|
||||||
|
id="stop71" />
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<linearGradient
|
||||||
|
id="XMLID_11_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="95"
|
||||||
|
y1="103"
|
||||||
|
x2="87.293"
|
||||||
|
y2="95.293">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
id="stop76" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#393B38"
|
||||||
|
id="stop78" />
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<linearGradient
|
||||||
|
id="XMLID_12_"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
x1="96"
|
||||||
|
y1="104"
|
||||||
|
x2="88.0002"
|
||||||
|
y2="96.0002">
|
||||||
|
<stop
|
||||||
|
offset="0"
|
||||||
|
style="stop-color:#888A85"
|
||||||
|
id="stop83" />
|
||||||
|
<stop
|
||||||
|
offset="0.0072"
|
||||||
|
style="stop-color:#8C8E89"
|
||||||
|
id="stop85" />
|
||||||
|
<stop
|
||||||
|
offset="0.0673"
|
||||||
|
style="stop-color:#ABACA9"
|
||||||
|
id="stop87" />
|
||||||
|
<stop
|
||||||
|
offset="0.1347"
|
||||||
|
style="stop-color:#C5C6C4"
|
||||||
|
id="stop89" />
|
||||||
|
<stop
|
||||||
|
offset="0.2115"
|
||||||
|
style="stop-color:#DBDBDA"
|
||||||
|
id="stop91" />
|
||||||
|
<stop
|
||||||
|
offset="0.3012"
|
||||||
|
style="stop-color:#EBEBEB"
|
||||||
|
id="stop93" />
|
||||||
|
<stop
|
||||||
|
offset="0.4122"
|
||||||
|
style="stop-color:#F7F7F6"
|
||||||
|
id="stop95" />
|
||||||
|
<stop
|
||||||
|
offset="0.5679"
|
||||||
|
style="stop-color:#FDFDFD"
|
||||||
|
id="stop97" />
|
||||||
|
<stop
|
||||||
|
offset="1"
|
||||||
|
style="stop-color:#FFFFFF"
|
||||||
|
id="stop99" />
|
||||||
|
</linearGradient>
|
||||||
|
<g
|
||||||
|
id="g6245"><g
|
||||||
|
id="g6263"
|
||||||
|
transform="translate(12,0)"
|
||||||
|
style="opacity:1"><path
|
||||||
|
sodipodi:nodetypes="cccccc"
|
||||||
|
id="path2350"
|
||||||
|
d="M 23,25 L 23,121 L 76.525498,121 C 76.989247,121 107,91.601715 107,91.147428 L 107,25 L 23,25 z "
|
||||||
|
style="opacity:0.6;fill:#000000;fill-opacity:1;filter:url(#filter3217)"
|
||||||
|
transform="matrix(1.047619,0,0,1.0416667,-2.0952381,-4.041666)" /><path
|
||||||
|
style="fill:url(#radialGradient6272)"
|
||||||
|
d="M 24.000002,24 L 24.000002,120 L 77.5255,120 C 77.989249,120 108,90.601715 108,90.147428 L 108,24 L 24.000002,24 z "
|
||||||
|
id="path34"
|
||||||
|
sodipodi:nodetypes="cccccc" /><path
|
||||||
|
style="fill:url(#radialGradient6274);fill-opacity:1"
|
||||||
|
d="M 26.606384,25.714285 C 26.134518,25.714285 25.750001,26.092145 25.750001,26.555844 L 25.750001,117.44415 C 25.750001,117.9087 26.134518,118.28572 26.606384,118.28572 L 77.280277,118.28572 C 77.505506,118.28572 77.726453,118.19652 77.885739,118.03914 L 105.99908,90.412457 C 106.15921,90.255085 106.25,90.038805 106.25,89.817475 L 106.25,26.555844 C 106.25,26.092145 105.86634,25.714285 105.39361,25.714285 L 26.606384,25.714285 z "
|
||||||
|
id="path59" /><path
|
||||||
|
d="M 76.5255,120 C 76.5255,120 88.18749,110.99999 92.99999,106.28571 C 97.81249,101.57142 107,90.147428 107,90.147428 C 107,90.147428 99,96 83,96 C 83,112 76.5255,120 76.5255,120 z "
|
||||||
|
id="path101"
|
||||||
|
style="opacity:0.5;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter6241)"
|
||||||
|
sodipodi:nodetypes="csccc" /><path
|
||||||
|
sodipodi:nodetypes="csccc"
|
||||||
|
style="fill:url(#linearGradient6276)"
|
||||||
|
id="path6233"
|
||||||
|
d="M 77.5255,120 C 77.5255,120 89.18749,110.99999 93.99999,106.28571 C 98.81249,101.57142 108,90.147428 108,90.147428 C 108,90.147428 100,96 84,96 C 84,112 77.5255,120 77.5255,120 z " /></g><use
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
xlink:href="#g6263"
|
||||||
|
id="use6270"
|
||||||
|
width="128"
|
||||||
|
height="128"
|
||||||
|
transform="translate(-28,-16)" /></g>
|
||||||
|
|
||||||
|
</svg>
|
After Width: | Height: | Size: 14 KiB |
BIN
src/calibre/gui2/images/news/liberation.png
Normal file
BIN
src/calibre/gui2/images/news/liberation.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 899 B |
BIN
src/calibre/gui2/images/news/new_yorker.png
Normal file
BIN
src/calibre/gui2/images/news/new_yorker.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 670 B |
Binary file not shown.
Before Width: | Height: | Size: 4.2 KiB |
@ -188,6 +188,6 @@ class DetailView(QDialog, Ui_Dialog):
|
|||||||
|
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
self.log.setPlainText(self.job.gui_text())
|
self.log.setPlainText(self.job.console_text())
|
||||||
vbar = self.log.verticalScrollBar()
|
vbar = self.log.verticalScrollBar()
|
||||||
vbar.setValue(vbar.maximum())
|
vbar.setValue(vbar.maximum())
|
||||||
|
@ -575,7 +575,7 @@ class BooksView(TableView):
|
|||||||
self.setItemDelegateForColumn(col, self.rating_delegate)
|
self.setItemDelegateForColumn(col, self.rating_delegate)
|
||||||
|
|
||||||
def set_context_menu(self, edit_metadata, send_to_device, convert, view,
|
def set_context_menu(self, edit_metadata, send_to_device, convert, view,
|
||||||
save, open_folder, book_details):
|
save, open_folder, book_details, similar_menu=None):
|
||||||
self.setContextMenuPolicy(Qt.DefaultContextMenu)
|
self.setContextMenuPolicy(Qt.DefaultContextMenu)
|
||||||
self.context_menu = QMenu(self)
|
self.context_menu = QMenu(self)
|
||||||
if edit_metadata is not None:
|
if edit_metadata is not None:
|
||||||
@ -590,6 +590,8 @@ class BooksView(TableView):
|
|||||||
self.context_menu.addAction(open_folder)
|
self.context_menu.addAction(open_folder)
|
||||||
if book_details is not None:
|
if book_details is not None:
|
||||||
self.context_menu.addAction(book_details)
|
self.context_menu.addAction(book_details)
|
||||||
|
if similar_menu is not None:
|
||||||
|
self.context_menu.addMenu(similar_menu)
|
||||||
|
|
||||||
def contextMenuEvent(self, event):
|
def contextMenuEvent(self, event):
|
||||||
self.context_menu.popup(event.globalPos())
|
self.context_menu.popup(event.globalPos())
|
||||||
|
@ -126,6 +126,21 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
self.location_selected)
|
self.location_selected)
|
||||||
QObject.connect(self.stack, SIGNAL('currentChanged(int)'),
|
QObject.connect(self.stack, SIGNAL('currentChanged(int)'),
|
||||||
self.location_view.location_changed)
|
self.location_view.location_changed)
|
||||||
|
|
||||||
|
self.output_formats = sorted(['EPUB', 'LRF'])
|
||||||
|
for f in self.output_formats:
|
||||||
|
self.output_format.addItem(f)
|
||||||
|
self.output_format.setCurrentIndex(self.output_formats.index(prefs['output_format']))
|
||||||
|
def change_output_format(x):
|
||||||
|
of = unicode(x).strip()
|
||||||
|
if of != prefs['output_format']:
|
||||||
|
if of in ('EPUB', 'LIT'):
|
||||||
|
warning_dialog(self, 'Warning',
|
||||||
|
'<p>%s support is still in beta. If you find bugs, please report them by opening a <a href="http://calibre.kovidgoyal.net">ticket</a>.'%of).exec_()
|
||||||
|
prefs.set('output_format', of)
|
||||||
|
|
||||||
|
self.connect(self.output_format, SIGNAL('currentIndexChanged(QString)'),
|
||||||
|
change_output_format)
|
||||||
|
|
||||||
####################### Vanity ########################
|
####################### Vanity ########################
|
||||||
self.vanity_template = _('<p>For help visit <a href="http://%s.kovidgoyal.net/user_manual">%s.kovidgoyal.net</a><br>')%(__appname__, __appname__)
|
self.vanity_template = _('<p>For help visit <a href="http://%s.kovidgoyal.net/user_manual">%s.kovidgoyal.net</a><br>')%(__appname__, __appname__)
|
||||||
@ -234,10 +249,33 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
QObject.connect(self.advanced_search_button, SIGNAL('clicked(bool)'), self.do_advanced_search)
|
QObject.connect(self.advanced_search_button, SIGNAL('clicked(bool)'), self.do_advanced_search)
|
||||||
|
|
||||||
####################### Library view ########################
|
####################### Library view ########################
|
||||||
|
similar_menu = QMenu(_('Similar books...'))
|
||||||
|
similar_menu.addAction(self.action_books_by_same_author)
|
||||||
|
similar_menu.addAction(self.action_books_in_this_series)
|
||||||
|
similar_menu.addAction(self.action_books_with_the_same_tags)
|
||||||
|
similar_menu.addAction(self.action_books_by_this_publisher)
|
||||||
|
self.action_books_by_same_author.setShortcut(Qt.ALT + Qt.Key_A)
|
||||||
|
self.action_books_in_this_series.setShortcut(Qt.ALT + Qt.Key_S)
|
||||||
|
self.action_books_by_this_publisher.setShortcut(Qt.ALT + Qt.Key_P)
|
||||||
|
self.action_books_with_the_same_tags.setShortcut(Qt.ALT+Qt.Key_T)
|
||||||
|
self.addAction(self.action_books_by_same_author)
|
||||||
|
self.addAction(self.action_books_by_this_publisher)
|
||||||
|
self.addAction(self.action_books_in_this_series)
|
||||||
|
self.addAction(self.action_books_with_the_same_tags)
|
||||||
|
self.similar_menu = similar_menu
|
||||||
|
self.connect(self.action_books_by_same_author, SIGNAL('triggered()'),
|
||||||
|
lambda : self.show_similar_books('author'))
|
||||||
|
self.connect(self.action_books_in_this_series, SIGNAL('triggered()'),
|
||||||
|
lambda : self.show_similar_books('series'))
|
||||||
|
self.connect(self.action_books_with_the_same_tags, SIGNAL('triggered()'),
|
||||||
|
lambda : self.show_similar_books('tag'))
|
||||||
|
self.connect(self.action_books_by_this_publisher, SIGNAL('triggered()'),
|
||||||
|
lambda : self.show_similar_books('publisher'))
|
||||||
self.library_view.set_context_menu(self.action_edit, self.action_sync,
|
self.library_view.set_context_menu(self.action_edit, self.action_sync,
|
||||||
self.action_convert, self.action_view,
|
self.action_convert, self.action_view,
|
||||||
self.action_save, self.action_open_containing_folder,
|
self.action_save, self.action_open_containing_folder,
|
||||||
self.action_show_book_details)
|
self.action_show_book_details,
|
||||||
|
similar_menu=similar_menu)
|
||||||
self.memory_view.set_context_menu(None, None, None, self.action_view, self.action_save, None, None)
|
self.memory_view.set_context_menu(None, None, None, self.action_view, self.action_save, None, None)
|
||||||
self.card_view.set_context_menu(None, None, None, self.action_view, self.action_save, None, None)
|
self.card_view.set_context_menu(None, None, None, self.action_view, self.action_save, None, None)
|
||||||
QObject.connect(self.library_view, SIGNAL('files_dropped(PyQt_PyObject)'),
|
QObject.connect(self.library_view, SIGNAL('files_dropped(PyQt_PyObject)'),
|
||||||
@ -258,7 +296,7 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
db = LibraryDatabase2(self.library_path)
|
db = LibraryDatabase2(self.library_path)
|
||||||
except OSError, err:
|
except OSError, err:
|
||||||
error_dialog(self, _('Bad database location'), unicode(err)).exec_()
|
error_dialog(self, _('Bad database location'), unicode(err)).exec_()
|
||||||
dir = unicode(QFileDialog.getExistingDirectory(self,
|
dir = unicode(QFileDialog.getExistingDirectory(self,
|
||||||
_('Choose a location for your ebook library.'), os.path.expanduser('~')))
|
_('Choose a location for your ebook library.'), os.path.expanduser('~')))
|
||||||
if not dir:
|
if not dir:
|
||||||
QCoreApplication.exit(1)
|
QCoreApplication.exit(1)
|
||||||
@ -341,6 +379,34 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
error_dialog(self, _('Failed to start content server'),
|
error_dialog(self, _('Failed to start content server'),
|
||||||
unicode(self.content_server.exception)).exec_()
|
unicode(self.content_server.exception)).exec_()
|
||||||
|
|
||||||
|
def show_similar_books(self, type):
|
||||||
|
search, join = [], ' '
|
||||||
|
idx = self.library_view.currentIndex()
|
||||||
|
if not idx.isValid():
|
||||||
|
return
|
||||||
|
row = idx.row()
|
||||||
|
if type == 'series':
|
||||||
|
series = idx.model().db.series(row)
|
||||||
|
if series:
|
||||||
|
search = ['series:'+series]
|
||||||
|
elif type == 'publisher':
|
||||||
|
publisher = idx.model().db.publisher(row)
|
||||||
|
if publisher:
|
||||||
|
search = ['publisher:'+publisher]
|
||||||
|
elif type == 'tag':
|
||||||
|
tags = idx.model().db.tags(row)
|
||||||
|
if tags:
|
||||||
|
search = ['tag:'+t for t in tags.split(',')]
|
||||||
|
elif type == 'author':
|
||||||
|
authors = idx.model().db.authors(row)
|
||||||
|
if authors:
|
||||||
|
search = ['author:'+a.strip().replace('|', ',') for a in authors.split(',')]
|
||||||
|
join = ' or '
|
||||||
|
if search:
|
||||||
|
self.search.set_search_string(join.join(search))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def toggle_cover_flow(self, show):
|
def toggle_cover_flow(self, show):
|
||||||
if show:
|
if show:
|
||||||
self.library_view.setCurrentIndex(self.library_view.currentIndex())
|
self.library_view.setCurrentIndex(self.library_view.currentIndex())
|
||||||
@ -438,7 +504,7 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
return
|
return
|
||||||
info, cp, fs = job.result
|
info, cp, fs = job.result
|
||||||
self.location_view.model().update_devices(cp, fs)
|
self.location_view.model().update_devices(cp, fs)
|
||||||
self.device_info = _('Connected ')+' '.join(info[:-1])
|
self.device_info = _('Connected ')+info[0]
|
||||||
self.vanity.setText(self.vanity_template%dict(version=self.latest_version, device=self.device_info))
|
self.vanity.setText(self.vanity_template%dict(version=self.latest_version, device=self.device_info))
|
||||||
|
|
||||||
self.device_manager.books(Dispatcher(self.metadata_downloaded))
|
self.device_manager.books(Dispatcher(self.metadata_downloaded))
|
||||||
@ -621,13 +687,13 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
files, names, on_card=on_card,
|
files, names, on_card=on_card,
|
||||||
titles=titles
|
titles=titles
|
||||||
)
|
)
|
||||||
self.upload_memory[job] = (metadata, on_card, memory)
|
self.upload_memory[job] = (metadata, on_card, memory, files)
|
||||||
|
|
||||||
def books_uploaded(self, job):
|
def books_uploaded(self, job):
|
||||||
'''
|
'''
|
||||||
Called once books have been uploaded.
|
Called once books have been uploaded.
|
||||||
'''
|
'''
|
||||||
metadata, on_card, memory = self.upload_memory.pop(job)
|
metadata, on_card, memory, files = self.upload_memory.pop(job)
|
||||||
|
|
||||||
if job.exception is not None:
|
if job.exception is not None:
|
||||||
if isinstance(job.exception, FreeSpaceError):
|
if isinstance(job.exception, FreeSpaceError):
|
||||||
@ -648,6 +714,8 @@ class Main(MainWindow, Ui_MainWindow):
|
|||||||
view = self.card_view if on_card else self.memory_view
|
view = self.card_view if on_card else self.memory_view
|
||||||
view.model().resort(reset=False)
|
view.model().resort(reset=False)
|
||||||
view.model().research()
|
view.model().research()
|
||||||
|
for f in files:
|
||||||
|
getattr(f, 'close', lambda : True)()
|
||||||
if memory and memory[1]:
|
if memory and memory[1]:
|
||||||
self.library_view.model().delete_books_by_id(memory[1])
|
self.library_view.model().delete_books_by_id(memory[1])
|
||||||
|
|
||||||
|
@ -27,15 +27,9 @@
|
|||||||
<normaloff>:/library</normaloff>:/library</iconset>
|
<normaloff>:/library</normaloff>:/library</iconset>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="centralwidget" >
|
<widget class="QWidget" name="centralwidget" >
|
||||||
<layout class="QGridLayout" >
|
<layout class="QGridLayout" name="gridLayout" >
|
||||||
<item row="0" column="0" >
|
<item row="0" column="0" >
|
||||||
<layout class="QHBoxLayout" >
|
<layout class="QHBoxLayout" name="horizontalLayout_3" >
|
||||||
<property name="spacing" >
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<property name="margin" >
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="LocationView" name="location_view" >
|
<widget class="LocationView" name="location_view" >
|
||||||
<property name="sizePolicy" >
|
<property name="sizePolicy" >
|
||||||
@ -89,29 +83,47 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="vanity" >
|
<layout class="QVBoxLayout" name="verticalLayout_3" >
|
||||||
<property name="sizePolicy" >
|
<item>
|
||||||
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
|
<widget class="QLabel" name="vanity" >
|
||||||
<horstretch>0</horstretch>
|
<property name="sizePolicy" >
|
||||||
<verstretch>0</verstretch>
|
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
|
||||||
</sizepolicy>
|
<horstretch>0</horstretch>
|
||||||
</property>
|
<verstretch>0</verstretch>
|
||||||
<property name="maximumSize" >
|
</sizepolicy>
|
||||||
<size>
|
</property>
|
||||||
<width>16777215</width>
|
<property name="maximumSize" >
|
||||||
<height>90</height>
|
<size>
|
||||||
</size>
|
<width>16777215</width>
|
||||||
</property>
|
<height>90</height>
|
||||||
<property name="text" >
|
</size>
|
||||||
<string/>
|
</property>
|
||||||
</property>
|
<property name="text" >
|
||||||
<property name="textFormat" >
|
<string/>
|
||||||
<enum>Qt::RichText</enum>
|
</property>
|
||||||
</property>
|
<property name="textFormat" >
|
||||||
<property name="openExternalLinks" >
|
<enum>Qt::RichText</enum>
|
||||||
<bool>true</bool>
|
</property>
|
||||||
</property>
|
<property name="openExternalLinks" >
|
||||||
</widget>
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2" >
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2" >
|
||||||
|
<property name="text" >
|
||||||
|
<string>Output:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="output_format" />
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
@ -581,6 +593,42 @@
|
|||||||
<string>Show book details</string>
|
<string>Show book details</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="action_books_by_same_author" >
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="images.qrc" >
|
||||||
|
<normaloff>:/images/user_profile.svg</normaloff>:/images/user_profile.svg</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Books by same author</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="action_books_in_this_series" >
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="images.qrc" >
|
||||||
|
<normaloff>:/images/books_in_series.svg</normaloff>:/images/books_in_series.svg</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Books in this series</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="action_books_by_this_publisher" >
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="images.qrc" >
|
||||||
|
<normaloff>:/images/publisher.png</normaloff>:/images/publisher.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Books by this publisher</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
|
<action name="action_books_with_the_same_tags" >
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="images.qrc" >
|
||||||
|
<normaloff>:/images/tags.svg</normaloff>:/images/tags.svg</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text" >
|
||||||
|
<string>Books with the same tags</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
|
@ -32,7 +32,10 @@ class BookInfoDisplay(QWidget):
|
|||||||
self.setMaximumWidth(width)
|
self.setMaximumWidth(width)
|
||||||
QLabel.setPixmap(self, pixmap)
|
QLabel.setPixmap(self, pixmap)
|
||||||
|
|
||||||
aspect_ratio = pixmap.width()/float(pixmap.height())
|
try:
|
||||||
|
aspect_ratio = pixmap.width()/float(pixmap.height())
|
||||||
|
except ZeroDivisionError:
|
||||||
|
aspect_ratio = 1
|
||||||
self.setMaximumWidth(int(aspect_ratio*self.HEIGHT))
|
self.setMaximumWidth(int(aspect_ratio*self.HEIGHT))
|
||||||
|
|
||||||
def sizeHint(self):
|
def sizeHint(self):
|
||||||
|
@ -473,9 +473,12 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
|||||||
return current_page
|
return current_page
|
||||||
|
|
||||||
def save_current_position(self):
|
def save_current_position(self):
|
||||||
pos = self.view.bookmark()
|
try:
|
||||||
bookmark = '%d#%s'%(self.current_index, pos)
|
pos = self.view.bookmark()
|
||||||
self.iterator.add_bookmark(('calibre_current_page_bookmark', bookmark))
|
bookmark = '%d#%s'%(self.current_index, pos)
|
||||||
|
self.iterator.add_bookmark(('calibre_current_page_bookmark', bookmark))
|
||||||
|
except:
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
def load_ebook(self, pathtoebook):
|
def load_ebook(self, pathtoebook):
|
||||||
if self.iterator is not None:
|
if self.iterator is not None:
|
||||||
|
@ -47,6 +47,7 @@ entry_points = {
|
|||||||
'fb2-meta = calibre.ebooks.metadata.fb2:main',
|
'fb2-meta = calibre.ebooks.metadata.fb2:main',
|
||||||
'any2lrf = calibre.ebooks.lrf.any.convert_from:main',
|
'any2lrf = calibre.ebooks.lrf.any.convert_from:main',
|
||||||
'any2epub = calibre.ebooks.epub.from_any:main',
|
'any2epub = calibre.ebooks.epub.from_any:main',
|
||||||
|
'any2lit = calibre.ebooks.lit.from_any:main',
|
||||||
'lrf2lrs = calibre.ebooks.lrf.lrfparser:main',
|
'lrf2lrs = calibre.ebooks.lrf.lrfparser:main',
|
||||||
'lrs2lrf = calibre.ebooks.lrf.lrs.convert_from:main',
|
'lrs2lrf = calibre.ebooks.lrf.lrs.convert_from:main',
|
||||||
'pdfreflow = calibre.ebooks.lrf.pdf.reflow:main',
|
'pdfreflow = calibre.ebooks.lrf.pdf.reflow:main',
|
||||||
@ -184,6 +185,7 @@ def setup_completion(fatal_errors):
|
|||||||
from calibre.ebooks.odt.to_oeb import option_parser as odt2oeb
|
from calibre.ebooks.odt.to_oeb import option_parser as odt2oeb
|
||||||
from calibre.ebooks.epub.from_feeds import option_parser as feeds2epub
|
from calibre.ebooks.epub.from_feeds import option_parser as feeds2epub
|
||||||
from calibre.ebooks.epub.from_any import option_parser as any2epub
|
from calibre.ebooks.epub.from_any import option_parser as any2epub
|
||||||
|
from calibre.ebooks.lit.from_any import option_parser as any2lit
|
||||||
from calibre.ebooks.epub.from_comic import option_parser as comic2epub
|
from calibre.ebooks.epub.from_comic import option_parser as comic2epub
|
||||||
from calibre.gui2.main import option_parser as guiop
|
from calibre.gui2.main import option_parser as guiop
|
||||||
any_formats = ['epub', 'htm', 'html', 'xhtml', 'xhtm', 'rar', 'zip',
|
any_formats = ['epub', 'htm', 'html', 'xhtml', 'xhtm', 'rar', 'zip',
|
||||||
@ -207,7 +209,8 @@ def setup_completion(fatal_errors):
|
|||||||
f.write(opts_and_exts('pdf2lrf', htmlop, ['pdf']))
|
f.write(opts_and_exts('pdf2lrf', htmlop, ['pdf']))
|
||||||
f.write(opts_and_exts('any2lrf', htmlop, any_formats))
|
f.write(opts_and_exts('any2lrf', htmlop, any_formats))
|
||||||
f.write(opts_and_exts('calibre', guiop, any_formats))
|
f.write(opts_and_exts('calibre', guiop, any_formats))
|
||||||
f.write(opts_and_exts('any2lrf', any2epub, any_formats))
|
f.write(opts_and_exts('any2epub', any2epub, any_formats))
|
||||||
|
f.write(opts_and_exts('any2lit', any2lit, any_formats))
|
||||||
f.write(opts_and_exts('lrf2lrs', lrf2lrsop, ['lrf']))
|
f.write(opts_and_exts('lrf2lrs', lrf2lrsop, ['lrf']))
|
||||||
f.write(opts_and_exts('lrf-meta', metaop, ['lrf']))
|
f.write(opts_and_exts('lrf-meta', metaop, ['lrf']))
|
||||||
f.write(opts_and_exts('rtf-meta', metaop, ['rtf']))
|
f.write(opts_and_exts('rtf-meta', metaop, ['rtf']))
|
||||||
@ -423,7 +426,8 @@ def install_man_pages(fatal_errors):
|
|||||||
raise
|
raise
|
||||||
print 'Failed to install MAN pages as help2man is missing from your system'
|
print 'Failed to install MAN pages as help2man is missing from your system'
|
||||||
break
|
break
|
||||||
raw = re.compile(r'^\.IP\s*^([A-Z :]+)$', re.MULTILINE).sub(r'.SS\n\1', p.stdout.read())
|
o = p.stdout.read()
|
||||||
|
raw = re.compile(r'^\.IP\s*^([A-Z :]+)$', re.MULTILINE).sub(r'.SS\n\1', o)
|
||||||
if not raw.strip():
|
if not raw.strip():
|
||||||
print 'Unable to create MAN page for', prog
|
print 'Unable to create MAN page for', prog
|
||||||
continue
|
continue
|
||||||
|
@ -567,23 +567,27 @@ class Job(object):
|
|||||||
return 'ERROR'
|
return 'ERROR'
|
||||||
|
|
||||||
def console_text(self):
|
def console_text(self):
|
||||||
ans = [u'Error in job: ']
|
ans = [u'Job: ']
|
||||||
if self.description:
|
if self.description:
|
||||||
ans[0] += self.description
|
ans[0] += self.description
|
||||||
|
if self.exception is not None:
|
||||||
|
header = unicode(self.exception.__class__.__name__) if \
|
||||||
|
hasattr(self.exception, '__class__') else u'Error'
|
||||||
|
header = u'**%s**'%header
|
||||||
|
header += u': '
|
||||||
|
try:
|
||||||
|
header += unicode(self.exception)
|
||||||
|
except:
|
||||||
|
header += unicode(repr(self.exception))
|
||||||
|
ans.append(header)
|
||||||
|
if self.traceback:
|
||||||
|
ans.append(u'**Traceback**:')
|
||||||
|
ans.extend(self.traceback.split('\n'))
|
||||||
|
|
||||||
if self.log:
|
if self.log:
|
||||||
if isinstance(self.log, str):
|
if isinstance(self.log, str):
|
||||||
self.log = unicode(self.log, 'utf-8', 'replace')
|
self.log = unicode(self.log, 'utf-8', 'replace')
|
||||||
ans.append(self.log)
|
ans.append(self.log)
|
||||||
header = unicode(self.exception.__class__.__name__) if \
|
|
||||||
hasattr(self.exception, '__class__') else u'Error'
|
|
||||||
header += u': '
|
|
||||||
try:
|
|
||||||
header += unicode(self.exception)
|
|
||||||
except:
|
|
||||||
header += unicode(repr(self.exception))
|
|
||||||
ans.append(header)
|
|
||||||
if self.traceback:
|
|
||||||
ans.append(self.traceback)
|
|
||||||
return (u'\n'.join(ans)).encode('utf-8')
|
return (u'\n'.join(ans)).encode('utf-8')
|
||||||
|
|
||||||
def gui_text(self):
|
def gui_text(self):
|
||||||
@ -611,7 +615,7 @@ class Job(object):
|
|||||||
self.log = unicode(self.log, 'utf-8', 'replace')
|
self.log = unicode(self.log, 'utf-8', 'replace')
|
||||||
ans.extend(self.log.split('\n'))
|
ans.extend(self.log.split('\n'))
|
||||||
|
|
||||||
return '\n'.join(ans)
|
return '<br>'.join(ans)
|
||||||
|
|
||||||
|
|
||||||
class ParallelJob(Job):
|
class ParallelJob(Job):
|
||||||
|
@ -240,7 +240,7 @@ If not, head over to <a href="http://calibre.kovidgoyal.net/wiki/Development#Tra
|
|||||||
return 'download.html', data, None
|
return 'download.html', data, None
|
||||||
|
|
||||||
|
|
||||||
LINUX_INSTALLER = '''
|
LINUX_INSTALLER = r'''
|
||||||
import sys, os, shutil, tarfile, subprocess, tempfile, urllib2, re, stat
|
import sys, os, shutil, tarfile, subprocess, tempfile, urllib2, re, stat
|
||||||
|
|
||||||
MOBILEREAD='https://dev.mobileread.com/dist/kovid/calibre/'
|
MOBILEREAD='https://dev.mobileread.com/dist/kovid/calibre/'
|
||||||
@ -428,7 +428,7 @@ def main():
|
|||||||
|
|
||||||
print 'Extracting files to %s ...'%destdir
|
print 'Extracting files to %s ...'%destdir
|
||||||
extract_tarball(f, destdir)
|
extract_tarball(f, destdir)
|
||||||
pi = os.path.join(destdir, calibre_postinstall)
|
pi = os.path.join(destdir, 'calibre_postinstall')
|
||||||
subprocess.call('pi', shell=True)
|
subprocess.call(pi, shell=True)
|
||||||
return 0
|
return 0
|
||||||
'''
|
'''
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -175,7 +175,7 @@ mark_frame(void *context, uint32_t uncomp, uint32_t comp)
|
|||||||
PyObject *rtable = self->rtable;
|
PyObject *rtable = self->rtable;
|
||||||
PyObject *entry = NULL;
|
PyObject *entry = NULL;
|
||||||
|
|
||||||
entry = Py_BuildValue("(LL)", uncomp, comp);
|
entry = Py_BuildValue("(II)", uncomp, comp);
|
||||||
if (entry) {
|
if (entry) {
|
||||||
PyList_Append(rtable, entry);
|
PyList_Append(rtable, entry);
|
||||||
Py_DECREF(entry);
|
Py_DECREF(entry);
|
||||||
|
@ -17,7 +17,8 @@ from PyQt4.Qt import QApplication, QFile, Qt, QPalette, QSize, QImage, QPainter,
|
|||||||
from PyQt4.QtWebKit import QWebPage
|
from PyQt4.QtWebKit import QWebPage
|
||||||
|
|
||||||
|
|
||||||
from calibre import browser, __appname__, iswindows, LoggingInterface, strftime, __version__
|
from calibre import browser, __appname__, iswindows, LoggingInterface, \
|
||||||
|
strftime, __version__, preferred_encoding
|
||||||
from calibre.ebooks.BeautifulSoup import BeautifulSoup, NavigableString, CData, Tag
|
from calibre.ebooks.BeautifulSoup import BeautifulSoup, NavigableString, CData, Tag
|
||||||
from calibre.ebooks.metadata.opf import OPFCreator
|
from calibre.ebooks.metadata.opf import OPFCreator
|
||||||
from calibre.ebooks.lrf import entity_to_unicode
|
from calibre.ebooks.lrf import entity_to_unicode
|
||||||
@ -788,6 +789,7 @@ class BasicNewsRecipe(object, LoggingInterface):
|
|||||||
html= u'''\
|
html= u'''\
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
body {
|
body {
|
||||||
background: white no-repeat fixed center center;
|
background: white no-repeat fixed center center;
|
||||||
@ -817,12 +819,13 @@ class BasicNewsRecipe(object, LoggingInterface):
|
|||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
'''%dict(title=self.title, author=self.__author__,
|
'''%dict(title=self.title if isinstance(self.title, unicode) else self.title.decode(preferred_encoding, 'replace'),
|
||||||
date=time.strftime(self.timefmt),
|
author=self.__author__ if isinstance(self.__author__, unicode) else self.__author__.decode(preferred_encoding, 'replace'),
|
||||||
|
date=strftime(self.timefmt),
|
||||||
app=__appname__ +' '+__version__,
|
app=__appname__ +' '+__version__,
|
||||||
img=img)
|
img=img)
|
||||||
f2 = tempfile.NamedTemporaryFile(suffix='cover.html')
|
f2 = tempfile.NamedTemporaryFile(suffix='cover.html')
|
||||||
f2.write(html)
|
f2.write(html.encode('utf-8'))
|
||||||
f2.flush()
|
f2.flush()
|
||||||
page = QWebPage()
|
page = QWebPage()
|
||||||
pal = page.palette()
|
pal = page.palette()
|
||||||
|
@ -17,7 +17,7 @@ recipe_modules = [
|
|||||||
'blic', 'novosti', 'danas', 'vreme', 'times_online', 'the_scotsman',
|
'blic', 'novosti', 'danas', 'vreme', 'times_online', 'the_scotsman',
|
||||||
'nytimes_sub', 'security_watch', 'cyberpresse', 'st_petersburg_times',
|
'nytimes_sub', 'security_watch', 'cyberpresse', 'st_petersburg_times',
|
||||||
'clarin', 'financial_times', 'heise', 'le_monde', 'harpers', 'science_aas',
|
'clarin', 'financial_times', 'heise', 'le_monde', 'harpers', 'science_aas',
|
||||||
'science_news', 'the_nation', 'lrb', 'harpers_full'
|
'science_news', 'the_nation', 'lrb', 'harpers_full', 'liberation',
|
||||||
]
|
]
|
||||||
|
|
||||||
import re, imp, inspect, time, os
|
import re, imp, inspect, time, os
|
||||||
|
38
src/calibre/web/feeds/recipes/liberation.py
Normal file
38
src/calibre/web/feeds/recipes/liberation.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
'''
|
||||||
|
liberation.fr
|
||||||
|
'''
|
||||||
|
|
||||||
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
|
class Liberation(BasicNewsRecipe):
|
||||||
|
title = u'Liberation'
|
||||||
|
__author__ = 'Darko Miletic'
|
||||||
|
description = 'News from France'
|
||||||
|
oldest_article = 7
|
||||||
|
max_articles_per_feed = 100
|
||||||
|
no_stylesheets = True
|
||||||
|
use_embedded_content = False
|
||||||
|
|
||||||
|
html2lrf_options = ['--base-font-size', '10']
|
||||||
|
|
||||||
|
keep_only_tags = [
|
||||||
|
dict(name='h1')
|
||||||
|
,dict(name='div', attrs={'class':'articleContent'})
|
||||||
|
,dict(name='div', attrs={'class':'entry'})
|
||||||
|
]
|
||||||
|
remove_tags = [
|
||||||
|
dict(name='p', attrs={'class':'clear'})
|
||||||
|
,dict(name='ul', attrs={'class':'floatLeft clear'})
|
||||||
|
,dict(name='div', attrs={'class':'clear floatRight'})
|
||||||
|
,dict(name='object')
|
||||||
|
]
|
||||||
|
|
||||||
|
feeds = [
|
||||||
|
(u'La une', u'http://www.liberation.fr/rss/laune')
|
||||||
|
,(u'Monde' , u'http://www.liberation.fr/rss/monde')
|
||||||
|
,(u'Sports', u'http://www.liberation.fr/rss/sports')
|
||||||
|
]
|
@ -1,78 +1,34 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
__license__ = 'GPL v3'
|
|
||||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
__license__ = 'GPL v3'
|
||||||
__docformat__ = 'restructuredtext en'
|
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
|
||||||
|
'''
|
||||||
import re
|
newyorker.com
|
||||||
from calibre import strftime
|
'''
|
||||||
from calibre.web.feeds.news import BasicNewsRecipe
|
|
||||||
from calibre.ebooks.BeautifulSoup import NavigableString
|
from calibre.web.feeds.news import BasicNewsRecipe
|
||||||
|
|
||||||
class NewYorker(BasicNewsRecipe):
|
class NewYorker(BasicNewsRecipe):
|
||||||
|
title = u'The New Yorker'
|
||||||
title = 'The New Yorker'
|
__author__ = 'Darko Miletic'
|
||||||
__author__ = 'Kovid Goyal'
|
description = 'Best of the US journalism'
|
||||||
description = 'News and opinion'
|
oldest_article = 7
|
||||||
|
max_articles_per_feed = 100
|
||||||
remove_tags = [
|
no_stylesheets = False
|
||||||
dict(name='div', id=['printoptions', 'header', 'articleBottom']),
|
use_embedded_content = False
|
||||||
dict(name='div', attrs={'class':['utils', 'icons']})
|
|
||||||
]
|
keep_only_tags = [
|
||||||
|
dict(name='div' , attrs={'id':'printbody' })
|
||||||
|
]
|
||||||
def parse_index(self):
|
remove_tags = [
|
||||||
toc_pat = re.compile(r'/magazine/toc/\d+/\d+/\d+/toc_\d+')
|
dict(name='div' , attrs={'class':'utils' })
|
||||||
soup = self.soup(self.browser.open('http://www.newyorker.com/').read())
|
,dict(name='div' , attrs={'id':'bottomFeatures' })
|
||||||
a = soup.find('a', href=toc_pat)
|
,dict(name='div' , attrs={'id':'articleBottom' })
|
||||||
if a is None:
|
]
|
||||||
raise Exception('Could not find the current issue of The New Yorker')
|
|
||||||
href = a['href']
|
feeds = [
|
||||||
href = 'http://www.newyorker.com'+href[href.index('/magazine'):]
|
(u'The New Yorker', u'http://feeds.newyorker.com/services/rss/feeds/everything.xml')
|
||||||
soup = self.soup(self.browser.open(href).read())
|
]
|
||||||
img = soup.find(id='inThisIssuePhoto')
|
|
||||||
if img is not None:
|
def print_version(self, url):
|
||||||
self.cover_url = 'http://www.newyorker.com'+img['src']
|
return url + '?printable=true'
|
||||||
alt = img.get('alt', None)
|
|
||||||
if alt:
|
|
||||||
self.timefmt = ' [%s]'%alt
|
|
||||||
features = soup.findAll(attrs={'class':re.compile('feature')})
|
|
||||||
|
|
||||||
category, sections, articles = None, [], []
|
|
||||||
for feature in features:
|
|
||||||
head = feature.find('img', alt=True, attrs={'class':'featurehed'})
|
|
||||||
if head is None:
|
|
||||||
continue
|
|
||||||
if articles:
|
|
||||||
sections.append((category, articles))
|
|
||||||
category, articles = head['alt'], []
|
|
||||||
if category in ('', 'AUDIO', 'VIDEO', 'BLOGS', 'GOINGS ON'):
|
|
||||||
continue
|
|
||||||
|
|
||||||
for a in feature.findAll('a', href=True):
|
|
||||||
href = 'http://www.newyorker.com'+a['href']+'?printable=true'
|
|
||||||
title, in_title, desc = '', True, ''
|
|
||||||
for tag in a.contents:
|
|
||||||
if getattr(tag, 'name', None) == 'br':
|
|
||||||
in_title = False
|
|
||||||
continue
|
|
||||||
if isinstance(tag, NavigableString):
|
|
||||||
text = unicode(tag)
|
|
||||||
if in_title:
|
|
||||||
title += text
|
|
||||||
else:
|
|
||||||
desc += text
|
|
||||||
if title and not 'Audio:' in title:
|
|
||||||
art = {
|
|
||||||
'title': title,
|
|
||||||
'desc': desc, 'content':'',
|
|
||||||
'url': href,
|
|
||||||
'date': strftime('%a, %d %b'),
|
|
||||||
}
|
|
||||||
articles.append(art)
|
|
||||||
|
|
||||||
# from IPython.Shell import IPShellEmbed
|
|
||||||
# ipshell = IPShellEmbed()
|
|
||||||
# ipshell()
|
|
||||||
# raise Exception()
|
|
||||||
|
|
||||||
return sections
|
|
||||||
|
@ -213,7 +213,7 @@ def upload_src_tarball():
|
|||||||
check_call('scp dist/calibre-*.tar.gz divok:%s/'%DOWNLOADS)
|
check_call('scp dist/calibre-*.tar.gz divok:%s/'%DOWNLOADS)
|
||||||
|
|
||||||
def stage_one():
|
def stage_one():
|
||||||
check_call('sudo rm -rf build', shell=True)
|
check_call('sudo rm -rf build src/calibre/plugins/*', shell=True)
|
||||||
os.mkdir('build')
|
os.mkdir('build')
|
||||||
shutil.rmtree('docs')
|
shutil.rmtree('docs')
|
||||||
os.mkdir('docs')
|
os.mkdir('docs')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user