1 # -*- encoding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/lic enses/>.
21 ##############################################################################
25 from StringIO import StringIO
29 from reportlab.pdfgen import canvas
30 from reportlab import platypus
35 from lxml import etree
37 from reportlab.platypus.doctemplate import ActionFlowable
38 from tools.safe_eval import safe_eval as eval
42 class PageCount(platypus.Flowable):
44 self.canv.beginForm("pageCount")
45 self.canv.setFont("Helvetica", utils.unit_get(str(8)))
46 self.canv.drawString(0, 0, str(self.canv.getPageNumber()))
49 class PageReset(platypus.Flowable):
51 self.canv._pageNumber = 0
53 class _rml_styles(object,):
54 def __init__(self, nodes, localcontext):
55 self.localcontext = localcontext
59 self.table_styles = {}
60 self.default_style = reportlab.lib.styles.getSampleStyleSheet()
63 for style in node.findall('blockTableStyle'):
64 self.table_styles[style.get('id')] = self._table_style_get(style)
65 for style in node.findall('paraStyle'):
66 sname = style.get('name')
67 self.styles[sname] = self._para_style_update(style)
69 self.styles_obj[sname] = reportlab.lib.styles.ParagraphStyle(sname, self.default_style["Normal"], **self.styles[sname])
71 for variable in node.findall('initialize'):
72 for name in variable.findall('name'):
73 self.names[ name.get('id')] = name.get('value')
75 def _para_style_update(self, node):
77 for attr in ['textColor', 'backColor', 'bulletColor', 'borderColor']:
79 data[attr] = color.get(node.get(attr))
80 for attr in ['fontName', 'bulletFontName', 'bulletText']:
82 data[attr] = node.get(attr)
83 for attr in ['fontSize', 'leftIndent', 'rightIndent', 'spaceBefore', 'spaceAfter',
84 'firstLineIndent', 'bulletIndent', 'bulletFontSize', 'leading',
85 'borderWidth','borderPadding','borderRadius']:
87 data[attr] = utils.unit_get(node.get(attr))
88 if node.get('alignment'):
90 'right':reportlab.lib.enums.TA_RIGHT,
91 'center':reportlab.lib.enums.TA_CENTER,
92 'justify':reportlab.lib.enums.TA_JUSTIFY
94 data['alignment'] = align.get(node.get('alignment').lower(), reportlab.lib.enums.TA_LEFT)
97 def _table_style_get(self, style_node):
99 for node in style_node:
100 start = utils.tuple_int_get(node, 'start', (0,0) )
101 stop = utils.tuple_int_get(node, 'stop', (-1,-1) )
102 if node.tag=='blockValign':
103 styles.append(('VALIGN', start, stop, str(node.get('value'))))
104 elif node.tag=='blockFont':
105 styles.append(('FONT', start, stop, str(node.get('name'))))
106 elif node.tag=='blockTextColor':
107 styles.append(('TEXTCOLOR', start, stop, color.get(str(node.get('colorName')))))
108 elif node.tag=='blockLeading':
109 styles.append(('LEADING', start, stop, utils.unit_get(node.get('length'))))
110 elif node.tag=='blockAlignment':
111 styles.append(('ALIGNMENT', start, stop, str(node.get('value'))))
112 elif node.tag=='blockSpan':
113 styles.append(('SPAN', start, stop))
114 elif node.tag=='blockLeftPadding':
115 styles.append(('LEFTPADDING', start, stop, utils.unit_get(node.get('length'))))
116 elif node.tag=='blockRightPadding':
117 styles.append(('RIGHTPADDING', start, stop, utils.unit_get(node.get('length'))))
118 elif node.tag=='blockTopPadding':
119 styles.append(('TOPPADDING', start, stop, utils.unit_get(node.get('length'))))
120 elif node.tag=='blockBottomPadding':
121 styles.append(('BOTTOMPADDING', start, stop, utils.unit_get(node.get('length'))))
122 elif node.tag=='blockBackground':
123 styles.append(('BACKGROUND', start, stop, color.get(node.get('colorName'))))
125 styles.append(('FONTSIZE', start, stop, utils.unit_get(node.get('size'))))
126 elif node.tag=='lineStyle':
127 kind = node.get('kind')
128 kind_list = [ 'GRID', 'BOX', 'OUTLINE', 'INNERGRID', 'LINEBELOW', 'LINEABOVE','LINEBEFORE', 'LINEAFTER' ]
129 assert kind in kind_list
131 if node.get('thickness'):
132 thick = float(node.get('thickness'))
133 styles.append((kind, start, stop, thick, color.get(node.get('colorName'))))
134 return platypus.tables.TableStyle(styles)
136 def para_style_get(self, node):
138 sname = node.get('style')
140 if sname in self.styles_obj:
141 style = self.styles_obj[sname]
143 sys.stderr.write('Warning: style not found, %s - setting default!\n' % (node.get('style'),) )
145 style = self.default_style['Normal']
146 para_update = self._para_style_update(node)
148 # update style only is necessary
149 style = copy.deepcopy(style)
150 style.__dict__.update(para_update)
153 class _rml_doc(object):
154 def __init__(self, node, localcontext, images={}, path='.', title=None):
155 self.localcontext = localcontext
157 self.filename = self.etree.get('filename')
162 def docinit(self, els):
163 from reportlab.lib.fonts import addMapping
164 from reportlab.pdfbase import pdfmetrics
165 from reportlab.pdfbase.ttfonts import TTFont
168 for font in node.findall('registerFont'):
169 name = font.get('fontName').encode('ascii')
170 fname = font.get('fontFile').encode('ascii')
171 pdfmetrics.registerFont(TTFont(name, fname ))
172 addMapping(name, 0, 0, name) #normal
173 addMapping(name, 0, 1, name) #italic
174 addMapping(name, 1, 0, name) #bold
175 addMapping(name, 1, 1, name) #italic and bold
177 def _textual_image(self, node):
180 rc +=( etree.tostring(n) or '') + n.tail
181 return base64.decodestring(node.tostring())
183 def _images(self, el):
185 for node in el.findall('image'):
186 rc =( node.text or '')
187 result[node.get('name')] = base64.decodestring(rc)
190 def render(self, out):
191 el = self.etree.findall('docinit')
195 el = self.etree.findall('stylesheet')
196 self.styles = _rml_styles(el,self.localcontext)
198 el = self.etree.findall('images')
200 self.images.update( self._images(el[0]) )
201 el = self.etree.findall('template')
203 pt_obj = _rml_template(self.localcontext, out, el[0], self, images=self.images, path=self.path, title=self.title)
204 el = utils._child_get(self.etree, self, 'story')
207 self.canvas = canvas.Canvas(out)
208 pd = self.etree.find('pageDrawing')[0]
209 pd_obj = _rml_canvas(self.canvas, self.localcontext, None, self, self.images, path=self.path, title=self.title)
212 self.canvas.showPage()
215 class _rml_canvas(object):
216 def __init__(self, canvas, localcontext, doc_tmpl=None, doc=None, images={}, path='.', title=None):
217 self.localcontext = localcontext
219 self.styles = doc.styles
220 self.doc_tmpl = doc_tmpl
226 self.canvas.setTitle(self.title)
228 def _textual(self, node, x=0, y=0):
229 text = node.text and node.text.encode('utf-8') or ''
230 rc = utils._process_text(self, text)
233 from reportlab.lib.sequencer import getSequencer
235 rc += str(seq.next(n.get('id')))
236 if n.tag == 'pageCount':
238 self.canvas.translate(x,y)
239 self.canvas.doForm('pageCount')
241 self.canvas.translate(-x,-y)
242 if n.tag == 'pageNumber':
243 rc += str(self.canvas.getPageNumber())
244 rc += utils._process_text(self, n.tail)
247 def _drawString(self, node):
248 v = utils.attr_get(node, ['x','y'])
249 text=self._textual(node, **v)
250 text = utils.xml2str(text)
251 self.canvas.drawString(text=text, **v)
253 def _drawCenteredString(self, node):
254 v = utils.attr_get(node, ['x','y'])
255 text=self._textual(node, **v)
256 text = utils.xml2str(text)
257 self.canvas.drawCentredString(text=text, **v)
259 def _drawRightString(self, node):
260 v = utils.attr_get(node, ['x','y'])
261 text=self._textual(node, **v)
262 text = utils.xml2str(text)
263 self.canvas.drawRightString(text=text, **v)
265 def _rect(self, node):
266 if node.get('round'):
267 self.canvas.roundRect(radius=utils.unit_get(node.get('round')), **utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'}))
269 self.canvas.rect(**utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'}))
271 def _ellipse(self, node):
272 x1 = utils.unit_get(node.get('x'))
273 x2 = utils.unit_get(node.get('width'))
274 y1 = utils.unit_get(node.get('y'))
275 y2 = utils.unit_get(node.get('height'))
277 self.canvas.ellipse(x1,y1,x2,y2, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'}))
279 def _curves(self, node):
280 line_str = node.text.split()
282 while len(line_str)>7:
283 self.canvas.bezier(*[utils.unit_get(l) for l in line_str[0:8]])
284 line_str = line_str[8:]
286 def _lines(self, node):
287 line_str = node.text.split()
289 while len(line_str)>3:
290 lines.append([utils.unit_get(l) for l in line_str[0:4]])
291 line_str = line_str[4:]
292 self.canvas.lines(lines)
294 def _grid(self, node):
295 xlist = [utils.unit_get(s) for s in node.get('xs').split(',')]
296 ylist = [utils.unit_get(s) for s in node.get('ys').split(',')]
298 self.canvas.grid(xlist, ylist)
300 def _translate(self, node):
301 dx = utils.unit_get(node.get('dx')) or 0
302 dy = utils.unit_get(node.get('dy')) or 0
303 self.canvas.translate(dx,dy)
305 def _circle(self, node):
306 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'}))
308 def _place(self, node):
309 flows = _rml_flowable(self.doc, self.localcontext, images=self.images, path=self.path, title=self.title).render(node)
310 infos = utils.attr_get(node, ['x','y','width','height'])
312 infos['y']+=infos['height']
314 w,h = flow.wrap(infos['width'], infos['height'])
315 if w<=infos['width'] and h<=infos['height']:
317 flow.drawOn(self.canvas,infos['x'],infos['y'])
320 raise ValueError, "Not enough space"
322 def _line_mode(self, node):
323 ljoin = {'round':1, 'mitered':0, 'bevelled':2}
324 lcap = {'default':0, 'round':1, 'square':2}
326 if node.get('width'):
327 self.canvas.setLineWidth(utils.unit_get(node.get('width')))
329 self.canvas.setLineJoin(ljoin[node.get('join')])
331 self.canvas.setLineCap(lcap[node.get('cap')])
332 if node.get('miterLimit'):
333 self.canvas.setDash(utils.unit_get(node.get('miterLimit')))
335 dashes = node.get('dash').split(',')
336 for x in range(len(dashes)):
337 dashes[x]=utils.unit_get(dashes[x])
338 self.canvas.setDash(node.get('dash').split(','))
340 def _image(self, node):
342 from reportlab.lib.utils import ImageReader
343 if not node.get('file') :
345 image_data = self.images[node.get('name')]
346 s = cStringIO.StringIO(image_data)
349 if self.localcontext:
350 res = utils._regex.findall(node.text)
352 newtext = eval(key, {}, self.localcontext)
354 image_data = base64.decodestring(node.text)
355 if not image_data: return False
356 s = cStringIO.StringIO(image_data)
358 if node.get('file') in self.images:
359 s = cStringIO.StringIO(self.images[node.get('file')])
362 u = urllib.urlopen(str(node.get('file')))
363 s = cStringIO.StringIO(u.read())
365 u = file(os.path.join(self.path,str(node.get('file'))), 'rb')
366 s = cStringIO.StringIO(u.read())
368 (sx,sy) = img.getSize()
371 for tag in ('width','height','x','y'):
373 args[tag] = utils.unit_get(node.get(tag))
374 if ('width' in args) and (not 'height' in args):
375 args['height'] = sy * args['width'] / sx
376 elif ('height' in args) and (not 'width' in args):
377 args['width'] = sx * args['height'] / sy
378 elif ('width' in args) and ('height' in args):
379 if (float(args['width'])/args['height'])>(float(sx)>sy):
380 args['width'] = sx * args['height'] / sy
382 args['height'] = sy * args['width'] / sx
383 self.canvas.drawImage(img, **args)
385 def _path(self, node):
386 self.path = self.canvas.beginPath()
387 self.path.moveTo(**utils.attr_get(node, ['x','y']))
388 for n in utils._child_get(node, self):
391 vals = utils.text_get(n).split()
392 self.path.moveTo(utils.unit_get(vals[0]), utils.unit_get(vals[1]))
393 elif n.tag=='curvesto':
394 vals = utils.text_get(n).split()
398 pos.append(utils.unit_get(vals.pop(0)))
399 self.path.curveTo(*pos)
401 data = n.text.split() # Not sure if I must merge all TEXT_NODE ?
403 x = utils.unit_get(data.pop(0))
404 y = utils.unit_get(data.pop(0))
405 self.path.lineTo(x,y)
406 if (not node.get('close')) or utils.bool_get(node.get('close')):
408 self.canvas.drawPath(self.path, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'}))
410 def render(self, node):
412 'drawCentredString': self._drawCenteredString,
413 'drawRightString': self._drawRightString,
414 'drawString': self._drawString,
416 'ellipse': self._ellipse,
417 'lines': self._lines,
419 'curves': self._curves,
420 'fill': lambda node: self.canvas.setFillColor(color.get(node.get('color'))),
421 'stroke': lambda node: self.canvas.setStrokeColor(color.get(node.get('color'))),
422 'setFont': lambda node: self.canvas.setFont(node.get('name'), utils.unit_get(node.get('size'))),
423 'place': self._place,
424 'circle': self._circle,
425 'lineMode': self._line_mode,
427 'rotate': lambda node: self.canvas.rotate(float(node.get('degrees'))),
428 'translate': self._translate,
431 for n in utils._child_get(node, self):
435 class _rml_draw(object):
436 def __init__(self, localcontext ,node, styles, images={}, path='.', title=None):
437 self.localcontext = localcontext
443 self.canvas_title = title
445 def render(self, canvas, doc):
447 cnv = _rml_canvas(canvas, self.localcontext, doc, self.styles, images=self.images, path=self.path, title=self.canvas_title)
448 cnv.render(self.node)
449 canvas.restoreState()
451 class _rml_flowable(object):
452 def __init__(self, doc, localcontext, images={}, path='.', title=None):
453 self.localcontext = localcontext
455 self.styles = doc.styles
460 def _textual(self, node):
461 rc1 = utils._process_text(self, node.text or '')
462 for n in utils._child_get(node,self):
463 txt_n = copy.deepcopy(n)
464 for key in txt_n.attrib.keys():
465 if key in ('rml_except', 'rml_loop', 'rml_tag'):
466 del txt_n.attrib[key]
467 if True or not self._textual(n).isspace():
468 if not n.tag == 'bullet':
469 txt_n.text = utils.xml2str(self._textual(n))
470 txt_n.tail = n.tail and utils._process_text(self, n.tail.replace('\n','')) or ''
471 rc1 += etree.tostring(txt_n)
474 def _table(self, node):
475 childs = utils._child_get(node,self,'tr')
487 st = copy.deepcopy(self.styles.table_styles[tr.get('style')])
492 if tr.get('paraStyle'):
493 paraStyle = self.styles.styles[tr.get('paraStyle')]
496 for td in utils._child_get(tr, self,'td'):
498 st = copy.deepcopy(self.styles.table_styles[td.get('style')])
505 if td.get('paraStyle'):
507 paraStyle = self.styles.styles[td.get('paraStyle')]
511 for n in utils._child_get(td, self):
512 if n.tag == etree.Comment:
515 fl = self._flowable(n, extra_style=paraStyle)
516 if isinstance(fl,list):
522 flow = self._textual(td)
524 if len(data2)>length:
527 while len(ab)<length:
529 while len(data2)<length:
534 if node.get('colWidths'):
535 assert length == len(node.get('colWidths').split(','))
536 colwidths = [utils.unit_get(f.strip()) for f in node.get('colWidths').split(',')]
537 if node.get('rowHeights'):
538 rowheights = [utils.unit_get(f.strip()) for f in node.get('rowHeights').split(',')]
539 if len(rowheights) == 1:
540 rowheights = rowheights[0]
541 table = platypus.LongTable(data = data, colWidths=colwidths, rowHeights=rowheights, **(utils.attr_get(node, ['splitByRow'] ,{'repeatRows':'int','repeatCols':'int'})))
542 if node.get('style'):
543 table.setStyle(self.styles.table_styles[node.get('style')])
548 def _illustration(self, node):
549 class Illustration(platypus.flowables.Flowable):
550 def __init__(self, node, localcontext, styles, self2):
551 self.localcontext = localcontext.copy()
554 self.width = utils.unit_get(node.get('width'))
555 self.height = utils.unit_get(node.get('height'))
557 def wrap(self, *args):
558 return (self.width, self.height)
561 drw = _rml_draw(self.localcontext ,self.node,self.styles, images=self.self2.images, path=self.self2.path, title=self.self2.title)
562 drw.render(self.canv, None)
563 return Illustration(node, self.localcontext, self.styles, self)
565 def _textual_image(self, node):
566 return base64.decodestring(node.text)
568 def _flowable(self, node, extra_style=None):
570 style = self.styles.para_style_get(node)
572 style.__dict__.update(extra_style)
574 for i in self._textual(node).split('\n'):
575 result.append(platypus.Paragraph(i, style, **(utils.attr_get(node, [], {'bulletText':'str'}))))
577 elif node.tag=='barCode':
579 from reportlab.graphics.barcode import code128
580 from reportlab.graphics.barcode import code39
581 from reportlab.graphics.barcode import code93
582 from reportlab.graphics.barcode import common
583 from reportlab.graphics.barcode import fourstate
584 from reportlab.graphics.barcode import usps
587 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'})
589 'codabar': lambda x: common.Codabar(x, **args),
590 'code11': lambda x: common.Code11(x, **args),
591 'code128': lambda x: code128.Code128(x, **args),
592 'standard39': lambda x: code39.Standard39(x, **args),
593 'standard93': lambda x: code93.Standard93(x, **args),
594 'i2of5': lambda x: common.I2of5(x, **args),
595 'extended39': lambda x: code39.Extended39(x, **args),
596 'extended93': lambda x: code93.Extended93(x, **args),
597 'msi': lambda x: common.MSI(x, **args),
598 'fim': lambda x: usps.FIM(x, **args),
599 'postnet': lambda x: usps.POSTNET(x, **args),
603 code = node.get('code').lower()
604 return codes[code](self._textual(node))
605 elif node.tag=='name':
606 self.styles.names[ node.get('id')] = node.get('value')
608 elif node.tag=='xpre':
609 style = self.styles.para_style_get(node)
610 return platypus.XPreformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int','frags':'int'})))
611 elif node.tag=='pre':
612 style = self.styles.para_style_get(node)
613 return platypus.Preformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int'})))
614 elif node.tag=='illustration':
615 return self._illustration(node)
616 elif node.tag=='blockTable':
617 return self._table(node)
618 elif node.tag=='title':
619 styles = reportlab.lib.styles.getSampleStyleSheet()
620 style = styles['Title']
621 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
622 elif re.match('^h([1-9]+[0-9]*)$', (node.text or '')):
623 styles = reportlab.lib.styles.getSampleStyleSheet()
624 style = styles['Heading'+str(node.get[1:])]
625 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
626 elif node.tag=='image':
627 if not node.get('file'):
629 image_data = self.doc.images[node.get('name')].read()
632 if self.localcontext:
633 newtext = utils._process_text(self, node.text or '')
635 image_data = base64.decodestring(node.text)
636 if not image_data: return False
637 image = cStringIO.StringIO(image_data)
638 return platypus.Image(image, mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height'])))
640 return platypus.Image(node.get('file'), mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height'])))
641 from reportlab.lib.utils import ImageReader
642 name = str(node.get('file'))
643 img = ImageReader(name)
644 (sx,sy) = img.getSize()
646 for tag in ('width','height'):
648 args[tag] = utils.unit_get(node.get(tag))
649 if ('width' in args) and (not 'height' in args):
650 args['height'] = sy * args['width'] / sx
651 elif ('height' in args) and (not 'width' in args):
652 args['width'] = sx * args['height'] / sy
653 elif ('width' in args) and ('height' in args):
654 if (float(args['width'])/args['height'])>(float(sx)>sy):
655 args['width'] = sx * args['height'] / sy
657 args['height'] = sy * args['width'] / sx
658 return platypus.Image(name, mask=(250,255,250,255,250,255), **args)
659 elif node.tag=='spacer':
660 if node.get('width'):
661 width = utils.unit_get(node.get('width'))
663 width = utils.unit_get('1cm')
664 length = utils.unit_get(node.get('length'))
665 return platypus.Spacer(width=width, height=length)
666 elif node.tag=='section':
667 return self.render(node)
668 elif node.tag == 'pageNumberReset':
670 elif node.tag in ('pageBreak', 'nextPage'):
671 return platypus.PageBreak()
672 elif node.tag=='condPageBreak':
673 return platypus.CondPageBreak(**(utils.attr_get(node, ['height'])))
674 elif node.tag=='setNextTemplate':
675 return platypus.NextPageTemplate(str(node.get('name')))
676 elif node.tag=='nextFrame':
677 return platypus.CondPageBreak(1000) # TODO: change the 1000 !
678 elif node.tag == 'setNextFrame':
679 from reportlab.platypus.doctemplate import NextFrameFlowable
680 return NextFrameFlowable(str(node.get('name')))
681 elif node.tag == 'currentFrame':
682 from reportlab.platypus.doctemplate import CurrentFrameFlowable
683 return CurrentFrameFlowable(str(node.get('name')))
684 elif node.tag == 'frameEnd':
685 return EndFrameFlowable()
686 elif node.tag == 'hr':
687 width_hr=node.get('width') or '100%'
688 color_hr=node.get('color') or 'black'
689 thickness_hr=node.get('thickness') or 1
690 lineCap_hr=node.get('lineCap') or 'round'
691 return platypus.flowables.HRFlowable(width=width_hr,color=color.get(color_hr),thickness=float(thickness_hr),lineCap=str(lineCap_hr))
693 sys.stderr.write('Warning: flowable not yet implemented: %s !\n' % (node.tag,))
696 def render(self, node_story):
697 def process_story(node_story):
699 for node in utils._child_get(node_story, self):
700 if node.tag == etree.Comment:
703 flow = self._flowable(node)
705 if isinstance(flow,list):
706 sub_story = sub_story + flow
708 sub_story.append(flow)
710 return process_story(node_story)
713 class EndFrameFlowable(ActionFlowable):
714 def __init__(self,resume=0):
715 ActionFlowable.__init__(self,('frameEnd',resume))
717 class TinyDocTemplate(platypus.BaseDocTemplate):
718 def ___handle_pageBegin(self):
719 self.page = self.page + 1
720 self.pageTemplate.beforeDrawPage(self.canv,self)
721 self.pageTemplate.checkPageSize(self.canv,self)
722 self.pageTemplate.onPage(self.canv,self)
723 for f in self.pageTemplate.frames: f._reset()
725 self._curPageFlowableCount = 0
726 if hasattr(self,'_nextFrameIndex'):
727 del self._nextFrameIndex
728 for f in self.pageTemplate.frames:
732 self.handle_frameBegin()
733 def afterFlowable(self, flowable):
734 if isinstance(flowable, PageReset):
735 self.canv._pageNumber = 0
737 class _rml_template(object):
738 def __init__(self, localcontext, out, node, doc, images={}, path='.', title=None):
739 self.localcontext = localcontext
743 if not node.get('pageSize'):
744 pageSize = (utils.unit_get('21cm'), utils.unit_get('29.7cm'))
746 ps = map(lambda x:x.strip(), node.get('pageSize').replace(')', '').replace('(', '').split(','))
747 pageSize = ( utils.unit_get(ps[0]),utils.unit_get(ps[1]) )
749 self.doc_tmpl = TinyDocTemplate(out, pagesize=pageSize, **utils.attr_get(node, ['leftMargin','rightMargin','topMargin','bottomMargin'], {'allowSplitting':'int','showBoundary':'bool','title':'str','author':'str'}))
750 self.page_templates = []
751 self.styles = doc.styles
753 pts = node.findall('pageTemplate')
756 for frame_el in pt.findall('frame'):
757 frame = platypus.Frame( **(utils.attr_get(frame_el, ['x1','y1', 'width','height', 'leftPadding', 'rightPadding', 'bottomPadding', 'topPadding'], {'id':'str', 'showBoundary':'bool'})) )
758 if utils.attr_get(frame_el, ['last']):
759 frame.lastFrame = True
760 frames.append( frame )
762 gr = pt.findall('pageGraphics')\
763 or pt[1].findall('pageGraphics')
767 drw = _rml_draw(self.localcontext,gr[0], self.doc, images=images, path=self.path, title=self.title)
768 self.page_templates.append( platypus.PageTemplate(frames=frames, onPage=drw.render, **utils.attr_get(pt, [], {'id':'str'}) ))
770 drw = _rml_draw(self.localcontext,node,self.doc,title=self.title)
771 self.page_templates.append( platypus.PageTemplate(frames=frames,onPage=drw.render, **utils.attr_get(pt, [], {'id':'str'}) ))
772 self.doc_tmpl.addPageTemplates(self.page_templates)
774 def render(self, node_stories):
776 r = _rml_flowable(self.doc,self.localcontext, images=self.images, path=self.path, title=self.title)
778 for node_story in node_stories:
780 fis.append(platypus.PageBreak())
781 fis += r.render(node_story)
783 if self.localcontext:
784 fis.append(PageCount())
785 self.doc_tmpl.build(fis)
787 def parseNode(rml, localcontext = {},fout=None, images={}, path='.',title=None):
788 node = etree.XML(rml)
789 r = _rml_doc(node, localcontext, images, path, title=title)
790 fp = cStringIO.StringIO()
794 def parseString(rml, localcontext = {},fout=None, images={}, path='.',title=None):
795 node = etree.XML(rml)
796 r = _rml_doc(node, localcontext, images, path, title=title)
803 fp = cStringIO.StringIO()
808 print 'Usage: trml2pdf input.rml >output.pdf'
809 print 'Render the standard input (RML) and output a PDF file'
812 if __name__=="__main__":
814 if sys.argv[1]=='--help':
816 print parseString(file(sys.argv[1], 'r').read()),
818 print 'Usage: trml2pdf input.rml >output.pdf'
819 print 'Try \'trml2pdf --help\' for more information.'