bzr revid: pinky-2e44d4c158101b4eb29c9fb7cee91c9aaabfed06
This commit is contained in:
pinky 2007-01-07 23:34:45 +00:00
parent 3af6ec6aa7
commit ac5f844444
27 changed files with 6750 additions and 0 deletions

View File

@ -0,0 +1,155 @@
"""Module to analyze Python source code; for syntax coloring tools.
Interface:
tags = fontify(pytext, searchfrom, searchto)
The 'pytext' argument is a string containing Python source code.
The (optional) arguments 'searchfrom' and 'searchto' may contain a slice in pytext.
The returned value is a list of tuples, formatted like this:
[('keyword', 0, 6, None), ('keyword', 11, 17, None), ('comment', 23, 53, None), etc. ]
The tuple contents are always like this:
(tag, startindex, endindex, sublist)
tag is one of 'keyword', 'string', 'comment' or 'identifier'
sublist is not used, hence always None.
"""
# Based on FontText.py by Mitchell S. Chapman,
# which was modified by Zachary Roadhouse,
# then un-Tk'd by Just van Rossum.
# Many thanks for regular expression debugging & authoring are due to:
# Tim (the-incredib-ly y'rs) Peters and Cristian Tismer
# So, who owns the copyright? ;-) How about this:
# Copyright 1996-2001:
# Mitchell S. Chapman,
# Zachary Roadhouse,
# Tim Peters,
# Just van Rossum
__version__ = "0.4"
import string
import re
# First a little helper, since I don't like to repeat things. (Tismer speaking)
import string
def replace(where, what, with):
return string.join(string.split(where, what), with)
# This list of keywords is taken from ref/node13.html of the
# Python 1.3 HTML documentation. ("access" is intentionally omitted.)
keywordsList = [
"as", "assert", "exec",
"del", "from", "lambda", "return",
"and", "elif", "global", "not", "try",
"break", "else", "if", "or", "while",
"class", "except", "import", "pass",
"continue", "finally", "in", "print",
"def", "for", "is", "raise", "yield"]
# Build up a regular expression which will match anything
# interesting, including multi-line triple-quoted strings.
commentPat = r"#[^\n]*"
pat = r"q[^\\q\n]*(\\[\000-\377][^\\q\n]*)*q"
quotePat = replace(pat, "q", "'") + "|" + replace(pat, 'q', '"')
# Way to go, Tim!
pat = r"""
qqq
[^\\q]*
(
( \\[\000-\377]
| q
( \\[\000-\377]
| [^\q]
| q
( \\[\000-\377]
| [^\\q]
)
)
)
[^\\q]*
)*
qqq
"""
pat = string.join(string.split(pat), '') # get rid of whitespace
tripleQuotePat = replace(pat, "q", "'") + "|" + replace(pat, 'q', '"')
# Build up a regular expression which matches all and only
# Python keywords. This will let us skip the uninteresting
# identifier references.
# nonKeyPat identifies characters which may legally precede
# a keyword pattern.
nonKeyPat = r"(^|[^a-zA-Z0-9_.\"'])"
keyPat = nonKeyPat + "(" + string.join(keywordsList, "|") + ")" + nonKeyPat
matchPat = commentPat + "|" + keyPat + "|" + tripleQuotePat + "|" + quotePat
matchRE = re.compile(matchPat)
idKeyPat = "[ \t]*[A-Za-z_][A-Za-z_0-9.]*" # Ident w. leading whitespace.
idRE = re.compile(idKeyPat)
def fontify(pytext, searchfrom = 0, searchto = None):
if searchto is None:
searchto = len(pytext)
# Cache a few attributes for quicker reference.
search = matchRE.search
idSearch = idRE.search
tags = []
tags_append = tags.append
commentTag = 'comment'
stringTag = 'string'
keywordTag = 'keyword'
identifierTag = 'identifier'
start = 0
end = searchfrom
while 1:
m = search(pytext, end)
if m is None:
break # EXIT LOOP
start = m.start()
if start >= searchto:
break # EXIT LOOP
match = m.group(0)
end = start + len(match)
c = match[0]
if c not in "#'\"":
# Must have matched a keyword.
if start <> searchfrom:
# there's still a redundant char before and after it, strip!
match = match[1:-1]
start = start + 1
else:
# this is the first keyword in the text.
# Only a space at the end.
match = match[:-1]
end = end - 1
tags_append((keywordTag, start, end, None))
# If this was a defining keyword, look ahead to the
# following identifier.
if match in ["def", "class"]:
m = idSearch(pytext, end)
if m is not None:
start = m.start()
if start == end:
match = m.group(0)
end = start + len(match)
tags_append((identifierTag, start, end, None))
elif c == "#":
tags_append((commentTag, start, end, None))
else:
tags_append((stringTag, start, end, None))
return tags
def test(path):
f = open(path)
text = f.read()
f.close()
tags = fontify(text)
for tag, start, end, sublist in tags:
print tag, `text[start:end]`

View File

@ -0,0 +1,7 @@
#!/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/lib/__init__.py
__version__=''' $Id: __init__.py 2385 2004-06-17 15:26:05Z rgbecker $ '''
import os
RL_DEBUG = os.environ.has_key('RL_DEBUG')

44
bin/reportlab/lib/abag.py Normal file
View File

@ -0,0 +1,44 @@
#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/lib/abag.py
__version__=''' $Id: abag.py 2385 2004-06-17 15:26:05Z rgbecker $ '''
class ABag:
"""
'Attribute Bag' - a trivial BAG class for holding attributes.
You may initialize with keyword arguments.
a = ABag(k0=v0,....,kx=vx,....) ==> getattr(a,'kx')==vx
c = a.clone(ak0=av0,.....) copy with optional additional attributes.
"""
def __init__(self,**attr):
for k,v in attr.items():
setattr(self,k,v)
def clone(self,**attr):
n = apply(ABag,(),self.__dict__)
if attr != {}: apply(ABag.__init__,(n,),attr)
return n
def __repr__(self):
import string
n = self.__class__.__name__
L = [n+"("]
keys = self.__dict__.keys()
for k in keys:
v = getattr(self, k)
rk = repr(k)
rv = repr(v)
rk = " "+string.replace(rk, "\n", "\n ")
rv = " "+string.replace(rv, "\n", "\n ")
L.append(rk)
L.append(rv)
L.append(") #"+n)
return string.join(L, "\n")
if __name__=="__main__":
AB = ABag(a=1, c="hello")
CD = AB.clone()
print AB
print CD

View File

@ -0,0 +1,138 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/attrmap.py
__version__=''' $Id: attrmap.py 2741 2005-12-07 21:52:33Z andy $ '''
from UserDict import UserDict
from reportlab.lib.validators import isAnything, _SequenceTypes, DerivedValue
from reportlab import rl_config
class CallableValue:
'''a class to allow callable initial values'''
def __init__(self,func,*args,**kw):
#assert iscallable(func)
self.func = func
self.args = args
self.kw = kw
def __call__(self):
return apply(self.func,self.args,self.kw)
class AttrMapValue:
'''Simple multi-value holder for attribute maps'''
def __init__(self,validate=None,desc=None,initial=None, **kw):
self.validate = validate or isAnything
self.desc = desc
self._initial = initial
for k,v in kw.items():
setattr(self,k,v)
def __getattr__(self,name):
#hack to allow callable initial values
if name=='initial':
if isinstance(self._initial,CallableValue): return self._initial()
return self._initial
elif name=='hidden':
return 0
raise AttributeError, name
class AttrMap(UserDict):
def __init__(self,BASE=None,UNWANTED=[],**kw):
data = {}
if BASE:
if isinstance(BASE,AttrMap):
data = BASE.data #they used BASECLASS._attrMap
else:
if type(BASE) not in (type(()),type([])): BASE = (BASE,)
for B in BASE:
if hasattr(B,'_attrMap'):
data.update(getattr(B._attrMap,'data',{}))
else:
raise ValueError, 'BASE=%s has wrong kind of value' % str(B)
UserDict.__init__(self,data)
self.remove(UNWANTED)
self.data.update(kw)
def update(self,kw):
if isinstance(kw,AttrMap): kw = kw.data
self.data.update(kw)
def remove(self,unwanted):
for k in unwanted:
try:
del self[k]
except KeyError:
pass
def clone(self,UNWANTED=[],**kw):
c = AttrMap(BASE=self,UNWANTED=UNWANTED)
c.update(kw)
return c
def validateSetattr(obj,name,value):
'''validate setattr(obj,name,value)'''
if rl_config.shapeChecking:
map = obj._attrMap
if map and name[0]!= '_':
#we always allow the inherited values; they cannot
#be checked until draw time.
if isinstance(value, DerivedValue):
#let it through
pass
else:
try:
validate = map[name].validate
if not validate(value):
raise AttributeError, "Illegal assignment of '%s' to '%s' in class %s" % (value, name, obj.__class__.__name__)
except KeyError:
raise AttributeError, "Illegal attribute '%s' in class %s" % (name, obj.__class__.__name__)
obj.__dict__[name] = value
def _privateAttrMap(obj,ret=0):
'''clone obj._attrMap if required'''
A = obj._attrMap
oA = getattr(obj.__class__,'_attrMap',None)
if ret:
if oA is A:
return A.clone(), oA
else:
return A, None
else:
if oA is A:
obj._attrMap = A.clone()
def _findObjectAndAttr(src, P):
'''Locate the object src.P for P a string, return parent and name of attribute
'''
P = string.split(P, '.')
if len(P) == 0:
return None, None
else:
for p in P[0:-1]:
src = getattr(src, p)
return src, P[-1]
def hook__setattr__(obj):
if not hasattr(obj,'__attrproxy__'):
C = obj.__class__
import new
obj.__class__=new.classobj(C.__name__,(C,)+C.__bases__,
{'__attrproxy__':[],
'__setattr__':lambda self,k,v,osa=getattr(obj,'__setattr__',None),hook=hook: hook(self,k,v,osa)})
def addProxyAttribute(src,name,validate=None,desc=None,initial=None,dst=None):
'''
Add a proxy attribute 'name' to src with targets dst
'''
#sanity
assert hasattr(src,'_attrMap'), 'src object has no _attrMap'
A, oA = _privateAttrMap(src,1)
if type(dst) not in _SequenceTypes: dst = dst,
D = []
DV = []
for d in dst:
if type(d) in _SequenceTypes:
d, e = d[0], d[1:]
obj, attr = _findObjectAndAttr(src,d)
if obj:
dA = getattr(obj,'_attrMap',None)

View File

@ -0,0 +1,365 @@
#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/lib/codecharts.py
#$Header $
__version__=''' $Id '''
__doc__="""Routines to print code page (character set) drawings.
To be sure we can accurately represent characters in various encodings
and fonts, we need some routines to display all those characters.
These are defined herein. The idea is to include flowable, drawable
and graphic objects for single and multi-byte fonts. """
import string
import codecs
from reportlab.pdfgen.canvas import Canvas
from reportlab.platypus import Flowable
from reportlab.pdfbase import pdfmetrics, cidfonts
from reportlab.graphics.shapes import Drawing, Group, String, Circle, Rect
from reportlab.graphics.widgetbase import Widget
from reportlab.lib import colors
adobe2codec = {
'WinAnsiEncoding':'winansi',
'MacRomanEncoding':'macroman',
'MacExpert':'macexpert',
'PDFDoc':'pdfdoc',
}
class CodeChartBase(Flowable):
"""Basic bits of drawing furniture used by
single and multi-byte versions: ability to put letters
into boxes."""
def calcLayout(self):
"Work out x and y positions for drawing"
rows = self.codePoints * 1.0 / self.charsPerRow
if rows == int(rows):
self.rows = int(rows)
else:
self.rows = int(rows) + 1
# size allows for a gray column of labels
self.width = self.boxSize * (1+self.charsPerRow)
self.height = self.boxSize * (1+self.rows)
#handy lists
self.ylist = []
for row in range(self.rows + 2):
self.ylist.append(row * self.boxSize)
self.xlist = []
for col in range(self.charsPerRow + 2):
self.xlist.append(col * self.boxSize)
def formatByte(self, byt):
if self.hex:
return '%02X' % byt
else:
return '%d' % byt
def drawChars(self, charList):
"""Fills boxes in order. None means skip a box.
Empty boxes at end get filled with gray"""
extraNeeded = (self.rows * self.charsPerRow - len(charList))
for i in range(extraNeeded):
charList.append(None)
#charList.extend([None] * extraNeeded)
row = 0
col = 0
self.canv.setFont(self.fontName, self.boxSize * 0.75)
for ch in charList: # may be 2 bytes or 1
if ch is None:
self.canv.setFillGray(0.9)
self.canv.rect((1+col) * self.boxSize, (self.rows - row - 1) * self.boxSize,
self.boxSize, self.boxSize, stroke=0, fill=1)
self.canv.setFillGray(0.0)
else:
try:
self.canv.drawCentredString(
(col+1.5) * self.boxSize,
(self.rows - row - 0.875) * self.boxSize,
ch,
)
except:
self.canv.setFillGray(0.9)
self.canv.rect((1+col) * self.boxSize, (self.rows - row - 1) * self.boxSize,
self.boxSize, self.boxSize, stroke=0, fill=1)
self.canv.drawCentredString(
(col+1.5) * self.boxSize,
(self.rows - row - 0.875) * self.boxSize,
'?',
)
self.canv.setFillGray(0.0)
col = col + 1
if col == self.charsPerRow:
row = row + 1
col = 0
def drawLabels(self, topLeft = ''):
"""Writes little labels in the top row and first column"""
self.canv.setFillGray(0.8)
self.canv.rect(0, self.ylist[-2], self.width, self.boxSize, fill=1, stroke=0)
self.canv.rect(0, 0, self.boxSize, self.ylist[-2], fill=1, stroke=0)
self.canv.setFillGray(0.0)
#label each row and column
self.canv.setFont('Helvetica-Oblique',0.375 * self.boxSize)
byt = 0
for row in range(self.rows):
if self.rowLabels:
label = self.rowLabels[row]
else: # format start bytes as hex or decimal
label = self.formatByte(row * self.charsPerRow)
self.canv.drawCentredString(0.5 * self.boxSize,
(self.rows - row - 0.75) * self.boxSize,
label
)
for col in range(self.charsPerRow):
self.canv.drawCentredString((col + 1.5) * self.boxSize,
(self.rows + 0.25) * self.boxSize,
self.formatByte(col)
)
if topLeft:
self.canv.setFont('Helvetica-BoldOblique',0.5 * self.boxSize)
self.canv.drawCentredString(0.5 * self.boxSize,
(self.rows + 0.25) * self.boxSize,
topLeft
)
class SingleByteEncodingChart(CodeChartBase):
def __init__(self, faceName='Helvetica', encodingName='WinAnsiEncoding',
charsPerRow=16, boxSize=14, hex=1):
self.codePoints = 256
self.faceName = faceName
self.encodingName = encodingName
self.fontName = self.faceName + '-' + self.encodingName
self.charsPerRow = charsPerRow
self.boxSize = boxSize
self.hex = hex
self.rowLabels = None
pdfmetrics.registerFont(pdfmetrics.Font(self.fontName,
self.faceName,
self.encodingName)
)
self.calcLayout()
def draw(self):
self.drawLabels()
charList = [None] * 32 + map(chr, range(32, 256))
#we need to convert these to Unicode, since ReportLab
#2.0 can only draw in Unicode.
encName = self.encodingName
#apply some common translations
encName = adobe2codec.get(encName, encName)
decoder = codecs.lookup(encName)[1]
def decodeFunc(txt):
if txt is None:
return None
else:
return decoder(txt, errors='replace')[0]
charList = [decodeFunc(ch) for ch in charList]
self.drawChars(charList)
self.canv.grid(self.xlist, self.ylist)
class KutenRowCodeChart(CodeChartBase):
"""Formats one 'row' of the 94x94 space used in many Asian encodings.aliases
These deliberately resemble the code charts in Ken Lunde's "Understanding
CJKV Information Processing", to enable manual checking. Due to the large
numbers of characters, we don't try to make one graphic with 10,000 characters,
but rather output a sequence of these."""
#would be cleaner if both shared one base class whose job
#was to draw the boxes, but never mind...
def __init__(self, row, faceName, encodingName):
self.row = row
self.codePoints = 94
self.boxSize = 18
self.charsPerRow = 20
self.rows = 5
self.rowLabels = ['00','20','40','60','80']
self.hex = 0
self.faceName = faceName
self.encodingName = encodingName
try:
# the dependent files might not be available
font = cidfonts.CIDFont(self.faceName, self.encodingName)
pdfmetrics.registerFont(font)
except:
# fall back to English and at least shwo we can draw the boxes
self.faceName = 'Helvetica'
self.encodingName = 'WinAnsiEncoding'
self.fontName = self.faceName + '-' + self.encodingName
self.calcLayout()
def makeRow(self, row):
"""Works out the character values for this kuten row"""
cells = []
if string.find(self.encodingName, 'EUC') > -1:
# it is an EUC family encoding.
for col in range(1, 95):
ch = chr(row + 160) + chr(col+160)
cells.append(ch)
## elif string.find(self.encodingName, 'GB') > -1:
## # it is an EUC family encoding.
## for col in range(1, 95):
## ch = chr(row + 160) + chr(col+160)
else:
cells.append([None] * 94)
return cells
def draw(self):
self.drawLabels(topLeft= 'R%d' % self.row)
# work out which characters we need for the row
#assert string.find(self.encodingName, 'EUC') > -1, 'Only handles EUC encoding today, you gave me %s!' % self.encodingName
# pad out by 1 to match Ken Lunde's tables
charList = [None] + self.makeRow(self.row)
self.drawChars(charList)
self.canv.grid(self.xlist, self.ylist)
class Big5CodeChart(CodeChartBase):
"""Formats one 'row' of the 94x160 space used in Big 5
These deliberately resemble the code charts in Ken Lunde's "Understanding
CJKV Information Processing", to enable manual checking."""
def __init__(self, row, faceName, encodingName):
self.row = row
self.codePoints = 160
self.boxSize = 18
self.charsPerRow = 16
self.rows = 10
self.hex = 1
self.faceName = faceName
self.encodingName = encodingName
self.rowLabels = ['4','5','6','7','A','B','C','D','E','F']
try:
# the dependent files might not be available
font = cidfonts.CIDFont(self.faceName, self.encodingName)
pdfmetrics.registerFont(font)
except:
# fall back to English and at least shwo we can draw the boxes
self.faceName = 'Helvetica'
self.encodingName = 'WinAnsiEncoding'
self.fontName = self.faceName + '-' + self.encodingName
self.calcLayout()
def makeRow(self, row):
"""Works out the character values for this Big5 row.
Rows start at 0xA1"""
cells = []
if string.find(self.encodingName, 'B5') > -1:
# big 5, different row size
for y in [4,5,6,7,10,11,12,13,14,15]:
for x in range(16):
col = y*16+x
ch = chr(row) + chr(col)
cells.append(ch)
else:
cells.append([None] * 160)
return cells
def draw(self):
self.drawLabels(topLeft='%02X' % self.row)
charList = self.makeRow(self.row)
self.drawChars(charList)
self.canv.grid(self.xlist, self.ylist)
def hBoxText(msg, canvas, x, y, fontName):
"""Helper for stringwidth tests on Asian fonts.
Registers font if needed. Then draws the string,
and a box around it derived from the stringWidth function"""
canvas.saveState()
try:
font = pdfmetrics.getFont(fontName)
except KeyError:
font = cidfonts.UnicodeCIDFont(fontName)
pdfmetrics.registerFont(font)
canvas.setFillGray(0.8)
canvas.rect(x,y,pdfmetrics.stringWidth(msg, fontName, 16),16,stroke=0,fill=1)
canvas.setFillGray(0)
canvas.setFont(fontName, 16,16)
canvas.drawString(x,y,msg)
canvas.restoreState()
class CodeWidget(Widget):
"""Block showing all the characters"""
def __init__(self):
self.x = 0
self.y = 0
self.width = 160
self.height = 160
def draw(self):
dx = self.width / 16.0
dy = self.height / 16.0
g = Group()
g.add(Rect(self.x, self.y, self.width, self.height,
fillColor=None, strokeColor=colors.black))
for x in range(16):
for y in range(16):
charValue = y * 16 + x
if charValue > 32:
s = String(self.x + x * dx,
self.y + (self.height - y*dy), chr(charValue))
g.add(s)
return g
def test():
c = Canvas('codecharts.pdf')
c.setFont('Helvetica-Bold', 24)
c.drawString(72, 750, 'Testing code page charts')
cc1 = SingleByteEncodingChart()
cc1.drawOn(c, 72, 500)
cc2 = SingleByteEncodingChart(charsPerRow=32)
cc2.drawOn(c, 72, 300)
cc3 = SingleByteEncodingChart(charsPerRow=25, hex=0)
cc3.drawOn(c, 72, 100)
## c.showPage()
##
## c.setFont('Helvetica-Bold', 24)
## c.drawString(72, 750, 'Multi-byte Kuten code chart examples')
## KutenRowCodeChart(1, 'HeiseiMin-W3','EUC-H').drawOn(c, 72, 600)
## KutenRowCodeChart(16, 'HeiseiMin-W3','EUC-H').drawOn(c, 72, 450)
## KutenRowCodeChart(84, 'HeiseiMin-W3','EUC-H').drawOn(c, 72, 300)
##
## c.showPage()
## c.setFont('Helvetica-Bold', 24)
## c.drawString(72, 750, 'Big5 Code Chart Examples')
## #Big5CodeChart(0xA1, 'MSungStd-Light-Acro','ETenms-B5-H').drawOn(c, 72, 500)
c.save()
print 'saved codecharts.pdf'
if __name__=='__main__':
test()

565
bin/reportlab/lib/colors.py Normal file
View File

