You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

零基础使用FPDF生成PDF遭遇字符编码异常问题求助

解决FPDF生成PDF时的Unicode字符编码错误

错误原因

你碰到的FPDFUnicodeEncodingException是因为当前使用的字体(helvetica/Arial)不支持爬取文本里的特殊Unicode字符(比如示例中的’)。FPDF默认字体仅覆盖ASCII字符集,没法处理网页爬取的各种特殊符号(弯引号、长横线、非英文字符等)。

解决方案

方案1:加载支持Unicode的字体(推荐)

FPDF允许加载外部TrueType/OpenType字体,这类字体基本覆盖所有Unicode字符,步骤如下:

  1. 准备字体文件
    系统自带的DejaVu Sans、Noto Sans都是不错的选择,它们支持几乎所有Unicode字符。找到系统字体目录(Windows:C:\Windows\Fonts;Linux:/usr/share/fonts;macOS:/Library/Fonts),复制对应的.ttf文件到你项目里新建的fonts文件夹中,比如:

    • DejaVuSans.ttf(普通样式)
    • DejaVuSans-Bold.ttf(粗体)
  2. 在代码中加载字体
    初始化FPDF对象后,添加字体加载代码:

    pdf = FPDF()
    # 加载Unicode字体,uni=True表示启用Unicode支持
    pdf.add_font("DejaVuSans", "", "fonts/DejaVuSans.ttf", uni=True)
    pdf.add_font("DejaVuSans", "B", "fonts/DejaVuSans-Bold.ttf", uni=True)
    
  3. 替换代码中的字体引用
    把所有pdf.set_font里的字体名从Arial改成DejaVuSans,比如:

    # 原代码:pdf.set_font("Arial", "U", 30)
    pdf.set_font("DejaVuSans", "U", 30)
    # 原代码:pdf.set_font("Arial", "B", 20)
    pdf.set_font("DejaVuSans", "B", 20)
    # 原代码:pdf.set_font("Arial", size=16)
    pdf.set_font("DejaVuSans", size=16)
    

方案2:预处理文本替换/过滤特殊字符(应急用)

如果暂时没法加载外部字体,可以先清理爬取的文本,把不支持的字符换成ASCII等价字符,或者直接移除:

  1. 写个文本清理函数

    def clean_text(text):
        # 替换常见特殊字符为ASCII版本
        char_map = {
            "’": "'",
            "‘": "'",
            "“": '"',
            "”": '"',
            "—": "-",
            "–": "-"
        }
        for old_char, new_char in char_map.items():
            text = text.replace(old_char, new_char)
        # 可选:移除所有ASCII以外的字符
        text = text.encode("ascii", errors="ignore").decode("ascii")
        return text
    
  2. 在代码中应用清理
    使用文本的地方都先调用clean_text:

    # 目录章节名
    chapter_name = f"Chapter {chapter_dot_part[title_index]}: {clean_text(titles[title_index])}\n"
    # 章节标题
    pdf.write(txt=clean_text(titles[chapter]) + "\n\n")
    # 正文内容
    pdf.write(txt=clean_text(line.text) + "\n")
    

修正后的完整代码(方案1)

import os
from fpdf import FPDF

async def generate_pdf(data, indices, name, cover, path, list_ch=True):
    # 简化文件名生成逻辑
    file_name = f"{name}({indices[0]+1}-{indices[1]}).pdf" if list_ch else f"{name}.pdf"
    output_path = os.path.join(path, file_name)
    doc_name = data.title
    titles = data.df["Title"].iloc[indices[0]:indices[1]]
    chapter_content = data.df["Content"].iloc[indices[0]:indices[1]]
    chapter_dot_part = [f"{data.df.at[index, 'Chapter']}.{data.df.at[index, 'Part']}" 
                        for index in range(indices[0], indices[1])]

    pdf = FPDF()
    # 加载Unicode字体
    pdf.add_font("DejaVuSans", "", "fonts/DejaVuSans.ttf", uni=True)
    pdf.add_font("DejaVuSans", "B", "fonts/DejaVuSans-Bold.ttf", uni=True)

    pdf.add_page("P", "A3")
    pdf.image(cover, 0, 0, w=297, h=420)

    pdf.add_page("P", "A3")
    pdf.set_xy(143, 0)
    pdf.set_font("DejaVuSans", "U", 30)
    pdf.write(txt=f"{doc_name}\n\n")
    pdf.set_font("DejaVuSans", "B", 20)
    pdf.write(txt="Table of contents\n\n")
    for title_index in range(len(titles)):
        chapter_name = f"Chapter {chapter_dot_part[title_index]}: {titles[title_index]}\n"
        pdf.write(txt=chapter_name)

    for chapter in range(len(chapter_content)):
        pdf.add_page("P", "A3")
        pdf.set_xy(0, 0)
        pdf.set_font("DejaVuSans", "U", 23)
        pdf.write(txt=f"{titles[chapter]}\n\n")
        pdf.set_font("DejaVuSans", size=16)
        for line in chapter_content[chapter].find_all("p"):
            pdf.write(txt=f"{line.text}\n")

    pdf.output(output_path)

注:原代码里chapter_dot_part是列表,循环时直接用chapter_dot_part会把整个列表拼进字符串,已经修正为chapter_dot_part[title_index]。

内容的提问来源于stack exchange,提问作者Renni Stewart

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 07:18:19