The Notepad is old and grody

Publishing the Dice Word List book

Scribbled · PDF  · ◊ Pollen Source

I recently published a small book, the Dice Word List, and I wanted to write down some of the technical details and approaches I took in getting that little thing out the door. Parts of this post assume some familiarity with Pollen and LaTeX.

Although the book’s cover is not very complex or impressive, creating it was the biggest detour I took. I didn’t just want a cover, I wanted better tools for making covers for future books as well.

I’ve made a few books for fun over the years, and creating the cover was always my least favorite part of the process. I had to fire up a graphics program, calculate the size of everything, and click around until I had what I wanted. If my page count changed, I’d have to recalculate everything and manually futz with it some more. This kind of activity always felt wrong in the context of the rest of the project, the whole point of which which was to automate book production with code, not clicks.

So for this project, I created bookcover: a Racket language for writing book covers as programs. Check out the examples in the documentation to get a feel for how it works.

Writing and packaging bookcover was a fun publishing exercise in its own right, and it deserves its own blog post. I learned a lot about Racket in the process.The web book Beautiful Racket, and the post Languages as Dotfiles from the Racket blog, were a huge help in learning and understanding the concepts I needed to write this package. Also, I’m a documentation nerd; I love that Racket’s documentation system is itself a full-powered Racket language, and I love the fact that they have such well-considered and accessible style guides for code and prose. It was great to have an excuse to use these tools and to contribute to the Racket ecosystem in some small way.

But the best part is that I now have a way to crank out book covers for future books.I have wanted to have this kind of tool ever since I read about Faber Finds generative book covers. And, if you use Racket, you do too.

As with previous projects, I used LaTeX to produce the book’s PDF. LaTeX is itself a programming language, but, like most people, I find its syntax grotesque and arbitrary (kind of like its name). So for just about all the interesting bits I used Pollen as a preprocessor.

For example, here’s the part of my LaTeX template that sets the book’s measurements:

📄 book.tex.pp
% Here is where we configure our paper size and margins.
◊(define UNITS "mm")
◊(define STOCKHEIGHT 152.4)
◊(define STOCKWIDTH 105.0)
◊(define TEXTBLOCKHEIGHT (* (/ 7.0 9.0) STOCKHEIGHT))
◊(define TEXTBLOCKWIDTH (* (/ 7.0 9.0) STOCKWIDTH))
◊(define SPINEMARGIN (/ STOCKWIDTH 8.0))
◊(define UPPERMARGIN (/ STOCKHEIGHT 9.0))
◊(define (c num) (format "~a~a" (real->decimal-string num 3) UNITS))

\setstocksize{◊c[STOCKHEIGHT]}{◊c[STOCKWIDTH]}
\settrimmedsize{◊c[STOCKHEIGHT]}{◊c[STOCKWIDTH]}{*}
\settypeblocksize{◊c[TEXTBLOCKHEIGHT]}{◊c[TEXTBLOCKWIDTH]}{*}
\setlrmargins{◊c[SPINEMARGIN]}{*}{*}
\setulmargins{◊c[UPPERMARGIN]}{*}{*}
\setheadfoot{13pt}{2\onelineskip}  % first was originally \onelineskip
\setheaderspaces{*}{\onelineskip}{*}
\checkandfixthelayout

The first part of that is sensible Pollen code, the second part is LaTeX with Pollen functions wedged in.

I can’t tell you how many times I had to read the LaTeX documentation and scratch my head in order to understand how to code that second half. Looking at it now, I’ve already forgotten how it works exactly. But thanks to the crystalline little safe space of Pollen written above it, it’s easy for me to come back to it, understand what is happening, and how to change it if I want to.

Further down, there is also Pollen code that slurps up the raw raw word list text file from the EFF’s website, cooks up LaTeX code for each line, and inserts it all into the book’s middle. This means that in future I could simply substitute a different word list and easily generate a book for it.

I wrote a short preface to the book with some (I hope) fun and useful information in it: how to use Diceware, how secure is it really, all that sort of thing. But security and word list design are really deep topics, and I wanted some good way of referring the interested reader to more informative reads on these topics.

The problem is, if you put a URL in a printed book, sooner or later it will break: the web page it links to is going to disappear, or the information in it is going to go out of date. Plus, some URLs are really long. Who’s going to bother typing in a long web address?

The gods of the internet have provided the solution to the broken link problem in the form of PURL: the Persistent URL, a service of the Internet Archive. PURL works like a URL shortener such as bit.ly or goo.gl: it lets you create a short URL, and say, in effect, “make this short URL redirect to this other, long URL.” But unlike other URL shorteners, PURL isn’t a profit center or a marketing tool: it’s a service of the Internet Archive, a nonprofit whose whole raison d’être is preserving the Internet, and who will (likely, hopefully) be around for many more decades.

So I made some PURLs of the links I wanted and put them on the last page of the preface as QR codes.

Page of QR codes from the first version of the book
Page of QR codes from the first version of the book

Open the camera app on your phone and point it at one of the codes: it’ll take you right to the web page. If any of those pages ever disappear, I can redirect the PURL to a snapshot of it on the Internet Archive, or to another page. This way the links will be good for at least as long as I’m alive, which is more than can be said for the URLs in 99% of the other books in the world.

I’m kind of shocked that more people don’t know about and use PURLs. They could, for example, be used as your main “web presence” address, since they are more permanent than even domain names.

