Text paragraph with fixed ratio within svg - javascript

I generate dynamic SVG graphics on the fly using JavaScript. For this purpose a paragraph of text should be added into a box with a fixed aspect ratio inside the svg image. The text length may differ between short and also very long text length. As the actual font size is not important for my purpose I use the viewBox attribute to show the whole paragraph within the box.
As far as I researched and tested until now, svg does not provide any automatic line breaking functionality, therefore I might use a standard HTML div within a foreignObject to make use of HTML line breaking.
Are there any possibilities to get a div with fixed aspect ratio based on its content length?
I already managed to get such a div by an ittertive decreasing of width until the ratio more or less fits the purpose. But this solution is rather imprecise and needs to add the div to the DOM before actually inserting it into the svg. Are there any CSS solutions?

As unfortunately nobody could help to solve this problem, I implemented the following (more or less working) solution:
for(var i = 0; i < 200; i++){
if($('#wrapper').width()/$('#wrapper').height() <= 5){
console.log($('#wrapper').width()/$('#wrapper').height())
break;
}
$('#wrapper').width($('#wrapper').width()*0.8);
}
for(var y = 0; y < 200; y++){
if($('#wrapper').width()/$('#wrapper').height() >= 4.9){
break;
}
$('#wrapper').width($('#wrapper').width()*1.02);
}
This approach tries to itteratively converge the aspect ratio towards an approximately correct ratio.
This is by far not an optimal solution, but at least a working one.

Related

retrieving the aspect ratio of an svg <image>

TLDR;
given this svg element:
<image width="30" height="48" x="3.75" y="6" href="http://some/image.jpg">
How can I retrieve the image's actual height and width (seeing as it is defined in part by the image's aspect ratio).
I have a d3js script that draws a bunch of <rect>s and a bunch of <image>s.
Now stuff is laid out so that the images fall inside the rects, as though the rects were borders. There is other stuff inside the rects too.
Now each of these images has it's own unique and special aspect ratio and this bothers me because it means each of the rects then has a different amount of blank space. This is untidy.
To avoid this I want to load the images then get the actual image dimensions and then adjust the positions and sizes of the surrounding goodies. The crux of the matter is getting the actual image sizes. How can I do this?
I've googled around a bit and spent some quality time with the debugging console to no avail. Just nothing comes up. I'll keep hunting but an answer would be really nice.
First, set the width attribute only, keep height unspecified.
Then, call the getBBox for the image element.
Note that image box is available after it's properly rendered by the SVG
const image = parent.append('image').attr('xlink:href', url).attr('width', 100);
setTimeout(() => {
const box = image.node().getBBox();
const ratio = box.width / box.height;
}, 0);
This is the best I can come up with. I would be surprised and horrified if there isn't an easier way. Anyway:
add a normal html <img> element with a suitable src.
use js to fetch the image height and width
remove the extra html
Ugly but it works...
Here's some code:
var oImage = document.createElement("img");
oImage.setAttribute("src",sUrl);
document.body.appendChild(oImage);
var iWidth = oImage.width;
var iHeight = oImage.height;
oImage.remove();

Is it possible to more accurately measure SVG text height?

I'm trying to measure the exact height used to render a given string with a given font with an SVG text tag.
I've tried using getBBox and getExtentOfChar, but the height returned by both of these includes some extra space above (and sometimes below) the actual text rendered.
http://upload.wikimedia.org/wikipedia/commons/3/39/Typography_Line_Terms.svg
Using the terms in this image, I'm trying to get the either the cap height + descender height of the text being rendered. Or, if that's not possible, just the cap height. Is there a good way to calculate these values?
Here's a quick codepen showing the extra space I'm talking about:
http://codepen.io/pcorey/pen/amkGl
HTML:
<div>
<svg><text>Hello</text></svg>
<svg><text>Age</text></svg>
</div>
JS:
$(function() {
$('svg').each(function() {
var svg = $(this);
var text = svg.find('text');
var bbox = text.get(0).getBBox();
svg.get(0).setAttribute('viewBox',
[bbox.x,
bbox.y,
bbox.width,
bbox.height].join(' '));
});
});
I understand that this is a fairly font-specific thing, so this might be totally impossible...
No. All the SVG DOM methods (getBBox(), getExtentOfChar()) are defined to return the full glyph cell height. That extra space above the cap height is allowance for taller glyphs - such as accented capitals. I think this is true for HTML DOM methods as well.
There are, however, JS libraries around which may be of use. For example:
https://github.com/Pomax/fontmetrics.js
I have not used this library myself, so I can't tell you how reliable or accurate it is.

Issue with overlapping horizontal rectangles

