2017年7月21日金曜日

PDF のファイル構造を分析/解析するツール

PDFXplorer 1.0.0


辞書の内容を、リスト表示できます(下図参照)


特徴:
  • 各オブジェクト(Bool, Number, String, Name, Array, Dictionary, Stream, Indirect Reference)を認識します。
    • 右側のリストに列挙。表示例:
      • /Filter Name /FlateDecode
      • /MediaBox Array [0 0 256 156 ]
      • /Pages Indirect Reference 3 0 R
    • 左側ツリーにも列挙。表示例:
      • /Filter /FlateDecode
      • /MediaBox 4 elements
      • /Pages 3 0 R
    • アイコン・デザインに気合が入っています。素晴らしい!
  • PDF のデザインを尊重するような表示になっています:
    • Name の /FlateDecode は /FlateDecode と、そのまま表示します。
    • Document Structure を表示しています。つまり /Type と /Pages を表示しています。
  • stream について
    • /Filter /FlateDecode を解除して表示可能
    • Display mode:
      • Binary
      • Text
      • Image (DCT streams only)
    • Save stream to disk でファイルに保存可能
  • 編集はできません。
総評
  • 使いやすい!
  • PDF ファイルの構造を忠実に表現しようとしているように感じました。

pdfbox PDFDebugger


下図参照

起動例:

java -jar pdfbox-app-3.0.0-RC1.jar debug pdfFile

特徴:

  •  各オブジェクトを認識します。
    • 左側のツリーには、オブジェクトの一覧を列挙します。表示例:
      • Interpolate: true
      • Width:  712
      • Ordering:  Japan1
      • Encoding:  Identity-H 
      • MediaBox:  (4)
      • Resources:  (3) [6 0 R]
      • Contents:  (2) [4 0 R]
      • 0:  (5) [2 0 R]  /T:Page
    • 右側のリストには、オブジェクトの内容を表示します。
  • stream について
    • ページ (/T:Page とついているもの) は、ページをレンダリングします。
    • 画像 (/T:XObject /S:Image つき) は、画像をレンダリングします。
    • フォント (/T:Font /S:Type0 つき) は、フォントの字体一覧をレンダリングします。一覧の列:
      • Code
      • CID
      • GID
      • Unicode Character
      • Glyph
 総評:
  • 使いやすい。
  • 一部、オブジェクトの表示機能がついているのが特徴的です。
  • MediaBox など、配列はツリーを展開しないと要素が見えません。

PDF Document Inspector

http://www.cheapimpostor.com/PDFInspector/
macOS アプリ。未評価。

PDFedit

http://pdfedit.cz/en/pdfedit_windows.html

Windows 版はこちらの PDFedit ではなく、TIAEditor という別物になっているようです…

iText RUPS

https://github.com/itext/rups

RUPS → Reads, Updates PDF Syntax?

下図参照:
特徴:
  • PDFXplorer と大体同じです。
  • iText RUPS では編集(辞書オブジェクトの中身の修正程度)ができます。
    • 編集しても保存確認はしません。File → Save as... で自分で保存しましょう。
  • 画面右側にナビゲーションが実装されています。
    • Pages にて、特定 Page へジャンプできます。
    • XRef にて、参照元の間接オブジェクトへジャンプできます。
  • Stream のテキスト表示に、Graphics operators 用のシンタックスハイライトが装備されています。Text objects, Text positioning, XObjects, Path 系, Special graphics state, その他, Graphics operators 以外は黒, のような色分けかなと思いました。


peepdf

https://pypi.org/project/peepdf/0.3.2/
https://github.com/jesparza/peepdf

PDF のファイル内容を、コマンドライン上で、対話式に解析できるツールです。

Windows で peepdf を使うなら、
ネイティブの CPython を使用するよりも、
Cygwin 上の Python 3.8 を使った方が良いでしょう。

peepdf のインストール中にネイティブライブラリのビルドを必要とするようで、
これがどうも解決するのが大変なためです。


2017年3月24日金曜日

PDF GetPageRotation /Rotate 0 90 180 270 正規化する

/Rotate 90 は時計回りに 90 度回転します。


/Rotate を付けてもらうには…
  • Adobe Acrobat 8.0 を使用
  • 複合機でスキャンした PDF を回転

サンプル PDF はこちら

/Rotate の付与状況は PDFXplorer で確認できます。



こうしてできてしまった PDF を正規化するサンプルコード。iTextSharp 4.1.6 を使用しています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public void MakePdf2(string fppdfIn, string fppdfOut) {
 
  using (var si = File.OpenRead(fppdfIn)) {
    PdfReader reader = new PdfReader(si);
    reader.ConsolidateNamedDestinations();
 
    using (var fs = File.Create(fppdfOut)) {
      Document document = null;
      PdfWriter writer = null;
      int numPages = reader.NumberOfPages;
      for (int pageNum = 1; pageNum <= numPages; pageNum++) {
        var pageSizeAfterRotation = reader.GetPageSizeWithRotation(pageNum);
        if (document == null) {
          document = new Document(pageSizeAfterRotation);
          writer = PdfWriter.GetInstance(document, fs);
          document.Open();
        }
        else {
          document.SetPageSize(pageSizeAfterRotation);
          document.NewPage();
        }
 
        PdfTemplate background = writer.GetImportedPage(reader, pageNum);
 
        float[] transferMatrix = new float[6];
        switch (reader.GetPageRotation(pageNum)) {
          case 0:
          default:
            transferMatrix[0] = 1;
            transferMatrix[3] = 1;
            break;
          case 270:
            transferMatrix[0] = (float)Math.Cos((float)(270 / 180.0f * Math.PI));
            transferMatrix[1] = (float)-Math.Sin((float)(270 / 180.0f * Math.PI));
            transferMatrix[2] = -transferMatrix[1];
            transferMatrix[3] = transferMatrix[0];
            transferMatrix[4] = pageSizeAfterRotation.Width;
            //transferMatrix[5] = pageSizeAfterRotation.Height;
            break;
          case 180:
            transferMatrix[0] = (float)Math.Cos((float)(180 / 180.0f * Math.PI));
            transferMatrix[1] = (float)-Math.Sin((float)(180 / 180.0f * Math.PI));
            transferMatrix[2] = -transferMatrix[1];
            transferMatrix[3] = transferMatrix[0];
            transferMatrix[4] = pageSizeAfterRotation.Width;
            transferMatrix[5] = pageSizeAfterRotation.Height;
            break;
          case 90:
            transferMatrix[0] = (float)Math.Cos((float)(90 / 180.0f * Math.PI));
            transferMatrix[1] = (float)-Math.Sin((float)(90 / 180.0f * Math.PI));
            transferMatrix[2] = -transferMatrix[1];
            transferMatrix[3] = transferMatrix[0];
            //transferMatrix[4] = pageSizeAfterRotation.Width;
            transferMatrix[5] = pageSizeAfterRotation.Height;
            break;
        }
 
        var ww = writer.DirectContentUnder;
        ww.AddTemplate(background, transferMatrix[0], transferMatrix[1], transferMatrix[2],
          transferMatrix[3], transferMatrix[4], transferMatrix[5]);
      }
 
      document.Close();
      writer.Close();
    }
  }
}