Cuaderno de GNUtas

Tablas con filas «multicolumna» en Org Mode (un apaño)

El sistema de tablas en texto plano que proporciona Org Mode es extremadamente potente, además de que resulta una delicia tener en un periquete una tabla armada con un par de pulsaciones de teclado. No obstante, también adolece de ciertas limitaciones menores, especialmente en lo que atañe a la disposición de la tabla en la exportación a otros formatos. Son, ya digo, pequeños inconvenientes que no deslucen en absoluto el conjunto, y que probablemente algunos de ellos sean resueltos en futuras versiones del modo del unicornio. Entre todos estos pecata minuta una funcionalidad que echo mucho en falta, por ejemplo, es la de poder construir tablas con filas multicolumna, es decir, filas que abarquen todas o una parte de las columnas de una tabla. Una forma de salir del paso de este escollo es usar dentro de Org el otro constructor de tablas que se incluye en GNU Emacs, el paquete table.el, que sí permite expansión de filas y de celdas, entre otras bondades, pero que es algo latoso de usar o de domar, y menos intuitivo y ágil que el sistema tabular de Org. Y lo peor es que nos veremos bastante limitados para controlar el formato de estas tablas, ya que el soporte que presta Org a table.el es muy escueto. Si puedo, prefiero no usar esta opción. Pero por suerte podemos ensayar otros apaños, gracias a un poco de Elisp y las facilidades que Org nos presta para fabricarnos nuestros propios filtros de exportación.

Contamos, en efecto, con un buen ramillete de hooks cuyas funciones se evalúan en el pre-proceso, y que se enfocan en distintos aspectos de la exportación o de las partes del documento. El que aquí nos interesa es el que está focalizado en las tablas: org-export-filter-table-functions. A este hook le anclaremos una función típica de sustitución condicional (el principal argumento será la o las tablas de nuestro documento) que se encargue de reemplazar por expresiones regulares (sí, las gratas expresiones regulares: no queda otra) una marcas textuales que nos sacaremos enseguida del sombrero, a fin de generar el formato de salida que nos apetece. Partamos, por ejemplo, de que lo que queremos es exportar nuestras tablas con filas expandidas a LATEX. Si en nuestra tabla de (pongamos por caso) cinco columnas deseamos que la primera fila ocupe todas las columnas, nuestra meta entonces es llegar a la macro de LATEX:

\multicolumn{5}{|c|}{nuestro texto de la fila} \\

Refrescando un poco la memoria latexiana, la macro multicolumn indica que queremos una fila que se extienda por cinco columnas (primer argumento), que su contenido esté centrado y entre sendas líneas (segundo argumento) y que el contenido de esa fila expandida no sea otro que el que encerramos en el tercer argumento. Para llegar a esto, la notación de marcas que nos sacamos del gorro para nuestro apaño sería el siguiente:

z-nuestro texto de la fila-z,5,c

No hace falta ser un lince para traducirlo: el texto de la fila encerrado entre z-...-z, una coma, el número de columnas a expandir, otra coma y por último la marca de centrado que precisa el código de LATEX. Pero el apaño aún no está completo del todo: ¿cómo metemos una celda espandida dentro de la tabla de Org si, sencillamente, no podemos hacer tal cosa? Solución un pelín sucia, pero es lo que hay: dejamos el resto de celdas de la fila vacías. Lo interesante (al menos a efectos visuales) es que nuestra celda llena quede en el centro, aunque no es necesario . Así pues, nuestra tabla quedaría como ésta:

|-------+-------+----------------------------------+-------+-------|
|       |       | z-nuestro texto de la fila-z,5,c |       |       |
|-------+-------+----------------------------------+-------+-------|
| celda | celda | celda                            | celda | celda |
| celda | celda | celda                            | celda | celda |
| celda | celda | celda                            | celda | celda |
|-------+-------+----------------------------------+-------+-------|

Ya sólo nos queda escribir nuestra función, que podemos incluir directamente en nuestro documento de Org como bloque de código y anclarla al hook que queremos mediante la marca #+BIND::

