whiteleaf7 / narou

Narou.rb - 小説家になろうのダウンローダ&縦書き整形&管理アプリ。Kindle(などの電子書籍端末)でなろうを読む場合に超便利です!
Other
483 stars 56 forks source link

Windows で使っている Java が Adopt Open JDK だと "AozoraEpub3でEPUBに変換" するときにエラーになる #399

Open ndxbn opened 2 years ago

ndxbn commented 2 years ago

掲題のとおりです。 この ISSUE は、どちらかというと FAQ 的な内容になっているので、適切な場所があるのであれば、そちらに移動し、Close してください。

利用者側がとれる対処方法

Oracle Java へ向いているかどうかは、CLI からの確認はおそらくできません。環境変数 PATH を見て、大丈夫そうかどうかを確認するしかないと思います。

環境変数 PATH が正しく設定できている例

環境変数 PATH が正しく設定できている例

なぜ Oracle Java 以外がインストールされた状態になってしまったのか

私の場合は、Chocolatey で Java に依存しているものをインストールしているのですが、それらが2017年の Oracle Java の有償化に伴って Adopt へ依存先を変更したためでした。 例えば PlantUML などを使っている場合はなりがちだと想います。 これで、パッケージマネージャ上では Oracle Java への依存がなくなった状態となり、クリーンアップをしたことで、Oracle Java がアンインストールされました。

実行時のエラーと narou trace の内容

I:\narou>narou convert 1
ID:1 聖女の魔力は万能です の変換を開始
縦書用の変換が終了しました
AozoraEpub3でEPUBに変換しています...C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/novelconverter.rb:202:in `txt_to_epub': invalid byte sequence in UTF-8 (ArgumentError)
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/novelconverter.rb:315:in `convert_txt_to_ebook_file'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/command/convert.rb:300:in `convert_txt_to_ebook_file'

  エラーが発生したため終了しました。
  詳細なエラーログは narou trace で表示出来ます。もしくは --backtrace オプションを付けて再度実行して下さい。

I:\narou>narou trace
--- 2022/06/11 13:50:12 ---
C:/Ruby30-x64/bin/narou convert 1

