JavaFX GUIデザインツール Scene Builder 16リリース
JavaのモダンなGUIライブラリ JavaFX のデザインツール Scene Builder 16が3月末にリリースされました。
Scene Builder は、JavaFX GUIライブラリの画面レイアウトを作成するツールです。ドラッグ&ドロップで部品を配置しプレビュー表示が可能です。Javaのコードを書く必要がなく、設定はXMLファイルとして生成されます。
元々はOracleがJavaFXの支援ツールとして開発していましたが、オープンソース化され、現在はGluon社が開発をサポートしています。
日本語環境での文字化け問題
Scene Builder のバージョン10の頃から、日本語環境で実行すると文字化けが発生する問題が生じました。
Scene Builderのメニューなどで日本語表示されるべき文字が、別の記号等に文字化けしています。
たとえば、メニュー項目の「ファイル」が、「ファイル」と本来とは別の文字・記号で表示されています。
文字化けの原因の推察
リリースされたScene Builderに含まれるリソースバンドルの日本語プロパティファイルを調べると、native2asciiでユニコードエスケープされています。このプロパティファイルの中でメニュー項目の「ファイル」の「フ」に該当する箇所のユニコードエスケープ表記を見ると、「\u00e3\u0192\u2022」となっています。文字にすると「フ」になります。本来「フ」に該当するユニコードエスケープ表記は「\u30d5」です。
Scene Builderのソースコードリポジトリに登録されている日本語プロパティファイルはUTF-8符号化で記述されています。ユニコードエスケープ(native2ascii)されてはいません。ということは、ビルドの過程でUTF-8符号化のプロパティファイルがユニコードスケープ(native2ascii)され、その際に文字化けが生じているものと推察しました。
文字化けが生じるビルド過程
Scene BuilderのビルドにはGradleツールが使われています。 Gradleのビルド定義(app/build.gradleファイル)を調べるとプロパティファイルを処理している個所は次の通りです。
processResources { def buildDate = new Date().format(buildDateFormat) from ('src/main/resources') { include '**/*.properties' expand([ version: version, javaVersion: System.getProperty('java.runtime.version') + ', ' + System.getProperty('java.vendor'), buildDate: buildDate ]) filter(EscapeUnicode) } into buildDir }
この中で、次の記述がユニコードエスケープを実施している個所です。
filter(EscapeUnicode)
文字化けの再現に挑戦
問題の解決にはまず事象の再現が基本となります。そこで、この文字化けの再現を試みます。 Windows 10 日本語版の上で Scene Builder のソースコード一式を展開し、gradleでビルドしたところ、日本語プロパティファイルは文字化けとなったものの、リリースされているScene Builderの文字化けとは異なるものとなりました。
「ファイル」はユニコードエスケープでは次のコードに化けています。
\u7e5d\u8f14\u3043\u7e67\uff64\u7e5d\uff6b
文字にすると次です。
繝輔ぃ繧、繝ォ
ということで、単純には再現できませんでした。再現には、Gradleの振る舞いを理解する必要があります。
Gradle の filter(EscapeUnicode)
Gradleの振る舞いを調べていくと、EscapeUnicodeによるフィルタは、入力ファイルをデフォルトエンコーディングとして解釈しユニコードエスケープするとありました。
そのため、Windows 10日本語環境で実行したときに、UTF-8をSJISと誤って解釈した結果となりました。
では、Scene Builderのリリース版に含まれる文字化けはどのように発生するのでしょうか? ここで、Windows OSの英語版でビルドしていると仮定し、Windows OS英語版のデフォルトエンコーディングが何かを調べました。すると、コードページ1252 (ラテン文字1)でした。
Windows OS英語版でScene Builderをビルドすると、EscapeUnicodeによるフィルタは、入力ファイルをCP1252(Windows-1252)として解釈します。例えば、「フ」はUTF-8符号化すると「e3 83 95」となります。これをCP1252として解釈すると、「フ」となり、ユニコードエスケープすると「\u00e3\u0192\u2022」となります。
なるほど、事象が再現しそうです。
問題再現の環境設定
問題再現のために、Windows 10日本語版でデフォルトのエンコーディングをCP1252に変更します。 Windowsの「設定」 > 「時刻と言語」 > 「地域」 で、地域設定を「英語(米国)」に変更します。
D:\work> jshell | JShellへようこそ -- バージョン15.0.1 | 概要については、次を入力してください: /help intro jshell> System.out.println(System.getProperty("file.encoding")) Cp1252
変更できたので、Scene Builderのソースをビルドします。 結果、文字化けを再現することができました。
問題の解決
問題の原因は、GradleのEscapeUnicodeフィルタが入力のプロパティファイルをデフォルトエンコーディングで扱うため、UTF-8以外のデフォルトエンコーディングを持つ環境でビルドすると文字化けが発生するというものです。
UTF-8以外のデフォルトエンコーディングを持つ環境の代表例がWindows OSです。
解決の方針
解決案を列挙します。それぞれ検討・検証していきます。
解決案1
Javaは、JDK 9からリソースバンドル・プロパティファイルをUTF-8で扱えるようになりました(それ以前はISO 8859-1)。そこで、ユニコードエスケープをやめればうまくいくのではというのが解決案1のアプローチです。
次の記述を削除します。
filter(EscapeUnicode)
しかしながら、この結果は不調でした。日本語プロパティファイルはユニコードエスケープされなくなったものの、文字化けです。これは、Gradleがリソースディレクトリのファイルをコピーする際に文字列の解釈が入ってしまい、UTF-8以外のデフォルトエンコーディング環境で文字化けるというものと思い割れます。
解決案2
GradleでEscapeUnicodeの処理をUTF-8として扱うよう設定を追加するアプローチです。 ただし、環境変数での設定やGradlegが生成するファイルの修正ではなく、Scene Builder側で管理するファイルでの設定を追求します。
調査結果、次の記述をbuild.gradleに追加します。
filteringCharset = 'UTF-8'
これはうまくいきました。
解決案3
Scene Builderには、リソースバンドルのプロパティファイルが3つ登録されています。デフォルトの英語プロパティ、中国語プロパティ、そして日本語プロパティです。
英語および中国語のプロパティファイルは、ユニコードエスケープされたファイルがソースコードリポジトリに登録されています。
では、日本語もユニコードエスケープしてしまえというのが解決案3です。
しかし、プロパティファイルはUTF-8で扱えるのがJava SE 9以降であり、しかもnative2asciiコマンドがJDK 10から削除されているご時勢です。ユニコードエスケープしたファイルを扱うのはとても苦痛ですから、このアプローチは最後の手段として取っておくこととします。
解決の提案
解決案2がうまくいったので、これをPRとして提案したところ、採用されました(2021.2)。
よかったよかった。
最後に雑記
経緯は次のチケット(個人Redmine)に記載しています。 www.torutk.com