File: test_platypus_general.py

package info (click to toggle)
python-reportlab 3.1.8-3%2Bdeb8u1
  • links: PTS
  • area: main
  • in suites: jessie
  • size: 6,996 kB
  • ctags: 9,483
  • sloc: python: 69,727; ansic: 19,108; xml: 1,494; makefile: 416; java: 193; sh: 100
file content (594 lines) | stat: -rw-r--r-- 25,193 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
#Copyright ReportLab Europe Ltd. 2000-2012
#see license.txt for license details
__version__=''' $Id$ '''

#tests and documents Page Layout API
__doc__="""This is not obvious so here's a brief explanation.  This module is both
the test script and user guide for layout.  Each page has two frames on it:
one for commentary, and one for demonstration objects which may be drawn in
various esoteric ways.  The two functions getCommentary() and getExamples()
return the 'story' for each.  The run() function gets the stories, then
builds a special "document model" in which the frames are added to each page
and drawn into.
"""
from reportlab.lib.testutils import setOutDir,makeSuiteForClasses, outputfile, printLocation
setOutDir(__name__)
import copy, sys, os
from reportlab.pdfgen import canvas
from reportlab import platypus
from reportlab.platypus import BaseDocTemplate, PageTemplate, Flowable, FrameBreak
from reportlab.platypus import Paragraph, Preformatted
from reportlab.lib.units import inch, cm
from reportlab.lib.styles import PropertySet, getSampleStyleSheet, ParagraphStyle
from reportlab.lib import colors
from reportlab.rl_config import defaultPageSize
from reportlab.lib.utils import haveImages, _RL_DIR, rl_isfile, open_for_read, fileName2FSEnc
import unittest
from reportlab.lib.testutils import testsFolder
if haveImages:
    _GIF = os.path.join(testsFolder,'pythonpowered.gif')
    if not rl_isfile(_GIF): _GIF = None
else:
    _GIF = None
if _GIF: _GIFFSEnc=fileName2FSEnc(_GIF)

_JPG = os.path.join(testsFolder,'..','docs','images','lj8100.jpg')
if not rl_isfile(_JPG): _JPG = None

def getFurl(fn):
    furl = fn.replace(os.sep,'/')
    if sys.platform=='win32' and furl[1]==':': furl = furl[0]+'|'+furl[2:]
    if furl[0]!='/': furl = '/'+furl
    return 'file://'+furl

PAGE_HEIGHT = defaultPageSize[1]

#################################################################
#
#  first some drawing utilities
#
#
################################################################

BASEFONT = ('Times-Roman', 10)

def framePage(canvas,doc):
    #canvas.drawImage("snkanim.gif", 36, 36)
    canvas.saveState()
    canvas.setStrokeColorRGB(1,0,0)
    canvas.setLineWidth(5)
    canvas.line(66,72,66,PAGE_HEIGHT-72)

    canvas.setFont('Times-Italic',12)
    canvas.drawRightString(523, PAGE_HEIGHT - 56, "Platypus User Guide and Test Script")

    canvas.setFont('Times-Roman',12)
    canvas.drawString(4 * inch, 0.75 * inch,
                        "Page %d" % canvas.getPageNumber())
    canvas.restoreState()

def getParagraphs(textBlock):
    """Within the script, it is useful to whack out a page in triple
    quotes containing separate paragraphs. This breaks one into its
    constituent paragraphs, using blank lines as the delimiter."""
    lines = textBlock.split('\n')
    paras = []
    currentPara = []
    for line in lines:
        if len(line.strip()) == 0:
            #blank, add it
            if currentPara != []:
                paras.append('\n'.join(currentPara))
                currentPara = []
        else:
            currentPara.append(line)
    #...and the last one
    if currentPara != []:
        paras.append('\n'.join(currentPara))

    return paras

