Notxor tiene un blog

Defenestrando la vida

Publicar html con org-mode

2021-01-13

En el artículo de hoy hablo de cómo exportar de org-mode a html para utilizarlo en un proyecto con varios ficheros org interconectados entre sí. Al exportar, las referencias org se convierten en sus correspondientes enlaces html guardando la misma estructura de directorios que tengan los ficheros org. Es la forma en la que org-mode publica. Si quieres ahorrarte el suplicio de leer el artículo y como lo cuento, busca el apartado publishing en la documentación de org-mode... pero si prefieres leer un resumen en español y saber cómo lo hago yo: sigue leyendo.

¿De qué va todo esto de publicar html desde org-mode? ¿Para qué lo puedo utilizar? Pues pondré un ejemplo, con un proyecto que estoy empezando. Hace un tiempo puse por aquí una prueba para crear un librojuego con org-mode. Era una prueba pequeña y apenas se utilizaban cuatro ficheros. Como eran tan pocos ficheros, las cabeceras y la exportación se podía hacer a mano. Sin embargo, en un proyecto largo, tener que hacerlo así puede ser un poco complicado, tedioso y sujeto a errores y olvidos.

El caso de uso propuesto es parte de un proyecto más amplio. El problema es que estoy haciendo una especie de MUD para aplicarlo en un proyecto de prevención del acoso escolar para la Asociación PICA1 con la que, los que leéis el blog habitualmente, sabéis que colaboro. El caso es que la parte servidor del mismo está prácticamente finalizada, a la espera de que se llene de contenido y de corregir los fallos y completar las carencias que se encuentren durante el proceso. Para dotar de contenido algo tan interactivo como un MUD, no sirve un guión lineal. Por ello, pensé en escribir guiones más interactivos, en formato librojuego. He probado varios sistemas que automatizan la creación de ese tipo de juegos: he probado Undum, he probado Twine, he probado Squiffy... y ninguno de los tres ha funcionado como esperaba. El primero, que había que escribir el código en puro html y javascript, sin embargo, por alguna razón que no llegué a averiguar algunos enlaces no funcionaban. De los otros dos, además de tener que instalar node y un montón de paquetes, no terminaron de funcionar correctamente. La otra idea que se me ocurrió era montar un wiki enlazando páginas directamente. Sin embargo, para que funcione tienes que tener un servidor y para trabajar en modo monousuario es muy lioso.

Cuando le comenté los problemas que tenía para encontrar una herramienta adecuada a un amigo del CAAD2, me dijo: pensaba que lo harías con algún pichorro de Emacs. Y tenía razón, al final no me tendré que pegar con cosas como electron y node o desperdiciar espacio en disco y en memoria con esas cosas, teniendo suficiente con el texto gestionado por los chismáticos de Emacs y org-mode.

Estructura de ficheros

Para iniciar el proyecto es recomendable tener clara la estructura que le daremos a nuestro contenido. El proyecto lo he llamado, de forma provisional, Kvintero3 y tiene la siguiente estructura de ficheros:

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

se pueden apreciar algunas cosas así a bote pronto: los directorios org y html serán idénticos, pero mientras el primero está compuesto de los fuentes de org-mode el segundo es el resultado de convertir los fuentes en html. En esos directorios hay otros subdirectorios para la(s) hoja(s) de estilos, las imágenes y los archivos javascript. También hay un directorio que se llama plantillas, donde hay sólo un fichero de momento y más adelante analizaremos su contenido. Además de todo ésto también hay un fichero de código que se llama kvintero.el que contiene un par de formas que definen algunas variables del proyecto para poder exportar cómodamente en tres teclazos.

Es una estructura muy simple, aunque su visualización en árbol, pueda dar una primera impresión de complejidad. En realidad, sólo necesitas crear el directorio org y dentro de él: el css, el img y el js, cada uno con sus cosas, para que no se mezclen.

Para que dicha estructura sea efectiva es recomendable que luego, en los enlaces se utilicen siempre paths relativos. Para que el sitio desplegado o publicado, ─como prefieras llamarlo─, funcione incluso sin un servidor, es siempre recomendable el uso de dichos paths relativos. Además, se pueden utilizar anclas a etiquetas concretas dentro de otros ficheros org4, como veremos después.

El código

Como he dicho antes, el código es muy sencillo, lo pongo aquí y luego lo comentaré un poco:

