【指鹿为马】模糊处理 PDF 文本:使用构建的 CMap 对复制的文本进行加扰

查看 11|回复 0
作者:icer233   
前言
刷视频的时候看到了关于CMap映射的内容, 挺感兴趣的, 说不定可以做一些有趣的事, 就自己研究了一下
什么是 CMap
在嵌入字体的 PDF 文件中,这些字形存储在表格中,通常遵循源字体中的编码。考虑到减小文件大小,某些文件仅使用字体的子集。例如,要存储短语 , 使用的字体,只需 T、H、E、C、A、S、O、N、M 就足够了。THE CAT SAT ON THE MAT
但并非每种情况,字形和码位都有 1 对 1 的关系。尤其是当连字开始发挥作用时。(是的,我知道 fl 有它自己的码位 ,但可以肯定的是,每当你想搜索 “fly” 或 “flight” 等单词时,你永远不会想输入它。此外,有时字形的位置并不像其码位那样位于正确的位置,而只是 0、1、2、...因此,为了精确识别每个字符形,我们需要一个 map 来链接字符形和它所代表的码位。
以下是 CMap 的一个示例:
/CIDInit /ProcSet findresource begin 12 dict begin begincmap /CIDSystemInfo > def
/CMapName /F6+0 def
/CMapType 2 def
1 begincodespacerange   endcodespacerange
19 beginbfchar



















endbfchar
8 beginbfrange
  
  
  
  
  
  
  
  
endbfrange
6 beginbfrange
  []
  []
  []
  []
  []
  []
endbfrange
endcmap CMapName currentdict /CMap defineresource pop end end
CMap 中有 2 个主要部分,和 .它们中的每一个都以一个数字开头,表示它拥有的条目数。它开始beginbfchar, beginbfrange
bfchar
这通常用于一对一的关系。第一个代码是字形 ID,第二个代码表示 Unicode 代码点。例如,表示字形 07 映射到码位 0035。
bfrange
这按顺序用于批量映射:将字形表中的范围映射到 Unicode 码位。例如:表示:  
  • 映射 31 至 00F2
  • 映射 32 至 00F3
  • 映射 33 至 00F4
  • 映射 39 到 00FA

    此外,还可以将字形分配给多个 Unicode 字符,这在处理连字时很有用。 表示将字形 02 映射到序列 (0066, 006c)。bfrange``  []
    如何获取并更改CMap?
    使用开源软件QPDF就可以了
    执行命令:
    qpdf --qdf --object-streams=disable document.pdf decompressed.pdf
    使用二进制/十六进制编辑器打开decompressed.pdf, 比如记事本就可以
    找到目标字体的CMap的Unicode码并更改
    保存后使用命令:
    qpdf --compress-streams=y decompressed.pdf output.pdf
    重新压缩pdf
    举例:
    以下是一个我用Typora生成的PDF, 只有一个字"鹿"
    [ol]
  • 用第一条命令得到decompressed.pdf
  • 同过工具查到"鹿"和"马"的Unicode分别为U+9E7F和U+9A6C
  • 在PDF中搜索并将其替换为, 并保存
  • 用第二条命令重新压缩PDF
  • 成功了! 看起来是"鹿", 复制出来是"马"
    [/ol]
    一个不完善的程序
    如果QPDF没有在电脑PATH里, 需要把qpdf.exe复制到程序根目录
    # -*- coding: utf-8 -*-
    import os
    # 检测输入
    if not os.path.exists('./input.pdf'):
        print('请将PDF文件复制到根目录')
        exit()
    # 解析pdf
    os.system('qpdf --qdf --object-streams=disable input.pdf decompressed.txt')
    def chinese_to_unicode(text):
        return ''.join([f'{ord(c):04x}' for c in text])
    ori_text = input('请输入要替换的中文字符:')
    rep_text = input('替换为:')
    ori_uni = chinese_to_unicode(ori_text).upper()
    rep_uni = chinese_to_unicode(rep_text).upper()
    print('\n原文本:'+'\n替换为:')
    print('\n请在decompressed.txt文件中手动完成替换并保存!')
    os.system('pause')
    os.system('qpdf --compress-streams=y decompressed.txt output.pdf')
    os.system('del decompressed.txt')
    程序的问题在于只能改一个字以及必须手动完成替换.
    第一个问题很好解决, 第二个问题在于解密出来的PDF强制转为TXT并且使用普通的Python读写是不行的, 毕竟是强制转换, 解决方案是当作二进制文件读写, 但是这样一来替换起来有些麻烦, 就懒得写了, 有兴趣的读者可以写写看
    后记
    这虽然能一定程度上防止他人复制你的PDF内容, 但或许只能恶心一下别人罢了, 毕竟现在的OCR技术已经很成熟了
    本文关于到底如何修改CMap的步骤不是很完善, 只是一个简单的替换, 网上关于这些的资料比较少, 有了解的可以分享一下更具体的方法
    参考
    混淆 PDF 文本:使用精心制作的 CMap 对复制的文本进行加扰 — 1A23 Blog
    PDF转Word,技术壁垒有多厚? #知识科普 #干货分享 - 抖音 (douyin.com)
    QDF Mode — QPDF 11.9.1 documentation

    字形, 文本

  • 您需要登录后才可以回帖 登录 | 立即注册