Pdf file size too big created using jspdf - javascript

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

Related

How to handle SVG and canvas in pdfmake & html2pdfmake

I am using html2pdfmake to convert a div to pdf. My div contains tables and also contains some svg charts. But i am not getting the svg into the pdf. I tried converting it using base64 but is paste the base64 code into the pdf.
I converted my svg to base64 like :
var html = d3.select('#idOfSVG').select("svg").attr("version", 1.1).attr("xmlns", "http://www.w3.org/2000/svg").node().parentNode.innerHTML;
var imgsrc = 'data:image/svg+xml;base64,'+ btoa(html);
var base_image = new Image();
base_image.src = imgsrc;
canvas = document.createElement('canvas');
canvas.id = 'canvas'
document.body.appendChild(canvas);
canvas.width = 500;
canvas.height = 500;
canvas.getContext('2d').drawImage(base_image,0,0);
Then i added the same into the content.
Also when i used ParseHtml(content,document.getElementById("myPDFDiv"))
My div myPDFDiv gets deleted from the DOM.
Any help will be highly appreciated !!
You can use html2pdfmake, it iterates all the elements in the div and make JSON for the same which you can use with pdfmake.
You can add SVG support by adding a switch case for SVG like :
Case 'SVG' :
addImage(cnt,e);
function addImage (cnt,e) {
//Serialize the svg element and then put it in a canvas
//Then update the cnt object
var url = canvas.toDataURL('image/png');
cnt.push({image: url,
width: 200,
height: 250,
margin: [ 20, 0, 0, 0 ]
});
}
Hope it helps !!

Creating image from two other images using javascript [duplicate]

I am wondering if there is a way to combine multiple images into a single image using only JavaScript. Is this something that Canvas will be able to do. The effect can be done with positing, but can you combine them into a single image for download?
Update Oct 1, 2008:
Thanks for the advice, I was helping someone work on a js/css only site, with jQuery and they were looking to have some MacOS dock-like image effects with multiple images that overlay each other. The solution we came up with was just absolute positioning, and using the effect on a parent <div> relatively positioned. It would have been much easier to combine the images and create the effect on that single image.
It then got me thinking about online image editors like Picnik and wondering if there could be a browser based image editor with photoshop capabilities written only in javascript. I guess that is not a possibility, maybe in the future?
I know this is an old question and the OP found a workaround solution, but this will work if the images and canvas are already part of the HTML page.
<img id="img1" src="imgfile1.png">
<img id="img2" src="imgfile2.png">
<canvas id="canvas"></canvas>
<script type="text/javascript">
var img1 = document.getElementById('img1');
var img2 = document.getElementById('img2');
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
canvas.width = img1.width;
canvas.height = img1.height;
context.globalAlpha = 1.0;
context.drawImage(img1, 0, 0);
context.globalAlpha = 0.5; //Remove if pngs have alpha
context.drawImage(img2, 0, 0);
</script>
Or, if you want to load the images on the fly:
<canvas id="canvas"></canvas>
<script type="text/javascript">
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var img1 = new Image();
var img2 = new Image();
img1.onload = function() {
canvas.width = img1.width;
canvas.height = img1.height;
img2.src = 'imgfile2.png';
};
img2.onload = function() {
context.globalAlpha = 1.0;
context.drawImage(img1, 0, 0);
context.globalAlpha = 0.5; //Remove if pngs have alpha
context.drawImage(img2, 0, 0);
};
img1.src = 'imgfile1.png';
</script>
MarvinJ provides the method combineByAlpha() in which combines multiple images using its alpha channel. Therefore, you just need to have your images in a format that supports transparency, like PNG, and use that method, as follow:
Marvin.combineByAlpha(image, imageOver, imageOutput, x, y);
image1:
image2:
image3:
Result:
Runnable Example:
var canvas = document.getElementById("canvas");
image1 = new MarvinImage();
image1.load("https://i.imgur.com/ChdMiH7.jpg", imageLoaded);
image2 = new MarvinImage();
image2.load("https://i.imgur.com/h3HBUBt.png", imageLoaded);
image3 = new MarvinImage();
image3.load("https://i.imgur.com/UoISVdT.png", imageLoaded);
var loaded=0;
function imageLoaded(){
if(++loaded == 3){
var image = new MarvinImage(image1.getWidth(), image1.getHeight());
Marvin.combineByAlpha(image1, image2, image, 0, 0);
Marvin.combineByAlpha(image, image3, image, 190, 120);
image.draw(canvas);
}
}
<script src="https://www.marvinj.org/releases/marvinj-0.8.js"></script>
<canvas id="canvas" width="450" height="297"></canvas>
I don't think you can or would want to do this with client side javascript ("combing them into a single image for download"), because it's running on the client: even if you could combine them into a single image file on the client, at that point you've already downloaded all of the individual images, so the merge is pointless.

