Removed reportlab -> 2.0

Better report on timesheets

bzr revid: pinky-f7e72504718a6a358300c76ac783f3eb37406dbd
This commit is contained in:
pinky 2007-01-07 21:24:58 +00:00
parent 44d01beae7
commit 23387d6a5c
157 changed files with 2 additions and 55856 deletions

View File

@ -1244,6 +1244,7 @@ class orm(object):
'relate': resrelate
}
print result
return result
# TODO: ameliorer avec NULL

View File

@ -37,7 +37,7 @@ import os
# Change this to UTF-8 if you plan tu use Reportlab's UTF-8 support
#
# reportlab use "code page 1252" encoding by default. cfr reportlab user guide p.46
encoding = 'cp1252'
encoding = 'utf-8'
def str2xml(s):
return s.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')

View File

@ -1,23 +0,0 @@
#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/__init__.py
__version__=''' $Id$ '''
__doc__="""The Reportlab PDF generation library."""
Version = "1.20"
def getStory(context):
if context.target == 'UserGuide':
# parse some local file
import os
myDir = os.path.split(__file__)[0]
import yaml
return yaml.parseFile(myDir + os.sep + 'mydocs.yaml')
else:
# this signals that it should revert to default processing
return None
def getMonitor():
import reportlab.monitor
mon = reportlab.monitor.ReportLabToolkitMonitor()
return mon

View File

@ -1,25 +0,0 @@
This directory is intended to act as a placeholder for extending ReportLab
with extra extensions. It has been packagised with an empty __init__.py.
So a typical extender should add his package extension as
reportlab/extensions/great_extension/__init__.py
/dingo.py
/etc etc
and single modules as
reportlab/extensions/my_module.py
Then client code can do
from reportlab.extensions.great_extension import dingo
if you extend with just a single module it might be simpler to add that
into extensions so that you could do
from reportlab.extensions import my_module.
ReportLab can take no responsibility for name clashes and problems caused by
modules and packages in reportlab/extensions.

View File

@ -1,7 +0,0 @@
#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/extensions/__init__.py
__version__=''' $Id$ '''
__doc__="""
"""
# No usable content. Do not add to this file!

View File

@ -1,8 +0,0 @@
This directory is a convenient place to put
fonts where Reportlab will find them. If running
in a server environment, this is a good place to
put Type 1 fonts needed by your application. If
in a desktop environment, you might prefer to add
your font directories to the T1SearchPath in
reportlab/rl_config.py instead.

View File

@ -1,471 +0,0 @@
StartFontMetrics 2.0
Comment Generated by pfaedit
Comment Creation Date: Thu Jun 26 18:26:51 2003
FontName Wargames
FullName Wargames
FamilyName Wargames
Weight Regular
Notice (Copyright (c) Dustin Norlander, 2002.
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or any later version.)
ItalicAngle 0
IsFixedPitch false
UnderlinePosition -133
UnderlineThickness 20
Version Version 1.0; 2001; initial release
EncodingScheme ISO10646-1
FontBBox 0 -280 860 800
CapHeight 799
XHeight 521
Ascender 799
Descender -279
StartCharMetrics 87
C 0 ; WX 500 ; N .notdef ; B 62 0 438 800 ;
C 32 ; WX 500 ; N space ; B 0 0 0 0 ;
C 33 ; WX 208 ; N exclam ; B 40 0 145 800 ;
C 34 ; WX 390 ; N quotedbl ; B 60 556 338 800 ;
C 39 ; WX 229 ; N quotesingle ; B 49 556 154 800 ;
C 40 ; WX 365 ; N parenleft ; B 40 0 284 800 ;
C 41 ; WX 361 ; N parenright ; B 49 0 293 800 ;
C 43 ; WX 490 ; N plus ; B 29 277 414 662 ;
C 44 ; WX 329 ; N comma ; B 0 -140 244 244 ;
C 45 ; WX 484 ; N hyphen ; B 29 277 414 383 ;
C 46 ; WX 338 ; N period ; B 34 -4 277 239 ;
C 47 ; WX 507 ; N slash ; B 49 0 434 800 ;
C 48 ; WX 604 ; N zero ; B 0 0 522 800 ;
C 49 ; WX 330 ; N one ; B 0 0 244 800 ;
C 50 ; WX 600 ; N two ; B 0 0 522 800 ;
C 51 ; WX 597 ; N three ; B 0 0 522 800 ;
C 52 ; WX 597 ; N four ; B 0 0 522 800 ;
C 53 ; WX 594 ; N five ; B 0 0 522 800 ;
C 54 ; WX 600 ; N six ; B 0 0 522 800 ;
C 55 ; WX 594 ; N seven ; B 0 0 522 800 ;
C 56 ; WX 597 ; N eight ; B 0 0 522 800 ;
C 57 ; WX 603 ; N nine ; B 0 0 522 800 ;
C 58 ; WX 233 ; N colon ; B 60 167 165 551 ;
C 59 ; WX 228 ; N semicolon ; B 40 -14 146 512 ;
C 61 ; WX 547 ; N equal ; B 80 277 463 575 ;
C 63 ; WX 501 ; N question ; B 40 0 423 730 ;
C 64 ; WX 945 ; N at ; B 60 0 860 662 ;
C 65 ; WX 734 ; N A ; B 0 0 662 800 ;
C 66 ; WX 730 ; N B ; B 0 0 662 800 ;
C 67 ; WX 705 ; N C ; B 0 0 662 800 ;
C 68 ; WX 732 ; N D ; B 0 0 662 800 ;
C 69 ; WX 576 ; N E ; B 0 0 522 800 ;
C 70 ; WX 568 ; N F ; B 0 0 522 800 ;
C 71 ; WX 716 ; N G ; B 0 0 662 800 ;
C 72 ; WX 734 ; N H ; B 0 0 662 800 ;
C 73 ; WX 326 ; N I ; B 0 0 244 800 ;
C 74 ; WX 598 ; N J ; B 0 0 522 800 ;
C 75 ; WX 733 ; N K ; B 0 0 662 800 ;
C 76 ; WX 692 ; N L ; B 0 0 662 800 ;
C 77 ; WX 872 ; N M ; B 0 0 800 800 ;
C 78 ; WX 734 ; N N ; B 0 0 662 800 ;
C 79 ; WX 732 ; N O ; B 0 0 662 800 ;
C 80 ; WX 701 ; N P ; B 0 0 662 800 ;
C 81 ; WX 734 ; N Q ; B 0 0 662 800 ;
C 82 ; WX 726 ; N R ; B 0 0 662 800 ;
C 83 ; WX 591 ; N S ; B 0 0 522 800 ;
C 84 ; WX 632 ; N T ; B 0 0 662 800 ;
C 85 ; WX 733 ; N U ; B 0 0 662 800 ;
C 86 ; WX 725 ; N V ; B 0 0 662 800 ;
C 87 ; WX 872 ; N W ; B 0 0 800 800 ;
C 88 ; WX 732 ; N X ; B 0 0 662 800 ;
C 89 ; WX 617 ; N Y ; B 0 0 662 800 ;
C 90 ; WX 575 ; N Z ; B 0 0 522 800 ;
C 91 ; WX 309 ; N bracketleft ; B 0 0 244 800 ;
C 92 ; WX 522 ; N backslash ; B 49 0 434 800 ;
C 93 ; WX 373 ; N bracketright ; B 49 0 293 800 ;
C 97 ; WX 605 ; N a ; B 0 0 522 522 ;
C 98 ; WX 589 ; N b ; B 0 0 522 800 ;
C 99 ; WX 588 ; N c ; B 0 0 522 522 ;
C 100 ; WX 599 ; N d ; B 0 0 522 800 ;
C 101 ; WX 590 ; N e ; B 0 0 522 522 ;
C 102 ; WX 445 ; N f ; B 0 0 383 800 ;
C 103 ; WX 599 ; N g ; B 0 -280 522 521 ;
C 104 ; WX 590 ; N h ; B 0 0 522 800 ;
C 105 ; WX 327 ; N i ; B 0 0 244 713 ;
C 106 ; WX 326 ; N j ; B 0 -280 244 521 ;
C 107 ; WX 590 ; N k ; B 0 0 522 800 ;
C 108 ; WX 326 ; N l ; B 0 0 244 800 ;
C 109 ; WX 743 ; N m ; B 0 0 662 522 ;
C 110 ; WX 604 ; N n ; B 0 0 522 522 ;
C 111 ; WX 602 ; N o ; B 0 0 522 522 ;
C 112 ; WX 594 ; N p ; B 0 -280 522 521 ;
C 113 ; WX 617 ; N q ; B 0 -280 662 521 ;
C 114 ; WX 575 ; N r ; B 0 0 522 522 ;
C 115 ; WX 458 ; N s ; B 0 0 383 522 ;
C 116 ; WX 448 ; N t ; B 0 0 383 662 ;
C 117 ; WX 605 ; N u ; B 0 0 522 522 ;
C 118 ; WX 596 ; N v ; B 0 0 522 522 ;
C 119 ; WX 743 ; N w ; B 0 0 662 522 ;
C 120 ; WX 588 ; N x ; B 0 0 522 522 ;
C 121 ; WX 599 ; N y ; B 0 -280 522 521 ;
C 122 ; WX 584 ; N z ; B 0 0 522 522 ;
C 124 ; WX 230 ; N bar ; B 49 139 154 662 ;
C 160 ; WX 500 ; N uni00A0 ; B 0 0 0 0 ;
C -1 ; WX 484 ; N uni2010 ; B 29 277 414 383 ;
C -1 ; WX 0 ; N .null ; B 0 0 0 0 ;
C -1 ; WX 500 ; N nonmarkingreturn ; B 0 0 0 0 ;
EndCharMetrics
StartKernData
StartKernPairs 357
KPX A Y -104
KPX A U 25
KPX A T -104
KPX A Q 23
KPX A O 23
KPX A I 24
KPX A G 23
KPX A C 23
KPX B Z 21
KPX B U 28
KPX B Q 27
KPX B O 27
KPX B N 20
KPX B L 20
KPX B K 20
KPX B I 27
KPX B H 20
KPX B G 27
KPX B F 21
KPX B E 21
KPX B C 27
KPX C Z 21
KPX C U 28
KPX C Q 27
KPX C O 27
KPX C N 20
KPX C L 20
KPX C K 20
KPX C I 27
KPX C H 20
KPX C G 27
KPX C F 21
KPX C E 21
KPX C C 27
KPX D Z 21
KPX D U 28
KPX D Q 27
KPX D O 27
KPX D N 20
KPX D L 20
KPX D K 20
KPX D I 27
KPX D H 20
KPX D G 27
KPX D F 21
KPX D E 21
KPX D C 27
KPX E Z 21
KPX E U 28
KPX E Q 27
KPX E O 27
KPX E N 20
KPX E L 20
KPX E K 20
KPX E I 27
KPX E H 20
KPX E G 27
KPX E F 21
KPX E E 21
KPX E C 27
KPX F Z 21
KPX F U 28
KPX F Q 27
KPX F O 27
KPX F N 20
KPX F L 20
KPX F K 20
KPX F J -234
KPX F I 27
KPX F H 20
KPX F G 27
KPX F F 21
KPX F E 21
KPX F C 27
KPX G Z 21
KPX G U 28
KPX G Q 27
KPX G O 27
KPX G N 20
KPX G L 20
KPX G K 20
KPX G I 27
KPX G H 20
KPX G G 27
KPX G F 21
KPX G E 21
KPX G C 27
KPX H Z 21
KPX H U 28
KPX H Q 27
KPX H O 27
KPX H N 20
KPX H L 20
KPX H K 20
KPX H I 27
KPX H H 20
KPX H G 27
KPX H F 21
KPX H E 21
KPX H C 27
KPX I Z 21
KPX I U 28
KPX I Q 27
KPX I O 27
KPX I N 20
KPX I L 20
KPX I K 20
KPX I I 27
KPX I H 20
KPX I G 27
KPX I F 21
KPX I E 21
KPX I C 27
KPX J Z 21
KPX J U 28
KPX J Q 27
KPX J O 27
KPX J N 20
KPX J L 20
KPX J K 20
KPX J I 27
KPX J H 20
KPX J G 27
KPX J F 21
KPX J E 21
KPX J C 27
KPX K Z 21
KPX K U 28
KPX K Q 27
KPX K O 27
KPX K N 20
KPX K L 20
KPX K K 20
KPX K I 27
KPX K H 20
KPX K G 27
KPX K F 21
KPX K E 21
KPX K C 27
KPX L Y -104
KPX L V -95
KPX L U 25
KPX L T -104
KPX L Q 23
KPX L O 23
KPX L I 24
KPX L G 23
KPX L C 23
KPX M Z 21
KPX M U 28
KPX M Q 27
KPX M O 27
KPX M N 20
KPX M L 20
KPX M K 20
KPX M I 27
KPX M H 20
KPX M G 27
KPX M F 21
KPX M E 21
KPX M C 27
KPX N Z 21
KPX N U 28
KPX N Q 27
KPX N O 27
KPX N N 20
KPX N L 20
KPX N K 20
KPX N I 27
KPX N H 20
KPX N G 27
KPX N F 21
KPX N E 21
KPX N C 27
KPX O Z 21
KPX O U 28
KPX O Q 27
KPX O O 27
KPX O N 20
KPX O L 20
KPX O K 20
KPX O I 27
KPX O H 20
KPX O G 27
KPX O F 21
KPX O E 21
KPX O C 27
KPX P Z 75
KPX P Y 63
KPX P X 71
KPX P V 73
KPX P U 82
KPX P T 63
KPX P S 71
KPX P Q 81
KPX P O 81
KPX P J -252
KPX P G 81
KPX P C 81
KPX Q Z 21
KPX Q U 28
KPX Q Q 27
KPX Q O 27
KPX Q N 20
KPX Q L 20
KPX Q K 20
KPX Q I 27
KPX Q H 20
KPX Q G 27
KPX Q F 21
KPX Q E 21
KPX Q C 27
KPX R Z 21
KPX R U 28
KPX R Q 27
KPX R O 27
KPX R N 20
KPX R L 20
KPX R K 20
KPX R J -83
KPX R I 27
KPX R H 20
KPX R G 27
KPX R F 21
KPX R E 21
KPX R C 27
KPX S Z 21
KPX S U 28
KPX S Q 27
KPX S O 27
KPX S N 20
KPX S L 20
KPX S K 20
KPX S I 27
KPX S H 20
KPX S G 27
KPX S F 21
KPX S E 21
KPX S C 27
KPX T Z 75
KPX T Y 64
KPX T X 73
KPX T W -40
KPX T V 73
KPX T U 83
KPX T T 64
KPX T S 73
KPX T R -39
KPX T Q 81
KPX T P -39
KPX T O 81
KPX T N -37
KPX T M -40
KPX T L -37
KPX T K -37
KPX T J -181
KPX T I -28
KPX T H -37
KPX T G 81
KPX T F -36
KPX T E -36
KPX T D -39
KPX T C 81
KPX T B -39
KPX T A -39
KPX U Z 21
KPX U U 28
KPX U Q 27
KPX U O 27
KPX U N 20
KPX U L 20
KPX U K 20
KPX U I 27
KPX U H 20
KPX U G 27
KPX U F 21
KPX U E 21
KPX U C 27
KPX V Z 21
KPX V U 28
KPX V Q 27
KPX V O 27
KPX V N 20
KPX V L 20
KPX V K 20
KPX V J -124
KPX V I 27
KPX V H 20
KPX V G 27
KPX V F 21
KPX V E 21
KPX V C 27
KPX W Z 21
KPX W U 28
KPX W Q 27
KPX W O 27
KPX W N 20
KPX W L 20
KPX W K 20
KPX W I 27
KPX W H 20
KPX W G 27
KPX W F 21
KPX W E 21
KPX W C 27
KPX X Z 21
KPX X U 28
KPX X Q 27
KPX X O 27
KPX X N 20
KPX X L 20
KPX X K 20
KPX X I 27
KPX X H 20
KPX X G 27
KPX X F 21
KPX X E 21
KPX X C 27
KPX Y Z 80
KPX Y Y 68
KPX Y X 77
KPX Y W -36
KPX Y V 78
KPX Y U 86
KPX Y T 68
KPX Y S 77
KPX Y R -34
KPX Y Q 85
KPX Y P -34
KPX Y O 85
KPX Y N -33
KPX Y M -36
KPX Y L -33
KPX Y K -33
KPX Y J -176
KPX Y I -24
KPX Y H -33
KPX Y G 85
KPX Y F -32
KPX Y E -32
KPX Y D -34
KPX Y C 85
KPX Y B -34
KPX Y A -34
KPX Z Z 21
KPX Z U 28
KPX Z Q 27
KPX Z O 27
KPX Z N 20
KPX Z L 20
KPX Z K 20
KPX Z I 27
KPX Z H 20
KPX Z G 27
KPX Z F 21
KPX Z E 21
KPX Z C 27
EndKernPairs
EndKernData
EndFontMetrics

Binary file not shown.

View File

@ -1,4 +0,0 @@
#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/graphics/__init__.py
__version__=''' $Id$ '''

View File

@ -1,4 +0,0 @@
#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/graphics/charts/__init__.py
__version__=''' $Id$ '''

View File

@ -1,92 +0,0 @@
#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/graphics/charts/areas.py
"""This module defines a Area mixin classes
"""
__version__=''' $Id$ '''
from reportlab.lib.validators import isNumber, isColor, isColorOrNone, isNoneOrShape
from reportlab.graphics.widgetbase import Widget
from reportlab.graphics.shapes import Rect, Group, Line, Polygon
from reportlab.lib.attrmap import AttrMap, AttrMapValue
class PlotArea(Widget):
"Abstract base class representing a chart's plot area, pretty unusable by itself."
_attrMap = AttrMap(
x = AttrMapValue(isNumber, desc='X position of the lower-left corner of the chart.'),
y = AttrMapValue(isNumber, desc='Y position of the lower-left corner of the chart.'),
width = AttrMapValue(isNumber, desc='Width of the chart.'),
height = AttrMapValue(isNumber, desc='Height of the chart.'),
strokeColor = AttrMapValue(isColorOrNone, desc='Color of the plot area border.'),
strokeWidth = AttrMapValue(isNumber, desc='Width plot area border.'),
fillColor = AttrMapValue(isColorOrNone, desc='Color of the plot area interior.'),
background = AttrMapValue(isNoneOrShape, desc='Handle to background object.'),
debug = AttrMapValue(isNumber, desc='Used only for debugging.'),
)
def __init__(self):
self.x = 20
self.y = 10
self.height = 85
self.width = 180
self.strokeColor = None
self.strokeWidth = 1
self.fillColor = None
self.background = None
self.debug = 0
def makeBackground(self):
if self.background is not None:
BG = self.background
if isinstance(BG,Group):
g = BG
for bg in g.contents:
bg.x = self.x
bg.y = self.y
bg.width = self.width
bg.height = self.height
else:
g = Group()
if type(BG) not in (type(()),type([])): BG=(BG,)
for bg in BG:
bg.x = self.x
bg.y = self.y
bg.width = self.width
bg.height = self.height
g.add(bg)
return g
else:
strokeColor,strokeWidth,fillColor=self.strokeColor, self.strokeWidth, self.fillColor
if (strokeWidth and strokeColor) or fillColor:
g = Group()
_3d_dy = getattr(self,'_3d_dy',None)
x = self.x
y = self.y
h = self.height
w = self.width
if _3d_dy is not None:
_3d_dx = self._3d_dx
if fillColor and not strokeColor:
from reportlab.lib.colors import Blacker
c = Blacker(fillColor, getattr(self,'_3d_blacken',0.7))
else:
c = strokeColor
if not strokeWidth: strokeWidth = 0.5
if fillColor or strokeColor or c:
bg = Polygon([x,y,x,y+h,x+_3d_dx,y+h+_3d_dy,x+w+_3d_dx,y+h+_3d_dy,x+w+_3d_dx,y+_3d_dy,x+w,y],
strokeColor=strokeColor or c or grey, strokeWidth=strokeWidth, fillColor=fillColor)
g.add(bg)
g.add(Line(x,y,x+_3d_dx,y+_3d_dy, strokeWidth=0.5, strokeColor=c))
g.add(Line(x+_3d_dx,y+_3d_dy, x+_3d_dx,y+h+_3d_dy,strokeWidth=0.5, strokeColor=c))
fc = Blacker(c, getattr(self,'_3d_blacken',0.8))
g.add(Polygon([x,y,x+_3d_dx,y+_3d_dy,x+w+_3d_dx,y+_3d_dy,x+w,y],
strokeColor=strokeColor or c or grey, strokeWidth=strokeWidth, fillColor=fc))
bg = Line(x+_3d_dx,y+_3d_dy, x+w+_3d_dx,y+_3d_dy,strokeWidth=0.5, strokeColor=c)
else:
bg = None
else:
bg = Rect(x, y, w, h,
strokeColor=strokeColor, strokeWidth=strokeWidth, fillColor=fillColor)
if bg: g.add(bg)
return g
else:
return None

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,165 +0,0 @@
from reportlab.lib.colors import blue, _PCMYK_black
from reportlab.graphics.charts.textlabels import Label
from reportlab.graphics.shapes import Circle, Drawing, Group, Line, Rect, String
from reportlab.graphics.widgetbase import Widget
from reportlab.lib.attrmap import *
from reportlab.lib.validators import *
from reportlab.lib.units import cm
from reportlab.pdfbase.pdfmetrics import getFont
from reportlab.graphics.charts.lineplots import _maxWidth
class DotBox(Widget):
"""Returns a dotbox widget."""
#Doesn't use TypedPropertyCollection for labels - this can be a later improvement
_attrMap = AttrMap(
xlabels = AttrMapValue(isNoneOrListOfNoneOrStrings,
desc="List of text labels for boxes on left hand side"),
ylabels = AttrMapValue(isNoneOrListOfNoneOrStrings,
desc="Text label for second box on left hand side"),
labelFontName = AttrMapValue(isString,
desc="Name of font used for the labels"),
labelFontSize = AttrMapValue(isNumber,
desc="Size of font used for the labels"),
labelOffset = AttrMapValue(isNumber,
desc="Space between label text and grid edge"),
strokeWidth = AttrMapValue(isNumber,
desc='Width of the grid and dot outline'),
gridDivWidth = AttrMapValue(isNumber,
desc="Width of each 'box'"),
gridColor = AttrMapValue(isColor,
desc='Colour for the box and gridding'),
dotDiameter = AttrMapValue(isNumber,
desc="Diameter of the circle used for the 'dot'"),
dotColor = AttrMapValue(isColor,
desc='Colour of the circle on the box'),
dotXPosition = AttrMapValue(isNumber,
desc='X Position of the circle'),
dotYPosition = AttrMapValue(isNumber,
desc='X Position of the circle'),
x = AttrMapValue(isNumber,
desc='X Position of dotbox'),
y = AttrMapValue(isNumber,
desc='Y Position of dotbox'),
)
def __init__(self):
self.xlabels=["Value", "Blend", "Growth"]
self.ylabels=["Small", "Medium", "Large"]
self.labelFontName = "Helvetica"
self.labelFontSize = 6
self.labelOffset = 5
self.strokeWidth = 0.5
self.gridDivWidth=0.5*cm
self.gridColor=colors.Color(25/255.0,77/255.0,135/255.0)
self.dotDiameter=0.4*cm
self.dotColor=colors.Color(232/255.0,224/255.0,119/255.0)
self.dotXPosition = 1
self.dotYPosition = 1
self.x = 30
self.y = 5
def _getDrawingDimensions(self):
leftPadding=rightPadding=topPadding=bottomPadding=5
#find width of grid
tx=len(self.xlabels)*self.gridDivWidth
#add padding (and offset)
tx=tx+leftPadding+rightPadding+self.labelOffset
#add in maximum width of text
tx=tx+_maxWidth(self.xlabels, self.labelFontName, self.labelFontSize)
#find height of grid
ty=len(self.ylabels)*self.gridDivWidth
#add padding (and offset)
ty=ty+topPadding+bottomPadding+self.labelOffset
#add in maximum width of text
ty=ty+_maxWidth(self.ylabels, self.labelFontName, self.labelFontSize)
#print (tx, ty)
return (tx,ty)
def demo(self,drawing=None):
if not drawing:
tx,ty=self._getDrawingDimensions()
drawing = Drawing(tx,ty)
drawing.add(self.draw())
return drawing
def draw(self):
g = Group()
#box
g.add(Rect(self.x,self.y,len(self.xlabels)*self.gridDivWidth,len(self.ylabels)*self.gridDivWidth,
strokeColor=self.gridColor,
strokeWidth=self.strokeWidth,
fillColor=None))
#internal gridding
for f in range (1,len(self.ylabels)):
#horizontal
g.add(Line(strokeColor=self.gridColor,
strokeWidth=self.strokeWidth,
x1 = self.x,
y1 = self.y+f*self.gridDivWidth,
x2 = self.x+len(self.xlabels)*self.gridDivWidth,
y2 = self.y+f*self.gridDivWidth))
for f in range (1,len(self.xlabels)):
#vertical
g.add(Line(strokeColor=self.gridColor,
strokeWidth=self.strokeWidth,
x1 = self.x+f*self.gridDivWidth,
y1 = self.y,
x2 = self.x+f*self.gridDivWidth,
y2 = self.y+len(self.ylabels)*self.gridDivWidth))
# draw the 'dot'
g.add(Circle(strokeColor=self.gridColor,
strokeWidth=self.strokeWidth,
fillColor=self.dotColor,
cx = self.x+(self.dotXPosition*self.gridDivWidth),
cy = self.y+(self.dotYPosition*self.gridDivWidth),
r = self.dotDiameter/2.0))
#used for centering y-labels (below)
ascent=getFont(self.labelFontName).face.ascent
if ascent==0:
ascent=0.718 # default (from helvetica)
ascent=ascent*self.labelFontSize # normalize
#do y-labels
if self.ylabels != None:
for f in range (len(self.ylabels)-1,-1,-1):
if self.ylabels[f]!= None:
g.add(String(strokeColor=self.gridColor,
text = self.ylabels[f],
fontName = self.labelFontName,
fontSize = self.labelFontSize,
fillColor=_PCMYK_black,
x = self.x-self.labelOffset,
y = self.y+(f*self.gridDivWidth+(self.gridDivWidth-ascent)/2.0),
textAnchor = 'end'))
#do x-labels
if self.xlabels != None:
for f in range (0,len(self.xlabels)):
if self.xlabels[f]!= None:
l=Label()
l.x=self.x+(f*self.gridDivWidth)+(self.gridDivWidth+ascent)/2.0
l.y=self.y+(len(self.ylabels)*self.gridDivWidth)+self.labelOffset
l.angle=90
l.textAnchor='start'
l.fontName = self.labelFontName
l.fontSize = self.labelFontSize
l.fillColor = _PCMYK_black
l.setText(self.xlabels[f])
l.boxAnchor = 'sw'
l.draw()
g.add(l)
return g
if __name__ == "__main__":
d = DotBox()
d.demo().save(fnRoot="dotbox")

View File

@ -1,372 +0,0 @@
#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/graphics/charts/doughnut.py
# doughnut chart
"""Doughnut chart
Produces a circular chart like the doughnut charts produced by Excel.
Can handle multiple series (which produce concentric 'rings' in the chart).
"""
__version__=''' $Id$ '''
import copy
from math import sin, cos, pi
from types import ListType, TupleType
from reportlab.lib import colors
from reportlab.lib.validators import isColor, isNumber, isListOfNumbersOrNone,\
isListOfNumbers, isColorOrNone, isString,\
isListOfStringsOrNone, OneOf, SequenceOf,\
isBoolean, isListOfColors,\
isNoneOrListOfNoneOrStrings,\
isNoneOrListOfNoneOrNumbers,\
isNumberOrNone
from reportlab.lib.attrmap import *
from reportlab.pdfgen.canvas import Canvas
from reportlab.graphics.shapes import Group, Drawing, Line, Rect, Polygon, Ellipse, \
Wedge, String, SolidShape, UserNode, STATE_DEFAULTS
from reportlab.graphics.widgetbase import Widget, TypedPropertyCollection, PropHolder
from reportlab.graphics.charts.areas import PlotArea
from reportlab.graphics.charts.textlabels import Label
from reportlab.graphics.widgets.markers import Marker
class SectorProperties(PropHolder):
"""This holds descriptive information about the sectors in a doughnut chart.
It is not to be confused with the 'sector itself'; this just holds
a recipe for how to format one, and does not allow you to hack the
angles. It can format a genuine Sector object for you with its
format method.
"""
_attrMap = AttrMap(
strokeWidth = AttrMapValue(isNumber),
fillColor = AttrMapValue(isColorOrNone),
strokeColor = AttrMapValue(isColorOrNone),
strokeDashArray = AttrMapValue(isListOfNumbersOrNone),
popout = AttrMapValue(isNumber),
fontName = AttrMapValue(isString),
fontSize = AttrMapValue(isNumber),
fontColor = AttrMapValue(isColorOrNone),
labelRadius = AttrMapValue(isNumber),
)
def __init__(self):
self.strokeWidth = 0
self.fillColor = None
self.strokeColor = STATE_DEFAULTS["strokeColor"]
self.strokeDashArray = STATE_DEFAULTS["strokeDashArray"]
self.popout = 0
self.fontName = STATE_DEFAULTS["fontName"]
self.fontSize = STATE_DEFAULTS["fontSize"]
self.fontColor = STATE_DEFAULTS["fillColor"]
self.labelRadius = 1.2
class Doughnut(Widget):
_attrMap = AttrMap(
x = AttrMapValue(isNumber, desc='X position of the chart within its container.'),
y = AttrMapValue(isNumber, desc='Y position of the chart within its container.'),
width = AttrMapValue(isNumber, desc='width of doughnut bounding box. Need not be same as width.'),
height = AttrMapValue(isNumber, desc='height of doughnut bounding box. Need not be same as height.'),
data = AttrMapValue(None, desc='list of numbers defining sector sizes; need not sum to 1'),
labels = AttrMapValue(isListOfStringsOrNone, desc="optional list of labels to use for each data point"),
startAngle = AttrMapValue(isNumber, desc="angle of first slice; like the compass, 0 is due North"),
direction = AttrMapValue(OneOf('clockwise', 'anticlockwise'), desc="'clockwise' or 'anticlockwise'"),
slices = AttrMapValue(None, desc="collection of sector descriptor objects"),
)
def __init__(self):
self.x = 0
self.y = 0
self.width = 100
self.height = 100
self.data = [1,1]
self.labels = None # or list of strings
self.startAngle = 90
self.direction = "clockwise"
self.slices = TypedPropertyCollection(SectorProperties)
self.slices[0].fillColor = colors.darkcyan
self.slices[1].fillColor = colors.blueviolet
self.slices[2].fillColor = colors.blue
self.slices[3].fillColor = colors.cyan
def demo(self):
d = Drawing(200, 100)
dn = Doughnut()
dn.x = 50
dn.y = 10
dn.width = 100
dn.height = 80
dn.data = [10,20,30,40,50,60]
dn.labels = ['a','b','c','d','e','f']
dn.slices.strokeWidth=0.5
dn.slices[3].popout = 10
dn.slices[3].strokeWidth = 2
dn.slices[3].strokeDashArray = [2,2]
dn.slices[3].labelRadius = 1.75
dn.slices[3].fontColor = colors.red
dn.slices[0].fillColor = colors.darkcyan
dn.slices[1].fillColor = colors.blueviolet
dn.slices[2].fillColor = colors.blue
dn.slices[3].fillColor = colors.cyan
dn.slices[4].fillColor = colors.aquamarine
dn.slices[5].fillColor = colors.cadetblue
dn.slices[6].fillColor = colors.lightcoral
d.add(dn)
return d
def normalizeData(self, data=None):
from operator import add
sum = float(reduce(add,data,0))
return abs(sum)>=1e-8 and map(lambda x,f=360./sum: f*x, data) or len(data)*[0]
def makeSectors(self):
# normalize slice data
if type(self.data) in (ListType, TupleType) and type(self.data[0]) in (ListType, TupleType):
#it's a nested list, more than one sequence
normData = []
n = []
for l in self.data:
t = self.normalizeData(l)
normData.append(t)
n.append(len(t))
else:
normData = self.normalizeData(self.data)
n = len(normData)
#labels
if self.labels is None:
labels = []
if type(n) not in (ListType,TupleType):
labels = [''] * n
else:
for m in n:
labels = list(labels) + [''] * m
else:
labels = self.labels
#there's no point in raising errors for less than enough errors if
#we silently create all for the extreme case of no labels.
if type(n) not in (ListType,TupleType):
i = n-len(labels)
if i>0:
labels = list(labels) + [''] * i
else:
tlab = 0
for m in n:
tlab = tlab+m
i = tlab-len(labels)
if i>0:
labels = list(labels) + [''] * i
xradius = self.width/2.0
yradius = self.height/2.0
centerx = self.x + xradius
centery = self.y + yradius
if self.direction == "anticlockwise":
whichWay = 1
else:
whichWay = -1
g = Group()
i = 0
sn = 0
startAngle = self.startAngle #% 360
if type(self.data[0]) in (ListType, TupleType):
#multi-series doughnut
styleCount = len(self.slices)
iradius = (self.height/5.0)/len(self.data)
for series in normData:
for angle in series:
endAngle = (startAngle + (angle * whichWay)) #% 360
if abs(startAngle-endAngle)>=1e-5:
if startAngle < endAngle:
a1 = startAngle
a2 = endAngle
else:
a1 = endAngle
a2 = startAngle
#if we didn't use %stylecount here we'd end up with the later sectors
#all having the default style
sectorStyle = self.slices[i%styleCount]
# is it a popout?
cx, cy = centerx, centery
if sectorStyle.popout != 0:
# pop out the sector
averageAngle = (a1+a2)/2.0
aveAngleRadians = averageAngle * pi/180.0
popdistance = sectorStyle.popout
cx = centerx + popdistance * cos(aveAngleRadians)
cy = centery + popdistance * sin(aveAngleRadians)
if type(n) in (ListType,TupleType):
theSector = Wedge(cx, cy, xradius+(sn*iradius)-iradius, a1, a2, yradius=yradius+(sn*iradius)-iradius, radius1=yradius+(sn*iradius)-(2*iradius))
else:
theSector = Wedge(cx, cy, xradius, a1, a2, yradius=yradius, radius1=iradius)
theSector.fillColor = sectorStyle.fillColor
theSector.strokeColor = sectorStyle.strokeColor
theSector.strokeWidth = sectorStyle.strokeWidth
theSector.strokeDashArray = sectorStyle.strokeDashArray
g.add(theSector)
startAngle = endAngle
if labels[i] != "":
averageAngle = (a1+a2)/2.0
aveAngleRadians = averageAngle*pi/180.0
labelRadius = sectorStyle.labelRadius
labelX = centerx + (0.5 * self.width * cos(aveAngleRadians) * labelRadius)
labelY = centery + (0.5 * self.height * sin(aveAngleRadians) * labelRadius)
theLabel = String(labelX, labelY, labels[i])
theLabel.textAnchor = "middle"
theLabel.fontSize = sectorStyle.fontSize
theLabel.fontName = sectorStyle.fontName
theLabel.fillColor = sectorStyle.fontColor
g.add(theLabel)
i = i + 1
sn = sn + 1
else:
#single series doughnut
styleCount = len(self.slices)
iradius = self.height/5.0
for angle in normData:
endAngle = (startAngle + (angle * whichWay)) #% 360
if abs(startAngle-endAngle)>=1e-5:
if startAngle < endAngle:
a1 = startAngle
a2 = endAngle
else:
a1 = endAngle
a2 = startAngle
#if we didn't use %stylecount here we'd end up with the later sectors
#all having the default style
sectorStyle = self.slices[i%styleCount]
# is it a popout?
cx, cy = centerx, centery
if sectorStyle.popout != 0:
# pop out the sector
averageAngle = (a1+a2)/2.0
aveAngleRadians = averageAngle * pi/180.0
popdistance = sectorStyle.popout
cx = centerx + popdistance * cos(aveAngleRadians)
cy = centery + popdistance * sin(aveAngleRadians)
if n > 1:
theSector = Wedge(cx, cy, xradius, a1, a2, yradius=yradius, radius1=iradius)
elif n==1:
theSector = Wedge(cx, cy, xradius, a1, a2, yradius=yradius, iradius=iradius)
theSector.fillColor = sectorStyle.fillColor
theSector.strokeColor = sectorStyle.strokeColor
theSector.strokeWidth = sectorStyle.strokeWidth
theSector.strokeDashArray = sectorStyle.strokeDashArray
g.add(theSector)
# now draw a label
if labels[i] != "":
averageAngle = (a1+a2)/2.0
aveAngleRadians = averageAngle*pi/180.0
labelRadius = sectorStyle.labelRadius
labelX = centerx + (0.5 * self.width * cos(aveAngleRadians) * labelRadius)
labelY = centery + (0.5 * self.height * sin(aveAngleRadians) * labelRadius)
theLabel = String(labelX, labelY, labels[i])
theLabel.textAnchor = "middle"
theLabel.fontSize = sectorStyle.fontSize
theLabel.fontName = sectorStyle.fontName
theLabel.fillColor = sectorStyle.fontColor
g.add(theLabel)
startAngle = endAngle
i = i + 1
return g
def draw(self):
g = Group()
g.add(self.makeSectors())
return g
def sample1():
"Make up something from the individual Sectors"
d = Drawing(400, 400)
g = Group()
s1 = Wedge(centerx=200, centery=200, radius=150, startangledegrees=0, endangledegrees=120, radius1=100)
s1.fillColor=colors.red
s1.strokeColor=None
d.add(s1)
s2 = Wedge(centerx=200, centery=200, radius=150, startangledegrees=120, endangledegrees=240, radius1=100)
s2.fillColor=colors.green
s2.strokeColor=None
d.add(s2)
s3 = Wedge(centerx=200, centery=200, radius=150, startangledegrees=240, endangledegrees=260, radius1=100)
s3.fillColor=colors.blue
s3.strokeColor=None
d.add(s3)
s4 = Wedge(centerx=200, centery=200, radius=150, startangledegrees=260, endangledegrees=360, radius1=100)
s4.fillColor=colors.gray
s4.strokeColor=None
d.add(s4)
return d
def sample2():
"Make a simple demo"
d = Drawing(400, 400)
dn = Doughnut()
dn.x = 50
dn.y = 50
dn.width = 300
dn.height = 300
dn.data = [10,20,30,40,50,60]
d.add(dn)
return d
def sample3():
"Make a more complex demo"
d = Drawing(400, 400)
dn = Doughnut()
dn.x = 50
dn.y = 50
dn.width = 300
dn.height = 300
dn.data = [[10,20,30,40,50,60], [10,20,30,40]]
dn.labels = ['a','b','c','d','e','f']
d.add(dn)
return d
if __name__=='__main__':
from reportlab.graphics.renderPDF import drawToFile
d = sample1()
drawToFile(d, 'doughnut1.pdf')
d = sample2()
drawToFile(d, 'doughnut2.pdf')
d = sample3()
drawToFile(d, 'doughnut3.pdf')

View File

@ -1,486 +0,0 @@
#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/graphics/charts/legends.py
"""This will be a collection of legends to be used with charts.
"""
__version__=''' $Id$ '''
import string, copy
from reportlab.lib import colors
from reportlab.lib.validators import isNumber, OneOf, isString, isColorOrNone, isNumberOrNone, isListOfNumbersOrNone
from reportlab.lib.attrmap import *
from reportlab.pdfbase.pdfmetrics import stringWidth, getFont
from reportlab.graphics.widgetbase import Widget
from reportlab.graphics.shapes import Drawing, Group, String, Rect, Line, STATE_DEFAULTS
class Legend(Widget):
"""A simple legend containing rectangular swatches and strings.
The swatches are filled rectangles whenever the respective
color object in 'colorNamePairs' is a subclass of Color in
reportlab.lib.colors. Otherwise the object passed instead is
assumed to have 'x', 'y', 'width' and 'height' attributes.
A legend then tries to set them or catches any error. This
lets you plug-in any widget you like as a replacement for
the default rectangular swatches.
Strings can be nicely aligned left or right to the swatches.
"""
_attrMap = AttrMap(
x = AttrMapValue(isNumber, desc="x-coordinate of upper-left reference point"),
y = AttrMapValue(isNumber, desc="y-coordinate of upper-left reference point"),
deltax = AttrMapValue(isNumberOrNone, desc="x-distance between neighbouring swatches"),
deltay = AttrMapValue(isNumberOrNone, desc="y-distance between neighbouring swatches"),
dxTextSpace = AttrMapValue(isNumber, desc="Distance between swatch rectangle and text"),
autoXPadding = AttrMapValue(isNumber, desc="x Padding between columns if deltax=None"),
autoYPadding = AttrMapValue(isNumber, desc="y Padding between rows if deltay=None"),
dx = AttrMapValue(isNumber, desc="Width of swatch rectangle"),
dy = AttrMapValue(isNumber, desc="Height of swatch rectangle"),
columnMaximum = AttrMapValue(isNumber, desc="Max. number of items per column"),
alignment = AttrMapValue(OneOf("left", "right"), desc="Alignment of text with respect to swatches"),
colorNamePairs = AttrMapValue(None, desc="List of color/name tuples (color can also be widget)"),
fontName = AttrMapValue(isString, desc="Font name of the strings"),
fontSize = AttrMapValue(isNumber, desc="Font size of the strings"),
fillColor = AttrMapValue(isColorOrNone, desc=""),
strokeColor = AttrMapValue(isColorOrNone, desc="Border color of the swatches"),
strokeWidth = AttrMapValue(isNumber, desc="Width of the border color of the swatches"),
callout = AttrMapValue(None, desc="a user callout(self,g,x,y,(color,text))"),
)
def __init__(self):
# Upper-left reference point.
self.x = 0
self.y = 0
# Alginment of text with respect to swatches.
self.alignment = "left"
# x- and y-distances between neighbouring swatches.
self.deltax = 75
self.deltay = 20
self.autoXPadding = 5
self.autoYPadding = 2
# Size of swatch rectangle.
self.dx = 10
self.dy = 10
# Distance between swatch rectangle and text.
self.dxTextSpace = 10
# Max. number of items per column.
self.columnMaximum = 3
# Color/name pairs.
self.colorNamePairs = [ (colors.red, "red"),
(colors.blue, "blue"),
(colors.green, "green"),
(colors.pink, "pink"),
(colors.yellow, "yellow") ]
# Font name and size of the labels.
self.fontName = STATE_DEFAULTS['fontName']
self.fontSize = STATE_DEFAULTS['fontSize']
self.fillColor = STATE_DEFAULTS['fillColor']
self.strokeColor = STATE_DEFAULTS['strokeColor']
self.strokeWidth = STATE_DEFAULTS['strokeWidth']
def _calculateMaxWidth(self, colorNamePairs):
"Calculate the maximum width of some given strings."
m = 0
for t in map(lambda p:str(p[1]),colorNamePairs):
if t:
for s in string.split(t,'\n'):
m = max(m,stringWidth(s, self.fontName, self.fontSize))
return m
def _calcHeight(self):
deltay = self.deltay
dy = self.dy
thisy = upperlefty = self.y - dy
ascent=getFont(self.fontName).face.ascent/1000.
if ascent==0: ascent=0.718 # default (from helvetica)
leading = self.fontSize*1.2
columnCount = 0
count = 0
lowy = upperlefty
for unused, name in colorNamePairs:
T = string.split(name and str(name) or '','\n')
S = []
# thisy+dy/2 = y+leading/2
y = thisy+(dy-ascent)*0.5-leading
newy = thisy-max(deltay,len(S)*leading)
lowy = min(y,newy)
if count == columnMaximum-1:
count = 0
thisy = upperlefty
columnCount = columnCount + 1
else:
thisy = newy
count = count+1
return upperlefty - lowy
def draw(self):
g = Group()
colorNamePairs = self.colorNamePairs
thisx = upperleftx = self.x
thisy = upperlefty = self.y - self.dy
dx, dy, alignment, columnMaximum = self.dx, self.dy, self.alignment, self.columnMaximum
deltax, deltay, dxTextSpace = self.deltax, self.deltay, self.dxTextSpace
fontName, fontSize, fillColor = self.fontName, self.fontSize, self.fillColor
strokeWidth, strokeColor = self.strokeWidth, self.strokeColor
leading = fontSize*1.2
if not deltay:
deltay = max(dy,leading)+self.autoYPadding
if not deltax:
maxWidth = self._calculateMaxWidth(colorNamePairs)
deltax = maxWidth+dx+dxTextSpace+self.autoXPadding
else:
if alignment=='left': maxWidth = self._calculateMaxWidth(colorNamePairs)
def gAdd(t,g=g,fontName=fontName,fontSize=fontSize,fillColor=fillColor):
t.fontName = fontName
t.fontSize = fontSize
t.fillColor = fillColor
return g.add(t)
ascent=getFont(fontName).face.ascent/1000.
if ascent==0: ascent=0.718 # default (from helvetica)
ascent=ascent*fontSize # normalize
columnCount = 0
count = 0
callout = getattr(self,'callout',None)
for col, name in colorNamePairs:
T = string.split(name and str(name) or '','\n')
S = []
# thisy+dy/2 = y+leading/2
y = thisy+(dy-ascent)*0.5
if callout: callout(self,g,thisx,y,colorNamePairs[count])
if alignment == "left":
for t in T:
# align text to left
s = String(thisx+maxWidth,y,t)
s.textAnchor = "end"
S.append(s)
y = y-leading
x = thisx+maxWidth+dxTextSpace
elif alignment == "right":
for t in T:
# align text to right
s = String(thisx+dx+dxTextSpace, y, t)
s.textAnchor = "start"
S.append(s)
y = y-leading
x = thisx
else:
raise ValueError, "bad alignment"
# Make a 'normal' color swatch...
if isinstance(col, colors.Color):
r = Rect(x, thisy, dx, dy)
r.fillColor = col
r.strokeColor = strokeColor
r.strokeWidth = strokeWidth
g.add(r)
else:
#try and see if we should do better.
try:
c = copy.deepcopy(col)
c.x = x
c.y = thisy
c.width = dx
c.height = dy
g.add(c)
except:
pass
map(gAdd,S)
if count%columnMaximum == columnMaximum-1:
thisx = thisx+deltax
thisy = upperlefty
columnCount = columnCount + 1
else:
thisy = thisy-max(deltay,len(S)*leading)
count = count+1
return g
class LineSwatch(Widget):
"""basically a Line with properties added so it can be used in a LineLegend"""
_attrMap = AttrMap(
x = AttrMapValue(isNumber, desc="x-coordinate for swatch line start point"),
y = AttrMapValue(isNumber, desc="y-coordinate for swatch line start point"),
width = AttrMapValue(isNumber, desc="length of swatch line"),
height = AttrMapValue(isNumber, desc="used for line strokeWidth"),
strokeColor = AttrMapValue(isColorOrNone, desc="color of swatch line"),
strokeDashArray = AttrMapValue(isListOfNumbersOrNone, desc="dash array for swatch line"),
)
def __init__(self):
from reportlab.lib.colors import red
from reportlab.graphics.shapes import Line
self.x = 0
self.y = 0
self.width = 20
self.height = 1
self.strokeColor = red
self.strokeDashArray = None
def draw(self):
l = Line(self.x,self.y,self.x+self.width,self.y)
l.strokeColor = self.strokeColor
l.strokeDashArray = self.strokeDashArray
l.strokeWidth = self.height
return l
class LineLegend(Legend):
"""A subclass of Legend for drawing legends with lines as the
swatches rather than rectangles. Useful for lineCharts and
linePlots. Should be similar in all other ways the the standard
Legend class.
"""
_attrMap = AttrMap(
x = AttrMapValue(isNumber, desc="x-coordinate of upper-left reference point"),
y = AttrMapValue(isNumber, desc="y-coordinate of upper-left reference point"),
deltax = AttrMapValue(isNumberOrNone, desc="x-distance between neighbouring line-swatches"),
deltay = AttrMapValue(isNumberOrNone, desc="y-distance between neighbouring line-swatches"),
dxTextSpace = AttrMapValue(isNumber, desc="Distance between line-swatches and text"),
autoXPadding = AttrMapValue(isNumber, desc="x Padding between columns if deltax=None"),
autoYPadding = AttrMapValue(isNumber, desc="y Padding between rows if deltay=None"),
dx = AttrMapValue(isNumber, desc="Width of line-swatch - ie length of the line"),
dy = AttrMapValue(isNumber, desc="Height of line-swatch - ie strokeWidth to be used for the line"),
columnMaximum = AttrMapValue(isNumber, desc="Max. number of items per column"),
alignment = AttrMapValue(OneOf("left", "right"), desc="Alignment of text with respect to line-swatches"),
colorNamePairs = AttrMapValue(None, desc="List of color/name tuples (color can also be widget)"),
fontName = AttrMapValue(isString, desc="Font name of the strings"),
fontSize = AttrMapValue(isNumber, desc="Font size of the strings"),
fillColor = AttrMapValue(isColorOrNone, desc=""),
strokeColor = AttrMapValue(isColorOrNone, desc="Stroke color of the line-swatches"),
strokeWidth = AttrMapValue(isNumber, desc="Width of the line-swatches"),
callout = AttrMapValue(None, desc="a user callout(self,g,x,y,(color,text))"),
)
def __init__(self):
Legend.__init__(self)
# Size of swatch rectangle.
self.dx = 10 #width of line
self.dy = 2 #strokeWidth for line
# Color/name pairs.
self.colorNamePairs = []
for col, colName in [ (colors.red, "red"),
(colors.blue, "blue"),
(colors.green, "green"),
(colors.pink, "pink"),
(colors.yellow, "yellow") ]:
l = LineSwatch()
l.strokeColor = col
self.colorNamePairs.append((l, colName))
# Font name and size of the labels.
self.fillColor = STATE_DEFAULTS['fillColor']
self.strokeColor = STATE_DEFAULTS['strokeColor']
self.strokeWidth = STATE_DEFAULTS['strokeWidth']
def draw(self):
g = Group()
colorNamePairs = self.colorNamePairs
thisx = upperleftx = self.x
thisy = upperlefty = self.y - self.dy
dx, dy, alignment, columnMaximum = self.dx, self.dy, self.alignment, self.columnMaximum
deltax, deltay, dxTextSpace = self.deltax, self.deltay, self.dxTextSpace
fontName, fontSize, fillColor = self.fontName, self.fontSize, self.fillColor
strokeWidth, strokeColor = self.strokeWidth, self.strokeColor
leading = fontSize*1.2
if not deltay:
deltay = max(dy,leading)+self.autoYPadding
if not deltax:
maxWidth = self._calculateMaxWidth(colorNamePairs)
deltax = maxWidth+dx+dxTextSpace+self.autoXPadding
else:
if alignment=='left': maxWidth = self._calculateMaxWidth(colorNamePairs)
def gAdd(t,g=g,fontName=fontName,fontSize=fontSize,fillColor=fillColor):
t.fontName = fontName
t.fontSize = fontSize
t.fillColor = fillColor
return g.add(t)
ascent=getFont(fontName).face.ascent/1000.
if ascent==0: ascent=0.718 # default (from helvetica)
ascent=ascent*fontSize # normalize
columnCount = 0
count = 0
callout = getattr(self,'callout',None)
for col, name in colorNamePairs:
T = string.split(name and str(name) or '','\n')
S = []
# thisy+dy/2 = y+leading/2
y = thisy+(dy-ascent)*0.5
if callout: callout(self,g,thisx,y,colorNamePairs[count])
if alignment == "left":
for t in T:
# align text to left
s = String(thisx+maxWidth,y,t)
s.textAnchor = "end"
S.append(s)
y = y-leading
x = thisx+maxWidth+dxTextSpace
elif alignment == "right":
for t in T:
# align text to right
s = String(thisx+dx+dxTextSpace, y, t)
s.textAnchor = "start"
S.append(s)
y = y-leading
x = thisx
else:
raise ValueError, "bad alignment"
# Make a 'normal' color line-swatch...
if isinstance(col, colors.Color):
l = LineSwatch()
l.x = x
l.y = thisy
l.width = dx
l.height = dy
l.strokeColor = col
g.add(l)
else:
#try and see if we should do better.
try:
c = copy.deepcopy(col)
c.x = x
c.y = thisy
c.width = dx
c.height = dy
g.add(c)
except:
pass
map(gAdd,S)
if count%columnMaximum == columnMaximum-1:
thisx = thisx+deltax
thisy = upperlefty
columnCount = columnCount + 1
else:
thisy = thisy-max(deltay,len(S)*leading)
count = count+1
return g
def demo(self):
"Make sample legend."
d = Drawing(200, 100)
legend = Legend()
legend.alignment = 'left'
legend.x = 0
legend.y = 100
legend.dxTextSpace = 5
items = string.split('red green blue yellow pink black white', ' ')
items = map(lambda i:(getattr(colors, i), i), items)
legend.colorNamePairs = items
d.add(legend, 'legend')
return d
def sample1c():
"Make sample legend."
d = Drawing(200, 100)
legend = Legend()
legend.alignment = 'right'
legend.x = 0
legend.y = 100
legend.dxTextSpace = 5
items = string.split('red green blue yellow pink black white', ' ')
items = map(lambda i:(getattr(colors, i), i), items)
legend.colorNamePairs = items
d.add(legend, 'legend')
return d
def sample2c():
"Make sample legend."
d = Drawing(200, 100)
legend = Legend()
legend.alignment = 'right'
legend.x = 20
legend.y = 90
legend.deltax = 60
legend.dxTextSpace = 10
legend.columnMaximum = 4
items = string.split('red green blue yellow pink black white', ' ')
items = map(lambda i:(getattr(colors, i), i), items)
legend.colorNamePairs = items
d.add(legend, 'legend')
return d
def sample3():
"Make sample legend with line swatches."
d = Drawing(200, 100)
legend = LineLegend()
legend.alignment = 'right'
legend.x = 20
legend.y = 90
legend.deltax = 60
legend.dxTextSpace = 10
legend.columnMaximum = 4
items = string.split('red green blue yellow pink black white', ' ')
items = map(lambda i:(getattr(colors, i), i), items)
legend.colorNamePairs = items
d.add(legend, 'legend')
return d
def sample3a():
"Make sample legend with line swatches and dasharrays on the lines."
d = Drawing(200, 100)
legend = LineLegend()
legend.alignment = 'right'
legend.x = 20
legend.y = 90
legend.deltax = 60
legend.dxTextSpace = 10
legend.columnMaximum = 4
items = string.split('red green blue yellow pink black white', ' ')
darrays = ([2,1], [2,5], [2,2,5,5], [1,2,3,4], [4,2,3,4], [1,2,3,4,5,6], [1])
cnp = []
for i in range(0, len(items)):
l = LineSwatch()
l.strokeColor = getattr(colors, items[i])
l.strokeDashArray = darrays[i]
cnp.append((l, items[i]))
legend.colorNamePairs = cnp
d.add(legend, 'legend')
return d

View File

@ -1,656 +0,0 @@
#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/graphics/charts/linecharts.py
"""
This modules defines a very preliminary Line Chart example.
"""
__version__=''' $Id$ '''
import string
from types import FunctionType, StringType
from reportlab.lib import colors
from reportlab.lib.validators import isNumber, isColor, isColorOrNone, isListOfStrings, \
isListOfStringsOrNone, SequenceOf, isBoolean, NoneOr, \
isListOfNumbersOrNone
from reportlab.lib.attrmap import *
from reportlab.lib.formatters import Formatter
from reportlab.graphics.widgetbase import Widget, TypedPropertyCollection, PropHolder
from reportlab.graphics.shapes import Line, Rect, Group, Drawing, Polygon, PolyLine
from reportlab.graphics.widgets.signsandsymbols import NoEntry
from reportlab.graphics.charts.axes import XCategoryAxis, YValueAxis
from reportlab.graphics.charts.textlabels import Label
from reportlab.graphics.widgets.markers import uSymbol2Symbol, isSymbol, makeMarker
from reportlab.graphics.charts.areas import PlotArea
class LineChartProperties(PropHolder):
_attrMap = AttrMap(
strokeWidth = AttrMapValue(isNumber, desc='Width of a line.'),
strokeColor = AttrMapValue(isColorOrNone, desc='Color of a line.'),
strokeDashArray = AttrMapValue(isListOfNumbersOrNone, desc='Dash array of a line.'),
symbol = AttrMapValue(NoneOr(isSymbol), desc='Widget placed at data points.'),
)
class LineChart(PlotArea):
pass
# This is conceptually similar to the VerticalBarChart.
# Still it is better named HorizontalLineChart... :-/
class HorizontalLineChart(LineChart):
"""Line chart with multiple lines.
A line chart is assumed to have one category and one value axis.
Despite its generic name this particular line chart class has
a vertical value axis and a horizontal category one. It may
evolve into individual horizontal and vertical variants (like
with the existing bar charts).
Available attributes are:
x: x-position of lower-left chart origin
y: y-position of lower-left chart origin
width: chart width
height: chart height
useAbsolute: disables auto-scaling of chart elements (?)
lineLabelNudge: distance of data labels to data points
lineLabels: labels associated with data values
lineLabelFormat: format string or callback function
groupSpacing: space between categories
joinedLines: enables drawing of lines
strokeColor: color of chart lines (?)
fillColor: color for chart background (?)
lines: style list, used cyclically for data series
valueAxis: value axis object
categoryAxis: category axis object
categoryNames: category names
data: chart data, a list of data series of equal length
"""
_attrMap = AttrMap(BASE=LineChart,
useAbsolute = AttrMapValue(isNumber, desc='Flag to use absolute spacing values.'),
lineLabelNudge = AttrMapValue(isNumber, desc='Distance between a data point and its label.'),
lineLabels = AttrMapValue(None, desc='Handle to the list of data point labels.'),
lineLabelFormat = AttrMapValue(None, desc='Formatting string or function used for data point labels.'),
lineLabelArray = AttrMapValue(None, desc='explicit array of line label values, must match size of data if present.'),
groupSpacing = AttrMapValue(isNumber, desc='? - Likely to disappear.'),
joinedLines = AttrMapValue(isNumber, desc='Display data points joined with lines if true.'),
lines = AttrMapValue(None, desc='Handle of the lines.'),
valueAxis = AttrMapValue(None, desc='Handle of the value axis.'),
categoryAxis = AttrMapValue(None, desc='Handle of the category axis.'),
categoryNames = AttrMapValue(isListOfStringsOrNone, desc='List of category names.'),
data = AttrMapValue(None, desc='Data to be plotted, list of (lists of) numbers.'),
inFill = AttrMapValue(isBoolean, desc='Whether infilling should be done.'),
reversePlotOrder = AttrMapValue(isBoolean, desc='If true reverse plot order.'),
annotations = AttrMapValue(None, desc='list of callables, will be called with self, xscale, yscale.'),
)
def __init__(self):
LineChart.__init__(self)
# Allow for a bounding rectangle.
self.strokeColor = None
self.fillColor = None
# Named so we have less recoding for the horizontal one :-)
self.categoryAxis = XCategoryAxis()
self.valueAxis = YValueAxis()
# This defines two series of 3 points. Just an example.
self.data = [(100,110,120,130),
(70, 80, 80, 90)]
self.categoryNames = ('North','South','East','West')
self.lines = TypedPropertyCollection(LineChartProperties)
self.lines.strokeWidth = 1
self.lines[0].strokeColor = colors.red
self.lines[1].strokeColor = colors.green
self.lines[2].strokeColor = colors.blue
# control spacing. if useAbsolute = 1 then
# the next parameters are in points; otherwise
# they are 'proportions' and are normalized to
# fit the available space.
self.useAbsolute = 0 #- not done yet
self.groupSpacing = 1 #5
self.lineLabels = TypedPropertyCollection(Label)
self.lineLabelFormat = None
self.lineLabelArray = None
# This says whether the origin is above or below
# the data point. +10 means put the origin ten points
# above the data point if value > 0, or ten
# points below if data value < 0. This is different
# to label dx/dy which are not dependent on the
# sign of the data.
self.lineLabelNudge = 10
# If you have multiple series, by default they butt
# together.
# New line chart attributes.
self.joinedLines = 1 # Connect items with straight lines.
self.inFill = 0
self.reversePlotOrder = 0
def demo(self):
"""Shows basic use of a line chart."""
drawing = Drawing(200, 100)
data = [
(13, 5, 20, 22, 37, 45, 19, 4),
(14, 10, 21, 28, 38, 46, 25, 5)
]
lc = HorizontalLineChart()
lc.x = 20
lc.y = 10
lc.height = 85
lc.width = 170
lc.data = data
lc.lines.symbol = makeMarker('Circle')
drawing.add(lc)
return drawing
def calcPositions(self):
"""Works out where they go.
Sets an attribute _positions which is a list of
lists of (x, y) matching the data.
"""
self._seriesCount = len(self.data)
self._rowLength = max(map(len,self.data))
if self.useAbsolute:
# Dimensions are absolute.
normFactor = 1.0
else:
# Dimensions are normalized to fit.
normWidth = self.groupSpacing
availWidth = self.categoryAxis.scale(0)[1]
normFactor = availWidth / normWidth
self._positions = []
for rowNo in range(len(self.data)):
lineRow = []
for colNo in range(len(self.data[rowNo])):
datum = self.data[rowNo][colNo]
if datum is not None:
(groupX, groupWidth) = self.categoryAxis.scale(colNo)
x = groupX + (0.5 * self.groupSpacing * normFactor)
y = self.valueAxis.scale(0)
height = self.valueAxis.scale(datum) - y
lineRow.append((x, y+height))
self._positions.append(lineRow)
def _innerDrawLabel(self, rowNo, colNo, x, y):
"Draw a label for a given item in the list."
labelFmt = self.lineLabelFormat
labelValue = self.data[rowNo][colNo]
if labelFmt is None:
labelText = None
elif type(labelFmt) is StringType:
if labelFmt == 'values':
labelText = self.lineLabelArray[rowNo][colNo]
else:
labelText = labelFmt % labelValue
elif type(labelFmt) is FunctionType:
labelText = labelFmt(labelValue)
elif isinstance(labelFmt, Formatter):
labelText = labelFmt(labelValue)
else:
msg = "Unknown formatter type %s, expected string or function"
raise Exception, msg % labelFmt
if labelText:
label = self.lineLabels[(rowNo, colNo)]
# Make sure labels are some distance off the data point.
if y > 0:
label.setOrigin(x, y + self.lineLabelNudge)
else:
label.setOrigin(x, y - self.lineLabelNudge)
label.setText(labelText)
else:
label = None
return label
def drawLabel(self, G, rowNo, colNo, x, y):
'''Draw a label for a given item in the list.
G must have an add method'''
G.add(self._innerDrawLabel(rowNo,colNo,x,y))
def makeLines(self):
g = Group()
labelFmt = self.lineLabelFormat
P = range(len(self._positions))
if self.reversePlotOrder: P.reverse()
inFill = self.inFill
if inFill:
inFillY = self.categoryAxis._y
inFillX0 = self.valueAxis._x
inFillX1 = inFillX0 + self.categoryAxis._length
inFillG = getattr(self,'_inFillG',g)
# Iterate over data rows.
for rowNo in P:
row = self._positions[rowNo]
styleCount = len(self.lines)
styleIdx = rowNo % styleCount
rowStyle = self.lines[styleIdx]
rowColor = rowStyle.strokeColor
dash = getattr(rowStyle, 'strokeDashArray', None)
if hasattr(self.lines[styleIdx], 'strokeWidth'):
strokeWidth = self.lines[styleIdx].strokeWidth
elif hasattr(self.lines, 'strokeWidth'):
strokeWidth = self.lines.strokeWidth
else:
strokeWidth = None
# Iterate over data columns.
if self.joinedLines:
points = []
for colNo in range(len(row)):
points += row[colNo]
if inFill:
points = points + [inFillX1,inFillY,inFillX0,inFillY]
inFillG.add(Polygon(points,fillColor=rowColor,strokeColor=rowColor,strokeWidth=0.1))
else:
line = PolyLine(points,strokeColor=rowColor,strokeLineCap=0,strokeLineJoin=1)
if strokeWidth:
line.strokeWidth = strokeWidth
if dash:
line.strokeDashArray = dash
g.add(line)
if hasattr(self.lines[styleIdx], 'symbol'):
uSymbol = self.lines[styleIdx].symbol
elif hasattr(self.lines, 'symbol'):
uSymbol = self.lines.symbol
else:
uSymbol = None
if uSymbol:
for colNo in range(len(row)):
x1, y1 = row[colNo]
symbol = uSymbol2Symbol(uSymbol,x1,y1,rowStyle.strokeColor)
if symbol: g.add(symbol)
# Draw item labels.
for colNo in range(len(row)):
x1, y1 = row[colNo]
self.drawLabel(g, rowNo, colNo, x1, y1)
return g
def draw(self):
"Draws itself."
vA, cA = self.valueAxis, self.categoryAxis
vA.setPosition(self.x, self.y, self.height)
if vA: vA.joinAxis = cA
if cA: cA.joinAxis = vA
vA.configure(self.data)
# If zero is in chart, put x axis there, otherwise
# use bottom.
xAxisCrossesAt = vA.scale(0)
if ((xAxisCrossesAt > self.y + self.height) or (xAxisCrossesAt < self.y)):
y = self.y
else:
y = xAxisCrossesAt
cA.setPosition(self.x, y, self.width)
cA.configure(self.data)
self.calcPositions()
g = Group()
g.add(self.makeBackground())
if self.inFill:
self._inFillG = Group()
g.add(self._inFillG)
g.add(cA)
g.add(vA)
vA.gridStart = cA._x
vA.gridEnd = cA._x+cA._length
cA.gridStart = vA._y
cA.gridEnd = vA._y+vA._length
cA.makeGrid(g,parent=self)
vA.makeGrid(g,parent=self)
g.add(self.makeLines())
for a in getattr(self,'annotations',()): g.add(a(self,cA.scale,vA.scale))
return g
def _cmpFakeItem(a,b):
'''t, z0, z1, x, y = a[:5]'''
return cmp((-a[1],a[3],a[0],-a[4]),(-b[1],b[3],b[0],-b[4]))
class _FakeGroup:
def __init__(self):
self._data = []
def add(self,what):
if what: self._data.append(what)
def value(self):
return self._data
def sort(self):
self._data.sort(_cmpFakeItem)
#for t in self._data: print t
class HorizontalLineChart3D(HorizontalLineChart):
_attrMap = AttrMap(BASE=HorizontalLineChart,
theta_x = AttrMapValue(isNumber, desc='dx/dz'),
theta_y = AttrMapValue(isNumber, desc='dy/dz'),
zDepth = AttrMapValue(isNumber, desc='depth of an individual series'),
zSpace = AttrMapValue(isNumber, desc='z gap around series'),
)
theta_x = .5
theta_y = .5
zDepth = 10
zSpace = 3
def calcPositions(self):
HorizontalLineChart.calcPositions(self)
nSeries = self._seriesCount
zSpace = self.zSpace
zDepth = self.zDepth
if self.categoryAxis.style=='parallel_3d':
_3d_depth = nSeries*zDepth+(nSeries+1)*zSpace
else:
_3d_depth = zDepth + 2*zSpace
self._3d_dx = self.theta_x*_3d_depth
self._3d_dy = self.theta_y*_3d_depth
def _calc_z0(self,rowNo):
zSpace = self.zSpace
if self.categoryAxis.style=='parallel_3d':
z0 = rowNo*(self.zDepth+zSpace)+zSpace
else:
z0 = zSpace
return z0
def _zadjust(self,x,y,z):
return x+z*self.theta_x, y+z*self.theta_y
def makeLines(self):
labelFmt = self.lineLabelFormat
P = range(len(self._positions))
if self.reversePlotOrder: P.reverse()
inFill = self.inFill
assert not inFill, "inFill not supported for 3d yet"
#if inFill:
#inFillY = self.categoryAxis._y
#inFillX0 = self.valueAxis._x
#inFillX1 = inFillX0 + self.categoryAxis._length
#inFillG = getattr(self,'_inFillG',g)
zDepth = self.zDepth
_zadjust = self._zadjust
theta_x = self.theta_x
theta_y = self.theta_y
F = _FakeGroup()
from utils3d import _make_3d_line_info
tileWidth = getattr(self,'_3d_tilewidth',None)
if not tileWidth and self.categoryAxis.style!='parallel_3d': tileWidth = 1
# Iterate over data rows.
for rowNo in P:
row = self._positions[rowNo]
n = len(row)
styleCount = len(self.lines)
styleIdx = rowNo % styleCount
rowStyle = self.lines[styleIdx]
rowColor = rowStyle.strokeColor
dash = getattr(rowStyle, 'strokeDashArray', None)
z0 = self._calc_z0(rowNo)
z1 = z0 + zDepth
if hasattr(self.lines[styleIdx], 'strokeWidth'):
strokeWidth = self.lines[styleIdx].strokeWidth
elif hasattr(self.lines, 'strokeWidth'):
strokeWidth = self.lines.strokeWidth
else:
strokeWidth = None
# Iterate over data columns.
if self.joinedLines:
if n:
x0, y0 = row[0]
for colNo in xrange(1,n):
x1, y1 = row[colNo]
_make_3d_line_info( F, x0, x1, y0, y1, z0, z1,
theta_x, theta_y,
rowColor, fillColorShaded=None, tileWidth=tileWidth,
strokeColor=None, strokeWidth=None, strokeDashArray=None,
shading=0.1)
x0, y0 = x1, y1
if hasattr(self.lines[styleIdx], 'symbol'):
uSymbol = self.lines[styleIdx].symbol
elif hasattr(self.lines, 'symbol'):
uSymbol = self.lines.symbol
else:
uSymbol = None
if uSymbol:
for colNo in xrange(n):
x1, y1 = row[colNo]
x1, y1 = _zadjust(x1,y1,z0)
symbol = uSymbol2Symbol(uSymbol,x1,y1,rowColor)
if symbol: F.add((2,z0,z0,x1,y1,symbol))
# Draw item labels.
for colNo in xrange(n):
x1, y1 = row[colNo]
x1, y1 = _zadjust(x1,y1,z0)
L = self._innerDrawLabel(rowNo, colNo, x1, y1)
if L: F.add((2,z0,z0,x1,y1,L))
F.sort()
g = Group()
map(lambda x,a=g.add: a(x[-1]),F.value())
return g
class VerticalLineChart(LineChart):
pass
def sample1():
drawing = Drawing(400, 200)
data = [
(13, 5, 20, 22, 37, 45, 19, 4),
(5, 20, 46, 38, 23, 21, 6, 14)
]
lc = HorizontalLineChart()
lc.x = 50
lc.y = 50
lc.height = 125
lc.width = 300
lc.data = data
lc.joinedLines = 1
lc.lines.symbol = makeMarker('FilledDiamond')
lc.lineLabelFormat = '%2.0f'
catNames = string.split('Jan Feb Mar Apr May Jun Jul Aug', ' ')
lc.categoryAxis.categoryNames = catNames
lc.categoryAxis.labels.boxAnchor = 'n'
lc.valueAxis.valueMin = 0
lc.valueAxis.valueMax = 60
lc.valueAxis.valueStep = 15
drawing.add(lc)
return drawing
class SampleHorizontalLineChart(HorizontalLineChart):
"Sample class overwriting one method to draw additional horizontal lines."
def demo(self):
"""Shows basic use of a line chart."""
drawing = Drawing(200, 100)
data = [
(13, 5, 20, 22, 37, 45, 19, 4),
(14, 10, 21, 28, 38, 46, 25, 5)
]
lc = SampleHorizontalLineChart()
lc.x = 20
lc.y = 10
lc.height = 85
lc.width = 170
lc.data = data
lc.strokeColor = colors.white
lc.fillColor = colors.HexColor(0xCCCCCC)
drawing.add(lc)
return drawing
def makeBackground(self):
g = Group()
g.add(HorizontalLineChart.makeBackground(self))
valAxis = self.valueAxis
valTickPositions = valAxis._tickValues
for y in valTickPositions:
y = valAxis.scale(y)
g.add(Line(self.x, y, self.x+self.width, y,
strokeColor = self.strokeColor))
return g
def sample1a():
drawing = Drawing(400, 200)
data = [
(13, 5, 20, 22, 37, 45, 19, 4),
(5, 20, 46, 38, 23, 21, 6, 14)
]
lc = SampleHorizontalLineChart()
lc.x = 50
lc.y = 50
lc.height = 125
lc.width = 300
lc.data = data
lc.joinedLines = 1
lc.strokeColor = colors.white
lc.fillColor = colors.HexColor(0xCCCCCC)
lc.lines.symbol = makeMarker('FilledDiamond')
lc.lineLabelFormat = '%2.0f'
catNames = string.split('Jan Feb Mar Apr May Jun Jul Aug', ' ')
lc.categoryAxis.categoryNames = catNames
lc.categoryAxis.labels.boxAnchor = 'n'
lc.valueAxis.valueMin = 0
lc.valueAxis.valueMax = 60
lc.valueAxis.valueStep = 15
drawing.add(lc)
return drawing
def sample2():
drawing = Drawing(400, 200)
data = [
(13, 5, 20, 22, 37, 45, 19, 4),
(5, 20, 46, 38, 23, 21, 6, 14)
]
lc = HorizontalLineChart()
lc.x = 50
lc.y = 50
lc.height = 125
lc.width = 300
lc.data = data
lc.joinedLines = 1
lc.lines.symbol = makeMarker('Smiley')
lc.lineLabelFormat = '%2.0f'
lc.strokeColor = colors.black
lc.fillColor = colors.lightblue
catNames = string.split('Jan Feb Mar Apr May Jun Jul Aug', ' ')
lc.categoryAxis.categoryNames = catNames
lc.categoryAxis.labels.boxAnchor = 'n'
lc.valueAxis.valueMin = 0
lc.valueAxis.valueMax = 60
lc.valueAxis.valueStep = 15
drawing.add(lc)
return drawing
def sample3():
drawing = Drawing(400, 200)
data = [
(13, 5, 20, 22, 37, 45, 19, 4),
(5, 20, 46, 38, 23, 21, 6, 14)
]
lc = HorizontalLineChart()
lc.x = 50
lc.y = 50
lc.height = 125
lc.width = 300
lc.data = data
lc.joinedLines = 1
lc.lineLabelFormat = '%2.0f'
lc.strokeColor = colors.black
lc.lines[0].symbol = makeMarker('Smiley')
lc.lines[1].symbol = NoEntry
lc.lines[0].strokeWidth = 2
lc.lines[1].strokeWidth = 4
catNames = string.split('Jan Feb Mar Apr May Jun Jul Aug', ' ')
lc.categoryAxis.categoryNames = catNames
lc.categoryAxis.labels.boxAnchor = 'n'
lc.valueAxis.valueMin = 0
lc.valueAxis.valueMax = 60
lc.valueAxis.valueStep = 15
drawing.add(lc)
return drawing

File diff suppressed because it is too large Load Diff

View File

@ -1,81 +0,0 @@
#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/graphics/charts/markers.py
"""
This modules defines a collection of markers used in charts.
The make* functions return a simple shape or a widget as for
the smiley.
"""
__version__=''' $Id$ '''
from reportlab.lib import colors
from reportlab.graphics.shapes import Rect, Line, Circle, Polygon
from reportlab.graphics.widgets.signsandsymbols import SmileyFace
def makeEmptySquare(x, y, size, color):
"Make an empty square marker."
d = size/2.0
rect = Rect(x-d, y-d, 2*d, 2*d)
rect.strokeColor = color
rect.fillColor = None
return rect
def makeFilledSquare(x, y, size, color):
"Make a filled square marker."
d = size/2.0
rect = Rect(x-d, y-d, 2*d, 2*d)
rect.strokeColor = color
rect.fillColor = color
return rect
def makeFilledDiamond(x, y, size, color):
"Make a filled diamond marker."
d = size/2.0
poly = Polygon((x-d,y, x,y+d, x+d,y, x,y-d))
poly.strokeColor = color
poly.fillColor = color
return poly
def makeEmptyCircle(x, y, size, color):
"Make a hollow circle marker."
d = size/2.0
circle = Circle(x, y, d)
circle.strokeColor = color
circle.fillColor = colors.white
return circle
def makeFilledCircle(x, y, size, color):
"Make a hollow circle marker."
d = size/2.0
circle = Circle(x, y, d)
circle.strokeColor = color
circle.fillColor = color
return circle
def makeSmiley(x, y, size, color):
"Make a smiley marker."
d = size
s = SmileyFace()
s.fillColor = color
s.x = x-d
s.y = y-d
s.size = d*2
return s

View File

@ -1,863 +0,0 @@
#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/graphics/charts/piecharts.py
# experimental pie chart script. Two types of pie - one is a monolithic
#widget with all top-level properties, the other delegates most stuff to
#a wedges collection whic lets you customize the group or every individual
#wedge.
"""Basic Pie Chart class.
This permits you to customize and pop out individual wedges;
supports elliptical and circular pies.
"""
__version__=''' $Id$ '''
import copy
from math import sin, cos, pi
from reportlab.lib import colors
from reportlab.lib.validators import isColor, isNumber, isListOfNumbersOrNone,\
isListOfNumbers, isColorOrNone, isString,\
isListOfStringsOrNone, OneOf, SequenceOf,\
isBoolean, isListOfColors, isNumberOrNone,\
isNoneOrListOfNoneOrStrings, isTextAnchor,\
isNoneOrListOfNoneOrNumbers, isBoxAnchor,\
isStringOrNone
from reportlab.lib.attrmap import *
from reportlab.pdfgen.canvas import Canvas
from reportlab.graphics.shapes import Group, Drawing, Ellipse, Wedge, String, STATE_DEFAULTS, ArcPath, Polygon
from reportlab.graphics.widgetbase import Widget, TypedPropertyCollection, PropHolder
from textlabels import Label
_ANGLE2BOXANCHOR={0:'w', 45:'sw', 90:'s', 135:'se', 180:'e', 225:'ne', 270:'n', 315: 'nw', -45: 'nw'}
class WedgeLabel(Label):
def _checkDXY(self,ba):
pass
def _getBoxAnchor(self):
na = (int((self._pmv%360)/45.)*45)%360
if not (na % 90): # we have a right angle case
da = (self._pmv - na) % 360
if abs(da)>5:
na = na + (da>0 and 45 or -45)
ba = _ANGLE2BOXANCHOR[na]
self._checkDXY(ba)
return ba
class WedgeProperties(PropHolder):
"""This holds descriptive information about the wedges in a pie chart.
It is not to be confused with the 'wedge itself'; this just holds
a recipe for how to format one, and does not allow you to hack the
angles. It can format a genuine Wedge object for you with its
format method.
"""
_attrMap = AttrMap(
strokeWidth = AttrMapValue(isNumber),
fillColor = AttrMapValue(isColorOrNone),
strokeColor = AttrMapValue(isColorOrNone),
strokeDashArray = AttrMapValue(isListOfNumbersOrNone),
popout = AttrMapValue(isNumber),
fontName = AttrMapValue(isString),
fontSize = AttrMapValue(isNumber),
fontColor = AttrMapValue(isColorOrNone),
labelRadius = AttrMapValue(isNumber),
label_dx = AttrMapValue(isNumber),
label_dy = AttrMapValue(isNumber),
label_angle = AttrMapValue(isNumber),
label_boxAnchor = AttrMapValue(isBoxAnchor),
label_boxStrokeColor = AttrMapValue(isColorOrNone),
label_boxStrokeWidth = AttrMapValue(isNumber),
label_boxFillColor = AttrMapValue(isColorOrNone),
label_strokeColor = AttrMapValue(isColorOrNone),
label_strokeWidth = AttrMapValue(isNumber),
label_text = AttrMapValue(isStringOrNone),
label_leading = AttrMapValue(isNumberOrNone),
label_width = AttrMapValue(isNumberOrNone),
label_maxWidth = AttrMapValue(isNumberOrNone),
label_height = AttrMapValue(isNumberOrNone),
label_textAnchor = AttrMapValue(isTextAnchor),
label_visible = AttrMapValue(isBoolean,desc="True if the label is to be drawn"),
label_topPadding = AttrMapValue(isNumber,'padding at top of box'),
label_leftPadding = AttrMapValue(isNumber,'padding at left of box'),
label_rightPadding = AttrMapValue(isNumber,'padding at right of box'),
label_bottomPadding = AttrMapValue(isNumber,'padding at bottom of box'),
)
def __init__(self):
self.strokeWidth = 0
self.fillColor = None
self.strokeColor = STATE_DEFAULTS["strokeColor"]
self.strokeDashArray = STATE_DEFAULTS["strokeDashArray"]
self.popout = 0
self.fontName = STATE_DEFAULTS["fontName"]
self.fontSize = STATE_DEFAULTS["fontSize"]
self.fontColor = STATE_DEFAULTS["fillColor"]
self.labelRadius = 1.2
self.label_dx = self.label_dy = self.label_angle = 0
self.label_text = None
self.label_topPadding = self.label_leftPadding = self.label_rightPadding = self.label_bottomPadding = 0
self.label_boxAnchor = 'c'
self.label_boxStrokeColor = None #boxStroke
self.label_boxStrokeWidth = 0.5 #boxStrokeWidth
self.label_boxFillColor = None
self.label_strokeColor = None
self.label_strokeWidth = 0.1
self.label_leading = self.label_width = self.label_maxWidth = self.label_height = None
self.label_textAnchor = 'start'
self.label_visible = 1
def _addWedgeLabel(self,text,add,angle,labelX,labelY,wedgeStyle,labelClass=WedgeLabel):
# now draw a label
if self.simpleLabels:
theLabel = String(labelX, labelY, text)
theLabel.textAnchor = "middle"
else:
theLabel = labelClass()
theLabel._pmv = angle
theLabel.x = labelX
theLabel.y = labelY
theLabel.dx = wedgeStyle.label_dx
theLabel.dy = wedgeStyle.label_dy
theLabel.angle = wedgeStyle.label_angle
theLabel.boxAnchor = wedgeStyle.label_boxAnchor
theLabel.boxStrokeColor = wedgeStyle.label_boxStrokeColor
theLabel.boxStrokeWidth = wedgeStyle.label_boxStrokeWidth
theLabel.boxFillColor = wedgeStyle.label_boxFillColor
theLabel.strokeColor = wedgeStyle.label_strokeColor
theLabel.strokeWidth = wedgeStyle.label_strokeWidth
_text = wedgeStyle.label_text
if _text is None: _text = text
theLabel._text = _text
theLabel.leading = wedgeStyle.label_leading
theLabel.width = wedgeStyle.label_width
theLabel.maxWidth = wedgeStyle.label_maxWidth
theLabel.height = wedgeStyle.label_height
theLabel.textAnchor = wedgeStyle.label_textAnchor
theLabel.visible = wedgeStyle.label_visible
theLabel.topPadding = wedgeStyle.label_topPadding
theLabel.leftPadding = wedgeStyle.label_leftPadding
theLabel.rightPadding = wedgeStyle.label_rightPadding
theLabel.bottomPadding = wedgeStyle.label_bottomPadding
theLabel.fontSize = wedgeStyle.fontSize
theLabel.fontName = wedgeStyle.fontName
theLabel.fillColor = wedgeStyle.fontColor
add(theLabel)
def _fixLabels(labels,n):
if labels is None:
labels = [''] * n
else:
i = n-len(labels)
if i>0: labels = labels + ['']*i
return labels
class Pie(Widget):
_attrMap = AttrMap(
x = AttrMapValue(isNumber, desc='X position of the chart within its container.'),
y = AttrMapValue(isNumber, desc='Y position of the chart within its container.'),
width = AttrMapValue(isNumber, desc='width of pie bounding box. Need not be same as width.'),
height = AttrMapValue(isNumber, desc='height of pie bounding box. Need not be same as height.'),
data = AttrMapValue(isListOfNumbers, desc='list of numbers defining wedge sizes; need not sum to 1'),
labels = AttrMapValue(isListOfStringsOrNone, desc="optional list of labels to use for each data point"),
startAngle = AttrMapValue(isNumber, desc="angle of first slice; like the compass, 0 is due North"),
direction = AttrMapValue( OneOf('clockwise', 'anticlockwise'), desc="'clockwise' or 'anticlockwise'"),
slices = AttrMapValue(None, desc="collection of wedge descriptor objects"),
simpleLabels = AttrMapValue(isBoolean, desc="If true(default) use String not super duper WedgeLabel"),
other_threshold = AttrMapValue(isNumber, desc='A value for doing thresh holding, not used yet.'),
)
other_threshold=None
def __init__(self):
self.x = 0
self.y = 0
self.width = 100
self.height = 100
self.data = [1]
self.labels = None # or list of strings
self.startAngle = 90
self.direction = "clockwise"
self.simpleLabels = 1
self.slices = TypedPropertyCollection(WedgeProperties)
self.slices[0].fillColor = colors.darkcyan
self.slices[1].fillColor = colors.blueviolet
self.slices[2].fillColor = colors.blue
self.slices[3].fillColor = colors.cyan
def demo(self):
d = Drawing(200, 100)
pc = Pie()
pc.x = 50
pc.y = 10
pc.width = 100
pc.height = 80
pc.data = [10,20,30,40,50,60]
pc.labels = ['a','b','c','d','e','f']
pc.slices.strokeWidth=0.5
pc.slices[3].popout = 10
pc.slices[3].strokeWidth = 2
pc.slices[3].strokeDashArray = [2,2]
pc.slices[3].labelRadius = 1.75
pc.slices[3].fontColor = colors.red
pc.slices[0].fillColor = colors.darkcyan
pc.slices[1].fillColor = colors.blueviolet
pc.slices[2].fillColor = colors.blue
pc.slices[3].fillColor = colors.cyan
pc.slices[4].fillColor = colors.aquamarine
pc.slices[5].fillColor = colors.cadetblue
pc.slices[6].fillColor = colors.lightcoral
d.add(pc)
return d
def normalizeData(self):
from operator import add
data = self.data
self._sum = sum = float(reduce(add,data,0))
return abs(sum)>=1e-8 and map(lambda x,f=360./sum: f*x, data) or len(data)*[0]
def makeWedges(self):
# normalize slice data
normData = self.normalizeData()
n = len(normData)
labels = _fixLabels(self.labels,n)
xradius = self.width/2.0
yradius = self.height/2.0
centerx = self.x + xradius
centery = self.y + yradius
if self.direction == "anticlockwise":
whichWay = 1
else:
whichWay = -1
g = Group()
i = 0
styleCount = len(self.slices)
startAngle = self.startAngle #% 360
for angle in normData:
endAngle = (startAngle + (angle * whichWay)) #% 360
if abs(startAngle-endAngle)>=1e-5:
if startAngle < endAngle:
a1 = startAngle
a2 = endAngle
else:
a1 = endAngle
a2 = startAngle
#if we didn't use %stylecount here we'd end up with the later wedges
#all having the default style
wedgeStyle = self.slices[i%styleCount]
# is it a popout?
cx, cy = centerx, centery
if wedgeStyle.popout <> 0:
# pop out the wedge
averageAngle = (a1+a2)/2.0
aveAngleRadians = averageAngle * pi/180.0
popdistance = wedgeStyle.popout
cx = centerx + popdistance * cos(aveAngleRadians)
cy = centery + popdistance * sin(aveAngleRadians)
if n > 1:
theWedge = Wedge(cx, cy, xradius, a1, a2, yradius=yradius)
elif n==1:
theWedge = Ellipse(cx, cy, xradius, yradius)
theWedge.fillColor = wedgeStyle.fillColor
theWedge.strokeColor = wedgeStyle.strokeColor
theWedge.strokeWidth = wedgeStyle.strokeWidth
theWedge.strokeDashArray = wedgeStyle.strokeDashArray
g.add(theWedge)
text = labels[i]
if text:
averageAngle = (a1+a2)/2.0
aveAngleRadians = averageAngle*pi/180.0
labelRadius = wedgeStyle.labelRadius
labelX = cx + (0.5 * self.width * cos(aveAngleRadians) * labelRadius)
labelY = cy + (0.5 * self.height * sin(aveAngleRadians) * labelRadius)
_addWedgeLabel(self,text,g.add,averageAngle,labelX,labelY,wedgeStyle)
startAngle = endAngle
i = i + 1
return g
def draw(self):
g = Group()
g.add(self.makeWedges())
return g
class LegendedPie(Pie):
"""Pie with a two part legend (one editable with swatches, one hidden without swatches)."""
_attrMap = AttrMap(BASE=Pie,
drawLegend = AttrMapValue(isBoolean, desc="If true then create and draw legend"),
legend1 = AttrMapValue(None, desc="Handle to legend for pie"),
legendNumberFormat = AttrMapValue(None, desc="Formatting routine for number on right hand side of legend."),
legendNumberOffset = AttrMapValue(isNumber, desc="Horizontal space between legend and numbers on r/hand side"),
pieAndLegend_colors = AttrMapValue(isListOfColors, desc="Colours used for both swatches and pie"),
legend_names = AttrMapValue(isNoneOrListOfNoneOrStrings, desc="Names used in legend (or None)"),
legend_data = AttrMapValue(isNoneOrListOfNoneOrNumbers, desc="Numbers used on r/hand side of legend (or None)"),
leftPadding = AttrMapValue(isNumber, desc='Padding on left of drawing'),
rightPadding = AttrMapValue(isNumber, desc='Padding on right of drawing'),
topPadding = AttrMapValue(isNumber, desc='Padding at top of drawing'),
bottomPadding = AttrMapValue(isNumber, desc='Padding at bottom of drawing'),
)
def __init__(self):
Pie.__init__(self)
self.x = 0
self.y = 0
self.height = 100
self.width = 100
self.data = [38.4, 20.7, 18.9, 15.4, 6.6]
self.labels = None
self.direction = 'clockwise'
PCMYKColor, black = colors.PCMYKColor, colors.black
self.pieAndLegend_colors = [PCMYKColor(11,11,72,0,spotName='PANTONE 458 CV'),
PCMYKColor(100,65,0,30,spotName='PANTONE 288 CV'),
PCMYKColor(11,11,72,0,spotName='PANTONE 458 CV',density=75),
PCMYKColor(100,65,0,30,spotName='PANTONE 288 CV',density=75),
PCMYKColor(11,11,72,0,spotName='PANTONE 458 CV',density=50),
PCMYKColor(100,65,0,30,spotName='PANTONE 288 CV',density=50)]
#Allows us up to six 'wedges' to be coloured
self.slices[0].fillColor=self.pieAndLegend_colors[0]
self.slices[1].fillColor=self.pieAndLegend_colors[1]
self.slices[2].fillColor=self.pieAndLegend_colors[2]
self.slices[3].fillColor=self.pieAndLegend_colors[3]
self.slices[4].fillColor=self.pieAndLegend_colors[4]
self.slices[5].fillColor=self.pieAndLegend_colors[5]
self.slices.strokeWidth = 0.75
self.slices.strokeColor = black
legendOffset = 17
self.legendNumberOffset = 51
self.legendNumberFormat = '%.1f%%'
self.legend_data = self.data
#set up the legends
from reportlab.graphics.charts.legends import Legend
self.legend1 = Legend()
self.legend1.x = self.width+legendOffset
self.legend1.y = self.height
self.legend1.deltax = 5.67
self.legend1.deltay = 14.17
self.legend1.dxTextSpace = 11.39
self.legend1.dx = 5.67
self.legend1.dy = 5.67
self.legend1.columnMaximum = 7
self.legend1.alignment = 'right'
self.legend_names = ['AAA:','AA:','A:','BBB:','NR:']
for f in range(0,len(self.data)):
self.legend1.colorNamePairs.append((self.pieAndLegend_colors[f], self.legend_names[f]))
self.legend1.fontName = "Helvetica-Bold"
self.legend1.fontSize = 6
self.legend1.strokeColor = black
self.legend1.strokeWidth = 0.5
self._legend2 = Legend()
self._legend2.dxTextSpace = 0
self._legend2.dx = 0
self._legend2.alignment = 'right'
self._legend2.fontName = "Helvetica-Oblique"
self._legend2.fontSize = 6
self._legend2.strokeColor = self.legend1.strokeColor
self.leftPadding = 5
self.rightPadding = 5
self.topPadding = 5
self.bottomPadding = 5
self.drawLegend = 1
def draw(self):
if self.drawLegend:
self.legend1.colorNamePairs = []
self._legend2.colorNamePairs = []
for f in range(0,len(self.data)):
if self.legend_names == None:
self.slices[f].fillColor = self.pieAndLegend_colors[f]
self.legend1.colorNamePairs.append((self.pieAndLegend_colors[f], None))
else:
try:
self.slices[f].fillColor = self.pieAndLegend_colors[f]
self.legend1.colorNamePairs.append((self.pieAndLegend_colors[f], self.legend_names[f]))
except IndexError:
self.slices[f].fillColor = self.pieAndLegend_colors[f%len(self.pieAndLegend_colors)]
self.legend1.colorNamePairs.append((self.pieAndLegend_colors[f%len(self.pieAndLegend_colors)], self.legend_names[f]))
if self.legend_data != None:
ldf = self.legend_data[f]
lNF = self.legendNumberFormat
from types import StringType
if ldf is None or lNF is None:
pass
elif type(lNF) is StringType:
ldf = lNF % ldf
elif callable(lNF):
ldf = lNF(ldf)
else:
p = self.legend_names[f]
if self.legend_data != None:
ldf = self.legend_data[f]
lNF = self.legendNumberFormat
if ldf is None or lNF is None:
pass
elif type(lNF) is StringType:
ldf = lNF % ldf
elif callable(lNF):
ldf = lNF(ldf)
else:
msg = "Unknown formatter type %s, expected string or function" % self.legendNumberFormat
raise Exception, msg
self._legend2.colorNamePairs.append((None,ldf))
p = Pie.draw(self)
if self.drawLegend:
p.add(self.legend1)
#hide from user - keeps both sides lined up!
self._legend2.x = self.legend1.x+self.legendNumberOffset
self._legend2.y = self.legend1.y
self._legend2.deltax = self.legend1.deltax
self._legend2.deltay = self.legend1.deltay
self._legend2.dy = self.legend1.dy
self._legend2.columnMaximum = self.legend1.columnMaximum
p.add(self._legend2)
p.shift(self.leftPadding, self.bottomPadding)
return p
def _getDrawingDimensions(self):
tx = self.rightPadding
if self.drawLegend:
tx = tx+self.legend1.x+self.legendNumberOffset #self._legend2.x
tx = tx + self._legend2._calculateMaxWidth(self._legend2.colorNamePairs)
ty = self.bottomPadding+self.height+self.topPadding
return (tx,ty)
def demo(self, drawing=None):
if not drawing:
tx,ty = self._getDrawingDimensions()
drawing = Drawing(tx, ty)
drawing.add(self.draw())
return drawing
from utils3d import _getShaded, _2rad, _360, _pi_2, _2pi
class Wedge3dProperties(PropHolder):
"""This holds descriptive information about the wedges in a pie chart.
It is not to be confused with the 'wedge itself'; this just holds
a recipe for how to format one, and does not allow you to hack the
angles. It can format a genuine Wedge object for you with its
format method.
"""
_attrMap = AttrMap(
fillColor = AttrMapValue(isColorOrNone),
fillColorShaded = AttrMapValue(isColorOrNone),
fontColor = AttrMapValue(isColorOrNone),
fontName = AttrMapValue(isString),
fontSize = AttrMapValue(isNumber),
label_angle = AttrMapValue(isNumber),
label_bottomPadding = AttrMapValue(isNumber,'padding at bottom of box'),
label_boxAnchor = AttrMapValue(isBoxAnchor),
label_boxFillColor = AttrMapValue(isColorOrNone),
label_boxStrokeColor = AttrMapValue(isColorOrNone),
label_boxStrokeWidth = AttrMapValue(isNumber),
label_dx = AttrMapValue(isNumber),
label_dy = AttrMapValue(isNumber),
label_height = AttrMapValue(isNumberOrNone),
label_leading = AttrMapValue(isNumberOrNone),
label_leftPadding = AttrMapValue(isNumber,'padding at left of box'),
label_maxWidth = AttrMapValue(isNumberOrNone),
label_rightPadding = AttrMapValue(isNumber,'padding at right of box'),
label_strokeColor = AttrMapValue(isColorOrNone),
label_strokeWidth = AttrMapValue(isNumber),
label_text = AttrMapValue(isStringOrNone),
label_textAnchor = AttrMapValue(isTextAnchor),
label_topPadding = AttrMapValue(isNumber,'padding at top of box'),
label_visible = AttrMapValue(isBoolean,desc="True if the label is to be drawn"),
label_width = AttrMapValue(isNumberOrNone),
labelRadius = AttrMapValue(isNumber),
popout = AttrMapValue(isNumber),
shading = AttrMapValue(isNumber),
strokeColor = AttrMapValue(isColorOrNone),
strokeColorShaded = AttrMapValue(isColorOrNone),
strokeDashArray = AttrMapValue(isListOfNumbersOrNone),
strokeWidth = AttrMapValue(isNumber),
visible = AttrMapValue(isBoolean,'set to false to skip displaying'),
)
def __init__(self):
self.strokeWidth = 0
self.shading = 0.3
self.visible = 1
self.strokeColorShaded = self.fillColorShaded = self.fillColor = None
self.strokeColor = STATE_DEFAULTS["strokeColor"]
self.strokeDashArray = STATE_DEFAULTS["strokeDashArray"]
self.popout = 0
self.fontName = STATE_DEFAULTS["fontName"]
self.fontSize = STATE_DEFAULTS["fontSize"]
self.fontColor = STATE_DEFAULTS["fillColor"]
self.labelRadius = 1.2
self.label_dx = self.label_dy = self.label_angle = 0
self.label_text = None
self.label_topPadding = self.label_leftPadding = self.label_rightPadding = self.label_bottomPadding = 0
self.label_boxAnchor = 'c'
self.label_boxStrokeColor = None #boxStroke
self.label_boxStrokeWidth = 0.5 #boxStrokeWidth
self.label_boxFillColor = None
self.label_strokeColor = None
self.label_strokeWidth = 0.1
self.label_leading = self.label_width = self.label_maxWidth = self.label_height = None
self.label_textAnchor = 'start'
self.label_visible = 1
class _SL3D:
def __init__(self,lo,hi):
if lo<0:
lo += 360
hi += 360
self.lo = lo
self.hi = hi
self.mid = (lo+hi)*0.5
def __str__(self):
return '_SL3D(%.2f,%.2f)' % (self.lo,self.hi)
_270r = _2rad(270)
class Pie3d(Pie):
_attrMap = AttrMap(BASE=Pie,
perspective = AttrMapValue(isNumber, desc='A flattening parameter.'),
depth_3d = AttrMapValue(isNumber, desc='depth of the pie.'),
angle_3d = AttrMapValue(isNumber, desc='The view angle.'),
)
perspective = 70
depth_3d = 25
angle_3d = 180
def _popout(self,i):
return self.slices[i].popout or 0
def CX(self, i,d ):
return self._cx+(d and self._xdepth_3d or 0)+self._popout(i)*cos(_2rad(self._sl3d[i].mid))
def CY(self,i,d):
return self._cy+(d and self._ydepth_3d or 0)+self._popout(i)*sin(_2rad(self._sl3d[i].mid))
def OX(self,i,o,d):
return self.CX(i,d)+self._radiusx*cos(_2rad(o))
def OY(self,i,o,d):
return self.CY(i,d)+self._radiusy*sin(_2rad(o))
def rad_dist(self,a):
_3dva = self._3dva
return min(abs(a-_3dva),abs(a-_3dva+360))
def __init__(self):
self.x = 0
self.y = 0
self.width = 300
self.height = 200
self.data = [12.50,20.10,2.00,22.00,5.00,18.00,13.00]
self.labels = None # or list of strings
self.startAngle = 90
self.direction = "clockwise"
self.simpleLabels = 1
self.slices = TypedPropertyCollection(Wedge3dProperties)
self.slices[0].fillColor = colors.darkcyan
self.slices[1].fillColor = colors.blueviolet
self.slices[2].fillColor = colors.blue
self.slices[3].fillColor = colors.cyan
self.slices[4].fillColor = colors.azure
self.slices[5].fillColor = colors.crimson
self.slices[6].fillColor = colors.darkviolet
def _fillSide(self,L,i,angle,strokeColor,strokeWidth,fillColor):
rd = self.rad_dist(angle)
if rd<self.rad_dist(self._sl3d[i].mid):
p = [self.CX(i,0),self.CY(i,0),
self.CX(i,1),self.CY(i,1),
self.OX(i,angle,1),self.OY(i,angle,1),
self.OX(i,angle,0),self.OY(i,angle,0)]
L.append((rd,Polygon(p, strokeColor=strokeColor, fillColor=fillColor,strokeWidth=strokeWidth,strokeLineJoin=1)))
def draw(self):
slices = self.slices
_3d_angle = self.angle_3d
_3dva = self._3dva = _360(_3d_angle+90)
a0 = _2rad(_3dva)
self._xdepth_3d = cos(a0)*self.depth_3d
self._ydepth_3d = sin(a0)*self.depth_3d
self._cx = self.x+self.width/2.0
self._cy = self.y+(self.height - self._ydepth_3d)/2.0
radius = self._radius = self._cx-self.x
self._radiusx = radiusx = radius
self._radiusy = radiusy = (1.0 - self.perspective/100.0)*radius
data = self.normalizeData()
sum = self._sum
CX = self.CX
CY = self.CY
OX = self.OX
OY = self.OY
rad_dist = self.rad_dist
_fillSide = self._fillSide
n = len(data)
_sl3d = self._sl3d = []
g = Group()
last = _360(self.startAngle)
a0 = self.direction=='clockwise' and -1 or 1
for v in data:
v *= a0
angle1, angle0 = last, v+last
last = angle0
if a0>0: angle0, angle1 = angle1, angle0
_sl3d.append(_SL3D(angle0,angle1))
#print '%d: %.2f %.2f --> %s' %(len(_sl3d)-1,angle0,angle1,_sl3d[-1])
labels = _fixLabels(self.labels,n)
a0 = _3d_angle
a1 = _3d_angle+180
T = []
S = []
L = []
class WedgeLabel3d(WedgeLabel):
def _checkDXY(self,ba):
if ba[0]=='n':
if not hasattr(self,'_ody'):
self._ody = self.dy
self.dy = -self._ody + self._ydepth_3d
WedgeLabel3d._ydepth_3d = self._ydepth_3d
for i in xrange(n):
style = slices[i]
if not style.visible: continue
sl = _sl3d[i]
lo = angle0 = sl.lo
hi = angle1 = sl.hi
if abs(hi-lo)<=1e-7: continue
fillColor = _getShaded(style.fillColor,style.fillColorShaded,style.shading)
strokeColor = _getShaded(style.strokeColor,style.strokeColorShaded,style.shading) or fillColor
strokeWidth = style.strokeWidth
cx0 = CX(i,0)
cy0 = CY(i,0)
cx1 = CX(i,1)
cy1 = CY(i,1)
#background shaded pie bottom
g.add(Wedge(cx1,cy1,radiusx, lo, hi,yradius=radiusy,
strokeColor=strokeColor,strokeWidth=strokeWidth,fillColor=fillColor,
strokeLineJoin=1))
#connect to top
if lo < a0 < hi: angle0 = a0
if lo < a1 < hi: angle1 = a1
if 1:
p = ArcPath(strokeColor=strokeColor, fillColor=fillColor,strokeWidth=strokeWidth,strokeLineJoin=1)
p.addArc(cx1,cy1,radiusx,angle0,angle1,yradius=radiusy,moveTo=1)
p.lineTo(OX(i,angle1,0),OY(i,angle1,0))
p.addArc(cx0,cy0,radiusx,angle0,angle1,yradius=radiusy,reverse=1)
p.closePath()
if angle0<=_3dva and angle1>=_3dva:
rd = 0
else:
rd = min(rad_dist(angle0),rad_dist(angle1))
S.append((rd,p))
_fillSide(S,i,lo,strokeColor,strokeWidth,fillColor)
_fillSide(S,i,hi,strokeColor,strokeWidth,fillColor)
#bright shaded top
fillColor = style.fillColor
strokeColor = style.strokeColor or fillColor
T.append(Wedge(cx0,cy0,radiusx,lo,hi,yradius=radiusy,
strokeColor=strokeColor,strokeWidth=strokeWidth,fillColor=fillColor,strokeLineJoin=1))
text = labels[i]
if text:
rat = style.labelRadius
self._radiusx *= rat
self._radiusy *= rat
mid = sl.mid
_addWedgeLabel(self,text,L.append,mid,OX(i,mid,0),OY(i,mid,0),style,labelClass=WedgeLabel3d)
self._radiusx = radiusx
self._radiusy = radiusy
S.sort(lambda a,b: -cmp(a[0],b[0]))
map(g.add,map(lambda x:x[1],S)+T+L)
return g
def demo(self):
d = Drawing(200, 100)
pc = Pie()
pc.x = 50
pc.y = 10
pc.width = 100
pc.height = 80
pc.data = [10,20,30,40,50,60]
pc.labels = ['a','b','c','d','e','f']
pc.slices.strokeWidth=0.5
pc.slices[3].popout = 10
pc.slices[3].strokeWidth = 2
pc.slices[3].strokeDashArray = [2,2]
pc.slices[3].labelRadius = 1.75
pc.slices[3].fontColor = colors.red
pc.slices[0].fillColor = colors.darkcyan
pc.slices[1].fillColor = colors.blueviolet
pc.slices[2].fillColor = colors.blue
pc.slices[3].fillColor = colors.cyan
pc.slices[4].fillColor = colors.aquamarine
pc.slices[5].fillColor = colors.cadetblue
pc.slices[6].fillColor = colors.lightcoral
self.slices[1].visible = 0
self.slices[3].visible = 1
self.slices[4].visible = 1
self.slices[5].visible = 1
self.slices[6].visible = 0
d.add(pc)
return d
def sample0a():
"Make a degenerated pie chart with only one slice."
d = Drawing(400, 200)
pc = Pie()
pc.x = 150
pc.y = 50
pc.data = [10]
pc.labels = ['a']
pc.slices.strokeWidth=1#0.5
d.add(pc)
return d
def sample0b():
"Make a degenerated pie chart with only one slice."
d = Drawing(400, 200)
pc = Pie()
pc.x = 150
pc.y = 50
pc.width = 120
pc.height = 100
pc.data = [10]
pc.labels = ['a']
pc.slices.strokeWidth=1#0.5
d.add(pc)
return d
def sample1():
"Make a typical pie chart with with one slice treated in a special way."
d = Drawing(400, 200)
pc = Pie()
pc.x = 150
pc.y = 50
pc.data = [10, 20, 30, 40, 50, 60]
pc.labels = ['a', 'b', 'c', 'd', 'e', 'f']
pc.slices.strokeWidth=1#0.5
pc.slices[3].popout = 20
pc.slices[3].strokeWidth = 2
pc.slices[3].strokeDashArray = [2,2]
pc.slices[3].labelRadius = 1.75
pc.slices[3].fontColor = colors.red
d.add(pc)
return d
def sample2():
"Make a pie chart with nine slices."
d = Drawing(400, 200)
pc = Pie()
pc.x = 125
pc.y = 25
pc.data = [0.31, 0.148, 0.108,
0.076, 0.033, 0.03,
0.019, 0.126, 0.15]
pc.labels = ['1', '2', '3', '4', '5', '6', '7', '8', 'X']
pc.width = 150
pc.height = 150
pc.slices.strokeWidth=1#0.5
pc.slices[0].fillColor = colors.steelblue
pc.slices[1].fillColor = colors.thistle
pc.slices[2].fillColor = colors.cornflower
pc.slices[3].fillColor = colors.lightsteelblue
pc.slices[4].fillColor = colors.aquamarine
pc.slices[5].fillColor = colors.cadetblue
pc.slices[6].fillColor = colors.lightcoral
pc.slices[7].fillColor = colors.tan
pc.slices[8].fillColor = colors.darkseagreen
d.add(pc)
return d
def sample3():
"Make a pie chart with a very slim slice."
d = Drawing(400, 200)
pc = Pie()
pc.x = 125
pc.y = 25
pc.data = [74, 1, 25]
pc.width = 150
pc.height = 150
pc.slices.strokeWidth=1#0.5
pc.slices[0].fillColor = colors.steelblue
pc.slices[1].fillColor = colors.thistle
pc.slices[2].fillColor = colors.cornflower
d.add(pc)
return d
def sample4():
"Make a pie chart with several very slim slices."
d = Drawing(400, 200)
pc = Pie()
pc.x = 125
pc.y = 25
pc.data = [74, 1, 1, 1, 1, 22]
pc.width = 150
pc.height = 150
pc.slices.strokeWidth=1#0.5
pc.slices[0].fillColor = colors.steelblue
pc.slices[1].fillColor = colors.thistle
pc.slices[2].fillColor = colors.cornflower
pc.slices[3].fillColor = colors.lightsteelblue
pc.slices[4].fillColor = colors.aquamarine
pc.slices[5].fillColor = colors.cadetblue
d.add(pc)
return d

View File

@ -1,186 +0,0 @@
from reportlab.lib.colors import Color, white, black
from reportlab.graphics.charts.textlabels import Label
from reportlab.graphics.shapes import Polygon, Line, Circle, String, Drawing, PolyLine, Group, Rect
from reportlab.graphics.widgetbase import Widget, TypedPropertyCollection
from reportlab.lib.attrmap import *
from reportlab.lib.validators import *
from reportlab.lib.units import cm
from reportlab.pdfbase.pdfmetrics import stringWidth, getFont
from reportlab.graphics.widgets.grids import ShadedRect, Grid
class SlideBox(Widget):
"""Returns a slidebox widget"""
_attrMap = AttrMap(
labelFontName = AttrMapValue(isString, desc="Name of font used for the labels"),
labelFontSize = AttrMapValue(isNumber, desc="Size of font used for the labels"),
labelStrokeColor = AttrMapValue(isColorOrNone, desc="Colour for for number outlines"),
labelFillColor = AttrMapValue(isColorOrNone, desc="Colour for number insides"),
startColor = AttrMapValue(isColor, desc='Color of first box'),
endColor = AttrMapValue(isColor, desc='Color of last box'),
numberOfBoxes = AttrMapValue(isInt, desc='How many boxes there are'),
trianglePosition = AttrMapValue(isInt, desc='Which box is highlighted by the triangles'),
triangleHeight = AttrMapValue(isNumber, desc="Height of indicator triangles"),
triangleWidth = AttrMapValue(isNumber, desc="Width of indicator triangles"),
triangleFillColor = AttrMapValue(isColor, desc="Colour of indicator triangles"),
triangleStrokeColor = AttrMapValue(isColorOrNone, desc="Colour of indicator triangle outline"),
triangleStrokeWidth = AttrMapValue(isNumber, desc="Colour of indicator triangle outline"),
boxHeight = AttrMapValue(isNumber, desc="Height of the boxes"),
boxWidth = AttrMapValue(isNumber, desc="Width of the boxes"),
boxSpacing = AttrMapValue(isNumber, desc="Space between the boxes"),
boxOutlineColor = AttrMapValue(isColorOrNone, desc="Colour used to outline the boxes (if any)"),
boxOutlineWidth = AttrMapValue(isNumberOrNone, desc="Width of the box outline (if any)"),
leftPadding = AttrMapValue(isNumber, desc='Padding on left of drawing'),
rightPadding = AttrMapValue(isNumber, desc='Padding on right of drawing'),
topPadding = AttrMapValue(isNumber, desc='Padding at top of drawing'),
bottomPadding = AttrMapValue(isNumber, desc='Padding at bottom of drawing'),
background = AttrMapValue(isColorOrNone, desc='Colour of the background to the drawing (if any)'),
sourceLabelText = AttrMapValue(isNoneOrString, desc="Text used for the 'source' label (can be empty)"),
sourceLabelOffset = AttrMapValue(isNumber, desc='Padding at bottom of drawing'),
sourceLabelFontName = AttrMapValue(isString, desc="Name of font used for the 'source' label"),
sourceLabelFontSize = AttrMapValue(isNumber, desc="Font size for the 'source' label"),
sourceLabelFillColor = AttrMapValue(isColorOrNone, desc="Colour ink for the 'source' label (bottom right)"),
)
def __init__(self):
self.labelFontName = "Helvetica-Bold"
self.labelFontSize = 10
self.labelStrokeColor = black
self.labelFillColor = white
self.startColor = colors.Color(232/255.0,224/255.0,119/255.0)
self.endColor = colors.Color(25/255.0,77/255.0,135/255.0)
self.numberOfBoxes = 7
self.trianglePosition = 7
self.triangleHeight = 0.12*cm
self.triangleWidth = 0.38*cm
self.triangleFillColor = white
self.triangleStrokeColor = black
self.triangleStrokeWidth = 0.58
self.boxHeight = 0.55*cm
self.boxWidth = 0.73*cm
self.boxSpacing = 0.075*cm
self.boxOutlineColor = black
self.boxOutlineWidth = 0.58
self.leftPadding=5
self.rightPadding=5
self.topPadding=5
self.bottomPadding=5
self.background=None
self.sourceLabelText = "Source: ReportLab"
self.sourceLabelOffset = 0.2*cm
self.sourceLabelFontName = "Helvetica-Oblique"
self.sourceLabelFontSize = 6
self.sourceLabelFillColor = black
def _getDrawingDimensions(self):
tx=(self.numberOfBoxes*self.boxWidth)
if self.numberOfBoxes>1: tx=tx+((self.numberOfBoxes-1)*self.boxSpacing)
tx=tx+self.leftPadding+self.rightPadding
ty=self.boxHeight+self.triangleHeight
ty=ty+self.topPadding+self.bottomPadding+self.sourceLabelOffset+self.sourceLabelFontSize
return (tx,ty)
def _getColors(self):
# for calculating intermediate colors...
numShades = self.numberOfBoxes+1
fillColorStart = self.startColor
fillColorEnd = self.endColor
colorsList =[]
for i in range(0,numShades):
colorsList.append(colors.linearlyInterpolatedColor(fillColorStart, fillColorEnd, 0, numShades-1, i))
return colorsList
def demo(self,drawing=None):
from reportlab.lib import colors
if not drawing:
tx,ty=self._getDrawingDimensions()
drawing = Drawing(tx,ty)
drawing.add(self.draw())
return drawing
def draw(self):
g = Group()
ys = self.bottomPadding+(self.triangleHeight/2)+self.sourceLabelOffset+self.sourceLabelFontSize
if self.background:
x,y = self._getDrawingDimensions()
g.add(Rect(-self.leftPadding,-ys,x,y,
strokeColor=None,
strokeWidth=0,
fillColor=self.background))
ascent=getFont(self.labelFontName).face.ascent/1000.
if ascent==0: ascent=0.718 # default (from helvetica)
ascent=ascent*self.labelFontSize # normalize
colorsList = self._getColors()
# Draw the boxes - now uses ShadedRect from grids
x=0
for f in range (0,self.numberOfBoxes):
sr=ShadedRect()
sr.x=x
sr.y=0
sr.width=self.boxWidth
sr.height=self.boxHeight
sr.orientation = 'vertical'
sr.numShades = 30
sr.fillColorStart = colorsList[f]
sr.fillColorEnd = colorsList[f+1]
sr.strokeColor = None
sr.strokeWidth = 0
g.add(sr)
g.add(Rect(x,0,self.boxWidth,self.boxHeight,
strokeColor=self.boxOutlineColor,
strokeWidth=self.boxOutlineWidth,
fillColor=None))
g.add(String(x+self.boxWidth/2.,(self.boxHeight-ascent)/2.,
text = str(f+1),
fillColor = self.labelFillColor,
strokeColor=self.labelStrokeColor,
textAnchor = 'middle',
fontName = self.labelFontName,
fontSize = self.labelFontSize))
x=x+self.boxWidth+self.boxSpacing
#do triangles
xt = (self.trianglePosition*self.boxWidth)
if self.trianglePosition>1:
xt = xt+(self.trianglePosition-1)*self.boxSpacing
xt = xt-(self.boxWidth/2)
g.add(Polygon(
strokeColor = self.triangleStrokeColor,
strokeWidth = self.triangleStrokeWidth,
fillColor = self.triangleFillColor,
points=[xt,self.boxHeight-(self.triangleHeight/2),
xt-(self.triangleWidth/2),self.boxHeight+(self.triangleHeight/2),
xt+(self.triangleWidth/2),self.boxHeight+(self.triangleHeight/2),
xt,self.boxHeight-(self.triangleHeight/2)]))
g.add(Polygon(
strokeColor = self.triangleStrokeColor,
strokeWidth = self.triangleStrokeWidth,
fillColor = self.triangleFillColor,
points=[xt,0+(self.triangleHeight/2),
xt-(self.triangleWidth/2),0-(self.triangleHeight/2),
xt+(self.triangleWidth/2),0-(self.triangleHeight/2),
xt,0+(self.triangleHeight/2)]))
#source label
if self.sourceLabelText != None:
g.add(String(x-self.boxSpacing,0-(self.triangleHeight/2)-self.sourceLabelOffset-(self.sourceLabelFontSize),
text = self.sourceLabelText,
fillColor = self.sourceLabelFillColor,
textAnchor = 'end',
fontName = self.sourceLabelFontName,
fontSize = self.sourceLabelFontSize))
g.shift(self.leftPadding, ys)
return g
if __name__ == "__main__":
d = SlideBox()
d.demo().save(fnRoot="slidebox")

View File

@ -1,353 +0,0 @@
#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/graphics/charts/spider.py
# spider chart, also known as radar chart
"""Spider Chart
Normal use shows variation of 5-10 parameters against some 'norm' or target.
When there is more than one series, place the series with the largest
numbers first, as it will be overdrawn by each successive one.
"""
__version__=''' $Id$ '''
import copy
from math import sin, cos, pi
from reportlab.lib import colors
from reportlab.lib.validators import isColor, isNumber, isListOfNumbersOrNone,\
isListOfNumbers, isColorOrNone, isString,\
isListOfStringsOrNone, OneOf, SequenceOf,\
isBoolean, isListOfColors, isNumberOrNone,\
isNoneOrListOfNoneOrStrings, isTextAnchor,\
isNoneOrListOfNoneOrNumbers, isBoxAnchor,\
isStringOrNone
from reportlab.lib.attrmap import *
from reportlab.pdfgen.canvas import Canvas
from reportlab.graphics.shapes import Group, Drawing, Line, Rect, Polygon, Ellipse, \
Wedge, String, STATE_DEFAULTS
from reportlab.graphics.widgetbase import Widget, TypedPropertyCollection, PropHolder
from reportlab.graphics.charts.areas import PlotArea
from piecharts import WedgeLabel
from reportlab.graphics.widgets.markers import makeMarker, uSymbol2Symbol
class StrandProperties(PropHolder):
"""This holds descriptive information about concentric 'strands'.
Line style, whether filled etc.
"""
_attrMap = AttrMap(
strokeWidth = AttrMapValue(isNumber),
fillColor = AttrMapValue(isColorOrNone),
strokeColor = AttrMapValue(isColorOrNone),
strokeDashArray = AttrMapValue(isListOfNumbersOrNone),
fontName = AttrMapValue(isString),
fontSize = AttrMapValue(isNumber),
fontColor = AttrMapValue(isColorOrNone),
labelRadius = AttrMapValue(isNumber),
markers = AttrMapValue(isBoolean),
markerType = AttrMapValue(isAnything),
markerSize = AttrMapValue(isNumber),
label_dx = AttrMapValue(isNumber),
label_dy = AttrMapValue(isNumber),
label_angle = AttrMapValue(isNumber),
label_boxAnchor = AttrMapValue(isBoxAnchor),
label_boxStrokeColor = AttrMapValue(isColorOrNone),
label_boxStrokeWidth = AttrMapValue(isNumber),
label_boxFillColor = AttrMapValue(isColorOrNone),
label_strokeColor = AttrMapValue(isColorOrNone),
label_strokeWidth = AttrMapValue(isNumber),
label_text = AttrMapValue(isStringOrNone),
label_leading = AttrMapValue(isNumberOrNone),
label_width = AttrMapValue(isNumberOrNone),
label_maxWidth = AttrMapValue(isNumberOrNone),
label_height = AttrMapValue(isNumberOrNone),
label_textAnchor = AttrMapValue(isTextAnchor),
label_visible = AttrMapValue(isBoolean,desc="True if the label is to be drawn"),
label_topPadding = AttrMapValue(isNumber,'padding at top of box'),
label_leftPadding = AttrMapValue(isNumber,'padding at left of box'),
label_rightPadding = AttrMapValue(isNumber,'padding at right of box'),
label_bottomPadding = AttrMapValue(isNumber,'padding at bottom of box'),
)
def __init__(self):
self.strokeWidth = 0
self.fillColor = None
self.strokeColor = STATE_DEFAULTS["strokeColor"]
self.strokeDashArray = STATE_DEFAULTS["strokeDashArray"]
self.fontName = STATE_DEFAULTS["fontName"]
self.fontSize = STATE_DEFAULTS["fontSize"]
self.fontColor = STATE_DEFAULTS["fillColor"]
self.labelRadius = 1.2
self.markers = 0
self.markerType = None
self.markerSize = 0
self.label_dx = self.label_dy = self.label_angle = 0
self.label_text = None
self.label_topPadding = self.label_leftPadding = self.label_rightPadding = self.label_bottomPadding = 0
self.label_boxAnchor = 'c'
self.label_boxStrokeColor = None #boxStroke
self.label_boxStrokeWidth = 0.5 #boxStrokeWidth
self.label_boxFillColor = None
self.label_strokeColor = None
self.label_strokeWidth = 0.1
self.label_leading = self.label_width = self.label_maxWidth = self.label_height = None
self.label_textAnchor = 'start'
self.label_visible = 1
class SpiderChart(PlotArea):
_attrMap = AttrMap(BASE=PlotArea,
data = AttrMapValue(None, desc='Data to be plotted, list of (lists of) numbers.'),
labels = AttrMapValue(isListOfStringsOrNone, desc="optional list of labels to use for each data point"),
startAngle = AttrMapValue(isNumber, desc="angle of first slice; like the compass, 0 is due North"),
direction = AttrMapValue( OneOf('clockwise', 'anticlockwise'), desc="'clockwise' or 'anticlockwise'"),
strands = AttrMapValue(None, desc="collection of strand descriptor objects"),
)
def __init__(self):
PlotArea.__init__(self)
self.data = [[10,12,14,16,14,12], [6,8,10,12,9,11]]
self.labels = None # or list of strings
self.startAngle = 90
self.direction = "clockwise"
self.strands = TypedPropertyCollection(StrandProperties)
self.strands[0].fillColor = colors.cornsilk
self.strands[1].fillColor = colors.cyan
def demo(self):
d = Drawing(200, 100)
sp = SpiderChart()
sp.x = 50
sp.y = 10
sp.width = 100
sp.height = 80
sp.data = [[10,12,14,16,18,20],[6,8,4,6,8,10]]
sp.labels = ['a','b','c','d','e','f']
d.add(sp)
return d
def normalizeData(self, outer = 0.0):
"""Turns data into normalized ones where each datum is < 1.0,
and 1.0 = maximum radius. Adds 10% at outside edge by default"""
data = self.data
theMax = 0.0
for row in data:
for element in row:
assert element >=0, "Cannot do spider plots of negative numbers!"
if element > theMax:
theMax = element
theMax = theMax * (1.0+outer)
scaled = []
for row in data:
scaledRow = []
for element in row:
scaledRow.append(element / theMax)
scaled.append(scaledRow)
return scaled
def draw(self):
# normalize slice data
g = self.makeBackground() or Group()
xradius = self.width/2.0
yradius = self.height/2.0
self._radius = radius = min(xradius, yradius)
centerx = self.x + xradius
centery = self.y + yradius
data = self.normalizeData()
n = len(data[0])
#labels
if self.labels is None:
labels = [''] * n
else:
labels = self.labels
#there's no point in raising errors for less than enough errors if
#we silently create all for the extreme case of no labels.
i = n-len(labels)
if i>0:
labels = labels + ['']*i
spokes = []
csa = []
angle = self.startAngle*pi/180
direction = self.direction == "clockwise" and -1 or 1
angleBetween = direction*(2 * pi)/n
markers = self.strands.markers
for i in xrange(n):
car = cos(angle)*radius
sar = sin(angle)*radius
csa.append((car,sar,angle))
spoke = Line(centerx, centery, centerx + car, centery + sar, strokeWidth = 0.5)
#print 'added spoke (%0.2f, %0.2f) -> (%0.2f, %0.2f)' % (spoke.x1, spoke.y1, spoke.x2, spoke.y2)
spokes.append(spoke)
if labels:
si = self.strands[i]
text = si.label_text
if text is None: text = labels[i]
if text:
labelRadius = si.labelRadius
L = WedgeLabel()
L.x = centerx + labelRadius*car
L.y = centery + labelRadius*sar
L.boxAnchor = si.label_boxAnchor
L._pmv = angle*180/pi
L.dx = si.label_dx
L.dy = si.label_dy
L.angle = si.label_angle
L.boxAnchor = si.label_boxAnchor
L.boxStrokeColor = si.label_boxStrokeColor
L.boxStrokeWidth = si.label_boxStrokeWidth
L.boxFillColor = si.label_boxFillColor
L.strokeColor = si.label_strokeColor
L.strokeWidth = si.label_strokeWidth
L._text = text
L.leading = si.label_leading
L.width = si.label_width
L.maxWidth = si.label_maxWidth
L.height = si.label_height
L.textAnchor = si.label_textAnchor
L.visible = si.label_visible
L.topPadding = si.label_topPadding
L.leftPadding = si.label_leftPadding
L.rightPadding = si.label_rightPadding
L.bottomPadding = si.label_bottomPadding
L.fontName = si.fontName
L.fontSize = si.fontSize
L.fillColor = si.fontColor
spokes.append(L)
angle = angle + angleBetween
# now plot the polygons
rowIdx = 0
for row in data:
# series plot
points = []
car, sar = csa[-1][:2]
r = row[-1]
points.append(centerx+car*r)
points.append(centery+sar*r)
for i in xrange(n):
car, sar = csa[i][:2]
r = row[i]
points.append(centerx+car*r)
points.append(centery+sar*r)
# make up the 'strand'
strand = Polygon(points)
strand.fillColor = self.strands[rowIdx].fillColor
strand.strokeColor = self.strands[rowIdx].strokeColor
strand.strokeWidth = self.strands[rowIdx].strokeWidth
strand.strokeDashArray = self.strands[rowIdx].strokeDashArray
g.add(strand)
# put in a marker, if it needs one
if markers:
if hasattr(self.strands[rowIdx], 'markerType'):
uSymbol = self.strands[rowIdx].markerType
elif hasattr(self.strands, 'markerType'):
uSymbol = self.strands.markerType
else:
uSymbol = None
m_x = centerx+car*r
m_y = centery+sar*r
m_size = self.strands[rowIdx].markerSize
m_fillColor = self.strands[rowIdx].fillColor
m_strokeColor = self.strands[rowIdx].strokeColor
m_strokeWidth = self.strands[rowIdx].strokeWidth
m_angle = 0
if type(uSymbol) is type(''):
symbol = makeMarker(uSymbol,
size = m_size,
x = m_x,
y = m_y,
fillColor = m_fillColor,
strokeColor = m_strokeColor,
strokeWidth = m_strokeWidth,
angle = m_angle,
)
else:
symbol = uSymbol2Symbol(uSymbol,m_x,m_y,m_fillColor)
for k,v in (('size', m_size), ('fillColor', m_fillColor),
('x', m_x), ('y', m_y),
('strokeColor',m_strokeColor), ('strokeWidth',m_strokeWidth),
('angle',m_angle),):
try:
setattr(uSymbol,k,v)
except:
pass
g.add(symbol)
rowIdx = rowIdx + 1
# spokes go over strands
for spoke in spokes:
g.add(spoke)
return g
def sample1():
"Make a simple spider chart"
d = Drawing(400, 400)
pc = SpiderChart()
pc.x = 50
pc.y = 50
pc.width = 300
pc.height = 300
pc.data = [[10,12,14,16,14,12], [6,8,10,12,9,15],[7,8,17,4,12,8,3]]
pc.labels = ['a','b','c','d','e','f']
pc.strands[2].fillColor=colors.palegreen
d.add(pc)
return d
def sample2():
"Make a spider chart with markers, but no fill"
d = Drawing(400, 400)
pc = SpiderChart()
pc.x = 50
pc.y = 50
pc.width = 300
pc.height = 300
pc.data = [[10,12,14,16,14,12], [6,8,10,12,9,15],[7,8,17,4,12,8,3]]
pc.labels = ['U','V','W','X','Y','Z']
pc.strands.strokeWidth = 2
pc.strands[0].fillColor = None
pc.strands[1].fillColor = None
pc.strands[2].fillColor = None
pc.strands[0].strokeColor = colors.red
pc.strands[1].strokeColor = colors.blue
pc.strands[2].strokeColor = colors.green
pc.strands.markers = 1
pc.strands.markerType = "FilledDiamond"
pc.strands.markerSize = 6
d.add(pc)
return d
if __name__=='__main__':
d = sample1()
from reportlab.graphics.renderPDF import drawToFile
drawToFile(d, 'spider.pdf')
d = sample2()
drawToFile(d, 'spider2.pdf')
#print 'saved spider.pdf'

View File

@ -1,440 +0,0 @@
#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/graphics/charts/textlabels.py
__version__=''' $Id$ '''
import string
from reportlab.lib import colors
from reportlab.lib.validators import isNumber, isNumberOrNone, OneOf, isColorOrNone, isString, \
isTextAnchor, isBoxAnchor, isBoolean, NoneOr, isInstanceOf, isNoneOrString
from reportlab.lib.attrmap import *
from reportlab.pdfbase.pdfmetrics import stringWidth
from reportlab.graphics.shapes import Drawing, Group, Circle, Rect, String, STATE_DEFAULTS
from reportlab.graphics.shapes import _PATH_OP_ARG_COUNT, _PATH_OP_NAMES, definePath
from reportlab.graphics.widgetbase import Widget, PropHolder
_gs = None
_A2BA= {
'x': {0:'n', 45:'ne', 90:'e', 135:'se', 180:'s', 225:'sw', 270:'w', 315: 'nw', -45: 'nw'},
'y': {0:'e', 45:'se', 90:'s', 135:'sw', 180:'w', 225:'nw', 270:'n', 315: 'ne', -45: 'ne'},
}
def _simpleSplit(txt,mW,SW):
L = []
ws = SW(' ')
O = []
w = -ws
for t in string.split(txt):
lt = SW(t)
if w+ws+lt<=mW or O==[]:
O.append(t)
w = w + ws + lt
else:
L.append(string.join(O,' '))
O = [t]
w = lt
if O!=[]: L.append(string.join(O,' '))
return L
def _pathNumTrunc(n):
if int(n)==n: return int(n)
return round(n,5)
def _processGlyph(G, truncate=1, pathReverse=0):
O = []
P = []
R = []
for g in G+(('end',),):
op = g[0]
if O and op in ['moveTo', 'moveToClosed','end']:
if O[0]=='moveToClosed':
O = O[1:]
if pathReverse:
for i in xrange(0,len(P),2):
P[i+1], P[i] = P[i:i+2]
P.reverse()
O.reverse()
O.insert(0,'moveTo')
O.append('closePath')
i = 0
if truncate: P = map(_pathNumTrunc,P)
for o in O:
j = i + _PATH_OP_ARG_COUNT[_PATH_OP_NAMES.index(o)]
if o=='closePath':
R.append(o)
else:
R.append((o,)+ tuple(P[i:j]))
i = j
O = []
P = []
O.append(op)
P.extend(g[1:])
return R
def _text2PathDescription(text, x=0, y=0, fontName='Times-Roman', fontSize=1000,
anchor='start', truncate=1, pathReverse=0):
global _gs
if not _gs:
import _renderPM
_gs = _renderPM.gstate(1,1)
from reportlab.graphics import renderPM
renderPM._setFont(_gs,fontName,fontSize)
P = []
if not anchor =='start':
textLen = stringWidth(text, fontName,fontSize)
if text_anchor=='end':
x = x-textLen
elif text_anchor=='middle':
x = x - textLen/2.
for g in _gs._stringPath(text,x,y):
P.extend(_processGlyph(g,truncate=truncate,pathReverse=pathReverse))
return P
def _text2Path(text, x=0, y=0, fontName='Times-Roman', fontSize=1000,
anchor='start', truncate=1, pathReverse=0):
return definePath(_text2PathDescription(text,x=x,y=y,fontName=fontName,
fontSize=fontSize,anchor=anchor,truncate=truncate,pathReverse=pathReverse))
_BA2TA={'w':'start','nw':'start','sw':'start','e':'end', 'ne': 'end', 'se':'end', 'n':'middle','s':'middle','c':'middle'}
class Label(Widget):
"""A text label to attach to something else, such as a chart axis.
This allows you to specify an offset, angle and many anchor
properties relative to the label's origin. It allows, for example,
angled multiline axis labels.
"""
# fairly straight port of Robin Becker's textbox.py to new widgets
# framework.
_attrMap = AttrMap(
x = AttrMapValue(isNumber),
y = AttrMapValue(isNumber),
dx = AttrMapValue(isNumber),
dy = AttrMapValue(isNumber),
angle = AttrMapValue(isNumber),
boxAnchor = AttrMapValue(isBoxAnchor),
boxStrokeColor = AttrMapValue(isColorOrNone),
boxStrokeWidth = AttrMapValue(isNumber),
boxFillColor = AttrMapValue(isColorOrNone),
fillColor = AttrMapValue(isColorOrNone),
strokeColor = AttrMapValue(isColorOrNone),
strokeWidth = AttrMapValue(isNumber),
text = AttrMapValue(isString),
fontName = AttrMapValue(isString),
fontSize = AttrMapValue(isNumber),
leading = AttrMapValue(isNumberOrNone),
width = AttrMapValue(isNumberOrNone),
maxWidth = AttrMapValue(isNumberOrNone),
height = AttrMapValue(isNumberOrNone),
textAnchor = AttrMapValue(isTextAnchor),
visible = AttrMapValue(isBoolean,desc="True if the label is to be drawn"),
topPadding = AttrMapValue(isNumber,'padding at top of box'),
leftPadding = AttrMapValue(isNumber,'padding at left of box'),
rightPadding = AttrMapValue(isNumber,'padding at right of box'),
bottomPadding = AttrMapValue(isNumber,'padding at bottom of box'),
)
def __init__(self,**kw):
self._setKeywords(**kw)
self._setKeywords(
_text = 'Multi-Line\nString',
boxAnchor = 'c',
angle = 0,
x = 0,
y = 0,
dx = 0,
dy = 0,
topPadding = 0,
leftPadding = 0,
rightPadding = 0,
bottomPadding = 0,
boxStrokeWidth = 0.5,
boxStrokeColor = None,
strokeColor = None,
boxFillColor = None,
leading = None,
width = None,
maxWidth = None,
height = None,
fillColor = STATE_DEFAULTS['fillColor'],
fontName = STATE_DEFAULTS['fontName'],
fontSize = STATE_DEFAULTS['fontSize'],
strokeWidth = 0.1,
textAnchor = 'start',
visible = 1,
)
def setText(self, text):
"""Set the text property. May contain embedded newline characters.
Called by the containing chart or axis."""
self._text = text
def setOrigin(self, x, y):
"""Set the origin. This would be the tick mark or bar top relative to
which it is defined. Called by the containing chart or axis."""
self.x = x
self.y = y
def demo(self):
"""This shows a label positioned with its top right corner
at the top centre of the drawing, and rotated 45 degrees."""
d = Drawing(200, 100)
# mark the origin of the label
d.add(Circle(100,90, 5, fillColor=colors.green))
lab = Label()
lab.setOrigin(100,90)
lab.boxAnchor = 'ne'
lab.angle = 45
lab.dx = 0
lab.dy = -20
lab.boxStrokeColor = colors.green
lab.setText('Another\nMulti-Line\nString')
d.add(lab)
return d
def _getBoxAnchor(self):
'''hook for allowing special box anchor effects'''
ba = self.boxAnchor
if ba in ('autox', 'autoy'):
angle = self.angle
na = (int((angle%360)/45.)*45)%360
if not (na % 90): # we have a right angle case
da = (angle - na) % 360
if abs(da)>5:
na = na + (da>0 and 45 or -45)
ba = _A2BA[ba[-1]][na]
return ba
def computeSize(self):
# the thing will draw in its own coordinate system
self._lines = string.split(self._text, '\n')
self._lineWidths = []
topPadding = self.topPadding
leftPadding = self.leftPadding
rightPadding = self.rightPadding
bottomPadding = self.bottomPadding
SW = lambda text, fN=self.fontName, fS=self.fontSize: stringWidth(text, fN, fS)
if self.maxWidth:
L = []
for l in self._lines:
L[-1:-1] = _simpleSplit(l,self.maxWidth,SW)
self._lines = L
if not self.width:
w = 0
for line in self._lines:
thisWidth = SW(line)
self._lineWidths.append(thisWidth)
w = max(w,thisWidth)
self._width = w+leftPadding+rightPadding
else:
self._width = self.width
self._height = self.height or ((self.leading or 1.2*self.fontSize) * len(self._lines)+topPadding+bottomPadding)
self._ewidth = (self._width-leftPadding-rightPadding)
self._eheight = (self._height-topPadding-bottomPadding)
boxAnchor = self._getBoxAnchor()
if boxAnchor in ['n','ne','nw']:
self._top = -topPadding
elif boxAnchor in ['s','sw','se']:
self._top = self._height-topPadding
else:
self._top = 0.5*self._eheight
self._bottom = self._top - self._eheight
if boxAnchor in ['ne','e','se']:
self._left = leftPadding - self._width
elif boxAnchor in ['nw','w','sw']:
self._left = leftPadding
else:
self._left = -self._ewidth*0.5
self._right = self._left+self._ewidth
def _getTextAnchor(self):
'''This can be overridden to allow special effects'''
ta = self.textAnchor
if ta=='boxauto': ta = _BA2TA[self._getBoxAnchor()]
return ta
def draw(self):
_text = self._text
self._text = _text or ''
self.computeSize()
self._text = _text
g = Group()
g.translate(self.x + self.dx, self.y + self.dy)
g.rotate(self.angle)
y = self._top - self.fontSize
textAnchor = self._getTextAnchor()
if textAnchor == 'start':
x = self._left
elif textAnchor == 'middle':
x = self._left + self._ewidth*0.5
else:
x = self._right
# paint box behind text just in case they
# fill it
if self.boxFillColor or (self.boxStrokeColor and self.boxStrokeWidth):
g.add(Rect( self._left-self.leftPadding,
self._bottom-self.bottomPadding,
self._width,
self._height,
strokeColor=self.boxStrokeColor,
strokeWidth=self.boxStrokeWidth,
fillColor=self.boxFillColor)
)
fillColor, fontName, fontSize = self.fillColor, self.fontName, self.fontSize
strokeColor, strokeWidth, leading = self.strokeColor, self.strokeWidth, (self.leading or 1.2*fontSize)
if strokeColor:
for line in self._lines:
s = _text2Path(line, x, y, fontName, fontSize, textAnchor)
s.fillColor = fillColor
s.strokeColor = strokeColor
s.strokeWidth = strokeWidth
g.add(s)
y = y - leading
else:
for line in self._lines:
s = String(x, y, line)
s.textAnchor = textAnchor
s.fontName = fontName
s.fontSize = fontSize
s.fillColor = fillColor
g.add(s)
y = y - leading
return g
class LabelDecorator:
_attrMap = AttrMap(
x = AttrMapValue(isNumberOrNone),
y = AttrMapValue(isNumberOrNone),
dx = AttrMapValue(isNumberOrNone),
dy = AttrMapValue(isNumberOrNone),
angle = AttrMapValue(isNumberOrNone),
boxAnchor = AttrMapValue(isBoxAnchor),
boxStrokeColor = AttrMapValue(isColorOrNone),
boxStrokeWidth = AttrMapValue(isNumberOrNone),
boxFillColor = AttrMapValue(isColorOrNone),
fillColor = AttrMapValue(isColorOrNone),
strokeColor = AttrMapValue(isColorOrNone),
strokeWidth = AttrMapValue(isNumberOrNone),
fontName = AttrMapValue(isNoneOrString),
fontSize = AttrMapValue(isNumberOrNone),
leading = AttrMapValue(isNumberOrNone),
width = AttrMapValue(isNumberOrNone),
maxWidth = AttrMapValue(isNumberOrNone),
height = AttrMapValue(isNumberOrNone),
textAnchor = AttrMapValue(isTextAnchor),
visible = AttrMapValue(isBoolean,desc="True if the label is to be drawn"),
)
def __init__(self):
self.textAnchor = 'start'
self.boxAnchor = 'w'
for a in self._attrMap.keys():
if not hasattr(self,a): setattr(self,a,None)
def decorate(self,l,L):
chart,g,rowNo,colNo,x,y,width,height,x00,y00,x0,y0 = l._callOutInfo
L.setText(chart.categoryAxis.categoryNames[colNo])
g.add(L)
def __call__(self,l):
from copy import deepcopy
L = Label()
for a,v in self.__dict__.items():
if v is None: v = getattr(l,a,None)
setattr(L,a,v)
self.decorate(l,L)
isOffsetMode=OneOf('high','low','bar','axis')
class LabelOffset(PropHolder):
_attrMap = AttrMap(
posMode = AttrMapValue(isOffsetMode,desc="Where to base +ve offset"),
pos = AttrMapValue(isNumber,desc='Value for positive elements'),
negMode = AttrMapValue(isOffsetMode,desc="Where to base -ve offset"),
neg = AttrMapValue(isNumber,desc='Value for negative elements'),
)
def __init__(self):
self.posMode=self.negMode='axis'
self.pos = self.neg = 0
def _getValue(self, chart, val):
flipXY = chart._flipXY
A = chart.categoryAxis
jA = A.joinAxis
if val>=0:
mode = self.posMode
delta = self.pos
else:
mode = self.negMode
delta = self.neg
if flipXY:
v = A._x
else:
v = A._y
if jA:
if flipXY:
_v = jA._x
else:
_v = jA._y
if mode=='high':
v = _v + jA._length
elif mode=='low':
v = _v
elif mode=='bar':
v = _v+val
return v+delta
NoneOrInstanceOfLabelOffset=NoneOr(isInstanceOf(LabelOffset))
class BarChartLabel(Label):
"""
An extended Label allowing for nudging, lines visibility etc
"""
_attrMap = AttrMap(
BASE=Label,
lineStrokeWidth = AttrMapValue(isNumberOrNone, desc="Non-zero for a drawn line"),
lineStrokeColor = AttrMapValue(isColorOrNone, desc="Color for a drawn line"),
fixedEnd = AttrMapValue(NoneOrInstanceOfLabelOffset, desc="None or fixed draw ends +/-"),
fixedStart = AttrMapValue(NoneOrInstanceOfLabelOffset, desc="None or fixed draw starts +/-"),
nudge = AttrMapValue(isNumber, desc="Non-zero sign dependent nudge"),
)
def __init__(self):
Label.__init__(self)
self.lineStrokeWidth = 0
self.lineStrokeColor = None
self.nudge = 0
self.fixedStart = self.fixedEnd = None
self._pmv = 0
def _getBoxAnchor(self):
a = self.boxAnchor
if self._pmv<0: a = {'nw':'se','n':'s','ne':'sw','w':'e','c':'c','e':'w','sw':'ne','s':'n','se':'nw'}[a]
return a
def _getTextAnchor(self):
a = self.textAnchor
if self._pmv<0: a = {'start':'end', 'middle':'middle', 'end':'start'}[a]
return a
class NA_Label(BarChartLabel):
"""
An extended Label allowing for nudging, lines visibility etc
"""
_attrMap = AttrMap(
BASE=BarChartLabel,
text = AttrMapValue(isNoneOrString, desc="Text to be used for N/A values"),
)
def __init__(self):
BarChartLabel.__init__(self)
self.text = 'n/a'
NoneOrInstanceOfNA_Label=NoneOr(isInstanceOf(NA_Label))

View File

@ -1,191 +0,0 @@
#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/graphics/charts/utils.py
"Utilities used here and there."
__version__=''' $Id$ '''
from time import mktime, gmtime, strftime
import string
### Dinu's stuff used in some line plots (likely to vansih).
def mkTimeTuple(timeString):
"Convert a 'dd/mm/yyyy' formatted string to a tuple for use in the time module."
list = [0] * 9
dd, mm, yyyy = map(int, string.split(timeString, '/'))
list[:3] = [yyyy, mm, dd]
return tuple(list)
def str2seconds(timeString):
"Convert a number of seconds since the epoch into a date string."
return mktime(mkTimeTuple(timeString))
def seconds2str(seconds):
"Convert a date string into the number of seconds since the epoch."
return strftime('%Y-%m-%d', gmtime(seconds))
### Aaron's rounding function for making nice values on axes.
from math import log10
def nextRoundNumber(x):
"""Return the first 'nice round number' greater than or equal to x
Used in selecting apropriate tick mark intervals; we say we want
an interval which places ticks at least 10 points apart, work out
what that is in chart space, and ask for the nextRoundNumber().
Tries the series 1,2,5,10,20,50,100.., going up or down as needed.
"""
#guess to nearest order of magnitude
if x in (0, 1):
return x
if x < 0:
return -1.0 * nextRoundNumber(-x)
else:
lg = int(log10(x))
if lg == 0:
if x < 1:
base = 0.1
else:
base = 1.0
elif lg < 0:
base = 10.0 ** (lg - 1)
else:
base = 10.0 ** lg # e.g. base(153) = 100
# base will always be lower than x
if base >= x:
return base * 1.0
elif (base * 2) >= x:
return base * 2.0
elif (base * 5) >= x:
return base * 5.0
else:
return base * 10.0
### Robin's stuff from rgb_ticks.
from math import log10, floor
_intervals=(.1, .2, .25, .5)
_j_max=len(_intervals)-1
def find_interval(lo,hi,I=5):
'determine tick parameters for range [lo, hi] using I intervals'
if lo >= hi:
if lo==hi:
if lo==0:
lo = -.1
hi = .1
else:
lo = 0.9*lo
hi = 1.1*hi
else:
raise ValueError, "lo>hi"
x=(hi - lo)/float(I)
b= (x>0 and (x<1 or x>10)) and 10**floor(log10(x)) or 1
b = b
while 1:
a = x/b
if a<=_intervals[-1]: break
b = b*10
j = 0
while a>_intervals[j]: j = j + 1
while 1:
ss = _intervals[j]*b
n = lo/ss
l = int(n)-(n<0)
n = ss*l
x = ss*(l+I)
a = I*ss
if n>0:
if a>=hi:
n = 0.0
x = a
elif hi<0:
a = -a
if lo>a:
n = a
x = 0
if hi<=x and n<=lo: break
j = j + 1
if j>_j_max:
j = 0
b = b*10
return n, x, ss, lo - n + x - hi
def find_good_grid(lower,upper,n=(4,5,6,7,8,9), grid=None):
if grid:
t = divmod(lower,grid)[0] * grid
hi, z = divmod(upper,grid)
if z>1e-8: hi = hi+1
hi = hi*grid
else:
try:
n[0]
except TypeError:
n = xrange(max(1,n-2),max(n+3,2))
w = 1e308
for i in n:
z=find_interval(lower,upper,i)
if z[3]<w:
t, hi, grid = z[:3]
w=z[3]
return t, hi, grid
def ticks(lower, upper, n=(4,5,6,7,8,9), split=1, percent=0, grid=None):
'''
return tick positions and labels for range lower<=x<=upper
n=number of intervals to try (can be a list or sequence)
split=1 return ticks then labels else (tick,label) pairs
'''
t, hi, grid = find_good_grid(lower, upper, n, grid)
power = floor(log10(grid))
if power==0: power = 1
w = grid/10.**power
w = int(w)!=w
if power > 3 or power < -3:
format = '%+'+`w+7`+'.0e'
else:
if power >= 0:
digits = int(power)+w
format = '%' + `digits`+'.0f'
else:
digits = w-int(power)
format = '%'+`digits+2`+'.'+`digits`+'f'
if percent: format=format+'%%'
T = []
n = int(float(hi-t)/grid+0.1)+1
if split:
labels = []
for i in xrange(n):
v = t+grid*i
T.append(v)
labels.append(format % v)
return T, labels
else:
for i in xrange(n):
v = t+grid*i
T.append((v, format % v))
return T

View File

@ -1,233 +0,0 @@
from reportlab.lib import colors
from reportlab.lib.attrmap import *
from reportlab.pdfgen.canvas import Canvas
from reportlab.graphics.shapes import Group, Drawing, Ellipse, Wedge, String, STATE_DEFAULTS, Polygon, Line
def _getShaded(col,shd=None,shading=0.1):
if shd is None:
from reportlab.lib.colors import Blacker
if col: shd = Blacker(col,1-shading)
return shd
def _getLit(col,shd=None,lighting=0.1):
if shd is None:
from reportlab.lib.colors import Whiter
if col: shd = Whiter(col,1-lighting)
return shd
def _draw_3d_bar(G, x1, x2, y0, yhigh, xdepth, ydepth,
fillColor=None, fillColorShaded=None,
strokeColor=None, strokeWidth=1, shading=0.1):
fillColorShaded = _getShaded(fillColor,None,shading)
fillColorShadedTop = _getShaded(fillColor,None,shading/2.0)
def _add_3d_bar(x1, x2, y1, y2, xoff, yoff,
G=G,strokeColor=strokeColor, strokeWidth=strokeWidth, fillColor=fillColor):
G.add(Polygon((x1,y1, x1+xoff,y1+yoff, x2+xoff,y2+yoff, x2,y2),
strokeWidth=strokeWidth, strokeColor=strokeColor, fillColor=fillColor,strokeLineJoin=1))
usd = max(y0, yhigh)
if xdepth or ydepth:
if y0!=yhigh: #non-zero height
_add_3d_bar( x2, x2, y0, yhigh, xdepth, ydepth, fillColor=fillColorShaded) #side
_add_3d_bar(x1, x2, usd, usd, xdepth, ydepth, fillColor=fillColorShadedTop) #top
G.add(Polygon((x1,y0,x2,y0,x2,yhigh,x1,yhigh),
strokeColor=strokeColor, strokeWidth=strokeWidth, fillColor=fillColor,strokeLineJoin=1)) #front
if xdepth or ydepth:
G.add(Line( x1, usd, x2, usd, strokeWidth=strokeWidth, strokeColor=strokeColor or fillColorShaded))
class _YStrip:
def __init__(self,y0,y1, slope, fillColor, fillColorShaded, shading=0.1):
self.y0 = y0
self.y1 = y1
self.slope = slope
self.fillColor = fillColor
self.fillColorShaded = _getShaded(fillColor,fillColorShaded,shading)
def _ystrip_poly( x0, x1, y0, y1, xoff, yoff):
return [x0,y0,x0+xoff,y0+yoff,x1+xoff,y1+yoff,x1,y1]
def _make_3d_line_info( G, x0, x1, y0, y1, z0, z1,
theta_x, theta_y,
fillColor, fillColorShaded=None, tileWidth=1,
strokeColor=None, strokeWidth=None, strokeDashArray=None,
shading=0.1):
zwidth = abs(z1-z0)
xdepth = zwidth*theta_x
ydepth = zwidth*theta_y
depth_slope = xdepth==0 and 1e150 or -ydepth/float(xdepth)
x = float(x1-x0)
slope = x==0 and 1e150 or (y1-y0)/x
c = slope>depth_slope and _getShaded(fillColor,fillColorShaded,shading) or fillColor
zy0 = z0*theta_y
zx0 = z0*theta_x
tileStrokeWidth = 0.6
if tileWidth is None:
D = [(x1,y1)]
else:
T = ((y1-y0)**2+(x1-x0)**2)**0.5
tileStrokeWidth *= tileWidth
if T<tileWidth:
D = [(x1,y1)]
else:
n = int(T/float(tileWidth))+1
dx = float(x1-x0)/n
dy = float(y1-y0)/n
D = []
a = D.append
for i in xrange(1,n):
a((x0+dx*i,y0+dy*i))
a = G.add
x_0 = x0+zx0
y_0 = y0+zy0
for x,y in D:
x_1 = x+zx0
y_1 = y+zy0
P = Polygon(_ystrip_poly(x_0, x_1, y_0, y_1, xdepth, ydepth),
fillColor = c, strokeColor=c, strokeWidth=tileStrokeWidth)
a((0,z0,z1,x_0,y_0,P))
x_0 = x_1
y_0 = y_1
from math import pi, sin, cos
_pi_2 = pi*0.5
_2pi = 2*pi
_180_pi=180./pi
def _2rad(angle):
return (angle*pi)/180
def mod_2pi(radians):
radians = radians % _2pi
if radians<-1e-6: radians += _2pi
return radians
def _2deg(o):
return o*_180_pi
def _360(a):
a %= 360
if a<-1e-6: a += 360
return a
_ZERO = 1e-8
_ONE = 1-_ZERO
class _Segment:
def __init__(self,s,i,data):
S = data[s]
x0 = S[i-1][0]
y0 = S[i-1][1]
x1 = S[i][0]
y1 = S[i][1]
if x1<x0:
x0,y0,x1,y1 = x1,y1,x0,y0
# (y-y0)*(x1-x0) = (y1-y0)*(x-x0)
# (x1-x0)*y + (y0-y1)*x = y0*(x1-x0)+x0*(y0-y1)
# a*y+b*x = c
self.a = float(x1-x0)
self.b = float(y1-y0)
self.x0 = x0
self.x1 = x1
self.y0 = y0
self.y1 = y1
self.series = s
self.i = i
self.s = s
def __str__(self):
return '[(%s,%s),(%s,%s)]' % (self.x0,self.y0,self.x1,self.y1)
__repr__ = __str__
def intersect(self,o,I):
'''try to find an intersection with _Segment o
'''
x0 = self.x0
ox0 = o.x0
assert x0<=ox0
if ox0>self.x1: return 1
if o.s==self.s and o.i in (self.i-1,self.i+1): return
a = self.a
b = self.b
oa = o.a
ob = o.b
det = ob*a - oa*b
if -1e-8<det<1e-8: return
dx = x0 - ox0
dy = self.y0 - o.y0
u = (oa*dy - ob*dx)/det
ou = (a*dy - b*dx)/det
if u<0 or u>1 or ou<0 or ou>1: return
x = x0 + u*a
y = self.y0 + u*b
if _ZERO<u<_ONE:
t = self.s,self.i,x,y
if t not in I: I.append(t)
if _ZERO<ou<_ONE:
t = o.s,o.i,x,y
if t not in I: I.append(t)
def _segCmp(a,b):
return cmp((a.x0,a.x1,a.y0,a.y1,a.s,a.i),(b.x0,b.x1,b.y0,b.y1,b.s,b.i))
def find_intersections(data,small=0):
'''
data is a sequence of series
each series is a list of (x,y) coordinates
where x & y are ints or floats
find_intersections returns a sequence of 4-tuples
i, j, x, y
where i is a data index j is an insertion position for data[i]
and x, y are coordinates of an intersection of series data[i]
with some other series. If correctly implemented we get all such
intersections. We don't count endpoint intersections and consider
parallel lines as non intersecting (even when coincident).
We ignore segments that have an estimated size less than small.
'''
#find all line segments
S = []
a = S.append
for s in xrange(len(data)):
ds = data[s]
if not ds: continue
n = len(ds)
if n==1: continue
for i in xrange(1,n):
seg = _Segment(s,i,data)
if seg.a+abs(seg.b)>=small: a(seg)
S.sort(_segCmp)
I = []
n = len(S)
for i in xrange(0,n-1):
s = S[i]
for j in xrange(i+1,n):
if s.intersect(S[j],I)==1: break
I.sort()
return I
if __name__=='__main__':
from reportlab.graphics.shapes import Drawing
from reportlab.lib.colors import lightgrey, pink
D = Drawing(300,200)
_draw_3d_bar(D, 10, 20, 10, 50, 5, 5, fillColor=lightgrey, strokeColor=pink)
_draw_3d_bar(D, 30, 40, 10, 45, 5, 5, fillColor=lightgrey, strokeColor=pink)
D.save(formats=['pdf'],outDir='.',fnRoot='_draw_3d_bar')
print find_intersections([[(0,0.5),(1,0.5),(0.5,0),(0.5,1)],[(.2666666667,0.4),(0.1,0.4),(0.1,0.2),(0,0),(1,1)],[(0,1),(0.4,0.1),(1,0.1)]])
print find_intersections([[(0.1, 0.2), (0.1, 0.4)], [(0, 1), (0.4, 0.1)]])
print find_intersections([[(0.2, 0.4), (0.1, 0.4)], [(0.1, 0.8), (0.4, 0.1)]])
print find_intersections([[(0,0),(1,1)],[(0.4,0.1),(1,0.1)]])
print find_intersections([[(0,0.5),(1,0.5),(0.5,0),(0.5,1)],[(0,0),(1,1)],[(0.1,0.8),(0.4,0.1),(1,0.1)]])

View File

@ -1,361 +0,0 @@
#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/graphics/renderPDF.py
# renderPDF - draws Drawings onto a canvas
"""Usage:
import renderpdf
renderpdf.draw(drawing, canvas, x, y)
Execute the script to see some test drawings.
changed
"""
__version__=''' $Id$ '''
from reportlab.graphics.shapes import *
from reportlab.pdfgen.canvas import Canvas
from reportlab.pdfbase.pdfmetrics import stringWidth
from reportlab.lib.utils import getStringIO
from reportlab import rl_config
# the main entry point for users...
def draw(drawing, canvas, x, y, showBoundary=rl_config._unset_):
"""As it says"""
R = _PDFRenderer()
R.draw(drawing, canvas, x, y, showBoundary=showBoundary)
from renderbase import Renderer, StateTracker, getStateDelta
class _PDFRenderer(Renderer):
"""This draws onto a PDF document. It needs to be a class
rather than a function, as some PDF-specific state tracking is
needed outside of the state info in the SVG model."""
def __init__(self):
self._stroke = 0
self._fill = 0
self._tracker = StateTracker()
def drawNode(self, node):
"""This is the recursive method called for each node
in the tree"""
#print "pdf:drawNode", self
#if node.__class__ is Wedge: stop
if not (isinstance(node, Path) and node.isClipPath):
self._canvas.saveState()
#apply state changes
deltas = getStateDelta(node)
self._tracker.push(deltas)
self.applyStateChanges(deltas, {})
#draw the object, or recurse
self.drawNodeDispatcher(node)
self._tracker.pop()
if not (isinstance(node, Path) and node.isClipPath):
self._canvas.restoreState()
def drawRect(self, rect):
if rect.rx == rect.ry == 0:
#plain old rectangle
self._canvas.rect(
rect.x, rect.y,
rect.width, rect.height,
stroke=self._stroke,
fill=self._fill
)
else:
#cheat and assume ry = rx; better to generalize
#pdfgen roundRect function. TODO
self._canvas.roundRect(
rect.x, rect.y,
rect.width, rect.height, rect.rx,
fill=self._fill,
stroke=self._stroke
)
def drawImage(self, image):
# currently not implemented in other renderers
if image.path and os.path.exists(image.path):
self._canvas.drawInlineImage(
image.path,
image.x, image.y,
image.width, image.height
)
def drawLine(self, line):
if self._stroke:
self._canvas.line(line.x1, line.y1, line.x2, line.y2)
def drawCircle(self, circle):
self._canvas.circle(
circle.cx, circle.cy, circle.r,
fill=self._fill,
stroke=self._stroke
)
def drawPolyLine(self, polyline):
if self._stroke:
assert len(polyline.points) >= 2, 'Polyline must have 2 or more points'
head, tail = polyline.points[0:2], polyline.points[2:],
path = self._canvas.beginPath()
path.moveTo(head[0], head[1])
for i in range(0, len(tail), 2):
path.lineTo(tail[i], tail[i+1])
self._canvas.drawPath(path)
def drawWedge(self, wedge):
centerx, centery, radius, startangledegrees, endangledegrees = \
wedge.centerx, wedge.centery, wedge.radius, wedge.startangledegrees, wedge.endangledegrees
yradius, radius1, yradius1 = wedge._xtraRadii()
if yradius is None: yradius = radius
angle = endangledegrees-startangledegrees
path = self._canvas.beginPath()
if (radius1==0 or radius1 is None) and (yradius1==0 or yradius1 is None):
path.moveTo(centerx, centery)
path.arcTo(centerx-radius, centery-yradius, centerx+radius, centery+yradius,
startangledegrees, angle)
else:
path.arc(centerx-radius, centery-yradius, centerx+radius, centery+yradius,
startangledegrees, angle)
path.arcTo(centerx-radius1, centery-yradius1, centerx+radius1, centery+yradius1,
endangledegrees, -angle)
path.close()
self._canvas.drawPath(path,
fill=self._fill,
stroke=self._stroke)
def drawEllipse(self, ellipse):
#need to convert to pdfgen's bounding box representation
x1 = ellipse.cx - ellipse.rx
x2 = ellipse.cx + ellipse.rx
y1 = ellipse.cy - ellipse.ry
y2 = ellipse.cy + ellipse.ry
self._canvas.ellipse(x1,y1,x2,y2,fill=self._fill,stroke=self._stroke)
def drawPolygon(self, polygon):
assert len(polygon.points) >= 2, 'Polyline must have 2 or more points'
head, tail = polygon.points[0:2], polygon.points[2:],
path = self._canvas.beginPath()
path.moveTo(head[0], head[1])
for i in range(0, len(tail), 2):
path.lineTo(tail[i], tail[i+1])
path.close()
self._canvas.drawPath(
path,
stroke=self._stroke,
fill=self._fill
)
def drawString(self, stringObj):
if self._fill:
S = self._tracker.getState()
text_anchor, x, y, text = S['textAnchor'], stringObj.x,stringObj.y,stringObj.text
if not text_anchor in ['start','inherited']:
font, font_size = S['fontName'], S['fontSize']
textLen = stringWidth(text, font,font_size)
if text_anchor=='end':
x = x-textLen
elif text_anchor=='middle':
x = x - textLen/2
else:
raise ValueError, 'bad value for textAnchor '+str(text_anchor)
t = self._canvas.beginText(x,y)
t.textLine(text)
self._canvas.drawText(t)
def drawPath(self, path):
from reportlab.graphics.shapes import _renderPath
pdfPath = self._canvas.beginPath()
drawFuncs = (pdfPath.moveTo, pdfPath.lineTo, pdfPath.curveTo, pdfPath.close)
isClosed = _renderPath(path, drawFuncs)
if isClosed:
fill = self._fill
else:
fill = 0
if path.isClipPath:
self._canvas.clipPath(pdfPath, fill=fill, stroke=self._stroke)
else:
self._canvas.drawPath(pdfPath,
fill=fill,
stroke=self._stroke)
def applyStateChanges(self, delta, newState):
"""This takes a set of states, and outputs the PDF operators
needed to set those properties"""
for key, value in delta.items():
if key == 'transform':
self._canvas.transform(value[0], value[1], value[2],
value[3], value[4], value[5])
elif key == 'strokeColor':
#this has different semantics in PDF to SVG;
#we always have a color, and either do or do
#not apply it; in SVG one can have a 'None' color
if value is None:
self._stroke = 0
else:
self._stroke = 1
self._canvas.setStrokeColor(value)
elif key == 'strokeWidth':
self._canvas.setLineWidth(value)
elif key == 'strokeLineCap': #0,1,2
self._canvas.setLineCap(value)
elif key == 'strokeLineJoin':
self._canvas.setLineJoin(value)
# elif key == 'stroke_dasharray':
# self._canvas.setDash(array=value)
elif key == 'strokeDashArray':
if value:
self._canvas.setDash(value)
else:
self._canvas.setDash()
elif key == 'fillColor':
#this has different semantics in PDF to SVG;
#we always have a color, and either do or do
#not apply it; in SVG one can have a 'None' color
if value is None:
self._fill = 0
else:
self._fill = 1
self._canvas.setFillColor(value)
elif key in ['fontSize', 'fontName']:
# both need setting together in PDF
# one or both might be in the deltas,
# so need to get whichever is missing
fontname = delta.get('fontName', self._canvas._fontname)
fontsize = delta.get('fontSize', self._canvas._fontsize)
self._canvas.setFont(fontname, fontsize)
from reportlab.platypus import Flowable
class GraphicsFlowable(Flowable):
"""Flowable wrapper around a Pingo drawing"""
def __init__(self, drawing):
self.drawing = drawing
self.width = self.drawing.width
self.height = self.drawing.height
def draw(self):
draw(self.drawing, self.canv, 0, 0)
def drawToFile(d, fn, msg="", showBoundary=rl_config._unset_, autoSize=1):
"""Makes a one-page PDF with just the drawing.
If autoSize=1, the PDF will be the same size as
the drawing; if 0, it will place the drawing on
an A4 page with a title above it - possibly overflowing
if too big."""
c = Canvas(fn)
c.setFont('Times-Roman', 36)
c.drawString(80, 750, msg)
c.setTitle(msg)
if autoSize:
c.setPageSize((d.width, d.height))
draw(d, c, 0, 0, showBoundary=showBoundary)
else:
#show with a title
c.setFont('Times-Roman', 12)
y = 740
i = 1
y = y - d.height
draw(d, c, 80, y, showBoundary=showBoundary)
c.showPage()
c.save()
if sys.platform=='mac' and not hasattr(fn, "write"):
try:
import macfs, macostools
macfs.FSSpec(fn).SetCreatorType("CARO", "PDF ")
macostools.touched(fn)
except:
pass
def drawToString(d, msg="", showBoundary=rl_config._unset_,autoSize=1):
"Returns a PDF as a string in memory, without touching the disk"
s = getStringIO()
drawToFile(d, s, msg=msg, showBoundary=showBoundary,autoSize=autoSize)
return s.getvalue()
#########################################################
#
# test code. First, defin a bunch of drawings.
# Routine to draw them comes at the end.
#
#########################################################
def test():
c = Canvas('renderPDF.pdf')
c.setFont('Times-Roman', 36)
c.drawString(80, 750, 'Graphics Test')
# print all drawings and their doc strings from the test
# file
#grab all drawings from the test module
from reportlab.graphics import testshapes
drawings = []
for funcname in dir(testshapes):
if funcname[0:10] == 'getDrawing':
drawing = eval('testshapes.' + funcname + '()') #execute it
docstring = eval('testshapes.' + funcname + '.__doc__')
drawings.append((drawing, docstring))
#print in a loop, with their doc strings
c.setFont('Times-Roman', 12)
y = 740
i = 1
for (drawing, docstring) in drawings:
assert (docstring is not None), "Drawing %d has no docstring!" % i
if y < 300: #allows 5-6 lines of text
c.showPage()
y = 740
# draw a title
y = y - 30
c.setFont('Times-BoldItalic',12)
c.drawString(80, y, 'Drawing %d' % i)
c.setFont('Times-Roman',12)
y = y - 14
textObj = c.beginText(80, y)
textObj.textLines(docstring)
c.drawText(textObj)
y = textObj.getY()
y = y - drawing.height
draw(drawing, c, 80, y)
i = i + 1
if y!=740: c.showPage()
c.save()
print 'saved renderPDF.pdf'
##def testFlowable():
## """Makes a platypus document"""
## from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
## from reportlab.lib.styles import getSampleStyleSheet
## styles = getSampleStyleSheet()
## styNormal = styles['Normal']
##
## doc = SimpleDocTemplate('test_flowable.pdf')
## story = []
## story.append(Paragraph("This sees is a drawing can work as a flowable", styNormal))
##
## import testdrawings
## drawings = []
##
## for funcname in dir(testdrawings):
## if funcname[0:10] == 'getDrawing':
## drawing = eval('testdrawings.' + funcname + '()') #execute it
## docstring = eval('testdrawings.' + funcname + '.__doc__')
## story.append(Paragraph(docstring, styNormal))
## story.append(Spacer(18,18))
## story.append(drawing)
## story.append(Spacer(36,36))
##
## doc.build(story)
## print 'saves test_flowable.pdf'
if __name__=='__main__':
test()
#testFlowable()

View File

@ -1,631 +0,0 @@
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history www.reportlab.co.uk/rl-cgi/viewcvs.cgi/rlextra/graphics/Csrc/renderPM/renderP.py
__version__=''' $Id$ '''
"""Usage:
from reportlab.graphics import renderPM
renderPM.drawToFile(drawing,filename,fmt='GIF',configPIL={....})
Other functions let you create a PM drawing as string or into a PM buffer.
Execute the script to see some test drawings."""
from reportlab.graphics.shapes import *
from reportlab.graphics.renderbase import StateTracker, getStateDelta
from reportlab.pdfbase.pdfmetrics import getFont
from math import sin, cos, pi, ceil
from reportlab.lib.utils import getStringIO, open_and_read
from reportlab import rl_config
class RenderPMError(Exception):
pass
import string, os, sys
try:
import _renderPM
except ImportError, errMsg:
raise ImportError, "No module named _renderPM\n" + \
(str(errMsg)!='No module named _renderPM' and "it may be the wrong version or badly installed!" or
"see http://www.reportlab.org/rl_addons.html")
from types import TupleType, ListType
_SeqTypes = (TupleType,ListType)
def _getImage():
try:
from PIL import Image
except ImportError:
import Image
return Image
def Color2Hex(c):
#assert isinstance(colorobj, colors.Color) #these checks don't work well RGB
if c: return ((0xFF&int(255*c.red)) << 16) | ((0xFF&int(255*c.green)) << 8) | (0xFF&int(255*c.blue))
return c
# the main entry point for users...
def draw(drawing, canvas, x, y, showBoundary=rl_config._unset_):
"""As it says"""
R = _PMRenderer()
R.draw(drawing, canvas, x, y, showBoundary=showBoundary)
from reportlab.graphics.renderbase import Renderer
class _PMRenderer(Renderer):
"""This draws onto a pix map image. It needs to be a class
rather than a function, as some image-specific state tracking is
needed outside of the state info in the SVG model."""
def __init__(self):
self._tracker = StateTracker()
def pop(self):
self._tracker.pop()
self.applyState()
def push(self,node):
deltas = getStateDelta(node)
self._tracker.push(deltas)
self.applyState()
def applyState(self):
s = self._tracker.getState()
self._canvas.ctm = s['ctm']
self._canvas.strokeWidth = s['strokeWidth']
self._canvas.strokeColor = Color2Hex(s['strokeColor'])
self._canvas.lineCap = s['strokeLineCap']
self._canvas.lineJoin = s['strokeLineJoin']
da = s['strokeDashArray']
da = da and (0,da) or None
self._canvas.dashArray = da
self._canvas.fillColor = Color2Hex(s['fillColor'])
self._canvas.setFont(s['fontName'], s['fontSize'])
def initState(self,x,y):
deltas = STATE_DEFAULTS.copy()
deltas['transform'] = self._canvas._baseCTM[0:4]+(x,y)
self._tracker.push(deltas)
self.applyState()
def drawNode(self, node):
"""This is the recursive method called for each node
in the tree"""
#apply state changes
self.push(node)
#draw the object, or recurse
self.drawNodeDispatcher(node)
# restore the state
self.pop()
def drawRect(self, rect):
c = self._canvas
if rect.rx == rect.ry == 0:
#plain old rectangle, draw clockwise (x-axis to y-axis) direction
c.rect(rect.x,rect.y, rect.width, rect.height)
else:
c.roundRect(rect.x,rect.y, rect.width, rect.height, rect.rx, rect.ry)
def drawLine(self, line):
self._canvas.line(line.x1,line.y1,line.x2,line.y2)
def drawImage(self, image):
if image.path and os.path.exists(image.path):
if type(image.path) is type(''):
im = _getImage().open(image.path).convert('RGB')
else:
im = image.path.convert('RGB')
srcW, srcH = im.size
dstW, dstH = image.width, image.height
if dstW is None: dstW = srcW
if dstH is None: dstH = srcH
self._canvas._aapixbuf(
image.x, image.y, dstW, dstH,
im.tostring(), srcW, srcH, 3,
)
def drawCircle(self, circle):
c = self._canvas
c.circle(circle.cx,circle.cy, circle.r)
c.fillstrokepath()
def drawPolyLine(self, polyline, _doClose=0):
P = polyline.points
assert len(P) >= 2, 'Polyline must have 1 or more points'
c = self._canvas
c.pathBegin()
c.moveTo(P[0], P[1])
for i in range(2, len(P), 2):
c.lineTo(P[i], P[i+1])
if _doClose:
c.pathClose()
c.pathFill()
c.pathStroke()
def drawEllipse(self, ellipse):
c=self._canvas
c.ellipse(ellipse.cx, ellipse.cy, ellipse.rx,ellipse.ry)
c.fillstrokepath()
def drawPolygon(self, polygon):
self.drawPolyLine(polygon,_doClose=1)
def drawString(self, stringObj):
fill = self._canvas.fillColor
if fill is not None:
S = self._tracker.getState()
text_anchor, x, y, text = S['textAnchor'], stringObj.x,stringObj.y,stringObj.text
if not text_anchor in ['start','inherited']:
font, font_size = S['fontName'], S['fontSize']
textLen = stringWidth(text, font,font_size)
if text_anchor=='end':
x = x-textLen
elif text_anchor=='middle':
x = x - textLen/2
else:
raise ValueError, 'bad value for textAnchor '+str(text_anchor)
self._canvas.drawString(x,y,text)
def drawPath(self, path):
c = self._canvas
if path is EmptyClipPath:
del c._clipPaths[-1]
if c._clipPaths:
P = c._clipPaths[-1]
icp = P.isClipPath
P.isClipPath = 1
self.drawPath(P)
P.isClipPath = icp
else:
c.clipPathClear()
return
c.pathBegin()
drawFuncs = (c.moveTo, c.lineTo, c.curveTo, c.pathClose)
from reportlab.graphics.shapes import _renderPath
isClosed = _renderPath(path, drawFuncs)
if path.isClipPath:
c.clipPathSet()
c._clipPaths.append(path)
else:
if isClosed: c.pathFill()
c.pathStroke()
def _setFont(gs,fontName,fontSize):
try:
gs.setFont(fontName,fontSize)
except _renderPM.Error, errMsg:
if errMsg.args[0]!="Can't find font!": raise
#here's where we try to add a font to the canvas
try:
f = getFont(fontName)
if _renderPM._version<='0.98': #added reader arg in 0.99
_renderPM.makeT1Font(fontName,f.face.findT1File(),f.encoding.vector)
else:
_renderPM.makeT1Font(fontName,f.face.findT1File(),f.encoding.vector,open_and_read)
except:
s1, s2 = map(str,sys.exc_info()[:2])
raise RenderPMError, "Can't setFont(%s) missing the T1 files?\nOriginally %s: %s" % (fontName,s1,s2)
gs.setFont(fontName,fontSize)
def _convert2pilp(im):
Image = _getImage()
return im.convert("P", dither=Image.NONE, palette=Image.ADAPTIVE)
def _saveAsPICT(im,fn,fmt,transparent=None):
im = _convert2pilp(im)
cols, rows = im.size
#s = _renderPM.pil2pict(cols,rows,im.tostring(),im.im.getpalette(),transparent is not None and Color2Hex(transparent) or -1)
s = _renderPM.pil2pict(cols,rows,im.tostring(),im.im.getpalette())
if not hasattr(fn,'write'):
open(os.path.splitext(fn)[0]+'.'+string.lower(fmt),'wb').write(s)
if os.name=='mac':
from reportlab.lib.utils import markfilename
markfilename(fn,ext='PICT')
else:
fn.write(s)
BEZIER_ARC_MAGIC = 0.5522847498 #constant for drawing circular arcs w/ Beziers
class PMCanvas:
def __init__(self,w,h,dpi=72,bg=0xffffff,configPIL=None):
'''configPIL dict is passed to image save method'''
scale = dpi/72.0
w = int(w*scale+0.5)
h = int(h*scale+0.5)
self.__dict__['_gs'] = _renderPM.gstate(w,h,bg=bg)
self.__dict__['_bg'] = bg
self.__dict__['_baseCTM'] = (scale,0,0,scale,0,0)
self.__dict__['_clipPaths'] = []
self.__dict__['configPIL'] = configPIL
self.__dict__['_dpi'] = dpi
self.ctm = self._baseCTM
def _drawTimeResize(self,w,h,bg=None):
if bg is None: bg = self._bg
self._drawing.width, self._drawing.height = w, h
A = {'ctm':None, 'strokeWidth':None, 'strokeColor':None, 'lineCap':None, 'lineJoin':None, 'dashArray':None, 'fillColor':None}
gs = self._gs
fN,fS = gs.fontName, gs.fontSize
for k in A.keys():
A[k] = getattr(gs,k)
del gs, self._gs
gs = self.__dict__['_gs'] = _renderPM.gstate(w,h,bg=bg)
for k in A.keys():
setattr(self,k,A[k])
gs.setFont(fN,fS)
def toPIL(self):
im = _getImage().new('RGB', size=(self._gs.width, self._gs.height))
im.fromstring(self._gs.pixBuf)
return im
def saveToFile(self,fn,fmt=None):
im = self.toPIL()
if fmt is None:
if type(fn) is not StringType:
raise ValueError, "Invalid type '%s' for fn when fmt is None" % type(fn)
fmt = os.path.splitext(fn)[1]
if fmt.startswith('.'): fmt = fmt[1:]
configPIL = self.configPIL or {}
fmt = string.upper(fmt)
if fmt in ['GIF']:
im = _convert2pilp(im)
elif fmt in ['PCT','PICT']:
return _saveAsPICT(im,fn,fmt,transparent=configPIL.get('transparent',None))
elif fmt in ['PNG','TIFF','BMP', 'PPM', 'TIF']:
if fmt=='TIF': fmt = 'TIFF'
if fmt=='PNG':
try:
from PIL import PngImagePlugin
except ImportError:
import PngImagePlugin
elif fmt=='BMP':
try:
from PIL import BmpImagePlugin
except ImportError:
import BmpImagePlugin
elif fmt in ('JPG','JPEG'):
fmt = 'JPEG'
else:
raise RenderPMError,"Unknown image kind %s" % fmt
if fmt=='TIFF':
tc = configPIL.get('transparent',None)
if tc:
from PIL import ImageChops, Image
T = 768*[0]
for o, c in zip((0,256,512), tc.bitmap_rgb()):
T[o+c] = 255
#if type(fn) is type(''): ImageChops.invert(im.point(T).convert('L').point(255*[0]+[255])).save(fn+'_mask.gif','GIF')
im = Image.merge('RGBA', im.split()+(ImageChops.invert(im.point(T).convert('L').point(255*[0]+[255])),))
#if type(fn) is type(''): im.save(fn+'_masked.gif','GIF')
for a,d in ('resolution',self._dpi),('resolution unit','inch'):
configPIL[a] = configPIL.get(a,d)
apply(im.save,(fn,fmt),configPIL)
if not hasattr(fn,'write') and os.name=='mac':
from reportlab.lib.utils import markfilename
markfilename(fn,ext=fmt)
def saveToString(self,fmt='GIF'):
s = getStringIO()
self.saveToFile(s,fmt=fmt)
return s.getvalue()
def _saveToBMP(self,f):
'''
Niki Spahiev, <niki@vintech.bg>, asserts that this is a respectable way to get BMP without PIL
f is a file like object to which the BMP is written
'''
import struct
gs = self._gs
pix, width, height = gs.pixBuf, gs.width, gs.height
f.write(struct.pack('=2sLLLLLLhh24x','BM',len(pix)+54,0,54,40,width,height,1,24))
rowb = width * 3
for o in range(len(pix),0,-rowb):
f.write(pix[o-rowb:o])
f.write( '\0' * 14 )
def setFont(self,fontName,fontSize,leading=None):
_setFont(self._gs,fontName,fontSize)
def __setattr__(self,name,value):
setattr(self._gs,name,value)
def __getattr__(self,name):
return getattr(self._gs,name)
def fillstrokepath(self,stroke=1,fill=1):
if fill: self.pathFill()
if stroke: self.pathStroke()
def _bezierArcSegmentCCW(self, cx,cy, rx,ry, theta0, theta1):
"""compute the control points for a bezier arc with theta1-theta0 <= 90.
Points are computed for an arc with angle theta increasing in the
counter-clockwise (CCW) direction. returns a tuple with starting point
and 3 control points of a cubic bezier curve for the curvto opertator"""
# Requires theta1 - theta0 <= 90 for a good approximation
assert abs(theta1 - theta0) <= 90
cos0 = cos(pi*theta0/180.0)
sin0 = sin(pi*theta0/180.0)
x0 = cx + rx*cos0
y0 = cy + ry*sin0
cos1 = cos(pi*theta1/180.0)
sin1 = sin(pi*theta1/180.0)
x3 = cx + rx*cos1
y3 = cy + ry*sin1
dx1 = -rx * sin0
dy1 = ry * cos0
#from pdfgeom
halfAng = pi*(theta1-theta0)/(2.0 * 180.0)
k = abs(4.0 / 3.0 * (1.0 - cos(halfAng) ) /(sin(halfAng)) )
x1 = x0 + dx1 * k
y1 = y0 + dy1 * k
dx2 = -rx * sin1
dy2 = ry * cos1
x2 = x3 - dx2 * k
y2 = y3 - dy2 * k
return ((x0,y0), ((x1,y1), (x2,y2), (x3,y3)) )
def bezierArcCCW(self, cx,cy, rx,ry, theta0, theta1):
"""return a set of control points for Bezier approximation to an arc
with angle increasing counter clockwise. No requirement on |theta1-theta0| <= 90
However, it must be true that theta1-theta0 > 0."""
# I believe this is also clockwise
# pretty much just like Robert Kern's pdfgeom.BezierArc
angularExtent = theta1 - theta0
# break down the arc into fragments of <=90 degrees
if abs(angularExtent) <= 90.0: # we just need one fragment
angleList = [(theta0,theta1)]
else:
Nfrag = int( ceil( abs(angularExtent)/90.) )
fragAngle = float(angularExtent)/ Nfrag # this could be negative
angleList = []
for ii in range(Nfrag):
a = theta0 + ii * fragAngle
b = a + fragAngle # hmm.. is I wonder if this is precise enought
angleList.append((a,b))
ctrlpts = []
for (a,b) in angleList:
if not ctrlpts: # first time
[(x0,y0), pts] = self._bezierArcSegmentCCW(cx,cy, rx,ry, a,b)
ctrlpts.append(pts)
else:
[(tmpx,tmpy), pts] = self._bezierArcSegmentCCW(cx,cy, rx,ry, a,b)
ctrlpts.append(pts)
return ((x0,y0), ctrlpts)
def addEllipsoidalArc(self, cx,cy, rx, ry, ang1, ang2):
"""adds an ellisesoidal arc segment to a path, with an ellipse centered
on cx,cy and with radii (major & minor axes) rx and ry. The arc is
drawn in the CCW direction. Requires: (ang2-ang1) > 0"""
((x0,y0), ctrlpts) = self.bezierArcCCW(cx,cy, rx,ry,ang1,ang2)
self.lineTo(x0,y0)
for ((x1,y1), (x2,y2),(x3,y3)) in ctrlpts:
self.curveTo(x1,y1,x2,y2,x3,y3)
def drawCentredString(self, x, y, text, text_anchor='middle'):
if self.fillColor is not None:
textLen = stringWidth(text, self.fontName,self.fontSize)
if text_anchor=='end':
x = x-textLen
elif text_anchor=='middle':
x = x - textLen/2
self.drawString(x,y,text)
def drawRightString(self, text, x, y):
self.drawCentredString(text,x,y,text_anchor='end')
def line(self,x1,y1,x2,y2):
if self.strokeColor is not None:
self.pathBegin()
self.moveTo(x1,y1)
self.lineTo(x2,y2)
self.pathStroke()
def rect(self,x,y,width,height,stroke=1,fill=1):
self.pathBegin()
self.moveTo(x, y)
self.lineTo(x+width, y)
self.lineTo(x+width, y + height)
self.lineTo(x, y + height)
self.pathClose()
self.fillstrokepath(stroke=stroke,fill=fill)
def roundRect(self, x, y, width, height, rx,ry):
"""rect(self, x, y, width, height, rx,ry):
Draw a rectangle if rx or rx and ry are specified the corners are
rounded with ellipsoidal arcs determined by rx and ry
(drawn in the counter-clockwise direction)"""
if rx==0: rx = ry
if ry==0: ry = rx
x2 = x + width
y2 = y + height
self.pathBegin()
self.moveTo(x+rx,y)
self.addEllipsoidalArc(x2-rx, y+ry, rx, ry, 270, 360 )
self.addEllipsoidalArc(x2-rx, y2-ry, rx, ry, 0, 90)
self.addEllipsoidalArc(x+rx, y2-ry, rx, ry, 90, 180)
self.addEllipsoidalArc(x+rx, y+ry, rx, ry, 180, 270)
self.pathClose()
self.fillstrokepath()
def circle(self, cx, cy, r):
"add closed path circle with center cx,cy and axes r: counter-clockwise orientation"
self.ellipse(cx,cy,r,r)
def ellipse(self, cx,cy,rx,ry):
"""add closed path ellipse with center cx,cy and axes rx,ry: counter-clockwise orientation
(remember y-axis increases downward) """
self.pathBegin()
# first segment
x0 = cx + rx # (x0,y0) start pt
y0 = cy
x3 = cx # (x3,y3) end pt of arc
y3 = cy-ry
x1 = cx+rx
y1 = cy-ry*BEZIER_ARC_MAGIC
x2 = x3 + rx*BEZIER_ARC_MAGIC
y2 = y3
self.moveTo(x0, y0)
self.curveTo(x1,y1,x2,y2,x3,y3)
# next segment
x0 = x3
y0 = y3
x3 = cx-rx
y3 = cy
x1 = cx-rx*BEZIER_ARC_MAGIC
y1 = cy-ry
x2 = x3
y2 = cy- ry*BEZIER_ARC_MAGIC
self.curveTo(x1,y1,x2,y2,x3,y3)
# next segment
x0 = x3
y0 = y3
x3 = cx
y3 = cy+ry
x1 = cx-rx
y1 = cy+ry*BEZIER_ARC_MAGIC
x2 = cx -rx*BEZIER_ARC_MAGIC
y2 = cy+ry
self.curveTo(x1,y1,x2,y2,x3,y3)
#last segment
x0 = x3
y0 = y3
x3 = cx+rx
y3 = cy
x1 = cx+rx*BEZIER_ARC_MAGIC
y1 = cy+ry
x2 = cx+rx
y2 = cy+ry*BEZIER_ARC_MAGIC
self.curveTo(x1,y1,x2,y2,x3,y3)
self.pathClose()
def saveState(self):
'''do nothing for compatibility'''
pass
def setFillColor(self,aColor):
self.fillColor = Color2Hex(aColor)
def setStrokeColor(self,aColor):
self.strokeColor = Color2Hex(aColor)
restoreState = saveState
# compatibility routines
def setLineCap(self,cap):
self.lineCap = cap
def setLineWidth(self,width):
self.strokeWidth = width
def drawToPMCanvas(d, dpi=72, bg=0xffffff, configPIL=None, showBoundary=rl_config._unset_):
c = PMCanvas(d.width, d.height, dpi=dpi, bg=bg, configPIL=configPIL)
draw(d, c, 0, 0, showBoundary=showBoundary)
return c
def drawToPIL(d, dpi=72, bg=0xffffff, configPIL=None, showBoundary=rl_config._unset_):
return drawToPMCanvas(d, dpi=dpi, bg=bg, configPIL=configPIL, showBoundary=showBoundary).toPIL()
def drawToPILP(d, dpi=72, bg=0xffffff, configPIL=None, showBoundary=rl_config._unset_):
Image = _getImage()
im = drawToPIL(d, dpi=dpi, bg=bg, configPIL=configPIL, showBoundary=showBoundary)
return im.convert("P", dither=Image.NONE, palette=Image.ADAPTIVE)
def drawToFile(d,fn,fmt='GIF', dpi=72, bg=0xffffff, configPIL=None, showBoundary=rl_config._unset_):
'''create a pixmap and draw drawing, d to it then save as a file
configPIL dict is passed to image save method'''
c = drawToPMCanvas(d, dpi=dpi, bg=bg, configPIL=configPIL, showBoundary=showBoundary)
c.saveToFile(fn,fmt)
def drawToString(d,fmt='GIF', dpi=72, bg=0xffffff, configPIL=None, showBoundary=rl_config._unset_):
s = getStringIO()
drawToFile(d,s,fmt=fmt, dpi=dpi, bg=bg, configPIL=configPIL)
return s.getvalue()
save = drawToFile
def test():
def ext(x):
if x=='tiff': x='tif'
return x
#grab all drawings from the test module and write out.
#make a page of links in HTML to assist viewing.
import os
from reportlab.graphics import testshapes
getAllTestDrawings = testshapes.getAllTestDrawings
drawings = []
if not os.path.isdir('pmout'):
os.mkdir('pmout')
htmlTop = """<html><head><title>renderPM output results</title></head>
<body>
<h1>renderPM results of output</h1>
"""
htmlBottom = """</body>
</html>
"""
html = [htmlTop]
i = 0
#print in a loop, with their doc strings
for (drawing, docstring, name) in getAllTestDrawings(doTTF=hasattr(_renderPM,'ft_get_face')):
fnRoot = 'renderPM%d' % i
if 1 or i==10:
w = int(drawing.width)
h = int(drawing.height)
html.append('<hr><h2>Drawing %s %d</h2>\n<pre>%s</pre>' % (name, i, docstring))
for k in ['gif','tiff', 'png', 'jpg', 'pct']:
if k in ['gif','png','jpg','pct']:
html.append('<p>%s format</p>\n' % string.upper(k))
try:
filename = '%s.%s' % (fnRoot, ext(k))
fullpath = os.path.join('pmout', filename)
if os.path.isfile(fullpath):
os.remove(fullpath)
if k=='pct':
from reportlab.lib.colors import white
drawToFile(drawing,fullpath,fmt=k,configPIL={'transparent':white})
else:
drawToFile(drawing,fullpath,fmt=k)
if k in ['gif','png','jpg']:
html.append('<img src="%s" border="1"><br>\n' % filename)
print 'wrote',fullpath
except AttributeError:
print 'Problem drawing %s file'%k
raise
if os.environ.get('RL_NOEPSPREVIEW','0')=='1': drawing.__dict__['preview'] = 0
drawing.save(formats=['eps','pdf'],outDir='pmout',fnRoot=fnRoot)
i = i + 1
#if i==10: break
html.append(htmlBottom)
htmlFileName = os.path.join('pmout', 'index.html')
open(htmlFileName, 'w').writelines(html)
if sys.platform=='mac':
from reportlab.lib.utils import markfilename
markfilename(htmlFileName,ext='HTML')
print 'wrote %s' % htmlFileName
if __name__=='__main__':
test()

View File

@ -1,859 +0,0 @@
#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/graphics/renderPS.py
__version__=''' $Id$ '''
import string, types
from reportlab.pdfbase.pdfmetrics import getFont, stringWidth # for font info
from reportlab.lib.utils import fp_str, getStringIO
from reportlab.lib.colors import black
from reportlab.graphics.renderbase import StateTracker, getStateDelta
from reportlab.graphics.shapes import STATE_DEFAULTS
import math
from types import StringType
from operator import getitem
from reportlab import rl_config
# we need to create encoding vectors for each font we use, or they will
# come out in Adobe's old StandardEncoding, which NOBODY uses.
PS_WinAnsiEncoding="""
/RE { %def
findfont begin
currentdict dup length dict begin
{ %forall
1 index /FID ne { def } { pop pop } ifelse
} forall
/FontName exch def dup length 0 ne { %if
/Encoding Encoding 256 array copy def
0 exch { %forall
dup type /nametype eq { %ifelse
Encoding 2 index 2 index put
pop 1 add
}{ %else
exch pop
} ifelse
} forall
} if pop
currentdict dup end end
/FontName get exch definefont pop
} bind def
/WinAnsiEncoding [
39/quotesingle 96/grave 128/euro 130/quotesinglbase/florin/quotedblbase
/ellipsis/dagger/daggerdbl/circumflex/perthousand
/Scaron/guilsinglleft/OE 145/quoteleft/quoteright
/quotedblleft/quotedblright/bullet/endash/emdash
/tilde/trademark/scaron/guilsinglright/oe/dotlessi
159/Ydieresis 164/currency 166/brokenbar 168/dieresis/copyright
/ordfeminine 172/logicalnot 174/registered/macron/ring
177/plusminus/twosuperior/threesuperior/acute/mu
183/periodcentered/cedilla/onesuperior/ordmasculine
188/onequarter/onehalf/threequarters 192/Agrave/Aacute
/Acircumflex/Atilde/Adieresis/Aring/AE/Ccedilla
/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute
/Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute
/Ocircumflex/Otilde/Odieresis/multiply/Oslash
/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn
/germandbls/agrave/aacute/acircumflex/atilde/adieresis
/aring/ae/ccedilla/egrave/eacute/ecircumflex
/edieresis/igrave/iacute/icircumflex/idieresis
/eth/ntilde/ograve/oacute/ocircumflex/otilde
/odieresis/divide/oslash/ugrave/uacute/ucircumflex
/udieresis/yacute/thorn/ydieresis
] def
"""
class PSCanvas:
def __init__(self,size=(300,300), PostScriptLevel=2):
self.width, self.height = size
self.code = []
self._sep = '\n'
self._strokeColor = self._fillColor = self._lineWidth = \
self._font = self._fontSize = self._lineCap = \
self._lineJoin = self._color = None
self._fontsUsed = [] # track them as we go
self.setFont(STATE_DEFAULTS['fontName'],STATE_DEFAULTS['fontSize'])
self.setStrokeColor(STATE_DEFAULTS['strokeColor'])
self.setLineCap(2)
self.setLineJoin(0)
self.setLineWidth(1)
self.PostScriptLevel=PostScriptLevel
def comment(self,msg):
self.code.append('%'+msg)
def drawImage(self, image, x1,y1, x2=None,y2=None): # Postscript Level2 version
# select between postscript level 1 or level 2
if PostScriptLevel==1:
self._drawImageLevel1(image, x1,y1, x2=None,y2=None)
elif PostScriptLevel == 2 :
self._drawImageLevel2(image, x1,y1, x2=None,y2=None)
else :
raise 'PostScriptLevelException'
def clear(self):
self.code.append('showpage') # ugh, this makes no sense oh well.
def save(self,f=None):
if not hasattr(f,'write'):
file = open(f,'wb')
else:
file = f
if self.code[-1]!='showpage': self.clear()
self.code.insert(0,'''\
%%!PS-Adobe-3.0 EPSF-3.0
%%%%BoundingBox: 0 0 %d %d
%%%% Initialization:
/m {moveto} bind def
/l {lineto} bind def
/c {curveto} bind def
%s
''' % (self.width,self.height, PS_WinAnsiEncoding))
# for each font used, reencode the vectors
fontReencode = []
for fontName in self._fontsUsed:
fontReencode.append('WinAnsiEncoding /%s /%s RE' % (fontName, fontName))
self.code.insert(1, string.join(fontReencode, self._sep))
file.write(string.join(self.code,self._sep))
if file is not f:
file.close()
from reportlab.lib.utils import markfilename
markfilename(f,creatorcode='XPR3',filetype='EPSF')
def saveState(self):
self.code.append('gsave')
def restoreState(self):
self.code.append('grestore')
def stringWidth(self, s, font=None, fontSize=None):
"""Return the logical width of the string if it were drawn
in the current font (defaults to self.font)."""
font = font or self._font
fontSize = fontSize or self._fontSize
return stringWidth(s, font, fontSize)
def setLineCap(self,v):
if self._lineCap!=v:
self._lineCap = v
self.code.append('%d setlinecap'%v)
def setLineJoin(self,v):
if self._lineJoin!=v:
self._lineJoin = v
self.code.append('%d setlinejoin'%v)
def setDash(self, array=[], phase=0):
"""Two notations. pass two numbers, or an array and phase"""
# copied and modified from reportlab.canvas
psoperation = "setdash"
if type(array) == types.IntType or type(array) == types.FloatType:
self._code.append('[%s %s] 0 %s' % (array, phase, psoperation))
elif type(array) == types.ListType or type(array) == types.TupleType:
assert phase >= 0, "phase is a length in user space"
textarray = string.join(map(str, array))
self.code.append('[%s] %s %s' % (textarray, phase, psoperation))
def setStrokeColor(self, color):
self._strokeColor = color
self.setColor(color)
def setColor(self, color):
if self._color!=color:
self._color = color
if color:
if hasattr(color, "cyan"):
self.code.append('%s setcmykcolor' % fp_str(color.cyan, color.magenta, color.yellow, color.black))
else:
self.code.append('%s setrgbcolor' % fp_str(color.red, color.green, color.blue))
def setFillColor(self, color):
self._fillColor = color
self.setColor(color)
def setLineWidth(self, width):
if width != self._lineWidth:
self._lineWidth = width
self.code.append('%s setlinewidth' % width)
def setFont(self,font,fontSize,leading=None):
if self._font!=font or self._fontSize!=fontSize:
self._fontCodeLoc = len(self.code)
self._font = font
self._fontSize = fontSize
self.code.append('')
def line(self, x1, y1, x2, y2):
if self._strokeColor != None:
self.code.append('%s m %s l stroke' % (fp_str(x1, y1), fp_str(x2, y2)))
def _escape(self, s):
'''
return a copy of string s with special characters in postscript strings
escaped with backslashes.
Have not handled characters that are converted normally in python strings
i.e. \n -> newline
'''
str = string.replace(s, chr(0x5C), r'\\' )
str = string.replace(str, '(', '\(' )
str = string.replace(str, ')', '\)')
return str
def drawString(self, x, y, s, angle=0):
if self._fillColor != None:
if not self.code[self._fontCodeLoc]:
psName = getFont(self._font).face.name
self.code[self._fontCodeLoc]='(%s) findfont %s scalefont setfont' % (psName,fp_str(self._fontSize))
if psName not in self._fontsUsed:
self._fontsUsed.append(psName)
self.setColor(self._fillColor)
s = self._escape(s)
## before inverting...
## if angle == 0 : # do special case of angle = 0 first. Avoids a bunch of gsave/grestore ops
## self.code.append('%s m 1 -1 scale (%s) show 1 -1 scale' % (fp_str(x,y),s))
## else : # general case, rotated text
## self.code.append('gsave %s %s translate %s rotate' % (x,y,angle))
## self.code.append('0 0 m 1 -1 scale (%s) show' % s)
## self.code.append('grestore')
if angle == 0 : # do special case of angle = 0 first. Avoids a bunch of gsave/grestore ops
self.code.append('%s m (%s) show ' % (fp_str(x,y),s))
else : # general case, rotated text
self.code.append('gsave %s %s translate %s rotate' % (x,y,angle))
self.code.append('0 0 m (%s) show' % s)
self.code.append('grestore')
def drawCentredString(self, x, y, text, text_anchor='middle'):
if self.fillColor is not None:
textLen = stringWidth(text, self._font,self._fontSize)
if text_anchor=='end':
x = x-textLen
elif text_anchor=='middle':
x = x - textLen/2
self.drawString(x,y,text)
def drawRightString(self, text, x, y):
self.drawCentredString(text,x,y,text_anchor='end')
def drawCurve(self, x1, y1, x2, y2, x3, y3, x4, y4, closed=0):
codeline = '%s m %s curveto'
data = (fp_str(x1, y1), fp_str(x2, y2, x3, y3, x4, y4))
if self._fillColor != None:
self.setColor(self._fillColor)
self.code.append((codeline % data) + ' eofill')
if self._strokeColor != None:
self.setColor(self._strokeColor)
self.code.append((codeline % data)
+ ((closed and ' closepath') or '')
+ ' stroke')
########################################################################################
def rect(self, x1,y1, x2,y2, stroke=1, fill=1):
"Draw a rectangle between x1,y1, and x2,y2"
# Path is drawn in counter-clockwise direction"
x1, x2 = min(x1,x2), max(x1, x2) # from piddle.py
y1, y2 = min(y1,y2), max(y1, y2)
self.polygon(((x1,y1),(x2,y1),(x2,y2),(x1,y2)), closed=1, stroke=stroke, fill = fill)
def roundRect(self, x1,y1, x2,y2, rx=8, ry=8):
"""Draw a rounded rectangle between x1,y1, and x2,y2,
with corners inset as ellipses with x radius rx and y radius ry.
These should have x1<x2, y1<y2, rx>0, and ry>0."""
# Path is drawn in counter-clockwise direction
x1, x2 = min(x1,x2), max(x1, x2) # from piddle.py
y1, y2 = min(y1,y2), max(y1, y2)
# Note: arcto command draws a line from current point to beginning of arc
# save current matrix, translate to center of ellipse, scale by rx ry, and draw
# a circle of unit radius in counterclockwise dir, return to original matrix
# arguments are (cx, cy, rx, ry, startAngle, endAngle)
ellipsePath = 'matrix currentmatrix %s %s translate %s %s scale 0 0 1 %s %s arc setmatrix'
# choice between newpath and moveTo beginning of arc
# go with newpath for precision, does this violate any assumptions in code???
rrCode = ['newpath'] # Round Rect code path
# upper left corner ellipse is first
rrCode.append(ellipsePath % (x1+rx, y1+ry, rx, -ry, 90, 180))
rrCode.append(ellipsePath % (x1+rx, y2-ry, rx, -ry, 180, 270))
rrCode.append(ellipsePath % (x2-rx, y2-ry, rx, -ry, 270, 360))
rrCode.append(ellipsePath % (x2-rx, y1+ry, rx, -ry, 0, 90) )
rrCode.append('closepath')
self._fillAndStroke(rrCode)
def ellipse(self, x1,y1, x2,y2):
"""Draw an orthogonal ellipse inscribed within the rectangle x1,y1,x2,y2.
These should have x1<x2 and y1<y2."""
#Just invoke drawArc to actually draw the ellipse
self.drawArc(x1,y1, x2,y2)
def circle(self, xc, yc, r):
self.ellipse(xc-r,yc-r, xc+r,yc+r)
def drawArc(self, x1,y1, x2,y2, startAng=0, extent=360, fromcenter=0):
"""Draw a partial ellipse inscribed within the rectangle x1,y1,x2,y2,
starting at startAng degrees and covering extent degrees. Angles
start with 0 to the right (+x) and increase counter-clockwise.
These should have x1<x2 and y1<y2."""
#calculate centre of ellipse
#print "x1,y1,x2,y2,startAng,extent,fromcenter", x1,y1,x2,y2,startAng,extent,fromcenter
cx, cy = (x1+x2)/2.0, (y1+y2)/2.0
rx, ry = (x2-x1)/2.0, (y2-y1)/2.0
codeline = self._genArcCode(x1, y1, x2, y2, startAng, extent)
startAngleRadians = math.pi*startAng/180.0
extentRadians = math.pi*extent/180.0
endAngleRadians = startAngleRadians + extentRadians
codelineAppended = 0
# fill portion
if self._fillColor != None:
self.setColor(self._fillColor)
self.code.append(codeline)
codelineAppended = 1
if self._strokeColor!=None: self.code.append('gsave')
self.lineTo(cx,cy)
self.code.append('eofill')
if self._strokeColor!=None: self.code.append('grestore')
# stroke portion
if self._strokeColor != None:
# this is a bit hacked up. There is certainly a better way...
self.setColor(self._strokeColor)
(startx, starty) = (cx+rx*math.cos(startAngleRadians), cy+ry*math.sin(startAngleRadians))
if not codelineAppended:
self.code.append(codeline)
if fromcenter:
# move to center
self.lineTo(cx,cy)
self.lineTo(startx, starty)
self.code.append('closepath')
self.code.append('stroke')
def _genArcCode(self, x1, y1, x2, y2, startAng, extent):
"Calculate the path for an arc inscribed in rectangle defined by (x1,y1),(x2,y2)"
#calculate semi-minor and semi-major axes of ellipse
xScale = abs((x2-x1)/2.0)
yScale = abs((y2-y1)/2.0)
#calculate centre of ellipse
x, y = (x1+x2)/2.0, (y1+y2)/2.0
codeline = 'matrix currentmatrix %s %s translate %s %s scale 0 0 1 %s %s %s setmatrix'
if extent >= 0:
arc='arc'
else:
arc='arcn'
data = (x,y, xScale, yScale, startAng, startAng+extent, arc)
return codeline % data
def polygon(self, p, closed=0, stroke=1, fill=1):
assert len(p) >= 2, 'Polygon must have 2 or more points'
start = p[0]
p = p[1:]
polyCode = []
polyCode.append("%s m" % fp_str(start))
for point in p:
polyCode.append("%s l" % fp_str(point))
if closed:
polyCode.append("closepath")
self._fillAndStroke(polyCode,stroke=stroke,fill=fill)
def lines(self, lineList, color=None, width=None):
if self._strokeColor != None:
self._setColor(self._strokeColor)
codeline = '%s m %s l stroke'
for line in lineList:
self.code.append(codeline % (fp_str(line[0]),fp_str(line[1])))
def moveTo(self,x,y):
self.code.append('%s m' % fp_str(x, y))
def lineTo(self,x,y):
self.code.append('%s l' % fp_str(x, y))
def curveTo(self,x1,y1,x2,y2,x3,y3):
self.code.append('%s c' % fp_str(x1,y1,x2,y2,x3,y3))
def closePath(self):
self.code.append('closepath')
def polyLine(self, p):
assert len(p) >= 1, 'Polyline must have 1 or more points'
if self._strokeColor != None:
self.setColor(self._strokeColor)
self.moveTo(p[0][0], p[0][1])
for t in p[1:]:
self.lineTo(t[0], t[1])
self.code.append('stroke')
def drawFigure(self, partList, closed=0):
figureCode = []
first = 1
for part in partList:
op = part[0]
args = list(part[1:])
if op == figureLine:
if first:
first = 0
figureCode.append("%s m" % fp_str(args[:2]))
else:
figureCode.append("%s l" % fp_str(args[:2]))
figureCode.append("%s l" % fp_str(args[2:]))
elif op == figureArc:
first = 0
x1,y1,x2,y2,startAngle,extent = args[:6]
figureCode.append(self._genArcCode(x1,y1,x2,y2,startAngle,extent))
elif op == figureCurve:
if first:
first = 0
figureCode.append("%s m" % fp_str(args[:2]))
else:
figureCode.append("%s l" % fp_str(args[:2]))
figureCode.append("%s curveto" % fp_str(args[2:]))
else:
raise TypeError, "unknown figure operator: "+op
if closed:
figureCode.append("closepath")
self._fillAndStroke(figureCode)
def _fillAndStroke(self,code,clip=0,fill=1,stroke=1):
fill = self._fillColor and fill
stroke = self._strokeColor and stroke
if fill or stroke or clip:
self.code.extend(code)
if fill:
if stroke or clip: self.code.append("gsave")
self.setColor(self._fillColor)
self.code.append("eofill")
if stroke or clip: self.code.append("grestore")
if stroke:
if clip: self.code.append("gsave")
self.setColor(self._strokeColor)
self.code.append("stroke")
if clip: self.code.append("grestore")
if clip:
self.code.append("clip")
self.code.append("newpath")
def translate(self,x,y):
self.code.append('%s translate' % fp_str(x,y))
def scale(self,x,y):
self.code.append('%s scale' % fp_str(x,y))
def transform(self,a,b,c,d,e,f):
self.code.append('[%s] concat' % fp_str(a,b,c,d,e,f))
def _drawTimeResize(self,w,h):
'''if this is used we're probably in the wrong world'''
self.width, self.height = w, h
############################################################################################
# drawImage(self. image, x1, y1, x2=None, y2=None) is now defined by either _drawImageLevel1
# ._drawImageLevel2, the choice is made in .__init__ depending on option
def _drawImageLevel1(self, image, x1, y1, x2=None,y2=None):
# Postscript Level1 version available for fallback mode when Level2 doesn't work
"""drawImage(self,image,x1,y1,x2=None,y2=None) : If x2 and y2 are ommitted, they are
calculated from image size. (x1,y1) is upper left of image, (x2,y2) is lower right of
image in piddle coordinates."""
# For now let's start with 24 bit RGB images (following piddlePDF again)
print "Trying to drawImage in piddlePS"
component_depth = 8
myimage = image.convert('RGB')
imgwidth, imgheight = myimage.size
if not x2:
x2 = imgwidth + x1
if not y2:
y2 = y1 + imgheight
drawwidth = x2 - x1
drawheight = y2 - y1
print 'Image size (%d, %d); Draw size (%d, %d)' % (imgwidth, imgheight, drawwidth, drawheight)
# now I need to tell postscript how big image is
# "image operators assume that they receive sample data from
# their data source in x-axis major index order. The coordinate
# of the lower-left corner of the first sample is (0,0), of the
# second (1,0) and so on" -PS2 ref manual p. 215
#
# The ImageMatrix maps unit squre of user space to boundary of the source image
#
# The CurrentTransformationMatrix (CTM) maps the unit square of
# user space to the rect...on the page that is to receive the
# image. A common ImageMatrix is [width 0 0 -height 0 height]
# (for a left to right, top to bottom image )
# first let's map the user coordinates start at offset x1,y1 on page
self.code.extend([
'gsave',
'%s %s translate' % (x1,-y1 - drawheight), # need to start are lower left of image
'%s %s scale' % (drawwidth,drawheight),
'/scanline %d 3 mul string def' % imgwidth # scanline by multiples of image width
])
# now push the dimensions and depth info onto the stack
# and push the ImageMatrix to map the source to the target rectangle (see above)
# finally specify source (PS2 pp. 225 ) and by exmample
self.code.extend([
'%s %s %s' % (imgwidth, imgheight, component_depth),
'[%s %s %s %s %s %s]' % (imgwidth, 0, 0, -imgheight, 0, imgheight),
'{ currentfile scanline readhexstring pop } false 3',
'colorimage '
])
# data source output--now we just need to deliver a hex encode
# series of lines of the right overall size can follow
# piddlePDF again
rawimage = myimage.tostring()
assert(len(rawimage) == imgwidth*imgheight, 'Wrong amount of data for image')
#compressed = zlib.compress(rawimage) # no zlib at moment
hex_encoded = self._AsciiHexEncode(rawimage)
# write in blocks of 78 chars per line
outstream = getStringIO(hex_encoded)
dataline = outstream.read(78)
while dataline <> "":
self.code.append(dataline)
dataline= outstream.read(78)
self.code.append('% end of image data') # for clarity
self.code.append('grestore') # return coordinates to normal
# end of drawImage
def _AsciiHexEncode(self, input): # also based on piddlePDF
"Helper function used by images"
output = getStringIO()
for char in input:
output.write('%02x' % ord(char))
return output.getvalue()
def _drawImageLevel2(self, image, x1,y1, x2=None,y2=None): # Postscript Level2 version
Image = import_Image()
if not Image: return
### what sort of image are we to draw
if image.mode=='L' :
print 'found image.mode= L'
imBitsPerComponent = 8
imNumComponents = 1
myimage = image
elif image.mode == '1':
print 'found image.mode= 1'
myimage = image.convert('L')
imNumComponents = 1
myimage = image
else :
myimage = image.convert('RGB')
imNumComponents = 3
imBitsPerComponent = 8
imwidth, imheight = myimage.size
# print 'imwidth = %s, imheight = %s' % myimage.size
if not x2:
x2 = imwidth + x1
if not y2:
y2 = y1 + imheight
drawwidth = x2 - x1
drawheight = y2 - y1
self.code.extend([
'gsave',
'%s %s translate' % (x1,-y1 - drawheight), # need to start are lower left of image
'%s %s scale' % (drawwidth,drawheight)])
if imNumComponents == 3 :
self.code.append('/DeviceRGB setcolorspace')
elif imNumComponents == 1 :
self.code.append('/DeviceGray setcolorspace')
print 'setting colorspace gray'
# create the image dictionary
self.code.append("""
<<
/ImageType 1
/Width %d /Height %d %% dimensions of source image
/BitsPerComponent %d""" % (imwidth, imheight, imBitsPerComponent) )
if imNumComponents == 1:
self.code.append('/Decode [0 1]')
if imNumComponents == 3:
self.code.append('/Decode [0 1 0 1 0 1] %% decode color values normally')
self.code.extend([ '/ImageMatrix [%s 0 0 %s 0 %s]' % (imwidth, -imheight, imheight),
'/DataSource currentfile /ASCIIHexDecode filter',
'>> % End image dictionary',
'image'])
# after image operator just need to dump image dat to file as hexstring
rawimage = myimage.tostring()
assert(len(rawimage) == imwidth*imheight, 'Wrong amount of data for image')
#compressed = zlib.compress(rawimage) # no zlib at moment
hex_encoded = self._AsciiHexEncode(rawimage)
# write in blocks of 78 chars per line
outstream = getStringIO(hex_encoded)
dataline = outstream.read(78)
while dataline <> "":
self.code.append(dataline)
dataline= outstream.read(78)
self.code.append('> % end of image data') # > is EOD for hex encoded filterfor clarity
self.code.append('grestore') # return coordinates to normal
# renderpdf - draws them onto a canvas
"""Usage:
from reportlab.graphics import renderPS
renderPS.draw(drawing, canvas, x, y)
Execute the script to see some test drawings."""
from shapes import *
from renderbase import Renderer
# hack so we only get warnings once each
#warnOnce = WarnOnce()
# the main entry point for users...
def draw(drawing, canvas, x=0, y=0, showBoundary=rl_config.showBoundary):
"""As it says"""
R = _PSRenderer()
R.draw(drawing, canvas, x, y, showBoundary=showBoundary)
def _pointsFromList(L):
'''
given a list of coordinates [x0, y0, x1, y1....]
produce a list of points [(x0,y0), (y1,y0),....]
'''
P=[]
for i in range(0,len(L),2):
P.append((L[i],L[i+1]))
return P
class _PSRenderer(Renderer):
"""This draws onto a EPS document. It needs to be a class
rather than a function, as some EPS-specific state tracking is
needed outside of the state info in the SVG model."""
def __init__(self):
self._tracker = StateTracker()
def drawNode(self, node):
"""This is the recursive method called for each node
in the tree"""
self._canvas.comment('begin node %s'%`node`)
color = self._canvas._color
if not (isinstance(node, Path) and node.isClipPath):
self._canvas.saveState()
#apply state changes
deltas = getStateDelta(node)
self._tracker.push(deltas)
self.applyStateChanges(deltas, {})
#draw the object, or recurse
self.drawNodeDispatcher(node)
rDeltas = self._tracker.pop()
if not (isinstance(node, Path) and node.isClipPath):
self._canvas.restoreState()
self._canvas.comment('end node %s'%`node`)
self._canvas._color = color
#restore things we might have lost (without actually doing anything).
for k, v in rDeltas.items():
if self._restores.has_key(k):
setattr(self._canvas,self._restores[k],v)
## _restores = {'stroke':'_stroke','stroke_width': '_lineWidth','stroke_linecap':'_lineCap',
## 'stroke_linejoin':'_lineJoin','fill':'_fill','font_family':'_font',
## 'font_size':'_fontSize'}
_restores = {'strokeColor':'_strokeColor','strokeWidth': '_lineWidth','strokeLineCap':'_lineCap',
'strokeLineJoin':'_lineJoin','fillColor':'_fillColor','fontName':'_font',
'fontSize':'_fontSize'}
def drawRect(self, rect):
if rect.rx == rect.ry == 0:
#plain old rectangle
self._canvas.rect(
rect.x, rect.y,
rect.x+rect.width, rect.y+rect.height)
else:
#cheat and assume ry = rx; better to generalize
#pdfgen roundRect function. TODO
self._canvas.roundRect(
rect.x, rect.y,
rect.x+rect.width, rect.y+rect.height, rect.rx, rect.ry
)
def drawLine(self, line):
if self._canvas._strokeColor:
self._canvas.line(line.x1, line.y1, line.x2, line.y2)
def drawCircle(self, circle):
self._canvas.circle( circle.cx, circle.cy, circle.r)
def drawWedge(self, wedge):
yradius, radius1, yradius1 = wedge._xtraRadii()
if (radius1==0 or radius1 is None) and (yradius1==0 or yradius1 is None):
startangledegrees = wedge.startangledegrees
endangledegrees = wedge.endangledegrees
centerx= wedge.centerx
centery = wedge.centery
radius = wedge.radius
extent = endangledegrees - startangledegrees
self._canvas.drawArc(centerx-radius, centery-yradius, centerx+radius, centery+yradius,
startangledegrees, extent, fromcenter=1)
else:
self.drawPolygon(wedge.asPolygon())
def drawPolyLine(self, p):
if self._canvas._strokeColor:
self._canvas.polyLine(_pointsFromList(p.points))
def drawEllipse(self, ellipse):
#need to convert to pdfgen's bounding box representation
x1 = ellipse.cx - ellipse.rx
x2 = ellipse.cx + ellipse.rx
y1 = ellipse.cy - ellipse.ry
y2 = ellipse.cy + ellipse.ry
self._canvas.ellipse(x1,y1,x2,y2)
def drawPolygon(self, p):
self._canvas.polygon(_pointsFromList(p.points), closed=1)
def drawString(self, stringObj):
if self._canvas._fillColor:
S = self._tracker.getState()
text_anchor, x, y, text = S['textAnchor'], stringObj.x,stringObj.y,stringObj.text
if not text_anchor in ['start','inherited']:
font, fontSize = S['fontName'], S['fontSize']
textLen = stringWidth(text, font,fontSize)
if text_anchor=='end':
x = x-textLen
elif text_anchor=='middle':
x = x - textLen/2
else:
raise ValueError, 'bad value for text_anchor '+str(text_anchor)
self._canvas.drawString(x,y,text)
def drawPath(self, path):
from reportlab.graphics.shapes import _renderPath
c = self._canvas
drawFuncs = (c.moveTo, c.lineTo, c.curveTo, c.closePath)
isClosed = _renderPath(path, drawFuncs)
if not isClosed:
c._fillColor = None
c._fillAndStroke([], clip=path.isClipPath)
def applyStateChanges(self, delta, newState):
"""This takes a set of states, and outputs the operators
needed to set those properties"""
for key, value in delta.items():
if key == 'transform':
self._canvas.transform(value[0], value[1], value[2],
value[3], value[4], value[5])
elif key == 'strokeColor':
#this has different semantics in PDF to SVG;
#we always have a color, and either do or do
#not apply it; in SVG one can have a 'None' color
self._canvas.setStrokeColor(value)
elif key == 'strokeWidth':
self._canvas.setLineWidth(value)
elif key == 'strokeLineCap': #0,1,2
self._canvas.setLineCap(value)
elif key == 'strokeLineJoin':
self._canvas.setLineJoin(value)
elif key == 'strokeDashArray':
if value:
self._canvas.setDash(value)
else:
self._canvas.setDash()
## elif key == 'stroke_opacity':
## warnOnce('Stroke Opacity not supported yet')
elif key == 'fillColor':
#this has different semantics in PDF to SVG;
#we always have a color, and either do or do
#not apply it; in SVG one can have a 'None' color
self._canvas.setFillColor(value)
## elif key == 'fill_rule':
## warnOnce('Fill rules not done yet')
## elif key == 'fill_opacity':
## warnOnce('Fill opacity not done yet')
elif key in ['fontSize', 'fontName']:
# both need setting together in PDF
# one or both might be in the deltas,
# so need to get whichever is missing
fontname = delta.get('fontName', self._canvas._font)
fontsize = delta.get('fontSize', self._canvas._fontSize)
self._canvas.setFont(fontname, fontsize)
def drawToFile(d,fn, showBoundary=rl_config.showBoundary):
c = PSCanvas((d.width,d.height))
draw(d, c, 0, 0, showBoundary=showBoundary)
c.save(fn)
def drawToString(d, showBoundary=rl_config.showBoundary):
"Returns a PS as a string in memory, without touching the disk"
s = getStringIO()
drawToFile(d, s, showBoundary=showBoundary)
return s.getvalue()
#########################################################
#
# test code. First, defin a bunch of drawings.
# Routine to draw them comes at the end.
#
#########################################################
def test(outdir='epsout'):
import os
# print all drawings and their doc strings from the test
# file
if not os.path.isdir(outdir):
os.mkdir(outdir)
#grab all drawings from the test module
import testshapes
drawings = []
for funcname in dir(testshapes):
#if funcname[0:11] == 'getDrawing2':
# print 'hacked to only show drawing 2'
if funcname[0:10] == 'getDrawing':
drawing = eval('testshapes.' + funcname + '()') #execute it
docstring = eval('testshapes.' + funcname + '.__doc__')
drawings.append((drawing, docstring))
i = 0
for (d, docstring) in drawings:
filename = outdir + os.sep + 'renderPS_%d.eps'%i
drawToFile(d,filename)
print 'saved', filename
i = i + 1
if __name__=='__main__':
import sys
if len(sys.argv)>1:
outdir = sys.argv[1]
else:
outdir = 'epsout'
test(outdir)

View File

@ -1,813 +0,0 @@
"""An experimental SVG renderer for the ReportLab graphics framework.
This will create SVG code from the ReportLab Graphics API (RLG).
To read existing SVG code and convert it into ReportLab graphics
objects download the svglib module here:
http://python.net/~gherman/#svglib
"""
import math, string, types, sys, os
from types import StringType
from operator import getitem
from reportlab.pdfbase.pdfmetrics import stringWidth # for font info
from reportlab.lib.utils import fp_str
from reportlab.lib.colors import black
from reportlab.graphics.renderbase import StateTracker, getStateDelta, Renderer
from reportlab.graphics.shapes import STATE_DEFAULTS, Path, UserNode
from reportlab.graphics.shapes import * # (only for test0)
from reportlab import rl_config
from reportlab.lib.utils import getStringIO
from xml.dom import getDOMImplementation
### some constants ###
sin = math.sin
cos = math.cos
pi = math.pi
LINE_STYLES = 'stroke-width stroke-linecap stroke fill stroke-dasharray'
TEXT_STYLES = 'font-family font-size'
### top-level user function ###
def drawToString(d, showBoundary=rl_config.showBoundary):
"Returns a SVG as a string in memory, without touching the disk"
s = getStringIO()
drawToFile(d, s, showBoundary=showBoundary)
return s.getvalue()
def drawToFile(d, fn, showBoundary=rl_config.showBoundary):
c = SVGCanvas((d.width, d.height))
draw(d, c, 0, 0, showBoundary=showBoundary)
c.save(fn)
def draw(drawing, canvas, x=0, y=0, showBoundary=rl_config.showBoundary):
"""As it says."""
r = _SVGRenderer()
r.draw(drawing, canvas, x, y, showBoundary=showBoundary)
### helper functions ###
def _pointsFromList(L):
"""
given a list of coordinates [x0, y0, x1, y1....]
produce a list of points [(x0,y0), (y1,y0),....]
"""
P=[]
for i in range(0,len(L), 2):
P.append((L[i], L[i+1]))
return P
def transformNode(doc, newTag, node=None, **attrDict):
"""Transform a DOM node into new node and copy selected attributes.
Creates a new DOM node with tag name 'newTag' for document 'doc'
and copies selected attributes from an existing 'node' as provided
in 'attrDict'. The source 'node' can be None. Attribute values will
be converted to strings.
E.g.
n = transformNode(doc, "node1", x="0", y="1")
-> DOM node for <node1 x="0" y="1"/>
n = transformNode(doc, "node1", x=0, y=1+1)
-> DOM node for <node1 x="0" y="2"/>
n = transformNode(doc, "node1", node0, x="x0", y="x0", zoo=bar())
-> DOM node for <node1 x="[node0.x0]" y="[node0.y0]" zoo="[bar()]"/>
"""
newNode = doc.createElement(newTag)
for newAttr, attr in attrDict.items():
sattr = str(attr)
if not node:
newNode.setAttribute(newAttr, sattr)
else:
attrVal = node.getAttribute(sattr)
newNode.setAttribute(newAttr, attrVal or sattr)
return newNode
### classes ###
class SVGCanvas:
def __init__(self, size=(300,300)):
self.verbose = 0
self.width, self.height = self.size = size
# self.height = size[1]
self.code = []
self.style = {}
self.path = ''
self._strokeColor = self._fillColor = self._lineWidth = \
self._font = self._fontSize = self._lineCap = \
self._lineJoin = self._color = None
implementation = getDOMImplementation('minidom')
self.doc = implementation.createDocument(None, "svg", None)
self.svg = self.doc.documentElement
self.svg.setAttribute("width", str(size[0]))
self.svg.setAttribute("height", str(self.height))
title = self.doc.createElement('title')
text = self.doc.createTextNode('...')
title.appendChild(text)
self.svg.appendChild(title)
desc = self.doc.createElement('desc')
text = self.doc.createTextNode('...')
desc.appendChild(text)
self.svg.appendChild(desc)
self.setFont(STATE_DEFAULTS['fontName'], STATE_DEFAULTS['fontSize'])
self.setStrokeColor(STATE_DEFAULTS['strokeColor'])
self.setLineCap(2)
self.setLineJoin(0)
self.setLineWidth(1)
# Add a rectangular clipping path identical to view area.
clipPath = transformNode(self.doc, "clipPath", id="clip")
clipRect = transformNode(self.doc, "rect", x=0, y=0,
width=self.width, height=self.height)
clipPath.appendChild(clipRect)
self.svg.appendChild(clipPath)
self.groupTree = transformNode(self.doc, "g",
id="group",
transform="scale(1,-1) translate(0,-%d)" % self.height,
style="clip-path: url(#clip)")
self.svg.appendChild(self.groupTree)
self.currGroup = self.groupTree
def save(self, f=None):
if type(f) is StringType:
file = open(f, 'w')
else:
file = f
file.write("""\
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20000303 Stylable//EN" "http://www.w3.org/TR/2000/03/WD-SVG-20000303/DTD/svg-20000303-stylable.dtd" >\n""")
# use = self.doc.createElement('use')
# use.setAttribute("xlink:href", "#group")
# use.setAttribute("transform", "scale(1, -1)")
# self.svg.appendChild(use)
result = self.svg.toprettyxml(indent=" ")
file.write(result)
if file is not f:
file.close()
### helpers ###
def NOTUSED_stringWidth(self, s, font=None, fontSize=None):
"""Return the logical width of the string if it were drawn
in the current font (defaults to self.font).
"""
font = font or self._font
fontSize = fontSize or self._fontSize
return stringWidth(s, font, fontSize)
def _formatStyle(self, include=''):
str = ''
include = string.split(include)
keys = self.style.keys()
if include:
#2.1-safe version of the line below follows:
#keys = filter(lambda k: k in include, keys)
tmp = []
for word in keys:
if word in include:
tmp.append(word)
keys = tmp
items = []
for k in keys:
items.append((k, self.style[k]))
items = map(lambda i: "%s: %s"%(i[0], i[1]), items)
str = string.join(items, '; ') + ';'
return str
def _escape(self, s):
"""
return a copy of string s with special characters in postscript strings
escaped with backslashes.
Have not handled characters that are converted normally in python strings
i.e. \n -> newline
"""
str = string.replace(s, chr(0x5C), r'\\' )
str = string.replace(str, '(', '\(' )
str = string.replace(str, ')', '\)')
return str
def _genArcCode(self, x1, y1, x2, y2, startAng, extent):
"""Calculate the path for an arc inscribed in rectangle defined
by (x1,y1),(x2,y2)."""
return
#calculate semi-minor and semi-major axes of ellipse
xScale = abs((x2-x1)/2.0)
yScale = abs((y2-y1)/2.0)
#calculate centre of ellipse
x, y = (x1+x2)/2.0, (y1+y2)/2.0
codeline = 'matrix currentmatrix %s %s translate %s %s scale 0 0 1 %s %s %s setmatrix'
if extent >= 0:
arc='arc'
else:
arc='arcn'
data = (x,y, xScale, yScale, startAng, startAng+extent, arc)
return codeline % data
def _fillAndStroke(self, code, clip=0):
path = transformNode(self.doc, "path",
d=self.path, style=self._formatStyle(LINE_STYLES))
self.currGroup.appendChild(path)
self.path = ''
return
"""
if self._fillColor or self._strokeColor or clip:
self.code.extend(code)
if self._fillColor:
if self._strokeColor or clip:
self.code.append("gsave")
self.setColor(self._fillColor)
self.code.append("eofill")
if self._strokeColor or clip:
self.code.append("grestore")
if self._strokeColor != None:
if clip: self.code.append("gsave")
self.setColor(self._strokeColor)
self.code.append("stroke")
if clip: self.code.append("grestore")
if clip:
self.code.append("clip")
self.code.append("newpath")
"""
### styles ###
def setLineCap(self, v):
vals = {0:'butt', 1:'round', 2:'square'}
if self._lineCap != v:
self._lineCap = v
self.style['stroke-linecap'] = vals[v]
def setLineJoin(self, v):
vals = {0:'miter', 1:'round', 2:'bevel'}
if self._lineJoin != v:
self._lineJoin = v
self.style['stroke-linecap'] = vals[v]
def setDash(self, array=[], phase=0):
"""Two notations. Pass two numbers, or an array and phase."""
join = string.join
if type(array) in (types.IntType, types.FloatType):
self.style['stroke-dasharray'] = join(map(str, ([array, phase])), ', ')
elif type(array) in (types.ListType, types.TupleType) and len(array) > 0:
assert phase >= 0, "phase is a length in user space"
self.style['stroke-dasharray'] = join(map(str, (array+[phase])), ', ')
def setStrokeColor(self, color):
self._strokeColor = color
self.setColor(color)
if color == None:
self.style['stroke'] = 'none'
else:
r, g, b = color.red, color.green, color.blue
self.style['stroke'] = 'rgb(%d%%,%d%%,%d%%)' % (r*100, g*100, b*100)
def setColor(self, color):
if self._color != color:
self._color = color
def setFillColor(self, color):
self._fillColor = color
self.setColor(color)
if color == None:
self.style['fill'] = 'none'
else:
r, g, b = color.red, color.green, color.blue
self.style['fill'] = 'rgb(%d%%,%d%%,%d%%)' % (r*100, g*100, b*100)
def setLineWidth(self, width):
if width != self._lineWidth:
self._lineWidth = width
self.style['stroke-width'] = width
def setFont(self, font, fontSize):
if self._font != font or self._fontSize != fontSize:
self._font, self._fontSize = (font, fontSize)
self.style['font-family'] = font
self.style['font-size'] = fontSize
### shapes ###
def rect(self, x1,y1, x2,y2, rx=8, ry=8):
"Draw a rectangle between x1,y1 and x2,y2."
if self.verbose: print "+++ SVGCanvas.rect"
rect = transformNode(self.doc, "rect",
x=x1, y=y1, width=x2-x1, height=y2-y1,
style=self._formatStyle(LINE_STYLES))
self.currGroup.appendChild(rect)
def roundRect(self, x1,y1, x2,y2, rx=8, ry=8):
"""Draw a rounded rectangle between x1,y1 and x2,y2.
Corners inset as ellipses with x-radius rx and y-radius ry.
These should have x1<x2, y1<y2, rx>0, and ry>0.
"""
rect = transformNode(self.doc, "rect",
x=x1, y=y1, width=x2-x1, height=y2-y1, rx=rx, ry=ry,
style=self._formatStyle(LINE_STYLES))
self.currGroup.appendChild(rect)
def drawString(self, s, x, y, angle=0):
if self.verbose: print "+++ SVGCanvas.drawString"
if self._fillColor != None:
self.setColor(self._fillColor)
s = self._escape(s)
st = self._formatStyle(TEXT_STYLES)
if angle != 0:
st = st + " rotate(%f %f %f);" % (angle, x, y)
st = st + " fill: %s;" % self.style['fill']
text = transformNode(self.doc, "text",
x=x, y=y, style=st,
transform="translate(0,%d) scale(1,-1)" % (2*y))
content = self.doc.createTextNode(s)
text.appendChild(content)
self.currGroup.appendChild(text)
def comment(self, data):
"Add a comment."
comment = self.doc.createComment(data)
# self.currGroup.appendChild(comment)
def drawImage(self, image, x1, y1, x2=None, y2=None):
pass
def line(self, x1, y1, x2, y2):
if self._strokeColor != None:
if 0: # something is wrong with line in my SVG viewer...
line = transformNode(self.doc, "line",
x=x1, y=y1, x2=x2, y2=y2,
style=self._formatStyle(LINE_STYLES))
self.currGroup.appendChild(line)
path = transformNode(self.doc, "path",
d="M %f,%f L %f,%f Z" % (x1,y1,x2,y2),
style=self._formatStyle(LINE_STYLES))
self.currGroup.appendChild(path)
def ellipse(self, x1, y1, x2, y2):
"""Draw an orthogonal ellipse inscribed within the rectangle x1,y1,x2,y2.
These should have x1<x2 and y1<y2.
"""
ellipse = transformNode(self.doc, "ellipse",
cx=(x1+x2)/2.0, cy=(y1+y2)/2.0, rx=(x2-x1)/2.0, ry=(y2-y1)/2.0,
style=self._formatStyle(LINE_STYLES))
self.currGroup.appendChild(ellipse)
def circle(self, xc, yc, r):
circle = transformNode(self.doc, "circle",
cx=xc, cy=yc, r=r,
style=self._formatStyle(LINE_STYLES))
self.currGroup.appendChild(circle)
def drawCurve(self, x1, y1, x2, y2, x3, y3, x4, y4, closed=0):
pass
return
codeline = '%s m %s curveto'
data = (fp_str(x1, y1), fp_str(x2, y2, x3, y3, x4, y4))
if self._fillColor != None:
self.setColor(self._fillColor)
self.code.append((codeline % data) + ' eofill')
if self._strokeColor != None:
self.setColor(self._strokeColor)
self.code.append((codeline % data)
+ ((closed and ' closepath') or '')
+ ' stroke')
def drawArc(self, x1,y1, x2,y2, startAng=0, extent=360, fromcenter=0):
"""Draw a partial ellipse inscribed within the rectangle x1,y1,x2,y2.
Starting at startAng degrees and covering extent degrees. Angles
start with 0 to the right (+x) and increase counter-clockwise.
These should have x1<x2 and y1<y2.
"""
cx, cy = (x1+x2)/2.0, (y1+y2)/2.0
rx, ry = (x2-x1)/2.0, (y2-y1)/2.0
mx = rx * cos(startAng*pi/180) + cx
my = ry * sin(startAng*pi/180) + cy
ax = rx * cos((startAng+extent)*pi/180) + cx
ay = ry * sin((startAng+extent)*pi/180) + cy
str = ''
if fromcenter:
str = str + "M %f, %f L %f, %f " % (cx, cy, ax, ay)
if fromcenter:
str = str + "A %f, %f %d %d %d %f, %f " % \
(rx, ry, 0, extent>=180, 0, mx, my)
else:
str = str + "M %f, %f A %f, %f %d %d %d %f, %f Z " % \
(mx, my, rx, ry, 0, extent>=180, 0, mx, my)
if fromcenter:
str = str + "L %f, %f Z " % (cx, cy)
path = transformNode(self.doc, "path",
d=str, style=self._formatStyle())
self.currGroup.appendChild(path)
def polygon(self, points, closed=0):
assert len(points) >= 2, 'Polygon must have 2 or more points'
if self._strokeColor != None:
self.setColor(self._strokeColor)
pairs = []
for i in xrange(len(points)):
pairs.append("%f %f" % (points[i]))
pts = string.join(pairs, ', ')
polyline = transformNode(self.doc, "polygon",
points=pts, style=self._formatStyle(LINE_STYLES))
self.currGroup.appendChild(polyline)
# self._fillAndStroke(polyCode)
def lines(self, lineList, color=None, width=None):
# print "### lineList", lineList
return
if self._strokeColor != None:
self._setColor(self._strokeColor)
codeline = '%s m %s l stroke'
for line in lineList:
self.code.append(codeline % (fp_str(line[0]), fp_str(line[1])))
def polyLine(self, points):
assert len(points) >= 1, 'Polyline must have 1 or more points'
if self._strokeColor != None:
self.setColor(self._strokeColor)
pairs = []
for i in xrange(len(points)):
pairs.append("%f %f" % (points[i]))
pts = string.join(pairs, ', ')
polyline = transformNode(self.doc, "polyline",
points=pts, style=self._formatStyle(LINE_STYLES))
self.currGroup.appendChild(polyline)
### groups ###
def startGroup(self):
if self.verbose: print "+++ begin SVGCanvas.startGroup"
currGroup, group = self.currGroup, transformNode(self.doc, "g", transform="")
currGroup.appendChild(group)
self.currGroup = group
if self.verbose: print "+++ end SVGCanvas.startGroup"
return currGroup
def endGroup(self,currGroup):
if self.verbose: print "+++ begin SVGCanvas.endGroup"
self.currGroup = currGroup
if self.verbose: print "+++ end SVGCanvas.endGroup"
def transform(self, a, b, c, d, e, f):
if self.verbose: print "!!! begin SVGCanvas.transform", a, b, c, d, e, f
tr = self.currGroup.getAttribute("transform")
t = 'matrix(%f, %f, %f, %f, %f, %f)' % (a,b,c,d,e,f)
if (a, b, c, d, e, f) != (1, 0, 0, 1, 0, 0):
self.currGroup.setAttribute("transform", "%s %s" % (tr, t))
def translate(self, x, y):
# probably never used
print "!!! begin SVGCanvas.translate"
return
tr = self.currGroup.getAttribute("transform")
t = 'translate(%f, %f)' % (x, y)
self.currGroup.setAttribute("transform", "%s %s" % (tr, t))
def scale(self, x, y):
# probably never used
print "!!! begin SVGCanvas.scale"
return
tr = self.groups[-1].getAttribute("transform")
t = 'scale(%f, %f)' % (x, y)
self.currGroup.setAttribute("transform", "%s %s" % (tr, t))
### paths ###
def moveTo(self, x, y):
self.path = self.path + 'M %f %f ' % (x, y)
def lineTo(self, x, y):
self.path = self.path + 'L %f %f ' % (x, y)
def curveTo(self, x1, y1, x2, y2, x3, y3):
self.path = self.path + 'C %f %f %f %f %f %f ' % (x1, y1, x2, y2, x3, y3)
def closePath(self):
self.path = self.path + 'Z '
def saveState(self):
pass
def restoreState(self):
pass
class _SVGRenderer(Renderer):
"""This draws onto an SVG document.
"""
def __init__(self):
self._tracker = StateTracker()
self.verbose = 0
def drawNode(self, node):
"""This is the recursive method called for each node in the tree.
"""
if self.verbose: print "### begin _SVGRenderer.drawNode"
self._canvas.comment('begin node %s'%`node`)
color = self._canvas._color
if not (isinstance(node, Path) and node.isClipPath):
pass # self._canvas.saveState()
#apply state changes
deltas = getStateDelta(node)
self._tracker.push(deltas)
self.applyStateChanges(deltas, {})
#draw the object, or recurse
self.drawNodeDispatcher(node)
rDeltas = self._tracker.pop()
if not (isinstance(node, Path) and node.isClipPath):
pass # self._canvas.restoreState()
self._canvas.comment('end node %s'%`node`)
self._canvas._color = color
#restore things we might have lost (without actually doing anything).
for k, v in rDeltas.items():
if self._restores.has_key(k):
setattr(self._canvas,self._restores[k],v)
if self.verbose: print "### end _SVGRenderer.drawNode"
_restores = {'strokeColor':'_strokeColor','strokeWidth': '_lineWidth','strokeLineCap':'_lineCap',
'strokeLineJoin':'_lineJoin','fillColor':'_fillColor','fontName':'_font',
'fontSize':'_fontSize'}
def drawGroup(self, group):
if self.verbose: print "### begin _SVGRenderer.drawGroup"
currGroup = self._canvas.startGroup()
a, b, c, d, e, f = self._tracker.getCTM()
for childNode in group.getContents():
if isinstance(childNode, UserNode):
node2 = childNode.provideNode()
else:
node2 = childNode
self.drawNode(node2)
self._canvas.transform(a, b, c, d, e, f)
self._canvas.endGroup(currGroup)
if self.verbose: print "### end _SVGRenderer.drawGroup"
def drawRect(self, rect):
if rect.rx == rect.ry == 0:
#plain old rectangle
self._canvas.rect(
rect.x, rect.y,
rect.x+rect.width, rect.y+rect.height)
else:
#cheat and assume ry = rx; better to generalize
#pdfgen roundRect function. TODO
self._canvas.roundRect(
rect.x, rect.y,
rect.x+rect.width, rect.y+rect.height,
rect.rx, rect.ry
)
def drawString(self, stringObj):
if self._canvas._fillColor:
S = self._tracker.getState()
text_anchor, x, y, text = S['textAnchor'], stringObj.x, stringObj.y, stringObj.text
if not text_anchor in ['start', 'inherited']:
font, fontSize = S['fontName'], S['fontSize']
textLen = stringWidth(text, font,fontSize)
if text_anchor=='end':
x = x-textLen
elif text_anchor=='middle':
x = x - textLen/2
else:
raise ValueError, 'bad value for text_anchor ' + str(text_anchor)
self._canvas.drawString(text,x,y)
def drawLine(self, line):
if self._canvas._strokeColor:
self._canvas.line(line.x1, line.y1, line.x2, line.y2)
def drawCircle(self, circle):
self._canvas.circle( circle.cx, circle.cy, circle.r)
def drawWedge(self, wedge):
centerx, centery, radius, startangledegrees, endangledegrees = \
wedge.centerx, wedge.centery, wedge.radius, wedge.startangledegrees, wedge.endangledegrees
yradius = wedge.yradius or wedge.radius
(x1, y1) = (centerx-radius, centery-yradius)
(x2, y2) = (centerx+radius, centery+yradius)
extent = endangledegrees - startangledegrees
self._canvas.drawArc(x1, y1, x2, y2, startangledegrees, extent, fromcenter=1)
def drawPolyLine(self, p):
if self._canvas._strokeColor:
self._canvas.polyLine(_pointsFromList(p.points))
def drawEllipse(self, ellipse):
#need to convert to pdfgen's bounding box representation
x1 = ellipse.cx - ellipse.rx
x2 = ellipse.cx + ellipse.rx
y1 = ellipse.cy - ellipse.ry
y2 = ellipse.cy + ellipse.ry
self._canvas.ellipse(x1,y1,x2,y2)
def drawPolygon(self, p):
self._canvas.polygon(_pointsFromList(p.points), closed=1)
def drawPath(self, path):
# print "### drawPath", path.points
from reportlab.graphics.shapes import _renderPath
c = self._canvas
drawFuncs = (c.moveTo, c.lineTo, c.curveTo, c.closePath)
isClosed = _renderPath(path, drawFuncs)
if not isClosed:
c._fillColor = None
c._fillAndStroke([], clip=path.isClipPath)
def applyStateChanges(self, delta, newState):
"""This takes a set of states, and outputs the operators
needed to set those properties"""
for key, value in delta.items():
if key == 'transform':
pass
#self._canvas.transform(value[0], value[1], value[2], value[3], value[4], value[5])
elif key == 'strokeColor':
self._canvas.setStrokeColor(value)
elif key == 'strokeWidth':
self._canvas.setLineWidth(value)
elif key == 'strokeLineCap': #0,1,2
self._canvas.setLineCap(value)
elif key == 'strokeLineJoin':
self._canvas.setLineJoin(value)
elif key == 'strokeDashArray':
if value:
self._canvas.setDash(value)
else:
self._canvas.setDash()
elif key == 'fillColor':
self._canvas.setFillColor(value)
elif key in ['fontSize', 'fontName']:
fontname = delta.get('fontName', self._canvas._font)
fontsize = delta.get('fontSize', self._canvas._fontSize)
self._canvas.setFont(fontname, fontsize)
def test0(outdir='svgout'):
# print all drawings and their doc strings from the test
# file
if not os.path.isdir(outdir):
os.mkdir(outdir)
#grab all drawings from the test module
from reportlab.graphics import testshapes
drawings = []
for funcname in dir(testshapes):
#if funcname[0:11] == 'getDrawing2':
# print 'hacked to only show drawing 2'
if funcname[0:10] == 'getDrawing':
drawing = eval('testshapes.' + funcname + '()')
docstring = eval('testshapes.' + funcname + '.__doc__')
drawings.append((drawing, docstring))
# return
i = 0
for (d, docstring) in drawings:
filename = outdir + os.sep + 'renderSVG_%d.svg' % i
drawToFile(d, filename)
# print 'saved', filename
i = i + 1
def test1():
from reportlab.graphics.testshapes import getDrawing01
d = getDrawing01()
drawToFile(d, "svgout/test.svg")
def test2():
from reportlab.lib.corp import RL_CorpLogo
from reportlab.graphics.shapes import Drawing
rl = RL_CorpLogo()
d = Drawing(rl.width,rl.height)
d.add(rl)
drawToFile(d, "svgout/corplogo.svg")
if __name__=='__main__':
test0()
test1()
test2()

View File

@ -1,315 +0,0 @@
#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/graphics/renderbase.py
"""
Superclass for renderers to factor out common functionality and default implementations.
"""
__version__=''' $Id $ '''
from reportlab.graphics.shapes import *
from reportlab import rl_config
def inverse(A):
"For A affine 2D represented as 6vec return 6vec version of A**(-1)"
# I checked this RGB
det = float(A[0]*A[3] - A[2]*A[1])
R = [A[3]/det, -A[1]/det, -A[2]/det, A[0]/det]
return tuple(R+[-R[0]*A[4]-R[2]*A[5],-R[1]*A[4]-R[3]*A[5]])
def mmult(A, B):
"A postmultiplied by B"
# I checked this RGB
# [a0 a2 a4] [b0 b2 b4]
# [a1 a3 a5] * [b1 b3 b5]
# [ 1 ] [ 1 ]
#
return (A[0]*B[0] + A[2]*B[1],
A[1]*B[0] + A[3]*B[1],
A[0]*B[2] + A[2]*B[3],
A[1]*B[2] + A[3]*B[3],
A[0]*B[4] + A[2]*B[5] + A[4],
A[1]*B[4] + A[3]*B[5] + A[5])
def getStateDelta(shape):
"""Used to compute when we need to change the graphics state.
For example, if we have two adjacent red shapes we don't need
to set the pen color to red in between. Returns the effect
the given shape would have on the graphics state"""
delta = {}
for (prop, value) in shape.getProperties().items():
if STATE_DEFAULTS.has_key(prop):
delta[prop] = value
return delta
class StateTracker:
"""Keeps a stack of transforms and state
properties. It can contain any properties you
want, but the keys 'transform' and 'ctm' have
special meanings. The getCTM()
method returns the current transformation
matrix at any point, without needing to
invert matrixes when you pop."""
def __init__(self, defaults=None):
# one stack to keep track of what changes...
self.__deltas = []
# and another to keep track of cumulative effects. Last one in
# list is the current graphics state. We put one in to simplify
# loops below.
self.__combined = []
if defaults is None:
defaults = STATE_DEFAULTS.copy()
#ensure that if we have a transform, we have a CTM
if defaults.has_key('transform'):
defaults['ctm'] = defaults['transform']
self.__combined.append(defaults)
def push(self,delta):
"""Take a new state dictionary of changes and push it onto
the stack. After doing this, the combined state is accessible
through getState()"""
newstate = self.__combined[-1].copy()
for (key, value) in delta.items():
if key == 'transform': #do cumulative matrix
newstate['transform'] = delta['transform']
newstate['ctm'] = mmult(self.__combined[-1]['ctm'], delta['transform'])
#print 'statetracker transform = (%0.2f, %0.2f, %0.2f, %0.2f, %0.2f, %0.2f)' % tuple(newstate['transform'])
#print 'statetracker ctm = (%0.2f, %0.2f, %0.2f, %0.2f, %0.2f, %0.2f)' % tuple(newstate['ctm'])
else: #just overwrite it
newstate[key] = value
self.__combined.append(newstate)
self.__deltas.append(delta)
def pop(self):
"""steps back one, and returns a state dictionary with the
deltas to reverse out of wherever you are. Depending
on your back end, you may not need the return value,
since you can get the complete state afterwards with getState()"""
del self.__combined[-1]
newState = self.__combined[-1]
lastDelta = self.__deltas[-1]
del self.__deltas[-1]
#need to diff this against the last one in the state
reverseDelta = {}
#print 'pop()...'
for key, curValue in lastDelta.items():
#print ' key=%s, value=%s' % (key, curValue)
prevValue = newState[key]
if prevValue <> curValue:
#print ' state popping "%s"="%s"' % (key, curValue)
if key == 'transform':
reverseDelta[key] = inverse(lastDelta['transform'])
else: #just return to previous state
reverseDelta[key] = prevValue
return reverseDelta
def getState(self):
"returns the complete graphics state at this point"
return self.__combined[-1]
def getCTM(self):
"returns the current transformation matrix at this point"""
return self.__combined[-1]['ctm']
def __getitem__(self,key):
"returns the complete graphics state value of key at this point"
return self.__combined[-1][key]
def __setitem__(self,key,value):
"sets the complete graphics state value of key to value"
self.__combined[-1][key] = value
def testStateTracker():
print 'Testing state tracker'
defaults = {'fillColor':None, 'strokeColor':None,'fontName':None, 'transform':[1,0,0,1,0,0]}
deltas = [
{'fillColor':'red'},
{'fillColor':'green', 'strokeColor':'blue','fontName':'Times-Roman'},
{'transform':[0.5,0,0,0.5,0,0]},
{'transform':[0.5,0,0,0.5,2,3]},
{'strokeColor':'red'}
]
st = StateTracker(defaults)
print 'initial:', st.getState()
print
for delta in deltas:
print 'pushing:', delta
st.push(delta)
print 'state: ',st.getState(),'\n'
for delta in deltas:
print 'popping:',st.pop()
print 'state: ',st.getState(),'\n'
def _expandUserNode(node,canvas):
if isinstance(node, UserNode):
try:
if hasattr(node,'_canvas'):
ocanvas = 1
else:
node._canvas = canvas
ocanvas = None
onode = node
node = node.provideNode()
finally:
if not ocanvas: del onode._canvas
return node
class Renderer:
"""Virtual superclass for graphics renderers."""
def __init__(self):
self._tracker = StateTracker()
def undefined(self, operation):
raise ValueError, "%s operation not defined at superclass class=%s" %(operation, self.__class__)
def draw(self, drawing, canvas, x=0, y=0, showBoundary=rl_config._unset_):
"""This is the top level function, which draws the drawing at the given
location. The recursive part is handled by drawNode."""
#stash references for ease of communication
if showBoundary is rl_config._unset_: showBoundary=rl_config.showBoundary
self._canvas = canvas
canvas.__dict__['_drawing'] = self._drawing = drawing
drawing._parent = None
try:
#bounding box
if showBoundary: canvas.rect(x, y, drawing.width, drawing.height)
canvas.saveState()
self.initState(x,y)
self.drawNode(drawing)
self.pop()
canvas.restoreState()
finally:
#remove any circular references
del self._canvas, self._drawing, canvas._drawing, drawing._parent
def initState(self,x,y):
deltas = STATE_DEFAULTS.copy()
deltas['transform'] = [1,0,0,1,x,y]
self._tracker.push(deltas)
self.applyStateChanges(deltas, {})
def pop(self):
self._tracker.pop()
def drawNode(self, node):
"""This is the recursive method called for each node
in the tree"""
# Undefined here, but with closer analysis probably can be handled in superclass
self.undefined("drawNode")
def drawNodeDispatcher(self, node):
"""dispatch on the node's (super) class: shared code"""
canvas = getattr(self,'_canvas',None)
# replace UserNode with its contents
try:
node = _expandUserNode(node,canvas)
if hasattr(node,'_canvas'):
ocanvas = 1
else:
node._canvas = canvas
ocanvas = None
#draw the object, or recurse
if isinstance(node, Line):
self.drawLine(node)
elif isinstance(node, Image):
self.drawImage(node)
elif isinstance(node, Rect):
self.drawRect(node)
elif isinstance(node, Circle):
self.drawCircle(node)
elif isinstance(node, Ellipse):
self.drawEllipse(node)
elif isinstance(node, PolyLine):
self.drawPolyLine(node)
elif isinstance(node, Polygon):
self.drawPolygon(node)
elif isinstance(node, Path):
self.drawPath(node)
elif isinstance(node, String):
self.drawString(node)
elif isinstance(node, Group):
self.drawGroup(node)
elif isinstance(node, Wedge):
self.drawWedge(node)
else:
print 'DrawingError','Unexpected element %s in drawing!' % str(node)
finally:
if not ocanvas: del node._canvas
_restores = {'stroke':'_stroke','stroke_width': '_lineWidth','stroke_linecap':'_lineCap',
'stroke_linejoin':'_lineJoin','fill':'_fill','font_family':'_font',
'font_size':'_fontSize'}
def drawGroup(self, group):
# just do the contents. Some renderers might need to override this
# if they need a flipped transform
canvas = getattr(self,'_canvas',None)
for node in group.getContents():
node = _expandUserNode(node,canvas)
try:
if hasattr(node,'_canvas'):
ocanvas = 1
else:
node._canvas = canvas
ocanvas = None
node._parent = group
self.drawNode(node)
finally:
del node._parent
if not ocanvas: del node._canvas
def drawWedge(self, wedge):
# by default ask the wedge to make a polygon of itself and draw that!
#print "drawWedge"
polygon = wedge.asPolygon()
self.drawPolygon(polygon)
def drawPath(self, path):
polygons = path.asPolygons()
for polygon in polygons:
self.drawPolygon(polygon)
def drawRect(self, rect):
# could be implemented in terms of polygon
self.undefined("drawRect")
def drawLine(self, line):
self.undefined("drawLine")
def drawCircle(self, circle):
self.undefined("drawCircle")
def drawPolyLine(self, p):
self.undefined("drawPolyLine")
def drawEllipse(self, ellipse):
self.undefined("drawEllipse")
def drawPolygon(self, p):
self.undefined("drawPolygon")
def drawString(self, stringObj):
self.undefined("drawString")
def applyStateChanges(self, delta, newState):
"""This takes a set of states, and outputs the operators
needed to set those properties"""
self.undefined("applyStateChanges")
if __name__=='__main__':
print "this file has no script interpretation"
print __doc__

View File

@ -1,73 +0,0 @@
#Autogenerated by ReportLab guiedit do not edit
from reportlab.graphics.charts.legends import Legend
from reportlab.graphics.charts.lineplots import ScatterPlot
from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
from reportlab.graphics.charts.textlabels import Label
from excelcolors import *
class Bubble(_DrawingEditorMixin,Drawing):
def __init__(self,width=200,height=150,*args,**kw):
apply(Drawing.__init__,(self,width,height)+args,kw)
self._add(self,ScatterPlot(),name='chart',validate=None,desc="The main chart")
self.chart.width = 115
self.chart.height = 80
self.chart.x = 30
self.chart.y = 40
self.chart.lines[0].strokeColor = color01
self.chart.lines[1].strokeColor = color02
self.chart.lines[2].strokeColor = color03
self.chart.lines[3].strokeColor = color04
self.chart.lines[4].strokeColor = color05
self.chart.lines[5].strokeColor = color06
self.chart.lines[6].strokeColor = color07
self.chart.lines[7].strokeColor = color08
self.chart.lines[8].strokeColor = color09
self.chart.lines[9].strokeColor = color10
self.chart.lines.symbol.kind ='Circle'
self.chart.lines.symbol.size = 15
self.chart.fillColor = backgroundGrey
self.chart.lineLabels.fontName = 'Helvetica'
self.chart.xValueAxis.labels.fontName = 'Helvetica'
self.chart.xValueAxis.labels.fontSize = 7
self.chart.xValueAxis.forceZero = 0
self.chart.data = [((100,100), (200,200), (250,210), (300,300), (350,450))]
self.chart.xValueAxis.avoidBoundFrac = 1
self.chart.xValueAxis.gridEnd = 115
self.chart.xValueAxis.tickDown = 3
self.chart.xValueAxis.visibleGrid = 1
self.chart.yValueAxis.tickLeft = 3
self.chart.yValueAxis.labels.fontName = 'Helvetica'
self.chart.yValueAxis.labels.fontSize = 7
self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart")
self.Title.fontName = 'Helvetica-Bold'
self.Title.fontSize = 7
self.Title.x = 100
self.Title.y = 135
self.Title._text = 'Chart Title'
self.Title.maxWidth = 180
self.Title.height = 20
self.Title.textAnchor ='middle'
self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart")
self.Legend.colorNamePairs = [(color01, 'Widgets')]
self.Legend.fontName = 'Helvetica'
self.Legend.fontSize = 7
self.Legend.x = 153
self.Legend.y = 85
self.Legend.dxTextSpace = 5
self.Legend.dy = 5
self.Legend.dx = 5
self.Legend.deltay = 5
self.Legend.alignment ='right'
self.chart.lineLabelFormat = None
self.chart.xLabel = 'X Axis'
self.chart.y = 30
self.chart.yLabel = 'Y Axis'
self.chart.yValueAxis.labelTextFormat = '%d'
self.chart.yValueAxis.forceZero = 1
self.chart.xValueAxis.forceZero = 1
self._add(self,0,name='preview',validate=None,desc=None)
if __name__=="__main__": #NORUNTESTS
Bubble().save(formats=['pdf'],outDir=None,fnRoot='bubble')

View File

@ -1,84 +0,0 @@
#Autogenerated by ReportLab guiedit do not edit
from reportlab.graphics.charts.legends import Legend
from excelcolors import *
from reportlab.graphics.charts.barcharts import HorizontalBarChart
from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
from reportlab.graphics.charts.textlabels import Label
class ClusteredBar(_DrawingEditorMixin,Drawing):
def __init__(self,width=200,height=150,*args,**kw):
apply(Drawing.__init__,(self,width,height)+args,kw)
self._add(self,HorizontalBarChart(),name='chart',validate=None,desc="The main chart")
self.chart.width = 115
self.chart.height = 80
self.chart.x = 30
self.chart.y = 40
self.chart.bars[0].fillColor = color01
self.chart.bars[1].fillColor = color02
self.chart.bars[2].fillColor = color03
self.chart.bars[3].fillColor = color04
self.chart.bars[4].fillColor = color05
self.chart.bars[5].fillColor = color06
self.chart.bars[6].fillColor = color07
self.chart.bars[7].fillColor = color08
self.chart.bars[8].fillColor = color09
self.chart.bars[9].fillColor = color10
self.chart.fillColor = backgroundGrey
self.chart.barLabels.fontName = 'Helvetica'
self.chart.valueAxis.labels.fontName = 'Helvetica'
self.chart.valueAxis.labels.fontSize = 6
self.chart.valueAxis.forceZero = 1
self.chart.data = [(100, 150, 180), (125, 180, 200)]
self.chart.groupSpacing = 15
self.chart.valueAxis.avoidBoundFrac = 1
self.chart.valueAxis.gridEnd = 80
self.chart.valueAxis.tickDown = 3
self.chart.valueAxis.visibleGrid = 1
self.chart.categoryAxis.categoryNames = ['North', 'South', 'Central']
self.chart.categoryAxis.tickLeft = 3
self.chart.categoryAxis.labels.fontName = 'Helvetica'
self.chart.categoryAxis.labels.fontSize = 6
self.chart.categoryAxis.labels.dx = -3
self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart")
self.Title.fontName = 'Helvetica-Bold'
self.Title.fontSize = 7
self.Title.x = 100
self.Title.y = 135
self.Title._text = 'Chart Title'
self.Title.maxWidth = 180
self.Title.height = 20
self.Title.textAnchor ='middle'
self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart")
self.Legend.colorNamePairs = [(color01, 'Widgets'), (color02, 'Sprockets')]
self.Legend.fontName = 'Helvetica'
self.Legend.fontSize = 7
self.Legend.x = 153
self.Legend.y = 85
self.Legend.dxTextSpace = 5
self.Legend.dy = 5
self.Legend.dx = 5
self.Legend.deltay = 5
self.Legend.alignment ='right'
self._add(self,Label(),name='XLabel',validate=None,desc="The label on the horizontal axis")
self.XLabel.fontName = 'Helvetica'
self.XLabel.fontSize = 7
self.XLabel.x = 85
self.XLabel.y = 10
self.XLabel.textAnchor ='middle'
self.XLabel.maxWidth = 100
self.XLabel.height = 20
self.XLabel._text = "X Axis"
self._add(self,Label(),name='YLabel',validate=None,desc="The label on the vertical axis")
self.YLabel.fontName = 'Helvetica'
self.YLabel.fontSize = 7
self.YLabel.x = 12
self.YLabel.y = 80
self.YLabel.angle = 90
self.YLabel.textAnchor ='middle'
self.YLabel.maxWidth = 100
self.YLabel.height = 20
self.YLabel._text = "Y Axis"
self._add(self,0,name='preview',validate=None,desc=None)
if __name__=="__main__": #NORUNTESTS
ClusteredBar().save(formats=['pdf'],outDir=None,fnRoot='clustered_bar')

View File

@ -1,83 +0,0 @@
#Autogenerated by ReportLab guiedit do not edit
from reportlab.graphics.charts.legends import Legend
from excelcolors import *
from reportlab.graphics.charts.barcharts import VerticalBarChart
from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
from reportlab.graphics.charts.textlabels import Label
class ClusteredColumn(_DrawingEditorMixin,Drawing):
def __init__(self,width=200,height=150,*args,**kw):
apply(Drawing.__init__,(self,width,height)+args,kw)
self._add(self,VerticalBarChart(),name='chart',validate=None,desc="The main chart")
self.chart.width = 115
self.chart.height = 80
self.chart.x = 30
self.chart.y = 40
self.chart.bars[0].fillColor = color01
self.chart.bars[1].fillColor = color02
self.chart.bars[2].fillColor = color03
self.chart.bars[3].fillColor = color04
self.chart.bars[4].fillColor = color05
self.chart.bars[5].fillColor = color06
self.chart.bars[6].fillColor = color07
self.chart.bars[7].fillColor = color08
self.chart.bars[8].fillColor = color09
self.chart.bars[9].fillColor = color10
self.chart.fillColor = backgroundGrey
self.chart.barLabels.fontName = 'Helvetica'
self.chart.valueAxis.labels.fontName = 'Helvetica'
self.chart.valueAxis.labels.fontSize = 7
self.chart.valueAxis.forceZero = 1
self.chart.data = [(100, 150, 180), (125, 180, 200)]
self.chart.groupSpacing = 15
self.chart.valueAxis.avoidBoundFrac = 1
self.chart.valueAxis.gridEnd = 115
self.chart.valueAxis.tickLeft = 3
self.chart.valueAxis.visibleGrid = 1
self.chart.categoryAxis.categoryNames = ['North', 'South', 'Central']
self.chart.categoryAxis.tickDown = 3
self.chart.categoryAxis.labels.fontName = 'Helvetica'
self.chart.categoryAxis.labels.fontSize = 7
self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart")
self.Title.fontName = 'Helvetica-Bold'
self.Title.fontSize = 7
self.Title.x = 100
self.Title.y = 135
self.Title._text = 'Chart Title'
self.Title.maxWidth = 180
self.Title.height = 20
self.Title.textAnchor ='middle'
self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart")
self.Legend.colorNamePairs = [(color01, 'Widgets'), (color02, 'Sprockets')]
self.Legend.fontName = 'Helvetica'
self.Legend.fontSize = 7
self.Legend.x = 153
self.Legend.y = 85
self.Legend.dxTextSpace = 5
self.Legend.dy = 5
self.Legend.dx = 5
self.Legend.deltay = 5
self.Legend.alignment ='right'
self._add(self,Label(),name='XLabel',validate=None,desc="The label on the horizontal axis")
self.XLabel.fontName = 'Helvetica'
self.XLabel.fontSize = 7
self.XLabel.x = 85
self.XLabel.y = 10
self.XLabel.textAnchor ='middle'
self.XLabel.maxWidth = 100
self.XLabel.height = 20
self.XLabel._text = "X Axis"
self._add(self,Label(),name='YLabel',validate=None,desc="The label on the vertical axis")
self.YLabel.fontName = 'Helvetica'
self.YLabel.fontSize = 7
self.YLabel.x = 12
self.YLabel.y = 80
self.YLabel.angle = 90
self.YLabel.textAnchor ='middle'
self.YLabel.maxWidth = 100
self.YLabel.height = 20
self.YLabel._text = "Y Axis"
self._add(self,0,name='preview',validate=None,desc=None)
if __name__=="__main__": #NORUNTESTS
ClusteredColumn().save(formats=['pdf'],outDir=None,fnRoot='clustered_column')

View File

@ -1,45 +0,0 @@
# define standard colors to mimic those used by Microsoft Excel
from reportlab.lib.colors import CMYKColor, PCMYKColor
#colour names as comments at the end of each line are as a memory jogger ONLY
#NOT HTML named colours!
#Main colours as used for bars etc
color01 = PCMYKColor(40,40,0,0) # Lavender
color02 = PCMYKColor(0,66,33,39) # Maroon
color03 = PCMYKColor(0,0,20,0) # Yellow
color04 = PCMYKColor(20,0,0,0) # Cyan
color05 = PCMYKColor(0,100,0,59) # Purple
color06 = PCMYKColor(0,49,49,0) # Salmon
color07 = PCMYKColor(100,49,0,19) # Blue
color08 = PCMYKColor(20,20,0,0) # PaleLavender
color09 = PCMYKColor(100,100,0,49) # NavyBlue
color10 = PCMYKColor(0,100,0,0) # Purple
#Highlight colors - eg for the tops of bars
color01Light = PCMYKColor(39,39,0,25) # Light Lavender
color02Light = PCMYKColor(0,66,33,54) # Light Maroon
color03Light = PCMYKColor(0,0,19,25) # Light Yellow
color04Light = PCMYKColor(19,0,0,25) # Light Cyan
color05Light = PCMYKColor(0,100,0,69) # Light Purple
color06Light = PCMYKColor(0,49,49,25) # Light Salmon
color07Light = PCMYKColor(100,49,0,39) # Light Blue
color08Light = PCMYKColor(19,19,0,25) # Light PaleLavender
color09Light = PCMYKColor(100,100,0,62) # Light NavyBlue
color10Light = PCMYKColor(0,100,0,25) # Light Purple
#Lowlight colors - eg for the sides of bars
color01Dark = PCMYKColor(39,39,0,49) # Dark Lavender
color02Dark = PCMYKColor(0,66,33,69) # Dark Maroon
color03Dark = PCMYKColor(0,0,20,49) # Dark Yellow
color04Dark = PCMYKColor(20,0,0,49) # Dark Cyan
color05Dark = PCMYKColor(0,100,0,80) # Dark Purple
color06Dark = PCMYKColor(0,50,50,49) # Dark Salmon
color07Dark = PCMYKColor(100,50,0,59) # Dark Blue
color08Dark = PCMYKColor(20,20,0,49) # Dark PaleLavender
color09Dark = PCMYKColor(100,100,0,79) # Dark NavyBlue
color10Dark = PCMYKColor(0,100,0,49) # Dark Purple
#for standard grey backgrounds
backgroundGrey = PCMYKColor(0,0,0,24)

View File

@ -1,65 +0,0 @@
#Autogenerated by ReportLab guiedit do not edit
from reportlab.graphics.charts.piecharts import Pie
from excelcolors import *
from reportlab.graphics.widgets.grids import ShadedRect
from reportlab.graphics.charts.legends import Legend
from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
from reportlab.graphics.charts.textlabels import Label
class ExplodedPie(_DrawingEditorMixin,Drawing):
def __init__(self,width=200,height=150,*args,**kw):
apply(Drawing.__init__,(self,width,height)+args,kw)
self._add(self,Pie(),name='chart',validate=None,desc="The main chart")
self.chart.width = 100
self.chart.height = 100
self.chart.x = 25
self.chart.y = 25
self.chart.slices[0].fillColor = color01
self.chart.slices[1].fillColor = color02
self.chart.slices[2].fillColor = color03
self.chart.slices[3].fillColor = color04
self.chart.slices[4].fillColor = color05
self.chart.slices[5].fillColor = color06
self.chart.slices[6].fillColor = color07
self.chart.slices[7].fillColor = color08
self.chart.slices[8].fillColor = color09
self.chart.slices[9].fillColor = color10
self.chart.data = (100, 150, 180)
self.chart.startAngle = -90
self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart")
self.Title.fontName = 'Helvetica-Bold'
self.Title.fontSize = 7
self.Title.x = 100
self.Title.y = 135
self.Title._text = 'Chart Title'
self.Title.maxWidth = 180
self.Title.height = 20
self.Title.textAnchor ='middle'
self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart")
self.Legend.colorNamePairs = [(color01, 'North'), (color02, 'South'), (color03, 'Central')]
self.Legend.fontName = 'Helvetica'
self.Legend.fontSize = 7
self.Legend.x = 160
self.Legend.y = 85
self.Legend.dxTextSpace = 5
self.Legend.dy = 5
self.Legend.dx = 5
self.Legend.deltay = 5
self.Legend.alignment ='right'
self.Legend.columnMaximum = 10
self.chart.slices.strokeWidth = 1
self.chart.slices.fontName = 'Helvetica'
self.background = ShadedRect()
self.background.fillColorStart = backgroundGrey
self.background.fillColorEnd = backgroundGrey
self.background.numShades = 1
self.background.strokeWidth = 0.5
self.background.x = 20
self.background.y = 20
self.chart.slices.popout = 5
self.background.height = 110
self.background.width = 110
self._add(self,0,name='preview',validate=None,desc=None)
if __name__=="__main__": #NORUNTESTS
ExplodedPie().save(formats=['pdf'],outDir=None,fnRoot='exploded_pie')

View File

@ -1,54 +0,0 @@
#Autogenerated by ReportLab guiedit do not edit
from reportlab.graphics.charts.legends import Legend
from reportlab.graphics.charts.spider import SpiderChart
from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
from reportlab.graphics.charts.textlabels import Label
from excelcolors import *
class FilledRadarChart(_DrawingEditorMixin,Drawing):
def __init__(self,width=200,height=150,*args,**kw):
apply(Drawing.__init__,(self,width,height)+args,kw)
self._add(self,SpiderChart(),name='chart',validate=None,desc="The main chart")
self.chart.width = 90
self.chart.height = 90
self.chart.x = 45
self.chart.y = 25
self.chart.strands[0].fillColor = color01
self.chart.strands[1].fillColor = color02
self.chart.strands[2].fillColor = color03
self.chart.strands[3].fillColor = color04
self.chart.strands[4].fillColor = color05
self.chart.strands[5].fillColor = color06
self.chart.strands[6].fillColor = color07
self.chart.strands[7].fillColor = color08
self.chart.strands[8].fillColor = color09
self.chart.strands[9].fillColor = color10
self.chart.strands.fontName = 'Helvetica'
self.chart.strands.fontSize = 6
self.chart.fillColor = backgroundGrey
self.chart.data = [(125, 180, 200), (100, 150, 180)]
self.chart.labels = ['North', 'South', 'Central']
self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart")
self.Title.fontName = 'Helvetica-Bold'
self.Title.fontSize = 7
self.Title.x = 100
self.Title.y = 135
self.Title._text = 'Chart Title'
self.Title.maxWidth = 180
self.Title.height = 20
self.Title.textAnchor ='middle'
self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart")
self.Legend.colorNamePairs = [(color01, 'Widgets'), (color02, 'Sprockets')]
self.Legend.fontName = 'Helvetica'
self.Legend.fontSize = 7
self.Legend.x = 153
self.Legend.y = 85
self.Legend.dxTextSpace = 5
self.Legend.dy = 5
self.Legend.dx = 5
self.Legend.deltay = 5
self.Legend.alignment ='right'
self._add(self,0,name='preview',validate=None,desc=None)
if __name__=="__main__": #NORUNTESTS
FilledRadarChart().save(formats=['pdf'],outDir=None,fnRoot='filled_radar')

View File

@ -1,83 +0,0 @@
#Autogenerated by ReportLab guiedit do not edit
from reportlab.graphics.charts.legends import Legend
from reportlab.graphics.charts.lineplots import LinePlot
from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
from reportlab.graphics.charts.textlabels import Label
from excelcolors import *
class LineChart(_DrawingEditorMixin,Drawing):
def __init__(self,width=200,height=150,*args,**kw):
apply(Drawing.__init__,(self,width,height)+args,kw)
self._add(self,LinePlot(),name='chart',validate=None,desc="The main chart")
self.chart.width = 115
self.chart.height = 80
self.chart.x = 30
self.chart.y = 40
self.chart.lines[0].strokeColor = color01
self.chart.lines[1].strokeColor = color02
self.chart.lines[2].strokeColor = color03
self.chart.lines[3].strokeColor = color04
self.chart.lines[4].strokeColor = color05
self.chart.lines[5].strokeColor = color06
self.chart.lines[6].strokeColor = color07
self.chart.lines[7].strokeColor = color08
self.chart.lines[8].strokeColor = color09
self.chart.lines[9].strokeColor = color10
self.chart.fillColor = backgroundGrey
self.chart.lineLabels.fontName = 'Helvetica'
self.chart.xValueAxis.labels.fontName = 'Helvetica'
self.chart.xValueAxis.labels.fontSize = 7
self.chart.xValueAxis.forceZero = 0
self.chart.data = [((0, 50), (100,100), (200,200), (250,210), (300,300), (400,500)), ((0, 150), (100,200), (200,300), (250,200), (300,400), (400, 600))]
self.chart.xValueAxis.avoidBoundFrac = 1
self.chart.xValueAxis.gridEnd = 115
self.chart.xValueAxis.tickDown = 3
self.chart.xValueAxis.visibleGrid = 1
self.chart.yValueAxis.tickLeft = 3
self.chart.yValueAxis.labels.fontName = 'Helvetica'
self.chart.yValueAxis.labels.fontSize = 7
self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart")
self.Title.fontName = 'Helvetica-Bold'
self.Title.fontSize = 7
self.Title.x = 100
self.Title.y = 135
self.Title._text = 'Chart Title'
self.Title.maxWidth = 180
self.Title.height = 20
self.Title.textAnchor ='middle'
self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart")
self.Legend.colorNamePairs = [(color01, 'Widgets'), (color02, 'Sprockets')]
self.Legend.fontName = 'Helvetica'
self.Legend.fontSize = 7
self.Legend.x = 153
self.Legend.y = 85
self.Legend.dxTextSpace = 5
self.Legend.dy = 5
self.Legend.dx = 5
self.Legend.deltay = 5
self.Legend.alignment ='right'
self._add(self,Label(),name='XLabel',validate=None,desc="The label on the horizontal axis")
self.XLabel.fontName = 'Helvetica'
self.XLabel.fontSize = 7
self.XLabel.x = 85
self.XLabel.y = 10
self.XLabel.textAnchor ='middle'
self.XLabel.maxWidth = 100
self.XLabel.height = 20
self.XLabel._text = "X Axis"
self._add(self,Label(),name='YLabel',validate=None,desc="The label on the vertical axis")
self.YLabel.fontName = 'Helvetica'
self.YLabel.fontSize = 7
self.YLabel.x = 12
self.YLabel.y = 80
self.YLabel.angle = 90
self.YLabel.textAnchor ='middle'
self.YLabel.maxWidth = 100
self.YLabel.height = 20
self.YLabel._text = "Y Axis"
self.chart.yValueAxis.forceZero = 1
self.chart.xValueAxis.forceZero = 1
self._add(self,0,name='preview',validate=None,desc=None)
if __name__=="__main__": #NORUNTESTS
LineChart().save(formats=['pdf'],outDir=None,fnRoot='line_chart')

View File

@ -1,94 +0,0 @@
#Autogenerated by ReportLab guiedit do not edit
from reportlab.graphics.charts.legends import Legend
from reportlab.graphics.charts.lineplots import LinePlot
from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
from reportlab.graphics.widgets.markers import makeMarker
from reportlab.graphics.charts.textlabels import Label
from excelcolors import *
class LineChartWithMarkers(_DrawingEditorMixin,Drawing):
def __init__(self,width=200,height=150,*args,**kw):
apply(Drawing.__init__,(self,width,height)+args,kw)
self._add(self,LinePlot(),name='chart',validate=None,desc="The main chart")
self.chart.width = 115
self.chart.height = 80
self.chart.x = 30
self.chart.y = 40
self.chart.lines[0].strokeColor = color01
self.chart.lines[1].strokeColor = color02
self.chart.lines[2].strokeColor = color03
self.chart.lines[3].strokeColor = color04
self.chart.lines[4].strokeColor = color05
self.chart.lines[5].strokeColor = color06
self.chart.lines[6].strokeColor = color07
self.chart.lines[7].strokeColor = color08
self.chart.lines[8].strokeColor = color09
self.chart.lines[9].strokeColor = color10
self.chart.lines[0].symbol = makeMarker('FilledSquare')
self.chart.lines[1].symbol = makeMarker('FilledDiamond')
self.chart.lines[2].symbol = makeMarker('FilledStarFive')
self.chart.lines[3].symbol = makeMarker('FilledTriangle')
self.chart.lines[4].symbol = makeMarker('FilledCircle')
self.chart.lines[5].symbol = makeMarker('FilledPentagon')
self.chart.lines[6].symbol = makeMarker('FilledStarSix')
self.chart.lines[7].symbol = makeMarker('FilledHeptagon')
self.chart.lines[8].symbol = makeMarker('FilledOctagon')
self.chart.lines[9].symbol = makeMarker('FilledCross')
self.chart.fillColor = backgroundGrey
self.chart.lineLabels.fontName = 'Helvetica'
self.chart.xValueAxis.labels.fontName = 'Helvetica'
self.chart.xValueAxis.labels.fontSize = 7
self.chart.xValueAxis.forceZero = 0
self.chart.data = [((0, 50), (100,100), (200,200), (250,210), (300,300), (400,500)), ((0, 150), (100,200), (200,300), (250,200), (300,400), (400, 600))]
self.chart.xValueAxis.avoidBoundFrac = 1
self.chart.xValueAxis.gridEnd = 115
self.chart.xValueAxis.tickDown = 3
self.chart.xValueAxis.visibleGrid = 1
self.chart.yValueAxis.tickLeft = 3
self.chart.yValueAxis.labels.fontName = 'Helvetica'
self.chart.yValueAxis.labels.fontSize = 7
self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart")
self.Title.fontName = 'Helvetica-Bold'
self.Title.fontSize = 7
self.Title.x = 100
self.Title.y = 135
self.Title._text = 'Chart Title'
self.Title.maxWidth = 180
self.Title.height = 20
self.Title.textAnchor ='middle'
self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart")
self.Legend.colorNamePairs = [(color01, 'Widgets'), (color02, 'Sprockets')]
self.Legend.fontName = 'Helvetica'
self.Legend.fontSize = 7
self.Legend.x = 153
self.Legend.y = 85
self.Legend.dxTextSpace = 5
self.Legend.dy = 5
self.Legend.dx = 5
self.Legend.deltay = 5
self.Legend.alignment ='right'
self._add(self,Label(),name='XLabel',validate=None,desc="The label on the horizontal axis")
self.XLabel.fontName = 'Helvetica'
self.XLabel.fontSize = 7
self.XLabel.x = 85
self.XLabel.y = 10
self.XLabel.textAnchor ='middle'
self.XLabel.maxWidth = 100
self.XLabel.height = 20
self.XLabel._text = "X Axis"
self._add(self,Label(),name='YLabel',validate=None,desc="The label on the vertical axis")
self.YLabel.fontName = 'Helvetica'
self.YLabel.fontSize = 7
self.YLabel.x = 12
self.YLabel.y = 80
self.YLabel.angle = 90
self.YLabel.textAnchor ='middle'
self.YLabel.maxWidth = 100
self.YLabel.height = 20
self.YLabel._text = "Y Axis"
self.chart.yValueAxis.forceZero = 1
self.chart.xValueAxis.forceZero = 1
self._add(self,0,name='preview',validate=None,desc=None)
if __name__=="__main__": #NORUNTESTS
LineChartWithMarkers().save(formats=['pdf'],outDir=None,fnRoot='linechart_with_markers')

View File

@ -1,66 +0,0 @@
#Autogenerated by ReportLab guiedit do not edit
from reportlab.graphics.charts.legends import Legend
from excelcolors import *
from reportlab.graphics.charts.spider import SpiderChart
from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
from reportlab.graphics.charts.textlabels import Label
class RadarChart(_DrawingEditorMixin,Drawing):
def __init__(self,width=200,height=150,*args,**kw):
apply(Drawing.__init__,(self,width,height)+args,kw)
self._add(self,SpiderChart(),name='chart',validate=None,desc="The main chart")
self.chart.width = 90
self.chart.height = 90
self.chart.x = 45
self.chart.y = 25
self.chart.strands[0].strokeColor= color01
self.chart.strands[1].strokeColor= color02
self.chart.strands[2].strokeColor= color03
self.chart.strands[3].strokeColor= color04
self.chart.strands[4].strokeColor= color05
self.chart.strands[5].strokeColor= color06
self.chart.strands[6].strokeColor= color07
self.chart.strands[7].strokeColor= color08
self.chart.strands[8].strokeColor= color09
self.chart.strands[9].strokeColor= color10
self.chart.strands[0].fillColor = None
self.chart.strands[1].fillColor = None
self.chart.strands[2].fillColor = None
self.chart.strands[3].fillColor = None
self.chart.strands[4].fillColor = None
self.chart.strands[5].fillColor = None
self.chart.strands[6].fillColor = None
self.chart.strands[7].fillColor = None
self.chart.strands[8].fillColor = None
self.chart.strands[9].fillColor = None
self.chart.strands.strokeWidth = 1
self.chart.strands.fontName = 'Helvetica'
self.chart.strands.fontSize = 6
self.chart.fillColor = backgroundGrey
self.chart.data = [(125, 180, 200), (100, 150, 180)]
self.chart.labels = ['North', 'South', 'Central']
self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart")
self.Title.fontName = 'Helvetica-Bold'
self.Title.fontSize = 7
self.Title.x = 100
self.Title.y = 135
self.Title._text = 'Chart Title'
self.Title.maxWidth = 180
self.Title.height = 20
self.Title.textAnchor ='middle'
self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart")
self.Legend.colorNamePairs = [(color01, 'Widgets'), (color02, 'Sprockets')]
self.Legend.fontName = 'Helvetica'
self.Legend.fontSize = 7
self.Legend.x = 153
self.Legend.y = 85
self.Legend.dxTextSpace = 5
self.Legend.dy = 5
self.Legend.dx = 5
self.Legend.deltay = 5
self.Legend.alignment ='right'
self.chart.strands.strokeWidth = 1
self._add(self,0,name='preview',validate=None,desc=None)
if __name__=="__main__": #NORUNTESTS
RadarChart().save(formats=['pdf'],outDir=None,fnRoot='radar')

View File

@ -1,59 +0,0 @@
# runs all the GUIedit charts in this directory -
# makes a PDF sample for eaxh existing chart type
import sys
import glob
import string
import inspect
import types
def moduleClasses(mod):
def P(obj, m=mod.__name__, CT=types.ClassType):
return (type(obj)==CT and obj.__module__==m)
try:
return inspect.getmembers(mod, P)[0][1]
except:
return None
def getclass(f):
return moduleClasses(__import__(f))
def run(format, VERBOSE=0):
formats = string.split(format, ',')
for i in range(0, len(formats)):
formats[i] == string.lower(string.strip(formats[i]))
allfiles = glob.glob('*.py')
allfiles.sort()
for fn in allfiles:
f = string.split(fn, '.')[0]
c = getclass(f)
if c != None:
print c.__name__
try:
for fmt in formats:
if fmt:
c().save(formats=[fmt],outDir='.',fnRoot=c.__name__)
if VERBOSE:
print " %s.%s" % (c.__name__, fmt)
except:
print " COULDN'T CREATE '%s.%s'!" % (c.__name__, format)
if __name__ == "__main__":
if len(sys.argv) == 1:
run('pdf,pict,png')
else:
try:
if sys.argv[1] == "-h":
print 'usage: runall.py [FORMAT] [-h]'
print ' if format is supplied is should be one or more of pdf,gif,eps,png etc'
print ' if format is missing the following formats are assumed: pdf,pict,png'
print ' -h prints this message'
else:
t = sys.argv[1:]
for f in t:
run(f)
except:
print 'usage: runall.py [FORMAT][-h]'
print ' if format is supplied is should be one or more of pdf,gif,eps,png etc'
print ' if format is missing the following formats are assumed: pdf,pict,png'
print ' -h prints this message'
raise

View File

@ -1,71 +0,0 @@
#Autogenerated by ReportLab guiedit do not edit
from reportlab.graphics.charts.legends import Legend
from reportlab.graphics.charts.lineplots import ScatterPlot
from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
from reportlab.graphics.charts.textlabels import Label
from excelcolors import *
class Scatter(_DrawingEditorMixin,Drawing):
def __init__(self,width=200,height=150,*args,**kw):
apply(Drawing.__init__,(self,width,height)+args,kw)
self._add(self,ScatterPlot(),name='chart',validate=None,desc="The main chart")
self.chart.width = 115
self.chart.height = 80
self.chart.x = 30
self.chart.y = 40
self.chart.lines[0].strokeColor = color01
self.chart.lines[1].strokeColor = color02
self.chart.lines[2].strokeColor = color03
self.chart.lines[3].strokeColor = color04
self.chart.lines[4].strokeColor = color05
self.chart.lines[5].strokeColor = color06
self.chart.lines[6].strokeColor = color07
self.chart.lines[7].strokeColor = color08
self.chart.lines[8].strokeColor = color09
self.chart.lines[9].strokeColor = color10
self.chart.fillColor = backgroundGrey
self.chart.lineLabels.fontName = 'Helvetica'
self.chart.xValueAxis.labels.fontName = 'Helvetica'
self.chart.xValueAxis.labels.fontSize = 7
self.chart.xValueAxis.forceZero = 0
self.chart.data = [((100,100), (200,200), (250,210), (300,300), (400,500)), ((100,200), (200,300), (250,200), (300,400), (400, 600))]
self.chart.xValueAxis.avoidBoundFrac = 1
self.chart.xValueAxis.gridEnd = 115
self.chart.xValueAxis.tickDown = 3
self.chart.xValueAxis.visibleGrid = 1
self.chart.yValueAxis.tickLeft = 3
self.chart.yValueAxis.labels.fontName = 'Helvetica'
self.chart.yValueAxis.labels.fontSize = 7
self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart")
self.Title.fontName = 'Helvetica-Bold'
self.Title.fontSize = 7
self.Title.x = 100
self.Title.y = 135
self.Title._text = 'Chart Title'
self.Title.maxWidth = 180
self.Title.height = 20
self.Title.textAnchor ='middle'
self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart")
self.Legend.colorNamePairs = [(color01, 'Widgets'), (color02, 'Sprockets')]
self.Legend.fontName = 'Helvetica'
self.Legend.fontSize = 7
self.Legend.x = 153
self.Legend.y = 85
self.Legend.dxTextSpace = 5
self.Legend.dy = 5
self.Legend.dx = 5
self.Legend.deltay = 5
self.Legend.alignment ='right'
self.chart.lineLabelFormat = None
self.chart.xLabel = 'X Axis'
self.chart.y = 30
self.chart.yLabel = 'Y Axis'
self.chart.yValueAxis.labelTextFormat = '%d'
self.chart.yValueAxis.forceZero = 1
self.chart.xValueAxis.forceZero = 1
self._add(self,0,name='preview',validate=None,desc=None)
if __name__=="__main__": #NORUNTESTS
Scatter().save(formats=['pdf'],outDir=None,fnRoot='scatter')

View File

@ -1,82 +0,0 @@
#Autogenerated by ReportLab guiedit do not edit
from reportlab.graphics.charts.legends import Legend
from reportlab.graphics.charts.lineplots import ScatterPlot
from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
from reportlab.graphics.charts.textlabels import Label
from excelcolors import *
class ScatterLines(_DrawingEditorMixin,Drawing):
def __init__(self,width=200,height=150,*args,**kw):
apply(Drawing.__init__,(self,width,height)+args,kw)
self._add(self,ScatterPlot(),name='chart',validate=None,desc="The main chart")
self.chart.width = 115
self.chart.height = 80
self.chart.x = 30
self.chart.y = 40
self.chart.lines[0].strokeColor = color01
self.chart.lines[1].strokeColor = color02
self.chart.lines[2].strokeColor = color03
self.chart.lines[3].strokeColor = color04
self.chart.lines[4].strokeColor = color05
self.chart.lines[5].strokeColor = color06
self.chart.lines[6].strokeColor = color07
self.chart.lines[7].strokeColor = color08
self.chart.lines[8].strokeColor = color09
self.chart.lines[9].strokeColor = color10
self.chart.lines[0].symbol = None
self.chart.lines[1].symbol = None
self.chart.lines[2].symbol = None
self.chart.lines[3].symbol = None
self.chart.lines[4].symbol = None
self.chart.lines[5].symbol = None
self.chart.lines[6].symbol = None
self.chart.lines[7].symbol = None
self.chart.lines[8].symbol = None
self.chart.lines[9].symbol = None
self.chart.fillColor = backgroundGrey
self.chart.lineLabels.fontName = 'Helvetica'
self.chart.xValueAxis.labels.fontName = 'Helvetica'
self.chart.xValueAxis.labels.fontSize = 7
self.chart.xValueAxis.forceZero = 0
self.chart.data = [((100,100), (200,200), (250,210), (300,300), (400,500)), ((100,200), (200,300), (250,200), (300,400), (400, 600))]
self.chart.xValueAxis.avoidBoundFrac = 1
self.chart.xValueAxis.gridEnd = 115
self.chart.xValueAxis.tickDown = 3
self.chart.xValueAxis.visibleGrid = 1
self.chart.yValueAxis.tickLeft = 3
self.chart.yValueAxis.labels.fontName = 'Helvetica'
self.chart.yValueAxis.labels.fontSize = 7
self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart")
self.Title.fontName = 'Helvetica-Bold'
self.Title.fontSize = 7
self.Title.x = 100
self.Title.y = 135
self.Title._text = 'Chart Title'
self.Title.maxWidth = 180
self.Title.height = 20
self.Title.textAnchor ='middle'
self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart")
self.Legend.colorNamePairs = [(color01, 'Widgets'), (color02, 'Sprockets')]
self.Legend.fontName = 'Helvetica'
self.Legend.fontSize = 7
self.Legend.x = 153
self.Legend.y = 85
self.Legend.dxTextSpace = 5
self.Legend.dy = 5
self.Legend.dx = 5
self.Legend.deltay = 5
self.Legend.alignment ='right'
self.chart.lineLabelFormat = None
self.chart.xLabel = 'X Axis'
self.chart.y = 30
self.chart.yLabel = 'Y Axis'
self.chart.yValueAxis.gridEnd = 115
self.chart.yValueAxis.visibleGrid = 1
self.chart.yValueAxis.labelTextFormat = '%d'
self.chart.yValueAxis.forceZero = 1
self.chart.xValueAxis.forceZero = 1
self.chart.joinedLines = 1
self._add(self,0,name='preview',validate=None,desc=None)
if __name__=="__main__": #NORUNTESTS
ScatterLines().save(formats=['pdf'],outDir=None,fnRoot='scatter_lines')

View File

@ -1,72 +0,0 @@
#Autogenerated by ReportLab guiedit do not edit
from reportlab.graphics.charts.legends import Legend
from reportlab.graphics.charts.lineplots import ScatterPlot
from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
from reportlab.graphics.charts.textlabels import Label
from excelcolors import *
class ScatterLinesMarkers(_DrawingEditorMixin,Drawing):
def __init__(self,width=200,height=150,*args,**kw):
apply(Drawing.__init__,(self,width,height)+args,kw)
self._add(self,ScatterPlot(),name='chart',validate=None,desc="The main chart")
self.chart.width = 115
self.chart.height = 80
self.chart.x = 30
self.chart.y = 40
self.chart.lines[0].strokeColor = color01
self.chart.lines[1].strokeColor = color02
self.chart.lines[2].strokeColor = color03
self.chart.lines[3].strokeColor = color04
self.chart.lines[4].strokeColor = color05
self.chart.lines[5].strokeColor = color06
self.chart.lines[6].strokeColor = color07
self.chart.lines[7].strokeColor = color08
self.chart.lines[8].strokeColor = color09
self.chart.lines[9].strokeColor = color10
self.chart.fillColor = backgroundGrey
self.chart.lineLabels.fontName = 'Helvetica'
self.chart.xValueAxis.labels.fontName = 'Helvetica'
self.chart.xValueAxis.labels.fontSize = 7
self.chart.xValueAxis.forceZero = 0
self.chart.data = [((100,100), (200,200), (250,210), (300,300), (400,500)), ((100,200), (200,300), (250,200), (300,400), (400, 600))]
self.chart.xValueAxis.avoidBoundFrac = 1
self.chart.xValueAxis.gridEnd = 115
self.chart.xValueAxis.tickDown = 3
self.chart.xValueAxis.visibleGrid = 1
self.chart.yValueAxis.tickLeft = 3
self.chart.yValueAxis.labels.fontName = 'Helvetica'
self.chart.yValueAxis.labels.fontSize = 7
self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart")
self.Title.fontName = 'Helvetica-Bold'
self.Title.fontSize = 7
self.Title.x = 100
self.Title.y = 135
self.Title._text = 'Chart Title'
self.Title.maxWidth = 180
self.Title.height = 20
self.Title.textAnchor ='middle'
self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart")
self.Legend.colorNamePairs = [(color01, 'Widgets'), (color02, 'Sprockets')]
self.Legend.fontName = 'Helvetica'
self.Legend.fontSize = 7
self.Legend.x = 153
self.Legend.y = 85
self.Legend.dxTextSpace = 5
self.Legend.dy = 5
self.Legend.dx = 5
self.Legend.deltay = 5
self.Legend.alignment ='right'
self.chart.lineLabelFormat = None
self.chart.xLabel = 'X Axis'
self.chart.y = 30
self.chart.yLabel = 'Y Axis'
self.chart.yValueAxis.gridEnd = 115
self.chart.yValueAxis.visibleGrid = 1
self.chart.yValueAxis.labelTextFormat = '%d'
self.chart.yValueAxis.forceZero = 1
self.chart.xValueAxis.forceZero = 1
self.chart.joinedLines = 1
self._add(self,0,name='preview',validate=None,desc=None)
if __name__=="__main__": #NORUNTESTS
ScatterLinesMarkers().save(formats=['pdf'],outDir=None,fnRoot='scatter_lines_markers')

View File

@ -1,61 +0,0 @@
#Autogenerated by ReportLab guiedit do not edit
from reportlab.graphics.charts.piecharts import Pie
from reportlab.graphics.widgets.grids import ShadedRect
from reportlab.graphics.charts.legends import Legend
from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
from reportlab.graphics.charts.textlabels import Label
from excelcolors import *
class SimplePie(_DrawingEditorMixin,Drawing):
def __init__(self,width=200,height=150,*args,**kw):
apply(Drawing.__init__,(self,width,height)+args,kw)
self._add(self,Pie(),name='chart',validate=None,desc="The main chart")
self.chart.width = 100
self.chart.height = 100
self.chart.x = 25
self.chart.y = 25
self.chart.slices[0].fillColor = color01
self.chart.slices[1].fillColor = color02
self.chart.slices[2].fillColor = color03
self.chart.slices[3].fillColor = color04
self.chart.slices[4].fillColor = color05
self.chart.slices[5].fillColor = color06
self.chart.slices[6].fillColor = color07
self.chart.slices[7].fillColor = color08
self.chart.slices[8].fillColor = color09
self.chart.slices[9].fillColor = color10
self.chart.data = (100, 150, 180)
self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart")
self.Title.fontName = 'Helvetica-Bold'
self.Title.fontSize = 7
self.Title.x = 100
self.Title.y = 135
self.Title._text = 'Chart Title'
self.Title.maxWidth = 180
self.Title.height = 20
self.Title.textAnchor ='middle'
self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart")
self.Legend.colorNamePairs = [(color01, 'North'), (color02, 'South'),(color03, 'Central')]
self.Legend.fontName = 'Helvetica'
self.Legend.fontSize = 7
self.Legend.x = 160
self.Legend.y = 85
self.Legend.dxTextSpace = 5
self.Legend.dy = 5
self.Legend.dx = 5
self.Legend.deltay = 5
self.Legend.alignment ='right'
self.chart.slices.strokeWidth = 1
self.chart.slices.fontName = 'Helvetica'
self.background = ShadedRect()
self.background.fillColorStart = backgroundGrey
self.background.fillColorEnd = backgroundGrey
self.background.numShades = 1
self.background.strokeWidth = 0.5
self.background.x = 25
self.background.y = 25
self.Legend.columnMaximum = 10
self._add(self,0,name='preview',validate=None,desc=None)
if __name__=="__main__": #NORUNTESTS
SimplePie().save(formats=['pdf'],outDir=None,fnRoot=None)

View File

@ -1,85 +0,0 @@
#Autogenerated by ReportLab guiedit do not edit
from reportlab.graphics.charts.legends import Legend
from reportlab.graphics.charts.barcharts import HorizontalBarChart
from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
from reportlab.graphics.charts.textlabels import Label
from excelcolors import *
class StackedBar(_DrawingEditorMixin,Drawing):
def __init__(self,width=200,height=150,*args,**kw):
apply(Drawing.__init__,(self,width,height)+args,kw)
self._add(self,HorizontalBarChart(),name='chart',validate=None,desc="The main chart")
self.chart.width = 115
self.chart.height = 80
self.chart.x = 30
self.chart.y = 40
self.chart.bars[0].fillColor = color01
self.chart.bars[1].fillColor = color02
self.chart.bars[2].fillColor = color03
self.chart.bars[3].fillColor = color04
self.chart.bars[4].fillColor = color05
self.chart.bars[5].fillColor = color06
self.chart.bars[6].fillColor = color07
self.chart.bars[7].fillColor = color08
self.chart.bars[8].fillColor = color09
self.chart.bars[9].fillColor = color10
self.chart.fillColor = backgroundGrey
self.chart.barLabels.fontName = 'Helvetica'
self.chart.valueAxis.labels.fontName = 'Helvetica'
self.chart.valueAxis.labels.fontSize = 6
self.chart.valueAxis.forceZero = 1
self.chart.data = [(100, 150, 180), (125, 180, 200)]
self.chart.groupSpacing = 15
self.chart.valueAxis.avoidBoundFrac = 1
self.chart.valueAxis.gridEnd = 80
self.chart.valueAxis.tickDown = 3
self.chart.valueAxis.visibleGrid = 1
self.chart.categoryAxis.categoryNames = ['North', 'South', 'Central']
self.chart.categoryAxis.tickLeft = 3
self.chart.categoryAxis.labels.fontName = 'Helvetica'
self.chart.categoryAxis.labels.fontSize = 6
self.chart.categoryAxis.labels.dx = -3
self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart")
self.Title.fontName = 'Helvetica-Bold'
self.Title.fontSize = 7
self.Title.x = 100
self.Title.y = 135
self.Title._text = 'Chart Title'
self.Title.maxWidth = 180
self.Title.height = 20
self.Title.textAnchor ='middle'
self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart")
self.Legend.colorNamePairs = [(color01, 'Widgets'), (color02, 'Sprockets')]
self.Legend.fontName = 'Helvetica'
self.Legend.fontSize = 7
self.Legend.x = 153
self.Legend.y = 85
self.Legend.dxTextSpace = 5
self.Legend.dy = 5
self.Legend.dx = 5
self.Legend.deltay = 5
self.Legend.alignment ='right'
self._add(self,Label(),name='XLabel',validate=None,desc="The label on the horizontal axis")
self.XLabel.fontName = 'Helvetica'
self.XLabel.fontSize = 7
self.XLabel.x = 85
self.XLabel.y = 10
self.XLabel.textAnchor ='middle'
self.XLabel.maxWidth = 100
self.XLabel.height = 20
self.XLabel._text = "X Axis"
self._add(self,Label(),name='YLabel',validate=None,desc="The label on the vertical axis")
self.YLabel.fontName = 'Helvetica'
self.YLabel.fontSize = 7
self.YLabel.x = 12
self.YLabel.y = 80
self.YLabel.angle = 90
self.YLabel.textAnchor ='middle'
self.YLabel.maxWidth = 100
self.YLabel.height = 20
self.YLabel._text = "Y Axis"
self.chart.categoryAxis.style='stacked'
self._add(self,0,name='preview',validate=None,desc=None)
if __name__=="__main__": #NORUNTESTS
StackedBar().save(formats=['pdf'],outDir=None,fnRoot='stacked_bar')

View File

@ -1,84 +0,0 @@
#Autogenerated by ReportLab guiedit do not edit
from reportlab.graphics.charts.legends import Legend
from reportlab.graphics.charts.barcharts import VerticalBarChart
from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, String
from reportlab.graphics.charts.textlabels import Label
from excelcolors import *
class StackedColumn(_DrawingEditorMixin,Drawing):
def __init__(self,width=200,height=150,*args,**kw):
apply(Drawing.__init__,(self,width,height)+args,kw)
self._add(self,VerticalBarChart(),name='chart',validate=None,desc="The main chart")
self.chart.width = 115
self.chart.height = 80
self.chart.x = 30
self.chart.y = 40
self.chart.bars[0].fillColor = color01
self.chart.bars[1].fillColor = color02
self.chart.bars[2].fillColor = color03
self.chart.bars[3].fillColor = color04
self.chart.bars[4].fillColor = color05
self.chart.bars[5].fillColor = color06
self.chart.bars[6].fillColor = color07
self.chart.bars[7].fillColor = color08
self.chart.bars[8].fillColor = color09
self.chart.bars[9].fillColor = color10
self.chart.fillColor = backgroundGrey
self.chart.barLabels.fontName = 'Helvetica'
self.chart.valueAxis.labels.fontName = 'Helvetica'
self.chart.valueAxis.labels.fontSize = 7
self.chart.valueAxis.forceZero = 1
self.chart.data = [(100, 150, 180), (125, 180, 200)]
self.chart.groupSpacing = 15
self.chart.valueAxis.avoidBoundFrac = 1
self.chart.valueAxis.gridEnd = 115
self.chart.valueAxis.tickLeft = 3
self.chart.valueAxis.visibleGrid = 1
self.chart.categoryAxis.categoryNames = ['North', 'South', 'Central']
self.chart.categoryAxis.tickDown = 3
self.chart.categoryAxis.labels.fontName = 'Helvetica'
self.chart.categoryAxis.labels.fontSize = 7
self._add(self,Label(),name='Title',validate=None,desc="The title at the top of the chart")
self.Title.fontName = 'Helvetica-Bold'
self.Title.fontSize = 7
self.Title.x = 100
self.Title.y = 135
self.Title._text = 'Chart Title'
self.Title.maxWidth = 180
self.Title.height = 20
self.Title.textAnchor ='middle'
self._add(self,Legend(),name='Legend',validate=None,desc="The legend or key for the chart")
self.Legend.colorNamePairs = [(color01, 'Widgets'), (color02, 'Sprockets')]
self.Legend.fontName = 'Helvetica'
self.Legend.fontSize = 7
self.Legend.x = 153
self.Legend.y = 85
self.Legend.dxTextSpace = 5
self.Legend.dy = 5
self.Legend.dx = 5
self.Legend.deltay = 5
self.Legend.alignment ='right'
self._add(self,Label(),name='XLabel',validate=None,desc="The label on the horizontal axis")
self.XLabel.fontName = 'Helvetica'
self.XLabel.fontSize = 7
self.XLabel.x = 85
self.XLabel.y = 10
self.XLabel.textAnchor ='middle'
self.XLabel.maxWidth = 100
self.XLabel.height = 20
self.XLabel._text = "X Axis"
self._add(self,Label(),name='YLabel',validate=None,desc="The label on the vertical axis")
self.YLabel.fontName = 'Helvetica'
self.YLabel.fontSize = 7
self.YLabel.x = 12
self.YLabel.y = 80
self.YLabel.angle = 90
self.YLabel.textAnchor ='middle'
self.YLabel.maxWidth = 100
self.YLabel.height = 20
self.YLabel._text = "Y Axis"
self.chart.categoryAxis.style='stacked'
self._add(self,0,name='preview',validate=None,desc=None)
if __name__=="__main__": #NORUNTESTS
StackedColumn().save(formats=['pdf'],outDir=None,fnRoot='stacked_column')

File diff suppressed because it is too large Load Diff

View File

@ -1,294 +0,0 @@
#! /usr/bin/python2.3
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/testdrawings.py
__version__=''' $Id $ '''
"""This contains a number of routines to generate test drawings
for reportlab/graphics. For now they are contrived, but we will expand them
to try and trip up any parser. Feel free to add more.
"""
from reportlab.graphics.shapes import *
from reportlab.lib import colors
def getDrawing1():
"""Hello World, on a rectangular background"""
D = Drawing(400, 200)
D.add(Rect(50, 50, 300, 100, fillColor=colors.yellow)) #round corners
D.add(String(180,100, 'Hello World', fillColor=colors.red))
return D
def getDrawing2():
"""This demonstrates the basic shapes. There are
no groups or references. Each solid shape should have
a purple fill."""
D = Drawing(400, 200) #, fillColor=colors.purple)
D.add(Line(10,10,390,190))
D.add(Circle(100,100,20, fillColor=colors.purple))
D.add(Circle(200,100,20, fillColor=colors.purple))
D.add(Circle(300,100,20, fillColor=colors.purple))
D.add(Wedge(330,100,40, -10,40, fillColor=colors.purple))
D.add(PolyLine([120,10,130,20,140,10,150,20,160,10,
170,20,180,10,190,20,200,10]))
D.add(Polygon([300,20,350,20,390,80,300,75, 330, 40]))
D.add(Ellipse(50, 150, 40, 20))
D.add(Rect(120, 150, 60, 30,
strokeWidth=10,
strokeColor=colors.red,
fillColor=colors.yellow)) #square corners
D.add(Rect(220, 150, 60, 30, 10, 10)) #round corners
D.add(String(10,50, 'Basic Shapes', fillColor=colors.black))
return D
##def getDrawing2():
## """This drawing uses groups. Each group has two circles and a comment.
## The line style is set at group level and should be red for the left,
## bvlue for the right."""
## D = Drawing(400, 200)
##
## Group1 = Group()
##
## Group1.add(String(50, 50, 'Group 1', fillColor=colors.black))
## Group1.add(Circle(75,100,25))
## Group1.add(Circle(125,100,25))
## D.add(Group1)
##
## Group2 = Group(
## String(250, 50, 'Group 2', fillColor=colors.black),
## Circle(275,100,25),
## Circle(325,100,25)#,
##def getDrawing2():
## """This drawing uses groups. Each group has two circles and a comment.
## The line style is set at group level and should be red for the left,
## bvlue for the right."""
## D = Drawing(400, 200)
##
## Group1 = Group()
##
## Group1.add(String(50, 50, 'Group 1', fillColor=colors.black))
## Group1.add(Circle(75,100,25))
## Group1.add(Circle(125,100,25))
## D.add(Group1)
##
## Group2 = Group(
## String(250, 50, 'Group 2', fillColor=colors.black),
## Circle(275,100,25),
## Circle(325,100,25)#,
##
## #group attributes
## #strokeColor=colors.blue
## )
## D.add(Group2)
## return D
##
##
##def getDrawing3():
## """This uses a named reference object. The house is a 'subroutine'
## the basic brick colored walls are defined, but the roof and window
## color are undefined and may be set by the container."""
##
## D = Drawing(400, 200, fill=colors.bisque)
##
##
## House = Group(
## Rect(2,20,36,30, fill=colors.bisque), #walls
## Polygon([0,20,40,20,20,5]), #roof
## Rect(8, 38, 8, 12), #door
## Rect(25, 38, 8, 7), #window
## Rect(8, 25, 8, 7), #window
## Rect(25, 25, 8, 7) #window
##
## )
## D.addDef('MyHouse', House)
##
## # one row all the same color
## D.add(String(20, 40, 'British Street...',fill=colors.black))
## for i in range(6):
## x = i * 50
## D.add(NamedReference('MyHouse',
## House,
## transform=translate(x, 40),
## fill = colors.brown
## )
## )
##
## # now do a row all different
## D.add(String(20, 120, 'Mediterranean Street...',fill=colors.black))
## x = 0
## for color in (colors.blue, colors.yellow, colors.orange,
## colors.red, colors.green, colors.chartreuse):
## D.add(NamedReference('MyHouse',
## House,
## transform=translate(x,120),
## fill = color,
## )
## )
## x = x + 50
## #..by popular demand, the mayor gets a big one at the end
## D.add(NamedReference('MyHouse',
## House,
## transform=mmult(translate(x,110), scale(1.2,1.2)),
## fill = color,
## )
## )
##
##
## return D
##
##def getDrawing4():
## """This tests that attributes are 'unset' correctly when
## one steps back out of a drawing node. All the circles are part of a
## group setting the line color to blue; the second circle explicitly
## sets it to red. Ideally, the third circle should go back to blue."""
## D = Drawing(400, 200)
##
##
## G = Group(
## Circle(100,100,20),
## Circle(200,100,20, stroke=colors.blue),
## Circle(300,100,20),
## stroke=colors.red,
## stroke_width=3,
## fill=colors.aqua
## )
## D.add(G)
##
##
## D.add(String(10,50, 'Stack Unwinding - should be red, blue, red'))
##
## return D
##
##
##def getDrawing5():
## """This Rotates Coordinate Axes"""
## D = Drawing(400, 200)
##
##
##
## Axis = Group(
## Line(0,0,100,0), #x axis
## Line(0,0,0,50), # y axis
## Line(0,10,10,10), #ticks on y axis
## Line(0,20,10,20),
## Line(0,30,10,30),
## Line(0,40,10,40),
## Line(10,0,10,10), #ticks on x axis
## Line(20,0,20,10),
## Line(30,0,30,10),
## Line(40,0,40,10),
## Line(50,0,50,10),
## Line(60,0,60,10),
## Line(70,0,70,10),
## Line(80,0,80,10),
## Line(90,0,90,10),
## String(20, 35, 'Axes', fill=colors.black)
## )
##
## D.addDef('Axes', Axis)
##
## D.add(NamedReference('Axis', Axis,
## transform=translate(10,10)))
## D.add(NamedReference('Axis', Axis,
## transform=mmult(translate(150,10),rotate(15)))
## )
## return D
##
##def getDrawing6():
## """This Rotates Text"""
## D = Drawing(400, 300, fill=colors.black)
##
## xform = translate(200,150)
## C = (colors.black,colors.red,colors.green,colors.blue,colors.brown,colors.gray, colors.pink,
## colors.lavender,colors.lime, colors.mediumblue, colors.magenta, colors.limegreen)
##
## for i in range(12):
## D.add(String(0, 0, ' - - Rotated Text', fill=C[i%len(C)], transform=mmult(xform, rotate(30*i))))
##
## return D
##
##def getDrawing7():
## """This defines and tests a simple UserNode0 (the trailing zero denotes
## an experimental method which is not part of the supported API yet).
## Each of the four charts is a subclass of UserNode which generates a random
## series when rendered."""
##
## class MyUserNode(UserNode0):
## import whrandom, math
##
##
## def provideNode(self, sender):
## """draw a simple chart that changes everytime it's drawn"""
## # print "here's a random number %s" % self.whrandom.random()
## #print "MyUserNode.provideNode being called by %s" % sender
## g = Group()
## #g._state = self._state # this is naughty
## PingoNode.__init__(g, self._state) # is this less naughty ?
## w = 80.0
## h = 50.0
## g.add(Rect(0,0, w, h, stroke=colors.black))
## N = 10.0
## x,y = (0,h)
## dx = w/N
## for ii in range(N):
## dy = (h/N) * self.whrandom.random()
## g.add(Line(x,y,x+dx, y-dy))
## x = x + dx
## y = y - dy
## return g
##
## D = Drawing(400,200, fill=colors.white) # AR - same size as others
##
## D.add(MyUserNode())
##
## graphcolor= [colors.green, colors.red, colors.brown, colors.purple]
## for ii in range(4):
## D.add(Group( MyUserNode(stroke=graphcolor[ii], stroke_width=2),
## transform=translate(ii*90,0) ))
##
## #un = MyUserNode()
## #print un.provideNode()
## return D
##
##def getDrawing8():
## """Test Path operations--lineto, curveTo, etc."""
## D = Drawing(400, 200, fill=None, stroke=colors.purple, stroke_width=2)
##
## xform = translate(200,100)
## C = (colors.black,colors.red,colors.green,colors.blue,colors.brown,colors.gray, colors.pink,
## colors.lavender,colors.lime, colors.mediumblue, colors.magenta, colors.limegreen)
## p = Path(50,50)
## p.lineTo(100,100)
## p.moveBy(-25,25)
## p.curveTo(150,125, 125,125, 200,50)
## p.curveTo(175, 75, 175, 98, 62, 87)
##
##
## D.add(p)
## D.add(String(10,30, 'Tests of path elements-lines and bezier curves-and text formating'))
## D.add(Line(220,150, 220,200, stroke=colors.red))
## D.add(String(220,180, "Text should be centered", text_anchor="middle") )
##
##
## return D
if __name__=='__main__':
print __doc__

View File

@ -1,547 +0,0 @@
#! /usr/bin/python2.3
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/testshapes.py
# testshapes.py - draws shapes onto a PDF canvas.
"""
Execute the script to see some test drawings.
This contains a number of routines to generate test drawings
for reportlab/graphics. For now many of them are contrived,
but we will expand them to try and trip up any parser.
Feel free to add more.
"""
__version__ = ''' $Id $ '''
import os, sys
from reportlab.lib import colors
from reportlab.lib.units import cm
from reportlab.pdfgen.canvas import Canvas
from reportlab.pdfbase.pdfmetrics import stringWidth
from reportlab.platypus import Flowable
from reportlab.graphics.shapes import *
from reportlab.graphics.renderPDF import _PDFRenderer
import unittest
_FONTS = ['Times-Roman','Courier','Times-BoldItalic',]
#########################################################
#
# Collections of shape drawings.
#
#########################################################
def getFailedDrawing(funcName):
"""Generate a drawing in case something goes really wrong.
This will create a drawing to be displayed whenever some
other drawing could not be executed, because the generating
function does something terribly wrong! The box contains
an attention triangle, plus some error message.
"""
D = Drawing(400, 200)
points = [200,170, 140,80, 260,80]
D.add(Polygon(points,
strokeWidth=0.5*cm,
strokeColor=colors.red,
fillColor=colors.yellow))
s = String(200, 40,
"Error in generating function '%s'!" % funcName,
textAnchor='middle')
D.add(s)
return D
# These are the real drawings to be eye-balled.
def getDrawing01():
"""Hello World, on a rectangular background.
The rectangle's fillColor is yellow.
The string's fillColor is red.
"""
D = Drawing(400, 200)
D.add(Rect(50, 50, 300, 100, fillColor=colors.yellow))
D.add(String(180,100, 'Hello World', fillColor=colors.red))
return D
def getDrawing02():
"""Various Line shapes.
The lines are blue and their strokeWidth is 5 mm.
One line has a strokeDashArray set to [5, 10, 15].
"""
D = Drawing(400, 200)
D.add(Line(50,50, 300,100,
strokeColor=colors.blue,
strokeWidth=0.5*cm,
))
D.add(Line(50,100, 300,50,
strokeColor=colors.blue,
strokeWidth=0.5*cm,
strokeDashArray=[5, 10, 15],
))
#x = 1/0 # Comment this to see the actual drawing!
return D
def getDrawing03():
"""Text strings in various sizes and different fonts.
Font size increases from 12 to 36 and from bottom left
to upper right corner. The first ones should be in
Times-Roman. Finally, a solitary Courier string at
the top right corner.
"""
D = Drawing(400, 200)
for size in range(12, 36, 4):
D.add(String(10+size*2,
10+size*2,
'Hello World',
fontName=_FONTS[0],
fontSize=size))
D.add(String(150, 150,
'Hello World',
fontName=_FONTS[1],
fontSize=36))
return D
def getDrawing04():
"""Text strings in various colours.
Colours are blue, yellow and red from bottom left
to upper right.
"""
D = Drawing(400, 200)
i = 0
for color in (colors.blue, colors.yellow, colors.red):
D.add(String(50+i*30, 50+i*30,
'Hello World', fillColor=color))
i = i + 1
return D
def getDrawing05():
"""Text strings with various anchors (alignments).
Text alignment conforms to the anchors in the left column.
"""
D = Drawing(400, 200)
lineX = 250
D.add(Line(lineX,10, lineX,190, strokeColor=colors.gray))
y = 130
for anchor in ('start', 'middle', 'end'):
D.add(String(lineX, y, 'Hello World', textAnchor=anchor))
D.add(String(50, y, anchor + ':'))
y = y - 30
return D
def getDrawing06():
"""This demonstrates all the basic shapes at once.
There are no groups or references.
Each solid shape should have a purple fill.
"""
purple = colors.purple
purple = colors.green
D = Drawing(400, 200) #, fillColor=purple)
D.add(Line(10,10, 390,190))
D.add(Circle(100,100,20, fillColor=purple))
D.add(Circle(200,100,40, fillColor=purple))
D.add(Circle(300,100,30, fillColor=purple))
D.add(Wedge(330,100,40, -10,40, fillColor=purple))
D.add(PolyLine([120,10, 130,20, 140,10, 150,20, 160,10,
170,20, 180,10, 190,20, 200,10], fillColor=purple))
D.add(Polygon([300,20, 350,20, 390,80, 300,75, 330,40], fillColor=purple))
D.add(Ellipse(50,150, 40, 20, fillColor=purple))
D.add(Rect(120,150, 60,30,
strokeWidth=10,
strokeColor=colors.yellow,
fillColor=purple)) #square corners
D.add(Rect(220, 150, 60, 30, 10, 10, fillColor=purple)) #round corners
D.add(String(10,50, 'Basic Shapes', fillColor=colors.black))
return D
def getDrawing07():
"""This tests the ability to translate and rotate groups. The first set of axes should be
near the bottom left of the drawing. The second should be rotated counterclockwise
by 15 degrees. The third should be rotated by 30 degrees."""
D = Drawing(400, 200)
Axis = Group(
Line(0,0,100,0), #x axis
Line(0,0,0,50), # y axis
Line(0,10,10,10), #ticks on y axis
Line(0,20,10,20),
Line(0,30,10,30),
Line(0,40,10,40),
Line(10,0,10,10), #ticks on x axis
Line(20,0,20,10),
Line(30,0,30,10),
Line(40,0,40,10),
Line(50,0,50,10),
Line(60,0,60,10),
Line(70,0,70,10),
Line(80,0,80,10),
Line(90,0,90,10),
String(20, 35, 'Axes', fill=colors.black)
)
firstAxisGroup = Group(Axis)
firstAxisGroup.translate(10,10)
D.add(firstAxisGroup)
secondAxisGroup = Group(Axis)
secondAxisGroup.translate(150,10)
secondAxisGroup.rotate(15)
D.add(secondAxisGroup)
thirdAxisGroup = Group(Axis, transform=mmult(translate(300,10), rotate(30)))
D.add(thirdAxisGroup)
return D
def getDrawing08():
"""This tests the ability to scale coordinates. The bottom left set of axes should be
near the bottom left of the drawing. The bottom right should be stretched vertically
by a factor of 2. The top left one should be stretched horizontally by a factor of 2.
The top right should have the vertical axiss leaning over to the right by 30 degrees."""
D = Drawing(400, 200)
Axis = Group(
Line(0,0,100,0), #x axis
Line(0,0,0,50), # y axis
Line(0,10,10,10), #ticks on y axis
Line(0,20,10,20),
Line(0,30,10,30),
Line(0,40,10,40),
Line(10,0,10,10), #ticks on x axis
Line(20,0,20,10),
Line(30,0,30,10),
Line(40,0,40,10),
Line(50,0,50,10),
Line(60,0,60,10),
Line(70,0,70,10),
Line(80,0,80,10),
Line(90,0,90,10),
String(20, 35, 'Axes', fill=colors.black)
)
firstAxisGroup = Group(Axis)
firstAxisGroup.translate(10,10)
D.add(firstAxisGroup)
secondAxisGroup = Group(Axis)
secondAxisGroup.translate(150,10)
secondAxisGroup.scale(1,2)
D.add(secondAxisGroup)
thirdAxisGroup = Group(Axis)
thirdAxisGroup.translate(10,125)
thirdAxisGroup.scale(2,1)
D.add(thirdAxisGroup)
fourthAxisGroup = Group(Axis)
fourthAxisGroup.translate(250,125)
fourthAxisGroup.skew(30,0)
D.add(fourthAxisGroup)
return D
def getDrawing09():
"""This tests rotated strings
Some renderers will have a separate mechanism for font drawing. This test
just makes sure strings get transformed the same way as regular graphics."""
D = Drawing(400, 200)
fontName = _FONTS[0]
fontSize = 12
text = "I should be totally horizontal and enclosed in a box"
textWidth = stringWidth(text, fontName, fontSize)
g1 = Group(
String(20, 20, text, fontName=fontName, fontSize = fontSize),
Rect(18, 18, textWidth + 4, fontSize + 4, fillColor=None)
)
D.add(g1)
text = "I should slope up by 15 degrees, so my right end is higher than my left"
textWidth = stringWidth(text, fontName, fontSize)
g2 = Group(
String(20, 20, text, fontName=fontName, fontSize = fontSize),
Rect(18, 18, textWidth + 4, fontSize + 4, fillColor=None)
)
g2.translate(0, 50)
g2.rotate(15)
D.add(g2)
return D
def getDrawing10():
"""This tests nested groups with multiple levels of coordinate transformation.
Each box should be staggered up and to the right, moving by 25 points each time."""
D = Drawing(400, 200)
fontName = _FONTS[0]
fontSize = 12
g1 = Group(
Rect(0, 0, 100, 20, fillColor=colors.yellow),
String(5, 5, 'Text in the box', fontName=fontName, fontSize = fontSize)
)
D.add(g1)
g2 = Group(g1, transform = translate(25,25))
D.add(g2)
g3 = Group(g2, transform = translate(25,25))
D.add(g3)
g4 = Group(g3, transform = translate(25,25))
D.add(g4)
return D
from widgets.signsandsymbols import SmileyFace
def getDrawing11():
'''test of anchoring'''
def makeSmiley(x, y, size, color):
"Make a smiley data item representation."
d = size
s = SmileyFace()
s.fillColor = color
s.x = x-d
s.y = y-d
s.size = d*2
return s
D = Drawing(400, 200) #, fillColor=colors.purple)
g = Group(transform=(1,0,0,1,0,0))
g.add(makeSmiley(100,100,10,colors.red))
g.add(Line(90,100,110,100,strokeColor=colors.green))
g.add(Line(100,90,100,110,strokeColor=colors.green))
D.add(g)
g = Group(transform=(2,0,0,2,100,-100))
g.add(makeSmiley(100,100,10,colors.blue))
g.add(Line(90,100,110,100,strokeColor=colors.green))
g.add(Line(100,90,100,110,strokeColor=colors.green))
D.add(g)
g = Group(transform=(2,0,0,2,0,0))
return D
def getDrawing12():
"""Text strings in a non-standard font.
All that is required is to place the .afm and .pfb files
on the font patch given in rl_config.py,
for example in reportlab/lib/fonts/.
"""
faceName = "Wargames-Regular"
D = Drawing(400, 200)
for size in range(12, 36, 4):
D.add(String(10+size*2,
10+size*2,
'Hello World',
fontName=faceName,
fontSize=size))
return D
def getDrawing13():
'Test Various TTF Fonts'
from reportlab.pdfbase import pdfmetrics, ttfonts
pdfmetrics.registerFont(ttfonts.TTFont("LuxiSerif", "luxiserif.ttf"))
pdfmetrics.registerFont(ttfonts.TTFont("Rina", "rina.ttf"))
_FONTS[1] = 'LuxiSerif'
_FONTS[2] = 'Rina'
F = ['Times-Roman','LuxiSerif', 'Rina']
if sys.platform=='win32':
for name, ttf in [('Adventurer Light SF','Advlit.ttf'),('ArialMS','ARIAL.TTF'),
('Book Antiqua','BKANT.TTF'),
('Century Gothic','GOTHIC.TTF'),
('Comic Sans MS', 'COMIC.TTF'),
('Elementary Heavy SF Bold','Vwagh.ttf'),
('Firenze SF','flot.ttf'),
('Garamond','GARA.TTF'),
('Jagger','Rols.ttf'),
('Monotype Corsiva','MTCORSVA.TTF'),
('Seabird SF','seag.ttf'),
('Tahoma','TAHOMA.TTF'),
('VerdanaMS','VERDANA.TTF'),
]:
for D in ('c:\WINNT','c:\Windows'):
fn = os.path.join(D,'Fonts',ttf)
if os.path.isfile(fn):
try:
f = ttfonts.TTFont(name, fn)
pdfmetrics.registerFont(f)
F.append(name)
except:
pass
def drawit(F,w=400,h=200,fontSize=12,slack=2,gap=5):
D = Drawing(w,h)
th = 2*gap + fontSize*1.2
gh = gap + .2*fontSize
y = h
maxx = 0
for fontName in F:
y -= th
text = fontName+": I should be totally horizontal and enclosed in a box"
textWidth = stringWidth(text, fontName, fontSize)
maxx = max(maxx,textWidth+20)
D.add(
Group(Rect(8, y-gh, textWidth + 4, th, strokeColor=colors.red, strokeWidth=.5, fillColor=colors.lightgrey),
String(10, y, text, fontName=fontName, fontSize = fontSize)))
y -= 5
return maxx, h-y+gap, D
maxx, maxy, D = drawit(F)
if maxx>400 or maxy>200: _,_,D = drawit(F,maxx,maxy)
return D
def getAllFunctionDrawingNames(doTTF=1):
"Get a list of drawing function names from somewhere."
funcNames = []
# Here we get the names from the global name space.
symbols = globals().keys()
symbols.sort()
for funcName in symbols:
if funcName[0:10] == 'getDrawing':
if doTTF or funcName!='getDrawing13':
funcNames.append(funcName)
return funcNames
def _evalFuncDrawing(name, D, l=None, g=None):
try:
d = eval(name + '()', g or globals(), l or locals())
except:
d = getFailedDrawing(name)
D.append((d, eval(name + '.__doc__'), name[3:]))
def getAllTestDrawings(doTTF=1):
D = []
for f in getAllFunctionDrawingNames(doTTF=doTTF):
_evalFuncDrawing(f,D)
return D
def writePDF(drawings):
"Create and save a PDF file containing some drawings."
pdfPath = os.path.splitext(sys.argv[0])[0] + '.pdf'
c = Canvas(pdfPath)
c.setFont(_FONTS[0], 32)
c.drawString(80, 750, 'ReportLab Graphics-Shapes Test')
# Print drawings in a loop, with their doc strings.
c.setFont(_FONTS[0], 12)
y = 740
i = 1
for (drawing, docstring, funcname) in drawings:
if y < 300: # Allows 5-6 lines of text.
c.showPage()
y = 740
# Draw a title.
y = y - 30
c.setFont(_FONTS[2],12)
c.drawString(80, y, '%s (#%d)' % (funcname, i))
c.setFont(_FONTS[0],12)
y = y - 14
textObj = c.beginText(80, y)
textObj.textLines(docstring)
c.drawText(textObj)
y = textObj.getY()
y = y - drawing.height
drawing.drawOn(c, 80, y)
i = i + 1
c.save()
print 'wrote %s ' % pdfPath
class ShapesTestCase(unittest.TestCase):
"Test generating all kinds of shapes."
def setUp(self):
"Prepare some things before the tests start."
self.funcNames = getAllFunctionDrawingNames()
self.drawings = []
def tearDown(self):
"Do what has to be done after the tests are over."
writePDF(self.drawings)
# This should always succeed. If each drawing would be
# wrapped in a dedicated test method like this one, it
# would be possible to have a count for wrong tests
# as well... Something like this is left for later...
def testAllDrawings(self):
"Make a list of drawings."
for f in self.funcNames:
if f[0:10] == 'getDrawing':
# Make an instance and get its doc string.
# If that fails, use a default error drawing.
_evalFuncDrawing(f,self.drawings)
def makeSuite():
"Make a test suite for unit testing."
suite = unittest.TestSuite()
suite.addTest(ShapesTestCase('testAllDrawings'))
return suite
if __name__ == "__main__":
unittest.TextTestRunner().run(makeSuite())

View File

@ -1,490 +0,0 @@
#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/graphics/widgetbase.py
__version__=''' $Id$ '''
import string
from reportlab.graphics import shapes
from reportlab import rl_config
from reportlab.lib import colors
from reportlab.lib.validators import *
from reportlab.lib.attrmap import *
class PropHolder:
'''Base for property holders'''
_attrMap = None
def verify(self):
"""If the _attrMap attribute is not None, this
checks all expected attributes are present; no
unwanted attributes are present; and (if a
checking function is found) checks each
attribute has a valid value. Either succeeds
or raises an informative exception.
"""
if self._attrMap is not None:
for key in self.__dict__.keys():
if key[0] <> '_':
msg = "Unexpected attribute %s found in %s" % (key, self)
assert self._attrMap.has_key(key), msg
for (attr, metavalue) in self._attrMap.items():
msg = "Missing attribute %s from %s" % (attr, self)
assert hasattr(self, attr), msg
value = getattr(self, attr)
args = (value, attr, self.__class__.__name__)
assert metavalue.validate(value), "Invalid value %s for attribute %s in class %s" % args
if rl_config.shapeChecking:
"""This adds the ability to check every attribute assignment
as it is made. It slows down shapes but is a big help when
developing. It does not get defined if rl_config.shapeChecking = 0.
"""
def __setattr__(self, name, value):
"""By default we verify. This could be off
in some parallel base classes."""
validateSetattr(self,name,value)
def getProperties(self,recur=1):
"""Returns a list of all properties which can be edited and
which are not marked as private. This may include 'child
widgets' or 'primitive shapes'. You are free to override
this and provide alternative implementations; the default
one simply returns everything without a leading underscore.
"""
from reportlab.lib.validators import isValidChild
# TODO when we need it, but not before -
# expose sequence contents?
props = {}
for name in self.__dict__.keys():
if name[0:1] <> '_':
component = getattr(self, name)
if recur and isValidChild(component):
# child object, get its properties too
childProps = component.getProperties(recur=recur)
for (childKey, childValue) in childProps.items():
#key might be something indexed like '[2].fillColor'
#or simple like 'fillColor'; in the former case we
#don't need a '.' between me and my child.
if childKey[0] == '[':
props['%s%s' % (name, childKey)] = childValue
else:
props['%s.%s' % (name, childKey)] = childValue
else:
props[name] = component
return props
def setProperties(self, propDict):
"""Permits bulk setting of properties. These may include
child objects e.g. "chart.legend.width = 200".
All assignments will be validated by the object as if they
were set individually in python code.
All properties of a top-level object are guaranteed to be
set before any of the children, which may be helpful to
widget designers.
"""
childPropDicts = {}
for (name, value) in propDict.items():
parts = string.split(name, '.', 1)
if len(parts) == 1:
#simple attribute, set it now
setattr(self, name, value)
else:
(childName, remains) = parts
try:
childPropDicts[childName][remains] = value
except KeyError:
childPropDicts[childName] = {remains: value}
# now assign to children
for (childName, childPropDict) in childPropDicts.items():
child = getattr(self, childName)
child.setProperties(childPropDict)
def dumpProperties(self, prefix=""):
"""Convenience. Lists them on standard output. You
may provide a prefix - mostly helps to generate code
samples for documentation.
"""
propList = self.getProperties().items()
propList.sort()
if prefix:
prefix = prefix + '.'
for (name, value) in propList:
print '%s%s = %s' % (prefix, name, value)
class Widget(PropHolder, shapes.UserNode):
"""Base for all user-defined widgets. Keep as simple as possible. Does
not inherit from Shape so that we can rewrite shapes without breaking
widgets and vice versa."""
def _setKeywords(self,**kw):
for k,v in kw.items():
if not self.__dict__.has_key(k):
setattr(self,k,v)
def draw(self):
msg = "draw() must be implemented for each Widget!"
raise shapes.NotImplementedError, msg
def demo(self):
msg = "demo() must be implemented for each Widget!"
raise shapes.NotImplementedError, msg
def provideNode(self):
return self.draw()
def getBounds(self):
"Return outer boundary as x1,y1,x2,y2. Can be overridden for efficiency"
return self.draw().getBounds()
_ItemWrapper={}
class TypedPropertyCollection(PropHolder):
"""A container with properties for objects of the same kind.
This makes it easy to create lists of objects. You initialize
it with a class of what it is to contain, and that is all you
can add to it. You can assign properties to the collection
as a whole, or to a numeric index within it; if so it creates
a new child object to hold that data.
So:
wedges = TypedPropertyCollection(WedgeProperties)
wedges.strokeWidth = 2 # applies to all
wedges.strokeColor = colors.red # applies to all
wedges[3].strokeColor = colors.blue # only to one
The last line should be taken as a prescription of how to
create wedge no. 3 if one is needed; no error is raised if
there are only two data points.
"""
def __init__(self, exampleClass):
#give it same validation rules as what it holds
self.__dict__['_value'] = exampleClass()
self.__dict__['_children'] = {}
def __getitem__(self, index):
try:
return self._children[index]
except KeyError:
Klass = self._value.__class__
if _ItemWrapper.has_key(Klass):
WKlass = _ItemWrapper[Klass]
else:
class WKlass(Klass):
def __getattr__(self,name):
try:
return self.__class__.__bases__[0].__getattr__(self,name)
except:
if self._index and self._parent._children.has_key(self._index):
if self._parent._children[self._index].__dict__.has_key(name):
return getattr(self._parent._children[self._index],name)
return getattr(self._parent,name)
_ItemWrapper[Klass] = WKlass
child = WKlass()
child._parent = self
if type(index) in (type(()),type([])):
index = tuple(index)
if len(index)>1:
child._index = tuple(index[:-1])
else:
child._index = None
else:
child._index = None
for i in filter(lambda x,K=child.__dict__.keys(): x in K,child._attrMap.keys()):
del child.__dict__[i]
self._children[index] = child
return child
def has_key(self,key):
if type(key) in (type(()),type([])): key = tuple(key)
return self._children.has_key(key)
def __setitem__(self, key, value):
msg = "This collection can only hold objects of type %s" % self._value.__class__.__name__
assert isinstance(value, self._value.__class__), msg
def __len__(self):
return len(self._children.keys())
def getProperties(self,recur=1):
# return any children which are defined and whatever
# differs from the parent
props = {}
for (key, value) in self._value.getProperties(recur=recur).items():
props['%s' % key] = value
for idx in self._children.keys():
childProps = self._children[idx].getProperties(recur=recur)
for (key, value) in childProps.items():
if not hasattr(self,key) or getattr(self, key)<>value:
newKey = '[%s].%s' % (idx, key)
props[newKey] = value
return props
def setVector(self,**kw):
for name, value in kw.items():
for i in xrange(len(value)):
setattr(self[i],name,value[i])
def __getattr__(self,name):
return getattr(self._value,name)
def __setattr__(self,name,value):
return setattr(self._value,name,value)
## No longer needed!
class StyleProperties(PropHolder):
"""A container class for attributes used in charts and legends.
Attributes contained can be those for any graphical element
(shape?) in the ReportLab graphics package. The idea for this
container class is to be useful in combination with legends
and/or the individual appearance of data series in charts.
A legend could be as simple as a wrapper around a list of style
properties, where the 'desc' attribute contains a descriptive
string and the rest could be used by the legend e.g. to draw
something like a color swatch. The graphical presentation of
the legend would be its own business, though.
A chart could be inspecting a legend or, more directly, a list
of style properties to pick individual attributes that it knows
about in order to render a particular row of the data. A bar
chart e.g. could simply use 'strokeColor' and 'fillColor' for
drawing the bars while a line chart could also use additional
ones like strokeWidth.
"""
_attrMap = AttrMap(
strokeWidth = AttrMapValue(isNumber),
strokeLineCap = AttrMapValue(isNumber),
strokeLineJoin = AttrMapValue(isNumber),
strokeMiterLimit = AttrMapValue(None),
strokeDashArray = AttrMapValue(isListOfNumbersOrNone),
strokeOpacity = AttrMapValue(isNumber),
strokeColor = AttrMapValue(isColorOrNone),
fillColor = AttrMapValue(isColorOrNone),
desc = AttrMapValue(isString),
)
def __init__(self, **kwargs):
"Initialize with attributes if any."
for k, v in kwargs.items():
setattr(self, k, v)
def __setattr__(self, name, value):
"Verify attribute name and value, before setting it."
validateSetattr(self,name,value)
class TwoCircles(Widget):
def __init__(self):
self.leftCircle = shapes.Circle(100,100,20, fillColor=colors.red)
self.rightCircle = shapes.Circle(300,100,20, fillColor=colors.red)
def draw(self):
return shapes.Group(self.leftCircle, self.rightCircle)
class Face(Widget):
"""This draws a face with two eyes.
It exposes a couple of properties
to configure itself and hides all other details.
"""
_attrMap = AttrMap(
x = AttrMapValue(isNumber),
y = AttrMapValue(isNumber),
size = AttrMapValue(isNumber),
skinColor = AttrMapValue(isColorOrNone),
eyeColor = AttrMapValue(isColorOrNone),
mood = AttrMapValue(OneOf('happy','sad','ok')),
)
def __init__(self):
self.x = 10
self.y = 10
self.size = 80
self.skinColor = None
self.eyeColor = colors.blue
self.mood = 'happy'
def demo(self):
pass
def draw(self):
s = self.size # abbreviate as we will use this a lot
g = shapes.Group()
g.transform = [1,0,0,1,self.x, self.y]
# background
g.add(shapes.Circle(s * 0.5, s * 0.5, s * 0.5, fillColor=self.skinColor))
# left eye
g.add(shapes.Circle(s * 0.35, s * 0.65, s * 0.1, fillColor=colors.white))
g.add(shapes.Circle(s * 0.35, s * 0.65, s * 0.05, fillColor=self.eyeColor))
# right eye
g.add(shapes.Circle(s * 0.65, s * 0.65, s * 0.1, fillColor=colors.white))
g.add(shapes.Circle(s * 0.65, s * 0.65, s * 0.05, fillColor=self.eyeColor))
# nose
g.add(shapes.Polygon(
points=[s * 0.5, s * 0.6, s * 0.4, s * 0.3, s * 0.6, s * 0.3],
fillColor=None))
# mouth
if self.mood == 'happy':
offset = -0.05
elif self.mood == 'sad':
offset = +0.05
else:
offset = 0
g.add(shapes.Polygon(
points = [
s * 0.3, s * 0.2, #left of mouth
s * 0.7, s * 0.2, #right of mouth
s * 0.6, s * (0.2 + offset), # the bit going up or down
s * 0.4, s * (0.2 + offset) # the bit going up or down
],
fillColor = colors.pink,
strokeColor = colors.red,
strokeWidth = s * 0.03
))
return g
class TwoFaces(Widget):
def __init__(self):
self.faceOne = Face()
self.faceOne.mood = "happy"
self.faceTwo = Face()
self.faceTwo.x = 100
self.faceTwo.mood = "sad"
def draw(self):
"""Just return a group"""
return shapes.Group(self.faceOne, self.faceTwo)
def demo(self):
"""The default case already looks good enough,
no implementation needed here"""
pass
class Sizer(Widget):
"Container to show size of all enclosed objects"
_attrMap = AttrMap(BASE=shapes.SolidShape,
contents = AttrMapValue(isListOfShapes,desc="Contained drawable elements"),
)
def __init__(self, *elements):
self.contents = []
self.fillColor = colors.cyan
self.strokeColor = colors.magenta
for elem in elements:
self.add(elem)
def _addNamedNode(self,name,node):
'if name is not None add an attribute pointing to node and add to the attrMap'
if name:
if name not in self._attrMap.keys():
self._attrMap[name] = AttrMapValue(isValidChild)
setattr(self, name, node)
def add(self, node, name=None):
"""Appends non-None child node to the 'contents' attribute. In addition,
if a name is provided, it is subsequently accessible by name
"""
# propagates properties down
if node is not None:
assert isValidChild(node), "Can only add Shape or UserNode objects to a Group"
self.contents.append(node)
self._addNamedNode(name,node)
def getBounds(self):
# get bounds of each object
if self.contents:
b = []
for elem in self.contents:
b.append(elem.getBounds())
return shapes.getRectsBounds(b)
else:
return (0,0,0,0)
def draw(self):
g = shapes.Group()
(x1, y1, x2, y2) = self.getBounds()
r = shapes.Rect(
x = x1,
y = y1,
width = x2-x1,
height = y2-y1,
fillColor = self.fillColor,
strokeColor = self.strokeColor
)
g.add(r)
for elem in self.contents:
g.add(elem)
return g
def test():
from reportlab.graphics.charts.piecharts import WedgeProperties
wedges = TypedPropertyCollection(WedgeProperties)
wedges.fillColor = colors.red
wedges.setVector(fillColor=(colors.blue,colors.green,colors.white))
print len(_ItemWrapper)
d = shapes.Drawing(400, 200)
tc = TwoCircles()
d.add(tc)
import renderPDF
renderPDF.drawToFile(d, 'sample_widget.pdf', 'A Sample Widget')
print 'saved sample_widget.pdf'
d = shapes.Drawing(400, 200)
f = Face()
f.skinColor = colors.yellow
f.mood = "sad"
d.add(f, name='theFace')
print 'drawing 1 properties:'
d.dumpProperties()
renderPDF.drawToFile(d, 'face.pdf', 'A Sample Widget')
print 'saved face.pdf'
d2 = d.expandUserNodes()
renderPDF.drawToFile(d2, 'face_copy.pdf', 'An expanded drawing')
print 'saved face_copy.pdf'
print 'drawing 2 properties:'
d2.dumpProperties()
if __name__=='__main__':
test()

View File

@ -1,4 +0,0 @@
#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/graphics/widgets/__init__.py
__version__=''' $Id$ '''

View File

@ -1,303 +0,0 @@
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/widgets/eventcal.py
# Event Calendar widget
# author: Andy Robinson
"""This file is a
"""
__version__=''' $Id$ '''
from reportlab.lib import colors
from reportlab.lib.validators import *
from reportlab.lib.attrmap import *
from reportlab.graphics.shapes import Line, Rect, Polygon, Drawing, Group, String, Circle, Wedge
from reportlab.graphics.charts.textlabels import Label
from reportlab.graphics.widgetbase import Widget
from reportlab.graphics import renderPDF
class EventCalendar(Widget):
def __init__(self):
self.x = 0
self.y = 0
self.width = 300
self.height = 150
self.timeColWidth = None # if declared, use it; otherwise auto-size.
self.trackRowHeight = 20
self.data = [] # list of Event objects
self.trackNames = None
self.startTime = None #displays ALL data on day if not set
self.endTime = None # displays ALL data on day if not set
self.day = 0
# we will keep any internal geometry variables
# here. These are computed by computeSize(),
# which is the first thing done when drawing.
self._talksVisible = [] # subset of data which will get plotted, cache
self._startTime = None
self._endTime = None
self._trackCount = 0
self._colWidths = []
self._colLeftEdges = [] # left edge of each column
def computeSize(self):
"Called at start of draw. Sets various column widths"
self._talksVisible = self.getRelevantTalks(self.data)
self._trackCount = len(self.getAllTracks())
self.computeStartAndEndTimes()
self._colLeftEdges = [self.x]
if self.timeColWidth is None:
w = self.width / (1 + self._trackCount)
self._colWidths = [w] * (1+ self._trackCount)
for i in range(self._trackCount):
self._colLeftEdges.append(self._colLeftEdges[-1] + w)
else:
self._colWidths = [self.timeColWidth]
w = (self.width - self.timeColWidth) / self._trackCount
for i in range(self._trackCount):
self._colWidths.append(w)
self._colLeftEdges.append(self._colLeftEdges[-1] + w)
def computeStartAndEndTimes(self):
"Work out first and last times to display"
if self.startTime:
self._startTime = self.startTime
else:
for (title, speaker, trackId, day, start, duration) in self._talksVisible:
if self._startTime is None: #first one
self._startTime = start
else:
if start < self._startTime:
self._startTime = start
if self.endTime:
self._endTime = self.endTime
else:
for (title, speaker, trackId, day, start, duration) in self._talksVisible:
if self._endTime is None: #first one
self._endTime = start + duration
else:
if start + duration > self._endTime:
self._endTime = start + duration
def getAllTracks(self):
tracks = []
for (title, speaker, trackId, day, hours, duration) in self.data:
if trackId is not None:
if trackId not in tracks:
tracks.append(trackId)
tracks.sort()
return tracks
def getRelevantTalks(self, talkList):
"Scans for tracks actually used"
used = []
for talk in talkList:
(title, speaker, trackId, day, hours, duration) = talk
assert trackId <> 0, "trackId must be None or 1,2,3... zero not allowed!"
if day == self.day:
if (((self.startTime is None) or ((hours + duration) >= self.startTime))
and ((self.endTime is None) or (hours <= self.endTime))):
used.append(talk)
return used
def scaleTime(self, theTime):
"Return y-value corresponding to times given"
axisHeight = self.height - self.trackRowHeight
# compute fraction between 0 and 1, 0 is at start of period
proportionUp = ((theTime - self._startTime) / (self._endTime - self._startTime))
y = self.y + axisHeight - (axisHeight * proportionUp)
return y
def getTalkRect(self, startTime, duration, trackId, text):
"Return shapes for a specific talk"
g = Group()
y_bottom = self.scaleTime(startTime + duration)
y_top = self.scaleTime(startTime)
y_height = y_top - y_bottom
if trackId is None:
#spans all columns
x = self._colLeftEdges[1]
width = self.width - self._colWidths[0]
else:
#trackId is 1-based and these arrays have the margin info in column
#zero, so no need to add 1
x = self._colLeftEdges[trackId]
width = self._colWidths[trackId]
lab = Label()
lab.setText(text)
lab.setOrigin(x + 0.5*width, y_bottom+0.5*y_height)
lab.boxAnchor = 'c'
lab.width = width
lab.height = y_height
lab.fontSize = 6
r = Rect(x, y_bottom, width, y_height, fillColor=colors.cyan)
g.add(r)
g.add(lab)
#now for a label
# would expect to color-code and add text
return g
def draw(self):
self.computeSize()
g = Group()
# time column
g.add(Rect(self.x, self.y, self._colWidths[0], self.height - self.trackRowHeight, fillColor=colors.cornsilk))
# track headers
x = self.x + self._colWidths[0]
y = self.y + self.height - self.trackRowHeight
for trk in range(self._trackCount):
wid = self._colWidths[trk+1]
r = Rect(x, y, wid, self.trackRowHeight, fillColor=colors.yellow)
s = String(x + 0.5*wid, y, 'Track %d' % trk, align='middle')
g.add(r)
g.add(s)
x = x + wid
for talk in self._talksVisible:
(title, speaker, trackId, day, start, duration) = talk
r = self.getTalkRect(start, duration, trackId, title + '\n' + speaker)
g.add(r)
return g
def test():
"Make a conference event for day 1 of UP Python 2003"
d = Drawing(400,200)
cal = EventCalendar()
cal.x = 50
cal.y = 25
cal.data = [
# these might be better as objects instead of tuples, since I
# predict a large number of "optionsl" variables to affect
# formatting in future.
#title, speaker, track id, day, start time (hrs), duration (hrs)
# track ID is 1-based not zero-based!
('Keynote: Why design another programming language?', 'Guido van Rossum', None, 1, 9.0, 1.0),
('Siena Web Service Architecture', 'Marc-Andre Lemburg', 1, 1, 10.5, 1.5),
('Extreme Programming in Python', 'Chris Withers', 2, 1, 10.5, 1.5),
('Pattern Experiences in C++', 'Mark Radford', 3, 1, 10.5, 1.5),
('What is the Type of std::toupper()', 'Gabriel Dos Reis', 4, 1, 10.5, 1.5),
('Linguistic Variables: Clear Thinking with Fuzzy Logic ', 'Walter Banks', 5, 1, 10.5, 1.5),
('lunch, short presentations, vendor presentations', '', None, 1, 12.0, 2.0),
("CORBA? Isn't that obsolete", 'Duncan Grisby', 1, 1, 14.0, 1.5),
("Python Design Patterns", 'Duncan Booth', 2, 1, 14.0, 1.5),
("Inside Security Checks and Safe Exceptions", 'Brandon Bray', 3, 1, 14.0, 1.5),
("Studying at a Distance", 'Panel Discussion, Panel to include Alan Lenton & Francis Glassborow', 4, 1, 14.0, 1.5),
("Coding Standards - Given the ANSI C Standard why do I still need a coding Standard", 'Randy Marques', 5, 1, 14.0, 1.5),
("RESTful Python", 'Hamish Lawson', 1, 1, 16.0, 1.5),
("Parsing made easier - a radical old idea", 'Andrew Koenig', 2, 1, 16.0, 1.5),
("C++ & Multimethods", 'Julian Smith', 3, 1, 16.0, 1.5),
("C++ Threading", 'Kevlin Henney', 4, 1, 16.0, 1.5),
("The Organisation Strikes Back", 'Alan Griffiths & Sarah Lees', 5, 1, 16.0, 1.5),
('Birds of a Feather meeting', '', None, 1, 17.5, 2.0),
('Keynote: In the Spirit of C', 'Greg Colvin', None, 2, 9.0, 1.0),
('The Infinite Filing Cabinet - object storage in Python', 'Jacob Hallen', 1, 2, 10.5, 1.5),
('Introduction to Python and Jython for C++ and Java Programmers', 'Alex Martelli', 2, 2, 10.5, 1.5),
('Template metaprogramming in Haskell', 'Simon Peyton Jones', 3, 2, 10.5, 1.5),
('Plenty People Programming: C++ Programming in a Group, Workshop with a difference', 'Nico Josuttis', 4, 2, 10.5, 1.5),
('Design and Implementation of the Boost Graph Library', 'Jeremy Siek', 5, 2, 10.5, 1.5),
('lunch, short presentations, vendor presentations', '', None, 2, 12.0, 2.0),
("Building GUI Applications with PythonCard and PyCrust", 'Andy Todd', 1, 2, 14.0, 1.5),
("Integrating Python, C and C++", 'Duncan Booth', 2, 2, 14.0, 1.5),
("Secrets and Pitfalls of Templates", 'Nicolai Josuttis & David Vandevoorde', 3, 2, 14.0, 1.5),
("Being a Mentor", 'Panel Discussion, Panel to include Alan Lenton & Francis Glassborow', 4, 2, 14.0, 1.5),
("The Embedded C Extensions to C", 'Willem Wakker', 5, 2, 14.0, 1.5),
("Lightning Talks", 'Paul Brian', 1, 2, 16.0, 1.5),
("Scripting Java Applications with Jython", 'Anthony Eden', 2, 2, 16.0, 1.5),
("Metaprogramming and the Boost Metaprogramming Library", 'David Abrahams', 3, 2, 16.0, 1.5),
("A Common Vendor ABI for C++ -- GCC's why, what and not", 'Nathan Sidwell & Gabriel Dos Reis', 4, 2, 16.0, 1.5),
("The Timing and Cost of Choices", 'Hubert Matthews', 5, 2, 16.0, 1.5),
('Birds of a Feather meeting', '', None, 2, 17.5, 2.0),
('Keynote: The Cost of C &amp; C++ Compatibility', 'Andy Koenig', None, 3, 9.0, 1.0),
('Prying Eyes: Generic Observer Implementations in C++', 'Andrei Alexandrescu', 1, 2, 10.5, 1.5),
('The Roadmap to Generative Programming With C++', 'Ulrich Eisenecker', 2, 2, 10.5, 1.5),
('Design Patterns in C++ and C# for the Common Language Runtime', 'Brandon Bray', 3, 2, 10.5, 1.5),
('Extreme Hour (XH): (workshop) - Jutta Eckstein and Nico Josuttis', 'Jutta Ecstein', 4, 2, 10.5, 1.5),
('The Lambda Library : Unnamed Functions for C++', 'Jaako Jarvi', 5, 2, 10.5, 1.5),
('lunch, short presentations, vendor presentations', '', None, 3, 12.0, 2.0),
('Reflective Metaprogramming', 'Daveed Vandevoorde', 1, 3, 14.0, 1.5),
('Advanced Template Issues and Solutions (double session)', 'Herb Sutter',2, 3, 14.0, 3),
('Concurrent Programming in Java (double session)', 'Angelika Langer', 3, 3, 14.0, 3),
('What can MISRA-C (2nd Edition) do for us?', 'Chris Hills', 4, 3, 14.0, 1.5),
('C++ Metaprogramming Concepts and Results', 'Walter E Brown', 5, 3, 14.0, 1.5),
('Binding C++ to Python with the Boost Python Library', 'David Abrahams', 1, 3, 16.0, 1.5),
('Using Aspect Oriented Programming for Enterprise Application Integration', 'Arno Schmidmeier', 4, 3, 16.0, 1.5),
('Defective C++', 'Marc Paterno', 5, 3, 16.0, 1.5),
("Speakers' Banquet & Birds of a Feather meeting", '', None, 3, 17.5, 2.0),
('Keynote: The Internet, Software and Computers - A Report Card', 'Alan Lenton', None, 4, 9.0, 1.0),
('Multi-Platform Software Development; Lessons from the Boost libraries', 'Beman Dawes', 1, 5, 10.5, 1.5),
('The Stability of the C++ ABI', 'Steve Clamage', 2, 5, 10.5, 1.5),
('Generic Build Support - A Pragmatic Approach to the Software Build Process', 'Randy Marques', 3, 5, 10.5, 1.5),
('How to Handle Project Managers: a survival guide', 'Barb Byro', 4, 5, 10.5, 1.5),
('lunch, ACCU AGM', '', None, 5, 12.0, 2.0),
('Sauce: An OO recursive descent parser; its design and implementation.', 'Jon Jagger', 1, 5, 14.0, 1.5),
('GNIRTS ESAC REWOL - Bringing the UNIX filters to the C++ iostream library.', 'JC van Winkel', 2, 5, 14.0, 1.5),
('Pattern Writing: Live and Direct', 'Frank Buschmann & Kevlin Henney', 3, 5, 14.0, 3.0),
('The Future of Programming Languages - A Goldfish Bowl', 'Francis Glassborow and friends', 3, 5, 14.0, 1.5),
('Honey, I Shrunk the Threads: Compile-time checked multithreaded transactions in C++', 'Andrei Alexandrescu', 1, 5, 16.0, 1.5),
('Fun and Functionality with Functors', 'Lois Goldthwaite', 2, 5, 16.0, 1.5),
('Agile Enough?', 'Alan Griffiths', 4, 5, 16.0, 1.5),
("Conference Closure: A brief plenary session", '', None, 5, 17.5, 0.5),
]
#return cal
cal.day = 1
d.add(cal)
for format in ['pdf']:#,'gif','png']:
out = d.asString(format)
open('eventcal.%s' % format, 'wb').write(out)
print 'saved eventcal.%s' % format
if __name__=='__main__':
test()

View File

@ -1,879 +0,0 @@
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/widgets/flags.py
# Flag Widgets - a collection of flags as widgets
# author: John Precedo (johnp@reportlab.com)
"""This file is a collection of flag graphics as widgets.
All flags are represented at the ratio of 1:2, even where the official ratio for the flag is something else
(such as 3:5 for the German national flag). The only exceptions are for where this would look _very_ wrong,
such as the Danish flag whose (ratio is 28:37), or the Swiss flag (which is square).
Unless otherwise stated, these flags are all the 'national flags' of the countries, rather than their
state flags, naval flags, ensigns or any other variants. (National flags are the flag flown by civilians
of a country and the ones usually used to represent a country abroad. State flags are the variants used by
the government and by diplomatic missions overseas).
To check on how close these are to the 'official' representations of flags, check the World Flag Database at
http://www.flags.ndirect.co.uk/
The flags this file contains are:
EU Members:
United Kingdom, Austria, Belgium, Denmark, Finland, France, Germany, Greece, Ireland, Italy, Luxembourg,
Holland (The Netherlands), Spain, Sweden
Others:
USA, Czech Republic, European Union, Switzerland, Turkey, Brazil
(Brazilian flag contributed by Publio da Costa Melo [publio@planetarium.com.br]).
"""
__version__=''' $Id$ '''
from reportlab.lib import colors
from reportlab.lib.validators import *
from reportlab.lib.attrmap import *
from reportlab.graphics.shapes import Line, Rect, Polygon, Drawing, Group, String, Circle, Wedge
from reportlab.graphics.widgetbase import Widget
from reportlab.graphics import renderPDF
from signsandsymbols import _Symbol
import copy
from math import sin, cos, pi
validFlag=OneOf(None,
'UK',
'USA',
'Afghanistan',
'Austria',
'Belgium',
'China',
'Cuba',
'Denmark',
'Finland',
'France',
'Germany',
'Greece',
'Ireland',
'Italy',
'Japan',
'Luxembourg',
'Holland',
'Palestine',
'Portugal',
'Russia',
'Spain',
'Sweden',
'Norway',
'CzechRepublic',
'Turkey',
'Switzerland',
'EU',
'Brazil'
)
_size = 100.
class Star(_Symbol):
"""This draws a 5-pointed star.
possible attributes:
'x', 'y', 'size', 'fillColor', 'strokeColor'
"""
_attrMap = AttrMap(BASE=_Symbol,
angle = AttrMapValue(isNumber, desc='angle in degrees'),
)
_size = 100.
def __init__(self):
_Symbol.__init__(self)
self.size = 100
self.fillColor = colors.yellow
self.strokeColor = None
self.angle = 0
def demo(self):
D = Drawing(200, 100)
et = Star()
et.x=50
et.y=0
D.add(et)
labelFontSize = 10
D.add(String(et.x+(et.size/2.0),(et.y-(1.2*labelFontSize)),
et.__class__.__name__, fillColor=colors.black, textAnchor='middle',
fontSize=labelFontSize))
return D
def draw(self):
s = float(self.size) #abbreviate as we will use this a lot
g = Group()
# new algorithm from markers.StarFive
R = float(self.size)/2
r = R*sin(18*(pi/180.0))/cos(36*(pi/180.0))
P = []
angle = 90
for i in xrange(5):
for radius in R, r:
theta = angle*(pi/180.0)
P.append(radius*cos(theta))
P.append(radius*sin(theta))
angle = angle + 36
# star specific bits
star = Polygon(P,
fillColor = self.fillColor,
strokeColor = self.strokeColor,
strokeWidth=s/50)
g.rotate(self.angle)
g.shift(self.x+self.dx,self.y+self.dy)
g.add(star)
return g
class Flag(_Symbol):
"""This is a generic flag class that all the flags in this file use as a basis.
This class basically provides edges and a tidy-up routine to hide any bits of
line that overlap the 'outside' of the flag
possible attributes:
'x', 'y', 'size', 'fillColor'
"""
_attrMap = AttrMap(BASE=_Symbol,
fillColor = AttrMapValue(isColor, desc='Background color'),
border = AttrMapValue(isBoolean, 'Whether a background is drawn'),
kind = AttrMapValue(validFlag, desc='Which flag'),
)
_cache = {}
def __init__(self,**kw):
_Symbol.__init__(self)
self.kind = None
self.size = 100
self.fillColor = colors.white
self.border=1
self.setProperties(kw)
def availableFlagNames(self):
'''return a list of the things we can display'''
return filter(lambda x: x is not None, self._attrMap['kind'].validate._enum)
def _Flag_None(self):
s = _size # abbreviate as we will use this a lot
g = Group()
g.add(Rect(0, 0, s*2, s, fillColor = colors.purple, strokeColor = colors.black, strokeWidth=0))
return g
def _borderDraw(self,f):
s = self.size # abbreviate as we will use this a lot
g = Group()
g.add(f)
x, y, sW = self.x+self.dx, self.y+self.dy, self.strokeWidth/2.
g.insert(0,Rect(-sW, -sW, width=getattr(self,'_width',2*s)+3*sW, height=getattr(self,'_height',s)+2*sW,
fillColor = None, strokeColor = self.strokeColor, strokeWidth=sW*2))
g.shift(x,y)
g.scale(s/_size, s/_size)
return g
def draw(self):
kind = self.kind or 'None'
f = self._cache.get(kind)
if not f:
f = getattr(self,'_Flag_'+kind)()
self._cache[kind] = f._explode()
return self._borderDraw(f)
def clone(self):
return copy.copy(self)
def demo(self):
D = Drawing(200, 100)
name = self.availableFlagNames()
import time
name = name[int(time.time()) % len(name)]
fx = Flag()
fx.kind = name
fx.x = 0
fx.y = 0
D.add(fx)
labelFontSize = 10
D.add(String(fx.x+(fx.size/2),(fx.y-(1.2*labelFontSize)),
name, fillColor=colors.black, textAnchor='middle',
fontSize=labelFontSize))
labelFontSize = int(fx.size/4)
D.add(String(fx.x+(fx.size),(fx.y+((fx.size/2))),
"SAMPLE", fillColor=colors.gold, textAnchor='middle',
fontSize=labelFontSize, fontName="Helvetica-Bold"))
return D
def _Flag_UK(self):
s = _size
g = Group()
w = s*2
g.add(Rect(0, 0, w, s, fillColor = colors.navy, strokeColor = colors.black, strokeWidth=0))
g.add(Polygon([0,0, s*.225,0, w,s*(1-.1125), w,s, w-s*.225,s, 0, s*.1125], fillColor = colors.mintcream, strokeColor=None, strokeWidth=0))
g.add(Polygon([0,s*(1-.1125), 0, s, s*.225,s, w, s*.1125, w,0, w-s*.225,0], fillColor = colors.mintcream, strokeColor=None, strokeWidth=0))
g.add(Polygon([0, s-(s/15), (s-((s/10)*4)), (s*0.65), (s-(s/10)*3), (s*0.65), 0, s], fillColor = colors.red, strokeColor = None, strokeWidth=0))
g.add(Polygon([0, 0, (s-((s/10)*3)), (s*0.35), (s-((s/10)*2)), (s*0.35), (s/10), 0], fillColor = colors.red, strokeColor = None, strokeWidth=0))
g.add(Polygon([w, s, (s+((s/10)*3)), (s*0.65), (s+((s/10)*2)), (s*0.65), w-(s/10), s], fillColor = colors.red, strokeColor = None, strokeWidth=0))
g.add(Polygon([w, (s/15), (s+((s/10)*4)), (s*0.35), (s+((s/10)*3)), (s*0.35), w, 0], fillColor = colors.red, strokeColor = None, strokeWidth=0))
g.add(Rect(((s*0.42)*2), 0, width=(0.16*s)*2, height=s, fillColor = colors.mintcream, strokeColor = None, strokeWidth=0))
g.add(Rect(0, (s*0.35), width=w, height=s*0.3, fillColor = colors.mintcream, strokeColor = None, strokeWidth=0))
g.add(Rect(((s*0.45)*2), 0, width=(0.1*s)*2, height=s, fillColor = colors.red, strokeColor = None, strokeWidth=0))
g.add(Rect(0, (s*0.4), width=w, height=s*0.2, fillColor = colors.red, strokeColor = None, strokeWidth=0))
return g
def _Flag_USA(self):
s = _size # abbreviate as we will use this a lot
g = Group()
box = Rect(0, 0, s*2, s, fillColor = colors.mintcream, strokeColor = colors.black, strokeWidth=0)
g.add(box)
for stripecounter in range (13,0, -1):
stripeheight = s/13.0
if not (stripecounter%2 == 0):
stripecolor = colors.red
else:
stripecolor = colors.mintcream
redorwhiteline = Rect(0, (s-(stripeheight*stripecounter)), width=s*2, height=stripeheight,
fillColor = stripecolor, strokeColor = None, strokeWidth=20)
g.add(redorwhiteline)
bluebox = Rect(0, (s-(stripeheight*7)), width=0.8*s, height=stripeheight*7,
fillColor = colors.darkblue, strokeColor = None, strokeWidth=0)
g.add(bluebox)
lss = s*0.045
lss2 = lss/2
s9 = s/9
s7 = s/7
for starxcounter in range(5):
for starycounter in range(4):
ls = Star()
ls.size = lss
ls.x = 0-s/22+lss/2+s7+starxcounter*s7
ls.fillColor = colors.mintcream
ls.y = s-(starycounter+1)*s9+lss2
g.add(ls)
for starxcounter in range(6):
for starycounter in range(5):
ls = Star()
ls.size = lss
ls.x = 0-(s/22)+lss/2+s/14+starxcounter*s7
ls.fillColor = colors.mintcream
ls.y = s-(starycounter+1)*s9+(s/18)+lss2
g.add(ls)
return g
def _Flag_Afghanistan(self):
s = _size
g = Group()
box = Rect(0, 0, s*2, s,
fillColor = colors.mintcream, strokeColor = colors.black, strokeWidth=0)
g.add(box)
greenbox = Rect(0, ((s/3.0)*2.0), width=s*2.0, height=s/3.0,
fillColor = colors.limegreen, strokeColor = None, strokeWidth=0)
g.add(greenbox)
blackbox = Rect(0, 0, width=s*2.0, height=s/3.0,
fillColor = colors.black, strokeColor = None, strokeWidth=0)
g.add(blackbox)
return g
def _Flag_Austria(self):
s = _size # abbreviate as we will use this a lot
g = Group()
box = Rect(0, 0, s*2, s, fillColor = colors.mintcream,
strokeColor = colors.black, strokeWidth=0)
g.add(box)
redbox1 = Rect(0, 0, width=s*2.0, height=s/3.0,
fillColor = colors.red, strokeColor = None, strokeWidth=0)
g.add(redbox1)
redbox2 = Rect(0, ((s/3.0)*2.0), width=s*2.0, height=s/3.0,
fillColor = colors.red, strokeColor = None, strokeWidth=0)
g.add(redbox2)
return g
def _Flag_Belgium(self):
s = _size
g = Group()
box = Rect(0, 0, s*2, s,
fillColor = colors.black, strokeColor = colors.black, strokeWidth=0)
g.add(box)
box1 = Rect(0, 0, width=(s/3.0)*2.0, height=s,
fillColor = colors.black, strokeColor = None, strokeWidth=0)
g.add(box1)
box2 = Rect(((s/3.0)*2.0), 0, width=(s/3.0)*2.0, height=s,
fillColor = colors.gold, strokeColor = None, strokeWidth=0)
g.add(box2)
box3 = Rect(((s/3.0)*4.0), 0, width=(s/3.0)*2.0, height=s,
fillColor = colors.red, strokeColor = None, strokeWidth=0)
g.add(box3)
return g
def _Flag_China(self):
s = _size
g = Group()
self._width = w = s*1.5
g.add(Rect(0, 0, w, s, fillColor=colors.red, strokeColor=None, strokeWidth=0))
def addStar(x,y,size,angle,g=g,w=s/20,x0=0,y0=s/2):
s = Star()
s.fillColor=colors.yellow
s.angle = angle
s.size = size*w*2
s.x = x*w+x0
s.y = y*w+y0
g.add(s)
addStar(5,5,3, 0)
addStar(10,1,1,36.86989765)
addStar(12,3,1,8.213210702)
addStar(12,6,1,16.60154960)
addStar(10,8,1,53.13010235)
return g
def _Flag_Cuba(self):
s = _size
g = Group()
for i in range(5):
stripe = Rect(0, i*s/5, width=s*2, height=s/5,
fillColor = [colors.darkblue, colors.mintcream][i%2],
strokeColor = None,
strokeWidth=0)
g.add(stripe)
redwedge = Polygon(points = [ 0, 0, 4*s/5, (s/2), 0, s],
fillColor = colors.red, strokeColor = None, strokeWidth=0)
g.add(redwedge)
star = Star()
star.x = 2.5*s/10
star.y = s/2
star.size = 3*s/10
star.fillColor = colors.white
g.add(star)
box = Rect(0, 0, s*2, s,
fillColor = None,
strokeColor = colors.black,
strokeWidth=0)
g.add(box)
return g
def _Flag_Denmark(self):
s = _size
g = Group()
self._width = w = s*1.4
box = Rect(0, 0, w, s,
fillColor = colors.red, strokeColor = colors.black, strokeWidth=0)
g.add(box)
whitebox1 = Rect(((s/5)*2), 0, width=s/6, height=s,
fillColor = colors.mintcream, strokeColor = None, strokeWidth=0)
g.add(whitebox1)
whitebox2 = Rect(0, ((s/2)-(s/12)), width=w, height=s/6,
fillColor = colors.mintcream, strokeColor = None, strokeWidth=0)
g.add(whitebox2)
return g
def _Flag_Finland(self):
s = _size
g = Group()
# crossbox specific bits
box = Rect(0, 0, s*2, s,
fillColor = colors.ghostwhite, strokeColor = colors.black, strokeWidth=0)
g.add(box)
blueline1 = Rect((s*0.6), 0, width=0.3*s, height=s,
fillColor = colors.darkblue, strokeColor = None, strokeWidth=0)
g.add(blueline1)
blueline2 = Rect(0, (s*0.4), width=s*2, height=s*0.3,
fillColor = colors.darkblue, strokeColor = None, strokeWidth=0)
g.add(blueline2)
return g
def _Flag_France(self):
s = _size
g = Group()
box = Rect(0, 0, s*2, s, fillColor = colors.navy, strokeColor = colors.black, strokeWidth=0)
g.add(box)
bluebox = Rect(0, 0, width=((s/3.0)*2.0), height=s,
fillColor = colors.blue, strokeColor = None, strokeWidth=0)
g.add(bluebox)
whitebox = Rect(((s/3.0)*2.0), 0, width=((s/3.0)*2.0), height=s,
fillColor = colors.mintcream, strokeColor = None, strokeWidth=0)
g.add(whitebox)
redbox = Rect(((s/3.0)*4.0), 0, width=((s/3.0)*2.0), height=s,
fillColor = colors.red,
strokeColor = None,
strokeWidth=0)
g.add(redbox)
return g
def _Flag_Germany(self):
s = _size
g = Group()
box = Rect(0, 0, s*2, s,
fillColor = colors.gold, strokeColor = colors.black, strokeWidth=0)
g.add(box)
blackbox1 = Rect(0, ((s/3.0)*2.0), width=s*2.0, height=s/3.0,
fillColor = colors.black, strokeColor = None, strokeWidth=0)
g.add(blackbox1)
redbox1 = Rect(0, (s/3.0), width=s*2.0, height=s/3.0,
fillColor = colors.orangered, strokeColor = None, strokeWidth=0)
g.add(redbox1)
return g
def _Flag_Greece(self):
s = _size
g = Group()
box = Rect(0, 0, s*2, s, fillColor = colors.gold,
strokeColor = colors.black, strokeWidth=0)
g.add(box)
for stripecounter in range (9,0, -1):
stripeheight = s/9.0
if not (stripecounter%2 == 0):
stripecolor = colors.deepskyblue
else:
stripecolor = colors.mintcream
blueorwhiteline = Rect(0, (s-(stripeheight*stripecounter)), width=s*2, height=stripeheight,
fillColor = stripecolor, strokeColor = None, strokeWidth=20)
g.add(blueorwhiteline)
bluebox1 = Rect(0, ((s)-stripeheight*5), width=(stripeheight*5), height=stripeheight*5,
fillColor = colors.deepskyblue, strokeColor = None, strokeWidth=0)
g.add(bluebox1)
whiteline1 = Rect(0, ((s)-stripeheight*3), width=stripeheight*5, height=stripeheight,
fillColor = colors.mintcream, strokeColor = None, strokeWidth=0)
g.add(whiteline1)
whiteline2 = Rect((stripeheight*2), ((s)-stripeheight*5), width=stripeheight, height=stripeheight*5,
fillColor = colors.mintcream, strokeColor = None, strokeWidth=0)
g.add(whiteline2)
return g
def _Flag_Ireland(self):
s = _size
g = Group()
box = Rect(0, 0, s*2, s,
fillColor = colors.forestgreen, strokeColor = colors.black, strokeWidth=0)
g.add(box)
whitebox = Rect(((s*2.0)/3.0), 0, width=(2.0*(s*2.0)/3.0), height=s,
fillColor = colors.mintcream, strokeColor = None, strokeWidth=0)
g.add(whitebox)
orangebox = Rect(((2.0*(s*2.0)/3.0)), 0, width=(s*2.0)/3.0, height=s,
fillColor = colors.darkorange, strokeColor = None, strokeWidth=0)
g.add(orangebox)
return g
def _Flag_Italy(self):
s = _size
g = Group()
g.add(Rect(0,0,s*2,s,fillColor=colors.forestgreen,strokeColor=None, strokeWidth=0))
g.add(Rect((2*s)/3, 0, width=(s*4)/3, height=s, fillColor = colors.mintcream, strokeColor = None, strokeWidth=0))
g.add(Rect((4*s)/3, 0, width=(s*2)/3, height=s, fillColor = colors.red, strokeColor = None, strokeWidth=0))
return g
def _Flag_Japan(self):
s = _size
g = Group()
w = self._width = s*1.5
g.add(Rect(0,0,w,s,fillColor=colors.mintcream,strokeColor=None, strokeWidth=0))
g.add(Circle(cx=w/2,cy=s/2,r=0.3*w,fillColor=colors.red,strokeColor=None, strokeWidth=0))
return g
def _Flag_Luxembourg(self):
s = _size
g = Group()
box = Rect(0, 0, s*2, s,
fillColor = colors.mintcream, strokeColor = colors.black, strokeWidth=0)
g.add(box)
redbox = Rect(0, ((s/3.0)*2.0), width=s*2.0, height=s/3.0,
fillColor = colors.red, strokeColor = None, strokeWidth=0)
g.add(redbox)
bluebox = Rect(0, 0, width=s*2.0, height=s/3.0,
fillColor = colors.dodgerblue, strokeColor = None, strokeWidth=0)
g.add(bluebox)
return g
def _Flag_Holland(self):
s = _size
g = Group()
box = Rect(0, 0, s*2, s,
fillColor = colors.mintcream, strokeColor = colors.black, strokeWidth=0)
g.add(box)
redbox = Rect(0, ((s/3.0)*2.0), width=s*2.0, height=s/3.0,
fillColor = colors.red, strokeColor = None, strokeWidth=0)
g.add(redbox)
bluebox = Rect(0, 0, width=s*2.0, height=s/3.0,
fillColor = colors.darkblue, strokeColor = None, strokeWidth=0)
g.add(bluebox)
return g
def _Flag_Portugal(self):
return Group()
def _Flag_Russia(self):
s = _size
g = Group()
w = self._width = s*1.5
t = s/3
g.add(Rect(0, 0, width=w, height=t, fillColor = colors.red, strokeColor = None, strokeWidth=0))
g.add(Rect(0, t, width=w, height=t, fillColor = colors.blue, strokeColor = None, strokeWidth=0))
g.add(Rect(0, 2*t, width=w, height=t, fillColor = colors.mintcream, strokeColor = None, strokeWidth=0))
return g
def _Flag_Spain(self):
s = _size
g = Group()
w = self._width = s*1.5
g.add(Rect(0, 0, width=w, height=s, fillColor = colors.red, strokeColor = None, strokeWidth=0))
g.add(Rect(0, (s/4), width=w, height=s/2, fillColor = colors.yellow, strokeColor = None, strokeWidth=0))
return g
def _Flag_Sweden(self):
s = _size
g = Group()
self._width = s*1.4
box = Rect(0, 0, self._width, s,
fillColor = colors.dodgerblue, strokeColor = colors.black, strokeWidth=0)
g.add(box)
box1 = Rect(((s/5)*2), 0, width=s/6, height=s,
fillColor = colors.gold, strokeColor = None, strokeWidth=0)
g.add(box1)
box2 = Rect(0, ((s/2)-(s/12)), width=self._width, height=s/6,
fillColor = colors.gold,
strokeColor = None,
strokeWidth=0)
g.add(box2)
return g
def _Flag_Norway(self):
s = _size
g = Group()
self._width = s*1.4
box = Rect(0, 0, self._width, s,
fillColor = colors.red, strokeColor = colors.black, strokeWidth=0)
g.add(box)
box = Rect(0, 0, self._width, s,
fillColor = colors.red, strokeColor = colors.black, strokeWidth=0)
g.add(box)
whiteline1 = Rect(((s*0.2)*2), 0, width=s*0.2, height=s,
fillColor = colors.ghostwhite, strokeColor = None, strokeWidth=0)
g.add(whiteline1)
whiteline2 = Rect(0, (s*0.4), width=self._width, height=s*0.2,
fillColor = colors.ghostwhite, strokeColor = None, strokeWidth=0)
g.add(whiteline2)
blueline1 = Rect(((s*0.225)*2), 0, width=0.1*s, height=s,
fillColor = colors.darkblue, strokeColor = None, strokeWidth=0)
g.add(blueline1)
blueline2 = Rect(0, (s*0.45), width=self._width, height=s*0.1,
fillColor = colors.darkblue, strokeColor = None, strokeWidth=0)
g.add(blueline2)
return g
def _Flag_CzechRepublic(self):
s = _size
g = Group()
box = Rect(0, 0, s*2, s,
fillColor = colors.mintcream,
strokeColor = colors.black,
strokeWidth=0)
g.add(box)
redbox = Rect(0, 0, width=s*2, height=s/2,
fillColor = colors.red,
strokeColor = None,
strokeWidth=0)
g.add(redbox)
bluewedge = Polygon(points = [ 0, 0, s, (s/2), 0, s],
fillColor = colors.darkblue, strokeColor = None, strokeWidth=0)
g.add(bluewedge)
return g
def _Flag_Palestine(self):
s = _size
g = Group()
box = Rect(0, s/3, s*2, s/3,
fillColor = colors.mintcream,
strokeColor = None,
strokeWidth=0)
g.add(box)
greenbox = Rect(0, 0, width=s*2, height=s/3,
fillColor = colors.limegreen,
strokeColor = None,
strokeWidth=0)
g.add(greenbox)
blackbox = Rect(0, 2*s/3, width=s*2, height=s/3,
fillColor = colors.black,
strokeColor = None,
strokeWidth=0)
g.add(blackbox)
redwedge = Polygon(points = [ 0, 0, 2*s/3, (s/2), 0, s],
fillColor = colors.red, strokeColor = None, strokeWidth=0)
g.add(redwedge)
return g
def _Flag_Turkey(self):
s = _size
g = Group()
box = Rect(0, 0, s*2, s,
fillColor = colors.red,
strokeColor = colors.black,
strokeWidth=0)
g.add(box)
whitecircle = Circle(cx=((s*0.35)*2), cy=s/2, r=s*0.3,
fillColor = colors.mintcream,
strokeColor = None,
strokeWidth=0)
g.add(whitecircle)
redcircle = Circle(cx=((s*0.39)*2), cy=s/2, r=s*0.24,
fillColor = colors.red,
strokeColor = None,
strokeWidth=0)
g.add(redcircle)
ws = Star()
ws.angle = 15
ws.size = s/5
ws.x = (s*0.5)*2+ws.size/2
ws.y = (s*0.5)
ws.fillColor = colors.mintcream
ws.strokeColor = None
g.add(ws)
return g
def _Flag_Switzerland(self):
s = _size
g = Group()
self._width = s
g.add(Rect(0, 0, s, s, fillColor = colors.red, strokeColor = colors.black, strokeWidth=0))
g.add(Line((s/2), (s/5.5), (s/2), (s-(s/5.5)),
fillColor = colors.mintcream, strokeColor = colors.mintcream, strokeWidth=(s/5)))
g.add(Line((s/5.5), (s/2), (s-(s/5.5)), (s/2),
fillColor = colors.mintcream, strokeColor = colors.mintcream, strokeWidth=s/5))
return g
def _Flag_EU(self):
s = _size
g = Group()
w = self._width = 1.5*s
g.add(Rect(0, 0, w, s, fillColor = colors.darkblue, strokeColor = None, strokeWidth=0))
centerx=w/2
centery=s/2
radius=s/3
yradius = radius
xradius = radius
nStars = 12
delta = 2*pi/nStars
for i in range(nStars):
rad = i*delta
gs = Star()
gs.x=cos(rad)*radius+centerx
gs.y=sin(rad)*radius+centery
gs.size=s/10
gs.fillColor=colors.gold
g.add(gs)
return g
def _Flag_Brazil(self):
s = _size # abbreviate as we will use this a lot
g = Group()
m = s/14
self._width = w = (m * 20)
def addStar(x,y,size, g=g, w=w, s=s, m=m):
st = Star()
st.fillColor=colors.mintcream
st.size = size*m
st.x = (w/2) + (x * (0.35 * m))
st.y = (s/2) + (y * (0.35 * m))
g.add(st)
g.add(Rect(0, 0, w, s, fillColor = colors.green, strokeColor = None, strokeWidth=0))
g.add(Polygon(points = [ 1.7*m, (s/2), (w/2), s-(1.7*m), w-(1.7*m),(s/2),(w/2), 1.7*m],
fillColor = colors.yellow, strokeColor = None, strokeWidth=0))
g.add(Circle(cx=w/2, cy=s/2, r=3.5*m,
fillColor=colors.blue,strokeColor=None, strokeWidth=0))
g.add(Wedge((w/2)-(2*m), 0, 8.5*m, 50, 98.1, 8.5*m,
fillColor=colors.mintcream,strokeColor=None, strokeWidth=0))
g.add(Wedge((w/2), (s/2), 3.501*m, 156, 352, 3.501*m,
fillColor=colors.mintcream,strokeColor=None, strokeWidth=0))
g.add(Wedge((w/2)-(2*m), 0, 8*m, 48.1, 100, 8*m,
fillColor=colors.blue,strokeColor=None, strokeWidth=0))
g.add(Rect(0, 0, w, (s/4) + 1.7*m,
fillColor = colors.green, strokeColor = None, strokeWidth=0))
g.add(Polygon(points = [ 1.7*m,(s/2), (w/2),s/2 - 2*m, w-(1.7*m),(s/2) , (w/2),1.7*m],
fillColor = colors.yellow, strokeColor = None, strokeWidth=0))
g.add(Wedge(w/2, s/2, 3.502*m, 166, 342.1, 3.502*m,
fillColor=colors.blue,strokeColor=None, strokeWidth=0))
addStar(3.2,3.5,0.3)
addStar(-8.5,1.5,0.3)
addStar(-7.5,-3,0.3)
addStar(-4,-5.5,0.3)
addStar(0,-4.5,0.3)
addStar(7,-3.5,0.3)
addStar(-3.5,-0.5,0.25)
addStar(0,-1.5,0.25)
addStar(1,-2.5,0.25)
addStar(3,-7,0.25)
addStar(5,-6.5,0.25)
addStar(6.5,-5,0.25)
addStar(7,-4.5,0.25)
addStar(-5.5,-3.2,0.25)
addStar(-6,-4.2,0.25)
addStar(-1,-2.75,0.2)
addStar(2,-5.5,0.2)
addStar(4,-5.5,0.2)
addStar(5,-7.5,0.2)
addStar(5,-5.5,0.2)
addStar(6,-5.5,0.2)
addStar(-8.8,-3.2,0.2)
addStar(2.5,0.5,0.2)
addStar(-0.2,-3.2,0.14)
addStar(-7.2,-2,0.14)
addStar(0,-8,0.1)
sTmp = "ORDEM E PROGRESSO"
nTmp = len(sTmp)
delta = 0.850848010347/nTmp
radius = 7.9 *m
centerx = (w/2)-(2*m)
centery = 0
for i in range(nTmp):
rad = 2*pi - i*delta -4.60766922527
x=cos(rad)*radius+centerx
y=sin(rad)*radius+centery
if i == 6:
z = 0.35*m
else:
z= 0.45*m
g2 = Group(String(x, y, sTmp[i], fontName='Helvetica-Bold',
fontSize = z,strokeColor=None,fillColor=colors.green))
g2.rotate(rad)
g.add(g2)
return g
def makeFlag(name):
flag = Flag()
flag.kind = name
return flag
def test():
"""This function produces three pdf files with examples of all the signs and symbols from this file.
"""
# page 1
labelFontSize = 10
X = (20,245)
flags = [
'UK',
'USA',
'Afghanistan',
'Austria',
'Belgium',
'Denmark',
'Cuba',
'Finland',
'France',
'Germany',
'Greece',
'Ireland',
'Italy',
'Luxembourg',
'Holland',
'Palestine',
'Portugal',
'Spain',
'Sweden',
'Norway',
'CzechRepublic',
'Turkey',
'Switzerland',
'EU',
'Brazil',
]
y = Y0 = 530
f = 0
D = None
for name in flags:
if not D: D = Drawing(450,650)
flag = makeFlag(name)
i = flags.index(name)
flag.x = X[i%2]
flag.y = y
D.add(flag)
D.add(String(flag.x+(flag.size/2),(flag.y-(1.2*labelFontSize)),
name, fillColor=colors.black, textAnchor='middle', fontSize=labelFontSize))
if i%2: y = y - 125
if (i%2 and y<0) or name==flags[-1]:
renderPDF.drawToFile(D, 'flags%02d.pdf'%f, 'flags.py - Page #%d'%(f+1))
y = Y0
f = f+1
D = None
if __name__=='__main__':
test()

View File

@ -1,504 +0,0 @@
#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/graphics/widgets/grids.py
__version__=''' $Id$ '''
from reportlab.lib import colors
from reportlab.lib.validators import isNumber, isColorOrNone, isBoolean, isListOfNumbers, OneOf, isListOfColors
from reportlab.lib.attrmap import AttrMap, AttrMapValue
from reportlab.graphics.shapes import Drawing, Group, Line, Rect, LineShape, definePath, EmptyClipPath
from reportlab.graphics.widgetbase import Widget
def frange(start, end=None, inc=None):
"A range function, that does accept float increments..."
if end == None:
end = start + 0.0
start = 0.0
if inc == None:
inc = 1.0
L = []
end = end - inc*0.0001 #to avoid numrical problems
while 1:
next = start + len(L) * inc
if inc > 0 and next >= end:
break
elif inc < 0 and next <= end:
break
L.append(next)
return L
def makeDistancesList(list):
"""Returns a list of distances between adjacent numbers in some input list.
E.g. [1, 1, 2, 3, 5, 7] -> [0, 1, 1, 2, 2]
"""
d = []
for i in range(len(list[:-1])):
d.append(list[i+1] - list[i])
return d
class Grid(Widget):
"""This makes a rectangular grid of equidistant stripes.
The grid contains an outer border rectangle, and stripes
inside which can be drawn with lines and/or as solid tiles.
The drawing order is: outer rectangle, then lines and tiles.
The stripes' width is indicated as 'delta'. The sequence of
stripes can have an offset named 'delta0'. Both values need
to be positive!
"""
_attrMap = AttrMap(
x = AttrMapValue(isNumber, desc="The grid's lower-left x position."),
y = AttrMapValue(isNumber, desc="The grid's lower-left y position."),
width = AttrMapValue(isNumber, desc="The grid's width."),
height = AttrMapValue(isNumber, desc="The grid's height."),
orientation = AttrMapValue(OneOf(('vertical', 'horizontal')),
desc='Determines if stripes are vertical or horizontal.'),
useLines = AttrMapValue(OneOf((0, 1)),
desc='Determines if stripes are drawn with lines.'),
useRects = AttrMapValue(OneOf((0, 1)),
desc='Determines if stripes are drawn with solid rectangles.'),
delta = AttrMapValue(isNumber,
desc='Determines the width/height of the stripes.'),
delta0 = AttrMapValue(isNumber,
desc='Determines the stripes initial width/height offset.'),
deltaSteps = AttrMapValue(isListOfNumbers,
desc='List of deltas to be used cyclically.'),
stripeColors = AttrMapValue(isListOfColors,
desc='Colors applied cyclically in the right or upper direction.'),
fillColor = AttrMapValue(isColorOrNone,
desc='Background color for entire rectangle.'),
strokeColor = AttrMapValue(isColorOrNone,
desc='Color used for lines.'),
strokeWidth = AttrMapValue(isNumber,
desc='Width used for lines.'),
rectStrokeColor = AttrMapValue(isColorOrNone, desc='Color for outer rect stroke.'),
rectStrokeWidth = AttrMapValue(isColorOrNone, desc='Width for outer rect stroke.'),
)
def __init__(self):
self.x = 0
self.y = 0
self.width = 100
self.height = 100
self.orientation = 'vertical'
self.useLines = 0
self.useRects = 1
self.delta = 20
self.delta0 = 0
self.deltaSteps = []
self.fillColor = colors.white
self.stripeColors = [colors.red, colors.green, colors.blue]
self.strokeColor = colors.black
self.strokeWidth = 2
def demo(self):
D = Drawing(100, 100)
g = Grid()
D.add(g)
return D
def makeOuterRect(self):
strokeColor = getattr(self,'rectStrokeColor',self.strokeColor)
strokeWidth = getattr(self,'rectStrokeWidth',self.strokeWidth)
if self.fillColor or (strokeColor and strokeWidth):
rect = Rect(self.x, self.y, self.width, self.height)
rect.fillColor = self.fillColor
rect.strokeColor = strokeColor
rect.strokeWidth = strokeWidth
return rect
else:
return None
def makeLinePosList(self, start, isX=0):
"Returns a list of positions where to place lines."
w, h = self.width, self.height
if isX:
length = w
else:
length = h
if self.deltaSteps:
r = [start + self.delta0]
i = 0
while 1:
if r[-1] > start + length:
del r[-1]
break
r.append(r[-1] + self.deltaSteps[i % len(self.deltaSteps)])
i = i + 1
else:
r = frange(start + self.delta0, start + length, self.delta)
r.append(start + length)
if self.delta0 != 0:
r.insert(0, start)
#print 'Grid.makeLinePosList() -> %s' % r
return r
def makeInnerLines(self):
# inner grid lines
group = Group()
w, h = self.width, self.height
if self.useLines == 1:
if self.orientation == 'vertical':
r = self.makeLinePosList(self.x, isX=1)
for x in r:
line = Line(x, self.y, x, self.y + h)
line.strokeColor = self.strokeColor
line.strokeWidth = self.strokeWidth
group.add(line)
elif self.orientation == 'horizontal':
r = self.makeLinePosList(self.y, isX=0)
for y in r:
line = Line(self.x, y, self.x + w, y)
line.strokeColor = self.strokeColor
line.strokeWidth = self.strokeWidth
group.add(line)
return group
def makeInnerTiles(self):
# inner grid lines
group = Group()
w, h = self.width, self.height
# inner grid stripes (solid rectangles)
if self.useRects == 1:
cols = self.stripeColors
if self.orientation == 'vertical':
r = self.makeLinePosList(self.x, isX=1)
elif self.orientation == 'horizontal':
r = self.makeLinePosList(self.y, isX=0)
dist = makeDistancesList(r)
i = 0
for j in range(len(dist)):
if self.orientation == 'vertical':
x = r[j]
stripe = Rect(x, self.y, dist[j], h)
elif self.orientation == 'horizontal':
y = r[j]
stripe = Rect(self.x, y, w, dist[j])
stripe.fillColor = cols[i % len(cols)]
stripe.strokeColor = None
group.add(stripe)
i = i + 1
return group
def draw(self):
# general widget bits
group = Group()
group.add(self.makeOuterRect())
group.add(self.makeInnerTiles())
group.add(self.makeInnerLines(),name='_gridLines')
return group
class DoubleGrid(Widget):
"""This combines two ordinary Grid objects orthogonal to each other.
"""
_attrMap = AttrMap(
x = AttrMapValue(isNumber, desc="The grid's lower-left x position."),
y = AttrMapValue(isNumber, desc="The grid's lower-left y position."),
width = AttrMapValue(isNumber, desc="The grid's width."),
height = AttrMapValue(isNumber, desc="The grid's height."),
grid0 = AttrMapValue(None, desc="The first grid component."),
grid1 = AttrMapValue(None, desc="The second grid component."),
)
def __init__(self):
self.x = 0
self.y = 0
self.width = 100
self.height = 100
g0 = Grid()
g0.x = self.x
g0.y = self.y
g0.width = self.width
g0.height = self.height
g0.orientation = 'vertical'
g0.useLines = 1
g0.useRects = 0
g0.delta = 20
g0.delta0 = 0
g0.deltaSteps = []
g0.fillColor = colors.white
g0.stripeColors = [colors.red, colors.green, colors.blue]
g0.strokeColor = colors.black
g0.strokeWidth = 1
g1 = Grid()
g1.x = self.x
g1.y = self.y
g1.width = self.width
g1.height = self.height
g1.orientation = 'horizontal'
g1.useLines = 1
g1.useRects = 0
g1.delta = 20
g1.delta0 = 0
g1.deltaSteps = []
g1.fillColor = colors.white
g1.stripeColors = [colors.red, colors.green, colors.blue]
g1.strokeColor = colors.black
g1.strokeWidth = 1
self.grid0 = g0
self.grid1 = g1
## # This gives an AttributeError:
## # DoubleGrid instance has no attribute 'grid0'
## def __setattr__(self, name, value):
## if name in ('x', 'y', 'width', 'height'):
## setattr(self.grid0, name, value)
## setattr(self.grid1, name, value)
def demo(self):
D = Drawing(100, 100)
g = DoubleGrid()
D.add(g)
return D
def draw(self):
group = Group()
g0, g1 = self.grid0, self.grid1
# Order groups to make sure both v and h lines
# are visible (works only when there is only
# one kind of stripes, v or h).
G = g0.useRects == 1 and g1.useRects == 0 and (g0,g1) or (g1,g0)
for g in G:
group.add(g.makeOuterRect())
for g in G:
group.add(g.makeInnerTiles())
group.add(g.makeInnerLines(),name='_gridLines')
return group
class ShadedRect(Widget):
"""This makes a rectangle with shaded colors between two colors.
Colors are interpolated linearly between 'fillColorStart'
and 'fillColorEnd', both of which appear at the margins.
If 'numShades' is set to one, though, only 'fillColorStart'
is used.
"""
_attrMap = AttrMap(
x = AttrMapValue(isNumber, desc="The grid's lower-left x position."),
y = AttrMapValue(isNumber, desc="The grid's lower-left y position."),
width = AttrMapValue(isNumber, desc="The grid's width."),
height = AttrMapValue(isNumber, desc="The grid's height."),
orientation = AttrMapValue(OneOf(('vertical', 'horizontal')), desc='Determines if stripes are vertical or horizontal.'),
numShades = AttrMapValue(isNumber, desc='The number of interpolating colors.'),
fillColorStart = AttrMapValue(isColorOrNone, desc='Start value of the color shade.'),
fillColorEnd = AttrMapValue(isColorOrNone, desc='End value of the color shade.'),
strokeColor = AttrMapValue(isColorOrNone, desc='Color used for border line.'),
strokeWidth = AttrMapValue(isNumber, desc='Width used for lines.'),
cylinderMode = AttrMapValue(isBoolean, desc='True if shading reverses in middle.'),
)
def __init__(self,**kw):
self.x = 0
self.y = 0
self.width = 100
self.height = 100
self.orientation = 'vertical'
self.numShades = 20
self.fillColorStart = colors.pink
self.fillColorEnd = colors.black
self.strokeColor = colors.black
self.strokeWidth = 2
self.cylinderMode = 0
self.setProperties(kw)
def demo(self):
D = Drawing(100, 100)
g = ShadedRect()
D.add(g)
return D
def _flipRectCorners(self):
"Flip rectangle's corners if width or height is negative."
x, y, width, height, fillColorStart, fillColorEnd = self.x, self.y, self.width, self.height, self.fillColorStart, self.fillColorEnd
if width < 0 and height > 0:
x = x + width
width = -width
if self.orientation=='vertical': fillColorStart, fillColorEnd = fillColorEnd, fillColorStart
elif height<0 and width>0:
y = y + height
height = -height
if self.orientation=='horizontal': fillColorStart, fillColorEnd = fillColorEnd, fillColorStart
elif height < 0 and height < 0:
x = x + width
width = -width
y = y + height
height = -height
return x, y, width, height, fillColorStart, fillColorEnd
def draw(self):
# general widget bits
group = Group()
x, y, w, h, c0, c1 = self._flipRectCorners()
numShades = self.numShades
if self.cylinderMode:
if not numShades%2: numShades = numShades+1
halfNumShades = (numShades-1)/2 + 1
num = float(numShades) # must make it float!
vertical = self.orientation == 'vertical'
if vertical:
if numShades == 1:
V = [x]
else:
V = frange(x, x + w, w/num)
else:
if numShades == 1:
V = [y]
else:
V = frange(y, y + h, h/num)
for v in V:
stripe = vertical and Rect(v, y, w/num, h) or Rect(x, v, w, h/num)
if self.cylinderMode:
if V.index(v)>=halfNumShades:
col = colors.linearlyInterpolatedColor(c1,c0,V[halfNumShades],V[-1], v)
else:
col = colors.linearlyInterpolatedColor(c0,c1,V[0],V[halfNumShades], v)
else:
col = colors.linearlyInterpolatedColor(c0,c1,V[0],V[-1], v)
stripe.fillColor = col
stripe.strokeColor = col
stripe.strokeWidth = 1
group.add(stripe)
if self.strokeColor and self.strokeWidth>=0:
rect = Rect(x, y, w, h)
rect.strokeColor = self.strokeColor
rect.strokeWidth = self.strokeWidth
rect.fillColor = None
group.add(rect)
return group
def colorRange(c0, c1, n):
"Return a range of intermediate colors between c0 and c1"
if n==1: return [c0]
C = []
if n>1:
lim = n-1
for i in range(n):
C.append(colors.linearlyInterpolatedColor(c0,c1,0,lim, i))
return C
def centroid(P):
'''compute average point of a set of points'''
return reduce(lambda x,y, fn=float(len(P)): (x[0]+y[0]/fn,x[1]+y[1]/fn),P,(0,0))
def rotatedEnclosingRect(P, angle, rect):
'''
given P a sequence P of x,y coordinate pairs and an angle in degrees
find the centroid of P and the axis at angle theta through it
find the extreme points of P wrt axis parallel distance and axis
orthogonal distance. Then compute the least rectangle that will still
enclose P when rotated by angle.
The class R
'''
from math import pi, cos, sin, tan
x0, y0 = centroid(P)
theta = (angle/180.)*pi
s,c=sin(theta),cos(theta)
def parallelAxisDist((x,y),s=s,c=c,x0=x0,y0=y0):
return (s*(y-y0)+c*(x-x0))
def orthogonalAxisDist((x,y),s=s,c=c,x0=x0,y0=y0):
return (c*(y-y0)+s*(x-x0))
L = map(parallelAxisDist,P)
L.sort()
a0, a1 = L[0], L[-1]
L = map(orthogonalAxisDist,P)
L.sort()
b0, b1 = L[0], L[-1]
rect.x, rect.width = a0, a1-a0
rect.y, rect.height = b0, b1-b0
g = Group(transform=(c,s,-s,c,x0,y0))
g.add(rect)
return g
class ShadedPolygon(Widget,LineShape):
_attrMap = AttrMap(BASE=LineShape,
angle = AttrMapValue(isNumber,desc="Shading angle"),
fillColorStart = AttrMapValue(isColorOrNone),
fillColorEnd = AttrMapValue(isColorOrNone),
numShades = AttrMapValue(isNumber, desc='The number of interpolating colors.'),
cylinderMode = AttrMapValue(isBoolean, desc='True if shading reverses in middle.'),
points = AttrMapValue(isListOfNumbers),
)
def __init__(self,**kw):
self.angle = 90
self.fillColorStart = colors.red
self.fillColorEnd = colors.green
self.cylinderMode = 0
self.numShades = 50
self.points = [-1,-1,2,2,3,-1]
LineShape.__init__(self,kw)
def draw(self):
P = self.points
P = map(lambda i, P=P:(P[i],P[i+1]),xrange(0,len(P),2))
path = definePath([('moveTo',)+P[0]]+map(lambda x: ('lineTo',)+x,P[1:])+['closePath'],
fillColor=None, strokeColor=None)
path.isClipPath = 1
g = Group()
g.add(path)
rect = ShadedRect(strokeWidth=0,strokeColor=None)
for k in 'fillColorStart', 'fillColorEnd', 'numShades', 'cylinderMode':
setattr(rect,k,getattr(self,k))
g.add(rotatedEnclosingRect(P, self.angle, rect))
g.add(EmptyClipPath)
path = path.copy()
path.isClipPath = 0
path.strokeColor = self.strokeColor
path.strokeWidth = self.strokeWidth
g.add(path)
return g
if __name__=='__main__': #noruntests
from reportlab.lib.colors import blue
from reportlab.graphics.shapes import Drawing
angle=45
D = Drawing(120,120)
D.add(ShadedPolygon(points=(10,10,60,60,110,10),strokeColor=None,strokeWidth=1,angle=90,numShades=50,cylinderMode=0))
D.save(formats=['gif'],fnRoot='shobj',outDir='/tmp')

View File

@ -1,228 +0,0 @@
#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/graphics/widgets/markers.py
"""
This modules defines a collection of markers used in charts.
"""
__version__=''' $Id$ '''
from types import FunctionType, ClassType
from reportlab.graphics.shapes import Rect, Line, Circle, Polygon, Drawing, Group
from reportlab.graphics.widgets.signsandsymbols import SmileyFace
from reportlab.graphics.widgetbase import Widget
from reportlab.lib.validators import isNumber, isColorOrNone, OneOf, Validator
from reportlab.lib.attrmap import AttrMap, AttrMapValue
from reportlab.lib.colors import black
from reportlab.graphics.widgets.flags import Flag
from math import sin, cos, pi
import copy, new
_toradians = pi/180.0
class Marker(Widget):
'''A polymorphic class of markers'''
_attrMap = AttrMap(BASE=Widget,
kind = AttrMapValue(
OneOf(None, 'Square', 'Diamond', 'Circle', 'Cross', 'Triangle', 'StarSix',
'Pentagon', 'Hexagon', 'Heptagon', 'Octagon', 'StarFive',
'FilledSquare', 'FilledCircle', 'FilledDiamond', 'FilledCross',
'FilledTriangle','FilledStarSix', 'FilledPentagon', 'FilledHexagon',
'FilledHeptagon', 'FilledOctagon', 'FilledStarFive',
'Smiley'),
desc='marker type name'),
size = AttrMapValue(isNumber,desc='marker size'),
x = AttrMapValue(isNumber,desc='marker x coordinate'),
y = AttrMapValue(isNumber,desc='marker y coordinate'),
dx = AttrMapValue(isNumber,desc='marker x coordinate adjustment'),
dy = AttrMapValue(isNumber,desc='marker y coordinate adjustment'),
angle = AttrMapValue(isNumber,desc='marker rotation'),
fillColor = AttrMapValue(isColorOrNone, desc='marker fill colour'),
strokeColor = AttrMapValue(isColorOrNone, desc='marker stroke colour'),
strokeWidth = AttrMapValue(isNumber, desc='marker stroke width'),
)
def __init__(self,*args,**kw):
self.kind = None
self.strokeColor = black
self.strokeWidth = 0.1
self.fillColor = None
self.size = 5
self.x = self.y = self.dx = self.dy = self.angle = 0
self.setProperties(kw)
def clone(self):
return new.instance(self.__class__,self.__dict__.copy())
def _Smiley(self):
x, y = self.x+self.dx, self.y+self.dy
d = self.size/2.0
s = SmileyFace()
s.fillColor = self.fillColor
s.strokeWidth = self.strokeWidth
s.strokeColor = self.strokeColor
s.x = x-d
s.y = y-d
s.size = d*2
return s
def _Square(self):
x, y = self.x+self.dx, self.y+self.dy
d = self.size/2.0
s = Rect(x-d,y-d,2*d,2*d,fillColor=self.fillColor,strokeColor=self.strokeColor,strokeWidth=self.strokeWidth)
return s
def _Diamond(self):
d = self.size/2.0
return self._doPolygon((-d,0,0,d,d,0,0,-d))
def _Circle(self):
x, y = self.x+self.dx, self.y+self.dy
s = Circle(x,y,self.size/2.0,fillColor=self.fillColor,strokeColor=self.strokeColor,strokeWidth=self.strokeWidth)
return s
def _Cross(self):
x, y = self.x+self.dx, self.y+self.dy
s = float(self.size)
h, s = s/2, s/6
return self._doPolygon((-s,-h,-s,-s,-h,-s,-h,s,-s,s,-s,h,s,h,s,s,h,s,h,-s,s,-s,s,-h))
def _Triangle(self):
x, y = self.x+self.dx, self.y+self.dy
r = float(self.size)/2
c = 30*_toradians
s = sin(30*_toradians)*r
c = cos(c)*r
return self._doPolygon((0,r,-c,-s,c,-s))
def _StarSix(self):
r = float(self.size)/2
c = 30*_toradians
s = sin(c)*r
c = cos(c)*r
z = s/2
g = c/2
return self._doPolygon((0,r,-z,s,-c,s,-s,0,-c,-s,-z,-s,0,-r,z,-s,c,-s,s,0,c,s,z,s))
def _StarFive(self):
R = float(self.size)/2
r = R*sin(18*_toradians)/cos(36*_toradians)
P = []
angle = 90
for i in xrange(5):
for radius in R, r:
theta = angle*_toradians
P.append(radius*cos(theta))
P.append(radius*sin(theta))
angle = angle + 36
return self._doPolygon(P)
def _Pentagon(self):
return self._doNgon(5)
def _Hexagon(self):
return self._doNgon(6)
def _Heptagon(self):
return self._doNgon(7)
def _Octagon(self):
return self._doNgon(8)
def _doPolygon(self,P):
x, y = self.x+self.dx, self.y+self.dy
if x or y: P = map(lambda i,P=P,A=[x,y]: P[i] + A[i&1], range(len(P)))
return Polygon(P, strokeWidth =self.strokeWidth, strokeColor=self.strokeColor, fillColor=self.fillColor)
def _doFill(self):
old = self.fillColor
if old is None:
self.fillColor = self.strokeColor
r = (self.kind and getattr(self,'_'+self.kind[6:]) or Group)()
self.fillColor = old
return r
def _doNgon(self,n):
P = []
size = float(self.size)/2
for i in xrange(n):
r = (2.*i/n+0.5)*pi
P.append(size*cos(r))
P.append(size*sin(r))
return self._doPolygon(P)
_FilledCircle = _doFill
_FilledSquare = _doFill
_FilledDiamond = _doFill
_FilledCross = _doFill
_FilledTriangle = _doFill
_FilledStarSix = _doFill
_FilledPentagon = _doFill
_FilledHexagon = _doFill
_FilledHeptagon = _doFill
_FilledOctagon = _doFill
_FilledStarFive = _doFill
def draw(self):
if self.kind:
m = getattr(self,'_'+self.kind)
if self.angle:
_x, _dx, _y, _dy = self.x, self.dx, self.y, self.dy
self.x, self.dx, self.y, self.dy = 0,0,0,0
try:
m = m()
finally:
self.x, self.dx, self.y, self.dy = _x, _dx, _y, _dy
if not isinstance(m,Group):
_m, m = m, Group()
m.add(_m)
if self.angle: m.rotate(self.angle)
x, y = _x+_dx, _y+_dy
if x or y: m.shift(x,y)
else:
m = m()
else:
m = Group()
return m
def uSymbol2Symbol(uSymbol,x,y,color):
if type(uSymbol) == FunctionType:
symbol = uSymbol(x, y, 5, color)
elif type(uSymbol) == ClassType and issubclass(uSymbol,Widget):
size = 10.
symbol = uSymbol()
symbol.x = x - (size/2)
symbol.y = y - (size/2)
try:
symbol.size = size
symbol.color = color
except:
pass
elif isinstance(uSymbol,Marker) or isinstance(uSymbol,Flag):
symbol = uSymbol.clone()
if isinstance(uSymbol,Marker): symbol.fillColor = symbol.fillColor or color
symbol.x, symbol.y = x, y
else:
symbol = None
return symbol
class _isSymbol(Validator):
def test(self,x):
return callable(x) or isinstance(x,Marker) or isinstance(x,Flag) \
or (type(x)==ClassType and issubclass(x,Widget))
isSymbol = _isSymbol()
def makeMarker(name,**kw):
if Marker._attrMap['kind'].validate(name):
m = apply(Marker,(),kw)
m.kind = name
elif name[-5:]=='_Flag' and Flag._attrMap['kind'].validate(name[:-5]):
m = apply(Flag,(),kw)
m.kind = name[:-5]
m.size = 10
else:
raise ValueError, "Invalid marker name %s" % name
return m
if __name__=='__main__':
D = Drawing()
D.add(Marker())
D.save(fnRoot='Marker',formats=['pdf'], outDir='/tmp')

View File

@ -1,919 +0,0 @@
#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/graphics/widgets/signsandsymbols.py
# signsandsymbols.py
# A collection of new widgets
# author: John Precedo (johnp@reportlab.com)
"""This file is a collection of widgets to produce some common signs and symbols.
Widgets include:
- ETriangle (an equilateral triangle),
- RTriangle (a right angled triangle),
- Octagon,
- Crossbox,
- Tickbox,
- SmileyFace,
- StopSign,
- NoEntry,
- NotAllowed (the red roundel from 'no smoking' signs),
- NoSmoking,
- DangerSign (a black exclamation point in a yellow triangle),
- YesNo (returns a tickbox or a crossbox depending on a testvalue),
- FloppyDisk,
- ArrowOne, and
- ArrowTwo
"""
__version__=''' $Id$ '''
from reportlab.lib import colors
from reportlab.lib.validators import *
from reportlab.lib.attrmap import *
from reportlab.graphics import shapes
from reportlab.graphics.widgetbase import Widget
from reportlab.graphics import renderPDF
class _Symbol(Widget):
"""Abstract base widget
possible attributes:
'x', 'y', 'size', 'fillColor', 'strokeColor'
"""
_nodoc = 1
_attrMap = AttrMap(
x = AttrMapValue(isNumber,desc='symbol x coordinate'),
y = AttrMapValue(isNumber,desc='symbol y coordinate'),
dx = AttrMapValue(isNumber,desc='symbol x coordinate adjustment'),
dy = AttrMapValue(isNumber,desc='symbol x coordinate adjustment'),
size = AttrMapValue(isNumber),
fillColor = AttrMapValue(isColorOrNone),
strokeColor = AttrMapValue(isColorOrNone),
strokeWidth = AttrMapValue(isNumber),
)
def __init__(self):
assert self.__class__.__name__!='_Symbol', 'Abstract class _Symbol instantiated'
self.x = self.y = self.dx = self.dy = 0
self.size = 100
self.fillColor = colors.red
self.strokeColor = None
self.strokeWidth = 0.1
def demo(self):
D = shapes.Drawing(200, 100)
s = float(self.size)
ob = self.__class__()
ob.x=50
ob.y=0
ob.draw()
D.add(ob)
D.add(shapes.String(ob.x+(s/2),(ob.y-12),
ob.__class__.__name__, fillColor=colors.black, textAnchor='middle',
fontSize=10))
return D
class ETriangle(_Symbol):
"""This draws an equilateral triangle."""
def __init__(self):
pass #AbstractSymbol
def draw(self):
# general widget bits
s = float(self.size) # abbreviate as we will use this a lot
g = shapes.Group()
# Triangle specific bits
ae = s*0.125 #(ae = 'an eighth')
triangle = shapes.Polygon(points = [
self.x, self.y,
self.x+s, self.y,
self.x+(s/2),self.y+s],
fillColor = self.fillColor,
strokeColor = self.strokeColor,
strokeWidth=s/50.)
g.add(triangle)
return g
class RTriangle(_Symbol):
"""This draws a right-angled triangle.
possible attributes:
'x', 'y', 'size', 'fillColor', 'strokeColor'
"""
def __init__(self):
self.x = 0
self.y = 0
self.size = 100
self.fillColor = colors.green
self.strokeColor = None
def draw(self):
# general widget bits
s = float(self.size) # abbreviate as we will use this a lot
g = shapes.Group()
# Triangle specific bits
ae = s*0.125 #(ae = 'an eighth')
triangle = shapes.Polygon(points = [
self.x, self.y,
self.x+s, self.y,
self.x,self.y+s],
fillColor = self.fillColor,
strokeColor = self.strokeColor,
strokeWidth=s/50.)
g.add(triangle)
return g
class Octagon(_Symbol):
"""This widget draws an Octagon.
possible attributes:
'x', 'y', 'size', 'fillColor', 'strokeColor'
"""
def __init__(self):
self.x = 0
self.y = 0
self.size = 100
self.fillColor = colors.yellow
self.strokeColor = None
def draw(self):
# general widget bits
s = float(self.size) # abbreviate as we will use this a lot
g = shapes.Group()
# Octagon specific bits
athird=s/3
octagon = shapes.Polygon(points=[self.x+athird, self.y,
self.x, self.y+athird,
self.x, self.y+(athird*2),
self.x+athird, self.y+s,
self.x+(athird*2), self.y+s,
self.x+s, self.y+(athird*2),
self.x+s, self.y+athird,
self.x+(athird*2), self.y],
strokeColor = self.strokeColor,
fillColor = self.fillColor,
strokeWidth=10)
g.add(octagon)
return g
class Crossbox(_Symbol):
"""This draws a black box with a red cross in it - a 'checkbox'.
possible attributes:
'x', 'y', 'size', 'crossColor', 'strokeColor', 'crosswidth'
"""
_attrMap = AttrMap(BASE=_Symbol,
crossColor = AttrMapValue(isColorOrNone),
crosswidth = AttrMapValue(isNumber),
)
def __init__(self):
self.x = 0
self.y = 0
self.size = 100
self.fillColor = colors.white
self.crossColor = colors.red
self.strokeColor = colors.black
self.crosswidth = 10
def draw(self):
# general widget bits
s = float(self.size) # abbreviate as we will use this a lot
g = shapes.Group()
# crossbox specific bits
box = shapes.Rect(self.x+1, self.y+1, s-2, s-2,
fillColor = self.fillColor,
strokeColor = self.strokeColor,
strokeWidth=2)
g.add(box)
crossLine1 = shapes.Line(self.x+(s*0.15), self.y+(s*0.15), self.x+(s*0.85), self.y+(s*0.85),
fillColor = self.crossColor,
strokeColor = self.crossColor,
strokeWidth = self.crosswidth)
g.add(crossLine1)
crossLine2 = shapes.Line(self.x+(s*0.15), self.y+(s*0.85), self.x+(s*0.85) ,self.y+(s*0.15),
fillColor = self.crossColor,
strokeColor = self.crossColor,
strokeWidth = self.crosswidth)
g.add(crossLine2)
return g
class Tickbox(_Symbol):
"""This draws a black box with a red tick in it - another 'checkbox'.
possible attributes:
'x', 'y', 'size', 'tickColor', 'strokeColor', 'tickwidth'
"""
_attrMap = AttrMap(BASE=_Symbol,
tickColor = AttrMapValue(isColorOrNone),
tickwidth = AttrMapValue(isNumber),
)
def __init__(self):
self.x = 0
self.y = 0
self.size = 100
self.tickColor = colors.red
self.strokeColor = colors.black
self.fillColor = colors.white
self.tickwidth = 10
def draw(self):
# general widget bits
s = float(self.size) # abbreviate as we will use this a lot
g = shapes.Group()
# tickbox specific bits
box = shapes.Rect(self.x+1, self.y+1, s-2, s-2,
fillColor = self.fillColor,
strokeColor = self.strokeColor,
strokeWidth=2)
g.add(box)
tickLine = shapes.PolyLine(points = [self.x+(s*0.15), self.y+(s*0.35), self.x+(s*0.35), self.y+(s*0.15),
self.x+(s*0.35), self.y+(s*0.15), self.x+(s*0.85) ,self.y+(s*0.85)],
fillColor = self.tickColor,
strokeColor = self.tickColor,
strokeWidth = self.tickwidth)
g.add(tickLine)
return g
class SmileyFace(_Symbol):
"""This draws a classic smiley face.
possible attributes:
'x', 'y', 'size', 'fillColor'
"""
def __init__(self):
_Symbol.__init__(self)
self.x = 0
self.y = 0
self.size = 100
self.fillColor = colors.yellow
self.strokeColor = colors.black
def draw(self):
# general widget bits
s = float(self.size) # abbreviate as we will use this a lot
g = shapes.Group()
# SmileyFace specific bits
g.add(shapes.Circle(cx=self.x+(s/2), cy=self.y+(s/2), r=s/2,
fillColor=self.fillColor, strokeColor=self.strokeColor,
strokeWidth=max(s/38.,self.strokeWidth)))
for i in (1,2):
g.add(shapes.Ellipse(self.x+(s/3)*i,self.y+(s/3)*2, s/30, s/10,
fillColor=self.strokeColor, strokeColor = self.strokeColor,
strokeWidth=max(s/38.,self.strokeWidth)))
# calculate a pointslist for the mouth
# THIS IS A HACK! - don't use if there is a 'shapes.Arc'
centerx=self.x+(s/2)
centery=self.y+(s/2)
radius=s/3
yradius = radius
xradius = radius
startangledegrees=200
endangledegrees=340
degreedelta = 1
pointslist = []
a = pointslist.append
from math import sin, cos, pi
degreestoradians = pi/180.0
radiansdelta = degreedelta*degreestoradians
startangle = startangledegrees*degreestoradians
endangle = endangledegrees*degreestoradians
while endangle<startangle:
endangle = endangle+2*pi
angle = startangle
while angle<endangle:
x = centerx + cos(angle)*radius
y = centery + sin(angle)*yradius
a(x); a(y)
angle = angle+radiansdelta
# make the mouth
smile = shapes.PolyLine(pointslist,
fillColor = self.strokeColor,
strokeColor = self.strokeColor,
strokeWidth = max(s/38.,self.strokeWidth))
g.add(smile)
return g
class StopSign(_Symbol):
"""This draws a (British) stop sign.
possible attributes:
'x', 'y', 'size'
"""
_attrMap = AttrMap(BASE=_Symbol,
stopColor = AttrMapValue(isColorOrNone,desc='color of the word stop'),
)
def __init__(self):
self.x = 0
self.y = 0
self.size = 100
self.strokeColor = colors.black
self.fillColor = colors.orangered
self.stopColor = colors.ghostwhite
def draw(self):
# general widget bits
s = float(self.size) # abbreviate as we will use this a lot
g = shapes.Group()
# stop-sign specific bits
athird=s/3
outerOctagon = shapes.Polygon(points=[self.x+athird, self.y,
self.x, self.y+athird,
self.x, self.y+(athird*2),
self.x+athird, self.y+s,
self.x+(athird*2), self.y+s,
self.x+s, self.y+(athird*2),
self.x+s, self.y+athird,
self.x+(athird*2), self.y],
strokeColor = self.strokeColor,
fillColor = None,
strokeWidth=1)
g.add(outerOctagon)
innerOctagon = shapes.Polygon(points=[self.x+athird+(s/75), self.y+(s/75),
self.x+(s/75), self.y+athird+(s/75),
self.x+(s/75), self.y+(athird*2)-(s/75),
self.x+athird+(s/75), self.y+s-(s/75),
self.x+(athird*2)-(s/75), (self.y+s)-(s/75),
(self.x+s)-(s/75), self.y+(athird*2)-(s/75),
(self.x+s)-(s/75), self.y+athird+(s/75),
self.x+(athird*2)-(s/75), self.y+(s/75)],
strokeColor = None,
fillColor = self.fillColor,
strokeWidth=0)
g.add(innerOctagon)
if self.stopColor:
g.add(shapes.String(self.x+(s*0.5),self.y+(s*0.4),
'STOP', fillColor=self.stopColor, textAnchor='middle',
fontSize=s/3, fontName="Helvetica-Bold"))
return g
class NoEntry(_Symbol):
"""This draws a (British) No Entry sign - a red circle with a white line on it.
possible attributes:
'x', 'y', 'size'
"""
_attrMap = AttrMap(BASE=_Symbol,
innerBarColor = AttrMapValue(isColorOrNone,desc='color of the inner bar'),
)
def __init__(self):
self.x = 0
self.y = 0
self.size = 100
self.strokeColor = colors.black
self.fillColor = colors.orangered
self.innerBarColor = colors.ghostwhite
def draw(self):
# general widget bits
s = float(self.size) # abbreviate as we will use this a lot
g = shapes.Group()
# no-entry-sign specific bits
if self.strokeColor:
g.add(shapes.Circle(cx = (self.x+(s/2)), cy = (self.y+(s/2)), r = s/2, fillColor = None, strokeColor = self.strokeColor, strokeWidth=1))
if self.fillColor:
g.add(shapes.Circle(cx = (self.x+(s/2)), cy =(self.y+(s/2)), r = ((s/2)-(s/50)), fillColor = self.fillColor, strokeColor = None, strokeWidth=0))
innerBarColor = self.innerBarColor
if innerBarColor:
g.add(shapes.Rect(self.x+(s*0.1), self.y+(s*0.4), width=s*0.8, height=s*0.2, fillColor = innerBarColor, strokeColor = innerBarColor, strokeLineCap = 1, strokeWidth = 0))
return g
class NotAllowed(_Symbol):
"""This draws a 'forbidden' roundel (as used in the no-smoking sign).
possible attributes:
'x', 'y', 'size'
"""
_attrMap = AttrMap(BASE=_Symbol,
)
def __init__(self):
self.x = 0
self.y = 0
self.size = 100
self.strokeColor = colors.red
self.fillColor = colors.white
def draw(self):
# general widget bits
s = float(self.size) # abbreviate as we will use this a lot
g = shapes.Group()
strokeColor = self.strokeColor
# not=allowed specific bits
outerCircle = shapes.Circle(cx = (self.x+(s/2)), cy = (self.y+(s/2)), r = (s/2)-(s/10), fillColor = self.fillColor, strokeColor = strokeColor, strokeWidth=s/10.)
g.add(outerCircle)
centerx=self.x+s
centery=self.y+(s/2)-(s/6)
radius=s-(s/6)
yradius = radius/2
xradius = radius/2
startangledegrees=100
endangledegrees=-80
degreedelta = 90
pointslist = []
a = pointslist.append
from math import sin, cos, pi
degreestoradians = pi/180.0
radiansdelta = degreedelta*degreestoradians
startangle = startangledegrees*degreestoradians
endangle = endangledegrees*degreestoradians
while endangle<startangle:
endangle = endangle+2*pi
angle = startangle
while angle<endangle:
x = centerx + cos(angle)*radius
y = centery + sin(angle)*yradius
a(x); a(y)
angle = angle+radiansdelta
crossbar = shapes.PolyLine(pointslist, fillColor = strokeColor, strokeColor = strokeColor, strokeWidth = s/10.)
g.add(crossbar)
return g
class NoSmoking(NotAllowed):
"""This draws a no-smoking sign.
possible attributes:
'x', 'y', 'size'
"""
def __init__(self):
NotAllowed.__init__(self)
def draw(self):
# general widget bits
s = float(self.size) # abbreviate as we will use this a lot
g = NotAllowed.draw(self)
# no-smoking-sign specific bits
newx = self.x+(s/2)-(s/3.5)
newy = self.y+(s/2)-(s/32)
cigarrette1 = shapes.Rect(x = newx, y = newy, width = (s/2), height =(s/16),
fillColor = colors.ghostwhite, strokeColor = colors.gray, strokeWidth=0)
newx=newx+(s/2)+(s/64)
g.insert(-1,cigarrette1)
cigarrette2 = shapes.Rect(x = newx, y = newy, width = (s/80), height =(s/16),
fillColor = colors.orangered, strokeColor = None, strokeWidth=0)
newx= newx+(s/35)
g.insert(-1,cigarrette2)
cigarrette3 = shapes.Rect(x = newx, y = newy, width = (s/80), height =(s/16),
fillColor = colors.orangered, strokeColor = None, strokeWidth=0)
newx= newx+(s/35)
g.insert(-1,cigarrette3)
cigarrette4 = shapes.Rect(x = newx, y = newy, width = (s/80), height =(s/16),
fillColor = colors.orangered, strokeColor = None, strokeWidth=0)
newx= newx+(s/35)
g.insert(-1,cigarrette4)
return g
class DangerSign(_Symbol):
"""This draws a 'danger' sign: a yellow box with a black exclamation point.
possible attributes:
'x', 'y', 'size', 'strokeColor', 'fillColor', 'strokeWidth'
"""
def __init__(self):
self.x = 0
self.y = 0
self.size = 100
self.strokeColor = colors.black
self.fillColor = colors.gold
self.strokeWidth = self.size*0.125
def draw(self):
# general widget bits
s = float(self.size) # abbreviate as we will use this a lot
g = shapes.Group()
ew = self.strokeWidth
ae = s*0.125 #(ae = 'an eighth')
# danger sign specific bits
ew = self.strokeWidth
ae = s*0.125 #(ae = 'an eighth')
outerTriangle = shapes.Polygon(points = [
self.x, self.y,
self.x+s, self.y,
self.x+(s/2),self.y+s],
fillColor = None,
strokeColor = self.strokeColor,
strokeWidth=0)
g.add(outerTriangle)
innerTriangle = shapes.Polygon(points = [
self.x+(s/50), self.y+(s/75),
(self.x+s)-(s/50), self.y+(s/75),
self.x+(s/2),(self.y+s)-(s/50)],
fillColor = self.fillColor,
strokeColor = None,
strokeWidth=0)
g.add(innerTriangle)
exmark = shapes.Polygon(points=[
((self.x+s/2)-ew/2), self.y+ae*2.5,
((self.x+s/2)+ew/2), self.y+ae*2.5,
((self.x+s/2)+((ew/2))+(ew/6)), self.y+ae*5.5,
((self.x+s/2)-((ew/2))-(ew/6)), self.y+ae*5.5],
fillColor = self.strokeColor,
strokeColor = None)
g.add(exmark)
exdot = shapes.Polygon(points=[
((self.x+s/2)-ew/2), self.y+ae,
((self.x+s/2)+ew/2), self.y+ae,
((self.x+s/2)+ew/2), self.y+ae*2,
((self.x+s/2)-ew/2), self.y+ae*2],
fillColor = self.strokeColor,
strokeColor = None)
g.add(exdot)
return g
class YesNo(_Symbol):
"""This widget draw a tickbox or crossbox depending on 'testValue'.
If this widget is supplied with a 'True' or 1 as a value for
testValue, it will use the tickbox widget. Otherwise, it will
produce a crossbox.
possible attributes:
'x', 'y', 'size', 'tickcolor', 'crosscolor', 'testValue'
"""
_attrMap = AttrMap(BASE=_Symbol,
tickcolor = AttrMapValue(isColor),
crosscolor = AttrMapValue(isColor),
testValue = AttrMapValue(isBoolean),
)
def __init__(self):
self.x = 0
self.y = 0
self.size = 100
self.tickcolor = colors.green
self.crosscolor = colors.red
self.testValue = 1
def draw(self):
if self.testValue:
yn=Tickbox()
yn.tickColor=self.tickcolor
else:
yn=Crossbox()
yn.crossColor=self.crosscolor
yn.x=self.x
yn.y=self.y
yn.size=self.size
yn.draw()
return yn
def demo(self):
D = shapes.Drawing(200, 100)
yn = YesNo()
yn.x = 15
yn.y = 25
yn.size = 70
yn.testValue = 0
yn.draw()
D.add(yn)
yn2 = YesNo()
yn2.x = 120
yn2.y = 25
yn2.size = 70
yn2.testValue = 1
yn2.draw()
D.add(yn2)
labelFontSize = 8
D.add(shapes.String(yn.x+(yn.size/2),(yn.y-(1.2*labelFontSize)),
'testValue=0', fillColor=colors.black, textAnchor='middle',
fontSize=labelFontSize))
D.add(shapes.String(yn2.x+(yn2.size/2),(yn2.y-(1.2*labelFontSize)),
'testValue=1', fillColor=colors.black, textAnchor='middle',
fontSize=labelFontSize))
labelFontSize = 10
D.add(shapes.String(yn.x+85,(yn.y-20),
self.__class__.__name__, fillColor=colors.black, textAnchor='middle',
fontSize=labelFontSize))
return D
class FloppyDisk(_Symbol):
"""This widget draws an icon of a floppy disk.
possible attributes:
'x', 'y', 'size', 'diskcolor'
"""
_attrMap = AttrMap(BASE=_Symbol,
diskColor = AttrMapValue(isColor),
)
def __init__(self):
self.x = 0
self.y = 0
self.size = 100
self.diskColor = colors.black
def draw(self):
# general widget bits
s = float(self.size) # abbreviate as we will use this a lot
g = shapes.Group()
# floppy disk specific bits
diskBody = shapes.Rect(x=self.x, y=self.y+(s/100), width=s, height=s-(s/100),
fillColor = self.diskColor,
strokeColor = None,
strokeWidth=0)
g.add(diskBody)
label = shapes.Rect(x=self.x+(s*0.1), y=(self.y+s)-(s*0.5), width=s*0.8, height=s*0.48,
fillColor = colors.whitesmoke,
strokeColor = None,
strokeWidth=0)
g.add(label)
labelsplash = shapes.Rect(x=self.x+(s*0.1), y=(self.y+s)-(s*0.1), width=s*0.8, height=s*0.08,
fillColor = colors.royalblue,
strokeColor = None,
strokeWidth=0)
g.add(labelsplash)
line1 = shapes.Line(x1=self.x+(s*0.15), y1=self.y+(0.6*s), x2=self.x+(s*0.85), y2=self.y+(0.6*s),
fillColor = colors.black,
strokeColor = colors.black,
strokeWidth=0)
g.add(line1)
line2 = shapes.Line(x1=self.x+(s*0.15), y1=self.y+(0.7*s), x2=self.x+(s*0.85), y2=self.y+(0.7*s),
fillColor = colors.black,
strokeColor = colors.black,
strokeWidth=0)
g.add(line2)
line3 = shapes.Line(x1=self.x+(s*0.15), y1=self.y+(0.8*s), x2=self.x+(s*0.85), y2=self.y+(0.8*s),
fillColor = colors.black,
strokeColor = colors.black,
strokeWidth=0)
g.add(line3)
metalcover = shapes.Rect(x=self.x+(s*0.2), y=(self.y), width=s*0.5, height=s*0.35,
fillColor = colors.silver,
strokeColor = None,
strokeWidth=0)
g.add(metalcover)
coverslot = shapes.Rect(x=self.x+(s*0.28), y=(self.y)+(s*0.035), width=s*0.12, height=s*0.28,
fillColor = self.diskColor,
strokeColor = None,
strokeWidth=0)
g.add(coverslot)
return g
class ArrowOne(_Symbol):
"""This widget draws an arrow (style one).
possible attributes:
'x', 'y', 'size', 'fillColor'
"""
def __init__(self):
self.x = 0
self.y = 0
self.size = 100
self.fillColor = colors.red
def draw(self):
# general widget bits
s = float(self.size) # abbreviate as we will use this a lot
g = shapes.Group()
# arrow specific bits
body = shapes.Rect(x=self.x, y=(self.y+(s/2))-(s/6), width=2*(s/3), height=(s/3),
fillColor = self.fillColor,
strokeColor = None,
strokeWidth=0)
g.add(body)
head = shapes.Polygon(points = [self.x+(3*(s/6)), (self.y+(s/2)),
self.x+(3*(s/6)), self.y+8*(s/10),
self.x+s, self.y+(s/2),
self.x+(3*(s/6)), self.y+2*(s/10)],
fillColor = self.fillColor,
strokeColor = None,
strokeWidth=0)
g.add(head)
return g
class ArrowTwo(ArrowOne):
"""This widget draws an arrow (style two).
possible attributes:
'x', 'y', 'size', 'fillColor'
"""
def __init__(self):
self.x = 0
self.y = 0
self.size = 100
self.fillColor = colors.blue
def draw(self):
# general widget bits
s = float(self.size) # abbreviate as we will use this a lot
g = shapes.Group()
# arrow specific bits
body = shapes.Rect(x=self.x, y=(self.y+(s/2))-(s/24), width=9*(s/10), height=(s/12),
fillColor = self.fillColor,
strokeColor = None,
strokeWidth=0)
g.add(body)
head = shapes.Polygon(points = [self.x+(2.5*(s/3)), (self.y+(s/2)),
self.x+(4*(s/6)), self.y+4*(s/6),
self.x+s, self.y+(s/2),
self.x+(4*(s/6)), self.y+2*(s/6)],
fillColor = self.fillColor,
strokeColor = None,
strokeWidth=0)
g.add(head)
return g
def test():
"""This function produces a pdf with examples of all the signs and symbols from this file.
"""
labelFontSize = 10
D = shapes.Drawing(450,650)
cb = Crossbox()
cb.x = 20
cb.y = 530
D.add(cb)
D.add(shapes.String(cb.x+(cb.size/2),(cb.y-(1.2*labelFontSize)),
cb.__class__.__name__, fillColor=colors.black, textAnchor='middle',
fontSize=labelFontSize))
tb = Tickbox()
tb.x = 170
tb.y = 530
D.add(tb)
D.add(shapes.String(tb.x+(tb.size/2),(tb.y-(1.2*labelFontSize)),
tb.__class__.__name__, fillColor=colors.black, textAnchor='middle',
fontSize=labelFontSize))
yn = YesNo()
yn.x = 320
yn.y = 530
D.add(yn)
tempstring = yn.__class__.__name__ + '*'
D.add(shapes.String(yn.x+(tb.size/2),(yn.y-(1.2*labelFontSize)),
tempstring, fillColor=colors.black, textAnchor='middle',
fontSize=labelFontSize))
D.add(shapes.String(130,6,
"(The 'YesNo' widget returns a tickbox if testvalue=1, and a crossbox if testvalue=0)", fillColor=colors.black, textAnchor='middle',
fontSize=labelFontSize*0.75))
ss = StopSign()
ss.x = 20
ss.y = 400
D.add(ss)
D.add(shapes.String(ss.x+(ss.size/2), ss.y-(1.2*labelFontSize),
ss.__class__.__name__, fillColor=colors.black, textAnchor='middle',
fontSize=labelFontSize))
ne = NoEntry()
ne.x = 170
ne.y = 400
D.add(ne)
D.add(shapes.String(ne.x+(ne.size/2),(ne.y-(1.2*labelFontSize)),
ne.__class__.__name__, fillColor=colors.black, textAnchor='middle',
fontSize=labelFontSize))
sf = SmileyFace()
sf.x = 320
sf.y = 400
D.add(sf)
D.add(shapes.String(sf.x+(sf.size/2),(sf.y-(1.2*labelFontSize)),
sf.__class__.__name__, fillColor=colors.black, textAnchor='middle',
fontSize=labelFontSize))
ds = DangerSign()
ds.x = 20
ds.y = 270
D.add(ds)
D.add(shapes.String(ds.x+(ds.size/2),(ds.y-(1.2*labelFontSize)),
ds.__class__.__name__, fillColor=colors.black, textAnchor='middle',
fontSize=labelFontSize))
na = NotAllowed()
na.x = 170
na.y = 270
D.add(na)
D.add(shapes.String(na.x+(na.size/2),(na.y-(1.2*labelFontSize)),
na.__class__.__name__, fillColor=colors.black, textAnchor='middle',
fontSize=labelFontSize))
ns = NoSmoking()
ns.x = 320
ns.y = 270
D.add(ns)
D.add(shapes.String(ns.x+(ns.size/2),(ns.y-(1.2*labelFontSize)),
ns.__class__.__name__, fillColor=colors.black, textAnchor='middle',
fontSize=labelFontSize))
a1 = ArrowOne()
a1.x = 20
a1.y = 140
D.add(a1)
D.add(shapes.String(a1.x+(a1.size/2),(a1.y-(1.2*labelFontSize)),
a1.__class__.__name__, fillColor=colors.black, textAnchor='middle',
fontSize=labelFontSize))
a2 = ArrowTwo()
a2.x = 170
a2.y = 140
D.add(a2)
D.add(shapes.String(a2.x+(a2.size/2),(a2.y-(1.2*labelFontSize)),
a2.__class__.__name__, fillColor=colors.black, textAnchor='middle',
fontSize=labelFontSize))
fd = FloppyDisk()
fd.x = 320
fd.y = 140
D.add(fd)
D.add(shapes.String(fd.x+(fd.size/2),(fd.y-(1.2*labelFontSize)),
fd.__class__.__name__, fillColor=colors.black, textAnchor='middle',
fontSize=labelFontSize))
renderPDF.drawToFile(D, 'signsandsymbols.pdf', 'signsandsymbols.py')
print 'wrote file: signsandsymbols.pdf'
if __name__=='__main__':
test()

View File

@ -1,155 +0,0 @@
"""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

@ -1,7 +0,0 @@
#! /usr/bin/python2.3
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/__init__.py
__version__=''' $Id$ '''
import os
RL_DEBUG = os.environ.has_key('RL_DEBUG')

View File

@ -1,44 +0,0 @@
#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$ '''
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

@ -1,132 +0,0 @@
#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$ '''
from UserDict import UserDict
from reportlab.lib.validators import isAnything, _SequenceTypes
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]!= '_':
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

@ -1,340 +0,0 @@
#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
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
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))
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, faceName, encName):
"""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()
fontName = faceName + '-' + encName
try:
font = pdfmetrics.getFont(fontName)
except KeyError:
font = cidfonts.CIDFont(faceName, encName)
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()

View File

@ -1,564 +0,0 @@
#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$ '''
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)
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)

View File

@ -1,443 +0,0 @@
#! /usr/bin/python2.3
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/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$ '''
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. """
#wbite 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'])
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

@ -1,11 +0,0 @@
#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$ '''
__doc__="""
holder for all reportlab's enumerated types
"""
TA_LEFT = 0
TA_CENTER = 1
TA_RIGHT = 2
TA_JUSTIFY = 4

View File

@ -1,81 +0,0 @@
#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

@ -1,89 +0,0 @@
#! /usr/bin/python2.3
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/fonts.py
__version__=''' $Id$ '''
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

@ -1,100 +0,0 @@
#! /usr/bin/python2.3
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/formatters.py
__version__=''' $Id$ '''
__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

@ -1,61 +0,0 @@
#! /usr/bin/python2.3
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/logger.py
__version__=''' $Id$ '''
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

@ -1,603 +0,0 @@
#! /usr/bin/python2.3
# 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$ '''
_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
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
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

@ -1,55 +0,0 @@
#! /usr/bin/python2.3
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/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$ '''
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

@ -1,348 +0,0 @@
#! /usr/bin/python2.3
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/randomtext.py
__version__=''' $Id$ '''
###############################################################################
# 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

@ -1,440 +0,0 @@
"""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 pyRXP
if pyRXP.version>='0.5':
def warnCB(s):
print s
pyRXP_parser = pyRXP.Parser(
ErrorOnValidityErrors=1,
NoNoDTDWarning=1,
ExpandCharacterEntities=0,
ExpandGeneralEntities=0,
warnCB = warnCB,
srcName='string input')
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)
else:
def parsexml(xmlText, oneOutermostTag=0,eoCB=None,entityReplacer=None):
'''eoCB is the entity open callback'''
def warnCB(s):
print s
flags = 0x0157e1ff | pyRXP.PARSER_FLAGS['ErrorOnValidityErrors']
for k in ('ExpandCharacterEntities','ExpandGeneralEntities'):
flags = flags & (~pyRXP.PARSER_FLAGS[k])
p = pyRXP.parse(xmlText,srcName='string input',flags=flags,warnCB=warnCB,eoCB=eoCB)
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

@ -1,284 +0,0 @@
#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$ '''
"""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

@ -1,38 +0,0 @@
#! /usr/bin/python2.3
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/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))

View File

@ -1,256 +0,0 @@
#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$ '''
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
}
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

@ -1,294 +0,0 @@
# 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$ '''
__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

@ -1,23 +0,0 @@
#! /usr/bin/python2.3
#Copyright ReportLab Europe Ltd. 2000-2004
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/units.py
__version__=''' $Id$ '''
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[:-2])*pica
return float(s)
except:
raise ValueError, "Can't convert '%s' to length" % s

View File

@ -1,776 +0,0 @@
#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/utils.py
__version__=''' $Id$ '''
import string, os, sys, imp
from types import *
from reportlab.lib.logger import warnOnce
SeqTypes = (ListType,TupleType)
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 = __loader__.archive
_archivepfx = _archive + os.sep
_archivedir = os.path.dirname(__loader__.archive)
_archivedirpfx = _archivedir + os.sep
_archivepfxlen = len(_archivepfx)
_archivedirpfxlen = len(_archivedirpfx)
def __startswith_rl(fn,
_archivepfx=os.path.normcase(_archivepfx),
_archivedirpfx=os.path.normcase(_archivedirpfx),
_archive=os.path.normcase(_archive),
_archivedir=os.path.normcase(_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 type(a[0]) in SeqTypes: 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),',','.')
_rl_tempdir=None
def get_rl_tempdir(*subdirs):
global _rl_tempdir
if _rl_tempdir is None:
import tempfile
_rl_tempdir = os.path.join(tempfile.gettempdir(),'ReportLab_tmp%s' % (sys.platform=='unix' and `os.getuid()` or ''))
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:
import tempfile
fn = tempfile.mktemp()
return os.path.join(get_rl_tempdir(),fn)
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 type(baseDir) not in SeqTypes:
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()
class ImageReader:
"Wraps up either PIL or Java to get data from bitmaps"
def __init__(self, fileName):
if not haveImages:
warnOnce('Imaging Library not available, unable to import bitmaps')
return
#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
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)
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)
else:
rgb = self._image.convert('RGB')
self._data = rgb.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"
return ImageReader.getImageData(imageFileName)
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','????'),
})
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 type(x) in SeqTypes: _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

View File

@ -1,260 +0,0 @@
#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$ '''
"""
This module contains some standard verifying functions which can be
used in an attribute map.
"""
import string, sys
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) is StringType
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 isInstanceOf(Validator):
def __init__(self,klass=None):
self._klass = klass
def test(self,x):
return isinstance(x,self._klass)
isBoolean = _isBoolean()
isString = _isString()
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')

View File

@ -1,770 +0,0 @@
# 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:
from _xmlplus.parsers import sgmlop
#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()

View File

@ -1,188 +0,0 @@
#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$ '''
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)

View File

@ -1,6 +0,0 @@
#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/pdfbase/__init__.py
__version__=''' $Id$ '''
__doc__="""
"""

View File

@ -1,452 +0,0 @@
#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/pdfbase/_cidfontdata.py
#$Header $
__version__=''' $Id$ '''
__doc__="""
This defines additional static data to support CID fonts.
Canned data is provided for the Japanese fonts supported by Adobe. We
can add Chinese, Korean and Vietnamese in due course. The data was
extracted by creating very simple postscript documents and running
through Distiller, then examining the resulting PDFs.
Each font is described as a big nested dictionary. This lets us keep
code out of the module altogether and avoid circular dependencies.
The encoding and font data are grouped by some standard 'language
prefixes':
chs = Chinese Simplified (mainland)
cht = Chinese Traditional (Taiwan)
kor = Korean
jpn = Japanese
"""
languages = ['jpn', 'kor', 'cht', 'chs']
#breaking down the lists let us check if something is present
#for a specific language
typeFaces_chs = ['STSong-Light'] # to do
typeFaces_cht = ['MSung-Light', 'MHei-Medium'] # to do
typeFaces_jpn = ['HeiseiMin-W3', 'HeiseiKakuGo-W5']
typeFaces_kor = ['HYSMyeongJo-Medium','HYGothic-Medium']
allowedTypeFaces = typeFaces_chs + typeFaces_cht + typeFaces_jpn + typeFaces_kor
encodings_jpn = [
# official encoding names, comments taken verbatim from PDF Spec
'83pv-RKSJ-H', #Macintosh, JIS X 0208 character set with KanjiTalk6
#extensions, Shift-JIS encoding, Script Manager code 1
'90ms-RKSJ-H', #Microsoft Code Page 932 (lfCharSet 0x80), JIS X 0208
#character set with NEC and IBM extensions
'90ms-RKSJ-V', #Vertical version of 90ms-RKSJ-H
'90msp-RKSJ-H', #Same as 90ms-RKSJ-H, but replaces half-width Latin
#characters with proportional forms
'90msp-RKSJ-V', #Vertical version of 90msp-RKSJ-H
'90pv-RKSJ-H', #Macintosh, JIS X 0208 character set with KanjiTalk7
#extensions, Shift-JIS encoding, Script Manager code 1
'Add-RKSJ-H', #JIS X 0208 character set with Fujitsu FMR extensions,
#Shift-JIS encoding
'Add-RKSJ-V', #Vertical version of Add-RKSJ-H
'EUC-H', #JIS X 0208 character set, EUC-JP encoding
'EUC-V', #Vertical version of EUC-H
'Ext-RKSJ-H', #JIS C 6226 (JIS78) character set with NEC extensions,
#Shift-JIS encoding
'Ext-RKSJ-V', #Vertical version of Ext-RKSJ-H
'H', #JIS X 0208 character set, ISO-2022-JP encoding,
'V', #Vertical version of H
'UniJIS-UCS2-H', #Unicode (UCS-2) encoding for the Adobe-Japan1 character
#collection
'UniJIS-UCS2-V', #Vertical version of UniJIS-UCS2-H
'UniJIS-UCS2-HW-H', #Same as UniJIS-UCS2-H, but replaces proportional Latin
#characters with half-width forms
'UniJIS-UCS2-HW-V' #Vertical version of UniJIS-UCS2-HW-H
]
encodings_kor = [
'KSC-EUC-H', # KS X 1001:1992 character set, EUC-KR encoding
'KSC-EUC-V', # Vertical version of KSC-EUC-H
'KSCms-UHC-H', # Microsoft Code Page 949 (lfCharSet 0x81), KS X 1001:1992
#character set plus 8,822 additional hangul, Unified Hangul
#Code (UHC) encoding
'KSCms-UHC-V', #Vertical version of KSCms-UHC-H
'KSCms-UHC-HW-H', #Same as KSCms-UHC-H, but replaces proportional Latin
# characters with halfwidth forms
'KSCms-UHC-HW-V', #Vertical version of KSCms-UHC-HW-H
'KSCpc-EUC-H', #Macintosh, KS X 1001:1992 character set with MacOS-KH
#extensions, Script Manager Code 3
'UniKS-UCS2-H', #Unicode (UCS-2) encoding for the Adobe-Korea1 character collection
'UniKS-UCS2-V' #Vertical version of UniKS-UCS2-H
]
encodings_chs = [
'GB-EUC-H', # Microsoft Code Page 936 (lfCharSet 0x86), GB 2312-80
# character set, EUC-CN encoding
'GB-EUC-V', # Vertical version of GB-EUC-H
'GBpc-EUC-H', # Macintosh, GB 2312-80 character set, EUC-CN encoding,
# Script Manager code 2
'GBpc-EUC-V', # Vertical version of GBpc-EUC-H
'GBK-EUC-H', # Microsoft Code Page 936 (lfCharSet 0x86), GBK character
# set, GBK encoding
'GBK-EUC-V', # Vertical version of GBK-EUC-V
'UniGB-UCS2-H', # Unicode (UCS-2) encoding for the Adobe-GB1
# character collection
'UniGB-UCS2-V' # Vertical version of UniGB-UCS2-H.
]
encodings_cht = [
'B5pc-H', # Macintosh, Big Five character set, Big Five encoding,
# Script Manager code 2
'B5pc-V', # Vertical version of B5pc-H
'ETen-B5-H', # Microsoft Code Page 950 (lfCharSet 0x88), Big Five
# character set with ETen extensions
'ETen-B5-V', # Vertical version of ETen-B5-H
'ETenms-B5-H', # Microsoft Code Page 950 (lfCharSet 0x88), Big Five
# character set with ETen extensions; this uses proportional
# forms for half-width Latin characters.
'ETenms-B5-V', # Vertical version of ETenms-B5-H
'CNS-EUC-H', # CNS 11643-1992 character set, EUC-TW encoding
'CNS-EUC-V', # Vertical version of CNS-EUC-H
'UniCNS-UCS2-H', # Unicode (UCS-2) encoding for the Adobe-CNS1
# character collection
'UniCNS-UCS2-V' # Vertical version of UniCNS-UCS2-H.
]
# the Identity encodings simply dump out all character
# in the font in the order they were defined.
allowedEncodings = (['Identity-H', 'Identity-V'] +
encodings_chs +
encodings_cht +
encodings_jpn +
encodings_kor
)
CIDFontInfo = {}
#statically describe the fonts in Adobe's Japanese Language Packs
CIDFontInfo['HeiseiMin-W3'] = {
'Type':'/Font',
'Subtype':'/Type0',
'Name': '/%(internalName)s' , #<-- the internal name
'BaseFont': '/HeiseiMin-W3',
'Encoding': '/%(encodings)s',
#there could be several descendant fonts if it is an old-style
#type 0 compound font. For CID fonts there is just one.
'DescendantFonts': [{
'Type':'/Font',
'Subtype':'/CIDFontType0',
'BaseFont':'/HeiseiMin-W3',
'FontDescriptor': {
'Type': '/FontDescriptor',
'Ascent': 723,
'CapHeight': 709,
'Descent': -241,
'Flags': 6,
'FontBBox': (-123, -257, 1001, 910),
'FontName': '/HeiseiMin-W3',
'ItalicAngle': 0,
'StemV': 69,
'XHeight': 450#,
# 'Style': {'Panose': '<010502020400000000000000>'}
},
'CIDSystemInfo': {
'Registry': '(Adobe)',
'Ordering': '(Japan1)',
'Supplement': 2
},
#default width is 1000 em units
'DW': 1000,
#widths of any which are not the default.
'W': [1, [250, 333, 408, 500],
5, [500, 833, 778, 180, 333],
10, [333, 500, 564, 250, 333, 250, 278, 500],
18, 26, 500, 27, 28, 278, 29, 31, 564,
32, [444, 921, 722, 667],
36, [667, 722, 611, 556, 722],
41, [722, 333, 389, 722, 611, 889, 722],
48, [722, 556, 722, 667, 556, 611, 722],
55, [722, 944, 722],
58, [722, 611, 333, 500, 333, 469, 500, 333,
444, 500, 444, 500, 444, 333, 500],
73, [500, 278],
75, [278, 500, 278, 778, 500], 80, 82, 500,
83, [333, 389, 278, 500],
87, [500, 722, 500],
90, [500, 444, 480, 200, 480, 333],
97, [278], 99, [200], 101, [333, 500], 103, [500, 167],
107, [500], 109, [500, 333], 111, [333, 556],
113, [556, 500], 117, [250], 119, [350, 333, 444],
123, [500], 126, [444, 333], 128, 137, 333,
138, [1000, 889, 276, 611, 722, 889, 310, 667, 278],
147, [278, 500, 722, 500, 564, 760, 564, 760],
157, 158, 300, 159, [500, 300, 750], 162, 163, 750,
164, 169, 722, 170, [667, 611], 172, 174, 611, 175,
178, 333, 179, 185, 722, 187, 191, 722, 192,
[556, 444], 194, 203, 444, 204, 207, 278, 208,
214, 500, 216, 222, 500,
223, [556, 722, 611, 500, 389, 980, 444],
231, [500], 323, [500], 325, [500],
327, 389, 500]
## 'W': (
## # starting at character ID 1, next n characters have the widths given.
## 1, (277,305,500,668,668,906,727,305,445,445,508,668,305,379,305,539),
## # all Characters from ID 17 to 26 are 668 em units wide
## 17, 26, 668,
## 27, (305, 305, 668, 668, 668, 566, 871, 727, 637, 652, 699, 574, 555,
## 676, 687, 242, 492, 664, 582, 789, 707, 734, 582, 734, 605, 605,
## 641, 668, 727, 945, 609, 609, 574, 445, 668, 445, 668, 668, 590,
## 555, 609, 547, 602, 574, 391, 609, 582, 234, 277, 539, 234, 895,
## 582, 605, 602, 602, 387, 508, 441, 582, 562, 781, 531, 570, 555,
## 449, 246, 449, 668),
## # these must be half width katakana and the like.
## 231, 632, 500
## )
}]# end list of descendant fonts
} #end HeiseiMin-W3
CIDFontInfo['HeiseiKakuGo-W5'] = {'Type':'/Font',
'Subtype':'/Type0',
'Name': '/%(internalName)s', #<-- the internal name
'BaseFont': '/HeiseiKakuGo-W5',
'Encoding': '/%(encodings)s',
'DescendantFonts': [{'Type':'/Font',
'Subtype':'/CIDFontType0',
'BaseFont':'/HeiseiKakuGo-W5',
'FontDescriptor': {
'Type': '/FontDescriptor',
'Ascent': 752,
'CapHeight': 737,
'Descent': -221,
'Flags': 4,
'FontBBox': [-92, -250, 1010, 922],
'FontName': '/HeiseKakuGo-W5',
'ItalicAngle': 0,
'StemH': 0,
'StemV': 114,
'XHeight': 553,
## 'Style': {'Panose': '<0801020b0600000000000000>'}
},
'CIDSystemInfo': {
'Registry': '(Adobe)',
'Ordering': '(Japan1)',
'Supplement': 2
},
'DW': 1000,
'W': (
1, (277,305,500,668,668,906,727,305,445,445,508,668,305,379,305,539),
17, 26, 668,
27, (305, 305, 668, 668, 668, 566, 871, 727, 637, 652, 699, 574, 555,
676, 687, 242, 492, 664, 582, 789, 707, 734, 582, 734, 605, 605,
641, 668, 727, 945, 609, 609, 574, 445, 668, 445, 668, 668, 590,
555, 609, 547, 602, 574, 391, 609, 582, 234, 277, 539, 234, 895,
582, 605, 602, 602, 387, 508, 441, 582, 562, 781, 531, 570, 555,
449, 246, 449, 668),
231, 632, 500
)
}] # end descendant fonts
}
CIDFontInfo['HYGothic-Medium'] = {'Type':'/Font',
'Subtype':'/Type0',
'Name': '/%(internalName)s', #<-- the internal name
'BaseFont': '/' + 'HYGothic-Medium',
'Encoding': '/%(encodings)s',
'DescendantFonts': [{'Type':'/Font',
'Subtype':'/CIDFontType0',
'BaseFont':'/'+'HYGothic-Medium',
'FontDescriptor': {
'Type': '/FontDescriptor',
'Ascent': 752,
'AvgWidth': -271,
'CapHeight': 737,
'Descent': -142,
'Flags': 6,
'FontBBox': [-6, -145, 1003, 880],
'FontName': '/'+'HYSMyeongJo-Medium',
'ItalicAngle': 0,
'Leading': 148,
'MaxWidth': 1000,
'MissingWidth': 500,
'StemH': 0,
'StemV': 58,
'XHeight': 553
},
'CIDSystemInfo': {
'Registry': '(Adobe)',
'Ordering': '(Korea1)',
'Supplement': 1
},
'DW': 1000,
'W': (1, 94, 500)
}] # end descendant fonts
}
CIDFontInfo['HYSMyeongJo-Medium'] = {'Type':'/Font',
'Subtype':'/Type0',
'Name': '/%(internalName)s', #<-- the internal name
'BaseFont': '/' + 'HYSMyeongJo-Medium',
'Encoding': '/%(encodings)s',
'DescendantFonts': [{'Type':'/Font',
'Subtype':'/CIDFontType2',
'BaseFont':'/'+'HYSMyeongJo-Medium',
'FontDescriptor': {
'Type': '/FontDescriptor',
'Ascent': 752,
'AvgWidth': 500,
'CapHeight': 737,
'Descent': -271,
'Flags': 6,
'FontBBox': [0, -148, 1001, 880],
'FontName': '/'+'HYSMyeongJo-Medium',
'ItalicAngle': 0,
'Leading': 148,
'MaxWidth': 1000,
'MissingWidth': 500,
'StemH': 91,
'StemV': 58,
'XHeight': 553
},
'CIDSystemInfo': {
'Registry': '(Adobe)',
'Ordering': '(Korea1)',
'Supplement': 1
},
'DW': 1000,
'W': [1, [333, 416],
3, [416, 833, 625, 916, 833, 250, 500],
10, 11, 500,
12, [833, 291, 833, 291, 375, 625],
18, 26, 625, 27, 28, 333, 29, 30, 833,
31, [916, 500, 1000, 791, 708],
36, [708, 750, 708, 666, 750, 791, 375,
500, 791, 666, 916, 791, 750, 666,
750, 708, 666, 791],
54, [791, 750, 1000, 708],
58, [708, 666, 500, 375, 500],
63, 64, 500,
65, [333, 541, 583, 541, 583],
70, [583, 375, 583],
73, [583, 291, 333, 583, 291, 875, 583],
80, 82, 583,
83, [458, 541, 375, 583],
87, [583, 833, 625],
90, [625, 500, 583], 93, 94, 583,
95, [750]
]
}] # end descendant fonts
}
#WARNING - not checked, just copied Korean to get some output
CIDFontInfo['STSong-Light'] = {'Type':'/Font',
'Subtype':'/Type0',
'Name': '/%(internalName)s', #<-- the internal name
'BaseFont': '/' + 'STSong-Light',
'Encoding': '/%(encodings)s',
'DescendantFonts': [{'Type':'/Font',
'Subtype':'/CIDFontType0',
'BaseFont':'/'+'STSong-Light',
'FontDescriptor': {
'Type': '/FontDescriptor',
'Ascent': 752,
'CapHeight': 737,
'Descent': -271,
'Flags': 6,
'FontBBox': [-25, -254, 1000, 880],
'FontName': '/'+'STSongStd-Light',
'ItalicAngle': 0,
'Leading': 148,
'MaxWidth': 1000,
'MissingWidth': 500,
'StemH': 91,
'StemV': 58,
'XHeight': 553
},
'CIDSystemInfo': {
'Registry': '(Adobe)',
'Ordering': '(GB1)',
'Supplement': 0
},
'DW': 1000,
'W': [1, [207, 270, 342, 467, 462, 797, 710, 239, 374],
10, [374, 423, 605, 238, 375, 238, 334, 462],
18, 26, 462, 27, 28, 238, 29, 31, 605,
32, [344, 748, 684, 560, 695, 739, 563, 511, 729,
793, 318, 312, 666, 526, 896, 758, 772, 544,
772, 628, 465, 607, 753, 711, 972, 647, 620,
607, 374, 333, 374, 606, 500, 239, 417, 503,
427, 529, 415, 264, 444, 518, 241, 230, 495,
228, 793, 527, 524],
81, [524, 504, 338, 336, 277, 517, 450, 652, 466,
452, 407, 370, 258, 370, 605]
]
}] # end descendant fonts
}
CIDFontInfo['MSung-Light'] = {'Type':'/Font',
'Subtype':'/Type0',
'Name': '/%(internalName)s', #<-- the internal name
'BaseFont': '/' + 'MSung-Light',
'Encoding': '/%(encodings)s',
'DescendantFonts': [{'Type':'/Font',
'Subtype':'/CIDFontType0',
'BaseFont':'/'+'MSung-Light',
'FontDescriptor': {
'Type': '/FontDescriptor',
'Ascent': 752,
'CapHeight': 737,
'Descent': -271,
'Flags': 6,
'FontBBox': [-160, -249, 1015, 888],
'FontName': '/'+'MSung-Light',
'ItalicAngle': 0,
'Leading': 148,
'MaxWidth': 1000,
'MissingWidth': 500,
'StemH': 45,
'StemV': 58,
'XHeight': 553
},
'CIDSystemInfo': {
'Registry': '(Adobe)',
'Ordering': '(CNS1)',
'Supplement': 1
},
'DW': 1000,
'W': [1, 2, 250, 3, [408, 668, 490, 875, 698, 250, 240],
10, [240, 417, 667, 250, 313, 250, 520, 500],
18, 26, 500, 27, 28, 250, 29, 31, 667,
32, [396, 921, 677, 615, 719, 760, 625, 552, 771,
802, 354],
43, [354, 781, 604, 927, 750, 823, 563, 823, 729,
542, 698, 771, 729, 948, 771, 677, 635, 344,
520, 344, 469, 500, 250, 469, 521, 427, 521,
438, 271, 469, 531, 250],
75, [250, 458, 240, 802, 531, 500, 521],
82, [521, 365, 333, 292, 521, 458, 677, 479, 458,
427, 480, 496, 480, 667]]
}] # end descendant fonts
}
#shift-jis saying 'This is Heisei-Minchou'
message1 = '\202\261\202\352\202\315\225\275\220\254\226\276\222\251\202\305\202\267\201B'
message2 = '\202\261\202\352\202\315\225\275\220\254\212p\203S\203V\203b\203N\202\305\202\267\201B'
##def pswidths(text):
## import string
## words = string.split(text)
## out = []
## for word in words:
## if word == '[':
## out.append(word)
## else:
## out.append(word + ',')
## return eval(string.join(out, ''))

File diff suppressed because it is too large Load Diff

View File

@ -1,399 +0,0 @@
#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/pdfbase/cidfonts.py
#$Header $
__version__=''' $Id$ '''
__doc__="""CID (Asian multi-byte) font support.
This defines classes to represent CID fonts. They know how to calculate
their own width and how to write themselves into PDF files."""
import os
from types import ListType, TupleType, DictType
from string import find, split, strip
import marshal
import md5
import time
import reportlab
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase._cidfontdata import allowedTypeFaces, allowedEncodings, CIDFontInfo
from reportlab.pdfgen.canvas import Canvas
from reportlab.pdfbase import pdfdoc
from reportlab.rl_config import CMapSearchPath
def findCMapFile(name):
"Returns full filename, or raises error"
for dirname in CMapSearchPath:
cmapfile = dirname + os.sep + name
if os.path.isfile(cmapfile):
return cmapfile
raise IOError, 'CMAP file for encodings "%s" not found!' % name
def structToPDF(structure):
"Converts deeply nested structure to PDFdoc dictionary/array objects"
if type(structure) is DictType:
newDict = {}
for k, v in structure.items():
newDict[k] = structToPDF(v)
return pdfdoc.PDFDictionary(newDict)
elif type(structure) in (ListType, TupleType):
newList = []
for elem in structure:
newList.append(structToPDF(elem))
return pdfdoc.PDFArray(newList)
else:
return structure
class CIDEncoding(pdfmetrics.Encoding):
"""Multi-byte encoding. These are loaded from CMAP files.
A CMAP file is like a mini-codec. It defines the correspondence
between code points in the (multi-byte) input data and Character
IDs. """
# aims to do similar things to Brian Hooper's CMap class,
# but I could not get it working and had to rewrite.
# also, we should really rearrange our current encoding
# into a SingleByteEncoding since many of its methods
# should not apply here.
def __init__(self, name, useCache=1):
self.name = name
self._mapFileHash = None
self._codeSpaceRanges = []
self._notDefRanges = []
self._cmap = {}
self.source = None
if useCache:
from reportlab.lib.utils import get_rl_tempdir
fontmapdir = get_rl_tempdir('FastCMAPS')
if os.path.isfile(fontmapdir + os.sep + name + '.fastmap'):
self.fastLoad(fontmapdir)
self.source = fontmapdir + os.sep + name + '.fastmap'
else:
self.parseCMAPFile(name)
self.source = 'CMAP: ' + name
self.fastSave(fontmapdir)
else:
self.parseCMAPFile(name)
def _hash(self, text):
hasher = md5.new()
hasher.update(text)
return hasher.digest()
def parseCMAPFile(self, name):
"""This is a tricky one as CMAP files are Postscript
ones. Some refer to others with a 'usecmap'
command"""
started = time.clock()
cmapfile = findCMapFile(name)
# this will CRAWL with the unicode encodings...
rawdata = open(cmapfile, 'r').read()
self._mapFileHash = self._hash(rawdata)
#if it contains the token 'usecmap', parse the other
#cmap file first....
usecmap_pos = find(rawdata, 'usecmap')
if usecmap_pos > -1:
#they tell us to look in another file
#for the code space ranges. The one
# to use will be the previous word.
chunk = rawdata[0:usecmap_pos]
words = split(chunk)
otherCMAPName = words[-1]
#print 'referred to another CMAP %s' % otherCMAPName
self.parseCMAPFile(otherCMAPName)
# now continue parsing this, as it may
# override some settings
words = split(rawdata)
while words <> []:
if words[0] == 'begincodespacerange':
words = words[1:]
while words[0] <> 'endcodespacerange':
strStart, strEnd, words = words[0], words[1], words[2:]
start = int(strStart[1:-1], 16)
end = int(strEnd[1:-1], 16)
self._codeSpaceRanges.append((start, end),)
elif words[0] == 'beginnotdefrange':
words = words[1:]
while words[0] <> 'endnotdefrange':
strStart, strEnd, strValue = words[0:3]
start = int(strStart[1:-1], 16)
end = int(strEnd[1:-1], 16)
value = int(strValue)
self._notDefRanges.append((start, end, value),)
words = words[3:]
elif words[0] == 'begincidrange':
words = words[1:]
while words[0] <> 'endcidrange':
strStart, strEnd, strValue = words[0:3]
start = int(strStart[1:-1], 16)
end = int(strEnd[1:-1], 16)
value = int(strValue)
# this means that 'start' corresponds to 'value',
# start+1 corresponds to value+1 and so on up
# to end
offset = 0
while start + offset <= end:
self._cmap[start + offset] = value + offset
offset = offset + 1
words = words[3:]
else:
words = words[1:]
finished = time.clock()
print 'parsed CMAP %s in %0.4f seconds' % (self.name, finished - started)
def translate(self, text):
"Convert a string into a list of CIDs"
output = []
cmap = self._cmap
lastChar = ''
for char in text:
if lastChar <> '':
#print 'convert character pair "%s"' % (lastChar + char)
num = ord(lastChar) * 256 + ord(char)
else:
#print 'convert character "%s"' % char
num = ord(char)
lastChar = char
found = 0
for low, high in self._codeSpaceRanges:
if low < num < high:
try:
cid = cmap[num]
#print '%d -> %d' % (num, cid)
except KeyError:
#not defined. Try to find the appropriate
# notdef character, or failing that return
# zero
cid = 0
for low2, high2, notdef in self._notDefRanges:
if low2 < num < high2:
cid = notdef
break
output.append(cid)
found = 1
break
if found:
lastChar = ''
else:
lastChar = char
return output
def fastSave(self, directory):
f = open(os.path.join(directory, self.name + '.fastmap'), 'wb')
marshal.dump(self._mapFileHash, f)
marshal.dump(self._codeSpaceRanges, f)
marshal.dump(self._notDefRanges, f)
marshal.dump(self._cmap, f)
f.close()
def fastLoad(self, directory):
started = time.clock()
f = open(os.path.join(directory, self.name + '.fastmap'), 'rb')
self._mapFileHash = marshal.load(f)
self._codeSpaceRanges = marshal.load(f)
self._notDefRanges = marshal.load(f)
self._cmap = marshal.load(f)
f.close()
finished = time.clock()
#print 'loaded %s in %0.4f seconds' % (self.name, finished - started)
class CIDTypeFace(pdfmetrics.TypeFace):
"""Multi-byte type face.
Conceptually similar to a single byte typeface,
but the glyphs are identified by a numeric Character
ID (CID) and not a glyph name. """
def __init__(self, name):
"""Initialised from one of the canned dictionaries in allowedEncodings
Or rather, it will be shortly..."""
pdfmetrics.TypeFace.__init__(self, name)
self._extractDictInfo(name)
def _extractDictInfo(self, name):
try:
fontDict = CIDFontInfo[name]
except KeyError:
raise KeyError, ("Unable to find information on CID typeface '%s'" % name +
"Only the following font names work:" + repr(allowedTypeFaces)
)
descFont = fontDict['DescendantFonts'][0]
self.ascent = descFont['FontDescriptor']['Ascent']
self.descent = descFont['FontDescriptor']['Descent']
self._defaultWidth = descFont['DW']
self._explicitWidths = self._expandWidths(descFont['W'])
# should really support self.glyphWidths, self.glyphNames
# but not done yet.
def _expandWidths(self, compactWidthArray):
"""Expands Adobe nested list structure to get a dictionary of widths.
Here is an example of such a structure.
(
# starting at character ID 1, next n characters have the widths given.
1, (277,305,500,668,668,906,727,305,445,445,508,668,305,379,305,539),
# all Characters from ID 17 to 26 are 668 em units wide
17, 26, 668,
27, (305, 305, 668, 668, 668, 566, 871, 727, 637, 652, 699, 574, 555,
676, 687, 242, 492, 664, 582, 789, 707, 734, 582, 734, 605, 605,
641, 668, 727, 945, 609, 609, 574, 445, 668, 445, 668, 668, 590,
555, 609, 547, 602, 574, 391, 609, 582, 234, 277, 539, 234, 895,
582, 605, 602, 602, 387, 508, 441, 582, 562, 781, 531, 570, 555,
449, 246, 449, 668),
# these must be half width katakana and the like.
231, 632, 500
)
"""
data = compactWidthArray[:]
widths = {}
while data:
start, data = data[0], data[1:]
if type(data[0]) in (ListType, TupleType):
items, data = data[0], data[1:]
for offset in range(len(items)):
widths[start + offset] = items[offset]
else:
end, width, data = data[0], data[1], data[2:]
for idx in range(start, end+1):
widths[idx] = width
return widths
def getCharWidth(self, characterId):
return self._explicitWidths.get(characterId, self._defaultWidth)
class CIDFont(pdfmetrics.Font):
"Represents a built-in multi-byte font"
def __init__(self, face, encoding):
self._multiByte = 1
assert face in allowedTypeFaces, "TypeFace '%s' not supported! Use any of these instead: %s" % (face, allowedTypeFaces)
self.faceName = face
#should cache in registry...
self.face = CIDTypeFace(face)
assert encoding in allowedEncodings, "Encoding '%s' not supported! Use any of these instead: %s" % (encoding, allowedEncodings)
self.encodingName = encoding
self.encoding = CIDEncoding(encoding)
#legacy hack doing quick cut and paste.
self.fontName = self.faceName + '-' + self.encodingName
self.name = self.fontName
# need to know if it is vertical or horizontal
self.isVertical = (self.encodingName[-1] == 'V')
def stringWidth(self, text, size):
cidlist = self.encoding.translate(text)
if self.isVertical:
#this part is "not checked!" but seems to work.
#assume each is 1000 ems high
return len(cidlist) * size
else:
w = 0
for cid in cidlist:
w = w + self.face.getCharWidth(cid)
return 0.001 * w * size
def addObjects(self, doc):
"""The explicit code in addMinchoObjects and addGothicObjects
will be replaced by something that pulls the data from
_cidfontdata.py in the next few days."""
internalName = 'F' + repr(len(doc.fontMapping)+1)
bigDict = CIDFontInfo[self.face.name]
bigDict['Name'] = '/' + internalName
bigDict['Encoding'] = '/' + self.encodingName
#convert to PDF dictionary/array objects
cidObj = structToPDF(bigDict)
# link into document, and add to font map
r = doc.Reference(cidObj, internalName)
fontDict = doc.idToObject['BasicFonts'].dict
fontDict[internalName] = r
doc.fontMapping[self.name] = '/' + internalName
def precalculate(cmapdir):
# crunches through all, making 'fastmap' files
import os
files = os.listdir(cmapdir)
for file in files:
if os.path.isfile(cmapdir + os.sep + self.name + '.fastmap'):
continue
try:
enc = CIDEncoding(file)
except:
print 'cannot parse %s, skipping' % enc
continue
enc.fastSave(cmapdir)
print 'saved %s.fastmap' % file
def test():
# only works if you have cirrect encodings on your box!
c = Canvas('test_japanese.pdf')
c.setFont('Helvetica', 30)
c.drawString(100,700, 'Japanese Font Support')
pdfmetrics.registerFont(CIDFont('HeiseiMin-W3','90ms-RKSJ-H'))
pdfmetrics.registerFont(CIDFont('HeiseiKakuGo-W5','90ms-RKSJ-H'))
# the two typefaces
c.setFont('HeiseiMin-W3-90ms-RKSJ-H', 16)
# this says "This is HeiseiMincho" in shift-JIS. Not all our readers
# have a Japanese PC, so I escaped it. On a Japanese-capable
# system, print the string to see Kanji
message1 = '\202\261\202\352\202\315\225\275\220\254\226\276\222\251\202\305\202\267\201B'
c.drawString(100, 675, message1)
c.save()
print 'saved test_japanese.pdf'
## print 'CMAP_DIR = ', CMAP_DIR
## tf1 = CIDTypeFace('HeiseiMin-W3')
## print 'ascent = ',tf1.ascent
## print 'descent = ',tf1.descent
## for cid in [1,2,3,4,5,18,19,28,231,1742]:
## print 'width of cid %d = %d' % (cid, tf1.getCharWidth(cid))
encName = '90ms-RKSJ-H'
enc = CIDEncoding(encName)
print message1, '->', enc.translate(message1)
f = CIDFont('HeiseiMin-W3','90ms-RKSJ-H')
print 'width = %0.2f' % f.stringWidth(message1, 10)
#testing all encodings
## import time
## started = time.time()
## import glob
## for encName in _cidfontdata.allowedEncodings:
## #encName = '90ms-RKSJ-H'
## enc = CIDEncoding(encName)
## print 'encoding %s:' % encName
## print ' codeSpaceRanges = %s' % enc._codeSpaceRanges
## print ' notDefRanges = %s' % enc._notDefRanges
## print ' mapping size = %d' % len(enc._cmap)
## finished = time.time()
## print 'constructed all encodings in %0.2f seconds' % (finished - started)
if __name__=='__main__':
test()

File diff suppressed because it is too large Load Diff

View File

@ -1,632 +0,0 @@
"""Support for Acrobat Forms in ReportLab documents
This module is somewhat experimental at this time.
Includes basic support for
textfields,
select fields (drop down lists), and
check buttons.
The public interface consists of functions at the moment.
At some later date these operations may be made into canvas
methods. (comments?)
The ...Absolute(...) functions position the fields with respect
to the absolute canvas coordinate space -- that is, they do not
respect any coordinate transforms in effect for the canvas.
The ...Relative(...) functions position the ONLY THE LOWER LEFT
CORNER of the field using the coordinate transform in effect for
the canvas. THIS WILL ONLY WORK CORRECTLY FOR TRANSLATED COORDINATES
-- THE SHAPE, SIZE, FONTSIZE, AND ORIENTATION OF THE FIELD WILL NOT BE EFFECTED
BY SCALING, ROTATION, SKEWING OR OTHER NON-TRANSLATION COORDINATE
TRANSFORMS.
Please note that all field names (titles) in a given document must be unique.
Textfields and select fields only support the "base 14" canvas fonts
at this time.
See individual function docstrings below for more information.
The function test1(...) generates a simple test file.
THIS CONTRIBUTION WAS COMMISSIONED BY REPORTLAB USERS
WHO WISH TO REMAIN ANONYMOUS.
"""
### NOTE: MAKE THE STRING FORMATS DYNAMIC IN PATTERNS TO SUPPORT ENCRYPTION XXXX
import string
from reportlab.pdfbase.pdfdoc import LINEEND, PDFString, PDFStream, PDFDictionary, PDFName
#==========================public interfaces
def textFieldAbsolute(canvas, title, x, y, width, height, value="", maxlen=1000000, multiline=0):
"""Place a text field on the current page
with name title at ABSOLUTE position (x,y) with
dimensions (width, height), using value as the default value and
maxlen as the maximum permissible length. If multiline is set make
it a multiline field.
"""
theform = getForm(canvas)
return theform.textField(canvas, title, x, y, x+width, y+height, value, maxlen, multiline)
def textFieldRelative(canvas, title, xR, yR, width, height, value="", maxlen=1000000, multiline=0):
"same as textFieldAbsolute except the x and y are relative to the canvas coordinate transform"
(xA, yA) = canvas.absolutePosition(xR,yR)
return textFieldAbsolute(canvas, title, xA, yA, width, height, value, maxlen, multiline)
def buttonFieldAbsolute(canvas, title, value, x, y):
"""Place a check button field on the current page
with name title and default value value (one of "Yes" or "Off")
at ABSOLUTE position (x,y).
"""
theform = getForm(canvas)
return theform.buttonField(canvas, title, value, x, y)
def buttonFieldRelative(canvas, title, value, xR, yR):
"same as buttonFieldAbsolute except the x and y are relative to the canvas coordinate transform"
(xA, yA) = canvas.absolutePosition(xR,yR)
return buttonFieldAbsolute(canvas, title, value, xA, yA)
def selectFieldAbsolute(canvas, title, value, options, x, y, width, height):
"""Place a select field (drop down list) on the current page
with name title and
with options listed in the sequence options
default value value (must be one of options)
at ABSOLUTE position (x,y) with dimensions (width, height)."""
theform = getForm(canvas)
theform.selectField(canvas, title, value, options, x, y, x+width, y+height)
def selectFieldRelative(canvas, title, value, options, xR, yR, width, height):
"same as textFieldAbsolute except the x and y are relative to the canvas coordinate transform"
(xA, yA) = canvas.absolutePosition(xR,yR)
return selectFieldAbsolute(canvas, title, value, options, xA, yA, width, height)
def test1():
from reportlab.pdfgen import canvas
fn = "formtest1.pdf"
c = canvas.Canvas(fn)
# first page
c.setFont("Courier", 10)
c.drawString(100, 500, "hello world")
textFieldAbsolute(c, "fieldA", 100, 600, 100, 20, "default value")
textFieldAbsolute(c, "fieldB", 100, 300, 100, 50, "another default value", multiline=1)
selectFieldAbsolute(c, "fieldC", "France", ["Canada", "France", "China"], 100, 200, 100, 20)
c.rect(100, 600, 100, 20)
buttonFieldAbsolute(c, "field2", "Yes", 100, 700)
c.rect(100, 700, 20, 20)
buttonFieldAbsolute(c, "field3", "Off", 100, 800)
c.rect(100, 800, 20, 20)
# second page
c.showPage()
c.setFont("Helvetica", 7)
c.translate(50, 20)
c.drawString(100, 500, "hello world")
textFieldRelative(c, "fieldA_1", 100, 600, 100, 20, "default value 2")
c.setStrokeColorRGB(1,0,0)
c.setFillColorRGB(0,1,0.5)
textFieldRelative(c, "fieldB_1", 100, 300, 100, 50, "another default value 2", multiline=1)
selectFieldRelative(c, "fieldC_1", "France 1", ["Canada 0", "France 1", "China 2"], 100, 200, 100, 20)
c.rect(100, 600, 100, 20)
buttonFieldRelative(c, "field2_1", "Yes", 100, 700)
c.rect(100, 700, 20, 20)
buttonFieldRelative(c, "field3_1", "Off", 100, 800)
c.rect(100, 800, 20, 20)
c.save()
print "wrote", fn
#==========================end of public interfaces
from pdfpattern import PDFPattern
def getForm(canvas):
"get form from canvas, create the form if needed"
try:
return canvas.AcroForm
except AttributeError:
theform = canvas.AcroForm = AcroForm()
# install the form in the document
d = canvas._doc
cat = d._catalog
cat.AcroForm = theform
return theform
class AcroForm:
def __init__(self):
self.fields = []
def textField(self, canvas, title, xmin, ymin, xmax, ymax, value="", maxlen=1000000, multiline=0):
# determine the page ref
doc = canvas._doc
page = doc.thisPageRef()
# determine text info
(R,G,B) = canvas._fillColorRGB
#print "rgb", (R,G,B)
font = canvas. _fontname
fontsize = canvas. _fontsize
field = TextField(title, value, xmin, ymin, xmax, ymax, page, maxlen,
font, fontsize, R, G, B, multiline)
self.fields.append(field)
canvas._addAnnotation(field)
def selectField(self, canvas, title, value, options, xmin, ymin, xmax, ymax):
# determine the page ref
doc = canvas._doc
page = doc.thisPageRef()
# determine text info
(R,G,B) = canvas._fillColorRGB
#print "rgb", (R,G,B)
font = canvas. _fontname
fontsize = canvas. _fontsize
field = SelectField(title, value, options, xmin, ymin, xmax, ymax, page,
font=font, fontsize=fontsize, R=R, G=G, B=B)
self.fields.append(field)
canvas._addAnnotation(field)
def buttonField(self, canvas, title, value, xmin, ymin):
# determine the page ref
doc = canvas._doc
page = doc.thisPageRef()
field = ButtonField(title, value, xmin, ymin, page)
self.fields.append(field)
canvas._addAnnotation(field)
def format(self, document):
from reportlab.pdfbase.pdfdoc import PDFArray
proxy = PDFPattern(FormPattern, Resources=GLOBALRESOURCES, fields=PDFArray(self.fields))
return proxy.format(document)
FormPattern = [
'<<', LINEEND,
' /NeedAppearances true ', LINEEND,
' /DA ', PDFString('/Helv 0 Tf 0 g '), LINEEND,
' /DR ', LINEEND,
["Resources"],
' /Fields ', LINEEND,
["fields"],
'>>'
]
def FormFontsDictionary():
from reportlab.pdfbase.pdfdoc import PDFDictionary
fontsdictionary = PDFDictionary()
fontsdictionary.__RefOnly__ = 1
for (fullname, shortname) in FORMFONTNAMES.items():
fontsdictionary[shortname] = FormFont(fullname, shortname)
fontsdictionary["ZaDb"] = ZADB
return fontsdictionary
def FormResources():
return PDFPattern(FormResourcesDictionaryPattern,
Encoding=ENCODING, Font=GLOBALFONTSDICTIONARY)
ZaDbPattern = [
' <<'
' /BaseFont'
' /ZapfDingbats'
' /Name'
' /ZaDb'
' /Subtype'
' /Type1'
' /Type'
' /Font'
'>>']
ZADB = PDFPattern(ZaDbPattern)
FormResourcesDictionaryPattern = [
'<<',
' /Encoding ',
["Encoding"], LINEEND,
' /Font ',
["Font"], LINEEND,
'>>'
]
FORMFONTNAMES = {
"Helvetica": "Helv",
"Helvetica-Bold": "HeBo",
'Courier': "Cour",
'Courier-Bold': "CoBo",
'Courier-Oblique': "CoOb",
'Courier-BoldOblique': "CoBO",
'Helvetica-Oblique': "HeOb",
'Helvetica-BoldOblique': "HeBO",
'Times-Roman': "Time",
'Times-Bold': "TiBo",
'Times-Italic': "TiIt",
'Times-BoldItalic': "TiBI",
}
EncodingPattern = [
'<<',
' /PDFDocEncoding ',
["PDFDocEncoding"], LINEEND,
'>>',
]
PDFDocEncodingPattern = [
'<<'
' /Differences'
' ['
' 24'
' /breve'
' /caron'
' /circumflex'
' /dotaccent'
' /hungarumlaut'
' /ogonek'
' /ring'
' /tilde'
' 39'
' /quotesingle'
' 96'
' /grave'
' 128'
' /bullet'
' /dagger'
' /daggerdbl'
' /ellipsis'
' /emdash'
' /endash'
' /florin'
' /fraction'
' /guilsinglleft'
' /guilsinglright'
' /minus'
' /perthousand'
' /quotedblbase'
' /quotedblleft'
' /quotedblright'
' /quoteleft'
' /quoteright'
' /quotesinglbase'
' /trademark'
' /fi'
' /fl'
' /Lslash'
' /OE'
' /Scaron'
' /Ydieresis'
' /Zcaron'
' /dotlessi'
' /lslash'
' /oe'
' /scaron'
' /zcaron'
' 160'
' /Euro'
' 164'
' /currency'
' 166'
' /brokenbar'
' 168'
' /dieresis'
' /copyright'
' /ordfeminine'
' 172'
' /logicalnot'
' /.notdef'
' /registered'
' /macron'
' /degree'
' /plusminus'
' /twosuperior'
' /threesuperior'
' /acute'
' /mu'
' 183'
' /periodcentered'
' /cedilla'
' /onesuperior'
' /ordmasculine'
' 188'
' /onequarter'
' /onehalf'
' /threequarters'
' 192'
' /Agrave'
' /Aacute'
' /Acircumflex'
' /Atilde'
' /Adieresis'
' /Aring'
' /AE'
' /Ccedilla'
' /Egrave'
' /Eacute'
' /Ecircumflex'
' /Edieresis'
' /Igrave'
' /Iacute'
' /Icircumflex'
' /Idieresis'
' /Eth'
' /Ntilde'
' /Ograve'
' /Oacute'
' /Ocircumflex'
' /Otilde'
' /Odieresis'
' /multiply'
' /Oslash'
' /Ugrave'
' /Uacute'
' /Ucircumflex'
' /Udieresis'
' /Yacute'
' /Thorn'
' /germandbls'
' /agrave'
' /aacute'
' /acircumflex'
' /atilde'
' /adieresis'
' /aring'
' /ae'
' /ccedilla'
' /egrave'
' /eacute'
' /ecircumflex'
' /edieresis'
' /igrave'
' /iacute'
' /icircumflex'
' /idieresis'
' /eth'
' /ntilde'
' /ograve'
' /oacute'
' /ocircumflex'
' /otilde'
' /odieresis'
' /divide'
' /oslash'
' /ugrave'
' /uacute'
' /ucircumflex'
' /udieresis'
' /yacute'
' /thorn'
' /ydieresis'
' ]'
' /Type'
' /Encoding'
'>>']
# global constant
PDFDOCENC = PDFPattern(PDFDocEncodingPattern)
# global constant
ENCODING = PDFPattern(EncodingPattern, PDFDocEncoding=PDFDOCENC)
def FormFont(BaseFont, Name):
from reportlab.pdfbase.pdfdoc import PDFName
return PDFPattern(FormFontPattern, BaseFont=PDFName(BaseFont), Name=PDFName(Name), Encoding=PDFDOCENC)
FormFontPattern = [
'<<',
' /BaseFont ',
["BaseFont"], LINEEND,
' /Encoding ',
["Encoding"], LINEEND,
' /Name ',
["Name"], LINEEND,
' /Subtype '
' /Type1 '
' /Type '
' /Font '
'>>' ]
# global constants
GLOBALFONTSDICTIONARY = FormFontsDictionary()
GLOBALRESOURCES = FormResources()
def TextField(title, value, xmin, ymin, xmax, ymax, page,
maxlen=1000000, font="Helvetica-Bold", fontsize=9, R=0, G=0, B=0.627, multiline=0):
from reportlab.pdfbase.pdfdoc import PDFString, PDFName
Flags = 0
if multiline:
Flags = Flags | (1<<12) # bit 13 is at position 12 :)
fontname = FORMFONTNAMES[font]
return PDFPattern(TextFieldPattern,
value=PDFString(value), maxlen=maxlen, page=page,
title=PDFString(title),
xmin=xmin, ymin=ymin, xmax=xmax, ymax=ymax,
fontname=PDFName(fontname), fontsize=fontsize, R=R, G=G, B=B, Flags=Flags)
TextFieldPattern = [
'<<'
' /DA'
' (', ["fontname"],' ',["fontsize"],' Tf ',["R"],' ',["G"],' ',["B"],' rg)'
' /DV ',
["value"], LINEEND,
' /F 4 /FT /Tx'
'/MK << /BC [ 0 0 0 ] >>'
' /MaxLen ',
["maxlen"], LINEEND,
' /P ',
["page"], LINEEND,
' /Rect '
' [', ["xmin"], " ", ["ymin"], " ", ["xmax"], " ", ["ymax"], ' ]'
'/Subtype /Widget'
' /T ',
["title"], LINEEND,
' /Type'
' /Annot'
' /V ',
["value"], LINEEND,
' /Ff ',
["Flags"],LINEEND,
'>>']
def SelectField(title, value, options, xmin, ymin, xmax, ymax, page,
font="Helvetica-Bold", fontsize=9, R=0, G=0, B=0.627):
#print "ARGS", (title, value, options, xmin, ymin, xmax, ymax, page, font, fontsize, R, G, B)
from reportlab.pdfbase.pdfdoc import PDFString, PDFName, PDFArray
if value not in options:
raise ValueError, "value %s must be one of options %s" % (repr(value), repr(options))
fontname = FORMFONTNAMES[font]
optionstrings = map(PDFString, options)
optionarray = PDFArray(optionstrings)
return PDFPattern(SelectFieldPattern,
Options=optionarray,
Selected=PDFString(value), Page=page,
Name=PDFString(title),
xmin=xmin, ymin=ymin, xmax=xmax, ymax=ymax,
fontname=PDFName(fontname), fontsize=fontsize, R=R, G=G, B=B)
SelectFieldPattern = [
'<< % a select list',LINEEND,
' /DA ',
' (', ["fontname"],' ',["fontsize"],' Tf ',["R"],' ',["G"],' ',["B"],' rg)',LINEEND,
#' (/Helv 12 Tf 0 g)',LINEEND,
' /DV ',
["Selected"],LINEEND,
' /F ',
' 4',LINEEND,
' /FT ',
' /Ch',LINEEND,
' /MK ',
' <<',
' /BC',
' [',
' 0',
' 0',
' 0',
' ]',
' /BG',
' [',
' 1',
' 1',
' 1',
' ]',
' >>',LINEEND,
' /Opt ',
["Options"],LINEEND,
' /P ',
["Page"],LINEEND,
'/Rect',
' [',["xmin"], " ", ["ymin"], " ", ["xmax"], " ", ["ymax"],
' ] ',LINEEND,
'/Subtype',
' /Widget',LINEEND,
' /T ',
["Name"],LINEEND,
' /Type ',
' /Annot',
' /V ',
["Selected"],LINEEND,
'>>']
def ButtonField(title, value, xmin, ymin, page):
if value not in ("Yes", "Off"):
raise ValueError, "button value must be 'Yes' or 'Off': "+repr(value)
(dx, dy) = (16.77036, 14.90698)
return PDFPattern(ButtonFieldPattern,
Name=PDFString(title),
xmin=xmin, ymin=ymin, xmax=xmin+dx, ymax=ymin+dy,
Hide=HIDE,
APDOff=APDOFF,
APDYes=APDYES,
APNYes=APNYES,
Value=PDFName(value),
Page=page)
ButtonFieldPattern = ['<< ',
'/AA',
' <<',
' /D ',
["Hide"], LINEEND,
#' %(imported.18.0)s',
' >> ',
'/AP ',
' <<',
' /D',
' <<',
' /Off ',
#' %(imported.40.0)s',
["APDOff"], LINEEND,
' /Yes ',
#' %(imported.39.0)s',
["APDYes"], LINEEND,
' >>', LINEEND,
' /N',
' << ',
' /Yes ',
#' %(imported.38.0)s',
["APNYes"], LINEEND,
' >>',
' >>', LINEEND,
' /AS ',
["Value"], LINEEND,
' /DA ',
PDFString('/ZaDb 0 Tf 0 g'), LINEEND,
'/DV ',
["Value"], LINEEND,
'/F ',
' 4 ',
'/FT ',
' /Btn ',
'/H ',
' /T ',
'/MK ',
' <<',
' /AC (\\376\\377)',
#PDFString('\376\377'),
' /CA ',
PDFString('4'),
' /RC ',
PDFString('\376\377'),
' >> ',LINEEND,
'/P ',
["Page"], LINEEND,
'/Rect',
' [',["xmin"], " ", ["ymin"], " ", ["xmax"], " ", ["ymax"],
' ] ',LINEEND,
'/Subtype',
' /Widget ',
'/T ',
["Name"], LINEEND,
'/Type',
' /Annot ',
'/V ',
["Value"], LINEEND,
' >>']
HIDE = PDFPattern([
'<< '
'/S '
' /Hide '
'>>'])
def buttonStreamDictionary():
"everything except the length for the button appearance streams"
result = PDFDictionary()
result["SubType"] = "/Form"
result["BBox"] = "[0 0 16.77036 14.90698]"
font = PDFDictionary()
font["ZaDb"] = ZADB
resources = PDFDictionary()
resources["ProcSet"] = "[ /PDF /Text ]"
resources["Font"] = font
result["Resources"] = resources
return result
def ButtonStream(content):
dict = buttonStreamDictionary()
result = PDFStream(dict, content)
result.filters = []
return result
APDOFF = ButtonStream('0.749 g 0 0 16.7704 14.907 re f'+LINEEND)
APDYES = ButtonStream(
'0.749 g 0 0 16.7704 14.907 re f q 1 1 14.7704 12.907 re W '+
'n BT /ZaDb 11.3086 Tf 0 g 1 0 0 1 3.6017 3.3881 Tm (4) Tj ET'+LINEEND)
APNYES = ButtonStream(
'q 1 1 14.7704 12.907 re W n BT /ZaDb 11.3086 Tf 0 g 1 0 0 1 3.6017 3.3881 Tm (4) Tj ET Q'+LINEEND)
#==== script interpretation
if __name__=="__main__":
test1()

View File

@ -1,773 +0,0 @@
#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/pdfbase/pdfmetrics.py
#$Header $
__version__=''' $Id$ '''
__doc__="""
This provides a database of font metric information and
efines Font, Encoding and TypeFace classes aimed at end users.
There are counterparts to some of these in pdfbase/pdfdoc.py, but
the latter focus on constructing the right PDF objects. These
classes are declarative and focus on letting the user construct
and query font objects.
The module maintains a registry of font objects at run time.
It is independent of the canvas or any particular context. It keeps
a registry of Font, TypeFace and Encoding objects. Ideally these
would be pre-loaded, but due to a nasty circularity problem we
trap attempts to access them and do it on first access.
"""
import string, os
from types import StringType, ListType, TupleType
from reportlab.pdfbase import _fontdata
from reportlab.lib.logger import warnOnce
from reportlab.lib.utils import rl_isfile, open_and_read, open_and_readlines
from reportlab.rl_config import defaultEncoding
standardFonts = _fontdata.standardFonts
standardEncodings = _fontdata.standardEncodings
_dummyEncoding=' _not an encoding_ '
# conditional import - try both import techniques, and set a flag
try:
import _rl_accel
try:
_stringWidth = _rl_accel.stringWidth
_rl_accel.defaultEncoding(_dummyEncoding)
except:
_stringWidth = None
except ImportError:
_stringWidth = None
_typefaces = {}
_encodings = {}
_fonts = {}
class FontError(Exception):
pass
class FontNotFoundError(Exception):
pass
def parseAFMFile(afmFileName):
"""Quick and dirty - gives back a top-level dictionary
with top-level items, and a 'widths' key containing
a dictionary of glyph names and widths. Just enough
needed for embedding. A better parser would accept
options for what data you wwanted, and preserve the
order."""
lines = open_and_readlines(afmFileName, 'r')
if len(lines)<=1:
#likely to be a MAC file
lines = string.split(lines,'\r')
if len(lines)<=1:
raise ValueError, 'AFM file %s hasn\'t enough data' % afmFileName
topLevel = {}
glyphLevel = []
lines = map(string.strip, lines)
#pass 1 - get the widths
inMetrics = 0 # os 'TOP', or 'CHARMETRICS'
for line in lines:
if line[0:16] == 'StartCharMetrics':
inMetrics = 1
elif line[0:14] == 'EndCharMetrics':
inMetrics = 0
elif inMetrics:
chunks = string.split(line, ';')
chunks = map(string.strip, chunks)
cidChunk, widthChunk, nameChunk = chunks[0:3]
# character ID
l, r = string.split(cidChunk)
assert l == 'C', 'bad line in font file %s' % line
cid = string.atoi(r)
# width
l, r = string.split(widthChunk)
assert l == 'WX', 'bad line in font file %s' % line
width = string.atoi(r)
# name
l, r = string.split(nameChunk)
assert l == 'N', 'bad line in font file %s' % line
name = r
glyphLevel.append((cid, width, name))
# pass 2 font info
inHeader = 0
for line in lines:
if line[0:16] == 'StartFontMetrics':
inHeader = 1
if line[0:16] == 'StartCharMetrics':
inHeader = 0
elif inHeader:
if line[0:7] == 'Comment': pass
try:
left, right = string.split(line,' ',1)
except:
raise ValueError, "Header information error in afm %s: line='%s'" % (afmFileName, line)
try:
right = string.atoi(right)
except:
pass
topLevel[left] = right
return (topLevel, glyphLevel)
class TypeFace:
def __init__(self, name):
self.name = name
self.glyphNames = []
self.glyphWidths = {}
self.ascent = 0
self.descent = 0
# all typefaces of whatever class should have these 3 attributes.
# these are the basis for family detection.
self.familyName = None # should set on load/construction if possible
self.bold = 0 # bold faces should set this
self.italic = 0 #italic faces should set this
if name == 'ZapfDingbats':
self.requiredEncoding = 'ZapfDingbatsEncoding'
elif name == 'Symbol':
self.requiredEncoding = 'SymbolEncoding'
else:
self.requiredEncoding = None
if name in standardFonts:
self.builtIn = 1
self._loadBuiltInData(name)
else:
self.builtIn = 0
def _loadBuiltInData(self, name):
"""Called for the built in 14 fonts. Gets their glyph data.
We presume they never change so this can be a shared reference."""
name = str(name) #needed for pycanvas&jython/2.1 compatibility
self.glyphWidths = _fontdata.widthsByFontGlyph[name]
self.glyphNames = self.glyphWidths.keys()
self.ascent,self.descent = _fontdata.ascent_descent[name]
def getFontFiles(self):
"Info function, return list of the font files this depends on."
return []
def findT1File(self, ext='.pfb'):
possible_exts = (string.lower(ext), string.upper(ext))
if hasattr(self,'pfbFileName'):
r_basename = os.path.splitext(self.pfbFileName)[0]
for e in possible_exts:
if rl_isfile(r_basename + e):
return r_basename + e
try:
r = _fontdata.findT1File(self.name)
except:
afm = bruteForceSearchForAFM(self.name)
if afm:
if string.lower(ext) == '.pfb':
for e in possible_exts:
pfb = os.path.splitext(afm)[0] + e
if rl_isfile(pfb):
r = pfb
else:
r = None
elif string.lower(ext) == '.afm':
r = afm
else:
r = None
if r is None:
warnOnce("Can't find %s for face '%s'" % (ext, self.name))
return r
def bruteForceSearchForAFM(faceName):
"""Looks in all AFM files on path for face with given name.
Returns AFM file name or None. Ouch!"""
import glob
from reportlab.rl_config import T1SearchPath
for dirname in T1SearchPath:
if not os.path.isdir(dirname):
continue
possibles = glob.glob(dirname + os.sep + '*.[aA][fF][mM]')
for possible in possibles:
(topDict, glyphDict) = parseAFMFile(possible)
if topDict['FontName'] == faceName:
return possible
return None
#for faceName in standardFonts:
# registerTypeFace(TypeFace(faceName))
class Encoding:
"""Object to help you create and refer to encodings."""
def __init__(self, name, base=None):
self.name = name
self.frozen = 0
if name in standardEncodings:
assert base is None, "Can't have a base encoding for a standard encoding"
self.baseEncodingName = name
self.vector = _fontdata.encodings[name]
elif base == None:
# assume based on the usual one
self.baseEncodingName = defaultEncoding
self.vector = _fontdata.encodings[defaultEncoding]
elif type(base) is StringType:
baseEnc = getEncoding(base)
self.baseEncodingName = baseEnc.name
self.vector = baseEnc.vector[:]
elif type(base) in (ListType, TupleType):
self.baseEncodingName = defaultEncoding
self.vector = base[:]
elif isinstance(base, Encoding):
# accept a vector
self.baseEncodingName = base.name
self.vector = base.vector[:]
def __getitem__(self, index):
"Return glyph name for that code point, or None"
# THIS SHOULD BE INLINED FOR SPEED
return self.vector[index]
def __setitem__(self, index, value):
# should fail if they are frozen
assert self.frozen == 0, 'Cannot modify a frozen encoding'
if self.vector[index]!=value:
L = list(self.vector)
L[index] = value
self.vector = tuple(L)
def freeze(self):
self.vector = tuple(self.vector)
self.frozen = 1
def isEqual(self, other):
return ((self.name == other.name) and (self.vector == other.vector))
def modifyRange(self, base, newNames):
"""Set a group of character names starting at the code point 'base'."""
assert self.frozen == 0, 'Cannot modify a frozen encoding'
idx = base
for name in newNames:
self.vector[idx] = name
idx = idx + 1
def getDifferences(self, otherEnc):
"""Return a compact list of the code points differing between two encodings
This is in the Adobe format: list of
[[b1, name1, name2, name3],
[b2, name4]]
where b1...bn is the starting code point, and the glyph names following
are assigned consecutive code points."""
ranges = []
curRange = None
for i in xrange(len(self.vector)):
glyph = self.vector[i]
if glyph==otherEnc.vector[i]:
if curRange:
ranges.append(curRange)
curRange = []
else:
if curRange:
curRange.append(glyph)
elif glyph:
curRange = [i, glyph]
if curRange:
ranges.append(curRange)
return ranges
def makePDFObject(self):
"Returns a PDF Object representing self"
# avoid circular imports - this cannot go at module level
from reportlab.pdfbase import pdfdoc
D = {}
baseEnc = getEncoding(self.baseEncodingName)
differences = self.getDifferences(baseEnc) #[None] * 256)
# if no differences, we just need the base name
if differences == []:
return pdfdoc.PDFName(self.baseEncodingName)
else:
#make up a dictionary describing the new encoding
diffArray = []
for range in differences:
diffArray.append(range[0]) # numbers go 'as is'
for glyphName in range[1:]:
if glyphName is not None:
# there is no way to 'unset' a character in the base font.
diffArray.append('/' + glyphName)
#print 'diffArray = %s' % diffArray
D["Differences"] = pdfdoc.PDFArray(diffArray)
D["BaseEncoding"] = pdfdoc.PDFName(self.baseEncodingName)
D["Type"] = pdfdoc.PDFName("Encoding")
PD = pdfdoc.PDFDictionary(D)
return PD
#for encName in standardEncodings:
# registerEncoding(Encoding(encName))
class Font:
"""Represents a font (i.e combination of face and encoding).
Defines suitable machinery for single byte fonts. This is
a concrete class which can handle the basic built-in fonts;
not clear yet if embedded ones need a new font class or
just a new typeface class (which would do the job through
composition)"""
def __init__(self, name, faceName, encName):
self.fontName = name
self.face = getTypeFace(faceName)
self.encoding= getEncoding(encName)
self._calcWidths()
# multi byte fonts do their own stringwidth calculations.
# signal this here.
self._multiByte = 0
def _calcWidths(self):
"""Vector of widths for stringWidth function"""
#synthesize on first request
w = [0] * 256
gw = self.face.glyphWidths
vec = self.encoding.vector
for i in range(256):
glyphName = vec[i]
if glyphName is not None:
try:
width = gw[glyphName]
w[i] = width
except KeyError:
import reportlab.rl_config
if reportlab.rl_config.warnOnMissingFontGlyphs:
print 'typeface "%s" does not have a glyph "%s", bad font!' % (self.face.name, glyphName)
else:
pass
self.widths = w
if not _stringWidth:
def stringWidth(self, text, size):
"""This is the "purist" approach to width. The practical one
is to use the stringWidth one which may be optimized
in C."""
w = 0
widths = self.widths
for ch in text:
w = w + widths[ord(ch)]
return w * 0.001 * size
def _formatWidths(self):
"returns a pretty block in PDF Array format to aid inspection"
text = '['
for i in range(256):
text = text + ' ' + str(self.widths[i])
if i == 255:
text = text + ' ]'
if i % 16 == 15:
text = text + '\n'
return text
def addObjects(self, doc):
"""Makes and returns one or more PDF objects to be added
to the document. The caller supplies the internal name
to be used (typically F1, F2... in sequence) """
# avoid circular imports - this cannot go at module level
from reportlab.pdfbase import pdfdoc
# construct a Type 1 Font internal object
internalName = 'F' + repr(len(doc.fontMapping)+1)
pdfFont = pdfdoc.PDFType1Font()
pdfFont.Name = internalName
pdfFont.BaseFont = self.face.name
pdfFont.__Comment__ = 'Font %s' % self.fontName
pdfFont.Encoding = self.encoding.makePDFObject()
# is it a built-in one? if not, need more stuff.
if not self.face.name in standardFonts:
pdfFont.FirstChar = 0
pdfFont.LastChar = 255
pdfFont.Widths = pdfdoc.PDFArray(self.widths)
pdfFont.FontDescriptor = self.face.addObjects(doc)
# now link it in
ref = doc.Reference(pdfFont, internalName)
# also refer to it in the BasicFonts dictionary
fontDict = doc.idToObject['BasicFonts'].dict
fontDict[internalName] = pdfFont
# and in the font mappings
doc.fontMapping[self.fontName] = '/' + internalName
PFB_MARKER=chr(0x80)
PFB_ASCII=chr(1)
PFB_BINARY=chr(2)
PFB_EOF=chr(3)
def _pfbSegLen(p,d):
'''compute a pfb style length from the first 4 bytes of string d'''
return ((((ord(d[p+3])<<8)|ord(d[p+2])<<8)|ord(d[p+1]))<<8)|ord(d[p])
def _pfbCheck(p,d,m,fn):
if d[p]!=PFB_MARKER or d[p+1]!=m:
raise ValueError, 'Bad pfb file\'%s\' expected chr(%d)chr(%d) at char %d, got chr(%d)chr(%d)' % (fn,ord(PFB_MARKER),ord(m),p,ord(d[p]),ord(d[p+1]))
if m==PFB_EOF: return
p = p + 2
l = _pfbSegLen(p,d)
p = p + 4
if p+l>len(d):
raise ValueError, 'Bad pfb file\'%s\' needed %d+%d bytes have only %d!' % (fn,p,l,len(d))
return p, p+l
class EmbeddedType1Face(TypeFace):
"""A Type 1 font other than one of the basic 14.
Its glyph data will be embedded in the PDF file."""
def __init__(self, afmFileName, pfbFileName):
# ignore afm file for now
TypeFace.__init__(self, None)
#None is a hack, name will be supplied by AFM parse lower done
#in this __init__ method.
self.afmFileName = os.path.abspath(afmFileName)
self.pfbFileName = os.path.abspath(pfbFileName)
self.requiredEncoding = None
self._loadGlyphs(pfbFileName)
self._loadMetrics(afmFileName)
def getFontFiles(self):
return [self.afmFileName, self.pfbFileName]
def _loadGlyphs(self, pfbFileName):
"""Loads in binary glyph data, and finds the four length
measurements needed for the font descriptor"""
assert rl_isfile(pfbFileName), 'file %s not found' % pfbFileName
d = open_and_read(pfbFileName, 'b')
s1, l1 = _pfbCheck(0,d,PFB_ASCII,pfbFileName)
s2, l2 = _pfbCheck(l1,d,PFB_BINARY,pfbFileName)
s3, l3 = _pfbCheck(l2,d,PFB_ASCII,pfbFileName)
_pfbCheck(l3,d,PFB_EOF,pfbFileName)
self._binaryData = d[s1:l1]+d[s2:l2]+d[s3:l3]
self._length = len(self._binaryData)
self._length1 = l1-s1
self._length2 = l2-s2
self._length3 = l3-s3
def _loadMetrics(self, afmFileName):
"""Loads in and parses font metrics"""
#assert os.path.isfile(afmFileName), "AFM file %s not found" % afmFileName
(topLevel, glyphData) = parseAFMFile(afmFileName)
self.name = topLevel['FontName']
self.familyName = topLevel['FamilyName']
self.ascent = topLevel.get('Ascender', 1000)
self.descent = topLevel.get('Descender', 0)
self.capHeight = topLevel.get('CapHeight', 1000)
self.italicAngle = topLevel.get('ItalicAngle', 0)
self.stemV = topLevel.get('stemV', 0)
self.xHeight = topLevel.get('XHeight', 1000)
strBbox = topLevel.get('FontBBox', [0,0,1000,1000])
tokens = string.split(strBbox)
self.bbox = []
for tok in tokens:
self.bbox.append(string.atoi(tok))
glyphWidths = {}
for (cid, width, name) in glyphData:
glyphWidths[name] = width
self.glyphWidths = glyphWidths
self.glyphNames = glyphWidths.keys()
self.glyphNames.sort()
# for font-specific encodings like Symbol, Dingbats, Carta we
# need to make a new encoding as well....
if topLevel.get('EncodingScheme', None) == 'FontSpecific':
names = [None] * 256
for (code, width, name) in glyphData:
if code >=0 and code <=255:
names[code] = name
encName = self.name + 'Encoding'
self.requiredEncoding = encName
enc = Encoding(encName, names)
registerEncoding(enc)
def addObjects(self, doc):
"""Add whatever needed to PDF file, and return a FontDescriptor reference"""
from reportlab.pdfbase import pdfdoc
fontFile = pdfdoc.PDFStream()
fontFile.content = self._binaryData
#fontFile.dictionary['Length'] = self._length
fontFile.dictionary['Length1'] = self._length1
fontFile.dictionary['Length2'] = self._length2
fontFile.dictionary['Length3'] = self._length3
#fontFile.filters = [pdfdoc.PDFZCompress]
fontFileRef = doc.Reference(fontFile, 'fontFile:' + self.pfbFileName)
fontDescriptor = pdfdoc.PDFDictionary({
'Type': '/FontDescriptor',
'Ascent':self.ascent,
'CapHeight':self.capHeight,
'Descent':self.descent,
'Flags': 34,
'FontBBox':pdfdoc.PDFArray(self.bbox),
'FontName':pdfdoc.PDFName(self.name),
'ItalicAngle':self.italicAngle,
'StemV':self.stemV,
'XHeight':self.xHeight,
'FontFile': fontFileRef,
})
fontDescriptorRef = doc.Reference(fontDescriptor, 'fontDescriptor:' + self.name)
return fontDescriptorRef
def registerTypeFace(face):
assert isinstance(face, TypeFace), 'Not a TypeFace: %s' % face
_typefaces[face.name] = face
# HACK - bold/italic do not apply for type 1, so egister
# all combinations of mappings.
from reportlab.lib import fonts
ttname = string.lower(face.name)
if not face.name in standardFonts:
fonts.addMapping(ttname, 0, 0, face.name)
fonts.addMapping(ttname, 1, 0, face.name)
fonts.addMapping(ttname, 0, 1, face.name)
fonts.addMapping(ttname, 1, 1, face.name)
def registerEncoding(enc):
assert isinstance(enc, Encoding), 'Not an Encoding: %s' % enc
if _encodings.has_key(enc.name):
# already got one, complain if they are not the same
if enc.isEqual(_encodings[enc.name]):
enc.freeze()
else:
raise FontError('Encoding "%s" already registered with a different name vector!' % enc.Name)
else:
_encodings[enc.name] = enc
enc.freeze()
# have not yet dealt with immutability!
def registerFont(font):
"Registers a font, including setting up info for accelerated stringWidth"
#assert isinstance(font, Font), 'Not a Font: %s' % font
fontName = font.fontName
_fonts[fontName] = font
if font._multiByte:
# CID fonts don't need to have typeface registered.
#need to set mappings so it can go in a paragraph even if within
# bold tags
from reportlab.lib import fonts
ttname = string.lower(font.fontName)
fonts.addMapping(ttname, 0, 0, font.fontName)
fonts.addMapping(ttname, 1, 0, font.fontName)
fonts.addMapping(ttname, 0, 1, font.fontName)
fonts.addMapping(ttname, 1, 1, font.fontName)
#cannot accelerate these yet...
else:
if _stringWidth:
_rl_accel.setFontInfo(string.lower(fontName),
_dummyEncoding,
font.face.ascent,
font.face.descent,
font.widths)
def getTypeFace(faceName):
"""Lazily construct known typefaces if not found"""
try:
return _typefaces[faceName]
except KeyError:
# not found, construct it if known
if faceName in standardFonts:
face = TypeFace(faceName)
(face.familyName, face.bold, face.italic) = _fontdata.standardFontAttributes[faceName]
registerTypeFace(face)
## print 'auto-constructing type face %s with family=%s, bold=%d, italic=%d' % (
## face.name, face.familyName, face.bold, face.italic)
return face
else:
#try a brute force search
afm = bruteForceSearchForAFM(faceName)
if afm:
for e in ('.pfb', '.PFB'):
pfb = os.path.splitext(afm)[0] + e
if rl_isfile(pfb): break
assert rl_isfile(pfb), 'file %s not found!' % pfb
face = EmbeddedType1Face(afm, pfb)
registerTypeFace(face)
return face
else:
raise
def getEncoding(encName):
"""Lazily construct known encodings if not found"""
try:
return _encodings[encName]
except KeyError:
if encName in standardEncodings:
enc = Encoding(encName)
registerEncoding(enc)
#print 'auto-constructing encoding %s' % encName
return enc
else:
raise
def getFont(fontName):
"""Lazily constructs known fonts if not found.
Names of form 'face-encoding' will be built if
face and encoding are known. Also if the name is
just one of the standard 14, it will make up a font
in the default encoding."""
try:
return _fonts[fontName]
except KeyError:
#it might have a font-specific encoding e.g. Symbol
# or Dingbats. If not, take the default.
face = getTypeFace(fontName)
if face.requiredEncoding:
font = Font(fontName, fontName, face.requiredEncoding)
else:
font = Font(fontName, fontName, defaultEncoding)
registerFont(font)
return font
def getRegisteredFontNames():
"Returns what's in there"
reg = _fonts.keys()
reg.sort()
return reg
def _slowStringWidth(text, fontName, fontSize):
"""Define this anyway so it can be tested, but whether it is used or not depends on _rl_accel"""
font = getFont(fontName)
return font.stringWidth(text, fontSize)
#this is faster, but will need more special-casing for multi-byte fonts.
#wid = getFont(fontName).widths
#w = 0
#for ch in text:
# w = w + wid[ord(ch)]
#return 0.001 * w * fontSize
if _stringWidth:
import new
Font.stringWidth = new.instancemethod(_rl_accel._instanceStringWidth,None,Font)
stringWidth = _stringWidth
#if accelerator present, make sure we at least
#register Courier font, since it will fall back to Courier
#as its default font.
f = getFont('Courier')
def _SWRecover(text, fontName, fontSize, encoding):
'''This is called when _rl_accel's database doesn't know about a font.
Currently encoding is always a dummy.
'''
try:
font = getFont(fontName)
if font._multiByte:
return font.stringWidth(text, fontSize)
else:
registerFont(font)
return _stringWidth(text,fontName,fontSize,encoding)
except:
warnOnce('Font %s:%s not found - using Courier:%s for widths'%(fontName,encoding,encoding))
return _stringWidth(text,'courier',fontSize,encoding)
_rl_accel._SWRecover(_SWRecover)
else:
stringWidth = _slowStringWidth
def dumpFontData():
print 'Registered Encodings:'
keys = _encodings.keys()
keys.sort()
for encName in keys:
print ' ',encName
print
print 'Registered Typefaces:'
faces = _typefaces.keys()
faces.sort()
for faceName in faces:
print ' ',faceName
print
print 'Registered Fonts:'
k = _fonts.keys()
k.sort()
for key in k:
font = _fonts[key]
print ' %s (%s/%s)' % (font.fontName, font.face.name, font.encoding.name)
def test3widths(texts):
# checks all 3 algorithms give same answer, note speed
import time
for fontName in standardFonts[0:1]:
t0 = time.time()
for text in texts:
l1 = _stringWidth(text, fontName, 10)
t1 = time.time()
print 'fast stringWidth took %0.4f' % (t1 - t0)
t0 = time.time()
w = getFont(fontName).widths
for text in texts:
l2 = 0
for ch in text:
l2 = l2 + w[ord(ch)]
t1 = time.time()
print 'slow stringWidth took %0.4f' % (t1 - t0)
t0 = time.time()
for text in texts:
l3 = getFont(fontName).stringWidth(text, 10)
t1 = time.time()
print 'class lookup and stringWidth took %0.4f' % (t1 - t0)
print
def testStringWidthAlgorithms():
rawdata = open('../../rlextra/rml2pdf/doc/rml_user_guide.prep').read()
print 'rawdata length %d' % len(rawdata)
print 'test one huge string...'
test3widths([rawdata])
print
words = string.split(rawdata)
print 'test %d shorter strings (average length %0.2f chars)...' % (len(words), 1.0*len(rawdata)/len(words))
test3widths(words)
def test():
helv = TypeFace('Helvetica')
registerTypeFace(helv)
print helv.glyphNames[0:30]
wombat = TypeFace('Wombat')
print wombat.glyphNames
registerTypeFace(wombat)
dumpFontData()
if __name__=='__main__':
test()
testStringWidthAlgorithms()

View File

@ -1,59 +0,0 @@
"""
helper for importing pdf structures into a ReportLab generated document
"""
from reportlab.pdfbase.pdfdoc import format
import string
class PDFPattern:
__RefOnly__ = 1
def __init__(self, pattern_sequence, **keywordargs):
"""
Description of a kind of PDF object using a pattern.
Pattern sequence should contain strings or singletons of form [string].
Strings are literal strings to be used in the object.
Singletons are names of keyword arguments to include.
Keyword arguments can be non-instances which are substituted directly in string conversion,
or they can be object instances in which case they should be pdfdoc.* style
objects with a x.format(doc) method.
Keyword arguments may be set on initialization or subsequently using __setitem__, before format.
"constant object" instances can also be inserted in the patterns.
"""
self.pattern = pattern_sequence
self.arguments = keywordargs
from types import StringType, InstanceType
toptypes = (StringType, InstanceType)
for x in pattern_sequence:
if type(x) not in toptypes:
if len(x)!=1:
raise ValueError, "sequence elts must be strings or singletons containing strings: "+repr(x)
if type(x[0]) is not StringType:
raise ValueError, "Singletons must contain strings or instances only: "+repr(x[0])
def __setitem__(self, item, value):
self.arguments[item] = value
def __getitem__(self, item):
return self.arguments[item]
def format(self, document):
L = []
arguments = self.arguments
from types import StringType, InstanceType
for x in self.pattern:
tx = type(x)
if tx is StringType:
L.append(x)
elif tx is InstanceType:
L.append( x.format(document) )
else:
name = x[0]
value = arguments.get(name, None)
if value is None:
raise ValueError, "%s value not defined" % repr(name)
if type(value) is InstanceType:
#L.append( value.format(document) )
L.append(format(value, document))
else:
L.append( str(value) )
return string.join(L, "")

View File

@ -1,453 +0,0 @@
#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/pdfbase/pdfutils.py
__version__=''' $Id$ '''
__doc__=''
# pdfutils.py - everything to do with images, streams,
# compression, and some constants
import os
from reportlab import rl_config
from string import join, replace, strip, split
from reportlab.lib.utils import getStringIO, ImageReader
LINEEND = '\015\012'
def _chunker(src,dst=[],chunkSize=60):
for i in xrange(0,len(src),chunkSize):
dst.append(src[i:i+chunkSize])
return dst
##########################################################
#
# Image compression helpers. Preprocessing a directory
# of images will offer a vast speedup.
#
##########################################################
def cacheImageFile(filename, returnInMemory=0, IMG=None):
"Processes image as if for encoding, saves to a file with .a85 extension."
from reportlab.lib.utils import open_for_read
import zlib
cachedname = os.path.splitext(filename)[0] + '.a85'
if filename==cachedname:
if cachedImageExists(filename):
if returnInMemory: return split(open_for_read(cachedname).read(),LINEEND)[:-1]
else:
raise IOError, 'No such cached image %s' % filename
else:
img = ImageReader(filename)
if IMG is not None: IMG.append(img)
imgwidth, imgheight = img.getSize()
raw = img.getRGBData()
code = []
# this describes what is in the image itself
code.append('BI')
code.append('/W %s /H %s /BPC 8 /CS /RGB /F [/A85 /Fl]' % (imgwidth, imgheight))
code.append('ID')
#use a flate filter and Ascii Base 85
assert(len(raw) == imgwidth * imgheight, "Wrong amount of data for image")
compressed = zlib.compress(raw) #this bit is very fast...
encoded = _AsciiBase85Encode(compressed) #...sadly this may not be
#append in blocks of 60 characters
_chunker(encoded,code)
code.append('EI')
if returnInMemory: return code
#save it to a file
f = open(cachedname,'wb')
f.write(join(code, LINEEND)+LINEEND)
f.close()
if rl_config.verbose:
print 'cached image as %s' % cachedname
def preProcessImages(spec):
"""Preprocesses one or more image files.
Accepts either a filespec ('C:\mydir\*.jpg') or a list
of image filenames, crunches them all to save time. Run this
to save huge amounts of time when repeatedly building image
documents."""
import types, glob
if type(spec) is types.StringType:
filelist = glob.glob(spec)
else: #list or tuple OK
filelist = spec
for filename in filelist:
if cachedImageExists(filename):
if rl_config.verbose:
print 'cached version of %s already exists' % filename
else:
cacheImageFile(filename)
def cachedImageExists(filename):
"""Determines if a cached image already exists for a given file.
Determines if a cached image exists which has the same name
and equal or newer date to the given file."""
cachedname = os.path.splitext(filename)[0] + '.a85'
if os.path.isfile(cachedname):
#see if it is newer
original_date = os.stat(filename)[8]
cached_date = os.stat(cachedname)[8]
if original_date > cached_date:
return 0
else:
return 1
else:
return 0
##############################################################
#
# PDF Helper functions
#
##############################################################
try:
from _rl_accel import escapePDF, _instanceEscapePDF
_escape = escapePDF
except ImportError:
try:
from reportlab.lib._rl_accel import escapePDF, _instanceEscapePDF
_escape = escapePDF
except ImportError:
_instanceEscapePDF=None
if rl_config.sys_version>='2.1':
_ESCAPEDICT={}
for c in range(0,256):
if c<32 or c>=127:
_ESCAPEDICT[chr(c)]= '\\%03o' % c
elif c in (ord('\\'),ord('('),ord(')')):
_ESCAPEDICT[chr(c)] = '\\'+chr(c)
else:
_ESCAPEDICT[chr(c)] = chr(c)
del c
#Michael Hudson donated this
def _escape(s):
return join(map(lambda c, d=_ESCAPEDICT: d[c],s),'')
else:
def _escape(s):
"""Escapes some PDF symbols (in fact, parenthesis).
PDF escapes are almost like Python ones, but brackets
need slashes before them too. Uses Python's repr function
and chops off the quotes first."""
s = repr(s)[1:-1]
s = replace(s, '(','\(')
s = replace(s, ')','\)')
return s
def _normalizeLineEnds(text,desired=LINEEND):
"""Normalizes different line end character(s).
Ensures all instances of CR, LF and CRLF end up as
the specified one."""
unlikely = '\000\001\002\003'
text = replace(text, '\015\012', unlikely)
text = replace(text, '\015', unlikely)
text = replace(text, '\012', unlikely)
text = replace(text, unlikely, desired)
return text
def _AsciiHexEncode(input):
"""Encodes input using ASCII-Hex coding.
This is a verbose encoding used for binary data within
a PDF file. One byte binary becomes two bytes of ASCII.
Helper function used by images."""
output = getStringIO()
for char in input:
output.write('%02x' % ord(char))
output.write('>')
return output.getvalue()
def _AsciiHexDecode(input):
"""Decodes input using ASCII-Hex coding.
Not used except to provide a test of the inverse function."""
#strip out all whitespace
stripped = join(split(input),'')
assert stripped[-1] == '>', 'Invalid terminator for Ascii Hex Stream'
stripped = stripped[:-1] #chop off terminator
assert len(stripped) % 2 == 0, 'Ascii Hex stream has odd number of bytes'
i = 0
output = getStringIO()
while i < len(stripped):
twobytes = stripped[i:i+2]
output.write(chr(eval('0x'+twobytes)))
i = i + 2
return output.getvalue()
if 1: # for testing always define this
def _AsciiBase85EncodePYTHON(input):
"""Encodes input using ASCII-Base85 coding.
This is a compact encoding used for binary data within
a PDF file. Four bytes of binary data become five bytes of
ASCII. This is the default method used for encoding images."""
outstream = getStringIO()
# special rules apply if not a multiple of four bytes.
whole_word_count, remainder_size = divmod(len(input), 4)
cut = 4 * whole_word_count
body, lastbit = input[0:cut], input[cut:]
for i in range(whole_word_count):
offset = i*4
b1 = ord(body[offset])
b2 = ord(body[offset+1])
b3 = ord(body[offset+2])
b4 = ord(body[offset+3])
if b1<128:
num = (((((b1<<8)|b2)<<8)|b3)<<8)|b4
else:
num = 16777216L * b1 + 65536 * b2 + 256 * b3 + b4
if num == 0:
#special case
outstream.write('z')
else:
#solve for five base-85 numbers
temp, c5 = divmod(num, 85)
temp, c4 = divmod(temp, 85)
temp, c3 = divmod(temp, 85)
c1, c2 = divmod(temp, 85)
assert ((85**4) * c1) + ((85**3) * c2) + ((85**2) * c3) + (85*c4) + c5 == num, 'dodgy code!'
outstream.write(chr(c1+33))
outstream.write(chr(c2+33))
outstream.write(chr(c3+33))
outstream.write(chr(c4+33))
outstream.write(chr(c5+33))
# now we do the final bit at the end. I repeated this separately as
# the loop above is the time-critical part of a script, whereas this
# happens only once at the end.
#encode however many bytes we have as usual
if remainder_size > 0:
while len(lastbit) < 4:
lastbit = lastbit + '\000'
b1 = ord(lastbit[0])
b2 = ord(lastbit[1])
b3 = ord(lastbit[2])
b4 = ord(lastbit[3])
num = 16777216L * b1 + 65536 * b2 + 256 * b3 + b4
#solve for c1..c5
temp, c5 = divmod(num, 85)
temp, c4 = divmod(temp, 85)
temp, c3 = divmod(temp, 85)
c1, c2 = divmod(temp, 85)
#print 'encoding: %d %d %d %d -> %d -> %d %d %d %d %d' % (
# b1,b2,b3,b4,num,c1,c2,c3,c4,c5)
lastword = chr(c1+33) + chr(c2+33) + chr(c3+33) + chr(c4+33) + chr(c5+33)
#write out most of the bytes.
outstream.write(lastword[0:remainder_size + 1])
#terminator code for ascii 85
outstream.write('~>')
return outstream.getvalue()
def _AsciiBase85DecodePYTHON(input):
"""Decodes input using ASCII-Base85 coding.
This is not used - Acrobat Reader decodes for you
- but a round trip is essential for testing."""
outstream = getStringIO()
#strip all whitespace
stripped = join(split(input),'')
#check end
assert stripped[-2:] == '~>', 'Invalid terminator for Ascii Base 85 Stream'
stripped = stripped[:-2] #chop off terminator
#may have 'z' in it which complicates matters - expand them
stripped = replace(stripped,'z','!!!!!')
# special rules apply if not a multiple of five bytes.
whole_word_count, remainder_size = divmod(len(stripped), 5)
#print '%d words, %d leftover' % (whole_word_count, remainder_size)
#assert remainder_size <> 1, 'invalid Ascii 85 stream!'
cut = 5 * whole_word_count
body, lastbit = stripped[0:cut], stripped[cut:]
for i in range(whole_word_count):
offset = i*5
c1 = ord(body[offset]) - 33
c2 = ord(body[offset+1]) - 33
c3 = ord(body[offset+2]) - 33
c4 = ord(body[offset+3]) - 33
c5 = ord(body[offset+4]) - 33
num = ((85L**4) * c1) + ((85**3) * c2) + ((85**2) * c3) + (85*c4) + c5
temp, b4 = divmod(num,256)
temp, b3 = divmod(temp,256)
b1, b2 = divmod(temp, 256)
assert num == 16777216 * b1 + 65536 * b2 + 256 * b3 + b4, 'dodgy code!'
outstream.write(chr(b1))
outstream.write(chr(b2))
outstream.write(chr(b3))
outstream.write(chr(b4))
#decode however many bytes we have as usual
if remainder_size > 0:
while len(lastbit) < 5:
lastbit = lastbit + '!'
c1 = ord(lastbit[0]) - 33
c2 = ord(lastbit[1]) - 33
c3 = ord(lastbit[2]) - 33
c4 = ord(lastbit[3]) - 33
c5 = ord(lastbit[4]) - 33
num = (((85*c1+c2)*85+c3)*85+c4)*85L + (c5
+(0,0,0xFFFFFF,0xFFFF,0xFF)[remainder_size])
temp, b4 = divmod(num,256)
temp, b3 = divmod(temp,256)
b1, b2 = divmod(temp, 256)
assert num == 16777216 * b1 + 65536 * b2 + 256 * b3 + b4, 'dodgy code!'
#print 'decoding: %d %d %d %d %d -> %d -> %d %d %d %d' % (
# c1,c2,c3,c4,c5,num,b1,b2,b3,b4)
#the last character needs 1 adding; the encoding loses
#data by rounding the number to x bytes, and when
#divided repeatedly we get one less
if remainder_size == 2:
lastword = chr(b1)
elif remainder_size == 3:
lastword = chr(b1) + chr(b2)
elif remainder_size == 4:
lastword = chr(b1) + chr(b2) + chr(b3)
else:
lastword = ''
outstream.write(lastword)
#terminator code for ascii 85
return outstream.getvalue()
try:
from _rl_accel import _AsciiBase85Encode # builtin or on the path
except ImportError:
try:
from reportlab.lib._rl_accel import _AsciiBase85Encode # where we think it should be
except ImportError:
_AsciiBase85Encode = _AsciiBase85EncodePYTHON
try:
from _rl_accel import _AsciiBase85Decode # builtin or on the path
except ImportError:
try:
from reportlab.lib._rl_accel import _AsciiBase85Decode # where we think it should be
except ImportError:
_AsciiBase85Decode = _AsciiBase85DecodePYTHON
def _wrap(input, columns=60):
"Wraps input at a given column size by inserting LINEEND characters."
output = []
length = len(input)
i = 0
pos = columns * i
while pos < length:
output.append(input[pos:pos+columns])
i = i + 1
pos = columns * i
return join(output, LINEEND)
#########################################################################
#
# JPEG processing code - contributed by Eric Johnson
#
#########################################################################
# Read data from the JPEG file. We should probably be using PIL to
# get this information for us -- but this way is more fun!
# Returns (width, height, color components) as a triple
# This is based on Thomas Merz's code from GhostScript (viewjpeg.ps)
def readJPEGInfo(image):
"Read width, height and number of components from open JPEG file."
import struct
#Acceptable JPEG Markers:
# SROF0=baseline, SOF1=extended sequential or SOF2=progressive
validMarkers = [0xC0, 0xC1, 0xC2]
#JPEG markers without additional parameters
noParamMarkers = \
[ 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0x01 ]
#Unsupported JPEG Markers
unsupportedMarkers = \
[ 0xC3, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF ]
#read JPEG marker segments until we find SOFn marker or EOF
done = 0
while not done:
x = struct.unpack('B', image.read(1))
if x[0] == 0xFF: #found marker
x = struct.unpack('B', image.read(1))
#print "Marker: ", '%0.2x' % x[0]
#check marker type is acceptable and process it
if x[0] in validMarkers:
image.seek(2, 1) #skip segment length
x = struct.unpack('B', image.read(1)) #data precision
if x[0] != 8:
raise 'PDFError', ' JPEG must have 8 bits per component'
y = struct.unpack('BB', image.read(2))
height = (y[0] << 8) + y[1]
y = struct.unpack('BB', image.read(2))
width = (y[0] << 8) + y[1]
y = struct.unpack('B', image.read(1))
color = y[0]
return width, height, color
done = 1
elif x[0] in unsupportedMarkers:
raise 'PDFError', ' Unsupported JPEG marker: %0.2x' % x[0]
elif x[0] not in noParamMarkers:
#skip segments with parameters
#read length and skip the data
x = struct.unpack('BB', image.read(2))
image.seek( (x[0] << 8) + x[1] - 2, 1)
class _fusc:
def __init__(self,k, n):
assert k, 'Argument k should be a non empty string'
self._k = k
self._klen = len(k)
self._n = int(n) or 7
def encrypt(self,s):
return self.__rotate(_AsciiBase85Encode(''.join(map(chr,self.__fusc(map(ord,s))))),self._n)
def decrypt(self,s):
return ''.join(map(chr,self.__fusc(map(ord,_AsciiBase85Decode(self.__rotate(s,-self._n))))))
def __rotate(self,s,n):
l = len(s)
if n<0: n = l+n
n %= l
if not n: return s
return s[-n:]+s[:l-n]
def __fusc(self,s):
slen = len(s)
return map(lambda x,y: x ^ y,s,map(ord,((int(slen/self._klen)+1)*self._k)[:slen]))

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +0,0 @@
#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/pdfgen/__init__.py
__version__=''' $Id$ '''
__doc__=''

File diff suppressed because it is too large Load Diff

View File

@ -1,93 +0,0 @@
#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/pdfgen/pathobject.py
__version__=''' $Id$ '''
__doc__="""
PDFPathObject is an efficient way to draw paths on a Canvas. Do not
instantiate directly, obtain one from the Canvas instead.
Progress Reports:
8.83, 2000-01-13, gmcm:
created from pdfgen.py
"""
import string
from reportlab.pdfgen import pdfgeom
from reportlab.lib.utils import fp_str
class PDFPathObject:
"""Represents a graphic path. There are certain 'modes' to PDF
drawing, and making a separate object to expose Path operations
ensures they are completed with no run-time overhead. Ask
the Canvas for a PDFPath with getNewPathObject(); moveto/lineto/
curveto wherever you want; add whole shapes; and then add it back
into the canvas with one of the relevant operators.
Path objects are probably not long, so we pack onto one line"""
def __init__(self):
self._code = []
self._code.append('n') #newpath
def getCode(self):
"pack onto one line; used internally"
return string.join(self._code, ' ')
def moveTo(self, x, y):
self._code.append('%s m' % fp_str(x,y))
def lineTo(self, x, y):
self._code.append('%s l' % fp_str(x,y))
def curveTo(self, x1, y1, x2, y2, x3, y3):
self._code.append('%s c' % fp_str(x1, y1, x2, y2, x3, y3))
def arc(self, x1,y1, x2,y2, startAng=0, extent=90):
"""Contributed to piddlePDF by Robert Kern, 28/7/99.
Draw a partial ellipse inscribed within the rectangle x1,y1,x2,y2,
starting at startAng degrees and covering extent degrees. Angles
start with 0 to the right (+x) and increase counter-clockwise.
These should have x1<x2 and y1<y2.
The algorithm is an elliptical generalization of the formulae in
Jim Fitzsimmon's TeX tutorial <URL: http://www.tinaja.com/bezarc1.pdf>."""
pointList = pdfgeom.bezierArc(x1,y1, x2,y2, startAng, extent)
#move to first point
self._code.append('%s m' % fp_str(pointList[0][:2]))
for curve in pointList:
self._code.append('%s c' % fp_str(curve[2:]))
def arcTo(self, x1,y1, x2,y2, startAng=0, extent=90):
"""Like arc, but draws a line from the current point to
the start if the start is not the current point."""
pointList = pdfgeom.bezierArc(x1,y1, x2,y2, startAng, extent)
self._code.append('%s l' % fp_str(pointList[0][:2]))
for curve in pointList:
self._code.append('%s c' % fp_str(curve[2:]))
def rect(self, x, y, width, height):
"""Adds a rectangle to the path"""
self._code.append('%s re' % fp_str((x, y, width, height)))
def ellipse(self, x, y, width, height):
"""adds an ellipse to the path"""
pointList = pdfgeom.bezierArc(x, y, x + width,y + height, 0, 360)
self._code.append('%s m' % fp_str(pointList[0][:2]))
for curve in pointList:
self._code.append('%s c' % fp_str(curve[2:]))
def circle(self, x_cen, y_cen, r):
"""adds a circle to the path"""
x1 = x_cen - r
#x2 = x_cen + r
y1 = y_cen - r
#y2 = y_cen + r
width = height = 2*r
#self.ellipse(x_cen - r, y_cen - r, x_cen + r, y_cen + r)
self.ellipse(x1, y1, width, height)
def close(self):
"draws a line back to where it started"
self._code.append('h')

View File

@ -1,77 +0,0 @@
#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/pdfgen/pdfgeom.py
__version__=''' $Id$ '''
__doc__="""
This module includes any mathematical methods needed for PIDDLE.
It should have no dependencies beyond the Python library.
So far, just Robert Kern's bezierArc.
"""
from math import sin, cos, pi, ceil
def bezierArc(x1,y1, x2,y2, startAng=0, extent=90):
"""bezierArc(x1,y1, x2,y2, startAng=0, extent=90) --> List of Bezier
curve control points.
(x1, y1) and (x2, y2) are the corners of the enclosing rectangle. The
coordinate system has coordinates that increase to the right and down.
Angles, measured in degress, start with 0 to the right (the positive X
axis) and increase counter-clockwise. The arc extends from startAng
to startAng+extent. I.e. startAng=0 and extent=180 yields an openside-down
semi-circle.
The resulting coordinates are of the form (x1,y1, x2,y2, x3,y3, x4,y4)
such that the curve goes from (x1, y1) to (x4, y4) with (x2, y2) and
(x3, y3) as their respective Bezier control points."""
x1,y1, x2,y2 = min(x1,x2), max(y1,y2), max(x1,x2), min(y1,y2)
if abs(extent) <= 90:
arcList = [startAng]
fragAngle = float(extent)
Nfrag = 1
else:
arcList = []
Nfrag = int(ceil(abs(extent)/90.))
fragAngle = float(extent) / Nfrag
x_cen = (x1+x2)/2.
y_cen = (y1+y2)/2.
rx = (x2-x1)/2.
ry = (y2-y1)/2.
halfAng = fragAngle * pi / 360.
kappa = abs(4. / 3. * (1. - cos(halfAng)) / sin(halfAng))
if fragAngle < 0:
sign = -1
else:
sign = 1
pointList = []
for i in range(Nfrag):
theta0 = (startAng + i*fragAngle) * pi / 180.
theta1 = (startAng + (i+1)*fragAngle) *pi / 180.
if fragAngle > 0:
pointList.append((x_cen + rx * cos(theta0),
y_cen - ry * sin(theta0),
x_cen + rx * (cos(theta0) - kappa * sin(theta0)),
y_cen - ry * (sin(theta0) + kappa * cos(theta0)),
x_cen + rx * (cos(theta1) + kappa * sin(theta1)),
y_cen - ry * (sin(theta1) - kappa * cos(theta1)),
x_cen + rx * cos(theta1),
y_cen - ry * sin(theta1)))
else:
pointList.append((x_cen + rx * cos(theta0),
y_cen - ry * sin(theta0),
x_cen + rx * (cos(theta0) + kappa * sin(theta0)),
y_cen - ry * (sin(theta0) - kappa * cos(theta0)),
x_cen + rx * (cos(theta1) - kappa * sin(theta1)),
y_cen - ry * (sin(theta1) + kappa * cos(theta1)),
x_cen + rx * cos(theta1),
y_cen - ry * sin(theta1)))
return pointList

View File

@ -1,186 +0,0 @@
#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/pdfgen/pdfimages.py
__version__=''' $Id$ '''
__doc__="""
Image functionality sliced out of canvas.py for generalization
"""
import os
import string
from types import StringType
import reportlab
from reportlab.pdfbase import pdfutils
from reportlab.pdfbase import pdfdoc
from reportlab.lib.utils import fp_str, getStringIO
from reportlab.lib.utils import import_zlib, haveImages
class PDFImage:
"""Wrapper around different "image sources". You can make images
from a PIL Image object, a filename (in which case it uses PIL),
an image we previously cached (optimisation, hardly used these
days) or a JPEG (which PDF supports natively)."""
def __init__(self, image, x,y, width=None, height=None, caching=0):
self.image = image
self.point = (x,y)
self.dimensions = (width, height)
self.filename = None
self.imageCaching = caching
# the following facts need to be determined,
# whatever the source. Declare what they are
# here for clarity.
self.colorSpace = 'DeviceRGB'
self.bitsPerComponent = 8
self.filters = []
self.source = None # JPEG or PIL, set later
self.getImageData()
def jpg_imagedata(self):
#directly process JPEG files
#open file, needs some error handling!!
fp = open(self.image, 'rb')
result = self._jpg_imagedata(fp)
fp.close()
return result
def _jpg_imagedata(self,imageFile):
self.source = 'JPEG'
info = pdfutils.readJPEGInfo(imageFile)
imgwidth, imgheight = info[0], info[1]
if info[2] == 1:
colorSpace = 'DeviceGray'
elif info[2] == 3:
colorSpace = 'DeviceRGB'
else: #maybe should generate an error, is this right for CMYK?
colorSpace = 'DeviceCMYK'
imageFile.seek(0) #reset file pointer
imagedata = []
#imagedata.append('BI /Width %d /Height /BitsPerComponent 8 /ColorSpace /%s /Filter [/Filter [ /ASCII85Decode /DCTDecode] ID' % (info[0], info[1], colorSpace))
imagedata.append('BI /W %d /H %d /BPC 8 /CS /%s /F [/A85 /DCT] ID' % (imgwidth, imgheight, colorSpace))
#write in blocks of (??) 60 characters per line to a list
compressed = imageFile.read()
encoded = pdfutils._AsciiBase85Encode(compressed)
pdfutils._chunker(encoded,imagedata)
imagedata.append('EI')
return (imagedata, imgwidth, imgheight)
def cache_imagedata(self):
image = self.image
if not pdfutils.cachedImageExists(image):
zlib = import_zlib()
if not zlib: return
if not haveImages: return
pdfutils.cacheImageFile(image)
#now we have one cached, slurp it in
cachedname = os.path.splitext(image)[0] + '.a85'
imagedata = open(cachedname,'rb').readlines()
#trim off newlines...
imagedata = map(string.strip, imagedata)
return imagedata
def PIL_imagedata(self):
image = self.image
if image.format=='JPEG':
fp=image.fp
fp.seek(0)
return self._jpg_imagedata(fp)
self.source = 'PIL'
zlib = import_zlib()
if not zlib: return
myimage = image.convert('RGB')
imgwidth, imgheight = myimage.size
# this describes what is in the image itself
# *NB* according to the spec you can only use the short form in inline images
#imagedata=['BI /Width %d /Height /BitsPerComponent 8 /ColorSpace /%s /Filter [/Filter [ /ASCII85Decode /FlateDecode] ID]' % (imgwidth, imgheight,'RGB')]
imagedata=['BI /W %d /H %d /BPC 8 /CS /RGB /F [/A85 /Fl] ID' % (imgwidth, imgheight)]
#use a flate filter and Ascii Base 85 to compress
raw = myimage.tostring()
assert(len(raw) == imgwidth * imgheight, "Wrong amount of data for image")
compressed = zlib.compress(raw) #this bit is very fast...
encoded = pdfutils._AsciiBase85Encode(compressed) #...sadly this may not be
#append in blocks of 60 characters
pdfutils._chunker(encoded,imagedata)
imagedata.append('EI')
return (imagedata, imgwidth, imgheight)
def getImageData(self):
"Gets data, height, width - whatever type of image"
image = self.image
(width, height) = self.dimensions
if type(image) == StringType:
self.filename = image
if os.path.splitext(image)[1] in ['.jpg', '.JPG', '.jpeg', '.JPEG']:
(imagedata, imgwidth, imgheight) = self.jpg_imagedata()
else:
if not self.imageCaching:
imagedata = pdfutils.cacheImageFile(image,returnInMemory=1)
else:
imagedata = self.cache_imagedata()
#parse line two for width, height
words = string.split(imagedata[1])
imgwidth = string.atoi(words[1])
imgheight = string.atoi(words[3])
else:
import sys
if sys.platform[0:4] == 'java':
#jython, PIL not available
(imagedata, imgwidth, imgheight) = self.JAVA_imagedata()
else:
(imagedata, imgwidth, imgheight) = self.PIL_imagedata()
#now build the PDF for the image.
if not width:
width = imgwidth
if not height:
height = imgheight
self.width = width
self.height = height
self.imageData = imagedata
def drawInlineImage(self, canvas): #, image, x,y, width=None,height=None):
"""Draw an Image into the specified rectangle. If width and
height are omitted, they are calculated from the image size.
Also allow file names as well as images. This allows a
caching mechanism"""
(x,y) = self.point
# this says where and how big to draw it
if not canvas.bottomup: y = y+self.height
canvas._code.append('q %s 0 0 %s cm' % (fp_str(self.width), fp_str(self.height, x, y)))
# self._code.extend(imagedata) if >=python-1.5.2
for line in self.imageData:
canvas._code.append(line)
canvas._code.append('Q')
def format(self, document):
"""Allow it to be used within pdfdoc framework. This only
defines how it is stored, not how it is drawn later."""
dict = pdfdoc.PDFDictionary()
dict['Type'] = '/XObject'
dict['Subtype'] = '/Image'
dict['Width'] = self.width
dict['Height'] = self.height
dict['BitsPerComponent'] = 8
dict['ColorSpace'] = pdfdoc.PDFName(self.colorSpace)
content = string.join(self.imageData[3:-1], '\n') + '\n'
strm = pdfdoc.PDFStream(dictionary=dict, content=content)
return strm.format(document)
if __name__=='__main__':
srcfile = os.path.join(
os.path.dirname(reportlab.__file__),
'test',
'pythonpowered.gif'
)
assert os.path.isfile(srcfile), 'image not found'
pdfdoc.LongFormat = 1
img = PDFImage(srcfile, 100, 100)
import pprint
doc = pdfdoc.PDFDocument()
print 'source=',img.source
print img.format(doc)

View File

@ -1,309 +0,0 @@
# a Pythonesque Canvas v0.8
# Author : Jerome Alet - <alet@librelogiciel.com>
# License : ReportLab's license
#
# $Id$
#
__doc__ = """pycanvas.Canvas : a Canvas class which can also output Python source code.
pycanvas.Canvas class works exactly like canvas.Canvas, but you can
call str() on pycanvas.Canvas instances. Doing so will return the
Python source code equivalent to your own program, which would, when
run, produce the same PDF document as your original program.
Generated Python source code defines a doIt() function which accepts
a filename or file-like object as its first parameter, and an
optional boolean parameter named "regenerate".
The doIt() function will generate a PDF document and save it in the
file you specified in this argument. If the regenerate parameter is
set then it will also return an automatically generated equivalent
Python source code as a string of text, which you can run again to
produce the very same PDF document and the Python source code, which
you can run again... ad nauseam ! If the regenerate parameter is
unset or not used at all (it then defaults to being unset) then None
is returned and the doIt() function is much much faster, it is also
much faster than the original non-serialized program.
the reportlab/test/test_pdfgen_pycanvas.py program is the test suite
for pycanvas, you can do the following to run it :
First set verbose=1 in reportlab/rl_config.py
then from the command interpreter :
$ cd reportlab/test
$ python test_pdfgen_pycanvas.py >n1.py
this will produce both n1.py and test_pdfgen_pycanvas.pdf
then :
$ python n1.py n1.pdf >n2.py
$ python n2.py n2.pdf >n3.py
$ ...
n1.py, n2.py, n3.py and so on will be identical files.
they eventually may end being a bit different because of
rounding problems, mostly in the comments, but this
doesn't matter since the values really are the same
(e.g. 0 instead of 0.0, or .53 instead of 0.53)
n1.pdf, n2.pdf, n3.pdf and so on will be PDF files
similar to test_pdfgen_pycanvas.pdf.
Alternatively you can import n1.py (or n3.py, or n16384.py if you prefer)
in your own program, and then call its doIt function :
import n1
pythonsource = n1.doIt("myfile.pdf", regenerate=1)
Or if you don't need the python source code and want a faster result :
import n1
n1.doIt("myfile.pdf")
When the generated source code is run directly as an independant program,
then the equivalent python source code is printed to stdout, e.g. :
python n1.py
will print the python source code equivalent to n1.py
Why would you want to use such a beast ?
- To linearize (serialize?) a program : optimizing some complex
parts for example.
- To debug : reading the generated Python source code may help you or
the ReportLab team to diagnose problems. The generated code is now
clearly commented and shows nesting levels, page numbers, and so
on. You can use the generated script when asking for support : we
can see the results you obtain without needing your datas or complete
application.
- To create standalone scripts : say your program uses a high level
environment to generate its output (databases, RML, etc...), using
this class would give you an equivalent program but with complete
independance from the high level environment (e.g. if you don't
have Oracle).
- To contribute some nice looking PDF documents to the ReportLab website
without having to send a complete application you don't want to
distribute.
- ... Insert your own ideas here ...
- For fun because you can do it !
"""
import cStringIO
from reportlab.pdfgen import canvas
from reportlab.pdfgen import pathobject
from reportlab.pdfgen import textobject
PyHeader = '''#! /usr/bin/env python
#
# This code was entirely generated by ReportLab (http://www.reportlab.com)
#
import sys
from reportlab.pdfgen import pathobject
from reportlab.pdfgen import textobject
from reportlab.lib.colors import Color
def doIt(file, regenerate=0) :
"""Generates a PDF document, save it into file.
file : either a filename or a file-like object.
regenerate : if set then this function returns the Python source
code which when run will produce the same result.
if unset then this function returns None, and is
much faster.
"""
if regenerate :
from reportlab.pdfgen.pycanvas import Canvas
else :
from reportlab.pdfgen.canvas import Canvas
'''
PyFooter = '''
# if we want the equivalent Python source code, then send it back
if regenerate :
return str(c)
if __name__ == "__main__" :
if len(sys.argv) != 2 :
# second argument must be the name of the PDF file to create
sys.stderr.write("%s needs one and only one argument\\n" % sys.argv[0])
sys.exit(-1)
else :
# we've got a filename, we can proceed.
print doIt(sys.argv[1], regenerate=1)
sys.exit(0)'''
def buildargs(*args, **kwargs) :
"""Constructs a printable list of arguments suitable for use in source function calls."""
arguments = ""
for arg in args :
arguments = arguments + ("%s, " % repr(arg))
for (kw, val) in kwargs.items() :
arguments = arguments+ ("%s=%s, " % (kw, repr(val)))
if arguments[-2:] == ", " :
arguments = arguments[:-2]
return arguments
class PDFAction :
"""Base class to fake method calls or attributes on PDF objects (Canvas, PDFPathObject, PDFTextObject)."""
def __init__(self, parent, action) :
"""Saves a pointer to the parent object, and the method name."""
self._parent = parent
self._action = action
def __getattr__(self, name) :
"""Probably a method call on an attribute, returns the real one."""
return getattr(getattr(self._parent._object, self._action), name)
def __call__(self, *args, **kwargs) :
"""The fake method is called, print it then call the real one."""
if not self._parent._parent._in :
self._precomment()
self._parent._parent._PyWrite(" %s.%s(%s)" % (self._parent._name, self._action, apply(buildargs, args, kwargs)))
self._postcomment()
self._parent._parent._in = self._parent._parent._in + 1
retcode = apply(getattr(self._parent._object, self._action), args, kwargs)
self._parent._parent._in = self._parent._parent._in - 1
return retcode
def __hash__(self) :
return hash(getattr(self._parent._object, self._action))
def __coerce__(self, other) :
"""Needed."""
return coerce(getattr(self._parent._object, self._action), other)
def _precomment(self) :
"""To be overriden."""
pass
def _postcomment(self) :
"""To be overriden."""
pass
class PDFObject :
"""Base class for PDF objects like PDFPathObject and PDFTextObject."""
_number = 0
def __init__(self, parent) :
"""Saves a pointer to the parent Canvas."""
self._parent = parent
self._initdone = 0
def __getattr__(self, name) :
"""The user's programs wants to call one of our methods or get an attribute, fake it."""
return PDFAction(self, name)
def __repr__(self) :
"""Returns the name used in the generated source code (e.g. 'p' or 't')."""
return self._name
def __call__(self, *args, **kwargs) :
"""Real object initialisation is made here, because now we've got the arguments."""
if not self._initdone :
self.__class__._number = self.__class__._number + 1
methodname = apply(self._postinit, args, kwargs)
self._parent._PyWrite("\n # create PDF%sObject number %i\n %s = %s.%s(%s)" % (methodname[5:], self.__class__._number, self._name, self._parent._name, methodname, apply(buildargs, args, kwargs)))
self._initdone = 1
return self
class Canvas :
"""Our fake Canvas class, which will intercept each and every method or attribute access."""
class TextObject(PDFObject) :
_name = "t"
def _postinit(self, *args, **kwargs) :
self._object = apply(textobject.PDFTextObject, (self._parent, ) + args, kwargs)
return "beginText"
class PathObject(PDFObject) :
_name = "p"
def _postinit(self, *args, **kwargs) :
self._object = apply(pathobject.PDFPathObject, args, kwargs)
return "beginPath"
class Action(PDFAction) :
"""Class called for every Canvas method call."""
def _precomment(self) :
"""Outputs comments before the method call."""
if self._action == "showPage" :
self._parent._PyWrite("\n # Ends page %i" % self._parent._pagenumber)
elif self._action == "saveState" :
state = {}
d = self._parent._object.__dict__
for name in self._parent._object.STATE_ATTRIBUTES:
state[name] = d[name]
self._parent._PyWrite("\n # Saves context level %i %s" % (self._parent._contextlevel, state))
self._parent._contextlevel = self._parent._contextlevel + 1
elif self._action == "restoreState" :
self._parent._contextlevel = self._parent._contextlevel - 1
self._parent._PyWrite("\n # Restores context level %i %s" % (self._parent._contextlevel, self._parent._object.state_stack[-1]))
elif self._action == "beginForm" :
self._parent._formnumber = self._parent._formnumber + 1
self._parent._PyWrite("\n # Begins form %i" % self._parent._formnumber)
elif self._action == "endForm" :
self._parent._PyWrite("\n # Ends form %i" % self._parent._formnumber)
elif self._action == "save" :
self._parent._PyWrite("\n # Saves the PDF document to disk")
def _postcomment(self) :
"""Outputs comments after the method call."""
if self._action == "showPage" :
self._parent._pagenumber = self._parent._pagenumber + 1
self._parent._PyWrite("\n # Begins page %i" % self._parent._pagenumber)
elif self._action in [ "endForm", "drawPath", "clipPath" ] :
self._parent._PyWrite("")
_name = "c"
def __init__(self, *args, **kwargs) :
"""Initialize and begins source code."""
self._parent = self # nice trick, isn't it ?
self._in = 0
self._contextlevel = 0
self._pagenumber = 1
self._formnumber = 0
self._footerpresent = 0
self._object = apply(canvas.Canvas, args, kwargs)
self._pyfile = cStringIO.StringIO()
self._PyWrite(PyHeader)
try :
del kwargs["filename"]
except KeyError :
pass
self._PyWrite(" # create the PDF document\n %s = Canvas(file, %s)\n\n # Begins page 1" % (self._name, apply(buildargs, args[1:], kwargs)))
def __nonzero__(self) :
"""This is needed by platypus' tables."""
return 1
def __str__(self) :
"""Returns the equivalent Python source code."""
if not self._footerpresent :
self._PyWrite(PyFooter)
self._footerpresent = 1
return self._pyfile.getvalue()
def __getattr__(self, name) :
"""Method or attribute access."""
if name == "beginPath" :
return self.PathObject(self)
elif name == "beginText" :
return self.TextObject(self)
else :
return self.Action(self, name)
def _PyWrite(self, pycode) :
"""Outputs the source code with a trailing newline."""
self._pyfile.write("%s\n" % pycode)
if __name__ == '__main__':
print 'For test scripts, look in reportlab/test'

Some files were not shown because too many files have changed in this diff Show More