Temporeum Timelinev1.0.2

Export and print

A timeline leaves the screen in four ways, and none of them needs a server: the view as a standalone SVG or a PNG, and the whole range as a PDF or on paper through the browser's own print. All four start from the frame the screen draws, so a file is the picture, not a screenshot of it.

  svg: () => download("plan.svg", new Blob([timeline.exportSvg()], { type: "image/svg+xml" })),
  png: async () => download("plan.png", await timeline.exportPng({ scale: 2 })),

From the running example example/export

Live example: A quarter's plan, exported: SVG, PNG and PDF files, and the browser's print

Loading…

Run “A quarter's plan, exported: SVG, PNG and PDF files, and the browser's print” on your own machine
  1. Make an empty folder and put this example’s two files in it:

    index.html — the page the code runs in, its markup and styles:

    <div id="timeline" style="height: 360px"></div>
    <p>
      <button type="button" data-export="svg">SVG</button>
      <button type="button" data-export="png">PNG</button>
      <button type="button" data-export="pdf">PDF</button>
      <button type="button" data-export="print">Print</button>
      <span class="log" id="log" aria-live="polite">The view shows two weeks; a PDF and a print hold the whole quarter.</span>
    </p>
    

    main.js — the code, as it is in the editor now, your edits included:

  2. In a terminal in that folder, install the library and Vite (it lets the browser follow the imports), then start it:

    npm init -y
    npm install @temporeum/timeline-element
    npm install --save-dev vite
    npx vite
  3. Open the address it prints, http://localhost:5173/.

The plugin

import { createTimeline, registerPlugin } from "@temporeum/timeline-element";
import { exportPlugin } from "@temporeum/timeline-element/export";
import { ganttPlugin } from "@temporeum/timeline-element/gantt";

// PNG, PDF and print are a plugin, and so is the Gantt chart this page prints.
registerPlugin(exportPlugin, ganttPlugin);

From the running example example/export

The PNG encoder, the PDF writer and the print view are a plugin on their own entry point, @temporeum/timeline-element/export: a page that never exports does not download them. exportSvg() needs no plugin — a frame is SVG already. Without the plugin, the promises of exportPng, exportPdf and print reject, naming the import that is missing. Before the first frame there is nothing to export: exportSvg() throws, and the other three reject, all with the same "Nothing drawn yet" error. Registered once, it serves every timeline on the page. A page that runs the timeline inside a sandboxed <iframe> has to allow what the export does: allow-downloads to save a file, allow-modals for print().

The view: SVG and PNG

exportSvg() returns the view as a standalone SVG document — its own stylesheet inside, so any viewer shows it as the screen did. A theme of "auto" is pinned to the one the screen shows when you export: a file must not change with the colour scheme of whoever opens it.

exportPng({ scale }) draws the same frame into a canvas, scale device pixels to a CSS pixel (2 by default), and resolves to a PNG Blob. Item labels are HTML on screen, and a browser will not let a canvas that drew HTML be read back, so in a PNG they are set as SVG text in the same place, size and colour.

The whole range: PDF and print

  pdf: async () => {
    const pdf = await timeline.exportPdf({ size: "a4", orientation: "landscape" });

From the running example example/export

exportPdf(options) resolves to the bytes of a PDF of the whole range — the time the items cover — and print(options) sends the same pages to the browser's print dialog, resolving when the dialog is closed. The pages are cut the same way for both:

  • Time is not cut. Every page shows the whole range across the sheet's width.
  • Lanes are cut between rows, where no item spans the break — so every item is on exactly one page, and none is cut in two. The exception is a page with no such place: an item taller than a whole page (the break goes through it, and through nothing else where it can), or rows of overlapping multi-row items that together are taller than a page. Then the cut is at the page's edge, and an item it goes through prints on its first page, up to that edge.
  • A page prints at 100%. It is drawn at the sheet's printable size, a CSS pixel to ¾ of a point.
  • What prints is the content. Selection, focus and loading marks are not printed, nor is the overview strip; dependency arrows, the resource load and sticky lane headers are. Paper is light unless you configured a theme.

The options are the sheet: size ("a3", "a4", "a5", "letter", "legal", or [width, height] in points; "a4" by default), orientation ("landscape" by default), margin in points (36, half an inch), range ("all", or two instants), and for the PDF a title — the timeline's ariaLabel by default.

The PDF is written by the library itself, with no dependency: vector shapes, and text set in Helvetica that a reader selects, searches and copies as written. Helvetica covers the Latin alphabets of Western and Central Europe; a script outside them needs a font embedded in the file, which the writer does not do yet.

print() puts a print view at the end of the document — one section a sheet, shown to print media only — calls window.print(), and takes the view away when the browser says the print is over (afterprint).

Without the element

The pieces are in @temporeum/timeline-core/export, pure functions over the scene, so they also run on a server or in a build step: printPages(options, sheet) gives the frames of a print, scenesToPdf(scenes, sheet) writes them as a PDF, standaloneSvg(scene) and svgToPng(document, svg) make the files of one frame, and mountPrintView(document, scenes, paper) is the print view.