home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2011 January / maximum-cd-2011-01.iso / DiscContents / calibre-0.7.26.msi / file_890 (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-10-31  |  16.9 KB  |  519 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 re
  10. import sys
  11. import uuid
  12. import tempfile
  13. from urlparse import urlparse, urlunparse
  14. from urllib import unquote
  15. from functools import partial
  16. from itertools import izip
  17. from calibre.customize.conversion import InputFormatPlugin
  18. from calibre.ebooks.chardet import xml_to_unicode
  19. from calibre.customize.conversion import OptionRecommendation
  20. from calibre.constants import islinux, isfreebsd, iswindows
  21. from calibre import unicode_path
  22. from calibre.utils.localization import get_lang
  23. from calibre.utils.filenames import ascii_filename
  24. from calibre.ebooks.conversion.utils import PreProcessor
  25.  
  26. class Link(object):
  27.     
  28.     def url_to_local_path(cls, url, base):
  29.         path = url.path
  30.         isabs = False
  31.         if iswindows and path.startswith('/'):
  32.             path = path[1:]
  33.             isabs = True
  34.         
  35.         path = urlunparse(('', '', path, url.params, url.query, ''))
  36.         path = unquote(path)
  37.         if isabs or os.path.isabs(path):
  38.             return path
  39.         return os.path.abspath(os.path.join(base, path))
  40.  
  41.     url_to_local_path = classmethod(url_to_local_path)
  42.     
  43.     def __init__(self, url, base):
  44.         self.url = url
  45.         self.parsed_url = urlparse(self.url)
  46.         self.is_local = self.parsed_url.scheme in ('', 'file')
  47.         if self.is_local:
  48.             pass
  49.         self.is_internal = not bool(self.parsed_url.path)
  50.         self.path = None
  51.         self.fragment = unquote(self.parsed_url.fragment)
  52.         if self.is_local and not (self.is_internal):
  53.             self.path = self.url_to_local_path(self.parsed_url, base)
  54.         
  55.  
  56.     
  57.     def __hash__(self):
  58.         if self.path is None:
  59.             return hash(self.url)
  60.         return hash(self.path)
  61.  
  62.     
  63.     def __eq__(self, other):
  64.         return self.path == getattr(other, 'path', other)
  65.  
  66.     
  67.     def __str__(self):
  68.         return u'Link: %s --> %s' % (self.url, self.path)
  69.  
  70.  
  71.  
  72. class IgnoreFile(Exception):
  73.     
  74.     def __init__(self, msg, errno):
  75.         Exception.__init__(self, msg)
  76.         self.doesnt_exist = errno == 2
  77.         self.errno = errno
  78.  
  79.  
  80.  
  81. class HTMLFile(object):
  82.     HTML_PAT = re.compile('<\\s*html', re.IGNORECASE)
  83.     TITLE_PAT = re.compile('<title>([^<>]+)</title>', re.IGNORECASE)
  84.     LINK_PAT = re.compile('<\\s*a\\s+.*?href\\s*=\\s*(?:(?:"(?P<url1>[^"]+)")|(?:\\\'(?P<url2>[^\\\']+)\\\')|(?P<url3>[^\\s>]+))', re.DOTALL | re.IGNORECASE)
  85.     
  86.     def __init__(self, path_to_html_file, level, encoding, verbose, referrer = None):
  87.         self.path = unicode_path(path_to_html_file, abs = True)
  88.         self.title = os.path.splitext(os.path.basename(self.path))[0]
  89.         self.base = os.path.dirname(self.path)
  90.         self.level = level
  91.         self.referrer = referrer
  92.         self.links = []
  93.         
  94.         try:
  95.             
  96.             try:
  97.                 f = _[1]
  98.                 src = f.read()
  99.             finally:
  100.                 pass
  101.  
  102.         except IOError:
  103.             err = None
  104.             msg = 'Could not read from file: %s with error: %s' % (self.path, unicode(err))
  105.             if level == 0:
  106.                 raise IOError(msg)
  107.             level == 0
  108.             raise IgnoreFile(msg, err.errno)
  109.  
  110.         if level > 0:
  111.             pass
  112.         self.is_binary = not bool(self.HTML_PAT.search(src[:4096]))
  113.         if not self.is_binary:
  114.             if encoding is None:
  115.                 encoding = xml_to_unicode(src[:4096], verbose = verbose)[-1]
  116.                 self.encoding = encoding
  117.             else:
  118.                 self.encoding = encoding
  119.             src = src.decode(encoding, 'replace')
  120.             match = self.TITLE_PAT.search(src)
  121.             self.title = None if match is not None else self.title
  122.             self.find_links(src)
  123.         
  124.  
  125.     
  126.     def __eq__(self, other):
  127.         return self.path == getattr(other, 'path', other)
  128.  
  129.     
  130.     def __str__(self):
  131.         return None % (u'HTMLFile:%d:%s:%s', self.level if self.is_binary else 'a', self.path)
  132.  
  133.     
  134.     def __repr__(self):
  135.         return str(self)
  136.  
  137.     
  138.     def find_links(self, src):
  139.         for match in self.LINK_PAT.finditer(src):
  140.             url = None
  141.             for i in ('url1', 'url2', 'url3'):
  142.                 url = match.group(i)
  143.                 if url:
  144.                     break
  145.                     continue
  146.             
  147.             link = self.resolve(url)
  148.             if link not in self.links:
  149.                 self.links.append(link)
  150.                 continue
  151.         
  152.  
  153.     
  154.     def resolve(self, url):
  155.         return Link(url, self.base)
  156.  
  157.  
  158.  
  159. def depth_first(root, flat, visited = set([])):
  160.     yield root
  161.     visited.add(root)
  162.     for link in root.links:
  163.         if link.path is not None and link not in visited:
  164.             
  165.             try:
  166.                 index = flat.index(link)
  167.             except ValueError:
  168.                 continue
  169.  
  170.             hf = flat[index]
  171.             if hf not in visited:
  172.                 yield hf
  173.                 visited.add(hf)
  174.                 for hf in depth_first(hf, flat, visited):
  175.                     if hf not in visited:
  176.                         yield hf
  177.                         visited.add(hf)
  178.                         continue
  179.                 
  180.             
  181.         hf not in visited
  182.     
  183.  
  184.  
  185. def traverse(path_to_html_file, max_levels = sys.maxint, verbose = 0, encoding = None):
  186.     level = 0
  187.     flat = [
  188.         HTMLFile(path_to_html_file, level, encoding, verbose)]
  189.     next_level = list(flat)
  190.     while level < max_levels and len(next_level) > 0:
  191.         level += 1
  192.         nl = []
  193.         for hf in next_level:
  194.             rejects = []
  195.             for link in hf.links:
  196.                 if link.path is None or link.path in flat:
  197.                     continue
  198.                 
  199.                 
  200.                 try:
  201.                     nf = HTMLFile(link.path, level, encoding, verbose, referrer = hf)
  202.                     if nf.is_binary:
  203.                         raise IgnoreFile('%s is a binary file' % nf.path, -1)
  204.                     nf.is_binary
  205.                     nl.append(nf)
  206.                     flat.append(nf)
  207.                 continue
  208.                 except IgnoreFile:
  209.                     err = None
  210.                     rejects.append(link)
  211.                     if not (err.doesnt_exist) or verbose > 1:
  212.                         print repr(err)
  213.                     
  214.                     verbose > 1
  215.                 
  216.  
  217.             
  218.             for link in rejects:
  219.                 hf.links.remove(link)
  220.             
  221.         
  222.         next_level = list(nl)
  223.         continue
  224.         None<EXCEPTION MATCH>IgnoreFile
  225.     orec = sys.getrecursionlimit()
  226.     sys.setrecursionlimit(500000)
  227.     
  228.     try:
  229.         return (flat, list(depth_first(flat[0], flat)))
  230.     finally:
  231.         sys.setrecursionlimit(orec)
  232.  
  233.  
  234.  
  235. def get_filelist(htmlfile, dir, opts, log):
  236.     log.info('Building file list...')
  237.     filelist = None[traverse(htmlfile, max_levels = int(opts.max_levels), verbose = opts.verbose, encoding = opts.input_encoding) if opts.breadth_first else 1]
  238.     if opts.verbose:
  239.         log.debug('\tFound files...')
  240.         for f in filelist:
  241.             log.debug('\t\t', f)
  242.         
  243.     
  244.     return filelist
  245.  
  246.  
  247. class HTMLInput(InputFormatPlugin):
  248.     name = 'HTML Input'
  249.     author = 'Kovid Goyal'
  250.     description = 'Convert HTML and OPF files to an OEB'
  251.     file_types = set([
  252.         'opf',
  253.         'html',
  254.         'htm',
  255.         'xhtml',
  256.         'xhtm',
  257.         'shtm',
  258.         'shtml'])
  259.     options = set([
  260.         OptionRecommendation(name = 'breadth_first', recommended_value = False, level = OptionRecommendation.LOW, help = _('Traverse links in HTML files breadth first. Normally, they are traversed depth first.')),
  261.         OptionRecommendation(name = 'max_levels', recommended_value = 5, level = OptionRecommendation.LOW, help = _('Maximum levels of recursion when following links in HTML files. Must be non-negative. 0 implies that no links in the root HTML file are followed. Default is %default.')),
  262.         OptionRecommendation(name = 'dont_package', recommended_value = False, level = OptionRecommendation.LOW, help = _('Normally this input plugin re-arranges all the input files into a standard folder hierarchy. Only use this option if you know what you are doing as it can result in various nasty side effects in the rest of of the conversion pipeline.')),
  263.         OptionRecommendation(name = 'unwrap_factor', recommended_value = 0, help = _('Average line length for line breaking if the HTML is from a previous partial conversion of a PDF file. Default is %default which disables this.'))])
  264.     
  265.     def convert(self, stream, opts, file_ext, log, accelerators):
  266.         self._is_case_sensitive = None
  267.         basedir = os.getcwd()
  268.         self.opts = opts
  269.         fname = None
  270.         if hasattr(stream, 'name'):
  271.             basedir = os.path.dirname(stream.name)
  272.             fname = os.path.basename(stream.name)
  273.         
  274.         if file_ext != 'opf':
  275.             if opts.dont_package:
  276.                 raise ValueError('The --dont-package option is not supported for an HTML input file')
  277.             opts.dont_package
  278.             get_metadata = get_metadata
  279.             import calibre.ebooks.metadata.html
  280.             mi = get_metadata(stream)
  281.             if fname:
  282.                 metadata_from_filename = metadata_from_filename
  283.                 import calibre.ebooks.metadata.meta
  284.                 fmi = metadata_from_filename(fname)
  285.                 fmi.smart_update(mi)
  286.                 mi = fmi
  287.             
  288.             oeb = self.create_oebbook(stream.name, basedir, opts, log, mi)
  289.             return oeb
  290.         create_oebbook = create_oebbook
  291.         import calibre.ebooks.conversion.plumber
  292.         return create_oebbook(log, stream.name, opts, self, encoding = opts.input_encoding)
  293.  
  294.     
  295.     def is_case_sensitive(self, path):
  296.         if getattr(self, '_is_case_sensitive', None) is not None:
  297.             return self._is_case_sensitive
  298.         if not path or not os.path.exists(path):
  299.             if not islinux:
  300.                 pass
  301.             return isfreebsd
  302.         if os.path.exists(path.lower()):
  303.             pass
  304.         self._is_case_sensitive = not os.path.exists(path.upper())
  305.         return self._is_case_sensitive
  306.  
  307.     
  308.     def create_oebbook(self, htmlpath, basedir, opts, log, mi):
  309.         create_oebbook = create_oebbook
  310.         import calibre.ebooks.conversion.plumber
  311.         DirContainer = DirContainer
  312.         rewrite_links = rewrite_links
  313.         urlnormalize = urlnormalize
  314.         urldefrag = urldefrag
  315.         BINARY_MIME = BINARY_MIME
  316.         OEB_STYLES = OEB_STYLES
  317.         xpath = xpath
  318.         import calibre.ebooks.oeb.base
  319.         guess_type = guess_type
  320.         import calibre
  321.         import cssutils
  322.         self.OEB_STYLES = OEB_STYLES
  323.         oeb = create_oebbook(log, None, opts, self, encoding = opts.input_encoding, populate = False)
  324.         self.oeb = oeb
  325.         metadata = oeb.metadata
  326.         if mi.title:
  327.             metadata.add('title', mi.title)
  328.         
  329.         if mi.authors:
  330.             for a in mi.authors:
  331.                 metadata.add('creator', a, attrib = {
  332.                     'role': 'aut' })
  333.             
  334.         
  335.         if mi.publisher:
  336.             metadata.add('publisher', mi.publisher)
  337.         
  338.         if mi.isbn:
  339.             metadata.add('identifier', mi.isbn, attrib = {
  340.                 'scheme': 'ISBN' })
  341.         
  342.         if not metadata.language:
  343.             oeb.logger.warn(u'Language not specified')
  344.             metadata.add('language', get_lang().replace('_', '-'))
  345.         
  346.         if not metadata.creator:
  347.             oeb.logger.warn('Creator not specified')
  348.             metadata.add('creator', self.oeb.translate(__('Unknown')))
  349.         
  350.         if not metadata.title:
  351.             oeb.logger.warn('Title not specified')
  352.             metadata.add('title', self.oeb.translate(__('Unknown')))
  353.         
  354.         bookid = str(uuid.uuid4())
  355.         metadata.add('identifier', bookid, id = 'uuid_id', scheme = 'uuid')
  356.         for ident in metadata.identifier:
  357.             if 'id' in ident.attrib:
  358.                 self.oeb.uid = metadata.identifier[0]
  359.                 break
  360.                 continue
  361.         
  362.         filelist = get_filelist(htmlpath, basedir, opts, log)
  363.         filelist = _[1]
  364.         htmlfile_map = { }
  365.         for f in filelist:
  366.             path = f.path
  367.             oeb.container = DirContainer(os.path.dirname(path), log)
  368.             bname = os.path.basename(path)
  369.             (id, href) = oeb.manifest.generate(id = 'html', href = ascii_filename(bname))
  370.             htmlfile_map[path] = href
  371.             item = oeb.manifest.add(id, href, 'text/html')
  372.             item.html_input_href = bname
  373.             oeb.spine.add(item, True)
  374.         
  375.         self.added_resources = { }
  376.         self.log = log
  377.         self.log('Normalizing filename cases')
  378.         for path, href in htmlfile_map.items():
  379.             self.added_resources[path] = href
  380.         
  381.         self.urlnormalize = urlnormalize
  382.         self.DirContainer = DirContainer
  383.         self.urldefrag = urldefrag
  384.         self.guess_type = guess_type
  385.         self.BINARY_MIME = BINARY_MIME
  386.         self.log('Rewriting HTML links')
  387.         for f in filelist:
  388.             path = f.path
  389.             dpath = os.path.dirname(path)
  390.             oeb.container = DirContainer(dpath, log)
  391.             item = oeb.manifest.hrefs[htmlfile_map[path]]
  392.             rewrite_links(item.data, partial(self.resource_adder, base = dpath))
  393.         
  394.         for item in oeb.manifest.values():
  395.             if item.media_type in self.OEB_STYLES:
  396.                 dpath = None
  397.                 for path, href in self.added_resources.items():
  398.                     if href == item.href:
  399.                         dpath = os.path.dirname(path)
  400.                         break
  401.                         continue
  402.                     None if not self.is_case_sensitive(path) else []
  403.                 
  404.                 cssutils.replaceUrls(item.data, partial(self.resource_adder, base = dpath))
  405.                 continue
  406.         
  407.         toc = self.oeb.toc
  408.         self.oeb.auto_generated_toc = True
  409.         titles = []
  410.         headers = []
  411.         for item in self.oeb.spine:
  412.             if not item.linear:
  413.                 continue
  414.             
  415.             html = item.data
  416.             title = ''.join(xpath(html, '/h:html/h:head/h:title/text()'))
  417.             title = re.sub('\\s+', ' ', title.strip())
  418.             if title:
  419.                 titles.append(title)
  420.             
  421.             headers.append('(unlabled)')
  422.             for tag in ('h1', 'h2', 'h3', 'h4', 'h5', 'strong'):
  423.                 expr = '/h:html/h:body//h:%s[position()=1]/text()'
  424.                 header = ''.join(xpath(html, expr % tag))
  425.                 header = re.sub('\\s+', ' ', header.strip())
  426.                 if header:
  427.                     headers[-1] = header
  428.                     break
  429.                     continue
  430.             
  431.         
  432.         use = titles
  433.         if len(titles) > len(set(titles)):
  434.             use = headers
  435.         
  436.         for title, item in izip(use, self.oeb.spine):
  437.             if not item.linear:
  438.                 continue
  439.             
  440.             toc.add(title, item.href)
  441.         
  442.         oeb.container = DirContainer(os.getcwdu(), oeb.log)
  443.         return oeb
  444.  
  445.     
  446.     def link_to_local_path(self, link_, base = None):
  447.         if not isinstance(link_, unicode):
  448.             
  449.             try:
  450.                 link_ = link_.decode('utf-8', 'error')
  451.             self.log.warn('Failed to decode link %r. Ignoring' % link_)
  452.             return (None, None)
  453.  
  454.         
  455.         
  456.         try:
  457.             l = None(Link, link_ if base else os.getcwdu())
  458.         except:
  459.             self.log.exception('Failed to process link: %r' % link_)
  460.             return (None, None)
  461.  
  462.         if l.path is None:
  463.             return (None, None)
  464.         link = l.path.replace('/', os.sep).strip()
  465.         frag = l.fragment
  466.         if not link:
  467.             return (None, None)
  468.         return (link, frag)
  469.  
  470.     
  471.     def resource_adder(self, link_, base = None):
  472.         (link, frag) = self.link_to_local_path(link_, base = base)
  473.         if link is None:
  474.             return link_
  475.         
  476.         try:
  477.             if base and not os.path.isabs(link):
  478.                 link = os.path.join(base, link)
  479.             
  480.             link = os.path.abspath(link)
  481.         except:
  482.             link is None
  483.             return link_
  484.  
  485.         if not os.access(link, os.R_OK):
  486.             return link_
  487.         if os.path.isdir(link):
  488.             self.log.warn(link_, 'is a link to a directory. Ignoring.')
  489.             return link_
  490.         nlink = self.added_resources[link]
  491.         if frag:
  492.             nlink = '#'.join((nlink, frag))
  493.         
  494.         return nlink
  495.  
  496.     
  497.     def css_import_handler(self, base, href):
  498.         (link, frag) = self.link_to_local_path(href, base = base)
  499.         if link is None and not os.access(link, os.R_OK) or os.path.isdir(link):
  500.             return (None, None)
  501.         
  502.         try:
  503.             raw = open(link, 'rb').read().decode('utf-8', 'replace')
  504.             raw = self.oeb.css_preprocessor(raw, add_namespace = True)
  505.         except:
  506.             os.path.isdir(link)
  507.             self.log.exception('Failed to read CSS file: %r' % link)
  508.             return (None, None)
  509.  
  510.         return (None, raw)
  511.  
  512.     
  513.     def preprocess_html(self, options, html):
  514.         self.options = options
  515.         preprocessor = PreProcessor(self.options, log = getattr(self, 'log', None))
  516.         return preprocessor(html)
  517.  
  518.  
  519.