Separación de guiones en caracteres activos

Separación de guiones en caracteres activos

Digamos que quiero activar ciertas letras y hacer algunas cosas tortuosas con ellas, como

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

(Sé que esto es terrible porque prohíbe el uso de secuencias de control que contengan una "y".)
Ahora me gustaría que TeX todavía considere la posibilidad de separar guiones en la "y" normalmente (polietileno). Sé que TeX no dividirá una palabra que contenga una\hbox , pero ¿es posible engañar a TeX haciéndole creer que solo hay una letra inocente? ¿Por qué lo pienso?podríaser posible:

  • A TeX no le importa per se si los caracteres de una palabra están activos o no cuando busca un punto de interrupción adecuado;
  • en el momento en que se produce el carácter activo, todavía sabe qué letra debería estar allí.

Se recomienda una solución lo más general posible (es decir, independientemente de lo que haga el personaje activo), pero si eso no es factible, esto es lo que me gustaría que hiciera ese personaje activo:

  • imprimirse normalmente
  • insertarse en un \hboxy\raise
  • agregue algunos kerns antes o después
  • manipular su apariencia con \pdfliterals

Respuesta1

Usando LuaTeX, es posible evitar activar las letras y simplemente manipularlas después de la separación de palabras, como señaló Ulrike Fischer en los comentarios.

A continuación se muestra una implementación de este enfoque, inspirada en elchickenizepaquete. Dado que esta es la primera vez que escribo código en Lua, cualquier sugerencia es bienvenida.

transform.lua

Primero, defino una función que itera a través de los nodos de glifo en una lista y verifica si el carácter tiene una entrada en una tabla llamada chartbl, en cuyo caso llama a una función transform_charque usa los valores de la tabla para manipular el nodo de glifo. Luego, esta función se registra como post_linebreak_filter, de modo que se aplicará a una lista de párrafos después de dividirla en líneas (por lo tanto, obedeciendo patrones de separación de palabras):

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)

Ahora transform_char(n)se puede adaptar a las necesidades específicas. En este caso, agregamos un kern y un pdfliteral antes y después del carácter y virtualmente cambiamos el carácter:

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

Los valores para cada una de las operaciones se almacenan en 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"
  }
}

Ejemplo:

\directlua{dofile("transform.lua")}
\hsize1cm
\noindent polyethylene

polietileno


Para documentación: en principio, este enfoque es una solución general ya que, hasta donde tengo entendido, todo lo que TeX quiera hacer con un personaje también se puede hacer en Lua, con posibles cambios en la transform_charfunción. Sin embargo, para tareas más complejas, esto parece ser más difícil de hacer que escribirlo en TeX.

Entonces, lo que inicialmente intenté hacer fue llamar post_linebreak_filtera una macro TeX en cada carácter que coloca el resultado de la transformación deseada en un \boxregistro y luego hacer que Lua reemplace el nodo por ese cuadro.

Creo que esto es imposible. Cualquier código llamado por tex.printsolo se ejecuta después del código Lua, y los enfoques para una interacción concurrente discutidos enesta preguntason, según me parece, no aplicables a esta situación:

  1. Poner el código en una rutina coy hacer que llame
tex.print("\\directlua{coroutine.resume(co)}")
coroutine.yield()

cada vez que TeX debe ejecutar alguna macro antes de continuar funciona para 14 caracteres, después de lo cual aparece
! TeX capacity exceeded, sorry [text input levels=15]

  1. En la respuesta a la pregunta vinculada anteriormente, \loopse usa a en el código TeX para reanudar repetidamente la rutina, lo que obviamente no funciona si el código Lua se va a usar en una devolución de llamada.

Como último recurso, solo veo la posibilidad de activar los caracteres, guardar la lista de párrafos y reemplazar temporalmente los cuadros creados por el personaje activo con un nodo de glifo que contenga el carácter original en pre_linebreak_filtery volver a cambiarlos post_linebreak_filterusando la lista guardada. . Pero eso es tarea para otro día...

información relacionada