I'm currently using d3.js version 5 and was wondering if there is an actual way for me to export my charts to PDF?
For example in the screenshot provided below, there is a button for me to use or an option for me to export that specific chart to PDF
Asked
Active
Viewed 4,694 times
6

Elijah Leis
- 367
- 7
- 18
-
Do you need the conversion to happen on client side ([`jsPDF`](https://parall.ax/products/jspdf)) or running it server side ([`wkhtmltopdf`](https://wkhtmltopdf.org/index.html)) will be alright? – timur May 18 '20 at 03:39
1 Answers
7
You can use PDFKIT library to achieve this, this snippet is inspired by the example they provided in the live demo, I just extended it by adding the D3.js
example along javascript to retrieve the HTML text.
Update: I added custom implementation to allow custom file name for the downloaded PDF, basically I create <a>
tag, append it to body, then assign download
attribute to it, and the href
attribute contains the blob
object URL we created.
NOTE: this will not work in the snippet since its a sandbox, it should work fine in your local machine and production.
const svgToPdfExample = (svg) => {
const doc = new window.PDFDocument();
const chunks = [];
const stream = doc.pipe({
// writable stream implementation
write: (chunk) => chunks.push(chunk),
end: () => {
const pdfBlob = new Blob(chunks, {
type: "application/octet-stream",
});
var blobUrl = URL.createObjectURL(pdfBlob);
//window.open(`${blobUrl}?customfilename.pdf`);
/* custom file name download */
const a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
a.href = blobUrl;
a.download = "test.pdf"; // <---- file name
a.click();
window.URL.revokeObjectURL(url);
},
// readable streaaam stub iplementation
on: (event, action) => {},
once: (...args) => {},
emit: (...args) => {},
});
window.SVGtoPDF(doc, svg, 0, 0);
doc.end();
};
document.getElementById("download").addEventListener("click", function () {
const svgElement = document.getElementById("svg");
svgToPdfExample(svgElement.innerHTML);
});
var margin = { top: 20, right: 20, bottom: 70, left: 40 },
width = 900 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%Y-%m").parse;
var x = d3.scale.ordinal().rangeRoundBands([0, width], 0.05);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg
.axis()
.scale(x)
.orient("bottom")
.tickFormat(d3.time.format("%Y-%m"));
var yAxis = d3.svg.axis().scale(y).orient("left").ticks(10);
var svg = d3
.select("body")
.append("svg")
.attr("id", "svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.csv(
"https://gist.githubusercontent.com/d3noob/8952219/raw/5017886e4fe22af2a7e06e20cf381bcf09cdc6db/bar-data.csv",
function (error, data) {
data.forEach(function (d) {
d.date = parseDate(d.date);
d.value = +d.value;
});
x.domain(
data.map(function (d) {
return d.date;
})
);
y.domain([
0,
d3.max(data, function (d) {
return d.value;
}),
]);
svg
.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)");
svg
.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Value ($)");
svg
.selectAll("bar")
.data(data)
.enter()
.append("rect")
.style("fill", "steelblue")
.attr("x", function (d) {
return x(d.date);
})
.attr("width", x.rangeBand())
.attr("y", function (d) {
return y(d.value);
})
.attr("height", function (d) {
return height - y(d.value);
});
}
);
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
<!DOCTYPE html>
<meta charset="utf-8">
<head>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/pdfkit@0.10.0/js/pdfkit.standalone.js"></script>
<script src="https://cdn.jsdelivr.net/npm/svg-to-pdfkit@0.1.8/source.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<button id="download">Donwload SVG as PDF</button>
</body>

ROOT
- 11,363
- 5
- 30
- 45
-
ROOT, can we pass arbitrary name for the file going to be downloaded with extension like `pdf-download.pdf`? – intekhab Jun 14 '20 at 14:26
-
@intekhab, will check if this is applicable and update my answer accordingly. – ROOT Jun 14 '20 at 15:17
-
1@intekhab, I updated my answer, please have a look and let me know if you need any help. – ROOT Jun 14 '20 at 16:22
-
1Thanks @ROOT, working good!. I need to use some text(html element) at top/bottom of svg in PDF(it's not only svg); can we do that using this library? I have done my job using jsPDF(in jsPDF we can add jsPDF.fromHTML() multiple times). What is your view on these two library(jsPDF and pdfkit)? Which one is better? Any documentation reference will be appreciated. https://github.com/foliojs/pdfkit/issues/480, looks like not possible to add text(or html text). Is it? – intekhab Jun 14 '20 at 16:45
-
@intekhab, I can't give you much insights on jsPDF since I didn't use it before, but just by looking at the code for both projects on github, jsPDF is much smaller in size compared to PDFKit, (jsPDF is ~ 300KB compared to PDFkit ~3MB) PDFkit rich in features, but you should consider the size if its a concern for you. There is a work around in jsPDF for SVG here: https://stackoverflow.com/questions/27706956/render-svg-to-pdf-using-jspdf I would consider using jsPDF if your task is just as small as saving HTML/SVG to PDF, size of the lib is a real issue here. hope this answered your questions – ROOT Jun 14 '20 at 18:38