I'm using the EasyQRCodeJS plugin but I am finding it hard to download/save the QR Code to my device in an SVG format.
Here's the code I am using. It's creating and displaying the QR Code fine displaying but the file will not open so I am doing someone incorrectly.
// Options
var options = {
text: "https://github.com/ushelp/EasyQRCodeJS",
width: 200,
height: 200,
colorDark : "#000000",
colorLight : "#ffffff",
correctLevel : QRCode.CorrectLevel.H, // L, M, Q, H
logo: "https://assets.website-files.com/6130bd1fcd31de20d9599493/627b6e7e75f1c5ef5f36ff22_customjava.svg",
logoWidth: 50, // fixed logo width. default is width/3.5
logoHeight: 50, // fixed logo height. default is heigth/3.5,
crossOrigin : 'anonymous',
quietZone: 0,
quietZoneColor: "rgba(0,0,0,0)",
drawer: 'svg',
logoBackgroundColor: '#fffff', // Logo backgroud color, Invalid when logBgTransparent is true; default is '#ffffff'
logoBackgroundTransparent: false, // Whether use transparent image, default is false
};
// Create QRCode Object
new QRCode(document.getElementById("qrcode"), options);
function download(filename, text) {
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
// Start file download.
document.getElementById("button").addEventListener("click", function(){
/* alert(QRCode); */
var text = QRCode;
var filename = "qrcode.svg";
download(filename, text);
}, false);
<script src="https://cdn.jsdelivr.net/npm/easyqrcodejs#4.4.12/dist/easy.qrcode.min.js"></script>
<div id="qrcode"></div>
<input type="button" id="button" value="Download" />
var text = QRCode; won't return your svg code but the QRCode object.
Add a element variable and fetch it's innerHTML like so:
var text = qrcodeEl.innerHTML;
Example
// Options
var options = {
text: "https://github.com/ushelp/EasyQRCodeJS",
width: 200,
height: 200,
colorDark: "#000000",
colorLight: "#ffffff",
correctLevel: QRCode.CorrectLevel.H, // L, M, Q, H
logo: "https://assets.website-files.com/6130bd1fcd31de20d9599493/627b6e7e75f1c5ef5f36ff22_customjava.svg",
logoWidth: 50, // fixed logo width. default is width/3.5
logoHeight: 50, // fixed logo height. default is heigth/3.5,
crossOrigin: "anonymous",
quietZone: 0,
quietZoneColor: "rgba(0,0,0,0)",
drawer: "svg",
logoBackgroundColor: "#fffff", // Logo backgroud color, Invalid when logBgTransparent is true; default is '#ffffff'
logoBackgroundTransparent: false // Whether use transparent image, default is false
};
const qrcodeEl = document.getElementById("qrcode");
const QR = new QRCode(qrcodeEl, options);
function download(filename, text) {
var element = document.createElement("a");
console.log(text);
element.setAttribute(
"href",
"data:text/plain;charset=utf-8," + encodeURIComponent(text)
);
element.setAttribute("download", filename);
element.style.display = "none";
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
// Start file download.
document.getElementById("button").addEventListener(
"click",
function() {
/* alert(QRCode); */
var text = qrcodeEl.innerHTML;
console.log(text)
var filename = "qrcode.svg";
download(filename, text);
},
false
);
<script src="https://cdn.jsdelivr.net/npm/easyqrcodejs#4.4.12/dist/easy.qrcode.min.js"></script>
<div id="qrcode"></div>
<input type="button" id="button" value="Download" />
Download won't work in SO snippet due to security settings.
Embed logo svg
This will only work if you're using a data URL for your logo.
External logo svgs will most likely not work due to CORS policies.
If logos are on the same domain you might use an ajax fetch() to retrieve all data.
I've converted the logo with Yoksel's URL-encoder for svg.
// getTransformToElement polyfill
SVGElement.prototype.getTransformToElement =
SVGElement.prototype.getTransformToElement ||
function (toElement) {
return toElement.getScreenCTM().inverse().multiply(this.getScreenCTM());
};
// Options
var options = {
text: "https://github.com/ushelp/EasyQRCodeJS",
width: 200,
height: 200,
colorDark: "#000000",
colorLight: "#ffffff",
correctLevel: QRCode.CorrectLevel.H, // L, M, Q, H
logo:
"data:image/svg+xml,%3Csvg width='441' height='352' viewBox='0 0 441 352' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M26.0869 35.7268V26.0865H35.8516V7.65674H7.65723V35.7268H26.0869ZM26.0869 58.8637H7.65723V78.1444H26.0869V58.8637ZM26.0869 101.281H7.65723V120.562H26.0869V101.281ZM26.0869 143.699H7.65723V162.98H26.0869V143.699ZM26.0869 186.117H7.65723V205.397H26.0869V186.117ZM26.0869 228.534H7.65723V247.815H26.0869V228.534ZM26.0869 270.952H7.65723V290.232H26.0869V270.952ZM26.0869 313.369H7.65723V341.439H35.8516V323.01H26.0869V313.369ZM59.2869 323.01V341.439H78.8162V323.01H59.2869ZM102.251 323.01V341.439H121.781V323.01H102.251ZM145.216 323.01V341.439H164.745V323.01H145.216ZM188.181 323.01V341.439H207.71V323.01H188.181ZM231.145 323.01V341.439H250.675V323.01H231.145ZM274.11 323.01V341.439H293.639V323.01H274.11ZM317.074 323.01V341.439H336.604V323.01H317.074ZM360.039 323.01V341.439H379.568V323.01H360.039ZM403.004 323.01V341.439H431.198V313.369H412.768V323.01H403.004ZM412.768 290.232H431.198V270.952H412.768V290.232ZM412.768 247.815H431.198V228.534H412.768V247.815ZM412.768 205.397H431.198V186.117H412.768V205.397ZM412.768 162.98H431.198V143.699H412.768V162.98ZM412.768 120.562H431.198V101.281H412.768V120.562ZM412.768 78.1445H431.198V58.8638H412.768V78.1445ZM412.768 35.7269V26.0865H403.004V7.65674H431.198V35.7269H412.768ZM379.568 26.0865V7.65674H360.039V26.0865H379.568ZM336.604 26.0865V7.65674H317.074V26.0865H336.604ZM293.639 26.0865V7.65674H274.11V26.0865H293.639ZM250.675 26.0865V7.65674H231.145V26.0865H250.675ZM207.71 26.0865V7.65674H188.181V26.0865H207.71ZM164.745 26.0865V7.65674H145.216V26.0865H164.745ZM121.781 26.0865V7.65674H102.251V26.0865H121.781ZM78.8162 26.0865V7.65674H59.2869V26.0865H78.8162Z' fill='%235836F5'/%3E%3Crect x='80.377' y='76.5498' width='274.398' height='196.547' fill='%235836F5'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M167.67 122.48L177.04 133.328L133.073 171.309L176.976 208.345L167.734 219.301L110.996 171.438L167.67 122.48Z' fill='white'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M268.656 122.48L259.286 133.328L303.253 171.309L259.35 208.345L268.593 219.301L325.33 171.438L268.656 122.48Z' fill='white'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M187.544 234.91L237.714 103.343L251.107 108.45L200.937 240.018L187.544 234.91Z' fill='%23C9BFFC'/%3E%3Ccircle cx='419.042' cy='20.9894' r='20.9894' fill='%235836F5'/%3E%3Ccircle cx='20.9894' cy='20.9894' r='20.9894' fill='%235836F5'/%3E%3Ccircle cx='419.042' cy='331.011' r='20.9894' fill='%235836F5'/%3E%3Ccircle cx='20.9894' cy='331.011' r='20.9894' fill='%235836F5'/%3E%3C/svg%3E%0A",
logoWidth: 50, // fixed logo width. default is width/3.5
logoHeight: 50, // fixed logo height. default is heigth/3.5,
crossOrigin: "anonymous",
quietZone: 0,
quietZoneColor: "rgba(0,0,0,0)",
drawer: "svg",
logoBackgroundColor: "#fffff", // Logo backgroud color, Invalid when logBgTransparent is true; default is '#ffffff'
logoBackgroundTransparent: false // Whether use transparent image, default is false
};
const qrcodeEl = document.getElementById("qrcode");
new QRCode(qrcodeEl, options);
function download(filename, text) {
var element = document.createElement("a");
console.log(text);
element.setAttribute(
"href",
"data:text/plain;charset=utf-8," + encodeURIComponent(text)
);
element.setAttribute("download", filename);
element.style.display = "none";
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
// Start file download.
document.getElementById("button").addEventListener(
"click",
function () {
//embed logo
embedLogo();
var text = qrcodeEl.innerHTML;
console.log(text);
var filename = "qrcode.svg";
download(filename, text);
},
false
);
function embedLogo() {
let qrSVG = document.querySelector("svg");
let logo = qrSVG.querySelector("image");
let imgTrans = logo.getAttribute("transform");
let imgWidth = logo.getAttribute("width");
let imgHeight = logo.getAttribute("height");
//convert data url to svg
let logoSrc = logo
.getAttribute("xlink:href")
.replaceAll("data:image/svg+xml,", "")
.replaceAll("%3C", "<")
.replaceAll("%3E", ">")
.replaceAll("%3C/", "/")
.replaceAll("%0A", "\n")
.replaceAll("%23", "#");
// append logo svg
let logogroup = document.createElementNS("http://www.w3.org/2000/svg", "g");
qrSVG.appendChild(logogroup);
logogroup.innerHTML = logoSrc;
let logoEmbedded = logogroup.querySelector("svg");
logoEmbedded.setAttribute("width", imgWidth);
logoEmbedded.setAttribute("height", imgHeight);
logogroup.setAttribute("transform", imgTrans);
//convert nesest logo svg to group
nestedSvgToGroup(logoEmbedded);
logogroup.removeAttribute("transform");
logo.remove();
}
/**
* convert nested svg to embedded g
*/
function nestedSvgToGroup(svg, groupMatrix = 0) {
let svgSub = svg;
if (svg.parentNode) {
let parent = svg.parentNode.closest("svg");
let svgSubChildren = [...svgSub.children];
groupMatrix = groupMatrix ? groupMatrix : transFormToMatrix(svgSub);
//replace nested svg with group - apply matrix
let group = document.createElementNS("http://www.w3.org/2000/svg", "g");
group.classList.add("svgNest");
group.setAttribute("transform", `matrix( ${groupMatrix} )`);
//copy children to group
svgSubChildren.forEach(function (child, i) {
group.appendChild(child);
});
//remove nested svg
svgSub.replaceWith(group);
}
}
function transFormToMatrix(el) {
let type = el.nodeName.toLowerCase();
let matrixString = "";
let types = [
"path",
"polygon",
"polyline",
"rect",
"ellipse",
"circle",
"line",
"text",
"g",
"svg"
];
if (types.indexOf(type) !== -1) {
// get el matrix
let matrix = el.getTransformToElement(el.parentNode.closest("svg"));
let [a, b, c, d, e, f] = [
matrix.a,
matrix.b,
matrix.c,
matrix.d,
matrix.e,
matrix.f
];
matrixString = [a, b, c, d, e, f].join(" ");
//exclude non transformed elements
if (matrixString != "1 0 0 1 0 0") {
el.setAttribute("transform", `matrix(${matrixString})`);
el.removeAttribute("transform-origin");
}
}
return matrixString;
}
<script src="https://cdn.jsdelivr.net/npm/easyqrcodejs#4.4.12/dist/easy.qrcode.min.js"></script>
<div id="qrcode"></div>
<p><input type="button" id="button" value="Download" /></p>
How it works
convert your logo to a data url and add it to your option
embedLogo() helper will add a nested svg to the parent QRcode svg
nestedSvgToGroup() will translate transformations to a matrix()
<image> is removed
Codepen Example
See also: Issues with conversion of svg element to .svg
Related
I have a demo here: https://codepen.io/Jsbbvk/pen/KKXNPYO
var canvas = new fabric.Canvas("canvas");
const text = new fabric.IText("sample text")
text.set({
fontFamily: "Impact",
left: (this.canvas.width - text.width) / 2,
top: (this.canvas.height - text.height) / 2,
})
canvas.add(text)
canvas.requestRenderAll()
If you visit this codepen on a mobile device, you'll see that the Impact font doesn't load. However, visiting this codepen on a desktop will work. Is there a fix to this?
Impact is not preinstalled on android devices
Actually, the term "web safe fonts" is just plain wrong! (see also: "Impact" font not working on mobile Chrome
Workaround use a async font loader to check the availability
Check if fonts are available via document.fonts.check()
If necessary: load the font via asynchronous helper function loadFonts(fontsToLoad) (based on FontFace.load() method)
You also need to init fabric.js after fonts are loaded:
JS:
async function initFabric() {
var canvas = new fabric.Canvas("canvas");
const text = new fabric.IText("sample text");
let canvasContainer = document.querySelector(".canvas-container");
//load fonts if necessary
let fontsToLoad = checkFontAvailability(fonts);
await loadFonts(fontsToLoad);
text.set({
fontFamily: "Impact",
left: (this.canvas.width - text.width) / 2,
top: (this.canvas.height - text.height) / 2
});
canvas.add(text);
canvas.requestRenderAll();
}
Working example
/**
* fonts to check
*/
let fonts = [{
'font-family': 'Arial',
'font-style': 'normal',
'font-weight': 400,
'src': 'https://fonts.gstatic.com/s/anton/v23/1Ptgg87LROyAm3Kz-C8.woff2'
},
{
'font-family': 'Times New Roman',
'font-style': 'normal',
'font-weight': 400,
'src': 'https://fonts.gstatic.com/s/anton/v23/1Ptgg87LROyAm3Kz-C8.woff2'
},
{
// Inter as replacement
'font-family': 'Helvetica',
'font-style': 'normal',
'font-weight': 400,
'src': 'https://fonts.gstatic.com/s/inter/v12/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7.woff2'
},
{
// Anton as replacement
'font-family': 'Impact',
'font-style': 'normal',
'font-weight': 400,
'src': 'https://fonts.gstatic.com/s/anton/v23/1Ptgg87LROyAm3Kz-C8.woff2'
},
]
/**
* init fabric after font check
*/
initFabric();
async function initFabric() {
var canvas = new fabric.Canvas("canvas");
const text = new fabric.IText("sample text");
let canvasContainer = document.querySelector('.canvas-container');
//load fonts if necessary
let fontsToLoad = checkFontAvailability(fonts);
await loadFonts(fontsToLoad);
text.set({
fontFamily: "Impact",
left: (this.canvas.width - text.width) / 2,
top: (this.canvas.height - text.height) / 2,
})
canvas.add(text)
canvas.requestRenderAll()
//add buttons
fonts.forEach(function(font) {
let fontFamily = font['font-family']
let btn = document.createElement('button')
btn.setAttribute('type', 'button')
btn.classList.add('btn-font');
btn.textContent = fontFamily
canvasContainer.parentNode.insertBefore(btn, canvasContainer);
btn.addEventListener("click", () => {
text.fontFamily = fontFamily
canvas.renderAll()
})
})
}
function checkFontAvailability(fonts) {
let info = [];
let fontsToLoad = [];
if (fonts.length) {
fonts.forEach(function(font) {
let fontFamily = font['font-family'];
let fontApplied = document.fonts.check(`12px ${fontFamily}`);
if (!fontApplied) {
fontsToLoad.push(font)
}
})
}
return fontsToLoad;
}
async function loadFonts(fontsToLoad) {
if (fontsToLoad.length) {
for (let i = 0; i < fontsToLoad.length; i++) {
let fontProps = fontsToLoad[i];
let fontFamily = fontProps['font-family'];
let fontWeight = fontProps['font-weight'];
let fontStyle = fontProps['font-style'];
let fontUrl = Array.isArray(fontProps['src']) ? fontProps['src'][0][0] : fontProps[
'src'];
if (fontUrl.indexOf('url(') === -1) {
fontUrl = 'url(' + fontUrl + ')';
}
let fontFormat = fontProps['src'][0][1] ? fontProps['src'][1] : '';
const font = new FontFace(fontFamily, fontUrl);
font.weight = fontWeight;
font.style = fontStyle;
await font.load();
document.fonts.add(font);
console.log(fontFamily, 'loaded')
// apply font styles to invisible elements
let fontDOMEl = document.createElement('div');
fontDOMEl.textContent = '';
document.body.appendChild(fontDOMEl);
fontDOMEl.setAttribute(
"style",
`position:fixed; height:0; width:0; overflow:hidden; font-family:${fontFamily}; font-weight:${fontWeight}; font-style:${fontStyle}`
);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.3.0/fabric.min.js"></script>
<canvas width="300" height="300" id="canvas"></canvas>
loadFonts() will append invisible elements to your DOM with the desired font-families applied.
This will ensure the loaded fonts are also available for canvas elements.
I am using jspdf here.I am using Highchart plugin also
When you click submit1 button charts will generate,and when you press download button those chart will be downloaded as pdf.
Everything is working but I need to just reduce the font size of the chart title in downloaded pdf. http://plnkr.co/edit/Z3hJcC8pgzZxXPLWpfBw?p=preview
Here is my live demo in plunker. also here I have the html and javascript code
HTML
<div><img id="logo" style="width:30%" src="https://www.gstatic.com/webp/gallery3/3.png"/>dddddddd</div>
<input type="button" id="download" value="download" /> click to console
<div><button id="button1" class="button1">submit1</button></div>
<p id="bottom-content">footer</p>
SCRIPT
$(document).ready(function() {
var index = 0;
var id = [];
$('#button1').on('click', function() {
$('body').append($("<div id='chart" + index + "'></div>"));
Highcharts.chart('chart' + index, {
title: {
text: 'Chart-'+index+''
},
series: [{
data: [1, 2, 3]
}]
});
var temp = "chart" + index + "";
console.log(temp);
id.push(temp);
index++;
});
$('#download').on('click', function() {
console.log(id);
var doc = new jsPDF('portrait', 'pt', 'a4', true);
var elementHandler = {
'#ignorePDF': function(element, renderer) {
return true;
}
};
var source = document.getElementById("logo");
doc.fromHTML(source, 15, 15, {
'width': 200,
'elementHandlers': elementHandler
});
var DOMURL = window.URL || window.webkitURL || window;
var elements = id;
// console.log(xx);
for (let i in elements) {
let svg = document.querySelectorAll('svg');
let canvas = document.createElement('canvas');
let canvasIE = document.createElement('canvas');
let context = canvas.getContext('2d');
let data1 = (new XMLSerializer()).serializeToString(svg[i]);
canvg(canvas, data1);
let svgBlob = new Blob([data1], {
type: 'image/svg+xml;charset=utf-8'
});
let url = DOMURL.createObjectURL(svgBlob);
let img = new Image();
img.src = url;
img.onload = function() {
context.canvas.width = $("#"+elements[i]).find('svg').width();
context.canvas.height = $("#"+elements[i]).find('svg').height();
context.drawImage(img, 0, 0);
// freeing up the memory as image is drawn to canvas
DOMURL.revokeObjectURL(url);
var dataUrl;
if (isIEBrowser()) { // Check of IE browser
var svg = $(elements[i]).highcharts().container.innerHTML;
canvg(canvasIE, svg[i]);
dataUrl = canvasIE.toDataURL('image/JPEG');
} else {
dataUrl = canvas.toDataURL('image/jpeg');
}
doc.addImage(dataUrl, 'JPEG', 20, 150, 560, 350);
let bottomContent = document.getElementById("bottom-content");
doc.fromHTML(bottomContent, 15, 700, {
'width': 560,
'elementHandlers': elementHandler
});
let logo = document.getElementById("logo");
doc.fromHTML(logo, 15, 200, {
'width': 560,
'elementHandlers': elementHandler
});
doc.addPage();
};
}
setTimeout(function() {
doc.save('TestChart"'+index+'".pdf');
}, 1000);
});
});
function isIEBrowser() {
var ieBrowser;
var ua = window.navigator.userAgent;
var msie = ua.indexOf("MSIE ");
if (msie > 0 || !!navigator.userAgent.match(/Trident.*rv\:11\./)) // Internet Explorer
{
ieBrowser = true;
} else //Other browser
{
console.log('Other Browser');
ieBrowser = false;
}
return ieBrowser;
};
You can change the font-size title style, after click on download button, in setTitle method:
var charts = Highcharts.charts;
charts.forEach(function(chart){
chart.setTitle({
style: {
fontSize: '32px'
}
});
});
Live demo: http://plnkr.co/edit/DpFULmbOa8vjk1VEZ5nD?p=preview
API: https://api.highcharts.com/class-reference/Highcharts.Chart#setTitle
How can I get base64 with image inside of svg? Check this Fiddle that I got from another question. If you see the second graphic, its not generating the image that overlays the bar.
var chart = new google.visualization.ColumnChart(document.getElementById('chart_survey'));
$("[fill='#FFFFFF']").each(function( index, element ) {
var svgimg = document.createElementNS('http://www.w3.org/2000/svg','image');
svgimg.setAttributeNS(null,'x',element.x.baseVal.value);
svgimg.setAttributeNS(null,'y',element.y.baseVal.value);
svgimg.setAttributeNS(null,'width',element.width.baseVal.value);
svgimg.setAttributeNS(null,'height',element.height.baseVal.value);
svgimg.setAttributeNS(null,'preserveAspectRatio','none');
svgimg.setAttributeNS('http://www.w3.org/1999/xlink','href', '${application.contextPath}/images/textura/patt.gif');
$(element).parent().append(svgimg);
});
$('#test').val(chart.getImageURI())
In order to save this svg to a png, which keeps the linked <image> then you'll have to encode each <image>'s href to a dataURL first.
Edit
I rewrote the original snippets (which can still be found in the edit history).
I changed the <image> tags to <use>. This results in a much smaller memory usage.
I also added a fallback for IE11, which accepts to encode the external image to data URL, but still fails to convert svg to png via canvas.
The fallback will replace the destination <img>tag, with the canvas. The later can be saved by user with a right click.
A few caveats :
It doesn't work in Safari 7, and maybe in other outdated webkit browsers. That's a strange bug, since it does work like a charm in localhost, but won't on any other network (even on my home network, using 192.168.xxx).
IE 9 & IE 10 will fail to convert the external images to data URL, CORS problem.
// What to do with the result (either data URL or directly the canvas if tainted)
var callback = function(d, isTainted) {
if (!isTainted) {
$('#chartImg')[0].src = d;
} else
$('#chartImg')[0].parentNode.replaceChild(d, $('#chartImg')[0]);
};
// The url of the external image (must be cross-origin compliant)
var extURL = 'https://dl.dropboxusercontent.com/s/13dv8vzmrlcmla2/tex2.jpg';
google.load('visualization', '1', {
packages: ['corechart']
})
var encodeCall = getbase64URI.bind(this, extURL, callback);
google.setOnLoadCallback(encodeCall);
// Google Chart part
function drawVisualizationDaily(imgUrl, callback, isTainted) {
var data = google.visualization.arrayToDataTable([
['Daily', 'Sales'],
['Mon', 4],
['Tue', 6],
['Wed', 6],
['Thu', 5],
['Fri', 3],
['Sat', 7],
['Sun', 7]
]);
var chart = new google.visualization.ColumnChart(document.getElementById('visualization'));
chart.draw(data, {
title: "Daily Sales",
width: 500,
height: 400,
hAxis: {
title: "Daily"
}
});
// Link to chart's svg element
var svgNode = chart.ea.querySelector('svg');
// Create a symbol for our image
var symbol = document.createElementNS('http://www.w3.org/2000/svg', 'symbol');
// An svg wrapper to allow size changing with <use>
symbol.setAttributeNS(null, 'viewBox', '0,0,10,10');
symbol.setAttributeNS(null, 'preserveAspectRatio', 'none');
symbol.id = 'background';
// And the actual image, with our encoded image
var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
img.setAttributeNS(null, 'preserveAspectRatio', 'none');
img.setAttributeNS(null, 'width', '100%');
img.setAttributeNS(null, 'height', '100%');
img.setAttributeNS('http://www.w3.org/1999/xlink', 'href', imgUrl);
symbol.appendChild(img);
svgNode.appendChild(symbol);
var blueRects = $("[fill='#3366cc']");
var max = blueRects.length - 1;
blueRects.each(function(index, element) {
var svgimg = document.createElementNS('http://www.w3.org/2000/svg', 'use');
svgimg.setAttributeNS(null, 'x', element.x.baseVal.value);
svgimg.setAttributeNS(null, 'y', element.y.baseVal.value);
svgimg.setAttributeNS(null, 'width', element.width.baseVal.value);
svgimg.setAttributeNS(null, 'height', element.height.baseVal.value);
svgimg.setAttributeNS('http://www.w3.org/1999/xlink', 'href', '#background');
svgNode.appendChild(svgimg);
if (index === max && !isTainted) // no need to call it if we don't have our dataURL encoded images
// a load event would be better but it doesn't fire in IE ...
setTimeout(exportSVG.bind(this, svgNode, callback, isTainted), 200);
});
}
function exportSVG(svgNode, callback, isTainted) {
var svgData = (new XMLSerializer()).serializeToString(svgNode);
var img = new Image();
img.onload = function() {
var canvas = document.createElement('canvas');
canvas.width = svgNode.getAttribute('width');
canvas.height = svgNode.getAttribute('height');
canvas.getContext('2d').drawImage(this, 0, 0);
var data, isTainted;
try {
data = canvas.toDataURL();
} catch (e) {
data = canvas;
isTainted = true;
}
callback(data, isTainted);
}
img.src = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgData);
}
// A simple function to convert an images's url to base64 data URL
function getbase64URI(url, callback) {
var img = new Image();
img.crossOrigin = "Anonymous";
img.onload = function() {
var c = document.createElement('canvas');
c.width = this.width;
c.height = this.height;
c.getContext('2d').drawImage(this, 0, 0);
var isTainted;
try {
c.toDataURL();
} catch (e) {
isTainted = true;
}
// if the canvas is tainted, return the url
var output = (isTainted) ? url : c.toDataURL();
drawVisualizationDaily(output, callback, isTainted);
}
img.src = url;
}
svg { border: 1px solid yellow; }
img { border: 1px solid green; }
canvas { border: 1px solid red; }
<script src="http://www.google.com/jsapi?.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="visualization"></div>
Right-click this image to save it:
<br>
<img id="chartImg" />
This example creates an svg container populated by an image. In my example the image is an svg image but you should be able to put in any type of image (jpg, png, gif). The container is created first then the image is created inside the container.
// create svg
var svg = document.createElementNS('http://www.w3.org/2000/svg','svg');
svg.setAttribute('class','shadowed handle_icon_sensors');
svg.setAttribute('height','25');
svg.setAttribute('width','25');
svg.setAttribute('id',idAttr);
svg.setAttribute('z-index','21000');
document.getElementById("zones_container").appendChild(svg);
// create svg image
var svgimg = document.createElementNS('http://www.w3.org/2000/svg','image');
svgimg.setAttribute('height','25');
svgimg.setAttribute('width','25');
svgimg.setAttributeNS('http://www.w3.org/1999/xlink','href','svg/icon_sensorYellow.svg');
svgimg.setAttribute('x','0');
svgimg.setAttribute('y','0');
document.getElementById(idAttr).appendChild(svgimg);
Note, When converted to src of img element retained blue background-color .
Try , after .each()
// set namespace attributes
var svg = $("svg").attr({
"xmlns": "http://www.w3.org/2000/svg",
"xmlns:xlink": "http://www.w3.org/1999/xlink"
})[0];
// create `data URI` of `svg`
var dataURI = "data:image/svg+xml;charset=utf-8;base64," + btoa(svg.outerHTML.trim());
// post `svg` as `data URI` to server
$.post("/path/to/server/", {
html: dataURI
}, "html")
.then(function (data) {
// do stuff
// `svg` `data URI`
console.log(data);
}, function (jqxhr, textStatus, errorThrown) {
console.log(textStatus, errorThrown);
});
jsfiddle http://jsfiddle.net/R8A8P/58/
this html worked in react
<a
href={chart.getImageURI()}
download="chart.png">
getImage
</a>
How can I get base64 with image inside of svg? Check this Fiddle that I got from another question. If you see the second graphic, its not generating the image that overlays the bar.
var chart = new google.visualization.ColumnChart(document.getElementById('chart_survey'));
$("[fill='#FFFFFF']").each(function( index, element ) {
var svgimg = document.createElementNS('http://www.w3.org/2000/svg','image');
svgimg.setAttributeNS(null,'x',element.x.baseVal.value);
svgimg.setAttributeNS(null,'y',element.y.baseVal.value);
svgimg.setAttributeNS(null,'width',element.width.baseVal.value);
svgimg.setAttributeNS(null,'height',element.height.baseVal.value);
svgimg.setAttributeNS(null,'preserveAspectRatio','none');
svgimg.setAttributeNS('http://www.w3.org/1999/xlink','href', '${application.contextPath}/images/textura/patt.gif');
$(element).parent().append(svgimg);
});
$('#test').val(chart.getImageURI())
In order to save this svg to a png, which keeps the linked <image> then you'll have to encode each <image>'s href to a dataURL first.
Edit
I rewrote the original snippets (which can still be found in the edit history).
I changed the <image> tags to <use>. This results in a much smaller memory usage.
I also added a fallback for IE11, which accepts to encode the external image to data URL, but still fails to convert svg to png via canvas.
The fallback will replace the destination <img>tag, with the canvas. The later can be saved by user with a right click.
A few caveats :
It doesn't work in Safari 7, and maybe in other outdated webkit browsers. That's a strange bug, since it does work like a charm in localhost, but won't on any other network (even on my home network, using 192.168.xxx).
IE 9 & IE 10 will fail to convert the external images to data URL, CORS problem.
// What to do with the result (either data URL or directly the canvas if tainted)
var callback = function(d, isTainted) {
if (!isTainted) {
$('#chartImg')[0].src = d;
} else
$('#chartImg')[0].parentNode.replaceChild(d, $('#chartImg')[0]);
};
// The url of the external image (must be cross-origin compliant)
var extURL = 'https://dl.dropboxusercontent.com/s/13dv8vzmrlcmla2/tex2.jpg';
google.load('visualization', '1', {
packages: ['corechart']
})
var encodeCall = getbase64URI.bind(this, extURL, callback);
google.setOnLoadCallback(encodeCall);
// Google Chart part
function drawVisualizationDaily(imgUrl, callback, isTainted) {
var data = google.visualization.arrayToDataTable([
['Daily', 'Sales'],
['Mon', 4],
['Tue', 6],
['Wed', 6],
['Thu', 5],
['Fri', 3],
['Sat', 7],
['Sun', 7]
]);
var chart = new google.visualization.ColumnChart(document.getElementById('visualization'));
chart.draw(data, {
title: "Daily Sales",
width: 500,
height: 400,
hAxis: {
title: "Daily"
}
});
// Link to chart's svg element
var svgNode = chart.ea.querySelector('svg');
// Create a symbol for our image
var symbol = document.createElementNS('http://www.w3.org/2000/svg', 'symbol');
// An svg wrapper to allow size changing with <use>
symbol.setAttributeNS(null, 'viewBox', '0,0,10,10');
symbol.setAttributeNS(null, 'preserveAspectRatio', 'none');
symbol.id = 'background';
// And the actual image, with our encoded image
var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
img.setAttributeNS(null, 'preserveAspectRatio', 'none');
img.setAttributeNS(null, 'width', '100%');
img.setAttributeNS(null, 'height', '100%');
img.setAttributeNS('http://www.w3.org/1999/xlink', 'href', imgUrl);
symbol.appendChild(img);
svgNode.appendChild(symbol);
var blueRects = $("[fill='#3366cc']");
var max = blueRects.length - 1;
blueRects.each(function(index, element) {
var svgimg = document.createElementNS('http://www.w3.org/2000/svg', 'use');
svgimg.setAttributeNS(null, 'x', element.x.baseVal.value);
svgimg.setAttributeNS(null, 'y', element.y.baseVal.value);
svgimg.setAttributeNS(null, 'width', element.width.baseVal.value);
svgimg.setAttributeNS(null, 'height', element.height.baseVal.value);
svgimg.setAttributeNS('http://www.w3.org/1999/xlink', 'href', '#background');
svgNode.appendChild(svgimg);
if (index === max && !isTainted) // no need to call it if we don't have our dataURL encoded images
// a load event would be better but it doesn't fire in IE ...
setTimeout(exportSVG.bind(this, svgNode, callback, isTainted), 200);
});
}
function exportSVG(svgNode, callback, isTainted) {
var svgData = (new XMLSerializer()).serializeToString(svgNode);
var img = new Image();
img.onload = function() {
var canvas = document.createElement('canvas');
canvas.width = svgNode.getAttribute('width');
canvas.height = svgNode.getAttribute('height');
canvas.getContext('2d').drawImage(this, 0, 0);
var data, isTainted;
try {
data = canvas.toDataURL();
} catch (e) {
data = canvas;
isTainted = true;
}
callback(data, isTainted);
}
img.src = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgData);
}
// A simple function to convert an images's url to base64 data URL
function getbase64URI(url, callback) {
var img = new Image();
img.crossOrigin = "Anonymous";
img.onload = function() {
var c = document.createElement('canvas');
c.width = this.width;
c.height = this.height;
c.getContext('2d').drawImage(this, 0, 0);
var isTainted;
try {
c.toDataURL();
} catch (e) {
isTainted = true;
}
// if the canvas is tainted, return the url
var output = (isTainted) ? url : c.toDataURL();
drawVisualizationDaily(output, callback, isTainted);
}
img.src = url;
}
svg { border: 1px solid yellow; }
img { border: 1px solid green; }
canvas { border: 1px solid red; }
<script src="http://www.google.com/jsapi?.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="visualization"></div>
Right-click this image to save it:
<br>
<img id="chartImg" />
This example creates an svg container populated by an image. In my example the image is an svg image but you should be able to put in any type of image (jpg, png, gif). The container is created first then the image is created inside the container.
// create svg
var svg = document.createElementNS('http://www.w3.org/2000/svg','svg');
svg.setAttribute('class','shadowed handle_icon_sensors');
svg.setAttribute('height','25');
svg.setAttribute('width','25');
svg.setAttribute('id',idAttr);
svg.setAttribute('z-index','21000');
document.getElementById("zones_container").appendChild(svg);
// create svg image
var svgimg = document.createElementNS('http://www.w3.org/2000/svg','image');
svgimg.setAttribute('height','25');
svgimg.setAttribute('width','25');
svgimg.setAttributeNS('http://www.w3.org/1999/xlink','href','svg/icon_sensorYellow.svg');
svgimg.setAttribute('x','0');
svgimg.setAttribute('y','0');
document.getElementById(idAttr).appendChild(svgimg);
Note, When converted to src of img element retained blue background-color .
Try , after .each()
// set namespace attributes
var svg = $("svg").attr({
"xmlns": "http://www.w3.org/2000/svg",
"xmlns:xlink": "http://www.w3.org/1999/xlink"
})[0];
// create `data URI` of `svg`
var dataURI = "data:image/svg+xml;charset=utf-8;base64," + btoa(svg.outerHTML.trim());
// post `svg` as `data URI` to server
$.post("/path/to/server/", {
html: dataURI
}, "html")
.then(function (data) {
// do stuff
// `svg` `data URI`
console.log(data);
}, function (jqxhr, textStatus, errorThrown) {
console.log(textStatus, errorThrown);
});
jsfiddle http://jsfiddle.net/R8A8P/58/
this html worked in react
<a
href={chart.getImageURI()}
download="chart.png">
getImage
</a>
When I click the text on a card, I want to be able to edit the text like this website:
http://www.vistaprint.com/vp/ns/studio3.aspx?pf_id=064&combo_id=120585&free_studio_gallery=true&referer=http%3a%2f%2fwww.vistaprint.com%2fvp%2fns%2fdefault.aspx%3fdr%3d1%26rd%3d1%26GNF%3d0%26GP%3d5%252f19%252f2012%2b12%253a36%253a37%2bAM%26GPS%3d2448654652%26GNF%3d1%26GPLSID%3d&rd=1
Here is my code:
$(document).ready(function () {
var Total_layers = 0;
var Text = {};
/*set up stage for drawing image*/
var stage = new Kinetic.Stage({
container: "container",
width: 600,
height: 400
});
// var Layer = {};
/*create a layer object for placing text image over it and place it over the stage*/
var layer = new Kinetic.Layer();
//Layer[Total_layers]
/*Text Property*/
var New_Text = "Company Name";
var Text_Font = "Arial";
var Text_Color = "Black";
var Text_Size = "30";
var Text_Pos_X = 200;
var Text_Pos_y = 100;
var Selected_Text = new Kinetic.Text({});
var current_layer = 0;
// var text_selected = 1;
/*Add event for them*/
//var formElement = document.getElementById("New Text");
// formElement.addEventListener('change', Text_Changed, false);
var formElement = document.getElementById("selectFontSize");
formElement.addEventListener('change', Text_Size_Changed, false);
/*This Function will be Executed when the Size of the Text in consideration is changed*/
function Text_Size_Changed(e) {
var target = e.target;
Text_Size = target.value;
Text_Pos_X = 200; //Text[Total_layers].x;
Text_Pos_Y = 100; //Text[Total_layers].y;
//DeleteLayer(Total_layers);
layer.remove(Selected_Text);
Draw_text(Total_layers);
}
/*Function to swap the Kinetic Text object and get the selected Text object to The Top*/
function swap_layers(Selected_text) {
var temp = new Kinetic.Text({});
for (var i = 1; i <= Total_layers; i++) {
if (Text[i] == Selected_text) {
temp = Text[i];
Text[i] = Text[Total_layers];
Text[Total_layers] = temp;
break;
}
}
}
/*Add different Events to the Text objects once They are instantiated*/
function add_events(dest_Text) {
dest_Text.on("mouseover", function () {
document.body.style.cursor = "pointer";
});
dest_Text.on("mouseout", function () {
document.body.style.cursor = "default";
});
dest_Text.on("click", function () {
$("#selectFontSize").change(function () {
dest_Text.setFontSize($("#selectFontSize").val());
layer.draw(); // vì gọi layer.draw nên tất cả text trong layer đó đều dc vẽ lại
});
$("#selectFontFamily").change(function () {
swap_layers(dest_Text);
//dest_Text.setFontFamily($("#selectFontFamily").val());
//layer.draw();
});
});
}
/*Draw the Text over the layer depening upon the Text_object_Number*/
function Draw_text(Text_object_Number) {
/*Set the Properties of the Topmost object that is been modified and which will be added to the layer*/
Text[Text_object_Number] = new Kinetic.Text({
x: Text_Pos_X,
y: Text_Pos_Y,
text: New_Text,
fontSize: Text_Size,
fontFamily: Text_Font,
textFill: Text_Color,
align: "center",
verticalAlign: "middle",
draggable: "true"
});
/*Adds all the Text objects onto the layer and adds the events to every Text object */
//for (var i = 1; i <= Text_object_Number; i++) {
layer.add(Text[Text_object_Number]);
add_events(Text[Text_object_Number]);
//}
stage.add(layer);
}
$("#add_text").click(function () {
Total_layers++;
Text[Total_layers] = new Kinetic.Text({
x: Text_Pos_X,
y: Text_Pos_y,
text: New_Text,
fontSize: 30,
fontFamily: Text_Font,
textFill: Text_Color,
align: "center",
verticalAlign: "middle",
draggable: "true"
});
add_events(Text[Total_layers]);
layer.add(Text[Total_layers]);
stage.add(layer);
});
/*Adding an image to the present Context*/
var imageObj = new Image();
//alert("abc");
imageObj.onload = function () {
var T_shirt = new Kinetic.Image({
x: 60,
y: 0,
image: imageObj,
width: 550,
height: 400,
name: "image"
});
layer.add(T_shirt);
stage.add(layer);
}
imageObj.src = "../../Content/Image/imagepreview1.jpg";
});
I have tried many ways, but I still can't resolve this problem.
How can I do this by using canvas kineticjs in html5?
The very best solution imho is to use a simple JavaScript prompt:
myText.on('click', function(evt) {
this.setText(prompt('New Text:'));
layer.draw(); //redraw the layer containing the textfield
});
See my answer in this thread: editable Text option in kinetic js
You can't edit the text directly on the canvas, but what you can do is change it with events. So what you need is an input form that is created next to the canvas, and you can read from the form using javascript.
<input type=text id=changeText/>
this way when you click on some text a new input tag will appear and you can type in it and the text inside of the canvas will change as you type.
mytext.on('click', function(){ ... create new input element at the side ... });
//add some jQuery
$('#changeText').onchange( mytext.setText($('$changeText').val()));