SSブログ

iTextのソースを追ってみた [日記]

拙作『青空文庫形式のテキストファイルをPDFに変換するツール、略して「青P」』で、動作報告をいくつかもらいました。

主だったものは、次の2点。

  • (フォントをPDFファイル中に埋め込まず)標準のフォント(日本語用)を使用すると、携帯端末のPDFビューアでは、文字が表示されない(らしい)。フォントの埋め込みを行えば表示できる(らしい)
  • 縦書きにすると、括弧や記号が横書きの文字で表示される。
    (標準フォントの場合、全角の括弧は正しく縦書き用になるのに、半角の括弧は横書き用のまま)

これらは青Pの問題というよりも、PDF作成のために使っている iText の都合です。

原因は何なのか。解決策はあるのか。iTextのソースコードを追ってみました。


標準フォントを使ったPDFが 携帯端末のPDFビューアで見えない件

以前、
「PDFのバージョン(?)によって標準フォントが異なる。指定しているフォントが違うのではないか。」
という情報をもらいました。

iTextで使える日本語のフォントは、次の3つです。

  • HeiseiMin-W3
  • HeiseiKakuGo-W5
  • KozMinPro-Regular

com\lowagie\text\pdf\fonts にあるファイル「cjkfonts.properties」に使用可能なフォントの一覧が書かれています。
フォントの詳細は、同じディレクトリにあるフォント毎のファイル「HeiseiMin-W3.properties」等です。
(このファイルは、iText.jar ではなく iTextAsian.jar にある)
ということは、cjkfonts.properties を編集し、<フォント名>.properties を用意すれば、どんなフォントにでも対応できるのですが……。

自分の環境に無いフォントが指定されていると、別のフォントを代わりに使って表示するというのが、PDFの仕様として決まっていたはずです。
この時にレイアウトが崩れないよう文字の大きさ(メトリクス)をPDFファイルの中に持っています。

なので、フォントが違うから表示されないというのは“ない”はずなのです。

PDFファイル中のファイル名の部分を(バイナリエディタ)で適当に書き換えてもWindows版Adobe Reader 9 は、ちゃんと表示してくれます。


では、何が問題なのか。

標準フォントを使うとき、フォント名以外にエンコーディング(文字コードみたいなもの)も指定します。

iTextで使える日本語用のエンコーディングは、次の5つです(末尾にVが付いているのが縦書き用)
(通常は UniJIS-UCS2-H、UniJIS-UCS2-V を使います)

  • UniJIS-UCS2-H
  • UniJIS-UCS2-V
  • UniJIS-UCS2-HW-H
  • UniJIS-UCS2-HW-V
  • Adobe-Japan1-UCS2

com\lowagie\text\pdf\fonts にあるファイル「cjkencodings.properties」に使用可能なエンコーディングの一覧が書かれています。
同じディレクトリにある「UniJIS-UCS2-H.cmap」等のファイルには、PDF内部の文字コード(CID)との対応表が書かれています。 (このファイルも iTextAsian.jar にある)

PDFファイルで指定されているエンコーディングをPDFビューアが理解できないと、文字が表示できません。
(ちなみに Windows環境のAdobe Reader 9が理解できるエンコーディングは、フォルダ C:\Program Files\Adobe\Reader 9.0\Resource\CMap にあります)

ここから、
標準フォントを使ったPDFが 携帯端末のPDFビューアで見えない件は、携帯端末のPDFビューアは UniJIS-UCS2-H、UniJIS-UCS2-V に対応していないのではないかと推測できます。

現物(PDFが表示できる携帯端末)が無いので確認はできないし、じゃあ、どのエンコーディングを使えばいいのか調査もできません。
誰か iPad を無償提供してくれないだろうか。(自分で買えよ)

携帯端末のPDFビューアを使いたい人は、フォント埋め込みで対応してください。

ちなみに、このあたりの処理は、クラス com.lowagie.text.pdf.CJKFont が行っています。


縦書きにしても、括弧が横書き用の件

「標準フォントを使うとちゃんと表示されるのだから、標準フォントを埋め込んだら良いのではないか」
との提案がありました。

もっともは意見ですが、結論から言えば、
どんなフォントを使っても、フォント埋め込みをしたら、結果は一緒。
(それに埋め込みをしない場合でも、半角の括弧は横書き用が使われます)

iTextで埋め込みできるフォントは、次の2種類(多分)

  • True Type Font
  • Open Type Font

これらのフォントファイルには、縦書きに対応しているものと、していないものがありますが、その違いは、
vhea
vmtx
という2つの情報を持っているかどうかです(持っていれば縦書き対応)
言い換えれば、vheavmtx に縦書き用の文字情報が記述されているのです。

そして(大体察しているでしょうが) iTextは vheavmtx を全く見ていません。
イメージで言えば、iTextでフォント埋め込み時の縦書きは、横書きを1文字ずつ改行しているようなものだったのです。

では、どうすればいいのか。
理想を言えば vheavmtx をちゃんと利用するようにコードを書き換えるのです。
さらに理想を言えば、↑の内容を“誰かが”(これ重要。自分じゃやる気無し)実装して、公式のコードを更新してくれることです。
ちなみに、修正が必要なのは、クラス com.lowagie.text.pdf.TrueTypeUnicode 辺りでしょうか。


それでは、『青P』は、どうやって縦書きでも括弧をちゃんと表示しているのか。

「括弧とかの文字は1文字ずつ切り出し、90°回転して出力」

という力技に出ました。

そこいら辺のコードを適当に抜粋すると、こんな感じでやっています。

PdfContentByte cb = ...;
BaseFont font = ...;

for (int i = 0; i < str.length(); i++) {

    char c = str.charAt(i);
    float width = font.getWidthPoint(c, font_size);

    if ("()「」-以下略".indexOf(c) >= 0) {
        cb.setTextMatrix(0, -1, 1, 0, x + width/2, y - width/2);
    } else if ("。、.,".indexOf(c) >= 0) {
        cb.setTextMatrix(x + width/2, y + width/2);
    } else {
        cb.setTextMatrix(x, y);
    }

    cb.showText(new String(new char[]{c}));
    y -= width;
}

今回、ソースを追った iText のバージョンは 2.1.7 です。

現時点での最新版は 5.0.1 です。

バージョンに大きな隔たりがありますが、2.1.7の次が5.0.0だったので、それほどの違いはありません。
が、Javaのクラスのパッケージ名が変更になったので、上記記事内の lowagieitextpdf に置き換えて見てください。

[船] 今日の一冊
虫姫―御書物同心日記

虫姫 ― 御書物同心日記

  • 作者: 出久根 達郎
  • 出版社/メーカー: 講談社
  • 発売日: 2002/06
  • メディア: 単行本
某シューティングゲームとは何の関係も無い。

タグ:iText 青P
nice!(0)  コメント(1)  トラックバック(0) 

nice! 0

コメント 1

通りすがり

iTextの2.1.7と5以降では、ライセンスで非常に大きな差があります。
2.1.7まではGPL/LGPL/MPLからの選択だったので、使用条件は概ね「使っていることを明示しておくこと」「求められたらiTextのソースをDL出来るようにしておくこと」の二つだけでしたが(細かい条件は適時確認してください)、5以降はAGPLという非常に「感染力」の強いライセンスに変更されました。iTextの5以降を使っている場合、たとえWebアプリでもダイナミックリンクでも、全てのソースを使用者(Webアプリの場合はWebの閲覧者すべて)に公開する必要があります。お気をつけを。
by 通りすがり (2012-05-23 14:21) 

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。