Aprovechar el compilador TeX para escribir un convertidor TeX a UTF8

Aprovechar el compilador TeX para escribir un convertidor TeX a UTF8

Manejar la mayor parte del texto en UTF-8 en lugar de cadenas TeX tiene muchas ventajas, no solo es fácil para la vista, los dedos y los editores, sino que también resulta muy fácil enviar su texto a los correctores ortográficos, correctores gramaticales y otros analizadores. .... imagínate escribiendo esta cadena

 ĄąĆćĘę£łŃńÓóŚś-źŻż

Usar estrategias simples de búsqueda/reemplazo puede ser desastroso debido a cosas como:

 \def\L{\matbb{L}}

dejando errores enterrados en lo más profundo de los archivos convertidos. Sin mencionar cuán profundamente podría quedar enterrada la definición del personaje.

Entiendo que uno puede identificar de forma única a los personajes (como enaquí) y esopandoc,tex4htyhiperreferenciaabordar este problema en algún nivel. Mi pregunta es: ¿Qué tan viable sería implementar dicho convertidor en el propio TeX?

(agregado por David)

aporte:

\documentclass{article}

\newcommand\zzz{hello}

\begin{document}

\L\"{o}\"{o}\c{k} \zzz

\renewcommand\L{LLL}
\renewcommand\"[1]{#1#1}
\renewcommand\c{c}

\L\"{o}\"{o}\c{k} \zzz

\end{document}

Convertido al formulario con texto UTF-8, pero se ejecuta el uso de macros:

\documentclass{article}

\newcommand\zzz{hello}

\begin{document}

Łööķ \zzz

\renewcommand\L{LLL}
\renewcommand\"[1]{#1#1}
\renewcommand\c{c}

\L\"{o}\"{o}\c{k} \zzz

\end{document}

Respuesta1

En primer lugar, si prefiere una entrada como ĄąĆćĘę£łŃńÓóŚś-źŻżen su .texarchivo, simplemente puede escribirla (o pegarla). Todo lo que necesita es \usepackage[utf8]{inputenc}si está usando pdfTeX, o ni siquiera eso si usa un motor compatible con Unicode (XeTeX o LuaTeX). ). Por ejemplo, lo siguiente simplemente funciona (cuando se compila con xelatex):

\documentclass{article}
\begin{document}
ĄąĆćĘę£łŃńÓóŚś-źŻż
\end{document}

producción

Si el problema es que no tiene una distribución de teclado conveniente (o fácil de recordar) para escribir eso, entonces prefiere escribir usando las macros TeX (pero aún así prefiere que el archivo contenga caracteres como los anteriores), entonces esto es simplemente una cuestión de configurar su editor o sistema de entrada. Por ejemplo (sugerido en elcomentariospor el usuario Loop Space), Emacs puede hacer esto, con M-x set-input-method RET TeX: cuando presionas las teclas \=ode tu teclado, lo que se escribe en el archivo esō . No es necesario utilizar Emacs; Este tipo de característica también está disponible en métodos de entrada como UIM (ejemplo).

Así que no veo ninguna razón para usar TeX para realizar dicha conversión, si estás creando el .texarchivo: sería mejor encontrar una manera de insertar tus caracteres preferidos en primer lugar.


Sin embargo, la pregunta puede tener sentido si está trabajando con un .texarchivo creado por otra persona (y está bien que cambie el archivo), o si lo creó usted mismo antes de tener esta preferencia.

Lo principal que ofrece el uso de TeX (en lugar de una simple búsqueda y reemplazo en su editor) es la capacidad de saber cuándo las definiciones de macros gustan \Ly \Ohan cambiado. Este es también el problema ilustrado en la pregunta.

