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つの情報を持っているかどうかです(持っていれば縦書き対応)。
言い換えれば、vhea
、vmtx
に縦書き用の文字情報が記述されているのです。
そして(大体察しているでしょうが) iTextは vhea
、vmtx
を全く見ていません。
イメージで言えば、iTextでフォント埋め込み時の縦書きは、横書きを1文字ずつ改行しているようなものだったのです。
では、どうすればいいのか。
理想を言えば vhea
、vmtx
をちゃんと利用するようにコードを書き換えるのです。
さらに理想を言えば、↑の内容を“誰かが”(これ重要。自分じゃやる気無し)実装して、公式のコードを更新してくれることです。
ちなみに、修正が必要なのは、クラス 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のクラスのパッケージ名が変更になったので、上記記事内の lowagie
は itextpdf
に置き換えて見てください。
今日の一冊 | ||
|
某シューティングゲームとは何の関係も無い。 |
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)