ReportLab 导出 PDF(页面布局)

news2025/5/20 4:14:58

ReportLab 导出 PDF(文档创建)
ReportLab 导出 PDF(页面布局)
ReportLab 导出 PDF(图文表格)

PLATYPUS - 页面布局和排版

  • 1. 设计目标
  • 2. 开始
  • 3. Flowables
    • 3.1. Flowable.draw()
    • 3.2. Flowable.drawOn(canvas,x,y)
    • 3.3. Flowable.wrap(availWidth, availHeight)
    • 3.4. Flowable.split(self, availWidth, availheight)
  • 4. 流动定位的准则
  • 5. Frames
    • 5.1. Frame.addFromList(drawlist, canvas)
    • 5.2. Frame.split(flowable,canv)
    • 5.3. Frame.drawBoundary(canvas)
  • 6. 文档和模板
    • 6.1. BaseDocTemplate.addPageTemplates(self,pageTemplates)
    • 6.2. BaseDocTemplate.build(self, flowables, filename=None,canvasmaker=canvas.Canvas)
    • 6.3. BaseDocTemplate.afterInit(self)
    • 6.4. BaseDocTemplate.afterPage(self)
    • 6.5. BaseDocTemplate.beforeDocument(self)
    • 6.6. BaseDocTemplate.beforePage(self)
    • 6.7. BaseDocTemplate.filterFlowables(self,flowables)
    • 6.8. BaseDocTemplate.afterFlowable(self, flowable)

1. 设计目标

Platypus 是"Page Layout and Typography Using Scripts"的缩写。它是一个高水平的页面布局库, 让你可以用最少的努力以编程方式创建复杂的文档。
Platypus 的设计力求将 "高层次 "的布局决定与文档内容尽可能分开 。例如,段落使用段落样式,页面使用页面模板,目的是让数百个有数千页的文件可以按照不同的样式规格重新格式化,只需在一个包含段落样式和页面布局规格的共享文件中修改几行即可。
Platypus的整体设计可以认为有几个层次,自上而下,这些是:
DocTemplates 作为文档的最外层容器。
PageTemplates 作为各种页面布局的规格。
Frames 页面中可包含流动文本或图形的区域规格。
Flowables 对应"flowed into the document"流入文档的文本或图形元素(即图像、段落和表格等内容,但不包括页脚或固定页面图形等内容)。
pdfgen.Canvas 为最终从其他图层接收文档绘画的最低层。
在这里插入图片描述
上面的插图形象地说明了 DocTemplate、PageTemplate 和 Flowables 的概念 。然而,它具有欺骗性,因为每一个 PageTemplate 实际上可以指定任何数量的页面的格式(而不是像从图中推断的那样只指定一个)。
DocTemplate 包含一个或多个 PageTemplate,每个 PageTemplate 包含一个或多个Frame。
Flowables 是指可以 flowed(流入) Frame的东西,例如 Paragraph 或 Table。
要使用 platypus,你需要从 DocTemplate 类中创建一个文档,并向其 build 方法传递一个 Flowables列表。document 的 build 方法知道如何将 flowable 列表处理成合理的东西。
在内部,DocTemplate 类使用各种事件来实现页面布局和格式化。每个事件都有一个对应的处理方法,称为 handle_XXX ,其中 XXX 是事件名称。一个典型的事件是 frameBegin,它发生在机械开始第一次使用一个框架的时候。
Platypus 故事由一系列基本元素组成,这些元素被称为 Flowables,它们驱动着数据驱动的 Platypus格式化引擎。为了修改引擎的行为,一种特殊的可流式元素 ActionFlowables 告诉布局引擎,例如,跳到下一列或者换成另一个 PageTemplate。

2. 开始

考虑以下代码序列,它为 Platypus 提供了一个非常简单的 "hello world "例子。

from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.rl_config import defaultPageSize
from reportlab.lib.units import inch

PAGE_HEIGHT=defaultPageSize[1]
PAGE_WIDTH=defaultPageSize[0]

styles = getSampleStyleSheet()
Title = "Hello world"
pageinfo = "platypus example"

