Some popular, rules-heavy roleplaying games can benefit from reference cards, where all the information about a specific item is printed on a small card, allowing each player to have a stack of cards specifically for the rules she cares about. This post deals specifically with creating such cards for spells, one per card. While you can spend six bucks on a spell card PDF to print out at your own expense and cut with a paper cutter, why do this you can spend ten bucks to buy Avery® business cards and build the output yourself?
This post acts as a how-to describing how you can build such cards yourself, should the muse strike you. More generally, it demonstrates how you can use two different but complementary XML-based technologies (XSLT and FOP) to format output for Avery business cards, mailing labels, etc. from an XML data source. An assumption is made that you know what XML is and have enough technical chops to install and setup the various technologies involved.
Formatting Objects Processor
The Formatting Objects Processor (FOP) is a free, cross-platform technology from Apache. It claims to be “driven by XSL formatting objects (XSL-FO)”. All this really means is that it makes use of a specific XML format called XSL-FO, which is built to represent printed page layout. FOP can read in this format and render it into a wide variety of output, including text, PostScript and the format we’ll be using here, PDF. The XSL-FO standard has enough in common with HTML that its basics are fairly simple to pickup if you know that language; however, it is more complicated and much more accurate, capable of millimeter-level control over the output.
In principle, all you need to to use FOP is a source file written in XSL-FO markup. Nearly every demo you’ll find on the web uses the same mechanism of generating this source that we will (XSLT, see below); however, FOP really doesn’t care where it comes from. Keep in mind that it’s just an XML file, so you could just as easily, say, build the XSL-FO with a Perl script or even by hand. Typically, you will also have some source of data in hand, such as a database or text file. In our case, we have some XML data in a totally different format.
The d20 Standard Reference Document in XML
The d20 game uses a (mostly) open standard, based around the d20 Standard Reference Document. Fortunately for us, an enterprising soul named Andargor has gone through the trouble of translating this SRD into XML and some other data-based formats. His XML files use a fairly straight-forward format of his invention. This format, of course, is pure data and nothing like the XSL-FO layout format. Nor should it be. So, what we need is something to translate the d20 XML into the XSL-FO markup describing the page layout we want.
Extensible Stylesheet Language Transformations
Many paths exist to convert the d20 XML into XSL-FO. Python scripts, Java, heck even sed. We’re using a technology meant to transform XML input into some other kind of output (usually XML, HTML or text) called the Extensible Stylesheet Language (XSL). XSL is yet another special XML format geared towards matching XML data and transforming it. (It is, believe it or not, turing complete.) It makes heavy use of a standard called XPath, which is a funky query language for specifying exact subsets of nodes within an XML document. Though XPath looks nothing like SQL, it is similar in purpose, except querying a hierarchy tree instead of relational tables.
An XSL transformation (XSLT) is specified in an
file. Typically, such files are very specific, written exactly for the
combination of data type being transformed and output format being
written. That is, the XSL file we use here is specifically for turning
the d20 SRD XML format into XSL-FO formatted for Avery business cards.
If you need to change either end of this, you should make a copy of the
file and mutate it into a new transformation that does what you
We need some kind of tool that allows a two-step workflow (or knows how to combine the two steps into one). First, we convert the d20 SRD XML data into XSL-FO using XSLT. (That’s a lot of acronyms.) Second, we take the result of that and feed it into FOP to build our PDF. The four files are involved are:
spells.xml- The d20 XML data containing all the spells in the SRD.
spellcards.xsl- The XSLT to do the translation into XSL-FO.
temp.fo- The XSL-FO generated by the transformation.
spellcards.pdf- The final PDF file.
A number of tools exist to do this. I happen to use oXygen, as it combines everything needed to run this process into a single step, and also functions as a very good XML and XSLT editor. It’s a powerful tool, but pricey. Stylus Studio costs even more. Some less feature-rich alternatives might be TestXSLT. If you want to go the Java route, your best bet is to download the recent FOP package, which contains the Xalan XSLT engine and a command line to use it. If you know of other tools, leave a comment with a URL.
Once you have the tools picked out, download the XML d20 SRD and find the
spells.xml file. Now, take a look at the transformation file. It is fairly well commented.
Save a copy of it to your local drive as your
spellcards.xsl file. Now tell your tools to use
spellcards.xsl to transform
temp.fo, then channel it into FOP.
If using oXygen, do the following (note, there may be some changes between versions of oXygen, so this may not be exact):
- Open the two files.
spellcards.xslthe active document and select the menu “Document → XML Document → Configure Transformation Scenario”.
- In the dialog that comes up, click “New”.
- Name the scenario “spellcards” or something similar
- In the dialog’s “XSLT” tab, set the “XML URL” to your local copy of
spells.xml. You should be able to leave the other settings alone on this tab.
- In the dialog’s “FO Processor” tab, check “Perform FO Processing” and make sure the “XSLT result as input” is selected. You want to use the “pdf” method and the built-in FOP.
- In the dialog’s “Output” tab, click the “Save As” radio button and
provide a place to save the
spellcards.pdffile. Click the “Open in browser” checkbox and the “Saved file” radio button.
- Click OK to close the dialog.
- Back in the “Configure Transformation Scenario” dialog, you can click “Transform Now” to test out your settings.
- Hopefully, you should be looking at a PDF now. If you need to run the transformation again, you can hit the big red “play button” toolbar icon (which is called “Apply Transformation Scenario”).
If you are using the command line tool that comes with FOP instead of oXygen, the command you need should be something like this:
fop -xml spells.xml -xsl spellcards.xsl -pdf spellcards.pdf
Once you have the PDF created, make a test print of one of the pages.
Put this page behind one of the Avery business card pages and hold it up
to the light to see how it lines up. Every printer feeds slightly
differently, so you may need to tweak the measurements slightly to align
the cards just right. You do this by altering the
Search this file for the word “tweak”. You’ll find a couple, which identify spots in the file that measurements can be altered. One place is the margins for the whole page. The other is the padding within a table cell. Adjust these until your test page lines up with the Avery labels. Once this all works, print out a test page on the Avery label paper. Hopefully it will still match up. If not, tweak again. Once it works, print them all.
Note, the measurement tweaking locations (as well as some nearby
spellcards.xsl can be altered more radically
to support other types of Avery pages, such as index cards or mailing
You may not want to make cards of every single thing in the
spells.xml file. You can use different XPath statements
spellcards.xsl file to select subsets of the
data. There are two logical places to do this in the file, both of which
can be found by searching for “filter” in the text.
The primary location is the select of the main
apply-templates tag. In the file, there are a half-dozen or
so alternate examples for this statement that you can use. Just comment
out the first one and uncomment the one you want. You should be able to
find a statement close to what you want and alter it to taste.
For more exotic filtering, there is a very slim chance that you what
you want can’t be done in the main select. You can, instead, examine
each spell as it comes through and make more complicated tests. The
transformation file contains a skeleton to do such filtering, but always
lets everything through. You’d need to change this to set
include to true.
Filtering this data is harder than it could be, because the
level tag in the
spells.xml file is a string
that combines all of the level information into a single field,
rather than more structured data. For example, if a line like:
<level>Cleric 6, Druid 6, Sorcerer/Wizard 6</level>
<level> <class>Cleric</class> <classlevel>6</classlevel> </level> <level> <class>Druid</class> <classlevel>6</classlevel> </level> <level> <class>Sorcerer/Wizard</class> <classlevel>6</classlevel> </level>
…it would be a lot easier to do more powerful XSLT filtering and sorting.