Cuaderno de GNUtas

Conservar los enlaces de los PDFs de un documento LaTeX compilado con pdfpages (un apaño con Elisp y pdftk)

¿Qué tenemos?
Un documento *.tex* «maestro» que contiene múltiples ordenes de \includepdf y su correspondiente tabla de contenido.
¿Qué queremos obtener?
Un PDF resultante, exactamente igual al que obtenemos con el paquete pdfpages, sólo que ahora se conservarán los enlaces activos de los PDFs incluidos. La tabla de contenido no tendrá enlaces, pero nuestro nuevo PDF sí contará con marcadores para navegación interna. Cada nuevo marcador será un título de sección que corresponde a su PDF incrustado.

La función en Elisp que he escrito requiere del programa pdftk. Y, por supuesto, que estemos dentro de GNU Emacs ☺.

La idea es ejecutar un bucle que va añadiendo los marcadores a cada sub-PDF1. Toda la información que necesita la función (títulos de sección para crear los marcadores y nombre/ruta de cada PDF incluido) la extrae del archivo maestro *tex. No necesita más, y no importa si los sub-pdf están en otros directorios, etc. Es decir, que esta función obtiene lo que precisa de las órdenes \includepdf y \addcontenstsline, dentro de nuestro documento, y no se va a vendimiar en otros sitios.

La función es muy sencilla de usar: dentro de nuestro ducumento maestro (y una vez compilado de manera normal para asegurarnos de que todos los PDFs están incluidos), llamamos a la función mi-pdf-con-enlaces. Al instante se nos pedirá en el minibúfer que introduzcamos un rango de páginas previas hasta el primer PDF incrustado. Es decir, que si el primer PDF incrustado empieza en la pág. 10, introducimos el rango «1-9». ¡Y ya está!. En un periquete tenemos disponible nuestro PDF, exactamente igual que el que obtenemos compilando con pdfpages, pero con todos los enlaces conservados y los marcadores de navegación interna con los títulos de sección.

Definimos, primero, estas dos funciones previas para obtener el nombre/ruta cada PDF y el título de las secciones:

(defun extrae-pdf-seccion ()
  (save-excursion
    (re-search-backward "addcontentsline{" nil t)
    (re-search-forward "addcontentsline{" nil t)
    (sp-beginning-of-next-sexp 2)
    (let
	((punto (point)))
      (sp-end-of-sexp)
      (save-restriction
	(narrow-to-region punto (point))
	(buffer-string)))))

(defun extrae-pdf-ruta ()
  (save-excursion
    (re-search-forward "pdf}" nil t)
    (re-search-backward "{" nil t)
    (forward-char 1)
    (let
	((punto (point)))
      (sp-end-of-sexp)
      (save-restriction
	(narrow-to-region punto (point))
	(buffer-string)))))

Y nuestra función principal:

(defun mi-pdf-con-enlaces ()
  (interactive)
  (let*
      ((dir-temp "/tmp/pdftk_trabajo")
       (marcadores (concat dir-temp "/" "marcadores.txt"))
       (buf (file-name-sans-extension (buffer-name)))
       (prim-pags (read-from-minibuffer "Páginas previas: "))
       (resultado (format "%s_web.pdf" buf)))
    (shell-command (concat "rm -rf " dir-temp))
    (shell-command (concat "mkdir -p " dir-temp))
    ;; primero extraemos las primeras páginas
    (shell-command (concat "pdftk A=" buf ".pdf" " cat A" prim-pags  " output " dir-temp "/" buf ".pdf"))
    ;; obtenemos nombre de sección y ruta de cada PDF con un bucle
    (save-excursion
      (goto-char (point-min))
      (while (re-search-forward "includepdf" nil t)
	(let
	    ((seccion (extrae-pdf-seccion))
	     (pdf (expand-file-name (extrae-pdf-ruta))))
	  (write-region (concat "BookmarkBegin"
				"\nBookmarkTitle:" " " seccion
				"\nBookmarkLevel: 1"
				"\nBookmarkPageNumber: 1")
			nil marcadores)
	  (shell-command (concat
			  "pdftk "
			  pdf
			  ;; necesario para marcadores con acentos y demás
			  " update_info_utf8 "
			  marcadores
			  " output "
			  dir-temp
			  "/"
			  (file-name-base pdf)
			  ".pdf")))))
     ;; Y producimos el PDF final, que debe incluir ya los marcadores correctamente
    (shell-command (concat "pdftk " dir-temp "/" "*.pdf " " cat output " resultado))))

Conservar los enlaces de los PDF incluidos en LaTeX (una solución con Elisp y pdftk) from Juan Manuel Macías Chaín on Vimeo.

Publicado: 06/08/20

Última actualización: 06/08/20


Índice general

Acerca de...

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

Notas:

1

La idea del bucle la tomé de este hilo de StackExchenge traduciendo en términos de Elisp lo que allí se muestra mediante Bash.

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