<p>一般来说,我认为pdf文本可以通过不同的算法进行压缩/编码,因此<code>pdfrw</code>不会自行解码文本。所以一般来说,你不知道什么是正确的方法,因为每种情况都不同。我试过简单的pdf <a href="http://www.africau.edu/images/default/sample.pdf" rel="nofollow noreferrer">from here</a>,里面只包含纯文本</p>
<p>可能您没有弄清楚字符和十六进制代码之间的正确对应关系是因为它可能是一个压缩流-这意味着每个代码取决于字符在整个流中的位置加上之前所有字符的值。例如,文本可以是<code>zlib</code>压缩的</p>
<p>另外,pdf文本是一系列用于定位/格式化/输出文本的命令,因此一般来说,您必须能够对所有这些命令进行解码/编码,才能真正处理任何文本。您的格式可能包含符号表,其中所有使用的符号都映射到十六进制值。为了找出正确的映射,所有符号都应该出现在示例文本中</p>
<p>对于您的情况,您可能会使用下一个表,对于转换,我使用字母<code>R</code>具有十六进制值<code>0x34</code>的事实:</p>
<p><a href="https://tio.run/##bc1NCoMwGIThvacYhJJ81Ip/CxG8RNfdSI01pSaSRKqIZ09Du@2sH@adNzdqVXovp1kbB7vZaNAGMoGCVBBqmYTpnOCmUw/ByyJBXtRETYSwwFPrer249G1kUEO8j2LlChdo03N2ZYQzsrWsKH09F@t4RUcDtt9HwxUdDDF9n@QALgPNCSfUaFtkv8TfDLspRpH3Hw" rel="nofollow noreferrer" title="Python 3 – Try It Online">Try it online!</a></p>
<pre><code>import sys
for i, n in enumerate(range(32, 128)):
sys.stdout.write(f"{hex(n - ord('R') + 0x34).ljust(4)}: '{chr(n)}' ")
if (i + 1) % 8 == 0:
sys.stdout.write('\n')
</code></pre>
<p>输出:</p>
<pre><code>0x2 : ' ' 0x3 : '!' 0x4 : '"' 0x5 : '#' 0x6 : '$' 0x7 : '%' 0x8 : '&' 0x9 : '''
0xa : '(' 0xb : ')' 0xc : '*' 0xd : '+' 0xe : ',' 0xf : '-' 0x10: '.' 0x11: '/'
0x12: '0' 0x13: '1' 0x14: '2' 0x15: '3' 0x16: '4' 0x17: '5' 0x18: '6' 0x19: '7'
0x1a: '8' 0x1b: '9' 0x1c: ':' 0x1d: ';' 0x1e: '<' 0x1f: '=' 0x20: '>' 0x21: '?'
0x22: '@' 0x23: 'A' 0x24: 'B' 0x25: 'C' 0x26: 'D' 0x27: 'E' 0x28: 'F' 0x29: 'G'
0x2a: 'H' 0x2b: 'I' 0x2c: 'J' 0x2d: 'K' 0x2e: 'L' 0x2f: 'M' 0x30: 'N' 0x31: 'O'
0x32: 'P' 0x33: 'Q' 0x34: 'R' 0x35: 'S' 0x36: 'T' 0x37: 'U' 0x38: 'V' 0x39: 'W'
0x3a: 'X' 0x3b: 'Y' 0x3c: 'Z' 0x3d: '[' 0x3e: '\' 0x3f: ']' 0x40: '^' 0x41: '_'
0x42: '`' 0x43: 'a' 0x44: 'b' 0x45: 'c' 0x46: 'd' 0x47: 'e' 0x48: 'f' 0x49: 'g'
0x4a: 'h' 0x4b: 'i' 0x4c: 'j' 0x4d: 'k' 0x4e: 'l' 0x4f: 'm' 0x50: 'n' 0x51: 'o'
0x52: 'p' 0x53: 'q' 0x54: 'r' 0x55: 's' 0x56: 't' 0x57: 'u' 0x58: 'v' 0x59: 'w'
0x5a: 'x' 0x5b: 'y' 0x5c: 'z' 0x5d: '{' 0x5e: '|' 0x5f: '}' 0x60: '~' 0x61: ''
</code></pre>
<p>从十六进制到字符的转换代码很简单:</p>
<pre><code>hex_val = '0030'
print(chr(int(hex_val, 16) - 0x34 + ord('R')))
</code></pre>
<p>如果您在字符和十六进制值之间有一些更奇特的映射,那么您只需创建一个包含所有可能字符的文本,然后使用转换器进行转换,查看每个字母的十六进制值</p>
<p>另外,我刚刚试图弄清楚PDF中的文本是如何编码的,使用了哪些命令,它看起来像是字符串,末尾的<code>Tj</code>命令包含文本本身。因此,我在下面的代码中编写了pdf文本修饰符,它接受文件名或URL作为第一个参数,输出文件名作为第二个参数,或者只是运行它以使用默认示例,所需的替换在脚本开头作为<code>changes</code>变量列出</p>
<p>但下一个修饰符不会解码十六进制格式。它只是方便地替换任何纯文本编码</p>
<p><a href="https://repl.it/@moytrage/StackOverflow64075158#main.py" rel="nofollow noreferrer">Try it online!</a></p>
<pre><code>import sys, os, io
# Needs: python -m pip install pdfrw
from pdfrw import PdfReader, PdfWriter
changes = {'And': 'Or', 'text': 'string'}
def ReplaceText(text, reps = {}):
res, in_block = '', False
for line in text.splitlines():
line = line.strip()
nline = line
if line == 'BT':
in_block = True
elif line == 'ET':
in_block = False
elif in_block:
cmd = line.rpartition(' ')[2]
if cmd.lower() == 'tj':
for k, v in reps.items():
nline = nline.replace(k, v)
res += nline + '\n'
return res
ifn = sys.argv[1] if len(sys.argv) > 1 else 'http://www.africau.edu/images/default/sample.pdf'
ofn = (ifn[:ifn.rfind('.')] + '.processed.pdf') if len(sys.argv) <= 2 else sys.argv[2]
if ifn.lower().startswith('http'):
# Needs: python -m pip install requests
import requests
ofn = (ifn[ifn.rfind('/') + 1:] + '.processed.pdf') if len(sys.argv) <= 2 else sys.argv[2]
ifn = io.BytesIO(requests.get(ifn).content)
r = PdfReader(ifn)
for page in r.pages:
page.Contents.stream = ReplaceText(page.Contents.stream, changes)
PdfWriter(ofn, trailer = r).write()
</code></pre>