跳转至

表格与 TableStyle

TableLongTable 类继承自 Flowable 类,用于实现简单的文本网格机制。 LongTable 类在计算列宽时使用贪心算法,适用于需要较高处理速度的长表格。 Table 的单元格可以包含任何可以转换为 Python string 的内容,或 Flowables(或 Flowables 的列表)。

目前的表格是在高效绘制与规范化和功能性之间的折衷方案。 我们假设读者对 HTML 表格有一定的了解。简而言之,它们具有以下特点:

  • 可以包含任何可转换为字符串的内容;可流动对象(如其他表格);或完整的子故事

  • 如果您不提供行高,它们可以自动计算行高以适应数据。 (它们也可以自动计算列宽,但通常由设计者手动设置列宽效果更好,而且绘制速度更快。)

  • 如果需要,它们可以跨页分割(参见 canSplit 属性)。 您可以指定在表格顶部和底部重复显示的行数(例如在第 2、3、4 页等再次显示表头)

  • 它们具有简单而强大的着色和网格线指定方式,非常适合财务或数据库表格, 在这些场景中您可能事先不知道行数。您可以轻松地说"让最后一行加粗并在其上方画一条线"

  • 样式与数据分离,因此您可以声明少量表格样式,并将它们用于一系列报表。 样式也可以"继承",就像段落一样。

然而,与 HTML 表格相比有一个主要限制。 它们定义的是一个简单的矩形网格。没有简单的行或列合并功能; 如果您需要合并单元格,必须将表格嵌套在表格单元格内, 或者使用更复杂的方案,即合并区域的起始单元格包含实际内容。

Tables 的创建方式是向构造函数传递可选的列宽序列、可选的行高序列,以及按行排列的数据。 表格的绘制可以通过使用 TableStyle 实例来控制。这允许控制线条(如果有的话)的颜色和粗细, 以及文本的字体、对齐方式和内边距。系统提供了基本的自动行高和/或列宽计算机制。

7.1 Table 用户方法

以下是客户端程序员感兴趣的主要方法。

`Table(data, colWidths=None, rowHeights=None, style=None, splitByRow=1,

repeatRows=0, repeatCols=0, rowSplitRange=None, spaceBefore=None, spaceAfter=None, cornerRadii=None)`

data 参数是一个由单元格值序列组成的序列,每个单元格值应该可以使用 str 函数转换为字符串值, 或者是一个 Flowable 实例(如 Paragraph),或者是此类实例的列表(或元组)。 如果单元格值是 FlowableFlowable 列表,它们必须具有确定的宽度,或者包含该单元格的列必须具有固定宽度。 第一行的单元格值在 data[0] 中,即值按行排列。第 ijth. 个单元格的值在 data[i][j] 中。 单元格值中的换行符 '\n' 被视为换行字符,在绘制时用于将单元格格式化为多行文本。

其他参数的含义比较直观,colWidths 参数是一个数字序列或可能的 None, 表示各列的宽度。colWidths 中的元素数量决定了表格的列数。 值为 None 表示相应列的宽度应自动计算。

rowHeights 参数是一个数字序列或可能的 None,表示各行的高度。 rowHeights 中的元素数量决定了表格的行数。 值为 None 表示相应行的高度应自动计算。

style 参数可以是表格的初始样式。

splitByRow 参数仅当表格同时太高和太宽而无法适应当前上下文时才需要。 在这种情况下,您必须决定是先"平铺"向下再向右,还是先向右再向下。 此参数是一个布尔值,指示当当前绘制区域空间不足且调用者希望 Table 分割时, Table 应先尝试按行分割再尝试按列分割。 目前按列分割 Table 尚未实现,因此将 splitByRow 设置为 False 将导致 NotImplementedError

repeatRows 参数指定当 Table 被要求分割自身时应重复的前导行数或行号元组。 如果是元组,它应指定哪些前导行应被重复;这适用于表格首次出现时比后续分割部分有更多前导行的情况。 repeatCols 参数目前被忽略,因为 Table 不能按列分割。

rowSplitRange 参数可用于将表格的分割控制在其行的子集内;这可以防止在表格开头或结尾附近分割。

