PDF is a popular format for creating documents and reports as it's highly customisable and preserves important details.
This demo shows how to use chart.export() to create PDF reports with custom layouts, fonts and embedded KeyLines charts in vector quality.
Tailor-made layouts
Different documents may have different layout or text requirements, which is why the doc option gives you full control over the appearance of the exported PDF.
Passing an instance of PDFDocument to doc allows you to customise the page layout using the methods provided by PDFKit, such as text(), font() and list().
Customised fonts
The fonts option tackles the limited default font support in PDF by letting you embed your own fonts. Optionally, you can also use the specify separate font files for 'regular' and 'bold' versions using the weight property.
To minimise the size of the output file the export will only embed fonts that were used in the exported image.
Before you start
KeyLines uses external dependencies to run PDF export:
- PDFKit
- SVG-to-PDFKit (for PDFs containing SVGs)
See Adding dependencies for more details and guidance.
Note that some viewers and in-browser viewers may not fully support viewing PDF. For example, we do not recommend using Firefox built-in PDF viewer.
Key functions used:
import KeyLines from "keylines";
import { data, defaultStyle, lorem, initZoomIds } from "./data.js";
const margin = 0.4 * 72; // 72dpi so this is 0.4inches
const iconFonts = {
"Material Icons": { src: "/fonts/MaterialIcons/MaterialIcons-Regular.ttf" },
};
const ralewayFontSrcs = [
{ name: "Raleway-Bold", src: "/fonts/Raleway/Raleway-Bold.ttf" },
{ name: "Raleway-Regular", src: "/fonts/Raleway/Raleway-Regular.ttf" },
{ name: "Raleway-Italic", src: "/fonts/Raleway/Raleway-Italic.ttf" },
];
const timesNewFont = { regular: "Times-Roman", bold: "Times-Bold", italic: "Times-Italic" };
const helveticaFont = { regular: "Helvetica", bold: "Helvetica-Bold", italic: "Helvetica-Oblique" };
const ralewayFont = { regular: "Raleway-Regular", bold: "Raleway-Bold", italic: "Raleway-Italic" };
let chart;
function downloadPdf(pdfUrl) {
// Create the link to download the image
const snapshotLink = document.createElement("a");
snapshotLink.download = `chart-export.pdf`;
snapshotLink.href = pdfUrl;
snapshotLink.click();
// Important - remember to revoke the url afterwards to free it from browser memory
URL.revokeObjectURL(pdfUrl);
}
async function addExportedChartAndCaption(width, height, fontFamily, captionText, doc) {
doc.font(fontFamily.italic, 10);
const captionHeight = doc.heightOfString(captionText);
// add chart
await chart.export({
type: "pdf",
doc,
fonts: iconFonts,
extents: "view",
fitTo: {
width,
height: height - captionHeight,
},
});
// add caption
doc.moveDown().text(captionText, { align: "center", width });
}
async function loadAndRegisterFonts(fontSrcs, doc) {
let error = false;
for (let i = 0; i < fontSrcs.length; i++) {
const font = fontSrcs[i];
const response = await fetch(font.src);
if (!response.ok) {
console.warn(`Unable to load font ${font.name}`);
error = true;
}
const fontBuffer = await response.arrayBuffer();
doc.registerFont(font.name, fontBuffer);
}
return !error;
}
async function generateLandscapeReport(doc, docTitle, captionText) {
const fontsLoaded = await loadAndRegisterFonts(ralewayFontSrcs, doc);
const fontFamily = fontsLoaded ? ralewayFont : helveticaFont;
// Add title
doc.font(fontFamily.bold, 35).text(docTitle, { align: "center" });
// Allowing a margin separation between them,
// calculate the remaining space for the chart & bullet points
const chartWidth = ((doc.page.width - 3 * margin) * 2) / 3;
const contentHeight = doc.page.height - margin - doc.y;
const chartHeight = (contentHeight * 4) / 5;
// Split the padding equally above and below the main content
doc.y += (contentHeight - chartHeight) / 2;
const contentTop = doc.y;
await addExportedChartAndCaption(chartWidth, chartHeight, fontFamily, captionText, doc);
// Add the bullet points to the right of the chart
doc.x += chartWidth + margin;
doc.y = contentTop;
doc.font(fontFamily.regular, 16);
for (let i = 0; i < 4; i++) {
doc.list([lorem[4]]).moveDown();
}
}
async function generatePortraitReport(doc, docTitle, captionText) {
const fontFamily = timesNewFont;
// Add title
doc.font(fontFamily.bold, 20).text(docTitle, { align: "center" });
const contentWidth = doc.page.width - 2 * margin;
const chartHeight = (doc.page.height - margin - doc.y) * 0.5;
await addExportedChartAndCaption(contentWidth, chartHeight, fontFamily, captionText, doc);
doc.moveDown(2);
// Add columns of text
doc.font(fontFamily.regular, 13);
doc.text(lorem[3], { align: "justify", columns: 2 });
}
async function exportReport() {
const orientation = document.querySelector('input[name="orientation"]:checked').value;
const docTitle = document.getElementById("title").value;
const captionText = document.getElementById("caption").value;
// Create the PDF documnet
const doc = new PDFDocument({ layout: orientation, margin });
const reportGenerator =
orientation === "landscape" ? generateLandscapeReport : generatePortraitReport;
await reportGenerator(doc, docTitle, captionText);
const docData = [];
doc.on("data", docData.push.bind(docData));
doc.on("end", () => {
downloadPdf(URL.createObjectURL(new Blob(docData, { type: "application/pdf" })));
});
doc.end();
}
async function startKeyLines() {
const options = {
backColour: "#F0F8FF",
selectedNode: { b: "#111", bw: 5, fbc: "#333", fc: "white" },
iconFontFamily: "Material Icons",
imageAlignment: defaultStyle.imageAlignment,
handMode: true,
logo: { u: "/images/Logo.png" },
};
chart = await KeyLines.create({ container: "klchart", options });
data.items.forEach((item) => {
if (item.type === "node") {
const kind = item.d.kind;
const icon = defaultStyle.kindIcons[kind];
item.fi = {
t: icon,
c: defaultStyle.nodeColours[kind],
};
}
});
await chart.load(data);
await chart.layout("organic", { fit: false });
chart.zoom("fit", { animate: true, time: 500, ids: initZoomIds });
document.getElementById("exportDoc").addEventListener("click", exportReport);
}
async function loadFontsAndStart() {
// wait for Material icons to load before starting keylines
document.fonts.load("24px 'Material Icons'").then(startKeyLines);
}
window.addEventListener("DOMContentLoaded", loadFontsAndStart); export const data = {
type: "LinkChart",
items: [
{
type: "link",
id: "252-250",
id1: "252",
id2: "250",
w: 3,
off: 0,
},
{
type: "link",
id: "293-291",
id1: "293",
id2: "291",
w: 3,
off: 0,
},
{
type: "link",
id: "252-249",
id1: "252",
id2: "249",
w: 3,
off: 0,
},
{
type: "link",
id: "293-290",
id1: "293",
id2: "290",
w: 3,
off: 0,
},
{
type: "link",
id: "253-252",
id1: "253",
id2: "252",
w: 3,
off: 0,
},
{
type: "link",
id: "294-293",
id1: "294",
id2: "293",
w: 3,
off: 0,
},
{
type: "link",
id: "447-294",
id1: "447",
id2: "294",
w: 3,
off: 0,
},
{
type: "link",
id: "476-253",
id1: "476",
id2: "253",
w: 3,
off: 0,
},
{
type: "link",
id: "663-253",
id1: "663",
id2: "253",
w: 3,
off: 0,
},
{
type: "link",
id: "447-3",
id1: "447",
id2: "3",
w: 3,
off: 0,
},
{
type: "link",
id: "476-3",
id1: "476",
id2: "3",
w: 3,
off: 0,
},
{
type: "link",
id: "663-3",
id1: "663",
id2: "3",
w: 3,
off: 0,
},
{
type: "link",
id: "445-447",
id1: "445",
id2: "447",
w: 3,
off: 0,
},
{
type: "link",
id: "446-447",
id1: "446",
id2: "447",
w: 3,
off: 0,
},
{
type: "link",
id: "469-476",
id1: "469",
id2: "476",
w: 3,
off: 0,
},
{
type: "link",
id: "470-476",
id1: "470",
id2: "476",
w: 3,
off: 0,
},
{
type: "link",
id: "471-476",
id1: "471",
id2: "476",
w: 3,
off: 0,
},
{
type: "link",
id: "472-476",
id1: "472",
id2: "476",
w: 3,
off: 0,
},
{
type: "link",
id: "473-476",
id1: "473",
id2: "476",
w: 3,
off: 0,
},
{
type: "link",
id: "474-476",
id1: "474",
id2: "476",
w: 3,
off: 0,
},
{
type: "link",
id: "475-476",
id1: "475",
id2: "476",
w: 3,
off: 0,
},
{
type: "link",
id: "655-663",
id1: "655",
id2: "663",
w: 3,
off: 0,
},
{
type: "link",
id: "656-663",
id1: "656",
id2: "663",
w: 3,
off: 0,
},
{
type: "link",
id: "657-663",
id1: "657",
id2: "663",
w: 3,
off: 0,
},
{
type: "link",
id: "658-663",
id1: "658",
id2: "663",
w: 3,
off: 0,
},
{
type: "link",
id: "659-663",
id1: "659",
id2: "663",
w: 3,
off: 0,
},
{
type: "link",
id: "660-663",
id1: "660",
id2: "663",
w: 3,
off: 0,
},
{
type: "link",
id: "661-663",
id1: "661",
id2: "663",
w: 3,
off: 0,
},
{
type: "link",
id: "662-663",
id1: "662",
id2: "663",
w: 3,
off: 0,
},
{
type: "link",
id: "447-292",
id1: "447",
id2: "292",
w: 3,
off: 0,
},
{
type: "link",
id: "476-251",
id1: "476",
id2: "251",
w: 3,
off: 0,
},
{
type: "link",
id: "663-251",
id1: "663",
id2: "251",
w: 3,
off: 0,
},
{
type: "link",
id: "447-437",
id1: "447",
id2: "437",
w: 3,
off: 0,
},
{
type: "link",
id: "476-460",
id1: "476",
id2: "460",
w: 3,
off: 0,
},
{
type: "link",
id: "476-464",
id1: "476",
id2: "464",
w: 3,
off: 0,
},
{
type: "link",
id: "663-654",
id1: "663",
id2: "654",
w: 3,
off: 0,
},
{
type: "link",
id: "447-441",
id1: "447",
id2: "441",
w: 3,
off: 0,
},
{
type: "link",
id: "447-444",
id1: "447",
id2: "444",
w: 3,
off: 0,
},
{
type: "link",
id: "476-468",
id1: "476",
id2: "468",
w: 3,
off: 0,
},
{
type: "link",
id: "garage-damages-3-445",
id1: "3",
id2: "445",
w: 3,
off: 0,
},
{
type: "link",
id: "garage-damages-3-446",
id1: "3",
id2: "446",
w: 3,
off: 0,
},
{
type: "link",
id: "garage-damages-3-469",
id1: "3",
id2: "469",
w: 3,
off: 0,
},
{
type: "link",
id: "garage-damages-3-470",
id1: "3",
id2: "470",
w: 3,
off: 0,
},
{
type: "link",
id: "garage-damages-3-471",
id1: "3",
id2: "471",
w: 3,
off: 0,
},
{
type: "link",
id: "garage-damages-3-472",
id1: "3",
id2: "472",
w: 3,
off: 0,
},
{
type: "link",
id: "garage-damages-3-473",
id1: "3",
id2: "473",
w: 3,
off: 0,
},
{
type: "link",
id: "garage-damages-3-474",
id1: "3",
id2: "474",
w: 3,
off: 0,
},
{
type: "link",
id: "garage-damages-3-475",
id1: "3",
id2: "475",
w: 3,
off: 0,
},
{
type: "link",
id: "garage-damages-3-655",
id1: "3",
id2: "655",
w: 3,
off: 0,
},
{
type: "link",
id: "garage-damages-3-656",
id1: "3",
id2: "656",
w: 3,
off: 0,
},
{
type: "link",
id: "garage-damages-3-657",
id1: "3",
id2: "657",
w: 3,
off: 0,
},
{
type: "link",
id: "garage-damages-3-658",
id1: "3",
id2: "658",
w: 3,
off: 0,
},
{
type: "link",
id: "garage-damages-3-659",
id1: "3",
id2: "659",
w: 3,
off: 0,
},
{
type: "link",
id: "garage-damages-3-660",
id1: "3",
id2: "660",
w: 3,
off: 0,
},
{
type: "link",
id: "garage-damages-3-661",
id1: "3",
id2: "661",
w: 3,
off: 0,
},
{
type: "link",
id: "garage-damages-3-662",
id1: "3",
id2: "662",
w: 3,
off: 0,
},
{
type: "link",
id: "people-252-460",
id1: "252",
id2: "460",
w: 3,
off: 0,
},
{
type: "link",
id: "people-252-464",
id1: "252",
id2: "464",
w: 3,
off: 0,
},
{
type: "link",
id: "people-252-468",
id1: "252",
id2: "468",
w: 3,
off: 0,
},
{
type: "link",
id: "people-252-654",
id1: "252",
id2: "654",
w: 3,
off: 0,
},
{
type: "link",
id: "people-293-437",
id1: "293",
id2: "437",
w: 3,
off: 0,
},
{
type: "link",
id: "people-293-441",
id1: "293",
id2: "441",
w: 3,
off: 0,
},
{
type: "link",
id: "people-293-444",
id1: "293",
id2: "444",
w: 3,
off: 0,
},
{
type: "link",
id: "people-437-441",
id1: "437",
id2: "441",
w: 3,
off: 0,
},
{
type: "link",
id: "people-437-444",
id1: "437",
id2: "444",
w: 3,
off: 0,
},
{
type: "link",
id: "people-441-444",
id1: "441",
id2: "444",
w: 3,
off: 0,
},
{
type: "link",
id: "people-460-464",
id1: "460",
id2: "464",
w: 3,
off: 0,
},
{
type: "link",
id: "people-460-468",
id1: "460",
id2: "468",
w: 3,
off: 0,
},
{
type: "link",
id: "people-464-468",
id1: "464",
id2: "468",
w: 3,
off: 0,
},
{
type: "link",
id: "person-garage-3-252",
id1: "3",
id2: "252",
w: 3,
off: 0,
},
{
type: "link",
id: "person-garage-3-293",
id1: "3",
id2: "293",
w: 3,
off: 0,
},
{
type: "node",
id: "3",
t: "Torphy - Ebert",
d: {
kind: "garage",
},
c: "rgba(255,255,255,1)",
b: "rgba(0,107,95,1)",
x: -1497.4758377075195,
y: -1202.4347877502441,
},
{
type: "node",
id: "249",
t: "0065 Cordia Throughway, Vivienneton",
d: {
kind: "address",
},
c: "rgba(255,255,255,1)",
b: "rgba(164,39,104,1)",
x: -1466.7487869262695,
y: -454.68124771118164,
},
{
type: "node",
id: "250",
t: "055 9497 2435",
d: {
kind: "telephone",
},
c: "rgba(255,255,255,1)",
b: "rgba(164,39,104,1)",
x: -1550.6120681762695,
y: -432.18075942993164,
},
{
type: "node",
id: "251",
t: "QWE09EE",
d: {
kind: "vehicle",
},
c: "rgba(255,255,255,1)",
b: "rgba(127,203,104,1)",
x: -1475.888557434082,
y: -870.048038482666,
},
{
type: "node",
id: "252",
t: "Ellen Larkin",
d: {
kind: "person",
},
c: "rgba(255,255,255,1)",
b: "rgba(166,116,186,1)",
x: -1564.0012283325195,
y: -686.4468421936035,
},
{
type: "node",
id: "253",
t: "Policy 910",
d: {
kind: "policy",
},
c: "rgba(255,255,255,1)",
b: "rgba(31,120,180,1)",
x: -1450.9795608520508,
y: -805.0684852600098,
},
{
type: "node",
id: "290",
t: "2253 Desiree Drive, Lake Cadeside",
d: {
kind: "address",
},
c: "rgba(255,255,255,1)",
b: "rgba(164,39,104,1)",
x: -1216.8798904418945,
y: -1862.4563331604004,
},
{
type: "node",
id: "291",
t: "0313 652 3599",
d: {
kind: "telephone",
},
c: "rgba(255,255,255,1)",
b: "rgba(164,39,104,1)",
x: -1267.6861038208008,
y: -1925.0965309143066,
},
{
type: "node",
id: "292",
t: "ASD984R",
d: {
kind: "vehicle",
},
c: "rgba(255,255,255,1)",
b: "rgba(127,203,104,1)",
x: -1759.520393371582,
y: -1844.9278297424316,
},
{
type: "node",
id: "293",
t: "Ahmad Lemke",
d: {
kind: "person",
},
c: "rgba(255,255,255,1)",
b: "rgba(166,116,186,1)",
x: -1414.8723831176758,
y: -1728.5325050354004,
},
{
type: "node",
id: "294",
t: "Policy 961",
d: {
kind: "policy",
},
c: "rgba(255,255,255,1)",
b: "rgba(31,120,180,1)",
x: -1520.4905471801758,
y: -1879.7627296447754,
},
{
type: "node",
id: "437",
t: "August Fay DVM",
d: {
kind: "person",
},
c: "rgba(255,255,255,1)",
b: "rgba(166,116,186,1)",
x: -1417.4956741333008,
y: -1924.5281105041504,
},
{
type: "node",
id: "441",
t: "Lora Nitzsche",
d: {
kind: "person",
},
c: "rgba(255,255,255,1)",
b: "rgba(166,116,186,1)",
x: -1518.036994934082,
y: -1976.313877105713,
},
{
type: "node",
id: "444",
t: "Yesenia Schinner",
d: {
kind: "person",
},
c: "rgba(255,255,255,1)",
b: "rgba(166,116,186,1)",
x: -1610.619026184082,
y: -1909.568515777588,
},
{
type: "node",
id: "445",
t: "Damage 129",
d: {
kind: "damage",
},
c: "rgba(255,255,255,1)",
b: "rgba(255,134,21,1)",
x: -1532.5708084106445,
y: -1479.2611122131348,
},
{
type: "node",
id: "446",
t: "Damage 294",
d: {
kind: "damage",
},
c: "rgba(255,255,255,1)",
b: "rgba(255,134,21,1)",
x: -1633.2661209106445,
y: -1465.8401832580566,
},
{
type: "node",
id: "447",
t: "Claim 451",
d: {
kind: "claim",
},
c: "rgba(255,255,255,1)",
b: "rgba(255,47,63,1)",
x: -1573.300666809082,
y: -1687.7504615783691,
},
{
type: "node",
id: "460",
t: "Nya Reilly",
d: {
kind: "person",
},
c: "rgba(255,255,255,1)",
b: "rgba(166,116,186,1)",
x: -1756.694465637207,
y: -634.4872779846191,
},
{
type: "node",
id: "464",
t: "Linda Kub",
d: {
kind: "person",
},
c: "rgba(255,255,255,1)",
b: "rgba(166,116,186,1)",
x: -1857.9077224731445,
y: -635.5139503479004,
},
{
type: "node",
id: "468",
t: "Lori Lueilwitz",
d: {
kind: "person",
},
c: "rgba(255,255,255,1)",
b: "rgba(166,116,186,1)",
x: -1722.832649230957,
y: -535.9418678283691,
},
{
type: "node",
id: "469",
t: "Damage 468",
d: {
kind: "damage",
},
c: "rgba(255,255,255,1)",
b: "rgba(255,134,21,1)",
x: -1617.355110168457,
y: -1022.916675567627,
},
{
type: "node",
id: "470",
t: "Damage 586",
d: {
kind: "damage",
},
c: "rgba(255,255,255,1)",
b: "rgba(255,134,21,1)",
x: -1537.0869216918945,
y: -971.7761421203613,
},
{
type: "node",
id: "471",
t: "Damage 638",
d: {
kind: "damage",
},
c: "rgba(255,255,255,1)",
b: "rgba(255,134,21,1)",
x: -1706.604377746582,
y: -1131.728801727295,
},
{
type: "node",
id: "472",
t: "Damage 581",
d: {
kind: "damage",
},
c: "rgba(255,255,255,1)",
b: "rgba(255,134,21,1)",
x: -1773.801399230957,
y: -1051.3435401916504,
},
{
type: "node",
id: "473",
t: "Damage 602",
d: {
kind: "damage",
},
c: "rgba(255,255,255,1)",
b: "rgba(255,134,21,1)",
x: -1769.793586730957,
y: -1156.489854812622,
},
{
type: "node",
id: "474",
t: "Damage 771",
d: {
kind: "damage",
},
c: "rgba(255,255,255,1)",
b: "rgba(255,134,21,1)",
x: -1695.019905090332,
y: -1060.3865242004395,
},
{
type: "node",
id: "475",
t: "Damage 927",
d: {
kind: "damage",
},
c: "rgba(255,255,255,1)",
b: "rgba(255,134,21,1)",
x: -1814.770149230957,
y: -1105.2457389831543,
},
{
type: "node",
id: "476",
t: "Claim 503",
d: {
kind: "claim",
},
c: "rgba(255,255,255,1)",
b: "rgba(255,47,63,1)",
x: -1709.5419998168945,
y: -877.073184967041,
},
{
type: "node",
id: "654",
t: "Marietta Turner Jr.",
d: {
kind: "person",
},
c: "rgba(255,255,255,1)",
b: "rgba(166,116,186,1)",
x: -1324.6194534301758,
y: -770.9541358947754,
},
{
type: "node",
id: "655",
t: "Damage 712",
d: {
kind: "damage",
},
c: "rgba(255,255,255,1)",
b: "rgba(255,134,21,1)",
x: -1364.6671829223633,
y: -1311.3736000061035,
},
{
type: "node",
id: "656",
t: "Damage 708",
d: {
kind: "damage",
},
c: "rgba(255,255,255,1)",
b: "rgba(255,134,21,1)",
x: -1211.3150100708008,
y: -1058.8240699768066,
},
{
type: "node",
id: "657",
t: "Damage 233",
d: {
kind: "damage",
},
c: "rgba(255,255,255,1)",
b: "rgba(255,134,21,1)",
x: -1188.2261428833008,
y: -1203.4983215332031,
},
{
type: "node",
id: "658",
t: "Damage 912",
d: {
kind: "damage",
},
c: "rgba(255,255,255,1)",
b: "rgba(255,134,21,1)",
x: -1270.8091506958008,
y: -1193.0131721496582,
},
{
type: "node",
id: "659",
t: "Damage 500",
d: {
kind: "damage",
},
c: "rgba(255,255,255,1)",
b: "rgba(255,134,21,1)",
x: -1183.8325271606445,
y: -1130.365379333496,
},
{
type: "node",
id: "660",
t: "Damage 120",
d: {
kind: "damage",
},
c: "rgba(255,255,255,1)",
b: "rgba(255,134,21,1)",
x: -1288.408576965332,
y: -1298.7427940368652,
},
{
type: "node",
id: "661",
t: "Damage 855",
d: {
kind: "damage",
},
c: "rgba(255,255,255,1)",
b: "rgba(255,134,21,1)",
x: -1262.979133605957,
y: -972.7531471252441,
},
{
type: "node",
id: "662",
t: "Damage 280",
d: {
kind: "damage",
},
c: "rgba(255,255,255,1)",
b: "rgba(255,134,21,1)",
x: -1227.943977355957,
y: -1265.8992881774902,
},
{
type: "node",
id: "663",
t: "Claim 411",
d: {
kind: "claim",
},
c: "rgba(255,255,255,1)",
b: "rgba(255,47,63,1)",
x: -1336.150276184082,
y: -1056.543529510498,
},
],
};
export const defaultStyle = {
nodeColours: {
telephone: "#A42768",
address: "#A42768",
person: "#A674BA",
policy: "#1F78B4",
claim: "#FF2F3F",
vehicle: "#7FCB68",
garage: "#006B5F",
damage: "#FF8615",
},
kindIcons: {
person: "person",
telephone: "phone",
address: "home",
policy: "folder",
claim: "receipt",
vehicle: "directions_car",
garage: "build",
damage: "taxi_alert",
},
imageAlignment: {
directions_car: { dy: -5 },
receipt: { e: 0.9 },
taxi_alert: { dy: -5, dx: 5, e: 0.8 },
phone: { e: 0.8 },
build: { e: 0.8 },
},
};
export const initZoomIds = [
"662",
"656",
"661",
"660",
"659",
"663",
"655",
"658",
"657",
"3",
"475",
"474",
"473",
"471",
"469",
"470",
"476",
"472",
"251",
"253",
"654",
"252",
"464",
"460",
"468",
];
export const lorem = [];
lorem[0] =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
lorem[1] = `${lorem[0]}\n\n${lorem[0]}`;
lorem[2] = `${lorem[0]}\n\n${lorem[0]}\n\n${lorem[0]}`;
lorem[3] = `${lorem[0]}\n\n${lorem[0]}\n\n${lorem[0]}\n\n${lorem[0]}`;
lorem[4] = lorem[0].slice(0, 100); <!doctype html>
<html lang="en" style="background-color: #2d383f">
<head>
<meta charset="utf-8" />
<title>PDF Reports</title>
<link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/keylines.css" />
<link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/minimalsdk.css" />
<link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/sdk-layout.css" />
<link rel="stylesheet" type="text/css" href="@ci/theme/kl/css/demo.css" />
<link rel="stylesheet" href="./style.css" />
</head>
<body>
<div id="klchart" class="klchart"></div>
<script src="pdfkit/js/pdfkit.standalone.js" defer type="text/javascript"></script>
<script src="svg-to-pdfkit" defer type="text/javascript"></script>
<script type="module" src="./code.js"></script>
</body>
</html> #caption {
border: 1px solid rgba(179, 179, 179, 0.705);
padding-left: 7px;
padding-top: 8px;
resize: none;
}
[type="radio"] + svg {
background-color: rgba(179, 179, 179, 0.521);
border: 1px solid rgba(179, 179, 179, 0.705);
margin-left: -18px;
margin-right: 10px;
}
[type="text"] {
font-family: muli;
width: calc(100% - 6px);
margin: 8px 3px 10px 3px;
}
[type="radio"]:checked + svg {
border: 2px solid #009968;
}
[type="radio"]:hover + svg {
border: 2px solid #00c980;
}