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

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.

Related

Styling SVG elements with stroke and globally-positioned pattern

Goal
I am trying to replicate this effect algorithmically:
This is for a project I am working on where I need to make stylised visualisations of different kinds of pizza. The image shows a Margherita (tomato base, mozzarella and basil on top).
The rules I need to adhere to:
each ‘layer’ consists of uniformly positioned objects with a stroke and filled with a pattern (that shares coordinates)
the layers need to cover lower layers
Means
To achieve the uniform distribution, I use Poisson-Disc sampling.
I chose SVG because I need the result to be visible in the browser and generate this server-side.
For efficiency and simplicity – as the viewing size will be smaller – I decided to reference one object with <use> elements and vary it only with rotations, as opposed to the sample.
Attempts
Every approach I tried reached a dead end:
Creating a <clipPath> filled with <use> elements for clipping the background.
This does not allow me to add a stroke around the clipped area, which I need. A workaround would be to use a feMorphology filter, but that seems like it’s going to be needlessly costly on the client. A second workaround seemed to be:
Grouping the elements and using that group twice: once in a <clipPath> for clipping the pattern background, once directly on the canvas with an added stroke.
This does not work as <g> elements are unsupported in web browsers due to completely arbitrary reasons (it does work in Inkscape, however, which I used for the proof-of-concept). A workaround would be to use two copies of all the <use> elements, but that would essentially double the file size.
Grouping the elements and applying a fill with SVG patterns.
This does not work as since we create the distribution using <use> elements, the pattern looks identical in every instance. Moreover, I cannot rotate the objects, as the pattern would get rotated too. A workaround would be not to use <use>, but that would create the same problem as in point 2.
Those approaches won't work because patterns are affected by any transforms applied to the same shape.
In the solution below, we create a whole layer of an ingredient (id="basil-layer"). Then use that layer to first draw the ingredient outlines (strokes). Then afterwards we use a mask, created from that same layer, to draw hatching on top of the outlines.
You'll need to duplicate this process for each of the ingredients.
More documentation of what's happening inside the code.
<svg width="600" height="400">
<defs>
<pattern id="diagonalHatch" patternUnits="userSpaceOnUse" viewBox="0 0 4 4" width="16" height="16">
<rect fill="black" width="4" height="4"/>
<path d="M-1,1 l2,-2 M0,4 l4,-4 M3,5 l2,-2"
style="stroke:green; stroke-width:0.5" />
</pattern>
<!-- Definition for a leaf of basil -->
<ellipse id="basil" cx="0" cy="0" rx="60" ry="30"/>
<!-- A layer of N pieces of basil -->
<g id="basil-layer">
<use xlink:href="#basil" transform="translate(300,200)"/>
<use xlink:href="#basil" transform="translate(400,150) rotate(45)"/>
<use xlink:href="#basil" transform="translate(450,200) rotate(110)"/>
</g>
<!-- A mask that consists of all the pieces of basil -->
<!-- The fill is white to keep the *insides* of the basil shape.
And we stroke with black so that this mask doesn't hide any of the
green stroke outline of the leaf, when use this mask to lay down
the hatch pattern on top of the drawn basil leaves. -->
<mask id="basil-layer-mask">
<use xlink:href="#basil-layer" fill="white" stroke="black" stroke-width="2"/>
</mask>
</defs>
<!-- Fill SVG with a black background -->
<rect width="100%" height="100%" fill="black"/>
<!-- Draw all the basil pieces with a black fill and a green outline -->
<use xlink:href="#basil-layer" fill="black" stroke="green" stroke-width="2"/>
<!-- Finally draw the basil layer hatching.
This is a whole-SVG sized rectangle of hatching masked by the basil layer mask -->
<rect width="100%" height="100%" fill="url(#diagonalHatch)" mask="url(#basil-layer-mask)"/>
</svg>

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?

Changing lower letter to upper letter in SVG

Svg File
I am working on a graphics application where I can only use Arc, Line, cubic and Quadratic bézier. I have an SVG file, which is a bit long but I just took first few path elements to build to see the output. I wrote a small application which replaces lower letters to upper letters and produced a simple SVG file which consists upper causes and consists only four SVG commands. I have created two paths with original for and the second path with a modified version to confirm my output. Everything seems work fine excluding lower letter ‘m’. The result is not correct. I am not sure way. Any help should be very much appreciated. What mistake I am making? To see the converted version you have to uncomment the send path
There are several things wrong with your SVG.
Your path consists of eleven subpaths. Each of the subpaths form a very thin horizontal rectangle. For example, look at the first subpath:
M 21.52,184.48, H 91.11, v-.07, H21.54, l 0,.07 Z
That's a rectangle that is 91px wide and 0.07 pixels high. A rectangle that is 7/100 of a pixel high is going to be very faint at best. It is visible in Firefox, but Chrome seems to be culling it completely for some reason.
Your fill is invalid.
fill="New_Gradient_35"
should be:
fill="url(#New_Gradient_35)"
Your path definition is invalid. There should not be commas before a path command letter.
M21.52,184.48,H91.11,v-.07,H21.54,l0,.07Z,
should be
M21.52,184.48H91.11v-.07H21.54l0,.07Z
Chrome is a little forgiving. It will allow commas after a coordinate value, but not after a path command. So it doesn't mind:
M21.52,184.48,H91.11,v-.07,H21.54,l0,.07Z
but it doesn't like the comma between the Z and the M:
M21.52,184.48,H91.11,v-.07,H21.54,l0,.07Z,
M ...etc...
Firefox doesn't accept either comma.
You are specifying a stroke colour, but your stroke-width is "0", so your stroke will not be drawn. But perhaps that was intentional.
If you fix these issues, you get a more successful result. I'm not sure if it was what you are expecting though. It doesn't look much like a letter "m" to me.
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 498.9 498.9" width="100%" height="100%">
<linearGradient id="New_Gradient_35" x1="369.44" y1="1293.18" x2="137.44" y2="718.97" gradientTransform="translate(15.85 -609.22)" gradientUnits="userSpaceOnUse">
<stop offset="0.07"/>
<stop offset="0.93" stop-color="#454d51"/>
</linearGradient>
<title>Test One</title>
<g id="Case">
<path fill="url(#New_Gradient_35)" stroke-width="0" stroke="red" d="M21.52,184.48H91.11v-.07H21.54l0,.07Z
M469,147.4q1.62,0,3.21,.12l-406.4,0c.67,-.05,1.35,-.09,2,-.12l401.16,0Z
m8.55,.76,.22,0H60.82l.56,-.1h6l-5.47,.06Z
m1,.2,.7,.15,-419.89,0,.4,-.09,418.79,0Z
m2.21,.5,.31,.08H185.41v-.07Z
m1.88,.5,.37,.11H55.54l.23,-.07,426.9,0Z
m.74,.22,.16,0,-428.69,0,.43,-.13,428.11,0Z
m.85,.27,.47,.15H227.91l-63.75,0v0l58.44,-.08Z
m.8,.27,.23,.08,-432.07,0,.35,-.12,431.49,0Z
m.55,.19,.33,.12,-433.37,0,.39,-.14Z
m.83,.31,.2,.08H51.93l.2,-.08Z" />
</g>
</svg>

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 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