Change SVG Colour - svg

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.

Related

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.

SVG mask doesn't work if a media query is present

TL;DR: I need to mask out a portion of one rectangle in SVG, based on the size and position of another existing rectangle, which will be changing dynamically. A Chrome bug is blocking the mask + use approach I tried. How can I do a mask or inverted clip path based on an existing shape?
Full Overview:
I'm using D3.js, and I am using the brush control to add a brush to a rectangle in an embedded SVG. By default, this adds some extra elements to the SVG, including a rect with class extent that shows the size of the brushed area.
Rather than have the brush extent be rendered as a semi-transparent overlay on top of the rectangle, as in most D3 examples, I am trying to "cut out" the extent from a semi-transparent overlay, so that the brush area shows the true color below. Per this question, I am trying to do this with a mask element, with a child use element referencing the extent. With some D3 magic, I now have a structure like this:
<svg width="100" height="100">
<g class="brush-layer inverted">
<defs>
<mask id="mask835">
<rect fill="#fff" width="100%" height="100%"></rect>
<use fill="#000" xlink:href="#extent848"></use>
</mask>
</defs>
<g class="brush" style="pointer-events: none;">
<rect class="overlay" mask="url(#mask835)" width="100%" height="17"></rect>
<rect class="extent" x="30" width="52" height="17" id="extent848"></rect>
</g>
</g>
</svg>
This works great... sort of. It turns out that there appears to be a tricky Chrome bug, which I've filed here, which prevents the mask from being applied if there's a #media query in the CSS. You can see the working version here and the failing version here (fails in Chrome, works in FF).
I need this to work in Chrome, and can't drop the #media query. I also need to make the use element work, because D3 will automatically resize the extent rectangle, and that's the shape I need to mask out.
So, how can I mask out a portion of one rect, based on another rect, without using the mask + use strategy above?
One possible workaround might be to use a custom clip-path, but it's probably not going to be as elegant. Some examples of how to do do this with clip-path can be found in this question.

Avoid line between tiled SVG shapes

I am using multiple differently colored rectangles to build a SVG data visualization. This works great but sometimes background color bleeds through between the rectangles. I am browsing with Chrome but other browsers seem similarly affected.
http://jsfiddle.net/dVEPk/
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<rect x="10.5" y="10" height="100" width="100"
style="stroke:none; fill: #00cc00"/>
<rect x="110.5" y="10" height="100" width="100"
style="stroke:none; fill: #00cc00"/>
</svg>
In Chrome, if the x offset is an integer, the line is not visible.
I'm sure I can avoid lines by making rectangles a little larger than the space they have to occupy. But this seems like a hack: is there an SVG idiom or best practice to achieve perfectly tiled shapes without "grout"?
I'm also concerned by rendering performance because my visualizations can be very large (say 100MB XML .svg). I'd like to be able to give the renderer hints like "none of the shapes in this <g> are overlapping"?
This is antialiasing at work between the shape and the background. If you want to turn it off set shape-rendering="crispEdges" on the shapes. You can either set that on the rect elements or on the <svg> in which case the rect elements will inherit it.
You may be able to adjust the line's positions by adding 0.5 to the co-ordinates. See the cairo FAQ for more details on this.

Using a clipping path with a positioned object in Webkit

Consider this simple SVG file:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:x="http://www.w3.org/1999/xlink"
viewBox="0 0 353 150">
<defs>
<clipPath id="walk0"><rect width="44" height="70" /></clipPath>
<image id="img" x:href="http://phrogz.net/tmp/walking-girl2.png"
width="353" height="70" />
</defs>
<use x:href="#img" clip-path="url(#walk0)" />
<use x:href="#img" y="80" clip-path="url(#walk0)" />
</svg>
The intent is to have two copies of the spritesheet clipped to the same region, with the second copy 80 units lower down. This works as intended in Firefox (the clipping path is applied before the y offset). In Chrome and Safari, however, the second image is not shown. (The clipping path is applied using global SVG unit space, and hence shows an empty area of the image.)
1) Which one of these browsers is correct?, or
2) What is the simplest, standards-based way to achieve this goal?
I can work around the problem by using wrapping <g> elements with transforms; this works in both Firefox and Chrome. But I'm hoping that there's a simpler way to achieve the same results in a correct and cross-browser manner.
FWIW, I also tried setting clipPathUnits="objectBoundingBox" on the clipPath, but this always produced an unclipped image. This was true even when I wrapped the <image> in a <symbol> with an explicit viewBox, height and width and referenced that with the <use> instead of the <image>. Either I don't understand how objectBoundingBox is supposed to work, or support for it is currently broken. It is certainly possible that the answer is the former instead of the latter. ;)
The easiest, standards-compliant way to differentiate between these is to use the SVG test suite provided by W3.org. This suite provides tests for use structs that you can play with to determine compliance, among many others.
The problem is how your y value is being parsed, which is causing your figure to translate out of the second frame, but only in some browsers. This is the correct, cross-browser way to specify the desired translation:
<use x:href="#img" clip-path="url(#walk0)"transform="translate(0,80)"/>
I would assume the dubious parsing with respect to the current clipping pane is a regression.

How can I make text automatically scale down in an 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.

Resources