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

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. from __future__ import with_statement
  5. __license__ = 'GPL v3'
  6. __copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
  7. __docformat__ = 'restructuredtext en'
  8. import os
  9. import shutil
  10. import re
  11. from calibre.customize.conversion import OutputFormatPlugin
  12. from calibre.ptempfile import TemporaryDirectory
  13. from calibre import CurrentDir
  14. from calibre.customize.conversion import OptionRecommendation
  15. from calibre.constants import filesystem_encoding
  16. from lxml import etree
  17. block_level_tags = ('address', 'body', 'blockquote', 'center', 'dir', 'div', 'dl', 'fieldset', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'isindex', 'menu', 'noframes', 'noscript', 'ol', 'p', 'pre', 'table', 'ul')
  18.  
  19. class EPUBOutput(OutputFormatPlugin):
  20.     name = 'EPUB Output'
  21.     author = 'Kovid Goyal'
  22.     file_type = 'epub'
  23.     options = set([
  24.         OptionRecommendation(name = 'extract_to', help = _('Extract the contents of the generated EPUB file to the specified directory. The contents of the directory are first deleted, so be careful.')),
  25.         OptionRecommendation(name = 'dont_split_on_page_breaks', recommended_value = False, level = OptionRecommendation.LOW, help = _('Turn off splitting at page breaks. Normally, input files are automatically split at every page break into two files. This gives an output ebook that can be parsed faster and with less resources. However, splitting is slow and if your source file contains a very large number of page breaks, you should turn off splitting on page breaks.')),
  26.         OptionRecommendation(name = 'flow_size', recommended_value = 260, help = _('Split all HTML files larger than this size (in KB). This is necessary as most EPUB readers cannot handle large file sizes. The default of %defaultKB is the size required for Adobe Digital Editions.')),
  27.         OptionRecommendation(name = 'no_default_epub_cover', recommended_value = False, help = _("Normally, if the input file has no cover and you don't specify one, a default cover is generated with the title, authors, etc. This option disables the generation of this cover.")),
  28.         OptionRecommendation(name = 'no_svg_cover', recommended_value = False, help = _('Do not use SVG for the book cover. Use this option if your EPUB is going to be used on a device that does not support SVG, like the iPhone or the JetBook Lite. Without this option, such devices will display the cover as a blank page.')),
  29.         OptionRecommendation(name = 'preserve_cover_aspect_ratio', recommended_value = False, help = _('When using an SVG cover, this option will cause the cover to scale to cover the available screen area, but still preserve its aspect ratio (ratio of width to height). That means there may be white borders at the sides or top and bottom of the image, but the image will never be distorted. Without this option the image may be slightly distorted, but there will be no borders.'))])
  30.     recommendations = set([
  31.         ('pretty_print', True, OptionRecommendation.HIGH)])
  32.     
  33.     def workaround_webkit_quirks(self):
  34.         XPath = XPath
  35.         import calibre.ebooks.oeb.base
  36.         for x in self.oeb.spine:
  37.             root = x.data
  38.             body = XPath('//h:body')(root)
  39.             if body:
  40.                 body = body[0]
  41.             
  42.             if not hasattr(body, 'xpath'):
  43.                 continue
  44.             
  45.             for pre in XPath('//h:pre')(body):
  46.                 if not (pre.text) and len(pre) == 0:
  47.                     pre.tag = 'div'
  48.                     continue
  49.             
  50.         
  51.  
  52.     
  53.     def upshift_markup(self):
  54.         XPath = XPath
  55.         import calibre.ebooks.oeb.base
  56.         for x in self.oeb.spine:
  57.             root = x.data
  58.             body = XPath('//h:body')(root)
  59.             if body:
  60.                 body = body[0]
  61.             
  62.             if not hasattr(body, 'xpath'):
  63.                 continue
  64.             
  65.             for u in XPath('//h:u')(root):
  66.                 u.tag = 'span'
  67.                 u.set('style', 'text-decoration:underline')
  68.             
  69.         
  70.  
  71.     
  72.     def convert(self, oeb, output_path, input_plugin, opts, log):
  73.         self.log = log
  74.         self.opts = opts
  75.         self.oeb = oeb
  76.         self.workaround_ade_quirks()
  77.         self.workaround_webkit_quirks()
  78.         self.upshift_markup()
  79.         RescaleImages = RescaleImages
  80.         import calibre.ebooks.oeb.transforms.rescale
  81.         RescaleImages()(oeb, opts)
  82.         Split = Split
  83.         import calibre.ebooks.oeb.transforms.split
  84.         split = Split(not (self.opts.dont_split_on_page_breaks), max_flow_size = self.opts.flow_size * 1024)
  85.         split(self.oeb, self.opts)
  86.         CoverManager = CoverManager
  87.         import calibre.ebooks.oeb.transforms.cover
  88.         cm = CoverManager(no_default_cover = self.opts.no_default_epub_cover, no_svg_cover = self.opts.no_svg_cover, preserve_aspect_ratio = self.opts.preserve_cover_aspect_ratio)
  89.         cm(self.oeb, self.opts, self.log)
  90.         self.workaround_sony_quirks()
  91.         if self.oeb.toc.count() == 0:
  92.             self.log.warn('This EPUB file has no Table of Contents. Creating a default TOC')
  93.             first = iter(self.oeb.spine).next()
  94.             self.oeb.toc.add(_('Start'), first.href)
  95.         
  96.         OPF = OPF
  97.         import calibre.ebooks.oeb.base
  98.         identifiers = oeb.metadata['identifier']
  99.         uuid = None
  100.         for x in identifiers:
  101.             if x.get(OPF('scheme'), None).lower() == 'uuid' or unicode(x).startswith('urn:uuid:'):
  102.                 uuid = unicode(x).split(':')[-1]
  103.                 break
  104.                 continue
  105.         
  106.         if uuid is None:
  107.             self.log.warn('No UUID identifier found')
  108.             uuid4 = uuid4
  109.             import uuid
  110.             uuid = str(uuid4())
  111.             oeb.metadata.add('identifier', uuid, scheme = 'uuid', id = uuid)
  112.         
  113.         
  114.         try:
  115.             tdir = _[1]
  116.             plugin_for_output_format = plugin_for_output_format
  117.             import calibre.customize.ui
  118.             metadata_xml = None
  119.             extra_entries = []
  120.             oeb_output = plugin_for_output_format('oeb')
  121.             oeb_output.convert(oeb, tdir, input_plugin, opts, log)
  122.             opf = _[2][0]
  123.             [](_[3][0])
  124.             encrypted_fonts = getattr(input_plugin, 'encrypted_fonts', [])
  125.             encryption = None
  126.             initialize_container = initialize_container
  127.             import calibre.ebooks.epub
  128.             epub = initialize_container(output_path, os.path.basename(opf), extra_entries = extra_entries)
  129.             epub.add_dir(tdir)
  130.             if metadata_xml is not None:
  131.                 epub.writestr('META-INF/metadata.xml', metadata_xml.encode('utf-8'))
  132.             
  133.             if opts.extract_to is not None:
  134.                 if os.path.exists(opts.extract_to):
  135.                     shutil.rmtree(opts.extract_to)
  136.                 
  137.                 os.mkdir(opts.extract_to)
  138.                 epub.extractall(path = opts.extract_to)
  139.                 self.log.info('EPUB extracted to', opts.extract_to)
  140.             
  141.             epub.close()
  142.         finally:
  143.             pass
  144.  
  145.  
  146.     
  147.     def encrypt_fonts(self, uris, tdir, uuid):
  148.         unhexlify = unhexlify
  149.         import binascii
  150.         key = re.sub('[^a-fA-F0-9]', '', uuid)
  151.         if len(key) < 16:
  152.             raise ValueError('UUID identifier %r is invalid' % uuid)
  153.         len(key) < 16
  154.         key = unhexlify(key + key[:32])
  155.         key = tuple(map(ord, key))
  156.         paths = []
  157.         CurrentDir(tdir).__enter__()
  158.         
  159.         try:
  160.             paths = [ os.path.join(*x.split('/')) for x in uris ]
  161.             uris = dict(zip(uris, paths))
  162.             fonts = []
  163.             for uri in list(uris.keys()):
  164.                 path = uris[uri]
  165.                 self.log.debug('Encrypting font:', uri)
  166.                 
  167.                 try:
  168.                     f = _[2]
  169.                     data = f.read(1024)
  170.                     f.seek(0)
  171.                     for i in range(1024):
  172.                         f.write(chr(ord(data[i]) ^ key[i % 16]))
  173.                 finally:
  174.                     pass
  175.  
  176.                 fonts.append(u'\n                <enc:EncryptedData>\n                    <enc:EncryptionMethod Algorithm="http://ns.adobe.com/pdf/enc#RC"/>\n                    <enc:CipherData>\n                    <enc:CipherReference URI="%s"/>\n                    </enc:CipherData>\n                </enc:EncryptedData>\n                ' % uri.replace('"', '\\"'))
  177.             
  178.             if fonts:
  179.                 ans = '<encryption\n                    xmlns="urn:oasis:names:tc:opendocument:xmlns:container"\n                    xmlns:enc="http://www.w3.org/2001/04/xmlenc#"\n                    xmlns:deenc="http://ns.adobe.com/digitaleditions/enc">\n                    '
  180.                 ans += u'\n'.join(fonts).encode('utf-8')
  181.                 ans += '\n</encryption>'
  182.                 return ans
  183.         finally:
  184.             pass
  185.  
  186.  
  187.     
  188.     def condense_ncx(self, ncx_path):
  189.         if not self.opts.pretty_print:
  190.             tree = etree.parse(ncx_path)
  191.             for tag in tree.getroot().iter(tag = etree.Element):
  192.                 if tag.text:
  193.                     tag.text = tag.text.strip()
  194.                 
  195.                 if tag.tail:
  196.                     tag.tail = tag.tail.strip()
  197.                     continue
  198.             
  199.             compressed = etree.tostring(tree.getroot(), encoding = 'utf-8')
  200.             open(ncx_path, 'wb').write(compressed)
  201.         
  202.  
  203.     
  204.     def workaround_ade_quirks(self):
  205.         XPath = XPath
  206.         XHTML = XHTML
  207.         OEB_STYLES = OEB_STYLES
  208.         barename = barename
  209.         urlunquote = urlunquote
  210.         import calibre.ebooks.oeb.base
  211.         stylesheet = None
  212.         for item in self.oeb.manifest:
  213.             if item.media_type.lower() in OEB_STYLES:
  214.                 stylesheet = item
  215.                 break
  216.                 continue
  217.         
  218.         frag_pat = re.compile('[-A-Za-z0-9_:.]+$')
  219.         for node in self.oeb.toc.iter():
  220.             href = getattr(node, 'href', None)
  221.             if hasattr(href, 'partition'):
  222.                 (base, _, frag) = href.partition('#')
  223.                 frag = urlunquote(frag)
  224.                 if frag and frag_pat.match(frag) is None:
  225.                     self.log.warn('Removing invalid fragment identifier %r from TOC' % frag)
  226.                     node.href = base
  227.                 
  228.             frag_pat.match(frag) is None
  229.         
  230.         for x in self.oeb.spine:
  231.             root = x.data
  232.             body = XPath('//h:body')(root)
  233.             if body:
  234.                 body = body[0]
  235.             
  236.             if hasattr(body, 'xpath'):
  237.                 bad = []
  238.                 for x in XPath('//h:img')(body):
  239.                     src = x.get('src', '').strip()
  240.                     if src in ('', '#') or src.startswith('http:'):
  241.                         bad.append(x)
  242.                         continue
  243.                 
  244.                 for img in bad:
  245.                     img.getparent().remove(img)
  246.                 
  247.                 for x in XPath('//h:a[@name]')(body):
  248.                     if not x.get('id', False):
  249.                         x.set('id', x.get('name'))
  250.                         continue
  251.                 
  252.                 for br in XPath('./h:br')(body):
  253.                     if br.getparent() is None:
  254.                         continue
  255.                     
  256.                     
  257.                     try:
  258.                         prior = br.itersiblings(preceding = True).next()
  259.                         priortag = barename(prior.tag)
  260.                         priortext = prior.tail
  261.                     except:
  262.                         priortag = 'body'
  263.                         priortext = body.text
  264.  
  265.                     if priortext:
  266.                         priortext = priortext.strip()
  267.                     
  268.                     br.tag = XHTML('p')
  269.                     br.text = u'┬á'
  270.                     style = br.get('style', '').split(';')
  271.                     style = filter(None, map((lambda x: x.strip()), style))
  272.                     style.append('margin:0pt; border:0pt')
  273.                     if not priortext and priortag in block_level_tags:
  274.                         style.append('height:1em')
  275.                     else:
  276.                         style.append('height:0pt')
  277.                     br.set('style', '; '.join(style))
  278.                 
  279.             
  280.             for tag in XPath('//h:embed')(root):
  281.                 tag.getparent().remove(tag)
  282.             
  283.             for tag in XPath('//h:object')(root):
  284.                 if tag.get('type', '').lower().strip() in ('image/svg+xml',):
  285.                     continue
  286.                 
  287.                 tag.getparent().remove(tag)
  288.             
  289.             for tag in XPath('//h:title|//h:style')(root):
  290.                 if not tag.text:
  291.                     tag.getparent().remove(tag)
  292.                     continue
  293.             
  294.             for tag in XPath('//h:script')(root):
  295.                 if not (tag.text) and not tag.get('src', False):
  296.                     tag.getparent().remove(tag)
  297.                     continue
  298.             
  299.             for tag in XPath('//h:body/descendant::h:script')(root):
  300.                 tag.getparent().remove(tag)
  301.             
  302.             for tag in XPath('//h:form')(root):
  303.                 tag.getparent().remove(tag)
  304.             
  305.             for tag in XPath('//h:center')(root):
  306.                 tag.tag = XHTML('div')
  307.                 tag.set('style', 'text-align:center')
  308.             
  309.             for tag in XPath('//h:img[@src]')(root):
  310.                 tag.set('src', tag.get('src', '').replace('&', ''))
  311.             
  312.             special_chars = re.compile(u'[ΓÇï┬¡]')
  313.             for elem in root.iterdescendants():
  314.                 if getattr(elem, 'text', False):
  315.                     elem.text = special_chars.sub('', elem.text)
  316.                     elem.text = elem.text.replace(u'ΓÇæ', '-')
  317.                 
  318.                 if getattr(elem, 'tail', False):
  319.                     elem.tail = special_chars.sub('', elem.tail)
  320.                     elem.tail = elem.tail.replace(u'ΓÇæ', '-')
  321.                     continue
  322.             
  323.             if stylesheet is not None:
  324.                 CSSRule = CSSRule
  325.                 import cssutils.css
  326.                 for lb in XPath('//h:ul[@class]|//h:ol[@class]')(root):
  327.                     sel = '.' + lb.get('class')
  328.                     for rule in stylesheet.data.cssRules.rulesOfType(CSSRule.STYLE_RULE):
  329.                         if sel == rule.selectorList.selectorText:
  330.                             rule.style.removeProperty('margin-left')
  331.                             rule.style.removeProperty('padding-left')
  332.                             continue
  333.                     
  334.                 
  335.         
  336.  
  337.     
  338.     def workaround_sony_quirks(self):
  339.         urldefrag = urldefrag
  340.         XPath = XPath
  341.         import calibre.ebooks.oeb.base
  342.         
  343.         def frag_is_at_top(root, frag):
  344.             body = XPath('//h:body')(root)
  345.             if body:
  346.                 body = body[0]
  347.             else:
  348.                 return False
  349.             tree = body.getroottree()
  350.             elem = XPath('//*[@id="%s" or @name="%s"]' % (frag, frag))(root)
  351.             if elem:
  352.                 elem = elem[0]
  353.             else:
  354.                 return False
  355.             path = elem.getpath(elem)
  356.             for el in body.iterdescendants():
  357.                 epath = tree.getpath(el)
  358.                 if epath == path:
  359.                     break
  360.                 
  361.                 if el.text and el.text.strip():
  362.                     return False
  363.                 if not path.startswith(epath):
  364.                     if el.tail and el.tail.strip():
  365.                         return False
  366.                     continue
  367.                 el.tail.strip()
  368.             
  369.             return True
  370.  
  371.         
  372.         def simplify_toc_entry(toc):
  373.             if toc.href:
  374.                 (href, frag) = urldefrag(toc.href)
  375.                 if frag:
  376.                     for x in self.oeb.spine:
  377.                         if x.href == href:
  378.                             if frag_is_at_top(x.data, frag):
  379.                                 self.log.debug('Removing anchor from TOC href:', href + '#' + frag)
  380.                                 toc.href = href
  381.                             
  382.                             break
  383.                             continue
  384.                     
  385.                 
  386.             
  387.             for x in toc:
  388.                 simplify_toc_entry(x)
  389.             
  390.  
  391.         if self.oeb.toc:
  392.             simplify_toc_entry(self.oeb.toc)
  393.         
  394.  
  395.  
  396.