RL2
bzr revid: pinky-b6477822105ee9382f0078db43cf51da9cf0321a
This commit is contained in:
parent
1e479b40d6
commit
131e3f481e
|
@ -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.
|
|
@ -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 |
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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® Acrobat® 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()
|
|
@ -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 © 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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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 & 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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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 <unichar code=0xfc/>="<unichar code=0xfc/>" and this &#xfc;="ü" and this \\xc3\\xbc="\xc3\xbc". On the other hand this
|
||||
should be a pound sign &pound;="£" and this an 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()
|
|
@ -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 © 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 © 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())
|
|
@ -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()
|
|
@ -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 & BOTTOM, the blue RIGHT & TOP
|
||||
and the green CENTER & 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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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)
|
|
@ -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
|
Loading…
Reference in New Issue