LuaTeX: zaznacz granice matematyczne bez wpływu na wynik składu (zarówno formularz, jak i spacje)

Nov 21 2020

Jak wynika z odpowiedzi na moje poprzednie pytanie , węzły granic matematyki nie są niezawodnym sposobem oznaczania granic matematycznych. Istnieją dwa możliwe obejścia, których można użyć do oznaczenia granic: 1) Dodaj zdefiniowane przez użytkownika węzły whatsit wokół listy matematycznej, 2) Zdefiniuj i ustaw atrybut „matematyka” dla węzłów na liście matematycznej. Takie oznaczenie granicy może być przydatne na wiele sposobów, aby postprocesować listę węzłów po zakończeniu pracy TeX-a z łamaniem linii: a) dodać ograniczniki typu <beginmath> <endmath> podczas próby wyodrębnienia tekstu b) selektywne kolorowanie tekstu i matematyki, c) itp. a) może być również używany razem z „tekstem alternatywnym” dodanym do wartości beginmath whatsit w celu zapewnienia alternatywnego / tekstowego opisu równania.

Jeśli chodzi o podejście 1), dokument LuaTeX stwierdza: „Silnik LuaTEX po prostu przejdzie przez takie rzeczy, nigdy nie patrząc na zawartość”. Chociaż są posty, które sugerują, że to, co to może wpłynąć na mikrotypografię, łamanie linii i łamanie stron : link do posta, który pokazuje łamanie stron, na które ma wpływ , i link do posta, który pokazuje mikrotypografię dotkniętą problemem (co może również wpływać na łamanie linii w TeX).

Biorąc pod uwagę podejście 2) wymaga iteracji po całej zawartości list matematycznych, byłoby wolniejsze w porównaniu do 1), gdyby zostało wywołane za pomocą filtra mlist_to_hlist? (Chyba że prawdopodobnie $<tex_equation>$można bezpiecznie przedefiniować, aby ustawić i usunąć atrybut przed i za $separatorem, chociaż takich separatorów jest więcej ...). Pytanie brzmi więc, jak można osiągnąć wyznaczenie granicy matematycznej za pomocą podejścia 1) tak, aby naprawdę nie wpłynęło to na mikrotypografię, łamanie linii i łamanie stron? A może podejście 2) to jedyny bezpieczny zakład?

Oto moja pierwsza próba matematyki inline, szukam recenzji eksperta i rozszerzenia do matematyki wyświetlania (środowiska wyrównywania itp.).

Kod:

% >>> 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}

Konsola (poszukaj 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

Odpowiedzi

7 MarcelKrüger Nov 21 2020 at 16:09

Patrząc na listy węzłów po przełamaniu linii, węzły graniczne nie są niezawodnym rozwiązaniem. Ma to co najmniej dwa powody:

  1. Każdy węzeł można odrzucić lub nie. Jeśli twój węzeł graniczny jest możliwy do odrzucenia, pojawia się ten sam problem, co w przypadku węzłów włączania / wyłączania matematyki: znikają na początku wiersza. Zatem żądanego węzła nie można odrzucić, ale pamiętaj o zasadzie dotyczącej odrzucanych węzłów na początku wiersza: po zakończeniu wiersza wszystkie odrzucalne węzły są usuwane, aż napotkany zostanie pierwszy niemożliwy do odrzucenia węzeł. W szczególności oznacza to, że nowy węzeł graniczny, którego nie można pominąć, powstrzymałby usuwanie potencjalnie następujących po usunięciu węzłów, wpływając na wynik. (Są inne powody, dla których dodanie tutaj węzła nieodrzucanego, np. Whatsit, zawsze wpłynie na łamanie linii, ale ten jest najłatwiejszy do opisania)

  2. Gdy pojawi się łamanie linii, możesz mieć listę hlist zawierającą matematykę, ale nie zawierającą żadnych granic matematycznych. Dość skrajnym przykładem byłoby:

    \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}
    

    Tutaj pole 1 zawiera tylko środkową część wzoru matematycznego, więc nie ma powodu, aby zawierało jakikolwiek węzeł graniczny. Jednak nadal jest to matematyka.

    (Wariantem tego, który jest bardziej prawdopodobny w praktyce, jest formuła matematyczna dzieląca strony. Druga strona nie będzie miała początkowego węzła matematycznego, ponieważ tam nie zaczyna się matematyka).

Więc co można z tym zrobić? Istnieją dwie możliwości:

  1. Chociaż Twoje podejście 1) nie może działać niezawodnie, możesz zaimplementować 2) i dodać atrybut. Możesz po prostu iterować po liście węzłów w post_mlist_to_hlist_filter¹, aby to dodać, wtedy zostaną omówione zarówno matematyka w wierszu , jak i wyświetlanie.
  2. Zrób wszystko, co chcesz zrobić przed przełamaniem linii i po prostu użyj istniejących węzłów granic matematyki. W większości przypadków i tak chcesz trochę popracować przed przełamaniem linii, więc jest to całkiem naturalne.

W zależności od konkretnego przypadku użycia, jedno z rozwiązań będzie często całkiem naturalne. Np. Wyodrębnianie tekstu powinno prawie zawsze opierać się na tekście sprzed przełamania linii (lub nawet wcześniejszego ukształtowania), więc pasuje dobrze do 2, podczas gdy efektywne dodawanie różnych kolorów dla węzłów matematycznych oznacza po prostu dodanie atrybutu i tak, więc pasuje naturalnie do 1 .

¹: W nowoczesnych wersjach LuaLaTeX, mlist_to_hlistprawie nigdy nie powinno się ustawiać bezpośrednio, ale zamiast tego pre_mlist_to_hlist_filterlub post_mlist_to_hlist_filterpowinno być używane. Mają w zasadzie ten sam interfejs, co mlist_to_hlist, ale nie musisz dzwonić do node.mlist_to_hlistsiebie, a Twój kod pozostaje kompatybilny z innymi pakietami używającymi tych wywołań zwrotnych. Na przykład kod w pytaniu można dostosować, zastępując

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

z

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

i wymiana

return node.mlist_to_hlist(h,d,p)

z tylko

return h