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