Utilizaremos el proyecto open source Apache FOP (Formatting Objects Processor) que nos da una serie de funciones para convertir un documento XML a un documento PDF, PS, Texto entre otros.
Este proyecto toma un archivo XSL-FO y lo convierte en formato PDF, por lo que habrá que convertir primero nuestro archivo XML a XSL-FO antes de convertirlo a un documento PDF.
Archivos XML y XSLT
Vamos a crear dos archivos, el primero será en formato xml. En este archivo se guardarán los datos de nuestro documento PDF.
Archivo glossary.xml
<?xml version="1.0"?> <glossary> <term-entry> <term>basic-link</term> <definition>The fo:basic-link is used for representing the start resource of a simple link.</definition> </term-entry> <term-entry> <term>bidi-override</term> <definition>The fo:bidi-override inline formatting object is used where it is necessary to override the default Unicode-bidirectionality algorithm direction for different (or nested) inline scripts in mixed-language documents.</definition> </term-entry> <term-entry> <term>block</term> <definition>The fo:block formatting object is commonly used for formatting paragraphs, titles, headlines, figure and table captions, etc.</definition> </term-entry>
Archivo glossary.xslt
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" version="1.0"> <xsl:template match="glossary"> <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"> <fo:layout-master-set> <fo:simple-page-master master-name="all" page-height="11.5in" page-width="8.5in" margin-top="1in" margin-bottom="1in" margin-left="0.75in" margin-right="0.75in"> <fo:region-body margin-top="1in" margin-bottom="0.75in"/> <fo:region-before extent="0.75in"/> <fo:region-after extent="0.5in"/> </fo:simple-page-master> </fo:layout-master-set> <fo:page-sequence master-reference="all" format="i"> <fo:static-content flow-name="xsl-region-before"> <fo:block text-align="start" font-size="10pt" font-family="serif" line-height="1em + 2pt"> <fo:retrieve-marker retrieve-class-name="term" retrieve-boundary="page" retrieve-position="first-starting-within-page"/> <fo:leader leader-alignment="reference-area" leader-pattern="dots" leader-length="4in"/> <fo:retrieve-marker retrieve-class-name="term" retrieve-boundary="page" retrieve-position="last-ending-within-page"/> </fo:block> </fo:static-content> <fo:static-content flow-name="xsl-region-after"> <fo:block text-align="start" font-size="10pt" font-family="serif" line-height="1em + 2pt"> Page (<fo:page-number/>) </fo:block> </fo:static-content> <fo:flow flow-name="xsl-region-body"> <xsl:apply-templates select="term-entry"/> </fo:flow> </fo:page-sequence> </fo:root> </xsl:template> <xsl:template match="term-entry"> <fo:block text-align="start" font-size="12pt" font-family="sans-serif"> <xsl:apply-templates select="term"/> <xsl:apply-templates select="definition"/> </fo:block> </xsl:template> <xsl:template match="term"> <fo:block color="blue" space-before.optimum="3pt"> <fo:marker marker-class-name="term"><xsl:value-of select="."/> </fo:marker> <xsl:value-of select="."/> </fo:block> </xsl:template> <xsl:template match="definition"> <fo:block text-align="start" start-indent="2em"> <xsl:value-of select="."/> </fo:block> </xsl:template> </xsl:stylesheet>
Convertir documento XML a XSL-FO
El proyecto Apache FOP incluye las librerías: FOP, Logger, Graphics, Xerces, XML y XSLT.
De esta manera declaramos las librerías:
import org.apache.avalon.framework.logger.*; import org.apache.fop.apps.*; import org.xml.sax.InputSource; import org.xml.sax.*; import org.w3c.dom.*;
public void generarXSLFO(File archivoXML, File archivoXSLT)
{
try {
System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
System.setProperty("javax.xml.transform.TransformerFactory", "org.apache.xalan.processor.TransformerFactoryImpl");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document documento = builder.parse(archivoXML);
TransformerFactory tFactory = TransformerFactory.newInstance();
StreamSource stylesource = new StreamSource(archivoXSLT);
Transformer transformer = tFactory.newTransformer(stylesource);
DOMSource source = new DOMSource(documento);
StreamResult result = new StreamResult("glossary.fo");
transformer.transform(source, result);
} catch (TransformerConfigurationException e) {
System.out.println(e.getMessage());
} catch (TransformerException e) {
System.out.println(e.getMessage());
} catch (SAXException e) {
System.out.println(e.getMessage());
} catch (ParserConfigurationException e) {
System.out.println(e.getMessage());
} catch (IOException e) {
System.out.println(e.getMessage());
}
}Generar documento PDF
Una vez generados el archivo glossary.fo, podemos generar nuestro documento PDF.
public void generarPDF()
{
try {
Driver driver = new Driver();
Logger logger = new ConsoleLogger(ConsoleLogger.LEVEL_INFO);
driver.setLogger(logger);
org.apache.fop.messaging.MessageHandler.setScreenLogger(logger);
driver.setRenderer(Driver.RENDER_PDF);
InputStream input = new FileInputStream(new File("glossary.fo"));
driver.setInputSource(new InputSource(input));
OutputStream output = new FileOutputStream(new File("glossary.pdf"));
driver.setOutputStream(output);
driver.run();
output.flush();
output.close();
} catch (IOException e) {
System.out.println(e.getMessage());
} catch (org.apache.fop.apps.FOPException e) {
System.out.println(e.getMessage());
}
}Código Completo
Código del archivo ConvertirPDF.java
package org.luisalberto.xml2pdf;
import java.io.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import org.apache.avalon.framework.logger.*;
import org.apache.fop.apps.*;
import org.xml.sax.InputSource;
import org.xml.sax.*;
import org.w3c.dom.*;
public class ConvertirPDF
{
public void generarXSLFO(File archivoXML, File archivoXSLT)
{
try {
System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
System.setProperty("javax.xml.transform.TransformerFactory", "org.apache.xalan.processor.TransformerFactoryImpl");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document documento = builder.parse(archivoXML);
TransformerFactory tFactory = TransformerFactory.newInstance();
StreamSource stylesource = new StreamSource(archivoXSLT);
Transformer transformer = tFactory.newTransformer(stylesource);
DOMSource source = new DOMSource(documento);
StreamResult result = new StreamResult("glossary.fo");
transformer.transform(source, result);
} catch (TransformerConfigurationException e) {
System.out.println(e.getMessage());
} catch (TransformerException e) {
System.out.println(e.getMessage());
} catch (SAXException e) {
System.out.println(e.getMessage());
} catch (ParserConfigurationException e) {
System.out.println(e.getMessage());
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
public void generarPDF()
{
try {
Driver driver = new Driver();
Logger logger = new ConsoleLogger(ConsoleLogger.LEVEL_INFO);
driver.setLogger(logger);
org.apache.fop.messaging.MessageHandler.setScreenLogger(logger);
driver.setRenderer(Driver.RENDER_PDF);
InputStream input = new FileInputStream(new File("glossary.fo"));
driver.setInputSource(new InputSource(input));
OutputStream output = new FileOutputStream(new File("glossary.pdf"));
driver.setOutputStream(output);
driver.run();
output.flush();
output.close();
} catch (IOException e) {
} catch (org.apache.fop.apps.FOPException e) {
System.out.println(e.getMessage());
}
}
}Código del archivo Test.java
package org.luisalberto.xml2pdf;
import java.io.*;
import org.luisalberto.xml2pdf.ConvertirPDF;
public class Test
{
public static void main(String[] argv)
{
ConvertirPDF xmlPDF= new ConvertirPDF();
File archivoXML = new File("glossary.xml");
File archivoXSLT = new File("glossary.xslt");
xmlPDF.generarXSLFO(archivoXML, archivoXSLT);
xmlPDF.generarPDF();
}
}