The viewer
Intent
<quario-viewer> puts a compiled report on screen as pages, and wires the
export buttons that turn those pages into files. Reach for it when your users
should see a document before they download it, or should choose the format
themselves.
Design decisions
The viewer compiles nothing. It takes a compiled report as a property and never touches the event stream. Compiling stays your application’s job, on your instance, with your registry.
It paints the same layout the PDF target writes. @quario/layout lays
out preview and document against the same faces, which is why the preview
breaks its pages where the document does.
The preview approximates rather than imitating. It’s a faithful page break, not a pixel-faithful print simulation. Treat it as the document’s structure on screen, not as a proof of the final raster.
Export targets are yours to choose, in your order. Hand it the targets you
want buttons for, and the buttons appear in array order. A viewer given no
targets exports nothing. It can hand a reader only the formats it knows a content
type for: pdf, docx, xlsx and csv. A target of your own gets no button.
The sheet is the layout’s either way — targets buy downloads, not the render.
A failed render resolves the mount. The element doesn’t hang or throw into
your render tree. It reports through an error event and stays mounted.
The colour scheme paints chrome only. The sheet stays the document’s, because a document that changed colour with the reader’s theme would stop being the document.
The marking goes on the sheet. An unlicensed report carries the marking in the preview just as it does in an export. The viewer never shows an unmarked page for a render that would export marked.
API walkthrough
Mounting
Import @quario/viewer/register to define the element, or import
QuarioViewer and define it yourself. Set properties as element properties,
not attributes, because a compiled report and a font record aren’t strings.
import { pdf } from '@quario/pdf'
const el = /** @type {import('@quario/viewer').QuarioViewer} */ (
document.createElement('quario-viewer')
)
/** One decision, read by the preview and by the export. */
/** @type {import('@quario/viewer').ViewerPage} */
const page = { size: 'A4', margin: 54 }
el.report = report
el.data = data
el.page = page
el.targets = [pdf({ page })]
el.filename = 'invoice-2043'
el.addEventListener('error', (event) => {
console.error(event.detail.kind, event.detail.error)
})
document.body.append(el)The element and the export target are two consumers of one decision, and nothing reconciles them for you. Declare the geometry once and hand it to both, as above.
Fonts
fonts takes the same record pdf({ fonts }) takes. Pass the identical object
to both so the preview measures against the faces the document will embed, and
hold it across renders. The record and its buffers are the identity the viewer
remembers a face by, so a fresh buffer means parsing the file again.
Reacting
rendered fires when a render lands on the sheet. error carries { error, kind }. kind is mount-render before any render has landed, and
update-render after. It’s export for a download the viewer couldn’t
produce, and host-option for a property it rejected before rendering
anything. The mounting sample above listens for it.
renderComplete resolves once the pages within reach have painted, not every
page, which is what a test awaits rather than a timer. A host that reads pixels
off a page far down the document finds it blank until it’s scrolled to.
Zoom and chrome
zoom takes 'fit' or a number. colorScheme takes 'light', 'dark' or
'auto' and reaches the surrounding chrome, never the sheet.
When not to use it
A server rendering a PDF into a response needs none of this. The viewer is for a browser where someone is looking. If your users only ever receive a file, render the PDF target directly and skip the element and its layout dependency.