def getCommentary():
    """Returns the story for the commentary - all the paragraphs."""

    styleSheet = getSampleStyleSheet()

    story = []
    story.append(Paragraph("""
        PLATYPUS User Guide and Test Script
        """, styleSheet['Heading1']))


    spam = """
    Welcome to PLATYPUS!

    Platypus stands for "Page Layout and Typography Using Scripts".  It is a high
    level page layout library which lets you programmatically create complex
    documents with a minimum of effort.

    This document is both the user guide & the output of the test script.
    In other words, a script used platypus to create the document you are now
    reading, and the fact that you are reading it proves that it works.  Or
    rather, that it worked for this script anyway.  It is a first release!

    Platypus is built 'on top of' PDFgen, the Python library for creating PDF
    documents.  To learn about PDFgen, read the document testpdfgen.pdf.

    """

    for text in getParagraphs(spam):
        story.append(Paragraph(text, styleSheet['BodyText']))

    story.append(Paragraph("""
        What concepts does PLATYPUS deal with?
        """, styleSheet['Heading2']))
    story.append(Paragraph("""
        The central concepts in PLATYPUS are Flowable Objects, Frames, Flow
        Management, Styles and Style Sheets, Paragraphs and Tables.  This is
        best explained in contrast to PDFgen, the layer underneath PLATYPUS.
        PDFgen is a graphics library, and has primitive commans to draw lines
        and strings.  There is nothing in it to manage the flow of text down
        the page.  PLATYPUS works at the conceptual level fo a desktop publishing
        package; you can write programs which deal intelligently with graphic
        objects and fit them onto the page.
        """, styleSheet['BodyText']))

    story.append(Paragraph("""
        How is this document organized?
        """, styleSheet['Heading2']))

    story.append(Paragraph("""
        Since this is a test script, we'll just note how it is organized.
        the top of each page contains commentary.  The bottom half contains
        example drawings and graphic elements to whicht he commentary will
        relate.  Down below, you can see the outline of a text frame, and
        various bits and pieces within it.  We'll explain how they work
        on the next page.
        """, styleSheet['BodyText']))

    story.append(FrameBreak())
    #######################################################################
    #     Commentary Page 2
    #######################################################################

    story.append(Paragraph("""
        Flowable Objects
        """, styleSheet['Heading2']))
    spam = """
        The first and most fundamental concept is that of a 'Flowable Object'.
        In PDFgen, you draw stuff by calling methods of the canvas to set up
        the colors, fonts and line styles, and draw the graphics primitives.
        If you set the pen color to blue, everything you draw after will be
        blue until you change it again.  And you have to handle all of the X-Y
        coordinates yourself.

        A 'Flowable object' is exactly what it says.  It knows how to draw itself
        on the canvas, and the way it does so is totally independent of what
        you drew before or after.  Furthermore, it draws itself at the location
        on the page you specify.

        The most fundamental Flowable Objects in most documents are likely to be
        paragraphs, tables, diagrams/charts and images - but there is no
        restriction.  You can write your own easily, and I hope that people
        will start to contribute them.  PINGO users - we provide a "PINGO flowable" object to let
        you insert platform-independent graphics into the flow of a document.

        When you write a flowable object, you inherit from Flowable and
        must implement two methods.  object.wrap(availWidth, availHeight) will be called by other parts of
        the system, and tells you how much space you have.  You should return
        how much space you are going to use.  For a fixed-size object, this
        is trivial, but it is critical - PLATYPUS needs to figure out if things
        will fit on the page before drawing them.  For other objects such as paragraphs,
        the height is obviously determined by the available width.


        The second method is object.draw().  Here, you do whatever you want.
        The Flowable base class sets things up so that you have an origin of
        (0,0) for your drawing, and everything will fit nicely if you got the
        height and width right.  It also saves and restores the graphics state
        around your calls, so you don;t have to reset all the properties you
        changed.

        Programs which actually draw a Flowable don't
        call draw() this directly - they call object.drawOn(canvas, x, y).
        So you can write code in your own coordinate system, and things
        can be drawn anywhere on the page (possibly even scaled or rotated).
        """
    for text in getParagraphs(spam):
        story.append(Paragraph(text, styleSheet['BodyText']))

    story.append(FrameBreak())
    #######################################################################
    #     Commentary Page 3
    #######################################################################

    story.append(Paragraph("""
        Available Flowable Objects
        """, styleSheet['Heading2']))

    story.append(Paragraph("""
        Platypus comes with a basic set of flowable objects.  Here we list their
        class names and tell you what they do:
        """, styleSheet['BodyText']))
    #we can use the bullet feature to do a definition list
    story.append(Paragraph("""
        <para color=green bcolor=red bg=pink>This is a <font bgcolor=yellow color=red>contrived</font> object to give an example of a Flowable -
        just a fixed-size box with an X through it and a centred string.</para>""",
            styleSheet['Definition'],
            bulletText='XBox  '  #hack - spot the extra space after
            ))

    story.append(Paragraph("""
        This is the basic unit of a document.  Paragraphs can be finely
        tuned and offer a host of properties through their associated
        ParagraphStyle.""",
            styleSheet['Definition'],
            bulletText='Paragraph  '  #hack - spot the extra space after
            ))

    story.append(Paragraph("""
        This is used for printing code and other preformatted text.
        There is no wrapping, and line breaks are taken where they occur.
        Many paragraph style properties do not apply.  You may supply
        an optional 'dedent' parameter to trim a number of characters
        off the front of each line.""",
            styleSheet['Definition'],
            bulletText='Preformatted  '  #hack - spot the extra space after
            ))
    story.append(Paragraph("""
        This is a straight wrapper around an external image file.  By default
        the image will be drawn at a scale of one pixel equals one point, and
        centred in the frame.  You may supply an optional width and height.""",
            styleSheet['Definition'],
            bulletText='Image  '  #hack - spot the extra space after
            ))

    story.append(Paragraph("""
        This is a table drawing class; it is intended to be simpler
        than a full HTML table model yet be able to draw attractive output,
        and behave intelligently when the numbers of rows and columns vary.
        Still need to add the cell properties (shading, alignment, font etc.)""",
            styleSheet['Definition'],
            bulletText='Table  '  #hack - spot the extra space after
            ))

    story.append(Paragraph("""
        This is a 'null object' which merely takes up space on the page.
        Use it when you want some extra padding betweene elements.""",
            styleSheet['Definition'],
            bulletText='Spacer '  #hack - spot the extra space after
            ))

    story.append(Paragraph("""
        A FrameBreak causes the document to call its handle_frameEnd method.""",
            styleSheet['Definition'],
            bulletText='FrameBreak  '  #hack - spot the extra space after
            ))

    story.append(Paragraph("""
        This is in progress, but a macro is basically a chunk of Python code to
        be evaluated when it is drawn.  It could do lots of neat things.""",
            styleSheet['Definition'],
            bulletText='Macro  '  #hack - spot the extra space after
            ))

    story.append(FrameBreak())

    story.append(Paragraph(
                "The next example uses a custom font",
                styleSheet['Italic']))
    def code(txt,story=story,styleSheet=styleSheet):
        story.append(Preformatted(txt,styleSheet['Code']))
    code('''import reportlab.rl_config
    reportlab.rl_config.warnOnMissingFontGlyphs = 0

    from reportlab.pdfbase import pdfmetrics
    fontDir = os.path.join(_RL_DIR,'fonts')
    face = pdfmetrics.EmbeddedType1Face(os.path.join(fontDir,'DarkGardenMK.afm'),
            os.path.join(fontDir,'DarkGardenMK.pfb'))
    faceName = face.name  # should be 'DarkGardenMK'
    pdfmetrics.registerTypeFace(face)
    font = pdfmetrics.Font(faceName, faceName, 'WinAnsiEncoding')
    pdfmetrics.registerFont(font)


    # put it inside a paragraph.
    story.append(Paragraph(
        """This is an ordinary paragraph, which happens to contain
        text in an embedded font:
        <font name="DarkGardenMK">DarkGardenMK</font>.
        Now for the real challenge...""", styleSheet['Normal']))


    styRobot = ParagraphStyle('Robot', styleSheet['Normal'])
    styRobot.fontSize = 16
    styRobot.leading = 20
    styRobot.fontName = 'DarkGardenMK'

    story.append(Paragraph(
                "This whole paragraph is 16-point DarkGardenMK.",
                styRobot))''')

    story.append(FrameBreak())
    if _GIF:
        story.append(Paragraph("""We can use images via the file name""", styleSheet['BodyText']))
        code('''    story.append(platypus.Image('%s'))'''%_GIFFSEnc)
        code('''    story.append(platypus.Image(fileName2FSEnc('%s')))''' % _GIFFSEnc)
        story.append(Paragraph("""They can also be used with a file URI or from an open python file!""", styleSheet['BodyText']))
        code('''    story.append(platypus.Image('%s'))'''% getFurl(_GIFFSEnc))
        code('''    story.append(platypus.Image(open_for_read('%s','b')))''' % _GIFFSEnc)
        story.append(FrameBreak())
        story.append(Paragraph("""Images can even be obtained from the internet.""", styleSheet['BodyText']))
        code('''    img = platypus.Image('http://www.reportlab.com/rsrc/encryption.gif')
    story.append(img)''')
        story.append(FrameBreak())

    if _JPG:
        story.append(Paragraph("""JPEGs are a native PDF image format. They should be available even if PIL cannot be used.""", styleSheet['BodyText']))
        story.append(FrameBreak())
    return story

