how to reuse a svg element and resize - svg

I have a rectangle
<rect id="greenRect" fill="lightgreen" height="20" width="20"/>
Now I want to reuse the rect, but make it bigger
<use href="#greenRect" y="150" height="50" width="50"/>
The size (height/width) don't seem to get overwritten from the original <rect> element.
How would I achieve such a thing?

Solution 1: Wrap the rect in a <symbol> with a viewBox attribute.
<symbol id="greenRect" viewBox="0 0 20 20">
<rect fill="lightgreen" height="20" width="20"/>
</symbol>
<!-- symbols are not rendered, so if you want to see your original,
you have to also use it -->
<use href="#greenRect" height="20" width="20"/>
<use href="#greenRect" y="150" height="50" width="50"/>
Solution 2: scale and translate your rectangle
<rect id="greenRect" fill="lightgreen" height="20" width="20"/>
<!-- transformation are applied right-to-left -->
<use href="#greenRect" transform="translate(0 150) scale(2.5)"/>

Related

SVG how to set max width for group

I have some SVG elements grouped together in a <g> element (exactly the barcode 1D, PHP generates a barcode).
<g
style="fill:#000000;stroke:none"
id="barcode1D"
transform="matrix(1.2083333,0,0,0.8247805,62.027778,573.54235)">
<rect
x="0"
y="0"
width="4"
height="30"
id="xyz" />
....
<rect
x="224"
y="0"
width="0"
height="30"
id="xyzn" /> </g>
The barcode is generated in various widths, lengths. How can I set the width permanently ?
Based on this example, I am asking for a hint. Thank you for your help in advance.
SVG g element does not have width and height attributes. Therefore, you can not set height and width on it.
You should use a foreignObject with a svg inside of it to do so.
<svg width="640" height="480" xmlns="http://www.w3.org/2000/svg">
<foreignObject id="G" width="300" height="200">
<svg>
<!-- Barcode here -->
<rect fill="black" stroke-width="2" height="112" width="84" y="55" x="55" stroke="#000000"/>
<circle fill="#FF0000" stroke="#000000" stroke-width="5" cx="155" cy="65" id="svg_7" r="50"/>
</svg>
</foreignObject>
</svg>

Use <use> within <clipPath> in <defs>

Can I use <use> to refer to a previously declared object inside a <clipPath> declaration?
So that it uses that element as definition for the clip path?
For example:
<defs>
<rect id="foo" width="20" height="20" />
<clipPath id="bar">
<use href="#foo" />
</clipPath>
</defs>
Yes, you can use the <use> command inside the clipPath declaration to refer to any external objects inside the svg file.
In the example below, clipPath cuts a 80 x 80px square from the image.
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 300 300" style="border:1px solid red">
<defs>
<rect id="foo" width="80" height="80" />
<clipPath id="bar">
<use href="#foo" />
</clipPath>
</defs>
<image clip-path="url(#bar)" xlink:href="http://lorempixel.com/600/600/nature/1" x="20" y="20" height="100%" width="100%" />
</svg>
The red square shows the border of the entire canvas svg.

Creating a border with rounded corners using SVG masks results in lighter corners

