Documentation

Columns since beta.5

Text that runs down one column and continues in the next, then on the next page. SplitView is the ready-made newspaper. Flow and Region build any other layout: a title area, a sidebar, columns of different widths.

Everything inside paginates as it does on a page. A paragraph splits at its lines, a table repeats its header, keepTogether holds, orphans and widows count.

Experimental: the shape of these components may still change before 1.0.

Newspaper columns

import { Document, Page, Text, SplitView } from "@jasy/pdf";

Document([
  Page({ size: "A4", margin: 56, gap: 12 }, [
    Text("The Valley Courier", { size: 28, bold: true, color: "#1450aa" }),
    SplitView({ columns: 3, colGap: 16, rowGap: 8 }, paragraphs),
    Text("This line follows the columns, right where they end.", { italic: true }),
  ]),
]);

The content fills the first column, then the next, then the next page. On its last page the columns balance: they come out about equally tall, as CSS columns do, and what follows sits right below them.

OptionDefaultWhat it does
columns2how many columns, side by side and equally wide
gap0space between the columns and between the content's elements
colGapgapspace between the columns
rowGapgapspace between the content's elements
balancetrueequal columns on the last page; false fills one column, then the next

Breaking to the next column

ColumnBreak() starts the next column. From the last column, it starts the first one on the next page.

SplitView({ columns: 2, gap: 16 }, [
  Text("Summary", { bold: true }),
  Text(summary),
  ColumnBreak(),
  Text("Details", { bold: true }),
  Text(details),
]);

A ColumnBreak outside a column (in a Row, a table row, or no column at all) does nothing, and reports it once as column-break-ignored. To start a new page, use PageBreak.

A layout of your own

Flow takes a template: a function that builds one page of the layout. Put a Region wherever the content should go. The content fills the Regions in the order they appear, then continues on the next page with a fresh template.

import { Document, Page, Column, Row, Box, Text, Expanded, Flow, Region } from "@jasy/pdf";

Document([
  Page({ size: "A4", margin: 56 }, [
    Flow(
      {
        gap: 8,
        template: ({ index }) =>
          Column({ gap: 12 }, [
            index === 0
              ? Text("Annual Report", { size: 22, bold: true, color: "#1450aa" })
              : Text("Annual Report, continued", { size: 9, italic: true, color: "gray" }),
            Row({ gap: 16 }, [
              Expanded(Region()),
              Box({ bg: "#f3f5f9", padding: 8 }, [Region({ width: 150 })]),
            ]),
          ]),
      },
      report,
    ),
  ]),
]);

The template gets { index, pageNumber }: index counts the pages of this Flow from 0, so the first page can carry the title and the others a running head. A ColumnBreak in the content moves on to the sidebar.

A Region is sized by the layout around it, like any element: Expanded, a width or height in points or %, min and max. With nothing given, it fills what it is offered.

Flow optionDefaultWhat it does
template-({ index, pageNumber }) => element, built for every page
gap0space between the content's elements
balancefalseon the last page, spread the content so the Regions end about equal

A SplitView is exactly this: a Flow whose template is a Row of Expanded(Region()).

Where a Flow gets its height

A Flow has no height of its own. It takes the rest of the page, or the height of the box it sits in. On its last page it shrinks to what the content used, so whatever follows sits right below.

In a box with a fixed height, the Flow cannot continue on the next page. What does not fit is cut off and handled by onOverflow, like any other block that is too tall.

What jasy refuses

A mistake in a layout like this would otherwise lose content without a word. Each one throws a named error that says what to change:

ErrorWhen
FlowWithoutRegionErrorthe template holds no Region, so the content has nowhere to go
RegionOutsideFlowErrora Region in the content, or anywhere outside a template
RegionReusedErrorone Region on two pages - build the Regions inside the template function
FlowUnboundedErrorthe Flow has no height to take, e.g. in a Row without a height
FlowRegionsEmptyErrorevery Region ends up 0 wide or 0 tall
RegionInPageBuilderErrora Region inside a PageBuilder, which builds again for every page

In Vue, the template is the #page slot - see Components. In React, it is the page render prop - see Components.