にexeをバイナリから編集する方法を説明しました。

じゃあ今度はPDFをバイナリから編集できないかと思い、調べてここにまとめようと考えました。

調べると同じような記事がなんこか見つかりました。

まあ、でも、実際に自分でやってみないとわからないことってあると思うので、ここに自分がやってみるとどうだったかを残しておきます。

てかexeのバイナリ編集も似たような記事たくさんあるしね。


はじめに

じゃあさっそくPDFをバイナリから見ていこうと思ったのですが、正直

PDFが読めるようになるお話

を読めばPDFからテキストファイルの場所がわかります。

そのためその部分のバイナリを変えればテキストを編集できます。

しかし、ここのサイトのにあるPDFのサンプルの文字を書き換えてみると

こんな感じになります。

これはHをeに変え、WをXに書き換えたものです。

するとXだけ文字化けしました。

この理由はフォントが使用されている文字しか埋め込まれていなかったためです。

そのためにXに対応するフォントが存在せず、こうなってしまったということです。

しかしXの文字は存在しているため、コピーするとXorldが得られます。

普通にwordからPDFを作るときは基本文字は全て埋め込まれます。

使用した文字だけ埋め込むのは

オプション>保存>次の文書を共有するときに再現性を保つ>ファイルにフォントを埋め込む>文書で使用されている文字だけを埋め込む

にチェックを入れればできます。

このPDFがどう作られたかはわかりませんが、おそらくそんな感じの設定で作られたんだと思います。


PDFを編集する

一応PDFに存在するテキストを編集する方法をまとめると


1.PDFtkというフリーソフトで

PDFファイル output 保存先PDFファイル uncompress

というコマンドでPDFの圧縮された部分を解凍しておく。


2.PDFの一番下の部分をバイナリで見る。

すると

startxref アドレス

と書かれているため、そのアドレスに飛ぶとxrefというオブジェクトのアドレス表にたどり着く。

PDFはこのxrefというオブジェクトを指し示す表と、オブジェクトというデータの単位が集まって構成されている。

xrefは

アドレス 世代番号 n

というように書かれており例として上から3行だけ見ると

0000000000 65535 f

0000000016 00000 n

0000000268 00000 n

こんな感じのが何列もある。

上から0,1,2番目のオブジェクトのアドレスを指し示している。

0番目のオブジェクトは使わないっぽい。

とりあえず1番目のオブジェクトの場所を見てみると

/Type Catalog

のオブジェクトがあります。

そこに /Pages 3 0 R と書かれているため3番目のオブジェクトをxrefを参考に見る。

(何番目に何のオブジェクトがあるかはPDFによって変化します。)

xrefを参考にしなくてもオブジェクトは 3 0 obj~endobj のように書かれるため、

3 0 obj を検索すれば見つかります。

こんな感じで

Catalog>Pages>Page>Contents まで辿ってみてみると

stream~endstreamで囲まれた中にBT~ETに囲まれた部分がある。

この中に <文字列>Tj という部分があるからバイナリからこの文字列を編集できる。


フォントをいじってみる

もうテキストは編集できるようになりました。

しかしテキストを編集してもうまく表示されないこともあり、見た目はフォントの方で決まることがわかります。

ならばフォントの方を変えれば見た目だけ変えられるのではないかと考えました


やっぱりさっきのサイトみたいにアドレスを辿ってみるのをやりたいのでやってみます。

先ほどのサイトのPDFサンプルをPDFtkでuncompressしたものを見ていきます。


これからオブジェクトを辿っていきますが、ここに何番目に何のオブジェクトがあったかまとめておきます。

1    Catalog
2    DestOutputProfile
3    Pages
4    Metadata
5    Kids
6    GS0 //ExtGState
7    C0_0 //Font
8    Contents //テキストとか
9    DescendantFonts //10へのアドレス
10   Font
11   CIDSystemInfo
12   FontDescriptor
13   FontFile3
14   CIDSet
15


まずPDFの最後を見ると

startxref 607246

とあるためxrefをみると(xrefは見なくてもいいけど一応)

xref

xref
0 16
0000000000 65535 f 
0000000015 00000 n 1 0 obj
0000047056 00000 n 2 0 obj
0000046997 00000 n 3 0 obj

こんな感じであるため1 0 objを見ると

Catalog