spaceBeforespaceAfter 参数可用于在 platypus 故事中渲染表格时在表格前后添加额外空间。

style 参数可以是表格的初始样式。

cornerRadii 参数可以是左上、右上、左下和右下圆角半径的列表。 正的非零半径表示该角应为圆角。此参数将覆盖参数 style 列表中的任何 ROUNDEDCORNERS 命令(即它具有优先权)。

Table.setStyle(tblStyle)

此方法将 TableStyle 类(下面将讨论)的特定实例应用到 Table 实例上。 这是让 tables 以美观格式显示的唯一方法。

连续使用 setStyle 方法会以累加方式应用样式。 也就是说,后应用的样式在重叠处会覆盖先应用的样式。

7.2 TableStyle

此类通过传递一个命令序列来创建,每条命令是一个元组, 由其第一个元素(字符串)标识;命令元组的其余元素表示命令的起始和结束单元格坐标, 以及可能的粗细和颜色等参数。

7.3 TableStyle 用户方法

TableStyle(commandSequence)

创建方法使用参数中的命令序列初始化 TableStyle,示例如下:

    LIST_STYLE = TableStyle(
        [('LINEABOVE', (0,0), (-1,0), 2, colors.green),
        ('LINEABOVE', (0,1), (-1,-1), 0.25, colors.black),
        ('LINEBELOW', (0,-1), (-1,-1), 2, colors.green),
        ('ALIGN', (1,1), (-1,-1), 'RIGHT')]
        )

TableStyle.add(commandSequence)

此方法允许您向现有的 TableStyle 添加命令,即您可以在多条语句中构建 TableStyles

    LIST_STYLE.add('BACKGROUND', (0,0), (-1,0), colors.Color(0,0.7,0.7))

TableStyle.getCommands()

此方法返回实例的命令序列。

    cmds = LIST_STYLE.getCommands()

7.4 TableStyle 命令

传递给 TableStyles 的命令分为三大类,分别影响表格背景、绘制线条或设置单元格样式。

每条命令的第一个元素是其标识符,第二个和第三个参数确定受影响单元格框的坐标, 负坐标从极限值向回计数,类似于 Python 的索引方式。 坐标以 (列, 行) 的形式给出,遵循电子表格的 'A1' 模型, 而不是数学家更习惯的 'RC' 排序。 左上角单元格为 (0, 0),右下角为 (-1, -1)。根据命令的不同, 从索引 3 开始可能有各种额外参数。

TableStyle 单元格格式化命令

所有单元格格式化命令都以标识符开头,后跟起始和结束单元格定义,可能还有其他参数。 单元格格式化命令如下:

FONT                    - 接受字体名称,可选的字号和可选的行距。
FONTNAME (或 FACE)      - 接受字体名称。
FONTSIZE (或 SIZE)      - 接受以磅为单位的字号;行距可能会不同步。
LEADING                 - 接受以磅为单位的行距。
TEXTCOLOR               - 接受颜色名称或 (R,G,B) 元组。
ALIGNMENT (或 ALIGN)    - 接受 LEFT、RIGHT、CENTRE (或 CENTER) 或 DECIMAL 之一。
LEFTPADDING             - 接受整数,默认为 6。
RIGHTPADDING            - 接受整数,默认为 6。
BOTTOMPADDING           - 接受整数,默认为 3。
TOPPADDING              - 接受整数,默认为 3。
BACKGROUND              - 接受由对象、字符串名称或数字元组/列表定义的颜色,
                          或接受描述所需渐变填充的列表/元组,该列表/元组应包含三个元素,
                          格式为 [DIRECTION, startColor, endColor],其中 DIRECTION 为 VERTICAL 或 HORIZONTAL。
ROWBACKGROUNDS          - 接受循环使用的颜色列表。
COLBACKGROUNDS          - 接受循环使用的颜色列表。
VALIGN                  - 接受 TOP、MIDDLE 或默认的 BOTTOM 之一

此命令设置相关单元格的背景颜色。以下示例展示了 BACKGROUNDTEXTCOLOR 命令的使用效果:

