読者です 読者をやめる 読者になる 読者になる

ConflueceのPDFエクスポートでの改行処理の修正

こちらで書いたとおり、現状のConflueceのPDFエクスポートでは日本語(CJK)環境の改行処理に問題があります。Flying Saucer(xhtmlrenderer)の課題トラッカーにパッチが添付されていたので(xhtmlrenderer: 課題 206)、参考させていただき修正してみます。完全に対応できたわけではないですが、メモとして残しておきます。

この方法では本文のみしか対応出来ていません。詳しくは最後に追記しています。

追加(2010/10/13)

修正されました。こちらを参照。

Flying Saucerの改行処理はorg.xhtmlrenderer.layout.Breakerにあります。CJK環境は考慮されていないため、まず行中の空白文字で区切ったうえで、出力領域の幅におさまる分だけ1行に詰めて改行する仕様になっています(空白がないと改行されない)。そのため、CJKに限らず途中に空白文字が含まれない文章はあふれてしまいます。

添付されていたパッチでは、LineBreakMeasurerを使用して改行を処理しています。LineBreakMeasurerは、AWTのコンポーネントにテキストを配置するときになどに使用され、テキストの区切りはBreakIteratorが行います。BreakIteratorは、アルファベット、漢字、カナ、記号などの境界でテキストを切り出します。

ただ、BreakIteratorのJDK標準の実装では

じゅげむじゅげむごこうのすりきれかいじゃりすいぎょのすいぎょうまつうんらいまつふうらいまつくうねるところにすむところやぶらこうじのやぶこうじぱいぽぱいぽぱいぽのしゅーりんがんしゅーりんがんのぐーりんだいぐーりんだいのぽんぽこぴーのぽんぽこなーのちょうきゅうめいのちょうすけ
http://ja.wikipedia.org/wiki/%E5%AF%BF%E9%99%90%E7%84%A1

みたいのは区切られないようなので、出力幅いっぱいのところで改行を入れてしまう必要があります。

このような方向で修正したものが↓のファイルです。
http://dl.dropbox.com/u/191294/xhtmlrenderer/Breaker.java
これを、#CONF-18704に添付されているconfluence-flyingpdf-plugin-1.7.jarのMETA-INF/lib/xhtmlrenderer-8.2-atlassian.jarに含まれるclassファイルと置き換えて、プラグイン管理からインストールしました。
xhtmlrendererのjarを入れ替えようとしたのですが、Flying SaucerのR8をもとに修正してビルドしたjarではエラーで動作しなかったのでclassを入れ替えました。

いくつかの文章をPDFにエクスポートしてみたのですが、現状ではまだ次のような問題が残っています。

  • 空白を含む日本語の文章がページ中央で改行されてしまう場合がある。
  • 禁則処理がされない

一点目は空白で区切る初期の実装を残しているためですが、省いても良いかもしれません。

参考)BreakIterator

BreakIteratorで

プログラミング言語JavaおよびJavaプラットフォームは、1990年代前半にサン・マイクロシステムズでジェームズ・ゴスリンなどの人々によって開発された。現在はJava技術の標準化作業は、Java Community Process (JCP) というプロセスを経て行われている。
http://ja.wikipedia.org/wiki/Java

を区切ると次のようになります。

プログラミング
言語
Java
および
Java
プラットフォーム


1990
年代前半

サン・マイクロシステムズ

ジェームズ・ゴスリン
などの
人々
によって
開発
された

現在

Java
技術

標準化作業


Java

Community

Process

(
JCP
)

という
プロセス




われている

こんなコードです。

        String source = "プログラミング言語JavaおよびJavaプラットフォームは、1990年代前半にサン・マイクロシステムズでジェームズ・ゴスリンなどの人々によって開発された。現在はJava技術の標準化作業は、Java Community Process (JCP) というプロセスを経て行われている。";
        BreakIterator boundary =  BreakIterator.getWordInstance();
        boundary.setText(source);

        int start = boundary.first();
        for (int end = boundary.next();
             end != BreakIterator.DONE;
             start = end, end = boundary.next()) {
             System.out.println(source.substring(start,end));
        }

追記(2010/5/19)

Breakerを修正する方法では、本文中のみしか対応出来ていませんでした。{warning}のようなマクロでは文字が改行されずあふれてしまいます。
Breakerでは、public static void breakText(LayoutContext c, LineBreakContext context, int avail, CalculatedStyle style)というメソッドで改行の処理をしています。availに描画するボックスの幅が渡されるのですが、{warning}の場合には、実際のボックスの幅よりも大きい値が渡されるようです(英文の場合は問題なし)。なのでBreakerのみでは修正が完結せずavailの計算の部分も確認する必要がありそうです。
{warning}はtableで記述されるので、おそらく表組みもNGだと思います。