> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nemu.cc/llms.txt
> Use this file to discover all available pages before exploring further.

# Overview

> The low level Document and Page classes for placing elements with full control

The Document API places elements directly on a page. You create a `Document`, build elements
with the page factories, add them to the page or to a container, and call `build`. Elements
flow top to bottom by default, or sit at exact coordinates when given a `position`.

```ts theme={null}
import { Document } from "@nemu-ai/pdf";

const pdf = new Document({ page_size: "A4", margin: 50 });
const page = pdf.create_page();

page.add(
  page.text({ content: "Invoice", style: { font_size: 28, font_weight: "bold" } }),
  page.text({ content: "Thank you for your business.", style: { color: "#6b7280" } }),
);

await pdf.build("invoice.pdf");
```

<Warning>
  Page factories like `page.text` build an element but do not place it. Add what you build with
  `page.add` or a container's `add`, or it will not render.
</Warning>

## The Document class

Construct a `Document` with an optional `DocumentOptions` object.

```ts theme={null}
new Document(options?: DocumentOptions)

interface DocumentOptions {
  page_size?: "A4" | "Letter" | "Legal" | "Custom";
  custom_dimensions?: { width: number; height: number };
  margin?: number | MarginValues;
  parse_markdown?: boolean;
}
```

<ParamField path="page_size" type="&#x22;A4&#x22; | &#x22;Letter&#x22; | &#x22;Legal&#x22; | &#x22;Custom&#x22;">
  The page format. Use `"Custom"` together with `custom_dimensions` for any other size.
</ParamField>

<ParamField path="custom_dimensions" type="{ width: number; height: number }">
  Width and height in points, used when you need a size outside the named formats.
</ParamField>

<ParamField path="margin" type="number | MarginValues">
  A single number for all sides, or per side values.
</ParamField>

<ParamField path="parse_markdown" type="boolean">
  Enables markdown parsing on text content. Individual text elements can override this.
</ParamField>

## Document methods

```ts theme={null}
pdf.create_page(theme?: Theme): Page
pdf.add_page(): Page
pdf.set_theme(theme: Theme): void
pdf.load_font_sync(name: string, file_path: string, variable?: boolean): void
pdf.load_image_sync(name: string, file_path: string): void
await pdf.build(file_path: string): Promise<void>
```

`create_page` returns a fresh page and optionally applies a theme to it. `add_page` appends
another page to the document. `set_theme` applies a theme document wide. `load_font_sync`
registers a font by name, with `variable: true` to enable weight instancing for a custom
variable font. `load_image_sync` registers an image by name so it can be referenced from
`page.image`.

## The build pipeline

`build` runs the engine in three passes. Measure computes each element's intrinsic size from
font metrics. Layout assigns positions from the page margin and header zone. Render draws
every element in z index order.

```ts theme={null}
await pdf.build(file_path: string): Promise<void>
await pdf.prepare_pdf(): Promise<any>
await pdf.render_to(file_path: string): Promise<void>
```

`build` is `prepare_pdf` followed by `render_to`. The split exists so a caller can measure
before rendering, which is how the [Doc API](/packages/pdf/doc/overview) paginates.

## Page factories

The page builds elements but does not place them. Add what you build with `page.add` or a
container's `add`.

```ts theme={null}
page.text(options: CreateTextOptions): TextElement
page.rect(options: CreateRectOptions): RectElement
page.image(options: CreateImageOptions): ImageElement
page.table(options: CreateTableOptions): TableElement
page.create_container(options?: CreateContainerOptions): ContainerElement
page.header_container(options?: CreateContainerOptions): HeaderContainer
page.footer_container(options?: CreateContainerOptions): FooterContainer
page.add(...elements: BaseElement[]): this
```

<Note>
  `header_container` and `footer_container` are idempotent. Calling either again returns the
  existing zone rather than creating a new one.
</Note>

The page also exposes `get_content_width()` and `get_content_height()`, which give the
usable area inside the margins. They are handy for sizing full width containers and tables.

## A complete example

```ts theme={null}
import { Document } from "@nemu-ai/pdf";

const pdf = new Document({ page_size: "A4", margin: 50 });
const page = pdf.create_page();

const card = page.rect({
  width: page.get_content_width(),
  height: 120,
  shape_style: { fill_color: "#1a365d", border_radius: 10 },
});

page.add(
  page.text({ content: "Invoice", style: { font_size: 28, font_weight: "bold" } }),
  page.text({ content: "Thank you for your business.", style: { color: "#6b7280" } }),
  card,
);

await pdf.build("invoice.pdf");
```

## Next steps

<CardGroup cols={2}>
  <Card title="Elements" icon="shapes" href="/packages/pdf/document/elements">
    Text, rectangles, images, overlay children, and z index ordering.
  </Card>

  <Card title="Layout" icon="objects-column" href="/packages/pdf/document/layout">
    Group children with flow and flex containers.
  </Card>

  <Card title="Themes" icon="palette" href="/packages/pdf/document/themes">
    Centralize named colors and apply defaults by element type.
  </Card>

  <Card title="Tables" icon="table" href="/packages/pdf/document/tables">
    Build rows and columns with the low level table factory.
  </Card>
</CardGroup>
