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 render(self, out):
164 el = self.dom.documentElement.getElementsByTagName('docinit')
168 el = self.dom.documentElement.getElementsByTagName('stylesheet')
169 self.styles = _rml_styles(el)
171 el = self.dom.documentElement.getElementsByTagName('template')
173 pt_obj = _rml_template(out, el[0], self, images=self.images, path=self.path)
174 pt_obj.render(self.dom.documentElement.getElementsByTagName('story'))
176 self.canvas = canvas.Canvas(out)
177 pd = self.dom.documentElement.getElementsByTagName('pageDrawing')[0]
178 pd_obj = _rml_canvas(self.canvas, None, self, self.images, path=self.path)
180 self.canvas.showPage()
183 class _rml_canvas(object):
184 def __init__(self, canvas, doc_tmpl=None, doc=None, images={}, path='.'):
186 self.styles = doc.styles
187 self.doc_tmpl = doc_tmpl
192 def _textual(self, node):
194 for n in node.childNodes:
195 if n.nodeType == n.ELEMENT_NODE:
196 if n.localName == 'pageNumber':
197 rc += str(self.canvas.getPageNumber())
198 elif n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE):
199 # this doesn't need to be "entities" encoded like flowables need to
201 return rc.encode(encoding, 'replace')
203 def _drawString(self, node):
204 self.canvas.drawString(text=self._textual(node), **utils.attr_get(node, ['x','y']))
205 def _drawCenteredString(self, node):
206 self.canvas.drawCentredString(text=self._textual(node), **utils.attr_get(node, ['x','y']))
207 def _drawRightString(self, node):
208 self.canvas.drawRightString(text=self._textual(node), **utils.attr_get(node, ['x','y']))
209 def _rect(self, node):
210 if node.hasAttribute('round'):
211 self.canvas.roundRect(radius=utils.unit_get(node.getAttribute('round')), **utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'}))
213 self.canvas.rect(**utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'}))
215 def _ellipse(self, node):
216 x1 = utils.unit_get(node.getAttribute('x'))
217 x2 = utils.unit_get(node.getAttribute('width'))
218 y1 = utils.unit_get(node.getAttribute('y'))
219 y2 = utils.unit_get(node.getAttribute('height'))
220 self.canvas.ellipse(x1,y1,x2,y2, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'}))
221 def _curves(self, node):
222 line_str = utils.text_get(node).split()
224 while len(line_str)>7:
225 self.canvas.bezier(*[utils.unit_get(l) for l in line_str[0:8]])
226 line_str = line_str[8:]
227 def _lines(self, node):
228 line_str = utils.text_get(node).split()
230 while len(line_str)>3:
231 lines.append([utils.unit_get(l) for l in line_str[0:4]])
232 line_str = line_str[4:]
233 self.canvas.lines(lines)
234 def _grid(self, node):
235 xlist = [utils.unit_get(s) for s in node.getAttribute('xs').split(',')]
236 ylist = [utils.unit_get(s) for s in node.getAttribute('ys').split(',')]
237 self.canvas.grid(xlist, ylist)
238 def _translate(self, node):
241 if node.hasAttribute('dx'):
242 dx = utils.unit_get(node.getAttribute('dx'))
243 if node.hasAttribute('dy'):
244 dy = utils.unit_get(node.getAttribute('dy'))
245 self.canvas.translate(dx,dy)
247 def _circle(self, node):
248 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'}))
250 def _place(self, node):
251 flows = _rml_flowable(self.doc, images=self.images, path=self.path).render(node)
252 infos = utils.attr_get(node, ['x','y','width','height'])
254 infos['y']+=infos['height']
256 w,h = flow.wrap(infos['width'], infos['height'])
257 if w<=infos['width'] and h<=infos['height']:
259 flow.drawOn(self.canvas,infos['x'],infos['y'])
262 raise ValueError, "Not enough space"
264 def _line_mode(self, node):
265 ljoin = {'round':1, 'mitered':0, 'bevelled':2}
266 lcap = {'default':0, 'round':1, 'square':2}
267 if node.hasAttribute('width'):
268 self.canvas.setLineWidth(utils.unit_get(node.getAttribute('width')))
269 if node.hasAttribute('join'):
270 self.canvas.setLineJoin(ljoin[node.getAttribute('join')])
271 if node.hasAttribute('cap'):
272 self.canvas.setLineCap(lcap[node.getAttribute('cap')])
273 if node.hasAttribute('miterLimit'):
274 self.canvas.setDash(utils.unit_get(node.getAttribute('miterLimit')))
275 if node.hasAttribute('dash'):
276 dashes = node.getAttribute('dash').split(',')
277 for x in range(len(dashes)):
278 dashes[x]=utils.unit_get(dashes[x])
279 self.canvas.setDash(node.getAttribute('dash').split(','))
281 def _image(self, node):
283 from reportlab.lib.utils import ImageReader
285 if not node.hasAttribute('file'):
286 s = self.images[node.getAttribute('name')]
289 u = urllib.urlopen(str(node.getAttribute('file')))
290 s = StringIO.StringIO(u.read())
292 u = file(os.path.join(self.path,str(node.getAttribute('file'))), 'rb')
293 s = StringIO.StringIO(u.read())
295 (sx,sy) = img.getSize()
298 for tag in ('width','height','x','y'):
299 if node.hasAttribute(tag):
300 args[tag] = utils.unit_get(node.getAttribute(tag))
301 if ('width' in args) and (not 'height' in args):
302 args['height'] = sy * args['width'] / sx
303 elif ('height' in args) and (not 'width' in args):
304 args['width'] = sx * args['height'] / sy
305 elif ('width' in args) and ('height' in args):
306 if (float(args['width'])/args['height'])>(float(sx)>sy):
307 args['width'] = sx * args['height'] / sy
309 args['height'] = sy * args['width'] / sx
310 self.canvas.drawImage(img, **args)
312 def _path(self, node):
313 self.path = self.canvas.beginPath()
314 self.path.moveTo(**utils.attr_get(node, ['x','y']))
315 for n in node.childNodes:
316 if n.nodeType == node.ELEMENT_NODE:
317 if n.localName=='moveto':
318 vals = utils.text_get(n).split()
319 self.path.moveTo(utils.unit_get(vals[0]), utils.unit_get(vals[1]))
320 elif n.localName=='curvesto':
321 vals = utils.text_get(n).split()
325 pos.append(utils.unit_get(vals.pop(0)))
326 self.path.curveTo(*pos)
327 elif (n.nodeType == node.TEXT_NODE):
328 data = n.data.split() # Not sure if I must merge all TEXT_NODE ?
330 x = utils.unit_get(data.pop(0))
331 y = utils.unit_get(data.pop(0))
332 self.path.lineTo(x,y)
333 if (not node.hasAttribute('close')) or utils.bool_get(node.getAttribute('close')):
335 self.canvas.drawPath(self.path, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'}))
337 def render(self, node):
339 'drawCentredString': self._drawCenteredString,
340 'drawRightString': self._drawRightString,
341 'drawString': self._drawString,
343 'ellipse': self._ellipse,
344 'lines': self._lines,
346 'curves': self._curves,
347 'fill': lambda node: self.canvas.setFillColor(color.get(node.getAttribute('color'))),
348 'stroke': lambda node: self.canvas.setStrokeColor(color.get(node.getAttribute('color'))),
349 'setFont': lambda node: self.canvas.setFont(node.getAttribute('name'), utils.unit_get(node.getAttribute('size'))),
350 'place': self._place,
351 'circle': self._circle,
352 'lineMode': self._line_mode,
354 'rotate': lambda node: self.canvas.rotate(float(node.getAttribute('degrees'))),
355 'translate': self._translate,
358 for nd in node.childNodes:
359 if nd.nodeType==nd.ELEMENT_NODE:
361 if nd.localName==tag:
365 class _rml_draw(object):
366 def __init__(self, node, styles, images={}, path='.'):
373 def render(self, canvas, doc):
375 cnv = _rml_canvas(canvas, doc, self.styles, images=self.images, path=self.path)
376 cnv.render(self.node)
377 canvas.restoreState()
379 class _rml_flowable(object):
380 def __init__(self, doc, images={}, path='.'):
382 self.styles = doc.styles
386 def _textual(self, node):
388 for n in node.childNodes:
389 if n.nodeType == node.ELEMENT_NODE:
390 if n.localName == 'getName':
391 newNode = self.doc.dom.createTextNode(self.styles.names.get(n.getAttribute('id'),'Unknown name'))
392 node.insertBefore(newNode, n)
394 elif n.localName == 'pageNumber':
395 rc += '<pageNumber/>' # TODO: change this !
397 #CHECKME: I wonder if this is useful since we don't stock the result. Maybe for the getName tag?
400 elif n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE):
401 rc += str2xml(n.data)
402 return rc.encode(encoding, 'replace')
404 def _table(self, node):
409 childs = _child_get(node,'tr')
414 for td in _child_get(tr, 'td'):
416 for n in td.childNodes:
417 if n.nodeType==node.ELEMENT_NODE:
418 flow.append( self._flowable(n) )
420 flow = self._textual(td)
422 if len(data2)>length:
425 while len(ab)<length:
427 while len(data2)<length:
430 if node.hasAttribute('colWidths'):
431 assert length == len(node.getAttribute('colWidths').split(','))
432 colwidths = [utils.unit_get(f.strip()) for f in node.getAttribute('colWidths').split(',')]
433 if node.hasAttribute('rowHeights'):
434 rowheights = [utils.unit_get(f.strip()) for f in node.getAttribute('rowHeights').split(',')]
435 table = platypus.Table(data = data, colWidths=colwidths, rowHeights=rowheights, **(utils.attr_get(node, ['splitByRow'] ,{'repeatRows':'int','repeatCols':'int'})))
436 if node.hasAttribute('style'):
437 table.setStyle(self.styles.table_styles[node.getAttribute('style')])
440 def _illustration(self, node):
441 class Illustration(platypus.flowables.Flowable):
442 def __init__(self, node, styles, self2):
445 self.width = utils.unit_get(node.getAttribute('width'))
446 self.height = utils.unit_get(node.getAttribute('height'))
448 def wrap(self, *args):
449 return (self.width, self.height)
452 drw = _rml_draw(self.node, self.styles, images=self.self2.images, path=self.self2.path)
453 drw.render(self.canv, None)
454 return Illustration(node, self.styles, self)
456 def _flowable(self, node):
457 if node.localName=='para':
458 style = self.styles.para_style_get(node)
459 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
460 elif node.localName=='barCode':
462 from reportlab.extensions.barcode import code128
463 from reportlab.extensions.barcode import code39
464 from reportlab.extensions.barcode import code93
465 from reportlab.extensions.barcode import common
466 from reportlab.extensions.barcode import fourstate
467 from reportlab.extensions.barcode import usps
469 print 'Warning: Reportlab barcode extension not installed !'
471 args = utils.attr_get(node, [], {'ratio':'float','xdim':'unit','height':'unit','checksum':'bool','quiet':'bool'})
473 'codabar': lambda x: common.Codabar(x, **args),
474 'code11': lambda x: common.Code11(x, **args),
475 'code128': lambda x: code128.Code128(x, **args),
476 'standard39': lambda x: code39.Standard39(x, **args),
477 'standard93': lambda x: code93.Standard93(x, **args),
478 'i2of5': lambda x: common.I2of5(x, **args),
479 'extended39': lambda x: code39.Extended39(x, **args),
480 'extended93': lambda x: code93.Extended93(x, **args),
481 'msi': lambda x: common.MSI(x, **args),
482 'fim': lambda x: usps.FIM(x, **args),
483 'postnet': lambda x: usps.POSTNET(x, **args),
486 if node.hasAttribute('code'):
487 code = node.getAttribute('code').lower()
488 return codes[code](self._textual(node))
489 elif node.localName=='name':
490 self.styles.names[ node.getAttribute('id')] = node.getAttribute('value')
492 elif node.localName=='xpre':
493 style = self.styles.para_style_get(node)
494 return platypus.XPreformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int','frags':'int'})))
495 elif node.localName=='pre':
496 style = self.styles.para_style_get(node)
497 return platypus.Preformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int'})))
498 elif node.localName=='illustration':
499 return self._illustration(node)
500 elif node.localName=='blockTable':
501 return self._table(node)
502 elif node.localName=='title':
503 styles = reportlab.lib.styles.getSampleStyleSheet()
504 style = styles['Title']
505 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
506 elif node.localName=='h1':
507 styles = reportlab.lib.styles.getSampleStyleSheet()
508 style = styles['Heading1']
509 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
510 elif node.localName=='h2':
511 styles = reportlab.lib.styles.getSampleStyleSheet()
512 style = styles['Heading2']
513 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
514 elif node.localName=='h3':
515 styles = reportlab.lib.styles.getSampleStyleSheet()
516 style = styles['Heading3']
517 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
518 elif node.localName=='image':
519 if not node.hasAttribute('file'):
520 name = '/tmp/image_%d.jpg' % (int(node.getAttribute('name')),)
521 file(name,'wb+').write( self.doc.images[node.getAttribute('name')].read() )
522 return platypus.Image(name, mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height'])))
524 return platypus.Image(node.getAttribute('file'), mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height'])))
526 from reportlab.lib.utils import ImageReader
527 name = str(node.getAttribute('file'))
528 img = ImageReader(name)
529 (sx,sy) = img.getSize()
532 for tag in ('width','height'):
533 if node.hasAttribute(tag):
534 args[tag] = utils.unit_get(node.getAttribute(tag))
535 if ('width' in args) and (not 'height' in args):
536 args['height'] = sy * args['width'] / sx
537 elif ('height' in args) and (not 'width' in args):
538 args['width'] = sx * args['height'] / sy
539 elif ('width' in args) and ('height' in args):
540 if (float(args['width'])/args['height'])>(float(sx)>sy):
541 args['width'] = sx * args['height'] / sy
543 args['height'] = sy * args['width'] / sx
544 return platypus.Image(name, mask=(250,255,250,255,250,255), **args)
545 elif node.localName=='spacer':
546 if node.hasAttribute('width'):
547 width = utils.unit_get(node.getAttribute('width'))
549 width = utils.unit_get('1cm')
550 length = utils.unit_get(node.getAttribute('length'))
551 return platypus.Spacer(width=width, height=length)
552 elif node.localName=='section':
553 return self.render(node)
554 elif node.localName in ('pageBreak', 'nextPage'):
555 return platypus.PageBreak()
556 elif node.localName=='condPageBreak':
557 return platypus.CondPageBreak(**(utils.attr_get(node, ['height'])))
558 elif node.localName=='setNextTemplate':
559 return platypus.NextPageTemplate(str(node.getAttribute('name')))
560 elif node.localName=='nextFrame':
561 return platypus.CondPageBreak(1000) # TODO: change the 1000 !
562 elif node.localName == 'currentFrame':
563 #return platypus.CondPageBreak(1000) # TODO: change the 1000 !
564 from reportlab.platypus.doctemplate import CurrentFrameFlowable
565 return CurrentFrameFlowable(str(node.getAttribute('name')))
566 elif node.localName == 'frameEnd':
568 return EndFrameFlowable()
570 sys.stderr.write('Warning: flowable not yet implemented: %s !\n' % (node.localName,))
573 def render(self, node_story):
575 node = node_story.firstChild
577 if node.nodeType == node.ELEMENT_NODE:
578 flow = self._flowable(node)
580 if type(flow) == type([]):
584 node = node.nextSibling
587 from reportlab.platypus.doctemplate import ActionFlowable
589 class EndFrameFlowable(ActionFlowable):
590 def __init__(self,resume=0):
591 ActionFlowable.__init__(self,('frameEnd',resume))
593 class TinyDocTemplate(platypus.BaseDocTemplate):
595 def handle_pageBegin(self):
596 self.page = self.page + 1
597 self.pageTemplate.beforeDrawPage(self.canv,self)
598 self.pageTemplate.checkPageSize(self.canv,self)
599 self.pageTemplate.onPage(self.canv,self)
600 for f in self.pageTemplate.frames: f._reset()
602 #keep a count of flowables added to this page. zero indicates bad stuff
603 self._curPageFlowableCount = 0
604 if hasattr(self,'_nextFrameIndex'):
605 del self._nextFrameIndex
606 for f in self.pageTemplate.frames:
610 self.handle_frameBegin()
612 class _rml_template(object):
613 def __init__(self, out, node, doc, images={}, path='.'):
616 if not node.hasAttribute('pageSize'):
617 pageSize = (utils.unit_get('21cm'), utils.unit_get('29.7cm'))
619 ps = map(lambda x:x.strip(), node.getAttribute('pageSize').replace(')', '').replace('(', '').split(','))
620 pageSize = ( utils.unit_get(ps[0]),utils.unit_get(ps[1]) )
621 cm = reportlab.lib.units.cm
622 self.doc_tmpl = TinyDocTemplate(out, pagesize=pageSize, **utils.attr_get(node, ['leftMargin','rightMargin','topMargin','bottomMargin'], {'allowSplitting':'int','showBoundary':'bool','title':'str','author':'str'}))
623 self.page_templates = []
624 self.styles = doc.styles
626 pts = node.getElementsByTagName('pageTemplate')
629 for frame_el in pt.getElementsByTagName('frame'):
630 frame = platypus.Frame( **(utils.attr_get(frame_el, ['x1','y1', 'width','height', 'leftPadding', 'rightPadding', 'bottomPadding', 'topPadding'], {'id':'str', 'showBoundary':'bool'})) )
631 frames.append( frame )
632 gr = pt.getElementsByTagName('pageGraphics')
634 drw = _rml_draw(gr[0], self.doc, images=images)
635 self.page_templates.append( platypus.PageTemplate(frames=frames, onPage=drw.render, **utils.attr_get(pt, [], {'id':'str'}) ))
637 self.page_templates.append( platypus.PageTemplate(frames=frames, **utils.attr_get(pt, [], {'id':'str'}) ))
638 self.doc_tmpl.addPageTemplates(self.page_templates)
640 def render(self, node_stories):
642 r = _rml_flowable(self.doc,images=self.images, path=self.path)
643 for node_story in node_stories:
644 fis += r.render(node_story)
645 fis.append(platypus.PageBreak())
646 self.doc_tmpl.build(fis)
648 def parseString(data, fout=None, images={}, path='.'):
649 r = _rml_doc(data, images, path)
656 fp = StringIO.StringIO()
661 print 'Usage: trml2pdf input.rml >output.pdf'
662 print 'Render the standard input (RML) and output a PDF file'
665 if __name__=="__main__":
667 if sys.argv[1]=='--help':
669 print parseString(file(sys.argv[1], 'r').read()),
671 print 'Usage: trml2pdf input.rml >output.pdf'
672 print 'Try \'trml2pdf --help\' for more information.'