home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2010 November / maximum-cd-2010-11.iso / DiscContents / calibre-0.7.13.msi / file_886 (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-08-06  |  14.7 KB  |  388 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.             oeb_output = plugin_for_output_format('oeb')
  119.             oeb_output.convert(oeb, tdir, input_plugin, opts, log)
  120.             opf = _[2][0]
  121.             [](_[3][0])
  122.             encrypted_fonts = getattr(input_plugin, 'encrypted_fonts', [])
  123.             encryption = None
  124.             initialize_container = initialize_container
  125.             import calibre.ebooks.epub
  126.             epub = initialize_container(output_path, os.path.basename(opf))
  127.             epub.add_dir(tdir)
  128.             if opts.extract_to is not None:
  129.                 os.mkdir(opts.extract_to)
  130.                 epub.extractall(path = opts.extract_to)
  131.                 self.log.info('EPUB extracted to', opts.extract_to)
  132.             
  133.             epub.close()
  134.         finally:
  135.             pass
  136.  
  137.  
  138.     
  139.     def encrypt_fonts(self, uris, tdir, uuid):
  140.         unhexlify = unhexlify
  141.         import binascii
  142.         key = re.sub('[^a-fA-F0-9]', '', uuid)
  143.         if len(key) < 16:
  144.             raise ValueError('UUID identifier %r is invalid' % uuid)
  145.         len(key) < 16
  146.         key = unhexlify(key + key[:32])
  147.         key = tuple(map(ord, key))
  148.         paths = []
  149.         CurrentDir(tdir).__enter__()
  150.         
  151.         try:
  152.             paths = [ os.path.join(*x.split('/')) for x in uris ]
  153.             uris = dict(zip(uris, paths))
  154.             fonts = []
  155.             for uri in list(uris.keys()):
  156.                 path = uris[uri]
  157.                 self.log.debug('Encrypting font:', uri)
  158.                 
  159.                 try:
  160.                     f = _[2]
  161.                     data = f.read(1024)
  162.                     f.seek(0)
  163.                     for i in range(1024):
  164.                         f.write(chr(ord(data[i]) ^ key[i % 16]))
  165.                 finally:
  166.                     pass
  167.  
  168.                 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('"', '\\"'))
  169.             
  170.             if fonts:
  171.                 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                    '
  172.                 ans += u'\n'.join(fonts).encode('utf-8')
  173.                 ans += '\n</encryption>'
  174.                 return ans
  175.         finally:
  176.             pass
  177.  
  178.  
  179.     
  180.     def condense_ncx(self, ncx_path):
  181.         if not self.opts.pretty_print:
  182.             tree = etree.parse(ncx_path)
  183.             for tag in tree.getroot().iter(tag = etree.Element):
  184.                 if tag.text:
  185.                     tag.text = tag.text.strip()
  186.                 
  187.                 if tag.tail:
  188.                     tag.tail = tag.tail.strip()
  189.                     continue
  190.             
  191.             compressed = etree.tostring(tree.getroot(), encoding = 'utf-8')
  192.             open(ncx_path, 'wb').write(compressed)
  193.         
  194.  
  195.     
  196.     def workaround_ade_quirks(self):
  197.         XPath = XPath
  198.         XHTML = XHTML
  199.         OEB_STYLES = OEB_STYLES
  200.         barename = barename
  201.         urlunquote = urlunquote
  202.         import calibre.ebooks.oeb.base
  203.         stylesheet = None
  204.         for item in self.oeb.manifest:
  205.             if item.media_type.lower() in OEB_STYLES:
  206.                 stylesheet = item
  207.                 break
  208.                 continue
  209.         
  210.         frag_pat = re.compile('[-A-Za-z0-9_:.]+$')
  211.         for node in self.oeb.toc.iter():
  212.             href = getattr(node, 'href', None)
  213.             if hasattr(href, 'partition'):
  214.                 (base, _, frag) = href.partition('#')
  215.                 frag = urlunquote(frag)
  216.                 if frag and frag_pat.match(frag) is None:
  217.                     self.log.warn('Removing invalid fragment identifier %r from TOC' % frag)
  218.                     node.href = base
  219.                 
  220.             frag_pat.match(frag) is None
  221.         
  222.         for x in self.oeb.spine:
  223.             root = x.data
  224.             body = XPath('//h:body')(root)
  225.             if body:
  226.                 body = body[0]
  227.             
  228.             if hasattr(body, 'xpath'):
  229.                 bad = []
  230.                 for x in XPath('//h:img')(body):
  231.                     src = x.get('src', '').strip()
  232.                     if src in ('', '#') or src.startswith('http:'):
  233.                         bad.append(x)
  234.                         continue
  235.                 
  236.                 for img in bad:
  237.                     img.getparent().remove(img)
  238.                 
  239.                 for x in XPath('//h:a[@name]')(body):
  240.                     if not x.get('id', False):
  241.                         x.set('id', x.get('name'))
  242.                         continue
  243.                 
  244.                 for br in XPath('./h:br')(body):
  245.                     if br.getparent() is None:
  246.                         continue
  247.                     
  248.                     
  249.                     try:
  250.                         prior = br.itersiblings(preceding = True).next()
  251.                         priortag = barename(prior.tag)
  252.                         priortext = prior.tail
  253.                     except:
  254.                         priortag = 'body'
  255.                         priortext = body.text
  256.  
  257.                     if priortext:
  258.                         priortext = priortext.strip()
  259.                     
  260.                     br.tag = XHTML('p')
  261.                     br.text = u'┬á'
  262.                     style = br.get('style', '').split(';')
  263.                     style = filter(None, map((lambda x: x.strip()), style))
  264.                     style.append('margin:0pt; border:0pt')
  265.                     if not priortext and priortag in block_level_tags:
  266.                         style.append('height:1em')
  267.                     else:
  268.                         style.append('height:0pt')
  269.                     br.set('style', '; '.join(style))
  270.                 
  271.             
  272.             for tag in XPath('//h:embed')(root):
  273.                 tag.getparent().remove(tag)
  274.             
  275.             for tag in XPath('//h:object')(root):
  276.                 if tag.get('type', '').lower().strip() in ('image/svg+xml',):
  277.                     continue
  278.                 
  279.                 tag.getparent().remove(tag)
  280.             
  281.             for tag in XPath('//h:title|//h:style')(root):
  282.                 if not tag.text:
  283.                     tag.getparent().remove(tag)
  284.                     continue
  285.             
  286.             for tag in XPath('//h:script')(root):
  287.                 if not (tag.text) and not tag.get('src', False):
  288.                     tag.getparent().remove(tag)
  289.                     continue
  290.             
  291.             for tag in XPath('//h:body/descendant::h:script')(root):
  292.                 tag.getparent().remove(tag)
  293.             
  294.             for tag in XPath('//h:form')(root):
  295.                 tag.getparent().remove(tag)
  296.             
  297.             for tag in XPath('//h:center')(root):
  298.                 tag.tag = XHTML('div')
  299.                 tag.set('style', 'text-align:center')
  300.             
  301.             for tag in XPath('//h:img[@src]')(root):
  302.                 tag.set('src', tag.get('src', '').replace('&', ''))
  303.             
  304.             special_chars = re.compile(u'[ΓÇï┬¡]')
  305.             for elem in root.iterdescendants():
  306.                 if getattr(elem, 'text', False):
  307.                     elem.text = special_chars.sub('', elem.text)
  308.                     elem.text = elem.text.replace(u'ΓÇæ', '-')
  309.                 
  310.                 if getattr(elem, 'tail', False):
  311.                     elem.tail = special_chars.sub('', elem.tail)
  312.                     elem.tail = elem.tail.replace(u'ΓÇæ', '-')
  313.                     continue
  314.             
  315.             if stylesheet is not None:
  316.                 CSSRule = CSSRule
  317.                 import cssutils.css
  318.                 for lb in XPath('//h:ul[@class]|//h:ol[@class]')(root):
  319.                     sel = '.' + lb.get('class')
  320.                     for rule in stylesheet.data.cssRules.rulesOfType(CSSRule.STYLE_RULE):
  321.                         if sel == rule.selectorList.selectorText:
  322.                             rule.style.removeProperty('margin-left')
  323.                             rule.style.removeProperty('padding-left')
  324.                             continue
  325.                     
  326.                 
  327.         
  328.  
  329.     
  330.     def workaround_sony_quirks(self):
  331.         urldefrag = urldefrag
  332.         XPath = XPath
  333.         import calibre.ebooks.oeb.base
  334.         
  335.         def frag_is_at_top(root, frag):
  336.             body = XPath('//h:body')(root)
  337.             if body:
  338.                 body = body[0]
  339.             else:
  340.                 return False
  341.             tree = body.getroottree()
  342.             elem = XPath('//*[@id="%s" or @name="%s"]' % (frag, frag))(root)
  343.             if elem:
  344.                 elem = elem[0]
  345.             else:
  346.                 return False
  347.             path = elem.getpath(elem)
  348.             for el in body.iterdescendants():
  349.                 epath = tree.getpath(el)
  350.                 if epath == path:
  351.                     break
  352.                 
  353.                 if el.text and el.text.strip():
  354.                     return False
  355.                 if not path.startswith(epath):
  356.                     if el.tail and el.tail.strip():
  357.                         return False
  358.                     continue
  359.                 el.tail.strip()
  360.             
  361.             return True
  362.  
  363.         
  364.         def simplify_toc_entry(toc):
  365.             if toc.href:
  366.                 (href, frag) = urldefrag(toc.href)
  367.                 if frag:
  368.                     for x in self.oeb.spine:
  369.                         if x.href == href:
  370.                             if frag_is_at_top(x.data, frag):
  371.                                 self.log.debug('Removing anchor from TOC href:', href + '#' + frag)
  372.                                 toc.href = href
  373.                             
  374.                             break
  375.                             continue
  376.                     
  377.                 
  378.             
  379.             for x in toc:
  380.                 simplify_toc_entry(x)
  381.             
  382.  
  383.         if self.oeb.toc:
  384.             simplify_toc_entry(self.oeb.toc)
  385.         
  386.  
  387.  
  388.