2009-10-20 10:52:23 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
2008-06-16 11:00:21 +00:00
|
|
|
##############################################################################
|
2010-05-11 12:56:31 +00:00
|
|
|
#
|
2008-11-15 06:14:55 +00:00
|
|
|
# OpenERP, Open Source Management Solution
|
2009-10-14 12:32:15 +00:00
|
|
|
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
2008-06-16 11:00:21 +00:00
|
|
|
#
|
2008-11-03 18:27:16 +00:00
|
|
|
# This program is free software: you can redistribute it and/or modify
|
2009-10-14 12:32:15 +00:00
|
|
|
# it under the terms of the GNU Affero General Public License as
|
|
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
|
|
# License, or (at your option) any later version.
|
2008-06-16 11:00:21 +00:00
|
|
|
#
|
2008-11-03 18:27:16 +00:00
|
|
|
# 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
|
2009-10-14 12:32:15 +00:00
|
|
|
# GNU Affero General Public License for more details.
|
2008-06-16 11:00:21 +00:00
|
|
|
#
|
2009-10-14 12:32:15 +00:00
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
2010-05-11 12:56:31 +00:00
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2008-06-16 11:00:21 +00:00
|
|
|
#
|
2008-11-03 18:27:16 +00:00
|
|
|
##############################################################################
|
2006-12-07 13:41:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
import sys
|
2009-01-09 12:18:20 +00:00
|
|
|
from StringIO import StringIO
|
2006-12-07 13:41:40 +00:00
|
|
|
import copy
|
|
|
|
import reportlab
|
2008-06-30 20:47:46 +00:00
|
|
|
import re
|
2006-12-07 13:41:40 +00:00
|
|
|
from reportlab.pdfgen import canvas
|
|
|
|
from reportlab import platypus
|
2009-01-16 11:21:49 +00:00
|
|
|
import cStringIO
|
2006-12-07 13:41:40 +00:00
|
|
|
import utils
|
|
|
|
import color
|
|
|
|
import os
|
2009-04-28 10:24:21 +00:00
|
|
|
from lxml import etree
|
|
|
|
import base64
|
|
|
|
from reportlab.platypus.doctemplate import ActionFlowable
|
2010-05-03 23:49:53 +00:00
|
|
|
from tools.safe_eval import safe_eval as eval
|
2010-10-01 14:07:24 +00:00
|
|
|
from reportlab.lib.units import inch,cm,mm
|
2008-09-24 15:34:10 +00:00
|
|
|
|
2007-01-07 21:24:58 +00:00
|
|
|
encoding = 'utf-8'
|
2006-12-07 13:41:40 +00:00
|
|
|
|
2010-10-01 14:07:24 +00:00
|
|
|
class NumberedCanvas(canvas.Canvas):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
canvas.Canvas.__init__(self, *args, **kwargs)
|
|
|
|
self._codes = []
|
|
|
|
self._flag=False
|
|
|
|
self._pageCount=0
|
2010-10-04 13:35:06 +00:00
|
|
|
self._currentPage =0
|
|
|
|
self._pageCounter=0
|
2010-10-01 14:07:24 +00:00
|
|
|
self.pages={}
|
|
|
|
|
|
|
|
def showPage(self):
|
2010-10-04 13:35:06 +00:00
|
|
|
self._currentPage +=1
|
2010-10-01 14:07:24 +00:00
|
|
|
if not self._flag:
|
|
|
|
self._pageCount += 1
|
|
|
|
else:
|
2010-10-04 13:35:06 +00:00
|
|
|
self.pages.update({self._currentPage:self._pageCount})
|
2010-10-01 14:07:24 +00:00
|
|
|
self._codes.append({'code': self._code, 'stack': self._codeStack})
|
|
|
|
self._startPage()
|
|
|
|
self._flag=False
|
|
|
|
|
|
|
|
def pageCount(self):
|
2010-10-04 13:35:06 +00:00
|
|
|
if self.pages.get(self._pageCounter,False):
|
2010-10-01 14:07:24 +00:00
|
|
|
self._pageNumber=0
|
2010-10-04 13:35:06 +00:00
|
|
|
self._pageCounter +=1
|
|
|
|
key=self._pageCounter
|
2010-10-01 14:07:24 +00:00
|
|
|
if not self.pages.get(key,False):
|
|
|
|
while not self.pages.get(key,False):
|
|
|
|
key = key + 1
|
2010-10-04 13:35:06 +00:00
|
|
|
self.setFont("Helvetica", 8)
|
2010-10-05 07:27:09 +00:00
|
|
|
self.drawRightString((self._pagesize[0]-35), (self._pagesize[1]-43),
|
|
|
|
"Page %(this)i of %(total)i" % {
|
2010-10-01 14:07:24 +00:00
|
|
|
'this': self._pageNumber+1,
|
|
|
|
'total': self.pages.get(key,False),
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
def save(self):
|
|
|
|
"""add page info to each page (page x of y)"""
|
|
|
|
# reset page counter
|
|
|
|
self._pageNumber = 0
|
|
|
|
for code in self._codes:
|
|
|
|
self._code = code['code']
|
|
|
|
self._codeStack = code['stack']
|
|
|
|
self.pageCount()
|
|
|
|
canvas.Canvas.showPage(self)
|
2010-10-04 13:35:06 +00:00
|
|
|
# self.restoreState()
|
2010-10-01 14:07:24 +00:00
|
|
|
self._doc.SaveToFile(self._filename, self)
|
|
|
|
|
2008-06-30 22:23:47 +00:00
|
|
|
class PageCount(platypus.Flowable):
|
2008-07-22 14:24:36 +00:00
|
|
|
def draw(self):
|
|
|
|
self.canv.beginForm("pageCount")
|
|
|
|
self.canv.setFont("Helvetica", utils.unit_get(str(8)))
|
2010-09-27 13:17:41 +00:00
|
|
|
self.canv.drawString(0, 0, str(self.canv.getPageNumber()))
|
2008-07-22 14:24:36 +00:00
|
|
|
self.canv.endForm()
|
2008-06-30 22:23:47 +00:00
|
|
|
|
|
|
|
class PageReset(platypus.Flowable):
|
2008-07-22 14:24:36 +00:00
|
|
|
def draw(self):
|
|
|
|
self.canv._pageNumber = 0
|
2008-06-30 22:23:47 +00:00
|
|
|
|
2009-04-28 10:24:21 +00:00
|
|
|
class _rml_styles(object,):
|
|
|
|
def __init__(self, nodes, localcontext):
|
|
|
|
self.localcontext = localcontext
|
2008-07-22 14:24:36 +00:00
|
|
|
self.styles = {}
|
2010-05-14 13:13:55 +00:00
|
|
|
self.styles_obj = {}
|
2008-07-22 14:24:36 +00:00
|
|
|
self.names = {}
|
|
|
|
self.table_styles = {}
|
2010-05-14 13:13:55 +00:00
|
|
|
self.default_style = reportlab.lib.styles.getSampleStyleSheet()
|
|
|
|
|
2008-07-22 14:24:36 +00:00
|
|
|
for node in nodes:
|
2009-04-28 10:24:21 +00:00
|
|
|
for style in node.findall('blockTableStyle'):
|
|
|
|
self.table_styles[style.get('id')] = self._table_style_get(style)
|
|
|
|
for style in node.findall('paraStyle'):
|
2010-05-14 13:13:55 +00:00
|
|
|
sname = style.get('name')
|
|
|
|
self.styles[sname] = self._para_style_update(style)
|
2010-09-08 05:56:20 +00:00
|
|
|
|
2010-05-14 13:13:55 +00:00
|
|
|
self.styles_obj[sname] = reportlab.lib.styles.ParagraphStyle(sname, self.default_style["Normal"], **self.styles[sname])
|
|
|
|
|
2009-04-28 10:24:21 +00:00
|
|
|
for variable in node.findall('initialize'):
|
|
|
|
for name in variable.findall('name'):
|
|
|
|
self.names[ name.get('id')] = name.get('value')
|
2008-07-22 14:24:36 +00:00
|
|
|
|
|
|
|
def _para_style_update(self, node):
|
|
|
|
data = {}
|
|
|
|
for attr in ['textColor', 'backColor', 'bulletColor', 'borderColor']:
|
2009-04-28 10:24:21 +00:00
|
|
|
if node.get(attr):
|
|
|
|
data[attr] = color.get(node.get(attr))
|
2008-07-22 14:24:36 +00:00
|
|
|
for attr in ['fontName', 'bulletFontName', 'bulletText']:
|
2009-04-28 10:24:21 +00:00
|
|
|
if node.get(attr):
|
|
|
|
data[attr] = node.get(attr)
|
2008-08-11 09:27:55 +00:00
|
|
|
for attr in ['fontSize', 'leftIndent', 'rightIndent', 'spaceBefore', 'spaceAfter',
|
2008-07-22 14:24:36 +00:00
|
|
|
'firstLineIndent', 'bulletIndent', 'bulletFontSize', 'leading',
|
|
|
|
'borderWidth','borderPadding','borderRadius']:
|
2009-04-28 10:24:21 +00:00
|
|
|
if node.get(attr):
|
|
|
|
data[attr] = utils.unit_get(node.get(attr))
|
|
|
|
if node.get('alignment'):
|
2008-07-22 14:24:36 +00:00
|
|
|
align = {
|
|
|
|
'right':reportlab.lib.enums.TA_RIGHT,
|
|
|
|
'center':reportlab.lib.enums.TA_CENTER,
|
|
|
|
'justify':reportlab.lib.enums.TA_JUSTIFY
|
|
|
|
}
|
2009-04-28 10:24:21 +00:00
|
|
|
data['alignment'] = align.get(node.get('alignment').lower(), reportlab.lib.enums.TA_LEFT)
|
2008-07-22 14:24:36 +00:00
|
|
|
return data
|
|
|
|
|
|
|
|
def _table_style_get(self, style_node):
|
|
|
|
styles = []
|
2009-11-28 11:58:09 +00:00
|
|
|
for node in style_node:
|
2009-04-28 10:24:21 +00:00
|
|
|
start = utils.tuple_int_get(node, 'start', (0,0) )
|
|
|
|
stop = utils.tuple_int_get(node, 'stop', (-1,-1) )
|
|
|
|
if node.tag=='blockValign':
|
|
|
|
styles.append(('VALIGN', start, stop, str(node.get('value'))))
|
|
|
|
elif node.tag=='blockFont':
|
|
|
|
styles.append(('FONT', start, stop, str(node.get('name'))))
|
|
|
|
elif node.tag=='blockTextColor':
|
|
|
|
styles.append(('TEXTCOLOR', start, stop, color.get(str(node.get('colorName')))))
|
|
|
|
elif node.tag=='blockLeading':
|
|
|
|
styles.append(('LEADING', start, stop, utils.unit_get(node.get('length'))))
|
|
|
|
elif node.tag=='blockAlignment':
|
|
|
|
styles.append(('ALIGNMENT', start, stop, str(node.get('value'))))
|
|
|
|
elif node.tag=='blockSpan':
|
|
|
|
styles.append(('SPAN', start, stop))
|
|
|
|
elif node.tag=='blockLeftPadding':
|
|
|
|
styles.append(('LEFTPADDING', start, stop, utils.unit_get(node.get('length'))))
|
|
|
|
elif node.tag=='blockRightPadding':
|
|
|
|
styles.append(('RIGHTPADDING', start, stop, utils.unit_get(node.get('length'))))
|
|
|
|
elif node.tag=='blockTopPadding':
|
|
|
|
styles.append(('TOPPADDING', start, stop, utils.unit_get(node.get('length'))))
|
|
|
|
elif node.tag=='blockBottomPadding':
|
|
|
|
styles.append(('BOTTOMPADDING', start, stop, utils.unit_get(node.get('length'))))
|
|
|
|
elif node.tag=='blockBackground':
|
|
|
|
styles.append(('BACKGROUND', start, stop, color.get(node.get('colorName'))))
|
|
|
|
if node.get('size'):
|
|
|
|
styles.append(('FONTSIZE', start, stop, utils.unit_get(node.get('size'))))
|
|
|
|
elif node.tag=='lineStyle':
|
|
|
|
kind = node.get('kind')
|
|
|
|
kind_list = [ 'GRID', 'BOX', 'OUTLINE', 'INNERGRID', 'LINEBELOW', 'LINEABOVE','LINEBEFORE', 'LINEAFTER' ]
|
|
|
|
assert kind in kind_list
|
|
|
|
thick = 1
|
|
|
|
if node.get('thickness'):
|
|
|
|
thick = float(node.get('thickness'))
|
|
|
|
styles.append((kind, start, stop, thick, color.get(node.get('colorName'))))
|
2008-07-22 14:24:36 +00:00
|
|
|
return platypus.tables.TableStyle(styles)
|
|
|
|
|
|
|
|
def para_style_get(self, node):
|
|
|
|
style = False
|
2010-05-14 13:13:55 +00:00
|
|
|
sname = node.get('style')
|
|
|
|
if sname:
|
|
|
|
if sname in self.styles_obj:
|
|
|
|
style = self.styles_obj[sname]
|
2008-07-22 14:24:36 +00:00
|
|
|
else:
|
2009-04-28 10:24:21 +00:00
|
|
|
sys.stderr.write('Warning: style not found, %s - setting default!\n' % (node.get('style'),) )
|
2008-07-22 14:24:36 +00:00
|
|
|
if not style:
|
2010-05-14 13:13:55 +00:00
|
|
|
style = self.default_style['Normal']
|
|
|
|
para_update = self._para_style_update(node)
|
|
|
|
if para_update:
|
|
|
|
# update style only is necessary
|
|
|
|
style = copy.deepcopy(style)
|
|
|
|
style.__dict__.update(para_update)
|
2008-07-22 14:24:36 +00:00
|
|
|
return style
|
2006-12-07 13:41:40 +00:00
|
|
|
|
|
|
|
class _rml_doc(object):
|
2009-04-28 10:24:21 +00:00
|
|
|
def __init__(self, node, localcontext, images={}, path='.', title=None):
|
|
|
|
self.localcontext = localcontext
|
|
|
|
self.etree = node
|
|
|
|
self.filename = self.etree.get('filename')
|
2008-07-22 14:24:36 +00:00
|
|
|
self.images = images
|
|
|
|
self.path = path
|
2008-09-24 15:34:10 +00:00
|
|
|
self.title = title
|
2008-07-22 14:24:36 +00:00
|
|
|
|
|
|
|
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:
|
2009-04-28 10:24:21 +00:00
|
|
|
for font in node.findall('registerFont'):
|
|
|
|
name = font.get('fontName').encode('ascii')
|
|
|
|
fname = font.get('fontFile').encode('ascii')
|
2008-07-22 14:24:36 +00:00
|
|
|
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
|
|
|
|
|
2009-02-07 19:26:06 +00:00
|
|
|
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
|
2010-05-11 12:56:31 +00:00
|
|
|
|
2009-11-24 14:44:05 +00:00
|
|
|
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
|
2009-02-07 19:26:06 +00:00
|
|
|
|
2008-07-22 14:24:36 +00:00
|
|
|
def _textual_image(self, node):
|
|
|
|
rc = ''
|
2009-11-28 11:58:09 +00:00
|
|
|
for n in node:
|
2009-04-28 10:24:21 +00:00
|
|
|
rc +=( etree.tostring(n) or '') + n.tail
|
|
|
|
return base64.decodestring(node.tostring())
|
2008-07-22 14:24:36 +00:00
|
|
|
|
|
|
|
def _images(self, el):
|
|
|
|
result = {}
|
2009-04-28 10:24:21 +00:00
|
|
|
for node in el.findall('image'):
|
|
|
|
rc =( node.text or '')
|
|
|
|
result[node.get('name')] = base64.decodestring(rc)
|
2008-07-22 14:24:36 +00:00
|
|
|
return result
|
|
|
|
|
|
|
|
def render(self, out):
|
2009-04-28 10:24:21 +00:00
|
|
|
el = self.etree.findall('docinit')
|
2008-07-22 14:24:36 +00:00
|
|
|
if el:
|
|
|
|
self.docinit(el)
|
|
|
|
|
2009-04-28 10:24:21 +00:00
|
|
|
el = self.etree.findall('stylesheet')
|
|
|
|
self.styles = _rml_styles(el,self.localcontext)
|
2008-07-22 14:24:36 +00:00
|
|
|
|
2009-04-28 10:24:21 +00:00
|
|
|
el = self.etree.findall('images')
|
2008-07-22 14:24:36 +00:00
|
|
|
if el:
|
|
|
|
self.images.update( self._images(el[0]) )
|
2009-04-28 10:24:21 +00:00
|
|
|
el = self.etree.findall('template')
|
2008-07-22 14:24:36 +00:00
|
|
|
if len(el):
|
2009-04-28 10:24:21 +00:00
|
|
|
pt_obj = _rml_template(self.localcontext, out, el[0], self, images=self.images, path=self.path, title=self.title)
|
|
|
|
el = utils._child_get(self.etree, self, 'story')
|
|
|
|
pt_obj.render(el)
|
2008-07-22 14:24:36 +00:00
|
|
|
else:
|
|
|
|
self.canvas = canvas.Canvas(out)
|
2009-04-28 10:24:21 +00:00
|
|
|
pd = self.etree.find('pageDrawing')[0]
|
|
|
|
pd_obj = _rml_canvas(self.canvas, self.localcontext, None, self, self.images, path=self.path, title=self.title)
|
2008-07-22 14:24:36 +00:00
|
|
|
pd_obj.render(pd)
|
|
|
|
|
|
|
|
self.canvas.showPage()
|
|
|
|
self.canvas.save()
|
2006-12-07 13:41:40 +00:00
|
|
|
|
|
|
|
class _rml_canvas(object):
|
2009-07-22 10:46:16 +00:00
|
|
|
def __init__(self, canvas, localcontext, doc_tmpl=None, doc=None, images={}, path='.', title=None):
|
2009-04-28 10:24:21 +00:00
|
|
|
self.localcontext = localcontext
|
2008-07-22 14:24:36 +00:00
|
|
|
self.canvas = canvas
|
|
|
|
self.styles = doc.styles
|
|
|
|
self.doc_tmpl = doc_tmpl
|
|
|
|
self.doc = doc
|
|
|
|
self.images = images
|
|
|
|
self.path = path
|
2008-09-24 15:34:10 +00:00
|
|
|
self.title = title
|
|
|
|
if self.title:
|
|
|
|
self.canvas.setTitle(self.title)
|
2008-07-22 14:24:36 +00:00
|
|
|
|
|
|
|
def _textual(self, node, x=0, y=0):
|
2009-08-17 12:37:47 +00:00
|
|
|
text = node.text and node.text.encode('utf-8') or ''
|
|
|
|
rc = utils._process_text(self, text)
|
2009-04-28 10:24:21 +00:00
|
|
|
for n in node:
|
|
|
|
if n.tag == 'seq':
|
|
|
|
from reportlab.lib.sequencer import getSequencer
|
|
|
|
seq = getSequencer()
|
|
|
|
rc += str(seq.next(n.get('id')))
|
|
|
|
if n.tag == '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.tag == 'pageNumber':
|
|
|
|
rc += str(self.canvas.getPageNumber())
|
|
|
|
rc += utils._process_text(self, n.tail)
|
2010-09-27 10:09:55 +00:00
|
|
|
return rc.replace('\n','')
|
2008-07-22 14:24:36 +00:00
|
|
|
|
|
|
|
def _drawString(self, node):
|
|
|
|
v = utils.attr_get(node, ['x','y'])
|
2009-04-28 10:24:21 +00:00
|
|
|
text=self._textual(node, **v)
|
2009-06-26 11:27:44 +00:00
|
|
|
text = utils.xml2str(text)
|
2009-04-28 10:24:21 +00:00
|
|
|
self.canvas.drawString(text=text, **v)
|
|
|
|
|
2008-07-22 14:24:36 +00:00
|
|
|
def _drawCenteredString(self, node):
|
|
|
|
v = utils.attr_get(node, ['x','y'])
|
2009-04-28 10:24:21 +00:00
|
|
|
text=self._textual(node, **v)
|
2009-06-26 11:27:44 +00:00
|
|
|
text = utils.xml2str(text)
|
2009-04-28 10:24:21 +00:00
|
|
|
self.canvas.drawCentredString(text=text, **v)
|
|
|
|
|
2008-07-22 14:24:36 +00:00
|
|
|
def _drawRightString(self, node):
|
|
|
|
v = utils.attr_get(node, ['x','y'])
|
2009-04-28 10:24:21 +00:00
|
|
|
text=self._textual(node, **v)
|
2009-06-26 11:27:44 +00:00
|
|
|
text = utils.xml2str(text)
|
2009-04-28 10:24:21 +00:00
|
|
|
self.canvas.drawRightString(text=text, **v)
|
|
|
|
|
2008-07-22 14:24:36 +00:00
|
|
|
def _rect(self, node):
|
2009-04-28 10:24:21 +00:00
|
|
|
if node.get('round'):
|
|
|
|
self.canvas.roundRect(radius=utils.unit_get(node.get('round')), **utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'}))
|
2008-07-22 14:24:36 +00:00
|
|
|
else:
|
|
|
|
self.canvas.rect(**utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'}))
|
|
|
|
|
|
|
|
def _ellipse(self, node):
|
2009-04-28 10:24:21 +00:00
|
|
|
x1 = utils.unit_get(node.get('x'))
|
|
|
|
x2 = utils.unit_get(node.get('width'))
|
|
|
|
y1 = utils.unit_get(node.get('y'))
|
|
|
|
y2 = utils.unit_get(node.get('height'))
|
|
|
|
|
2008-07-22 14:24:36 +00:00
|
|
|
self.canvas.ellipse(x1,y1,x2,y2, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'}))
|
2009-04-28 10:24:21 +00:00
|
|
|
|
2008-07-22 14:24:36 +00:00
|
|
|
def _curves(self, node):
|
2009-04-28 10:24:21 +00:00
|
|
|
line_str = node.text.split()
|
2008-07-22 14:24:36 +00:00
|
|
|
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:]
|
2009-04-28 10:24:21 +00:00
|
|
|
|
2008-07-22 14:24:36 +00:00
|
|
|
def _lines(self, node):
|
2009-04-28 10:24:21 +00:00
|
|
|
line_str = node.text.split()
|
2008-07-22 14:24:36 +00:00
|
|
|
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)
|
2009-04-28 10:24:21 +00:00
|
|
|
|
2008-07-22 14:24:36 +00:00
|
|
|
def _grid(self, node):
|
2009-04-28 10:24:21 +00:00
|
|
|
xlist = [utils.unit_get(s) for s in node.get('xs').split(',')]
|
|
|
|
ylist = [utils.unit_get(s) for s in node.get('ys').split(',')]
|
|
|
|
|
2008-07-22 14:24:36 +00:00
|
|
|
self.canvas.grid(xlist, ylist)
|
2009-04-28 10:24:21 +00:00
|
|
|
|
2008-07-22 14:24:36 +00:00
|
|
|
def _translate(self, node):
|
2009-04-28 10:24:21 +00:00
|
|
|
dx = utils.unit_get(node.get('dx')) or 0
|
|
|
|
dy = utils.unit_get(node.get('dy')) or 0
|
2008-07-22 14:24:36 +00:00
|
|
|
self.canvas.translate(dx,dy)
|
|
|
|
|
|
|
|
def _circle(self, node):
|
2009-04-28 10:24:21 +00:00
|
|
|
self.canvas.circle(x_cen=utils.unit_get(node.get('x')), y_cen=utils.unit_get(node.get('y')), r=utils.unit_get(node.get('radius')), **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'}))
|
2008-07-22 14:24:36 +00:00
|
|
|
|
|
|
|
def _place(self, node):
|
2009-07-22 10:46:16 +00:00
|
|
|
flows = _rml_flowable(self.doc, self.localcontext, images=self.images, path=self.path, title=self.title).render(node)
|
2008-07-22 14:24:36 +00:00
|
|
|
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}
|
2009-04-28 10:24:21 +00:00
|
|
|
|
|
|
|
if node.get('width'):
|
|
|
|
self.canvas.setLineWidth(utils.unit_get(node.get('width')))
|
|
|
|
if node.get('join'):
|
|
|
|
self.canvas.setLineJoin(ljoin[node.get('join')])
|
|
|
|
if node.get('cap'):
|
|
|
|
self.canvas.setLineCap(lcap[node.get('cap')])
|
|
|
|
if node.get('miterLimit'):
|
|
|
|
self.canvas.setDash(utils.unit_get(node.get('miterLimit')))
|
|
|
|
if node.get('dash'):
|
|
|
|
dashes = node.get('dash').split(',')
|
2008-07-22 14:24:36 +00:00
|
|
|
for x in range(len(dashes)):
|
|
|
|
dashes[x]=utils.unit_get(dashes[x])
|
2009-04-28 10:24:21 +00:00
|
|
|
self.canvas.setDash(node.get('dash').split(','))
|
2008-07-22 14:24:36 +00:00
|
|
|
|
|
|
|
def _image(self, node):
|
|
|
|
import urllib
|
|
|
|
from reportlab.lib.utils import ImageReader
|
2009-04-28 10:24:21 +00:00
|
|
|
if not node.get('file') :
|
|
|
|
if node.get('name'):
|
|
|
|
image_data = self.images[node.get('name')]
|
2009-01-16 11:21:49 +00:00
|
|
|
s = cStringIO.StringIO(image_data)
|
2008-07-22 14:24:36 +00:00
|
|
|
else:
|
2009-04-28 10:24:21 +00:00
|
|
|
if self.localcontext:
|
2010-08-30 09:59:46 +00:00
|
|
|
res = utils._regex.findall(node.text)
|
|
|
|
for key in res:
|
|
|
|
newtext = eval(key, {}, self.localcontext)
|
|
|
|
node.text = newtext
|
|
|
|
image_data = None
|
|
|
|
if node.text:
|
|
|
|
image_data = base64.decodestring(node.text)
|
|
|
|
if image_data:
|
|
|
|
s = cStringIO.StringIO(image_data)
|
|
|
|
else:
|
|
|
|
return False
|
2008-07-22 14:24:36 +00:00
|
|
|
else:
|
2009-04-28 10:24:21 +00:00
|
|
|
if node.get('file') in self.images:
|
|
|
|
s = cStringIO.StringIO(self.images[node.get('file')])
|
2008-07-22 14:24:36 +00:00
|
|
|
else:
|
|
|
|
try:
|
2009-04-28 10:24:21 +00:00
|
|
|
u = urllib.urlopen(str(node.get('file')))
|
|
|
|
s = cStringIO.StringIO(u.read())
|
2010-06-22 09:45:40 +00:00
|
|
|
except Exception:
|
2009-04-28 10:24:21 +00:00
|
|
|
u = file(os.path.join(self.path,str(node.get('file'))), 'rb')
|
|
|
|
s = cStringIO.StringIO(u.read())
|
2008-07-22 14:24:36 +00:00
|
|
|
img = ImageReader(s)
|
|
|
|
(sx,sy) = img.getSize()
|
|
|
|
|
|
|
|
args = {}
|
|
|
|
for tag in ('width','height','x','y'):
|
2009-04-28 10:24:21 +00:00
|
|
|
if node.get(tag):
|
|
|
|
args[tag] = utils.unit_get(node.get(tag))
|
2008-07-22 14:24:36 +00:00
|
|
|
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)
|
2010-10-04 13:35:06 +00:00
|
|
|
# self.canvas._doc.SaveToFile(self.canvas._filename, self.canvas)
|
2008-07-22 14:24:36 +00:00
|
|
|
|
|
|
|
def _path(self, node):
|
|
|
|
self.path = self.canvas.beginPath()
|
|
|
|
self.path.moveTo(**utils.attr_get(node, ['x','y']))
|
2009-04-28 10:24:21 +00:00
|
|
|
for n in utils._child_get(node, self):
|
|
|
|
if not n.text :
|
|
|
|
if n.tag=='moveto':
|
2008-07-22 14:24:36 +00:00
|
|
|
vals = utils.text_get(n).split()
|
|
|
|
self.path.moveTo(utils.unit_get(vals[0]), utils.unit_get(vals[1]))
|
2009-04-28 10:24:21 +00:00
|
|
|
elif n.tag=='curvesto':
|
2008-07-22 14:24:36 +00:00
|
|
|
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)
|
2009-04-28 10:24:21 +00:00
|
|
|
elif n.text:
|
|
|
|
data = n.text.split() # Not sure if I must merge all TEXT_NODE ?
|
2008-07-22 14:24:36 +00:00
|
|
|
while len(data)>1:
|
|
|
|
x = utils.unit_get(data.pop(0))
|
|
|
|
y = utils.unit_get(data.pop(0))
|
|
|
|
self.path.lineTo(x,y)
|
2009-04-28 10:24:21 +00:00
|
|
|
if (not node.get('close')) or utils.bool_get(node.get('close')):
|
2008-07-22 14:24:36 +00:00
|
|
|
self.path.close()
|
|
|
|
self.canvas.drawPath(self.path, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'}))
|
|
|
|
|
2009-11-26 06:30:20 +00:00
|
|
|
def setFont(self, node):
|
2010-05-11 12:56:31 +00:00
|
|
|
from reportlab.pdfbase import pdfmetrics
|
2009-11-26 08:22:08 +00:00
|
|
|
fname = node.get('name')
|
2010-05-11 12:56:31 +00:00
|
|
|
#TODO : other fonts should be supported
|
2009-11-26 08:22:08 +00:00
|
|
|
if fname not in pdfmetrics.standardFonts:
|
2010-05-11 12:56:31 +00:00
|
|
|
fname = self.canvas._fontname
|
2009-11-26 08:22:08 +00:00
|
|
|
return self.canvas.setFont(fname, utils.unit_get(node.get('size')))
|
2009-11-26 06:30:20 +00:00
|
|
|
|
2008-07-22 14:24:36 +00:00
|
|
|
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,
|
2009-04-28 10:24:21 +00:00
|
|
|
'fill': lambda node: self.canvas.setFillColor(color.get(node.get('color'))),
|
|
|
|
'stroke': lambda node: self.canvas.setStrokeColor(color.get(node.get('color'))),
|
2009-11-26 06:30:20 +00:00
|
|
|
'setFont': self.setFont ,
|
2008-07-22 14:24:36 +00:00
|
|
|
'place': self._place,
|
|
|
|
'circle': self._circle,
|
|
|
|
'lineMode': self._line_mode,
|
|
|
|
'path': self._path,
|
2009-04-28 10:24:21 +00:00
|
|
|
'rotate': lambda node: self.canvas.rotate(float(node.get('degrees'))),
|
2008-07-22 14:24:36 +00:00
|
|
|
'translate': self._translate,
|
|
|
|
'image': self._image
|
|
|
|
}
|
2009-04-28 10:24:21 +00:00
|
|
|
for n in utils._child_get(node, self):
|
|
|
|
if n.tag in tags:
|
|
|
|
tags[n.tag](n)
|
2006-12-07 13:41:40 +00:00
|
|
|
|
|
|
|
class _rml_draw(object):
|
2009-04-28 10:24:21 +00:00
|
|
|
def __init__(self, localcontext ,node, styles, images={}, path='.', title=None):
|
|
|
|
self.localcontext = localcontext
|
2008-07-22 14:24:36 +00:00
|
|
|
self.node = node
|
|
|
|
self.styles = styles
|
|
|
|
self.canvas = None
|
|
|
|
self.images = images
|
|
|
|
self.path = path
|
2008-09-24 15:34:10 +00:00
|
|
|
self.canvas_title = title
|
2008-07-22 14:24:36 +00:00
|
|
|
|
|
|
|
def render(self, canvas, doc):
|
|
|
|
canvas.saveState()
|
2009-07-22 10:46:16 +00:00
|
|
|
cnv = _rml_canvas(canvas, self.localcontext, doc, self.styles, images=self.images, path=self.path, title=self.canvas_title)
|
2008-07-22 14:24:36 +00:00
|
|
|
cnv.render(self.node)
|
|
|
|
canvas.restoreState()
|
2006-12-07 13:41:40 +00:00
|
|
|
|
|
|
|
class _rml_flowable(object):
|
2009-07-22 10:46:16 +00:00
|
|
|
def __init__(self, doc, localcontext, images={}, path='.', title=None):
|
2009-04-28 10:24:21 +00:00
|
|
|
self.localcontext = localcontext
|
2008-07-22 14:24:36 +00:00
|
|
|
self.doc = doc
|
|
|
|
self.styles = doc.styles
|
|
|
|
self.images = images
|
|
|
|
self.path = path
|
2008-09-24 15:34:10 +00:00
|
|
|
self.title = title
|
2008-07-22 14:24:36 +00:00
|
|
|
|
|
|
|
def _textual(self, node):
|
2009-04-28 10:24:21 +00:00
|
|
|
rc1 = utils._process_text(self, node.text or '')
|
|
|
|
for n in utils._child_get(node,self):
|
2009-05-25 05:24:18 +00:00
|
|
|
txt_n = copy.deepcopy(n)
|
|
|
|
for key in txt_n.attrib.keys():
|
|
|
|
if key in ('rml_except', 'rml_loop', 'rml_tag'):
|
|
|
|
del txt_n.attrib[key]
|
2009-06-16 13:37:50 +00:00
|
|
|
if True or not self._textual(n).isspace():
|
2009-10-09 10:33:54 +00:00
|
|
|
if not n.tag == 'bullet':
|
|
|
|
txt_n.text = utils.xml2str(self._textual(n))
|
|
|
|
txt_n.tail = n.tail and utils._process_text(self, n.tail.replace('\n','')) or ''
|
2009-05-25 05:24:18 +00:00
|
|
|
rc1 += etree.tostring(txt_n)
|
2009-04-28 10:24:21 +00:00
|
|
|
return rc1
|
2008-07-22 14:24:36 +00:00
|
|
|
|
|
|
|
def _table(self, node):
|
2009-04-28 10:24:21 +00:00
|
|
|
childs = utils._child_get(node,self,'tr')
|
|
|
|
if not childs:
|
|
|
|
return None
|
2008-07-22 14:24:36 +00:00
|
|
|
length = 0
|
|
|
|
colwidths = None
|
|
|
|
rowheights = None
|
|
|
|
data = []
|
|
|
|
styles = []
|
2009-04-28 10:24:21 +00:00
|
|
|
posy = 0
|
2008-07-22 14:24:36 +00:00
|
|
|
for tr in childs:
|
|
|
|
paraStyle = None
|
2009-04-28 10:24:21 +00:00
|
|
|
if tr.get('style'):
|
|
|
|
st = copy.deepcopy(self.styles.table_styles[tr.get('style')])
|
2008-07-22 14:24:36 +00:00
|
|
|
for s in st._cmds:
|
|
|
|
s[1][1] = posy
|
|
|
|
s[2][1] = posy
|
|
|
|
styles.append(st)
|
2009-04-28 10:24:21 +00:00
|
|
|
if tr.get('paraStyle'):
|
|
|
|
paraStyle = self.styles.styles[tr.get('paraStyle')]
|
2008-07-22 14:24:36 +00:00
|
|
|
data2 = []
|
|
|
|
posx = 0
|
2009-04-28 10:24:21 +00:00
|
|
|
for td in utils._child_get(tr, self,'td'):
|
|
|
|
if td.get('style'):
|
|
|
|
st = copy.deepcopy(self.styles.table_styles[td.get('style')])
|
2008-07-22 14:24:36 +00:00
|
|
|
for s in st._cmds:
|
|
|
|
s[1][1] = posy
|
|
|
|
s[2][1] = posy
|
|
|
|
s[1][0] = posx
|
|
|
|
s[2][0] = posx
|
|
|
|
styles.append(st)
|
2009-04-28 10:24:21 +00:00
|
|
|
if td.get('paraStyle'):
|
2008-07-22 14:24:36 +00:00
|
|
|
# TODO: merge styles
|
2009-04-28 10:24:21 +00:00
|
|
|
paraStyle = self.styles.styles[td.get('paraStyle')]
|
2008-07-22 14:24:36 +00:00
|
|
|
posx += 1
|
|
|
|
|
|
|
|
flow = []
|
2009-04-28 10:24:21 +00:00
|
|
|
for n in utils._child_get(td, self):
|
2009-05-25 06:42:45 +00:00
|
|
|
if n.tag == etree.Comment:
|
|
|
|
n.text = ''
|
|
|
|
continue
|
2009-04-28 10:24:21 +00:00
|
|
|
fl = self._flowable(n, extra_style=paraStyle)
|
|
|
|
if isinstance(fl,list):
|
|
|
|
flow += fl
|
|
|
|
else:
|
2008-07-22 14:24:36 +00:00
|
|
|
flow.append( fl )
|
2009-04-28 10:24:21 +00:00
|
|
|
|
2008-07-22 14:24:36 +00:00
|
|
|
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
|
2009-04-28 10:24:21 +00:00
|
|
|
|
|
|
|
if node.get('colWidths'):
|
|
|
|
assert length == len(node.get('colWidths').split(','))
|
|
|
|
colwidths = [utils.unit_get(f.strip()) for f in node.get('colWidths').split(',')]
|
|
|
|
if node.get('rowHeights'):
|
|
|
|
rowheights = [utils.unit_get(f.strip()) for f in node.get('rowHeights').split(',')]
|
2008-07-22 14:24:36 +00:00
|
|
|
if len(rowheights) == 1:
|
|
|
|
rowheights = rowheights[0]
|
2008-09-08 08:30:33 +00:00
|
|
|
table = platypus.LongTable(data = data, colWidths=colwidths, rowHeights=rowheights, **(utils.attr_get(node, ['splitByRow'] ,{'repeatRows':'int','repeatCols':'int'})))
|
2009-04-28 10:24:21 +00:00
|
|
|
if node.get('style'):
|
|
|
|
table.setStyle(self.styles.table_styles[node.get('style')])
|
2008-07-22 14:24:36 +00:00
|
|
|
for s in styles:
|
|
|
|
table.setStyle(s)
|
|
|
|
return table
|
|
|
|
|
|
|
|
def _illustration(self, node):
|
|
|
|
class Illustration(platypus.flowables.Flowable):
|
2009-04-28 10:24:21 +00:00
|
|
|
def __init__(self, node, localcontext, styles, self2):
|
2010-06-04 16:18:02 +00:00
|
|
|
self.localcontext = (localcontext or {}).copy()
|
2008-07-22 14:24:36 +00:00
|
|
|
self.node = node
|
|
|
|
self.styles = styles
|
2009-04-28 10:24:21 +00:00
|
|
|
self.width = utils.unit_get(node.get('width'))
|
|
|
|
self.height = utils.unit_get(node.get('height'))
|
2008-07-22 14:24:36 +00:00
|
|
|
self.self2 = self2
|
|
|
|
def wrap(self, *args):
|
|
|
|
return (self.width, self.height)
|
|
|
|
def draw(self):
|
|
|
|
canvas = self.canv
|
2009-04-28 10:24:21 +00:00
|
|
|
drw = _rml_draw(self.localcontext ,self.node,self.styles, images=self.self2.images, path=self.self2.path, title=self.self2.title)
|
2008-07-22 14:24:36 +00:00
|
|
|
drw.render(self.canv, None)
|
2009-04-28 10:24:21 +00:00
|
|
|
return Illustration(node, self.localcontext, self.styles, self)
|
2008-07-22 14:24:36 +00:00
|
|
|
|
|
|
|
def _textual_image(self, node):
|
2009-04-28 10:24:21 +00:00
|
|
|
return base64.decodestring(node.text)
|
2008-07-22 14:24:36 +00:00
|
|
|
|
|
|
|
def _flowable(self, node, extra_style=None):
|
2009-04-28 10:24:21 +00:00
|
|
|
if node.tag=='para':
|
2008-07-22 14:24:36 +00:00
|
|
|
style = self.styles.para_style_get(node)
|
|
|
|
if extra_style:
|
|
|
|
style.__dict__.update(extra_style)
|
2009-04-28 10:24:21 +00:00
|
|
|
result = []
|
|
|
|
for i in self._textual(node).split('\n'):
|
|
|
|
result.append(platypus.Paragraph(i, style, **(utils.attr_get(node, [], {'bulletText':'str'}))))
|
|
|
|
return result
|
|
|
|
elif node.tag=='barCode':
|
2008-07-22 14:24:36 +00:00
|
|
|
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:
|
|
|
|
return None
|
2009-07-01 08:28:50 +00:00
|
|
|
args = utils.attr_get(node, [], {'ratio':'float','xdim':'unit','height':'unit','checksum':'int','quiet':'int','width':'unit','stop':'bool','bearers':'int','barWidth':'float','barHeight':'float'})
|
2008-07-22 14:24:36 +00:00
|
|
|
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'
|
2009-04-28 10:24:21 +00:00
|
|
|
if node.get('code'):
|
|
|
|
code = node.get('code').lower()
|
2008-07-22 14:24:36 +00:00
|
|
|
return codes[code](self._textual(node))
|
2009-04-28 10:24:21 +00:00
|
|
|
elif node.tag=='name':
|
|
|
|
self.styles.names[ node.get('id')] = node.get('value')
|
2008-07-22 14:24:36 +00:00
|
|
|
return None
|
2009-04-28 10:24:21 +00:00
|
|
|
elif node.tag=='xpre':
|
2008-07-22 14:24:36 +00:00
|
|
|
style = self.styles.para_style_get(node)
|
|
|
|
return platypus.XPreformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int','frags':'int'})))
|
2009-04-28 10:24:21 +00:00
|
|
|
elif node.tag=='pre':
|
2008-07-22 14:24:36 +00:00
|
|
|
style = self.styles.para_style_get(node)
|
|
|
|
return platypus.Preformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int'})))
|
2009-04-28 10:24:21 +00:00
|
|
|
elif node.tag=='illustration':
|
2008-07-22 14:24:36 +00:00
|
|
|
return self._illustration(node)
|
2009-04-28 10:24:21 +00:00
|
|
|
elif node.tag=='blockTable':
|
2008-07-22 14:24:36 +00:00
|
|
|
return self._table(node)
|
2009-04-28 10:24:21 +00:00
|
|
|
elif node.tag=='title':
|
2008-07-22 14:24:36 +00:00
|
|
|
styles = reportlab.lib.styles.getSampleStyleSheet()
|
|
|
|
style = styles['Title']
|
|
|
|
return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
|
2010-04-02 08:49:26 +00:00
|
|
|
elif re.match('^h([1-9]+[0-9]*)$', (node.tag or '')):
|
2008-07-22 14:24:36 +00:00
|
|
|
styles = reportlab.lib.styles.getSampleStyleSheet()
|
2010-04-02 08:49:26 +00:00
|
|
|
style = styles['Heading'+str(node.tag[1:])]
|
2008-07-22 14:24:36 +00:00
|
|
|
return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
|
2009-04-28 10:24:21 +00:00
|
|
|
elif node.tag=='image':
|
|
|
|
if not node.get('file'):
|
|
|
|
if node.get('name'):
|
|
|
|
image_data = self.doc.images[node.get('name')].read()
|
2008-07-22 14:24:36 +00:00
|
|
|
else:
|
|
|
|
import base64
|
2009-05-22 06:05:41 +00:00
|
|
|
if self.localcontext:
|
|
|
|
newtext = utils._process_text(self, node.text or '')
|
|
|
|
node.text = newtext
|
2009-04-28 10:24:21 +00:00
|
|
|
image_data = base64.decodestring(node.text)
|
2009-05-25 10:17:44 +00:00
|
|
|
if not image_data: return False
|
2009-04-28 10:24:21 +00:00
|
|
|
image = cStringIO.StringIO(image_data)
|
2008-07-22 14:24:36 +00:00
|
|
|
return platypus.Image(image, mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height'])))
|
|
|
|
else:
|
2009-04-28 10:24:21 +00:00
|
|
|
return platypus.Image(node.get('file'), mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height'])))
|
2008-07-22 14:24:36 +00:00
|
|
|
from reportlab.lib.utils import ImageReader
|
2009-04-28 10:24:21 +00:00
|
|
|
name = str(node.get('file'))
|
2008-07-22 14:24:36 +00:00
|
|
|
img = ImageReader(name)
|
|
|
|
(sx,sy) = img.getSize()
|
|
|
|
args = {}
|
|
|
|
for tag in ('width','height'):
|
2009-04-28 10:24:21 +00:00
|
|
|
if node.get(tag):
|
|
|
|
args[tag] = utils.unit_get(node.get(tag))
|
2008-07-22 14:24:36 +00:00
|
|
|
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)
|
2009-04-28 10:24:21 +00:00
|
|
|
elif node.tag=='spacer':
|
|
|
|
if node.get('width'):
|
|
|
|
width = utils.unit_get(node.get('width'))
|
2008-07-22 14:24:36 +00:00
|
|
|
else:
|
|
|
|
width = utils.unit_get('1cm')
|
2009-04-28 10:24:21 +00:00
|
|
|
length = utils.unit_get(node.get('length'))
|
2008-07-22 14:24:36 +00:00
|
|
|
return platypus.Spacer(width=width, height=length)
|
2009-04-28 10:24:21 +00:00
|
|
|
elif node.tag=='section':
|
2008-07-22 14:24:36 +00:00
|
|
|
return self.render(node)
|
2009-04-28 10:24:21 +00:00
|
|
|
elif node.tag == 'pageNumberReset':
|
2008-07-22 14:24:36 +00:00
|
|
|
return PageReset()
|
2009-04-28 10:24:21 +00:00
|
|
|
elif node.tag in ('pageBreak', 'nextPage'):
|
2008-07-22 14:24:36 +00:00
|
|
|
return platypus.PageBreak()
|
2009-04-28 10:24:21 +00:00
|
|
|
elif node.tag=='condPageBreak':
|
2008-07-22 14:24:36 +00:00
|
|
|
return platypus.CondPageBreak(**(utils.attr_get(node, ['height'])))
|
2009-04-28 10:24:21 +00:00
|
|
|
elif node.tag=='setNextTemplate':
|
|
|
|
return platypus.NextPageTemplate(str(node.get('name')))
|
|
|
|
elif node.tag=='nextFrame':
|
2008-07-22 14:24:36 +00:00
|
|
|
return platypus.CondPageBreak(1000) # TODO: change the 1000 !
|
2009-04-28 10:24:21 +00:00
|
|
|
elif node.tag == 'setNextFrame':
|
2008-07-22 14:24:36 +00:00
|
|
|
from reportlab.platypus.doctemplate import NextFrameFlowable
|
2009-04-28 10:24:21 +00:00
|
|
|
return NextFrameFlowable(str(node.get('name')))
|
|
|
|
elif node.tag == 'currentFrame':
|
2008-07-22 14:24:36 +00:00
|
|
|
from reportlab.platypus.doctemplate import CurrentFrameFlowable
|
2009-04-28 10:24:21 +00:00
|
|
|
return CurrentFrameFlowable(str(node.get('name')))
|
|
|
|
elif node.tag == 'frameEnd':
|
2008-07-22 14:24:36 +00:00
|
|
|
return EndFrameFlowable()
|
2009-04-28 10:24:21 +00:00
|
|
|
elif node.tag == 'hr':
|
|
|
|
width_hr=node.get('width') or '100%'
|
|
|
|
color_hr=node.get('color') or 'black'
|
|
|
|
thickness_hr=node.get('thickness') or 1
|
|
|
|
lineCap_hr=node.get('lineCap') or 'round'
|
2008-10-22 09:55:01 +00:00
|
|
|
return platypus.flowables.HRFlowable(width=width_hr,color=color.get(color_hr),thickness=float(thickness_hr),lineCap=str(lineCap_hr))
|
2008-07-22 14:24:36 +00:00
|
|
|
else:
|
2009-04-28 10:24:21 +00:00
|
|
|
sys.stderr.write('Warning: flowable not yet implemented: %s !\n' % (node.tag,))
|
2008-07-22 14:24:36 +00:00
|
|
|
return None
|
|
|
|
|
|
|
|
def render(self, node_story):
|
2009-04-28 10:24:21 +00:00
|
|
|
def process_story(node_story):
|
|
|
|
sub_story = []
|
|
|
|
for node in utils._child_get(node_story, self):
|
2009-05-25 06:42:45 +00:00
|
|
|
if node.tag == etree.Comment:
|
|
|
|
node.text = ''
|
|
|
|
continue
|
2008-08-11 09:27:55 +00:00
|
|
|
flow = self._flowable(node)
|
2008-07-22 14:24:36 +00:00
|
|
|
if flow:
|
2009-04-28 10:24:21 +00:00
|
|
|
if isinstance(flow,list):
|
|
|
|
sub_story = sub_story + flow
|
2008-07-22 14:24:36 +00:00
|
|
|
else:
|
2009-04-28 10:24:21 +00:00
|
|
|
sub_story.append(flow)
|
|
|
|
return sub_story
|
|
|
|
return process_story(node_story)
|
2006-12-07 13:41:40 +00:00
|
|
|
|
2006-12-08 09:07:43 +00:00
|
|
|
|
|
|
|
class EndFrameFlowable(ActionFlowable):
|
2008-07-22 14:24:36 +00:00
|
|
|
def __init__(self,resume=0):
|
|
|
|
ActionFlowable.__init__(self,('frameEnd',resume))
|
2006-12-08 09:07:43 +00:00
|
|
|
|
|
|
|
class TinyDocTemplate(platypus.BaseDocTemplate):
|
2008-07-22 14:24:36 +00:00
|
|
|
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()
|
|
|
|
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):
|
2010-10-01 14:07:24 +00:00
|
|
|
self.canv._pageCount=self.page
|
|
|
|
self.page=0
|
|
|
|
self.canv._flag=True
|
2008-07-22 14:24:36 +00:00
|
|
|
self.canv._pageNumber = 0
|
2006-12-08 09:07:43 +00:00
|
|
|
|
2006-12-07 13:41:40 +00:00
|
|
|
class _rml_template(object):
|
2009-04-28 10:24:21 +00:00
|
|
|
def __init__(self, localcontext, out, node, doc, images={}, path='.', title=None):
|
2010-10-06 13:25:34 +00:00
|
|
|
if not localcontext:
|
2010-10-07 09:51:27 +00:00
|
|
|
localcontext={'internal_header':True}
|
2009-04-28 10:24:21 +00:00
|
|
|
self.localcontext = localcontext
|
2008-07-22 14:24:36 +00:00
|
|
|
self.images= images
|
|
|
|
self.path = path
|
2008-09-24 15:34:10 +00:00
|
|
|
self.title = title
|
2010-10-05 07:27:09 +00:00
|
|
|
|
2009-04-28 10:24:21 +00:00
|
|
|
if not node.get('pageSize'):
|
2008-07-22 14:24:36 +00:00
|
|
|
pageSize = (utils.unit_get('21cm'), utils.unit_get('29.7cm'))
|
|
|
|
else:
|
2009-04-28 10:24:21 +00:00
|
|
|
ps = map(lambda x:x.strip(), node.get('pageSize').replace(')', '').replace('(', '').split(','))
|
2008-07-22 14:24:36 +00:00
|
|
|
pageSize = ( utils.unit_get(ps[0]),utils.unit_get(ps[1]) )
|
2009-04-28 10:24:21 +00:00
|
|
|
|
2008-07-22 14:24:36 +00:00
|
|
|
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
|
2010-10-04 13:35:06 +00:00
|
|
|
self.image=[]
|
2009-04-28 10:24:21 +00:00
|
|
|
pts = node.findall('pageTemplate')
|
2008-07-22 14:24:36 +00:00
|
|
|
for pt in pts:
|
|
|
|
frames = []
|
2009-04-28 10:24:21 +00:00
|
|
|
for frame_el in pt.findall('frame'):
|
2008-07-22 14:24:36 +00:00
|
|
|
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 )
|
2009-04-28 10:24:21 +00:00
|
|
|
try :
|
2009-11-28 11:58:09 +00:00
|
|
|
gr = pt.findall('pageGraphics')\
|
|
|
|
or pt[1].findall('pageGraphics')
|
2010-06-22 09:45:40 +00:00
|
|
|
except Exception: # FIXME: be even more specific, perhaps?
|
2009-04-28 10:24:21 +00:00
|
|
|
gr=''
|
2008-07-22 14:24:36 +00:00
|
|
|
if len(gr):
|
2010-10-05 07:27:09 +00:00
|
|
|
# self.image=[ n for n in utils._child_get(gr[0], self) if n.tag=='image' or not self.localcontext]
|
2009-04-28 10:24:21 +00:00
|
|
|
drw = _rml_draw(self.localcontext,gr[0], self.doc, images=images, path=self.path, title=self.title)
|
2008-07-22 14:24:36 +00:00
|
|
|
self.page_templates.append( platypus.PageTemplate(frames=frames, onPage=drw.render, **utils.attr_get(pt, [], {'id':'str'}) ))
|
|
|
|
else:
|
2009-04-28 10:24:21 +00:00
|
|
|
drw = _rml_draw(self.localcontext,node,self.doc,title=self.title)
|
2008-11-15 06:14:55 +00:00
|
|
|
self.page_templates.append( platypus.PageTemplate(frames=frames,onPage=drw.render, **utils.attr_get(pt, [], {'id':'str'}) ))
|
2008-07-22 14:24:36 +00:00
|
|
|
self.doc_tmpl.addPageTemplates(self.page_templates)
|
|
|
|
|
|
|
|
def render(self, node_stories):
|
|
|
|
fis = []
|
2009-04-28 10:24:21 +00:00
|
|
|
r = _rml_flowable(self.doc,self.localcontext, images=self.images, path=self.path, title=self.title)
|
2009-06-15 06:35:23 +00:00
|
|
|
story_cnt = 0
|
2008-07-22 14:24:36 +00:00
|
|
|
for node_story in node_stories:
|
2009-08-27 10:04:54 +00:00
|
|
|
if story_cnt > 0:
|
|
|
|
fis.append(platypus.PageBreak())
|
2008-07-22 14:24:36 +00:00
|
|
|
fis += r.render(node_story)
|
2010-09-08 05:56:20 +00:00
|
|
|
# Reset Page Number with new story tag
|
2010-09-27 12:52:23 +00:00
|
|
|
fis.append(PageReset())
|
2009-08-27 10:04:54 +00:00
|
|
|
story_cnt += 1
|
2010-10-07 09:51:27 +00:00
|
|
|
if self.localcontext and self.localcontext.get('internal_header',False):
|
|
|
|
self.doc_tmpl.afterFlowable(fis)
|
2010-10-04 13:35:06 +00:00
|
|
|
self.doc_tmpl.build(fis,canvasmaker=NumberedCanvas)
|
|
|
|
else:
|
2010-10-07 09:51:27 +00:00
|
|
|
fis.append(PageCount())
|
2010-10-04 13:35:06 +00:00
|
|
|
self.doc_tmpl.build(fis)
|
2006-12-07 13:41:40 +00:00
|
|
|
|
2009-04-28 10:24:21 +00:00
|
|
|
def parseNode(rml, localcontext = {},fout=None, images={}, path='.',title=None):
|
|
|
|
node = etree.XML(rml)
|
|
|
|
r = _rml_doc(node, localcontext, images, path, title=title)
|
2009-05-06 21:36:12 +00:00
|
|
|
#try to override some font mappings
|
|
|
|
try:
|
2009-11-24 14:44:05 +00:00
|
|
|
from customfonts import SetCustomFonts
|
|
|
|
SetCustomFonts(r)
|
2010-06-22 09:45:40 +00:00
|
|
|
except Exception:
|
2009-11-24 14:44:05 +00:00
|
|
|
pass
|
2009-04-28 10:24:21 +00:00
|
|
|
fp = cStringIO.StringIO()
|
|
|
|
r.render(fp)
|
|
|
|
return fp.getvalue()
|
|
|
|
|
|
|
|
def parseString(rml, localcontext = {},fout=None, images={}, path='.',title=None):
|
|
|
|
node = etree.XML(rml)
|
|
|
|
r = _rml_doc(node, localcontext, images, path, title=title)
|
2009-05-06 20:18:13 +00:00
|
|
|
|
2009-02-07 19:26:06 +00:00
|
|
|
#try to override some font mappings
|
|
|
|
try:
|
2009-11-24 14:44:05 +00:00
|
|
|
from customfonts import SetCustomFonts
|
|
|
|
SetCustomFonts(r)
|
2010-06-22 09:45:40 +00:00
|
|
|
except Exception:
|
2009-11-24 14:44:05 +00:00
|
|
|
pass
|
2009-05-06 20:18:13 +00:00
|
|
|
|
2008-07-22 14:24:36 +00:00
|
|
|
if fout:
|
|
|
|
fp = file(fout,'wb')
|
|
|
|
r.render(fp)
|
|
|
|
fp.close()
|
|
|
|
return fout
|
|
|
|
else:
|
2009-04-28 10:24:21 +00:00
|
|
|
fp = cStringIO.StringIO()
|
2008-07-22 14:24:36 +00:00
|
|
|
r.render(fp)
|
|
|
|
return fp.getvalue()
|
2006-12-07 13:41:40 +00:00
|
|
|
|
|
|
|
def trml2pdf_help():
|
2008-07-22 14:24:36 +00:00
|
|
|
print 'Usage: trml2pdf input.rml >output.pdf'
|
|
|
|
print 'Render the standard input (RML) and output a PDF file'
|
|
|
|
sys.exit(0)
|
2006-12-07 13:41:40 +00:00
|
|
|
|
|
|
|
if __name__=="__main__":
|
2008-07-22 14:24:36 +00:00
|
|
|
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.'
|
2008-07-23 15:01:27 +00:00
|
|
|
|