Sam Bobel
Sam Bobel

Reputation: 1824

Using PDFkit in browser, inserting an image from a link

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 command to do:

doc.image('http://upload.wikimedia.org/wikipedia/commons/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?

Upvotes: 5

Views: 10191

Answers (4)

Gerard Coma
Gerard Coma

Reputation: 21

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'
        });

Upvotes: 2

Sark Peha
Sark Peha

Reputation: 501

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.org/wikipedia/commons/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.

Upvotes: 2

Gaston Elhordoy
Gaston Elhordoy

Reputation: 181

This 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.org/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');
  });
});

Upvotes: 6

reaanb
reaanb

Reputation: 10066

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.com/devongovett/pdfkit/issues/354#issuecomment-68666894, where the same issue is discussed as applied to fonts.

Upvotes: 3

Related Questions