Python 实用技巧:从零自主生成结构化 PDF 页面

自动开具的发票、定时导出的数据报告、批量打印的证书与票据------在许多业务场景中,内容往往由程序动态拼装,且对版面布局有着严苛的定位要求。本文聚焦从零生成 PDF 文档这一核心能力,带你深入探索如何使用 Spire.PDF for Python 创建空白画布、精准绘制文本与基础图形、处理长文本自动分页以及灵活插入图片,将后端数据稳稳落地到排版可控、专业规范的 PDF 页面中。

为什么选择从零生成

与"转换"类操作相比,从零生成更贴近"程序自己排版"的需求:

  • 内容由数据驱动:发票金额、报表条目、证书姓名都来自数据库或接口,无法预先做成模板文件再转换。
  • 版面需要精确控制:标题放哪里、图片占多大、分隔线画在何处,都要由坐标决定。
  • 适合批量与定时:结合循环与文件写入,可以一次产出成百上千份风格统一的 PDF。
  • 不依赖中间格式:省去"先建 Word/Excel 再导出"的环节,流程更短、依赖更少。

理解这一点后,你会发现从零生成其实是一套"建文档 → 加页面 → 在画布上绘制"的线性流程,掌握几个核心对象就能应对大多数场景。

环境准备

本文使用 Spire.PDF for Python 来生成 PDF。它把每一页看作一块画布(Canvas),你可以在上面按坐标绘制文字、图片和图形。通过以下命令安装:

bash 复制代码
pip install Spire.PDF

安装完成后,同时导入 spire.pdf 与 spire.pdf.common 两个模块即可开始使用。

创建文档并添加页面

所有生成操作的起点都是 PdfDocument 对象,它代表一份完整的 PDF 文档。调用 Pages.Add() 会返回一个新的页面对象,而绘制则发生在该页面的 Canvas(画布)上。下面是最简骨架:新建文档、加一页、在坐标 (10, 10) 处写一行文字,最后保存并关闭。

python 复制代码
from spire.pdf.common import *
from spire.pdf import *

# 创建一份空白 PDF 文档
doc = PdfDocument()
# 新增一页,拿到该页的画布
page = doc.Pages.Add()

# 定义字体、颜色与画刷
font = PdfFont(PdfFontFamily.Helvetica, 30.0)
color = PdfRGBColor(Color.get_Black())
textBrush = PdfSolidBrush(color)

# 在坐标 (10, 10) 处绘制文本
page.Canvas.DrawString("Hello, World", font, textBrush, 10.0, 10.0)

# 保存并关闭文档
doc.SaveToFile("HelloWorld.pdf")
doc.Close()

这里的 PdfFont 接受字体族与字号两个基本参数;PdfRGBColor 配合 PdfSolidBrush 决定文字颜色;DrawString 的最后两个浮点数就是文字左上角在画布上的 x、y 坐标(单位为磅)。只要记住"先建文档、再加页、最后在画布上画"的顺序,后续所有内容都是在这个骨架上叠加。

在页面上绘制文本

文本是 PDF 里最常见的元素。除了直接给坐标,Spire.PDF 还支持通过 PdfStringFormat 设置水平与垂直对齐方式,让你不必手动计算每个字符的位置。下面的例子演示了居中、右对齐等对齐方式,以及如何在指定矩形区域内对齐文字。

python 复制代码
from spire.pdf.common import *
from spire.pdf import *

doc = PdfDocument()
page = doc.Pages.Add()

# 斜体字体 + 纯色画刷
font = PdfFont(PdfFontFamily.Helvetica, 18.0, PdfFontStyle.Italic)
brush = PdfSolidBrush(PdfRGBColor(Color.get_White()))

# 居中对齐格式
centerFormat = PdfStringFormat(PdfTextAlignment.Center, PdfVerticalAlignment.Middle)
page.Canvas.DrawString(
    "Go! Turn Around! Go! Go! Go!",
    font, brush,
    page.Canvas.ClientSize.Width / 2.0, 40.0,
    centerFormat
)

# 右对齐格式,锚点贴着页面宽度
rightFormat = PdfStringFormat(PdfTextAlignment.Right, PdfVerticalAlignment.Middle)
page.Canvas.DrawString(
    "Right!", font, brush,
    page.Canvas.ClientSize.Width, 20.0,
    rightFormat
)

doc.SaveToFile("DrawText.pdf")
doc.Close()

PdfStringFormat 的两个参数分别是水平对齐与垂直对齐枚举;当对齐方式为右对齐或居中时,传入的 x 坐标会被当作"对齐参考线"而非文字起点。page.Canvas.ClientSize 则返回当前页面的可用尺寸,常用于让内容相对页面宽度居中或贴边,避免写死数字。

让长文本自动分页

