Insert @crossable="no" page breaks prior to non @linear spine items.

This commit is contained in:
Marshall T. Vandegrift 2008-12-31 01:28:55 -05:00
parent 9e53eeba78
commit b9771763d0

View File

@ -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()