There is also surprisingly little guidance from the service itself about how it should be used. The “top-level directory” portion of a PURL (the jd/ part in the last two PURLs shown above) is called a “domain”. PURL domains seem like they should be an incredibly important, protected resource, since they are permanently tied to the account that created them (once you claim a domain, no one else can create PURLs in that domain)—and, once created can never be deleted! Despite this, creating a PURL domain is easy, too easy. Almost the first thing you see when you log in is a prompt and a form for creating new domains in one click, with no confirmation, no indication that you are doing something permanent. It’s like PURL’s design is begging you to mindlessly create as many domains as you can think of.

Before I realized this, I had created two PURL domains, one each for the two sites I was linking to: purl.org/eff and purl.org/stdcom. I’m somewhat embarrassed that I now apparently have permanent irrevocable ownership of these “domains”, and am still trying to find out who to contact to remedy this. Meanwhile, I did claim the jd domain and will probably be using solely that domain for all the PURLs I create for my own use, for the rest of my life.

Back to the book: here’s the LaTeX code I used to generate and place the QR codes:

📄 book.tex.pp
% In the header:
\usepackage{pst-barcode}
\usepackage{tabularx}

% …

\newcommand\qrlink[2]{%
    \begin{pspicture}(0.6in,0.6in)%
        \psbarcode[rotate=-25]{#1}{width=0.6 height=0.6}{qrcode}%
    \end{pspicture}%
    & #2 \par {\tiny \url{#1}} \\[0.55cm]%
}

% … and in the document body:

\begin{tabularx}{\textwidth}{m{0.8in} X}
    \qrlink{https://purl.org/eff/2016wordlist}{Article by Joseph Bonneau etc etc.}
    % … etc …
\end{tabularx}

I have no wish to explain this in detail, but if you are attempting to do something similar and are already poring over the manuals for the pst-barcode and tabularx packages, hopefully this will give you something to go on.

I licensed two weights of the Halyard Micro typeface for the book, and wanted very much to use it for the headings on the website as well. But this one-page website has only three headings total—not enough to justify the overhead (both technical and financial) of an embedded webfont.

This is where Pollen came in handy again. I used Pollen as a static site generator for the book’s website as well as for the pre-processing of the book itself. The fonts’ standard EULA says it’s fine to use images of the font on a website; so I wrote a function that takes a string, sets it in Halyard Micro, and saves it as an SVG. I then wrote some tag functions that make use of that function. In case it can be of use to anyone, here’s all the relevant code:

📄 pollen.rkt
(define heading-font (make-parameter "Halyard Micro"))
(define heading-size (make-parameter 20))
(define heading-color (make-parameter "orangered"))
(define heading-sizes '(42 30 24))

(define (normalize-string str)
  (let ([str-nopunct (regexp-replace* #rx"[^0-9a-zA-Z ]" str "")])
    (string-join (string-split (string-foldcase str-nopunct)) "-")))

(define (make-heading-svg level txt)
  (define heading-filename
    (format "img/h~a-~a.svg" level (normalize-string txt)))
  (define heading-pict
    (colorize (text txt (heading-font) (heading-size)) (heading-color)))
  (define the-svg (new svg-dc%
                       [width (pict-width heading-pict)]
                       [height (pict-height heading-pict)]
                       [output heading-filename]
                       [exists 'replace]))

  (send* the-svg
    (start-doc "useless string")
    (start-page))

  (draw-pict heading-pict the-svg 0 0)

  (send* the-svg
    (end-page)
    (end-doc))

  heading-filename)

(define (make-heading level attrs elems)
  (define h-size (list-ref heading-sizes (sub1 level)))
  (define h-str (apply string-append (filter string? (flatten elems))))
  (define h-tag (format-symbol "h~a" level))
  (parameterize ([heading-size h-size])
    `(,h-tag ,attrs (img [[src ,(make-heading-svg level h-str)]
                          [class "heading-svg"]
                          [alt ,h-str]]))))

(define-tag-function (h1 attrs elems) (make-heading 1 attrs elems))
(define-tag-function (h2 attrs elems) (make-heading 2 attrs elems))

Let’s get into the weeds for a bit here:

The first section sets up some parametersParameters are kind of like Racket’s thread-safe equivalent of a global variable, although they work a little differently. You can use parameterize to change their value temporarily for a given scope; at the end of that scope the parameter automatically reverts to its previous value. to use as defaults.

The normalize function transforms "My Heading!" into a string like "my-heading", making it ready to use as the base part of a filename—just tack .svg at the end.

The make-heading-svg function creates the SVG file and saves it in the img/ subfolder.

This in turn is used by the next function, make-heading, as it generates what becomes the <h1> or <h2> tag.

Finally, Pollen’s define—tag—function sets up the h1 and h2 tags to call make-heading with the appropriate heading level.

The upshot is that when, in my source markup, I write:

◊h2{Need Dice? Get the Good Dice.}

…it becomes, in the output:

<h2><img src="img/h2-need-dice-get-the-good-dice.svg" class="heading-svg" alt="Need Dice? Get the Good Dice."></h2>

…and of course, when the site is generated, the .svg file magically appears in the img folder, and everything looks awesome.

Maybe this seems like a lot of code for three headings.The basic technique of using an image of text instead of just the text is basically how we used to use non-standard fonts on the web before @font-face. It’s bad and dumb in most cases. Don’t do it. As with the book’s cover, I could have just made the images by hand in a graphics editor like Pixelmator. But, as with the book cover, since I did it with code rather than by farting around with a mouse, it’s very easy to change the headings or make new ones if I ever want to.

There you have it! A little book produced entirely with code. If you have an idea for another one, let me know.