当要写入的是一大段文章或整份报告时,手动按页切分既不现实也容易出错。Spire.PDF 提供了 PdfTextWidget 配合 PdfTextLayout 的方案:把整段文本连同字体、画刷交给一个"文本部件",再指定绘制区域与分页策略,引擎会自动把溢出的内容顺延到后续页面。

python 复制代码
from spire.pdf.common import *
from spire.pdf import *

def read_text(fname):
    with open(fname, "r", encoding="utf-8") as f:
        return f.read()

doc = PdfDocument()
section = doc.Sections.Add()
page = section.Pages.Add()

font = PdfFont(PdfFontFamily.Helvetica, 11.0)
str_format = PdfStringFormat()
str_format.LineSpacing = 20.0
brush = PdfBrushes.get_Black()

# 分页策略:内容超出当前页时自动换页
text_layout = PdfTextLayout()
text_layout.Break = PdfLayoutBreakType.FitPage
text_layout.Layout = PdfLayoutType.Paginate

# 以页面可用区域为绘制边界
bounds = RectangleF(PointF(10.0, 20.0), page.Canvas.ClientSize)
widget = PdfTextWidget(read_text("./Demos/Data/report.txt"), font, brush)
widget.StringFormat = str_format
widget.Draw(page, bounds, text_layout)

doc.SaveToFile("LongText.pdf", FileFormat.PDF)
doc.Close()

这里的关键是 PdfLayoutBreakType.FitPage 与 PdfLayoutType.Paginate 的组合:前者表示"放不下就断开",后者表示"按页流动"。RectangleF(PointF, SizeF) 定义了文字绘制的矩形框,超出框高度的部分会被推到新页面。借助这套机制,生成多页报告时你只需关心"写什么",无需操心"第几页写多少"。

在 PDF 中插入图片

生成的文档往往还需要配图,例如报表里的图表截图、证书上的校徽。Spire.PDF 通过 PdfImage.FromFile 加载图片,再用画布的 DrawImage 按指定位置和尺寸绘制;缩放比例由你自行计算,从而兼顾清晰度与版面。

python 复制代码
from spire.pdf.common import *
from spire.pdf import *

doc = PdfDocument()
page = doc.Pages.Add()

# 从文件加载图片
image = PdfImage.FromFile("./Demos/Data/ChartImage.png")
# 等比缩放到原图的 0.25 倍
width = image.Width * 0.25
height = image.Height * 0.25
# 让图片在页面中水平居中
x = (page.Canvas.ClientSize.Width - width) / 2

# 在 (x, 60) 处按计算尺寸绘制图片
page.Canvas.DrawImage(image, x, 60.0, width, height)

doc.SaveToFile("DrawImage.pdf")
doc.Close()

image.Width 与 image.Height 是图片的原始像素尺寸,DrawImage 的后四个参数依次是目标 x、y、宽、高。把宽高同时乘以同一个系数即可保持原图比例,避免拉伸变形;用页面宽度减去图片宽度再除以 2,则能轻松实现水平居中。

绘制基础图形(矩形与线条)

为了让版面更清晰,常常需要画边框、分隔线或底色块。Spire.PDF 的画布提供 DrawRectangle 与 DrawLine,分别依靠"画笔"(PdfPen,控制描边)与"画刷"(PdfSolidBrush,控制填充)来呈现。下面演示如何画一个只有描边的矩形,以及一条分隔线。

python 复制代码
from spire.pdf.common import *
from spire.pdf import *

doc = PdfDocument()
page = doc.Pages.Add()

# 黑色细画笔
pen = PdfPen(PdfRGBColor(Color.get_Black()), 0.1)
# 仅描边的矩形
page.Canvas.DrawRectangle(
    pen,
    RectangleF(PointF(130.0, 100.0), SizeF(400.0, 400.0))
)

# 红色画笔 + 红色填充的矩形
brush = PdfSolidBrush(PdfRGBColor(Color.get_Red()))
page.Canvas.DrawRectangle(
    pen, brush,
    RectangleF(PointF(130.0, 450.0), SizeF(100.0, 50.0))
)

# 一条从 (130, 200) 到 (400, 400) 的线段
page.Canvas.DrawLine(pen, 130.0, 200.0, 400.0, 400.0)

doc.SaveToFile("DrawShapes.pdf")
doc.Close()

DrawRectangle 有两种重载:只传 pen 时画空心矩形,同时传 pen 与 brush 时则描边并填充。PdfPen 的第二个参数是线宽,数值越小线越细。DrawLine 则直接接收起点与终点的坐标。这些图形元素非常适合用来做内容分区、重点框选或页脚分隔线。

综合示例:生成一份带标题、配图与边框的简报

把前面几项能力串起来,就能生成一份像样的单页简报:用矩形画出内容边框,顶部写标题,中部插入一张图表,底部附说明文字。下面演示"建文档 → 加页 → 画边框 → 写标题 → 贴图片"的完整链路。

