Hifenizando caracteres ativos

Hifenizando caracteres ativos

Digamos que eu queira ativar certas letras e fazer algumas coisas tortuosas com elas, como

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

(Eu sei que isso é terrível porque proíbe o uso de sequências de controle contendo um "y".)
Agora eu gostaria que o TeX ainda considerasse a hifenização no "y" normalmente (polietileno). Eu sei que o TeX não quebrará uma palavra que contenha um \hbox, mas é possível fazer com que o TeX pense que existe apenas uma letra inocente? Por que eu acho issopoderseja possível:

  • O TeX não se importa se os caracteres de uma palavra estão ativos ou não ao procurar um ponto de interrupção adequado;
  • no momento em que ocorre o caractere ativo, ele ainda sabe qual letra deveria estar lá.

Uma solução tão geral quanto possível é encorajada (ou seja, independentemente do que o personagem ativo faz), mas se isso não for viável, aqui está o que eu poderia querer que esse personagem ativo fizesse:

  • imprimir sozinho normalmente
  • insira-se em um \hboxe \raiseele
  • adicione alguns kernels antes ou depois
  • manipular sua aparência com \pdfliterals

Responder1

Usando LuaTeX, é possível evitar a ativação das letras e apenas manipulá-las após a hifenização, como Ulrike Fischer apontou nos comentários.

A seguir está uma implementação desta abordagem, inspirada nochickenizepacote. Como esta é a primeira vez que escrevo código em Lua, qualquer sugestão é bem-vinda.

transform.lua

Primeiro, defino uma função que itera pelos nós do glifo em uma lista e verifica se o caractere tem uma entrada em uma tabela chamada chartbl; nesse caso, ela chama uma função transform_charque usa os valores da tabela para manipular o nó do glifo. Esta função é então registrada como post_linebreak_filter, para que seja aplicada a uma lista de parágrafos depois de dividida em linhas (obedecendo, portanto, aos padrões de hifenização):

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)

Agora transform_char(n)pode ser adaptado às necessidades específicas. Neste caso, adicionamos um kern e um pdfliteral antes e depois do caractere e mudamos virtualmente o caractere:

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

Os valores para cada uma das operações são armazenados em 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"
  }
}

Exemplo:

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

polietileno


Para documentação: Em princípio, esta abordagem é uma solução geral, pois, pelo que entendi, tudo o que o TeX pode querer fazer com um caractere também pode ser feito em Lua, com possíveis alterações na transform_charfunção. No entanto, para tarefas mais complexas, isso parece ser mais difícil de fazer do que ter o TeX digitado.

Então, o que inicialmente tentei fazer foi chamar post_linebreak_filteruma macro TeX em cada caractere que coloca o resultado da transformação desejada em um \boxregistro e então fazer com que Lua substitua o nó por aquela caixa.

Eu acho que isso é impossível. Qualquer código chamado por tex.printé executado somente após o código Lua, e as abordagens para uma interação concorrente discutidas emessa questãonão são, como me parece, aplicáveis ​​a esta situação:

  1. Colocar o código em uma corrotina coe fazer com que ele chame
tex.print("\\directlua{coroutine.resume(co)}")
coroutine.yield()

sempre que o TeX deve executar alguma macro antes de continuar funciona por 14 caracteres, após o que recebo
! TeX capacity exceeded, sorry [text input levels=15]

  1. Na resposta à pergunta acima, a \loopé usado no código TeX para retomar repetidamente a corrotina, o que obviamente não funciona se o código Lua for usado em um retorno de chamada.

Como último recurso, vejo apenas a possibilidade de tornar os caracteres ativos, salvando o parágrafo hlist e substituindo temporariamente as caixas construídas pelo caracter ativo por um nó de glifo contendo o caracter original em pre_linebreak_filter, e alterando-os novamente no post_linebreak_filterusando a lista salva . Mas isso é tarefa para outro dia...

informação relacionada