Notxor tiene un blog

Defenestrando la vida

Publicar con org-mode II

2021-03-03

Este es un artículo de esos incoherentes en los que me dedico a contestar preguntas y peticiones de aclaración. Ya en este blog hablé de cómo publicar en html con org-mode, fue un artículo casi a vuela pluma y rápido sin detenerme en detalles y eso ha propiciado varias dudas. Además, algunos me habéis preguntado por más detalles sobre la publicación y los distintos formatos en los que podemos publicar. Además de detalles que hay que tener en cuenta. El principal foco de atención han sido las plantillas, y particularmente qué opciones de exportación son más aconsejables. También cómo montar un servidor local para visualizar los cambios y hacer pruebas, en el caso de que sea un proyecto para html: bien sea un blog o simplemente documentación. Y también cómo gestionar si se desea que la salida se produzca en varios formatos, por ejemplo, html y pdf. Y también si es mejor utilizar etiquetas include o enlazar los archivos. Algunas de estas preguntas tienen respuestas sencillas de procedimiento, pero a otras sólo puedo contestar: depende. Ahora bien, ese depende es lo que explico en adelante.

El depende principal es el de la salida que queremos obtener y en función de esa salida debemos trabajar. Cuando el proyecto sólo se publicará en un formato, no hay ningún problema: configuramos ese formato y no hará falta más. Cuando hacemos cambios generamos la salida y todo funcionará perfectamente. El problema viene cuando queremos generar documentos de diverso tipo con sus diferentes idiosincrasias. No hace falta rebuscar mucho, por ejemplo, si nos fijamos en la manera en que html y TeX numeran sus cabeceras, veremos que son significativamente distintas y que, por tanto, necesito tener configuradas opciones distintas según el formato de salida. ¿Eso complica las cosas? Pues un poco: necesitamos cabeceras de exportación con opciones distintas, e incluso quizá diferentes opciones de publicación. Ten en cuenta que exportación y publicación, aunque lo pueda parecer de primeras, no son sinónimos para mí.

Efectivamente, podemos separar lo que significa convertir nuestro documento org en otro formato, ─eso es exportación─, de lo que significa facilitar el acceso al contenido, ─que es la publicación─. Si te has perdido un poco, espero que lo entiendas con el ejemplo.

Vamos a imaginar que queremos generar una documentación de algún proyecto es una especie de wiki donde las distintas páginas estarán enlazadas mediante enlaces, valga la rebuznancia, entre ficheros org. Exportar esta wiki a formato html es sencillo: sólo hay que seguir los pasos del artículo donde explicaba como publicar html desde org-mode. Como todo ya está explicado en ese artículo no voy a dar muchos detalles: se ponen enlaces entre los archivos org y recordamos activar :recursive t en las opciones de publicación del proyecto, ya está todo listo... Pero, opciones de publicación... opciones de exportación... ¡uff! ¿de qué va todo esto?

Opciones de exportación

Algunos me dicen que copian las cabeceras de sus documentos org1 y no entienden por qué unas veces les muestra bien los subíndices, como en H2O ─por poner un ejemplo─, y otras veces muestra algo como H_2O o incluso H2O, sin ninguna explicación aparente. La respuesta a ese gran enigma es que estás copiando las opciones sin saber qué significan y qué comportamientos controlan. Revisa la documentación de org-mode, ahí está todo explicado. Pero si te da pereza sigue leyendo un poco.

La manera más rápida de generar una plantilla de exportación es copiar una ya hecha. Sin embargo, algunas veces nos encontramos con líneas como la siguiente:

#+options:  H:3 num:nil toc:nil \n:nil ::nil |:t ^:{} -:nil f:t *:t <:t

