How to render a blob on a canvas element? - html

How to render an image blob to a canvas element?
So far i have these two (simplified) functions to capture an image, transform it to a blob and eventually render the blob on a canvas
in this codepen, it just returns the default black image.
var canvas = document.getElementById('canvas');
var input = document.getElementById('input');
var ctx = canvas.getContext('2d');
var photo;
function picToBlob() {
var file = input.files[0];
canvas.toBlob(function(blob) {
var newImg = document.createElement("img"),
url = URL.createObjectURL(blob);
newImg.onload = function() {
ctx.drawImage(this, 0, 0);
photo = blob;
URL.revokeObjectURL(url);
};
newImg.src = url;
}, file.type, 0.5);
canvas.renderImage(photo);
}
HTMLCanvasElement.prototype.renderImage = function(blob) {
var canvas = this;
var ctx = canvas.getContext('2d');
var img = new Image();
img.onload = function() {
ctx.drawImage(img, 0, 0)
}
img.src = URL.createObjectURL(blob);
}
input.addEventListener('change', picToBlob, false);

I think you need to tidy up your code a bit. It's hard to know what you are trying to achieve because there are many unnecessary lines of code. The main problem is that blob is coming undefined here
HTMLCanvasElement.prototype.renderImage = function(blob){
because photo never gets initialized here inside the toBlob function...which is unnecessary for what you are trying to achieve.
Here's a simplified working version of your code snippet
var canvas = document.getElementById('canvas');
var input = document.getElementById('input');
function picToBlob() {
canvas.renderImage(input.files[0]);
}
HTMLCanvasElement.prototype.renderImage = function(blob){
var ctx = this.getContext('2d');
var img = new Image();
img.onload = function(){
ctx.drawImage(img, 0, 0)
}
img.src = URL.createObjectURL(blob);
};
input.addEventListener('change', picToBlob, false);
<input type='file' accept='image' capture='camera' id='input'>
<canvas id = 'canvas'></canvas>

You can also use createImageBitmap to directly render a blob into the canvas:
createImageBitmap(blob).then(imageBitmap=>{ctx.drawImage(imageBitmap,0,0)})
var canvas = document.getElementById('canvas');
var input = document.getElementById('input');
function blobToCanvas() {
createImageBitmap(input.files[0]).then(imageBitmap => {
console.log(imageBitmap);
canvas.getContext('2d').drawImage(imageBitmap, 0, 0)
})
}
input.addEventListener('change', blobToCanvas, false);
<input type='file' accept='image' capture='camera' id='input'>
<canvas id='canvas'></canvas>

You can use it as below
function renderImage(canvas, blob) {
const ctx = canvas.getContext('2d')
const img = new Image()
img.onload = (event) => {
URL.revokeObjectURL(event.target.src) // 👈 This is important. If you are not using the blob, you should release it if you don't want to reuse it. It's good for memory.
ctx.drawImage(event.target, 0, 0)
}
img.src = URL.createObjectURL(blob)
}
below is an example
/**
* #param {HTMLCanvasElement} canvas: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API
* #param {Blob} blob: https://developer.mozilla.org/en-US/docs/Web/API/Blob
* */
function renderImage(canvas, blob) {
const ctx = canvas.getContext('2d')
switch (blob.type) {
case "image/jpeg": // Normally, you don't need it (switch), but if you have a special case, then you can consider it.
case "image/png":
const img = new Image()
img.onload = (event) => {
URL.revokeObjectURL(event.target.src) // Once it loaded the resource, then you can free it at the beginning.
ctx.drawImage(event.target, 0, 0)
}
img.src = URL.createObjectURL(blob)
break
}
}
// 👇 below is test
(() => {
const canvas = document.querySelector('canvas')
const input = document.querySelector('input')
input.addEventListener('change',
(event) => {
const file = event.target.files[0]
const blob = new Blob(
[file],
{"type": file.type} // If the type is unknown, default is empty string.
)
renderImage(canvas, blob)
}
)
})()
<div><input type='file' accept='.png,.jpg'></div>
<canvas></canvas>
another example to show you What effect of the revokeObjectURL.
<div></div>
<canvas width="477" height="600"></canvas>
<script>
async function renderImage(canvas, blob, isNeedRevoke=true) {
const ctx = canvas.getContext('2d')
const img = new Image() // The upper part of the painting.
const img2 = new Image() // The lower part of the painting.
await new Promise(resolve => {
img.onload = (event) => {
if (isNeedRevoke) {
URL.revokeObjectURL(event.target.src)
}
ctx.drawImage(event.target,
0, 0, 477, 300,
0, 0, 477, 300
)
resolve()
}
img.src = URL.createObjectURL(blob)
setTimeout(resolve, 2000)
}).then(() => {
img2.onload = (event) => {
ctx.drawImage(event.target,
0, 300, 477, 300,
0, 300, 477, 300
)
}
img2.src = img.src // 👈 If URL.revokeObjectURL(img.src) happened, then img2.src can't find the resource, such that img2.onload will not happen.
})
}
function CreateTestButton(canvas, btnText, isNeedRevoke) {
const button = document.createElement("button")
button.innerText = btnText
button.onclick = async (event) => {
canvas.getContext("2d").clearRect(0, 0, canvas.width, canvas.height) // clear canvas
fetch("https://upload.wikimedia.org/wikipedia/commons/thumb/6/6a/PNG_Test.png/477px-PNG_Test.png")
.then(async response=>{
const blob = await response.blob()
renderImage(canvas, blob, isNeedRevoke)
}).catch(err=>console.error(err))
}
return button
}
(() => {
window.onload = () => {
const canvas = document.querySelector('canvas')
const div = document.querySelector('div')
const btn1 = CreateTestButton(canvas, "Without URL.revokeObjectURL", false)
const btn2 = CreateTestButton(canvas, "URL.revokeObjectURL", true)
div.append(btn1, btn2)
}
})()
</script>

Related

How to create Room name Text inside the room element?

Is there any way to create room name text inside the room element in forge viewer?
I have room elements in the forge viewer as per the below image.
So, I can read a room name from element properties. Then, I want to create room name text in forge viewer. May I have the solution?
Thanks In advance,
Update 2021-06-29
Added some conditions to avoid invalid data input.
/////////////////////////////////////////////////////////////////////
// Copyright (c) Autodesk, Inc. All rights reserved
// Written by Forge Partner Development
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted,
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.
//
// AUTODESK PROVIDES THIS PROGRAM 'AS IS' AND WITH ALL FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
/////////////////////////////////////////////////////////////////////
//ref: https://stackoverflow.com/a/61262544
class TextMeasurer {
constructor() {
const SVG_NS = 'http://www.w3.org/2000/svg';
this.svg = document.createElementNS(SVG_NS, 'svg');
this.svg.style.visibility = 'hidden';
this.svg.setAttribute('xmlns', SVG_NS)
this.svg.setAttribute('width', 0);
this.svg.setAttribute('height', 0);
this.svgtext = document.createElementNS(SVG_NS, 'text');
this.svg.appendChild(this.svgtext);
this.svgtext.setAttribute('x', 0);
this.svgtext.setAttribute('y', 0);
document.querySelector('body').appendChild(this.svg);
}
/**
* Measure a single line of text, including the bounding box, inner size and lead and trail X
* #param {string} text Single line of text
* #param {string} fontFamily Name of font family
* #param {string} fontSize Font size including units
*/
measureText(text, fontFamily, fontSize) {
this.svgtext.setAttribute('font-family', fontFamily);
this.svgtext.setAttribute('font-size', fontSize);
this.svgtext.textContent = text;
let bbox = this.svgtext.getBBox();
let textLength = this.svgtext.getComputedTextLength();
// measure the overflow before and after the line caused by font side bearing
// Rendering should start at X + leadX to have the edge of the text appear at X
// when rendering left-aligned left-to-right
let baseX = parseInt(this.svgtext.getAttribute('x'));
let overflow = bbox.width - textLength;
let leadX = Math.abs(baseX - bbox.x);
let trailX = overflow - leadX;
document.querySelector('body').removeChild(this.svg);
return {
bbWidth: bbox.width,
textLength: textLength,
leadX: leadX,
trailX: trailX,
bbHeight: bbox.height
};
}
}
class AecRoomTagsExtension extends Autodesk.Viewing.Extension {
constructor(viewer, options) {
super(viewer, options);
this.modelBuilder = null;
this.idPrefix = 100;
}
async load() {
const modelBuilderExt = await this.viewer.loadExtension('Autodesk.Viewing.SceneBuilder');
const modelBuilder = await modelBuilderExt.addNewModel({
conserveMemory: false,
modelNameOverride: 'Room Tags'
});
this.modelBuilder = modelBuilder;
if (!this.viewer.isLoadDone()) {
this.viewer.addEventListener(
Autodesk.Viewing.GEOMETRY_LOADED_EVENT,
() => this.createRoomTags(),
{ once: true }
);
} else {
this.createRoomTags();
}
return true;
}
unload() {
this.viewer.impl.unloadModel(this.modelBuilder.model);
return true;
}
pxToMm(val) {
return val / 3.7795275591;
}
mmToFt(val) {
return val / 304.8;
}
createLabel(params) {
const text = params.text;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'yellow';
ctx.fillRect(0, 0, canvas.width, canvas.height);
const fontSize = params.fontSize || 512;
const fontName = 'serif';
let offset = 2;
//Usage:
let m = new TextMeasurer();
let textDimensions = m.measureText(text, fontName, `${fontSize}px`);
canvas.height = textDimensions.bbHeight - (fontSize / 32 + 2) * offset;
canvas.width = textDimensions.bbWidth + offset + 3 * offset;
ctx.textBaseline = 'top';
ctx.fillStyle = '#000';
ctx.textAlign = 'left';
ctx.font = `${fontSize}px ${fontName}`;
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, textDimensions.bbWidth + offset * 2, canvas.height);
ctx.fillStyle = '#000';
ctx.fillText(text, offset, offset + (fontSize / 32 + 3) * offset);
ctx.strokeRect(0, 0, textDimensions.bbWidth + offset * 2, canvas.height);
const labelBlobUrl = canvas.toDataURL();
//console.log(labelBlobUrl);
const image = new Image();
const texture = new THREE.Texture();
texture.image = image;
image.src = labelBlobUrl;
image.onload = function () {
texture.needsUpdate = true;
};
const labelDbId = this.idPrefix++;
const matName = `label-mat-${labelDbId}`;
const material = new THREE.MeshPhongMaterial({ map: texture, side: THREE.DoubleSide, opacity: 0.8, transparent: true });
material.map.minFilter = THREE.LinearFilter;
this.modelBuilder.addMaterial(matName, material);
const labelMat = this.modelBuilder.findMaterial(matName);
const planeWidth = this.mmToFt(this.pxToMm(canvas.width));
const planeHeight = this.mmToFt(this.pxToMm(canvas.height));
let planeGeo = new THREE.PlaneBufferGeometry(planeWidth, planeHeight);
let plane = new THREE.Mesh(planeGeo, labelMat);
plane.matrix = new THREE.Matrix4().compose(
params.position,
new THREE.Quaternion(0, 0, 0, 1),
new THREE.Vector3(1, 1, 1)
);
plane.dbId = labelDbId;
this.modelBuilder.addMesh(plane);
}
async createRoomTags() {
const getRoomDbIdsAsync = () => {
return new Promise((resolve, reject) => {
this.viewer.search(
'Revit Rooms',
(dbIds) => resolve(dbIds),
(error) => reject(error),
['Category'],
{ searchHidden: true }
);
});
};
const getPropertiesAsync = (dbId, model) => {
return new Promise((resolve, reject) => {
model.getProperties2(
dbId,
(result) => resolve(result),
(error) => reject(error)
);
});
};
const getBoxAsync = (dbId, model) => {
return new Promise((resolve, reject) => {
const tree = model.getInstanceTree();
const frags = model.getFragmentList();
let bounds = new THREE.Box3();
tree.enumNodeFragments(dbId, function (fragId) {
let box = new THREE.Box3();
frags.getWorldBounds(fragId, box);
bounds.union(box);
}, true);
return resolve(bounds);
});
};
const getRoomNameAsync = async (dbId, model) => {
const tree = model.getInstanceTree();
let name = tree.getNodeName(dbId);
if (!name) {
const props = await getPropertiesAsync(dbId, model);
name = props?.name;
}
return name;
};
try {
let roomDbIds = await getRoomDbIdsAsync();
if (!roomDbIds || roomDbIds.length <= 0) {
throw new Error('No Rooms found in current model');
}
const model = this.viewer.model;
const currentViewableId = this.viewer.model?.getDocumentNode().data.viewableID;
const masterViews = this.viewer.model?.getDocumentNode().getMasterViews();
const masterViewIds = masterViews?.map(v => v.data.viewableID);
if (!masterViewIds.includes(currentViewableId)) {
throw new Error('Current view does not contain any Rooms');
}
for (let i = 0; i < roomDbIds.length; i++) {
const dbId = roomDbIds[i];
const name = await getRoomNameAsync(dbId, model);
if (!name) {
console.warn(`[AecRoomTagsExtension]: ${dbId} Room \`${name}\` doesn't have valid name`);
continue;
}
const roomProps = await getPropertiesAsync(dbId, model);
const possibleViewableIds = roomProps.properties.filter(prop => prop.attributeName === 'viewable_in').map(prop => prop.displayValue);
if (!possibleViewableIds.includes(currentViewableId)) {
console.warn(`[AecRoomTagsExtension]: ${dbId} Room \`${name}\` is not visible in current view \`${currentViewableId}\``);
continue;
}
const box = await getBoxAsync(dbId, model);
if (!box) {
console.warn(`[AecRoomTagsExtension]: ${dbId} Room \`${name}\` has an invalid bounding box`);
continue;
}
const center = box.center();
if (isNaN(center.x) || isNaN(center.y) || isNaN(center.z)) {
console.warn(`[AecRoomTagsExtension]: ${dbId} Room \`${name}\` has an invalid bounding box`);
continue;
}
//console.log(i, dbId, name, box, center);
const pos = new THREE.Vector3(
center.x,
center.y,
box.min.z + this.mmToFt(10)
);
this.createLabel({
text: name.replace(/ *\[[^)]*\] */g, ""),
position: pos,
fontSize: 512 // in pixel
});
}
// uncomment to prevent selection on tags
// const dbIds = this.modelBuilder.model.getFragmentList().fragments.fragId2dbId;
// const model = this.modelBuilder.model;
// this.viewer.lockSelection(dbIds, true, model);
} catch (ex) {
console.warn(`[AecRoomTagsExtension]: ${ex}`);
}
}
}
Autodesk.Viewing.theExtensionManager.registerExtension('Autodesk.ADN.AecRoomTagsExtension', AecRoomTagsExtension);
=============================
It's similar to the Gird solution: https://stackoverflow.com/a/68129012/7745569
Not perfect, but it works. You may need to adjust the tag placement point (position) based on your model. Currently, tags are placed on the center of the bottom face of the Room bounding box.
/////////////////////////////////////////////////////////////////////
// Copyright (c) Autodesk, Inc. All rights reserved
// Written by Forge Partner Development
//
// Permission to use, copy, modify, and distribute this software in
// object code form for any purpose and without fee is hereby granted,
// provided that the above copyright notice appears in all copies and
// that both that copyright notice and the limited warranty and
// restricted rights notice below appear in all supporting
// documentation.
//
// AUTODESK PROVIDES THIS PROGRAM 'AS IS' AND WITH ALL FAULTS.
// AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE. AUTODESK, INC.
// DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
// UNINTERRUPTED OR ERROR FREE.
/////////////////////////////////////////////////////////////////////
//ref: https://stackoverflow.com/a/61262544
class TextMeasurer {
constructor() {
const SVG_NS = 'http://www.w3.org/2000/svg';
this.svg = document.createElementNS(SVG_NS, 'svg');
this.svg.style.visibility = 'hidden';
this.svg.setAttribute('xmlns', SVG_NS)
this.svg.setAttribute('width', 0);
this.svg.setAttribute('height', 0);
this.svgtext = document.createElementNS(SVG_NS, 'text');
this.svg.appendChild(this.svgtext);
this.svgtext.setAttribute('x', 0);
this.svgtext.setAttribute('y', 0);
document.querySelector('body').appendChild(this.svg);
}
/**
* Measure a single line of text, including the bounding box, inner size and lead and trail X
* #param {string} text Single line of text
* #param {string} fontFamily Name of font family
* #param {string} fontSize Font size including units
*/
measureText(text, fontFamily, fontSize) {
this.svgtext.setAttribute('font-family', fontFamily);
this.svgtext.setAttribute('font-size', fontSize);
this.svgtext.textContent = text;
let bbox = this.svgtext.getBBox();
let textLength = this.svgtext.getComputedTextLength();
// measure the overflow before and after the line caused by font side bearing
// Rendering should start at X + leadX to have the edge of the text appear at X
// when rendering left-aligned left-to-right
let baseX = parseInt(this.svgtext.getAttribute('x'));
let overflow = bbox.width - textLength;
let leadX = Math.abs(baseX - bbox.x);
let trailX = overflow - leadX;
document.querySelector('body').removeChild(this.svg);
return {
bbWidth: bbox.width,
textLength: textLength,
leadX: leadX,
trailX: trailX,
bbHeight: bbox.height
};
}
}
class AecRoomTagsExtension extends Autodesk.Viewing.Extension {
constructor(viewer, options) {
super(viewer, options);
this.modelBuilder = null;
this.idPrefix = 100;
}
async load() {
const modelBuilderExt = await this.viewer.loadExtension('Autodesk.Viewing.SceneBuilder');
const modelBuilder = await modelBuilderExt.addNewModel({
conserveMemory: false,
modelNameOverride: 'Room Tags'
});
this.modelBuilder = modelBuilder;
if (!this.viewer.isLoadDone()) {
this.viewer.addEventListener(
Autodesk.Viewing.GEOMETRY_LOADED_EVENT,
() => this.createRoomTags(),
{ once: true }
);
} else {
this.createRoomTags();
}
return true;
}
unload() {
this.viewer.impl.unloadModel(this.modelBuilder.model);
return true;
}
pxToMm(val) {
return val / 3.7795275591;
}
mmToFt(val) {
return val / 304.8;
}
createLabel(params) {
const text = params.text;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'yellow';
ctx.fillRect(0, 0, canvas.width, canvas.height);
const fontSize = params.fontSize || 512;
const fontName = 'serif';
let offset = 2;
//Usage:
let m = new TextMeasurer();
let textDimensions = m.measureText(text, fontName, `${fontSize}px`);
canvas.height = textDimensions.bbHeight - (fontSize / 32 + 2) * offset;
canvas.width = textDimensions.bbWidth + offset + 3 * offset;
ctx.textBaseline = 'top';
ctx.fillStyle = '#000';
ctx.textAlign = 'left';
ctx.font = `${fontSize}px ${fontName}`;
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, textDimensions.bbWidth + offset * 2, canvas.height);
ctx.fillStyle = '#000';
ctx.fillText(text, offset, offset + (fontSize / 32 + 3) * offset);
ctx.strokeRect(0, 0, textDimensions.bbWidth + offset * 2, canvas.height);
const labelBlobUrl = canvas.toDataURL();
//console.log(labelBlobUrl);
const image = new Image();
const texture = new THREE.Texture();
texture.image = image;
image.src = labelBlobUrl;
image.onload = function () {
texture.needsUpdate = true;
};
const planeWidth = this.mmToFt(this.pxToMm(canvas.width));
const planeHeight = this.mmToFt(this.pxToMm(canvas.height));
let planeGeo = new THREE.PlaneBufferGeometry(planeWidth, planeHeight);
let plane = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ map: texture, side: THREE.DoubleSide, opacity: 0.8, transparent: true }));
plane.matrix = new THREE.Matrix4().compose(
params.position,
new THREE.Quaternion(0, 0, 0, 1),
new THREE.Vector3(1, 1, 1)
);
plane.dbId = this.idPrefix++;
this.modelBuilder.addMesh(plane);
}
async createRoomTags() {
const getRoomDbIdsAsync = () => {
return new Promise((resolve, reject) => {
this.viewer.search(
'Revit Rooms',
(dbIds) => resolve(dbIds),
(error) => reject(error),
['Category'],
{ searchHidden: true }
);
});
};
const getPropertiesAsync = (dbId, model) => {
return new Promise((resolve, reject) => {
model.getProperties2(
dbId,
(result) => resolve(result),
(error) => reject(error)
);
});
};
const getBoxAsync = (dbId, model) => {
return new Promise((resolve, reject) => {
const tree = model.getInstanceTree();
const frags = model.getFragmentList();
tree.enumNodeFragments(dbId, function (fragId) {
let bounds = new THREE.Box3();
frags.getWorldBounds(fragId, bounds);
return resolve(bounds);
}, true);
});
};
const getRoomName = (dbId, model) => {
const tree = model.getInstanceTree();
return tree.getNodeName(dbId);
};
try {
const roomDbIds = await getRoomDbIdsAsync();
if (!roomDbIds || roomDbIds.length <= 0) {
throw new Error('No Rooms found in current model');
}
const model = this.viewer.model;
const currentViewableId = this.viewer.model?.getDocumentNode().data.viewableID;
const firstRoomProps = await getPropertiesAsync(roomDbIds[0], this.viewer.model);
const possibleViewableIds = firstRoomProps.properties.filter(prop => prop.attributeName === 'viewable_in').map(prop => prop.displayValue);
const masterViews = this.viewer.model?.getDocumentNode().getMasterViews();
const masterViewIds = masterViews?.map(v => v.data.viewableID);
if (!masterViewIds.includes(currentViewableId) || !possibleViewableIds.includes(currentViewableId)) {
throw new Error('Current view does not contain any Rooms');
}
for (let i = roomDbIds.length - 1; i >= 0; i--) {
const dbId = roomDbIds[i];
const box = await getBoxAsync(dbId, model);
const name = getRoomName(dbId, model);
const center = box.center();
const pos = new THREE.Vector3(
center.x,
center.y,
box.min.z + this.mmToFt(10)
);
this.createLabel({
text: name.replace(/ *\[[^)]*\] */g, ""),
position: pos,
fontSize: 512 // in pixel
});
}
// uncomment to prevent selection on tags
// const dbIds = this.modelBuilder.model.getFragmentList().fragments.fragId2dbId;
// const model = this.modelBuilder.model;
// this.viewer.lockSelection(dbIds, true, model);
} catch (ex) {
console.warn(`[AecRoomTagsExtension]: ${ex}`);
}
}
}
Autodesk.Viewing.theExtensionManager.registerExtension('Autodesk.ADN.AecRoomTagsExtension', AecRoomTagsExtension);
Here are the dmeo snapshots:

fabricJS: Scale canvas view

I have converted a pdf to image format using PDF.JS and rendered that to the canvas. While that process the rendered pdf image is showing blurred in the canvas.
I have no idea how to scale the image to some viewable format rather than being so blurred.
Image of the pdf in canvas:
Here in the image you can clearly see that the rendered image is not in readable format!
Here is the fiddle link: https://jsfiddle.net/kjxes63f/
var fabricCanvas;
fabricCanvas = new fabric.Canvas('firtcanvas');
document.querySelector("#pdf-upload").addEventListener("change", function (e) {
var file = e.target.files[0]
console.log("pdf evente");
console.log(e);
if (file.type != "application/pdf") {
console.error(file.name, "is not a pdf file.")
return
}
var fileReader = new FileReader();
fileReader.onload = function () {
var typedarray = new Uint8Array(this.result);
console.log("typedarray");
console.log(typedarray);
console.log("this.result");
console.log(this.result);
PDFJS.getDocument(typedarray).then(function (pdf) {
// you can now use *pdf* here
console.log("the pdf has ", pdf.numPages, "page(s).")
pdf.getPage(pdf.numPages).then(function (page) {
// you can now use *page* here
var viewport = page.getViewport(2.0);
var fabricCanvas = document.querySelector("#firtcanvas")
fabricCanvas.height = viewport.height;
fabricCanvas.width = viewport.width;
page.render({
canvasContext: fabricCanvas.getContext('2d'),
viewport: viewport
}).then(function () {
bg = fabricCanvas.toDataURL("image/png");
fabric.Image.fromURL(bg, function (img) {
img.scaleToHeight(800);
img.scaleToWidth(600);
console.log("img");
console.log(img);
console.log(bg);
var imgCanvas = img.set({ left: 0, top: 0, width: 150, height: 150 });
fabricCanvas.add(imgCanvas);
});
});
});
});
};
fileReader.readAsArrayBuffer(file);
});
Construction drawings are often wide-format and hard to read onscreen without zoom. As a work-around I'd suggest adding a zoom function to your canvas like below.
var canvas = new fabric.Canvas('pdfcanvas');
canvas.selection = false;
canvas.setHeight(450);
canvas.setWidth(636);
//zoom function
canvas.on('mouse:wheel', function(opt) {
var delta = opt.e.deltaY;
var pointer = canvas.getPointer(opt.e);
var zoom = canvas.getZoom();
zoom = zoom - delta / 200;
if (zoom > 10) zoom = 10;
if (zoom < 1) {
zoom = 1;
canvas.setViewportTransform([1, 0, 0, 1, 0, 0]);
}
canvas.zoomToPoint({
'x': opt.e.offsetX,
'y': opt.e.offsetY
}, zoom);
opt.e.preventDefault();
opt.e.stopPropagation();
});
//pdf load
document.querySelector('#pdf-upload').addEventListener('change', function(e) {
var pageEl = document.getElementById('page-container');
var file = e.target.files[0];
if (file.type == 'application/pdf') {
var fileReader = new FileReader();
fileReader.onload = function() {
var typedarray = new Uint8Array(this.result);
PDFJS.getDocument(typedarray).then(function(pdf) {
//console.log('the pdf has ', pdf.numPages, 'page(s).');
pdf.getPage(pdf.numPages).then(function(pageEl) {
var viewport = pageEl.getViewport(2.0);
var canvasEl = document.querySelector('canvas');
canvasEl.height = viewport.height;
canvasEl.width = viewport.width;
pageEl.render({
'canvasContext': canvasEl.getContext('2d'),
'viewport': viewport
}).then(function() {
var bg = canvasEl.toDataURL('image/png');
fabric.Image.fromURL(bg, function(img) {
canvas.setBackgroundImage(img);
});
});
});
});
};
fileReader.readAsArrayBuffer(file);
};
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.4.0/fabric.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/1.8.349/pdf.min.js"></script>
<input id="pdf-upload" type="file">
<div id="page-container">
<canvas id="pdfcanvas"></canvas>
</div>

why i can't get full image size from convertToDataURLviaCanvas method in ionic 3?

I want original image size in offline mode also as shown in online mode in image 2.
My problem is that when i gone in app without internet connection, i loss full quality and size of images, because of i am using convertToDataURLviaCanvas().
so, please give here solution as soon as possible.
My code is:
I am using this function to convert all images:
convertToDataURLviaCanvas(url, outputFormat) {
return new Promise((resolve, reject) =>
{
let img = new Image();
img.crossOrigin = 'Anonymous';
img.onload = function () {
let canvas = <HTMLCanvasElement>document.createElement('CANVAS'),
ctx = canvas.getContext('2d'),
dataURL;
canvas.height = 1000;
canvas.width = 1000;
ctx.drawImage(img, 0, 0);
dataURL = canvas.toDataURL();
//callback(dataURL);
canvas = null;
resolve(dataURL);
};
img.src = url;
});
}
getTravelAdviceData() {
if(this.restProvider.getNetworkType() == 'none') {
// this.onlineGrid = false;
// this.offlineGrid = true;
this.storage.get('companyLogoOffline').then((data) => {
// Do something with latitude value
// console.log("offline data", data);
this.getcompanyLogo = data;
});
this.storage.get('travelTips64Image').then((data) => {
// Do something with latitude value
// console.log("offline data", data);
this.adviceArray = data;
// console.log("offline this.adviceArray", this.adviceArray);
});
} else {
// this.offlineGrid = false;
// this.onlineGrid = true;
this.restProvider.getTravelAdvice()
.then(data => {
let serviceData : any = data['consejosviaje'];
// this.adviceArray = serviceData;
let base64Image;
for (let i in serviceData) {
this.imagen = serviceData[i].imagen;
this.convertToDataURLviaCanvas(this.imagen, "image/jpeg").then(base64 => {
base64Image = base64;
this.texto = serviceData[i].texto;
this.adviceArrays64.push({'texto': this.texto, 'imagen': base64Image});
this.storage.set("travelTips64Image", this.adviceArrays64);
this.adviceArray = this.adviceArrays64;
});
}
});
}
}

HTML SVG download image [duplicate]

I want to convert SVG into bitmap images (like JPEG, PNG, etc.) through JavaScript.
Here is how you can do it through JavaScript:
Use the canvg JavaScript library to render the SVG image using Canvas: https://github.com/gabelerner/canvg
Capture a data URI encoded as a JPG (or PNG) from the Canvas, according to these instructions: Capture HTML Canvas as gif/jpg/png/pdf?
jbeard4 solution worked beautifully.
I'm using Raphael SketchPad to create an SVG. Link to the files in step 1.
For a Save button (id of svg is "editor", id of canvas is "canvas"):
$("#editor_save").click(function() {
// the canvg call that takes the svg xml and converts it to a canvas
canvg('canvas', $("#editor").html());
// the canvas calls to output a png
var canvas = document.getElementById("canvas");
var img = canvas.toDataURL("image/png");
// do what you want with the base64, write to screen, post to server, etc...
});
This seems to work in most browsers:
function copyStylesInline(destinationNode, sourceNode) {
var containerElements = ["svg","g"];
for (var cd = 0; cd < destinationNode.childNodes.length; cd++) {
var child = destinationNode.childNodes[cd];
if (containerElements.indexOf(child.tagName) != -1) {
copyStylesInline(child, sourceNode.childNodes[cd]);
continue;
}
var style = sourceNode.childNodes[cd].currentStyle || window.getComputedStyle(sourceNode.childNodes[cd]);
if (style == "undefined" || style == null) continue;
for (var st = 0; st < style.length; st++){
child.style.setProperty(style[st], style.getPropertyValue(style[st]));
}
}
}
function triggerDownload (imgURI, fileName) {
var evt = new MouseEvent("click", {
view: window,
bubbles: false,
cancelable: true
});
var a = document.createElement("a");
a.setAttribute("download", fileName);
a.setAttribute("href", imgURI);
a.setAttribute("target", '_blank');
a.dispatchEvent(evt);
}
function downloadSvg(svg, fileName) {
var copy = svg.cloneNode(true);
copyStylesInline(copy, svg);
var canvas = document.createElement("canvas");
var bbox = svg.getBBox();
canvas.width = bbox.width;
canvas.height = bbox.height;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, bbox.width, bbox.height);
var data = (new XMLSerializer()).serializeToString(copy);
var DOMURL = window.URL || window.webkitURL || window;
var img = new Image();
var svgBlob = new Blob([data], {type: "image/svg+xml;charset=utf-8"});
var url = DOMURL.createObjectURL(svgBlob);
img.onload = function () {
ctx.drawImage(img, 0, 0);
DOMURL.revokeObjectURL(url);
if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob)
{
var blob = canvas.msToBlob();
navigator.msSaveOrOpenBlob(blob, fileName);
}
else {
var imgURI = canvas
.toDataURL("image/png")
.replace("image/png", "image/octet-stream");
triggerDownload(imgURI, fileName);
}
document.removeChild(canvas);
};
img.src = url;
}
The solution to convert SVG to blob URL and blob URL to png image
const svg=`<svg version="1.1" baseProfile="full" width="300" height="200"
xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="red" />
<circle cx="150" cy="100" r="80" fill="green" />
<text x="150" y="125" font-size="60" text-anchor="middle" fill="white">SVG</text></svg>`
svgToPng(svg,(imgData)=>{
const pngImage = document.createElement('img');
document.body.appendChild(pngImage);
pngImage.src=imgData;
});
function svgToPng(svg, callback) {
const url = getSvgUrl(svg);
svgUrlToPng(url, (imgData) => {
callback(imgData);
URL.revokeObjectURL(url);
});
}
function getSvgUrl(svg) {
return URL.createObjectURL(new Blob([svg], { type: 'image/svg+xml' }));
}
function svgUrlToPng(svgUrl, callback) {
const svgImage = document.createElement('img');
// imgPreview.style.position = 'absolute';
// imgPreview.style.top = '-9999px';
document.body.appendChild(svgImage);
svgImage.onload = function () {
const canvas = document.createElement('canvas');
canvas.width = svgImage.clientWidth;
canvas.height = svgImage.clientHeight;
const canvasCtx = canvas.getContext('2d');
canvasCtx.drawImage(svgImage, 0, 0);
const imgData = canvas.toDataURL('image/png');
callback(imgData);
// document.body.removeChild(imgPreview);
};
svgImage.src = svgUrl;
}
change svg to match your element
function svg2img(){
var svg = document.querySelector('svg');
var xml = new XMLSerializer().serializeToString(svg);
var svg64 = btoa(xml); //for utf8: btoa(unescape(encodeURIComponent(xml)))
var b64start = 'data:image/svg+xml;base64,';
var image64 = b64start + svg64;
return image64;
};svg2img()
My use case was to have the svg data loaded from a network and this ES6 Class did the Job.
class SvgToPngConverter {
constructor() {
this._init = this._init.bind(this);
this._cleanUp = this._cleanUp.bind(this);
this.convertFromInput = this.convertFromInput.bind(this);
}
_init() {
this.canvas = document.createElement("canvas");
this.imgPreview = document.createElement("img");
this.imgPreview.style = "position: absolute; top: -9999px";
document.body.appendChild(this.imgPreview);
this.canvasCtx = this.canvas.getContext("2d");
}
_cleanUp() {
document.body.removeChild(this.imgPreview);
}
convertFromInput(input, callback) {
this._init();
let _this = this;
this.imgPreview.onload = function() {
const img = new Image();
_this.canvas.width = _this.imgPreview.clientWidth;
_this.canvas.height = _this.imgPreview.clientHeight;
img.crossOrigin = "anonymous";
img.src = _this.imgPreview.src;
img.onload = function() {
_this.canvasCtx.drawImage(img, 0, 0);
let imgData = _this.canvas.toDataURL("image/png");
if(typeof callback == "function"){
callback(imgData)
}
_this._cleanUp();
};
};
this.imgPreview.src = input;
}
}
Here is how you use it
let input = "https://restcountries.eu/data/afg.svg"
new SvgToPngConverter().convertFromInput(input, function(imgData){
// You now have your png data in base64 (imgData).
// Do what ever you wish with it here.
});
If you want a vanilla JavaScript version, you could head over to Babel website and transpile the code there.
Here a function that works without libraries and returns a Promise:
/**
* converts a base64 encoded data url SVG image to a PNG image
* #param originalBase64 data url of svg image
* #param width target width in pixel of PNG image
* #return {Promise<String>} resolves to png data url of the image
*/
function base64SvgToBase64Png (originalBase64, width) {
return new Promise(resolve => {
let img = document.createElement('img');
img.onload = function () {
document.body.appendChild(img);
let canvas = document.createElement("canvas");
let ratio = (img.clientWidth / img.clientHeight) || 1;
document.body.removeChild(img);
canvas.width = width;
canvas.height = width / ratio;
let ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
try {
let data = canvas.toDataURL('image/png');
resolve(data);
} catch (e) {
resolve(null);
}
};
img.onerror = function() {
resolve(null);
};
img.src = originalBase64;
});
}
On Firefox there is an issue for SVGs without set width / height.
See this working example including a fix for the Firefox issue.
This is an old question, in 2022 we have ES6 and we don't need 3rd party libraries.
Here is a very basic way to convert svg images into other formats.
The trick is to load the svg element as an img element, then use a canvas element to convert the image into the desired format. So, four steps are needed:
Extract svg as xml data string.
Load the xml data string into a img element
Convert the img element to a dataURL using a canvas element
Load the converted dataURL into a new img element
Step 1
Extracting a svg as xml data string is simple, we don't need to convert it as a base64 string. We just serialize it as XML then we encode the string as a URI:
// Select the element:
const $svg = document.getElementById('svg-container').querySelector('svg')
// Serialize it as xml string:
const svgAsXML = (new XMLSerializer()).serializeToString($svg)
// Encode it as a data string:
const svgData = `data:image/svg+xml,${encodeURIComponent(svgAsXML)}`
Step 2
Loading the xml data string into a img element:
// This function returns a Promise whenever the $img is loaded
const loadImage = async url => {
const $img = document.createElement('img')
$img.src = url
return new Promise((resolve, reject) => {
$img.onload = () => resolve($img)
$img.onerror = reject
$img.src = url
})
}
Step 3
Converting the img element to a dataURL using a canvas element:
const $canvas = document.createElement('canvas')
$canvas.width = $svg.clientWidth
$canvas.height = $svg.clientHeight
$canvas.getContext('2d').drawImage(img, 0, 0, $svg.clientWidth, $svg.clientHeight)
return $canvas.toDataURL(`image/${format}`, 1.0)
Step 4
Loading the converted dataURL into a new img element:
const $img = document.createElement('img')
$img.src = dataURL
$holder.appendChild($img)
Here you have a working snippet:
const $svg = document.getElementById('svg-container').querySelector('svg')
const $holder = document.getElementById('img-container')
const $label = document.getElementById('img-format')
const destroyChildren = $element => {
while ($element.firstChild) {
const $lastChild = $element.lastChild ?? false
if ($lastChild) $element.removeChild($lastChild)
}
}
const loadImage = async url => {
const $img = document.createElement('img')
$img.src = url
return new Promise((resolve, reject) => {
$img.onload = () => resolve($img)
$img.onerror = reject
})
}
const convertSVGtoImg = async e => {
const $btn = e.target
const format = $btn.dataset.format ?? 'png'
$label.textContent = format
destroyChildren($holder)
const svgAsXML = (new XMLSerializer()).serializeToString($svg)
const svgData = `data:image/svg+xml,${encodeURIComponent(svgAsXML)}`
const img = await loadImage(svgData)
const $canvas = document.createElement('canvas')
$canvas.width = $svg.clientWidth
$canvas.height = $svg.clientHeight
$canvas.getContext('2d').drawImage(img, 0, 0, $svg.clientWidth, $svg.clientHeight)
const dataURL = await $canvas.toDataURL(`image/${format}`, 1.0)
console.log(dataURL)
const $img = document.createElement('img')
$img.src = dataURL
$holder.appendChild($img)
}
const buttons = [...document.querySelectorAll('[data-format]')]
for (const $btn of buttons) {
$btn.onclick = convertSVGtoImg
}
.wrapper {
display: flex;
flex-flow: row nowrap;
width: 100vw;
}
.images {
display: flex;
flex-flow: row nowrap;
width: 70%;
}
.image {
width: 50%;
display: flex;
flex-flow: row wrap;
justify-content: center;
}
.label {
width: 100%;
text-align: center;
}
<div class="wrapper">
<div class="item images">
<div class="image left">
<div class="label">svg</div>
<div id="svg-container">
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="200" height="200" viewBox="0 0 248 204">
<path fill="#1d9bf0" d="M221.95 51.29c.15 2.17.15 4.34.15 6.53 0 66.73-50.8 143.69-143.69 143.69v-.04c-27.44.04-54.31-7.82-77.41-22.64 3.99.48 8 .72 12.02.73 22.74.02 44.83-7.61 62.72-21.66-21.61-.41-40.56-14.5-47.18-35.07 7.57 1.46 15.37 1.16 22.8-.87-23.56-4.76-40.51-25.46-40.51-49.5v-.64c7.02 3.91 14.88 6.08 22.92 6.32C11.58 63.31 4.74 33.79 18.14 10.71c25.64 31.55 63.47 50.73 104.08 52.76-4.07-17.54 1.49-35.92 14.61-48.25 20.34-19.12 52.33-18.14 71.45 2.19 11.31-2.23 22.15-6.38 32.07-12.26-3.77 11.69-11.66 21.62-22.2 27.93 10.01-1.18 19.79-3.86 29-7.95-6.78 10.16-15.32 19.01-25.2 26.16z"/>
</svg>
</div>
</div>
<div class="image right">
<div id="img-format" class="label"></div>
<div id="img-container"></div>
</div>
</div>
<div class="item buttons">
<button id="btn-png" data-format="png">PNG</button>
<button id="btn-jpg" data-format="jpeg">JPG</button>
<button id="btn-webp" data-format="webp">WEBP</button>
</div>
</div>
Svg to png can be converted depending on conditions:
If svg is in format SVG (string) paths:
create canvas
create new Path2D() and set svg as parameter
draw path on canvas
create image and use canvas.toDataURL() as src.
example:
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
let svgText = 'M10 10 h 80 v 80 h -80 Z';
let p = new Path2D('M10 10 h 80 v 80 h -80 Z');
ctx.stroke(p);
let url = canvas.toDataURL();
const img = new Image();
img.src = url;
Note that Path2D not supported in ie and partially supported in edge. Polyfill solves that:
https://github.com/nilzona/path2d-polyfill
Create svg blob and draw on canvas using .drawImage():
make canvas element
make a svgBlob object from the svg xml
make a url object from domUrl.createObjectURL(svgBlob);
create an Image object and assign url to image src
draw image into canvas
get png data string from canvas: canvas.toDataURL();
Nice description:
https://web.archive.org/web/20200125162931/http://ramblings.mcpher.com:80/Home/excelquirks/gassnips/svgtopng
Note that in ie you will get exception on stage of canvas.toDataURL(); It is because IE has too high security restriction and treats canvas as readonly after drawing image there. All other browsers restrict only if image is cross origin.
Use canvg JavaScript library. It is separate library but has useful functions.
Like:
ctx.drawSvg(rawSvg);
var dataURL = canvas.toDataURL();
I recently discovered a couple of image tracing libraries for JavaScript that indeed are able to build an acceptable approximation to the bitmap, both size and quality. I'm developing this JavaScript library and CLI :
https://www.npmjs.com/package/svg-png-converter
Which provides unified API for all of them, supporting browser and node, non depending on DOM, and a Command line tool.
For converting logos/cartoon/like images it does excellent job. For photos / realism some tweaking is needed since the output size can grow a lot.
It has a playground although right now I'm working on a better one, easier to use, since more features has been added:
https://cancerberosgx.github.io/demos/svg-png-converter/playground/#
There are several ways to convert SVG to PNG using the Canvg library.
In my case, I needed to get the PNG blob from inline SVG.
The library documentation provides an example (see OffscreenCanvas example).
But this method does not work at the moment in Firefox. Yes, you can enable the gfx.offscreencanvas.enabled option in the settings. But will every user on the site do this? :)
However, there is another way that will work in Firefox too.
const el = document.getElementById("some-svg"); //this is our inline SVG
var canvas = document.createElement('canvas'); //create a canvas for the SVG render
canvas.width = el.clientWidth; //set canvas sizes
canvas.height = el.clientHeight;
const svg = new XMLSerializer().serializeToString(el); //convert SVG to string
//render SVG inside canvas
const ctx = canvas.getContext('2d');
const v = await Canvg.fromString(ctx, svg);
await v.render();
let canvasBlob = await new Promise(resolve => canvas.toBlob(resolve));
For the last line thanks to this answer
get data URIs from SVG:
data:image/svg+xml;base64,${btoa(new XMLSerializer().serializeToString(svgElem))}
prepare an Image
create a canvas and use toDataURL to export.
Example
<!-- test data-->
<svg width="400" height="400"><g transform="translate(23.915343915343925,-80.03971756398937)" class="glyph" stroke="#000000" fill="#a0a0a0"><path d="M74.97 108.70L74.97 108.70L100.08 110.77Q93.89 147.91 87.35 179.89L87.35 179.89L148.23 179.89L148.23 194.34Q143.76 277.91 113.84 339.81L113.84 339.81Q144.44 363.54 163.70 382.46L163.70 382.46L146.51 402.75Q128.62 384.18 101.80 361.83L101.80 361.83Q75.32 405.85 34.39 436.80L34.39 436.80L17.20 415.82Q57.43 386.93 82.20 345.66L82.20 345.66Q57.78 326.40 27.86 304.39L27.86 304.39Q44.37 257.96 56.75 203.97L56.75 203.97L19.26 203.97L19.26 179.89L61.90 179.89Q69.47 145.16 74.97 108.70ZM93.20 323.99L93.20 323.99Q118.65 272.06 123.12 203.97L123.12 203.97L82.20 203.97Q69.47 260.03 55.71 297.17L55.71 297.17Q76.01 311.61 93.20 323.99ZM160.26 285.13L160.26 260.37L239.71 260.37L239.71 216.01Q268.25 191.24 294.05 155.48L294.05 155.48L170.58 155.48L170.58 130.71L322.94 130.71L322.94 155.48Q297.49 191.93 265.50 223.92L265.50 223.92L265.50 260.37L337.38 260.37L337.38 285.13L265.50 285.13L265.50 397.59Q265.50 431.64 237.65 431.64L237.65 431.64L187.09 431.64L180.21 407.57Q202.22 407.91 227.67 407.91L227.67 407.91Q239.71 407.91 239.71 390.03L239.71 390.03L239.71 285.13L160.26 285.13Z"></path></g></svg>
<button title="download">svg2png</button>
<script>
const output = {"name": "result.png", "width": 64, "height": 64}
document.querySelector("button").onclick = () => {
const svgElem = document.querySelector("svg")
// const uriData = `data:image/svg+xml;base64,${btoa(svgElem.outerHTML)}` // it may fail.
const uriData = `data:image/svg+xml;base64,${btoa(new XMLSerializer().serializeToString(svgElem))}`
const img = new Image()
img.src = uriData
img.onload = () => {
const canvas = document.createElement("canvas");
[canvas.width, canvas.height] = [output.width, output.height]
const ctx = canvas.getContext("2d")
ctx.drawImage(img, 0, 0, output.width, output.height)
// 👇 download
const a = document.createElement("a")
const quality = 1.0 // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingQuality
a.href = canvas.toDataURL("image/png", quality)
a.download = output.name
a.append(canvas)
a.click()
a.remove()
}
}
</script>
Here are my 2 cents. Somehow Download anchor tag is not working as expected in code snippet, however it was working fine in Chrome.
Here is working jsFiddle
const waitForImage = imgElem => new Promise(resolve => imgElem.complete ? resolve() : imgElem.onload = imgElem.onerror = resolve);
const svgToImgDownload = ext => {
if (!['png', 'jpg', 'webp'].includes(ext))
return;
const _svg = document.querySelector("#svg_container").querySelector('svg');
const xmlSerializer = new XMLSerializer();
let _svgStr = xmlSerializer.serializeToString(_svg);
const img = document.createElement('img');
img.src = 'data:image/svg+xml;base64,' + window.btoa(_svgStr);
waitForImage(img)
.then(_ => {
const canvas = document.createElement('canvas');
canvas.width = _svg.clientWidth;
canvas.height = _svg.clientHeight;
canvas.getContext('2d').drawImage(img, 0, 0, _svg.clientWidth, _svg.clientHeight);
return canvas.toDataURL('image/' + (ext == 'jpg' ? 'jpeg' : ext), 1.0);
})
.then(dataURL => {
console.log(dataURL);
document.querySelector("#img_download_btn").innerHTML = `Download`;
})
.catch(console.error);
};
document.querySelector('#map2Png').addEventListener('click', _ => svgToImgDownload('png'));
document.querySelector('#map2Jpg').addEventListener('click', _ => svgToImgDownload('jpg'));
document.querySelector('#map2Webp').addEventListener('click', _ => svgToImgDownload('webp'));
<div id="svg_container" style="float: left; width: 50%">
<svg width="200" height="200" viewBox="-100 -100 200 200">
<circle cx="0" cy="20" r="70" fill="#D1495B" />
<circle cx="0" cy="-75" r="12" fill="none" stroke="#F79257" stroke-width="2" />
<rect x="-17.5" y="-65" width="35" height="20" fill="#F79257" />
</svg>
</div>
<div>
<button id="map2Png">PNG</button>
<button id="map2Jpg">JPG</button>
<button id="map2Webp">WEBP</button>
</div>
<div id="img_download_btn"></div>

