[Checkins] SVN: z3c.rml/trunk/src/z3c/rml/ Change strategy of
parsing the XML and processing it to create the PDF. It
Stephan Richter
srichter at cosmos.phy.tufts.edu
Tue Mar 27 23:02:23 EDT 2007
Log message for revision 73805:
Change strategy of parsing the XML and processing it to create the PDF. It
is all interface and field based, which means that soon I will be able to
generate the reference documentation and DTD from the interfaces.
Changed:
D z3c.rml/trunk/src/z3c/rml/attr.py
A z3c.rml/trunk/src/z3c/rml/attrng.py
U z3c.rml/trunk/src/z3c/rml/canvas.py
U z3c.rml/trunk/src/z3c/rml/chart.py
A z3c.rml/trunk/src/z3c/rml/directive.py
U z3c.rml/trunk/src/z3c/rml/document.py
D z3c.rml/trunk/src/z3c/rml/element.py
U z3c.rml/trunk/src/z3c/rml/flowable.py
U z3c.rml/trunk/src/z3c/rml/form.py
U z3c.rml/trunk/src/z3c/rml/interfaces.py
A z3c.rml/trunk/src/z3c/rml/occurence.py
U z3c.rml/trunk/src/z3c/rml/page.py
U z3c.rml/trunk/src/z3c/rml/platypus.py
U z3c.rml/trunk/src/z3c/rml/special.py
U z3c.rml/trunk/src/z3c/rml/stylesheet.py
U z3c.rml/trunk/src/z3c/rml/template.py
A z3c.rml/trunk/src/z3c/rml/tests/input/tag-color.rml
U z3c.rml/trunk/src/z3c/rml/tests/input/tag-image-1.rml
U z3c.rml/trunk/src/z3c/rml/tests/input/tag-image.rml
A z3c.rml/trunk/src/z3c/rml/tests/input/tag-path.rml
A z3c.rml/trunk/src/z3c/rml/tests/input/tag-registerCidFont.rml
A z3c.rml/trunk/src/z3c/rml/tests/input/tag-registerTTFont.rml
A z3c.rml/trunk/src/z3c/rml/tests/input/tag-registerType1Face.rml
A z3c.rml/trunk/src/z3c/rml/tests/input/tag-textAnnotation.rml
U z3c.rml/trunk/src/z3c/rml/tests/test_rml.py
-=-
Deleted: z3c.rml/trunk/src/z3c/rml/attr.py
===================================================================
--- z3c.rml/trunk/src/z3c/rml/attr.py 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/attr.py 2007-03-28 03:02:22 UTC (rev 73805)
@@ -1,400 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2007 Zope Foundation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""RML-specific XML tools
-
-$Id$
-"""
-__docformat__ = "reStructuredText"
-import cStringIO
-import os
-import re
-import reportlab
-import reportlab.lib.colors
-import reportlab.lib.styles
-import reportlab.lib.units
-import reportlab.lib.utils
-import reportlab.lib.pagesizes
-import reportlab.graphics.widgets.markers
-import urllib
-from lxml import etree
-from xml.sax import saxutils
-
-from z3c.rml import interfaces
-
-DEFAULT = object()
-
-
-def getManager(context, interface):
- while (not interface.providedBy(context) and context is not None):
- context = context.parent
- if context is None:
- raise ValueError(
- 'Manager for %s could not be found.' %interface.getName())
- return context
-
-
-class Attribute(object):
-
- def __init__(self, name=None, default=DEFAULT):
- self.name = name
- self.default = default
-
- def convert(self, value, context=None):
- return value
-
- def get(self, element, default=DEFAULT, context=None):
- value = element.get(self.name, DEFAULT)
- if value is DEFAULT or value is default:
- if default is DEFAULT:
- return self.default
- return default
- return self.convert(value, context)
-
-class Combination(Attribute):
-
- def __init__(self, name=None, valueTypes=(), default=DEFAULT):
- super(Combination, self).__init__(name, default)
- self.valueTypes = valueTypes
-
- def convert(self, value, context=None):
- for valueType in self.valueTypes:
- try:
- return valueType.convert(value, context)
- except ValueError:
- pass
- raise ValueError(value)
-
-class Text(Attribute):
-
- def convert(self, value, context=None):
- return unicode(value)
-
-
-class Int(Attribute):
-
- def convert(self, value, context=None):
- return int(value)
-
-
-class Float(Attribute):
-
- def convert(self, value, context=None):
- return float(value)
-
-
-class StringOrInt(Attribute):
-
- def convert(self, value, context=None):
- try:
- return int(value)
- except ValueError:
- return str(value)
-
-
-class Sequence(Attribute):
-
- splitre = re.compile('[ \t\n,;]*')
- minLength = None
- maxLength = None
-
- def __init__(self, name=None, valueType=None, default=DEFAULT,
- splitre=None, minLength=None, maxLength=None, length=None):
- super(Sequence, self).__init__(name, default)
- self.valueType = valueType
- if minLength is not None:
- self.minLength = minLength
- if maxLength is not None:
- self.maxLength = maxLength
- if length is not None:
- self.minLength = self.maxLength = length
- if splitre is not None:
- self.splitre = splitre
-
- def convert(self, value, context=None):
- if value.startswith('(') and value.endswith(')'):
- value = value[1:-1]
- value = value.strip()
- values = self.splitre.split(value)
- result = [self.valueType.convert(value.strip(), context)
- for value in values]
- if ((self.minLength is not None and len(result) < self.minLength) and
- (self.maxLength is not None and len(result) > self.maxLength)):
- raise ValueError(
- 'Length of sequence must be at least %s and at most %i' % (
- self.minLength, self.maxLength))
- return result
-
-
-class BaseChoice(Attribute):
- choices = {}
-
- def convert(self, value, context=None):
- value = value.lower()
- if value in self.choices:
- return self.choices[value]
- raise ValueError(
- '%r not a valid value for attribute "%s"' % (value, self.name))
-
-
-class Choice(BaseChoice):
-
- def __init__(self, name=None, choices=None, default=DEFAULT):
- super(Choice, self).__init__(name, default)
- if isinstance(choices, (tuple, list)):
- choices = dict([(val.lower(), val) for val in choices])
- self.choices = choices
-
-
-class Bool(BaseChoice):
- choices = {'true': True, 'false': False,
- 'yes': True, 'no': False,
- '1': True, '0': False,
- }
-
-
-class DefaultBool(Bool):
- choices = Bool.choices.copy()
- choices.update({'default': None})
-
-
-class Measurement(Attribute):
-
- def __init__(self, name=None, default=DEFAULT,
- allowPercentage=False, allowStar=False):
- super(Measurement, self).__init__(name, default)
- self.allowPercentage = allowPercentage
- self.allowStar = allowStar
-
- units = [
- (re.compile('^(-?[0-9\.]+)\s*in$'), reportlab.lib.units.inch),
- (re.compile('^(-?[0-9\.]+)\s*cm$'), reportlab.lib.units.cm),
- (re.compile('^(-?[0-9\.]+)\s*mm$'), reportlab.lib.units.mm),
- (re.compile('^(-?[0-9\.]+)\s*$'), 1)
- ]
-
- allowPercentage = False
- allowStar = False
-
- def convert(self, value, context=None):
- if value == 'None':
- return None
- if value == '*' and self.allowStar:
- return value
- if value.endswith('%') and self.allowPercentage:
- return value
- for unit in self.units:
- res = unit[0].search(value, 0)
- if res:
- return unit[1]*float(res.group(1))
- raise ValueError('The value %r is not a valid measurement.' %value)
-
-class File(Text):
-
- open = staticmethod(urllib.urlopen)
- packageExtract = re.compile('^\[([0-9A-z_.]*)\]/(.*)$')
-
- doNotOpen = False
-
- def __init__(self, name=None, default=DEFAULT, doNotOpen=False):
- super(File, self).__init__(name, default)
- self.doNotOpen = doNotOpen
-
-
- def convert(self, value, context=None):
- # Check whether the value is of the form:
- # [<module.path>]/rel/path/image.gif"
- if value.startswith('['):
- result = self.packageExtract.match(value)
- if result is None:
- raise ValueError(
- 'The package-path-pair you specified was incorrect')
- modulepath, path = result.groups()
- module = __import__(modulepath, {}, {}, (modulepath))
- value = os.path.join(os.path.dirname(module.__file__), path)
- if self.doNotOpen:
- return value
- # Open/Download the file
- fileObj = self.open(value)
- sio = cStringIO.StringIO(fileObj.read())
- fileObj.close()
- sio.seek(0)
- return sio
-
-
-class Image(File):
-
- def __init__(self, name=None, default=DEFAULT, onlyOpen=False):
- super(Image, self).__init__(name, default)
- self.onlyOpen = onlyOpen
-
- def convert(self, value, context=None):
- fileObj = super(Image, self).convert(value, context)
- if self.onlyOpen:
- return fileObj
- return reportlab.lib.utils.ImageReader(fileObj)
-
-
-class Color(Text):
-
- def convert(self, value, context=None):
- if value == 'None':
- return None
- manager = getManager(context, interfaces.IColorsManager)
- if value in manager.colors:
- return manager.colors[value]
- return reportlab.lib.colors.toColor(value)
-
-
-class Style(Text):
-
- def __init__(self, name=None, default='Normal'):
- super(Style, self).__init__(name, default)
-
- def convert(self, value, context=None):
- manager = getManager(context, interfaces.IStylesManager)
- for styles in (manager.styles,
- reportlab.lib.styles.getSampleStyleSheet().byName):
- if value in styles:
- return styles[value]
- elif 'style.' + value in styles:
- return styles['style.' + value]
- elif value.startswith('style.') and value[6:] in styles:
- return styles[value[6:]]
- raise ValueError('Style %r could not be found.' %value)
-
- def get(self, element, default=DEFAULT, context=None):
- value = element.get(self.name, DEFAULT)
- if value is DEFAULT:
- if default is DEFAULT:
- return self.convert(self.default, context)
- elif default is None:
- return None
- return self.convert(default, context)
- return self.convert(value, context)
-
-
-class Symbol(Text):
-
- def convert(self, value, context=None):
- return reportlab.graphics.widgets.markers.makeMarker(value)
-
-class PageSize(Attribute):
-
- sizePair = Sequence(valueType=Measurement())
- words = Sequence(valueType=Attribute())
-
- def convert(self, value, context=None):
- # First try to get a pair
- try:
- return self.sizePair.convert(value, context)
- except ValueError:
- pass
- # Now we try to lookup a name. The following type of combinations must
- # work: "Letter" "LETTER" "A4 landscape" "letter portrait"
- words = self.words.convert(value, context)
- words = [word.lower() for word in words]
- # First look for the orientation
- orienter = None
- for orientation in ('landscape', 'portrait'):
- if orientation in words:
- orienter = getattr(reportlab.lib.pagesizes, orientation)
- words.remove(orientation)
- # We must have exactely one value left that matches a paper size
- pagesize = getattr(reportlab.lib.pagesizes, words[0].upper())
- # Now do the final touches
- if orienter:
- pagesize = orienter(pagesize)
- return pagesize
-
-
-class TextNode(Attribute):
- """Text ndoes are not really attributes, but behave mostly like it."""
-
- def __init__(self):
- super(TextNode, self).__init__('TEXT')
-
- def get(self, element, default=DEFAULT, context=None):
- if element.text is None:
- return u''
- return unicode(element.text).strip()
-
-class FirstLevelTextNode(TextNode):
- """Text ndoes are not really attributes, but behave mostly like it."""
-
- def __init__(self):
- super(TextNode, self).__init__('TEXT')
-
- def get(self, element, default=DEFAULT, context=None):
- text = element.text or u''
- for child in element.getchildren():
- text += child.tail or u''
- return text
-
-
-class TextNodeSequence(Sequence):
-
- def __init__(self, *args, **kw):
- super(TextNodeSequence, self).__init__('TEXT', *args, **kw)
-
- def get(self, element, default=DEFAULT, context=None):
- return self.convert(element.text, context)
-
-
-class TextNodeGrid(TextNodeSequence):
-
- def __init__(self, valueType=None, cols=None, default=DEFAULT):
- super(TextNodeSequence, self).__init__(
- 'TEXT', valueType, default, length=cols)
- self.cols = cols
-
- def convert(self, value, context=None):
- result = super(TextNodeGrid, self).convert(value, context)
- if len(result) % self.cols != 0:
- raise ValueError(
- 'Number of elements must be divisible by %i.' %self.cols)
- return [result[i*self.cols:(i+1)*self.cols]
- for i in range(len(result)/self.cols)]
-
-
-class RawXMLContent(Attribute):
-
- def __init__(self, default=DEFAULT):
- super(RawXMLContent, self).__init__('XML', default)
- # Do it in here, since we hace a recursive problem otherwise
- from z3c.rml import special
- self.handleElements = {'getName': special.GetName}
-
- def get(self, element, default=DEFAULT, context=None):
- # Replace what we can replace
- for subElement in element.iterdescendants():
- if subElement.tag in self.handleElements:
- substitute = self.handleElements[subElement.tag](
- subElement, context, None)
- substitute.process()
- # Now create the text
- text = saxutils.escape(element.text or u'')
- for child in element.getchildren():
- text += etree.tounicode(child)
- if text is None:
- if default is DEFAULT:
- return self.default
- return default
- return text
-
-
-class XMLContent(RawXMLContent):
-
- def get(self, element, default=DEFAULT, context=None):
- result = super(XMLContent, self).get(element, default, context)
- return result.strip().replace('\t', ' ')
Added: z3c.rml/trunk/src/z3c/rml/attrng.py
===================================================================
--- z3c.rml/trunk/src/z3c/rml/attrng.py 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/attrng.py 2007-03-28 03:02:22 UTC (rev 73805)
@@ -0,0 +1,383 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""RML Attribute Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import cStringIO
+import os
+import re
+import reportlab.graphics.widgets.markers
+import reportlab.lib.colors
+import reportlab.lib.pagesizes
+import reportlab.lib.styles
+import reportlab.lib.units
+import reportlab.lib.utils
+import urllib
+import zope.schema
+from lxml import etree
+from xml.sax import saxutils
+
+MISSING = object()
+
+def getManager(context, interface=None):
+ if interface is None:
+ # Avoid circular imports
+ from z3c.rml import interfaces
+ interface = interfaces.IManager
+ # Walk up the path until the manager is found
+ while (not interface.providedBy(context) and context is not None):
+ context = context.parent
+ # If no manager was found, raise an error
+ if context is None:
+ raise ValueError('The manager could not be found.')
+ return context
+
+
+class RMLAttribute(zope.schema.Field):
+ """An attribute of the RML directive."""
+
+ missing_value = MISSING
+ default = MISSING
+
+ def fromUnicode(self, ustr):
+ """See zope.schema.interfaces.IField"""
+ if self.context is None:
+ raise ValueError('Attribute not bound to a context.')
+ return super(RMLAttribute, self).fromUnicode(unicode(ustr))
+
+ def get(self):
+ """See zope.schema.interfaces.IField"""
+ value = self.context.element.get(self.__name__, self.missing_value)
+ if value is self.missing_value:
+ if self.default is not None:
+ return self.default
+ return self.missing_value
+ return self.fromUnicode(value)
+
+
+class BaseChoice(RMLAttribute):
+ choices = {}
+
+ def fromUnicode(self, value):
+ value = value.lower()
+ if value in self.choices:
+ return self.choices[value]
+ raise ValueError(
+ '%r not a valid value for attribute "%s"' % (value, self.__name__))
+
+
+class Combination(RMLAttribute):
+
+ def __init__(self, value_types=(), *args, **kw):
+ super(Combination, self).__init__(*args, **kw)
+ self.value_types = value_types
+
+ def fromUnicode(self, value):
+ for value_type in self.value_types:
+ bound = value_type.bind(self)
+ try:
+ return bound.fromUnicode(value)
+ except ValueError:
+ pass
+ raise ValueError(value)
+
+
+class String(RMLAttribute, zope.schema.Bytes):
+ """A simple Bytes string."""
+
+
+class Text(RMLAttribute, zope.schema.Text):
+ """A simple unicode string."""
+
+
+class Integer(RMLAttribute, zope.schema.Int):
+ # By making min and max simple attributes, we avoid some validation
+ # problems.
+ min = None
+ max = None
+
+
+class Float(RMLAttribute, zope.schema.Float):
+ # By making min and max simple attributes, we avoid some validation
+ # problems.
+ min = None
+ max = None
+
+
+class StringOrInt(RMLAttribute):
+
+ def fromUnicode(self, value):
+ try:
+ return int(value)
+ except ValueError:
+ return str(value)
+
+
+class Sequence(RMLAttribute, zope.schema._field.AbstractCollection):
+
+ splitre = re.compile('[ \t\n,;]*')
+
+ def __init__(self, splitre=None, *args, **kw):
+ super(Sequence, self).__init__(*args, **kw)
+ if splitre is not None:
+ self.splitre = splitre
+
+ def fromUnicode(self, ustr):
+ if ustr.startswith('(') and ustr.endswith(')'):
+ ustr = ustr[1:-1]
+ ustr = ustr.strip()
+ raw_values = self.splitre.split(ustr)
+ result = [self.value_type.bind(self.context).fromUnicode(raw.strip())
+ for raw in raw_values]
+ if ((self.min_length is not None and len(result) < self.min_length) and
+ (self.max_length is not None and len(result) > self.max_length)):
+ raise ValueError(
+ 'Length of sequence must be at least %s and at most %i' % (
+ self.min_length, self.max_length))
+ return result
+
+
+class Choice(BaseChoice):
+
+ def __init__(self, choices=None, *args, **kw):
+ super(Choice, self).__init__(*args, **kw)
+ if isinstance(choices, (tuple, list)):
+ choices = dict([(val.lower(), val) for val in choices])
+ self.choices = choices
+
+
+class Boolean(BaseChoice):
+ choices = {'true': True, 'false': False,
+ 'yes': True, 'no': False,
+ '1': True, '0': False,
+ }
+
+
+class BooleanWithDefault(Boolean):
+ choices = Boolean.choices.copy()
+ choices.update({'default': None})
+
+
+class Measurement(RMLAttribute):
+
+ def __init__(self, allowPercentage=False, allowStar=False, *args, **kw):
+ super(Measurement, self).__init__(*args, **kw)
+ self.allowPercentage = allowPercentage
+ self.allowStar = allowStar
+
+ units = [
+ (re.compile('^(-?[0-9\.]+)\s*in$'), reportlab.lib.units.inch),
+ (re.compile('^(-?[0-9\.]+)\s*cm$'), reportlab.lib.units.cm),
+ (re.compile('^(-?[0-9\.]+)\s*mm$'), reportlab.lib.units.mm),
+ (re.compile('^(-?[0-9\.]+)\s*$'), 1)
+ ]
+
+ allowPercentage = False
+ allowStar = False
+
+ def fromUnicode(self, value):
+ if value == 'None':
+ return None
+ if value == '*' and self.allowStar:
+ return value
+ if value.endswith('%') and self.allowPercentage:
+ return value
+ for unit in self.units:
+ res = unit[0].search(value, 0)
+ if res:
+ return unit[1]*float(res.group(1))
+ raise ValueError('The value %r is not a valid measurement.' %value)
+
+
+class File(Text):
+
+ open = staticmethod(urllib.urlopen)
+ packageExtract = re.compile('^\[([0-9A-z_.]*)\]/(.*)$')
+
+ doNotOpen = False
+
+ def __init__(self, doNotOpen=False, *args, **kw):
+ super(File, self).__init__(*args, **kw)
+ self.doNotOpen = doNotOpen
+
+ def fromUnicode(self, value):
+ # Check whether the value is of the form:
+ # [<module.path>]/rel/path/image.gif"
+ if value.startswith('['):
+ result = self.packageExtract.match(value)
+ if result is None:
+ raise ValueError(
+ 'The package-path-pair you specified was incorrect')
+ modulepath, path = result.groups()
+ module = __import__(modulepath, {}, {}, (modulepath))
+ value = os.path.join(os.path.dirname(module.__file__), path)
+ if self.doNotOpen:
+ return value
+ # Open/Download the file
+ fileObj = self.open(value)
+ sio = cStringIO.StringIO(fileObj.read())
+ fileObj.close()
+ sio.seek(0)
+ return sio
+
+
+class Image(File):
+
+ def __init__(self, onlyOpen=False, *args, **kw):
+ super(Image, self).__init__(*args, **kw)
+ self.onlyOpen = onlyOpen
+
+ def fromUnicode(self, value):
+ fileObj = super(Image, self).fromUnicode(value)
+ if self.onlyOpen:
+ return fileObj
+ return reportlab.lib.utils.ImageReader(fileObj)
+
+
+class Color(RMLAttribute):
+
+ def __init__(self, acceptNone=False, *args, **kw):
+ super(Color, self).__init__(*args, **kw)
+ self.acceptNone = acceptNone
+
+ def fromUnicode(self, value):
+ if self.acceptNone and value == 'None':
+ return None
+ manager = getManager(self.context)
+ if value in manager.colors:
+ return manager.colors[value]
+ return reportlab.lib.colors.toColor(value)
+
+
+class Style(String):
+
+ default = reportlab.lib.styles.getSampleStyleSheet().byName['Normal']
+
+ def fromUnicode(self, value):
+ manager = getManager(self.context)
+ for styles in (manager.styles,
+ reportlab.lib.styles.getSampleStyleSheet().byName):
+ if value in styles:
+ return styles[value]
+ elif 'style.' + value in styles:
+ return styles['style.' + value]
+ elif value.startswith('style.') and value[6:] in styles:
+ return styles[value[6:]]
+ raise ValueError('Style %r could not be found.' %value)
+
+
+class Symbol(Text):
+
+ def fromUnicode(self, value):
+ return reportlab.graphics.widgets.markers.makeMarker(value)
+
+
+class PageSize(RMLAttribute):
+
+ sizePair = Sequence(value_type=Measurement())
+ words = Sequence(value_type=String())
+
+ def fromUnicode(self, value):
+ # First try to get a pair
+ try:
+ return self.sizePair.fromUnicode(value)
+ except ValueError:
+ pass
+ # Now we try to lookup a name. The following type of combinations must
+ # work: "Letter" "LETTER" "A4 landscape" "letter portrait"
+ words = self.words.fromUnicode(value)
+ words = [word.lower() for word in words]
+ # First look for the orientation
+ orienter = None
+ for orientation in ('landscape', 'portrait'):
+ if orientation in words:
+ orienter = getattr(reportlab.lib.pagesizes, orientation)
+ words.remove(orientation)
+ # We must have exactely one value left that matches a paper size
+ pagesize = getattr(reportlab.lib.pagesizes, words[0].upper())
+ # Now do the final touches
+ if orienter:
+ pagesize = orienter(pagesize)
+ return pagesize
+
+
+class TextNode(RMLAttribute):
+ """Text nodes are not really attributes, but behave mostly like it."""
+
+ def get(self):
+ if self.context.element.text is None:
+ return u''
+ return unicode(self.context.element.text).strip()
+
+
+class FirstLevelTextNode(TextNode):
+ """Text ndoes are not really attributes, but behave mostly like it."""
+
+ def get(self):
+ text = self.context.element.text or u''
+ for child in self.context.element.getchildren():
+ text += child.tail or u''
+ return text.strip()
+
+
+class TextNodeSequence(Sequence):
+
+ def get(self):
+ return self.fromUnicode(self.context.element.text)
+
+
+class TextNodeGrid(TextNodeSequence):
+
+ def __init__(self, columns=None, *args, **kw):
+ super(TextNodeGrid, self).__init__(*args, **kw)
+ self.columns = columns
+
+ def fromUnicode(self, ustr):
+ result = super(TextNodeGrid, self).fromUnicode(ustr)
+ if len(result) % self.columns != 0:
+ raise ValueError(
+ 'Number of elements must be divisible by %i.' %self.columns)
+ return [result[i*self.columns:(i+1)*self.columns]
+ for i in range(len(result)/self.columns)]
+
+
+class RawXMLContent(RMLAttribute):
+
+ def __init__(self, *args, **kw):
+ super(RawXMLContent, self).__init__(*args, **kw)
+ # Do it in here, since we hace a recursive problem otherwise
+ from z3c.rml import special
+ self.handleElements = {'getName': special.GetName}
+
+ def get(self):
+ # Replace what we can replace
+ for subElement in self.context.element.iterdescendants():
+ if subElement.tag in self.handleElements:
+ substitute = self.handleElements[subElement.tag](
+ subElement, self.context)
+ substitute.process()
+ # Now create the text
+ text = saxutils.escape(self.context.element.text or u'')
+ for child in self.context.element.getchildren():
+ text += etree.tounicode(child)
+ return text
+
+
+class XMLContent(RawXMLContent):
+
+ def get(self):
+ result = super(XMLContent, self).get()
+ return result.strip().replace('\t', ' ')
Property changes on: z3c.rml/trunk/src/z3c/rml/attrng.py
___________________________________________________________________
Name: svn:keywords
+ Id
Modified: z3c.rml/trunk/src/z3c/rml/canvas.py
===================================================================
--- z3c.rml/trunk/src/z3c/rml/canvas.py 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/canvas.py 2007-03-28 03:02:22 UTC (rev 73805)
@@ -16,218 +16,474 @@
$Id$
"""
__docformat__ = "reStructuredText"
-import cStringIO
import zope.interface
import reportlab.pdfgen.canvas
-from z3c.rml import attr, element, flowable, interfaces, stylesheet
-from z3c.rml import chart, form, page
+from z3c.rml import attrng, directive, interfaces, occurence, stylesheet
+from z3c.rml import chart, flowable, form, page
-class DrawString(element.FunctionElement):
- functionName = 'drawString'
- args = (
- attr.Measurement('x'),
- attr.Measurement('y'),
- attr.TextNode())
+class IShape(interfaces.IRMLDirectiveSignature):
+ """A shape to be drawn on the canvas."""
+ x = attrng.Measurement(
+ title=u'X-Coordinate',
+ description=(u'The X-coordinate of the lower-left position of the '
+ u'shape.'),
+ required=True)
+ y = attrng.Measurement(
+ title=u'Y-Coordinate',
+ description=(u'The Y-coordinate of the lower-left position of the '
+ u'shape.'),
+ required=True)
+
+ fill = attrng.Boolean(
+ title=u'Fill',
+ description=u'A flag to specify whether the shape should be filled.',
+ required=False)
+
+ stroke = attrng.Boolean(
+ title=u'Stroke',
+ description=(u"A flag to specify whether the shape's outline should "
+ u"be drawn."),
+ required=False)
+
+
+class CanvasRMLDirective(directive.RMLDirective):
+ callable = None
+ attrMapping = None
+
+ def process(self):
+ kwargs = dict(self.getAttributeValues(attrMapping=self.attrMapping))
+ canvas = attrng.getManager(self, interfaces.ICanvasManager).canvas
+ getattr(canvas, self.callable)(**kwargs)
+
+
+class IDrawString(interfaces.IRMLDirectiveSignature):
+ """Draws a simple string (left aligned) onto the canvas at the specified
+ location."""
+
+ x = attrng.Measurement(
+ title=u'X-Coordinate',
+ description=(u'The X-coordinate of the lower-left position of the '
+ u'string.'),
+ required=True)
+
+ y = attrng.Measurement(
+ title=u'Y-Coordinate',
+ description=(u'The Y-coordinate of the lower-left position of the '
+ u'string.'),
+ required=True)
+
+ text = attrng.TextNode(
+ title=u'Text',
+ description=(u'The string/text that is put onto the canvas.'),
+ required=True)
+
+class DrawString(CanvasRMLDirective):
+ signature = IDrawString
+ callable = 'drawString'
+
+class IDrawRightString(IDrawString):
+ """Draws a simple string (right aligned) onto the canvas at the specified
+ location."""
+
class DrawRightString(DrawString):
- functionName = 'drawRightString'
+ signature = IDrawRightString
+ callable = 'drawRightString'
+class IDrawCenteredString(IDrawString):
+ """Draws a simple string (centered aligned) onto the canvas at the specified
+ location."""
+
class DrawCenteredString(DrawString):
- functionName = 'drawCentredString'
+ signature = IDrawCenteredString
+ callable = 'drawCentredString'
+class IDrawAlignedString(IDrawString):
+ """Draws a simple string (aligned to the pivot character) onto the canvas
+ at the specified location."""
+
+ pivotChar = attrng.Text(
+ title=u'Text',
+ description=(u'The string/text that is put onto the canvas.'),
+ min_length=1,
+ max_length=1,
+ default=u'.',
+ required=True)
+
class DrawAlignedString(DrawString):
- functionName = 'drawAlignedString'
- args = DrawString.args + (
- attr.Text('pivotChar', '.'), )
+ signature = IDrawAlignedString
+ callable = 'drawAlignedString'
-class Shape(element.FunctionElement):
- args = (
- attr.Measurement('x'),
- attr.Measurement('y') )
- kw = (
- ('fill', attr.Bool('fill')),
- ('stroke', attr.Bool('stroke')) )
+class IEllipse(IShape):
+ """Draws an ellipse on the canvas."""
+ width = attrng.Measurement(
+ title=u'Width',
+ description=u'The width of the ellipse.',
+ required=True)
-class Ellipse(Shape):
- functionName = 'ellipse'
- args = Shape.args + (
- attr.Measurement('width'),
- attr.Measurement('height') )
+ height = attrng.Measurement(
+ title=u'Height',
+ description=u'The height of the ellipse.',
+ required=True)
+class Ellipse(CanvasRMLDirective):
+ signature = IEllipse
+ callable = 'ellipse'
+ attrMapping = {'x': 'x1', 'y': 'y1'}
+
def process(self):
- args = self.getPositionalArguments()
- kw = self.getKeywordArguments()
+ kwargs = dict(self.getAttributeValues(attrMapping=self.attrMapping))
+ canvas = attrng.getManager(self, interfaces.ICanvasManager).canvas
# Convert width and height to end locations
- args[2] += args[0]
- args[3] += args[1]
- getattr(self.context, self.functionName)(*args, **kw)
+ kwargs['x2'] = kwargs['x1'] + kwargs['width']
+ del kwargs['width']
+ kwargs['y2'] = kwargs['y1'] + kwargs['height']
+ del kwargs['height']
+ getattr(canvas, self.callable)(**kwargs)
-class Circle(Shape):
- functionName = 'circle'
- args = Shape.args + (
- attr.Measurement('radius'), )
+class ICircle(IShape):
+ """Draws a circle on the canvas."""
-class Rectangle(Shape):
- functionName = 'rect'
- args = Shape.args + (
- attr.Measurement('width'),
- attr.Measurement('height') )
- kw = Shape.kw + (
- ('radius', attr.Measurement('round')),
- )
+ radius = attrng.Measurement(
+ title=u'Radius',
+ description=u'The radius of the circle.',
+ required=True)
+class Circle(CanvasRMLDirective):
+ signature = ICircle
+ callable = 'circle'
+ attrMapping = {'x': 'x_cen', 'y': 'y_cen', 'radius': 'r'}
+
+
+class IRectangle(IShape):
+ """Draws an ellipse on the canvas."""
+
+ width = attrng.Measurement(
+ title=u'Width',
+ description=u'The width of the rectangle.',
+ required=True)
+
+ height = attrng.Measurement(
+ title=u'Height',
+ description=u'The height of the rectangle.',
+ required=True)
+
+ round = attrng.Measurement(
+ title=u'Corner Radius',
+ description=u'The radius of the rounded corners.',
+ required=False)
+
+class Rectangle(CanvasRMLDirective):
+ signature = IRectangle
+ callable = 'rect'
+ attrMapping = {'round': 'radius'}
+
def process(self):
if 'round' in self.element.keys():
- self.functionName = 'roundRect'
+ self.callable = 'roundRect'
super(Rectangle, self).process()
-class Grid(element.FunctionElement):
- functionName = 'grid'
- args = (
- attr.Sequence('xs', attr.Measurement()),
- attr.Sequence('ys', attr.Measurement()) )
+class IGrid(interfaces.IRMLDirectiveSignature):
+ """A shape to be drawn on the canvas."""
+ xs = attrng.Sequence(
+ title=u'X-Coordinates',
+ description=(u'A sequence x-coordinates that represent the vertical '
+ u'line positions.'),
+ value_type=attrng.Measurement(),
+ required=True)
-class Lines(element.FunctionElement):
- functionName = 'lines'
- args = (
- attr.TextNodeGrid(attr.Measurement(), 4),
- )
+ ys = attrng.Sequence(
+ title=u'Y-Coordinates',
+ description=(u'A sequence y-coordinates that represent the horizontal '
+ u'line positions.'),
+ value_type=attrng.Measurement(),
+ required=True)
-class Curves(element.FunctionElement):
- functionName = 'bezier'
- args = (
- attr.TextNodeGrid(attr.Measurement(), 8),
- )
+class Grid(CanvasRMLDirective):
+ signature = IGrid
+ callable = 'grid'
+ attrMapping = {'xs': 'xlist', 'ys': 'ylist'}
+
+class ILines(interfaces.IRMLDirectiveSignature):
+ """A path of connected lines drawn on the canvas."""
+
+ linelist = attrng.TextNodeGrid(
+ title=u'Line List',
+ description=(u'A list of lines coordinates to draw.'),
+ value_type=attrng.Measurement(),
+ columns=4,
+ required=True)
+
+class Lines(CanvasRMLDirective):
+ signature = ILines
+ callable = 'lines'
+
+
+class ICurves(interfaces.IRMLDirectiveSignature):
+ """A path of connected bezier curves drawn on the canvas."""
+
+ curvelist = attrng.TextNodeGrid(
+ title=u'Curve List',
+ description=(u'A list of curve coordinates to draw.'),
+ value_type=attrng.Measurement(),
+ columns=8,
+ required=True)
+
+class Curves(CanvasRMLDirective):
+ signature = ICurves
+ callable = 'bezier'
+
def process(self):
- argset = self.getPositionalArguments()
- for args in argset[0]:
- getattr(self.context, self.functionName)(*args)
+ argset = self.getAttributeValues(valuesOnly=True)[0]
+ canvas = attrng.getManager(self, interfaces.ICanvasManager).canvas
+ for args in argset:
+ getattr(canvas, self.callable)(*args)
-class Image(element.FunctionElement):
- functionName = 'drawImage'
- args = (
- attr.Image('file'),
- attr.Measurement('x'),
- attr.Measurement('y') )
- kw = (
- ('width', attr.Measurement('width')),
- ('height', attr.Measurement('height')) )
+class IImage(interfaces.IRMLDirectiveSignature):
+ """Draws an external image on the canvas."""
+ file = attrng.Image(
+ title=u'File',
+ description=(u'Reference to the external file of the iamge.'),
+ required=True)
+
+ x = attrng.Measurement(
+ title=u'X-Coordinate',
+ description=(u'The X-coordinate of the lower-left position of the '
+ u'shape.'),
+ required=True)
+
+ y = attrng.Measurement(
+ title=u'Y-Coordinate',
+ description=(u'The Y-coordinate of the lower-left position of the '
+ u'shape.'),
+ required=True)
+
+ width = attrng.Measurement(
+ title=u'Width',
+ description=u'The width of the image.',
+ required=False)
+
+ height = attrng.Measurement(
+ title=u'Height',
+ description=u'The height of the image.',
+ required=False)
+
+ showBoundary = attrng.Boolean(
+ title=u'Show Boundary',
+ description=(u'A flag determining whether a border should be drawn '
+ u'around the image.'),
+ default=False,
+ required=False)
+
+ preserveAspectRatio = attrng.Boolean(
+ title=u'Preserve Aspect Ratio',
+ description=(u"A flag determining whether the image's aspect ration "
+ u"should be conserved under any circumstances."),
+ default=False,
+ required=False)
+
+class Image(CanvasRMLDirective):
+ signature = IImage
+ callable = 'drawImage'
+ attrMapping = {'file': 'image'}
+
def process(self):
- args = self.getPositionalArguments()
- kw = self.getKeywordArguments()
+ kwargs = dict(self.getAttributeValues(attrMapping=self.attrMapping))
+ preserve = kwargs.pop('preserveAspectRatio')
+ show = kwargs.pop('showBoundary')
- preserve = attr.Bool('preserveAspectRatio').get(self.element, False)
-
if preserve:
- imgX, imgY = args[0].getSize()
+ imgX, imgY = kwargs['image'].getSize()
# Scale image correctly, if width and/or height were specified
- if 'width' in kw and 'height' not in kw:
- kw['height'] = imgY * kw['width'] / imgX
- elif 'height' in kw and 'width' not in kw:
- kw['width'] = imgX * kw['height'] / imgY
- elif 'width' in kw and 'height' in kw:
- if float(kw['width']) / kw['height'] > float(imgX) / imgY:
- kw['width'] = imgX * kw['height'] / imgY
+ if 'width' in kwargs and 'height' not in kwargs:
+ kwargs['height'] = imgY * kwargs['width'] / imgX
+ elif 'height' in kwargs and 'width' not in kwargs:
+ kwargs['width'] = imgX * kwargs['height'] / imgY
+ elif 'width' in kwargs and 'height' in kwargs:
+ if float(kwargs['width'])/kwargs['height'] > float(imgX)/imgY:
+ kwargs['width'] = imgX * kwargs['height'] / imgY
else:
- kw['height'] = imgY * kw['width'] / imgX
+ kwargs['height'] = imgY * kwargs['width'] / imgX
- getattr(self.context, self.functionName)(*args, **kw)
+ canvas = attrng.getManager(self, interfaces.ICanvasManager).canvas
+ getattr(canvas, self.callable)(**kwargs)
- show = attr.Bool('showBoundary').get(self.element, False)
if show:
- width = kw.get('width', args[0].getSize()[0])
- height = kw.get('height', args[0].getSize()[1])
- self.context.rect(args[1], args[2], width, height)
+ width = kwargs.get('width', kwargs['image'].getSize()[0])
+ height = kwargs.get('height', kwargs['image'].getSize()[1])
+ canvas.rect(kwargs['x'], kwargs['y'], width, height)
-class Place(element.FunctionElement):
- args = (
- attr.Measurement('x'), attr.Measurement('y'),
- attr.Measurement('width'), attr.Measurement('height') )
+class IPlace(interfaces.IRMLDirectiveSignature):
+ """Draws a set of flowables on the canvas within a given region."""
+ x = attrng.Measurement(
+ title=u'X-Coordinate',
+ description=(u'The X-coordinate of the lower-left position of the '
+ u'place.'),
+ required=True)
+
+ y = attrng.Measurement(
+ title=u'Y-Coordinate',
+ description=(u'The Y-coordinate of the lower-left position of the '
+ u'place.'),
+ required=True)
+
+ width = attrng.Measurement(
+ title=u'Width',
+ description=u'The width of the place.',
+ required=False)
+
+ height = attrng.Measurement(
+ title=u'Height',
+ description=u'The height of the place.',
+ required=False)
+
+class Place(CanvasRMLDirective):
+ signature = IPlace
+
def process(self):
- x, y, width, height = self.getPositionalArguments()
+ x, y, width, height = self.getAttributeValues(
+ select=('x', 'y', 'width', 'height'), valuesOnly=True)
y += height
- flows = flowable.Flow(self.element, self.parent, self.context)
+ flows = flowable.Flow(self.element, self.parent)
flows.process()
+
+ canvas = attrng.getManager(self, interfaces.ICanvasManager).canvas
for flow in flows.flow:
flowWidth, flowHeight = flow.wrap(width, height)
if flowWidth <= width and flowHeight <= height:
y -= flowHeight
- flow.drawOn(self.context, x, y)
+ flow.drawOn(canvas, x, y)
height -= flowHeight
else:
raise ValueError("Not enough space")
-class Param(element.FunctionElement):
- args = (attr.Attribute('name'), attr.TextNode() )
+class IParam(interfaces.IRMLDirectiveSignature):
+ """Sets one paramter for the text annotation."""
+ name = attrng.String(
+ title=u'Name',
+ description=u'The name of the paramter.',
+ required=True)
+
+ value = attrng.TextNode(
+ title=u'Value',
+ description=(u'The parameter value.'),
+ required=True)
+
+class Param(directive.RMLDirective):
+ signature = IParam
+
def process(self):
- name, value = self.getPositionalArguments()
- self.context[name] = value
+ args = dict(self.getAttributeValues())
+ self.parent.params[args['name']] = args['value']
-class TextAnnotation(element.ContainerElement, element.FunctionElement):
- args = (attr.FirstLevelTextNode(), )
- paramTypes = {
- 'escape': attr.Int(),
- }
+class ITextAnnotation(interfaces.IRMLDirectiveSignature):
+ """Writes a low-level text annotation into the PDF."""
+ occurence.containing(
+ occurence.ZeroOrMore('param', IParam))
- subElements = {'param': Param}
+ contents = attrng.FirstLevelTextNode(
+ title=u'Contents',
+ description=u'The PDF commands that are inserted as annotation.',
+ required=True)
+class TextAnnotation(CanvasRMLDirective):
+ signature = ITextAnnotation
+ factories = {'param': Param}
+
+ paramTypes = {'escape': attrng.Integer()}
+
def process(self):
- contents = self.getPositionalArguments()[0]
- params = {}
- self.processSubElements(params)
+ contents = self.getAttributeValues(valuesOnly=True)[0]
+ self.params = {}
+ self.processSubDirectives()
for name, type in self.paramTypes.items():
- if name in params:
- params[name] = type.convert(params[name])
- self.context.textAnnotation(contents, **params)
+ if name in self.params:
+ bound = type.bind(self)
+ self.params[name] = bound.fromUnicode(self.params[name])
+ canvas = attrng.getManager(self, interfaces.ICanvasManager).canvas
+ canvas.textAnnotation(contents, **self.params)
-class MoveTo(element.FunctionElement):
- args = (
- attr.TextNodeSequence(attr.Measurement(), length=2),
- )
+class IMoveTo(interfaces.IRMLDirectiveSignature):
+ """Move the path cursor to the specified location."""
+ position = attrng.TextNodeSequence(
+ title=u'Position',
+ description=u'Position to which the path pointer is moved to.',
+ value_type=attrng.Measurement(),
+ min_length=2,
+ max_length=2,
+ required=True)
+
+class MoveTo(directive.RMLDirective):
+ signature = IMoveTo
+
def process(self):
- args = self.getPositionalArguments()
- self.context.moveTo(*args[0])
+ args = self.getAttributeValues(valuesOnly=True)
+ self.parent.path.moveTo(*args[0])
-class CurvesTo(Curves):
- functionName = 'curveTo'
- args = (
- attr.TextNodeGrid(attr.Measurement(), 6),
+
+class ICurvesTo(interfaces.IRMLDirectiveSignature):
+ """Create a bezier curve from the current location to the specified one."""
+
+ curvelist = attrng.TextNodeGrid(
+ title=u'Curve Specification',
+ description=u'Describes the end position and the curve properties.',
+ value_type=attrng.Measurement(),
+ columns=6,
+ required=True)
+
+class CurvesTo(directive.RMLDirective):
+ signature = ICurvesTo
+
+ def process(self):
+ argset = self.getAttributeValues(valuesOnly=True)[0]
+ for args in argset:
+ self.parent.path.curveTo(*args)
+
+class IPath(IShape):
+ """Create a line path."""
+ occurence.containing(
+ occurence.ZeroOrMore('moveTo', IMoveTo),
+ occurence.ZeroOrMore('curvesTo', ICurvesTo),
)
-class Path(element.FunctionElement):
- args = (
- attr.Measurement('x'),
- attr.Measurement('y') )
- kw = (
- ('close', attr.Bool('close')),
- ('fill', attr.Bool('fill')),
- ('stroke', attr.Bool('stroke')) )
+ points = attrng.TextNodeGrid(
+ title=u'Points',
+ description=(u'A list of coordinate points that define th path.'),
+ value_type=attrng.Measurement(),
+ columns=2,
+ required=True)
- points = attr.TextNodeGrid(attr.Measurement(), 2)
+ close = attrng.Boolean(
+ title=u'Close Path',
+ description=(u"A flag specifying whether the path should be closed."),
+ default=False,
+ required=False)
- subElements = {
+class Path(CanvasRMLDirective):
+ signature = IPath
+ factories = {
'moveto': MoveTo,
'curvesto': CurvesTo
}
@@ -235,107 +491,270 @@
def processPoints(self, text):
if text.strip() == '':
return
- for coords in self.points.convert(text):
+ bound = self.signature['points'].bind(self)
+ for coords in bound.fromUnicode(text):
self.path.lineTo(*coords)
def process(self):
- args = self.getPositionalArguments()
- kw = self.getKeywordArguments()
+ kwargs = dict(self.getAttributeValues(ignore=('points',)))
- self.path = self.context.beginPath()
- self.path.moveTo(*args)
+ # Start the path and set the cursor to the start location.
+ canvas = attrng.getManager(self, interfaces.ICanvasManager).canvas
+ self.path = canvas.beginPath()
+ self.path.moveTo(kwargs.pop('x'), kwargs.pop('y'))
+ # Process the text before the first sub-directive.
if self.element.text is not None:
self.processPoints(self.element.text)
- for subElement in self.element.getchildren():
- if subElement.tag in self.subElements:
- self.subElements[subElement.tag](
- subElement, self, self.path).process()
- if subElement.tail is not None:
- self.processPoints(subElement.tail)
+ # Handle each sub-directive.
+ for directive in self.element.getchildren():
+ if directive.tag in self.factories:
+ self.factories[directive.tag](directive, self).process()
+ # If there is more text after sub-directive, process it.
+ if directive.tail is not None:
+ self.processPoints(directive.tail)
- if kw.pop('close', False):
+ if kwargs.pop('close', False):
self.path.close()
- self.context.drawPath(self.path, **kw)
+ canvas.drawPath(self.path, **kwargs)
-class Fill(element.FunctionElement):
- functionName = 'setFillColor'
- args = (
- attr.Color('color'), )
+class IFill(interfaces.IRMLDirectiveSignature):
+ """Set the fill color."""
+ color = attrng.Color(
+ title=u'Color',
+ description=(u'The color value to be set.'),
+ required=True)
-class Stroke(element.FunctionElement):
- functionName = 'setStrokeColor'
- args = (
- attr.Color('color'), )
+class Fill(CanvasRMLDirective):
+ signature = IFill
+ callable = 'setFillColor'
+ attrMapping = {'color': 'aColor'}
-class SetFont(element.FunctionElement):
- functionName = 'setFont'
- args = (
- attr.Text('name'),
- attr.Measurement('size'), )
+class IStroke(interfaces.IRMLDirectiveSignature):
+ """Set the fill color."""
+ color = attrng.Color(
+ title=u'Color',
+ description=(u'The color value to be set.'),
+ required=True)
-class Scale(element.FunctionElement):
- functionName = 'scale'
- args = (attr.Float('sx'), attr.Float('sy'), )
+class Stroke(CanvasRMLDirective):
+ signature = IStroke
+ callable = 'setStrokeColor'
+ attrMapping = {'color': 'aColor'}
-class Translate(element.FunctionElement):
- functionName = 'translate'
- args = (attr.Measurement('dx', 0), attr.Measurement('dy', 0), )
+class ISetFont(interfaces.IRMLDirectiveSignature):
+ """Set the font name and/or size."""
+ name = attrng.String(
+ title=u'Font Name',
+ description=(u'The name of the font as it was registered.'),
+ required=True)
-class Rotate(element.FunctionElement):
- functionName = 'rotate'
- args = (attr.Float('degrees'), )
+ size = attrng.Measurement(
+ title=u'Size',
+ description=(u'The font size.'),
+ required=True)
+ leading = attrng.Measurement(
+ title=u'Leading',
+ description=(u'The font leading.'),
+ required=False)
-class Skew(element.FunctionElement):
- functionName = 'skew'
- args = (attr.Measurement('alpha'), attr.Measurement('beta'), )
+class SetFont(CanvasRMLDirective):
+ signature = ISetFont
+ callable = 'setFont'
+ attrMapping = {'name': 'psfontname'}
-class Transform(element.FunctionElement):
- functionName = 'transform'
- args = (attr.TextNodeSequence(attr.Float()), )
+class IScale(interfaces.IRMLDirectiveSignature):
+ """Scale the drawing using x and y sclaing factors."""
+ sx = attrng.Float(
+ title=u'X-Scaling-Factor',
+ description=(u'The scaling factor applied on x-coordinates.'),
+ required=True)
+
+ sy = attrng.Float(
+ title=u'Y-Scaling-Factor',
+ description=(u'The scaling factor applied on y-coordinates.'),
+ required=True)
+
+class Scale(CanvasRMLDirective):
+ signature = IScale
+ callable = 'scale'
+ attrMapping = {'sx': 'x', 'sy': 'y'}
+
+
+class ITranslate(interfaces.IRMLDirectiveSignature):
+ """Translate the drawing coordinates by the specified x and y offset."""
+
+ dx = attrng.Measurement(
+ title=u'X-Offset',
+ description=(u'The amount to move the drawing to the right.'),
+ required=True)
+
+ dy = attrng.Measurement(
+ title=u'Y-Offset',
+ description=(u'The amount to move the drawing upward.'),
+ required=True)
+
+class Translate(CanvasRMLDirective):
+ signature = ITranslate
+ callable = 'translate'
+
+
+class IRotate(interfaces.IRMLDirectiveSignature):
+ """Rotate the drawing counterclockwise."""
+
+ degrees = attrng.Measurement(
+ title=u'Angle',
+ description=(u'The angle in degrees.'),
+ required=True)
+
+class Rotate(CanvasRMLDirective):
+ signature = IRotate
+ callable = 'rotate'
+ attrMapping = {'degrees': 'theta'}
+
+
+class ISkew(interfaces.IRMLDirectiveSignature):
+ """Skew the drawing."""
+
+ alpha = attrng.Measurement(
+ title=u'Alpha',
+ description=(u'The amount to skew the drawing in the horizontal.'),
+ required=True)
+
+ beta = attrng.Measurement(
+ title=u'Beta',
+ description=(u'The amount to skew the drawing in the vertical.'),
+ required=True)
+
+class Skew(CanvasRMLDirective):
+ signature = ISkew
+ callable = 'skew'
+
+
+class ITransform(interfaces.IRMLDirectiveSignature):
+ """A full 2-D matrix transformation"""
+
+ matrix = attrng.TextNodeSequence(
+ title=u'Matrix',
+ description=u'The transformation matrix.',
+ value_type=attrng.Float(),
+ min_length=6,
+ max_length=6,
+ required=True)
+
+class Transform(CanvasRMLDirective):
+ signature = ITransform
+
def process(self):
- args = self.getPositionalArguments()
- getattr(self.context, self.functionName)(*args[0])
+ args = self.getAttributeValues(valuesOnly=True)
+ canvas = attrng.getManager(self, interfaces.ICanvasManager).canvas
+ canvas.transform(*args[0])
-class LineMode(element.FunctionElement):
- kw = (
- ('width', attr.Measurement('width')),
- ('dash', attr.Sequence('dash', attr.Measurement())),
- ('miterLimit', attr.Measurement('miterLimit')),
- ('join', attr.Choice(
- 'join', {'round': 1, 'mitered': 0, 'bevelled': 2})),
- ('cap', attr.Choice(
- 'cap', {'default': 0, 'round': 1, 'square': 2})),
- )
+class ILineMode(interfaces.IRMLDirectiveSignature):
+ """Set the line mode for the following graphics elements."""
+ width = attrng.Measurement(
+ title=u'Width',
+ description=(u'The line width.'),
+ required=False)
+
+ dash = attrng.Sequence(
+ title=u'Dash-Pattern',
+ description=(u'The dash-pattern of a line.'),
+ value_type=attrng.Measurement(),
+ required=False)
+
+ miterLimit = attrng.Measurement(
+ title=u'Miter Limit',
+ description=(u'The ???.'),
+ required=False)
+
+ join = attrng.Choice(
+ title=u'Join',
+ description=u'The way lines are joined together.',
+ choices=interfaces.JOIN_CHOICES,
+ required=False)
+
+ cap = attrng.Choice(
+ title=u'Cap',
+ description=u'The cap is the desciption of how the line-endings look.',
+ choices=interfaces.CAP_CHOICES,
+ required=False)
+
+class LineMode(CanvasRMLDirective):
+ signature = ILineMode
+
def process(self):
- kw = self.getKeywordArguments()
+ kw = dict(self.getAttributeValues())
+ canvas = attrng.getManager(self, interfaces.ICanvasManager).canvas
if 'width' in kw:
- self.context.setLineWidth(kw['width'])
+ canvas.setLineWidth(kw['width'])
if 'join' in kw:
- self.context.setLineJoin(kw['join'])
+ canvas.setLineJoin(kw['join'])
if 'cap' in kw:
- self.context.setLineCap(kw['cap'])
+ canvas.setLineCap(kw['cap'])
if 'miterLimit' in kw:
- self.context.setMiterLimit(kw['miterLimit'])
+ canvas.setMiterLimit(kw['miterLimit'])
if 'dash' in kw:
- self.context.setDash(kw['dash'])
+ canvas.setDash(kw['dash'])
-class Drawing(element.ContainerElement):
+class IDrawing(interfaces.IRMLDirectiveSignature):
+ """A container directive for all directives that draw directly on the
+ cnavas."""
+ occurence.containing(
+ occurence.ZeroOrMore('drawString', IDrawString),
+ occurence.ZeroOrMore('drawRightString', IDrawRightString),
+ occurence.ZeroOrMore('drawCenteredString', IDrawCenteredString),
+ occurence.ZeroOrMore('drawCentredString', IDrawCenteredString),
+ occurence.ZeroOrMore('drawAlignedString', IDrawAlignedString),
+ # Drawing Operations
+ occurence.ZeroOrMore('ellipse', IEllipse),
+ occurence.ZeroOrMore('circle', ICircle),
+ occurence.ZeroOrMore('rect', IRectangle),
+ occurence.ZeroOrMore('grid', IGrid),
+ occurence.ZeroOrMore('lines', ILines),
+ occurence.ZeroOrMore('curves', ICurves),
+ occurence.ZeroOrMore('image', IImage),
+ occurence.ZeroOrMore('place', IPlace),
+ occurence.ZeroOrMore('textAnnotation', ITextAnnotation),
+ occurence.ZeroOrMore('path', IPath),
+ # State Change Operations
+ occurence.ZeroOrMore('fill', IFill),
+ occurence.ZeroOrMore('stroke', IStroke),
+ occurence.ZeroOrMore('setFont', ISetFont),
+ occurence.ZeroOrMore('scale', IScale),
+ occurence.ZeroOrMore('translate', ITranslate),
+ occurence.ZeroOrMore('rotate', IRotate),
+ occurence.ZeroOrMore('skew', ISkew),
+ occurence.ZeroOrMore('transform', ITransform),
+ occurence.ZeroOrMore('lineMode', ILineMode),
+ # Form Field Elements
+ occurence.ZeroOrMore('barCode', form.IBarCode),
+ # Charts
+ #ZeroOrMore('barChart', IBarChart),
+ #ZeroOrMore('barChart3D', IBarChart3D),
+ #ZeroOrMore('linePlot', ILinePlot),
+ #ZeroOrMore('pieChart', IPieChart),
+ #ZeroOrMore('pieChart3D', IPieChart3D),
+ #ZeroOrMore('spiderChart', ISpiderChart),
+ )
- subElements = {
+class Drawing(directive.RMLDirective):
+ signature = IDrawing
+
+ factories = {
'drawString': DrawString,
'drawRightString': DrawRightString,
'drawCenteredString': DrawCenteredString,
@@ -374,58 +793,37 @@
}
+class IPageDrawing(IDrawing):
+ """Draws directly on the content of one page's canvas. Every call of this
+ directive creates a new page."""
+
+ occurence.containing(
+ #'mergePage': IMergePage,
+ *IDrawing.getTaggedValue('directives'))
+
class PageDrawing(Drawing):
+ signature = IDrawing
- subElements = Drawing.subElements.copy()
- subElements.update({
+ factories = Drawing.factories.copy()
+ factories.update({
'mergePage': page.MergePage
})
def process(self):
super(Drawing, self).process()
- self.context.showPage()
+ canvas = attrng.getManager(self, interfaces.ICanvasManager).canvas
+ canvas.showPage()
-class PageInfo(element.Element):
+class IPageInfo(interfaces.IRMLDirectiveSignature):
+ """Set's up page-global settings."""
- def process(self):
- pageSize = attr.PageSize('pageSize').get(self.element)
- self.context.setPageSize(pageSize)
+ pageSize = attrng.PageSize(
+ title=u'Page Size',
+ description=(u'The page size of all pages within this document.'),
+ required=True)
-
-class Canvas(element.ContainerElement):
- zope.interface.implements(interfaces.IPostProcessorManager)
-
- subElements = {
- 'stylesheet': stylesheet.Stylesheet,
- 'pageDrawing': PageDrawing,
- 'pageInfo': PageInfo,
- }
-
- def __init__(self, element, parent, context):
- super(Canvas, self).__init__(element, parent, context)
- self.postProcessors = []
-
- def process(self, outputFile):
- verbosity = attr.Bool('verbosity').get(self.element, 0)
- compression = attr.DefaultBool('compression').get(self.element, 0)
-
- # Create a temporary output file, so that post-processors can massage
- # the output
- tempOutput = cStringIO.StringIO()
-
- canvas = reportlab.pdfgen.canvas.Canvas(
- tempOutput,
- pageCompression=compression,
- verbosity=verbosity)
- self.processSubElements(canvas)
- canvas.save()
-
- # Process all post processors
- for name, processor in self.postProcessors:
- tempOutput.seek(0)
- tempOutput = processor.process(tempOutput)
-
- # Save the result into our real output file
- tempOutput.seek(0)
- outputFile.write(tempOutput.getvalue())
+class PageInfo(CanvasRMLDirective):
+ signature=IPageInfo
+ callable = 'setPageSize'
+ attrMapping = {'pageSize': 'size'}
Modified: z3c.rml/trunk/src/z3c/rml/chart.py
===================================================================
--- z3c.rml/trunk/src/z3c/rml/chart.py 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/chart.py 2007-03-28 03:02:22 UTC (rev 73805)
@@ -20,62 +20,94 @@
from reportlab.graphics import shapes
from reportlab.graphics.charts import barcharts, lineplots, piecharts
from reportlab.graphics.charts import spider, doughnut
-from z3c.rml import attr, element
+from z3c.rml import attrng, directive, interfaces, occurence
# Patches against Reportlab 2.0
lineplots.Formatter = reportlab.lib.formatters.Formatter
-class PropertyItem(element.Element):
- attrs = None
+class PropertyItem(directive.RMLDirective):
def process(self):
- attrs = element.extractAttributes(self.attrs, self.element, self)
- self.context.append(attrs)
+ attrs = dict(self.getAttributeValues())
+ self.parent.dataList.append(attrs)
-class PropertyCollection(element.ContainerElement):
+class PropertyCollection(directive.RMLDirective):
propertyName = None
- attrs = None
- subElements = None
def processAttributes(self):
- prop = getattr(self.context, self.propertyName)
+ prop = getattr(self.parent.context, self.propertyName)
# Get global properties
- attrs = element.extractAttributes(self.attrs, self.element, self)
- for name, value in attrs.items():
+ for name, value in self.getAttributeValues():
setattr(prop, name, value)
def process(self):
self.processAttributes()
# Get item specific properties
- prop = getattr(self.context, self.propertyName)
- dataList = []
- self.processSubElements(dataList)
- for index, data in enumerate(dataList):
+ prop = getattr(self.parent.context, self.propertyName)
+ self.dataList = []
+ self.processSubDirectives()
+ for index, data in enumerate(self.dataList):
for name, value in data.items():
setattr(prop[index], name, value)
-class Text(element.Element):
- attrs = (
- attr.Measurement('x'),
- attr.Measurement('y'),
- attr.Float('angle', 0),
- attr.TextNode(),
- attr.Measurement('fontSize'),
- attr.Color('fillColor'),
- attr.Text('fontName'),
- attr.Choice(
- 'textAnchor',
- ('start','middle','end','boxauto')),
- )
+class IText(interfaces.IRMLDirectiveSignature):
+ """Draw a text on the chart."""
+ x = attrng.Measurement(
+ title=u'X-Coordinate',
+ description=(u'The X-coordinate of the lower-left position of the '
+ u'text.'),
+ required=True)
+
+ y = attrng.Measurement(
+ title=u'Y-Coordinate',
+ description=(u'The Y-coordinate of the lower-left position of the '
+ u'text.'),
+ required=True)
+
+ angle = attrng.Float(
+ title=u'Rotation Angle',
+ description=(u'The angle about which the text will be rotated.'),
+ required=False)
+
+ text = attrng.TextNode(
+ title=u'Text',
+ description=u'The text to be printed.',
+ required=True)
+
+ fontName = attrng.String(
+ title=u'Font Name',
+ description=u'The name of the font.',
+ required=False)
+
+ fontSize = attrng.Measurement(
+ title=u'Font Size',
+ description=u'The font size for the text.',
+ required=False)
+
+ fillColor = attrng.Color(
+ title=u'Fill Color',
+ description=u'The color in which the text will appear.',
+ required=False)
+
+ textAnchor = attrng.Choice(
+ title=u'Text Anchor',
+ description=u'The position in the text to which the coordinates refer.',
+ choices=('start', 'middle', 'end', 'boxauto'),
+ required=False)
+
+
+class Text(directive.RMLDirective):
+ signature = IText
+
def process(self):
- attrs = element.extractAttributes(self.attrs, self.element, self)
+ attrs = dict(self.getAttributeValues())
string = shapes.String(
- attrs.pop('x'), attrs.pop('y'), attrs.pop('TEXT'))
- angle = attrs.pop('angle')
+ attrs.pop('x'), attrs.pop('y'), attrs.pop('text'))
+ angle = attrs.pop('angle', 0)
for name, value in attrs.items():
setattr(string, name, value)
group = shapes.Group(string)
@@ -84,29 +116,45 @@
self.parent.parent.drawing.add(group)
-class Texts(element.ContainerElement):
- subElements = {'text': Text}
+class ITexts(interfaces.IRMLDirectiveSignature):
+ """A set of texts drawn on the chart."""
+ occurence.containing(
+ occurence.ZeroOrMore('text', IText)
+ )
+class Texts(directive.RMLDirective):
+ signature = ITexts
+ factories = {'text': Text}
-class Series(element.Element):
- attrList = None
+class Series(directive.RMLDirective):
+
def process(self):
- attrs = element.extractPositionalArguments(
- self.attrList, self.element, self)
- self.context.append(attrs[0])
+ attrs = self.getAttributeValues(valuesOnly=True)
+ self.parent.data.append(attrs[0])
-class Data(element.ContainerElement):
+
+class Data(directive.RMLDirective):
series = None
def process(self):
- data = []
- self.subElements = {'series': self.series}
- self.processSubElements(data)
- self.context.data = data
+ self.data = []
+ self.factories = {'series': self.series}
+ self.processSubDirectives()
+ self.parent.context.data = self.data
+
+class ISeries1D(interfaces.IRMLDirectiveSignature):
+ """A one-dimensional series."""
+
+ values = attrng.TextNodeSequence(
+ title=u'Values',
+ description=u"Numerical values representing the series' data.",
+ value_type=attrng.Float(),
+ required=True)
+
class Series1D(Series):
- attrList = (attr.TextNodeSequence(attr.Float()),)
+ signature = ISeries1D
class Data1D(Data):
series = Series1D
@@ -114,414 +162,852 @@
class SingleData1D(Data1D):
def process(self):
- data = []
- self.subElements = {'series': self.series}
- self.processSubElements(data)
- self.context.data = data[0]
+ self.data = []
+ self.factories = {'series': self.series}
+ self.processSubDirectives()
+ self.parent.context.data = self.data[0]
+
+class ISeries2D(interfaces.IRMLDirectiveSignature):
+ """A two-dimensional series."""
+
+ values = attrng.TextNodeGrid(
+ title=u'Values',
+ description=u"Numerical values representing the series' data.",
+ value_type=attrng.Float(),
+ columns=2,
+ required=True)
+
class Series2D(Series):
- attrList = (attr.TextNodeGrid(attr.Float(), 2),)
+ signature = ISeries2D
class Data2D(Data):
series = Series2D
+class IBar(interfaces.IRMLDirectiveSignature):
+ """Define the look of a bar."""
+
+ strokeColor = attrng.Color(
+ title=u'Stroke Color',
+ description=u'The color in which the bar border is drawn.',
+ required=False)
+
+ strokeWidth = attrng.Measurement(
+ title=u'Stroke Width',
+ description=u'The width of the bar border line.',
+ required=False)
+
+ fillColor = attrng.Color(
+ title=u'Fill Color',
+ description=u'The color with which the bar is filled.',
+ required=False)
+
class Bar(PropertyItem):
- attrs = (
- attr.Color('strokeColor'),
- attr.Measurement('strokeWidth'),
- attr.Color('fillColor') )
+ signature = IBar
+class IBars(IBar):
+ """Collection of bar subscriptions."""
+ occurence.containing(
+ occurence.ZeroOrMore('bar', IBar)
+ )
class Bars(PropertyCollection):
+ signature = IBars
propertyName = 'bars'
- attrs = Bar.attrs
- subElements = {'bar': Bar}
+ factories = {'bar': Bar}
+class ILabelBase(interfaces.IRMLDirectiveSignature):
+
+ dx = attrng.Measurement(
+ title=u'Horizontal Extension',
+ description=(u'The width of the label.'),
+ required=False)
+
+ dy = attrng.Measurement(
+ title=u'Vertical Extension',
+ description=(u'The height of the label.'),
+ required=False)
+
+ angle = attrng.Float(
+ title=u'Angle',
+ description=(u'The angle to rotate the label.'),
+ required=False)
+
+ boxAnchor = attrng.Choice(
+ title=u'Box Anchor',
+ description=(u'The position relative to the label.'),
+ choices=('nw','n','ne','w','c','e','sw','s','se', 'autox', 'autoy'),
+ required=False)
+
+ boxStrokeColor = attrng.Color(
+ title=u'Box Stroke Color',
+ description=(u'The color of the box border line.'),
+ required=False)
+
+ boxStrokeWidth = attrng.Measurement(
+ title=u'Box Stroke Width',
+ description=u'The width of the box border line.',
+ required=False)
+
+ boxFillColor = attrng.Color(
+ title=u'Box Fill Color',
+ description=(u'The color in which the box is filled.'),
+ required=False)
+
+ boxTarget = attrng.Text(
+ title=u'Box Target',
+ description=u'The box target.',
+ required=False)
+
+ fillColor = attrng.Color(
+ title=u'Fill Color',
+ description=(u'The color in which the label is filled.'),
+ required=False)
+
+ strokeColor = attrng.Color(
+ title=u'Stroke Color',
+ description=(u'The color of the label.'),
+ required=False)
+
+ strokeWidth = attrng.Measurement(
+ title=u'Stroke Width',
+ description=u'The width of the label line.',
+ required=False)
+
+ frontName = attrng.String(
+ title=u'Font Name',
+ description=u'The font used to print the value.',
+ required=False)
+
+ frontSize = attrng.Measurement(
+ title=u'Font Size',
+ description=u'The size of the value text.',
+ required=False)
+
+ leading = attrng.Measurement(
+ title=u'Leading',
+ description=(u'The height of a single text line. It includes '
+ u'character height.'),
+ required=False)
+
+ width = attrng.Measurement(
+ title=u'Width',
+ description=u'The width the label.',
+ required=False)
+
+ maxWidth = attrng.Measurement(
+ title=u'Maximum Width',
+ description=u'The maximum width the label.',
+ required=False)
+
+ height = attrng.Measurement(
+ title=u'Height',
+ description=u'The height the label.',
+ required=False)
+
+ textAnchor = attrng.Choice(
+ title=u'Text Anchor',
+ description=u'The position in the text to which the coordinates refer.',
+ choices=('start', 'middle', 'end', 'boxauto'),
+ required=False)
+
+ visible = attrng.Boolean(
+ title=u'Visible',
+ description=u'A flag making the label text visible.',
+ required=False)
+
+ leftPadding = attrng.Measurement(
+ title=u'Left Padding',
+ description=u'The size of the padding on the left side.',
+ required=False)
+
+ rightPadding = attrng.Measurement(
+ title=u'Right Padding',
+ description=u'The size of the padding on the right side.',
+ required=False)
+
+ topPadding = attrng.Measurement(
+ title=u'Top Padding',
+ description=u'The size of the padding on the top.',
+ required=False)
+
+ bottomPadding = attrng.Measurement(
+ title=u'Bottom Padding',
+ description=u'The size of the padding on the bottom.',
+ required=False)
+
+
+class IPositionLabelBase(ILabelBase):
+
+ x = attrng.Measurement(
+ title=u'X-Coordinate',
+ description=(u'The X-coordinate of the lower-left position of the '
+ u'label.'),
+ required=False)
+
+ y = attrng.Measurement(
+ title=u'Y-Coordinate',
+ description=(u'The Y-coordinate of the lower-left position of the '
+ u'label.'),
+ required=False)
+
+
+class ILabel(IPositionLabelBase):
+ """A label for the chart."""
+
+ text = attrng.TextNode(
+ title=u'Text',
+ description=u'The label text to be displayed.',
+ required=True)
+
class Label(PropertyItem):
- attrs = (
- attr.Measurement('x'),
- attr.Measurement('y'),
- attr.Measurement('dx'),
- attr.Measurement('dy'),
- attr.Float('angle'),
- attr.Choice(
- 'boxAnchor',
- ('nw','n','ne','w','c','e','sw','s','se', 'autox', 'autoy')),
- attr.Color('boxStrokeColor'),
- attr.Measurement('boxStrokeWidth'),
- attr.Color('boxFillColor'),
- attr.Text('boxTarget'),
- attr.Color('fillColor'),
- attr.Color('strokeColor'),
- attr.Measurement('strokeWidth'),
- attr.Text('fontName'),
- attr.Measurement('fontSize'),
- attr.Measurement('leading'),
- attr.Measurement('width'),
- attr.Measurement('maxWidth'),
- attr.Measurement('height'),
- attr.Choice('textAnchor', ('start','middle','end','boxauto')),
- attr.Bool('visible'),
- attr.Measurement('topPadding'),
- attr.Measurement('leftPadding'),
- attr.Measurement('rightPadding'),
- attr.Measurement('bottomPadding'),
- attr.TextNode()
+ signature = ILabel
+
+
+class ILabels(IPositionLabelBase):
+ """A set of labels."""
+ occurence.containing(
+ occurence.ZeroOrMore('label', ILabel)
)
- attrs[-1].name = 'text'
-
class Labels(PropertyCollection):
+ signature = ILabels
propertyName = 'labels'
- attrs = Label.attrs[:-1]
- subElements = {'label': Label}
+ factories = {'label': Label}
-class Axis(element.ContainerElement):
- name = ''
- attrs = (
- attr.Bool('visible'),
- attr.Bool('visibleAxis'),
- attr.Bool('visibleTicks'),
- attr.Bool('visibleLabels'),
- attr.Bool('visibleGrid'),
- attr.Measurement('strokeWidth'),
- attr.Color('strokeColor'),
- attr.Sequence('strokeDashArray', attr.Float()),
- attr.Measurement('gridStrokeWidth'),
- attr.Color('gridStrokeColor'),
- attr.Sequence('gridStrokeDashArray', attr.Float()),
- attr.Measurement('gridStart'),
- attr.Measurement('gridEnd'),
- attr.Choice('style', ('parallel', 'stacked', 'parallel_3d')),
+class IAxis(interfaces.IRMLDirectiveSignature):
+ occurence.containing(
+ occurence.ZeroOrMore('labels', ILabels)
)
- subElements = {'labels': Labels}
+ visible = attrng.Boolean(
+ required=False)
+ visibleAxis = attrng.Boolean(
+ required=False)
+
+ visibleTicks = attrng.Boolean(
+ required=False)
+
+ visibleLabels = attrng.Boolean(
+ required=False)
+
+ visibleGrid = attrng.Boolean(
+ required=False)
+
+ strokeWidth = attrng.Measurement(
+ required=False)
+
+ strokeColor = attrng.Color(
+ required=False)
+
+ strokeDashArray = attrng.Sequence(
+ value_type=attrng.Float(),
+ required=False)
+
+ gridStrokeWidth = attrng.Measurement(
+ required=False)
+
+ gridStrokeColor = attrng.Color(
+ required=False)
+
+ gridStrokeDashArray = attrng.Sequence(
+ value_type=attrng.Float(),
+ required=False)
+
+ gridStart = attrng.Measurement(
+ required=False)
+
+ gridEnd = attrng.Measurement(
+ required=False)
+
+ style = attrng.Choice(
+ choices=('parallel', 'stacked', 'parallel_3d'),
+ required=False)
+
+
+class Axis(directive.RMLDirective):
+ signature = IAxis
+ name = ''
+ factories = {'labels': Labels}
+
def process(self):
- attrs = element.extractAttributes(self.attrs, self.element, self)
- axis = getattr(self.context, self.__name__)
- for name, value in attrs.items():
+ self.context = axis = getattr(self.parent.context, self.name)
+ for name, value in self.getAttributeValues():
setattr(axis, name, value)
- self.processSubElements(axis)
+ self.processSubDirectives()
-class Name(element.Element):
- attrs = (attr.TextNode(),)
+class IName(interfaces.IRMLDirectiveSignature):
+ text = attrng.TextNode(
+ title=u'Text',
+ required=True)
+
+class Name(directive.RMLDirective):
+ signature = IName
+
def process(self):
- attrs = element.extractAttributes(self.attrs, self.element, self)
- self.context.append(attrs['TEXT'])
+ text = self.getAttributeValues(valuesOnly=True)[0]
+ self.parent.names.append(text)
-class CategoryNames(element.ContainerElement):
- subElements = {'name': Name}
+class CategoryNames(directive.RMLDirective):
+ factories = {'name': Name}
def process(self):
- self.context.categoryNames = []
- self.processSubElements(self.context.categoryNames)
+ self.names = []
+ self.processSubDirectives()
+ self.parent.context.categoryNames = self.names
+
+class ICategoryAxis(IAxis):
+
+ categoryNames = attrng.Sequence(
+ value_type=attrng.Text(),
+ required=False)
+
+ joinAxis = attrng.Boolean(
+ required=False)
+
+ joinAxisPos = attrng.Measurement(
+ required=False)
+
+ reverseDirection = attrng.Boolean(
+ required=False)
+
+ labelAxisMode = attrng.Choice(
+ choices=('high', 'low', 'axis'),
+ required=False)
+
+ tickShift = attrng.Boolean(
+ required=False)
+
class CategoryAxis(Axis):
+ signature = ICategoryAxis
name = 'categoryAxis'
- attrs = Axis.attrs + (
- attr.Sequence('categoryNames', attr.Text()),
- attr.Bool('joinAxis'),
- attr.Measurement('joinAxisPos'),
- attr.Bool('reverseDirection'),
- attr.Choice('labelAxisMode', ('high', 'low', 'axis')),
- attr.Bool('tickShift'),
- )
- subElements = Axis.subElements.copy()
- subElements.update({
+ factories = Axis.factories.copy()
+ factories.update({
'categoryNames': CategoryNames,
})
+
+class IXCategoryAxis(ICategoryAxis):
+
+ tickUp = attrng.Measurement(
+ required=False)
+
+ tickDown = attrng.Measurement(
+ required=False)
+
+ joinAxisMode = attrng.Choice(
+ choices=('bottom', 'top', 'value', 'points', 'None'),
+ required=False)
+
class XCategoryAxis(CategoryAxis):
- attrs = CategoryAxis.attrs + (
- attr.Measurement('tickUp'),
- attr.Measurement('tickDown'),
- attr.Choice('joinAxisMode',
- ('bottom', 'top', 'value', 'points', 'None')) )
+ signature = IXCategoryAxis
+class IYCategoryAxis(ICategoryAxis):
+
+ tickLeft = attrng.Measurement(
+ required=False)
+
+ tickRight = attrng.Measurement(
+ required=False)
+
+ joinAxisMode = attrng.Choice(
+ choices=('bottom', 'top', 'value', 'points', 'None'),
+ required=False)
+
class YCategoryAxis(CategoryAxis):
- attrs = CategoryAxis.attrs + (
- attr.Measurement('tickLeft'),
- attr.Measurement('tickRight'),
- attr.Choice('joinAxisMode',
- ('bottom', 'top', 'value', 'points', 'None')) )
+ signature = IYCategoryAxis
+class IValueAxis(IAxis):
+
+ forceZero = attrng.Boolean(
+ required=False)
+
+ minimumTickSpacing = attrng.Measurement(
+ required=False)
+
+ maximumTicks = attrng.Integer(
+ required=False)
+
+ labelTextFormat = attrng.String(
+ required=False)
+
+ labelTextPostFormat = attrng.Text(
+ required=False)
+
+ labelTextScale = attrng.Float(
+ required=False)
+
+ valueMin = attrng.Float(
+ required=False)
+
+ valueMax = attrng.Float(
+ required=False)
+
+ valueStep = attrng.Float(
+ required=False)
+
+ valueSteps = attrng.Measurement(
+ required=False)
+
+ rangeRound = attrng.Text(
+ required=False)
+
+ zrangePref = attrng.Float(
+ required=False)
+
class ValueAxis(Axis):
+ signature = IValueAxis
name = 'valueAxis'
- attrs = Axis.attrs + (
- attr.Bool('forceZero'), # TODO: Support 'near'
- attr.Measurement('minimumTickSpacing'),
- attr.Int('maximumTicks'),
- attr.Attribute('labelTextFormat'),
- attr.Text('labelTextPostFormat'),
- attr.Float('labelTextScale'),
- attr.Float('valueMin'),
- attr.Float('valueMax'),
- attr.Float('valueStep'),
- attr.Measurement('valueSteps'),
- attr.Text('rangeRound'),
- attr.Float('zrangePref'),
- )
+class IXValueAxis(IValueAxis):
+
+ tickUp = attrng.Measurement(
+ required=False)
+
+ tickDown = attrng.Measurement(
+ required=False)
+
+ joinAxis = attrng.Boolean(
+ required=False)
+
+ joinAxisMode = attrng.Choice(
+ choices=('bottom', 'top', 'value', 'points', 'None'),
+ required=False)
+
+ joinAxisPos = attrng.Measurement(
+ required=False)
+
class XValueAxis(ValueAxis):
- attrs = ValueAxis.attrs + (
- attr.Measurement('tickUp'),
- attr.Measurement('tickDown'),
- attr.Bool('joinAxis'),
- attr.Choice('joinAxisMode',
- ('bottom', 'top', 'value', 'points', 'None')),
- attr.Float('joinAxisPos'),
- )
+ signature = IXValueAxis
+class LineXValueAxis(XValueAxis):
+ name = 'xValueAxis'
+
+class IYValueAxis(IValueAxis):
+
+ tickLeft = attrng.Measurement(
+ required=False)
+
+ tickRight = attrng.Measurement(
+ required=False)
+
+ joinAxis = attrng.Boolean(
+ required=False)
+
+ joinAxisMode = attrng.Choice(
+ choices=('bottom', 'top', 'value', 'points', 'None'),
+ required=False)
+
+ joinAxisPos = attrng.Measurement(
+ required=False)
+
class YValueAxis(ValueAxis):
- attrs = ValueAxis.attrs + (
- attr.Measurement('tickLeft'),
- attr.Measurement('tickRight'),
- attr.Bool('joinAxis'),
- attr.Choice('joinAxisMode',
- ('bottom', 'top', 'value', 'points', 'None')),
- attr.Float('joinAxisPos'),
- )
+ signature = IYValueAxis
+class LineYValueAxis(YValueAxis):
+ name = 'yValueAxis'
+
+class ILineBase(interfaces.IRMLDirectiveSignature):
+
+ strokeWidth = attrng.Measurement(
+ required=False)
+
+ strokeColor = attrng.Color(
+ required=False)
+
+ strokeDashArray = attrng.Sequence(
+ value_type = attrng.Float(),
+ required=False)
+
+ symbol = attrng.Symbol(
+ required=False)
+
+class ILine(ILineBase):
+
+ name = attrng.Text(
+ required=False)
+
class Line(PropertyItem):
- attrs = (
- attr.Measurement('strokeWidth'),
- attr.Color('strokeColor'),
- attr.Sequence('strokeDashArray', attr.Float()),
- attr.Symbol('symbol'),
- attr.Text('name'),
- )
+ signature = ILine
+class ILines(ILineBase):
+ pass
class Lines(PropertyCollection):
+ signature = ILines
propertyName = 'lines'
- attrs = Line.attrs[:-1]
- subElements = {'line': Line}
+ factories = {'line': Line}
+class ISliceLabel(ILabelBase):
+
+ text = attrng.TextNode(
+ title=u'Text',
+ description=u'The label text to be displayed.',
+ required=True)
+
class SliceLabel(Label):
- attrs = Label.attrs[2:]
+ signature = ISliceLabel
def process(self):
- attrs = element.extractAttributes(self.attrs, self.element, self)
- for name, value in attrs.items():
- self.context['label_'+name] = value
+ for name, value in self.getAttributeValues():
+ self.parent.context['label_'+name] = value
# Now we do not have simple labels anymore
- self.parent.parent.context.simpleLabels = False
+ self.parent.parent.parent.context.simpleLabels = False
-class SlicePointer(element.Element):
- attrs = (
- attr.Color('strokeColor'),
- attr.Measurement('strokeWidth'),
- attr.Measurement('elbowLength'),
- attr.Measurement('edgePad'),
- attr.Measurement('piePad'),
- )
+class ISlicePointer(interfaces.IRMLDirectiveSignature):
+
+ strokeColor = attrng.Color(
+ required=False)
+
+ strokeWidth = attrng.Measurement(
+ required=False)
+
+ elbowLength = attrng.Measurement(
+ required=False)
+
+ edgePad = attrng.Measurement(
+ required=False)
+
+ piePad = attrng.Measurement(
+ required=False)
+
+class SlicePointer(directive.RMLDirective):
+ signature = ISlicePointer
+
def process(self):
- attrs = element.extractAttributes(self.attrs, self.element, self)
- for name, value in attrs.items():
- self.context['label_pointer_'+name] = value
+ for name, value in self.getAttributeValues():
+ self.parent.context['label_pointer_'+name] = value
-class Slice(element.ContainerElement):
- attrs = (
- attr.Measurement('strokeWidth'),
- attr.Color('fillColor'),
- attr.Color('strokeColor'),
- attr.Sequence('strokeDashArray', attr.Float()),
- attr.Measurement('popout'),
- attr.Text('fontName'),
- attr.Measurement('fontSize'),
- attr.Measurement('labelRadius'),
- attr.Symbol('swatchMarker'),
- )
- subElements = {
+class ISliceBase(interfaces.IRMLDirectiveSignature):
+
+ strokeWidth = attrng.Measurement(
+ required=False)
+
+ fillColor = attrng.Color(
+ required=False)
+
+ strokeColor = attrng.Color(
+ required=False)
+
+ strokeDashArray = attrng.Sequence(
+ value_type=attrng.Float(),
+ required=False)
+
+ popout = attrng.Measurement(
+ required=False)
+
+ fontName = attrng.String(
+ required=False)
+
+ fontSize = attrng.Measurement(
+ required=False)
+
+ labelRadius = attrng.Measurement(
+ required=False)
+
+class ISlice(ISliceBase):
+
+ swatchMarker = attrng.Symbol(
+ required=False)
+
+
+class Slice(directive.RMLDirective):
+ signature = ISlice
+ factories = {
'label': SliceLabel,
'pointer': SlicePointer}
def process(self):
- attrs = element.extractAttributes(self.attrs, self.element, self)
- self.processSubElements(attrs)
- self.context.append(attrs)
+ self.context = attrs = dict(self.getAttributeValues())
+ self.processSubDirectives()
+ self.parent.context.append(attrs)
+
+class ISlice3D(ISlice):
+
+ fillColorShaded = attrng.Color(
+ required=False)
+
class Slice3D(Slice):
- attrs = Slice.attrs + (
- attr.Color('fillColorShaded'),
- )
-
+ signature = ISlice3D
subElements = {}
# Sigh, the 3-D Pie does not support advanced slice labels. :-(
# 'label': SliceLabel}
-class Slices(element.ContainerElement):
- attrs = Slice.attrs[:-1]
- subElements = {'slice': Slice}
+class ISlices(ISliceBase):
+ pass
+
+class Slices(directive.RMLDirective):
+ signature = ISlices
+ factories = {'slice': Slice}
+
def process(self):
# Get global slice properties
- attrs = element.extractAttributes(self.attrs, self.element, self)
- for name, value in attrs.items():
- setattr(self.context.slices, name, value)
+ for name, value in self.getAttributeValues():
+ setattr(self.parent.context.slices, name, value)
# Get slice specific properties
- slicesData = []
- self.processSubElements(slicesData)
+ self.context = slicesData = []
+ self.processSubDirectives()
for index, sliceData in enumerate(slicesData):
for name, value in sliceData.items():
- setattr(self.context.slices[index], name, value)
+ setattr(self.parent.context.slices[index], name, value)
+
+class ISlices3D(ISliceBase):
+
+ fillColorShaded = attrng.Color(
+ required=False)
+
class Slices3D(Slices):
- attrs = Slice3D.attrs[:-1]
- subElements = {'slice': Slice3D}
+ signature = ISlices3D
+ factories = {'slice': Slice3D}
-class SimpleLabels(element.ContainerElement):
- subElements = {'label': Name}
+class SimpleLabels(directive.RMLDirective):
+ factories = {'label': Name}
def process(self):
- self.context.labels = []
- self.processSubElements(self.context.labels)
+ self.names = []
+ self.processSubDirectives()
+ self.parent.context.labels = self.names
+class IStrandBase(interfaces.IRMLDirectiveSignature):
+
+ strokeWidth = attrng.Measurement(
+ required=False)
+
+ fillColor = attrng.Color(
+ required=False)
+
+ strokeColor= attrng.Color(
+ required=False)
+
+ strokeDashArray = attrng.Sequence(
+ value_type=attrng.Float(),
+ required=False)
+
+ symbol = attrng.Symbol(
+ required=False)
+
+ symbolSize = attrng.Measurement(
+ required=False)
+
+class IStrand(IStrandBase):
+
+ name = attrng.Text(
+ required=False)
+
class Strand(PropertyItem):
- attrs = (
- attr.Measurement('strokeWidth'),
- attr.Color('fillColor'),
- attr.Color('strokeColor'),
- attr.Sequence('strokeDashArray', attr.Float()),
- attr.Symbol('symbol'),
- attr.Measurement('symbolSize'),
- attr.Text('name'),
- )
+ signature = IStrand
-
class Strands(PropertyCollection):
+ signature = IStrandBase
propertyName = 'strands'
- attrs = Strand.attrs[:-1]
- subElements = {'strand': Strand}
+ attrs = IStrandBase
+ factories = {'strand': Strand}
+class IStrandLabelBase(ILabelBase):
+
+ _text = attrng.TextNode(
+ required=False)
+
+ row = attrng.Integer(
+ required=False)
+
+ col = attrng.Integer(
+ required=False)
+
+ format = attrng.String(
+ required=False)
+
+class IStrandLabel(IStrandLabelBase):
+
+ dR = attrng.Float(
+ required=False)
+
class StrandLabel(Label):
- attrs = Label.attrs[2:-1] + (attr.TextNode(),)
- attrs[-1].name = '_text'
- attrs += (
- attr.Int('row'),
- attr.Int('col'),
- attr.Attribute('format'),
- attr.Float('dR')
- )
+ signature = IStrandLabel
class StrandLabels(PropertyCollection):
+ signature = IStrandLabelBase
propertyName = 'strandLabels'
- attrs = StrandLabel.attrs[:-1]
- subElements = {'label': StrandLabel}
+ factories = {'label': StrandLabel}
def process(self):
self.processAttributes()
# Get item specific properties
- prop = getattr(self.context, self.propertyName)
- dataList = []
- self.processSubElements(dataList)
- for data in dataList:
+ prop = getattr(self.parent.context, self.propertyName)
+ self.dataList = []
+ self.processSubDirectives()
+ for data in self.dataList:
row = data.pop('row')
col = data.pop('col')
for name, value in data.items():
setattr(prop[row, col], name, value)
+class ISpoke(interfaces.IRMLDirectiveSignature):
+
+ strokeWidth = attrng.Measurement(
+ required=False)
+
+ fillColor = attrng.Color(
+ required=False)
+
+ strokeColor= attrng.Color(
+ required=False)
+
+ strokeDashArray = attrng.Sequence(
+ value_type=attrng.Float(),
+ required=False)
+
+ labelRadius = attrng.Measurement(
+ required=False)
+
+ visible = attrng.Measurement(
+ required=False)
+
class Spoke(PropertyItem):
- attrs = (
- attr.Measurement('strokeWidth'),
- attr.Color('fillColor'),
- attr.Color('strokeColor'),
- attr.Sequence('strokeDashArray', attr.Float()),
- attr.Measurement('labelRadius'),
- attr.Bool('visible'),
- )
+ signature = ISpoke
-
class Spokes(PropertyCollection):
+ signature = ISpoke
propertyName = 'spokes'
- attrs = Spoke.attrs[:-1]
- subElements = {'spoke': Spoke}
+ factories = {'spoke': Spoke}
+class ISpokeLabelBase(ILabelBase):
+ pass
+
+class ISpokeLabel(ISpokeLabelBase):
+
+ _text = attrng.TextNode(
+ required=False)
+
class SpokeLabel(Label):
- attrs = Label.attrs[2:-1] + (attr.TextNode(),)
- attrs[-1].name = '_text'
+ signature = ISpokeLabel
class SpokeLabels(PropertyCollection):
+ signature = ISpokeLabelBase
propertyName = 'spokeLabels'
- attrs = SpokeLabel.attrs[:-1]
- subElements = {'label': SpokeLabel}
+ factories = {'label': SpokeLabel}
-class Chart(element.ContainerElement):
- attrs = (
- # Drawing Options
- attr.Measurement('dx'),
- attr.Measurement('dy'),
- attr.Measurement('dwidth'),
- attr.Measurement('dheight'),
- attr.Float('angle'),
- # Plot Area Options
- attr.Measurement('x'),
- attr.Measurement('y'),
- attr.Measurement('width'),
- attr.Measurement('height'),
- attr.Color('strokeColor'),
- attr.Measurement('strokeWidth'),
- attr.Color('fillColor'),
- attr.Bool('debug'),
- )
+class IChart(interfaces.IRMLDirectiveSignature):
- subElements = {
+ # Drawing Options
+
+ dx = attrng.Measurement(
+ required=False)
+
+ dy = attrng.Measurement(
+ required=False)
+
+ dwidth = attrng.Measurement(
+ required=False)
+
+ dheight = attrng.Measurement(
+ required=False)
+
+ angle = attrng.Float(
+ required=False)
+
+ # Plot Area Options
+
+ x = attrng.Measurement(
+ required=False)
+
+ y = attrng.Measurement(
+ required=False)
+
+ width = attrng.Measurement(
+ required=False)
+
+ height = attrng.Measurement(
+ required=False)
+
+ strokeColor = attrng.Color(
+ required=False)
+
+ strokeWidth = attrng.Measurement(
+ required=False)
+
+ fillColor = attrng.Color(
+ required=False)
+
+ debug = attrng.Boolean(
+ required=False)
+
+class Chart(directive.RMLDirective):
+ signature = IChart
+ factories = {
'texts': Texts
}
- def getAttributes(self):
- attrs = [(attr.name, attr) for attr in self.attrs]
- return element.extractKeywordArguments(attrs, self.element, self)
-
def createChart(self, attributes):
raise NotImplementedError
def process(self):
- attrs = self.getAttributes()
+ attrs = dict(self.getAttributeValues())
angle = attrs.pop('angle', 0)
x, y = attrs.pop('dx'), attrs.pop('dy')
self.drawing = shapes.Drawing(attrs.pop('dwidth'), attrs.pop('dheight'))
- chart = self.createChart(attrs)
- self.processSubElements(chart)
+ self.context = chart = self.createChart(attrs)
+ self.processSubDirectives()
group = shapes.Group(chart)
group.translate(0,0)
group.rotate(angle)
self.drawing.add(group)
- self.drawing.drawOn(self.context, x, y)
+ manager = attrng.getManager(self, interfaces.ICanvasManager)
+ self.drawing.drawOn(manager.canvas, x, y)
+class IBarChart(IChart):
+
+ direction = attrng.Choice(
+ choices=('horizontal', 'vertical'),
+ default='horizontal',
+ required=False)
+
+ useAbsolute = attrng.Boolean(
+ default=False,
+ required=False)
+
+ barWidth = attrng.Measurement(
+ default=10,
+ required=False)
+
+ groupSpacing = attrng.Measurement(
+ default=5,
+ required=False)
+
+ barSpacing = attrng.Measurement(
+ default=0,
+ required=False)
+
class BarChart(Chart):
+ signature = IBarChart
nameBase = 'BarChart'
- attrs = Chart.attrs + (
- attr.Choice('direction', ('horizontal', 'vertical'), 'horizontal'),
- attr.Bool('useAbsolute', False),
- attr.Measurement('barWidth', 10),
- attr.Measurement('groupSpacing', 5),
- attr.Measurement('barSpacing', 0),
- )
-
- subElements = Chart.subElements.copy()
- subElements.update({
+ factories = Chart.factories.copy()
+ factories.update({
'data': Data1D,
'bars': Bars,
})
@@ -530,11 +1016,11 @@
direction = attrs.pop('direction')
# Setup sub-elements based on direction
if direction == 'horizontal':
- self.subElements['categoryAxis'] = YCategoryAxis
- self.subElements['valueAxis'] = XValueAxis
+ self.factories['categoryAxis'] = YCategoryAxis
+ self.factories['valueAxis'] = XValueAxis
else:
- self.subElements['categoryAxis'] = XCategoryAxis
- self.subElements['valueAxis'] = YValueAxis
+ self.factories['categoryAxis'] = XCategoryAxis
+ self.factories['valueAxis'] = YValueAxis
# Generate the chart
chart = getattr(
barcharts, direction.capitalize()+self.nameBase)()
@@ -543,30 +1029,48 @@
return chart
+class IBarChart3D(IBarChart):
+
+ theta_x = attrng.Float(
+ required=False)
+
+ theta_y = attrng.Float(
+ required=False)
+
+ zDepth = attrng.Measurement(
+ required=False)
+
+ zSpace = attrng.Measurement(
+ required=False)
+
class BarChart3D(BarChart):
+ signature = IBarChart3D
nameBase = 'BarChart3D'
- attrs = BarChart.attrs + (
- attr.Float('theta_x'),
- attr.Float('theta_y'),
- attr.Measurement('zDepth'),
- attr.Measurement('zSpace')
- )
+class ILinePlot(IChart):
+
+ reversePlotOrder = attrng.Boolean(
+ required=False)
+
+ lineLabelNudge = attrng.Measurement(
+ required=False)
+
+ lineLabelFormat = attrng.String(
+ required=False)
+
+ joinedLines = attrng.Boolean(
+ required=False)
+
class LinePlot(Chart):
- attrs = Chart.attrs + (
- attr.Bool('reversePlotOrder'),
- attr.Measurement('lineLabelNudge'),
- attr.Attribute('lineLabelFormat'),
- attr.Bool('joinedLines'),
- )
+ signature = ILinePlot
- subElements = Chart.subElements.copy()
- subElements.update({
+ factories = Chart.factories.copy()
+ factories.update({
'data': Data2D,
'lines': Lines,
- 'xValueAxis': XValueAxis,
- 'yValueAxis': YValueAxis,
+ 'xValueAxis': LineXValueAxis,
+ 'yValueAxis': LineYValueAxis,
'lineLabels': Labels,
})
@@ -577,24 +1081,45 @@
setattr(chart, name, value)
return chart
+
+class IPieChart(IChart):
+
+ startAngle = attrng.Integer(
+ required=False)
+
+ direction = attrng.Choice(
+ choices=('clockwise', 'anticlockwise'),
+ required=False)
+
+ checkLabelOverlap = attrng.Boolean(
+ required=False)
+
+ pointerLabelMode = attrng.Choice(
+ choices={'none': None,
+ 'leftright': 'LeftRight',
+ 'leftandright': 'LeftAndRight'},
+ required=False)
+
+ sameRadii = attrng.Boolean(
+ required=False)
+
+ orderMode = attrng.Choice(
+ choices=('fixed', 'alternate'),
+ required=False)
+
+ xradius = attrng.Measurement(
+ required=False)
+
+ yradius = attrng.Measurement(
+ required=False)
+
+
class PieChart(Chart):
+ signature = IPieChart
chartClass = piecharts.Pie
- attrs = Chart.attrs + (
- attr.Int('startAngle'),
- attr.Choice('direction', ('clockwise', 'anticlockwise')),
- attr.Bool('checkLabelOverlap'),
- attr.Choice('pointerLabelMode',
- {'none': None,
- 'leftright': 'LeftRight',
- 'leftandright': 'LeftAndRight'}),
- attr.Bool('sameRadii'),
- attr.Choice('orderMode', ('fixed', 'alternate')),
- attr.Measurement('xradius'),
- attr.Measurement('yradius'),
- )
- subElements = Chart.subElements.copy()
- subElements.update({
+ factories = Chart.factories.copy()
+ factories.update({
'data': SingleData1D,
'slices': Slices,
'labels': SimpleLabels,
@@ -607,28 +1132,41 @@
setattr(chart, name, value)
return chart
+
+class IPieChart3D(IPieChart):
+
+ perspective = attrng.Float(
+ required=False)
+
+ depth_3d = attrng.Measurement(
+ required=False)
+
+ angle_3d = attrng.Float(
+ required=False)
+
class PieChart3D(PieChart):
+ signature = IPieChart3D
chartClass = piecharts.Pie3d
- attrs = PieChart.attrs + (
- attr.Float('perspective'),
- attr.Measurement('depth_3d'),
- attr.Float('angle_3d'),
- )
- subElements = PieChart.subElements.copy()
- subElements.update({
+ factories = PieChart.factories.copy()
+ factories.update({
'slices': Slices3D,
})
+
+class ISpiderChart(IChart):
+
+ startAngle = attrng.Integer(
+ required=False)
+
+ direction = attrng.Choice(
+ choices=('clockwise', 'anticlockwise'),
+ required=False)
+
class SpiderChart(Chart):
- attrs = Chart.attrs + (
- attr.Int('startAngle'),
- attr.Choice('direction', ('clockwise', 'anticlockwise')),
- attr.Float('startAngle'),
- )
-
- subElements = Chart.subElements.copy()
- subElements.update({
+ signature = ISpiderChart
+ factories = Chart.factories.copy()
+ factories.update({
'data': Data1D,
'strands': Strands,
'strandLabels': StrandLabels,
Added: z3c.rml/trunk/src/z3c/rml/directive.py
===================================================================
--- z3c.rml/trunk/src/z3c/rml/directive.py 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/directive.py 2007-03-28 03:02:22 UTC (rev 73805)
@@ -0,0 +1,91 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""RML Directive Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import logging
+import zope.interface
+import zope.schema
+
+from z3c.rml import interfaces
+
+logger = logging.getLogger("z3c.rml")
+
+
+class RMLDirective(object):
+ zope.interface.implements(interfaces.IRMLDirective)
+ signature = None
+ factories = {}
+
+ def __init__(self, element, parent):
+ self.element = element
+ self.parent = parent
+
+ def getAttributeValues(self, ignore=None, select=None, attrMapping=None,
+ includeMissing=False, valuesOnly=False):
+ """See interfaces.IRMLDirective"""
+ items = []
+ for name, attr in zope.schema.getFieldsInOrder(self.signature):
+ # Only add the attribute to the list, if it is supposed there
+ if ((ignore is None or name not in ignore) and
+ (select is None or name in select)):
+ # Get the value.
+ value = attr.bind(self).get()
+ # If no value was found for a required field, raise a value
+ # error
+ if attr.required and value is attr.missing_value:
+ raise ValueError(
+ 'No value for required attribute %s' %name)
+ # Only add the entry if the value is not the missing value or
+ # missing values are requested to be included.
+ if value is not attr.missing_value or includeMissing:
+ items.append((name, value))
+
+ # Sort the items based on the section
+ if select is not None:
+ select = list(select)
+ items = sorted(items, key=lambda (n, v): select.index(n))
+
+ # If the attribute name does not match the internal API
+ # name, then convert the name to the internal one
+ if attrMapping:
+ items = [(attrMapping.get(name, name), value)
+ for name, value in items]
+
+ # Sometimes we only want the values without the names
+ if valuesOnly:
+ return [value for name, value in items]
+
+ return items
+
+ def processSubDirectives(self, select=None, ignore=None):
+ # Go through all children of the directive and try to process them.
+ for element in self.element.getchildren():
+ if select is not None and element.tag not in select:
+ continue
+ if ignore is not None and element.tag in ignore:
+ continue
+ # If the element is a directive, process it
+ if element.tag in self.factories:
+ directive = self.factories[element.tag](element, self)
+ directive.process()
+ else:
+ # Record any tags/elements that could not be processed.
+ logger.warn("Directive %r could not be processed and was "
+ "ignored." %element.tag)
+
+ def process(self):
+ self.processSubDirectives()
Property changes on: z3c.rml/trunk/src/z3c/rml/directive.py
___________________________________________________________________
Name: svn:keywords
+ Id
Modified: z3c.rml/trunk/src/z3c/rml/document.py
===================================================================
--- z3c.rml/trunk/src/z3c/rml/document.py 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/document.py 2007-03-28 03:02:22 UTC (rev 73805)
@@ -16,67 +16,143 @@
$Id$
"""
__docformat__ = "reStructuredText"
+import cStringIO
import sys
import zope.interface
+import reportlab.pdfgen.canvas
from reportlab.pdfbase import pdfmetrics, ttfonts, cidfonts
-from z3c.rml import attr, element, error, interfaces
+from z3c.rml import attrng, directive, interfaces, occurence
from z3c.rml import canvas, stylesheet, template
-class RegisterType1Face(element.Element):
- args = ( attr.Attribute('afmFile'), attr.Attribute('pfbFile') )
+class IRegisterType1Face(interfaces.IRMLDirectiveSignature):
+ """Register a new Type 1 font face."""
+ afmFile = attrng.String(
+ title=u'AFM File',
+ description=u'Path to AFM file used to register the Type 1 face.',
+ required=True)
+
+ pfbFile = attrng.String(
+ title=u'PFB File',
+ description=u'Path to PFB file used to register the Type 1 face.',
+ required=True)
+
+class RegisterType1Face(directive.RMLDirective):
+ signature = IRegisterType1Face
+
def process(self):
- args = element.extractPositionalArguments(self.args, self.element, self)
+ args = self.getAttributeValues(valuesOnly=True)
face = pdfmetrics.EmbeddedType1Face(*args)
pdfmetrics.registerTypeFace(face)
-class RegisterFont(element.Element):
- args = (
- attr.Attribute('name'),
- attr.Attribute('faceName'),
- attr.Attribute('encName') )
+class IRegisterFont(interfaces.IRMLDirectiveSignature):
+ """Register a new font based on a face and encoding."""
+ name = attrng.String(
+ title=u'Name',
+ description=(u'The name under which the font can be used in style '
+ u'declarations or other parameters that lookup a font.'),
+ required=True)
+
+ faceName = attrng.String(
+ title=u'Face Name',
+ description=(u'The name of the face the font uses. The face has to '
+ u'be previously registered.'),
+ required=True)
+
+ encName = attrng.String(
+ title=u'Encoding Name',
+ description=(u'The name of the encdoing to be used.'),
+ required=True)
+
+class RegisterFont(directive.RMLDirective):
+ signature = IRegisterFont
+
def process(self):
- args = element.extractPositionalArguments(self.args, self.element, self)
+ args = self.getAttributeValues(valuesOnly=True)
font = pdfmetrics.Font(*args)
pdfmetrics.registerFont(font)
-class RegisterTTFont(element.Element):
- args = (
- attr.Attribute('faceName'),
- attr.Attribute('fileName') )
+class IRegisterTTFont(interfaces.IRMLDirectiveSignature):
+ """Register a new TrueType font given the TT file and face name."""
+ faceName = attrng.String(
+ title=u'Face Name',
+ description=(u'The name of the face the font uses. The face has to '
+ u'be previously registered.'),
+ required=True)
+
+ fileName = attrng.String(
+ title=u'File Name',
+ description=u'File path of the of the TrueType font.',
+ required=True)
+
+class RegisterTTFont(directive.RMLDirective):
+ signature = IRegisterTTFont
+
def process(self):
- args = element.extractPositionalArguments(self.args, self.element, self)
+ args = self.getAttributeValues(valuesOnly=True)
font = ttfonts.TTFont(*args)
pdfmetrics.registerFont(font)
-class RegisterCidFont(element.Element):
- args = ( attr.Attribute('faceName'), )
+class IRegisterCidFont(interfaces.IRMLDirectiveSignature):
+ """Register a new CID font given the face name."""
+ faceName = attrng.String(
+ title=u'Face Name',
+ description=(u'The name of the face the font uses. The face has to '
+ u'be previously registered.'),
+ required=True)
+
+class RegisterCidFont(directive.RMLDirective):
+ signature = IRegisterCidFont
+
def process(self):
- args = element.extractPositionalArguments(self.args, self.element, self)
- pdfmetrics.registerFont(cidfonts.UnicodeCIDFont(*args))
+ args = self.getAttributeValues(valuesOnly=True)
+ font = cidfonts.UnicodeCIDFont(*args)
+ pdfmetrics.registerFont(font)
-class ColorDefinition(element.FunctionElement):
- args = (
- attr.Text('id'),
- attr.Color('RGB'), )
+class IColorDefinition(interfaces.IRMLDirectiveSignature):
+ """Define a new color and give it a name to be known under."""
+ id = attrng.String(
+ title=u'Id',
+ description=(u'The id/name the color will be available under.'),
+ required=True)
+
+ # XXX: This is really disgusting; need to rename to "color"!
+ # This is only here for compatibility with the original RML.
+ RGB = attrng.Color(
+ title=u'Color',
+ description=(u'The color value that is represented.'),
+ required=True)
+
+class ColorDefinition(directive.RMLDirective):
+ signature = IColorDefinition
+
def process(self):
- id, value = self.getPositionalArguments()
- manager = attr.getManager(self, interfaces.IColorsManager)
+ id, value = self.getAttributeValues(valuesOnly=True)
+ manager = attrng.getManager(self)
manager.colors[id] = value
-class DocInit(element.ContainerElement):
+class IDocInit(interfaces.IRMLDirectiveSignature):
+ occurence.containing(
+ occurence.ZeroOrMore('registerType1Face', IRegisterType1Face),
+ occurence.ZeroOrMore('registerFont', IRegisterFont),
+ occurence.ZeroOrMore('registerTTFont', IRegisterTTFont),
+ occurence.ZeroOrMore('registerCidFont', IRegisterCidFont),
+ occurence.ZeroOrMore('color', IColorDefinition),
+ )
- subElements = {
+class DocInit(directive.RMLDirective):
+ signature = IDocInit
+ factories = {
'registerType1Face': RegisterType1Face,
'registerFont': RegisterFont,
'registerTTFont': RegisterTTFont,
@@ -85,21 +161,56 @@
}
-class Document(element.ContainerElement):
- zope.interface.implements(
- interfaces.INamesManager,
- interfaces.IStylesManager,
- interfaces.IColorsManager)
+class IDocument(interfaces.IRMLDirectiveSignature):
+ occurence.containing(
+ occurence.ZeroOrOne('docinit', IDocInit),
+ )
- subElements = {
- 'docinit': DocInit
+ filename = attrng.String(
+ title=u'File Name',
+ description=(u'The default name of the output file, if no output '
+ u'file was provided.'),
+ required=True)
+
+ debug = attrng.Boolean(
+ title=u'Debug',
+ description=u'A flag to activate the debug output.',
+ required=False)
+
+ compression = attrng.BooleanWithDefault(
+ title=u'Compression',
+ description=(u'A flag determining whether page compression should '
+ u'be used.'),
+ required=False)
+
+ invariant = attrng.BooleanWithDefault(
+ title=u'Invariant',
+ description=(u'A flag that determines whether the produced PDF '
+ u'should be invariant with respect to the date and '
+ u'the exact contents.'),
+ required=False)
+
+class Document(directive.RMLDirective):
+ signature = IDocument
+ zope.interface.implements(interfaces.IManager,
+ interfaces.IPostProcessorManager,
+ interfaces.ICanvasManager)
+
+ factories = {
+ 'docinit': DocInit,
+ 'stylesheet': stylesheet.Stylesheet,
+ 'template': template.Template,
+ 'story': template.Story,
+ 'pageInfo': canvas.PageInfo,
+ 'pageDrawing': canvas.PageDrawing,
}
def __init__(self, element):
- self.element = element
+ super(Document, self).__init__(element, None)
self.names = {}
self.styles = {}
self.colors = {}
+ self.postProcessors = []
def process(self, outputFile=None):
"""Process document"""
@@ -107,10 +218,36 @@
# TODO: This is relative to the input file *not* the CWD!!!
outputFile = open(self.element.get('filename'), 'w')
- self.processSubElements(None)
+ # Create a temporary output file, so that post-processors can
+ # massage the output
+ self.outputFile = tempOutput = cStringIO.StringIO()
+ # Process common sub-directives
+ self.processSubDirectives(select=('docinit', 'stylesheet'))
+
+ # Handle Page Drawing Documents
if self.element.find('pageDrawing') is not None:
- canvas.Canvas(self.element, self, None).process(outputFile)
+ kwargs = dict(self.getAttributeValues(
+ select=('compression', 'debug'),
+ attrMapping={'compression': 'pageCompression',
+ 'debug': 'verbosity'}
+ ))
- if self.element.find('template') is not None:
- template.Template(self.element, self, None).process(outputFile)
+ self.canvas = reportlab.pdfgen.canvas.Canvas(tempOutput, **kwargs)
+ self.processSubDirectives(select=('pageInfo', 'pageDrawing'))
+ self.canvas.save()
+
+ # Handle Flowable-based documents.
+ elif self.element.find('template') is not None:
+ self.processSubDirectives(select=('template', 'story'))
+ self.doc.multiBuild(self.flowables)
+
+ # Process all post processors
+ for name, processor in self.postProcessors:
+ tempOutput.seek(0)
+ tempOutput = processor.process(tempOutput)
+
+ # Save the result into our real output file
+ tempOutput.seek(0)
+ outputFile.write(tempOutput.getvalue())
+
Deleted: z3c.rml/trunk/src/z3c/rml/element.py
===================================================================
--- z3c.rml/trunk/src/z3c/rml/element.py 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/element.py 2007-03-28 03:02:22 UTC (rev 73805)
@@ -1,93 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2007 Zope Foundation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
-# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
-# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
-# FOR A PARTICULAR PURPOSE.
-#
-##############################################################################
-"""Generic RML element
-
-$Id$
-"""
-__docformat__ = "reStructuredText"
-from z3c.rml import attr, error
-
-class Element(object):
-
- def __init__(self, element, parent, context):
- self.element = element
- self.parent = parent
- self.context = context
-
-class ContainerElement(Element):
-
- subElements = {}
- order = None
-
- def processSubElements(self, context):
- if self.order is not None:
- for tag in self.order:
- for element in self.element.findall(tag):
- self.subElements[tag](element, self, context).process()
- else:
- for subElement in self.element.getchildren():
- if subElement.tag in self.subElements:
- elem = self.subElements[subElement.tag](
- subElement, self, context)
- elem.__name__ = subElement.tag
- elem.process()
-
-
- def process(self):
- self.processSubElements(self.context)
-
-
-def extractAttributes(attrs, element, context=None):
- values = {}
- for Attr in attrs:
- value = Attr.get(element, context=context)
- if value is not attr.DEFAULT:
- values[Attr.name] = value
- return values
-
-
-def extractPositionalArguments(argsList, element, context=None):
- args = []
- for Attr in argsList:
- value = Attr.get(element, context=context)
- if value is attr.DEFAULT:
- raise error.RequiredAttributeMissing(element, Attr.name)
- args.append(value)
- return args
-
-def extractKeywordArguments(kwList, element, context=None):
- kw = {}
- for apiName, Attr in kwList:
- value = Attr.get(element, context=context)
- if value is not attr.DEFAULT:
- kw[apiName] = value
- return kw
-
-
-class FunctionElement(Element):
-
- functionName = None
- args = ()
- kw = ()
-
- def getPositionalArguments(self):
- return extractPositionalArguments(self.args, self.element, self)
-
- def getKeywordArguments(self):
- return extractKeywordArguments(self.kw, self.element, self)
-
- def process(self):
- args = self.getPositionalArguments()
- kw = self.getKeywordArguments()
- getattr(self.context, self.functionName)(*args, **kw)
Modified: z3c.rml/trunk/src/z3c/rml/flowable.py
===================================================================
--- z3c.rml/trunk/src/z3c/rml/flowable.py 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/flowable.py 2007-03-28 03:02:22 UTC (rev 73805)
@@ -18,12 +18,15 @@
__docformat__ = "reStructuredText"
import copy
import re
+import reportlab.lib.styles
import reportlab.platypus
import reportlab.platypus.doctemplate
import reportlab.platypus.flowables
import reportlab.platypus.tables
+import zope.schema
from reportlab.lib import styles
-from z3c.rml import attr, element, form, platypus, special, stylesheet
+from z3c.rml import attrng, directive, interfaces, occurence
+from z3c.rml import form, platypus, special, stylesheet
try:
import reportlab.graphics.barcode
@@ -34,45 +37,96 @@
reportlab.graphics.barcode = types.ModuleType('barcode')
reportlab.graphics.barcode.createBarcodeDrawing = None
-class Flowable(element.FunctionElement):
+class Flowable(directive.RMLDirective):
klass=None
+ attrMapping = None
def process(self):
- args = self.getPositionalArguments()
- kw = self.getKeywordArguments()
- self.parent.flow.append(self.klass(*args, **kw))
+ args = dict(self.getAttributeValues(attrMapping=self.attrMapping))
+ self.parent.flow.append(self.klass(**args))
+
+class ISpacer(interfaces.IRMLDirectiveSignature):
+ """Creates a vertical space in the flow."""
+
+ width = attrng.Measurement(
+ title=u'Width',
+ description=u'The width of the spacer. Currently not implemented.',
+ default=100,
+ required=False)
+
+ length = attrng.Measurement(
+ title=u'Length',
+ description=u'The height of the spacer.',
+ required=True)
+
class Spacer(Flowable):
+ signature = ISpacer
klass = reportlab.platypus.Spacer
- args = ( attr.Measurement('width', 100), attr.Measurement('length'), )
+ attrMapping = {'length': 'height'}
+class IIllustration(interfaces.IRMLDirectiveSignature):
+ """Inserts an illustration with graphics elements."""
+
+ width = attrng.Measurement(
+ title=u'Width',
+ description=u'The width of the illustration.',
+ required=True)
+
+ height = attrng.Measurement(
+ title=u'Height',
+ description=u'The height of the illustration.',
+ default=100,
+ required=True)
+
class Illustration(Flowable):
+ signature = IIllustration
klass = platypus.Illustration
- args = ( attr.Measurement('width'), attr.Measurement('height', 100))
def process(self):
- args = self.getPositionalArguments()
- self.parent.flow.append(self.klass(self, *args))
+ args = dict(self.getAttributeValues())
+ self.parent.flow.append(self.klass(self, **args))
+
+class IBarCodeFlowable(form.IBarCodeBase):
+ """Creates a bar code as a flowable."""
+
+ value = attrng.String(
+ title=u'Value',
+ description=u'The value represented by the code.',
+ required=True)
+
class BarCodeFlowable(Flowable):
+ signature = IBarCodeFlowable
klass = staticmethod(reportlab.graphics.barcode.createBarcodeDrawing)
- args = form.BarCode.args[:-1]
- kw = form.BarCode.kw[2:] + ( ('value', attr.Attribute('value')), )
+ attrMapping = {'code': 'codeName'}
-class Preformatted(Flowable):
- klass = reportlab.platypus.Preformatted
- args = ( attr.RawXMLContent(u''), attr.Style('style', 'Normal') )
+class IPluginFlowable(interfaces.IRMLDirectiveSignature):
+ """Inserts a custom flowable developed in Python."""
-class XPreformatted(Flowable):
- klass = reportlab.platypus.XPreformatted
- args = ( attr.RawXMLContent(u''), attr.Style('style', 'Normal') )
+ module = attrng.String(
+ title=u'Module',
+ description=u'The Python module in which the flowable is located.',
+ required=True)
+ function = attrng.String(
+ title=u'Function',
+ description=(u'The name of the factory function within the module '
+ u'that returns the custom flowable.'),
+ required=True)
+
+ params = attrng.TextNode(
+ title=u'Parameters',
+ description=(u'A list of parameters encoded as a long string.'),
+ required=False)
+
class PluginFlowable(Flowable):
- args = ( attr.Text('module'), attr.Text('function'), attr.TextNode())
+ signature = IPluginFlowable
def process(self):
- modulePath, functionName, text = self.getPositionalArguments()
+ modulePath, functionName, text = self.getAttributeValues(
+ valuesOnly=True)
module = __import__(modulePath, {}, {}, [modulePath])
function = getattr(module, functionName)
flowables = function(text)
@@ -81,294 +135,737 @@
self.parent.flow += list(flowables)
+class IMinimalParagraphBase(interfaces.IRMLDirectiveSignature):
+
+ style = attrng.Style(
+ title=u'Style',
+ description=(u'The paragraph style that is applied to the paragraph. '
+ u'See the ``paraStyle`` tag for creating a paragraph '
+ u'style.'),
+ default=reportlab.lib.styles.getSampleStyleSheet()['Normal'],
+ required=True)
+
+ bulletText = attrng.String(
+ title=u'Bullet Character',
+ description=(u'The bullet character is the ASCII representation of '
+ u'the symbol making up the bullet in a listing.'),
+ required=False)
+
+ dedent = attrng.Integer(
+ title=u'Dedent',
+ description=(u'Number of characters to be removed in front of every '
+ u'line of the text.'),
+ required=False)
+
+
+class IBold(interfaces.IRMLDirectiveSignature):
+ """Renders the text inside as bold."""
+
+class IItalic(interfaces.IRMLDirectiveSignature):
+ """Renders the text inside as italic."""
+
+class IUnderLine(interfaces.IRMLDirectiveSignature):
+ """Underlines the contained text."""
+
+class IBreak(interfaces.IRMLDirectiveSignature):
+ """Inserts a line break in the paragraph."""
+
+class IPageNumber(interfaces.IRMLDirectiveSignature):
+ """Inserts the current page number into the text."""
+
+class IParagraphBase(IMinimalParagraphBase):
+ occurence.containing(
+ occurence.ZeroOrMore('b', IBold),
+ occurence.ZeroOrMore('i', IItalic),
+ occurence.ZeroOrMore('u', IUnderLine),
+ occurence.ZeroOrMore('br', IBreak,
+ condition=occurence.laterThanReportlab21),
+ occurence.ZeroOrMore('pageNumber', IPageNumber)
+ )
+
+class IPreformatted(IMinimalParagraphBase):
+ """A preformatted text, similar to the <pre> tag in HTML."""
+
+ text = attrng.RawXMLContent(
+ title=u'Text',
+ description=(u'The text that will be layed out.'),
+ required=True)
+
+class Preformatted(Flowable):
+ signature = IPreformatted
+ klass = reportlab.platypus.Preformatted
+
+
+class IXPreformatted(IParagraphBase):
+ """A preformatted text that allows paragraph markup."""
+
+ text = attrng.RawXMLContent(
+ title=u'Text',
+ description=(u'The text that will be layed out.'),
+ required=True)
+
+class XPreformatted(Flowable):
+ signature = IXPreformatted
+ klass = reportlab.platypus.XPreformatted
+
+
+class IParagraph(IParagraphBase, stylesheet.IBaseParagraphStyle):
+ """Lays out an entire paragraph."""
+
+ text = attrng.XMLContent(
+ title=u'Text',
+ description=(u'The text that will be layed out.'),
+ required=True)
+
class Paragraph(Flowable):
+ signature = IParagraph
klass = reportlab.platypus.Paragraph
- args = ( attr.XMLContent(u''), attr.Style('style', 'Normal') )
- kw = ( ('bulletText', attr.Attribute('bulletText')), )
- styleAttrs = stylesheet.ParagraphStyle.attrs[3:]
+ styleAttributes = zope.schema.getFieldNames(stylesheet.IBaseParagraphStyle)
def processStyle(self, style):
- attrs = element.extractAttributes(self.styleAttrs, self.element, self)
+ attrs = self.getAttributeValues(select=self.styleAttributes)
if attrs:
style = copy.deepcopy(style)
- for name, value in attrs.items():
+ for name, value in attrs:
setattr(style, name, value)
return style
def process(self):
- args = self.getPositionalArguments()
- kw = self.getKeywordArguments()
- args[1] = self.processStyle(args[1])
- self.parent.flow.append(self.klass(*args, **kw))
+ args = dict(self.getAttributeValues(ignore=self.styleAttributes))
+ args['style'] = self.processStyle(args['style'])
+ self.parent.flow.append(self.klass(**args))
+
+class ITitle(IParagraph):
+ """The title is a simple paragraph with a special title style."""
+
+ style = attrng.Style(
+ title=u'Style',
+ description=(u'The paragraph style that is applied to the paragraph. '
+ u'See the ``paraStyle`` tag for creating a paragraph '
+ u'style.'),
+ default=reportlab.lib.styles.getSampleStyleSheet()['Title'],
+ required=True)
+
class Title(Paragraph):
- args = ( attr.XMLContent(u''), attr.Style('style', 'Title'), )
+ signature = ITitle
+
+class IHeading1(IParagraph):
+ """Heading 1 is a simple paragraph with a special heading 1 style."""
+
+ style = attrng.Style(
+ title=u'Style',
+ description=(u'The paragraph style that is applied to the paragraph. '
+ u'See the ``paraStyle`` tag for creating a paragraph '
+ u'style.'),
+ default=reportlab.lib.styles.getSampleStyleSheet()['Heading1'],
+ required=True)
+
class Heading1(Paragraph):
- args = ( attr.XMLContent(u''), attr.Style('style', 'Heading1'), )
+ signature = IHeading1
+
+class IHeading2(IParagraph):
+ """Heading 2 is a simple paragraph with a special heading 2 style."""
+
+ style = attrng.Style(
+ title=u'Style',
+ description=(u'The paragraph style that is applied to the paragraph. '
+ u'See the ``paraStyle`` tag for creating a paragraph '
+ u'style.'),
+ default=reportlab.lib.styles.getSampleStyleSheet()['Heading2'],
+ required=True)
+
class Heading2(Paragraph):
- args = ( attr.XMLContent(u''), attr.Style('style', 'Heading2'), )
+ signature = IHeading2
+
+class IHeading3(IParagraph):
+ """Heading 3 is a simple paragraph with a special heading 3 style."""
+
+ style = attrng.Style(
+ title=u'Style',
+ description=(u'The paragraph style that is applied to the paragraph. '
+ u'See the ``paraStyle`` tag for creating a paragraph '
+ u'style.'),
+ default=reportlab.lib.styles.getSampleStyleSheet()['Heading3'],
+ required=True)
+
class Heading3(Paragraph):
- args = ( attr.XMLContent(u''), attr.Style('style', 'Heading3'), )
+ signature = IHeading3
-class TableCell(element.Element):
- styleAttrs = (
- ('FONTNAME', (attr.Text('fontName'),)),
- ('FONTSIZE', (attr.Measurement('fontSize'),)),
- ('TEXTCOLOR', (attr.Color('fontColor'),)),
- ('LEADING', (attr.Measurement('leading'),)),
- ('LEFTPADDING', (attr.Measurement('leftPadding'),)),
- ('RIGHTPADDING', (attr.Measurement('rightPadding'),)),
- ('TOPPADDING', (attr.Measurement('topPadding'),)),
- ('BOTTOMPADDING', (attr.Measurement('bottomPadding'),)),
- ('BACKGROUND', (attr.Color('background'),)),
- ('ALIGNMENT', (attr.Choice('align',
- {'left': 'LEFT', 'right': 'RIGHT',
- 'center': 'CENTER', 'decimal': 'DECIMAL'}),)),
- ('VALIGN', (attr.Choice('vAlign',
- {'top': 'TOP', 'middle': 'MIDDLE',
- 'bottom': 'BOTTOM'}), )),
- ('LINEBELOW', (attr.Measurement('lineBelowThickness'),
- attr.Color('lineBelowColor'),
- attr.Choice('lineBelowCap',
- {'butt': 0, 'round': 1, 'square': 2}),
- attr.Int('lineBelowCount'),
- attr.Measurement('lineBelowSpace'))),
- ('LINEABOVE', (attr.Measurement('lineAboveThickness'),
- attr.Color('lineAboveColor'),
- attr.Choice('lineAboveCap',
- {'butt': 0, 'round': 1, 'square': 2}),
- attr.Int('lineAboveCount'),
- attr.Measurement('lineAboveSpace'))),
- ('LINEBEFORE', (attr.Measurement('lineLeftThickness'),
- attr.Color('lineLeftColor'),
- attr.Choice('lineLeftCap',
- {'butt': 0, 'round': 1, 'square': 2}),
- attr.Int('lineLeftCount'),
- attr.Measurement('lineLeftSpace'))),
- ('LINEAFTER', (attr.Measurement('lineRightThickness'),
- attr.Color('lineRightColor'),
- attr.Choice('lineRightCap',
- {'butt': 0, 'round': 1, 'square': 2}),
- attr.Int('lineRightCount'),
- attr.Measurement('lineRightSpace'))),
+class ITableCell(interfaces.IRMLDirectiveSignature):
+ """A table cell within a table."""
+
+ content = attrng.RawXMLContent(
+ title=u'Content',
+ description=(u'The content of the cell; can be text or any flowable.'),
+ required=True)
+
+ fontName = attrng.String(
+ title=u'Font Name',
+ description=u'The name of the font for the cell.',
+ required=False)
+
+ fontSize = attrng.Measurement(
+ title=u'Font Size',
+ description=u'The font size for the text of the cell.',
+ required=False)
+
+ leading = attrng.Measurement(
+ title=u'Leading',
+ description=(u'The height of a single text line. It includes '
+ u'character height.'),
+ required=False)
+
+ fontColor = attrng.Color(
+ title=u'Font Color',
+ description=u'The color in which the text will appear.',
+ required=False)
+
+ leftPadding = attrng.Measurement(
+ title=u'Left Padding',
+ description=u'The size of the padding on the left side.',
+ required=False)
+
+ rightPadding = attrng.Measurement(
+ title=u'Right Padding',
+ description=u'The size of the padding on the right side.',
+ required=False)
+
+ topPadding = attrng.Measurement(
+ title=u'Top Padding',
+ description=u'The size of the padding on the top.',
+ required=False)
+
+ bottomPadding = attrng.Measurement(
+ title=u'Bottom Padding',
+ description=u'The size of the padding on the bottom.',
+ required=False)
+
+ background = attrng.Color(
+ title=u'Background Color',
+ description=u'The color to use as the background for the cell.',
+ required=False)
+
+ align = attrng.Choice(
+ title=u'Text Alignment',
+ description=u'The text alignment within the cell.',
+ choices=interfaces.ALIGN_TEXT_CHOICES,
+ required=False)
+
+ vAlign = attrng.Choice(
+ title=u'Vertical Alignment',
+ description=u'The vertical alignment of the text within the cell.',
+ choices=interfaces.VALIGN_TEXT_CHOICES,
+ required=False)
+
+ lineBelowThickness = attrng.Measurement(
+ title=u'Line Below Thickness',
+ description=u'The thickness of the line below the cell.',
+ required=False)
+
+ lineBelowColor = attrng.Color(
+ title=u'Line Below Color',
+ description=u'The color of the line below the cell.',
+ required=False)
+
+ lineBelowCap = attrng.Choice(
+ title=u'Line Below Cap',
+ description=u'The cap at the end of the line below the cell.',
+ choices=interfaces.CAP_CHOICES,
+ required=False)
+
+ lineBelowCount = attrng.Integer(
+ title=u'Line Below Count',
+ description=(u'Describes whether the line below is a single (1) or '
+ u'double (2) line.'),
+ required=False)
+
+ lineBelowSpace = attrng.Measurement(
+ title=u'Line Below Space',
+ description=u'The space of the line below the cell.',
+ required=False)
+
+ lineAboveThickness = attrng.Measurement(
+ title=u'Line Above Thickness',
+ description=u'The thickness of the line above the cell.',
+ required=False)
+
+ lineAboveColor = attrng.Color(
+ title=u'Line Above Color',
+ description=u'The color of the line above the cell.',
+ required=False)
+
+ lineAboveCap = attrng.Choice(
+ title=u'Line Above Cap',
+ description=u'The cap at the end of the line above the cell.',
+ choices=interfaces.CAP_CHOICES,
+ required=False)
+
+ lineAboveCount = attrng.Integer(
+ title=u'Line Above Count',
+ description=(u'Describes whether the line above is a single (1) or '
+ u'double (2) line.'),
+ required=False)
+
+ lineAboveSpace = attrng.Measurement(
+ title=u'Line Above Space',
+ description=u'The space of the line above the cell.',
+ required=False)
+
+ lineLeftThickness = attrng.Measurement(
+ title=u'Left Line Thickness',
+ description=u'The thickness of the line left of the cell.',
+ required=False)
+
+ lineLeftColor = attrng.Color(
+ title=u'Left Line Color',
+ description=u'The color of the line left of the cell.',
+ required=False)
+
+ lineLeftCap = attrng.Choice(
+ title=u'Line Left Cap',
+ description=u'The cap at the end of the line left of the cell.',
+ choices=interfaces.CAP_CHOICES,
+ required=False)
+
+ lineLeftCount = attrng.Integer(
+ title=u'Line Left Count',
+ description=(u'Describes whether the left line is a single (1) or '
+ u'double (2) line.'),
+ required=False)
+
+ lineLeftSpace = attrng.Measurement(
+ title=u'Line Left Space',
+ description=u'The space of the line left of the cell.',
+ required=False)
+
+ lineRightThickness = attrng.Measurement(
+ title=u'Right Line Thickness',
+ description=u'The thickness of the line right of the cell.',
+ required=False)
+
+ lineRightColor = attrng.Color(
+ title=u'Right Line Color',
+ description=u'The color of the line right of the cell.',
+ required=False)
+
+ lineRightCap = attrng.Choice(
+ title=u'Line Right Cap',
+ description=u'The cap at the end of the line right of the cell.',
+ choices=interfaces.CAP_CHOICES,
+ required=False)
+
+ lineRightCount = attrng.Integer(
+ title=u'Line Right Count',
+ description=(u'Describes whether the right line is a single (1) or '
+ u'double (2) line.'),
+ required=False)
+
+ lineRightSpace = attrng.Measurement(
+ title=u'Line Right Space',
+ description=u'The space of the line right of the cell.',
+ required=False)
+
+class TableCell(directive.RMLDirective):
+ signature = ITableCell
+ styleAttributesMapping = (
+ ('FONTNAME', ('fontName',)),
+ ('FONTSIZE', ('fontSize',)),
+ ('TEXTCOLOR', ('fontColor',)),
+ ('LEADING', ('leading',)),
+ ('LEFTPADDING', ('leftPadding',)),
+ ('RIGHTPADDING', ('rightPadding',)),
+ ('TOPPADDING', ('topPadding',)),
+ ('BOTTOMPADDING', ('bottomPadding',)),
+ ('BACKGROUND', ('background',)),
+ ('ALIGNMENT', ('align',)),
+ ('VALIGN', ('vAlign',)),
+ ('LINEBELOW', ('lineBelowThickness', 'lineBelowColor',
+ 'lineBelowCap', 'lineBelowCount', 'lineBelowSpace')),
+ ('LINEABOVE', ('lineAboveThickness', 'lineAboveColor',
+ 'lineAboveCap', 'lineAboveCount', 'lineAboveSpace')),
+ ('LINEBEFORE', ('lineLeftThickness', 'lineLeftColor',
+ 'lineLeftCap', 'lineLeftCount', 'lineLeftSpace')),
+ ('LINEAFTER', ('lineRightThickness', 'lineRightColor',
+ 'lineRightCap', 'lineRightCount', 'lineRightSpace')),
)
def processStyle(self):
row = len(self.parent.parent.rows)
col = len(self.parent.cols)
- for styleName, attrs in self.styleAttrs:
- args = []
- for attribute in attrs:
- value = attribute.get(self.element, context=self)
- if value is not attr.DEFAULT:
- args.append(value)
- if args or len(attrs) == 0:
+ for styleAction, attrNames in self.styleAttributesMapping:
+ args = self.getAttributeValues(select=attrNames, valuesOnly=True)
+ if args or len(attrNames) == 0:
self.parent.parent.style.add(
- styleName, [col, row], [col, row], *args)
+ styleAction, [col, row], [col, row], *args)
def process(self):
# Produce style
self.processStyle()
# Produce cell data
- flow = Flow(self.element, self.parent, self.context)
+ flow = Flow(self.element, self.parent)
flow.process()
content = flow.flow
if len(content) == 0:
- content = attr.TextNode().get(self.element)
+ content = self.getAttributeValues(
+ select=('content',), valuesOnly=True)[0]
self.parent.cols.append(content)
-class TableRow(element.ContainerElement):
- subElements = {'td': TableCell}
+class ITableRow(interfaces.IRMLDirectiveSignature):
+ """A table row in the block table."""
+ occurence.containing(
+ occurence.OneOrMore('td', ITableCell),
+ )
+class TableRow(directive.RMLDirective):
+ signature = ITableRow
+ factories = {'td': TableCell}
+
def process(self):
self.cols = []
- self.processSubElements(None)
+ self.processSubDirectives()
self.parent.rows.append(self.cols)
-class TableBulkData(element.Element):
+class ITableBulkData(interfaces.IRMLDirectiveSignature):
+ """Bulk Data allows one to wuickly create a table."""
+
+ content = attrng.TextNodeSequence(
+ title=u'Content',
+ description=u'The bulk data.',
+ splitre=re.compile('\n'),
+ value_type=attrng.Sequence(splitre=re.compile(','),
+ value_type=attrng.Text())
+ )
+
+class TableBulkData(directive.RMLDirective):
+ signature = ITableBulkData
+
def process(self):
- attribute = attr.TextNodeSequence(
- splitre=re.compile('\n'),
- valueType=attr.Sequence(
- splitre=re.compile(','),
- valueType=attr.Text()
- ))
- self.parent.rows = attribute.get(self.element)
+ self.parent.rows = self.getAttributeValues(valuesOnly=True)[0]
class BlockTableStyle(stylesheet.BlockTableStyle):
def process(self):
- self.parent.style = copy.deepcopy(self.parent.style)
- attrs = element.extractAttributes(self.attrs, self.element, self)
- for name, value in attrs.items():
- setattr(self.parent.style, name, value)
- self.processSubElements(self.parent.style)
+ self.style = copy.deepcopy(self.parent.style)
+ attrs = self.getAttributeValues()
+ for name, value in attrs:
+ setattr(self.style, name, value)
+ self.processSubDirectives()
+ self.parent.style = self.style
-class BlockTable(element.ContainerElement, Flowable):
- klass = reportlab.platypus.Table
- kw = (
- ('rowHeights', attr.Sequence('rowHeights', attr.Measurement())),
- ('colWidths', attr.Sequence('colWidths',
- attr.Measurement(allowPercentage=True, allowStar=True))),
+class IBlockTable(interfaces.IRMLDirectiveSignature):
+ """A typical block table."""
+ occurence.containing(
+ occurence.ZeroOrMore('tr', ITableRow),
+ occurence.ZeroOrOne('bulkData', ITableBulkData),
+ occurence.ZeroOrMore('blockTableStyle', stylesheet.IBlockTableStyle),
)
- attrs = ( ('repeatRows', attr.Int('repeatRows')), )
+ style = attrng.Style(
+ title=u'Style',
+ description=(u'The table style that is applied to the table. '),
+ required=False)
+ rowHeights = attrng.Sequence(
+ title=u'Row Heights',
+ description=u'A list of row heights in the table.',
+ value_type=attrng.Measurement(),
+ required=False)
- subElements = {
+ colWidths = attrng.Sequence(
+ title=u'Column Widths',
+ description=u'A list of column widths in the table.',
+ value_type=attrng.Measurement(allowPercentage=True, allowStar=True),
+ required=False)
+
+ repeatRows = attrng.Integer(
+ title=u'Repeat Rows',
+ description=u'A flag to repeat rows upon table splits.',
+ required=False)
+
+
+class BlockTable(Flowable):
+ signature = IBlockTable
+ klass = reportlab.platypus.Table
+ factories = {
'tr': TableRow,
'bulkData': TableBulkData,
'blockTableStyle': BlockTableStyle}
def process(self):
+ attrs = dict(self.getAttributeValues())
# Get the table style; create a new one, if none is found
- self.style = attr.Style('style', 'table').get(self.element, None, self)
+ self.style = attrs.pop('style', None)
if self.style is None:
self.style = reportlab.platypus.tables.TableStyle()
# Extract all table rows and cells
self.rows = []
- self.processSubElements(None)
+ self.processSubDirectives(None)
# Create the table
- kw = self.getKeywordArguments()
-
- table = self.klass(self.rows, style=self.style, **kw)
-
- attrs = element.extractKeywordArguments(self.attrs, self.element)
- for name, value in attrs.items():
- setattr(table, name, value)
-
- # Must set keepWithNExt on table, since the style is not stored corr.
+ repeatRows = attrs.pop('repeatRows', None)
+ table = self.klass(self.rows, style=self.style, **attrs)
+ if repeatRows:
+ table.repeatRows = repeatRows
+ # Must set keepWithNext on table, since the style is not stored corr.
if hasattr(self.style, 'keepWithNext'):
table.keepWithNext = self.style.keepWithNext
self.parent.flow.append(table)
+class INextFrame(interfaces.IRMLDirectiveSignature):
+ """Switch to the next frame."""
+ name = attrng.StringOrInt(
+ title=u'Name',
+ description=(u'The name or index of the next frame.'),
+ required=False)
+
class NextFrame(Flowable):
+ signature = INextFrame
klass = reportlab.platypus.doctemplate.FrameBreak
- kw = (
- ('ix', attr.StringOrInt('name')), )
+ attrMapping = {'name': 'ix'}
+
+class ISetNextFrame(interfaces.IRMLDirectiveSignature):
+ """Define the next frame to switch to."""
+ name = attrng.StringOrInt(
+ title=u'Name',
+ description=(u'The name or index of the next frame.'),
+ required=True)
+
class SetNextFrame(Flowable):
+ signature = INextFrame
klass = reportlab.platypus.doctemplate.NextFrameFlowable
- kw = (
- ('ix', attr.StringOrInt('name')), )
+ attrMapping = {'name': 'ix'}
+
+class INextPage(interfaces.IRMLDirectiveSignature):
+ """Switch to the next page."""
+
class NextPage(Flowable):
+ signature = INextPage
klass = reportlab.platypus.PageBreak
+
+class ISetNextTemplate(interfaces.IRMLDirectiveSignature):
+ """Define the next page template to use."""
+ name = attrng.StringOrInt(
+ title=u'Name',
+ description=u'The name or index of the next page template.',
+ required=True)
+
class SetNextTemplate(Flowable):
+ signature = ISetNextTemplate
klass = reportlab.platypus.doctemplate.NextPageTemplate
- args = ( attr.StringOrInt('name'), )
+ attrMapping = {'name': 'pt'}
+
+class IConditionalPageBreak(interfaces.IRMLDirectiveSignature):
+ """Switch to the next page if not enough vertical space is available."""
+ height = attrng.Measurement(
+ title=u'height',
+ description=u'The minimal height that must be remaining on the page.',
+ required=True)
+
class ConditionalPageBreak(Flowable):
+ signature = IConditionalPageBreak
klass = reportlab.platypus.CondPageBreak
- args = ( attr.Measurement('height'), )
+class IKeepInFrame(interfaces.IRMLDirectiveSignature):
+ """Ask a flowable to stay within the frame."""
+
+ maxWidth = attrng.Measurement(
+ title=u'Maximum Width',
+ description=u'The maximum width the flowables are allotted.',
+ default=None,
+ required=False)
+
+ maxHeight = attrng.Measurement(
+ title=u'Maximum Height',
+ description=u'The maximum height the flowables are allotted.',
+ default=None,
+ required=False)
+
+ mergeSpace = attrng.Boolean(
+ title=u'Merge Space',
+ description=u'A flag to set whether the space should be merged.',
+ required=False)
+
+ onOverflow = attrng.Choice(
+ title=u'On Overflow',
+ description=u'Defines what has to be done, if an overflow is detected.',
+ choices=('error', 'overflow', 'shrink', 'truncate'),
+ required=False)
+
+ id = attrng.Text(
+ title=u'Name/Id',
+ description=u'The name/id of the flowable.',
+ required=False)
+
+ frame = attrng.StringOrInt(
+ title=u'Frame',
+ description=u'The frame to which the flowable should be fitted.',
+ required=False)
+
class KeepInFrame(Flowable):
+ signature = IKeepInFrame
klass = reportlab.platypus.flowables.KeepInFrame
- args = (
- attr.Measurement('maxWidth', None),
- attr.Measurement('maxHeight', None), )
- kw = (
- ('mergeSpace', attr.Bool('mergeSpace')),
- ('mode', attr.Choice('onOverflow',
- ('error', 'overflow', 'shrink', 'truncate'))),
- ('name', attr.Text('id')),
- ('frame', attr.StringOrInt('frame')), )
+ attrMapping = {'onOverflow': 'mode', 'id': 'name'}
def process(self):
- args = self.getPositionalArguments()
- kw = self.getKeywordArguments()
+ args = dict(self.getAttributeValues(attrMapping=self.attrMapping))
+ # Circumvent broken-ness in zope.schema
+ args['maxWidth'] = args.get('maxWidth', None)
+ args['maxHeight'] = args.get('maxHeight', None)
# If the frame was specifed, get us there
- frame = kw.pop('frame', None)
+ frame = args.pop('frame', None)
if frame:
self.parent.flow.append(
reportlab.platypus.doctemplate.FrameBreak(frame))
# Create the content of the container
- flow = Flow(self.element, self.parent, self.context)
+ flow = Flow(self.element, self.parent)
flow.process()
- kw['content'] = flow.flow
+ args['content'] = flow.flow
# Create the keep in frame container
- frame = self.klass(*args, **kw)
+ frame = self.klass(**args)
self.parent.flow.append(frame)
+class IImageAndFlowables(interfaces.IRMLDirectiveSignature):
+ """An image with flowables around it."""
+
+ imageName = attrng.Image(
+ title=u'Image',
+ description=u'The file that is used to extract the image data.',
+ onlyOpen=True,
+ required=True)
+
+ imageWidth = attrng.Measurement(
+ title=u'Image Width',
+ description=u'The width of the image.',
+ required=False)
+
+ imageHeight = attrng.Measurement(
+ title=u'Image Height',
+ description=u'The height the image.',
+ required=False)
+
+ imageMask = attrng.Color(
+ title=u'Mask',
+ description=u'The height the image.',
+ required=False)
+
+ imageLeftPadding = attrng.Measurement(
+ title=u'Image Left Padding',
+ description=u'The padding on the left side of the image.',
+ required=False)
+
+ imageRightPadding = attrng.Measurement(
+ title=u'Image Right Padding',
+ description=u'The padding on the right side of the image.',
+ required=False)
+
+ imageTopPadding = attrng.Measurement(
+ title=u'Image Top Padding',
+ description=u'The padding on the top of the image.',
+ required=False)
+
+ imageBottomPadding = attrng.Measurement(
+ title=u'Image Bottom Padding',
+ description=u'The padding on the bottom of the image.',
+ required=False)
+
+ iamgeSide = attrng.Choice(
+ title=u'Image Side',
+ description=u'The side at which the image will be placed.',
+ choices=('left', 'right'),
+ required=False)
+
class ImageAndFlowables(Flowable):
+ signature = IImageAndFlowables
klass = reportlab.platypus.flowables.ImageAndFlowables
- args = ( attr.Image('imageName', onlyOpen=True), )
- kw = (
- ('width', attr.Measurement('imageWidth')),
- ('height', attr.Measurement('imageHeight')),
- ('mask', attr.Color('imageMask')),
- ('imageLeftPadding', attr.Measurement('imageLeftPadding')),
- ('imageRightPadding', attr.Measurement('imageRightPadding')),
- ('imageTopPadding', attr.Measurement('imageTopPadding')),
- ('imageBottomPadding', attr.Measurement('imageBottomPadding')),
- ('imageSide', attr.Choice('imageSide', ('left', 'right'))) )
+ attrMapping = {'imageWidth': 'width', 'imageHeight': 'height',
+ 'imageMask': 'mask', 'imageName': 'filename'}
def process(self):
- flow = Flow(self.element, self.parent, self.context)
+ flow = Flow(self.element, self.parent)
flow.process()
- args = self.getPositionalArguments()
- kw = self.getKeywordArguments()
# Create the image
- img = reportlab.platypus.flowables.Image(
- width=kw.get('width'), height=kw.get('height'),
- mask=kw.get('mask', 'auto'), *args)
- for option in ('width', 'height', 'mask'):
- if option in kw:
- del kw[option]
+ args = dict(self.getAttributeValues(
+ select=('imageName', 'imageWidth', 'imageHeight', 'imageMask'),
+ attrMapping=self.attrMapping))
+ img = reportlab.platypus.flowables.Image(**args)
# Create the flowable and add it
+ args = dict(self.getAttributeValues(
+ ignore=('imageName', 'imageWidth', 'imageHeight', 'imageMask'),
+ attrMapping=self.attrMapping))
self.parent.flow.append(
- self.klass(img, flow.flow, **kw))
+ self.klass(img, flow.flow, **args))
+class IPTO(interfaces.IRMLDirectiveSignature):
+ '''A container for flowables decorated with trailer & header lists.
+ If the split operation would be called then the trailer and header
+ lists are injected before and after the split. This allows specialist
+ "please turn over" and "continued from previous" like behaviours.'''
+
class PTO(Flowable):
+ signature = IPTO
klass = reportlab.platypus.flowables.PTOContainer
def process(self):
# Get Content
- flow = Flow(self.element, self.parent, self.context)
+ flow = Flow(self.element, self.parent)
flow.process()
# Get the header
ptoHeader = self.element.find('pto_header')
header = None
if ptoHeader:
- header = Flow(ptoHeader, self.parent, self.context)
+ header = Flow(ptoHeader, self.parent)
header.process()
header = header.flow
# Get the trailer
ptoTrailer = self.element.find('pto_trailer')
trailer = None
if ptoTrailer:
- trailer = Flow(ptoTrailer, self.parent, self.context)
+ trailer = Flow(ptoTrailer, self.parent)
trailer.process()
trailer = trailer.flow
# Create and add the PTO Container
self.parent.flow.append(self.klass(flow.flow, trailer, header))
+class IIndent(interfaces.IRMLDirectiveSignature):
+ """Indent the contained flowables."""
+
+ left = attrng.Measurement(
+ title=u'Left',
+ description=u'The indentation to the left.',
+ required=False)
+
+ right = attrng.Measurement(
+ title=u'Right',
+ description=u'The indentation to the right.',
+ required=False)
+
class Indent(Flowable):
- kw = (
- ('left', attr.Measurement('left')),
- ('right', attr.Measurement('right')) )
+ signature = IIndent
def process(self):
- kw = self.getKeywordArguments()
+ kw = dict(self.getAttributeValues())
# Indent
self.parent.flow.append(reportlab.platypus.doctemplate.Indenter(**kw))
# Add Content
- flow = Flow(self.element, self.parent, self.context)
+ flow = Flow(self.element, self.parent)
flow.process()
self.parent.flow += flow.flow
# Dedent
@@ -377,57 +874,181 @@
self.parent.flow.append(reportlab.platypus.doctemplate.Indenter(**kw))
+class IFixedSize(interfaces.IRMLDirectiveSignature):
+ """Create a container flowable of a fixed size."""
+
+ width = attrng.Measurement(
+ title=u'Width',
+ description=u'The width the flowables are allotted.',
+ required=True)
+
+ height = attrng.Measurement(
+ title=u'Height',
+ description=u'The height the flowables are allotted.',
+ required=True)
+
class FixedSize(Flowable):
+ signature = IFixedSize
klass = reportlab.platypus.flowables.KeepInFrame
- args = (
- attr.Measurement('width'),
- attr.Measurement('height'), )
+ attrMapping = {'width': 'maxWidth', 'height': 'maxHeight'}
def process(self):
- flow = Flow(self.element, self.parent, self.context)
+ flow = Flow(self.element, self.parent)
flow.process()
- args = self.getPositionalArguments()
- frame = self.klass(content=flow.flow, mode='shrink', *args)
+ args = dict(self.getAttributeValues(attrMapping=self.attrMapping))
+ frame = self.klass(content=flow.flow, mode='shrink', **args)
self.parent.flow.append(frame)
+
+class IBookmark(interfaces.IRMLDirectiveSignature):
+ """
+ This creates a bookmark to the current page which can be referred to with
+ the given key elsewhere.
+
+ PDF offers very fine grained control over how Acrobat reader is zoomed
+ when people link to this. The default is to keep the user's current zoom
+ settings. the last arguments may or may not be needed depending on the
+ choice of 'fitType'.
+ """
+
+ name = attrng.Text(
+ title=u'Name',
+ description=u'The name of the bookmark.',
+ required=True)
+
+ fitType = attrng.Choice(
+ title=u'Fit Type',
+ description=u'The Fit Type.',
+ choices=('Fit', 'FitH', 'FitV', 'FitR'),
+ required=False)
+
+ left = attrng.Measurement(
+ title=u'Left',
+ description=u'The left position.',
+ required=False)
+
+ right = attrng.Measurement(
+ title=u'Right',
+ description=u'The right position.',
+ required=False)
+
+ top = attrng.Measurement(
+ title=u'Top',
+ description=u'The top position.',
+ required=False)
+
+ right = attrng.Measurement(
+ title=u'Right',
+ description=u'The right position.',
+ required=False)
+
+ zoom = attrng.Float(
+ title=u'Zoom',
+ description=u'The zoom level when clicking on the bookmark.',
+ required=False)
+
class Bookmark(Flowable):
+ signature = IBookmark
klass = platypus.BookmarkPage
- args = ( attr.Text('name'), )
- kw = (
- ('fitType', attr.Choice('fitType', ('Fit', 'FitH', 'FitV', 'FitR'))),
- ('left', attr.Measurement('left')),
- ('right', attr.Measurement('right')),
- ('top', attr.Measurement('top')),
- ('bottom', attr.Measurement('bottom')),
- ('zoom', attr.Float('zoom')),
- )
+ attrMapping = {'name': 'key'}
+
+class IHorizontalRow(interfaces.IRMLDirectiveSignature):
+ """Create a horizontal line on the page."""
+
+ width = attrng.Measurement(
+ title=u'Width',
+ description=u'The width of the line on the page.',
+ allowPercentage=True,
+ required=False)
+
+ thickness = attrng.Measurement(
+ title=u'Thickness',
+ description=u'Line Thickness',
+ required=False)
+
+ color = attrng.Color(
+ title=u'Color',
+ description=u'The color of the line.',
+ required=False)
+
+ lineCap = attrng.Choice(
+ title=u'Cap',
+ description=u'The cap at the end of the line.',
+ choices=interfaces.CAP_CHOICES.keys(),
+ required=False)
+
+ spaceBefore = attrng.Measurement(
+ title=u'Space Before',
+ description=u'The vertical space before the line.',
+ required=False)
+
+ spaceAfter = attrng.Measurement(
+ title=u'Space After',
+ description=u'The vertical space after the line.',
+ required=False)
+
+ align = attrng.Choice(
+ title=u'Alignment',
+ description=u'The alignment of the line within the frame.',
+ choices=interfaces.ALIGN_TEXT_CHOICES,
+ required=False)
+
+ valign = attrng.Choice(
+ title=u'Vertical Alignment',
+ description=u'The vertical alignment of the line.',
+ choices=interfaces.VALIGN_TEXT_CHOICES,
+ required=False)
+
+ dash = attrng.Sequence(
+ title=u'Dash-Pattern',
+ description=u'The dash-pattern of a line.',
+ value_type=attrng.Measurement(),
+ default=None,
+ required=False)
+
class HorizontalRow(Flowable):
+ signature = IHorizontalRow
klass = reportlab.platypus.flowables.HRFlowable
- kw = (
- ('width', attr.Measurement('width', allowPercentage=True)),
- ('thickness', attr.Measurement('thickness')),
- ('color', attr.Color('color')),
- ('lineCap', attr.Choice('lineCap', ('butt', 'round', 'square') )),
- ('spaceBefore', attr.Measurement('spaceBefore')),
- ('spaceAfter', attr.Measurement('spaceAfter')),
- ('hAlign', attr.Choice(
- 'align', ('left', 'right', 'center', 'centre', 'decimal') )),
- ('vAlign', attr.Choice('vAlign', ('top', 'middle', 'bottom') )),
- ('dash', attr.Sequence('dash', attr.Measurement())),
- )
+ attrMapping = {'align': 'hAlign'}
+
+class IOutlineAdd(interfaces.IRMLDirectiveSignature):
+ """Add a new entry to the outline of the PDF."""
+
+ title = attrng.TextNode(
+ title=u'Title',
+ description=u'The text displayed for this item.',
+ required=True)
+
+ key = attrng.String(
+ title=u'Key',
+ description=u'The unique key of the item.',
+ required=False)
+
+ level = attrng.Integer(
+ title=u'Level',
+ description=u'The level in the outline tree.',
+ required=False)
+
+ closed = attrng.Boolean(
+ title=u'Closed',
+ description=(u'A flag to determine whether the sub-tree is closed '
+ u'by default.'),
+ required=False)
+
+
class OutlineAdd(Flowable):
+ signature = IOutlineAdd
klass = platypus.OutlineAdd
- args = ( attr.TextNode(), attr.Text('key', None) )
- kw = (
- ('level', attr.Int('level')),
- ('closed', attr.Bool('closed')),
- )
-class Flow(element.ContainerElement):
- subElements = {
+class IFlow(interfaces.IRMLDirectiveSignature):
+ """A list of flowables."""
+
+class Flow(directive.RMLDirective):
+
+ factories = {
# Generic Flowables
'spacer': Spacer,
'illustration': Illustration,
@@ -466,5 +1087,5 @@
self.flow = []
def process(self):
- self.processSubElements(None)
+ self.processSubDirectives()
return self.flow
Modified: z3c.rml/trunk/src/z3c/rml/form.py
===================================================================
--- z3c.rml/trunk/src/z3c/rml/form.py 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/form.py 2007-03-28 03:02:22 UTC (rev 73805)
@@ -17,7 +17,7 @@
"""
__docformat__ = "reStructuredText"
import types
-from z3c.rml import attr, element
+from z3c.rml import attrng, directive, interfaces
try:
import reportlab.graphics.barcode
@@ -29,62 +29,199 @@
reportlab.graphics.barcode.getCodeNames = lambda : ()
-class BarCode(element.FunctionElement):
- args = (
- attr.Choice('code', reportlab.graphics.barcode.getCodeNames()),
- attr.TextNode(),
- )
- kw = (
- ('x', attr.Measurement('x')),
- ('y', attr.Measurement('y')),
- ('width', attr.Measurement('width')),
- ('height', attr.Measurement('height')),
- ('strokeColor', attr.Color('strokeColor')),
- ('strokeWidth', attr.Measurement('strokeWidth')),
- ('fillColor', attr.Color('fillColor')),
- ('barStrokeColor', attr.Color('barStrokeColor')),
- ('barStrokeWidth', attr.Measurement('barStrokeWidth')),
- ('barFillColor', attr.Color('barFillColor')),
- ('gap', attr.Measurement('gap')),
- # Bar code dependent attributes
- # I2of5, Code128, Standard93, FIM, POSTNET, Ean13B
- ('barWidth', attr.Measurement('barWidth')),
- # I2of5, Code128, Standard93, FIM, POSTNET
- ('barHeight', attr.Measurement('barHeight')),
- # I2of5
- ('ratio', attr.Float('ratio')),
- # I2of5
- # Should be boolean, but some code want it as int; will still work
- ('checksum', attr.Int('checksum')),
- # I2of5
- ('bearers', attr.Float('bearers')),
- # I2of5, Code128, Standard93, FIM, Ean13
- ('quiet', attr.Bool('quiet')),
- # I2of5, Code128, Standard93, FIM, Ean13
- ('lquiet', attr.Measurement('lquiet')),
- # I2of5, Code128, Standard93, FIM, Ean13
- ('rquiet', attr.Measurement('rquiet')),
- # I2of5, Code128, Standard93, FIM, POSTNET, Ean13
- ('fontName', attr.Text('fontName')),
- # I2of5, Code128, Standard93, FIM, POSTNET, Ean13
- ('fontSize', attr.Measurement('fontSize')),
- # I2of5, Code128, Standard93, FIM, POSTNET, Ean13
- ('humanReadable', attr.Bool('humanReadable')),
- # I2of5, Standard93
- ('stop', attr.Bool('atop')),
- # FIM, POSTNET
- ('spaceWidth', attr.Measurement('spaceWidth')),
- # POSTNET
- ('shortHeight', attr.Measurement('shortHeight')),
- # Ean13
- ('textColor', attr.Color('textColor')),
- )
+class IBarCodeBase(interfaces.IRMLDirectiveSignature):
+ """Create a bar code."""
+ code = attrng.Choice(
+ title=u'Code',
+ description=u'The name of the type of code to use.',
+ choices=reportlab.graphics.barcode.getCodeNames(),
+ required=True)
+
+ value = attrng.TextNode(
+ title=u'Value',
+ description=u'The value represented by the code.',
+ required=True)
+
+ width = attrng.Measurement(
+ title=u'Width',
+ description=u'The width of the barcode.',
+ required=False)
+
+ height = attrng.Measurement(
+ title=u'Height',
+ description=u'The height of the barcode.',
+ required=False)
+
+ strokeColor = attrng.Color(
+ title=u'Stroke Color',
+ description=(u'The color of the line strokes in the area.'),
+ required=False)
+
+ strokeWidth = attrng.Measurement(
+ title=u'Stroke Width',
+ description=u'The width of the line strokes in the area.',
+ required=False)
+
+ fillColor = attrng.Color(
+ title=u'Fill Color',
+ description=(u'The color of the filled shapes in the area.'),
+ required=False)
+
+ barStrokeColor = attrng.Color(
+ title=u'Bar Stroke Color',
+ description=(u'The color of the line strokes in the barcode.'),
+ required=False)
+
+ barStrokeWidth = attrng.Measurement(
+ title=u'Bar Stroke Width',
+ description=u'The width of the line strokes in the barcode.',
+ required=False)
+
+ barFillColor = attrng.Color(
+ title=u'Bar Fill Color',
+ description=(u'The color of the filled shapes in the barcode.'),
+ required=False)
+
+ gap = attrng.Measurement(
+ title=u'Gap',
+ description=u'The width of the inter-character gaps.',
+ required=False)
+
+ # Bar code dependent attributes
+ # I2of5, Code128, Standard93, FIM, POSTNET, Ean13B
+ barWidth = attrng.Measurement(
+ title=u'Bar Width',
+ description=u'The width of the smallest bar within the barcode',
+ required=False)
+
+ # I2of5, Code128, Standard93, FIM, POSTNET
+ barHeight = attrng.Measurement(
+ title=u'Bar Height',
+ description=u'The height of the symbol.',
+ required=False)
+
+ # I2of5
+ ratio = attrng.Float(
+ title=u'Ratio',
+ description=(u'The ratio of wide elements to narrow elements. '
+ u'Must be between 2.0 and 3.0 (or 2.2 and 3.0 if the '
+ u'barWidth is greater than 20 mils (.02 inch)).'),
+ min=2.0,
+ max=3.0,
+ required=False)
+
+ # I2of5
+ # Should be boolean, but some code want it as int; will still work
+ checksum = attrng.Integer(
+ title=u'Ratio',
+ description=(u'A flag that enables the computation and inclusion of '
+ u'the check digit.'),
+ required=False)
+
+ # I2of5
+ bearers = attrng.Float(
+ title=u'Bearers',
+ description=(u'Height of bearer bars (horizontal bars along the top '
+ u'and bottom of the barcode). Default is 3 '
+ u'x-dimensions. Set to zero for no bearer bars.'
+ u'(Bearer bars help detect misscans, so it is '
+ u'suggested to leave them on).'),
+ required=False)
+
+ # I2of5, Code128, Standard93, FIM, Ean13
+ quiet = attrng.Boolean(
+ title=u'Quiet Zone',
+ description=(u'A flag to include quiet zones in the symbol.'),
+ required=False)
+
+ # I2of5, Code128, Standard93, FIM, Ean13
+ lquiet = attrng.Measurement(
+ title=u'Left Quiet Zone',
+ description=(u"Quiet zone size to the left of code, if quiet is "
+ u"true. Default is the greater of .25 inch or .15 times "
+ u"the symbol's length."),
+ required=False)
+
+ # I2of5, Code128, Standard93, FIM, Ean13
+ rquiet = attrng.Measurement(
+ title=u'Right Quiet Zone',
+ description=(u"Quiet zone size to the right of code, if quiet is "
+ u"true. Default is the greater of .25 inch or .15 times "
+ u"the symbol's length."),
+ required=False)
+
+ # I2of5, Code128, Standard93, FIM, POSTNET, Ean13
+ frontName = attrng.String(
+ title=u'Font Name',
+ description=(u'The font used to print the value.'),
+ required=False)
+
+ # I2of5, Code128, Standard93, FIM, POSTNET, Ean13
+ frontSize = attrng.Measurement(
+ title=u'Font Size',
+ description=(u'The size of the value text.'),
+ required=False)
+
+ # I2of5, Code128, Standard93, FIM, POSTNET, Ean13
+ humanReadable = attrng.Boolean(
+ title=u'Human Readable',
+ description=(u'A flag when set causes the value to be printed below '
+ u'the bar code.'),
+ required=False)
+
+ # I2of5, Standard93
+ stop = attrng.Boolean(
+ title=u'Show Start/Stop',
+ description=(u'A flag to specify whether the start/stop symbols '
+ u'are to be shown.'),
+ required=False)
+
+ # FIM, POSTNET
+ spaceWidth = attrng.Measurement(
+ title=u'Space Width',
+ description=u'The space of the inter-character gaps.',
+ required=False)
+
+ # POSTNET
+ shortHeight = attrng.Measurement(
+ title=u'Short Height',
+ description=u'The height of the short bar.',
+ required=False)
+
+ # Ean13
+ textColor = attrng.Color(
+ title=u'Text Color',
+ description=(u'The color of human readable text.'),
+ required=False)
+
+
+class IBarCode(IBarCodeBase):
+ """A barcode graphic."""
+
+ x = attrng.Measurement(
+ title=u'X-Position',
+ description=u'The x-position of the lower-left corner of the barcode.',
+ default=0,
+ required=False)
+
+ y = attrng.Measurement(
+ title=u'Y-Position',
+ description=u'The y-position of the lower-left corner of the barcode.',
+ default=0,
+ required=False)
+
+
+
+class BarCode(directive.RMLDirective):
+ signature = IBarCode
+
def process(self):
- kw = self.getKeywordArguments()
- name, value = self.getPositionalArguments()
- kw['value'] = str(value)
+ kw = dict(self.getAttributeValues())
+ name = kw.pop('code')
+ kw['value'] = str(kw['value'])
x = kw.pop('x', 0)
y = kw.pop('y', 0)
code = reportlab.graphics.barcode.createBarcodeDrawing(name, **kw)
- code.drawOn(self.context, x, y)
+ manager = attrng.getManager(self, interfaces.ICanvasManager)
+ code.drawOn(manager.canvas, x, y)
Modified: z3c.rml/trunk/src/z3c/rml/interfaces.py
===================================================================
--- z3c.rml/trunk/src/z3c/rml/interfaces.py 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/interfaces.py 2007-03-28 03:02:22 UTC (rev 73805)
@@ -16,11 +16,38 @@
$Id$
"""
__docformat__ = "reStructuredText"
+import reportlab.lib.enums
import zope.interface
+import zope.schema
+from z3c.rml import attrng
+from z3c.rml.occurence import ZeroOrMore, ZeroOrOne, OneOrMore
+
+JOIN_CHOICES = {'round': 1, 'mitered': 0, 'bevelled': 2}
+CAP_CHOICES = {'default': 0, 'butt': 0, 'round': 1, 'square': 2}
+ALIGN_CHOICES = {
+ 'left': reportlab.lib.enums.TA_LEFT,
+ 'right': reportlab.lib.enums.TA_RIGHT,
+ 'center': reportlab.lib.enums.TA_CENTER,
+ 'centre': reportlab.lib.enums.TA_CENTER,
+ 'justify': reportlab.lib.enums.TA_JUSTIFY}
+ALIGN_TEXT_CHOICES = {
+ 'left': 'LEFT', 'right': 'RIGHT', 'center': 'CENTER', 'centre': 'CENTER',
+ 'decimal': 'DECIMAL'}
+VALIGN_TEXT_CHOICES = {
+ 'top': 'TOP', 'middle': 'MIDDLE', 'bottom': 'BOTTOM'}
+SPLIT_CHOICES = ('splitfirst', 'splitlast')
+
+
class IRML2PDF(zope.interface.Interface):
"""This is the main public API of z3c.rml"""
+ def parseString(xml):
+ """Parse an XML string and convert it to PDF.
+
+ The output is a ``StringIO`` object.
+ """
+
def go(xmlInputName, outputFileName=None, outDir=None, dtdDir=None):
"""Convert RML 2 PDF.
@@ -28,25 +55,68 @@
``outputFileName``.
"""
-class INamesManager(zope.interface.Interface):
- """Manages custom names"""
-
+class IManager(zope.interface.Interface):
+ """A manager of all document-global variables."""
names = zope.interface.Attribute("Names dict")
+ styles = zope.interface.Attribute("Styles dict")
+ colors = zope.interface.Attribute("Colors dict")
-class IStylesManager(zope.interface.Interface):
- """Manages custom styles"""
+class IPostProcessorManager(zope.interface.Interface):
+ """Manages all post processors"""
- styles = zope.interface.Attribute("Styles dict")
+ postProcessors = zope.interface.Attribute(
+ "List of tuples of the form: (name, processor)")
+class ICanvasManager(zope.interface.Interface):
+ """A manager for the canvas."""
+ canvas = zope.interface.Attribute("Canvas")
-class IColorsManager(zope.interface.Interface):
- """Manages custom colors"""
+class IRMLDirectiveSignature(zope.interface.Interface):
+ """The attribute and sub-directives signature of the current
+ RML directive."""
- colors = zope.interface.Attribute("Colors dict")
+class IRMLDirective(zope.interface.Interface):
+ """A directive in RML extracted from an Element Tree element."""
-class IPostProcessorManager(zope.interface.Interface):
- """Manages all post processors"""
+ signature = zope.schema.Field(
+ title=u'Signature',
+ description=(u'The signature of the RML directive.'),
+ required=True)
- postProcessors = zope.interface.Attribute(
- "List of tuples of the form: (name, processor)")
+ parent = zope.schema.Field(
+ title=u'Parent RML Element',
+ description=u'The parent in the RML element hierarchy',
+ required=True,)
+
+ element = zope.schema.Field(
+ title=u'Element',
+ description=(u'The Element Tree element from which the data '
+ u'is retrieved.'),
+ required=True)
+
+ def getAttributeValues(ignore=None, select=None, includeMissing=False):
+ """Return a list of name-value-tuples based on the signature.
+
+ If ``ignore`` is specified, all attributes are returned except the
+ ones listed in the argument. The values of the sequence are the
+ attribute names.
+
+ If ``select`` is specified, only attributes listed in the argument are
+ returned. The values of the sequence are the attribute names.
+
+ If ``includeMissing`` is set to true, then even missing attributes are
+ included in the value list.
+ """
+
+ def processSubDirectives(self):
+ """Process all sub-directives."""
+
+ def process(self):
+ """Process the directive.
+
+ The main task for this method is to interpret the available data and
+ to make the corresponding calls in the Reportlab document.
+
+ This call should also process all sub-directives and process them.
+ """
Added: z3c.rml/trunk/src/z3c/rml/occurence.py
===================================================================
--- z3c.rml/trunk/src/z3c/rml/occurence.py 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/occurence.py 2007-03-28 03:02:22 UTC (rev 73805)
@@ -0,0 +1,110 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Condition Implementation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+import reportlab
+import sys
+import zope.interface
+import zope.schema
+from zope.schema import fieldproperty
+
+class ICondition(zope.interface.Interface):
+ """Condition that is checked before a directive is available."""
+
+ __doc__ = zope.schema.TextLine(
+ title=u'Description',
+ description=u'The description of the condition.',
+ required=True)
+
+ def __call__(directive):
+ """Check whether the condition is fulfilled for the given directive."""
+
+
+class IOccurence(zope.interface.Interface):
+ """Description of the occurence of a sub-directive."""
+
+ __doc__ = zope.schema.TextLine(
+ title=u'Description',
+ description=u'The description of the occurence.',
+ required=True)
+
+ tag = zope.schema.BytesLine(
+ title=u'Tag',
+ description=u'The tag of the sub-directive within the directive',
+ required=True)
+
+ signature = zope.schema.Field(
+ title=u'Signature',
+ description=u'The signature of the sub-directive.',
+ required=True)
+
+ condition = zope.schema.Field(
+ title=u'Condition',
+ description=u'The condition that the directive is available.',
+ required=False)
+
+
+ at zope.interface.implementer(ICondition)
+def laterThanReportlab21(directive):
+ """The directive is only available in Reportlab 2.1 and higher."""
+ return [int(num) for num in reportlab.Version.split('.')] >= (2, 0)
+
+
+def containing(*occurences):
+ frame = sys._getframe(1)
+ f_locals = frame.f_locals
+ f_globals = frame.f_globals
+
+ if not (f_locals is not f_globals
+ and f_locals.get('__module__')
+ and f_locals.get('__module__') == f_globals.get('__name__')
+ ):
+ raise TypeError("contains not called from signature interface")
+
+ f_locals['__interface_tagged_values__'] = {'directives': occurences}
+
+
+class Occurence(object):
+ zope.interface.implements(IOccurence)
+
+ tag = fieldproperty.FieldProperty(IOccurence['tag'])
+ signature = fieldproperty.FieldProperty(IOccurence['signature'])
+ condition = fieldproperty.FieldProperty(IOccurence['condition'])
+
+ def __init__(self, tag, signature, condition=None):
+ self.tag = tag
+ self.signature = signature
+ self.condition = condition
+
+
+class ZeroOrMore(Occurence):
+ """Zero or More
+
+ This sub-directive can occur zero or more times.
+ """
+
+class ZeroOrOne(Occurence):
+ """Zero or one
+
+ This sub-directive can occur zero or one time.
+ """
+
+class OneOrMore(Occurence):
+ """One or More
+
+ This sub-directive can occur one or more times.
+ """
Property changes on: z3c.rml/trunk/src/z3c/rml/occurence.py
___________________________________________________________________
Name: svn:keywords
+ Id
Modified: z3c.rml/trunk/src/z3c/rml/page.py
===================================================================
--- z3c.rml/trunk/src/z3c/rml/page.py 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/page.py 2007-03-28 03:02:22 UTC (rev 73805)
@@ -17,7 +17,7 @@
"""
__docformat__ = "reStructuredText"
import cStringIO
-from z3c.rml import attr, element, interfaces
+from z3c.rml import attrng, directive, interfaces
try:
import pyPdf
@@ -48,11 +48,25 @@
return outputFile
-class MergePage(element.FunctionElement):
- args = ( attr.File('filename'), attr.Int('page') )
+class IMergePage(interfaces.IRMLDirectiveSignature):
+ """Merges an existing PDF Page into the one to be generated."""
+ filename = attrng.File(
+ title=u'File',
+ description=(u'Reference to the PDF file to extract the page from.'),
+ required=True)
+
+ page = attrng.Integer(
+ title=u'Page Number',
+ description=u'The page number of the PDF file that is used to merge..',
+ required=True)
+
+
+class MergePage(directive.RMLDirective):
+ signature = IMergePage
+
def getProcessor(self):
- manager = attr.getManager(self, interfaces.IPostProcessorManager)
+ manager = attrng.getManager(self, interfaces.IPostProcessorManager)
procs = dict(manager.postProcessors)
if 'MERGE' not in procs:
proc = MergePostProcessor()
@@ -64,8 +78,9 @@
if pyPdf is None:
raise Exception(
'pyPdf is not installed, so this feature is not available.')
- inputFile, inPage = self.getPositionalArguments()
- outPage = self.context.getPageNumber()-1
+ inputFile, inPage = self.getAttributeValues(valuesOnly=True)
+ manager = attrng.getManager(self, interfaces.ICanvasManager)
+ outPage = manager.canvas.getPageNumber()-1
proc = self.getProcessor()
pageOperations = proc.operations.setdefault(outPage, [])
Modified: z3c.rml/trunk/src/z3c/rml/platypus.py
===================================================================
--- z3c.rml/trunk/src/z3c/rml/platypus.py 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/platypus.py 2007-03-28 03:02:22 UTC (rev 73805)
@@ -17,7 +17,11 @@
"""
__docformat__ = "reStructuredText"
import reportlab.platypus.flowables
+import zope.interface
+from z3c.rml import interfaces
+
+
class BaseFlowable(reportlab.platypus.flowables.Flowable):
def __init__(self, *args, **kw):
reportlab.platypus.flowables.Flowable.__init__(self)
@@ -44,7 +48,9 @@
from z3c.rml import canvas
self.canv.saveState()
drawing = canvas.Drawing(
- self.processor.element, self.processor, self.canv)
+ self.processor.element, self.processor)
+ zope.interface.alsoProvides(drawing, interfaces.ICanvasManager)
+ drawing.canvas = self.canv
drawing.process()
self.canv.restoreState()
@@ -55,8 +61,7 @@
class OutlineAdd(BaseFlowable):
def draw(self):
- title, key = self.args
- if key is None:
- key = str(hash(self))
- self.canv.bookmarkPage(key)
- self.canv.addOutlineEntry(title, key, **self.kw)
+ if self.kw.get('key', None) is None:
+ self.kw['key'] = str(hash(self))
+ self.canv.bookmarkPage(self.kw['key'])
+ self.canv.addOutlineEntry(**self.kw)
Modified: z3c.rml/trunk/src/z3c/rml/special.py
===================================================================
--- z3c.rml/trunk/src/z3c/rml/special.py 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/special.py 2007-03-28 03:02:22 UTC (rev 73805)
@@ -16,25 +16,45 @@
$Id$
"""
__docformat__ = "reStructuredText"
-from z3c.rml import attr, element, interfaces
+from z3c.rml import attrng, directive, interfaces
-class Name(element.FunctionElement):
- args = (
- attr.Text('id'),
- attr.Text('value'), )
+class IName(interfaces.IRMLDirectiveSignature):
+ """Defines a name for a string."""
+ id = attrng.String(
+ title=u'Id',
+ description=u'The id under which the value will be known.',
+ required=True)
+
+ value = attrng.Text(
+ title=u'Value',
+ description=u'The text that is displayed if the id is called.',
+ required=True)
+
+class Name(directive.RMLDirective):
+ signature = IName
+
def process(self):
- id, value = self.getPositionalArguments()
- manager = attr.getManager(self, interfaces.INamesManager)
+ id, value = self.getAttributeValues(valuesOnly=True)
+ manager = attrng.getManager(self)
manager.names[id] = value
-class GetName(element.Element):
+class IGetName(interfaces.IRMLDirectiveSignature):
+ """Get the text for the id."""
+ id = attrng.String(
+ title=u'Id',
+ description=u'The id as which the value is known.',
+ required=True)
+
+class GetName(directive.RMLDirective):
+ signature = IGetName
+
def process(self):
- id = attr.Text('id').get(self.element)
- manager = attr.getManager(self, interfaces.INamesManager)
+ id = dict(self.getAttributeValues()).pop('id')
+ manager = attrng.getManager(self)
text = manager.names[id] + (self.element.tail or u'')
# Now replace the element with the text
parent = self.element.getparent()
@@ -45,12 +65,23 @@
parent.remove(self.element)
-class Alias(element.FunctionElement):
- args = (
- attr.Text('id'),
- attr.Style('value'), )
+class IAlias(interfaces.IRMLDirectiveSignature):
+ """Defines an alias for a given style."""
+ id = attrng.String(
+ title=u'Id',
+ description=u'The id as which the style will be known.',
+ required=True)
+
+ value = attrng.Style(
+ title=u'Value',
+ description=u'The style that is represented.',
+ required=True)
+
+class Alias(directive.RMLDirective):
+ signature = IAlias
+
def process(self):
- id, value = self.getPositionalArguments()
- manager = attr.getManager(self, interfaces.IStylesManager)
+ id, value = self.getAttributeValues(valuesOnly=True)
+ manager = attrng.getManager(self)
manager.styles[id] = value
Modified: z3c.rml/trunk/src/z3c/rml/stylesheet.py
===================================================================
--- z3c.rml/trunk/src/z3c/rml/stylesheet.py 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/stylesheet.py 2007-03-28 03:02:22 UTC (rev 73805)
@@ -20,122 +20,311 @@
import reportlab.lib.styles
import reportlab.lib.enums
import reportlab.platypus
-from z3c.rml import attr, element, error, interfaces, special
+from z3c.rml import attrng, directive, interfaces, occurence, special
-class Initialize(element.ContainerElement):
+class IInitialize(interfaces.IRMLDirectiveSignature):
+ """Do some RML processing initialization."""
+ occurence.containing(
+ occurence.ZeroOrMore('name', special.IName),
+ occurence.ZeroOrMore('alias', special.IAlias),
+ )
- subElements = {
+class Initialize(directive.RMLDirective):
+ signature = IInitialize
+ factories = {
'name': special.Name,
'alias': special.Alias,
}
-class ParagraphStyle(element.Element):
- attrs = (
- attr.Text('name'),
- attr.Text('alias'),
- attr.Style('parent'),
- attr.Text('fontName'),
- attr.Measurement('fontSize'),
- attr.Measurement('leading'),
- attr.Measurement('leftIndent'),
- attr.Measurement('rightIndent'),
- attr.Measurement('firstLineIndent'),
- attr.Measurement('spaceBefore'),
- attr.Measurement('spaceAfter'),
- attr.Choice('alignment',
- {'left':reportlab.lib.enums.TA_LEFT,
- 'right':reportlab.lib.enums.TA_RIGHT,
- 'center':reportlab.lib.enums.TA_CENTER,
- 'justify':reportlab.lib.enums.TA_JUSTIFY}),
- attr.Text('bulletFontName'),
- attr.Measurement('bulletFontSize'),
- attr.Measurement('bulletIndent'),
- attr.Color('textColor'),
- attr.Color('backColor'),
- attr.Bool('keepWithNext')
- )
+class IBaseParagraphStyle(interfaces.IRMLDirectiveSignature):
+
+ fontName = attrng.String(
+ title=u'Font Name',
+ description=u'The name of the font for the paragraph.',
+ required=False)
+
+ fontSize = attrng.Measurement(
+ title=u'Font Size',
+ description=u'The font size for the text of the paragraph.',
+ required=False)
+
+ leading = attrng.Measurement(
+ title=u'Leading',
+ description=(u'The height of a single paragraph line. It includes '
+ u'character height.'),
+ required=False)
+
+ leftIndent = attrng.Measurement(
+ title=u'Left Indentation',
+ description=u'General indentation on the left side.',
+ required=False)
+
+ rightIndent = attrng.Measurement(
+ title=u'Right Indentation',
+ description=u'General indentation on the right side.',
+ required=False)
+
+ firstLineIndent = attrng.Measurement(
+ title=u'First Line Indentation',
+ description=u'The indentation of the first line in the paragraph.',
+ required=False)
+
+ spaceBefore = attrng.Measurement(
+ title=u'Space Before',
+ description=u'The vertical space before the paragraph.',
+ required=False)
+
+ spaceAfter = attrng.Measurement(
+ title=u'Space After',
+ description=u'The vertical space after the paragraph.',
+ required=False)
+
+ alignment = attrng.Choice(
+ title=u'Alignment',
+ description=u'The text alignment.',
+ choices=interfaces.ALIGN_CHOICES,
+ required=False)
+
+ bulletFontName = attrng.String(
+ title=u'Bullet Font Name',
+ description=u'The font in which the bullet character will be rendered.',
+ required=False)
+
+ bulletFontSize = attrng.Measurement(
+ title=u'Bullet Font Size',
+ description=u'The font size of the bullet character.',
+ required=False)
+
+ bulletIndent = attrng.Measurement(
+ title=u'Bullet Indentation',
+ description=u'The indentation that is kept for a bullet point.',
+ required=False)
+
+ textColor = attrng.Color(
+ title=u'Text Color',
+ description=u'The color in which the text will appear.',
+ required=False)
+
+ backColor = attrng.Color(
+ title=u'Background Color',
+ description=u'The background color of the paragraph.',
+ required=False)
+
+ keepWithNext = attrng.Boolean(
+ title=u'Keep with Next',
+ description=(u'When set, this paragraph will always be in the same '
+ u'frame as the following flowable.'),
+ required=False)
+
+
+class IParagraphStyle(IBaseParagraphStyle):
+ """Defines a paragraph style and gives it a name."""
+
+ name = attrng.String(
+ title=u'Name',
+ description=u'The name of the style.',
+ required=True)
+
+ alias = attrng.String(
+ title=u'Alias',
+ description=u'An alias under which the style will also be known as.',
+ required=False)
+
+ parent = attrng.Style(
+ title=u'Parent',
+ description=(u'The apragraph style that will be used as a base for '
+ u'this one.'),
+ required=False)
+
+class ParagraphStyle(directive.RMLDirective):
+ signature = IParagraphStyle
+
def process(self):
- attrs = element.extractKeywordArguments(
- [(attrib.name, attrib) for attrib in self.attrs], self.element,
- self.parent)
+ kwargs = dict(self.getAttributeValues())
- parent = attrs.pop(
+ parent = kwargs.pop(
'parent', reportlab.lib.styles.getSampleStyleSheet()['Normal'])
style = copy.deepcopy(parent)
- for name, value in attrs.items():
+ for name, value in kwargs.items():
setattr(style, name, value)
- manager = attr.getManager(self, interfaces.IStylesManager)
+ manager = attrng.getManager(self)
manager.styles[style.name] = style
-class TableStyleCommand(element.Element):
+class ITableStyleCommand(interfaces.IRMLDirectiveSignature):
+
+ start = attrng.Sequence(
+ title=u'Start Coordinates',
+ description=u'The start table coordinates for the style instruction',
+ value_type=attrng.Combination(
+ value_types=(attrng.Integer(),
+ attrng.Choice(choices=interfaces.SPLIT_CHOICES))
+ ),
+ default=[0, 0],
+ min_length=2,
+ max_length=2,
+ required=True)
+
+ end = attrng.Sequence(
+ title=u'End Coordinates',
+ description=u'The end table coordinates for the style instruction',
+ value_type=attrng.Combination(
+ value_types=(attrng.Integer(),
+ attrng.Choice(choices=interfaces.SPLIT_CHOICES))
+ ),
+ default=[-1, -1],
+ min_length=2,
+ max_length=2,
+ required=True)
+
+class TableStyleCommand(directive.RMLDirective):
name = None
- attrs = (
- attr.Sequence('start', attr.Combination(
- valueTypes=(attr.Int(),
- attr.Choice(choices=('splitfirst', 'splitlast')) )),
- [0, 0], length=2),
- attr.Sequence('stop', attr.Combination(
- valueTypes=(attr.Int(),
- attr.Choice(choices=('splitfirst', 'splitlast')) )),
- [-1, -1], length=2) )
def process(self):
args = [self.name]
- for attribute in self.attrs:
- value = attribute.get(self.element, context=self)
- if value is not attr.DEFAULT:
- args.append(value)
- self.context.add(*args)
+ args += self.getAttributeValues(valuesOnly=True)
+ self.parent.style.add(*args)
+
+class IBlockFont(ITableStyleCommand):
+ """Set the font properties for the texts."""
+
+ name = attrng.String(
+ title=u'Font Name',
+ description=u'The name of the font for the cell.',
+ required=False)
+
+ size = attrng.Measurement(
+ title=u'Font Size',
+ description=u'The font size for the text of the cell.',
+ required=False)
+
+ leading = attrng.Measurement(
+ title=u'Leading',
+ description=(u'The height of a single text line. It includes '
+ u'character height.'),
+ required=False)
+
class BlockFont(TableStyleCommand):
+ signature = IBlockFont
name = 'FONT'
- attrs = TableStyleCommand.attrs + (
- attr.Text('name'),
- attr.Measurement('size'),
- attr.Measurement('leading') )
+class IBlockLeading(ITableStyleCommand):
+ """Set the text leading."""
+
+ length = attrng.Measurement(
+ title=u'Length',
+ description=(u'The height of a single text line. It includes '
+ u'character height.'),
+ required=True)
+
class BlockLeading(TableStyleCommand):
+ signature = IBlockLeading
name = 'LEADING'
- attrs = TableStyleCommand.attrs + (attr.Measurement('length'), )
+class IBlockTextColor(ITableStyleCommand):
+ """Set the text color."""
+
+ colorName = attrng.Color(
+ title=u'Color Name',
+ description=u'The color in which the text will appear.',
+ required=True)
+
class BlockTextColor(TableStyleCommand):
+ signature = IBlockTextColor
name = 'TEXTCOLOR'
- attrs = TableStyleCommand.attrs + (attr.Color('colorName'), )
+class IBlockAlignment(ITableStyleCommand):
+ """Set the text alignment."""
+
+ value = attrng.Choice(
+ title=u'Text Alignment',
+ description=u'The text alignment within the cell.',
+ choices=interfaces.ALIGN_TEXT_CHOICES,
+ required=True)
+
class BlockAlignment(TableStyleCommand):
+ signature = IBlockAlignment
name = 'ALIGNMENT'
- attrs = TableStyleCommand.attrs + (
- attr.Choice('value',
- {'left': 'LEFT', 'right': 'RIGHT',
- 'center': 'CENTER', 'decimal': 'DECIMAL'}), )
+class IBlockLeftPadding(ITableStyleCommand):
+ """Set the left padding of the cells."""
+
+ length = attrng.Measurement(
+ title=u'Length',
+ description=u'The size of the padding.',
+ required=True)
+
class BlockLeftPadding(TableStyleCommand):
+ signature = IBlockLeftPadding
name = 'LEFTPADDING'
- attrs = TableStyleCommand.attrs + (attr.Measurement('length'), )
+class IBlockRightPadding(ITableStyleCommand):
+ """Set the right padding of the cells."""
+
+ length = attrng.Measurement(
+ title=u'Length',
+ description=u'The size of the padding.',
+ required=True)
+
class BlockRightPadding(TableStyleCommand):
+ signature = IBlockRightPadding
name = 'RIGHTPADDING'
- attrs = TableStyleCommand.attrs + (attr.Measurement('length'), )
+class IBlockBottomPadding(ITableStyleCommand):
+ """Set the bottom padding of the cells."""
+
+ length = attrng.Measurement(
+ title=u'Length',
+ description=u'The size of the padding.',
+ required=True)
+
class BlockBottomPadding(TableStyleCommand):
+ signature = IBlockBottomPadding
name = 'BOTTOMPADDING'
- attrs = TableStyleCommand.attrs + (attr.Measurement('length'), )
+class IBlockTopPadding(ITableStyleCommand):
+ """Set the top padding of the cells."""
+
+ length = attrng.Measurement(
+ title=u'Length',
+ description=u'The size of the padding.',
+ required=True)
+
class BlockTopPadding(TableStyleCommand):
+ signature = IBlockTopPadding
name = 'TOPPADDING'
- attrs = TableStyleCommand.attrs + (attr.Measurement('length'), )
+class IBlockBackground(ITableStyleCommand):
+ """Define the background color of the cells.
+
+ It also supports alternating colors.
+ """
+
+ colorName = attrng.Color(
+ title=u'Color Name',
+ description=u'The color to use as the background for every cell.',
+ required=False)
+
+ colorsByRow = attrng.Sequence(
+ title=u'Colors By Row',
+ description=u'A list of colors to be used circularly for rows.',
+ value_type=attrng.Color(acceptNone=True),
+ required=False)
+
+ colorsByCol = attrng.Sequence(
+ title=u'Colors By Column',
+ description=u'A list of colors to be used circularly for columns.',
+ value_type=attrng.Color(acceptNone=True),
+ required=False)
+
class BlockBackground(TableStyleCommand):
+ signature = IBlockBackground
name = 'BACKGROUND'
- attrs = TableStyleCommand.attrs + (
- attr.Color('colorName'),
- attr.Sequence('colorsByRow', attr.Color()),
- attr.Sequence('colorsByCol', attr.Color()) )
def process(self):
args = [self.name]
@@ -144,64 +333,148 @@
elif 'colorsByCol' in self.element.keys():
args = [BlockColBackground.name]
- for attribute in self.attrs:
- value = attribute.get(self.element, context=self)
- if value is not attr.DEFAULT:
- args.append(value)
- self.context.add(*args)
+ args += self.getAttributeValues(valuesOnly=True)
+ self.parent.style.add(*args)
+class IBlockRowBackground(ITableStyleCommand):
+ """Define the background colors for rows."""
+
+ colorNames = attrng.Sequence(
+ title=u'Colors By Row',
+ description=u'A list of colors to be used circularly for rows.',
+ value_type=attrng.Color(),
+ required=True)
+
class BlockRowBackground(TableStyleCommand):
+ signature = IBlockRowBackground
name = 'ROWBACKGROUNDS'
- attrs = TableStyleCommand.attrs + (
- attr.Sequence('colorNames', attr.Color()), )
+class IBlockColBackground(ITableStyleCommand):
+ """Define the background colors for columns."""
+
+ colorNames = attrng.Sequence(
+ title=u'Colors By Row',
+ description=u'A list of colors to be used circularly for rows.',
+ value_type=attrng.Color(),
+ required=True)
+
class BlockColBackground(TableStyleCommand):
+ signature = IBlockColBackground
name = 'COLBACKGROUNDS'
- attrs = TableStyleCommand.attrs + (
- attr.Sequence('colorNames', attr.Color()), )
+class IBlockValign(ITableStyleCommand):
+ """Define the vertical alignment of the cells."""
+
+ value = attrng.Choice(
+ title=u'Vertical Alignment',
+ description=u'The vertical alignment of the text with the cells.',
+ choices=interfaces.VALIGN_TEXT_CHOICES,
+ required=True)
+
class BlockValign(TableStyleCommand):
+ signature = IBlockValign
name = 'VALIGN'
- attrs = TableStyleCommand.attrs + (
- attr.Choice('value',
- {'top': 'TOP', 'middle': 'MIDDLE', 'bottom': 'BOTTOM'}), )
+class IBlockSpan(ITableStyleCommand):
+ """Define a span over multiple cells (rows and columns)."""
+
class BlockSpan(TableStyleCommand):
+ signature = IBlockSpan
name = 'SPAN'
+class ILineStyle(ITableStyleCommand):
+ """Define the border line style of each cell."""
+
+ kind = attrng.Choice(
+ title=u'Kind',
+ description=u'The kind of line actions to be taken.',
+ choices=('GRID', 'BOX', 'OUTLINE', 'INNERGRID',
+ 'LINEBELOW', 'LINEABOVE', 'LINEBEFORE', 'LINEAFTER'),
+ required=True)
+
+ thickness = attrng.Measurement(
+ title=u'Thickness',
+ description=u'Line Thickness',
+ default=1,
+ required=True)
+
+ colorName = attrng.Color(
+ title=u'Color',
+ description=u'The color of the border line.',
+ default=None,
+ required=True)
+
+ cap = attrng.Choice(
+ title=u'Cap',
+ description=u'The cap at the end of a border line.',
+ choices=interfaces.CAP_CHOICES,
+ default=1,
+ required=True)
+
+ dash = attrng.Sequence(
+ title=u'Dash-Pattern',
+ description=u'The dash-pattern of a line.',
+ value_type=attrng.Measurement(),
+ default=None,
+ required=False)
+
+ join = attrng.Choice(
+ title=u'Join',
+ description=u'The way lines are joined together.',
+ choices=interfaces.JOIN_CHOICES,
+ default=1,
+ required=False)
+
+ count = attrng.Integer(
+ title=u'Count',
+ description=(u'Describes whether the line is a single (1) or '
+ u'double (2) line.'),
+ default=1,
+ required=False)
+
class LineStyle(TableStyleCommand):
- attrs = TableStyleCommand.attrs + (
- attr.Measurement('thickness', default=1),
- attr.Color('colorName', default=None),
- attr.Choice('cap', ('butt', 'round', 'square'), default=1),
- attr.Sequence('dash', attr.Measurement(), default=None),
- attr.Bool('join', default=1),
- attr.Int('count', default=1),
+ signature = ILineStyle
+
+ def process(self):
+ name = self.getAttributeValues(select=('kind',), valuesOnly=True)[0]
+ args = [name]
+ args += self.getAttributeValues(ignore=('kind',), valuesOnly=True)
+ self.parent.style.add(*args)
+
+class IBlockTableStyle(interfaces.IRMLDirectiveSignature):
+ """A style defining the look of a table."""
+ occurence.containing(
+ occurence.ZeroOrMore('blockFont', IBlockFont),
+ occurence.ZeroOrMore('blockLeading', IBlockLeading),
+ occurence.ZeroOrMore('blockTextColor', IBlockTextColor),
+ occurence.ZeroOrMore('blockAlignment', IBlockAlignment),
+ occurence.ZeroOrMore('blockLeftPadding', IBlockLeftPadding),
+ occurence.ZeroOrMore('blockRightPadding', IBlockRightPadding),
+ occurence.ZeroOrMore('blockBottomPadding', IBlockBottomPadding),
+ occurence.ZeroOrMore('blockTopPadding', IBlockTopPadding),
+ occurence.ZeroOrMore('blockBackground', IBlockBackground),
+ occurence.ZeroOrMore('blockRowBackground', IBlockRowBackground),
+ occurence.ZeroOrMore('blockColBackground', IBlockColBackground),
+ occurence.ZeroOrMore('blockValign', IBlockValign),
+ occurence.ZeroOrMore('blockSpan', IBlockSpan),
+ occurence.ZeroOrMore('lineStyle', ILineStyle)
)
- @property
- def name(self):
- cmds = ['GRID', 'BOX', 'OUTLINE', 'INNERGRID',
- 'LINEBELOW', 'LINEABOVE', 'LINEBEFORE', 'LINEAFTER']
- return attr.Choice(
- 'kind', dict([(cmd.lower(), cmd) for cmd in cmds])
- ).get(self.element, context=self)
+ id = attrng.String(
+ title=u'Id',
+ description=u'The name/id of the style.',
+ required=True)
- def process(self):
- args = [self.name]
- for attribute in self.attrs:
- value = attribute.get(self.element, context=self)
- if value is not attr.DEFAULT:
- args.append(value)
- self.context.add(*args)
+ keepWithNext = attrng.Boolean(
+ title=u'Keep with Next',
+ description=(u'When set, this paragraph will always be in the same '
+ u'frame as the following flowable.'),
+ required=False)
-class BlockTableStyle(element.ContainerElement):
+class BlockTableStyle(directive.RMLDirective):
+ signature = IBlockTableStyle
- attrs = (
- attr.Bool('keepWithNext'),
- )
-
- subElements = {
+ factories = {
'blockFont': BlockFont,
'blockLeading': BlockLeading,
'blockTextColor': BlockTextColor,
@@ -219,25 +492,34 @@
}
def process(self):
- id = attr.Text('id').get(self.element, context=self)
+ kw = dict(self.getAttributeValues())
+ id = kw.pop('id')
# Create Style
- style = reportlab.platypus.tables.TableStyle()
- attrs = element.extractAttributes(self.attrs, self.element, self)
- for name, value in attrs.items():
- setattr(style, name, value)
+ self.style = reportlab.platypus.tables.TableStyle()
+ for name, value in kw.items():
+ setattr(self.style, name, value)
# Fill style
- self.processSubElements(style)
+ self.processSubDirectives()
# Add style to the manager
- manager = attr.getManager(self, interfaces.IStylesManager)
- manager.styles[id] = style
+ manager = attrng.getManager(self)
+ manager.styles[id] = self.style
-class Stylesheet(element.ContainerElement):
+class IStylesheet(interfaces.IRMLDirectiveSignature):
+ """A styleheet defines the styles that can be used in the document."""
+ occurence.containing(
+ occurence.ZeroOrOne('initialize', IInitialize),
+ occurence.ZeroOrMore('paraStyle', IParagraphStyle),
+ occurence.ZeroOrMore('blockTableStyle', IBlockTableStyle),
+ # TODO:
+ #occurence.ZeroOrMore('boxStyle', IBoxStyle),
+ )
- subElements = {
+class Stylesheet(directive.RMLDirective):
+ signature = IStylesheet
+
+ factories = {
'initialize': Initialize,
'paraStyle': ParagraphStyle,
'blockTableStyle': BlockTableStyle,
- # TODO: 'boxStyle': BoxStyle,
}
- order = ('initialize', 'paraStyle', 'blockTableStyle')
Modified: z3c.rml/trunk/src/z3c/rml/template.py
===================================================================
--- z3c.rml/trunk/src/z3c/rml/template.py 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/template.py 2007-03-28 03:02:22 UTC (rev 73805)
@@ -16,136 +16,256 @@
$Id$
"""
__docformat__ = "reStructuredText"
+import zope.interface
from reportlab import platypus
-from z3c.rml import attr, canvas, element, flowable, interfaces, stylesheet
+from z3c.rml import attrng, directive, interfaces, occurence
+from z3c.rml import canvas, flowable, stylesheet
+class IStory(flowable.IFlow):
+ """The story of the PDF file."""
+
+ firstPageTemplate = attrng.Text(
+ title=u'First Page Template',
+ description=u'The first page template to be used.',
+ default=None,
+ required=False)
+
class Story(flowable.Flow):
+ signature = IStory
- def getFirstPageTemplateIndex(self, doc):
- fpt = attr.Text('firstPageTemplate').get(self.element, None)
+ def process(self):
+ self.parent.flowables = super(Story, self).process()
+ self.parent.doc._firstPageTemplateIndex = self.getFirstPTIndex()
+
+ def getFirstPTIndex(self):
+ args = dict(self.getAttributeValues(select=('firstPageTemplate',)))
+ fpt = args.pop('firstPageTemplate', None)
if fpt is None:
return 0
- for idx, pageTemplate in enumerate(doc.pageTemplates):
+ for idx, pageTemplate in enumerate(self.parent.doc.pageTemplates):
if pageTemplate.id == fpt:
return idx
raise ValueError('%r is not a correct page template id.' %fpt)
-class Frame(element.FunctionElement):
- args = (
- attr.Measurement('x1', allowPercentage=True),
- attr.Measurement('y1', allowPercentage=True),
- attr.Measurement('width', allowPercentage=True),
- attr.Measurement('height', allowPercentage=True),
- )
- kw = (
- ('id', attr.Text('id')),
- # Non-RML compliant extensions
- ('leftPadding', attr.Measurement('leftPadding', 0)),
- ('rightPadding', attr.Measurement('rightPadding', 0)),
- ('topPadding', attr.Measurement('topPadding', 0)),
- ('bottomPadding', attr.Measurement('bottomPadding', 0)),
- ('showBoundary', attr.Bool('showBoundary')),
- )
+class IFrame(interfaces.IRMLDirectiveSignature):
+ """A frame on a page."""
+
+ x1 = attrng.Measurement(
+ title=u'X-Position',
+ description=u'The X-Position of the lower-left corner of the frame.',
+ allowPercentage=True,
+ required=True)
+
+ y1 = attrng.Measurement(
+ title=u'Y-Position',
+ description=u'The Y-Position of the lower-left corner of the frame.',
+ allowPercentage=True,
+ required=True)
+
+ width = attrng.Measurement(
+ title=u'Width',
+ description=u'The width of the frame.',
+ allowPercentage=True,
+ required=True)
+
+ height = attrng.Measurement(
+ title=u'Height',
+ description=u'The height of the frame.',
+ allowPercentage=True,
+ required=True)
+
+ id = attrng.Text(
+ title=u'Id',
+ description=u'The id of the frame.',
+ required=False)
+
+ leftPadding = attrng.Measurement(
+ title=u'Left Padding',
+ description=u'The left padding of the frame.',
+ default=0,
+ required=False)
+
+ rightPadding = attrng.Measurement(
+ title=u'Right Padding',
+ description=u'The right padding of the frame.',
+ default=0,
+ required=False)
+
+ topPadding = attrng.Measurement(
+ title=u'Top Padding',
+ description=u'The top padding of the frame.',
+ default=0,
+ required=False)
+
+ bottomPadding = attrng.Measurement(
+ title=u'Bottom Padding',
+ description=u'The bottom padding of the frame.',
+ default=0,
+ required=False)
+
+ showBoundary = attrng.Boolean(
+ title=u'Show Boundary',
+ description=u'A flag to show the boundary of the frame.',
+ required=False)
+
+
+class Frame(directive.RMLDirective):
+ signature = IFrame
+
def process(self):
# get the page size
- size = self.context.pagesize
+ size = self.parent.pt.pagesize
if size is None:
- size = self.parent.context.pagesize
+ size = self.parent.parent.parent.doc.pagesize
# Get the arguments
- args = self.getPositionalArguments()
- kw = self.getKeywordArguments()
+ args = dict(self.getAttributeValues())
# Deal with percentages
- if isinstance(args[0], basestring) and args[0].endswith('%'):
- args[0] = float(args[0][:-1])/100*size[0]
- if isinstance(args[1], basestring) and args[1].endswith('%'):
- args[1] = float(args[1][:-1])/100*size[1]
- if isinstance(args[2], basestring) and args[2].endswith('%'):
- args[2] = float(args[2][:-1])/100*size[0]
- if isinstance(args[3], basestring) and args[3].endswith('%'):
- args[3] = float(args[3][:-1])/100*size[1]
- frame = platypus.Frame(*args, **kw)
+ for name, dir in (('x1', 0), ('y1', 1), ('width', 0), ('height', 1)):
+ if isinstance(args[name], basestring) and args[name].endswith('%'):
+ args[name] = float(args[name][:-1])/100*size[dir]
+ frame = platypus.Frame(**args)
self.parent.frames.append(frame)
-class PageGraphics(element.Element):
+class IPageGraphics(canvas.IDrawing):
+ """Define the page graphics for the page template."""
+class PageGraphics(directive.RMLDirective):
+ zope.interface.implements(interfaces.ICanvasManager)
+ signature = IPageGraphics
+
def process(self):
def drawOnCanvas(canv, doc):
canv.saveState()
- drawing = canvas.Drawing(self.element, self, canv)
+ self.canvas = canv
+ drawing = canvas.Drawing(self.element, self)
drawing.process()
canv.restoreState()
- self.context.onPage = drawOnCanvas
+ self.parent.pt.onPage = drawOnCanvas
-class PageTemplate(element.FunctionElement, element.ContainerElement):
- args = (attr.Text('id'),)
- kw = (
- ('pagesize', attr.PageSize('pageSize',)),
- ('rotation', attr.Int('rotation')) )
+class IPageTemplate(interfaces.IRMLDirectiveSignature):
+ """Define a page template."""
+ occurence.containing(
+ occurence.OneOrMore('frame', IFrame),
+ occurence.ZeroOrOne('pageGraphics', IPageGraphics),
+ )
- subElements = {
+ id = attrng.Text(
+ title=u'Id',
+ description=u'The id of the template.',
+ required=True)
+
+ pagesize = attrng.PageSize(
+ title=u'Page Size',
+ description=u'The Page Size.',
+ required=False)
+
+ rotation = attrng.Integer(
+ title=u'Rotation',
+ description=u'The rotation of the page in multiples of 90 degrees.',
+ required=False)
+
+
+
+class PageTemplate(directive.RMLDirective):
+ signature = IPageTemplate
+ factories = {
'frame': Frame,
'pageGraphics': PageGraphics,
}
def process(self):
- args = self.getPositionalArguments()
+ args = dict(self.getAttributeValues())
+ pagesize = args.pop('pagesize', None)
+
self.frames = []
- pt = platypus.PageTemplate(*args)
- self.processSubElements(pt)
- pt.frames = self.frames
+ self.pt = platypus.PageTemplate(**args)
- kw = self.getKeywordArguments()
- if 'pagesize' in kw:
- pt.pagesize = kw['pagesize']
+ self.processSubDirectives()
+ self.pt.frames = self.frames
- self.context.addPageTemplates(pt)
+ if pagesize:
+ self.pt.pagesize = pagesize
+ self.parent.parent.doc.addPageTemplates(self.pt)
-class Template(element.ContainerElement):
- templateArgs = (
- ('pagesize', attr.PageSize('pageSize',)),
- ('rotation', attr.Int('rotation')),
- ('leftMargin', attr.Measurement('leftMargin')),
- ('rightMargin', attr.Measurement('rightMargin')),
- ('topMargin', attr.Measurement('topMargin')),
- ('bottomMargin', attr.Measurement('bottomMargin')),
- ('showBoundary', attr.Bool('showBoundary')),
- ('allowSplitting', attr.Bool('allowSplitting')),
- ('title', attr.Text('title')),
- ('author', attr.Text('author')) )
+class ITemplate(interfaces.IRMLDirectiveSignature):
+ """Define a page template."""
+ occurence.containing(
+ occurence.OneOrMore('pagetemplate', IPageTemplate),
+ )
- documentArgs = (
- ('_debug', attr.Bool('debug')),
- ('pageCompression', attr.DefaultBool('compression')),
- ('invariant', attr.DefaultBool('invariant')) )
+ pagesize = attrng.PageSize(
+ title=u'Page Size',
+ description=u'The Page Size.',
+ required=False)
- subElements = {
- 'pageTemplate': PageTemplate,
- 'stylesheet': stylesheet.Stylesheet,
- }
+ rotation = attrng.Integer(
+ title=u'Rotation',
+ description=u'The rotation of the page in multiples of 90 degrees.',
+ required=False)
- def process(self, outputFile):
- docElement = self.element
- self.processSubElements(None)
+ leftMargin = attrng.Measurement(
+ title=u'Left Margin',
+ description=u'The left margin of the template.',
+ default=0,
+ required=False)
- self.element = self.element.find('template')
+ rightMargin = attrng.Measurement(
+ title=u'Right Margin',
+ description=u'The right margin of the template.',
+ default=0,
+ required=False)
- kw = element.extractKeywordArguments(
- self.documentArgs, docElement, self)
- kw.update(element.extractKeywordArguments(
- self.templateArgs, self.element, self))
- doc = platypus.BaseDocTemplate(outputFile, **kw)
+ topMargin = attrng.Measurement(
+ title=u'Top Margin',
+ description=u'The top margin of the template.',
+ default=0,
+ required=False)
- self.processSubElements(doc)
+ bottomMargin = attrng.Measurement(
+ title=u'Bottom Margin',
+ description=u'The bottom margin of the template.',
+ default=0,
+ required=False)
- story = Story(docElement.find('story'), self, doc)
- flowables = story.process()
+ showBoundary = attrng.Boolean(
+ title=u'Show Boundary',
+ description=u'A flag to show the boundary of the template.',
+ required=False)
- doc._firstPageTemplateIndex = story.getFirstPageTemplateIndex(doc)
- doc.multiBuild(flowables)
+ allowSplitting = attrng.Boolean(
+ title=u'Allow Splitting',
+ description=u'A flag to allow splitting over multiple templates.',
+ required=False)
+
+ title = attrng.Text(
+ title=u'Title',
+ description=u'The title of the PDF document.',
+ required=False)
+
+ author = attrng.Text(
+ title=u'Author',
+ description=u'The author of the PDF document.',
+ required=False)
+
+class Template(directive.RMLDirective):
+ signature = ITemplate
+ factories = {
+ 'pageTemplate': PageTemplate,
+ }
+
+ def process(self):
+ args = self.getAttributeValues()
+ args += self.parent.getAttributeValues(
+ select=('debug', 'compression', 'invariant'),
+ attrMapping={'debug': '_debug', 'compression': 'pageCompression'})
+
+ self.parent.doc = platypus.BaseDocTemplate(
+ self.parent.outputFile, **dict(args))
+ self.processSubDirectives()
Added: z3c.rml/trunk/src/z3c/rml/tests/input/tag-color.rml
===================================================================
--- z3c.rml/trunk/src/z3c/rml/tests/input/tag-color.rml 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/tests/input/tag-color.rml 2007-03-28 03:02:22 UTC (rev 73805)
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="iso-8859-1" standalone="no"?>
+<!DOCTYPE document SYSTEM "rml.dtd">
+<document filename="tag-color.pdf">
+ <docinit>
+ <color id="favorite-color" RGB="yellow" />
+ </docinit>
+ <template>
+ <pageTemplate id="main">
+ <frame id="first" x1="1cm" y1="1cm" width="19cm" height="26cm"/>
+ </pageTemplate>
+ </template>
+ <story>
+ <para fontSize="40" textColor="favorite-color">
+ This is my favorite color!
+ </para>
+ </story>
+</document>
Modified: z3c.rml/trunk/src/z3c/rml/tests/input/tag-image-1.rml
===================================================================
--- z3c.rml/trunk/src/z3c/rml/tests/input/tag-image-1.rml 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/tests/input/tag-image-1.rml 2007-03-28 03:02:22 UTC (rev 73805)
@@ -1,19 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE document SYSTEM "rml.dtd">
-
<document filename="drawString.pdf">
<pageDrawing>
- <image file="zope3logo.gif" x="2in" y="10in"
+ <image file="[z3c.rml.tests]/input/zope3logo.gif"
+ x="2in" y="10in"
showBoundary="true" />
- <image file="zope3logo.gif" x="2in" y="8in" width="5in"
+ <image file="[z3c.rml.tests]/input/zope3logo.gif"
+ x="2in" y="8in" width="5in"
preserveAspectRatio="true" />
- <image file="zope3logo.gif" x="2in" y="4in" height="3in"
+ <image file="[z3c.rml.tests]/input/zope3logo.gif"
+ x="2in" y="4in" height="3in"
preserveAspectRatio="true" />
- <image file="zope3logo.gif" x="2in" y="2in" width="0.5in" height="3in"
+ <image file="[z3c.rml.tests]/input/zope3logo.gif"
+ x="2in" y="2in" width="0.5in" height="3in"
preserveAspectRatio="true" />
</pageDrawing>
Modified: z3c.rml/trunk/src/z3c/rml/tests/input/tag-image.rml
===================================================================
--- z3c.rml/trunk/src/z3c/rml/tests/input/tag-image.rml 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/tests/input/tag-image.rml 2007-03-28 03:02:22 UTC (rev 73805)
@@ -1,16 +1,19 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE document SYSTEM "rml.dtd">
-
-<document filename="drawString.pdf">
+<document filename="tag-image.pdf">
<pageDrawing>
- <image file="zope3logo.gif" x="2in" y="10in" />
+ <image file="[z3c.rml.tests]/input/zope3logo.gif"
+ x="2in" y="10in" />
- <image file="zope3logo.gif" x="2in" y="8in" width="5in" />
+ <image file="[z3c.rml.tests]/input/zope3logo.gif"
+ x="2in" y="8in" width="5in" />
- <image file="zope3logo.gif" x="2in" y="4in" height="3in" />
+ <image file="[z3c.rml.tests]/input/zope3logo.gif"
+ x="2in" y="4in" height="3in" />
- <image file="zope3logo.gif" x="2in" y="2in" width="0.5in" height="3in" />
+ <image file="[z3c.rml.tests]/input/zope3logo.gif"
+ x="2in" y="2in" width="0.5in" height="3in" />
</pageDrawing>
</document>
Added: z3c.rml/trunk/src/z3c/rml/tests/input/tag-path.rml
===================================================================
--- z3c.rml/trunk/src/z3c/rml/tests/input/tag-path.rml 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/tests/input/tag-path.rml 2007-03-28 03:02:22 UTC (rev 73805)
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE document SYSTEM "rml.dtd">
+
+<document filename="tag-path.pdf">
+ <pageDrawing>
+
+ <path x="2cm" y="26cm" close="true">
+ 6cm 26cm
+ 8cm 23cm
+ 4cm 23cm
+ </path>
+
+ <path x="10cm" y="26cm" close="true" fill="true">
+ 14cm 26cm
+ 16cm 23cm
+ 12cm 23cm
+ </path>
+
+ <path x="2cm" y="22cm" >
+ 4cm 22cm
+ <moveto>
+ 6cm 22cm
+ </moveto>
+ 7cm 20.5cm
+ <moveto>
+ 8cm 19cm
+ </moveto>
+ 6cm 19cm
+ <moveto>
+ 4cm 19cm
+ </moveto>
+ 3cm 20.5cm
+ </path>
+
+ </pageDrawing>
+</document>
Added: z3c.rml/trunk/src/z3c/rml/tests/input/tag-registerCidFont.rml
===================================================================
--- z3c.rml/trunk/src/z3c/rml/tests/input/tag-registerCidFont.rml 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/tests/input/tag-registerCidFont.rml 2007-03-28 03:02:22 UTC (rev 73805)
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE document SYSTEM "rml.dtd">
+<document filename="tag-registerTTFont.pdf">
+ <docinit>
+ <registerCidFont faceName="HeiseiMin-W3" />
+ </docinit>
+ <template>
+ <pageTemplate id="main">
+ <frame id="first" x1="1cm" y1="1cm" width="19cm" height="26cm"/>
+ </pageTemplate>
+ </template>
+ <story>
+ <para fontName="HeiseiMin-W3" fontSize="40">
+ æ¥æ¬èªã¯é£ããã§ããï¼
+ </para>
+ </story>
+</document>
Added: z3c.rml/trunk/src/z3c/rml/tests/input/tag-registerTTFont.rml
===================================================================
--- z3c.rml/trunk/src/z3c/rml/tests/input/tag-registerTTFont.rml 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/tests/input/tag-registerTTFont.rml 2007-03-28 03:02:22 UTC (rev 73805)
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="iso-8859-1" standalone="no"?>
+<!DOCTYPE document SYSTEM "rml.dtd">
+<document filename="tag-registerTTFont.pdf">
+ <docinit>
+ <registerTTFont faceName="rina" fileName="rina.ttf"/>
+ </docinit>
+ <template>
+ <pageTemplate id="main">
+ <frame id="first" x1="1cm" y1="1cm" width="19cm" height="26cm"/>
+ </pageTemplate>
+ </template>
+ <story>
+ <para fontName="rina" fontSize="40">Hello World!</para>
+ </story>
+</document>
Added: z3c.rml/trunk/src/z3c/rml/tests/input/tag-registerType1Face.rml
===================================================================
--- z3c.rml/trunk/src/z3c/rml/tests/input/tag-registerType1Face.rml 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/tests/input/tag-registerType1Face.rml 2007-03-28 03:02:22 UTC (rev 73805)
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="iso-8859-1" standalone="no"?>
+<!DOCTYPE document SYSTEM "rml.dtd">
+<document filename="tag-registerType1Face.pdf">
+ <docinit>
+ <registerType1Face afmFile="LeERC___.AFM" pfbFile="LeERC___.PFB" />
+ <registerFont
+ name="LettErrorRobot-Chrome"
+ faceName="LettErrorRobot-Chrome"
+ encName="WinAnsiEncoding" />
+ </docinit>
+ <template>
+ <pageTemplate id="main">
+ <frame id="first" x1="1cm" y1="1cm" width="19cm" height="26cm"/>
+ </pageTemplate>
+ </template>
+ <story>
+ <para fontName="LettErrorRobot-Chrome" fontSize="40">Hello World!</para>
+ </story>
+</document>
Added: z3c.rml/trunk/src/z3c/rml/tests/input/tag-textAnnotation.rml
===================================================================
--- z3c.rml/trunk/src/z3c/rml/tests/input/tag-textAnnotation.rml 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/tests/input/tag-textAnnotation.rml 2007-03-28 03:02:22 UTC (rev 73805)
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE document SYSTEM "rml.dtd">
+
+<document filename="tag-textAnnotation.pdf">
+ <pageDrawing>
+ <textAnnotation>
+ <param name="Rect">0,0,1,1</param>
+ <param name="F">3</param>
+ <param name="escape">6</param>
+X::PDF
+PX(S)
+MT(PINK)
+ </textAnnotation>
+ </pageDrawing>
+</document>
Modified: z3c.rml/trunk/src/z3c/rml/tests/test_rml.py
===================================================================
--- z3c.rml/trunk/src/z3c/rml/tests/test_rml.py 2007-03-27 19:48:05 UTC (rev 73804)
+++ z3c.rml/trunk/src/z3c/rml/tests/test_rml.py 2007-03-28 03:02:22 UTC (rev 73805)
@@ -19,7 +19,7 @@
import unittest
import sys
import z3c.rml.tests
-from z3c.rml import rml2pdf, attr
+from z3c.rml import rml2pdf, attrng
class RMLRenderingTestCase(unittest.TestCase):
@@ -30,17 +30,17 @@
def setUp(self):
# Switch file opener for Image attibute
- self._imageOpen = attr.Image.open
+ self._fileOpen = attrng.File.open
def testOpen(img, filename):
path = os.path.join(os.path.dirname(self._inPath), filename)
return open(path)
- attr.Image.open = testOpen
+ attrng.File.open = testOpen
import z3c.rml.tests.module
sys.modules['module'] = z3c.rml.tests.module
sys.modules['mymodule'] = z3c.rml.tests.module
def tearDown(self):
- attr.Image.open = self._imageOpen
+ attrng.File.open = self._fileOpen
del sys.modules['module']
del sys.modules['mymodule']
@@ -58,7 +58,7 @@
inPath = os.path.join(inputDir, filename)
outPath = os.path.join(outputDir, filename[:-4] + '.pdf')
# Create new type, so that we can get test matching
- TestCase = type(filename[:-4].title(), (RMLRenderingTestCase,), {})
+ TestCase = type(filename[:-4], (RMLRenderingTestCase,), {})
case = TestCase(inPath, outPath)
suite.addTest(case)
return suite
More information about the Checkins
mailing list