How to ensure uploaded SVG has width and height Attributes - javascript

My users can upload image files, which could be SVGs.
I need to make sure that all SVGs have a width and height attribute for something that's occurring later on.
If I have the SVG as a file (via an input type="file") how can I add a width and height to it if it is missing?

I ended up doing the following, though Im sure it could be improved/done in a much better way
let svgReader = new FileReader();
svgReader.onload = function (e) {
let svg = $(e.target.result);
for(let i =0;i<svg.length;i++) {
if (svg[i] instanceof SVGElement) {
svg = $(svg[i]);
break;
}
}
let width = svg.attr("width");
let height = svg.attr("height");
if (height == null || height == undefined) {
svg.attr("height", 300);
}
if (width == null || width == undefined) {
svg.attr("height", 300);
}
let svgFile = new XMLSerializer().serializeToString(svg[0]);
let new file = new File([svgFile], files[index].name, {type: "image/svg+xml"});
};
svgReader.readAsText(files[0]);
I had to go through the SVG to find an instance of SVG element, as some of the SVGs I tested with had additional tags or comments that messed it up otherwise. Also, the 300 value for width and height was just made up, but seems to be ok, probably could have used the view box dimensions instead, but it works for me

Related

JavaScript - Get width of element during css transition

I want to display an element's width in javascript. This part is already working, but here is my issue :
When the element's width is animated, during the animation I want to see the real element's width, and not the final element's width.
I'm working with Angular, and what I wanted to do was possible by including JQuery using the function $(el).width() but I want to remove JQuery uses.
I already tried (assuming el is a HTML element) :
el.offsetWidth
el.clientWidth
el.scrollWidth
el.getBoundingClientRect().width
Some time ago I faced the same issue, with images who are not loaded yet and had the size of 0. I fixed the problem with using the following code to get the original image size. Be sure you continue your code inside the onload function
var image = new Image();
image.onload = function() {
// set its dimension to target size
var width = image.width;
var height = image.height;
};
image.src = el.img;
It is also possible to do this with a base64 string if needed
var image = new Image();
image.onload = function() {
// set its dimension to target size
var width = image.width;
var height = image.height;
};
image.src = "data:image/jpg;base64," + img;

How to include <div> labels when converting SVG to PNG

SVG newbie here. I'm trying to do an enhancement wherein country labels (as <div>) are included during conversion of an SVG map to PNG.
The map to be converted looks like this:
This is what it looks like in the DOM. div labels to be included are highlighted.
This is what I've tried so far:
var svg = $('svg', $(svgParentId));
// append map labels // From this line...
var outerHtmlColl = $(svgParentId).find('.marker-label'); //
//
_.forEach(outerHtmlColl, function(item){ //
svg = svg.append(item.outerHTML); //
}); // ...to this line
// check whether style should be applied
if (styleSheet) {
var styles = loadStyles([styleSheet]);
var defs = $('defs', svg);
if (!defs.length) {
svg.prepend('<defs></defs>');
defs = $('defs', svg).first();
}
var style = $('style', defs);
if (!style.length) {
defs.prepend('<style type="text/css"></style>');
style = $('style', defs).first();
style.html(styles);
}
}
var serializer = new XMLSerializer();
var svgStr = serializer.serializeToString(svg[0]);
var canvas = $('#hiddenCanvas')[0];
canvas.width = width;
canvas.height = height;
canvg(canvas, svgStr,
{
ignoreDimensions: true, //does not try to resize canvas
scaleWidth: width,
scaleHeight: height,
renderCallback: function () {
var pngImg = canvas.toDataURL("image/png");
func(pngImg.substring(22));
}
}
);
Unfortunately, this doesn't work as seen below:
I'm not sure exactly what I'm missing here (or if this is possible at all...). Any inputs would be greatly appreciated.
Thank you.
Div's are not valid SVG sub-elements, so moving them into the SVG won't work.
You should move the text in the div's into the SVG by converting the div elements to <text> elements. Use the text elements' x and y attributes to position the label within the SVG.

image smaller than the original on canvas to make drawImage()

