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

使用PyPDF处理PDF时,如何将特殊编码文本转为可读格式?

解决方案:将PDF中十六进制字形编码转为可读文本

问题根源

你看到的<0004>这类十六进制值是PDF字体的字形索引(GID),不是通用字符编码(如UTF-8),它和字体内置的字符映射表(ToUnicode CMap/标准编码)直接绑定,所以单纯用latin-1/UTF-8解码无效。


方案1:用PyMuPDF(fitz)直接提取可读文本

PyMuPDF会自动处理字体的字符映射,即使PDF没有ToUnicode表,也会尝试通过字形名或标准编码映射到Unicode字符,提取结果可直接传入你的__process_raw_data函数:

import fitz

def extract_processable_text(pdf_path):
    doc = fitz.open(pdf_path)
    raw_text = []
    for page in doc:
        # 按文本块提取,保留基础排版结构
        text_dict = page.get_text("dict")
        for block in text_dict["blocks"]:
            if block["type"] == 0:
                for line in block["lines"]:
                    line_text = "".join([span["text"] for span in line["spans"]])
                    raw_text.append(line_text)
    doc.close()
    return "\n".join(raw_text)

# 使用示例
readable_text = extract_processable_text("source.pdf")
processed_text = __process_raw_data(readable_text)

方案2:手动解码字形索引(针对PyPDF操作场景)

如果必须用PyPDF直接处理原始PDF内容,可通过字体的映射表将十六进制字形转为Unicode:

from pypdf import PdfReader
from pypdf.generic import NameObject
from pypdf._encodings import WINANSI_ENCODING, MACROMAN_ENCODING
from pypdf._cmap import CMap

def get_font_char_map(font):
    # 优先获取ToUnicode映射表
    if "/ToUnicode" in font:
        cmap_stream = font["/ToUnicode"].get_object()
        return CMap.from_stream(cmap_stream)
    # 无ToUnicode时用标准编码映射
    encoding = font.get("/Encoding", NameObject("/WinAnsiEncoding"))
    if encoding == NameObject("/WinAnsiEncoding"):
        return {v: k for k, v in WINANSI_ENCODING.items()}
    elif encoding == NameObject("/MacRomanEncoding"):
        return {v: k for k, v in MACROMAN_ENCODING.items()}
    return None

def decode_glyph_hex(hex_str, char_map):
    # 解析<0004>格式的字形编码
    glyphs = [hex_str[i:i+4] for i in range(1, len(hex_str)-1, 4)]
    chars = []
    for g in glyphs:
        gid = int(g, 16)
        chars.append(char_map.get(gid, f"<{g}>"))
    return "".join(chars)

# 使用示例
reader = PdfReader("source.pdf")
page = reader.pages[0]
# 获取页面字体及映射
font_res = page["/Resources"]["/Font"]
for font_name in font_res:
    font = font_res[font_name].get_object()
    char_map = get_font_char_map(font)
    if char_map:
        # 假设你从页面内容中拿到了<0004>这类字符串
        raw_glyph = "<0074006800690073>"  # 对应"this"的字形编码
        readable = decode_glyph_hex(raw_glyph, char_map)
        processed = __process_raw_data(readable)

方案3:替换字体并保留格式

如果接受替换字体,用pikepdf替换时可最大程度保留原有排版、图片:

import pikepdf

def replace_pdf_font(source_path, output_path, font_path):
    with pikepdf.open(source_path) as doc:
        # 加载目标字体(建议用覆盖字形全的字体,如Noto Sans)
        new_font = pikepdf.Font.open(font_path)
        # 遍历所有页面替换字体
        for page in doc.pages:
            if "/Font" in page.Resources:
                fonts = page.Resources["/Font"]
                for font_key in fonts:
                    fonts[font_key] = new_font
        doc.save(output_path)

# 使用示例:替换为系统中的Noto Sans字体
replace_pdf_font("source.pdf", "processed.pdf", "/usr/share/fonts/truetype/noto/NotoSans-Regular.ttf")

注意:替换字体时需确保新字体包含原PDF中所有字形,否则会出现方块替代字符。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 17:50:32