@ -0,0 +1,565 @@
#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/lib/colors.py
__version__=''' $Id: colors.py 2587 2005-05-03 11:41:54Z rgbecker $ '''
import string, math
from types import StringType, ListType, TupleType
from reportlab.lib.utils import fp_str
_SeqTypes = (ListType,TupleType)
class Color:
"""This class is used to represent color. Components red, green, blue
are in the range 0 (dark) to 1 (full intensity)."""
def __init__(self, red=0, green=0, blue=0):
"Initialize with red, green, blue in range [0-1]."
self.red, self.green, self.blue = red,green,blue
def __repr__(self):
return "Color(%s)" % string.replace(fp_str(self.red, self.green, self.blue),' ',',')
def __hash__(self):
return hash( (self.red, self.green, self.blue) )
def __cmp__(self,other):
try:
dsum = 4*self.red-4*other.red + 2*self.green-2*other.green + self.blue-other.blue
except:
return -1
if dsum > 0: return 1
if dsum < 0: return -1
return 0
def rgb(self):
"Returns a three-tuple of components"
return (self.red, self.green, self.blue)
def bitmap_rgb(self):
return tuple(map(lambda x: int(x*255)&255, self.rgb()))
def hexval(self):
return '0x%02x%02x%02x' % self.bitmap_rgb()
class CMYKColor(Color):
"""This represents colors using the CMYK (cyan, magenta, yellow, black)
model commonly used in professional printing. This is implemented
as a derived class so that renderers which only know about RGB "see it"
as an RGB color through its 'red','green' and 'blue' attributes, according
to an approximate function.
The RGB approximation is worked out when the object in constructed, so
the color attributes should not be changed afterwards.
Extra attributes may be attached to the class to support specific ink models,
and renderers may look for these."""
def __init__(self, cyan=0, magenta=0, yellow=0, black=0,
spotName=None, density=1, knockout=None):
"""
Initialize with four colors in range [0-1]. the optional
spotName, density & knockout may be of use to specific renderers.
spotName is intended for use as an identifier to the renderer not client programs.
density is used to modify the overall amount of ink.
knockout is a renderer dependent option that determines whether the applied colour
knocksout (removes) existing colour; None means use the global default.
"""
self.cyan = cyan
self.magenta = magenta
self.yellow = yellow
self.black = black
self.spotName = spotName
self.density = max(min(density,1),0) # force into right range
self.knockout = knockout
# now work out the RGB approximation. override
self.red, self.green, self.blue = cmyk2rgb( (cyan, magenta, yellow, black) )
if density<1:
#density adjustment of rgb approximants, effectively mix with white
r, g, b = self.red, self.green, self.blue
r = density*(r-1)+1
g = density*(g-1)+1
b = density*(b-1)+1
self.red, self.green, self.blue = (r,g,b)
def __repr__(self):
return "CMYKColor(%s%s%s%s)" % (
string.replace(fp_str(self.cyan, self.magenta, self.yellow, self.black),' ',','),
(self.spotName and (',spotName='+repr(self.spotName)) or ''),
(self.density!=1 and (',density='+fp_str(self.density)) or ''),
(self.knockout is not None and (',knockout=%d' % self.knockout) or ''),
)
def __hash__(self):
return hash( (self.cyan, self.magenta, self.yellow, self.black, self.density, self.spotName) )
def __cmp__(self,other):
"""Partial ordering of colors according to a notion of distance.
Comparing across the two color models is of limited use."""
# why the try-except? What can go wrong?
if isinstance(other, CMYKColor):
dsum = (((( (self.cyan-other.cyan)*2 +
(self.magenta-other.magenta))*2+
(self.yellow-other.yellow))*2+
(self.black-other.black))*2+
(self.density-other.density))*2 + cmp(self.spotName or '',other.spotName or '')
else: # do the RGB comparison
try:
dsum = ((self.red-other.red)*2+(self.green-other.green))*2+(self.blue-other.blue)
except: # or just return 'not equal' if not a color
return -1
if dsum >= 0:
return dsum>0
else:
return -1
def cmyk(self):
"Returns a tuple of four color components - syntactic sugar"
return (self.cyan, self.magenta, self.yellow, self.black)
def _density_str(self):
return fp_str(self.density)
class PCMYKColor(CMYKColor):
'''100 based CMYKColor with density and a spotName; just like Rimas uses'''
def __init__(self,cyan,magenta,yellow,black,density=100,spotName=None,knockout=None):
CMYKColor.__init__(self,cyan/100.,magenta/100.,yellow/100.,black/100.,spotName,density/100.,knockout=knockout)
def __repr__(self):
return "PCMYKColor(%s%s%s%s)" % (
string.replace(fp_str(self.cyan*100, self.magenta*100, self.yellow*100, self.black*100),' ',','),
(self.spotName and (',spotName='+repr(self.spotName)) or ''),
(self.density!=1 and (',density='+fp_str(self.density*100)) or ''),
(self.knockout is not None and (',knockout=%d' % self.knockout) or ''),
)
def cmyk2rgb((c,m,y,k),density=1):
"Convert from a CMYK color tuple to an RGB color tuple"
# From the Adobe Postscript Ref. Manual 2nd ed.
r = 1.0 - min(1.0, c + k)
g = 1.0 - min(1.0, m + k)
b = 1.0 - min(1.0, y + k)
return (r,g,b)
def rgb2cmyk(r,g,b):
'''one way to get cmyk from rgb'''
c = 1 - r
m = 1 - g
y = 1 - b
k = min(c,m,y)
c = min(1,max(0,c-k))
m = min(1,max(0,m-k))
y = min(1,max(0,y-k))
k = min(1,max(0,k))
return (c,m,y,k)
def color2bw(colorRGB):
"Transform an RGB color to a black and white equivalent."
col = colorRGB
r, g, b = col.red, col.green, col.blue
n = (r + g + b) / 3.0
bwColorRGB = Color(n, n, n)
return bwColorRGB
def HexColor(val):
"""This function converts a hex string, or an actual integer number,
into the corresponding color. E.g., in "AABBCC" or 0xAABBCC,
AA is the red, BB is the green, and CC is the blue (00-FF).
HTML uses a hex string with a preceding hash; if this is present,
it is stripped off. (AR, 3-3-2000)
For completeness I assume that #aabbcc or 0xaabbcc are hex numbers
otherwise a pure integer is converted as decimal rgb
"""
if type(val) == StringType:
b = 10
if val[:1] == '#':
val = val[1:]
b = 16
elif string.lower(val[:2]) == '0x':
b = 16
val = val[2:]
val = string.atoi(val,b)
return Color(((val>>16)&0xFF)/255.0,((val>>8)&0xFF)/255.0,(val&0xFF)/255.0)
def linearlyInterpolatedColor(c0, c1, x0, x1, x):
"""
Linearly interpolates colors. Can handle RGB, CMYK and PCMYK
colors - give ValueError if colours aren't the same.
Doesn't currently handle 'Spot Color Interpolation'.
"""
if c0.__class__ != c1.__class__:
raise ValueError, "Color classes must be the same for interpolation!"
if x1<x0:
x0,x1,c0,c1 = x1,x0,c1,c0 # normalized so x1>x0
if x<x0-1e-8 or x>x1+1e-8: # fudge factor for numerical problems
raise ValueError, "Can't interpolate: x=%f is not between %f and %f!" % (x,x0,x1)
if x<=x0:
return c0
elif x>=x1:
return c1
cname = c0.__class__.__name__
dx = float(x1-x0)
x = x-x0
if cname == 'Color': # RGB
r = c0.red+x*(c1.red - c0.red)/dx
g = c0.green+x*(c1.green- c0.green)/dx
b = c0.blue+x*(c1.blue - c0.blue)/dx
return Color(r,g,b)
elif cname == 'CMYKColor':
c = c0.cyan+x*(c1.cyan - c0.cyan)/dx
m = c0.magenta+x*(c1.magenta - c0.magenta)/dx
y = c0.yellow+x*(c1.yellow - c0.yellow)/dx
k = c0.black+x*(c1.black - c0.black)/dx
d = c0.density+x*(c1.density - c0.density)/dx
return CMYKColor(c,m,y,k, density=d)
elif cname == 'PCMYKColor':
if cmykDistance(c0,c1)<1e-8:
#colors same do density and preserve spotName if any
assert c0.spotName == c1.spotName, "Identical cmyk, but different spotName"
c = c0.cyan
m = c0.magenta
y = c0.yellow
k = c0.black
d = c0.density+x*(c1.density - c0.density)/dx
return PCMYKColor(c*100,m*100,y*100,k*100, density=d*100, spotName=c0.spotName)
elif cmykDistance(c0,_CMYK_white)<1e-8:
#special c0 is white
c = c1.cyan
m = c1.magenta
y = c1.yellow
k = c1.black
d = x*c1.density/dx
return PCMYKColor(c*100,m*100,y*100,k*100, density=d*100, spotName=c1.spotName)
elif cmykDistance(c1,_CMYK_white)<1e-8:
#special c1 is white
c = c0.cyan
m = c0.magenta
y = c0.yellow
k = c0.black
d = x*c0.density/dx
d = c0.density*(1-x/dx)
return PCMYKColor(c*100,m*100,y*100,k*100, density=d*100, spotName=c0.spotName)
else:
c = c0.cyan+x*(c1.cyan - c0.cyan)/dx
m = c0.magenta+x*(c1.magenta - c0.magenta)/dx
y = c0.yellow+x*(c1.yellow - c0.yellow)/dx
k = c0.black+x*(c1.black - c0.black)/dx
d = c0.density+x*(c1.density - c0.density)/dx
return PCMYKColor(c*100,m*100,y*100,k*100, density=d*100)
else:
raise ValueError, "Can't interpolate: Unknown color class %s!" % cname
# special case -- indicates no drawing should be done
# this is a hangover from PIDDLE - suggest we ditch it since it is not used anywhere
#transparent = Color(-1, -1, -1)
_CMYK_white=CMYKColor(0,0,0,0)
_PCMYK_white=PCMYKColor(0,0,0,0)
_CMYK_black=CMYKColor(0,0,0,1)
_PCMYK_black=PCMYKColor(0,0,0,100)
# Special colors
ReportLabBlueOLD = HexColor(0x4e5688)
ReportLabBlue = HexColor(0x00337f)
ReportLabBluePCMYK = PCMYKColor(100,65,0,30,spotName='Pantone 288U')
ReportLabLightBlue = HexColor(0xb7b9d3)
ReportLabFidBlue=HexColor(0x3366cc)
ReportLabFidRed=HexColor(0xcc0033)
ReportLabGreen = HexColor(0x336600)
ReportLabLightGreen = HexColor(0x339933)
# color constants -- mostly from HTML standard
aliceblue = HexColor(0xF0F8FF)
antiquewhite = HexColor(0xFAEBD7)
aqua = HexColor(0x00FFFF)
aquamarine = HexColor(0x7FFFD4)
azure = HexColor(0xF0FFFF)
beige = HexColor(0xF5F5DC)
bisque = HexColor(0xFFE4C4)
black = HexColor(0x000000)
blanchedalmond = HexColor(0xFFEBCD)
blue = HexColor(0x0000FF)
blueviolet = HexColor(0x8A2BE2)
brown = HexColor(0xA52A2A)
burlywood = HexColor(0xDEB887)
cadetblue = HexColor(0x5F9EA0)
chartreuse = HexColor(0x7FFF00)
chocolate = HexColor(0xD2691E)
coral = HexColor(0xFF7F50)
cornflowerblue = cornflower = HexColor(0x6495ED)
cornsilk = HexColor(0xFFF8DC)
crimson = HexColor(0xDC143C)
cyan = HexColor(0x00FFFF)
darkblue = HexColor(0x00008B)
darkcyan = HexColor(0x008B8B)
darkgoldenrod = HexColor(0xB8860B)
darkgray = HexColor(0xA9A9A9)
darkgreen = HexColor(0x006400)
darkkhaki = HexColor(0xBDB76B)
darkmagenta = HexColor(0x8B008B)
darkolivegreen = HexColor(0x556B2F)
darkorange = HexColor(0xFF8C00)
darkorchid = HexColor(0x9932CC)
darkred = HexColor(0x8B0000)
darksalmon = HexColor(0xE9967A)
darkseagreen = HexColor(0x8FBC8B)
darkslateblue = HexColor(0x483D8B)
darkslategray = HexColor(0x2F4F4F)
darkturquoise = HexColor(0x00CED1)
darkviolet = HexColor(0x9400D3)
deeppink = HexColor(0xFF1493)
deepskyblue = HexColor(0x00BFFF)
dimgray = HexColor(0x696969)
dodgerblue = HexColor(0x1E90FF)
firebrick = HexColor(0xB22222)
floralwhite = HexColor(0xFFFAF0)
forestgreen = HexColor(0x228B22)
fuchsia = HexColor(0xFF00FF)
gainsboro = HexColor(0xDCDCDC)
ghostwhite = HexColor(0xF8F8FF)
gold = HexColor(0xFFD700)
goldenrod = HexColor(0xDAA520)
gray = HexColor(0x808080)
grey = gray
green = HexColor(0x008000)
greenyellow = HexColor(0xADFF2F)
honeydew = HexColor(0xF0FFF0)
hotpink = HexColor(0xFF69B4)
indianred = HexColor(0xCD5C5C)
indigo = HexColor(0x4B0082)
ivory = HexColor(0xFFFFF0)
khaki = HexColor(0xF0E68C)
lavender = HexColor(0xE6E6FA)
lavenderblush = HexColor(0xFFF0F5)
lawngreen = HexColor(0x7CFC00)
lemonchiffon = HexColor(0xFFFACD)
lightblue = HexColor(0xADD8E6)
lightcoral = HexColor(0xF08080)
lightcyan = HexColor(0xE0FFFF)
lightgoldenrodyellow = HexColor(0xFAFAD2)
lightgreen = HexColor(0x90EE90)
lightgrey = HexColor(0xD3D3D3)
lightpink = HexColor(0xFFB6C1)
lightsalmon = HexColor(0xFFA07A)
lightseagreen = HexColor(0x20B2AA)
lightskyblue = HexColor(0x87CEFA)
lightslategray = HexColor(0x778899)
lightsteelblue = HexColor(0xB0C4DE)
lightyellow = HexColor(0xFFFFE0)
lime = HexColor(0x00FF00)
limegreen = HexColor(0x32CD32)
linen = HexColor(0xFAF0E6)
magenta = HexColor(0xFF00FF)
maroon = HexColor(0x800000)
mediumaquamarine = HexColor(0x66CDAA)
mediumblue = HexColor(0x0000CD)
mediumorchid = HexColor(0xBA55D3)
mediumpurple = HexColor(0x9370DB)
mediumseagreen = HexColor(0x3CB371)
mediumslateblue = HexColor(0x7B68EE)
mediumspringgreen = HexColor(0x00FA9A)
mediumturquoise = HexColor(0x48D1CC)
mediumvioletred = HexColor(0xC71585)
midnightblue = HexColor(0x191970)
mintcream = HexColor(0xF5FFFA)
mistyrose = HexColor(0xFFE4E1)
moccasin = HexColor(0xFFE4B5)
navajowhite = HexColor(0xFFDEAD)
navy = HexColor(0x000080)
oldlace = HexColor(0xFDF5E6)
olive = HexColor(0x808000)
olivedrab = HexColor(0x6B8E23)
orange = HexColor(0xFFA500)
orangered = HexColor(0xFF4500)
orchid = HexColor(0xDA70D6)
palegoldenrod = HexColor(0xEEE8AA)
palegreen = HexColor(0x98FB98)
paleturquoise = HexColor(0xAFEEEE)
palevioletred = HexColor(0xDB7093)
papayawhip = HexColor(0xFFEFD5)
peachpuff = HexColor(0xFFDAB9)
peru = HexColor(0xCD853F)
pink = HexColor(0xFFC0CB)
plum = HexColor(0xDDA0DD)
powderblue = HexColor(0xB0E0E6)
purple = HexColor(0x800080)
red = HexColor(0xFF0000)
rosybrown = HexColor(0xBC8F8F)
royalblue = HexColor(0x4169E1)
saddlebrown = HexColor(0x8B4513)
salmon = HexColor(0xFA8072)
sandybrown = HexColor(0xF4A460)
seagreen = HexColor(0x2E8B57)
seashell = HexColor(0xFFF5EE)
sienna = HexColor(0xA0522D)
silver = HexColor(0xC0C0C0)
skyblue = HexColor(0x87CEEB)
slateblue = HexColor(0x6A5ACD)
slategray = HexColor(0x708090)
snow = HexColor(0xFFFAFA)
springgreen = HexColor(0x00FF7F)
steelblue = HexColor(0x4682B4)
tan = HexColor(0xD2B48C)
teal = HexColor(0x008080)
thistle = HexColor(0xD8BFD8)
tomato = HexColor(0xFF6347)
turquoise = HexColor(0x40E0D0)
violet = HexColor(0xEE82EE)
wheat = HexColor(0xF5DEB3)
white = HexColor(0xFFFFFF)
whitesmoke = HexColor(0xF5F5F5)
yellow = HexColor(0xFFFF00)
yellowgreen = HexColor(0x9ACD32)
fidblue=HexColor(0x3366cc)
fidred=HexColor(0xcc0033)
fidlightblue=HexColor("#d6e0f5")
ColorType=type(black)
################################################################
#
# Helper functions for dealing with colors. These tell you
# which are predefined, so you can print color charts;
# and can give the nearest match to an arbitrary color object
#
#################################################################
def colorDistance(col1, col2):
"""Returns a number between 0 and root(3) stating how similar
two colours are - distance in r,g,b, space. Only used to find
names for things."""
return math.sqrt(
(col1.red - col2.red)**2 +
(col1.green - col2.green)**2 +
(col1.blue - col2.blue)**2
)
def cmykDistance(col1, col2):
"""Returns a number between 0 and root(4) stating how similar
two colours are - distance in r,g,b, space. Only used to find
names for things."""
return math.sqrt(
(col1.cyan - col2.cyan)**2 +
(col1.magenta - col2.magenta)**2 +
(col1.yellow - col2.yellow)**2 +
(col1.black - col2.black)**2
)
_namedColors = None
def getAllNamedColors():
#returns a dictionary of all the named ones in the module
# uses a singleton for efficiency
global _namedColors
if _namedColors is not None: return _namedColors
import colors
_namedColors = {}
for (name, value) in colors.__dict__.items():
if isinstance(value, Color):
_namedColors[name] = value
return _namedColors
def describe(aColor,mode=0):
'''finds nearest colour match to aColor.
mode=0 print a string desription
mode=1 return a string description
mode=2 return (distance, colorName)
'''
namedColors = getAllNamedColors()
closest = (10, None, None) #big number, name, color
for (name, color) in namedColors.items():
distance = colorDistance(aColor, color)
if distance < closest[0]:
closest = (distance, name, color)
if mode<=1:
s = 'best match is %s, distance %0.4f' % (closest[1], closest[0])
if mode==0: print s
else: return s
elif mode==2:
return (closest[1], closest[0])
else:
raise ValueError, "Illegal value for mode "+str(mode)
def toColor(arg,default=None):
'''try to map an arbitrary arg to a color instance'''
if isinstance(arg,Color): return arg
tArg = type(arg)
if tArg in _SeqTypes:
assert 3<=len(arg)<=4, 'Can only convert 3 and 4 sequences to color'
assert 0<=min(arg) and max(arg)<=1
return len(arg)==3 and Color(arg[0],arg[1],arg[2]) or CMYKColor(arg[0],arg[1],arg[2],arg[3])
elif tArg == StringType:
C = getAllNamedColors()
s = string.lower(arg)
if C.has_key(s): return C[s]
try:
return toColor(eval(arg))
except:
pass
try:
return HexColor(arg)
except:
if default is None:
raise 'Invalid color value', str(arg)
return default
def toColorOrNone(arg,default=None):
'''as above but allows None as a legal value'''
if arg is None:
return None
else:
return toColor(arg, default)
def setColors(**kw):
UNDEF = []
progress = 1
assigned = {}
while kw and progress:
progress = 0
for k, v in kw.items():
if type(v) in (type(()),type([])):
c = map(lambda x,UNDEF=UNDEF: toColor(x,UNDEF),v)
if type(v) is type(()): c = tuple(c)
ok = UNDEF not in c
else:
c = toColor(v,UNDEF)
ok = c is not UNDEF
if ok:
assigned[k] = c
del kw[k]
progress = 1
if kw: raise ValueError("Can't convert\n%s" % str(kw))
getAllNamedColors()
for k, c in assigned.items():
globals()[k] = c
if isinstance(c,Color): _namedColors[k] = c
def Whiter(c,f):
'''given a color combine with white as c*f w*(1-f) 0<=f<=1'''
c = toColor(c)
if isinstance(c,PCMYKColor):
w = _PCMYK_white
elif isinstance(c,CMYKColor): w = _CMYK_white
else: w = white
return linearlyInterpolatedColor(w, c, 0, 1, f)
def Blacker(c,f):
'''given a color combine with black as c*f+b*(1-f) 0<=f<=1'''
c = toColor(c)
if isinstance(c,PCMYKColor):
b = _PCMYK_black
elif isinstance(c,CMYKColor): b = _CMYK_black
else: b = black
return linearlyInterpolatedColor(b, c, 0, 1, f)

452
bin/reportlab/lib/corp.py Normal file
View File

