HTML5 canvas, convert canvas to PDF with jspdf.js - javascript

I am trying to convert HTML5 canvas to PDF in JavaScript but I get a black background PDF. I tried to change the background color but still get black. The following is code I am trying:
Canvas = document.getElementById("chart");
Context = Canvas.getContext("2d");
var imgData = Canvas.toDataURL('image/jpeg');
var pdf = new jsPDF('landscape');
pdf.addImage(imgData, 'JPEG', 0, 0, 1350, 750);
pdf.save('download.pdf');
If you have any idea, I'd appreciate it very much.

A good approach is to use combination of jspdf.js and html2canvas. I have made a jsfiddle for you.
<canvas id="canvas" width="480" height="320"></canvas>
<button id="download">Download Pdf</button>
'
html2canvas($("#canvas"), {
onrendered: function(canvas) {
var imgData = canvas.toDataURL(
'image/png');
var doc = new jsPDF('p', 'mm');
doc.addImage(imgData, 'PNG', 10, 10);
doc.save('sample-file.pdf');
}
});
jsfiddle: http://jsfiddle.net/rpaul/p4s5k59s/5/

One very simple solution is that you are saving the image as jpeg. Saving instead as png works fine for my application. Of note, Blizzard's response also includes the print as png, and also produces non-black fill for transparent layers in the canvas.
var canvas = event.context.canvas;
var data = canvas.toDataURL('image/png');
instead of
var canvas = event.context.canvas;
var data = canvas.toDataURL('image/jpg');

I had the same problem, e.g. the first time when i create a pdf the canvas image is ok, but all the other next, came out black. No picture!
The workaround i found is as follows: after each call to pdf.addImage() i redraw the picture in the canvas. It works now fine for me.
EDIT: As requested, here some more details:
Let say i have a canvas drawing function like this (just an example, it doesn't matter):
function drawCanvas(cv) {
for(var i=0; i<cv.height; i++) {
for(var j=0, d=0; j<cv.width; j++) {
cv.data[d] = 0xff0000;
d += 4;
}
}
}
I had to fix my printing function as follows:
var cv=document.getElementById('canvas');
printPDF(cv) {
var imgData=cv.toDataURL("image/jpeg", 1.0);
var doc=new jsPDF("p","mm","a4");
doc.addImage(imgData,'JPEG',15,40,180,180);
drawCanvas(cv); // <--- this line is needed, draw again
}
drawCanvas(cv); // <--- draw my image to the canvas, ok
printPDF(cv); // first time is fine
printPDF(cv); // second time without repaint would be black
I admit, i did'nt investigate further, just happy that this works.

At first, you need to set the desired background color on the canvas before getting the data. Then, you need to draw jpeg image on the canvas.

Just change the format JPEG to PNG
pdf.addImage(imgData, 'PNG', 0, 0, 1350, 750);

Related

Execute Function After Canvas toDataURL() Function is Complete

I'm converting images to base64 using canvas. What i need to do is convert those images and then show the result to the user (original image and base64 version). Everything works as expected with small images, but when i try to convert large images (>3MB) and the conversion time increases, the base64 version is empty.
This might be is caused because the result is shown before the toDataURL() function is completed.
I need to show the result after all the needed processing has ended, for testing purposes.
Here's my code:
var convertToBase64 = function(url, callback)
{
var image = new Image();
image.onload = function ()
{
//create canvas and draw image...
var imageData = canvas.toDataURL('image/png');
callback(imageData);
};
image.src = url;
};
convertToBase64('img/circle.png', function(imageData)
{
window.open(imageData);
});
Even though i'm using image.onload() with a callback, i'm unable to show the result after the toDataURL() has been processed.
What am i doing wrong?
UPDATE: I tried both the solutions below and they didn't work. I'm using AngularJS and Electron in this project. Any way i can force the code to be synchronous? Or maybe some solution using Promises?
UPDATE #2: #Kaiido pointed out that toDataURL() is in fact synchronous and this issue is more likely due to maximum URI length. Since i'm using Electron and the image preview was for testing purposes only, i'm going to save the file in a folder and analise it from there.
Your code seems absolutely fine. Not sure why isn't working you. Maybe, there are some issues with your browser. Perhaps try using a different one. Also you could use a custom event, which gets triggered when the image conversion is competed.
// using jQuery for custom event
function convertToBase64(url) {
var image = new Image();
image.src = url;
image.onload = function() {
var canvas = document.createElement('canvas');
canvas.width = image.width;
canvas.height = image.height;
var ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0);
var imageData = canvas.toDataURL();
$(document).trigger('conversionCompleted', imageData);
};
};
convertToBase64('4mb.jpg');
$(document).on('conversionCompleted', function(e, d) {
window.open(d);
});
This approach might work for you. It shows the image onscreen using the native html element, then draws it to a canvas, then converts the canvas to Base64, then clears the canvas and draws the converted image onto the canvas. You can then scroll between the top image (original) and the bottom image (converted). I tried it on large images and it takes a second or two for the second image to draw but it seems to work...
Html is here:
<img id="imageID">
<canvas id="myCanvas" style="width:400;height:400;">
</canvas>
Script is here:
var ctx;
function convertToBase64(url, callback)
{
var image = document.getElementById("imageID");
image.onload = function() {
var canvas = document.getElementById("myCanvas");
canvas.width = image.naturalWidth;
canvas.height = image.naturalHeight;
ctx = canvas.getContext("2d");
ctx.drawImage(image,0,0);
var imageData = canvas.toDataURL('image/png');
ctx.fillStyle ="#FFFFFF";
ctx.fillRect(0,0,canvas.width,canvas.height);
callback(imageData);
};
image.src = url;
};
var imagename = 'images/bigfiletest.jpg';
window.onload = function () {
convertToBase64(imagename, function(imageData) {
var myImage = new Image();
myImage.src = imageData;
ctx.drawImage(myImage,0,0);
});
}
Note that I also tried it without the callback and it worked fine as well...

