A Beautiful Gift With Clojure and LaTeX

For my parents’ 26th anniversary, I decided to convert an online religious text they read into a beautiful, well-typeset book.

The online text was built by volunteers using an archaic version of Microsoft Word and looks like this:

Anyone who has read science or math literature is exposed to the high-quality output LaTeX produces.

Fortunately LaTeX’s abilities extend far beyond the domain of mathematical symbols.

I was able to combine Clojure’s excellent HTML processing infrastructure (enlive) and LaTeX to produce a nice looking document.

The entire process took a few hours.

Here are two pages from the final output:

This blog post contains latex and clojure snippets to produce that output. I am not good at designing books or combining typefaces and would appreciate advice.

The LaTeX Pieces

The inspiration for this book came from this TeX StackExchange thread.

A user was working on replicating a 16th century bible (img from LaTeX Stack Exchange):

Using that piece as inspiration, I converged on the following theme:

  1. A garamond typeface - I think they fit the theme of religious texts quite well. Fortunately a nice package ebgaramond makes it easy to typeset your entire document in this beautiful font.
1
\usepackage{ebgaramond}

is all you need to put in your LaTeX document.

  1. Liberal use of ornaments on page-borders, special pages etc.

The pgfornament package comes with very beautiful ornaments. When combined with TikZ, a seasoned user can create very sophisticated and professional documents.

I am not a seasoned user so I was perfect satisfied with using something out-of-the-box. Each page in the book was going to have these ornaments in the page-corners:

The pgfornaments package combined with eso-pic allows you to achieve exactly that.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
\makeatletter
\AddToShipoutPicture{%
  \begingroup
  \setlength{\@tempdima}{2mm}%
  \setlength{\@tempdimb}{\paperwidth-\@tempdima-2cm}%
  \setlength{\@tempdimc}{\paperheight-\@tempdima}%
  \put(\LenToUnit{\@tempdima},\LenToUnit{\@tempdimc}){%
    \pgfornament[anchor=north west,width=2cm]{63}}
  \put(\LenToUnit{\@tempdima},\LenToUnit{\@tempdima}){%
    \pgfornament[anchor=south west,width=2cm,symmetry=h]{63}}
  \put(\LenToUnit{\@tempdimb},\LenToUnit{\@tempdimc}){%
    \pgfornament[anchor=north east,width=2cm,symmetry=v]{63}}
  \put(\LenToUnit{\@tempdimb},\LenToUnit{\@tempdima}){%
    \pgfornament[anchor=south east,width=2cm,symmetry=c]{63}}
  \endgroup
}
\makeatother

Next, I decided that each chapter would begin at a new-page.

Chapter numbers and subtitles (if any) would be adorned above and below with ornaments. Essentially I was going for:

Note that the ornaments in the corner are the result of eso-pic.

The borders in the north, south, east and west, and the styling around the chapter title are accomplished by:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
\newpage
  \newgeometry{left=0cm,bottom=0cm,top=0cm,right=0cm}
  \begin{tikzpicture}[remember picture, overlay]
  \node[anchor=north] at (current page.north){\pgfornament[width=6cm,symmetry=h]{46}};
  \node[anchor=south] at (current page.south){\pgfornament[width=6cm]{46}};
  \node[anchor=north,rotate=90] at (current page.west){\pgfornament[width=6cm,symmetry=h]{46}};
  \node[anchor=north,rotate=-90] at (current page.east){\pgfornament[width=6cm,symmetry=h]{46}};
  \node[inner sep=6pt] (chapter) at (current page.center){\Huge Chapter I};
    \node[inner sep=12pt, below of=chapter, text width=10cm, align=center, outer sep=12pt] (title1) { };
  \node[inner sep=12pt, below of=title1, text width=10cm, align=center, outer sep=12pt] (title) { Salutations -- The Story of Grinding Wheat and Its Philosophical Significance};
  \node[anchor=north] at (title.south){\pgfornament[width=5cm]{60}};
  \node[anchor=south] at (chapter.north){\pgfornament[width=5cm,symmetry=h]{49}};
  \end{tikzpicture} 
\newpage

\restoregeometry

This forms the template for the book. Next, we populate the contents.

The Clojure Pieces

Enlive is a fantastic HTML parsing library for clojure. The hierarchical structure of HTML is captured in a clojure map:

To transform a single chapter, we traverse this map (tree) and transform the text as is appropriate. This is governed by where in the document the text occurs.

After manually inspecting a few chapters, I made a small table that mapped root - leaf paths in the DOM to handlers that would transform the text.

In clojure this can be succintly described as so:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
(defn convert-chapter-parse
  ([a-map]
   (->> (convert-chapter-parse a-map [])
        flatten
        (apply str)))

  ([a-map parent-path]
   (let [current-node-path (conj parent-path
                                 (if (= (:tag a-map)
                                        :body)
                                   [(:tag a-map)]
                                   [(:tag a-map) (:attrs a-map)]))
         node-contents (:content a-map)]

     (map
      (fn [an-item]
        (if (map? an-item)
          (convert-chapter-parse an-item
                                current-node-path)
          (let [fixed-item (-> an-item
                               (StringEscapeUtils/unescapeHtml3)
                               (string/replace #"\&"
                                               (Matcher/quoteReplacement "\\&")))]
            (format-content fixed-item
                            current-node-path))))
      node-contents))))

Essentially you keep track of where you are in the tree (relative to the root element) and then fetch a function from a table that transforms your text appropriately.

The table itself looks like this:

1
2
3
4
5
6
7
8
9
   [[:body] [:p nil]]
   identity

   [[:body] [:p nil] [:font {:size 5}]]
   identity

   [[:body] [:p nil] [:b nil]]
   (fn [text]
     (str "\\section*{" text "}"))

Simple.

Run this on the entire book and I managed to have a neatly typeset book hosted here.

Remarks

While a seemingly simple exercise (under 100 lines of code), html allows you to get the same output with different templating. I noticed that converting 10 chapters at a time and inspecting the batch for quirks was a better approach for measuring coverage.

LaTeX isn’t particularly fond of how HTML, MS Word etc use / handle double quotes, apostrophes and so on. I have a couple of string/replace functions but it clearly wasn’t enough to deal with the entire book. This is a problem that can only be solved by actually reading the book.

Overall, this turned out to be a really appreciated gift.


Twitter: @shriphani
Instagram: @life_of_ess
Fortior Per Mentem
(c) Shriphani Palakodety 2013-2020