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

如何从含自定义字体的PDF中直接提取符号与字体的映射编码?

自动提取PDF中(CID+字体)到特殊符号的映射方案(用pdfplumber)

针对你用pdfplumber提取带特殊科学符号的大PDF时遇到的CID编码、符号识别错误问题,完全可以自动提取(cid:X)+字体名称到对应符号的映射,不用手动核对每一对,核心思路是利用PDF自带的字符映射机制,结合pdfplumber的底层数据:

具体实现步骤

1. 直接从pdfplumber字符数据中批量收集映射

pdfplumber在解析页面字符时,已经会尝试通过PDF的ToUnicode映射表把CID转成对应的Unicode字符,你可以直接遍历所有页面的字符,提取cid、fontname和对应的text,自动生成映射字典:

import pdfplumber

def build_cid_font_mapping(pdf_path):
    cid_font_map = {}
    with pdfplumber.open(pdf_path) as pdf:
        # 遍历所有页面
        for page in pdf.pages:
            # 遍历页面中的每个字符对象
            for char in page.chars:
                cid_key = f"(cid:{char['cid']})"
                font_key = char["fontname"]
                symbol = char["text"]
                # 只记录未存在的映射(避免重复覆盖)
                if symbol and (cid_key, font_key) not in cid_font_map:
                    cid_font_map[(cid_key, font_key)] = symbol
    return cid_font_map

# 使用示例
mapping = build_cid_font_mapping("your_large_pdf.pdf")
# 打印示例映射
for (cid, font), symbol in mapping.items():
    print(f"{(cid, font)} -> {symbol}")

这个方法直接利用pdfplumber已经解析好的结果,不需要自己解析复杂的PDF字体字典,适合大部分规范的PDF,能自动收集像('(cid:8)', 'EJDEHH+6Brane') -> ḁ̃这类映射。

2. 手动解析PDF的ToUnicode映射表(应对特殊情况)

如果某些PDF没有嵌入ToUnicode表,导致pdfplumber无法直接解析出符号,你可以手动提取字体的CMap映射:

from pdfminer.pdftypes import PDFStream

def parse_tounicode_cmap(cmap_content):
    cid_to_unicode = {}
    lines = cmap_content.splitlines()
    in_cid_block = False
    for line in lines:
        line = line.strip()
        if line == "begincidchar":
            in_cid_block = True
        elif line == "endcidchar":
            in_cid_block = False
        elif in_cid_block and len(line.split()) == 2:
            cid_str, unicode_hex = line.split()
            cid = int(cid_str)
            # 把十六进制Unicode转成字符
            char = chr(int(unicode_hex.strip("<>"), 16))
            cid_to_unicode[f"(cid:{cid})"] = char
    return cid_to_unicode

def get_font_cmap(pdf, target_font):
    # 遍历页面资源中的字体
    for page in pdf.pages:
        page_resources = page.doc.catalog["Pages"]["Kids"][page.page_number-1]["Resources"]
        if "Font" not in page_resources:
            continue
        for _, font_obj in page_resources["Font"].items():
            if font_obj.get("BaseFont") == target_font:
                if "ToUnicode" in font_obj:
                    # 提取ToUnicode流内容
                    stream = PDFStream(font_obj["ToUnicode"])
                    cmap_content = stream.get_data().decode()
                    return parse_tounicode_cmap(cmap_content)
    return None

# 使用示例
with pdfplumber.open("your_large_pdf.pdf") as pdf:
    font_cmap = get_font_cmap(pdf, "EJDEHH+6Brane")
    print(font_cmap.get("(cid:8)"))  # 输出 ḁ̃

关键注意点

  • 同一CID对应不同符号的问题:必须用(cid, fontname)的组合作为映射的key,因为不同字体的CID编码是独立的,上面的两种方法都天然处理了这个情况。
  • 大体积PDF优化:可以分批次处理页面,比如每次处理10页,避免内存占用过高。
  • 极端情况:如果PDF既没有ToUnicode表,也没有嵌入字体的编码信息,那只能结合OCR工具识别字符,但这种情况很少见。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 08:08:28