pagelayout

Paginated document layout engine: format-neutral page-model IR with SVG/PDF backends.

layout
pagination
docx
svg
pdf
typesetting
Download zip
Author
Version
0.1.1
License
Apache-2.0
Last updated
11 hours ago
Downloads
13

#pagelayout

Paginated document layout engine. Frontends (DOCX first) produce engine input; the engine measures, line-breaks, and paginates into a format-neutral page-model IR — pages of positioned glyph runs, rectangles, images, and link regions; backends (SVG first, then PDF via pdflite) transcribe the IR. Design record: docs/page-layout-engine.md at the repository root.

Coordinates are points, y-down, origin at the page's top-left; text is positioned by baseline, and glyph runs carry their own advances so backends never re-measure.

///|
test "build a one-page model" {
let model = @pagelayout.PageModel::new()
let font = model.add_font({ family: "Carlito", bold: false, italic: false, })
let page = @pagelayout.Page::{ width_pt: 612, height_pt: 792, items: [], }
page.items.push(
Text({
font,
size_pt: 12,
x_pt: 72,
baseline_pt: 82.5,
text: "Hi",
advances_pt: [6.5, 6.5],
color: @pagelayout.black,
}),
)
model.pages.push(page)
inspect(model.pages.length(), content="1")
}

OOXML quantities stay in typed units until the layout boundary:

///|
test "unit conversions" {
inspect(@pagelayout.Twips(1440).to_pt(), content="72")
inspect(@pagelayout.HalfPoints(24).to_pt(), content="12")
inspect(@pagelayout.Emu(914400).to_pt(), content="72")
inspect(@pagelayout.EighthPoints(4).to_pt(), content="0.5")
}

Color

pub(all) struct Color {
r : Int
g : Int
b : Int
} derive(Eq, ToJson,
Debug
)

Solid sRGB color; each channel is 0..=255 (checked by PageModel::validate, clamped on entry by Color::rgb). No alpha in v1: solid colors keep the SVG output inside svgdiff's deterministic subset and match common document text.

Color::equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn Color::equal(Color, Color) -> Bool

Color::not_equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn Color::not_equal(x : Color, y : Color) -> Bool

Color::rgb

fn Color::rgb(r : Int, g : Int, b : Int) -> Color

Build a color, clamping each channel into 0..=255. Frontends convert untrusted document values through this so every encoding of the model (Eq, JSON, hex) agrees.

Color::to_hex

fn Color::to_hex(self : Color) -> String

Lowercase #rrggbb, the form both the SVG emitter and tests use. Channels are formatted modulo the valid range (see Color::rgb).

Color::to_json

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn Color::to_json(Color) -> Json

Color::to_repr

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn Color::to_repr(Color) ->
Repr

EighthPoints

pub(all) struct EighthPoints(Int) derive(Eq, ToJson,
Debug
)

Eighths of a point — OOXML's unit for border widths (w:sz on borders).

EighthPoints::equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn EighthPoints::equal(EighthPoints, EighthPoints) -> Bool

EighthPoints::not_equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn EighthPoints::not_equal(x : EighthPoints, y : EighthPoints) -> Bool

EighthPoints::to_json

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn EighthPoints::to_json(EighthPoints) -> Json

EighthPoints::to_pt

fn EighthPoints::to_pt(self : EighthPoints) -> Double

Convert to points (8 eighth-points per point).

EighthPoints::to_repr

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn EighthPoints::to_repr(EighthPoints) ->
Repr

Emu

pub(all) struct Emu(Int) derive(Eq, ToJson,
Debug
)

English Metric Units — OOXML's unit for drawing/image extents (914400 per inch).

Emu::equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn Emu::equal(Emu, Emu) -> Bool

Emu::not_equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn Emu::not_equal(x : Emu, y : Emu) -> Bool

Emu::to_json

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn Emu::to_json(Emu) -> Json

Emu::to_pt

fn Emu::to_pt(self : Emu) -> Double

Convert to points (12700 EMU per point).

Emu::to_repr

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn Emu::to_repr(Emu) ->
Repr

FontSpec

pub(all) struct FontSpec {
family : String
bold : Bool
italic : Bool
} derive(Eq, ToJson,
Debug
)

A font face a glyph run references, by index into PageModel::fonts. family is the resolved (post font-mapping) family name; bold and italic select the face within it.

FontSpec::equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn FontSpec::equal(FontSpec, FontSpec) -> Bool

FontSpec::not_equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn FontSpec::not_equal(x : FontSpec, y : FontSpec) -> Bool

FontSpec::to_json

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn FontSpec::to_json(FontSpec) -> Json

FontSpec::to_repr

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn FontSpec::to_repr(FontSpec) ->
Repr

GlyphRun

pub(all) struct GlyphRun {
font : Int
size_pt : Double
x_pt : Double
baseline_pt : Double
text : String
advances_pt : Array[Double]
color : Color
} derive(Eq, ToJson,
Debug
)

A run of text on one baseline in one font/size/color. advances_pt[i] is the horizontal advance of the char at index i of text (UTF-16 code units; the trailing surrogate of a pair carries advance 0).

GlyphRun::equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn GlyphRun::equal(GlyphRun, GlyphRun) -> Bool

GlyphRun::not_equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn GlyphRun::not_equal(x : GlyphRun, y : GlyphRun) -> Bool

GlyphRun::to_json

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn GlyphRun::to_json(GlyphRun) -> Json

GlyphRun::to_repr

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn GlyphRun::to_repr(GlyphRun) ->
Repr

GlyphRun::width_pt

fn GlyphRun::width_pt(self : GlyphRun) -> Double

Total advance of the run: where the next run on the same baseline starts.

HalfPoints

