ox-hugo で日本語の文書を書くと,サマリ (このサイトのトップページにあるような,各ページの先頭部分) や脚注に謎の空白が入ることがあることの原因と対策…について書こうとしたのだけど,issue を上げたら作者の方に爆速で fix していただいた (ox-hugo #320) ので,その顛末と補足について.
日本語の行送り
ox-hugo で日本語の文書を書くと,サマリ (このサイトのトップページにあるような,各ページの先頭部分) や脚注の中で,謎の空白が入ることがあるのを見つけた.
日本語は原則として分かち書きをしない1言語なので,たとえば,
これは
テストです.
という二行を連結する際には,
これは テストです.
と空白を挟むのではなく,
これはテストです.
とそのまま連結するのが正しいが,前者のような出力がされてしまっていた.これには二つの原因がある.
- Hugo が (サマリ作成などのため) 行を連結 (word unwrapping) する際に,連結箇所に空白を挿入するため.2
- ox-fugo が org の footnote (
[fn::]
) を Markdown の footnote ([^fn:]
) に変換する際の行連結で,連結箇所に空白を挿入するため.
ox-hugo には,上記の一番目の事象については Markdown の出力時に「改行 (を含むホワイトスペース) の前後がマルチバイト文字のとき」にあらかじめ空白を挟まずに行連結しておく (ことにより,Hugo による行連結時の空白の挿入を避ける) work around が実装されていたが,これはロケールが中国語 (“zh*") のときのみ有効3にされており,日本語ではその恩恵が得られなかった.
また,二番目の事象 (footnote の扱い) については (中国語も含めて) 行連結の際に一律で空白を挿入するようになっていた.
Issue の報告と爆速な改修
日本語も中国語と同様に分かち書きをしない言語なので,footnote の件も含めてその旨を issue として報告した (issue #320).該当 issue を見るとわかるように,(途中少し regression も発生したりしたが) 作者の方に爆速で fix をしていただいた4.
なお,昨日のエントリーは,作者とのやりとりのために作ったテストページ.古い版では Test #1 のサマリや Test #2 の脚注で,それぞれ (元ソースでの) 改行部分に不要な空白が入ってしまっていたが,現在のページは改修後の ox-hugo で処理したものなので,期待通りの出力となっている.
ox-hugo の作者の方に多大なる感謝を.
(おまけ 1) 韓国語の処理について
改修されたソースは,「日本語と中国語に対する」処理と「CJK に対する」処理が混在した形になっている.韓国語の処理をするときに困ってしまう可能性があるが,ハングルを知らないのにあまり口を出すのも混乱の元なので強くは指摘していない (Wikipedia によると,ハングルは元々は分かち書きをする言語だったのが現代ハングルではしなくなった,というようなことが書いてあるが,どこまで鵜呑みにいて良いかわからないし…).この問題は韓国語ネイティブな方にお任せすることにした.
(おまけ 2) 本来は…
今回,中国語対応の処理がすでにあったのでそこに (最小限の修正で) 日本語の処理を加えてもらったが,本来は言語依存でなくキャラクターに応じて処理をするのが正しいと思う.
いまの実装では「行連結の際に空白を挿入しない文字」を :multibyte:
としている (ので,キリル文字などで問題が出る) のだが,これを \cj
や
\cc
を使うようにするだけでよかった気がする.さらに言えば,Emacs は
\c|
という「fill の際に改行しても良い文字」というクラスが定義されており (調べてみるまで知らなかった),ひらがな,カタカナ,漢字などはこれに含まれるので,こちらを使うのが本来かもしれない.
まあいま困っているわけではないし,issue もすでに閉じてしまったので,何か困るまではとりあえずこのままということで.
(おまけ 3) さらに言えば…
そもそも,このあたりの各種言語の特性に関する処理を elisp 側でいちいち考慮するのは何かおかしい気もする (共通的なライブラリがあるべき).
行連結は Emacs の join-line
5 が使えるかと思ったのだが,ソースを見ると分かち書きをしない言語 (日本語や中国語) のことを考慮していない.もちろん fill-paragraph
はそのあたりも考慮しているが,そのままでは文字列処理に使えない.6
というあたりを調べるために fill-paragraph
関連のソース (fill.el
)
を初めて追いかけてみたが,禁則処理も含めてなんだかとても複雑なことをしている.これを実装した人には頭が下がる思いになるとともに,多言語処理の闇の深さを垣間見た気がする.
というわけで,この件に関する現実逃避はとりあえず終わり.