@veztraa/report-core
v0.3.3
Published
Type-safe schema, TypeScript types, and {{expression}} engine for ReportForge. Defines 12 element types including signature blocks and repeating container bands, plus font catalog with handwriting and multilingual Noto fonts.
Maintainers
Readme
@veztraa/report-core
Shared TypeScript types, Zod schema, and {{expression}} engine for ReportForge. Defines 12 element types including signature (draw/type/upload), container (repeating bands), chart, table, richtext, and more. Also exports SUPPORTED_FONTS with 4 handwriting fonts and 8 multilingual Noto fonts (Chinese, Japanese, Korean, Arabic, Hebrew, Hindi, Thai). Used internally by @veztraa/report-renderer and @veztraa/report-designer.
Installation
npm install @veztraa/report-coreTemplate Schema
Every ReportForge report is a plain JSON object that matches the Template type:
import type { Template } from "@veztraa/report-core";
const template: Template = {
version: "1.0",
name: "Invoice",
page: {
size: "A4", // A4 | A3 | A5 | Letter | Legal
orientation: "portrait", // portrait | landscape
margin: { top: 30, right: 30, bottom: 30, left: 30 },
},
header: { height: 60, elements: [] },
footer: { height: 40, elements: [] },
body: {
elements: [
{
id: "el_001",
type: "text",
x: 0, y: 0, width: 400, height: 30,
content: "Invoice #{{invoice.number}}",
style: { fontSize: 18, fontWeight: "bold", color: "#1a1a2e" },
},
{
id: "el_002",
type: "table",
x: 0, y: 50, width: 535, height: 200,
dataSource: "items",
columns: [
{ key: "name", label: "Item", width: 300 },
{ key: "qty", label: "Qty", width: 80 },
{ key: "price", label: "Price", width: 100 },
],
},
],
},
};Element Types
| Type | Description |
|---|---|
| text | Plain text with {{field}} bindings |
| richtext | HTML rich text (headings, lists, tables, images) |
| table | Data-bound table with repeating rows |
| container | Group of child elements; with a dataSource it becomes a repeating band (one instance per array item) |
| image | Image from URL or base64 |
| chart | Bar, line, or pie chart |
| barcode | 1D barcode (Code128, EAN, etc.) |
| qrcode | QR code |
| rectangle | Filled or stroked rectangle |
| line | Horizontal or vertical line |
| pagebreak | Forces a new page |
| signature | Signature block — typed name in a handwriting font, drawn signature, or uploaded image; with optional label, title, and date |
Repeating containers (bands)
A container groups child elements into a reusable block. Give it a dataSource
and it becomes a repeating band: the whole block is rendered once per item in
the bound array, and each child's {{field}} resolves against that item (merged
over the parent data scope). Without a dataSource it's a static group rendered
once. Children carry x/y relative to the container's top-left, and
containers may be nested.
{
id: "items_band",
type: "container",
x: 0, y: 80, width: 535, height: 120,
dataSource: "items", // repeats once per element of data.items
gap: 16, // vertical space between instances
children: [
{ id: "name", type: "text", x: 0, y: 0, width: 535, height: 22, content: "{{name}}" },
{ id: "body", type: "richtext", x: 0, y: 26, width: 535, height: 80, content: "{{description}}" },
],
}Signature element
{
id: "sig_1",
type: "signature",
x: 0, y: 200, width: 240, height: 92,
name: "{{signer.name}}", // displayed as typed handwriting or used as alt text
title: "{{signer.title}}",
label: "Authorized Signature",
date: "{{signedDate}}",
scriptFont: "Great Vibes", // Great Vibes | Dancing Script | Pacifico | Sacramento
scriptColor: "#1e293b",
lineColor: "#94a3b8",
align: "left", // left | center | right
showLine: true,
// imageSrc: "data:image/png;base64,..." // overrides the typed name with a drawn/uploaded image
}Pagination
The renderer lays out a single tall canvas and slices it into pages of the chosen size. Content-driven elements grow to fit their data and flow across pages automatically:
- Tables break between rows; the header row repeats on each continuation page.
- Rich text breaks between lines; a heading, embedded image, or table line is never split.
- Repeating containers flow item-by-item. An item that fits on a page is kept together (moved whole to the next page rather than split); an item taller than a page flows across pages.
- A
pagebreakelement forces following content onto a new page.
This is handled entirely by @veztraa/report-renderer and the CLI — no manual
page math is required in the template.
Expression Engine
import { evaluate, evaluateCondition, resolveArray } from "@veztraa/report-core";
const data = { user: { name: "Alice" }, items: [{ price: 10 }, { price: 20 }] };
evaluate("Hello {{user.name}}!", data); // "Hello Alice!"
evaluate("Total: {{items.1.price}}", data); // "Total: 20"
evaluateCondition("{{user.name}} === 'Alice'", data); // true
resolveArray("items", data); // [{ price: 10 }, { price: 20 }]Schema Validation
import { TemplateSchema } from "@veztraa/report-core";
const result = TemplateSchema.safeParse(untrustedInput);
if (!result.success) {
console.error(result.error.issues);
}Font Catalog
SUPPORTED_FONTS lists all font families available in the renderer:
import { SUPPORTED_FONTS } from "@veztraa/report-core";
// [{ value: "Roboto", label: "Roboto", category: "sans-serif" }, ...]| Category | Families |
|---|---|
| sans-serif | Roboto, Open Sans, Lato, Montserrat, Inter, Poppins, Nunito, Raleway, Ubuntu |
| serif | Playfair Display, Merriweather |
| monospace | Source Code Pro, Courier |
| handwriting | Great Vibes, Dancing Script, Pacifico, Sacramento |
| multilingual | Noto Sans (Latin/Cyrillic/Greek), Noto Sans SC (Chinese), Noto Sans JP (Japanese), Noto Sans KR (Korean), Noto Sans Arabic, Noto Sans Hebrew, Noto Sans Devanagari (Hindi), Noto Sans Thai |
Handwriting fonts are bundled in
@veztraa/report-renderer. Noto multilingual fonts are loaded from CDN on first use — choose the family that matches your content's script, as@react-pdfhas no automatic glyph fallback.
Keywords
pdf report template schema typescript zod expression-engine report-template pdf-template reportforge
License
MIT © Veztraa Solutions