@ -0,0 +1,452 @@
#!/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/lib/corp.py
""" This module includes some reusable routines for ReportLab's
'Corporate Image' - the logo, standard page backdrops and
so on - you are advised to do the same for your own company!"""
__version__=''' $Id: corp.py 2484 2004-12-10 07:27:50Z andy $ '''
from reportlab.lib.units import inch,cm
from reportlab.lib.validators import *
from reportlab.lib.attrmap import *
from reportlab.graphics.shapes import definePath, Group, Drawing, Rect, PolyLine, String
from reportlab.graphics.widgetbase import Widget
from reportlab.lib.colors import Color, black, white, ReportLabBlue
from reportlab.pdfbase.pdfmetrics import stringWidth
from math import sin, pi
class RL_CorpLogo(Widget):
'''Dinu's fat letter logo as hacked into decent paths by Robin'''
_attrMap = AttrMap(
x = AttrMapValue(isNumber,'Logo x-coord'),
y = AttrMapValue(isNumber,'Logo y-coord'),
angle = AttrMapValue(isNumber,'Logo rotation'),
strokeColor = AttrMapValue(isColorOrNone, 'Logo lettering stroke color'),
fillColor = AttrMapValue(isColorOrNone, 'Logo lettering fill color'),
strokeWidth = AttrMapValue(isNumber,'Logo lettering stroke width'),
background = AttrMapValue(isColorOrNone,desc="Logo background color"),
border = AttrMapValue(isColorOrNone,desc="Logo border color"),
borderWidth = AttrMapValue(isNumber,desc="Logo border width (1)"),
shadow = AttrMapValue(isNumberOrNone,desc="None or fraction of background for shadowing" ),
width = AttrMapValue(isNumber, desc="width in points of the logo (default 129)"),
height = AttrMapValue(isNumber, desc="height in points of the logo (default 86)"),
skewX = AttrMapValue(isNumber, desc="x-skew of the logo (default 10)"),
skewY = AttrMapValue(isNumber, desc="y-skew of the logo (default 0)"),
showPage = AttrMapValue(isBoolean, desc="If true show the page lines"),
xFlip = AttrMapValue(isBoolean, desc="If true do x reversal"),
yFlip = AttrMapValue(isBoolean, desc="If true do y reversal"),
)
def __init__(self):
self.fillColor = white
self.strokeColor = None
self.strokeWidth = 0.1
self.background = ReportLabBlue
self.border = None
self.borderWidth = 1
self.shadow = 0.5
self.height = 86
self.width = 130
self.x = self.y = self.angle = self.skewY = self._dx = 0
self.skewX = 10
self._dy = 35.5
self.showPage = 1
def demo(self):
D = Drawing(self.width, self.height)
D.add(self)
return D
def _paintLogo(self, g, dx=0, dy=0, strokeColor=None, strokeWidth=0.1, fillColor=white):
P = [
('moveTo' ,15.7246,0 ), ('lineTo' ,9.49521,0 ), ('lineTo' ,6.64988,6.83711 ), ('curveTo' ,6.62224,6.95315 ,6.57391,7.10646 ,6.50485,7.29708 ), ('curveTo' ,6.43578,7.48767 ,6.35059,7.71559 ,6.24931,7.98079 ), ('lineTo' ,6.29074,6.71282 ), ('lineTo' ,6.29074,0 ), ('lineTo' ,0.55862,0 ), ('lineTo' ,0.55862,19.19365 ), ('lineTo' ,6.45649,19.19365 ), ('curveTo' ,9.05324,19.19365 ,10.99617,18.73371 ,12.28532,17.8138 ), ('curveTo' ,13.92439,16.63697 ,14.7439,14.96293 ,14.7439,12.79161 ), ('curveTo' ,14.7439,10.47114 ,13.64354,8.86755 ,11.44276,7.98079 ), 'closePath', ('moveTo' ,6.31838,10.30542 ), ('lineTo' ,6.70513,10.30542 ), ('curveTo' ,7.36812,10.30542 ,7.92062,10.53331 ,8.36261,10.98912 ), ('curveTo' ,8.80461,11.44491 ,9.0256,12.02504 ,9.0256,12.72947 ), ('curveTo' ,9.0256,14.16321 ,8.19227,14.88004 ,6.52556,14.88004 ), ('lineTo' ,6.31838,14.88004 ), 'closePath',
('moveTo' ,25.06173,4.54978 ), ('lineTo' ,30.47611,4.45033 ), ('curveTo' ,30.08951,2.88402 ,29.33668,1.70513 ,28.21787,0.91369 ), ('curveTo' ,27.09906,0.12223 ,25.63726,-0.27348 ,23.83245,-0.27348 ), ('curveTo' ,21.69611,-0.27348 ,20.02024,0.32322 ,18.80475,1.5166 ), ('curveTo' ,17.59846,2.72658 ,16.99531,4.37988 ,16.99531,6.47662 ), ('curveTo' ,16.99531,8.6065 ,17.64451,10.34269 ,18.94286,11.68527 ), ('curveTo' ,20.24124,13.03612 ,21.91711,13.71152 ,23.97056,13.71152 ), ('curveTo' ,26.01482,13.71152 ,27.64466,13.06096 ,28.86015,11.75985 ), ('curveTo' ,30.07566,10.45042 ,30.68326,8.71423 ,30.68326,6.5512 ), ('lineTo' ,30.65586,5.66859 ), ('lineTo' ,22.53407,5.66859 ), ('curveTo' ,22.59855,4.29287 ,23.03132,3.60503 ,23.83245,3.60503 ), ('curveTo' ,24.45861,3.60503 ,24.86837,3.91994 ,25.06173,4.54978 ), 'closePath', ('moveTo' ,25.18604,8.35371 ), ('curveTo' ,25.18604,8.60235 ,25.15384,8.83024 ,25.08937,9.03742 ), ('curveTo' ,25.02489,9.24463 ,24.93514,9.42278 ,24.82001,9.57197 ), ('curveTo' ,24.70492,9.72113 ,24.56911,9.83923 ,24.41255,9.92624 ), ('curveTo' ,24.25603,10.01326 ,24.08568,10.05678 ,23.90152,10.05678 ), ('curveTo' ,23.51474,10.05678 ,23.20169,9.89725 ,22.96225,9.57819 ), ('curveTo' ,22.72283,9.25913 ,22.60314,8.85096 ,22.60314,8.35371 ), 'closePath',
('moveTo' ,38.36308,-5.99181 ), ('lineTo' ,32.82428,-5.99181 ), ('lineTo' ,32.82428,13.43804 ), ('lineTo' ,38.36308,13.43804 ), ('lineTo' ,38.23873,11.53608 ), ('curveTo' ,38.46886,11.93387 ,38.70371,12.27159 ,38.94327,12.54922 ), ('curveTo' ,39.18254,12.82685 ,39.44037,13.05268 ,39.71676,13.22671 ), ('curveTo' ,39.99286,13.40074 ,40.28988,13.52712 ,40.60753,13.60585 ), ('curveTo' ,40.92518,13.68459 ,41.27759,13.72396 ,41.66419,13.72396 ), ('curveTo' ,43.10068,13.72396 ,44.2702,13.07755 ,45.17246,11.78472 ), ('curveTo' ,46.06588,10.50844 ,46.51229,8.81368 ,46.51229,6.70038 ), ('curveTo' ,46.51229,4.55394 ,46.08415,2.85502 ,45.22785,1.60362 ), ('curveTo' ,44.38983,0.35221 ,43.23416,-0.27348 ,41.76084,-0.27348 ), ('curveTo' ,40.41659,-0.27348 ,39.24235,0.42679 ,38.23873,1.82739 ), ('curveTo' ,38.2847,1.40472 ,38.31239,1.04007 ,38.32153,0.73345 ), ('curveTo' ,38.34923,0.41851 ,38.36308,0.04146 ,38.36308,-0.3978 ), 'closePath', ('moveTo' ,40.7802,6.84954 ), ('curveTo' ,40.7802,7.72802 ,40.66734,8.40964 ,40.44193,8.89448 ), ('curveTo' ,40.21621,9.37929 ,39.89621,9.62168 ,39.48191,9.62168 ), ('curveTo' ,38.62533,9.62168 ,38.19718,8.68108 ,38.19718,6.79983 ), ('curveTo' ,38.19718,4.87712 ,38.61177,3.91581 ,39.44037,3.91581 ), ('curveTo' ,39.85466,3.91581 ,40.18174,4.1727 ,40.42101,4.68654 ), ('curveTo' ,40.66057,5.20037 ,40.7802,5.92135 ,40.7802,6.84954 ), 'closePath',
('moveTo' ,62.10648,6.51392 ), ('curveTo' ,62.10648,4.44205 ,61.47118,2.79288 ,60.2003,1.56631 ), ('curveTo' ,58.92971,0.33978 ,57.22626,-0.27348 ,55.08965,-0.27348 ), ('curveTo' ,52.99018,-0.27348 ,51.31914,0.35221 ,50.07595,1.60362 ), ('curveTo' ,48.8419,2.8633 ,48.22517,4.55394 ,48.22517,6.67551 ), ('curveTo' ,48.22517,8.79709 ,48.85575,10.50016 ,50.1175,11.78472 ), ('curveTo' ,51.36982,13.07755 ,53.03172,13.72396 ,55.1035,13.72396 ), ('curveTo' ,57.28608,13.72396 ,58.99866,13.08168 ,60.24185,11.79712 ), ('curveTo' ,61.48503,10.51259 ,62.10648,8.75154 ,62.10648,6.51392 ), 'closePath', ('moveTo' ,56.73358,6.67551 ), ('curveTo' ,56.73358,7.17276 ,56.69675,7.62236 ,56.62308,8.02428 ), ('curveTo' ,56.54942,8.42623 ,56.44334,8.77016 ,56.30544,9.05607 ), ('curveTo' ,56.16724,9.34198 ,56.00134,9.56369 ,55.80804,9.72113 ), ('curveTo' ,55.61474,9.8786 ,55.39817,9.95733 ,55.1589,9.95733 ), ('curveTo' ,54.68921,9.95733 ,54.31174,9.65898 ,54.02621,9.06229 ), ('curveTo' ,53.74068,8.54018 ,53.59807,7.75702 ,53.59807,6.71282 ), ('curveTo' ,53.59807,5.68515 ,53.74068,4.90202 ,54.02621,4.36332 ), ('curveTo' ,54.31174,3.76663 ,54.69392,3.46828 ,55.17275,3.46828 ), ('curveTo' ,55.62388,3.46828 ,55.99692,3.7625 ,56.29159,4.35088 ), ('curveTo' ,56.58625,5.0056 ,56.73358,5.78047 ,56.73358,6.67551 ), 'closePath',
('moveTo' ,69.78629,0 ), ('lineTo' ,64.2475,0 ), ('lineTo' ,64.2475,13.43804 ), ('lineTo' ,69.78629,13.43804 ), ('lineTo' ,69.49605,10.81507 ), ('curveTo' ,70.33407,12.77921 ,71.71988,13.76126 ,73.65346,13.76126 ), ('lineTo' ,73.65346,8.16725 ), ('curveTo' ,73.04586,8.4656 ,72.5302,8.61478 ,72.10647,8.61478 ), ('curveTo' ,71.36068,8.61478 ,70.78756,8.37236 ,70.38711,7.88755 ), ('curveTo' ,69.98637,7.40274 ,69.78629,6.69623 ,69.78629,5.76804 ), 'closePath',
('moveTo' ,81.55427,0 ), ('lineTo' ,76.00163,0 ), ('lineTo' ,76.00163,9.42278 ), ('lineTo' ,74.42725,9.42278 ), ('lineTo' ,74.42725,13.43804 ), ('lineTo' ,76.00163,13.43804 ), ('lineTo' ,76.00163,17.39113 ), ('lineTo' ,81.55427,17.39113 ), ('lineTo' ,81.55427,13.43804 ), ('lineTo' ,83.39121,13.43804 ), ('lineTo' ,83.39121,9.42278 ), ('lineTo' ,81.55427,9.42278 ), 'closePath',
('moveTo' ,95.17333,0 ), ('lineTo' ,85.09024,0 ), ('lineTo' ,85.09024,19.19365 ), ('lineTo' ,90.85002,19.19365 ), ('lineTo' ,90.85002,4.61196 ), ('lineTo' ,95.17333,4.61196 ), 'closePath',
('moveTo' ,110.00787,0 ), ('lineTo' ,104.45523,0 ), ('curveTo' ,104.5012,0.44754 ,104.53803,0.87433 ,104.56573,1.2804 ), ('curveTo' ,104.59313,1.68651 ,104.62083,2.01385 ,104.64853,2.26246 ), ('curveTo' ,103.69087,0.57182 ,102.40644,-0.27348 ,100.79492,-0.27348 ), ('curveTo' ,99.39527,-0.27348 ,98.28557,0.35637 ,97.46611,1.61605 ), ('curveTo' ,96.65578,2.86746 ,96.25062,4.59952 ,96.25062,6.81227 ), ('curveTo' ,96.25062,8.95041 ,96.66963,10.63276 ,97.50765,11.8593 ), ('curveTo' ,98.34538,13.10242 ,99.4872,13.72396 ,100.93312,13.72396 ), ('curveTo' ,102.41557,13.72396 ,103.61249,12.92008 ,104.52418,11.31231 ), ('curveTo' ,104.50591,11.47806 ,104.49206,11.62309 ,104.48293,11.74741 ), ('curveTo' ,104.4735,11.87173 ,104.46437,11.9753 ,104.45523,12.05819 ), ('lineTo' ,104.39983,12.84135 ), ('lineTo' ,104.35858,13.43804 ), ('lineTo' ,110.00787,13.43804 ), 'closePath', ('moveTo' ,104.39983,6.88685 ), ('curveTo' ,104.39983,7.38409 ,104.37921,7.80676 ,104.33766,8.15481 ), ('curveTo' ,104.29641,8.5029 ,104.22952,8.78672 ,104.13758,9.00636 ), ('curveTo' ,104.04535,9.22598 ,103.92572,9.38341 ,103.77839,9.47874 ), ('curveTo' ,103.63106,9.57403 ,103.45161,9.62168 ,103.23974,9.62168 ), ('curveTo' ,102.30036,9.62168 ,101.83096,8.49875 ,101.83096,6.25285 ), ('curveTo' ,101.83096,4.64508 ,102.24967,3.8412 ,103.0877,3.8412 ), ('curveTo' ,103.96255,3.8412 ,104.39983,4.85641 ,104.39983,6.88685 ), 'closePath',
('moveTo' ,118.22604,0 ), ('lineTo' ,112.5629,0 ), ('lineTo' ,112.5629,20.99616 ), ('lineTo' ,118.10169,20.99616 ), ('lineTo' ,118.10169,13.63694 ), ('curveTo' ,118.10169,13.01538 ,118.07399,12.30268 ,118.01889,11.49877 ), ('curveTo' ,118.52542,12.31096 ,119.03636,12.88693 ,119.55202,13.22671 ), ('curveTo' ,120.08625,13.55821 ,120.75838,13.72396 ,121.5687,13.72396 ), ('curveTo' ,123.07885,13.72396 ,124.24837,13.09827 ,125.07697,11.84686 ), ('curveTo' ,125.90586,10.60373 ,126.32015,8.85099 ,126.32015,6.5885 ), ('curveTo' ,126.32015,4.42546 ,125.89201,2.74314 ,125.03571,1.54147 ), ('curveTo' ,124.18826,0.3315 ,123.01432,-0.27348 ,121.51331,-0.27348 ), ('curveTo' ,120.78608,-0.27348 ,120.16905,-0.12432 ,119.66252,0.17403 ), ('curveTo' ,119.41383,0.3315 ,119.15835,0.54283 ,118.8961,0.80803 ), ('curveTo' ,118.63356,1.07322 ,118.36866,1.40472 ,118.10169,1.80252 ), ('curveTo' ,118.11112,1.64505 ,118.12025,1.51039 ,118.12939,1.3985 ), ('curveTo' ,118.13852,1.28662 ,118.14766,1.19339 ,118.15709,1.11881 ), 'closePath', ('moveTo' ,120.58806,6.70038 ), ('curveTo' ,120.58806,8.62306 ,120.11837,9.5844 ,119.17898,9.5844 ), ('curveTo' ,118.35039,9.5844 ,117.93609,8.67693 ,117.93609,6.86198 ), ('curveTo' ,117.93609,4.96417 ,118.36424,4.01526 ,119.22053,4.01526 ), ('curveTo' ,120.13222,4.01526 ,120.58806,4.91027 ,120.58806,6.70038 ), 'closePath',
] + (self.showPage and [
('moveTo',38.30626,-7.28346),('lineTo',38.30626,-25.55261),('lineTo',85.15777,-25.55261),('lineTo',85.15777,-1.39019),('lineTo',90.46172,-1.39019),('lineTo',90.46172,-31.15121),('lineTo',32.70766,-31.15121),('lineTo',32.70766,-7.28346), 'closePath',
('moveTo' ,32.70766,14.52164 ), ('lineTo' ,32.70766,47.81862 ), ('lineTo' ,80.14849,47.81862 ), ('lineTo' ,90.46172,37.21073 ), ('lineTo' ,90.46172,20.12025 ), ('lineTo' ,85.15777,20.12025 ), ('lineTo' ,85.15777,30.72814 ), ('lineTo' ,73.66589,30.72814 ), ('lineTo' ,73.66589,42.22002 ), ('lineTo' ,38.30626,42.22002 ), ('lineTo' ,38.30626,14.52164 ), 'closePath', ('moveTo' ,79.2645,36.32674 ), ('lineTo' ,85.15777,36.32674 ), ('lineTo' ,79.2645,42.22002 ), 'closePath',
] or [])
g.add(definePath(P,strokeColor=strokeColor,strokeWidth=strokeWidth,fillColor=fillColor, dx=dx, dy=dy))
def draw(self):
fillColor = self.fillColor
strokeColor = self.strokeColor
g = Group()
bg = self.background
bd = self.border
bdw = self.borderWidth
shadow = self.shadow
x, y = self.x, self.y
if bg:
if shadow is not None and 0<=shadow<1:
shadow = Color(bg.red*shadow,bg.green*shadow,bg.blue*shadow)
self._paintLogo(g,dy=-2.5, dx=2,fillColor=shadow)
self._paintLogo(g,fillColor=fillColor,strokeColor=strokeColor)
g.skew(kx=self.skewX, ky=self.skewY)
g.shift(self._dx,self._dy)
G = Group()
G.add(g)
_w, _h = 130, 86
w, h = self.width, self.height
if bg or (bd and bdw):
G.insert(0,Rect(0,0,_w,_h,fillColor=bg,strokeColor=bd,strokeWidth=bdw))
if w!=_w or h!=_h: G.scale(w/float(_w),h/float(_h))
angle = self.angle
if self.angle:
w, h = w/2., h/2.
G.shift(-w,-h)
G.rotate(angle)
G.shift(w,h)
xFlip = getattr(self,'xFlip',0) and -1 or 0
yFlip = getattr(self,'yFlip',0) and -1 or 0
if xFlip or yFlip:
sx = xFlip or 1
sy = yFlip or 1
G.shift(sx*x+w*xFlip,sy*y+yFlip*h)
G = Group(G,transform=(sx,0,0,sy,0,0))
else:
G.shift(x,y)
return G
class RL_CorpLogoReversed(RL_CorpLogo):
def __init__(self):
RL_CorpLogo.__init__(self)
self.background = white
self.fillColor = ReportLabBlue
class RL_CorpLogoThin(Widget):
"""The ReportLab Logo.
New version created by John Precedo on 7-8 August 2001.
Based on bitmapped imaged from E-Id.
Improved by Robin Becker."""
_attrMap = AttrMap(
x = AttrMapValue(isNumber),
y = AttrMapValue(isNumber),
height = AttrMapValue(isNumberOrNone),
width = AttrMapValue(isNumberOrNone),
fillColor = AttrMapValue(isColorOrNone),
strokeColor = AttrMapValue( isColorOrNone)
)
_h = 90.5
_w = 136.5
_text='R e p o r t L a b'
_fontName = 'Helvetica-Bold'
_fontSize = 16
def __init__(self):
self.fillColor = ReportLabBlue
self.strokeColor = white
self.x = 0
self.y = 0
self.height = self._h
self.width = self._w
def demo(self):
D = Drawing(self.width, self.height)
D.add(self)
return D
def _getText(self, x=0, y=0, color=None):
return String(x,y, self._text, fontName=self._fontName, fontSize=self._fontSize, fillColor=color)
def _sw(self,f=None,l=None):
text = self._text
if f is None: f = 0
if l is None: l = len(text)
return stringWidth(text[f:l],self._fontName,self._fontSize)
def _addPage(self, g, strokeWidth=3, color=None, dx=0, dy=0):
x1, x2 = 31.85+dx, 80.97+dx
fL = 10 # fold length
y1, y2 = dy-34, dy+50.5
L = [[x1,dy-4,x1,y1, x2, y1, x2, dy-1],
[x1,dy+11,x1,y2,x2-fL,y2,x2,y2-fL,x2,dy+14],
[x2-10,y2,x2-10,y2-fL,x2,y2-fL]]
for l in L:
g.add(PolyLine(l, strokeWidth=strokeWidth, strokeColor=color, strokeLineJoin=0))
def draw(self):
sx = 0.5
fillColor = self.fillColor
strokeColor = self.strokeColor
shadow = Color(fillColor.red*sx,fillColor.green*sx,fillColor.blue*sx)
g = Group()
g2= Group()
g.add(Rect(fillColor=fillColor, strokeColor=fillColor, x=0, y=0, width=self._w, height=self._h))
sx = (self._w-2)/self._sw()
g2.scale(sx,1)
self._addPage(g2,strokeWidth=3,dx=2,dy=-2.5,color=shadow)
self._addPage(g2,strokeWidth=3,color=strokeColor)
g2.scale(1/sx,1)
g2.add(self._getText(x=1,y=0,color=shadow))
g2.add(self._getText(x=0,y=1,color=strokeColor))
g2.scale(sx,1)
g2.skew(kx=10, ky=0)
g2.shift(0,38)
g.add(g2)
g.scale(self.width/self._w,self.height/self._h)
g.shift(self.x,self.y)
return g
class ReportLabLogo:
"""vector reportlab logo centered in a 250x by 150y rectangle"""
def __init__(self, atx=0, aty=0, width=2.5*inch, height=1.5*inch, powered_by=0):
self.origin = (atx, aty)
self.dimensions = (width, height)
self.powered_by = powered_by
def draw(self, canvas):
from reportlab.graphics import renderPDF
canvas.saveState()
(atx,aty) = self.origin
(width, height) = self.dimensions
logo = RL_CorpLogo()
logo.width, logo.height = width, height
renderPDF.draw(logo.demo(),canvas,atx,aty,0)
canvas.restoreState()
class RL_BusinessCard(Widget):
"""Widget that creates a single business card.
Uses RL_CorpLogo for the logo.
For a black border around your card, set self.border to 1.
To change the details on the card, over-ride the following properties:
self.name, self.position, self.telephone, self.mobile, self.fax, self.email, self.web
The office locations are set in self.rh_blurb_top ("London office" etc), and
self.rh_blurb_bottom ("New York office" etc).
"""
# for items where it 'isString' the string can be an empty one...
_attrMap = AttrMap(
fillColor = AttrMapValue(isColorOrNone),
strokeColor = AttrMapValue(isColorOrNone),
altStrokeColor = AttrMapValue(isColorOrNone),
x = AttrMapValue(isNumber),
y = AttrMapValue(isNumber),
height = AttrMapValue(isNumber),
width = AttrMapValue(isNumber),
borderWidth = AttrMapValue(isNumber),
bleed=AttrMapValue(isNumberOrNone),
cropMarks=AttrMapValue(isBoolean),
border=AttrMapValue(isBoolean),
name=AttrMapValue(isString),
position=AttrMapValue(isString),
telephone=AttrMapValue(isString),
mobile=AttrMapValue(isString),
fax=AttrMapValue(isString),
email=AttrMapValue(isString),
web=AttrMapValue(isString),
rh_blurb_top=AttrMapValue(isListOfStringsOrNone),
rh_blurb_bottom=AttrMapValue(isListOfStringsOrNone)
)
_h = 5.35*cm
_w = 8.5*cm
_fontName = 'Helvetica-Bold'
_strapline = "strategic reporting solutions for e-business"
def __init__(self):
self.fillColor = ReportLabBlue
self.strokeColor = black
self.altStrokeColor = white
self.x = 0
self.y = 0
self.height = self._h
self.width = self._w
self.borderWidth = self.width/6.15
self.bleed=0.2*cm
self.cropMarks=1
self.border=0
#Over-ride these with your own info
self.name="Joe Cool"
self.position="Freelance Demonstrator"
self.telephone="020 8545 7271"
self.mobile="-"
self.fax="020 8544 1311"
self.email="info@reportlab.com"
self.web="www.reportlab.com"
self.rh_blurb_top = ["London office:",
"ReportLab Europe Ltd",
"Lombard Business Park",
"8 Lombard Road",
"Wimbledon",
"London SW19 3TZ",
"United Kingdom"]
self.rh_blurb_bottom = ["New York office:",
"ReportLab Inc",
"219 Harper Street",
"Highland Park",
"New Jersey 08904",
"USA"]
def demo(self):
D = Drawing(self.width, self.height)
D.add(self)
return D
def draw(self):
fillColor = self.fillColor
strokeColor = self.strokeColor
g = Group()
g.add(Rect(x = 0, y = 0,
fillColor = self.fillColor,
strokeColor = self.fillColor,
width = self.borderWidth,
height = self.height))
g.add(Rect(x = 0, y = self.height-self.borderWidth,
fillColor = self.fillColor,
strokeColor = self.fillColor,
width = self.width,
height = self.borderWidth))
g2 = Group()
rl=RL_CorpLogo()
rl.height = 1.25*cm
rl.width = 1.9*cm
rl.draw()
g2.add(rl)
g.add(g2)
g2.shift(x=(self.width-(rl.width+(self.width/42))),
y=(self.height - (rl.height+(self.height/42))))
g.add(String(x = self.borderWidth/5.0,
y = ((self.height - (rl.height+(self.height/42)))+((38/90.5)*rl.height)),
fontSize = 6,
fillColor = self.altStrokeColor,
fontName = "Helvetica-BoldOblique",
textAnchor = 'start',
text = self._strapline))
leftText=["Tel:", "Mobile:", "Fax:", "Email:", "Web:"]
leftDetails=[self.telephone,self.mobile,self.fax,self.email,self.web]
leftText.reverse()
leftDetails.reverse()
for f in range(len(leftText),0,-1):
g.add(String(x = self.borderWidth+(self.borderWidth/5.0),
y = (self.borderWidth/5.0)+((f-1)*(5*1.2)),
fontSize = 5,
fillColor = self.strokeColor,
fontName = "Helvetica",
textAnchor = 'start',
text = leftText[f-1]))
g.add(String(x = self.borderWidth+(self.borderWidth/5.0)+self.borderWidth,
y = (self.borderWidth/5.0)+((f-1)*(5*1.2)),
fontSize = 5,
fillColor = self.strokeColor,
fontName = "Helvetica",
textAnchor = 'start',
text = leftDetails[f-1]))
rightText=self.rh_blurb_bottom
rightText.reverse()
for f in range(len(rightText),0,-1):
g.add(String(x = self.width-((self.borderWidth/5.0)),
y = (self.borderWidth/5.0)+((f-1)*(5*1.2)),
fontSize = 5,
fillColor = self.strokeColor,
fontName = "Helvetica",
textAnchor = 'end',
text = rightText[f-1]))
ty = (self.height-self.borderWidth-(self.borderWidth/5.0)+2)
# g.add(Line(self.borderWidth, ty, self.borderWidth+(self.borderWidth/5.0), ty))
# g.add(Line(self.borderWidth+(self.borderWidth/5.0), ty, self.borderWidth+(self.borderWidth/5.0),
# ty+(self.borderWidth/5.0)))
# g.add(Line(self.borderWidth, ty-10,
# self.borderWidth+(self.borderWidth/5.0), ty-10))
rightText=self.rh_blurb_top
for f in range(1,(len(rightText)+1)):
g.add(String(x = self.width-(self.borderWidth/5.0),
y = ty-((f)*(5*1.2)),
fontSize = 5,
fillColor = self.strokeColor,
fontName = "Helvetica",
textAnchor = 'end',
text = rightText[f-1]))
g.add(String(x = self.borderWidth+(self.borderWidth/5.0),
y = ty-10,
fontSize = 10,
fillColor = self.strokeColor,
fontName = "Helvetica",
textAnchor = 'start',
text = self.name))
ty1 = ty-10*1.2
g.add(String(x = self.borderWidth+(self.borderWidth/5.0),
y = ty1-8,
fontSize = 8,
fillColor = self.strokeColor,
fontName = "Helvetica",
textAnchor = 'start',
text = self.position))
if self.border:
g.add(Rect(x = 0, y = 0,
fillColor=None,
strokeColor = black,
width = self.width,
height = self.height))
g.shift(self.x,self.y)
return g
def test():
"""This function produces a pdf with examples. """
#white on blue
rl = RL_CorpLogo()
rl.width = 129
rl.height = 86
D = Drawing(rl.width,rl.height)
D.add(rl)
D.__dict__['verbose'] = 1
D.save(fnRoot='corplogo_whiteonblue',formats=['pdf','eps','jpg','gif'])
#blue on white
rl = RL_CorpLogoReversed()
rl.width = 129
rl.height = 86
D = Drawing(rl.width,rl.height)
D.add(rl)
D.__dict__['verbose'] = 1
D.save(fnRoot='corplogo_blueonwhite',formats=['pdf','eps','jpg','gif'])
#gray on white
rl = RL_CorpLogoReversed()
rl.fillColor = Color(0.2, 0.2, 0.2)
rl.width = 129
rl.height = 86
D = Drawing(rl.width,rl.height)
D.add(rl)
D.__dict__['verbose'] = 1
D.save(fnRoot='corplogo_grayonwhite',formats=['pdf','eps','jpg','gif'])
rl = RL_BusinessCard()
rl.x=25
rl.y=25
rl.border=1
D = Drawing(rl.width+50,rl.height+50)
D.add(rl)
D.__dict__['verbose'] = 1
D.save(fnRoot='RL_BusinessCard',formats=['pdf'])
if __name__=='__main__':
test()

View File

@ -0,0 +1,11 @@
#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/lib/enums.py
__version__=''' $Id: enums.py 2385 2004-06-17 15:26:05Z rgbecker $ '''
__doc__="""
holder for all reportlab's enumerated types
"""
TA_LEFT = 0
TA_CENTER = 1
TA_RIGHT = 2
TA_JUSTIFY = 4

View File

@ -0,0 +1,81 @@
#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/lib/extformat.py
from tokenize import tokenprog
import sys
def _matchorfail(text, pos):
match = tokenprog.match(text, pos)
if match is None: raise ValueError(text, pos)
return match, match.end()
'''
Extended dictionary formatting
We allow expressions in the parentheses instead of
just a simple variable.
'''
def dictformat(_format, L={}, G={}):
format = _format
S = {}
chunks = []
pos = 0
n = 0
while 1:
pc = format.find("%", pos)
if pc < 0: break
nextchar = format[pc+1]
if nextchar == "(":
chunks.append(format[pos:pc])
pos, level = pc+2, 1
while level:
match, pos = _matchorfail(format, pos)
tstart, tend = match.regs[3]
token = format[tstart:tend]
if token == "(": level = level+1
elif token == ")": level = level-1
vname = '__superformat_%d' % n
n += 1
S[vname] = eval(format[pc+2:pos-1],L,G)
chunks.append('%%(%s)' % vname)
else:
nc = pc+1+(nextchar=="%")
chunks.append(format[pos:nc])
pos = nc
if pos < len(format): chunks.append(format[pos:])
return (''.join(chunks)) % S
def magicformat(format):
"""Evaluate and substitute the appropriate parts of the string."""
try: 1/0
except: frame = sys.exc_traceback.tb_frame
while frame.f_globals["__name__"] == __name__: frame = frame.f_back
return dictformat(format,frame.f_locals, frame.f_globals)
if __name__=='__main__':
from reportlab.lib.formatters import DecimalFormatter
_DF={}
def df(n,dp=2,ds='.',ts=','):
try:
_df = _DF[dp,ds]
except KeyError:
_df = _DF[dp,ds] = DecimalFormatter(places=dp,decimalSep=ds,thousandSep=ts)
return _df(n)
from reportlab.lib.extformat import magicformat
Z={'abc': ('ab','c')}
x = 300000.23
percent=79.2
class dingo:
a=3
print magicformat('''
$%%(df(x,dp=3))s --> $%(df(x,dp=3))s
$%%(df(x,dp=2,ds=',',ts='.'))s --> $%(df(x,dp=2,ds=',',ts='.'))s
%%(percent).2f%%%% --> %(percent).2f%%
%%(dingo.a)s --> %(dingo.a)s
%%(Z['abc'][0])s --> %(Z['abc'][0])s
''')

View File

@ -0,0 +1,89 @@
#!/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/lib/fonts.py
__version__=''' $Id: fonts.py 2385 2004-06-17 15:26:05Z rgbecker $ '''
import string, sys, os
###############################################################################
# A place to put useful font stuff
###############################################################################
#
# Font Mappings
# The brute force approach to finding the correct postscript font name;
# much safer than the rule-based ones we tried.
# preprocessor to reduce font face names to the shortest list
# possible. Add any aliases you wish; it keeps looking up
# until it finds no more translations to do. Any input
# will be lowercased before checking.
_family_alias = {
'serif':'times',
'sansserif':'helvetica',
'monospaced':'courier',
'arial':'helvetica'
}
#maps a piddle font to a postscript one.
_tt2ps_map = {
#face, bold, italic -> ps name
('times', 0, 0) :'Times-Roman',
('times', 1, 0) :'Times-Bold',
('times', 0, 1) :'Times-Italic',
('times', 1, 1) :'Times-BoldItalic',
('courier', 0, 0) :'Courier',
('courier', 1, 0) :'Courier-Bold',
('courier', 0, 1) :'Courier-Oblique',
('courier', 1, 1) :'Courier-BoldOblique',
('helvetica', 0, 0) :'Helvetica',
('helvetica', 1, 0) :'Helvetica-Bold',
('helvetica', 0, 1) :'Helvetica-Oblique',
('helvetica', 1, 1) :'Helvetica-BoldOblique',
# there is only one Symbol font
('symbol', 0, 0) :'Symbol',
('symbol', 1, 0) :'Symbol',
('symbol', 0, 1) :'Symbol',
('symbol', 1, 1) :'Symbol',
# ditto for dingbats
('zapfdingbats', 0, 0) :'ZapfDingbats',
('zapfdingbats', 1, 0) :'ZapfDingbats',
('zapfdingbats', 0, 1) :'ZapfDingbats',
('zapfdingbats', 1, 1) :'ZapfDingbats',
}
_ps2tt_map={}
for k,v in _tt2ps_map.items():
if not _ps2tt_map.has_key(k):
_ps2tt_map[string.lower(v)] = k
def ps2tt(psfn):
'ps fontname to family name, bold, italic'
psfn = string.lower(psfn)
if _ps2tt_map.has_key(psfn):
return _ps2tt_map[psfn]
raise ValueError, "Can't map determine family/bold/italic for %s" % psfn
def tt2ps(fn,b,i):
'family name + bold & italic to ps font name'
K = (string.lower(fn),b,i)
if _tt2ps_map.has_key(K):
return _tt2ps_map[K]
else:
fn, b1, i1 = ps2tt(K[0])
K = fn, b1|b, i1|i
if _tt2ps_map.has_key(K):
return _tt2ps_map[K]
raise ValueError, "Can't find concrete font for family=%s, bold=%d, italic=%d" % (fn, b, i)
def addMapping(face, bold, italic, psname):
'allow a custom font to be put in the mapping'
k = (string.lower(face), bold, italic)
_tt2ps_map[k] = psname
# rebuild inverse - inefficient
for k,v in _tt2ps_map.items():
if not _ps2tt_map.has_key(k):
_ps2tt_map[string.lower(v)] = k

View File

@ -0,0 +1,100 @@
#!/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/lib/formatters.py
__version__=''' $Id: formatters.py 2385 2004-06-17 15:26:05Z rgbecker $ '''
__doc__="""
These help format numbers and dates in a user friendly way.
Used by the graphics framework.
"""
import string, sys, os
class Formatter:
"Base formatter - simply applies python format strings"
def __init__(self, pattern):
self.pattern = pattern
def format(self, obj):
return self.pattern % obj
def __repr__(self):
return "%s('%s')" % (self.__class__.__name__, self.pattern)
def __call__(self, x):
return self.format(x)
class DecimalFormatter(Formatter):
"""lets you specify how to build a decimal.
A future NumberFormatter class will take Microsoft-style patterns
instead - "$#,##0.00" is WAY easier than this."""
def __init__(self, places=2, decimalSep='.', thousandSep=None, prefix=None, suffix=None):
self.places = places
self.dot = decimalSep
self.comma = thousandSep
self.prefix = prefix
self.suffix = suffix
def format(self, num):
# positivize the numbers
sign=num<0
if sign:
num = -num
places, sep = self.places, self.dot
strip = places<=0
if places and strip: places = -places
strInt = ('%.' + str(places) + 'f') % num
if places:
strInt, strFrac = strInt.split('.')
strFrac = sep + strFrac
if strip:
while strFrac and strFrac[-1] in ['0',sep]: strFrac = strFrac[:-1]
else:
strFrac = ''
if self.comma is not None:
strNew = ''
while strInt:
left, right = strInt[0:-3], strInt[-3:]
if left == '':
#strNew = self.comma + right + strNew
strNew = right + strNew
else:
strNew = self.comma + right + strNew
strInt = left
strInt = strNew
strBody = strInt + strFrac
if sign: strBody = '-' + strBody
if self.prefix:
strBody = self.prefix + strBody
if self.suffix:
strBody = strBody + self.suffix
return strBody
def __repr__(self):
return "%s(places=%d, decimalSep=%s, thousandSep=%s, prefix=%s, suffix=%s)" % (
self.__class__.__name__,
self.places,
repr(self.dot),
repr(self.comma),
repr(self.prefix),
repr(self.suffix)
)
if __name__=='__main__':
def t(n, s, places=2, decimalSep='.', thousandSep=None, prefix=None, suffix=None):
f=DecimalFormatter(places,decimalSep,thousandSep,prefix,suffix)
r = f(n)
print "places=%2d dot=%-4s comma=%-4s prefix=%-4s suffix=%-4s result=%10s %s" %(f.places, f.dot, f.comma, f.prefix, f.suffix,r, r==s and 'OK' or 'BAD')
t(1000.9,'1,000.9',1,thousandSep=',')
t(1000.95,'1,001.0',1,thousandSep=',')
t(1000.95,'1,001',-1,thousandSep=',')
t(1000.9,'1,001',0,thousandSep=',')
t(1000.9,'1000.9',1)
t(1000.95,'1001.0',1)
t(1000.95,'1001',-1)
t(1000.9,'1001',0)
t(1000.1,'1000.1',1)
t(1000.55,'1000.6',1)
t(1000.449,'1000.4',-1)
t(1000.45,'1000',0)

View File

@ -0,0 +1,61 @@
#!/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/lib/logger.py
__version__=''' $Id: logger.py 2385 2004-06-17 15:26:05Z rgbecker $ '''
from sys import stderr
class Logger:
'''
An extended file type thing initially equivalent to sys.stderr
You can add/remove file type things; it has a write method
'''
def __init__(self):
self._fps = [stderr]
self._fns = {}
def add(self,fp):
'''add the file/string fp to the destinations'''
if type(fp) is StringType:
if fp in self._fns: return
fp = open(fn,'wb')
self._fns[fn] = fp
self._fps.append(fp)
def remove(self,fp):
'''remove the file/string fp from the destinations'''
if type(fp) is StringType:
if fp not in self._fns: return
fn = fp
fp = self._fns[fn]
del self.fns[fn]
if fp in self._fps:
del self._fps[self._fps.index(fp)]
def write(self,text):
'''write text to all the destinations'''
if text[-1]!='\n': text=text+'\n'
map(lambda fp,t=text: fp.write(t),self._fps)
def __call__(self,text):
self.write(text)
logger=Logger()
class WarnOnce:
def __init__(self,kind='Warn'):
self.uttered = {}
self.pfx = '%s: '%kind
self.enabled = 1
def once(self,warning):
if not self.uttered.has_key(warning):
if self.enabled: logger.write(self.pfx + warning)
self.uttered[warning] = 1
def __call__(self,warning):
self.once(warning)
warnOnce=WarnOnce()
infoOnce=WarnOnce('Info')

View File