data=  [['00', '01', '02', '03', '04'],
        ['10', '11', '12', '13', '14'],
        ['20', '21', '22', '23', '24'],
        ['30', '31', '32', '33', '34']]
t=Table(data)
t.setStyle(TableStyle([('BACKGROUND',(1,1),(-2,-2),colors.green),
                        ('TEXTCOLOR',(0,0),(1,-1),colors.red)]))

Produces:

要查看对齐样式的效果,我们需要一些宽度和网格线,但应该很容易看出样式的来源。

data=  [['00', '01', '02', '03', '04'],
        ['10', '11', '12', '13', '14'],
        ['20', '21', '22', '23', '24'],
        ['30', '31', '32', '33', '34']]
t=Table(data,5*[0.4*inch], 4*[0.4*inch])
t.setStyle(TableStyle([('ALIGN',(1,1),(-2,-2),'RIGHT'),
                        ('TEXTCOLOR',(1,1),(-2,-2),colors.red),
                        ('VALIGN',(0,0),(0,-1),'TOP'),
                        ('TEXTCOLOR',(0,0),(0,-1),colors.blue),
                        ('ALIGN',(0,-1),(-1,-1),'CENTER'),
                        ('VALIGN',(0,-1),(-1,-1),'MIDDLE'),
                        ('TEXTCOLOR',(0,-1),(-1,-1),colors.green),
                        ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
                        ('BOX', (0,0), (-1,-1), 0.25, colors.black),
                        ]))

Produces:

TableStyle 线条命令

线条命令以标识符开头,后跟起始和结束单元格坐标,然后始终跟以所需线条的粗细(以磅为单位)和颜色。
颜色可以是名称,也可以指定为 (R, G, B) 元组,其中 R、G 和 B 为浮点数,(0, 0, 0) 表示黑色。
线条命令名称有:GRID、BOX、OUTLINE、INNERGRID、LINEBELOW、LINEABOVE、LINEBEFORE
和 LINEAFTER。BOX 和 OUTLINE 等效,GRID 等效于同时应用 BOX 和 INNERGRID。

通过以下示例我们可以看到一些线条命令的效果。

data=  [['00', '01', '02', '03', '04'],
        ['10', '11', '12', '13', '14'],
        ['20', '21', '22', '23', '24'],
        ['30', '31', '32', '33', '34']]
t=Table(data,style=[('GRID',(1,1),(-2,-2),1,colors.green),
                    ('BOX',(0,0),(1,-1),2,colors.red),
                    ('LINEABOVE',(1,2),(-2,2),1,colors.blue),
                    ('LINEBEFORE',(2,1),(2,-2),1,colors.pink),
                    ])

Produces:

线条命令在表格分割时会产生问题;以下示例展示了表格在不同位置的分割效果

data=  [['00', '01', '02', '03', '04'],
        ['10', '11', '12', '13', '14'],
        ['20', '21', '22', '23', '24'],
        ['30', '31', '32', '33', '34']]
t=Table(data,style=[
                ('GRID',(0,0),(-1,-1),0.5,colors.grey),
                ('GRID',(1,1),(-2,-2),1,colors.green),
                ('BOX',(0,0),(1,-1),2,colors.red),
                ('BOX',(0,0),(-1,-1),2,colors.black),
                ('LINEABOVE',(1,2),(-2,2),1,colors.blue),
                ('LINEBEFORE',(2,1),(2,-2),1,colors.pink),
                ('BACKGROUND', (0, 0), (0, 1), colors.pink),
                ('BACKGROUND', (1, 1), (1, 2), colors.lavender),
                ('BACKGROUND', (2, 2), (2, 3), colors.orange),
                ])

Produces:

分别为未分割时、在第一行或第二行分割时的效果。

复杂单元格值

如上所述,我们可以使用复杂的单元格值,包括 ParagraphsImages 和其他 Flowables,或它们的列表。 要查看其实际效果,请看以下代码及其生成的表格。 请注意,Image 具有白色背景,这会遮挡您为单元格选择的任何背景颜色。 要获得更好的效果,您应该使用透明背景。

