1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 import sys
24
25 import cStringIO as StringIO
26 import xml.dom.minidom
27 import copy
28 import locale
29
30 import reportlab
31 from reportlab.pdfgen import canvas
32 from reportlab import platypus
33
34 import utils
35 import color
36
37
38
39
40 try:
41 from zephir.config import charset
42 encoding = charset
43 except:
44 encoding = locale.getpreferredencoding()
45
46
48 clds = []
49 for n in node.childNodes:
50 if (n.nodeType==n.ELEMENT_NODE) and (n.localName==childs):
51 clds.append(n)
52 return clds
53
56 self.styles = {}
57 self.names = {}
58 self.table_styles = {}
59 for node in nodes:
60 for style in node.getElementsByTagName('blockTableStyle'):
61 self.table_styles[style.getAttribute('id')] = self._table_style_get(style)
62 for style in node.getElementsByTagName('paraStyle'):
63 self.styles[style.getAttribute('name')] = self._para_style_get(style)
64 for variable in node.getElementsByTagName('initialize'):
65 for name in variable.getElementsByTagName('name'):
66 self.names[ name.getAttribute('id')] = name.getAttribute('value')
67
69 for attr in ['textColor', 'backColor', 'bulletColor']:
70 if node.hasAttribute(attr):
71 style.__dict__[attr] = color.get(node.getAttribute(attr))
72 for attr in ['fontName', 'bulletFontName', 'bulletText']:
73 if node.hasAttribute(attr):
74 style.__dict__[attr] = node.getAttribute(attr)
75 for attr in ['fontSize', 'leftIndent', 'rightIndent', 'spaceBefore', 'spaceAfter', 'firstLineIndent', 'bulletIndent', 'bulletFontSize', 'leading']:
76 if node.hasAttribute(attr):
77 style.__dict__[attr] = utils.unit_get(node.getAttribute(attr))
78 if node.hasAttribute('alignment'):
79 align = {
80 'right':reportlab.lib.enums.TA_RIGHT,
81 'center':reportlab.lib.enums.TA_CENTER,
82 'justify':reportlab.lib.enums.TA_JUSTIFY
83 }
84 style.alignment = align.get(node.getAttribute('alignment').lower(), reportlab.lib.enums.TA_LEFT)
85 return style
86
88 styles = []
89 for node in style_node.childNodes:
90 if node.nodeType==node.ELEMENT_NODE:
91 start = utils.tuple_int_get(node, 'start', (0,0) )
92 stop = utils.tuple_int_get(node, 'stop', (-1,-1) )
93 if node.localName=='blockValign':
94 styles.append(('VALIGN', start, stop, str(node.getAttribute('value'))))
95 elif node.localName=='blockFont':
96 styles.append(('FONT', start, stop, str(node.getAttribute('name'))))
97 elif node.localName=='blockTextColor':
98 styles.append(('TEXTCOLOR', start, stop, color.get(str(node.getAttribute('colorName')))))
99 elif node.localName=='blockLeading':
100 styles.append(('LEADING', start, stop, utils.unit_get(node.getAttribute('length'))))
101 elif node.localName=='blockAlignment':
102 styles.append(('ALIGNMENT', start, stop, str(node.getAttribute('value'))))
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
119 thick = 1
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)
124
126 styles = reportlab.lib.styles.getSampleStyleSheet()
127 style = copy.deepcopy(styles["Normal"])
128 self._para_style_update(style, node)
129 return style
130
132 style = False
133 if node.hasAttribute('style'):
134 if node.getAttribute('style') in self.styles:
135 style = copy.deepcopy(self.styles[node.getAttribute('style')])
136 else:
137 sys.stderr.write('Warning: style not found, %s - setting default!\n' % (node.getAttribute('style'),) )
138 if not style:
139 styles = reportlab.lib.styles.getSampleStyleSheet()
140 style = copy.deepcopy(styles['Normal'])
141 return self._para_style_update(style, node)
142
145 self.dom = xml.dom.minidom.parseString(data)
146 self.filename = self.dom.documentElement.getAttribute('filename')
147
149 from reportlab.lib.fonts import addMapping
150 from reportlab.pdfbase import pdfmetrics
151 from reportlab.pdfbase.ttfonts import TTFont
152
153 for node in els:
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)
159 addMapping(name, 0, 1, name)
160 addMapping(name, 1, 0, name)
161 addMapping(name, 1, 1, name)
162
164 el = self.dom.documentElement.getElementsByTagName('docinit')
165 if el:
166 self.docinit(el)
167
168 el = self.dom.documentElement.getElementsByTagName('stylesheet')
169 self.styles = _rml_styles(el)
170
171 el = self.dom.documentElement.getElementsByTagName('template')
172 if len(el):
173 pt_obj = _rml_template(out, el[0], self)
174 pt_obj.render(self.dom.documentElement.getElementsByTagName('story')[0])
175 else:
176 self.canvas = canvas.Canvas(out)
177 pd = self.dom.documentElement.getElementsByTagName('pageDrawing')[0]
178 pd_obj = _rml_canvas(self.canvas, None, self)
179 pd_obj.render(pd)
180 self.canvas.showPage()
181 self.canvas.save()
182
184 - def __init__(self, canvas, doc_tmpl=None, doc=None):
185 self.canvas = canvas
186 self.styles = doc.styles
187 self.doc_tmpl = doc_tmpl
188 self.doc = doc
189
190 - def _textual(self, node):
191 rc = ''
192 for n in node.childNodes:
193 if n.nodeType == n.ELEMENT_NODE:
194 if n.localName=='pageNumber':
195 rc += str(self.canvas.getPageNumber())
196 elif (n.nodeType == node.CDATA_SECTION_NODE):
197 rc += n.data
198 elif (n.nodeType == node.TEXT_NODE):
199 rc += n.data
200 return rc.encode(encoding)
201
203 self.canvas.drawString(text=self._textual(node), **utils.attr_get(node, ['x','y']))
205 self.canvas.drawCentredString(text=self._textual(node), **utils.attr_get(node, ['x','y']))
207 self.canvas.drawRightString(text=self._textual(node), **utils.attr_get(node, ['x','y']))
209 if node.hasAttribute('round'):
210 self.canvas.roundRect(radius=utils.unit_get(node.getAttribute('round')), **utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'}))
211 else:
212 self.canvas.rect(**utils.attr_get(node, ['x','y','width','height'], {'fill':'bool','stroke':'bool'}))
220 line_str = utils.text_get(node).split()
221 lines = []
222 while len(line_str)>7:
223 self.canvas.bezier(*[utils.unit_get(l) for l in line_str[0:8]])
224 line_str = line_str[8:]
226 line_str = utils.text_get(node).split()
227 lines = []
228 while len(line_str)>3:
229 lines.append([utils.unit_get(l) for l in line_str[0:4]])
230 line_str = line_str[4:]
231 self.canvas.lines(lines)
233 xlist = [utils.unit_get(s) for s in node.getAttribute('xs').split(',')]
234 ylist = [utils.unit_get(s) for s in node.getAttribute('ys').split(',')]
235 self.canvas.grid(xlist, ylist)
237 dx = 0
238 dy = 0
239 if node.hasAttribute('dx'):
240 dx = utils.unit_get(node.getAttribute('dx'))
241 if node.hasAttribute('dy'):
242 dy = utils.unit_get(node.getAttribute('dy'))
243 self.canvas.translate(dx,dy)
244
247
249 flows = _rml_flowable(self.doc).render(node)
250 infos = utils.attr_get(node, ['x','y','width','height'])
251
252 infos['y']+=infos['height']
253 for flow in flows:
254 w,h = flow.wrap(infos['width'], infos['height'])
255 if w<=infos['width'] and h<=infos['height']:
256 infos['y']-=h
257 flow.drawOn(self.canvas,infos['x'],infos['y'])
258 infos['height']-=h
259 else:
260 raise ValueError, "Not enough space"
261
263 ljoin = {'round':1, 'mitered':0, 'bevelled':2}
264 lcap = {'default':0, 'round':1, 'square':2}
265 if node.hasAttribute('width'):
266 self.canvas.setLineWidth(utils.unit_get(node.getAttribute('width')))
267 if node.hasAttribute('join'):
268 self.canvas.setLineJoin(ljoin[node.getAttribute('join')])
269 if node.hasAttribute('cap'):
270 self.canvas.setLineCap(lcap[node.getAttribute('cap')])
271 if node.hasAttribute('miterLimit'):
272 self.canvas.setDash(utils.unit_get(node.getAttribute('miterLimit')))
273 if node.hasAttribute('dash'):
274 dashes = node.getAttribute('dash').split(',')
275 for x in range(len(dashes)):
276 dashes[x]=utils.unit_get(dashes[x])
277 self.canvas.setDash(node.getAttribute('dash').split(','))
278
280 import urllib
281 from reportlab.lib.utils import ImageReader
282 u = urllib.urlopen(str(node.getAttribute('file')))
283 s = StringIO.StringIO()
284 s.write(u.read())
285 s.seek(0)
286 img = ImageReader(s)
287 (sx,sy) = img.getSize()
288
289 args = {}
290 for tag in ('width','height','x','y'):
291 if node.hasAttribute(tag):
292 args[tag] = utils.unit_get(node.getAttribute(tag))
293 if ('width' in args) and (not 'height' in args):
294 args['height'] = sy * args['width'] / sx
295 elif ('height' in args) and (not 'width' in args):
296 args['width'] = sx * args['height'] / sy
297 elif ('width' in args) and ('height' in args):
298 if (float(args['width'])/args['height'])>(float(sx)>sy):
299 args['width'] = sx * args['height'] / sy
300 else:
301 args['height'] = sy * args['width'] / sx
302 self.canvas.drawImage(img, **args)
303
305 self.path = self.canvas.beginPath()
306 self.path.moveTo(**utils.attr_get(node, ['x','y']))
307 for n in node.childNodes:
308 if n.nodeType == node.ELEMENT_NODE:
309 if n.localName=='moveto':
310 vals = utils.text_get(n).split()
311 self.path.moveTo(utils.unit_get(vals[0]), utils.unit_get(vals[1]))
312 elif n.localName=='curvesto':
313 vals = utils.text_get(n).split()
314 while len(vals)>5:
315 pos=[]
316 while len(pos)<6:
317 pos.append(utils.unit_get(vals.pop(0)))
318 self.path.curveTo(*pos)
319 elif (n.nodeType == node.TEXT_NODE):
320 data = n.data.split()
321 while len(data)>1:
322 x = utils.unit_get(data.pop(0))
323 y = utils.unit_get(data.pop(0))
324 self.path.lineTo(x,y)
325 if (not node.hasAttribute('close')) or utils.bool_get(node.getAttribute('close')):
326 self.path.close()
327 self.canvas.drawPath(self.path, **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'}))
328
330 tags = {
331 'drawCentredString': self._drawCenteredString,
332 'drawRightString': self._drawRightString,
333 'drawString': self._drawString,
334 'rect': self._rect,
335 'ellipse': self._ellipse,
336 'lines': self._lines,
337 'grid': self._grid,
338 'curves': self._curves,
339 'fill': lambda node: self.canvas.setFillColor(color.get(node.getAttribute('color'))),
340 'stroke': lambda node: self.canvas.setStrokeColor(color.get(node.getAttribute('color'))),
341 'setFont': lambda node: self.canvas.setFont(node.getAttribute('name'), utils.unit_get(node.getAttribute('size'))),
342 'place': self._place,
343 'circle': self._circle,
344 'lineMode': self._line_mode,
345 'path': self._path,
346 'rotate': lambda node: self.canvas.rotate(float(node.getAttribute('degrees'))),
347 'translate': self._translate,
348 'image': self._image
349 }
350 for nd in node.childNodes:
351 if nd.nodeType==nd.ELEMENT_NODE:
352 for tag in tags:
353 if nd.localName==tag:
354 tags[tag](nd)
355 break
356
359 self.node = node
360 self.styles = styles
361 self.canvas = None
362
363 - def render(self, canvas, doc):
364 canvas.saveState()
365 cnv = _rml_canvas(canvas, doc, self.styles)
366 cnv.render(self.node)
367 canvas.restoreState()
368
371 self.doc = doc
372 self.styles = doc.styles
373
374 - def _textual(self, node):
375 rc = ''
376 for n in node.childNodes:
377 if n.nodeType == node.ELEMENT_NODE:
378 if n.localName=='getName':
379 newNode = self.doc.dom.createTextNode(self.styles.names.get(n.getAttribute('id'),'Unknown name'))
380 node.insertBefore(newNode, n)
381 node.removeChild(n)
382 if n.localName=='pageNumber':
383 rc+='<pageNumber/>'
384 else:
385 self._textual(n)
386 rc += n.toxml()
387 elif (n.nodeType == node.CDATA_SECTION_NODE):
388 rc += n.data
389 elif (n.nodeType == node.TEXT_NODE):
390 rc += n.data
391 return rc.encode(encoding)
392
394 length = 0
395 colwidths = None
396 rowheights = None
397 data = []
398 for tr in _child_get(node,'tr'):
399 data2 = []
400 for td in _child_get(tr, 'td'):
401 flow = []
402 for n in td.childNodes:
403 if n.nodeType==node.ELEMENT_NODE:
404 flow.append( self._flowable(n) )
405 if not len(flow):
406 flow = self._textual(td)
407 data2.append( flow )
408 if len(data2)>length:
409 length=len(data2)
410 for ab in data:
411 while len(ab)<length:
412 ab.append('')
413 while len(data2)<length:
414 data2.append('')
415 data.append( data2 )
416 if node.hasAttribute('colWidths'):
417 assert length == len(node.getAttribute('colWidths').split(','))
418 colwidths = [utils.unit_get(f.strip()) for f in node.getAttribute('colWidths').split(',')]
419 if node.hasAttribute('rowHeights'):
420 rowheights = [utils.unit_get(f.strip()) for f in node.getAttribute('rowHeights').split(',')]
421 table = platypus.Table(data = data, colWidths=colwidths, rowHeights=rowheights, **(utils.attr_get(node, ['splitByRow'] ,{'repeatRows':'int','repeatCols':'int'})))
422 if node.hasAttribute('style'):
423 table.setStyle(self.styles.table_styles[node.getAttribute('style')])
424 return table
425
427 class Illustration(platypus.flowables.Flowable):
428 def __init__(self, node, styles):
429 self.node = node
430 self.styles = styles
431 self.width = utils.unit_get(node.getAttribute('width'))
432 self.height = utils.unit_get(node.getAttribute('height'))
433 def wrap(self, *args):
434 return (self.width, self.height)
435 def draw(self):
436 canvas = self.canv
437 drw = _rml_draw(self.node, self.styles)
438 drw.render(self.canv, None)
439 return Illustration(node, self.styles)
440
442 if node.localName=='para':
443 style = self.styles.para_style_get(node)
444 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
445 elif node.localName=='name':
446 self.styles.names[ node.getAttribute('id')] = node.getAttribute('value')
447 return None
448 elif node.localName=='xpre':
449 style = self.styles.para_style_get(node)
450 return platypus.XPreformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int','frags':'int'})))
451 elif node.localName=='pre':
452 style = self.styles.para_style_get(node)
453 return platypus.Preformatted(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str','dedent':'int'})))
454 elif node.localName=='illustration':
455 return self._illustration(node)
456 elif node.localName=='blockTable':
457 return self._table(node)
458 elif node.localName=='title':
459 styles = reportlab.lib.styles.getSampleStyleSheet()
460 style = styles['Title']
461 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
462 elif node.localName=='h1':
463 styles = reportlab.lib.styles.getSampleStyleSheet()
464 style = styles['Heading1']
465 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
466 elif node.localName=='h2':
467 styles = reportlab.lib.styles.getSampleStyleSheet()
468 style = styles['Heading2']
469 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
470 elif node.localName=='h3':
471 styles = reportlab.lib.styles.getSampleStyleSheet()
472 style = styles['Heading3']
473 return platypus.Paragraph(self._textual(node), style, **(utils.attr_get(node, [], {'bulletText':'str'})))
474 elif node.localName=='image':
475 return platypus.Image(node.getAttribute('file'), mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height'])))
476 elif node.localName=='spacer':
477 if node.hasAttribute('width'):
478 width = utils.unit_get(node.getAttribute('width'))
479 else:
480 width = utils.unit_get('1cm')
481 length = utils.unit_get(node.getAttribute('length'))
482 return platypus.Spacer(width=width, height=length)
483 elif node.localName=='pageBreak':
484 return platypus.PageBreak()
485 elif node.localName=='condPageBreak':
486 return platypus.CondPageBreak(**(utils.attr_get(node, ['height'])))
487 elif node.localName=='setNextTemplate':
488 return platypus.NextPageTemplate(str(node.getAttribute('name')))
489 elif node.localName=='nextFrame':
490 return platypus.CondPageBreak(1000)
491 else:
492 sys.stderr.write('Warning: flowable not yet implemented: %s !\n' % (node.localName,))
493 return None
494
495 - def render(self, node_story):
496 story = []
497 node = node_story.firstChild
498 while node:
499 if node.nodeType == node.ELEMENT_NODE:
500 flow = self._flowable(node)
501 if flow:
502 story.append(flow)
503 node = node.nextSibling
504 return story
505
508 if not node.hasAttribute('pageSize'):
509 pageSize = (utils.unit_get('21cm'), utils.unit_get('29.7cm'))
510 else:
511 ps = map(lambda x:x.strip(), node.getAttribute('pageSize').replace(')', '').replace('(', '').split(','))
512 pageSize = ( utils.unit_get(ps[0]),utils.unit_get(ps[1]) )
513 cm = reportlab.lib.units.cm
514 self.doc_tmpl = platypus.BaseDocTemplate(out, pagesize=pageSize, **utils.attr_get(node, ['leftMargin','rightMargin','topMargin','bottomMargin'], {'allowSplitting':'int','showBoundary':'bool','title':'str','author':'str'}))
515 self.page_templates = []
516 self.styles = doc.styles
517 self.doc = doc
518 pts = node.getElementsByTagName('pageTemplate')
519 for pt in pts:
520 frames = []
521 for frame_el in pt.getElementsByTagName('frame'):
522 frame = platypus.Frame( **(utils.attr_get(frame_el, ['x1','y1', 'width','height', 'leftPadding', 'rightPadding', 'bottomPadding', 'topPadding'], {'id':'text', 'showBoundary':'bool'})) )
523 frames.append( frame )
524 gr = pt.getElementsByTagName('pageGraphics')
525 if len(gr):
526 drw = _rml_draw(gr[0], self.doc)
527 self.page_templates.append( platypus.PageTemplate(frames=frames, onPage=drw.render, **utils.attr_get(pt, [], {'id':'str'}) ))
528 else:
529 self.page_templates.append( platypus.PageTemplate(frames=frames, **utils.attr_get(pt, [], {'id':'str'}) ))
530 self.doc_tmpl.addPageTemplates(self.page_templates)
531
532 - def render(self, node_story):
533 r = _rml_flowable(self.doc)
534 fis = r.render(node_story)
535 self.doc_tmpl.build(fis)
536
538 r = _rml_doc(data)
539 if fout:
540 fp = file(fout,'wb')
541 r.render(fp)
542 fp.close()
543 return fout
544 else:
545 fp = StringIO.StringIO()
546 r.render(fp)
547 return fp.getvalue()
548
550 print 'Usage: trml2pdf input.rml >output.pdf'
551 print 'Render the standard input (RML) and output a PDF file'
552 sys.exit(0)
553
554 if __name__=="__main__":
555 if len(sys.argv)>1:
556 if sys.argv[1]=='--help':
557 trml2pdf_help()
558 print parseString(file(sys.argv[1], 'r').read()),
559 else:
560 print 'Usage: trml2pdf input.rml >output.pdf'
561 print 'Try \'trml2pdf --help\' for more information.'
562