How to handle SVG and canvas in pdfmake & html2pdfmake - javascript

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 !!

Related

How to covert multiple svg to png in single html page?

I used d3 and venn.js for creating this venn diagram.
The code goes here : Svg actually created inside div venn2 by these scripts.
<div id="venn2"></div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="js/venn.js"></script>
<script>
var sets = [
{sets:["A"], size: 12, label: "A"},
{sets:["B"], size:12, label: "B"},
{sets: ["A", "B"], size: 4, label: "AB"}
];
var chart = venn.VennDiagram()
.wrap(false)
.fontSize("14px")
.width(400)
.height(400);
function updateVenn(sets) {
var div = d3.select("#venn2").datum(sets);
var layout = chart(div),
textCentres = layout.textCentres;
div.selectAll(".label").style("fill", "white");
div.selectAll(".venn-circle path").style("fill-opacity", .6);
return layout;
}
</script>
The script I got here to convert svg to png via canvas.
<canvas id="canvas" ></canvas>
<div id="png-container" ></div>
<script>
var svgString = new XMLSerializer().serializeToString(document.querySelector('svg'));
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var DOMURL = self.URL || self.webkitURL || self;
var img = new Image();
var svg = new Blob([svgString], {type: "image/svg+xml;charset=utf-8"});
var url = DOMURL.createObjectURL(svg);
img.onload = function() {
ctx.drawImage(img, 0, 0);
var png = canvas.toDataURL("image/png");
document.querySelector('#png-container').innerHTML = '<img src="'+png+'"/>';
DOMURL.revokeObjectURL(png);
};
img.src = url;
</script>
So, the venn diagram as svg was created inside venn2 div by script 1 and svg then written as a png by script 2.
It worked perfectly fine for one svg image per page.
When I have more than one such svg venn diagrams on single html page. Only the first gets converted to png.
But I am unable to fetch the svg at position 2 and 3 or more to convert to png.
I am stuck at this code
var svgString = new XMLSerializer().serializeToString(querySelectorAll('svg'));
where 'svg' means only first svg i guess but not later.
I can't even create svgs with different "id" as svg is formed by d3 and venn.js scripts.
The question is that : How to convert all svg images in a html page
when I don't know their id to png images via above code?
I do not know how to parse this whole string var svgString to convert all to different png images?
In case someone else comes looking for sample code, I figured it out as follows.
My case: I had an svg with two layers drawn by plotly. I was only getting the first layer like the OP. Plotly drew into an element with id="chart". A new canvas already created with id="layer0".
Eventually I needed to send back to PHP as a dataURL so:
var nodes = document.getElementById("chart").querySelectorAll('svg');
for (var i = 0; i < nodes.length; i++) {
result = new XMLSerializer().serializeToString(nodes[i]);
eval('layer'+[i]+'chart').src = 'data:image/svg+xml;base64,' + btoa(result);
}
elem0 = document.getElementById("layer0");
ctx0 = elem0.getContext("2d");
layer0chart.onload = function() {
ctx0.drawImage(layer0chart,0,0,w*1.5,h*1.5);
ctx0.drawImage(layer1chart,0,0,w*1.5,h*1.5);
canvasdata = elem0.toDataURL();
console.log(canvasdata);
}
I appreciate the help from #BioDeveloper and #RobertLongson. The key for me ultimately was to also make sure the toDataURL was being called as an image was being loaded. Cheers.

html2canvas Images with .svg format are not rendering

When we are taking screenshots using html2canvas, images having .svg extention will not render properly. I have tried giving allowTaint:true option, still it is not working.
Code snippet:
<div class="myDiv" style="background-image:url('image.svg');">
</div>
html2canvas($(".myDiv"), {
allowTaint: true,
onrendered: function (canvas){
//use canvas
}
});
html2canvas will capture background svg image if we used img tag instead of div tag with url in scr attribute. So the solution I found for this is, dynamically creating new img tag for each div before calling html2canvas function.
$(".myDiv").each(function () {
var url = $(this).css("background-image")
var src = url.substring(5, url.length - 2);
var image = new Image();
image.src = src;
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
ctx.canvas.width = $(this).height();
ctx.canvas.height = $(this).width();
ctx.drawImage(image, 0, 0);
$(this).append(canvas);
});
This will work only for cases where you can replace div tag with img tag.

html2canvas not support transform property?

I build an image editor in javascript for my customer and we issue one huge problem. When we want to generate image where is some text rotated, canvas not generate full image with all contents over background.
Here is example:
When I click on save button I get empty background like this:
On other way, when I just add content and NOT do rotation, transform not exists in CSS, I can change color of text, change font, change background and all will look nice like on this example:
Result is perfect:
Here is my code:
function generateImage(canvas, width){
var img = new Image();
img.crossOrigin = "Anonymous";
img.width = width;
img.height = (img.width / ( 16 / 9 ));
img.src = canvas.toDataURL();
img.align = 'middle';
img.class = 'img-responsive';
img.style.width = '100%';
img.style.maxWidth = width;
document.body.appendChild(img);
}
html2canvas($this, {
onrendered: function(canvas) {
generateImage(canvas, ($this.width()+2));
},
width: $this.width()+2,
height: $this.height()+2
});
The main question is:
Is there a way to add in canvas CSS transition support to can render proper full image or some 3D canvas drawing which work properly?

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 make html5 canvas display crisp image based off chrome screencap

I am making a chrome extension that uses the chrome.tabs.captureVisibleTab method to grab a screencap of the current tab and then displays that in a popup from the chrome extension. If I use the img tag and use the data coming from the chrome function, such as data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgMCAgMDAwMEAwME…UUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAf/2Q==, the <img> displays pixel perfect, it is crisp and that's what I want. Problem is that I need to make it into a canvas, and when I do that, it becomes blurry.
This is a screencap of the <img> that uses the link data provided by Google to be used as a comparison to the canvas which is below.
This is the blurry canvas.
This is the code that I am using to try to do this, but I can't figure out how to make the canvas crisp like the image.
chrome.runtime.sendMessage({msg: "capture"}, function(response) {
console.log(response.imgSrc);
var img = document.createElement('img');
var _canvas = document.createElement("canvas");
img.src = response.imgSrc;
img.height = 436;
img.width = 800;
document.getElementById('main-canvas').appendChild(img);
draw(response);
});
function draw(response) {
var canvas = document.getElementById('imageCanvas');
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
//Get a 2d context
var ctx = canvas.getContext('2d');
//use image to paint canvas
var _image = new Image();
_image.onload = function() {
ctx.drawImage(_image, 0, 0, 800, 436);
}
_image.src = response.imgSrc;
document.getElementById('main-canvas').appendChild(canvas);
}
This is what I used to fix my problem: High Resolution Canvas from HTML5Rocks

Categories