在活動字符處連字符

在活動字符處連字符

假設我想讓某些字母處於活動狀態並用它們做一些不正當的事情,例如

\catcode`y=13
\defy{\leavevmode\raise.1ex\hbox{\char`y}}

(我知道這很糟糕,因為它禁止使用包含“y”的控制序列。)
現在我希望 TeX 仍然考慮通常在“y”處連字符(聚乙稀)。我知道 TeX 不會破壞包含 an 的單詞\hbox,但是是否有可能欺騙 TeX 認為只有一個無辜的字母?為什麼我這麼認為可能有可能:

  • 在尋找合適的斷點時,TeX 本身並不關心單字中的字元是否處於活動狀態;
  • 當活動字元出現時,它仍然知道哪個字母應該在那裡。

鼓勵使用盡可能通用的解決方案(即,無論活動角色做什麼),但如果這不可行,我可能想讓這樣的活動角色做什麼:

  • 正常列印自身
  • 將自身插入到 an 中\hbox並且\raiseit
  • 在其之前或之後添加一些緊排
  • \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 程式碼之後執行,並且並發交互的方法在這個問題在我看來,不適用於這種情況:

  1. 將程式碼放入協程co並讓它調用
tex.print("\\directlua{coroutine.resume(co)}")
coroutine.yield()

每當 TeX 在繼續處理 14 個字元之前應該執行一些宏,之後我得到
! TeX capacity exceeded, sorry [text input levels=15]

  1. 在上面連結問題的答案中,\loopTeX 程式碼中使用 a 來重複恢復協程,如果要在回調中使用 Lua 程式碼,這顯然不起作用。

作為最後的手段,我只看到使字元處於活動狀態的可能性,保存段落 hlist 並暫時用包含 中原始字元的字形節點替換由活動字元建立的框,然後使用已儲存的清單pre_linebreak_filter將它們變更回來post_linebreak_filter

相關內容