MokaCMS - Open Source per il Web Content Management

VII parte: Da XML ad PDF utilizzando XSLT e FO (seconda puntata)di e

Il foglio di stile xsl-fo per produrre automaticamente il PDF di un articolo nel nostro CMS

Riprendiamo lo sviluppo del foglio di stile xsl-fo che sarà  utilizzato per produrre automaticamente il PDF di un articolo di Mokabyte, riprendendo dallo sviluppo del piede di pagina e poi passando allo sviluppo del corpo dell‘articolo.

Nel numero precedente abbiamo iniziato lo sviluppo del foglio di stile impostando la gabbia e definendo l‘intestazione. Ora affrontiamo gli altri elementi del file.
Il piè di pagina dovrà  riportare il numero di pagina allineato al centro e sovrapposto ad una linea. Al termine di questa linea dovranno essere collocati 2 quadrati sovrapposti tra loro e di colore diverso; verde scuro il quadrato posto in primo piano e verde chiaro il quadrato messo in secondo piano.
Ã? più difficile a dirsi che a farsi, e non è molto diverso da ciò che è stato fatto per l‘intestazione.
La figura 1 mostra come desideriamo che appaia il piè di pagina.

Tutto il piè di pagina dovrà  essere contenuto in un fo:block-container. Si ricordi che questi elementi possono essere posizionati ovunque a differenza dei fo:block che vengono inseriti uno dietro l‘altro.
Tramite gli attributi top, bottom, left e right, si è in grado di posizionare il contenitore. Non è necessario specificare tutti e quattro gli attributi, ne basta solo 1, e in questo caso sono stati specificati bottom e left.
Il contenitore sarà  alto 1,25 cm e largo 14,5 cm, e disterà  di 2 cm dal bordo basso del foglio e di 3,60 cm dal bordo sinistro.
All‘interno di questo contenitore si dovranno inserire tre fo:block-container e un fo:block. Il primo sarà  di tipo fo:block-container, dovrà  contenere il numero di pagina.
Il numero di pagina è dato dall‘elemento fo:page-number il quale manterrà  in automatico il conteggio delle pagine senza l‘apporto di programmazione. Il secondo blocco sarà  di tipo fo:block e conterrà  le linee estetiche. Le linee sono due perchà© nel mezzo è posizionato il blocco che contiene il numero di pagina.
Le linee sono create sempre con il fo:leader, proprio come è stato fatto per l‘intestazione.
Rimangono due fo:block-container da esaminare. il primo risulterà  un quadrato verde chiaro, alto e largo 0,5 cm; dista dal bordo destro del contenitore principale di 0,2 cm e dal bordo sinistro sempre del contenitore principale di 14,2 cm. Il secondo è come il primo con la differenza che è di colore verde scuro e che è posizionato a 0,3 cm dal bordo alto, a 14,4 cm dal bordo sinistro e a 0 cm dal bordo destro. In questo modo si ottiene il risultato mostrato precedentemente in Figura 6.

Il corpo della pagina

Prima di costruire quest‘ ultima parte bisogna effettuare ulteriori modifiche all‘xml.
Si è deciso di inserire un‘immagine dell‘autore, la quale potrebbe essere la fotografia dell‘autore stesso, o magari un suo logo, o più semplicemente il logo di Mokabyte. La struttura e la logica applicata per questo scopo sono le stesse che sono state usate per reperire in maniera semplice le informazioni relative alla rivista. Ã? stato dunque creato l‘elemento author contenente gli elementi name e image. Nell‘elemento image è contenuto l‘url dell‘immagine, mentre nell‘elemento name è presente il nome dell‘autore:

Di Simona De Rosa e Massimiliano Bigatti image/author_big_3x3.gif

