News

14 Agosto 2018

Unire file pdf

Recentemente ho fatto parte del comitato organizzatore di una conferenza di fisica a Firenze (Plasmonica 2018). Tra i vari problemi tecnici che ho dovuto risolvere c'è stato quello della preparazione del libro degli abstract. Inizialmente avevo pensato a una classe LaTeX che, dopo le pagine iniziali, ritagliasse i pdf forniti dagli autori (secondo un template a loro fornito) per poter inserire una testatina e il numero di pagina. Avevo già optato per questa opzione in situazioni analoghe ma il breve tempo a disposizione mi ha fatto ripiegare su un approccio più spartano: la semplice concatenazione dei pdf, copertina, programma della conferenza e via via tutti gli abstract inviati dagli autori.

Ho usato programmi come PDF Split and Merge e Sejda SDK. Con mio rammarico ho scoperto però che i pdf da loro generati erano particolarmente indigesti ai vari lettori di PDF, compreso Acrobat, per errori nei parametri dei font. In alcune pagine infatti i caratteri si sovrapponevano, avevano spaziature esagerate, venivano sostituiti da simboli vari, ecc...

Ho scoperto che PDF Split and Merge, Sejda, ecc... semplicemente concatenano il codice dei pdf, senza particolari accorgimenti. Non ho indagato a fondo ma probabilmente il fatto di avere così tanti pdf diversi (gli abstract di tutti gli autori) creati con software diversi, caratteri diversi, immagini vettoriali contenenti caratteri di varie famiglie di font, credo abbia prodotto un disastro una volta concatenati tutti insieme.

La soluzione è stata di usare Ghostscript. Questo programma è un interprete vero e proprio, quindi non si limita a concatenare lo stream contenuto nei pdf ma lo interpreta e lo ricrea da zero. In questo modo il pdf così prodotto è in un certo senso ripulito e uniformato. Inoltre dato che lo stream del pdf viene ricreato da zero, si ottiene un file finale con dimensioni minori della semplice somma di tutte le dimensioni dei pdf di partenza. Senza contare che si possono cambiare diversi parametri, ad esempio la compressione delle immagini ecc... così da ottenere un file ancora più piccolo.

L'idea è stata quella di creare un file batch (.bat eseguibile su Windows). Il vantaggio di questo approccio è anche quello di poter avere in maniera semplice la lista dei file da concatenare, con la possibilità di spostarli, cambiarli, ecc... e ricreare il file unico con un semplice doppio clic sul file batch. Il contenuto del file batch è il seguente (necessita che sia installato Ghostscript e pdftk):

gswin64c.exe ^
-q -dNOPAUSE -dBATCH -dSAFER -sDEVICE=pdfwrite ^
-sOutputFile="merged.pdf" ^
-dDEVICEWIDTHPOINTS=595 -dDEVICEHEIGHTPOINTS=842 -dFIXEDMEDIA ^
-dPDFFitPage ^
-dPDFSETTINGS=/prepress -dCompatibilityLevel="1.5" ^
-dDOPDFMARKS ^
 "1_intro.pdf"^
 "2_Plasmonica2018_program.pdf"^
 "3_nanoantennas.pdf"^
 "cartella1\pluto.pdf"^
 ...
 "cartellaN\pippo.pdf"^
 "pdfmark.txt"
pause
pdftk A="merged.pdf" cat A1-end output merged-nobookmarks.pdf
pause

gswin64c.exe è l'eseguibile di Ghostscript sui sistemi Windows a 64 bit. Il carattere ^ (caret) serve per poter andare a capo negli script batch (in sostanza il caret è il carattere di escape e in questo caso lo si usa per fare l'escape del carattere invisibile di newline). Bisogna stare molto attenti a come usare gli spazi quando si usa il caret. Come potete notare c'è uno spazio prima di ogni nome di file che verrà concatenato. Quello spazio è fondamentale.

Ecco il significato delle opzioni passate a Ghostscript (si consiglia la consultazione del manuale ufficiale di Ghostscript):

-q: quiet
-dNOPAUSE: disable prompt and pause at the end of every page
-dBATCH: exit after finishing without showing a prompt 
-dSAFER : disable some dangerous commands and pipes
-sDEVICE=pdfwrite : select type of output
-dDEVICEWIDTHPOINTS=595 : width in points
-dDEVICEHEIGHTPOINTS=842 : heigth in points
-dFIXEDMEDIA : force paper size
-sOutputFile="merged.pdf" : specify output file 
-dPDFSETTINGS=/prepress : PDF optimization option (prepress is the best)
-dCompatibilityLevel="1.5" : PDF specifications. Default 1.4
-dPDFFitPage : the pages are scaled to fill the set page dimension
-dDOPDFMARKS : call the program pdfmark at the end of the operations.

In questo modo, in particolare grazie all'opzione prepress si ottiene un pdf di ottima qualità (se chiaramente la qualità dei pdf di partenza era ottima...) L'opzione prepress riassume in sé una miriade di opzioni (vedere il manuale di Ghostscript per i dettagli). Le immagini vettoriali rimangono tali, mentre le immagini raster vengono ricodificate. Per evitare questo si possono passare le ulteriori opzioni (chiaramente il file diventerà di dimensioni maggiori):

-dAutoFilterColorImages=false : normalmente è a true.
-dAutoFilterGrayImages=false : normalmente è a true.
-dColorImageFilter=/FlateEncode : normalmente DCTEncode
-dGrayImageFilter=/FlateEncode : normalmente DCTEncode

Il codice però non finisce qui. Nella lista dei file, l'ultimo, pdfmark.txt, contiene le opzioni per il programma pdfmark che viene lanciato automaticamente alla fine della concatenazione con l'opzione -dDOPDFMARKS. Questo programma applica al pdf i bookmarks e altri metadati del pdf, così come descritti in pdfmark.txt. Questo passo è necessario perché Ghostscript non sa trattare i bookmarks e i metadati dei pdf. In particolare ho impostato titolo, autore, ecc, del pdf e non ho aggiunto alcun bookmark. Il contenuto del file pdfmark.txt è il seguente (per la sintassi cercare su Google "pdfmark" e troverete le specifiche di questo linguaggio creato dalla Adobe):

[ /Author (Francesco Biccari)
  /CreationDate (2018-07-01)
  /Creator (Ghostscript 9.22)
  /Producer (Ghostscript 9.22)
  /Title (Plasmonica 2018. Book of abstracts)
  /DOCINFO pdfmark
  
[ /Title ()
  /Page 1
  /OUT pdfmark

Alla fine di questo processo si ottiene quindi il file merged.pdf. Manca ancora un passo. Purtroppo né Ghostscript né pdfmark hanno un'opzione per eliminare i bookmark da un file pdf. I bookmark dei file iniziali rimangono nel file finale, spesso non rimandano alla pagina corretta e in ogni caso sono da eliminare perché è spesso inutile tenere i bookmarks dei singoli file di partenza. Per fare questo o si usa Adobe Acrobat (a pagamento) oppure si può usare un piccolo trucco tramite pdftk.

Quello che fa l'ultima riga dello script è di chiamare il programma pdftk, facendolo lavorare sul file unico appena prodotto, merged.pdf. Dopodiché non si fa altro che "ricopiare" il contenuto del pdf in un altro file, specificando però l'intervallo di pagine (in questo caso comunque tutte). Se si specifica l'intervallo di pagine, pdftk non ricopia nel file finale i bookmarks. Si ottiene quindi infine il file merged-nobookmarks.pdf.