Node building

Creating text nodes

What are nodes?

Text data structure

  • Hierarchical structure
func NewText() *Text
type Text struct {
	Settings TypesettingSettings
	Items    []any
}

Text can be a nested structure. Think of

<em>this is <u>emphasized and underlined</u></em>

where you can consider the underlined part as a child of the emphasized part.

ul := NewText()
ul.Settings[]
ul.Items = append(ul.Items,"emphasized and underlined")

em := NewText()
em.Settings...
em.Items = append(em.Items, "this is ")
em.Items = append(em.Items, ul

Format a paragraph

  • font family needed

This breaks a paragraph into lines:

func (fe *Document) FormatParagraph(te *Text, hsize bag.ScaledPoint, opts ...TypesettingOption) (*node.VList, *ParagraphInfo, error)

hsize is the desired horizontal size.

format paragraph calls mknodes

func (fe *Document) Mknodes(ts *Text) (head node.Node, tail node.Node, err error)

which creates a list of nodes from a nested Text data structure.

Typesetting options

The FormatParagraph() method takes a typesetting options as the last argument.

type TypesettingOption func(*Options)
Option Description
Leading(leading bag.ScaledPoint) Set the vertical distance between two baselines
Language(language *lang.Lang) Set the language used for hyphenation
FontSize(size bag.ScaledPoint) Set the font size for the paragraph
IndentLeft(size bag.ScaledPoint, rows int) Set the text indent
HorizontalAlign(a HorizontalAlignment) Sets the horizontal alignment for a paragraph

Text settings

Settings on a Text object control how its content is converted to nodes by Mknodes.

Setting Type Description
SettingFontFamily *FontFamily The font family to use
SettingSize bag.ScaledPoint Font size
SettingColor *color.Color Text color
SettingLeading bag.ScaledPoint Line height (baseline to baseline)
SettingHAlign HorizontalAlignment Horizontal alignment
SettingIndentLeft bag.ScaledPoint Left indentation
SettingIndentLeftRows int Number of rows to indent (0 = all)
SettingIndentLeftSteps []node.IndentStep Left inset that changes from row to row, in place of the two above (see Linebreaking)
SettingIndentRightSteps []node.IndentStep The same for the right inset
SettingLeader string Leader pattern string (see below)
SettingHyperlink document.Hyperlink Link target (URI or local destination)
SettingDest string Named PDF destination at the start of the text, a target for internal links
SettingFontWeight FontWeight or int Font weight, 100 to 900
SettingStyle FontStyle Upright, italic or oblique
SettingLanguage *lang.Lang Hyphenation language of the text
SettingOpenTypeFeature string or []string OpenType features, such as "+smcp,-liga"
SettingLetterSpacing bag.ScaledPoint Extra space after every glyph and every space (CSS letter-spacing)
SettingFontExpansion float64 How far glyphs may be narrowed or widened to even out the spacing of a justified line, 0.05 is 5%; ragged paragraphs are set without it
SettingHorizontalScale float64 Glyphs narrower or wider by a fixed factor, 0.9 is 90%; the line breaker sees the scaled width
SettingHalfLeading bool Leading half above, half below each line, as in CSS; unset puts it below the line, as TeX does
SettingLineModel node.LineModel Line spacing by a model of your own, in place of the two above (see Linebreaking)
SettingBreaker node.Breaker Breaks chosen by a breaker of your own, in place of Knuth-Plass (see Linebreaking)
SettingRecordLineTrims bool Record each line’s trims at the paragraph font’s text edges, for text-box-trim
SettingBackgroundArea BackgroundArea The box an inline background covers: BackgroundAreaEmBox (default) or BackgroundAreaAscentDescent, the content area browsers paint

Leaders via SettingLeader

When a Text item has a SettingLeader value, Mknodes creates a leader glue node instead of normal text. The string value (e.g. ".") is shaped into glyphs, packed into an HList pattern, and attached to a glue node with StretchFilll (order 3). The leader dominates over line-end glue, pushing trailing content to the right margin.

leaderText := frontend.NewText()
leaderText.Settings[frontend.SettingFontFamily] = ff
leaderText.Settings[frontend.SettingSize] = bag.MustSP("10pt")
leaderText.Settings[frontend.SettingLeader] = " . "

pageNum := frontend.NewText()
pageNum.Settings[frontend.SettingFontFamily] = ff
pageNum.Items = append(pageNum.Items, "42")

tocLine := frontend.NewText()
tocLine.Settings[frontend.SettingFontFamily] = ff
tocLine.Items = append(tocLine.Items, "Chapter 1")
tocLine.Items = append(tocLine.Items, leaderText)
tocLine.Items = append(tocLine.Items, pageNum)

vl, _, _ := doc.FormatParagraph(tocLine, hsize,
    frontend.Family(ff),
    frontend.HorizontalAlign(frontend.HAlignLeft))

This produces: Chapter 1 . . . . . . . . . 42 with the dots vertically aligned across lines (global grid).