(require 'ox-publish)

(setq org-publish-project-alist
      '(
        ("fuentes-org"
         :base-directory "~/proyectos/kvintero/org/"
         :base-extension "org"
         :publishing-directory "~/proyectos/kvintero/html/"
         :recursive t
         :publishing-function org-html-publish-to-html
         :headline-levels 4
         :html-preamble t)
        ("org-auxiliares"
         :base-directory "~/proyectos/kvintero/org/"
         :base-extension "css\\|js\\|png\\|ttf"
         :publishing-directory "~/proyectos/kvintero/html/"
         :recursive t
         :publishing-function org-publish-attachment)
        ("kvintero" :components ("fuentes-org" "org-auxiliares"))))

La primera línea no necesita mucho que explicar: nos aseguramos que está en el sistema el paquete que proporciona ox-publish. Después de esa primera forma, nos encontramos con otra que establece la lista de proyectos para publicar. Cada elemento de la lista tiene una forma general tal que:

("nombre-proyecto" ...)

Vamos a analizarlo un poco más despacio.

Proyectos de publicación

El primer elemento de la lista trata con los fuentes. Establece cuál es su directorio base en :base-directory. Vemos que coincide también con el segundo elemento. En él buscará los ficheros con los que debe trabajar. Como se puede observar el valor se establede al lugar donde se encuentran los ficheros que fuentes-org debe tratar o trabajar. En :base-extension se especifica de qué tipo son los ficheros tiene que procesar. Si nos fijamos en la primera forma la extensión es única: org, pero en la segunda se establece una lista de extensiones separadas por \\|. Lo siguiente que se necesita es el lugar donde se depositará el resultado del proceso y se pone en :publishing-directory. En este caso es un directorio local, pero también se puede configurar un path con el formato que entienda tramp, por ejemplo: /ssh:usuario@host:path/al/sitio. También se pueden especificar ficheros que se ignorarán durante el proceso con otras variables, si necesitas esta información: mira en la documentación de org-mode.

Está previsto que se puedan meter otros directorios dentro del directorio base para gestionar diferentes subproyectos y queremos que se procesen todos, guardando la estructura. Para permitir que se publiquen todos esos subproyectos hay que establecer :recursive a t y el sistema analizará también todos los subdirectorios que se encuentren dentro del base.

Por último, en :publish-function se establece qué función procesará los ficheros. En el primer caso se quiere que se convierta el fichero procesado a html y para ello que se utilice la función org-html-publish-to-html. En el segundo caso queremos que copie los ficheros nada más y por eso se establece a org-publish-attachment.

Hay más opciones que configurar si se necesitan: funciones que se ejecuten antes o después de la publicación, por ejemplo, y muchas otras opciones. Vuelvo a recomendar que le echéis un vistazo a la documentación de org-mode.

La tercera de las formas, que antes no la he mencionado, es un conjunto de ambas, especificado por una lista de componentes. Llamando a ésta, se llama a las dos anteriores.

Luego en el texto del proyecto hay que recordar enlazar de forma relativa a la estructura de directorios. Además también se pueden enlazar lugares concretos de un fichero. Ya sé que lo sabéis pero está bien recordarlo. Para hacerlo suelo utilizar las formas:

