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