Pdf file size too big created using jspdf

I am using jspdf for creating PDF inside browser. I am having multiple charts having svg as chart Data. For adding data to pdf I am converting svg to png using canvas and then Base64 Data using canvas.toDataURL method. After all this conversions size of the file created by jspdf is huge (about 50 MB).
Below is the code for div of chart data and canvas.
newdiv = document.createElement("div");
newdiv.className = "big_Con_graph big_Con_graph0";
newdiv.style.height = "0px";
newdiv.id = "big_Con_graph" + id;
below is the dimensions for SVG chart load.
document.getElementById("big_Con_graph" + id).style.display = "block";
var big_chartReference = FusionCharts("big_myChartId"+id);
if(big_chartReference != null){
big_chartReference.dispose();
}
var big_width = "1088";
var big_height = "604";
now below is the code for conversion of above graph SVG data and adding to PDF.
var elem_graph = $($('.big_Con_graph,big_Con_graph0')[count]).clone(true);
svgString = $(elem_graph).find("span").html();
var img = document.createElement('img');
var DOMURL = self.URL || self.webkitURL || self;
var svg = new Blob([svgString], {type: "image/svg+xml;charset=utf-8"});
var url = DOMURL.createObjectURL(svg);
img.onload = pdfAfterImageLoad(img,pdf,imgLoadSequence,DOMURL,totalReports,reportName);
img.src = url;
this is the code for PDFAfterImageLoad function:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
var png = canvas.toDataURL("image/png");
pdf.addImage(png, 'PNG', leftmargin, 120, 485, 270);
I am using png, so imagequality parameter can not be used.
Can anyone help me decrease the file size?
You need to compress the images in the PDF's that you are generating. Try using Deflate.js and adler32cs.js and use the compress parameter in both jsPDF and addImage functions that you are using. For eg :
var doc = new jsPDF('p', 'pt','a4',true);
make sure you set the last parameter as 'true' refer to : https://github.com/MrRio/jsPDF/blob/ddbfc0f0250ca908f8061a72fa057116b7613e78/jspdf.js#L146
Go through it and you can clearly see that the last parameter is for enabling compression.
Also use :
pdf.addImage(png, 'PNG', leftmargin, 120, 485, 270,'','FAST');
instead of
pdf.addImage(png, 'PNG', leftmargin, 120, 485, 270);
you can choose between NONE, FAST, MEDIUM and SLOW, whichever suits you best.
If you add several images to one document, use
pdf.addImage(png, 'PNG', leftmargin, 120, 485, 270, undefined,'FAST');
not
pdf.addImage(png, 'PNG', leftmargin, 120, 485, 270,'','FAST');
otherwise the first image will substitute all others.
Perfect. PDF size 90mb to 3mb with great quality.
pdf.addImage(png, 'PNG', 0, 0, 485, 270, undefined,'FAST');
In my case did not work with compress parameter. So, I resized the current image on my own with the following function:
function resizeBase64Img(base64, width, height) {
var canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
var context = canvas.getContext("2d");
var deferred = $.Deferred();
$("<img/>").attr("src", "data:image/gif;base64," + base64).load(function() {
context.scale(width/this.width, height/this.height);
context.drawImage(this, 0, 0);
deferred.resolve($("<img/>").attr("src", canvas.toDataURL()));
});
return deferred.promise();
}
And call it as follows:
//select my image in base64
var imageStr64 = "/9j/4RiDRXhpZgAATU0AKgA...";
//resize image, add image and create the pdf
resizeBase64Img(imageStr64, 1000, 1000).then(function(newImg){
doc.addImage($(newImg).attr('src'), 15, 90, 180,180);
doc.save('mypdf.pdf');
});
You can play with 'width' and 'height' parameters in resizeBase64Img function in order to generate heavy pdf with better or worst image quality.
Have you tried canvg? It's not very likely that it will decrease the filesize, but at least you can try.
See a snippet in this reply.
I've tried the compress parameter and passing compression:'MEDIUM' toe the addImage method as suggested but nothing happened
What worked for me was converting the canvas to DataUrl base64 string and passing a compression value
pdf.addImage({ imageData: canvas.toDataURL('image/jpeg', 0.6),format: 'JPEG', x: 2, y: 100, width: newImageWidth, height: newImageHeight});
This way the image is compressed before it is added to the PDF

