
Nehmen wir an, ich möchte bestimmte Buchstaben aktivieren und damit ein paar abwegige Dinge anstellen, wie
\catcode`y=13
\defy{\leavevmode\raise.1ex\hbox{\char`y}}
(Ich weiß, dass das schrecklich ist, da es die Verwendung von Steuersequenzen, die ein „y“ enthalten, verbietet.)
Nun möchte ich, dass TeX die Silbentrennung am „y“ weiterhin normal berücksichtigt (Polyethylen). Ich weiß, dass TeX ein Wort, das ein\hbox
, aber ist es möglich, TeX dazu zu bringen, zu glauben, dass es sich nur um einen harmlosen Buchstaben handelt? Warum ich denke, dass eskönntemöglich sein:
- Ob die Zeichen eines Wortes aktiv sind oder nicht, ist TeX bei der Suche nach einem geeigneten Haltepunkt grundsätzlich egal;
- zum Zeitpunkt des Auftretens des aktiven Zeichens ist noch bekannt, welcher Buchstabe dort stehen sollte.
Eine möglichst allgemeine Lösung wird empfohlen (d. h. unabhängig von der Funktion des aktiven Charakters). Wenn dies jedoch nicht umsetzbar ist, möchte ich einen aktiven Charakter möglicherweise folgendermaßen ausführen lassen:
- druckt sich normal
- fügt sich in ein
\hbox
und\raise
es - füge davor oder danach ein paar Kerns hinzu
- manipulieren Sie sein Aussehen mit
\pdfliteral
s
Antwort1
Mit LuaTeX ist es möglich, die Aktivierung der Buchstaben zu vermeiden und sie erst nach der Silbentrennung zu bearbeiten, wie Ulrike Fischer in den Kommentaren anmerkte.
Nachfolgend sehen Sie eine Implementierung dieses Ansatzes, inspiriert von derchickenize
Paket. Da dies das erste Mal ist, dass ich Code in Lua schreibe, sind alle Vorschläge willkommen.
transform.lua
Zuerst definiere ich eine Funktion, die die Glyphenknoten in einer Liste durchläuft und prüft, ob das Zeichen einen Eintrag in einer Tabelle namens hat chartbl
. In diesem Fall ruft sie eine Funktion auf transform_char
, die die Werte in der Tabelle verwendet, um den Glyphenknoten zu bearbeiten. Diese Funktion wird dann als registriert post_linebreak_filter
, sodass sie auf eine Absatzliste angewendet wird, nachdem sie in Zeilen aufgeteilt wurde (und somit Silbentrennungsmuster befolgt):
transform_chars = function(head)
for l in node.traverse_id(node.id("hhead"),head) do
for n in node.traverse_id(node.id("glyph"),l.head) do
chr = n.char
if chartbl[chr] ~= nil then
transformed = transform_char(n)
l.head = node.insert_before(l.head,n,node.copy(transformed))
node.remove(l.head,n)
end
end
end
return head
end
callback.register("post_linebreak_filter",transform_chars)
Nun transform_char(n)
kann noch an die jeweiligen Bedürfnisse angepasst werden. In diesem Fall fügen wir vor und nach dem Zeichen einen Kern und ein PDF-Literal ein und verschieben das Zeichen virtuell:
transform_char = function(c)
kbfn = node.new(node.id("kern")) -- additional kern before char
pdfbfn = node.new(node.id("whatsit"),node.subtype("pdf_literal")) -- pdf literal before
cn = node.new(node.id("glyph")) -- char
cn = node.copy(c)
pdfan = node.new(node.id("whatsit"),node.subtype("pdf_literal")) -- pdf literal after
kan = node.new(node.id("kern")) -- additional kern after char
tbl = chartbl[c.char]
kbfn.kern = tex.sp(tbl["kbf"])
pdfbfn.data = tbl["pdfbf"]
cn.xoffset = tex.sp(tbl["xoff"])
cn.yoffset = tex.sp(tbl["yoff"])
pdfan.data = tbl["pdfa"]
kan.kern = tex.sp(tbl["ka"])
kbfn.next = pdfbfn
pdfbfn.next = cn
cn.next = pdfan
pdfan.next = kan
t = node.hpack(kbfn)
return t
end
Die Werte für die einzelnen Operationen werden hier gespeichert chartbl
:
chartbl = {
[string.byte("y")] = {
["kbf"] = "-0.1em",
["pdfbf"] = "-1 0 0 1 5.5 0 cm",
["xoff"] = "0ex",
["yoff"] = "0.5ex",
["pdfa"] = "-1 0 0 1 -5.5 0 cm",
["ka"] = "0.2em"
}
}
Beispiel:
\directlua{dofile("transform.lua")}
\hsize1cm
\noindent polyethylene
Zur Dokumentation: Im Prinzip ist dieser Ansatz eine allgemeine Lösung, da, soweit ich verstanden habe, alles, was TeX mit einem Zeichen machen möchte, auch in Lua gemacht werden kann, mit möglichen Änderungen an der transform_char
Funktion. Bei komplexeren Aufgaben scheint dies jedoch schwieriger zu sein, als es von TeX setzen zu lassen.
Ich habe also zunächst versucht, post_linebreak_filter
für jedes Zeichen ein TeX-Makro aufzurufen, das das Ergebnis der gewünschten Transformation in ein \box
Register einträgt, und dann den Knoten von Lua durch dieses Feld ersetzen zu lassen.
Ich denke, das ist unmöglich. Jeder von aufgerufene Code tex.print
wird erst nach dem Lua-Code ausgeführt, und die indiese Fragesind, wie mir scheint, auf diese Situation nicht anwendbar:
- Den Code in eine Coroutine einfügen
co
und aufrufen lassen
tex.print("\\directlua{coroutine.resume(co)}")
coroutine.yield()
wenn TeX ein Makro ausführen soll, bevor es weitergeht, funktioniert es für 14 Zeichen, danach bekomme ich
! TeX capacity exceeded, sorry [text input levels=15]
- In der Antwort auf die oben verlinkte Frage
\loop
wird im TeX-Code ein verwendet, um die Coroutine immer wieder fortzusetzen, was offensichtlich nicht funktioniert, wenn der Lua-Code in einem Callback verwendet werden soll.
Als letzten Ausweg sehe ich nur die Möglichkeit, die Zeichen zu aktivieren, die Absatzliste zu speichern und die vom aktiven Zeichen erstellten Kästchen vorübergehend durch einen Glyphenknoten zu ersetzen, der das ursprüngliche Zeichen enthält pre_linebreak_filter
, und sie post_linebreak_filter
mithilfe der gespeicherten Liste wieder in zu ändern. Aber das ist eine Aufgabe für einen anderen Tag ...