Stylesheet for printing

April 8, 2018 by Stefan Huber

CSS can also be applied for styling paged media. Major browsers support basic primitives for print stylesheets. However, some more advanced features are only supported by proprietary render engines.

Loading the print stylesheet

A complete stylesheet document or single CSS rules can be contextually loaded based on the media type. There are basically 4 different media type contexts for stylesheets:

  • all: which is default and is used for all contexts.
  • screen: only for screens like desktops, mobile phones or tablets
  • print: only for printers
  • speech: the special context for screenreaders, which reads the page out loud

Inside the head tag of a HTML-document stylesheet links are declared. The media attribute can be specified to only load a stylesheet for the print context.

<link rel="stylesheet" href="print.css" type="text/css" media="print">

@media rule

Within a stylesheet the @media rule can be used to contextualize a single or several styling rules only.

@media print {
    /* css rules */
}

Page breaks

A powerful feature for print stylesheets is the possibility to define when a page break should occur. Therefore the page-break-* properties are available.

The rules page-break-before and page-break-after define if a page break should occure before or after a HTML element. The attribute values auto, always and avoid are probably self explanatory. The values left and right are especifically available for two-sided documents. The left value means that the next page should be a left page of a two-sided document. This might add a blank page if the next page is a right page. For a right value it works the same, however the other way around.

page-break-before: auto|always|avoid|left|right;
page-break-after: auto|always|avoid|left|right;

Additionally, the rule page-break-inside allows for avoiding page breaks inside an element.

page-break-inside: auto|avoid;

The special @Page rule

The @page rule allows for specifing the format of a printed page. It is important to note, that only a couple of CSS properties can be changed inside @page (others are ignored). The @page rule allows specifying margin, size, page-break-*.

@page {
    /* css properties */
}

Furthermore it is possible to use the pseudo-classes :first, :blank, :left, :right to target pages more specifically.

Handling orphans and widows

Orphans and widows are very annoying effects when preparing a text for print. An orphan is the first line of a new paragraph, which is printed on the bottom of a page. A widow is the last line of a paragraph, which is printed on the top of a new page.

The orphans and widows rules allow for stating the minimum number of lines a pragraph needs to have either on the old or the new page. The page-break-inside rule can be used to avoid page breaks inside paragraphs altogehter.

@media print {
  p {
    orphans: 3;
    widows: 3;
  }
}

Some Examples

A webpage typically includes weblinks, which are very useful inside the browser, but provide no functionality on paper. However, it might be good practice to at least add the link target placed after the link text.

a[href^=http]:after {
    content:" [" attr(href) "]";
}

The above snippet would print the URL in square brackets after a link text, which has a weblink inside the href attribute.

Chapter numbers

CSS has support for counters, which are a handy feature for numbering chapters. Everytime a CSS rule is applied inside a document a counter can be changed: counter-increment for incrementing the counter or counter-reset for resetting the counter to 0. The value of a counter can be accessed with the function counter(countername) and the respective counter name. Counters can have any name like a variable in programming.

h1:before {
    counter-increment: header1;
    content: counter(header1) ". ";    
}
h1 {
    counter-reset: header2;
}
h2:before {
    counter-increment: header2;
    content: counter(header1) "." counter(header2) ". ";
}
h2 {
    counter-reset: header3;
}
h3:before {
    counter-increment: header3;
    content: counter(header1) "." counter(header2) "." counter(header3) ". ";
}

The above snippet would place a chapter number like 1.3.2. before a HTML heading (h1, h2, h3).

Page numbers

Printing page numbers e.g. inside a footer is possible with CSS counters as well. Unfortunately, the presented solution works only in Firefox for now. The Chrome Browser doesn’t increment the counter as the footer is placed with position fixed.

<footer>
    <div id="page-number"></div>
</footer>
@media print {
    footer {
        position: fixed;
        bottom: 0pt;
        left: 0pt;
        right: 0pt;
        text-align: center;
    }  
    #page-number::after {
        counter-increment: page;
        content:"Page " counter(page)
    }
}

Testing

A general workflow for developing and testing print stylesheets would look something like this:

  1. Modify the print stylesheet
  2. Reload the stylesheet inside the browser
  3. Open the menu and select print and check the print preview

This is quite cumbersome to always require to navigate to print preview. Fortunately, Chrome supports emulation of the print media type. The instructions can be found here. This offers some simplification for developing print stylesheets. However, elements like headers, footers and page-breaks cannot be tested with this approach and require opening the print preview.

PDF generation

There are many software libraries for generating PDFs. To name just a few:

  • FPDF: FPDF is a PHP library for generating PDFs.
  • PDFBox: PDFBox is Java library for generating PDFs.
  • PDFKit: PDFKit is JavaScript library for generating PDFs.

Many of these libraries use their own API and/or templating engines for PDF generation. For developers it is quite a tedious task to learn these. Since e.g. the Chrome browser supports a headless mode, one can use it for PDF generation.

With the following command one can generate a PDF from a website (styled with a printable stylesheet). $LOCATION is the local filepath where the PDF should be stored. $URL is the URL of the website to print. Of course the command chrome has to be available via command line (check PATH environment variable).

chrome --headless --disable-gpu --print-to-pdf=$LOCATION $URL

Using a headless browser for PDF generation has the advantage that no new library and template language has to be learned by a developer.

Conclusion

Browsers generally support print stylesheets. Unfortunately, major browsers don’t support some key CSS functionality for paged media. There are several working drafts from the W3C like CSS Paged Media Module Level 3 and CSS Generated Content for Paged Media Module, but currently no major browser has support. For advanced paged media support there is a proprietary and quite pricey render engine called prince, which has support for the W3C drafts.

© 2019, Stefan Huber