Select a report template and position the chart to get the view you want to export.
Then click Download to generate the sample PDF report.
You can customize the PDF output by passing your own PDFKit document in the doc property.
See also
import React, { useRef, useState } from "react";
import { createRoot } from "react-dom/client";
// Must be after the import of 'regraph/export'
import PDFDocument from "pdfkit/js/pdfkit.standalone";
import { Chart } from "regraph";
import { items, loremArray } from "./data";
import "regraph/export";
const slideIcon = "/img/slide.svg";
const reportIcon = "/img/report.svg";
// margins are specified in points, with 72 points per inch.
const margin = 72 * 0.4;
const title = "Investigative report";
const caption = "Policy 910 has two claims, each with an unusually high number of damage reports";
function report() {
const doc = new PDFDocument({
layout: "portrait",
margin,
});
doc.font("Times-Bold").fontSize(30).text(title, { align: "center" });
doc.font("Times-Italic").fontSize(10).text(caption, margin, 435, { align: "center" });
doc
.font("Times-Roman")
.fontSize(12)
.text(loremArray[3], margin, 480, { align: "justify", columns: 2 });
// update pointer to position where chart should be added
doc.x = margin;
doc.y = 120;
return doc;
}
function slide() {
const doc = new PDFDocument({
layout: "landscape",
margin,
});
const { width: pageWidth } = doc.page;
doc.font("Helvetica-Bold").fontSize(30).text(title, { align: "center" });
// move to starting position of bulletpoints
doc.x = 500;
doc.y = 130;
const bulletWidth = pageWidth - doc.x - margin;
doc.font("Helvetica").fontSize(16);
for (let i = 0; i < 4; i++) {
doc.list([loremArray[4]], { width: bulletWidth });
doc.moveDown();
}
doc
.font("Helvetica-Oblique")
.fontSize(14)
.text(caption, margin + 30, 500, { width: 400, align: "justify" });
// update pointer to position where chart should be added
doc.x = margin;
doc.y = 130;
return doc;
}
function downloadPdf(url) {
// Create the link to download the image
const downloadLink = document.createElement("a");
downloadLink.download = `ReGraph_export.pdf`;
downloadLink.href = url;
downloadLink.click();
// revoke the url afterwards to free it from browser memory
URL.revokeObjectURL(url);
}
function PdfReport() {
const chartRef = useRef(null);
return (
<div className="story">
<ExportControls chartRef={chartRef} />
<div className="chart-wrapper">
<Chart ref={chartRef} items={items} />
</div>
</div>
);
}
function ExportControls({ chartRef }) {
const [documentLayout, setDocumentLayout] = useState("report");
const [exportEnabled, setExportEnabled] = useState(true);
const createDocument = () => {
let doc;
let fitTo = {};
if (documentLayout === "slide") {
doc = slide(PDFDocument);
fitTo = { width: 450, height: 350 };
} else {
doc = report(PDFDocument);
const chartExportWidth = doc.page.width - (doc.page.margins.left + doc.page.margins.right);
fitTo = { width: chartExportWidth, height: 300 };
}
const options = {
type: "pdf",
extents: "view",
doc,
fitTo,
};
return [doc, options];
};
const exportChartAndDownloadDoc = (doc, options) => {
const docData = [];
doc.on("data", docData.push.bind(docData));
doc.on("end", () => {
downloadPdf(URL.createObjectURL(new Blob(docData, { type: "application/pdf" })));
});
setExportEnabled(false);
if (chartRef.current) {
chartRef.current
.export(options)
.then(() => {
doc.end();
setExportEnabled(true);
})
.catch((err) => {
console.error("Regraph Export:", err.message);
setExportEnabled(true);
});
}
};
return (
<div
className="options overlay"
style={{ flexBasis: "auto", minWidth: 400, flexWrap: "nowrap" }}
>
<div
style={{
padding: 6,
display: "flex",
inlineSize: "62px",
overflowWrap: "break-word",
textAlign: "left",
cursor: "default",
}}
>
Template:
</div>
<div
className="options__group"
style={{
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "start",
}}
>
<div className="options__group__subgroup">
<button
type="button"
style={{ padding: 3 }}
className={documentLayout === "report" ? "active" : ""}
onClick={() => {
setDocumentLayout("report");
}}
>
<img
src={reportIcon}
alt="Report icon"
height={60}
width={60}
style={{ filter: `brightness(${documentLayout === "report" ? 1 : 0})` }}
/>
</button>
<button
type="button"
style={{ padding: 3 }}
className={documentLayout === "slide" ? "active" : ""}
onClick={() => {
setDocumentLayout("slide");
}}
>
<img
src={slideIcon}
alt="Slide icon"
height={60}
width={60}
style={{ filter: `brightness(${documentLayout === "slide" ? 1 : 0})` }}
/>
</button>
</div>
</div>
<span className="separator" style={{ minHeight: "60px", height: "80%" }} />
<button
type="button"
style={{ margin: "3px 6px" }}
disabled={exportEnabled}
onClick={async () => {
const [doc, options] = createDocument();
exportChartAndDownloadDoc(doc, options);
}}
>
Download
</button>
</div>
);
}
const FontReadyChart = React.lazy(() =>
document.fonts.load("24px 'Material Icons'").then(() => ({
default: PdfReport,
}))
);
export function Demo() {
return (
<React.Suspense fallback="">
<FontReadyChart />
</React.Suspense>
);
}
const root = createRoot(document.getElementById("regraph"));
root.render(<Demo />); export const items = {
3: {
color: "rgba(255,255,255,1)",
data: { kind: "garage" },
fontIcon: { color: "#006B5F", fontFamily: "Material Icons", text: "build" },
border: { color: "rgba(0,107,95,1)" },
label: [{ text: "Torphy - Ebert", position: "s" }],
},
249: {
color: "rgba(255,255,255,1)",
data: { kind: "address" },
fontIcon: { color: "#A42768", fontFamily: "Material Icons", text: "home" },
border: { color: "rgba(164,39,104,1)" },
label: [{ text: "0065 Cordia Throughway, Vivienneton", position: "s" }],
},
250: {
color: "rgba(255,255,255,1)",
data: { kind: "telephone" },
fontIcon: { color: "#A42768", fontFamily: "Material Icons", text: "phone" },
border: { color: "rgba(164,39,104,1)" },
label: [{ text: "055 9497 2435", position: "s" }],
},
251: {
color: "rgba(255,255,255,1)",
data: { kind: "vehicle" },
fontIcon: { color: "#7FCB68", fontFamily: "Material Icons", text: "directions_car" },
border: { color: "rgba(127,203,104,1)" },
label: [{ text: "QWE09EE", position: "s" }],
},
252: {
color: "rgba(255,255,255,1)",
data: { kind: "person" },
fontIcon: { color: "#A674BA", fontFamily: "Material Icons", text: "person" },
border: { color: "rgba(166,116,186,1)" },
label: [{ text: "Ellen Larkin", position: "s" }],
},
253: {
color: "rgba(255,255,255,1)",
data: { kind: "policy" },
fontIcon: { color: "#1F78B4", fontFamily: "Material Icons", text: "folder_open" },
border: { color: "rgba(31,120,180,1)" },
label: [{ text: "Policy 910", position: "s" }],
},
290: {
color: "rgba(255,255,255,1)",
data: { kind: "address" },
fontIcon: { color: "#A42768", fontFamily: "Material Icons", text: "home" },
border: { color: "rgba(164,39,104,1)" },
label: [{ text: "2253 Desiree Drive, Lake Cadeside", position: "s" }],
},
291: {
color: "rgba(255,255,255,1)",
data: { kind: "telephone" },
fontIcon: { color: "#A42768", fontFamily: "Material Icons", text: "phone" },
border: { color: "rgba(164,39,104,1)" },
label: [{ text: "0313 652 3599", position: "s" }],
},
292: {
color: "rgba(255,255,255,1)",
data: { kind: "vehicle" },
fontIcon: { color: "#7FCB68", fontFamily: "Material Icons", text: "directions_car" },
border: { color: "rgba(127,203,104,1)" },
label: [{ text: "ASD984R", position: "s" }],
},
293: {
color: "rgba(255,255,255,1)",
data: { kind: "person" },
fontIcon: { color: "#A674BA", fontFamily: "Material Icons", text: "person" },
border: { color: "rgba(166,116,186,1)" },
label: [{ text: "Ahmad Lemke", position: "s" }],
},
294: {
color: "rgba(255,255,255,1)",
data: { kind: "policy" },
fontIcon: { color: "#1F78B4", fontFamily: "Material Icons", text: "folder_open" },
border: { color: "rgba(31,120,180,1)" },
label: [{ text: "Policy 961", position: "s" }],
},
437: {
color: "rgba(255,255,255,1)",
data: { kind: "person" },
fontIcon: { color: "#A674BA", fontFamily: "Material Icons", text: "person" },
border: { color: "rgba(166,116,186,1)" },
label: [{ text: "August Fay DVM", position: "s" }],
},
441: {
color: "rgba(255,255,255,1)",
data: { kind: "person" },
fontIcon: { color: "#A674BA", fontFamily: "Material Icons", text: "person" },
border: { color: "rgba(166,116,186,1)" },
label: [{ text: "Lora Nitzsche", position: "s" }],
},
444: {
color: "rgba(255,255,255,1)",
data: { kind: "person" },
fontIcon: { color: "#A674BA", fontFamily: "Material Icons", text: "person" },
border: { color: "rgba(166,116,186,1)" },
label: [{ text: "Yesenia Schinner", position: "s" }],
},
445: {
color: "rgba(255,255,255,1)",
data: { kind: "damage" },
fontIcon: {
color: "#FF8615",
fontFamily: "Material Icons",
text: "directions_car",
},
border: { color: "rgba(255,134,21,1)" },
label: [{ text: "Damage 129", position: "s" }],
},
446: {
color: "rgba(255,255,255,1)",
data: { kind: "damage" },
fontIcon: {
color: "#FF8615",
fontFamily: "Material Icons",
text: "directions_car",
},
border: { color: "rgba(255,134,21,1)" },
label: [{ text: "Damage 294", position: "s" }],
},
447: {
color: "rgba(255,255,255,1)",
data: { kind: "claim" },
fontIcon: { color: "#FF2F3F", fontFamily: "Material Icons", text: "receipt" },
border: { color: "rgba(255,47,63,1)" },
label: [{ text: "Claim 451", position: "s" }],
},
460: {
color: "rgba(255,255,255,1)",
data: { kind: "person" },
fontIcon: { color: "#A674BA", fontFamily: "Material Icons", text: "person" },
border: { color: "rgba(166,116,186,1)" },
label: [{ text: "Nya Reilly", position: "s" }],
},
464: {
color: "rgba(255,255,255,1)",
data: { kind: "person" },
fontIcon: { color: "#A674BA", fontFamily: "Material Icons", text: "person" },
border: { color: "rgba(166,116,186,1)" },
label: [{ text: "Linda Kub", position: "s" }],
},
468: {
color: "rgba(255,255,255,1)",
data: { kind: "person" },
fontIcon: { color: "#A674BA", fontFamily: "Material Icons", text: "person" },
border: { color: "rgba(166,116,186,1)" },
label: [{ text: "Lori Lueilwitz", position: "s" }],
},
469: {
color: "rgba(255,255,255,1)",
data: { kind: "damage" },
fontIcon: {
color: "#FF8615",
fontFamily: "Material Icons",
text: "directions_car",
},
border: { color: "rgba(255,134,21,1)" },
label: [{ text: "Damage 468", position: "s" }],
},
470: {
color: "rgba(255,255,255,1)",
data: { kind: "damage" },
fontIcon: {
color: "#FF8615",
fontFamily: "Material Icons",
text: "directions_car",
},
border: { color: "rgba(255,134,21,1)" },
label: [{ text: "Damage 586", position: "s" }],
},
471: {
color: "rgba(255,255,255,1)",
data: { kind: "damage" },
fontIcon: {
color: "#FF8615",
fontFamily: "Material Icons",
text: "directions_car",
},
border: { color: "rgba(255,134,21,1)" },
label: [{ text: "Damage 638", position: "s" }],
},
472: {
color: "rgba(255,255,255,1)",
data: { kind: "damage" },
fontIcon: {
color: "#FF8615",
fontFamily: "Material Icons",
text: "directions_car",
},
border: { color: "rgba(255,134,21,1)" },
label: [{ text: "Damage 581", position: "s" }],
},
473: {
color: "rgba(255,255,255,1)",
data: { kind: "damage" },
fontIcon: {
color: "#FF8615",
fontFamily: "Material Icons",
text: "directions_car",
},
border: { color: "rgba(255,134,21,1)" },
label: [{ text: "Damage 602", position: "s" }],
},
474: {
color: "rgba(255,255,255,1)",
data: { kind: "damage" },
fontIcon: {
color: "#FF8615",
fontFamily: "Material Icons",
text: "directions_car",
},
border: { color: "rgba(255,134,21,1)" },
label: [{ text: "Damage 771", position: "s" }],
},
475: {
color: "rgba(255,255,255,1)",
data: { kind: "damage" },
fontIcon: {
color: "#FF8615",
fontFamily: "Material Icons",
text: "directions_car",
},
border: { color: "rgba(255,134,21,1)" },
label: [{ text: "Damage 927", position: "s" }],
},
476: {
color: "rgba(255,255,255,1)",
data: { kind: "claim" },
fontIcon: { color: "#FF2F3F", fontFamily: "Material Icons", text: "receipt" },
border: { color: "rgba(255,47,63,1)" },
label: [{ text: "Claim 503", position: "s" }],
},
654: {
color: "rgba(255,255,255,1)",
data: { kind: "person" },
fontIcon: { color: "#A674BA", fontFamily: "Material Icons", text: "person" },
border: { color: "rgba(166,116,186,1)" },
label: [{ text: "Marietta Turner Jr.", position: "s" }],
},
655: {
color: "rgba(255,255,255,1)",
data: { kind: "damage" },
fontIcon: {
color: "#FF8615",
fontFamily: "Material Icons",
text: "directions_car",
},
border: { color: "rgba(255,134,21,1)" },
label: [{ text: "Damage 712", position: "s" }],
},
656: {
color: "rgba(255,255,255,1)",
data: { kind: "damage" },
fontIcon: {
color: "#FF8615",
fontFamily: "Material Icons",
text: "directions_car",
},
border: { color: "rgba(255,134,21,1)" },
label: [{ text: "Damage 708", position: "s" }],
},
657: {
color: "rgba(255,255,255,1)",
data: { kind: "damage" },
fontIcon: {
color: "#FF8615",
fontFamily: "Material Icons",
text: "directions_car",
},
border: { color: "rgba(255,134,21,1)" },
label: [{ text: "Damage 233", position: "s" }],
},
658: {
color: "rgba(255,255,255,1)",
data: { kind: "damage" },
fontIcon: {
color: "#FF8615",
fontFamily: "Material Icons",
text: "directions_car",
},
border: { color: "rgba(255,134,21,1)" },
label: [{ text: "Damage 912", position: "s" }],
},
659: {
color: "rgba(255,255,255,1)",
data: { kind: "damage" },
fontIcon: {
color: "#FF8615",
fontFamily: "Material Icons",
text: "directions_car",
},
border: { color: "rgba(255,134,21,1)" },
label: [{ text: "Damage 500", position: "s" }],
},
660: {
color: "rgba(255,255,255,1)",
data: { kind: "damage" },
fontIcon: {
color: "#FF8615",
fontFamily: "Material Icons",
text: "directions_car",
},
border: { color: "rgba(255,134,21,1)" },
label: [{ text: "Damage 120", position: "s" }],
},
661: {
color: "rgba(255,255,255,1)",
data: { kind: "damage" },
fontIcon: {
color: "#FF8615",
fontFamily: "Material Icons",
text: "directions_car",
},
border: { color: "rgba(255,134,21,1)" },
label: [{ text: "Damage 855", position: "s" }],
},
662: {
color: "rgba(255,255,255,1)",
data: { kind: "damage" },
fontIcon: {
color: "#FF8615",
fontFamily: "Material Icons",
text: "directions_car",
},
border: { color: "rgba(255,134,21,1)" },
label: [{ text: "Damage 280", position: "s" }],
},
663: {
color: "rgba(255,255,255,1)",
data: { kind: "claim" },
fontIcon: { color: "#FF2F3F", fontFamily: "Material Icons", text: "receipt" },
border: { color: "rgba(255,47,63,1)" },
label: [{ text: "Claim 411", position: "s" }],
},
"252-250": { id1: "252", id2: "250", width: 3 },
"293-291": { id1: "293", id2: "291", width: 3 },
"252-249": { id1: "252", id2: "249", width: 3 },
"293-290": { id1: "293", id2: "290", width: 3 },
"253-252": { id1: "253", id2: "252", width: 3 },
"294-293": { id1: "294", id2: "293", width: 3 },
"447-294": { id1: "447", id2: "294", width: 3 },
"476-253": { id1: "476", id2: "253", width: 3 },
"663-253": { id1: "663", id2: "253", width: 3 },
"447-3": { id1: "447", id2: "3", width: 3 },
"476-3": { id1: "476", id2: "3", width: 3 },
"663-3": { id1: "663", id2: "3", width: 3 },
"445-447": { id1: "445", id2: "447", width: 3 },
"446-447": { id1: "446", id2: "447", width: 3 },
"469-476": { id1: "469", id2: "476", width: 3 },
"470-476": { id1: "470", id2: "476", width: 3 },
"471-476": { id1: "471", id2: "476", width: 3 },
"472-476": { id1: "472", id2: "476", width: 3 },
"473-476": { id1: "473", id2: "476", width: 3 },
"474-476": { id1: "474", id2: "476", width: 3 },
"475-476": { id1: "475", id2: "476", width: 3 },
"655-663": { id1: "655", id2: "663", width: 3 },
"656-663": { id1: "656", id2: "663", width: 3 },
"657-663": { id1: "657", id2: "663", width: 3 },
"658-663": { id1: "658", id2: "663", width: 3 },
"659-663": { id1: "659", id2: "663", width: 3 },
"660-663": { id1: "660", id2: "663", width: 3 },
"661-663": { id1: "661", id2: "663", width: 3 },
"662-663": { id1: "662", id2: "663", width: 3 },
"447-292": { id1: "447", id2: "292", width: 3 },
"476-251": { id1: "476", id2: "251", width: 3 },
"663-251": { id1: "663", id2: "251", width: 3 },
"447-437": { id1: "447", id2: "437", width: 3 },
"476-460": { id1: "476", id2: "460", width: 3 },
"476-464": { id1: "476", id2: "464", width: 3 },
"663-654": { id1: "663", id2: "654", width: 3 },
"447-441": { id1: "447", id2: "441", width: 3 },
"447-444": { id1: "447", id2: "444", width: 3 },
"476-468": { id1: "476", id2: "468", width: 3 },
"garage-damages-3-445": {
id1: "3",
id2: "445",
width: 3,
},
"garage-damages-3-446": {
id1: "3",
id2: "446",
width: 3,
},
"garage-damages-3-469": {
id1: "3",
id2: "469",
width: 3,
},
"garage-damages-3-470": {
id1: "3",
id2: "470",
width: 3,
},
"garage-damages-3-471": {
id1: "3",
id2: "471",
width: 3,
},
"garage-damages-3-472": {
id1: "3",
id2: "472",
width: 3,
},
"garage-damages-3-473": {
id1: "3",
id2: "473",
width: 3,
},
"garage-damages-3-474": {
id1: "3",
id2: "474",
width: 3,
},
"garage-damages-3-475": {
id1: "3",
id2: "475",
width: 3,
},
"garage-damages-3-655": {
id1: "3",
id2: "655",
width: 3,
},
"garage-damages-3-656": {
id1: "3",
id2: "656",
width: 3,
},
"garage-damages-3-657": {
id1: "3",
id2: "657",
width: 3,
},
"garage-damages-3-658": {
id1: "3",
id2: "658",
width: 3,
},
"garage-damages-3-659": {
id1: "3",
id2: "659",
width: 3,
},
"garage-damages-3-660": {
id1: "3",
id2: "660",
width: 3,
},
"garage-damages-3-661": {
id1: "3",
id2: "661",
width: 3,
},
"garage-damages-3-662": {
id1: "3",
id2: "662",
width: 3,
},
"people-252-460": {
id1: "252",
id2: "460",
width: 3,
},
"people-252-464": {
id1: "252",
id2: "464",
width: 3,
},
"people-252-468": {
id1: "252",
id2: "468",
width: 3,
},
"people-252-654": {
id1: "252",
id2: "654",
width: 3,
},
"people-293-437": {
id1: "293",
id2: "437",
width: 3,
},
"people-293-441": {
id1: "293",
id2: "441",
width: 3,
},
"people-293-444": {
id1: "293",
id2: "444",
width: 3,
},
"people-437-441": {
id1: "437",
id2: "441",
width: 3,
},
"people-437-444": {
id1: "437",
id2: "444",
width: 3,
},
"people-441-444": {
id1: "441",
id2: "444",
width: 3,
},
"people-460-464": {
id1: "460",
id2: "464",
width: 3,
},
"people-460-468": {
id1: "460",
id2: "468",
width: 3,
},
"people-464-468": {
id1: "464",
id2: "468",
width: 3,
},
"person-garage-3-252": {
id1: "3",
id2: "252",
width: 3,
},
"person-garage-3-293": {
id1: "3",
id2: "293",
width: 3,
},
};
const lorem =
"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.";
export const loremArray = [
lorem,
`${lorem}\n\n${lorem}`,
`${lorem}\n\n${lorem}\n\n${lorem}`,
`${lorem}\n\n${lorem}\n\n${lorem}\n\n${lorem}`,
lorem.slice(0, 100),
]; <!doctype html>
<html>
<head>
<link rel="stylesheet" href="./style.css" />
</head>
<body>
<div id="regraph" style="height: 100vh"></div>
<script type="module" src="./code.jsx"></script>
</body>
</html> /* Import for Google's Material Icons */
@import url("https://fonts.googleapis.com/icon?family=Material+Icons");