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 s = self.images[node.getAttribute('name')]
306 if node.getAttribute('file') in self.images:
307 s = StringIO.StringIO(self.images[node.getAttribute('file')])
310 u = urllib.urlopen(str(node.getAttribute('file')))
311 s = StringIO.StringIO(u.read())
313 u = file(os.path.join(self.path,str(node.getAttribute('file'))), 'rb')
314 s = StringIO.StringIO(u.read())
316 (sx,sy) = img.getSize()
319 for tag in ('width','height','x','y'):
320 if node.hasAttribute(tag):
321 args[tag] = utils.unit_get(node.getAttribute(tag))
322 if ('width' in args) and (not 'height' in args):
323 args['height'] = sy * args['width'] / sx
324 elif ('height' in args) and (not 'width' in args):
325 args['width'] = sx * args['height'] / sy
326 elif ('width' in args) and ('height' in args):
327 if (float(args['width'])/args['height'])>(float(sx)>sy):
328 args['width'] = sx * args['height'] / sy
330 args['height'] = sy * args['width'] / sx
331 self.canvas.drawImage(img, **args)
333 def _path(self, node):
334 self.path = self.canvas.beginPath()
335 self.path.moveTo(**utils.attr_get(node, ['x','y']))
336 for n in node.childNodes:
337 if n.nodeType == node.ELEMENT_NODE:
338 if n.localName=='moveto':
339 vals = utils.text_get(n).split()
340 self.path.moveTo(utils.unit_get(vals[0]), utils.unit_get(vals[1]))
341 elif n.localName=='curvesto':
342 vals = utils.text_get(n).split()
346 pos.append(utils.unit_get(vals.pop(0)))
347 self.path.curveTo(*pos)
348 elif (n.nodeType == node.TEXT_NODE):
349 data = n.data.split() # Not sure if I must merge all TEXT_NODE ?
351 x = utils.unit_get(data.pop(0))
352 y = utils.unit_get(data.pop(0))
353 self.path.lineTo(x,y)
354 if (not node.hasAttribute('close')) or utils.bool_get(node.getAttribute('close')):
356 self.canvas.drawPath(self.path, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'}))
358 def render(self, node):
360 'drawCentredString': self._drawCenteredString,
361 'drawRightString': self._drawRightString,
362 'drawString': self._drawString,
364 'ellipse': self._ellipse,
365 'lines': self._lines,
367 'curves': self._curves,
368 'fill': lambda node: self.canvas.setFillColor(color.get(node.getAttribute('color'))),
369 'stroke': lambda node: self.canvas.setStrokeColor(color.get(node.getAttribute('color'))),
370 'setFont': lambda node: self.canvas.setFont(node.getAttribute('name'), utils.unit_get(node.getAttribute('size'))),
371 'place': self._place,
372 'circle': self._circle,
373 'lineMode': self._line_mode,
375 'rotate': lambda node: self.canvas.rotate(float(node.getAttribute('degrees'))),
376 'translate': self._translate,
379 for nd in node.childNodes:
380 if nd.nodeType==nd.ELEMENT_NODE:
382 if nd.localName==tag:
386 class _rml_draw(object):
387 def __init__(self, node, styles, images={}, path='.'):
394 def render(self, canvas, doc):
396 cnv = _rml_canvas(canvas, doc, self.styles, images=self.images, path=self.path)
397 cnv.render(self.node)
398 canvas.restoreState()
400 class _rml_flowable(object):
401 def __init__(self, doc, images={}, path='.'):
403 self.styles = doc.styles
407 def _textual(self, node):
409 for n in node.childNodes:
410 if n.nodeType == node.ELEMENT_NODE:
411 if n.localName == 'getName':
412 newNode = self.doc.dom.createTextNode(self.styles.names.get(n.getAttribute('id'),'Unknown name'))
413 node.insertBefore(newNode, n)
415 elif n.localName == 'pageNumber':
416 rc += '<pageNumber/>' # TODO: change this !
418 #CHECKME: I wonder if this is useful since we don't stock the result. Maybe for the getName tag?
421 elif n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE):
422 rc += str2xml(n.data)
423 return rc.encode(encoding, 'replace')
425 def _table(self, node):
430 childs = _child_get(node,'tr')
435 for td in _child_get(tr, 'td'):
437 for n in td.childNodes:
438 if n.nodeType==node.ELEMENT_NODE:
439 flow.append( self._flowable(n) )
441 flow = self._textual(td)
443 if len(data2)>length:
446 while len(ab)<length:
448 while len(data2)<length:
451 if node.hasAttribute('colWidths'):
452 assert length == len(node.getAttribute('colWidths').split(','))
453 colwidths = [utils.unit_get(f.strip()) for f in node.getAttribute('colWidths').split(',')]
454 if node.hasAttribute('rowHeights'):
455 rowheights = [utils.unit_get(f.strip()) for f in node.getAttribute('rowHeights').split(',')]
456 table = platypus.Table(data = data, colWidths=colwidths, rowHeights=rowheights, **(utils.attr_get(node, ['splitByRow'] ,{'repeatRows':'int','repeatCols':'int'})))
457 if node.hasAttribute('style'):
458 table.setStyle(self.styles.table_styles[node.getAttribute('style')])
461 def _illustration(self, node):
462 class Illustration(platypus.flowables.Flowable):
463 def __init__(self, node, styles, self2):
466 self.width = utils.unit_get(node.getAttribute('width'))
467 self.height = utils.unit_get(node.getAttribute('height'))
469 def wrap(self, *args):
470 return (self.width, self.height)
473 drw = _rml_draw(self.node, self.styles, images=self.self2.images, path=self.self2.path)
474 drw.render(self.canv, None)
475 return Illustration(node, self.styles, self)
477 def _textual_image(self, node):
480 for n in node.childNodes:
481 if n.nodeType in (node.CDATA_SECTION_NODE, node.TEXT_NODE):
483 return base64.decodestring(rc)
485 def _flowable(self, node):
486 if node.localName=='para':
487 style = self.styles.para_style_get(node)
488 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
489 elif node.localName=='barCode':
491 from reportlab.graphics.barcode import code128
492 from reportlab.graphics.barcode import code39
493 from reportlab.graphics.barcode import code93
494 from reportlab.graphics.barcode import common
495 from reportlab.graphics.barcode import fourstate
496 from reportlab.graphics.barcode import usps
498 print 'Warning: Reportlab barcode extension not installed !'
500 args = utils.attr_get(node, [], {'ratio':'float','xdim':'unit','height':'unit','checksum':'bool','quiet':'bool'})
502 'codabar': lambda x: common.Codabar(x, **args),
503 'code11': lambda x: common.Code11(x, **args),
504 'code128': lambda x: code128.Code128(x, **args),
505 'standard39': lambda x: code39.Standard39(x, **args),
506 'standard93': lambda x: code93.Standard93(x, **args),
507 'i2of5': lambda x: common.I2of5(x, **args),
508 'extended39': lambda x: code39.Extended39(x, **args),
509 'extended93': lambda x: code93.Extended93(x, **args),
510 'msi': lambda x: common.MSI(x, **args),
511 'fim': lambda x: usps.FIM(x, **args),
512 'postnet': lambda x: usps.POSTNET(x, **args),
515 if node.hasAttribute('code'):
516 code = node.getAttribute('code').lower()
517 return codes[code](self._textual(node))
518 elif node.localName=='name':
519 self.styles.names[ node.getAttribute('id')] = node.getAttribute('value')
521 elif node.localName=='xpre':
522 style = self.styles.para_style_get(node)
523 return platypus.XPreformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int','frags':'int'})))
524 elif node.localName=='pre':
525 style = self.styles.para_style_get(node)
526 return platypus.Preformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int'})))
527 elif node.localName=='illustration':
528 return self._illustration(node)
529 elif node.localName=='blockTable':
530 return self._table(node)
531 elif node.localName=='title':
532 styles = reportlab.lib.styles.getSampleStyleSheet()
533 style = styles['Title']
534 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
535 elif node.localName=='h1':
536 styles = reportlab.lib.styles.getSampleStyleSheet()
537 style = styles['Heading1']
538 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
539 elif node.localName=='h2':
540 styles = reportlab.lib.styles.getSampleStyleSheet()
541 style = styles['Heading2']
542 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
543 elif node.localName=='h3':
544 styles = reportlab.lib.styles.getSampleStyleSheet()
545 style = styles['Heading3']
546 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
547 elif node.localName=='image':
548 if not node.hasAttribute('file'):
549 if node.hasAttribute('name'):
550 image_data = self.doc.images[node.getAttribute('name')].read()
553 image_data = base64.decodestring(node.firstChild.nodeValue)
554 image = StringIO.StringIO(image_data)
555 return platypus.Image(image, mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height'])))
557 return platypus.Image(node.getAttribute('file'), mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height'])))
559 from reportlab.lib.utils import ImageReader
560 name = str(node.getAttribute('file'))
561 img = ImageReader(name)
562 (sx,sy) = img.getSize()
565 for tag in ('width','height'):
566 if node.hasAttribute(tag):
567 args[tag] = utils.unit_get(node.getAttribute(tag))
568 if ('width' in args) and (not 'height' in args):
569 args['height'] = sy * args['width'] / sx
570 elif ('height' in args) and (not 'width' in args):
571 args['width'] = sx * args['height'] / sy
572 elif ('width' in args) and ('height' in args):
573 if (float(args['width'])/args['height'])>(float(sx)>sy):
574 args['width'] = sx * args['height'] / sy
576 args['height'] = sy * args['width'] / sx
577 return platypus.Image(name, mask=(250,255,250,255,250,255), **args)
578 elif node.localName=='spacer':
579 if node.hasAttribute('width'):
580 width = utils.unit_get(node.getAttribute('width'))
582 width = utils.unit_get('1cm')
583 length = utils.unit_get(node.getAttribute('length'))
584 return platypus.Spacer(width=width, height=length)
585 elif node.localName=='section':
586 return self.render(node)
587 elif node.localName in ('pageBreak', 'nextPage'):
588 return platypus.PageBreak()
589 elif node.localName=='condPageBreak':
590 return platypus.CondPageBreak(**(utils.attr_get(node, ['height'])))
591 elif node.localName=='setNextTemplate':
592 return platypus.NextPageTemplate(str(node.getAttribute('name')))
593 elif node.localName=='nextFrame':
594 return platypus.CondPageBreak(1000) # TODO: change the 1000 !
595 elif node.localName == 'currentFrame':
596 #return platypus.CondPageBreak(1000) # TODO: change the 1000 !
597 from reportlab.platypus.doctemplate import CurrentFrameFlowable
598 return CurrentFrameFlowable(str(node.getAttribute('name')))
599 elif node.localName == 'frameEnd':
601 return EndFrameFlowable()
603 sys.stderr.write('Warning: flowable not yet implemented: %s !\n' % (node.localName,))
606 def render(self, node_story):
608 node = node_story.firstChild
610 if node.nodeType == node.ELEMENT_NODE:
611 flow = self._flowable(node)
613 if type(flow) == type([]):
617 node = node.nextSibling
620 from reportlab.platypus.doctemplate import ActionFlowable
622 class EndFrameFlowable(ActionFlowable):
623 def __init__(self,resume=0):
624 ActionFlowable.__init__(self,('frameEnd',resume))
626 class TinyDocTemplate(platypus.BaseDocTemplate):
628 def ___handle_pageBegin(self):
629 self.page = self.page + 1
630 self.pageTemplate.beforeDrawPage(self.canv,self)
631 self.pageTemplate.checkPageSize(self.canv,self)
632 self.pageTemplate.onPage(self.canv,self)
633 for f in self.pageTemplate.frames: f._reset()
635 #keep a count of flowables added to this page. zero indicates bad stuff
636 self._curPageFlowableCount = 0
637 if hasattr(self,'_nextFrameIndex'):
638 del self._nextFrameIndex
639 for f in self.pageTemplate.frames:
643 self.handle_frameBegin()
645 class _rml_template(object):
646 def __init__(self, out, node, doc, images={}, path='.'):
649 if not node.hasAttribute('pageSize'):
650 pageSize = (utils.unit_get('21cm'), utils.unit_get('29.7cm'))
652 ps = map(lambda x:x.strip(), node.getAttribute('pageSize').replace(')', '').replace('(', '').split(','))
653 pageSize = ( utils.unit_get(ps[0]),utils.unit_get(ps[1]) )
654 cm = reportlab.lib.units.cm
655 self.doc_tmpl = TinyDocTemplate(out, pagesize=pageSize, **utils.attr_get(node, ['leftMargin','rightMargin','topMargin','bottomMargin'], {'allowSplitting':'int','showBoundary':'bool','title':'str','author':'str'}))
656 self.page_templates = []
657 self.styles = doc.styles
659 pts = node.getElementsByTagName('pageTemplate')
662 for frame_el in pt.getElementsByTagName('frame'):
663 frame = platypus.Frame( **(utils.attr_get(frame_el, ['x1','y1', 'width','height', 'leftPadding', 'rightPadding', 'bottomPadding', 'topPadding'], {'id':'str', 'showBoundary':'bool'})) )
664 frames.append( frame )
665 gr = pt.getElementsByTagName('pageGraphics')
667 drw = _rml_draw(gr[0], self.doc, images=images)
668 self.page_templates.append( platypus.PageTemplate(frames=frames, onPage=drw.render, **utils.attr_get(pt, [], {'id':'str'}) ))
670 self.page_templates.append( platypus.PageTemplate(frames=frames, **utils.attr_get(pt, [], {'id':'str'}) ))
671 self.doc_tmpl.addPageTemplates(self.page_templates)
673 def render(self, node_stories):
675 r = _rml_flowable(self.doc,images=self.images, path=self.path)
676 for node_story in node_stories:
677 fis += r.render(node_story)
678 fis.append(platypus.PageBreak())
679 self.doc_tmpl.build(fis)
681 def parseString(data, fout=None, images={}, path='.'):
682 r = _rml_doc(data, images, path)
689 fp = StringIO.StringIO()
694 print 'Usage: trml2pdf input.rml >output.pdf'
695 print 'Render the standard input (RML) and output a PDF file'
698 if __name__=="__main__":
700 if sys.argv[1]=='--help':
702 print parseString(file(sys.argv[1], 'r').read()),
704 print 'Usage: trml2pdf input.rml >output.pdf'
705 print 'Try \'trml2pdf --help\' for more information.'