I = Image('../images/replogo.gif')
I.drawHeight = 1.25*inch*I.drawHeight / I.drawWidth
I.drawWidth = 1.25*inch
P0 = Paragraph('''
               <b>A pa<font color=red>r</font>a<i>graph</i></b>
               <super><font color=yellow>1</font></super>''',
               styleSheet["BodyText"])
P = Paragraph('''
       <para align=center spaceb=3>The <b>ReportLab Left
       <font color=red>Logo</font></b>
       Image</para>''',
       styleSheet["BodyText"])
data=  [['A',   'B', 'C',     P0, 'D'],
        ['00', '01', '02', [I,P], '04'],
        ['10', '11', '12', [P,I], '14'],
        ['20', '21', '22',  '23', '24'],
        ['30', '31', '32',  '33', '34']]

t=Table(data,style=[('GRID',(1,1),(-2,-2),1,colors.green),
                    ('BOX',(0,0),(1,-1),2,colors.red),
                    ('LINEABOVE',(1,2),(-2,2),1,colors.blue),
                    ('LINEBEFORE',(2,1),(2,-2),1,colors.pink),
                    ('BACKGROUND', (0, 0), (0, 1), colors.pink),
                    ('BACKGROUND', (1, 1), (1, 2), colors.lavender),
                    ('BACKGROUND', (2, 2), (2, 3), colors.orange),
                    ('BOX',(0,0),(-1,-1),2,colors.black),
                    ('GRID',(0,0),(-1,-1),0.5,colors.black),
                    ('VALIGN',(3,0),(3,0),'BOTTOM'),
                    ('BACKGROUND',(3,0),(3,0),colors.limegreen),
                    ('BACKGROUND',(3,1),(3,1),colors.khaki),
                    ('ALIGN',(3,1),(3,1),'CENTER'),
                    ('BACKGROUND',(3,2),(3,2),colors.beige),
                    ('ALIGN',(3,2),(3,2),'LEFT'),
                    ])

t._argW[3]=1.5*inch

Produces:

TableStyle 合并命令

我们的 Table 类支持合并概念,但其指定方式与 HTML 不同。样式规范为

SPAN, (sc,sr), (ec,er)

表示 scec 列、srer 行的单元格应合并为一个超级单元格, 其内容由单元格 (sc, sr) 决定。其他单元格应存在,但应包含空字符串,否则可能会得到意外结果。

data=  [['Top\nLeft', '', '02', '03', '04'],
        ['', '', '12', '13', '14'],
        ['20', '21', '22', 'Bottom\nRight', ''],
        ['30', '31', '32', '', '']]
t=Table(data,style=[
                ('GRID',(0,0),(-1,-1),0.5,colors.grey),
                ('BACKGROUND',(0,0),(1,1),colors.palegreen),
                ('SPAN',(0,0),(1,1)),
                ('BACKGROUND',(-2,-2),(-1,-1), colors.pink),
                ('SPAN',(-2,-2),(-1,-1)),
                ])

Produces:

请注意,我们不需要对 GRID 命令过于保守。合并后的单元格不会被网格线穿过。

TableStyle 杂项命令

要控制 Table 的分割,可以使用 NOSPLIT 命令。样式规范为

NOSPLIT, (sc,sr), (ec,er)

要求 scec 列、srer 行中的单元格不可被分割。



要控制 Table 的圆角,可以使用 ROUNDEDCORNERS 命令。样式规范为

ROUNDEDCORNERS, [tl, tr, bl, br]

指定左上、右上、左下和右下的半径。值为 0 表示方角。将整个数组替换为 None 可关闭所有圆角。

圆角处的边框将弯曲为一个八分之一圆弧。

特殊的 TableStyle 索引

在任何样式命令中,第一个行索引可以设置为特殊字符串 'splitlast''splitfirst', 表示该样式仅用于分割表格的最后一行或续表的第一行。这允许在分割点附近实现更美观的效果。

编程 Flowables

以下可流动对象允许您在包装时有条件地求值和执行表达式与语句:

8.1 DocAssign(self, var, expr, life='forever')

将名称为 var 的变量赋值为表达式 expr 的值。例如:

DocAssign('i',3)

8.2 DocExec(self, stmt, lifetime='forever')

执行语句 stmt。例如:

DocExec('i-=1')