C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/novelconverter.rb:202:in `txt_to_epub': invalid byte sequence in UTF-8 (ArgumentError)
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/novelconverter.rb:315:in `convert_txt_to_ebook_file'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/command/convert.rb:300:in `convert_txt_to_ebook_file'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/commandbase.rb:152:in `call'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/commandbase.rb:152:in `hook_call'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/command/convert.rb:252:in `block in convert_novel_main'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/command/convert.rb:248:in `each'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/command/convert.rb:248:in `convert_novel_main'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/command/convert.rb:201:in `block (2 levels) in convert_novels'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/mixin/locker.rb:26:in `lock'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/command/convert.rb:200:in `block in convert_novels'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/command/convert.rb:199:in `each'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/command/convert.rb:199:in `with_index'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/command/convert.rb:199:in `convert_novels'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/command/convert.rb:159:in `block in main'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/command/convert.rb:152:in `each'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/command/convert.rb:152:in `main'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/command/convert.rb:133:in `execute'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/commandbase.rb:125:in `execute!'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/commandbase.rb:134:in `execute!'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/command/convert.rb:123:in `block in execute!'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/narou.rb:369:in `concurrency_call'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/command/convert.rb:122:in `execute!'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/commandline.rb:29:in `run'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/commandline.rb:43:in `run!'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/narou.rb:50:in `block in <top (required)>'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/lib/backtracer.rb:16:in `capture'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/narou.rb:49:in `<top (required)>'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/bin/narou:13:in `require_relative'
  from C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/narou-3.8.1/bin/narou:13:in `<top (required)>'
  from C:/Ruby30-x64/bin/narou:25:in `load'
  from C:/Ruby30-x64/bin/narou:25:in `<main>'
ndxbn commented 2 years ago

ref https://github.com/whiteleaf7/narou/issues/395 :おそらく同じような内容なんだと思います。インストールし直したのかどうかはわかりませんが、 PATH が書き換わったとかなんじゃないかなぁと思ってます。

takotakot commented 2 years ago

エラー

参考までに、どのようなエラーとなるのか共有していただくことはできますか? エラーメッセージで検索する方もいらっしゃりそうです。

ndxbn commented 2 years ago

Eclipse Adoptium の jre-18.0.1.10-hotspot では再現しました。 Adopt Open JDK の最新版である OpenJDK 16 では再現できませんでした。

おそらく 17 か 18 か、もしくは Transition to Eclipse - An Update | AdoptOpenJDK Blog のときに変更になったのだと思います。

topstone commented 2 years ago

私は ScoopOpenJDK を入れて使っています。

OpenJDK 18 では再現しました。OpenJDK 17 では再現しませんでした。

ちゃんと調べていませんが、「Java 18 でデフォルトcharset が UTF-8 に。Windows環境のアプリ等は確認要」という辺りが原因のような気がします。

topstone commented 2 years ago

以下、正しい対処法ではなく「取り敢えず動かす」ための修正です。かなり強引なので、内容が理解できる方のみ参考にして下さい。

lib/novelconverter.rb の168行目を

java_encoding = ""

と変更し、199行目を

stdout_capture = res[0].force_encoding('Windows-31J').encode('UTF-8')

と変更すると、OpenJDK 18, 19, 20 でも error なしに変換できるっぽいです。(ただし、この変更をしても202行目の検出が正常に動作するかどうかはわかりません。)

なお、136行目の -c 0 も AozoraEpub3 側で error になるようで、逆に133行目の -c FILENAME だと大丈夫に見えるので、私は現時点では136行目を以下のように変更して使っています。

cover_option = %!--cover "#{cover_filename}"!

参考までに、現時点の私の環境は以下の通りです。

kubo commented 2 years ago

予想が入っていて、試験もしていませんが、以下のどちらかの方法で直るのではないかと思う。

  1. 以下の行のダブルクォートの中に -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8 を追加 https://github.com/whiteleaf7/narou/blob/1b07ae1ae017a581bc987622ba9dcd26728cf28c/lib/novelconverter.rb#L168

または

  1. 以下の部分で UTF-8 決め打ちにする代わりに、UTF-8 として不正な場合はロケールのエンコーディングを使用 https://github.com/whiteleaf7/narou/blob/1b07ae1ae017a581bc987622ba9dcd26728cf28c/lib/helper.rb#L422-L423 例えば、以下のコードを helper.rb に追加して、stdout.force_encoding(Encoding::UTF_8)guess_encoding(stdout) に変更(stderrも同様)
    ENCODING_CANDIDATES = [Encoding::UTF_8, Encoding.find('locale')]
    def guess_encoding(s)
     old_enc = s.encoding
     ENCODING_CANDIDATES.each do |enc|
       s.force_encoding(enc)
       if s.valid_encoding?
         s.encode!(Encoding::UTF_8)
         return
       end
     end
     s.force_encoding(old_enc)
     raise "Cannot guess encoding of #{s}"
    end

理由

-Dfile.encoding=UTF-8 を java の引数に渡しているのは、標準出力の出力先がパイプやファイルのときの文字コードを UTF-8 にするのが目的と思われる。しかし、Java 18 からこの方法は使えなくなった。 https://bugs.openjdk.org/browse/JDK-8283620 には it cannot be officially changed any more. と書かれていて、Java 18 では公式な方法では変更不可能。 そこで、非公式な方法で対処するのが上記の 1番目の案。2番目は UTF-8 でもロケールのエンコーディングでもどちらでも対処できるようにする案。

場合によっては2番目の案はさらに修正が必要かもしれない。上記の案では標準出力全体が同じ文字コードである必要があるが、もしも2つの文字コードが混在とかしていたら、行に分割して、行毎に文字コードを推測するといった処理が必要になるかも。

未確認ですが、Java 19 からなら、-Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 が使えるかもしれない。 https://download.java.net/java/early_access/loom/docs/api/java.base/java/lang/System.html#stdout.encoding によるとJava起動時には UTF-8 に設定可。ただし、Java 18 のドキュメントには載ってないので、Java 18 での回避策にはならないと思う。

... Windows は使ってないので、上記はすべて机上の推論です ...

topstone commented 1 year ago

この問題について、「取り敢えず動かす」形に変更した gem を narouq という名称で置いておきます

topstone commented 1 year ago

参考になる方もいると思いますので、2023年12月時点の私の環境を記しておきます。

topstone commented 1 year ago

ご存知の方も多いと思いますが、最新の改造版 AozoraEpub3 である 1.1.1b15Q は既に Java 8 では動きません

Java 8 の active support 期間は2022年3月31日に終了しているので、改造版 AozoraEpub3 が (最新の LTS である) Java 17 に合わせるのは妥当だと思います。(Java 17 の active support 期間は2026年9月30日まで。) 前述してありますが、Java 21 でも動きます。

happynow commented 6 months ago

kuboさんが書かれた通り、現行の narou.rb 3.8.2 が Java19で新たに追加された実行パラメータ stdout.encodingstdout.encoding に対応していません。

koboさんの 1 の方法が簡単で良いと思います。sunのパラメータは非公式なものですが既存の動作に悪影響はありません。

修正前 https://github.com/whiteleaf7/narou/blob/1b07ae1ae017a581bc987622ba9dcd26728cf28c/lib/novelconverter.rb#L168

下記のように sun. のプレフィックスが付いたもの(非公式)と付いてないもの(公式)を同時に指定してもいいでしょう。 そうすれば、Java18だとか19だとか、バージョンに関係なく動作すると思います。 存在しないパラメータをコマンドオプション -D で指定しても実行エラーにならないはずなので。

修正案 novelconverter.rb  168行目

    java_encoding = "-Dfile.encoding=UTF-8" +
                    " -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8" +
                    " -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8"

この修正を、とりあえず以下の環境で試したらエラーが解消されました。

環境共通 Windows 10 Home(64bit) 22H2 narou-3.8.2

環境1 openjdk 21.0.1 2023-10-17 LTS AozoraEpub3-1.1.1b19Q →修正前:エラー、修正後:エラー解消

環境2 java 18 2022-03-22 AozoraEpub3-1.1.0b55Q →修正前:エラー、修正後:エラー解消

環境3 openjdk 9.0.4 AozoraEpub3-1.1.0b55Q →修正前後とも正常動作

1 の方法だけで十分かもしれませんが、1 と 2 の両方の対応を実装することも考えられます。

ただし、2 の方法は java.exe の時だけ実行するよう条件付けたほうがいいかもしれません。 問題の Helper::AsyncCommand.exec メソッドは AozoraEpub3 以外でも使っています。 例えば kindlegen.exe の実行でもこのメソッドを使っており、更に呼び出し元で

https://github.com/whiteleaf7/narou/blob/4660df5c6200f1a883d554dc1f1562aa6fb5ca1f/lib/novelconverter.rb#L265

と、force_encoding をもう一回やっています。これは削除すればいいと思いますが、注意が必要です。 ENCODING_CANDIDATES に何を設定するかも、もう少し検討が必要でしょう。

なお、いくつかのJDKで試したところ、 sun. が付いていない stdout.encoding, stderr.encoding が追加されたのは jdk19 でしたが oracle-jdk12、open-jdk9 でも既に sun.stdout.encoding, sun.stderr.encoding のプロパティは非公式ながら機能してました。

Rich-Richie commented 3 months ago

このAozoraEpub3のエラーは、narouの最新バージョン(v 3.9)で修正されましたか?混乱しています。また、このコンポーネントはどの程度重要なのでしょうか?私はこのアプリを使っていて問題を感じたことはありません。

kokotaro commented 3 months ago

UbuntuでopenJDK21(openjdk-21-jdk)を使用した場合、解消 WindowsでAdoptium(OpenJDK21U-jdk_x64_windows_hotspot_21.0.2_13.msi)を使用した場合、解消 Java8が存在しAozoraEpub3-1.1.0b46.zipを手元に保管している場合に影響は無いですが、既に正式版を公式サイト からダウンロード出来ない以上、改造版を使用するユーザーが増える傾向を考えるとそこそこ重要度高いと思います。