Prince is a HTML-to-PDF-via-CSS converter. Prince is often used for typesetting books, and baseline alignment — where lines of body text align across columns and pages — is a commonly requested feature. CSS does not (yet) provide for this, but Prince 14 is able to align baselines with the help of a small script. This guide will show you how.
In a document with only plain text, baseline alignment will happen automatically. In this example, all lines align perfectly with no need for adjustments:
htmlarticle { columns: 2; font: 12pt/1.5 Georgia, serif; } p { margin: 0; padding: 0; text-indent: 1.4em; }
The harmony of plain body text is quickly broken when headlines and figures are introduced. As can be seen in this example, lines of body text are no longer aligned:
htmlarticle { columns: 2; } img { float: top; }
Click on the thumbnail above to see the non-aligned lines. For typographers, this is a no-no.
The trick to restore harmony in this document is to add space above and below elements that disturb the peaceful rhythm. This is done through a little script. After typesetting the document, the script calculates how much space must be added to align lines. Thereafter, a second round of formatting is started.
In the next example, the script carefully adds padding so that baselines align. To better see the added padding, a background color is added to the adjusted elements:
htmlarticle { columns: 2; font: 12pt/1.5 Georgia, serif; /* line-height is 18 point */ } h1, img { background: khaki; } img { float: top; } ... <body onload="snap(18, 'h1, img', 'padding')">
Voila!
In the previous example, the onload
attribute of the body
element contains the call to the snap()
function. There are three arguments. The first is a numerical value which represents the line-height (in points) of the body text. The line-height of this document is 18 points (12 points × 1.5).
Thereafter comes a selector, (actually, a comma-separated list of selectors) and the name a method to be used when adjusting the element. In the above example, the script will adjust padding for h1
and img
elements.
To more easily see the effect, we add more headlines and figures:
htmlh1, img { background: khaki; } ... <body onload="snap(18, 'h1, img', 'padding')">
As can be seen in the rendering above, the extra padding is not evenly distributed: elements that are on the top of the page have most of the padding set on padding-bottom
, and vice versa for elements at the bottom. The idea is to fill the whole of the page and not leave white space along the edges.
If you prefer an even distribution of space above and below the content, you can use the 'padding-even' method. This is a not CSS property, it's just the name of a method which our script recognizes.
htmlh1, img { background: khaki; } ... <body onload="snap(18, 'h1, img', 'padding-even')">
By setting margin
instead of padding
, the khaki-colored areas diminish, but baselines are still aligned:
html<body onload="snap(18, 'h1, img', 'margin')">
In general, it's better to use the 'padding' method rather than 'margin'; margin-collapsing may get in the way.
The script can also increase the height of elements rather than adding padding or margins. For textual elements, this looks the same as if the padding is added at the bottom of the element. But for images, the added height will result in a slightly changed aspect ratio:
html<body onload="snap(18, 'h1, img', 'height')">
To retain the aspect ratio for images, the object-fit
property can be used:
htmlimg { float: top; object-fit: cover; } ... <body onload="snap(18, 'h1, img', 'height')">
Side boxes with padding, images, and body text of their own pose a challenge. Consider this example:
htmlfigure { background: #ddd; padding: 0.6em; float: top; } ... <h1>Birds</h1> ... <figure> <img src=gjess1.jpg> <figcaption>... </figure> <body onload="snap(18, 'h1', 'height'); ">
The size of the heading element has been adjusted, but two challenges remain. First, the body text inside the figure
element must be baseline-aligned with the body text in the left column. Second, the size of the figure
element must be adjusted so that the text below the caption also aligns with the left column.
We will solve the first challenge by using a method called push
. This method pushes an element downwards until it snaps into place. To solve the second challenge, another margin-bottom
method is used.
html<body onload=" snap(18, 'h1', 'margin-bottom'); snap(18, 'figcaption', 'push'); snap(18, 'figure', 'margin-bottom'); ">
In the above example, the snap
function is called three times. Each call causes another rerun of Prince. For large documents, you want to minimize the number of reruns. In some cases it is possible to combine several function calls into one. In this example, the first two calls are independent of each other and can therefore be combined:
html<body onload=" snap(18, 'h1', 'margin-bottom, 'figcaption', 'push'); snap(18, 'figure', 'margin-bottom'); ">
Superscripts sometimes upset the baseline rhythm by pushing lines further apart:
html... <sup>2</sup>
You will notice that the superscript at the end of the first paragraph makes the line slightly higher than the others. This can be fixed by setting the line-height
of the sup
element:
htmlsup { line-height: 0 }
The observant reader has probably realized that the script, while wonderful, does not really know anything about baselines. It merely tries to ensure that all boxes in the document have a height which is a multiple of the line-height. This strategy makes it possible for a simple script to fix an important problem, but it also has limitations. In some cases, the increase in size will result in elements being pushed to the next column, or the next page. The current script does not alert authors when this happens.