LuaTeX:タイプセット出力(フォームと空白の両方)に影響を与えずに数学の境界をマークします

Nov 21 2020

前の質問への回答からわかるように、数学境界ノードは、数学境界をマークする信頼できる方法ではありません。境界をマークするために使用できる2つの可能な回避策があります:1)数学リストの周りにユーザー定義のwhatsitノードを追加します。2)数学リスト内のノードの「数学」属性を定義して設定します。このような境界のマーキングは、TeXが行を分割した後、ノードリストを後処理するために多くの方法で便利です。a)テキストを抽出しようとしているときに<beginmath> <endmath>タイプの区切り文字を追加します。b)テキストと数学の選択的な色付けc)など。 a)方程式の代替/テキストの説明を提供するためにwhatsit値を開始するために追加された「代替テキスト」と一緒に使用することもできます。

アプローチ1)に関して、LuaTeXドキュメントには、「LuaTEXエンジンは、内容を見ることなく、そのようなものを単純にステップオーバーする」と記載されています。リンク:whatsitsはmicrotypographyに影響を与えることができることを示唆している記事、の改行、および出力をpagebreakingありますが、ポストの影響を受けショー改ページ、およびリンクがすることをポストショーがmicrotypography(もTeXの系での改行影響を与えることができる)の影響を受けていることは。

アプローチ2)がすべての数学リストの内容を反復処理する必要がある場合、1)mlist_to_hlistフィルターを使用して呼び出された場合に比べて遅くなりますか?(おそらく$<tex_equation>$安全に再定義して、$区切り文字の前後に属性を設定および設定解除できる場合を除きますが、そのような区切り文字は他にもあります...)。したがって、問題は、アプローチ1)を使用して数学の境界をマークし、マイクロタイポグラフィ、改行、およびページ区切りに本当に影響を与えないようにする方法です。それとも、アプローチ2)が唯一の安全な賭けですか?

これがインライン数学の最初の試みであり、専門家によるレビューと、表示数学(整列などの環境)の拡張機能を探しています。

コード:

% >>> lualatex mathmode.tex
\documentclass{article}
\usepackage[callback={}]{nodetree}

\begin{document}

\directlua{
local function mathboundary(h,d,p)
    local beginmath = node.new("whatsit","user_defined")
    beginmath.type = string.byte("s")
    beginmath.value = "beginmath"
    beginmath.attr = h.attr
    beginmath.user_id = luatexbase.new_whatsit'beginmath'
    local endmath = node.new("whatsit","user_defined")
    endmath.type = string.byte("s")
    endmath.value = "endmath"
    endmath.attr = h.attr
    endmath.user_id = luatexbase.new_whatsit'endmath'
    h = node.insert_after(h,node.tail(h),endmath)
    h = node.insert_before(h,h,beginmath)
    return node.mlist_to_hlist(h,d,p)
end

luatexbase.add_to_callback('mlist_to_hlist', mathboundary, 'Mark math')
}

\setbox0=\vbox{{\noindent Hello\\ $x=a+b^2$}}

\directlua{
    local nodetree = require('nodetree')
    nodetree.print(tex.box[0])
}

\box0

\end{document}

コンソール(探してくださいWHATSIT subtype: user_defined):

└─VLIST width: 345pt, depth: 0.83pt, height: 18.94pt
  ╚═head:
    ├─HLIST subtype: line, width: 345pt, depth: 0.11pt, height: 6.94pt
    │ ╚═head:
    │   ├─LOCAL_PAR 
    │   ├─GLYPH subtype: 256, char: H, width: 7.5pt, height: 6.83pt
    │   ├─GLYPH subtype: 256, char: e, width: 4.44pt, height: 4.48pt, depth: 0.11pt
    │   ├─GLYPH subtype: 256, char: l, width: 2.78pt, height: 6.94pt
    │   ├─GLYPH subtype: 256, char: l, width: 2.78pt, height: 6.94pt
    │   ├─GLYPH subtype: 256, char: o, width: 5pt, height: 4.48pt, depth: 0.11pt
    │   ├─PENALTY penalty: 10000
    │   ├─GLUE stretch: +1fil
    │   ├─PENALTY penalty: -10000
    │   └─GLUE subtype: rightskip
    ├─PENALTY subtype: linebreakpenalty, penalty: 300
    ├─GLUE subtype: baselineskip, width: 3.75pt
    └─HLIST subtype: line, width: 345pt, depth: 0.83pt, height: 8.14pt
      ╚═head:
        ├─WHATSIT subtype: user_defined, user_id: 1, type: 115, value: beginmath
        ├─GLYPH subtype: 256, char: x, width: 5.72pt, height: 4.31pt
        ├─GLUE subtype: thickmuskip, width: 2.78pt, stretch: 2.78pt
        ├─GLYPH subtype: 256, char: =, width: 7.78pt, height: 3.67pt, depth: -1.33pt
        ├─PENALTY subtype: noadpenalty, penalty: 500
        ├─GLUE subtype: thickmuskip, width: 2.78pt, stretch: 2.78pt
        ├─GLYPH subtype: 256, char: a, width: 5.29pt, height: 4.31pt
        ├─GLUE subtype: medmuskip, width: 2.22pt, stretch: 1.11pt, shrink: 2.22pt
        ├─GLYPH subtype: 256, char: +, width: 7.78pt, height: 5.83pt, depth: 0.83pt
        ├─PENALTY subtype: noadpenalty, penalty: 700
        ├─GLUE subtype: medmuskip, width: 2.22pt, stretch: 1.11pt, shrink: 2.22pt
        ├─GLYPH subtype: 256, char: b, width: 4.29pt, height: 6.94pt
        ├─HLIST subtype: sup, width: 4.49pt, height: 4.51pt, shift: -3.63pt
        │ ╚═head:
        │   └─GLYPH char: 2, width: 3.99pt, height: 4.51pt
        ├─WHATSIT subtype: user_defined, user_id: 2, type: 115, value: endmath
        ├─MATH subtype: endmath
        ├─PENALTY subtype: linepenalty, penalty: 10000
        ├─GLUE subtype: parfillskip, stretch: +1fil
        └─GLUE subtype: rightskip

