
特定の文字をアクティブにして、次のような不正行為をしたいとします。
\catcode`y=13
\defy{\leavevmode\raise.1ex\hbox{\char`y}}
(これは「y」を含む制御シーケンスの使用を禁止する点でひどいことだとわかっています。)
では、TeX が「y」でハイフンを付けるのを通常どおり考慮してくれることを望みます (ポリエチレン)。TeX が を含む単語を分割しないことはわかっています\hbox
が、TeX に無害な文字があると思わせることは可能でしょうか? なぜそう思うのかかもしれない可能になる:
- TeX は、適切なブレークポイントを探すときに、単語内の文字がアクティブであるかどうかは、それ自体では気にしません。
- アクティブ文字が発生した時点では、そこにどの文字が存在するべきかがまだ分かっています。
できるだけ一般的なソリューションが推奨されます (つまり、アクティブ キャラクターが何をするかに関係なく)。ただし、それが実現できない場合は、次のようなアクティブ キャラクターに実行させたいことがあります。
- 正常に印刷される
- 挿入する
\hbox
と\raise
、 - その前か後にカーニングを追加する
- 外観を
\pdfliteral
sで操作する
答え1
Ulrike Fischer がコメントで指摘したように、LuaTeX を使用すると、文字をアクティブにせずに、ハイフネーション後に文字を操作することが可能になります。
以下は、このアプローチの実装例です。chickenize
パッケージ。Lua でコードを書くのは初めてなので、何かご提案があれば歓迎します。
transform.lua
まず、リスト内のグリフ ノードを反復処理し、文字が というテーブルにエントリを持っているかどうかを確認する関数を定義します。chartbl
エントリがある場合は、transform_char
テーブルの値を使用してグリフ ノードを操作する関数を呼び出します。この関数は として登録され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
。しかし、それは別の日に行う作業です...