在企业日常运营中,JSON 格式的数据广泛应用于 API 接口、配置文件和数据交换场景。但将结构化的 JSON 数据转换为适合阅读和分发的 PDF 报告,往往需要手动复制粘贴或借助复杂的模板工具,不仅效率低下,还容易出现格式错乱、数据遗漏。对于需要定期生成财务报表、项目进度报告、客户信息汇总等场景,自动化生成 PDF 文档成为提升工作效率的关键需求。
本文将介绍如何使用 Python 从 JSON 数据自动生成格式规范的 PDF 文档,所有代码基于 Free Spire.PDF for Python(- pip install spire.pdf.free
复制代码 )。整个过程完全自动化,无需人工干预,适用于业务报表生成、数据归档、合同附件制作、API 数据可视化等场景。你可以灵活控制文档布局、字体样式、表格结构和页面元素,确保生成的 PDF 专业美观。
一、准备 JSON 数据并创建 PDF 文档
首先定义示例 JSON 数据(以销售数据报告为例,包含产品信息、销售记录和统计摘要),并初始化 PDF 文档对象。
- import json
- from spire.pdf import PdfDocument, PdfPageBase
- from spire.pdf.graphics import *
- json_data = """
- {
- "report_title": "2024年第一季度销售报告",
- "company": "ABC科技有限公司",
- "date": "2024-03-31",
- "summary": {
- "total_sales": 1580000,
- "total_orders": 342,
- "average_order_value": 4619.88
- },
- "products": [
- {"name": "笔记本电脑", "category": "电子产品", "quantity": 120, "unit_price": 5999, "total": 719880},
- {"name": "无线鼠标", "category": "配件", "quantity": 450, "unit_price": 89, "total": 40050},
- {"name": "机械键盘", "category": "配件", "quantity": 280, "unit_price": 399, "total": 111720},
- {"name": "显示器", "category": "电子产品", "quantity": 95, "unit_price": 2499, "total": 237405},
- {"name": "USB集线器", "category": "配件", "quantity": 600, "unit_price": 59, "total": 35400}
- ]
- }
- """
- data = json.loads(json_data)
- doc = PdfDocument()
- page = doc.Pages.Add()
复制代码 将 JSON 字符串解析为 Python 字典。创建 PDF 文档对象,添加新页面。示例数据包含报告标题、公司信息、统计摘要和产品列表,模拟真实业务场景。
二、绘制文档标题和公司信息
在页面顶部绘制报告标题和公司基本信息,建立文档的整体框架。
- def draw_header(page, data):
- margin_left = 50
- margin_top = 50
- y_position = margin_top
- company_font = PdfTrueTypeFont("Arial", 14.0, PdfFontStyle.Bold, True)
- company_brush = PdfSolidBrush(PdfRGBColor(Color.get_DarkGray()))
- page.Canvas.DrawString(data["company"], company_font, company_brush, margin_left, y_position)
- y_position += 25
- title_font = PdfTrueTypeFont("Arial", 24.0, PdfFontStyle.Bold, True)
- title_brush = PdfSolidBrush(PdfRGBColor(Color.get_Navy()))
- title_format = PdfStringFormat(PdfTextAlignment.Left)
- page.Canvas.DrawString(data["report_title"], title_font, title_brush, margin_left, y_position, title_format)
- y_position += 35
- date_font = PdfTrueTypeFont("Arial", 11.0, PdfFontStyle.Regular, True)
- date_brush = PdfSolidBrush(PdfRGBColor(Color.get_Gray()))
- page.Canvas.DrawString(f"报告日期:{data['date']}", date_font, date_brush, margin_left, y_position)
- return y_position + 30
- current_y = draw_header(page, data)
复制代码 设置字体名称、大小和样式(如 Bold)。定义文本颜色,在指定坐标绘制文本,可配合设置对齐方式。通过递增 y_position 实现垂直布局,避免重叠。
三、绘制统计摘要信息
在标题下方展示关键的业务统计数据,以简洁方式呈现核心指标。
- def draw_summary(page, data, y_position):
- margin_left = 50
- summary = data["summary"]
- section_font = PdfTrueTypeFont("Arial", 16.0, PdfFontStyle.Bold, True)
- section_brush = PdfSolidBrush(PdfRGBColor(Color.get_Black()))
- page.Canvas.DrawString("销售摘要", section_font, section_brush, margin_left, y_position)
- y_position += 30
- content_font = PdfTrueTypeFont("Arial", 11.0, PdfFontStyle.Regular, True)
- content_brush = PdfSolidBrush(PdfRGBColor(Color.get_Black()))
- summary_items = [
- f"总销售额:¥{summary['total_sales']:,.2f}",
- f"订单总数:{summary['total_orders']}",
- f"平均订单价值:¥{summary['average_order_value']:,.2f}"
- ]
- for item in summary_items:
- page.Canvas.DrawString(item, content_font, content_brush, margin_left, y_position)
- y_position += 22
- return y_position + 20
- current_y = draw_summary(page, data, current_y)
复制代码
使用 f-string 格式化数字,添加千位分隔符,保留两位小数。摘要信息逐行排列,间隔 22 个单位,形成清晰的信息层级。
四、创建产品明细表格
表格是展示结构化数据的最佳方式。将 JSON 中的产品列表转换为 PDF 表格,包含表头和数据行。
- def draw_product_table(page, data, y_position):
- margin_left = 50
- products = data["products"]
- table_title_font = PdfTrueTypeFont("Arial", 16.0, PdfFontStyle.Bold, True)
- table_title_brush = PdfSolidBrush(PdfRGBColor(Color.get_Black()))
- page.Canvas.DrawString("产品明细", table_title_font, table_title_brush, margin_left, y_position)
- y_position += 30
- columns = ["产品名称", "类别", "数量", "单价", "总价"]
- column_widths = [120, 100, 80, 100, 100]
- header_font = PdfTrueTypeFont("Arial", 11.0, PdfFontStyle.Bold, True)
- header_brush = PdfSolidBrush(PdfRGBColor(Color.get_White()))
- header_bg_brush = PdfSolidBrush(PdfRGBColor(Color.get_Navy()))
- x_position = margin_left
- for i, col in enumerate(columns):
- rect = RectangleF(x_position, y_position, column_widths[i], 25)
- page.Canvas.DrawRectangle(header_bg_brush, rect)
- page.Canvas.DrawString(col, header_font, header_brush, x_position + 5, y_position + 5)
- x_position += column_widths[i]
- y_position += 25
- row_font = PdfTrueTypeFont("Arial", 10.0, PdfFontStyle.Regular, True)
- row_brush = PdfSolidBrush(PdfRGBColor(Color.get_Black()))
- alternate_brush = PdfSolidBrush(PdfRGBColor(Color.get_LightGray()))
- for idx, product in enumerate(products):
- bg_brush = PdfSolidBrush(PdfRGBColor(Color.get_White())) if idx % 2 == 0 else alternate_brush
- x_position = margin_left
- row_height = 22
- row_rect = RectangleF(margin_left, y_position, sum(column_widths), row_height)
- page.Canvas.DrawRectangle(bg_brush, row_rect)
- cell_data = [
- product["name"],
- product["category"],
- str(product["quantity"]),
- f"¥{product['unit_price']:,}",
- f"¥{product['total']:,}"
- ]
- for i, cell_value in enumerate(cell_data):
- page.Canvas.DrawString(cell_value, row_font, row_brush, x_position + 5, y_position + 4)
- x_position += column_widths[i]
- y_position += row_height
- border_pen = PdfPen(PdfRGBColor(Color.get_Black()), 0.5)
- table_width = sum(column_widths)
- table_height = 25 + len(products) * 22
- page.Canvas.DrawRectangle(border_pen, RectangleF(margin_left, y_position - table_height, table_width, table_height))
- # 横线
- for i in range(len(products) + 2):
- if i == 0:
- line_y = y_position - table_height
- elif i == 1:
- line_y = y_position - table_height + 25
- else:
- line_y = y_position - table_height + 25 + (i - 1) * 22
- page.Canvas.DrawLine(border_pen, margin_left, line_y, margin_left + table_width, line_y)
- # 竖线
- x_pos = margin_left
- for width in column_widths:
- page.Canvas.DrawLine(border_pen, x_pos, y_position - table_height, x_pos, y_position)
- x_pos += width
- page.Canvas.DrawLine(border_pen, x_pos, y_position - table_height, x_pos, y_position)
- return y_position + 30
- current_y = draw_product_table(page, data, current_y)
复制代码
表格采用表头深色背景、白色文字的设计,数据行使用交替背景色(白色和浅灰色),提升视觉区分度。用于绘制矩形区域,绘制横线和竖线形成网格。列宽根据内容长度合理分配。
五、添加页脚信息
在页面底部添加页脚,包含页码和版权信息,使文档更完整专业。
- def draw_footer(page, page_number, total_pages):
- page_width = page.Canvas.ClientSize.Width
- margin_bottom = 30
- line_pen = PdfPen(PdfRGBColor(Color.get_LightGray()), 0.5)
- page.Canvas.DrawLine(line_pen, 50, page.Canvas.ClientSize.Height - margin_bottom - 15, page_width - 50, page.Canvas.ClientSize.Height - margin_bottom - 15)
- footer_font = PdfTrueTypeFont("Arial", 9.0, PdfFontStyle.Regular, True)
- footer_brush = PdfSolidBrush(PdfRGBColor(Color.get_Gray()))
- footer_format = PdfStringFormat(PdfTextAlignment.Center)
- page_text = f"第 {page_number} 页 / 共 {total_pages} 页"
- page.Canvas.DrawString(page_text, footer_font, footer_brush, page_width / 2, page.Canvas.ClientSize.Height - margin_bottom, footer_format)
- copyright_text = "© 2024 ABC科技有限公司 - 内部资料,请勿外传"
- page.Canvas.DrawString(copyright_text, footer_font, footer_brush, page_width / 2, page.Canvas.ClientSize.Height - margin_bottom + 15, footer_format)
- draw_footer(page, 1, 1)
复制代码
页脚包含水平分隔线、页码和版权声明,居中对齐,字体较小颜色较浅,不影响主内容。
六、保存 PDF 文档
- output_file = "销售报告.pdf"
- doc.SaveToFile(output_file)
- doc.Close()
- print(f"PDF 文档已生成:{output_file}")
复制代码 将内存中的 PDF 文档保存到文件系统,释放资源。
关键类与方法解析
主要类说明
- PdfDocument:PDF 文档对象,管理整个文档生命周期
- PdfPageBase:页面对象,提供画布用于绘制内容
- PdfTrueTypeFont:字体类,设置字体名称、大小、样式
- PdfSolidBrush:画刷类,定义填充颜色
- PdfPen:画笔类,定义线条颜色和宽度
- PdfStringFormat:文本格式类,设置对齐方式等
- RectangleF:矩形结构,定义绘制区域
- PdfRGBColor:颜色类,支持 RGB 色彩模式
常用方法
- :添加新页面
- page.Canvas.DrawString(text, font, brush, x, y, format)
复制代码 :绘制文本
- page.Canvas.DrawRectangle(brush/pen, rectangle)
复制代码 :绘制矩形
- page.Canvas.DrawLine(pen, x1, y1, x2, y2)
复制代码 :绘制直线
- :保存文档
- :关闭文档释放资源
字体与样式设置- # 创建字体
- font = PdfTrueTypeFont("Arial", 12.0, PdfFontStyle.Bold, True)
- # 参数:字体名称、字号、样式(Bold/Italic/Regular/Underline)、是否嵌入
- # 创建画刷
- brush = PdfSolidBrush(PdfRGBColor(Color.get_Navy()))
- # 支持的颜色:get_Black(), get_White(), get_Red(), get_Blue(), get_Navy() 等
- # 创建画笔
- pen = PdfPen(PdfRGBColor(Color.get_Black()), 0.5)
- # 设置文本格式
- format = PdfStringFormat(PdfTextAlignment.Center, PdfVerticalAlignment.Middle)
复制代码
布局技巧
- 垂直布局:递增 y 坐标实现从上到下排列
- 水平布局:递增 x 坐标实现从左到右排列
- 边距控制:设置 margin_left、margin_top 等变量统一管理
- 间距调整:根据字体高度和内容重要性调整行间距
扩展应用场景
1. 多页文档支持
当数据量较大时,需自动分页:- if current_y > page.Canvas.ClientSize.Height - 100:
- page = doc.Pages.Add()
- current_y = 50
复制代码
2. 从文件读取 JSON- with open('sales_data.json', 'r', encoding='utf-8') as f:
- data = json.load(f)
复制代码
3. 添加图表可视化(简单柱状图)- for i, product in enumerate(products):
- bar_height = product['total'] / 10000
- bar_rect = RectangleF(margin_left + i * 60, chart_y, 50, bar_height)
- page.Canvas.DrawRectangle(bar_brush, bar_rect)
复制代码
4. 批量生成报告- import glob
- for json_file in glob.glob('reports/*.json'):
- with open(json_file, 'r') as f:
- data = json.load(f)
- generate_pdf(data, f"report_{json_file}.pdf")
复制代码
5. 自定义模板系统- class PDFTemplate:
- def __init__(self, config):
- self.font_config = config['fonts']
- self.color_scheme = config['colors']
- self.layout = config['layout']
- def render(self, data):
- # 根据配置渲染 PDF
- pass
复制代码
总结
通过本文示例,你已掌握使用 Python 从 JSON 数据自动生成专业 PDF 文档的完整流程:解析 JSON 数据结构 → 绘制标题、摘要、表格和页脚 → 保存文件。整个过程完全自动化,特别适用于业务报表、数据归档和文档分发场景。
相比手动制作 PDF,代码方式具有显著优势:高效性(一键生成)、一致性(统一格式和样式)、灵活性(根据数据动态调整布局)、可扩展性(轻松集成到现有系统)。在此基础上,可以进一步探索添加水印、加密文档、插入图像、生成目录等高级功能,为企业级文档自动化提供完整解决方案。 |