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.
Figura 1 - Il piè di pagina
<xsl:template
name='footerTemplate'>
<fo:block-container
height="1.25cm" width="14.5cm" bottom="2.00cm"
left="3.60cm" position="absolute">
<fo:block-container height="0.50cm" width="1.0cm"
top="0.10cm" left="6.75cm" right="6.75cm"
position="absolute">
<fo:block xsl:use-attribute-sets="footer"><fo:page-number/></fo:block>
</fo:block-container>
<fo:block xsl:use-attribute-sets="footer">
<fo:leader leader-pattern="rule" rule-thickness="0.01cm"
leader-length="6.75cm" color="rgb(0,102,0)"/>
<fo:leader leader-pattern="space" rule-thickness="0.01cm"
leader-length="0.5cm"/>
<fo:leader leader-pattern="space" rule-thickness="0.01cm"
leader-length="0.5cm"/>
<fo:leader leader-pattern="rule" rule-thickness="0.01cm"
leader-length="6.75cm" color="rgb(0,102,0)"/>
</fo:block>
<fo:block-container background-color="rgb(166,206,57)"
height="0.5cm" width="0.5cm" left="14.2cm"
right="0.2cm" position="absolute"/>
<fo:block-container background-color="rgb(0,144,102)"
height="0.5cm" width="0.5cm" top="0.3cm"
left="14.4cm" right="0.0cm" position="absolute"/>
</fo:block-container>
</xsl:template>
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:
<author>
<name>Di Simona De Rosa e Massimiliano Bigatti</name>
<image>image/author_big_3x3.gif</image>
</author>
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.
<table>
<row>
<column width="8.00cm">
<column width="6.50cm">
</row>
</table>
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.
<xsl:template
name='bodyTemplate'>
<xsl:variable name="image" select="author/image"/>
<fo:block-container height="6.0cm" width="3.60cm"
top="1cm" left="-4.0cm" position="absolute">
<fo:block xsl:use-attribute-sets="author">
<fo:external-graphic src="url('{$image}')"/>
</fo:block>
<fo:block xsl:use-attribute-sets="author">
<xsl:value-of select="author/name"/>
</fo:block>
</fo:block-container>
</xsl:template>
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.
<fo:block>
<xsl:for-each select="child::*">
<xsl:choose>
<xsl:when test="name()='introduction'">
<fo:block xsl:use-attribute-sets="introduction"><xsl:value-of
select="."/></fo:block>
</xsl:when>
<xsl:when test="name()='title'">
<fo:block xsl:use-attribute-sets="title"><xsl:value-of
select="."/></fo:block>
</xsl:when>
<xsl:when test="name()='paragraph'">
<fo:block xsl:use-attribute-sets="subTitle"><xsl:value-of
select="."/></fo:block>
</xsl:when>
Ritornando
al discorso sulle tabelle, analizzato in parte precedentemente,
si veda la costruzione di una tabella in xsl-fo.
<fo:table
xsl:use-attribute-sets="table">
<fo:table-column column-number='1' column-width='width'/>
<fo:table-column column-number='N' column-width='
width '/>
<fo:table-body>
<fo:table-row>
<fo:table-cell column-number='1' ><fo:block>
</fo:block></fo:table-cell>
<fo:table-cell column-number='N'><fo:block>
</fo:block></fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell column-number='1'><fo:block>
</fo:block></fo:table-cell>
<fo:table-cell column-number='N'><fo:block>
</fo:block></fo:table-cell>
</fo:table-row>
</fo:table-body>
<fo:table>
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.
<xsl:when
test="name()='table'">
<fo:table xsl:use-attribute-sets="table">
<xsl:for-each select="row[position()='1']">
<xsl:for-each select="column">
<xsl:variable name="nColonna" select="position()"/>
<xsl:variable name="width" select="@width"/>
<fo:table-column column-number='{$nColonna}' column-width='{$width}'/>
</xsl:for-each>
</xsl:for-each>
<fo:table-body>
<xsl:for-each select="row">
<fo:table-row>
<xsl:for-each select="column">
<xsl:variable name="nColonna" select="position()"/>
<fo:table-cell column-number='{$nColonna}' xsl:use-attribute-sets="border.cell">
<fo:block><xsl:value-of select="."/></fo:block>
</fo:table-cell>
</xsl:for-each>
</fo:table-row>
</xsl:for-each>
</fo:table-body>
</fo:table>
</xsl:when>
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.
<xsl:when
test="name()='ordered-list'">
<xsl:variable name="positionNode"><xsl:number
count="ordered-list" format="1"/></xsl:variable>
<xsl:if test="$positionNode='1'">
<fo:block xsl:use-attribute-sets="listBlockPadding"/>
</xsl:if>
<fo:block>
<fo:list-block xsl:use-attribute-sets="list.Block">
<fo:list-item>
<fo:list-item-label xsl:use-attribute-sets="list.item.label">
<fo:block xsl:use-attribute-sets="font.list.item.label.order"><xsl:number
count="ordered-list" format="1."/></fo:block>
</fo:list-item-label>
<fo:list-item-body xsl:use-attribute-sets="list.item.body">
<fo:block><xsl:value-of select="."/></fo:block>
</fo:list-item-body>
</fo:list-item>
</fo:list-block>
</fo:block>
</xsl:when>
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:\programmi\fop-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.
Figura 2 - trasformazione Fop
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:\WINDOWS\Fonts.
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.5\conf.
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:\Programmi\fop-0.20.5
java
-cp "build\fop.jar;lib\xercesImpl.jar;lib\xalan.jar"
org.apache.fop.fonts.apps.TTFReader \WINDOWS\Fonts\TAHOMA.TTF
C:\Programmi\fop-0.20.5\xmlFonts\tahoma.xml
java
-cp "build\fop.jar;lib\xercesImpl.jar;lib\xalan.jar"
org.apache.fop.fonts.apps.TTFReader \WINDOWS\Fonts\TAHOMABD.TTF
C:\Programmi\fop-0.20.5\xmlFonts\tahomaBold.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.
<entry>
<key>baseDir</key>
<value>C:\articolo</value>
</entry>
<entry>
<key>fontBaseDir</key>
<value>C:\programmi\fop-0.20.5\xmlFonts</value>
</entry>
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).
<fonts>
<!-- Tahoma -->
<font metrics-file="tahoma.xml" kerning="yes"
embed-file="C:\WINDOWS\Fonts\TAHOMA.TTF">
<font-triplet name="Tahoma" style="normal"
weight="normal"/>
</font>
<font metrics-file="tahomaBold.xml" kerning="yes"
embed-file="C:\WINDOWS\Fonts\TAHOMABD.TTF">
<font-triplet name="TahomaBD" style="normal"
weight="bold"/>
</font>
</fonts>
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.
|