-#!/usr/bin/python
-# -*- coding: utf-8 -*-
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
+# $Id$
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
# trml2pdf - An RML to PDF converter
# Copyright (C) 2003, Fabien Pinckaers, UCL, FSA
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import sys
-import StringIO
+from StringIO import StringIO
import xml.dom.minidom
import copy
import reportlab
+import re
from reportlab.pdfgen import canvas
from reportlab import platypus
-
+import cStringIO
import utils
import color
import os
encoding = 'utf-8'
def str2xml(s):
- return s.replace('&', '&').replace('<', '<').replace('>', '>')
+ return s.replace('&', '&').replace('<', '<').replace('>', '>')
def _child_get(node, childs):
- clds = []
- for n in node.childNodes:
- if (n.nodeType==n.ELEMENT_NODE) and (n.localName==childs):
- clds.append(n)
- return clds
+ clds = []
+ for n in node.childNodes:
+ if (n.nodeType==n.ELEMENT_NODE) and (n.localName==childs):
+ clds.append(n)
+ return clds
+
+class PageCount(platypus.Flowable):
+ def draw(self):
+ self.canv.beginForm("pageCount")
+ self.canv.setFont("Helvetica", utils.unit_get(str(8)))
+ self.canv.drawString(0, 0, str(self.canv.getPageNumber()))
+ self.canv.endForm()
+
+class PageReset(platypus.Flowable):
+ def draw(self):
+ self.canv._pageNumber = 0
class _rml_styles(object):
- def __init__(self, nodes):
- self.styles = {}
- self.names = {}
- self.table_styles = {}
- for node in nodes:
- for style in node.getElementsByTagName('blockTableStyle'):
- self.table_styles[style.getAttribute('id')] = self._table_style_get(style)
- for style in node.getElementsByTagName('paraStyle'):
- self.styles[style.getAttribute('name')] = self._para_style_get(style)
- for variable in node.getElementsByTagName('initialize'):
- for name in variable.getElementsByTagName('name'):
- self.names[ name.getAttribute('id')] = name.getAttribute('value')
-
- def _para_style_update(self, style, node):
- for attr in ['textColor', 'backColor', 'bulletColor']:
- if node.hasAttribute(attr):
- style.__dict__[attr] = color.get(node.getAttribute(attr))
- for attr in ['fontName', 'bulletFontName', 'bulletText']:
- if node.hasAttribute(attr):
- style.__dict__[attr] = node.getAttribute(attr)
- for attr in ['fontSize', 'leftIndent', 'rightIndent', 'spaceBefore', 'spaceAfter', 'firstLineIndent', 'bulletIndent', 'bulletFontSize', 'leading']:
- if node.hasAttribute(attr):
- style.__dict__[attr] = utils.unit_get(node.getAttribute(attr))
- if node.hasAttribute('alignment'):
- align = {
- 'right':reportlab.lib.enums.TA_RIGHT,
- 'center':reportlab.lib.enums.TA_CENTER,
- 'justify':reportlab.lib.enums.TA_JUSTIFY
- }
- style.alignment = align.get(node.getAttribute('alignment').lower(), reportlab.lib.enums.TA_LEFT)
- return style
-
- def _table_style_get(self, style_node):
- styles = []
- for node in style_node.childNodes:
- if node.nodeType==node.ELEMENT_NODE:
- start = utils.tuple_int_get(node, 'start', (0,0) )
- stop = utils.tuple_int_get(node, 'stop', (-1,-1) )
- if node.localName=='blockValign':
- styles.append(('VALIGN', start, stop, str(node.getAttribute('value'))))
- elif node.localName=='blockFont':
- styles.append(('FONT', start, stop, str(node.getAttribute('name'))))
- elif node.localName=='blockTextColor':
- styles.append(('TEXTCOLOR', start, stop, color.get(str(node.getAttribute('colorName')))))
- elif node.localName=='blockLeading':
- styles.append(('LEADING', start, stop, utils.unit_get(node.getAttribute('length'))))
- elif node.localName=='blockAlignment':
- styles.append(('ALIGNMENT', start, stop, str(node.getAttribute('value'))))
- elif node.localName=='blockSpan':
- styles.append(('SPAN', start, stop))
- elif node.localName=='blockLeftPadding':
- styles.append(('LEFTPADDING', start, stop, utils.unit_get(node.getAttribute('length'))))
- elif node.localName=='blockRightPadding':
- styles.append(('RIGHTPADDING', start, stop, utils.unit_get(node.getAttribute('length'))))
- elif node.localName=='blockTopPadding':
- styles.append(('TOPPADDING', start, stop, utils.unit_get(node.getAttribute('length'))))
- elif node.localName=='blockBottomPadding':
- styles.append(('BOTTOMPADDING', start, stop, utils.unit_get(node.getAttribute('length'))))
- elif node.localName=='blockBackground':
- styles.append(('BACKGROUND', start, stop, color.get(node.getAttribute('colorName'))))
- if node.hasAttribute('size'):
- styles.append(('FONTSIZE', start, stop, utils.unit_get(node.getAttribute('size'))))
- elif node.localName=='lineStyle':
- kind = node.getAttribute('kind')
- kind_list = [ 'GRID', 'BOX', 'OUTLINE', 'INNERGRID', 'LINEBELOW', 'LINEABOVE','LINEBEFORE', 'LINEAFTER' ]
- assert kind in kind_list
- thick = 1
- if node.hasAttribute('thickness'):
- thick = float(node.getAttribute('thickness'))
- styles.append((kind, start, stop, thick, color.get(node.getAttribute('colorName'))))
- return platypus.tables.TableStyle(styles)
-
- def _para_style_get(self, node):
- styles = reportlab.lib.styles.getSampleStyleSheet()
- style = copy.deepcopy(styles["Normal"])
- self._para_style_update(style, node)
- return style
-
- def para_style_get(self, node):
- style = False
- if node.hasAttribute('style'):
- if node.getAttribute('style') in self.styles:
- style = copy.deepcopy(self.styles[node.getAttribute('style')])
- else:
- sys.stderr.write('Warning: style not found, %s - setting default!\n' % (node.getAttribute('style'),) )
- if not style:
- styles = reportlab.lib.styles.getSampleStyleSheet()
- style = copy.deepcopy(styles['Normal'])
- return self._para_style_update(style, node)
+ def __init__(self, nodes):
+ self.styles = {}
+ self.names = {}
+ self.table_styles = {}
+ for node in nodes:
+ for style in node.getElementsByTagName('blockTableStyle'):
+ self.table_styles[style.getAttribute('id')] = self._table_style_get(style)
+ for style in node.getElementsByTagName('paraStyle'):
+ self.styles[style.getAttribute('name')] = self._para_style_update(style)
+ for variable in node.getElementsByTagName('initialize'):
+ for name in variable.getElementsByTagName('name'):
+ self.names[ name.getAttribute('id')] = name.getAttribute('value')
+
+ def _para_style_update(self, node):
+ data = {}
+ for attr in ['textColor', 'backColor', 'bulletColor', 'borderColor']:
+ if node.hasAttribute(attr):
+ data[attr] = color.get(node.getAttribute(attr))
+ for attr in ['fontName', 'bulletFontName', 'bulletText']:
+ if node.hasAttribute(attr):
+ data[attr] = node.getAttribute(attr)
+ for attr in ['fontSize', 'leftIndent', 'rightIndent', 'spaceBefore', 'spaceAfter',
+ 'firstLineIndent', 'bulletIndent', 'bulletFontSize', 'leading',
+ 'borderWidth','borderPadding','borderRadius']:
+ if node.hasAttribute(attr):
+ data[attr] = utils.unit_get(node.getAttribute(attr))
+ if node.hasAttribute('alignment'):
+ align = {
+ 'right':reportlab.lib.enums.TA_RIGHT,
+ 'center':reportlab.lib.enums.TA_CENTER,
+ 'justify':reportlab.lib.enums.TA_JUSTIFY
+ }
+ data['alignment'] = align.get(node.getAttribute('alignment').lower(), reportlab.lib.enums.TA_LEFT)
+ return data
+
+ def _table_style_get(self, style_node):
+ styles = []
+ for node in style_node.childNodes:
+ if node.nodeType==node.ELEMENT_NODE:
+ start = utils.tuple_int_get(node, 'start', (0,0) )
+ stop = utils.tuple_int_get(node, 'stop', (-1,-1) )
+ if node.localName=='blockValign':
+ styles.append(('VALIGN', start, stop, str(node.getAttribute('value'))))
+ elif node.localName=='blockFont':
+ styles.append(('FONT', start, stop, str(node.getAttribute('name'))))
+ elif node.localName=='blockTextColor':
+ styles.append(('TEXTCOLOR', start, stop, color.get(str(node.getAttribute('colorName')))))
+ elif node.localName=='blockLeading':
+ styles.append(('LEADING', start, stop, utils.unit_get(node.getAttribute('length'))))
+ elif node.localName=='blockAlignment':
+ styles.append(('ALIGNMENT', start, stop, str(node.getAttribute('value'))))
+ elif node.localName=='blockSpan':
+ styles.append(('SPAN', start, stop))
+ elif node.localName=='blockLeftPadding':
+ styles.append(('LEFTPADDING', start, stop, utils.unit_get(node.getAttribute('length'))))
+ elif node.localName=='blockRightPadding':
+ styles.append(('RIGHTPADDING', start, stop, utils.unit_get(node.getAttribute('length'))))
+ elif node.localName=='blockTopPadding':
+ styles.append(('TOPPADDING', start, stop, utils.unit_get(node.getAttribute('length'))))
+ elif node.localName=='blockBottomPadding':
+ styles.append(('BOTTOMPADDING', start, stop, utils.unit_get(node.getAttribute('length'))))
+ elif node.localName=='blockBackground':
+ styles.append(('BACKGROUND', start, stop, color.get(node.getAttribute('colorName'))))
+ if node.hasAttribute('size'):
+ styles.append(('FONTSIZE', start, stop, utils.unit_get(node.getAttribute('size'))))
+ elif node.localName=='lineStyle':
+ kind = node.getAttribute('kind')
+ kind_list = [ 'GRID', 'BOX', 'OUTLINE', 'INNERGRID', 'LINEBELOW', 'LINEABOVE','LINEBEFORE', 'LINEAFTER' ]
+ assert kind in kind_list
+ thick = 1
+ if node.hasAttribute('thickness'):
+ thick = float(node.getAttribute('thickness'))
+ styles.append((kind, start, stop, thick, color.get(node.getAttribute('colorName'))))
+ return platypus.tables.TableStyle(styles)
+
+ def para_style_get(self, node):
+ style = False
+ if node.hasAttribute('style'):
+ if node.getAttribute('style') in self.styles:
+ styles = reportlab.lib.styles.getSampleStyleSheet()
+ sname = node.getAttribute('style')
+ style = reportlab.lib.styles.ParagraphStyle(sname, styles["Normal"], **self.styles[sname])
+ else:
+ sys.stderr.write('Warning: style not found, %s - setting default!\n' % (node.getAttribute('style'),) )
+ if not style:
+ styles = reportlab.lib.styles.getSampleStyleSheet()
+ style = copy.deepcopy(styles['Normal'])
+ style.__dict__.update(self._para_style_update(node))
+ return style
class _rml_doc(object):
- def __init__(self, data, images={}, path='.'):
- self.dom = xml.dom.minidom.parseString(data)
- self.filename = self.dom.documentElement.getAttribute('filename')
- self.images = images
- self.path = path
-
- def docinit(self, els):
- from reportlab.lib.fonts import addMapping
- from reportlab.pdfbase import pdfmetrics
- from reportlab.pdfbase.ttfonts import TTFont
-
- for node in els:
- for font in node.getElementsByTagName('registerFont'):
- name = font.getAttribute('fontName').encode('ascii')
- fname = font.getAttribute('fontFile').encode('ascii')
- pdfmetrics.registerFont(TTFont(name, fname ))
- addMapping(name, 0, 0, name) #normal
- addMapping(name, 0, 1, name) #italic
- addMapping(name, 1, 0, name) #bold
- addMapping(name, 1, 1, name) #italic and bold
-
- def _textual_image(self, node):
- import base64
- rc = ''
- for n in node.childNodes:
- if n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE):
- rc += n.data
- return base64.decodestring(rc)
-
- def _images(self, el):
- result = {}
- for node in el.getElementsByTagName('image'):
- result[node.getAttribute('name')] = self._textual_image(node)
- return result
-
- def render(self, out):
- self.canvas = canvas.Canvas(out)
- el = self.dom.documentElement.getElementsByTagName('docinit')
- if el:
- self.docinit(el)
-
- el = self.dom.documentElement.getElementsByTagName('stylesheet')
- self.styles = _rml_styles(el)
-
- el = self.dom.documentElement.getElementsByTagName('images')
- if el:
- self.images.update( self._images(el[0]) )
-
- el = self.dom.documentElement.getElementsByTagName('template')
- if len(el):
- pt_obj = _rml_template(out, el[0], self, images=self.images, path=self.path)
- pt_obj.render(self.dom.documentElement.getElementsByTagName('story'))
- del self.canvas
- else:
- pd = self.dom.documentElement.getElementsByTagName('pageDrawing')[0]
- pd_obj = _rml_canvas(self.canvas, None, self, self.images, path=self.path)
- pd_obj.render(pd)
- self.canvas.showPage()
- self.canvas.save()
+ def __init__(self, data, images={}, path='.', title=None):
+ self.dom = xml.dom.minidom.parseString(data)
+ self.filename = self.dom.documentElement.getAttribute('filename')
+ self.images = images
+ self.path = path
+ self.title = title
+
+ def docinit(self, els):
+ from reportlab.lib.fonts import addMapping
+ from reportlab.pdfbase import pdfmetrics
+ from reportlab.pdfbase.ttfonts import TTFont
+
+ for node in els:
+ for font in node.getElementsByTagName('registerFont'):
+ name = font.getAttribute('fontName').encode('ascii')
+ fname = font.getAttribute('fontFile').encode('ascii')
+ pdfmetrics.registerFont(TTFont(name, fname ))
+ addMapping(name, 0, 0, name) #normal
+ addMapping(name, 0, 1, name) #italic
+ addMapping(name, 1, 0, name) #bold
+ addMapping(name, 1, 1, name) #italic and bold
+
+ def setTTFontMapping(self,face, fontname,filename, mode='all'):
+ from reportlab.lib.fonts import addMapping
+ from reportlab.pdfbase import pdfmetrics
+ from reportlab.pdfbase.ttfonts import TTFont
+
+ pdfmetrics.registerFont(TTFont(fontname, filename ))
+ if (mode == 'all'):
+ addMapping(face, 0, 0, fontname) #normal
+ addMapping(face, 0, 1, fontname) #italic
+ addMapping(face, 1, 0, fontname) #bold
+ addMapping(face, 1, 1, fontname) #italic and bold
+ elif (mode== 'normal') or (mode == 'regular'):
+ addMapping(face, 0, 0, fontname) #normal
+ elif (mode == 'italic'):
+ addMapping(face, 0, 1, fontname) #italic
+ elif (mode == 'bold'):
+ addMapping(face, 1, 0, fontname) #bold
+ elif (mode == 'bolditalic'):
+ addMapping(face, 1, 1, fontname) #italic and bold
+
+ def _textual_image(self, node):
+ import base64
+ rc = ''
+ for n in node.childNodes:
+ if n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE):
+ rc += n.data
+ return base64.decodestring(rc)
+
+ def _images(self, el):
+ result = {}
+ for node in el.getElementsByTagName('image'):
+ result[node.getAttribute('name')] = self._textual_image(node)
+ return result
+
+ def render(self, out):
+ el = self.dom.documentElement.getElementsByTagName('docinit')
+ if el:
+ self.docinit(el)
+
+ el = self.dom.documentElement.getElementsByTagName('stylesheet')
+ self.styles = _rml_styles(el)
+
+ el = self.dom.documentElement.getElementsByTagName('images')
+ if el:
+ self.images.update( self._images(el[0]) )
+
+ el = self.dom.documentElement.getElementsByTagName('template')
+ if len(el):
+ pt_obj = _rml_template(out, el[0], self, images=self.images, path=self.path, title=self.title)
+ pt_obj.render(self.dom.documentElement.getElementsByTagName('story'))
+ else:
+ self.canvas = canvas.Canvas(out)
+ pd = self.dom.documentElement.getElementsByTagName('pageDrawing')[0]
+ pd_obj = _rml_canvas(self.canvas, None, self, self.images, path=self.path, title=self.title)
+ pd_obj.render(pd)
+
+ self.canvas.showPage()
+ self.canvas.save()
class _rml_canvas(object):
- def __init__(self, canvas, doc_tmpl=None, doc=None, images={}, path='.'):
- self.canvas = canvas
- self.styles = doc.styles
- self.doc_tmpl = doc_tmpl
- self.doc = doc
- self.images = images
- self.path = path
-
- def _textual(self, node):
- rc = ''
- for n in node.childNodes:
- if n.nodeType == n.ELEMENT_NODE:
- if n.localName == 'pageNumber':
- rc += str(self.canvas.getPageNumber())
- elif n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE):
- # this doesn't need to be "entities" encoded like flowables need to
- rc += n.data
- return rc.encode(encoding, 'replace')
-
- def _drawString(self, node):
- self.canvas.drawString(text=self._textual(node), **utils.attr_get(node, ['x','y']))
- def _drawCenteredString(self, node):
- self.canvas.drawCentredString(text=self._textual(node), **utils.attr_get(node, ['x','y']))
- def _drawRightString(self, node):
- self.canvas.drawRightString(text=self._textual(node), **utils.attr_get(node, ['x','y']))
- def _rect(self, node):
- if node.hasAttribute('round'):
- self.canvas.roundRect(radius=utils.unit_get(node.getAttribute('round')), **utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'}))
- else:
- self.canvas.rect(**utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'}))
-
- def _ellipse(self, node):
- x1 = utils.unit_get(node.getAttribute('x'))
- x2 = utils.unit_get(node.getAttribute('width'))
- y1 = utils.unit_get(node.getAttribute('y'))
- y2 = utils.unit_get(node.getAttribute('height'))
- self.canvas.ellipse(x1,y1,x2,y2, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'}))
- def _curves(self, node):
- line_str = utils.text_get(node).split()
- lines = []
- while len(line_str)>7:
- self.canvas.bezier(*[utils.unit_get(l) for l in line_str[0:8]])
- line_str = line_str[8:]
- def _lines(self, node):
- line_str = utils.text_get(node).split()
- lines = []
- while len(line_str)>3:
- lines.append([utils.unit_get(l) for l in line_str[0:4]])
- line_str = line_str[4:]
- self.canvas.lines(lines)
- def _grid(self, node):
- xlist = [utils.unit_get(s) for s in node.getAttribute('xs').split(',')]
- ylist = [utils.unit_get(s) for s in node.getAttribute('ys').split(',')]
- self.canvas.grid(xlist, ylist)
- def _translate(self, node):
- dx = 0
- dy = 0
- if node.hasAttribute('dx'):
- dx = utils.unit_get(node.getAttribute('dx'))
- if node.hasAttribute('dy'):
- dy = utils.unit_get(node.getAttribute('dy'))
- self.canvas.translate(dx,dy)
-
- def _circle(self, node):
- self.canvas.circle(x_cen=utils.unit_get(node.getAttribute('x')), y_cen=utils.unit_get(node.getAttribute('y')), r=utils.unit_get(node.getAttribute('radius')), **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'}))
-
- def _place(self, node):
- flows = _rml_flowable(self.doc, images=self.images, path=self.path).render(node)
- infos = utils.attr_get(node, ['x','y','width','height'])
-
- infos['y']+=infos['height']
- for flow in flows:
- w,h = flow.wrap(infos['width'], infos['height'])
- if w<=infos['width'] and h<=infos['height']:
- infos['y']-=h
- flow.drawOn(self.canvas,infos['x'],infos['y'])
- infos['height']-=h
- else:
- raise ValueError, "Not enough space"
-
- def _line_mode(self, node):
- ljoin = {'round':1, 'mitered':0, 'bevelled':2}
- lcap = {'default':0, 'round':1, 'square':2}
- if node.hasAttribute('width'):
- self.canvas.setLineWidth(utils.unit_get(node.getAttribute('width')))
- if node.hasAttribute('join'):
- self.canvas.setLineJoin(ljoin[node.getAttribute('join')])
- if node.hasAttribute('cap'):
- self.canvas.setLineCap(lcap[node.getAttribute('cap')])
- if node.hasAttribute('miterLimit'):
- self.canvas.setDash(utils.unit_get(node.getAttribute('miterLimit')))
- if node.hasAttribute('dash'):
- dashes = node.getAttribute('dash').split(',')
- for x in range(len(dashes)):
- dashes[x]=utils.unit_get(dashes[x])
- self.canvas.setDash(node.getAttribute('dash').split(','))
-
- def _image(self, node):
- import urllib
- from reportlab.lib.utils import ImageReader
-
- if not node.hasAttribute('file'):
- if node.getAttribute('name') in self.images:
- scontent = self.images[node.getAttribute('name')]
- s = StringIO.StringIO(scontent)
- else:
- return True
- else:
- if node.getAttribute('file') in self.images:
- s = StringIO.StringIO(self.images[node.getAttribute('file')])
- else:
- try:
- u = urllib.urlopen(str(node.getAttribute('file')))
- s = StringIO.StringIO(u.read())
- except:
- u = file(os.path.join(self.path,str(node.getAttribute('file'))), 'rb')
- s = StringIO.StringIO(u.read())
- img = ImageReader(s)
- (sx,sy) = img.getSize()
-
- args = {}
- for tag in ('width','height','x','y'):
- if node.hasAttribute(tag):
- args[tag] = utils.unit_get(node.getAttribute(tag))
- if ('width' in args) and (not 'height' in args):
- args['height'] = sy * args['width'] / sx
- elif ('height' in args) and (not 'width' in args):
- args['width'] = sx * args['height'] / sy
- elif ('width' in args) and ('height' in args):
- if (float(args['width'])/args['height'])>(float(sx)>sy):
- args['width'] = sx * args['height'] / sy
- else:
- args['height'] = sy * args['width'] / sx
- self.canvas.drawImage(img, **args)
-
- def _path(self, node):
- self.path = self.canvas.beginPath()
- self.path.moveTo(**utils.attr_get(node, ['x','y']))
- for n in node.childNodes:
- if n.nodeType == node.ELEMENT_NODE:
- if n.localName=='moveto':
- vals = utils.text_get(n).split()
- self.path.moveTo(utils.unit_get(vals[0]), utils.unit_get(vals[1]))
- elif n.localName=='curvesto':
- vals = utils.text_get(n).split()
- while len(vals)>5:
- pos=[]
- while len(pos)<6:
- pos.append(utils.unit_get(vals.pop(0)))
- self.path.curveTo(*pos)
- elif (n.nodeType == node.TEXT_NODE):
- data = n.data.split() # Not sure if I must merge all TEXT_NODE ?
- while len(data)>1:
- x = utils.unit_get(data.pop(0))
- y = utils.unit_get(data.pop(0))
- self.path.lineTo(x,y)
- if (not node.hasAttribute('close')) or utils.bool_get(node.getAttribute('close')):
- self.path.close()
- self.canvas.drawPath(self.path, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'}))
-
- def render(self, node):
- tags = {
- 'drawCentredString': self._drawCenteredString,
- 'drawRightString': self._drawRightString,
- 'drawString': self._drawString,
- 'rect': self._rect,
- 'ellipse': self._ellipse,
- 'lines': self._lines,
- 'grid': self._grid,
- 'curves': self._curves,
- 'fill': lambda node: self.canvas.setFillColor(color.get(node.getAttribute('color'))),
- 'stroke': lambda node: self.canvas.setStrokeColor(color.get(node.getAttribute('color'))),
- 'setFont': lambda node: self.canvas.setFont(node.getAttribute('name'), utils.unit_get(node.getAttribute('size'))),
- 'place': self._place,
- 'circle': self._circle,
- 'lineMode': self._line_mode,
- 'path': self._path,
- 'rotate': lambda node: self.canvas.rotate(float(node.getAttribute('degrees'))),
- 'translate': self._translate,
- 'image': self._image
- }
- for nd in node.childNodes:
- if nd.nodeType==nd.ELEMENT_NODE:
- for tag in tags:
- if nd.localName==tag:
- tags[tag](nd)
- break
+ def __init__(self, canvas, doc_tmpl=None, doc=None, images={}, path='.', title=None):
+ self.canvas = canvas
+ self.styles = doc.styles
+ self.doc_tmpl = doc_tmpl
+ self.doc = doc
+ self.images = images
+ self.path = path
+ self.title = title
+ if self.title:
+ self.canvas.setTitle(self.title)
+
+ def _textual(self, node, x=0, y=0):
+ rc = ''
+ for n in node.childNodes:
+ if n.nodeType == n.ELEMENT_NODE:
+ if n.localName == 'seq':
+ from reportlab.lib.sequencer import getSequencer
+ seq = getSequencer()
+ rc += str(seq.next(n.getAttribute('id')))
+ if n.localName == 'pageCount':
+ if x or y:
+ self.canvas.translate(x,y)
+ self.canvas.doForm('pageCount')
+ if x or y:
+ self.canvas.translate(-x,-y)
+ if n.localName == 'pageNumber':
+ rc += str(self.canvas.getPageNumber())
+ elif n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE):
+ # this doesn't need to be "entities" encoded like flowables need to
+ rc += n.data
+ return rc.encode(encoding, 'replace')
+
+ def _drawString(self, node):
+ v = utils.attr_get(node, ['x','y'])
+ self.canvas.drawString(text=self._textual(node, **v), **v)
+ def _drawCenteredString(self, node):
+ v = utils.attr_get(node, ['x','y'])
+ self.canvas.drawCentredString(text=self._textual(node, **v), **v)
+ def _drawRightString(self, node):
+ v = utils.attr_get(node, ['x','y'])
+ self.canvas.drawRightString(text=self._textual(node, **v), **v)
+ def _rect(self, node):
+ if node.hasAttribute('round'):
+ self.canvas.roundRect(radius=utils.unit_get(node.getAttribute('round')), **utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'}))
+ else:
+ self.canvas.rect(**utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'}))
+
+ def _ellipse(self, node):
+ x1 = utils.unit_get(node.getAttribute('x'))
+ x2 = utils.unit_get(node.getAttribute('width'))
+ y1 = utils.unit_get(node.getAttribute('y'))
+ y2 = utils.unit_get(node.getAttribute('height'))
+ self.canvas.ellipse(x1,y1,x2,y2, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'}))
+ def _curves(self, node):
+ line_str = utils.text_get(node).split()
+ lines = []
+ while len(line_str)>7:
+ self.canvas.bezier(*[utils.unit_get(l) for l in line_str[0:8]])
+ line_str = line_str[8:]
+ def _lines(self, node):
+ line_str = utils.text_get(node).split()
+ lines = []
+ while len(line_str)>3:
+ lines.append([utils.unit_get(l) for l in line_str[0:4]])
+ line_str = line_str[4:]
+ self.canvas.lines(lines)
+ def _grid(self, node):
+ xlist = [utils.unit_get(s) for s in node.getAttribute('xs').split(',')]
+ ylist = [utils.unit_get(s) for s in node.getAttribute('ys').split(',')]
+ self.canvas.grid(xlist, ylist)
+ def _translate(self, node):
+ dx = 0
+ dy = 0
+ if node.hasAttribute('dx'):
+ dx = utils.unit_get(node.getAttribute('dx'))
+ if node.hasAttribute('dy'):
+ dy = utils.unit_get(node.getAttribute('dy'))
+ self.canvas.translate(dx,dy)
+
+ def _circle(self, node):
+ self.canvas.circle(x_cen=utils.unit_get(node.getAttribute('x')), y_cen=utils.unit_get(node.getAttribute('y')), r=utils.unit_get(node.getAttribute('radius')), **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'}))
+
+ def _place(self, node):
+ flows = _rml_flowable(self.doc, images=self.images, path=self.path, title=self.title).render(node)
+ infos = utils.attr_get(node, ['x','y','width','height'])
+
+ infos['y']+=infos['height']
+ for flow in flows:
+ w,h = flow.wrap(infos['width'], infos['height'])
+ if w<=infos['width'] and h<=infos['height']:
+ infos['y']-=h
+ flow.drawOn(self.canvas,infos['x'],infos['y'])
+ infos['height']-=h
+ else:
+ raise ValueError, "Not enough space"
+
+ def _line_mode(self, node):
+ ljoin = {'round':1, 'mitered':0, 'bevelled':2}
+ lcap = {'default':0, 'round':1, 'square':2}
+ if node.hasAttribute('width'):
+ self.canvas.setLineWidth(utils.unit_get(node.getAttribute('width')))
+ if node.hasAttribute('join'):
+ self.canvas.setLineJoin(ljoin[node.getAttribute('join')])
+ if node.hasAttribute('cap'):
+ self.canvas.setLineCap(lcap[node.getAttribute('cap')])
+ if node.hasAttribute('miterLimit'):
+ self.canvas.setDash(utils.unit_get(node.getAttribute('miterLimit')))
+ if node.hasAttribute('dash'):
+ dashes = node.getAttribute('dash').split(',')
+ for x in range(len(dashes)):
+ dashes[x]=utils.unit_get(dashes[x])
+ self.canvas.setDash(node.getAttribute('dash').split(','))
+
+ def _image(self, node):
+ import urllib
+ from reportlab.lib.utils import ImageReader
+
+# s = StringIO()
+ if not node.hasAttribute('file'):
+
+ if node.hasAttribute('name'):
+ image_data = self.images[node.getAttribute('name')]
+ s = cStringIO.StringIO(image_data)
+ else:
+ import base64
+ image_data = base64.decodestring(node.firstChild.nodeValue)
+ if not image_data: return False
+ s = cStringIO.StringIO(image_data)
+# s.write(image_data)
+ else:
+ if node.getAttribute('file') in self.images:
+ s = cStringIO.StringIO(self.images[node.getAttribute('file')])
+# s.write(self.images[node.getAttribute('file')])
+ else:
+ try:
+ u = urllib.urlopen(str(node.getAttribute('file')))
+ except:
+ u = file(os.path.join(self.path,str(node.getAttribute('file'))), 'rb')
+ s = cStringIO.StringIO(u.read())
+ img = ImageReader(s)
+ (sx,sy) = img.getSize()
+
+ args = {}
+ for tag in ('width','height','x','y'):
+ if node.hasAttribute(tag):
+ args[tag] = utils.unit_get(node.getAttribute(tag))
+ if ('width' in args) and (not 'height' in args):
+ args['height'] = sy * args['width'] / sx
+ elif ('height' in args) and (not 'width' in args):
+ args['width'] = sx * args['height'] / sy
+ elif ('width' in args) and ('height' in args):
+ if (float(args['width'])/args['height'])>(float(sx)>sy):
+ args['width'] = sx * args['height'] / sy
+ else:
+ args['height'] = sy * args['width'] / sx
+ self.canvas.drawImage(img, **args)
+
+ def _path(self, node):
+ self.path = self.canvas.beginPath()
+ self.path.moveTo(**utils.attr_get(node, ['x','y']))
+ for n in node.childNodes:
+ if n.nodeType == node.ELEMENT_NODE:
+ if n.localName=='moveto':
+ vals = utils.text_get(n).split()
+ self.path.moveTo(utils.unit_get(vals[0]), utils.unit_get(vals[1]))
+ elif n.localName=='curvesto':
+ vals = utils.text_get(n).split()
+ while len(vals)>5:
+ pos=[]
+ while len(pos)<6:
+ pos.append(utils.unit_get(vals.pop(0)))
+ self.path.curveTo(*pos)
+ elif (n.nodeType == node.TEXT_NODE):
+ data = n.data.split() # Not sure if I must merge all TEXT_NODE ?
+ while len(data)>1:
+ x = utils.unit_get(data.pop(0))
+ y = utils.unit_get(data.pop(0))
+ self.path.lineTo(x,y)
+ if (not node.hasAttribute('close')) or utils.bool_get(node.getAttribute('close')):
+ self.path.close()
+ self.canvas.drawPath(self.path, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'}))
+
+ def render(self, node):
+ tags = {
+ 'drawCentredString': self._drawCenteredString,
+ 'drawRightString': self._drawRightString,
+ 'drawString': self._drawString,
+ 'rect': self._rect,
+ 'ellipse': self._ellipse,
+ 'lines': self._lines,
+ 'grid': self._grid,
+ 'curves': self._curves,
+ 'fill': lambda node: self.canvas.setFillColor(color.get(node.getAttribute('color'))),
+ 'stroke': lambda node: self.canvas.setStrokeColor(color.get(node.getAttribute('color'))),
+ 'setFont': lambda node: self.canvas.setFont(node.getAttribute('name'), utils.unit_get(node.getAttribute('size'))),
+ 'place': self._place,
+ 'circle': self._circle,
+ 'lineMode': self._line_mode,
+ 'path': self._path,
+ 'rotate': lambda node: self.canvas.rotate(float(node.getAttribute('degrees'))),
+ 'translate': self._translate,
+ 'image': self._image
+ }
+ for nd in node.childNodes:
+ if nd.nodeType==nd.ELEMENT_NODE:
+ for tag in tags:
+ if nd.localName==tag:
+ tags[tag](nd)
+ break
class _rml_draw(object):
- def __init__(self, node, styles, images={}, path='.'):
- self.node = node
- self.styles = styles
- self.canvas = None
- self.images = images
- self.path = path
-
- def render(self, canvas, doc):
- canvas.saveState()
- cnv = _rml_canvas(canvas, doc, self.styles, images=self.images, path=self.path)
- cnv.render(self.node)
- canvas.restoreState()
+ def __init__(self, node, styles, images={}, path='.', title=None):
+ self.node = node
+ self.styles = styles
+ self.canvas = None
+ self.images = images
+ self.path = path
+ self.canvas_title = title
+
+ def render(self, canvas, doc):
+ canvas.saveState()
+ cnv = _rml_canvas(canvas, doc, self.styles, images=self.images, path=self.path, title=self.canvas_title)
+ cnv.render(self.node)
+ canvas.restoreState()
class _rml_flowable(object):
- def __init__(self, doc, images={}, path='.'):
- self.doc = doc
- self.styles = doc.styles
- self.images = images
- self.path = path
-
- def _textual(self, node):
- rc = ''
- for n in node.childNodes:
- if n.nodeType == node.ELEMENT_NODE:
- if n.localName == 'getName':
- newNode = self.doc.dom.createTextNode(self.styles.names.get(n.getAttribute('id'),'Unknown name'))
- node.insertBefore(newNode, n)
- node.removeChild(n)
- n = newNode
- elif n.localName == 'pageNumber':
- newNode = self.doc.dom.createTextNode(str(self.doc.canvas.getPageNumber()))
- node.insertBefore(newNode, n)
- node.removeChild(n)
- n = newNode
- else:
- #CHECKME: I wonder if this is useful since we don't stock the result. Maybe for the getName tag?
- self._textual(n)
- rc += n.toxml()
- elif n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE):
- rc += str2xml(n.data)
- return rc.encode(encoding, 'replace')
-
- def _table(self, node):
- length = 0
- colwidths = None
- rowheights = None
- data = []
- childs = _child_get(node,'tr')
- if not childs:
- return None
- for tr in childs:
- data2 = []
- for td in _child_get(tr, 'td'):
- flow = []
- for n in td.childNodes:
- if n.nodeType==node.ELEMENT_NODE:
- flow.append( self._flowable(n) )
- if not len(flow):
- flow = self._textual(td)
- data2.append( flow )
- if len(data2)>length:
- length=len(data2)
- for ab in data:
- while len(ab)<length:
- ab.append('')
- while len(data2)<length:
- data2.append('')
- data.append( data2 )
- if node.hasAttribute('colWidths'):
- assert length == len(node.getAttribute('colWidths').split(','))
- colwidths = [utils.unit_get(f.strip()) for f in node.getAttribute('colWidths').split(',')]
- if node.hasAttribute('rowHeights'):
- rowheights = [utils.unit_get(f.strip()) for f in node.getAttribute('rowHeights').split(',')]
- if len(rowheights) == 1:
- rowheights = rowheights[0]
- table = platypus.Table(data = data, colWidths=colwidths, rowHeights=rowheights, **(utils.attr_get(node, ['splitByRow'] ,{'repeatRows':'int','repeatCols':'int'})))
- if node.hasAttribute('style'):
- table.setStyle(self.styles.table_styles[node.getAttribute('style')])
- return table
-
- def _illustration(self, node):
- class Illustration(platypus.flowables.Flowable):
- def __init__(self, node, styles, self2):
- self.node = node
- self.styles = styles
- self.width = utils.unit_get(node.getAttribute('width'))
- self.height = utils.unit_get(node.getAttribute('height'))
- self.self2 = self2
- def wrap(self, *args):
- return (self.width, self.height)
- def draw(self):
- canvas = self.canv
- drw = _rml_draw(self.node, self.styles, images=self.self2.images, path=self.self2.path)
- drw.render(self.canv, None)
- return Illustration(node, self.styles, self)
-
- def _textual_image(self, node):
- import base64
- rc = ''
- for n in node.childNodes:
- if n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE):
- rc += n.data
- return base64.decodestring(rc)
-
- def _flowable(self, node):
- if node.localName=='para':
- style = self.styles.para_style_get(node)
- return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
- elif node.localName=='barCode':
- try:
- from reportlab.graphics.barcode import code128
- from reportlab.graphics.barcode import code39
- from reportlab.graphics.barcode import code93
- from reportlab.graphics.barcode import common
- from reportlab.graphics.barcode import fourstate
- from reportlab.graphics.barcode import usps
- except Exception, e:
- print 'Warning: Reportlab barcode extension not installed !'
- return None
- args = utils.attr_get(node, [], {'ratio':'float','xdim':'unit','height':'unit','checksum':'bool','quiet':'bool'})
- codes = {
- 'codabar': lambda x: common.Codabar(x, **args),
- 'code11': lambda x: common.Code11(x, **args),
- 'code128': lambda x: code128.Code128(x, **args),
- 'standard39': lambda x: code39.Standard39(x, **args),
- 'standard93': lambda x: code93.Standard93(x, **args),
- 'i2of5': lambda x: common.I2of5(x, **args),
- 'extended39': lambda x: code39.Extended39(x, **args),
- 'extended93': lambda x: code93.Extended93(x, **args),
- 'msi': lambda x: common.MSI(x, **args),
- 'fim': lambda x: usps.FIM(x, **args),
- 'postnet': lambda x: usps.POSTNET(x, **args),
- }
- code = 'code128'
- if node.hasAttribute('code'):
- code = node.getAttribute('code').lower()
- return codes[code](self._textual(node))
- elif node.localName=='name':
- self.styles.names[ node.getAttribute('id')] = node.getAttribute('value')
- return None
- elif node.localName=='xpre':
- style = self.styles.para_style_get(node)
- return platypus.XPreformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int','frags':'int'})))
- elif node.localName=='pre':
- style = self.styles.para_style_get(node)
- return platypus.Preformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int'})))
- elif node.localName=='illustration':
- return self._illustration(node)
- elif node.localName=='blockTable':
- return self._table(node)
- elif node.localName=='title':
- styles = reportlab.lib.styles.getSampleStyleSheet()
- style = styles['Title']
- return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
- elif node.localName=='h1':
- styles = reportlab.lib.styles.getSampleStyleSheet()
- style = styles['Heading1']
- return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
- elif node.localName=='h2':
- styles = reportlab.lib.styles.getSampleStyleSheet()
- style = styles['Heading2']
- return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
- elif node.localName=='h3':
- styles = reportlab.lib.styles.getSampleStyleSheet()
- style = styles['Heading3']
- return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
- elif node.localName=='image':
- if not node.hasAttribute('file'):
- if node.hasAttribute('name'):
- image_data = self.doc.images[node.getAttribute('name')].read()
- else:
- import base64
- image_data = base64.decodestring(node.firstChild.nodeValue)
- image = StringIO.StringIO(image_data)
- return platypus.Image(image, mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height'])))
- else:
- return platypus.Image(node.getAttribute('file'), mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height'])))
-
- from reportlab.lib.utils import ImageReader
- name = str(node.getAttribute('file'))
- img = ImageReader(name)
- (sx,sy) = img.getSize()
-
- args = {}
- for tag in ('width','height'):
- if node.hasAttribute(tag):
- args[tag] = utils.unit_get(node.getAttribute(tag))
- if ('width' in args) and (not 'height' in args):
- args['height'] = sy * args['width'] / sx
- elif ('height' in args) and (not 'width' in args):
- args['width'] = sx * args['height'] / sy
- elif ('width' in args) and ('height' in args):
- if (float(args['width'])/args['height'])>(float(sx)>sy):
- args['width'] = sx * args['height'] / sy
- else:
- args['height'] = sy * args['width'] / sx
- return platypus.Image(name, mask=(250,255,250,255,250,255), **args)
- elif node.localName=='spacer':
- if node.hasAttribute('width'):
- width = utils.unit_get(node.getAttribute('width'))
- else:
- width = utils.unit_get('1cm')
- length = utils.unit_get(node.getAttribute('length'))
- return platypus.Spacer(width=width, height=length)
- elif node.localName=='section':
- return self.render(node)
- elif node.localName in ('pageBreak', 'nextPage'):
- return platypus.PageBreak()
- elif node.localName=='condPageBreak':
- return platypus.CondPageBreak(**(utils.attr_get(node, ['height'])))
- elif node.localName=='setNextTemplate':
- return platypus.NextPageTemplate(str(node.getAttribute('name')))
- elif node.localName=='nextFrame':
- return platypus.CondPageBreak(1000) # TODO: change the 1000 !
- elif node.localName == 'setNextFrame':
- from reportlab.platypus.doctemplate import NextFrameFlowable
- return NextFrameFlowable(str(node.getAttribute('name')))
- elif node.localName == 'currentFrame':
- from reportlab.platypus.doctemplate import CurrentFrameFlowable
- return CurrentFrameFlowable(str(node.getAttribute('name')))
- elif node.localName == 'frameEnd':
- return EndFrameFlowable()
- else:
- sys.stderr.write('Warning: flowable not yet implemented: %s !\n' % (node.localName,))
- return None
-
- def render(self, node_story):
- story = []
- node = node_story.firstChild
- while node:
- if node.nodeType == node.ELEMENT_NODE:
- flow = self._flowable(node)
- if flow:
- if type(flow) == type([]):
- story = story + flow
- else:
- story.append(flow)
- node = node.nextSibling
- return story
+ def __init__(self, doc, images={}, path='.', title=None):
+ self.doc = doc
+ self.styles = doc.styles
+ self.images = images
+ self.path = path
+ self.title = title
+
+ def _textual(self, node):
+ rc = ''
+ for n in node.childNodes:
+ if n.nodeType == node.ELEMENT_NODE:
+ if n.localName == 'getName':
+ newNode = self.doc.dom.createTextNode(self.styles.names.get(n.getAttribute('id'),'Unknown name'))
+ node.insertBefore(newNode, n)
+ node.removeChild(n)
+ n = newNode
+ elif n.localName == 'pageNumber':
+ rc += '<pageNumber/>' # TODO: change this !
+ else:
+ #CHECKME: I wonder if this is useful since we don't stock the result. Maybe for the getName tag?
+ self._textual(n)
+ rc += n.toxml()
+ elif n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE):
+ rc += str2xml(n.data)
+ return rc.encode(encoding, 'replace')
+
+ def _table(self, node):
+ length = 0
+ colwidths = None
+ rowheights = None
+ data = []
+ childs = _child_get(node,'tr')
+ if not childs:
+ return None
+ posy = 0
+ styles = []
+ for tr in childs:
+ paraStyle = None
+ if tr.hasAttribute('style'):
+ st = copy.deepcopy(self.styles.table_styles[tr.getAttribute('style')])
+ for s in st._cmds:
+ s[1][1] = posy
+ s[2][1] = posy
+ styles.append(st)
+ if tr.hasAttribute('paraStyle'):
+ paraStyle = self.styles.styles[tr.getAttribute('paraStyle')]
+
+ data2 = []
+ posx = 0
+ for td in _child_get(tr, 'td'):
+ if td.hasAttribute('style'):
+ st = copy.deepcopy(self.styles.table_styles[td.getAttribute('style')])
+ for s in st._cmds:
+ s[1][1] = posy
+ s[2][1] = posy
+ s[1][0] = posx
+ s[2][0] = posx
+ styles.append(st)
+ if td.hasAttribute('paraStyle'):
+ # TODO: merge styles
+ paraStyle = self.styles.styles[td.getAttribute('paraStyle')]
+ posx += 1
+
+ flow = []
+ for n in td.childNodes:
+ if n.nodeType==node.ELEMENT_NODE:
+ fl = self._flowable(n, extra_style=paraStyle)
+ flow.append( fl )
+ if not len(flow):
+ flow = self._textual(td)
+ data2.append( flow )
+ if len(data2)>length:
+ length=len(data2)
+ for ab in data:
+ while len(ab)<length:
+ ab.append('')
+ while len(data2)<length:
+ data2.append('')
+ data.append( data2 )
+ posy += 1
+ if node.hasAttribute('colWidths'):
+ assert length == len(node.getAttribute('colWidths').split(','))
+ colwidths = [utils.unit_get(f.strip()) for f in node.getAttribute('colWidths').split(',')]
+ if node.hasAttribute('rowHeights'):
+ rowheights = [utils.unit_get(f.strip()) for f in node.getAttribute('rowHeights').split(',')]
+ if len(rowheights) == 1:
+ rowheights = rowheights[0]
+ table = platypus.LongTable(data = data, colWidths=colwidths, rowHeights=rowheights, **(utils.attr_get(node, ['splitByRow'] ,{'repeatRows':'int','repeatCols':'int'})))
+ if node.hasAttribute('style'):
+ table.setStyle(self.styles.table_styles[node.getAttribute('style')])
+ for s in styles:
+ table.setStyle(s)
+ return table
+
+ def _illustration(self, node):
+ class Illustration(platypus.flowables.Flowable):
+ def __init__(self, node, styles, self2):
+ self.node = node
+ self.styles = styles
+ self.width = utils.unit_get(node.getAttribute('width'))
+ self.height = utils.unit_get(node.getAttribute('height'))
+ self.self2 = self2
+ def wrap(self, *args):
+ return (self.width, self.height)
+ def draw(self):
+ canvas = self.canv
+ drw = _rml_draw(self.node, self.styles, images=self.self2.images, path=self.self2.path, title=self.self2.title)
+ drw.render(self.canv, None)
+ return Illustration(node, self.styles, self)
+
+ def _textual_image(self, node):
+ import base64
+ rc = ''
+ for n in node.childNodes:
+ if n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE):
+ rc += n.data
+ return base64.decodestring(rc)
+
+ def _flowable(self, node, extra_style=None):
+ if node.localName=='para':
+ style = self.styles.para_style_get(node)
+ if extra_style:
+ style.__dict__.update(extra_style)
+ return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
+ elif node.localName=='barCode':
+ try:
+ from reportlab.graphics.barcode import code128
+ from reportlab.graphics.barcode import code39
+ from reportlab.graphics.barcode import code93
+ from reportlab.graphics.barcode import common
+ from reportlab.graphics.barcode import fourstate
+ from reportlab.graphics.barcode import usps
+ except Exception, e:
+ print 'Warning: Reportlab barcode extension not installed !'
+ return None
+ args = utils.attr_get(node, [], {'ratio':'float','xdim':'unit','height':'unit','checksum':'bool','quiet':'bool'})
+ codes = {
+ 'codabar': lambda x: common.Codabar(x, **args),
+ 'code11': lambda x: common.Code11(x, **args),
+ 'code128': lambda x: code128.Code128(x, **args),
+ 'standard39': lambda x: code39.Standard39(x, **args),
+ 'standard93': lambda x: code93.Standard93(x, **args),
+ 'i2of5': lambda x: common.I2of5(x, **args),
+ 'extended39': lambda x: code39.Extended39(x, **args),
+ 'extended93': lambda x: code93.Extended93(x, **args),
+ 'msi': lambda x: common.MSI(x, **args),
+ 'fim': lambda x: usps.FIM(x, **args),
+ 'postnet': lambda x: usps.POSTNET(x, **args),
+ }
+ code = 'code128'
+ if node.hasAttribute('code'):
+ code = node.getAttribute('code').lower()
+ return codes[code](self._textual(node))
+ elif node.localName=='name':
+ self.styles.names[ node.getAttribute('id')] = node.getAttribute('value')
+ return None
+ elif node.localName=='xpre':
+ style = self.styles.para_style_get(node)
+ return platypus.XPreformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int','frags':'int'})))
+ elif node.localName=='pre':
+ style = self.styles.para_style_get(node)
+ return platypus.Preformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int'})))
+ elif node.localName=='illustration':
+ return self._illustration(node)
+ elif node.localName=='blockTable':
+ return self._table(node)
+ elif node.localName=='title':
+ styles = reportlab.lib.styles.getSampleStyleSheet()
+ style = styles['Title']
+ return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
+ elif re.match('^h([1-9]+[0-9]*)$', node.localName):
+ styles = reportlab.lib.styles.getSampleStyleSheet()
+ style = styles['Heading'+str(node.localName[1:])]
+ return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
+ elif node.localName=='image':
+ if not node.hasAttribute('file'):
+ if node.hasAttribute('name'):
+ image_data = self.doc.images[node.getAttribute('name')].read()
+ else:
+ import base64
+ image_data = base64.decodestring(node.firstChild.nodeValue)
+
+ image = StringIO()
+ image.write(image_data)
+ image.seek(0)
+ return platypus.Image(image, mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height'])))
+ else:
+ return platypus.Image(node.getAttribute('file'), mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height'])))
+
+ from reportlab.lib.utils import ImageReader
+ name = str(node.getAttribute('file'))
+ img = ImageReader(name)
+ (sx,sy) = img.getSize()
+
+ args = {}
+ for tag in ('width','height'):
+ if node.hasAttribute(tag):
+ args[tag] = utils.unit_get(node.getAttribute(tag))
+ if ('width' in args) and (not 'height' in args):
+ args['height'] = sy * args['width'] / sx
+ elif ('height' in args) and (not 'width' in args):
+ args['width'] = sx * args['height'] / sy
+ elif ('width' in args) and ('height' in args):
+ if (float(args['width'])/args['height'])>(float(sx)>sy):
+ args['width'] = sx * args['height'] / sy
+ else:
+ args['height'] = sy * args['width'] / sx
+ return platypus.Image(name, mask=(250,255,250,255,250,255), **args)
+ elif node.localName=='spacer':
+ if node.hasAttribute('width'):
+ width = utils.unit_get(node.getAttribute('width'))
+ else:
+ width = utils.unit_get('1cm')
+ length = utils.unit_get(node.getAttribute('length'))
+ return platypus.Spacer(width=width, height=length)
+ elif node.localName=='section':
+ return self.render(node)
+ elif node.localName == 'pageNumberReset':
+ return PageReset()
+ elif node.localName in ('pageBreak', 'nextPage'):
+ return platypus.PageBreak()
+ elif node.localName=='condPageBreak':
+ return platypus.CondPageBreak(**(utils.attr_get(node, ['height'])))
+ elif node.localName=='setNextTemplate':
+ return platypus.NextPageTemplate(str(node.getAttribute('name')))
+ elif node.localName=='nextFrame':
+ return platypus.CondPageBreak(1000) # TODO: change the 1000 !
+ elif node.localName == 'setNextFrame':
+ from reportlab.platypus.doctemplate import NextFrameFlowable
+ return NextFrameFlowable(str(node.getAttribute('name')))
+ elif node.localName == 'currentFrame':
+ from reportlab.platypus.doctemplate import CurrentFrameFlowable
+ return CurrentFrameFlowable(str(node.getAttribute('name')))
+ elif node.localName == 'frameEnd':
+ return EndFrameFlowable()
+ elif node.localName == 'hr':
+ width_hr=node.hasAttribute('width') and node.getAttribute('width') or '100%'
+ color_hr=node.hasAttribute('color') and node.getAttribute('color') or 'black'
+ thickness_hr=node.hasAttribute('thickness') and node.getAttribute('thickness') or 1
+ lineCap_hr=node.hasAttribute('lineCap') and node.getAttribute('lineCap') or 'round'
+ return platypus.flowables.HRFlowable(width=width_hr,color=color.get(color_hr),thickness=float(thickness_hr),lineCap=str(lineCap_hr))
+ else:
+ sys.stderr.write('Warning: flowable not yet implemented: %s !\n' % (node.localName,))
+ return None
+
+ def render(self, node_story):
+ story = []
+ node = node_story.firstChild
+ while node:
+ if node.nodeType == node.ELEMENT_NODE:
+ flow = self._flowable(node)
+ if flow:
+ if type(flow) == type([]):
+ story = story + flow
+ else:
+ story.append(flow)
+ node = node.nextSibling
+ return story
from reportlab.platypus.doctemplate import ActionFlowable
class EndFrameFlowable(ActionFlowable):
- def __init__(self,resume=0):
- ActionFlowable.__init__(self,('frameEnd',resume))
+ def __init__(self,resume=0):
+ ActionFlowable.__init__(self,('frameEnd',resume))
class TinyDocTemplate(platypus.BaseDocTemplate):
-
- def ___handle_pageBegin(self):
- self.page = self.page + 1
- self.pageTemplate.beforeDrawPage(self.canv,self)
- self.pageTemplate.checkPageSize(self.canv,self)
- self.pageTemplate.onPage(self.canv,self)
- for f in self.pageTemplate.frames: f._reset()
- self.beforePage()
- #keep a count of flowables added to this page. zero indicates bad stuff
- self._curPageFlowableCount = 0
- if hasattr(self,'_nextFrameIndex'):
- del self._nextFrameIndex
- for f in self.pageTemplate.frames:
- if f.id == 'first':
- self.frame = f
- break
- self.handle_frameBegin()
+ def ___handle_pageBegin(self):
+ self.page = self.page + 1
+ self.pageTemplate.beforeDrawPage(self.canv,self)
+ self.pageTemplate.checkPageSize(self.canv,self)
+ self.pageTemplate.onPage(self.canv,self)
+ for f in self.pageTemplate.frames: f._reset()
+ self.beforePage()
+ #keep a count of flowables added to this page. zero indicates bad stuff
+ self._curPageFlowableCount = 0
+ if hasattr(self,'_nextFrameIndex'):
+ del self._nextFrameIndex
+ for f in self.pageTemplate.frames:
+ if f.id == 'first':
+ self.frame = f
+ break
+ self.handle_frameBegin()
+ def afterFlowable(self, flowable):
+ if isinstance(flowable, PageReset):
+ self.canv._pageNumber = 0
class _rml_template(object):
- def __init__(self, out, node, doc, images={}, path='.'):
- self.images= images
- self.path = path
- if not node.hasAttribute('pageSize'):
- pageSize = (utils.unit_get('21cm'), utils.unit_get('29.7cm'))
- else:
- ps = map(lambda x:x.strip(), node.getAttribute('pageSize').replace(')', '').replace('(', '').split(','))
- pageSize = ( utils.unit_get(ps[0]),utils.unit_get(ps[1]) )
- cm = reportlab.lib.units.cm
- self.doc_tmpl = TinyDocTemplate(out, pagesize=pageSize, **utils.attr_get(node, ['leftMargin','rightMargin','topMargin','bottomMargin'], {'allowSplitting':'int','showBoundary':'bool','title':'str','author':'str'}))
- self.page_templates = []
- self.styles = doc.styles
- self.doc = doc
- pts = node.getElementsByTagName('pageTemplate')
- for pt in pts:
- frames = []
- for frame_el in pt.getElementsByTagName('frame'):
- frame = platypus.Frame( **(utils.attr_get(frame_el, ['x1','y1', 'width','height', 'leftPadding', 'rightPadding', 'bottomPadding', 'topPadding'], {'id':'str', 'showBoundary':'bool'})) )
- if utils.attr_get(frame_el, ['last']):
- frame.lastFrame = True
- frames.append( frame )
- gr = pt.getElementsByTagName('pageGraphics')
- if len(gr):
- drw = _rml_draw(gr[0], self.doc, images=images, path=self.path)
- self.page_templates.append( platypus.PageTemplate(frames=frames, onPage=drw.render, **utils.attr_get(pt, [], {'id':'str'}) ))
- else:
- self.page_templates.append( platypus.PageTemplate(frames=frames, **utils.attr_get(pt, [], {'id':'str'}) ))
- self.doc_tmpl.addPageTemplates(self.page_templates)
-
- def render(self, node_stories):
- fis = []
- r = _rml_flowable(self.doc,images=self.images, path=self.path)
- for node_story in node_stories:
- fis += r.render(node_story)
- fis.append(platypus.PageBreak())
- self.doc_tmpl.build(fis)
-
-def parseString(data, fout=None, images={}, path='.'):
- r = _rml_doc(data, images, path)
- if fout:
- fp = file(fout,'wb')
- r.render(fp)
- fp.close()
- return fout
- else:
- fp = StringIO.StringIO()
- r.render(fp)
- return fp.getvalue()
+ def __init__(self, out, node, doc, images={}, path='.', title=None):
+ self.images= images
+ self.path = path
+ self.title = title
+ if not node.hasAttribute('pageSize'):
+ pageSize = (utils.unit_get('21cm'), utils.unit_get('29.7cm'))
+ else:
+ ps = map(lambda x:x.strip(), node.getAttribute('pageSize').replace(')', '').replace('(', '').split(','))
+ pageSize = ( utils.unit_get(ps[0]),utils.unit_get(ps[1]) )
+ cm = reportlab.lib.units.cm
+ self.doc_tmpl = TinyDocTemplate(out, pagesize=pageSize, **utils.attr_get(node, ['leftMargin','rightMargin','topMargin','bottomMargin'], {'allowSplitting':'int','showBoundary':'bool','title':'str','author':'str'}))
+ self.page_templates = []
+ self.styles = doc.styles
+ self.doc = doc
+ pts = node.getElementsByTagName('pageTemplate')
+ for pt in pts:
+ frames = []
+ for frame_el in pt.getElementsByTagName('frame'):
+ frame = platypus.Frame( **(utils.attr_get(frame_el, ['x1','y1', 'width','height', 'leftPadding', 'rightPadding', 'bottomPadding', 'topPadding'], {'id':'str', 'showBoundary':'bool'})) )
+ if utils.attr_get(frame_el, ['last']):
+ frame.lastFrame = True
+ frames.append( frame )
+ gr = pt.getElementsByTagName('pageGraphics')
+ if len(gr):
+ drw = _rml_draw(gr[0], self.doc, images=images, path=self.path, title=self.title)
+ self.page_templates.append( platypus.PageTemplate(frames=frames, onPage=drw.render, **utils.attr_get(pt, [], {'id':'str'}) ))
+ else:
+ drw = _rml_draw(node,self.doc,title=self.title)
+ self.page_templates.append( platypus.PageTemplate(frames=frames,onPage=drw.render, **utils.attr_get(pt, [], {'id':'str'}) ))
+ self.doc_tmpl.addPageTemplates(self.page_templates)
+
+ def render(self, node_stories):
+ fis = []
+ r = _rml_flowable(self.doc,images=self.images, path=self.path, title=self.title)
+ for node_story in node_stories:
+ fis += r.render(node_story)
+ if node_story==node_stories[-1]:
+
+ fis.append(PageCount())
+
+ fis.append(platypus.PageBreak())
+
+ self.doc_tmpl.build(fis)
+
+def parseString(data, fout=None, images={}, path='.',title=None):
+ r = _rml_doc(data, images, path, title=title)
+
+ #try to override some font mappings
+ try:
+ from customfonts import SetCustomFonts
+ SetCustomFonts(r)
+ except:
+ pass
+
+ if fout:
+ fp = file(fout,'wb')
+ r.render(fp)
+ fp.close()
+ return fout
+ else:
+ fp = StringIO()
+ r.render(fp)
+ return fp.getvalue()
def trml2pdf_help():
- print 'Usage: trml2pdf input.rml >output.pdf'
- print 'Render the standard input (RML) and output a PDF file'
- sys.exit(0)
+ print 'Usage: trml2pdf input.rml >output.pdf'
+ print 'Render the standard input (RML) and output a PDF file'
+ sys.exit(0)
if __name__=="__main__":
- if len(sys.argv)>1:
- if sys.argv[1]=='--help':
- trml2pdf_help()
- print parseString(file(sys.argv[1], 'r').read())
- else:
- print 'Usage: trml2pdf input.rml >output.pdf'
- print 'Try \'trml2pdf --help\' for more information.'
+ if len(sys.argv)>1:
+ if sys.argv[1]=='--help':
+ trml2pdf_help()
+ print parseString(file(sys.argv[1], 'r').read()),
+ else:
+ print 'Usage: trml2pdf input.rml >output.pdf'
+ print 'Try \'trml2pdf --help\' for more information.'
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+