@ -0,0 +1,605 @@
#!/usr/bin/env python
# normalDate.py - version 1.0 - 20000717
#hacked by Robin Becker 10/Apr/2001
#major changes include
# using Types instead of type(0) etc
# BusinessDate class
# __radd__, __rsub__ methods
# formatMS stuff
# derived from an original version created
# by Jeff Bauer of Rubicon Research and used
# with his kind permission
__version__=''' $Id: normalDate.py 2742 2005-12-12 11:58:08Z oualid $ '''
_bigBangScalar = -4345732 # based on (-9999, 1, 1) BC/BCE minimum
_bigCrunchScalar = 2958463 # based on (9999,12,31) AD/CE maximum
_daysInMonthNormal = [31,28,31,30,31,30,31,31,30,31,30,31]
_daysInMonthLeapYear = [31,29,31,30,31,30,31,31,30,31,30,31]
_dayOfWeekName = ['Monday', 'Tuesday', 'Wednesday', 'Thursday',
'Friday', 'Saturday', 'Sunday']
_monthName = ['January', 'February', 'March', 'April', 'May', 'June',
'July','August','September','October','November','December']
from types import IntType, StringType, ListType, TupleType
import string, re, time, datetime
if hasattr(time,'struct_time'):
_DateSeqTypes = (ListType,TupleType,time.struct_time)
else:
_DateSeqTypes = (ListType,TupleType)
_fmtPat = re.compile('\\{(m{1,5}|yyyy|yy|d{1,4})\\}',re.MULTILINE|re.IGNORECASE)
_iso_re = re.compile(r'(\d\d\d\d|\d\d)-(\d\d)-(\d\d)')
def getStdMonthNames():
return map(string.lower,_monthName)
def getStdShortMonthNames():
return map(lambda x: x[:3],getStdMonthNames())
def getStdDayNames():
return map(string.lower,_dayOfWeekName)
def getStdShortDayNames():
return map(lambda x: x[:3],getStdDayNames())
def isLeapYear(year):
"""determine if specified year is leap year, returns Python boolean"""
if year < 1600:
if year % 4:
return 0
else:
return 1
elif year % 4 != 0:
return 0
elif year % 100 != 0:
return 1
elif year % 400 != 0:
return 0
else:
return 1
class NormalDateException(Exception):
"""Exception class for NormalDate"""
pass
class NormalDate:
"""
NormalDate is a specialized class to handle dates without
all the excess baggage (time zones, daylight savings, leap
seconds, etc.) of other date structures. The minimalist
strategy greatly simplifies its implementation and use.
Internally, NormalDate is stored as an integer with values
in a discontinuous range of -99990101 to 99991231. The
integer value is used principally for storage and to simplify
the user interface. Internal calculations are performed by
a scalar based on Jan 1, 1900.
Valid NormalDate ranges include (-9999,1,1) B.C.E. through
(9999,12,31) C.E./A.D.
1.0 - No changes, except the version number. After 3 years of use
by various parties I think we can consider it stable.
0.8 - added Prof. Stephen Walton's suggestion for a range method
- module author resisted the temptation to use lambda <0.5 wink>
0.7 - added Dan Winkler's suggestions for __add__, __sub__ methods
0.6 - modifications suggested by Kevin Digweed to fix:
- dayOfWeek, dayOfWeekAbbrev, clone methods
- permit NormalDate to be a better behaved superclass
0.5 - minor tweaking
0.4 - added methods __cmp__, __hash__
- added Epoch variable, scoped to the module
- added setDay, setMonth, setYear methods
0.3 - minor touch-ups
0.2 - fixed bug for certain B.C.E leap years
- added Jim Fulton's suggestions for short alias class name =ND
and __getstate__, __setstate__ methods
Special thanks: Roedy Green
"""
def __init__(self, normalDate=None):
"""
Accept 1 of 4 values to initialize a NormalDate:
1. None - creates a NormalDate for the current day
2. integer in yyyymmdd format
3. string in yyyymmdd format
4. tuple in (yyyy, mm, dd) - localtime/gmtime can also be used
"""
if normalDate is None:
self.setNormalDate(time.localtime(time.time()))
else:
self.setNormalDate(normalDate)
def add(self, days):
"""add days to date; use negative integers to subtract"""
if not type(days) is IntType:
raise NormalDateException( \
'add method parameter must be integer type')
self.normalize(self.scalar() + days)
def __add__(self, days):
"""add integer to normalDate and return a new, calculated value"""
if not type(days) is IntType:
raise NormalDateException( \
'__add__ parameter must be integer type')
cloned = self.clone()
cloned.add(days)
return cloned
def __radd__(self,days):
'''for completeness'''
return self.__add__(days)
def clone(self):
"""return a cloned instance of this normalDate"""
return self.__class__(self.normalDate)
def __cmp__(self, target):
if target is None:
return 1
elif not hasattr(target, 'normalDate'):
return 1
else:
return cmp(self.normalDate, target.normalDate)
def day(self):
"""return the day as integer 1-31"""
return int(repr(self.normalDate)[-2:])
def dayOfWeek(self):
"""return integer representing day of week, Mon=0, Tue=1, etc."""
return apply(dayOfWeek, self.toTuple())
def dayOfWeekAbbrev(self):
"""return day of week abbreviation for current date: Mon, Tue, etc."""
return _dayOfWeekName[self.dayOfWeek()][:3]
def dayOfWeekName(self):
"""return day of week name for current date: Monday, Tuesday, etc."""
return _dayOfWeekName[self.dayOfWeek()]
def dayOfYear(self):
"""day of year"""
if self.isLeapYear():
daysByMonth = _daysInMonthLeapYear
else:
daysByMonth = _daysInMonthNormal
priorMonthDays = 0
for m in xrange(self.month() - 1):
priorMonthDays = priorMonthDays + daysByMonth[m]
return self.day() + priorMonthDays
def daysBetweenDates(self, normalDate):
"""
return value may be negative, since calculation is
self.scalar() - arg
"""
if type(normalDate) is _NDType:
return self.scalar() - normalDate.scalar()
else:
return self.scalar() - NormalDate(normalDate).scalar()
def equals(self, target):
if type(target) is _NDType:
if target is None:
return self.normalDate is None
else:
return self.normalDate == target.normalDate
else:
return 0
def endOfMonth(self):
"""returns (cloned) last day of month"""
return self.__class__(self.__repr__()[-8:-2]+str(self.lastDayOfMonth()))
def firstDayOfMonth(self):
"""returns (cloned) first day of month"""
return self.__class__(self.__repr__()[-8:-2]+"01")
def formatUS(self):
"""return date as string in common US format: MM/DD/YY"""
d = self.__repr__()
return "%s/%s/%s" % (d[-4:-2], d[-2:], d[-6:-4])
def formatUSCentury(self):
"""return date as string in 4-digit year US format: MM/DD/YYYY"""
d = self.__repr__()
return "%s/%s/%s" % (d[-4:-2], d[-2:], d[-8:-4])
def _fmtM(self):
return str(self.month())
def _fmtMM(self):
return '%02d' % self.month()
def _fmtMMM(self):
return self.monthAbbrev()
def _fmtMMMM(self):
return self.monthName()
def _fmtMMMMM(self):
return self.monthName()[0]
def _fmtD(self):
return str(self.day())
def _fmtDD(self):
return '%02d' % self.day()
def _fmtDDD(self):
return self.dayOfWeekAbbrev()
def _fmtDDDD(self):
return self.dayOfWeekName()
def _fmtYY(self):
return '%02d' % (self.year()%100)
def _fmtYYYY(self):
return str(self.year())
def formatMS(self,fmt):
'''format like MS date using the notation
{YY} --> 2 digit year
{YYYY} --> 4 digit year
{M} --> month as digit
{MM} --> 2 digit month
{MMM} --> abbreviated month name
{MMMM} --> monthname
{MMMMM} --> first character of monthname
{D} --> day of month as digit
{DD} --> 2 digit day of month
{DDD} --> abrreviated weekday name
{DDDD} --> weekday name
'''
r = fmt[:]
f = 0
while 1:
m = _fmtPat.search(r,f)
if m:
y = getattr(self,'_fmt'+string.upper(m.group()[1:-1]))()
i, j = m.span()
r = (r[0:i] + y) + r[j:]
f = i + len(y)
else:
return r
def __getstate__(self):
"""minimize persistent storage requirements"""
return self.normalDate
def __hash__(self):
return hash(self.normalDate)
def __int__(self):
return self.normalDate
def isLeapYear(self):
"""
determine if specified year is leap year, returning true (1) or
false (0)
"""
return isLeapYear(self.year())
def _isValidNormalDate(self, normalDate):
"""checks for date validity in [-]yyyymmdd format"""
if type(normalDate) is not IntType:
return 0
if len(repr(normalDate)) > 9:
return 0
if normalDate < 0:
dateStr = "%09d" % normalDate
else:
dateStr = "%08d" % normalDate
if len(dateStr) < 8:
return 0
elif len(dateStr) == 9:
if (dateStr[0] != '-' and dateStr[0] != '+'):
return 0
year = int(dateStr[:-4])
if year < -9999 or year > 9999 or year == 0:
return 0 # note: zero (0) is not a valid year
month = int(dateStr[-4:-2])
if month < 1 or month > 12:
return 0
if isLeapYear(year):
maxDay = _daysInMonthLeapYear[month - 1]
else:
maxDay = _daysInMonthNormal[month - 1]
day = int(dateStr[-2:])
if day < 1 or day > maxDay:
return 0
if year == 1582 and month == 10 and day > 4 and day < 15:
return 0 # special case of 10 days dropped: Oct 5-14, 1582
return 1
def lastDayOfMonth(self):
"""returns last day of the month as integer 28-31"""
if self.isLeapYear():
return _daysInMonthLeapYear[self.month() - 1]
else:
return _daysInMonthNormal[self.month() - 1]
def localeFormat(self):
"""override this method to use your preferred locale format"""
return self.formatUS()
def month(self):
"""returns month as integer 1-12"""
return int(repr(self.normalDate)[-4:-2])
def monthAbbrev(self):
"""returns month as a 3-character abbreviation, i.e. Jan, Feb, etc."""
return _monthName[self.month() - 1][:3]
def monthName(self):
"""returns month name, i.e. January, February, etc."""
return _monthName[self.month() - 1]
def normalize(self, scalar):
"""convert scalar to normalDate"""
if scalar < _bigBangScalar:
msg = "normalize(%d): scalar below minimum" % \
_bigBangScalar
raise NormalDateException(msg)
if scalar > _bigCrunchScalar:
msg = "normalize(%d): scalar exceeds maximum" % \
_bigCrunchScalar
raise NormalDateException(msg)
from math import floor
if scalar >= -115860:
year = 1600 + int(floor((scalar + 109573) / 365.2425))
elif scalar >= -693597:
year = 4 + int(floor((scalar + 692502) / 365.2425))
else:
year = -4 + int(floor((scalar + 695058) / 365.2425))
days = scalar - firstDayOfYear(year) + 1
if days <= 0:
year = year - 1
days = scalar - firstDayOfYear(year) + 1
daysInYear = 365
if isLeapYear(year):
daysInYear = daysInYear + 1
if days > daysInYear:
year = year + 1
days = scalar - firstDayOfYear(year) + 1
# add 10 days if between Oct 15, 1582 and Dec 31, 1582
if (scalar >= -115860 and scalar <= -115783):
days = days + 10
if isLeapYear(year):
daysByMonth = _daysInMonthLeapYear
else:
daysByMonth = _daysInMonthNormal
dc = 0; month = 12
for m in xrange(len(daysByMonth)):
dc = dc + daysByMonth[m]
if dc >= days:
month = m + 1
break
# add up the days in prior months
priorMonthDays = 0
for m in xrange(month - 1):
priorMonthDays = priorMonthDays + daysByMonth[m]
day = days - priorMonthDays
self.setNormalDate((year, month, day))
def range(self, days):
"""Return a range of normalDates as a list. Parameter
may be an int or normalDate."""
if type(days) is not IntType:
days = days - self # if not int, assume arg is normalDate type
r = []
for i in range(days):
r.append(self + i)
return r
def __repr__(self):
"""print format: [-]yyyymmdd"""
# Note: When disassembling a NormalDate string, be sure to
# count from the right, i.e. epochMonth = int(`Epoch`[-4:-2]),
# or the slice won't work for dates B.C.
if self.normalDate < 0:
return "%09d" % self.normalDate
else:
return "%08d" % self.normalDate
def scalar(self):
"""days since baseline date: Jan 1, 1900"""
(year, month, day) = self.toTuple()
days = firstDayOfYear(year) + day - 1
if self.isLeapYear():
for m in xrange(month - 1):
days = days + _daysInMonthLeapYear[m]
else:
for m in xrange(month - 1):
days = days + _daysInMonthNormal[m]
if year == 1582:
if month > 10 or (month == 10 and day > 4):
days = days - 10
return days
def setDay(self, day):
"""set the day of the month"""
maxDay = self.lastDayOfMonth()
if day < 1 or day > maxDay:
msg = "day is outside of range 1 to %d" % maxDay
raise NormalDateException(msg)
(y, m, d) = self.toTuple()
self.setNormalDate((y, m, day))
def setMonth(self, month):
"""set the month [1-12]"""
if month < 1 or month > 12:
raise NormalDateException('month is outside range 1 to 12')
(y, m, d) = self.toTuple()
self.setNormalDate((y, month, d))
def setNormalDate(self, normalDate):
"""
accepts date as scalar string/integer (yyyymmdd) or tuple
(year, month, day, ...)"""
tn=type(normalDate)
if tn is IntType:
self.normalDate = normalDate
elif tn is StringType:
try:
self.normalDate = int(normalDate)
except:
m = _iso_re.match(normalDate)
if m:
self.setNormalDate(m.group(1)+m.group(2)+m.group(3))
else:
raise NormalDateException("unable to setNormalDate(%s)" % `normalDate`)
elif tn in _DateSeqTypes:
self.normalDate = int("%04d%02d%02d" % normalDate[:3])
elif tn is _NDType:
self.normalDate = normalDate.normalDate
elif isinstance(normalDate,(datetime.datetime,datetime.date)):
self.normalDate = (normalDate.year*100+normalDate.month)*100+normalDate.day
if not self._isValidNormalDate(self.normalDate):
raise NormalDateException("unable to setNormalDate(%s)" % `normalDate`)
def setYear(self, year):
if year == 0:
raise NormalDateException('cannot set year to zero')
elif year < -9999:
raise NormalDateException('year cannot be less than -9999')
elif year > 9999:
raise NormalDateException('year cannot be greater than 9999')
(y, m, d) = self.toTuple()
self.setNormalDate((year, m, d))
__setstate__ = setNormalDate
def __sub__(self, v):
if type(v) is IntType:
return self.__add__(-v)
return self.scalar() - v.scalar()
def __rsub__(self,v):
if type(v) is IntType:
return NormalDate(v) - self
else:
return v.scalar() - self.scalar()
def toTuple(self):
"""return date as (year, month, day) tuple"""
return (self.year(), self.month(), self.day())
def year(self):
"""return year in yyyy format, negative values indicate B.C."""
return int(repr(self.normalDate)[:-4])
################# Utility functions #################
def bigBang():
"""return lower boundary as a NormalDate"""
return NormalDate((-9999, 1, 1))
def bigCrunch():
"""return upper boundary as a NormalDate"""
return NormalDate((9999, 12, 31))
def dayOfWeek(y, m, d):
"""return integer representing day of week, Mon=0, Tue=1, etc."""
if m == 1 or m == 2:
m = m + 12
y = y - 1
return (d + 2*m + 3*(m+1)/5 + y + y/4 - y/100 + y/400) % 7
def firstDayOfYear(year):
"""number of days to the first of the year, relative to Jan 1, 1900"""
if type(year) is not IntType:
msg = "firstDayOfYear() expected integer, got %s" % type(year)
raise NormalDateException(msg)
if year == 0:
raise NormalDateException('first day of year cannot be zero (0)')
elif year < 0: # BCE calculation
firstDay = (year * 365) + int((year - 1) / 4) - 693596
else: # CE calculation
leapAdjust = int((year + 3) / 4)
if year > 1600:
leapAdjust = leapAdjust - int((year + 99 - 1600) / 100) + \
int((year + 399 - 1600) / 400)
firstDay = year * 365 + leapAdjust - 693963
if year > 1582:
firstDay = firstDay - 10
return firstDay
def FND(d):
'''convert to ND if required'''
return (type(d) is _NDType) and d or ND(d)
Epoch=bigBang()
ND=NormalDate
_NDType = type(Epoch)
BDEpoch=ND(15821018)
BDEpochScalar = -115857
class BusinessDate(NormalDate):
"""
Specialised NormalDate
"""
def add(self, days):
"""add days to date; use negative integers to subtract"""
if not type(days) is IntType:
raise NormalDateException('add method parameter must be integer type')
self.normalize(self.scalar() + days)
def __add__(self, days):
"""add integer to BusinessDate and return a new, calculated value"""
if not type(days) is IntType:
raise NormalDateException('__add__ parameter must be integer type')
cloned = self.clone()
cloned.add(days)
return cloned
def __sub__(self, v):
return type(v) is IntType and self.__add__(-v) or self.scalar() - v.scalar()
def asNormalDate(self):
return ND(self.normalDate)
def daysBetweenDates(self, normalDate):
return self.asNormalDate.daysBetweenDates(normalDate)
def _checkDOW(self):
if self.dayOfWeek()>4: raise NormalDateException("%s isn't a business day" % `self.normalDate`)
def normalize(self, i):
i = int(i)
NormalDate.normalize(self,(i/5)*7+i%5+BDEpochScalar)
def scalar(self):
d = self.asNormalDate()
i = d - BDEpoch #luckily BDEpoch is a Monday so we don't have a problem
#concerning the relative weekday
return 5*(i/7) + i%7
def setNormalDate(self, normalDate):
NormalDate.setNormalDate(self,normalDate)
self._checkDOW()
if __name__ == '__main__':
today = NormalDate()
print "NormalDate test:"
print " Today (%s) is: %s %s" % (today, today.dayOfWeekAbbrev(), today.localeFormat())
yesterday = today - 1
print " Yesterday was: %s %s" % (yesterday.dayOfWeekAbbrev(), yesterday.localeFormat())
tomorrow = today + 1
print " Tomorrow will be: %s %s" % (tomorrow.dayOfWeekAbbrev(), tomorrow.localeFormat())
print " Days between tomorrow and yesterday: %d" % (tomorrow - yesterday)
print today.formatMS('{d}/{m}/{yy}')
print today.formatMS('{dd}/{m}/{yy}')
print today.formatMS('{ddd} {d}/{m}/{yy}')
print today.formatMS('{dddd} {d}/{m}/{yy}')
print today.formatMS('{d}/{mm}/{yy}')
print today.formatMS('{d}/{mmm}/{yy}')
print today.formatMS('{d}/{mmmm}/{yy}')
print today.formatMS('{d}/{m}/{yyyy}')
b = BusinessDate('20010116')
print 'b=',b,'b.scalar()', b.scalar()

View File

@ -0,0 +1,55 @@
#!/bin/env python
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/pagesizes.py
"""This module defines a few common page sizes in points (1/72 inch).
To be expanded to include things like label sizes, envelope windows
etc."""
__version__=''' $Id: pagesizes.py 2385 2004-06-17 15:26:05Z rgbecker $ '''
from reportlab.lib.units import cm, inch
_W, _H = (21*cm, 29.7*cm)
A6 = (_W*.5, _H*.5)
A5 = (_H*.5, _W)
A4 = (_W, _H)
A3 = (_H, _W*2)
A2 = (_W*2, _H*2)
A1 = (_H*2, _W*4)
A0 = (_W*4, _H*4)
LETTER = (8.5*inch, 11*inch)
LEGAL = (8.5*inch, 14*inch)
ELEVENSEVENTEEN = (11*inch, 17*inch)
# lower case is deprecated as of 12/2001, but here
# for compatability
letter=LETTER
legal=LEGAL
elevenSeventeen = ELEVENSEVENTEEN
_BW, _BH = (25*cm, 35.3*cm)
B6 = (_BW*.5, _BH*.5)
B5 = (_BH*.5, _BW)
B4 = (_BW, _BH)
B3 = (_BH*2, _BW)
B2 = (_BW*2, _BH*2)
B1 = (_BH*4, _BW*2)
B0 = (_BW*4, _BH*4)
def landscape(pagesize):
"""Use this to get page orientation right"""
a, b = pagesize
if a < b:
return (b, a)
else:
return (a, b)
def portrait(pagesize):
"""Use this to get page orientation right"""
a, b = pagesize
if a >= b:
return (b, a)
else:
return (a, b)

View File

@ -0,0 +1,348 @@
#!/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/lib/randomtext.py
__version__=''' $Id: randomtext.py 2436 2004-09-11 14:18:33Z rgbecker $ '''
###############################################################################
# generates so-called 'Greek Text' for use in filling documents.
###############################################################################
"""
This module exposes a function randomText() which generates paragraphs.
These can be used when testing out document templates and stylesheets.
A number of 'themes' are provided - please contribute more!
We need some real Greek text too.
There are currently six themes provided:
STARTUP (words suitable for a business plan - or not as the case may be),
COMPUTERS (names of programming languages and operating systems etc),
BLAH (variations on the word 'blah'),
BUZZWORD (buzzword bingo),
STARTREK (Star Trek),
PRINTING (print-related terms)
PYTHON (snippets and quotes from Monty Python)
CHOMSKY (random lingusitic nonsense)
EXAMPLE USAGE:
from reportlab.lib import randomtext
print randomtext.randomText(randomtext.PYTHON, 10)
This prints a random number of random sentences (up to a limit
of ten) using the theme 'PYTHON'.
"""
#theme one :-)
STARTUP = ['strategic', 'direction', 'proactive', 'venture capital',
'reengineering', 'forecast', 'resources', 'SWOT analysis',
'forward-thinking', 'profit', 'growth', 'doubletalk', 'B2B', 'B2C',
'venture capital', 'IPO', "NASDAQ meltdown - we're all doomed!"]
#theme two - computery things.
COMPUTERS = ['Python', 'Perl', 'Pascal', 'Java', 'Javascript',
'VB', 'Basic', 'LISP', 'Fortran', 'ADA', 'APL', 'C', 'C++',
'assembler', 'Larry Wall', 'Guido van Rossum', 'XML', 'HTML',
'cgi', 'cgi-bin', 'Amiga', 'Macintosh', 'Dell', 'Microsoft',
'firewall', 'server', 'Linux', 'Unix', 'MacOS', 'BeOS', 'AS/400',
'sendmail', 'TCP/IP', 'SMTP', 'RFC822-compliant', 'dynamic',
'Internet', 'A/UX', 'Amiga OS', 'BIOS', 'boot managers', 'CP/M',
'DOS', 'file system', 'FreeBSD', 'Freeware', 'GEOS', 'GNU',
'Hurd', 'Linux', 'Mach', 'Macintosh OS', 'mailing lists', 'Minix',
'Multics', 'NetWare', 'NextStep', 'OS/2', 'Plan 9', 'Realtime',
'UNIX', 'VMS', 'Windows', 'X Windows', 'Xinu', 'security', 'Intel',
'encryption', 'PGP' , 'software', 'ActiveX', 'AppleScript', 'awk',
'BETA', 'COBOL', 'Delphi', 'Dylan', 'Eiffel', 'extreme programming',
'Forth', 'Fortran', 'functional languages', 'Guile', 'format your hard drive',
'Icon', 'IDL', 'Infer', 'Intercal', 'J', 'Java', 'JavaScript', 'CD-ROM',
'JCL', 'Lisp', '"literate programming"', 'Logo', 'MUMPS', 'C: drive',
'Modula-2', 'Modula-3', 'Oberon', 'Occam', 'OpenGL', 'parallel languages',
'Pascal', 'Perl', 'PL/I', 'PostScript', 'Prolog', 'hardware', 'Blue Screen of Death',
'Rexx', 'RPG', 'Scheme', 'scripting languages', 'Smalltalk', 'crash!', 'disc crash',
'Spanner', 'SQL', 'Tcl/Tk', 'TeX', 'TOM', 'Visual', 'Visual Basic', '4GL',
'VRML', 'Virtual Reality Modeling Language', 'difference engine', '...went into "yo-yo mode"',
'Sun', 'Sun Microsystems', 'Hewlett Packard', 'output device',
'CPU', 'memory', 'registers', 'monitor', 'TFT display', 'plasma screen',
'bug report', '"mis-feature"', '...millions of bugs!', 'pizza',
'"illiterate programming"','...lots of pizza!', 'pepperoni pizza',
'coffee', 'Jolt Cola[TM]', 'beer', 'BEER!']
#theme three - 'blah' - for when you want to be subtle. :-)
BLAH = ['Blah', 'BLAH', 'blahblah', 'blahblahblah', 'blah-blah',
'blah!', '"Blah Blah Blah"', 'blah-de-blah', 'blah?', 'blah!!!',
'blah...', 'Blah.', 'blah;', 'blah, Blah, BLAH!', 'Blah!!!']
#theme four - 'buzzword bingo' time!
BUZZWORD = ['intellectual capital', 'market segment', 'flattening',
'regroup', 'platform', 'client-based', 'long-term', 'proactive',
'quality vector', 'out of the loop', 'implement',
'streamline', 'cost-centered', 'phase', 'synergy',
'synergize', 'interactive', 'facilitate',
'appropriate', 'goal-setting', 'empowering', 'low-risk high-yield',
'peel the onion', 'goal', 'downsize', 'result-driven',
'conceptualize', 'multidisciplinary', 'gap analysis', 'dysfunctional',
'networking', 'knowledge management', 'goal-setting',
'mastery learning', 'communication', 'real-estate', 'quarterly',
'scalable', 'Total Quality Management', 'best of breed',
'nimble', 'monetize', 'benchmark', 'hardball',
'client-centered', 'vision statement', 'empowerment',
'lean & mean', 'credibility', 'synergistic',
'backward-compatible', 'hardball', 'stretch the envelope',
'bleeding edge', 'networking', 'motivation', 'best practice',
'best of breed', 'implementation', 'Total Quality Management',
'undefined', 'disintermediate', 'mindset', 'architect',
'gap analysis', 'morale', 'objective', 'projection',
'contribution', 'proactive', 'go the extra mile', 'dynamic',
'world class', 'real estate', 'quality vector', 'credibility',
'appropriate', 'platform', 'projection', 'mastery learning',
'recognition', 'quality', 'scenario', 'performance based',
'solutioning', 'go the extra mile', 'downsize', 'phase',
'networking', 'experiencing slippage', 'knowledge management',
'high priority', 'process', 'ethical', 'value-added', 'implement',
're-factoring', 're-branding', 'embracing change']
#theme five - Star Trek
STARTREK = ['Starfleet', 'Klingon', 'Romulan', 'Cardassian', 'Vulcan',
'Benzite', 'IKV Pagh', 'emergency transponder', 'United Federation of Planets',
'Bolian', "K'Vort Class Bird-of-Prey", 'USS Enterprise', 'USS Intrepid',
'USS Reliant', 'USS Voyager', 'Starfleet Academy', 'Captain Picard',
'Captain Janeway', 'Tom Paris', 'Harry Kim', 'Counsellor Troi',
'Lieutenant Worf', 'Lieutenant Commander Data', 'Dr. Beverly Crusher',
'Admiral Nakamura', 'Irumodic Syndrome', 'Devron system', 'Admiral Pressman',
'asteroid field', 'sensor readings', 'Binars', 'distress signal', 'shuttlecraft',
'cloaking device', 'shuttle bay 2', 'Dr. Pulaski', 'Lwaxana Troi', 'Pacifica',
'William Riker', "Chief O'Brian", 'Soyuz class science vessel', 'Wolf-359',
'Galaxy class vessel', 'Utopia Planitia yards', 'photon torpedo', 'Archer IV',
'quantum flux', 'spacedock', 'Risa', 'Deep Space Nine', 'blood wine',
'quantum torpedoes', 'holodeck', 'Romulan Warbird', 'Betazoid', 'turbolift', 'battle bridge',
'Memory Alpha', '...with a phaser!', 'Romulan ale', 'Ferrengi', 'Klingon opera',
'Quark', 'wormhole', 'Bajoran', 'cruiser', 'warship', 'battlecruiser', '"Intruder alert!"',
'scout ship', 'science vessel', '"Borg Invasion imminent!" ', '"Abandon ship!"',
'Red Alert!', 'warp-core breech', '"All hands abandon ship! This is not a drill!"']
#theme six - print-related terms
PRINTING = ['points', 'picas', 'leading', 'kerning', 'CMYK', 'offset litho',
'type', 'font family', 'typography', 'type designer',
'baseline', 'white-out type', 'WOB', 'bicameral', 'bitmap',
'blockletter', 'bleed', 'margin', 'body', 'widow', 'orphan',
'cicero', 'cursive', 'letterform', 'sidehead', 'dingbat', 'leader',
'DPI', 'drop-cap', 'paragraph', 'En', 'Em', 'flush left', 'left justified',
'right justified', 'centered', 'italic', 'Latin letterform', 'ligature',
'uppercase', 'lowercase', 'serif', 'sans-serif', 'weight', 'type foundry',
'fleuron', 'folio', 'gutter', 'whitespace', 'humanist letterform', 'caption',
'page', 'frame', 'ragged setting', 'flush-right', 'rule', 'drop shadows',
'prepress', 'spot-colour', 'duotones', 'colour separations', 'four-colour printing',
'Pantone[TM]', 'service bureau', 'imagesetter']
#it had to be done!...
#theme seven - the "full Monty"!
PYTHON = ['Good evening ladies and Bruces','I want to buy some cheese', 'You do have some cheese, do you?',
"Of course sir, it's a cheese shop sir, we've got...",'discipline?... naked? ... With a melon!?',
'The Church Police!!' , "There's a dead bishop on the landing", 'Would you like a twist of lemming sir?',
'"Conquistador Coffee brings a new meaning to the word vomit"','Your lupins please',
'Crelm Toothpaste, with the miracle ingredient Fraudulin',
"Well there's the first result and the Silly Party has held Leicester.",
'Hello, I would like to buy a fish license please', "Look, it's people like you what cause unrest!",
"When we got home, our Dad would thrash us to sleep with his belt!", 'Luxury', "Gumby Brain Specialist",
"My brain hurts!!!", "My brain hurts too.", "How not to be seen",
"In this picture there are 47 people. None of them can be seen",
"Mrs Smegma, will you stand up please?",
"Mr. Nesbitt has learned the first lesson of 'Not Being Seen', not to stand up.",
"My hovercraft is full of eels", "Ah. You have beautiful thighs.", "My nipples explode with delight",
"Drop your panties Sir William, I cannot wait 'til lunchtime",
"I'm a completely self-taught idiot.", "I always wanted to be a lumberjack!!!",
"Told you so!! Oh, coitus!!", "",
"Nudge nudge?", "Know what I mean!", "Nudge nudge, nudge nudge?", "Say no more!!",
"Hello, well it's just after 8 o'clock, and time for the penguin on top of your television set to explode",
"Oh, intercourse the penguin!!", "Funny that penguin being there, isn't it?",
"I wish to register a complaint.", "Now that's what I call a dead parrot", "Pining for the fjords???",
"No, that's not dead, it's ,uhhhh, resting", "This is an ex-parrot!!",
"That parrot is definitely deceased.", "No, no, no - it's spelt Raymond Luxury Yach-t, but it's pronounced 'Throatwobbler Mangrove'.",
"You're a very silly man and I'm not going to interview you.", "No Mungo... never kill a customer."
"And I'd like to conclude by putting my finger up my nose",
"egg and Spam", "egg bacon and Spam", "egg bacon sausage and Spam", "Spam bacon sausage and Spam",
"Spam egg Spam Spam bacon and Spam", "Spam sausage Spam Spam Spam bacon Spam tomato and Spam",
"Spam Spam Spam egg and Spam", "Spam Spam Spam Spam Spam Spam baked beans Spam Spam Spam",
"Spam!!", "I don't like Spam!!!", "You can't have egg, bacon, Spam and sausage without the Spam!",
"I'll have your Spam. I Love it!",
"I'm having Spam Spam Spam Spam Spam Spam Spam baked beans Spam Spam Spam and Spam",
"Have you got anything without Spam?", "There's Spam egg sausage and Spam, that's not got much Spam in it.",
"No one expects the Spanish Inquisition!!", "Our weapon is surprise, surprise and fear!",
"Get the comfy chair!", "Amongst our weaponry are such diverse elements as: fear, surprise, ruthless efficiency, an almost fanatical devotion to the Pope, and nice red uniforms - Oh damn!",
"Nobody expects the... Oh bugger!", "What swims in the sea and gets caught in nets? Henri Bergson?",
"Goats. Underwater goats with snorkels and flippers?", "A buffalo with an aqualung?",
"Dinsdale was a looney, but he was a happy looney.", "Dinsdale!!",
"The 127th Upper-Class Twit of the Year Show", "What a great Twit!",
"thought by many to be this year's outstanding twit",
"...and there's a big crowd here today to see these prize idiots in action.",
"And now for something completely different.", "Stop that, it's silly",
"We interrupt this program to annoy you and make things generally irritating",
"This depraved and degrading spectacle is going to stop right now, do you hear me?",
"Stop right there!", "This is absolutely disgusting and I'm not going to stand for it",
"I object to all this sex on the television. I mean, I keep falling off",
"Right! Stop that, it's silly. Very silly indeed", "Very silly indeed", "Lemon curry?",
"And now for something completely different, a man with 3 buttocks",
"I've heard of unisex, but I've never had it", "That's the end, stop the program! Stop it!"]
leadins=[
"To characterize a linguistic level L,",
"On the other hand,",
"This suggests that",
"It appears that",
"Furthermore,",
"We will bring evidence in favor of the following thesis: ",
"To provide a constituent structure for T(Z,K),",
"From C1, it follows that",
"For any transformation which is sufficiently diversified in application to be of any interest,",
"Analogously,",
"Clearly,",
"Note that",
"Of course,",
"Suppose, for instance, that",
"Thus",
"With this clarification,",
"Conversely,",
"We have already seen that",
"By combining adjunctions and certain deformations,",
"I suggested that these results would follow from the assumption that",
"If the position of the trace in (99c) were only relatively inaccessible to movement,",
"However, this assumption is not correct, since",
"Comparing these examples with their parasitic gap counterparts in (96) and (97), we see that",
"In the discussion of resumptive pronouns following (81),",
"So far,",
"Nevertheless,",
"For one thing,",
"Summarizing, then, we assume that",
"A consequence of the approach just outlined is that",
"Presumably,",
"On our assumptions,",
"It may be, then, that",
"It must be emphasized, once again, that",
"Let us continue to suppose that",
"Notice, incidentally, that",
"A majority of informed linguistic specialists agree that",
]
subjects = [
"the notion of level of grammaticalness",
"a case of semigrammaticalness of a different sort",
"most of the methodological work in modern linguistics",
"a subset of English sentences interesting on quite independent grounds",
"the natural general principle that will subsume this case",
"an important property of these three types of EC",
"any associated supporting element",
"the appearance of parasitic gaps in domains relatively inaccessible to ordinary extraction",
"the speaker-hearer's linguistic intuition",
"the descriptive power of the base component",
"the earlier discussion of deviance",
"this analysis of a formative as a pair of sets of features",
"this selectionally introduced contextual feature",
"a descriptively adequate grammar",
"the fundamental error of regarding functional notions as categorial",
"relational information",
"the systematic use of complex symbols",
"the theory of syntactic features developed earlier",
]
verbs= [
"can be defined in such a way as to impose",
"delimits",
"suffices to account for",
"cannot be arbitrary in",
"is not subject to",
"does not readily tolerate",
"raises serious doubts about",
"is not quite equivalent to",
"does not affect the structure of",
"may remedy and, at the same time, eliminate",
"is not to be considered in determining",
"is to be regarded as",
"is unspecified with respect to",
"is, apparently, determined by",
"is necessary to impose an interpretation on",
"appears to correlate rather closely with",
"is rather different from",
]
objects = [
"problems of phonemic and morphological analysis.",
"a corpus of utterance tokens upon which conformity has been defined by the paired utterance test.",
"the traditional practice of grammarians.",
"the levels of acceptability from fairly high (e.g. (99a)) to virtual gibberish (e.g. (98d)).",
"a stipulation to place the constructions into these various categories.",
"a descriptive fact.",
"a parasitic gap construction.",
"the extended c-command discussed in connection with (34).",
"the ultimate standard that determines the accuracy of any proposed grammar.",
"the system of base rules exclusive of the lexicon.",
"irrelevant intervening contexts in selectional rules.",
"nondistinctness in the sense of distinctive feature theory.",
"a general convention regarding the forms of the grammar.",
"an abstract underlying order.",
"an important distinction in language use.",
"the requirement that branching is not tolerated within the dominance scope of a complex symbol.",
"the strong generative capacity of the theory.",
]
def format_wisdom(text,line_length=72):
try:
import textwrap
return textwrap.fill(text, line_length)
except:
return text
def chomsky(times = 1):
if not isinstance(times, int):
return format_wisdom(__doc__)
import random
prevparts = []
newparts = []
output = []
for i in xrange(times):
for partlist in (leadins, subjects, verbs, objects):
while 1:
part = random.choice(partlist)
if part not in prevparts:
break
newparts.append(part)
output.append(' '.join(newparts))
prevparts = newparts
newparts = []
return format_wisdom(' '.join(output))
from reportlab import rl_config
if rl_config.invariant:
if not getattr(rl_config,'_random',None):
rl_config._random = 1
import random
random.seed(2342471922L)
del random
del rl_config
def randomText(theme=STARTUP, sentences=5):
#this may or may not be appropriate in your company
if type(theme)==type(''):
if theme.lower()=='chomsky': return chomsky(sentences)
elif theme.upper() in ('STARTUP','COMPUTERS','BLAH','BUZZWORD','STARTREK','PRINTING','PYTHON'):
theme = globals()[theme]
else:
raise ValueError('Unknown theme "%s"' % theme)
from random import randint, choice
RANDOMWORDS = theme
#sentences = 5
output = ""
for sentenceno in range(randint(1,sentences)):
output = output + 'Blah'
for wordno in range(randint(10,25)):
if randint(0,4)==0:
word = choice(RANDOMWORDS)
else:
word = 'blah'
output = output + ' ' +word
output = output+'. '
return output
if __name__=='__main__':
print chomsky(5)

