Specifying the Units in an SVG Path Generator - html

Due to confidentiality, I cannot get into the specifics of the code I am working with, but I'll do my best to describe the issue I'm having.
My professor made a simple-ish React program that generates and outputs SVGs based on a set of parameters for a microchip manufacturing project. We make use of the Flatten-js npm library to create the svgs, along with some helper functions that cobble everything together. We specify parameters as a number, but the number represents units in microns. We have a save function that downloads the svg from the webpage, which makes use of the Flatten.Polygon.svg() method. The method takes some parameters, but specifying units is not one of them. The SVG looks something like:
<svg>
<path stroke="none" stroke-width="1" fill="black" fill-rule="evenodd" fill-opacity="1" d="
M5999.774896627996,6000.3432414235895 L5992.810049576532,6002.509804857073 A0.0338333333333333,0.0338333333333333 0 0,1 5992.789950423468,6002.445192150708 L5999.754797474932,6000.278628717223 A0.04999999999999982,0.04999999999999982 0 0,1 5999.8,6000.25 L6005.7,6000.25 A0.04999999999999982,0.04999999999999982 0 0,1 6005.7,6000.35 L5999.8,6000.35 A0.04999999999999982,0.04999999999999982 0 0,1 5999.774896627996,6000.3432414235895 z
..."></path>
</svg>
The issue is, I need to import the svg into Adobe Illustrator and Fusion 360. When I import them, the SVG works well enough to contain the shapes we predict, but the scaling is wrong. Since the units were meant to be in microns, but F360 works in mm, I expected to have to scale it down to 0.001 the size, but it is way too small when scaled. I'm assuming this has something to do with the program assuming the path's DPI, so in Illustrator I hoped that I could specify PPI manually, but it only has preset values of 72PPI and 300PPI, but the size of the svg in Illustrator is the same regardless of the PPI setting.
To a point, this ceases to be about React, and more about the specifics of html and svg paths, but the program does use React to do what it does. I'm hoping we can do it in code, but if I have to manually edit SVGs I will. Any insight would be appreciated. Thanks!

To spell out what #enxaneta probably thinks, a <svg width="1mm" height="1mm" viewBox="5000 6000 1000 1000"> would identify the
userspace units for the path as micron.
It turns out that this was most definitely the solution. That, coupled with a transform attribute to scale it from microns to mm. Thank you everyone for the speedy responses!

Related

SVG displays incorrectly in Firefox

I have an SVG that I plan to put on a website.
The SVG was generated from illustrator using using the method described here
When viewed in Firefox 84.0.1 I get this:
When viewed in Safari 14.0.2 (16610.3.7.1.9) and Chrome 87.0.4280.88 I get this:
Is this an issue with Firefox or an issue with the SVG?
What's the best way to debug?
Edit
I was able to isolate the problem to the following path. As pointed out by others the issue appears to be with invalid path descriptions in the SVG.
I was able to solve problem by simplifying the path in illustrator and re-exporting.
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="138.71" height="102.25" viewBox="0 0 138.71 102.25"><path d="M73.72,95.49a9.59,9.59,0,0,1,5.2,1.3,1990328647664.52,1990328647664.52,0,0,0,.2.2,9,9,0,0,1,3.7,3.8,8.75,8.75,0,0,0,7.7,4.8,8.46,8.46,0,0,0,7.7-4.8A9.27,9.27,0,0,1,102,97c.1-.1.2-.1.3-.2a10.56,10.56,0,0,1,5.2-1.3,8.71,8.71,0,0,0,7.8-4.3,8.39,8.39,0,0,0-.2-8.9,9.55,9.55,0,0,1-1.5-5.2v-.3a9.69,9.69,0,0,1,1.4-5.2,9,9,0,0,0,.3-9.1,8.64,8.64,0,0,0-8-4.3,9.59,9.59,0,0,1-5.2-1.3,2487910809616.32,2487910809616.32,0,0,1-.2-.2,9,9,0,0,1-3.7-3.8,8.75,8.75,0,0,0-7.7-4.8,8.46,8.46,0,0,0-7.7,4.8,9.27,9.27,0,0,1-3.8,3.8c-.1.1-.2.1-.3.2a10.56,10.56,0,0,1-5.2,1.3,8.71,8.71,0,0,0-7.8,4.3,8.39,8.39,0,0,0,.2,8.9,9.55,9.55,0,0,1,1.5,5.2v.3a9.69,9.69,0,0,1-1.4,5.2,9,9,0,0,0-.3,9.1A8.83,8.83,0,0,0,73.72,95.49Z" transform="translate(-61.55 -45.07)" fill="none" stroke="#6d6665" stroke-linejoin="round" stroke-width="6"/><path d="M154.45,95.49a9.11,9.11,0,0,1,5.3,1.4.1.1,0,0,1,.1.1c4.25,2.56,5.31,9,11.4,8.6,6.22.44,7.09-6.08,11.5-8.6s10.63-.31,13.3-5.8c3.54-5.22-1.82-9.29-1.7-14.4,0-4.93,5-9.26,1.7-14.3-2.73-5.6-9-3.13-13.3-5.7a.1.1,0,0,1-.1-.1c-4.25-2.55-5.31-9-11.4-8.6-6.23-.44-7.09,6.09-11.5,8.6s-10.64.31-13.3,5.8c-3.55,5.23,1.81,9.29,1.7,14.4a9.69,9.69,0,0,1-1.4,5.2C142.83,87.73,147.57,96.09,154.45,95.49Z" transform="translate(-61.55 -45.07)" fill="none" stroke="#6d6665" stroke-linejoin="round" stroke-width="6"/><text transform="translate(9.1 84.85)" font-size="12" fill="#231f20" font-family="MyriadPro-Regular, Myriad Pro">O<tspan x="8.27" y="0" letter-spacing="0em">r</tspan><tspan x="12.24" y="0">i</tspan><tspan x="15.05" y="0" letter-spacing="-0.01em">g</tspan><tspan x="21.68" y="0">inal</tspan><tspan x="4.68" y="14.4">32 pts</tspan></text><text transform="translate(84.57 84.85)" font-size="12" fill="#231f20" font-family="MyriadPro-Regular, Myriad Pro">Simplified<tspan x="9.92" y="14.4">15 pts</tspan></text></svg>
Here is an excerpt from the start of one of the paths in question:
M 279.17, 522.38
a 9.59, 9.59, 0,0,1, 5.2,1.3,
a 497582161915.69, 497582161915.69, 0,0,1, 0.2, 0.2,
a 9, 9, 0,0,1, 3.7, 3.8,
M is a Move path command. a means draw a elliptical Arc.
The first two numbers in the arc command are the X and Y radii of the ellipse. Somehow that second arc has ended up with an enormous radius.
With an radius that large, you could approximate a small section of it with a straight line. That is probably what Chrome and Safari are doing. However Firefox is clearly getting confused by values that large.
How that arc ended up like that, I have no idea. But it appears you have struck an edge case that FF isn't handling very well.
I would suggest reporting it as an Illustrator bug and possibly a FireFox bug. In the meantime, to work around the problem, you would need to edit those shapes and try and fix those faulty arc segments.