# 首页
def myFirstPage(canvas, doc):
    canvas.saveState()
    # 标题
    canvas.setFont('Times-Bold',16)
    canvas.drawCentredString(PAGE_WIDTH/2.0, PAGE_HEIGHT-108, Title)
    # 页脚 填充固定字符
    canvas.setFont('Times-Roman',9)
    canvas.drawString(inch, 0.75 * inch, "First Page / %s" % pageinfo)
    canvas.restoreState()

# 非首页
def myLaterPages(canvas, doc):
    canvas.saveState()
    # 页脚 填充页码
    canvas.setFont('Times-Roman',9)
    canvas.drawString(inch, 0.75 * inch, "Page %d %s" % (doc.page, pageinfo))
    canvas.restoreState()

def main(filename: str):
    doc = SimpleDocTemplate(filename)

    # 与标题间隔
    Story = [Spacer(1, 2*inch)]
    style = styles["Normal"]
    for i in range(100):
        bogustext = ("This is Paragraph number %s. " % i) * 5
        p = Paragraph(bogustext, style)
        Story.append(p)
        # 段落之间间隔
        Story.append(Spacer(1, 0.2*inch))
    # 添加
    doc.build(Story, onFirstPage=myFirstPage, onLaterPages=myLaterPages)

if __name__ == '__main__':
    main(filename='example.pdf')

我们创建一个"store"并构建文档。请注意,我们在这里使用的是"canned"(罐头)文档模板,它是预建的页面模板。
我们还使用了预建的段落样式 。
我们在这里只使用了两种类型的"flowables"–Spacers和Paragraphs 。第一个Spacer确保段落跳过标题字符串。

在这里插入图片描述在这里插入图片描述在这里插入图片描述

3. Flowables

Flowables 是可以被绘制的东西,它有wrap, draw和可能的split方法。Flowable 是一个抽象的基类,用于绘制事物,一个实例知道它的大小,并在它自己的坐标系中绘制(这需要基 API 在调用 Flowable.draw方法时提供一个绝对坐标系)。要获得一个实例,使用 f=Flowable()。
注意: Flowable 类是一个抽象类,通常只作为基类使用.
为了说明使用 Flowables 的一般方式,我们将展示如何在画布上使用和绘制衍生类 Paragraph。

def main(filename):
    from reportlab.lib.styles import getSampleStyleSheet
    from reportlab.platypus import Paragraph
    from reportlab.pdfgen.canvas import Canvas
    
    styleSheet = getSampleStyleSheet()
    style = styleSheet['BodyText']
    P=Paragraph('This is a very silly example',style)
    canv = Canvas(filename)
    aW = 460 # available width and height
    aH = 800
    w,h = P.wrap(aW, aH) # find required space
    if w<=aW and h<=aH:
        P.drawOn(canv,0,aH)
        aH = aH - h # reduce the available height
        canv.save()
    else:
        raise ValueError("Not enough room")

if __name__ == '__main__':
    main(filename='example.pdf')

在这里插入图片描述

3.1. Flowable.draw()

这将被调用来要求 flowable 实际渲染自己。Flowable类没有实现draw。调用代码应该确保 flowable 有一个属性canv,它pdfgen.Canvas,它应该被绘制到Canvas上,并且Canvas处于一个适当的状态(就翻译、旋转等而言)。通常这个方法只在内部被drawOn方法调用,派生类必须实现这个方法。派生类必须实现这个方法。

3.2. Flowable.drawOn(canvas,x,y)

这是控制引擎用来将flowable渲染到特定画布的方法。它处理转换为画布坐标(x,y),并确保flowable有一个canv属性,这样draw方法(在基类中没有实现)就可以在一个绝对坐标框架中渲染。

3.3. Flowable.wrap(availWidth, availHeight)

在询问对象的大小、绘制或其他什么之前,这个函数将被包围的框架调用。它返回实际使用的尺寸。

3.4. Flowable.split(self, availWidth, availheight)