Entonces, para resolver esto, tengo la siguiente solución usando la introspectiva (también conocida comoreflexivo) capacidades que vienen con LuaTeX: específicamente, token.get_macroque nos permite ver las definiciones de macros y la process_input_bufferdevolución de llamada que nos permite examinar cada línea de entrada (y cambiarla si queremos). La idea es:

  • Antes de que comience el texto, registre las definiciones "originales" de todas las macros de reemplazo de caracteres conocidas ( \L, \", \c, etc.). Esto nos permite saber cuándo se han redefinido.
  • Para cada línea en la entrada, busque aquellas macros que ocurren en la línea,comprobar si sus definiciones no han cambiadoy (si es así) reemplácelos junto con sus argumentos con los reemplazos apropiados.

Entonces, con el ejemplo de la pregunta, en un archivo llamado digamos mwe.tex:

\documentclass{article}
\directlua{dofile('rewrite.lua')}

\newcommand\zzz{hello}

\begin{document}

\L\"{o}\"{o}\c{k} \zzz

\renewcommand\L{LLL}
\renewcommand\"[1]{#1#1}
\renewcommand\c{c}

\L\"{o}\"{o}\c{k} \zzz

\end{document}

(tenga en cuenta la \directlua{dofile(...)}línea que se agregó), puede ejecutar lualatex mwe.tex(algunas líneas recortadas):

9:41:29:~/tmp% lualatex mwe.tex
This is LuaTeX, Version 1.0.4 (TeX Live 2017) 
...
The original definition of #\L# is \TU-cmd \L \TU\L 
The original definition of #\c# is \TU-cmd \c \TU\c 
The original definition of #\"# is \TU-cmd \"\TU\" 
...
Processing line: \begin{document}
 --> Rewrote line to \begin{document}
...
Processing line: \L\"{o}\"{o}\c{k} \zzz
 --> Rewrote line to Łööķ \zzz
Processing line: 
 --> Rewrote line to 
Processing line: \renewcommand\L{LLL}
 ^ This line contains a \def or \newcommand or \renewcommand. Not rewriting.
...
Processing line: \L\"{o}\"{o}\c{k} \zzz
 --> Rewrote line to \L\"{o}\"{o}\c{k} \zzz

Y encontrarás un mwe.rewritten.texarchivo que contiene:

\newcommand\zzz{hello}

\begin{document}
\relax

Łööķ \zzz

\renewcommand\L{LLL}
\renewcommand\"[1]{#1#1}
\renewcommand\c{c}

\L\"{o}\"{o}\c{k} \zzz

\end{document}
\relax

donde se puede ver que solo han ocurrido los reemplazos que deberían haber ocurrido. El archivo Lua (llamado rewrite.luaarriba) que hace que esto suceda es:

print('')
rewritten_file = io.open(tex.jobname .. '.rewritten.tex', 'w')

funny_noarg = {
   ["\\L"] = "Ł",
   -- Define similarly for \oe \OE \ae \AE \aa \AA \o \O \l \i \j
}
funny_nonletter = {
   ['\\"'] = function(c) return c .. "̈" end,
   -- Define similarly for \` \' \^ \~ \= \.
}
funny_letter = {
   ["\\c"] = function(c) return c .. "̧" end,
   -- Define similarly for \u \v \H \c \d \b \t
}

orig_defs = {}
function populate_orig_defs()
   function set_def(s)
      definition = token.get_macro(s:sub(2))
      orig_defs[s] = definition
      print('The original definition of #' .. s .. '# is ' .. definition)
   end
   for s, v in pairs(funny_noarg) do set_def(s) end
   for s, v in pairs(funny_letter) do set_def(s) end
   for s, v in pairs(funny_nonletter) do set_def(s) end
end
populate_orig_defs()

function literalize(s)
   -- The string s, with special characters escaped, in a format safe for using inside gsub.
   -- https://stackoverflow.com/questions/1745448/lua-plain-string-gsub#comment18401212_1746473
   return s:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%0")
end
function replace(s)
   print('Processing line: ' .. s)
   if s:find([[\def]]) ~= nil or s:find([[\newcommand]]) ~= nil or s:find([[\renewcommand]]) ~= nil then
      print(' ^ This line contains a \\def or \\newcommand or \\renewcommand. Not rewriting.')
     rewritten_file:write(s .. '\n')
     return nil
   end
   for k, v in pairs(funny_noarg) do
      -- followed by a nonletter. TODO: Can use the catcode tables.
      if token.get_macro(k:sub(2)) == orig_defs[k] then
         s = s:gsub(literalize(k) .. '([^a-zA-Z])', function(capture) return v .. capture end)
      end
   end
   for k, v in pairs(funny_letter) do
      -- followed by a letter inside {}. TODO: Can use the catcode tables, also can support \c c, for example.
      if token.get_macro(k:sub(2)) == orig_defs[k] then
         s = s:gsub(literalize(k) .. '{(.)}', v)
      end
   end
   for k, v in pairs(funny_nonletter) do
      -- followed by a letter inside {}. TODO: We could also support \"o for example.
      if token.get_macro(k:sub(2)) == orig_defs[k] then
         s = s:gsub(literalize(k) .. '{(.)}', v)
      end
   end
   print(' --> Rewrote line to ' .. s)
   rewritten_file:write(s .. '\n')
   return nil
end

luatexbase.add_to_callback('process_input_buffer', replace, 'Replace some macros with UTF-8 equivalents')

Como esto es sólo una prueba de concepto y no un sistema de calidad de producción, tomé algunos atajos que puedes completar si estás interesado en seguir este enfoque:

  • Solo se enumeran los equivalentes Unicode para algunas de las macros con acento o caracteres especiales de TeX.
  • Debe volver a insertar la \documentclass{article}línea (y, de hecho, todo lo que tenga antes de la \directlua{dofile(…)}línea). (Para divertirte, puedes intentar mover la líneaantes \documentclassy mira lo que pasa.)
  • Probablemente quieras tener esta línea después de todas \usepackagelas líneas, tal vez al comienzo de \begin{document}. (Si ha probado lo anterior, sabrá por qué).
  • Debes eliminar la \relaxlínea al final (probablemente podríamos hacer que esto no aparezca...)
  • Se supone que el archivo de entrada contiene la convención LaTeX \={o}y no \=o; con unas cuantas líneas más podríamos apoyar también a este último. De manera similar si en lugar de \c{k}tenemos \c ko \c {k}, etc.
  • Ignora por completo (no reemplaza nada) las líneas que contienen \defo \newcommand; en cambio, si quisiéramos (¡si el archivo de entrada estuviera tan mal escrito!), podríamos saltar hasta el final \defo lo que sea, y procesar el resto.
  • Se supone que (para saber cuándo \otermina una secuencia de control como) que las “letras” son a-zA-Z; es posible que desee agregar algo @a esa lista y, de hecho, podríamos usar la definición exacta de "letra" bajo el régimen de código cat activo en ese momento; LuaTeX también proporciona eso.

Tenga en cuenta que incluso si normalmente compila su archivo con pdfTeX o XeTeX, puede usar LuaTeX solo para esta conversión y volver a usar pdfTeX/XeTeX en el archivo convertido.

información relacionada