Why is <feDropShadow> not displaying?

Edit: trying to create a mcve I wasn't able to repro the issue. Now I'm completely baffled. Works on codesandbox, not in my project.
Initial question
I want to create a dynamic inline SVG element and map its rotation to an [(ngModel)]. Nothing fancy.
The fancy part is that I want to use a <filter> with a <feDropShadow>. And I want the shadow to be dynamic (always point up, regardless of the needle's rotation). It's something I've done before using Vue.
Here's a fiddle demonstrating the effect: https://jsfiddle.net/websiter/y4ghan0k/
But, for the life of me, I can't get the <feDropShadow> to work in Angular when the <svg> is inlined in the template. It just won't display. No error or warning. If I insert it as <img src="path/to/svg"> it works as expected (the shadow is displayed), but then I can't rotate the path anymore, as the element transformed needs to be a child of the element bearing the filter.
Note it's not because of this url() filter issue - I am prefixing the filter with this.location.path().
Here's the gist of my Angular code:
component.ts:
import { Location } from '#angular/common';
export class SomeComponent {
constructor(private location: Location) {}
dsLink = `url(${this.location.path()}#drop-shadow)`;
}
component.html:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="180" y="100"
viewBox="0 0 180 100" xml:space="preserve">
<defs>
<filter xmlns="http://www.w3.org/2000/svg" id="drop-shadow" height="130%">
<feDropShadow dx="0" dy="-4" flood-color="rgba(0,0,0,.65)"/>
</filter>
</defs>
<g [attr.filter]="dsLink">
<path fill="#fff" d="M102.2,89.5c0-0.1,0-0.1,0-0.2c0-0.2,0-0.4-0.1-0.6L92.9,6.8c-0.1-0.8-3.2-0.9-3.3,0
L78.7,88.5c-0.1,0.2-0.1,0.4-0.1,0.6c0,0.1,0,0.1,0,0.2l0,0.1c0,0,0,0.1,0.1,0.1c0.5,2.4,5.6,4.4,11.7,4.4
c6.2,0.1,11.2-1.8,11.8-4.2c0,0,0.1-0.1,0.1-0.1L102.2,89.5z">
</path>
</g>
</svg>
For simplicity, I've removed the [(ngModel)] from path which is supposed to rotate the needle.
The filter url() appears to be correct, there's no error. But the shadow is not displayed.
Is there anything special I need to do/know in order to make Angular handle <svg> elements inline?
What am I missing?
I finally cracked it so I'm posting it here, hoping it will help others.
In short: use unique ids for filters in each of your component instances. Otherwise, each instance will use the first filter found in DOM (with that id) and if that filter happens to be inside a parent with display: none, visibility:hidden or opacity: 0, applying the filter will make whatever you apply it to invisible as well.
The issue had to do with the fact I was using the same component in different tabs. This created separate instances of the component, each of them using the same id (#drop-shadow). While having duplicate ids is obviously invalid HTML, this wouldn't actually have been a problem if we weren't dealing with filters. Because, since the <defs> are identical, it wouldn't really matter if the component on the 4th tab would use the <defs> defined by the component on the first tab.
Except when dealing with <filter>s, because they are actually calculating, pixel by pixel, the rendering result, dynamically. Which means that, when the <svg> defining the <filter> is not rendered, using the filter will make the browser calculate (pixel by pixel) the result applying the filter and it always result in all the pixels being invisible.
So the solution is to assign a unique id in each separate instance of the component.

How do I rewrite on client side a svg file I fetched from the server?

Background:
I running an express server with a route to all my static files.
In the static files, I have a folder full of map .svg's.
On the client side (React), I fetch the maps and adding them to to a component.
Question:
I need to rewrite the .svg's, and add this to their path attribute: fill=url(#some_flag).
Svg before:
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="1024pt" height="1024pt" viewBox="0 0 1024 1024"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0,1024) scale(0.1,-0.1)"
fill="#000000" stroke="none">
<path d="... numbers...-78 -28 -115 "/>
</g>
</svg>
Svg After:
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="1024pt" height="1024pt" viewBox="0 0 1024 1024"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0,1024) scale(0.1,-0.1)"
fill="#000000" stroke="none">
<path d="... numbers...-78 -28 -115"
fill="url(#some_flag)"/> <!--THE ADDED ATTRIBUTE -->
</g>
</svg>
I had a few directions in mind:
Pure Javascript: setAttribute of path, which means I'll have to give each path an id?
somehow penetrate the .svg with CSS? I tried it and its impossible to override inline SVG attributes
Have you dealt with it before (of course you did...)? What are my options?
I'm answering this part: << I had a few directions in mind:
1- Pure Javascript: setAttribute of path, which means I'll have to give each path an id? >>.
It can be the best solution, but I wouldn't use "id" because it is not reliable enough (no unicity guaranty, if inadvertently duplicated).
You want to add an attribute fill="url(#some_flag)", and your #some_flag is probably indexed somewhere (flag[i]), and linkable to the sequence of path. If #some_flag must be computed from the geometry, you may pre-process it into an array, or compute at run-time. The CSS selector can be more complex also, depending on your needs. This may answer that part of your question:
document.querySelectorAll("g > path")
.forEach((x,i) => x.setAttribute("fill",`url(${flag[i]})`));
Actually I did experiment it with a svg map excerpted from The Economist Big Mac Index (a local copy):
fetch("./bigMacSvg1.txt", {method:"GET"})
.then(res => res.text())
.then(res => {
//console.log(res);
document.querySelector(".here").innerHTML = res; //div for svg
document.querySelectorAll("path:nth-child(n+2)")
.forEach(x => x.setAttribute("fill","#a1b2c3"));
})
.catch(err => {console.log(err)});
The above code sets a light color to every country, but not to the ocean (first path).
As I think you've discovered, including SVG's via an img tag is limiting. Avoid it.
You can set up a sort of proxy component that fetches the SVG contents, converts them to JSX, and lets you include the SVG contents inline. This will allow them to be easily manipulated with CSS and JavaScript.
Here's a library that will handle translating the files to JSX format.
https://www.npmjs.com/package/svg-to-jsx

