Reference

Outlines/ Bookmarks

PDF supports outlines (Adobe calls them “bookmarks”). By default xhtml2pdf defines the <h1> to <h6> tags to be shown in the outline. But you can specify exactly for every tag which outline behaviour it should have. Therefore you may want to use the following vendor specific styles:

  • -pdf-outline

    set it to “true” if the block element should appear in the outline

  • -pdf-outline-level

    set the value starting with “0” for the level on which the outline should appear. Missing predecessors are inserted automatically with the same name as the current outline

  • -pdf-outline-open set to “true” if the outline should be shown uncollapsed

Example:

h1 {
  -pdf-outline: true;  -pdf-level: 0;
  -pdf-open: false;
}

Table of Contents

It is possible to automatically generate a Table of Contents (TOC) with xhtml2pdf. By default all headings from <h1> to <h6> will be inserted into that TOC. But you may change that behaviour by setting the CSS property -pdf-outline to true or false. To generate the TOC simply insert <pdf:toc /> into your document. You then may modify the look of it by defining styles for the pdf:toc tag and the classes pdftoc.pdftoclevel0 to pdftoc.pdftoclevel5. Here is a simple example for a nice looking CSS:

pdftoc {
    color: #666;
}
pdftoc.pdftoclevel0 {
    font-weight: bold;
    margin-top: 0.5em;
}
pdftoc.pdftoclevel1 {
    margin-left: 1em;
}
pdftoc.pdftoclevel2 {
    margin-left: 2em;
    font-style: italic;
}

Tables

Tables are supported but may behave a little different to the way you might expect them to do. These restriction are due to the underlying table mechanism of ReportLab.

  • The main restriction is that table cells that are longer than one page lead to an error

  • Tables can not float left or right and can not be inlined

Long cells

xhtml2pdf is not able to split table cells that are larger than the available space. To work around it you may define what should happen in this case. The -pdf-keep-in-frame-mode can be one of: “error”, “overflow”, “shrink”, “truncate”, where “shrink” is the default value.

table {    -pdf-keep-in-frame-mode: shrink;}

Cell widths

The table renderer is not able to adjust the width of the table automatically. Therefore you should explicitly set the width of the table and to the table rows or cells.

Headers

Table rows can repeat at the top of every page a table runs over. Mark them up as a <thead> and they all repeat:

<table>
  <thead>
    <tr><th>Column 1</th><th>...</th></tr>
  </thead>
  <tbody>
    ...
  </tbody>
</table>

The number of repeated rows can also be given explicitly, with the repeat property, which is useful for a header that is not marked up as one:

<table repeat="1">
  <tr><th>Column 1</th><th>...</th></tr>
  ...
</table>

<tfoot> is read as ordinary rows: there is no repeating footer.

Borders

Borders are supported. Use corresponding CSS styles.

Backgrounds and borders of a block

The background colour and the borders of a block element are painted paragraph by paragraph, not once around the block. Two consequences worth knowing before they surprise you:

  • A margin between the paragraphs of a block with a background opens a strip of the paper colour inside the box, and a border-top on the container draws a rule above every paragraph. Use padding on the paragraphs and put a rule in an element of its own.

  • A table inside a block does not inherit that background. Put the colour on the <td>.

  • An empty block draws nothing at all, borders included, because it has no height. A horizontal rule made of a bordered empty <div> needs something in it, typically a non-breaking space at font-size: 1pt.

Images

Size

By default JPG images are supported. If the Python Imaging Library (PIL) is installed the file types supported by it are available too. As mapping pixels to points is not trivial the images may appear bigger in the PDF as in the browser. To adjust this you may want to use the zoom style. Here is a small example:

img { zoom: 80%; }

The width and height attributes are pixels at 96 dpi, so a picture that fills the 210 mm of an A4 page is width="794". Declare both and the proportions will be taken from the width if they disagree with the picture’s own.

Position/ floating

Since Reportlab Toolkit does not yet support the use of images within paragraphs, images are always rendered in a separate paragraph. Therefore floating is not available yet.

Barcodes

You can embed barcodes automatically in a document. Various barcode formats are supported through the type property. If you want the original barcode text to be appeared on the document, simply add humanreadable="1", otherwise simply omit this property. Some barcode formats have a checksum as an option and it will be on by default, set checksum="0" to override. Alignment is achieved through align property and available values are any of "baseline", "top", "middle", "bottom" whereas default is baseline. Finally, bar width and height can be controlled through barwidth and barheight properties respectively.

<pdf:barcode value="BARCODE TEXT COMES HERE" type="code128" humanreadable="1" align="right" />

Custom Tags

xhtml2pdf provides some custom tags. They are all prefixed by the namespace identifier pdf:. The HTML5 parser does not know them, so it ignores the closing slash of <pdf:toc /> and treats the element as still open; the tags themselves take that into account, and a closing tag works just as well:

