home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2011 January / maximum-cd-2011-01.iso / DiscContents / calibre-0.7.26.msi / file_985 (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-10-31  |  75.7 KB  |  2,439 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. __license__ = 'GPL v3'
  5. __copyright__ = '2008, Marshall T. Vandegrift <llasram@gmail.cam> and         Kovid Goyal <kovid@kovidgoyal.net>'
  6. from collections import defaultdict
  7. from itertools import count
  8. from itertools import izip
  9. import random
  10. import re
  11. from struct import pack
  12. import time
  13. from urlparse import urldefrag
  14. from cStringIO import StringIO
  15. from calibre.ebooks.mobi.langcodes import iana2mobi
  16. from calibre.ebooks.mobi.mobiml import MBP_NS
  17. from calibre.ebooks.oeb.base import OEB_DOCS
  18. from calibre.ebooks.oeb.base import OEB_RASTER_IMAGES
  19. from calibre.ebooks.oeb.base import XHTML
  20. from calibre.ebooks.oeb.base import XHTML_NS
  21. from calibre.ebooks.oeb.base import XML_NS
  22. from calibre.ebooks.oeb.base import namespace
  23. from calibre.ebooks.oeb.base import prefixname
  24. from calibre.ebooks.oeb.base import urlnormalize
  25. from calibre.ebooks.compression.palmdoc import compress_doc
  26. from calibre.utils.magick.draw import Image, save_cover_data_to, thumbnail
  27. INDEXING = True
  28. FCIS_FLIS = True
  29. WRITE_PBREAKS = True
  30. EXTH_CODES = {
  31.     'creator': 100,
  32.     'publisher': 101,
  33.     'description': 103,
  34.     'identifier': 104,
  35.     'subject': 105,
  36.     'pubdate': 106,
  37.     'date': 106,
  38.     'review': 107,
  39.     'contributor': 108,
  40.     'rights': 109,
  41.     'type': 111,
  42.     'source': 112,
  43.     'title': 503 }
  44. RECORD_SIZE = 4096
  45. UNCOMPRESSED = 1
  46. PALMDOC = 2
  47. HUFFDIC = 17480
  48. PALM_MAX_IMAGE_SIZE = 63 * 1024
  49. OTHER_MAX_IMAGE_SIZE = 10 * 1024 * 1024
  50. MAX_THUMB_SIZE = 16 * 1024
  51. MAX_THUMB_DIMEN = (180, 240)
  52. TAGX = {
  53.     'chapter': '\x00\x00\x00\x01\x01\x01\x01\x00\x02\x01\x02\x00\x03\x01\x04\x00\x04\x01\x08\x00\x00\x00\x00\x01',
  54.     'subchapter': '\x00\x00\x00\x01\x01\x01\x01\x00\x02\x01\x02\x00\x03\x01\x04\x00\x04\x01\x08\x00\x05\x01\x10\x00\x15\x01\x10\x00\x16\x01 \x00\x17\x01@\x00\x00\x00\x00\x01',
  55.     'periodical': '\x00\x00\x00\x02\x01\x01\x01\x00\x02\x01\x02\x00\x03\x01\x04\x00\x04\x01\x08\x00\x05\x01\x10\x00\x15\x01 \x00\x16\x01@\x00\x17\x01\x80\x00\x00\x00\x00\x01E\x01\x01\x00F\x01\x02\x00G\x01\x04\x00\x00\x00\x00\x01',
  56.     'secondary_book': '\x00\x00\x00\x01\x01\x01\x01\x00\x00\x00\x00\x01',
  57.     'secondary_periodical': '\x00\x00\x00\x01\x01\x01\x01\x00\x0b\x03\x02\x00\x00\x00\x00\x01' }
  58. INDXT = {
  59.     'chapter': '\x0f',
  60.     'subchapter': '\x1f',
  61.     'article': '?',
  62.     'chapter with subchapters': 'o',
  63.     'periodical': '\xdf',
  64.     'section': '\xff' }
  65.  
  66. def encode(data):
  67.     return data.encode('utf-8')
  68.  
  69. DECINT_FORWARD = 0
  70. DECINT_BACKWARD = 1
  71.  
  72. def decint(value, direction):
  73.     bytes = []
  74.     while True:
  75.         b = value & 127
  76.         value >>= 7
  77.         bytes.append(b)
  78.         if value == 0:
  79.             break
  80.             continue
  81.     if direction == DECINT_FORWARD:
  82.         bytes[0] |= 128
  83.     elif direction == DECINT_BACKWARD:
  84.         bytes[-1] |= 128
  85.     
  86.     return ''.join((lambda .0: for b in .0:
  87. chr(b))(reversed(bytes)))
  88.  
  89.  
  90. def align_block(raw, multiple = 4, pad = '\x00'):
  91.     extra = len(raw) % multiple
  92.     if extra == 0:
  93.         return raw
  94.     return raw + pad * (multiple - extra)
  95.  
  96.  
  97. def rescale_image(data, maxsizeb, dimen = None):
  98.     if dimen is not None:
  99.         data = thumbnail(data, width = dimen, height = dimen)[-1]
  100.     else:
  101.         data = save_cover_data_to(data, 'img.jpg', return_data = True)
  102.     if len(data) <= maxsizeb:
  103.         return data
  104.     orig_data = data
  105.     img = Image()
  106.     quality = 95
  107.     img.load(data)
  108.     while len(data) >= maxsizeb and quality >= 10:
  109.         quality -= 5
  110.         img.set_compression_quality(quality)
  111.         data = img.export('jpg')
  112.         continue
  113.         len(data) <= maxsizeb
  114.     if len(data) <= maxsizeb:
  115.         return data
  116.     orig_data = data
  117.     scale = 0.9
  118.     while len(data) >= maxsizeb and scale >= 0.05:
  119.         img = Image()
  120.         img.load(orig_data)
  121.         (w, h) = img.size
  122.         img.size = (int(scale * w), int(scale * h))
  123.         img.set_compression_quality(quality)
  124.         data = img.export('jpg')
  125.         scale -= 0.05
  126.         continue
  127.         len(data) <= maxsizeb
  128.     return data
  129.  
  130.  
  131. class Serializer(object):
  132.     NSRMAP = {
  133.         '': None,
  134.         XML_NS: 'xml',
  135.         XHTML_NS: '',
  136.         MBP_NS: 'mbp' }
  137.     
  138.     def __init__(self, oeb, images, write_page_breaks_after_item = True):
  139.         self.oeb = oeb
  140.         self.images = images
  141.         self.logger = oeb.logger
  142.         self.write_page_breaks_after_item = write_page_breaks_after_item
  143.         self.id_offsets = { }
  144.         self.href_offsets = defaultdict(list)
  145.         self.breaks = []
  146.         buffer = self.buffer = StringIO()
  147.         buffer.write('<html>')
  148.         self.serialize_head()
  149.         self.serialize_body()
  150.         buffer.write('</html>')
  151.         self.fixup_links()
  152.         self.text = buffer.getvalue()
  153.  
  154.     
  155.     def serialize_head(self):
  156.         buffer = self.buffer
  157.         buffer.write('<head>')
  158.         if len(self.oeb.guide) > 0:
  159.             self.serialize_guide()
  160.         
  161.         buffer.write('</head>')
  162.  
  163.     
  164.     def serialize_guide(self):
  165.         buffer = self.buffer
  166.         hrefs = self.oeb.manifest.hrefs
  167.         buffer.write('<guide>')
  168.         for ref in self.oeb.guide.values():
  169.             path = urldefrag(ref.href)[0]
  170.             if path not in hrefs or hrefs[path].media_type not in OEB_DOCS:
  171.                 continue
  172.             
  173.             buffer.write('<reference type="')
  174.             if ref.type.startswith('other.'):
  175.                 self.serialize_text(ref.type.replace('other.', ''), quot = True)
  176.             else:
  177.                 self.serialize_text(ref.type, quot = True)
  178.             buffer.write('" ')
  179.             if ref.title is not None:
  180.                 buffer.write('title="')
  181.                 self.serialize_text(ref.title, quot = True)
  182.                 buffer.write('" ')
  183.             
  184.             self.serialize_href(ref.href)
  185.             buffer.write(' />')
  186.         
  187.         buffer.write('</guide>')
  188.  
  189.     
  190.     def serialize_href(self, href, base = None):
  191.         hrefs = self.oeb.manifest.hrefs
  192.         (path, frag) = urldefrag(urlnormalize(href))
  193.         if path and base:
  194.             path = base.abshref(path)
  195.         
  196.         if path and path not in hrefs:
  197.             return False
  198.         buffer = self.buffer
  199.         item = path not in hrefs if path else None
  200.         if item and item.spine_position is None:
  201.             return False
  202.         path = item.spine_position is None if item else base.href
  203.         href = None if frag else path
  204.         buffer.write('filepos=')
  205.         self.href_offsets[href].append(buffer.tell())
  206.         buffer.write('0000000000')
  207.         return True
  208.  
  209.     
  210.     def serialize_body(self):
  211.         buffer = self.buffer
  212.         self.anchor_offset = buffer.tell()
  213.         buffer.write('<body>')
  214.         self.anchor_offset_kindle = buffer.tell()
  215.         if 'text' in self.oeb.guide:
  216.             href = self.oeb.guide['text'].href
  217.             buffer.write('<a ')
  218.             self.serialize_href(href)
  219.             buffer.write(' />')
  220.         
  221.         spine = _[1]
  222.         [](_[2])
  223.         for item in spine:
  224.             self.serialize_item(item)
  225.         
  226.         buffer.write('</body>')
  227.  
  228.     
  229.     def serialize_item(self, item):
  230.         buffer = self.buffer
  231.         if not item.linear:
  232.             self.breaks.append(buffer.tell() - 1)
  233.         
  234.         self.id_offsets[urlnormalize(item.href)] = buffer.tell()
  235.         buffer.write('<div>')
  236.         for elem in item.data.find(XHTML('body')):
  237.             self.serialize_elem(elem, item)
  238.         
  239.         buffer.write('<div></div>')
  240.         if self.write_page_breaks_after_item:
  241.             buffer.write('<mbp:pagebreak/>')
  242.         
  243.         buffer.write('</div>')
  244.  
  245.     
  246.     def serialize_elem(self, elem, item, nsrmap = NSRMAP):
  247.         buffer = self.buffer
  248.         if not isinstance(elem.tag, basestring) or namespace(elem.tag) not in nsrmap:
  249.             return None
  250.         tag = prefixname(elem.tag, nsrmap)
  251.         id = elem.attrib.pop('id', None)
  252.         if id is not None:
  253.             href = '#'.join((item.href, id))
  254.             if not self.anchor_offset:
  255.                 pass
  256.             offset = buffer.tell()
  257.             self.id_offsets[urlnormalize(href)] = offset
  258.         
  259.         if self.anchor_offset is not None and tag == 'a' and not (elem.attrib) and not len(elem) and not (elem.text):
  260.             return None
  261.         self.anchor_offset = buffer.tell()
  262.         buffer.write('<')
  263.         buffer.write(tag)
  264.         if elem.attrib:
  265.             for attr, val in elem.attrib.items():
  266.                 if namespace(attr) not in nsrmap:
  267.                     continue
  268.                 
  269.                 attr = prefixname(attr, nsrmap)
  270.                 buffer.write(' ')
  271.                 if attr == 'href':
  272.                     if self.serialize_href(val, item):
  273.                         continue
  274.                     
  275.                 elif attr == 'src':
  276.                     href = urlnormalize(item.abshref(val))
  277.                     if href in self.images:
  278.                         index = self.images[href]
  279.                         buffer.write('recindex="%05d"' % index)
  280.                         continue
  281.                     
  282.                 
  283.                 buffer.write(attr)
  284.                 buffer.write('="')
  285.                 self.serialize_text(val, quot = True)
  286.                 buffer.write('"')
  287.             
  288.         
  289.         if elem.text or len(elem) > 0:
  290.             buffer.write('>')
  291.             if elem.text:
  292.                 self.anchor_offset = None
  293.                 self.serialize_text(elem.text)
  294.             
  295.             for child in elem:
  296.                 self.serialize_elem(child, item)
  297.                 if child.tail:
  298.                     self.anchor_offset = None
  299.                     self.serialize_text(child.tail)
  300.                     continue
  301.             
  302.             buffer.write('</%s>' % tag)
  303.         else:
  304.             buffer.write('/>')
  305.  
  306.     
  307.     def serialize_text(self, text, quot = False):
  308.         text = text.replace('&', '&')
  309.         text = text.replace('<', '<')
  310.         text = text.replace('>', '>')
  311.         text = text.replace(u'┬¡', '')
  312.         if quot:
  313.             text = text.replace('"', '"')
  314.         
  315.         self.buffer.write(encode(text))
  316.  
  317.     
  318.     def fixup_links(self):
  319.         buffer = self.buffer
  320.         id_offsets = self.id_offsets
  321.         for href, hoffs in self.href_offsets.items():
  322.             if href not in id_offsets:
  323.                 self.logger.warn('Hyperlink target %r not found' % href)
  324.                 (href, _) = urldefrag(href)
  325.             
  326.             ioff = self.id_offsets[href]
  327.             for hoff in hoffs:
  328.                 buffer.seek(hoff)
  329.                 buffer.write('%010d' % ioff)
  330.             
  331.         
  332.  
  333.  
  334.  
  335. class MobiWriter(object):
  336.     COLLAPSE_RE = re.compile('[ \\t\\r\\n\\v]+')
  337.     
  338.     def __init__(self, opts, compression = PALMDOC, imagemax = None, prefer_author_sort = False, write_page_breaks_after_item = True):
  339.         self.opts = opts
  340.         self.write_page_breaks_after_item = write_page_breaks_after_item
  341.         if not compression:
  342.             pass
  343.         self._compression = UNCOMPRESSED
  344.         if not imagemax:
  345.             pass
  346.         self._imagemax = OTHER_MAX_IMAGE_SIZE
  347.         self._prefer_author_sort = prefer_author_sort
  348.         self._primary_index_record = None
  349.         self._conforming_periodical_toc = False
  350.         self._indexable = False
  351.         self._ctoc = ''
  352.         self._ctoc_records = []
  353.         self._ctoc_offset = 0
  354.         self._ctoc_largest = 0
  355.         self._HTMLRecords = []
  356.         self._tbSequence = ''
  357.         self._MobiDoc = None
  358.         self._anchor_offset_kindle = 0
  359.         self._initialIndexRecordFound = False
  360.         self._firstSectionConcluded = False
  361.         self._currentSectionIndex = 0
  362.  
  363.     
  364.     def generate(cls, opts):
  365.         imagemax = None if opts.rescale_images else None
  366.         prefer_author_sort = opts.prefer_author_sort
  367.         return cls(compression = PALMDOC, imagemax = imagemax, prefer_author_sort = prefer_author_sort)
  368.  
  369.     generate = classmethod(generate)
  370.     
  371.     def __call__(self, oeb, path):
  372.         if hasattr(path, 'write'):
  373.             return self._dump_stream(oeb, path)
  374.         
  375.         try:
  376.             stream = _[1]
  377.             return self._dump_stream(oeb, stream)
  378.         finally:
  379.             pass
  380.  
  381.  
  382.     
  383.     def _write(self, *data):
  384.         for datum in data:
  385.             self._stream.write(datum)
  386.         
  387.  
  388.     
  389.     def _tell(self):
  390.         return self._stream.tell()
  391.  
  392.     
  393.     def _dump_stream(self, oeb, stream):
  394.         self._oeb = oeb
  395.         self._stream = stream
  396.         self._records = [
  397.             None]
  398.         self._generate_content()
  399.         self._generate_record0()
  400.         self._write_header()
  401.         self._write_content()
  402.  
  403.     
  404.     def _generate_content(self):
  405.         self._map_image_names()
  406.         self._generate_text()
  407.         if INDEXING and self._indexable:
  408.             
  409.             try:
  410.                 self._generate_index()
  411.             self._oeb.log.exception('Failed to generate index')
  412.  
  413.         
  414.         self._generate_images()
  415.  
  416.     
  417.     def _map_image_names(self):
  418.         index = 1
  419.         self._images = images = { }
  420.         mh_href = None
  421.         if 'masthead' in self._oeb.guide:
  422.             mh_href = self._oeb.guide['masthead'].href
  423.             images[mh_href] = 1
  424.             index += 1
  425.         
  426.         for item in self._oeb.manifest.values():
  427.             if item.media_type in OEB_RASTER_IMAGES:
  428.                 if item.href == mh_href:
  429.                     continue
  430.                 
  431.                 images[item.href] = index
  432.                 index += 1
  433.                 continue
  434.         
  435.  
  436.     
  437.     def _read_text_record(self, text):
  438.         pos = text.tell()
  439.         text.seek(0, 2)
  440.         npos = min((pos + RECORD_SIZE, text.tell()))
  441.         last = ''
  442.         while not last.decode('utf-8', 'ignore'):
  443.             size = len(last) + 1
  444.             text.seek(npos - size)
  445.             last = text.read(size)
  446.         extra = 0
  447.         
  448.         try:
  449.             last.decode('utf-8')
  450.         except UnicodeDecodeError:
  451.             prev = len(last)
  452.             while True:
  453.                 text.seek(npos - prev)
  454.                 last = text.read(len(last) + 1)
  455.                 
  456.                 try:
  457.                     last.decode('utf-8')
  458.                 except UnicodeDecodeError:
  459.                     continue
  460.  
  461.                 break
  462.             extra = len(last) - prev
  463.  
  464.         text.seek(pos)
  465.         data = text.read(RECORD_SIZE)
  466.         overlap = text.read(extra)
  467.         text.seek(npos)
  468.         return (data, overlap)
  469.  
  470.     
  471.     def _generate_flat_indexed_navpoints(self):
  472.         self._oeb.logger.info('Indexing flat navPoints ...')
  473.         numberOfHTMLRecords = self._content_length // RECORD_SIZE + 1
  474.         x = numberOfHTMLRecords
  475.         while x:
  476.             self._HTMLRecords.append(HTMLRecordData())
  477.             x -= 1
  478.         toc = self._oeb.toc
  479.         myIndex = 0
  480.         myEndingRecord = 0
  481.         previousOffset = 0
  482.         previousLength = 0
  483.         offset = 0
  484.         length = 0
  485.         entries = list(toc.iter())[1:]
  486.         for i, child in enumerate(entries):
  487.             if not (child.title) or not child.title.strip():
  488.                 child.title = '(none)'
  489.             
  490.             if not (child.title) or not child.title.strip():
  491.                 child.title = '(none)'
  492.             
  493.             h = child.href
  494.             if h not in self._id_offsets:
  495.                 self._oeb.log.warning('  Could not find TOC entry "%s", aborting indexing ...' % child.title)
  496.                 return False
  497.             offset = self._id_offsets[h]
  498.             length = None
  499.             for sibling in entries[i + 1:]:
  500.                 h2 = sibling.href
  501.                 if h2 in self._id_offsets:
  502.                     offset2 = self._id_offsets[h2]
  503.                     if offset2 > offset:
  504.                         length = offset2 - offset
  505.                         break
  506.                     
  507.                 offset2 > offset
  508.             
  509.             if length is None:
  510.                 length = self._content_length - offset
  511.             
  512.             if self.opts.verbose > 3:
  513.                 self._oeb.logger.info('child %03d: %s' % (i, child))
  514.                 self._oeb.logger.info('    title: %s' % child.title)
  515.                 self._oeb.logger.info('    depth: %d' % child.depth())
  516.                 self._oeb.logger.info('   offset: 0x%06X \tlength: 0x%06X \tnext: 0x%06X' % (offset, length, offset + length))
  517.             
  518.             if i and child.depth() == 1 and entries[i - 1].depth() == 1:
  519.                 if offset != previousOffset + previousLength:
  520.                     self._oeb.log.warning('*** TOC discontinuity ***')
  521.                     self._oeb.log.warning(" node %03d: '%s' offset: 0x%X length: 0x%X" % (i - 1, entries[i - 1].title, previousOffset, previousLength))
  522.                     self._oeb.log.warning(" node %03d: '%s' offset: 0x%X != 0x%06X" % (i, child.title, offset, previousOffset + previousLength))
  523.                     self._oeb.log.warning('_generate_flat_indexed_navpoints: Failed to generate index')
  524.                     self._HTMLRecords = []
  525.                     return False
  526.             
  527.             previousOffset = offset
  528.             previousLength = length
  529.             myStartingRecord = offset // RECORD_SIZE
  530.             if self._HTMLRecords[myStartingRecord].openingNode == -1:
  531.                 self._HTMLRecords[myStartingRecord].openingNode = myIndex
  532.             
  533.             myEndingRecord = (offset + length) // RECORD_SIZE
  534.             if myEndingRecord > myStartingRecord:
  535.                 interimSpanRecord = myStartingRecord + 1
  536.                 while interimSpanRecord <= myEndingRecord:
  537.                     self._HTMLRecords[interimSpanRecord].continuingNode = myIndex
  538.                     self._HTMLRecords[interimSpanRecord].currentSectionNodeCount = 1
  539.                     interimSpanRecord += 1
  540.                     continue
  541.                     None if self._HTMLRecords[myStartingRecord].currentSectionNodeCount == -1 else self._HTMLRecords[myStartingRecord]
  542.                 if self.opts.verbose > 3:
  543.                     None(self._oeb.logger.info % (' node %03d: %-15.15s... spans HTML records %03d - %03d \t offset: 0x%06X length: 0x%06X', myIndex if child.title.strip() > '' else '(missing)', myStartingRecord, interimSpanRecord, offset, length))
  544.                 
  545.             elif self.opts.verbose > 3:
  546.                 None(self._oeb.logger.info % (' node %03d: %-15.15s... spans HTML records %03d - %03d \t offset: 0x%06X length: 0x%06X', myIndex if child.title.strip() > '' else '(missing)', myStartingRecord, myStartingRecord, offset, length))
  547.             
  548.             myIndex += 1
  549.         
  550.         return True
  551.  
  552.     
  553.     def _generate_indexed_navpoints(self):
  554.         self._oeb.logger.info('Indexing navPoints ...')
  555.         numberOfHTMLRecords = self._content_length // RECORD_SIZE + 1
  556.         x = numberOfHTMLRecords
  557.         while x:
  558.             self._HTMLRecords.append(HTMLRecordData())
  559.             x -= 1
  560.         toc = self._oeb.toc
  561.         myIndex = 0
  562.         myEndingRecord = 0
  563.         previousOffset = 0
  564.         previousLength = 0
  565.         offset = 0
  566.         length = 0
  567.         sectionChangedInRecordNumber = -1
  568.         sectionChangesInThisRecord = False
  569.         entries = list(toc.iter())[1:]
  570.         for firstSequentialNode, node in enumerate(list(self._ctoc_map)):
  571.             if node['klass'] != 'article' and node['klass'] != 'chapter':
  572.                 continue
  573.                 continue
  574.             if self.opts.verbose > 3:
  575.                 self._oeb.logger.info('\tFirst sequential node: %03d' % firstSequentialNode)
  576.             
  577.         
  578.         for i, child in enumerate(entries):
  579.             h = child.href
  580.             if h not in self._id_offsets:
  581.                 self._oeb.log.warning('  Could not find TOC entry "%s", aborting indexing ...' % child.title)
  582.                 return False
  583.             offset = self._id_offsets[h]
  584.             length = None
  585.             for sibling in entries[i + 1:]:
  586.                 h2 = sibling.href
  587.                 if h2 in self._id_offsets:
  588.                     offset2 = self._id_offsets[h2]
  589.                     if offset2 > offset:
  590.                         length = offset2 - offset
  591.                         break
  592.                     
  593.                 offset2 > offset
  594.             
  595.             if length is None:
  596.                 length = self._content_length - offset
  597.             
  598.             if self.opts.verbose > 3:
  599.                 self._oeb.logger.info('child %03d: %s' % (i, child))
  600.                 self._oeb.logger.info('    title: %s' % child.title)
  601.                 self._oeb.logger.info('    depth: %d' % child.depth())
  602.                 self._oeb.logger.info('   offset: 0x%06X \tlength: 0x%06X \tnext: 0x%06X' % (offset, length, offset + length))
  603.             
  604.             if i > firstSequentialNode and self._ctoc_map[i - 1]['klass'] != 'section':
  605.                 if offset != previousOffset + previousLength:
  606.                     self._oeb.log.warning('*** TOC discontinuity: nodes are not sequential ***')
  607.                     self._oeb.log.info(" node %03d: '%s' offset: 0x%X length: 0x%X" % (i - 1, entries[i - 1].title, previousOffset, previousLength))
  608.                     self._oeb.log.warning(" node %03d: '%s' offset: 0x%X != 0x%06X" % (i, child.title, offset, previousOffset + previousLength))
  609.                     self._oeb.log.info('...')
  610.                     while i - 6 > 0:
  611.                         pass
  612.                     i - 6
  613.                 self._oeb.log.info('...')
  614.                 self._oeb.log.warning('_generate_indexed_navpoints: Failed to generate index')
  615.                 self._HTMLRecords = []
  616.                 return False
  617.                 self._ctoc_map[i - 1]['klass'] != 'section'
  618.             
  619.             previousOffset = offset
  620.             previousLength = length
  621.             thisRecord = offset // RECORD_SIZE
  622.             if self._ctoc_map[i]['klass'] == 'article':
  623.                 if thisRecord > 0:
  624.                     if sectionChangesInThisRecord:
  625.                         self._HTMLRecords[thisRecord].continuingNodeParent = self._currentSectionIndex - 1
  626.                     else:
  627.                         self._HTMLRecords[thisRecord].continuingNodeParent = self._currentSectionIndex
  628.                 
  629.             
  630.             if self._ctoc_map[i]['klass'] == 'periodical':
  631.                 continue
  632.             
  633.             if self._ctoc_map[i]['klass'] == 'section':
  634.                 if thisRecord > 0:
  635.                     sectionChangesInThisRecord = True
  636.                     self._currentSectionIndex += 1
  637.                     self._HTMLRecords[thisRecord].nextSectionNumber = self._currentSectionIndex
  638.                     self._HTMLRecords[thisRecord].nextSectionOpeningNode = myIndex
  639.                     continue
  640.                 
  641.             
  642.             if self._HTMLRecords[thisRecord].openingNode == -1:
  643.                 self._HTMLRecords[thisRecord].openingNode = myIndex
  644.                 self._HTMLRecords[thisRecord].openingNodeParent = self._currentSectionIndex
  645.             
  646.             myEndingRecord = (offset + length) // RECORD_SIZE
  647.             if myEndingRecord > thisRecord:
  648.                 sectionChangesInThisRecord = False
  649.                 interimSpanRecord = thisRecord + 1
  650.                 while interimSpanRecord <= myEndingRecord:
  651.                     self._HTMLRecords[interimSpanRecord].continuingNode = myIndex
  652.                     self._HTMLRecords[interimSpanRecord].continuingNodeParent = self._currentSectionIndex
  653.                     self._HTMLRecords[interimSpanRecord].currentSectionNodeCount = 1
  654.                     interimSpanRecord += 1
  655.                     continue
  656.                     None if sectionChangedInRecordNumber == thisRecord else None if self._HTMLRecords[thisRecord].currentSectionNodeCount == -1 else self._HTMLRecords[thisRecord]
  657.                 if self.opts.verbose > 3:
  658.                     None(self._oeb.logger.info % ('     node: %03d %-10.10s %-15.15s... spans HTML records %03d-%03d \t offset: 0x%06X length: 0x%06X', myIndex, self._ctoc_map[i]['klass'] if child.title.strip() > '' else '(missing)', thisRecord, interimSpanRecord, offset, length))
  659.                 
  660.             elif thisRecord == numberOfHTMLRecords - 1:
  661.                 if self._HTMLRecords[thisRecord].continuingNode == -1:
  662.                     self._HTMLRecords[thisRecord].continuingNode = self._HTMLRecords[thisRecord].openingNode - 1
  663.                 
  664.             elif self.opts.verbose > 3:
  665.                 None(self._oeb.logger.info % ('     node: %03d %-10.10s %-15.15s... spans HTML records %03d-%03d \t offset: 0x%06X length: 0x%06X', myIndex, self._ctoc_map[i]['klass'] if child.title.strip() > '' else '(missing)', thisRecord, thisRecord, offset, length))
  666.             
  667.             myIndex += 1
  668.         
  669.         return True
  670.  
  671.     
  672.     def _generate_tbs_book(self, nrecords, lastrecord):
  673.         if self.opts.verbose > 3:
  674.             self._oeb.logger.info('Assembling TBS for Book: HTML record %03d of %03d' % (nrecords, lastrecord))
  675.         
  676.         tbsType = 0
  677.         tbSequence = ''
  678.         if self._initialIndexRecordFound == False:
  679.             if self._HTMLRecords[nrecords].currentSectionNodeCount == -1:
  680.                 tbSequence = decint(len(tbSequence) + 1, DECINT_FORWARD)
  681.             else:
  682.                 self._initialIndexRecordFound = True
  683.                 if self._HTMLRecords[nrecords].currentSectionNodeCount == 1:
  684.                     tbsType = 2
  685.                 else:
  686.                     tbsType = 6
  687.                 tbSequence = decint(tbsType, DECINT_FORWARD)
  688.                 tbSequence += decint(0, DECINT_FORWARD)
  689.                 if tbsType != 2:
  690.                     tbSequence += chr(self._HTMLRecords[nrecords].currentSectionNodeCount)
  691.                 
  692.                 tbSequence += decint(len(tbSequence) + 1, DECINT_FORWARD)
  693.         elif nrecords == lastrecord and self._HTMLRecords[nrecords].currentSectionNodeCount == 1:
  694.             tbsType = 2
  695.         elif self._HTMLRecords[nrecords].continuingNode > 0 and self._HTMLRecords[nrecords].openingNode == -1:
  696.             tbsType = 3
  697.             self._HTMLRecords[nrecords].currentSectionNodeCount = 128
  698.         else:
  699.             tbsType = 6
  700.         shiftedNCXEntry = self._HTMLRecords[nrecords].continuingNode << 3
  701.         shiftedNCXEntry |= tbsType
  702.         tbSequence = decint(shiftedNCXEntry, DECINT_FORWARD)
  703.         tbSequence += decint(0, DECINT_FORWARD)
  704.         if tbsType != 2:
  705.             tbSequence += chr(self._HTMLRecords[nrecords].currentSectionNodeCount)
  706.         
  707.         tbSequence += decint(len(tbSequence) + 1, DECINT_FORWARD)
  708.         self._tbSequence = tbSequence
  709.  
  710.     
  711.     def _generate_tbs_flat_periodical(self, nrecords, lastrecord):
  712.         tbsType = 0
  713.         tbSequence = ''
  714.         if self._initialIndexRecordFound == False:
  715.             if self._HTMLRecords[nrecords].currentSectionNodeCount == -1:
  716.                 tbSequence = decint(len(tbSequence) + 1, DECINT_FORWARD)
  717.             else:
  718.                 self._initialIndexRecordFound = True
  719.                 tbsType = 6
  720.                 tbSequence = decint(tbsType, DECINT_FORWARD)
  721.                 tbSequence += decint(0, DECINT_FORWARD)
  722.                 tbSequence += chr(self._HTMLRecords[nrecords].currentSectionNodeCount + 2)
  723.                 tbSequence += decint(len(tbSequence) + 1, DECINT_FORWARD)
  724.                 if self.opts.verbose > 2:
  725.                     self._oeb.logger.info('\nAssembling TBS for Flat Periodical: HTML record %03d of %03d, section %d' % (nrecords, lastrecord, self._HTMLRecords[nrecords].continuingNodeParent))
  726.                     self._HTMLRecords[nrecords].dumpData(nrecords, self._oeb)
  727.                 
  728.         elif self.opts.verbose > 2:
  729.             self._oeb.logger.info('\nAssembling TBS for Flat Periodical: HTML record %03d of %03d, section %d' % (nrecords, lastrecord, self._HTMLRecords[nrecords].continuingNodeParent))
  730.             self._HTMLRecords[nrecords].dumpData(nrecords, self._oeb)
  731.         
  732.         if nrecords == lastrecord and self._HTMLRecords[nrecords].currentSectionNodeCount == 1:
  733.             tbsType = 6
  734.             tbSequence = decint(tbsType, DECINT_FORWARD)
  735.             tbSequence += decint(0, DECINT_FORWARD)
  736.             tbSequence += chr(2)
  737.             arg3 = self._HTMLRecords[nrecords].continuingNode
  738.             arg3 += 1
  739.             arg3 <<= 4
  740.             arg3 |= 0
  741.             tbSequence += decint(arg3, DECINT_FORWARD)
  742.             tbSequence += decint(len(tbSequence) + 1, DECINT_FORWARD)
  743.         elif self._HTMLRecords[nrecords].continuingNode > 0 and self._HTMLRecords[nrecords].openingNode == -1:
  744.             tbsType = 6
  745.             self._HTMLRecords[nrecords].currentSectionNodeCount = 128
  746.             tbSequence = decint(tbsType, DECINT_FORWARD)
  747.             tbSequence += decint(0, DECINT_FORWARD)
  748.             tbSequence += chr(2)
  749.             arg3 = self._HTMLRecords[nrecords].continuingNode
  750.             arg3 += self._HTMLRecords[nrecords].continuingNodeParent + 1
  751.             arg3 <<= 4
  752.             arg3 |= 1
  753.             tbSequence += decint(arg3, DECINT_FORWARD)
  754.             tbSequence += chr(self._HTMLRecords[nrecords].currentSectionNodeCount)
  755.             tbSequence += decint(len(tbSequence) + 1, DECINT_FORWARD)
  756.         else:
  757.             tbsType = 7
  758.             tbSequence = decint(tbsType, DECINT_FORWARD)
  759.             tbSequence += decint(0, DECINT_FORWARD)
  760.             tbSequence += chr(2)
  761.             tbSequence += decint(0, DECINT_FORWARD)
  762.             arg4 = self._HTMLRecords[nrecords].continuingNode
  763.             arg4 += self._HTMLRecords[nrecords].continuingNodeParent + 1
  764.             arg4 <<= 4
  765.             arg4 |= 4
  766.             tbSequence += decint(arg4, DECINT_FORWARD)
  767.             tbSequence += chr(self._HTMLRecords[nrecords].currentSectionNodeCount)
  768.             tbSequence += decint(len(tbSequence) + 1, DECINT_FORWARD)
  769.         self._tbSequence = tbSequence
  770.  
  771.     
  772.     def _generate_tbs_structured_periodical(self, nrecords, lastrecord):
  773.         tbsType = 0
  774.         tbSequence = ''
  775.         if self._initialIndexRecordFound == False:
  776.             if self._HTMLRecords[nrecords].currentSectionNodeCount == -1:
  777.                 tbSequence = decint(len(tbSequence) + 1, DECINT_FORWARD)
  778.             else:
  779.                 self._initialIndexRecordFound = True
  780.                 if self.opts.verbose > 2:
  781.                     self._oeb.logger.info('\nAssembling TBS for Structured Periodical: HTML record %03d of %03d, section %d' % (nrecords, lastrecord, self._HTMLRecords[nrecords].continuingNodeParent))
  782.                     self._HTMLRecords[nrecords].dumpData(nrecords, self._oeb)
  783.                 
  784.                 tbsType = 6
  785.                 tbSequence = decint(tbsType, DECINT_FORWARD)
  786.                 tbSequence += decint(0, DECINT_FORWARD)
  787.                 tbSequence += chr(2)
  788.                 arg3 = self._sectionCount
  789.                 arg3 += 0
  790.                 arg3 <<= 4
  791.                 arg3 |= 4
  792.                 tbSequence += decint(arg3, DECINT_FORWARD)
  793.                 tbSequence += chr(self._HTMLRecords[nrecords].currentSectionNodeCount)
  794.                 tbSequence += decint(len(tbSequence) + 1, DECINT_FORWARD)
  795.         elif self._firstSectionConcluded == False:
  796.             if self._HTMLRecords[nrecords].nextSectionNumber == -1:
  797.                 if self.opts.verbose > 2:
  798.                     self._oeb.logger.info('\nAssembling TBS for Structured Periodical: HTML record %03d of %03d, section %d' % (nrecords, lastrecord, self._HTMLRecords[nrecords].continuingNodeParent))
  799.                     self._HTMLRecords[nrecords].dumpData(nrecords, self._oeb)
  800.                 
  801.                 if nrecords == lastrecord and self._HTMLRecords[nrecords].currentSectionNodeCount == 1:
  802.                     tbsType = 6
  803.                     tbSequence = decint(tbsType, DECINT_FORWARD)
  804.                     tbSequence += decint(0, DECINT_FORWARD)
  805.                     tbSequence += chr(2)
  806.                     arg3 = self._sectionCount
  807.                     arg3 += self._HTMLRecords[nrecords].continuingNode
  808.                     arg3 <<= 4
  809.                     arg3 |= 4
  810.                     tbSequence += decint(arg3, DECINT_FORWARD)
  811.                     tbSequence += chr(self._HTMLRecords[nrecords].currentSectionNodeCount)
  812.                     tbSequence += decint(len(tbSequence) + 1, DECINT_FORWARD)
  813.                 elif self._HTMLRecords[nrecords].continuingNode > 0 and self._HTMLRecords[nrecords].openingNode == -1:
  814.                     tbsType = 6
  815.                     self._HTMLRecords[nrecords].currentSectionNodeCount = 128
  816.                     tbSequence = decint(tbsType, DECINT_FORWARD)
  817.                     tbSequence += decint(0, DECINT_FORWARD)
  818.                     tbSequence += chr(2)
  819.                     arg3 = self._sectionCount
  820.                     arg3 += self._HTMLRecords[nrecords].continuingNode
  821.                     arg3 <<= 4
  822.                     arg3 |= 1
  823.                     tbSequence += decint(arg3, DECINT_FORWARD)
  824.                     tbSequence += chr(self._HTMLRecords[nrecords].currentSectionNodeCount)
  825.                     tbSequence += decint(len(tbSequence) + 1, DECINT_FORWARD)
  826.                 else:
  827.                     tbsType = 7
  828.                     tbSequence = decint(tbsType, DECINT_FORWARD)
  829.                     tbSequence += decint(0, DECINT_FORWARD)
  830.                     tbSequence += chr(2)
  831.                     tbSequence += decint(0, DECINT_FORWARD)
  832.                     arg4 = self._sectionCount
  833.                     arg4 += self._HTMLRecords[nrecords].continuingNode
  834.                     arg4 <<= 4
  835.                     arg4 |= 4
  836.                     tbSequence += decint(arg4, DECINT_FORWARD)
  837.                     tbSequence += chr(self._HTMLRecords[nrecords].currentSectionNodeCount)
  838.                     tbSequence += decint(len(tbSequence) + 1, DECINT_FORWARD)
  839.             elif self._HTMLRecords[nrecords].nextSectionNumber > 0:
  840.                 tbsType = 3
  841.                 if self.opts.verbose > 2:
  842.                     self._oeb.logger.info('\nAssembling TBS for Structured Periodical: HTML record %03d of %03d, switching sections %d-%d' % (nrecords, lastrecord, self._HTMLRecords[nrecords].continuingNodeParent, self._HTMLRecords[nrecords].nextSectionNumber))
  843.                     self._HTMLRecords[nrecords].dumpData(nrecords, self._oeb)
  844.                 
  845.                 tbSequence = decint(tbsType, DECINT_FORWARD)
  846.                 tbSequence += decint(0, DECINT_FORWARD)
  847.                 tbSequence += decint(0, DECINT_FORWARD)
  848.                 arg3 = self._HTMLRecords[nrecords].continuingNodeParent + 1 << 4
  849.                 arg3Flags = 0
  850.                 arg3 |= arg3Flags
  851.                 tbSequence += decint(arg3, DECINT_FORWARD)
  852.                 sectionBase = self._HTMLRecords[nrecords].continuingNodeParent
  853.                 sectionDelta = self._sectionCount - sectionBase - 1
  854.                 articleOffset = self._HTMLRecords[nrecords].continuingNode + 1
  855.                 arg4 = sectionDelta + articleOffset << 4
  856.                 arg4Flags = 0
  857.                 if self._HTMLRecords[nrecords].currentSectionNodeCount > 1:
  858.                     arg4Flags = 4
  859.                 else:
  860.                     arg4Flags = 0
  861.                 arg4 |= arg4Flags
  862.                 tbSequence += decint(arg4, DECINT_FORWARD)
  863.                 if arg4Flags == 4:
  864.                     nodeCountValue = self._HTMLRecords[nrecords].currentSectionNodeCount
  865.                     nodeCountValue = None if nodeCountValue == 0 else nodeCountValue
  866.                     tbSequence += chr(nodeCountValue)
  867.                 
  868.                 arg5 = sectionDelta + articleOffset
  869.                 if self._HTMLRecords[nrecords].currentSectionNodeCount < 2:
  870.                     arg5 -= 1
  871.                 
  872.                 arg5 <<= 4
  873.                 arg5Flags = 8
  874.                 arg5 |= arg5Flags
  875.                 tbSequence += decint(arg5, DECINT_FORWARD)
  876.                 arg6 = sectionDelta + self._HTMLRecords[nrecords].nextSectionOpeningNode
  877.                 arg6 <<= 4
  878.                 if self._HTMLRecords[nrecords].nextSectionNodeCount > 1:
  879.                     arg6Flags = 4
  880.                 else:
  881.                     arg6Flags = 0
  882.                 arg6 |= arg6Flags
  883.                 tbSequence += decint(arg6, DECINT_FORWARD)
  884.                 if arg6Flags == 4:
  885.                     nodeCountValue = self._HTMLRecords[nrecords].nextSectionNodeCount
  886.                     nodeCountValue = None if nodeCountValue == 0 else nodeCountValue
  887.                     tbSequence += chr(nodeCountValue)
  888.                 
  889.                 tbSequence += decint(len(tbSequence) + 1, DECINT_FORWARD)
  890.                 self._firstSectionConcluded = True
  891.             
  892.         elif self._HTMLRecords[nrecords].nextSectionNumber == -1:
  893.             if self.opts.verbose > 2:
  894.                 self._oeb.logger.info('\nAssembling TBS for Structured Periodical: HTML record %03d of %03d, section %d' % (nrecords, lastrecord, self._HTMLRecords[nrecords].continuingNodeParent))
  895.                 self._HTMLRecords[nrecords].dumpData(nrecords, self._oeb)
  896.             
  897.             tbsType = 2
  898.             tbSequence = decint(tbsType, DECINT_FORWARD)
  899.             tbSequence += decint(0, DECINT_FORWARD)
  900.             arg2 = self._HTMLRecords[nrecords].continuingNodeParent + 1
  901.             arg2 <<= 4
  902.             arg2Flags = 0
  903.             if self._HTMLRecords[nrecords].currentSectionNodeCount > 0:
  904.                 arg2Flags = 1
  905.                 arg2 |= arg2Flags
  906.             
  907.             tbSequence += decint(arg2, DECINT_FORWARD)
  908.             if arg2Flags:
  909.                 tbSequence += decint(0, DECINT_FORWARD)
  910.             
  911.             arg3 = self._sectionCount - self._HTMLRecords[nrecords].continuingNodeParent
  912.             arg3 += self._HTMLRecords[nrecords].continuingNode
  913.             arg3 <<= 4
  914.             arg3Flags = 1
  915.             if self._HTMLRecords[nrecords].currentSectionNodeCount > 0:
  916.                 arg3Flags = 4
  917.             
  918.             arg3 |= arg3Flags
  919.             tbSequence += decint(arg3, DECINT_FORWARD)
  920.             if arg3Flags == 4:
  921.                 nodeCountValue = self._HTMLRecords[nrecords].currentSectionNodeCount
  922.                 nodeCountValue = None if nodeCountValue == 0 else nodeCountValue
  923.                 tbSequence += chr(nodeCountValue)
  924.             else:
  925.                 tbSequence += decint(0, DECINT_FORWARD)
  926.             tbSequence += decint(len(tbSequence) + 1, DECINT_FORWARD)
  927.         else:
  928.             tbsType = 3
  929.             if self.opts.verbose > 2:
  930.                 self._oeb.logger.info('\nAssembling TBS for Structured Periodical: HTML record %03d of %03d, switching sections %d-%d' % (nrecords, lastrecord, self._HTMLRecords[nrecords].continuingNodeParent, self._HTMLRecords[nrecords].nextSectionNumber))
  931.                 self._HTMLRecords[nrecords].dumpData(nrecords, self._oeb)
  932.             
  933.             tbSequence = decint(tbsType, DECINT_FORWARD)
  934.             tbSequence += decint(0, DECINT_FORWARD)
  935.             tbSequence += decint(0, DECINT_FORWARD)
  936.             arg3 = self._HTMLRecords[nrecords].continuingNodeParent + 1 << 4
  937.             arg3Flags = 0
  938.             arg3 |= arg3Flags
  939.             tbSequence += decint(arg3, DECINT_FORWARD)
  940.             sectionBase = self._HTMLRecords[nrecords].continuingNodeParent
  941.             sectionDelta = self._sectionCount - sectionBase - 1
  942.             articleOffset = self._HTMLRecords[nrecords].continuingNode + 1
  943.             arg4 = sectionDelta + articleOffset << 4
  944.             arg4Flags = 0
  945.             if self._HTMLRecords[nrecords].currentSectionNodeCount > 1:
  946.                 arg4Flags = 4
  947.             else:
  948.                 arg4Flags = 0
  949.             arg4 |= arg4Flags
  950.             tbSequence += decint(arg4, DECINT_FORWARD)
  951.             if arg4Flags == 4:
  952.                 nodeCountValue = self._HTMLRecords[nrecords].currentSectionNodeCount
  953.                 nodeCountValue = None if nodeCountValue == 0 else nodeCountValue
  954.                 tbSequence += chr(nodeCountValue)
  955.             
  956.             arg5 = sectionDelta + articleOffset
  957.             if self._HTMLRecords[nrecords].currentSectionNodeCount < 2:
  958.                 arg5 -= 1
  959.             
  960.             arg5 <<= 4
  961.             arg5Flags = 8
  962.             arg5 |= arg5Flags
  963.             tbSequence += decint(arg5, DECINT_FORWARD)
  964.             arg6 = sectionDelta + self._HTMLRecords[nrecords].nextSectionOpeningNode
  965.             arg6 <<= 4
  966.             if self._HTMLRecords[nrecords].nextSectionNodeCount > 1:
  967.                 arg6Flags = 4
  968.             else:
  969.                 arg6Flags = 0
  970.             arg6 |= arg6Flags
  971.             tbSequence += decint(arg6, DECINT_FORWARD)
  972.             if arg6Flags == 4:
  973.                 nodeCountValue = self._HTMLRecords[nrecords].nextSectionNodeCount
  974.                 nodeCountValue = None if nodeCountValue == 0 else nodeCountValue
  975.                 tbSequence += chr(nodeCountValue)
  976.             
  977.             tbSequence += decint(len(tbSequence) + 1, DECINT_FORWARD)
  978.         self._tbSequence = tbSequence
  979.  
  980.     
  981.     def _evaluate_periodical_toc(self):
  982.         toc = self._oeb.toc
  983.         nodes = list(toc.iter())[1:]
  984.         toc_conforms = True
  985.         for i, child in enumerate(nodes):
  986.             if not child.klass == 'periodical' or child.depth() != 3:
  987.                 if (child.klass == 'section' or child.depth() != 2 or child.klass == 'article') and child.depth() != 1:
  988.                     self._oeb.logger.warn('Nonconforming TOC entry: "%s" found at depth %d' % (child.klass, child.depth()))
  989.                     self._oeb.logger.warn("  <title>: '%-25.25s...' \t\tklass=%-15.15s \tdepth:%d  \tplayOrder=%03d" % (child.title, child.klass, child.depth(), child.play_order))
  990.                     toc_conforms = False
  991.                     continue
  992.         
  993.         if self._oeb.metadata['date'] == [] and self._oeb.metadata['timestamp'] == []:
  994.             self._oeb.logger.info('metadata missing date/timestamp')
  995.             toc_conforms = False
  996.         
  997.         if 'masthead' not in self._oeb.guide:
  998.             self._oeb.logger.info('mastheadImage missing from manifest')
  999.             toc_conforms = False
  1000.         
  1001.         None(self._oeb.logger.info if toc_conforms else '  TOC structure non-conforming')
  1002.         return toc_conforms
  1003.  
  1004.     
  1005.     def _generate_text(self):
  1006.         self._oeb.logger.info('Serializing markup content...')
  1007.         serializer = Serializer(self._oeb, self._images, write_page_breaks_after_item = self.write_page_breaks_after_item)
  1008.         breaks = serializer.breaks
  1009.         text = serializer.text
  1010.         self._anchor_offset_kindle = serializer.anchor_offset_kindle
  1011.         self._id_offsets = serializer.id_offsets
  1012.         self._content_length = len(text)
  1013.         self._text_length = len(text)
  1014.         text = StringIO(text)
  1015.         buf = []
  1016.         nrecords = 0
  1017.         lastrecord = self._content_length // RECORD_SIZE
  1018.         offset = 0
  1019.         if self._compression != UNCOMPRESSED:
  1020.             self._oeb.logger.info('  Compressing markup content...')
  1021.         
  1022.         (data, overlap) = self._read_text_record(text)
  1023.         if self.opts.mobi_periodical:
  1024.             self._oeb.logger.info('  MOBI periodical specified, evaluating TOC for periodical conformance ...')
  1025.             self._conforming_periodical_toc = self._evaluate_periodical_toc()
  1026.         
  1027.         self._ctoc_records.append(self._generate_ctoc())
  1028.         toc = self._oeb.toc
  1029.         entries = list(toc.iter())[1:]
  1030.         if len(entries):
  1031.             self._indexable = self._generate_indexed_navpoints()
  1032.         else:
  1033.             self._oeb.logger.info('  No entries found in TOC ...')
  1034.             self._indexable = False
  1035.         if not self._indexable:
  1036.             self._oeb.logger.info('  Writing unindexed mobi ...')
  1037.         
  1038.         while len(data) > 0:
  1039.             if self._compression == PALMDOC:
  1040.                 data = compress_doc(data)
  1041.             
  1042.             record = StringIO()
  1043.             record.write(data)
  1044.             if WRITE_PBREAKS:
  1045.                 record.write(overlap)
  1046.                 record.write(pack('>B', len(overlap)))
  1047.                 nextra = 0
  1048.                 pbreak = 0
  1049.                 running = offset
  1050.                 while breaks and breaks[0] - offset < RECORD_SIZE:
  1051.                     pbreak = breaks.pop(0) - running >> 3
  1052.                     if self.opts.verbose > 2:
  1053.                         self._oeb.logger.info('pbreak = 0x%X at 0x%X' % (pbreak, record.tell()))
  1054.                     
  1055.                     encoded = decint(pbreak, DECINT_FORWARD)
  1056.                     record.write(encoded)
  1057.                     running += pbreak << 3
  1058.                     nextra += len(encoded)
  1059.                 lsize = 1
  1060.                 while True:
  1061.                     size = decint(nextra + lsize, DECINT_BACKWARD)
  1062.                     if len(size) == lsize:
  1063.                         break
  1064.                     
  1065.                     lsize += 1
  1066.                 record.write(size)
  1067.             
  1068.             if INDEXING and self._indexable:
  1069.                 booktype = self._MobiDoc.mobiType
  1070.                 if booktype == 2:
  1071.                     self._generate_tbs_book(nrecords, lastrecord)
  1072.                 elif booktype == 258:
  1073.                     self._generate_tbs_flat_periodical(nrecords, lastrecord)
  1074.                 elif booktype == 257 or booktype == 259:
  1075.                     self._generate_tbs_structured_periodical(nrecords, lastrecord)
  1076.                 else:
  1077.                     raise NotImplementedError('Indexing for mobitype 0x%X not implemented' % booktype)
  1078.                 (booktype == 259).write(self._tbSequence)
  1079.             
  1080.             self._records.append(record.getvalue())
  1081.             buf.append(self._records[-1])
  1082.             nrecords += 1
  1083.             offset += RECORD_SIZE
  1084.             (data, overlap) = self._read_text_record(text)
  1085.         if INDEXING:
  1086.             extra = sum(map(len, buf)) % 4
  1087.             if extra == 0:
  1088.                 extra = 4
  1089.             
  1090.             self._records.append('\x00' * (4 - extra))
  1091.             nrecords += 1
  1092.         
  1093.         self._text_nrecords = nrecords
  1094.  
  1095.     
  1096.     def _generate_images(self):
  1097.         self._oeb.logger.info('Serializing images...')
  1098.         images = [ (index, href) for href, index in self._images.items() ]
  1099.         images.sort()
  1100.         self._first_image_record = None
  1101.         for _, href in images:
  1102.             item = self._oeb.manifest.hrefs[href]
  1103.             
  1104.             try:
  1105.                 data = rescale_image(item.data, self._imagemax)
  1106.             except:
  1107.                 []
  1108.                 []
  1109.                 self._oeb.logger.warn('Bad image file %r' % item.href)
  1110.                 continue
  1111.  
  1112.             self._records.append(data)
  1113.             if self._first_image_record is None:
  1114.                 self._first_image_record = len(self._records) - 1
  1115.                 continue
  1116.             []
  1117.         
  1118.  
  1119.     
  1120.     def _generate_end_records(self):
  1121.         if FCIS_FLIS:
  1122.             self._flis_number = len(self._records)
  1123.             self._records.append('FLIS\x00\x00\x00\x08\x00A\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x01\x00\x03\x00\x00\x00\x03\x00\x00\x00\x01' + '\xff\xff\xff\xff')
  1124.             fcis = 'FCIS\x00\x00\x00\x14\x00\x00\x00\x10\x00\x00\x00\x01\x00\x00\x00\x00'
  1125.             fcis += pack('>I', self._text_length)
  1126.             fcis += '\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x08\x00\x01\x00\x01\x00\x00\x00\x00'
  1127.             self._fcis_number = len(self._records)
  1128.             self._records.append(fcis)
  1129.             self._records.append('\xe9\x8e\r\n')
  1130.         else:
  1131.             self._flis_number = len(self._records)
  1132.             self._records.append('\xe9\x8e\r\n')
  1133.  
  1134.     
  1135.     def _generate_record0(self):
  1136.         metadata = self._oeb.metadata
  1137.         exth = self._build_exth()
  1138.         last_content_record = len(self._records) - 1
  1139.         self._generate_end_records()
  1140.         record0 = StringIO()
  1141.         record0.write(pack('>HHIHHHH', self._compression, 0, self._text_length, self._text_nrecords - 1, RECORD_SIZE, 0, 0))
  1142.         uid = random.randint(0, 0xFFFFFFFFL)
  1143.         title = unicode(metadata.title[0]).encode('utf-8')
  1144.         record0.write('MOBI')
  1145.         btype = self._MobiDoc.mobiType
  1146.         record0.write(pack('>IIIII', 232, btype, 65001, uid, 6))
  1147.         record0.write('\xff\xff\xff\xff\xff\xff\xff\xff')
  1148.         if btype < 256:
  1149.             record0.write(pack('>I', 0xFFFFFFFFL))
  1150.         elif btype > 256 and self._indexable:
  1151.             if self._primary_index_record is None:
  1152.                 record0.write(pack('>I', 0xFFFFFFFFL))
  1153.             else:
  1154.                 record0.write(pack('>I', self._primary_index_record + 2 + len(self._ctoc_records)))
  1155.         else:
  1156.             record0.write(pack('>I', 0xFFFFFFFFL))
  1157.         record0.write('\xff' * 28)
  1158.         record0.write(pack('>I', self._text_nrecords + 1))
  1159.         record0.write(pack('>II', 248 + len(exth), len(title)))
  1160.         record0.write(iana2mobi(str(metadata.language[0])))
  1161.         record0.write('\x00\x00\x00\x00\x00\x00\x00\x00')
  1162.         None(record0.write(pack, '>II', 6 if self._first_image_record else 0))
  1163.         record0.write('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
  1164.         record0.write(pack('>I', 80))
  1165.         record0.write('\x00' * 32)
  1166.         record0.write(pack('>IIII', 0xFFFFFFFFL, 0xFFFFFFFFL, 0, 0))
  1167.         record0.write('\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
  1168.         record0.write(pack('>HH', 1, last_content_record))
  1169.         record0.write('\x00\x00\x00\x01')
  1170.         if FCIS_FLIS:
  1171.             record0.write(pack('>I', self._fcis_number))
  1172.             record0.write(pack('>I', 1))
  1173.             record0.write(pack('>I', self._flis_number))
  1174.             record0.write(pack('>I', 1))
  1175.         else:
  1176.             record0.write(pack('>I', 0xFFFFFFFFL))
  1177.             record0.write(pack('>I', 0xFFFFFFFFL))
  1178.             record0.write(pack('>I', 0xFFFFFFFFL))
  1179.             record0.write(pack('>I', 1))
  1180.         record0.write('\x00\x00\x00\x00\x00\x00\x00\x00')
  1181.         record0.write(pack('>IIII', 0xFFFFFFFFL, 0, 0xFFFFFFFFL, 0xFFFFFFFFL))
  1182.         trailingDataFlags = 1
  1183.         if self._indexable:
  1184.             trailingDataFlags |= 2
  1185.         
  1186.         if WRITE_PBREAKS:
  1187.             trailingDataFlags |= 4
  1188.         
  1189.         record0.write(pack('>I', trailingDataFlags))
  1190.         None(record0.write(pack, '>I' if self._primary_index_record is None else self._primary_index_record))
  1191.         record0.write(exth)
  1192.         record0.write(title)
  1193.         record0 = record0.getvalue()
  1194.         self._records[0] = record0 + '\x00' * (2452 - len(record0))
  1195.  
  1196.     
  1197.     def _build_exth(self):
  1198.         oeb = self._oeb
  1199.         exth = StringIO()
  1200.         nrecs = 0
  1201.         for term in oeb.metadata:
  1202.             if term not in EXTH_CODES:
  1203.                 continue
  1204.             
  1205.             code = EXTH_CODES[term]
  1206.             items = oeb.metadata[term]
  1207.             for item in items:
  1208.                 data = self.COLLAPSE_RE.sub(' ', unicode(item))
  1209.                 data = data.encode('utf-8')
  1210.                 exth.write(pack('>II', code, len(data) + 8))
  1211.                 exth.write(data)
  1212.                 nrecs += 1
  1213.             
  1214.             if term == 'rights':
  1215.                 rights = unicode(oeb.metadata.rights[0]).encode('utf-8')
  1216.                 exth.write(pack('>II', EXTH_CODES['rights'], len(rights) + 8))
  1217.                 exth.write(rights)
  1218.                 continue
  1219.             None if term == 'identifier' else None if term == 'creator' else []
  1220.         
  1221.         if oeb.metadata['date'] != []:
  1222.             datestr = str(oeb.metadata['date'][0])
  1223.         elif oeb.metadata['timestamp'] != []:
  1224.             datestr = str(oeb.metadata['timestamp'][0])
  1225.         
  1226.         if datestr is not None:
  1227.             exth.write(pack('>II', EXTH_CODES['pubdate'], len(datestr) + 8))
  1228.             exth.write(datestr)
  1229.             nrecs += 1
  1230.         else:
  1231.             raise NotImplementedError('missing date or timestamp needed for mobi_periodical')
  1232.         if (datestr is not None).metadata.cover and unicode(oeb.metadata.cover[0]) in oeb.manifest.ids:
  1233.             id = unicode(oeb.metadata.cover[0])
  1234.             item = oeb.manifest.ids[id]
  1235.             href = item.href
  1236.             if href in self._images:
  1237.                 index = self._images[href] - 1
  1238.                 exth.write(pack('>III', 201, 12, index))
  1239.                 exth.write(pack('>III', 203, 12, 0))
  1240.                 nrecs += 2
  1241.                 index = self._add_thumbnail(item)
  1242.                 if index is not None:
  1243.                     exth.write(pack('>III', 202, 12, index - 1))
  1244.                     nrecs += 1
  1245.                 
  1246.             
  1247.         
  1248.         exth = exth.getvalue()
  1249.         trail = len(exth) % 4
  1250.         pad = '\x00' * (4 - trail)
  1251.         exth = [
  1252.             'EXTH',
  1253.             pack('>II', len(exth) + 12, nrecs),
  1254.             exth,
  1255.             pad]
  1256.         return ''.join(exth)
  1257.  
  1258.     
  1259.     def _add_thumbnail(self, item):
  1260.         
  1261.         try:
  1262.             data = rescale_image(item.data, MAX_THUMB_SIZE, MAX_THUMB_DIMEN)
  1263.         except IOError:
  1264.             self._oeb.logger.warn('Bad image file %r' % item.href)
  1265.             return None
  1266.  
  1267.         manifest = self._oeb.manifest
  1268.         (id, href) = manifest.generate('thumbnail', 'thumbnail.jpeg')
  1269.         manifest.add(id, href, 'image/jpeg', data = data)
  1270.         index = len(self._images) + 1
  1271.         self._images[href] = index
  1272.         self._records.append(data)
  1273.         return index
  1274.  
  1275.     
  1276.     def _write_header(self):
  1277.         title = str(self._oeb.metadata.title[0])
  1278.         title = re.sub('[^-A-Za-z0-9]+', '_', title)[:31]
  1279.         title = title + '\x00' * (32 - len(title))
  1280.         now = int(time.time())
  1281.         nrecords = len(self._records)
  1282.         self._write(title, pack('>HHIIIIII', 0, 0, now, now, 0, 0, 0, 0), 'BOOK', 'MOBI', pack('>IIH', nrecords, 0, nrecords))
  1283.         offset = self._tell() + 8 * nrecords + 2
  1284.         for id, record in izip(count(), self._records):
  1285.             self._write(pack('>I', offset), '\x00', pack('>I', id)[1:])
  1286.             offset += len(record)
  1287.         
  1288.         self._write('\x00\x00')
  1289.  
  1290.     
  1291.     def _write_content(self):
  1292.         for record in self._records:
  1293.             self._write(record)
  1294.         
  1295.  
  1296.     
  1297.     def _generate_index(self):
  1298.         self._oeb.log('Generating INDX ...')
  1299.         self._primary_index_record = None
  1300.         (indxt, indxt_count, indices, last_name) = self._generate_indxt()
  1301.         if last_name is None:
  1302.             self._oeb.log.warn('Input document has no TOC. No index generated.')
  1303.             return None
  1304.         indx1 = StringIO()
  1305.         indx1.write('INDX' + pack('>I', 192))
  1306.         indx1.write('\x00\x00\x00\x00')
  1307.         indx1.write(pack('>I', 1))
  1308.         indx1.write('\x00\x00\x00\x00')
  1309.         indx1.write(pack('>I', 192 + len(indxt)))
  1310.         indx1.write(pack('>I', indxt_count + 1))
  1311.         indx1.write('\xff\xff\xff\xff\xff\xff\xff\xff')
  1312.         indx1.write('\x00' * 156)
  1313.         indx1.write(indxt)
  1314.         indx1.write(indices)
  1315.         indx1 = indx1.getvalue()
  1316.         idxt0 = chr(len(last_name)) + last_name + pack('>H', indxt_count + 1)
  1317.         idxt0 = align_block(idxt0)
  1318.         indx0 = StringIO()
  1319.         if self._MobiDoc.mobiType == 2:
  1320.             tagx = TAGX['chapter']
  1321.         else:
  1322.             tagx = TAGX['periodical']
  1323.         tagx = align_block('TAGX' + pack('>I', 8 + len(tagx)) + tagx)
  1324.         indx0_indices_pos = 192 + len(tagx) + len(idxt0)
  1325.         indx0_indices = align_block('IDXT' + pack('>H', 192 + len(tagx)))
  1326.         header = StringIO()
  1327.         header.write('INDX')
  1328.         header.write(pack('>I', 192))
  1329.         header.write('\x00\x00\x00\x00')
  1330.         header.write(pack('>I', 0))
  1331.         header.write(pack('>I', 6))
  1332.         header.write(pack('>I', indx0_indices_pos))
  1333.         header.write(pack('>I', 1))
  1334.         header.write(pack('>I', 65001))
  1335.         header.write(iana2mobi(str(self._oeb.metadata.language[0])))
  1336.         header.write(pack('>I', indxt_count + 1))
  1337.         header.write('\x00\x00\x00\x00')
  1338.         header.write('\x00\x00\x00\x00')
  1339.         header.write('\x00\x00\x00\x00')
  1340.         header.write(pack('>I', len(self._ctoc_records)))
  1341.         header.write('\x00' * 124)
  1342.         header.write(pack('>I', 192))
  1343.         header.write('\x00\x00\x00\x00\x00\x00\x00\x00')
  1344.         header = header.getvalue()
  1345.         indx0.write(header)
  1346.         indx0.write(tagx)
  1347.         indx0.write(idxt0)
  1348.         indx0.write(indx0_indices)
  1349.         indx0 = indx0.getvalue()
  1350.         self._primary_index_record = len(self._records)
  1351.         self._records.extend([
  1352.             indx0,
  1353.             indx1])
  1354.         for i, ctoc_record in enumerate(self._ctoc_records):
  1355.             self._records.append(ctoc_record)
  1356.         
  1357.         if self._MobiDoc.mobiType > 256:
  1358.             tagx = TAGX['secondary_periodical']
  1359.             tagx_len = 8 + len(tagx)
  1360.             indx0 = StringIO()
  1361.             indx0.write('INDX' + pack('>I', 192) + '\x00\x00\x00\x00\x00\x00\x00\x00')
  1362.             indx0.write(pack('>I', 6))
  1363.             indx0.write(pack('>I', 232))
  1364.             indx0.write(pack('>I', 1))
  1365.             indx0.write(pack('>I', 65001))
  1366.             indx0.write('\xff\xff\xff\xff')
  1367.             indx0.write(pack('>I', 4))
  1368.             indx0.write('\x00\x00\x00\x00')
  1369.             indx0.write('\x00' * 136)
  1370.             indx0.write(pack('>I', 192))
  1371.             indx0.write('\x00\x00\x00\x00\x00\x00\x00\x00')
  1372.             indx0.write('TAGX' + pack('>I', tagx_len) + tagx)
  1373.             indx0.write('\rmastheadImage\x00\x04')
  1374.             indx0.write('IDXT\x00\xd8\x00\x00')
  1375.             indx1 = StringIO()
  1376.             indx1.write('INDX' + pack('>I', 192) + '\x00\x00\x00\x00')
  1377.             indx1.write(pack('>I', 1))
  1378.             indx1.write(pack('>I', 0))
  1379.             indx1.write('\x00\x00\x00\xf0')
  1380.             indx1.write(pack('>I', 4))
  1381.             indx1.write('\xff\xff\xff\xff\xff\xff\xff\xff')
  1382.             indx1.write('\x00' * (192 - indx1.tell()))
  1383.             indx1.write('\x00\x01\x80')
  1384.             indx1.write('\x06author\x02\x80\x80\xc7')
  1385.             indx1.write('\x0bdescription\x02\x80\x80\xc6')
  1386.             indx1.write('\rmastheadImage\x02\x85\x80\xc5')
  1387.             indx1.write('IDXT\x00\xc0\x00\xc3\x00\xce\x00\xde')
  1388.             indx0 = indx0.getvalue()
  1389.             indx1 = indx1.getvalue()
  1390.             self._records.extend((indx0, indx1))
  1391.             if self.opts.verbose > 3:
  1392.                 mkdtemp = mkdtemp
  1393.                 import tempfile
  1394.                 import os
  1395.                 t = mkdtemp()
  1396.                 for i, n in enumerate([
  1397.                     'sindx1',
  1398.                     'sindx0',
  1399.                     'ctoc',
  1400.                     'indx0',
  1401.                     'indx1']):
  1402.                     open(os.path.join(t, n + '.bin'), 'wb').write(self._records[-(i + 1)])
  1403.                 
  1404.                 self._oeb.log.debug('Index records dumped to', t)
  1405.             
  1406.         
  1407.  
  1408.     
  1409.     def _clean_text_value(self, text):
  1410.         if text is not None and text.strip():
  1411.             text = text.strip()
  1412.             if not isinstance(text, unicode):
  1413.                 text = text.decode('utf-8', 'replace')
  1414.             
  1415.             text = text.encode('utf-8')
  1416.         else:
  1417.             text = '(none)'.encode('utf-8')
  1418.         return text
  1419.  
  1420.     
  1421.     def _add_to_ctoc(self, ctoc_str, record_offset):
  1422.         if 64504 - self._ctoc.tell() < 2 + len(ctoc_str):
  1423.             pad = 64504 - self._ctoc.tell()
  1424.             self._ctoc.write('\x00' * pad)
  1425.             self._ctoc_records.append(self._ctoc.getvalue())
  1426.             self._ctoc.truncate(0)
  1427.             self._ctoc_offset += 65536
  1428.             record_offset = self._ctoc_offset
  1429.         
  1430.         offset = self._ctoc.tell() + record_offset
  1431.         self._ctoc.write(decint(len(ctoc_str), DECINT_FORWARD) + ctoc_str)
  1432.         return offset
  1433.  
  1434.     
  1435.     def _add_flat_ctoc_node(self, node, ctoc, title = None):
  1436.         t = None if title is None else title
  1437.         t = self._clean_text_value(t)
  1438.         self._last_toc_entry = t
  1439.         ctoc_name_map = { }
  1440.         if node.klass == 'article':
  1441.             ctoc_name_map['klass'] = 'chapter'
  1442.         else:
  1443.             ctoc_name_map['klass'] = node.klass
  1444.         ctoc_name_map['titleOffset'] = self._add_to_ctoc(t, self._ctoc_offset)
  1445.         self._chapterCount += 1
  1446.         self._ctoc_map.append(ctoc_name_map)
  1447.  
  1448.     
  1449.     def _add_structured_ctoc_node(self, node, ctoc, title = None):
  1450.         if node.klass is None:
  1451.             return None
  1452.         t = node.klass is None if title is None else title
  1453.         t = self._clean_text_value(t)
  1454.         self._last_toc_entry = t
  1455.         ctoc_name_map = { }
  1456.         ctoc_name_map['klass'] = node.klass
  1457.         if node.klass == 'chapter':
  1458.             ctoc_name_map['titleOffset'] = self._add_to_ctoc(t, self._ctoc_offset)
  1459.             self._chapterCount += 1
  1460.         elif node.klass == 'periodical':
  1461.             ctoc_name_map['titleOffset'] = self._add_to_ctoc(t, self._ctoc_offset)
  1462.             for entry in self._ctoc_map:
  1463.                 if entry['klass'] == 'periodical':
  1464.                     ctoc_name_map['classOffset'] = entry['classOffset']
  1465.                     break
  1466.                     continue
  1467.             else:
  1468.                 ctoc_name_map['classOffset'] = self._add_to_ctoc(node.klass, 0)
  1469.             self._periodicalCount += 1
  1470.         elif node.klass == 'section':
  1471.             ctoc_name_map['titleOffset'] = self._add_to_ctoc(t, self._ctoc_offset)
  1472.             for entry in self._ctoc_map:
  1473.                 if entry['klass'] == 'section':
  1474.                     ctoc_name_map['classOffset'] = entry['classOffset']
  1475.                     break
  1476.                     continue
  1477.             else:
  1478.                 ctoc_name_map['classOffset'] = self._add_to_ctoc(node.klass, 0)
  1479.             self._sectionCount += 1
  1480.         elif node.klass == 'article':
  1481.             ctoc_name_map['titleOffset'] = self._add_to_ctoc(t, self._ctoc_offset)
  1482.             for entry in self._ctoc_map:
  1483.                 if entry['klass'] == 'article':
  1484.                     ctoc_name_map['classOffset'] = entry['classOffset']
  1485.                     break
  1486.                     continue
  1487.             else:
  1488.                 ctoc_name_map['classOffset'] = self._add_to_ctoc(node.klass, 0)
  1489.             if node.description:
  1490.                 d = self._clean_text_value(node.description)
  1491.                 ctoc_name_map['descriptionOffset'] = self._add_to_ctoc(d, self._ctoc_offset)
  1492.             else:
  1493.                 ctoc_name_map['descriptionOffset'] = None
  1494.             if node.author:
  1495.                 a = self._clean_text_value(node.author)
  1496.                 ctoc_name_map['authorOffset'] = self._add_to_ctoc(a, self._ctoc_offset)
  1497.             else:
  1498.                 ctoc_name_map['authorOffset'] = None
  1499.             self._articleCount += 1
  1500.         else:
  1501.             raise NotImplementedError('writer._generate_ctoc.add_node: title: %s has unrecognized klass: %s, playOrder: %d' % (node.title, node.klass, node.play_order))
  1502.         self._ctoc_map.append(ctoc_name_map)
  1503.  
  1504.     
  1505.     def _generate_ctoc(self):
  1506.         toc = self._oeb.toc
  1507.         reduced_toc = []
  1508.         self._ctoc_map = []
  1509.         self._last_toc_entry = None
  1510.         self._ctoc = StringIO()
  1511.         self._periodicalCount = 0
  1512.         self._sectionCount = 0
  1513.         self._articleCount = 0
  1514.         self._chapterCount = 0
  1515.         if self._conforming_periodical_toc:
  1516.             self._oeb.logger.info('Generating structured CTOC ...')
  1517.             for child in toc.iter():
  1518.                 if self.opts.verbose > 2:
  1519.                     self._oeb.logger.info('  %s' % child)
  1520.                 
  1521.                 self._add_structured_ctoc_node(child, self._ctoc)
  1522.             
  1523.         else:
  1524.             self._oeb.logger.info('Generating flat CTOC ...')
  1525.             previousOffset = -1
  1526.             currentOffset = 0
  1527.             for i, child in enumerate(toc.iterdescendants()):
  1528.                 if child.klass is None:
  1529.                     child.klass = 'chapter'
  1530.                 
  1531.                 if (child.klass == 'article' or child.klass == 'chapter') and child.depth() == 1:
  1532.                     if self.opts.verbose > 2:
  1533.                         self._oeb.logger.info('adding (klass:%s depth:%d) %s to flat ctoc' % (child.klass, child.depth(), child))
  1534.                     
  1535.                     h = child.href
  1536.                     if h is None:
  1537.                         self._oeb.logger.warn('  Ignoring TOC entry with no href:', child.title)
  1538.                         continue
  1539.                     
  1540.                     if h not in self._id_offsets:
  1541.                         self._oeb.logger.warn('  Ignoring missing TOC entry:', unicode(child))
  1542.                         continue
  1543.                     
  1544.                     currentOffset = self._id_offsets[h]
  1545.                     if currentOffset != previousOffset:
  1546.                         self._add_flat_ctoc_node(child, self._ctoc)
  1547.                         reduced_toc.append(child)
  1548.                         previousOffset = currentOffset
  1549.                     else:
  1550.                         self._oeb.logger.warn("  Ignoring redundant href: %s in '%s'" % (h, child.title))
  1551.                 currentOffset != previousOffset
  1552.                 if self.opts.verbose > 2:
  1553.                     self._oeb.logger.info('skipping class: %s depth %d at position %d' % (child.klass, child.depth(), i))
  1554.                     continue
  1555.             
  1556.             self._oeb.toc.nodes = reduced_toc
  1557.         if not (self._periodicalCount) and not (self._sectionCount) or not (self._articleCount) or not (self.opts.mobi_periodical):
  1558.             mobiType = 2
  1559.         elif self._periodicalCount:
  1560.             pt = None
  1561.             if self._oeb.metadata.publication_type:
  1562.                 x = unicode(self._oeb.metadata.publication_type[0]).split(':')
  1563.                 if len(x) > 1:
  1564.                     pt = x[1]
  1565.                 
  1566.             
  1567.             mobiType = {
  1568.                 'newspaper': 257 }.get(pt, 259)
  1569.         else:
  1570.             raise NotImplementedError('_generate_ctoc: Unrecognized document structured')
  1571.         self._MobiDoc = not (self.opts.mobi_periodical)(mobiType)
  1572.         if self.opts.verbose > 2:
  1573.             structType = 'book'
  1574.             if mobiType > 256:
  1575.                 structType = None if mobiType == 258 else 'structured periodical'
  1576.             
  1577.             self._oeb.logger.info('Instantiating a %s MobiDocument of type 0x%X' % (structType, mobiType))
  1578.             if mobiType > 256:
  1579.                 self._oeb.logger.info('periodicalCount: %d  sectionCount: %d  articleCount: %d' % (self._periodicalCount, self._sectionCount, self._articleCount))
  1580.             else:
  1581.                 self._oeb.logger.info('chapterCount: %d' % self._chapterCount)
  1582.         
  1583.         self._ctoc.write('\x00')
  1584.         ctoc = self._ctoc.getvalue()
  1585.         rec_count = len(self._ctoc_records)
  1586.         None(self._oeb.logger.info % ('  CNCX utilization: %d %s %.0f%% full', rec_count + 1 if rec_count else 'record,', len(ctoc) / 655))
  1587.         return align_block(ctoc)
  1588.  
  1589.     
  1590.     def _write_periodical_node(self, indxt, indices, index, offset, length, count, firstSection, lastSection):
  1591.         pos = 192 + indxt.tell()
  1592.         indices.write(pack('>H', pos))
  1593.         name = '%04X' % count
  1594.         indxt.write(chr(len(name)) + name)
  1595.         indxt.write(INDXT['periodical'])
  1596.         indxt.write(chr(1))
  1597.         indxt.write(decint(offset, DECINT_FORWARD))
  1598.         indxt.write(decint(length, DECINT_FORWARD))
  1599.         indxt.write(decint(self._ctoc_map[index]['titleOffset'], DECINT_FORWARD))
  1600.         indxt.write(decint(0, DECINT_FORWARD))
  1601.         indxt.write(decint(self._ctoc_map[index]['classOffset'], DECINT_FORWARD))
  1602.         indxt.write(decint(firstSection, DECINT_FORWARD))
  1603.         indxt.write(decint(lastSection, DECINT_FORWARD))
  1604.         indxt.write(decint(0, DECINT_FORWARD))
  1605.  
  1606.     
  1607.     def _write_section_node(self, indxt, indices, myCtocMapIndex, index, offset, length, count, firstArticle, lastArticle, parentIndex):
  1608.         pos = 192 + indxt.tell()
  1609.         indices.write(pack('>H', pos))
  1610.         name = '%04X' % count
  1611.         indxt.write(chr(len(name)) + name)
  1612.         indxt.write(INDXT['section'])
  1613.         indxt.write(chr(0))
  1614.         indxt.write(decint(offset, DECINT_FORWARD))
  1615.         indxt.write(decint(length, DECINT_FORWARD))
  1616.         indxt.write(decint(self._ctoc_map[myCtocMapIndex]['titleOffset'], DECINT_FORWARD))
  1617.         indxt.write(decint(1, DECINT_FORWARD))
  1618.         indxt.write(decint(self._ctoc_map[myCtocMapIndex]['classOffset'], DECINT_FORWARD))
  1619.         indxt.write(decint(parentIndex, DECINT_FORWARD))
  1620.         indxt.write(decint(firstArticle, DECINT_FORWARD))
  1621.         indxt.write(decint(lastArticle, DECINT_FORWARD))
  1622.  
  1623.     
  1624.     def _write_article_node(self, indxt, indices, index, offset, length, count, parentIndex):
  1625.         pos = 192 + indxt.tell()
  1626.         indices.write(pack('>H', pos))
  1627.         name = '%04X' % count
  1628.         indxt.write(chr(len(name)) + name)
  1629.         indxt.write(INDXT['article'])
  1630.         hasAuthor = None if self._ctoc_map[index]['authorOffset'] else False
  1631.         hasDescription = None if self._ctoc_map[index]['descriptionOffset'] else False
  1632.         flagBits = 0
  1633.         if hasAuthor:
  1634.             flagBits |= 4
  1635.         
  1636.         if hasDescription:
  1637.             flagBits |= 2
  1638.         
  1639.         indxt.write(pack('>B', flagBits))
  1640.         indxt.write(decint(offset, DECINT_FORWARD))
  1641.         indxt.write(decint(length, DECINT_FORWARD))
  1642.         indxt.write(decint(self._ctoc_map[index]['titleOffset'], DECINT_FORWARD))
  1643.         indxt.write(decint(2, DECINT_FORWARD))
  1644.         indxt.write(decint(self._ctoc_map[index]['classOffset'], DECINT_FORWARD))
  1645.         indxt.write(decint(parentIndex, DECINT_FORWARD))
  1646.         descriptionOffset = self._ctoc_map[index]['descriptionOffset']
  1647.         if descriptionOffset:
  1648.             indxt.write(decint(descriptionOffset, DECINT_FORWARD))
  1649.         
  1650.         authorOffset = self._ctoc_map[index]['authorOffset']
  1651.         if authorOffset:
  1652.             indxt.write(decint(authorOffset, DECINT_FORWARD))
  1653.         
  1654.  
  1655.     
  1656.     def _write_chapter_node(self, indxt, indices, index, offset, length, count):
  1657.         if self.opts.verbose > 2:
  1658.             pass
  1659.         
  1660.         pos = 192 + indxt.tell()
  1661.         indices.write(pack('>H', pos))
  1662.         name = '%04X' % count
  1663.         indxt.write(chr(len(name)) + name)
  1664.         indxt.write(INDXT['chapter'])
  1665.         indxt.write(decint(offset, DECINT_FORWARD))
  1666.         indxt.write(decint(length, DECINT_FORWARD))
  1667.         indxt.write(decint(self._ctoc_map[index]['titleOffset'], DECINT_FORWARD))
  1668.         indxt.write(decint(0, DECINT_FORWARD))
  1669.  
  1670.     
  1671.     def _compute_offset_length(self, i, node, entries):
  1672.         h = node.href
  1673.         if h not in self._id_offsets:
  1674.             self._oeb.log.warning('Could not find TOC entry:', node.title)
  1675.             return (-1, -1)
  1676.         offset = self._id_offsets[h]
  1677.         length = None
  1678.         for sibling in entries[i + 1:]:
  1679.             h2 = sibling.href
  1680.             if h2 in self._id_offsets:
  1681.                 offset2 = self._id_offsets[h2]
  1682.                 if offset2 > offset:
  1683.                     length = offset2 - offset
  1684.                     break
  1685.                 
  1686.             offset2 > offset
  1687.         
  1688.         if length is None:
  1689.             length = self._content_length - offset
  1690.         
  1691.         return (offset, length)
  1692.  
  1693.     
  1694.     def _establish_document_structure(self):
  1695.         documentType = None
  1696.         
  1697.         try:
  1698.             klass = self._ctoc_map[0]['klass']
  1699.         except:
  1700.             klass = None
  1701.  
  1702.         if klass == 'chapter' or klass == None:
  1703.             documentType = 'book'
  1704.             if self.opts.verbose > 2:
  1705.                 self._oeb.logger.info('Adding a MobiBook to self._MobiDoc')
  1706.             
  1707.             self._MobiDoc.documentStructure = MobiBook()
  1708.         elif klass == 'periodical':
  1709.             documentType = klass
  1710.             if self.opts.verbose > 2:
  1711.                 self._oeb.logger.info('Adding a MobiPeriodical to self._MobiDoc')
  1712.             
  1713.             self._MobiDoc.documentStructure = MobiPeriodical(self._MobiDoc.getNextNode())
  1714.             self._MobiDoc.documentStructure.startAddress = self._anchor_offset_kindle
  1715.         else:
  1716.             raise NotImplementedError('_establish_document_structure: unrecognized klass: %s' % klass)
  1717.         return klass == None
  1718.  
  1719.     
  1720.     def _generate_section_indices(self, child, currentSection, myPeriodical, myDoc):
  1721.         sectionTitles = list(child.iter())[1:]
  1722.         sectionIndices = []
  1723.         sectionParents = []
  1724.         for j, section in enumerate(sectionTitles):
  1725.             if section.klass == 'periodical':
  1726.                 sectionIndices.append(currentSection)
  1727.                 if self.opts.verbose > 3:
  1728.                     self._oeb.logger.info('Periodical: %15.15s \tkls:%s \tdpt:%d  ply:%03d' % (section.title, section.klass, section.depth(), section.play_order))
  1729.                 
  1730.             self.opts.verbose > 3
  1731.             if section.klass == 'section':
  1732.                 myNewSection = myPeriodical.addSectionParent(myDoc, j)
  1733.                 sectionParents.append(myNewSection)
  1734.                 currentSection += 1
  1735.                 sectionIndices.append(currentSection)
  1736.                 if self.opts.verbose > 3:
  1737.                     self._oeb.logger.info('   Section: %15.15s \tkls:%s \tdpt:%d  ply:%03d \tindex:%d' % (section.title, section.klass, section.depth(), section.play_order, j))
  1738.                 
  1739.             self.opts.verbose > 3
  1740.             if section.klass == 'article':
  1741.                 sectionIndices.append(currentSection)
  1742.                 continue
  1743.             if self.opts.verbose > 3:
  1744.                 self._oeb.logger.info(' Unrecognized class %s in structured document' % section.klass)
  1745.                 continue
  1746.         
  1747.         return (sectionIndices, sectionParents)
  1748.  
  1749.     
  1750.     def _generate_section_article_indices(self, i, section, entries, sectionIndices, sectionParents):
  1751.         sectionArticles = list(section.iter())[1:]
  1752.         for j, article in enumerate(sectionArticles):
  1753.             (offset, length) = self._compute_offset_length(i, article, entries)
  1754.             if self.opts.verbose > 2:
  1755.                 self._oeb.logger.info('article %02d: offset = 0x%06X length = 0x%06X' % (j, offset, length))
  1756.             
  1757.             ctoc_map_index = i + j + 1
  1758.             mySectionParent = sectionParents[sectionIndices[i - 1]]
  1759.             myNewArticle = MobiArticle(mySectionParent, offset, length, ctoc_map_index)
  1760.             mySectionParent.addArticle(myNewArticle)
  1761.         
  1762.  
  1763.     
  1764.     def _add_book_chapters(self, myDoc, indxt, indices):
  1765.         chapterCount = myDoc.documentStructure.chapterCount()
  1766.         if self.opts.verbose > 3:
  1767.             self._oeb.logger.info('Writing %d chapters for mobitype 0x%03X' % (chapterCount, myDoc.mobiType))
  1768.         
  1769.         for c, chapter in enumerate(list(myDoc.documentStructure.chapters)):
  1770.             index = chapter.myCtocMapIndex
  1771.             self._write_chapter_node(indxt, indices, index, chapter.startAddress, chapter.length, c)
  1772.             last_name = '%04X' % c
  1773.         
  1774.         return (last_name, c)
  1775.  
  1776.     
  1777.     def _add_periodical_flat_articles(self, myDoc, indxt, indices):
  1778.         sectionParent = myDoc.documentStructure.sectionParents[0]
  1779.         articleCount = len(sectionParent.articles)
  1780.         if self.opts.verbose > 3:
  1781.             self._oeb.logger.info('Writing %d articles for mobitype 0x%03X' % (articleCount, myDoc.mobiType))
  1782.         
  1783.         index = 0
  1784.         offset = myDoc.documentStructure.startAddress
  1785.         length = myDoc.documentStructure.length
  1786.         c = 0
  1787.         firstSection = myDoc.documentStructure.firstSectionIndex
  1788.         lastSection = myDoc.documentStructure.lastSectionIndex
  1789.         self._write_periodical_node(indxt, indices, index, offset, length, c, firstSection, lastSection)
  1790.         index += 1
  1791.         offset = sectionParent.startAddress
  1792.         length = sectionParent.sectionLength
  1793.         c += 1
  1794.         firstArticle = sectionParent.firstArticleIndex
  1795.         lastArticle = sectionParent.lastArticleIndex
  1796.         parentIndex = sectionParent.parentIndex
  1797.         self._write_section_node(indxt, indices, sectionParent.myCtocMapIndex, index, offset, length, c, firstArticle, lastArticle, parentIndex)
  1798.         last_name = '%04X' % c
  1799.         for i, article in enumerate(list(sectionParent.articles)):
  1800.             index = article.myCtocMapIndex
  1801.             offset = article.startAddress
  1802.             length = article.articleLength
  1803.             c += 1
  1804.             parentIndex = article.sectionParentIndex
  1805.             self._write_article_node(indxt, indices, index, offset, length, c, parentIndex)
  1806.         
  1807.         last_name = '%04X' % c
  1808.         return (last_name, c)
  1809.  
  1810.     
  1811.     def _add_periodical_structured_articles(self, myDoc, indxt, indices):
  1812.         if self.opts.verbose > 2:
  1813.             self._oeb.logger.info('Writing NCXEntries for mobiType 0x%03X' % myDoc.mobiType)
  1814.         
  1815.         sectionParent = myDoc.documentStructure.sectionParents[0]
  1816.         index = 0
  1817.         offset = myDoc.documentStructure.startAddress
  1818.         length = myDoc.documentStructure.length
  1819.         c = 0
  1820.         firstSection = myDoc.documentStructure.firstSectionIndex
  1821.         lastSection = myDoc.documentStructure.lastSectionIndex
  1822.         self._write_periodical_node(indxt, indices, index, offset, length, c, firstSection, lastSection)
  1823.         sectionCount = firstSection
  1824.         while sectionCount <= lastSection:
  1825.             sectionParent = myDoc.documentStructure.sectionParents[sectionCount - 1]
  1826.             offset = sectionParent.startAddress
  1827.             length = sectionParent.sectionLength
  1828.             c += 1
  1829.             firstArticle = sectionParent.firstArticleIndex
  1830.             lastArticle = sectionParent.lastArticleIndex
  1831.             parentIndex = sectionParent.parentIndex
  1832.             self._write_section_node(indxt, indices, sectionParent.myCtocMapIndex, sectionCount, offset, length, c, firstArticle, lastArticle, parentIndex)
  1833.             sectionCount += 1
  1834.         sectionCount = firstSection
  1835.         while sectionCount <= lastSection:
  1836.             sectionParent = myDoc.documentStructure.sectionParents[sectionCount - 1]
  1837.             last_name = '%04X' % c
  1838.             for i, article in enumerate(list(sectionParent.articles)):
  1839.                 if self.opts.verbose > 3:
  1840.                     self._oeb.logger.info('Adding section:article %d:%02d' % (sectionParent.myIndex, i))
  1841.                 
  1842.                 index = article.myCtocMapIndex
  1843.                 offset = article.startAddress
  1844.                 length = article.articleLength
  1845.                 c += 1
  1846.                 parentIndex = article.sectionParentIndex
  1847.                 self._write_article_node(indxt, indices, index, offset, length, c, parentIndex)
  1848.                 last_name = '%04X' % c
  1849.             
  1850.             sectionCount += 1
  1851.         return (last_name, c)
  1852.  
  1853.     
  1854.     def _generate_indxt(self):
  1855.         documentType = 'unknown'
  1856.         sectionIndices = []
  1857.         sectionParents = []
  1858.         currentSection = 0
  1859.         toc = self._oeb.toc
  1860.         indxt = StringIO()
  1861.         indices = StringIO()
  1862.         c = 0
  1863.         indices.write('IDXT')
  1864.         c = 0
  1865.         last_name = None
  1866.         documentType = self._establish_document_structure()
  1867.         myDoc = self._MobiDoc
  1868.         nodes = list(toc.iter())[0:1]
  1869.         for i, child in enumerate(nodes):
  1870.             if documentType == 'periodical':
  1871.                 myPeriodical = myDoc.documentStructure
  1872.                 if self.opts.verbose > 3:
  1873.                     self._oeb.logger.info('\nDocument: %s \tkls:%s \tdpt:%d  ply:%03d' % (child.title, child.klass, child.depth(), child.play_order))
  1874.                 
  1875.                 (sectionIndices, sectionParents) = self._generate_section_indices(child, currentSection, myPeriodical, myDoc)
  1876.                 continue
  1877.             if documentType == 'book':
  1878.                 myBook = myDoc.documentStructure
  1879.                 if self.opts.verbose > 3:
  1880.                     self._oeb.logger.info('\nBook: %-19.19s \tkls:%s \tdpt:%d  ply:%03d' % (child.title, child.klass, child.depth(), child.play_order))
  1881.                 
  1882.             self.opts.verbose > 3
  1883.             if self.opts.verbose > 3:
  1884.                 self._oeb.logger.info('unknown document type %12.12s \tdepth:%d' % (child.title, child.depth()))
  1885.                 continue
  1886.         
  1887.         entries = list(toc.iter())[1:]
  1888.         for i, child in enumerate(entries):
  1889.             if not (child.title) or not child.title.strip():
  1890.                 continue
  1891.             
  1892.             (offset, length) = self._compute_offset_length(i, child, entries)
  1893.             if (child.klass == 'chapter' or not (self.opts.mobi_periodical)) and child.klass == 'article':
  1894.                 myNewChapter = MobiChapter(myDoc.getNextNode(), offset, length, i)
  1895.                 myBook.addChapter(myNewChapter)
  1896.                 
  1897.                 try:
  1898.                     if self.opts.verbose > 3:
  1899.                         self._oeb.logger.info('  Chapter: %-14.14s \tcls:%s \tdpt:%d  ply:%03d \toff:0x%X \t:len0x%X' % (child.title, child.klass, child.depth(), child.play_order, offset, length))
  1900.                 if self.opts.verbose > 3:
  1901.                     self._oeb.logger.info('  Chapter: %-14.14s \tclass:%s \tdepth:%d  playOrder:%03d \toff:0x%X \t:len0x%X' % ('(bad string)', child.klass, child.depth(), child.play_order, offset, length))
  1902.                 
  1903.  
  1904.                 continue
  1905.             self.opts.verbose > 3
  1906.             if child.klass == 'section' and self.opts.mobi_periodical:
  1907.                 if self.opts.verbose > 3:
  1908.                     self._oeb.logger.info('\n  Section: %-15.15s \tkls:%s \tdpt:%d  ply:%03d' % (child.title, child.klass, child.depth(), child.play_order))
  1909.                 
  1910.                 self._generate_section_article_indices(i, child, entries, sectionIndices, sectionParents)
  1911.                 continue
  1912.         
  1913.         if self.opts.verbose > 3:
  1914.             self._oeb.logger.info('')
  1915.         
  1916.         mobiType = myDoc.mobiType
  1917.         if self.opts.verbose > 3:
  1918.             self._MobiDoc.dumpInfo()
  1919.         
  1920.         if mobiType == 2:
  1921.             (last_name, c) = self._add_book_chapters(myDoc, indxt, indices)
  1922.         elif mobiType == 258 and myDoc.documentStructure.sectionCount() == 1:
  1923.             (last_name, c) = self._add_periodical_flat_articles(myDoc, indxt, indices)
  1924.         else:
  1925.             (last_name, c) = self._add_periodical_structured_articles(myDoc, indxt, indices)
  1926.         return (align_block(indxt.getvalue()), c, align_block(indices.getvalue()), last_name)
  1927.  
  1928.  
  1929.  
  1930. class HTMLRecordData(object):
  1931.     
  1932.     def __init__(self):
  1933.         self._continuingNode = -1
  1934.         self._continuingNodeParent = -1
  1935.         self._openingNode = -1
  1936.         self._openingNodeParent = -1
  1937.         self._currentSectionNodeCount = -1
  1938.         self._nextSectionNumber = -1
  1939.         self._nextSectionOpeningNode = -1
  1940.         self._nextSectionNodeCount = -1
  1941.  
  1942.     
  1943.     def getContinuingNode(self):
  1944.         return self._continuingNode
  1945.  
  1946.     
  1947.     def setContinuingNode(self, value):
  1948.         self._continuingNode = value
  1949.  
  1950.     continuingNode = property(getContinuingNode, setContinuingNode, None, None)
  1951.     
  1952.     def getContinuingNodeParent(self):
  1953.         return self._continuingNodeParent
  1954.  
  1955.     
  1956.     def setContinuingNodeParent(self, value):
  1957.         self._continuingNodeParent = value
  1958.  
  1959.     continuingNodeParent = property(getContinuingNodeParent, setContinuingNodeParent, None, None)
  1960.     
  1961.     def getOpeningNode(self):
  1962.         return self._openingNode
  1963.  
  1964.     
  1965.     def setOpeningNode(self, value):
  1966.         self._openingNode = value
  1967.  
  1968.     openingNode = property(getOpeningNode, setOpeningNode, None, None)
  1969.     
  1970.     def getOpeningNodeParent(self):
  1971.         return self._openingNodeParent
  1972.  
  1973.     
  1974.     def setOpeningNodeParent(self, value):
  1975.         self._openingNodeParent = value
  1976.  
  1977.     openingNodeParent = property(getOpeningNodeParent, setOpeningNodeParent, None, None)
  1978.     
  1979.     def getCurrentSectionNodeCount(self):
  1980.         return self._currentSectionNodeCount
  1981.  
  1982.     
  1983.     def setCurrentSectionNodeCount(self, value):
  1984.         self._currentSectionNodeCount = value
  1985.  
  1986.     currentSectionNodeCount = property(getCurrentSectionNodeCount, setCurrentSectionNodeCount, None, None)
  1987.     
  1988.     def getNextSectionNumber(self):
  1989.         return self._nextSectionNumber
  1990.  
  1991.     
  1992.     def setNextSectionNumber(self, value):
  1993.         self._nextSectionNumber = value
  1994.  
  1995.     nextSectionNumber = property(getNextSectionNumber, setNextSectionNumber, None, None)
  1996.     
  1997.     def getNextSectionOpeningNode(self):
  1998.         return self._nextSectionOpeningNode
  1999.  
  2000.     
  2001.     def setNextSectionOpeningNode(self, value):
  2002.         self._nextSectionOpeningNode = value
  2003.  
  2004.     nextSectionOpeningNode = property(getNextSectionOpeningNode, setNextSectionOpeningNode, None, None)
  2005.     
  2006.     def getNextSectionNodeCount(self):
  2007.         return self._nextSectionNodeCount
  2008.  
  2009.     
  2010.     def setNextSectionNodeCount(self, value):
  2011.         self._nextSectionNodeCount = value
  2012.  
  2013.     nextSectionNodeCount = property(getNextSectionNodeCount, setNextSectionNodeCount, None, None)
  2014.     
  2015.     def dumpData(self, recordNumber, oeb):
  2016.         oeb.logger.info('---  Summary of HTML Record 0x%x [%d] indexing  ---' % (recordNumber, recordNumber))
  2017.         oeb.logger.info('            continuingNode: %03d' % self.continuingNode)
  2018.         oeb.logger.info('      continuingNodeParent: %03d' % self.continuingNodeParent)
  2019.         oeb.logger.info('               openingNode: %03d' % self.openingNode)
  2020.         oeb.logger.info('         openingNodeParent: %03d' % self.openingNodeParent)
  2021.         oeb.logger.info('   currentSectionNodeCount: %03d' % self.currentSectionNodeCount)
  2022.         oeb.logger.info('         nextSectionNumber: %03d' % self.nextSectionNumber)
  2023.         oeb.logger.info('    nextSectionOpeningNode: %03d' % self.nextSectionOpeningNode)
  2024.         oeb.logger.info('      nextSectionNodeCount: %03d' % self.nextSectionNodeCount)
  2025.  
  2026.  
  2027.  
  2028. class MobiDocument(object):
  2029.     _nextNode = -1
  2030.     
  2031.     def __init__(self, mobitype):
  2032.         self._mobitype = mobitype
  2033.         self._documentStructure = None
  2034.  
  2035.     
  2036.     def getMobiType(self):
  2037.         return self._mobitype
  2038.  
  2039.     
  2040.     def setMobiType(self, value):
  2041.         self._mobitype = value
  2042.  
  2043.     mobiType = property(getMobiType, setMobiType, None, None)
  2044.     
  2045.     def getDocumentStructure(self):
  2046.         return self._documentStructure
  2047.  
  2048.     
  2049.     def setDocumentStructure(self, value):
  2050.         self._documentStructure = value
  2051.  
  2052.     documentStructure = property(getDocumentStructure, setDocumentStructure, None, None)
  2053.     
  2054.     def getNextNode(self):
  2055.         self._nextNode += 1
  2056.         return self._nextNode
  2057.  
  2058.     
  2059.     def dumpInfo(self):
  2060.         self._documentStructure.dumpInfo()
  2061.  
  2062.  
  2063.  
  2064. class MobiBook(object):
  2065.     
  2066.     def __init__(self):
  2067.         self._chapters = []
  2068.  
  2069.     
  2070.     def chapterCount(self):
  2071.         return len(self._chapters)
  2072.  
  2073.     
  2074.     def getChapters(self):
  2075.         return self._chapters
  2076.  
  2077.     
  2078.     def setChapters(self, value):
  2079.         self._chapters = value
  2080.  
  2081.     chapters = property(getChapters, setChapters, None, None)
  2082.     
  2083.     def addChapter(self, value):
  2084.         self._chapters.append(value)
  2085.  
  2086.     
  2087.     def dumpInfo(self):
  2088.         print '%20s:' % 'Book'
  2089.         print '%20s: %d' % ('Number of chapters', len(self._chapters))
  2090.         for count, chapter in enumerate(self._chapters):
  2091.             print '%20s: %d' % ('myCtocMapIndex', chapter.myCtocMapIndex)
  2092.             print '%20s: %d' % ('Chapter', count)
  2093.             print '%20s: 0x%X' % ('startAddress', chapter.startAddress)
  2094.             print '%20s: 0x%X' % ('length', chapter.length)
  2095.             print 
  2096.         
  2097.  
  2098.  
  2099.  
  2100. class MobiChapter(object):
  2101.     
  2102.     def __init__(self, myIndex, startAddress, length, ctoc_map_index):
  2103.         self._myIndex = myIndex
  2104.         self._startAddress = startAddress
  2105.         self._length = length
  2106.         self._myCtocMapIndex = ctoc_map_index
  2107.  
  2108.     
  2109.     def getMyCtocMapIndex(self):
  2110.         return self._myCtocMapIndex
  2111.  
  2112.     
  2113.     def setMyCtocMapIndex(self, value):
  2114.         self._myCtocMapIndex = value
  2115.  
  2116.     myCtocMapIndex = property(getMyCtocMapIndex, setMyCtocMapIndex, None, None)
  2117.     
  2118.     def getMyIndex(self):
  2119.         return self._myIndex
  2120.  
  2121.     myIndex = property(getMyIndex, None, None, None)
  2122.     
  2123.     def getStartAddress(self):
  2124.         return self._startAddress
  2125.  
  2126.     
  2127.     def setStartAddress(self, value):
  2128.         self._startAddress = value
  2129.  
  2130.     startAddress = property(getStartAddress, setStartAddress, None, None)
  2131.     
  2132.     def getLength(self):
  2133.         return self._length
  2134.  
  2135.     
  2136.     def setLength(self, value):
  2137.         self._length = value
  2138.  
  2139.     length = property(getLength, setLength, None, None)
  2140.  
  2141.  
  2142. class MobiPeriodical(object):
  2143.     
  2144.     def __init__(self, myIndex):
  2145.         self._myIndex = myIndex
  2146.         self._sectionParents = []
  2147.         self._startAddress = 0xFFFFFFFFL
  2148.         self._length = 0xFFFFFFFFL
  2149.         self._firstSectionIndex = 0xFFFFFFFFL
  2150.         self._lastSectionIndex = 0xFFFFFFFFL
  2151.         self._myCtocMapIndex = 0
  2152.  
  2153.     
  2154.     def getMyIndex(self):
  2155.         return self._myIndex
  2156.  
  2157.     
  2158.     def setMyIndex(self, value):
  2159.         self._myIndex = value
  2160.  
  2161.     myIndex = property(getMyIndex, setMyIndex, None, None)
  2162.     
  2163.     def getSectionParents(self):
  2164.         return self._sectionParents
  2165.  
  2166.     
  2167.     def setSectionParents(self, value):
  2168.         self._sectionParents = value
  2169.  
  2170.     sectionParents = property(getSectionParents, setSectionParents, None, None)
  2171.     
  2172.     def sectionCount(self):
  2173.         return len(self._sectionParents)
  2174.  
  2175.     
  2176.     def getStartAddress(self):
  2177.         return self._startAddress
  2178.  
  2179.     
  2180.     def setStartAddress(self, value):
  2181.         self._startAddress = value
  2182.  
  2183.     startAddress = property(getStartAddress, setStartAddress, None, None)
  2184.     
  2185.     def getLength(self):
  2186.         return self._length
  2187.  
  2188.     
  2189.     def setLength(self, value):
  2190.         self._length = value
  2191.  
  2192.     length = property(getLength, setLength, None, None)
  2193.     
  2194.     def getFirstSectionIndex(self):
  2195.         return self._firstSectionIndex
  2196.  
  2197.     
  2198.     def setFirstSectionIndex(self, value):
  2199.         self._firstSectionIndex = value
  2200.  
  2201.     firstSectionIndex = property(getFirstSectionIndex, setFirstSectionIndex, None, None)
  2202.     
  2203.     def getLastSectionIndex(self):
  2204.         return self._lastSectionIndex
  2205.  
  2206.     
  2207.     def setLastSectionIndex(self, value):
  2208.         self._lastSectionIndex = value
  2209.  
  2210.     lastSectionIndex = property(getLastSectionIndex, setLastSectionIndex, None, None)
  2211.     
  2212.     def getMyCtocMapIndex(self):
  2213.         return self._myCtocMapIndex
  2214.  
  2215.     
  2216.     def setMyCtocMapIndex(self, value):
  2217.         self._myCtocMapIndex = value
  2218.  
  2219.     myCtocMapIndex = property(getMyCtocMapIndex, setMyCtocMapIndex, None, None)
  2220.     
  2221.     def addSectionParent(self, myIndex, ctoc_map_index):
  2222.         newSection = MobiSection(myIndex)
  2223.         newSection.parentIndex = self._myIndex
  2224.         newSection.sectionIndex = len(self._sectionParents)
  2225.         newSection.myCtocMapIndex = ctoc_map_index
  2226.         self._sectionParents.append(newSection)
  2227.         return newSection
  2228.  
  2229.     
  2230.     def dumpInfo(self):
  2231.         print '%20s:' % 'Periodical'
  2232.         print '%20s: 0x%X' % ('myIndex', self.myIndex)
  2233.         print '%20s: 0x%X' % ('startAddress', self.startAddress)
  2234.         print '%20s: 0x%X' % ('length', self.length)
  2235.         print '%20s: 0x%X' % ('myCtocMapIndex', self.myCtocMapIndex)
  2236.         print '%20s: 0x%X' % ('firstSectionIndex', self.firstSectionIndex)
  2237.         print '%20s: 0x%X' % ('lastSectionIndex', self.lastSectionIndex)
  2238.         print '%20s: %d' % ('Number of Sections', len(self._sectionParents))
  2239.         for count, section in enumerate(self._sectionParents):
  2240.             print '\t%20s: %d' % ('Section', count)
  2241.             print '\t%20s: 0x%X' % ('startAddress', section.startAddress)
  2242.             print '\t%20s: 0x%X' % ('length', section.sectionLength)
  2243.             print '\t%20s: 0x%X' % ('parentIndex', section.parentIndex)
  2244.             print '\t%20s: 0x%X' % ('myIndex', section.myIndex)
  2245.             print '\t%20s: 0x%X' % ('firstArticleIndex', section.firstArticleIndex)
  2246.             print '\t%20s: 0x%X' % ('lastArticleIndex', section.lastArticleIndex)
  2247.             print '\t%20s: 0x%X' % ('articles', len(section.articles))
  2248.             print '\t%20s: 0x%X' % ('myCtocMapIndex', section.myCtocMapIndex)
  2249.             print 
  2250.             for artCount, article in enumerate(section.articles):
  2251.                 print '\t\t%20s: %d' % ('Article', artCount)
  2252.                 print '\t\t%20s: 0x%X' % ('startAddress', article.startAddress)
  2253.                 print '\t\t%20s: 0x%X' % ('length', article.articleLength)
  2254.                 print '\t\t%20s: 0x%X' % ('sectionIndex', article.sectionParentIndex)
  2255.                 print '\t\t%20s: 0x%X' % ('myIndex', article.myIndex)
  2256.                 print '\t\t%20s: 0x%X' % ('myCtocMapIndex', article.myCtocMapIndex)
  2257.                 print 
  2258.             
  2259.         
  2260.  
  2261.  
  2262.  
  2263. class MobiSection(object):
  2264.     
  2265.     def __init__(self, myMobiDoc):
  2266.         self._myMobiDoc = myMobiDoc
  2267.         self._myIndex = myMobiDoc.getNextNode()
  2268.         self._parentIndex = 0xFFFFFFFFL
  2269.         self._firstArticleIndex = 0
  2270.         self._lastArticleIndex = 0
  2271.         self._startAddress = 0xFFFFFFFFL
  2272.         self._sectionLength = 0xFFFFFFFFL
  2273.         self._articles = []
  2274.         self._myCtocMapIndex = -1
  2275.  
  2276.     
  2277.     def getMyMobiDoc(self):
  2278.         return self._myMobiDoc
  2279.  
  2280.     
  2281.     def setMyMobiDoc(self, value):
  2282.         self._myMobiDoc = value
  2283.  
  2284.     myMobiDoc = property(getMyMobiDoc, setMyMobiDoc, None, None)
  2285.     
  2286.     def getMyIndex(self):
  2287.         return self._myIndex
  2288.  
  2289.     
  2290.     def setMyIndex(self, value):
  2291.         self._myIndex = value
  2292.  
  2293.     myIndex = property(getMyIndex, setMyIndex, None, None)
  2294.     
  2295.     def getParentIndex(self):
  2296.         return self._parentIndex
  2297.  
  2298.     
  2299.     def setParentIndex(self, value):
  2300.         self._parentIndex = value
  2301.  
  2302.     parenIndex = property(getParentIndex, setParentIndex, None, None)
  2303.     
  2304.     def getFirstArticleIndex(self):
  2305.         return self._firstArticleIndex
  2306.  
  2307.     
  2308.     def setFirstArticleIndex(self, value):
  2309.         self._firstArticleIndex = value
  2310.  
  2311.     firstArticleIndex = property(getFirstArticleIndex, setFirstArticleIndex, None, None)
  2312.     
  2313.     def getLastArticleIndex(self):
  2314.         return self._lastArticleIndex
  2315.  
  2316.     
  2317.     def setLastArticleIndex(self, value):
  2318.         self._lastArticleIndex = value
  2319.  
  2320.     lastArticleIndex = property(getLastArticleIndex, setLastArticleIndex, None, None)
  2321.     
  2322.     def getStartAddress(self):
  2323.         return self._startAddress
  2324.  
  2325.     
  2326.     def setStartAddress(self, value):
  2327.         self._startAddress = value
  2328.  
  2329.     startAddress = property(getStartAddress, setStartAddress, None, None)
  2330.     
  2331.     def getSectionLength(self):
  2332.         return self._sectionLength
  2333.  
  2334.     
  2335.     def setSectionLength(self, value):
  2336.         self._sectionLength = value
  2337.  
  2338.     sectionLength = property(getSectionLength, setSectionLength, None, None)
  2339.     
  2340.     def getArticles(self):
  2341.         return self._articles
  2342.  
  2343.     
  2344.     def setArticles(self, value):
  2345.         self._articles = value
  2346.  
  2347.     articles = property(getArticles, setArticles, None, None)
  2348.     
  2349.     def getMyCtocMapIndex(self):
  2350.         return self._myCtocMapIndex
  2351.  
  2352.     
  2353.     def setMyCtocMapIndex(self, value):
  2354.         self._myCtocMapIndex = value
  2355.  
  2356.     myCtocMapIndex = property(getMyCtocMapIndex, setMyCtocMapIndex, None, None)
  2357.     
  2358.     def addArticle(self, article):
  2359.         self._articles.append(article)
  2360.         self.myMobiDoc.documentStructure.lastSectionIndex = self.myIndex
  2361.  
  2362.  
  2363.  
  2364. class MobiArticle(object):
  2365.     
  2366.     def __init__(self, sectionParent, startAddress, length, ctocMapIndex):
  2367.         self._mySectionParent = sectionParent
  2368.         self._myMobiDoc = sectionParent.myMobiDoc
  2369.         self._myIndex = sectionParent.myMobiDoc.getNextNode()
  2370.         self._myCtocMapIndex = ctocMapIndex
  2371.         self._sectionParentIndex = sectionParent.myIndex
  2372.         self._startAddress = startAddress
  2373.         self._articleLength = length
  2374.  
  2375.     
  2376.     def getMySectionParent(self):
  2377.         return self._mySectionParent
  2378.  
  2379.     
  2380.     def setMySectionParent(self, value):
  2381.         self._mySectionParent = value
  2382.  
  2383.     mySectionParent = property(getMySectionParent, setMySectionParent, None, None)
  2384.     
  2385.     def getMyMobiDoc(self):
  2386.         return self._myMobiDoc
  2387.  
  2388.     
  2389.     def setMyMobiDoc(self, value):
  2390.         self._myMobiDoc = value
  2391.  
  2392.     myMobiDoc = property(getMyMobiDoc, setMyMobiDoc, None, None)
  2393.     
  2394.     def getMyIndex(self):
  2395.         return self._myIndex
  2396.  
  2397.     
  2398.     def setMyIndex(self, value):
  2399.         self._sectionIndex = value
  2400.  
  2401.     myIndex = property(getMyIndex, setMyIndex, None, None)
  2402.     
  2403.     def getSectionParentIndex(self):
  2404.         return self._sectionParentIndex
  2405.  
  2406.     
  2407.     def setSectionParentIndex(self, value):
  2408.         self._sectionParentIndex = value
  2409.  
  2410.     sectionParentIndex = property(getSectionParentIndex, setSectionParentIndex, None, None)
  2411.     
  2412.     def getStartAddress(self):
  2413.         return self._startAddress
  2414.  
  2415.     
  2416.     def setStartAddress(self, value):
  2417.         self._startAddress = value
  2418.  
  2419.     startAddress = property(getStartAddress, setStartAddress, None, None)
  2420.     
  2421.     def getArticleLength(self):
  2422.         return self._articleLength
  2423.  
  2424.     
  2425.     def setArticleLength(self, value):
  2426.         self._articleLength = value
  2427.  
  2428.     articleLength = property(getArticleLength, setArticleLength, None, None)
  2429.     
  2430.     def getMyCtocMapIndex(self):
  2431.         return self._myCtocMapIndex
  2432.  
  2433.     
  2434.     def setMyCtocMapIndex(self, value):
  2435.         self._myCtocMapIndex = value
  2436.  
  2437.     myCtocMapIndex = property(getMyCtocMapIndex, setMyCtocMapIndex, None, None)
  2438.  
  2439.