Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Html to Canvas to Base64 to PDF

So I'm trying to work out a way to convert html to pdf entirely on the clients side, somewhere I read that you can convert a base64 to a pdf on the client side, and from there I remembered you can create a canvas from html and a Base 64 from a Canvas. So I got everything working up until converting the base64 to pdf. I cant seem to get the last part to work. Here is my code

var element =  document.getElementById('canvas1');
        console.log(element);
if (typeof(element) == 'undefined' || element == null)
{
        html2canvas(document.body, {
  onrendered: function(canvas) {
    document.body.appendChild(canvas);
    $('canvas').attr( 'id', 'canvas1' );
  }
});
    }
    setTimeout(function() {
 canvas = document.getElementById("canvas1");
var jpegUrl = canvas.toDataURL("image/jpeg");
var pngUrl = canvas.toDataURL(); // PNG is the default
console.log("jp"+jpegUrl);
console.log("png"+pngUrl);
window.open("data:application/pdf;base64," + pngUrl);
console.log('new');
},500);

I'm using the html2canvas library to convert the html to canvas and attach it to the body, and so far that works flawlessly. But why cant I convert the base64 to pdf. It just opens a blank page with the url "data:" as if its not loading the base64 string with it. Any help would be appreciated, I might be over thinking this and im sure there is an easier way. Here is a jsfiddle of the problem.

like image 687
Joe Komputer Avatar asked Dec 14 '22 22:12

Joe Komputer


1 Answers

Problem

I guess first off, we'll go through what's wrong with your code I guess. First of all, rather than setTimeout, the "pdf-ing" code should be inside onRendered as potentially, you might find that the code runs before the canvas could be rendered.

Next, how you're prefixing the PDF from the datauri. with a data:application/pdf;base64,. Canvas outputs the full datauri already. For example data:image/PNG;base64,BASE64DATAHERE.... What your're doing is prefixing the data:application/pdf:base64, so it'll look like data:application/pdf:base64,data:image/PNG;base64,BASE64DATAHERE... which is wrong. You need to replace the prefixes completely. I made a simple demo of this here where I replaced a PNG datauri with jpeg.

Code is

//datauri is an image with a datauri of a png
prefixJpg.src="data:image/jpeg;base64,"+datauri.src
replaceJpg.src="data:image/jpeg;base64,"+datauri.src.substring(22) 
//remove the first 22 characters

The output should look like

enter image description here

The prefixing one cannot be rendered as it's not a valid base64 image data.

Continuing on, looking at the output, we note that, the image with the jpeg datauri, has transparent background. JPEG can't have transparent background. So in essence, the data is still PNG, and only because lenient renderer that the browser accepts that though it says it's jpeg it's actually still png, and still show it.

This brings us to the final problem with your code. As far as I know, in most cases, you can't actually convert something by just changing the datauri. I tried testing this by manually changing an image datauri to a datauri with pdf prefix. Chrome loaded it's builtin pdf plugin but it "failed to load".


Solution

Looking around there's actually a pdf writer written in javascript:

jsPDF

It has a pdf rendered that you can use and has all sorts of methods that you can look at. As an example, you can create a pdf from a datauri with the .addImage() method. I updated your code to use jsPDF.

 html2canvas(document.body, {
  onrendered: function(canvas) {
    var pdf = new jsPDF();
    var marginLeft=20;
    var marginRight=20
    pdf.addImage(canvas.toDataURL("image/jpeg"),"jpeg",marginLeft,marginRight)
    window.location=pdf.output("datauristring")
  }
});

And it seems to work well.

like image 162
mfirdaus Avatar answered Dec 17 '22 13:12

mfirdaus