home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2010 November / maximum-cd-2010-11.iso / DiscContents / calibre-0.7.13.msi / file_1002 (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-08-06  |  14.9 KB  |  453 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__ = '2008, Marshall T. Vandegrift <llasram@gmail.com>'
  7. import re
  8. import operator
  9. import math
  10. from collections import defaultdict
  11. from lxml import etree
  12. from calibre.ebooks.oeb.base import XHTML, XHTML_NS
  13. from calibre.ebooks.oeb.base import CSS_MIME, OEB_STYLES
  14. from calibre.ebooks.oeb.base import namespace, barename
  15. from calibre.ebooks.oeb.stylizer import Stylizer
  16. COLLAPSE = re.compile('[ \\t\\r\\n\\v]+')
  17. STRIPNUM = re.compile('[-0-9]+$')
  18.  
  19. def asfloat(value, default):
  20.     if not isinstance(value, (int, long, float)):
  21.         value = default
  22.     
  23.     return float(value)
  24.  
  25.  
  26. def dynamic_rescale_factor(node):
  27.     classes = node.get('class', '').split(' ')
  28.     classes = _[1]
  29.     if not classes:
  30.         return None
  31.     factor = 1
  32.     for x in classes:
  33.         
  34.         try:
  35.             factor *= float(x) / 100
  36.         continue
  37.         except ValueError:
  38.             classes
  39.             classes
  40.             []
  41.             continue
  42.             continue
  43.             []
  44.         
  45.  
  46.     
  47.     return factor
  48.  
  49.  
  50. class KeyMapper(object):
  51.     
  52.     def __init__(self, sbase, dbase, dkey):
  53.         self.sbase = float(sbase)
  54.         self.dprop = [ (self.relate(x, dbase), float(x)) for x in dkey ]
  55.         self.cache = { }
  56.  
  57.     
  58.     def relate(size, base):
  59.         if size == 0:
  60.             return base
  61.         size = float(size)
  62.         base = float(base)
  63.         if abs(size - base) < 0.1:
  64.             return 0
  65.         sign = abs(size - base) < 0.1 if size < base else 1
  66.         endp = size == 0 if size < base else 36
  67.         diff = abs(base - size) * 3 + (36 - size) / 100
  68.         logb = abs(base - endp)
  69.         result = sign * math.log(diff, logb)
  70.         return result
  71.  
  72.     relate = staticmethod(relate)
  73.     
  74.     def __getitem__(self, ssize):
  75.         ssize = asfloat(ssize, 0)
  76.         if ssize in self.cache:
  77.             return self.cache[ssize]
  78.         dsize = self.map(ssize)
  79.         self.cache[ssize] = dsize
  80.         return dsize
  81.  
  82.     
  83.     def map(self, ssize):
  84.         sbase = self.sbase
  85.         prop = self.relate(ssize, sbase)
  86.         diff = [ (abs(prop - p), s) for p, s in self.dprop ]
  87.         dsize = min(diff)[1]
  88.         return dsize
  89.  
  90.  
  91.  
  92. class ScaleMapper(object):
  93.     
  94.     def __init__(self, sbase, dbase):
  95.         self.dscale = float(dbase) / float(sbase)
  96.  
  97.     
  98.     def __getitem__(self, ssize):
  99.         ssize = asfloat(ssize, 0)
  100.         dsize = ssize * self.dscale
  101.         return dsize
  102.  
  103.  
  104.  
  105. class NullMapper(object):
  106.     
  107.     def __init__(self):
  108.         pass
  109.  
  110.     
  111.     def __getitem__(self, ssize):
  112.         return ssize
  113.  
  114.  
  115.  
  116. def FontMapper(sbase = None, dbase = None, dkey = None):
  117.     if sbase and dbase and dkey:
  118.         return KeyMapper(sbase, dbase, dkey)
  119.     if sbase and dbase:
  120.         return ScaleMapper(sbase, dbase)
  121.     return NullMapper()
  122.  
  123.  
  124. class CSSFlattener(object):
  125.     
  126.     def __init__(self, fbase = None, fkey = None, lineh = None, unfloat = False, untable = False):
  127.         self.fbase = fbase
  128.         self.fkey = fkey
  129.         self.lineh = lineh
  130.         self.unfloat = unfloat
  131.         self.untable = untable
  132.  
  133.     
  134.     def config(cls, cfg):
  135.         return cfg
  136.  
  137.     config = classmethod(config)
  138.     
  139.     def generate(cls, opts):
  140.         return cls()
  141.  
  142.     generate = classmethod(generate)
  143.     
  144.     def __call__(self, oeb, context):
  145.         oeb.logger.info('Flattening CSS and remapping font sizes...')
  146.         self.oeb = oeb
  147.         self.context = context
  148.         self.stylize_spine()
  149.         self.sbase = None if self.fbase else None
  150.         self.fmap = FontMapper(self.sbase, self.fbase, self.fkey)
  151.         self.flatten_spine()
  152.  
  153.     
  154.     def stylize_spine(self):
  155.         self.stylizers = { }
  156.         profile = self.context.source
  157.         css = ''
  158.         for item in self.oeb.spine:
  159.             html = item.data
  160.             body = html.find(XHTML('body'))
  161.             bs = body.get('style', '').split(';')
  162.             bs.append('margin-top: 0pt')
  163.             bs.append('margin-bottom: 0pt')
  164.             bs.append('margin-left : %fpt' % float(self.context.margin_left))
  165.             bs.append('margin-right : %fpt' % float(self.context.margin_right))
  166.             if self.context.change_justification != 'original':
  167.                 bs.append('text-align: ' + self.context.change_justification)
  168.             
  169.             body.set('style', '; '.join(bs))
  170.             stylizer = Stylizer(html, item.href, self.oeb, self.context, profile, user_css = self.context.extra_css, extra_css = css)
  171.             self.stylizers[item] = stylizer
  172.         
  173.  
  174.     
  175.     def baseline_node(self, node, stylizer, sizes, csize):
  176.         csize = stylizer.style(node)['font-size']
  177.         if node.text:
  178.             sizes[csize] += len(COLLAPSE.sub(' ', node.text))
  179.         
  180.         for child in node:
  181.             self.baseline_node(child, stylizer, sizes, csize)
  182.             if child.tail:
  183.                 sizes[csize] += len(COLLAPSE.sub(' ', child.tail))
  184.                 continue
  185.         
  186.  
  187.     
  188.     def baseline_spine(self):
  189.         sizes = defaultdict(float)
  190.         for item in self.oeb.spine:
  191.             html = item.data
  192.             stylizer = self.stylizers[item]
  193.             body = html.find(XHTML('body'))
  194.             fsize = self.context.source.fbase
  195.             self.baseline_node(body, stylizer, sizes, fsize)
  196.         
  197.         
  198.         try:
  199.             sbase = max(sizes.items(), key = operator.itemgetter(1))[0]
  200.         except:
  201.             sbase = 12
  202.  
  203.         self.oeb.logger.info('Source base font size is %0.05fpt' % sbase)
  204.         return sbase
  205.  
  206.     
  207.     def clean_edges(self, cssdict, style, fsize):
  208.         slineh = self.sbase * 1.26
  209.         dlineh = self.lineh
  210.         for kind in ('margin', 'padding'):
  211.             for edge in ('bottom', 'top'):
  212.                 property = '%s-%s' % (kind, edge)
  213.                 if property not in cssdict:
  214.                     continue
  215.                 
  216.                 if '%' in cssdict[property]:
  217.                     continue
  218.                 
  219.                 value = style[property]
  220.                 if value == 0:
  221.                     continue
  222.                     continue
  223.                 if value <= slineh:
  224.                     cssdict[property] = '%0.5fem' % dlineh / fsize
  225.                     continue
  226.                 
  227.                 try:
  228.                     value = round(value / slineh) * dlineh
  229.                 except:
  230.                     self.oeb.logger.warning('Invalid length:', value)
  231.                     value = 0
  232.  
  233.                 cssdict[property] = '%0.5fem' % value / fsize
  234.             
  235.         
  236.  
  237.     
  238.     def flatten_node(self, node, stylizer, names, styles, psize, left = 0):
  239.         if not isinstance(node.tag, basestring) or namespace(node.tag) != XHTML_NS:
  240.             return None
  241.         tag = barename(node.tag)
  242.         style = stylizer.style(node)
  243.         cssdict = style.cssdict()
  244.         
  245.         try:
  246.             font_size = style['font-size']
  247.         except:
  248.             namespace(node.tag) != XHTML_NS
  249.             font_size = None if self.sbase is not None else self.context.source.fbase
  250.  
  251.         if 'align' in node.attrib:
  252.             cssdict['text-align'] = node.attrib['align']
  253.             del node.attrib['align']
  254.         
  255.         if node.tag == XHTML('font'):
  256.             node.tag = XHTML('span')
  257.             if 'size' in node.attrib:
  258.                 
  259.                 def force_int(raw):
  260.                     return int(re.search('([0-9+-]+)', raw).group(1))
  261.  
  262.                 size = node.attrib['size'].strip()
  263.                 if size:
  264.                     fnums = self.context.source.fnums
  265.                     if size[0] in ('+', '-'):
  266.                         esize = 3 + force_int(size)
  267.                         if esize < 1:
  268.                             esize = 1
  269.                         
  270.                         if esize > 7:
  271.                             esize = 7
  272.                         
  273.                         font_size = fnums[esize]
  274.                     else:
  275.                         
  276.                         try:
  277.                             font_size = fnums[force_int(size)]
  278.                         except:
  279.                             font_size = fnums[3]
  280.  
  281.                     cssdict['font-size'] = '%.1fpt' % font_size
  282.                 
  283.                 del node.attrib['size']
  284.             
  285.             if 'face' in node.attrib:
  286.                 del node.attrib['face']
  287.             
  288.         
  289.         if 'color' in node.attrib:
  290.             cssdict['color'] = node.attrib['color']
  291.             del node.attrib['color']
  292.         
  293.         if 'bgcolor' in node.attrib:
  294.             cssdict['background-color'] = node.attrib['bgcolor']
  295.             del node.attrib['bgcolor']
  296.         
  297.         if cssdict.get('font-weight', '').lower() == 'medium':
  298.             cssdict['font-weight'] = 'normal'
  299.         
  300.         if not self.context.disable_font_rescaling:
  301.             _sbase = None if self.sbase is not None else self.context.source.fbase
  302.             dyn_rescale = dynamic_rescale_factor(node)
  303.             if dyn_rescale is not None:
  304.                 fsize = self.fmap[_sbase]
  305.                 fsize *= dyn_rescale
  306.                 cssdict['font-size'] = '%0.5fem' % fsize / psize
  307.                 psize = fsize
  308.             elif 'font-size' in cssdict or tag == 'body':
  309.                 fsize = self.fmap[font_size]
  310.                 cssdict['font-size'] = '%0.5fem' % fsize / psize
  311.                 psize = fsize
  312.             
  313.         
  314.         if cssdict:
  315.             if self.lineh and self.fbase and tag != 'body':
  316.                 self.clean_edges(cssdict, style, psize)
  317.             
  318.             margin = asfloat(style['margin-left'], 0)
  319.             indent = asfloat(style['text-indent'], 0)
  320.             left += margin
  321.             if left + indent < 0:
  322.                 
  323.                 try:
  324.                     percent = (margin - indent) / style['width']
  325.                     cssdict['margin-left'] = '%d%%' % percent * 100
  326.                 except ZeroDivisionError:
  327.                     pass
  328.  
  329.                 left -= indent
  330.             
  331.             if 'display' in cssdict and cssdict['display'] == 'in-line':
  332.                 cssdict['display'] = 'inline'
  333.             
  334.             if self.unfloat and 'float' in cssdict and cssdict.get('display', 'none') != 'none':
  335.                 del cssdict['display']
  336.             
  337.             if self.untable and 'display' in cssdict and cssdict['display'].startswith('table'):
  338.                 display = cssdict['display']
  339.                 if display == 'table-cell':
  340.                     cssdict['display'] = 'inline'
  341.                 else:
  342.                     cssdict['display'] = 'block'
  343.             
  344.             if 'vertical-align' in cssdict and cssdict['vertical-align'] == 'sup':
  345.                 cssdict['vertical-align'] = 'super'
  346.             
  347.         
  348.         if self.lineh and 'line-height' not in cssdict:
  349.             lineh = self.lineh / psize
  350.             cssdict['line-height'] = '%0.5fem' % lineh
  351.         
  352.         if (self.context.remove_paragraph_spacing or self.context.insert_blank_line) and tag in ('p', 'div'):
  353.             for prop in ('margin', 'padding', 'border'):
  354.                 for edge in ('top', 'bottom'):
  355.                     cssdict['%s-%s' % (prop, edge)] = '0pt'
  356.                 
  357.             
  358.             if self.context.insert_blank_line:
  359.                 cssdict['margin-top'] = cssdict['margin-bottom'] = '0.5em'
  360.             
  361.             if self.context.remove_paragraph_spacing:
  362.                 cssdict['text-indent'] = '%1.1fem' % self.context.remove_paragraph_spacing_indent_size
  363.             
  364.         
  365.         if cssdict:
  366.             items = cssdict.items()
  367.             items.sort()
  368.             css = u';\n'.join((lambda .0: for key, val in .0:
  369. u'%s: %s' % (key, val))(items))
  370.             if not node.get('class', '').strip():
  371.                 pass
  372.             classes = 'calibre'
  373.             klass = STRIPNUM.sub('', classes.split()[0].replace('_', ''))
  374.             if css in styles:
  375.                 match = styles[css]
  376.             elif not names[klass]:
  377.                 pass
  378.             match = klass + str('')
  379.             styles[css] = match
  380.             names[klass] += 1
  381.             node.attrib['class'] = match
  382.         elif 'class' in node.attrib:
  383.             del node.attrib['class']
  384.         
  385.         if 'style' in node.attrib:
  386.             del node.attrib['style']
  387.         
  388.         for child in node:
  389.             self.flatten_node(child, stylizer, names, styles, psize, left)
  390.         
  391.  
  392.     
  393.     def flatten_head(self, item, stylizer, href):
  394.         html = item.data
  395.         head = html.find(XHTML('head'))
  396.         for node in head:
  397.             if node.tag == XHTML('link') and node.get('rel', 'stylesheet') == 'stylesheet' and node.get('type', CSS_MIME) in OEB_STYLES:
  398.                 head.remove(node)
  399.                 continue
  400.             if node.tag == XHTML('style') and node.get('type', CSS_MIME) in OEB_STYLES:
  401.                 head.remove(node)
  402.                 continue
  403.         
  404.         href = item.relhref(href)
  405.         etree.SubElement(head, XHTML('link'), rel = 'stylesheet', type = CSS_MIME, href = href)
  406.         stylizer.page_rule['margin-top'] = '%fpt' % float(self.context.margin_top)
  407.         stylizer.page_rule['margin-bottom'] = '%fpt' % float(self.context.margin_bottom)
  408.         items = stylizer.page_rule.items()
  409.         items.sort()
  410.         css = '; '.join((lambda .0: for key, val in .0:
  411. '%s: %s' % (key, val))(items))
  412.         style = etree.SubElement(head, XHTML('style'), type = CSS_MIME)
  413.         style.text = '\n\t\t@page { %s; }' % css
  414.         rules = [ r.cssText for r in stylizer.font_face_rules ]
  415.         for r in rules:
  416.             style.text += '\n\t\t' + r + '\n'
  417.         
  418.  
  419.     
  420.     def replace_css(self, css):
  421.         manifest = self.oeb.manifest
  422.         (id, href) = manifest.generate('css', 'stylesheet.css')
  423.         for item in manifest.values():
  424.             if item.media_type in OEB_STYLES:
  425.                 manifest.remove(item)
  426.                 continue
  427.         
  428.         item = manifest.add(id, href, CSS_MIME, data = css)
  429.         return href
  430.  
  431.     
  432.     def flatten_spine(self):
  433.         names = defaultdict(int)
  434.         styles = { }
  435.         for item in self.oeb.spine:
  436.             html = item.data
  437.             stylizer = self.stylizers[item]
  438.             body = html.find(XHTML('body'))
  439.             fsize = self.context.dest.fbase
  440.             self.flatten_node(body, stylizer, names, styles, fsize)
  441.         
  442.         items = [ (key, val) for val, key in styles.items() ]
  443.         items.sort()
  444.         css = ''.join((lambda .0: for key, val in .0:
  445. '.%s {\n%s;\n}\n\n' % (key, val))(items))
  446.         href = self.replace_css(css)
  447.         for item in self.oeb.spine:
  448.             stylizer = self.stylizers[item]
  449.             self.flatten_head(item, stylizer, href)
  450.         
  451.  
  452.  
  453.