背景
因我们在工作中需要用到pdf文件, 而对于复杂的定制的pdf文件往往不好处理. 所以本文介绍如何使用Reportlab生成定制pdf文件。
Reportlab是Python的一个标准库, 可以根据图形命令直接创建PDF. 没有干预步骤。应用程序可以非常快速地生成PDF。
基本组件
pdfgen之canvas
pdfgen包是生成PDF文档的最低级别接口。一个pdfgen引擎本质上是一个将文档"绘制"到页面序列上的指令序列。提供绘画操作的接口对象是pdfgen canvas。canvas 也是我们在绘制PDF中最重要的组件。它将从文档页面的左下角为原点, 使用 X/Y 坐标将元素绝对放置在页面中。
模板
-
PageTemplate类是一个语义相当简单的容器类。每个实例都包含一个Frames的列表,并且有一些 方法应该在每个页面的开始和结束时被调用, 可以使用此参数绘制我们的页眉和页脚. 绘制复杂Pdf文件时 我们可以为每页设置单独的页面模板。
-
BaseDocTemplate 创建一个适合创建基本文档的文档模板。它带有相当多的内部机制,但没有默认的页面模板. 所以需要添加定制好的页面模板(PageTemplate)。
实现
from reportlab.graphics import shapes
from reportlab.graphics import widgetbase
from reportlab.graphics.charts.piecharts import Pie
from reportlab.graphics.charts.textlabels import Label
from reportlab.graphics.shapes import Drawing
from reportlab.lib import colors
from reportlab.lib.colors import HexColor
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT
from reportlab.lib.pagesizes import A5
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.units import mm
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.platypus import BaseDocTemplate, Paragraph, Table, Spacer, PageBreak, Image, PageTemplate, \
Frame, NextPageTemplate, FrameBreak
doc = BaseDocTemplate('test.pdf', pagesize=(A5[0], A5[1]), topMargin=15 * mm, bottomMargin=30)
pdfmetrics.registerFont(TTFont('song', r'./STSONG.TTF'))
# 因为reportlab 碰到中文字体会乱码, 所以这里向reportlab注册宋体, 以正常显示中文字体
# 单栏页面对象
frameT = Frame(doc.leftMargin, doc.bottomMargin, doc.width, doc.height, id='normal')
# 双栏页面对象
frame1 = Frame(doc.leftMargin, doc.bottomMargin, doc.width / 2 - 6, doc.height - doc.bottomMargin, id='col1')
frame2 = Frame(doc.leftMargin + doc.width / 2 + 6, doc.bottomMargin, doc.width / 2 - 6,
doc.height - doc.bottomMargin, id='col2', showBoundary=1)
doc.addPageTemplates([PageTemplate(id='OneCol', frames=frameT, onPage=header, onPageEnd=footer),
PageTemplate(id='TwoCol', frames=[frame1, frame2], onPage=header, onPageEnd=foot2),
])
- 添加页眉页脚, 为了区分单双页面, 添加两个页脚
def footer(canvas, doc):
canvas.saveState()
page_num = canvas.getPageNumber()
content = Paragraph(f"单栏第{page_num}页", styleC)
w, h = content.wrap(doc.width, doc.bottomMargin)
content.drawOn(canvas, doc.leftMargin, h)
canvas.restoreState()
def foot2(canvas, doc):
canvas.saveState()
page_num = canvas.getPageNumber()
content = Paragraph(f"双栏第{page_num}页", styleC)
w, h = content.wrap(doc.width, doc.bottomMargin)
content.drawOn(canvas, doc.leftMargin, h)
canvas.restoreState()
def header(canvas, doc):
canvas.saveState()
header_content = Paragraph("这是页眉", styleC)
w, h = header_content.wrap(doc.width, doc.topMargin)
header_content.drawOn(canvas, doc.leftMargin, doc.height + doc.topMargin - h)
canvas.restoreState()
- 单栏页面填充文本信息, 饼图和reportlab的小组件
def draw_pie(data=[], labels=[], use_colors=[]):
pie = Pie()
pie.x = 30 # x,y饼图在框中的坐标
pie.y = 10
pie.slices.label_boxStrokeColor = colors.white # 标签边框的颜色
pie.data = data # 饼图上的数据
pie.labels = labels # 数据的标签
pie.simpleLabels = 0 # 0 标签在标注线的右侧;1 在线上边
pie.sameRadii = 1 # 0 饼图是椭圆;1 饼图是圆形
pie.slices.strokeColor = colors.red # 圆饼的边界颜色
pie.strokeWidth = 1 # 圆饼周围空白区域的宽度
pie.strokeColor = colors.white # 整体饼图边界的颜色
pie.slices.label_pointer_piePad = 5 # 圆饼和标签的距离
pie.slices.label_pointer_edgePad = 15 # 标签和外边框的距离
pie.width = 220
pie.direction = 'clockwise'
pie.pointerLabelMode = 'LeftRight'
for i, col in enumerate(use_colors):
pie.slices[i].fillColor = col
return pie
data = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
labs = ['0000000', '1111111', '2222222', '3333333', '4444444',
'5555555', '6666666', '7777777', '8888888', '9999999']
color = [HexColor("#696969"), HexColor("#A9A9A9"), HexColor("#D8BFD8"),
HexColor("#DCDCDC"), HexColor('#E6E6FA'), HexColor("#B0C4DE"),
HexColor("#778899"), HexColor('#B0C4DE'), HexColor("#6495ED"),
HexColor("#483D8B")
]
def auto_legender(chart, title='饼图'):
width = 300
height = 150
d = Drawing(width, height)
lab = Label()
lab.x = 140 # x和y是文字的位置坐标
lab.y = 130
lab.setText(title)
lab.fontName = 'song' # 增加对中文字体的支持
lab.fontSize = 10
d.add(lab)
d.add(chart)
return d
def add_face():
d = shapes.Drawing(200, 100)
f = widgetbase.Face()
f.skinColor = colors.yellow
d.add(f)
return d
Elements = []
Elements.append(Paragraph("单栏第一页 " * 5, styleN))
pie = auto_legender(draw_pie(data, labs, color))
face = add_face()
Elements.append(pie)
Elements.append(face)
- 双栏填充文本信息, 图片和表格
Elements.append(PageBreak())
Elements.append(Paragraph("这是双栏第一个区域", styleC))
Elements.append(Spacer(width=doc.width, height=30 * mm)) # 添加空行
Elements.append(Image('default.png', width=40 * mm, height=40 * mm)) # 添加图片
Elements.append(FrameBreak()) # 切换Frame
Elements.append(Paragraph("这是双栏第二个区域<br/>", styleC))
Elements.append(Spacer(width=doc.width, height=10 * mm))
# 添加一个表格
data = [
[1, 2, 3, 4, 5],
['a', 'b', 'c', 'd', 'e'],
[1, 2, 3, 4, 5],
['a', 'b', 'c', 'd', 'e'],
]
Elements.append(Table(data, style=[
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('FONT', (0, 0), (-1, -1), 'song'),
('GRID', (0, 0), (-1, -1), 1, colors.black),
]))
总结
Reportlab 支持高度自定义生成Pdf文档, 支持HTML, XML文本, 绘制各种图形, 自定义表格等。