I am developing a system for image filter with html5 canvas, however, as I am at the beginning I have emerged me some doubts and mistakes.
Here's what I've developed so far:
$(document).ready(function() {
$("#uploadImagem").change(function(e) {
var _URL = window.URL || window.webkitURL,
arquivo = e.target.files[0],
tipoImagem = /image.*/,
reader,
imagem;
if(!arquivo.type.match(tipoImagem)) {
alert("Somente imagens são aceitas!");
return;
}
imagem = new Image();
imagem.onload = function() {
if(this.width > 600 || this.height > 400) {
alert("Somente imagens com largura máxima de 600px e altura máxima de 400px");
return;
} else {
$("#filtrarImagem").width(this.width).height(this.height);
}
};
imagem.src = _URL.createObjectURL(arquivo);
reader = new FileReader();
reader.onload = fileOnload;
reader.readAsDataURL(arquivo);
});
function fileOnload(e) {
var $img = $("<img>", {src: e.target.result}),
canvas = $("#filtrarImagem")[0],
context = canvas.getContext("2d");
$img.load(function() {
context.drawImage(this, 0, 0);
});
}
});
When I do imagem.onload... I wish, if the image pixels were greater than 600 and 400 he gave the alert and stop there, but even so the image appears on the canvas.
In the same imagem.onload... function, if the image pixels correspond to the canvas required (id = "filtrarImagem") is the size of the image, but the image to the canvas becomes smaller than the original uploaded, and she was to occupy all the canvas and get the original size.
How to continue?
You have two separate handlers for image loading. There is no need to set two image sources with the same URL, just reuse a single image for both checking size and setting canvas size as well as draw it into the canvas.
I would suggest the following steps (you tagged the question with jQuery but I would highly recommend working in vanilla JavaScript when you work with non-DOM oriented elements such as canvas).
Example
if (typeof window.URL === "undefined") window.URL = window.webkitURL;
$("input")[0].onchange = function(e) {
var arquivo = e.target.files[0],
imagem = new Image();
if (!arquivo.type.match(/image.*/)) {
alert("Somente imagens são aceitas!");
return;
}
// STEP 1: load as image
imagem.onload = doSetup;
imagem.src = URL.createObjectURL(arquivo)
};
// STEP 2: now that we have the image loaded we can check size and setup canvas
function doSetup() {
URL.revokeObjectURL(this.src); // clean up memory for object-URL
if (this.width > 600 || this.height > 400) {
alert("Too big!");
return; // exit!
}
// image OK, setup canvas
var c = $("canvas")[0]; // work with the element itself, not the jQuery object
c.width = this.width; // important: use canvas.width, not style or css
c.height = this.height;
// draw in image
var ctx = c.getContext("2d");
ctx.drawImage(this, 0, 0);
// NEXT: ... from here, invoke filter etc.
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="file"><br><canvas></canvas>
The jQuery width() and height() functions use CSS to change the size of the canvas which does not do what you would expect it to do. Change the width and height attributes of the canvas element directly.
The reason why your check for the size does not prevent the image from being drawn, is because your check is in the handler for imagem.onload in $("#uploadImagem").change while the drawing happens in $img.onload assigned from reader.onload. These two events are processed independently. One of them returning prematurely does not prevent the other from being executed.

Fabric.js: How to observe object:scaling event and update properties dynamically?

I'm trying to get the shape properties to dynamically update when the shape is being scaled.
Here's a fiddle: http://jsfiddle.net/anirudhrantsandraves/DAjrp/
The console log commands:
console.log('Width = '+scaledObject.currentWidth);
console.log('Height = '+scaledObject.currentHeight);
are supposed to dynamically display the height and width of the shape as it is being scaled.
The properties remain the same in the console when the shape gets scaled. However, when I select the object again and scale it, the previous values of the properties are displayed.
Is there a way to make this dynamic?
getWidth() and getHeight() are used to get the current width and height in Fabric.js.
So in "object:scaling" event:
canvas.on('object:scaling', onObjectScaled);
function onObjectScaled(e)
{
var scaledObject = e.target;
console.log('Width = '+scaledObject.getWidth());
console.log('Height = '+scaledObject.getHeight());
}
will serve your purpose.
Updated fiddle — http://jsfiddle.net/DAjrp/2/
Just in case, if you wanted to get the scaled height
canvas.on("object:scaling", (e) => {
canvas.getActiveObjects().forEach((o) => {
// if it's scaled then just multiple the height by scaler
const objHeight = o.scaleY ? o.height * o.scaleY : o.height;
// ...
});
});

Google Maps API + Wax.g + IE = Tiny Tiles

I'm using wax.g to display custom tiles on a Google Map. This works fine in every browser I've tested, except IE. In other browsers, the tiles are rendered correctly, but in IE, the tiles a rendering as a smaller version of their true selves. The issue is best explained in the following screenshots:
What they should look like: http://cl.ly/image/2E0U2b0c0f0W
What they look like in IE: http://cl.ly/image/2F3B353q0G0c
This issue doesn't happen all the time, and if I pan or zoom, sometimes I get the correctly sized tiles, and sometimes I don't. Not sure if this is a Wax issue or an issue with how the Google Maps API renders custom overlayMapTypes. Has anyone else experienced this issue? Any insight is much appreciated...
(cross-posted from MapBox GitHub issue - no answers there yet)
So the problem is that my images were inheriting a style height and width of auto, and Wax.g's getTile method method creates an Image using new Image(256, 256), which writes to height and width attributes of the img itself (img attributes, not inline style). The inline styles took priority over the attributes, so the img was being sized using auto. I fixed this by ensuring the img had a height and width of 256 using inline styling.
Wax.g code:
// Get a tile element from a coordinate, zoom level, and an ownerDocument.
wax.g.connector.prototype.getTile = function(coord, zoom, ownerDocument) {
var key = zoom + '/' + coord.x + '/' + coord.y;
if (!this.cache[key]) {
var img = this.cache[key] = new Image(256, 256);
this.cache[key].src = this.getTileUrl(coord, zoom);
this.cache[key].setAttribute('gTileKey', key);
this.cache[key].onerror = function() { img.style.display = 'none'; };
}
return this.cache[key];
};
My modified code:
// Get a tile element from a coordinate, zoom level, and an ownerDocument.
wax.g.connector.prototype.getTile = function(coord, zoom, ownerDocument) {
var key = zoom + '/' + coord.x + '/' + coord.y;
if (!this.cache[key]) {
var img = this.cache[key] = new Image(256, 256);
img.style.height = '256px'; // added this line
img.style.width = '256px'; // added this line
this.cache[key].src = this.getTileUrl(coord, zoom);
this.cache[key].setAttribute('gTileKey', key);
this.cache[key].onerror = function() { img.style.display = 'none'; };
}
return this.cache[key];
};

Categories

Resources