View File

@ -0,0 +1,29 @@
#Copyright ReportLab Europe Ltd. 2000-2006
#see license.txt for license details
# $URI:$
__version__=''' $Id: rltempfile.py 2892 2006-05-19 14:16:02Z rgbecker $ '''
_rl_tempdir=None
__all__ = ('get_rl_tempdir', 'get_rl_tempdir')
import os, tempfile
def _rl_getuid():
if hasattr(os,'getuid'):
return os.getuid()
else:
return ''
def get_rl_tempdir(*subdirs):
global _rl_tempdir
if _rl_tempdir is None:
_rl_tempdir = os.path.join(tempfile.gettempdir(),'ReportLab_tmp%s' % str(_rl_getuid()))
d = _rl_tempdir
if subdirs: d = os.path.join(*((d,)+subdirs))
try:
os.makedirs(d)
except:
pass
return d
def get_rl_tempfile(fn=None):
if not fn:
fn = tempfile.mktemp()
return os.path.join(get_rl_tempdir(),fn)

View File

@ -0,0 +1,431 @@
"""Radically simple xml parsing
Example parse
<this type="xml">text <b>in</b> xml</this>
( "this",
{"type": "xml"},
[ "text ",
("b", None, ["in"], None),
" xml"
]
None )
{ 0: "this"
"type": "xml"
1: ["text ",
{0: "b", 1:["in"]},
" xml"]
}
Ie, xml tag translates to a tuple:
(name, dictofattributes, contentlist, miscellaneousinfo)
where miscellaneousinfo can be anything, (but defaults to None)
(with the intention of adding, eg, line number information)
special cases: name of "" means "top level, no containing tag".
Top level parse always looks like this
("", list, None, None)
contained text of None means <simple_tag\>
In order to support stuff like
<this></this><one></one>
AT THE MOMENT &amp; ETCETERA ARE IGNORED. THEY MUST BE PROCESSED
IN A POST-PROCESSING STEP.
PROLOGUES ARE NOT UNDERSTOOD. OTHER STUFF IS PROBABLY MISSING.
"""
RequirePyRXP = 0 # set this to 1 to disable the nonvalidating fallback parser.
import string
try:
#raise ImportError, "dummy error"
simpleparse = 0
import pyRXPU
def warnCB(s):
print s
pyRXP_parser = pyRXPU.Parser(
ErrorOnValidityErrors=1,
NoNoDTDWarning=1,
ExpandCharacterEntities=1,
ExpandGeneralEntities=1,
warnCB = warnCB,
srcName='string input',
ReturnUTF8 = 1,
)
def parsexml(xmlText, oneOutermostTag=0,eoCB=None,entityReplacer=None):
pyRXP_parser.eoCB = eoCB
p = pyRXP_parser.parse(xmlText)
return oneOutermostTag and p or ('',None,[p],None)
except ImportError:
simpleparse = 1
NONAME = ""
NAMEKEY = 0
CONTENTSKEY = 1
CDATAMARKER = "<![CDATA["
LENCDATAMARKER = len(CDATAMARKER)
CDATAENDMARKER = "]]>"
replacelist = [("&lt;", "<"), ("&gt;", ">"), ("&amp;", "&")] # amp must be last
#replacelist = []
def unEscapeContentList(contentList):
result = []
from string import replace
for e in contentList:
if "&" in e:
for (old, new) in replacelist:
e = replace(e, old, new)
result.append(e)
return result
def parsexmlSimple(xmltext, oneOutermostTag=0,eoCB=None,entityReplacer=unEscapeContentList):
"""official interface: discard unused cursor info"""
if RequirePyRXP:
raise ImportError, "pyRXP not found, fallback parser disabled"
(result, cursor) = parsexml0(xmltext,entityReplacer=entityReplacer)
if oneOutermostTag:
return result[2][0]
else:
return result
if simpleparse:
parsexml = parsexmlSimple
def parseFile(filename):
raw = open(filename, 'r').read()
return parsexml(raw)
verbose = 0
def skip_prologue(text, cursor):
"""skip any prologue found after cursor, return index of rest of text"""
### NOT AT ALL COMPLETE!!! definitely can be confused!!!
from string import find
prologue_elements = ("!DOCTYPE", "?xml", "!--")
done = None
while done is None:
#print "trying to skip:", repr(text[cursor:cursor+20])
openbracket = find(text, "<", cursor)
if openbracket<0: break
past = openbracket+1
found = None
for e in prologue_elements:
le = len(e)
if text[past:past+le]==e:
found = 1
cursor = find(text, ">", past)
if cursor<0:
raise ValueError, "can't close prologue %s" % `e`
cursor = cursor+1
if found is None:
done=1
#print "done skipping"
return cursor
def parsexml0(xmltext, startingat=0, toplevel=1,
# snarf in some globals
strip=string.strip, split=string.split, find=string.find, entityReplacer=unEscapeContentList,
#len=len, None=None
#LENCDATAMARKER=LENCDATAMARKER, CDATAMARKER=CDATAMARKER
):
"""simple recursive descent xml parser...
return (dictionary, endcharacter)
special case: comment returns (None, endcharacter)"""
#from string import strip, split, find
#print "parsexml0", `xmltext[startingat: startingat+10]`
# DEFAULTS
NameString = NONAME
ContentList = AttDict = ExtraStuff = None
if toplevel is not None:
#if verbose: print "at top level"
#if startingat!=0:
# raise ValueError, "have to start at 0 for top level!"
xmltext = strip(xmltext)
cursor = startingat
#look for interesting starting points
firstbracket = find(xmltext, "<", cursor)
afterbracket2char = xmltext[firstbracket+1:firstbracket+3]
#print "a", `afterbracket2char`
#firstampersand = find(xmltext, "&", cursor)
#if firstampersand>0 and firstampersand<firstbracket:
# raise ValueError, "I don't handle ampersands yet!!!"
docontents = 1
if firstbracket<0:
# no tags
#if verbose: print "no tags"
if toplevel is not None:
#D = {NAMEKEY: NONAME, CONTENTSKEY: [xmltext[cursor:]]}
ContentList = [xmltext[cursor:]]
if entityReplacer: ContentList = entityReplacer(ContentList)
return (NameString, AttDict, ContentList, ExtraStuff), len(xmltext)
else:
raise ValueError, "no tags at non-toplevel %s" % `xmltext[cursor:cursor+20]`
#D = {}
L = []
# look for start tag
# NEED to force always outer level is unnamed!!!
#if toplevel and firstbracket>0:
#afterbracket2char = xmltext[firstbracket:firstbracket+2]
if toplevel is not None:
#print "toplevel with no outer tag"
NameString = name = NONAME
cursor = skip_prologue(xmltext, cursor)
#break
elif firstbracket<0:
raise ValueError, "non top level entry should be at start tag: %s" % repr(xmltext[:10])
# special case: CDATA
elif afterbracket2char=="![" and xmltext[firstbracket:firstbracket+9]=="<![CDATA[":
#print "in CDATA", cursor
# skip straight to the close marker
startcdata = firstbracket+9
endcdata = find(xmltext, CDATAENDMARKER, startcdata)
if endcdata<0:
raise ValueError, "unclosed CDATA %s" % repr(xmltext[cursor:cursor+20])
NameString = CDATAMARKER
ContentList = [xmltext[startcdata: endcdata]]
cursor = endcdata+len(CDATAENDMARKER)
docontents = None
# special case COMMENT
elif afterbracket2char=="!-" and xmltext[firstbracket:firstbracket+4]=="<!--":
#print "in COMMENT"
endcommentdashes = find(xmltext, "--", firstbracket+4)
if endcommentdashes<firstbracket:
raise ValueError, "unterminated comment %s" % repr(xmltext[cursor:cursor+20])
endcomment = endcommentdashes+2
if xmltext[endcomment]!=">":
raise ValueError, "invalid comment: contains double dashes %s" % repr(xmltext[cursor:cursor+20])
return (None, endcomment+1) # shortcut exit
else:
# get the rest of the tag
#if verbose: print "parsing start tag"
# make sure the tag isn't in doublequote pairs
closebracket = find(xmltext, ">", firstbracket)
noclose = closebracket<0
startsearch = closebracket+1
pastfirstbracket = firstbracket+1
tagcontent = xmltext[pastfirstbracket:closebracket]
# shortcut, no equal means nothing but name in the tag content
if '=' not in tagcontent:
if tagcontent[-1]=="/":
# simple case
#print "simple case", tagcontent
tagcontent = tagcontent[:-1]
docontents = None
name = strip(tagcontent)
NameString = name
cursor = startsearch
else:
if '"' in tagcontent:
# check double quotes
stop = None
# not inside double quotes! (the split should have odd length)
if noclose or len(split(tagcontent+".", '"'))% 2:
stop=1
while stop is None:
closebracket = find(xmltext, ">", startsearch)
startsearch = closebracket+1
noclose = closebracket<0
tagcontent = xmltext[pastfirstbracket:closebracket]
# not inside double quotes! (the split should have odd length)
if noclose or len(split(tagcontent+".", '"'))% 2:
stop=1
if noclose:
raise ValueError, "unclosed start tag %s" % repr(xmltext[firstbracket:firstbracket+20])
cursor = startsearch
#cursor = closebracket+1
# handle simple tag /> syntax
if xmltext[closebracket-1]=="/":
#if verbose: print "it's a simple tag"
closebracket = closebracket-1
tagcontent = tagcontent[:-1]
docontents = None
#tagcontent = xmltext[firstbracket+1:closebracket]
tagcontent = strip(tagcontent)
taglist = split(tagcontent, "=")
#if not taglist:
# raise ValueError, "tag with no name %s" % repr(xmltext[firstbracket:firstbracket+20])
taglist0 = taglist[0]
taglist0list = split(taglist0)
#if len(taglist0list)>2:
# raise ValueError, "bad tag head %s" % repr(taglist0)
name = taglist0list[0]
#print "tag name is", name
NameString = name
# now parse the attributes
attributename = taglist0list[-1]
# put a fake att name at end of last taglist entry for consistent parsing
taglist[-1] = taglist[-1]+" f"
AttDict = D = {}
taglistindex = 1
lasttaglistindex = len(taglist)
#for attentry in taglist[1:]:
while taglistindex<lasttaglistindex:
#print "looking for attribute named", attributename
attentry = taglist[taglistindex]
taglistindex = taglistindex+1
attentry = strip(attentry)
if attentry[0]!='"':
raise ValueError, "attribute value must start with double quotes" + repr(attentry)
while '"' not in attentry[1:]:
# must have an = inside the attribute value...
if taglistindex>lasttaglistindex:
raise ValueError, "unclosed value " + repr(attentry)
nextattentry = taglist[taglistindex]
taglistindex = taglistindex+1
attentry = "%s=%s" % (attentry, nextattentry)
attentry = strip(attentry) # only needed for while loop...
attlist = split(attentry)
nextattname = attlist[-1]
attvalue = attentry[:-len(nextattname)]
attvalue = strip(attvalue)
try:
first = attvalue[0]; last=attvalue[-1]
except:
raise ValueError, "attvalue,attentry,attlist="+repr((attvalue, attentry,attlist))
if first==last=='"' or first==last=="'":
attvalue = attvalue[1:-1]
#print attributename, "=", attvalue
D[attributename] = attvalue
attributename = nextattname
# pass over other tags and content looking for end tag
if docontents is not None:
#print "now looking for end tag"
ContentList = L
while docontents is not None:
nextopenbracket = find(xmltext, "<", cursor)
if nextopenbracket<cursor:
#if verbose: print "no next open bracket found"
if name==NONAME:
#print "no more tags for noname", repr(xmltext[cursor:cursor+10])
docontents=None # done
remainder = xmltext[cursor:]
cursor = len(xmltext)
if remainder:
L.append(remainder)
else:
raise ValueError, "no close bracket for %s found after %s" % (name,repr(xmltext[cursor: cursor+20]))
# is it a close bracket?
elif xmltext[nextopenbracket+1]=="/":
#print "found close bracket", repr(xmltext[nextopenbracket:nextopenbracket+20])
nextclosebracket = find(xmltext, ">", nextopenbracket)
if nextclosebracket<nextopenbracket:
raise ValueError, "unclosed close tag %s" % repr(xmltext[nextopenbracket: nextopenbracket+20])
closetagcontents = xmltext[nextopenbracket+2: nextclosebracket]
closetaglist = split(closetagcontents)
#if len(closetaglist)!=1:
#print closetagcontents
#raise ValueError, "bad close tag format %s" % repr(xmltext[nextopenbracket: nextopenbracket+20])
# name should match
closename = closetaglist[0]
#if verbose: print "closetag name is", closename
if name!=closename:
prefix = xmltext[:cursor]
endlinenum = len(split(prefix, "\n"))
prefix = xmltext[:startingat]
linenum = len(split(prefix, "\n"))
raise ValueError, \
"at lines %s...%s close tag name doesn't match %s...%s %s" %(
linenum, endlinenum, `name`, `closename`, repr(xmltext[cursor: cursor+100]))
remainder = xmltext[cursor:nextopenbracket]
if remainder:
#if verbose: print "remainder", repr(remainder)
L.append(remainder)
cursor = nextclosebracket+1
#print "for", name, "found close tag"
docontents = None # done
# otherwise we are looking at a new tag, recursively parse it...
# first record any intervening content
else:
remainder = xmltext[cursor:nextopenbracket]
if remainder:
L.append(remainder)
#if verbose:
# #print "skipping", repr(remainder)
# #print "--- recursively parsing starting at", xmltext[nextopenbracket:nextopenbracket+20]
(parsetree, cursor) = parsexml0(xmltext, startingat=nextopenbracket, toplevel=None, entityReplacer=entityReplacer)
if parsetree:
L.append(parsetree)
# maybe should check for trailing garbage?
# toplevel:
# remainder = strip(xmltext[cursor:])
# if remainder:
# raise ValueError, "trailing garbage at top level %s" % repr(remainder[:20])
if ContentList:
if entityReplacer: ContentList = entityReplacer(ContentList)
t = (NameString, AttDict, ContentList, ExtraStuff)
return (t, cursor)
import types
def pprettyprint(parsedxml):
"""pretty printer mainly for testing"""
st = types.StringType
if type(parsedxml) is st:
return parsedxml
(name, attdict, textlist, extra) = parsedxml
if not attdict: attdict={}
join = string.join
attlist = []
for k in attdict.keys():
v = attdict[k]
attlist.append("%s=%s" % (k, `v`))
attributes = join(attlist, " ")
if not name and attributes:
raise ValueError, "name missing with attributes???"
if textlist is not None:
# with content
textlistpprint = map(pprettyprint, textlist)
textpprint = join(textlistpprint, "\n")
if not name:
return textpprint # no outer tag
# indent it
nllist = string.split(textpprint, "\n")
textpprint = " "+join(nllist, "\n ")
return "<%s %s>\n%s\n</%s>" % (name, attributes, textpprint, name)
# otherwise must be a simple tag
return "<%s %s/>" % (name, attributes)
dump = 0
def testparse(s):
from time import time
from pprint import pprint
now = time()
D = parsexmlSimple(s)
print "DONE", time()-now
if dump&4:
pprint(D)
#pprint(D)
if dump&1:
print "============== reformatting"
p = pprettyprint(D)
print p
def test():
testparse("""<this type="xml">text &lt;&gt;<b>in</b> <funnytag foo="bar"/> xml</this>
<!-- comment -->
<![CDATA[
<this type="xml">text <b>in</b> xml</this> ]]>
<tag with="<brackets in values>">just testing brackets feature</tag>
""")
filenames = [ #"../../reportlab/demos/pythonpoint/pythonpoint.xml",
"samples/hamlet.xml"]
#filenames = ["moa.xml"]
dump=1
if __name__=="__main__":
test()
from time import time
now = time()
for f in filenames:
t = open(f).read()
print "parsing", f
testparse(t)
print "elapsed", time()-now

View File

@ -0,0 +1,284 @@
#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/lib/sequencer.py
__version__=''' $Id: sequencer.py 2423 2004-08-24 11:36:22Z rgbecker $ '''
"""This module defines a single public class, Sequencer, which aids in
numbering and formatting lists."""
#
# roman numbers conversion thanks to
#
# fredrik lundh, november 1996 (based on a C hack from 1984)
#
# fredrik@pythonware.com
# http://www.pythonware.com
_RN_TEMPLATES = [ 0, 01, 011, 0111, 012, 02, 021, 0211, 02111, 013 ]
_RN_LETTERS = "IVXLCDM"
from string import lower
def _format_I(value):
if value < 0 or value > 3999:
raise ValueError, "illegal value"
str = ""
base = -1
while value:
value, index = divmod(value, 10)
tmp = _RN_TEMPLATES[index]
while tmp:
tmp, index = divmod(tmp, 8)
str = _RN_LETTERS[index+base] + str
base = base + 2
return str
def _format_i(num):
return lower(_format_I(num))
def _format_123(num):
"""The simplest formatter"""
return str(num)
def _format_ABC(num):
"""Uppercase. Wraps around at 26."""
n = (num -1) % 26
return chr(n+65)
def _format_abc(num):
"""Lowercase. Wraps around at 26."""
n = (num -1) % 26
return chr(n+97)
class _Counter:
"""Private class used by Sequencer. Each counter
knows its format, and the IDs of anything it
resets, as well as its value. Starts at zero
and increments just before you get the new value,
so that it is still 'Chapter 5' and not 'Chapter 6'
when you print 'Figure 5.1'"""
def __init__(self):
self._base = 0
self._value = self._base
self._formatter = _format_123
self._resets = []
def setFormatter(self, formatFunc):
self._formatter = formatFunc
def reset(self, value=None):
if value:
self._value = value
else:
self._value = self._base
def next(self):
self._value = self._value + 1
v = self._value
for counter in self._resets:
counter.reset()
return v
def _this(self):
return self._value
def nextf(self):
"""Returns next value formatted"""
return self._formatter(self.next())
def thisf(self):
return self._formatter(self._this())
def chain(self, otherCounter):
if not otherCounter in self._resets:
self._resets.append(otherCounter)
class Sequencer:
"""Something to make it easy to number paragraphs, sections,
images and anything else. The features include registering
new string formats for sequences, and 'chains' whereby
some counters are reset when their parents.
It keeps track of a number of
'counters', which are created on request:
Usage:
>>> seq = layout.Sequencer()
>>> seq.next('Bullets')
1
>>> seq.next('Bullets')
2
>>> seq.next('Bullets')
3
>>> seq.reset('Bullets')
>>> seq.next('Bullets')
1
>>> seq.next('Figures')
1
>>>
"""
def __init__(self):
self._counters = {} #map key to current number
self._defaultCounter = None
self._formatters = {
# the formats it knows initially
'1':_format_123,
'A':_format_ABC,
'a':_format_abc,
'I':_format_I,
'i':_format_i,
}
def _getCounter(self, counter=None):
"""Creates one if not present"""
try:
return self._counters[counter]
except KeyError:
cnt = _Counter()
self._counters[counter] = cnt
return cnt
def _this(self, counter=None):
"""Retrieves counter value but does not increment. For
new counters, sets base value to 1."""
if not counter:
counter = self._defaultCounter
return self._getCounter(counter)._this()
def next(self, counter=None):
"""Retrieves the numeric value for the given counter, then
increments it by one. New counters start at one."""
if not counter:
counter = self._defaultCounter
return self._getCounter(counter).next()
def thisf(self, counter=None):
if not counter:
counter = self._defaultCounter
return self._getCounter(counter).thisf()
def nextf(self, counter=None):
"""Retrieves the numeric value for the given counter, then
increments it by one. New counters start at one."""
if not counter:
counter = self._defaultCounter
return self._getCounter(counter).nextf()
def setDefaultCounter(self, default=None):
"""Changes the key used for the default"""
self._defaultCounter = default
def registerFormat(self, format, func):
"""Registers a new formatting function. The funtion
must take a number as argument and return a string;
fmt is a short menmonic string used to access it."""
self._formatters[format] = func
def setFormat(self, counter, format):
"""Specifies that the given counter should use
the given format henceforth."""
func = self._formatters[format]
self._getCounter(counter).setFormatter(func)
def reset(self, counter=None, base=0):
if not counter:
counter = self._defaultCounter
self._getCounter(counter)._value = base
def chain(self, parent, child):
p = self._getCounter(parent)
c = self._getCounter(child)
p.chain(c)
def __getitem__(self, key):
"""Allows compact notation to support the format function.
s['key'] gets current value, s['key+'] increments."""
if key[-1:] == '+':
counter = key[:-1]
return self.nextf(counter)
else:
return self.thisf(key)
def format(self, template):
"""The crowning jewels - formats multi-level lists."""
return template % self
def dump(self):
"""Write current state to stdout for diagnostics"""
counters = self._counters.items()
counters.sort()
print 'Sequencer dump:'
for (key, counter) in counters:
print ' %s: value = %d, base = %d, format example = %s' % (
key, counter._this(), counter._base, counter.thisf())
"""Your story builder needs to set this to"""
_sequencer = None
def getSequencer():
global _sequencer
if _sequencer is None:
_sequencer = Sequencer()
return _sequencer
def setSequencer(seq):
global _sequencer
s = _sequencer
_sequencer = seq
return s
def test():
s = Sequencer()
print 'Counting using default sequence: %d %d %d' % (s.next(),s.next(), s.next())
print 'Counting Figures: Figure %d, Figure %d, Figure %d' % (
s.next('figure'), s.next('figure'), s.next('figure'))
print 'Back to default again: %d' % s.next()
s.setDefaultCounter('list1')
print 'Set default to list1: %d %d %d' % (s.next(),s.next(), s.next())
s.setDefaultCounter()
print 'Set default to None again: %d %d %d' % (s.next(),s.next(), s.next())
print
print 'Creating Appendix counter with format A, B, C...'
s.setFormat('Appendix', 'A')
print ' Appendix %s, Appendix %s, Appendix %s' % (
s.nextf('Appendix'), s.nextf('Appendix'),s.nextf('Appendix'))
def format_french(num):
return ('un','deux','trois','quatre','cinq')[(num-1)%5]
print
print 'Defining a custom format with french words:'
s.registerFormat('french', format_french)
s.setFormat('FrenchList', 'french')
print ' ',
for i in range(1,6):
print s.nextf('FrenchList'),
print
print 'Chaining H1 and H2 - H2 goes back to one when H1 increases'
s.chain('H1','H2')
print ' H1 = %d' % s.next('H1')
print ' H2 = %d' % s.next('H2')
print ' H2 = %d' % s.next('H2')
print ' H2 = %d' % s.next('H2')
print ' H1 = %d' % s.next('H1')
print ' H2 = %d' % s.next('H2')
print ' H2 = %d' % s.next('H2')
print ' H2 = %d' % s.next('H2')
print
print 'GetItem notation - append a plus to increment'
print ' seq["Appendix"] = %s' % s["Appendix"]
print ' seq["Appendix+"] = %s' % s["Appendix+"]
print ' seq["Appendix+"] = %s' % s["Appendix+"]
print ' seq["Appendix"] = %s' % s["Appendix"]
print
print 'Finally, string format notation for nested lists. Cool!'
print 'The expression ("Figure %(Chapter)s.%(Figure+)s" % seq) gives:'
print ' Figure %(Chapter)s.%(Figure+)s' % s
print ' Figure %(Chapter)s.%(Figure+)s' % s
print ' Figure %(Chapter)s.%(Figure+)s' % s
if __name__=='__main__':
test()