回答

7 MarcelKrüger Nov 21 2020 at 16:09

改行後にノードリストを見る場合、境界ノードは信頼できるソリューションではありません。これには少なくとも2つの理由があります。

  1. すべてのノードは破棄可能または破棄不可のいずれかです。境界ノードが破棄可能である場合、math-on / math-offノードの場合と同じ問題が発生します。それらは行の先頭で消えます。したがって、必要なノードを破棄することはできませんが、行の先頭にある破棄可能なノードに関する規則を覚えておいてください。改行後、最初の破棄できないノードが検出されるまで、すべての破棄可能なノードが削除されます。特にこれは、新しい破棄不可能な境界ノードが、潜在的に後続の破棄可能な境界ノードの削除を抑制し、出力に影響を与えることを意味します。(ここに破棄できないノードを追加する理由は他にもあります。たとえば、whatsitは常に改行に影響しますが、これは最も簡単に説明できます)

  2. 改行が来ると、数学を含むが数学の境界を含まないhlistを作成できます。かなり極端な例は次のとおりです。

    \documentclass{article}
    \begin{document}
    \showoutput
    \setbox0\vbox{
      \hsize=1cm
      \noindent
      Hi $1+2+3+4$
    }
    \setbox1\vsplit0 to\baselineskip 
    \setbox2\vsplit0 to\baselineskip
    \showbox0
    \showbox1
    \showbox2
    \end{document}
    

    ここで、ボックス1には数式の中央部分のみが含まれているため、境界ノードが含まれている理由はありません。それでもまだ数学です。

    (実際に発生する可能性が高いこれの変形は、ページ間で分割される数式です。2番目のページには、数学が開始されないため、開始数学ノードがありません。)

では、これについて何ができるでしょうか?2つのオプションがあります:

  1. あなたのアプローチは1)確実に機能することはできませんが、2)を実装して属性を追加することができます。基本的に、post_mlist_to_hlist_filter¹のノードリストを繰り返し処理してこれを追加すると、インライン計算と表示計算の両方がカバーされます。
  2. 改行する前にやりたいことは何でもして、既存の数学境界ノードを使用してください。ほとんどの場合、とにかく改行する前にいくつかの作業を行いたいので、これは非常に自然です。

特定のユースケースに応じて、解決策の1つは非常に自然なものになることがよくあります。たとえば、テキスト抽出はほとんどの場合、改行前(または整形前)のテキストに基づいている必要があるため、2に適合しますが、数学ノードに異なる色を追加することは、効果的に属性を追加することを意味するため、1に自然に適合します。

¹:現代LuaLaTeXのバージョンでは、mlist_to_hlistほとんど直接設定されていないが、代わりにするんpre_mlist_to_hlist_filterpost_mlist_to_hlist_filter使用する必要があります。それらは基本的にと同じインターフェースをmlist_to_hlist持っていnode.mlist_to_hlistますが、あなた自身を呼び出す必要はなく、あなたのコードはこれらのコールバックを使用して他のパッケージと互換性を保ちます。たとえば、質問のコードは、置き換えることで調整できます

luatexbase.add_to_callback('mlist_to_hlist', mathboundary, 'Mark math')

luatexbase.add_to_callback('pre_mlist_to_hlist_filter', mathboundary, 'Mark math')

と交換

return node.mlist_to_hlist(h,d,p)

ただで

return h