mirror of
https://github.com/kovidgoyal/calibre.git
synced 2026-05-28 17:52:34 -04:00
Merge upstream changes.
This commit is contained in:
@@ -5,9 +5,5 @@
|
||||
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.5</pydev_property>
|
||||
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_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_project>
|
||||
|
||||
@@ -122,6 +122,8 @@ def freeze():
|
||||
elif exe not in executables:
|
||||
print >>sys.stderr, 'Invalid invocation of calibre loader. CALIBRE_CX_EXE=%%s is unknown'%%exe
|
||||
else:
|
||||
from PyQt4.QtCore import QCoreApplication
|
||||
QCoreApplication.setLibraryPaths([sys.frozen_path, os.path.join(sys.frozen_path, "qtplugins")])
|
||||
sys.argv[0] = exe
|
||||
module, func = executables[exe]
|
||||
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:
|
||||
continue
|
||||
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)
|
||||
|
||||
print 'Creating launchers'
|
||||
|
||||
@@ -2,7 +2,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
__appname__ = 'calibre'
|
||||
__version__ = '0.4.113'
|
||||
__version__ = '0.4.115'
|
||||
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
|
||||
'''
|
||||
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)
|
||||
return zf
|
||||
|
||||
def config(defaults=None):
|
||||
def config(defaults=None, name='epub'):
|
||||
desc = _('Options to control the conversion to EPUB')
|
||||
if defaults is None:
|
||||
c = Config('epub', desc)
|
||||
c = Config(name, desc)
|
||||
else:
|
||||
c = StringConfig(defaults, desc)
|
||||
|
||||
|
||||
@@ -116,7 +116,8 @@ def unarchive(path, tdir):
|
||||
return f, ext
|
||||
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]
|
||||
if not ext:
|
||||
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())
|
||||
|
||||
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):
|
||||
return common_config(defaults=defaults)
|
||||
@@ -148,14 +151,14 @@ def config(defaults=None):
|
||||
def formats():
|
||||
return ['html', 'rar', 'zip', 'oebzip']+list(MAP.keys())
|
||||
|
||||
def option_parser():
|
||||
|
||||
return config().option_parser(usage=_('''\
|
||||
USAGE = _('''\
|
||||
%%prog [options] filename
|
||||
|
||||
Convert any of a large number of ebook formats to an epub file. Supported formats are: %s
|
||||
''')%formats()
|
||||
)
|
||||
Convert any of a large number of ebook formats to a %s file. Supported formats are: %s
|
||||
''')
|
||||
|
||||
def option_parser(usage=USAGE):
|
||||
return config().option_parser(usage=usage%('EPUB', formats()))
|
||||
|
||||
def main(args=sys.argv):
|
||||
parser = option_parser()
|
||||
|
||||
@@ -32,14 +32,14 @@ Conversion of HTML/OPF files follows several stages:
|
||||
* 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 import html
|
||||
from PyQt4.Qt import QApplication, QPixmap
|
||||
|
||||
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.ptempfile import TemporaryDirectory
|
||||
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):
|
||||
'''
|
||||
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)
|
||||
with CurrentDir(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:
|
||||
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()):
|
||||
link = to_unicode(link)
|
||||
plink = Link(link, base)
|
||||
@@ -209,17 +210,16 @@ TITLEPAGE = '''\
|
||||
</html>
|
||||
'''
|
||||
|
||||
def create_cover_image(src, dest, screen_size):
|
||||
from PyQt4.Qt import QApplication, QImage, Qt
|
||||
if QApplication.instance() is None:
|
||||
app = QApplication([])
|
||||
app
|
||||
im = QImage()
|
||||
def create_cover_image(src, dest, screen_size, rescale_cover=True):
|
||||
try:
|
||||
from PyQt4.Qt import QImage, Qt
|
||||
if QApplication.instance() is None:
|
||||
QApplication([])
|
||||
im = QImage()
|
||||
im.load(src)
|
||||
if im.isNull():
|
||||
raise ValueError
|
||||
if screen_size is not None:
|
||||
raise ValueError('Invalid cover image')
|
||||
if rescale_cover and screen_size is not None:
|
||||
width, height = im.width(), im.height()
|
||||
dw, dh = (screen_size[0]-width)/float(width), (screen_size[1]-height)/float(height)
|
||||
delta = min(dw, dh)
|
||||
@@ -227,7 +227,7 @@ def create_cover_image(src, dest, screen_size):
|
||||
nwidth = int(width + delta*(width))
|
||||
nheight = int(height + delta*(height))
|
||||
im = im.scaled(int(nwidth), int(nheight), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
|
||||
im.save(dest)
|
||||
im.save(dest)
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
@@ -240,7 +240,6 @@ def process_title_page(mi, filelist, htmlfilemap, opts, tdir):
|
||||
if mi.cover:
|
||||
if f(filelist[0].path) == f(mi.cover):
|
||||
old_title_page = htmlfilemap[filelist[0].path]
|
||||
|
||||
#logger = logging.getLogger('html2epub')
|
||||
metadata_cover = mi.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'))
|
||||
cover_dest = os.path.join(tdir, 'content', *cpath.split('/'))
|
||||
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
|
||||
|
||||
specified_cover = opts.cover
|
||||
if specified_cover and not os.path.exists(specified_cover):
|
||||
specified_cover = 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
|
||||
|
||||
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):
|
||||
os.remove(cover_dest)
|
||||
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)
|
||||
if opts.output is None:
|
||||
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)
|
||||
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
|
||||
mi.cover = None
|
||||
mi.cover_data = (None, None)
|
||||
@@ -357,24 +364,43 @@ def convert(htmlfile, opts, notification=None):
|
||||
check(opf_path, opts.pretty_print)
|
||||
opf = OPF(opf_path, tdir)
|
||||
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.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:
|
||||
raw = opf.render()
|
||||
if not raw.startswith('<?xml '):
|
||||
raw = '<?xml version="1.0" encoding="UTF-8"?>\n'+raw
|
||||
f.write(raw)
|
||||
epub = initialize_container(opts.output)
|
||||
epub.add_dir(tdir)
|
||||
if create_epub:
|
||||
epub = initialize_container(opts.output)
|
||||
epub.add_dir(tdir)
|
||||
epub.close()
|
||||
logger.info(_('Output written to ')+opts.output)
|
||||
|
||||
if opts.show_opf:
|
||||
print open(os.path.join(tdir, 'metadata.opf')).read()
|
||||
logger.info('Output written to %s'%opts.output)
|
||||
|
||||
if opts.extract_to is not None:
|
||||
epub.extractall(opts.extract_to)
|
||||
epub.close()
|
||||
if os.path.exists(opts.extract_to):
|
||||
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):
|
||||
|
||||
@@ -170,7 +170,10 @@ class EbookIterator(object):
|
||||
dat = self.serialize_bookmarks(bookmarks)
|
||||
if os.path.splitext(self.pathtoebook)[1].lower() == '.epub' and \
|
||||
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')
|
||||
for name in zipf.namelist():
|
||||
if name == 'META-INF/calibre_bookmarks.txt':
|
||||
|
||||
@@ -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 urlparse import urldefrag, urlparse, urlunparse
|
||||
from urllib import unquote as urlunquote
|
||||
import logging
|
||||
from lxml import etree
|
||||
from calibre import LoggingInterface
|
||||
|
||||
XML_PARSER = etree.XMLParser(recover=True, resolve_entities=False)
|
||||
XML_NS = 'http://www.w3.org/XML/1998/namespace'
|
||||
@@ -88,6 +90,13 @@ def urlnormalize(href):
|
||||
return urlunparse(parts)
|
||||
|
||||
|
||||
class FauxLogger(object):
|
||||
def __getattr__(self, name):
|
||||
return self
|
||||
def __call__(self, message):
|
||||
print message
|
||||
|
||||
|
||||
class AbstractContainer(object):
|
||||
def read_xml(self, path):
|
||||
return etree.fromstring(
|
||||
@@ -108,6 +117,10 @@ class DirContainer(AbstractContainer):
|
||||
with open(urlunquote(path), 'wb') as f:
|
||||
return f.write(data)
|
||||
|
||||
def exists(self, path):
|
||||
path = os.path.join(self.rootdir, path)
|
||||
return os.path.isfile(path)
|
||||
|
||||
|
||||
class Metadata(object):
|
||||
TERMS = set(['contributor', 'coverage', 'creator', 'date', 'description',
|
||||
@@ -530,13 +543,14 @@ class TOC(object):
|
||||
node.to_ncx(point, playorder, depth+1)
|
||||
return parent
|
||||
|
||||
|
||||
|
||||
class OEBBook(object):
|
||||
def __init__(self, opfpath=None, container=None):
|
||||
def __init__(self, opfpath=None, container=None, logger=FauxLogger()):
|
||||
if not container:
|
||||
container = DirContainer(os.path.dirname(opfpath))
|
||||
opfpath = os.path.basename(opfpath)
|
||||
self.container = container
|
||||
self.logger = logger
|
||||
opf = self._read_opf(opfpath)
|
||||
self._all_from_opf(opf)
|
||||
|
||||
@@ -590,17 +604,28 @@ class OEBBook(object):
|
||||
if item.id == uid:
|
||||
self.uid = item
|
||||
break
|
||||
else:
|
||||
self.logger.log_warn(u'Unique-identifier %r not found.' % uid)
|
||||
self.uid = metadata.identifier[0]
|
||||
|
||||
def _manifest_from_opf(self, opf):
|
||||
self.manifest = manifest = Manifest(self)
|
||||
for elem in xpath(opf, '/o2:package/o2:manifest/o2:item'):
|
||||
manifest.add(elem.get('id'), elem.get('href'),
|
||||
elem.get('media-type'), elem.get('fallback'))
|
||||
href = elem.get('href')
|
||||
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):
|
||||
self.spine = spine = Spine(self)
|
||||
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'))
|
||||
extras = []
|
||||
for item in self.manifest.values():
|
||||
@@ -614,7 +639,11 @@ class OEBBook(object):
|
||||
def _guide_from_opf(self, opf):
|
||||
self.guide = guide = Guide(self)
|
||||
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):
|
||||
children = xpath(navpoint, 'ncx:navPoint')
|
||||
|
||||
@@ -9,7 +9,7 @@ __copyright__ = '2008, Marshall T. Vandegrift <llasram@gmail.com>'
|
||||
import sys
|
||||
import os
|
||||
from cStringIO import StringIO
|
||||
from struct import pack, unpack
|
||||
from struct import pack
|
||||
from itertools import izip, count, chain
|
||||
import time
|
||||
import random
|
||||
@@ -17,19 +17,20 @@ import re
|
||||
import copy
|
||||
import uuid
|
||||
import functools
|
||||
import logging
|
||||
from urlparse import urldefrag
|
||||
from urllib import unquote as urlunquote
|
||||
from lxml import etree
|
||||
from calibre.ebooks.lit import LitError
|
||||
from calibre.ebooks.lit.reader import msguid, DirectoryEntry
|
||||
from calibre.ebooks.lit.reader import DirectoryEntry
|
||||
import calibre.ebooks.lit.maps as maps
|
||||
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 OEBBook
|
||||
from calibre.ebooks.lit.oeb import FauxLogger, OEBBook
|
||||
from calibre.ebooks.lit.stylizer import Stylizer
|
||||
from calibre.ebooks.lit.lzx import Compressor
|
||||
import calibre
|
||||
from calibre import LoggingInterface
|
||||
from calibre import plugins
|
||||
msdes, msdeserror = plugins['msdes']
|
||||
import calibre.ebooks.lit.mssha1 as mssha1
|
||||
@@ -135,11 +136,15 @@ def decint(value):
|
||||
def randbytes(n):
|
||||
return ''.join(chr(random.randint(0, 255)) for x in xrange(n))
|
||||
|
||||
def warn(x):
|
||||
print x
|
||||
|
||||
class ReBinary(object):
|
||||
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.logger = logger
|
||||
self.dir = os.path.dirname(path)
|
||||
self.manifest = oeb.manifest
|
||||
self.tags, self.tattrs = map
|
||||
@@ -268,8 +273,8 @@ class ReBinary(object):
|
||||
|
||||
def build_ahc(self):
|
||||
if len(self.anchors) > 6:
|
||||
print "calibre: warning: More than six anchors in file %r. " \
|
||||
"Some links may not work properly." % self.path
|
||||
self.logger.log_warn("More than six anchors in file %r. " \
|
||||
"Some links may not work properly." % self.path)
|
||||
data = StringIO()
|
||||
data.write(unichr(len(self.anchors)).encode('utf-8'))
|
||||
for anchor, offset in self.anchors:
|
||||
@@ -293,8 +298,9 @@ def preserve(function):
|
||||
return wrapper
|
||||
|
||||
class LitWriter(object):
|
||||
def __init__(self, oeb):
|
||||
def __init__(self, oeb, logger=FauxLogger()):
|
||||
self._oeb = oeb
|
||||
self._logger = logger
|
||||
self._litize_oeb()
|
||||
|
||||
def _litize_oeb(self):
|
||||
@@ -307,6 +313,9 @@ class LitWriter(object):
|
||||
elif MS_COVER_TYPE in oeb.guide:
|
||||
href = oeb.guide[MS_COVER_TYPE].href
|
||||
cover = oeb.manifest.hrefs[href]
|
||||
elif 'cover' in oeb.guide:
|
||||
href = oeb.guide['cover'].href
|
||||
cover = oeb.manifest.hrefs[href]
|
||||
else:
|
||||
html = oeb.spine[0].data
|
||||
imgs = xpath(html, '//img[position()=1]')
|
||||
@@ -319,7 +328,7 @@ class LitWriter(object):
|
||||
if type not in oeb.guide:
|
||||
oeb.guide.add(type, title, cover.href)
|
||||
else:
|
||||
print "calibre: warning: No suitable cover image found."
|
||||
self._logger.log_warn('No suitable cover image found.')
|
||||
|
||||
def dump(self, stream):
|
||||
self._stream = stream
|
||||
@@ -461,15 +470,16 @@ class LitWriter(object):
|
||||
self._add_folder('/data')
|
||||
for item in self._oeb.manifest.values():
|
||||
if item.media_type not in LIT_MIMES:
|
||||
print "calibre: warning: File %r of unknown media-type %r " \
|
||||
"excluded from output." % (item.href, item.media_type)
|
||||
self._logger.log_warn("File %r of unknown media-type %r " \
|
||||
"excluded from output." % (item.href, item.media_type))
|
||||
continue
|
||||
name = '/data/' + item.id
|
||||
data = item.data
|
||||
secnum = 0
|
||||
if not isinstance(data, basestring):
|
||||
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 + '/aht', rebin.aht, 0)
|
||||
item.page_breaks = rebin.page_breaks
|
||||
@@ -548,7 +558,8 @@ class LitWriter(object):
|
||||
meta.attrib['ms--minimum_level'] = '0'
|
||||
meta.attrib['ms--attr5'] = '1'
|
||||
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
|
||||
self._meta = meta
|
||||
self._add_file('/meta', meta)
|
||||
@@ -707,8 +718,25 @@ def option_parser():
|
||||
parser.add_option(
|
||||
'-o', '--output', default=None,
|
||||
help=_('Output file. Default is derived from input filename.'))
|
||||
parser.add_option(
|
||||
'--verbose', default=False, action='store_true',
|
||||
help=_('Useful for debugging.'))
|
||||
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):
|
||||
parser = option_parser()
|
||||
opts, args = parser.parse_args(argv[1:])
|
||||
@@ -716,14 +744,7 @@ def main(argv=sys.argv):
|
||||
parser.print_help()
|
||||
return 1
|
||||
opfpath = args[0]
|
||||
litpath = opts.output
|
||||
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
|
||||
oeb2lit(opts, opfpath)
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -1920,7 +1920,7 @@ def process_file(path, options, logger=None):
|
||||
options.anchor_ids = True
|
||||
files = options.spine if (options.use_spine and hasattr(options, 'spine')) else [path]
|
||||
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)
|
||||
oname = options.output
|
||||
if not oname:
|
||||
|
||||
@@ -101,7 +101,6 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
||||
for item in items:
|
||||
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'])
|
||||
|
||||
added_html = False
|
||||
@@ -255,16 +254,11 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
||||
sc.set('max_cover', mcs)
|
||||
config['delete_news_from_library_on_upload'] = self.delete_news.isChecked()
|
||||
config['upload_news_to_device'] = self.sync_news.isChecked()
|
||||
of = str(self.output_format.currentText())
|
||||
fmts = []
|
||||
for i in range(self.viewer.count()):
|
||||
if self.viewer.item(i).checkState() == Qt.Checked:
|
||||
fmts.append(str(self.viewer.item(i).text()))
|
||||
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):
|
||||
d = error_dialog(self, _('Invalid database location'),
|
||||
|
||||
@@ -149,7 +149,7 @@
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_2" >
|
||||
<item row="1" column="0" >
|
||||
<item row="0" column="0" >
|
||||
<widget class="QLabel" name="label_5" >
|
||||
<property name="text" >
|
||||
<string>Format for &single file save:</string>
|
||||
@@ -159,10 +159,10 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" >
|
||||
<item row="0" column="1" >
|
||||
<widget class="QComboBox" name="single_format" />
|
||||
</item>
|
||||
<item row="2" column="0" >
|
||||
<item row="1" column="0" >
|
||||
<widget class="QLabel" name="label_2" >
|
||||
<property name="text" >
|
||||
<string>Default network &timeout:</string>
|
||||
@@ -172,7 +172,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" >
|
||||
<item row="1" column="1" >
|
||||
<widget class="QSpinBox" name="timeout" >
|
||||
<property name="toolTip" >
|
||||
<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>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1" >
|
||||
<item row="2" column="1" >
|
||||
<widget class="QComboBox" name="language" />
|
||||
</item>
|
||||
<item row="3" column="0" >
|
||||
<item row="2" column="0" >
|
||||
<widget class="QLabel" name="label_7" >
|
||||
<property name="text" >
|
||||
<string>Choose &language (requires restart):</string>
|
||||
@@ -204,34 +204,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" 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" >
|
||||
<item row="3" column="1" >
|
||||
<widget class="QComboBox" name="priority" >
|
||||
<item>
|
||||
<property name="text" >
|
||||
@@ -250,7 +223,7 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" >
|
||||
<item row="3" column="0" >
|
||||
<widget class="QLabel" name="priority_label" >
|
||||
<property name="text" >
|
||||
<string>Job &priority:</string>
|
||||
|
||||
@@ -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 |
Binary file not shown.
|
After Width: | Height: | Size: 899 B |
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):
|
||||
self.log.setPlainText(self.job.gui_text())
|
||||
self.log.setPlainText(self.job.console_text())
|
||||
vbar = self.log.verticalScrollBar()
|
||||
vbar.setValue(vbar.maximum())
|
||||
|
||||
@@ -575,7 +575,7 @@ class BooksView(TableView):
|
||||
self.setItemDelegateForColumn(col, self.rating_delegate)
|
||||
|
||||
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.context_menu = QMenu(self)
|
||||
if edit_metadata is not None:
|
||||
@@ -590,6 +590,8 @@ class BooksView(TableView):
|
||||
self.context_menu.addAction(open_folder)
|
||||
if book_details is not None:
|
||||
self.context_menu.addAction(book_details)
|
||||
if similar_menu is not None:
|
||||
self.context_menu.addMenu(similar_menu)
|
||||
|
||||
def contextMenuEvent(self, event):
|
||||
self.context_menu.popup(event.globalPos())
|
||||
|
||||
@@ -126,6 +126,21 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
self.location_selected)
|
||||
QObject.connect(self.stack, SIGNAL('currentChanged(int)'),
|
||||
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 ########################
|
||||
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)
|
||||
|
||||
####################### 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.action_convert, self.action_view,
|
||||
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.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)'),
|
||||
@@ -258,7 +296,7 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
db = LibraryDatabase2(self.library_path)
|
||||
except OSError, err:
|
||||
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('~')))
|
||||
if not dir:
|
||||
QCoreApplication.exit(1)
|
||||
@@ -341,6 +379,34 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
error_dialog(self, _('Failed to start content server'),
|
||||
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):
|
||||
if show:
|
||||
self.library_view.setCurrentIndex(self.library_view.currentIndex())
|
||||
@@ -438,7 +504,7 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
return
|
||||
info, cp, fs = job.result
|
||||
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.device_manager.books(Dispatcher(self.metadata_downloaded))
|
||||
@@ -621,13 +687,13 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
files, names, on_card=on_card,
|
||||
titles=titles
|
||||
)
|
||||
self.upload_memory[job] = (metadata, on_card, memory)
|
||||
self.upload_memory[job] = (metadata, on_card, memory, files)
|
||||
|
||||
def books_uploaded(self, job):
|
||||
'''
|
||||
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 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.model().resort(reset=False)
|
||||
view.model().research()
|
||||
for f in files:
|
||||
getattr(f, 'close', lambda : True)()
|
||||
if memory and memory[1]:
|
||||
self.library_view.model().delete_books_by_id(memory[1])
|
||||
|
||||
|
||||
+79
-31
@@ -27,15 +27,9 @@
|
||||
<normaloff>:/library</normaloff>:/library</iconset>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget" >
|
||||
<layout class="QGridLayout" >
|
||||
<layout class="QGridLayout" name="gridLayout" >
|
||||
<item row="0" column="0" >
|
||||
<layout class="QHBoxLayout" >
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3" >
|
||||
<item>
|
||||
<widget class="LocationView" name="location_view" >
|
||||
<property name="sizePolicy" >
|
||||
@@ -89,29 +83,47 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="vanity" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize" >
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>90</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string/>
|
||||
</property>
|
||||
<property name="textFormat" >
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="openExternalLinks" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3" >
|
||||
<item>
|
||||
<widget class="QLabel" name="vanity" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize" >
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>90</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string/>
|
||||
</property>
|
||||
<property name="textFormat" >
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="openExternalLinks" >
|
||||
<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>
|
||||
</layout>
|
||||
</item>
|
||||
@@ -581,6 +593,42 @@
|
||||
<string>Show book details</string>
|
||||
</property>
|
||||
</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>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
||||
@@ -32,7 +32,10 @@ class BookInfoDisplay(QWidget):
|
||||
self.setMaximumWidth(width)
|
||||
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))
|
||||
|
||||
def sizeHint(self):
|
||||
|
||||
@@ -473,9 +473,12 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
|
||||
return current_page
|
||||
|
||||
def save_current_position(self):
|
||||
pos = self.view.bookmark()
|
||||
bookmark = '%d#%s'%(self.current_index, pos)
|
||||
self.iterator.add_bookmark(('calibre_current_page_bookmark', bookmark))
|
||||
try:
|
||||
pos = self.view.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):
|
||||
if self.iterator is not None:
|
||||
|
||||
@@ -47,6 +47,7 @@ entry_points = {
|
||||
'fb2-meta = calibre.ebooks.metadata.fb2:main',
|
||||
'any2lrf = calibre.ebooks.lrf.any.convert_from:main',
|
||||
'any2epub = calibre.ebooks.epub.from_any:main',
|
||||
'any2lit = calibre.ebooks.lit.from_any:main',
|
||||
'lrf2lrs = calibre.ebooks.lrf.lrfparser:main',
|
||||
'lrs2lrf = calibre.ebooks.lrf.lrs.convert_from: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.epub.from_feeds import option_parser as feeds2epub
|
||||
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.gui2.main import option_parser as guiop
|
||||
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('any2lrf', htmlop, 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('lrf-meta', metaop, ['lrf']))
|
||||
f.write(opts_and_exts('rtf-meta', metaop, ['rtf']))
|
||||
@@ -423,7 +426,8 @@ def install_man_pages(fatal_errors):
|
||||
raise
|
||||
print 'Failed to install MAN pages as help2man is missing from your system'
|
||||
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():
|
||||
print 'Unable to create MAN page for', prog
|
||||
continue
|
||||
|
||||
+16
-12
@@ -567,23 +567,27 @@ class Job(object):
|
||||
return 'ERROR'
|
||||
|
||||
def console_text(self):
|
||||
ans = [u'Error in job: ']
|
||||
ans = [u'Job: ']
|
||||
if 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 isinstance(self.log, str):
|
||||
self.log = unicode(self.log, 'utf-8', 'replace')
|
||||
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')
|
||||
|
||||
def gui_text(self):
|
||||
@@ -611,7 +615,7 @@ class Job(object):
|
||||
self.log = unicode(self.log, 'utf-8', 'replace')
|
||||
ans.extend(self.log.split('\n'))
|
||||
|
||||
return '\n'.join(ans)
|
||||
return '<br>'.join(ans)
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
LINUX_INSTALLER = '''
|
||||
LINUX_INSTALLER = r'''
|
||||
import sys, os, shutil, tarfile, subprocess, tempfile, urllib2, re, stat
|
||||
|
||||
MOBILEREAD='https://dev.mobileread.com/dist/kovid/calibre/'
|
||||
@@ -428,7 +428,7 @@ def main():
|
||||
|
||||
print 'Extracting files to %s ...'%destdir
|
||||
extract_tarball(f, destdir)
|
||||
pi = os.path.join(destdir, calibre_postinstall)
|
||||
subprocess.call('pi', shell=True)
|
||||
pi = os.path.join(destdir, 'calibre_postinstall')
|
||||
subprocess.call(pi, shell=True)
|
||||
return 0
|
||||
'''
|
||||
'''
|
||||
|
||||
+227
-220
File diff suppressed because it is too large
Load Diff
+231
-221
File diff suppressed because it is too large
Load Diff
+227
-220
File diff suppressed because it is too large
Load Diff
+239
-225
File diff suppressed because it is too large
Load Diff
+227
-220
File diff suppressed because it is too large
Load Diff
+231
-221
File diff suppressed because it is too large
Load Diff
+231
-221
File diff suppressed because it is too large
Load Diff
+227
-220
File diff suppressed because it is too large
Load Diff
+269
-246
File diff suppressed because it is too large
Load Diff
+227
-220
File diff suppressed because it is too large
Load Diff
+235
-222
File diff suppressed because it is too large
Load Diff
+231
-221
File diff suppressed because it is too large
Load Diff
+344
-288
File diff suppressed because it is too large
Load Diff
+227
-220
File diff suppressed because it is too large
Load Diff
+229
-222
File diff suppressed because it is too large
Load Diff
+310
-277
File diff suppressed because it is too large
Load Diff
+231
-221
File diff suppressed because it is too large
Load Diff
+227
-220
File diff suppressed because it is too large
Load Diff
+227
-220
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 *entry = NULL;
|
||||
|
||||
entry = Py_BuildValue("(LL)", uncomp, comp);
|
||||
entry = Py_BuildValue("(II)", uncomp, comp);
|
||||
if (entry) {
|
||||
PyList_Append(rtable, 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 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.metadata.opf import OPFCreator
|
||||
from calibre.ebooks.lrf import entity_to_unicode
|
||||
@@ -788,6 +789,7 @@ class BasicNewsRecipe(object, LoggingInterface):
|
||||
html= u'''\
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<style type="text/css">
|
||||
body {
|
||||
background: white no-repeat fixed center center;
|
||||
@@ -817,12 +819,13 @@ class BasicNewsRecipe(object, LoggingInterface):
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
'''%dict(title=self.title, author=self.__author__,
|
||||
date=time.strftime(self.timefmt),
|
||||
'''%dict(title=self.title if isinstance(self.title, unicode) else self.title.decode(preferred_encoding, 'replace'),
|
||||
author=self.__author__ if isinstance(self.__author__, unicode) else self.__author__.decode(preferred_encoding, 'replace'),
|
||||
date=strftime(self.timefmt),
|
||||
app=__appname__ +' '+__version__,
|
||||
img=img)
|
||||
f2 = tempfile.NamedTemporaryFile(suffix='cover.html')
|
||||
f2.write(html)
|
||||
f2.write(html.encode('utf-8'))
|
||||
f2.flush()
|
||||
page = QWebPage()
|
||||
pal = page.palette()
|
||||
|
||||
@@ -17,7 +17,7 @@ recipe_modules = [
|
||||
'blic', 'novosti', 'danas', 'vreme', 'times_online', 'the_scotsman',
|
||||
'nytimes_sub', 'security_watch', 'cyberpresse', 'st_petersburg_times',
|
||||
'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
|
||||
|
||||
@@ -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
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import re
|
||||
from calibre import strftime
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import NavigableString
|
||||
|
||||
class NewYorker(BasicNewsRecipe):
|
||||
|
||||
title = 'The New Yorker'
|
||||
__author__ = 'Kovid Goyal'
|
||||
description = 'News and opinion'
|
||||
|
||||
remove_tags = [
|
||||
dict(name='div', id=['printoptions', 'header', 'articleBottom']),
|
||||
dict(name='div', attrs={'class':['utils', 'icons']})
|
||||
]
|
||||
|
||||
|
||||
def parse_index(self):
|
||||
toc_pat = re.compile(r'/magazine/toc/\d+/\d+/\d+/toc_\d+')
|
||||
soup = self.soup(self.browser.open('http://www.newyorker.com/').read())
|
||||
a = soup.find('a', href=toc_pat)
|
||||
if a is None:
|
||||
raise Exception('Could not find the current issue of The New Yorker')
|
||||
href = a['href']
|
||||
href = 'http://www.newyorker.com'+href[href.index('/magazine'):]
|
||||
soup = self.soup(self.browser.open(href).read())
|
||||
img = soup.find(id='inThisIssuePhoto')
|
||||
if img is not None:
|
||||
self.cover_url = 'http://www.newyorker.com'+img['src']
|
||||
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
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
newyorker.com
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class NewYorker(BasicNewsRecipe):
|
||||
title = u'The New Yorker'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'Best of the US journalism'
|
||||
oldest_article = 7
|
||||
max_articles_per_feed = 100
|
||||
no_stylesheets = False
|
||||
use_embedded_content = False
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='div' , attrs={'id':'printbody' })
|
||||
]
|
||||
remove_tags = [
|
||||
dict(name='div' , attrs={'class':'utils' })
|
||||
,dict(name='div' , attrs={'id':'bottomFeatures' })
|
||||
,dict(name='div' , attrs={'id':'articleBottom' })
|
||||
]
|
||||
|
||||
feeds = [
|
||||
(u'The New Yorker', u'http://feeds.newyorker.com/services/rss/feeds/everything.xml')
|
||||
]
|
||||
|
||||
def print_version(self, url):
|
||||
return url + '?printable=true'
|
||||
|
||||
@@ -213,7 +213,7 @@ def upload_src_tarball():
|
||||
check_call('scp dist/calibre-*.tar.gz divok:%s/'%DOWNLOADS)
|
||||
|
||||
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')
|
||||
shutil.rmtree('docs')
|
||||
os.mkdir('docs')
|
||||
|
||||
Reference in New Issue
Block a user