Merge from trunk

This commit is contained in:
Charles Haley 2012-07-31 13:56:19 +02:00
commit 22213a91d5
8 changed files with 121 additions and 72 deletions

View File

@ -734,7 +734,11 @@ If this property is detected by |app|, the following custom properties are recog
opf.pubdate opf.pubdate
opf.isbn opf.isbn
opf.language opf.language
opf.series
opf.seriesindex
In addition to this, you can specify the picture to use as the cover by naming it ``opf.cover`` (right click, Picture->Options->Name) in the ODT. If no picture with this name is found, the 'smart' method is used. In addition to this, you can specify the picture to use as the cover by naming it ``opf.cover`` (right click, Picture->Options->Name) in the ODT. If no picture with this name is found, the 'smart' method is used.
To prevent this you can set the custom property ``opf.nocover`` ('Yes or No' type) to Yes. As the cover detection might result in double covers in certain output formats, the process will remove the paragraph (only if the only content is the cover!) from the document. But this works only with the named picture!
To disable cover detection you can set the custom property ``opf.nocover`` ('Yes or No' type) to Yes in advanced mode.

View File

@ -140,7 +140,7 @@ extensions = [
['calibre/utils/podofo/podofo.cpp'], ['calibre/utils/podofo/podofo.cpp'],
libraries=['podofo'], libraries=['podofo'],
lib_dirs=[podofo_lib], lib_dirs=[podofo_lib],
inc_dirs=[podofo_inc], inc_dirs=[podofo_inc, os.path.dirname(podofo_inc)],
optional=True, optional=True,
error=podofo_error), error=podofo_error),

View File

