3分钟上手!WeasyPrint与Python Web框架集成:Django/Flask PDF生成方案
【免费下载链接】WeasyPrint The awesome document factory 项目地址: https://gitcode.com/gh_mirrors/we/WeasyPrint
你是否还在为Python Web应用生成PDF文件烦恼?从发票、报告到用户证书,动态PDF生成是许多业务系统的刚需功能。但现有方案要么依赖复杂的LaTeX模板,要么受制于浏览器渲染的兼容性问题。本文将展示如何用WeasyPrint与Django/Flask无缝集成,3行代码实现专业级PDF文档生成。
读完本文你将掌握:
- Django视图中直接输出PDF响应的2种实现方式
- Flask路由中生成可下载PDF的最佳实践
- 自定义页眉页脚和分页控制的CSS技巧
- 生产环境中的性能优化与缓存策略
核心原理:HTML+CSS=PDF的现代方案
WeasyPrint作为Python生态中最成熟的PDF生成工具,其核心优势在于将Web标准(HTML/CSS)转化为高质量PDF。不同于传统PDF库需要手动绘制内容,WeasyPrint允许开发者使用熟悉的前端技术定义文档结构,通过HTML.write_pdf()方法一键转换。
这种"所见即所得"的开发模式带来三大好处:
- 开发效率:复用前端工程师和现有CSS框架
- 设计灵活:利用CSS Paged Media实现复杂排版
- 维护成本:模板与业务逻辑分离,便于修改
Django集成:从模板到PDF响应
Django与WeasyPrint的集成遵循MVT架构,典型场景是在视图层将渲染好的模板转换为PDF。以下是两种生产级实现方案:
基础方案:直接生成PDF响应
from django.http import HttpResponse
from django.template.loader import render_to_string
from weasyprint import HTML # 导入核心类[weasyprint/__init__.py#L116]
def invoice_pdf(request, invoice_id):
invoice = Invoice.objects.get(id=invoice_id)
html = render_to_string('invoices/invoice.html', {'invoice': invoice})
# 核心转换代码:HTML字符串 → PDF字节流
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = f'attachment; filename="invoice_{invoice_id}.pdf"'
HTML(string=html).write_pdf(response) # 关键调用[weasyprint/__init__.py#L267]
return response
高级方案:带权限控制的PDF视图
对于需要用户认证的敏感文档,建议使用Django的权限系统结合WeasyPrint的缓存机制:
from django.contrib.auth.decorators import login_required
from weasyprint import HTML, Attachment # 支持文件附件[weasyprint/__init__.py#L319]
@login_required
def secure_report(request):
# 权限验证
if not request.user.has_perm('reports.view_secure'):
return HttpResponseForbidden()
# 优化:使用缓存减少重复渲染[docs/common_use_cases.rst#L490]
cache_key = f"report_{request.user.id}"
cached_pdf = cache.get(cache_key)
if not cached_pdf:
html = render_to_string('reports/secure.html', {'user': request.user})
# 添加数字签名附件
attachments = [Attachment(string=request.user.signature, name='signature.png')]
# 生成PDF/A-3a归档格式[docs/common_use_cases.rst#L108]
pdf = HTML(string=html).write_pdf(pdf_variant='pdf/a-3a', attachments=attachments)
cache.set(cache_key, pdf, 3600) # 缓存1小时
response = HttpResponse(cached_pdf, content_type='application/pdf')
response['Content-Disposition'] = 'inline; filename="secure_report.pdf"'
return response
Flask集成:轻量级路由中的PDF生成
Flask的轻量级特性与WeasyPrint的简洁API相得益彰,特别适合中小型应用。以下是两种常用模式:
路由直接输出
from flask import Flask, render_template_string
from weasyprint import HTML # 核心依赖[weasyprint/__init__.py]
app = Flask(__name__)
@app.route('/resume/<username>')
def generate_resume(username):
user = get_user_profile(username)
# 内联模板示例
html_template = '''
<!DOCTYPE html>
<html>
<head>
<style>
@page { size: A4; margin: 2cm } /* 页面设置[docs/common_use_cases.rst#L88] */
.header { position: fixed; top: 0; right: 0 }
</style>
</head>
<body>
<div class="header">文档</div>
<h1>{{ user.name }}的简历</h1>
<!-- 内容 -->
</body>
</html>
'''
html = render_template_string(html_template, user=user)
return HttpResponse(
HTML(string=html).write_pdf(),
content_type='application/pdf',
headers={'Content-Disposition': f'attachment; filename="{username}_resume.pdf"'}
)
使用蓝图组织PDF相关路由
对于大型Flask应用,建议使用蓝图集中管理PDF生成功能:
from flask import Blueprint, request, make_response
from weasyprint import HTML, CSS # CSS类[weasyprint/__init__.py#L272]
pdf_bp = Blueprint('pdf', __name__, url_prefix='/pdf')
@pdf_bp.route('/receipt', methods=['POST'])
def generate_receipt():
data = request.json
# 应用自定义样式表
css = CSS(string='''
.total { font-size: 18pt; color: #e63946; }
@page { @bottom-center { content: "Page " counter(page) } } /* 页码[docs/common_use_cases.rst#L99] */
''')
html = f'''
<html>
<head><title>Receipt #{data['id']}</title></head>
<body>
<h1>Payment Receipt</h1>
<p>Amount: ${data['amount']}</p>
<p class="total">Total: ${data['amount']}</p>
</body>
</html>
'''
# 生成PDF并设置缓存控制头
response = make_response(HTML(string=html).write_pdf(stylesheets=[css]))
response.headers['Content-Type'] = 'application/pdf'
response.headers['Cache-Control'] = 'public, max-age=86400' # 缓存1天
return response
高级排版控制:CSS技巧与最佳实践
专业PDF通常需要精确的排版控制,WeasyPrint完全支持CSS Paged Media标准,通过docs/common_use_cases.rst#adjust-document-dimensions可实现复杂布局:
自定义页面大小与边距
@page {
size: A5 landscape; /* 横向A5纸张 */
margin: 2cm;
/* 页眉页脚 */
@top-center {
content: "文档";
font-size: 10pt;
color: #666;
}
@bottom-right {
content: "Page " counter(page) " of " counter(pages);
}
}
处理分页与表格断裂
/* 避免标题孤行 */
h2 {
page-break-after: avoid;
page-break-inside: avoid;
}
/* 表格跨页显示优化 */
table {
page-break-inside: auto;
}
tr {
page-break-inside: avoid;
page-break-after: auto;
}
性能优化:生产环境注意事项
在高并发场景下,建议实施以下优化策略(docs/common_use_cases.rst#improve-rendering-speed-and-memory-use):
- 图片优化:启用图片压缩与缓存
HTML(string=html).write_pdf(
optimize_images=True, # 自动优化图片
jpeg_quality=75, # JPEG质量控制
dpi=150, # 降低分辨率
cache=image_cache # 共享缓存对象
)
- 异步生成:对大型文档使用Celery异步处理
@shared_task
def generate_large_report(user_id, report_id):
# 长时间运行的PDF生成任务
html = render_to_string('large_report.html', {'user_id': user_id})
pdf = HTML(string=html).write_pdf()
Report.objects.filter(id=report_id).update(pdf_file=pdf)
- 资源复用:共享字体配置与CSS解析结果
from weasyprint.text.fonts import FontConfiguration
# 全局字体配置单例
font_config = FontConfiguration()
def generate_pdf(html):
return HTML(string=html).write_pdf(font_config=font_config)
总结与后续展望
WeasyPrint通过将Web技术栈转化为PDF生成能力,大幅降低了Python后端的文档生成门槛。无论是Django的企业级应用还是Flask的轻量级服务,都能通过简洁API实现专业文档输出。
官方文档docs/common_use_cases.rst还提供了更多高级场景:
- PDF/UA无障碍标准支持
- Factur-X/ZUGFeRD电子发票生成
- 动态表单与文件附件功能
建议收藏本文并关注项目更新,下一篇我们将深入探讨复杂报表的自动化测试与CI/CD集成方案。
【免费下载链接】WeasyPrint The awesome document factory 项目地址: https://gitcode.com/gh_mirrors/we/WeasyPrint
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



