--- a/reportlab/graphics/renderbase.py Wed Dec 07 13:30:14 2005 +0000
+++ b/reportlab/graphics/renderbase.py Wed Dec 07 21:52:33 2005 +0000
@@ -9,6 +9,7 @@
__version__=''' $Id $ '''
from reportlab.graphics.shapes import *
+from reportlab.lib.validators import DerivedValue
from reportlab import rl_config
def inverse(A):
@@ -55,47 +56,47 @@
invert matrixes when you pop."""
def __init__(self, defaults=None):
# one stack to keep track of what changes...
- self.__deltas = []
+ 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 = []
+ 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)
+ 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()
+ 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'])
+ 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)
+ 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]
+ 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()...'
@@ -112,19 +113,19 @@
def getState(self):
"returns the complete graphics state at this point"
- return self.__combined[-1]
+ return self._combined[-1]
def getCTM(self):
"returns the current transformation matrix at this point"""
- return self.__combined[-1]['ctm']
+ 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]
+ 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
+ self._combined[-1][key] = value
def testStateTracker():
print 'Testing state tracker'
@@ -179,6 +180,7 @@
def __init__(self):
self._tracker = StateTracker()
+ self._nodeStack = [] #track nodes visited
def undefined(self, operation):
raise ValueError, "%s operation not defined at superclass class=%s" %(operation, self.__class__)
@@ -195,7 +197,7 @@
#bounding box
if showBoundary: canvas.rect(x, y, drawing.width, drawing.height)
canvas.saveState()
- self.initState(x,y)
+ self.initState(x,y) #this is the push()
self.drawNode(drawing)
self.pop()
canvas.restoreState()
@@ -218,12 +220,32 @@
# Undefined here, but with closer analysis probably can be handled in superclass
self.undefined("drawNode")
+ def getStateValue(self, key):
+ """Return current state parameter for given key"""
+ currentState = self._tracker._combined[-1]
+ return currentState[key]
+
+ def fillDerivedValues(self, node):
+ """Examine a node for any values which are Derived,
+ and replace them with their calculated values.
+ Generally things may look at the drawing or their
+ parent.
+
+ """
+ for (key, value) in node.__dict__.items():
+ if isinstance(value, DerivedValue):
+ #just replace with default for key?
+ #print ' fillDerivedValues(%s)' % key
+ newValue = value.getValue(self, key)
+ #print ' got value of %s' % newValue
+ node.__dict__[key] = newValue
+
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'):
@@ -232,6 +254,9 @@
node._canvas = canvas
ocanvas = None
+ self.fillDerivedValues(node)
+
+
#draw the object, or recurse
if isinstance(node, Line):
self.drawLine(node)
@@ -270,6 +295,9 @@
canvas = getattr(self,'_canvas',None)
for node in group.getContents():
node = _expandUserNode(node,canvas)
+
+ #here is where we do derived values - this seems to get everything. Touch wood.
+ self.fillDerivedValues(node)
try:
if hasattr(node,'_canvas'):
ocanvas = 1
--- a/reportlab/graphics/shapes.py Wed Dec 07 13:30:14 2005 +0000
+++ b/reportlab/graphics/shapes.py Wed Dec 07 21:52:33 2005 +0000
@@ -569,8 +569,10 @@
height = AttrMapValue(isNumber,desc="Drawing height in points."),
canv = AttrMapValue(None),
background = AttrMapValue(isValidChildOrNone,desc="Background widget for the drawing"),
- hAlign = AttrMapValue(OneOf("LEFT", "RIGHT", "CENTER", "CENTRE"), desc="Alignment within parent document"),
- vAlign = AttrMapValue(OneOf("TOP", "BOTTOM", "CENTER", "CENTRE"), desc="Alignment within parent document"),
+ hAlign = AttrMapValue(OneOf("LEFT", "RIGHT", "CENTER", "CENTRE"), desc="Horizontal alignment within parent document"),
+ vAlign = AttrMapValue(OneOf("TOP", "BOTTOM", "CENTER", "CENTRE"), desc="Vertical alignment within parent document"),
+ #AR temporary hack to track back up.
+ #fontName = AttrMapValue(isStringOrNone),
renderScale = AttrMapValue(isNumber,desc="Global scaling for rendering"),
)
@@ -584,6 +586,7 @@
self.height = height
self.hAlign = 'LEFT'
self.vAlign = 'BOTTOM'
+ #self.fontName = 'Helvetica'
self.renderScale = 1.0
def _renderPy(self):
--- a/reportlab/graphics/testshapes.py Wed Dec 07 13:30:14 2005 +0000
+++ b/reportlab/graphics/testshapes.py Wed Dec 07 21:52:33 2005 +0000
@@ -196,7 +196,8 @@
D.add(Rect(220, 150, 60, 30, 10, 10, fillColor=purple)) #round corners
- D.add(String(10,50, 'Basic Shapes', fillColor=colors.black))
+ from reportlab.lib.validators import inherit
+ D.add(String(10,50, 'Basic Shapes', fillColor=colors.black, fontName=inherit))
return D
@@ -443,6 +444,31 @@
if maxx>400 or maxy>200: _,_,D = drawit(F,maxx,maxy)
return D
+##def getDrawing14():
+## """This tests inherited properties. Each font should be as it says."""
+## D = Drawing(400, 200)
+##
+## fontSize = 12
+## D.fontName = 'Courier'
+##
+## g1 = Group(
+## Rect(0, 0, 150, 20, fillColor=colors.yellow),
+## String(5, 5, 'Inherited Courier', fontName=inherit, 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
+
def getAllFunctionDrawingNames(doTTF=1):
"Get a list of drawing function names from somewhere."
@@ -459,10 +485,10 @@
return funcNames
def _evalFuncDrawing(name, D, l=None, g=None):
- try:
- d = eval(name + '()', g or globals(), l or locals())
- except:
- d = getFailedDrawing(name)
+ #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):
--- a/reportlab/lib/attrmap.py Wed Dec 07 13:30:14 2005 +0000
+++ b/reportlab/lib/attrmap.py Wed Dec 07 21:52:33 2005 +0000
@@ -3,7 +3,7 @@
#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.lib.validators import isAnything, _SequenceTypes, DerivedValue
from reportlab import rl_config
class CallableValue:
@@ -74,12 +74,18 @@
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__)
+ #we always allow the inherited values; they cannot
+ #be checked until draw time.
+ if isinstance(value, DerivedValue):
+ #let it through
+ pass
+ else:
+ try:
+ validate = map[name].validate
+ if not validate(value):
+ raise AttributeError, "Illegal assignment of '%s' to '%s' in class %s" % (value, name, obj.__class__.__name__)
+ except KeyError:
+ raise AttributeError, "Illegal attribute '%s' in class %s" % (name, obj.__class__.__name__)
obj.__dict__[name] = value
def _privateAttrMap(obj,ret=0):
--- a/reportlab/lib/validators.py Wed Dec 07 13:30:14 2005 +0000
+++ b/reportlab/lib/validators.py Wed Dec 07 21:52:33 2005 +0000
@@ -156,7 +156,7 @@
class _isValidChildOrNone(_isValidChild):
def test(self,x):
- return _isValidChild.test(self,x) or x is None
+ return x is None or _isValidChild.test(self,x)
class _isCallable(Validator):
def test(self, x):
@@ -239,6 +239,34 @@
def test(self,x):
return isinstance(x,self._klass)
+class DerivedValue:
+ """This is used for magic values which work themselves out.
+ An example would be an "inherit" property, so that one can have
+
+ drawing.chart.categoryAxis.labels.fontName = inherit
+
+ and pick up the value from the top of the drawing.
+ Validators will permit this provided that a value can be pulled
+ in which satisfies it. And the renderer will have special
+ knowledge of these so they can evaluate themselves.
+ """
+ def getValue(self, renderer, attr):
+ """Override this. The renderers will pass the renderer,
+ and the attribute name. Algorithms can then backtrack up
+ through all the stuff the renderer provides, including
+ a correct stack of parent nodes."""
+ return None
+
+class Inherit(DerivedValue):
+ def __repr__(self):
+ return "inherit"
+
+ def getValue(self, renderer, attr):
+ return renderer.getStateValue(attr)
+
+inherit = Inherit()
+
+
isAuto = Auto()
isBoolean = _isBoolean()
isString = _isString()