The template
This is the whole thing — an HTML page with {{ }} bindings. Nothing is
generated from a designer format; what you see here is what the engine renders.
template.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<!-- Stylesheets, scripts and fonts go here. Every page of this report gets them. -->
<link rel="stylesheet" href="assets/css/report.css">
<!--
The charts, drawn from the page's own data island once the fonts are in. It loads here rather
than at the end of the page because the head is the part every page of this report shares, and
the script waits for the document itself before it touches anything.
-->
<script src="assets/js/charts.js"></script>
</head>
<body>
<!--
The report's data, as JSON, for chart and script code to read:
const model = JSON.parse(document.getElementById('report-data').textContent);
Here rather than in a page because the data belongs to the report: this file is merged into
every page, so writing it once puts a copy in each.
-->
<script id="report-data" type="application/json">{{json}}</script>
<!--
What goes on every sheet. Leave either empty for none, and leave room for them in the page's
own padding — neither reserves space, they draw over the content.
The header is placed in the page, fixed to the top of the sheet, so this report's CSS styles it
and the designer's preview shows it exactly as it will print. The footer is drawn by the browser
instead, because it is the only way to print the page number:
Page <span class="pageNumber"></span> of <span class="totalPages"></span>
-->
<template id="report-header"></template>
<template id="report-footer"></template>
</body>
</html>
What it carries
Stylesheets, scripts, images and fonts travel inside the report, so nothing is resolved from the outside while it renders.
assets/css/report.cssassets/js/charts.js