<pdf:toc></pdf:toc>

Lists

The type attribute of <ol> and <ul> chooses the counter: 1, a, A, i and I for an ordered list, and circle, disk and square for an unordered one. It is only read when it is written down, and CSS list-style-type is the fuller way to say the same thing. No base-14 font has a hollow circle, so circle draws the same bullet as disk.

Forms

Form controls become PDF form fields:

  • <input type="text">, with its value as the field’s contents

  • <input type="checkbox">

  • <input type="hidden">, a field that holds its value and takes no room

  • <textarea>, with what it holds as the field’s contents

  • <select>, a drop-down of its <option> labels, opening on the one marked selected

Every control needs a name: that is the name of the field. Two limitations worth knowing: an <option> cannot carry a value apart from its label, because a PDF choice field holds one string per option; and <input type="radio"> is drawn but is not a field, as there is no radio group in the PDF library underneath.

<form>
  <input type="text" name="reference" value="MER-4181">
  <textarea name="notes" cols="40" rows="4">Delivered short</textarea>
  <select name="claim">
    <option>Damage</option>
    <option selected="selected">Delay</option>
  </select>
  <input type="hidden" name="form_id" value="MER-CLAIM-2026">
</form>

Demonstration

The layout features this page describes, a page each.

reference.html
<html>
<head>
    <title>Layout reference</title>
    <style>
        @page {
            size: a4 portrait;
            @frame content_frame { left: 50pt; width: 495pt; top: 50pt; height: 740pt; }
        }
        body { font-family: Helvetica; font-size: 11pt; }
        h1 { font-size: 150%; -pdf-outline: true; -pdf-outline-level: 0; }
        h2 { font-size: 118%; -pdf-outline: true; -pdf-outline-level: 1; }
        /* <pdf:nextpage> directly after <pdf:toc /> loses the table of
           contents; the CSS page break does not. */
        .page { page-break-after: always; }
        table.grid { width: 100%; }
        table.grid td, table.grid th {
            border: 1px solid #204a87; padding: 4pt;
        }
        table.grid th { background-color: #d3d7cf; }
        .long td { border: 1px solid #a40000; padding: 4pt; }
    </style>
</head>
<body>
    <div class="page">
    <h1>Table of contents</h1>

    <p>
        Built from the outline below. Every heading in this document declares
        <code>-pdf-outline</code>, so each becomes a bookmark as well as an
        entry here, with the page it landed on.
    </p>

    <pdf:toc />
    </div>

    <div class="page">
    <h1>Tables</h1>

    <h2>Borders, headers and spans</h2>

    <table class="grid" cellspacing="0">
        <tr>
            <th>Feature</th><th>Column A</th><th>Column B</th>
        </tr>
        <tr>
            <td>An ordinary row</td><td>left</td><td>right</td>
        </tr>
        <tr>
            <td colspan="3">One cell spanning all three columns</td>
        </tr>
        <tr>
            <td rowspan="2">Spanning two rows</td><td>first</td><td>row</td>
        </tr>
        <tr>
            <td>second</td><td>row</td>
        </tr>
    </table>

    <h2>Cell widths</h2>

    <table class="grid" cellspacing="0">
        <tr>
            <td width="20%">20%</td>
            <td width="30%">30%</td>
            <td width="50%">50%</td>
        </tr>
    </table>

    <h2>A long cell</h2>

    <table class="long" cellspacing="0">
        <tr>
            <td>
                A cell whose content is longer than one line wraps inside the
                cell rather than pushing the table wider. To PDF or not to PDF,
                that is the question, and the answer keeps this cell busy for
                several lines so the wrapping is visible.
            </td>
        </tr>
    </table>

    </div>

    <div class="page">
    <h1>Images</h1>

    <h2>Size</h2>

    <p>
        <img src="img/denker.png" width="60"> at 60pt wide, and
        <img src="img/denker.png" width="120"> at 120pt.
    </p>

    <h2>Position</h2>

    <p>
        <img src="img/denker.png" width="50" align="left">
        An image aligned left has the text flow past it, which is how a figure
        sits beside its description. The paragraph continues alongside until it
        clears the image.
    </p>

    </div>

    <div>
    <h1>Barcodes</h1>

    <p>Each of these is a <code>&lt;pdf:barcode&gt;</code> with a different type.</p>

    <p>code128</p>
    <p><pdf:barcode value="xhtml2pdf" type="code128" /></p>

    <p>code39</p>
    <p><pdf:barcode value="XHTML2PDF" type="code39" /></p>

    <p>qr &mdash; needs an explicit <code>barwidth</code>, which for a QR code
       is the size of one module; without it the whole symbol comes out too
       small to scan.</p>
    <p><pdf:barcode value="https://xhtml2pdf.readthedocs.io" type="qr"
                    barwidth="15mm" /></p>
    </div>
</body>
</html>