def getExamples():
    """Returns all the example flowable objects"""
    styleSheet = getSampleStyleSheet()

    story = []

    #make a style with indents and spacing
    sty = ParagraphStyle('obvious', None)
    sty.leftIndent = 18
    sty.rightIndent = 18
    sty.firstLineIndent = 18
    sty.spaceBefore = 6
    sty.spaceAfter = 6
    story.append(Paragraph("""Now for some demo stuff - we need some on this page,
        even before we explain the concepts fully""", styleSheet['BodyText']))
    p = Paragraph("""
        Platypus is all about fitting objects into frames on the page.  You
        are looking at a fairly simple Platypus paragraph in Debug mode.
        It has some gridlines drawn around it to show the left and right indents,
        and the space before and after, all of which are attributes set in
        the style sheet.  To be specific, this paragraph has left and
        right indents of 18 points, a first line indent of 36 points,
        and 6 points of space before and after itself.  A paragraph
        object fills the width of the enclosing frame, as you would expect.""", sty)

    p.debug = 1   #show me the borders
    story.append(p)

    story.append(Paragraph("""Same but with justification 1.5 extra leading and green text.""", styleSheet['BodyText']))
    p = Paragraph("""
        <para align=justify leading="+1.5" fg=green><font color=red>Platypus</font> is all about fitting objects into frames on the page.  You
        are looking at a fairly simple Platypus paragraph in Debug mode.
        It has some gridlines drawn around it to show the left and right indents,
        and the space before and after, all of which are attributes set in
        the style sheet.  To be specific, this paragraph has left and
        right indents of 18 points, a first line indent of 36 points,
        and 6 points of space before and after itself.  A paragraph
        object fills the width of the enclosing frame, as you would expect.</para>""", sty)

    p.debug = 1   #show me the borders
    story.append(p)

    story.append(platypus.XBox(4*inch, 0.75*inch,
            'This is a box with a fixed size'))

    story.append(Paragraph("""
        All of this is being drawn within a text frame which was defined
        on the page.  This frame is in 'debug' mode so you can see the border,
        and also see the margins which it reserves.  A frame does not have
        to have margins, but they have been set to 6 points each to create
        a little space around the contents.
        """, styleSheet['BodyText']))

    story.append(FrameBreak())

    #######################################################################
    #     Examples Page 2
    #######################################################################

    story.append(Paragraph("""
        Here's the base class for Flowable...
        """, styleSheet['Italic']))

    code = '''class Flowable:
        """Abstract base class for things to be drawn.  Key concepts:
    1. It knows its size
    2. It draws in its own coordinate system (this requires the
        base API to provide a translate() function.
        """
    def __init__(self):
        self.width = 0
        self.height = 0
        self.wrapped = 0

    def drawOn(self, canvas, x, y):
        "Tell it to draw itself on the canvas.  Do not override"
        self.canv = canvas
        self.canv.saveState()
        self.canv.translate(x, y)

        self.draw()   #this is the bit you overload

        self.canv.restoreState()
        del self.canv

    def wrap(self, availWidth, availHeight):
        """This will be called by the enclosing frame before objects
        are asked their size, drawn or whatever.  It returns the
        size actually used."""
        return (self.width, self.height)
    '''

    story.append(Preformatted(code, styleSheet['Code'], dedent=4))
    story.append(FrameBreak())
    #######################################################################
    #     Examples Page 3
    #######################################################################

    story.append(Paragraph(
                "Here are some examples of the remaining objects above.",
                styleSheet['Italic']))

    story.append(Paragraph("This is a bullet point", styleSheet['Bullet'], bulletText='O'))
    story.append(Paragraph("Another bullet point", styleSheet['Bullet'], bulletText='O'))


    story.append(Paragraph("""Here is a Table, which takes all kinds of formatting options...""",
                styleSheet['Italic']))
    story.append(platypus.Spacer(0, 12))

    g = platypus.Table(
            (('','North','South','East','West'),
             ('Quarter 1',100,200,300,400),
             ('Quarter 2',100,200,300,400),
             ('Total',200,400,600,800)),
            (72,36,36,36,36),
            (24, 16,16,18)
            )
    style = platypus.TableStyle([('ALIGN', (1,1), (-1,-1), 'RIGHT'),
                               ('ALIGN', (0,0), (-1,0), 'CENTRE'),
                               ('GRID', (0,0), (-1,-1), 0.25, colors.black),
                               ('LINEBELOW', (0,0), (-1,0), 2, colors.black),
                               ('LINEBELOW',(1,-1), (-1, -1), 2, (0.5, 0.5, 0.5)),
                               ('TEXTCOLOR', (0,1), (0,-1), colors.black),
                               ('BACKGROUND', (0,0), (-1,0), (0,0.7,0.7))
                               ])
    g.setStyle(style)
    story.append(g)
    story.append(FrameBreak())

    #######################################################################
    #     Examples Page 4 - custom fonts
    #######################################################################
    # custom font with LettError-Robot font
    import reportlab.rl_config
    reportlab.rl_config.warnOnMissingFontGlyphs = 0

    from reportlab.pdfbase import pdfmetrics
    fontDir = os.path.join(_RL_DIR,'fonts')
    face = pdfmetrics.EmbeddedType1Face(os.path.join(fontDir,'DarkGardenMK.afm'),os.path.join(fontDir,'DarkGardenMK.pfb'))
    faceName = face.name  # should be 'DarkGardenMK'
    pdfmetrics.registerTypeFace(face)
    font = pdfmetrics.Font(faceName, faceName, 'WinAnsiEncoding')
    pdfmetrics.registerFont(font)


    # put it inside a paragraph.
    story.append(Paragraph(
        """This is an ordinary paragraph, which happens to contain
        text in an embedded font:
        <font name="DarkGardenMK">DarkGardenMK</font>.
        Now for the real challenge...""", styleSheet['Normal']))


    styRobot = ParagraphStyle('Robot', styleSheet['Normal'])
    styRobot.fontSize = 16
    styRobot.leading = 20
    styRobot.fontName = 'DarkGardenMK'

    story.append(Paragraph(
                "This whole paragraph is 16-point DarkGardenMK.",
                styRobot))
    story.append(FrameBreak())

    if _GIF:
        story.append(Paragraph("Here is an Image flowable obtained from a string filename.",styleSheet['Italic']))
        story.append(platypus.Image(_GIF))
        story.append(Paragraph( "Here is an Image flowable obtained from a utf8 filename.", styleSheet['Italic']))
        #story.append(platypus.Image(fileName2FSEnc(_GIF)))
        story.append(Paragraph("Here is an Image flowable obtained from a string file url.",styleSheet['Italic']))
        story.append(platypus.Image(getFurl(_GIF)))
        story.append(Paragraph("Here is an Image flowable obtained from an open file.",styleSheet['Italic']))
        story.append(platypus.Image(open_for_read(_GIF,'b')))
        story.append(FrameBreak())
        try:
            img = platypus.Image('http://www.reportlab.com/rsrc/encryption.gif')
            story.append(Paragraph("Here is an Image flowable obtained from a string http url.",styleSheet['Italic']))
            story.append(img)
        except:
            story.append(Paragraph("The image could not be obtained from a string http url.",styleSheet['Italic']))
        story.append(FrameBreak())

    if _JPG:
        img = platypus.Image(_JPG)
        story.append(Paragraph("Here is an JPEG Image flowable obtained from a filename.",styleSheet['Italic']))
        story.append(img)
        story.append(Paragraph("Here is an JPEG Image flowable obtained from an open file.",styleSheet['Italic']))
        img = platypus.Image(open_for_read(_JPG,'b'))
        story.append(img)
        story.append(FrameBreak())


    return story

