[reportlab-users] Inheritance for ParagraphStyles
Luc Saffre
reportlab-users@reportlab.com
Fri, 07 Mar 2003 16:25:40 +0200
This is a multi-part message in MIME format.
--------------000007020601010100000400
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit
Hello,
I modified PropertySet in reportlab/lib/styles.py because I wanted true
inheritance for ParagraphStyles.
- If, after creation of a StyleSheet1 using getSampleStyleSheet I
modify the textColor attribute of Style "Normal" to blue, then the
normal behaviour should be that also all children of "Normal" change
their textColor to blue (except if they assigned themselves an
explicit textColor). This is the main reason for my change.
- Besides this I thought it useful that one can also access the Styles
in a StyleSheet1 using "attribute" syntax. For example on can now
write::
stylesheet.Normal.spaceAfter = 6
which is equivament to the classic syntax::
stylesheet["Normal"].spaceAfter = 6
If there are application where this change could cause problems, please
let me know. I am willing to continue working on this project.
Andy, please consider taking my changes into the standard distribution
of the toolkit.
The current implementation has some possible drawbacks:
- Attribute lookup costs (theoretically) more at runtime. Before
starting a frenetic discussion about this, somebody should make some
performance tests so that we know about what we discuss.
- The "name" and "parent" instance attributes of PropertySet have been
renamed to _name and _parent. Code which accessed these attributes
must be modified.
- The refresh() method no longer exists. Those who used this method
will hopefully be glad about my changes.
Best regards
Luc
--------------000007020601010100000400
Content-Type: text/plain;
name="styles.py"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="styles.py"
#copyright ReportLab Inc. 2000
#see license.txt for license details
#history http://cvs.sourceforge.net/cgi-bin/cvsweb.cgi/reportlab/lib/styles.py?cvsroot=reportlab
#$Header: /cvsroot/reportlab/reportlab/lib/styles.py,v 1.15 2002/07/24 19:56:37 andy_robinson Exp $
__version__=''' $Id: styles.py,v 1.15 2002/07/24 19:56:37 andy_robinson Exp $ '''
from reportlab.lib.colors import white, black
from reportlab.lib.enums import TA_LEFT, TA_CENTER
"""
Changes made by Luc Saffre <luc.saffre@gmx.net> on 2003-03-07 :
I changed PropertySet because I wanted true inheritance.
- If, after creation of a StyleSheet1 using getSampleStyleSheet I
modify the textColor attribute of Style "Normal" to blue, then the
normal behaviour should be that also all children of "Normal" change
their textColor to blue (except if they assigned themselves an
explicit textColor). This is the main reason for my change.
- Besides this I thought it useful that one can also access the Styles
in a StyleSheet1 using "attribute" syntax. For example on can now
write::
stylesheet.Normal.spaceAfter = 6
which is equivament to the classic syntax::
stylesheet["Normal"].spaceAfter = 6
- The un-elegant limit that PropertySet may not contain a property
called "name" or "parent" has been removed.
The current implementation has two possible drawbacks:
- Attribute lookup costs (theoretically) more at runtime. Before
starting a frenetic discussion about this, somebody should make some
performance tests so that we know about what we discuss.
- The "name" and "parent" instance attributes of PropertySet have been
renamed to _name and _parent. Code which accessed these attributes
must be modified.
- The refresh() method no longer exists. Those who used this method
will hopefully be glad about my changes...
"""
###########################################################
# 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._props = kw
def __repr__(self):
return "<%s '%s'>" % (self.__class__.__name__, self._name)
def __getattr__(self,name):
try:
return self.__dict__["_props"][name]
except KeyError,e:
pass
if self.__dict__["_parent"] is not None:
try:
return getattr(self.__dict__["_parent"],name)
except KeyError,e:
pass
return self.defaults[name]
def listAttrs(self, indent=''):
print indent + 'name =', self._name
print indent + 'parent =', self._parent
keylist = self._props.keys()
keylist.sort()
for key in keylist:
value = self._props.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 __getattr__(self,name):
return self.__getitem__(name)
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
--------------000007020601010100000400--