@ -32,7 +32,7 @@ binary_includes = [
'/usr/lib/libunrar.so', '/usr/lib/libunrar.so',
'/usr/lib/libsqlite3.so.0', '/usr/lib/libsqlite3.so.0',
'/usr/lib/libmng.so.1', '/usr/lib/libmng.so.1',
'/usr/lib/libpodofo.so.0.8.4', '/usr/lib/libpodofo.so.0.9.1',
'/lib/libz.so.1', '/lib/libz.so.1',
'/usr/lib/libtiff.so.5', '/usr/lib/libtiff.so.5',
'/lib/libbz2.so.1', '/lib/libbz2.so.1',

View File

@ -243,9 +243,6 @@ class Py2App(object):
@flush @flush
def get_local_dependencies(self, path_to_lib): def get_local_dependencies(self, path_to_lib):
for x in self.get_dependencies(path_to_lib): for x in self.get_dependencies(path_to_lib):
if x.startswith('libpodofo'):
yield x, x
continue
for y in (SW+'/lib/', '/usr/local/lib/', SW+'/qt/lib/', for y in (SW+'/lib/', '/usr/local/lib/', SW+'/qt/lib/',
'/opt/local/lib/', '/opt/local/lib/',
SW+'/python/Python.framework/', SW+'/freetype/lib/'): SW+'/python/Python.framework/', SW+'/freetype/lib/'):
@ -330,10 +327,6 @@ class Py2App(object):
for f in glob.glob('src/calibre/plugins/*.so'): for f in glob.glob('src/calibre/plugins/*.so'):
shutil.copy2(f, dest) shutil.copy2(f, dest)
self.fix_dependencies_in_lib(join(dest, basename(f))) self.fix_dependencies_in_lib(join(dest, basename(f)))
if 'podofo' in f:
self.change_dep('libpodofo.0.8.4.dylib',
self.FID+'/'+'libpodofo.0.8.4.dylib', join(dest, basename(f)))
@flush @flush
def create_plist(self): def create_plist(self):
@ -380,7 +373,7 @@ class Py2App(object):
@flush @flush
def add_podofo(self): def add_podofo(self):
info('\nAdding PoDoFo') info('\nAdding PoDoFo')
pdf = join(SW, 'lib', 'libpodofo.0.8.4.dylib') pdf = join(SW, 'lib', 'libpodofo.0.9.1.dylib')
self.install_dylib(pdf) self.install_dylib(pdf)
@flush @flush

View File

@ -322,24 +322,7 @@ cp build/podofo-*/build/src/Release/podofo.exp lib/
cp build/podofo-*/build/podofo_config.h include/podofo/ cp build/podofo-*/build/podofo_config.h include/podofo/
cp -r build/podofo-*/src/* include/podofo/ cp -r build/podofo-*/src/* include/podofo/
You have to use >=0.8.2 You have to use >=0.9.1
The following patch (against -r1269) was required to get it to compile:
Index: src/PdfFiltersPrivate.cpp
===================================================================
--- src/PdfFiltersPrivate.cpp (revision 1261)
+++ src/PdfFiltersPrivate.cpp (working copy)
@@ -1019,7 +1019,7 @@
/*
* Prepare for input from a memory buffer.
*/
-GLOBAL(void)
+void
jpeg_memory_src (j_decompress_ptr cinfo, const JOCTET * buffer, size_t bufsize)
{
my_src_ptr src;
ImageMagick ImageMagick

View File

@ -1,3 +1,7 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import (unicode_literals, division, absolute_import,
print_function)
''' '''
Created on 29 Jun 2012 Created on 29 Jun 2012
@ -8,6 +12,7 @@ import hashlib, threading
from base64 import b64encode, b64decode from base64 import b64encode, b64decode
from functools import wraps from functools import wraps
from calibre import prints
from calibre.constants import numeric_version, DEBUG from calibre.constants import numeric_version, DEBUG
from calibre.devices.interface import DevicePlugin from calibre.devices.interface import DevicePlugin
from calibre.devices.usbms.books import Book, BookList from calibre.devices.usbms.books import Book, BookList
@ -23,8 +28,8 @@ from calibre.utils.ipc import eintr_retry_call
from calibre.utils.config import from_json, tweaks from calibre.utils.config import from_json, tweaks
from calibre.utils.date import isoformat, now from calibre.utils.date import isoformat, now
from calibre.utils.filenames import ascii_filename as sanitize, shorten_components_to from calibre.utils.filenames import ascii_filename as sanitize, shorten_components_to
from calibre.utils.mdns import publish as publish_zeroconf from calibre.utils.mdns import (publish as publish_zeroconf, unpublish as
from calibre.utils.mdns import unpublish as unpublish_zeroconf unpublish_zeroconf)
def synchronous(tlockname): def synchronous(tlockname):
"""A decorator to place an instance based lock around a method """ """A decorator to place an instance based lock around a method """
@ -132,11 +137,11 @@ class SMART_DEVICE_APP (DeviceConfig, DevicePlugin):
return return
total_elapsed = time.time() - self.debug_start_time total_elapsed = time.time() - self.debug_start_time
elapsed = time.time() - self.debug_time elapsed = time.time() - self.debug_time
print 'SMART_DEV (%7.2f:%7.3f) %s'%(total_elapsed, elapsed, prints('SMART_DEV (%7.2f:%7.3f) %s'%(total_elapsed, elapsed,
inspect.stack()[1][3]), inspect.stack()[1][3]), end='')
for a in args: for a in args:
print a, prints(a, end='')
print print()
self.debug_time = time.time() self.debug_time = time.time()
# Various methods required by the plugin architecture # Various methods required by the plugin architecture

View File

@ -196,6 +196,13 @@ def get_metadata(stream, extract_cover=True):
mi.publisher = data['opf.publisher'] mi.publisher = data['opf.publisher']
if data.get('opf.pubdate', ''): if data.get('opf.pubdate', ''):
mi.pubdate = parse_date(data['opf.pubdate'], assume_utc=True) mi.pubdate = parse_date(data['opf.pubdate'], assume_utc=True)
if data.get('opf.series', ''):
mi.series = data['opf.series']
if data.get('opf.seriesindex', ''):
try:
mi.series_index = float(data['opf.seriesindex'])
except ValueError:
mi.series_index = 1.0
if data.get('opf.language', ''): if data.get('opf.language', ''):
cl = canonicalize_lang(data['opf.language']) cl = canonicalize_lang(data['opf.language'])
if cl: if cl:
@ -216,10 +223,9 @@ def read_cover(stream, zin, mi, opfmeta, extract_cover):
otext = odLoad(stream) otext = odLoad(stream)
cover_href = None cover_href = None
cover_data = None cover_data = None
# check that it's really a ODT cover_frame = None
if otext.mimetype == u'application/vnd.oasis.opendocument.text': for frm in otext.topnode.getElementsByType(odFrame):
for elem in otext.text.getElementsByType(odFrame): img = frm.getElementsByType(odImage)
img = elem.getElementsByType(odImage)
if len(img) > 0: # there should be only one if len(img) > 0: # there should be only one
i_href = img[0].getAttribute('href') i_href = img[0].getAttribute('href')
try: try:
@ -232,9 +238,10 @@ def read_cover(stream, zin, mi, opfmeta, extract_cover):
continue continue
else: else:
continue continue
if opfmeta and elem.getAttribute('name').lower() == u'opf.cover': if opfmeta and frm.getAttribute('name').lower() == u'opf.cover':
cover_href = i_href cover_href = i_href
cover_data = (fmt, raw) cover_data = (fmt, raw)
cover_frame = frm.getAttribute('name') # could have upper case
break break
if cover_href is None and 0.8 <= height/width <= 1.8 and height*width >= 12000: if cover_href is None and 0.8 <= height/width <= 1.8 and height*width >= 12000:
cover_href = i_href cover_href = i_href
@ -244,6 +251,7 @@ def read_cover(stream, zin, mi, opfmeta, extract_cover):
if cover_href is not None: if cover_href is not None:
mi.cover = cover_href mi.cover = cover_href
mi.odf_cover_frame = cover_frame
if extract_cover: if extract_cover:
if not cover_data: if not cover_data:
raw = zin.read(cover_href) raw = zin.read(cover_href)

View File

@ -10,6 +10,9 @@ import os
from lxml import etree from lxml import etree
from odf.odf2xhtml import ODF2XHTML from odf.odf2xhtml import ODF2XHTML
from odf.opendocument import load as odLoad
from odf.draw import Frame as odFrame, Image as odImage
from odf.namespaces import TEXTNS as odTEXTNS
from calibre import CurrentDir, walk from calibre import CurrentDir, walk
@ -138,22 +141,84 @@ class Extract(ODF2XHTML):
r.selectorText = '.'+replace_name r.selectorText = '.'+replace_name
return sheet.cssText, sel_map return sheet.cssText, sel_map
def search_page_img(self, mi, log):
for frm in self.document.topnode.getElementsByType(odFrame):
try:
if frm.getAttrNS(odTEXTNS,u'anchor-type') == 'page':
log.warn('Document has Pictures anchored to Page, will all end up before first page!')
break
except ValueError:
pass
def filter_cover(self, mi, log):
# filter the Element tree (remove the detected cover)
if mi.cover and mi.odf_cover_frame:
for frm in self.document.topnode.getElementsByType(odFrame):
# search the right frame
if frm.getAttribute('name') == mi.odf_cover_frame:
img = frm.getElementsByType(odImage)
# only one draw:image allowed in the draw:frame
if len(img) == 1 and img[0].getAttribute('href') == mi.cover:
# ok, this is the right frame with the right image
# check if there are more childs
if len(frm.childNodes) != 1:
break
# check if the parent paragraph more childs
para = frm.parentNode
if para.tagName != 'text:p' or len(para.childNodes) != 1:
break
# now it should be safe to remove the text:p
parent = para.parentNode
parent.removeChild(para)
log("Removed cover image paragraph from document...")
break
def filter_load(self, odffile, mi, log):
""" This is an adaption from ODF2XHTML. It adds a step between
load and parse of the document where the Element tree can be
modified.
"""
# first load the odf structure
self.lines = []
self._wfunc = self._wlines
if isinstance(odffile, basestring) \
or hasattr(odffile, 'read'): # Added by Kovid
self.document = odLoad(odffile)
else:
self.document = odffile
# filter stuff
self.search_page_img(mi, log)
try:
self.filter_cover(mi, log)
except:
pass
# parse the modified tree and generate xhtml
self._walknode(self.document.topnode)
def __call__(self, stream, odir, log): def __call__(self, stream, odir, log):
from calibre.utils.zipfile import ZipFile from calibre.utils.zipfile import ZipFile
from calibre.ebooks.metadata.meta import get_metadata from calibre.ebooks.metadata.odt import get_metadata
from calibre.ebooks.metadata.opf2 import OPFCreator from calibre.ebooks.metadata.opf2 import OPFCreator
from calibre.customize.ui import quick_metadata
if not os.path.exists(odir): if not os.path.exists(odir):
os.makedirs(odir) os.makedirs(odir)
with CurrentDir(odir): with CurrentDir(odir):
log('Extracting ODT file...') log('Extracting ODT file...')
html = self.odf2xhtml(stream) stream.seek(0)
mi = get_metadata(stream, 'odt')
if not mi.title:
mi.title = _('Unknown')
if not mi.authors:
mi.authors = [_('Unknown')]
self.filter_load(stream, mi, log)
html = self.xhtml()
# A blanket img specification like this causes problems # A blanket img specification like this causes problems
# with EPUB output as the containing element often has # with EPUB output as the containing element often has
# an absolute height and width set that is larger than # an absolute height and width set that is larger than
# the available screen real estate # the available screen real estate
html = html.replace('img { width: 100%; height: 100%; }', '') html = html.replace('img { width: 100%; height: 100%; }', '')
# odf2xhtml creates empty title tag
html = html.replace('<title></title>','<title>%s</title>'%(mi.title,))
try: try:
html = self.fix_markup(html, log) html = self.fix_markup(html, log)
except: except:
@ -162,15 +227,6 @@ class Extract(ODF2XHTML):
f.write(html.encode('utf-8')) f.write(html.encode('utf-8'))
zf = ZipFile(stream, 'r') zf = ZipFile(stream, 'r')
self.extract_pictures(zf) self.extract_pictures(zf)
stream.seek(0)
with quick_metadata:
# We dont want the cover, as it will lead to a duplicated image
# if no external cover is specified.
mi = get_metadata(stream, 'odt')
if not mi.title:
mi.title = _('Unknown')
if not mi.authors:
mi.authors = [_('Unknown')]
opf = OPFCreator(os.path.abspath(os.getcwdu()), mi) opf = OPFCreator(os.path.abspath(os.getcwdu()), mi)
opf.create_manifest([(os.path.abspath(f), None) for f in opf.create_manifest([(os.path.abspath(f), None) for f in
walk(os.getcwdu())]) walk(os.getcwdu())])