Files

130 lines
4.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""
PDF工具类 - 用于生成和填充PDF文档
"""
import io
import os
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import mm
from reportlab.pdfgen import canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.lib.colors import HexColor
from pypdf import PdfReader, PdfWriter
from app.core.logger import log
class PDFUtil:
"""PDF工具类"""
# 字体路径(需要中文字体支持)
FONT_PATH = os.path.join(os.path.dirname(__file__), '..', '..', 'static', 'fonts')
@classmethod
def register_chinese_font(cls):
"""注册中文字体"""
try:
# 尝试注册思源黑体或其他中文字体
font_file = os.path.join(cls.FONT_PATH, 'SimHei.ttf')
if os.path.exists(font_file):
pdfmetrics.registerFont(TTFont('SimHei', font_file))
return 'SimHei'
# 尝试系统字体
system_fonts = [
'C:/Windows/Fonts/simhei.ttf', # Windows
'/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc', # Linux
'/System/Library/Fonts/PingFang.ttc', # macOS
]
for font_path in system_fonts:
if os.path.exists(font_path):
pdfmetrics.registerFont(TTFont('ChineseFont', font_path))
return 'ChineseFont'
log.warning("未找到中文字体,将使用默认字体")
return 'Helvetica'
except Exception as e:
log.error(f"注册中文字体失败: {e}")
return 'Helvetica'
@classmethod
def create_overlay_pdf(cls, data: dict, page_width: float, page_height: float) -> io.BytesIO:
"""
创建覆盖层PDF用于在模板上叠加文字
参数:
- data: 要填充的数据字典,格式: {字段名: (x, y, 值, 字体大小, 颜色)}
- page_width: 页面宽度
- page_height: 页面高度
返回:
- BytesIO: PDF字节流
"""
buffer = io.BytesIO()
c = canvas.Canvas(buffer, pagesize=(page_width, page_height))
font_name = cls.register_chinese_font()
for field_name, field_config in data.items():
if isinstance(field_config, tuple) and len(field_config) >= 3:
x, y, value = field_config[:3]
font_size = field_config[3] if len(field_config) > 3 else 12
color = field_config[4] if len(field_config) > 4 else '#000000'
c.setFont(font_name, font_size)
c.setFillColor(HexColor(color))
c.drawString(x * mm, y * mm, str(value))
c.save()
buffer.seek(0)
return buffer
@classmethod
def fill_pdf_template(cls, template_path: str, data: dict, field_positions: dict) -> bytes:
"""
填充PDF模板
参数:
- template_path: 模板文件路径
- data: 数据字典
- field_positions: 字段位置配置,格式: {字段名: (x, y, 字体大小, 颜色)}
返回:
- bytes: 填充后的PDF字节
"""
# 读取模板
reader = PdfReader(template_path)
writer = PdfWriter()
# 获取第一页尺寸
first_page = reader.pages[0]
page_width = float(first_page.mediabox.width)
page_height = float(first_page.mediabox.height)
# 构建覆盖数据
overlay_data = {}
for field_name, position in field_positions.items():
if field_name in data and data[field_name] is not None:
x, y = position[:2]
font_size = position[2] if len(position) > 2 else 12
color = position[3] if len(position) > 3 else '#000000'
overlay_data[field_name] = (x, y, data[field_name], font_size, color)
# 创建覆盖层
overlay_buffer = cls.create_overlay_pdf(overlay_data, page_width, page_height)
overlay_reader = PdfReader(overlay_buffer)
# 合并页面
for i, page in enumerate(reader.pages):
if i == 0 and len(overlay_reader.pages) > 0:
page.merge_page(overlay_reader.pages[0])
writer.add_page(page)
# 输出
output = io.BytesIO()
writer.write(output)
output.seek(0)
return output.read()