I am trying to export the d3 chart as png image on button click.Also,i have mentioned below the function which i am using here:
$(function() {
$('#btnSave').click(function(){
html2canvas($('#test'),
{
onrendered: function (canvas) {
var a = document.createElement('a');
// toDataURL defaults to png, so we need to request a jpeg, then convert for file download.
a.href = canvas.toDataURL();
a.download = 'somefilename.jpg';
a.click();
}
});
});
});
When i am clicking on export button i am not getting the chart properly.Also i have mentioned the screen shot which i am getting after export.
I would suggest not to use html2Canvas here because capturing any SVG images would create taint on the canvas and this library does not allow that (unless you force allowTaint: true which isn't a good option either).
Here's the issue you can go through: SVG Taint on canvas
Here's an approach to capture the SVG:
JS FIDDLE
Relevant code to capture image without any special library:
var svgString = new XMLSerializer().serializeToString(d3.select('body svg#custom_id').node()),
blob = new Blob([svgString], {type: "image/svg+xml;charset=utf-8"}),
url = window.URL.createObjectURL(blob);
var img = d3.select('body').append('img').classed('exportImg', true).node();
img.onload = function () {
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d');
canvas.width = w + m[1] + m[3];
canvas.height = h + m[0] + m[2];
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
// $('body').append(canvas);
d3.select('img.exportImg').remove();
// create link
var a = document.createElement('a');
a.href = canvas.toDataURL();
a.download = 'somefilename';
a.click();
};
img.src = url;
And by adding style to the paths, I meant, NOT using CSS (I'm sorry I wasn't clear there). Here is what I was talking about:
.enter().append("svg:path")
.attr("d", path).style('fill', 'none').style('stroke', 'steelblue').style('stroke-opacity', 0.7).style('stroke-width', 1.5);
Added the same to foreground and background. You can do the styling to the axes too in a similar fashion. CSS properties aren't captured while creating the SVG string.
Hope this helps. (And I've used sample base64 code for the image as those images weren't available, you can change that) :)
Related
I want to take a screenshot of the following canvas:
<div class="A">
<canvas width="700" height="500" style="filter: brightness(200%)"></canvas>
<div class="A">
I have the following method:
takeSnapshot() {
const canvas = document.getElementsByClassName('A')[0].childNodes[0] as HTMLCanvasElement;
console.log(canvas);
const dataUrl = canvas.toDataURL('png');
const link = document.getElementsByClassName('link')[0];
link.setAttribute('download', 'snapshot-' + this.convertTimeStamp(Date.now()) + '.png');
link.setAttribute('href', dataUrl.replace('image/png', 'image/octet-stream'));
link.click();
}
But the style style="filter: brightness(200%)" is not keepling on the png dowloaded image. How can i do?
Filter canvas pixels
The filter applied by the canvas style does not effect the pixels held by the canvas. This style is applied while compositing the canvas with the rest of the page.
To change the pixels on the canvas you need to set CanvasRenderingContext2D filter property and then render what you want the filter applied to.
Note that filter is experimental so check support.
Note that some filters will taint the canvas and thus not let you access the pixels, toDataURL will throw an error and you will not be able to download the canvas.
This does not apply to the standard named CSS filters like functions blur(), brightness(), contrast(), drop-shadow(), grayscale(), hue-rotate(), invert(), opacity(), saturate(), and sepia()
Example
Applying brightness filter before downloading canvas content.
takeSnapshot() {
const canvas = document.getElementsByClassName('A')[0].childNodes[0];
const ctx = canvas.getContext("2d");
// Set important canvas state to defaults
ctx.globalAlpha = 1;
ctx.setTransform(1, 0, 0, 1, 0, 0);
// Set the filter function
ctx.filter = "brightness(200%)";
// Make sure all pixels are completely replaced. Important if there are transparent pixels
ctx.globalCompositeOperation = "copy";
// Copy canvas to itself applying the filter as it does so.
ctx.drawImage(canvas, 0, 0);
// Turn off filter
ctx.filter = "none";
// Restore default comp op
ctx.globalCompositeOperation = "source-over";
// Download
const link = document.getElementsByClassName('link')[0];
link.download = 'snapshot-' + this.convertTimeStamp(Date.now()) + '.png';
link.href = canvas.toDataURL('png').replace('image/png', 'image/octet-stream');
link.click();
}
Note This requires the CanvasRenderingContext2D. If the canvas content was created using another API you will not be able to get a 2d context. You thus need to create a temp canvas, the same size and draw the old canvas onto the new one (using the same method as in the example above) then download the new canvas.
If canvas.getContext("2d") returns null
As I don't know how your canvas was rendered the more robust version of the function checks to see if a 2D context can be obtained, if not then copy (using filter) to a new canvas and save that.
takeSnapshot() {
var dlCanvas, ctx;
const can = document.getElementsByClassName('A')[0].childNodes[0];
ctx = can.getContext("2d"); // ctx may be null (eg the canvas was rendered using webGL
if (!ctx) { // failed to get 2D API create a second canvas to apply the filter
dlCanvas = Object.assign(document.createElement("canvas"), {
width: can.width, height: can.height
});
ctx = dlCanvas.getContext("2d");
} else {
dlCanvas = can;
}
ctx.globalAlpha = 1;
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.filter = "brightness(200%)";
ctx.globalCompositeOperation = "copy";
ctx.drawImage(can, 0, 0);
ctx.filter = "none";
ctx.globalCompositeOperation = "source-over";
const link = document.getElementsByClassName('link')[0];
link.download = 'snapshot-' + this.convertTimeStamp(Date.now()) + '.png';
link.href = dlCanvas.toDataURL('png').replace('image/png', 'image/octet-stream');
link.click();
}
I am using jsPDF to create a PDF by looping through some HTML code to find the values it needs. I am able to get the values just fine, but when I try to insert the header_img it does not work. I have found many solutions on Google but the one I am working with now is the only one that does not throw an error.
This is the code being used to get take the url that is provided via the loop, convert it to a DataURL, and then insert the image into the PDF. It does not give me any errors, but all that is in the PDF is the black border and no image. Any ideas?
function getDataUri(url, cb) {
var image = new Image();
image.setAttribute('crossOrigin', 'anonymous'); //getting images from external domain
image.onload = function () {
console.log(url);
var canvas = document.createElement('canvas');
canvas.width = this.naturalWidth;
canvas.height = this.naturalHeight;
//next three lines for white background in case png has a transparent background
var ctx = canvas.getContext('2d');
ctx.fillStyle = '#fff'; /// set white fill style
ctx.fillRect(0, 0, canvas.width, canvas.height);
canvas.getContext('2d').drawImage(this, 0, 0);
cb(canvas.toDataURL('image/jpeg'));
};
image.src = url;
}
var pdf = new jsPDF('p', 'in', 'letter');
var left_margin = .5;
var top_margin =.5;
var height = 2.313;
var width = 3.25;
var header_img_height = 1;
//Draw border
pdf.setLineWidth(1/92);
pdf.setDrawColor(0, 0, 0);
pdf.rect(left_margin, top_margin, width, height);
//example image
var header_img = 'http://www.quotehd.com/imagequotes/authors24/jeff-olsen-quote-two-clicks-and-its-broke.jpg';
let logo = null;
//callback to get DataURL
getDataUri(header_img, function(dataUri) {
logo = dataUri;
console.log("logo=" + logo);
//Add image to PDF
pdf.addImage(logo, 'JPEG', left_margin, top_margin, header_img_height, width);
});
pdf.output('save', 'test.pdf');
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.4.1/jspdf.debug.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I ended up solving the issue by writing a PHP script that would convert the image to Base64.
$img_src = urldecode($_GET['url']);
$img_str = base64_encode(file_get_contents($img_src));
echo 'data:' . getimagesize($img_src)['mime'] . ';base64,' . $img_str;
Then using jQuery, I passed the url to the PHP script and got the Base64 data in return:
function getBase64(url) {
var out = '';
$.ajax({
url: 'get-dataurl.php?url=' + encodeURI(url),
async: false,
dataTye: 'text',
success: function(data) {
out = data;
}
});
return out;
}
Certainly was not ideal, but it works.
You can encode the image to base64, you would use this tool or any similar and replace it on your code.
I am trying to generate image of my charts from my local server(I've installed highcharts on it).
Following is the code
var chart = Highcharts.charts[i];
var render_width = chart.chartWidth;
var render_height = chart.chartHeight;
// Get the cart's SVG code
var svg = chart.getSVG({
exporting: {
sourceWidth: chart.chartWidth,
sourceHeight: chart.chartHeight
}
});
// Create a canvas
var canvas = document.createElement('canvas');
canvas.height = render_height;
canvas.width = render_width;
document.body.appendChild(canvas);
// Create an image and draw the SVG onto the canvas
var image = new Image;
image.onload = function() {
canvas.getContext('2d').drawImage(this, 0, 0, render_width, render_height);
console.log(image.src);
afterPlotExport();
};
image.src = 'data:image/svg+xml;base64,' + window.btoa(svg);
}
When i'm trying to log inside onload() function i'm expecting a base64 string.But the string which is generated appears as broken image when i copy paste it to an online base64 to image converter.
Any help would be appreciated as to why the image is appearing broken?
I am trying to generate a PDF from HTML using jspdf plugin. I am stuck with loading Images into PDF. There is a function addImage() in plugin which takes base64 encoding of an image, but converting an image to base64 is not working.
I have used below two ways
//Create canvas, draw image in context and get the data url
imgToDataURL = function (img, outputFormat){
var canvas = document.createElement('canvas');
canvas.width = 20;
canvas.height = 20;
var c = canvas.getContext('2d');
c.height = img.height;
c.width=img.width;
c.drawImage(img, 0, 0);
c.save();
var dataurl = canvas.toDataURL(outputFormat);
return dataurl;
}
var img = new Image();
img.crossOrigin = 'Anonymous';
img.src = "image path"
img.height= 60;
img.width=60;
var dataURL = this.imgToDataURL(img,"image/jpeg");
renderer.pdf.addImage(dataURL, 'png',x+padding,y + this.internal.getLineHeight(),imageWidth,imageHeight);
This is printing wrong dataURL I am getting a white image. If I hard code the base64 code i.e return a hardcoded dataURL then addImage works fine. So the issue is with canvas.toDataURL which is giving me wrong output
this is the 2nd way
convertImgToBase64URL = function (url, callback, outputFormat){
var img = new Image();
img.crossOrigin = 'Anonymous';
img.onload = function(){
var canvas = document.createElement('CANVAS'),
ctx = canvas.getContext('2d'), dataURL;
canvas.height = this.height;
canvas.width = this.width;
ctx.drawImage(this, 0, 0);
dataURL = canvas.toDataURL(outputFormat);
callback(dataURL);
canvas = null;
};
img.src = url;
}
this.convertImgToBase64URL("Image Path",function(base64){
renderer.pdf.addImage(base64, 'PNG', 20, 20,48,48);
})
I have run this inside a javascript and the onload function is not even running I am not sure what is my mistake is.
Please suggest me what mistake I am doing in either way
In the first you missed assigning an onload function to your image. For that reason, the moment you try to create the dataURL, the image might not be loaded yet, ergo, the blank image. You could try changing the last few lines to this:
...
img.width=60;
img.onload = function () {
var dataURL = this.imgToDataURL(img,"image/jpeg");
renderer.pdf.addImage(dataURL, 'png',x+padding,y + this.internal.getLineHeight(),imageWidth,imageHeight);
}
img.src = "image path";
As for the second one, there seems to be a problem with the convertImgToBase64URL() which accepts 3 parameters, but you are passing 2. In you example, outputFormat parameter is undefined.
I'm working on an SVG graphic export (with d3.js library) to PNG. The problem is the label textPath. When exporting to PNG, the text does not appear. Does anyone know if there is solution for this problem?
The code I'm using to do the conversion is:
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");
var a = document.createElement("a");
a.download = "grafico.png";
a.href = png;
a.click();
DOMURL.revokeObjectURL(png);
};
img.src = url;
Thank you very much to all.
A greeting,
Sonia
Fixed. I've solved with attributes in the css style-sheet such as:
font-size: 16px;
color: black;
fill: none;
Same issue here. It seems that this method of conversion is not getting the body's font-size. However, dumping the generated SVG into a .svg file and opening that file in the browser renders correctly.