HTML5 canvas, convert canvas to PDF with jspdf.js

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

Export Highcharts to PDF (using javascript and local server - no internet connection)

I am using Highcharts in my application (without any internet connection)
I have multiple charts on a html page, and I want to generate a PDF report that contains all the charts from this page.
How can I do this without sending the data to any server on the internet ?
I will be thankful for any help or any example you can provide.
Thank you in advance :)
Yes this is possible but involves a few different libraries to get working. The first Library is jsPDF which allows the creation of PDF in the browser. The second is canvg which allows for the rendering and parsing of SVG's, the bit that is really cool though is it can render an svg on to canvas element. Lastly is Highcharts export module which will allow us to send the svg to the canvg to turn into a data URL which can then be given to jsPDF to turn into your pdf.
Here is an example http://fiddle.jshell.net/leighking2/dct9tfvn/ you can also see in there source files you will need to include in your project.
So to start highcharts provides an example of using canvg with it's export to save a chart as a png. because you want all the iamges in a pdf this has been slightly altered for our purpose to just return the data url
// create canvas function from highcharts example http://jsfiddle.net/highcharts/PDnmQ/
(function (H) {
H.Chart.prototype.createCanvas = function (divId) {
var svg = this.getSVG(),
width = parseInt(svg.match(/width="([0-9]+)"/)[1]),
height = parseInt(svg.match(/height="([0-9]+)"/)[1]),
canvas = document.createElement('canvas');
canvas.setAttribute('width', width);
canvas.setAttribute('height', height);
if (canvas.getContext && canvas.getContext('2d')) {
canvg(canvas, svg);
return canvas.toDataURL("image/jpeg");
} 
else {
alert("Your browser doesn't support this feature, please use a modern browser");
return false;
}
}
}(Highcharts));
Then for the example i have set up export on a button click. This will look for all elements of a certain class (so choose one to add to all of your chart elements) and then call their highcharts.createCanvas function.
$('#export_all').click(function () {
var doc = new jsPDF();
// chart height defined here so each chart can be palced
// in a different position
var chartHeight = 80;
// All units are in the set measurement for the document
// This can be changed to "pt" (points), "mm" (Default), "cm", "in"
doc.setFontSize(40);
doc.text(35, 25, "My Exported Charts");
//loop through each chart
$('.myChart').each(function (index) {
var imageData = $(this).highcharts().createCanvas();
// add image to doc, if you have lots of charts,
// you will need to check if you have gone bigger
// than a page and do doc.addPage() before adding
// another image.
/**
* addImage(imagedata, type, x, y, width, height)
*/
doc.addImage(imageData, 'JPEG', 45, (index * chartHeight) + 40, 120, chartHeight);
});
//save with name
doc.save('demo.pdf');
});
important to note here that if you have lots of charts you will need to handle placing them on a new page. The documentation for jsPDF looks really outdated (they do have a good demos page though just not a lot to explain all the options possible), there is an addPage() function and then you can just play with widths and heights until you find something that works.
the last part is to just setup the graphs with an extra option to not display the export button on each graph that would normally display.
//charts
$('#chart1').highcharts({
navigation: {
buttonOptions: {
enabled: false
}
},
//this is just normal highcharts setup form here for two graphs see fiddle for full details
The result isn't too bad i'm impressed with the quality of the graphs as I wasn't expecting much from this, with some playing of the pdf positions and sizes could look really good.
Here is a screen shot showing the network requests made before and after the export, when the export is made no requests are made http://i.imgur.com/ppML6Gk.jpg
here is an example of what the pdf looks like http://i.imgur.com/6fQxLZf.png (looks better when view as actual pdf)
quick example to be tried on local https://github.com/leighquince/HighChartLocalExport
You need to setup your own exporting server, locally like in the article
Here is an example using the library pdfmake:
html:
<div id="chart_exchange" style="width: 450px; height: 400px; margin: 0 auto"></div>
<button id="export">export</button>
<canvas id="chart_exchange_canvas" width="450" height="400" style="display: none;"></canvas>
javascript:
function drawInlineSVG(svgElement, canvas_id, callback) {
var can = document.getElementById(canvas_id);
var ctx = can.getContext('2d');
var img = new Image();
img.setAttribute('src', 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svgElement))));
img.onload = function() {
ctx.drawImage(img, 0, 0);
callback(can.toDataURL("image/png"));
}
}
full working code:
https://jsfiddle.net/dimitrisscript/f6sbdsps/
Maybe this link can help you out.
http://bit.ly/1IYJIyF
Try refer to the exporting properties (fallbackToExportServer: false) and the necessary file that need to be include (offline-exporting.js).
Whereas for the export all at once part, currently I myself also still trying. Will update here if any.
This question is a bit old but was something i was working on myself recently and had some trouble with it.
I used the jsPDF library: https://github.com/MrRio/jsPDF
Issues i ran into involved jsPDF not supporting the SVG image exported by the high chart + images being blurry and low quality.
Below is the solution I used to get two charts into one pdf document:
function createPDF() {
var doc = new jsPDF('p', 'pt', 'a4'); //Create pdf
if ($('#chart1').length > 0) {
var chartSVG = $('#chart1').highcharts().getSVG();
var chartImg = new Image();
chartImg.onload = function () {
var w = 762;
var h = 600;
var chartCanvas = document.createElement('canvas');
chartCanvas.width = w * 2;
chartCanvas.height = h * 2;
chartCanvas.style.width = w + 'px';
chartCanvas.style.height = h + 'px';
var context = chartCanvas.getContext('2d');
chartCanvas.webkitImageSmoothingEnabled = true;
chartCanvas.mozImageSmoothingEnabled = true;
chartCanvas.imageSmoothingEnabled = true;
chartCanvas.imageSmoothingQuality = "high";
context.scale(2, 2);
chartCanvas.getContext('2d').drawImage(chartImg, 0, 0, 762, 600);
var chartImgData = chartCanvas.toDataURL("image/png");
doc.addImage(chartImgData, 'png', 40, 260, 250, 275);
if ($('#chart2').length > 0) {
var chart2SVG = $('#chart2').highcharts().getSVG(),
chart2Img = new Image();
chart2Img.onload = function () {
var chart2Canvas = document.createElement('canvas');
chart2Canvas.width = w * 2;
chart2Canvas.height = h * 2;
chart2Canvas.style.width = w + 'px';
chart2Canvas.style.height = h + 'px';
var context = chart2Canvas.getContext('2d');
chart2Canvas.webkitImageSmoothingEnabled = true;
chart2Canvas.mozImageSmoothingEnabled = true;
chart2Canvas.imageSmoothingEnabled = true;
chart2Canvas.imageSmoothingQuality = "high";
context.scale(2, 2);
chart2Canvas.getContext('2d').drawImage(chart2Img, 0, 0, 762, 600);
var chart2ImgData = chart2Canvas.toDataURL("image/png");
doc.addImage(chart2ImgData, 'PNG', 300, 260, 250, 275);
doc.save('ChartReport.pdf');
}
chart2Img.src = "data:image/svg+xml;base64," + window.btoa(unescape(encodeURIComponent(chart2SVG)));
}
}
chartImg.src = "data:image/svg+xml;base64," + window.btoa(unescape(encodeURIComponent(chartSVG)));
}
}
scripts to include:
<script src="http://code.highcharts.com/highcharts.js"></script>
<script src="http://code.highcharts.com/modules/exporting.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.2.61/jspdf.min.js"></script>

