1"""
2Parser for PythonPoint using the xmllib.py in the standard Python
3distribution.  Slow, but always present.  We intend to add new parsers
4as Python 2.x and the XML package spread in popularity and stabilise.
5
6The parser has a getPresentation method; it is called from
7pythonpoint.py.
8"""
9
10import string, imp, sys, os, copy
11from reportlab.lib.utils import isSeq, uniChr, isPy3
12from reportlab.lib import colors
13from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY
14from reportlab.lib.utils import recursiveImport
15from reportlab.platypus.paraparser import HTMLParser, known_entities
16from tools.pythonpoint import pythonpoint
17from reportlab.platypus import figures
18from reportlab.lib.utils import asNative
19
20
21def getModule(modulename,fromPath='tools.pythonpoint.styles'):
22    """Get a module containing style declarations.
23
24    Search order is:
25        tools/pythonpoint/
26        tools/pythonpoint/styles/
27        ./
28    """
29
30    try:
31        exec('from tools.pythonpoint import '+modulename)
32        return eval(modulename)
33    except ImportError:
34        try:
35            exec('from tools.pythonpoint.styles import '+modulename)
36            return eval(modulename)
37        except ImportError:
38            exec('import '+modulename)
39            return eval(modulename)
40
41
42class PPMLParser(HTMLParser):
43    attributes = {
44        #this defines the available attributes for all objects,
45        #and their default values.  Although these don't have to
46        #be strings, the ones parsed from the XML do, so
47        #everything is a quoted string and the parser has to
48        #convert these to numbers where appropriate.
49        'stylesheet': {
50            'path':'None',
51            'module':'None',
52            'function':'getParagraphStyles'
53            },
54        'frame': {
55            'x':'0',
56            'y':'0',
57            'width':'0',
58            'height':'0',
59            'border':'false',
60            'leftmargin':'0',    #this is ignored
61            'topmargin':'0',     #this is ignored
62            'rightmargin':'0',   #this is ignored
63            'bottommargin':'0',  #this is ignored
64            },
65        'slide': {
66            'id':'None',
67            'title':'None',
68            'effectname':'None',     # Split, Blinds, Box, Wipe, Dissolve, Glitter
69            'effectdirection':'0',   # 0,90,180,270
70            'effectdimension':'H',   # H or V - horizontal or vertical
71            'effectmotion':'I',      # Inwards or Outwards
72            'effectduration':'1',    #seconds,
73            'outlineentry':'None',
74            'outlinelevel':'0'       # 1 is a child, 2 is a grandchild etc.
75            },
76        'para': {
77            'style':'Normal',
78            'bullettext':'',
79            'effectname':'None',
80            'effectdirection':'0',
81            'effectdimension':'H',
82            'effectmotion':'I',
83            'effectduration':'1'
84            },
85        'image': {
86            'filename':'',
87            'width':'None',
88            'height':'None',
89            'effectname':'None',
90            'effectdirection':'0',
91            'effectdimension':'H',
92            'effectmotion':'I',
93            'effectduration':'1'
94            },
95        'table': {
96            'widths':'None',
97            'heights':'None',
98            'fieldDelim':',',
99            'rowDelim':'\n',
100            'style':'None',
101            'effectname':'None',
102            'effectdirection':'0',
103            'effectdimension':'H',
104            'effectmotion':'I',
105            'effectduration':'1'
106            },
107        'rectangle': {
108            'x':'0',
109            'y':'0',
110            'width':'100',
111            'height':'100',
112            'fill':'None',
113            'stroke':'(0,0,0)',
114            'linewidth':'0',
115            'effectname':'None',
116            'effectdirection':'0',
117            'effectdimension':'H',
118            'effectmotion':'I',
119            'effectduration':'1'
120            },
121        'roundrect': {
122            'x':'0',
123            'y':'0',
124            'width':'100',
125            'height':'100',
126            'radius':'6',
127            'fill':'None',
128            'stroke':'(0,0,0)',
129            'linewidth':'0',
130            'effectname':'None',
131            'effectdirection':'0',
132            'effectdimension':'H',
133            'effectmotion':'I',
134            'effectduration':'1'
135            },
136        'line': {
137            'x1':'0',
138            'y1':'0',
139            'x2':'100',
140            'y2':'100',
141            'stroke':'(0,0,0)',
142            'width':'0',
143            'effectname':'None',
144            'effectdirection':'0',
145            'effectdimension':'H',
146            'effectmotion':'I',
147            'effectduration':'1'
148            },
149        'ellipse': {
150            'x1':'0',
151            'y1':'0',
152            'x2':'100',
153            'y2':'100',
154            'stroke':'(0,0,0)',
155            'fill':'None',
156            'linewidth':'0',
157            'effectname':'None',
158            'effectdirection':'0',
159            'effectdimension':'H',
160            'effectmotion':'I',
161            'effectduration':'1'
162            },
163        'polygon': {
164            'points':'(0,0),(50,0),(25,25)',
165            'stroke':'(0,0,0)',
166            'linewidth':'0',
167            'stroke':'(0,0,0)',
168            'fill':'None',
169            'effectname':'None',
170            'effectdirection':'0',
171            'effectdimension':'H',
172            'effectmotion':'I',
173            'effectduration':'1'
174            },
175        'string':{
176            'x':'0',
177            'y':'0',
178            'color':'(0,0,0)',
179            'font':'Times-Roman',
180            'size':'12',
181            'align':'left',
182            'effectname':'None',
183            'effectdirection':'0',
184            'effectdimension':'H',
185            'effectmotion':'I',
186            'effectduration':'1'
187            },
188        'customshape':{
189            'path':'None',
190            'module':'None',
191            'class':'None',
192            'initargs':'None'
193            }
194        }
195
196    def __init__(self,verbose=0, caseSensitive=0, ignoreUnknownTags=1):
197        self.caseSensitive = caseSensitive
198        self.ignoreUnknownTags = ignoreUnknownTags
199        self.presentations = []
200        #yes, I know a generic stack would be easier...
201        #still, testing if we are 'in' something gives
202        #a degree of validation.
203        self._curPres = None
204        self._curSection = None
205        self._curSlide = None
206        self._curFrame = None
207        self._curPara = None    #the only places we are interested in
208        self._curPrefmt = None
209        self._curPyCode = None
210        self._curString = None
211        self._curTable = None
212        self._curTitle = None
213        self._curAuthor = None
214        self._curSubject = None
215        self.fx = 1
216        HTMLParser.__init__(self)
217        if not isPy3:
218            try:
219                self.parser.returnUnicode = False
220            except:
221                pass
222
223    def _arg(self,tag,args,name):
224        "What's this for???"
225        if name in args:
226            v = args[name]
227        else:
228            if tag in self.attributes:
229                v = self.attributes[tag][name]
230            else:
231                v = None
232        return v
233
234    def ceval(self,tag,args,name):
235        if name in args:
236            v = args[name]
237        else:
238            if tag in self.attributes:
239                v = self.attributes[tag][name]
240            else:
241                return None
242
243        # handle named colors (names from reportlab.lib.colors)
244        if name in ('color', 'stroke', 'fill'):
245            v = str(pythonpoint.checkColor(v))
246
247        return eval(v)
248
249    def getPresentation(self):
250        return self._curPres
251
252
253    def handle_data(self, data):
254        #the only data should be paragraph text, preformatted para
255        #text, 'string text' for a fixed string on the page,
256        #or table data
257        data = asNative(data)
258        if self._curPara:
259            self._curPara.rawtext = self._curPara.rawtext + data
260        elif self._curPrefmt:
261            self._curPrefmt.rawtext = self._curPrefmt.rawtext + data
262        elif self._curPyCode:
263            self._curPyCode.rawtext = self._curPyCode.rawtext + data
264        elif  self._curString:
265            self._curString.text = self._curString.text + data
266        elif self._curTable:
267            self._curTable.rawBlocks.append(data)
268        elif self._curTitle != None:  # need to allow empty strings,
269            # hence explicitly testing for None
270            self._curTitle = self._curTitle + data
271        elif self._curAuthor != None:
272            self._curAuthor = self._curAuthor + data
273        elif self._curSubject != None:
274            self._curSubject = self._curSubject + data
275
276    def handle_cdata(self, data):
277        #just append to current paragraph text, so we can quote XML
278        if self._curPara:
279            self._curPara.rawtext = self._curPara.rawtext + data
280        elif self._curPrefmt:
281            self._curPrefmt.rawtext = self._curPrefmt.rawtext + data
282        elif self._curPyCode:
283            self._curPyCode.rawtext = self._curPyCode.rawtext + data
284        elif  self._curString:
285            self._curString.text = self._curString.text + data
286        elif self._curTable:
287            self._curTable.rawBlocks.append(data)
288        elif self._curAuthor != None:
289            self._curAuthor = self._curAuthor + data
290        elif self._curSubject != None:
291            self._curSubject = self._curSubject + data
292
293    def start_presentation(self, args):
294        self._curPres = pythonpoint.PPPresentation()
295        self._curPres.filename = self._arg('presentation',args,'filename')
296        self._curPres.effectName = self._arg('presentation',args,'effect')
297        self._curPres.pageDuration = self._arg('presentation',args,'pageDuration')
298
299        h = self._arg('presentation',args,'pageHeight')
300        if h:
301            self._curPres.pageHeight = h
302        w = self._arg('presentation',args,'pageWidth')
303        if w:
304            self._curPres.pageWidth = w
305        #print 'page size =', self._curPres.pageSize
306
307    def end_presentation(self):
308        pass
309##        print 'Fully parsed presentation',self._curPres.filename
310
311    def start_title(self, args):
312        self._curTitle = ''
313
314    def end_title(self):
315        self._curPres.title = self._curTitle
316        self._curTitle = None
317
318    def start_author(self, args):
319        self._curAuthor = ''
320
321    def end_author(self):
322        self._curPres.author = self._curAuthor
323        self._curAuthor = None
324
325    def start_subject(self, args):
326        self._curSubject = ''
327
328    def end_subject(self):
329        self._curPres.subject = self._curSubject
330        self._curSubject = None
331
332    def start_stylesheet(self, args):
333        #makes it the current style sheet.
334        path = self._arg('stylesheet',args,'path')
335        if path=='None': path = []
336        if not isSeq(path): path = [path]
337        path.append('styles')
338        path.append(os.getcwd())
339        modulename = self._arg('stylesheet', args, 'module')
340        funcname = self._arg('stylesheet', args, 'function')
341        try:
342            found = imp.find_module(modulename, path)
343            (file, pathname, description) = found
344            mod = imp.load_module(modulename, file, pathname, description)
345        except ImportError:
346            #last gasp
347            mod = getModule(modulename)
348
349        #now get the function
350        func = getattr(mod, funcname)
351        pythonpoint.setStyles(func())
352##        print 'set global stylesheet to %s.%s()' % (modulename, funcname)
353
354    def end_stylesheet(self):
355        pass
356
357    def start_section(self, args):
358        name = self._arg('section',args,'name')
359        self._curSection = pythonpoint.PPSection(name)
360
361    def end_section(self):
362        self._curSection = None
363
364    def start_slide(self, args):
365        s = pythonpoint.PPSlide()
366        s.id = self._arg('slide',args,'id')
367        s.title = self._arg('slide',args,'title')
368        a = self._arg('slide',args,'effectname')
369        if a != 'None':
370            s.effectName = a
371        s.effectDirection = self.ceval('slide',args,'effectdirection')
372        s.effectDimension = self._arg('slide',args,'effectdimension')
373        s.effectDuration = self.ceval('slide',args,'effectduration')
374        s.effectMotion = self._arg('slide',args,'effectmotion')
375
376        #HACK - may not belong here in the long run...
377        #by default, use the slide title for the outline entry,
378        #unless it is specified as an arg.
379        a = self._arg('slide',args,'outlineentry')
380        if a == "Hide":
381            s.outlineEntry = None
382        elif a != 'None':
383            s.outlineEntry = a
384        else:
385            s.outlineEntry = s.title
386
387        s.outlineLevel = self.ceval('slide',args,'outlinelevel')
388
389        #let it know its section, which may be none
390        s.section = self._curSection
391        self._curSlide = s
392
393    def end_slide(self):
394        self._curPres.slides.append(self._curSlide)
395        self._curSlide = None
396
397    def start_frame(self, args):
398        self._curFrame = pythonpoint.PPFrame(
399            self.ceval('frame',args,'x'),
400            self.ceval('frame',args,'y'),
401            self.ceval('frame',args,'width'),
402            self.ceval('frame',args,'height')
403            )
404        if self._arg('frame',args,'border')=='true':
405            self._curFrame.showBoundary = 1
406
407    def end_frame(self):
408        self._curSlide.frames.append(self._curFrame)
409        self._curFrame = None
410
411    def start_notes(self, args):
412        name = self._arg('notes',args,'name')
413        self._curNotes = pythonpoint.PPNotes()
414
415    def end_notes(self):
416        self._curSlide.notes.append(self._curNotes)
417        self._curNotes = None
418
419    def start_registerFont(self, args):
420        name = self._arg('font',args,'name')
421        path = self._arg('font',args,'path')
422        pythonpoint.registerFont0(self.sourceFilename, name, path)
423
424
425    def end_registerFont(self):
426        pass
427
428
429    def pack_slide(self, element, args):
430        if self.fx:
431            effectName = self._arg(element,args,'effectname')
432            if effectName != 'None':
433                curSlide = copy.deepcopy(self._curSlide)
434                if self._curFrame:
435                    curFrame = copy.deepcopy(self._curFrame)
436                    curSlide.frames.append(curFrame)
437                self._curPres.slides.append(curSlide)
438                self._curSlide.effectName = effectName
439                self._curSlide.effectDirection = self.ceval(element,args,'effectdirection')
440                self._curSlide.effectDimension = self._arg(element,args,'effectdimension')
441                self._curSlide.effectDuration = self.ceval(element,args,'effectduration')
442                self._curSlide.effectMotion = self._arg(element,args,'effectmotion')
443                self._curSlide.outlineEntry = None
444
445    def start_para(self, args):
446        self.pack_slide('para', args)
447        self._curPara = pythonpoint.PPPara()
448        self._curPara.style = self._arg('para',args,'style')
449
450        # hack - bullet character if bullet style
451        bt = self._arg('para',args,'bullettext')
452        if bt == '':
453            if self._curPara.style == 'Bullet':
454                bt = u'\u2022'  # Symbol Font bullet character, reasonable default
455            elif self._curPara.style == 'Bullet2':
456                bt = u'\u2022'  # second-level bullet
457            else:
458                bt = None
459
460        self._curPara.bulletText = bt
461
462    def end_para(self):
463        if self._curFrame:
464            self._curFrame.content.append(self._curPara)
465            self._curPara = None
466        elif self._curNotes:
467            self._curNotes.content.append(self._curPara)
468            self._curPara = None
469
470
471    def start_prefmt(self, args):
472        self._curPrefmt = pythonpoint.PPPreformattedText()
473        self._curPrefmt.style = self._arg('prefmt',args,'style')
474
475
476    def end_prefmt(self):
477        self._curFrame.content.append(self._curPrefmt)
478        self._curPrefmt = None
479
480
481    def start_pycode(self, args):
482        self._curPyCode = pythonpoint.PPPythonCode()
483        self._curPyCode.style = self._arg('pycode',args,'style')
484
485
486    def end_pycode(self):
487        self._curFrame.content.append(self._curPyCode)
488        self._curPyCode = None
489
490
491    def start_image(self, args):
492        self.pack_slide('image',args)
493        sourceFilename = self.sourceFilename # XXX
494        filename = self._arg('image',args,'filename')
495        filename = os.path.join(os.path.dirname(sourceFilename), filename)
496        self._curImage = pythonpoint.PPImage()
497        self._curImage.filename = filename
498        self._curImage.width = self.ceval('image',args,'width')
499        self._curImage.height = self.ceval('image',args,'height')
500
501
502    def end_image(self):
503        self._curFrame.content.append(self._curImage)
504        self._curImage = None
505
506
507    def start_table(self, args):
508        self.pack_slide('table',args)
509        self._curTable = pythonpoint.PPTable()
510        self._curTable.widths = self.ceval('table',args,'widths')
511        self._curTable.heights = self.ceval('table',args,'heights')
512        #these may contain escapes like tabs - handle with
513        #a bit more care.
514        if 'fieldDelim' in args:
515            self._curTable.fieldDelim = eval('"' + args['fieldDelim'] + '"')
516        if 'rowDelim' in args:
517            self._curTable.rowDelim = eval('"' + args['rowDelim'] + '"')
518        if 'style' in args:
519            self._curTable.style = args['style']
520
521
522    def end_table(self):
523        self._curFrame.content.append(self._curTable)
524        self._curTable = None
525
526
527    def start_spacer(self, args):
528        """No contents so deal with it here."""
529        sp = pythonpoint.PPSpacer()
530        sp.height = eval(args['height'])
531        self._curFrame.content.append(sp)
532
533
534    def end_spacer(self):
535        pass
536
537
538    ## the graphics objects - go into either the current section
539    ## or the current slide.
540    def start_fixedimage(self, args):
541        sourceFilename = self.sourceFilename
542        filename = self._arg('image',args,'filename')
543        filename = os.path.join(os.path.dirname(sourceFilename), filename)
544        img = pythonpoint.PPFixedImage()
545        img.filename = filename
546        img.x = self.ceval('fixedimage',args,'x')
547        img.y = self.ceval('fixedimage',args,'y')
548        img.width = self.ceval('fixedimage',args,'width')
549        img.height = self.ceval('fixedimage',args,'height')
550        self._curFixedImage = img
551
552
553    def end_fixedimage(self):
554        if self._curSlide:
555            self._curSlide.graphics.append(self._curFixedImage)
556        elif self._curSection:
557            self._curSection.graphics.append(self._curFixedImage)
558        self._curFixedImage = None
559
560
561    def start_rectangle(self, args):
562        self.pack_slide('rectangle', args)
563        rect = pythonpoint.PPRectangle(
564                    self.ceval('rectangle',args,'x'),
565                    self.ceval('rectangle',args,'y'),
566                    self.ceval('rectangle',args,'width'),
567                    self.ceval('rectangle',args,'height')
568                    )
569        rect.fillColor = self.ceval('rectangle',args,'fill')
570        rect.strokeColor = self.ceval('rectangle',args,'stroke')
571        self._curRectangle = rect
572
573
574    def end_rectangle(self):
575        if self._curSlide:
576            self._curSlide.graphics.append(self._curRectangle)
577        elif self._curSection:
578            self._curSection.graphics.append(self._curRectangle)
579        self._curRectangle = None
580
581
582    def start_roundrect(self, args):
583        self.pack_slide('roundrect', args)
584        rrect = pythonpoint.PPRoundRect(
585                    self.ceval('roundrect',args,'x'),
586                    self.ceval('roundrect',args,'y'),
587                    self.ceval('roundrect',args,'width'),
588                    self.ceval('roundrect',args,'height'),
589                    self.ceval('roundrect',args,'radius')
590                    )
591        rrect.fillColor = self.ceval('roundrect',args,'fill')
592        rrect.strokeColor = self.ceval('roundrect',args,'stroke')
593        self._curRoundRect = rrect
594
595
596    def end_roundrect(self):
597        if self._curSlide:
598            self._curSlide.graphics.append(self._curRoundRect)
599        elif self._curSection:
600            self._curSection.graphics.append(self._curRoundRect)
601        self._curRoundRect = None
602
603
604    def start_line(self, args):
605        self.pack_slide('line', args)
606        self._curLine = pythonpoint.PPLine(
607                    self.ceval('line',args,'x1'),
608                    self.ceval('line',args,'y1'),
609                    self.ceval('line',args,'x2'),
610                    self.ceval('line',args,'y2')
611                    )
612        self._curLine.strokeColor = self.ceval('line',args,'stroke')
613
614
615    def end_line(self):
616        if self._curSlide:
617            self._curSlide.graphics.append(self._curLine)
618        elif self._curSection:
619            self._curSection.graphics.append(self._curLine)
620        self._curLine = None
621
622
623    def start_ellipse(self, args):
624        self.pack_slide('ellipse', args)
625        self._curEllipse = pythonpoint.PPEllipse(
626                    self.ceval('ellipse',args,'x1'),
627                    self.ceval('ellipse',args,'y1'),
628                    self.ceval('ellipse',args,'x2'),
629                    self.ceval('ellipse',args,'y2')
630                    )
631        self._curEllipse.strokeColor = self.ceval('ellipse',args,'stroke')
632        self._curEllipse.fillColor = self.ceval('ellipse',args,'fill')
633
634
635    def end_ellipse(self):
636        if self._curSlide:
637            self._curSlide.graphics.append(self._curEllipse)
638        elif self._curSection:
639            self._curSection.graphics.append(self._curEllipse)
640        self._curEllipse = None
641
642
643    def start_polygon(self, args):
644        self.pack_slide('polygon', args)
645        self._curPolygon = pythonpoint.PPPolygon(self.ceval('polygon',args,'points'))
646        self._curPolygon.strokeColor = self.ceval('polygon',args,'stroke')
647        self._curPolygon.fillColor = self.ceval('polygon',args,'fill')
648
649
650    def end_polygon(self):
651        if self._curSlide:
652            self._curSlide.graphics.append(self._curPolygon)
653        elif self._curSection:
654            self._curSection.graphics.append(self._curPolygon)
655        self._curEllipse = None
656
657
658    def start_string(self, args):
659        self.pack_slide('string', args)
660        self._curString = pythonpoint.PPString(
661                            self.ceval('string',args,'x'),
662                            self.ceval('string',args,'y')
663                            )
664        self._curString.color = self.ceval('string',args,'color')
665        self._curString.font = self._arg('string',args,'font')
666        self._curString.size = self.ceval('string',args,'size')
667        if args['align'] == 'left':
668            self._curString.align = TA_LEFT
669        elif args['align'] == 'center':
670            self._curString.align = TA_CENTER
671        elif args['align'] == 'right':
672            self._curString.align = TA_RIGHT
673        elif args['align'] == 'justify':
674            self._curString.align = TA_JUSTIFY
675        #text comes later within the tag
676
677
678    def end_string(self):
679        #controller should have set the text
680        if self._curSlide:
681            self._curSlide.graphics.append(self._curString)
682        elif self._curSection:
683            self._curSection.graphics.append(self._curString)
684        self._curString = None
685
686
687    def start_infostring(self, args):
688        # like a string, but lets them embed page no, author etc.
689        self.start_string(args)
690        self._curString.hasInfo = 1
691
692
693    def end_infostring(self):
694        self.end_string()
695
696
697    def start_customshape(self, args):
698        #loads one
699        path = self._arg('customshape',args,'path')
700        if path=='None':
701            path = []
702        else:
703            path=[path]
704
705        # add package root folder and input file's folder to path
706        path.append(os.path.dirname(self.sourceFilename))
707        path.append(os.path.dirname(pythonpoint.__file__))
708
709        modulename = self._arg('customshape',args,'module')
710        funcname = self._arg('customshape',args,'class')
711        try:
712            found = imp.find_module(modulename, path)
713            (file, pathname, description) = found
714            mod = imp.load_module(modulename, file, pathname, description)
715        except ImportError:
716            mod = getModule(modulename)
717
718        #now get the function
719
720        func = getattr(mod, funcname)
721        initargs = self.ceval('customshape',args,'initargs')
722        self._curCustomShape = func(*initargs)
723
724    def end_customshape(self):
725        if self._curSlide:
726            self._curSlide.graphics.append(self._curCustomShape)
727        elif self._curSection:
728            self._curSection.graphics.append(self._curCustomShape)
729        self._curCustomShape = None
730
731    def start_drawing(self, args):
732        #loads one
733        moduleName = args["module"]
734        funcName = args["constructor"]
735        showBoundary = int(args.get("showBoundary", "0"))
736        hAlign = args.get("hAlign", "CENTER")
737
738
739        # the path for the imports should include:
740        # 1. document directory
741        # 2. python path if baseDir not given, or
742        # 3. baseDir if given
743        try:
744            dirName = sdict["baseDir"]
745        except:
746            dirName = None
747        importPath = [os.getcwd()]
748        if dirName is None:
749            importPath.extend(sys.path)
750        else:
751            importPath.insert(0, dirName)
752
753        modul = recursiveImport(moduleName, baseDir=importPath)
754        func = getattr(modul, funcName)
755        drawing = func()
756
757        drawing.hAlign = hAlign
758        if showBoundary:
759            drawing._showBoundary = 1
760
761        self._curDrawing = pythonpoint.PPDrawing()
762        self._curDrawing.drawing = drawing
763
764    def end_drawing(self):
765        self._curFrame.content.append(self._curDrawing)
766        self._curDrawing = None
767
768    def start_pageCatcherFigure(self, args):
769        filename = args["filename"]
770        pageNo = int(args["pageNo"])
771        width = float(args.get("width", "595"))
772        height = float(args.get("height", "842"))
773
774
775        fig = figures.PageCatcherFigureNonA4(filename, pageNo, args.get("caption", ""), width, height)
776        sf = args.get('scaleFactor', None)
777        if sf: sf = float(sf)
778        border = not (args.get('border', None) in ['0','no'])
779
780        fig.scaleFactor = sf
781        fig.border = border
782
783        #self.ceval('pageCatcherFigure',args,'scaleFactor'),
784        #initargs = self.ceval('customshape',args,'initargs')
785        self._curFigure = pythonpoint.PPFigure()
786        self._curFigure.figure = fig
787
788    def end_pageCatcherFigure(self):
789        self._curFrame.content.append(self._curFigure)
790        self._curFigure = None
791
792    ## intra-paragraph XML should be allowed through into PLATYPUS
793    def unknown_starttag(self, tag, attrs):
794        if  self._curPara:
795            echo = '<%s' % tag
796            for key, value in attrs.items():
797                echo = echo + ' %s="%s"' % (key, value)
798            echo = echo + '>'
799            self._curPara.rawtext = self._curPara.rawtext + echo
800        else:
801            print('Unknown start tag %s' % tag)
802
803
804    def unknown_endtag(self, tag):
805        if  self._curPara:
806            self._curPara.rawtext = self._curPara.rawtext + '</%s>'% tag
807        else:
808            print('Unknown end tag %s' % tag)
809
810    def handle_charref(self, name):
811        try:
812            if name[0]=='x':
813                n = int(name[1:],16)
814            else:
815                n = int(name)
816        except ValueError:
817            self.unknown_charref(name)
818            return
819        self.handle_data(uniChr(n).encode('utf8'))
820
821    #HTMLParser interface
822    def handle_starttag(self, tag, attrs):
823        "Called by HTMLParser when a tag starts"
824
825        #tuple tree parser used to expect a dict.  HTML parser
826        #gives list of two-element tuples
827        if isinstance(attrs, list):
828            d = {}
829            for (k,  v) in attrs:
830                d[k] = v
831            attrs = d
832        if not self.caseSensitive: tag = tag.lower()
833        try:
834            start = getattr(self,'start_'+tag)
835        except AttributeError:
836            if not self.ignoreUnknownTags:
837                raise ValueError('Invalid tag "%s"' % tag)
838            start = self.start_unknown
839        #call it
840        start(attrs or {})
841
842    def handle_endtag(self, tag):
843        "Called by HTMLParser when a tag ends"
844        #find the existing end_tagname method
845        if not self.caseSensitive: tag = tag.lower()
846        try:
847            end = getattr(self,'end_'+tag)
848        except AttributeError:
849            if not self.ignoreUnknownTags:
850                raise ValueError('Invalid tag "%s"' % tag)
851            end = self.end_unknown
852        #call it
853        end()
854
855    def handle_entityref(self, name):
856        "Handles a named entity.  "
857        try:
858            v = uniChr(known_entities[name])
859        except:
860            v = u'&amp;%s;' % name
861        self.handle_data(v)
862
863    def start_unknown(self,attr):
864        pass
865    def end_unknown(self):
866        pass
867