アクティブな文字でのハイフネーション

アクティブな文字でのハイフネーション

特定の文字をアクティブにして、次のような不正行為をしたいとします。

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

(これは「y」を含む制御シーケンスの使用を禁止する点でひどいことだとわかっています。)
では、TeX が「y」でハイフンを付けるのを通常どおり考慮してくれることを望みます (ポリエチレン)。TeX が を含む単語を分割しないことはわかっています\hboxが、TeX に無害な文字があると思わせることは可能でしょうか? なぜそう思うのかかもしれない可能になる:

  • TeX は、適切なブレークポイントを探すときに、単語内の文字がアクティブであるかどうかは、それ自体では気にしません。
  • アクティブ文字が発生した時点では、そこにどの文字が存在するべきかがまだ分かっています。

できるだけ一般的なソリューションが推奨されます (つまり、アクティブ キャラクターが何をするかに関係なく)。ただし、それが実現できない場合は、次のようなアクティブ キャラクターに実行させたいことがあります。

  • 正常に印刷される
  • 挿入する\hbox\raise
  • その前か後にカーニングを追加する
  • 外観を\pdfliteralsで操作する

答え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.printLuaコードの後に​​のみ実行され、この質問私には、この状況には当てはまらないように思われます。

  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。しかし、それは別の日に行う作業です...

関連情報