
PoDoFo è una libreria C++ free che serve per la gestione e modifica di file PDF. Potrace invece è un tool che consente di trasformare una bitmap in svg tramite comandi da terminale.
Prima di commentare il codice vediamo brevemente la procedura di installazione di PoDoFo e delle sue dipendenze
Dipendenze:
1 |
sudo apt-get install imagemagick inkscape pdftk cmake libcppunit-dev libfreetype6-dev libfontconfig1-dev libjpeg-dev libtiff-dev libtiff4-dev libpng12-dev libssl-dev zlib1g-dev potrace |
Andare su http://podofo.sourceforge.net/download.html e scaricare l'ultima versione stabile di PoDoFo. Per generalità diciamo che l'ultima versione è la 0.9.* (al posto dell'asterisco, d'ora in avanti, mettete il numero della vostra versione).
Estrarre l'archivio scaricato, e posizionarsi tramite terminale all'interno della cartella.
1 2 3 4 5 6 |
cd podofo-0.9.* mkdir podofo-build cd podofo-build cmake -G "Unix Makefiles" ../ make sudo make install |
Una volta installato tutto quanto vediamo l'idea che sta dietro all'estrazione delle note manuali in formato SVG.
Supponendo di avere a disposizione sia il PDF annotato che quello non annotato, l'algoritmo è riassumibile in questi brevi step:
- Cancellazione dei file di esecuzioni precedenti
- Salvataggio delle pagine dei PDF in immagini PNG
- Creazione delle immagini differenza in formato PNM tra le pagine dei due PDF
- Tramite Potrace si convertono questi PNM in SVG
Il codice completo è qua sotto. Ricordate di passare come argomenti il path del PDF originario e quello annotato.
Ecco due PDF di prova: original.pdf e ann.pdf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
#include <iostream> #include <vector> #include <string> #include <stdlib.h> #include <stdio.h> #include <podofo/podofo.h> using namespace PoDoFo; using namespace std; // Divide una stringa ogni volta che trova delimiters e copia le parti in un vettore void Tokenize(const string& str, vector <string>& tokens, const string& delimiters){ // Skip delimiters at beginning. string::size_type lastPos = str.find_first_not_of(delimiters, 0); // Find first "non-delimiter". string::size_type pos = str.find_first_of(delimiters, lastPos); while (string::npos != pos || string::npos != lastPos) { // Found a token, add it to the vector. tokens.push_back(str.substr(lastPos, pos - lastPos)); // Skip delimiters. Note the "not_of" lastPos = str.find_first_not_of(delimiters, pos); // Find next "non-delimiter" pos = str.find_first_of(delimiters, lastPos); } } // Mette in una stringa l'output di un comando da terminale string GetStdoutFromCommand(string cmd) { string data; FILE * stream; const int max_buffer = 256; char buffer[max_buffer]; cmd.append(" 2>&1"); stream = popen(cmd.c_str(), "r"); if (stream) { while (!feof(stream)) if (fgets(buffer, max_buffer, stream) != NULL) data.append(buffer); pclose(stream); } return data; } // Funzione main int main(int argc, char *argv[]) { system("rm *.svg && rm *.png && rm *.pnm && rm *.svg"); string pdfPath=argv[1]; string pdfWithNotesPath=argv[2]; PdfMemDocument pdfWithoutNotes(argv[1]); //pdf con note PdfMemDocument pdfWithNotes(argv[2]); //pdf con note string pdfConvert = "convert -density 150 -depth 8 "+pdfPath+" page_%01d.png"; system(pdfConvert.c_str()); string pdfWithNotesConvert = "convert -density 150 -depth 8 "+pdfWithNotesPath+" pageWithNotes_%01d.png"; system(pdfWithNotesConvert.c_str()); string ls2 = GetStdoutFromCommand("ls page_*.png"); vector <string> pagePnm; Tokenize(ls2, pagePnm, "\n"); ls2 = GetStdoutFromCommand("ls pageWithNotes_*.png"); vector <string> pagePnmWithNotes; Tokenize(ls2, pagePnmWithNotes, "\n"); string resultCompare; string command; for (int indexPdfPage = 0; indexPdfPage < pagePnm.size(); indexPdfPage++) { //serve per avere una misura di somiglianza tra le due pagine command = "compare -metric PSNR "+pagePnm[indexPdfPage]+" "+pagePnmWithNotes[indexPdfPage]+" diffNotes.png"; resultCompare = GetStdoutFromCommand(command); if(resultCompare.find("inf")==string::npos){ //si crea una mappa binaria che rappresenta la differenza delle due immagini command = "convert "+pagePnm[indexPdfPage]+" "+pagePnmWithNotes[indexPdfPage]+" -density 200 -compose difference -composite -separate -evaluate-sequence Add diffNotes.png"; system(command.c_str()); cout << command << endl; //conversione in pnm (formato che vuole potrace per la conversione in svg), invertendo l'immagine e sostituendo il colore bianco con il trasparente command = "convert diffNotes.png -matte +clone -transparent \"#000000\" -composite -negate diffNotes.pnm"; system(command.c_str()); system("convert diffNotes.pnm -resize 600x diffNotes.pnm"); //creo svg dal pnm con potrace system("potrace diffNotes.pnm -s -o diffNotes.svg"); //ridimensionamento del file svg (di default è a 750px) //NB: 480 sono pt, non px! (480pt=600px) stringstream ss; ss << indexPdfPage; system(("rsvg-convert diffNotes.svg -w 480 -f svg -o Note_"+ss.str()+".svg").c_str()); } } system("rm diffNotes*"); return 0; } |
Se avete bisogno di ulteriori chiarimenti, non esitate a scrivermi nei commenti.
Alla prossima!