Cuaderno de GNUtas

Compilando LuaLaTeX a varios PDF

Un libro que compuse recientemente fue un volumen de actas de un congreso de crítica textual donde los capítulos consistían en las contribuciones de cada ponente. Cuando el libro se publicase tenía que mandarles a los autores una separata en PDF de su artículo. Había cerca de una veintena de artículos. Separar el PDF del volumen en distintos PDFs, por rango de página, no tiene ninguna dificultad y hay muchas herramientas para ello, tanto gráficas como de línea de comandos. De este último grupo yo suelo usar casi siempre el versátil script pdfjam, que ejecuta pdfLATEX en segundo plano. Incluso tengo escrita una función para Emacs que agiliza bastante el proceso. El problema es que hacer tantos cortes en un PDF resulta un proceso pesado y repetitivo, siendo la pesadez mayor la de ir mirando cada rango de páginas para ir creando las separatas.

Ante un paisaje así, uno puede pensar si sería posible que LuaLATEX compilara no un solo PDF, sino varios PDF por capítulo. La respuesta rápida suele ser «no». Pero si nos dejan algo más de tiempo para contestar, podemos explicar este apaño, que consiste en los siguientes dos pasos:

  1. Hacer que LuaLATEX genere un script en shell que contenga, de manera reiterada, las órdenes de pdfjam para separar cada capítulo del PDF final.
  2. Pedirle también a LuaLATEX que ejecute ese script cuando ya haya completado su trabajo de compilar el PDF «grande». Así, al final tendremos, junto a éste, todos sus PDF «vástagos».

Claro, la situación es muy idílica. Pero el problema se concreta en: ¿Cómo sabe LuaLATEX de qué página a qué página cortar y cómo se las apañará para generar nuestro script durante la compilación. Para el primer escollo tenemos una muy buena solución con el paquete pageslts, que nos permite declarar diversos valores variables / contadores como CurrentPage1. Para lo segundo, nuestro mejor aliado son las primitivas de TEX \immediate\write\, que se encargan de generar un archivo de texto en la compilación, nuestro ansiado script. Quedaría ver cómo hacer que luego lo ejecute LuaLATEX, pero eso lo dejamos para el final. De momento, comenzamos a hacer lo que más nos divierte, que es escribir código.

Nuestro preámbulo

Partamos de un ejemplo hipotético donde tenemos tres capítulos (clase book) y un poco de texto introductorio antes. Comenzamos cargando lo mínimo necesario para nuestro ejemplo, que llamaremos ejemplo.tex:

\documentclass[a4paper]{book}
\usepackage{pageslts}
% Nos hará falta también el paquete calc
\usepackage{calc}
% y lipsum, para generar texto falso
\usepackage{lipsum}

A continuación, declaramos una variable / contador para la primera página y nos aseguramos de comenzar con el valor de 1:

\newcounter{PrimPag}
\setcounter{PrimPag}{1}

En este punto, declaramos el archivo de texto (ergo el script) que escribirá LuaLATEX por nosotros:

\newwrite\script
\immediate\openout\script=separatas.sh %% Así se llamará nuestro script

Y, como se trata de un script de bash, la orden necesaria para que se añada al principio de todo el shebang (ojo, no podemos escribir literalmente «#», porque nos traerá un problema de catcodes. Tenemos que anteponer antes un \string:

\immediate\write\script{\string#!/bin/bash} % escribe el shebang

Una vez escrito todo esto, ahora es cuando viene el meollo del asunto: nuestro comando de LATEX que se encargue de añadir las sucesivas órdenes de pdfjam para que se corte cada capítulo. La idea es que el valor de nuestro contador PrimPag vaya variando tras corte. Así pues, el rango será <el valor de PrimPag +1> (de ahí nuestra necesidad de cargar el paquete calc a <el valor que tenga CurrentPage en cada caso>:

\newcommand{\separata}[1]{%
%la orden de pdfjam:
\immediate\write\script{pdfjam\space \jobname.pdf\space \thePrimPag -\theCurrentPage\space -o\space \jobname-#1.pdf}%
% recalcula el valor de PrimPag añadiendo 1
\setcounter{PrimPag}{\theCurrentPage+1}
}

Nuestro documento

Escribimos el cuerpo de nuestro documento de ejemplo, con la siguiente estructura:

  \begin{document}
  \pagenumbering{arabic} % necesario para pageslts
  % Texto introductorio antes de los capítulos. Este también lo cortará, pero no nos hará
  % falta al final
  \lipsum[1-50]
  % Nuestro primer corte:
  \separata{intro}
%% Ojo, por defecto en la clase book los capítulos comienzan siempre en página impar. Para
%% asegurar bien la primera página del rango, debemos actualizarla antes de cada capítulo
  \setcounter{PrimPag}{\theCurrentPage}
  \chapter{Primer artículo}
  \lipsum[1-30]
  %nuestro segundo corte
  \separata{Primer_artículo}
  \setcounter{PrimPag}{\theCurrentPage}
  \chapter{Segundo artículo}
  \lipsum[1-30]
  %nuestro tercer corte
  \separata{Segundo_artículo}
  \setcounter{PrimPag}{\theCurrentPage}
  \chapter{Tercer artículo}
  \lipsum[1-30]
  %nuestro cuarto corte
  \separata{Tercer_artículo}
  % Y aquí añadimos el término "exit" al script y lo cerramos
  \immediate\write\script{exit}%
  \immediate\closeout\script%
  \end{document}

Si compilamos nuestro documento, obtendremos el PDF grande y, de propina, nuestro script ya listo para ejecutarlo cuando queramos, separatas.sh, que habrá quedado, finalmente, con este contenido:

#!/bin/bash
pdfjam ejemplo.pdf 1-9 -o ejemplo-intro.pdf
pdfjam ejemplo.pdf 10-16 -o ejemplo-Primer_artículo.pdf
pdfjam ejemplo.pdf 17-22 -o ejemplo-Segundo_artículo.pdf
pdfjam ejemplo.pdf 23-28 -o ejemplo-Tercer_artículo.pdf
exit

Pero si hemos llegado hasta aquí, seguramente habremos alcanzado tal nivel de excelencia en la pereza que querremos delegar la ejecución del script en el propio LuaLATEX. Fácil. Tendremos que cargar en nuestro documento el paquete shellesc, que nos permitirá ejecutar comandos de shell en la compilación. Y al final de nuestro documento, justo antes del \end{document}, añadimos tres órdenes: una para darle permisos de ejecución al script, otra para correr de nuevo LuaLATEX y que pueda generarse aquél. La última, la propia ejecución del script

\ShellEscape{%
chmod a+x separatas.sh && lualatex ejemplo.tex && ./separatas.sh}

Con esta macro añadida (\ShellEscape) no podremos compilar nuestro documento de manera normal, porque no tendrían efecto los tres anteriores comandos de shell. La razón es que LATEX no tiene permisos para ejecutar órdenes del sistema, a fin de evitar catástrofes inesperadas. Debemos correr la compilación desde línea de comandos, con la opción shell-escape:

lualatex --shell-escape ejemplo.tex

Publicado: 07/11/2019

Última actualización: 07/11/2019


Índice general

Acerca de...

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

Notas:

1

Para esta ingeniosa argucia me he inspirado en una de las respuestas de este hilo de texstackexchange.

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