当wrap失败时,更复杂的框架会调用这个函数。愚蠢的flowables应该返回[],这意味着它们无法拆分。聪明的flowables应该自己拆分并返回一个flowables列表。客户端代码要确保避免重复尝试拆分。如果空间足够,拆分方法应该返回[self]。否则,flowable应该重新排列,并返回一个按顺序考虑的flowable列表[f0,…]。实现的拆分方法应该避免改变self,因为这将允许复杂的布局机制在一个可流动的列表上进行多次递。

4. 流动定位的准则

有两种方法,默认情况下返回零,为可流动物的垂直间距提供指导。
Flowable.getSpaceAfter(self):
Flowable.getSpaceBefore(self):
这些方法会返回flowable后面或前面应该有多少空间。这些空间不属于flowable本身,也就是说,flowable的draw方法在渲染时不应该考虑它。控制程序将使用返回的值来确定上下文中特定flowable需要多少空间。
所有的flowables都有一个hAlign属性:(‘LEFT’,‘RIGHT’,‘CENTER’或’CENTRE’)。对于占满整个框架宽度的段落,这个属性没有影响。对于小于框架宽度的表格、图像或其他对象,这决定了它们的水平位置。
下面的章节将涵盖最重要的特定类型的可流动文件,段落和表格。

5. Frames

Frames是活动的容器,它本身就包含在PageTemplate中,Frames有一个位置和大小,并保持一个剩余可绘制空间的概念。如:

Frame(x1, y1, width, height, leftPadding=6, bottomPadding=6, rightPadding=6, topPadding=6, id=None, showBoundary=0)

创建一个左下角坐标为(x1,y1)的Frame实例(在使用时相对于画布),尺寸为 width x height。Padding参数是用于减少绘画空间的正量。参数id是运行时使用的标识符,例如"LeftColumn"或"RightColumn"等。如果showBoundary参数是非零,那么框架的边界将在运行时被绘制出来(这有时很有用)。
Frames可以直接与canvases和flowables一起使用来创建文档。Frame.addFromList方法为你处理wrap 和 drawOn调用。你不需要所有的Platypus引擎来获得有用的东西到PDF中。

def main(filename): 
    from reportlab.pdfgen.canvas import Canvas
    from reportlab.lib.styles import getSampleStyleSheet
    from reportlab.lib.units import inch
    from reportlab.platypus import Paragraph, Frame
    
    styles = getSampleStyleSheet()
    styleN = styles['Normal']
    styleH = styles['Heading1']
    story = []
    #add some flowables
    story.append(Paragraph("This is a Heading",styleH))
    story.append(Paragraph("This is a paragraph in <i>Normal</i> style.",styleN))
    c = Canvas(filename)
    f = Frame(inch, inch, 6*inch, 9*inch, showBoundary=1)
    f.addFromList(story,c)
    c.save()

if __name__ == '__main__':
    main(filename='example.pdf')

在这里插入图片描述

5.1. Frame.addFromList(drawlist, canvas)

消耗drawlist前面的Flowables,直到帧满为止。如果不能容纳一个对象,则引发一个异常。

5.2. Frame.split(flowable,canv)

要求flowable使用可用空间进行分割,并返回flowable的列表。

5.3. Frame.drawBoundary(canvas)

将框架边界画成一个矩形(主要用于调试)。

6. 文档和模板

BaseDocTemplate类
实现了文档格式化的基本机制。该类的一个实例包含了一个或多个PageTemplate的列表,这些PageTemplate可用于描述单页信息的布局。build方法可用于处理Flowables列表,以生成一个PDF文档。

from reportlab.lib.pagesizes import A4
from reportlab.platypus import BaseDocTemplate
from reportlab.lib.units import inch

BaseDocTemplate(filename,
                pagesize=A4,
                pageTemplates=[],
                showBoundary=0,		# 控制是否绘制Frame的边界,这对于调试来说是很有用的
                leftMargin=inch,
                rightMargin=inch,
                topMargin=inch,
                bottomMargin=inch,
                allowSplitting=1,	# allowSplitting参数决定了内置方法是否应该尝试split单个Flowables跨越Frame
                title=None,
                author=None,
                _pageBreakQuick=1,	# 参数决定了在结束页面之前,是否应该尝试结束页面上的所有框架
                encrypt=None		# encrypt 参数决定了是否对文档进行加密,以及如何加密
                )