convert SVG code into png in javascript [duplicate]

I want to convert SVG into bitmap images (like JPEG, PNG, etc.) through JavaScript.
Here is how you can do it through JavaScript:
Use the canvg JavaScript library to render the SVG image using Canvas: https://github.com/gabelerner/canvg
Capture a data URI encoded as a JPG (or PNG) from the Canvas, according to these instructions: Capture HTML Canvas as gif/jpg/png/pdf?
jbeard4 solution worked beautifully.
I'm using Raphael SketchPad to create an SVG. Link to the files in step 1.
For a Save button (id of svg is "editor", id of canvas is "canvas"):
$("#editor_save").click(function() {
// the canvg call that takes the svg xml and converts it to a canvas
canvg('canvas', $("#editor").html());
// the canvas calls to output a png
var canvas = document.getElementById("canvas");
var img = canvas.toDataURL("image/png");
// do what you want with the base64, write to screen, post to server, etc...
});
This seems to work in most browsers:
function copyStylesInline(destinationNode, sourceNode) {
var containerElements = ["svg","g"];
for (var cd = 0; cd < destinationNode.childNodes.length; cd++) {
var child = destinationNode.childNodes[cd];
if (containerElements.indexOf(child.tagName) != -1) {
copyStylesInline(child, sourceNode.childNodes[cd]);
continue;
}
var style = sourceNode.childNodes[cd].currentStyle || window.getComputedStyle(sourceNode.childNodes[cd]);
if (style == "undefined" || style == null) continue;
for (var st = 0; st < style.length; st++){
child.style.setProperty(style[st], style.getPropertyValue(style[st]));
}
}
}
function triggerDownload (imgURI, fileName) {
var evt = new MouseEvent("click", {
view: window,
bubbles: false,
cancelable: true
});
var a = document.createElement("a");
a.setAttribute("download", fileName);
a.setAttribute("href", imgURI);
a.setAttribute("target", '_blank');
a.dispatchEvent(evt);
}
function downloadSvg(svg, fileName) {
var copy = svg.cloneNode(true);
copyStylesInline(copy, svg);
var canvas = document.createElement("canvas");
var bbox = svg.getBBox();
canvas.width = bbox.width;
canvas.height = bbox.height;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, bbox.width, bbox.height);
var data = (new XMLSerializer()).serializeToString(copy);
var DOMURL = window.URL || window.webkitURL || window;
var img = new Image();
var svgBlob = new Blob([data], {type: "image/svg+xml;charset=utf-8"});
var url = DOMURL.createObjectURL(svgBlob);
img.onload = function () {
ctx.drawImage(img, 0, 0);
DOMURL.revokeObjectURL(url);
if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob)
{
var blob = canvas.msToBlob();
navigator.msSaveOrOpenBlob(blob, fileName);
}
else {
var imgURI = canvas
.toDataURL("image/png")
.replace("image/png", "image/octet-stream");
triggerDownload(imgURI, fileName);
}
document.removeChild(canvas);
};
img.src = url;
}
The solution to convert SVG to blob URL and blob URL to png image
const svg=`<svg version="1.1" baseProfile="full" width="300" height="200"
xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="red" />
<circle cx="150" cy="100" r="80" fill="green" />
<text x="150" y="125" font-size="60" text-anchor="middle" fill="white">SVG</text></svg>`
svgToPng(svg,(imgData)=>{
const pngImage = document.createElement('img');
document.body.appendChild(pngImage);
pngImage.src=imgData;
});
function svgToPng(svg, callback) {
const url = getSvgUrl(svg);
svgUrlToPng(url, (imgData) => {
callback(imgData);
URL.revokeObjectURL(url);
});
}
function getSvgUrl(svg) {
return URL.createObjectURL(new Blob([svg], { type: 'image/svg+xml' }));
}
function svgUrlToPng(svgUrl, callback) {
const svgImage = document.createElement('img');
// imgPreview.style.position = 'absolute';
// imgPreview.style.top = '-9999px';
document.body.appendChild(svgImage);
svgImage.onload = function () {
const canvas = document.createElement('canvas');
canvas.width = svgImage.clientWidth;
canvas.height = svgImage.clientHeight;
const canvasCtx = canvas.getContext('2d');
canvasCtx.drawImage(svgImage, 0, 0);
const imgData = canvas.toDataURL('image/png');
callback(imgData);
// document.body.removeChild(imgPreview);
};
svgImage.src = svgUrl;
}
change svg to match your element
function svg2img(){
var svg = document.querySelector('svg');
var xml = new XMLSerializer().serializeToString(svg);
var svg64 = btoa(xml); //for utf8: btoa(unescape(encodeURIComponent(xml)))
var b64start = 'data:image/svg+xml;base64,';
var image64 = b64start + svg64;
return image64;
};svg2img()
My use case was to have the svg data loaded from a network and this ES6 Class did the Job.
class SvgToPngConverter {
constructor() {
this._init = this._init.bind(this);
this._cleanUp = this._cleanUp.bind(this);
this.convertFromInput = this.convertFromInput.bind(this);
}
_init() {
this.canvas = document.createElement("canvas");
this.imgPreview = document.createElement("img");
this.imgPreview.style = "position: absolute; top: -9999px";
document.body.appendChild(this.imgPreview);
this.canvasCtx = this.canvas.getContext("2d");
}
_cleanUp() {
document.body.removeChild(this.imgPreview);
}
convertFromInput(input, callback) {
this._init();
let _this = this;
this.imgPreview.onload = function() {
const img = new Image();
_this.canvas.width = _this.imgPreview.clientWidth;
_this.canvas.height = _this.imgPreview.clientHeight;
img.crossOrigin = "anonymous";
img.src = _this.imgPreview.src;
img.onload = function() {
_this.canvasCtx.drawImage(img, 0, 0);
let imgData = _this.canvas.toDataURL("image/png");
if(typeof callback == "function"){
callback(imgData)
}
_this._cleanUp();
};
};
this.imgPreview.src = input;
}
}
Here is how you use it
let input = "https://restcountries.eu/data/afg.svg"
new SvgToPngConverter().convertFromInput(input, function(imgData){
// You now have your png data in base64 (imgData).
// Do what ever you wish with it here.
});
If you want a vanilla JavaScript version, you could head over to Babel website and transpile the code there.
Here a function that works without libraries and returns a Promise:
/**
* converts a base64 encoded data url SVG image to a PNG image
* #param originalBase64 data url of svg image
* #param width target width in pixel of PNG image
* #return {Promise<String>} resolves to png data url of the image
*/
function base64SvgToBase64Png (originalBase64, width) {
return new Promise(resolve => {
let img = document.createElement('img');
img.onload = function () {
document.body.appendChild(img);
let canvas = document.createElement("canvas");
let ratio = (img.clientWidth / img.clientHeight) || 1;
document.body.removeChild(img);
canvas.width = width;
canvas.height = width / ratio;
let ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
try {
let data = canvas.toDataURL('image/png');
resolve(data);
} catch (e) {
resolve(null);
}
};
img.onerror = function() {
resolve(null);
};
img.src = originalBase64;
});
}
On Firefox there is an issue for SVGs without set width / height.
See this working example including a fix for the Firefox issue.
This is an old question, in 2022 we have ES6 and we don't need 3rd party libraries.
Here is a very basic way to convert svg images into other formats.
The trick is to load the svg element as an img element, then use a canvas element to convert the image into the desired format. So, four steps are needed:
Extract svg as xml data string.
Load the xml data string into a img element
Convert the img element to a dataURL using a canvas element
Load the converted dataURL into a new img element
Step 1
Extracting a svg as xml data string is simple, we don't need to convert it as a base64 string. We just serialize it as XML then we encode the string as a URI:
// Select the element:
const $svg = document.getElementById('svg-container').querySelector('svg')
// Serialize it as xml string:
const svgAsXML = (new XMLSerializer()).serializeToString($svg)
// Encode it as a data string:
const svgData = `data:image/svg+xml,${encodeURIComponent(svgAsXML)}`
Step 2
Loading the xml data string into a img element:
// This function returns a Promise whenever the $img is loaded
const loadImage = async url => {
const $img = document.createElement('img')
$img.src = url
return new Promise((resolve, reject) => {
$img.onload = () => resolve($img)
$img.onerror = reject
$img.src = url
})
}
Step 3
Converting the img element to a dataURL using a canvas element:
const $canvas = document.createElement('canvas')
$canvas.width = $svg.clientWidth
$canvas.height = $svg.clientHeight
$canvas.getContext('2d').drawImage(img, 0, 0, $svg.clientWidth, $svg.clientHeight)
return $canvas.toDataURL(`image/${format}`, 1.0)
Step 4
Loading the converted dataURL into a new img element:
const $img = document.createElement('img')
$img.src = dataURL
$holder.appendChild($img)
Here you have a working snippet:
const $svg = document.getElementById('svg-container').querySelector('svg')
const $holder = document.getElementById('img-container')
const $label = document.getElementById('img-format')
const destroyChildren = $element => {
while ($element.firstChild) {
const $lastChild = $element.lastChild ?? false
if ($lastChild) $element.removeChild($lastChild)
}
}
const loadImage = async url => {
const $img = document.createElement('img')
$img.src = url
return new Promise((resolve, reject) => {
$img.onload = () => resolve($img)
$img.onerror = reject
})
}
const convertSVGtoImg = async e => {
const $btn = e.target
const format = $btn.dataset.format ?? 'png'
$label.textContent = format
destroyChildren($holder)
const svgAsXML = (new XMLSerializer()).serializeToString($svg)
const svgData = `data:image/svg+xml,${encodeURIComponent(svgAsXML)}`
const img = await loadImage(svgData)
const $canvas = document.createElement('canvas')
$canvas.width = $svg.clientWidth
$canvas.height = $svg.clientHeight
$canvas.getContext('2d').drawImage(img, 0, 0, $svg.clientWidth, $svg.clientHeight)
const dataURL = await $canvas.toDataURL(`image/${format}`, 1.0)
console.log(dataURL)
const $img = document.createElement('img')
$img.src = dataURL
$holder.appendChild($img)
}
const buttons = [...document.querySelectorAll('[data-format]')]
for (const $btn of buttons) {
$btn.onclick = convertSVGtoImg
}
.wrapper {
display: flex;
flex-flow: row nowrap;
width: 100vw;
}
.images {
display: flex;
flex-flow: row nowrap;
width: 70%;
}
.image {
width: 50%;
display: flex;
flex-flow: row wrap;
justify-content: center;
}
.label {
width: 100%;
text-align: center;
}
<div class="wrapper">
<div class="item images">
<div class="image left">
<div class="label">svg</div>
<div id="svg-container">
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="200" height="200" viewBox="0 0 248 204">
<path fill="#1d9bf0" d="M221.95 51.29c.15 2.17.15 4.34.15 6.53 0 66.73-50.8 143.69-143.69 143.69v-.04c-27.44.04-54.31-7.82-77.41-22.64 3.99.48 8 .72 12.02.73 22.74.02 44.83-7.61 62.72-21.66-21.61-.41-40.56-14.5-47.18-35.07 7.57 1.46 15.37 1.16 22.8-.87-23.56-4.76-40.51-25.46-40.51-49.5v-.64c7.02 3.91 14.88 6.08 22.92 6.32C11.58 63.31 4.74 33.79 18.14 10.71c25.64 31.55 63.47 50.73 104.08 52.76-4.07-17.54 1.49-35.92 14.61-48.25 20.34-19.12 52.33-18.14 71.45 2.19 11.31-2.23 22.15-6.38 32.07-12.26-3.77 11.69-11.66 21.62-22.2 27.93 10.01-1.18 19.79-3.86 29-7.95-6.78 10.16-15.32 19.01-25.2 26.16z"/>
</svg>
</div>
</div>
<div class="image right">
<div id="img-format" class="label"></div>
<div id="img-container"></div>
</div>
</div>
<div class="item buttons">
<button id="btn-png" data-format="png">PNG</button>
<button id="btn-jpg" data-format="jpeg">JPG</button>
<button id="btn-webp" data-format="webp">WEBP</button>
</div>
</div>
Svg to png can be converted depending on conditions:
If svg is in format SVG (string) paths:
create canvas
create new Path2D() and set svg as parameter
draw path on canvas
create image and use canvas.toDataURL() as src.
example:
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
let svgText = 'M10 10 h 80 v 80 h -80 Z';
let p = new Path2D('M10 10 h 80 v 80 h -80 Z');
ctx.stroke(p);
let url = canvas.toDataURL();
const img = new Image();
img.src = url;
Note that Path2D not supported in ie and partially supported in edge. Polyfill solves that:
https://github.com/nilzona/path2d-polyfill
Create svg blob and draw on canvas using .drawImage():
make canvas element
make a svgBlob object from the svg xml
make a url object from domUrl.createObjectURL(svgBlob);
create an Image object and assign url to image src
draw image into canvas
get png data string from canvas: canvas.toDataURL();
Nice description:
https://web.archive.org/web/20200125162931/http://ramblings.mcpher.com:80/Home/excelquirks/gassnips/svgtopng
Note that in ie you will get exception on stage of canvas.toDataURL(); It is because IE has too high security restriction and treats canvas as readonly after drawing image there. All other browsers restrict only if image is cross origin.
Use canvg JavaScript library. It is separate library but has useful functions.
Like:
ctx.drawSvg(rawSvg);
var dataURL = canvas.toDataURL();
I recently discovered a couple of image tracing libraries for JavaScript that indeed are able to build an acceptable approximation to the bitmap, both size and quality. I'm developing this JavaScript library and CLI :
https://www.npmjs.com/package/svg-png-converter
Which provides unified API for all of them, supporting browser and node, non depending on DOM, and a Command line tool.
For converting logos/cartoon/like images it does excellent job. For photos / realism some tweaking is needed since the output size can grow a lot.
It has a playground although right now I'm working on a better one, easier to use, since more features has been added:
https://cancerberosgx.github.io/demos/svg-png-converter/playground/#
There are several ways to convert SVG to PNG using the Canvg library.
In my case, I needed to get the PNG blob from inline SVG.
The library documentation provides an example (see OffscreenCanvas example).
But this method does not work at the moment in Firefox. Yes, you can enable the gfx.offscreencanvas.enabled option in the settings. But will every user on the site do this? :)
However, there is another way that will work in Firefox too.
const el = document.getElementById("some-svg"); //this is our inline SVG
var canvas = document.createElement('canvas'); //create a canvas for the SVG render
canvas.width = el.clientWidth; //set canvas sizes
canvas.height = el.clientHeight;
const svg = new XMLSerializer().serializeToString(el); //convert SVG to string
//render SVG inside canvas
const ctx = canvas.getContext('2d');
const v = await Canvg.fromString(ctx, svg);
await v.render();
let canvasBlob = await new Promise(resolve => canvas.toBlob(resolve));
For the last line thanks to this answer
get data URIs from SVG:
data:image/svg+xml;base64,${btoa(new XMLSerializer().serializeToString(svgElem))}
prepare an Image
create a canvas and use toDataURL to export.
Example
<!-- test data-->
<svg width="400" height="400"><g transform="translate(23.915343915343925,-80.03971756398937)" class="glyph" stroke="#000000" fill="#a0a0a0"><path d="M74.97 108.70L74.97 108.70L100.08 110.77Q93.89 147.91 87.35 179.89L87.35 179.89L148.23 179.89L148.23 194.34Q143.76 277.91 113.84 339.81L113.84 339.81Q144.44 363.54 163.70 382.46L163.70 382.46L146.51 402.75Q128.62 384.18 101.80 361.83L101.80 361.83Q75.32 405.85 34.39 436.80L34.39 436.80L17.20 415.82Q57.43 386.93 82.20 345.66L82.20 345.66Q57.78 326.40 27.86 304.39L27.86 304.39Q44.37 257.96 56.75 203.97L56.75 203.97L19.26 203.97L19.26 179.89L61.90 179.89Q69.47 145.16 74.97 108.70ZM93.20 323.99L93.20 323.99Q118.65 272.06 123.12 203.97L123.12 203.97L82.20 203.97Q69.47 260.03 55.71 297.17L55.71 297.17Q76.01 311.61 93.20 323.99ZM160.26 285.13L160.26 260.37L239.71 260.37L239.71 216.01Q268.25 191.24 294.05 155.48L294.05 155.48L170.58 155.48L170.58 130.71L322.94 130.71L322.94 155.48Q297.49 191.93 265.50 223.92L265.50 223.92L265.50 260.37L337.38 260.37L337.38 285.13L265.50 285.13L265.50 397.59Q265.50 431.64 237.65 431.64L237.65 431.64L187.09 431.64L180.21 407.57Q202.22 407.91 227.67 407.91L227.67 407.91Q239.71 407.91 239.71 390.03L239.71 390.03L239.71 285.13L160.26 285.13Z"></path></g></svg>
<button title="download">svg2png</button>
<script>
const output = {"name": "result.png", "width": 64, "height": 64}
document.querySelector("button").onclick = () => {
const svgElem = document.querySelector("svg")
// const uriData = `data:image/svg+xml;base64,${btoa(svgElem.outerHTML)}` // it may fail.
const uriData = `data:image/svg+xml;base64,${btoa(new XMLSerializer().serializeToString(svgElem))}`
const img = new Image()
img.src = uriData
img.onload = () => {
const canvas = document.createElement("canvas");
[canvas.width, canvas.height] = [output.width, output.height]
const ctx = canvas.getContext("2d")
ctx.drawImage(img, 0, 0, output.width, output.height)
// 👇 download
const a = document.createElement("a")
const quality = 1.0 // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingQuality
a.href = canvas.toDataURL("image/png", quality)
a.download = output.name
a.append(canvas)
a.click()
a.remove()
}
}
</script>
Here are my 2 cents. Somehow Download anchor tag is not working as expected in code snippet, however it was working fine in Chrome.
Here is working jsFiddle
const waitForImage = imgElem => new Promise(resolve => imgElem.complete ? resolve() : imgElem.onload = imgElem.onerror = resolve);
const svgToImgDownload = ext => {
if (!['png', 'jpg', 'webp'].includes(ext))
return;
const _svg = document.querySelector("#svg_container").querySelector('svg');
const xmlSerializer = new XMLSerializer();
let _svgStr = xmlSerializer.serializeToString(_svg);
const img = document.createElement('img');
img.src = 'data:image/svg+xml;base64,' + window.btoa(_svgStr);
waitForImage(img)
.then(_ => {
const canvas = document.createElement('canvas');
canvas.width = _svg.clientWidth;
canvas.height = _svg.clientHeight;
canvas.getContext('2d').drawImage(img, 0, 0, _svg.clientWidth, _svg.clientHeight);
return canvas.toDataURL('image/' + (ext == 'jpg' ? 'jpeg' : ext), 1.0);
})
.then(dataURL => {
console.log(dataURL);
document.querySelector("#img_download_btn").innerHTML = `Download`;
})
.catch(console.error);
};
document.querySelector('#map2Png').addEventListener('click', _ => svgToImgDownload('png'));
document.querySelector('#map2Jpg').addEventListener('click', _ => svgToImgDownload('jpg'));
document.querySelector('#map2Webp').addEventListener('click', _ => svgToImgDownload('webp'));
<div id="svg_container" style="float: left; width: 50%">
<svg width="200" height="200" viewBox="-100 -100 200 200">
<circle cx="0" cy="20" r="70" fill="#D1495B" />
<circle cx="0" cy="-75" r="12" fill="none" stroke="#F79257" stroke-width="2" />
<rect x="-17.5" y="-65" width="35" height="20" fill="#F79257" />
</svg>
</div>
<div>
<button id="map2Png">PNG</button>
<button id="map2Jpg">JPG</button>
<button id="map2Webp">WEBP</button>
</div>
<div id="img_download_btn"></div>