Other Useful Flowables
Preformatted(text, style, bulletText=None, dedent=0, maxLineLength=None, splitChars=None, newLineChars=None)
Creates a preformatted paragraph which does no wrapping, line splitting or other manipulations.
No XML
style tags are taken account of in the text.
If dedent is non zero dedent
common leading
spaces will be removed from the front of each line.
Defining a maximum line length
You can use the property maxLineLength
to define a maximum line length. If a line length exceeds this maximum value, the line will be automatically splitted.
The line will be split on any single character defined in splitChars
. If no value is provided for this property, the line will be split on any of the following standard characters: space, colon, full stop, semi-colon, coma, hyphen, forward slash, back slash, left parenthesis, left square bracket and left curly brace
Characters can be automatically inserted at the beginning of each line that has been created. You can set the property newLineChars
to the characters you want to use.
from reportlab.lib.styles import getSampleStyleSheet
stylesheet=getSampleStyleSheet()
normalStyle = stylesheet['Code']
text='''
class XPreformatted(Paragraph):
def __init__(self, text, style, bulletText = None, frags=None, caseSensitive=1):
self.caseSensitive = caseSensitive
if maximumLineLength and text:
text = self.stopLine(text, maximumLineLength, splitCharacters)
cleaner = lambda text, dedent=dedent: ''.join(_dedenter(text or '',dedent))
self._setup(text, style, bulletText, frags, cleaner)
'''
t=Preformatted(text,normalStyle,maxLineLength=60, newLineChars='> ')
Produces
XPreformatted(text, style, bulletText=None, dedent=0, frags=None)
This is a non rearranging form of the Paragraph
class; XML tags are allowed in
text
and have the same meanings as for the Paragraph
class.
As for Preformatted
, if dedent is non zero dedent
common leading
spaces will be removed from the front of each line.
from reportlab.lib.styles import getSampleStyleSheet
stylesheet=getSampleStyleSheet()
normalStyle = stylesheet['Code']
text='''
This is a non rearranging form of the <b>Paragraph</b> class;
<b><font color=red>XML</font></b> tags are allowed in <i>text</i> and
have the same meanings as for the <b>Paragraph</b> class.
As for <b>Preformatted</b>, if dedent is non zero
<font color="red" size="+1">dedent</font>
common leading spaces will be removed from the
front of each line.
You can have &amp; style entities as well for & < > and ".
'''
t=XPreformatted(text,normalStyle,dedent=3)
Produces
Image(filename, width=None, height=None)
Create a flowable which will contain the image defined by the data in file filename
which can be
filepath, file like object or an instance of a reportlab.graphics.shapes.Drawing
.
The default PDF image type jpeg is supported and if the PIL extension to Python
is installed the other image types can also be handled. If width
and or height
are specified
then they determine the dimension of the displayed image in points. If either dimension is
not specified (or specified as None
) then the corresponding pixel dimension of the image is assumed
to be in points and used.
Image("lj8100.jpg")
will display as
Whereas
im = Image("lj8100.jpg", width=2*inch, height=2*inch)
im.hAlign = 'CENTER'
Produces
Spacer(width, height)
This does exactly as would be expected; it adds a certain amount of space into the story. At present this only works for vertical space.
PageBreak()
This Flowable
represents a page break. It works by effectively consuming all vertical
space given to it. This is sufficient for a single Frame
document, but would only be a
frame break for multiple frames so the BaseDocTemplate
mechanism
detects pageBreaks
internally and handles them specially.
CondPageBreak(height)
This Flowable
attempts to force a Frame
break if insufficient vertical space remains
in the current Frame
. It is thus probably wrongly named and should probably be renamed as
CondFrameBreak
.
KeepTogether(flowables)
This compound Flowable
takes a list of Flowables
and attempts to keep them in the same Frame
.
If the total height of the Flowables
in the list flowables
exceeds the current frame's available
space then all the space is used and a frame break is forced.
TableOfContents()
A table of contents can be generated by using the TableOfContents
flowable.
The following steps are needed to add a table of contents to your document:
Create an instance of TableOfContents
. Override the level styles (optional) and add the object to the story:
toc = TableOfContents()
PS = ParagraphStyle
toc.levelStyles = [
PS(fontName='Times-Bold', fontSize=14, name='TOCHeading1',
leftIndent=20, firstLineIndent=-20, spaceBefore=5, leading=16),
PS(fontSize=12, name='TOCHeading2',
leftIndent=40, firstLineIndent=-20, spaceBefore=0, leading=12),
PS(fontSize=10, name='TOCHeading3',
leftIndent=60, firstLineIndent=-20, spaceBefore=0, leading=12),
PS(fontSize=10, name='TOCHeading4',
leftIndent=100, firstLineIndent=-20, spaceBefore=0, leading=12),
]
story.append(toc)
Entries to the table of contents can be done either manually by calling
the addEntry
method on the TableOfContents
object or automatically by sending
a 'TOCEntry'
notification in the afterFlowable
method of the DocTemplate
you are using.
The data to be passed to notify
is a list of three or four items countaining
a level number, the entry text, the page number and an optional destination key
which the entry should point to.
This list will usually be created in a document template's method
like afterFlowable(), making notification calls using the notify()
method with appropriate data like this:
def afterFlowable(self, flowable):
"""Detect Level 1 and 2 headings, build outline,
and track chapter title."""
if isinstance(flowable, Paragraph):
txt = flowable.getPlainText()
if style == 'Heading1':
# ...
self.notify('TOCEntry', (0, txt, self.page))
elif style == 'Heading2':
# ...
key = 'h2-%s' % self.seq.nextf('heading2')
self.canv.bookmarkPage(key)
self.notify('TOCEntry', (1, txt, self.page, key))
# ...
This way, whenever a paragraph of style 'Heading1'
or 'Heading2'
is added to the story, it will appear in the table of contents.
Heading2
entries will be clickable because a bookmarked key has been supplied.
Finally you need to use the multiBuild
method of the DocTemplate because tables of contents need several passes to be generated:
doc.multiBuild(story)
Below is a simple but working example of a document with a table of contents:
from reportlab.lib.styles import ParagraphStyle as PS
from reportlab.platypus import PageBreak
from reportlab.platypus.paragraph import Paragraph
from reportlab.platypus.doctemplate import PageTemplate, BaseDocTemplate
from reportlab.platypus.tableofcontents import TableOfContents
from reportlab.platypus.frames import Frame
from reportlab.lib.units import cm
class MyDocTemplate(BaseDocTemplate):
def __init__(self, filename, **kw):
self.allowSplitting = 0
BaseDocTemplate.__init__(self, filename, **kw)
template = PageTemplate('normal', [Frame(2.5*cm, 2.5*cm, 15*cm, 25*cm, id='F1')])
self.addPageTemplates(template)
def afterFlowable(self, flowable):
"Registers TOC entries."
if flowable.__class__.__name__ == 'Paragraph':
text = flowable.getPlainText()
style = flowable.style.name
if style == 'Heading1':
self.notify('TOCEntry', (0, text, self.page))
if style == 'Heading2':
self.notify('TOCEntry', (1, text, self.page))
h1 = PS(name = 'Heading1',
fontSize = 14,
leading = 16)
h2 = PS(name = 'Heading2',
fontSize = 12,
leading = 14,
leftIndent = delta)
# Build story.
story = []
toc = TableOfContents()
# For conciseness we use the same styles for headings and TOC entries
toc.levelStyles = [h1, h2]
story.append(toc)
story.append(PageBreak())
story.append(Paragraph('First heading', h1))
story.append(Paragraph('Text in first heading', PS('body')))
story.append(Paragraph('First sub heading', h2))
story.append(Paragraph('Text in first sub heading', PS('body')))
story.append(PageBreak())
story.append(Paragraph('Second sub heading', h2))
story.append(Paragraph('Text in second sub heading', PS('body')))
story.append(Paragraph('Last heading', h1))
doc = MyDocTemplate('mintoc.pdf')
doc.multiBuild(story)
SimpleIndex()
An index can be generated by using the SimpleIndex
flowable.
The following steps are needed to add an index to your document:
Use the index tag in paragraphs to index terms:
story = []
...
story.append('The third <index item="word" />word of this paragraph is indexed.')
Create an instance of SimpleIndex
and add it to the story where you want it to appear:
index = SimpleIndex(dot=' . ', headers=headers)
story.append(index)
The parameters which you can pass into the SimpleIndex constructor are explained in the reportlab reference. Now, build the document by using the canvas maker returned by SimpleIndex.getCanvasMaker():
doc.build(story, canvasmaker=index.getCanvasMaker())
To build an index with multiple levels, pass a comma-separated list of items to the item attribute of an index tag:
<index item="terma,termb,termc" />
<index item="terma,termd" />
terma will respresent the top-most level and termc the most specific term. termd and termb will appear in the same level inside terma.
If you need to index a term containing a comma, you will need to escape it by doubling it. To avoid the ambiguity of three consecutive commas (an escaped comma followed by a list separator or a list separator followed by an escaped comma?) introduce a space in the right position. Spaces at the beginning or end of terms will be removed.
<index item="comma(,,), ,, ,... " />
This indexes the terms "comma (,)", "," and "...".
ListFlowable(),ListItem()
Use these classes to make ordered and unordered lists. Lists can be nested.
ListFlowable()
will create an ordered list, which can contain any flowable. The class has a number of parameters to change font, colour, size, style and position of list numbers, or of bullets in unordered lists. The type of numbering can also be set to use lower or upper case letters ('A,B,C' etc.) or Roman numerals (capitals or lowercase) using the bulletType property. To change the list to an unordered type, set bulletType='bullet'.
Items within a ListFlowable()
list can be changed from their default appearance by wrapping them in a ListItem()
class and setting its properties.
The following will create an ordered list, and set the third item to an unordered sublist.
from reportlab.platypus import ListFlowable, ListItem
from reportlab.lib.styles import getSampleStyleSheet
styles = getSampleStyleSheet()
style = styles["Normal"]
t = ListFlowable(
[
Paragraph("Item no.1", style),
ListItem(Paragraph("Item no. 2", style),bulletColor="green",value=7),
ListFlowable(
[
Paragraph("sublist item 1", style),
ListItem(Paragraph('sublist item 2', style),bulletColor='red',value='square')
],
bulletType='bullet',
start='square',
),
Paragraph("Item no.4", style),
],
bulletType='i'
)
Image required
To cope with nesting the start
parameter can be set to a list of possible starts; for ul
acceptable starts are any unicode character or specific names known to flowables.py eg
bulletchar
, circle
, square
, disc
, diamond
, diamondwx
, rarrowhead
, sparkle
, squarelrs
or blackstar
. For ol
the start
can be any character from '1iaAI'
to indicate different number styles.
BalancedColumns()
Use the BalancedColumns
class to make a flowable that splits its content flowables into two or more roughly equal sized columns.
Effectively n
frames are synthesized to take the content and the flowable tries to balance the content between them. The created frames
will be split when the total height is too large and the split will maintain the balance.
from reportlab.platypus.flowables import BalancedColumns
from reportlab.platypus.frames import ShowBoundaryValue
F = [
list of flowables........
]
story.append(
Balanced(
F, #the flowables we are balancing
nCols = 2, #the number of columns
needed = 72,#the minimum space needed by the flowable
spacBefore = 0,
spaceAfter = 0,
showBoundary = None, #optional boundary showing
leftPadding=None, #these override the created frame
rightPadding=None, #paddings if specified else the
topPadding=None, #default frame paddings
bottomPadding=None, #are used
innerPadding=None, #the gap between frames if specified else
#use max(leftPadding,rightPadding)
name='', #for identification purposes when stuff goes awry
endSlack=0.1, #height disparity allowance ie 10% of available height
)
)