创建一个适合创建基本文档的文档模板。它带有相当多的内部机制,但没有默认的页面模板。所需的filename可以是一个字符串,一个用于接收创建的PDF文档的文件名;也可以是一个有write方法的对象,如 BytesIO 或 file 或 socket。
showBoundary控制是否绘制Frame的边界,这对于调试来说是很有用的。
allowSplitting参数决定了内置方法是否应该尝试split单个Flowables跨越Frame。
_pageBreakQuick参数决定了在结束页面之前,是否应该尝试结束页面上的所有框架。
encrypt 参数决定了是否对文档进行加密,以及如何加密。默认情况下,文档是不加密的。如果encrypt是一个字符串对象,那么它将作为pdf的用户密码。如果encrypt是一个reportlab.lib.pdfencrypt.StandardEncryption的实例,那么这个对象就被用来加密pdf。这允许对加密设置进行更精细的控制。

PageTemplate类
是一个语义相当简单的容器类。每个实例都包含一个Frames的列表,并且有一些方法应该在每个页面的开始和结束时被调用。

PageTemplate(id=None, frames=[], onPage=_doNothing, onPageEnd=_doNothing)

用于初始化一个实例,frames参数应该是一个Frames的列表,而可选的onPageonPageEnd参数是可调用的,它们的签名应该是 def XXX(canvas,document),其中canvas和document是正在绘制的画布和文档。这些例程的目的是用来绘制页面的非流动(即标准)部分。
这些属性函数与纯虚拟方法 PageTemplate.beforPage 和 PageTemplate.afterPage完全平行,这两个方法的签名是 beforPage(self,canvas,document)。这些方法允许使用类派生来定义标准行为,而属性则允许改变实例。在运行时,id 参数用于执行 PageTemplate 的切换,所以 id=‘FirstPage’ 或 id='TwoColumns’是典型的。

6.1. BaseDocTemplate.addPageTemplates(self,pageTemplates)

此方法用于在现有文档中添加一个或一系列PageTemplate。

6.2. BaseDocTemplate.build(self, flowables, filename=None,canvasmaker=canvas.Canvas)

这是应用程序程序员感兴趣的主要方法。假设文档实例被正确设置,build方法将story以flowables
列表的形式接收(flowables参数),并在列表中循环,将flowables列表一次一个地强制通过格式化
机制。实际上,这使得BaseDocTemplate实例发出对实例handle_XXX方法的调用来处理各种事件。

6.3. BaseDocTemplate.afterInit(self)

这个方法在基类初始化后被调用;派生类可以覆盖该方法来添加默认的PageTemplates。

6.4. BaseDocTemplate.afterPage(self)

这是在页面处理后,紧接着当前页面模板的afterDrawPage方法被调用。一个派生类可以使用这个方
法来做一些依赖于页面信息的事情,比如字典页面上的首字和尾字。

6.5. BaseDocTemplate.beforeDocument(self)

在对文档进行任何处理之前,但在处理机制准备好之后,就会调用这个函数,因此它可以用来对实
例的pdfgen.canvas等进行处理。因此,它可以用来对实例的pdfgen.canvas等进行操作。

6.6. BaseDocTemplate.beforePage(self)

这是在页面处理开始时,在当前页面模板的beforeDrawPage方法之前调用的。它可以用来重置页面
特定的信息持有者。

6.7. BaseDocTemplate.filterFlowables(self,flowables)

在主 handle_flowable 方法开始时,调用这个函数来过滤flowables。在返回时,如果flowables[0]
被设置为None,则会被丢弃,主方法立即返回。

6.8. BaseDocTemplate.afterFlowable(self, flowable)

在flowable被渲染后调用。有兴趣的类可以使用这个钩子来收集特定页面或框架上存在的信息。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2337457.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【CVE-2024-10929】ARM CPU漏洞安全通告

