PDF の正体と、テキストをコピーできないことがある理由
PDF は描画命令の一覧です。このグリフをこの座標に、このフォントで、この大きさで置け、次はここへ移動して次のグリフを描け、と指示します。これは見出しである、これは段落である、この段はあの段より前に来る、といったことは書かれていません。ビューアーがページの構造について知っているように見えるものはすべて、印どうしの間隔を測って後から再構成された結果です。PDF にまつわる不満のほとんどは、これで説明がつきます。
PDF が実際に保存しているもの
ページ上のテキストはコンテンツストリームの中にあり、短い演算子の並びとして表されます。
BT
/F1 11 Tf
72 708 Td
[(In) -18 (v) 22 (oice)] TJ
180 0 Td
(2026-08-25) Tj
ET
Tf はフォントとサイズを選び、Td はテキストカーソルを移動し、TJ はグリフの間にカーニングのオフセットを挟みながら描画します。ここに何がないかに注目してください。空白文字がどこにもありません。日付の前の空きは 180 単位の移動にすぎず、抽出器はその移動のそれぞれが空白なのか、タブなのか、段の境界なのか、あるいは何でもないのかを判断しなければなりません。しきい値が少しずれるだけで、Invoice2026-08-25 や I n v o i c e が出てきます。
読み順にも同じ問題があります。グリフは描画された順に出てきます。2 段組みのレイアウトでは、左の段をページの下まで描いてから右の段を描くことが多く、その場合はコピーがうまくいきます。生成側が紙面を横切って 1 行ずつ描いていた場合、コピーすると 2 つの段が交互に混ざり、意味をなさなくなります。
テキストの PDF とスキャンを見分ける
スキャンした文書にはテキストがまったく含まれていません。ページの写真を PDF で包んだものです。光学文字認識は画像の上に不可視のテキスト層を追加し、これによってファイルは検索可能になりますが、その層は推測にすぎません。そのため、誤りは検索の失敗や崩れたコピーとして現れます。
| 確認方法 | テキストの PDF | スキャンの PDF |
|---|---|---|
| 単語を選択する | 1 文字ずつハイライトされる | 何も選択できないか、ページ全体が選択される |
| 400% に拡大する | 文字は鮮明なまま | 文字がぼやけるかブロック状になる |
| 1 ページあたりのサイズ | 多くは 20 ~ 100 KB | 多くは 300 KB ~数 MB |
| Extract Images from PDF を実行する | 本来の画像が返るか、何も返らない | 1 ページにつきページ全体の画像が 1 枚返る |
フォント、埋め込み、サブセット化
フォントは埋め込まれているか、名前とメトリクスで参照されているかのどちらかです。埋め込みとは、フォントのプログラムがファイルの中に入っていることを指します。参照の場合、ビューアーは表示するマシン上で似たフォントを探します。文書が作成した場所では正しく見えて、他の場所では崩れるのは、この参照フォントが原因です。代替フォントは文字幅が違うため、行が折り返し直され、表があふれ、1 ページの手紙が 2 ページになります。
埋め込まれたフォントは、ほぼ必ずサブセット化されています。使われているグリフだけが含まれ、しかも番号が振り直されていることが多く、A のグリフがコード 3 に置かれていることもあります。実際の文字に戻すための対応表は別の ToUnicode テーブルにあり、これが欠けていたり誤っていたりすると、ページは完璧に表示されるのに、コピーしたテキストは意味不明になります。文字ではなく内部のグリフコードが渡されるからです。保存し直しても直りません。抜け道は、元の文書に戻るか、描画したページに対して文字認識をかけるかのどちらかです。
ページサイズ、ボックス、回転
座標の単位はポイントで、1 インチあたり 72 ポイントです。各ページは複数の矩形を持っています。MediaBox は用紙全体、CropBox は実際に表示される領域であり、こちらはより小さいこともあります。
| 用紙 | ポイント | ミリメートル |
|---|---|---|
| A4 | 595 x 842 | 210 x 297 |
| Letter | 612 x 792 | 216 x 279 |
| Legal | 612 x 1008 | 216 x 356 |
A4 と Letter は画面上では同じに見えますが、紙の上では違います。ヨーロッパの報告書とアメリカの報告書を結合すると、ページサイズが交互に変わる文書ができあがります。
各ページはさらに、0 度、90 度、180 度、270 度のいずれかの回転フラグを持っています。これにより、横向きで保存された内容を正しい向きで表示できます。Rotate PDF は何も描き直さずにこのフラグを設定するので、処理は瞬時で無劣化ですが、その下にある座標は変わりません。フラグを無視するツールはテキストを横向きに抽出しますし、フラグが混在したページを結合すると、見た目は統一されているのに内容は複数の向きに散らばった文書ができあがります。
結合、分割、そして壊れる部分
ページはページツリーの中にあります。Merge PDF はページオブジェクトとそれが参照するすべてを 1 つのファイルにコピーし、ツリーを組み直します。Split PDF はその枝を 1 本取り出します。ページ自体は無傷で通りますが、文書に付随しているものはそうとは限りません。
- しおり は、ページオブジェクトを指す独立したアウトラインです。多くの結合ツールはこれを丸ごと捨てますし、分割すると、もう存在しないページを指す項目が残ります。
- フォームフィールド の名前は、ページ単位ではなく文書単位で管理されます。そのため、同じフォームの写しを 2 つ結合すると
nameという名前のフィールドが 2 つできます。多くのビューアーはこれを値を共有する 1 つのフィールドとして扱うので、片方に入力すればもう片方にも入ります。先にフラット化するか、名前を変更してください。 - リンクやコメントのような 注釈 はページと一緒に付いてきますが、分割で対象のページが外れた内部リンクは、行き先を失います。
結合後のファイルが元のファイルの合計より大きくなることはよくあります。共有されるはずのリソースが重複するからです。同じフォントのサブセットをそれぞれ埋め込んだ 4 つのファイルからは 4 つのサブセットができ、カラープロファイルもメタデータブロックも 4 つになります。逆に小さくなることもあります。書き直しの過程で未使用のオブジェクトと保存履歴が捨てられるからです。30 回保存された文書は、それ以前のすべてのリビジョンを末尾に抱えています。
パスワード、権限、タグ
パスワードには 2 種類あり、両者は比較できるものではありません。ユーザーパスワード は文書を開くために必要なものです。内容はそこから導かれた鍵で実際に暗号化されているので、それがなければ読むものは何もありません。現代のファイルは AES-256 を使い、非常に古いものは 40 ビットの RC4 を使いますが、後者は脆弱です。
オーナーパスワード は、印刷禁止、コピー禁止、編集禁止といった権限フラグを設定します。ファイルは依然として暗号化されていますが、ユーザーパスワードが空なので、その鍵はどのビューアーでも独力で導けます。これらのフラグは要請にすぎず、尊重するビューアーもあれば無視するビューアーもあります。パスワードを求められずに開くファイルなら、その制限は助言的なものです。プロパティのダイアログに何と書かれていても同じです。
タグ付けは別の層の話です。見出し、リスト、表のセル、図の説明を示し、読み順を確定させる、省略可能な構造ツリーです。タグ付きの PDF ならスクリーンリーダーは文書を読み上げますが、タグのない PDF では座標から推測するので、コピーして貼り付けたときと同じように入り乱れた結果になります。スキャンは定義上タグを持ちませんし、文字認識はテキストを追加しても構造は追加しません。Images to PDF や PDF to Images の出力には、そのどちらもありません。
サイズはどこにあるのか、ブラウザーは何を抱えられるのか
ファイルサイズの正体は、ほぼ必ず画像です。A4 のページに必要な画素数は、150 dpi でおよそ 1240 x 1754、300 dpi で 2480 x 3508 です。したがって 1200 万画素で貼り込まれた写真は、印刷で使える量をはるかに超えるデータを抱えており、文書を確実に小さくできる変更はダウンサンプリングだけです。テキストだけの PDF は保存し直してもほとんど効果がありません。コンテンツストリームはすでに圧縮されているからです。予想外に大きい場合は、埋め込みフォント、保存履歴、添付ファイルを見てください。
これらの処理はすべて手元のブラウザー内で行われるため、何もアップロードされませんし、制約になるのはファイルサイズではなくメモリです。負荷が高いのは描画です。A4 のページを 4 倍のスケールで描画するとおよそ 2380 x 3368 ピクセルになり、1 ページで非圧縮ビットマップおよそ 32 MB に相当します。そのスケールで 50 ページを扱えば、マシンが音を上げるよりずっと前にブラウザーのタブがメモリを使い切ります。非常に大きな文書はまず分割し、それからまとまりごとに処理してください。