how to set png image color in winjs

Hi I am working on a window 8 app with the help of html and javascript. Where I have to show some png images received from a api and have to set their color as received from the api. I have googled a lot but did not find a solution. Although I know in xaml it can be done some how like this
<Rectangle Fill="{Binding Path=widget.logo, Converter={StaticResource PSWidgetLogoColorConverter1}}" Height="60" Width="60">
<Rectangle.OpacityMask>
<ImageBrush ImageSource="{Binding Path=widget.logo,Converter={StaticResource PSWidgetImageConverter1}}"/>
</Rectangle.OpacityMask>
</Rectangle>
but can't figure out how to do it here.
Your valuable suggestions are welcome pls help.
I think you have to change the color inside a canvas element.
add the canvas tag to your app:
<canvas id="myCanvas" />
load the image inside the canvas:
var myImage = new Image();
canvas = document.getElementById("myCanvas");
if (canvas.getContext) {
ctx = canvas.getContext("2d");
myImage.onload = function() {
ctx.drawImage(myImage, 0, 0);
}
myImage.src = "kestral.png";
}
get the image data and change it for your needs:
getImageData returns all rgba values inside the canvas at position x,y,width and height. So it is: getImageData(x,y,width,height). As a result you get a Array with the size: width*height*4 You can then loop over it and change every rgba color:
myImage = ctx.getImageData(0, 0, 200, 200);
var picLength = 200*200;
// Loop through data.
for (var i = 0; i < picLength * 4; i += 4) {
// First bytes are red bytes.
// Second bytes are green bytes.
// Third bytes are blue bytes.
// Fourth bytes are alpha bytes
//check for full alpha
if(myImage.data[i+3] == 255) {
//change it to black
myImage.data[i]=0;
myImage.data[i+1]=0;
myImage.data[i+2]=0;
myImage.data[i+3]=0;
}
}
put the changed image data back
ctx.putImageData(myImage, 0, 0);
Here is a good and complete example on msdn.

JavaScript filter image color