[[file:cualquier.org::*Título de apartado][Texto mostrado]]
[[file:cualquier.org::#id-definido][Texto mostrado]]
[[file:cualquier.org::mi objetivo][Texto mostrado]]

El formato con *Título de apartado busca entre los títulos de sección que contenga el fichero el que coincida con Título de apartado. Con #id-definido se busca un elemento que en sus propiedades contenga #+name: id-definido. Y por último, si quieres saltar a algún sitio situado en medio de un documento, que no tiene lista de propiedades donde ponerle nombre, se puede utilizar la forma <<mi objetivo>>, en el texto y se enlaza de la tercera forma.

Como digo, supongo que todos esos detalles ya los conocéis, al menos quienes utilizáis habitualmente org-mode, pero, ─como digo─, no está de más repasarlo.

Plantillas y documentos

Una forma de estandarizar y ahorrar tiempo en la publicación de los ficheros org, tanto en html como en pdf es proporcionar al sistema una plantilla que haga las veces de configuración de la página, estableciendo los valores oportunos. En esas plantillas lo que se hace, normalmente, es situar todas las opciones que pondríamos en la cabecera que sean comunes a todas las páginas. Para este proyecto he preparado la siguiente cabecera:

#+author:  Notxor
#+email:   notxor@nueva-actitud.org
#+options: ':t *:t -:t ::t <:t H:3 \n:nil ^:t arch:headline author:t c:nil
#+options: creator:comment d:(not LOGBOOK) date:t e:t email:nil f:t inline:t
#+options: num:t p:nil pri:nil stat:t tags:t tasks:t tex:t timestamp:t toc:t
#+language: es
#+exclude_tags: noexport
#+select_tags:  export

#+options: html-postamble:auto html-postamble:nil tex:t
#+html_container: div
#+html_doctype: xhtml-strict
#+html_head: <link rel="stylesheet" type="text/css" href="css/tus-estilos-favoritos.css" />

Todos esos valores comunes pueden cambiar en un momento dado, porque se quiera mostrar una determinada página con otra hoja de estilos o porque varíen los paths, o por cualquier otro motivo. Estas cosas harán que necesitemos más tipos de cabecera. Por ejemplo, si creamos un subdirectorio dentro de nuestro sitio base y utilizamos la misma cabecera para publicar el sitio, no encontrará el fichero .css, necesitamos cambiar la última línea a:

#+html_head: <link rel="stylesheet" type="text/css" href="../css/style.css" />

Modificando el path en el que debe buscar la hoja de estilos.

Si ya tenemos una plantilla en la que basarnos, generar las demás que nos hagan falta es tan sencillo como copiar la plantilla-base y modificar los valores que queramos cambiar... y llamaremos a dicha plantilla desde el fichero que queramos con el formato:

#+setupfile: ../plantilla/plantilla-base.org
#+title: Este es el Título
#+subtitle: y este el Subtítulo

Como se puede apreciar, la cabecera queda resumida a dos o tres líneas, muchas veces será suficiente establecer el #+setupfile:, o como mucho añadirle un título.

Otro factor a destacar es que establecemos que utilizando la etiqueta noexport se puede eliminar todo un árbol interno del documento cuando lo exportamos.

* Notas    :noexport:

Esta información no se exportará al fichero ~html~

Hoja de estilos

Antes de meternos en un pequeño tutorial, que consistirá en que iré haciendo algunos pasos para la creación de un sitio y, si quieres, puedas irlos siguiendo para comprobar que todo funciona. También necesitas una hoja de estilos que nos sirva de punto de partida. Por Internet ha muchos ficheros css para la exportación de org-mode publicados, puedes utilizar el que más te guste... si eres vago como yo, puedes descargarte el que voy a utilizar en el proyecto de:

http://thomasf.github.io/solarized-css

es el mismo que utilicé en aquella prueba sin hacerle ninguna modificación. Sin embargo, esta vez sí le haré cambios. En la web, el autor, recomienda el uso de un enlace directo al sitio de Internet. Pero, es una hoja destinada a mostrar sólo una estructura org, más como agenda, y no para publicar documentos. Además quiero ajustar las fuentes y otros parámetros. Por eso descargo el fichero y luego iré contando las modificaciones que le haré, según avance el tutorial.

Puedes elegir entre dos temas de color:

solarized-dark

Captura-pantalla_solarized-dark.png

solarized-light

Captura-pantalla_solarized-light.png

Particularmente me gusta más el solarized-light y será el que utilice. No olvidéis modificar en la plantilla el nombre de la hoja de estilo que más te guste. Prefiero siempre partir de una hoja de estilos ya creada y modificarla que crear una desde cero.

Publicación

Yo comenzaré con un pequeño fichero de texto en formato org para empezar el proyecto, cualquier texto vale, yo he preparado estos pequeños párrafos para comenzar:

#+setupfile: ../plantillas/plantilla-base.org
#+title: Kvintero ─ Introducción

Dos campanas tañieron al unísono cuando se abrió el arco iris.  Los
viajeros hubieran podido oír las dos, si hubieran prestado atención.
Pero impactados por el viaje no las escucharon.

La primera sonó escondida, como olvidada, oculta entre el ruido del
tráfico y el ajetreo de la ciudad.

La segunda sonó rotunda bajo las estrellas en una noche silenciosa,
pero fue ignorada por aquellos que pudieron llegar a oírla.  Su sonido
rodó por los tejados de pizarra y paja, rebotó por las calles de
piedra y acero, deshaciéndose en ecos hacia las montañas.

Una tercera quiso sonar con ellas, pero rajada y rota apenas consiguió
emitir un chirrido metálico. Sin embargo, ésta obtuvo más atención
cuando lanzó su desafinado tañido.

El /brujo/ abrió los ojos y sonrió con malicia.

[[file:piedra.org][Continuar...]]

Comenzamos con los cambios:

El autor del css utiliza la carga de fuentes desde la habitual URL de gúguel, una política que no es de mi agrado. Yo, en cambio, he bajado de Internet algunas fuentes de daFont, un sitio donde se pueden encontrar fuentes de todo tipo y desde el que se pueden descargar para uso personal. Cada uno se puede bajar las fuentes que quiera, yo he bajado Feathergraphy, que es gratuita para uso personal5, en los títulos y para el texto utilizaré la habitual Linux Libertine que uso también como fuente principal en este blog, y que tiene una licencia libre y se puede utilizar para cualquier fin.

Los ficheros ttf los he colocado en un directorio llamado fuentes que no aparece en el árbol de directorios anterior, tenéis que crearlo, copiar las fuente ahí e importarlas en el css como sigue:

@font-face {
    font-family: "Libertine Regular";
    src: url('../fuentes/LinLibertine_R.ttf') format('truetype');
    font-weight: normal;
    font-style: normal;
}
@font-face {
    font-family: "Feathergraphy";
    src: url('../fuentes/Feathergraphy2.ttf') format('truetype');
    font-weight: normal;
    font-style: normal;
}

El paso siguiente es modificar algunos estilos. En las cabeceras, se añade una instrucción font-family: 'Feathergraphy' y en el apartado body se añade font-family: 'Libertine Regular'. Para ver el resultado hay que publicarlo. Sigamos los siguientes pasos:

  1. Cargar el código que hicimos antes con el comando M-x load-file y seleccionado el fichero kvintero.el que habíamos creado para tener la lista de proyectos para publicación.
  2. A partir de este momento, si estamos en un fichero org podemos emplear el comando de exportar C-c C-e, ahora seleccionamos el apartado Publish (P), y choose project (x), elegimos el proyecto kvintero y se realiza la magia.

    Si no estamos en un fichero org, podemos utilizar el comando M-x org-publish-project RET kvintero RET.

  3. Abrir el fichero en un navegador web. Obtendremos algo parecido a:
Captura-pantalla_Cambio-fuentes.png

Vista la imagen, no me gusta cómo queda el título y voy a separarlo en dos. La cabecera del fichero org quedará así:

#+setupfile: ../plantillas/plantilla-base.org
#+title: Kvintero

* Introducción

Tras modificar el org lo guardamos y volvemos a publicar el proyecto, bien con C-c C-e o con M-x org-publish-project. Volvemos a ver cómo ha reaccionado nuestro html. El resultado quedará como:

Captura-pantalla_cabecera-numero.png

Bueno, parece que ha surtido efecto, pero me gusta menos aún: aparece un índice que no quiero y el título me lo numera... algo más hay que cambiar. Y lo que hay que cambiar está en la plantilla. En concreto en la línea:

#+options: num:t p:nil pri:nil stat:t tags:t tasks:t tex:t timestamp:t toc:t

Hay que cambiarla por:

#+options: num:nil p:nil pri:nil stat:t tags:t tasks:t tex:t timestamp:t toc:nil

Volvemos a publicar el proyecto por el método habitual. ¿Qué ha ocurrido? ¿Nada? Pues sí, no ha ocurrido nada, porque hemos modificado un fichero que no está dentro del proceso del proyecto. Para que tenga efecto hay que rehacer todos los ficheros que enlacen la cabecera. Lo que yo hago es entrar en el dired, si tienes activado normalmente, como yo el dired-sidebar lo tienes a mano. Pasas a ese buffer, seleccionas los ficheros, ─si son muchos, puedes seleccionar el directorio entero─, pulsando m para marcar y luego pulsas T (obsérvese la mayúscula). Es lo mismo que hacer un touch en una consola, que es el otro método rápido. Si ahora repites el comando de publicar el proyecto, el resultado será:

Captura-pantalla_cabecera-sin-numero.png

Poniendo una imagen

Voy a poner una imagen en el proyecto, más para ilustrar de otro modo cómo es el proceso de mantener un proyecto completo, el ir haciendo pequeñas modificaciones e ir viendo cómo se van produciendo los resultados.

Lo primero que necesito es encontrar una imagen. Yo me descargué una fotografía de un menhir de Internet y le apliqué algunos filtros con GIMP. Al final, el fichero .png lo he copiado en el directorio img que cuelga del directorio base. Para cargar la imagen, se hace como siempre en org-mode.

#+setupfile: ../plantillas/plantilla-base.org
#+title: Kvintero

* Introducción

[[file:img/menhir.png]]

Una vez introducido el enlace de la imagen, si queremos verla aparecer en nuestro documento html debemos publicar el proyecto. Como seguramente estaremos aún en el fichero org, la pulsación completa será C-c C-e P x kvintero y obtendremos algo así:

Captura-pantalla_imagen-centrada.png

Está bien, pero es un poco soso. Vamos a darle un poco de vidilla con la hoja de estilos, a ver si lo adornamos un poco. Primero, me gustaría que la imagen se desplazara a la izquierda y el texto se ajustara a ella mostrándose a la derecha, y no debajo. Eso es fácil de hacer con css.

.float-left {
    float: left;
    margin-right: 1em;
}
.float-right {
    float: right;
    margin-left: 1em;
}

En principio me bastaba con float-left, pero ya puestos, he creado también float-right para tenerlo disponible en el futuro. Así podré manejar imágenes ajustándolas en uno de los laterales. Además añado un margen en el lado donde irá el texto, para que no se junte en exceso a la imagen.

Ya puestos a hacer cambios, ─y puesto que es un documento que hará las veces de /librojuego/─, me gustaría resaltar más algunos elementos, hacer que la primera letra del párrafo inicial se muestre más grande y remarcar la primera línea destacándola. Para esto he añadido un poco más de código a la hoja de estilos.

.p-inicio::first-line {
    font-weight: bold;
    color: rgba(0,0,0,0.85);
}
.p-inicio::first-letter {
    position: relative;
    padding-top: 0.1em;
    display: block;
    float: left;
    font-family: 'Feathergraphy';
    font-weight: normal;
    font-size: 3.2em;
    line-height: 0.8em;
    color: #d33682;
    margin-right: 0.1em;
}

La primera parte, .p-inicio::first-line, establece que la primera línea del párrafo se escriba en negrita y con un color negro con una transparencia del 15%.

La segunda parte, .p-inicio::first-letter, establece que la primera letra del párrafo se muestre con un tamaña 3.2 veces el tamaño de letra normal y que utilice el tipo de letra y el color del título.

Bueno, hechos los cambios necesitamos visualizarlos en nuestro navegador favorito. Como seguramente seguiremos en el buffer donde hemos hecho los cambios podemos publicar el proyecto utilizando la combinación M-x org-publish-project RET kvintero RET. El resultado que debemos obtener sería algo así:

Captura-pantalla_imagen-fluye.png

¿No? ¿No ha salido bien? Claro, hemos modificado la hoja de estilos, pero no le hemos dicho al documento qué estilos y dónde tiene que aplicarlos. Eso es fácil:

#+setupfile: ../plantillas/plantilla-base.org
#+title: Kvintero

* Introducción

#+attr_html: :class float-left
[[file:img/menhir.png]]

#+attr_html: :class p-inicio
Dos campanas tañieron al unísono cuando se abrió el arco iris.  Los
viajeros hubieran podido oír las dos, si hubieran prestado atención.
Pero impactados por el viaje no las escucharon.

Delante del elemento del documento que queremos aplicar uno de los estilos que acabamos de crear, utilizaremos el comentario especial #+attr_html: para establecer la :class con el nombre que hayamos puesto en la hoja de estilos. Si lo haces como está en el código anterior y publicas el proyecto como venimos haciendo hasta ahora, obtendrás los resultados como se mostraron arriba.

Por supuesto, ajustar todas estas cosas lleva su tiempo y es recomendable utilizar las herramientas de desarrollador web que provea el navegador que utilices. Yo normalmente estoy utilizando Falkon:

Captura-pantalla_poniendo-clases.png

Primero hago los ajustes ahí, probando colores, márgenes, fuentes, etc. y luego lo paso al código definitivo de la hoja de estilos.

Conclusiones

Publicar proyectos es una forma sencilla, pero potente de crear contenido web que se puede emplear para muchas cosas desde org-mode. Una de los usos para desarrolladores es, generar la documentación para colocarla en Internet o distribuirla con el programa. Generar un epub fácilmente. Tener un sitio de Internet estático de una manera sencilla. También, en lugar de enfocar la publicación a html se puede enfocar a pdf con LaTeX, o incluso tener proyectos que generen la documentación en varios formatos: info, html, pdf, etc.

Hay un montón de aspectos que no he utilizado aquí, pero están explicados en la documentación de org.

Nota al pie de página:

1

Protención a la Infancia Contra el Abuso https://asociacionpica.org

2

Club de Aventuras AD en http://www.caad.es/... sí, aún existe el foro.

3

Su significado sería Quinta Tierra: en Esperanto Kvin significa 5 y Tero, Tierra.

4

Hay una explicación completa de dichas anclas en el apartado 4.8 Search Options in File Links del manual de org-mode.

5

Si la vas a emplear para algún proyecto profesional, que no se te olvide pagar los pocos euros que cuesta.

Categoría: emacs org-mode juegos librojuegos

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.