whiteleaf7 / narou

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

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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

ndxbn opened this issue · comments

掲題のとおりです。
この 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>'

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

エラー

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

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

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

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

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

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

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

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}"!

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

  • Windows 10 Home 21H1 (64-bit)
  • ConEmu 220418 (64-bit)
  • PowerShell 7.2.4 (64-bit)
  • ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x64-mingw-ucrt]
  • narou.rb 3.8.1
  • AozoraEpub3 1.1.1b13Q
  • OpenJDK 17.0.2, 18.0.1.1, 19-27, 20-2

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

  1. 以下の行のダブルクォートの中に -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8 を追加
    java_encoding = "-Dfile.encoding=UTF-8"

または

  1. 以下の部分で UTF-8 決め打ちにする代わりに、UTF-8 として不正な場合はロケールのエンコーディングを使用

    narou/lib/helper.rb

    Lines 422 to 423 in 1b07ae1

    stdout.force_encoding(Encoding::UTF_8)
    stderr.force_encoding(Encoding::UTF_8)

    例えば、以下のコードを 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 は使ってないので、上記はすべて机上の推論です ...

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

  • 妥当な修正方法なのかどうか分からないので、本家には pull request を投げておりません。
  • 変更内容はご自身で確認下さい。
  • 本家「narou」に対して「narouq」という名称にしたため、本家との併存が可能です。
  • しっかり試したわけではないのですが、私の環境では Java17 でも Java20 でも動きました。