diff --git a/src/calibre/ebooks/conversion/plugins/mobi_output.py b/src/calibre/ebooks/conversion/plugins/mobi_output.py index 726ae2a1d0..10d5d87d76 100644 --- a/src/calibre/ebooks/conversion/plugins/mobi_output.py +++ b/src/calibre/ebooks/conversion/plugins/mobi_output.py @@ -170,13 +170,13 @@ class MOBIOutput(OutputFormatPlugin): resources = Resources(oeb, opts, self.is_periodical, add_fonts=create_kf8) - kf8 = self.create_kf8() if create_kf8 else None + kf8 = self.create_kf8(resources) if create_kf8 else None self.write_mobi(input_plugin, output_path, kf8, resources) - def create_kf8(self): + def create_kf8(self, resources): from calibre.ebooks.mobi.writer8.main import KF8Writer - return KF8Writer(self.oeb, self.opts) + return KF8Writer(self.oeb, self.opts, resources) def write_mobi(self, input_plugin, output_path, kf8, resources): from calibre.ebooks.mobi.mobiml import MobiMLizer diff --git a/src/calibre/ebooks/mobi/writer2/resources.py b/src/calibre/ebooks/mobi/writer2/resources.py index 0be814de27..2fcb93790c 100644 --- a/src/calibre/ebooks/mobi/writer2/resources.py +++ b/src/calibre/ebooks/mobi/writer2/resources.py @@ -7,6 +7,8 @@ __license__ = 'GPL v3' __copyright__ = '2012, Kovid Goyal ' __docformat__ = 'restructuredtext en' +import imghdr + from calibre.ebooks.mobi import MAX_THUMB_DIMEN, MAX_THUMB_SIZE from calibre.ebooks.mobi.utils import (rescale_image, mobify_image, write_font_record) @@ -23,6 +25,7 @@ class Resources(object): self.item_map = {} self.records = [] + self.mime_map = {} self.masthead_offset = 0 self.used_image_indices = set() self.image_indices = set() @@ -76,6 +79,7 @@ class Resources(object): self.image_indices.add(len(self.records)) self.records.append(data) self.item_map[item.href] = index + self.mime_map[item.href] = 'image/%s'%imghdr.what(None, data) index += 1 if cover_href and item.href == cover_href: diff --git a/src/calibre/ebooks/mobi/writer8/main.py b/src/calibre/ebooks/mobi/writer8/main.py index 8d643deb75..cbf2b8376c 100644 --- a/src/calibre/ebooks/mobi/writer8/main.py +++ b/src/calibre/ebooks/mobi/writer8/main.py @@ -8,20 +8,25 @@ __copyright__ = '2012, Kovid Goyal ' __docformat__ = 'restructuredtext en' import copy +from functools import partial import cssutils -from calibre.ebooks.oeb.base import (OEB_DOCS, OEB_STYLES, SVG_MIME) +from calibre import isbytestring +from calibre.ebooks.oeb.base import (OEB_DOCS, OEB_STYLES, SVG_MIME, XPath) XML_DOCS = OEB_DOCS | {SVG_MIME} class KF8Writer(object): - def __init__(self, oeb, opts): + def __init__(self, oeb, opts, resources): self.oeb, self.opts, self.log = oeb, opts, oeb.log self.used_images = set() + self.resources = resources self.dup_data() + self.replace_resource_links() + self.create_pieces() def dup_data(self): @@ -41,6 +46,51 @@ class KF8Writer(object): def data(self, item): return self._data_cache.get(item.href, item.data) + def replace_resource_links(self): + ''' Replace links to resources (raster images/fonts) with pointers to + the MOBI record containing the resource. The pointers are of the form: + kindle:embed:XXXX?mime=image/* The ?mime= is apparently optional and + not used for fonts. ''' + + def pointer(item, oref): + ref = item.abshref(oref) + idx = self.resources.item_map.get(ref, None) + if idx is not None: + is_image = self.resources.records[idx-1][:4] not in {b'FONT'} + if is_image: + self.used_images.add(ref) + return 'kindle:embed:%04d?mime=%s'%(idx, + self.resources.mime_map[ref]) + else: + return 'kindle:embed:%04d'%idx + return oref + + for item in self.oeb.manifest: + + if item.media_type in XML_DOCS: + root = self.data(item) + for tag in XPath('//h:img|//svg:image')(root): + for attr, ref in tag.attrib.iteritems(): + if attr.split('}')[-1].lower() in {'src', 'href'}: + tag.attrib[attr] = pointer(item, ref) + + for tag in XPath('//h:style')(root): + if tag.text: + sheet = cssutils.parseString(tag.text) + replacer = partial(pointer, item) + cssutils.replaceUrls(sheet, replacer, + ignoreImportRules=True) + repl = sheet.cssText + if isbytestring(repl): + repl = repl.decode('utf-8') + tag.text = '\n'+ repl + '\n' + + elif item.media_type in OEB_STYLES: + sheet = self.data(item) + replacer = partial(pointer, item) + cssutils.replaceUrls(sheet, replacer, ignoreImportRules=True) + + def create_pieces(self): self.flows = [None] # First flow item is reserved for the text