Can you combine multiple images into a single one using JavaScript?

I am wondering if there is a way to combine multiple images into a single image using only JavaScript. Is this something that Canvas will be able to do. The effect can be done with positing, but can you combine them into a single image for download?
Update Oct 1, 2008:
Thanks for the advice, I was helping someone work on a js/css only site, with jQuery and they were looking to have some MacOS dock-like image effects with multiple images that overlay each other. The solution we came up with was just absolute positioning, and using the effect on a parent <div> relatively positioned. It would have been much easier to combine the images and create the effect on that single image.
It then got me thinking about online image editors like Picnik and wondering if there could be a browser based image editor with photoshop capabilities written only in javascript. I guess that is not a possibility, maybe in the future?
I know this is an old question and the OP found a workaround solution, but this will work if the images and canvas are already part of the HTML page.
<img id="img1" src="imgfile1.png">
<img id="img2" src="imgfile2.png">
<canvas id="canvas"></canvas>
<script type="text/javascript">
var img1 = document.getElementById('img1');
var img2 = document.getElementById('img2');
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
canvas.width = img1.width;
canvas.height = img1.height;
context.globalAlpha = 1.0;
context.drawImage(img1, 0, 0);
context.globalAlpha = 0.5; //Remove if pngs have alpha
context.drawImage(img2, 0, 0);
</script>
Or, if you want to load the images on the fly:
<canvas id="canvas"></canvas>
<script type="text/javascript">
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var img1 = new Image();
var img2 = new Image();
img1.onload = function() {
canvas.width = img1.width;
canvas.height = img1.height;
img2.src = 'imgfile2.png';
};
img2.onload = function() {
context.globalAlpha = 1.0;
context.drawImage(img1, 0, 0);
context.globalAlpha = 0.5; //Remove if pngs have alpha
context.drawImage(img2, 0, 0);
};
img1.src = 'imgfile1.png';
</script>
MarvinJ provides the method combineByAlpha() in which combines multiple images using its alpha channel. Therefore, you just need to have your images in a format that supports transparency, like PNG, and use that method, as follow:
Marvin.combineByAlpha(image, imageOver, imageOutput, x, y);
image1:
image2:
image3:
Result:
Runnable Example:
var canvas = document.getElementById("canvas");
image1 = new MarvinImage();
image1.load("https://i.imgur.com/ChdMiH7.jpg", imageLoaded);
image2 = new MarvinImage();
image2.load("https://i.imgur.com/h3HBUBt.png", imageLoaded);
image3 = new MarvinImage();
image3.load("https://i.imgur.com/UoISVdT.png", imageLoaded);
var loaded=0;
function imageLoaded(){
if(++loaded == 3){
var image = new MarvinImage(image1.getWidth(), image1.getHeight());
Marvin.combineByAlpha(image1, image2, image, 0, 0);
Marvin.combineByAlpha(image, image3, image, 190, 120);
image.draw(canvas);
}
}
<script src="https://www.marvinj.org/releases/marvinj-0.8.js"></script>
<canvas id="canvas" width="450" height="297"></canvas>
I don't think you can or would want to do this with client side javascript ("combing them into a single image for download"), because it's running on the client: even if you could combine them into a single image file on the client, at that point you've already downloaded all of the individual images, so the merge is pointless.

Categories