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