如何从含自定义字体的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
相关产品推荐
相关产品推荐

