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_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)
|
||||
@ -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
|
||||
@ -272,8 +272,15 @@ def process_title_page(mi, filelist, htmlfilemap, opts, tdir):
|
||||
os.remove(cover_dest)
|
||||
return None, old_title_page is not 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):
|
||||
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()
|
||||
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)
|
||||
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):
|
||||
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':
|
||||
|
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 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',
|
||||
@ -532,11 +545,12 @@ class TOC(object):
|
||||
|
||||
|
||||
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>
|
||||
|
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):
|
||||
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())
|
||||
|
@ -127,6 +127,21 @@ class Main(MainWindow, Ui_MainWindow):
|
||||
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__)
|
||||
self.vanity_template += _('<b>%s</b>: %s by <b>Kovid Goyal %%(version)s</b><br>%%(device)s</p>')%(__appname__, __version__)
|
||||
@ -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)'),
|
||||
@ -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])
|
||||
|
||||
|
@ -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" >
|
||||
@ -88,6 +82,8 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3" >
|
||||
<item>
|
||||
<widget class="QLabel" name="vanity" >
|
||||
<property name="sizePolicy" >
|
||||
@ -113,6 +109,22 @@
|
||||
</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>
|
||||
<item row="1" column="0" >
|
||||
@ -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)
|
||||
|
||||
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):
|
||||
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
|
||||
|
@ -567,15 +567,13 @@ 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.log:
|
||||
if isinstance(self.log, str):
|
||||
self.log = unicode(self.log, 'utf-8', 'replace')
|
||||
ans.append(self.log)
|
||||
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)
|
||||
@ -583,7 +581,13 @@ class Job(object):
|
||||
header += unicode(repr(self.exception))
|
||||
ans.append(header)
|
||||
if self.traceback:
|
||||
ans.append(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)
|
||||
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
|
||||
'''
|
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 *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
|
||||
|
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
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import re
|
||||
from calibre import strftime
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
newyorker.com
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
from calibre.ebooks.BeautifulSoup import NavigableString
|
||||
|
||||
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
|
||||
|
||||
title = 'The New Yorker'
|
||||
__author__ = 'Kovid Goyal'
|
||||
description = 'News and opinion'
|
||||
|
||||
keep_only_tags = [
|
||||
dict(name='div' , attrs={'id':'printbody' })
|
||||
]
|
||||
remove_tags = [
|
||||
dict(name='div', id=['printoptions', 'header', 'articleBottom']),
|
||||
dict(name='div', attrs={'class':['utils', 'icons']})
|
||||
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 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
|
||||
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')
|
||||
|
Loading…
x
Reference in New Issue
Block a user