Photo Gallery, XSLT, and Looping
I decided to make my photo gallery export to XML so I could perform XSLT transforms on the output. My first project was getting the PHP gallery that I wrote to output XML. This proved to be easy. The next step was to write the XSL to convert to FO that would produce a PDF document. This step took a long time, mainly because I’ve yet to deal with loops in XSL.
What I learned early on was that the basic loops available in most modern programming languages did not exist in XSL. The reasons for that I’m not quite sure but I’m sure it was a design goal not an accident. Although they do support the ‘for each’ loop which is very helpful in XML parsing. I even found a person who wrote a “loop compiler” that is an XSLT to allow your XSL to do for and while loops. The way to achieve loops in XSL is through the use of recursively calling templates. If you go back to CS 101 you will remember that any loop written in a traditional ‘for’ or ‘while’ syntax can be rewritten using recursion. So after getting my mind around this I set out to write my XSLT.
What I had was a list of media objects represented in the XML, then the media node had children representing the name of the media file and the location of the media’s thumbnail image. I wanted the XSLT to loop over all the media nodes and draw a fo:table-cell for each one. I then wanted it to display some number of columns and then close the previous row and start a new row. Traditionally written:
$i = 0;
forEach($array) {
if( ($i mod $numberOfColumns ) == 0) {
if($i != 0) { ...close row... }
...open row... }
. . . build cell . . .
if( $i == count($array)) {
...close row... }
$i++; }
You have two problems making this work in XSLT one is that variables in XSLT are static so you can not change them inside the loop (i++). The other problem is that to open and close rows in FO you have to use < and > which cause problems in the parsing of the XSL. So I set out to write this loop using recursion, after getting the basic XSL template calling syntax figured out I was on my way. The basic design looks like this:
$totalNumberOfElements = count($array);
function buildRows( $position, $numberOfColumns ) {
...open row...
buildCells($i, $numberOfColumns );
...close row...
if(($i+$numberOfColumns ) < $totalNumberOfElements) {
buildRows( $i + $numberOfColumns , $numberOfColumns )
}
}
function buildCells($i, $numberOfColumns) {
... build cell $i ...
if($numberOfColumns >= 0) {
buildCells($i+1, $numberOfColumns-1);
}
}
The two goofy items in the XSL code are that element indexing starts at 1 hence the $i+1. Also you will see a $i+0 in a few places this was done because I believe $i was passed in as a Char[] instead of a Number datatype the ‘+0′ makes is a number. There is probably a more elegant way of converting datatypes but I’m not an expert.
If you want to look at the files here they are:
XSL File
XML File
PDF Output
I will eventually have this pdf transforms working within the photo gallery but I need to get Cocoon up and running first.
March 9th, 2003 at 10:43 am
So, is cocoon doing the transforms on your site now?