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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
|
<?xml version="1.0" standalone="no"?>
<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.1//EN"
"http://cvs.apache.org/viewcvs.cgi/*checkout*/xml-forrest/src/resources/schema/dtd/document-v11.dtd">
<!-- Embedding FOP -->
<document>
<header>
<title>Embedding FOP</title>
<subtitle>How to Embed FOP in a Java application</subtitle>
</header>
<body>
<section id="overview">
<title>Overview</title>
<p>Review <link href="running.html">Running FOP</link> for important information that applies to embedded applications as well as command-line use, such as options and performance.
</p>
<p>To embed FOP in your application, instantiate org.apache.fop.apps.Driver.
Once this class is
instantiated, methods are called to set the
Renderer to use
and the OutputStream to use to output the results of the
rendering (where applicable). In the case of the Renderer and
ElementMapping(s), the Driver may be supplied either with the
object itself, or the name of the class, in which case Driver will
instantiate the class itself. The advantage of the latter is it
enables runtime determination of Renderer and ElementMapping(s).
</p>
</section>
<section id="basics">
<title>Basics</title>
<p>The simplest way to use Driver is to instantiate it with the
InputSource and OutputStream, then set the renderer desired and
call the run method.
</p>
<p>Here is an example use of Driver which outputs PDF:
</p>
<source><![CDATA[
import org.apache.fop.apps.Driver;
/*..*/
Driver driver = new Driver(new InputSource(args[0]),
new FileOutputStream(args[1]));
driver.setRenderer(Driver.RENDER_PDF);
driver.run();]]></source>
<p>
In the example above, args[0] contains the path to an XSL-FO file, while
args[1] contains a path for the target PDF file.
</p>
<p>You also need to set up logging. Global logging for all FOP
processes is managed by MessageHandler. Per-instance logging
is handled by Driver. You want to set both using an implementation
of org.apache.avalon.framework.logger.Logger. See
<jump href="#logging">below</jump> for more information.
</p>
<source><![CDATA[
import org.apache.avalon.framework.logger.Logger;
import org.apache.avalon.framework.logger.ConsoleLogger;
/*..*/
Logger logger = new ConsoleLogger(ConsoleLogger.LEVEL_INFO);
MessageHandler.setScreenLogger(logger);
driver.setLogger(logger);]]></source>
<p>To setup the user config file you can do the following
</p>
<source><![CDATA[
import org.apache.fop.apps.Options;
/*..*/
userConfigFile = new File(userConfig);
options = new Options(userConfigFile);]]></source>
<note>
This is all you need to do, it sets up a static configuration class.
</note>
<p>Once the Driver is set up, the render method
is called. Depending on whether DOM or SAX is being used, the
invocation of the method is either <code>render(Document)</code> or
<code>render(Parser, InputSource)</code> respectively.
</p>
<p>
<strong>Another possibility may be used to build the FO Tree. You can
call <code>getContentHandler()</code> and fire the SAX events yourself.
</strong>
</p>
<p>Once the FO Tree is built, the format() and render() methods may be
called in that order.
</p>
<p>Here is an example use of Driver:</p>
<source><![CDATA[
Driver driver = new Driver();
driver.setRenderer(Driver.RENDER_PDF);
driver.setInputSource(new FileInputSource(args[0]));
driver.setOutputStream(new FileOutputStream(args[1]));
driver.run();]]></source>
<p>You can also specify an xml and xsl file for the input.
</p>
<p>Here is an example use of Driver with the XSLTInputHandler:</p>
<source><![CDATA[
Driver driver = new Driver();
driver.setRenderer(Driver.RENDER_PDF);
InputHandler inputHandler = new XSLTInputHandler(xmlFile, xslFile);
XMLReader parser = inputHandler.getParser();
driver.setOutputStream(new FileOutputStream(outFile));
driver.render(parser, inputHandler.getInputSource());]]></source>
<p>Have a look at the classes CommandLineStarter or FopServlet for complete
examples. Also, have a look at the examples at the bottom of this page.
</p>
<note>If your XSL-FO files contain SVG then Batik will be used. When Batik is
initialised it uses certain classes in <code>java.awt</code> that
intialises the java AWT classes. This means that a daemon thread
is created by the JVM and on Unix it will need to connect to a
DISPLAY.
The thread means that the Java application will not automatically quit
when finished, you will need to call <code>System.exit()</code>. These
issues should be fixed in the upcoming JDK 1.4</note>
</section>
<section id="logging">
<title>Controlling logging</title>
<p>FOP uses Jakarta Avalon's
<fork href="http://jakarta.apache.org/avalon/api/org/apache/avalon/framework/logger/Logger.html">Logger</fork>
interface to do logging. See the <fork href="http://jakarta.apache.org/avalon/">Jakarta Avalon project</fork> for more information.</p>
<p>Per default FOP uses the ConsoleLogger which logs to System.out. If you want to do logging using a
logging framework (such as LogKit, Log4J or JDK 1.4 Logging) you can set a
different Logger implementation on the Driver object. Here's an example how you would use LogKit:</p>
<source><![CDATA[
Hierarchy hierarchy = Hierarchy.getDefaultHierarchy();
PatternFormatter formatter = new PatternFormatter(
"[%{priority}]: %{message}\n%{throwable}" );
LogTarget target = null;
target = new StreamTarget(System.out, formatter);
hierarchy.setDefaultLogTarget(target);
log = hierarchy.getLoggerFor("fop");
log.setPriority(Priority.INFO);
driver.setLogger(new org.apache.avalon.framework.logger.LogKitLogger(log));]]></source>
<p>The LogKitLogger class implements the Logger interface so all logging calls are being redirected to LogKit.
More information on Jakarta LogKit can be found <fork href="http://jakarta.apache.org/avalon/logkit/index.html">here</fork>.</p>
<p>Similar implementations exist for Log4J (org.apache.avalon.framework.logger.Log4JLogger) and
JDK 1.4 logging (org.apache.avalon.framework.logger.Jdk14Logger).</p>
<p>If you want FOP to be totally silent you can also set an org.apache.avalon.framework.logger.NullLogger instance.</p>
<p>If you want to use yet another logging facility you simply have to create a class that implements org.apache.avalon.framework.logging.Logger
and set it on the Driver object. See the existing implementations in Avalon Framework for examples.</p>
</section>
<section id="hints">
<title>Hints</title>
<section id="input">
<title>XML/XSL/DOM Inputs</title>
<p>
You can supply your input to FOP from a variety of data sources.
For example, you may have a DOM and an XSL stylesheet; or you may want to
set variables in the stylesheet.
<fork href="http://xml.apache.org/xalan-j/usagepatterns.html">
Xalan Basic Usage Patterns</fork> provides some interface documentation and cookbook solutions for such situations.
</p>
<p>
You can use the content handler from the driver to create a SAXResult.
The transformer then can fire SAX events on the content handler which
will in turn create the rendered output.
</p>
<p>
Examples showing this can be found at the bott
</p>
</section>
<section id="object-reuse">
<title>Object reuse</title>
<p>
If FOP is going to be used multiple times within your application
it may be useful to reuse certain objects to save time.
</p>
<p>
The renderers and the driver can both be reused. A renderer is reusable
once the previous render has been completed. The driver is reuseable
after the rendering is complete and the reset method is called.
You will need to setup the driver again with a new OutputStream,
IntputStream and renderer.
</p>
</section>
<section id="render-info">
<title>Getting information on the rendering process</title>
<p>
To get the number of pages that were rendered by FOP you can call
<code>Driver.getResults()</code>. This returns a FormattingResults object
where you can lookup the number of pages produced. It also gives you the
page-sequences that were produced along with their id attribute and their
number of pages. This is particularly useful if you render multiple
documents (each enclosed by a page-sequence) and have to know the number of
pages of each document.
</p>
</section>
</section>
<section id="servlet">
<title>Using FOP in a Servlet</title>
<p>
Here is a minimal code snippet to demonstrate the basics:
</p>
<source>response.setContentType("application/pdf");
Driver driver=new Driver( new InputSource("foo.fo"),
response.getOutputStream());
driver.setRenderer(Driver.RENDER_PDF);
driver.run();</source>
<p>
There are numerous problems with the code snippet above.
Its purpose is only to demonstrate the basic concepts.
See xml-fop/examples/servlet for a working example of FOP used in a servlet.
After building the servlet, drop the fop.war into the webapps directory of Tomcat.
Then access a URL as follows:
</p>
<p>http://localhost:8080/fop/fop?fo=/home/path/to/fofile.fo</p>
<p>http://localhost:8080/fop/fop?xml=/home/path/to/xmlfile.xml&xsl=/home/path/to/xslfile.xsl</p>
<p>The source code for the servlet can be found under xml-fop/examples/servlet/src/FopServlet.java.</p>
<note>
Some versions of Internet Explorer will not automatically show the PDF.
This is well-known to be a limitation of Internet Explorer, and is not a problem with the servlet.
However, Internet Explorer can still be used to download the PDF so that it can be viewed later. Also, appending ".pdf" to the end of the URL may help.
</note>
</section>
<section id="servlet-transform">
<title>Using FOP in a Servlet with an XSLT Transformation</title>
<p>
If both the source XML and XSL are read from files, use the TraxInputHandler:
</p>
<source>response.setContentType("application/pdf");
XSLTInputHandler input
=new XSLTInputHandler(new File("foo.xml"), new File("foo.xsl"));
Driver driver=new Driver();
driver.setOutputStream(response.getOutputStream());
driver.setRenderer(Driver.RENDER_PDF);
driver.render(input.getParser(), input.getInputSource());</source>
<p>
This code snippet has the same problems as the one from the <link href="#servlet">section above</link>.
</p>
<p>
If your source XML is generated on the fly (for example from a database, a web service, or another servlet), create a transformer object explicitly, and use a SAX event stream to feed the transformation result into FOP:
</p>
<source>response.setContentType("application/pdf");
Driver driver =new Driver();
driver.setOutputStream(response.getOutputStream());
driver.setRenderer(Driver.RENDER_PDF);
Transformer transformer=TransformerFactory.newInstance()
.newTransformer(new StreamSource("foo.xsl"));
transformer.transform(xmlsource, new SAXResult(driver.getContentHandler()));</source>
<p>
You don't have to call run() or render() on the driver object.
</p>
<p>
The <code>xmlsource</code> is a placeholder for your actual XML source.
If you have to read the XML from a string, supply a <code>new StreamSource(new StringReader(xmlstring))</code>.
Constructing and reparsing an XML string is generally less desirable than using a SAXSource if you generate your XML.
You can alternatively supply a DOMSource as well.
You may also use dynamically generated XSL if you like.
</p>
<p>
Because you have an explicit transformer object, you can also use it to explicitly set parameters for the transformation run.
</p>
</section>
<section id="config-external">
<title>Using a Configuration File</title>
<p>
To access an external configuration:
</p>
<source>org.apache.fop.apps.Options options = new Options(new File("userconfig.xml"));</source>
<p>
No further reference to the <code>options</code> variable is necessary.
</p>
<p>
See <link href="#multithreading">Multithreading FOP</link> for issues related to changing configuration in a multithreaded environment.
</p>
</section>
<section id="config-internal">
<title>Setting the Configuration Programmatically</title>
<p>
If you wish to set configuration options from within your embedded application, use the <code>Configuration.put</code> method. Here is an example that sets the "baseDir" configuration in a Unix environment:
</p>
<source>org.apache.fop.configuration.Configuration.put("baseDir","/my/base/dir");</source>
<p>
Here is another that sets baseDir in a Windows environment:
</p>
<source>org.apache.fop.configuration.Configuration.put("baseDir","C:\my\base\dir");</source>
<p>
See <link href="#multithreading">Multithreading FOP</link> for issues related to changing configuration in a multithreaded environment.
</p>
</section>
<section id="multithreading">
<title>Multithreading FOP</title>
<p>
FOP is not currently completely thread safe.
Although the relevant methods of the Driver object are synchronized, FOP uses static
variables for configuration data and loading images.
Here are some tips to mitigate these problems:
</p>
<ul>
<li>To avoid having your threads blocked, create a Driver object for each thread.</li>
<li>If possible, do not change the configuration data while there is a Driver object rendering.
Setup the configuration only once, preferably in the <code>init()</code> method of the servlet.
</li>
<li>If you must change the configuration data more often, or if you have multiple servlets within the same webapp using FOP, consider implementing a singleton class to encapsulate the configuration settings and to run FOP in synchronized methods.
</li>
</ul>
</section>
<section id="servlet-engine">
<title>Servlet Engines</title>
<p>
When using a servlet engine, there are potential CLASSPATH issues, and potential conflicts with existing XML/XSLT libraries.
Servlet containers also often use their own classloaders for loading webapps, which can cause bugs and security problems.
</p>
<section id="tomcat">
<title>Tomcat</title>
<p>
Check Tomcat's documentation for detailed instructions about installing FOP and Cocoon.
There are known bugs that must be addressed, particularly for Tomcat 4.0.3.
</p>
</section>
<section id="websphere">
<title>WebSphere 3.5</title>
<p>
Put a copy of a working parser in some directory where WebSphere can access it.
For example, if /usr/webapps/yourapp/servlets is the CLASSPATH for your servlets, copy the Xerces jar into it (any other directory would also be fine).
Do not add the jar to the servlet CLASSPATH, but add it to the CLASSPATH of the application server which contains your web application.
In the WebSphere administration console, click on the "environment" button in the "general" tab.
In the "variable name" box, enter "CLASSPATH".
In the "value" box, enter the correct path to the parser jar file (/usr/webapps/yourapp/servlets/Xerces.jar in our example here).
Press "OK", then apply the change and restart the application server.
</p>
</section>
</section>
<section id="examples">
<title>Examples</title>
<p>
The directory "xml-fop/examples/embedding" contains several working examples.
In contrast of the examples above the examples here primarily use JAXP for
XML access. This may be easier to understand for people familiar with JAXP.
</p>
<section id="ExampleFO2PDF">
<title>ExampleFO2PDF.java</title>
<p>
This example demonstrates the basic usage pattern to transform an XSL-FO
file to PDF using FOP.
</p>
<figure src="images/EmbeddingExampleFO2PDF.png" alt="Example XSL-FO to PDF"/>
</section>
<section id="ExampleXML2FO">
<title>ExampleXML2FO.java</title>
<p>
This example has nothing to do with FOP. It is there to show you how an XML
file can be converted to XSL-FO using XSLT. The JAXP API is used to do the
transformation. Make sure you've got a JAXP-compliant XSLT processor in your
classpath (ex. <fork href="http://xml.apache.org/xalan-j">Xalan</fork>).
</p>
<figure src="images/EmbeddingExampleXML2FO.png" alt="Example XML to XSL-FO"/>
</section>
<section id="ExampleXML2PDF">
<title>ExampleXML2PDF.java</title>
<p>
This example demonstrates how you can convert an arbitrary XML file to PDF
using XSLT and XSL-FO/FOP. It is a combination of the first two examples
above. The example uses JAXP to transform the XML file to XSL-FO and FOP to
transform the XSL-FO to PDF.
</p>
<figure src="images/EmbeddingExampleXML2PDF.png" alt="Example XML to PDF (via XSL-FO)"/>
<p>
The output (XSL-FO) from the XSL transformation is piped through to FOP using
SAX events. This is the most efficient way to do this because the
intermediate result doesn't have to be saved somewhere. Often, novice users
save the intermediate result in a file, a byte array or a DOM tree. We
strongly discourage you to do this if it isn't absolutely necessary. The
performance is significantly higher with SAX.
</p>
</section>
<section id="ExampleObj2XML">
<title>ExampleObj2XML.java</title>
<p>
This example is a preparatory example for the next one. It's an example that
shows how an arbitrary Java object can be converted to XML. It's an often
needed task to do this. Often people create a DOM tree from a Java object and
use that. This is pretty straightforward. The example here however shows how
to do this using SAX which will probably be faster and not even more
complicated once you know how this works.
</p>
<figure src="images/EmbeddingExampleObj2XML.png" alt="Example Java object to XML"/>
<p>
For this example we've created two classes: ProjectTeam and ProjectMember
(found in xml-fop/examples/embedding/java/embedding/model). They represent
the same data structure found in
xml-fop/examples/embedding/xml/xml/projectteam.xml. We want to serialize a
project team with several members which exist as Java objects to XML.
Therefore we created the two classes: ProjectTeamInputSource and
ProjectTeamXMLReader (in the same place as ProjectTeam above).
</p>
<p>
The XMLReader implementation (regard it as a special kind of XML parser)is
responsible for creating SAX events from the Java object. The InputSource
class is only used to hold the ProjectTeam object to be used.
</p>
<p>
Have a look at the source of ExampleObj2XML.java to find out how this is
used. For more detailed information see other resources on JAXP (ex.
<fork href="http://java.sun.com/xml/jaxp/dist/1.1/docs/tutorial/xslt/3_generate.html">An older JAXP tutorial</fork>).
</p>
</section>
<section id="ExampleObj2PDF">
<title>ExampleObj2PDF.java</title>
<p>
The last example here combines the previous and the third to demonstrate
how you can transform a Java object to a PDF directly in one smooth run
by generating SAX events from the Java object that get fed to an XSL
transformation. The result of the transformation is then converted to PDF
using FOP as before.
</p>
<figure src="images/EmbeddingExampleObj2PDF.png" alt="Example Java object to PDF (via XML and XSL-FO)"/>
</section>
<section id="example-notes">
<title>Final notes</title>
<p>
These examples should give you an idea of what's possible. It should be easy
to adjust these examples to your needs. For examples, you can use a DOMSource
instead of a StreamSource to feed a DOM tree as input for an XSL
transformation.
</p>
<p>
If you think you have a decent example that should be here, contact us via
one of the mailing lists and we'll see to it that it gets added. Also, if
you can't find the solution to your particular problem drop us a message on
the fop-user mailing list.
</p>
</section>
</section>
</body>
</document>
|