xlink:href attribute of the "use" tag inside svg stops working sometimes in IE11

In our code we are using constructions like:
<svg><use xlink:href="#undo"></use></svg>
where "#undo" is
<svg>
<symbol id="undo" viewBox="4 -4 24 24"><path d="M18 1c-2.6 0-4.7.7-7 3L8 1l-1 9 9-1-3-3s2-2 5-2c2.6 0 4.2 1.6 4 5-.1 1.9-.8 3.3-1 4l2 2s2-3 2-7c0-4.3-3-7-7-7z"/></symbol>
</svg>
in the same document.
This works almost in every browser. But for some reason it stops working after some DOM manipulations (deattaching and attaching back) in IE11 only. Can anybody light why this happens?
Unfortunately we haven't managed to reproduce it in small isolated sample. Probably somebody can direct me - where to look for, what attributes to check or something else. Any help highly appreciated.
Update 1
Some clarification: in my case svg icon just desappears, icon content is not visible in spite of the svg element still in DOM and visible. I haven't told about mouse events.
I've tried
<svg><use xlink:href="#undo" style="pointer-events: none;"></use></svg>
as suggested by #Robert Londson (in the Click events stop working after replacing attribute of <use> element in <svg> (Win7/IE11) thread) but this hasn't help.

What does d mean in svg path?

<svg height="210" width="400">
<path d="M150 0 L75 200 L225 200 Z" />
</svg>
Does d stand for something in particular? I couldn't find anything via Google.
d is Path Data. The definition of the outline of a shape.
Reference : http://www.w3.org/TR/SVG/paths.html#PathData
d refers to the <path> data. (mdn link)
M tells the canvas to put the pen down at a specific location. It doesn't draw to that point from wherever it was previously.
L tells the canvas to draw a line from wherever it was last to the given coordinate.
Z tells the canvas to stop drawing (pick the pen up).
I found this overview from Dashing d3js on SVGs helpful.