Nos puede parecer algo esotérico y casi mágico, que casi da miedo tocar para no cagarla, pero tenemos que ser conscientes que cada elemento de esa lista tiene efectos sobre el documento generado. Además esos elementos se circunscriben al fichero en cuestión y sólo afectan a la exportación del mismo. Utilizar otras opciones, para otros contenidos es lo que hace que la utilidad de las plantillas sea increíble. Por ejemplo, en la exportación a html podemos no necesitar tener el índice de los apartados, ─especialmente si lo vamos a tratar como páginas web enlazadas─, pero al generar la salida para pdf con los mismos archivos, nos puede parecer imprescindible. Lo más rápido será tener plantillas para generar html y para generar pdf... además, al generar así los html querremos que se creen un montón de ficheros con una determinada estructura, mientras que para el pdf lo querremos todo en un archivo monolítico. Bien, vale, pero me estoy enrollando. Vamos con las opciones, no voy a ser exhaustivo, porque están bien explicadas en la documentación de org-mode. Voy a explicar, brevemente las que he puesto en el ejemplo anterior y alguna más porque la considero importante conocer.

H
Establece el número de niveles de cabecera que se exportarán como tal. A partir del número expresado, el resto de cabeceras org se exportarán como elementos de una lista.
num
Establece si queremos que las cabeceras se numeren al exportar. Un valor de nil hará que se omita el número. También podemos tenerlo activado mediante t y si, en el documento, queremos que algún apartado no sea numerado se le puede añadir una propiedad (C-c C-x p) UNNUMBERED puesta a t. Si ponemos esa propiedad a notoc, el apartado y todos sus subapartados no aparecerán tampoco en el índice de contenido.
toc
Establece si al exportar se generará un índice de contenido para el fichero. Si en lugar de nil o t aparece un número, lo que hace es limitar el nivel de encabezado que aparecerá en el índice de contenido.
\n
Establece si debe preservar los saltos de línea cuando realiza la exportación.
:
Establece si las regiones de ancho fijo (que comienzan cada línea con un carácter : y un espacio o retorno de carro) se exportan al fichero generado o se ocultan. Si establecemos el valor a nil, podemos utilizar estas secciones como si fueran comentarios dentro de nuestro fichero org que luego no aparecerán en la salida. Podemos hacer algo similar con los drawers y la opción d.
|
Establece si exportará las tablas.
^
Establece cómo se tratarán los subíndices y superíndices. Los valores pueden ser nil, t ó {}. Los dos primeros desactivan y activan, respectivamente, la notación que emplea los caracteres _ para subíndices y ^ para superíndices. La opción marcada con las llaves indica que sólo tendrá efecto, convertir a subíndice o superíndice, en aquellas cadenas que se encuentren entre llaves a continuación de uno de esos caracteres.
-
Activar o desactivar la conversión de cadenas especiales. Es decir, si activamos esta opción la salida interpretará las cadenas especiales \-, -- y ---.
f
Activa o desactiva la exportación de las notas a pie de página.
*
Activa o desactiva la exportación de texto enfatizado, negrita, cursiva, etc.
<
Activa o desactiva la inclusión de marcas de tiempo y fecha en la exportación.

Hay más y todas interesantes y están muy bien explicadas en la documentación de org-mode. Además, otra opción que me parece muy importante es poder exportar el contenido con otro nombre. Como es normal, por defecto, el contenido se exportará a un fichero con nombre idéntico al fichero org pero con la extensión adecuada: html, pdf, etc.

Si como hemos visto antes, tenemos un fichero que realizará la exportación a html y otro a pdf, no podemos llamar a los dos con el mismo nombre, porque ya hemos visto que podemos necesitar diferentes opciones de exportación. Por ejemplo, si tenemos un fichero exportar-html.org y otro exportar-pdf.org, por defecto se generarán los archivos de salida exportar-html.html y exportar-pdf.pdf ... feo ¿no? Nos gustaría obtener como resultado exportar.html y exportar.pdf. ¿Cómo lo podemos hacer? Añadiendo en cada fichero el nombre del fichero donde queremos exportar, por ejemplo, en nuestro fichero exportar-html.org:

#+setupfile: mi-plantilla-html.org
#+export_file_name: exportar.html

Opciones de publicación

Mucho de lo que voy a hablar ahora, ya lo conté en el artículo sobre publicar html que dije antes. Voy a repetir algunas cosas, pero añadiendo la opción de que queremos exportar tanto a html en ficheros distintos, como en pdf en un fichero monolítico.