Hey, I'm looking for a way to take a black and white img element, and using JavaScript, specify an RGB value so that the image becomes that color. Any ideas (aside from libraries)?
Also I'm trying to do this with IE only. The reason I'm doing it in IE only is because I'm making a small sidebar gadget.
In Internet Explorer, you could use Visual Filters.
Edit: you want to use the Light filter, here is an exemple
<STYLE>
.aFilter {
filter:light();
}
</STYLE>
<SCRIPT>
window.onload=fnInit;
function fnInit(){
oDiv.filters[0].addAmbient(50,20,180,100);
}
</SCRIPT>
with filter: <img CLASS="aFilter" ID="oDiv" src="http://cache2.artprintimages.com/p/LRG/8/853/2USY000Z/black-and-white-cats.jpg">
without: <img src="http://cache2.artprintimages.com/p/LRG/8/853/2USY000Z/black-and-white-cats.jpg">
Something like this?
Edit: Ah, no canvas. No worries.
<html>
<head>
<script type="text/javascript">
function createCanvas(image){
// create a new canvas element
var myCanvas = document.createElement("canvas");
var myCanvasContext = myCanvas.getContext("2d");
var imgWidth=image.width;
var imgHeight=image.height;
// set the width and height to the same as the image
myCanvas.width= imgWidth;
myCanvas.height = imgHeight;
// draw the image
myCanvasContext.drawImage(image,0,0);
// get all the image data into an array
var imageData = myCanvasContext.getImageData(0,0, imgWidth, imgHeight);
// go through it all...
for (j=0; j<imageData.width; j++)
{
for (i=0; i<imageData.height; i++)
{
// index: red, green, blue, alpha, red, green, blue, alpha..etc.
var index=(i*4)*imageData.width+(j*4);
var red=imageData.data[index];
var alpha=imageData.data[index+3];
// set the red to the same
imageData.data[index]=red;
// set the rest to black
imageData.data[index+1]=0;
imageData.data[index+2]=0;
imageData.data[index+3]=alpha;
delete c;
}
}
// put the image data back into the canvas
myCanvasContext.putImageData(imageData,0,0,0,0, imageData.width, imageData.height);
// append it to the body
document.body.appendChild(myCanvas);
}
function loadImage(){
var img = new Image();
img.onload = function (){
createCanvas(img);
}
img.src = "monkey.jpg";
}
</script>
</head>
<body onload="loadImage()">
</body>
</html>
The only way you'll be able to do this in JavaScript is with the <canvas> tag. Here is an excellent tutorial if you're interested in learning how to use it.
Edit: I'm not an expert on MS proprietary filters, but here are the Microsoft docs for image filters in IE.

How to save/export a DOM element to an image?

I have a web page which has a form element (with its ID known) and
inside the form there are multiple DIVs, and the position of each div
may be changed.
What I'd like to do is:
a) Save the current state of this form
// var currentForm=document.forms['myFrm'].innerHTML;
would probably suffice...
b) Save or export the entire form with the most current position of each DIV
to an image file.
// how to save/export the javascript var of currentForm to an image
file is the key question.
Any help/pointer would be appreciated.
After hours of research, I finally found a solution to take a screenshot of an element, even if the origin-clean FLAG is set (to prevent XSS), that´s why you can even capture for example Google Maps (in my case). I wrote an universal function to get a screenshot. The only thing you need in addition is the html2canvas library (https://html2canvas.hertzen.com/).
Example:
getScreenshotOfElement($("div#toBeCaptured").get(0), 0, 0, 100, 100, function(data) {
// in the data variable there is the base64 image
// exmaple for displaying the image in an <img>
$("img#captured").attr("src", "data:image/png;base64,"+data);
}
Keep in mind console.log() and alert() won´t generate an output if the size of the image is great.
Function:
function getScreenshotOfElement(element, posX, posY, width, height, callback) {
html2canvas(element, {
onrendered: function (canvas) {
var context = canvas.getContext('2d');
var imageData = context.getImageData(posX, posY, width, height).data;
var outputCanvas = document.createElement('canvas');
var outputContext = outputCanvas.getContext('2d');
outputCanvas.width = width;
outputCanvas.height = height;
var idata = outputContext.createImageData(width, height);
idata.data.set(imageData);
outputContext.putImageData(idata, 0, 0);
callback(outputCanvas.toDataURL().replace("data:image/png;base64,", ""));
},
width: width,
height: height,
useCORS: true,
taintTest: false,
allowTaint: false
});
}
There is a library called Domvas that should do what you want.
It gives you the ability to take arbitrary DOM content and paint it to
a Canvas of your choice.
After that exporting an image from a canvas element should be pretty easy:
var canvas = document.getElementById("mycanvas");
var img = canvas.toDataURL("image/png");
document.write('<img src="'+img+'"/>');
Do you want to do it completely in JavaScript? If so, one possible solution could be to transform the HTML to an SVG. Or maybe you can use the <canvas> tag and draw it manually.

Categories