normordis-pdf
Pure-Rust institutional PDF generation for the NORMAXIS mini-app framework.
This repository now contains the standalone normordis-pdf library crate along with two helper tools:
tools/dotx2ndttools/ndt-tools
A backup/restore helper is also available in scripts/.
Documentação bilingue / Bilingual documentation
Este repositório oferece documentação em português e inglês.
- Português: introdução e início rápido abaixo.
- English: see the English sections below.
- Manual:
MANUAL.mdé atualmente em português;MANUAL.en.mdfornece um ponto de entrada em inglês.
Português (pt-PT)
normordis-pdf gera documentos formais — relatórios, cartas, certificados e formulários — diretamente em Rust, sem dependências externas (sem LaTeX, sem Typst, sem Chromium). Está orientado para padrões de administração pública portuguesa e inclui Liberation Sans para renderização pronta a usar.
Três modelos de composição, todos combináveis num único documento:
| Modelo | Descrição |
|---|---|
| Flow | Elementos empilham-se verticalmente; quebras de página automáticas; cabeçalho re-injectado |
| Fixed Box | Elementos posicionados em coordenadas absolutas; não afeta o cursor |
| Templates NDT | Templates JSON com injeção de dados em tempo de execução |
Início rápido
# Cargo.toml
[]
= "1.0.0"
Documento Flow
use ;
let pdf = new
.push
.push
.push
.render_to_bytes?;
write?;
English
Overview
normordis-pdf generates formal documents — reports, letters, certificates, forms — directly from Rust, with no external binary dependency (no LaTeX, no Typst, no Chromium). It targets Portuguese public administration document standards and embeds Liberation Sans for out-of-the-box rendering with real glyph metrics.
Three composition models, all mixable in a single document:
| Model | Description |
|---|---|
| Flow | Elements stack vertically; automatic page breaks; header re-injection |
| Fixed Box | Elements placed at absolute coordinates; no cursor effect |
| NDT Templates | JSON-driven document templates with runtime data injection |
Quick Start
# Cargo.toml
[]
= "1.0.0"
Flow document
use ;
let pdf = new
.push
.push
.push
.render_to_bytes?;
write?;
NCRTF rich text
use DocumentBuilder;
let ncrtf = r#"{
"ncrtf": "1.0",
"blocks": [
{"type":"heading","level":1,"children":[{"type":"text","text":"Título","marks":[]}]},
{"type":"paragraph","alignment":"justify","children":[
{"type":"text","text":"Texto com ","marks":[]},
{"type":"text","text":"negrito","marks":["bold"]},
{"type":"text","text":" e itálico.","marks":["italic"]}
]}
]
}"#;
let pdf = new
.push_ncrtf?
.render_to_bytes?;
NDT template
use DocumentBuilder;
const TEMPLATE: &str = include_str!;
let data = json!.to_string;
let pdf = new
.push_ndt?
.render_to_bytes?;
Features
Flow elements
| Type | Struct / method |
|---|---|
| Paragraph (plain or rich) | Paragraph::new(text) |
| Section heading | Section::new(text, level) |
| Ordered / bullet / checklist | List::new(items, ListType::Bullet) |
| Table | Table::new(headers, rows) |
| Image | FlowImage::new(bytes, width_mm) |
| Spacer | Spacer::new(height_mm) |
| Horizontal rule | HorizontalRule::new() |
| Page break | PageBreak |
Fixed Box elements
| Type | Builder method |
|---|---|
| Text at absolute position | DocumentBuilder::fixed_text(box, text, align) |
| Image at absolute position | DocumentBuilder::fixed_image(box, bytes, fit) |
| Decorative line | DocumentBuilder::fixed_line(x1, y1, x2, y2, color) |
NCRTF v1.0 — rich text format
NCRTF (NORMAXIS Canonical Rich Text Format) is a JSON schema for inline-styled paragraphs. It is the interchange format between editors (such as @normaxis/nx-doc) and this renderer.
Supported block types: paragraph, heading (levels 1–4), list (bullet / ordered / checklist).
Supported inline marks: bold, italic, underline, strikethrough, code.
NDT v1.0.0 — document templates
NDT (NORMAXIS Document Template) is a JSON-driven template format for institutional documents. Templates define a layout schema; runtime data is injected at render time.
Template file (*.ndt.json):
Data file (*.ndt-data.json):
Supported body element types: paragraph, heading, rich_text, table, list, image,
spacer, horizontal_rule, page_break, fixed_text, fixed_image, fixed_line,
fixed_box, zone_ref, conditional, repeat, include.
Supported conditional operators: exists, empty, eq, neq, gt, lt.
Bundled templates
Ready-to-use NDT templates are provided under examples/templates/:
| File | Description |
|---|---|
relatorio-simples.ndt.json |
Simple institutional report |
oficio-nacional.ndt.json |
Official letter (ofício) |
certidao-generica.ndt.json |
Generic certificate (certidão) |
formulario-generico.ndt.json |
Generic two-section form |
Examples
Run any example with:
| Example | Description |
|---|---|
01_basic_document |
Flow document with headings, paragraphs, table, list |
02_ncrtf_document |
Document built from NCRTF rich text JSON |
03_ndt_template |
Document rendered from an NDT template + runtime data |
04_mixed_layout |
Flow + Fixed Box mixed (office letter style) |
05_fidelity |
Sectioned header/footer, watermark, runtime fields |
06_advanced_layout |
Indentation, col_span, multi-page tables |
07_named_styles |
Named paragraph styles with inheritance |
08_portuguese_spacing |
Portuguese hyphenation and line breaking |
09_typography |
Text decorations, tab stops, borders |
10_advanced_elements |
Forms, footnotes, TOC |
11_size_benchmark |
Large document size benchmark |
12_compliance |
PDF/A-1b + traceability |
13_accessibility |
PDF/UA-2 tagged document |
14_custom_fonts |
Custom TTF/OTF fonts via font_from_bytes, per-paragraph .font_family(), fallback chain |
15_fonts_from_dir |
Load all fonts from a directory with fonts_from_dir |
Fonts
Four font families are embedded at compile time — Liberation Sans, Liberation Serif, Liberation Mono, and Libertinus Serif. No system fonts are required.
// Register any TTF/OTF font family — from bytes or from disk
let pdf = new
.font_from_bytes?
.push
.render_to_bytes?;
Key font APIs:
| API | Description |
|---|---|
DocumentBuilder::font_from_bytes(name, regular, bold?, italic?, bold_italic?) |
Register from &[u8] (e.g. include_bytes!) |
DocumentBuilder::font_from_file(name, regular, bold?, italic?, bold_italic?) |
Register from TTF/OTF file paths |
DocumentBuilder::fonts_from_dir(path) |
Scan a directory; groups files by -Bold / -Italic suffix |
DocumentBuilder::default_font(name) |
Change the document default family |
Paragraph::font_family(name) |
Per-paragraph font override |
FontRegistry::register_bytes / register_file / load_dir |
Direct registry manipulation |
DocumentStyle::font_fallback |
FontFallbackChain — tried in order when the requested font is not registered |
Common Word font names (Arial, Calibri, Times New Roman, Cambria, Consolas, etc.) are pre-registered as aliases to their Liberation equivalents.
Version constants
VERSION // "1.0.0" — crate version
NDT_VERSION // "1.0.0" — NDT engine version
NCRTF_VERSION // "1.0" — NCRTF parser version
API stability
All public items re-exported from normordis_pdf::* are considered stable from v1.0.0 onwards. Internal modules (normordis_pdf::template::*, normordis_pdf::richtext::*, etc.) are not stable and may change between minor versions.
License
EUPL-1.2 — see LICENSE or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12.