bzr revid: pinky-b6477822105ee9382f0078db43cf51da9cf0321a
This commit is contained in:
pinky 2007-01-07 23:36:48 +00:00
parent 1e479b40d6
commit 131e3f481e
56 changed files with 11213 additions and 0 deletions

View File

@ -0,0 +1,20 @@
This directory should contain all test scripts.
All test scripts should be named like test_*.py, where * describes which
parts of the ReportLab toolkit are being tested (see sample test scripts).
The test scripts are expected to make use of the PyUnit test environment
named unittest (see pyunit.sourceforge.net). For convenience this comes
bundled with the ReportLab toolkit in the reportlab.test subpackage.
As of now, this folder has a flat structure, but it might be restructured
in the future as the amount of tests will grow dramatically.
The file runAll.py begins by deleting any files with extension ".pdf" or
".log" in the test directory, so you can't confuse old and current
test output. It then loops over all test scripts following the
aforementioned pattern and executes them.
Any PDF or log files written should be examined, at least before
major releases. If you are writing tests, ensure that you only
leave behind files which you intend a human to check.

View File

@ -0,0 +1,3 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/__init__.py

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,93 @@
#!/usr/bin/env python
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/runAll.py
"""Runs all test files in all subfolders.
"""
import os, glob, sys, string, traceback
from reportlab.test import unittest
from reportlab.test.utils import GlobDirectoryWalker, outputfile, printLocation
def makeSuite(folder, exclude=[],nonImportable=[],pattern='test_*.py'):
"Build a test suite of all available test files."
allTests = unittest.TestSuite()
if os.path.isdir(folder): sys.path.insert(0, folder)
for filename in GlobDirectoryWalker(folder, pattern):
modname = os.path.splitext(os.path.basename(filename))[0]
if modname not in exclude:
try:
exec 'import %s as module' % modname
allTests.addTest(module.makeSuite())
except:
tt, tv, tb = sys.exc_info()[:]
nonImportable.append((filename,traceback.format_exception(tt,tv,tb)))
del tt,tv,tb
del sys.path[0]
return allTests
def main(pattern='test_*.py'):
try:
folder = os.path.dirname(__file__)
assert folder
except:
folder = os.path.dirname(sys.argv[0]) or os.getcwd()
#allow for Benn's "screwball cygwin distro":
if folder == '':
folder = '.'
from reportlab.lib.utils import isSourceDistro
haveSRC = isSourceDistro()
def cleanup(folder,patterns=('*.pdf', '*.log','*.svg','runAll.txt', 'test_*.txt','_i_am_actually_a_*.*')):
if not folder: return
for pat in patterns:
for filename in GlobDirectoryWalker(folder, pattern=pat):
try:
os.remove(filename)
except:
pass
# special case for reportlab/test directory - clean up
# all PDF & log files before starting run. You don't
# want this if reusing runAll anywhere else.
if string.find(folder, 'reportlab' + os.sep + 'test') > -1: cleanup(folder)
cleanup(outputfile(''))
NI = []
cleanOnly = '--clean' in sys.argv
if not cleanOnly:
testSuite = makeSuite(folder,nonImportable=NI,pattern=pattern+(not haveSRC and 'c' or ''))
unittest.TextTestRunner().run(testSuite)
if haveSRC: cleanup(folder,patterns=('*.pyc','*.pyo'))
if not cleanOnly:
if NI:
sys.stderr.write('\n###################### the following tests could not be imported\n')
for f,tb in NI:
print 'file: "%s"\n%s\n' % (f,string.join(tb,''))
printLocation()
def mainEx():
'''for use in subprocesses'''
try:
main()
finally:
sys.stdout.flush()
sys.stderr.flush()
sys.stdout.close()
os.close(sys.stderr.fileno())
def runExternally():
cmd = sys.executable+' -c"from reportlab.test import runAll;runAll.mainEx()"'
i,o,e=os.popen3(cmd)
i.close()
out = o.read()
err=e.read()
return '\n'.join((out,err))
def checkForFailure(outerr):
return '\nFAILED' in outerr
if __name__ == '__main__': #noruntests
main()

View File

@ -0,0 +1,279 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_charts_textlabels.py
"""
Tests for the text Label class.
"""
import os, sys, copy
from os.path import join, basename, splitext
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.lib import colors
from reportlab.lib.units import cm
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.pdfgen.canvas import Canvas
from reportlab.graphics.shapes import *
from reportlab.graphics.charts.textlabels import Label
from reportlab.platypus.flowables import Spacer, PageBreak
from reportlab.platypus.paragraph import Paragraph
from reportlab.platypus.xpreformatted import XPreformatted
from reportlab.platypus.frames import Frame
from reportlab.platypus.doctemplate import PageTemplate, BaseDocTemplate
def myMainPageFrame(canvas, doc):
"The page frame used for all PDF documents."
canvas.saveState()
#canvas.rect(2.5*cm, 2.5*cm, 15*cm, 25*cm)
canvas.setFont('Times-Roman', 12)
pageNumber = canvas.getPageNumber()
canvas.drawString(10*cm, cm, str(pageNumber))
canvas.restoreState()
class MyDocTemplate(BaseDocTemplate):
"The document template used for all PDF documents."
_invalidInitArgs = ('pageTemplates',)
def __init__(self, filename, **kw):
frame1 = Frame(2.5*cm, 2.5*cm, 15*cm, 25*cm, id='F1')
self.allowSplitting = 0
apply(BaseDocTemplate.__init__, (self, filename), kw)
template = PageTemplate('normal', [frame1], myMainPageFrame)
self.addPageTemplates(template)
class LabelTestCase(unittest.TestCase):
"Test Label class."
def _test0(self):
"Perform original test function."
pdfPath = outputfile('test_charts_textlabels.pdf')
c = Canvas(pdfPath)
label = Label()
demoLabel = label.demo()
demoLabel.drawOn(c, 0, 0)
c.save()
def _makeProtoLabel(self):
"Return a label prototype for further modification."
protoLabel = Label()
protoLabel.dx = 0
protoLabel.dy = 0
protoLabel.boxStrokeWidth = 0.1
protoLabel.boxStrokeColor = colors.black
protoLabel.boxFillColor = colors.yellow
# protoLabel.text = 'Hello World!' # Does not work as expected.
return protoLabel
def _makeDrawings(self, protoLabel, text=None):
# Set drawing dimensions.
w, h = drawWidth, drawHeight = 400, 100
drawings = []
for boxAnchors in ('sw se nw ne', 'w e n s', 'c'):
boxAnchors = string.split(boxAnchors, ' ')
# Create drawing.
d = Drawing(w, h)
d.add(Line(0,h/2, w, h/2, strokeColor=colors.gray, strokeWidth=0.5))
d.add(Line(w/2,0, w/2, h, strokeColor=colors.gray, strokeWidth=0.5))
labels = []
for boxAnchor in boxAnchors:
# Modify label, put it on a drawing.
label = copy.deepcopy(protoLabel)
label.boxAnchor = boxAnchor
args = {'ba':boxAnchor, 'text':text or 'Hello World!'}
label.setText('(%(ba)s) %(text)s (%(ba)s)' % args)
labels.append(label)
for label in labels:
d.add(label)
drawings.append(d)
return drawings
def test1(self):
"Test all different box anchors."
# Build story.
story = []
styleSheet = getSampleStyleSheet()
bt = styleSheet['BodyText']
h1 = styleSheet['Heading1']
h2 = styleSheet['Heading2']
h3 = styleSheet['Heading3']
story.append(Paragraph('Tests for class <i>Label</i>', h1))
story.append(Paragraph('Testing box anchors', h2))
story.append(Paragraph("""This should display "Hello World" labels
written as black text on a yellow box relative to the origin of the crosshair
axes. The labels indicate their relative position being one of the nine
canonical points of a box: sw, se, nw, ne, w, e, n, s or c (standing for
<i>southwest</i>, <i>southeast</i>... and <i>center</i>).""", bt))
story.append(Spacer(0, 0.5*cm))
# Round 1a
story.append(Paragraph('Helvetica 10pt', h3))
story.append(Spacer(0, 0.5*cm))
w, h = drawWidth, drawHeight = 400, 100
protoLabel = self._makeProtoLabel()
protoLabel.setOrigin(drawWidth/2, drawHeight/2)
protoLabel.textAnchor = 'start'
protoLabel.fontName = 'Helvetica'
protoLabel.fontSize = 10
drawings = self._makeDrawings(protoLabel)
for d in drawings:
story.append(d)
story.append(Spacer(0, 1*cm))
story.append(PageBreak())
# Round 1b
story.append(Paragraph('Helvetica 18pt', h3))
story.append(Spacer(0, 0.5*cm))
w, h = drawWidth, drawHeight = 400, 100
protoLabel = self._makeProtoLabel()
protoLabel.setOrigin(drawWidth/2, drawHeight/2)
protoLabel.textAnchor = 'start'
protoLabel.fontName = 'Helvetica'
protoLabel.fontSize = 18
drawings = self._makeDrawings(protoLabel)
for d in drawings:
story.append(d)
story.append(Spacer(0, 1*cm))
story.append(PageBreak())
# Round 1c
story.append(Paragraph('Helvetica 18pt, multi-line', h3))
story.append(Spacer(0, 0.5*cm))
w, h = drawWidth, drawHeight = 400, 100
protoLabel = self._makeProtoLabel()
protoLabel.setOrigin(drawWidth/2, drawHeight/2)
protoLabel.textAnchor = 'start'
protoLabel.fontName = 'Helvetica'
protoLabel.fontSize = 18
drawings = self._makeDrawings(protoLabel, text='Hello\nWorld!')
for d in drawings:
story.append(d)
story.append(Spacer(0, 1*cm))
story.append(PageBreak())
story.append(Paragraph('Testing text (and box) anchors', h2))
story.append(Paragraph("""This should display labels as before,
but now with a fixes size and showing some effect of setting the
textAnchor attribute.""", bt))
story.append(Spacer(0, 0.5*cm))
# Round 2a
story.append(Paragraph("Helvetica 10pt, textAnchor='start'", h3))
story.append(Spacer(0, 0.5*cm))
w, h = drawWidth, drawHeight = 400, 100
protoLabel = self._makeProtoLabel()
protoLabel.setOrigin(drawWidth/2, drawHeight/2)
protoLabel.width = 4*cm
protoLabel.height = 1.5*cm
protoLabel.textAnchor = 'start'
protoLabel.fontName = 'Helvetica'
protoLabel.fontSize = 10
drawings = self._makeDrawings(protoLabel)
for d in drawings:
story.append(d)
story.append(Spacer(0, 1*cm))
story.append(PageBreak())
# Round 2b
story.append(Paragraph("Helvetica 10pt, textAnchor='middle'", h3))
story.append(Spacer(0, 0.5*cm))
w, h = drawWidth, drawHeight = 400, 100
protoLabel = self._makeProtoLabel()
protoLabel.setOrigin(drawWidth/2, drawHeight/2)
protoLabel.width = 4*cm
protoLabel.height = 1.5*cm
protoLabel.textAnchor = 'middle'
protoLabel.fontName = 'Helvetica'
protoLabel.fontSize = 10
drawings = self._makeDrawings(protoLabel)
for d in drawings:
story.append(d)
story.append(Spacer(0, 1*cm))
story.append(PageBreak())
# Round 2c
story.append(Paragraph("Helvetica 10pt, textAnchor='end'", h3))
story.append(Spacer(0, 0.5*cm))
w, h = drawWidth, drawHeight = 400, 100
protoLabel = self._makeProtoLabel()
protoLabel.setOrigin(drawWidth/2, drawHeight/2)
protoLabel.width = 4*cm
protoLabel.height = 1.5*cm
protoLabel.textAnchor = 'end'
protoLabel.fontName = 'Helvetica'
protoLabel.fontSize = 10
drawings = self._makeDrawings(protoLabel)
for d in drawings:
story.append(d)
story.append(Spacer(0, 1*cm))
story.append(PageBreak())
# Round 2d
story.append(Paragraph("Helvetica 10pt, multi-line, textAnchor='start'", h3))
story.append(Spacer(0, 0.5*cm))
w, h = drawWidth, drawHeight = 400, 100
protoLabel = self._makeProtoLabel()
protoLabel.setOrigin(drawWidth/2, drawHeight/2)
protoLabel.width = 4*cm
protoLabel.height = 1.5*cm
protoLabel.textAnchor = 'start'
protoLabel.fontName = 'Helvetica'
protoLabel.fontSize = 10
drawings = self._makeDrawings(protoLabel, text='Hello\nWorld!')
for d in drawings:
story.append(d)
story.append(Spacer(0, 1*cm))
story.append(PageBreak())
path = outputfile('test_charts_textlabels.pdf')
doc = MyDocTemplate(path)
doc.multiBuild(story)
def makeSuite():
return makeSuiteForClasses(LabelTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,51 @@
"""Tests that all manuals can be built.
"""
import os, sys
from reportlab.test import unittest
from reportlab.test.utils import SecureTestCase, printLocation
class ManualTestCase(SecureTestCase):
"Runs all 3 manual-builders from the top."
def test0(self):
"Test if all manuals buildable from source."
import reportlab
rlFolder = os.path.dirname(reportlab.__file__)
docsFolder = os.path.join(rlFolder, 'docs')
os.chdir(docsFolder)
if os.path.isfile('userguide.pdf'):
os.remove('userguide.pdf')
if os.path.isfile('graphguide.pdf'):
os.remove('graphguide.pdf')
if os.path.isfile('reference.pdf'):
os.remove('reference.pdf')
if os.path.isfile('graphics_reference.pdf'):
os.remove('graphics_reference.pdf')
os.system("%s genAll.py -s" % sys.executable)
assert os.path.isfile('userguide.pdf'), 'genAll.py failed to generate userguide.pdf!'
assert os.path.isfile('graphguide.pdf'), 'genAll.py failed to generate graphguide.pdf!'
assert os.path.isfile('reference.pdf'), 'genAll.py failed to generate reference.pdf!'
assert os.path.isfile('graphics_reference.pdf'), 'genAll.py failed to generate graphics_reference.pdf!'
def makeSuite():
suite = unittest.TestSuite()
loader = unittest.TestLoader()
if sys.platform[:4] != 'java':
suite.addTest(loader.loadTestsFromTestCase(ManualTestCase))
return suite
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,175 @@
#!/usr/bin/env python
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_docstrings.py
"""This is a test on a package level that find all modules,
classes, methods and functions that do not have a doc string
and lists them in individual log files.
Currently, methods with leading and trailing double underscores
are skipped.
"""
import os, sys, glob, string, re
from types import ModuleType, ClassType, MethodType, FunctionType
import reportlab
from reportlab.test import unittest
from reportlab.test.utils import SecureTestCase, GlobDirectoryWalker, outputfile, printLocation
RL_HOME = os.path.dirname(reportlab.__file__)
def getModuleObjects(folder, rootName, typ, pattern='*.py'):
"Get a list of all objects defined *somewhere* in a package."
# Define some abbreviations.
find = string.find
split = string.split
replace = string.replace
objects = []
lookup = {}
for file in GlobDirectoryWalker(folder, pattern):
folder = os.path.dirname(file)
if os.path.basename(file) == '__init__.py':
continue
## if os.path.exists(os.path.join(folder, '__init__.py')):
#### print 'skipping', os.path.join(folder, '__init__.py')
## continue
sys.path.insert(0, folder)
cwd = os.getcwd()
os.chdir(folder)
modName = os.path.splitext(os.path.basename(file))[0]
prefix = folder[find(folder, rootName):]
prefix = replace(prefix, os.sep, '.')
mName = prefix + '.' + modName
try:
module = __import__(mName)
except ImportError:
# Restore sys.path and working directory.
os.chdir(cwd)
del sys.path[0]
continue
# Get the 'real' (leaf) module
# (__import__ loads only the top-level one).
if find(mName, '.') != -1:
for part in split(mName, '.')[1:]:
module = getattr(module, part)
# Find the objects in the module's content.
modContentNames = dir(module)
# Handle modules.
if typ == ModuleType:
if find(module.__name__, 'reportlab') > -1:
objects.append((mName, module))
continue
for n in modContentNames:
obj = eval(mName + '.' + n)
# Handle functions and classes.
if typ in (FunctionType, ClassType):
if type(obj) == typ and not lookup.has_key(obj):
if typ == ClassType:
if find(obj.__module__, rootName) != 0:
continue
objects.append((mName, obj))
lookup[obj] = 1
# Handle methods.
elif typ == MethodType:
if type(obj) == ClassType:
for m in dir(obj):
a = getattr(obj, m)
if type(a) == typ and not lookup.has_key(a):
if find(a.im_class.__module__, rootName) != 0:
continue
cName = obj.__name__
objects.append((mName, a))
lookup[a] = 1
# Restore sys.path and working directory.
os.chdir(cwd)
del sys.path[0]
return objects
class DocstringTestCase(SecureTestCase):
"Testing if objects in the ReportLab package have docstrings."
def _writeLogFile(self, objType):
"Write log file for different kind of documentable objects."
cwd = os.getcwd()
objects = getModuleObjects(RL_HOME, 'reportlab', objType)
objects.sort()
os.chdir(cwd)
expl = {FunctionType:'functions',
ClassType:'classes',
MethodType:'methods',
ModuleType:'modules'}[objType]
path = outputfile("test_docstrings-%s.log" % expl)
file = open(path, 'w')
file.write('No doc strings found for the following %s below.\n\n' % expl)
p = re.compile('__.+__')
lines = []
for name, obj in objects:
if objType == MethodType:
n = obj.__name__
# Skip names with leading and trailing double underscores.
if p.match(n):
continue
if objType == FunctionType:
if not obj.__doc__ or len(obj.__doc__) == 0:
lines.append("%s.%s\n" % (name, obj.__name__))
else:
if not obj.__doc__ or len(obj.__doc__) == 0:
if objType == ClassType:
lines.append("%s.%s\n" % (obj.__module__, obj.__name__))
elif objType == MethodType:
lines.append("%s.%s\n" % (obj.im_class, obj.__name__))
else:
lines.append("%s\n" % (obj.__name__))
lines.sort()
for line in lines:
file.write(line)
file.close()
def test0(self):
"Test if functions have a doc string."
self._writeLogFile(FunctionType)
def test1(self):
"Test if classes have a doc string."
self._writeLogFile(ClassType)
def test2(self):
"Test if methods have a doc string."
self._writeLogFile(MethodType)
def test3(self):
"Test if modules have a doc string."
self._writeLogFile(ModuleType)
def makeSuite():
suite = unittest.TestSuite()
loader = unittest.TestLoader()
if sys.platform[:4] != 'java': suite.addTest(loader.loadTestsFromTestCase(DocstringTestCase))
return suite
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,102 @@
"""This executes tests defined outside the normal test suite.
See docstring for class ExternalTestCase for more information.
"""
import os, string, fnmatch, re, sys
import reportlab
from reportlab.test import unittest
from reportlab.test.utils import SecureTestCase, printLocation
RL_HOME = os.path.dirname(reportlab.__file__)
EXTRA_FILE = 'extra.txt'
class ExternalTestCase(SecureTestCase):
"""Test case starting cases external to the normal RL suite.
This test case runs additional test cases defined in external
modules which must also be using the unittest framework.
These additional modules must be defined in a file named
'extra.txt' which needs to be located in the reportlab/test
folder and contains one path per line.
The paths of these modules can contain stuff from the Unix
world like '~', '.', '..' and '$HOME' and can have absolute
or relative paths. If they are relative they start from the
reportlab/test folder.
This is a sample 'extra.txt' file:
foo.py
./foo.py
bar/foo.py
../foo.py
/bar/foo.py
~/foo.py
~/bar/foo.py
~/../foo.py
$HOME/bar/foo.py
"""
def test0(self):
"Execute external test cases."
cwd = os.getcwd()
# look for a file named 'extra.txt' in test directory,
# exit if not found
extraFilename = os.path.join(RL_HOME, 'test', EXTRA_FILE)
if not os.path.exists(extraFilename):
return
# read module paths from file
extraModulenames = open(extraFilename).readlines()
extraModulenames = map(string.strip, extraModulenames)
# expand pathnames as much as possible
for f in extraModulenames:
if f == '':
continue
if f[0] == '#':
continue
f = os.path.expanduser(f)
f = os.path.expandvars(f)
f = os.path.abspath(f)
if os.path.exists(f):
# look for a makeSuite function and execute it if present
folder = os.path.abspath(os.path.dirname(f))
modname = os.path.splitext(os.path.basename(f))[0]
os.chdir(folder)
sys.path.insert(0, folder)
module = __import__(modname) # seems to fail sometimes...
if 'makeSuite' in dir(module):
print "running", f
testSuite = module.makeSuite()
unittest.TextTestRunner().run(testSuite)
os.chdir(cwd)
sys.path = sys.path[1:]
def makeSuite():
suite = unittest.TestSuite()
loader = unittest.TestLoader()
if sys.platform[:4] != 'java':
suite.addTest(loader.loadTestsFromTestCase(ExternalTestCase))
return suite
#noruntests
if __name__ == "__main__":
if len(sys.argv) > 1:
EXTRA_FILE = sys.argv[1]
assert os.path.isfile(EXTRA_FILE), 'file %s not found!' % EXTRA_FILE
# otherwise, extra.txt will be used
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,423 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_graphics_charts.py
"""
Tests for chart class.
"""
import os, sys, copy
from os.path import join, basename, splitext
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.lib import colors
from reportlab.lib.units import cm
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.validators import Auto
from reportlab.pdfgen.canvas import Canvas
from reportlab.graphics.shapes import *
from reportlab.graphics.charts.textlabels import Label
from reportlab.platypus.flowables import Spacer, PageBreak
from reportlab.platypus.paragraph import Paragraph
from reportlab.platypus.xpreformatted import XPreformatted
from reportlab.platypus.frames import Frame
from reportlab.platypus.doctemplate \
import PageTemplate, BaseDocTemplate
from reportlab.graphics.charts.barcharts import VerticalBarChart
from reportlab.graphics.charts.linecharts import HorizontalLineChart
from reportlab.graphics.charts.lineplots import LinePlot
from reportlab.graphics.charts.piecharts import Pie
from reportlab.graphics.charts.legends import Legend
from reportlab.graphics.charts.spider import SpiderChart
from reportlab.graphics.widgets.markers import makeMarker
def myMainPageFrame(canvas, doc):
"The page frame used for all PDF documents."
canvas.saveState()
#canvas.rect(2.5*cm, 2.5*cm, 15*cm, 25*cm)
canvas.setFont('Times-Roman', 12)
pageNumber = canvas.getPageNumber()
canvas.drawString(10*cm, cm, str(pageNumber))
canvas.restoreState()
class MyDocTemplate(BaseDocTemplate):
"The document template used for all PDF documents."
_invalidInitArgs = ('pageTemplates',)
def __init__(self, filename, **kw):
frame1 = Frame(2.5*cm, 2.5*cm, 15*cm, 25*cm, id='F1')
self.allowSplitting = 0
apply(BaseDocTemplate.__init__, (self, filename), kw)
template = PageTemplate('normal', [frame1], myMainPageFrame)
self.addPageTemplates(template)
def sample1bar(data=[(13, 5, 20, 22, 37, 45, 19, 4)]):
drawing = Drawing(400, 200)
bc = VerticalBarChart()
bc.x = 50
bc.y = 50
bc.height = 125
bc.width = 300
bc.data = data
bc.strokeColor = colors.black
bc.valueAxis.valueMin = 0
bc.valueAxis.valueMax = 60
bc.valueAxis.valueStep = 15
bc.categoryAxis.labels.boxAnchor = 'ne'
bc.categoryAxis.labels.dx = 8
bc.categoryAxis.labels.dy = -2
bc.categoryAxis.labels.angle = 30
catNames = string.split('Jan Feb Mar Apr May Jun Jul Aug', ' ')
catNames = map(lambda n:n+'-99', catNames)
bc.categoryAxis.categoryNames = catNames
drawing.add(bc)
return drawing
def sample2bar(data=[(13, 5, 20, 22, 37, 45, 19, 4),
(14, 6, 21, 23, 38, 46, 20, 5)]):
return sample1bar(data)
def sample1line(data=[(13, 5, 20, 22, 37, 45, 19, 4)]):
drawing = Drawing(400, 200)
bc = HorizontalLineChart()
bc.x = 50
bc.y = 50
bc.height = 125
bc.width = 300
bc.data = data
bc.strokeColor = colors.black
bc.valueAxis.valueMin = 0
bc.valueAxis.valueMax = 60
bc.valueAxis.valueStep = 15
bc.categoryAxis.labels.boxAnchor = 'ne'
bc.categoryAxis.labels.dx = 8
bc.categoryAxis.labels.dy = -2
bc.categoryAxis.labels.angle = 30
catNames = string.split('Jan Feb Mar Apr May Jun Jul Aug', ' ')
catNames = map(lambda n:n+'-99', catNames)
bc.categoryAxis.categoryNames = catNames
drawing.add(bc)
return drawing
def sample2line(data=[(13, 5, 20, 22, 37, 45, 19, 4),
(14, 6, 21, 23, 38, 46, 20, 5)]):
return sample1line(data)
def sample3(drawing=None):
"Add sample swatches to a diagram."
d = drawing or Drawing(400, 200)
swatches = Legend()
swatches.alignment = 'right'
swatches.x = 80
swatches.y = 160
swatches.deltax = 60
swatches.dxTextSpace = 10
swatches.columnMaximum = 4
items = [(colors.red, 'before'), (colors.green, 'after')]
swatches.colorNamePairs = items
d.add(swatches, 'legend')
return d
def sample4pie():
width = 300
height = 150
d = Drawing(width, height)
pc = Pie()
pc.x = 150
pc.y = 50
pc.data = [1, 50, 100, 100, 100, 100, 100, 100, 100, 50]
pc.labels = ['0','a','b','c','d','e','f','g','h','i']
pc.slices.strokeWidth=0.5
pc.slices[3].popout = 20
pc.slices[3].strokeWidth = 2
pc.slices[3].strokeDashArray = [2,2]
pc.slices[3].labelRadius = 1.75
pc.slices[3].fontColor = colors.red
d.add(pc)
legend = Legend()
legend.x = width-5
legend.y = height-5
legend.dx = 20
legend.dy = 5
legend.deltax = 0
legend.boxAnchor = 'nw'
legend.colorNamePairs=Auto(chart=pc)
d.add(legend)
return d
def autoLegender(i,chart,styleObj,sym='symbol'):
if sym:
setattr(styleObj[0],sym, makeMarker('Diamond',size=6))
setattr(styleObj[1],sym,makeMarker('Square'))
width = 300
height = 150
legend = Legend()
legend.x = width-5
legend.y = 5
legend.dx = 20
legend.dy = 5
legend.deltay = 0
legend.boxAnchor = 'se'
if i=='col auto':
legend.colorNamePairs[0]=(Auto(chart=chart),'auto chart=self.chart')
legend.colorNamePairs[1]=(Auto(obj=chart,index=1),'auto chart=self.chart index=1')
elif i=='full auto':
legend.colorNamePairs=Auto(chart=chart)
elif i=='swatch set':
legend.swatchMarker=makeMarker('Circle')
legend.swatchMarker.size = 10
elif i=='swatch auto':
legend.swatchMarker=Auto(chart=chart)
d = Drawing(width,height)
d.background = Rect(0,0,width,height,strokeWidth=1,strokeColor=colors.red,fillColor=None)
m = makeMarker('Cross')
m.x = width-5
m.y = 5
m.fillColor = colors.red
m.strokeColor = colors.yellow
d.add(chart)
d.add(legend)
d.add(m)
return d
def lpleg(i=None):
chart = LinePlot()
return autoLegender(i,chart,chart.lines)
def hlcleg(i=None):
chart = HorizontalLineChart()
return autoLegender(i,chart,chart.lines)
def bcleg(i=None):
chart = VerticalBarChart()
return autoLegender(i,chart,chart.bars,None)
def pcleg(i=None):
chart = Pie()
return autoLegender(i,chart,chart.slices,None)
def scleg(i=None):
chart = SpiderChart()
return autoLegender(i,chart,chart.strands,None)
def plpleg(i=None):
from reportlab.lib.colors import pink, red, green
pie = Pie()
pie.x = 0
pie.y = 0
pie.pointerLabelMode='LeftAndRight'
pie.slices.label_boxStrokeColor = red
pie.simpleLabels = 0
pie.sameRadii = 1
pie.data = [1, 0.1, 1.7, 4.2,0,0]
pie.labels = ['abcdef', 'b', 'c', 'd','e','fedcba']
pie.strokeWidth=1
pie.strokeColor=green
pie.slices.label_pointer_piePad = 6
pie.width = 160
pie.direction = 'clockwise'
pie.pointerLabelMode = 'LeftRight'
return autoLegender(i,pie,pie.slices,None)
def notFail(d):
try:
return d.getContents()
except:
import traceback
traceback.print_exc()
return None
STORY = []
styleSheet = getSampleStyleSheet()
bt = styleSheet['BodyText']
h1 = styleSheet['Heading1']
h2 = styleSheet['Heading2']
h3 = styleSheet['Heading3']
FINISHED = 0
class ChartTestCase(unittest.TestCase):
"Test chart classes."
def setUp(self):
"Hook method for setting up the test fixture before exercising it."
global STORY
self.story = STORY
if self.story == []:
self.story.append(Paragraph('Tests for chart classes', h1))
def tearDown(self):
"Hook method for deconstructing the test fixture after testing it."
if FINISHED:
path=outputfile('test_graphics_charts.pdf')
doc = MyDocTemplate(path)
doc.build(self.story)
def test0(self):
"Test bar charts."
story = self.story
story.append(Paragraph('Single data row', h2))
story.append(Spacer(0, 0.5*cm))
drawing = sample1bar()
story.append(drawing)
story.append(Spacer(0, 1*cm))
def test1(self):
"Test bar charts."
story = self.story
story.append(Paragraph('Double data row', h2))
story.append(Spacer(0, 0.5*cm))
drawing = sample2bar()
story.append(drawing)
story.append(Spacer(0, 1*cm))
def test2(self):
"Test bar charts."
story = self.story
story.append(Paragraph('Double data row with legend', h2))
story.append(Spacer(0, 0.5*cm))
drawing = sample2bar()
drawing = sample3(drawing)
story.append(drawing)
story.append(Spacer(0, 1*cm))
def test3(self):
"Test line charts."
story = self.story
story.append(Paragraph('Single data row', h2))
story.append(Spacer(0, 0.5*cm))
drawing = sample1line()
story.append(drawing)
story.append(Spacer(0, 1*cm))
def test4(self):
"Test line charts."
story = self.story
story.append(Paragraph('Single data row', h2))
story.append(Spacer(0, 0.5*cm))
drawing = sample2line()
story.append(drawing)
story.append(Spacer(0, 1*cm))
def test4b(self):
story = self.story
for code in (lpleg, hlcleg, bcleg, pcleg, scleg, plpleg):
code_name = code.func_code.co_name
for i in ('standard', 'col auto', 'full auto', 'swatch set', 'swatch auto'):
d = code(i)
assert notFail(d),'getContents failed for %s %s' % (code_name,i)
story.append(Paragraph('%s %s' % (i,code_name), h2))
story.append(Spacer(0, 0.5*cm))
story.append(code(i))
story.append(Spacer(0, 1*cm))
def test5(self):
"Test pie charts."
story = self.story
story.append(Paragraph('Pie', h2))
story.append(Spacer(0, 0.5*cm))
drawing = sample4pie()
story.append(drawing)
story.append(Spacer(0, 1*cm))
def test6(self):
from reportlab.graphics.charts.piecharts import Pie, _makeSideArcDefs, intervalIntersection
L = []
def intSum(arcs,A):
s = 0
for a in A:
for arc in arcs:
i = intervalIntersection(arc[1:],a)
if i: s += i[1] - i[0]
return s
def subtest(sa,d):
pc = Pie()
pc.direction=d
pc.startAngle=sa
arcs = _makeSideArcDefs(sa,d)
A = [x[1] for x in pc.makeAngles()]
arcsum = sum([a[2]-a[1] for a in arcs])
isum = intSum(arcs,A)
mi = max([a[2]-a[1] for a in arcs])
ni = min([a[2]-a[1] for a in arcs])
l = []
s = (arcsum-360)
if s>1e-8: l.append('Arc length=%s != 360' % s)
s = abs(isum-360)
if s>1e-8: l.append('interval intersection length=%s != 360' % s)
if mi>360: l.append('max interval intersection length=%s >360' % mi)
if ni<0: l.append('min interval intersection length=%s <0' % ni)
if l:
l.append('sa: %s d: %s' % (sa,d))
l.append('sidearcs: %s' % str(arcs))
l.append('Angles: %s' % A)
raise ValueError('piecharts._makeSideArcDefs failure\n%s' % '\n'.join(l))
for d in ('anticlockwise','clockwise'):
for sa in (225, 180, 135, 90, 45, 0, -45, -90):
subtest(sa,d)
# This triggers the document build operation (hackish).
global FINISHED
FINISHED = 1
def makeSuite():
return makeSuiteForClasses(ChartTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,91 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
"""
Tests for RLG Image shapes.
"""
import os
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.graphics.shapes import Image, Drawing
from reportlab.graphics import renderPDF
from reportlab.lib.pagesizes import A4
IMAGES = []
IMAGENAME = 'cs_logo.gif'
IMAGENAME = 'pythonpowered.gif'
class ImageTestCase(unittest.TestCase):
"Test RLG Image shape."
def __del__(self):
if IMAGES[-1] != None:
return
else:
del IMAGES[-1]
d = Drawing(A4[0], A4[1])
for img in IMAGES:
d.add(img)
outPath = outputfile("test_graphics_images.pdf")
renderPDF.drawToFile(d, outPath) #, '')
assert os.path.exists(outPath) == 1
def test0(self):
"Test convert a bitmap file as Image shape into a tmp. PDF file."
d = Drawing(110, 44)
inPath = IMAGENAME
img = Image(0, 0, 110, 44, inPath)
d.add(img)
IMAGES.append(img)
def test1(self):
"Test Image shape, adding it to a PDF page."
inPath = IMAGENAME
img = Image(0, 0, 110, 44, inPath)
IMAGES.append(img)
def test2(self):
"Test scaled Image shape adding it to a PDF page."
inPath = IMAGENAME
img = Image(0, 0, 110, 44, inPath)
d = Drawing(110, 44)
d.add(img)
d.translate(120, 0)
d.scale(2, 2)
IMAGES.append(d)
def test3(self):
"Test rotated Image shape adding it to a PDF page."
inPath = IMAGENAME
img = Image(0, 0, 110, 44, inPath)
d = Drawing(110, 44)
d.add(img)
d.translate(420, 0)
d.scale(2, 2)
d.rotate(45)
IMAGES.append(d)
IMAGES.append(None) # used to indicate last test
def makeSuite():
return makeSuiteForClasses(ImageTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,82 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_graphics_layout.py
"""
Tests for getBounds methods of various graphical widgets
"""
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, printLocation
from reportlab.graphics import shapes
##from reportlab.graphics.charts.barcharts import VerticalBarChart
##from reportlab.graphics.charts.linecharts import HorizontalLineChart
##from reportlab.graphics.charts.piecharts import Pie
##from reportlab.graphics.charts.legends import Legend
class BoundsTestCase(unittest.TestCase):
def testLine(self):
s = shapes.Line(10,20,30,40)
assert s.getBounds() == (10,20,30,40)
def testRect(self):
s = shapes.Rect(10,20,30,40) #width, height
assert s.getBounds() == (10,20,40,60)
def testCircle(self):
s = shapes.Circle(100, 50, 10)
assert s.getBounds() == (90,40,110,60)
def testEllipse(self):
s = shapes.Ellipse(100, 50, 10, 5)
assert s.getBounds() == (90,45,110,55)
def testWedge(self):
s = shapes.Wedge(0,0,10,0,90)
assert s.getBounds() == (0,0,10,10), 'expected (0,0,10,10) got %s' % repr(s.getBounds())
def testPolygon(self):
points = [0,0,10,30,25,15]
s = shapes.Polygon(points)
assert s.getBounds() == (0,0,25,30)
s = shapes.PolyLine(points)
assert s.getBounds() == (0,0,25,30)
def testString(self):
s = shapes.String(0,0,'Hello World', fontName='Courier',fontSize=10)
assert s.getBounds() == (0, -2.0, 66.0, 10)
def testGroup(self):
g = shapes.Group()
g.add(shapes.Rect(0,0,10,10))
g.add(shapes.Rect(50,50,10,10))
assert g.getBounds() == (0,0,60,60)
g.translate(40,40)
assert g.getBounds() == (40,40,100,100)
g.translate(-40,-40)
g.rotate(90)
#approx bounds needed, trig functions create an error of 3e-15
assert map(int, g.getBounds()) == [-60,0,0,60]
def testWidget(self):
from reportlab.graphics.charts.barcharts import VerticalBarChart
vbc = VerticalBarChart()
vbc.x = 50
vbc.y = 50
from reportlab.graphics.widgetbase import Sizer
siz = Sizer()
siz.add(vbc, 'vbc')
assert siz.getBounds()[0:2] <> (0,0)
def makeSuite():
return makeSuiteForClasses(BoundsTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,86 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_graphics_speed.py
"""
This does a test drawing with lots of things in it, running
with and without attribute checking.
"""
__version__ = ''' $Id $ '''
import os, sys, time, profile
import reportlab.rl_config
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.lib import colors
from reportlab.lib.units import cm
from reportlab.pdfgen.canvas import Canvas
from reportlab.pdfbase.pdfmetrics import stringWidth
from reportlab.platypus import Flowable
from reportlab.graphics.shapes import *
from reportlab.graphics.charts.piecharts import Pie
class GraphicsSpeedTestCase(unittest.TestCase):
"Test speed of the graphics rendering process."
def test0(self, isFast=0):
"""Hello World, on a rectangular background.
The rectangle's fillColor is yellow.
The string's fillColor is red.
"""
reportlab.rl_config.shapeChecking = not isFast
pdfPath = outputfile('test_graphics_speed_fast.pdf')
c = Canvas(pdfPath)
t0 = time.time()
d = Drawing(400, 200)
num = 100
for i in range(num):
pc = Pie()
pc.x = 150
pc.y = 50
pc.data = [10,20,30,40,50,60]
pc.labels = ['a','b','c','d','e','f']
pc.slices.strokeWidth=0.5
pc.slices[3].popout = 20
pc.slices[3].strokeWidth = 2
pc.slices[3].strokeDashArray = [2,2]
pc.slices[3].labelRadius = 1.75
pc.slices[3].fontColor = colors.red
d.add(pc)
d.drawOn(c, 80, 500)
t1 = time.time()
result = 'drew %d pie charts in %0.4f' % (num, t1 - t0)
open(outputfile('test_graphics_speed_test%s.log' % (isFast+1)), 'w').write(result)
def test1(self, isFast=1):
"Same as test1(), but with shape checking turned on."
self.test0(isFast)
def test2(self):
"This is a profiled version of test1()."
fileName = outputfile('test_graphics_speed_profile.log')
# This runs ok, when only this test script is executed,
# but fails, when imported from runAll.py...
## profile.run("t = GraphicsSpeedTestCase('test2')", fileName)
def makeSuite():
return makeSuiteForClasses(GraphicsSpeedTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,34 @@
#!/bin/env python
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_hello.py
__version__=''' $Id'''
__doc__="""most basic test possible that makes a PDF.
Useful if you want to test that a really minimal PDF is healthy,
since the output is about the smallest thing we can make."""
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.pdfgen.canvas import Canvas
class HelloTestCase(unittest.TestCase):
"Simplest test that makes PDF"
def test(self):
c = Canvas(outputfile('test_hello.pdf'))
c.setAuthor('\xe3\x83\x9b\xe3\x83\x86\xe3\x83\xab\xe3\x83\xbbe\xe3\x83\x91\xe3\x83\xb3\xe3\x83\x95\xe3\x83\xac\xe3\x83\x83\xe3\x83\x88')
c.setFont('Helvetica-Bold', 36)
c.drawString(100,700, 'Hello World')
c.save()
def makeSuite():
return makeSuiteForClasses(HelloTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,55 @@
#!/bin/env python
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_images.py
__version__=''' $Id'''
__doc__="""Tests to do with image handling.
Most of them make use of test\pythonpowereed.gif."""
import os,md5
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, printLocation
from reportlab.lib.utils import ImageReader
"""To avoid depending on external stuff, I made a small 5x5 image and
attach its 'file contents' here in several formats.
The image looks like this, with K=black, R=red, G=green, B=blue, W=white.
K R G B W
K R G B W
K R G B W
K R G B W
K R G B W
"""
sampleRAW = '\x00\x00\x00\xff\x00\x00\x00\xff\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\xff\x00\x00\x00\xff\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\xff\x00\x00\x00\xff\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\xff\x00\x00\x00\xff\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\xff\x00\x00\x00\xff\x00\x00\x00\xff\xff\xff\xff'
samplePNG = '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x05\x00\x00\x00\x05\x08\x02\x00\x00\x00\x02\r\xb1\xb2\x00\x00\x00:IDATx\x9cb```\xf8\x0f\xc3\xff\xff\xff\x07\x00\x00\x00\xff\xffbb@\x05\x00\x00\x00\x00\xff\xffB\xe7\x03\x00\x00\x00\xff\xffB\xe7\x03\x00\x00\x00\xff\xffB\xe7\x03\x00\x00\x00\xff\xff\x03\x00\x9e\x01\x06\x03\x03\xc4A\xb4\x00\x00\x00\x00IEND\xaeB`\x82'
class ReaderTestCase(unittest.TestCase):
"Simplest tests to import images, work under Jython or PIL"
def test(self):
import reportlab.test
from reportlab.lib.utils import rl_isfile
imageFileName = os.path.dirname(reportlab.test.__file__) + os.sep + 'pythonpowered.gif'
assert rl_isfile(imageFileName), "%s not found!" % imageFileName
ir = ImageReader(imageFileName)
assert ir.getSize() == (110,44)
pixels = ir.getRGBData()
assert md5.md5(pixels).hexdigest() == '02e000bf3ffcefe9fc9660c95d7e27cf'
def makeSuite():
return makeSuiteForClasses(ReaderTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,56 @@
#!/bin/env python
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_invariant.py
__version__=''' $Id'''
__doc__="""Verfy that if in invariant mode, repeated runs
make identical file. This does NOT test across platforms
or python versions, only a user can do that :-)"""
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.pdfgen.canvas import Canvas
filename = outputfile('test_invariant.pdf')
class InvarTestCase(unittest.TestCase):
"Simplest test that makes PDF"
def test(self):
import os
c = Canvas(filename, invariant=1, pageCompression=0)
c.setFont('Helvetica-Bold', 36)
c.drawString(100,700, 'Hello World')
gif = os.path.join(os.path.dirname(unittest.__file__),'pythonpowered.gif')
c.drawImage(gif,100,600)
c.save()
raw1 = open(filename, 'rb').read()
c = Canvas(filename, invariant=1, pageCompression=0)
c.setFont('Helvetica-Bold', 36)
c.drawString(100,700, 'Hello World')
c.drawImage(gif,100,600)
c.save()
raw2 = open(filename, 'rb').read()
assert raw1 == raw2, 'repeated runs differ!'
def makeSuite():
return makeSuiteForClasses(InvarTestCase)
#noruntests
if __name__ == "__main__":
# add some diagnostics, useful in invariant tests
import sys, md5, os
verbose = ('-v' in sys.argv)
unittest.TextTestRunner().run(makeSuite())
if verbose:
#tell us about the file we produced
fileSize = os.stat(filename)[6]
raw = open(filename,'rb').read()
digest = md5.md5(raw).hexdigest()
major, minor = sys.version_info[0:2]
print '%s on %s (Python %d.%d):\n %d bytes, digest %s' % (
filename,sys.platform, major, minor, fileSize, digest)
printLocation()

View File

@ -0,0 +1,161 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_lib_colors.py
"""Tests for the reportlab.lib.colors module.
"""
import os, math
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.pdfgen.canvas import Canvas
import reportlab.pdfgen.canvas
from reportlab.lib import colors
from reportlab.lib.units import inch, cm
def framePage(canvas, title):
canvas.setFont('Times-BoldItalic',20)
canvas.drawString(inch, 10.5 * inch, title)
canvas.setFont('Times-Roman',10)
canvas.drawCentredString(4.135 * inch, 0.75 * inch,
'Page %d' % canvas.getPageNumber())
#draw a border
canvas.setStrokeColorRGB(1,0,0)
canvas.setLineWidth(5)
canvas.line(0.8 * inch, inch, 0.8 * inch, 10.75 * inch)
#reset carefully afterwards
canvas.setLineWidth(1)
canvas.setStrokeColorRGB(0,0,0)
class ColorTestCase(unittest.TestCase):
""
def test0(self):
"Test color2bw function on all named colors."
cols = colors.getAllNamedColors().values()
for col in cols:
gray = colors.color2bw(col)
r, g, b = gray.red, gray.green, gray.blue
assert r == g == b
def test1(self):
"Test colorDistance function."
cols = colors.getAllNamedColors().values()
for col in cols:
d = colors.colorDistance(col, col)
assert d == 0
def test2(self):
"Test toColor function on half a dozen ways to say 'red'."
allRed = [colors.red, [1, 0, 0], (1, 0, 0),
'red', 'RED', '0xFF0000', '0xff0000']
for thing in allRed:
assert colors.toColor(thing) == colors.red
def test3(self):
"Test roundtrip RGB to CMYK conversion."
# Take all colors as test subjects, except 'transparent'.
## rgbCols = colors.getAllNamedColors()
## del rgbCols['transparent']
rgbCols = colors.getAllNamedColors().items()
# Make a roundtrip test (RGB > CMYK > RGB).
for name, rgbCol in rgbCols:
r1, g1, b1 = rgbCol.red, rgbCol.green, rgbCol.blue
c, m, y, k = colors.rgb2cmyk(r1, g1, b1)
r2, g2, b2 = colors.cmyk2rgb((c, m, y, k))
rgbCol2 = colors.Color(r2, g2, b2)
# Make sure the differences for each RGB component
# isreally small (< power(10, -N)!
N = 16 # max. value found to work on Python2.0 and Win2K.
deltas = map(abs, (r1-r2, g1-g2, b1-b2))
assert deltas < [math.pow(10, -N)] * 3
def test4(self):
"Construct CMYK instances and test round trip conversion"
rgbCols = colors.getAllNamedColors().items()
# Make a roundtrip test (RGB > CMYK > RGB).
for name, rgbCol in rgbCols:
r1, g1, b1 = rgbCol.red, rgbCol.green, rgbCol.blue
c, m, y, k = colors.rgb2cmyk(r1, g1, b1)
cmykCol = colors.CMYKColor(c,m,y,k)
r2, g2, b2 = cmykCol.red, cmykCol.green, cmykCol.blue #colors.cmyk2rgb((c, m, y, k))
rgbCol2 = colors.Color(r2, g2, b2)
# Make sure the differences for each RGB component
# isreally small (< power(10, -N)!
N = 16 # max. value found to work on Python2.0 and Win2K.
deltas = map(abs, (r1-r2, g1-g2, b1-b2))
assert deltas < [math.pow(10, -N)] * 3
def test5(self):
"List and display all named colors and their gray equivalents."
canvas = reportlab.pdfgen.canvas.Canvas(outputfile('test_lib_colors.pdf'))
#do all named colors
framePage(canvas, 'Color Demo - page %d' % canvas.getPageNumber())
all_colors = reportlab.lib.colors.getAllNamedColors().items()
all_colors.sort() # alpha order by name
canvas.setFont('Times-Roman', 10)
text = 'This shows all the named colors in the HTML standard (plus their gray and CMYK equivalents).'
canvas.drawString(72,740, text)
canvas.drawString(200,725,'Pure RGB')
canvas.drawString(300,725,'B&W Approx')
canvas.drawString(400,725,'CMYK Approx')
y = 700
for (name, color) in all_colors:
canvas.setFillColor(colors.black)
canvas.drawString(100, y, name)
canvas.setFillColor(color)
canvas.rect(200, y-10, 80, 30, fill=1)
canvas.setFillColor(colors.color2bw(color))
canvas.rect(300, y-10, 80, 30, fill=1)
c, m, yel, k = colors.rgb2cmyk(color.red, color.green, color.blue)
CMYK = colors.CMYKColor(c,m,yel,k)
canvas.setFillColor(CMYK)
canvas.rect(400, y-10, 80, 30, fill=1)
y = y - 40
if y < 100:
canvas.showPage()
framePage(canvas, 'Color Demo - page %d' % canvas.getPageNumber())
canvas.setFont('Times-Roman', 10)
y = 700
canvas.drawString(200,725,'Pure RGB')
canvas.drawString(300,725,'B&W Approx')
canvas.drawString(400,725,'CMYK Approx')
canvas.save()
def makeSuite():
return makeSuiteForClasses(ColorTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,123 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_lib_sequencer.py
"""Tests for the reportlab.lib.sequencer module.
"""
import sys, random
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, printLocation
from reportlab.lib.sequencer import Sequencer
class SequencerTestCase(unittest.TestCase):
"Test Sequencer usage."
def test0(self):
"Test sequencer default counter."
seq = Sequencer()
msg = 'Initial value is not zero!'
assert seq._this() == 0, msg
def test1(self):
"Test incrementing default counter."
seq = Sequencer()
for i in range(1, 101):
n = seq.next()
msg = 'Sequence value is not correct!'
assert seq._this() == n, msg
def test2(self):
"Test resetting default counter."
seq = Sequencer()
start = seq._this()
for i in range(1, 101):
n = seq.next()
seq.reset()
msg = 'Sequence value not correctly reset!'
assert seq._this() == start, msg
def test3(self):
"Test incrementing dedicated counter."
seq = Sequencer()
for i in range(1, 101):
n = seq.next('myCounter1')
msg = 'Sequence value is not correct!'
assert seq._this('myCounter1') == n, msg
def test4(self):
"Test resetting dedicated counter."
seq = Sequencer()
start = seq._this('myCounter1')
for i in range(1, 101):
n = seq.next('myCounter1')
seq.reset('myCounter1')
msg = 'Sequence value not correctly reset!'
assert seq._this('myCounter1') == start, msg
def test5(self):
"Test incrementing multiple dedicated counters."
seq = Sequencer()
startMyCounter0 = seq._this('myCounter0')
startMyCounter1 = seq._this('myCounter1')
for i in range(1, 101):
n = seq.next('myCounter0')
msg = 'Sequence value is not correct!'
assert seq._this('myCounter0') == n, msg
m = seq.next('myCounter1')
msg = 'Sequence value is not correct!'
assert seq._this('myCounter1') == m, msg
## def testRandom(self):
## "Test randomly manipulating multiple dedicated counters."
##
## seq = Sequencer()
## counterNames = ['c0', 'c1', 'c2', 'c3']
##
## # Init.
## for cn in counterNames:
## setattr(self, cn, seq._this(cn))
## msg = 'Counter start value is not correct!'
## assert seq._this(cn) == 0, msg
##
## # Increment/decrement.
## for i in range(1, 101):
## n = seq.next('myCounter0')
## msg = 'Sequence value is not correct!'
## assert seq._this('myCounter0') == n, msg
## m = seq.next('myCounter1')
## msg = 'Sequence value is not correct!'
## assert seq._this('myCounter1') == m, msg
def makeSuite():
return makeSuiteForClasses(SequencerTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,115 @@
"""Tests for reportlab.lib.utils
"""
import os
import reportlab
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, printLocation
from reportlab.lib import colors
from reportlab.lib.utils import recursiveImport, recursiveGetAttr, recursiveSetAttr, rl_isfile, \
isCompactDistro
class ImporterTestCase(unittest.TestCase):
"Test import utilities"
count = 0
def setUp(self):
from time import time
from reportlab.lib.utils import get_rl_tempdir
s = `int(time())` + `self.count`
self.__class__.count += 1
self._tempdir = get_rl_tempdir('reportlab_test','tmp_%s' % s)
_testmodulename = os.path.join(self._tempdir,'test_module_%s.py' % s)
f = open(_testmodulename,'w')
f.write('__all__=[]\n')
f.close()
self._testmodulename = os.path.splitext(os.path.basename(_testmodulename))[0]
def tearDown(self):
from shutil import rmtree
rmtree(self._tempdir,1)
def test1(self):
"try stuff known to be in the path"
m1 = recursiveImport('reportlab.pdfgen.canvas')
import reportlab.pdfgen.canvas
assert m1 == reportlab.pdfgen.canvas
def test2(self):
"try under a well known directory NOT on the path"
D = os.path.join(os.path.dirname(reportlab.__file__), 'tools','pythonpoint')
fn = os.path.join(D,'stdparser.py')
if rl_isfile(fn) or rl_isfile(fn+'c') or rl_isfile(fn+'o'):
m1 = recursiveImport('stdparser', baseDir=D)
def test3(self):
"ensure CWD is on the path"
try:
cwd = os.getcwd()
os.chdir(self._tempdir)
m1 = recursiveImport(self._testmodulename)
finally:
os.chdir(cwd)
def test4(self):
"ensure noCWD removes current dir from path"
try:
cwd = os.getcwd()
os.chdir(self._tempdir)
import sys
try:
del sys.modules[self._testmodulename]
except KeyError:
pass
self.assertRaises(ImportError,
recursiveImport,
self._testmodulename,
noCWD=1)
finally:
os.chdir(cwd)
def test5(self):
"recursive attribute setting/getting on modules"
import reportlab.lib.units
inch = recursiveGetAttr(reportlab, 'lib.units.inch')
assert inch == 72
recursiveSetAttr(reportlab, 'lib.units.cubit', 18*inch)
cubit = recursiveGetAttr(reportlab, 'lib.units.cubit')
assert cubit == 18*inch
def test6(self):
"recursive attribute setting/getting on drawings"
from reportlab.graphics.charts.barcharts import sampleH1
drawing = sampleH1()
recursiveSetAttr(drawing, 'barchart.valueAxis.valueMax', 72)
theMax = recursiveGetAttr(drawing, 'barchart.valueAxis.valueMax')
assert theMax == 72
def test7(self):
"test open and read of a simple relative file"
from reportlab.lib.utils import open_and_read
b = open_and_read('../docs/images/Edit_Prefs.gif')
def test8(self):
"test open and read of a relative file: URL"
from reportlab.lib.utils import open_and_read
b = open_and_read('file:../docs/images/Edit_Prefs.gif')
def test9(self):
"test open and read of an http: URL"
from reportlab.lib.utils import open_and_read
b = open_and_read('http://www.reportlab.com/rsrc/encryption.gif')
def test10(self):
"test open and read of a simple relative file"
from reportlab.lib.utils import open_and_read, getStringIO
b = getStringIO(open_and_read('../docs/images/Edit_Prefs.gif'))
b = open_and_read(b)
def makeSuite():
return makeSuiteForClasses(ImporterTestCase)
if __name__ == "__main__": #noruntests
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,165 @@
"""Tests (incomplete) for the reportlab.lib.validators module.
"""
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, printLocation
from reportlab.lib import colors
from reportlab.lib import validators
class ValidatorTestCase(unittest.TestCase):
"Test validating functions."
def test0(self):
"Test isBoolean validator."
msg = "Validation failed for 'boolean' %s!"
booleans = [0, 1, 'yes','no','true','false']
badbooleans = ['a',3,-1,()]
isBoolean = validators.isBoolean
for b in booleans:
assert isBoolean(b) == 1, msg % str(b)
for b in badbooleans:
assert isBoolean(b) == 0, msg % str(b)
def test1(self):
"Test isNumber validator."
msg = 'Validation failed for number %s!'
numbers = [0, 1, 2, -1, -2, 0.0, 0.1, -0.1]
badNumbers = ['aaa',(1,1),(1+1j),colors]
isNumber = validators.isNumber
isListOfNumbers = validators.isListOfNumbers
for n in numbers:
assert isNumber(n) == 1, msg % str(n)
for n in badNumbers:
assert isNumber(n) == 0, msg % str(n)
msg = 'Validation failed for numbers %s!'
assert isListOfNumbers(numbers) == 1, msg % str(numbers)
assert isListOfNumbers(badNumbers) == 0, msg % str(badNumbers)
assert isListOfNumbers(numbers+[colors]) == 0, msg % str(numbers+[colors])
def test2(self):
"Test isNumberOrNone validator."
msg = 'Validation failed for number %s!'
numbers = [None, 0, 1, 2, -1, -2, 0.0, 0.1, -0.1] #, 2L, -2L]
isNumberOrNone = validators.isNumberOrNone
for n in numbers:
assert isNumberOrNone(n) == 1, msg % str(n)
def test4(self):
"Test isString validator."
msg = 'Validation failed for string %s!'
strings = ['', '\n', ' ', 'foo', '""']
badStrings = [1,2.0,None,('a','b')]
isString = validators.isString
isListOfStrings = validators.isListOfStrings
for s in strings:
assert isString(s) == 1, msg % str(s)
for s in badStrings:
assert isString(s) == 0, msg % str(s)
msg = 'Validation failed for strings %s!'
assert isListOfStrings(strings) == 1, msg % str(strings)
assert isListOfStrings(badStrings) == 0, msg % str(badStrings)
assert isListOfStrings(strings+[1]) == 0, msg % str(strings+[1])
def test5(self):
"Test isTextAnchor validator."
msg = 'Validation failed for text anchor %s!'
strings = ['start', 'middle', 'end']
isTextAnchor = validators.isTextAnchor
for s in strings:
assert isTextAnchor(s) == 1, msg % s
"""
def isListOfNumbersOrNone(x):
def isListOfShapes(x):
def isListOfStrings(x):
def isListOfStringsOrNone(x):
def isTransform(x):
def isColor(x):
def isColorOrNone(x):
def isValidChild(x):
class OneOf:
class SequenceOf:
"""
def test6(self):
"Test OneOf validator."
msg = 'Validation failed for OneOf %s!'
choices = ('clockwise', 'anticlockwise')
OneOf = validators.OneOf(choices)
for c in choices:
assert OneOf(c) == 1, msg % c
for c in ('a', 'b', 'c'):
assert OneOf(c) == 0, msg % c
OneOf = validators.OneOf('clockwise', 'anticlockwise')
for c in choices:
assert OneOf(c) == 1, msg % c
for c in ('a', 'b', 'c'):
assert OneOf(c) == 0, msg % c
try:
validators.OneOf(choices,'bongo')
raise AssertionError, "OneOf failed to detect bad arguments"
except ValueError:
pass
def test7(self):
"Test isInt validator"
msg = 'Validation failed for isInt %s!'
isInt = validators.isInt
for c in (1,2,-3,0,'-4','4'):
assert isInt(c), msg % str(c)
for c in (1.2,0.0,-3.0,'-4.0','4.4','AAAA'):
assert not isInt(c), msg % str(c)
def test8(self):
"test Sequence of validator"
msg = 'Validation failed for SequenceOf %s!'
v=validators.SequenceOf(validators.OneOf(('eps','pdf','png','gif','jpg','tif')),lo=1,hi=3,emptyOK=0)
for c in (['png'],('eps',),('eps','pdf')):
assert v(c), msg % str(c)
v._lo = 2
for c in ([],(),('eps'),('eps','pdf','a'),['eps','pdf','png','gif']):
assert not v(c), msg % str(c)
v._emptyOK=1
for c in ([],(),('eps','pdf')):
assert v(c), msg % str(c)
def makeSuite():
return makeSuiteForClasses(ValidatorTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,84 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history www.reportlab.co.uk/rl-cgi/viewcvs.cgi/rlextra/rlj/jpsupport.py
# Temporary japanese support for ReportLab.
"""
The code in this module will disappear any day now and be replaced
by classes in reportlab.pdfbase.cidfonts
"""
import string, os
import codecs
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfgen.canvas import Canvas
from reportlab.lib import colors
from reportlab.lib.codecharts import KutenRowCodeChart, hBoxText
global VERBOSE
VERBOSE = 0
class CHSFontTests(unittest.TestCase):
def test0(self):
"A basic document drawing some strings"
# if they do not have the Japanese font files, go away quietly
from reportlab.pdfbase.cidfonts import UnicodeCIDFont, findCMapFile
pdfmetrics.registerFont(UnicodeCIDFont('STSong-Light'))
c = Canvas(outputfile('test_multibyte_chs.pdf'))
c.setFont('Helvetica', 30)
c.drawString(100,700, 'Simplified Chinese Font Support')
c.setFont('Helvetica', 10)
c.drawString(100,680, 'Short sample: "China - Zhang Ziyi" (famous actress)')
# the two typefaces
hBoxText(u'\u4e2d\u56fd - \u7ae0\u5b50\u6021',
c,
100,
660,
'STSong-Light',
)
c.setFont('Helvetica',10)
c.drawCentredString(297, 36, 'Page %d' % c.getPageNumber())
c.showPage()
## # full kuten chart in EUC
## c.setFont('Helvetica', 18)
## c.drawString(72,750, 'Characters available in GB 2312-80, EUC encoding')
## y = 600
## enc = 'GB_EUC_H'
## for row in range(1, 95):
## KutenRowCodeChart(row, 'STSong-Light',enc).drawOn(c, 72, y)
## y = y - 125
## if y < 50:
## c.setFont('Helvetica',10)
## c.drawCentredString(297, 36, 'Page %d' % c.getPageNumber())
## c.showPage()
## y = 700
##
c.save()
if VERBOSE:
print 'saved '+outputfile('test_multibyte_chs.pdf')
def makeSuite():
return makeSuiteForClasses(CHSFontTests)
#noruntests
if __name__ == "__main__":
VERBOSE = 1
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,131 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history www.reportlab.co.uk/rl-cgi/viewcvs.cgi/rlextra/rlj/jpsupport.py
# Temporary japanese support for ReportLab.
"""
Test of traditional Chinese (as written in Taiwan)
"""
import string, os
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfgen.canvas import Canvas
from reportlab.lib import colors
from reportlab.lib.codecharts import Big5CodeChart, hBoxText
global VERBOSE
VERBOSE = 0
class CHTFontTests(unittest.TestCase):
def hDraw(self, c, msg, fnt, x, y):
"Helper - draws it with a box around"
c.setFont(fnt, 16, 16)
c.drawString(x, y, msg)
c.rect(x,y,pdfmetrics.stringWidth(msg, fnt, 16),16,stroke=1,fill=0)
def test0(self):
"A basic document drawing some strings"
# if they do not have the Japanese font files, go away quietly
from reportlab.pdfbase.cidfonts import UnicodeCIDFont, findCMapFile
## enc = 'ETenms-B5-H'
## try:
## findCMapFile(enc)
## except:
## #they don't have the font pack, return silently
## print 'CMap not found'
## return
pdfmetrics.registerFont(UnicodeCIDFont('MSung-Light'))
c = Canvas(outputfile('test_multibyte_cht.pdf'))
c.setFont('Helvetica', 24)
c.drawString(100,700, 'Traditional Chinese Font Support')
c.setFont('Helvetica', 10)
c.drawString(100,680, 'Short sample: "Taiwan - Ang Lee" (movie director)')
hBoxText(u'\u81fa\u7063 - \u674e\u5b89' , c, 100, 600, 'MSung-Light')
## #hBoxText(message3 + ' MHei-Medium', c, 100, 580, 'MHei-Medium', enc)
##
##
##
## c.setFont('Helvetica', 10)
## tx = c.beginText(100, 500)
## tx.textLines("""
## This test document shows Traditional Chinese output from Reportlab PDF Library.
## You may use one Chinese font, MSung-Light, and a number of different
## encodings.
##
## The available encoding names (with comments from the PDF specification) are:
## encodings_cht = [
## 'B5pc-H', # Macintosh, Big Five character set, Big Five encoding,
## # Script Manager code 2
## 'B5pc-V', # Vertical version of B5pc-H
## 'ETen-B5-H', # Microsoft Code Page 950 (lfCharSet 0x88), Big Five
## # character set with ETen extensions
## 'ETen-B5-V', # Vertical version of ETen-B5-H
## 'ETenms-B5-H', # Microsoft Code Page 950 (lfCharSet 0x88), Big Five
## # character set with ETen extensions; this uses proportional
## # forms for half-width Latin characters.
## 'ETenms-B5-V', # Vertical version of ETenms-B5-H
## 'CNS-EUC-H', # CNS 11643-1992 character set, EUC-TW encoding
## 'CNS-EUC-V', # Vertical version of CNS-EUC-H
## 'UniCNS-UCS2-H', # Unicode (UCS-2) encoding for the Adobe-CNS1
## # character collection
## 'UniCNS-UCS2-V' # Vertical version of UniCNS-UCS2-H.
## ]
##
## The next 32 pages show the complete character set available in the encoding
## "ETen-B5-H". This is Big5 with the ETen extensions. ETen extensions are the
## most common extension to Big5 and include circled and roman numbers, Japanese
## hiragana and katakana, Cyrillic and fractions in rows C6-C8; and 7 extra characters
## and some line drawing characters in row F9.
## """)
## c.drawText(tx)
## c.setFont('Helvetica',10)
## c.drawCentredString(297, 36, 'Page %d' % c.getPageNumber())
##
## c.showPage()
##
## # full Big5 code page
## c.setFont('Helvetica', 18)
## c.drawString(72,750, 'Characters available in Big 5')
## y = 500
## for row in range(0xA1,0xFF):
## cc = Big5CodeChart(row, 'MSung-Light',enc)
## cc.charsPerRow = 16
## cc.rows = 10
## cc.codePoints = 160
## cc.drawOn(c, 72, y)
## y = y - cc.height - 25
## if y < 50:
## c.setFont('Helvetica',10)
## c.drawCentredString(297, 36, 'Page %d' % c.getPageNumber())
## c.showPage()
## y = 600
##
##
c.save()
if VERBOSE:
print 'saved '+outputfile('test_multibyte_cht.pdf')
def makeSuite():
return makeSuiteForClasses(CHTFontTests)
#noruntests
if __name__ == "__main__":
VERBOSE = 1
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,460 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history www.reportlab.co.uk/rl-cgi/viewcvs.cgi/rlextra/rlj/jpsupport.py
# Temporary japanese support for ReportLab.
"""
The code in this module will disappear any day now and be replaced
by classes in reportlab.pdfbase.cidfonts
"""
import string, os
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfgen.canvas import Canvas
from reportlab.lib import colors
from reportlab.lib.codecharts import KutenRowCodeChart
from reportlab.pdfbase.cidfonts import CIDFont, findCMapFile, UnicodeCIDFont
global VERBOSE
VERBOSE = 0
class JapaneseFontTests(unittest.TestCase):
def hDraw(self, c, msg, fnt, x, y):
"Helper - draws it with a box around"
c.setFont(fnt, 16, 16)
font = pdfmetrics.getFont(fnt)
c.drawString(x, y, msg)
width = font.stringWidth(msg, 16)
c.rect(x,y,width,16,stroke=1,fill=0)
def test0(self):
"A basic document drawing some strings"
## # if they do not have the Japanese font files, go away quietly
## try:
## from reportlab.pdfbase.cidfonts import CIDFont, findCMapFile, UnicodeCIDFont
## findCMapFile('90ms-RKSJ-H')
## findCMapFile('90msp-RKSJ-H')
## findCMapFile('UniJIS-UCS2-H')
## findCMapFile('EUC-H')
## except:
## #don't have the font pack. return silently
## return
##
## pdfmetrics.registerFont(CIDFont('HeiseiMin-W3','90ms-RKSJ-H'))
## pdfmetrics.registerFont(CIDFont('HeiseiKakuGo-W5','90ms-RKSJ-H'))
c = Canvas(outputfile('test_multibyte_jpn.pdf'))
c.setFont('Helvetica', 30)
c.drawString(100,700, 'Japanese Font Support')
c.setStrokeColor(colors.red)
## # the two typefaces
## c.setFont('HeiseiMin-W3-90ms-RKSJ-H', 16)
## # this says "This is HeiseiMincho" in shift-JIS. Not all our readers
## # have a Japanese PC, so I escaped it. On a Japanese-capable
## # system, print the string to see Kanji
## message1 = '\202\261\202\352\202\315\225\275\220\254\226\276\222\251\202\305\202\267\201B'
## c.drawString(100, 675, message1)
## wid = pdfmetrics.stringWidth(message1, 'HeiseiMin-W3-90ms-RKSJ-H', 16)
## c.rect(100,675,wid,16,stroke=1,fill=0)
##
## c.setFont('HeiseiKakuGo-W5-90ms-RKSJ-H', 16)
## # this says "This is HeiseiKakugo" in shift-JIS
## message2 = '\202\261\202\352\202\315\225\275\220\254\212p\203S\203V\203b\203N\202\305\202\267\201B'
## c.drawString(100, 650, message2)
## wid = pdfmetrics.stringWidth(message2, 'HeiseiKakuGo-W5-90ms-RKSJ-H', 16)
## c.rect(100,650,wid,16,stroke=1,fill=0)
##
##
##
## self.hDraw(c, '\223\214\213\236 says Tokyo in Shift-JIS', 'HeiseiMin-W3-90ms-RKSJ-H', 100, 600)
##
##
## pdfmetrics.registerFont(CIDFont('HeiseiMin-W3','90msp-RKSJ-H'))
## self.hDraw(c, '\223\214\213\236, but in proportional Shift-JIS.', 'HeiseiMin-W3-90msp-RKSJ-H', 100, 575)
##
## pdfmetrics.registerFont(CIDFont('HeiseiMin-W3','EUC-H'))
## self.hDraw(c, '\xC5\xEC\xB5\xFE says Tokyo in EUC', 'HeiseiMin-W3-EUC-H', 100, 550)
##
## #this is super-slow until we do encoding caching.
## pdfmetrics.registerFont(CIDFont('HeiseiMin-W3','UniJIS-UCS2-H'))
##
## def asciiToUCS2(text):
## s = ''
## for ch in text:
## s = s + chr(0) + ch
## return s
## msg = '\x67\x71\x4E\xAC' + asciiToUCS2(' says Tokyo in UTF16')
## self.hDraw(c, msg,'HeiseiMin-W3-UniJIS-UCS2-H', 100, 525)
#unicode font automatically supplies the encoding
pdfmetrics.registerFont(UnicodeCIDFont('HeiseiMin-W3'))
msg = u'\u6771\u4EAC : Unicode font, unicode input'
self.hDraw(c, msg, 'HeiseiMin-W3', 100, 500)
msg = u'\u6771\u4EAC : Unicode font, utf8 input'.encode('utf8')
self.hDraw(c, msg, 'HeiseiMin-W3', 100, 475)
## # now try verticals
## pdfmetrics.registerFont(CIDFont('HeiseiMin-W3','90ms-RKSJ-V'))
## c.setFont('HeiseiMin-W3-90ms-RKSJ-V', 16)
## c.drawString(400, 650, '\223\214\213\236 vertical Shift-JIS')
## height = c.stringWidth('\223\214\213\236 vertical Shift-JIS', 'HeiseiMin-W3-90ms-RKSJ-V', 16)
## c.rect(400-8,650,16,-height)
##
## pdfmetrics.registerFont(CIDFont('HeiseiMin-W3','EUC-V'))
## c.setFont('HeiseiMin-W3-EUC-V', 16)
## c.drawString(425, 650, '\xC5\xEC\xB5\xFE vertical EUC')
## height = c.stringWidth('\xC5\xEC\xB5\xFE vertical EUC', 'HeiseiMin-W3-EUC-V', 16)
## c.rect(425-8,650,16,-height)
##
from reportlab.platypus.paragraph import Paragraph
from reportlab.lib.styles import ParagraphStyle
jStyle = ParagraphStyle('jtext',
fontName='HeiseiMin-W3',
fontSize=12,
wordWrap="CJK"
)
gatwickText = '\xe3\x82\xac\xe3\x83\x88\xe3\x82\xa6\xe3\x82\xa3\xe3\x83\x83\xe3\x82\xaf\xe7\xa9\xba\xe6\xb8\xaf\xe3\x81\xa8\xe9\x80\xa3\xe7\xb5\xa1\xe9\x80\x9a\xe8\xb7\xaf\xe3\x81\xa7\xe7\x9b\xb4\xe7\xb5\x90\xe3\x81\x95\xe3\x82\x8c\xe3\x81\xa6\xe3\x81\x84\xe3\x82\x8b\xe5\x94\xaf\xe4\xb8\x80\xe3\x81\xae\xe3\x83\x9b\xe3\x83\x86\xe3\x83\xab\xe3\x81\xa7\xe3\x81\x82\xe3\x82\x8b\xe5\xbd\x93\xe3\x83\x9b\xe3\x83\x86\xe3\x83\xab\xe3\x81\xaf\xe3\x80\x81\xe8\xa1\x97\xe3\x81\xae\xe4\xb8\xad\xe5\xbf\x83\xe9\x83\xa8\xe3\x81\x8b\xe3\x82\x8930\xe5\x88\x86\xe3\x81\xae\xe5\xa0\xb4\xe6\x89\x80\xe3\x81\xab\xe3\x81\x94\xe3\x81\x96\xe3\x81\x84\xe3\x81\xbe\xe3\x81\x99\xe3\x80\x82\xe5\x85\xa8\xe5\xae\xa2\xe5\xae\xa4\xe3\x81\xab\xe9\xab\x98\xe9\x80\x9f\xe3\x82\xa4\xe3\x83\xb3\xe3\x82\xbf\xe3\x83\xbc\xe3\x83\x8d\xe3\x83\x83\xe3\x83\x88\xe7\x92\xb0\xe5\xa2\x83\xe3\x82\x92\xe5\xae\x8c\xe5\x82\x99\xe3\x81\x97\xe3\x81\xa6\xe3\x81\x8a\xe3\x82\x8a\xe3\x81\xbe\xe3\x81\x99\xe3\x80\x82\xe3\x83\x95\xe3\x82\xa1\xe3\x83\x9f\xe3\x83\xaa\xe3\x83\xbc\xe3\x83\xab\xe3\x83\xbc\xe3\x83\xa0\xe3\x81\xaf5\xe5\x90\x8d\xe6\xa7\x98\xe3\x81\xbe\xe3\x81\xa7\xe3\x81\x8a\xe6\xb3\x8a\xe3\x82\x8a\xe3\x81\x84\xe3\x81\x9f\xe3\x81\xa0\xe3\x81\x91\xe3\x81\xbe\xe3\x81\x99\xe3\x80\x82\xe3\x81\xbe\xe3\x81\x9f\xe3\x80\x81\xe3\x82\xa8\xe3\x82\xb0\xe3\x82\xbc\xe3\x82\xaf\xe3\x83\x86\xe3\x82\xa3\xe3\x83\x96\xe3\x83\xab\xe3\x83\xbc\xe3\x83\xa0\xe3\x81\xae\xe3\x81\x8a\xe5\xae\xa2\xe6\xa7\x98\xe3\x81\xaf\xe3\x80\x81\xe3\x82\xa8\xe3\x82\xb0\xe3\x82\xbc\xe3\x82\xaf\xe3\x83\x86\xe3\x82\xa3\xe3\x83\x96\xe3\x83\xa9\xe3\x82\xa6\xe3\x83\xb3\xe3\x82\xb8\xe3\x82\x92\xe3\x81\x94\xe5\x88\xa9\xe7\x94\xa8\xe3\x81\x84\xe3\x81\x9f\xe3\x81\xa0\xe3\x81\x91\xe3\x81\xbe\xe3\x81\x99\xe3\x80\x82\xe4\xba\x8b\xe5\x89\x8d\xe3\x81\xab\xe3\x81\x94\xe4\xba\x88\xe7\xb4\x84\xe3\x81\x84\xe3\x81\x9f\xe3\x81\xa0\xe3\x81\x91\xe3\x82\x8b\xe3\x82\xbf\xe3\x82\xa4\xe3\x83\xa0\xe3\x83\x88\xe3\x82\xa5\xe3\x83\x95\xe3\x83\xa9\xe3\x82\xa4\xe3\x83\xbb\xe3\x83\x91\xe3\x83\x83\xe3\x82\xb1\xe3\x83\xbc\xe3\x82\xb8\xe3\x81\xab\xe3\x81\xaf\xe3\x80\x81\xe7\xa9\xba\xe6\xb8\xaf\xe3\x81\xae\xe9\xa7\x90\xe8\xbb\x8a\xe6\x96\x99\xe9\x87\x91\xe3\x81\x8c\xe5\x90\xab\xe3\x81\xbe\xe3\x82\x8c\xe3\x81\xa6\xe3\x81\x8a\xe3\x82\x8a\xe3\x81\xbe\xe3\x81\x99\xe3\x80\x82'
c.setFont('HeiseiMin-W3', 12)
## from reportlab.lib.textsplit import wordSplit
## y = 400
## splat = wordSplit(gatwickText, 250, 'HeiseiMin-W3', 12, encoding='utf8')
## for (line, extraSpace) in splat:
## c.drawString(100,y,line)
## y -= 14
jPara = Paragraph(gatwickText, jStyle)
jPara.wrap(250, 200)
#from pprint import pprint as pp
#pp(jPara.blPara)
jPara.drawOn(c, 100, 250)
c.setFillColor(colors.purple)
tx = c.beginText(100, 200)
tx.setFont('Helvetica', 12)
tx.textLines("""This document shows sample output in Japanese
from the Reportlab PDF library. This page shows the two fonts
available and tests our ability to measure the width of glyphs
in both horizontal and vertical writing, with proportional and
fixed-width characters. The red boxes should be the same width
(or height) as the character strings they surround.
The next pages show more samples and information.
""")
c.drawText(tx)
c.setFont('Helvetica',10)
c.drawCentredString(297, 36, 'Page %d' % c.getPageNumber())
c.showPage()
c.setFont('Helvetica', 30)
c.drawString(100,700, 'Japanese TrueType Font Support')
msg = u'\u6771\u4EAC : Unicode font, utf8 input'.encode('utf8')
from reportlab.pdfbase.ttfonts import TTFont
try:
msmincho = TTFont('MS Mincho','msmincho.ttc',subfontIndex=0)
fn = ' file=msmincho.ttc subfont 0'
except:
try:
msmincho = TTFont('MS Mincho','msmincho.ttf')
fn = 'file=msmincho.ttf'
except:
msmincho = None
if msmincho is None:
c.drawString(100,600, 'Cannot find msmincho.ttf or msmincho.ttc')
else:
pdfmetrics.registerFont(msmincho)
c.setFont('MS Mincho', 30)
c.drawString(100,600, msg+fn)
if fn.endswith('0'):
try:
msmincho1 = TTFont('MS Mincho 1','msmincho.ttc',subfontIndex=1)
pdfmetrics.registerFont(msmincho1)
fn = ' file=msmincho.ttc subfont 1'
c.setFont('MS Mincho 1',30)
c.drawString(100,500,msg+fn)
except:
c.setFont('Helvetica',30)
c.drawString(100,500,msg+fn)
c.showPage()
# realistic text sample
## sample = """Adobe Acrobat
##\x83h\x83L\x83\x85\x83\x81\x83\x93\x83g\x82\xaa\x8aJ\x82\xa9\x82\xc8\x82\xad\x82\xc4\x8d\xa2\x82\xc1\x82\xbd\x82\xb1\x82\xc6\x82\xcd
##\x82\xa0\x82\xe8\x82\xdc\x82\xb9\x82\xf1\x82\xa9\x81B\x8e\x96\x8b\xc6\x8cv\x89\xe6\x8f\x91\x81A\x89c\x8b\xc6\x83\x8c\x83|\x81[\x83g
##\x81A\x83J\x83^\x83\x8d\x83O\x82\xe2\x83p\x83\x93\x83t\x83\x8c\x83b\x83g\x82\xc8\x82\xc7\x90\xa7\x8d\xec\x95\xa8\x82\xcc\x8e\xed
##\x97\xde\x82\xc9\x82\xa9\x82\xa9\x82\xed\x82\xe7\x82\xb8\x81A
##\x83h\x83L\x83\x85\x83\x81\x83\x93\x83g\x82\xcdAdobe&reg; Acrobat&reg; 5.0\x82\xf0\x8eg\x82\xc1\x82\xc4Adobe PDF\x81iPortable Document
##Format\x81j\x83t\x83@\x83C\x83\x8b\x82\xc9\x95\xcf\x8a\xb7\x82\xb5\x82\xdc\x82\xb5\x82\xe5\x82\xa4\x81B\x96\xb3\x8f\x9e\x94z\x95z\x82\xcc
##Adobe Acrobat Reader\x82\xf0\x8eg\x82\xa6\x82\xce\x81A\x83n\x81[\x83h\x83E\x83F\x83A\x81A\x83\\\x83t\x83g\x83E\x83F\x83A\x82\xc9\x82\xa9
##\x82\xa9\x82\xed\x82\xe7\x82\xb8\x81A\x92N\x82\xc5\x82\xe0\x82\xa0\x82\xc8\x82\xbd\x82\xcc\x83h\x83L\x83\x85\x83\x81\x83\x93\x83g\x82\xf0
##\x83I\x83\x8a\x83W\x83i\x83\x8b\x82\xcc\x91\xcc\x8d\xd9\x82\xc5\x8aJ\x82\xad\x82\xb1\x82\xc6\x82\xaa\x82\xc5\x82\xab\x82\xdc\x82\xb7\x81B
##\x82\xa0\x82\xc8\x82\xbd\x82\xcc\x88\xd3\x90}\x82\xb5\x82\xbd\x82\xc6\x82\xa8\x82\xe8\x82\xc9\x8f\xee\x95\xf1\x82\xf0\x93`\x82\xa6\x82\xe9
##\x82\xb1\x82\xc6\x82\xaa\x82\xc5\x82\xab\x82\xdc\x82\xb7\x81B
##\x82\xb3\x82\xe7\x82\xc9\x81AAdobe Acrobat 5.0\x82\xc5\x82\xcd\x81AWeb\x83u\x83\x89\x83E\x83U\x82\xa9\x82\xe7\x83R\x83\x81\x83\x93\x83g\x82\xe2
##\x83}\x81[\x83N\x83A\x83b\x83v\x82\xf0\x8f\x91\x82\xab\x8d\x9e\x82\xf1\x82\xbe\x82\xe8\x81A\x93d\x8eq\x8f\x90\x96\xbc\x82\xf0\x8f\x91\x82\xab
##\x8d\x9e\x82\xdd\x81A\x8c\xb4\x96{\x82\xc6\x82\xb5\x82\xc4\x83\x8d\x81[\x83J\x83\x8b\x82\xc9\x95\xdb\x91\xb6\x82\xb7\x82\xe9\x82\xb1\x82\xc6\x82\xe0\x89\xc2\x94\\\x82\xc5\x82\xb7\x81B
##\x8a\xe9\x8b\xc6\x93\xe0\x82\xa0\x82\xe9\x82\xa2\x82\xcd\x8a\xe9\x8b\xc6\x82\xcc\x98g\x82\xf0\x92\xb4\x82\xa6\x82\xc4\x83`\x81[\x83\x80\x82\xc5
##\x82\xcc\x83h\x83L\x83\x85\x83\x81\x83\x93\x83g\x83\x8f\x81[\x83N\x82\xcc\x90\xb6\x8eY\x90\xab\x82\xf0\x8c\xfc\x8f\xe3\x82\xb3\x82\xb9\x82\xe9\x82\xb1\x82\xc6\x82\xaa\x82\xc5\x82\xab\x82\xdc\x82\xb7\x81B
##
##Adobe Acrobat 5.0\x82\xc5\x8d\xec\x90\xac\x82\xb5\x82\xbdAdobe PDF\x82\xcd\x81A(Acrobat 5.0\x82\xc5\x82\xcc\x82\xdd\x83T\x83|\x81[\x83g
##\x82\xb5\x82\xc4\x82\xa2\x82\xe9\x88\xc3\x8d\x86\x89\xbb\x90\xdd\x92\xe8\x82\xf0\x8f\x9c\x82\xa2\x82\xc4\x82\xcd)\x8f]\x97\x88\x82\xdc
##\x82\xc5\x82\xcc\x83o\x81[\x83W\x83\x87\x83\x93(3\x82\xa8\x82\xe6\x82\xd1\x82S)\x82\xccAcrobat Reader\x82\xc5\x82\xe0\x8aJ\x82\xad
##\x82\xb1\x82\xc6\x82\xaa\x82\xc5\x82\xab\x82\xdc\x82\xb7\x81B\x8f\xee\x95\xf1\x8b\xa4\x97L\x82\xcc\x83c\x81[\x83\x8b\x82\xc6\x82\xb5
##\x82\xc4\x81A\x82\xb3\x82\xe7\x82\xc9\x90i\x95\xe0\x82\xb5\x82\xbdAdobe Acrobat 5.0\x82\xf0\x81A\x8f]\x97\x88\x82\xcc\x8a\xc2\x8b\xab
##\x82\xc5\x82\xe0\x88\xc0\x90S\x82\xb5\x82\xc4\x82\xb2\x97\x98\x97p\x82\xa2\x82\xbd\x82\xbe\x82\xaf\x82\xdc\x82\xb7\x81B
##
##\x96{\x90\xbb\x95i\x82\xf0\x83l\x83b\x83g\x83\x8f\x81[\x83N\x82\xc8\x82\xc7\x82\xf0\x89\xee\x82\xb5\x82\xc4\x92\xbc\x90\xda\x82\xa0\x82\xe9
##\x82\xa2\x82\xcd\x8a\xd4\x90\xda\x82\xc9\x95\xa1\x90\x94\x82\xcc\x92[\x96\x96\x82\xa9\x82\xe7\x8eg\x97p\x82\xb7\x82\xe9\x8f\xea\x8d\x87\x81A
##\x82\xbb\x82\xcc\x92[\x96\x96\x82\xc6\x93\xaf\x90\x94\x82\xcc\x83\x89\x83C\x83Z\x83\x93\x83X\x82\xf0\x82\xb2\x8dw\x93\xfc\x82\xad\x82\xbe
##\x82\xb3\x82\xa2\x81B\x96{\x90\xbb\x95i\x82\xcd\x83N\x83\x89\x83C\x83A\x83\x93\x83g\x97p\x83\\\x83t\x83g\x83E\x83F\x83A\x82\xc5\x82\xa0\x82\xe8
##\x81A\x83T\x81[\x83o\x97p\x83\\\x83t\x83g\x83E\x83F\x83A\x82\xc6\x82\xb5\x82\xc4\x82\xa8\x8eg\x82\xa2\x82\xa2\x82\xbd\x82\xbe\x82\xad\x82\xb1\x82\xc6
##\x82\xcd\x81A\x8f\xe3\x8bL\x95\xfb\x96@\x82\xc9\x82\xe6\x82\xe9\x88\xc8\x8aO\x81A\x8b\x96\x91\xf8\x82\xb3\x82\xea\x82\xc4\x82\xa2\x82\xdc\x82\xb9
##\x82\xf1\x81B\x95\xa1\x90\x94\x82\xcc\x83\x89\x83C\x83Z\x83\x93\x83X\x82\xf0\x82\xb2\x8dw\x93\xfc\x82\xb3\x82\xea\x82\xe9\x8f\xea\x8d\x87\x82\xc9
##\x82\xcd\x83\x89\x83C\x83Z\x83\x93\x83X\x83v\x83\x8d\x83O\x83\x89\x83\x80\x82\xf0\x82\xb2\x97\x98\x97p\x82\xc9\x82\xc8\x82\xe9\x82\xc6\x82\xa8\x93\xbe\x82\xc5\x82\xb7\x81B
##
##
##\x81y\x82\xa8\x92m\x82\xe7\x82\xb9\x81zMicrosoft Office XP\x82\xa9\x82\xe7PDF\x82\xf0\x8d\xec\x90\xac\x82\xb7\x82\xe9\x82\xc9\x82\xcd
##"""
## c.setFont('Helvetica', 24)
## c.drawString(100,750, "Sample text from Adobe's web site")
## tx = c.beginText(100,700)
## tx.setFont('Helvetica', 10)
## tx.textLine('Note: line wrapping has not been preserved and some lines may be wrapped in mid-word.')
## tx.textLine('We are just testing that we see Japanese and not random characters!')
## tx.setFont('HeiseiMin-W3-90ms-RKSJ-H',6)
## tx.textLines(sample)
## tx.setFont('Helvetica', 8)
## tx.textLine()
## tx.textLine()
## tx.textLines("""
## This test document shows Japanese output from the Reportlab PDF Library.
## You may use two fonts, HeiseiMin-W3 and HeiseiKakuGo-W5, and a number of
## different encodings.
##
## The available encoding names (with comments from the PDF specification) are:
## encodings_jpn = [
## # official encoding names, comments taken verbatim from PDF Spec
## '83pv-RKSJ-H', #Macintosh, JIS X 0208 character set with KanjiTalk6
## #extensions, Shift-JIS encoding, Script Manager code 1
## '90ms-RKSJ-H', #Microsoft Code Page 932 (lfCharSet 0x80), JIS X 0208
## #character set with NEC and IBM extensions
## '90ms-RKSJ-V', #Vertical version of 90ms-RKSJ-H
## '90msp-RKSJ-H', #Same as 90ms-RKSJ-H, but replaces half-width Latin
## #characters with proportional forms
## '90msp-RKSJ-V', #Vertical version of 90msp-RKSJ-H
## '90pv-RKSJ-H', #Macintosh, JIS X 0208 character set with KanjiTalk7
## #extensions, Shift-JIS encoding, Script Manager code 1
## 'Add-RKSJ-H', #JIS X 0208 character set with Fujitsu FMR extensions,
## #Shift-JIS encoding
## 'Add-RKSJ-V', #Vertical version of Add-RKSJ-H
## 'EUC-H', #JIS X 0208 character set, EUC-JP encoding
## 'EUC-V', #Vertical version of EUC-H
## 'Ext-RKSJ-H', #JIS C 6226 (JIS78) character set with NEC extensions,
## #Shift-JIS encoding
## 'Ext-RKSJ-V', #Vertical version of Ext-RKSJ-H
## 'H', #JIS X 0208 character set, ISO-2022-JP encoding,
## 'V', #Vertical version of H
## 'UniJIS-UCS2-H', #Unicode (UCS-2) encoding for the Adobe-Japan1 character
## #collection
## 'UniJIS-UCS2-V', #Vertical version of UniJIS-UCS2-H
## 'UniJIS-UCS2-HW-H', #Same as UniJIS-UCS2-H, but replaces proportional Latin
## #characters with half-width forms
## 'UniJIS-UCS2-HW-V' #Vertical version of UniJIS-UCS2-HW-H
## ]
##
## The next few pages show the complete character set available in the encoding
## "90ms-RKSJ-H" - Shift-JIS with the standard Microsoft extensions.
## """)
## c.drawText(tx)
##
## c.setFont('Helvetica',10)
## c.drawCentredString(297, 36, 'Page %d' % c.getPageNumber())
##
##
##
## c.showPage()
from reportlab.lib import textsplit
c.setFont('HeiseiMin-W3', 14)
y = 700
c.drawString(70, y, 'cannot end line')
y -= 20
for group in textsplit.CANNOT_START_LINE:
c.drawString(70, y, group)
y -= 20
c.setFont('Helvetica',10)
c.drawString(70, y, ' '.join(map(lambda x: repr(x)[4:-1], group)))
c.setFont('HeiseiMin-W3', 14)
y -= 20
y -= 20
c.drawString(70, y, 'cannot end line')
y -= 20
for group in textsplit.CANNOT_END_LINE:
c.drawString(70, y, group)
y -= 20
c.setFont('Helvetica',10)
c.drawString(70, y, ' '.join(map(lambda x: repr(x)[2:], group)))
c.setFont('HeiseiMin-W3', 14)
y -= 20
c.showPage()
#utf8 encoded paragraph
sample2_uni = u'''\u30ac\u30c8\u30a6\u30a3\u30c3\u30af\u7a7a\u6e2f\u3068\u9023\u7d61\u901a
\u8def\u3067\u76f4\u7d50\u3055\u308c\u3066\u3044\u308b\u552f\u4e00\u306e\u30db\u30c6\u30eb
\u3067\u3042\u308b\u5f53\u30db\u30c6\u30eb\u306f\u3001\u8857\u306e\u4e2d\u5fc3\u90e8\u304b
\u308930\u5206\u306e\u5834\u6240\u306b\u3054\u3056\u3044\u307e\u3059\u3002\u5168\u5ba2\u5ba4
\u306b\u9ad8\u901f\u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u74b0\u5883\u3092\u5b8c\u5099
\u3057\u3066\u304a\u308a\u307e\u3059\u3002\u30d5\u30a1\u30df\u30ea\u30fc\u30eb\u30fc\u30e0
\u306f5\u540d\u69d8\u307e\u3067\u304a\u6cca\u308a\u3044\u305f\u3060\u3051\u307e\u3059\u3002
\u307e\u305f\u3001\u30a8\u30b0\u30bc\u30af\u30c6\u30a3\u30d6\u30eb\u30fc\u30e0\u306e\u304a
\u5ba2\u69d8\u306f\u3001\u30a8\u30b0\u30bc\u30af\u30c6\u30a3\u30d6\u30e9\u30a6\u30f3\u30b8
\u3092\u3054\u5229\u7528\u3044\u305f\u3060\u3051\u307e\u3059\u3002\u4e8b\u524d\u306b\u3054
\u4e88\u7d04\u3044\u305f\u3060\u3051\u308b\u30bf\u30a4\u30e0\u30c8\u30a5\u30d5\u30e9\u30a4
\u30fb\u30d1\u30c3\u30b1\u30fc\u30b8\u306b\u306f\u3001\u7a7a\u6e2f\u306e\u99d0\u8eca\u6599
\u91d1\u304c\u542b\u307e\u308c\u3066\u304a\u308a\u307e\u3059\u3002'''
oneline_uni = u''.join(sample2_uni.split())
sample2_utf8 = oneline_uni.encode('utf8')
from reportlab.platypus import Paragraph
from reportlab.lib.styles import ParagraphStyle
jsty = ParagraphStyle('japanese',fontName='HeiseiMin-W3', wordWrap='CJK')
jpara = Paragraph(oneline_uni, style=jsty)
c.drawString(100, 710, 'Try to wrap a paragraph using a style with wordWrap="CJK"')
w, h = jpara.wrap(400,400)
jpara.drawOn(c, 100, 700 - h)
#now try to split it...
c.drawString(100, 510, 'Now try to split a paragraph as if over a page break')
topPara, bottomPara = jpara.split(400, 30)
w1, h1 = topPara.wrap(400, 30)
topPara.drawOn(c, 100, 450)
w2, h2 = bottomPara.wrap(400, 30)
bottomPara.drawOn(c, 100, 400)
#print 'split into heights %0.2f, %0.2f' % (topPara.height, bottomPara.height)
## c.showPage()
##
##
## # full kuten chart in EUC
## c.setFont('Helvetica', 24)
## c.drawString(72,750, 'Characters available in JIS 0208-1997')
## y = 600
## for row in range(1, 95):
## KutenRowCodeChart(row, 'HeiseiMin-W3','EUC-H').drawOn(c, 72, y)
## y = y - 125
## if y < 50:
## c.setFont('Helvetica',10)
## c.drawCentredString(297, 36, 'Page %d' % c.getPageNumber())
## c.showPage()
## y = 700
##
## c.showPage()
#try with Unicode truetype - Mincho for starters
## import time
## started = time.clock()
## c.showPage()
## c.setFont('Helvetica',16)
## c.drawString(100,750, 'About to say Tokyo in MS Gothic...')
##
## from reportlab.pdfbase.ttfonts import TTFont, TTFontFile
## f = TTFontFile("msgothic.ttf")
## print f.name
##
## pdfmetrics.registerFont(TTFont(f.name, f))
##
## utfText = u'Andr\202'.encode('utf8')
## c.setFont(f.name,16)
## c.drawString(100,700, utfText)
##
##
## #tokyoUCS2 = '\x67\x71\x4E\xAC'
## finished = time.clock()
c.save()
if VERBOSE:
print 'saved test_multibyte_jpn.pdf'
def ___test2_all(self):
"""Dumps out ALl GLYPHS in a CID font.
Reach for your microscope :-)"""
try:
from reportlab.pdfbase.cidfonts import CIDFont, findCMapFile
findCMapFile('90ms-RKSJ-H')
findCMapFile('Identity-H')
except:
#don't have the font pack. return silently
return
pdfmetrics.registerFont(CIDFont('HeiseiMin-W3','Identity-H'))
c = Canvas('test_japanese_2.pdf')
c.setFont('Helvetica', 30)
c.drawString(100,800, 'All Glyphs in Adobe-Japan-1-2 collection!')
# the two typefaces
c.setFont('HeiseiMin-W3-Identity-H', 2)
x0 = 50
y0 = 700
dx = 2
dy = 2
for row in range(256):
for cell in range(256):
s = chr(row) + chr(cell)
x = x0 + cell*dx
y = y0 - row*dy
c.drawString(x,y,s)
c.save()
if VERBOSE:
print 'saved '+outputfile('test_multibyte_jpn.pdf')
def makeSuite():
return makeSuiteForClasses(JapaneseFontTests)
#noruntests
if __name__ == "__main__":
VERBOSE = 1
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,125 @@
import string, os
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfgen.canvas import Canvas
from reportlab.lib import colors
from reportlab.lib.codecharts import KutenRowCodeChart, hBoxText
from reportlab.pdfbase.cidfonts import UnicodeCIDFont, findCMapFile
global VERBOSE
VERBOSE = 0
class KoreanFontTests(unittest.TestCase):
def test0(self):
# if they do not have the font files or encoding, go away quietly
## try:
## from reportlab.pdfbase.cidfonts import CIDFont, findCMapFile
## findCMapFile('KSCms-UHC-H')
## except:
## #don't have the font pack. return silently
## print 'CMap not found'
## return
localFontName = 'HYSMyeongJo-Medium'
c = Canvas(outputfile('test_multibyte_kor.pdf'))
c.setFont('Helvetica', 30)
c.drawString(100,700, 'Korean Font Support')
c.setFont('Helvetica', 10)
c.drawString(100,680, 'Short sample in Unicode; grey area should outline the text with correct width.')
hBoxText(u'\ub300\ud55c\ubbfc\uad6d = Korea',
c, 100, 660, 'HYSMyeongJo-Medium')
hBoxText(u'\uc548\uc131\uae30 = AHN Sung-Gi (Actor)',
c, 100, 640, 'HYGothic-Medium')
## pdfmetrics.registerFont(UnicodeCIDFont('HYSMyeongJo-Medium'))
## c.setFont('Helvetica', 10)
## c.drawString(100,610, "Longer sample From Adobe's Acrobat web page in EUC:")
##
## sample = """\xbf\xad \xbc\xf6 \xbe\xf8\xb4\xc2 \xb9\xae\xbc\xad\xb4\xc2 \xbe\xc6\xb9\xab\xb7\xb1 \xbc\xd2\xbf\xeb\xc0\xcc \xbe\xf8\xbd\xc0\xb4\xcf\xb4\xd9. \xbb\xe7\xbe\xf7 \xb0\xe8\xc8\xb9\xbc\xad, \xbd\xba\xc7\xc1\xb7\xb9\xb5\xe5\xbd\xc3\xc6\xae, \xb1\xd7\xb7\xa1\xc7\xc8\xc0\xcc \xb8\xb9\xc0\xcc \xc6\xf7\xc7\xd4\xb5\xc8 \xbc\xd2\xc3\xa5\xc0\xda \xb6\xc7\xb4\xc2 \xc0\xa5
##\xbb\xe7\xc0\xcc\xc6\xae\xb8\xa6 \xc0\xdb\xbc\xba\xc7\xcf\xb4\xc2 \xb0\xe6\xbf\xec Adobe\xa2\xe7 Acrobat\xa2\xe7 5.0 \xbc\xd2\xc7\xc1\xc6\xae\xbf\xfe\xbe\xee\xb8\xa6 \xbb\xe7\xbf\xeb\xc7\xd8\xbc\xad \xc7\xd8\xb4\xe7 \xb9\xae\xbc\xad\xb8\xa6 Adobe
##Portable Document Format (PDF) \xc6\xc4\xc0\xcf\xb7\xce \xba\xaf\xc8\xaf\xc7\xd2 \xbc\xf6 \xc0\xd6\xbd\xc0\xb4\xcf\xb4\xd9. \xb4\xa9\xb1\xb8\xb3\xaa \xb1\xa4\xb9\xfc\xc0\xa7\xc7\xd1 \xc1\xbe\xb7\xf9\xc0\xc7
##\xc7\xcf\xb5\xe5\xbf\xfe\xbe\xee\xbf\xcd \xbc\xd2\xc7\xc1\xc6\xae\xbf\xfe\xbe\xee\xbf\xa1\xbc\xad \xb9\xae\xbc\xad\xb8\xa6 \xbf\xad \xbc\xf6 \xc0\xd6\xc0\xb8\xb8\xe7 \xb7\xb9\xc0\xcc\xbe\xc6\xbf\xf4, \xc6\xf9\xc6\xae, \xb8\xb5\xc5\xa9, \xc0\xcc\xb9\xcc\xc1\xf6 \xb5\xee\xc0\xbb \xbf\xf8\xba\xbb \xb1\xd7\xb4\xeb\xb7\xce \xc0\xc7\xb5\xb5\xc7\xd1 \xb9\xd9 \xb4\xeb\xb7\xce
##\xc7\xa5\xbd\xc3\xc7\xd2 \xbc\xf6 \xc0\xd6\xbd\xc0\xb4\xcf\xb4\xd9. Acrobat 5.0\xc0\xbb \xbb\xe7\xbf\xeb\xc7\xcf\xbf\xa9 \xc0\xa5 \xba\xea\xb6\xf3\xbf\xec\xc0\xfa\xbf\xa1\xbc\xad \xb9\xae\xbc\xad\xb8\xa6 \xbd\xc2\xc0\xce\xc7\xcf\xb0\xed \xc1\xd6\xbc\xae\xc0\xbb \xc3\xdf\xb0\xa1\xc7\xcf\xb4\xc2 \xb9\xe6\xbd\xc4\xc0\xb8\xb7\xce
##\xb1\xe2\xbe\xf7\xc0\xc7 \xbb\xfd\xbb\xea\xbc\xba\xc0\xbb \xc7\xe2\xbb\xf3\xbd\xc3\xc5\xb3 \xbc\xf6 \xc0\xd6\xbd\xc0\xb4\xcf\xb4\xd9.
##
##\xc0\xfa\xc0\xdb\xb1\xc7 &copy; 2001 Adobe Systems Incorporated. \xb8\xf0\xb5\xe7 \xb1\xc7\xb8\xae\xb0\xa1 \xba\xb8\xc8\xa3\xb5\xcb\xb4\xcf\xb4\xd9.
##\xbb\xe7\xbf\xeb\xc0\xda \xbe\xe0\xb0\xfc
##\xbf\xc2\xb6\xf3\xc0\xce \xbb\xe7\xbf\xeb\xc0\xda \xba\xb8\xc8\xa3 \xb1\xd4\xc1\xa4
##Adobe\xc0\xc7 \xc0\xe5\xbe\xd6\xc0\xda \xc1\xf6\xbf\xf8
##\xbc\xd2\xc7\xc1\xc6\xae\xbf\xfe\xbe\xee \xba\xd2\xb9\xfd \xc0\xcc\xbf\xeb \xb9\xe6\xc1\xf6
##"""
## tx = c.beginText(100,600)
## tx.setFont('HYSMyeongJo-Medium-KSC-EUC-H', 7, 8)
## tx.textLines(sample)
## tx.setFont('Helvetica', 10, 12)
## tx.textLine()
## tx.textLines("""This test document shows Korean output from the Reportlab PDF Library.
## You may use one Korean font, HYSMyeongJo-Medium, and a number of different
## encodings.
##
## The available encoding names (with comments from the PDF specification) are:
## encodings_kor = [
## 'KSC-EUC-H', # KS X 1001:1992 character set, EUC-KR encoding
## 'KSC-EUC-V', # Vertical version of KSC-EUC-H
## 'KSCms-UHC-H', # Microsoft Code Page 949 (lfCharSet 0x81), KS X 1001:1992
## #character set plus 8,822 additional hangul, Unified Hangul
## #Code (UHC) encoding
## 'KSCms-UHC-V', #Vertical version of KSCms-UHC-H
## 'KSCms-UHC-HW-H', #Same as KSCms-UHC-H, but replaces proportional Latin
## # characters with halfwidth forms
## 'KSCms-UHC-HW-V', #Vertical version of KSCms-UHC-HW-H
## 'KSCpc-EUC-H', #Macintosh, KS X 1001:1992 character set with MacOS-KH
## #extensions, Script Manager Code 3
## 'UniKS-UCS2-H', #Unicode (UCS-2) encoding for the Adobe-Korea1 character collection
## 'UniKS-UCS2-V' #Vertical version of UniKS-UCS2-H
## ]
##
## The following pages show all characters in the KS X 1001:1992 standard, using the
## encoding 'KSC-EUC-H' above. More characters (a LOT more) are available if you
## use UHC encoding or the Korean Unicode subset, for which the correct encoding
## names are also listed above.
## """)
##
## c.drawText(tx)
##
## c.setFont('Helvetica',10)
## c.drawCentredString(297, 36, 'Page %d' % c.getPageNumber())
## c.showPage()
##
## # full kuten chart in EUC
## c.setFont('Helvetica', 18)
## c.drawString(72,750, 'Characters available in KS X 1001:1992, EUC encoding')
## y = 600
## for row in range(1, 95):
## KutenRowCodeChart(row, 'HYSMyeongJo-Medium','KSC-EUC-H').drawOn(c, 72, y)
## y = y - 125
## if y < 50:
## c.setFont('Helvetica',10)
## c.drawCentredString(297, 36, 'Page %d' % c.getPageNumber())
## c.showPage()
## y = 700
c.save()
if VERBOSE:
print 'saved '+outputfile('test_multibyte_kor.pdf')
def makeSuite():
return makeSuiteForClasses(KoreanFontTests)
#noruntests
if __name__ == "__main__":
VERBOSE = 1
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,174 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_paragraphs.py
# tests some paragraph styles
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.platypus import Paragraph, SimpleDocTemplate, XBox, Indenter, XPreformatted
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.units import inch
from reportlab.lib.colors import red, black, navy, white, green
from reportlab.lib.randomtext import randomText
from reportlab.rl_config import defaultPageSize
(PAGE_WIDTH, PAGE_HEIGHT) = defaultPageSize
def myFirstPage(canvas, doc):
canvas.saveState()
canvas.setStrokeColor(red)
canvas.setLineWidth(5)
canvas.line(66,72,66,PAGE_HEIGHT-72)
canvas.setFont('Times-Bold',24)
canvas.drawString(108, PAGE_HEIGHT-54, "TESTING PARAGRAPH STYLES")
canvas.setFont('Times-Roman',12)
canvas.drawString(4 * inch, 0.75 * inch, "First Page")
canvas.restoreState()
def myLaterPages(canvas, doc):
canvas.saveState()
canvas.setStrokeColor(red)
canvas.setLineWidth(5)
canvas.line(66,72,66,PAGE_HEIGHT-72)
canvas.setFont('Times-Roman',12)
canvas.drawString(4 * inch, 0.75 * inch, "Page %d" % doc.page)
canvas.restoreState()
class ParagraphTestCase(unittest.TestCase):
"Test Paragraph class (eyeball-test)."
def test0(self):
"""Test...
The story should contain...
Features to be visually confirmed by a human being are:
1. ...
2. ...
3. ...
"""
story = []
#need a style
styNormal = ParagraphStyle('normal')
styGreen = ParagraphStyle('green',parent=styNormal,textColor=green)
# some to test
stySpaced = ParagraphStyle('spaced',
parent=styNormal,
spaceBefore=12,
spaceAfter=12)
story.append(
Paragraph("This is a normal paragraph. "
+ randomText(), styNormal))
story.append(
Paragraph("This has 12 points space before and after, set in the style. "
+ randomText(), stySpaced))
story.append(
Paragraph("This is normal. " +
randomText(), styNormal))
story.append(
Paragraph("""<para spacebefore="12" spaceafter="12">
This has 12 points space before and after, set inline with
XML tag. It works too.""" + randomText() + "</para",
styNormal))
story.append(
Paragraph("This is normal. " +
randomText(), styNormal))
styBackground = ParagraphStyle('MyTitle',
fontName='Helvetica-Bold',
fontSize=24,
leading=28,
textColor=white,
backColor=navy)
story.append(
Paragraph("This is a title with a background. ", styBackground))
story.append(
Paragraph("""<para backcolor="pink">This got a background from the para tag</para>""", styNormal))
story.append(
Paragraph("""<para>\n\tThis has newlines and tabs on the front but inside the para tag</para>""", styNormal))
story.append(
Paragraph("""<para> This has spaces on the front but inside the para tag</para>""", styNormal))
story.append(
Paragraph("""\n\tThis has newlines and tabs on the front but no para tag""", styNormal))
story.append(
Paragraph(""" This has spaces on the front but no para tag""", styNormal))
story.append(Paragraph("""This has <font color=blue>blue text</font> here.""", styNormal))
story.append(Paragraph("""This has <i>italic text</i> here.""", styNormal))
story.append(Paragraph("""This has <b>bold text</b> here.""", styNormal))
story.append(Paragraph("""This has <u>underlined text</u> here.""", styNormal))
story.append(Paragraph("""This has <font color=blue><u>blue and <font color=red>red</font> underlined text</u></font> here.""", styNormal))
story.append(Paragraph("""<u>green underlining</u>""", styGreen))
story.append(Paragraph("""<u>green <font size=+4><i>underlining</font></i></u>""", styGreen))
story.append(Paragraph("""This has m<super>2</super> a superscript.""", styNormal))
story.append(Paragraph("""This has m<sub>2</sub> a subscript. Like H<sub>2</sub>O!""", styNormal))
story.append(Paragraph("""This has a font change to <font name=Helvetica>Helvetica</font>.""", styNormal))
#This one fails:
#story.append(Paragraph("""This has a font change to <font name=Helvetica-Oblique>Helvetica-Oblique</font>.""", styNormal))
story.append(Paragraph("""This has a font change to <font name=Helvetica><i>Helvetica in italics</i></font>.""", styNormal))
story.append(Paragraph('''This one uses upper case tags and has set caseSensitive=0: Here comes <FONT FACE="Helvetica" SIZE="14pt">Helvetica 14</FONT> with <STRONG>strong</STRONG> <EM>emphasis</EM>.''', styNormal, caseSensitive=0))
story.append(Paragraph('''The same as before, but has set not set caseSensitive, thus the tags are ignored: Here comes <FONT FACE="Helvetica" SIZE="14pt">Helvetica 14</FONT> with <STRONG>strong</STRONG> <EM>emphasis</EM>.''', styNormal))
story.append(Paragraph('''This one uses fonts with size "14pt" and also uses the em and strong tags: Here comes <font face="Helvetica" size="14pt">Helvetica 14</font> with <Strong>strong</Strong> <em>emphasis</em>.''', styNormal, caseSensitive=0))
story.append(Paragraph('''This uses a font size of 3cm: Here comes <font face="Courier" size="3cm">Courier 3cm</font> and normal again.''', styNormal, caseSensitive=0))
story.append(Paragraph('''This is just a very long silly text to see if the <FONT face="Courier">caseSensitive</FONT> flag also works if the paragraph is <EM>very</EM> long. '''*20, styNormal, caseSensitive=0))
story.append(Indenter("1cm"))
story.append(Paragraph("<para><bullet bulletIndent='-1cm'><seq id='s0'/>)</bullet>Indented list. %s</para>" % randomText(), styNormal))
story.append(Paragraph("<para><bullet bulletIndent='-1cm'><seq id='s0'/>)</bullet>Indented list. %s</para>" % randomText(), styNormal))
story.append(Paragraph("<para><bullet bulletIndent='-1cm'><seq id='s0'/>)</bullet>Indented list. %s</para>" % randomText(), styNormal))
story.append(Indenter("1cm"))
story.append(XPreformatted("<para leftIndent='0.5cm' backcolor=pink><bullet bulletIndent='-1cm'><seq id='s1'/>)</bullet>Indented list.</para>", styNormal))
story.append(XPreformatted("<para leftIndent='0.5cm' backcolor=palegreen><bullet bulletIndent='-1cm'><seq id='s1'/>)</bullet>Indented list.</para>", styNormal))
story.append(Indenter("-1cm"))
story.append(Paragraph("<para><bullet bulletIndent='-1cm'><seq id='s0'/>)</bullet>Indented list. %s</para>" % randomText(), styNormal))
story.append(Indenter("-1cm"))
story.append(Paragraph("<para>Indented list using seqChain/Format<seqChain order='s0 s1 s2 s3 s4'/><seqReset id='s0'/><seqFormat id='s0' value='1'/><seqFormat id='s1' value='a'/><seqFormat id='s2' value='i'/><seqFormat id='s3' value='A'/><seqFormat id='s4' value='I'/></para>", stySpaced))
story.append(Indenter("1cm"))
story.append(Paragraph("<para><bullet bulletIndent='-1cm'><seq id='s0'/>)</bullet>Indented list. %s</para>" % randomText(), styNormal))
story.append(Paragraph("<para><bullet bulletIndent='-1cm'><seq id='s0'/>)</bullet>Indented list. %s</para>" % randomText(), styNormal))
story.append(Paragraph("<para><bullet bulletIndent='-1cm'><seq id='s0'/>)</bullet>Indented list. %s</para>" % randomText(), styNormal))
story.append(Indenter("1cm"))
story.append(XPreformatted("<para backcolor=pink><bullet bulletIndent='-1cm'><seq id='s1'/>)</bullet>Indented list.</para>", styNormal))
story.append(XPreformatted("<para backcolor=pink><bullet bulletIndent='-1cm'><seq id='s1'/>)</bullet>Indented list.</para>", styNormal))
story.append(Indenter("-1cm"))
story.append(Paragraph("<para><bullet bulletIndent='-1cm'><seq id='s0'/>)</bullet>Indented list. %s</para>" % randomText(), styNormal))
story.append(Indenter("1cm"))
story.append(XPreformatted("<para backcolor=palegreen><bullet bulletIndent='-1cm'><seq id='s1'/>)</bullet>Indented list.</para>", styNormal))
story.append(Indenter("1cm"))
story.append(XPreformatted("<para><bullet bulletIndent='-1cm'><seq id='s2'/>)</bullet>Indented list. line1</para>", styNormal))
story.append(XPreformatted("<para><bullet bulletIndent='-1cm'><seq id='s2'/>)</bullet>Indented list. line2</para>", styNormal))
story.append(Indenter("-1cm"))
story.append(XPreformatted("<para backcolor=palegreen><bullet bulletIndent='-1cm'><seq id='s1'/>)</bullet>Indented list.</para>", styNormal))
story.append(Indenter("-1cm"))
story.append(Indenter("-1cm"))
template = SimpleDocTemplate(outputfile('test_paragraphs.pdf'),
showBoundary=1)
template.build(story,
onFirstPage=myFirstPage, onLaterPages=myLaterPages)
def makeSuite():
return makeSuiteForClasses(ParagraphTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,255 @@
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.pdfgen.canvas import Canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfbase import pdfutils
from reportlab.platypus.paragraph import Paragraph
from reportlab.lib.styles import ParagraphStyle
from reportlab.graphics.shapes import Drawing, String, Ellipse
import re
import codecs
textPat = re.compile(r'\([^(]*\)')
#test sentences
testCp1252 = 'copyright %s trademark %s registered %s ReportLab! Ol%s!' % (chr(169), chr(153),chr(174), chr(0xe9))
testUni = unicode(testCp1252, 'cp1252')
testUTF8 = testUni.encode('utf-8')
# expected result is octal-escaped text in the PDF
expectedCp1252 = pdfutils._escape(testCp1252)
def extractText(pdfOps):
"""Utility to rip out the PDF text within a block of PDF operators.
PDF will show a string draw as something like "(Hello World) Tj"
i.e. text is in curved brackets. Crude and dirty, probably fails
on escaped brackets.
"""
found = textPat.findall(pdfOps)
#chop off '(' and ')'
return map(lambda x:x[1:-1], found)
def subsetToUnicode(ttf, subsetCodeStr):
"""Return unicode string represented by given subsetCode string
as found when TrueType font rendered to PDF, ttf must be the font
object that was used."""
# This relies on TTFont internals and uses the first document
# and subset it finds
subset = ttf.state.values()[0].subsets[0]
chrs = []
for codeStr in subsetCodeStr.split('\\'):
if codeStr:
chrs.append(unichr(subset[int(codeStr[1:], 8)]))
return u''.join(chrs)
class TextEncodingTestCase(unittest.TestCase):
"""Tests of expected Unicode and encoding behaviour
"""
def setUp(self):
self.luxi = TTFont("Luxi", "luxiserif.ttf")
pdfmetrics.registerFont(self.luxi)
self.styNormal = ParagraphStyle(name='Helvetica', fontName='Helvetica-Oblique')
self.styTrueType = ParagraphStyle(name='TrueType', fontName='luxi')
def testStringWidth(self):
msg = 'Hello World'
assert abs(pdfmetrics.stringWidth(msg, 'Courier', 10) - 66.0) < 0.01
assert abs(pdfmetrics.stringWidth(msg, 'Helvetica', 10) - 51.67) < 0.01
assert abs(pdfmetrics.stringWidth(msg, 'Times-Roman', 10) - 50.27) < 0.01
assert abs(pdfmetrics.stringWidth(msg, 'Luxi', 10) - 50.22) < 0.01
uniMsg1 = u"Hello World"
assert abs(pdfmetrics.stringWidth(uniMsg1, 'Courier', 10) - 66.0) < 0.01
assert abs(pdfmetrics.stringWidth(uniMsg1, 'Helvetica', 10) - 51.67) < 0.01
assert abs(pdfmetrics.stringWidth(uniMsg1, 'Times-Roman', 10) - 50.27) < 0.01
assert abs(pdfmetrics.stringWidth(uniMsg1, 'Luxi', 10) - 50.22) < 0.01
# Courier are all 600 ems wide. So if one 'measures as utf8' one will
# get a wrong width as extra characters are seen
assert len(testCp1252) == 52
assert abs(pdfmetrics.stringWidth(testCp1252, 'Courier', 10, 'cp1252') - 312.0) < 0.01
# the test string has 5 more bytes and so "measures too long" if passed to
# a single-byte font which treats it as a single-byte string.
assert len(testUTF8)==57
assert abs(pdfmetrics.stringWidth(testUTF8, 'Courier', 10) - 312.0) < 0.01
assert len(testUni)==52
assert abs(pdfmetrics.stringWidth(testUni, 'Courier', 10) - 312.0) < 0.01
# now try a TrueType font. Should be able to accept Unicode or UTF8
assert abs(pdfmetrics.stringWidth(testUTF8, 'Luxi', 10) - 224.44) < 0.01
assert abs(pdfmetrics.stringWidth(testUni, 'Luxi', 10) - 224.44) < 0.01
def testUtf8Canvas(self):
"""Verify canvas declared as utf8 autoconverts.
This assumes utf8 input. It converts to the encoding of the
underlying font, so both text lines APPEAR the same."""
c = Canvas(outputfile('test_pdfbase_encodings_utf8.pdf'))
c.drawString(100,700, testUTF8)
# Set a font with UTF8 encoding
c.setFont('Luxi', 12)
# This should pass the UTF8 through unchanged
c.drawString(100,600, testUTF8)
# and this should convert from Unicode to UTF8
c.drawString(100,500, testUni)
# now add a paragraph in Latin-1 in the latin-1 style
p = Paragraph(testUTF8, style=self.styNormal, encoding="utf-8")
w, h = p.wrap(150, 100)
p.drawOn(c, 100, 400) #3
c.rect(100,300,w,h)
# now add a paragraph in UTF-8 in the UTF-8 style
p2 = Paragraph(testUTF8, style=self.styTrueType, encoding="utf-8")
w, h = p2.wrap(150, 100)
p2.drawOn(c, 300, 400) #4
c.rect(100,300,w,h)
# now add a paragraph in Unicode in the latin-1 style
p3 = Paragraph(testUni, style=self.styNormal)
w, h = p3.wrap(150, 100)
p3.drawOn(c, 100, 300)
c.rect(100,300,w,h)
# now add a paragraph in Unicode in the UTF-8 style
p4 = Paragraph(testUni, style=self.styTrueType)
p4.wrap(150, 100)
p4.drawOn(c, 300, 300)
c.rect(300,300,w,h)
# now a graphic
d1 = Drawing(400,50)
d1.add(Ellipse(200,25,200,12.5, fillColor=None))
d1.add(String(200,25,testUTF8, textAnchor='middle', encoding='utf-8'))
d1.drawOn(c, 100, 150)
# now a graphic in utf8
d2 = Drawing(400,50)
d2.add(Ellipse(200,25,200,12.5, fillColor=None))
d2.add(String(200,25,testUTF8, fontName='Luxi', textAnchor='middle', encoding='utf-8'))
d2.drawOn(c, 100, 100)
# now a graphic in Unicode with T1 font
d3 = Drawing(400,50)
d3.add(Ellipse(200,25,200,12.5, fillColor=None))
d3.add(String(200,25,testUni, textAnchor='middle'))
d3.drawOn(c, 100, 50)
# now a graphic in Unicode with TT font
d4 = Drawing(400,50)
d4.add(Ellipse(200,25,200,12.5, fillColor=None))
d4.add(String(200,25,testUni, fontName='Luxi', textAnchor='middle'))
d4.drawOn(c, 100, 0)
extracted = extractText(c.getCurrentPageContent())
self.assertEquals(extracted[0], expectedCp1252)
self.assertEquals(extracted[1], extracted[2])
#self.assertEquals(subsetToUnicode(self.luxi, extracted[1]), testUni)
c.save()
class FontEncodingTestCase(unittest.TestCase):
"""Make documents with custom encodings of Type 1 built-in fonts.
Nothing really to do with character encodings; this is about hacking the font itself"""
def test0(self):
"Make custom encodings of standard fonts"
# make a custom encoded font.
c = Canvas(outputfile('test_pdfbase_encodings.pdf'))
c.setPageCompression(0)
c.setFont('Helvetica', 12)
c.drawString(100, 700, 'The text below should be in a custom encoding in which all vowels become "z"')
# invent a new language where vowels are replaced with letter 'z'
zenc = pdfmetrics.Encoding('EncodingWithoutVowels', 'WinAnsiEncoding')
for ch in 'aeiou':
zenc[ord(ch)] = 'z'
for ch in 'AEIOU':
zenc[ord(ch)] = 'Z'
pdfmetrics.registerEncoding(zenc)
# now we can make a font based on this encoding
# AR hack/workaround: the name of the encoding must be a Python codec!
f = pdfmetrics.Font('FontWithoutVowels', 'Helvetica-Oblique', 'EncodingWithoutVowels')
pdfmetrics.registerFont(f)
c.setFont('FontWithoutVowels', 12)
c.drawString(125, 675, "The magic word is squamish ossifrage")
# now demonstrate adding a Euro to MacRoman, which lacks one
c.setFont('Helvetica', 12)
c.drawString(100, 650, "MacRoman encoding lacks a Euro. We'll make a Mac font with the Euro at #219:")
# WinAnsi Helvetica
pdfmetrics.registerFont(pdfmetrics.Font('Helvetica-WinAnsi', 'Helvetica-Oblique', 'WinAnsiEncoding'))
c.setFont('Helvetica-WinAnsi', 12)
c.drawString(125, 625, 'WinAnsi with Euro: character 128 = "\200"')
pdfmetrics.registerFont(pdfmetrics.Font('MacHelvNoEuro', 'Helvetica-Oblique', 'MacRomanEncoding'))
c.setFont('MacHelvNoEuro', 12)
c.drawString(125, 600, 'Standard MacRoman, no Euro: Character 219 = "\333"') # oct(219)=0333
# now make our hacked encoding
euroMac = pdfmetrics.Encoding('MacWithEuro', 'MacRomanEncoding')
euroMac[219] = 'Euro'
pdfmetrics.registerEncoding(euroMac)
pdfmetrics.registerFont(pdfmetrics.Font('MacHelvWithEuro', 'Helvetica-Oblique', 'MacWithEuro'))
c.setFont('MacHelvWithEuro', 12)
c.drawString(125, 575, 'Hacked MacRoman with Euro: Character 219 = "\333"') # oct(219)=0333
# now test width setting with and without _rl_accel - harder
# make an encoding where 'm' becomes 'i'
c.setFont('Helvetica', 12)
c.drawString(100, 500, "Recode 'm' to 'i' and check we can measure widths. Boxes should surround letters.")
sample = 'Mmmmm. ' * 6 + 'Mmmm'
c.setFont('Helvetica-Oblique',12)
c.drawString(125, 475, sample)
w = c.stringWidth(sample, 'Helvetica-Oblique', 12)
c.rect(125, 475, w, 12)
narrowEnc = pdfmetrics.Encoding('m-to-i')
narrowEnc[ord('m')] = 'i'
narrowEnc[ord('M')] = 'I'
pdfmetrics.registerEncoding(narrowEnc)
pdfmetrics.registerFont(pdfmetrics.Font('narrow', 'Helvetica-Oblique', 'm-to-i'))
c.setFont('narrow', 12)
c.drawString(125, 450, sample)
w = c.stringWidth(sample, 'narrow', 12)
c.rect(125, 450, w, 12)
c.setFont('Helvetica', 12)
c.drawString(100, 400, "Symbol & Dingbats fonts - check we still get valid PDF in StandardEncoding")
c.setFont('Symbol', 12)
c.drawString(100, 375, 'abcdefghijklmn')
c.setFont('ZapfDingbats', 12)
c.drawString(300, 375, 'abcdefghijklmn')
c.save()
def makeSuite():
return makeSuiteForClasses(
TextEncodingTestCase,
#FontEncodingTestCase - nobbled for now due to old stuff which needs removing.
)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,90 @@
import os
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.pdfgen.canvas import Canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.test.test_pdfbase_pdfmetrics import makeWidthTestForAllGlyphs
class EmbeddingTestCase(unittest.TestCase):
"Make documents with embedded fonts"
def test0(self):
"""Make documents with embedded fonts.
Just vam Rossum has kindly donated a font which we may use
for testing purposes. You need to contact him at just@letterror.com
if you want to use it for real."""
#LettError fonts should always be there. The others are voluntary.
ok = 1
c = Canvas(outputfile('test_pdfbase_fontembed.pdf'))
c.setPageCompression(0)
c.setFont('Helvetica', 12)
c.drawString(100, 700, 'This is Helvetica. The text below should be different fonts...')
if os.path.isfile('GDB_____.AFM') and os.path.isfile('GDB_____.PFB'):
# a normal text font
garaFace = pdfmetrics.EmbeddedType1Face('GDB_____.AFM','GDB_____.PFB')
faceName = 'AGaramond-Bold' # pulled from AFM file
pdfmetrics.registerTypeFace(garaFace)
garaFont = pdfmetrics.Font('MyGaramondBold', faceName, 'WinAnsiEncoding')
pdfmetrics.registerFont(garaFont)
c.setFont('AGaramond-Bold', 12)
c.drawString(100, 650, 'This should be in AGaramond-Bold')
if os.path.isfile('CR______.AFM') and os.path.isfile('CR______.PFB'):
# one with a custom encoding
cartaFace = pdfmetrics.EmbeddedType1Face('CR______.AFM','CR______.PFB')
faceName = 'Carta' # pulled from AFM file
pdfmetrics.registerTypeFace(cartaFace)
cartaFont = pdfmetrics.Font('Carta', 'Carta', 'CartaEncoding')
pdfmetrics.registerFont(cartaFont)
text = 'This should be in Carta, a map symbol font:'
c.setFont('Helvetica', 12)
c.drawString(100, 600, text)
w = c.stringWidth(text, 'Helvetica', 12)
c.setFont('Carta', 12)
c.drawString(100+w, 600, ' Hello World')
# LettError sample - creates on demand, we hope
y = 550
## justFace = pdfmetrics.EmbeddedType1Face('LeERC___.AFM','LeERC___.PFB')
##
## faceName = 'LettErrorRobot-Chrome' # pulled from AFM file
## pdfmetrics.registerTypeFace(justFace)
##
## justFont = pdfmetrics.Font('LettErrorRobot-Chrome', faceName, 'WinAnsiEncoding')
## pdfmetrics.registerFont(justFont)
c.setFont('LettErrorRobot-Chrome', 12)
c.drawString(100, y, 'This should be in LettErrorRobot-Chrome')
def testNamedFont(canv, fontName):
canv.showPage()
makeWidthTestForAllGlyphs(canv, fontName, outlining=0)
testNamedFont(c, 'LettErrorRobot-Chrome')
c.save()
def makeSuite():
return makeSuiteForClasses(EmbeddingTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,121 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_pdfbase_pdfmetrics.py
#test_pdfbase_pdfmetrics_widths
"""
Various tests for PDF metrics.
The main test prints out a PDF documents enabling checking of widths of every
glyph in every standard font. Long!
"""
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase import _fontdata
from reportlab.pdfgen.canvas import Canvas
from reportlab.lib import colors
verbose = 0
fontNamesToTest = _fontdata.standardFonts #[0:12] #leaves out Symbol and Dingbats for now
def decoratePage(c, header):
c.setFont('Helvetica-Oblique',10)
c.drawString(72, 800, header)
c.drawCentredString(297, 54, 'Page %d' % c.getPageNumber())
def makeWidthTestForAllGlyphs(canv, fontName, outlining=1):
"""New page, then runs down doing all the glyphs in one encoding"""
thisFont = pdfmetrics.getFont(fontName)
encName = thisFont.encName
canv.setFont('Helvetica-Bold', 12)
title = 'Glyph Metrics Test for font %s, ascent=%s, descent=%s, encoding=%s' % (fontName, str(thisFont.face.ascent), str(thisFont.face.descent), encName)
canv.drawString(80, 750, title)
canv.setFont('Helvetica-Oblique',10)
canv.drawCentredString(297, 54, 'Page %d' % canv.getPageNumber())
if outlining:
# put it in the outline
canv.bookmarkPage('GlyphWidths:' + fontName)
canv.addOutlineEntry(fontName,'GlyphWidths:' + fontName, level=1)
y = 720
widths = thisFont.widths
glyphNames = thisFont.encoding.vector
# need to get the right list of names for the font in question
for i in range(256):
if y < 72:
canv.showPage()
decoratePage(canv, title)
y = 750
glyphName = glyphNames[i]
if glyphName is not None:
canv.setFont('Helvetica', 10)
text = unicode(chr(i),encName).encode('utf8')*30
try:
w = canv.stringWidth(text, fontName, 10)
canv.drawString(80, y, '%03d %s w=%3d' % (i, glyphName, int((w/3.)*10)))
canv.setFont(fontName, 10)
canv.drawString(200, y, text)
# now work out width and put a red marker next to the end.
canv.setFillColor(colors.red)
canv.rect(200 + w, y-1, 5, 10, stroke=0, fill=1)
canv.setFillColor(colors.black)
except KeyError:
canv.drawString(200, y, 'Could not find glyph named "%s"' % glyphName)
y = y - 12
def makeTestDoc(fontNames):
filename = outputfile('test_pdfbase_pdfmetrics.pdf')
c = Canvas(filename)
c.bookmarkPage('Glyph Width Tests')
c.showOutline()
c.addOutlineEntry('Glyph Width Tests', 'Glyph Width Tests', level=0)
if verbose:
print # get it on a different line to the unittest log output.
for fontName in fontNames:
if verbose:
print 'width test for', fontName
makeWidthTestForAllGlyphs(c, fontName)
c.showPage()
c.save()
if verbose:
if verbose:
print 'saved',filename
class PDFMetricsTestCase(unittest.TestCase):
"Test various encodings used in PDF files."
def test0(self):
"Visual test for correct glyph widths"
makeTestDoc(fontNamesToTest)
def makeSuite():
return makeSuiteForClasses(PDFMetricsTestCase)
#noruntests
if __name__=='__main__':
usage = """Usage:
(1) test_pdfbase_pdfmetrics.py - makes doc for all standard fonts
(2) test_pdfbase_pdfmetrics.py fontname - " " for just one font."""
import sys
verbose = 1
# accept font names as arguments; otherwise it does the lot
if len(sys.argv) > 1:
for arg in sys.argv[1:]:
if not arg in fontNamesToTest:
print 'unknown font %s' % arg
print usage
sys.exit(0)
fontNamesToTest = sys.argv[1:]
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,52 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_pdfbase_pdfutils.py
"""Tests for utility functions in reportlab.pdfbase.pdfutils.
"""
import os
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, printLocation
from reportlab.pdfbase.pdfutils import _AsciiHexEncode, _AsciiHexDecode
from reportlab.pdfbase.pdfutils import _AsciiBase85Encode, _AsciiBase85Decode
class PdfEncodingTestCase(unittest.TestCase):
"Test various encodings used in PDF files."
def testAsciiHex(self):
"Test if the obvious test for whether ASCII-Hex encoding works."
plainText = 'What is the average velocity of a sparrow?'
encoded = _AsciiHexEncode(plainText)
decoded = _AsciiHexDecode(encoded)
msg = "Round-trip AsciiHex encoding failed."
assert decoded == plainText, msg
def testAsciiBase85(self):
"Test if the obvious test for whether ASCII-Base85 encoding works."
msg = "Round-trip AsciiBase85 encoding failed."
plain = 'What is the average velocity of a sparrow?'
#the remainder block can be absent or from 1 to 4 bytes
for i in xrange(55):
encoded = _AsciiBase85Encode(plain)
decoded = _AsciiBase85Decode(encoded)
assert decoded == plain, msg
plain = plain + chr(i)
def makeSuite():
return makeSuiteForClasses(PdfEncodingTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,77 @@
#!/bin/env python
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_pdfbase_postscript.py
__version__=''' $Id'''
__doc__="""Tests Postscript XObjects.
Nothing visiblke in Acrobat, but the resulting files
contain graphics and tray commands if exported to
a Postscript device in Acrobat 4.0"""
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.pdfgen.canvas import Canvas
class PostScriptTestCase(unittest.TestCase):
"Simplest test that makes PDF"
def testVisible(self):
"Makes a document with extra text - should export and distill"
c = Canvas(outputfile('test_pdfbase_postscript_visible.pdf'))
c.setPageCompression(0)
c.setFont('Helvetica-Bold', 18)
c.drawString(100,700, 'Hello World. This is page 1 of a 2 page document.')
c.showPage()
c.setFont('Helvetica-Bold', 16)
c.drawString(100,700, 'Page 2. This has some postscript drawing code.')
c.drawString(100,680, 'If you print it using a PS device and Acrobat 4/5,')
c.drawString(100,660, 'or export to Postscript, you should see the word')
c.drawString(100,640, '"Hello PostScript" below. In ordinary Acrobat Reader')
c.drawString(100,620, 'we expect to see nothing.')
c.addPostScriptCommand('/Helvetica findfont 48 scalefont setfont 100 400 moveto (Hello PostScript) show')
c.drawString(100,500, 'This document also inserts two postscript')
c.drawString(100,480, ' comments at beginning and endof the stream;')
c.drawString(100,460, 'search files for "%PS_BEFORE" and "%PS_AFTER".')
c.addPostScriptCommand('%PS_BEFORE', position=0)
c.addPostScriptCommand('%PS_AFTER', position=2)
c.save()
def testTray(self):
"Makes a document with tray command - only works on printers supporting it"
c = Canvas(outputfile('test_pdfbase_postscript_tray.pdf'))
c.setPageCompression(0)
c.setFont('Helvetica-Bold', 18)
c.drawString(100,700, 'Hello World. This is page 1 of a 2 page document.')
c.drawString(100,680, 'This also has a tray command ("5 setpapertray").')
c.addPostScriptCommand('5 setpapertray')
c.showPage()
c.setFont('Helvetica-Bold', 16)
c.drawString(100,700, 'Page 2. This should come from a different tray.')
c.drawString(100,680, 'Also, if you print it using a PS device and Acrobat 4/5,')
c.drawString(100,660, 'or export to Postscript, you should see the word')
c.drawString(100,640, '"Hello PostScript" below. In ordinary Acrobat Reader')
c.drawString(100,620, 'we expect to see nothing.')
c.addPostScriptCommand('/Helvetica findfont 48 scalefont setfont 100 400 moveto (Hello PostScript) show')
c.save()
def makeSuite():
return makeSuiteForClasses(PostScriptTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
print 'saved '+outputfile('test_pdfgen_postscript_visible.pdf')
print 'saved '+outputfile('test_pdfgen_postscript_tray.pdf')
printLocation()

View File

@ -0,0 +1,391 @@
"""Test TrueType font subsetting & embedding code.
This test uses a sample font (luxiserif.ttf) taken from XFree86 which is called Luxi
Serif Regular and is covered under the license in ../fonts/luxiserif_licence.txt.
"""
import string
from cStringIO import StringIO
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.pdfgen.canvas import Canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.pdfdoc import PDFDocument, PDFError
from reportlab.pdfbase.ttfonts import TTFont, TTFontFace, TTFontFile, TTFOpenFile, \
TTFontParser, TTFontMaker, TTFError, \
parse_utf8, makeToUnicodeCMap, \
FF_SYMBOLIC, FF_NONSYMBOLIC, \
calcChecksum, add32, _L2U32
def utf8(code):
"Convert a given UCS character index into UTF-8"
if code < 0 or code > 0x7FFFFFFF:
raise ValueError, 'Invalid UCS character 0x%x' % code
elif code < 0x00000080:
return chr(code)
elif code < 0x00000800:
return '%c%c' % \
(0xC0 + (code >> 6),
0x80 + (code & 0x3F))
elif code < 0x00010000:
return '%c%c%c' % \
(0xE0 + (code >> 12),
0x80 + ((code >> 6) & 0x3F),
0x80 + (code & 0x3F))
elif code < 0x00200000:
return '%c%c%c%c' % \
(0xF0 + (code >> 18),
0x80 + ((code >> 12) & 0x3F),
0x80 + ((code >> 6) & 0x3F),
0x80 + (code & 0x3F))
elif code < 0x04000000:
return '%c%c%c%c%c' % \
(0xF8 + (code >> 24),
0x80 + ((code >> 18) & 0x3F),
0x80 + ((code >> 12) & 0x3F),
0x80 + ((code >> 6) & 0x3F),
0x80 + (code & 0x3F))
else:
return '%c%c%c%c%c%c' % \
(0xFC + (code >> 30),
0x80 + ((code >> 24) & 0x3F),
0x80 + ((code >> 18) & 0x3F),
0x80 + ((code >> 12) & 0x3F),
0x80 + ((code >> 6) & 0x3F),
0x80 + (code & 0x3F))
def _simple_subset_generation(fn,npages,alter=0):
c = Canvas(outputfile(fn))
c.setFont('Helvetica', 30)
c.drawString(100,700, 'Unicode TrueType Font Test %d pages' % npages)
# Draw a table of Unicode characters
for p in xrange(npages):
for fontName in ('TestFont','RinaFont'):
c.setFont(fontName, 10)
for i in xrange(32):
for j in xrange(32):
ch = utf8(i * 32 + j+p*alter)
c.drawString(80 + j * 13 + int(j / 16) * 4, 600 - i * 13 - int(i / 8) * 8, ch)
c.showPage()
c.save()
class TTFontsTestCase(unittest.TestCase):
"Make documents with TrueType fonts"
def testTTF(self):
"Test PDF generation with TrueType fonts"
pdfmetrics.registerFont(TTFont("TestFont", "luxiserif.ttf"))
pdfmetrics.registerFont(TTFont("RinaFont", "rina.ttf"))
_simple_subset_generation('test_pdfbase_ttfonts1.pdf',1)
_simple_subset_generation('test_pdfbase_ttfonts3.pdf',3)
_simple_subset_generation('test_pdfbase_ttfonts35.pdf',3,5)
# Do it twice with the same font object
c = Canvas(outputfile('test_pdfbase_ttfontsadditional.pdf'))
# Draw a table of Unicode characters
c.setFont('TestFont', 10)
c.drawString(100, 700, 'Hello, ' + utf8(0xffee))
c.save()
class TTFontFileTestCase(unittest.TestCase):
"Tests TTFontFile, TTFontParser and TTFontMaker classes"
def testFontFileFailures(self):
"Tests TTFontFile constructor error checks"
self.assertRaises(TTFError, TTFontFile, "nonexistent file")
self.assertRaises(TTFError, TTFontFile, StringIO(""))
self.assertRaises(TTFError, TTFontFile, StringIO("invalid signature"))
self.assertRaises(TTFError, TTFontFile, StringIO("OTTO - OpenType not supported yet"))
self.assertRaises(TTFError, TTFontFile, StringIO("\0\1\0\0"))
def testFontFileReads(self):
"Tests TTFontParset.read_xxx"
class FakeTTFontFile(TTFontParser):
def __init__(self, data):
self._ttf_data = data
self._pos = 0
ttf = FakeTTFontFile("\x81\x02\x03\x04" "\x85\x06" "ABCD" "\x7F\xFF" "\x80\x00" "\xFF\xFF")
self.assertEquals(ttf.read_ulong(), _L2U32(0x81020304L)) # big-endian
self.assertEquals(ttf._pos, 4)
self.assertEquals(ttf.read_ushort(), 0x8506)
self.assertEquals(ttf._pos, 6)
self.assertEquals(ttf.read_tag(), 'ABCD')
self.assertEquals(ttf._pos, 10)
self.assertEquals(ttf.read_short(), 0x7FFF)
self.assertEquals(ttf.read_short(), -0x8000)
self.assertEquals(ttf.read_short(), -1)
def testFontFile(self):
"Tests TTFontFile and TTF parsing code"
ttf = TTFontFile("luxiserif.ttf")
self.assertEquals(ttf.name, "LuxiSerif")
self.assertEquals(ttf.flags, FF_SYMBOLIC)
self.assertEquals(ttf.italicAngle, 0.0)
self.assertEquals(ttf.ascent, 783) # FIXME: or 992?
self.assertEquals(ttf.descent, -206) # FIXME: or -210?
self.assertEquals(ttf.capHeight, 0)
self.assertEquals(ttf.bbox, [-204, -211, 983, 992])
self.assertEquals(ttf.stemV, 87)
self.assertEquals(ttf.defaultWidth, 250)
def testAdd32(self):
"Test add32"
self.assertEquals(add32(10, -6), 4)
self.assertEquals(add32(6, -10), -4)
self.assertEquals(add32(_L2U32(0x80000000L), -1), 0x7FFFFFFF)
self.assertEquals(add32(0x7FFFFFFF, 1), _L2U32(0x80000000L))
def testChecksum(self):
"Test calcChecksum function"
self.assertEquals(calcChecksum(""), 0)
self.assertEquals(calcChecksum("\1"), 0x01000000)
self.assertEquals(calcChecksum("\x01\x02\x03\x04\x10\x20\x30\x40"), 0x11223344)
self.assertEquals(calcChecksum("\x81"), _L2U32(0x81000000L))
self.assertEquals(calcChecksum("\x81\x02"), _L2U32(0x81020000L))
self.assertEquals(calcChecksum("\x81\x02\x03"), _L2U32(0x81020300L))
self.assertEquals(calcChecksum("\x81\x02\x03\x04"), _L2U32(0x81020304L))
self.assertEquals(calcChecksum("\x81\x02\x03\x04\x05"), _L2U32(0x86020304L))
self.assertEquals(calcChecksum("\x41\x02\x03\x04\xD0\x20\x30\x40"), 0x11223344)
self.assertEquals(calcChecksum("\xD1\x02\x03\x04\x40\x20\x30\x40"), 0x11223344)
self.assertEquals(calcChecksum("\x81\x02\x03\x04\x90\x20\x30\x40"), 0x11223344)
self.assertEquals(calcChecksum("\x7F\xFF\xFF\xFF\x00\x00\x00\x01"), _L2U32(0x80000000L))
def testFontFileChecksum(self):
"Tests TTFontFile and TTF parsing code"
file = TTFOpenFile("luxiserif.ttf")[1].read()
TTFontFile(StringIO(file), validate=1) # should not fail
file1 = file[:12345] + "\xFF" + file[12346:] # change one byte
self.assertRaises(TTFError, TTFontFile, StringIO(file1), validate=1)
file1 = file[:8] + "\xFF" + file[9:] # change one byte
self.assertRaises(TTFError, TTFontFile, StringIO(file1), validate=1)
def testSubsetting(self):
"Tests TTFontFile and TTF parsing code"
ttf = TTFontFile("luxiserif.ttf")
subset = ttf.makeSubset([0x41, 0x42])
subset = TTFontFile(StringIO(subset), 0)
for tag in ('cmap', 'head', 'hhea', 'hmtx', 'maxp', 'name', 'OS/2',
'post', 'cvt ', 'fpgm', 'glyf', 'loca', 'prep'):
self.assert_(subset.get_table(tag))
subset.seek_table('loca')
for n in range(4):
pos = subset.read_ushort() # this is actually offset / 2
self.failIf(pos % 2 != 0, "glyph %d at +%d should be long aligned" % (n, pos * 2))
self.assertEquals(subset.name, "LuxiSerif")
self.assertEquals(subset.flags, FF_SYMBOLIC)
self.assertEquals(subset.italicAngle, 0.0)
self.assertEquals(subset.ascent, 783) # FIXME: or 992?
self.assertEquals(subset.descent, -206) # FIXME: or -210?
self.assertEquals(subset.capHeight, 0)
self.assertEquals(subset.bbox, [-204, -211, 983, 992])
self.assertEquals(subset.stemV, 87)
def testFontMaker(self):
"Tests TTFontMaker class"
ttf = TTFontMaker()
ttf.add("ABCD", "xyzzy")
ttf.add("QUUX", "123")
ttf.add("head", "12345678xxxx")
stm = ttf.makeStream()
ttf = TTFontParser(StringIO(stm), 0)
self.assertEquals(ttf.get_table("ABCD"), "xyzzy")
self.assertEquals(ttf.get_table("QUUX"), "123")
class TTFontFaceTestCase(unittest.TestCase):
"Tests TTFontFace class"
def testAddSubsetObjects(self):
"Tests TTFontFace.addSubsetObjects"
face = TTFontFace("luxiserif.ttf")
doc = PDFDocument()
fontDescriptor = face.addSubsetObjects(doc, "TestFont", [ 0x78, 0x2017 ])
fontDescriptor = doc.idToObject[fontDescriptor.name].dict
self.assertEquals(fontDescriptor['Type'], '/FontDescriptor')
self.assertEquals(fontDescriptor['Ascent'], face.ascent)
self.assertEquals(fontDescriptor['CapHeight'], face.capHeight)
self.assertEquals(fontDescriptor['Descent'], face.descent)
self.assertEquals(fontDescriptor['Flags'], (face.flags & ~FF_NONSYMBOLIC) | FF_SYMBOLIC)
self.assertEquals(fontDescriptor['FontName'], "/TestFont")
self.assertEquals(fontDescriptor['FontBBox'].sequence, face.bbox)
self.assertEquals(fontDescriptor['ItalicAngle'], face.italicAngle)
self.assertEquals(fontDescriptor['StemV'], face.stemV)
fontFile = fontDescriptor['FontFile2']
fontFile = doc.idToObject[fontFile.name]
self.assert_(fontFile.content != "")
class TTFontTestCase(unittest.TestCase):
"Tests TTFont class"
def testParseUTF8(self):
"Tests parse_utf8"
self.assertEquals(parse_utf8(""), [])
for i in range(0, 0x80):
self.assertEquals(parse_utf8(chr(i)), [i])
for i in range(0x80, 0xA0):
self.assertRaises(ValueError, parse_utf8, chr(i))
self.assertEquals(parse_utf8("abc"), [0x61, 0x62, 0x63])
self.assertEquals(parse_utf8("\xC2\xA9x"), [0xA9, 0x78])
self.assertEquals(parse_utf8("\xE2\x89\xA0x"), [0x2260, 0x78])
self.assertRaises(ValueError, parse_utf8, "\xE2\x89x")
# for i in range(0, 0xFFFF): - overkill
for i in range(0x80, 0x200) + range(0x300, 0x400) + [0xFFFE, 0xFFFF]:
self.assertEquals(parse_utf8(utf8(i)), [i])
def testStringWidth(self):
"Test TTFont.stringWidth"
font = TTFont("TestFont", "luxiserif.ttf")
self.assert_(font.stringWidth("test", 10) > 0)
width = font.stringWidth(utf8(0x2260) * 2, 1000)
expected = font.face.getCharWidth(0x2260) * 2
self.assert_(abs(width - expected) < 0.01, "%g != %g" % (width, expected))
def testSplitString(self):
"Tests TTFont.splitString"
doc = PDFDocument()
font = TTFont("TestFont", "luxiserif.ttf")
text = string.join(map(utf8, xrange(0, 511)), "")
allchars = string.join(map(chr, xrange(0, 256)), "")
nospace = allchars[:32] + allchars[33:]
chunks = [(0, allchars), (1, nospace)]
self.assertEquals(font.splitString(text, doc), chunks)
# Do it twice
self.assertEquals(font.splitString(text, doc), chunks)
text = string.join(map(utf8, range(510, -1, -1)), "")
allchars = string.join(map(chr, range(255, -1, -1)), "")
nospace = allchars[:223] + allchars[224:]
chunks = [(1, nospace), (0, allchars)]
self.assertEquals(font.splitString(text, doc), chunks)
def testSplitStringSpaces(self):
# In order for justification (word spacing) to work, the space
# glyph must have a code 32, and no other character should have
# that code in any subset, or word spacing will be applied to it.
doc = PDFDocument()
font = TTFont("TestFont", "luxiserif.ttf")
text = string.join(map(utf8, range(512, -1, -1)), "")
chunks = font.splitString(text, doc)
state = font.state[doc]
self.assertEquals(state.assignments[32], 32)
self.assertEquals(state.subsets[0][32], 32)
self.assertEquals(state.subsets[1][32], 32)
def testSubsetInternalName(self):
"Tests TTFont.getSubsetInternalName"
doc = PDFDocument()
font = TTFont("TestFont", "luxiserif.ttf")
# Actually generate some subsets
text = string.join(map(utf8, range(0, 513)), "")
font.splitString(text, doc)
self.assertRaises(IndexError, font.getSubsetInternalName, -1, doc)
self.assertRaises(IndexError, font.getSubsetInternalName, 3, doc)
self.assertEquals(font.getSubsetInternalName(0, doc), "/F1+0")
self.assertEquals(font.getSubsetInternalName(1, doc), "/F1+1")
self.assertEquals(font.getSubsetInternalName(2, doc), "/F1+2")
self.assertEquals(doc.delayedFonts, [font])
def testAddObjectsEmpty(self):
"TTFont.addObjects should not fail when no characters were used"
font = TTFont("TestFont", "luxiserif.ttf")
doc = PDFDocument()
font.addObjects(doc)
def no_longer_testAddObjectsResets(self):
"Test that TTFont.addObjects resets the font"
# Actually generate some subsets
doc = PDFDocument()
font = TTFont("TestFont", "luxiserif.ttf")
font.splitString('a', doc) # create some subset
doc = PDFDocument()
font.addObjects(doc)
self.assertEquals(font.frozen, 0)
self.assertEquals(font.nextCode, 0)
self.assertEquals(font.subsets, [])
self.assertEquals(font.assignments, {})
font.splitString('ba', doc) # should work
def testParallelConstruction(self):
"Test that TTFont can be used for different documents at the same time"
doc1 = PDFDocument()
doc2 = PDFDocument()
font = TTFont("TestFont", "luxiserif.ttf")
self.assertEquals(font.splitString(u'hello ', doc1), [(0, 'hello ')])
self.assertEquals(font.splitString(u'hello ', doc2), [(0, 'hello ')])
self.assertEquals(font.splitString(u'\u0410\u0411'.encode('UTF-8'), doc1), [(0, '\x80\x81')])
self.assertEquals(font.splitString(u'\u0412'.encode('UTF-8'), doc2), [(0, '\x80')])
font.addObjects(doc1)
self.assertEquals(font.splitString(u'\u0413'.encode('UTF-8'), doc2), [(0, '\x81')])
font.addObjects(doc2)
def testAddObjects(self):
"Test TTFont.addObjects"
# Actually generate some subsets
doc = PDFDocument()
font = TTFont("TestFont", "luxiserif.ttf")
font.splitString('a', doc) # create some subset
internalName = font.getSubsetInternalName(0, doc)[1:]
font.addObjects(doc)
pdfFont = doc.idToObject[internalName]
self.assertEquals(doc.idToObject['BasicFonts'].dict[internalName], pdfFont)
self.assertEquals(pdfFont.Name, internalName)
self.assertEquals(pdfFont.BaseFont, "AAAAAA+LuxiSerif")
self.assertEquals(pdfFont.FirstChar, 0)
self.assertEquals(pdfFont.LastChar, 127)
self.assertEquals(len(pdfFont.Widths.sequence), 128)
toUnicode = doc.idToObject[pdfFont.ToUnicode.name]
self.assert_(toUnicode.content != "")
fontDescriptor = doc.idToObject[pdfFont.FontDescriptor.name]
self.assertEquals(fontDescriptor.dict['Type'], '/FontDescriptor')
def testMakeToUnicodeCMap(self):
"Test makeToUnicodeCMap"
self.assertEquals(makeToUnicodeCMap("TestFont", [ 0x1234, 0x4321, 0x4242 ]),
"""/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo
<< /Registry (TestFont)
/Ordering (TestFont)
/Supplement 0
>> def
/CMapName /TestFont def
/CMapType 2 def
1 begincodespacerange
<00> <02>
endcodespacerange
3 beginbfchar
<00> <1234>
<01> <4321>
<02> <4242>
endbfchar
endcmap
CMapName currentdict /CMap defineresource pop
end
end""")
def makeSuite():
suite = makeSuiteForClasses(
TTFontsTestCase,
TTFontFileTestCase,
TTFontFaceTestCase,
TTFontTestCase)
return suite
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,39 @@
#!/bin/env python
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_pdfgen_callback.py
__version__=''' $Id: test_pdfgen_callback.py 2619 2005-06-24 14:49:15Z rgbecker $ '''
__doc__='checks callbacks work'
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.pdfgen.canvas import Canvas
from reportlab.test.test_pdfgen_general import makeDocument
_PAGE_COUNT = 0
class CallBackTestCase(unittest.TestCase):
"checks it gets called"
def callMe(self, pageNo):
self.pageCount = pageNo
def test0(self):
"Make a PDFgen document with most graphics features"
self.pageCount = 0
makeDocument(outputfile('test_pdfgen_callback.pdf'), pageCallBack=self.callMe)
#no point saving it!
assert self.pageCount >= 7, 'page count not called!'
def makeSuite():
return makeSuiteForClasses(CallBackTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,840 @@
#!/bin/env python
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_pdfgen_general.py
__version__=''' $Id: test_pdfgen_general.py 2852 2006-05-08 15:04:15Z rgbecker $ '''
__doc__='testscript for reportlab.pdfgen'
#tests and documents new low-level canvas
import os, string
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.pdfgen import canvas # gmcm 2000/10/13, pdfgen now a package
from reportlab.lib.units import inch, cm
from reportlab.lib import colors
from reportlab.lib.utils import haveImages
#################################################################
#
# first some drawing utilities
#
#
################################################################
BASEFONT = ('Times-Roman', 10)
def framePageForm(c):
c.beginForm("frame")
c.saveState()
# forms can't do non-constant operations
#canvas.setFont('Times-BoldItalic',20)
#canvas.drawString(inch, 10.5 * inch, title)
#c.setFont('Times-Roman',10)
#c.drawCentredString(4.135 * inch, 0.75 * inch,
# 'Page %d' % c.getPageNumber())
#draw a border
c.setFillColor(colors.ReportLabBlue)
c.rect(0.3*inch, inch, 0.5*inch, 10*inch, fill=1)
from reportlab.lib import corp
c.translate(0.8*inch, 9.6*inch)
c.rotate(90)
logo = corp.ReportLabLogo(width=1.3*inch, height=0.5*inch, powered_by=1)
c.setFillColorRGB(1,1,1)
c.setStrokeColorRGB(1,1,1)
logo.draw(c)
#c.setStrokeColorRGB(1,0,0)
#c.setLineWidth(5)
#c.line(0.8 * inch, inch, 0.8 * inch, 10.75 * inch)
#reset carefully afterwards
#canvas.setLineWidth(1)
#canvas.setStrokeColorRGB(0,0,0)\
c.restoreState()
c.endForm()
def framePage(canvas, title):
global closeit
titlelist.append(title)
#canvas._inPage0() # do we need this at all? would be good to eliminate it
canvas.saveState()
canvas.setFont('Times-BoldItalic',20)
canvas.drawString(inch, 10.5 * inch, title)
canvas.bookmarkHorizontalAbsolute(title, 10.8*inch)
#newsection(title)
canvas.addOutlineEntry(title+" section", title, level=0, closed=closeit)
closeit = not closeit # close every other one
canvas.setFont('Times-Roman',10)
canvas.drawCentredString(4.135 * inch, 0.75 * inch,
'Page %d' % canvas.getPageNumber())
canvas.restoreState()
canvas.doForm("frame")
def makesubsection(canvas, title, horizontal):
canvas.bookmarkHorizontalAbsolute(title, horizontal)
#newsubsection(title)
canvas.addOutlineEntry(title+" subsection", title, level=1)
# outline helpers
#outlinenametree = []
#def newsection(name):
# outlinenametree.append(name)
#def newsubsection(name):
# from types import TupleType
# thissection = outlinenametree[-1]
# if type(thissection) is not TupleType:
# subsectionlist = []
# thissection = outlinenametree[-1] = (thissection, subsectionlist)
# else:
# (sectionname, subsectionlist) = thissection
# subsectionlist.append(name)
class DocBlock:
"""A DocBlock has a chunk of commentary and a chunk of code.
It prints the code and commentary, then executes the code,
which is presumed to draw in a region reserved for it.
"""
def __init__(self):
self.comment1 = "A doc block"
self.code = "canvas.setTextOrigin(cm, cm)\ncanvas.textOut('Hello World')"
self.comment2 = "That was a doc block"
self.drawHeight = 0
def _getHeight(self):
"splits into lines"
self.comment1lines = string.split(self.comment1, '\n')
self.codelines = string.split(self.code, '\n')
self.comment2lines = string.split(self.comment2, '\n')
textheight = (len(self.comment1lines) +
len(self.code) +
len(self.comment2lines) +
18)
return max(textheight, self.drawHeight)
def draw(self, canvas, x, y):
#specifies top left corner
canvas.saveState()
height = self._getHeight()
canvas.rect(x, y-height, 6*inch, height)
#first draw the text
canvas.setTextOrigin(x + 3 * inch, y - 12)
canvas.setFont('Times-Roman',10)
canvas.textLines(self.comment1)
drawCode(canvas, self.code)
canvas.textLines(self.comment2)
#now a box for the drawing, slightly within rect
canvas.rect(x + 9, y - height + 9, 198, height - 18)
#boundary:
self.namespace = {'canvas':canvas,'cm': cm,'inch':inch}
canvas.translate(x+9, y - height + 9)
codeObj = compile(self.code, '<sample>','exec')
exec codeObj in self.namespace
canvas.restoreState()
def drawAxes(canvas, label):
"""draws a couple of little rulers showing the coords -
uses points as units so you get an imperial ruler
one inch on each side"""
#y axis
canvas.line(0,0,0,72)
for y in range(9):
tenths = (y+1) * 7.2
canvas.line(-6,tenths,0,tenths)
canvas.line(-6, 66, 0, 72) #arrow...
canvas.line(6, 66, 0, 72) #arrow...
canvas.line(0,0,72,0)
for x in range(9):
tenths = (x+1) * 7.2
canvas.line(tenths,-6,tenths, 0)
canvas.line(66, -6, 72, 0) #arrow...
canvas.line(66, +6, 72, 0) #arrow...
canvas.drawString(18, 30, label)
def drawCrossHairs(canvas, x, y):
"""just a marker for checking text metrics - blue for fun"""
canvas.saveState()
canvas.setStrokeColorRGB(0,1,0)
canvas.line(x-6,y,x+6,y)
canvas.line(x,y-6,x,y+6)
canvas.restoreState()
def drawCode(canvas, code):
"""Draws a block of text at current point, indented and in Courier"""
canvas.addLiteral('36 0 Td')
canvas.setFillColor(colors.blue)
canvas.setFont('Courier',10)
t = canvas.beginText()
t.textLines(code)
c.drawText(t)
canvas.setFillColor(colors.black)
canvas.addLiteral('-36 0 Td')
canvas.setFont('Times-Roman',10)
def makeDocument(filename, pageCallBack=None):
#the extra arg is a hack added later, so other
#tests can get hold of the canvas just before it is
#saved
global titlelist, closeit
titlelist = []
closeit = 0
c = canvas.Canvas(filename)
c.setPageCompression(0)
c.setPageCallBack(pageCallBack)
framePageForm(c) # define the frame form
c.showOutline()
framePage(c, 'PDFgen graphics API test script')
makesubsection(c, "PDFgen", 10*inch)
#quickie encoding test: when canvas encoding not set,
#the following should do (tm), (r) and (c)
msg_uni = u'copyright\u00A9 trademark\u2122 registered\u00AE ReportLab in unicode!'
msg_utf8 = msg_uni.replace('unicode','utf8').encode('utf8')
c.drawString(100, 100, msg_uni)
c.drawString(100, 80, msg_utf8)
t = c.beginText(inch, 10*inch)
t.setFont('Times-Roman', 10)
drawCrossHairs(c, t.getX(),t.getY())
t.textLines("""
The ReportLab library permits you to create PDF documents directly from
your Python code. The "pdfgen" subpackage is the lowest level exposed
to the user and lets you directly position test and graphics on the
page, with access to almost the full range of PDF features.
The API is intended to closely mirror the PDF / Postscript imaging
model. There is an almost one to one correspondence between commands
and PDF operators. However, where PDF provides several ways to do a job,
we have generally only picked one.
The test script attempts to use all of the methods exposed by the Canvas
class, defined in reportlab/pdfgen/canvas.py
First, let's look at text output. There are some basic commands
to draw strings:
- canvas.setFont(fontname, fontsize [, leading])
- canvas.drawString(x, y, text)
- canvas.drawRightString(x, y, text)
- canvas.drawCentredString(x, y, text)
The coordinates are in points starting at the bottom left corner of the
page. When setting a font, the leading (i.e. inter-line spacing)
defaults to 1.2 * fontsize if the fontsize is not provided.
For more sophisticated operations, you can create a Text Object, defined
in reportlab/pdfgen/testobject.py. Text objects produce tighter PDF, run
faster and have many methods for precise control of spacing and position.
Basic usage goes as follows:
- tx = canvas.beginText(x, y)
- tx.textOut('Hello') # this moves the cursor to the right
- tx.textLine('Hello again') # prints a line and moves down
- y = tx.getY() # getX, getY and getCursor track position
- canvas.drawText(tx) # all gets drawn at the end
The green crosshairs below test whether the text cursor is working
properly. They should appear at the bottom left of each relevant
substring.
""")
t.setFillColorRGB(1,0,0)
t.setTextOrigin(inch, 4*inch)
drawCrossHairs(c, t.getX(),t.getY())
t.textOut('textOut moves across:')
drawCrossHairs(c, t.getX(),t.getY())
t.textOut('textOut moves across:')
drawCrossHairs(c, t.getX(),t.getY())
t.textOut('textOut moves across:')
drawCrossHairs(c, t.getX(),t.getY())
t.textLine('')
drawCrossHairs(c, t.getX(),t.getY())
t.textLine('textLine moves down')
drawCrossHairs(c, t.getX(),t.getY())
t.textLine('textLine moves down')
drawCrossHairs(c, t.getX(),t.getY())
t.textLine('textLine moves down')
drawCrossHairs(c, t.getX(),t.getY())
t.setTextOrigin(4*inch,3.25*inch)
drawCrossHairs(c, t.getX(),t.getY())
t.textLines('This is a multi-line\nstring with embedded newlines\ndrawn with textLines().\n')
drawCrossHairs(c, t.getX(),t.getY())
t.textLines(['This is a list of strings',
'drawn with textLines().'])
c.drawText(t)
t = c.beginText(2*inch,2*inch)
t.setFont('Times-Roman',10)
drawCrossHairs(c, t.getX(),t.getY())
t.textOut('Small text.')
drawCrossHairs(c, t.getX(),t.getY())
t.setFont('Courier',14)
t.textOut('Bigger fixed width text.')
drawCrossHairs(c, t.getX(),t.getY())
t.setFont('Times-Roman',10)
t.textOut('Small text again.')
drawCrossHairs(c, t.getX(),t.getY())
c.drawText(t)
#try out the decimal tabs high on the right.
c.setStrokeColor(colors.silver)
c.line(7*inch, 6*inch, 7*inch, 4.5*inch)
c.setFillColor(colors.black)
c.setFont('Times-Roman',10)
c.drawString(6*inch, 6.2*inch, "Testing decimal alignment")
c.drawString(6*inch, 6.05*inch, "- aim for silver line")
c.line(7*inch, 6*inch, 7*inch, 4.5*inch)
c.drawAlignedString(7*inch, 5.8*inch, "1,234,567.89")
c.drawAlignedString(7*inch, 5.6*inch, "3,456.789")
c.drawAlignedString(7*inch, 5.4*inch, "123")
c.setFillColor(colors.red)
c.drawAlignedString(7*inch, 5.2*inch, "(7,192,302.30)")
#mark the cursor where it stopped
c.showPage()
##############################################################
#
# page 2 - line styles
#
###############################################################
#page 2 - lines and styles
framePage(c, 'Line Drawing Styles')
# three line ends, lines drawn the hard way
#firt make some vertical end markers
c.setDash(4,4)
c.setLineWidth(0)
c.line(inch,9.2*inch,inch, 7.8*inch)
c.line(3*inch,9.2*inch,3*inch, 7.8*inch)
c.setDash() #clears it
c.setLineWidth(5)
c.setLineCap(0)
p = c.beginPath()
p.moveTo(inch, 9*inch)
p.lineTo(3*inch, 9*inch)
c.drawPath(p)
c.drawString(4*inch, 9*inch, 'the default - butt caps project half a width')
makesubsection(c, "caps and joins", 8.5*inch)
c.setLineCap(1)
p = c.beginPath()
p.moveTo(inch, 8.5*inch)
p.lineTo(3*inch, 8.5*inch)
c.drawPath(p)
c.drawString(4*inch, 8.5*inch, 'round caps')
c.setLineCap(2)
p = c.beginPath()
p.moveTo(inch, 8*inch)
p.lineTo(3*inch, 8*inch)
c.drawPath(p)
c.drawString(4*inch, 8*inch, 'square caps')
c.setLineCap(0)
# three line joins
c.setLineJoin(0)
p = c.beginPath()
p.moveTo(inch, 7*inch)
p.lineTo(2*inch, 7*inch)
p.lineTo(inch, 6.7*inch)
c.drawPath(p)
c.drawString(4*inch, 6.8*inch, 'Default - mitered join')
c.setLineJoin(1)
p = c.beginPath()
p.moveTo(inch, 6.5*inch)
p.lineTo(2*inch, 6.5*inch)
p.lineTo(inch, 6.2*inch)
c.drawPath(p)
c.drawString(4*inch, 6.3*inch, 'round join')
c.setLineJoin(2)
p = c.beginPath()
p.moveTo(inch, 6*inch)
p.lineTo(2*inch, 6*inch)
p.lineTo(inch, 5.7*inch)
c.drawPath(p)
c.drawString(4*inch, 5.8*inch, 'bevel join')
c.setDash(6,6)
p = c.beginPath()
p.moveTo(inch, 5*inch)
p.lineTo(3*inch, 5*inch)
c.drawPath(p)
c.drawString(4*inch, 5*inch, 'dash 6 points on, 6 off- setDash(6,6) setLineCap(0)')
makesubsection(c, "dash patterns", 5*inch)
c.setLineCap(1)
p = c.beginPath()
p.moveTo(inch, 4.5*inch)
p.lineTo(3*inch, 4.5*inch)
c.drawPath(p)
c.drawString(4*inch, 4.5*inch, 'dash 6 points on, 6 off- setDash(6,6) setLineCap(1)')
c.setLineCap(0)
c.setDash([1,2,3,4,5,6],0)
p = c.beginPath()
p.moveTo(inch, 4.0*inch)
p.lineTo(3*inch, 4.0*inch)
c.drawPath(p)
c.drawString(4*inch, 4*inch, 'dash growing - setDash([1,2,3,4,5,6],0) setLineCap(0)')
c.setLineCap(1)
c.setLineJoin(1)
c.setDash(32,12)
p = c.beginPath()
p.moveTo(inch, 3.0*inch)
p.lineTo(2.5*inch, 3.0*inch)
p.lineTo(inch, 2*inch)
c.drawPath(p)
c.drawString(4*inch, 3*inch, 'dash pattern, join and cap style interacting - ')
c.drawString(4*inch, 3*inch - 12, 'round join & miter results in sausages')
c.textAnnotation('Annotation',Rect=(4*inch, 3*inch-72, inch,inch-12))
c.showPage()
##############################################################
#
# higher level shapes
#
###############################################################
framePage(c, 'Shape Drawing Routines')
t = c.beginText(inch, 10*inch)
t.textLines("""
Rather than making your own paths, you have access to a range of shape routines.
These are built in pdfgen out of lines and bezier curves, but use the most compact
set of operators possible. We can add any new ones that are of general use at no
cost to performance.""")
t.textLine()
#line demo
makesubsection(c, "lines", 10*inch)
c.line(inch, 8*inch, 3*inch, 8*inch)
t.setTextOrigin(4*inch, 8*inch)
t.textLine('canvas.line(x1, y1, x2, y2)')
#bezier demo - show control points
makesubsection(c, "bezier curves", 7.5*inch)
(x1, y1, x2, y2, x3, y3, x4, y4) = (
inch, 6.5*inch,
1.2*inch, 7.5 * inch,
3*inch, 7.5 * inch,
3.5*inch, 6.75 * inch
)
c.bezier(x1, y1, x2, y2, x3, y3, x4, y4)
c.setDash(3,3)
c.line(x1,y1,x2,y2)
c.line(x3,y3,x4,y4)
c.setDash()
t.setTextOrigin(4*inch, 7 * inch)
t.textLine('canvas.bezier(x1, y1, x2, y2, x3, y3, x4, y4)')
#rectangle
makesubsection(c, "rectangles", 7*inch)
c.rect(inch, 5.25 * inch, 2 * inch, 0.75 * inch)
t.setTextOrigin(4*inch, 5.5 * inch)
t.textLine('canvas.rect(x, y, width, height) - x,y is lower left')
#wedge
makesubsection(c, "wedges", 5*inch)
c.wedge(inch, 5*inch, 3*inch, 4*inch, 0, 315)
t.setTextOrigin(4*inch, 4.5 * inch)
t.textLine('canvas.wedge(x1, y1, x2, y2, startDeg, extentDeg)')
t.textLine('Note that this is an elliptical arc, not just circular!')
#wedge the other way
c.wedge(inch, 4*inch, 3*inch, 3*inch, 0, -45)
t.setTextOrigin(4*inch, 3.5 * inch)
t.textLine('Use a negative extent to go clockwise')
#circle
makesubsection(c, "circles", 3.5*inch)
c.circle(1.5*inch, 2*inch, 0.5 * inch)
c.circle(3*inch, 2*inch, 0.5 * inch)
t.setTextOrigin(4*inch, 2 * inch)
t.textLine('canvas.circle(x, y, radius)')
c.drawText(t)
c.showPage()
##############################################################
#
# Page 4 - fonts
#
###############################################################
framePage(c, "Font Control")
c.drawString(inch, 10*inch, 'Listing available fonts...')
y = 9.5*inch
for fontname in c.getAvailableFonts():
c.setFont(fontname,24)
c.drawString(inch, y, 'This should be %s' % fontname)
y = y - 28
makesubsection(c, "fonts and colors", 4*inch)
c.setFont('Times-Roman', 12)
t = c.beginText(inch, 4*inch)
t.textLines("""Now we'll look at the color functions and how they interact
with the text. In theory, a word is just a shape; so setFillColorRGB()
determines most of what you see. If you specify other text rendering
modes, an outline color could be defined by setStrokeColorRGB() too""")
c.drawText(t)
t = c.beginText(inch, 2.75 * inch)
t.setFont('Times-Bold',36)
t.setFillColor(colors.green) #green
t.textLine('Green fill, no stroke')
#t.setStrokeColorRGB(1,0,0) #ou can do this in a text object, or the canvas.
t.setStrokeColor(colors.red) #ou can do this in a text object, or the canvas.
t.setTextRenderMode(2) # fill and stroke
t.textLine('Green fill, red stroke - yuk!')
t.setTextRenderMode(0) # back to default - fill only
t.setFillColorRGB(0,0,0) #back to default
t.setStrokeColorRGB(0,0,0) #ditto
c.drawText(t)
c.showPage()
#########################################################################
#
# Page 5 - coord transforms
#
#########################################################################
framePage(c, "Coordinate Transforms")
c.setFont('Times-Roman', 12)
t = c.beginText(inch, 10 * inch)
t.textLines("""This shows coordinate transformations. We draw a set of axes,
moving down the page and transforming space before each one.
You can use saveState() and restoreState() to unroll transformations.
Note that functions which track the text cursor give the cursor position
in the current coordinate system; so if you set up a 6 inch high frame
2 inches down the page to draw text in, and move the origin to its top
left, you should stop writing text after six inches and not eight.""")
c.drawText(t)
drawAxes(c, "0. at origin")
c.addLiteral('%about to translate space')
c.translate(2*inch, 7 * inch)
drawAxes(c, '1. translate near top of page')
c.saveState()
c.translate(1*inch, -2 * inch)
drawAxes(c, '2. down 2 inches, across 1')
c.restoreState()
c.saveState()
c.translate(0, -3 * inch)
c.scale(2, -1)
drawAxes(c, '3. down 3 from top, scale (2, -1)')
c.restoreState()
c.saveState()
c.translate(0, -5 * inch)
c.rotate(-30)
drawAxes(c, "4. down 5, rotate 30' anticlockwise")
c.restoreState()
c.saveState()
c.translate(3 * inch, -5 * inch)
c.skew(0,30)
drawAxes(c, "5. down 5, 3 across, skew beta 30")
c.restoreState()
c.showPage()
#########################################################################
#
# Page 6 - clipping
#
#########################################################################
framePage(c, "Clipping")
c.setFont('Times-Roman', 12)
t = c.beginText(inch, 10 * inch)
t.textLines("""This shows clipping at work. We draw a chequerboard of rectangles
into a path object, and clip it. This then forms a mask which limits the region of
the page on which one can draw. This paragraph was drawn after setting the clipping
path, and so you should only see part of the text.""")
c.drawText(t)
c.saveState()
#c.setFillColorRGB(0,0,1)
p = c.beginPath()
#make a chesboard effect, 1 cm squares
for i in range(14):
x0 = (3 + i) * cm
for j in range(7):
y0 = (16 + j) * cm
p.rect(x0, y0, 0.85*cm, 0.85*cm)
c.addLiteral('%Begin clip path')
c.clipPath(p)
c.addLiteral('%End clip path')
t = c.beginText(3 * cm, 22.5 * cm)
t.textLines("""This shows clipping at work. We draw a chequerboard of rectangles
into a path object, and clip it. This then forms a mask which limits the region of
the page on which one can draw. This paragraph was drawn after setting the clipping
path, and so you should only see part of the text.
This shows clipping at work. We draw a chequerboard of rectangles
into a path object, and clip it. This then forms a mask which limits the region of
the page on which one can draw. This paragraph was drawn after setting the clipping
path, and so you should only see part of the text.
This shows clipping at work. We draw a chequerboard of rectangles
into a path object, and clip it. This then forms a mask which limits the region of
the page on which one can draw. This paragraph was drawn after setting the clipping
path, and so you should only see part of the text.""")
c.drawText(t)
c.restoreState()
t = c.beginText(inch, 5 * inch)
t.textLines("""You can also use text as an outline for clipping with the text render mode.
The API is not particularly clean on this and one has to follow the right sequence;
this can be optimized shortly.""")
c.drawText(t)
#first the outline
c.saveState()
t = c.beginText(inch, 3.0 * inch)
t.setFont('Helvetica-BoldOblique',108)
t.setTextRenderMode(5) #stroke and add to path
t.textLine('Python!')
t.setTextRenderMode(0)
c.drawText(t) #this will make a clipping mask
#now some small stuff which wil be drawn into the current clip mask
t = c.beginText(inch, 4 * inch)
t.setFont('Times-Roman',6)
t.textLines((('spam ' * 40) + '\n') * 15)
c.drawText(t)
#now reset canvas to get rid of the clipping mask
c.restoreState()
c.showPage()
#########################################################################
#
# Page 7 - images
#
#########################################################################
framePage(c, "Images")
c.setFont('Times-Roman', 12)
t = c.beginText(inch, 10 * inch)
if not haveImages:
c.drawString(inch, 11*inch,
"Python or Java Imaging Library not found! Below you see rectangles instead of images.")
t.textLines("""PDFgen uses the Python Imaging Library (or, under Jython, java.awt.image and javax.imageio)
to process a very wide variety of image formats.
This page shows image capabilities. If I've done things right, the bitmap should have
its bottom left corner aligned with the crosshairs.
There are two methods for drawing images. The recommended use is to call drawImage.
This produces the smallest PDFs and the fastest generation times as each image's binary data is
only embedded once in the file. Also you can use advanced features like transparency masks.
You can also use drawInlineImage, which puts images in the page stream directly.
This is slightly faster for Acrobat to render or for very small images, but wastes
space if you use images more than once.""")
c.drawText(t)
if haveImages:
gif = os.path.join(os.path.dirname(unittest.__file__),'pythonpowered.gif')
c.drawInlineImage(gif,2*inch, 7*inch)
else:
c.rect(2*inch, 7*inch, 110, 44)
c.line(1.5*inch, 7*inch, 4*inch, 7*inch)
c.line(2*inch, 6.5*inch, 2*inch, 8*inch)
c.drawString(4.5 * inch, 7.25*inch, 'inline image drawn at natural size')
if haveImages:
c.drawInlineImage(gif,2*inch, 5*inch, inch, inch)
else:
c.rect(2*inch, 5*inch, inch, inch)
c.line(1.5*inch, 5*inch, 4*inch, 5*inch)
c.line(2*inch, 4.5*inch, 2*inch, 6*inch)
c.drawString(4.5 * inch, 5.25*inch, 'inline image distorted to fit box')
c.drawString(1.5 * inch, 4*inch, 'Image XObjects can be defined once in the file and drawn many times.')
c.drawString(1.5 * inch, 3.75*inch, 'This results in faster generation and much smaller files.')
for i in range(5):
if haveImages:
(w, h) = c.drawImage(gif, (1.5 + i)*inch, 3*inch)
else:
c.rect((1.5 + i)*inch, 3*inch, 110, 44)
myMask = [254,255,222,223,0,1]
c.drawString(1.5 * inch, 2.5*inch, "The optional 'mask' parameter lets you define transparent colors. We used a color picker")
c.drawString(1.5 * inch, 2.3*inch, "to determine that the yellow in the image above is RGB=(225,223,0). We then define a mask")
c.drawString(1.5 * inch, 2.1*inch, "spanning these RGB values: %s. The background vanishes!!" % myMask)
c.drawString(2.5*inch, 1.2*inch, 'This would normally be obscured')
if haveImages:
c.drawImage(gif, 1*inch, 1.2*inch, w, h, mask=myMask)
c.drawImage(gif, 3*inch, 1.2*inch, w, h, mask='auto')
else:
c.rect(1*inch, 1.2*inch, w, h)
c.rect(3*inch, 1.2*inch, w, h)
c.showPage()
if haveImages:
import shutil
c.drawString(1*inch, 10.25*inch, 'This jpeg is actually a gif')
jpg = outputfile('_i_am_actually_a_gif.jpg')
shutil.copyfile(gif,jpg)
c.drawImage(jpg, 1*inch, 9.25*inch, w, h, mask='auto')
tjpg = os.path.join(os.path.dirname(os.path.dirname(gif)),'docs','images','lj8100.jpg')
if os.path.isfile(tjpg):
c.drawString(4*inch, 10.25*inch, 'This gif is actually a jpeg')
tgif = outputfile(os.path.basename('_i_am_actually_a_jpeg.gif'))
shutil.copyfile(tjpg,tgif)
c.drawImage(tgif, 4*inch, 9.25*inch, w, h, mask='auto')
c.showPage()
#########################################################################
#
# Page 8 - Forms and simple links
#
#########################################################################
framePage(c, "Forms and Links")
c.setFont('Times-Roman', 12)
t = c.beginText(inch, 10 * inch)
t.textLines("""Forms are sequences of text or graphics operations
which are stored only once in a PDF file and used as many times
as desired. The blue logo bar to the left is an example of a form
in this document. See the function framePageForm in this demo script
for an example of how to use canvas.beginForm(name, ...) ... canvas.endForm().
Documents can also contain cross references where (for example) a rectangle
on a page may be bound to a position on another page. If the user clicks
on the rectangle the PDF viewer moves to the bound position on the other
page. There are many other types of annotations and links supported by PDF.
For example, there is a bookmark to each page in this document and below
is a browsable index that jumps to those pages. In addition we show two
URL hyperlinks; for these, you specify a rectangle but must draw the contents
or any surrounding rectangle yourself.
""")
c.drawText(t)
nentries = len(titlelist)
xmargin = 3*inch
xmax = 7*inch
ystart = 6.54*inch
ydelta = 0.4*inch
for i in range(nentries):
yposition = ystart - i*ydelta
title = titlelist[i]
c.drawString(xmargin, yposition, title)
c.linkAbsolute(title, title, (xmargin-ydelta/4, yposition-ydelta/4, xmax, yposition+ydelta/2))
# test URLs
r1 = (inch, 3*inch, 5*inch, 3.25*inch) # this is x1,y1,x2,y2
c.linkURL('http://www.reportlab.com/', r1, thickness=1, color=colors.green)
c.drawString(inch+3, 3*inch+6, 'Hyperlink to www.reportlab.com, with green border')
r1 = (inch, 2.5*inch, 5*inch, 2.75*inch) # this is x1,y1,x2,y2
c.linkURL('mailto:reportlab-users@egroups.com', r1) #, border=0)
c.drawString(inch+3, 2.5*inch+6, 'mailto: hyperlink, without border')
r1 = (inch, 2*inch, 5*inch, 2.25*inch) # this is x1,y1,x2,y2
c.linkURL('http://www.reportlab.com/', r1,
thickness=2,
dashArray=[2,4],
color=colors.magenta)
c.drawString(inch+3, 2*inch+6, 'Hyperlink with custom border style')
xpdf = outputfile('test_hello.pdf').replace('\\','/')
link = 'Hard link to %s, with red border' % xpdf
r1 = (inch, 1.5*inch, inch+2*3+c.stringWidth(link,c._fontname, c._fontsize), 1.75*inch) # this is x1,y1,x2,y2
c.linkURL(xpdf, r1, thickness=1, color=colors.red, kind='GoToR')
c.drawString(inch+3, 1.5*inch+6, link )
### now do stuff for the outline
#for x in outlinenametree: print x
#stop
#apply(c.setOutlineNames0, tuple(outlinenametree))
return c
def run(filename):
c = makeDocument(filename)
c.save()
c = makeDocument(filename)
import os
f = os.path.splitext(filename)
f = open('%sm%s' % (f[0],f[1]),'wb')
f.write(c.getpdfdata())
f.close()
def pageShapes(c):
"""Demonstrates the basic lines and shapes"""
c.showPage()
framePage(c, "Basic line and shape routines""")
c.setTextOrigin(inch, 10 * inch)
c.setFont('Times-Roman', 12)
c.textLines("""pdfgen provides some basic routines for drawing straight and curved lines,
and also for solid shapes.""")
y = 9 * inch
d = DocBlock()
d.comment1 = 'Lesson one'
d.code = "canvas.textOut('hello, world')"
print d.code
d.comment2 = 'Lesson two'
d.draw(c, inch, 9 * inch)
class PdfgenTestCase(unittest.TestCase):
"Make documents with lots of Pdfgen features"
def test0(self):
"Make a PDFgen document with most graphics features"
run(outputfile('test_pdfgen_general.pdf'))
def makeSuite():
return makeSuiteForClasses(PdfgenTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,187 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#this test and associates functionality kinds donated by Ian Sparks.
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_pdfgen_links.py
"""
Tests for internal links and destinations
"""
#
# Fit tests
#
# Modification History
# ====================
#
# 11-Mar-2003 Ian Sparks
# * Initial version.
#
#
from reportlab.pdfgen import canvas
from reportlab.lib.units import inch
from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
def markPage(c,height=letter[1],width=letter[0]):
height = height / inch
width = width / inch
for y in range(int(height)):
for x in range(int(width)):
c.drawString(x*inch,y*inch,"x=%d y=%d" % (x,y) )
c.line(x*inch,0,x*inch,height*inch)
c.line(0,y*inch,width*inch,y*inch)
fn = outputfile("test_pdfgen_links.pdf")
class LinkTestCase(unittest.TestCase):
"Test classes."
def test1(self):
c = canvas.Canvas(fn,pagesize=letter)
#Page 1
c.setFont("Courier", 10)
markPage(c)
c.bookmarkPage("P1")
c.addOutlineEntry("Page 1","P1")
#Note : XYZ Left is ignored because at this zoom the whole page fits the screen
c.bookmarkPage("P1_XYZ",fit="XYZ",top=7*inch,left=3*inch,zoom=0.5)
c.addOutlineEntry("Page 1 XYZ #1 (top=7,left=3,zoom=0.5)","P1_XYZ",level=1)
c.bookmarkPage("P1_XYZ2",fit="XYZ",top=7*inch,left=3*inch,zoom=5)
c.addOutlineEntry("Page 1 XYZ #2 (top=7,left=3,zoom=5)","P1_XYZ2",level=1)
c.bookmarkPage("P1_FIT",fit="Fit")
c.addOutlineEntry("Page 1 Fit","P1_FIT",level=1)
c.bookmarkPage("P1_FITH",fit="FitH",top=2*inch)
c.addOutlineEntry("Page 1 FitH (top = 2 inch)","P1_FITH",level=1)
c.bookmarkPage("P1_FITV",fit="FitV",left=3*inch)
c.addOutlineEntry("Page 1 FitV (left = 3 inch)","P1_FITV",level=1)
c.bookmarkPage("P1_FITR",fit="FitR",left=1*inch,bottom=2*inch,right=5*inch,top=6*inch)
c.addOutlineEntry("Page 1 FitR (left=1,bottom=2,right=5,top=6)","P1_FITR",level=1)
c.bookmarkPage("P1_FORWARD")
c.addOutlineEntry("Forward References","P1_FORWARD",level=2)
c.addOutlineEntry("Page 3 XYZ (top=7,left=3,zoom=0)","P3_XYZ",level=3)
#Create link to FitR on page 3
c.saveState()
c.setFont("Courier", 14)
c.setFillColor(colors.blue)
c.drawString(inch+20,inch+20,"Click to jump to the meaning of life")
c.linkAbsolute("","MOL",(inch+10,inch+10,6*inch,2*inch))
c.restoreState()
#Create linkAbsolute to page 2
c.saveState()
c.setFont("Courier", 14)
c.setFillColor(colors.green)
c.drawString(4*inch,4*inch,"Jump to 2.5 inch position on page 2")
c.linkAbsolute("","HYPER_1",(3.75*inch,3.75*inch,8.25*inch,4.25*inch))
c.restoreState()
c.showPage()
#Page 2
c.setFont("Helvetica", 10)
markPage(c)
c.bookmarkPage("P2")
c.addOutlineEntry("Page 2","P2")
#Note : This time left will be at 3*inch because the zoom makes the page to big to fit
c.bookmarkPage("P2_XYZ",fit="XYZ",top=7*inch,left=3*inch,zoom=2)
c.addOutlineEntry("Page 2 XYZ (top=7,left=3,zoom=2.0)","P2_XYZ",level=1)
c.bookmarkPage("P2_FIT",fit="Fit")
c.addOutlineEntry("Page 2 Fit","P2_FIT",level=1)
c.bookmarkPage("P2_FITH",fit="FitH",top=2*inch)
c.addOutlineEntry("Page 2 FitH (top = 2 inch)","P2_FITH",level=1)
c.bookmarkPage("P2_FITV",fit="FitV",left=10*inch)
c.addOutlineEntry("Page 2 FitV (left = 10 inch)","P2_FITV",level=1)
c.bookmarkPage("P2_FITR",fit="FitR",left=1*inch,bottom=2*inch,right=5*inch,top=6*inch)
c.addOutlineEntry("Page 2 FitR (left=1,bottom=2,right=5,top=6)","P2_FITR",level=1)
c.bookmarkPage("P2_FORWARD")
c.addOutlineEntry("Forward References","P2_FORWARD",level=2)
c.addOutlineEntry("Page 3 XYZ (top=7,left=3,zoom=0)","P3_XYZ",level=3)
c.bookmarkPage("P2_BACKWARD")
c.addOutlineEntry("Backward References","P2_BACKWARD",level=2)
c.addOutlineEntry("Page 1 Fit","P1_FIT",level=3)
c.addOutlineEntry("Page 1 FitR (left=1,bottom=2,right=5,top=6)","P1_FITR",level=3)
#Horizontal absolute test from page 1. Note that because of the page size used on page 3 all this will do
#is put the view centered on the bookmark. If you want to see it "up close and personal" change page3 to be
#the same page size as the other pages.
c.saveState()
c.setFont("Courier", 14)
c.setFillColor(colors.green)
c.drawString(2.5*inch,2.5*inch,"This line is hyperlinked from page 1")
# c.bookmarkHorizontalAbsolute("HYPER_1",3*inch) #slightly higher than the text otherwise text is of screen above.
c.bookmarkPage("HYPER_1",fit="XYZ",top=2.5*inch,bottom=2*inch)
c.restoreState()
#
c.showPage()
#Page 3
c.setFont("Times-Roman", 10)
#Turn the page on its size and make it 2* the normal "width" in order to have something to test FitV against.
c.setPageSize((2*letter[1],letter[0]))
markPage(c,height=letter[0],width=2*letter[1])
c.bookmarkPage("P3")
c.addOutlineEntry("Page 3 (Double-wide landscape page)","P3")
#Note : XYZ with no zoom (set it to something first
c.bookmarkPage("P3_XYZ",fit="XYZ",top=7*inch,left=3*inch,zoom=0)
c.addOutlineEntry("Page 3 XYZ (top=7,left=3,zoom=0)","P3_XYZ",level=1)
#FitV works here because the page is so wide it can"t all fit on the page
c.bookmarkPage("P3_FITV",fit="FitV",left=10*inch)
c.addOutlineEntry("Page 3 FitV (left = 10 inch)","P3_FITV",level=1)
c.bookmarkPage("P3_BACKWARD")
c.addOutlineEntry("Backward References","P3_BACKWARD",level=2)
c.addOutlineEntry("Page 1 XYZ #1 (top=7,left=3,zoom=0.5)","P1_XYZ",level=3)
c.addOutlineEntry("Page 1 XYZ #2 (top=7,left=3,zoom=5)","P1_XYZ2",level=3)
c.addOutlineEntry("Page 2 FitV (left = 10 inch)","P2_FITV",level=3)
#Add link from page 1
c.saveState()
c.setFont("Courier", 40)
c.setFillColor(colors.green)
c.drawString(5*inch,6*inch,"42")
c.bookmarkPage("MOL",fit="FitR",left=4*inch,top=7*inch,bottom=4*inch,right=6*inch)
c.showOutline()
c.save()
def makeSuite():
return makeSuiteForClasses(LinkTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
print "wrote", fn
printLocation()

View File

@ -0,0 +1,70 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_pdfgen_pagemodes.py
# full screen test
"""Tests for PDF page modes support in reportlab.pdfgen.
"""
import os
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.pdfgen.canvas import Canvas
def fileDoesExist(path):
"Check if a file does exist."
return os.path.exists(path)
class PdfPageModeTestCase(unittest.TestCase):
"Testing different page modes for opening a file in Acrobat Reader."
baseFileName = 'test_pagemodes_'
def _doTest(self, filename, mode, desc):
"A generic method called by all test real methods."
filename = outputfile(self.baseFileName + filename)
c = Canvas(filename)
# Handle different modes.
if mode == 'FullScreen':
c.showFullScreen0()
elif mode == 'Outline':
c.bookmarkPage('page1')
c.addOutlineEntry('Token Outline Entry', 'page1')
c.showOutline()
elif mode == 'UseNone':
pass
c.setFont('Helvetica', 20)
c.drawString(100, 700, desc)
c.save()
assert fileDoesExist(filename)
def test0(self):
"This should open in full screen mode."
self._doTest('FullScreen.pdf', 'FullScreen', self.test0.__doc__)
def test1(self):
"This should open with outline visible."
self._doTest('Outline.pdf', 'Outline', self.test1.__doc__)
def test2(self):
"This should open in the user's default mode."
self._doTest('UseNone.pdf', 'UseNone', self.test2.__doc__)
def makeSuite():
return makeSuiteForClasses(PdfPageModeTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,790 @@
#!/bin/env python
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
__version__=''' $Id: test_pdfgen_pycanvas.py 2619 2005-06-24 14:49:15Z rgbecker $ '''
__doc__='testscript for reportlab.pdfgen'
#tests and documents new low-level canvas and the pycanvas module to output Python source code.
import string, os
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.pdfgen import pycanvas # gmcm 2000/10/13, pdfgen now a package
from reportlab.lib.units import inch, cm
from reportlab.lib import colors
from reportlab.lib.utils import haveImages, _RL_DIR, rl_isfile
#################################################################
#
# first some drawing utilities
#
#
################################################################
BASEFONT = ('Times-Roman', 10)
def framePageForm(c):
c.beginForm("frame")
c.saveState()
# forms can't do non-constant operations
#canvas.setFont('Times-BoldItalic',20)
#canvas.drawString(inch, 10.5 * inch, title)
#c.setFont('Times-Roman',10)
#c.drawCentredString(4.135 * inch, 0.75 * inch,
# 'Page %d' % c.getPageNumber())
#draw a border
c.setFillColor(colors.ReportLabBlue)
c.rect(0.3*inch, inch, 0.5*inch, 10*inch, fill=1)
from reportlab.lib import corp
c.translate(0.8*inch, 9.6*inch)
c.rotate(90)
logo = corp.ReportLabLogo(width=1.3*inch, height=0.5*inch, powered_by=1)
c.setFillColorRGB(1,1,1)
c.setStrokeColorRGB(1,1,1)
logo.draw(c)
#c.setStrokeColorRGB(1,0,0)
#c.setLineWidth(5)
#c.line(0.8 * inch, inch, 0.8 * inch, 10.75 * inch)
#reset carefully afterwards
#canvas.setLineWidth(1)
#canvas.setStrokeColorRGB(0,0,0)\
c.restoreState()
c.endForm()
titlelist = []
closeit = 0
def framePage(canvas, title):
global closeit
titlelist.append(title)
#canvas._inPage0() # do we need this at all? would be good to eliminate it
canvas.saveState()
canvas.setFont('Times-BoldItalic',20)
canvas.drawString(inch, 10.5 * inch, title)
canvas.bookmarkHorizontalAbsolute(title, 10.8*inch)
#newsection(title)
canvas.addOutlineEntry(title+" section", title, level=0, closed=closeit)
closeit = not closeit # close every other one
canvas.setFont('Times-Roman',10)
canvas.drawCentredString(4.135 * inch, 0.75 * inch,
'Page %d' % canvas.getPageNumber())
canvas.restoreState()
canvas.doForm("frame")
def makesubsection(canvas, title, horizontal):
canvas.bookmarkHorizontalAbsolute(title, horizontal)
#newsubsection(title)
canvas.addOutlineEntry(title+" subsection", title, level=1)
# outline helpers
#outlinenametree = []
#def newsection(name):
# outlinenametree.append(name)
#def newsubsection(name):
# from types import TupleType
# thissection = outlinenametree[-1]
# if type(thissection) is not TupleType:
# subsectionlist = []
# thissection = outlinenametree[-1] = (thissection, subsectionlist)
# else:
# (sectionname, subsectionlist) = thissection
# subsectionlist.append(name)
class DocBlock:
"""A DocBlock has a chunk of commentary and a chunk of code.
It prints the code and commentary, then executes the code,
which is presumed to draw in a region reserved for it.
"""
def __init__(self):
self.comment1 = "A doc block"
self.code = "canvas.setTextOrigin(cm, cm)\ncanvas.textOut('Hello World')"
self.comment2 = "That was a doc block"
self.drawHeight = 0
def _getHeight(self):
"splits into lines"
self.comment1lines = string.split(self.comment1, '\n')
self.codelines = string.split(self.code, '\n')
self.comment2lines = string.split(self.comment2, '\n')
textheight = (len(self.comment1lines) +
len(self.code) +
len(self.comment2lines) +
18)
return max(textheight, self.drawHeight)
def draw(self, canvas, x, y):
#specifies top left corner
canvas.saveState()
height = self._getHeight()
canvas.rect(x, y-height, 6*inch, height)
#first draw the text
canvas.setTextOrigin(x + 3 * inch, y - 12)
canvas.setFont('Times-Roman',10)
canvas.textLines(self.comment1)
drawCode(canvas, self.code)
canvas.textLines(self.comment2)
#now a box for the drawing, slightly within rect
canvas.rect(x + 9, y - height + 9, 198, height - 18)
#boundary:
self.namespace = {'canvas':canvas,'cm': cm,'inch':inch}
canvas.translate(x+9, y - height + 9)
codeObj = compile(self.code, '<sample>','exec')
exec codeObj in self.namespace
canvas.restoreState()
def drawAxes(canvas, label):
"""draws a couple of little rulers showing the coords -
uses points as units so you get an imperial ruler
one inch on each side"""
#y axis
canvas.line(0,0,0,72)
for y in range(9):
tenths = (y+1) * 7.2
canvas.line(-6,tenths,0,tenths)
canvas.line(-6, 66, 0, 72) #arrow...
canvas.line(6, 66, 0, 72) #arrow...
canvas.line(0,0,72,0)
for x in range(9):
tenths = (x+1) * 7.2
canvas.line(tenths,-6,tenths, 0)
canvas.line(66, -6, 72, 0) #arrow...
canvas.line(66, +6, 72, 0) #arrow...
canvas.drawString(18, 30, label)
def drawCrossHairs(canvas, x, y):
"""just a marker for checking text metrics - blue for fun"""
canvas.saveState()
canvas.setStrokeColorRGB(0,1,0)
canvas.line(x-6,y,x+6,y)
canvas.line(x,y-6,x,y+6)
canvas.restoreState()
def drawCode(canvas, code):
"""Draws a block of text at current point, indented and in Courier"""
canvas.addLiteral('36 0 Td')
canvas.setFillColor(colors.blue)
canvas.setFont('Courier',10)
t = canvas.beginText()
t.textLines(code)
c.drawText(t)
canvas.setFillColor(colors.black)
canvas.addLiteral('-36 0 Td')
canvas.setFont('Times-Roman',10)
def makeDocument(filename, pageCallBack=None):
#the extra arg is a hack added later, so other
#tests can get hold of the canvas just before it is
#saved
c = pycanvas.Canvas(filename)
c.setPageCompression(0)
c.setPageCallBack(pageCallBack)
framePageForm(c) # define the frame form
c.showOutline()
framePage(c, 'PDFgen graphics API test script')
makesubsection(c, "PDFgen and PIDDLE", 10*inch)
t = c.beginText(inch, 10*inch)
t.setFont('Times-Roman', 10)
drawCrossHairs(c, t.getX(),t.getY())
t.textLines("""
The ReportLab library permits you to create PDF documents directly from
your Python code. The "pdfgen" subpackage is the lowest level exposed
to the user and lets you directly position test and graphics on the
page, with access to almost the full range of PDF features.
The API is intended to closely mirror the PDF / Postscript imaging
model. There is an almost one to one correspondence between commands
and PDF operators. However, where PDF provides several ways to do a job,
we have generally only picked one.
The test script attempts to use all of the methods exposed by the Canvas
class, defined in reportlab/pdfgen/canvas.py
First, let's look at text output. There are some basic commands
to draw strings:
- canvas.setFont(fontname, fontsize [, leading])
- canvas.drawString(x, y, text)
- canvas.drawRightString(x, y, text)
- canvas.drawCentredString(x, y, text)
The coordinates are in points starting at the bottom left corner of the
page. When setting a font, the leading (i.e. inter-line spacing)
defaults to 1.2 * fontsize if the fontsize is not provided.
For more sophisticated operations, you can create a Text Object, defined
in reportlab/pdfgen/testobject.py. Text objects produce tighter PDF, run
faster and have many methods for precise control of spacing and position.
Basic usage goes as follows:
- tx = canvas.beginText(x, y)
- tx.textOut('Hello') # this moves the cursor to the right
- tx.textLine('Hello again') # prints a line and moves down
- y = tx.getY() # getX, getY and getCursor track position
- canvas.drawText(tx) # all gets drawn at the end
The green crosshairs below test whether the text cursor is working
properly. They should appear at the bottom left of each relevant
substring.
""")
t.setFillColorRGB(1,0,0)
t.setTextOrigin(inch, 4*inch)
drawCrossHairs(c, t.getX(),t.getY())
t.textOut('textOut moves across:')
drawCrossHairs(c, t.getX(),t.getY())
t.textOut('textOut moves across:')
drawCrossHairs(c, t.getX(),t.getY())
t.textOut('textOut moves across:')
drawCrossHairs(c, t.getX(),t.getY())
t.textLine('')
drawCrossHairs(c, t.getX(),t.getY())
t.textLine('textLine moves down')
drawCrossHairs(c, t.getX(),t.getY())
t.textLine('textLine moves down')
drawCrossHairs(c, t.getX(),t.getY())
t.textLine('textLine moves down')
drawCrossHairs(c, t.getX(),t.getY())
t.setTextOrigin(4*inch,3.25*inch)
drawCrossHairs(c, t.getX(),t.getY())
t.textLines('This is a multi-line\nstring with embedded newlines\ndrawn with textLines().\n')
drawCrossHairs(c, t.getX(),t.getY())
t.textLines(['This is a list of strings',
'drawn with textLines().'])
c.drawText(t)
t = c.beginText(2*inch,2*inch)
t.setFont('Times-Roman',10)
drawCrossHairs(c, t.getX(),t.getY())
t.textOut('Small text.')
drawCrossHairs(c, t.getX(),t.getY())
t.setFont('Courier',14)
t.textOut('Bigger fixed width text.')
drawCrossHairs(c, t.getX(),t.getY())
t.setFont('Times-Roman',10)
t.textOut('Small text again.')
drawCrossHairs(c, t.getX(),t.getY())
c.drawText(t)
#mark the cursor where it stopped
c.showPage()
##############################################################
#
# page 2 - line styles
#
###############################################################
#page 2 - lines and styles
framePage(c, 'Line Drawing Styles')
# three line ends, lines drawn the hard way
#firt make some vertical end markers
c.setDash(4,4)
c.setLineWidth(0)
c.line(inch,9.2*inch,inch, 7.8*inch)
c.line(3*inch,9.2*inch,3*inch, 7.8*inch)
c.setDash() #clears it
c.setLineWidth(5)
c.setLineCap(0)
p = c.beginPath()
p.moveTo(inch, 9*inch)
p.lineTo(3*inch, 9*inch)
c.drawPath(p)
c.drawString(4*inch, 9*inch, 'the default - butt caps project half a width')
makesubsection(c, "caps and joins", 8.5*inch)
c.setLineCap(1)
p = c.beginPath()
p.moveTo(inch, 8.5*inch)
p.lineTo(3*inch, 8.5*inch)
c.drawPath(p)
c.drawString(4*inch, 8.5*inch, 'round caps')
c.setLineCap(2)
p = c.beginPath()
p.moveTo(inch, 8*inch)
p.lineTo(3*inch, 8*inch)
c.drawPath(p)
c.drawString(4*inch, 8*inch, 'square caps')
c.setLineCap(0)
# three line joins
c.setLineJoin(0)
p = c.beginPath()
p.moveTo(inch, 7*inch)
p.lineTo(2*inch, 7*inch)
p.lineTo(inch, 6.7*inch)
c.drawPath(p)
c.drawString(4*inch, 6.8*inch, 'Default - mitered join')
c.setLineJoin(1)
p = c.beginPath()
p.moveTo(inch, 6.5*inch)
p.lineTo(2*inch, 6.5*inch)
p.lineTo(inch, 6.2*inch)
c.drawPath(p)
c.drawString(4*inch, 6.3*inch, 'round join')
c.setLineJoin(2)
p = c.beginPath()
p.moveTo(inch, 6*inch)
p.lineTo(2*inch, 6*inch)
p.lineTo(inch, 5.7*inch)
c.drawPath(p)
c.drawString(4*inch, 5.8*inch, 'bevel join')
c.setDash(6,6)
p = c.beginPath()
p.moveTo(inch, 5*inch)
p.lineTo(3*inch, 5*inch)
c.drawPath(p)
c.drawString(4*inch, 5*inch, 'dash 6 points on, 6 off- setDash(6,6) setLineCap(0)')
makesubsection(c, "dash patterns", 5*inch)
c.setLineCap(1)
p = c.beginPath()
p.moveTo(inch, 4.5*inch)
p.lineTo(3*inch, 4.5*inch)
c.drawPath(p)
c.drawString(4*inch, 4.5*inch, 'dash 6 points on, 6 off- setDash(6,6) setLineCap(1)')
c.setLineCap(0)
c.setDash([1,2,3,4,5,6],0)
p = c.beginPath()
p.moveTo(inch, 4.0*inch)
p.lineTo(3*inch, 4.0*inch)
c.drawPath(p)
c.drawString(4*inch, 4*inch, 'dash growing - setDash([1,2,3,4,5,6],0) setLineCap(0)')
c.setLineCap(1)
c.setLineJoin(1)
c.setDash(32,12)
p = c.beginPath()
p.moveTo(inch, 3.0*inch)
p.lineTo(2.5*inch, 3.0*inch)
p.lineTo(inch, 2*inch)
c.drawPath(p)
c.drawString(4*inch, 3*inch, 'dash pattern, join and cap style interacting - ')
c.drawString(4*inch, 3*inch - 12, 'round join & miter results in sausages')
c.showPage()
##############################################################
#
# higher level shapes
#
###############################################################
framePage(c, 'Shape Drawing Routines')
t = c.beginText(inch, 10*inch)
t.textLines("""
Rather than making your own paths, you have access to a range of shape routines.
These are built in pdfgen out of lines and bezier curves, but use the most compact
set of operators possible. We can add any new ones that are of general use at no
cost to performance.""")
t.textLine()
#line demo
makesubsection(c, "lines", 10*inch)
c.line(inch, 8*inch, 3*inch, 8*inch)
t.setTextOrigin(4*inch, 8*inch)
t.textLine('canvas.line(x1, y1, x2, y2)')
#bezier demo - show control points
makesubsection(c, "bezier curves", 7.5*inch)
(x1, y1, x2, y2, x3, y3, x4, y4) = (
inch, 6.5*inch,
1.2*inch, 7.5 * inch,
3*inch, 7.5 * inch,
3.5*inch, 6.75 * inch
)
c.bezier(x1, y1, x2, y2, x3, y3, x4, y4)
c.setDash(3,3)
c.line(x1,y1,x2,y2)
c.line(x3,y3,x4,y4)
c.setDash()
t.setTextOrigin(4*inch, 7 * inch)
t.textLine('canvas.bezier(x1, y1, x2, y2, x3, y3, x4, y4)')
#rectangle
makesubsection(c, "rectangles", 7*inch)
c.rect(inch, 5.25 * inch, 2 * inch, 0.75 * inch)
t.setTextOrigin(4*inch, 5.5 * inch)
t.textLine('canvas.rect(x, y, width, height) - x,y is lower left')
#wedge
makesubsection(c, "wedges", 5*inch)
c.wedge(inch, 5*inch, 3*inch, 4*inch, 0, 315)
t.setTextOrigin(4*inch, 4.5 * inch)
t.textLine('canvas.wedge(x1, y1, x2, y2, startDeg, extentDeg)')
t.textLine('Note that this is an elliptical arc, not just circular!')
#wedge the other way
c.wedge(inch, 4*inch, 3*inch, 3*inch, 0, -45)
t.setTextOrigin(4*inch, 3.5 * inch)
t.textLine('Use a negative extent to go clockwise')
#circle
makesubsection(c, "circles", 3.5*inch)
c.circle(1.5*inch, 2*inch, 0.5 * inch)
c.circle(3*inch, 2*inch, 0.5 * inch)
t.setTextOrigin(4*inch, 2 * inch)
t.textLine('canvas.circle(x, y, radius)')
c.drawText(t)
c.showPage()
##############################################################
#
# Page 4 - fonts
#
###############################################################
framePage(c, "Font Control")
c.drawString(inch, 10*inch, 'Listing available fonts...')
y = 9.5*inch
for fontname in c.getAvailableFonts():
c.setFont(fontname,24)
c.drawString(inch, y, 'This should be %s' % fontname)
y = y - 28
makesubsection(c, "fonts and colors", 4*inch)
c.setFont('Times-Roman', 12)
t = c.beginText(inch, 4*inch)
t.textLines("""Now we'll look at the color functions and how they interact
with the text. In theory, a word is just a shape; so setFillColorRGB()
determines most of what you see. If you specify other text rendering
modes, an outline color could be defined by setStrokeColorRGB() too""")
c.drawText(t)
t = c.beginText(inch, 2.75 * inch)
t.setFont('Times-Bold',36)
t.setFillColor(colors.green) #green
t.textLine('Green fill, no stroke')
#t.setStrokeColorRGB(1,0,0) #ou can do this in a text object, or the canvas.
t.setStrokeColor(colors.red) #ou can do this in a text object, or the canvas.
t.setTextRenderMode(2) # fill and stroke
t.textLine('Green fill, red stroke - yuk!')
t.setTextRenderMode(0) # back to default - fill only
t.setFillColorRGB(0,0,0) #back to default
t.setStrokeColorRGB(0,0,0) #ditto
c.drawText(t)
c.showPage()
#########################################################################
#
# Page 5 - coord transforms
#
#########################################################################
framePage(c, "Coordinate Transforms")
c.setFont('Times-Roman', 12)
t = c.beginText(inch, 10 * inch)
t.textLines("""This shows coordinate transformations. We draw a set of axes,
moving down the page and transforming space before each one.
You can use saveState() and restoreState() to unroll transformations.
Note that functions which track the text cursor give the cursor position
in the current coordinate system; so if you set up a 6 inch high frame
2 inches down the page to draw text in, and move the origin to its top
left, you should stop writing text after six inches and not eight.""")
c.drawText(t)
drawAxes(c, "0. at origin")
c.addLiteral('%about to translate space')
c.translate(2*inch, 7 * inch)
drawAxes(c, '1. translate near top of page')
c.saveState()
c.translate(1*inch, -2 * inch)
drawAxes(c, '2. down 2 inches, across 1')
c.restoreState()
c.saveState()
c.translate(0, -3 * inch)
c.scale(2, -1)
drawAxes(c, '3. down 3 from top, scale (2, -1)')
c.restoreState()
c.saveState()
c.translate(0, -5 * inch)
c.rotate(-30)
drawAxes(c, "4. down 5, rotate 30' anticlockwise")
c.restoreState()
c.saveState()
c.translate(3 * inch, -5 * inch)
c.skew(0,30)
drawAxes(c, "5. down 5, 3 across, skew beta 30")
c.restoreState()
c.showPage()
#########################################################################
#
# Page 6 - clipping
#
#########################################################################
framePage(c, "Clipping")
c.setFont('Times-Roman', 12)
t = c.beginText(inch, 10 * inch)
t.textLines("""This shows clipping at work. We draw a chequerboard of rectangles
into a path object, and clip it. This then forms a mask which limits the region of
the page on which one can draw. This paragraph was drawn after setting the clipping
path, and so you should only see part of the text.""")
c.drawText(t)
c.saveState()
#c.setFillColorRGB(0,0,1)
p = c.beginPath()
#make a chesboard effect, 1 cm squares
for i in range(14):
x0 = (3 + i) * cm
for j in range(7):
y0 = (16 + j) * cm
p.rect(x0, y0, 0.85*cm, 0.85*cm)
c.addLiteral('%Begin clip path')
c.clipPath(p)
c.addLiteral('%End clip path')
t = c.beginText(3 * cm, 22.5 * cm)
t.textLines("""This shows clipping at work. We draw a chequerboard of rectangles
into a path object, and clip it. This then forms a mask which limits the region of
the page on which one can draw. This paragraph was drawn after setting the clipping
path, and so you should only see part of the text.
This shows clipping at work. We draw a chequerboard of rectangles
into a path object, and clip it. This then forms a mask which limits the region of
the page on which one can draw. This paragraph was drawn after setting the clipping
path, and so you should only see part of the text.
This shows clipping at work. We draw a chequerboard of rectangles
into a path object, and clip it. This then forms a mask which limits the region of
the page on which one can draw. This paragraph was drawn after setting the clipping
path, and so you should only see part of the text.""")
c.drawText(t)
c.restoreState()
t = c.beginText(inch, 5 * inch)
t.textLines("""You can also use text as an outline for clipping with the text render mode.
The API is not particularly clean on this and one has to follow the right sequence;
this can be optimized shortly.""")
c.drawText(t)
#first the outline
c.saveState()
t = c.beginText(inch, 3.0 * inch)
t.setFont('Helvetica-BoldOblique',108)
t.setTextRenderMode(5) #stroke and add to path
t.textLine('Python!')
t.setTextRenderMode(0)
c.drawText(t) #this will make a clipping mask
#now some small stuff which wil be drawn into the current clip mask
t = c.beginText(inch, 4 * inch)
t.setFont('Times-Roman',6)
t.textLines((('spam ' * 40) + '\n') * 15)
c.drawText(t)
#now reset canvas to get rid of the clipping mask
c.restoreState()
c.showPage()
#########################################################################
#
# Page 7 - images
#
#########################################################################
framePage(c, "Images")
c.setFont('Times-Roman', 12)
t = c.beginText(inch, 10 * inch)
if not haveImages:
c.drawString(inch, 11*inch,
"Python Imaging Library not found! Below you see rectangles instead of images.")
t.textLines("""PDFgen uses the Python Imaging Library to process a very wide variety of image formats.
This page shows image capabilities. If I've done things right, the bitmap should have
its bottom left corner aligned with the crosshairs.
There are two methods for drawing images. The recommended use is to call drawImage.
This produces the smallest PDFs and the fastest generation times as each image's binary data is
only embedded once in the file. Also you can use advanced features like transparency masks.
You can also use drawInlineImage, which puts images in the page stream directly.
This is slightly faster for Acrobat to render or for very small images, but wastes
space if you use images more than once.""")
c.drawText(t)
gif = os.path.join(_RL_DIR,'test','pythonpowered.gif')
if haveImages and rl_isfile(gif):
c.drawInlineImage(gif,2*inch, 7*inch)
else:
c.rect(2*inch, 7*inch, 110, 44)
c.line(1.5*inch, 7*inch, 4*inch, 7*inch)
c.line(2*inch, 6.5*inch, 2*inch, 8*inch)
c.drawString(4.5 * inch, 7.25*inch, 'inline image drawn at natural size')
if haveImages and rl_isfile(gif):
c.drawInlineImage(gif,2*inch, 5*inch, inch, inch)
else:
c.rect(2*inch, 5*inch, inch, inch)
c.line(1.5*inch, 5*inch, 4*inch, 5*inch)
c.line(2*inch, 4.5*inch, 2*inch, 6*inch)
c.drawString(4.5 * inch, 5.25*inch, 'inline image distorted to fit box')
c.drawString(1.5 * inch, 4*inch, 'Image XObjects can be defined once in the file and drawn many times.')
c.drawString(1.5 * inch, 3.75*inch, 'This results in faster generation and much smaller files.')
for i in range(5):
if haveImages:
(w, h) = c.drawImage(gif, (1.5 + i)*inch, 3*inch)
else:
c.rect((1.5 + i)*inch, 3*inch, 110, 44)
myMask = [254,255,222,223,0,1]
c.drawString(1.5 * inch, 2.5*inch, "The optional 'mask' parameter lets you define transparent colors. We used a color picker")
c.drawString(1.5 * inch, 2.3*inch, "to determine that the yellow in the image above is RGB=(225,223,0). We then define a mask")
c.drawString(1.5 * inch, 2.1*inch, "spanning these RGB values: %s. The background vanishes!!" % myMask)
c.drawString(2.5*inch, 1.2*inch, 'This would normally be obscured')
if haveImages:
c.drawImage(gif, 3*inch, 1.2*inch, w, h, mask=myMask)
else:
c.rect(3*inch, 1.2*inch, 110, 44)
c.showPage()
#########################################################################
#
# Page 8 - Forms and simple links
#
#########################################################################
framePage(c, "Forms and Links")
c.setFont('Times-Roman', 12)
t = c.beginText(inch, 10 * inch)
t.textLines("""Forms are sequences of text or graphics operations
which are stored only once in a PDF file and used as many times
as desired. The blue logo bar to the left is an example of a form
in this document. See the function framePageForm in this demo script
for an example of how to use canvas.beginForm(name, ...) ... canvas.endForm().
Documents can also contain cross references where (for example) a rectangle
on a page may be bound to a position on another page. If the user clicks
on the rectangle the PDF viewer moves to the bound position on the other
page. There are many other types of annotations and links supported by PDF.
For example, there is a bookmark to each page in this document and below
is a browsable index that jumps to those pages. In addition we show two
URL hyperlinks; for these, you specify a rectangle but must draw the contents
or any surrounding rectangle yourself.
""")
c.drawText(t)
nentries = len(titlelist)
xmargin = 3*inch
xmax = 7*inch
ystart = 6.54*inch
ydelta = 0.4*inch
for i in range(nentries):
yposition = ystart - i*ydelta
title = titlelist[i]
c.drawString(xmargin, yposition, title)
c.linkAbsolute(title, title, (xmargin-ydelta/4, yposition-ydelta/4, xmax, yposition+ydelta/2))
# test URLs
r1 = (inch, 3*inch, 5*inch, 3.25*inch) # this is x1,y1,x2,y2
c.linkURL('http://www.reportlab.com/', r1, thickness=1, color=colors.green)
c.drawString(inch+3, 3*inch+6, 'Hyperlink to www.reportlab.com, with green border')
r1 = (inch, 2.5*inch, 5*inch, 2.75*inch) # this is x1,y1,x2,y2
c.linkURL('mailto:reportlab-users@egroups.com', r1) #, border=0)
c.drawString(inch+3, 2.5*inch+6, 'mailto: hyperlink, without border')
r1 = (inch, 2*inch, 5*inch, 2.25*inch) # this is x1,y1,x2,y2
c.linkURL('http://www.reportlab.com/', r1,
thickness=2,
dashArray=[2,4],
color=colors.magenta)
c.drawString(inch+3, 2*inch+6, 'Hyperlink with custom border style')
### now do stuff for the outline
#for x in outlinenametree: print x
#stop
#apply(c.setOutlineNames0, tuple(outlinenametree))
return c
def run(filename):
c = makeDocument(filename)
c.save()
source = str(c)
open(outputfile("test_pdfgen_pycanvas_out.txt"),"w").write(source)
import reportlab.rl_config
if reportlab.rl_config.verbose:
print source
def pageShapes(c):
"""Demonstrates the basic lines and shapes"""
c.showPage()
framePage(c, "Basic line and shape routines""")
c.setTextOrigin(inch, 10 * inch)
c.setFont('Times-Roman', 12)
c.textLines("""pdfgen provides some basic routines for drawing straight and curved lines,
and also for solid shapes.""")
y = 9 * inch
d = DocBlock()
d.comment1 = 'Lesson one'
d.code = "canvas.textOut('hello, world')"
print d.code
d.comment2 = 'Lesson two'
d.draw(c, inch, 9 * inch)
class PdfgenTestCase(unittest.TestCase):
"Make documents with lots of Pdfgen features"
def test0(self):
"Make a PDFgen document with most graphics features"
run(outputfile('test_pdfgen_pycanvas.pdf'))
def makeSuite():
return makeSuiteForClasses(PdfgenTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,114 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_platypus_breaking.py
"""Tests pageBreakBefore, frameBreakBefore, keepWithNext...
"""
import sys, os, time
from string import split, strip, join, whitespace
from operator import truth
from types import StringType, ListType
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.platypus.flowables import Flowable
from reportlab.lib import colors
from reportlab.lib.units import cm
from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus.paragraph import Paragraph
from reportlab.platypus.frames import Frame
from reportlab.lib.randomtext import randomText, PYTHON
from reportlab.platypus.doctemplate import PageTemplate, BaseDocTemplate
from reportlab.platypus.paragraph import *
def myMainPageFrame(canvas, doc):
"The page frame used for all PDF documents."
canvas.saveState()
canvas.setFont('Times-Roman', 12)
pageNumber = canvas.getPageNumber()
canvas.drawString(10*cm, cm, str(pageNumber))
canvas.restoreState()
class MyDocTemplate(BaseDocTemplate):
_invalidInitArgs = ('pageTemplates',)
def __init__(self, filename, **kw):
frame1 = Frame(2.5*cm, 15.5*cm, 6*cm, 10*cm, id='F1')
frame2 = Frame(11.5*cm, 15.5*cm, 6*cm, 10*cm, id='F2')
frame3 = Frame(2.5*cm, 2.5*cm, 6*cm, 10*cm, id='F3')
frame4 = Frame(11.5*cm, 2.5*cm, 6*cm, 10*cm, id='F4')
self.allowSplitting = 0
self.showBoundary = 1
apply(BaseDocTemplate.__init__, (self, filename), kw)
template = PageTemplate('normal', [frame1, frame2, frame3, frame4], myMainPageFrame)
self.addPageTemplates(template)
def _test0(self):
"This makes one long multi-page paragraph."
# Build story.
story = []
styleSheet = getSampleStyleSheet()
h1 = styleSheet['Heading1']
h1.pageBreakBefore = 1
h1.keepWithNext = 1
h2 = styleSheet['Heading2']
h2.frameBreakBefore = 1
h2.keepWithNext = 1
h3 = styleSheet['Heading3']
h3.backColor = colors.cyan
h3.keepWithNext = 1
bt = styleSheet['BodyText']
story.append(Paragraph("""
Subsequent pages test pageBreakBefore, frameBreakBefore and
keepTogether attributes. Generated at %s. The number in brackets
at the end of each paragraph is its position in the story. (%d)""" % (
time.ctime(time.time()), len(story)), bt))
for i in range(10):
story.append(Paragraph('Heading 1 always starts a new page (%d)' % len(story), h1))
for j in range(3):
story.append(Paragraph('Heading1 paragraphs should always'
'have a page break before. Heading 2 on the other hand'
'should always have a FRAME break before (%d)' % len(story), bt))
story.append(Paragraph('Heading 2 always starts a new frame (%d)' % len(story), h2))
story.append(Paragraph('Heading1 paragraphs should always'
'have a page break before. Heading 2 on the other hand'
'should always have a FRAME break before (%d)' % len(story), bt))
for j in range(3):
story.append(Paragraph(randomText(theme=PYTHON, sentences=2)+' (%d)' % len(story), bt))
story.append(Paragraph('I should never be at the bottom of a frame (%d)' % len(story), h3))
story.append(Paragraph(randomText(theme=PYTHON, sentences=1)+' (%d)' % len(story), bt))
doc = MyDocTemplate(outputfile('test_platypus_breaking.pdf'))
doc.multiBuild(story)
class BreakingTestCase(unittest.TestCase):
"Test multi-page splitting of paragraphs (eyeball-test)."
def test0(self):
_test0(self)
def makeSuite():
return makeSuiteForClasses(BreakingTestCase)
#noruntests
if __name__ == "__main__": #NORUNTESTS
if 'debug' in sys.argv:
_test0(None)
else:
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,581 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_platypus_general.py
__version__=''' $Id: test_platypus_general.py 2619 2005-06-24 14:49:15Z rgbecker $ '''
#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.
"""
import string, copy, sys, os
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
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
if haveImages:
_GIF = os.path.join(_RL_DIR,'test','pythonpowered.gif')
if not rl_isfile(_GIF): _GIF = None
else:
_GIF = None
_JPG = os.path.join(_RL_DIR,'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 = string.split(textBlock, '\n')
paras = []
currentPara = []
for line in lines:
if len(string.strip(line)) == 0:
#blank, add it
if currentPara <> []:
paras.append(string.join(currentPara, '\n'))
currentPara = []
else:
currentPara.append(line)
#...and the last one
if currentPara <> []:
paras.append(string.join(currentPara, '\n'))
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 &amp; 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 contrived 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(os.path.dirname(reportlab.__file__),'fonts')
face = pdfmetrics.EmbeddedType1Face(os.path.join(fontDir,'LeERC___.AFM'),
os.path.join(fontDir,'LeERC___.PFB'))
faceName = face.name # should be 'LettErrorRobot-Chrome'
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="LettErrorRobot-Chrome">LettErrorRobot-Chrome</font>.
Now for the real challenge...""", styleSheet['Normal']))
styRobot = ParagraphStyle('Robot', styleSheet['Normal'])
styRobot.fontSize = 16
styRobot.leading = 20
styRobot.fontName = 'LettErrorRobot-Chrome'
story.append(Paragraph(
"This whole paragraph is 16-point Letterror-Robot-Chrome.",
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'))'''%_GIF)
code(''' story.append(platypus.Image('%s'.encode('utf8')))''' % _GIF)
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(_GIF))
code(''' story.append(platypus.Image(open_for_read('%s','b')))''' % _GIF)
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(os.path.dirname(reportlab.__file__),'fonts')
face = pdfmetrics.EmbeddedType1Face(os.path.join(fontDir,'LeERC___.AFM'),os.path.join(fontDir,'LeERC___.PFB'))
faceName = face.name # should be 'LettErrorRobot-Chrome'
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="LettErrorRobot-Chrome">LettErrorRobot-Chrome</font>.
Now for the real challenge...""", styleSheet['Normal']))
styRobot = ParagraphStyle('Robot', styleSheet['Normal'])
styRobot.fontSize = 16
styRobot.leading = 20
styRobot.fontName = 'LettErrorRobot-Chrome'
story.append(Paragraph(
"This whole paragraph is 16-point Letterror-Robot-Chrome.",
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(_GIF.encode('utf8')))
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
apply(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 filter(lambda x: not isinstance(x,Flowable), flowables1)==[], "flowables1 argument error"
assert filter(lambda x: not isinstance(x,Flowable), flowables2)==[], "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'))
#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 makeSuite():
return makeSuiteForClasses(PlatypusTestCase)
#noruntests
if __name__ == "__main__":
if '-debug' in sys.argv:
run()
else:
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,140 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_platypus_indents.py
"""Tests for context-dependent indentation
"""
import sys, os, random
from string import split, strip, join, whitespace
from operator import truth
from types import StringType, ListType
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.pdfbase.pdfmetrics import stringWidth
from reportlab.platypus.paraparser import ParaParser
from reportlab.platypus.flowables import Flowable
from reportlab.lib.colors import Color
from reportlab.lib.units import cm
from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY
from reportlab.lib.utils import _className
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus.paragraph import Paragraph
from reportlab.platypus.frames import Frame
from reportlab.platypus.doctemplate \
import PageTemplate, BaseDocTemplate, Indenter, FrameBreak, NextPageTemplate
from reportlab.platypus import tableofcontents
from reportlab.platypus.tableofcontents import TableOfContents
from reportlab.platypus.tables import TableStyle, Table
from reportlab.platypus.paragraph import *
from reportlab.platypus.paragraph import _getFragWords
from reportlab.platypus.flowables import Spacer
def myMainPageFrame(canvas, doc):
"The page frame used for all PDF documents."
canvas.saveState()
canvas.rect(2.5*cm, 2.5*cm, 15*cm, 25*cm)
canvas.setFont('Times-Roman', 12)
pageNumber = canvas.getPageNumber()
canvas.drawString(10*cm, cm, str(pageNumber))
canvas.restoreState()
class MyDocTemplate(BaseDocTemplate):
_invalidInitArgs = ('pageTemplates',)
def __init__(self, filename, **kw):
frame1 = Frame(2.5*cm, 2.5*cm, 15*cm, 25*cm, id='F1')
self.allowSplitting = 0
apply(BaseDocTemplate.__init__, (self, filename), kw)
template1 = PageTemplate('normal', [frame1], myMainPageFrame)
frame2 = Frame(2.5*cm, 16*cm, 15*cm, 10*cm, id='F2', showBoundary=1)
frame3 = Frame(2.5*cm, 2.5*cm, 15*cm, 10*cm, id='F3', showBoundary=1)
template2 = PageTemplate('updown', [frame2, frame3])
self.addPageTemplates([template1, template2])
class IndentTestCase(unittest.TestCase):
"Test multi-page splitting of paragraphs (eyeball-test)."
def test0(self):
"This makes one long multi-page paragraph."
# Build story.
story = []
styleSheet = getSampleStyleSheet()
h1 = styleSheet['Heading1']
h1.spaceBefore = 18
bt = styleSheet['BodyText']
bt.spaceBefore = 6
story.append(Paragraph('Test of context-relative indentation',h1))
story.append(Spacer(18,18))
story.append(Indenter(0,0))
story.append(Paragraph("This should be indented 0 points at each edge. " + ("spam " * 25),bt))
story.append(Indenter(0,0))
story.append(Indenter(36,0))
story.append(Paragraph("This should be indented 36 points at the left. " + ("spam " * 25),bt))
story.append(Indenter(-36,0))
story.append(Indenter(0,36))
story.append(Paragraph("This should be indented 36 points at the right. " + ("spam " * 25),bt))
story.append(Indenter(0,-36))
story.append(Indenter(36,36))
story.append(Paragraph("This should be indented 36 points at each edge. " + ("spam " * 25),bt))
story.append(Indenter(36,36))
story.append(Paragraph("This should be indented a FURTHER 36 points at each edge. " + ("spam " * 25),bt))
story.append(Indenter(-72,-72))
story.append(Paragraph("This should be back to normal at each edge. " + ("spam " * 25),bt))
story.append(Indenter(36,36))
story.append(Paragraph(("""This should be indented 36 points at the left
and right. It should run over more than one page and the indent should
continue on the next page. """ + (random.randint(0,10) * 'x') + ' ') * 20 ,bt))
story.append(Indenter(-36,-36))
story.append(NextPageTemplate('updown'))
story.append(FrameBreak())
story.append(Paragraph('Another test of context-relative indentation',h1))
story.append(NextPageTemplate('normal')) # so NEXT page is different template...
story.append(Paragraph("""This time we see if the indent level is continued across
frames...this page has 2 frames, let's see if it carries top to bottom. Then
onto a totally different template.""",bt))
story.append(Indenter(0,0))
story.append(Paragraph("This should be indented 0 points at each edge. " + ("spam " * 25),bt))
story.append(Indenter(0,0))
story.append(Indenter(36,72))
story.append(Paragraph(("""This should be indented 36 points at the left
and 72 at the right. It should run over more than one frame and one page, and the indent should
continue on the next page. """ + (random.randint(0,10) * 'x') + ' ') * 35 ,bt))
story.append(Indenter(-36,-72))
story.append(Paragraph("This should be back to normal at each edge. " + ("spam " * 25),bt))
doc = MyDocTemplate(outputfile('test_platypus_indents.pdf'))
doc.multiBuild(story)
#noruntests
def makeSuite():
return makeSuiteForClasses(IndentTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,158 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_platypus_breaking.py
"""Tests ability to cycle through multiple page templates
"""
import sys, os, time
from string import split, strip, join, whitespace
from operator import truth
from types import StringType, ListType
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.platypus.flowables import Flowable
from reportlab.lib import colors
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm
from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus.paragraph import Paragraph
from reportlab.platypus.frames import Frame
from reportlab.lib.randomtext import randomText, PYTHON
from reportlab.platypus.doctemplate import PageTemplate, BaseDocTemplate, NextPageTemplate
from reportlab.platypus.paragraph import *
def myMainPageFrame(canvas, doc):
"The page frame used for all PDF documents."
canvas.saveState()
canvas.setFont('Times-Roman', 12)
pageNumber = canvas.getPageNumber()
canvas.drawString(10*cm, cm, str(pageNumber))
canvas.restoreState()
class LeftPageTemplate(PageTemplate):
def __init__(self):
#allow a bigger margin on the right for the staples
frame = Frame(1.5*cm, 2.5*cm, 16*cm, 25*cm, id='F1')
PageTemplate.__init__(self,
id='left',
frames=[frame],
pagesize=A4)
def beforeDrawPage(self, canv, doc):
"Decorate the page with an asymetric design"
canv.setFillColor(colors.cyan)
canv.rect(0.5*cm, 2.5*cm, 1*cm, 25*cm, stroke=1, fill=1)
canv.circle(19*cm, 10*cm, 0.5*cm, stroke=1, fill=1)
canv.circle(19*cm, 20*cm, 0.5*cm, stroke=1, fill=1)
canv.setFillColor(colors.black)
class RightPageTemplate(PageTemplate):
def __init__(self):
#allow a bigger margin on the right for the staples
frame = Frame(3.5*cm, 2.5*cm, 16*cm, 25*cm, id='F1')
PageTemplate.__init__(self,
id='right',
frames=[frame],
pagesize=A4)
def beforeDrawPage(self, canv, doc):
"Decorate the page with an asymetric design"
canv.setFillColor(colors.cyan)
canv.rect(19.5*cm, 2.5*cm, 1*cm, 25*cm, stroke=1, fill=1)
canv.circle(2*cm, 10*cm, 0.5*cm, stroke=1, fill=1)
canv.circle(2*cm, 20*cm, 0.5*cm, stroke=1, fill=1)
canv.setFillColor(colors.black)
class MyDocTemplate(BaseDocTemplate):
_invalidInitArgs = ('pageTemplates',)
def __init__(self, filename, **kw):
apply(BaseDocTemplate.__init__, (self, filename), kw)
self.addPageTemplates(
[
PageTemplate(id='plain',
frames=[Frame(2.5*cm, 2.5*cm, 16*cm, 25*cm, id='F1')]
),
LeftPageTemplate(),
RightPageTemplate()
]
)
class LeftRightTestCase(unittest.TestCase):
"Test multi-page splitting of paragraphs (eyeball-test)."
def testIt(self):
"This makes one long multi-page paragraph."
# Build story.
story = []
styleSheet = getSampleStyleSheet()
h1 = styleSheet['Heading1']
h1.pageBreakBefore = 1
h1.keepWithNext = 1
h2 = styleSheet['Heading2']
h2.frameBreakBefore = 1
h2.keepWithNext = 1
h3 = styleSheet['Heading3']
h3.backColor = colors.cyan
h3.keepWithNext = 1
bt = styleSheet['BodyText']
story.append(Paragraph("""
This tests ability to alternate left and right templates. We start on
a plain one. The next page should display a left-side template,
with a big inner margin and staple-like holes on the right.""",style=bt))
story.append(NextPageTemplate(['left','right']))
story.append(Paragraph("""
One can specify a list of templates instead of a single one in
order to sequence through them.""",style=bt))
for i in range(10):
story.append(Paragraph('Heading 1 always starts a new page (%d)' % len(story), h1))
for j in range(3):
story.append(Paragraph('Heading1 paragraphs should always'
'have a page break before. Heading 2 on the other hand'
'should always have a FRAME break before (%d)' % len(story), bt))
story.append(Paragraph('Heading 2 always starts a new frame (%d)' % len(story), h2))
story.append(Paragraph('Heading1 paragraphs should always'
'have a page break before. Heading 2 on the other hand'
'should always have a FRAME break before (%d)' % len(story), bt))
for j in range(3):
story.append(Paragraph(randomText(theme=PYTHON, sentences=2)+' (%d)' % len(story), bt))
story.append(Paragraph('I should never be at the bottom of a frame (%d)' % len(story), h3))
story.append(Paragraph(randomText(theme=PYTHON, sentences=1)+' (%d)' % len(story), bt))
story.append(NextPageTemplate('plain'))
story.append(Paragraph('Back to plain old page template',h1))
story.append(Paragraph('Back to plain old formatting', bt))
#doc = MyDocTemplate(outputfile('test_platypus_leftright.pdf'))
doc = MyDocTemplate(outputfile('test_platypus_leftright.pdf'))
doc.multiBuild(story)
def makeSuite():
return makeSuiteForClasses(LeftRightTestCase)
#noruntests
if __name__ == "__main__": #NORUNTESTS
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,184 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_platypus_paragraphs.py
"""Tests for the reportlab.platypus.paragraphs module.
"""
import sys, os
from string import split, strip, join, whitespace
from operator import truth
from types import StringType, ListType
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.pdfbase.pdfmetrics import stringWidth
from reportlab.platypus.paraparser import ParaParser
from reportlab.platypus.flowables import Flowable
from reportlab.lib.colors import Color
from reportlab.lib.units import cm
from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY
from reportlab.lib.utils import _className
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus.paragraph import Paragraph
from reportlab.platypus.frames import Frame
from reportlab.platypus.doctemplate \
import PageTemplate, BaseDocTemplate
from reportlab.platypus import tableofcontents
from reportlab.platypus.tableofcontents import TableOfContents
from reportlab.platypus.tables import TableStyle, Table
from reportlab.platypus.paragraph import *
from reportlab.platypus.paragraph import _getFragWords
def myMainPageFrame(canvas, doc):
"The page frame used for all PDF documents."
canvas.saveState()
canvas.rect(2.5*cm, 2.5*cm, 15*cm, 25*cm)
canvas.setFont('Times-Roman', 12)
pageNumber = canvas.getPageNumber()
canvas.drawString(10*cm, cm, str(pageNumber))
canvas.restoreState()
class MyDocTemplate(BaseDocTemplate):
_invalidInitArgs = ('pageTemplates',)
def __init__(self, filename, **kw):
frame1 = Frame(2.5*cm, 2.5*cm, 15*cm, 25*cm, id='F1')
self.allowSplitting = 0
apply(BaseDocTemplate.__init__, (self, filename), kw)
template = PageTemplate('normal', [frame1], myMainPageFrame)
self.addPageTemplates(template)
class ParagraphCorners(unittest.TestCase):
"some corner cases which should parse"
def check(text,bt = getSampleStyleSheet()['BodyText']):
try:
P = Paragraph(text,st)
except:
raise AssertionError("'%s' should parse"%text)
def test0(self):
self.check('<para />')
self.check('<para/>')
self.check('\t\t\t\n\n\n<para />')
self.check('\t\t\t\n\n\n<para/>')
self.check('<para\t\t\t\t/>')
self.check('<para></para>')
self.check('<para> </para>')
self.check('\t\t\n\t\t\t <para> </para>')
class ParagraphSplitTestCase(unittest.TestCase):
"Test multi-page splitting of paragraphs (eyeball-test)."
def test0(self):
"This makes one long multi-page paragraph."
# Build story.
story = []
styleSheet = getSampleStyleSheet()
bt = styleSheet['BodyText']
text = '''If you imagine that the box of X's tothe left is
an image, what I want to be able to do is flow a
series of paragraphs around the image
so that once the bottom of the image is reached, then text will flow back to the
left margin. I know that it would be possible to something like this
using tables, but I can't see how to have a generic solution.
There are two examples of this in the demonstration section of the reportlab
site.
If you look at the "minimal" euro python conference brochure, at the end of the
timetable section (page 8), there are adverts for "AdSu" and "O'Reilly". I can
see how the AdSu one might be done generically, but the O'Reilly, unsure...
I guess I'm hoping that I've missed something, and that
it's actually easy to do using platypus.
'''
from reportlab.platypus.flowables import ParagraphAndImage, Image
from reportlab.lib.utils import _RL_DIR
gif = os.path.join(_RL_DIR,'test','pythonpowered.gif')
story.append(ParagraphAndImage(Paragraph(text,bt),Image(gif)))
phrase = 'This should be a paragraph spanning at least three pages. '
description = ''.join([('%d: '%i)+phrase for i in xrange(250)])
story.append(ParagraphAndImage(Paragraph(description, bt),Image(gif),side='left'))
doc = MyDocTemplate(outputfile('test_platypus_paragraphandimage.pdf'))
doc.multiBuild(story)
def test1(self):
"This makes one long multi-page paragraph."
# Build story.
story = []
styleSheet = getSampleStyleSheet()
h3 = styleSheet['Heading3']
bt = styleSheet['BodyText']
text = '''If you imagine that the box of X's tothe left is
an image, what I want to be able to do is flow a
series of paragraphs around the image
so that once the bottom of the image is reached, then text will flow back to the
left margin. I know that it would be possible to something like this
using tables, but I can't see how to have a generic solution.
There are two examples of this in the demonstration section of the reportlab
site.
If you look at the "minimal" euro python conference brochure, at the end of the
timetable section (page 8), there are adverts for "AdSu" and "O'Reilly". I can
see how the AdSu one might be done generically, but the O'Reilly, unsure...
I guess I'm hoping that I've missed something, and that
it's actually easy to do using platypus.We can do greek letters <greek>mDngG</greek>. This should be a
u with a dieresis on top &lt;unichar code=0xfc/&gt;="<unichar code=0xfc/>" and this &amp;#xfc;="&#xfc;" and this \\xc3\\xbc="\xc3\xbc". On the other hand this
should be a pound sign &amp;pound;="&pound;" and this an alpha &amp;alpha;="&alpha;". You can have links in the page <link href=http://www.reportlab.com color=blue>ReportLab</link>.
Use scheme "pdf:" to indicate an external PDF link, "http:", "https:" to indicate an external link eg something to open in
your browser. If an internal link begins with something that looks like a scheme, precede with "document:".
'''
from reportlab.platypus.flowables import ImageAndFlowables, Image
from reportlab.lib.utils import _RL_DIR
gif = os.path.join(_RL_DIR,'test','pythonpowered.gif')
heading = Paragraph('This is a heading',h3)
story.append(ImageAndFlowables(Image(gif),[heading,Paragraph(text,bt)]))
phrase = 'This should be a paragraph spanning at least three pages. '
description = ''.join([('%d: '%i)+phrase for i in xrange(250)])
story.append(ImageAndFlowables(Image(gif),[heading,Paragraph(description, bt)],imageSide='left'))
doc = MyDocTemplate(outputfile('test_platypus_imageandflowables.pdf'))
doc.multiBuild(story)
class FragmentTestCase(unittest.TestCase):
"Test fragmentation of paragraphs."
def test0(self):
"Test empty paragraph."
styleSheet = getSampleStyleSheet()
B = styleSheet['BodyText']
text = ''
P = Paragraph(text, B)
frags = map(lambda f:f.text, P.frags)
assert frags == []
def test1(self):
"Test simple paragraph."
styleSheet = getSampleStyleSheet()
B = styleSheet['BodyText']
text = "X<font name=Courier>Y</font>Z"
P = Paragraph(text, B)
frags = map(lambda f:f.text, P.frags)
assert frags == ['X', 'Y', 'Z']
#noruntests
def makeSuite():
return makeSuiteForClasses(FragmentTestCase, ParagraphSplitTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,91 @@
#!/bin/env python
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history TBC
#$Header$
__version__=''' $Id'''
__doc__="""Tests of intra-paragraph parsing behaviour in Platypus."""
from types import TupleType, ListType, StringType, UnicodeType
from pprint import pprint as pp
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile
from reportlab.platypus import cleanBlockQuotedText
from reportlab.platypus.paraparser import ParaParser, ParaFrag
from reportlab.lib.colors import black
class ParaParserTestCase(unittest.TestCase):
"""Tests of data structures created by paragraph parser. Esp. ability
to accept unicode and preserve it"""
def setUp(self):
style=ParaFrag()
style.fontName='Times-Roman'
style.fontSize = 12
style.textColor = black
style.bulletFontName = black
style.bulletFontName='Times-Roman'
style.bulletFontSize=12
self.style = style
def testPlain(self):
txt = "Hello World"
stuff = ParaParser().parse(txt, self.style)
assert type(stuff) is TupleType
assert len(stuff) == 3
assert stuff[1][0].text == 'Hello World'
def testBold(self):
txt = "Hello <b>Bold</b> World"
fragList = ParaParser().parse(txt, self.style)[1]
self.assertEquals(map(lambda x:x.text, fragList), ['Hello ','Bold',' World'])
self.assertEquals(fragList[1].fontName, 'Times-Bold')
def testEntity(self):
"Numeric entities should be unescaped by parser"
txt = "Hello &#169; copyright"
fragList = ParaParser().parse(txt, self.style)[1]
self.assertEquals(map(lambda x:x.text, fragList), ['Hello ','\xc2\xa9',' copyright'])
def testEscaped(self):
"Escaped high-bit stuff should go straight through"
txt = "Hello \xc2\xa9 copyright"
fragList = ParaParser().parse(txt, self.style)[1]
assert fragList[0].text == txt
def testPlainUnicode(self):
"See if simple unicode goes through"
txt = u"Hello World"
stuff = ParaParser().parse(txt, self.style)
assert type(stuff) is TupleType
assert len(stuff) == 3
assert stuff[1][0].text == u'Hello World'
def testBoldUnicode(self):
txt = u"Hello <b>Bold</b> World"
fragList = ParaParser().parse(txt, self.style)[1]
self.assertEquals(map(lambda x:x.text, fragList), [u'Hello ',u'Bold',u' World'])
self.assertEquals(fragList[1].fontName, 'Times-Bold')
def testEntityUnicode(self):
"Numeric entities should be unescaped by parser"
txt = u"Hello &#169; copyright"
fragList = ParaParser().parse(txt, self.style)[1]
self.assertEquals(map(lambda x:x.text, fragList), [u'Hello ',u'\xc2\xa9',u' copyright'])
def testEscapedUnicode(self):
"Escaped high-bit stuff should go straight through"
txt = u"Hello \xa9 copyright"
fragList = ParaParser().parse(txt, self.style)[1]
assert fragList[0].text == txt
def makeSuite():
return makeSuiteForClasses(ParaParserTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())

View File

@ -0,0 +1,195 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_platypus_breaking.py
"""Tests pageBreakBefore, frameBreakBefore, keepWithNext...
"""
import sys
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.platypus.flowables import Flowable, PTOContainer, KeepInFrame
from reportlab.lib.units import cm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.colors import toColor, black
from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY
from reportlab.platypus.paragraph import Paragraph
from reportlab.platypus.tables import Table
from reportlab.platypus.frames import Frame
from reportlab.lib.randomtext import randomText
from reportlab.platypus.doctemplate import PageTemplate, BaseDocTemplate, FrameBreak
def myMainPageFrame(canvas, doc):
"The page frame used for all PDF documents."
canvas.saveState()
canvas.setFont('Times-Roman', 12)
pageNumber = canvas.getPageNumber()
canvas.drawString(10*cm, cm, str(pageNumber))
canvas.restoreState()
def _showDoc(fn,story):
pageTemplate = PageTemplate('normal', [Frame(72, 440, 170, 284, id='F1'),
Frame(326, 440, 170, 284, id='F2'),
Frame(72, 72, 170, 284, id='F3'),
Frame(326, 72, 170, 284, id='F4'),
], myMainPageFrame)
doc = BaseDocTemplate(outputfile(fn),
pageTemplates = pageTemplate,
showBoundary = 1,
)
doc.multiBuild(story)
text2 ='''We have already seen that the natural general principle that will
subsume this case cannot be arbitrary in the requirement that branching
is not tolerated within the dominance scope of a complex symbol.
Notice, incidentally, that the speaker-hearer's linguistic intuition is
to be regarded as the strong generative capacity of the theory. A
consequence of the approach just outlined is that the descriptive power
of the base component does not affect the structure of the levels of
acceptability from fairly high (e.g. (99a)) to virtual gibberish (e.g.
(98d)). By combining adjunctions and certain deformations, a
descriptively adequate grammar cannot be arbitrary in the strong
generative capacity of the theory.'''
text1='''
On our assumptions, a descriptively adequate grammar delimits the strong
generative capacity of the theory. For one thing, the fundamental error
of regarding functional notions as categorial is to be regarded as a
corpus of utterance tokens upon which conformity has been defined by the
paired utterance test. A majority of informed linguistic specialists
agree that the appearance of parasitic gaps in domains relatively
inaccessible to ordinary extraction is necessary to impose an
interpretation on the requirement that branching is not tolerated within
the dominance scope of a complex symbol. It may be, then, that the
speaker-hearer's linguistic intuition appears to correlate rather
closely with the ultimate standard that determines the accuracy of any
proposed grammar. Analogously, the notion of level of grammaticalness
may remedy and, at the same time, eliminate a general convention
regarding the forms of the grammar.'''
text0 = '''To characterize a linguistic level L,
this selectionally introduced contextual
feature delimits the requirement that
branching is not tolerated within the
dominance scope of a complex
symbol. Notice, incidentally, that the
notion of level of grammaticalness
does not affect the structure of the
levels of acceptability from fairly high
(e.g. (99a)) to virtual gibberish (e.g.
(98d)). Suppose, for instance, that a
subset of English sentences interesting
on quite independent grounds appears
to correlate rather closely with an
important distinction in language use.
Presumably, this analysis of a
formative as a pair of sets of features is
not quite equivalent to the system of
base rules exclusive of the lexicon. We
have already seen that the appearance
of parasitic gaps in domains relatively
inaccessible to ordinary extraction
does not readily tolerate the strong
generative capacity of the theory.'''
def _ptoTestCase(self):
"This makes one long multi-page paragraph."
# Build story.
story = []
def fbreak(story=story):
story.append(FrameBreak())
styleSheet = getSampleStyleSheet()
H1 = styleSheet['Heading1']
H1.pageBreakBefore = 0
H1.keepWithNext = 0
bt = styleSheet['BodyText']
pto = ParagraphStyle('pto',parent=bt)
pto.alignment = TA_RIGHT
pto.fontSize -= 1
def ColorParagraph(c,text,style):
return Paragraph('<para color=%s>%s</para>' % (c,text),style)
def ptoblob(blurb,content,trailer=None,header=None, story=story, H1=H1):
if type(content) not in (type([]),type(())): content = [content]
story.append(PTOContainer([Paragraph(blurb,H1)]+list(content),trailer,header))
t0 = [ColorParagraph('blue','Please turn over', pto )]
h0 = [ColorParagraph('blue','continued from previous page', pto )]
t1 = [ColorParagraph('red','Please turn over(inner)', pto )]
h1 = [ColorParagraph('red','continued from previous page(inner)', pto )]
ptoblob('First Try at a PTO',[Paragraph(text0,bt)],t0,h0)
fbreak()
c1 = Table([('alignment', 'align\012alignment'),
('bulletColor', 'bulletcolor\012bcolor'),
('bulletFontName', 'bfont\012bulletfontname'),
('bulletFontSize', 'bfontsize\012bulletfontsize'),
('bulletIndent', 'bindent\012bulletindent'),
('firstLineIndent', 'findent\012firstlineindent'),
('fontName', 'face\012fontname\012font'),
('fontSize', 'size\012fontsize'),
('leading', 'leading'),
('leftIndent', 'leftindent\012lindent'),
('rightIndent', 'rightindent\012rindent'),
('spaceAfter', 'spaceafter\012spacea'),
('spaceBefore', 'spacebefore\012spaceb'),
('textColor', 'fg\012textcolor\012color')],
style = [
('VALIGN',(0,0),(-1,-1),'TOP'),
('INNERGRID', (0,0), (-1,-1), 0.25, black),
('BOX', (0,0), (-1,-1), 0.25, black),
],
)
ptoblob('PTO with a table inside',c1,t0,h0)
fbreak()
ptoblob('A long PTO',[Paragraph(text0+' '+text1,bt)],t0,h0)
fbreak()
ptoblob('2 PTO (inner split)',[ColorParagraph('pink',text0,bt),PTOContainer([ColorParagraph(black,'Inner Starts',H1),ColorParagraph('yellow',text2,bt),ColorParagraph('black','Inner Ends',H1)],t1,h1),ColorParagraph('magenta',text1,bt)],t0,h0)
_showDoc('test_platypus_pto.pdf',story)
def _KeepInFrameTestCase(self,mode,offset=12):
story = []
def fbreak(story=story):
story.append(FrameBreak())
styleSheet = getSampleStyleSheet()
H1 = styleSheet['Heading1']
H1.pageBreakBefore = 0
H1.keepWithNext = 0
bt = styleSheet['BodyText']
story.append(KeepInFrame(170-offset,284-offset,[Paragraph(text0,bt)],mode=mode))
fbreak()
story.append(KeepInFrame(170-offset,284-offset,[Paragraph(text0,bt),Paragraph(text1,bt)],mode=mode))
fbreak()
story.append(KeepInFrame(170-offset,284-offset,[Paragraph(text0,bt),Paragraph(text1,bt),Paragraph(text2,bt)],mode=mode))
_showDoc('test_platypus_KeepInFrame%s.pdf'%mode,story)
class TestCases(unittest.TestCase):
"Test multi-page splitting of paragraphs (eyeball-test)."
def test0(self):
_ptoTestCase(self)
def test1(self):
_KeepInFrameTestCase(self,mode="shrink")
def test2(self):
_KeepInFrameTestCase(self,mode="overflow")
def test3(self):
_KeepInFrameTestCase(self,mode="truncate")
def test4(self):
from reportlab.platypus.doctemplate import LayoutError
self.assertRaises(LayoutError, _KeepInFrameTestCase,*(self,"error"))
def test5(self):
from reportlab.platypus.doctemplate import LayoutError
self.assertRaises(LayoutError, _KeepInFrameTestCase,*(self,"shrink",0))
def makeSuite():
return makeSuiteForClasses(TestCases)
#noruntests
if __name__ == "__main__": #NORUNTESTS
if 'debug' in sys.argv:
_KeepInFrameTestCase(None)
else:
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,717 @@
#!/bin/env python
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_platypus_tables.py
__version__=''' $Id: test_platypus_tables.py 2848 2006-05-04 23:45:29Z andy $ '''
__doc__='Test script for reportlab.tables'
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.platypus import Spacer, SimpleDocTemplate, Table, TableStyle
from reportlab.lib.units import inch, cm
from reportlab.lib import colors
def getTable():
t = Table((('','North','South','East','West'),
('Quarter 1',100,200,300,400),
('Quarter 2',100,400,600,800),
('Total',300,600,900,'1,200')),
(72,36,36,36,36),
(24, 16,16,18)
)
return t
def makeStyles():
styles = []
for i in range(5):
styles.append(TableStyle([('ALIGN', (1,1), (-1,-1), 'RIGHT'),
('ALIGN', (0,0), (-1,0), 'CENTRE'),
('HREF', (0,0), (0,0), 'www.google.com'),
]))
for style in styles[1:]:
style.add('GRID', (0,0), (-1,-1), 0.25, colors.black)
for style in styles[2:]:
style.add('LINEBELOW', (0,0), (-1,0), 2, colors.black)
for style in styles[3:]:
style.add('LINEABOVE', (0, -1), (-1,-1), 2, colors.black)
styles[-1].add('LINEBELOW',(1,-1), (-1, -1), 2, (0.5, 0.5, 0.5))
return styles
def run():
doc = SimpleDocTemplate(outputfile('test_platypus_tables.pdf'), pagesize=(8.5*inch, 11*inch), showBoundary=1)
styles = makeStyles()
lst = []
for style in styles:
t = getTable()
t.setStyle(style)
## print '--------------'
## for rowstyle in t._cellstyles:
## for s in rowstyle:
## print s.alignment
lst.append(t)
lst.append(Spacer(0,12))
doc.build(lst)
def old_tables_test():
from reportlab.lib.units import inch, cm
from reportlab.platypus.flowables import Image, PageBreak, Spacer, XBox
from reportlab.platypus.paragraph import Paragraph
from reportlab.platypus.xpreformatted import XPreformatted
from reportlab.platypus.flowables import Preformatted
from reportlab.platypus.doctemplate import SimpleDocTemplate
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus.tables import GRID_STYLE, BOX_STYLE, LABELED_GRID_STYLE, COLORED_GRID_STYLE, LIST_STYLE, LongTable
rowheights = (24, 16, 16, 16, 16)
rowheights2 = (24, 16, 16, 16, 30)
colwidths = (50, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32)
data = (
('', 'Jan', 'Feb', 'Mar','Apr','May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'),
('Mugs', 0, 4, 17, 3, 21, 47, 12, 33, 2, -2, 44, 89),
('T-Shirts', 0, 42, 9, -3, 16, 4, 72, 89, 3, 19, 32, 119),
('Key Ring', 0,0,0,0,0,0,1,0,0,0,2,13),
('Hats', 893, 912, '1,212', 643, 789, 159, 888, '1,298', 832, 453, '1,344','2,843')
)
data2 = (
('', 'Jan', 'Feb', 'Mar','Apr','May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'),
('Mugs', 0, 4, 17, 3, 21, 47, 12, 33, 2, -2, 44, 89),
('T-Shirts', 0, 42, 9, -3, 16, 4, 72, 89, 3, 19, 32, 119),
('Key Ring', 0,0,0,0,0,0,1,0,0,0,2,13),
('Hats\nLarge', 893, 912, '1,212', 643, 789, 159, 888, '1,298', 832, 453, '1,344','2,843')
)
styleSheet = getSampleStyleSheet()
lst = []
lst.append(Paragraph("Tables", styleSheet['Heading1']))
lst.append(Paragraph(__doc__, styleSheet['BodyText']))
lst.append(Paragraph("The Tables (shown in different styles below) were created using the following code:", styleSheet['BodyText']))
lst.append(Preformatted("""
colwidths = (50, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32)
rowheights = (24, 16, 16, 16, 16)
data = (
('', 'Jan', 'Feb', 'Mar','Apr','May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'),
('Mugs', 0, 4, 17, 3, 21, 47, 12, 33, 2, -2, 44, 89),
('T-Shirts', 0, 42, 9, -3, 16, 4, 72, 89, 3, 19, 32, 119),
('Key Ring', 0,0,0,0,0,0,1,0,0,0,2,13),
('Hats', 893, 912, '1,212', 643, 789, 159,
888, '1,298', 832, 453, '1,344','2,843')
)
t = Table(data, colwidths, rowheights)
""", styleSheet['Code'], dedent=4))
lst.append(Paragraph("""
You can then give the Table a TableStyle object to control its format. The first TableStyle used was
created as follows:
""", styleSheet['BodyText']))
lst.append(Preformatted("""
GRID_STYLE = TableStyle(
[('GRID', (0,0), (-1,-1), 0.25, colors.black),
('ALIGN', (1,1), (-1,-1), 'RIGHT')]
)
""", styleSheet['Code']))
lst.append(Paragraph("""
TableStyles are created by passing in a list of commands. There are two types of commands - line commands
and cell formatting commands. In all cases, the first three elements of a command are the command name,
the starting cell and the ending cell.
""", styleSheet['BodyText']))
lst.append(Paragraph("""
Line commands always follow this with the weight and color of the desired lines. Colors can be names,
or they can be specified as a (R,G,B) tuple, where R, G and B are floats and (0,0,0) is black. The line
command names are: GRID, BOX, OUTLINE, INNERGRID, LINEBELOW, LINEABOVE, LINEBEFORE
and LINEAFTER. BOX and OUTLINE are equivalent, and GRID is the equivalent of applying both BOX and
INNERGRID.
""", styleSheet['BodyText']))
lst.append(Paragraph("""
Cell formatting commands are:
""", styleSheet['BodyText']))
lst.append(Paragraph("""
FONT - takes fontname, fontsize and (optional) leading.
""", styleSheet['Definition']))
lst.append(Paragraph("""
TEXTCOLOR - takes a color name or (R,G,B) tuple.
""", styleSheet['Definition']))
lst.append(Paragraph("""
ALIGNMENT (or ALIGN) - takes one of LEFT, RIGHT, CENTRE (or CENTER) or DECIMAL.
""", styleSheet['Definition']))
lst.append(Paragraph("""
LEFTPADDING - defaults to 6.
""", styleSheet['Definition']))
lst.append(Paragraph("""
RIGHTPADDING - defaults to 6.
""", styleSheet['Definition']))
lst.append(Paragraph("""
BOTTOMPADDING - defaults to 3.
""", styleSheet['Definition']))
lst.append(Paragraph("""
A tablestyle is applied to a table by calling Table.setStyle(tablestyle).
""", styleSheet['BodyText']))
t = Table(data, colwidths, rowheights)
t.setStyle(GRID_STYLE)
lst.append(PageBreak())
lst.append(Paragraph("This is GRID_STYLE\n", styleSheet['BodyText']))
lst.append(t)
t = Table(data, colwidths, rowheights)
t.setStyle(BOX_STYLE)
lst.append(Paragraph("This is BOX_STYLE\n", styleSheet['BodyText']))
lst.append(t)
lst.append(Paragraph("""
It was created as follows:
""", styleSheet['BodyText']))
lst.append(Preformatted("""
BOX_STYLE = TableStyle(
[('BOX', (0,0), (-1,-1), 0.50, colors.black),
('ALIGN', (1,1), (-1,-1), 'RIGHT')]
)
""", styleSheet['Code']))
t = Table(data, colwidths, rowheights)
t.setStyle(LABELED_GRID_STYLE)
lst.append(Paragraph("This is LABELED_GRID_STYLE\n", styleSheet['BodyText']))
lst.append(t)
t = Table(data2, colwidths, rowheights2)
t.setStyle(LABELED_GRID_STYLE)
lst.append(Paragraph("This is LABELED_GRID_STYLE ILLUSTRATES EXPLICIT LINE SPLITTING WITH NEWLINE (different heights and data)\n", styleSheet['BodyText']))
lst.append(t)
lst.append(Paragraph("""
It was created as follows:
""", styleSheet['BodyText']))
lst.append(Preformatted("""
LABELED_GRID_STYLE = TableStyle(
[('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
('BOX', (0,0), (-1,-1), 2, colors.black),
('LINEBELOW', (0,0), (-1,0), 2, colors.black),
('LINEAFTER', (0,0), (0,-1), 2, colors.black),
('ALIGN', (1,1), (-1,-1), 'RIGHT')]
)
""", styleSheet['Code']))
lst.append(PageBreak())
t = Table(data, colwidths, rowheights)
t.setStyle(COLORED_GRID_STYLE)
lst.append(Paragraph("This is COLORED_GRID_STYLE\n", styleSheet['BodyText']))
lst.append(t)
lst.append(Paragraph("""
It was created as follows:
""", styleSheet['BodyText']))
lst.append(Preformatted("""
COLORED_GRID_STYLE = TableStyle(
[('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
('BOX', (0,0), (-1,-1), 2, colors.red),
('LINEBELOW', (0,0), (-1,0), 2, colors.black),
('LINEAFTER', (0,0), (0,-1), 2, colors.black),
('ALIGN', (1,1), (-1,-1), 'RIGHT')]
)
""", styleSheet['Code']))
t = Table(data, colwidths, rowheights)
t.setStyle(LIST_STYLE)
lst.append(Paragraph("This is LIST_STYLE\n", styleSheet['BodyText']))
lst.append(t)
lst.append(Paragraph("""
It was created as follows:
""", styleSheet['BodyText']))
lst.append(Preformatted("""
LIST_STYLE = TableStyle(
[('LINEABOVE', (0,0), (-1,0), 2, colors.green),
('LINEABOVE', (0,1), (-1,-1), 0.25, colors.black),
('LINEBELOW', (0,-1), (-1,-1), 2, colors.green),
('ALIGN', (1,1), (-1,-1), 'RIGHT')]
)
""", styleSheet['Code']))
t = Table(data, colwidths, rowheights)
ts = TableStyle(
[('LINEABOVE', (0,0), (-1,0), 2, colors.green),
('LINEABOVE', (0,1), (-1,-1), 0.25, colors.black),
('LINEBELOW', (0,-1), (-1,-1), 3, colors.green,'butt'),
('LINEBELOW', (0,-1), (-1,-1), 1, colors.white,'butt'),
('ALIGN', (1,1), (-1,-1), 'RIGHT'),
('TEXTCOLOR', (0,1), (0,-1), colors.red),
('BACKGROUND', (0,0), (-1,0), colors.Color(0,0.7,0.7))]
)
t.setStyle(ts)
lst.append(Paragraph("This is a custom style\n", styleSheet['BodyText']))
lst.append(t)
lst.append(Paragraph("""
It was created as follows:
""", styleSheet['BodyText']))
lst.append(Preformatted("""
ts = TableStyle(
[('LINEABOVE', (0,0), (-1,0), 2, colors.green),
('LINEABOVE', (0,1), (-1,-1), 0.25, colors.black),
('LINEBELOW', (0,-1), (-1,-1), 3, colors.green,'butt'),
('LINEBELOW', (0,-1), (-1,-1), 1, colors.white,'butt'),
('ALIGN', (1,1), (-1,-1), 'RIGHT'),
('TEXTCOLOR', (0,1), (0,-1), colors.red),
('BACKGROUND', (0,0), (-1,0), colors.Color(0,0.7,0.7))]
)
""", styleSheet['Code']))
data = (
('', 'Jan\nCold', 'Feb\n', 'Mar\n','Apr\n','May\n', 'Jun\nHot', 'Jul\n', 'Aug\nThunder', 'Sep\n', 'Oct\n', 'Nov\n', 'Dec\n'),
('Mugs', 0, 4, 17, 3, 21, 47, 12, 33, 2, -2, 44, 89),
('T-Shirts', 0, 42, 9, -3, 16, 4, 72, 89, 3, 19, 32, 119),
('Key Ring', 0,0,0,0,0,0,1,0,0,0,2,13),
('Hats', 893, 912, '1,212', 643, 789, 159, 888, '1,298', 832, 453, '1,344','2,843')
)
c = list(colwidths)
c[0] = None
c[8] = None
t = Table(data, c, [None]+list(rowheights[1:]))
t.setStyle(LIST_STYLE)
lst.append(Paragraph("""
This is a LIST_STYLE table with the first rowheight set to None ie automatic.
The top row cells are split at a newline '\\n' character. The first and August
column widths were also set to None.
""", styleSheet['BodyText']))
lst.append(t)
lst.append(Paragraph("""
This demonstrates a number of features useful in financial statements. The first is decimal alignment;
with ALIGN=DECIMAL the numbers align on the points; and the points are aligned based on
the RIGHTPADDING, which is usually 3 points so you should set it higher. The second is multiple lines;
one can specify double or triple lines and control the separation if desired. Finally, the coloured
negative numbers were (we regret to say) done in the style; we don't have a way to conditionally
format numbers based on value yet.
""", styleSheet['BodyText']))
t = Table([[u'Corporate Assets','Amount'],
['Fixed Assets','1,234,567.89'],
['Company Vehicle','1,234.8901'],
['Petty Cash','42'],
[u'Intellectual Property\u00ae','(42,078,231.56)'],
['Overdraft','(12,345)'],
['Boardroom Flat Screen','60 inches'],
['Net Position','Deep Sh*t.Really']
],
[144,72])
ts = TableStyle(
[#first the top row
('ALIGN', (1,1), (-1,-1), 'CENTER'),
('LINEABOVE', (0,0), (-1,0), 1, colors.purple),
('LINEBELOW', (0,0), (-1,0), 1, colors.purple),
('FONT', (0,0), (-1,0), 'Times-Bold'),
#bottom row has a line above, and two lines below
('LINEABOVE', (0,-1), (-1,-1), 1, colors.purple), #last 2 are count, sep
('LINEBELOW', (0,-1), (-1,-1), 0.5, colors.purple, 1, None, None, 4,1),
('LINEBELOW', (0,-1), (-1,-1), 1, colors.red),
('FONT', (0,-1), (-1,-1), 'Times-Bold'),
#numbers column
('ALIGN', (1,1), (-1,-1), 'DECIMAL'),
('RIGHTPADDING', (1,1), (-1,-1), 36),
('TEXTCOLOR', (1,4), (1,4), colors.red),
#red cell
]
)
t.setStyle(ts)
lst.append(t)
lst.append(Spacer(36,36))
lst.append(Paragraph("""
The red numbers should be aligned LEFT &amp; BOTTOM, the blue RIGHT &amp; TOP
and the green CENTER &amp; MIDDLE.
""", styleSheet['BodyText']))
XY = [['X00y', 'X01y', 'X02y', 'X03y', 'X04y'],
['X10y', 'X11y', 'X12y', 'X13y', 'X14y'],
['X20y', 'X21y', 'X22y', 'X23y', 'X24y'],
['X30y', 'X31y', 'X32y', 'X33y', 'X34y']]
t=Table(XY, 5*[0.6*inch], 4*[0.6*inch])
t.setStyle([('ALIGN',(1,1),(-2,-2),'LEFT'),
('TEXTCOLOR',(1,1),(-2,-2),colors.red),
('VALIGN',(0,0),(1,-1),'TOP'),
('ALIGN',(0,0),(1,-1),'RIGHT'),
('TEXTCOLOR',(0,0),(1,-1),colors.blue),
('ALIGN',(0,-1),(-1,-1),'CENTER'),
('VALIGN',(0,-1),(-1,-1),'MIDDLE'),
('TEXTCOLOR',(0,-1),(-1,-1),colors.green),
('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
('BOX', (0,0), (-1,-1), 0.25, colors.black),
])
lst.append(t)
data = [('alignment', 'align\012alignment'),
('bulletColor', 'bulletcolor\012bcolor'),
('bulletFontName', 'bfont\012bulletfontname'),
('bulletFontSize', 'bfontsize\012bulletfontsize'),
('bulletIndent', 'bindent\012bulletindent'),
('firstLineIndent', 'findent\012firstlineindent'),
('fontName', 'face\012fontname\012font'),
('fontSize', 'size\012fontsize'),
('leading', 'leading'),
('leftIndent', 'leftindent\012lindent'),
('rightIndent', 'rightindent\012rindent'),
('spaceAfter', 'spaceafter\012spacea'),
('spaceBefore', 'spacebefore\012spaceb'),
('textColor', 'fg\012textcolor\012color')]
t = Table(data)
t.setStyle([
('VALIGN',(0,0),(-1,-1),'TOP'),
('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
('BOX', (0,0), (-1,-1), 0.25, colors.black),
])
lst.append(t)
t = Table([ ('Attribute', 'Synonyms'),
('alignment', 'align, alignment'),
('bulletColor', 'bulletcolor, bcolor'),
('bulletFontName', 'bfont, bulletfontname'),
('bulletFontSize', 'bfontsize, bulletfontsize'),
('bulletIndent', 'bindent, bulletindent'),
('firstLineIndent', 'findent, firstlineindent'),
('fontName', 'face, fontname, font'),
('fontSize', 'size, fontsize'),
('leading', 'leading'),
('leftIndent', 'leftindent, lindent'),
('rightIndent', 'rightindent, rindent'),
('spaceAfter', 'spaceafter, spacea'),
('spaceBefore', 'spacebefore, spaceb'),
('textColor', 'fg, textcolor, color')])
t.repeatRows = 1
t.setStyle([
('FONT',(0,0),(-1,1),'Times-Bold',10,12),
('FONT',(0,1),(-1,-1),'Courier',8,8),
('VALIGN',(0,0),(-1,-1),'MIDDLE'),
('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
('BOX', (0,0), (-1,-1), 0.25, colors.black),
('BACKGROUND', (0, 0), (-1, 0), colors.green),
('BACKGROUND', (0, 1), (-1, -1), colors.pink),
('ALIGN', (0, 0), (-1, 0), 'CENTER'),
('ALIGN', (0, 1), (0, -1), 'LEFT'),
('ALIGN', (-1, 1), (-1, -1), 'RIGHT'),
('FONT', (0, 0), (-1, 0), 'Times-Bold', 12),
('ALIGN', (1, 1), (1, -1), 'CENTER'),
])
lst.append(t)
lst.append(Table(XY,
style=[ ('FONT',(0,0),(-1,-1),'Times-Roman', 5,6),
('GRID', (0,0), (-1,-1), 0.25, colors.blue),]))
lst.append(Table(XY,
style=[ ('FONT',(0,0),(-1,-1),'Times-Roman', 10,12),
('GRID', (0,0), (-1,-1), 0.25, colors.black),]))
lst.append(Table(XY,
style=[ ('FONT',(0,0),(-1,-1),'Times-Roman', 20,24),
('GRID', (0,0), (-1,-1), 0.25, colors.red),]))
lst.append(PageBreak())
data= [['00', '01', '02', '03', '04'],
['10', '11', '12', '13', '14'],
['20', '21', '22', '23', '24'],
['30', '31', '32', '33', '34']]
t=Table(data,style=[
('GRID',(0,0),(-1,-1),0.5,colors.grey),
('GRID',(1,1),(-2,-2),1,colors.green),
('BOX',(0,0),(1,-1),2,colors.red),
('BOX',(0,0),(-1,-1),2,colors.black),
('LINEABOVE',(1,2),(-2,2),1,colors.blue),
('LINEBEFORE',(2,1),(2,-2),1,colors.pink),
('BACKGROUND', (0, 0), (0, 1), colors.pink),
('BACKGROUND', (1, 1), (1, 2), colors.lavender),
('BACKGROUND', (2, 2), (2, 3), colors.orange),
])
lst.append(Paragraph("Illustrating splits: nosplit", styleSheet['BodyText']))
lst.append(t)
lst.append(Spacer(0,6))
lst.append(Paragraph("Illustrating splits: split(4in,30)", styleSheet['BodyText']))
for s in t.split(4*inch,30):
lst.append(s)
lst.append(Spacer(0,6))
lst.append(Spacer(0,6))
lst.append(Paragraph("Illustrating splits: split(4in,36)", styleSheet['BodyText']))
for s in t.split(4*inch,36):
lst.append(s)
lst.append(Spacer(0,6))
lst.append(Paragraph("Illustrating splits: split(4in,56)", styleSheet['BodyText']))
lst.append(Spacer(0,6))
for s in t.split(4*inch,56):
lst.append(s)
lst.append(Spacer(0,6))
lst.append(PageBreak())
data= [['00', '01', '02', '03', '04'],
['', '11', '12', '13', '14'],
['20', '21', '22', '23', '24'],
['30', '31', '', '33', '34']]
sty=[
('GRID',(0,0),(-1,-1),0.5,colors.grey),
('GRID',(1,1),(-2,-2),1,colors.green),
('BOX',(0,0),(1,-1),2,colors.red),
('BOX',(0,0),(-1,-1),2,colors.black),
('LINEABOVE',(1,2),(-2,2),1,colors.blue),
('LINEBEFORE',(2,1),(2,-2),1,colors.pink),
('BACKGROUND', (0, 0), (0, 1), colors.pink),
('SPAN',(0,0),(0,1)),
('BACKGROUND', (2, 2), (2, 3), colors.orange),
('SPAN',(2,2),(2,3)),
]
t=Table(data,style=sty)
lst.append(Paragraph("Illustrating splits with spans: nosplit", styleSheet['BodyText']))
lst.append(t)
lst.append(Spacer(0,6))
lst.append(Paragraph("Illustrating splits with spans: split(4in,30)", styleSheet['BodyText']))
for s in t.split(4*inch,30):
lst.append(s)
lst.append(Spacer(0,6))
lst.append(Spacer(0,6))
lst.append(Paragraph("Illustrating splits with spans: split(4in,36)", styleSheet['BodyText']))
for s in t.split(4*inch,36):
lst.append(s)
lst.append(Spacer(0,6))
lst.append(Paragraph("Illustrating splits with spans: split(4in,56)", styleSheet['BodyText']))
lst.append(Spacer(0,6))
for s in t.split(4*inch,56):
lst.append(s)
lst.append(Spacer(0,6))
data= [['00', '01', '02', '03', '04'],
['', '11', '12', '13', ''],
['20', '21', '22', '23', '24'],
['30', '31', '', '33', ''],
['40', '41', '', '43', '44']]
sty=[
('GRID',(0,0),(-1,-1),0.5,colors.grey),
('GRID',(1,1),(-2,-2),1,colors.green),
('BOX',(0,0),(1,-1),2,colors.red),
('BOX',(0,0),(-1,-1),2,colors.black),
('LINEABOVE',(1,2),(-2,2),1,colors.blue),
('LINEBEFORE',(2,1),(2,-2),1,colors.pink),
('BACKGROUND', (0, 0), (0, 1), colors.pink),
('SPAN',(0,0),(0,1)),
('BACKGROUND',(-2,1),(-1,1),colors.palegreen),
('SPAN',(-2,1),(-1,1)),
('BACKGROUND',(-2,3),(-1,3),colors.yellow),
('SPAN',(-2,3),(-1,3)),
('BACKGROUND', (2, 3), (2, 4), colors.orange),
('SPAN',(2,3),(2,4)),
]
t=Table(data,style=sty,repeatRows=2)
lst.append(Paragraph("Illustrating splits with spans and repeatRows: nosplit", styleSheet['BodyText']))
lst.append(t)
lst.append(Spacer(0,6))
if 0:
lst.append(Paragraph("Illustrating splits with spans and repeatRows: split(4in,30)", styleSheet['BodyText']))
for s in t.split(4*inch,30):
lst.append(s)
lst.append(Spacer(0,6))
lst.append(Spacer(0,6))
lst.append(Paragraph("Illustrating splits with spans and repeatRows: split(4in,36)", styleSheet['BodyText']))
for s in t.split(4*inch,36):
lst.append(s)
lst.append(Spacer(0,6))
lst.append(Paragraph("Illustrating splits with spans and repeatRows: split(4in,56)", styleSheet['BodyText']))
lst.append(Spacer(0,6))
t=Table(data,style=sty,repeatRows=2)
for s in t.split(4*inch,56):
lst.append(s)
lst.append(Spacer(0,6))
lst.append(PageBreak())
import os, reportlab.platypus
I = Image(os.path.join(os.path.dirname(reportlab.platypus.__file__),'..','tools','pythonpoint','demos','leftlogo.gif'))
I.drawHeight = 1.25*inch*I.drawHeight / I.drawWidth
I.drawWidth = 1.25*inch
#I.drawWidth = 9.25*inch #uncomment to see better messaging
P = Paragraph("<para align=center spaceb=3>The <b>ReportLab Left <font color=red>Logo</font></b> Image</para>", styleSheet["BodyText"])
data= [['A', 'B', 'C', Paragraph("<b>A pa<font color=red>r</font>a<i>graph</i></b><super><font color=yellow>1</font></super>",styleSheet["BodyText"]), 'D'],
['00', '01', '02', [I,P], '04'],
['10', '11', '12', [I,P], '14'],
['20', '21', '22', '23', '24'],
['30', '31', '32', '33', '34']]
t=Table(data,style=[('GRID',(1,1),(-2,-2),1,colors.green),
('BOX',(0,0),(1,-1),2,colors.red),
('LINEABOVE',(1,2),(-2,2),1,colors.blue),
('LINEBEFORE',(2,1),(2,-2),1,colors.pink),
('BACKGROUND', (0, 0), (0, 1), colors.pink),
('BACKGROUND', (1, 1), (1, 2), colors.lavender),
('BACKGROUND', (2, 2), (2, 3), colors.orange),
('BOX',(0,0),(-1,-1),2,colors.black),
('GRID',(0,0),(-1,-1),0.5,colors.black),
('VALIGN',(3,0),(3,0),'BOTTOM'),
('BACKGROUND',(3,0),(3,0),colors.limegreen),
('BACKGROUND',(3,1),(3,1),colors.khaki),
('ALIGN',(3,1),(3,1),'CENTER'),
('BACKGROUND',(3,2),(3,2),colors.beige),
('ALIGN',(3,2),(3,2),'LEFT'),
])
t._argW[3]=1.5*inch
lst.append(t)
# now for an attempt at column spanning.
lst.append(PageBreak())
data= [['A', 'BBBBB', 'C', 'D', 'E'],
['00', '01', '02', '03', '04'],
['10', '11', '12', '13', '14'],
['20', '21', '22', '23', '24'],
['30', '31', '32', '33', '34']]
sty = [
('ALIGN',(0,0),(-1,-1),'CENTER'),
('VALIGN',(0,0),(-1,-1),'TOP'),
('GRID',(0,0),(-1,-1),1,colors.green),
('BOX',(0,0),(-1,-1),2,colors.red),
#span 'BBBB' across middle 3 cells in top row
('SPAN',(1,0),(3,0)),
#now color the first cell in this range only,
#i.e. the one we want to have spanned. Hopefuly
#the range of 3 will come out khaki.
('BACKGROUND',(1,0),(1,0),colors.khaki),
('SPAN',(0,2),(-1,2)),
#span 'AAA'down entire left column
('SPAN',(0,0), (0, 1)),
('BACKGROUND',(0,0),(0,0),colors.cyan),
('LINEBELOW', (0,'splitlast'), (-1,'splitlast'), 1, colors.white,'butt'),
]
t=Table(data,style=sty, colWidths = [20] * 5, rowHeights = [20]*5)
lst.append(t)
# now for an attempt at percentage widths
lst.append(Spacer(18,18))
lst.append(Paragraph("This table has colWidths=5*['14%']!", styleSheet['BodyText']))
t=Table(data,style=sty, colWidths = ['14%'] * 5, rowHeights = [20]*5)
lst.append(t)
lst.append(Spacer(18,18))
lst.append(Paragraph("This table has colWidths=['14%','10%','19%','22%','*']!", styleSheet['BodyText']))
t=Table(data,style=sty, colWidths = ['14%','10%','19%','22%','*'], rowHeights = [20]*5)
lst.append(t)
# Mike's test example
lst.append(Spacer(18,18))
lst.append(Paragraph('Mike\'s Spanning Example', styleSheet['Heading1']))
data= [[Paragraph('World Domination: The First Five Years', styleSheet['BodyText']), ''],
[Paragraph('World <font color="green">Domination</font>: The First Five Years', styleSheet['BodyText']),''],
[Paragraph('World Domination: The First Five Years', styleSheet['BodyText']), ''],
]
t=Table(data, style=[('SPAN',(0,0),(1,0)),('SPAN',(0,1),(1,1)),('SPAN',(0,2),(1,2)),], colWidths = [3*cm,8*cm], rowHeights = [None]*3)
lst.append(t)
lst.append(Spacer(18,18))
lst.append(Paragraph('Mike\'s Non-spanning Example', styleSheet['Heading1']))
data= [[Paragraph('World Domination: The First Five Years', styleSheet['BodyText'])],
[Paragraph('World <font color="magenta">Domination</font>: The First Five Years', styleSheet['BodyText'])],
[Paragraph('World Domination: The First Five Years', styleSheet['BodyText'])],
]
t=Table(data, style=[], colWidths = [11*cm], rowHeights = [None]*3)
lst.append(t)
lst.append(Spacer(18,18))
lst.append(Paragraph('xpre example', styleSheet['Heading1']))
data= [ [
XPreformatted('Account Details', styleSheet['Heading3']),
'', XPreformatted('Client Details', styleSheet['Heading3']),
], #end of row 0
]
t=Table(data, style=[], colWidths = [80,230.0,80], rowHeights = [None]*1)
lst.append(t)
lst.append(PageBreak())
lst.append(Paragraph('Trying colour cycling in background', styleSheet['Heading1']))
lst.append(Paragraph("This should alternate pale blue and uncolored by row", styleSheet['BodyText']))
data= [['001', '01', '02', '03', '04', '05'],
['002', '01', '02', '03', '04', '05'],
['003', '01', '02', '03', '04', '05'],
['004', '01', '02', '03', '04', '05'],
['005', '01', '02', '03', '04', '05'],
['006', '01', '02', '03', '04', '05'],
['007', '01', '02', '03', '04', '05'],
['008', '01', '02', '03', '04', '05'],
['009', '01', '02', '03', '04', '05'],
['010', '01', '02', '03', '04', '05'],
]
t=Table(data,style=[
('GRID',(0,0),(-1,-1),0.5,colors.grey),
('ROWBACKGROUNDS', (0, 0), (-1, -1), (0xD0D0FF, None)),
])
lst.append(t)
lst.append(Spacer(0,6))
lst.append(Paragraph("And this should pale blue, pale pink and None by column", styleSheet['BodyText']))
data= [['001', '01', '02', '03', '04', '05'],
['002', '01', '02', '03', '04', '05'],
['003', '01', '02', '03', '04', '05'],
['004', '01', '02', '03', '04', '05'],
['005', '01', '02', '03', '04', '05'],
['006', '01', '02', '03', '04', '05'],
['007', '01', '02', '03', '04', '05'],
['008', '01', '02', '03', '04', '05'],
['009', '01', '02', '03', '04', '05'],
['010', '01', '02', '03', '04', '05'],
]
t=Table(data,style=[
('GRID',(0,0),(-1,-1),0.5,colors.grey),
('COLBACKGROUNDS', (0, 0), (-1, -1), (0xD0D0FF, 0xFFD0D0, None)),
])
lst.append(t)
lst.append(PageBreak())
lst.append(Paragraph("This spanning example illustrates automatic removal of grids and lines in spanned cells!", styleSheet['BodyText']))
lst.append(Spacer(0,6))
data= [['Top\nLeft', '', '02', '03', '04', '05', '06', '07'],
['', '', '12', 'Span (3,1) (6,2)', '','','','17'],
['20', '21', '22', '', '','','','27'],
['30', '31', '32', '33', '34','35','36','37'],
['40', 'In The\nMiddle', '', '', '44','45','46','47'],
['50', '', '', '', '54','55','56','57'],
['60', '', '', '','64', '65', 'Bottom\nRight', ''],
['70', '71', '72', '73','74', '75', '', '']]
t=Table(data,style=[
('GRID',(0,0),(-1,-1),0.5,colors.grey),
('BACKGROUND',(0,0),(1,1),colors.palegreen),
('SPAN',(0,0),(1,1)),
('BACKGROUND',(-2,-2),(-1,-1), colors.pink),
('SPAN',(-2,-2),(-1,-1)),
('SPAN',(1,4),(3,6)),
('BACKGROUND',(1,4),(3,6), colors.lightblue),
('SPAN',(3,1),(6,2)),
('BACKGROUND',(3,1),(6,2), colors.peachpuff),
('VALIGN',(3,1),(6,2),'TOP'),
('LINEABOVE', (0,2),(-1,2), 1, colors.black, 0, None, None, 2, 2),
('LINEBEFORE', (3,0),(3,-1), 1, colors.black, 0, None, None, 2, 2),
])
lst.append(t)
lst.append(PageBreak())
lst.append(Paragraph("und jetzt noch eine Tabelle mit 5000 Zeilen:", styleSheet['BodyText']))
sty = [ ('GRID',(0,0),(-1,-1),1,colors.green),
('BOX',(0,0),(-1,-1),2,colors.red),
]
data = [[str(i), Paragraph("xx "* (i%10), styleSheet["BodyText"]), Paragraph("blah "*(i%40), styleSheet["BodyText"])] for i in xrange(500)]
t=LongTable(data, style=sty, colWidths = [50,100,200])
lst.append(t)
SimpleDocTemplate(outputfile('tables.pdf'), showBoundary=1).build(lst)
class TablesTestCase(unittest.TestCase):
"Make documents with tables"
def test0(self):
"Make a document full of tables"
run()
def test1(self):
"Make a document full of tables"
old_tables_test()
def makeSuite():
return makeSuiteForClasses(TablesTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,183 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_platypus_toc.py
"""Tests for the Platypus TableOfContents class.
Currently there is only one such test. Most such tests, like this
one, will be generating a PDF document that needs to be eye-balled
in order to find out if it is 'correct'.
"""
import sys, os
from os.path import join, basename, splitext
from math import sqrt
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.lib.units import inch, cm
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus.paragraph import Paragraph
from reportlab.platypus.xpreformatted import XPreformatted
from reportlab.platypus.frames import Frame
from reportlab.platypus.doctemplate \
import PageTemplate, BaseDocTemplate
from reportlab.platypus import tableofcontents
from reportlab.platypus.tableofcontents import TableOfContents
from reportlab.platypus.tables import TableStyle, Table
from reportlab.lib import randomtext
def myMainPageFrame(canvas, doc):
"The page frame used for all PDF documents."
canvas.saveState()
canvas.rect(2.5*cm, 2.5*cm, 15*cm, 25*cm)
canvas.setFont('Times-Roman', 12)
pageNumber = canvas.getPageNumber()
canvas.drawString(10*cm, cm, str(pageNumber))
canvas.restoreState()
class MyDocTemplate(BaseDocTemplate):
"The document template used for all PDF documents."
_invalidInitArgs = ('pageTemplates',)
def __init__(self, filename, **kw):
frame1 = Frame(2.5*cm, 2.5*cm, 15*cm, 25*cm, id='F1')
self.allowSplitting = 0
apply(BaseDocTemplate.__init__, (self, filename), kw)
template = PageTemplate('normal', [frame1], myMainPageFrame)
self.addPageTemplates(template)
def afterFlowable(self, flowable):
"Registers TOC entries and makes outline entries."
if flowable.__class__.__name__ == 'Paragraph':
styleName = flowable.style.name
if styleName[:7] == 'Heading':
# Register TOC entries.
level = int(styleName[7:])
text = flowable.getPlainText()
pageNum = self.page
self.notify('TOCEntry', (level, text, pageNum))
# Add PDF outline entries (not really needed/tested here).
key = str(hash(flowable))
c = self.canv
c.bookmarkPage(key)
c.addOutlineEntry(text, key, level=level, closed=0)
def makeHeaderStyle(level, fontName='Times-Roman'):
"Make a header style for different levels."
assert level >= 0, "Level must be >= 0."
PS = ParagraphStyle
size = 24.0 / sqrt(1+level)
style = PS(name = 'Heading' + str(level),
fontName = fontName,
fontSize = size,
leading = size*1.2,
spaceBefore = size/4.0,
spaceAfter = size/8.0)
return style
def makeBodyStyle():
"Body text style - the default will do"
return ParagraphStyle('body')
def makeTocHeaderStyle(level, delta, epsilon, fontName='Times-Roman'):
"Make a header style for different levels."
assert level >= 0, "Level must be >= 0."
PS = ParagraphStyle
size = 12
style = PS(name = 'Heading' + str(level),
fontName = fontName,
fontSize = size,
leading = size*1.2,
spaceBefore = size/4.0,
spaceAfter = size/8.0,
firstLineIndent = -epsilon,
leftIndent = level*delta + epsilon)
return style
class TocTestCase(unittest.TestCase):
"Test TableOfContents class (eyeball-test)."
def test0(self):
"""Test story with TOC and a cascaded header hierarchy.
The story should contain exactly one table of contents that is
immediatly followed by a list of of cascaded levels of header
lines, each nested one level deeper than the previous one.
Features to be visually confirmed by a human being are:
1. TOC lines are indented in multiples of 1 cm.
2. Wrapped TOC lines continue with additional 0.5 cm indentation.
3. ...
"""
maxLevels = 12
# Create styles to be used for document headers
# on differnet levels.
headerLevelStyles = []
for i in range(maxLevels):
headerLevelStyles.append(makeHeaderStyle(i))
# Create styles to be used for TOC entry lines
# for headers on differnet levels.
tocLevelStyles = []
d, e = tableofcontents.delta, tableofcontents.epsilon
for i in range(maxLevels):
tocLevelStyles.append(makeTocHeaderStyle(i, d, e))
# Build story.
story = []
styleSheet = getSampleStyleSheet()
bt = styleSheet['BodyText']
description = '<font color=red>%s</font>' % self.test0.__doc__
story.append(XPreformatted(description, bt))
toc = TableOfContents()
toc.levelStyles = tocLevelStyles
story.append(toc)
for i in range(maxLevels):
story.append(Paragraph('HEADER, LEVEL %d' % i,
headerLevelStyles[i]))
#now put some body stuff in.
txt = randomtext.randomText(randomtext.PYTHON, 5)
para = Paragraph(txt, makeBodyStyle())
story.append(para)
path = outputfile('test_platypus_toc.pdf')
doc = MyDocTemplate(path)
doc.multiBuild(story)
def makeSuite():
return makeSuiteForClasses(TocTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,140 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_platypus_xref.py
"""Test long documents with indexes, tables and cross-references
"""
import sys, os, time
from string import split, strip, join, whitespace, find
from operator import truth
from types import StringType, ListType
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.lib import colors
from reportlab.lib.units import cm
from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus import Paragraph, Flowable, Frame, PageTemplate, BaseDocTemplate
from reportlab.platypus.frames import Frame
from reportlab.lib.randomtext import randomText, PYTHON
from reportlab.platypus.tableofcontents import TableOfContents, SimpleIndex
def myMainPageFrame(canvas, doc):
"The page frame used for all PDF documents."
canvas.saveState()
canvas.setFont('Times-Roman', 12)
pageNumber = canvas.getPageNumber()
canvas.drawString(10*cm, cm, str(pageNumber))
canvas.restoreState()
class MyDocTemplate(BaseDocTemplate):
_invalidInitArgs = ('pageTemplates',)
def __init__(self, filename, **kw):
frame1 = Frame(2.5*cm, 2.5*cm, 16*cm, 25*cm, id='Frame1')
self.allowSplitting = 0
self.showBoundary = 1
apply(BaseDocTemplate.__init__, (self, filename), kw)
template = PageTemplate('normal', [frame1], myMainPageFrame)
self.addPageTemplates(template)
def afterFlowable(self, flowable):
"Registers TOC and Index entries and makes outline entries."
if flowable.__class__.__name__ == 'Paragraph':
styleName = flowable.style.name
if styleName == 'Heading1':
level = 0
text = flowable.getPlainText()
pageNum = self.page
self.notify('TOCEntry', (level, text, pageNum))
# Add PDF outline entries (not really needed/tested here).
key = str(hash(flowable))
c = self.canv
c.bookmarkPage(key)
c.addOutlineEntry(text, key, level=level, closed=0)
# index a bunch of pythonic buzzwords. In real life this
# would be driven by markup.
try:
text = flowable.getPlainText()
except:
return
for phrase in ['uniform','depraved','finger', 'Fraudulin']:
if find(text, phrase) > -1:
self.notify('IndexEntry', (phrase, self.page))
#print 'IndexEntry:',phrase, self.page
def _test0(self):
"This makes one long multi-page paragraph."
# Build story.
story = []
styleSheet = getSampleStyleSheet()
h1 = styleSheet['Heading1']
h1.pageBreakBefore = 1
h1.keepWithNext = 1
h1.outlineLevel = 0
h2 = styleSheet['Heading2']
h2.backColor = colors.cyan
h2.keepWithNext = 1
h2.outlineLevel = 1
bt = styleSheet['BodyText']
story.append(Paragraph("""Cross-Referencing Test""", styleSheet["Title"]))
story.append(Paragraph("""
Subsequent pages test cross-references: indexes, tables and individual
cross references. The number in brackets at the end of each paragraph
is its position in the story. (%d)""" % len(story), bt))
story.append(Paragraph("""Table of Contents:""", styleSheet["Title"]))
toc = TableOfContents()
story.append(toc)
chapterNum = 1
for i in range(10):
story.append(Paragraph('Chapter %d: Chapters always starts a new page' % chapterNum, h1))
chapterNum = chapterNum + 1
for j in range(3):
story.append(Paragraph('Heading1 paragraphs should always'
'have a page break before. Heading 2 on the other hand'
'should always have a FRAME break before (%d)' % len(story), bt))
story.append(Paragraph('Heading 2 should always be kept with the next thing (%d)' % len(story), h2))
for j in range(3):
story.append(Paragraph(randomText(theme=PYTHON, sentences=2)+' (%d)' % len(story), bt))
story.append(Paragraph('I should never be at the bottom of a frame (%d)' % len(story), h2))
story.append(Paragraph(randomText(theme=PYTHON, sentences=1)+' (%d)' % len(story), bt))
story.append(Paragraph('The Index which goes at the back', h1))
story.append(SimpleIndex())
doc = MyDocTemplate(outputfile('test_platypus_xref.pdf'))
doc.multiBuild(story)
class BreakingTestCase(unittest.TestCase):
"Test multi-page splitting of paragraphs (eyeball-test)."
def test0(self):
_test0(self)
def makeSuite():
return makeSuiteForClasses(BreakingTestCase)
#noruntests
if __name__ == "__main__":
if 'debug' in sys.argv:
_test1(None)
else:
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,158 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_pyfiles.py
"""Tests performed on all Python source files of the ReportLab distribution.
"""
import os, sys, string, fnmatch, re
import reportlab
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, SecureTestCase, GlobDirectoryWalker, outputfile, printLocation
from reportlab.lib.utils import open_and_read, open_and_readlines
RL_HOME = os.path.dirname(reportlab.__file__)
# Helper function and class.
def unique(seq):
"Remove elements from a list that occur more than once."
# Return input if it has less than 2 elements.
if len(seq) < 2:
return seq
# Make a sorted copy of the input sequence.
seq2 = seq[:]
if type(seq2) == type(''):
seq2 = map(None, seq2)
seq2.sort()
# Remove adjacent elements if they are identical.
i = 0
while i < len(seq2)-1:
elem = seq2[i]
try:
while elem == seq2[i+1]:
del seq2[i+1]
except IndexError:
pass
i = i + 1
# Try to return something of the same type as the input.
if type(seq) == type(''):
return string.join(seq2, '')
else:
return seq2
class SelfTestCase(unittest.TestCase):
"Test unique() function."
def testUnique(self):
"Test unique() function."
cases = [([], []),
([0], [0]),
([0, 1, 2], [0, 1, 2]),
([2, 1, 0], [0, 1, 2]),
([0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 2, 3]),
('abcabcabc', 'abc')
]
msg = "Failed: unique(%s) returns %s instead of %s."
for sequence, expectedOutput in cases:
output = unique(sequence)
args = (sequence, output, expectedOutput)
assert output == expectedOutput, msg % args
class AsciiFileTestCase(unittest.TestCase):
"Test if Python files are pure ASCII ones."
def testAscii(self):
"Test if Python files are pure ASCII ones."
RL_HOME = os.path.dirname(reportlab.__file__)
allPyFiles = GlobDirectoryWalker(RL_HOME, '*.py')
for path in allPyFiles:
fileContent = open_and_read(path,'r')
nonAscii = filter(lambda c: ord(c)>127, fileContent)
nonAscii = unique(nonAscii)
truncPath = path[string.find(path, 'reportlab'):]
args = (truncPath, repr(map(ord, nonAscii)))
msg = "File %s contains characters: %s." % args
## if nonAscii:
## print msg
assert nonAscii == '', msg
class FilenameTestCase(unittest.TestCase):
"Test if Python files contain trailing digits."
def testTrailingDigits(self):
"Test if Python files contain trailing digits."
allPyFiles = GlobDirectoryWalker(RL_HOME, '*.py')
for path in allPyFiles:
#hack - exclude barcode extensions from this test
if string.find(path, 'barcode'):
pass
else:
basename = os.path.splitext(path)[0]
truncPath = path[string.find(path, 'reportlab'):]
msg = "Filename %s contains trailing digits." % truncPath
assert basename[-1] not in string.digits, msg
## if basename[-1] in string.digits:
## print truncPath
class FirstLineTestCase(SecureTestCase):
"Testing if objects in the ReportLab package have docstrings."
def findSuspiciousModules(self, folder, rootName):
"Get all modul paths with non-Unix-like first line."
firstLinePat = re.compile('^#!.*python.*')
paths = []
for file in GlobDirectoryWalker(folder, '*.py'):
if os.path.basename(file) == '__init__.py':
continue
firstLine = open_and_readlines(file)[0]
if not firstLinePat.match(firstLine):
paths.append(file)
return paths
def test1(self):
"Test if all Python files have a Unix-like first line."
path = outputfile("test_firstline.log")
file = open(path, 'w')
file.write('No Unix-like first line found in the files below.\n\n')
paths = self.findSuspiciousModules(RL_HOME, 'reportlab')
paths.sort()
for p in paths:
file.write("%s\n" % p)
file.close()
def makeSuite():
suite = makeSuiteForClasses(SelfTestCase, AsciiFileTestCase, FilenameTestCase)
if sys.platform[:4] != 'java':
loader = unittest.TestLoader()
suite.addTest(loader.loadTestsFromTestCase(FirstLineTestCase))
return suite
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,165 @@
#!/usr/bin/env python
import sys, string
from xml.dom import minidom
from xml.sax._exceptions import SAXReaderNotAvailable
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.graphics.shapes import *
from reportlab.graphics import renderSVG
def warnIgnoredRestofTest():
"Raise a warning (if possible) about a not fully completed test."
version = sys.version_info[:2]
msg = "XML parser not found - consider installing expat! Rest of test(s) ignored!"
if version >= (2, 1):
import warnings
warnings.warn(msg)
else:
# should better also be printed only once...
print msg
# Check if we have a default XML parser available or not.
try:
import xml
from xml.sax import make_parser
p = xml.sax.make_parser()
HAVE_XML_PARSER = 1
except SAXReaderNotAvailable:
HAVE_XML_PARSER = 0
def load(path):
"Helper function to read the generated SVG again."
doc = minidom.parse(path)
doc.normalize()
return doc.documentElement
class RenderSvgSimpleTestCase(unittest.TestCase):
"Testing renderSVG module."
def test0(self):
"Test two strings in drawing."
path = outputfile("test_renderSVG_simple_test0.svg")
d = Drawing(200, 100)
d.add(String(0, 0, "foo"))
d.add(String(100, 0, "bar"))
renderSVG.drawToFile(d, path)
if not HAVE_XML_PARSER:
warnIgnoredRestofTest()
return
svg = load(path)
fg = svg.getElementsByTagName('g')[0] # flipping group
dg = fg.getElementsByTagName('g')[0] # diagram group
textChildren = dg.getElementsByTagName('text') # text nodes
t0 = string.strip(textChildren[0].childNodes[0].nodeValue)
t1 = string.strip(textChildren[1].childNodes[0].nodeValue)
assert t0 == 'foo'
assert t1 == 'bar'
def test1(self):
"Test two strings in group in drawing."
path = outputfile("test_renderSVG_simple_test1.svg")
d = Drawing(200, 100)
g = Group()
g.add(String(0, 0, "foo"))
g.add(String(100, 0, "bar"))
d.add(g)
renderSVG.drawToFile(d, path)
if not HAVE_XML_PARSER:
warnIgnoredRestofTest()
return
svg = load(path)
fg = svg.getElementsByTagName('g')[0] # flipping group
dg = fg.getElementsByTagName('g')[0] # diagram group
g = dg.getElementsByTagName('g')[0] # custom group
textChildren = g.getElementsByTagName('text') # text nodes
t0 = string.strip(textChildren[0].childNodes[0].nodeValue)
t1 = string.strip(textChildren[1].childNodes[0].nodeValue)
assert t0 == 'foo'
assert t1 == 'bar'
def test2(self):
"Test two strings in transformed group in drawing."
path = outputfile("test_renderSVG_simple_test2.svg")
d = Drawing(200, 100)
g = Group()
g.add(String(0, 0, "foo"))
g.add(String(100, 0, "bar"))
g.scale(1.5, 1.2)
g.translate(50, 0)
d.add(g)
renderSVG.drawToFile(d, path)
if not HAVE_XML_PARSER:
warnIgnoredRestofTest()
return
svg = load(path)
fg = svg.getElementsByTagName('g')[0] # flipping group
dg = fg.getElementsByTagName('g')[0] # diagram group
g = dg.getElementsByTagName('g')[0] # custom group
textChildren = g.getElementsByTagName('text') # text nodes
t0 = string.strip(textChildren[0].childNodes[0].nodeValue)
t1 = string.strip(textChildren[1].childNodes[0].nodeValue)
assert t0 == 'foo'
assert t1 == 'bar'
class RenderSvgAxesTestCase(unittest.TestCase):
"Testing renderSVG module on Axes widgets."
def test0(self):
"Test two strings in drawing."
path = outputfile("axestest0.svg")
from reportlab.graphics.charts.axes import XCategoryAxis
d = XCategoryAxis().demo()
renderSVG.drawToFile(d, path)
def makeSuite():
return makeSuiteForClasses(RenderSvgSimpleTestCase, RenderSvgAxesTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,168 @@
__version__=''' $Id'''
__doc__='''basic tests.'''
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, printLocation
def getrc(defns,depth=1):
from sys import getrefcount, _getframe
f = _getframe(depth)
G0 = f.f_globals
L = f.f_locals
if L is not G0:
LL = [L]
while 1:
f = f.f_back
G = f.f_globals
L = f.f_locals
if G is not G0 or G is L: break
LL.append(L)
L = {}
for l in reversed(LL):
L.update(l)
else:
L = L.copy()
G0 = G0.copy()
return [getrefcount(eval(x,L,G0))-1 for x in defns.split()]
def checkrc(defns,rcv0):
rcv1 = getrc(defns,2)
return ' '.join(["%s %d-->%d" % (x,v,w) for x,v,w in zip(defns.split(),rcv0,rcv1) if v!=w])
class RlAccelTestCase(unittest.TestCase):
def testFpStr(self):
# should give siz decimal places if less than 1.
# if more, give up to seven sig figs
from _rl_accel import fp_str
assert fp_str(1,2,3)=='1 2 3'
assert fp_str(1) == '1'
assert fp_str(595.275574) == '595.2756'
assert fp_str(59.5275574) == '59.52756'
assert fp_str(5.95275574) == '5.952756'
def test_AsciiBase85Encode(self):
from _rl_accel import _AsciiBase85Encode
assert _AsciiBase85Encode('Dragan Andric')=='6ul^K@;[2RDIdd%@f~>'
def test_AsciiBase85Decode(self):
from _rl_accel import _AsciiBase85Decode
assert _AsciiBase85Decode('6ul^K@;[2RDIdd%@f~>')=='Dragan Andric'
def testEscapePDF(self):
from _rl_accel import escapePDF
assert escapePDF('(test)')=='\\(test\\)'
def test_instanceEscapePDF(self):
from _rl_accel import _instanceEscapePDF
assert _instanceEscapePDF('', '(test)')=='\\(test\\)'
def testCalcChecksum(self):
from _rl_accel import calcChecksum
assert calcChecksum('test')==1952805748
def testStringWidth(self):
from _rl_accel import stringWidthU
from reportlab.pdfbase.pdfmetrics import _py_stringWidth, getFont, registerFont, _fonts
from reportlab.pdfbase.ttfonts import TTFont
ttfn = 'Luxi-Serif'
t1fn = 'Times-Roman'
registerFont(TTFont(ttfn, "luxiserif.ttf"))
ttf = getFont(ttfn)
t1f = getFont(t1fn)
testCp1252 = 'copyright %s trademark %s registered %s ReportLab! Ol%s!' % (chr(169), chr(153),chr(174), chr(0xe9))
enc='cp1252'
senc = 'utf8'
intern(senc)
ts = 'ABCDEF\xce\x91\xce\xb2G'
utext = 'ABCDEF\xce\x91\xce\xb2G'.decode('utf8')
fontSize = 12
defns="ttfn t1fn ttf t1f testCp1252 enc senc ts utext fontSize ttf.face ttf.face.charWidths ttf.face.defaultWidth t1f.widths t1f.encName t1f.substitutionFonts _fonts"
rcv = getrc(defns)
def tfunc(ts,fn,fontSize,enc):
w1 = stringWidthU(ts,fn,fontSize,enc)
w2 = _py_stringWidth(ts,fn,fontSize,enc)
assert abs(w1-w2)<1e-10,"stringWidthU(%r,%r,%s,%r)-->%r != _py_stringWidth(...)-->%r" % (ts,fn,fontSize,enc,w1,w2)
tfunc(testCp1252,t1fn,fontSize,enc)
tfunc(ts,t1fn,fontSize,senc)
tfunc(utext,t1fn,fontSize,senc)
tfunc(ts,ttfn,fontSize,senc)
tfunc(testCp1252,ttfn,fontSize,enc)
tfunc(utext,ttfn,fontSize,senc)
rcc = checkrc(defns,rcv)
assert not rcc, "rc diffs (%s)" % rcc
def test_instanceStringWidth(self):
from reportlab.pdfbase.pdfmetrics import registerFont, getFont, _fonts, unicode2T1
from reportlab.pdfbase.ttfonts import TTFont
ttfn = 'Luxi-Serif'
t1fn = 'Times-Roman'
registerFont(TTFont(ttfn, "luxiserif.ttf"))
ttf = getFont(ttfn)
t1f = getFont(t1fn)
testCp1252 = 'copyright %s trademark %s registered %s ReportLab! Ol%s!' % (chr(169), chr(153),chr(174), chr(0xe9))
enc='cp1252'
senc = 'utf8'
ts = 'ABCDEF\xce\x91\xce\xb2G'
utext = 'ABCDEF\xce\x91\xce\xb2G'.decode(senc)
fontSize = 12
defns="ttfn t1fn ttf t1f testCp1252 enc senc ts utext fontSize ttf.face ttf.face.charWidths ttf.face.defaultWidth t1f.widths t1f.encName t1f.substitutionFonts _fonts"
rcv = getrc(defns)
def tfunc(f,ts,fontSize,enc):
w1 = f.stringWidth(ts,fontSize,enc)
w2 = f._py_stringWidth(ts,fontSize,enc)
assert abs(w1-w2)<1e-10,"f(%r).stringWidthU(%r,%s,%r)-->%r != f._py_stringWidth(...)-->%r" % (f,ts,fontSize,enc,w1,w2)
tfunc(t1f,testCp1252,fontSize,enc)
tfunc(t1f,ts,fontSize,senc)
tfunc(t1f,utext,fontSize,senc)
tfunc(ttf,ts,fontSize,senc)
tfunc(ttf,testCp1252,fontSize,enc)
tfunc(ttf,utext,fontSize,senc)
rcc = checkrc(defns,rcv)
assert not rcc, "rc diffs (%s)" % rcc
def test_unicode2T1(self):
from reportlab.pdfbase.pdfmetrics import _py_unicode2T1, getFont, _fonts
from _rl_accel import unicode2T1
t1fn = 'Times-Roman'
t1f = getFont(t1fn)
enc = 'cp1252'
senc = 'utf8'
testCp1252 = ('copyright %s trademark %s registered %s ReportLab! Ol%s!' % (chr(169), chr(153),chr(174), chr(0xe9))).decode(enc)
utext = 'This is the end of the \xce\x91\xce\xb2 world. This is the end of the \xce\x91\xce\xb2 world jap=\xe3\x83\x9b\xe3\x83\x86. This is the end of the \xce\x91\xce\xb2 world. This is the end of the \xce\x91\xce\xb2 world jap=\xe3\x83\x9b\xe3\x83\x86'.decode('utf8')
def tfunc(f,ts):
w1 = unicode2T1(ts,[f]+f.substitutionFonts)
w2 = _py_unicode2T1(ts,[f]+f.substitutionFonts)
assert w1==w2,"%r != %r" % (w1,w2)
defns="t1fn t1f testCp1252 enc senc utext t1f.widths t1f.encName t1f.substitutionFonts _fonts"
rcv = getrc(defns)
tfunc(t1f,testCp1252)
tfunc(t1f,utext)
rcc = checkrc(defns,rcv)
assert not rcc, "rc diffs (%s)" % rcc
def test_getFont(self):
from reportlab.pdfbase.pdfmetrics import _py_getFont, getFont
from _rl_accel import getFontU
assert getFontU is getFont
t1fn = 'Times-Roman'
assert _py_getFont(t1fn) is getFontU(t1fn)
def test_sameFrag(self):
pass
def makeSuite():
# only run the tests if _rl_accel is present
try:
import _rl_accel
Klass = RlAccelTestCase
except:
class Klass(unittest.TestCase):
pass
return makeSuiteForClasses(Klass)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,95 @@
#!/usr/bin/env python
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_source_chars.py
"""This tests for things in source files. Initially, absence of tabs :-)
"""
import os, sys, glob, string, re
from types import ModuleType, ClassType, MethodType, FunctionType
import reportlab
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, SecureTestCase, GlobDirectoryWalker, printLocation
from reportlab.lib.utils import open_and_read
class SourceTester(SecureTestCase):
def setUp(self):
SecureTestCase.setUp(self)
try:
fn = __file__
except:
fn = sys.argv[0]
self.output = open(outputfile(os.path.splitext(os.path.basename(fn))[0]+'.txt'),'w')
def checkFileForTabs(self, filename):
txt = open_and_read(filename, 'r')
chunks = string.split(txt, '\t')
tabCount = len(chunks) - 1
if tabCount:
#raise Exception, "File %s contains %d tab characters!" % (filename, tabCount)
self.output.write("file %s contains %d tab characters!\n" % (filename, tabCount))
def checkFileForTrailingSpaces(self, filename):
txt = open_and_read(filename, 'r')
initSize = len(txt)
badLines = 0
badChars = 0
for line in string.split(txt, '\n'):
stripped = string.rstrip(line)
spaces = len(line) - len(stripped) # OK, so they might be trailing tabs, who cares?
if spaces:
badLines = badLines + 1
badChars = badChars + spaces
if badChars <> 0:
self.output.write("file %s contains %d trailing spaces, or %0.2f%% wastage\n" % (filename, badChars, 100.0*badChars/initSize))
def testFiles(self):
topDir = os.path.dirname(reportlab.__file__)
w = GlobDirectoryWalker(topDir, '*.py')
for filename in w:
self.checkFileForTabs(filename)
self.checkFileForTrailingSpaces(filename)
def zapTrailingWhitespace(dirname):
"""Eliminates trailing spaces IN PLACE. Use with extreme care
and only after a backup or with version-controlled code."""
assert os.path.isdir(dirname), "Directory not found!"
print "This will eliminate all trailing spaces in py files under %s." % dirname
ok = raw_input("Shall I proceed? type YES > ")
if ok <> 'YES':
print 'aborted by user'
return
w = GlobDirectoryWalker(dirname, '*.py')
for filename in w:
# trim off final newline and detect real changes
txt = open(filename, 'r').read()
badChars = 0
cleaned = []
for line in string.split(txt, '\n'):
stripped = string.rstrip(line)
cleaned.append(stripped)
spaces = len(line) - len(stripped) # OK, so they might be trailing tabs, who cares?
if spaces:
badChars = badChars + spaces
if badChars <> 0:
open(filename, 'w').write(string.join(cleaned, '\n'))
print "file %s contained %d trailing spaces, FIXED" % (filename, badChars)
print 'done'
def makeSuite():
return makeSuiteForClasses(SourceTester)
#noruntests
if __name__ == "__main__":
if len(sys.argv) == 3 and sys.argv[1] == 'zap' and os.path.isdir(sys.argv[2]):
zapTrailingWhitespace(sys.argv[2])
else:
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,425 @@
import operator, string
from reportlab.platypus import *
#from reportlab import rl_config
from reportlab.lib.styles import PropertySet, getSampleStyleSheet, ParagraphStyle
from reportlab.lib import colors
from reportlab.platypus.paragraph import Paragraph
#from reportlab.lib.utils import fp_str
#from reportlab.pdfbase import pdfmetrics
from reportlab.platypus.flowables import PageBreak
import os
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from types import TupleType, ListType, StringType
class TableTestCase(unittest.TestCase):
def getDataBlock(self):
"Helper - data for our spanned table"
return [
# two rows are for headers
['Region','Product','Period',None,None,None,'Total'],
[None,None,'Q1','Q2','Q3','Q4',None],
# now for data
['North','Spam',100,110,120,130,460],
['North','Eggs',101,111,121,131,464],
['North','Guinness',102,112,122,132,468],
['South','Spam',100,110,120,130,460],
['South','Eggs',101,111,121,131,464],
['South','Guinness',102,112,122,132,468],
]
def test_document(self):
rowheights = (24, 16, 16, 16, 16)
rowheights2 = (24, 16, 16, 16, 30)
colwidths = (50, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32)
GRID_STYLE = TableStyle(
[('GRID', (0,0), (-1,-1), 0.25, colors.black),
('ALIGN', (1,1), (-1,-1), 'RIGHT')]
)
styleSheet = getSampleStyleSheet()
styNormal = styleSheet['Normal']
styNormal.spaceBefore = 6
styNormal.spaceAfter = 6
data = (
('', 'Jan', 'Feb', 'Mar','Apr','May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'),
('Mugs', 0, 4, 17, 3, 21, 47, 12, 33, 2, -2, 44, 89),
('T-Shirts', 0, 42, 9, -3, 16, 4, 72, 89, 3, 19, 32, 119),
('Miscellaneous accessories', 0,0,0,0,0,0,1,0,0,0,2,13),
('Hats', 893, 912, '1,212', 643, 789, 159, 888, '1,298', 832, 453, '1,344','2,843')
)
data2 = (
('', 'Jan', 'Feb', 'Mar','Apr','May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'),
('Mugs', 0, 4, 17, 3, 21, 47, 12, 33, 2, -2, 44, 89),
('T-Shirts', 0, 42, 9, -3, 16, 4, 72, 89, 3, 19, 32, 119),
('Key Ring', 0,0,0,0,0,0,1,0,0,0,2,13),
('Hats\nLarge', 893, 912, '1,212', 643, 789, 159, 888, '1,298', 832, 453, '1,344','2,843')
)
data3 = (
('', 'Jan', 'Feb', 'Mar','Apr','May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'),
('Mugs', 0, 4, 17, 3, 21, 47, 12, 33, 2, -2, 44, 89),
('T-Shirts', 0, 42, 9, -3, 16, 4, 72, 89, 3, 19, 32, 119),
('Key Ring', 0,0,0,0,0,0,1,0,0,0,2,13),
(Paragraph("Let's <b>really mess things up with a <i>paragraph</i>",styNormal),
893, 912, '1,212', 643, 789, 159, 888, '1,298', 832, 453, '1,344','2,843')
)
lst = []
lst.append(Paragraph("""Basics about column sizing and cell contents""", styleSheet['Heading1']))
t1 = Table(data, colwidths, rowheights)
t1.setStyle(GRID_STYLE)
lst.append(Paragraph("This is GRID_STYLE with explicit column widths. Each cell contains a string or number\n", styleSheet['BodyText']))
lst.append(t1)
lst.append(Spacer(18,18))
t2 = Table(data, None, None)
t2.setStyle(GRID_STYLE)
lst.append(Paragraph("""This is GRID_STYLE with no size info. It
does the sizes itself, measuring each text string
and computing the space it needs. If the text is
too wide for the frame, the table will overflow
as seen here.""",
styNormal))
lst.append(t2)
lst.append(Spacer(18,18))
t3 = Table(data2, None, None)
t3.setStyle(GRID_STYLE)
lst.append(Paragraph("""This demonstrates the effect of adding text strings with
newlines to a cell. It breaks where you specify, and if rowHeights is None (i.e
automatic) then you'll see the effect. See bottom left cell.""",
styNormal))
lst.append(t3)
lst.append(Spacer(18,18))
colWidths = (None, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32)
t3 = Table(data3, colWidths, None)
t3.setStyle(GRID_STYLE)
lst.append(Paragraph("""This table does not specify the size of the first column,
so should work out a sane one. In this case the element
at bottom left is a paragraph, which has no intrinsic size
(the height and width are a function of each other). So,
it tots up the extra space in the frame and divides it
between any such unsizeable columns. As a result the
table fills the width of the frame (except for the
6 point padding on either size).""",
styNormal))
lst.append(t3)
lst.append(PageBreak())
lst.append(Paragraph("""Row and Column spanning""", styleSheet['Heading1']))
lst.append(Paragraph("""This shows a very basic table. We do a faint pink grid
to show what's behind it - imagine this is not printed, as we'll overlay it later
with some black lines. We're going to "span" some cells, and have put a
value of None in the data to signify the cells we don't care about.
(In real life if you want an empty cell, put '' in it rather than None). """, styNormal))
sty = TableStyle([
#very faint grid to show what's where
('GRID', (0,0), (-1,-1), 0.25, colors.pink),
])
t = Table(self.getDataBlock(), colWidths=None, rowHeights=None, style=sty)
lst.append(t)
lst.append(Paragraph("""We now center the text for the "period"
across the four cells for each quarter. To do this we add a 'span'
command to the style to make the cell at row 1 column 3 cover 4 cells,
and a 'center' command for all cells in the top row. The spanning
is not immediately evident but trust us, it's happening - the word
'Period' is centered across the 4 columns. Note also that the
underlying grid shows through. All line drawing commands apply
to the underlying grid, so you have to take care what you put
grids through.""", styNormal))
sty = TableStyle([
#
('GRID', (0,0), (-1,-1), 0.25, colors.pink),
('ALIGN', (0,0), (-1,0), 'CENTER'),
('SPAN', (2,0), (5,0)),
])
t = Table(self.getDataBlock(), colWidths=None, rowHeights=None, style=sty)
lst.append(t)
lst.append(Paragraph("""We repeat this for the words 'Region', Product'
and 'Total', which each span the top 2 rows; and for 'Nprth' and 'South'
which span 3 rows. At the moment each cell's alignment is the default
(bottom), so these words appear to have "dropped down"; in fact they
are sitting on the bottom of their allocated ranges. You will just see that
all the 'None' values vanished, as those cells are not drawn any more.""", styNormal))
sty = TableStyle([
#
('GRID', (0,0), (-1,-1), 0.25, colors.pink),
('ALIGN', (0,0), (-1,0), 'CENTER'),
('SPAN', (2,0), (5,0)),
#span the other column heads down 2 rows
('SPAN', (0,0), (0,1)),
('SPAN', (1,0), (1,1)),
('SPAN', (6,0), (6,1)),
#span the 'north' and 'south' down 3 rows each
('SPAN', (0,2), (0,4)),
('SPAN', (0,5), (0,7)),
])
t = Table(self.getDataBlock(), colWidths=None, rowHeights=None, style=sty)
lst.append(t)
lst.append(PageBreak())
lst.append(Paragraph("""Now we'll tart things up a bit. First,
we set the vertical alignment of each spanned cell to 'middle'.
Next we add in some line drawing commands which do not slash across
the spanned cells (this needs a bit of work).
Finally we'll add some thicker lines to divide it up, and hide the pink. Voila!
""", styNormal))
sty = TableStyle([
#
# ('GRID', (0,0), (-1,-1), 0.25, colors.pink),
('TOPPADDING', (0,0), (-1,-1), 3),
#span the 'period'
('SPAN', (2,0), (5,0)),
#span the other column heads down 2 rows
('SPAN', (0,0), (0,1)),
('SPAN', (1,0), (1,1)),
('SPAN', (6,0), (6,1)),
#span the 'north' and 'south' down 3 rows each
('SPAN', (0,2), (0,4)),
('SPAN', (0,5), (0,7)),
#top row headings are centred
('ALIGN', (0,0), (-1,0), 'CENTER'),
#everything we span is vertically centred
#span the other column heads down 2 rows
('VALIGN', (0,0), (0,1), 'MIDDLE'),
('VALIGN', (1,0), (1,1), 'MIDDLE'),
('VALIGN', (6,0), (6,1), 'MIDDLE'),
#span the 'north' and 'south' down 3 rows each
('VALIGN', (0,2), (0,4), 'MIDDLE'),
('VALIGN', (0,5), (0,7), 'MIDDLE'),
#numeric stuff right aligned
('ALIGN', (2,1), (-1,-1), 'RIGHT'),
#draw lines carefully so as not to swipe through
#any of the 'spanned' cells
('GRID', (1,2), (-1,-1), 1.0, colors.black),
('BOX', (0,2), (0,4), 1.0, colors.black),
('BOX', (0,5), (0,7), 1.0, colors.black),
('BOX', (0,0), (0,1), 1.0, colors.black),
('BOX', (1,0), (1,1), 1.0, colors.black),
('BOX', (2,0), (5,0), 1.0, colors.black),
('GRID', (2,1), (5,1), 1.0, colors.black),
('BOX', (6,0), (6,1), 1.0, colors.black),
# do fatter boxes around some cells
('BOX', (0,0), (-1,1), 2.0, colors.black),
('BOX', (0,2), (-1,4), 2.0, colors.black),
('BOX', (0,5), (-1,7), 2.0, colors.black),
('BOX', (-1,0), (-1,-1), 2.0, colors.black),
])
t = Table(self.getDataBlock(), colWidths=None, rowHeights=None, style=sty)
lst.append(t)
lst.append(Paragraph("""How cells get sized""", styleSheet['Heading1']))
lst.append(Paragraph("""So far the table has been auto-sized. This can be
computationally expensive, and can lead to yucky effects. Imagine a lot of
numbers, one of which goes to 4 figures - tha numeric column will be wider.
The best approach is to specify the column
widths where you know them, and let the system do the heights. Here we set some
widths - an inch for the text columns and half an inch for the numeric ones.
""", styNormal))
t = Table(self.getDataBlock(),
colWidths=(72,72,36,36,36,36,56),
rowHeights=None,
style=sty)
lst.append(t)
lst.append(Paragraph("""The auto-sized example 2 steps back demonstrates
one advanced feature of the sizing algorithm. In the table below,
the columns for Q1-Q4 should all be the same width. We've made
the text above it a bit longer than "Period". Note that this text
is technically in the 3rd column; on our first implementation this
was sized and column 3 was therefore quite wide. To get it right,
we ensure that any cells which span columns, or which are 'overwritten'
by cells which span columns, are assigned zero width in the cell
sizing. Thus, only the string 'Q1' and the numbers below it are
calculated in estimating the width of column 3, and the phrase
"What time of year?" is not used. However, row-spanned cells are
taken into account. ALL the cells in the leftmost column
have a vertical span (or are occluded by others which do)
but it can still work out a sane width for them.
""", styNormal))
data = self.getDataBlock()
data[0][2] = "Which time of year?"
#data[7][0] = Paragraph("Let's <b>really mess things up with a <i>paragraph</i>",styNormal)
t = Table(data,
#colWidths=(72,72,36,36,36,36,56),
rowHeights=None,
style=sty)
lst.append(t)
lst.append(Paragraph("""Paragraphs and unsizeable objects in table cells.""", styleSheet['Heading1']))
lst.append(Paragraph("""Paragraphs and other flowable objects make table
sizing much harder. In general the height of a paragraph is a function
of its width so you can't ask it how wide it wants to be - and the
REALLY wide all-on-one-line solution is rarely what is wanted. We
refer to Paragraphs and their kin as "unsizeable objects". In this example
we have set the widths of all but the first column. As you can see
it uses all the available space across the page for the first column.
Note also that this fairly large cell does NOT contribute to the
height calculation for its 'row'. Under the hood it is in the
same row as the second Spam, but this row gets a height based on
its own contents and not the cell with the paragraph.
""", styNormal))
data = self.getDataBlock()
data[5][0] = Paragraph("Let's <b>really mess things up</b> with a <i>paragraph</i>, whose height is a function of the width you give it.",styNormal)
t = Table(data,
colWidths=(None,72,36,36,36,36,56),
rowHeights=None,
style=sty)
lst.append(t)
lst.append(Paragraph("""This one demonstrates that our current algorithm
does not cover all cases :-( The height of row 0 is being driven by
the width of the para, which thinks it should fit in 1 column and not 4.
To really get this right would involve multiple passes through all the cells
applying rules until everything which can be sized is sized (possibly
backtracking), applying increasingly dumb and brutal
rules on each pass.
""", styNormal))
data = self.getDataBlock()
data[0][2] = Paragraph("Let's <b>really mess things up</b> with a <i>paragraph</i>.",styNormal)
data[5][0] = Paragraph("Let's <b>really mess things up</b> with a <i>paragraph</i>, whose height is a function of the width you give it.",styNormal)
t = Table(data,
colWidths=(None,72,36,36,36,36,56),
rowHeights=None,
style=sty)
lst.append(t)
lst.append(Paragraph("""To avoid these problems remember the golden rule
of ReportLab tables: (1) fix the widths if you can, (2) don't use
a paragraph when a string will do.
""", styNormal))
lst.append(Paragraph("""Unsized columns that contain flowables without
precise widths, such as paragraphs and nested tables,
still need to try and keep their content within borders and ideally
even honor percentage requests. This can be tricky--and expensive.
But sometimes you can't follow the golden rules.
""", styNormal))
lst.append(Paragraph("""The code first calculates the minimum width
for each unsized column by iterating over every flowable in each column
and remembering the largest minimum width. It then allocates
available space to accomodate the minimum widths. Any remaining space
is divided up, treating a width of '*' as greedy, a width of None as
non-greedy, and a percentage as a weight. If a column is already
wider than its percentage warrants, it is not further expanded, and
the other widths accomodate it.
""", styNormal))
lst.append(Paragraph("""For instance, consider this tortured table.
It contains four columns, with widths of None, None, 60%, and 20%,
respectively, and a single row. The first cell contains a paragraph.
The second cell contains a table with fixed column widths that total
about 50% of the total available table width. The third cell contains
a string. The last cell contains a table with no set widths but a
single cell containing a paragraph.
""", styNormal))
ministy = TableStyle([
('GRID', (0,0), (-1,-1), 1.0, colors.black),
])
nested1 = [Paragraph(
'This is a paragraph. The column has a width of None.',
styNormal)]
nested2 = [Table(
[[Paragraph(
'This table is set to take up two and a half inches. The '
'column that holds it has a width of None.', styNormal)]],
colWidths=(180,),
rowHeights=None,
style=ministy)]
nested3 = '60% width'
nested4 = [Table(
[[[Paragraph(
"This is a table with a paragraph in it but no width set. "
"The column width in the containing table is 20%.",
styNormal)]]],
colWidths=(None,),
rowHeights=None,
style=ministy)]
t = Table([[nested1, nested2, nested3, nested4]],
colWidths=(None, None, '60%', '20%'),
rowHeights=None,
style=ministy)
lst.append(t)
lst.append(Paragraph("""Notice that the second column does expand to
account for the minimum size of its contents; and that the remaining
space goes to the third column, in an attempt to honor the '60%'
request as much as possible. This is reminiscent of the typical HTML
browser approach to tables.""", styNormal))
lst.append(Paragraph("""To get an idea of how potentially expensive
this is, consider the case of the last column: the table gets the
minimum width of every flowable of every cell in the column. In this
case one of the flowables is a table with a column without a set
width, so the nested table must itself iterate over its flowables.
The contained paragraph then calculates the width of every word in it
to see what the biggest word is, given the set font face and size. It
is easy to imagine creating a structure of this sort that took an
unacceptably large amount of time to calculate. Remember the golden
rule, if you can. """, styNormal))
lst.append(Paragraph("""This code does not yet handle spans well.""",
styNormal))
SimpleDocTemplate(outputfile('test_table_layout.pdf'), showBoundary=1).build(lst)
def makeSuite():
return makeSuiteForClasses(TableTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
print 'saved '+outputfile('test_table_layout.pdf')
printLocation()

View File

@ -0,0 +1,39 @@
"""Tests for the PythonPoint tool.
"""
import os, sys, string
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
import reportlab
class PythonPointTestCase(unittest.TestCase):
"Some very crude tests on PythonPoint."
def test0(self):
"Test if pythonpoint.pdf can be created from pythonpoint.xml."
join, dirname, isfile, abspath = os.path.join, os.path.dirname, os.path.isfile, os.path.abspath
rlDir = abspath(dirname(reportlab.__file__))
from reportlab.tools.pythonpoint import pythonpoint
from reportlab.lib.utils import isCompactDistro, open_for_read
ppDir = dirname(pythonpoint.__file__)
xml = join(ppDir, 'demos', 'pythonpoint.xml')
datafilename = 'pythonpoint.pdf'
outDir = outputfile('')
if isCompactDistro():
cwd = None
xml = open_for_read(xml)
else:
cwd = os.getcwd()
os.chdir(join(ppDir, 'demos'))
pdf = join(outDir, datafilename)
if isfile(pdf): os.remove(pdf)
pythonpoint.process(xml, outDir=outDir, verbose=0, datafilename=datafilename)
if cwd: os.chdir(cwd)
assert os.path.exists(pdf)
def makeSuite():
return makeSuiteForClasses(PythonPointTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,37 @@
#!/bin/env python
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
__version__='''$Id: test_utils.py 2619 2005-06-24 14:49:15Z rgbecker $'''
__doc__="""Test reportlab.lib.util module"""
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
class FmtTestCase(unittest.TestCase):
def testFmt(self):
from reportlab.lib.utils import FmtSelfDict
class MixedIn(FmtSelfDict):
def __init__(self):
self.a = 'AA'
self._b = '_BB'
self.d = '(overridden)'
obj = MixedIn()
self.assertEqual('blah', obj._fmt('blah'))
self.assertEqual('blah %', obj._fmt('blah %%'))
self.assertRaises(ValueError, obj._fmt, 'blah %')
self.assertEqual(
'moon AA june_BB spoon %(a)sCC ni',
obj._fmt('moon %(a)s june%(_b)s spoon %%(a)s%(c)s %(d)s', c='CC', C='boon', d='ni'))
self.assertRaises(AttributeError, obj._fmt, '%(c)s') # XXX bit weird, can this be changed?
def makeSuite():
return makeSuiteForClasses(FmtTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,138 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/test/test_widgetbase_tpc.py
"""
Tests for TypedPropertyCollection class.
"""
import os, sys, copy
from os.path import join, basename, splitext
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, printLocation
from reportlab.graphics.widgetbase import PropHolder, TypedPropertyCollection
from reportlab.lib.attrmap import AttrMap, AttrMapValue
from reportlab.lib.validators import isNumber
TPC = TypedPropertyCollection
class PH(PropHolder):
_attrMap = AttrMap(
a = AttrMapValue(isNumber),
b = AttrMapValue(isNumber)
)
class APH(PH):
def __init__(self):
self.a = 1
class BPH(APH):
def __init__(self):
APH.__init__(self)
def __getattr__(self,name):
if name=='b': return -1
raise AttributeError
class TPCTestCase(unittest.TestCase):
"Test TypedPropertyCollection class."
def test0(self):
"Test setting an invalid collective attribute."
t = TPC(PH)
try:
t.c = 42
except AttributeError:
pass
def test1(self):
"Test setting a valid collective attribute."
t = TPC(PH)
t.a = 42
assert t.a == 42
def test2(self):
"Test setting a valid collective attribute with an invalid value."
t = TPC(PH)
try:
t.a = 'fourty-two'
except AttributeError:
pass
def test3(self):
"Test setting a valid collective attribute with a convertible invalid value."
t = TPC(PH)
t.a = '42'
assert t.a == '42' # Or should it rather be an integer?
def test4(self):
"Test accessing an unset collective attribute."
t = TPC(PH)
try:
t.a
except AttributeError:
pass
def test5(self):
"Test overwriting a collective attribute in one slot."
t = TPC(PH)
t.a = 42
t[0].a = 4242
assert t[0].a == 4242
def test6(self):
"Test overwriting a one slot attribute with a collective one."
t = TPC(PH)
t[0].a = 4242
t.a = 42
assert t[0].a == 4242
def test7(self):
"Test to ensure we can handle classes with __getattr__ methods"
a=TypedPropertyCollection(APH)
b=TypedPropertyCollection(BPH)
a.a=3
b.a=4
try:
a.b
assert 1, "Shouldn't be able to see a.b"
except AttributeError:
pass
a.b=0
assert a.b==0, "Wrong value for "+str(a.b)
assert b.b==-1, "This should call __getattr__ special"
b.b=0
assert a[0].b==0
assert b[0].b==-1, "Class __getattr__ should return -1"
def makeSuite():
return makeSuiteForClasses(TPCTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,454 @@
from reportlab.test import unittest
from reportlab.test.utils import makeSuiteForClasses, outputfile, printLocation
from reportlab.lib import colors
from reportlab.graphics.shapes import Drawing, Group, Line, Rect
from reportlab.graphics.widgetbase import Widget
from reportlab.graphics.widgets.grids import *
from reportlab.graphics import renderPDF
from reportlab.graphics import renderSVG
class GridTestCase(unittest.TestCase):
"Testing diagrams containing grid widgets."
def _test0(self):
"Create color ranges."
c0, c1 = colors.Color(0, 0, 0), colors.Color(1, 1, 1)
for c in colorRange(c0, c1, 4):
print c
print
c0, c1 = colors.CMYKColor(0, 0, 0, 0), colors.CMYKColor(0, 0, 0, 1)
for c in colorRange(c0, c1, 4):
print c
print
c0, c1 = colors.PCMYKColor(0, 0, 0, 0), colors.PCMYKColor(0, 0, 0, 100)
for c in colorRange(c0, c1, 4):
print c
print
def makeDrawing0(self):
"Generate a RLG drawing with some uncommented grid samples."
D = Drawing(450, 650)
d = 80
s = 50
for row in range(10):
y = 530 - row*d
if row == 0:
for col in range(4):
x = 20 + col*d
g = Grid()
g.x = x
g.y = y
g.width = s
g.height = s
g.useRects = 0
g.useLines = 1
if col == 0:
pass
elif col == 1:
g.delta0 = 10
elif col == 2:
g.orientation = 'horizontal'
elif col == 3:
g.deltaSteps = [5, 10, 20, 30]
g.demo()
D.add(g)
elif row == 1:
for col in range(4):
x = 20 + col*d
g = Grid()
g.y = y
g.x = x
g.width = s
g.height = s
if col == 0:
pass
elif col == 1:
g.delta0 = 10
elif col == 2:
g.orientation = 'horizontal'
elif col == 3:
g.deltaSteps = [5, 10, 20, 30]
g.useRects = 1
g.useLines = 0
g.demo()
D.add(g)
elif row == 2:
for col in range(3):
x = 20 + col*d
g = Grid()
g.x = x
g.y = y
g.width = s
g.height = s
g.useLines = 1
g.useRects = 1
if col == 0:
pass
elif col == 1:
g.delta0 = 10
elif col == 2:
g.orientation = 'horizontal'
g.demo()
D.add(g)
elif row == 3:
for col in range(3):
x = 20 + col*d
sr = ShadedRect()
sr.x = x
sr.y = y
sr.width = s
sr.height = s
sr.fillColorStart = colors.Color(0, 0, 0)
sr.fillColorEnd = colors.Color(1, 1, 1)
if col == 0:
sr.numShades = 5
elif col == 1:
sr.numShades = 2
elif col == 2:
sr.numShades = 1
sr.demo()
D.add(sr)
elif row == 4:
for col in range(3):
x = 20 + col*d
sr = ShadedRect()
sr.x = x
sr.y = y
sr.width = s
sr.height = s
sr.fillColorStart = colors.red
sr.fillColorEnd = colors.blue
sr.orientation = 'horizontal'
if col == 0:
sr.numShades = 10
elif col == 1:
sr.numShades = 20
elif col == 2:
sr.numShades = 50
sr.demo()
D.add(sr)
elif row == 5:
for col in range(3):
x = 20 + col*d
sr = ShadedRect()
sr.x = x
sr.y = y
sr.width = s
sr.height = s
sr.fillColorStart = colors.white
sr.fillColorEnd = colors.green
sr.orientation = 'horizontal'
if col == 0:
sr.numShades = 10
elif col == 1:
sr.numShades = 20
sr.orientation = 'vertical'
elif col == 2:
sr.numShades = 50
sr.demo()
D.add(sr)
elif row == 6:
for col in range(3):
x = 20 + col*d
sr = ShadedRect()
sr.x = x
sr.y = y+s
sr.width = s
sr.height = -s
sr.fillColorStart = colors.white
sr.fillColorEnd = colors.green
sr.orientation = 'horizontal'
if col == 0:
sr.numShades = 10
elif col == 1:
sr.numShades = 20
sr.orientation = 'vertical'
elif col == 2:
sr.numShades = 50
sr.demo()
D.add(sr)
return D
def makeDrawing1(self):
"Generate a RLG drawing with some uncommented grid samples."
D = Drawing(450, 650)
d = 80
s = 50
for row in range(2):
y = 530 - row*d
if row == 0:
for col in range(4):
x = 20 + col*d
g = DoubleGrid()
g.x = x
g.y = y
g.width = s
g.height = s
# This should be done implicitely...
g.grid0.x = x
g.grid0.y = y
g.grid1.x = x
g.grid1.y = y
g.grid0.width = s
g.grid0.height = s
g.grid1.width = s
g.grid1.height = s
if col == 0:
pass
elif col == 1:
g.grid0.delta0 = 10
elif col == 2:
g.grid0.delta0 = 5
elif col == 3:
g.grid0.deltaSteps = [5, 10, 20, 30]
g.demo()
D.add(g)
elif row == 1:
for col in range(4):
x = 20 + col*d
g = DoubleGrid()
g.x = x
g.y = y
g.width = s
g.height = s
# This should be done implicitely...
g.grid0.x = x
g.grid0.y = y
g.grid1.x = x
g.grid1.y = y
g.grid0.width = s
g.grid0.height = s
g.grid1.width = s
g.grid1.height = s
if col == 0:
g.grid0.useRects = 0
g.grid0.useLines = 1
g.grid1.useRects = 0
g.grid1.useLines = 1
elif col == 1:
g.grid0.useRects = 1
g.grid0.useLines = 1
g.grid1.useRects = 0
g.grid1.useLines = 1
elif col == 2:
g.grid0.useRects = 1
g.grid0.useLines = 0
g.grid1.useRects = 0
g.grid1.useLines = 1
elif col == 3:
g.grid0.useRects = 1
g.grid0.useLines = 0
g.grid1.useRects = 1
g.grid1.useLines = 0
g.demo()
D.add(g)
return D
def makeDrawing2(self):
"Generate a RLG drawing with some uncommented grid samples."
D = Drawing(450, 650)
d = 80
s = 50
for row in range(10):
y = 530 - row*d
if row == 0:
for col in range(4):
x = 20 + col*d
g = Grid()
g.x = x
g.y = y
g.width = s
g.height = s
g.useRects = 0
g.useLines = 1
if col == 0:
pass
elif col == 1:
g.delta0 = 10
elif col == 2:
g.orientation = 'horizontal'
elif col == 3:
g.deltaSteps = [5, 10, 20, 30]
g.demo()
D.add(g)
elif row == 1:
for col in range(4):
x = 20 + col*d
g = Grid()
g.y = y
g.x = x
g.width = s
g.height = s
if col == 0:
pass
elif col == 1:
g.delta0 = 10
elif col == 2:
g.orientation = 'horizontal'
elif col == 3:
g.deltaSteps = [5, 10, 20, 30]
g.useRects = 1
g.useLines = 0
g.demo()
D.add(g)
elif row == 2:
for col in range(3):
x = 20 + col*d
g = Grid()
g.x = x
g.y = y
g.width = s
g.height = s
g.useLines = 1
g.useRects = 1
if col == 0:
pass
elif col == 1:
g.delta0 = 10
elif col == 2:
g.orientation = 'horizontal'
g.demo()
D.add(g)
elif row == 3:
for col in range(3):
x = 20 + col*d
sr = ShadedRect()
sr.x = x
sr.y = y
sr.width = s
sr.height = s
## sr.fillColorStart = colors.Color(0, 0, 0)
## sr.fillColorEnd = colors.Color(1, 1, 1)
sr.fillColorStart = colors.CMYKColor(0, 0, 0, 0)
sr.fillColorEnd = colors.CMYKColor(1, 1, 1, 1)
if col == 0:
sr.numShades = 5
elif col == 1:
sr.numShades = 2
elif col == 2:
sr.numShades = 1
sr.demo()
D.add(sr)
elif row == 4:
for col in range(3):
x = 20 + col*d
sr = ShadedRect()
sr.x = x
sr.y = y
sr.width = s
sr.height = s
## sr.fillColorStart = colors.red
## sr.fillColorEnd = colors.blue
sr.fillColorStart = colors.CMYKColor(1, 0, 0, 0)
sr.fillColorEnd = colors.CMYKColor(0, 0, 1, 0)
sr.orientation = 'horizontal'
if col == 0:
sr.numShades = 10
elif col == 1:
sr.numShades = 20
elif col == 2:
sr.numShades = 50
sr.demo()
D.add(sr)
elif row == 5:
for col in range(3):
x = 20 + col*d
sr = ShadedRect()
sr.x = x
sr.y = y
sr.width = s
sr.height = s
## sr.fillColorStart = colors.white
## sr.fillColorEnd = colors.green
sr.fillColorStart = colors.PCMYKColor(11.0,11.0,72.0,0.0, spotName='PANTONE 458 CV',density=1.00)
sr.fillColorEnd = colors.PCMYKColor(100.0,65.0,0.0,30.0, spotName='PANTONE 288 CV',density=1.00)
sr.orientation = 'horizontal'
if col == 0:
sr.numShades = 10
elif col == 1:
sr.numShades = 20
sr.orientation = 'vertical'
elif col == 2:
sr.numShades = 50
sr.demo()
D.add(sr)
elif row == 6:
for col in range(3):
x = 20 + col*d
sr = ShadedRect()
sr.x = x
sr.y = y+s
sr.width = s
sr.height = -s
sr.fillColorStart = colors.white
sr.fillColorEnd = colors.green
sr.orientation = 'horizontal'
if col == 0:
sr.numShades = 10
elif col == 1:
sr.numShades = 20
sr.orientation = 'vertical'
elif col == 2:
sr.numShades = 50
sr.demo()
D.add(sr)
return D
def test0(self):
"Generate PDF and SVG documents of first sample drawing."
d = self.makeDrawing0()
renderPDF.drawToFile(d, outputfile('test_widgets_grids0.pdf'))
renderSVG.drawToFile(d, outputfile('test_widgets_grids0.svg'))
def test1(self):
"Generate PDF and SVG documents of second sample drawing."
d = self.makeDrawing1()
renderPDF.drawToFile(d, outputfile('test_widgets_grids1.pdf'))
renderSVG.drawToFile(d, outputfile('test_widgets_grids1.svg'))
def test2(self):
"Generate PDF and SVG documents of third sample drawing."
d = self.makeDrawing2()
renderPDF.drawToFile(d, outputfile('test_widgets_grids2.pdf'))
renderSVG.drawToFile(d, outputfile('test_widgets_grids2.svg'))
def makeSuite():
return makeSuiteForClasses(GridTestCase)
#noruntests
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())
printLocation()

View File

@ -0,0 +1,723 @@
#!/usr/bin/env python
'''
Python unit testing framework, based on Erich Gamma's JUnit and Kent Beck's
Smalltalk testing framework.
This module contains the core framework classes that form the basis of
specific test cases and suites (TestCase, TestSuite etc.), and also a
text-based utility class for running the tests and reporting the results
(TextTestRunner).
Simple usage:
import unittest
class IntegerArithmenticTestCase(unittest.TestCase):
def testAdd(self): ## test method names begin 'test*'
self.assertEquals((1 + 2), 3)
self.assertEquals(0 + 1, 1)
def testMultiply(self):
self.assertEquals((0 * 10), 0)
self.assertEquals((5 * 8), 40)
if __name__ == '__main__':
unittest.main()
Further information is available in the bundled documentation, and from
http://pyunit.sourceforge.net/
Copyright (c) 1999, 2000, 2001 Steve Purcell
This module is free software, and you may redistribute it and/or modify
it under the same terms as Python itself, so long as this copyright message
and disclaimer are retained in their original form.
IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS,
AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
'''
__author__ = "Steve Purcell"
__email__ = "stephen_purcell at yahoo dot com"
__version__ = "#Revision: 1.43 $"[11:-2]
import time
import sys
import traceback
import string
import os
import types
##############################################################################
# Test framework core
##############################################################################
class TestResult:
"""Holder for test result information.
Test results are automatically managed by the TestCase and TestSuite
classes, and do not need to be explicitly manipulated by writers of tests.
Each instance holds the total number of tests run, and collections of
failures and errors that occurred among those test runs. The collections
contain tuples of (testcase, exceptioninfo), where exceptioninfo is the
formatted traceback of the error that occurred.
"""
def __init__(self):
self.failures = []
self.errors = []
self.testsRun = 0
self.shouldStop = 0
def startTest(self, test):
"Called when the given test is about to be run"
self.testsRun = self.testsRun + 1
def stopTest(self, test):
"Called when the given test has been run"
pass
def addError(self, test, err):
"""Called when an error has occurred. 'err' is a tuple of values as
returned by sys.exc_info().
"""
self.errors.append((test, self._exc_info_to_string(err)))
def addFailure(self, test, err):
"""Called when an error has occurred. 'err' is a tuple of values as
returned by sys.exc_info()."""
self.failures.append((test, self._exc_info_to_string(err)))
def addSuccess(self, test):
"Called when a test has completed successfully"
pass
def wasSuccessful(self):
"Tells whether or not this result was a success"
return len(self.failures) == len(self.errors) == 0
def stop(self):
"Indicates that the tests should be aborted"
self.shouldStop = 1
def _exc_info_to_string(self, err):
"""Converts a sys.exc_info()-style tuple of values into a string."""
return string.join(apply(traceback.format_exception, err), '')
def __repr__(self):
return "<%s run=%i errors=%i failures=%i>" % \
(self.__class__, self.testsRun, len(self.errors),
len(self.failures))
class TestCase:
"""A class whose instances are single test cases.
By default, the test code itself should be placed in a method named
'runTest'.
If the fixture may be used for many test cases, create as
many test methods as are needed. When instantiating such a TestCase
subclass, specify in the constructor arguments the name of the test method
that the instance is to execute.
Test authors should subclass TestCase for their own tests. Construction
and deconstruction of the test's environment ('fixture') can be
implemented by overriding the 'setUp' and 'tearDown' methods respectively.
If it is necessary to override the __init__ method, the base class
__init__ method must always be called. It is important that subclasses
should not change the signature of their __init__ method, since instances
of the classes are instantiated automatically by parts of the framework
in order to be run.
"""
# This attribute determines which exception will be raised when
# the instance's assertion methods fail; test methods raising this
# exception will be deemed to have 'failed' rather than 'errored'
failureException = AssertionError
def __init__(self, methodName='runTest'):
"""Create an instance of the class that will use the named test
method when executed. Raises a ValueError if the instance does
not have a method with the specified name.
"""
try:
self.__testMethodName = methodName
testMethod = getattr(self, methodName)
self.__testMethodDoc = testMethod.__doc__
except AttributeError:
raise ValueError, "no such test method in %s: %s" % \
(self.__class__, methodName)
def setUp(self):
"Hook method for setting up the test fixture before exercising it."
pass
def tearDown(self):
"Hook method for deconstructing the test fixture after testing it."
pass
def countTestCases(self):
return 1
def defaultTestResult(self):
return TestResult()
def shortDescription(self):
"""Returns a one-line description of the test, or None if no
description has been provided.
The default implementation of this method returns the first line of
the specified test method's docstring.
"""
doc = self.__testMethodDoc
return doc and string.strip(string.split(doc, "\n")[0]) or None
def id(self):
return "%s.%s" % (self.__class__, self.__testMethodName)
def __str__(self):
return "%s (%s)" % (self.__testMethodName, self.__class__)
def __repr__(self):
return "<%s testMethod=%s>" % \
(self.__class__, self.__testMethodName)
def run(self, result=None):
return self(result)
def __call__(self, result=None):
if result is None: result = self.defaultTestResult()
result.startTest(self)
testMethod = getattr(self, self.__testMethodName)
try:
try:
self.setUp()
except KeyboardInterrupt:
raise
except:
result.addError(self, self.__exc_info())
return
ok = 0
try:
testMethod()
ok = 1
except self.failureException, e:
result.addFailure(self, self.__exc_info())
except KeyboardInterrupt:
raise
except:
result.addError(self, self.__exc_info())
try:
self.tearDown()
except KeyboardInterrupt:
raise
except:
result.addError(self, self.__exc_info())
ok = 0
if ok: result.addSuccess(self)
finally:
result.stopTest(self)
def debug(self):
"""Run the test without collecting errors in a TestResult"""
self.setUp()
getattr(self, self.__testMethodName)()
self.tearDown()
def __exc_info(self):
"""Return a version of sys.exc_info() with the traceback frame
minimised; usually the top level of the traceback frame is not
needed.
"""
exctype, excvalue, tb = sys.exc_info()
if sys.platform[:4] == 'java': ## tracebacks look different in Jython
return (exctype, excvalue, tb)
newtb = tb.tb_next
if newtb is None:
return (exctype, excvalue, tb)
return (exctype, excvalue, newtb)
def fail(self, msg=None):
"""Fail immediately, with the given message."""
raise self.failureException, msg
def failIf(self, expr, msg=None):
"Fail the test if the expression is true."
if expr: raise self.failureException, msg
def failUnless(self, expr, msg=None):
"""Fail the test unless the expression is true."""
if not expr: raise self.failureException, msg
def failUnlessRaises(self, excClass, callableObj, *args, **kwargs):
"""Fail unless an exception of class excClass is thrown
by callableObj when invoked with arguments args and keyword
arguments kwargs. If a different type of exception is
thrown, it will not be caught, and the test case will be
deemed to have suffered an error, exactly as for an
unexpected exception.
"""
try:
apply(callableObj, args, kwargs)
except excClass:
return
else:
if hasattr(excClass,'__name__'): excName = excClass.__name__
else: excName = str(excClass)
raise self.failureException, excName
def failUnlessEqual(self, first, second, msg=None):
"""Fail if the two objects are unequal as determined by the '!='
operator.
"""
if first != second:
raise self.failureException, \
(msg or '%s != %s' % (`first`, `second`))
def failIfEqual(self, first, second, msg=None):
"""Fail if the two objects are equal as determined by the '=='
operator.
"""
if first == second:
raise self.failureException, \
(msg or '%s == %s' % (`first`, `second`))
assertEqual = assertEquals = failUnlessEqual
assertNotEqual = assertNotEquals = failIfEqual
assertRaises = failUnlessRaises
assert_ = failUnless
class TestSuite:
"""A test suite is a composite test consisting of a number of TestCases.
For use, create an instance of TestSuite, then add test case instances.
When all tests have been added, the suite can be passed to a test
runner, such as TextTestRunner. It will run the individual test cases
in the order in which they were added, aggregating the results. When
subclassing, do not forget to call the base class constructor.
"""
def __init__(self, tests=()):
self._tests = []
self.addTests(tests)
def __repr__(self):
return "<%s tests=%s>" % (self.__class__, self._tests)
__str__ = __repr__
def countTestCases(self):
cases = 0
for test in self._tests:
cases = cases + test.countTestCases()
return cases
def addTest(self, test):
self._tests.append(test)
def addTests(self, tests):
for test in tests:
self.addTest(test)
def run(self, result):
return self(result)
def __call__(self, result):
for test in self._tests:
if result.shouldStop:
break
test(result)
return result
def debug(self):
"""Run the tests without collecting errors in a TestResult"""
for test in self._tests: test.debug()
class FunctionTestCase(TestCase):
"""A test case that wraps a test function.
This is useful for slipping pre-existing test functions into the
PyUnit framework. Optionally, set-up and tidy-up functions can be
supplied. As with TestCase, the tidy-up ('tearDown') function will
always be called if the set-up ('setUp') function ran successfully.
"""
def __init__(self, testFunc, setUp=None, tearDown=None,
description=None):
TestCase.__init__(self)
self.__setUpFunc = setUp
self.__tearDownFunc = tearDown
self.__testFunc = testFunc
self.__description = description
def setUp(self):
if self.__setUpFunc is not None:
self.__setUpFunc()
def tearDown(self):
if self.__tearDownFunc is not None:
self.__tearDownFunc()
def runTest(self):
self.__testFunc()
def id(self):
return self.__testFunc.__name__
def __str__(self):
return "%s (%s)" % (self.__class__, self.__testFunc.__name__)
def __repr__(self):
return "<%s testFunc=%s>" % (self.__class__, self.__testFunc)
def shortDescription(self):
if self.__description is not None: return self.__description
doc = self.__testFunc.__doc__
return doc and string.strip(string.split(doc, "\n")[0]) or None
##############################################################################
# Locating and loading tests
##############################################################################
class TestLoader:
"""This class is responsible for loading tests according to various
criteria and returning them wrapped in a Test
"""
testMethodPrefix = 'test'
sortTestMethodsUsing = cmp
suiteClass = TestSuite
def loadTestsFromTestCase(self, testCaseClass):
"""Return a suite of all tests cases contained in testCaseClass"""
return self.suiteClass(map(testCaseClass,
self.getTestCaseNames(testCaseClass)))
def loadTestsFromModule(self, module):
"""Return a suite of all tests cases contained in the given module"""
tests = []
for name in dir(module):
obj = getattr(module, name)
if type(obj) == types.ClassType and issubclass(obj, TestCase):
tests.append(self.loadTestsFromTestCase(obj))
return self.suiteClass(tests)
def loadTestsFromName(self, name, module=None):
"""Return a suite of all tests cases given a string specifier.
The name may resolve either to a module, a test case class, a
test method within a test case class, or a callable object which
returns a TestCase or TestSuite instance.
The method optionally resolves the names relative to a given module.
"""
parts = string.split(name, '.')
if module is None:
if not parts:
raise ValueError, "incomplete test name: %s" % name
else:
parts_copy = parts[:]
while parts_copy:
try:
module = __import__(string.join(parts_copy,'.'))
break
except ImportError:
del parts_copy[-1]
if not parts_copy: raise
parts = parts[1:]
obj = module
for part in parts:
obj = getattr(obj, part)
import unittest
if type(obj) == types.ModuleType:
return self.loadTestsFromModule(obj)
elif type(obj) == types.ClassType and issubclass(obj, unittest.TestCase):
return self.loadTestsFromTestCase(obj)
elif type(obj) == types.UnboundMethodType:
return obj.im_class(obj.__name__)
elif callable(obj):
test = obj()
if not isinstance(test, unittest.TestCase) and \
not isinstance(test, unittest.TestSuite):
raise ValueError, \
"calling %s returned %s, not a test" % (obj,test)
return test
else:
raise ValueError, "don't know how to make test from: %s" % obj
def loadTestsFromNames(self, names, module=None):
"""Return a suite of all tests cases found using the given sequence
of string specifiers. See 'loadTestsFromName()'.
"""
suites = []
for name in names:
suites.append(self.loadTestsFromName(name, module))
return self.suiteClass(suites)
def getTestCaseNames(self, testCaseClass):
"""Return a sorted sequence of method names found within testCaseClass
"""
testFnNames = filter(lambda n,p=self.testMethodPrefix: n[:len(p)] == p,
dir(testCaseClass))
for baseclass in testCaseClass.__bases__:
for testFnName in self.getTestCaseNames(baseclass):
if testFnName not in testFnNames: # handle overridden methods
testFnNames.append(testFnName)
if self.sortTestMethodsUsing:
testFnNames.sort(self.sortTestMethodsUsing)
return testFnNames
defaultTestLoader = TestLoader()
##############################################################################
# Patches for old functions: these functions should be considered obsolete
##############################################################################
def _makeLoader(prefix, sortUsing, suiteClass=None):
loader = TestLoader()
loader.sortTestMethodsUsing = sortUsing
loader.testMethodPrefix = prefix
if suiteClass: loader.suiteClass = suiteClass
return loader
def getTestCaseNames(testCaseClass, prefix, sortUsing=cmp):
return _makeLoader(prefix, sortUsing).getTestCaseNames(testCaseClass)
def makeSuite(testCaseClass, prefix='test', sortUsing=cmp, suiteClass=TestSuite):
return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromTestCase(testCaseClass)
def findTestCases(module, prefix='test', sortUsing=cmp, suiteClass=TestSuite):
return _makeLoader(prefix, sortUsing, suiteClass).loadTestsFromModule(module)
##############################################################################
# Text UI
##############################################################################
class _WritelnDecorator:
"""Used to decorate file-like objects with a handy 'writeln' method"""
def __init__(self,stream):
self.stream = stream
def __getattr__(self, attr):
return getattr(self.stream,attr)
def writeln(self, *args):
if args: apply(self.write, args)
self.write('\n') # text-mode streams translate to \r\n if needed
class _TextTestResult(TestResult):
"""A test result class that can print formatted text results to a stream.
Used by TextTestRunner.
"""
separator1 = '=' * 70
separator2 = '-' * 70
def __init__(self, stream, descriptions, verbosity):
TestResult.__init__(self)
self.stream = stream
self.showAll = verbosity > 1
self.dots = verbosity == 1
self.descriptions = descriptions
def getDescription(self, test):
if self.descriptions:
return test.shortDescription() or str(test)
else:
return str(test)
def startTest(self, test):
TestResult.startTest(self, test)
if self.showAll:
self.stream.write(self.getDescription(test))
self.stream.write(" ... ")
def addSuccess(self, test):
TestResult.addSuccess(self, test)
if self.showAll:
self.stream.writeln("ok")
elif self.dots:
self.stream.write('.')
def addError(self, test, err):
TestResult.addError(self, test, err)
if self.showAll:
self.stream.writeln("ERROR")
elif self.dots:
self.stream.write('E')
def addFailure(self, test, err):
TestResult.addFailure(self, test, err)
if self.showAll:
self.stream.writeln("FAIL")
elif self.dots:
self.stream.write('F')
def printErrors(self):
if self.dots or self.showAll:
self.stream.writeln()
self.printErrorList('ERROR', self.errors)
self.printErrorList('FAIL', self.failures)
def printErrorList(self, flavour, errors):
for test, err in errors:
self.stream.writeln(self.separator1)
self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
self.stream.writeln(self.separator2)
self.stream.writeln("%s" % err)
class TextTestRunner:
"""A test runner class that displays results in textual form.
It prints out the names of tests as they are run, errors as they
occur, and a summary of the results at the end of the test run.
"""
def __init__(self, stream=sys.stderr, descriptions=1, verbosity=1):
self.stream = _WritelnDecorator(stream)
self.descriptions = descriptions
self.verbosity = verbosity
def _makeResult(self):
return _TextTestResult(self.stream, self.descriptions, self.verbosity)
def run(self, test):
"Run the given test case or test suite."
result = self._makeResult()
startTime = time.time()
test(result)
stopTime = time.time()
timeTaken = float(stopTime - startTime)
result.printErrors()
self.stream.writeln(result.separator2)
run = result.testsRun
self.stream.writeln("Ran %d test%s in %.3fs" %
(run, run != 1 and "s" or "", timeTaken))
self.stream.writeln()
if not result.wasSuccessful():
self.stream.write("FAILED (")
failed, errored = map(len, (result.failures, result.errors))
if failed:
self.stream.write("failures=%d" % failed)
if errored:
if failed: self.stream.write(", ")
self.stream.write("errors=%d" % errored)
self.stream.writeln(")")
else:
self.stream.writeln("OK")
return result
##############################################################################
# Facilities for running tests from the command line
##############################################################################
class TestProgram:
"""A command-line program that runs a set of tests; this is primarily
for making test modules conveniently executable.
"""
USAGE = """\
Usage: %(progName)s [options] [test] [...]
Options:
-h, --help Show this message
-v, --verbose Verbose output
-q, --quiet Minimal output
Examples:
%(progName)s - run default set of tests
%(progName)s MyTestSuite - run suite 'MyTestSuite'
%(progName)s MyTestCase.testSomething - run MyTestCase.testSomething
%(progName)s MyTestCase - run all 'test*' test methods
in MyTestCase
"""
def __init__(self, module='__main__', defaultTest=None,
argv=None, testRunner=None, testLoader=defaultTestLoader):
if type(module) == type(''):
self.module = __import__(module)
for part in string.split(module,'.')[1:]:
self.module = getattr(self.module, part)
else:
self.module = module
if argv is None:
argv = sys.argv
self.verbosity = 1
self.defaultTest = defaultTest
self.testRunner = testRunner
self.testLoader = testLoader
self.progName = os.path.basename(argv[0])
self.parseArgs(argv)
self.runTests()
def usageExit(self, msg=None):
if msg: print msg
print self.USAGE % self.__dict__
sys.exit(2)
def parseArgs(self, argv):
import getopt
try:
options, args = getopt.getopt(argv[1:], 'hHvq',
['help','verbose','quiet'])
for opt, value in options:
if opt in ('-h','-H','--help'):
self.usageExit()
if opt in ('-q','--quiet'):
self.verbosity = 0
if opt in ('-v','--verbose'):
self.verbosity = 2
if len(args) == 0 and self.defaultTest is None:
self.test = self.testLoader.loadTestsFromModule(self.module)
return
if len(args) > 0:
self.testNames = args
else:
self.testNames = (self.defaultTest,)
self.createTests()
except getopt.error, msg:
self.usageExit(msg)
def createTests(self):
self.test = self.testLoader.loadTestsFromNames(self.testNames,
self.module)
def runTests(self):
if self.testRunner is None:
self.testRunner = TextTestRunner(verbosity=self.verbosity)
result = self.testRunner.run(self.test)
sys.exit(not result.wasSuccessful())
main = TestProgram
##############################################################################
# Executing this module from the command line
##############################################################################
if __name__ == "__main__":
main(module=None)

308
bin/reportlab/test/utils.py Normal file
View File

@ -0,0 +1,308 @@
"""Utilities for testing Python packages.
"""
import sys, os, string, fnmatch, copy, re
from ConfigParser import ConfigParser
from reportlab.test import unittest
# Helper functions.
def isWritable(D):
try:
fn = '00DELETE.ME'
f = open(fn, 'w')
f.write('test of writability - can be deleted')
f.close()
if os.path.isfile(fn):
os.remove(fn)
return 1
except:
return 0
_TEST_DIR_IS_WRITABLE = None #not known yet
_OUTDIR = None
def canWriteTestOutputHere():
"""Is it a writable file system distro being invoked within
test directory? If so, can write test output here. If not,
it had better go in a temp directory. Only do this once per
process"""
global _TEST_DIR_IS_WRITABLE, _OUTDIR
if _TEST_DIR_IS_WRITABLE is not None:
return _TEST_DIR_IS_WRITABLE
D = [d[9:] for d in sys.argv if d.startswith('--outdir=')]
if D:
_OUTDIR = D[-1]
try:
os.makedirs(_OUTDIR)
except:
pass
map(sys.argv.remove,D)
_TEST_DIR_IS_WRITABLE = isWritable(_OUTDIR)
else:
from reportlab.lib.utils import isSourceDistro
if isSourceDistro():
curDir = os.getcwd()
parentDir = os.path.dirname(curDir)
if curDir.endswith('test') and parentDir.endswith('reportlab'):
#we're probably being run within the test directory.
#now check it's writeable.
_TEST_DIR_IS_WRITABLE = isWritable(curDir)
_OUTDIR = curDir
return _TEST_DIR_IS_WRITABLE
def outputfile(fn):
"""This works out where to write test output. If running
code in a zip file or a locked down file system, this will be a
temp directory; otherwise, the output of 'test_foo.py' will
normally be a file called 'test_foo.pdf', next door.
"""
if canWriteTestOutputHere():
D = _OUTDIR
else:
from reportlab.lib.utils import isSourceDistro, get_rl_tempdir
D = get_rl_tempdir('reportlab_test')
if fn: D = os.path.join(D,fn)
return D
def printLocation(depth=1):
if sys._getframe(depth).f_locals.get('__name__')=='__main__':
outDir = outputfile('')
if outDir!=_OUTDIR:
print 'Logs and output files written to folder "%s"' % outDir
def makeSuiteForClasses(*classes):
"Return a test suite with tests loaded from provided classes."
suite = unittest.TestSuite()
loader = unittest.TestLoader()
for C in classes:
suite.addTest(loader.loadTestsFromTestCase(C))
return suite
def getCVSEntries(folder, files=1, folders=0):
"""Returns a list of filenames as listed in the CVS/Entries file.
'folder' is the folder that should contain the CVS subfolder.
If there is no such subfolder an empty list is returned.
'files' is a boolean; 1 and 0 means to return files or not.
'folders' is a boolean; 1 and 0 means to return folders or not.
"""
join = os.path.join
split = string.split
# If CVS subfolder doesn't exist return empty list.
try:
f = open(join(folder, 'CVS', 'Entries'))
except IOError:
return []
# Return names of files and/or folders in CVS/Entries files.
allEntries = []
for line in f.readlines():
if folders and line[0] == 'D' \
or files and line[0] != 'D':
entry = split(line, '/')[1]
if entry:
allEntries.append(join(folder, entry))
return allEntries
# Still experimental class extending ConfigParser's behaviour.
class ExtConfigParser(ConfigParser):
"A slightly extended version to return lists of strings."
pat = re.compile('\s*\[.*\]\s*')
def getstringlist(self, section, option):
"Coerce option to a list of strings or return unchanged if that fails."
value = apply(ConfigParser.get, (self, section, option))
# This seems to allow for newlines inside values
# of the config file, but be careful!!
val = string.replace(value, '\n', '')
if self.pat.match(val):
return eval(val)
else:
return value
# This class as suggested by /F with an additional hook
# to be able to filter filenames.
class GlobDirectoryWalker:
"A forward iterator that traverses files in a directory tree."
def __init__(self, directory, pattern='*'):
self.index = 0
self.pattern = pattern
directory.replace('/',os.sep)
if os.path.isdir(directory):
self.stack = [directory]
self.files = []
else:
from reportlab.lib.utils import isCompactDistro, __loader__, rl_isdir
if not isCompactDistro() or not __loader__ or not rl_isdir(directory):
raise ValueError('"%s" is not a directory' % directory)
self.directory = directory[len(__loader__.archive)+len(os.sep):]
pfx = self.directory+os.sep
n = len(pfx)
self.files = map(lambda x, n=n: x[n:],filter(lambda x,pfx=pfx: x.startswith(pfx),__loader__._files.keys()))
self.stack = []
def __getitem__(self, index):
while 1:
try:
file = self.files[self.index]
self.index = self.index + 1
except IndexError:
# pop next directory from stack
self.directory = self.stack.pop()
self.files = os.listdir(self.directory)
# now call the hook
self.files = self.filterFiles(self.directory, self.files)
self.index = 0
else:
# got a filename
fullname = os.path.join(self.directory, file)
if os.path.isdir(fullname) and not os.path.islink(fullname):
self.stack.append(fullname)
if fnmatch.fnmatch(file, self.pattern):
return fullname
def filterFiles(self, folder, files):
"Filter hook, overwrite in subclasses as needed."
return files
class RestrictedGlobDirectoryWalker(GlobDirectoryWalker):
"An restricted directory tree iterator."
def __init__(self, directory, pattern='*', ignore=None):
apply(GlobDirectoryWalker.__init__, (self, directory, pattern))
if ignore == None:
ignore = []
self.ignoredPatterns = []
if type(ignore) == type([]):
for p in ignore:
self.ignoredPatterns.append(p)
elif type(ignore) == type(''):
self.ignoredPatterns.append(ignore)
def filterFiles(self, folder, files):
"Filters all items from files matching patterns to ignore."
indicesToDelete = []
for i in xrange(len(files)):
f = files[i]
for p in self.ignoredPatterns:
if fnmatch.fnmatch(f, p):
indicesToDelete.append(i)
indicesToDelete.reverse()
for i in indicesToDelete:
del files[i]
return files
class CVSGlobDirectoryWalker(GlobDirectoryWalker):
"An directory tree iterator that checks for CVS data."
def filterFiles(self, folder, files):
"""Filters files not listed in CVS subfolder.
This will look in the CVS subfolder of 'folder' for
a file named 'Entries' and filter all elements from
the 'files' list that are not listed in 'Entries'.
"""
join = os.path.join
cvsFiles = getCVSEntries(folder)
if cvsFiles:
indicesToDelete = []
for i in xrange(len(files)):
f = files[i]
if join(folder, f) not in cvsFiles:
indicesToDelete.append(i)
indicesToDelete.reverse()
for i in indicesToDelete:
del files[i]
return files
# An experimental untested base class with additional 'security'.
class SecureTestCase(unittest.TestCase):
"""Secure testing base class with additional pre- and postconditions.
We try to ensure that each test leaves the environment it has
found unchanged after the test is performed, successful or not.
Currently we restore sys.path and the working directory, but more
of this could be added easily, like removing temporary files or
similar things.
Use this as a base class replacing unittest.TestCase and call
these methods in subclassed versions before doing your own
business!
"""
def setUp(self):
"Remember sys.path and current working directory."
self._initialPath = copy.copy(sys.path)
self._initialWorkDir = os.getcwd()
def tearDown(self):
"Restore previous sys.path and working directory."
sys.path = self._initialPath
os.chdir(self._initialWorkDir)
class ScriptThatMakesFileTest(unittest.TestCase):
"""Runs a Python script at OS level, expecting it to produce a file.
It CDs to the working directory to run the script."""
def __init__(self, scriptDir, scriptName, outFileName, verbose=0):
self.scriptDir = scriptDir
self.scriptName = scriptName
self.outFileName = outFileName
self.verbose = verbose
# normally, each instance is told which method to run)
unittest.TestCase.__init__(self)
def setUp(self):
self.cwd = os.getcwd()
#change to reportlab directory first, so that
#relative paths may be given to scriptdir
import reportlab
self.rl_dir = os.path.dirname(reportlab.__file__)
self.fn = __file__
os.chdir(self.rl_dir)
os.chdir(self.scriptDir)
assert os.path.isfile(self.scriptName), "Script %s not found!" % self.scriptName
if os.path.isfile(self.outFileName):
os.remove(self.outFileName)
def tearDown(self):
os.chdir(self.cwd)
def runTest(self):
fmt = sys.platform=='win32' and '"%s" %s' or '%s %s'
p = os.popen(fmt % (sys.executable,self.scriptName),'r')
out = p.read()
if self.verbose:
print out
status = p.close()
assert os.path.isfile(self.outFileName), "File %s not created!" % self.outFileName