How can I make text automatically scale down in an SVG? - svg

I've got an SVG element that I've created with Inkscape. I then take that SVG and put in as part of an XSL-FO stylesheet that is used to transform XML data and then passed through the IBEX renderer to create a pdf (which is usually then printed). As an example, I have elements in the svg/stylesheet that look like this (extra noise due to the Inkscape export):
<text x="114" x="278.36218" id="id1" xml:space="preserve" style="big-long-style-string-from-inkscape">
<tspan x="114" y="278.36218" id="id2" style="style-string">
<xsl:value-of select="Alias1"/>
</tspan>
</text>
My problem lies in the fact that I don't know how big this text area is going to be. For this particular one, I've got an image to the right of the text in the SVG. However, if this string is the maximum allowed number of W's, it's way too long and goes over the image. What I'd like (in a perfect world) is a way to tell it how many pixels wide I want the text block to be and then have it automatically make the text smaller until it fits in that area. If I can't do that, truncating the text would also work. As a last ditch resort, I'm going to use a fixed width font and do the string truncation myself in the XML generation, although that creates something both less usable and less pretty.
I've poked around the SVG documentation and looked into flowRegions a bit as well as paths, but neither seem to be be quite what I want (maybe they are though). If it helps, here's that style string that Inkscape generates:
"font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
Thanks in advance for the help.

You have text of arbitrary line length (in terms of characters) and you want to scale it to fit inside a fixed amount of space? The only way I can think of to rescale text to a fixed size is to place it inside an svg element and then scale the SVG to that size:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Resizing Text</title>
<defs>
<svg id="text-1" viewBox="0 0 350 20">
<text id="text-2" x="0" y="0" fill="#000" alignment-baseline="before-edge">It's the end of the world as we know it, and I feel fine!</text>
</svg>
</defs>
<rect x="500" y="100" width="200" height="40" fill="#eee" />
<use x="510" y="110" width="180" height="20" xlink:href="#text-1" />
</svg>
However, as seen above, the viewBox on the encapsulating svg element needs to be set to the width of the text, which you presumably don't know.
If you're referencing this SVG inside a user agent with scripting available (e.g. a web browser) then you could easily write a script to capture the width of the text or tspan element and set the viewBox accordingly.

Related

Inline SVG pattern repeating horizontally and scaling vertically?

I'm looking for a way to use an inline SVG pattern that scales vertically and only repeats horizontally. Does anyone know if this is possible and if so how?
I know I can makes this just using a SVG as a background-image, but I want to be able to use this SVG in a javascript/component-based workflow so inline is the best fit for that.
Here is link with my work-in-progress: https://codepen.io/devotee/pen/GRJJpKL
And some code:
<div class="divider">
<svg width="100%" height="40px">
<defs>
<pattern id="pattern" x="0" y="0" width="60" height="6" patternUnits="userSpaceOnUse">
<path fill="none" stroke="#F5A861" d="M60 5C45 5 45 1 30 1S15 5 0 5"/>
</pattern>
</defs>
<rect x="10" y="6" width="100%" height="12" fill="url(#pattern)" />
</svg>
As you can see in the link, this repeats in both directions, so setting a bigger height value does not accomplish what I want. I would like this pattern to always fill the containers height (or simply be set to a value with CSS) but repeat horizontally.
Here are some images to illustrate what I mean:
Top is wanted behaviour, bottom is unwanted behaviour:
The background scales vertically and does not repeat. It takes as much space vertically as it can (fills parent height or whatever height value it has specified)
Top is wanted behaviour, bottom is unwanted behaviour:
It does not stretch the SVG horizontally but merely repeats it.
Any ideas or input on how to achieve this?

Inkscape - quality loss when using rect with image as pattern compared to just using <image>

