写点什么

使用 ReportLab 绘制 PDF

  • 2022 年 9 月 28 日
    北京
  • 本文字数:3105 字

    阅读完需:约 10 分钟

背景

因我们在工作中需要用到 pdf 文件, 而对于复杂的定制的 pdf 文件往往不好处理. 所以本文介绍如何使用 Reportlab 生成定制 pdf 文件。

Reportlab 是 Python 的一个标准库, 可以根据图形命令直接创建 PDF. 没有干预步骤。应用程序可以非常快速地生成 PDF。

基本组件

pdfgen 之 canvas

pdfgen 包是生成 PDF 文档的最低级别接口。一个 pdfgen 引擎本质上是一个将文档"绘制"到页面序列上的指令序列。提供绘画操作的接口对象是 pdfgen canvas。canvas 也是我们在绘制 PDF 中最重要的组件。它将从文档页面的左下角为原点, 使用 X/Y 坐标将元素绝对放置在页面中。

模板

  1. PageTemplate 类是一个语义相当简单的容器类。每个实例都包含一个 Frames 的列表,并且有一些 方法应该在每个页面的开始和结束时被调用, 可以使用此参数绘制我们的页眉和页脚. 绘制复杂 Pdf 文件时 我们可以为每页设置单独的页面模板。

  2. BaseDocTemplate 创建一个适合创建基本文档的文档模板。它带有相当多的内部机制,但没有默认的页面模板. 所以需要添加定制好的页面模板(PageTemplate)。

实现

from reportlab.graphics import shapesfrom reportlab.graphics import widgetbasefrom reportlab.graphics.charts.piecharts import Piefrom reportlab.graphics.charts.textlabels import Labelfrom reportlab.graphics.shapes import Drawingfrom reportlab.lib import colorsfrom reportlab.lib.colors import HexColorfrom reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHTfrom reportlab.lib.pagesizes import A5from reportlab.lib.styles import getSampleStyleSheetfrom reportlab.lib.units import mmfrom reportlab.pdfbase import pdfmetricsfrom reportlab.pdfbase.ttfonts import TTFontfrom 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()
复制代码
  1. 单栏页面填充文本信息, 饼图和 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 文本, 绘制各种图形, 自定义表格等。

更多学习资料戳下方!!!

https://qrcode.ceba.ceshiren.com/link?name=article&project_id=qrcode&from=infoQ&timestamp=1662366626&author=xueqi

用户头像

社区:ceshiren.com 2022.08.29 加入

微信公众号:霍格沃兹测试开发 提供性能测试、自动化测试、测试开发等资料、实事更新一线互联网大厂测试岗位内推需求,共享测试行业动态及资讯,更可零距离接触众多业内大佬

评论

发布
暂无评论
使用 ReportLab 绘制 PDF_测试_测吧(北京)科技有限公司_InfoQ写作社区