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

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. __license__ = 'GPL v3'
  5. __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
  6. __docformat__ = 'restructuredtext en'
  7. import os
  8. import posixpath
  9. import urllib
  10. import sys
  11. import re
  12. from lxml import etree
  13. from lxml.etree import XMLSyntaxError
  14. from calibre.ebooks.epub.fix import InvalidEpub, ParseError
  15. from calibre import guess_type, prepare_string_for_xml
  16. from calibre.ebooks.chardet import xml_to_unicode
  17. from calibre.constants import iswindows
  18. from calibre.utils.zipfile import ZipFile, ZIP_STORED
  19. exists = os.path.exists
  20. join = os.path.join
  21. OCF_NS = 'urn:oasis:names:tc:opendocument:xmlns:container'
  22. OPF_NS = 'http://www.idpf.org/2007/opf'
  23.  
  24. class Container(object):
  25.     META_INF = {
  26.         'container.xml': True,
  27.         'manifest.xml': False,
  28.         'encryption.xml': False,
  29.         'metadata.xml': False,
  30.         'signatures.xml': False,
  31.         'rights.xml': False }
  32.     
  33.     def __init__(self, path, log):
  34.         self.root = os.path.abspath(path)
  35.         self.log = log
  36.         self.dirtied = set([])
  37.         self.cache = { }
  38.         self.mime_map = { }
  39.         if exists(join(self.root, 'mimetype')):
  40.             os.remove(join(self.root, 'mimetype'))
  41.         
  42.         container_path = join(self.root, 'META-INF', 'container.xml')
  43.         if not exists(container_path):
  44.             raise InvalidEpub('No META-INF/container.xml in epub')
  45.         exists(container_path)
  46.         self.container = etree.fromstring(open(container_path, 'rb').read())
  47.         opf_files = self.container.xpath('child::ocf:rootfiles/ocf:rootfile[@media-type="%s" and @full-path]' % guess_type('a.opf')[0], namespaces = {
  48.             'ocf': OCF_NS })
  49.         if not opf_files:
  50.             raise InvalidEpub('META-INF/container.xml contains no link to OPF file')
  51.         opf_files
  52.         opf_path = os.path.join(self.root, *opf_files[0].get('full-path').split('/'))
  53.         if not exists(opf_path):
  54.             raise InvalidEpub('OPF file does not exist at location pointed to by META-INF/container.xml')
  55.         exists(opf_path)
  56.         self.name_map = { }
  57.         for dirpath, dirnames, filenames in os.walk(self.root):
  58.             for f in filenames:
  59.                 path = join(dirpath, f)
  60.                 name = os.path.relpath(path, self.root).replace(os.sep, '/')
  61.                 self.name_map[name] = path
  62.                 if path == opf_path:
  63.                     self.opf_name = name
  64.                     self.mime_map[name] = guess_type('a.opf')[0]
  65.                     continue
  66.             
  67.         
  68.         for item in self.opf.xpath('//opf:manifest/opf:item[@href and @media-type]', namespaces = {
  69.             'opf': OPF_NS }):
  70.             href = item.get('href')
  71.             self.mime_map[self.href_to_name(href, posixpath.dirname(self.opf_name))] = item.get('media-type')
  72.         
  73.  
  74.     
  75.     def manifest_worthy_names(self):
  76.         for name in self.name_map:
  77.             if name.endswith('.opf'):
  78.                 continue
  79.             
  80.             if name.startswith('META-INF') and posixpath.basename(name) in self.META_INF:
  81.                 continue
  82.             
  83.             yield name
  84.         
  85.  
  86.     
  87.     def delete_name(self, name):
  88.         self.mime_map.pop(name, None)
  89.         path = self.name_map[name]
  90.         os.remove(path)
  91.         self.name_map.pop(name)
  92.  
  93.     
  94.     def manifest_item_for_name(self, name):
  95.         href = self.name_to_href(name, posixpath.dirname(self.opf_name))
  96.         q = prepare_string_for_xml(href, attribute = True)
  97.         existing = self.opf.xpath('//opf:manifest/opf:item[@href="%s"]' % q, namespaces = {
  98.             'opf': OPF_NS })
  99.         if not existing:
  100.             return None
  101.         return existing[0]
  102.  
  103.     
  104.     def add_name_to_manifest(self, name):
  105.         item = self.manifest_item_for_name(name)
  106.         if item is not None:
  107.             return None
  108.         manifest = self.opf.xpath('//opf:manifest', namespaces = {
  109.             'opf': OPF_NS })[0]
  110.         item = manifest.makeelement('{%s}item' % OPF_NS, nsmap = {
  111.             'opf': OPF_NS }, href = self.name_to_href(name, posixpath.dirname(self.opf_name)), id = self.generate_manifest_id())
  112.         mt = guess_type(posixpath.basename(name))[0]
  113.         if not mt:
  114.             mt = 'application/octest-stream'
  115.         
  116.         item.set('media-type', mt)
  117.         manifest.append(item)
  118.  
  119.     
  120.     def generate_manifest_id(self):
  121.         items = self.opf.xpath('//opf:manifest/opf:item[@id]', namespaces = {
  122.             'opf': OPF_NS })
  123.         ids = []([ x.get('id') for x in items ])
  124.         for x in xrange(sys.maxint):
  125.             c = 'id%d' % x
  126.             if c not in ids:
  127.                 return c
  128.         
  129.  
  130.     
  131.     def opf(self):
  132.         return self.get(self.opf_name)
  133.  
  134.     opf = property(opf)
  135.     
  136.     def href_to_name(self, href, base = ''):
  137.         href = urllib.unquote(href.partition('#')[0])
  138.         name = href
  139.         if base:
  140.             name = posixpath.join(base, href)
  141.         
  142.         return name
  143.  
  144.     
  145.     def name_to_href(self, name, base):
  146.         if not base:
  147.             return name
  148.         return posixpath.relpath(name, base)
  149.  
  150.     
  151.     def get_raw(self, name):
  152.         path = self.name_map[name]
  153.         return open(path, 'rb').read()
  154.  
  155.     
  156.     def get(self, name):
  157.         if name in self.cache:
  158.             return self.cache[name]
  159.         raw = self.get_raw(name)
  160.         if name in self.mime_map:
  161.             
  162.             try:
  163.                 raw = self._parse(raw, self.mime_map[name])
  164.             except XMLSyntaxError:
  165.                 name in self.cache
  166.                 err = name in self.cache
  167.                 raise ParseError(name, unicode(err))
  168.             except:
  169.                 name in self.cache<EXCEPTION MATCH>XMLSyntaxError
  170.             
  171.  
  172.         name in self.cache
  173.         self.cache[name] = raw
  174.         return raw
  175.  
  176.     
  177.     def set(self, name, val):
  178.         self.cache[name] = val
  179.         self.dirtied.add(name)
  180.  
  181.     
  182.     def _parse(self, raw, mimetype):
  183.         mt = mimetype.lower()
  184.         if mt.endswith('+xml'):
  185.             parser = etree.XMLParser(no_network = True, huge_tree = not iswindows)
  186.             raw = xml_to_unicode(raw, strip_encoding_pats = True, assume_utf8 = True, resolve_entities = True)[0].strip()
  187.             idx = raw.find('<html')
  188.             if idx == -1:
  189.                 idx = raw.find('<HTML')
  190.             
  191.             if idx > -1:
  192.                 pre = raw[:idx]
  193.                 raw = raw[idx:]
  194.                 if '<!DOCTYPE' in pre:
  195.                     user_entities = { }
  196.                     for match in re.finditer('<!ENTITY\\s+(\\S+)\\s+([^>]+)', pre):
  197.                         val = match.group(2)
  198.                         if val.startswith('"') and val.endswith('"'):
  199.                             val = val[1:-1]
  200.                         
  201.                         user_entities[match.group(1)] = val
  202.                     
  203.                     if user_entities:
  204.                         pat = re.compile('&(%s);' % '|'.join(user_entities.keys()))
  205.                         raw = (pat.sub,)((lambda m: user_entities[m.group(1)]), raw)
  206.                     
  207.                 
  208.             
  209.             return etree.fromstring(raw, parser = parser)
  210.         return raw
  211.  
  212.     
  213.     def write(self, path):
  214.         for name in self.dirtied:
  215.             data = self.cache[name]
  216.             raw = data
  217.             if hasattr(data, 'xpath'):
  218.                 raw = etree.tostring(data, encoding = 'utf-8', xml_declaration = True)
  219.             
  220.             
  221.             try:
  222.                 f = _[1]
  223.                 f.write(raw)
  224.             finally:
  225.                 pass
  226.  
  227.         
  228.         self.dirtied.clear()
  229.         zf = ZipFile(path, 'w')
  230.         zf.writestr('mimetype', bytes(guess_type('a.epub')[0]), compression = ZIP_STORED)
  231.         zf.add_dir(self.root)
  232.         zf.close()
  233.  
  234.  
  235.