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">
</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>
<!--
Landscape changes what a masthead can be: there is width to spare and very little height, so
this one is a single band with the period on the right rather than a stacked block.
-->
<template id="report-header">
<header class="masthead">
<div class="title">
<h1>{{report.title}}</h1>
<p>{{report.owner}}</p>
</div>
<div class="period">
<span class="label">Reporting period</span>
<span class="value">{{report.period}}</span>
</div>
</header>
</template>
<template id="report-footer">
<div class="pagefoot">
<span class="mark">{{report.title}}</span>
<span>{{report.period}} — generated {{date report.generatedOn "dd MMM yyyy" "en-GB"}}</span>
<span>Page <span class="pageNumber"></span> of <span class="totalPages"></span></span>
</div>
</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.css