I’ve been following new developments around CSS Paged Media and PrintCSS for a while now. I never had the chance to test them out in a real word project. Until recently. The publishing house I work for wants to produce personalized novels. Customers can customize their novel in a web fronted written in Python. The output is plain HTML5.
To avoid overhead I set out to typeset the different novels with web technologies. These are my findings:
tl;dr: It pretty much went smoothly. CSS Paged Media is even more convenient than InDesign in some aspects.
Docraptor.com (with Prince XML as the engine) was my Converter of choice.
I would only wish for one missing feature, otherwise typesetting was a breeze.
The Dream (and the reality)
- Automatic generation of print ready PDF from HTML & CSS (✔)
- Easy setup of pages and type-areas (✔)
- Custom Fonts (✔)
- Widows and orphans can be avoided (✔)
- Justified text has no rivers (✔)
- Hyphenation is handled properly and automatically (✔)
- Fill the given type-area with text from left to right and top to bottom with lines of text (left to right: ✔| top to bottom: ⚠)
- Headings can start on a new right page (✔)
- Pages can be empty (✔)
- Running headers (✔)
- Pagination (✔)
So let’s see how far we can get.
Generate PDF from HTML & CSS
To output print ready PDFs you need a converter engine. After some research I came up with the following possibilities (feel free to suggest alternatives)
- antennahouse
- docraptor (which uses PrinceXML in the backend)
- PDFreactor
- PrinceXML
- Vivliostyle Formatter
- Weasyprint
Page Setup
Page Setup is fairly easy with CSS Paged Media. You can choose between standard paper sizes like »Letter« or »A4« or use a completely custom Page size and type area via the @page rule.
@page { size: 125mm 187mm; margin: 15mm 15mm 16mm 15mm; }
Custom Fonts
easy:
@font-face { font-family: Garamond; font-weight: normal; src: url(fonts/AGaramondPro-Regular.otf) format('opentype'); }
Embedding custom fonts works as it should.
Widows & Orphans
Avoiding widows and orphans can be done with this code.
p.example { widows:2; orphans:2; }
Just as a refresher as CSS-Tricks phrases it:
widows = minimum number of lines in a paragraph split on the new page.
orphans = minimum number of lines in a paragraph split on the old page.
Justified Text
Justification can be easily set in the CSS.
p.example { text-align: justify; }
This was easy. Are we done? Not quite.
Nice looking justified text ist intrinsically linked with Hyphenation. Without hyphens your text will have big gaps and sport the occasional river, which is ver ugly and hard to read.
So on to …
Hyphenation
Hyphenation is handled by the renderer. You can switch it on and off via a CSS-rule.
p.example { hyphens: auto; }
Different renderers use different Algorithms and dictionaries for hyphenation. So the output will vary:
- Vivliostyle: No hyphenation at the moment (Uses WebKit as rendering engine. As soon as webkit does hyphenation they will, too)
- Weasyprint: Experimental hyphenation possible via vendor specific prefix
»-weasy-hyphens« (uses pyphen as hyphenation engine) - PDFreactor: Hyphenation possible (Dictionary for the following languages: Bulgarian, Catalan, Danish, New German, German traditional, Greek, Modern, English (US), English (GB), Estonian, Galician, Interlingua, Indonesian (Bahasa Indonesia), Icelandic, Italian, Kurmanji (Northern Kurdish), Latin, Dutch, Polish, Russian, Swedish)
- PrinceXML: Hypheantion possible (I think they use the TEX algorithm and OpenOffice dictionaries)
It’s important that you set the »lang« attribute for your document. The attribute can change for certain paragraphs.
Soft hyphens (­) can be used to influence how words are hyphenated.
All the converters that supported hyphenation produced fairly good looking justified text.
Filling the page from top to bottom automatically
So this was the single point, which did not work as I wished. As I’m avoiding widows and orphans not every page can be easily filled with lines of text. If this was an InDesign project I’d fiddle with the kerning and the width of the letters to shorten or lengthen certain paragraphs and thus fill the page completely. I could do this manually for every book in this project, but this would defeat the purpose of the whole thing.
Ideally this would be done by the renderer:
- Is the type-area filled with lines?
- If not try to fill it by shortening or lengthening preceding paragraphs using kerning and font width to fill it (within given parameters of course)
Headings can start on an new right page
easy:
h1 { page-break-before: right; }
This will start a new page starting with the h1. Which brings us to …
Empty pages
CSS paged media has a selector for empty pages: »:blank«.
Note: Some PDF Renderers do not yet (i.e. PDFreactor) support this.
Pagination
Pagination can be set up with CSS counters, generated content and page areas. First we set up a counter (I gave it the name »page-counter«). This counter will count up one one every page.
@page { counter-increment: page-counter; }
Now we need a place to display this counter. CSS Paged Media has 16 page areas in the margin and one for the main page itself:
top-left-corner | top-left | top-center | top-right | top-right-corner |
left-top | main page area | right-top | ||
left-middle | right-middle | |||
left-bottom | right-bottom | |||
bottom-left-corner | bottom-left | bottom-center | bottom-right | bottom-right-corner |
So let’s display the page counter on »bottom-right« or »bottom-left« depending on the page:
@page :right { @bottom-right { content: counter(page-counter); } } @page :left { @bottom-left { content: counter(page-counter); } }
Conclusion
It worked pretty well. I’d definitely do another project like this one. With a basic understanding of HTML & CSS and the documentation all around the web this task was fun and easy to do. Granted this was »only« a novel.
Would you rather have used InDesign?
Some things are so easily set up with CSS, that I wish InDesign would adopt this kind of workflow.
For example: Our Book has Line indents on every paragraph, except the ones that follow a headline or an ornament. This can be set up with one simple rule:
h1 + p.normal, p.ornament + p.normal { text-indent: 0; }
Done. No search and replace or applying custom styles to paragraphs. So Much easier.
Why I chose Docraptor for Production
- Flexible pricing model (monthly fee for a given number of created documents)
- support of @page:blank
- Good Hyphenation
Further Reading:
- Designing for Print with CSS (smashing magazine)
- PrintCSS rocks (good reference)
- Building Books with CSS3 (Article on »a list apart«)