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 を使用しています。

  public void MakePdf2(string fppdfIn, string fppdfOut) {
    // https://www.codeproject.com/Articles/277065/Creating-PDF-documents-with-iTextSharp

    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();
      }
    }
  }