Skip to content

Commit

Permalink
- Implemented ol, ul, and li directives, which allow highly
Browse files Browse the repository at this point in the history
  flexible lists to be created. Also implemented a complimentary ``listStyle``
  directive.
  • Loading branch information
strichter committed Dec 20, 2012
1 parent 1055f98 commit 55bf1f9
Show file tree
Hide file tree
Showing 11 changed files with 498 additions and 36 deletions.
4 changes: 4 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ CHANGES
- Implemented ``startIndex`` and ``showIndex`` directive. Also hooked up
``index`` in paragraphs properly. You can now create real book indexes.

- Implemented ``ol``, ``ul``, and ``li`` directives, which allow highly
flexible lists to be created. Also implemented a complimentary ``listStyle``
directive.

- Don't show "doc" namespace in reference snippets.

- Create a list of RML2PDF and z3c.rml differences.
Expand Down
20 changes: 0 additions & 20 deletions RML-DIFFERENCES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,9 @@ naming.

- docinit: pageMode, pageLayout, useCropMarks
* alias
* name
* namedString
* outlineAdd
* registerFontFamily
* logConfig
* cropMarks
* startIndex

- template: firstPageTemplate

Expand Down Expand Up @@ -48,26 +44,12 @@ naming.

- widget

- para: -fontName, -fontSize, -leading, -leftIndent, -rightIndent,
-firstLineIndent, -spaceBefore, -spaceAfter, -alignement, -bulletFontName,
-bulletFontSize, -bulletIndent, -textColor, -backColor, -keepWithText,
-wordWrap, -borderColor, -borderWidth, -borderPadding, -borderRadius,
-dedent

- title: -<same as para>

-> Ditto h*

- a

- evalString

- -keepTogether

- pto
* pto_trailer
* pto_header

- image: -showBoundary, -preserveAspectRatio

- doForm
Expand Down Expand Up @@ -167,6 +149,4 @@ naming.

- rectangle, table row, table cell, etc.: href and/or destination (Test 038)

- ol, ul, li

- -addMapping
36 changes: 21 additions & 15 deletions src/z3c/rml/attr.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@
logger = logging.getLogger("z3c.rml")


def getFileInfo(attr):
root = attr.context
def getFileInfo(directive):
root = directive
while root.parent:
root = root.parent
return '(file %s, line %i)' % (
root.filename, attr.context.element.sourceline)
root.filename, directive.element.sourceline)


def getManager(context, interface=None):
Expand Down Expand Up @@ -87,7 +87,7 @@ def get(self):
name = self.deprecatedName
logger.warn(
u'Deprecated attribute "%s": %s %s' % (
name, self.deprecatedReason, getFileInfo(self)))
name, self.deprecatedReason, getFileInfo(self.context)))
else:
name = self.__name__
# Extract the value.
Expand All @@ -102,14 +102,16 @@ def get(self):

class BaseChoice(RMLAttribute):
choices = {}
doLower = True

def fromUnicode(self, value):
value = value.lower()
if self.doLower:
value = value.lower()
if value in self.choices:
return self.choices[value]
raise ValueError(
'%r not a valid value for attribute "%s". %s' % (
value, self.__name__, getFileInfo(self)))
value, self.__name__, getFileInfo(self.context)))


class Combination(RMLAttribute):
Expand All @@ -127,7 +129,8 @@ def fromUnicode(self, value):
except ValueError:
pass
raise ValueError(
'"%s" is not a valid value. %s' %(value, getFileInfo(self)))
'"%s" is not a valid value. %s' %(
value, getFileInfo(self.context)))


class String(RMLAttribute, zope.schema.Bytes):
Expand Down Expand Up @@ -185,18 +188,21 @@ def fromUnicode(self, ustr):
(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. %s' % (
self.min_length, self.max_length, getFileInfo(self)))
self.min_length, self.max_length,
getFileInfo(self.context)))
return result


class Choice(BaseChoice):
"""A choice of several values. The values are always case-insensitive."""