8.3 DocPara(self, expr, format=None, style=None, klass=None, escape=True)

创建一个段落,其文本为表达式 expr 的值。 如果指定了 format,它应使用 %(expr)s 对表达式 expr 进行字符串插值(如果有的话)。 它也可以使用 %(name)s 插值命名空间中的其他变量。例如:

DocPara('i',format='The value of i is %(__expr__)d',style=normal)

8.4 DocAssert(self, cond, format=None)

如果 cond 的求值结果为 False,则抛出包含 format 字符串的 AssertionError

DocAssert(val, 'val is False')

8.5 DocIf(self, cond, thenBlock, elseBlock=[])

如果 cond 的求值结果为 True,此可流动对象将被替换为 thenBlock,否则替换为 elseBlock

DocIf('i>3',Paragraph('The value of i is larger than 3',normal),\
        Paragraph('The value of i is not larger than 3',normal))

8.6 DocWhile(self, cond, whileBlock)

cond 的求值结果为 True 时,循环运行 whileBlock。例如:

DocAssign('i',5)
DocWhile('i',[DocPara('i',format='The value of i is %(__expr__)d',style=normal),DocExec('i-=1')])

此示例生成一组如下形式的段落:

The value of i is 5
The value of i is 4
The value of i is 3
The value of i is 2
The value of i is 1

其他有用的 Flowables

9.1 Preformatted(text, style, bulletText=None, dedent=0, maxLineLength=None, splitChars=None, newLineChars=None)

创建一个预格式化段落,不进行换行、行分割或其他文本处理。 文本中的 XML 样式标签不会被处理。 如果 dedent 为非零值,dedent 个公共前导空格将从每行开头移除。

定义最大行长度

您可以使用 maxLineLength 属性来定义最大行长度。如果行长度超过此最大值,该行将被自动分割。

行将在 splitChars 中定义的任何单个字符处分割。如果未为此属性提供值, 行将在以下标准字符处分割:空格、冒号、句号、分号、逗号、连字符、正斜杠、反斜杠、左括号、左方括号和左花括号

可以在每个被分割行的开头自动插入字符。您可以将 newLineChars 属性设置为要使用的字符。

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:

9.2 XPreformatted(text, style, bulletText=None, dedent=0, frags=None)

这是 Paragraph 类的非重排形式;text 中允许使用 XML 标签, 其含义与 Paragraph 类中的相同。 与 Preformatted 一样,如果 dedent 为非零值,dedent 个公共前导空格将从每行开头移除。

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;amp; style entities as well for &amp; &lt; &gt; and &quot;.

