Cuaderno de GNUtas

Ejecutar funciones en la exportación desde Org a otros formatos

Las posibilidades que nos ofrece Org Mode para exportar a otros formatos son de una variedad y un finura exquisitas. Podemos manejar variables desde la cabecera de nuestro documento, o crear y aplicar filtros de muchas maneras para contextos concretos, o añadir atributos a bloques para que lleguen con determinadas propiedades al documento meta. De entre todas esas facilidades, en fin, me resulta especialmente útil el anclaje de funciones denominado org-export-before-processing-hook, cuyo nombre es bastante elocuente y nos avisa de que su cometido es ejecutar en el momento de la exportación las funciones que le añadamos, con la salvedad (y aquí está la gracia) de que lo hará antes de que se expandan todos los comandos, e incluso de que entre en acción el exportador de Org. Esto supone que podemos manipular cualquier aspecto de nuestro documento (incluso el meramente textual) en ese lapso en que el proceso de exportación aún no se ha llevado a efecto. Partamos de un ejemplo muy sencillo para entender cómo funciona.

Imaginemos que nuestro documento de Org lo queremos exportar indistintamente y, muchas veces, a dos formatos, por ejemplo a LaTeX y a HTML. Pero deseamos, además (quién sabe por qué) que las ocurrencias de la cadena de texto «Juan Pérez» en nuestro documento pasen a ser «Sr. Pérez» sólo y únicamente cuando exportamos a HTML. Definimos, primero, una función con un simple búsqueda / reemplazo:

(defun cambiamos-a-juan (backend)
  "De Juan Pérez a Sr."
    (when  (eq backend 'html)
     (org-occur "Juan Pérez")
     (save-excursion
    (goto-char (point-min))
    (replace-regexp "Juan Pérez" "Sr. Pérez" nil))))

Y, por supuesto, añadimos la función al anclaje:

(add-hook 'org-export-before-processing-hook #'cambiamos-a-juan)

Las funciones que anclemos a este hook admiten un único argumento (backend). Antes de añadir en nuestra función la operación de búsqueda / reemplazo necesitamos llamar antes a la función de Org org-occur para que Juan Pérez se haga encontradizo en todo el documento. De lo contrario, no se ejecutará su cambio si se esconde entre los sub árboles plegados.

Todo esto, claro, en nuestro archivo de inicio. Así, cada vez que exportemos un documento Org a HTML, nuestro querido Juan Pérez obtendrá un estatus más respetable. Sin embargo, lo más probable es que queramos que estos cambios no sean globales sino aplicados a documentos en concreto. ¿Cómo hacemos? Fácil. Podemos encerrar este código en un bloque de código al estilo Org, colocarlo al principio del documento a exportar y aplicarle los atributos necesarios para que el código no se exporte sino que se evalue (y, por tanto, se ejecute) en el momento de la exportación. Ya vimos que Org puede hacer estas cosas, a cuento de la evaluación de código LaTeX. En este caso, el código a evaluar es emacs-lisp:

 #+begin_src emacs-lisp :exports code :exports results :results none
(defun cambiamos-a-juan (backend)
  "De Juan Pérez a Sr."
    (when  (eq backend 'html)
     (org-occur "Juan Pérez")
     (save-excursion
    (goto-char (point-min))
    (replace-regexp "Juan Pérez" "Sr. Pérez" nil))))
(add-hook 'org-export-before-processing-hook #'cambiamos-a-juan)
 #+end_src

En el anterior ejemplo vimos cómo modificar elementos del contenido. Pero podemos ir más lejos y alterar a conveniencia los aspectos formales de nuestro documento. Muestro aquí un caso concreto de mi trabajo. En mi traducción de la Odisea suelo exportar a LaTeX, pero en ocasiones también al formato odt de LibreOffice, y en ambos casos quiero que se inserte bajo el título la fecha actual junto a una coletilla. También quiero añadir la opción num:nil si se exporta a odt para evitar que me numere las secciones y salgan cosas como «1. Canto I; 2. Canto II; etc (eso lo evito directamente en LaTeX definiendo los títulos con titlesec, en mi clase Org de exportación a LaTeX). El código a evaluar, que tengo a la cabecera de mi Odisea luce tal que así:

#+BEGIN_SRC emacs-lisp :exports results :results none
;; código a evaluar para exportar a odt
(defun mi-filtro-odt (backend)
  "afina varias cosas para exp. a odt"
    (when  (eq backend 'odt)
    (save-excursion
    (goto-char (point-min))
    (insert "#+OPTIONS: num:nil")
	  (newline)
    (insert (format "#+DATE: En algún lugar de vuelta a Ítaca (versión para LibreOffice): %s" (format-time-string "%c")))
	  (newline))))

(add-hook 'org-export-before-processing-hook #'mi-filtro-odt)

;; código a evaluar para exportar a LaTeX
(defun mi-filtro-latex (backend)
  "añade varias cosas en la exportación a LaTeX"
    (when  (eq backend 'latex)
     (save-excursion
    (goto-char (point-min))
    (insert "#+DATE: En algún lugar de vuelta a Ítaca (version LaTeX): \\today{}")
	  (newline))))

(add-hook 'org-export-before-processing-hook #'mi-filtro-latex)
#+END_SRC

Lo que hacen estas dos funciones no es más que situarse en la cabecera del documento y añadir literalmente lo que deseo, según se exporte a LaTeX u a odt. En cuanto a la fecha, en LaTeX nos basta con el comando \today, cosa que Org hubiese añadido de oficio, pero también quería incluir mis coletillas1. En cuanto a la salida a odt, nos puede resultar útil la función format-time-string, que nos inserta la fecha en curso con las opciones que indiquemos en su argumento (en este caso "%c", para insertarla según nuestra configuración local). Todo eso lo aplicamos como segundo argumento de format. El primero es la cadena de texto donde se recoge el resultado de format-time-string (comodín %s), que a su vez la función insert imprime como literal.

Y, para terminar, un ejemplo sacado de estas propias Gnutas. Cuando introduzco un bloque example (como el que encierra el código del ejemplo anterior) quiero que en la salida a HTML tenga un determinado color de fondo. Puedo añadir justo antes del bloque el estilo de exportación como atributo:

#+ATTR_HTML: :style background-color:#fdf6e3;

Pero si automatizo eso, trabajo que me ahorro. Entonces:

#+BEGIN_SRC emacs-lisp :exports results :results none
(defun mi-filtro-html (backend)
  "añade estilo de fondo al bloque example"
   (when  (eq backend 'html)
    (org-occur "\\#\\+begin_example :exports code")
    (save-excursion
    (goto-char (point-min))
    (replace-regexp "\\#\\+begin_example :exports code" "#+ATTR_HTML: :style background-color:#fdf6e3\;\n#+ATTR_HTML: :style background-color:#fdf6e3;
#+begin_example :exports code" nil))))

(add-hook 'org-export-before-processing-hook #'mi-filtro-html)
#+END_SRC

Publicado: 22/04/2019

Última actualización: 22/04/2019


Índice general

Acerca de...

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

Notas:

1

También nos podemos contentar con la función time-stamp de Emacs.

En cualquier caso, si los filtros que queremos aplicar son generales, es decir, si queremos que se ejecuten las mismas acciones siempre para cada formato, lo más recomendable sería definir las funciones y anclarlas a org-export-before-processing-hook dentro de nuestro archivo de inicio.

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