I'm trying to recreate the look of an iOS app icon using SVG, but with an outline in case the icon is white on a white background.
Example:
<svg width="76" height="76">
<defs>
<mask id="myMask">
<rect fill="#fff" rx="15" ry="15" width="76" height="76"/>
</mask>
</defs>
<rect id="border" mask="url(#myMask)" fill="#000" x="0" y="0" width="76" height="76" />
<rect id="image" mask="url(#myMask)" fill="#fff" x="1" y="1" width="74" height="74" />
</svg>
(https://jsfiddle.net/d4ngtuqa/1/)
To do this, I'm rendering a filled rectangle behind an image rendered 2x2 pixels smaller (or another rect in my simplified example) and then applying an SVG mask to both layers.
However, when I do this, the rounded corners of the border render lighter than the rest of the border. Is this a rendering bug or something? Is there an alternate approach that could avoid this?
You don't need to mask both the image and the border. Just mask the image, then draw a 1px black border on top of it.
<svg width="76" height="76">
<defs>
<mask id="myMask">
<rect fill="#fff" rx="15" ry="15" width="76" height="76"/>
</mask>
</defs>
<rect id="image" mask="url(#myMask)" fill="#fff" x="0" y="0" width="76" height="76" />
<rect id="border" fill="none" stroke="#000" stroke-width="1" x="0.5" y="0.5" width="75" height="75" rx="15" ry="15" />
</svg>
Note that, in order to make the 1px border as neat and clean as possible, we use coordinates for the rect that align to a half-pixel (0.5 and 75.5), so that the line falls cleanly within a line of pixels.

svg mask and bounding box

I need to apply mask to a different objects.
Mask should cover whole object (visible part of it). I put the mask inside one specific place, at the top of the page, changed maskContentUnits to objectBoundingBox, so it worked perfectly.
But then a problem appeared.
I made a fiddle to illustrate the problem: http://jsfiddle.net/8qdt7vjr/1/
<body>
<svg width="0" height="0">
<defs>
<mask id="mask1" maskContentUnits="objectBoundingBox">
<ellipse cx=".5" cy=".5" rx=".5" ry=".5" fill="white" />
</mask>
</defs>
</svg>
<svg class="svg" width="200" height="150" overflow="visible">
<rect x="-50" y="-50" width="350" height="250" fill="none" stroke="green" stroke-width="2" />
<svg id="zzz" x="0" y="0" width="100%" height="100%" overflow="visible" mask="url(#mask1)">
<rect x="-50" y="-50" width="350" height="250" fill="blue" fill-opacity=".3" />
</svg>
<rect width="100%" height="100%" fill="red" fill-opacity=".1" stroke="red" stroke-width="1" />
</svg>
</body>
In this fiddle i want to apply mask to svg element with id="zzz". But inside this element there is a rect element, that increases zzz's bounding box. In the fiddle it is rect, but in my project it is an image tag. zzz has overflow set to hidden (in the fiddle I changed it to visible, so it is easier to see the problem), so I have a specific portion of image visible inside zzz.
The real problem is that zzz's inner content increases zzz's bounding box.
In the fiddle I want ellipse mask to be inside red rectangle (visible zzz's area), but it is inside green rectangle (zzz's bounding box) instead.
So the main question is: is there any way to achieve this goal? Mask will be used by many elements with different sizes and content and I do not want to clone it.
Is there any way to do this without cloning mask inside each element?
Is there any way to reduce bounding box to visible area?
Is there any way to show a part of an image without expanding parent's bounding box?
Is there any other way to do this?
Is it something like this you try to achieve?
<body>
<svg width="0" height="0">
<defs>
<mask id="mask1" maskContentUnits="userSpaceOnUse">
<ellipse cx="100" cy="75" rx="100" ry="75" fill="white" />
</mask>
</defs>
</svg>
<svg class="svg" width="200" height="150" overflow="visible">
<rect x="-50" y="-50" width="350" height="250" fill="none" stroke="green" stroke-width="2" />
<svg id="zzz" x="0" y="0" width="100%" height="100%" overflow="visible" mask="url(#mask1)">
<rect x="-50" y="-50" width="350" height="250" fill="blue" fill-opacity=".3" />
</svg>
<rect width="100%" height="100%" fill="red" fill-opacity=".1" stroke="red" stroke-width="1" />
</svg>
</body>
Maybe using userSpaceOnUse instead of objectBoundingBox can help achieve what you want... In combination with nested svg elements you can define the userspace to use and with that the part the mask is applied to...

SVG scaled within SVG at specific location(s)

I'm new to complex SVG and working on something and need help. I have a couple of SVG files that are already properly formatted with content.. lines, rectangles, text, etc. They are drawn with simple X=, Y=, X1=, Y1= and based on just whole numbers. The original SVG was designed for printing and the x/y positions were set based on printing at 300dpi.
So this exists with a couple SVG coming from other origins and I'm trying to merge into a new single SVG document. So, one of these elements, I need to put at position (x,y) based on either inches or centimeters (from what I've read so far), but I also need them to respect a specific size of... say 2 in tall, 3.4in wide.
Since the original SVG was based on just whole numbers and no orientation to "inches", what can I do.. or, how can it self-scale.
Without proper SVG syntax, here's basically some of the details.
SVG1 has an overall x/y rectangle area of 0,0 to 476,100
SVG2 has an overall x/y rectangle area of 0,0 to 273,24
The new SVG needs to be 4" by 6"
Ex: at position 1/4" down, 1" across from the top, I need to insert SVG1, and even though it is 476x100, it needs to be scaled into an area about 1/2" tall x 3" wide.
Similarly,
at position 2.8" down, 1.75" across, I need to insert SVG2, and its size needs to be about 2" tall, and 2.5" wide as a maximum area.
Scaled yes, but not to be skewed, they need to keep their original proportions and not clipped. If I can get the basic understanding, I can tweak the final dimensions, just don't know how to get the infrastructure of this working.
Thanks.
I finally got it after much playing around, just in case anyone is interested and relatively new with SVG as I am. As in the question, I had some pre-generated SVG output files that have their X,Y Height,Width settings all based on numeric value, with no context to inch, centimeter, etc. but my requirement was to fit into a given X inch by Y inch range.
So, I found out about "defs" tag, which is like declaring a variable that can later be used as a "put that thing HERE" within the later SVG body. At the TOP of the SVG, I was able to give the dimensions I needed. Then, by using the "g" tag for grouping, I am able to numerically position something to a given x,y position. Then, within that, I did another "g" to apply a scaling of the "variable" as declared in the "defs" section (as a "g" element can not have two "transform" tags within it).
What I came up with was something like below, and hope the detailed comments can help others in their research dealing with SVG.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Explicitly define the SVG window area as 4 inches by 6 inches -->
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="4in" height="6in" >
<!-- Add some styles, fonts etc for line drawing, labels and data -->
<style type="text/css" >
<![CDATA[
line.Black
{ stroke:RGB(0,0,0);
stroke-width:2;
}
text.Hdr
{
font-family: Arial;
font-size:x-small;
stroke: #000000;
stroke-width:.4;
}
text.Data
{
font-family: Courier New;
font-size:normal;
stroke: #000000;
stroke-width:.5;
}
]]>
</style>
<!-- all these "defs" are zero-based position for their own content
and will be speicifcally placed where needed via "g" tags.
The simple "id" name will be used as an "insert <something> here" -->
<defs>
<!-- Below will come from actual data from system
The "ID" is what becomes the "variable" that is later
used later in the SVG as the "put me here" -->
<g id="DataOneElement" >
<text class="Data">SOME TEXT DATA</text>
</g>
<!-- This partial linear barcode generated somewhere else
Notice these are just integer positions, and nothing
to do with specific "inches" measurements. Also, they
start at position 0,0 and go however large they need.
When applied with the "g" positioning, thats where it
starts, then gets scaled from there if needed bigger/smaller -->
<g id="DataPartialBarCode" >
<rect x="0" y="0" width="1" height="50" />
<rect x="4" y="0" width="1" height="50" />
<rect x="6" y="0" width="3" height="50" />
<rect x="10" y="0" width="3" height="50" />
<rect x="14" y="0" width="1" height="50" />
<rect x="16" y="0" width="3" height="50" />
<rect x="20" y="0" width="3" height="50" />
<rect x="24" y="0" width="1" height="50" />
<rect x="26" y="0" width="1" height="50" />
<rect x="30" y="0" width="1" height="50" />
<rect x="32" y="0" width="1" height="50" />
<rect x="34" y="0" width="1" height="50" />
<rect x="38" y="0" width="3" height="50" />
</g>
<!-- Actual data generated from AMS to populate these too.
Notice here too, the entire address starts as position 0,0 -->
<g id="SampleAddress" >
<text class="Data" x="0" y="0">Some Person's Name</text>
<text class="Data" x="0" y="17">First Address Line</text>
<text class="Data" x="0" y="30">Another Address</text>
<text class="Data" x="0" y="43">3rd Address line</text>
<text class="Data" x="0" y="56">And Testing for longer address content</text>
</g>
<!-- another bar code that will generated -->
<g id="AnotherBarCode" >
<rect x="0" y="0" width="1" height="70" />
<rect x="4" y="0" width="1" height="70" />
<rect x="6" y="0" width="3" height="70" />
<rect x="10" y="0" width="3" height="70" />
<rect x="14" y="0" width="1" height="70" />
<rect x="16" y="0" width="3" height="70" />
<rect x="20" y="0" width="1" height="70" />
<rect x="24" y="0" width="1" height="70" />
<rect x="26" y="0" width="1" height="70" />
<rect x="28" y="0" width="3" height="70" />
<rect x="32" y="0" width="1" height="70" />
<rect x="36" y="0" width="1" height="70" />
<rect x="38" y="0" width="3" height="70" />
<rect x="42" y="0" width="3" height="70" />
<rect x="46" y="0" width="1" height="70" />
</g>
</defs>
<!-- Now, starting the drawing of the SVG...
Border around entire box drawing area
Notice these are in specific INCH dimensions... -->
<line class="Black" x1="0in" y1="0in" x2="4in" y2="0in" />
<line class="Black" x1="0in" y1="0in" x2="0in" y2="6in" />
<line class="Black" x1="4in" y1="0in" x2="4in" y2="6in" />
<line class="Black" x1="0in" y1="6in" x2="4in" y2="6in" />
<!-- Translate is Across then Down from the top/left corner of SVG -->
<!-- Translate is NOT based on inch, cm, or other measurements
so you may have to tweak these numbers -->
<g transform="translate( 100 20 ) ">
<!-- Now, take whatever we are providing and scale it within the area.
In this case, scale the ENTIRE "g" object to 1.5 its original size -->
<g transform="scale(1.75)">
<!-- This is where the "defs" variable declaration comes
in, as looking at the "g" tag by the ID name -->
<use xlink:href="#DataOneElement" />
</g>
</g>
<!-- and now the partial barcode "defs" variable -->
<g transform="translate( 20 23 ) ">
<!-- In this case, scale the width by 115% and the height by 95% -->
<g transform="scale( 1.15 .95 )">
<use xlink:href="#DataPartialBarCode" />
</g>
</g>
<!-- Any other specific lines within the area -->
<line class="Black" x1="0in" y1=".8in" x2="4in" y2=".8in" />
<!-- Now, just insert the "defs" from above at a scale that will still be readable.
Cool thing, the entire address is considered a single element here. -->
<g transform="translate(20 97)">
<g transform="scale(.7)">
<use xlink:href="#SampleAddress" />
</g>
</g>
<!-- We can even show the address AGAIN, scaled differently elsewhere. -->
<g transform="translate(2 250)">
<g transform="scale(1.3)">
<use xlink:href="#SampleAddress" />
</g>
</g>
<!-- Another line and then barcode-->
<line class="Black" x1="0in" y1="1.55in" x2="4in" y2="1.55in" />
<g transform="translate( 175 175 ) ">
<!-- Scale this barcode 100% wide, but only 70% height -->
<g transform="scale(1 .7)">
<use xlink:href="#AnotherBarCode" />
</g>
</g>
</svg>

Resources