<< Prev  |  TOC  |  Front Page  |  Talkback  |  FAQ  |  Next >>
LINUX GAZETTE
...making Linux just a little more fun!
Flashkard Printed Output
By Phil Hughes

Flashkard Printed Output

Flashkard Printed Output

Hal Stanton's article about FlashKard made me abandon some primitive flashcard software I had been working on. But, what I wanted was a way to print the data on, well, flashcards. I was going to write one of my typical hacks--most likely using awk and troff--to print the cards but I decided to try to actually work with the xml.

What I mean by work with the xml is to use standard XML tools to format the data for output. I had never done this before but I knew an important buzzword which is XSLT which stands for XML Stylesheet Language Transformations. This is a language designed to define transformations from XML to other formats. So, I started reading. Typically, XSLT is used to transform XML into HTML but there is no restriction on what you can do with it so I decided to give it a try.

But, I still needed two more pieces: what to transform it into and, as there would be some program logic involved to place the cards on the page in the right place, some general-purpose programming language. After considering the various programming language alternatives--Python being the one that sounded best--I realized that if I just generated PostScript then I could let the printer itself deal with the placement issues. Strange but, I figured, why not.

Output Format

Having picked PostScript, I sat down to actually decide how to place the cards on the page. Flashcards need to be double-sided and, at first, I thought of printing one side and then running the card stock back thru the printer to do the other side. This is a logical nightmare as it is easy to get the paper in the printer wrong, have a registration problem or get out of order because of a printer jam.

I decided on an alternative approach which involves another high-tech device called a glue stick. The idea is to print the front and back of each card on the front of one page which you then fold in half, glue together and cut into the actual cards. The double layer of paper and the glue will make the cards heavy enough to work with.

At this point, it is time for a confession. This is not a beautiful, finished production system. What it is is something that works and a proof of concept. For a production environment, it is important to define card sizes and fonts in a configuration file. In addition, the message for each side is currently printed in a single line without consideration of size. Line folding needs to be implemented.

Ok, back to work. I picked a 1.5 x 2.5 inch card size which makes it possible to get nine cards--both front and back--on one side of letter-sized paper. There are 1 inch top and bottom margins and .5 inch left and right margins. In order to make folding and cutting easy, I want to print a fold line down the middle of the page (between the front sides and the back sides) and cut marks for the edges of the cards. With this fold, the printing on the back is upside down from the printing on the front. After considering this I decided it wasn't important--it just defined which way to turn over the card when using them.

The PostScript

Everything (that is, the PostScript and the the XSL) is all in one file which you can download here. You can just ignore the XML stuff for now; note that if you try to display this in your browser, it will not display correctly because of the XML. You can see the sample output here.

If you have never worked in PostScript, get ready. PostScript is an RPN (Reverse Polish Notation) language. If you have ever used an HP calculator you will know what I am talking about. If not, the quick explanation is that you do things by putting items on a stack and then operating on that stack. For example, to add two numbers, you place the numbers on the stack and then execute the add operator which fetches the numbers, adds them and puts the result back on the stack. Note that I hate RPN languages. :-)

Disclaimer aside, PostScript is actually a very clean language and not a bad language to do the work we need to do. The way you work with PostScript is you describe everything you want to put on a page--characters, lines, filled-in areas and such--and then you tell it to print the page. That means that we don't have to remember a lot of stuff and then work down the page sequentially--we just move around and put what we want on the page.

In PostScript the basic unit of length is 1/72 of an inch. Personally, I an not very excited about working in such units so I defined a function called inch which takes the current value on the stack, multiplies it by 72 and puts the value back on the stack.

/inch { 72 mul } def
This way, I just add the word inch after a number and it gets multiplied by 72.

If you look at the cutmarks function, you will see a whole bunch of moveto and lineto statements. As you might expect, these operators take two values off the stack (an x and a y coordinate where the 0,0 is the lower left corner of the page and a positive move in to the right or up) and either move the current location to the specified coordinates or draw a line from the current location to the specified location.

