
假設我想讓某些字母處於活動狀態並用它們做一些不正當的事情,例如
\catcode`y=13
\defy{\leavevmode\raise.1ex\hbox{\char`y}}
(我知道這很糟糕,因為它禁止使用包含“y”的控制序列。)
現在我希望 TeX 仍然考慮通常在“y”處連字符(聚乙稀)。我知道 TeX 不會破壞包含 an 的單詞\hbox
,但是是否有可能欺騙 TeX 認為只有一個無辜的字母?為什麼我這麼認為可能有可能:
- 在尋找合適的斷點時,TeX 本身並不關心單字中的字元是否處於活動狀態;
- 當活動字元出現時,它仍然知道哪個字母應該在那裡。
鼓勵使用盡可能通用的解決方案(即,無論活動角色做什麼),但如果這不可行,我可能想讓這樣的活動角色做什麼:
- 正常列印自身
- 將自身插入到 an 中
\hbox
並且\raise
it - 在其之前或之後添加一些緊排
\pdfliteral
用s操縱它的外觀
答案1
正如 Ulrike Fischer 在評論中指出的那樣,使用 LuaTeX 可以避免激活字母,而只是在連字符後面操作它們。
以下是此方法的實現,其靈感來自於chickenize
包裹。由於這是我第一次用Lua寫程式碼,歡迎任何建議。
transform.lua
首先,我定義一個函數,該函數迭代列表中的字形節點,並檢查該字元是否在名為 的表中具有條目chartbl
,在這種情況下,它會呼叫一個transform_char
使用表中的值來操作字形節點的函數。然後將此函數註冊為 a post_linebreak_filter
,以便在將其分成行後將其應用於段落列表(因此遵循連字符模式):
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)
現在transform_char(n)
可以適應特定的需求。在本例中,我們在字元前後添加緊排和 pdfliteral,並虛擬地移動字元:
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
每個操作的值儲存在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"
}
}
例子:
\directlua{dofile("transform.lua")}
\hsize1cm
\noindent polyethylene
對於文件:原則上,這種方法是一個通用的解決方案,因為據我了解,TeX 可能想要對字元執行的所有操作也可以在 Lua 中完成,並且可能對函數進行更改transform_char
。然而,對於更複雜的任務,這似乎比讓 TeX 排版更困難。
所以我最初嘗試做的是post_linebreak_filter
在每個字元上呼叫 TeX 宏,將所需轉換的結果放入暫存器中\box
,然後讓 Lua 用該框取代節點。
我認為這是不可能的。呼叫的任何程式碼tex.print
僅在 Lua 程式碼之後執行,並且並發交互的方法在這個問題在我看來,不適用於這種情況:
- 將程式碼放入協程
co
並讓它調用
tex.print("\\directlua{coroutine.resume(co)}")
coroutine.yield()
每當 TeX 在繼續處理 14 個字元之前應該執行一些宏,之後我得到
! TeX capacity exceeded, sorry [text input levels=15]
- 在上面連結問題的答案中,
\loop
TeX 程式碼中使用 a 來重複恢復協程,如果要在回調中使用 Lua 程式碼,這顯然不起作用。
作為最後的手段,我只看到使字元處於活動狀態的可能性,保存段落 hlist 並暫時用包含 中原始字元的字形節點替換由活動字元建立的框,然後使用已儲存的清單pre_linebreak_filter
將它們變更回來post_linebreak_filter