home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2011 January / maximum-cd-2011-01.iso / DiscContents / calibre-0.7.26.msi / file_1000 (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-10-31  |  15.1 KB  |  463 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.             bs.extend([
  167.                 'padding-left: 0pt',
  168.                 'padding-right: 0pt'])
  169.             if self.context.change_justification != 'original':
  170.                 bs.append('text-align: ' + self.context.change_justification)
  171.             
  172.             body.set('style', '; '.join(bs))
  173.             stylizer = Stylizer(html, item.href, self.oeb, self.context, profile, user_css = self.context.extra_css, extra_css = css)
  174.             self.stylizers[item] = stylizer
  175.         
  176.  
  177.     
  178.     def baseline_node(self, node, stylizer, sizes, csize):
  179.         csize = stylizer.style(node)['font-size']
  180.         if node.text:
  181.             sizes[csize] += len(COLLAPSE.sub(' ', node.text))
  182.         
  183.         for child in node:
  184.             self.baseline_node(child, stylizer, sizes, csize)
  185.             if child.tail:
  186.                 sizes[csize] += len(COLLAPSE.sub(' ', child.tail))
  187.                 continue
  188.         
  189.  
  190.     
  191.     def baseline_spine(self):
  192.         sizes = defaultdict(float)
  193.         for item in self.oeb.spine:
  194.             html = item.data
  195.             stylizer = self.stylizers[item]
  196.             body = html.find(XHTML('body'))
  197.             fsize = self.context.source.fbase
  198.             self.baseline_node(body, stylizer, sizes, fsize)
  199.         
  200.         
  201.         try:
  202.             sbase = max(sizes.items(), key = operator.itemgetter(1))[0]
  203.         except:
  204.             sbase = 12
  205.  
  206.         self.oeb.logger.info('Source base font size is %0.05fpt' % sbase)
  207.         return sbase
  208.  
  209.     
  210.     def clean_edges(self, cssdict, style, fsize):
  211.         slineh = self.sbase * 1.26
  212.         dlineh = self.lineh
  213.         for kind in ('margin', 'padding'):
  214.             for edge in ('bottom', 'top'):
  215.                 property = '%s-%s' % (kind, edge)
  216.                 if property not in cssdict:
  217.                     continue
  218.                 
  219.                 if '%' in cssdict[property]:
  220.                     continue
  221.                 
  222.                 value = style[property]
  223.                 if value == 0:
  224.                     continue
  225.                     continue
  226.                 if value <= slineh:
  227.                     cssdict[property] = '%0.5fem' % dlineh / fsize
  228.                     continue
  229.                 
  230.                 try:
  231.                     value = round(value / slineh) * dlineh
  232.                 except:
  233.                     self.oeb.logger.warning('Invalid length:', value)
  234.                     value = 0
  235.  
  236.                 cssdict[property] = '%0.5fem' % value / fsize
  237.             
  238.         
  239.  
  240.     
  241.     def flatten_node(self, node, stylizer, names, styles, psize, item_id, left = 0):
  242.         if not isinstance(node.tag, basestring) or namespace(node.tag) != XHTML_NS:
  243.             return None
  244.         tag = barename(node.tag)
  245.         style = stylizer.style(node)
  246.         cssdict = style.cssdict()
  247.         
  248.         try:
  249.             font_size = style['font-size']
  250.         except:
  251.             namespace(node.tag) != XHTML_NS
  252.             font_size = None if self.sbase is not None else self.context.source.fbase
  253.  
  254.         if 'align' in node.attrib:
  255.             cssdict['text-align'] = node.attrib['align']
  256.             del node.attrib['align']
  257.         
  258.         if node.tag == XHTML('font'):
  259.             node.tag = XHTML('span')
  260.             if 'size' in node.attrib:
  261.                 
  262.                 def force_int(raw):
  263.                     return int(re.search('([0-9+-]+)', raw).group(1))
  264.  
  265.                 size = node.attrib['size'].strip()
  266.                 if size:
  267.                     fnums = self.context.source.fnums
  268.                     if size[0] in ('+', '-'):
  269.                         
  270.                         try:
  271.                             esize = 3 + force_int(size)
  272.                         except:
  273.                             esize = 3
  274.  
  275.                         if esize < 1:
  276.                             esize = 1
  277.                         
  278.                         if esize > 7:
  279.                             esize = 7
  280.                         
  281.                         font_size = fnums[esize]
  282.                     else:
  283.                         
  284.                         try:
  285.                             font_size = fnums[force_int(size)]
  286.                         except:
  287.                             font_size = fnums[3]
  288.  
  289.                     cssdict['font-size'] = '%.1fpt' % font_size
  290.                 
  291.                 del node.attrib['size']
  292.             
  293.             if 'face' in node.attrib:
  294.                 del node.attrib['face']
  295.             
  296.         
  297.         if 'color' in node.attrib:
  298.             cssdict['color'] = node.attrib['color']
  299.             del node.attrib['color']
  300.         
  301.         if 'bgcolor' in node.attrib:
  302.             cssdict['background-color'] = node.attrib['bgcolor']
  303.             del node.attrib['bgcolor']
  304.         
  305.         if cssdict.get('font-weight', '').lower() == 'medium':
  306.             cssdict['font-weight'] = 'normal'
  307.         
  308.         if not self.context.disable_font_rescaling:
  309.             _sbase = None if self.sbase is not None else self.context.source.fbase
  310.             dyn_rescale = dynamic_rescale_factor(node)
  311.             if dyn_rescale is not None:
  312.                 fsize = self.fmap[_sbase]
  313.                 fsize *= dyn_rescale
  314.                 cssdict['font-size'] = '%0.5fem' % fsize / psize
  315.                 psize = fsize
  316.             elif 'font-size' in cssdict or tag == 'body':
  317.                 fsize = self.fmap[font_size]
  318.                 cssdict['font-size'] = '%0.5fem' % fsize / psize
  319.                 psize = fsize
  320.             
  321.         
  322.         if cssdict:
  323.             if self.lineh and self.fbase and tag != 'body':
  324.                 self.clean_edges(cssdict, style, psize)
  325.             
  326.             margin = asfloat(style['margin-left'], 0)
  327.             indent = asfloat(style['text-indent'], 0)
  328.             left += margin
  329.             if left + indent < 0:
  330.                 
  331.                 try:
  332.                     percent = (margin - indent) / style['width']
  333.                     cssdict['margin-left'] = '%d%%' % percent * 100
  334.                 except ZeroDivisionError:
  335.                     pass
  336.  
  337.                 left -= indent
  338.             
  339.             if 'display' in cssdict and cssdict['display'] == 'in-line':
  340.                 cssdict['display'] = 'inline'
  341.             
  342.             if self.unfloat and 'float' in cssdict and cssdict.get('display', 'none') != 'none':
  343.                 del cssdict['display']
  344.             
  345.             if self.untable and 'display' in cssdict and cssdict['display'].startswith('table'):
  346.                 display = cssdict['display']
  347.                 if display == 'table-cell':
  348.                     cssdict['display'] = 'inline'
  349.                 else:
  350.                     cssdict['display'] = 'block'
  351.             
  352.             if 'vertical-align' in cssdict and cssdict['vertical-align'] == 'sup':
  353.                 cssdict['vertical-align'] = 'super'
  354.             
  355.         
  356.         if self.lineh and 'line-height' not in cssdict:
  357.             lineh = self.lineh / psize
  358.             cssdict['line-height'] = '%0.5fem' % lineh
  359.         
  360.         if (self.context.remove_paragraph_spacing or self.context.insert_blank_line) and tag in ('p', 'div'):
  361.             if item_id != 'calibre_jacket' or self.context.output_profile.name == 'Kindle':
  362.                 for prop in ('margin', 'padding', 'border'):
  363.                     for edge in ('top', 'bottom'):
  364.                         cssdict['%s-%s' % (prop, edge)] = '0pt'
  365.                     
  366.                 
  367.             
  368.             if self.context.insert_blank_line:
  369.                 cssdict['margin-top'] = cssdict['margin-bottom'] = '0.5em'
  370.             
  371.             if self.context.remove_paragraph_spacing:
  372.                 cssdict['text-indent'] = '%1.1fem' % self.context.remove_paragraph_spacing_indent_size
  373.             
  374.         
  375.         if cssdict:
  376.             items = cssdict.items()
  377.             items.sort()
  378.             css = u';\n'.join((lambda .0: for key, val in .0:
  379. u'%s: %s' % (key, val))(items))
  380.             if not node.get('class', '').strip():
  381.                 pass
  382.             classes = 'calibre'
  383.             klass = STRIPNUM.sub('', classes.split()[0].replace('_', ''))
  384.             if css in styles:
  385.                 match = styles[css]
  386.             elif not names[klass]:
  387.                 pass
  388.             match = klass + str('')
  389.             styles[css] = match
  390.             names[klass] += 1
  391.             node.attrib['class'] = match
  392.         elif 'class' in node.attrib:
  393.             del node.attrib['class']
  394.         
  395.         if 'style' in node.attrib:
  396.             del node.attrib['style']
  397.         
  398.         for child in node:
  399.             self.flatten_node(child, stylizer, names, styles, psize, item_id, left)
  400.         
  401.  
  402.     
  403.     def flatten_head(self, item, stylizer, href):
  404.         html = item.data
  405.         head = html.find(XHTML('head'))
  406.         for node in head:
  407.             if node.tag == XHTML('link') and node.get('rel', 'stylesheet') == 'stylesheet' and node.get('type', CSS_MIME) in OEB_STYLES:
  408.                 head.remove(node)
  409.                 continue
  410.             if node.tag == XHTML('style') and node.get('type', CSS_MIME) in OEB_STYLES:
  411.                 head.remove(node)
  412.                 continue
  413.         
  414.         href = item.relhref(href)
  415.         etree.SubElement(head, XHTML('link'), rel = 'stylesheet', type = CSS_MIME, href = href)
  416.         stylizer.page_rule['margin-top'] = '%fpt' % float(self.context.margin_top)
  417.         stylizer.page_rule['margin-bottom'] = '%fpt' % float(self.context.margin_bottom)
  418.         items = stylizer.page_rule.items()
  419.         items.sort()
  420.         css = '; '.join((lambda .0: for key, val in .0:
  421. '%s: %s' % (key, val))(items))
  422.         style = etree.SubElement(head, XHTML('style'), type = CSS_MIME)
  423.         style.text = '\n\t\t@page { %s; }' % css
  424.         rules = [ r.cssText for r in stylizer.font_face_rules ]
  425.         for r in rules:
  426.             style.text += '\n\t\t' + r + '\n'
  427.         
  428.  
  429.     
  430.     def replace_css(self, css):
  431.         manifest = self.oeb.manifest
  432.         (id, href) = manifest.generate('css', 'stylesheet.css')
  433.         for item in manifest.values():
  434.             if item.media_type in OEB_STYLES:
  435.                 manifest.remove(item)
  436.                 continue
  437.         
  438.         item = manifest.add(id, href, CSS_MIME, data = css)
  439.         return href
  440.  
  441.     
  442.     def flatten_spine(self):
  443.         names = defaultdict(int)
  444.         styles = { }
  445.         for item in self.oeb.spine:
  446.             html = item.data
  447.             stylizer = self.stylizers[item]
  448.             body = html.find(XHTML('body'))
  449.             fsize = self.context.dest.fbase
  450.             self.flatten_node(body, stylizer, names, styles, fsize, item.id)
  451.         
  452.         items = [ (key, val) for val, key in styles.items() ]
  453.         items.sort()
  454.         css = ''.join((lambda .0: for key, val in .0:
  455. '.%s {\n%s;\n}\n\n' % (key, val))(items))
  456.         href = self.replace_css(css)
  457.         for item in self.oeb.spine:
  458.             stylizer = self.stylizers[item]
  459.             self.flatten_head(item, stylizer, href)
  460.         
  461.  
  462.  
  463.