Sempre per un‘organizzazione migliore dei dati è stato aggiunto l‘elemento introduction, che conterrà  alcune informazioni sull‘autore, come una breve biografia.
Inoltre è stata aggiunta una struttura di elementi per la creazione dinamica di tabelle all‘interno del documento. In questo modo si è in grado di costruire tabelle di numero di righe e di colonne diverse, utilizzando sempre la stessa parte di codice per la trasformazione.

...

L‘elemento table può contenere da uno a enne elementi row, di conseguenza quando questi sono ciclati da un‘elemento xsl:for-each fanno in modo che la tabella sia dinamica in numero di righe.
Lo stesso concetto si applica per l‘elemento column presente all‘interno dell‘elemento row. Cosi facendo si è in grado di costruire dinamicamente oltre che le righe anche le colonne di una tabella.
L‘ importante è che ogni elemento row contenga lo stesso numero di elementi column. Purtroppo è necessario imporre che numero di colonne sia uguale per tutte le righe, perchà© in xsl-fo a differenza dell‘ HTML è obbligatorio dichiarare a priori quante colonne saranno presenti nella tabella. Ci si può chiedere dunque come fa ad essere una tabella dinamica. La dinamicità  di questa tabella è infatti solo a livello di codice. Si sa che in un‘ xml se un campo è presente più volte, può essere prelevato in ciclo con l‘elemento xsl:for-each. In realtà  non è la tabella ad essere dinamica, ma è la sua costruzione a livello di xsl-fo che è dinamica. Si vedrà  poi come realizzare questo concetto.
Per prima cosa verrà  posizionata l‘immagine dell‘autore.

...

Si assegni all‘elemento xsl:variable l‘url dell‘immagine. Si creerà  nuovamente un elemento fo:block-container che conterrà  l‘immagine. In realtà  conterrà  l‘elemento fo:external-graphic, che è l‘equivalente dell‘elemento img di HTML.
Si noterà  dal codice che il contenitore è posizionato a -4cm dal bordo sinistro del foglio. Si ricordi che anche la region-body dista 4cm dal bordo sinistro del foglio. Se nell‘attributo left ci fosse stato 0cm l‘immagine sarebbe stata collocata all‘interno della region-body causando una sovrapposizione al testo che in seguito verrà  scritto. Quindi spostando il blocco che conterrà  l‘immagine a -4cm dal bordo, significa che la distanza tra il bordo e il contenitore è pari a 0cm di conseguenza l‘immagine è situata in un area non utilizzata della pagina. Cosଠfacendo non si creano sovrapposizioni. Originariamente il blocco era situato nella region-start, e questo faceva in modo che l‘immagine fosse ripetuta in ogni pagina dell‘articolo. E questo non è per niente estetico, ma può risultare utile in altre occasioni.
Lo stesso blocco contiene anche l‘elemento fo:block utilizzato per riportare il nome dell‘autore. Cosi il nome viene posizionato successivamente all‘immagine.

Costruzione dell‘articolo

Con l‘elemento xsl:for-each si eseguirà  un ciclo per tutti i nodi figli. Sarà  creato un blocco semplice per ogni nodo figlio, e tramite l‘utilizzo dell‘elemento xsl:use-attribute-sets ad ogni blocco sarà  applicato lo stile appropriato, come si vede dall‘esempio.

...

Ritornando al discorso sulle tabelle, analizzato in parte precedentemente, si veda la costruzione di una tabella in xsl-fo.

........................

In xsl-fo Si è obbligati a dichiarare quante colonne si utilizzeranno e quale grandezza ha ognuna di queste. Cosi si spiega l‘esistenza dell‘attributo width nel tag column.
Inoltre ogni volta che si costruisce una cella in fo, bisogna riportarne il numero.
Si riporta di seguito il codice necessario alla costruzione di una tabella, con le specifiche del caso.