1 0 obj 
<<
/OutputIntents [
<<
/OutputCondition ()
/Info (Japan Color 2001 Coated)
/DestOutputProfile 2 0 R
/OutputConditionIdentifier (JC200103)
/RegistryName (http://www.color.org)
/Type /OutputIntent
/S /GTS_PDFX
>>]
/Pages 3 0 R
/Metadata 4 0 R
/Type /Catalog
>>
endobj

今度は/Pagesを見てみると

Pages

3 0 obj 
<<
/Kids [5 0 R]
/Type /Pages
/Count 1
>>
endobj

Kidsを見ると

Kids

5 0 obj 
<<
/pdftk_PageNum 1
/TrimBox [0.0 0.0 595.276 841.89]
/CropBox [0.0 0.0 595.276 841.89]
/MediaBox [0.0 0.0 595.276 841.89]
/Resources 
<<
/ExtGState 
<<
/GS0 6 0 R
>>
/Font 
<<
/C0_0 7 0 R
>>
/ProcSet [/PDF /Text]
>>
/Parent 3 0 R
/Contents 8 0 R
/LastModified (D:20200119182202+09'00')
/BleedBox [0.0 0.0 595.276 841.89]
/Type /Page
>>
endobj

リソースの中にFontという項目がありました。

これがフォント関係のオブジェクトっぽいので7 0 objを見ていきます。

Font (Subtype Type0)

7 0 obj 
<<
/DescendantFonts 9 0 R
/BaseFont /VUSVFG+KozGoPr6N-Regular
/Subtype /Type0
/Type /Font
/Encoding /Identity-H
>>
endobj

KozGoPr6N-Regularというフォント名っぽいものがありましたね。

DescendantFontsというまたフォントの情報が入ってそうなオブジェクトがあるので見てみます。

DescendantFonts

9 0 obj [10 0 R]
endobj

なんか10 0 objを見ろと書いてありますね。

見てみます。

Font (Subtype CIDFontType0)

10 0 obj 
<<
/BaseFont /VUSVFG+KozGoPr6N-Regular
/Subtype /CIDFontType0
/DW 1000
/CIDSystemInfo 11 0 R
/FontDescriptor 12 0 R
/W [1 [233] 41 [700] 56 [919] 69 [604 539] 77 [248] 80 [589] 83 [346]]
/Type /Font
>>
endobj

またフォント名がありますね。

あとは/Wで文字の幅とかを表しているらしいです。

今度はCIDSystemInfoとFontDescriptorというものを参照しています。

PDFのFormatをみてみると、

CIDSystemInfo:CIDFontの文字コレクションを定義するエントリを含む辞書

FontDescriptor:CIDFontのグリフ幅以外のデフォルトメトリクスを記述するフォント記述子

らしいです。あんまよくわかんないのでとりあえず見てみます。


CIDSystemInfo

11 0 obj 
<<
/Supplement 6
/Registry (Adobe)
/Ordering (Japan1)
>>
endobj

Adobe関係の情報が書かれている?


FontDescriptor

12 0 obj 
<<
/FontName /VUSVFG+KozGoPr6N-Regular
/StemV 84
/FontFile3 13 0 R
/Ascent 1409
/Flags 4
/FontWeight 400
/XHeight 545
/FontFamily (Kozuka Gothic Pr6N R)
/FontStretch /Normal
/Descent -339
/ItalicAngle 0
/CIDSet 14 0 R
/FontBBox [-529 -339 1214 1409]
/Type /FontDescriptor
/CapHeight 763
>>
endobj

フォントに関する情報がなんかあります。

FontFile3:ここにフォントデータがあるそうです。

CIDSet:CIDを識別するストリーム。これが文字コードとグリフの対応表?

見ていきます。


FontFile

ストリームの中にフォント名、コピーライト、/FSTypeなどがありました。

埋め込みフォントのフォーマットがってどっかにあるんですかね。

このバイナリが読めないです。


CIDSet

これが文字コードとグリフとの対応表だと思うんですよね。

ストリームをひとつずつ見てみると
C0 00 00 00 00 40 00 80 06 04 90

いやーわからん。

Embedded OpenType (EOT) File Format

Embedded OpenType From Wikipedia

How can I extract embedded fonts from a PDF as valid font files? stack overflow

ここら辺になんかヒントがありそうなんですけどね。


やりたかったことは、文字コードとグリフIDの一部を入れ替えて、ContentにはHello Worldと書いてあるが、PDFではWello Horldと表示される、みたいなことです。

なんか面白いかなーって思ったんですがねー。

分かる人いたら教えて下さい。。




参考

プログラマーから見たPDFファイル

見て作って学ぶ、PDFファイルの基本構造

PDFが読めるようになるお話

PDF 構文 ファイル 解析手順

詳細PDF

PDFの解説

PDFから「使える」テキストを取り出す

僕「PDFとは何か知りたい」