#+BIND: org-export-filter-table-functions (tabla-latex)
#+begin_src emacs-lisp :exports results :results none
  (defun tabla-latex (texto backend info)
    "modificaciones entorno `tabular"
    (when (org-export-derived-backend-p backend 'latex)
      (replace-regexp-in-string  "[&\s]*z-\\(.+\\)-z,\\([[:digit:]]+\\),\\(.\\)[&\s]*" "\\\\multicolumn{\\2}{|\\3|}{\\1}" texto)))
#+end_src

Si exportamos el documento (y aceptados los permisos que nos solicite Emacs para evaluar la función), el código LATEX resultante será, por fin:

\begin{tabular}{lllll}
\hline
\multicolumn{5}{|c|}{nuestro texto de la fila}\\
\hline
celda & celda & celda & celda & celda\\
celda & celda & celda & celda & celda\\
celda & celda & celda & celda & celda\\
\hline
\end{tabular}

¿Y si queremos que la tabla se exporte a HTML con las mismas marcas que nos habíamos sacado del sombrero, y con parejo resultado? Pues entonces la función que tendríamos que anclar al hook, teniendo en cuenta el código HTML que queremos lograr, podría parecerse a esta otra:

#+BIND: org-export-filter-table-functions (tabla-html)
#+begin_src emacs-lisp :exports results :results none
(defun tabla-html (texto backend info)
    "modificaciones tablas html"
 (when (org-export-derived-backend-p backend 'html)
      (replace-regexp-in-string  "<th\sscope=\"col\"\sclass=\"org-left\">z-\\(.+\\)-z,\\(.\\),.</th>"
                                 "<th colspan=\"\\2\"\sscope=\"col\"\sclass=\"org-center\">\\1</th>"
        (replace-regexp-in-string "<.+>&#xa0;<.+>" "" texto))))
#+end_src

Al exportar a HTML, obtendríamos nuestra tabla con la primera fila multicolumna:

tabla-html.png

Es evidente que no es la más elegante de las soluciones, pero, al menos como queríamos, nos hace el apaño, en espera de que nuestro querido Org Mode pueda contar algún día con una funcionalidad tan grata como es la de las filas expandidas en una tabla.

Actualización de 17/11/20: para multicolumnas parciales

Podemos ensayar esta otra posibilidad en el código, al menos en la salida a LATEX, que permite multicolumnas parciales y además simplifica considerablemente el regexp. Basta con rellenar, como en el caso anterior, una sola celda (con las marcas que hemos comentado), y poner en las celdas vacías de esa misma fila los signos «!!».

#+BIND: org-export-filter-table-functions (tabla-latex)
#+begin_src emacs-lisp :exports results :results none
  (defun tabla-latex (texto backend info)
    "modificaciones entorno `tabular"
    (when (org-export-derived-backend-p backend 'latex)
      (replace-regexp-in-string  "z-\\(.+\\)-z,\\([[:digit:]]+\\),\\(.\\)" "\\\\multicolumn{\\2}{\\3}{\\1}"
                                 (replace-regexp-in-string  "!!\s+&" ""
                                 (replace-regexp-in-string  "&\s+!!" ""
#+end_src

Y nuestra tabla, con una fila multicolumna de tres celdas, quedaría así:

|---------------------------------------+-------+-------+-------+-------|
| z-texto que ocupa tres columnas-z,3,c | !!    | !!    | !!    | !!    |
|---------------------------------------+-------+-------+-------+-------|
| celda                                 | celda | celda | celda | celda |
| celda                                 | celda | celda | celda | celda |
| celda                                 | celda | celda | celda | celda |
|---------------------------------------+-------+-------+-------+-------|

Produciendo este código en LATEX:

\begin{center}
\begin{tabular}{lllll}
\hline
\multicolumn{3}{c}{texto que ocupa tres columnas}    \\
\hline
celda & celda & celda & celda & celda\\
celda & celda & celda & celda & celda\\
celda & celda & celda & celda & celda\\
\hline
\end{tabular}
\end{center}

Publicado: 14/04/20

Última actualización: 21/01/22


Índice general

Acerca de...

Esta obra está bajo una licencia de Creative Commons Reconocimiento-NoComercial 4.0 Internacional.

© Juan Manuel Macías
Creado con esmero en
GNU Emacs