Going down to the startit function, you can see all the setup work for the page. I define three, 9-element arrays, x, yf and yb which contain the x and y coordinates (yf for front, yb for back) of where to place the text for each of the nine cards. (Note that arrays in PostScript are indexed starting at 0.) The other two initialization steps are to define the font and font size to be used for the text and to set the card number counter cardno to 0.

Two other utility functions are defined, cardstep and pageout. pageout checks the current card number and if it is greater than 0, draws the cutmarks (by calling the cutmarks function and then prints the page using the showpage builtin. cardstep increments the card counter and then, if it is greater than 8, calls pageout to print the page and then resets cardno to 0 to prepare for the next page.

The last two functions are front and back. They move to the correct location on the page by indexing into the location arrays and then print the top value on the stack using the show builtin. The back function then calls cardstep to move along to the next position. Thus, the following two lines would print a card:

(Front Side) front
(Back Side) back

I said two lines but the spacing isn't important in PostScript. You would get the same result of this information was on one line. The parenthesis are used to delineate the string which is being placed on the stack..

All of the lines starting with a slash (/) have just defined functions. The real program starts with the line startit which calls the startit initialization function. Next, a series of calls to front and back must be input finally followed by a call to pageout to output the last page if there are any cards on it.

The XSL

I tested the PostScript with some sample data and it worked fine. So, on to the next part which is translating the XML from FlashKard into what is needed to drive the PostScript code. Two pieces are needed here, the XSL that I have to write and a program to read the XSL and the XML files from FlashKard and then output the PostScript to send to the printer.

The easy part was the program. xsltproc is exactly this program. One down. On to writing something in a language I have never seen before. But, could it be worse than writing in an RPN language?

As it turns out, there really isn't much to do. After some XSL boilerplate (<xsl:stylesheet ... > I needed to define the output format to be text as HTML is the default. What text means is "anything else". This is done with

<xsl:output method="text">

The first thing I want to output is the PostScript program itself. This is done by including it immediately after a <xsl:template match="/"> tag. The match of / matches the whole XML so it is processed at the start of the file. Note that I have put the %!PS on the same line as the xsl tag. This is necessary so that the printer will see this as the beginning of the first line of data. Otherwise the print spooler will think this is just text and print the PostScript rather than it being interpreted.

There is one other XSL tag before the matching </xsl:template> tag which is <xsl:apply-templates/>. This tells xsltproc that any other matching templates are to be applied here.

There is one other template with a match expression of match="e". This matches the block describing an individual card. This is explained in a comment to the FlashKard article. Within that block is an o block for the original language entry and a t block for the translation. Using the value-of feature, I grab these values, put them in parenthesis and follow them by either front or back.

That's it folks. Assuming the XSL in in ks.xsl, entering the command

xsltproc ks.xsl creatures.kvtml | lpr
will give you your first set of flashcards.

As I mentioned before, this is a proof of concept. Generalizing the PostScript, dealing with line folding and writing a shell scropt wrapper for this command line would clean things up and make a useful program.

Phil Hughes, Group Publisher of SSC, likes to get his hands dirty every now and then. But, you won't find him driving a car with an automatic transmission or using Emacs.


Unless otherwise mentioned, this work copyright © 2003-2004 by SSC, Inc. All rights reserved.

 

Phil Hughes is the publisher of Linux Journal, and thereby Linux Gazette. He dreams of permanently tele-commuting from his home on the Pacific coast of the Olympic Peninsula. As an employer, he is "Vicious, Evil, Mean, & Nasty, but kind of mellow" as a boss should be.


Copyright © 2004, Phil Hughes. Copying license http://www.linuxgazette.com/copying.html
Published in Issue 98 of Linux Gazette, January 2004

<< Prev  |  TOC  |  Front Page  |  Talkback  |  FAQ  |  Next >>