So basically I use a png image in two different versions. First by just opening it in Inkscape and storing it as svg, for the second by clicking "convert to pattern" and then storing as svg. The first looks like this in the file:
<image
width="1280"
height="720"
preserveAspectRatio="none"
xlink:href="data:image/png;base64,iVBOR..."
id="image10"
x="0"
y="0" />
and the second like this:
<defs
id="defs6">
<pattern
patternUnits="userSpaceOnUse"
width="1280"
height="720"
id="pattern4157">
<image
y="0"
x="0"
id="image10"
xlink:href="data:image/png;base64,iVBOR..."
preserveAspectRatio="none"
height="720"
width="1280" />
</pattern>
</defs>
<sodipodi .../>
<rect
style="stroke:none;fill:url(#pattern4157)"
width="1280"
height="720"
x="0"
y="0"
id="rect4160" />
Now, if I export both svg files as png using inkscape, the first one is identical with the source png, the second one becomes a tiny bit blurry, the color also seems ever so slightly darker and also the filesize shrinks from 1.7MB to 1.3MB. Why does this happen? How can I avoid this?
It might have to do with the size of the image? Did you check that both files after the second conversion were the same pixel size?
That being said, it seems a bit strange to me that you would use Inkscape, which is designed for vectorial graphics, to mess around with converting pixel-based images into a format that is made for vectorial graphics again, and back.

What's the difference in accessibility between having SVG inline or as an image?

