home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2010 November / maximum-cd-2010-11.iso / DiscContents / calibre-0.7.13.msi / file_1303 (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2010-08-06  |  24.0 KB  |  688 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. __license__ = 'GPL v3'
  5. __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
  6. import sys
  7. import collections
  8. import operator
  9. import copy
  10. import re
  11. from PyQt4.QtCore import Qt, QRectF, QString
  12. from PyQt4.QtGui import QFont, QColor, QPixmap, QGraphicsPixmapItem, QGraphicsItem, QFontMetrics, QPen, QBrush, QGraphicsRectItem
  13. from calibre.ebooks.lrf.fonts import FONT_MAP
  14. from calibre.ebooks.BeautifulSoup import Tag
  15. from calibre.ebooks.hyphenate import hyphenate_word
  16.  
  17. WEIGHT_MAP = lambda wt: int(wt / 10 - 1)
  18.  
  19. NULL = lambda a, b: a
  20.  
  21. COLOR = lambda a, b: QColor(*a)
  22.  
  23. WEIGHT = lambda a, b: WEIGHT_MAP(a)
  24.  
  25. class PixmapItem(QGraphicsPixmapItem):
  26.     
  27.     def __init__(self, data, encoding, x0, y0, x1, y1, xsize, ysize):
  28.         p = QPixmap()
  29.         p.loadFromData(data, encoding, Qt.AutoColor)
  30.         w = p.width()
  31.         h = p.height()
  32.         p = p.copy(x0, y0, min(w, x1 - x0), min(h, y1 - y0))
  33.         if p.width() != xsize or p.height() != ysize:
  34.             p = p.scaled(xsize, ysize, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
  35.         
  36.         QGraphicsPixmapItem.__init__(self, p)
  37.         self.height = ysize
  38.         self.width = xsize
  39.         self.setTransformationMode(Qt.SmoothTransformation)
  40.         self.setShapeMode(QGraphicsPixmapItem.BoundingRectShape)
  41.  
  42.     
  43.     def resize(self, width, height):
  44.         p = self.pixmap()
  45.         self.setPixmap(p.scaled(width, height, Qt.IgnoreAspectRatio, Qt.SmoothTransformation))
  46.         self.width = width
  47.         self.height = height
  48.  
  49.  
  50.  
  51. class Plot(PixmapItem):
  52.     
  53.     def __init__(self, plot, dpi):
  54.         img = plot.refobj
  55.         xsize = dpi * plot.attrs['xsize'] / 720
  56.         ysize = dpi * plot.attrs['ysize'] / 720
  57.         (x0, y0, x1, y1) = (img.x0, img.y0, img.x1, img.y1)
  58.         data = img.data
  59.         encoding = img.encoding
  60.         PixmapItem.__init__(self, data, encoding, x0, y0, x1, y1, xsize, ysize)
  61.  
  62.  
  63.  
  64. class FontLoader(object):
  65.     font_map = {
  66.         'Swis721 BT Roman': 'Liberation Sans',
  67.         'Dutch801 Rm BT Roman': 'Liberation Serif',
  68.         'Courier10 BT Roman': 'Liberation Mono' }
  69.     
  70.     def __init__(self, font_map, dpi):
  71.         self.face_map = { }
  72.         self.cache = { }
  73.         self.dpi = dpi
  74.         self.face_map = font_map
  75.  
  76.     
  77.     def font(self, text_style):
  78.         device_font = text_style.fontfacename in FONT_MAP
  79.         
  80.         try:
  81.             if device_font:
  82.                 face = self.font_map[text_style.fontfacename]
  83.             else:
  84.                 face = self.face_map[text_style.fontfacename]
  85.         except KeyError:
  86.             face = self.font_map['Dutch801 Rm BT Roman']
  87.  
  88.         sz = text_style.fontsize
  89.         wt = text_style.fontweight
  90.         style = text_style.fontstyle
  91.         font = (face, wt, style, sz)
  92.         if font in self.cache:
  93.             rfont = self.cache[font]
  94.         else:
  95.             italic = font[2] == QFont.StyleItalic
  96.             rfont = QFont(font[0], font[3], font[1], italic)
  97.             rfont.setPixelSize(font[3])
  98.             rfont.setBold(wt >= 69)
  99.             self.cache[font] = rfont
  100.         qfont = rfont
  101.         if text_style.emplinetype != 'none':
  102.             qfont = QFont(rfont)
  103.             qfont.setOverline(text_style.emplineposition == 'before')
  104.             qfont.setUnderline(text_style.emplineposition == 'after')
  105.         
  106.         return qfont
  107.  
  108.  
  109.  
  110. class Style(object):
  111.     map = collections.defaultdict((lambda : NULL))
  112.     
  113.     def __init__(self, style, dpi):
  114.         self.fdpi = dpi / 720
  115.         self.update(style.as_dict())
  116.  
  117.     
  118.     def update(self, *args, **kwds):
  119.         if len(args) > 0:
  120.             kwds = args[0]
  121.         
  122.         for attr in kwds:
  123.             setattr(self, attr, self.__class__.map[attr](kwds[attr], self.fdpi))
  124.         
  125.  
  126.     
  127.     def copy(self):
  128.         return copy.copy(self)
  129.  
  130.  
  131.  
  132. class TextStyle(Style):
  133.     map = collections.defaultdict((lambda : NULL), fontsize = operator.mul, fontwidth = operator.mul, fontweight = WEIGHT, textcolor = COLOR, textbgcolor = COLOR, wordspace = operator.mul, letterspace = operator.mul, baselineskip = operator.mul, linespace = operator.mul, parindent = operator.mul, parskip = operator.mul, textlinewidth = operator.mul, charspace = operator.mul, linecolor = COLOR)
  134.     
  135.     def __init__(self, style, font_loader, ruby_tags):
  136.         self.font_loader = font_loader
  137.         self.fontstyle = QFont.StyleNormal
  138.         for attr in ruby_tags:
  139.             setattr(self, attr, ruby_tags[attr])
  140.         
  141.         Style.__init__(self, style, font_loader.dpi)
  142.         self.emplinetype = 'none'
  143.         self.font = self.font_loader.font(self)
  144.  
  145.     
  146.     def update(self, *args, **kwds):
  147.         Style.update(self, *args, **kwds)
  148.         self.font = self.font_loader.font(self)
  149.  
  150.  
  151.  
  152. class BlockStyle(Style):
  153.     map = collections.defaultdict((lambda : NULL), bgcolor = COLOR, framecolor = COLOR)
  154.  
  155.  
  156. class ParSkip(object):
  157.     
  158.     def __init__(self, parskip):
  159.         self.height = parskip
  160.  
  161.     
  162.     def __str__(self):
  163.         return 'Parskip: ' + str(self.height)
  164.  
  165.  
  166.  
  167. class TextBlock(object):
  168.     
  169.     class HeightExceeded(Exception):
  170.         pass
  171.  
  172.     has_content = property(fget = (lambda self: self.peek_index < len(self.lines) - 1))
  173.     XML_ENTITIES = dict(zip(Tag.XML_SPECIAL_CHARS_TO_ENTITIES.values(), Tag.XML_SPECIAL_CHARS_TO_ENTITIES.keys()))
  174.     XML_ENTITIES['quot'] = '"'
  175.     
  176.     def __init__(self, tb, font_loader, respect_max_y, text_width, logger, opts, ruby_tags, link_activated):
  177.         self.block_id = tb.id
  178.         self.bs = BlockStyle(tb.style, font_loader.dpi)
  179.         self.ts = TextStyle(tb.textstyle, font_loader, ruby_tags)
  180.         self.bs.update(tb.attrs)
  181.         self.ts.update(tb.attrs)
  182.         self.lines = collections.deque()
  183.         self.line_length = min(self.bs.blockwidth, text_width)
  184.         self.line_length -= 2 * self.bs.sidemargin
  185.         self.line_offset = self.bs.sidemargin
  186.         self.first_line = True
  187.         self.current_style = self.ts.copy()
  188.         self.current_line = None
  189.         self.font_loader = font_loader
  190.         self.logger = logger
  191.         self.opts = opts
  192.         self.in_link = False
  193.         self.link_activated = link_activated
  194.         self.max_y = self if respect_max_y or self.bs.blockrule.lower() in ('vert-fixed', 'block-fixed') else sys.maxint
  195.         self.height = 0
  196.         self.peek_index = -1
  197.         
  198.         try:
  199.             self.populate(tb.content)
  200.             self.end_line()
  201.         except TextBlock.HeightExceeded:
  202.             pass
  203.  
  204.  
  205.     
  206.     def peek(self):
  207.         return self.lines[self.peek_index + 1]
  208.  
  209.     
  210.     def commit(self):
  211.         self.peek_index += 1
  212.  
  213.     
  214.     def reset(self):
  215.         self.peek_index = -1
  216.  
  217.     
  218.     def create_link(self, refobj):
  219.         if self.current_line is None:
  220.             self.create_line()
  221.         
  222.         self.current_line.start_link(refobj, self.link_activated)
  223.         self.link_activated(refobj, on_creation = True)
  224.  
  225.     
  226.     def end_link(self):
  227.         if self.current_line is not None:
  228.             self.current_line.end_link()
  229.         
  230.  
  231.     
  232.     def close_valign(self):
  233.         if self.current_line is not None:
  234.             self.current_line.valign = None
  235.         
  236.  
  237.     
  238.     def populate(self, tb):
  239.         self.create_line()
  240.         open_containers = collections.deque()
  241.         self.in_para = False
  242.         for i in tb.content:
  243.             if isinstance(i, basestring):
  244.                 self.process_text(i)
  245.                 continue
  246.             if i is None:
  247.                 if len(open_containers) > 0:
  248.                     for a, b in open_containers.pop():
  249.                         if callable(a):
  250.                             a(*b)
  251.                             continue
  252.                         setattr(self, a, b)
  253.                     
  254.                 
  255.             len(open_containers) > 0
  256.             if i.name == 'P':
  257.                 open_containers.append((('in_para', False),))
  258.                 self.in_para = True
  259.                 continue
  260.             if i.name == 'CR':
  261.                 if self.in_para:
  262.                     self.end_line()
  263.                     self.create_line()
  264.                 else:
  265.                     self.end_line()
  266.                     delta = self.current_style.parskip
  267.                     if isinstance(self.lines[-1], ParSkip):
  268.                         delta += self.current_style.baselineskip
  269.                     
  270.                     self.lines.append(ParSkip(delta))
  271.                     self.first_line = True
  272.             self.in_para
  273.             if i.name == 'Span':
  274.                 open_containers.append((('current_style', self.current_style.copy()),))
  275.                 self.current_style.update(i.attrs)
  276.                 continue
  277.             if i.name == 'CharButton':
  278.                 open_containers.append(((self.end_link, []),))
  279.                 self.create_link(i.attrs['refobj'])
  280.                 continue
  281.             if i.name == 'Italic':
  282.                 open_containers.append((('current_style', self.current_style.copy()),))
  283.                 self.current_style.update(fontstyle = QFont.StyleItalic)
  284.                 continue
  285.             if i.name == 'Plot':
  286.                 plot = Plot(i, self.font_loader.dpi)
  287.                 if self.current_line is None:
  288.                     self.create_line()
  289.                 
  290.                 if not self.current_line.can_add_plot(plot):
  291.                     self.end_line()
  292.                     self.create_line()
  293.                 
  294.                 self.current_line.add_plot(plot)
  295.                 continue
  296.             if i.name in ('Sup', 'Sub'):
  297.                 if self.current_line is None:
  298.                     self.create_line()
  299.                 
  300.                 self.current_line.valign = i.name
  301.                 open_containers.append(((self.close_valign, []),))
  302.                 continue
  303.             if i.name == 'Space' and self.current_line is not None:
  304.                 self.current_line.add_space(i.attrs['xsize'])
  305.                 continue
  306.             if i.name == 'EmpLine':
  307.                 if i.attrs:
  308.                     open_containers.append((('current_style', self.current_style.copy()),))
  309.                     self.current_style.update(i.attrs)
  310.                 
  311.             i.attrs
  312.             self.logger.warning('Unhandled TextTag %s' % (i.name,))
  313.             if not i.self_closing:
  314.                 open_containers.append([])
  315.                 continue
  316.         
  317.  
  318.     
  319.     def end_line(self):
  320.         if self.current_line is not None:
  321.             self.height += self.current_line.finalize(self.current_style.baselineskip, self.current_style.linespace, self.opts.visual_debug)
  322.             if self.height > self.max_y + 10:
  323.                 raise TextBlock.HeightExceeded(str(self.current_line))
  324.             self.height > self.max_y + 10
  325.             self.lines.append(self.current_line)
  326.             self.current_line = None
  327.         
  328.  
  329.     
  330.     def create_line(self):
  331.         line_length = self.line_length
  332.         line_offset = self.line_offset
  333.         if self.first_line:
  334.             line_length -= self.current_style.parindent
  335.             line_offset += self.current_style.parindent
  336.         
  337.         self.current_line = Line(line_length, line_offset, self.current_style.linespace, self.current_style.align, self.opts.hyphenate, self.block_id)
  338.         self.first_line = False
  339.  
  340.     
  341.     def process_text(self, raw):
  342.         for ent, rep in TextBlock.XML_ENTITIES.items():
  343.             raw = raw.replace(u'&%s;' % ent, rep)
  344.         
  345.         while len(raw) > 0:
  346.             if self.current_line is None:
  347.                 self.create_line()
  348.             
  349.             (pos, line_filled) = self.current_line.populate(raw, self.current_style)
  350.             raw = raw[pos:]
  351.             if line_filled:
  352.                 self.end_line()
  353.                 continue
  354.  
  355.     
  356.     def __iter__(self):
  357.         for line in self.lines:
  358.             yield line
  359.         
  360.  
  361.     
  362.     def __str__(self):
  363.         s = ''
  364.         for line in self:
  365.             s += str(line) + '\n'
  366.         
  367.         return s
  368.  
  369.  
  370.  
  371. class Link(QGraphicsRectItem):
  372.     inactive_brush = QBrush(QColor(255, 255, 255, 255))
  373.     active_brush = QBrush(QColor(0, 0, 0, 89))
  374.     
  375.     def __init__(self, parent, start, stop, refobj, slot):
  376.         QGraphicsRectItem.__init__(self, start, 0, stop - start, parent.height, parent)
  377.         self.refobj = refobj
  378.         self.slot = slot
  379.         self.brush = self.__class__.inactive_brush
  380.         self.setPen(QPen(Qt.NoPen))
  381.         self.setCursor(Qt.PointingHandCursor)
  382.         self.setAcceptsHoverEvents(True)
  383.  
  384.     
  385.     def hoverEnterEvent(self, event):
  386.         self.brush = self.__class__.active_brush
  387.         self.parentItem().update()
  388.  
  389.     
  390.     def hoverLeaveEvent(self, event):
  391.         self.brush = self.__class__.inactive_brush
  392.         self.parentItem().update()
  393.  
  394.     
  395.     def mousePressEvent(self, event):
  396.         self.hoverLeaveEvent(None)
  397.         self.slot(self.refobj)
  398.  
  399.  
  400.  
  401. class Line(QGraphicsItem):
  402.     whitespace = re.compile('\\s+')
  403.     
  404.     def __init__(self, line_length, offset, linespace, align, hyphenate, block_id):
  405.         QGraphicsItem.__init__(self)
  406.         self.line_length = line_length
  407.         self.offset = offset
  408.         self.line_space = linespace
  409.         self.align = align
  410.         self.hyphenate = hyphenate
  411.         self.block_id = block_id
  412.         self.tokens = collections.deque()
  413.         self.current_width = 0
  414.         self.length_in_space = 0
  415.         (self.height, self.descent, self.width) = (0, 0, 0)
  416.         self.links = collections.deque()
  417.         self.current_link = None
  418.         self.valign = None
  419.         if not hasattr(self, 'children'):
  420.             self.children = self.childItems
  421.         
  422.  
  423.     
  424.     def start_link(self, refobj, slot):
  425.         self.current_link = [
  426.             self.current_width,
  427.             sys.maxint,
  428.             refobj,
  429.             slot]
  430.  
  431.     
  432.     def end_link(self):
  433.         if self.current_link is not None:
  434.             self.current_link[1] = self.current_width
  435.             self.links.append(self.current_link)
  436.             self.current_link = None
  437.         
  438.  
  439.     
  440.     def can_add_plot(self, plot):
  441.         return self.line_length - self.current_width >= plot.width
  442.  
  443.     
  444.     def add_plot(self, plot):
  445.         self.tokens.append(plot)
  446.         self.current_width += plot.width
  447.         self.height = max(self.height, plot.height)
  448.         self.add_space(6)
  449.  
  450.     
  451.     def populate(self, phrase, ts, process_space = True):
  452.         phrase_pos = 0
  453.         processed = False
  454.         matches = self.__class__.whitespace.finditer(phrase)
  455.         font = QFont(ts.font)
  456.         if self.valign is not None:
  457.             font.setPixelSize(font.pixelSize() / 1.5)
  458.         
  459.         fm = QFontMetrics(font)
  460.         single_space_width = fm.width(' ')
  461.         height = fm.height()
  462.         descent = fm.descent()
  463.         for match in matches:
  464.             processed = True
  465.             (left, right) = match.span()
  466.             if not process_space:
  467.                 right = left
  468.             
  469.             space_width = single_space_width * (right - left)
  470.             word = phrase[phrase_pos:left]
  471.             width = fm.width(word)
  472.             if self.current_width + width < self.line_length:
  473.                 self.commit(word, width, height, descent, ts, font)
  474.                 if space_width > 0 and self.current_width + space_width < self.line_length:
  475.                     self.add_space(space_width)
  476.                 
  477.                 phrase_pos = right
  478.                 continue
  479.             
  480.             if self.hyphenate and len(word) > 3:
  481.                 tokens = hyphenate_word(word)
  482.                 for i in range(len(tokens) - 2, -1, -1):
  483.                     word = ''.join(tokens[0:i + 1]) + '-'
  484.                     width = fm.width(word)
  485.                     if self.current_width + width < self.line_length:
  486.                         self.commit(word, width, height, descent, ts, font)
  487.                         return (phrase_pos + len(word) - 1, True)
  488.                 
  489.             
  490.             if self.current_width < 5:
  491.                 for i in range(len(word) - 5, 0, -5):
  492.                     part = word[:i] + '-'
  493.                     width = fm.width(part)
  494.                     if self.current_width + width < self.line_length:
  495.                         self.commit(part, width, height, descent, ts, font)
  496.                         return (phrase_pos + len(part) - 1, True)
  497.                 
  498.             
  499.             return (phrase_pos, True)
  500.         
  501.         if not processed:
  502.             return self.populate(phrase + ' ', ts, False)
  503.         return (phrase_pos, False)
  504.  
  505.     
  506.     def commit(self, word, width, height, descent, ts, font):
  507.         self.tokens.append(Word(word, width, height, ts, font, self.valign))
  508.         self.current_width += width
  509.         self.height = max(self.height, height)
  510.         self.descent = max(self.descent, descent)
  511.  
  512.     
  513.     def add_space(self, min_width):
  514.         self.tokens.append(min_width)
  515.         self.current_width += min_width
  516.         self.length_in_space += min_width
  517.  
  518.     
  519.     def justify(self):
  520.         delta = self.line_length - self.current_width
  521.         if self.length_in_space > 0:
  522.             frac = 1 + float(delta) / self.length_in_space
  523.             for i in range(len(self.tokens)):
  524.                 if isinstance(self.tokens[i], (int, float)):
  525.                     self.tokens[i] *= frac
  526.                     continue
  527.             
  528.             self.current_width = self.line_length
  529.         
  530.  
  531.     
  532.     def finalize(self, baselineskip, linespace, vdebug):
  533.         if self.current_link is not None:
  534.             self.end_link()
  535.         
  536.         if self.current_width >= 0.85 * self.line_length and len(self.links) == 0:
  537.             self.justify()
  538.         
  539.         self.width = float(self.current_width)
  540.         if self.height == 0:
  541.             self.height = baselineskip
  542.         
  543.         self.height = float(self.height)
  544.         self.vdebug = vdebug
  545.         for link in self.links:
  546.             Link(self, *link)
  547.         
  548.         return self.height
  549.  
  550.     
  551.     def boundingRect(self):
  552.         return QRectF(0, 0, self.width, self.height)
  553.  
  554.     
  555.     def paint(self, painter, option, widget):
  556.         x = 0
  557.         y = 0 + self.height - self.descent
  558.         if self.vdebug:
  559.             painter.save()
  560.             painter.setPen(QPen(Qt.yellow, 1, Qt.DotLine))
  561.             painter.drawRect(self.boundingRect())
  562.             painter.restore()
  563.         
  564.         painter.save()
  565.         painter.setPen(QPen(Qt.NoPen))
  566.         for c in self.children():
  567.             painter.setBrush(c.brush)
  568.             painter.drawRect(c.boundingRect())
  569.         
  570.         painter.restore()
  571.         painter.save()
  572.         for tok in self.tokens:
  573.             if isinstance(tok, (int, float)):
  574.                 x += tok
  575.                 continue
  576.             if isinstance(tok, Word):
  577.                 painter.setFont(tok.font)
  578.                 if tok.highlight:
  579.                     painter.save()
  580.                     painter.setPen(QPen(Qt.NoPen))
  581.                     painter.setBrush(QBrush(Qt.yellow))
  582.                     painter.drawRect(x, 0, tok.width, tok.height)
  583.                     painter.restore()
  584.                 
  585.                 painter.setPen(QPen(tok.text_color))
  586.                 if tok.valign is None:
  587.                     painter.drawText(x, y, tok.string)
  588.                 elif tok.valign == 'Sub':
  589.                     painter.drawText(x + 1, y + self.descent / 1.5, tok.string)
  590.                 elif tok.valign == 'Sup':
  591.                     painter.drawText(x + 1, y - 2 * self.descent, tok.string)
  592.                 
  593.                 x += tok.width
  594.                 continue
  595.             painter.drawPixmap(x, 0, tok.pixmap())
  596.             x += tok.width
  597.         
  598.         painter.restore()
  599.  
  600.     
  601.     def words(self):
  602.         for w in self.tokens:
  603.             if isinstance(w, Word):
  604.                 yield w
  605.                 continue
  606.         
  607.  
  608.     
  609.     def search(self, phrase):
  610.         tokens = phrase.lower().split()
  611.         if len(tokens) < 1:
  612.             return None
  613.         words = self.words()
  614.         matches = []
  615.         
  616.         try:
  617.             while True:
  618.                 word = words.next()
  619.                 word.highlight = False
  620.                 if tokens[0] in unicode(word.string).lower():
  621.                     matches.append(word)
  622.                     for c in range(1, len(tokens)):
  623.                         word = words.next()
  624.                         print tokens[c], word.string
  625.                         if tokens[c] not in unicode(word.string):
  626.                             return None
  627.                         matches.append(word)
  628.                     
  629.                     for w in matches:
  630.                         w.highlight = True
  631.                     
  632.                     return self
  633.                 continue
  634.                 tokens[0] in unicode(word.string).lower()
  635.         except StopIteration:
  636.             len(tokens) < 1
  637.             len(tokens) < 1
  638.             return None
  639.  
  640.  
  641.     
  642.     def getx(self, textwidth):
  643.         if self.align == 'head':
  644.             return self.offset
  645.         if self.align == 'foot':
  646.             return textwidth - self.width
  647.         if self.align == 'center':
  648.             return (textwidth - self.width) / 2
  649.  
  650.     
  651.     def __unicode__(self):
  652.         s = u''
  653.         for tok in self.tokens:
  654.             if isinstance(tok, (int, float)):
  655.                 s += ' '
  656.                 continue
  657.             if isinstance(tok, Word):
  658.                 s += unicode(tok.string)
  659.                 continue
  660.         
  661.         return s
  662.  
  663.     
  664.     def __str__(self):
  665.         return unicode(self).encode('utf-8')
  666.  
  667.  
  668.  
  669. class Word(object):
  670.     
  671.     def __init__(self, string, width, height, ts, font, valign):
  672.         self.string = QString(string)
  673.         self.width = width
  674.         self.height = height
  675.         self.font = font
  676.         self.text_color = ts.textcolor
  677.         self.highlight = False
  678.         self.valign = valign
  679.  
  680.  
  681.  
  682. def main(args = sys.argv):
  683.     return 0
  684.  
  685. if __name__ == '__main__':
  686.     sys.exit(main())
  687.  
  688.