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)
This commit is contained in:
Kovid Goyal 2013-06-26 15:46:41 +05:30
parent 03452d2a03
commit 63d133ea5c
5 changed files with 36 additions and 4 deletions

View File

@ -1047,6 +1047,14 @@ class OPF(object): # {{{
if raw: if raw:
return raw.rpartition(':')[-1] 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): def guess_cover(self):
''' '''
Try to guess a cover. Needed for some old/badly formed OPF files. 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) Metadata.__init__(self, title='', other=other)
self.base_path = os.path.abspath(base_path) self.base_path = os.path.abspath(base_path)
self.page_progression_direction = None
if self.application_id is None: if self.application_id is None:
self.application_id = str(uuid.uuid4()) self.application_id = str(uuid.uuid4())
if not isinstance(self.toc, TOC): if not isinstance(self.toc, TOC):
@ -1356,6 +1365,8 @@ class OPFCreator(Metadata):
spine = E.spine() spine = E.spine()
if self.toc is not None: if self.toc is not None:
spine.set('toc', 'ncx') 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: if self.spine is not None:
for ref in self.spine: for ref in self.spine:
if ref.id is not None: if ref.id is not None:

View File

@ -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.mobi.reader.markup import expand_mobi8_markup
from calibre.ebooks.metadata.opf2 import Guide, OPFCreator from calibre.ebooks.metadata.opf2 import Guide, OPFCreator
from calibre.ebooks.metadata.toc import TOC 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.parse_utils import parse_html
from calibre.ebooks.oeb.base import XPath, XHTML, xml2text from calibre.ebooks.oeb.base import XPath, XHTML, xml2text
from calibre.utils.imghdr import what from calibre.utils.imghdr import what
@ -65,6 +65,7 @@ class Mobi8Reader(object):
self.mobi6_reader, self.log = mobi6_reader, log self.mobi6_reader, self.log = mobi6_reader, log
self.header = mobi6_reader.book_header self.header = mobi6_reader.book_header
self.encrypted_fonts = [] self.encrypted_fonts = []
self.resc_data = {}
def __call__(self): def __call__(self):
self.mobi6_reader.check_for_drm() self.mobi6_reader.check_for_drm()
@ -389,9 +390,11 @@ class Mobi8Reader(object):
data = sec[0] data = sec[0]
typ = data[:4] typ = data[:4]
href = None href = None
if typ in {b'FLIS', b'FCIS', b'SRCS', b'\xe9\x8e\r\n', if typ in {b'FLIS', b'FCIS', b'SRCS', b'\xe9\x8e\r\n', b'BOUN',
b'RESC', b'BOUN', b'FDST', b'DATP', b'AUDI', b'VIDE'}: b'FDST', b'DATP', b'AUDI', b'VIDE'}:
pass # Ignore these records pass # Ignore these records
elif typ == b'RESC':
self.resc_data = read_resc_record(data)
elif typ == b'FONT': elif typ == b'FONT':
font = read_font_record(data) font = read_font_record(data)
href = "fonts/%05d.%s" % (fname_idx, font['ext']) 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_manifest_from_files_in([os.getcwdu()], exclude=exclude)
opf.create_spine(spine) opf.create_spine(spine)
opf.set_toc(toc) 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: with open('metadata.opf', 'wb') as of, open('toc.ncx', 'wb') as ncx:
opf.render(of, ncx, 'toc.ncx') opf.render(of, ncx, 'toc.ncx')

View File

@ -7,7 +7,7 @@ __license__ = 'GPL v3'
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import struct, string, zlib, os import struct, string, zlib, os, re
from collections import OrderedDict from collections import OrderedDict
from io import BytesIO from io import BytesIO
@ -393,6 +393,15 @@ def mobify_image(data):
data = im.export('gif') data = im.export('gif')
return data return data
def read_resc_record(data):
ans = {}
match = re.search(br'''<spine [^>]*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 {{{ # Font records {{{
def read_font_record(data, extent=1040): def read_font_record(data, extent=1040):
''' '''

View File

@ -1210,6 +1210,7 @@ class Spine(object):
def __init__(self, oeb): def __init__(self, oeb):
self.oeb = oeb self.oeb = oeb
self.items = [] self.items = []
self.page_progression_direction = None
def _linear(self, linear): def _linear(self, linear):
if isinstance(linear, basestring): if isinstance(linear, basestring):
@ -1896,4 +1897,6 @@ class OEBBook(object):
attrib={'media-type': PAGE_MAP_MIME}) attrib={'media-type': PAGE_MAP_MIME})
spine.attrib['page-map'] = id spine.attrib['page-map'] = id
results[PAGE_MAP_MIME] = (href, self.pages.to_page_map()) 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 return results

View File

@ -330,6 +330,9 @@ class OEBReader(object):
if len(spine) == 0: if len(spine) == 0:
raise OEBError("Spine is empty") raise OEBError("Spine is empty")
self._spine_add_extra() 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): def _guide_from_opf(self, opf):
guide = self.oeb.guide guide = self.oeb.guide