Python中使用FPDF2生成PDF:行内加粗、Unicode支持及自动分页
使用FPDF2编写Python脚本生成PDF时,原本通过开启markdown=True的multi_cell()或write_html()实现行内加粗功能正常,但当文本包含Unicode字符““”时,出现以下错误:
fpdf.errors.FPDFUnicodeEncodingException: Character "“" at index 544 in text is outside the range of characters supported by the font used: "times". Please consider using a Unicode font.
于是下载了SourceSerifPro字体并通过add_font()添加,代码如下:
texto1 = f"""**{nombre}**, mayor de edad, vecino de **{ciudad_usuario}**, identificado con la **{id_tipo}** No. **{id_numero}** expedida en **{id_lugar_expedicion}**, actuando con mi propio nombre y ... """ fuente_tipo = "SourceSerifPro-Regular" class PDF(FPDF): def __init__(self): FPDF.__init__(self, unit="mm", format="letter", orientation="P") self.add_font("SourceSerifPro-Regular", "", "SourceSerifPro-Regular.ttf") def header(self): self.set_xy(25, 10) self.set_font(fuente_tipo, '', 12) self.ln(15) self.cell(0, 10, fecha, 0) self.ln(15) def footer(self): self.set_xy(185, -15) self.set_font(family= fuente_tipo, size= 8) self.cell(0, 10, 'Página ' + str(self.page_no()) + ' de n', 0) def add_txt_text(self, route): with open(route, "r", encoding="utf-8") as f: texto = f.read() self.multi_cell(0, None, texto, markdown = True, new_x='LEFT', new_y= 'NEXT') pdf = PDF() pdf.set_margins(25, 30, 10) pdf.add_page() pdf.set_auto_page_break(auto=True, margin=20) pdf.set_font(fuente_tipo, '', 12) pdf.multi_cell(0, None, texto1, markdown = True, new_x='LEFT', new_y= 'NEXT') # max_line_height=30, # This text has a unicode caracter pdf.add_txt_text('fundamento_juridico.txt') pdf.output('ejemplo.pdf')
但添加字体后又出现新错误:
File "C:\Users\user\Documents\PDF_generator\index.py", line 101, in <module> pdf.multi_cell(0, None, texto1, markdown = True, new_x='LEFT', new_y= 'NEXT') # max_line_height=30, File "C:\Users\user\AppData\Local\Programs\Python\Python39\lib\site-packages\fpdf\fpdf.py", line 216, in wrapper return fn(self, *args, **kwargs) File "C:\Users\user\AppData\Local\Programs\Python\Python39\lib\site-packages\fpdf\fpdf.py", line 3213, in multi_cell styled_text_fragments = self._preload_font_styles(normalized_string, markdown) File "C:\Users\user\AppData\Local\Programs\Python\Python39\lib\site-packages\fpdf\fpdf.py", line 3004, in _preload_font_styles self.set_font(style="B") File "C:\Users\user\AppData\Local\Programs\Python\Python39\lib\site-packages\fpdf\fpdf.py", line 1873, in set_font raise FPDFException( fpdf.errors.FPDFException: Undefined font: sourceserifpro-regularB - Use built-in fonts or FPDF.add_font() beforehand
当前困境:要么删除Unicode字符,要么无法使用行内加粗。需要实现同时支持行内加粗、Unicode字符、文本触底自动分页的PDF生成。
问题核心是仅添加了常规字重的字体,而Markdown加粗需要对应字体的粗体版本,调整步骤如下:
下载粗体字体文件:获取SourceSerifPro的粗体版本(如
SourceSerifPro-Bold.ttf),与常规字体放在同一目录。同时添加常规与粗体字体:在PDF类的
__init__方法中,分别添加两种字重的字体,注意粗体的style参数设为"B":
class PDF(FPDF): def __init__(self): FPDF.__init__(self, unit="mm", format="letter", orientation="P") # 添加常规字体 self.add_font("SourceSerifPro", "", "SourceSerifPro-Regular.ttf") # 添加粗体字体 self.add_font("SourceSerifPro", "B", "SourceSerifPro-Bold.ttf")
- 统一字体家族名称:将
fuente_tipo改为字体家族名(去掉-Regular后缀),让FPDF能自动匹配不同字重:
fuente_tipo = "SourceSerifPro"
- 调整所有字体调用:确保所有
set_font使用统一的家族名称,避免指定具体字重文件名。
修改后的完整代码:
texto1 = f"""**{nombre}**, mayor de edad, vecino de **{ciudad_usuario}**, identificado con la **{id_tipo}** No. **{id_numero}** expedida en **{id_lugar_expedicion}**, actuando con mi propio nombre y ... """ fuente_tipo = "SourceSerifPro" class PDF(FPDF): def __init__(self): FPDF.__init__(self, unit="mm", format="letter", orientation="P") self.add_font(fuente_tipo, "", "SourceSerifPro-Regular.ttf") self.add_font(fuente_tipo, "B", "SourceSerifPro-Bold.ttf") def header(self): self.set_xy(25, 10) self.set_font(fuente_tipo, '', 12) self.ln(15) self.cell(0, 10, fecha, 0) self.ln(15) def footer(self): self.set_xy(185, -15) self.set_font(family= fuente_tipo, size= 8) self.cell(0, 10, 'Página ' + str(self.page_no()) + ' de n', 0) def add_txt_text(self, route): with open(route, "r", encoding="utf-8") as f: texto = f.read() self.multi_cell(0, None, texto, markdown = True, new_x='LEFT', new_y= 'NEXT') pdf = PDF() pdf.set_margins(25, 30, 10) pdf.add_page() pdf.set_auto_page_break(auto=True, margin=20) pdf.set_font(fuente_tipo, '', 12) pdf.multi_cell(0, None, texto1, markdown = True, new_x='LEFT', new_y= 'NEXT') pdf.add_txt_text('fundamento_juridico.txt') pdf.output('ejemplo.pdf')
此修改后,既支持Unicode字符,又能正常解析Markdown行内加粗,同时set_auto_page_break已开启,文本触底会自动分页,满足所有需求。
内容的提问来源于stack exchange,提问作者José Daza

