Ok, so far I successfully implemented a PDF generation test web application (vaadin) after getting over many problems with Apache FOP 2.2. Record my progress here:
URL fo = getClass().getClassLoader().getResource("/static/topdf.fo");
String path = fo.getFile().substring(0, fo.getFile().lastIndexOf('/'));
......
base = new URI(fo.getProtocol() + "://" + fo.getHost() + path+"/"); // -- this works
// base = new URI(fo.getProtocol() + "://" + fo.getHost() + path); -- this does not work
......
fopFactory = FopFactory.newInstance(base, stream);
baseUri = base.toASCIIString();
........
HashMap model = new HashMap();
model.put("baseurl", baseUri);
model.put("basefonturl", baseUri+"fonts/");
model.put("basefontdir", base.getPath()+"/fonts/");
....
<fo:external-graphic src="url(vfs:///home/bing/opt/wildfly-10.1.0.Final/standalone/deployments/V8Experiment-1.0-SNAPSHOT.war/WEB-INF/classes/static/img/miso.png)"></fo:external-graphic>
<fo:external-graphic src="vfs:///home/bing/opt/wildfly-10.1.0.Final/standalone/deployments/V8Experiment-1.0-SNAPSHOT.war/WEB-INF/classes/static/img/miso.png"></fo:external-graphic>
<fo:external-graphic src="url(img/miso.png)"></fo:external-graphic>
<fo:external-graphic src="img/miso.png"></fo:external-graphic>
PipedOutputStream out = new PipedOutputStream();
InputStream in = new PipedInputStream(out);
new Thread(() -> {
try {
transformer.transform(src, result);
} catch (TransformerException e) {
e.printStackTrace();
}
;
// Clean-up, anyway
try {
out.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}).start();
return in;
Complete code: it's ugly but it's just a test
package com.bing.V8Experiment.views;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashMap;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;
import com.github.jknack.handlebars.Handlebars;
import com.github.jknack.handlebars.Template;
import com.vaadin.server.FileDownloader;
import com.vaadin.server.StreamResource;
import com.vaadin.ui.Button;
import com.vaadin.ui.Label;
import com.vaadin.ui.Notification;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.VerticalLayout;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.FopFactoryBuilder;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.DefaultConfiguration;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.MimeConstants;
import org.apache.fop.cli.CommandLineOptions;
import org.apache.fop.fonts.DefaultFontConfig;
import org.xml.sax.SAXException;
public class PDFDownload extends VerticalLayout {
public PDFDownload() throws SAXException, IOException {
// preparing the URL and base
URL fo = getClass().getClassLoader().getResource("/static/topdf.fo");
// My implementation of PDF generator
PDFStreamSource pdfsrc = new PDFStreamSource(fo);
StreamResource res = new StreamResource(pdfsrc, "Test.pdf");
// preparing the button
Button act = new Button("Generate some PDF");
FileDownloader fd = new FileDownloader(res);
fd.extend(act);
act.addClickListener(e -> {
PDFDownload.this.addComponent(new Label("We are generating the PDF for you, download should start in a while"));
});
TextArea foCfg = new TextArea("FO Configuration");
foCfg.setHeight("400px");
foCfg.setWidth("100%");
foCfg.setValue(pdfsrc.foCfgStr);
this.addComponents(
new Label("FO URL:" + fo.toExternalForm()),
new Label("Base URL: "+pdfsrc.baseUri),
foCfg,
act);
}
}
class PDFStreamSource implements StreamResource.StreamSource {
private URL fo;
private URI base;
FopFactoryBuilder builder;
FopFactory fopFactory;
String baseUri;
String foCfgStr;
public PDFStreamSource(URL fo) {
this.fo = fo;
String path = fo.getFile().substring(0, fo.getFile().lastIndexOf('/'));
try {
base = new URI(fo.getProtocol() + "://" + fo.getHost() + path+"/"); // -- this works
//
base = new URI(fo.getProtocol() + "://" + fo.getHost() + path); -- this does not work
} catch (URISyntaxException e1) {
e1.printStackTrace();
return;
}
baseUri = base.toASCIIString(); // for showing the uri on interface
// Step 0: construct a configuration file with Handlebar
Handlebars hbr = new Handlebars();
Template foCfg;
HashMap model = new HashMap();
model.put("baseurl", baseUri);
model.put("basefonturl", baseUri+"fonts/");
model.put("basefontdir", base.getPath()+"fonts/");
//
DefaultFontConfig df;
try {
foCfg = hbr.compile("static/fop");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
foCfgStr = "Failed to generate foCfgStr";
return;
}
try {
foCfgStr = foCfg.apply(model);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Step 1: Construct a FopFactory by specifying a reference to
// the configuration file
// (reuse if you plan to render multiple documents!)
InputStream stream = new ByteArrayInputStream(foCfgStr.getBytes());
try {
fopFactory = FopFactory.newInstance(base, stream);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
};
@Override
public InputStream getStream() {
try {
// Step 2: Set up output stream.
PipedOutputStream out = new PipedOutputStream();
InputStream in = new PipedInputStream(out);
// Step 3: Construct fop with desired output format
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out);
System.err.println("User agent URI:"+fop.getUserAgent().getResourceResolver().getBaseURI().toASCIIString());
System.err.println("Factory font manager resource resolver baseUri:"+fopFactory.getFontManager().getResourceResolver().getBaseURI().toASCIIString());
// Step 4: Setup JAXP using identity transformer
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(); // identity
// transformer
// Step 5: Setup input and output for XSLT transformation
// Setup input stream
Source src = new StreamSource(fo.openStream());
// Resulting SAX events (the generated FO) must be piped
// through to FOP
Result result = new SAXResult(fop.getDefaultHandler());
// Step 6: Start XSLT transformation and FOP processing
new Thread(() -> {
try {
transformer.transform(src, result);
} catch (TransformerException e) {
e.printStackTrace();
}
;
// Clean-up, anyway
try {
out.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}).start();
return in;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
Finally generated FOP configuration:
<?xml version="1.0"?>
<fop version="1.0">
<!-- Strict user configuration -->
<strict-configuration>true</strict-configuration>
<!-- Strict FO validation -->
<strict-validation>false</strict-validation>
<!-- Base URL for resolving relative URLs -->
<base>vfs:///home/bing/opt/wildfly-10.1.0.Final/standalone/deployments/V8Experiment-1.0-SNAPSHOT.war/WEB-INF/classes/static/</base>
<!-- Font Base URL for resolving relative font URLs -->
<font-base>vfs:///home/bing/opt/wildfly-10.1.0.Final/standalone/deployments/V8Experiment-1.0-SNAPSHOT.war/WEB-INF/classes/static/fonts/</font-base>
<!-- Source resolution in dpi (dots/pixels per inch) for determining the size of pixels in SVG and bitmap images, default: 72dpi -->
<source-resolution>72</source-resolution>
<!-- Target resolution in dpi (dots/pixels per inch) for specifying the target resolution for generated bitmaps, default: 72dpi -->
<target-resolution>72</target-resolution>
<!-- default page-height and page-width, in case
value is specified as auto -->
<default-page-settings height="11in" width="8.26in"/>
<!-- Use file name nl_Bel instead of the default nl_BE -->
<hyphenation-pattern lang="nl" country="BE">nl_Bel</hyphenation-pattern>
<!-- or for xml file -->
<hyphenation-pattern lang="fr" extension="xml">lang/fr.xml</hyphenation-pattern>
<!-- or for binary file -->
<hyphenation-pattern lang="fr" extension="hyp">lang/fr.hyp</hyphenation-pattern>
<renderers>
<renderer mime="application/pdf">
<fonts>
<!-- register all the fonts found in a directory -->
<directory>/home/bing/opt/wildfly-10.1.0.Final/standalone/deployments/V8Experiment-1.0-SNAPSHOT.war/WEB-INF/classes/static/fonts/</directory>
<!-- automatically detect operating system installed fonts -->
<!-- auto-detect/ -->
</fonts>
</renderer>
</renderers>
</fop>