Is there a simple way to get an image from a url to put in a PDFKit pdf?
I have a PDF being automatically generated in-browser. There's an image I want included, to which I have a URL. The catch is that I'm generating the PDF in-browser. Since I have the URL available from the internet, it seems like there should be an easy way to turn that image into something readable by PDFKit.
Is there a way for Javascript to turn an image URL into a buffer readable by PDFKit?
What I want is what you'd like the following mand to do:
doc.image('.jpg')
Thanks in advance. The solutions I found online have your server take in the link, and respond with a buffer. Is this the only way? Or is there a way all in-browser with no http posting?
Is there a simple way to get an image from a url to put in a PDFKit pdf?
I have a PDF being automatically generated in-browser. There's an image I want included, to which I have a URL. The catch is that I'm generating the PDF in-browser. Since I have the URL available from the internet, it seems like there should be an easy way to turn that image into something readable by PDFKit.
Is there a way for Javascript to turn an image URL into a buffer readable by PDFKit?
What I want is what you'd like the following mand to do:
doc.image('http://upload.wikimedia/wikipedia/mons/0/0c/Cow_female_black_white.jpg')
Thanks in advance. The solutions I found online have your server take in the link, and respond with a buffer. Is this the only way? Or is there a way all in-browser with no http posting?
Share Improve this question asked Oct 7, 2014 at 17:22 Sam BobelSam Bobel 1,8242 gold badges16 silver badges26 bronze badges 2- The server you're fetching the image from will likely either need to be yours (same origin) or need to respond with appropriate CORS headers. Wikimedia does, but most servers will not. If you need to work with arbitrary URLs, then your server will have to proxy the image. – josh3736 Commented Oct 7, 2014 at 17:28
- It's my server. So in that case is there an easy way to do it? – Sam Bobel Commented Oct 7, 2014 at 17:38
4 Answers
Reset to default 6This is a pretty old question but I'll add my notes since it's the first suggestion when looking for "pdfkit browser image" on Google.
I based my solution on the data uri option supported by PDFKit:
Just pass an image path, buffer, or data uri with base64 encoded data to the image method along with some optional arguments.
So after a quick look around I found the general approach to get a data uri from an image URL was using canvas, like in this post. Putting it together in PDFKit's interactive browser demo:
function getDataUri(url, callback) {
var image = new Image();
image.crossOrigin = 'anonymous'
image.onload = function () {
var canvas = document.createElement('canvas');
canvas.width = this.naturalWidth; // or 'width' if you want a special/scaled size
canvas.height = this.naturalHeight; // or 'height' if you want a special/scaled size
canvas.getContext('2d').drawImage(this, 0, 0);
// // Get raw image data
// callback(canvas.toDataURL('image/png').replace(/^data:image\/(png|jpg);base64,/, ''));
// ... or get as Data URI
callback(canvas.toDataURL('image/png'));
};
image.src = url;
}
// Usage
getDataUri('http://pdfkit/docs/img/14.png', function(dataUri) {
// create a document and pipe to a blob
var doc = new PDFDocument();
var stream = doc.pipe(blobStream());
doc.image(dataUri, 150, 200, {
width: 300
});
// end and display the document in the iframe to the right
doc.end();
stream.on('finish', function() {
iframe.src = stream.toBlobURL('application/pdf');
});
});
I retrieve the image via AJAX as a base64-encoded string, then use the following code to convert the base64-encoded string into a usable buffer:
var data = atob(base64);
var buffer = [];
for (var i = 0; i < data.length; ++i)
buffer.push(data.charCodeAt(i));
buffer._isBuffer = true;
buffer.readUInt16BE = function(offset, noAssert) {
var len = this.length;
if (offset >= len) return;
var val = this[offset] << 8;
if (offset + 1 < len)
val |= this[offset + 1];
return val;
};
pdf.image(buffer);
See also https://github./devongovett/pdfkit/issues/354#issuement-68666894, where the same issue is discussed as applied to fonts.
I'll weigh my 2 cents on the issue as I just spent a good deal of time getting it to work. It's a medley of answers I've found googling the issue.
var doc = new PDFDocument();
var stream = doc.pipe(blobStream());
var files = {
img1: {
url: 'http://upload.wikimedia/wikipedia/mons/0/0c/Cow_female_black_white.jpg',
}
};
Use the above object at a place to store all of the images and other files needed in the pdf.
var filesLoaded = 0;
//helper function to get 'files' object with base64 data
function loadedFile(xhr) {
for (var file in files) {
if (files[file].url === xhr.responseURL) {
var unit8 = new Uint8Array(xhr.response);
var raw = String.fromCharCode.apply(null,unit8);
var b64=btoa(raw);
var dataURI="data:image/jpeg;base64,"+b64;
files[file].data = dataURI;
}
}
filesLoaded += 1;
//Only create pdf after all files have been loaded
if (filesLoaded == Object.keys(files).length) {
showPDF();
}
}
//Initiate xhr requests
for (var file in files) {
files[file].xhr = new XMLHttpRequest();
files[file].xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
loadedFile(this);
}
};
files[file].xhr.responseType = 'arraybuffer';
files[file].xhr.open('GET', files[file].url);
files[file].xhr.send(null);
}
function showPDF() {
doc.image(files.img1.data, 100, 200, {fit: [80, 80]});
doc.end()
}
//IFFE that will download pdf on load
var saveData = (function () {
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
return function (blob, fileName) {
var url = window.URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
};
}());
stream.on('finish', function() {
var blob = stream.toBlob('application/pdf');
saveData(blob, 'aa.pdf');
});
The biggest issue I came across was getting the info from the arraybuffer
type to a string with base64 data. I hope this helps!
Here is the js fiddle where most of the xhr code came from.
I did it using NPM package axios to get a base64 encoded buffer:
on the project folder:
npm i axios
code:
var axios = require('axios');
let image = await axios.get("url", {responseType: 'arraybuffer'});
doc.image(image.data, 12, h, {
width: 570,
align: 'center',
valign: 'center'
});