page 183
When we invoke the PDF-generating templates with the
mode=generate-pdf
attribute, we pass in the
page-size
parameter to set the dimensions of the printed page. We generate PDFs with both letter-sized and A4-sized pages to support our customers around the world.
To create the PDF, we first create the output file of formatting objects, converting the various XML tags from our source document into the various formatting objects we need:
fo:block font-size=16pt line-height=19pt font-weight=bold space-after.optimum=12pt
Introduction to JavaServer Pages fo:block
fo:block space-after.optimum=6pt In todays environment, most web sites want to display dynamic content based on
the user and the session. Most content, such as images, text, and banner ads, is most
easily built with HTML editors. So we need to mix the static content of HTML files with directives for accessing or generating dynamic content.
fo:block fo:block space-after.optimum=6pt
JavaServer Pages meet this need. They provide server-side scripting support for generating web pages with combined
static and dynamic content. fo:block
Currently, the XSL:FO specification is a candidate recommendation at the World Wide Web Consortium W3C. Because future changes are likely, we wont discuss the formatting
objects themselves. It suffices to say that our stylesheet defines page layouts margins, running headers and footers, etc. and then creates a number of formatting objects inside
those page layouts. The FOP tool handles the details of calculating line, page, and column breaks, page references, and hyperlinks.
Once the file of formatting objects is created, we call an extension function to convert the formatting objects file into a PDF. Heres the exensions main code:
public static void buildPDFFileString foFilename, String pdfFilename {
try {
XMLReader parser = XMLReader Class.forNameorg.apache.xerces.parsers.SAXParser
.newInstance; Driver driver = new Driver;
driver.setRendererorg.apache.fop.render.pdf.PDFRenderer, Version.getVersion;
driver.addElementMappingorg.apache.fop.fo.StandardElementMapping; driver.addElementMappingorg.apache.fop.svg.SVGElementMapping;
driver.addPropertyListorg.apache.fop.fo.StandardPropertyListMapping; driver.addPropertyListorg.apache.fop.svg.SVGPropertyListMapping;
driver.setOutputStreamnew FileOutputStreampdfFilename; driver.buildFOTreeparser, new InputSourcefoFilename;
driver.format; driver.render;
}
The code merely creates the FOP
Driver
object, sets its various properties, and then tells it to render the formatting objects in a PDF file. The main difficulty here is in determining how
the various XML elements should be converted to formatting objects; once the conversion is done, we have a tool that generates high-quality printable output from our XML source files.
Best of all, this code uses open source tools exclusively.
page 184
9.5.7 Generating the JPEG Files
Another thing we need to produce for the tutorial is a series of JPEG files. To have precise control over the appearance of the titles in the tutorial, we create a JPEG file in which the title
text is written in a particular font. We discussed this code in Chapter 8
, so we wont go over it here. Heres the first significant section of the build-graphics.xsl file:
xsl:template match=tutorial mode=generate-graphics xsl:choose
xsl:when test=function-availablejpeg:buildJPEGFile xsl:value-of
select=jpeg:buildJPEGFiletitle, concatmaster, fileSep, masthead.jpg,
concatcurDir, fileSep, imagemaster, fileSep, masthead.jpg, baseFont, 27, 5, 30, 0, 0, 0
xsl:when xsl:otherwise
xsl:message terminate=yes Error JPEG library not available
xsl:message xsl:otherwise
xsl:choose
The
buildJPEGFile
function takes several parameters, including the title text in the example, our XPath expression passes in the value of the
title
element, the name of the background JPEG file we load this file, draw the text on top of it, and then save the new JPEG, the name
of the new JPEG file, the name of the font, and other details about the font size, the x- and y- coordinates where the text should start, and the color in which to draw it.
Although neither this extension nor the stylesheet that calls it are rocket science, they save us a tremendous amount of time in the tutorial development process. Before we had the Toot-O-
Matic, we had to ask our highly trained, highly talented, and highly overworked graphics staff to create these graphics for us; now we do it automatically and the graphics staff can
focus their talents on more important things.
9.5.8 Generating the Zip File
Our last task is to generate a zip file that contains all the files needed to view the tutorial locally. This includes all HTML files, all standard graphics, all JPEGs we generate, and any
graphics referenced in the XML source anything in an
img
tag. We call another Java extension to build the zip file. Determining which files should be loaded into the zip file
relies heavily on our naming conventions. When we invoke the
buildZipFile
function, we pass in several arguments. The first three are the root filename, the directory to which we write the output files, and the file separator for
this platform. The next argument is the
tutorial
node itself; the extension uses DOM functions to determine what files should be added to the zip file. The final argument is a
node-set
of all the things that reference graphics files in the XML source. That includes the
img
attribute of any tag and the
src
attribute of the
img
element. Heres what the function call looks like:
xsl:template match=tutorial mode=generate-zip-file xsl:choose
xsl:when test=function-availablezip:buildZipFile xsl:variable name=referencedGraphics
select=.img|image-columnimg|imgsrc xsl:value-of
select=zip:buildZipFilefn, curDir, fileSep, ., referencedGraphics
xsl:when xsl:otherwise
page 185 xsl:message terminate=yes
Error Zip file library not available xsl:message
xsl:otherwise xsl:choose
xsl:template
In the extension function code itself, we start by creating the
ZipOutputStream
itself:
ZipOutputStream zipOut = new ZipOutputStreamnew FileOutputStreamcurrentDirectory +
fileSeparator + baseFilename + .zip;
Once weve created our
ZipOutputStream
, well see if theres a comment for the zip file in the
zip-file-comment
attribute of the
tutorial
element:
Node currentNode = tutorialElement.nextNode; while currentNode = null
{ if currentNode.getLocalName.equalstutorial
{ ElementImpl currentElement = ElementImplcurrentNode;
String zipFileComment = currentElement.getAttributezip-file-comment; if zipFileComment = null
zipOut.setCommentzipFileComment; else
{ zipFileComment = currentElement.getAttributealt;
if zipFileComment = null zipOut.setCommentzipFileComment;
}
With everything we do with the DOM nodes, well need to make sure we actually work with the appropriate nodes; thats why we use the function call
getLocalName.equalstutorial
. Once weve found the
tutorial
element, we can work with its children to figure out the names of all the HTML and JPEG files we need to
add to the zip file. If the
tutorial
element has five
section
children, and the first
section
contains eleven
panel
s, then well need to write the files tootomatic-1-1.html through tootomatic-1-11.html to the zip file. This assumes that the base filename we use is
tootomatic . Heres an excerpt from the code:
int numKids = currentElement.getChildCount; int numSections = 0;
for int i = 0; i numKids; i++ {
Node currentChild = currentElement.getChildi; if currentChild.getLocalName.equalssection
{ ElementImpl currentChildElement = ElementImplcurrentChild
fileToZip = new FilecurrentDirectory + fileSeparator + index + ++numSections + .html;
fis = new FileInputStreamfileToZip; entry = new ZipEntrycurrentDirectory + fileSeparator +
fileToZip.getName; if zipOut = null
{ zipOut.putNextEntryentry;
whilebytes_read = fis.readbuffer = -1 zipOut.writebuffer, 0, bytes_read;
} fis.close;
int numGrandkids = currentChildElement.getChildCount; int numPanels = 0;
for int j = 0; j numGrandkids; j++ {