Mi costumbre, como ya conté, es tener en el proyecto un fichero elisp para configurar los proyectos de exportación y cargarlo según lo necesite. Pongo aquí uno que podría servirnos de ejemplo, o plantilla para nuestro proyecto de doble exportación:

(require 'ox-publish)

(setq org-publish-project-alist
      '(
        ("org-to-html"
         :base-directory "~/proyectos/programa/documentacion/"
         :base-extension "org"
         :publishing-directory "~/proyectos/programa/docs/"
         :language es
         :publishing-function org-html-publish-to-html
         :exclude "documentacion-pdf.org"
         :recursive t
         :section-numbers nil
         :headline-levels 6
         :html-preamble t)
        ("org-to-pdf"
         :base-directory "~/proyectos/programa/documentacion"
         :base-extension "org"
         :language es
         :publishing-directory "~/proyectos/programas/docs"
         :exclude "documentacion-html.org"
         :recursive nil
         :section-numbers t
         :publishing-function org-latex-publish-to-pdf
         :headline-levels 5)
        ("org-auxiliares"
         :base-directory "~/proyectos/programa/documentacion/"
         :base-extension "css\\|js\\|png\\|ttf\\|pdf"
         :publishing-directory "~/proyectos/programa/docs/"
         :recursive t
         :publishing-function org-publish-attachment)
        ("documentacion" :components ("org-to-html" "org-to-pdf" "org-auxiliares"))))

Ese código está planteado para una estructura tal que:

.
├── docs
│   └── ... (directorio donde se publicará)
├── documentacion
│   ├── documentacion-html.org
│   └── documentacion-pdf.org
├── publicacion-docs.el
├── org
│   ├── css
│   │   └── ... (hojas de estilo)
│   ├── img
│   │   └── ... (ficheros de imagen)
│   ├── js
│   │   └── ... (ficheros javascript)
│   └── ... (ficheros org)
├── plantillas
│   ├── plantilla-html.org
│   └── plantilla-pdf.org
└── README.org

Como se puede ver, tenemos dos tipos de plantillas: para html y para pdf. Dos tipos de ficheros fuente: para html y para pdf. ¿Cómo evitamos que al exportar el sistema nos cree más salida de la que necesitamos. Es decir, sólo necesitamos que se genere html desde el fichero documentación-html.org y sólo necesitamos que se genere pdf desde el fichero documentación-pdf.org. Para esto tenemos dos proyectos de exportación: org-to-html y org-to-pdf. Pero vemos que, aunque los contenidos al final serán los mismos las opciones cambian. Especialmente hay que señalar que el otro generador es excluido del proyecto. Cuando generamos html queremos que utilice el org correspondiente e ignore el otro.

Montar un servidor local

Cuando trabajamos en un proyecto que se visualizará como una página web es útil, muchas veces, contar con una manera de montar un servidor local que actúe como si fuera un sitio de Internet general, pero sólo accesible desde localhost (127.0.0.1). No tienes por qué hacerlo desde Emacs si no quieres, pero ya que estás editando con él, para qué lo vas a hacer desde fuera. Desde fuera hay muchas maneras: hay lenguajes que proporcionan sus propios servidores locales y que podemos utilizar.

Por ejemplo, en Python 3.x:

python -m http.server 8080

En PHP:

php -S localhost:8080

En Ruby:

ruby -run -e httpd . -p 8080

El listado puede ser muy largo, porque parece que cada lenguaje que se utiliza de uno u otro modo para programar para la web tiene un sistema de hacerlo. En todos los casos anteriores se cuenta que el lanzamiento se hace situándose en el directorio donde se encuentran los ficheros de salida y utilizan, algunos sin mencionarlo, el directorio actual (.) para levantar un demonio httpd. Además, en todos los ejemplos anteriores se ha utilizado el puerto 8080, pero se podría utilizar cualquier otro al uso, recordando que los puertos recomendados para los servicios, ─por ejemplo, 80 es el puerto http por defecto─, necesitan permisos de root para habilitarlos.

