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

如何用Python实现类Bionic Reading的PDF文本加粗并保留原格式与图片?

实现PDF仿生阅读(Bionic Reading)效果:保留格式与图表的可行方案

针对你需要给PDF单词前几个字母加粗、同时完全保留原格式和图表的需求,pypdf和ReportLab确实无法满足——前者仅能提取/合并文本,无法精细化编辑页面元素;后者需要从头生成PDF,无法复用原文档的布局、字体和非文本元素。下面是基于**PyMuPDF(fitz)**的解决方案,它能直接操作PDF页面的文本块,完美保留原有格式、图片和图表。

核心思路

PyMuPDF允许直接访问PDF的页面对象,读取文本块的字体、大小、位置等属性,修改文本内容后再写回原页面,非文本元素(图片、表格、矢量图形)完全不受影响,适合处理科研论文这类对格式要求极高的文档。

步骤与代码实现

  1. 安装依赖

    pip install pymupdf
    
  2. 核心处理代码

    import fitz  # PyMuPDF
    
    def apply_bionic_reading(input_pdf, output_pdf, bold_char_count=2):
        # 打开原PDF
        doc = fitz.open(input_pdf)
        
        for page in doc:
            # 获取页面的结构化文本数据(包含字体、位置等信息)
            text_dict = page.get_text("dict")
            
            for block in text_dict["blocks"]:
                # 仅处理纯文本块(跳过图片、表格等非文本元素)
                if block["type"] != 0:
                    continue
                
                for line in block["lines"]:
                    processed_spans = []
                    for span in line["spans"]:
                        original_text = span["text"]
                        original_font = span["font"]
                        font_size = span["size"]
                        text_color = span["color"]
                        
                        # 尝试获取对应粗体字体(优先用原字体的粗体变体)
                        bold_font = original_font.replace("Regular", "Bold") if "Regular" in original_font else f"{original_font}-Bold"
                        # 若粗体字体不存在, fallback 到原字体并启用粗体标志
                        if not doc.has_font(bold_font):
                            bold_font = original_font
                        
                        # 分割单词并处理每个单词的加粗部分
                        words = original_text.split()
                        segments = []
                        for word in words:
                            if len(word) <= bold_char_count:
                                # 短单词直接全加粗
                                segments.append((word, bold_font, True))
                            else:
                                # 前N个字符加粗,剩余部分保留原格式
                                segments.append((word[:bold_char_count], bold_font, True))
                                segments.append((word[bold_char_count:], original_font, False))
                            segments.append((" ", original_font, False))  # 还原单词间的空格
                        
                        # 合并同字体的连续文本段,减少不必要的span拆分
                        current_font = None
                        current_text = ""
                        current_is_bold = False
                        for seg_text, seg_font, seg_bold in segments:
                            if seg_font == current_font and seg_bold == current_is_bold:
                                current_text += seg_text
                            else:
                                if current_text:
                                    processed_spans.append({
                                        "text": current_text,
                                        "font": current_font,
                                        "size": font_size,
                                        "color": text_color,
                                        "flags": 1 if current_is_bold else 0  # 1代表粗体标志
                                    })
                                current_font = seg_font
                                current_text = seg_text
                                current_is_bold = seg_bold
                        # 添加最后一段文本
                        if current_text:
                            processed_spans.append({
                                "text": current_text,
                                "font": current_font,
                                "size": font_size,
                                "color": text_color,
                                "flags": 1 if current_is_bold else 0
                            })
                    
                    # 替换原行的span数据
                    line["spans"] = processed_spans
            
            # 将修改后的文本数据写回页面
            page.set_text("dict", text_dict)
        
        # 保存处理后的PDF
        doc.save(output_pdf)
        doc.close()
    
    # 使用示例:处理input.pdf,生成带仿生阅读效果的output.pdf,每个单词前2个字母加粗
    apply_bionic_reading("input.pdf", "output_bionic.pdf", bold_char_count=2)
    

关键注意事项

  • 字体兼容性:部分PDF使用的字体可能没有官方粗体变体,代码中通过启用flags=1让阅读器强制显示粗体,大部分主流阅读器(Adobe Acrobat、Chrome、Foxit)都支持此设置。
  • 格式保留:所有文本的位置、行距、段落缩进、字体大小完全与原文档一致,图片、表格、公式等非文本元素不会被修改或删除。
  • 科研论文适配:对于公式(通常为矢量图形),PyMuPDF不会识别为文本块,因此不会被修改,不影响公式的阅读。
  • 性能优化:仅修改文本块内容,无需重新渲染整个页面,处理大篇幅科研论文的速度远快于从头生成PDF的方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 18:53:14