mirror of
https://github.com/nottinghamtec/PyRIGS.git
synced 2026-01-17 13:32:15 +00:00
1612 lines
50 KiB
Python
1612 lines
50 KiB
Python
##############################################################################
|
|
#
|
|
# 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.
|
|
#
|
|
##############################################################################
|
|
"""Flowable Element Processing
|
|
"""
|
|
import copy
|
|
import logging
|
|
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, utils
|
|
from xml.sax.saxutils import unescape
|
|
from z3c.rml import attr, directive, interfaces, occurence
|
|
from z3c.rml import form, platypus, special, SampleStyleSheet, stylesheet
|
|
|
|
try:
|
|
import reportlab.graphics.barcode
|
|
except ImportError:
|
|
# barcode package has not been installed
|
|
import types
|
|
import reportlab.graphics
|
|
reportlab.graphics.barcode = types.ModuleType('barcode')
|
|
reportlab.graphics.barcode.createBarcodeDrawing = None
|
|
|
|
# XXX:Copy of reportlab.lib.pygments2xpre.pygments2xpre to fix bug in Python 2.
|
|
def pygments2xpre(s, language="python"):
|
|
"Return markup suitable for XPreformatted"
|
|
try:
|
|
from pygments import highlight
|
|
from pygments.formatters import HtmlFormatter
|
|
except ImportError:
|
|
return s
|
|
|
|
from pygments.lexers import get_lexer_by_name
|
|
|
|
l = get_lexer_by_name(language)
|
|
|
|
h = HtmlFormatter()
|
|
# XXX: Does not work in Python 2, since pygments creates non-unicode
|
|
# outpur snippets.
|
|
#from io import StringIO
|
|
from cStringIO import StringIO
|
|
out = StringIO()
|
|
highlight(s,l,h,out)
|
|
styles = [(cls, style.split(';')[0].split(':')[1].strip())
|
|
for cls, (style, ttype, level) in h.class2style.items()
|
|
if cls and style and style.startswith('color:')]
|
|
from reportlab.lib.pygments2xpre import _2xpre
|
|
return _2xpre(out.getvalue(),styles)
|
|
|
|
|
|
class Flowable(directive.RMLDirective):
|
|
klass=None
|
|
attrMapping = None
|
|
|
|
def process(self):
|
|
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 = attr.Measurement(
|
|
title=u'Width',
|
|
description=u'The width of the spacer. Currently not implemented.',
|
|
default=100,
|
|
required=False)
|
|
|
|
length = attr.Measurement(
|
|
title=u'Length',
|
|
description=u'The height of the spacer.',
|
|
required=True)
|
|
|
|
class Spacer(Flowable):
|
|
signature = ISpacer
|
|
klass = reportlab.platypus.Spacer
|
|
attrMapping = {'length': 'height'}
|
|
|
|
|
|
class IIllustration(interfaces.IRMLDirectiveSignature):
|
|
"""Inserts an illustration with graphics elements."""
|
|
|
|
width = attr.Measurement(
|
|
title=u'Width',
|
|
description=u'The width of the illustration.',
|
|
required=True)
|
|
|
|
height = attr.Measurement(
|
|
title=u'Height',
|
|
description=u'The height of the illustration.',
|
|
default=100,
|
|
required=True)
|
|
|
|
class Illustration(Flowable):
|
|
signature = IIllustration
|
|
klass = platypus.Illustration
|
|
|
|
def process(self):
|
|
args = dict(self.getAttributeValues())
|
|
self.parent.flow.append(self.klass(self, **args))
|
|
|
|
|
|
class IBarCodeFlowable(form.IBarCodeBase):
|
|
"""Creates a bar code as a flowable."""
|
|
|
|
value = attr.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)
|
|
attrMapping = {'code': 'codeName'}
|
|
|
|
class IPluginFlowable(interfaces.IRMLDirectiveSignature):
|
|
"""Inserts a custom flowable developed in Python."""
|
|
|
|
module = attr.String(
|
|
title=u'Module',
|
|
description=u'The Python module in which the flowable is located.',
|
|
required=True)
|
|
|
|
function = attr.String(
|
|
title=u'Function',
|
|
description=(u'The name of the factory function within the module '
|
|
u'that returns the custom flowable.'),
|
|
required=True)
|
|
|
|
params = attr.TextNode(
|
|
title=u'Parameters',
|
|
description=(u'A list of parameters encoded as a long string.'),
|
|
required=False)
|
|
|
|
class PluginFlowable(Flowable):
|
|
signature = IPluginFlowable
|
|
|
|
def process(self):
|
|
modulePath, functionName, text = self.getAttributeValues(
|
|
valuesOnly=True)
|
|
module = __import__(modulePath, {}, {}, [modulePath])
|
|
function = getattr(module, functionName)
|
|
flowables = function(text)
|
|
if not isinstance(flowables, (tuple, list)):
|
|
flowables = [flowables]
|
|
self.parent.flow += list(flowables)
|
|
|
|
|
|
class IMinimalParagraphBase(interfaces.IRMLDirectiveSignature):
|
|
|
|
style = attr.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.'),
|
|
required=False)
|
|
|
|
bulletText = attr.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 = attr.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."""
|
|
|
|
style = attr.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=SampleStyleSheet['Code'],
|
|
required=False)
|
|
|
|
text = attr.RawXMLContent(
|
|
title=u'Text',
|
|
description=(u'The text that will be layed out.'),
|
|
required=True)
|
|
|
|
maxLineLength = attr.Integer(
|
|
title=u'Max Line Length',
|
|
description=(u'The maximum number of characters on one line.'),
|
|
required=False)
|
|
|
|
newLineChars = attr.Text(
|
|
title=u'New Line Characters',
|
|
description=u'The characters placed at the beginning of a wrapped line',
|
|
required=False)
|
|
|
|
class Preformatted(Flowable):
|
|
signature = IPreformatted
|
|
klass = reportlab.platypus.Preformatted
|
|
|
|
class IXPreformatted(IParagraphBase):
|
|
"""A preformatted text that allows paragraph markup."""
|
|
|
|
style = attr.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=SampleStyleSheet['Normal'],
|
|
required=False)
|
|
|
|
text = attr.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 ICodeSnippet(IXPreformatted):
|
|
"""A code snippet with text highlighting."""
|
|
|
|
style = attr.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.'),
|
|
required=False)
|
|
|
|
language = attr.String(
|
|
title=u'Language',
|
|
description=u'The language the code snippet is written in.',
|
|
required=False)
|
|
|
|
class CodeSnippet(XPreformatted):
|
|
signature = ICodeSnippet
|
|
|
|
def process(self):
|
|
args = dict(self.getAttributeValues())
|
|
lang = args.pop('language', None)
|
|
args['text'] = unescape(args['text'])
|
|
if lang is not None:
|
|
args['text'] = pygments2xpre(args['text'], lang.lower())
|
|
if 'style' not in args:
|
|
args['style'] = attr._getStyle(self, 'Code')
|
|
self.parent.flow.append(self.klass(**args))
|
|
|
|
|
|
class IParagraph(IParagraphBase, stylesheet.IBaseParagraphStyle):
|
|
"""Lays out an entire paragraph."""
|
|
|
|
text = attr.XMLContent(
|
|
title=u'Text',
|
|
description=(u'The text that will be layed out.'),
|
|
required=True)
|
|
|
|
class Paragraph(Flowable):
|
|
signature = IParagraph
|
|
klass = reportlab.platypus.Paragraph
|
|
defaultStyle = 'Normal'
|
|
|
|
styleAttributes = zope.schema.getFieldNames(stylesheet.IBaseParagraphStyle)
|
|
|
|
def processStyle(self, style):
|
|
attrs = []
|
|
for attr in self.styleAttributes:
|
|
if self.element.get(attr) is not None:
|
|
attrs.append(attr)
|
|
attrs = self.getAttributeValues(select=attrs)
|
|
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))
|
|
if 'style' not in args:
|
|
args['style'] = attr._getStyle(self, self.defaultStyle)
|
|
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."""
|
|
|
|
class Title(Paragraph):
|
|
signature = ITitle
|
|
defaultStyle = 'Title'
|
|
|
|
class IHeading1(IParagraph):
|
|
"""Heading 1 is a simple paragraph with a special heading 1 style."""
|
|
|
|
class Heading1(Paragraph):
|
|
signature = IHeading1
|
|
defaultStyle = 'Heading1'
|
|
|
|
|
|
class IHeading2(IParagraph):
|
|
"""Heading 2 is a simple paragraph with a special heading 2 style."""
|
|
|
|
class Heading2(Paragraph):
|
|
signature = IHeading2
|
|
defaultStyle = 'Heading2'
|
|
|
|
|
|
class IHeading3(IParagraph):
|
|
"""Heading 3 is a simple paragraph with a special heading 3 style."""
|
|
|
|
class Heading3(Paragraph):
|
|
signature = IHeading3
|
|
defaultStyle = 'Heading3'
|
|
|
|
|
|
class IHeading4(IParagraph):
|
|
"""Heading 4 is a simple paragraph with a special heading 4 style."""
|
|
|
|
class Heading4(Paragraph):
|
|
signature = IHeading4
|
|
defaultStyle = 'Heading4'
|
|
|
|
|
|
class IHeading5(IParagraph):
|
|
"""Heading 5 is a simple paragraph with a special heading 5 style."""
|
|
|
|
class Heading5(Paragraph):
|
|
signature = IHeading5
|
|
defaultStyle = 'Heading5'
|
|
|
|
|
|
class IHeading6(IParagraph):
|
|
"""Heading 6 is a simple paragraph with a special heading 6 style."""
|
|
|
|
class Heading6(Paragraph):
|
|
signature = IHeading6
|
|
defaultStyle = 'Heading6'
|
|
|
|
|
|
class ITableCell(interfaces.IRMLDirectiveSignature):
|
|
"""A table cell within a table."""
|
|
|
|
content = attr.RawXMLContent(
|
|
title=u'Content',
|
|
description=(u'The content of the cell; can be text or any flowable.'),
|
|
required=True)
|
|
|
|
fontName = attr.String(
|
|
title=u'Font Name',
|
|
description=u'The name of the font for the cell.',
|
|
required=False)
|
|
|
|
fontSize = attr.Measurement(
|
|
title=u'Font Size',
|
|
description=u'The font size for the text of the cell.',
|
|
required=False)
|
|
|
|
leading = attr.Measurement(
|
|
title=u'Leading',
|
|
description=(u'The height of a single text line. It includes '
|
|
u'character height.'),
|
|
required=False)
|
|
|
|
fontColor = attr.Color(
|
|
title=u'Font Color',
|
|
description=u'The color in which the text will appear.',
|
|
required=False)
|
|
|
|
leftPadding = attr.Measurement(
|
|
title=u'Left Padding',
|
|
description=u'The size of the padding on the left side.',
|
|
required=False)
|
|
|
|
rightPadding = attr.Measurement(
|
|
title=u'Right Padding',
|
|
description=u'The size of the padding on the right side.',
|
|
required=False)
|
|
|
|
topPadding = attr.Measurement(
|
|
title=u'Top Padding',
|
|
description=u'The size of the padding on the top.',
|
|
required=False)
|
|
|
|
bottomPadding = attr.Measurement(
|
|
title=u'Bottom Padding',
|
|
description=u'The size of the padding on the bottom.',
|
|
required=False)
|
|
|
|
background = attr.Color(
|
|
title=u'Background Color',
|
|
description=u'The color to use as the background for the cell.',
|
|
required=False)
|
|
|
|
align = attr.Choice(
|
|
title=u'Text Alignment',
|
|
description=u'The text alignment within the cell.',
|
|
choices=interfaces.ALIGN_TEXT_CHOICES,
|
|
required=False)
|
|
|
|
vAlign = attr.Choice(
|
|
title=u'Vertical Alignment',
|
|
description=u'The vertical alignment of the text within the cell.',
|
|
choices=interfaces.VALIGN_TEXT_CHOICES,
|
|
required=False)
|
|
|
|
lineBelowThickness = attr.Measurement(
|
|
title=u'Line Below Thickness',
|
|
description=u'The thickness of the line below the cell.',
|
|
required=False)
|
|
|
|
lineBelowColor = attr.Color(
|
|
title=u'Line Below Color',
|
|
description=u'The color of the line below the cell.',
|
|
required=False)
|
|
|
|
lineBelowCap = attr.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 = attr.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 = attr.Measurement(
|
|
title=u'Line Below Space',
|
|
description=u'The space of the line below the cell.',
|
|
required=False)
|
|
|
|
lineAboveThickness = attr.Measurement(
|
|
title=u'Line Above Thickness',
|
|
description=u'The thickness of the line above the cell.',
|
|
required=False)
|
|
|
|
lineAboveColor = attr.Color(
|
|
title=u'Line Above Color',
|
|
description=u'The color of the line above the cell.',
|
|
required=False)
|
|
|
|
lineAboveCap = attr.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 = attr.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 = attr.Measurement(
|
|
title=u'Line Above Space',
|
|
description=u'The space of the line above the cell.',
|
|
required=False)
|
|
|
|
lineLeftThickness = attr.Measurement(
|
|
title=u'Left Line Thickness',
|
|
description=u'The thickness of the line left of the cell.',
|
|
required=False)
|
|
|
|
lineLeftColor = attr.Color(
|
|
title=u'Left Line Color',
|
|
description=u'The color of the line left of the cell.',
|
|
required=False)
|
|
|
|
lineLeftCap = attr.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 = attr.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 = attr.Measurement(
|
|
title=u'Line Left Space',
|
|
description=u'The space of the line left of the cell.',
|
|
required=False)
|
|
|
|
lineRightThickness = attr.Measurement(
|
|
title=u'Right Line Thickness',
|
|
description=u'The thickness of the line right of the cell.',
|
|
required=False)
|
|
|
|
lineRightColor = attr.Color(
|
|
title=u'Right Line Color',
|
|
description=u'The color of the line right of the cell.',
|
|
required=False)
|
|
|
|
lineRightCap = attr.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 = attr.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 = attr.Measurement(
|
|
title=u'Line Right Space',
|
|
description=u'The space of the line right of the cell.',
|
|
required=False)
|
|
|
|
href = attr.Text(
|
|
title=u'Link URL',
|
|
description=u'When specified, the cell becomes a link to that URL.',
|
|
required=False)
|
|
|
|
destination = attr.Text(
|
|
title=u'Link Destination',
|
|
description=(u'When specified, the cell becomes a link to that '
|
|
u'destination.'),
|
|
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')),
|
|
('HREF', ('href',)),
|
|
('DESTINATION', ('destination',)),
|
|
)
|
|
|
|
def processStyle(self):
|
|
row = len(self.parent.parent.rows)
|
|
col = len(self.parent.cols)
|
|
for styleAction, attrNames in self.styleAttributesMapping:
|
|
attrs = []
|
|
for attr in attrNames:
|
|
if self.element.get(attr) is not None:
|
|
attrs.append(attr)
|
|
if not attrs:
|
|
continue
|
|
args = self.getAttributeValues(select=attrs, valuesOnly=True)
|
|
if args:
|
|
self.parent.parent.style.add(
|
|
styleAction, [col, row], [col, row], *args)
|
|
|
|
def process(self):
|
|
# Produce style
|
|
self.processStyle()
|
|
# Produce cell data
|
|
flow = Flow(self.element, self.parent)
|
|
flow.process()
|
|
content = flow.flow
|
|
if len(content) == 0:
|
|
content = self.getAttributeValues(
|
|
select=('content',), valuesOnly=True)[0]
|
|
self.parent.cols.append(content)
|
|
|
|
|
|
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.processSubDirectives()
|
|
self.parent.rows.append(self.cols)
|
|
|
|
|
|
class ITableBulkData(interfaces.IRMLDirectiveSignature):
|
|
"""Bulk Data allows one to quickly create a table."""
|
|
|
|
content = attr.TextNodeSequence(
|
|
title=u'Content',
|
|
description=u'The bulk data.',
|
|
splitre=re.compile('\n'),
|
|
value_type=attr.Sequence(splitre=re.compile(','),
|
|
value_type=attr.Text())
|
|
)
|
|
|
|
class TableBulkData(directive.RMLDirective):
|
|
signature = ITableBulkData
|
|
|
|
def process(self):
|
|
self.parent.rows = self.getAttributeValues(valuesOnly=True)[0]
|
|
|
|
|
|
class BlockTableStyle(stylesheet.BlockTableStyle):
|
|
|
|
def process(self):
|
|
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 IBlockTable(interfaces.IRMLDirectiveSignature):
|
|
"""A typical block table."""
|
|
occurence.containing(
|
|
occurence.ZeroOrMore('tr', ITableRow),
|
|
occurence.ZeroOrOne('bulkData', ITableBulkData),
|
|
occurence.ZeroOrMore('blockTableStyle', stylesheet.IBlockTableStyle),
|
|
)
|
|
|
|
style = attr.Style(
|
|
title=u'Style',
|
|
description=(u'The table style that is applied to the table. '),
|
|
required=False)
|
|
|
|
rowHeights = attr.Sequence(
|
|
title=u'Row Heights',
|
|
description=u'A list of row heights in the table.',
|
|
value_type=attr.Measurement(),
|
|
required=False)
|
|
|
|
colWidths = attr.Sequence(
|
|
title=u'Column Widths',
|
|
description=u'A list of column widths in the table.',
|
|
value_type=attr.Measurement(allowPercentage=True, allowStar=True),
|
|
required=False)
|
|
|
|
repeatRows = attr.Integer(
|
|
title=u'Repeat Rows',
|
|
description=u'A flag to repeat rows upon table splits.',
|
|
required=False)
|
|
|
|
alignment = attr.Choice(
|
|
title=u'Alignment',
|
|
description=u'The alignment of whole table.',
|
|
choices=interfaces.ALIGN_TEXT_CHOICES,
|
|
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
|
|
style = attrs.pop('style', None)
|
|
if style is None:
|
|
self.style = reportlab.platypus.tables.TableStyle()
|
|
else:
|
|
self.style = copy.deepcopy(style)
|
|
hAlign = attrs.pop('alignment', None)
|
|
# Extract all table rows and cells
|
|
self.rows = []
|
|
self.processSubDirectives(None)
|
|
# Create the table
|
|
repeatRows = attrs.pop('repeatRows', None)
|
|
table = self.klass(self.rows, style=self.style, **attrs)
|
|
if repeatRows:
|
|
table.repeatRows = repeatRows
|
|
if hAlign:
|
|
table.hAlign = hAlign
|
|
# 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 = attr.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
|
|
attrMapping = {'name': 'ix'}
|
|
|
|
|
|
class ISetNextFrame(interfaces.IRMLDirectiveSignature):
|
|
"""Define the next frame to switch to."""
|
|
name = attr.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
|
|
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 = attr.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
|
|
attrMapping = {'name': 'pt'}
|
|
|
|
|
|
class IConditionalPageBreak(interfaces.IRMLDirectiveSignature):
|
|
"""Switch to the next page if not enough vertical space is available."""
|
|
height = attr.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
|
|
|
|
|
|
class IKeepInFrame(interfaces.IRMLDirectiveSignature):
|
|
"""Ask a flowable to stay within the frame."""
|
|
|
|
maxWidth = attr.Measurement(
|
|
title=u'Maximum Width',
|
|
description=u'The maximum width the flowables are allotted.',
|
|
default=None,
|
|
required=False)
|
|
|
|
maxHeight = attr.Measurement(
|
|
title=u'Maximum Height',
|
|
description=u'The maximum height the flowables are allotted.',
|
|
default=None,
|
|
required=False)
|
|
|
|
mergeSpace = attr.Boolean(
|
|
title=u'Merge Space',
|
|
description=u'A flag to set whether the space should be merged.',
|
|
required=False)
|
|
|
|
onOverflow = attr.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 = attr.Text(
|
|
title=u'Name/Id',
|
|
description=u'The name/id of the flowable.',
|
|
required=False)
|
|
|
|
frame = attr.StringOrInt(
|
|
title=u'Frame',
|
|
description=u'The frame to which the flowable should be fitted.',
|
|
required=False)
|
|
|
|
class KeepInFrame(Flowable):
|
|
signature = IKeepInFrame
|
|
klass = platypus.KeepInFrame
|
|
attrMapping = {'onOverflow': 'mode', 'id': 'name'}
|
|
|
|
def process(self):
|
|
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 = 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)
|
|
flow.process()
|
|
args['content'] = flow.flow
|
|
# Create the keep in frame container
|
|
frame = self.klass(**args)
|
|
self.parent.flow.append(frame)
|
|
|
|
class IKeepTogether(interfaces.IRMLDirectiveSignature):
|
|
"""Keep the child flowables in the same frame. Add frame break when
|
|
necessary."""
|
|
|
|
maxHeight = attr.Measurement(
|
|
title=u'Maximum Height',
|
|
description=u'The maximum height the flowables are allotted.',
|
|
default=None,
|
|
required=False)
|
|
|
|
class KeepTogether(Flowable):
|
|
signature = IKeepTogether
|
|
klass = reportlab.platypus.flowables.KeepTogether
|
|
|
|
def process(self):
|
|
args = dict(self.getAttributeValues())
|
|
|
|
# Create the content of the container
|
|
flow = Flow(self.element, self.parent)
|
|
flow.process()
|
|
|
|
# Create the keep in frame container
|
|
frame = self.klass(flow.flow, **args)
|
|
self.parent.flow.append(frame)
|
|
|
|
class IImage(interfaces.IRMLDirectiveSignature):
|
|
"""An image."""
|
|
|
|
src = attr.Image(
|
|
title=u'Image Source',
|
|
description=u'The file that is used to extract the image data.',
|
|
onlyOpen=True,
|
|
required=True)
|
|
|
|
width = attr.Measurement(
|
|
title=u'Image Width',
|
|
description=u'The width of the image.',
|
|
required=False)
|
|
|
|
height = attr.Measurement(
|
|
title=u'Image Height',
|
|
description=u'The height the image.',
|
|
required=False)
|
|
|
|
preserveAspectRatio = attr.Boolean(
|
|
title=u'Preserve Aspect Ratio',
|
|
description=(u'If set, the aspect ratio of the image is kept. When '
|
|
u'both, width and height, are specified, the image '
|
|
u'will be fitted into that bounding box.'),
|
|
default=False,
|
|
required=False)
|
|
|
|
mask = attr.Color(
|
|
title=u'Mask',
|
|
description=u'The color mask used to render the image.',
|
|
required=False)
|
|
|
|
align = attr.Choice(
|
|
title=u'Alignment',
|
|
description=u'The alignment of the image within the frame.',
|
|
choices=interfaces.ALIGN_TEXT_CHOICES,
|
|
required=False)
|
|
|
|
vAlign = attr.Choice(
|
|
title=u'Vertical Alignment',
|
|
description=u'The vertical alignment of the image.',
|
|
choices=interfaces.VALIGN_TEXT_CHOICES,
|
|
required=False)
|
|
|
|
class Image(Flowable):
|
|
signature = IImage
|
|
klass = reportlab.platypus.flowables.Image
|
|
attrMapping = {'src': 'filename', 'align': 'hAlign'}
|
|
|
|
def process(self):
|
|
args = dict(self.getAttributeValues(attrMapping=self.attrMapping))
|
|
preserveAspectRatio = args.pop('preserveAspectRatio', False)
|
|
if preserveAspectRatio:
|
|
img = utils.ImageReader(args['filename'])
|
|
args['filename'].seek(0)
|
|
iw, ih = img.getSize()
|
|
if 'width' in args and 'height' not in args:
|
|
args['height'] = args['width'] * ih / iw
|
|
elif 'width' not in args and 'height' in args:
|
|
args['width'] = args['height'] * iw / ih
|
|
elif 'width' in args and 'height' in args:
|
|
# In this case, the width and height specify a bounding box
|
|
# and the size of the image within that box is maximized.
|
|
if args['width'] * ih / iw <= args['height']:
|
|
args['height'] = args['width'] * ih / iw
|
|
elif args['height'] * iw / ih < args['width']:
|
|
args['width'] = args['height'] * iw / ih
|
|
else:
|
|
# This should not happen.
|
|
raise ValueError('Cannot keep image in bounding box.')
|
|
else:
|
|
# No size was specified, so do nothing.
|
|
pass
|
|
|
|
vAlign = args.pop('vAlign', None)
|
|
hAlign = args.pop('hAlign', None)
|
|
img = self.klass(**args)
|
|
if hAlign:
|
|
img.hAlign = hAlign
|
|
if vAlign:
|
|
img.vAlign = vAlign
|
|
self.parent.flow.append(img)
|
|
|
|
|
|
class IImageAndFlowables(interfaces.IRMLDirectiveSignature):
|
|
"""An image with flowables around it."""
|
|
|
|
imageName = attr.Image(
|
|
title=u'Image',
|
|
description=u'The file that is used to extract the image data.',
|
|
onlyOpen=True,
|
|
required=True)
|
|
|
|
imageWidth = attr.Measurement(
|
|
title=u'Image Width',
|
|
description=u'The width of the image.',
|
|
required=False)
|
|
|
|
imageHeight = attr.Measurement(
|
|
title=u'Image Height',
|
|
description=u'The height the image.',
|
|
required=False)
|
|
|
|
imageMask = attr.Color(
|
|
title=u'Mask',
|
|
description=u'The height the image.',
|
|
required=False)
|
|
|
|
imageLeftPadding = attr.Measurement(
|
|
title=u'Image Left Padding',
|
|
description=u'The padding on the left side of the image.',
|
|
required=False)
|
|
|
|
imageRightPadding = attr.Measurement(
|
|
title=u'Image Right Padding',
|
|
description=u'The padding on the right side of the image.',
|
|
required=False)
|
|
|
|
imageTopPadding = attr.Measurement(
|
|
title=u'Image Top Padding',
|
|
description=u'The padding on the top of the image.',
|
|
required=False)
|
|
|
|
imageBottomPadding = attr.Measurement(
|
|
title=u'Image Bottom Padding',
|
|
description=u'The padding on the bottom of the image.',
|
|
required=False)
|
|
|
|
imageSide = attr.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
|
|
attrMapping = {'imageWidth': 'width', 'imageHeight': 'height',
|
|
'imageMask': 'mask', 'imageName': 'filename'}
|
|
|
|
def process(self):
|
|
flow = Flow(self.element, self.parent)
|
|
flow.process()
|
|
# Create the image
|
|
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, **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)
|
|
flow.process()
|
|
# Get the header
|
|
ptoHeader = self.element.find('pto_header')
|
|
header = None
|
|
if ptoHeader is not None:
|
|
header = Flow(ptoHeader, self.parent)
|
|
header.process()
|
|
header = header.flow
|
|
# Get the trailer
|
|
ptoTrailer = self.element.find('pto_trailer')
|
|
trailer = None
|
|
if ptoTrailer is not None:
|
|
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 = attr.Measurement(
|
|
title=u'Left',
|
|
description=u'The indentation to the left.',
|
|
required=False)
|
|
|
|
right = attr.Measurement(
|
|
title=u'Right',
|
|
description=u'The indentation to the right.',
|
|
required=False)
|
|
|
|
class Indent(Flowable):
|
|
signature = IIndent
|
|
|
|
def process(self):
|
|
kw = dict(self.getAttributeValues())
|
|
# Indent
|
|
self.parent.flow.append(reportlab.platypus.doctemplate.Indenter(**kw))
|
|
# Add Content
|
|
flow = Flow(self.element, self.parent)
|
|
flow.process()
|
|
self.parent.flow += flow.flow
|
|
# Dedent
|
|
for name, value in kw.items():
|
|
kw[name] = -value
|
|
self.parent.flow.append(reportlab.platypus.doctemplate.Indenter(**kw))
|
|
|
|
|
|
class IFixedSize(interfaces.IRMLDirectiveSignature):
|
|
"""Create a container flowable of a fixed size."""
|
|
|
|
width = attr.Measurement(
|
|
title=u'Width',
|
|
description=u'The width the flowables are allotted.',
|
|
required=True)
|
|
|
|
height = attr.Measurement(
|
|
title=u'Height',
|
|
description=u'The height the flowables are allotted.',
|
|
required=True)
|
|
|
|
class FixedSize(Flowable):
|
|
signature = IFixedSize
|
|
klass = reportlab.platypus.flowables.KeepInFrame
|
|
attrMapping = {'width': 'maxWidth', 'height': 'maxHeight'}
|
|
|
|
def process(self):
|
|
flow = Flow(self.element, self.parent)
|
|
flow.process()
|
|
args = dict(self.getAttributeValues(attrMapping=self.attrMapping))
|
|
frame = self.klass(content=flow.flow, mode='shrink', **args)
|
|
self.parent.flow.append(frame)
|
|
|
|
|
|
class IBookmarkPage(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 = attr.Text(
|
|
title=u'Name',
|
|
description=u'The name of the bookmark.',
|
|
required=True)
|
|
|
|
fit = attr.Choice(
|
|
title=u'Fit',
|
|
description=u'The Fit Type.',
|
|
choices=('XYZ', 'Fit', 'FitH', 'FitV', 'FitR'),
|
|
required=False)
|
|
|
|
top = attr.Measurement(
|
|
title=u'Top',
|
|
description=u'The top position.',
|
|
required=False)
|
|
|
|
bottom = attr.Measurement(
|
|
title=u'Bottom',
|
|
description=u'The bottom position.',
|
|
required=False)
|
|
|
|
left = attr.Measurement(
|
|
title=u'Left',
|
|
description=u'The left position.',
|
|
required=False)
|
|
|
|
right = attr.Measurement(
|
|
title=u'Right',
|
|
description=u'The right position.',
|
|
required=False)
|
|
|
|
zoom = attr.Float(
|
|
title=u'Zoom',
|
|
description=u'The zoom level when clicking on the bookmark.',
|
|
required=False)
|
|
|
|
class BookmarkPage(Flowable):
|
|
signature = IBookmarkPage
|
|
klass = platypus.BookmarkPage
|
|
attrMapping = {'name': 'key', 'fitType': 'fit'}
|
|
|
|
|
|
class IBookmark(interfaces.IRMLDirectiveSignature):
|
|
"""
|
|
This creates a bookmark to the current page which can be referred to with
|
|
the given key elsewhere. (Used inside a story.)
|
|
"""
|
|
|
|
name = attr.Text(
|
|
title=u'Name',
|
|
description=u'The name of the bookmark.',
|
|
required=True)
|
|
|
|
x = attr.Measurement(
|
|
title=u'X Coordinate',
|
|
description=u'The x-position of the bookmark.',
|
|
default=0,
|
|
required=False)
|
|
|
|
y = attr.Measurement(
|
|
title=u'Y Coordinate',
|
|
description=u'The y-position of the bookmark.',
|
|
default=0,
|
|
required=False)
|
|
|
|
class Bookmark(Flowable):
|
|
signature = IBookmark
|
|
klass = platypus.Bookmark
|
|
attrMapping = {'name': 'key', 'x': 'relativeX', 'y': 'relativeY'}
|
|
|
|
|
|
class ILink(interfaces.IRMLDirectiveSignature):
|
|
"""Place an internal link around a set of flowables."""
|
|
|
|
destination = attr.Text(
|
|
title=u'Destination',
|
|
description=u'The name of the destination to link to.',
|
|
required=False)
|
|
|
|
url = attr.Text(
|
|
title=u'URL',
|
|
description=u'The URL to link to.',
|
|
required=False)
|
|
|
|
boxStrokeWidth = attr.Measurement(
|
|
title=u'Box Stroke Width',
|
|
description=u'The width of the box border line.',
|
|
required=False)
|
|
|
|
boxStrokeDashArray = attr.Sequence(
|
|
title=u'Box Stroke Dash Array',
|
|
description=u'The dash array of the box border line.',
|
|
value_type=attr.Float(),
|
|
required=False)
|
|
|
|
boxStrokeColor = attr.Color(
|
|
title=u'Box Stroke Color',
|
|
description=(u'The color in which the box border is drawn.'),
|
|
required=False)
|
|
|
|
|
|
class Link(Flowable):
|
|
signature = ILink
|
|
attrMapping = {'destination': 'destinationname',
|
|
'boxStrokeWidth': 'thickness',
|
|
'boxStrokeDashArray': 'dashArray',
|
|
'boxStrokeColor': 'color'}
|
|
|
|
def process(self):
|
|
flow = Flow(self.element, self.parent)
|
|
flow.process()
|
|
args = dict(self.getAttributeValues(attrMapping=self.attrMapping))
|
|
self.parent.flow.append(platypus.Link(flow.flow, **args))
|
|
|
|
|
|
class IHorizontalRow(interfaces.IRMLDirectiveSignature):
|
|
"""Create a horizontal line on the page."""
|
|
|
|
width = attr.Measurement(
|
|
title=u'Width',
|
|
description=u'The width of the line on the page.',
|
|
allowPercentage=True,
|
|
required=False)
|
|
|
|
thickness = attr.Measurement(
|
|
title=u'Thickness',
|
|
description=u'Line Thickness',
|
|
required=False)
|
|
|
|
color = attr.Color(
|
|
title=u'Color',
|
|
description=u'The color of the line.',
|
|
required=False)
|
|
|
|
lineCap = attr.Choice(
|
|
title=u'Cap',
|
|
description=u'The cap at the end of the line.',
|
|
choices=interfaces.CAP_CHOICES.keys(),
|
|
required=False)
|
|
|
|
spaceBefore = attr.Measurement(
|
|
title=u'Space Before',
|
|
description=u'The vertical space before the line.',
|
|
required=False)
|
|
|
|
spaceAfter = attr.Measurement(
|
|
title=u'Space After',
|
|
description=u'The vertical space after the line.',
|
|
required=False)
|
|
|
|
align = attr.Choice(
|
|
title=u'Alignment',
|
|
description=u'The alignment of the line within the frame.',
|
|
choices=interfaces.ALIGN_TEXT_CHOICES,
|
|
required=False)
|
|
|
|
valign = attr.Choice(
|
|
title=u'Vertical Alignment',
|
|
description=u'The vertical alignment of the line.',
|
|
choices=interfaces.VALIGN_TEXT_CHOICES,
|
|
required=False)
|
|
|
|
dash = attr.Sequence(
|
|
title=u'Dash-Pattern',
|
|
description=u'The dash-pattern of a line.',
|
|
value_type=attr.Measurement(),
|
|
default=None,
|
|
required=False)
|
|
|
|
class HorizontalRow(Flowable):
|
|
signature = IHorizontalRow
|
|
klass = reportlab.platypus.flowables.HRFlowable
|
|
attrMapping = {'align': 'hAlign'}
|
|
|
|
|
|
class IOutlineAdd(interfaces.IRMLDirectiveSignature):
|
|
"""Add a new entry to the outline of the PDF."""
|
|
|
|
title = attr.TextNode(
|
|
title=u'Title',
|
|
description=u'The text displayed for this item.',
|
|
required=True)
|
|
|
|
key = attr.String(
|
|
title=u'Key',
|
|
description=u'The unique key of the item.',
|
|
required=False)
|
|
|
|
level = attr.Integer(
|
|
title=u'Level',
|
|
description=u'The level in the outline tree.',
|
|
required=False)
|
|
|
|
closed = attr.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
|
|
|
|
|
|
class NamedStringFlowable(reportlab.platypus.flowables.Flowable,
|
|
special.TextFlowables):
|
|
|
|
def __init__(self, manager, id, value):
|
|
reportlab.platypus.flowables.Flowable.__init__(self)
|
|
self.manager = manager
|
|
self.id = id
|
|
self._value = value
|
|
self.value = u''
|
|
|
|
def wrap(self, *args):
|
|
return (0, 0)
|
|
|
|
def draw(self):
|
|
text = self._getText(self._value, self.manager.canvas,
|
|
include_final_tail=False)
|
|
self.manager.names[self.id] = text
|
|
|
|
|
|
class INamedString(interfaces.IRMLDirectiveSignature):
|
|
"""Defines a name for a string."""
|
|
|
|
id = attr.String(
|
|
title=u'Id',
|
|
description=u'The id under which the value will be known.',
|
|
required=True)
|
|
|
|
value = attr.XMLContent(
|
|
title=u'Value',
|
|
description=u'The text that is displayed if the id is called.',
|
|
required=True)
|
|
|
|
class NamedString(directive.RMLDirective):
|
|
signature = INamedString
|
|
|
|
def process(self):
|
|
id, value = self.getAttributeValues(valuesOnly=True)
|
|
manager = attr.getManager(self)
|
|
# We have to delay assigning values, otherwise the last one wins.
|
|
self.parent.flow.append(NamedStringFlowable(manager, id, self.element))
|
|
|
|
|
|
class IShowIndex(interfaces.IRMLDirectiveSignature):
|
|
"""Creates an index in the document."""
|
|
|
|
name = attr.String(
|
|
title=u'Name',
|
|
description=u'The name of the index.',
|
|
default='index',
|
|
required=False)
|
|
|
|
dot = attr.String(
|
|
title=u'Dot',
|
|
description=u'The character to use as a dot.',
|
|
required=False)
|
|
|
|
style = attr.Style(
|
|
title=u'Style',
|
|
description=u'The paragraph style that is applied to the index. ',
|
|
required=False)
|
|
|
|
tableStyle = attr.Style(
|
|
title=u'Table Style',
|
|
description=u'The table style that is applied to the index layout. ',
|
|
required=False)
|
|
|
|
class ShowIndex(directive.RMLDirective):
|
|
signature = IShowIndex
|
|
|
|
def process(self):
|
|
args = dict(self.getAttributeValues())
|
|
manager = attr.getManager(self)
|
|
index = manager.indexes[args['name']]
|
|
args['format'] = index.formatFunc.__name__[8:]
|
|
args['offset'] = index.offset
|
|
index.setup(**args)
|
|
self.parent.flow.append(index)
|
|
|
|
|
|
class IBaseLogCall(interfaces.IRMLDirectiveSignature):
|
|
|
|
message = attr.RawXMLContent(
|
|
title=u'Message',
|
|
description=u'The message to be logged.',
|
|
required=True)
|
|
|
|
class LogCallFlowable(reportlab.platypus.flowables.Flowable):
|
|
|
|
def __init__(self, logger, level, message):
|
|
self.logger = logger
|
|
self.level = level
|
|
self.message = message
|
|
|
|
def wrap(self, *args):
|
|
return (0, 0)
|
|
|
|
def draw(self):
|
|
self.logger.log(self.level, self.message)
|
|
|
|
class BaseLogCall(directive.RMLDirective):
|
|
signature = IBaseLogCall
|
|
level = None
|
|
|
|
def process(self):
|
|
message = self.getAttributeValues(
|
|
select=('message',), valuesOnly=True)[0]
|
|
manager = attr.getManager(self)
|
|
self.parent.flow.append(
|
|
LogCallFlowable(manager.logger, self.level, message))
|
|
|
|
class ILog(IBaseLogCall):
|
|
"""Log message at DEBUG level."""
|
|
|
|
level = attr.Choice(
|
|
title=u'Level',
|
|
description=u'The default log level.',
|
|
choices=interfaces.LOG_LEVELS,
|
|
doLower=False,
|
|
default=logging.INFO,
|
|
required=True)
|
|
|
|
class Log(BaseLogCall):
|
|
signature = ILog
|
|
|
|
@property
|
|
def level(self):
|
|
return self.getAttributeValues(select=('level',), valuesOnly=True)[0]
|
|
|
|
class IDebug(IBaseLogCall):
|
|
"""Log message at DEBUG level."""
|
|
|
|
class Debug(BaseLogCall):
|
|
signature = IDebug
|
|
level = logging.DEBUG
|
|
|
|
|
|
class IInfo(IBaseLogCall):
|
|
"""Log message at INFO level."""
|
|
|
|
class Info(BaseLogCall):
|
|
signature = IInfo
|
|
level = logging.INFO
|
|
|
|
|
|
class IWarning(IBaseLogCall):
|
|
"""Log message at WARNING level."""
|
|
|
|
class Warning(BaseLogCall):
|
|
signature = IWarning
|
|
level = logging.WARNING
|
|
|
|
|
|
class IError(IBaseLogCall):
|
|
"""Log message at ERROR level."""
|
|
|
|
class Error(BaseLogCall):
|
|
signature = IError
|
|
level = logging.ERROR
|
|
|
|
|
|
class ICritical(IBaseLogCall):
|
|
"""Log message at CRITICAL level."""
|
|
|
|
class Critical(BaseLogCall):
|
|
signature = ICritical
|
|
level = logging.CRITICAL
|
|
|
|
|
|
class IFlow(interfaces.IRMLDirectiveSignature):
|
|
"""A list of flowables."""
|
|
occurence.containing(
|
|
occurence.ZeroOrMore('spacer', ISpacer),
|
|
occurence.ZeroOrMore('illustration', IIllustration),
|
|
occurence.ZeroOrMore('pre', IPreformatted),
|
|
occurence.ZeroOrMore('xpre', IXPreformatted),
|
|
occurence.ZeroOrMore('codesnippet', ICodeSnippet),
|
|
occurence.ZeroOrMore('plugInFlowable', IPluginFlowable),
|
|
occurence.ZeroOrMore('barCodeFlowable', IBarCodeFlowable),
|
|
occurence.ZeroOrMore('outlineAdd', IOutlineAdd),
|
|
occurence.ZeroOrMore('title', ITitle),
|
|
occurence.ZeroOrMore('h1', IHeading1),
|
|
occurence.ZeroOrMore('h2', IHeading2),
|
|
occurence.ZeroOrMore('h3', IHeading3),
|
|
occurence.ZeroOrMore('h4', IHeading4),
|
|
occurence.ZeroOrMore('h5', IHeading5),
|
|
occurence.ZeroOrMore('h6', IHeading6),
|
|
occurence.ZeroOrMore('para', IParagraph),
|
|
occurence.ZeroOrMore('blockTable', IBlockTable),
|
|
occurence.ZeroOrMore('nextFrame', INextFrame),
|
|
occurence.ZeroOrMore('setNextFrame', ISetNextFrame),
|
|
occurence.ZeroOrMore('nextPage', INextPage),
|
|
occurence.ZeroOrMore('setNextTemplate', ISetNextTemplate),
|
|
occurence.ZeroOrMore('condPageBreak', IConditionalPageBreak),
|
|
occurence.ZeroOrMore('keepInFrame', IKeepInFrame),
|
|
occurence.ZeroOrMore('keepTogether', IKeepTogether),
|
|
occurence.ZeroOrMore('img', IImage),
|
|
occurence.ZeroOrMore('imageAndFlowables', IImageAndFlowables),
|
|
occurence.ZeroOrMore('pto', IPTO),
|
|
occurence.ZeroOrMore('indent', IIndent),
|
|
occurence.ZeroOrMore('fixedSize', IFixedSize),
|
|
occurence.ZeroOrMore('bookmarkPage', IBookmarkPage),
|
|
occurence.ZeroOrMore('bookmark', IBookmark),
|
|
occurence.ZeroOrMore('link', ILink),
|
|
occurence.ZeroOrMore('hr', IHorizontalRow),
|
|
occurence.ZeroOrMore('showIndex', IShowIndex),
|
|
occurence.ZeroOrMore('name', special.IName),
|
|
occurence.ZeroOrMore('namedString', INamedString),
|
|
occurence.ZeroOrMore('log', ILog),
|
|
occurence.ZeroOrMore('debug', IDebug),
|
|
occurence.ZeroOrMore('info', IInfo),
|
|
occurence.ZeroOrMore('warning', IWarning),
|
|
occurence.ZeroOrMore('error', IError),
|
|
occurence.ZeroOrMore('critical', ICritical),
|
|
)
|
|
|
|
class Flow(directive.RMLDirective):
|
|
|
|
factories = {
|
|
# Generic Flowables
|
|
'spacer': Spacer,
|
|
'illustration': Illustration,
|
|
'pre': Preformatted,
|
|
'xpre': XPreformatted,
|
|
'codesnippet': CodeSnippet,
|
|
'plugInFlowable': PluginFlowable,
|
|
'barCodeFlowable': BarCodeFlowable,
|
|
'outlineAdd': OutlineAdd,
|
|
# Paragraph-Like Flowables
|
|
'title': Title,
|
|
'h1': Heading1,
|
|
'h2': Heading2,
|
|
'h3': Heading3,
|
|
'h4': Heading4,
|
|
'h5': Heading5,
|
|
'h6': Heading6,
|
|
'para': Paragraph,
|
|
# Table Flowable
|
|
'blockTable': BlockTable,
|
|
# Page-level Flowables
|
|
'nextFrame': NextFrame,
|
|
'setNextFrame': SetNextFrame,
|
|
'nextPage': NextPage,
|
|
'setNextTemplate': SetNextTemplate,
|
|
'condPageBreak': ConditionalPageBreak,
|
|
'keepInFrame': KeepInFrame,
|
|
'keepTogether': KeepTogether,
|
|
'img': Image,
|
|
'imageAndFlowables': ImageAndFlowables,
|
|
'pto': PTO,
|
|
'indent': Indent,
|
|
'fixedSize': FixedSize,
|
|
'bookmarkPage': BookmarkPage,
|
|
'bookmark': Bookmark,
|
|
'link': Link,
|
|
'hr': HorizontalRow,
|
|
'showIndex': ShowIndex,
|
|
# Special Elements
|
|
'name': special.Name,
|
|
'namedString': NamedString,
|
|
# Logging
|
|
'log': Log,
|
|
'debug': Debug,
|
|
'info': Info,
|
|
'warning': Warning,
|
|
'error': Error,
|
|
'critical': Critical,
|
|
}
|
|
|
|
def __init__(self, *args, **kw):
|
|
super(Flow, self).__init__(*args, **kw)
|
|
self.flow = []
|
|
|
|
def process(self):
|
|
self.processSubDirectives()
|
|
return self.flow
|