
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
\hbox
e\raise
ele - adicione alguns kernels antes ou depois
- manipular sua aparência com
\pdfliteral
s
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 nochickenize
pacote. 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_char
que 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
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_char
funçã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_filter
uma macro TeX em cada caractere que coloca o resultado da transformação desejada em um \box
registro 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:
- Colocar o código em uma corrotina
co
e 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]
- 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_filter
usando a lista salva . Mas isso é tarefa para outro dia...