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