python 复制代码
from spire.pdf.common import *
from spire.pdf import *

doc = PdfDocument()
page = doc.Pages.Add()

# 1) 用矩形画出内容边框
border_pen = PdfPen(PdfRGBColor(Color.get_Black()), 1.0)
page.Canvas.DrawRectangle(
    border_pen,
    RectangleF(PointF(40.0, 40.0), SizeF(515.0, 740.0))
)

# 2) 在顶部居中写标题
title_font = PdfFont(PdfFontFamily.Helvetica, 24.0, PdfFontStyle.Italic)
title_brush = PdfSolidBrush(PdfRGBColor(Color.get_Black()))
title_format = PdfStringFormat(PdfTextAlignment.Center, PdfVerticalAlignment.Middle)
page.Canvas.DrawString(
    "月度数据简报", title_font, title_brush,
    page.Canvas.ClientSize.Width / 2.0, 70.0, title_format
)

# 3) 插入一张图表图片
image = PdfImage.FromFile("./Demos/Data/ChartImage.png")
img_w = image.Width * 0.6
img_h = image.Height * 0.6
img_x = (page.Canvas.ClientSize.Width - img_w) / 2
page.Canvas.DrawImage(image, img_x, 140.0, img_w, img_h)

# 4) 在图片下方写一段说明
note_font = PdfFont(PdfFontFamily.Helvetica, 11.0)
note_brush = PdfBrushes.get_Black()
page.Canvas.DrawString(
    "以上图表汇总了本月核心指标,详见附件数据。",
    note_font, note_brush, 60.0, 620.0
)

doc.SaveToFile("Briefing.pdf")
doc.Close()

这段脚本把边框、标题、图片与说明按坐标依次落到同一页上,体现出从零生成最典型的工作方式------所有元素的位置都由你掌控。若内容超出一页,只需把"写说明"那段替换为前面介绍的 PdfTextWidget 分页方案,即可无缝扩展为多页文档。

实用技巧

  • 坐标系以磅为单位:画布坐标的左上角是 (0, 0),x 向右、y 向下增长,单位与字号一致,方便对齐。
  • 善用 ClientSize 做相对定位 :用 page.Canvas.ClientSize.Width/Height 代替硬编码,能适配不同页面尺寸,居中、贴边更稳妥。
  • 长内容优先用 PdfTextWidget:一旦文字可能超过一页,就用分页布局而非手写换行,省心且不会截断。
  • 图形先做边框再填内容:先画矩形/分隔线确定区域,再在区域内绘制文字与图片,版面更整齐。
  • 记得 Close() 释放资源 :生成结束后调用 doc.Close(),避免文件被占用或句柄泄漏,尤其是批量生成时要成对出现。

总结

本文介绍了如何使用 Python 和 Spire.PDF 从零自主生成结构化 PDF 文档。通过创建空白画布、灵活绘制文本与基础图形、处理长文本自动分页以及精准插入图片,你可以将后端数据直接稳稳落地到排版可控的页面中,彻底摆脱对中间转换格式的依赖。在实际项目中,建议先规划好页面布局与分区,针对可能超长的正文提前做好分页策略。掌握这些核心能力后,无论是自动开具发票还是批量导出数据报告,都能通过代码高效稳妥地搞定。

相关推荐
benchmark_cc1 小时前
前复权数据每天可能变化,历史数据库应该覆盖还是保留原始价格?
大数据·python·pandas·量化交易·股票数据·quantdash
计算机编程指导师1 小时前
计算机毕设选题推荐:Hadoop大数据下的个体肥胖健康风险评估与可视化系统源码 毕业设计 毕设选题 数据分析 机器学习
大数据·hadoop·python·spark·毕业设计·课程设计·肥胖健康风险
code2cat1 小时前
【随笔】MCP输入Schema:把参数错误拦在工具执行之前
python·json schema·ai agent·mcp
计算机编程指导师1 小时前
Python大数据毕设怎么写?Django结合Hadoop+Spark京东销售分析系统源码 毕业设计 选题推荐 毕设选题 数据分析 机器学习
大数据·hadoop·python·spark·毕业设计·课程设计·京东商品
合尘猫1 小时前
AI 项目上线就停:一次事件循环阻塞的完整排查过程
人工智能·后端·python
染指11102 小时前
142.Agent-多Agent框架-通过Skills实现多Agent
开发语言·人工智能·python·langchain·agents
天国梦2 小时前
国密门禁品牌怎么选?2026年最新3个避坑要点
运维·服务器·数据库·python
Smoothcloud润云2 小时前
PyTorch推理请求超时与取消处理实战
人工智能·pytorch·python·深度学习·机器学习·gpu算力·gpu
敲代码的乔帮主2 小时前
Python 3 现代类型标注入门
开发语言·python