'''
t=XPreformatted(text,normalStyle,dedent=3)

Produces:

9.3 Image(filename, width=None, height=None)

创建一个可流动对象,其中包含由文件 filename 中数据定义的图像, filename 可以是文件路径、类文件对象或 reportlab.graphics.shapes.Drawing 的实例。 默认的 PDF 图像类型 jpeg 受到支持,如果安装了 PIL 扩展(Python 的), 还可以处理其他图像类型。如果指定了 width 和/或 height, 它们将确定显示图像的尺寸,单位为。 如果任一维度未指定(或指定为 None),则假定图像的相应像素维度以为单位并直接使用。

Image("lj8100.jpg")

将显示为

此处应显示一张图片。

im = Image("lj8100.jpg", width=2*inch, height=2*inch)
im.hAlign = 'CENTER'

生成

此处应显示一张图片。

9.4 Spacer(width, height)

此对象的作用完全符合预期;它在故事中添加一定量的空间。 目前这仅适用于垂直空间。

9.5 PageBreak()

Flowable 表示分页符。它通过有效消耗分配给它的所有垂直空间来工作。 这对于单 Frame 文档是足够的,但对于多帧文档仅相当于帧中断, 因此 BaseDocTemplate 机制在内部检测 pageBreaks 并对其进行特殊处理。

9.6 CondPageBreak(height)

Flowable 在当前 Frame 中剩余垂直空间不足时尝试强制执行 Frame 中断。 因此它的命名可能不太恰当,也许应该重命名为 CondFrameBreak

9.7 KeepTogether(flowables)

此复合 Flowable 接受一个 Flowables 列表,并尝试将它们保持在同一个 Frame 中。 如果列表 flowablesFlowables 的总高度超过当前帧的可用空间, 则会使用所有空间并强制执行帧中断。

9.8 TableOfContents()

可以使用 TableOfContents 可流动对象来生成目录。

以下步骤用于向文档添加目录:

创建 TableOfContents 的实例。覆盖级别样式(可选)并将对象添加到故事中:

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)

目录条目可以通过手动调用 TableOfContents 对象的 addEntry 方法添加, 也可以通过在您使用的 DocTemplateafterFlowable 方法中发送 'TOCEntry' 通知来自动添加。

传递给 notify 的数据是一个包含三到四项的列表,分别为级别编号、条目文本、页码和条目应指向的可选目标键。 此列表通常在文档模板的方法(如 afterFlowable())中创建, 通过使用适当数据调用 notify() 方法发出通知,如下所示:

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))
        # ...

这样,每当样式为 'Heading1''Heading2' 的段落被添加到故事中时,它就会出现在目录中。 Heading2 条目将是可点击的,因为提供了书签键。

最后,您需要使用 DocTemplate 的 multiBuild 方法,因为目录需要多次遍历才能生成:

doc.multiBuild(story)

以下是一个带有目录的文档的简单但完整的示例:

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)

9.9 SimpleIndex()

可以使用 SimpleIndex 可流动对象来生成索引。

以下步骤用于向文档添加索引:

在段落中使用 index 标签来索引术语:

story = []

...

story.append('The third <index item="word" />word of this paragraph is indexed.')

创建 SimpleIndex 的实例,并将其添加到故事中您希望其出现的位置:

index = SimpleIndex(dot=' . ', headers=headers)
story.append(index)

传递给 SimpleIndex 构造函数的参数在 ReportLab 参考文档中有说明。现在,使用 SimpleIndex.getCanvasMaker() 返回的 canvas maker 来构建文档:

doc.build(story, canvasmaker=index.getCanvasMaker())

要构建多级索引,请向 index 标签的 item 属性传递逗号分隔的项列表:

<index item="terma,termb,termc" />
<index item="terma,termd" />

terma 将代表最顶层级别,termc 代表最具体的术语。termd 和 termb 将出现在 terma 内的同一级别中。

如果需要索引包含逗号的术语,需要通过双写逗号来转义。为避免三个连续逗号的歧义(转义的逗号后跟列表分隔符,还是列表分隔符后跟转义的逗号?), 请在正确位置引入空格。术语开头或结尾的空格将被移除。

<index item="comma(,,), ,, ,...   " />

这将索引术语 "comma (,)"、"," 和 "..."。

9.10 ListFlowable(),ListItem()

使用这些类来创建有序和无序列表。列表可以嵌套。

ListFlowable() 将创建一个有序列表,其中可以包含任何可流动对象。 该类有许多参数可以更改字体、颜色、大小、样式和列表编号的位置, 或无序列表中的项目符号位置。编号类型也可以设置为使用小写或大写字母('A,B,C' 等) 或罗马数字(大写或小写),通过 bulletType 属性设置。 要将列表更改为无序类型,请设置 bulletType='bullet'。

ListFlowable() 列表中的项目可以通过将其包装在 ListItem() 类中并设置其属性来更改其默认外观。

以下将创建一个有序列表,并将第三个项目设置为无序子列表。

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'
)

Produces:

为了处理嵌套,start 参数可以设置为可能的起始值列表;对于 ul(无序列表), 可接受的起始值是任何 Unicode 字符或 flowables.py 中已知的特定名称,例如 bulletcharcirclesquarediscdiamonddiamondwxrarrowheadsparklesquarelrsblackstar。 对于 ol(有序列表),start 可以是 '1iaAI' 中的任何字符,表示不同的编号样式。

9.11 BalancedColumns()

使用 BalancedColumns 类创建一个可流动对象,将其内容可流动对象分成两个或更多大致等大的列。 实际上,系统会合成 n 个帧来容纳内容,该可流动对象尝试在它们之间平衡内容。 当总高度过大时,创建的帧将被分割,分割将保持平衡。

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
        )
    )