Documentation page: CSS Multi-column Layout: Newspaper-style Columns in PDF. This page is also available as Markdown at css-multi-column-layout.md.

CSS Multi-column Layout: Newspaper-style Columns in PDF

CSS multi-column layout sets a block of text in columns, the way a newspaper or a magazine sets its articles: the content flows from the bottom of one column to the top of the next, and the columns are balanced to about the same height. PD4ML lays out multi-column content as Chrome prints it — including sections that continue over several pages, headings that span all columns, and boxes that must not be split between columns.

.article {
  column-count: 3;
  column-gap: 7mm;
  column-rule: 1px solid #bbb;
}

Available from PD4ML v4.2.0, with the v42 layout engine: call useHtmlRenderer(Constants.LayoutEngine.v42) before readHTML(). The v40 engine does not lay out multi-column content.

PD4ML pd4ml = new PD4ML();
pd4ml.useHtmlRenderer(Constants.LayoutEngine.v42);
pd4ml.readHTML(new URL("file:report.html"));
pd4ml.writePDF(new FileOutputStream("report.pdf"));

The screenshots on this page are PD4ML output of the examples shown with them. A sample PDF with all of them is available for download.

What is supported

Property Values Notes
column-count a number, auto the number of columns
column-width a length, auto the minimum column width: as many columns as fit (at most column-count, if both are set); the columns may come out wider
columns shorthand columns: 3, columns: 45mm, columns: 45mm 3
column-gap a length, normal the space between columns; normal is 1em
column-rule width, style, color a line in the middle of each gap, between columns that both hold content; also column-rule-width, -style, -color
column-fill balance, auto balance (the default) evens out the column heights; auto fills the columns one after the other (with a fixed height)
column-span all, none an element with all runs across all columns
break-inside avoid on an element inside the columns: it is not split between two columns
orphans, widows a number the least number of a paragraph's lines at the bottom and at the top of a column (2 by default)

Multi-column layout applies to block containers (display: block, list-item, inline-block). Tables, table cells, flex and grid containers are not set in columns themselves, but they can be placed inside columns.

Balanced columns

By default, the columns are balanced: PD4ML finds the smallest column height into which the content fits and fills each column up to it — every column except the last one comes out the same height. A paragraph may continue from one column to the next, but orphans and widows keep at least two of its lines together at the bottom and at the top of a column.

.article {
  column-count: 3;
  column-gap: 7mm;
  column-rule: 1px solid #bbb;
}

Three balanced columns separated by thin vertical rules; the text flows from the bottom of the first column to the top of the second and on to the third

Columns by width

With column-width instead of column-count, the style sheet says how wide a column should be, and PD4ML sets as many such columns as fit the available width. The space that remains is shared among the columns, so they may come out somewhat wider than requested. This keeps a column from getting too narrow to read when the same style sheet is used for different page sizes.

.article {
  column-width: 45mm;   /* as many 45mm columns as fit: 3 here */
  column-gap: 5mm;
}

Three columns of about 45 millimetres each, filling the width of the page

A heading across all columns: column-span

An element with column-span: all runs across the full width of the multi-column container. The content before it is balanced into the columns on its own, and the content after it starts a new set of columns underneath — the way a long article carries an intermediate heading that belongs to the whole text, not to one of its columns.

.article { column-count: 2; column-gap: 8mm }
.article h3 {
  column-span: all;     /* across both columns */
  background: #eef3f8;
  padding: 3pt 5pt;
}

Two balanced columns, a shaded heading across the full width, and two more balanced columns below it

column-span applies to the elements directly inside the multi-column container.

Boxes that are not split: break-inside

Some elements should never be split between two columns: a note, a quotation, a small table or a figure with its caption. With break-inside: avoid, such an element is kept in one column; if it does not fit into the rest of a column, it moves to the top of the next one as a whole. The text around it is still distributed line by line.

.article { column-count: 3; column-gap: 6mm; column-rule: 1px dotted #999 }
.note {
  break-inside: avoid;  /* never split between two columns */
  border: 1px solid #2a5d8f;
  background: #f4f8fc;
  padding: 5pt;
  margin: 0 0 5pt;
}

Three columns with dotted rules; two bordered note boxes, each kept whole within one column

page-break-inside: avoid has the same effect.

Filling the columns one after the other: column-fill

With column-fill: auto and a fixed height, the columns are not balanced: the first column is filled up to the full height, then the second one, and so on. Short content leaves the last columns partly or entirely empty. Without a fixed height, column-fill: auto balances the columns as balance does.

.box {
  column-count: 3;
  column-gap: 6mm;
  column-fill: auto;    /* fill the first column first */
  height: 28mm;
  border: 1px solid #ccc;
  padding: 3mm;
}

A bordered box of fixed height with three columns: the first two are filled to the bottom of the box, the third holds the remaining lines

Columns across pages

A multi-column section longer than what remains of the page continues on the next page, the way it is printed from a browser: on each page, the text fills the first column down to the bottom of the page, then the next column, and only then moves on to the next page. On the last page, the remaining content is balanced, and whatever follows the section starts below its longest column, at full width again.

.report {
  column-count: 2;
  column-gap: 7mm;
  column-rule: 1px solid #ccc;
}

Two pages side by side: on the first, the left column and then the right column are filled to the bottom of the page; on the second, the rest of the text is balanced over both columns and a full-width line follows

A section that starts near the bottom of a page begins there, in the room that is left: its heading is not left alone at the bottom of the page. A box with break-inside: avoid that does not fit the rest of a column on one page moves to the next column, or to the next page.

Limitations

  • Column breaks: break-before: column and break-after: column are not supported; a column break falls where the content reaches the end of a column.
  • Columns of different widths are not part of CSS multi-column layout; use a grid or flex layout instead, where each column is an element of its own.
  • Page headers, footers and footnotes: with <pd4ml:page.header>/<pd4ml:page.footer> or footnotes on the same pages, the columns of a multi-column section that continues over several pages may end a little above or below the actual page bottom.
  • Named pages and page-width changes: in a document whose pages change their width (named @page pages of different sizes, <pd4ml:page.break> with a new width), a multi-column section is balanced over its whole content and not continued page by page.
  • Nested multi-column containers are laid out, but only the outermost one continues page by page.