@quario/docx
v0.3.1
Published
Tiny, flow-based Word render target for quario. Real Word tables, a navigable outline, live page-number fields.
Maintainers
Readme
@quario/docx
The Word render target for quario. Renders a report
definition to the bytes of a .docx package: a flow document, so the geometry and the typography
are quario's and the pagination is Word's.
Install
npm install quario @quario/docxThe engine is a peer dependency, installed beside the target. ESM-only, Node 22+, and
browser-ready through any standards-based ESM bundler. The renderer resolves a Uint8Array. You
write the file.
Quick start
import { writeFileSync } from "node:fs";
import { docx } from "@quario/docx";
import { quario } from "quario";
const schema = {
data: "$.orders[*]",
detail: {
columns: [
{ header: "Product", value: "{{ @.product }}" },
{ header: "Price", value: "{{ @.price }}" },
],
},
};
const data = { orders: [{ product: "Desk", price: 250 }] };
const bytes = await quario().report(schema).render(docx(), data);
writeFileSync("orders.docx", bytes);Options
Both are optional. The factory call validates both: an unknown option, or one of the wrong type, throws there rather than at the first render.
docx({
// Page geometry in PostScript points: "A4" (the default), "letter", or a
// [width, height] pair. The margin defaults to 54 pt, and a report that
// declares `page.margin` itself sets it instead — declaring both is an error.
page: { size: "A4", margin: 54 },
// Document properties. Strings only: no dates ride along, so rendering the
// same report twice produces the same bytes.
meta: { title: "Orders", author: "Acme BV", subject: "Q1" },
});What it renders
A report's bands become paragraphs. A table detail becomes a real Word table carrying the
author's column shares as a fixed grid, with span as gridSpan and a header row that repeats
after every page break. A split is a borderless one-row table. An image is an inline picture at
its natural size, capped at the text column, carrying its alt as the drawing's description.
The grouping becomes the navigation pane: a group header's first item takes Heading{depth+1}
from this package's own styles.xml, so no theme typography leaks in. Everything a report declares
is direct formatting on the runs and paragraphs that wear it. Normal stays empty, because a look
that depends on a style lookup is a look three readers may resolve three ways.
Page bands become a section's header and footer. A bare {{ page.number }} or {{ page.total }}
becomes a live PAGE / NUMPAGES field the reader's own application recomputes. Anything computed
from them freezes at the value the render saw. A group's reset: "page" opens a section that
restarts the numbering, and a break turns a page at every boundary its position names.
Determinism
Two renders of one report are byte-identical, on every supported runtime. fflate is pure
JavaScript, so its deflate output does not vary by engine. The package stamps every entry
1980-01-01 and nothing in the document carries a clock, so a digest over the bytes is a fair test.
Unlicensed marking
An unlicensed render writes the wording from report-start.marking as the first paragraph of
every footer. A licensed render writes none. The per-footer presence is normative. The wording's
look is best-effort.
Documentation
The quario documentation is the reference. The report schema is the normative specification of what a report may declare, and the support matrix says what each target makes of a declaration.
License
Commercial software with readable source. Evaluation is free, unlimited, and watermarked. Per-developer licenses at getquario.com. See the bundled LICENSE.
Pass your license key once, on the instance. quario verifies it offline:
const q = quario({ license: "quario_..." });
await q.license; // { licensed: true, licensee: "Acme BV", id: "1-ACME" }