From 63d133ea5c1a3ee8d4949e95ce9cf8e9e9c9d644 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 26 Jun 2013 15:46:41 +0530 Subject: [PATCH] AZW3 Input: Add support for page-progression-direction AZW3 Input: Add support for the page-progression-direction that is used to indicate page turns should happen from right to left. The attribute is passed into EPUB when converting. Fixes #1194766 [Incorrect conversion japanese MOBI](https://bugs.launchpad.net/calibre/+bug/1194766) --- src/calibre/ebooks/metadata/opf2.py | 11 +++++++++++ src/calibre/ebooks/mobi/reader/mobi8.py | 12 +++++++++--- src/calibre/ebooks/mobi/utils.py | 11 ++++++++++- src/calibre/ebooks/oeb/base.py | 3 +++ src/calibre/ebooks/oeb/reader.py | 3 +++ 5 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/calibre/ebooks/metadata/opf2.py b/src/calibre/ebooks/metadata/opf2.py index fb80cc8bfe..77e334dd3e 100644 --- a/src/calibre/ebooks/metadata/opf2.py +++ b/src/calibre/ebooks/metadata/opf2.py @@ -1047,6 +1047,14 @@ class OPF(object): # {{{ if raw: return raw.rpartition(':')[-1] + @property + def page_progression_direction(self): + spine = self.XPath('descendant::*[re:match(name(), "spine", "i")][1]')(self.root) + if spine: + for k, v in spine[0].attrib.iteritems(): + if k == 'page-progression-direction' or k.endswith('}page-progression-direction'): + return v + def guess_cover(self): ''' Try to guess a cover. Needed for some old/badly formed OPF files. @@ -1185,6 +1193,7 @@ class OPFCreator(Metadata): ''' Metadata.__init__(self, title='', other=other) self.base_path = os.path.abspath(base_path) + self.page_progression_direction = None if self.application_id is None: self.application_id = str(uuid.uuid4()) if not isinstance(self.toc, TOC): @@ -1356,6 +1365,8 @@ class OPFCreator(Metadata): spine = E.spine() if self.toc is not None: spine.set('toc', 'ncx') + if self.page_progression_direction is not None: + spine.set('page-progression-direction', self.page_progression_direction) if self.spine is not None: for ref in self.spine: if ref.id is not None: diff --git a/src/calibre/ebooks/mobi/reader/mobi8.py b/src/calibre/ebooks/mobi/reader/mobi8.py index aff79d65c2..97d38a9660 100644 --- a/src/calibre/ebooks/mobi/reader/mobi8.py +++ b/src/calibre/ebooks/mobi/reader/mobi8.py @@ -20,7 +20,7 @@ from calibre.ebooks.mobi.reader.ncx import read_ncx, build_toc from calibre.ebooks.mobi.reader.markup import expand_mobi8_markup from calibre.ebooks.metadata.opf2 import Guide, OPFCreator from calibre.ebooks.metadata.toc import TOC -from calibre.ebooks.mobi.utils import read_font_record +from calibre.ebooks.mobi.utils import read_font_record, read_resc_record from calibre.ebooks.oeb.parse_utils import parse_html from calibre.ebooks.oeb.base import XPath, XHTML, xml2text from calibre.utils.imghdr import what @@ -65,6 +65,7 @@ class Mobi8Reader(object): self.mobi6_reader, self.log = mobi6_reader, log self.header = mobi6_reader.book_header self.encrypted_fonts = [] + self.resc_data = {} def __call__(self): self.mobi6_reader.check_for_drm() @@ -389,9 +390,11 @@ class Mobi8Reader(object): data = sec[0] typ = data[:4] href = None - if typ in {b'FLIS', b'FCIS', b'SRCS', b'\xe9\x8e\r\n', - b'RESC', b'BOUN', b'FDST', b'DATP', b'AUDI', b'VIDE'}: + if typ in {b'FLIS', b'FCIS', b'SRCS', b'\xe9\x8e\r\n', b'BOUN', + b'FDST', b'DATP', b'AUDI', b'VIDE'}: pass # Ignore these records + elif typ == b'RESC': + self.resc_data = read_resc_record(data) elif typ == b'FONT': font = read_font_record(data) href = "fonts/%05d.%s" % (fname_idx, font['ext']) @@ -452,6 +455,9 @@ class Mobi8Reader(object): opf.create_manifest_from_files_in([os.getcwdu()], exclude=exclude) opf.create_spine(spine) opf.set_toc(toc) + ppd = self.resc_data.get('page-progression-direction', None) + if ppd: + opf.page_progression_direction = ppd with open('metadata.opf', 'wb') as of, open('toc.ncx', 'wb') as ncx: opf.render(of, ncx, 'toc.ncx') diff --git a/src/calibre/ebooks/mobi/utils.py b/src/calibre/ebooks/mobi/utils.py index e9bc4f669f..008b33a0ff 100644 --- a/src/calibre/ebooks/mobi/utils.py +++ b/src/calibre/ebooks/mobi/utils.py @@ -7,7 +7,7 @@ __license__ = 'GPL v3' __copyright__ = '2011, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import struct, string, zlib, os +import struct, string, zlib, os, re from collections import OrderedDict from io import BytesIO @@ -393,6 +393,15 @@ def mobify_image(data): data = im.export('gif') return data +def read_resc_record(data): + ans = {} + match = re.search(br''']*page-progression-direction=['"](.+?)['"]''', data) + if match is not None: + ppd = match.group(1).lower() + if ppd in {b'ltr', b'rtl'}: + ans['page-progression-direction'] = ppd.decode('ascii') + return ans + # Font records {{{ def read_font_record(data, extent=1040): ''' diff --git a/src/calibre/ebooks/oeb/base.py b/src/calibre/ebooks/oeb/base.py index d4b3a2b7ab..29fc27ee3f 100644 --- a/src/calibre/ebooks/oeb/base.py +++ b/src/calibre/ebooks/oeb/base.py @@ -1210,6 +1210,7 @@ class Spine(object): def __init__(self, oeb): self.oeb = oeb self.items = [] + self.page_progression_direction = None def _linear(self, linear): if isinstance(linear, basestring): @@ -1896,4 +1897,6 @@ class OEBBook(object): attrib={'media-type': PAGE_MAP_MIME}) spine.attrib['page-map'] = id results[PAGE_MAP_MIME] = (href, self.pages.to_page_map()) + if self.spine.page_progression_direction in {'ltr', 'rtl'}: + spine.attrib['page-progression-direction'] = self.spine.page_progression_direction return results diff --git a/src/calibre/ebooks/oeb/reader.py b/src/calibre/ebooks/oeb/reader.py index eb7e2eca4c..cb10b4ccce 100644 --- a/src/calibre/ebooks/oeb/reader.py +++ b/src/calibre/ebooks/oeb/reader.py @@ -330,6 +330,9 @@ class OEBReader(object): if len(spine) == 0: raise OEBError("Spine is empty") self._spine_add_extra() + for val in xpath(opf, '/o2:package/o2:spine/@page-progression-direction'): + if val in {'ltr', 'rtl'}: + spine.page_progression_direction = val def _guide_from_opf(self, opf): guide = self.oeb.guide