So I'm trying to solve a graphics problem. Basically there's a container, let's say its 600px wide. If there is only one rectangle in that container(with no overlapping rectangles) it takes up the width of it. However, the issue is when these rectangles overlap, the width has to shrink accordingly. We are given the top left and bottom left y-coordinates of this rectangle. (such as it starts 60px down and ends 120 px down the big container)
So I wrote an overlap algorithm that checks if an overlap exists and counts the number of rectangles that the rectangle overlaps with (including itself). Then I divide the container width by the maximum number of elements overlapped to get the width of the smaller rectangles.
for (i = 0; i < events.length; i++) {
var event = events[i];
var numCollisions = 0;
for (j = 0; j < events.length; j++) {
var eventCmp = events[j];
if (event.start <= eventCmp.start && event.end > eventCmp.start ||
event.start < eventCmp.end && event.end >= eventCmp.end) {
numCollisions++;
}
}
However, I've noticed a huge problem with this. If you look at this picture below the rightmost rectangle has two overlapping rectangles. By my algorithm you would get the container width/3 (including the rectangle itself) which is incorrect. The actual answer is the container width/2.
So the issue is (after this long winded explanation) that I need to find if two rectangles are horizontally aligned. I've been busting my head on this for hours. Any hints on how I can do this?
Well, the easiest answer is divide by 2 IF you have a collision (not caring how many collisions you actually have). If you need something more sophisticated, can you show a more sophisticated case?
a less than optimal, but easy to implement algo would be:
foreach y coord in the main container
rects = find all rects which contain this y coord
foreach rects as rect
rect.maxHorizNeighbors = max(rects.length, rect.maxHorizNeighbors)
theres still some other issues you'll need to address, but this should get you started on your homework assignment. in particular, theres a case where you could possibly end up with horizontal gaps. ill let you find that on your own.

box to keep proportion when scaling with javascript

I have made a script: http://jsfiddle.net/radar24/XZgh4/ which scales the given dimension into the outer div. everything seems fine, until I enter a dimension such as 200 x 99. then the box grows outside.
I really cannot find the cause of this, can anyone help?
The problem is that you're not restricting your proportions along both axes. Your box has a height:width proportion of 5:3. If you don't restrict along both axes, you can have bleeding outside of the boxes. An example might show this best.
Take the case of the height being the bigger of the two dimensions. Your code is only restricting it along the 500px axis. Consequently, if we throw a box in there with 5: >3 proportions, you get a creeping edge.
For instance, put "3" and "5" in your boxes. Fits perfectly. Now make it 3.1 and 5. Ruh roh.
You'll need to add another if statement in each section that THEN determines if the dimension ratio goes outside this boundary. In the above case, you'll need to make it so that the height of the 5:3.1 is not 500px, but rather, the height (less than 500px) that would make 3.1 to be equal to 300px. That would be 483px.
Does that makes sense?
If not, I'll try to rephrase again:
Put another set of if statements in the two if statements you already have. These check if, upon setting the LARGER dimension, it makes the SMALLER dimension go outside the bounds of the box in that direction.
in pseudocode
if (height > width)
calculate the height
calculate the width
if (width > div.width)
width = div.width
height = div.width * aspect;
Just ask me if this isn't clear enough!
Edit: Here's a JSFiddle that gets it right. You'll need to add further code if you want a white border along each edge.
Edit2: Here's the white border come back!
Edit3: You can also try prettying it up and using just aspects to do this. I did the first one for you. Three to go!
It's a pretty small mistake. You forgot to convert the width and height to integers before comparing them. So you would need to change if (width >= height) to if (parseInt(width) >= parseInt(height)).
jQuery .val() always return a string you should parse it into integer
changed jsfiddle
....
height = parseInt($('#height').val()) || 0;// making 0 as default value.
width = parseInt($('#width').val()) || 0;
....

Convert HTML element non-px dimensions to px in (IE issue)

I'm trying to get border width of a particular element.
Getting border width style setting is pretty easy by simply reading if from current calculated style of an element:
var styles = (
document.defaultView && document.defaultView.getComputedStyle ?
document.defaultView.getComputedStyle(de, null) :
de.currentStyle
);
Reading a particular border value is then rather simple by:
var top = styles.borderTopWidth;
var value = parseFloat(top);
This is all fine and dandy (as long as you don't use IE) and I can get top border width in the value variable. But this number relates to pixels only when border width was set in pixels. If it wasn't (was em for instance) than value has the number of that particular dimension.
I have to get an answer to any of these two questions:
How do I always get border width in pixels?
How do I calculate different units into pixels?
Example
I've prepared a jsFiddle example where you can see various dimensions reported by DOM and jQuery. Run it in different browsers and you'll see the difference in IE. All dimansions in Crome are in integer values while Firefox calculates margin and padding in floats while border in integers.
BTW: Margin, border and padding are all set to 2mm.
Most libraries solve this problem for you, as does YUI3 for example.
If you don't want to use those libraries, then at least you can peak at how they do it ;)
http://developer.yahoo.com/yui/3/api/dom-style-ie.js.html
Awnser contained therein.
You can generally get computed pixel sizes using element.offsetWidth and element.offsetHeight. This is somewhat sensitive if you want to support a range of browsers. In that case, use a library. For example, using jQuery you can get guaranteed pixel dimensions with something like this: jQuery("#theID").width().

Categories

Resources