Source code for bkgen.mobi


import os, logging, subprocess
from bl.dict import Dict
from bf.image import Image
from bxml.xml import XML, etree
from bkgen import NS, config

log = logging.getLogger(__name__)

[docs]class MOBI(Dict): NS = NS def __init__(self, **args): Dict.__init__(self, **args)
[docs] @classmethod def mobi_fn(C, mobi_path, mobi_name=None, ext='.mobi'): return os.path.join(os.path.dirname(os.path.abspath(mobi_path)), (mobi_name or os.path.basename(mobi_path.rstrip(os.path.sep)))+ext)
[docs] def build(self, build_path, metadata, mobi_name=None, manifest=None, spine_items=None, cover_src=None, nav_toc=None, nav_landmarks=None, nav_page_list=None, before_compile=None, nav_href='nav.html', nav_title="Navigation"): """build MOBI (Kindle ebook) output of the given project""" from .epub import EPUB if mobi_name is None: mobi_name = EPUB.epub_name_from_path(build_path) mobifn = self.mobi_fn(build_path, mobi_name=mobi_name) if nav_landmarks is None: nav_landmarks = EPUB.nav_landmarks({'href':nav_href, 'title':'Table of Contents', 'epub_type':'toc'}) result = EPUB(**self).build(build_path, metadata, epub_name=mobi_name, spine_items=spine_items, cover_src=cover_src, cover_html=False, nav_toc=nav_toc, nav_landmarks=nav_landmarks, nav_page_list=nav_page_list, nav_href=nav_href, nav_title=nav_title, zip=False) opffn = EPUB.get_opf_fn(build_path) self.move_anchors_before_paragraphs(build_path, opffn) EPUB.unhide_toc(os.path.join(build_path, nav_href)) EPUB.append_toc_to_spine(opffn, nav_href) self.size_images(opffn) if before_compile is not None: before_compile(build_path) self.compile_mobi(build_path, opffn, mobifn=mobifn) result.fn=mobifn return result
[docs] @classmethod def move_anchors_before_paragraphs(C, build_path, opffn): """In Kindle ebooks, link targets need to be moved before the containing paragraph so that the paragraph formatting can be displayed properly.""" opf = XML(fn=opffn) n = 0 for item in opf.root.xpath("opf:manifest/opf:item[contains(@media-type, 'html')]", namespaces=C.NS): x = XML(fn=os.path.join(build_path, item.get('href'))) anchors = [a for a in x.root.xpath("//html:a[@id and not(@href)]", namespaces=C.NS) if len(a.getchildren())==0 and a.text in [None, '']] for a in anchors: pp = a.xpath("ancestor::html:p", namespaces=C.NS) if len(pp) > 0: n += 1 p = pp[-1] parent = p.getparent() XML.remove(a, leave_tail=True) a.tail = '' parent.insert(parent.index(p), a) x.write()
[docs] def size_images(self, opffn): """resample images to the width / height specified in the img tag, and remove those size attributes""" opf = XML(fn=opffn) for item in [ item for item in opf.root.xpath("//opf:manifest/opf:item", namespaces=self.NS) if item.get('href')[-4:].lower()=='html']: x = XML(os.path.join(os.path.dirname(opffn), item.get('href'))) for img in x.root.xpath("//html:img[@width or @height]", namespaces=self.NS): srcfn = os.path.join(os.path.dirname(x.fn), img.get('src')) w, h = [int(i) for i in Image(fn=srcfn).identify(format="%w,%h").split(',')] width, height = w, h if img.get('width') is not None: width = int(img.attrib.pop('width')) if img.get('height') is None: height = int(h * (width/w)) if img.get('height') is not None: height = int(img.attrib.pop('height')) if img.get('width') is None: width = int(w * (height/h)) if width != w or height != h: Image(fn=srcfn).convert(outfn=srcfn, resize="%dx%d" % (width, height)) log.debug("%dx%d\t%dx%d\t%s" % (w, h, width, height, os.path.relpath(srcfn, os.path.dirname(opffn)))) x.write(canonicalized=False)
[docs] def compile_mobi(self, build_path, opffn, mobifn=None, config=config): """generate .mobi file using kindlegen""" if mobifn is None: mobifn = os.path.join(os.path.dirname(build_path), os.path.basename(build_path)+'.mobi') logfn = mobifn+'.kindlegen.txt' logf = open(logfn, 'wb') log.info("mobi: %s" % mobifn) cmd = [config.Resources.kindlegen, opffn, '-o', os.path.basename(mobifn)] subprocess.call(cmd, stdout=logf, stderr=logf) logf.close() log.info("kindlegen log: %s" % logfn) mobi_build_fn = os.path.join(os.path.dirname(opffn), os.path.basename(mobifn)) if os.path.exists(mobi_build_fn): if os.path.exists(mobifn) and mobifn != mobi_build_fn: os.remove(mobifn) os.rename(mobi_build_fn, mobifn)