View File

@ -0,0 +1,38 @@
#!/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/lib/set_ops.py
import types
import string
def __set_coerce(t, S):
if t is types.ListType:
return list(S)
elif t is types.TupleType:
return tuple(S)
elif t is types.StringType:
return string.join(S, '')
return S
def unique(seq):
result = []
for i in seq:
if i not in result:
result.append(i)
return __set_coerce(type(seq), result)
def intersect(seq1, seq2):
result = []
if type(seq1) != type(seq2) and type(seq2) == types.StringType: seq2 = list(seq2)
for i in seq1:
if i in seq2 and i not in result: result.append(i)
return __set_coerce(type(seq1), result)
def union(seq1, seq2):
if type(seq1) == type(seq2):
return unique(seq1 + seq2)
if type(seq1) == types.ListType or type(seq2) == types.ListType:
return unique(list(seq1) + list(seq2))
if type(seq1) == types.TupleType or type(seq2) == types.TupleType:
return unique(tuple(seq1) + tuple(seq2))
return unique(list(seq1) + list(seq2))

257
bin/reportlab/lib/styles.py Normal file
View File

@ -0,0 +1,257 @@
#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/lib/styles.py
__version__=''' $Id: styles.py 2830 2006-04-05 15:18:32Z rgbecker $ '''
from reportlab.lib.colors import white, black
from reportlab.lib.enums import TA_LEFT, TA_CENTER
###########################################################
# This class provides an 'instance inheritance'
# mechanism for its descendants, simpler than acquisition
# but not as far-reaching
###########################################################
class PropertySet:
defaults = {}
def __init__(self, name, parent=None, **kw):
"""When initialized, it copies the class defaults;
then takes a copy of the attributes of the parent
if any. All the work is done in init - styles
should cost little to use at runtime."""
# step one - validate the hell out of it
assert not self.defaults.has_key('name'), "Class Defaults may not contain a 'name' attribute"
assert not self.defaults.has_key('parent'), "Class Defaults may not contain a 'parent' attribute"
if parent:
assert parent.__class__ == self.__class__, "Parent style must have same class as new style"
#step two
self.name = name
self.parent = parent
self.__dict__.update(self.defaults)
#step two - copy from parent if any. Try to be
# very strict that only keys in class defaults are
# allowed, so they cannot inherit
self.refresh()
#step three - copy keywords if any
for (key, value) in kw.items():
self.__dict__[key] = value
def __repr__(self):
return "<%s '%s'>" % (self.__class__.__name__, self.name)
def refresh(self):
"""re-fetches attributes from the parent on demand;
use if you have been hacking the styles. This is
used by __init__"""
if self.parent:
for (key, value) in self.parent.__dict__.items():
if (key not in ['name','parent']):
self.__dict__[key] = value
def listAttrs(self, indent=''):
print indent + 'name =', self.name
print indent + 'parent =', self.parent
keylist = self.__dict__.keys()
keylist.sort()
keylist.remove('name')
keylist.remove('parent')
for key in keylist:
value = self.__dict__.get(key, None)
print indent + '%s = %s' % (key, value)
class ParagraphStyle(PropertySet):
defaults = {
'fontName':'Times-Roman',
'fontSize':10,
'leading':12,
'leftIndent':0,
'rightIndent':0,
'firstLineIndent':0,
'alignment':TA_LEFT,
'spaceBefore':0,
'spaceAfter':0,
'bulletFontName':'Times-Roman',
'bulletFontSize':10,
'bulletIndent':0,
'textColor': black,
'backColor':None,
'wordWrap':None,
}
class LineStyle(PropertySet):
defaults = {
'width':1,
'color': black
}
def prepareCanvas(self, canvas):
"""You can ask a LineStyle to set up the canvas for drawing
the lines."""
canvas.setLineWidth(1)
#etc. etc.
class StyleSheet1:
"""This may or may not be used. The idea is to
1. slightly simplify construction of stylesheets;
2. enforce rules to validate styles when added
(e.g. we may choose to disallow having both
'heading1' and 'Heading1' - actual rules are
open to discussion);
3. allow aliases and alternate style lookup
mechanisms
4. Have a place to hang style-manipulation
methods (save, load, maybe support a GUI
editor)
Access is via getitem, so they can be
compatible with plain old dictionaries.
"""
def __init__(self):
self.byName = {}
self.byAlias = {}
def __getitem__(self, key):
try:
return self.byAlias[key]
except KeyError:
try:
return self.byName[key]
except KeyError:
raise KeyError, "Style '%s' not found in stylesheet" % key
def has_key(self, key):
if self.byAlias.has_key(key):
return 1
elif self.byName.has_key(key):
return 1
else:
return 0
def add(self, style, alias=None):
key = style.name
if self.byName.has_key(key):
raise KeyError, "Style '%s' already defined in stylesheet" % key
if self.byAlias.has_key(key):
raise KeyError, "Style name '%s' is already an alias in stylesheet" % key
if alias:
if self.byName.has_key(alias):
raise KeyError, "Style '%s' already defined in stylesheet" % alias
if self.byAlias.has_key(alias):
raise KeyError, "Alias name '%s' is already an alias in stylesheet" % alias
#passed all tests? OK, add it
self.byName[key] = style
if alias:
self.byAlias[alias] = style
def list(self):
styles = self.byName.items()
styles.sort()
alii = {}
for (alias, style) in self.byAlias.items():
alii[style] = alias
for (name, style) in styles:
alias = alii.get(style, None)
print name, alias
style.listAttrs(' ')
print
def testStyles():
pNormal = ParagraphStyle('Normal',None)
pNormal.fontName = 'Times-Roman'
pNormal.fontSize = 12
pNormal.leading = 14.4
pNormal.listAttrs()
print
pPre = ParagraphStyle('Literal', pNormal)
pPre.fontName = 'Courier'
pPre.listAttrs()
return pNormal, pPre
def getSampleStyleSheet():
"""Returns a stylesheet object"""
stylesheet = StyleSheet1()
stylesheet.add(ParagraphStyle(name='Normal',
fontName='Times-Roman',
fontSize=10,
leading=12)
)
stylesheet.add(ParagraphStyle(name='BodyText',
parent=stylesheet['Normal'],
spaceBefore=6)
)
stylesheet.add(ParagraphStyle(name='Italic',
parent=stylesheet['BodyText'],
fontName = 'Times-Italic')
)
stylesheet.add(ParagraphStyle(name='Heading1',
parent=stylesheet['Normal'],
fontName = 'Times-Bold',
fontSize=18,
leading=22,
spaceAfter=6),
alias='h1')
stylesheet.add(ParagraphStyle(name='Title',
parent=stylesheet['Normal'],
fontName = 'Times-Bold',
fontSize=18,
leading=22,
alignment=TA_CENTER,
spaceAfter=6),
alias='title')
stylesheet.add(ParagraphStyle(name='Heading2',
parent=stylesheet['Normal'],
fontName = 'Times-Bold',
fontSize=14,
leading=18,
spaceBefore=12,
spaceAfter=6),
alias='h2')
stylesheet.add(ParagraphStyle(name='Heading3',
parent=stylesheet['Normal'],
fontName = 'Times-BoldItalic',
fontSize=12,
leading=14,
spaceBefore=12,
spaceAfter=6),
alias='h3')
stylesheet.add(ParagraphStyle(name='Bullet',
parent=stylesheet['Normal'],
firstLineIndent=0,
spaceBefore=3),
alias='bu')
stylesheet.add(ParagraphStyle(name='Definition',
parent=stylesheet['Normal'],
firstLineIndent=0,
leftIndent=36,
bulletIndent=0,
spaceBefore=6,
bulletFontName='Times-BoldItalic'),
alias='df')
stylesheet.add(ParagraphStyle(name='Code',
parent=stylesheet['Normal'],
fontName='Courier',
fontSize=8,
leading=8.8,
firstLineIndent=0,
leftIndent=36))
return stylesheet

View File

@ -0,0 +1,210 @@
#Copyright ReportLab Europe Ltd. 2000-2006
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/textsplit.py
"""Helpers for text wrapping, hyphenation, Asian text splitting and kinsoku shori.
How to split a 'big word' depends on the language and the writing system. This module
works on a Unicode string. It ought to grow by allowing ore algoriths to be plugged
in based on possible knowledge of the language and desirable 'niceness' of the algorithm.
"""
__version__=''' $Id: textsplit.py 2833 2006-04-05 16:01:20Z rgbecker $ '''
from types import StringType, UnicodeType
import unicodedata
from reportlab.pdfbase.pdfmetrics import stringWidth
from reportlab.rl_config import _FUZZ
CANNOT_START_LINE = [
#strongly prohibited e.g. end brackets, stop, exclamation...
u'!\',.:;?!")]\u3001\u3002\u300d\u300f\u3011\u3015\uff3d\u3011\uff09',
#middle priority e.g. continuation small vowels - wrapped on two lines but one string...
u'\u3005\u2015\u3041\u3043\u3045\u3047\u3049\u3063\u3083\u3085\u3087\u308e\u30a1\u30a3'
u'\u30a5\u30a7\u30a9\u30c3\u30e3\u30e5\u30e7\u30ee\u30fc\u30f5\u30f6',
#weakly prohibited - continuations, celsius symbol etc.
u'\u309b\u309c\u30fb\u30fd\u30fe\u309d\u309e\u2015\u2010\xb0\u2032\u2033\u2103\uffe0\uff05\u2030'
]
ALL_CANNOT_START = u''.join(CANNOT_START_LINE)
CANNOT_END_LINE = [
#strongly prohibited
u'\u2018\u201c\uff08[{\uff08\u3014\uff3b\uff5b\u3008\u300a\u300c\u300e\u3010',
#weaker - currency symbols, hash, postcode - prefixes
u'$\u00a3@#\uffe5\uff04\uffe1\uff20\u3012\u00a7'
]
ALL_CANNOT_END = u''.join(CANNOT_END_LINE)
def getCharWidths(word, fontName, fontSize):
"""Returns a list of glyph widths. Should be easy to optimize in _rl_accel
>>> getCharWidths('Hello', 'Courier', 10)
[6.0, 6.0, 6.0, 6.0, 6.0]
>>> from reportlab.pdfbase.cidfonts import UnicodeCIDFont
>>> from reportlab.pdfbase.pdfmetrics import registerFont
>>> registerFont(UnicodeCIDFont('HeiseiMin-W3'))
>>> getCharWidths(u'\u6771\u4EAC', 'HeiseiMin-W3', 10) #most kanji are 100 ems
[10.0, 10.0]
"""
#character-level function call; the performance is going to SUCK
return [stringWidth(uChar, fontName, fontSize) for uChar in word]
def wordSplit(word, availWidth, fontName, fontSize, encoding='utf8'):
"""Attempts to break a word which lacks spaces into two parts, the first of which
fits in the remaining space. It is allowed to add hyphens or whatever it wishes.
This is intended as a wrapper for some language- and user-choice-specific splitting
algorithms. It should only be called after line breaking on spaces, which covers western
languages and is highly optimised already. It works on the 'last unsplit word'.
Presumably with further study one could write a Unicode splitting algorithm for text
fragments whick was much faster.
Courier characters should be 6 points wide.
>>> wordSplit('HelloWorld', 30, 'Courier', 10)
[[0.0, 'Hello'], [0.0, 'World']]
>>> wordSplit('HelloWorld', 31, 'Courier', 10)
[[1.0, 'Hello'], [1.0, 'World']]
"""
if type(word) is not UnicodeType:
uword = word.decode(encoding)
else:
uword = word
charWidths = getCharWidths(uword, fontName, fontSize)
lines = dumbSplit(uword, charWidths, availWidth)
if type(word) is not UnicodeType:
lines2 = []
#convert back
for (extraSpace, text) in lines:
lines2.append([extraSpace, text.encode(encoding)])
lines = lines2
return lines
def dumbSplit(word, widths, availWidth):
"""This function attempts to fit as many characters as possible into the available
space, cutting "like a knife" between characters. This would do for Chinese.
It returns a list of (text, extraSpace) items where text is a Unicode string,
and extraSpace is the points of unused space available on the line. This is a
structure which is fairly easy to display, and supports 'backtracking' approaches
after the fact.
Test cases assume each character is ten points wide...
>>> dumbSplit(u'Hello', [10]*5, 60)
[[10.0, u'Hello']]
>>> dumbSplit(u'Hello', [10]*5, 50)
[[0.0, u'Hello']]
>>> dumbSplit(u'Hello', [10]*5, 40)
[[0.0, u'Hell'], [30, u'o']]
"""
_more = """
#>>> dumbSplit(u'Hello', [10]*5, 4) # less than one character
#(u'', u'Hello')
# this says 'Nihongo wa muzukashii desu ne!' (Japanese is difficult isn't it?) in 12 characters
>>> jtext = u'\u65e5\u672c\u8a9e\u306f\u96e3\u3057\u3044\u3067\u3059\u306d\uff01'
>>> dumbSplit(jtext, [10]*11, 30) #
(u'\u65e5\u672c\u8a9e', u'\u306f\u96e3\u3057\u3044\u3067\u3059\u306d\uff01')
"""
assert type(word) is UnicodeType
lines = []
widthUsed = 0.0
lineStartPos = 0
for (i, w) in enumerate(widths):
widthUsed += w
if widthUsed > availWidth + _FUZZ:
#used more than can fit...
#ping out with previous cut, then set up next line with one character
extraSpace = availWidth - widthUsed + w
#print 'ending a line; used %d, available %d' % (widthUsed, availWidth)
selected = word[lineStartPos:i]
#This is the most important of the Japanese typography rules.
#if next character cannot start a line, wrap it up to this line so it hangs
#in the right margin. We won't do two or more though - that's unlikely and
#would result in growing ugliness.
nextChar = word[i]
if nextChar in ALL_CANNOT_START:
#it's punctuation or a closing bracket of some kind. 'wrap up'
#so it stays on the line above, slightly exceeding our target width.
#print 'wrapping up', repr(nextChar)
selected += nextChar
extraSpace -= w
i += 1
lines.append([extraSpace, selected])
lineStartPos = i
widthUsed = w
i -= 1
#any characters left?
if widthUsed > 0:
extraSpace = availWidth - widthUsed
lines.append([extraSpace, word[lineStartPos:]])
return lines
def kinsokuShoriSplit(word, widths, availWidth):
#NOT USED OR FINISHED YET!
"""Split according to Japanese rules according to CJKV (Lunde).
Essentially look for "nice splits" so that we don't end a line
with an open bracket, or start one with a full stop, or stuff like
that. There is no attempt to try to split compound words into
constituent kanji. It currently uses wrap-down: packs as much
on a line as possible, then backtracks if needed
This returns a number of words each of which should just about fit
on a line. If you give it a whole paragraph at once, it will
do all the splits.
It's possible we might slightly step over the width limit
if we do hanging punctuation marks in future (e.g. dangle a Japanese
full stop in the right margin rather than using a whole character
box.
"""
lines = []
assert len(word) == len(widths)
curWidth = 0.0
curLine = []
i = 0 #character index - we backtrack at times so cannot use for loop
while 1:
ch = word[i]
w = widths[i]
if curWidth + w < availWidth:
curLine.append(ch)
curWidth += w
else:
#end of line. check legality
if ch in CANNOT_END_LINE[0]:
pass
#to be completed
# This recipe refers:
#
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
import re
rx=re.compile(u"([\u2e80-\uffff])", re.UNICODE)
def cjkwrap(text, width, encoding="utf8"):
return reduce(lambda line, word, width=width: '%s%s%s' %
(line,
[' ','\n', ''][(len(line)-line.rfind('\n')-1
+ len(word.split('\n',1)[0] ) >= width) or
line[-1:] == '\0' and 2],
word),
rx.sub(r'\1\0 ', unicode(text,encoding)).split(' ')
).replace('\0', '').encode(encoding)
if __name__=='__main__':
import doctest, textsplit
doctest.testmod(textsplit)

View File

