mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Insert @crossable="no" page breaks prior to non @linear spine items.
This commit is contained in:
parent
9e53eeba78
commit
b9771763d0
@ -21,9 +21,9 @@ from lxml import etree
|
|||||||
from PIL import Image
|
from PIL import Image
|
||||||
from calibre.ebooks.mobi.palmdoc import compress_doc
|
from calibre.ebooks.mobi.palmdoc import compress_doc
|
||||||
from calibre.ebooks.mobi.langcodes import iana2mobi
|
from calibre.ebooks.mobi.langcodes import iana2mobi
|
||||||
from calibre.ebooks.lit.oeb import XML_NS, XHTML, XHTML_NS, OEB_DOCS
|
from calibre.ebooks.oeb.base import XML_NS, XHTML, XHTML_NS, OEB_DOCS
|
||||||
from calibre.ebooks.lit.oeb import xpath, barename, namespace, prefixname
|
from calibre.ebooks.oeb.base import xpath, barename, namespace, prefixname
|
||||||
from calibre.ebooks.lit.oeb import FauxLogger, OEBBook
|
from calibre.ebooks.oeb.base import FauxLogger, OEBBook
|
||||||
|
|
||||||
MBP_NS = 'http://mobipocket.com/ns/mbp'
|
MBP_NS = 'http://mobipocket.com/ns/mbp'
|
||||||
def MBP(name): return '{%s}%s' % (MBP_NS, name)
|
def MBP(name): return '{%s}%s' % (MBP_NS, name)
|
||||||
@ -43,6 +43,8 @@ EXTH_CODES = {
|
|||||||
'title': 503,
|
'title': 503,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RECORD_SIZE = 0x1000
|
||||||
|
|
||||||
UNCOMPRESSED = 1
|
UNCOMPRESSED = 1
|
||||||
PALMDOC = 2
|
PALMDOC = 2
|
||||||
HUFFDIC = 17480
|
HUFFDIC = 17480
|
||||||
@ -50,6 +52,18 @@ HUFFDIC = 17480
|
|||||||
def encode(data):
|
def encode(data):
|
||||||
return data.encode('ascii', 'xmlcharrefreplace')
|
return data.encode('ascii', 'xmlcharrefreplace')
|
||||||
|
|
||||||
|
# Almost like the one for MS LIT, but not quite.
|
||||||
|
def decint(value):
|
||||||
|
bytes = []
|
||||||
|
while True:
|
||||||
|
b = value & 0x7f
|
||||||
|
value >>= 7
|
||||||
|
if not bytes:
|
||||||
|
b |= 0x80
|
||||||
|
bytes.append(chr(b))
|
||||||
|
if value == 0:
|
||||||
|
break
|
||||||
|
return ''.join(reversed(bytes))
|
||||||
|
|
||||||
|
|
||||||
class Serializer(object):
|
class Serializer(object):
|
||||||
@ -60,16 +74,14 @@ class Serializer(object):
|
|||||||
self.images = images
|
self.images = images
|
||||||
self.id_offsets = {}
|
self.id_offsets = {}
|
||||||
self.href_offsets = defaultdict(list)
|
self.href_offsets = defaultdict(list)
|
||||||
|
self.breaks = []
|
||||||
buffer = self.buffer = StringIO()
|
buffer = self.buffer = StringIO()
|
||||||
buffer.write('<html>')
|
buffer.write('<html>')
|
||||||
self.serialize_head()
|
self.serialize_head()
|
||||||
self.serialize_body()
|
self.serialize_body()
|
||||||
buffer.write('</html>')
|
buffer.write('</html>')
|
||||||
self.fixup_links()
|
self.fixup_links()
|
||||||
self.raw = buffer.getvalue()
|
self.text = buffer.getvalue()
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.raw
|
|
||||||
|
|
||||||
def serialize_head(self):
|
def serialize_head(self):
|
||||||
buffer = self.buffer
|
buffer = self.buffer
|
||||||
@ -110,18 +122,20 @@ class Serializer(object):
|
|||||||
def serialize_body(self):
|
def serialize_body(self):
|
||||||
buffer = self.buffer
|
buffer = self.buffer
|
||||||
buffer.write('<body>')
|
buffer.write('<body>')
|
||||||
for item in self.oeb.spine:
|
spine = [item for item in self.oeb.spine if item.linear]
|
||||||
|
spine.extend([item for item in self.oeb.spine if not item.linear])
|
||||||
|
for item in spine:
|
||||||
self.serialize_item(item)
|
self.serialize_item(item)
|
||||||
buffer.write('</body>')
|
buffer.write('</body>')
|
||||||
|
|
||||||
def serialize_item(self, item):
|
def serialize_item(self, item):
|
||||||
buffer = self.buffer
|
buffer = self.buffer
|
||||||
buffer.write('<mbp:pagebreak/>')
|
if not item.linear:
|
||||||
# TODO: Figure out how to make the 'crossable' stuff work for
|
self.breaks.append(buffer.tell() - 1)
|
||||||
# non-"linear" spine items.
|
|
||||||
self.id_offsets[item.id + '#calibre_top'] = buffer.tell()
|
self.id_offsets[item.id + '#calibre_top'] = buffer.tell()
|
||||||
for elem in item.data.find(XHTML('body')):
|
for elem in item.data.find(XHTML('body')):
|
||||||
self.serialize_elem(elem, item)
|
self.serialize_elem(elem, item)
|
||||||
|
buffer.write(' <mbp:pagebreak/>')
|
||||||
|
|
||||||
def serialize_elem(self, elem, item, nsrmap=NSRMAP):
|
def serialize_elem(self, elem, item, nsrmap=NSRMAP):
|
||||||
if namespace(elem.tag) not in nsrmap:
|
if namespace(elem.tag) not in nsrmap:
|
||||||
@ -213,18 +227,31 @@ class MobiWriter(object):
|
|||||||
|
|
||||||
def _generate_text(self):
|
def _generate_text(self):
|
||||||
serializer = Serializer(self._oeb, self._images)
|
serializer = Serializer(self._oeb, self._images)
|
||||||
text = str(serializer)
|
breaks = serializer.breaks
|
||||||
|
text = serializer.text
|
||||||
self._text_length = len(text)
|
self._text_length = len(text)
|
||||||
text = StringIO(text)
|
text = StringIO(text)
|
||||||
nrecords = 0
|
nrecords = 0
|
||||||
data = text.read(0x1000)
|
offset = 0
|
||||||
|
data = text.read(RECORD_SIZE)
|
||||||
while len(data) > 0:
|
while len(data) > 0:
|
||||||
nrecords += 1
|
|
||||||
if self._compress == PALMDOC:
|
if self._compress == PALMDOC:
|
||||||
data = compress_doc(data)
|
data = compress_doc(data)
|
||||||
# Without the NUL Mobipocket Desktop 6.2 will thrash. Why?
|
# Without the NUL Mobipocket Desktop 6.2 will thrash. Why?
|
||||||
self._records.append(data + '\0')
|
record = [data, '\0']
|
||||||
data = text.read(0x1000)
|
nextra = 0
|
||||||
|
pbreak = 0
|
||||||
|
running = 0
|
||||||
|
while breaks and (breaks[0] - offset) < RECORD_SIZE:
|
||||||
|
pbreak = (breaks.pop(0) - running) >> 3
|
||||||
|
record.append(decint(pbreak))
|
||||||
|
running += pbreak << 3
|
||||||
|
nextra += 1
|
||||||
|
record.append(decint(nextra + 1))
|
||||||
|
self._records.append(''.join(record))
|
||||||
|
nrecords += 1
|
||||||
|
offset += RECORD_SIZE
|
||||||
|
data = text.read(RECORD_SIZE)
|
||||||
self._text_nrecords = nrecords
|
self._text_nrecords = nrecords
|
||||||
|
|
||||||
def _rescale_image(self, data, maxsizeb, dimen=None):
|
def _rescale_image(self, data, maxsizeb, dimen=None):
|
||||||
@ -262,17 +289,17 @@ class MobiWriter(object):
|
|||||||
exth = self._build_exth()
|
exth = self._build_exth()
|
||||||
record0 = StringIO()
|
record0 = StringIO()
|
||||||
record0.write(pack('>HHIHHHH', self._compress, 0, self._text_length,
|
record0.write(pack('>HHIHHHH', self._compress, 0, self._text_length,
|
||||||
self._text_nrecords, 0x1000, 0, 0))
|
self._text_nrecords, RECORD_SIZE, 0, 0))
|
||||||
uid = random.randint(0, 0xffffffff)
|
uid = random.randint(0, 0xffffffff)
|
||||||
title = str(metadata.title[0])
|
title = str(metadata.title[0])
|
||||||
record0.write('MOBI')
|
record0.write('MOBI')
|
||||||
record0.write(pack('>IIIII', 0xe8, 2, 65001, uid, 5))
|
record0.write(pack('>IIIII', 0xe8, 2, 65001, uid, 6))
|
||||||
record0.write('\xff' * 40)
|
record0.write('\xff' * 40)
|
||||||
record0.write(pack('>I', self._text_nrecords + 1))
|
record0.write(pack('>I', self._text_nrecords + 1))
|
||||||
record0.write(pack('>II', 0xe8 + 16 + len(exth), len(title)))
|
record0.write(pack('>II', 0xe8 + 16 + len(exth), len(title)))
|
||||||
record0.write(iana2mobi(str(metadata.language[0])))
|
record0.write(iana2mobi(str(metadata.language[0])))
|
||||||
record0.write('\0' * 8)
|
record0.write('\0' * 8)
|
||||||
record0.write(pack('>II', 5, self._text_nrecords + 1))
|
record0.write(pack('>II', 6, self._text_nrecords + 1))
|
||||||
record0.write('\0' * 16)
|
record0.write('\0' * 16)
|
||||||
record0.write(pack('>I', 0x50))
|
record0.write(pack('>I', 0x50))
|
||||||
record0.write('\0' * 32)
|
record0.write('\0' * 32)
|
||||||
@ -280,7 +307,7 @@ class MobiWriter(object):
|
|||||||
# TODO: What the hell are these fields?
|
# TODO: What the hell are these fields?
|
||||||
record0.write(pack('>IIIIIIIIIIIIIIIII',
|
record0.write(pack('>IIIIIIIIIIIIIIIII',
|
||||||
0, 0, 0, 0xffffffff, 0, 0xffffffff, 0, 0xffffffff, 0, 0xffffffff,
|
0, 0, 0, 0xffffffff, 0, 0xffffffff, 0, 0xffffffff, 0, 0xffffffff,
|
||||||
0, 0xffffffff, 0, 0xffffffff, 0xffffffff, 1, 0xffffffff))
|
0, 0xffffffff, 0, 0xffffffff, 0xffffffff, 5, 0xffffffff))
|
||||||
record0.write(exth)
|
record0.write(exth)
|
||||||
record0.write(title)
|
record0.write(title)
|
||||||
record0 = record0.getvalue()
|
record0 = record0.getvalue()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user