安全之安全(security)博客目录导读 目录 一、概述 二、CVE详情 三、受影响产品 四、建议措施 五、致谢 六、版本历史 一、概述 在部分基于Arm架构的CPU中发现了一个潜在安全问题&#xff0c;称为Spectre-BSE&#xff08;Branch Status Eviction&#xff0c;分支状态驱逐…

OpenCV 图形API(33)图像滤波-----高斯模糊函数gaussianBlur()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 使用高斯滤波器对图像进行模糊处理。 该函数使用指定的高斯核对源图像进行滤波。输出图像必须与输入图像具有相同的类型和通道数。 cv::gapi::g…

【2025最新版】火鸟门户v8.5系统源码+PC、H5、小程序 +数据化大屏插件

一.介绍 火鸟地方门户系统V8.5源码 系统包含4端&#xff1a; PCH5小程序APP 二.搭建环境 系统环境&#xff1a;CentOS、 运行环境&#xff1a;宝塔 Linux 网站环境&#xff1a;Nginx 1.2.22 MySQL 5.6 PHP-7.4 常见插件&#xff1a;fileinfo &#xff1b; redis 三.测…

关于 传感器 的详细解析,涵盖定义、分类、工作原理、常见类型、应用领域、技术挑战及未来趋势,结合实例帮助理解其核心概念

以下是关于 传感器 的详细解析&#xff0c;涵盖定义、分类、工作原理、常见类型、应用领域、技术挑战及未来趋势&#xff0c;结合实例帮助理解其核心概念&#xff1a; 一、传感器的定义与核心功能 1. 定义 传感器&#xff08;Sensor&#xff09;是一种能够将物理量&#xff…

EtherCAT转ProfiNet边缘计算网关配置优化:汽车制造场景下PLC与机器人协同作业案例

1.行业背景与需求分析 智能汽车焊装车间是汽车制造的核心工艺环节&#xff0c;某德国豪华品牌在其上海MEB工厂新建的焊装车间中&#xff0c;采用西门子S7-1500PLC作为ProfiNet主站&#xff0c;负责整线协调与质量追溯&#xff1b;同时部署KUKAKR1500Titan机器人&#xff08;Eth…

HTTP协议 --- 超文本传输协议 和 TCP --- 传输控制协议

是基于 TCP 协议的 80 端口的一种 C/S 架构协议。 特点&#xff1a;无状态 --- 数据传输完成后&#xff0c;会断开 TCP 连接&#xff0c;哪怕浏览器还正常运行。 请求报文 --- 方法 响应报文 --- 状态码 是一种面向连接的可靠传输协议 。 面向连接 --- 在传输数据之前&am…

类和对象(下篇)(详解)

【本节目标】 1. 再谈构造函数 2. Static成员 3. 友元 4. 内部类 5. 再次理解封装 1. 再谈构造函数 1.1 构造函数体赋值 在创建对象时&#xff0c;编译器通过调用构造函数&#xff0c;给对象中各个成员变量一个合适的初始值。 #include <iostream> using name…

LeetCode【剑指offer】系列(位运算篇)

剑指offer15.二进制中1的个数 题目链接 题目&#xff1a;编写一个函数&#xff0c;输入是一个无符号整数&#xff08;以二进制串的形式&#xff09;&#xff0c;返回其二进制表达式中数字位数为 ‘1’ 的个数&#xff08;也被称为 汉明重量).&#xff09;。 思路一&#xff…

网络安全领域的AI战略准备:从概念到实践

网络安全领域的AI准备不仅涉及最新工具和技术的应用&#xff0c;更是一项战略必需。许多企业若因目标不明确、数据准备不足或与业务重点脱节而未能有效利用AI技术&#xff0c;可能面临严重后果&#xff0c;包括高级网络威胁数量的激增。 AI准备的核心要素 构建稳健的网络安全…

MacOs下解决远程终端内容复制并到本地粘贴板

常常需要在服务器上捣鼓东西&#xff0c;同时需要将内容复制到本地的需求。 1-内容是在远程终端用vim打开&#xff0c;如何用vim的类似指令达到快速复制到本地呢&#xff1f; 假设待复制的内容&#xff1a; #include <iostream> #include <cstring> using names…