@ -0,0 +1,294 @@
# Tables of Contents and Indices
#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/lib/tocindex.py
__version__=''' $Id: tocindex.py 2385 2004-06-17 15:26:05Z rgbecker $ '''
__doc__=''
"""
This module will contain standard Table of Contents and Index objects.
under development, and pending certain hooks adding in DocTemplate
As of today, it onyl handles the formatting aspect of TOCs
"""
import string
from reportlab.platypus import Flowable, BaseDocTemplate, Paragraph, \
PageBreak, Frame, PageTemplate, NextPageTemplate
from reportlab.platypus.doctemplate import IndexingFlowable
from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
from reportlab.platypus import tables
from reportlab.lib import enums
from reportlab.lib import colors
from reportlab.lib.units import inch, cm
from reportlab.rl_config import defaultPageSize
##############################################################
#
# we first define a paragraph style for each level of the
# table, and one for the table as whole; you can supply your
# own.
#
##############################################################
levelZeroParaStyle = ParagraphStyle(name='LevelZero',
fontName='Times-Roman',
fontSize=10,
leading=12)
levelOneParaStyle = ParagraphStyle(name='LevelOne',
parent = levelZeroParaStyle,
firstLineIndent = 0,
leftIndent = 18)
levelTwoParaStyle = ParagraphStyle(name='LevelTwo',
parent = levelOneParaStyle,
firstLineIndent = 0,
leftIndent = 36)
levelThreeParaStyle = ParagraphStyle(name='LevelThree',
parent = levelTwoParaStyle,
firstLineIndent = 0,
leftIndent = 54)
defaultTableStyle = tables.TableStyle([
('VALIGN',(0,0),(-1,-1),'TOP'),
('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
('BOX', (0,0), (-1,-1), 0.25, colors.black),
])
class TableOfContents0(IndexingFlowable):
"""This creates a formatted table of contents. It presumes
a correct block of data is passed in. The data block contains
a list of (level, text, pageNumber) triplets. You can supply
a paragraph style for each level (starting at zero)."""
def __init__(self):
self.entries = []
self.rightColumnWidth = 72
self.levelStyles = [levelZeroParaStyle,
levelOneParaStyle,
levelTwoParaStyle,
levelThreeParaStyle]
self.tableStyle = defaultTableStyle
self._table = None
self._entries = []
self._lastEntries = []
def beforeBuild(self):
# keep track of the last run
self._lastEntries = self._entries[:]
self.clearEntries()
def isIndexing(self):
return 1
def isSatisfied(self):
return (self._entries == self._lastEntries)
def notify(self, kind, stuff):
"""DocTemplate framework can call this with all kinds
of events; we say we are interested in 'TOCEntry'
events."""
if kind == 'TOCEntry':
(level, text, pageNum) = stuff
self.addEntry(level, text, pageNum)
#print 'TOC notified of ', stuff
## elif kind == 'debug':
## # hack to watch its state
## import pprint
## print 'Last Entries first 5:'
## for (level, text, pageNum) in self._lastEntries[0:5]:
## print (level, text[0:30], pageNum),
## print
## print 'Current Entries first 5:'
## for (level, text, pageNum) in self._lastEntries[0:5]:
## print (level, text[0:30], pageNum),
def clearEntries(self):
self._entries = []
def addEntry(self, level, text, pageNum):
"""Adds one entry; allows incremental buildup by a doctemplate.
Requires that enough styles are defined."""
assert type(level) == type(1), "Level must be an integer"
assert level < len(self.levelStyles), \
"Table of contents must have a style defined " \
"for paragraph level %d before you add an entry" % level
self._entries.append((level, text, pageNum))
def addEntries(self, listOfEntries):
"""Bulk creation. If you knew the titles but
not the page numbers, you could supply them to
get sensible output on the first run."""
for (level, text, pageNum) in listOfEntries:
self.addEntry(level, text, pageNum)
def wrap(self, availWidth, availHeight):
"""All table properties should be known by now."""
widths = (availWidth - self.rightColumnWidth,
self.rightColumnWidth)
# makes an internal table which does all the work.
# we draw the LAST RUN's entries! If there are
# none, we make some dummy data to keep the table
# from complaining
if len(self._lastEntries) == 0:
_tempEntries = [(0,'Placeholder for table of contents',0)]
else:
_tempEntries = self._lastEntries
tableData = []
for (level, text, pageNum) in _tempEntries:
leftColStyle = self.levelStyles[level]
#right col style is right aligned
rightColStyle = ParagraphStyle(name='leftColLevel%d' % level,
parent=leftColStyle,
leftIndent=0,
alignment=enums.TA_RIGHT)
leftPara = Paragraph(text, leftColStyle)
rightPara = Paragraph(str(pageNum), rightColStyle)
tableData.append([leftPara, rightPara])
self._table = tables.Table(tableData, colWidths=widths,
style=self.tableStyle)
self.width, self.height = self._table.wrap(availWidth, availHeight)
return (self.width, self.height)
def split(self, availWidth, availHeight):
"""At this stage we do not care about splitting the entries,
we wil just return a list of platypus tables. Presumably the
calling app has a pointer to the original TableOfContents object;
Platypus just sees tables."""
return self._table.split(availWidth, availHeight)
def drawOn(self, canvas, x, y, _sW=0):
"""Don't do this at home! The standard calls for implementing
draw(); we are hooking this in order to delegate ALL the drawing
work to the embedded table object"""
self._table.drawOn(canvas, x, y, _sW)
#################################################################################
#
# everything from here down is concerned with creating a good example document
# i.e. test code as well as tutorial
PAGE_HEIGHT = defaultPageSize[1]
def getSampleTOCData(depth=3):
"""Returns a longish block of page numbers and headings over 3 levels"""
from random import randint
pgNum = 2
data = []
for chapter in range(1,8):
data.append((0, """Chapter %d with a really long name which will hopefully
wrap onto a second line, fnding out if the right things happen with
full paragraphs n the table of contents""" % chapter, pgNum))
pgNum = pgNum + randint(0,2)
if depth > 1:
for section in range(1,5):
data.append((1, 'Chapter %d Section %d' % (chapter, section), pgNum))
pgNum = pgNum + randint(0,2)
if depth > 2:
for subSection in range(1,6):
data.append(2, 'Chapter %d Section %d Subsection %d' %
(chapter, section, subSection),
pgNum)
pgNum = pgNum + randint(0,1)
from pprint import pprint as pp
pp(data)
return data
def getSampleStory(depth=3):
"""Makes a story with lots of paragraphs. Uses the random
TOC data and makes paragraphs to correspond to each."""
from reportlab.platypus.doctemplate import randomText
from random import randint
styles = getSampleStyleSheet()
TOCData = getSampleTOCData(depth)
story = [Paragraph("This is a demo of the table of contents object",
styles['Heading1'])]
toc = TableOfContents0() # empty on first pass
#toc.addEntries(TOCData) # init with random page numbers
story.append(toc)
# the next full page should switch to internal page style
story.append(NextPageTemplate("Body"))
# now make some paragraphs to correspond to it
for (level, text, pageNum) in TOCData:
if level == 0:
#page break before chapter
story.append(PageBreak())
headingStyle = (styles['Heading1'], styles['Heading2'], styles['Heading3'])[level]
headingPara = Paragraph(text, headingStyle)
story.append(headingPara)
# now make some body text
for i in range(randint(1,6)):
bodyPara = Paragraph(randomText(),
styles['Normal'])
story.append(bodyPara)
return story
class MyDocTemplate(BaseDocTemplate):
"""Example of how to do the indexing. Need the onDraw hook
to find out which things are drawn on which pages"""
def afterInit(self):
"""Set up the page templates"""
frameT = Frame(self.leftMargin, self.bottomMargin, self.width, self.height, id='normal')
self.addPageTemplates([PageTemplate(id='Front',frames=frameT),
PageTemplate(id='Body',frames=frameT)
])
# just need a unique key generator for outline entries;
# easiest is to count all flowables in afterFlowable
# and set up a counter variable here
self._uniqueKey = 0
def afterFlowable(self, flowable):
"""Our rule for the table of contents is simply to take
the text of H1, H2 and H3 elements. We broadcast a
notification to the DocTemplate, which should inform
the TOC and let it pull them out. Also build an outline"""
self._uniqueKey = self._uniqueKey + 1
if hasattr(flowable, 'style'):
if flowable.style.name == 'Heading1':
self.notify('TOCEntry', (0, flowable.getPlainText(), self.page))
self.canv.bookmarkPage(str(self._uniqueKey))
self.canv.addOutlineEntry(flowable.getPlainText()[0:10], str(self._uniqueKey), 0)
elif flowable.style.name == 'Heading2':
self.notify('TOCEntry', (1, flowable.getPlainText(), self.page))
self.canv.bookmarkPage(str(self._uniqueKey))
self.canv.addOutlineEntry(flowable.getPlainText(), str(self._uniqueKey), 1)
elif flowable.style.name == 'Heading3':
self.notify('TOCEntry', (2, flowable.getPlainText(), self.page))
self.canv.bookmarkPage(str(self._uniqueKey))
self.canv.addOutlineEntry(flowable.getPlainText(), str(self._uniqueKey), 2)
def beforePage(self):
"""decorate the page"""
self.canv.saveState()
self.canv.setStrokeColor(colors.red)
self.canv.setLineWidth(5)
self.canv.line(66,72,66,PAGE_HEIGHT-72)
self.canv.setFont('Times-Roman',12)
self.canv.drawString(4 * inch, 0.75 * inch, "Page %d" % doc.page)
self.canv.restoreState()
if __name__=='__main__':
from reportlab.platypus import SimpleDocTemplate
doc = MyDocTemplate('tocindex.pdf')
#change this to depth=3 for a BIG document
story = getSampleStory(depth=2)
doc.multiBuild(story, 'tocindex.pdf')

View File

@ -0,0 +1,23 @@
#!/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/lib/units.py
__version__=''' $Id: units.py 2524 2005-02-17 08:43:01Z rgbecker $ '''
inch = 72.0
cm = inch / 2.54
mm = cm * 0.1
pica = 12.0
def toLength(s):
'''convert a string to a length'''
try:
if s[-2:]=='cm': return float(s[:-2])*cm
if s[-2:]=='in': return float(s[:-2])*inch
if s[-2:]=='pt': return float(s[:-2])
if s[-1:]=='i': return float(s[:-1])*inch
if s[-2:]=='mm': return float(s[:-2])*mm
if s[-4:]=='pica': return float(s[:-4])*pica
return float(s)
except:
raise ValueError, "Can't convert '%s' to length" % s

827
bin/reportlab/lib/utils.py Normal file
View File

@ -0,0 +1,827 @@
#Copyright ReportLab Europe Ltd. 2000-2006
#see license.txt for license details
# $URI:$
__version__=''' $Id: utils.py 2892 2006-05-19 14:16:02Z rgbecker $ '''
import string, os, sys, imp
from reportlab.lib.logger import warnOnce
from types import *
from rltempfile import get_rl_tempfile, get_rl_tempdir, _rl_getuid
SeqTypes = (ListType,TupleType)
if sys.hexversion<0x2020000:
def isSeqType(v):
return type(v) in SeqTypes
else:
def isSeqType(v):
return isinstance(v,(tuple,list))
if sys.hexversion<0x2030000:
True = 1
False = 0
def _findFiles(dirList,ext='.ttf'):
from os.path import isfile, isdir, join as path_join
from os import listdir
ext = ext.lower()
R = []
A = R.append
for D in dirList:
if not isdir(D): continue
for fn in listdir(D):
fn = path_join(D,fn)
if isfile(fn) and (not ext or fn.lower().endswith(ext)): A(fn)
return R
try:
_UserDict = dict
except:
from UserDict import UserDict as _UserDict
class CIDict(_UserDict):
def __init__(self,*a,**kw):
map(self.update, a)
self.update(kw)
def update(self,D):
for k,v in D.items(): self[k] = v
def __setitem__(self,k,v):
try:
k = k.lower()
except:
pass
_UserDict.__setitem__(self,k,v)
def __getitem__(self,k):
try:
k = k.lower()
except:
pass
return _UserDict.__getitem__(self,k)
def __delitem__(self,k):
try:
k = k.lower()
except:
pass
return _UserDict.__delitem__(self,k)
def get(self,k,dv=None):
try:
return self[k]
except KeyError:
return dv
def has_key(self,k):
try:
self[k]
return True
except:
return False
def pop(self,k,*a):
try:
k = k.lower()
except:
pass
return _UserDict.pop(*((self,k)+a))
def setdefault(self,k,*a):
try:
k = k.lower()
except:
pass
return _UserDict.setdefault(*((self,k)+a))
if os.name == 'mac':
#with the Mac, we need to tag the file in a special
#way so the system knows it is a PDF file.
#This supplied by Joe Strout
import macfs, macostools
_KNOWN_MAC_EXT = {
'BMP' : ('ogle','BMP '),
'EPS' : ('ogle','EPSF'),
'EPSF': ('ogle','EPSF'),
'GIF' : ('ogle','GIFf'),
'JPG' : ('ogle','JPEG'),
'JPEG': ('ogle','JPEG'),
'PCT' : ('ttxt','PICT'),
'PICT': ('ttxt','PICT'),
'PNG' : ('ogle','PNGf'),
'PPM' : ('ogle','.PPM'),
'TIF' : ('ogle','TIFF'),
'TIFF': ('ogle','TIFF'),
'PDF' : ('CARO','PDF '),
'HTML': ('MSIE','TEXT'),
}
def markfilename(filename,creatorcode=None,filetype=None,ext='PDF'):
try:
if creatorcode is None or filetype is None and ext is not None:
try:
creatorcode, filetype = _KNOWN_MAC_EXT[string.upper(ext)]
except:
return
macfs.FSSpec(filename).SetCreatorType(creatorcode,filetype)
macostools.touched(filename)
except:
pass
else:
def markfilename(filename,creatorcode=None,filetype=None):
pass
import reportlab
__RL_DIR=os.path.dirname(reportlab.__file__) #possibly relative
_RL_DIR=os.path.isabs(__RL_DIR) and __RL_DIR or os.path.abspath(__RL_DIR)
del reportlab
#Attempt to detect if this copy of reportlab is running in a
#file system (as opposed to mostly running in a zip or McMillan
#archive or Jar file). This is used by test cases, so that
#we can write test cases that don't get activated in a compiled
try:
__file__
except:
__file__ = sys.argv[0]
import glob, fnmatch
try:
_isFSD = not __loader__
_archive = os.path.normcase(os.path.normpath(__loader__.archive))
_archivepfx = _archive + os.sep
_archivedir = os.path.dirname(_archive)
_archivedirpfx = _archivedir + os.sep
_archivepfxlen = len(_archivepfx)
_archivedirpfxlen = len(_archivedirpfx)
def __startswith_rl(fn,
_archivepfx=_archivepfx,
_archivedirpfx=_archivedirpfx,
_archive=_archive,
_archivedir=_archivedir,
os_path_normpath=os.path.normpath,
os_path_normcase=os.path.normcase,
os_getcwd=os.getcwd,
os_sep=os.sep,
os_sep_len = len(os.sep)):
'''if the name starts with a known prefix strip it off'''
fn = os_path_normpath(fn.replace('/',os_sep))
nfn = os_path_normcase(fn)
if nfn in (_archivedir,_archive): return 1,''
if nfn.startswith(_archivepfx): return 1,fn[_archivepfxlen:]
if nfn.startswith(_archivedirpfx): return 1,fn[_archivedirpfxlen:]
cwd = os_path_normcase(os_getcwd())
n = len(cwd)
if nfn.startswith(cwd):
if fn[n:].startswith(os_sep): return 1, fn[n+os_sep_len:]
if n==len(fn): return 1,''
return not os.path.isabs(fn),fn
def _startswith_rl(fn):
return __startswith_rl(fn)[1]
def rl_glob(pattern,glob=glob.glob,fnmatch=fnmatch.fnmatch, _RL_DIR=_RL_DIR,pjoin=os.path.join):
c, pfn = __startswith_rl(pattern)
r = glob(pfn)
if c or r==[]:
r += map(lambda x,D=_archivepfx,pjoin=pjoin: pjoin(_archivepfx,x),filter(lambda x,pfn=pfn,fnmatch=fnmatch: fnmatch(x,pfn),__loader__._files.keys()))
return r
except:
_isFSD = os.path.isfile(__file__) #slight risk of wrong path
__loader__ = None
def _startswith_rl(fn):
return fn
def rl_glob(pattern,glob=glob.glob):
return glob(pattern)
del glob, fnmatch
_isFSSD = _isFSD and os.path.isfile(os.path.splitext(__file__)[0] +'.py')
def isFileSystemDistro():
'''return truth if a file system distribution'''
return _isFSD
def isCompactDistro():
'''return truth if not a file system distribution'''
return not _isFSD
def isSourceDistro():
'''return truth if a source file system distribution'''
return _isFSSD
try:
#raise ImportError
### NOTE! FP_STR SHOULD PROBABLY ALWAYS DO A PYTHON STR() CONVERSION ON ARGS
### IN CASE THEY ARE "LAZY OBJECTS". ACCELLERATOR DOESN'T DO THIS (YET)
try:
from _rl_accel import fp_str # in case of builtin version
except ImportError:
from reportlab.lib._rl_accel import fp_str # specific
except ImportError:
from math import log
_log_10 = lambda x,log=log,_log_e_10=log(10.0): log(x)/_log_e_10
_fp_fmts = "%.0f", "%.1f", "%.2f", "%.3f", "%.4f", "%.5f", "%.6f"
import re
_tz_re = re.compile('0+$')
del re
def fp_str(*a):
if len(a)==1 and isSeqType(a[0]): a = a[0]
s = []
A = s.append
for i in a:
sa =abs(i)
if sa<=1e-7: A('0')
else:
l = sa<=1 and 6 or min(max(0,(6-int(_log_10(sa)))),6)
n = _fp_fmts[l]%i
if l:
n = _tz_re.sub('',n)
try:
if n[-1]=='.': n = n[:-1]
except:
print i, n
raise
A((n[0]!='0' or len(n)==1) and n or n[1:])
return string.join(s)
#hack test for comma users
if ',' in fp_str(0.25):
_FP_STR = fp_str
def fp_str(*a):
return string.replace(apply(_FP_STR,a),',','.')
def recursiveImport(modulename, baseDir=None, noCWD=0, debug=0):
"""Dynamically imports possible packagized module, or raises ImportError"""
normalize = lambda x: os.path.normcase(os.path.abspath(os.path.normpath(x)))
path = map(normalize,sys.path)
if baseDir:
if not isSeqType(baseDir):
tp = [baseDir]
else:
tp = filter(None,list(baseDir))
for p in tp:
p = normalize(p)
if p not in path: path.insert(0,p)
if noCWD:
for p in ('','.',normalize('.')):
while p in path:
if debug: print 'removed "%s" from path' % p
path.remove(p)
elif '.' not in path:
path.insert(0,'.')
if debug:
import pprint
pp = pprint.pprint
print 'path=',
pp(path)
#make import errors a bit more informative
opath = sys.path
try:
sys.path = path
exec 'import %s\nm = %s\n' % (modulename,modulename) in locals()
sys.path = opath
return m
except ImportError:
sys.path = opath
msg = "recursiveimport(%s,baseDir=%s) failed" % (modulename,baseDir)
if baseDir:
msg = msg + " under paths '%s'" % `path`
raise ImportError, msg
def recursiveGetAttr(obj, name):
"Can call down into e.g. object1.object2[4].attr"
return eval(name, obj.__dict__)
def recursiveSetAttr(obj, name, value):
"Can call down into e.g. object1.object2[4].attr = value"
#get the thing above last.
tokens = string.split(name, '.')
if len(tokens) == 1:
setattr(obj, name, value)
else:
most = string.join(tokens[:-1], '.')
last = tokens[-1]
parent = recursiveGetAttr(obj, most)
setattr(parent, last, value)
def import_zlib():
try:
import zlib
except ImportError:
zlib = None
from reportlab.rl_config import ZLIB_WARNINGS
if ZLIB_WARNINGS: warnOnce('zlib not available')
return zlib
# Image Capability Detection. Set a flag haveImages
# to tell us if either PIL or Java imaging libraries present.
# define PIL_Image as either None, or an alias for the PIL.Image
# module, as there are 2 ways to import it
if sys.platform[0:4] == 'java':
try:
import javax.imageio
import java.awt.image
haveImages = 1
except:
haveImages = 0
else:
try:
from PIL import Image
except ImportError:
try:
import Image
except ImportError:
Image = None
haveImages = Image is not None
if haveImages: del Image
__StringIO=None
def getStringIO(buf=None):
'''unified StringIO instance interface'''
global __StringIO
if not __StringIO:
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
__StringIO = StringIO
return buf is not None and __StringIO(buf) or __StringIO()
class ArgvDictValue:
'''A type to allow clients of getArgvDict to specify a conversion function'''
def __init__(self,value,func):
self.value = value
self.func = func
def getArgvDict(**kw):
''' Builds a dictionary from its keyword arguments with overrides from sys.argv.
Attempts to be smart about conversions, but the value can be an instance
of ArgDictValue to allow specifying a conversion function.
'''
def handleValue(v,av,func):
if func:
v = func(av)
else:
t = type(v)
if t is StringType:
v = av
elif t is FloatType:
v = float(av)
elif t is IntType:
v = int(av)
elif t is ListType:
v = list(eval(av))
elif t is TupleType:
v = tuple(eval(av))
else:
raise TypeError, "Can't convert string '%s' to %s" % (av,str(t))
return v
A = sys.argv[1:]
R = {}
for k, v in kw.items():
if isinstance(v,ArgvDictValue):
v, func = v.value, v.func
else:
func = None
handled = 0
ke = k+'='
for a in A:
if string.find(a,ke)==0:
av = a[len(ke):]
A.remove(a)
R[k] = handleValue(v,av,func)
handled = 1
break
if not handled: R[k] = handleValue(v,v,func)
return R
def getHyphenater(hDict=None):
try:
from reportlab.lib.pyHnj import Hyphen
if hDict is None: hDict=os.path.join(os.path.dirname(__file__),'hyphen.mashed')
return Hyphen(hDict)
except ImportError, errMsg:
if str(errMsg)!='No module named pyHnj': raise
return None
def _className(self):
'''Return a shortened class name'''
try:
name = self.__class__.__name__
i=string.rfind(name,'.')
if i>=0: return name[i+1:]
return name
except AttributeError:
return str(self)
def open_for_read_by_name(name,mode='b'):
if 'r' not in mode: mode = 'r'+mode
try:
return open(name,mode)
except IOError:
if _isFSD or __loader__ is None: raise
#we have a __loader__, perhaps the filename starts with
#the dirname(reportlab.__file__) or is relative
name = _startswith_rl(name)
s = __loader__.get_data(name)
if 'b' not in mode and os.linesep!='\n': s = s.replace(os.linesep,'\n')
return getStringIO(s)
import urllib
def open_for_read(name,mode='b', urlopen=urllib.urlopen):
'''attempt to open a file or URL for reading'''
if hasattr(name,'read'): return name
try:
return open_for_read_by_name(name,mode)
except:
try:
return getStringIO(urlopen(name).read())
except:
raise IOError('Cannot open resource "%s"' % name)
del urllib
def open_and_read(name,mode='b'):
return open_for_read(name,mode).read()
def open_and_readlines(name,mode='t'):
return open_and_read(name,mode).split('\n')
def rl_isfile(fn,os_path_isfile=os.path.isfile):
if hasattr(fn,'read'): return True
if os_path_isfile(fn): return True
if _isFSD or __loader__ is None: return False
fn = _startswith_rl(fn)
return fn in __loader__._files.keys()
def rl_isdir(pn,os_path_isdir=os.path.isdir,os_path_normpath=os.path.normpath):
if os_path_isdir(pn): return True
if _isFSD or __loader__ is None: return False
pn = _startswith_rl(os_path_normpath(pn))
if not pn.endswith(os.sep): pn += os.sep
return len(filter(lambda x,pn=pn: x.startswith(pn),__loader__._files.keys()))>0
def rl_get_module(name,dir):
if sys.modules.has_key(name):
om = sys.modules[name]
del sys.modules[name]
else:
om = None
try:
f = None
try:
f, p, desc= imp.find_module(name,[dir])
return imp.load_module(name,f,p,desc)
except:
if isCompactDistro():
#attempt a load from inside the zip archive
import zipimport
dir = _startswith_rl(dir)
dir = (dir=='.' or not dir) and _archive or os.path.join(_archive,dir.replace('/',os.sep))
zi = zipimport.zipimporter(dir)
return zi.load_module(name)
raise ImportError('%s[%s]' % (name,dir))
finally:
if om: sys.modules[name] = om
del om
if f: f.close()
def _isPILImage(im):
try:
from PIL.Image import Image
return isinstance(im,Image)
except ImportError:
return 0
class ImageReader:
"Wraps up either PIL or Java to get data from bitmaps"
def __init__(self, fileName):
if isinstance(fileName,ImageReader):
self.__dict__ = fileName.__dict__ #borgize
return
if not haveImages:
raise RuntimeError('Imaging Library not available, unable to import bitmaps')
#start wih lots of null private fields, to be populated by
#the relevant engine.
self.fileName = fileName
self._image = None
self._width = None
self._height = None
self._transparent = None
self._data = None
if _isPILImage(fileName):
self._image = fileName
self.fp = fileName.fp
try:
self.fileName = im.fileName
except AttributeError:
self.fileName = 'PILIMAGE_%d' % id(self)
else:
try:
self.fp = open_for_read(fileName,'b')
#detect which library we are using and open the image
if sys.platform[0:4] == 'java':
from javax.imageio import ImageIO
self._image = ImageIO.read(self.fp)
else:
import PIL.Image
self._image = PIL.Image.open(self.fp)
if self._image=='JPEG': self.jpeg_fh = self._jpeg_fp
except:
et,ev,tb = sys.exc_info()
if hasattr(ev,'args'):
a = str(ev.args[-1])+(' fileName='+fileName)
ev.args= ev.args[:-1]+(a,)
raise et,ev,tb
else:
raise
def _jpeg_fh(self):
fp = self.fp
fp.seek(0)
return fp
def jpeg_fh(self):
return None
def getSize(self):
if (self._width is None or self._height is None):
if sys.platform[0:4] == 'java':
self._width = self._image.getWidth()
self._height = self._image.getHeight()
else:
self._width, self._height = self._image.size
return (self._width, self._height)
def getRGBData(self):
"Return byte array of RGB data as string"
if self._data is None:
if sys.platform[0:4] == 'java':
import jarray
from java.awt.image import PixelGrabber
width, height = self.getSize()
buffer = jarray.zeros(width*height, 'i')
pg = PixelGrabber(self._image, 0,0,width,height,buffer,0,width)
pg.grabPixels()
# there must be a way to do this with a cast not a byte-level loop,
# I just haven't found it yet...
pixels = []
a = pixels.append
for i in range(len(buffer)):
rgb = buffer[i]
a(chr((rgb>>16)&0xff))
a(chr((rgb>>8)&0xff))
a(chr(rgb&0xff))
self._data = ''.join(pixels)
self.mode = 'RGB'
else:
im = self._image
mode = self.mode = im.mode
if mode not in ('L','RGB','CMYK'):
im = im.convert('RGB')
self.mode = 'RGB'
self._data = im.tostring()
return self._data
def getImageData(self):
width, height = self.getSize()
return width, height, self.getRGBData()
def getTransparent(self):
if sys.platform[0:4] == 'java':
return None
else:
if self._image.info.has_key("transparency"):
transparency = self._image.info["transparency"] * 3
palette = self._image.palette
try:
palette = palette.palette
except:
palette = palette.data
return map(ord, palette[transparency:transparency+3])
else:
return None
def getImageData(imageFileName):
"Get width, height and RGB pixels from image file. Wraps Java/PIL"
try:
return imageFileName.getImageData()
except AttributeError:
return ImageReader(imageFileName).getImageData()
class DebugMemo:
'''Intended as a simple report back encapsulator
Typical usages
1) To record error data
dbg = DebugMemo(fn='dbgmemo.dbg',myVar=value)
dbg.add(anotherPayload='aaaa',andagain='bbb')
dbg.dump()
2) To show the recorded info
dbg = DebugMemo(fn='dbgmemo.dbg',mode='r')
dbg.load()
dbg.show()
3) To re-use recorded information
dbg = DebugMemo(fn='dbgmemo.dbg',mode='r')
dbg.load()
myTestFunc(dbg.payload('myVar'),dbg.payload('andagain'))
in addition to the payload variables the dump records many useful bits
of information which are also printed in the show() method.
'''
def __init__(self,fn='rl_dbgmemo.dbg',mode='w',getScript=1,modules=(),capture_traceback=1, stdout=None, **kw):
import time, socket
self.fn = fn
if mode!='w': return
if not stdout:
self.stdout = sys.stdout
else:
if hasattr(stdout,'write'):
self.stdout = stdout
else:
self.stdout = open(stdout,'w')
self.store = store = {}
if capture_traceback and sys.exc_info() != (None,None,None):
import traceback
s = getStringIO()
traceback.print_exc(None,s)
store['__traceback'] = s.getvalue()
cwd=os.getcwd()
lcwd = os.listdir(cwd)
exed = os.path.abspath(os.path.dirname(sys.argv[0]))
store.update({ 'gmt': time.asctime(time.gmtime(time.time())),
'platform': sys.platform,
'version': sys.version,
'hexversion': hex(sys.hexversion),
'executable': sys.executable,
'exec_prefix': sys.exec_prefix,
'prefix': sys.prefix,
'path': sys.path,
'argv': sys.argv,
'cwd': cwd,
'hostname': socket.gethostname(),
'lcwd': lcwd,
'byteorder': sys.byteorder,
'maxint': sys.maxint,
'maxint': getattr(sys,'maxunicode','????'),
'api_version': getattr(sys,'api_version','????'),
'version_info': getattr(sys,'version_info','????'),
'winver': getattr(sys,'winver','????'),
'environment': os.environ,
})
for M,A in (
(sys,('getwindowsversion','getfilesystemencoding')),
(os,('uname', 'ctermid', 'getgid', 'getuid', 'getegid',
'geteuid', 'getlogin', 'getgroups', 'getpgrp', 'getpid', 'getppid',
)),
):
for a in A:
if hasattr(M,a):
try:
store[a] = getattr(M,a)()
except:
pass
if exed!=cwd:
try:
store.update({'exed': exed, 'lexed': os.listdir(exed),})
except:
pass
if getScript:
fn = os.path.abspath(sys.argv[0])
if os.path.isfile(fn):
try:
store['__script'] = (fn,open(fn,'r').read())
except:
pass
module_versions = {}
for n,m in sys.modules.items():
if n=='reportlab' or n=='rlextra' or n[:10]=='reportlab.' or n[:8]=='rlextra.':
v = getattr(m,'__version__',None)
if v: module_versions[n] = v
store['__module_versions'] = module_versions
self.store['__payload'] = {}
self._add(kw)
def _add(self,D):
payload = self.store['__payload']
for k, v in D.items():
payload[k] = v
def add(self,**kw):
self._add(kw)
def dump(self):
import pickle
pickle.dump(self.store,open(self.fn,'wb'))
def load(self):
import pickle
self.store = pickle.load(open(self.fn,'rb'))
def _show_module_versions(self,k,v):
self._writeln(k[2:])
K = v.keys()
K.sort()
for k in K:
vk = v[k]
try:
m = recursiveImport(k,sys.path[:],1)
d = getattr(m,'__version__',None)==vk and 'SAME' or 'DIFFERENT'
except:
m = None
d = '??????unknown??????'
self._writeln(' %s = %s (%s)' % (k,vk,d))
def _banner(self,k,what):
self._writeln('###################%s %s##################' % (what,k[2:]))
def _start(self,k):
self._banner(k,'Start ')
def _finish(self,k):
self._banner(k,'Finish ')
def _show_lines(self,k,v):
self._start(k)
self._writeln(v)
self._finish(k)
def _show_file(self,k,v):
k = '%s %s' % (k,os.path.basename(v[0]))
self._show_lines(k,v[1])
def _show_payload(self,k,v):
if v:
import pprint
self._start(k)
pprint.pprint(v,self.stdout)
self._finish(k)
specials = {'__module_versions': _show_module_versions,
'__payload': _show_payload,
'__traceback': _show_lines,
'__script': _show_file,
}
def show(self):
K = self.store.keys()
K.sort()
for k in K:
if k not in self.specials.keys(): self._writeln('%-15s = %s' % (k,self.store[k]))
for k in K:
if k in self.specials.keys(): apply(self.specials[k],(self,k,self.store[k]))
def payload(self,name):
return self.store['__payload'][name]
def __setitem__(self,name,value):
self.store['__payload'][name] = value
def __getitem__(self,name):
return self.store['__payload'][name]
def _writeln(self,msg):
self.stdout.write(msg+'\n')
def _flatten(L,a):
for x in L:
if isSeqType(x): _flatten(x,a)
else: a(x)
def flatten(L):
'''recursively flatten the list or tuple L'''
R = []
_flatten(L,R.append)
return R
def find_locals(func,depth=0):
'''apply func to the locals at each stack frame till func returns a non false value'''
while 1:
_ = func(sys._getframe(depth).f_locals)
if _: return _
depth += 1
class _FmtSelfDict:
def __init__(self,obj,overrideArgs):
self.obj = obj
self._overrideArgs = overrideArgs
def __getitem__(self,k):
try:
return self._overrideArgs[k]
except KeyError:
try:
return self.obj.__dict__[k]
except KeyError:
return getattr(self.obj,k)
class FmtSelfDict:
'''mixin to provide the _fmt method'''
def _fmt(self,fmt,**overrideArgs):
D = _FmtSelfDict(self, overrideArgs)
return fmt % D

View File

@ -0,0 +1,324 @@
#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/lib/validators.py
__version__=''' $Id: validators.py 2875 2006-05-18 07:00:16Z andy $ '''
"""
This module contains some standard verifying functions which can be
used in an attribute map.
"""
import string, sys, codecs
from types import *
_SequenceTypes = (ListType,TupleType)
_NumberTypes = (FloatType,IntType)
from reportlab.lib import colors
if sys.hexversion<0x2030000:
True = 1
False = 0
class Validator:
"base validator class"
def __call__(self,x):
return self.test(x)
def __str__(self):
return getattr(self,'_str',self.__class__.__name__)
def normalize(self,x):
return x
def normalizeTest(self,x):
try:
self.normalize(x)
return True
except:
return False
class _isAnything(Validator):
def test(self,x):
return True
class _isNothing(Validator):
def test(self,x):
return False
class _isBoolean(Validator):
if sys.hexversion>=0x2030000:
def test(self,x):
if type(x) in (IntType,BooleanType): return x in (0,1)
return self.normalizeTest(x)
else:
def test(self,x):
if type(x) is IntType: return x in (0,1)
return self.normalizeTest(x)
def normalize(self,x):
if x in (0,1): return x
try:
S = string.upper(x)
except:
raise ValueError, 'Must be boolean'
if S in ('YES','TRUE'): return True
if S in ('NO','FALSE',None): return False
raise ValueError, 'Must be boolean'
class _isString(Validator):
def test(self,x):
return type(x) in (StringType, UnicodeType)
class _isCodec(Validator):
def test(self,x):
if type(x) not in (StringType, UnicodeType):
return False
try:
a,b,c,d = codecs.lookup(x)
return True
except LookupError:
return False
class _isNumber(Validator):
def test(self,x):
if type(x) in _NumberTypes: return True
return self.normalizeTest(x)
def normalize(self,x):
try:
return float(x)
except:
return int(x)
class _isInt(Validator):
def test(self,x):
if type(x) not in (IntType,StringType): return False
return self.normalizeTest(x)
def normalize(self,x):
return int(x)
class _isNumberOrNone(_isNumber):
def test(self,x):
return x is None or isNumber(x)
def normalize(self,x):
if x is None: return x
return _isNumber.normalize(x)
class _isListOfNumbersOrNone(Validator):
"ListOfNumbersOrNone validator class."
def test(self, x):
if x is None: return True
return isListOfNumbers(x)
class _isListOfShapes(Validator):
"ListOfShapes validator class."
def test(self, x):
from reportlab.graphics.shapes import Shape
if type(x) in _SequenceTypes:
answer = 1
for element in x:
if not isinstance(x, Shape):
answer = 0
return answer
else:
return False
class _isListOfStringsOrNone(Validator):
"ListOfStringsOrNone validator class."
def test(self, x):
if x is None: return True
return isListOfStrings(x)
class _isTransform(Validator):
"Transform validator class."
def test(self, x):
if type(x) in _SequenceTypes:
if len(x) == 6:
for element in x:
if not isNumber(element):
return False
return True
else:
return False
else:
return False
class _isColor(Validator):
"Color validator class."
def test(self, x):
return isinstance(x, colors.Color)
class _isColorOrNone(Validator):
"ColorOrNone validator class."
def test(self, x):
if x is None: return True
return isColor(x)
class _isValidChild(Validator):
"ValidChild validator class."
def test(self, x):
"""Is this child allowed in a drawing or group?
I.e. does it descend from Shape or UserNode?
"""
from reportlab.graphics.shapes import UserNode, Shape
return isinstance(x, UserNode) or isinstance(x, Shape)
class _isValidChildOrNone(_isValidChild):
def test(self,x):
return _isValidChild.test(self,x) or x is None
class _isCallable(Validator):
def test(self, x):
return callable(x)
class OneOf(Validator):
"""Make validator functions for list of choices.
Usage:
f = reportlab.lib.validators.OneOf('happy','sad')
or
f = reportlab.lib.validators.OneOf(('happy','sad'))
f('sad'),f('happy'), f('grumpy')
(1,1,0)
"""
def __init__(self, enum,*args):
if type(enum) in [ListType,TupleType]:
if args!=():
raise ValueError, "Either all singleton args or a single sequence argument"
self._enum = tuple(enum)+args
else:
self._enum = (enum,)+args
def test(self, x):
return x in self._enum
class SequenceOf(Validator):
def __init__(self,elemTest,name=None,emptyOK=1, NoneOK=0, lo=0,hi=0x7fffffff):
self._elemTest = elemTest
self._emptyOK = emptyOK
self._NoneOK = NoneOK
self._lo, self._hi = lo, hi
if name: self._str = name
def test(self, x):
if type(x) not in _SequenceTypes:
if x is None: return self._NoneOK
return False
if x==[] or x==():
return self._emptyOK
elif not self._lo<=len(x)<=self._hi: return False
for e in x:
if not self._elemTest(e): return False
return True
class EitherOr(Validator):
def __init__(self,tests,name=None):
if type(tests) not in _SequenceTypes: tests = (tests,)
self._tests = tests
if name: self._str = name
def test(self, x):
for t in self._tests:
if t(x): return True
return False
class NoneOr(Validator):
def __init__(self,elemTest,name=None):
self._elemTest = elemTest
if name: self._str = name
def test(self, x):
if x is None: return True
return self._elemTest(x)
class Auto(Validator):
def __init__(self,**kw):
self.__dict__.update(kw)
def test(self,x):
return x is self.__class__ or isinstance(x,self.__class__)
class AutoOr(NoneOr):
def test(self,x):
return isAuto(x) or self._elemTest(x)
class isInstanceOf(Validator):
def __init__(self,klass=None):
self._klass = klass
def test(self,x):
return isinstance(x,self._klass)
class matchesPattern(Validator):
"""Matches value, or its string representation, against regex"""
def __init__(self, pattern):
self._pattern = re.compile(pattern)
def test(self,x):
print 'testing %s against %s' % (x, self._pattern)
if type(x) is StringType:
text = x
else:
text = str(x)
return (self._pattern.match(text) <> None)
class DerivedValue:
"""This is used for magic values which work themselves out.
An example would be an "inherit" property, so that one can have
drawing.chart.categoryAxis.labels.fontName = inherit
and pick up the value from the top of the drawing.
Validators will permit this provided that a value can be pulled
in which satisfies it. And the renderer will have special
knowledge of these so they can evaluate themselves.
"""
def getValue(self, renderer, attr):
"""Override this. The renderers will pass the renderer,
and the attribute name. Algorithms can then backtrack up
through all the stuff the renderer provides, including
a correct stack of parent nodes."""
return None
class Inherit(DerivedValue):
def __repr__(self):
return "inherit"
def getValue(self, renderer, attr):
return renderer.getStateValue(attr)
inherit = Inherit()
isAuto = Auto()
isBoolean = _isBoolean()
isString = _isString()
isCodec = _isCodec()
isNumber = _isNumber()
isInt = _isInt()
isNoneOrInt = NoneOr(isInt,'isNoneOrInt')
isNumberOrNone = _isNumberOrNone()
isTextAnchor = OneOf('start','middle','end','boxauto')
isListOfNumbers = SequenceOf(isNumber,'isListOfNumbers')
isListOfNumbersOrNone = _isListOfNumbersOrNone()
isListOfShapes = _isListOfShapes()
isListOfStrings = SequenceOf(isString,'isListOfStrings')
isListOfStringsOrNone = _isListOfStringsOrNone()
isTransform = _isTransform()
isColor = _isColor()
isListOfColors = SequenceOf(isColor,'isListOfColors')
isColorOrNone = _isColorOrNone()
isShape = isValidChild = _isValidChild()
isNoneOrShape = isValidChildOrNone = _isValidChildOrNone()
isAnything = _isAnything()
isNothing = _isNothing()
isXYCoord = SequenceOf(isNumber,lo=2,hi=2,emptyOK=0)
isBoxAnchor = OneOf('nw','n','ne','w','c','e','sw','s','se', 'autox', 'autoy')
isNoneOrString = NoneOr(isString,'NoneOrString')
isNoneOrListOfNoneOrStrings=SequenceOf(isNoneOrString,'isNoneOrListOfNoneOrStrings',NoneOK=1)
isListOfNoneOrString=SequenceOf(isNoneOrString,'isListOfNoneOrString',NoneOK=0)
isNoneOrListOfNoneOrNumbers=SequenceOf(isNumberOrNone,'isNoneOrListOfNoneOrNumbers',NoneOK=1)
isCallable = _isCallable()
isStringOrCallable=EitherOr((isString,isCallable),'isStringOrCallable')
isStringOrCallableOrNone=NoneOr(isStringOrCallable,'isStringOrCallableNone')
isStringOrNone=NoneOr(isString,'isStringOrNone')

769
bin/reportlab/lib/xmllib.py Normal file
View File

@ -0,0 +1,769 @@
# A parser for XML, using the derived class as static DTD.
# Author: Sjoerd Mullender.
# sgmlop support added by fredrik@pythonware.com (May 19, 1998)
import re
import string
try:
import sgmlop # this works for both builtin on the path or relative
except ImportError:
sgmlop = None
# standard entity defs
ENTITYDEFS = {
'lt': '<',
'gt': '>',
'amp': '&',
'quot': '"',
'apos': '\''
}
# XML parser base class -- find tags and call handler functions.
# Usage: p = XMLParser(); p.feed(data); ...; p.close().
# The dtd is defined by deriving a class which defines methods with
# special names to handle tags: start_foo and end_foo to handle <foo>
# and </foo>, respectively. The data between tags is passed to the
# parser by calling self.handle_data() with some data as argument (the
# data may be split up in arbutrary chunks). Entity references are
# passed by calling self.handle_entityref() with the entity reference
# as argument.
# --------------------------------------------------------------------
# original re-based XML parser
_S = '[ \t\r\n]+'
_opS = '[ \t\r\n]*'
_Name = '[a-zA-Z_:][-a-zA-Z0-9._:]*'
interesting = re.compile('[&<]')
incomplete = re.compile('&(' + _Name + '|#[0-9]*|#x[0-9a-fA-F]*)?|'
'<([a-zA-Z_:][^<>]*|'
'/([a-zA-Z_:][^<>]*)?|'
'![^<>]*|'
'\?[^<>]*)?')
ref = re.compile('&(' + _Name + '|#[0-9]+|#x[0-9a-fA-F]+);?')
entityref = re.compile('&(?P<name>' + _Name + ')[^-a-zA-Z0-9._:]')
charref = re.compile('&#(?P<char>[0-9]+[^0-9]|x[0-9a-fA-F]+[^0-9a-fA-F])')
space = re.compile(_S)
newline = re.compile('\n')
starttagopen = re.compile('<' + _Name)
endtagopen = re.compile('</')
starttagend = re.compile(_opS + '(?P<slash>/?)>')
endbracket = re.compile('>')
tagfind = re.compile(_Name)
cdataopen = re.compile('<!\[CDATA\[')
cdataclose = re.compile('\]\]>')
special = re.compile('<!(?P<special>[^<>]*)>')
procopen = re.compile('<\?(?P<proc>' + _Name + ')' + _S)
procclose = re.compile('\?>')
commentopen = re.compile('<!--')
commentclose = re.compile('-->')
doubledash = re.compile('--')
attrfind = re.compile(
_opS + '(?P<name>' + _Name + ')'
'(' + _opS + '=' + _opS +
'(?P<value>\'[^\']*\'|"[^"]*"|[-a-zA-Z0-9.:+*%?!()_#=~]+))')
class SlowXMLParser:
# Interface -- initialize and reset this instance
def __init__(self, verbose=0):
self.verbose = verbose
self.reset()
# Interface -- reset this instance. Loses all unprocessed data
def reset(self):
self.rawdata = ''
self.stack = []
self.lasttag = '???'
self.nomoretags = 0
self.literal = 0
self.lineno = 1
# For derived classes only -- enter literal mode (CDATA) till EOF
def setnomoretags(self):
self.nomoretags = self.literal = 1
# For derived classes only -- enter literal mode (CDATA)
def setliteral(self, *args):
self.literal = 1
# Interface -- feed some data to the parser. Call this as
# often as you want, with as little or as much text as you
# want (may include '\n'). (This just saves the text, all the
# processing is done by goahead().)
def feed(self, data):
self.rawdata = self.rawdata + data
self.goahead(0)
# Interface -- handle the remaining data
def close(self):
self.goahead(1)
# Interface -- translate references
def translate_references(self, data):
newdata = []
i = 0
while 1:
res = ref.search(data, i)
if res is None:
newdata.append(data[i:])
return string.join(newdata, '')
if data[res.end(0) - 1] != ';':
self.syntax_error(self.lineno,
'; missing after entity/char reference')
newdata.append(data[i:res.start(0)])
str = res.group(1)
if str[0] == '#':
if str[1] == 'x':
newdata.append(chr(string.atoi(str[2:], 16)))
else:
newdata.append(chr(string.atoi(str[1:])))
else:
try:
newdata.append(self.entitydefs[str])
except KeyError:
# can't do it, so keep the entity ref in
newdata.append('&' + str + ';')
i = res.end(0)
# Internal -- handle data as far as reasonable. May leave state
# and data to be processed by a subsequent call. If 'end' is
# true, force handling all data as if followed by EOF marker.
def goahead(self, end):
rawdata = self.rawdata
i = 0
n = len(rawdata)
while i < n:
if self.nomoretags:
data = rawdata[i:n]
self.handle_data(data)
self.lineno = self.lineno + string.count(data, '\n')
i = n
break
res = interesting.search(rawdata, i)
if res:
j = res.start(0)
else:
j = n
if i < j:
data = rawdata[i:j]
self.handle_data(data)
self.lineno = self.lineno + string.count(data, '\n')
i = j
if i == n: break
if rawdata[i] == '<':
if starttagopen.match(rawdata, i):
if self.literal:
data = rawdata[i]
self.handle_data(data)
self.lineno = self.lineno + string.count(data, '\n')
i = i+1
continue
k = self.parse_starttag(i)
if k < 0: break
self.lineno = self.lineno + string.count(rawdata[i:k], '\n')
i = k
continue
if endtagopen.match(rawdata, i):
k = self.parse_endtag(i)
if k < 0: break
self.lineno = self.lineno + string.count(rawdata[i:k], '\n')
i = k
self.literal = 0
continue
if commentopen.match(rawdata, i):
if self.literal:
data = rawdata[i]
self.handle_data(data)
self.lineno = self.lineno + string.count(data, '\n')
i = i+1
continue
k = self.parse_comment(i)
if k < 0: break
self.lineno = self.lineno + string.count(rawdata[i:k], '\n')
i = k
continue
if cdataopen.match(rawdata, i):
k = self.parse_cdata(i)
if k < 0: break
self.lineno = self.lineno + string.count(rawdata[i:i], '\n')
i = k
continue
res = procopen.match(rawdata, i)
if res:
k = self.parse_proc(i, res)
if k < 0: break
self.lineno = self.lineno + string.count(rawdata[i:k], '\n')
i = k
continue
res = special.match(rawdata, i)
if res:
if self.literal:
data = rawdata[i]
self.handle_data(data)
self.lineno = self.lineno + string.count(data, '\n')
i = i+1
continue
self.handle_special(res.group('special'))
self.lineno = self.lineno + string.count(res.group(0), '\n')
i = res.end(0)
continue
elif rawdata[i] == '&':
res = charref.match(rawdata, i)
if res is not None:
i = res.end(0)
if rawdata[i-1] != ';':
self.syntax_error(self.lineno, '; missing in charref')
i = i-1
self.handle_charref(res.group('char')[:-1])
self.lineno = self.lineno + string.count(res.group(0), '\n')
continue
res = entityref.match(rawdata, i)
if res is not None:
i = res.end(0)
if rawdata[i-1] != ';':
self.syntax_error(self.lineno, '; missing in entityref')
i = i-1
self.handle_entityref(res.group('name'))
self.lineno = self.lineno + string.count(res.group(0), '\n')
continue
else:
raise RuntimeError, 'neither < nor & ??'
# We get here only if incomplete matches but
# nothing else
res = incomplete.match(rawdata, i)
if not res:
data = rawdata[i]
self.handle_data(data)
self.lineno = self.lineno + string.count(data, '\n')
i = i+1
continue
j = res.end(0)
if j == n:
break # Really incomplete
self.syntax_error(self.lineno, 'bogus < or &')
data = res.group(0)
self.handle_data(data)
self.lineno = self.lineno + string.count(data, '\n')
i = j
# end while
if end and i < n:
data = rawdata[i:n]
self.handle_data(data)
self.lineno = self.lineno + string.count(data, '\n')
i = n
self.rawdata = rawdata[i:]
# XXX if end: check for empty stack
# Internal -- parse comment, return length or -1 if not terminated
def parse_comment(self, i):
rawdata = self.rawdata
if rawdata[i:i+4] <> '<!--':
raise RuntimeError, 'unexpected call to handle_comment'
res = commentclose.search(rawdata, i+4)
if not res:
return -1
# doubledash search will succeed because it's a subset of commentclose
if doubledash.search(rawdata, i+4).start(0) < res.start(0):
self.syntax_error(self.lineno, "`--' inside comment")
self.handle_comment(rawdata[i+4: res.start(0)])
return res.end(0)
# Internal -- handle CDATA tag, return lenth or -1 if not terminated
def parse_cdata(self, i):
rawdata = self.rawdata
if rawdata[i:i+9] <> '<![CDATA[':
raise RuntimeError, 'unexpected call to handle_cdata'
res = cdataclose.search(rawdata, i+9)
if not res:
return -1
self.handle_cdata(rawdata[i+9:res.start(0)])
return res.end(0)
def parse_proc(self, i, res):
rawdata = self.rawdata
if not res:
raise RuntimeError, 'unexpected call to parse_proc'
name = res.group('proc')
res = procclose.search(rawdata, res.end(0))
if not res:
return -1
self.handle_proc(name, rawdata[res.pos:res.start(0)])
return res.end(0)
# Internal -- handle starttag, return length or -1 if not terminated
def parse_starttag(self, i):
rawdata = self.rawdata
# i points to start of tag
end = endbracket.search(rawdata, i+1)
if not end:
return -1
j = end.start(0)
# Now parse the data between i+1 and j into a tag and attrs
attrdict = {}
res = tagfind.match(rawdata, i+1)
if not res:
raise RuntimeError, 'unexpected call to parse_starttag'
k = res.end(0)
tag = res.group(0)
if hasattr(self, tag + '_attributes'):
attrlist = getattr(self, tag + '_attributes')
else:
attrlist = None
self.lasttag = tag
while k < j:
res = attrfind.match(rawdata, k)
if not res: break
attrname, attrvalue = res.group('name', 'value')
if attrvalue is None:
self.syntax_error(self.lineno, 'no attribute value specified')
attrvalue = attrname
elif attrvalue[:1] == "'" == attrvalue[-1:] or \
attrvalue[:1] == '"' == attrvalue[-1:]:
attrvalue = attrvalue[1:-1]
else:
self.syntax_error(self.lineno, 'attribute value not quoted')
if attrlist is not None and attrname not in attrlist:
self.syntax_error(self.lineno,
'unknown attribute %s of element %s' %
(attrname, tag))
if attrdict.has_key(attrname):
self.syntax_error(self.lineno, 'attribute specified twice')
attrdict[attrname] = self.translate_references(attrvalue)
k = res.end(0)
res = starttagend.match(rawdata, k)
if not res:
self.syntax_error(self.lineno, 'garbage in start tag')
self.finish_starttag(tag, attrdict)
if res and res.group('slash') == '/':
self.finish_endtag(tag)
return end.end(0)
# Internal -- parse endtag
def parse_endtag(self, i):
rawdata = self.rawdata
end = endbracket.search(rawdata, i+1)
if not end:
return -1
res = tagfind.match(rawdata, i+2)
if not res:
self.syntax_error(self.lineno, 'no name specified in end tag')
tag = ''
k = i+2
else:
tag = res.group(0)
k = res.end(0)
if k != end.start(0):
# check that there is only white space at end of tag
res = space.match(rawdata, k)
if res is None or res.end(0) != end.start(0):
self.syntax_error(self.lineno, 'garbage in end tag')
self.finish_endtag(tag)
return end.end(0)
# Internal -- finish processing of start tag
# Return -1 for unknown tag, 1 for balanced tag
def finish_starttag(self, tag, attrs):
self.stack.append(tag)
try:
method = getattr(self, 'start_' + tag)
except AttributeError:
self.unknown_starttag(tag, attrs)
return -1
else:
self.handle_starttag(tag, method, attrs)
return 1
# Internal -- finish processing of end tag
def finish_endtag(self, tag):
if not tag:
found = len(self.stack) - 1
if found < 0:
self.unknown_endtag(tag)
return
else:
if tag not in self.stack:
try:
method = getattr(self, 'end_' + tag)
except AttributeError:
self.unknown_endtag(tag)
return
found = len(self.stack)
for i in range(found):
if self.stack[i] == tag: found = i
while len(self.stack) > found:
tag = self.stack[-1]
try:
method = getattr(self, 'end_' + tag)
except AttributeError:
method = None
if method:
self.handle_endtag(tag, method)
else:
self.unknown_endtag(tag)
del self.stack[-1]
# Overridable -- handle start tag
def handle_starttag(self, tag, method, attrs):
method(attrs)
# Overridable -- handle end tag
def handle_endtag(self, tag, method):
method()
# Example -- handle character reference, no need to override
def handle_charref(self, name):
try:
if name[0] == 'x':
n = string.atoi(name[1:], 16)
else:
n = string.atoi(name)
except string.atoi_error:
self.unknown_charref(name)
return
if not 0 <= n <= 255:
self.unknown_charref(name)
return
self.handle_data(chr(n))
# Definition of entities -- derived classes may override
entitydefs = ENTITYDEFS
# Example -- handle entity reference, no need to override
def handle_entityref(self, name):
table = self.entitydefs
if table.has_key(name):
self.handle_data(table[name])
else:
self.unknown_entityref(name)
return
# Example -- handle data, should be overridden
def handle_data(self, data):
pass
# Example -- handle cdata, could be overridden
def handle_cdata(self, data):
pass
# Example -- handle comment, could be overridden
def handle_comment(self, data):
pass
# Example -- handle processing instructions, could be overridden
def handle_proc(self, name, data):
pass
# Example -- handle special instructions, could be overridden
def handle_special(self, data):
pass
# Example -- handle relatively harmless syntax errors, could be overridden
def syntax_error(self, lineno, message):
raise RuntimeError, 'Syntax error at line %d: %s' % (lineno, message)
# To be overridden -- handlers for unknown objects
def unknown_starttag(self, tag, attrs): pass
def unknown_endtag(self, tag): pass
def unknown_charref(self, ref): pass
def unknown_entityref(self, ref): pass
# --------------------------------------------------------------------
# accelerated XML parser
class FastXMLParser:
# Interface -- initialize and reset this instance
def __init__(self, verbose=0):
self.verbose = verbose
self.reset()
# Interface -- reset this instance. Loses all unprocessed data
def reset(self):
self.rawdata = ''
self.stack = []
self.lasttag = '???'
self.nomoretags = 0
self.literal = 0
self.lineno = 1
self.parser = sgmlop.XMLParser()
self.feed = self.parser.feed
self.parser.register(self)
# For derived classes only -- enter literal mode (CDATA) till EOF
def setnomoretags(self):
self.nomoretags = self.literal = 1
# For derived classes only -- enter literal mode (CDATA)
def setliteral(self, *args):
self.literal = 1
# Interface -- feed some data to the parser. Call this as
# often as you want, with as little or as much text as you
# want (may include '\n'). (This just saves the text, all the
# processing is done by goahead().)
def feed(self, data): # overridden by reset
self.parser.feed(data)
# Interface -- handle the remaining data
def close(self):
try:
self.parser.close()
finally:
self.parser = None
# Interface -- translate references
def translate_references(self, data):
newdata = []
i = 0
while 1:
res = ref.search(data, i)
if res is None:
newdata.append(data[i:])
return string.join(newdata, '')
if data[res.end(0) - 1] != ';':
self.syntax_error(self.lineno,
'; missing after entity/char reference')
newdata.append(data[i:res.start(0)])
str = res.group(1)
if str[0] == '#':
if str[1] == 'x':
newdata.append(chr(string.atoi(str[2:], 16)))
else:
newdata.append(chr(string.atoi(str[1:])))
else:
try:
newdata.append(self.entitydefs[str])
except KeyError:
# can't do it, so keep the entity ref in
newdata.append('&' + str + ';')
i = res.end(0)
# Internal -- finish processing of start tag
# Return -1 for unknown tag, 1 for balanced tag
def finish_starttag(self, tag, attrs):
self.stack.append(tag)
try:
method = getattr(self, 'start_' + tag)
except AttributeError:
self.unknown_starttag(tag, attrs)
return -1
else:
self.handle_starttag(tag, method, attrs)
return 1
# Internal -- finish processing of end tag
def finish_endtag(self, tag):
if not tag:
found = len(self.stack) - 1
if found < 0:
self.unknown_endtag(tag)
return
else:
if tag not in self.stack:
try:
method = getattr(self, 'end_' + tag)
except AttributeError:
self.unknown_endtag(tag)
return
found = len(self.stack)
for i in range(found):
if self.stack[i] == tag: found = i
while len(self.stack) > found:
tag = self.stack[-1]
try:
method = getattr(self, 'end_' + tag)
except AttributeError:
method = None
if method:
self.handle_endtag(tag, method)
else:
self.unknown_endtag(tag)
del self.stack[-1]
# Overridable -- handle start tag
def handle_starttag(self, tag, method, attrs):
method(attrs)
# Overridable -- handle end tag
def handle_endtag(self, tag, method):
method()
# Example -- handle character reference, no need to override
def handle_charref(self, name):
try:
if name[0] == 'x':
n = string.atoi(name[1:], 16)
else:
n = string.atoi(name)
except string.atoi_error:
self.unknown_charref(name)
return
if not 0 <= n <= 255:
self.unknown_charref(name)
return
self.handle_data(chr(n))
# Definition of entities -- derived classes may override
entitydefs = ENTITYDEFS
# Example -- handle entity reference, no need to override
def handle_entityref(self, name):
table = self.entitydefs
if table.has_key(name):
self.handle_data(table[name])
else:
self.unknown_entityref(name)
return
# Example -- handle data, should be overridden
def handle_data(self, data):
pass
# Example -- handle cdata, could be overridden
def handle_cdata(self, data):
pass
# Example -- handle comment, could be overridden
def handle_comment(self, data):
pass
# Example -- handle processing instructions, could be overridden
def handle_proc(self, name, data):
pass
# Example -- handle special instructions, could be overridden
def handle_special(self, data):
pass
# Example -- handle relatively harmless syntax errors, could be overridden
def syntax_error(self, lineno, message):
raise RuntimeError, 'Syntax error at line %d: %s' % (lineno, message)
# To be overridden -- handlers for unknown objects
def unknown_starttag(self, tag, attrs): pass
def unknown_endtag(self, tag): pass
def unknown_charref(self, ref): pass
def unknown_entityref(self, ref): pass
#sgmlop = None
# pick a suitable parser
if sgmlop:
XMLParser = FastXMLParser
else:
XMLParser = SlowXMLParser
# --------------------------------------------------------------------
# test stuff
class TestXMLParser(XMLParser):
def __init__(self, verbose=0):
self.testdata = ""
XMLParser.__init__(self, verbose)
def handle_data(self, data):
self.testdata = self.testdata + data
if len(`self.testdata`) >= 70:
self.flush()
def flush(self):
data = self.testdata
if data:
self.testdata = ""
print 'data:', `data`
def handle_cdata(self, data):
self.flush()
print 'cdata:', `data`
def handle_proc(self, name, data):
self.flush()
print 'processing:',name,`data`
def handle_special(self, data):
self.flush()
print 'special:',`data`
def handle_comment(self, data):
self.flush()
r = `data`
if len(r) > 68:
r = r[:32] + '...' + r[-32:]
print 'comment:', r
def syntax_error(self, lineno, message):
print 'error at line %d:' % lineno, message
def unknown_starttag(self, tag, attrs):
self.flush()
if not attrs:
print 'start tag: <' + tag + '>'
else:
print 'start tag: <' + tag,
for name, value in attrs.items():
print name + '=' + '"' + value + '"',
print '>'
def unknown_endtag(self, tag):
self.flush()
print 'end tag: </' + tag + '>'
def unknown_entityref(self, ref):
self.flush()
print '*** unknown entity ref: &' + ref + ';'
def unknown_charref(self, ref):
self.flush()
print '*** unknown char ref: &#' + ref + ';'
def close(self):
XMLParser.close(self)
self.flush()
def test(args = None):
import sys
if not args:
args = sys.argv[1:]
if args and args[0] == '-s':
args = args[1:]
klass = XMLParser
else:
klass = TestXMLParser
if args:
file = args[0]
else:
file = 'test.xml'
if file == '-':
f = sys.stdin
else:
try:
f = open(file, 'r')
except IOError, msg:
print file, ":", msg
sys.exit(1)
data = f.read()
if f is not sys.stdin:
f.close()
x = klass()
for c in data:
x.feed(c)
x.close()
if __name__ == '__main__': #NO_REPORTLAB_TEST
test()

188
bin/reportlab/lib/yaml.py Normal file
View File

@ -0,0 +1,188 @@
#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/lib/yaml.py
# parses "Yet Another Markup Language" into a list of tuples.
# Each tuple says what the data is e.g.
# ('Paragraph', 'Heading1', 'Why Reportlab Rules')
# and the pattern depends on type.
"""
.h1 Welcome to YAML!
YAML is "Yet Another Markup Language" - a markup language
which is easier to type in than XML, yet gives us a
reasonable selection of formats.
The general rule is that if a line begins with a '.',
it requires special processing. Otherwise lines
are concatenated to paragraphs, and blank lines
separate paragraphs.
If the line ".foo bar bletch" is encountered,
it immediately ends and writes out any current
paragraph.
It then looks for a parser method called 'foo';
if found, it is called with arguments (bar, bletch).
If this is not found, it assumes that 'foo' is a
paragraph style, and the text for the first line
of the paragraph is 'bar bletch'. It would be
up to the formatter to decide whether on not 'foo'
was a valid paragraph.
Special commands understood at present are:
dot image filename
- adds the image to the document
dot beginPre Code
- begins a Preformatted object in style 'Code'
dot endPre
- ends a preformatted object.
"""
__version__=''' $Id: yaml.py 2385 2004-06-17 15:26:05Z rgbecker $ '''
import sys
import string
#modes:
PLAIN = 1
PREFORMATTED = 2
BULLETCHAR = '\267' # assumes font Symbol, but works on all platforms
class BaseParser:
""""Simplest possible parser with only the most basic options.
This defines the line-handling abilities and basic mechanism.
The class YAMLParser includes capabilities for a fairly rich
story."""
def __init__(self):
self.reset()
def reset(self):
self._lineNo = 0
self._style = 'Normal' # the default
self._results = []
self._buf = []
self._mode = PLAIN
def parseFile(self, filename):
#returns list of objects
data = open(filename, 'r').readlines()
for line in data:
#strip trailing newlines
self.readLine(line[:-1])
self.endPara()
return self._results
def parseText(self, textBlock):
"Parses the a possible multi-line text block"
lines = string.split(textBlock, '\n')
for line in lines:
self.readLine(line)
self.endPara()
return self._results
def readLine(self, line):
#this is the inner loop
self._lineNo = self._lineNo + 1
stripped = string.lstrip(line)
if len(stripped) == 0:
if self._mode == PLAIN:
self.endPara()
else: #preformatted, append it
self._buf.append(line)
elif line[0]=='.':
# we have a command of some kind
self.endPara()
words = string.split(stripped[1:])
cmd, args = words[0], words[1:]
#is it a parser method?
if hasattr(self.__class__, cmd):
method = eval('self.'+cmd)
#this was very bad; any type error in the method was hidden
#we have to hack the traceback
try:
apply(method, tuple(args))
except TypeError, err:
sys.stderr.write("Parser method: apply(%s,%s) %s at line %d\n" % (cmd, tuple(args), err, self._lineNo))
raise
else:
# assume it is a paragraph style -
# becomes the formatter's problem
self.endPara() #end the last one
words = string.split(stripped, ' ', 1)
assert len(words)==2, "Style %s but no data at line %d" % (words[0], self._lineNo)
(styletag, data) = words
self._style = styletag[1:]
self._buf.append(data)
else:
#we have data, add to para
self._buf.append(line)
def endPara(self):
#ends the current paragraph, or preformatted block
text = string.join(self._buf, ' ')
if text:
if self._mode == PREFORMATTED:
#item 3 is list of lines
self._results.append(('PREFORMATTED', self._style,
string.join(self._buf,'\n')))
else:
self._results.append(('PARAGRAPH', self._style, text))
self._buf = []
self._style = 'Normal'
def beginPre(self, stylename):
self._mode = PREFORMATTED
self._style = stylename
def endPre(self):
self.endPara()
self._mode = PLAIN
def image(self, filename):
self.endPara()
self._results.append(('IMAGE', filename))
class Parser(BaseParser):
"""This adds a basic set of "story" components compatible with HTML & PDF.
Images, spaces"""
def vSpace(self, points):
"""Inserts a vertical spacer"""
self._results.append(('VSpace', points))
def pageBreak(self):
"""Inserts a frame break"""
self._results.append(('PageBreak','blah')) # must be a tuple
def custom(self, moduleName, funcName):
"""Goes and gets the Python object and adds it to the story"""
self.endPara()
self._results.append(('Custom',moduleName, funcName))
def nextPageTemplate(self, templateName):
self._results.append(('NextPageTemplate',templateName))
def parseFile(filename):
p = Parser()
return p.parseFile(filename)
def parseText(textBlock):
p = Parser()
return p.parseText(textBlock)
if __name__=='__main__': #NORUNTESTS
if len(sys.argv) <> 2:
results = parseText(__doc__)
else:
results = parseFile(sys.argv[1])
import pprint
pprint.pprint(results)