pub(all) struct HalfPoints(Int) derive(Eq, ToJson,
Debug
)

Half-points — OOXML's unit for font sizes (w:sz).

HalfPoints::equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn HalfPoints::equal(HalfPoints, HalfPoints) -> Bool

HalfPoints::not_equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn HalfPoints::not_equal(x : HalfPoints, y : HalfPoints) -> Bool

HalfPoints::to_json

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn HalfPoints::to_json(HalfPoints) -> Json

HalfPoints::to_pt

fn HalfPoints::to_pt(self : HalfPoints) -> Double

Convert to points (2 half-points per point).

HalfPoints::to_repr

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn HalfPoints::to_repr(HalfPoints) ->
Repr

ImageItem

pub(all) struct ImageItem {
x_pt : Double
y_pt : Double
w_pt : Double
h_pt : Double
data : Bytes
mime : String
} derive(Eq, ToJson,
Debug
)

A placed raster image; data is the encoded file, not pixels.

ImageItem::equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn ImageItem::equal(ImageItem, ImageItem) -> Bool

ImageItem::not_equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn ImageItem::not_equal(x : ImageItem, y : ImageItem) -> Bool

ImageItem::to_json

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn ImageItem::to_json(ImageItem) -> Json

ImageItem::to_repr

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn ImageItem::to_repr(ImageItem) ->
Repr

LinkRegion

pub(all) struct LinkRegion {
x_pt : Double
y_pt : Double
w_pt : Double
h_pt : Double
target : String
} derive(Eq, ToJson,
Debug
)

An interactive region (hyperlink); geometry only, painted by nothing.

LinkRegion::equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn LinkRegion::equal(LinkRegion, LinkRegion) -> Bool

LinkRegion::not_equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn LinkRegion::not_equal(x : LinkRegion, y : LinkRegion) -> Bool

LinkRegion::to_json

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn LinkRegion::to_json(LinkRegion) -> Json

LinkRegion::to_repr

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn LinkRegion::to_repr(LinkRegion) ->
Repr

Page

pub(all) struct Page {
width_pt : Double
height_pt : Double
items : Array[PageItem]
} derive(Eq, ToJson,
Debug
)

A single laid-out page: physical size plus its positioned items.

Page::equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn Page::equal(Page, Page) -> Bool

Page::not_equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn Page::not_equal(x : Page, y : Page) -> Bool

Page::to_json

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn Page::to_json(Page) -> Json

Page::to_repr

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn Page::to_repr(Page) ->
Repr

PageItem

pub(all) enum PageItem {
Text(GlyphRun)
Rect(RectItem)
Image(ImageItem)
Link(LinkRegion)
} derive(Eq, ToJson,
Debug
)

One positioned item on a page, in painter's order.

PageItem::equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn PageItem::equal(PageItem, PageItem) -> Bool

PageItem::not_equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn PageItem::not_equal(x : PageItem, y : PageItem) -> Bool

PageItem::to_json

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn PageItem::to_json(PageItem) -> Json

PageItem::to_repr

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn PageItem::to_repr(PageItem) ->
Repr

PageModel

pub(all) struct PageModel {
fonts : Array[FontSpec]
pages : Array[Page]
} derive(Eq, ToJson,
Debug
)

The layout engine's complete output: interned fonts plus pages.

PageModel::add_font

fn PageModel::add_font(self : PageModel, spec : FontSpec) -> Int

Intern spec, returning its index for GlyphRun::font.

PageModel::equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn PageModel::equal(PageModel, PageModel) -> Bool

PageModel::new

fn PageModel::new() -> PageModel

An empty model to accumulate fonts and pages into.

PageModel::not_equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn PageModel::not_equal(x : PageModel, y : PageModel) -> Bool

PageModel::to_json

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn PageModel::to_json(PageModel) -> Json

PageModel::to_repr

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn PageModel::to_repr(PageModel) ->
Repr

PageModel::validate

fn PageModel::validate(self : PageModel) -> Array[String]

Check every structural invariant; return one human-readable message per violation (empty means the model is well-formed):

  • every GlyphRun has exactly one advance per UTF-16 code unit of its text, a non-negative size, and a font index into fonts;
  • every RectItem has non-negative extents, and its stroke and stroke_w_pt agree (both present or both absent);
  • every color channel is within 0..=255;
  • every page has positive extents.

RectItem

pub(all) struct RectItem {
x_pt : Double
y_pt : Double
w_pt : Double
h_pt : Double
fill : Color?
stroke : Color?
stroke_w_pt : Double
} derive(Eq, ToJson,
Debug
)

Axis-aligned filled and/or stroked rectangle. Doubles as underlines, table borders, and shading (thin or filled).

RectItem::equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn RectItem::equal(RectItem, RectItem) -> Bool

RectItem::not_equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn RectItem::not_equal(x : RectItem, y : RectItem) -> Bool

RectItem::to_json

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn RectItem::to_json(RectItem) -> Json

RectItem::to_repr

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn RectItem::to_repr(RectItem) ->
Repr

Twips

pub(all) struct Twips(Int) derive(Eq, ToJson,
Debug
)

Twentieths of a point — OOXML's unit for indents, spacing, margins, page sizes (w:twip).

Twips::equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn Twips::equal(Twips, Twips) -> Bool

Twips::not_equal

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn Twips::not_equal(x : Twips, y : Twips) -> Bool

Twips::to_json

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn Twips::to_json(Twips) -> Json

Twips::to_pt

fn Twips::to_pt(self : Twips) -> Double

Convert to points (20 twips per point).

Twips::to_repr

#deprecated("implicit trait-method promotion is being removed; call via the trait")
fn Twips::to_repr(Twips) ->
Repr

black

let black : Color

Default document text color.