TeX コンパイラを利用して TeX から UTF8 へのコンバータを作成する

TeX コンパイラを利用して TeX から UTF8 へのコンバータを作成する

テキストのほとんどをTeX文字列ではなくUTF-8で扱うことには多くの利点があります。目や指、編集者にとって楽なだけでなく、スペルチェックや文法チェッカー、その他のアナライザーにテキストを入力するのも非常に簡単になります。この文字列を書くことを想像してみてください。

 ĄąĆćĘę£łŃńÓóŚś-źŻż

単純な検索/置換戦略を使用すると、次のような理由で悲惨な結果になる可能性があります。

 \def\L{\matbb{L}}

変換されたファイルの奥深くにエラーが残ってしまいます。文字の定義がどれだけ深く埋もれてしまうかは言うまでもありません。

文字を一意に識別できることを理解しています(ここ)そしてパンドックテキストそしてハイパーリファレンスこの問題を何らかのレベルで解決する必要があります。私の質問は、このようなコンバータを TeX 自体に実装することはどの程度実現可能かということです。

(Davidによる追加)

入力:

\documentclass{article}

\newcommand\zzz{hello}

\begin{document}

\L\"{o}\"{o}\c{k} \zzz

\renewcommand\L{LLL}
\renewcommand\"[1]{#1#1}
\renewcommand\c{c}

\L\"{o}\"{o}\c{k} \zzz

\end{document}

UTF-8 テキスト実行でマクロの使用が維持された形式に変換されました:

\documentclass{article}

\newcommand\zzz{hello}

\begin{document}

Łööķ \zzz

\renewcommand\L{LLL}
\renewcommand\"[1]{#1#1}
\renewcommand\c{c}

\L\"{o}\"{o}\c{k} \zzz

\end{document}

答え1

ĄąĆćĘę£łŃńÓóŚś-źŻżまず、ファイル内の のような入力を希望する場合は.tex、単にそれを入力 (または貼り付け) することができます。pdfTeX\usepackage[utf8]{inputenc}を使用している場合は のみが必要ですが、Unicode 対応エンジン (XeTeX または LuaTeX) を使用している場合は さえも必要ありません。たとえば、次のコードは でコンパイルするとそのまま機能しますxelatex

\documentclass{article}
\begin{document}
ĄąĆćĘę£łŃńÓóŚś-źŻż
\end{document}

出力

問題が、それを入力するのに便利な(または覚えやすい)キーボードレイアウトがないので、TeXマクロを使用して入力したい(ただし、ファイルに上記のような文字が含まれていることを望む)場合、これは単にエディタまたは入力システムを設定する問題です。たとえば(コメントユーザ Loop Space による) では、Emacs は を使ってこれを実現できます。キーボードのM-x set-input-method RET TeXキーを押すと、ファイルに入力されるのは です。Emacs を使う必要はありません。この種の機能は、UIM (\=oō)。

したがって、ファイルを作成する場合、TeX 自体を使用してこのような変換を行う理由はないと思います.tex。まず、好みの文字を挿入する方法を見つける方がよいでしょう。


ただし、他のユーザーが作成したファイル(ファイルを変更しても問題ない)を操作している場合.tex、またはこの設定を行う前に自分で作成したファイルを操作している場合は、この質問が意味をなす可能性があります。

TeX を使用する主な利点は (エディターの単純な検索と置換の代わりに)、 や などのマクロの定義がいつ変更されたかを知ることができることです\L\Oこれは、質問で説明されている問題でもあります。

そこで、これを解決するために、私は内省的な(別名反射的な) 機能は LuaTeX に付属しています。具体的には、token.get_macroマクロの定義を確認できる機能と、process_input_buffer入力の各行を調べる (必要に応じて変更する) ことができるコールバックです。アイデアは次のとおりです。

  • テキストが始まる前に、すべての既知の文字置換マクロ ( \L、、など) の「元の」定義を記録します。これにより\"\cマクロが再定義されたかどうかがわかります。
  • 入力の各行で、その行に出現するマクロを探します。定義が変更されていないか確認するそして、もしそうなら、それらとその引数を適切なものに置き換えます。

したがって、質問の例では、次のようにファイルを作成しますmwe.tex

\documentclass{article}
\directlua{dofile('rewrite.lua')}

\newcommand\zzz{hello}

\begin{document}

\L\"{o}\"{o}\c{k} \zzz

\renewcommand\L{LLL}
\renewcommand\"[1]{#1#1}
\renewcommand\c{c}

\L\"{o}\"{o}\c{k} \zzz

\end{document}

(\directlua{dofile(...)}追加された行に注意してください)、次のように実行できますlualatex mwe.tex(一部の行は省略されています)。

9:41:29:~/tmp% lualatex mwe.tex
This is LuaTeX, Version 1.0.4 (TeX Live 2017) 
...
The original definition of #\L# is \TU-cmd \L \TU\L 
The original definition of #\c# is \TU-cmd \c \TU\c 
The original definition of #\"# is \TU-cmd \"\TU\" 
...
Processing line: \begin{document}
 --> Rewrote line to \begin{document}
...
Processing line: \L\"{o}\"{o}\c{k} \zzz
 --> Rewrote line to Łööķ \zzz
Processing line: 
 --> Rewrote line to 
