2
Parser for PythonPoint using the xmllib.py in the standard Python
3
distribution. Slow, but always present. We intend to add new parsers
4
as Python 2.x and the XML package spread in popularity and stabilise.
6
The parser has a getPresentation method; it is called from
10
import string, imp, sys, os, copy
11
from reportlab.lib.utils import SeqTypes
12
from reportlab.lib import xmllib
13
from reportlab.lib import colors
14
from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY
15
from reportlab.lib.utils import recursiveImport
16
from reportlab.tools.pythonpoint import pythonpoint
17
from reportlab.platypus import figures
20
def getModule(modulename,fromPath='reportlab.tools.pythonpoint.styles'):
21
"""Get a module containing style declarations.
24
reportlab/tools/pythonpoint/
25
reportlab/tools/pythonpoint/styles/
30
exec 'from reportlab.tools.pythonpoint import '+modulename
31
return eval(modulename)
34
exec 'from reportlab.tools.pythonpoint.styles import '+modulename
35
return eval(modulename)
37
exec 'import '+modulename
38
return eval(modulename)
41
class PPMLParser(xmllib.XMLParser):
43
#this defines the available attributes for all objects,
44
#and their default values. Although these don't have to
45
#be strings, the ones parsed from the XML do, so
46
#everything is a quoted string and the parser has to
47
#convert these to numbers where appropriate.
51
'function':'getParagraphStyles'
59
'leftmargin':'0', #this is ignored
60
'topmargin':'0', #this is ignored
61
'rightmargin':'0', #this is ignored
62
'bottommargin':'0', #this is ignored
67
'effectname':'None', # Split, Blinds, Box, Wipe, Dissolve, Glitter
68
'effectdirection':'0', # 0,90,180,270
69
'effectdimension':'H', # H or V - horizontal or vertical
70
'effectmotion':'I', # Inwards or Outwards
71
'effectduration':'1', #seconds,
72
'outlineentry':'None',
73
'outlinelevel':'0' # 1 is a child, 2 is a grandchild etc.
79
'effectdirection':'0',
80
'effectdimension':'H',
89
'effectdirection':'0',
90
'effectdimension':'H',
101
'effectdirection':'0',
102
'effectdimension':'H',
115
'effectdirection':'0',
116
'effectdimension':'H',
130
'effectdirection':'0',
131
'effectdimension':'H',
143
'effectdirection':'0',
144
'effectdimension':'H',
157
'effectdirection':'0',
158
'effectdimension':'H',
163
'points':'(0,0),(50,0),(25,25)',
169
'effectdirection':'0',
170
'effectdimension':'H',
178
'font':'Times-Roman',
182
'effectdirection':'0',
183
'effectdimension':'H',
196
self.presentations = []
197
#yes, I know a generic stack would be easier...
198
#still, testing if we are 'in' something gives
199
#a degree of validation.
201
self._curSection = None
202
self._curSlide = None
203
self._curFrame = None
204
self._curPara = None #the only places we are interested in
205
self._curPrefmt = None
206
self._curPyCode = None
207
self._curString = None
208
self._curTable = None
209
self._curTitle = None
210
self._curAuthor = None
211
self._curSubject = None
213
xmllib.XMLParser.__init__(self)
216
def _arg(self,tag,args,name):
218
if args.has_key(name):
221
if self.attributes.has_key(tag):
222
v = self.attributes[tag][name]
228
def ceval(self,tag,args,name):
229
if args.has_key(name):
232
if self.attributes.has_key(tag):
233
v = self.attributes[tag][name]
237
# handle named colors (names from reportlab.lib.colors)
238
if name in ('color', 'stroke', 'fill'):
239
v = str(pythonpoint.checkColor(v))
244
def getPresentation(self):
248
def handle_data(self, data):
249
#the only data should be paragraph text, preformatted para
250
#text, 'string text' for a fixed string on the page,
254
self._curPara.rawtext = self._curPara.rawtext + data
255
elif self._curPrefmt:
256
self._curPrefmt.rawtext = self._curPrefmt.rawtext + data
257
elif self._curPyCode:
258
self._curPyCode.rawtext = self._curPyCode.rawtext + data
259
elif self._curString:
260
self._curString.text = self._curString.text + data
262
self._curTable.rawBlocks.append(data)
263
elif self._curTitle <> None: # need to allow empty strings,
264
# hence explicitly testing for None
265
self._curTitle = self._curTitle + data
266
elif self._curAuthor <> None:
267
self._curAuthor = self._curAuthor + data
268
elif self._curSubject <> None:
269
self._curSubject = self._curSubject + data
272
def handle_cdata(self, data):
273
#just append to current paragraph text, so we can quote XML
275
self._curPara.rawtext = self._curPara.rawtext + data
276
elif self._curPrefmt:
277
self._curPrefmt.rawtext = self._curPrefmt.rawtext + data
278
elif self._curPyCode:
279
self._curPyCode.rawtext = self._curPyCode.rawtext + data
280
elif self._curString:
281
self._curString.text = self._curString.text + data
283
self._curTable.rawBlocks.append(data)
284
elif self._curAuthor <> None:
285
self._curAuthor = self._curAuthor + data
286
elif self._curSubject <> None:
287
self._curSubject = self._curSubject + data
290
def start_presentation(self, args):
291
self._curPres = pythonpoint.PPPresentation()
292
self._curPres.filename = self._arg('presentation',args,'filename')
293
self._curPres.effectName = self._arg('presentation',args,'effect')
294
self._curPres.pageDuration = self._arg('presentation',args,'pageDuration')
296
h = self._arg('presentation',args,'pageHeight')
298
self._curPres.pageHeight = h
299
w = self._arg('presentation',args,'pageWidth')
301
self._curPres.pageWidth = w
302
#print 'page size =', self._curPres.pageSize
305
def end_presentation(self):
307
## print 'Fully parsed presentation',self._curPres.filename
310
def start_title(self, args):
315
self._curPres.title = self._curTitle
316
self._curTitle = None
319
def start_author(self, args):
323
def end_author(self):
324
self._curPres.author = self._curAuthor
325
self._curAuthor = None
328
def start_subject(self, args):
329
self._curSubject = ''
332
def end_subject(self):
333
self._curPres.subject = self._curSubject
334
self._curSubject = None
337
def start_stylesheet(self, args):
338
#makes it the current style sheet.
339
path = self._arg('stylesheet',args,'path')
340
if path=='None': path = []
341
if type(path) not in SeqTypes: path = [path]
342
path.append('styles')
343
path.append(os.getcwd())
344
modulename = self._arg('stylesheet', args, 'module')
345
funcname = self._arg('stylesheet', args, 'function')
347
found = imp.find_module(modulename, path)
348
(file, pathname, description) = found
349
mod = imp.load_module(modulename, file, pathname, description)
352
mod = getModule(modulename)
354
#now get the function
355
func = getattr(mod, funcname)
356
pythonpoint.setStyles(func())
357
## print 'set global stylesheet to %s.%s()' % (modulename, funcname)
360
def end_stylesheet(self):
364
def start_section(self, args):
365
name = self._arg('section',args,'name')
366
self._curSection = pythonpoint.PPSection(name)
369
def end_section(self):
370
self._curSection = None
373
def start_slide(self, args):
374
s = pythonpoint.PPSlide()
375
s.id = self._arg('slide',args,'id')
376
s.title = self._arg('slide',args,'title')
377
a = self._arg('slide',args,'effectname')
380
s.effectDirection = self.ceval('slide',args,'effectdirection')
381
s.effectDimension = self._arg('slide',args,'effectdimension')
382
s.effectDuration = self.ceval('slide',args,'effectduration')
383
s.effectMotion = self._arg('slide',args,'effectmotion')
385
#HACK - may not belong here in the long run...
386
#by default, use the slide title for the outline entry,
387
#unless it is specified as an arg.
388
a = self._arg('slide',args,'outlineentry')
390
s.outlineEntry = None
394
s.outlineEntry = s.title
396
s.outlineLevel = self.ceval('slide',args,'outlinelevel')
398
#let it know its section, which may be none
399
s.section = self._curSection
404
self._curPres.slides.append(self._curSlide)
405
self._curSlide = None
408
def start_frame(self, args):
409
self._curFrame = pythonpoint.PPFrame(
410
self.ceval('frame',args,'x'),
411
self.ceval('frame',args,'y'),
412
self.ceval('frame',args,'width'),
413
self.ceval('frame',args,'height')
415
if self._arg('frame',args,'border')=='true':
416
self._curFrame.showBoundary = 1
420
self._curSlide.frames.append(self._curFrame)
421
self._curFrame = None
424
def start_notes(self, args):
425
name = self._arg('notes',args,'name')
426
self._curNotes = pythonpoint.PPNotes()
430
self._curSlide.notes.append(self._curNotes)
431
self._curNotes = None
434
def start_registerFont(self, args):
435
name = self._arg('font',args,'name')
436
path = self._arg('font',args,'path')
437
pythonpoint.registerFont0(self.sourceFilename, name, path)
440
def end_registerFont(self):
444
def pack_slide(self, element, args):
446
effectName = self._arg(element,args,'effectname')
447
if effectName <> 'None':
448
curSlide = copy.deepcopy(self._curSlide)
450
curFrame = copy.deepcopy(self._curFrame)
451
curSlide.frames.append(curFrame)
452
self._curPres.slides.append(curSlide)
453
self._curSlide.effectName = effectName
454
self._curSlide.effectDirection = self.ceval(element,args,'effectdirection')
455
self._curSlide.effectDimension = self._arg(element,args,'effectdimension')
456
self._curSlide.effectDuration = self.ceval(element,args,'effectduration')
457
self._curSlide.effectMotion = self._arg(element,args,'effectmotion')
458
self._curSlide.outlineEntry = None
460
def start_para(self, args):
461
self.pack_slide('para', args)
462
self._curPara = pythonpoint.PPPara()
463
self._curPara.style = self._arg('para',args,'style')
465
# hack - bullet character if bullet style
466
bt = self._arg('para',args,'bullettext')
468
if self._curPara.style == 'Bullet':
469
bt = '\267' # Symbol Font bullet character, reasonable default
470
elif self._curPara.style == 'Bullet2':
471
bt = '\267' # second-level bullet
475
self._curPara.bulletText = bt
480
self._curFrame.content.append(self._curPara)
483
self._curNotes.content.append(self._curPara)
487
def start_prefmt(self, args):
488
self._curPrefmt = pythonpoint.PPPreformattedText()
489
self._curPrefmt.style = self._arg('prefmt',args,'style')
492
def end_prefmt(self):
493
self._curFrame.content.append(self._curPrefmt)
494
self._curPrefmt = None
497
def start_pycode(self, args):
498
self._curPyCode = pythonpoint.PPPythonCode()
499
self._curPyCode.style = self._arg('pycode',args,'style')
502
def end_pycode(self):
503
self._curFrame.content.append(self._curPyCode)
504
self._curPyCode = None
507
def start_image(self, args):
508
self.pack_slide('image',args)
509
sourceFilename = self.sourceFilename # XXX
510
filename = self._arg('image',args,'filename')
511
filename = os.path.join(os.path.dirname(sourceFilename), filename)
512
self._curImage = pythonpoint.PPImage()
513
self._curImage.filename = filename
514
self._curImage.width = self.ceval('image',args,'width')
515
self._curImage.height = self.ceval('image',args,'height')
519
self._curFrame.content.append(self._curImage)
520
self._curImage = None
523
def start_table(self, args):
524
self.pack_slide('table',args)
525
self._curTable = pythonpoint.PPTable()
526
self._curTable.widths = self.ceval('table',args,'widths')
527
self._curTable.heights = self.ceval('table',args,'heights')
528
#these may contain escapes like tabs - handle with
530
if args.has_key('fieldDelim'):
531
self._curTable.fieldDelim = eval('"' + args['fieldDelim'] + '"')
532
if args.has_key('rowDelim'):
533
self._curTable.rowDelim = eval('"' + args['rowDelim'] + '"')
534
if args.has_key('style'):
535
self._curTable.style = args['style']
539
self._curFrame.content.append(self._curTable)
540
self._curTable = None
543
def start_spacer(self, args):
544
"""No contents so deal with it here."""
545
sp = pythonpoint.PPSpacer()
546
sp.height = eval(args['height'])
547
self._curFrame.content.append(sp)
550
def end_spacer(self):
554
## the graphics objects - go into either the current section
555
## or the current slide.
556
def start_fixedimage(self, args):
557
sourceFilename = self.sourceFilename
558
filename = self._arg('image',args,'filename')
559
filename = os.path.join(os.path.dirname(sourceFilename), filename)
560
img = pythonpoint.PPFixedImage()
561
img.filename = filename
562
img.x = self.ceval('fixedimage',args,'x')
563
img.y = self.ceval('fixedimage',args,'y')
564
img.width = self.ceval('fixedimage',args,'width')
565
img.height = self.ceval('fixedimage',args,'height')
566
self._curFixedImage = img
569
def end_fixedimage(self):
571
self._curSlide.graphics.append(self._curFixedImage)
572
elif self._curSection:
573
self._curSection.graphics.append(self._curFixedImage)
574
self._curFixedImage = None
577
def start_rectangle(self, args):
578
self.pack_slide('rectangle', args)
579
rect = pythonpoint.PPRectangle(
580
self.ceval('rectangle',args,'x'),
581
self.ceval('rectangle',args,'y'),
582
self.ceval('rectangle',args,'width'),
583
self.ceval('rectangle',args,'height')
585
rect.fillColor = self.ceval('rectangle',args,'fill')
586
rect.strokeColor = self.ceval('rectangle',args,'stroke')
587
self._curRectangle = rect
590
def end_rectangle(self):
592
self._curSlide.graphics.append(self._curRectangle)
593
elif self._curSection:
594
self._curSection.graphics.append(self._curRectangle)
595
self._curRectangle = None
598
def start_roundrect(self, args):
599
self.pack_slide('roundrect', args)
600
rrect = pythonpoint.PPRoundRect(
601
self.ceval('roundrect',args,'x'),
602
self.ceval('roundrect',args,'y'),
603
self.ceval('roundrect',args,'width'),
604
self.ceval('roundrect',args,'height'),
605
self.ceval('roundrect',args,'radius')
607
rrect.fillColor = self.ceval('roundrect',args,'fill')
608
rrect.strokeColor = self.ceval('roundrect',args,'stroke')
609
self._curRoundRect = rrect
612
def end_roundrect(self):
614
self._curSlide.graphics.append(self._curRoundRect)
615
elif self._curSection:
616
self._curSection.graphics.append(self._curRoundRect)
617
self._curRoundRect = None
620
def start_line(self, args):
621
self.pack_slide('line', args)
622
self._curLine = pythonpoint.PPLine(
623
self.ceval('line',args,'x1'),
624
self.ceval('line',args,'y1'),
625
self.ceval('line',args,'x2'),
626
self.ceval('line',args,'y2')
628
self._curLine.strokeColor = self.ceval('line',args,'stroke')
633
self._curSlide.graphics.append(self._curLine)
634
elif self._curSection:
635
self._curSection.graphics.append(self._curLine)
639
def start_ellipse(self, args):
640
self.pack_slide('ellipse', args)
641
self._curEllipse = pythonpoint.PPEllipse(
642
self.ceval('ellipse',args,'x1'),
643
self.ceval('ellipse',args,'y1'),
644
self.ceval('ellipse',args,'x2'),
645
self.ceval('ellipse',args,'y2')
647
self._curEllipse.strokeColor = self.ceval('ellipse',args,'stroke')
648
self._curEllipse.fillColor = self.ceval('ellipse',args,'fill')
651
def end_ellipse(self):
653
self._curSlide.graphics.append(self._curEllipse)
654
elif self._curSection:
655
self._curSection.graphics.append(self._curEllipse)
656
self._curEllipse = None
659
def start_polygon(self, args):
660
self.pack_slide('polygon', args)
661
self._curPolygon = pythonpoint.PPPolygon(self.ceval('polygon',args,'points'))
662
self._curPolygon.strokeColor = self.ceval('polygon',args,'stroke')
663
self._curPolygon.fillColor = self.ceval('polygon',args,'fill')
666
def end_polygon(self):
668
self._curSlide.graphics.append(self._curPolygon)
669
elif self._curSection:
670
self._curSection.graphics.append(self._curPolygon)
671
self._curEllipse = None
674
def start_string(self, args):
675
self.pack_slide('string', args)
676
self._curString = pythonpoint.PPString(
677
self.ceval('string',args,'x'),
678
self.ceval('string',args,'y')
680
self._curString.color = self.ceval('string',args,'color')
681
self._curString.font = self._arg('string',args,'font')
682
self._curString.size = self.ceval('string',args,'size')
683
if args['align'] == 'left':
684
self._curString.align = TA_LEFT
685
elif args['align'] == 'center':
686
self._curString.align = TA_CENTER
687
elif args['align'] == 'right':
688
self._curString.align = TA_RIGHT
689
elif args['align'] == 'justify':
690
self._curString.align = TA_JUSTIFY
691
#text comes later within the tag
694
def end_string(self):
695
#controller should have set the text
697
self._curSlide.graphics.append(self._curString)
698
elif self._curSection:
699
self._curSection.graphics.append(self._curString)
700
self._curString = None
703
def start_infostring(self, args):
704
# like a string, but lets them embed page no, author etc.
705
self.start_string(args)
706
self._curString.hasInfo = 1
709
def end_infostring(self):
713
def start_customshape(self, args):
715
path = self._arg('customshape',args,'path')
721
# add package root folder and input file's folder to path
722
path.append(os.path.dirname(self.sourceFilename))
723
path.append(os.path.dirname(pythonpoint.__file__))
725
modulename = self._arg('customshape',args,'module')
726
funcname = self._arg('customshape',args,'class')
728
found = imp.find_module(modulename, path)
729
(file, pathname, description) = found
730
mod = imp.load_module(modulename, file, pathname, description)
732
mod = getModule(modulename)
734
#now get the function
736
func = getattr(mod, funcname)
737
initargs = self.ceval('customshape',args,'initargs')
738
self._curCustomShape = apply(func, initargs)
741
def end_customshape(self):
743
self._curSlide.graphics.append(self._curCustomShape)
744
elif self._curSection:
745
self._curSection.graphics.append(self._curCustomShape)
746
self._curCustomShape = None
750
def start_drawing(self, args):
753
moduleName = args["module"]
754
funcName = args["constructor"]
756
showBoundary = int(args.get("showBoundary", "0"))
758
hAlign = args.get("hAlign", "CENTER")
761
# the path for the imports should include:
762
# 1. document directory
763
# 2. python path if baseDir not given, or
764
# 3. baseDir if given
766
dirName = sdict["baseDir"]
769
importPath = [os.getcwd()]
771
importPath.extend(sys.path)
773
importPath.insert(0, dirName)
775
modul = recursiveImport(moduleName, baseDir=importPath)
776
func = getattr(modul, funcName)
779
drawing.hAlign = hAlign
781
drawing._showBoundary = 1
783
self._curDrawing = pythonpoint.PPDrawing()
784
self._curDrawing.drawing = drawing
787
def end_drawing(self):
788
self._curFrame.content.append(self._curDrawing)
789
self._curDrawing = None
792
def start_pageCatcherFigure(self, args):
793
filename = args["filename"]
794
pageNo = int(args["pageNo"])
795
width = float(args.get("width", "595"))
796
height = float(args.get("height", "842"))
799
fig = figures.PageCatcherFigureNonA4(filename, pageNo, args.get("caption", ""), width, height)
800
sf = args.get('scaleFactor', None)
801
if sf: sf = float(sf)
802
border = not (args.get('border', None) in ['0','no'])
807
#self.ceval('pageCatcherFigure',args,'scaleFactor'),
808
#initargs = self.ceval('customshape',args,'initargs')
809
self._curFigure = pythonpoint.PPFigure()
810
self._curFigure.figure = fig
814
def end_pageCatcherFigure(self):
815
self._curFrame.content.append(self._curFigure)
816
self._curFigure = None
818
## intra-paragraph XML should be allowed through into PLATYPUS
819
def unknown_starttag(self, tag, attrs):
822
for (key, value) in attrs.items():
823
echo = echo + ' %s="%s"' % (key, value)
825
self._curPara.rawtext = self._curPara.rawtext + echo
827
print 'Unknown start tag %s' % tag
830
def unknown_endtag(self, tag):
832
self._curPara.rawtext = self._curPara.rawtext + '</%s>'% tag
834
print 'Unknown end tag %s' % tag