The Printer Side

The printer's default implementation is much thinner, since a printed page has no interaction and therefore needs no buttons, panels or shortcuts. What it mainly provides is a set of typesetting widgets and a configuration, so that output from different renderers agrees on the page.

DefaultPrinterComponent

The component does three things: supplies the printer configuration context, lays down a background sheet carrying the body font, and renders the printer component. Its props are:

PropRole
printerThe printer core
rootThe document tree to render
configA partial printer configuration, only what you want changed
onUpdateCacheReceives the whole cache after each preprocessing pass, which a picker for cross reference targets can be built from

onUpdateCache appeared in tutorial chapter 6, where it logged the cache to the console to confirm that reference names really had been written into it. In a real application its purpose is to supply the editing side with data: a dropdown for choosing which theorem to reference takes its candidates from that cache.

The printer configuration

The printer configuration is also partial-override, in two parts, margins and fonts:

FieldContents
marginsparagraph (between paragraphs), special (around special elements), colon (a small gap), level (the width of one indentation level), structure (horizontal offset of structural text)
fontsbody, title, structure, display, weaken

There are two more fonts than on the editor side: display for displayed text such as a whole quotation, and weaken for de-emphasised text such as the body of a proof. In Chinese typesetting these two normally change typeface outright rather than merely size.

The level entry decides how wide one indentation level is. Several of the widgets below read it, so changing it in one place shifts the indentation of the whole document consistently.

<DefaultPrinterComponent
    printer={printer}
    root={tree}
    config={{
        margins: {paragraph: "0.6rem", level: "2.5rem"},
        fonts  : {display: {fontFamily: "Georgia"}, weaken: {fontSize: "0.95rem"}},
    }}
/>

Typesetting widgets

When writing printer renderers, reach for the widgets below rather than bare div and span. They take their fonts and spacing from the printer configuration, which is what keeps text playing the same role looking the same throughout the document; a bare tag instead hard-codes spacing and fonts into one renderer, where later changes to the configuration cannot reach them.

WidgetPurpose
PrinterPartBoxOne logical block; with subtitle_like it uses the title font
PrinterStructureBoxTextStructural text, such as a "Theorem 1" heading
PrinterParagraphBoxAn ordinary paragraph container with the standard spacing
PrinterDisplayTextDisplayed text, a quotation for instance
PrinterWeakenTextDe-emphasised text, the body of a proof for instance
PrinterNewLevelBoxIndents one level inward
PrinterOldLevelBoxOccupies the width of one indentation level, leaving room on the left for a number
PrinterDividerA divider carrying the configured spacing for special elements
AutoStackA container that arranges its contents in a row or a column depending on them

The last two indentation widgets are a pair, and it takes both to produce the familiar "number on the left, content on the right" arrangement: PrinterNewLevelBox pushes the content in by one level, and PrinterOldLevelBox places the number within the width of that level. Tutorial chapter 6 used exactly this combination when numbering items.

Chapters 4 and 5 of the tutorial were already using these widgets while writing the theorem renderer; they merely looked like ordinary layout components at the time. They can now be understood for what they are: the outlet of the printer configuration inside a renderer.

Renderer factories

The printer side has its own set of renderer factories mirroring the editor's: get_default_group_renderer, get_default_inline_renderer, get_default_structure_renderer, get_default_abstract_renderer, and auto_renderer, which belongs to no particular node type. Their options (outer, inner, pre_element, contexters and the rest) are covered in tutorial chapters 4 and 6, introduced one at a time in the order they become useful, which is easier to follow than a table here, so they are not repeated.

There is also a set of useless_renderer_block, useless_renderer_inline and useless_renderer_text, serving the same purpose as get_default_editors() on the editor side: they fill the default renderer slots of the Printer constructor so that a node with no renderer registered still displays.