使用PyPDF2解析PDF时,如何解码Tj前的十六进制字符串?
解决PDF中TJ操作符内十六进制字符串的解码问题
问题说明
用PyPDF2解析多数PDF内容正常,但部分PDF解析后会输出包含PDF绘图指令的原始内容,比如:
Decoded Data: ... BT 310.00 621.52 TD /F1 12 Tf [<0030004B004E004B005000460003003000430046004A0057004D00430054000300270047005100540047>] TJ ET ...
其中[]内的十六进制字符串无法通过普通字符编码解码,需要借助PDF的字体映射关系处理。当前使用的解析代码如下:
### get the content pdf = PdfFileReader(self.in_file) for page_number in range(0, pdf.getNumPages()): page = pdf.getPage(page_number) contents = page.getContents() ### adjust the contents... data = contents.get_data() encoding = chardet.detect(data)['encoding'] decoded_data = data.decode(encoding) print(f'Decoded Data: {decoded_data}')
解码步骤
这种十六进制字符串是PDF字体编码的产物,需要通过字体字典的映射关系转成可读文本,具体操作如下:
- 获取对应字体对象
从页面资源中提取字体字典,找到绘图指令中指定的字体(比如示例中的/F1):
resources = page['/Resources'] font_dict = resources['/Font'] target_font = font_dict['/F1'] # 匹配TJ指令前的/F1字体标识
- 提取ToUnicode映射表
PDF字体通常会携带/ToUnicode流,用于将字体编码映射为Unicode字符:
from PyPDF2.utils import CMapParser to_unicode_stream = target_font.get('/ToUnicode') if to_unicode_stream: cmap_content = to_unicode_stream.get_data().decode('utf-8') # 解析CMap映射表 char_map = CMapParser.parse(cmap_content)
- 解码十六进制字符串
把TJ内的十六进制字符串按编码单元拆分,通过映射表转成文本:
import re # 从解析内容中提取TJ内的十六进制内容 match_result = re.search(r'\[<([0-9A-Fa-f]+)>] TJ', decoded_data) if match_result: hex_content = match_result.group(1) # 示例为双字节编码,按每4位拆分一个编码单元 code_units = [hex_content[i:i+4] for i in range(0, len(hex_content), 4)] result_text = '' for unit in code_units: code = int(unit, 16) # 从映射表中获取对应字符,无映射则留空 result_text += char_map.get(code, '') print(f'解码结果:{result_text}')
- 处理无ToUnicode的情况
如果字体没有/ToUnicode,可以通过/BaseFont和/Encoding推导映射,比如使用PyPDF2内置的编码表:
from PyPDF2.constants import Encoding base_font = target_font.get('/BaseFont') encoding_type = target_font.get('/Encoding', '/WinAnsiEncoding') if encoding_type in Encoding: # 使用内置编码映射表处理 fallback_map = Encoding[encoding_type] # 同样拆分编码单元并映射
注意点
- 不同PDF的字体编码可能是单字节或多字节,需根据实际情况调整编码单元的拆分长度
- 部分加密或特殊嵌入字体可能需要额外处理字体的嵌入数据才能完成解码
内容的提问来源于stack exchange,提问作者Milind Deore
相关产品推荐
相关产品推荐

