見出し画像

SVFで出力したPDFをライブラリで扱うとフォントが変になる

業務システムでお世話になる確率の高い、帳票基盤システムのSVFをご存知でしょうか。
MSゴシックなどを使っていて、ライブラリで読み込んで保存しただけなのにフォントがおかしくなる、という問題に立ち向かったお話です。

前提

2024年現在、PDFを取り扱うための有名なライブラリだと、iTextやPDFBoxなどが候補になるかと思います。
PDFBoxはライセンスがApacheで、業務アプリなどでも非常に使いやすいと思います。
今回はPDFBoxを採用したケースです。

フォント化けの原因

核心のお話です。
SVFでMSゴシックやMS明朝などを指定したPDFを作り、それをPDFBoxでloadすると「フォント名」が化ける、これが原因です。
SVFで出力されたPDFを詳しく解析した結果、SJISでエンコードされたコード参照形式(#xxx)でフォント名が保存されていました。
PDFBoxもiTextも海外製ですので、こんなモン想定していないのかもしれません。
フォント名を正確にSJISでエンコードできずにUnicodeに変換した結果、例えば「MS明朝」なら「?l?r??’?」(文字化け)みたいになってしまいます。
この状態で保存されたPDFをビューアで表示しようとすると「これなんのフォント?」状態になり、別の代替フォントに差し変わっちゃうといった状態になります。
※使用していたSVFはバージョンが少しクラシックなものだったので、現在ではもしかしたら解消しているのかもしれません
※SVFだけではなく、CoReportsでも同様のPDFが出力されるケースを発見しました
※PDFBox側でアジア人がすんげぇ貢献したらいつか直るかもしれません(ゆういちろうは時間がありません。誰か挑戦してくれませんかねw)
※なんでSVFはコード参照形式で保存するんだろね?他のPDFライブラリではどうなるかなぁ

処方箋

ライブラリ改修とかはせずに、アプリ側のコードでなんとかする方法を示します。
仕組みを簡単に説明します。
①PDF内のフォントのうち、CID(Type 0)フォントすべてに以降の処理を行います
②まずUnicodeに変換されてしまったフォント名の文字列を1文字ずつ調べます
③文字がC1領域の制御コード(*1)の場合ASCII文字への変換テーブルを使って、そうでなければそのままバイト配列として保持します
④つなげたバイト配列をSJISと想定して、Javaの文字列(内部的にはUnicode)に変換します
⑤元の文字列から変化している場合、文字列が化けていると想定できますので、日本語フォント名から英語フォント名に変換(*2)していまいます(例:「MS明朝」→「MS-Mincho」)
(*1)ここがキモ、SJISの全角文字の上位1バイトがUnicodeの制御コードに変換されてしまっているはずです
(*2)文字コードを変えて、元のフォント名のままでいければと思ったんですが、うまくいかなかったので苦肉の策です

コードが長くなるので、これらをまとめたものをGitHubで公開しました。
以下よりご確認の上お役立てください。
https://github.com/yu1row/PDFBoxUtil

使い方の例

input.pdfのフォントを修正してoutput.pdfに保存します。

PDFBoxUtil.fontNormalize("./pdf/input.pdf", "./pdf/output.pdf");

蛇足

業務あいてぃーの世界では、PDFふたつを、想定箇所以外が変化していないか?というのを調べる業務が発生する場合があります。
目で見比べると視力が死ぬので、PDFを画像に変換してピクセル単位で比べる、などのような方法が執られたりするんですが、SVFで出力したPDFで文字が化けてたりするってことがあります。
これも今回紹介した理由が原因であることが多いと思いますが、そういう場合にもフォント名を変換して保存しなおしたものでやるとうまくいったりすると思いますので、ご担当者はご一考ください。

この記事が気に入ったらサポートをしてみませんか?