I am developing a web page trying to focus on accessibility, and created different graphics in SVG to go in it. After reading different sites online (1, 2, and 3), I included the <title> and <desc> tags, and added the attributes role and aria-labelledby to make the SVGs more accessible.
Some of those sources, seem to claim (directly or indirectly) that using SVG inline is better for accessibility; so I ran a few tests with NVDA to see the differences, but I fail to see any at first sight.
For example, using a simple SVG:
<svg width="100" height="100" viewBox="0 0 100 100" role="img" aria-labelledby="title desc">
<title id="title">Abstract Forms</title>
<desc id="desc">Red square containing a white circle containing a blue triangle pointing up.</desc>
<g stroke="none" stroke-width="0">
<rect x="0" y="0" width="100" height="100" fill="red" />
<circle cx="50" cy="50" r="40" fill="white" />
<path d="M 50,20 80,70 20,70 Z" fill="blue" />
</g>
</svg>
If I add it to the page like that, NVDA reads "Graphic. Abstract Forms. Red square containing a white circle containing a blue triangle pointing up."
And if I save it into a myImg.svg file, and add it to the page like this:
<img src="myImg.svg" alt="Red square containing a white circle containing a blue triangle pointing up" title="Abstract Forms" />
NVDA then reads "Graphic. Red square containing a white circle containing a blue triangle pointing up." (same thing as before, just not reading the title).
This may be an NVDA thing, and other screen readers may do it differently, but there doesn't seem to be any considerable difference between the two. At least not to claim that inlining the SVG is better for accessibility.
Then I thought it could be related to reading additional information; for example, if there was some text within the graphic. So I added a <text x="50" y="50" fill="black">Hello World</text> at the end of the SVG... but NVDA read the same thing as before; not even selecting the text it will read it (again I don't know if this is an NVDA thing and if other screen readers do it differently).
So my questions are: what are the differences between having SVG inline or as an image? And what are the benefits (for accessibility) of having the SVG inline?
You probably already self-answered your question.
Inline-svg is interpreted as part of the html webpage. So your svg title and description are interpreted as well and read by the screen reader.
When using an ‘img‘ tag to include the svg, the file is handled like an external file (like a jpg) and so only the ‘alt‘ attribute of the img tag (= the image description) is interpreted/read by the screen reader.
I have currently no source and can't test it a the moment, but I think there are also differences for links within the svg code: Links within inline svg are read by the screenreader, links within external svg files not.

How can I have a cumulative translate within an SVG patternTransform?

Consider the following SVG:
<?xml version="1.0" standalone="no"?>
<svg width="600" height="600" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<pattern id="Pattern0" patternTransform="skewX(5)" x="0" y="0" width="0.1" height="0.042">
<line x1="0" x2="1200" y1="0" y2="0" stroke-dasharray="25,25" style="stroke:#000000;stroke-width:1" />
</pattern>
</defs>
<rect fill="url(#Pattern0)" width="600" height="600" style="stroke:#0000FF;stroke-width:1" />
</svg>
Here's the fiddle to see it in action.
I'm trying to stagger the dashed lines in this pattern by some offset. The example above is a horizontal line, but the real lines may be rotated to any angle. The offset should be defined in the same units as the dasharray.
Adding a patternTransform="translate(x)" to the pattern doesn't work -- it just offsets the position of the entire repeated pattern instead of translating each repeated line in a cumulative fashion. Same goes for adding transform="translate(x)" to the line.
Adding a patternTransform="skewX(x)" works for horizontal lines and patternTransform="skewY(x)" works for vertical ones, but it doesn't work right at all for lines at other angles. Also, skewX/skewY require an angle as the argument, and I really need to move the lines based on the same units as the dashArray.
EDIT: Without boring anyone with the gory details, my ultimate goal is to translate AutoCAD hatch patterns into SVG files that I can use as background images in HTML. To see a little about how these patterns work, please see some of the examples here:
AutoCAD 2010 User Documentation (Note the three links on the left -- overview, dashed lines, and multiple lines).
I would love to use an SVG pattern to do this, but it's looking more and more like I'll have to return to previous attempts of using a loop to create individual lines. I was trying to avoid this because frankly I've forgotten way too much high school trig and getting enough lines (at an arbitrary angles) to fill the canvas is eluding me.
There isn't a short-cut way to create a staggered pattern. You'll need to draw out a set of staggered lines yourself, up to the point where you can get the effect you want with a repeating (possibly skewed or rotated) rectangular grid.
To get the effect of two dashed lines, with the dashes offset to each other, your repeating tile needs to have two lines.
<?xml version="1.0" standalone="no"?>
<svg width="600" height="600" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<pattern id="Pattern0" patternUnits="userSpaceOnUse"
x="0" y="0" width="100%" height="8.4%"
style="stroke:#000000;stroke-width:1;
stroke-dasharray:25,25; " >
<line x1="0" x2="100%" y1="1%" y2="1%" />
<line x1="0" x2="100%" y1="5.2%" y2="5.2%"
style="stroke-dashoffset:25" />
</pattern>
</defs>
<rect fill="url(#Pattern0)" width="600" height="600"
style="stroke:#0000FF;stroke-width:1" />
</svg>
Explanation of the changes:
I've changed to userSpaceOnUse units to define the pattern tile. This ensures that percentages defined when drawing the pattern are interpretted the same way as percentages drawn when defining the size of the tile. (User space is default for patternContentUnits.) The rectangle in your sample was the same size as the user space, so it wasn't making a difference, but it does if you change the size of the rectangle.
If you really wanted the spacing of the lines to be proportional to the size of the rectangle being filled, this is a problem. You could change both patternUnits and patternContentUnits to objectBoundingBox (the default for patternUnits), but then you have to define all your stroke properties relative to the rectangle size as well.
I've doubled the height of the tile, so that it is equivalent to two of your original tiles. One tile now includes two horizontal lines.
I've offset the first line slightly so it doesn't get cut off by the edge of the tile (you were only getting a half-pixel stroke width displayed, and the difference becomes obvious if the second line isn't similarly clipped).
I've added the second line, with the dash pattern offset, at a vertical position that is half the tile height below the first line (1% + 8.4%/2 = 5.2%).
Like #Robert Longson, I'm not 100% certain I understand what you're asking, so if this isn't the effect you want please post a drawing of what you're trying to achieve.

Change SVG Colour

Can you change the color of a shape inside an SVG? Currently I'm using a PNG that I have to manually create in Photoshop for each different menu and I'm wondering if I can make the whole process dynamic.
Can't you just use style="background-color: #------;" (or maybe color:)?
EDIT: My mistake, the one you need is fill, so style="fill:#------;", and it should work with any shape.
You could use a hue-rotate filter, or you could fix the colors as suggested above. Probably more compatible to change the colors to what you need, but in any case, here's an example of the filter variant:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<filter id="h200" x="0" y="0" width="1" height="1">
<feColorMatrix type="hueRotate" values="200"/>
</filter>
<image xlink:href="http://imgur.com/D9aFo.png" width="207" height="46" filter="url(#h200)"/>
</svg>
You can see it live here if you use a browser that supports svg filters, e.g Opera or Firefox.
#Brendan_Long is correct, all you need is the style tag on a path object. See a demo here: http://kemputing.com/demo/shapeDemoColour.svg
Code like so:
<path
style="fill:#000080;"
d="M 125.0,478.0 172.0,305.0 337.0,266.0 508.0,438.0 482.0,586.0 287.0,659.0 z"
id="path2985"/>
Make a path matching your image then programmatically change the colour attached to it. You might want to use a tool like inkscape to lend a hand.

Resources