Es bastante cómodo tener el servidor levantado, al hacer cambios y generar la salida en su correspondiente directorio, podemos ir viendo el efecto de esos cambios con cualquier navegador.

En los ejemplos anteriores hemos podido ve que las herramientas para montar servidores locales, necesitan todas un par de datos: el directorio donde está la página y el puerto por el que servir el contenido. Normalmente, en los ejemplos anteriores el directorio se omite, porque se entiende que el comando se lanza desde una consola estando situados en él. De la misma manera, en Emacs, necesitamos especificar qué directorio y qué puerto queremos utilizar. Esto lo hacemos con un poco de código elisp. Por ejemplo, una de las estrategias para configurar nuestro servidor local puede ser ajustar los valores necesarios en el mismo fichero que cargábamos antes con los proyectos de publicación, añadiendo las siguientes líneas:

(setq httpd-port 8080)
(setq httpd-root "~/mi-directorio")
(httpd-start)

Si sólo tienes un proyecto de publicación html, lo puedes establecer mediante M-x customize-group <RET> simple-httpd. En todo caso, esas tres líneas de código bastan para lanzar nuestro servidor. Las podemos evaluar con el código del proyecto de publicación o tenerlas a mano en algún buffer donde las podamos evaluar... o establecerlas una a una con M-x.

Flujo de trabajo

Más que por simple-httpd.es, que es el fuente donde descansa nuestro servidor, las preguntas estaban enfocadas al flujo de trabajo con html. Bien, hasta aquí hemos hablado de los primeros pasos:

  1. Crear el contenido con org-mode.
  2. Publicar el contenido con las herramientas de Emacs.
  3. Servir los html desde un servidor local para visualizarlos.

¿Cómo seguimos? Cuando tenemos alguna modificación que comprobar la publicamos y para eso, sólo hay que ir repitiendo los pasos del 1 al 3 y viéndolos en el navegador que más nos guste. Por otro lado, a algunos no os gusta partir de un css ya hecho, para ir modificando los distintos aspectos del mismo. La justificación que me habéis dado es válida: terminas con un css con un montón de elementos que tiene que cargar el navegador, pero que no son útiles. Por otro lado, además de la declaración del css que se incluye en la cabecera en la misma aparece un apartado de style, no sólo es que queda feo, es que ocupa también. Se puede aligerar la cabecera desactivando la exportación de esos valores mediante:

#+options: html-style: nil

Si queremos crear nuestro css desde lo más básico a lo más general, podemos observar dicha inclusión y tomarla como código básico para exportación. Al menos, la exportación desde org-mode establece las siguientes clases específicas:

p.author
Información del autor incluyendo el email.
p.date
Fecha de publicación
p.creator
Información sobre la versión de org-mode y Emacs.
.title
Título del documento.
.subtitle
Subtítulo del documento.
.todo
palabras clave TODO.
.done
palabras clave DONE.
.WAITING
clase para encabezados a la espera.
.timestamp
etiqueta de tiempo.
.timestamp-kwd
palabra clave asociada a una etiqueta de tiempo, como scheduled.
.timestamp-wrapper
espacio alrededor de la etiqueta de tiempo y su palabra clave.
.tag
Etiqueta en una cabecera. Típicamente las que aparecen a la derecha de la cabecera.
._HOME
Cada etiqueta utilizándose como una clase, reemplazando @ por _
.target
objetivo para links.
.linenr
número de línea en bloques de código
.code-highlighted
líneas de código destacadas
div.outline-N
Nivel de cabecera, donde N es un número (1, 2, ... 6)
div.outline-text-N
div extra para el texto de la cabecera.
.section-number-N
Número de sección de la cabecera.
.figure-number
Etiqueta para las figuras tal que «Figure 1:»
.table-number
Etiqueta para las tablas, tal que «Table 1:»
.listing-number
Etiqueta para bloques de código «Listing 1:»
div.figure
Clase para imágenes en líneas de texto.
pre.src
Código fuente formateado.
pre.example
bloques example
p.verse
Párrafos verse
div.footnotes
Cabecera para la sección de notas al pie.
p.footnote
Párrafo que contiene una nota al pie.
.footref
Número de referencia de una nota al pie (siempre como <sup>).
.footnum
Número de una nota al pie (siempre como <sup>).
.org-svg
Clase por defecto para una imagen .svg enlazada.