基于PAI+专属网关+私网连接:构建全链路 Deepseek 云上私有化部署与模型调用架构

DeepSeek - R1 是由深度求索公司推出的首款推理模型&#xff0c;该模型在数学、代码和推理任务上的表现优异&#xff0c;市场反馈火爆。在大模型技术商业化进程中&#xff0c;企业级用户普遍面临四大核心挑战&#xff1a; 算力投入成本高昂&#xff1a;构建千亿参数级模型的训…

【cocos creator 3.x】cocos creator2.x项目升级3.x项目改动点

1、基本改动 基本改动&#xff1a;去掉了cc.&#xff0c;改成在顶部添加导入 项目升级时候直接将cc.去掉&#xff0c;根据提示添加引用 node只保留position,scale,rotation,layer 其余属性如opacity&#xff0c;如果需要使用需要在节点手动添加UIOpacity组件 3d层和ui层分开…

List基础与难度题

1. 向 ArrayList 中添加元素并打印 功能描述&#xff1a; 程序创建一个空的 ArrayList 集合&#xff0c;用于存储字符串类型的元素。向该 ArrayList 中依次添加指定的字符串元素。使用增强型 for 循环遍历 ArrayList 中的所有元素&#xff0c;并将每个元素打印输出到控制台。 …

Oracle19C低版本一天遭遇两BUG(ORA-04031/ORA-600)

昨天帮朋友看一个系统异常卡顿的案例&#xff0c;在这里分享给大家 环境&#xff1a;Exadata X8M 数据库版本19.11 1.系统报错信息 表象为系统卡顿&#xff0c;页面无法刷出&#xff0c;登陆到主机上看到节点1 系统等待存在大量的 cursor: pin S wait on X等待 查看两个节…

【4.1.-4.20学习周报】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 摘要Abstract一、方法介绍1.1HippoRAG 1.2HippoRAG2二、实验2.1实验概况2.2实验代码2.3实验结果 总结 摘要 本博客介绍了论文《From RAG to Memory: Non-Parametri…

python学习—合并多个word文档

系列文章目录 python学习—合并TXT文本文件 python学习—统计嵌套文件夹内的文件数量并建立索引表格 python学习—查找指定目录下的指定类型文件 python学习—年会不能停&#xff0c;游戏抽签抽奖 python学习—循环语句-控制流 python学习—合并多个Excel工作簿表格文件 pytho…

[Python] UV工具入门使用指南——小试牛刀

背景 MCP开发使用到了uv&#xff0c;简单记录一下&#xff1a; 为什么MCP更推荐使用uv进行环境管理&#xff1f; MCP 依赖的 Python 环境可能包含多个模块&#xff0c;uv 通过 pyproject.toml 提供更高效的管理方式&#xff0c;并且可以避免 pip 的一些依赖冲突问题。…

PclSharp ——pcl的c#nuget包

简介&#xff1a; NuGet Gallery | PclSharp 1.8.1.20180820-beta07 下载.NET Framework 4.5.2 Developer Pack&#xff1a; 下载 .NET Framework 4.5.2 Developer Pack Offline Installer 离线安装nupkg&#xff1a; nupkg是visual studio 的NuGet Package的一个包文件 安…

MGR实现mysql高可用性

一。MGR和PXC的区别 1. PXC的消息广播机制是在节点间循环的&#xff0c;需要所有节点都确认消息&#xff0c;因此只要有一个节点故障&#xff0c;则会导致整个PXC都发生故障。而MGR则是多数派投票模式&#xff0c;个别少数派节点故障时&#xff0c;一般不影响整体的可用性。这…

新型多机器人协作运输系统,轻松应对复杂路面

受到鱼类、鸟类和蚂蚁等微小生物体协作操纵的启发&#xff0c;研究人员开发了多机器人协作运输系统&#xff08;Multirobot Cooperative Transportation Systems&#xff0c;MRCTS&#xff09;运输单个机器人无法处理的重型超大物体&#xff0c;可用于搜救行动、灾难响应、军事…