Il primo xsl:for-each costruisce la dichiarazione delle colonne, infatti cicla l‘elemento row, ma attenzione, cicla l‘elemento solo dove la posizione dell‘elemento ciclato è uno, per questo si impone che nell‘xml i tag column siano di ugual numero per ogni elemento row. In questo ciclo sono prelevati il numero di colonna dato dalla funzione position() e la larghezza stessa della colonna, specificata nell‘attributo width del tag column. La funzione position() ritorna la posizione dell‘elemento ciclato (in questo caso column) all‘interno dell‘elemento padre, che sempre nel nostro caso è l‘elemento row dove la sua posizione è uno, cioè il primo elemento row in assoluto.
Il for-each interno, invece cicla le colonne, assegnando alla variabile nColonna la posizione dell‘elemento corrente e alla variabile width il valore dell‘attributo width, cioè la larghezza della colonna.
Il secondo xsl:for-each esterno, invece, costruisce il corpo della tabella, applicando la stessa logica usata per la dichiarazione delle colonne, e prelevando il contenuto dell‘elemento column.
Proseguendo, si incontra un elemento nuovo, il fo:list-block. Questo elemento è utilizzato per creare elenchi puntati e/o numerati. Al suo interno è contenuto l‘elemento list-item il quale corrisponde ad una riga dell‘elenco. All‘interno di quest‘ ultimo troviamo altri due elementi: list-item-label e list-item-body.
Il primo deve contenere la numerazione dell‘elenco, oppure il simbolo da utilizzare; mentre il secondo contiene il testo dell‘elenco.

Eseguire la trasformazione Fop

Fino ad ora si è parlato di come costruire un foglio di stile per la formattazione di un‘articolo, ma non è ancora stato detto come effettuare la trasformazione e quindi creare cosi il PDF.
Per eseguire la trasformazione è necessaria una implementazione di Fop. Quella qui utilizzata è la 0.20.5 sviluppata in Java da Apache Software Foundation, ed è reperibile al seguente link www.apache.org/dist/xml/fop/ .
Successivamente si apra una finestra DOS, e ci si posizioni nella directory di Fop appena scaricata.
Ad esempio se Fop è stato scaricato direttamente in C:programmi, si digiti il seguente comando:

cd c:programmifop-0.20.5

una volta giunti qui possiamo eseguire il comando necessario alla trasformazione.
La sintassi è la seguente.

fop ?xsl percorsoFile/nomeFile.xsl ?xml percorsoFile/nomeFile.xml ?pdf percorsoFile/nomeFile.pdf 

quindi il comando necessario per la trasformazione dell‘articolo è il seguente:

fop ?xsl c:/articolo/mokaByteArticle.xsl ?xml c:/articolo/articolo.xml ?pdf c:/articolo/mokaByteArticle.pdf

Premendo invio, si vedrà  che il processore Fop esegue la trasformazione come mostrato in figura 2.

Finita la trasformazione in c:articolo troveremo finalmente il file PDF creato.

Aggiungere i fonts in Fop

Un‘ultima cosa, ma non per questo meno importante, è l‘utilizzo dei font.
I font disponibili in Fop sono solo cinque: Helvetica, Times New Roman, Courier New, Symbol e ZapfDingbats. Di conseguenza, per utilizzare altri font al di fuori di questi è necessario installarli.
Innanzi tutto è necessario stabilire dove risiedono i font di sistema, e in genere sono nella cartella C:WINDOWSFonts.

Tramite una classe di Fop, esattamente la TTFReader, si possono creare gli xml dei font di sistema da passare in input a Fop. Prima di creare gli xml bisogna creare la cartella in cui metterli. Quindi verrà  creata la cartella xmlFonts nella directory C:fop-0.20.5.
Una volta creati gli xml, bisogna modificare il file di configurazione userconfig.xml di Fop, situato nella directory C:fop-0.20.5conf.
Infine, bisogna aggiungere un parametro alla riga di chiamata del processore fop (-c; indica a fop che deve utilizzare un file di configurazione) e il percorso di userconfig.xml nel comando usato per la trasformazione.
Per esempio, si ipotizzi di voler utilizzare il font Tahoma in Fop. Per creare il file xml relativo a questo font bisogna eseguire le seguenti istruzioni:

