Active Foundation

Search documentation

Search all pages, headings and text, in the current package or topic or in all documentation.

Export

PNG, SVG, CSV and PDF export, the report options, and when an export is still.

@workspace/active-charts 6.0.0Status unknowncontent differs from the recorded release 6.0.0baseline ce081dc3

The package's own guide

This page is the package's docs/recipes/EXPORT.md, included unchanged from the package as it is in Foundation. Links to the package's internal design documents keep their text.

Selection, tooltip, legend and exportClient

Selected: nothing

Images (SVG, PNG, PDF) and data (CSV) from a chart's handle. The contract is ../API.md ยง ChartHandle and ADR-040 (W11).

Await the result#

Every export returns a promise. It resolves once the file has been handed to the browser and rejects when no file could be produced. An image export waits until the chart is still, so an export started during an entrance or zoom animation captures the final frame.

TSX
import {useRef} from 'react';import {LineChart, type ChartHandle} from '@workspace/active-charts';
function FundChart({rows}: {rows: {date: Date; nav: number}[]}) {  const chartRef = useRef<ChartHandle>(null);
  async function download(): Promise<void> {    try {      await chartRef.current?.exportAsPng('nav.png');    } catch {      // Show the host's own failure message.    }  }
  return (    <>      <LineChart        ref={chartRef}        data={rows}        xKey="date"        yKey="nav"        xAxis={{temporalSemantic: 'calendarDate'}}        height={300}      />      <button type="button" onClick={() => void download()}>        PNG      </button>    </>  );}

The same outcome is reported by onExportComplete(result) and onExportError(format, error), also for exports the reader starts from the context menu. Without onExportError, a failed menu export is written to the developer console; it is never swallowed.

Capture without a download#

Every export resolves with the file it produced. With download: false nothing is handed to the browser: the host gets the same file, captured the same way, and can store it (ADR-105).

TSX
async function attachToCase(): Promise<void> {  const result = await chartRef.current?.exportAsPdf('funding-ratio', {    download: false,  });  if (!result) return;  // `result.filename` is 'funding-ratio.pdf'; `result.blob.type` is  // 'application/pdf'. The upload is the host's own.  await fetch('/api/case/42/attachments', {    method: 'POST',    headers: {'X-File-Name': result.filename},    body: result.blob,  });}

A report heading#

The host may put a title, a subtitle and a caption into an image export. These are the host's words, in the host's language; the chart never writes them.

TSX
async function exportReport(): Promise<void> {  await chartRef.current?.exportAsPdf('funding-ratio.pdf', {    report: {      title: t('report.fundingRatio'),      subtitle: t('report.lastYear'),      caption: t('report.source'),    },  });}

Fonts#

Which script each format carries, and the host routes for what the PDF refuses: EXPORT-GUARANTEES.md.

  • SVG keeps the host font by name: it shows where that font is installed.

  • PNG embeds the host's web font when its @font-face files are readable (same origin); otherwise the image uses a system font.

  • PDF draws text only in a font it embeds, so the host supplies its fonts as TrueType data (ADR-058). Each text element draws in the first family that covers it. Without export fonts the PDF draws in Helvetica and refuses any character outside WinAnsi; complex scripts (Arabic, Hebrew, the Indic scripts, Thai, โ€ฆ), emoji and supplementary-plane characters are refused whatever the fonts. For those, export SVG or PNG, or print from a browser.

Supply the fonts once, for every chart and for the context menu:

TSX
const exportFonts: ChartExportFonts = {  families: [    {      family: 'Corporate Sans',      faces: [        // Loaded only when a PDF export needs it; the loader is the host's.        {          source: () =>            fetch('/fonts/corporate-regular.ttf').then((r) => r.arrayBuffer()),        },        {          source: () =>            fetch('/fonts/corporate-bold.ttf').then((r) => r.arrayBuffer()),          weight: 700,        },      ],    },    {      family: 'Noto Sans CJK',      faces: [        {          source: () =>            fetch('/fonts/noto-sans-cjk.ttf').then((r) => r.arrayBuffer()),        },      ],    },  ],};
function ChartsWithExportFonts() {  return (    <ChartDefaults      locale="en-GB"      t={englishChartTranslator}      exportFonts={exportFonts}    >      <AppShell />    </ChartDefaults>  );}

A single export can name its own set, or skip the defaults, and a refusal names why:

TSX
declare const exportFonts: ChartExportFonts;
async function exportPdf(): Promise<void> {  try {    await chartRef.current?.exportAsPdf('chart.pdf', {fonts: exportFonts});  } catch (err) {    if (      err instanceof ChartExportError &&      err.code === 'EXPORT_TEXT_UNSUPPORTED'    ) {      // err.reason, err.script and err.codePoint say what; offer PNG instead.      await chartRef.current?.exportAsPng('chart.png');    }  }}

Data#

exportAsCsv writes the host's raw rows: never the reduced, grouped or percentage values the chart displays. A standalone family writes its own data model: a treemap its node paths and values, a sankey its links, an organisation chart name, parent and title, a gauge its value and range.

This page as Markdown