2 # -*- coding: utf-8 -*-
4 # trml2pdf - An RML to PDF converter
5 # Copyright (C) 2003, Fabien Pinckaers, UCL, FSA
7 # Richard Waid <richard@iopen.net>
9 # This library is free software; you can redistribute it and/or
10 # modify it under the terms of the GNU Lesser General Public
11 # License as published by the Free Software Foundation; either
12 # version 2.1 of the License, or (at your option) any later version.
14 # This library is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 # Lesser General Public License for more details.
19 # You should have received a copy of the GNU Lesser General Public
20 # License along with this library; if not, write to the Free Software
21 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 import xml.dom.minidom
29 from reportlab.pdfgen import canvas
30 from reportlab import platypus
37 # Change this to UTF-8 if you plan tu use Reportlab's UTF-8 support
39 # reportlab use "code page 1252" encoding by default. cfr reportlab user guide p.46
43 return s.replace('&', '&').replace('<', '<').replace('>', '>')
45 def _child_get(node, childs):
47 for n in node.childNodes:
48 if (n.nodeType==n.ELEMENT_NODE) and (n.localName==childs):
52 class _rml_styles(object):
53 def __init__(self, nodes):
56 self.table_styles = {}
58 for style in node.getElementsByTagName('blockTableStyle'):
59 self.table_styles[style.getAttribute('id')] = self._table_style_get(style)
60 for style in node.getElementsByTagName('paraStyle'):
61 self.styles[style.getAttribute('name')] = self._para_style_get(style)
62 for variable in node.getElementsByTagName('initialize'):
63 for name in variable.getElementsByTagName('name'):
64 self.names[ name.getAttribute('id')] = name.getAttribute('value')
66 def _para_style_update(self, style, node):
67 for attr in ['textColor', 'backColor', 'bulletColor']:
68 if node.hasAttribute(attr):
69 style.__dict__[attr] = color.get(node.getAttribute(attr))
70 for attr in ['fontName', 'bulletFontName', 'bulletText']:
71 if node.hasAttribute(attr):
72 style.__dict__[attr] = node.getAttribute(attr)
73 for attr in ['fontSize', 'leftIndent', 'rightIndent', 'spaceBefore', 'spaceAfter', 'firstLineIndent', 'bulletIndent', 'bulletFontSize', 'leading']:
74 if node.hasAttribute(attr):
75 style.__dict__[attr] = utils.unit_get(node.getAttribute(attr))
76 if node.hasAttribute('alignment'):
78 'right':reportlab.lib.enums.TA_RIGHT,
79 'center':reportlab.lib.enums.TA_CENTER,
80 'justify':reportlab.lib.enums.TA_JUSTIFY
82 style.alignment = align.get(node.getAttribute('alignment').lower(), reportlab.lib.enums.TA_LEFT)
85 def _table_style_get(self, style_node):
87 for node in style_node.childNodes:
88 if node.nodeType==node.ELEMENT_NODE:
89 start = utils.tuple_int_get(node, 'start', (0,0) )
90 stop = utils.tuple_int_get(node, 'stop', (-1,-1) )
91 if node.localName=='blockValign':
92 styles.append(('VALIGN', start, stop, str(node.getAttribute('value'))))
93 elif node.localName=='blockFont':
94 styles.append(('FONT', start, stop, str(node.getAttribute('name'))))
95 elif node.localName=='blockTextColor':
96 styles.append(('TEXTCOLOR', start, stop, color.get(str(node.getAttribute('colorName')))))
97 elif node.localName=='blockLeading':
98 styles.append(('LEADING', start, stop, utils.unit_get(node.getAttribute('length'))))
99 elif node.localName=='blockAlignment':
100 styles.append(('ALIGNMENT', start, stop, str(node.getAttribute('value'))))
101 elif node.localName=='blockLeftPadding':
102 styles.append(('LEFTPADDING', start, stop, utils.unit_get(node.getAttribute('length'))))
103 elif node.localName=='blockRightPadding':
104 styles.append(('RIGHTPADDING', start, stop, utils.unit_get(node.getAttribute('length'))))
105 elif node.localName=='blockTopPadding':
106 styles.append(('TOPPADDING', start, stop, utils.unit_get(node.getAttribute('length'))))
107 elif node.localName=='blockBottomPadding':
108 styles.append(('BOTTOMPADDING', start, stop, utils.unit_get(node.getAttribute('length'))))
109 elif node.localName=='blockBackground':
110 styles.append(('BACKGROUND', start, stop, color.get(node.getAttribute('colorName'))))
111 if node.hasAttribute('size'):
112 styles.append(('FONTSIZE', start, stop, utils.unit_get(node.getAttribute('size'))))
113 elif node.localName=='lineStyle':
114 kind = node.getAttribute('kind')
115 kind_list = [ 'GRID', 'BOX', 'OUTLINE', 'INNERGRID', 'LINEBELOW', 'LINEABOVE','LINEBEFORE', 'LINEAFTER' ]
116 assert kind in kind_list
118 if node.hasAttribute('thickness'):
119 thick = float(node.getAttribute('thickness'))
120 styles.append((kind, start, stop, thick, color.get(node.getAttribute('colorName'))))
121 return platypus.tables.TableStyle(styles)
123 def _para_style_get(self, node):
124 styles = reportlab.lib.styles.getSampleStyleSheet()
125 style = copy.deepcopy(styles["Normal"])
126 self._para_style_update(style, node)
129 def para_style_get(self, node):
131 if node.hasAttribute('style'):
132 if node.getAttribute('style') in self.styles:
133 style = copy.deepcopy(self.styles[node.getAttribute('style')])
135 sys.stderr.write('Warning: style not found, %s - setting default!\n' % (node.getAttribute('style'),) )
137 styles = reportlab.lib.styles.getSampleStyleSheet()
138 style = copy.deepcopy(styles['Normal'])
139 return self._para_style_update(style, node)
141 class _rml_doc(object):
142 def __init__(self, data, images={}, path='.'):
143 self.dom = xml.dom.minidom.parseString(data)
144 self.filename = self.dom.documentElement.getAttribute('filename')
148 def docinit(self, els):
149 from reportlab.lib.fonts import addMapping
150 from reportlab.pdfbase import pdfmetrics
151 from reportlab.pdfbase.ttfonts import TTFont
154 for font in node.getElementsByTagName('registerFont'):
155 name = font.getAttribute('fontName').encode('ascii')
156 fname = font.getAttribute('fontFile').encode('ascii')
157 pdfmetrics.registerFont(TTFont(name, fname ))
158 addMapping(name, 0, 0, name) #normal
159 addMapping(name, 0, 1, name) #italic
160 addMapping(name, 1, 0, name) #bold
161 addMapping(name, 1, 1, name) #italic and bold
163 def _textual_image(self, node):
166 for n in node.childNodes:
167 if n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE):
169 return base64.decodestring(rc)
171 def _images(self, el):
173 for node in el.getElementsByTagName('image'):
174 result[node.getAttribute('name')] = self._textual_image(node)
177 def render(self, out):
178 el = self.dom.documentElement.getElementsByTagName('docinit')
182 el = self.dom.documentElement.getElementsByTagName('stylesheet')
183 self.styles = _rml_styles(el)
185 el = self.dom.documentElement.getElementsByTagName('images')
187 self.images.update( self._images(el[0]) )
189 el = self.dom.documentElement.getElementsByTagName('template')
191 pt_obj = _rml_template(out, el[0], self, images=self.images, path=self.path)
192 pt_obj.render(self.dom.documentElement.getElementsByTagName('story'))
194 self.canvas = canvas.Canvas(out)
195 pd = self.dom.documentElement.getElementsByTagName('pageDrawing')[0]
196 pd_obj = _rml_canvas(self.canvas, None, self, self.images, path=self.path)
198 self.canvas.showPage()
201 class _rml_canvas(object):
202 def __init__(self, canvas, doc_tmpl=None, doc=None, images={}, path='.'):
204 self.styles = doc.styles
205 self.doc_tmpl = doc_tmpl
210 def _textual(self, node):
212 for n in node.childNodes:
213 if n.nodeType == n.ELEMENT_NODE:
214 if n.localName == 'pageNumber':
215 rc += str(self.canvas.getPageNumber())
216 elif n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE):
217 # this doesn't need to be "entities" encoded like flowables need to
219 return rc.encode(encoding, 'replace')
221 def _drawString(self, node):
222 self.canvas.drawString(text=self._textual(node), **utils.attr_get(node, ['x','y']))
223 def _drawCenteredString(self, node):
224 self.canvas.drawCentredString(text=self._textual(node), **utils.attr_get(node, ['x','y']))
225 def _drawRightString(self, node):
226 self.canvas.drawRightString(text=self._textual(node), **utils.attr_get(node, ['x','y']))
227 def _rect(self, node):
228 if node.hasAttribute('round'):
229 self.canvas.roundRect(radius=utils.unit_get(node.getAttribute('round')), **utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'}))
231 self.canvas.rect(**utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'}))
233 def _ellipse(self, node):
234 x1 = utils.unit_get(node.getAttribute('x'))
235 x2 = utils.unit_get(node.getAttribute('width'))
236 y1 = utils.unit_get(node.getAttribute('y'))
237 y2 = utils.unit_get(node.getAttribute('height'))
238 self.canvas.ellipse(x1,y1,x2,y2, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'}))
239 def _curves(self, node):
240 line_str = utils.text_get(node).split()
242 while len(line_str)>7:
243 self.canvas.bezier(*[utils.unit_get(l) for l in line_str[0:8]])
244 line_str = line_str[8:]
245 def _lines(self, node):
246 line_str = utils.text_get(node).split()
248 while len(line_str)>3:
249 lines.append([utils.unit_get(l) for l in line_str[0:4]])
250 line_str = line_str[4:]
251 self.canvas.lines(lines)
252 def _grid(self, node):
253 xlist = [utils.unit_get(s) for s in node.getAttribute('xs').split(',')]
254 ylist = [utils.unit_get(s) for s in node.getAttribute('ys').split(',')]
255 self.canvas.grid(xlist, ylist)
256 def _translate(self, node):
259 if node.hasAttribute('dx'):
260 dx = utils.unit_get(node.getAttribute('dx'))
261 if node.hasAttribute('dy'):
262 dy = utils.unit_get(node.getAttribute('dy'))
263 self.canvas.translate(dx,dy)
265 def _circle(self, node):
266 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'}))
268 def _place(self, node):
269 flows = _rml_flowable(self.doc, images=self.images, path=self.path).render(node)
270 infos = utils.attr_get(node, ['x','y','width','height'])
272 infos['y']+=infos['height']
274 w,h = flow.wrap(infos['width'], infos['height'])
275 if w<=infos['width'] and h<=infos['height']:
277 flow.drawOn(self.canvas,infos['x'],infos['y'])
280 raise ValueError, "Not enough space"
282 def _line_mode(self, node):
283 ljoin = {'round':1, 'mitered':0, 'bevelled':2}
284 lcap = {'default':0, 'round':1, 'square':2}
285 if node.hasAttribute('width'):
286 self.canvas.setLineWidth(utils.unit_get(node.getAttribute('width')))
287 if node.hasAttribute('join'):
288 self.canvas.setLineJoin(ljoin[node.getAttribute('join')])
289 if node.hasAttribute('cap'):
290 self.canvas.setLineCap(lcap[node.getAttribute('cap')])
291 if node.hasAttribute('miterLimit'):
292 self.canvas.setDash(utils.unit_get(node.getAttribute('miterLimit')))
293 if node.hasAttribute('dash'):
294 dashes = node.getAttribute('dash').split(',')
295 for x in range(len(dashes)):
296 dashes[x]=utils.unit_get(dashes[x])
297 self.canvas.setDash(node.getAttribute('dash').split(','))
299 def _image(self, node):
301 from reportlab.lib.utils import ImageReader
303 if not node.hasAttribute('file'):
304 if node.getAttribute('name') in self.images:
305 scontent = self.images[node.getAttribute('name')]
306 s = StringIO.StringIO(scontent)
310 if node.getAttribute('file') in self.images:
311 s = StringIO.StringIO(self.images[node.getAttribute('file')])
314 u = urllib.urlopen(str(node.getAttribute('file')))
315 s = StringIO.StringIO(u.read())
317 u = file(os.path.join(self.path,str(node.getAttribute('file'))), 'rb')
318 s = StringIO.StringIO(u.read())
320 (sx,sy) = img.getSize()
323 for tag in ('width','height','x','y'):
324 if node.hasAttribute(tag):
325 args[tag] = utils.unit_get(node.getAttribute(tag))
326 if ('width' in args) and (not 'height' in args):
327 args['height'] = sy * args['width'] / sx
328 elif ('height' in args) and (not 'width' in args):
329 args['width'] = sx * args['height'] / sy
330 elif ('width' in args) and ('height' in args):
331 if (float(args['width'])/args['height'])>(float(sx)>sy):
332 args['width'] = sx * args['height'] / sy
334 args['height'] = sy * args['width'] / sx
335 self.canvas.drawImage(img, **args)
337 def _path(self, node):
338 self.path = self.canvas.beginPath()
339 self.path.moveTo(**utils.attr_get(node, ['x','y']))
340 for n in node.childNodes:
341 if n.nodeType == node.ELEMENT_NODE:
342 if n.localName=='moveto':
343 vals = utils.text_get(n).split()
344 self.path.moveTo(utils.unit_get(vals[0]), utils.unit_get(vals[1]))
345 elif n.localName=='curvesto':
346 vals = utils.text_get(n).split()
350 pos.append(utils.unit_get(vals.pop(0)))
351 self.path.curveTo(*pos)
352 elif (n.nodeType == node.TEXT_NODE):
353 data = n.data.split() # Not sure if I must merge all TEXT_NODE ?
355 x = utils.unit_get(data.pop(0))
356 y = utils.unit_get(data.pop(0))
357 self.path.lineTo(x,y)
358 if (not node.hasAttribute('close')) or utils.bool_get(node.getAttribute('close')):
360 self.canvas.drawPath(self.path, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'}))
362 def render(self, node):
364 'drawCentredString': self._drawCenteredString,
365 'drawRightString': self._drawRightString,
366 'drawString': self._drawString,
368 'ellipse': self._ellipse,
369 'lines': self._lines,
371 'curves': self._curves,
372 'fill': lambda node: self.canvas.setFillColor(color.get(node.getAttribute('color'))),
373 'stroke': lambda node: self.canvas.setStrokeColor(color.get(node.getAttribute('color'))),
374 'setFont': lambda node: self.canvas.setFont(node.getAttribute('name'), utils.unit_get(node.getAttribute('size'))),
375 'place': self._place,
376 'circle': self._circle,
377 'lineMode': self._line_mode,
379 'rotate': lambda node: self.canvas.rotate(float(node.getAttribute('degrees'))),
380 'translate': self._translate,
383 for nd in node.childNodes:
384 if nd.nodeType==nd.ELEMENT_NODE:
386 if nd.localName==tag:
390 class _rml_draw(object):
391 def __init__(self, node, styles, images={}, path='.'):
398 def render(self, canvas, doc):
400 cnv = _rml_canvas(canvas, doc, self.styles, images=self.images, path=self.path)
401 cnv.render(self.node)
402 canvas.restoreState()
404 class _rml_flowable(object):
405 def __init__(self, doc, images={}, path='.'):
407 self.styles = doc.styles
411 def _textual(self, node):
413 for n in node.childNodes:
414 if n.nodeType == node.ELEMENT_NODE:
415 if n.localName == 'getName':
416 newNode = self.doc.dom.createTextNode(self.styles.names.get(n.getAttribute('id'),'Unknown name'))
417 node.insertBefore(newNode, n)
419 elif n.localName == 'pageNumber':
420 rc += '<pageNumber/>' # TODO: change this !
422 #CHECKME: I wonder if this is useful since we don't stock the result. Maybe for the getName tag?
425 elif n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE):
426 rc += str2xml(n.data)
427 return rc.encode(encoding, 'replace')
429 def _table(self, node):
434 childs = _child_get(node,'tr')
439 for td in _child_get(tr, 'td'):
441 for n in td.childNodes:
442 if n.nodeType==node.ELEMENT_NODE:
443 flow.append( self._flowable(n) )
445 flow = self._textual(td)
447 if len(data2)>length:
450 while len(ab)<length:
452 while len(data2)<length:
455 if node.hasAttribute('colWidths'):
456 assert length == len(node.getAttribute('colWidths').split(','))
457 colwidths = [utils.unit_get(f.strip()) for f in node.getAttribute('colWidths').split(',')]
458 if node.hasAttribute('rowHeights'):
459 rowheights = [utils.unit_get(f.strip()) for f in node.getAttribute('rowHeights').split(',')]
460 table = platypus.Table(data = data, colWidths=colwidths, rowHeights=rowheights, **(utils.attr_get(node, ['splitByRow'] ,{'repeatRows':'int','repeatCols':'int'})))
461 if node.hasAttribute('style'):
462 table.setStyle(self.styles.table_styles[node.getAttribute('style')])
465 def _illustration(self, node):
466 class Illustration(platypus.flowables.Flowable):
467 def __init__(self, node, styles, self2):
470 self.width = utils.unit_get(node.getAttribute('width'))
471 self.height = utils.unit_get(node.getAttribute('height'))
473 def wrap(self, *args):
474 return (self.width, self.height)
477 drw = _rml_draw(self.node, self.styles, images=self.self2.images, path=self.self2.path)
478 drw.render(self.canv, None)
479 return Illustration(node, self.styles, self)
481 def _textual_image(self, node):
484 for n in node.childNodes:
485 if n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE):
487 return base64.decodestring(rc)
489 def _flowable(self, node):
490 if node.localName=='para':
491 style = self.styles.para_style_get(node)
492 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
493 elif node.localName=='barCode':
495 from reportlab.graphics.barcode import code128
496 from reportlab.graphics.barcode import code39
497 from reportlab.graphics.barcode import code93
498 from reportlab.graphics.barcode import common
499 from reportlab.graphics.barcode import fourstate
500 from reportlab.graphics.barcode import usps
502 print 'Warning: Reportlab barcode extension not installed !'
504 args = utils.attr_get(node, [], {'ratio':'float','xdim':'unit','height':'unit','checksum':'bool','quiet':'bool'})
506 'codabar': lambda x: common.Codabar(x, **args),
507 'code11': lambda x: common.Code11(x, **args),
508 'code128': lambda x: code128.Code128(x, **args),
509 'standard39': lambda x: code39.Standard39(x, **args),
510 'standard93': lambda x: code93.Standard93(x, **args),
511 'i2of5': lambda x: common.I2of5(x, **args),
512 'extended39': lambda x: code39.Extended39(x, **args),
513 'extended93': lambda x: code93.Extended93(x, **args),
514 'msi': lambda x: common.MSI(x, **args),
515 'fim': lambda x: usps.FIM(x, **args),
516 'postnet': lambda x: usps.POSTNET(x, **args),
519 if node.hasAttribute('code'):
520 code = node.getAttribute('code').lower()
521 return codes[code](self._textual(node))
522 elif node.localName=='name':
523 self.styles.names[ node.getAttribute('id')] = node.getAttribute('value')
525 elif node.localName=='xpre':
526 style = self.styles.para_style_get(node)
527 return platypus.XPreformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int','frags':'int'})))
528 elif node.localName=='pre':
529 style = self.styles.para_style_get(node)
530 return platypus.Preformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int'})))
531 elif node.localName=='illustration':
532 return self._illustration(node)
533 elif node.localName=='blockTable':
534 return self._table(node)
535 elif node.localName=='title':
536 styles = reportlab.lib.styles.getSampleStyleSheet()
537 style = styles['Title']
538 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
539 elif node.localName=='h1':
540 styles = reportlab.lib.styles.getSampleStyleSheet()
541 style = styles['Heading1']
542 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
543 elif node.localName=='h2':
544 styles = reportlab.lib.styles.getSampleStyleSheet()
545 style = styles['Heading2']
546 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
547 elif node.localName=='h3':
548 styles = reportlab.lib.styles.getSampleStyleSheet()
549 style = styles['Heading3']
550 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
551 elif node.localName=='image':
552 if not node.hasAttribute('file'):
553 if node.hasAttribute('name'):
554 image_data = self.doc.images[node.getAttribute('name')].read()
557 image_data = base64.decodestring(node.firstChild.nodeValue)
558 image = StringIO.StringIO(image_data)
559 return platypus.Image(image, mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height'])))
561 return platypus.Image(node.getAttribute('file'), mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height'])))
563 from reportlab.lib.utils import ImageReader
564 name = str(node.getAttribute('file'))
565 img = ImageReader(name)
566 (sx,sy) = img.getSize()
569 for tag in ('width','height'):
570 if node.hasAttribute(tag):
571 args[tag] = utils.unit_get(node.getAttribute(tag))
572 if ('width' in args) and (not 'height' in args):
573 args['height'] = sy * args['width'] / sx
574 elif ('height' in args) and (not 'width' in args):
575 args['width'] = sx * args['height'] / sy
576 elif ('width' in args) and ('height' in args):
577 if (float(args['width'])/args['height'])>(float(sx)>sy):
578 args['width'] = sx * args['height'] / sy
580 args['height'] = sy * args['width'] / sx
581 return platypus.Image(name, mask=(250,255,250,255,250,255), **args)
582 elif node.localName=='spacer':
583 if node.hasAttribute('width'):
584 width = utils.unit_get(node.getAttribute('width'))
586 width = utils.unit_get('1cm')
587 length = utils.unit_get(node.getAttribute('length'))
588 return platypus.Spacer(width=width, height=length)
589 elif node.localName=='section':
590 return self.render(node)
591 elif node.localName in ('pageBreak', 'nextPage'):
592 return platypus.PageBreak()
593 elif node.localName=='condPageBreak':
594 return platypus.CondPageBreak(**(utils.attr_get(node, ['height'])))
595 elif node.localName=='setNextTemplate':
596 return platypus.NextPageTemplate(str(node.getAttribute('name')))
597 elif node.localName=='nextFrame':
598 return platypus.CondPageBreak(1000) # TODO: change the 1000 !
599 elif node.localName == 'setNextFrame':
600 from reportlab.platypus.doctemplate import NextFrameFlowable
601 return NextFrameFlowable(str(node.getAttribute('name')))
602 elif node.localName == 'currentFrame':
603 from reportlab.platypus.doctemplate import CurrentFrameFlowable
604 return CurrentFrameFlowable(str(node.getAttribute('name')))
605 elif node.localName == 'frameEnd':
606 return EndFrameFlowable()
608 sys.stderr.write('Warning: flowable not yet implemented: %s !\n' % (node.localName,))
611 def render(self, node_story):
613 node = node_story.firstChild
615 if node.nodeType == node.ELEMENT_NODE:
616 flow = self._flowable(node)
618 if type(flow) == type([]):
622 node = node.nextSibling
625 from reportlab.platypus.doctemplate import ActionFlowable
627 class EndFrameFlowable(ActionFlowable):
628 def __init__(self,resume=0):
629 ActionFlowable.__init__(self,('frameEnd',resume))
631 class TinyDocTemplate(platypus.BaseDocTemplate):
633 def ___handle_pageBegin(self):
634 self.page = self.page + 1
635 self.pageTemplate.beforeDrawPage(self.canv,self)
636 self.pageTemplate.checkPageSize(self.canv,self)
637 self.pageTemplate.onPage(self.canv,self)
638 for f in self.pageTemplate.frames: f._reset()
640 #keep a count of flowables added to this page. zero indicates bad stuff
641 self._curPageFlowableCount = 0
642 if hasattr(self,'_nextFrameIndex'):
643 del self._nextFrameIndex
644 for f in self.pageTemplate.frames:
648 self.handle_frameBegin()
650 class _rml_template(object):
651 def __init__(self, out, node, doc, images={}, path='.'):
654 if not node.hasAttribute('pageSize'):
655 pageSize = (utils.unit_get('21cm'), utils.unit_get('29.7cm'))
657 ps = map(lambda x:x.strip(), node.getAttribute('pageSize').replace(')', '').replace('(', '').split(','))
658 pageSize = ( utils.unit_get(ps[0]),utils.unit_get(ps[1]) )
659 cm = reportlab.lib.units.cm
660 self.doc_tmpl = TinyDocTemplate(out, pagesize=pageSize, **utils.attr_get(node, ['leftMargin','rightMargin','topMargin','bottomMargin'], {'allowSplitting':'int','showBoundary':'bool','title':'str','author':'str'}))
661 self.page_templates = []
662 self.styles = doc.styles
664 pts = node.getElementsByTagName('pageTemplate')
667 for frame_el in pt.getElementsByTagName('frame'):
668 frame = platypus.Frame( **(utils.attr_get(frame_el, ['x1','y1', 'width','height', 'leftPadding', 'rightPadding', 'bottomPadding', 'topPadding'], {'id':'str', 'showBoundary':'bool'})) )
669 if utils.attr_get(frame_el, ['last']):
670 frame.lastFrame = True
671 frames.append( frame )
672 gr = pt.getElementsByTagName('pageGraphics')
674 drw = _rml_draw(gr[0], self.doc, images=images, path=self.path)
675 self.page_templates.append( platypus.PageTemplate(frames=frames, onPage=drw.render, **utils.attr_get(pt, [], {'id':'str'}) ))
677 self.page_templates.append( platypus.PageTemplate(frames=frames, **utils.attr_get(pt, [], {'id':'str'}) ))
678 self.doc_tmpl.addPageTemplates(self.page_templates)
680 def render(self, node_stories):
682 r = _rml_flowable(self.doc,images=self.images, path=self.path)
683 for node_story in node_stories:
684 fis += r.render(node_story)
685 fis.append(platypus.PageBreak())
686 self.doc_tmpl.build(fis)
688 def parseString(data, fout=None, images={}, path='.'):
689 r = _rml_doc(data, images, path)
696 fp = StringIO.StringIO()
701 print 'Usage: trml2pdf input.rml >output.pdf'
702 print 'Render the standard input (RML) and output a PDF file'
705 if __name__=="__main__":
707 if sys.argv[1]=='--help':
709 print parseString(file(sys.argv[1], 'r').read()),
711 print 'Usage: trml2pdf input.rml >output.pdf'
712 print 'Try \'trml2pdf --help\' for more information.'