cd C:Programmifop-0.20.5java -cp "buildfop.jar;libxercesImpl.jar;libxalan.jar" org.apache.fop.fonts.apps.TTFReader  WINDOWSFontsTAHOMA.TTF C:Programmifop-0.20.5xmlFonts	ahoma.xmljava -cp "buildfop.jar;libxercesImpl.jar;libxalan.jar" org.apache.fop.fonts.apps.TTFReader  WINDOWSFontsTAHOMABD.TTF C:Programmifop-0.20.5xmlFonts	ahomaBold.xml

La classe TTFReader risiede nel fop.jar. Sono inoltre richiesti i jar xercesImpl e xalan

Come si può notare, con questi comandi si otterranno due xml, uno per tahoma e uno per tahoma bold.
Una volta eseguito questo comando, è necessario aggiungere due chiavi, al file userconfig.xml, come riportato di seguito.

baseDirC:articolofontBaseDirC:programmifop-0.20.5xmlFonts

La prima indica dove risiedono i file necessari alla trasformazione, e la seconda specifica dove risiedono il file xml relativi ai font da utilizzare durante la trasformazione. Dopo di che, nell‘elemento fonts presente in userconfig.xml, bisogna dichiarare i font creati, specificando l‘xml, il TTF, il nome che servirà  al riconoscimento durante la trasformazione e lo stile (grassetto, normale, corsivo ecc).

Infine, per utilizzare effettivamente questo font nel foglio di stile creato in precedenza è necessario sostituire il valore dell‘attributo font-family - Helvetica con Tahoma per lo stile normale e TahomaBD per lo stile in grassetto. Questi nomi sono gli stessi indicati in userconfig.xml.

Ora il comando necessario alla trasformazione avrà , come già  accennato, un parametro in più:

fop -c C:/Programmi/fop-0.20.5/conf/userconfig.xml ?xsl c:/articolo/mokaByteArticle.xsl ?xml c:/articolo/articolo.xml ?pdf c:/articolo/mokaByteArticle.pdf

Se invece si desidera eseguire la trasformazione Fop da programma è possibile invocare direttamente le classi implementate da Apache Fop in modo simile al seguente:

String outFile = "mokaByteArticle.pdf";String xmlFile = "articolo.xml";String xslFile = "mokaByteArticle.xsl";String userConfig ="userconfig.xml"; Driver driver = new Driver();driver.setRenderer(Driver.RENDER_PDF);driver.setOutputStream(new java.io.FileOutputStream(outFile));//imposta le opzioni (non c‘è relazione con l‘oggetto Driver in quanto è una classe statica)userConfigFile = new File(userConfig);options = new Options(userConfigFile);Result res = new SAXResult(driver.getContentHandler());Source src = new StreamSource(xmlFile);Source xsltSrc = new StreamSource(xslFile);TransformerFactory transformerFactory = TransformerFactory.newInstance();Transformer transformer = transformerFactory.newTransformer(xsltSrc);transformer.transform(src, res);

Conclusioni

In questi due articoli si è visto come costruire un foglio di stile per generare un PDF a partire da un documento XML, producendo un risultato gradevole e d‘aspetto professionale. Il materiale qui illustrato sarà  integrato prossimamente nel sistema MokaCMS.

Condividi

Pubblicato nel numero
99 settembre 2005
Massimiliano Bigatti è sviluppatore senior, autore tecnico e appassionato di fotografia. Certificato, tra le altre, come SUN Certified Enterprise Architect for Java2 Enterprise Edition Technology, lavora presso un importante business solution provider. Nel tempo libero scrive per diverse riviste di informatica e ha scritto una decina di libri, quasi tutti…
Articoli nella stessa serie
Ti potrebbe interessare anche
Tags