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

使用Borb修改PDF字体触发Rectangle宽度非负AssertionError

问题:使用Borb修改指定字体文本时触发断言错误AssertionError: A Rectangle must have a non-negative width

问题背景

尝试参考#581-filtering-by-font示例,结合SimpleFindReplace修改PDF中指定字体(VFMBOX+HelveticaNeueLTStd-Roman)的文本字体,触发了断言错误。已确认文档中存在该目标字体,当前环境:

  • Borb版本:2.1.25
  • Python版本:3.11

相关代码

#!chapter_005/src/snippet_017.py
import typing

from borb.pdf import Document
from borb.pdf import PDF
from borb.toolkit import FontNameFilter
from borb.toolkit import SimpleTextExtraction
from borb.toolkit import SimpleFindReplace

def main():

    # 创建FontNameFilter
    l0: FontNameFilter = FontNameFilter("VFMBOX+HelveticaNeueLTStd-Roman")

    # 过滤后的文本传递给SimpleTextExtraction
    l1: SimpleTextExtraction = SimpleTextExtraction()
    l0.add_listener(l1)

    # 读取文档
    doc: typing.Optional[Document] = None
    with open("C:\Data\PDFFontChange\PD-1239-G_RevA_PR.pdf", "rb") as in_file_handle:
        doc = PDF.loads(in_file_handle, [l0])

    # 确认文档读取成功
    assert doc is not None

    templist = (l1.get_text()[0]).splitlines()
    # print(templist)
    i = 0 
    for x in templist :
        print(i, ' : ', x)
        i += 1
        doc = SimpleFindReplace.sub(x, x, doc, repl_font="Helvetica")

    # 保存修改后的文档
    with open("output2.pdf", "wb") as pdf_file_handle:
        PDF.dumps(pdf_file_handle, doc)

if __name__ == "__main__":
    main()

错误回溯

0  :  At the heart of your pump or dispenser,
Traceback (most recent call last):
  File "c:\Python\Glenn_Begin\PDF_Workings\BORB_PDF_FontNameAlterTrajan.py", line 43, in <module>
    main()
  File "c:\Python\Glenn_Begin\PDF_Workings\BORB_PDF_FontNameAlterTrajan.py", line 36, in main
    doc = SimpleFindReplace.sub(x, x, doc, repl_font="Helvetica")
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python\Glenn_Begin\.venv\Lib\site-packages\borb\toolkit\text\simple_find_replace.py", line 72, in sub
    ] = RegularExpressionTextExtraction.get_matches_for_pdf(pattern, doc)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python\Glenn_Begin\.venv\Lib\site-packages\borb\toolkit\text\regular_expression_text_extraction.py", line 375, in get_matches_for_pdf
    CanvasStreamProcessor(page, Canvas(), []).read(page_source, [cse])
  File "C:\Python\Glenn_Begin\.venv\Lib\site-packages\borb\pdf\canvas\canvas_stream_processor.py", line 277, in read
    raise e
  File "C:\Python\Glenn_Begin\.venv\Lib\site-packages\borb\pdf\canvas\canvas_stream_processor.py", line 271, in read
    operator.invoke(self, operands, event_listeners)
  File "C:\Python\Glenn_Begin\.venv\Lib\site-packages\borb\pdf\canvas\operator\text\show_text_with_glyph_positioning.py", line 84, in invoke
    l._event_occurred(tri)
  File "C:\Python\Glenn_Begin\.venv\Lib\site-packages\borb\toolkit\text\regular_expression_text_extraction.py", line 326, in _event_occurred
    self._render_text(event)
  File "C:\Python\Glenn_Begin\.venv\Lib\site-packages\borb\toolkit\text\regular_expression_text_extraction.py", line 338, in _render_text
    for e in text_render_info.split_on_glyphs():
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python\Glenn_Begin\.venv\Lib\site-packages\borb\pdf\canvas\event\chunk_of_text_render_event.py", line 177, in split_on_glyphs
    e._baseline_bounding_box = Rectangle(
                               ^^^^^^^^^^
  File "C:\Python\Glenn_Begin\.venv\Lib\site-packages\borb\pdf\canvas\geometry\rectangle.py", line 30, in __init__
    assert width >= 0, "A Rectangle must have a non-negative width."
           ^^^^^^^^^^
AssertionError: A Rectangle must have a non-negative width.

分析与解决方向

  1. 错误根源:该断言错误通常是因为PDF中存在宽度为负的文本片段(比如反向排版文字),导致split_on_glyphs方法创建Rectangle时触发断言校验。
  2. 现有代码问题:循环中逐行调用SimpleFindReplace.sub会重复扫描整个文档,且直接用提取的整行文本作为匹配模式,容易命中包含异常排版的文本片段。
  3. 修复建议:
    • 升级Borb到最新版本:后续版本可能修复了这类边界情况的处理逻辑。
    • 避免重复替换:一次性处理所有目标字体文本,而非循环逐行调用sub,减少异常触发概率。
    • 自定义Listener处理:直接在文本渲染阶段针对目标字体替换,绕过SimpleFindReplace的全局匹配逻辑。

优化代码示例:

from borb.pdf import Document, PDF
from borb.pdf.canvas.event.chunk_of_text_render_event import ChunkOfTextRenderEvent
from borb.pdf.canvas.font.simple_font.font_type_1 import FontType1
from borb.toolkit import FontNameFilter

class FontReplacementListener(FontNameFilter):
    def __init__(self, target_font_name: str, replacement_font: FontType1):
        super().__init__(target_font_name)
        self._replacement_font = replacement_font

    def _event_occurred(self, event: ChunkOfTextRenderEvent):
        # 替换目标字体
        event._font = self._replacement_font
        super()._event_occurred(event)

def main():
    # 初始化替换字体
    replacement_font = FontType1("Helvetica")
    # 创建自定义Listener
    listener = FontReplacementListener("VFMBOX+HelveticaNeueLTStd-Roman", replacement_font)
    
    # 读取并处理文档
    doc = None
    with open("C:\Data\PDFFontChange\PD-1239-G_RevA_PR.pdf", "rb") as in_file_handle:
        doc = PDF.loads(in_file_handle, [listener])
    
    # 保存结果
    with open("output.pdf", "wb") as pdf_file_handle:
        PDF.dumps(pdf_file_handle, doc)

if __name__ == "__main__":
    main()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 04:02:13