Documentation
¶
Overview ¶
Package layout is a pure computation package implementing the GxPDF layout engine. It has zero imports from creator/ or any PDF-specific package, enabling testing of layout logic without PDF generation and supporting swappable rendering backends.
The core concept is the Element interface: each element receives an Area describing available space and returns an immutable Plan describing what fits, how much space was consumed, and an Overflow element carrying any remainder for the next page.
Index ¶
- Constants
- Variables
- func ResolvePageNumbers(pages []PageLayout)
- type Align
- type Area
- type Block
- type BorderEdges
- type BorderSide
- type Box
- type Color
- type ColumnDef
- type Direction
- type Edges
- type Element
- type FontRef
- type FontResolver
- type FontStyle
- type FontWeight
- type LinkArea
- type Measurable
- type MockFontResolver
- func (m *MockFontResolver) Ascender(_ FontRef, size float64) float64
- func (m *MockFontResolver) Descender(_ FontRef, size float64) float64
- func (m *MockFontResolver) LineBreak(font FontRef, text string, size float64, maxWidth float64) []string
- func (m *MockFontResolver) LineHeight(_ FontRef, size float64) float64
- func (m *MockFontResolver) MeasureString(_ FontRef, text string, size float64) float64
- type PageDef
- type PageLayout
- type PageNumber
- type Paginator
- type Plan
- type Renderer
- type ResolvedEdges
- type RichText
- type RichTextFragment
- type Size
- type Status
- type Style
- type Table
- type TableCell
- type TableRow
- type Text
- type TextDrawOptions
- type Unit
- type VAlign
- type Value
Constants ¶
const PageNumberPlaceholder = "\x00PAGE\x00"
PageNumberPlaceholder is injected into text content where the current page number should appear. It is replaced in a second pass after all pages are known.
const TotalPagesPlaceholder = "\x00TOTAL\x00"
TotalPagesPlaceholder is injected into text content where the total page count should appear.
Variables ¶
var ( // PageA4 is the ISO A4 page size (210mm × 297mm). PageA4 = Size{Width: 595.276, Height: 841.890} // PageA3 is the ISO A3 page size (297mm × 420mm). PageA3 = Size{Width: 841.890, Height: 1190.551} // PageLetter is the US Letter page size (8.5in × 11in). PageLetter = Size{Width: 612, Height: 792} // PageLegal is the US Legal page size (8.5in × 14in). PageLegal = Size{Width: 612, Height: 1008} )
Standard page sizes in PDF points (width × height for portrait orientation).
var Black = Color{R: 0, G: 0, B: 0}
Black is the standard black color (0, 0, 0).
var White = Color{R: 1, G: 1, B: 1}
White is the standard white color (1, 1, 1).
Functions ¶
func ResolvePageNumbers ¶
func ResolvePageNumbers(pages []PageLayout)
ResolvePageNumbers performs a second pass over all pages, replacing PageNumberPlaceholder and TotalPagesPlaceholder strings inside Draw closures with the actual page number and total page count.
Because Draw closures are opaque functions, page number injection is implemented by wrapping Draw closures in Text blocks that contain the placeholder strings. The paginator substitutes those strings when the Text element is created with PageNumberPlaceholder or TotalPagesPlaceholder as content.
This function walks all blocks recursively and replaces placeholder content in any TextBlock created by the text layout.
Types ¶
type Align ¶
type Align int
Align specifies horizontal text alignment.
const ( // AlignLeft aligns text to the left edge (default). AlignLeft Align = iota // AlignCenter centers text within the available width. AlignCenter // AlignRight aligns text to the right edge. AlignRight // AlignJustify distributes words evenly across the full width. // The last line of a paragraph uses left alignment. AlignJustify )
type Area ¶
type Area struct {
// Width is the available horizontal space in PDF points.
Width float64
// Height is the remaining vertical space on the current page in PDF points.
Height float64
}
Area represents available space for a layout computation.
type Block ¶
type Block struct {
// X and Y are the position of this block within its parent coordinate space,
// measured in PDF points from the top-left corner.
X, Y float64
// Width and Height are the block's dimensions in PDF points.
Width, Height float64
// Draw is the rendering closure. It captures all layout data needed to
// emit PDF content and calls Renderer methods when invoked.
Draw func(r Renderer)
// Children holds nested blocks, enabling a recursive block tree for
// PDF/UA tagged PDF structure (P, H1, Table, etc.).
Children []Block
// Tag is the PDF structure element tag (e.g. "P", "H1", "Table", "TD").
// Empty string means untagged.
Tag string
// AltText is the accessibility alternative text for images and figures.
AltText string
// Links contains clickable regions within this block.
Links []LinkArea
// contains filtered or unexported fields
}
Block is a positioned piece of content ready for rendering. It is the atomic rendering unit produced by layout and consumed by the renderer.
type BorderEdges ¶
type BorderEdges struct {
// Top is the top border.
Top BorderSide
// Right is the right border.
Right BorderSide
// Bottom is the bottom border.
Bottom BorderSide
// Left is the left border.
Left BorderSide
}
BorderEdges groups the four border sides of a box.
type BorderSide ¶
type BorderSide struct {
// Width is the border line width in PDF points.
Width float64
// Color is the border line color.
Color Color
}
BorderSide describes a single border edge.
type Box ¶
type Box struct {
// Children are the contained elements laid out according to Direction.
Children []Element
// Direction controls whether children stack vertically or horizontally.
Direction Direction
// Style contains typographic and box-model properties.
Style Style
// Width is the explicit width of the box. Auto means use available width.
Width Value
// Height is the explicit height of the box. Auto means content-driven.
Height Value
}
Box is the universal container element. It supports two layout modes:
- Vertical (default): children are stacked top-to-bottom (CSS block layout)
- Horizontal: children are placed left-to-right (CSS row/inline layout)
Box respects margin, padding, and border from its Style. Background and border drawing closures are injected into the Block tree so that the renderer can emit them without the layout engine knowing about PDF.
func (*Box) PlanLayout ¶
PlanLayout implements Element. It resolves box-model spacing, delegates to vertical or horizontal layout, and returns a Plan with positioned Blocks and an optional Overflow element.
type Color ¶
type Color struct {
// R is the red component in [0, 1].
R float64
// G is the green component in [0, 1].
G float64
// B is the blue component in [0, 1].
B float64
}
Color represents an RGB color with components in the [0, 1] range.
type ColumnDef ¶
type ColumnDef struct {
// Width specifies the column width using Value units.
// UnitAuto means the column sizes itself to its content.
// UnitFr distributes the remaining space fractionally.
Width Value
}
ColumnDef describes how a single column's width is determined.
type Edges ¶
type Edges struct {
// Top is the top edge value.
Top Value
// Right is the right edge value.
Right Value
// Bottom is the bottom edge value.
Bottom Value
// Left is the left edge value.
Left Value
}
Edges represents four-sided spacing (margin, padding, etc.) where each side is an independent Value.
func UniformEdges ¶
UniformEdges constructs Edges with the same value on all four sides.
func (Edges) Resolve ¶
func (e Edges) Resolve(parentW, parentH, fontSize float64) ResolvedEdges
Resolve converts all edges to PDF points given the parent width, parent height, and font size. Horizontal edges (Left, Right) are resolved against parentW; vertical edges (Top, Bottom) are resolved against parentH.
type Element ¶
Element is the core layout interface. Every layoutable type implements it. PlanLayout must be pure — no side effects, no mutation of the receiver. The caller supplies the available Area; the returned Plan describes what was placed and what (if anything) could not fit.
type FontRef ¶
type FontRef struct {
// Family is the font family name (e.g. "Helvetica", "Inter", "Times New Roman").
Family string
// Weight selects the font weight within the family.
Weight FontWeight
// Style selects the font style (normal or italic/oblique).
Style FontStyle
}
FontRef identifies a font by its typographic attributes. It is used as a key when looking up metrics from the FontResolver.
func DefaultFont ¶
func DefaultFont() FontRef
DefaultFont returns a FontRef for the default document font (Helvetica, normal weight and style).
type FontResolver ¶
type FontResolver interface {
// MeasureString returns the width of text rendered at the given font and size,
// in PDF points. It must NOT include trailing whitespace width.
MeasureString(font FontRef, text string, size float64) float64
// LineHeight returns the total line height (ascender + descender + leading)
// for the given font and size, in PDF points.
LineHeight(font FontRef, size float64) float64
// Ascender returns the ascender height above the baseline for the given
// font and size, in PDF points.
Ascender(font FontRef, size float64) float64
// Descender returns the magnitude of the descender below the baseline
// for the given font and size, in PDF points. Always a positive value.
Descender(font FontRef, size float64) float64
// LineBreak splits text into lines that each fit within maxWidth points
// when rendered at the given font and size. It wraps at word boundaries
// where possible; it may split mid-word only when a single word exceeds
// maxWidth. Returns at least one element even for empty text.
LineBreak(font FontRef, text string, size float64, maxWidth float64) []string
}
FontResolver abstracts font measurement from the layout engine. Layout never imports creator/ or any PDF font package — it always goes through this interface. The concrete implementation in builder/internal bridges to the creator/ font subsystem.
type FontWeight ¶
type FontWeight int
FontWeight represents the weight (boldness) of a font.
const ( // WeightNormal is the standard weight (400). WeightNormal FontWeight = 400 // WeightBold is the bold weight (700). WeightBold FontWeight = 700 )
type LinkArea ¶
type LinkArea struct {
// X and Y are the offset from the parent Block's top-left corner.
X, Y float64
// Width and Height define the clickable region.
Width, Height float64
// URL is the target of the hyperlink.
URL string
}
LinkArea defines a hyperlink region within a Block.
type Measurable ¶
type Measurable interface {
// MinWidth returns the minimum width the element can occupy without
// losing content (e.g. width of the longest unbreakable word).
MinWidth() float64
// MaxWidth returns the width the element would occupy if given
// unlimited horizontal space (e.g. full text on one line).
MaxWidth() float64
}
Measurable is an optional interface that elements may implement to report their intrinsic width range. This is used by table column auto-sizing and flow layout algorithms.
type MockFontResolver ¶
type MockFontResolver struct{}
MockFontResolver is a deterministic font resolver for use in tests. It approximates character width as 0.5 * fontSize and line height as 1.2 * fontSize. All methods are safe for concurrent use.
func (*MockFontResolver) Ascender ¶
func (m *MockFontResolver) Ascender(_ FontRef, size float64) float64
Ascender returns 0.8 * fontSize as the ascender height.
func (*MockFontResolver) Descender ¶
func (m *MockFontResolver) Descender(_ FontRef, size float64) float64
Descender returns 0.2 * fontSize as the descender magnitude.
func (*MockFontResolver) LineBreak ¶
func (m *MockFontResolver) LineBreak(font FontRef, text string, size float64, maxWidth float64) []string
LineBreak splits text into lines that fit within maxWidth using the approximate 0.5 * fontSize per character metric.
func (*MockFontResolver) LineHeight ¶
func (m *MockFontResolver) LineHeight(_ FontRef, size float64) float64
LineHeight returns 1.2 * fontSize as the total line height.
func (*MockFontResolver) MeasureString ¶
func (m *MockFontResolver) MeasureString(_ FontRef, text string, size float64) float64
MeasureString returns an approximate width using 0.5 * fontSize per rune.
type PageDef ¶
type PageDef struct {
// Size is the physical page dimensions in PDF points.
Size Size
// Margins is the spacing between the page edge and the content area.
Margins Edges
// Header contains elements rendered at the top of every page.
// The header is laid out with unlimited height to measure it, then
// subtracted from the body area.
Header []Element
Footer []Element
// Content contains the main body elements to paginate.
Content []Element
}
PageDef describes a page template: its physical size, margins, and the content elements to be rendered on each page of the document.
type PageLayout ¶
type PageLayout struct {
// Size is the physical page dimensions in PDF points.
Size Size
// Blocks contains all content blocks for this page, with coordinates
// measured from the top-left corner of the page.
Blocks []Block
}
PageLayout holds all positioned blocks for a single rendered page, ready to be handed to the renderer.
type PageNumber ¶
type PageNumber struct {
// Format is the format string. Use PageNumberPlaceholder and
// TotalPagesPlaceholder as substitution markers.
// Example: PageNumberPlaceholder + " / " + TotalPagesPlaceholder
Format string
// Style controls the appearance of the page number text.
Style Style
// Fonts is the font resolver.
Fonts FontResolver
}
PageNumber is a special Element that renders the current page number. It uses a two-pass approach: on the first pass it inserts a placeholder that the paginator replaces with the actual page number after all pages are known.
func (*PageNumber) PlanLayout ¶
func (pn *PageNumber) PlanLayout(area Area) Plan
PlanLayout implements Element. It creates blocks tagged with "__pagenumber__" whose Draw closures are rebuilt by ResolvePageNumbers after pagination.
type Paginator ¶
type Paginator struct {
// Fonts is the font resolver used by all layout operations.
// If nil, MockFontResolver is used (suitable for tests).
Fonts FontResolver
}
Paginator runs the layout engine across a sequence of PageDefs and produces a slice of PageLayout values — one per physical output page. Content that does not fit on a page overflows automatically to the next page.
func (*Paginator) Paginate ¶
func (p *Paginator) Paginate(pages []*PageDef) []PageLayout
Paginate processes the given page definitions and returns one PageLayout per physical output page. The algorithm:
- For each PageDef, resolve margins.
- Layout header and footer at unlimited height to measure their heights.
- Compute bodyHeight = pageHeight - margins - headerH - footerH.
- Iterate content elements, calling PlanLayout on each.
- Full → accumulate blocks, advance cursor.
- Partial → flush page, push Overflow to next page.
- Nothing at page top → force layout with unlimited height (oversized element).
- Nothing mid-page → flush page, retry on fresh page.
- After all pages are generated, resolve page number placeholders.
type Plan ¶
type Plan struct {
// Status indicates how much content fit in the available area.
Status Status
// Consumed is the vertical space used by this plan in PDF points.
Consumed float64
// Blocks holds the positioned, ready-to-render content atoms.
Blocks []Block
// Overflow carries the portion of the element that did not fit.
// It is nil when Status == Full.
Overflow Element
}
Plan is the immutable result of a layout computation.
type Renderer ¶
type Renderer interface {
// DrawText renders a text string at the given position using the
// specified font, size, and color with optional decoration options.
DrawText(text string, x, y float64, font FontRef, size float64, color Color, options TextDrawOptions)
// DrawRect renders a filled and/or stroked rectangle.
DrawRect(x, y, width, height float64, fill *Color, stroke *Color, strokeWidth float64)
// DrawLine renders a line segment.
DrawLine(x1, y1, x2, y2 float64, color Color, width float64)
// DrawImage renders image data (PNG, JPEG) scaled to the given bounds.
DrawImage(data []byte, x, y, width, height float64)
// PushState saves the current graphics state.
PushState()
// PopState restores the most recently saved graphics state.
PopState()
// SetClipRect defines a rectangular clipping region.
SetClipRect(x, y, width, height float64)
}
Renderer is an abstract PDF rendering target. Layout closures call these methods to emit content without importing any PDF-specific package. The concrete implementation in builder/internal bridges to creator/.
type ResolvedEdges ¶
type ResolvedEdges struct {
// Top is the top edge in points.
Top float64
// Right is the right edge in points.
Right float64
// Bottom is the bottom edge in points.
Bottom float64
// Left is the left edge in points.
Left float64
}
ResolvedEdges holds four edge values already converted to PDF points.
func (ResolvedEdges) Horizontal ¶
func (r ResolvedEdges) Horizontal() float64
Horizontal returns the sum of the Left and Right edges.
func (ResolvedEdges) Vertical ¶
func (r ResolvedEdges) Vertical() float64
Vertical returns the sum of the Top and Bottom edges.
type RichText ¶
type RichText struct {
// Fragments is the ordered sequence of inline text segments. Each fragment
// carries its own style (font, size, color, decoration). Together they form
// a single paragraph.
Fragments []RichTextFragment
// Align controls horizontal alignment for every line in the paragraph.
Align Align
// LineHeight is a multiplier applied to the tallest font size on each line
// to compute the line's vertical extent. Defaults to 1.2 when zero.
LineHeight float64
// Fonts is the font resolver used for measurement. When nil, the internal
// mock approximation (0.5 * fontSize per character) is used.
Fonts FontResolver
}
RichText is a multi-style inline text element. It lays out a sequence of styled fragments as a single paragraph with shared word-wrapping. Fragments with different font sizes share a common baseline within each line: smaller runs are shifted down so their baseline aligns with the tallest run's baseline.
RichText implements both Element and Measurable.
func (*RichText) MaxWidth ¶
MaxWidth implements Measurable. It returns the width of all fragments rendered on a single line without wrapping.
func (*RichText) MinWidth ¶
MinWidth implements Measurable. It returns the width of the widest single word across all fragments, which is the minimum width the element can occupy without losing content.
func (*RichText) PlanLayout ¶
PlanLayout implements Element. It splits fragments into word-level runs, fills lines greedily, and returns positioned Blocks. Each Block corresponds to one wrapped line and contains a Draw closure that calls r.DrawText for every run on that line.
If not all lines fit vertically, the remaining content is returned as a new RichText in Plan.Overflow.
type RichTextFragment ¶
type RichTextFragment struct {
// Text is the content of this fragment.
Text string
// Style controls the typographic and decorative properties of this fragment.
// FontSize, Font, Color, Bold, Italic, Underline, and Strikethrough are all
// honored at the fragment level.
Style Style
// URL, when non-empty, makes the fragment a hyperlink. The renderer will
// record this as a LinkArea on the containing Block.
URL string
}
RichTextFragment is one styled segment within a RichText paragraph. Multiple fragments are laid out inline; word-wrap boundaries may fall inside or between fragments.
type Status ¶
type Status int
Status indicates how much content fit in the available area during layout.
type Style ¶
type Style struct {
// Font identifies the typeface to use for text within this element.
Font FontRef
// FontSize is the text size in PDF points. Defaults to 12 when zero.
FontSize float64
// Color is the foreground text color.
Color Color
// Background is the optional fill color for the element's background.
// Nil means transparent.
Background *Color
// TextAlign controls horizontal text alignment within the element.
TextAlign Align
// LineHeight is a multiplier applied to FontSize to compute inter-line
// spacing. A value of 1.2 (20% leading) is the default.
LineHeight float64
// LetterSpacing adds extra spacing between characters in PDF points.
LetterSpacing float64
// Margin is the space outside the border, separating this element from
// adjacent elements.
Margin Edges
// Padding is the space inside the border, between the border and content.
Padding Edges
// Border defines the four border sides.
Border BorderEdges
// Bold applies bold weight to the font. Overrides Font.Weight.
Bold bool
// Italic applies italic style to the font. Overrides Font.Style.
Italic bool
// Underline adds an underline decoration to text.
Underline bool
// Strikethrough adds a strikethrough decoration to text.
Strikethrough bool
// KeepWithNext prevents a page break between this element and the next
// sibling element.
KeepWithNext bool
// KeepTogether prevents this element from being split across pages.
// If the element does not fit on the current page, it is pushed to the
// next page as a whole. If it does not fit on a fresh page either, it
// is placed anyway (forced layout).
KeepTogether bool
// VerticalAlign controls how content is aligned vertically within its
// container (applies to table cells and columns).
VerticalAlign VAlign
}
Style is the complete set of visual and layout attributes that can be applied to any Element. Zero values produce sensible defaults.
func DefaultStyle ¶
func DefaultStyle() Style
DefaultStyle returns a Style with sensible defaults applied: 12pt Helvetica, black color, 1.2 line height, left alignment.
type Table ¶
type Table struct {
// Columns defines the width specification for each column.
// The number of entries here determines the column count.
Columns []ColumnDef
// Header rows are placed at the top of every page (repeated on overflow).
Header []TableRow
// Body rows form the main content; they split across pages.
Body []TableRow
Footer []TableRow
// Style is applied to the whole table (background, border).
Style Style
// Fonts is the font resolver used by cell content measurement.
// If nil, the MockFontResolver approximation is used.
Fonts FontResolver
}
Table is a layout element that renders a structured grid of cells with optional repeating header and footer rows and page-split support.
Column widths are resolved using a four-pass algorithm:
- Fixed (UnitPt/Mm/Cm/In): resolved to points directly
- Pct (UnitPct): resolved as a percentage of the available table width
- Fr (UnitFr): fractional share of remaining space after fixed/pct columns
- Auto (UnitAuto): intrinsic content width via the Measurable interface
Table implements Element and Measurable.
func (*Table) MaxWidth ¶
MaxWidth implements Measurable. It returns the sum of each column's maximum content width (full content without wrapping) across all cells.
func (*Table) MinWidth ¶
MinWidth implements Measurable. It returns the sum of each column's minimum content width (longest unbreakable word) across all cells.
func (*Table) PlanLayout ¶
PlanLayout implements Element. It resolves column widths, builds the cell grid, measures row heights, places header and footer rows on every page, and splits body rows across pages when needed.
type TableCell ¶
type TableCell struct {
// Content is the list of elements rendered inside the cell.
// Cells support any Element (Text, nested Table, Image, etc.).
Content []Element
// ColSpan is the number of columns this cell spans. 1 is normal.
ColSpan int
// RowSpan is the number of rows this cell spans. 1 is normal.
RowSpan int
// Style provides cell-level styling: padding, border, background, vertical align.
Style Style
}
TableCell is a single cell in a TableRow.
type TableRow ¶
type TableRow struct {
// Cells is the ordered list of cells in this row.
Cells []TableCell
// Style applies row-level styling (background color).
Style Style
}
TableRow is a single row in a table section (header, body, or footer).
type Text ¶
type Text struct {
// Content is the text string to lay out. Newlines are treated as hard breaks.
Content string
// Style controls typography, alignment, and box-model properties.
Style Style
// Fonts is the font resolver used for measurement and line breaking.
// If nil, an internal approximation is used.
Fonts FontResolver
}
Text is a single-style text element that supports word wrapping and justified alignment. It implements both Element and Measurable.
The FontResolver is required for accurate width measurement. When nil, the element falls back to an approximate 0.5*fontSize per character.
func (*Text) MaxWidth ¶
MaxWidth implements Measurable. It returns the width of the full content rendered on a single line.
func (*Text) MinWidth ¶
MinWidth implements Measurable. It returns the width of the longest unbreakable word in the content.
func (*Text) PlanLayout ¶
PlanLayout implements Element. It breaks Content into wrapped lines, positions each line according to alignment, and returns a Plan. If not all lines fit vertically, the remaining content is returned as Overflow.
type TextDrawOptions ¶
type TextDrawOptions struct {
// LetterSpacing adds extra spacing between each character in points.
LetterSpacing float64
// WordSpacing adds extra spacing between words in points (used for justification).
WordSpacing float64
// Underline requests an underline decoration.
Underline bool
// Strikethrough requests a strikethrough decoration.
Strikethrough bool
}
TextDrawOptions carries optional text decoration parameters for DrawText.
type Unit ¶
type Unit int
Unit identifies the measurement unit of a Value.
const ( // UnitPt represents PDF points (1/72 inch). The native unit for PDF. UnitPt Unit = iota // UnitMm represents millimeters. UnitMm // UnitCm represents centimeters. UnitCm // UnitIn represents inches. UnitIn // UnitPct represents a percentage of the parent dimension. UnitPct // UnitFr represents a fractional share (for table/grid column sizing). UnitFr // UnitAuto indicates content-driven sizing. UnitAuto )
type Value ¶
type Value struct {
// Amount is the numeric quantity in the specified Unit.
Amount float64
// Unit specifies how Amount should be interpreted.
Unit Unit
}
Value is a dimensional value with an associated unit. Use the constructor functions (Pt, Mm, Cm, In, Pct, Fr) to create values.
func Auto ¶
func Auto() Value
Auto returns the zero Value with UnitAuto, requesting content-driven sizing.