Get size of SVG graphics by javascript - javascript

To add a svg graphics in html page, it is common to use object tag to wrap it like this:
<object id="svgid" data="mysvg.svg" type="image/svg+xml"
wmode="transparent" width="100" height="100">
this browser is not able to show SVG: <a linkindex="3"
href="http://getfirefox.com">http://getfirefox.com</a>
is free and does it!
If you use Internet Explorer, you can also get a plugin:
<a linkindex="4" href="http://www.adobe.com/svg/viewer/install/main.html">
http://www.adobe.com/svg/viewer/install/main.html</a>
</object>
If you do not use width and height attributes in the object tag, the svg will be displayed in full size. Normally I get svg files from Open Graphics Library for testing. Is there any way to get svg's size by using JavaScript? Or maybe I should just look at the svg xml file to find out the size from the top svg tag?

See http://dev.opera.com/articles/view/svg-evolution-not-revolution/?page=2
It is possible to get access to the SVG DOM referenced by an HTML:object as long as the SVG file is on the same domain (otherwise this would be a security vulnerability. If your object tag has id="myobj" you would do:
var obj = document.getElementById("myobj"); // reference to the object tag
var svgdoc = obj.contentDocument; // reference to the SVG document
var svgelem = svgdoc.documentElement; // reference to the SVG element
Then you can get access to the svg element's width/height by:
svgelem.getAttribute("width")
svgelem.getAttribute("height")
Note that these attributes may be things like "100px", "300", "200em", "40mm", "100%" so you need to carefully parse it.

> Is there any way to get svg's size by using JavaScript?
No and yes.
No:
JavaScript won't be able to access the SVG file contents that are sitting in the browser.
So it wouldn't be possible to have a page containing an arbitrary SVG image and then have JavaScript determine anything from the SVG file itself.
The only data JS can access it that contained within the page's DOM: the original markup plus any JS-related modifications to the DOM. You could access the object element's attributes and descendant nodes, but that won't give you visibility of anything more than what you can see if you look at the page markup yourself.
Yes:
JS (in modern browsers) can parse any XML string into a DOM and then access the content using DOM-based methods.
You could get the SVG file contents by issuing an xmlHttpRequest-style request (i.e. JS performs an HTTP GET on the SVG file's URL). JS would then have the SVG file's full XML string and you can then access anything you like.
> Or maybe I should just look at the svg xml file to find out the size from the top svg tag?
This would be more feasible by far.
The only JS-based solution would require JS to perform a GET request on the SVG file's URL (as above), which is unlikely to be feasible - each page load might result in double-downloading each SVG file, and the parsing of the SVG's XML into a DOM by JS might be too resource intensive.
Having JS retrieve the SVG's XML content and parse it into a DOM to retrieve only the image dimensions is a bit overkill. It's perfectly possible, but generally not practical.
Querying the SVG's XML content server-side, determining a suitable width and height and then dynamically adding the width and height attributes prior to returning markup to the browser would be your best bet.

ES6: Using async/await you can do this in sequence-like way
async function getImage(url) {
return new Promise((resolve, reject) => {
let img = new Image();
img.onload = () => resolve(img);
img.onerror = reject;
img.src = url;
});
}
and use above function
let img = await getImage("yourimage.svg");
let w = img.width;
let h = img.height;
You can use it as long as the SVG file is on the same domain (details here). Working example
async function getImage(url) {
return new Promise((resolve, reject) => {
let img = new Image();
img.onload = () => resolve(img);
img.onerror = reject;
img.src = url;
});
}
async function start() {
// here is example url with embeded svg but you can use any url
let svgURL = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='120' width='220'%3E%3Cellipse cx='110' cy='60' rx='100' ry='50' stroke='black' stroke-width='3' fill='red' /%3E%3C/svg%3E";
let img = await getImage(svgURL);
let w = img.width;
let h = img.height;
// print
console.log({w,h});
pic.src = svgURL;
}
start();
<img id="pic">

Is there any way to get svg's size by
using JavaScript?
YES
var filesize = function(url, requestHandler) {
var requestObj = new XMLHttpRequest();
requestObj.open('head', address, true);
requestObj.onreadystatechange = callback;
requestObj.send(null);
};
Now a handling function:
var requestHandler = function(result) {
if (this.readyState === 1) {
this.abort();
}
return this.getResponseHeader("Content-length");
};
var size = fileSize("http://whatever.org/someSVGFile.svg", requestHandler);
alert(size);
That should return the filesize of your svg or any other file without a hitch. Server configuration is the only thing that might interfere.

Related

PDF.JS PDF is not rendering properly, appears mirrored and upside down

I'm trying to get pdf.js to work in IE. I've copied the code almost exactly from the "Hello World using base64 encoded PDF" example on the pdf.js site at https://mozilla.github.io/pdf.js/examples/. The PDF is upside down and mirrored. I've looked around and a common cause of this is re-using the canvas for multiple renders, but I'm not doing that I'm just rendering once, so I really have no idea.
At the top of my html document i have:
$html .= '<canvas width="600px" height="2000px" id="the-canvas"></canvas>';
Then I've basically copied the JS exactly from the demo like so (encodedString variable is my pdf base64 string)
var pdfData = atob(encodedString);
// Loaded via <script> tag, create shortcut to access PDF.js exports.
var pdfjsLib = window['pdfjs-dist/build/pdf'];
// The workerSrc property shall be specified.
pdfjsLib.GlobalWorkerOptions.workerSrc = '//mozilla.github.io/pdf.js/build/pdf.worker.js';
// Using DocumentInitParameters object to load binary data.
var loadingTask = pdfjsLib.getDocument({data: pdfData});
loadingTask.promise.then(function(pdf) {
console.log('PDF loaded');
// Fetch the first page
var pageNumber = 1;
pdf.getPage(pageNumber).then(function(page) {
console.log('Page loaded');
var scale = 1.5;
var viewport = page.getViewport({scale: scale});
// Prepare canvas using PDF page dimensions
var canvas = document.getElementById('the-canvas');
var context = canvas.getContext('2d');
//canvas.height = viewport.height;
//canvas.width = viewport.width;
// Render PDF page into canvas context
var renderContext = {
canvasContext: context,
viewport: viewport
};
var renderTask = page.render(renderContext);
renderTask.promise.then(function () {
console.log('Page rendered');
});
});
}, function (reason) {
// PDF loading error
console.error(reason);
});
The only thing i really changed was i commented out a couple of lines setting the canvas width and height based on viewport, because it wasnt working it was always collapsed, so instead i specified width and height inline with the canvas html.
I cant seem to include images with this new stack overflow design but the pdf is rendering and appears, but its upside down and the text is mirrored, like you're looking at the text in a mirror.
If anyone could give me advice i'd appreciate it. thanks
Change {scale: scale} to scale. It wants a number not an object. Example docs are wrong.
The method signature changed in PR #10369, hence:
In version 2.0.943 and earlier it takes "regular" parameters, i.e.
formatted as getViewport(scale, rotate, dontFlip).
In version 2.1.266 and later it takes an object, i.e. formatted as
getViewport({ scale, rotation, dontFlip })
Source:
https://github.com/mozilla/pdf.js/issues/10809
PR:
https://github.com/mozilla/pdf.js/pull/10369

Get pixels' colors from HTML Canvas WITHOUT getImageData()?

Task
I am currently trying to create a web extension for Firefox.
It should be able to read images' pixels.
For that purpose, I am rendering the image on an invsible canvas, and then I want to read it.
Example Code
function getImdata(reference) {
var canvas=document.createElement("canvas");
canvas.width=reference.naturalWidth;
canvas.height=reference.naturalHeight;
var context=canvas.getContext("2d");
context.drawImage(reference,0,0);
return context.getImageData(0,0,reference.naturalWidth,reference.naturalHeight); //Here I actually get the error...
}
Problem
However, I am getting a "Security Error" if I use "getImageData()".
Question
So I need a workaround, but couldn't find anything myself.
How can I read images' pixels without getImageData() ?
EDIT
Apparently it has something to do with CORS : HTML5 Canvas getImageData and Same Origin Policy
Thanks in advance!
There is. Since you're running from an extension your extension will have privileged access to cross-origin sources but only if loaded via fetch() and XMLHttpRequest() from a content script (or background script) - excerpt from that link:
Content scripts get the same cross-domain privileges as the rest of
the extension: so if the extension has requested cross-domain access
for a domain using the permissions key in manifest.json, then its
content scripts get access that domain as well.
This is accomplished by exposing more privileged XHR and fetch
instances in the content script [...]
Please note that these calls when called from a content script will not set origin and referer headers which sometimes can cause problems if the cross-origin site expects these to be set - for those cases you will need to use the non-privileged content.XMLHttpRequest or content.fetch() which will bring you back to square one.
The permissions in the manifest file (or if set permissions dynamically) must also allow access to these cross-origin sites.
This means however that you will have to "reload" the image source separately via these calls. You can do this the following way by first obtaining the original URL to the image you want to load, say, from a content script:
// example loading all images in current tab
let images = document.querySelectorAll("img");
for(let image of images) loadAsBitmap(image.src); // some sub-call using the url
Then load that source via the content script's fetch():
fetch(src).then(resp => { // load from original source
return resp.blob(); // obtain a blob
}).then(blob => { // convert blob, see below
// ...
};
When the blob is obtained you can convert it to an Object-URL and set that as source for an image and be able to go around the cross-origin restriction we otherwise face. In the content script, next steps would be:
let url = URL.createObjectURL(blob); // attach Object-URL to blob
let img = new Image(); // create image element *
img.onload = () => { // attach handler
let c = document.createElement("canvas"); // create canvas
let ctx = c.getContext("2d"); // get context
c.width = img.width; // canvas size = image
c.height = img.height;
ctx.drawImage(img, 0, 0); // draw in image
URL.revokeObjectURL(url); // remove reference.
let imageData =
ctx.getImageData(0,0,c.width,c.height); // get image data
// .. callback to a function that handles the image data
};
img.src = url; // start loading blob

svg to png not working, suspect svg element differences

Im having trouble figuring out why two different svg's would cause my javascript to work in one instance, but not in the other. I have only swapped out the svg elements in both examples, one works, one does not. Here is the code in two jsFiddles. The working example I got from here.
Ultimately, my goal is to save the current svg element of the floor plan, and insert the converted png as an inline img inside a rich text area. As if you took a cropped screenshot of the floor plan, and pasted it into a rich text area like you see in helpdesk sites. Im just having trouble converting to png.
Working: JsFiddle
Not Working: JsFiddle
Here is the js, i cant add the svg code or it will put me over the character limit of Stackoverflow.
var svgString = new XMLSerializer().serializeToString(document.querySelector('#svg2'));
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var DOMURL = self.URL || self.webkitURL || self;
var img = new Image();
var svg = new Blob([svgString], {
type: "image/svg+xml;charset=utf-8"
});
var url = DOMURL.createObjectURL(svg);
img.onload = function() {
ctx.drawImage(img, 0, 0);
var png = canvas.toDataURL("image/png");
document.querySelector('#png-container').innerHTML = '<img src="' + png + '"/>';
DOMURL.revokeObjectURL(png);
};
img.src = url;
Edit:
I have since settled on this code omitting the png/canvas conversions, and trying to place the serialized string of the svg, directly to the rich text area, but it gets an error in IE, Non-default namespace declarations must not have an empty URI. Line: 1, column142
var svgString = new XMLSerializer().serializeToString(document.querySelector('#svg2'));
var img = new Image();
var url = "data:image/svg+xml; charset=utf8, "+encodeURIComponent(svgString);
img.onload = function() {
document.querySelector('.ze_body').innerHTML = '<img src="'+url+'"/>';
};
img.src = url;
Also i think it would help if i showed more of the structure of how this rich text area currently sits. This is not our doing, its a product we use, and im just trying to get maps to paste into the description area so it can live with the ticket. Only access we have is js triggered from rules in the form.
Screenshot of ze_body element as it stands in the DOM
It comes from your namespace declaration :
Change xmlns:NS1="" NS1:xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:NS2="" NS2:xmlns:cc="http://creativecommons.org/ns#" xmlns:NS3="" NS3:xmlns:dc="http://purl.org/dc/elements/1.1/"
to xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"> and it should work.
Also note that since you are using XMLSerializer().serializeToString(), you don't need to create a blob and an objectURL, you can only pass data:image/svg+xml; charset=utf8, " and the encodeURIComponent(svgString) as the url of your image. (fiddle).
ps: You can read about namespace declaration here.

get image size (kb) with javascript with out loading it

Is there any way to get image size (kb) before loading it in browser by java script ?
for example there is an image in this address : "mysite.com/content/sample.jpg"
now how can we understand how much is the size (kb) of this image?
You can get it like below
window.onload = function() {
var image = new Image();
image.onload = function() {
var info = "Image Info:<br>Width: "+this.width+", Height: "+this.height;
document.getElementById("imageInfo").innerHTML = info;
}
image.src = 'http://upload.wikimedia.org/wikipedia/commons/1/16/HDRI_Sample_Scene_Balls_%28JPEG-HDR%29.jpg';
}
Here is the sample - http://plnkr.co/edit/X1cqZYv93FzK4zC5kRE7?p=preview
If it's only ever a jpeg, you can just load the first chunk of the file which contains the EXIF metadata. That metadata contains width and height most of the time, although you can't be 100% certain that it's there or that it's correct. But it's probably your best bet. There are a couple of nice EXIF libraries in Javascript, and I've used this one: https://github.com/bwindels/exif-parser
It still requires you to make an HTTP request for it though, so if that's what you're worried about, there's no way to avoid it.

Which way to create a canvas pattern from a dataToURL-image string as directly as possible?

I'm using an image that I much previously had made by
var patternImageAsDataURL= canvasObject.toDataURL('image/png');
In a later stage I want to make a canvas pattern object. The following code doesn't work - I assume the image is simply not loaded when going to the last line, where it is needed in the createPattern function.
var img = document.createElement('img');
img.src = patternImageAsDataURL;
// canvasctx was created somewhere else in the program
pattern = canvasctx.createPattern(img,'repeat');
I get the error: NS_ERROR_NOT_AVAILABLE: on the last line. (And when using console.log on width and heigth of img between the two last lines, I see when it's not working the dimensions are 0.)
When later on the same operation is done with the same dataURL, it does work. Though the image (img) should always be created anew. (Only reason I can see it's because of some internal optimization in Firefox. But that's offtopic here, unless someone does know the answer.) The width and height when printing them out to the console are correct then.
While I will quite soon program some pattern handling service, that should solve this, my question is in general and for speed concerns and for simplicity. (If I use some code with like 20 to 50 objects with patterns, I would prefer a lean solution over a memory or time saving function.)
Could I somehow use the dataURL more directly (and faster) for the
createPattern function?
And:
Could I force the program to wait after the img.src = patternImageAsDataURL; command until the image is loaded, and then to go on processing the code? (Like in the synchronous mode of the XMLrequests.)
(Using the onload event of the image isn't feasible in the current program flow.)
This is running on Firefox 32, Win 7.
A faster, more direct way to create a pattern
You can use a second canvas element as the source for a pattern.
This allows you to completely skip the interim step of creating an ImageURL and Image from your source canvas so your pattern creation will be faster.
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
// Make a temporary canvas to be the template for a pattern
var pc=document.createElement('canvas');
var px=pc.getContext('2d');
pc.width=4;
pc.height=4;
px.fillStyle='palegreen';
px.fillRect(0,0,2,2);
px.fillRect(2,2,2,2);
// Use the temporary canvas as the image source for "createPattern"
var pattern=ctx.createPattern(pc,'repeat');
ctx.fillStyle=pattern;
ctx.fillRect(50,50,100,75);
ctx.strokeRect(50,50,100,75);
body{ background-color: ivory; }
#canvas{border:1px solid red;}
<h4>Using a temporary canvas as source for a Pattern.</h4>
<canvas id="canvas" width=300 height=300></canvas>
Option 1 - Canvas as image source
The obvious is of course to use the canvas itself as image source for the pattern.
createPattern() can take image, canvas, context (although not all browsers allow this) or even video as source.
CanvasPattern createPattern(CanvasImageSource image,
[TreatNullAs=EmptyString] DOMString repetition);
where CanvasImageSource is defined as:
typedef (HTMLImageElement or
HTMLVideoElement or
HTMLCanvasElement or
CanvasRenderingContext2D or
ImageBitmap) CanvasImageSource;
This is also the only way that will allow you to not use onload at some point later (provided the pattern is generated and not drawn in from an image/video source).
You cannot deal with asynchronous behavior without using callbacks (or promises), and expect the program to work properly. Period.
Option 2 - Data-URIs
If you for some reason cannot use the original canvas as source, you have to deal with the image asynchronously. Add a onload handler for it and continue from inside it:
var img = document.createElement('img');
img.onload = function() {
pattern = canvasctx.createPattern(this, 'repeat');
// continue from here..
};
img.src = patternImageAsDataURL;
Note that the process of this is relative slow due to the additional encoding/decoding process on top of the image handling itself. You can find more details about this in this answer.
Option 3 - Blob and object-URL
A Blob lets you store the data in binary form. This is preferred over storing the binary data as encoded string as with data-URIs. This will be faster to embed as well as extract compared to data-URIs.
You can use URL form with the Blob and use that as image source.
First create the Blob directly from canvas:
var patternImageAsBlob = canvas.toBlob(...); //IE: msToBlob()
This is also an asynchronous call so you need to take that into account.
For example:
var patternAsBlob;
canvas.toBlob(function(blob) {
patternAsBlob = blob;
// continue from here
}
Then when you need it as an image, generate an Object-URL for it like this:
var img = new Image(),
url = URL.createObjectURL(patternAsBlob);
img.onload = function() {
URL.revokeObjectURL(url); // clean up by removing the url object
pattern = canvasctx.createPattern(this, 'repeat');
// continue from here..
};
img.src = url;
Tips
If you have several images to load and set, it would be better to make an image loader to load in all resources to an array, when done create the patterns.
This will simplify the asynchronous chain-calling (optionally use promises, but this is not yet supported in IE without a polyfill).
You may need a polyfill for toBlob in older browser. One can be found here.
You may need to "unprefix" the createObjectURL(), here is one way:
var domURL = self.URL || self.webkitURL || self;
var url = domURL.createObjectURL( ... );

Categories

Resources