¿Qué ocurre si quiero definir mis propias clases? Pues es algo sencillo. Vamos a imaginar que quiero crear una clase para el css que me formatee con un determinado estilo, por ejemplo, un aviso, que haga que el texto se escriba en rojo para destacarlo. El código css es muy sencillo:

.aviso {
  color: #FF0000;
}

Hacer que un párrafo de nuestro documento se muestre con el formato para la clase aviso es tan sencillo como escribir en nuestro fichero org:

...

#+attr_html: :class aviso
Este es un párrafo de aviso que mostrará el texto en rojo.

...

Si no quiero definir una clase y utilizar directamente las propiedades de html podría haber escrito el código así:

...

#+attr_html: :class aviso :style color:red;
Este es un párrafo de aviso que mostrará el texto en rojo.

...

Además de las clases mencionadas arriba, un css básico debe contar con definiciones para las etiquetas de texto habituales en un fichero html: <html>, <body>, <h1> ... <h6>, <p>, <em>, <i>, <b>, <table>, <dt>, <dh>, <dd>, <li>, <ol>, <ul>, <img>... y también puede definir estilos especiales para dos tipos de elementos marcados con id=: #preamble, #content y #postamble.

De estos tres últimos elementos, nuestro fichero org genera sólo, por decirlo así, el content. El pie y la cabecera están controladas por variables:

org-html-preamble
si no es nil genera un preámbulo con el contenido de la variable org-html-preamble-format.
org-html-preamble-format
cadena html que se mostrará en la exportación. Por defecto está vacía.
org-html-postamble
si no es nil genera un pie de página con el contenido de la variable org-html-postamble-format.
org-html-postamble-format

cadena html que se mostrará. Por defecto, su valor se establece:

(setq org-html-postamble-format
      '(("en" "<p class=\"author\">Author: %a (%e)</p>
<p class=\"date\">Date: %d</p>
<p class=\"creator\">%c</p>
<p class=\"validation\">%v page</p>")))

Algunos sistemas de publicación, se basan en todas estas estructuras, variables y métodos para generar sitios web estáticos. Por ejemplo, org-static-blog, con el que está hecho este blog, se basa en estos procedimientos añadiéndoles un poco de funcionalidad mediante código elisp.

Conclusiones

Este no es un artículo muy coherente. He escrito sobre distintos aspectos de lo mismo y hecho muchas referencias a un artículo anterior que no tienes por qué haber leído. Sin embargo, espero que hayas podido ver, o al menos intuir, toda la flexibilidad que tiene org-mode a la hora de exportar y publicar contenido.

Todo lo que he escrito lo he sacado de la documentación, por lo que la contestación rápida a las preguntas hubiera sido: lee el puto manual. Pero no me sale ser tan borde y prefiero contestar, porque muchas veces me obliga a buscar el porqué de las cosas y entender mejor lo que creía que ya sabía.

A veces, la necesidad de concisión hace que dé por sabido algo que parece no serlo. Eso es fuente de muchas de las preguntas que me hacéis y para las que, de vez en cuando, tampoco tengo una respuesta clara, porque yo también las puedo estar haciendo por inercia y sin leer el puto manual... así que: ¡gracias por las preguntas! Porque me obligan a buscar las respuestas.

Footnotes:

1

Hay quien me ha dicho: de sitios solventes como tu blog... ¿solvente mi blog? Revisa el concepto, por favor.

Categoría: emacs org-mode

Comentarios

Debido a algunos ataques mailintencionados a través de la herramienta de comentarios, he decidido no proporcionar dicha opción en el Blog. Si alguien quiere comentar algo, me puede encontrar en Mastodon y en Diaspora con el nick de Notxor.

También se ha abierto un grupo en Telegram, para usuarios de esa red.

Disculpen las molestias.