Processing line: \renewcommand\L{LLL}
 ^ This line contains a \def or \newcommand or \renewcommand. Not rewriting.
...
Processing line: \L\"{o}\"{o}\c{k} \zzz
 --> Rewrote line to \L\"{o}\"{o}\c{k} \zzz

以下の内容を含むファイルが見つかりますmwe.rewritten.tex:

\newcommand\zzz{hello}

\begin{document}
\relax

Łööķ \zzz

\renewcommand\L{LLL}
\renewcommand\"[1]{#1#1}
\renewcommand\c{c}

\L\"{o}\"{o}\c{k} \zzz

\end{document}
\relax

ここで、発生するはずだった置換のみが行われていることがわかります。rewrite.luaこれを実現する Lua ファイル (上記で呼び出された) は次のとおりです。

print('')
rewritten_file = io.open(tex.jobname .. '.rewritten.tex', 'w')

funny_noarg = {
   ["\\L"] = "Ł",
   -- Define similarly for \oe \OE \ae \AE \aa \AA \o \O \l \i \j
}
funny_nonletter = {
   ['\\"'] = function(c) return c .. "̈" end,
   -- Define similarly for \` \' \^ \~ \= \.
}
funny_letter = {
   ["\\c"] = function(c) return c .. "̧" end,
   -- Define similarly for \u \v \H \c \d \b \t
}

orig_defs = {}
function populate_orig_defs()
   function set_def(s)
      definition = token.get_macro(s:sub(2))
      orig_defs[s] = definition
      print('The original definition of #' .. s .. '# is ' .. definition)
   end
   for s, v in pairs(funny_noarg) do set_def(s) end
   for s, v in pairs(funny_letter) do set_def(s) end
   for s, v in pairs(funny_nonletter) do set_def(s) end
end
populate_orig_defs()

function literalize(s)
   -- The string s, with special characters escaped, in a format safe for using inside gsub.
   -- https://stackoverflow.com/questions/1745448/lua-plain-string-gsub#comment18401212_1746473
   return s:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%0")
end
function replace(s)
   print('Processing line: ' .. s)
   if s:find([[\def]]) ~= nil or s:find([[\newcommand]]) ~= nil or s:find([[\renewcommand]]) ~= nil then
      print(' ^ This line contains a \\def or \\newcommand or \\renewcommand. Not rewriting.')
     rewritten_file:write(s .. '\n')
     return nil
   end
   for k, v in pairs(funny_noarg) do
      -- followed by a nonletter. TODO: Can use the catcode tables.
      if token.get_macro(k:sub(2)) == orig_defs[k] then
         s = s:gsub(literalize(k) .. '([^a-zA-Z])', function(capture) return v .. capture end)
      end
   end
   for k, v in pairs(funny_letter) do
      -- followed by a letter inside {}. TODO: Can use the catcode tables, also can support \c c, for example.
      if token.get_macro(k:sub(2)) == orig_defs[k] then
         s = s:gsub(literalize(k) .. '{(.)}', v)
      end
   end
   for k, v in pairs(funny_nonletter) do
      -- followed by a letter inside {}. TODO: We could also support \"o for example.
      if token.get_macro(k:sub(2)) == orig_defs[k] then
         s = s:gsub(literalize(k) .. '{(.)}', v)
      end
   end
   print(' --> Rewrote line to ' .. s)
   rewritten_file:write(s .. '\n')
   return nil
end

luatexbase.add_to_callback('process_input_buffer', replace, 'Replace some macros with UTF-8 equivalents')

これは単なる概念実証であり、実稼働品質のシステムではないため、このアプローチを追求することに興味がある場合は、いくつかのショートカットを使用しました。

  • TeXのアクセントや特殊文字マクロのいくつかについては、Unicode相当のみをリストしました。
  • \documentclass{article}行(および行の前にあるもの)を再挿入する必要があります\directlua{dofile(…)}。(楽しみのために、行を移動してみることもできます。前に \documentclassそして何が起こるか見てみましょう。
  • おそらく、この行はすべての\usepackage行の後に、おそらく の先頭に配置する必要があります\begin{document}。(上記を試したことがあれば、その理由がわかるでしょう。)
  • \relax最後の行を削除する必要があります(おそらくこれを表示しないようにすることはできますが…)
  • これは、入力ファイルに LaTeX 規則が含まれており、\={o}が含まれていないことを前提としています\=o。数行追加することで、後者もサポートできます。同様に、 の代わりに\c{k}または\c kなど\c {k}があります。
  • \defまたはを含む行は完全に無視されます (何も置き換えません) \newcommand。代わりに、必要であれば (入力ファイルが非常に不適切に記述されている場合)、 または の最後までスキップして\def、残りを処理することもできます。
  • これは、( のような制御シーケンスが\o終了するタイミングを知るために)「文字」が であると想定していますa-zA-Z。このリストに追加したい場合があります。@実際、当時有効だった catcode 体制下での「文字」の正確な定義を使用することもできます。LuaTeX はそれを提供しています。

通常、pdfTeX または XeTeX を使用してファイルをコンパイルする場合でも、この変換にのみ LuaTeX を使用し、変換されたファイルでは pdfTeX/XeTeX を再び使用できることに注意してください。

関連情報