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=='blockSpan':
102 styles.append(('SPAN', start, stop))
103 elif node.localName=='blockLeftPadding':
104 styles.append(('LEFTPADDING', start, stop, utils.unit_get(node.getAttribute('length'))))
105 elif node.localName=='blockRightPadding':
106 styles.append(('RIGHTPADDING', start, stop, utils.unit_get(node.getAttribute('length'))))
107 elif node.localName=='blockTopPadding':
108 styles.append(('TOPPADDING', start, stop, utils.unit_get(node.getAttribute('length'))))
109 elif node.localName=='blockBottomPadding':
110 styles.append(('BOTTOMPADDING', start, stop, utils.unit_get(node.getAttribute('length'))))
111 elif node.localName=='blockBackground':
112 styles.append(('BACKGROUND', start, stop, color.get(node.getAttribute('colorName'))))
113 if node.hasAttribute('size'):
114 styles.append(('FONTSIZE', start, stop, utils.unit_get(node.getAttribute('size'))))
115 elif node.localName=='lineStyle':
116 kind = node.getAttribute('kind')
117 kind_list = [ 'GRID', 'BOX', 'OUTLINE', 'INNERGRID', 'LINEBELOW', 'LINEABOVE','LINEBEFORE', 'LINEAFTER' ]
118 assert kind in kind_list
120 if node.hasAttribute('thickness'):
121 thick = float(node.getAttribute('thickness'))
122 styles.append((kind, start, stop, thick, color.get(node.getAttribute('colorName'))))
123 return platypus.tables.TableStyle(styles)
125 def _para_style_get(self, node):
126 styles = reportlab.lib.styles.getSampleStyleSheet()
127 style = copy.deepcopy(styles["Normal"])
128 self._para_style_update(style, node)
131 def para_style_get(self, node):
133 if node.hasAttribute('style'):
134 if node.getAttribute('style') in self.styles:
135 style = copy.deepcopy(self.styles[node.getAttribute('style')])
137 sys.stderr.write('Warning: style not found, %s - setting default!\n' % (node.getAttribute('style'),) )
139 styles = reportlab.lib.styles.getSampleStyleSheet()
140 style = copy.deepcopy(styles['Normal'])
141 return self._para_style_update(style, node)
143 class _rml_doc(object):
144 def __init__(self, data, images={}, path='.'):
145 self.dom = xml.dom.minidom.parseString(data)
146 self.filename = self.dom.documentElement.getAttribute('filename')
150 def docinit(self, els):
151 from reportlab.lib.fonts import addMapping
152 from reportlab.pdfbase import pdfmetrics
153 from reportlab.pdfbase.ttfonts import TTFont
156 for font in node.getElementsByTagName('registerFont'):
157 name = font.getAttribute('fontName').encode('ascii')
158 fname = font.getAttribute('fontFile').encode('ascii')
159 pdfmetrics.registerFont(TTFont(name, fname ))
160 addMapping(name, 0, 0, name) #normal
161 addMapping(name, 0, 1, name) #italic
162 addMapping(name, 1, 0, name) #bold
163 addMapping(name, 1, 1, name) #italic and bold
165 def _textual_image(self, node):
168 for n in node.childNodes:
169 if n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE):
171 return base64.decodestring(rc)
173 def _images(self, el):
175 for node in el.getElementsByTagName('image'):
176 result[node.getAttribute('name')] = self._textual_image(node)
179 def render(self, out):
180 self.canvas = canvas.Canvas(out)
181 el = self.dom.documentElement.getElementsByTagName('docinit')
185 el = self.dom.documentElement.getElementsByTagName('stylesheet')
186 self.styles = _rml_styles(el)
188 el = self.dom.documentElement.getElementsByTagName('images')
190 self.images.update( self._images(el[0]) )
192 el = self.dom.documentElement.getElementsByTagName('template')
194 pt_obj = _rml_template(out, el[0], self, images=self.images, path=self.path)
195 pt_obj.render(self.dom.documentElement.getElementsByTagName('story'))
198 pd = self.dom.documentElement.getElementsByTagName('pageDrawing')[0]
199 pd_obj = _rml_canvas(self.canvas, None, self, self.images, path=self.path)
201 self.canvas.showPage()
204 class _rml_canvas(object):
205 def __init__(self, canvas, doc_tmpl=None, doc=None, images={}, path='.'):
207 self.styles = doc.styles
208 self.doc_tmpl = doc_tmpl
213 def _textual(self, node):
215 for n in node.childNodes:
216 if n.nodeType == n.ELEMENT_NODE:
217 if n.localName == 'pageNumber':
218 rc += str(self.canvas.getPageNumber())
219 elif n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE):
220 # this doesn't need to be "entities" encoded like flowables need to
222 return rc.encode(encoding, 'replace')
224 def _drawString(self, node):
225 self.canvas.drawString(text=self._textual(node), **utils.attr_get(node, ['x','y']))
226 def _drawCenteredString(self, node):
227 self.canvas.drawCentredString(text=self._textual(node), **utils.attr_get(node, ['x','y']))
228 def _drawRightString(self, node):
229 self.canvas.drawRightString(text=self._textual(node), **utils.attr_get(node, ['x','y']))
230 def _rect(self, node):
231 if node.hasAttribute('round'):
232 self.canvas.roundRect(radius=utils.unit_get(node.getAttribute('round')), **utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'}))
234 self.canvas.rect(**utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'}))
236 def _ellipse(self, node):
237 x1 = utils.unit_get(node.getAttribute('x'))
238 x2 = utils.unit_get(node.getAttribute('width'))
239 y1 = utils.unit_get(node.getAttribute('y'))
240 y2 = utils.unit_get(node.getAttribute('height'))
241 self.canvas.ellipse(x1,y1,x2,y2, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'}))
242 def _curves(self, node):
243 line_str = utils.text_get(node).split()
245 while len(line_str)>7:
246 self.canvas.bezier(*[utils.unit_get(l) for l in line_str[0:8]])
247 line_str = line_str[8:]
248 def _lines(self, node):
249 line_str = utils.text_get(node).split()
251 while len(line_str)>3:
252 lines.append([utils.unit_get(l) for l in line_str[0:4]])
253 line_str = line_str[4:]
254 self.canvas.lines(lines)
255 def _grid(self, node):
256 xlist = [utils.unit_get(s) for s in node.getAttribute('xs').split(',')]
257 ylist = [utils.unit_get(s) for s in node.getAttribute('ys').split(',')]
258 self.canvas.grid(xlist, ylist)
259 def _translate(self, node):
262 if node.hasAttribute('dx'):
263 dx = utils.unit_get(node.getAttribute('dx'))
264 if node.hasAttribute('dy'):
265 dy = utils.unit_get(node.getAttribute('dy'))
266 self.canvas.translate(dx,dy)
268 def _circle(self, node):
269 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'}))
271 def _place(self, node):
272 flows = _rml_flowable(self.doc, images=self.images, path=self.path).render(node)
273 infos = utils.attr_get(node, ['x','y','width','height'])
275 infos['y']+=infos['height']
277 w,h = flow.wrap(infos['width'], infos['height'])
278 if w<=infos['width'] and h<=infos['height']:
280 flow.drawOn(self.canvas,infos['x'],infos['y'])
283 raise ValueError, "Not enough space"
285 def _line_mode(self, node):
286 ljoin = {'round':1, 'mitered':0, 'bevelled':2}
287 lcap = {'default':0, 'round':1, 'square':2}
288 if node.hasAttribute('width'):
289 self.canvas.setLineWidth(utils.unit_get(node.getAttribute('width')))
290 if node.hasAttribute('join'):
291 self.canvas.setLineJoin(ljoin[node.getAttribute('join')])
292 if node.hasAttribute('cap'):
293 self.canvas.setLineCap(lcap[node.getAttribute('cap')])
294 if node.hasAttribute('miterLimit'):
295 self.canvas.setDash(utils.unit_get(node.getAttribute('miterLimit')))
296 if node.hasAttribute('dash'):
297 dashes = node.getAttribute('dash').split(',')
298 for x in range(len(dashes)):
299 dashes[x]=utils.unit_get(dashes[x])
300 self.canvas.setDash(node.getAttribute('dash').split(','))
302 def _image(self, node):
304 from reportlab.lib.utils import ImageReader
306 if not node.hasAttribute('file'):
307 if node.getAttribute('name') in self.images:
308 scontent = self.images[node.getAttribute('name')]
309 s = StringIO.StringIO(scontent)
313 if node.getAttribute('file') in self.images:
314 s = StringIO.StringIO(self.images[node.getAttribute('file')])
317 u = urllib.urlopen(str(node.getAttribute('file')))
318 s = StringIO.StringIO(u.read())
320 u = file(os.path.join(self.path,str(node.getAttribute('file'))), 'rb')
321 s = StringIO.StringIO(u.read())
323 (sx,sy) = img.getSize()
326 for tag in ('width','height','x','y'):
327 if node.hasAttribute(tag):
328 args[tag] = utils.unit_get(node.getAttribute(tag))
329 if ('width' in args) and (not 'height' in args):
330 args['height'] = sy * args['width'] / sx
331 elif ('height' in args) and (not 'width' in args):
332 args['width'] = sx * args['height'] / sy
333 elif ('width' in args) and ('height' in args):
334 if (float(args['width'])/args['height'])>(float(sx)>sy):
335 args['width'] = sx * args['height'] / sy
337 args['height'] = sy * args['width'] / sx
338 self.canvas.drawImage(img, **args)
340 def _path(self, node):
341 self.path = self.canvas.beginPath()
342 self.path.moveTo(**utils.attr_get(node, ['x','y']))
343 for n in node.childNodes:
344 if n.nodeType == node.ELEMENT_NODE:
345 if n.localName=='moveto':
346 vals = utils.text_get(n).split()
347 self.path.moveTo(utils.unit_get(vals[0]), utils.unit_get(vals[1]))
348 elif n.localName=='curvesto':
349 vals = utils.text_get(n).split()
353 pos.append(utils.unit_get(vals.pop(0)))
354 self.path.curveTo(*pos)
355 elif (n.nodeType == node.TEXT_NODE):
356 data = n.data.split() # Not sure if I must merge all TEXT_NODE ?
358 x = utils.unit_get(data.pop(0))
359 y = utils.unit_get(data.pop(0))
360 self.path.lineTo(x,y)
361 if (not node.hasAttribute('close')) or utils.bool_get(node.getAttribute('close')):
363 self.canvas.drawPath(self.path, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'}))
365 def render(self, node):
367 'drawCentredString': self._drawCenteredString,
368 'drawRightString': self._drawRightString,
369 'drawString': self._drawString,
371 'ellipse': self._ellipse,
372 'lines': self._lines,
374 'curves': self._curves,
375 'fill': lambda node: self.canvas.setFillColor(color.get(node.getAttribute('color'))),
376 'stroke': lambda node: self.canvas.setStrokeColor(color.get(node.getAttribute('color'))),
377 'setFont': lambda node: self.canvas.setFont(node.getAttribute('name'), utils.unit_get(node.getAttribute('size'))),
378 'place': self._place,
379 'circle': self._circle,
380 'lineMode': self._line_mode,
382 'rotate': lambda node: self.canvas.rotate(float(node.getAttribute('degrees'))),
383 'translate': self._translate,
386 for nd in node.childNodes:
387 if nd.nodeType==nd.ELEMENT_NODE:
389 if nd.localName==tag:
393 class _rml_draw(object):
394 def __init__(self, node, styles, images={}, path='.'):
401 def render(self, canvas, doc):
403 cnv = _rml_canvas(canvas, doc, self.styles, images=self.images, path=self.path)
404 cnv.render(self.node)
405 canvas.restoreState()
407 class _rml_flowable(object):
408 def __init__(self, doc, images={}, path='.'):
410 self.styles = doc.styles
414 def _textual(self, node):
416 for n in node.childNodes:
417 if n.nodeType == node.ELEMENT_NODE:
418 if n.localName == 'getName':
419 newNode = self.doc.dom.createTextNode(self.styles.names.get(n.getAttribute('id'),'Unknown name'))
420 node.insertBefore(newNode, n)
423 elif n.localName == 'pageNumber':
424 newNode = self.doc.dom.createTextNode(str(self.doc.canvas.getPageNumber()))
425 node.insertBefore(newNode, n)
429 #CHECKME: I wonder if this is useful since we don't stock the result. Maybe for the getName tag?
432 elif n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE):
433 rc += str2xml(n.data)
434 return rc.encode(encoding, 'replace')
436 def _table(self, node):
441 childs = _child_get(node,'tr')
446 for td in _child_get(tr, 'td'):
448 for n in td.childNodes:
449 if n.nodeType==node.ELEMENT_NODE:
450 flow.append( self._flowable(n) )
452 flow = self._textual(td)
454 if len(data2)>length:
457 while len(ab)<length:
459 while len(data2)<length:
462 if node.hasAttribute('colWidths'):
463 assert length == len(node.getAttribute('colWidths').split(','))
464 colwidths = [utils.unit_get(f.strip()) for f in node.getAttribute('colWidths').split(',')]
465 if node.hasAttribute('rowHeights'):
466 rowheights = [utils.unit_get(f.strip()) for f in node.getAttribute('rowHeights').split(',')]
467 if len(rowheights) == 1:
468 rowheights = rowheights[0]
469 table = platypus.Table(data = data, colWidths=colwidths, rowHeights=rowheights, **(utils.attr_get(node, ['splitByRow'] ,{'repeatRows':'int','repeatCols':'int'})))
470 if node.hasAttribute('style'):
471 table.setStyle(self.styles.table_styles[node.getAttribute('style')])
474 def _illustration(self, node):
475 class Illustration(platypus.flowables.Flowable):
476 def __init__(self, node, styles, self2):
479 self.width = utils.unit_get(node.getAttribute('width'))
480 self.height = utils.unit_get(node.getAttribute('height'))
482 def wrap(self, *args):
483 return (self.width, self.height)
486 drw = _rml_draw(self.node, self.styles, images=self.self2.images, path=self.self2.path)
487 drw.render(self.canv, None)
488 return Illustration(node, self.styles, self)
490 def _textual_image(self, node):
493 for n in node.childNodes:
494 if n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE):
496 return base64.decodestring(rc)
498 def _flowable(self, node):
499 if node.localName=='para':
500 style = self.styles.para_style_get(node)
501 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
502 elif node.localName=='barCode':
504 from reportlab.graphics.barcode import code128
505 from reportlab.graphics.barcode import code39
506 from reportlab.graphics.barcode import code93
507 from reportlab.graphics.barcode import common
508 from reportlab.graphics.barcode import fourstate
509 from reportlab.graphics.barcode import usps
511 print 'Warning: Reportlab barcode extension not installed !'
513 args = utils.attr_get(node, [], {'ratio':'float','xdim':'unit','height':'unit','checksum':'bool','quiet':'bool'})
515 'codabar': lambda x: common.Codabar(x, **args),
516 'code11': lambda x: common.Code11(x, **args),
517 'code128': lambda x: code128.Code128(x, **args),
518 'standard39': lambda x: code39.Standard39(x, **args),
519 'standard93': lambda x: code93.Standard93(x, **args),
520 'i2of5': lambda x: common.I2of5(x, **args),
521 'extended39': lambda x: code39.Extended39(x, **args),
522 'extended93': lambda x: code93.Extended93(x, **args),
523 'msi': lambda x: common.MSI(x, **args),
524 'fim': lambda x: usps.FIM(x, **args),
525 'postnet': lambda x: usps.POSTNET(x, **args),
528 if node.hasAttribute('code'):
529 code = node.getAttribute('code').lower()
530 return codes[code](self._textual(node))
531 elif node.localName=='name':
532 self.styles.names[ node.getAttribute('id')] = node.getAttribute('value')
534 elif node.localName=='xpre':
535 style = self.styles.para_style_get(node)
536 return platypus.XPreformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int','frags':'int'})))
537 elif node.localName=='pre':
538 style = self.styles.para_style_get(node)
539 return platypus.Preformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int'})))
540 elif node.localName=='illustration':
541 return self._illustration(node)
542 elif node.localName=='blockTable':
543 return self._table(node)
544 elif node.localName=='title':
545 styles = reportlab.lib.styles.getSampleStyleSheet()
546 style = styles['Title']
547 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
548 elif node.localName=='h1':
549 styles = reportlab.lib.styles.getSampleStyleSheet()
550 style = styles['Heading1']
551 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
552 elif node.localName=='h2':
553 styles = reportlab.lib.styles.getSampleStyleSheet()
554 style = styles['Heading2']
555 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
556 elif node.localName=='h3':
557 styles = reportlab.lib.styles.getSampleStyleSheet()
558 style = styles['Heading3']
559 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
560 elif node.localName=='image':
561 if not node.hasAttribute('file'):
562 if node.hasAttribute('name'):
563 image_data = self.doc.images[node.getAttribute('name')].read()
566 image_data = base64.decodestring(node.firstChild.nodeValue)
567 image = StringIO.StringIO(image_data)
568 return platypus.Image(image, mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height'])))
570 return platypus.Image(node.getAttribute('file'), mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height'])))
572 from reportlab.lib.utils import ImageReader
573 name = str(node.getAttribute('file'))
574 img = ImageReader(name)
575 (sx,sy) = img.getSize()
578 for tag in ('width','height'):
579 if node.hasAttribute(tag):
580 args[tag] = utils.unit_get(node.getAttribute(tag))
581 if ('width' in args) and (not 'height' in args):
582 args['height'] = sy * args['width'] / sx
583 elif ('height' in args) and (not 'width' in args):
584 args['width'] = sx * args['height'] / sy
585 elif ('width' in args) and ('height' in args):
586 if (float(args['width'])/args['height'])>(float(sx)>sy):
587 args['width'] = sx * args['height'] / sy
589 args['height'] = sy * args['width'] / sx
590 return platypus.Image(name, mask=(250,255,250,255,250,255), **args)
591 elif node.localName=='spacer':
592 if node.hasAttribute('width'):
593 width = utils.unit_get(node.getAttribute('width'))
595 width = utils.unit_get('1cm')
596 length = utils.unit_get(node.getAttribute('length'))
597 return platypus.Spacer(width=width, height=length)
598 elif node.localName=='section':
599 return self.render(node)
600 elif node.localName in ('pageBreak', 'nextPage'):
601 return platypus.PageBreak()
602 elif node.localName=='condPageBreak':
603 return platypus.CondPageBreak(**(utils.attr_get(node, ['height'])))
604 elif node.localName=='setNextTemplate':
605 return platypus.NextPageTemplate(str(node.getAttribute('name')))
606 elif node.localName=='nextFrame':
607 return platypus.CondPageBreak(1000) # TODO: change the 1000 !
608 elif node.localName == 'setNextFrame':
609 from reportlab.platypus.doctemplate import NextFrameFlowable
610 return NextFrameFlowable(str(node.getAttribute('name')))
611 elif node.localName == 'currentFrame':
612 from reportlab.platypus.doctemplate import CurrentFrameFlowable
613 return CurrentFrameFlowable(str(node.getAttribute('name')))
614 elif node.localName == 'frameEnd':
615 return EndFrameFlowable()
617 sys.stderr.write('Warning: flowable not yet implemented: %s !\n' % (node.localName,))
620 def render(self, node_story):
622 node = node_story.firstChild
624 if node.nodeType == node.ELEMENT_NODE:
625 flow = self._flowable(node)
627 if type(flow) == type([]):
631 node = node.nextSibling
634 from reportlab.platypus.doctemplate import ActionFlowable
636 class EndFrameFlowable(ActionFlowable):
637 def __init__(self,resume=0):
638 ActionFlowable.__init__(self,('frameEnd',resume))
640 class TinyDocTemplate(platypus.BaseDocTemplate):
642 def ___handle_pageBegin(self):
643 self.page = self.page + 1
644 self.pageTemplate.beforeDrawPage(self.canv,self)
645 self.pageTemplate.checkPageSize(self.canv,self)
646 self.pageTemplate.onPage(self.canv,self)
647 for f in self.pageTemplate.frames: f._reset()
649 #keep a count of flowables added to this page. zero indicates bad stuff
650 self._curPageFlowableCount = 0
651 if hasattr(self,'_nextFrameIndex'):
652 del self._nextFrameIndex
653 for f in self.pageTemplate.frames:
657 self.handle_frameBegin()
659 class _rml_template(object):
660 def __init__(self, out, node, doc, images={}, path='.'):
663 if not node.hasAttribute('pageSize'):
664 pageSize = (utils.unit_get('21cm'), utils.unit_get('29.7cm'))
666 ps = map(lambda x:x.strip(), node.getAttribute('pageSize').replace(')', '').replace('(', '').split(','))
667 pageSize = ( utils.unit_get(ps[0]),utils.unit_get(ps[1]) )
668 cm = reportlab.lib.units.cm
669 self.doc_tmpl = TinyDocTemplate(out, pagesize=pageSize, **utils.attr_get(node, ['leftMargin','rightMargin','topMargin','bottomMargin'], {'allowSplitting':'int','showBoundary':'bool','title':'str','author':'str'}))
670 self.page_templates = []
671 self.styles = doc.styles
673 pts = node.getElementsByTagName('pageTemplate')
676 for frame_el in pt.getElementsByTagName('frame'):
677 frame = platypus.Frame( **(utils.attr_get(frame_el, ['x1','y1', 'width','height', 'leftPadding', 'rightPadding', 'bottomPadding', 'topPadding'], {'id':'str', 'showBoundary':'bool'})) )
678 if utils.attr_get(frame_el, ['last']):
679 frame.lastFrame = True
680 frames.append( frame )
681 gr = pt.getElementsByTagName('pageGraphics')
683 drw = _rml_draw(gr[0], self.doc, images=images, path=self.path)
684 self.page_templates.append( platypus.PageTemplate(frames=frames, onPage=drw.render, **utils.attr_get(pt, [], {'id':'str'}) ))
686 self.page_templates.append( platypus.PageTemplate(frames=frames, **utils.attr_get(pt, [], {'id':'str'}) ))
687 self.doc_tmpl.addPageTemplates(self.page_templates)
689 def render(self, node_stories):
691 r = _rml_flowable(self.doc,images=self.images, path=self.path)
692 for node_story in node_stories:
693 fis += r.render(node_story)
694 fis.append(platypus.PageBreak())
695 self.doc_tmpl.build(fis)
697 def parseString(data, fout=None, images={}, path='.'):
698 r = _rml_doc(data, images, path)
705 fp = StringIO.StringIO()
710 print 'Usage: trml2pdf input.rml >output.pdf'
711 print 'Render the standard input (RML) and output a PDF file'
714 if __name__=="__main__":
716 if sys.argv[1]=='--help':
718 print parseString(file(sys.argv[1], 'r').read())
720 print 'Usage: trml2pdf input.rml >output.pdf'
721 print 'Try \'trml2pdf --help\' for more information.'