Export guarantees
What each export format guarantees for international text and fonts.
@workspace/active-charts 6.0.0Status unknowncontent differs from the recorded release 6.0.0baseline ce081dc3
This page is the package's docs/, included unchanged
from the package as it is in Foundation. Links to the package's internal
design documents keep their text.
What each export of @workspace/ guarantees for international
text, who owns that guarantee, and which route a consumer takes when the
package does not guarantee what it needs. Decided in
ADR-060; the font model is ADR-058 (PDF)
and ADR-059 (SVG, PNG); the API is API.md § 4.
active-grid carries the same tiers in its own words for its own formats.
The tiers#
| Tier | Owner | Formats | Script guarantee | Limitation |
|---|---|---|---|---|
| L0 | package | CSV | every script, including supplementary-plane characters and ZWJ sequences: the logical Unicode text, no direction or isolation controls | data only: no font, no layout |
| L1 | package + browser | the live chart | what the browser shapes (joining, reordering, marks, bidi) with the fonts the page loads | a glyph no loaded font has is the browser's fallback |
| L2 | package + browser | SVG, PNG | what the exporting browser shapes | see SVG and PNG: the file depends on fonts, and an SVG depends on its viewer |
| L3a | package | Latin, Greek, Cyrillic, Armenian, Georgian, Han (BMP), Kana, Hangul (precomposed), each text element fully covered by one embedded TrueType family; WinAnsi without a set | refused with a code, never drawn broken: complex and right-to-left scripts, combining marks, joiners and selectors, emoji, supplementary-plane characters, uncovered text | |
| L3b-raster | host | a PDF the host builds with a PNG image | everything the host's browser shapes | the chart is a picture: its text is not selectable or searchable |
| L3b-print | host | a PDF the host's browser prints | everything the host's browser shapes, as selectable text | a host recipe, not a package route; valid only as far as the host has verified it with its own browser and fonts |
No tier promises more than it says. "Supported" in any row below means the tier named, under its condition.
SVG and PNG#
| Mode | SVG | PNG |
|---|---|---|
| No set (inherit) | names the live font stack; embeds nothing; the viewer draws with the fonts it has | the page's same-origin web fonts of the live stack, embedded best effort; a glyph outside them draws in the browser's default font |
Explicit set, embed absent/false | names the host's families, then the live stack; embeds nothing; the viewer needs those fonts installed | every face of every family embedded; the browser falls back per glyph across the host's families, in order, before any system font (deterministic) |
Explicit set, embed | the same names, plus one @font- data URL per face; browsers honour it, other viewers may not | as above |
In every mode the browser shapes: the package lays text out in the live font and never again in the export font, so a family with other metrics can shift text. Put the live family first.
What an export shows#
Every export and print shows the chart at rest (ADR-064, ADR-065): its static polish (geometry, typography, axis layout, grid, theme) is in it; interaction state is not. No hover, recede or inactive paint, keyboard cue, legend preview, crosshair, emphasised node, gutter mark, tooltip or selection mark reaches an SVG, PNG, PDF or print, in any family: the state is painted by the package stylesheet, which an export never copies, and the transient marks carry one attribute the composer drops. A report that needs highlighted data encodes it in the data, as a series or an annotation.
Never mid-motion (ADR-104). An export waits until the chart is still:
two frames without a change, no running web animation, and past the end of
every scripted motion the chart declared on its scope (an update, a path
morph, a count-up or gauge replay, a grouped slot reflow, a discrete zoom),
because an eased tail that snaps to whole pixels leaves quiet frames before
it ends. It waits at most the chart's motion budget (the longest motion its
animation allows) plus one second, and at least four seconds; a chart that
never comes to rest within that rejects the export. An export started during
an entrance, an update, a reflow or a replay therefore writes the resting
geometry, and the tooltip, the crosshair, the cues, attention and the
colour-key marker are not in it (asserted in Chromium and Firefox for SVG,
with PNG and PDF waiting the same way).
An export means the same thing whichever route starts it (ADR-085). The
handle (Chart) and the context menu run one composed export
per chart: the same legend on the side it is painted (its entries, keys,
colours and hidden items as on the screen), the same export fonts and the
same rows. The menu writes its PNG at context and offers CSV
on every chart with a menu, the export-only menu of the scalars included
(ADR-087); the Sparkline has no menu and exports through its handle. A PDF
draws the whole chart on every route: the export clone always carries a
view, so svg2pdf scales the drawing into its box instead of clipping it
(ADR-088). A drawing that shows only part of its content says so in the image
too: a partial org chart view carries its note in the export's caption,
after the host's own.
Per script#
| Script or data | Live (L1) | CSV (L0) | SVG (L2) | PNG (L2) | PDF, no set | PDF, host set that covers it |
|---|---|---|---|---|---|---|
| Latin | browser | package | viewer-dependent | browser; set per glyph when given | Helvetica (WinAnsi) | drawn (L3a) |
| Cyrillic, Greek | browser | package | viewer-dependent | browser; set per glyph when given | EXPORT_ | drawn (L3a) |
| Han, Kana, Hangul (BMP) | browser | package | viewer-dependent | browser; set per glyph when given | EXPORT_ | drawn (L3a) |
| Arabic, Hebrew | browser | package | viewer-dependent | browser; set per glyph when given | EXPORT_ | EXPORT_; host L3b |
| Devanagari and other Indic | browser | package | viewer-dependent | browser; set per glyph when given | EXPORT_ | EXPORT_; host L3b |
| Combining mark in the text | browser | package | viewer-dependent | browser | EXPORT_ | EXPORT_ (normalizable says whether NFC would help) |
| Emoji, ZWJ sequences | browser | package | viewer-dependent | browser (colour emoji font of the OS) | EXPORT_ | EXPORT_; host L3b |
| Supplementary plane | browser | package | viewer-dependent | browser | EXPORT_ | EXPORT_; host L3b |
| A script no family covers | browser | package | viewer-dependent | browser default font | EXPORT_ | EXPORT_ |
Thai, Lao, Khmer, Myanmar, Tibetan, Mongolian, Syriac, Thaana and N'Ko are refused in PDF like Arabic. A refusal names one code point and, for a script, its name; it never carries the text.
Which route#
| The consumer needs | Route |
|---|---|
| the data | CSV (L0) |
| an image exactly as the browser shows it | PNG (L2); with an export font set for a deterministic font |
| a scalable, editable picture | SVG (L2), knowing the viewer draws the fonts; embed for browsers |
| a PDF with selectable text in a supported script | PDF (L3a) with an export font set that covers the text |
| a PDF with Arabic, Hebrew, Indic, emoji or other shaped text | a host route: L3b-raster (image in a PDF) or L3b-print (browser print) |
The package never hands over to a host route by itself: it refuses, and the host decides.
Errors#
Chart separates four kinds of failure; none carries the
exported text (at most one code point and a script name, the host's family
name, the detected font format, the missing module, the families tried).
| Kind | Codes | What the host does |
|---|---|---|
| a refusal of the text (L3a) | EXPORT_, EXPORT_, EXPORT_ | supply or extend fonts, or take a host route |
| a font set the export cannot use | EXPORT_, EXPORT_, EXPORT_ | fix the configuration |
| a font that did not arrive | EXPORT_ (the host loader's error is the cause) | retry; the next export calls the loader again |
| the export itself could not run | EXPORT_ (missing), EXPORT_, EXPORT_ | install the module, or offer no such export |
active-grid's Grid uses the same codes for the same meaning.
Configuring export fonts in a host#
The host owns its fonts: which families, which faces, where the bytes come from and the right to embed them. A typical enterprise set is a corporate Latin family first and a CJK family as its fallback, with faces loaded only when an export needs them:
const load = (path: string) => () => fetch(path, {signal: AbortSignal.timeout(15_000)}).then((response) => { if (!response.ok) throw new Error(`font ${response.status}`); return response.arrayBuffer(); });
const exportFonts: ChartExportFonts = { families: [ { family: 'Corporate Sans', faces: [ {source: load('/fonts/corporate-sans-regular.ttf')}, {source: load('/fonts/corporate-sans-bold.ttf'), weight: 700}, {source: load('/fonts/corporate-sans-italic.ttf'), style: 'italic'}, ], }, { family: 'Example CJK Sans', faces: [{source: load('/fonts/example-cjk-sans-regular.ttf')}], }, ],};
<ChartDefaults exportFonts={exportFonts}>{children}</ChartDefaults>;PDF needs static TrueType (
glyf) faces. A WOFF or WOFF2 web font, a CFF-flavoured OpenType font or a variable font cannot be embedded; SVG and PNG take TrueType, CFF, WOFF and WOFF2.Keep the object stable (a module constant): the package loads each face object once and shares the load between concurrent exports.
Name the live family first, so export metrics match the live layout.
The same object is an active-grid
Grid.Export Fonts
Host routes (L3b)#
These are recipes for a host. The package provides neither.
L3b-raster: a browser image in a PDF#
Render the chart in a browser or headless browser that has the fonts the text needs (installed in the image, or loaded as web fonts).
Wait for
document.and for the chart to be still.fonts. ready Take a PNG at a scale for print (for example 2x device pixels), with
exportor a screenshot of the chart element.As Png Place the image in the host's PDF.
The browser shapes every script it has fonts for, and the image looks like the screen. The chart's text is pixels: it cannot be selected, searched or read by assistive technology from the PDF, so the host adds any text it needs in its own PDF layer.
L3b-print: the browser prints a PDF#
Launch a browser the host controls (for example headless Chromium) with the fonts installed or loaded.
Render a page that contains the live chart, or its SVG export.
Wait for
document.and for the chart to be still.fonts. ready Print with the browser's PDF mechanism (in Chromium,
page.).pdf() Verify before relying on it: extract the text from the PDF (for example with
pdftotext) and render the PDF back to an image, for each script the host serves.
The browser shapes the text and writes it as selectable text with a
Unicode mapping. This was verified once in a research harness (Chromium 153
on Windows with the operating system's fonts): Arabic, Hebrew and Devanagari
came back from pdftotext in logical order, and the rendered PDF showed
joined Arabic, right-to-left Hebrew and formed Devanagari conjuncts. That is
evidence the recipe works, not a guarantee for another browser version,
operating system or font set. A host claims L3b-print only for what it has
verified itself.