class AndyTemplate(BaseDocTemplate):
    _invalidInitArgs = ('pageTemplates',)
    def __init__(self, filename, **kw):
        frame1 = platypus.Frame(inch, 5.6*inch, 6*inch, 5.2*inch,id='F1')
        frame2 = platypus.Frame(inch, inch, 6*inch, 4.5*inch, showBoundary=1,id='F2')
        self.allowSplitting = 0
        BaseDocTemplate.__init__(self,filename,**kw)
        self.addPageTemplates(PageTemplate('normal',[frame1,frame2],framePage))

    def fillFrame(self,flowables):
        f = self.frame
        while len(flowables)>0 and f is self.frame:
            self.handle_flowable(flowables)

    def build(self, flowables1, flowables2):
        assert [x for x in flowables1 if not isinstance(x,Flowable)]==[], "flowables1 argument error"
        assert [x for x in flowables2 if not isinstance(x,Flowable)]==[], "flowables2 argument error"
        self._startBuild()
        while (len(flowables1) > 0 or len(flowables1) > 0):
            self.clean_hanging()
            self.fillFrame(flowables1)
            self.fillFrame(flowables2)

        self._endBuild()

def showProgress(pageNo):
    print('CALLBACK SAYS: page %d' % pageNo)


def run():
    doc = AndyTemplate(outputfile('test_platypus_general.pdf'),subject='test0')
    #doc.setPageCallBack(showProgress)
    commentary = getCommentary()
    examples = getExamples()
    doc.build(commentary,examples)


class PlatypusTestCase(unittest.TestCase):
    "Make documents with lots of Platypus features"

    def test0(self):
        "Make a platypus document"
        run()

    def test1(self):
        #test from Wietse Jacobs
        from reportlab.lib.styles import ParagraphStyle
        from reportlab.graphics.shapes import Drawing, Rect
        from reportlab.platypus import SimpleDocTemplate
        normal = ParagraphStyle(name='Normal', fontName='Helvetica', fontSize=8.5, leading=11)
        header = ParagraphStyle(name='Heading1', parent=normal, fontSize=14, leading=19,
                    spaceAfter=6, keepWithNext=1)
        d = Drawing(400, 200)
        d.add(Rect(50, 50, 300, 100))

        story = [Paragraph("The section header", header), d,
                ]
        doc = SimpleDocTemplate(outputfile('test_drawing_keepwithnext.pdf'))
        doc.build(story)

def makeSuite():
    return makeSuiteForClasses(PlatypusTestCase)

#noruntests
if __name__ == "__main__":
    if '-debug' in sys.argv:
        run()
    else:
        unittest.TextTestRunner().run(makeSuite())
        printLocation()