SVGutils, or: Doomed to Program
Recently, I’ve been taking a bit of a break from programming in my spare time. In this vein, I’ve been playing some board/card games — and, having played a few, I thought it might be fun to try my hand at designing my own card game. I think some people prototype card games using index cards, but that seems to me inefficient if you need duplicate cards: if you need twenty of a particular card, that obviously calls for mass production by printing rather scribbling by hand. So I decided to at least use a computer to help produce a prototype — but the computer use would only be for doing a bit of text on some cards and then printing them out. No programming involved.
Card games tend to be different text/images, often with duplicates of each card, on only a handful of templates. I could knock up a template, copy it many times and then change the text on each copy. But what if I then want to change the template? This is a maintainability issue: clearly we should separate the templates from the text and images to be used on them. But that means we’ll need a way to substitute all the text and images into the template when it comes time to print. Hmmm. I tend to use Inkscape for vector graphics editing, and since Inkscape stores SVG files, which is XML-based, we can actually do simple text-based substitution on the SVG files to put in our custom text (and image file names) for each card. A fairly simple scripting task which only has a modicum of programming.
We will need a format to store the bits of text (e.g. card title, card type, main text) to be substituted for the various cards. We could store the data for cards in a CSV-like format, with a different column for each bit of text, and a row per card. But many cards will probably share the same substitution values: for example, they may have the same card-type, like “instant” or “attack”. It seems a shame to have to type that out many types, and it is also bad practice that again impacts maintainability. So some sort of grouping via a hierarchical format would help here, to share values among groups of cards. YAML is a nice lightweight markup language, which is easier to manually write than XML, so that’s a good candidate. YAML libraries exist already, so that’s not adding too much burden. And in the long run it will save effort, so that excuses a bit more programming.
Since we have this substitution mechanism, we can save ourselves even more repetition. Sometimes you have cards that are fairly similar to each other in content, perhaps only changing a particular word. For example, you might have bronze, silver and gold cards of the same variety. Or you might want to have something like classic playing cards: each combination of rank and suit, but you wouldn’t want to write out 52 cards. Much better to write the 4 suits and 13 ranks and then express that you want each possible combination. So we can add some basic “for-each” functionality to our YAML markup (don’t worry, it’s not going to end up Turing-complete — I think!). A satisfying addition that shrinks the card definitions further, which makes the added programming worthwhile.
Note: This isn’t the software I’m releasing today (SVGutils just does the tiling described in the next section) but in time I will probably release the card creation software too.
Once the substitution into the templates is done we’ll need to print the cards (I’m only printing on a standard desktop printer) — but all the cards will be in separate post-substitution files. It would be fiddly to print them all, and would be a terrible waste to have one card per sheet. What would be really useful is a program to tile the cards together onto a handful of printable A4-size pages so that it’s easy for me to print them out and cut them up. And extra nice would be the ability to print them double sided to give the cards backs automatically. More programming, but a big saving in effort for physically creating the cards.
How Did It Come To This?
So having started with the intention of design a non-computer-related card game, I ended up a few hours later knee-deep in YAML and SVG processing. Once you’re a programmer, I think you just can’t help yourself when you see tasks like these that call for automation and tools. Doomed to program! Haskell was an obvious choice of language to make the process enjoyable. In the next section I’ll explain which Haskell libraries I ended up using (for those that might be interested), and then I’ll finally talk about the library I wrote and released to do the tiling for printing: SVGutils.
A Tour of Haskell Libraries
Haskell has Hackage, a centralised repository of lots of Haskell libraries and programs. I was pleased to find that most of the libraries I needed were already on Hackage.
The first thing I wanted was a library to read in YAML. I’d come across YAML during some Ruby on Rails work, and it seemed like a nice way to store some structured information about the cards. YAML is nicer than XML to write in a text editor, which is what I was going to be doing. Hackage had several libraries relating to YAML, which I quickly whittled down to:
- yst: The yst library was actually in the same vein as several parts of the project, seeing as it provided the ability to read from YAML files and substitute into templates. From a read of its documentation, it’s centred around HTML, and I decided it looked like it would be easier to start from scratch than adjust a sizeable existing application (I may have been wrong, though).
- data-object-yaml: A library with a pleasingly short API, and a quick click on its main data type for representing YAML data looked good:
data Object key val = Mapping [(key, Object key val)] | Sequence [Object key val] | Scalar val
That’s nice and clear. You can use the richer YamlObject type as key and value, or you can just use String or Text if you don’t need the anchors and references that YAML supports (I don’t).
- HsSyck: Another good candidate, but its types always use the richer YAML format, and with a abstract text type (that is actually Bytestring), which isn’t as handy as being able to use String or Text.
So I chose data-object-yaml, based on looking at the API.
The next thing I needed was a library for performing string substitution in the SVG files (aka templating). There are many template libraries on Hackage. Some of them are specifically targeted at HTML, so I skipped over those. Some can work on XML, which is a possibility given that I’m working with SVG files (SVG is an XML-based format). But that requires me to put comments or custom tags into the XML file, which is slightly awkward to do from Inkscape. The simplest solution was to use the small template library. This can do simple substitution, substituting $foo in the document for whatever value you map from the key “foo”. This allowed me to construct my SVG files almost exactly as I wanted them, but put “$Name” as the content of the Inkscape text object where I wanted the name (which then showed on my screen as “$Name” while I fiddled with the font, centring, etc). Then I could use the template library to substitute for “$Name”, and my name text would end up exactly where I wanted it in the image. So the solution that knew nothing about the structure of the SVG file turned out to be the easiest. The template library lacked one or two functions that I needed, so I downloaded the source and tweaked it, then submitted some patches which ended up contributing to the latest version. Open source in action!
Finally, I needed a library that could tile my resulting SVG cards together for printing. A quick search on Hackage for SVG revealed a dearth of SVG tools, and certainly nothing like what I needed. So it looked like I was going to have to write that one myself. Thankfully there were many libraries on Hackage for dealing with XML, so I could at least get the SVG documents parsed as a generic XML document ready for me to do some SVG-specific bits. Since there are many XML libraries, I needed to narrow down the choice a bit. I remembered that Don Stewart had recently posted about popular libraries on Hackage in various categories (including XML), so I decided to follow the crowd and pick from the ones that were popular: xml, HaXml and hexpat.
HaXml can be used in a totally different way to xml and hexpat. HaXml can generate a Haskell source file with a data-type that matches the DTD or XSD schema of an XML file (in this case, the SVG spec), to allow you to manipulate the document in a type-safe manner. I’m all for type-safety, and I had a quick try with HaXml. The file it generated with the complete Haskell types for the SVG spec was certainly large — lots of types, and many of the attribute types had towards 100 members. The thing that put me off this approach is that XML documents often have cross-cutting concerns. For example, many items in SVG have the color attribute. In the DtdToHaskell output, each color attribute is a differently-named member of a different attributes item (and all have the type Maybe String, which doesn’t help to mark them out). If I want to write a program to manipulate the colours in an SVG file, then with the DtdToHaskell approach I have to write a lot of code to pick out each color. With the standard approach that treats XML files very uniformly it’s actually much easier, as I just skim down the tree looking for any color attributes. I glanced at the API for xml and hexpat — there didn’t seem much to distinguish them, but the former had simpler central types so I went with that.
So I had most of the bits I needed except for code dealing with combining many SVG files together into one for printing. That bit I wrote myself, and have bundled up the code into a package I’ve called SVGutils. It’s a light wrapper around the XML library that has a few functions for dealing with SVG files: primarily, tiling several into one. It has both a library and a command-line utility for doing this tiling. The tiling works by finding out the size of each SVG file and then placing the contents appropriately in a larger file with a paper size of your choosing, which can then be converted into PDF using Inkscape and stuck together using PDFtk (is it worth integrating this into the tool, I wonder?). It supports having “backs” for each file, which will also be tiled correspondingly (but mirrored) so that you can print out both the front and back then stick them together before cutting them up, or just print out the whole document double-sided. I’ve no idea if anyone other than me will find this useful, but it seems worth sharing, so it’s now on Hackage. In time I may expand the SVGutils library with more functionality — patches also welcome. I don’t expect it to end up as a library doing very complex SVG manipulations or rendering, but time will tell.
The End Result
So I’ve ended up releasing an SVG library, and have also got my complete program for taking in a YAML file and SVG templates and producing a PDF of the resulting images. What I had actually set out to do was to design a card game. I did actually finish the first version, and tested it last weekend. It turns out that, like most things, game design is harder than it first appears. More work needed, when I can tear myself away from programming!