def __init__(self, choices=None, *args, **kw):
def __init__(self, choices=None, doLower=True, *args, **kw):
super(Choice, self).__init__(*args, **kw)
if isinstance(choices, (tuple, list)):
choices = dict([(val.lower(), val) for val in choices])
choices = dict(
[(val.lower() if doLower else val, val) for val in choices])
self.choices = choices
self.doLower = doLower


class Boolean(BaseChoice):
Expand Down Expand Up @@ -252,7 +258,7 @@ def fromUnicode(self, value):
return unit[1]*float(res.group(1))
raise ValueError(
'The value %r is not a valid measurement. %s' % (
value, getFileInfo(self)))
value, getFileInfo(self.context)))


class File(Text):
Expand All @@ -278,7 +284,7 @@ def fromUnicode(self, value):
if result is None:
raise ValueError(
'The package-path-pair you specified was incorrect. %s' %(
getFileInfo(self)))
getFileInfo(self.context)))
modulepath, path = result.groups()
module = __import__(modulepath, {}, {}, (modulepath))
value = os.path.join(os.path.dirname(module.__file__), path)
Expand Down Expand Up @@ -341,7 +347,7 @@ def fromUnicode(self, value):
except:
raise ValueError(
'The color specification "%s" is not valid. %s' % (
value, getFileInfo(self)))
value, getFileInfo(self.context)))

def _getStyle(context, value):
manager = getManager(context)
Expand All @@ -354,7 +360,7 @@ def _getStyle(context, value):
elif value.startswith('style.') and value[6:] in styles:
return styles[value[6:]]
raise ValueError('Style %r could not be found. %s' % (
value, getFileInfo(self)))
value, getFileInfo(context)))

