Platypus的设计从上倒下分几层:
DocTemplates是最外层的容器
PageTemplates参数规划页面布局
Frame是页面的一个分区,承载flowables
flowables是“流进“文档的段落(Paragraph)、图片(Image)、表格(Table)、空白(Spacer)、分页符(PageBreak)……
pdfgen的画布canvas作为最底层,接收这些高层做出来的东西。
文档模版的结构
那是不是每次用Platypus做版式设计都得这么周全都要这么层次分明呢?
不需要!
实际上我们最经常用得是从reportlab.platypus引入SimpleDocTemplate模版生成文档对象,往里灌各个flowables,然后用文档对象的方法函数build()把它们组合成一个pdf文件。flowable是个基类,衍生出Paragraph、Image、Table等对象。
frame可以直接跟pdfgen的画布canvas联手生成pdf。
flowable对象有自己的方法函数drawOn()画到canvas上,生成pdf文件,比如Paragraph.drawOn(canvas, x, y。,x,y是坐标,指定段落在画布上的放置位置。
我试图找到一个用足四个层级的例子,reportlab的技术文档语焉不详,好不容易在这里:http://code.activestate.com/recipes/123612-basedoctemplate-with-2-pagetemplate/找到一个,稍微改了一下放在这里。
我们常用的SimpleDocTemplate是从基类BaseDocTemplate衍生出来的,这个例子用BaseDocTemplate生成的文档版式包容两种页面版式PageTemplate,一种是我们最常用的单栏(整个页面是一个分区),另一个是双栏(页面分成左右两个分区)。分栏由Frame完成,Frame里容纳Paragraph和Spacer这些flowables。效果如下:
单栏页页脚与双栏页页脚亦不同:
代码如下:
#----- BaseDocTemplate带两个页面模版PageTemplate(单栏和双栏) -----
from reportlab.platypus import BaseDocTemplate, Frame, Paragraph, \
NextPageTemplate, PageBreak, PageTemplate
from reportlab.lib.units import inch
from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
# 注册要用的中文字体
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfbase import pdfmetrics
pdfmetrics.registerFont(TTFont('lively', '/Library/Fonts/Chinese/ChaoZiSheZengYuBoShouShuJian-2.ttf'))
pdfmetrics.registerFont(TTFont('apple', '/Library/Fonts/Chinese/XiaoHuYao-2.ttf'))
# 设置段落格式
styles = getSampleStyleSheet()
# 加入支持中文的段落格式
styles.add(ParagraphStyle("text", fontName='lively', fontSize=18,
leading=30))
# 接受flowables的列表
Elements = []
# 文档对象,showBoundary=1显示边界
doc = BaseDocTemplate('basedoc.pdf', showBoundary=1)
# 单栏页面页脚
def foot1(canvas, document):
canvas.saveState()
canvas.setFont('apple', 12)
canvas.drawString(inch, 0.5 * inch, "单栏第{}页".format(doc.page))
canvas.restoreState()
# 双栏页面页脚
def foot2(canvas, document):
canvas.saveState()
canvas.setFont('apple', 12)
canvas.drawString(inch, 0.5 * inch, "双栏第{}页".format(doc.page))
canvas.restoreState()
# 单栏页面要用的单栏对象
frameT = Frame(doc.leftMargin, doc.bottomMargin, doc.width, doc.height, id='normal')
# 双栏页面要用的双栏对象
frame1 = Frame(doc.leftMargin, doc.bottomMargin, doc.width / 2 - 6, doc.height, id='col1')
frame2 = Frame(doc.leftMargin + doc.width / 2 + 6, doc.bottomMargin, doc.width / 2 - 6,
doc.height, id='col2')
# 将两个页面版式加入到文档对象里,文档对象的方法函数addPageTemplates()的参数是个列表
# 列表里放页面对象,页面对象由类PageTemplate生成
# 生成对象页面对象时frames=接收前面生成的frame对象,onPage=介绍前面定义的的函数名foot1和foot2
# foot1()和foot2()里放不用flowable实现直接往pdfgen.canvas画布上画的部分,比如页脚
doc.addPageTemplates([PageTemplate(id='OneCol', frames=frameT, onPage=foot1),
PageTemplate(id='TwoCol', frames=[frame1, frame2], onPage=foot2),
])
# 框架搭好后往里面灌flowables,flowables先放进列表Elements
# 单栏页面的frame的id定义成'normal',不特别声明会使用normal所在的单栏页面版式
# 灌进一个段落,段落格式用的是前面定义的'text'
Elements.append(Paragraph("西风吹老洞庭波,一夜湘君白发多。醉后不知天在水,满船清梦压星河。" * 30, styles['text']))
# NextPageTemplate()用来切换页面版式,要切换到双栏页面
Elements.append(NextPageTemplate('TwoCol'))
# 必须马上跟上一个分页符,后面的内容才会立刻灌进新版式的页面里
Elements.append(PageBreak())
Elements.append(Paragraph("无可奈何花落去,似曾相识燕归来。小园香径独徘徊。" * 30, styles['text']))
# 切换到单栏页面
Elements.append(NextPageTemplate('OneCol'))
Elements.append(PageBreak())
Elements.append(Paragraph('''沧海笑 滔滔两岸潮 浮沉随浪记今朝
苍天笑 纷纷世上潮 谁负谁胜出天知晓
江山笑 烟雨遥 涛浪淘尽 红尘俗世知多少
清风笑 竟若寂寥 豪情还剩了一襟晚照
苍生笑 不再寂寥 豪情仍在痴痴笑笑''', styles['text']))
# 把Elements里的内容生成pdf文件
doc.build(Elements)