class Style(String):
"""Requires a valid style to be entered.
Expand Down Expand Up @@ -467,7 +473,7 @@ def fromUnicode(self, ustr):
if len(result) % self.columns != 0:
raise ValueError(
'Number of elements must be divisible by %i. %s' %(
self.columns, getFileInfo(self)))
self.columns, getFileInfo(self.context)))
return [result[i*self.columns:(i+1)*self.columns]
for i in range(len(result)/self.columns)]

Expand Down
2 changes: 1 addition & 1 deletion src/z3c/rml/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from reportlab.lib import colors, fonts
from reportlab.platypus import tableofcontents

from z3c.rml import attr, canvas, directive, interfaces, occurence
from z3c.rml import attr, canvas, directive, interfaces, list, occurence
from z3c.rml import pdfinclude, special, storyplace, stylesheet, template


Expand Down
2 changes: 2 additions & 0 deletions src/z3c/rml/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
SPLIT_CHOICES = ('splitfirst', 'splitlast')
TEXT_TRANSFORM_CHOICES = ('uppercase', 'lowercase')
LIST_FORMATS = ('I', 'i', '123', 'ABC', 'abc')
ORDERED_LIST_TYPES = ('I', 'i', '1', 'A', 'a')
UNORDERED_BULLET_VALUES = ('circle', 'square', 'disc', 'diamond', 'rarrowhead')

class IRML2PDF(zope.interface.Interface):
"""This is the main public API of z3c.rml"""
Expand Down
181 changes: 181 additions & 0 deletions src/z3c/rml/list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
##############################################################################
#
# Copyright (c) 2012 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.
#
##############################################################################
"""``ul``, ``ol``, and ``li`` directives.
"""
__docformat__ = "reStructuredText"
import copy
import reportlab.lib.styles
import reportlab.platypus
import zope.schema
from reportlab.platypus import flowables

from z3c.rml import attr, directive, flowable, interfaces, occurence, stylesheet


class IListItem(stylesheet.IMinimalListStyle, flowable.IFlow):
"""A list item in an ordered or unordered list."""

style = attr.Style(
title=u'Style',
description=u'The list style that is applied to the list.',
required=False)

class ListItem(flowable.Flow):
signature = IListItem
klass = reportlab.platypus.ListItem
attrMapping = {}

styleAttributes = zope.schema.getFieldNames(stylesheet.IMinimalListStyle)

def processStyle(self, style):
attrs = self.getAttributeValues(select=self.styleAttributes)
if attrs or not hasattr(style, 'value'):
style = copy.deepcopy(style)
# Sigh, this is needed since unordered list items expect the value.
style.value = style.start
for name, value in attrs:
setattr(style, name, value)
return style

def process(self):
self.processSubDirectives()
args = dict(self.getAttributeValues(ignore=self.styleAttributes))
if 'style' not in args:
args['style'] = self.parent.baseStyle
args['style'] = self.processStyle(args['style'])
li = self.klass(self.flow, **args)
self.parent.flow.append(li)


class IOrderedListItem(IListItem):
"""An ordered list item."""

value = attr.Integer(
title=u'Bullet Value',
description=u'The counter value.',
required=False)

class OrderedListItem(ListItem):
signature = IOrderedListItem


class IUnorderedListItem(IListItem):
"""An ordered list item."""

value = attr.Choice(
title=u'Bullet Value',
description=u'The type of bullet character.',
choices=interfaces.UNORDERED_BULLET_VALUES,
required=False)

class UnorderedListItem(ListItem):
signature = IUnorderedListItem

styleAttributes = ListItem.styleAttributes + ['value']


class IListBase(stylesheet.IBaseListStyle):

style = attr.Style(
title=u'Style',
description=u'The list style that is applied to the list.',
required=False)

class ListBase(directive.RMLDirective):
klass = reportlab.platypus.ListFlowable
factories = {'li': ListItem}
attrMapping = {}

styleAttributes = zope.schema.getFieldNames(stylesheet.IBaseListStyle)

def __init__(self, *args, **kw):
super(ListBase, self).__init__(*args, **kw)
self.flow = []

def processStyle(self, style):
attrs = self.getAttributeValues(
select=self.styleAttributes, attrMapping=self.attrMapping)
if attrs:
style = copy.deepcopy(style)
for name, value in attrs:
setattr(style, name, value)
return style

def process(self):
args = dict(self.getAttributeValues(
ignore=self.styleAttributes, attrMapping=self.attrMapping))
if 'style' not in args:
args['style'] = reportlab.lib.styles.ListStyle('List')
args['style'] = self.baseStyle = self.processStyle(args['style'])
self.processSubDirectives()
li = self.klass(self.flow, **args)
self.parent.flow.append(li)


class IOrderedList(IListBase):
"""An ordered list."""
occurence.containing(
occurence.ZeroOrMore('li', IOrderedListItem),
)

bulletType = attr.Choice(
title=u'Bullet Type',
description=u'The type of bullet formatting.',
choices=interfaces.ORDERED_LIST_TYPES,
doLower=False,
required=False)

class OrderedList(ListBase):
signature = IOrderedList
factories = {'li': OrderedListItem}

styleAttributes = ListBase.styleAttributes + ['bulletType']


class IUnorderedList(IListBase):
"""And unordered list."""
occurence.containing(
occurence.ZeroOrMore('li', IUnorderedListItem),
)

value = attr.Choice(
title=u'Bullet Value',
description=u'The type of bullet character.',
choices=interfaces.UNORDERED_BULLET_VALUES,
default='disc',
required=False)

class UnorderedList(ListBase):
signature = IUnorderedList
attrMapping = {'value': 'start'}
factories = {'li': UnorderedListItem}

def getAttributeValues(self, *args, **kw):
res = super(UnorderedList, self).getAttributeValues(*args, **kw)
res.append(('bulletType', 'bullet'))
return res

flowable.Flow.factories['ol'] = OrderedList
flowable.IFlow.setTaggedValue(
'directives',
flowable.IFlow.getTaggedValue('directives') +
(occurence.ZeroOrMore('ol', IOrderedList),)
)

flowable.Flow.factories['ul'] = UnorderedList
flowable.IFlow.setTaggedValue(
'directives',
flowable.IFlow.getTaggedValue('directives') +
(occurence.ZeroOrMore('ul', IUnorderedList),)
)
Loading

0 comments on commit 55bf1f9

Please sign in to comment.