SVG text doesn't render on Safari, but renders on Chrome - google-chrome

I have some and SVG component that has text in the center - this text renders perfectly fine on Chrome, but just doesn't appear on Safari.
Here are the differences between the 2 browsers:
I'm not sure what the issue is. How can I resolve this?
Here's a link to the code:
https://codepen.io/iamegamind/pen/bGwmpVg?editors=1100
I've done this using the following code:
<svg style="width: 250px; height: 250px"
viewBox="0 0 150 120"
class="progress-card"
xmlns="http://www.w3.org/2000/svg">
<circle style=" stroke-dasharray: 339.29; stroke-dashoffset: 439.29; stroke-width: 8"
r="40"
cx="50%"
cy="50%"/>
<text class="title" x="63" y="50">
<tspan text-anchor="middle" alignment-baseline="central">
Your credit score
</tspan>
</text>
<text class="score" x="63" y="65" text-anchor="middle" alignment-baseline="central">
510
</text>
<text class="status" style="fill: rgb(2, 171, 118)" x="63" y="85" text-anchor="middle" alignment-baseline="central">
Excellent!
</text>
<text class="min" x="30" y="105" text-anchor="middle" alignment-baseline="central">
0
</text>
<text class="max" x="95" y="105" text-anchor="middle" alignment-baseline="central">
710
</text>
</svg>

Safari is hit or miss on supporting certain CSS properties on certain SVG sub-elements. In this case, it's the transform on the SVG text element that it's freaking out about. If you remove it, it will render just fine.
While you could add SVG transforms on the text element, my recommendation might be to not to put a rotate transform on the SVG element class, but instead put the rotate transform on a g element wrapping the circles within the SVG (why do you need two SVG elements btw?). Then position the text using x,y without the need to reverse the transform on the SVG element itself.

Related

SVG viewBox not correctly work in Chrome but in Edge

I am working a project by use SVG viewBox, this is a html snippet.
<body>
<svg width="960" height="960" viewBox="18897158,-480,960,960">
<g>
<circle cx="18897482" cy="-94" r="3" fill="#18ff00"></circle>
<circle cx="18897837" cy="-102" r="3" fill="#18ff00"></circle>
<line x1="18897482" y1="-94.00" x2="18897837" y2="-102" stroke-width="1" stroke="#18ff00">
</line>
<text x="18897660" y="-98" fill="#18ff00" dominant-baseline="middle"
text-anchor="middle">0.00094mm</text>
</g>
</svg>
</body>
When this html in Chrome, the line element does not display correctly, but it works in Edge. I want to know is this a bug in Chrome?

How to adjust the size of the multiline text in SVG and position it in the middle

I have a text that I want to adjust it to the size of svg container and position it in the middle (horizontally and vertically). I am looking for relative way, not absolute. So far I have tried putting the text inside svg and adjust it with viewBox attribute and also the transform: scale function.
Is there any standard way to do this?
UPDATE:
With the help of commentators I was able to put the text in the middle of the svg container. Thank you!
However, I am still unable to put multiline text in the middle. The second code snippet is the farthest I came to the solution.
Working code for one line text:
<svg width="890" height="500"overflow="hidden;">
<g>
<rect x="0" y="0" width="542" height="495" fill="#6fdd6f"></rect>
<svg x="0" y="0" width="542" height="495" viewBox="0 0 100 100">
<text alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" x="50%" y="50%">TXT</text>
</svg>
</g>
</svg>
Code with multiline that needs to be adjusted to center:
<svg width="890" height="500"overflow="hidden;">
<g>
<rect x="0" y="0" width="542" height="495" fill="#6fdd6f"></rect>
<svg x="0" y="0" width="542" height="495" viewBox="0 0 100 100">
<text alignment-baseline="middle" dominant-baseline="middle" text-anchor="middle" >
<tspan x="50%" y="50%">TXT</tspan>
<tspan dy="1em" x="50%" y="50%">more TXT</tspan>
<tspan dy="2em" x="50%" y="50%">end of TXT</tspan>
</text>
</svg>
</g>
</svg>

How can I scale an SVG coordinate system without scaling fonts?

I'm making a data visualization like so:
and it would be convenient to use viewBox to scale the SVG so that the width of the graphic is scaled to be the width of the data range.
My data point in this example is 12,549, and so I want the range to run from 0 to 14,000, and render this in a viewBox of width 14,000 using a rect of width 12,549. In other words, to use natural units for my data.
But when I do this, the font on my axis also scales, and a 10px font also scales, and becomes so small that it can't be seen.
So what I need is a way to scale the drawing units without scaling the font units, but I can't see a way to do this.
Update
Here is the code in question:
<svg width="960" height="50" class="bullet2" style="margin-top: 10px;">
<svg viewBox="0 0 14000 25" preserveAspectRatio="none" width="100%" height="25" class="bars">
<rect x="0" y="0" width="14000" height="25" class="background"/>
<rect x="0" y="0" height="12.5" width="12549" class="item player"/>
<rect x="0" y="12.5" height="12.5" width="3750" class="item team-average"/>
<line x1="2000" x2="2000" y1="0" y2="25" class="marker" style="stroke-width: 29.1667px;"/>
</svg>
<g class="axis">
<g transform="translate(0, 25)" class="tick" style="opacity: 1;">
<line y1="0" y2="5"/>
<text text-anchor="middle" dy="1em" y="6">0</text>
</g>
<g transform="translate(140, 25)" class="tick" style="opacity: 1;">
<line y1="0" y2="5"/>
<text text-anchor="middle" dy="1em" y="6">2000</text>
</g>
<g transform="translate(280, 25)" class="tick" style="opacity: 1;">
<line y1="0" y2="5"/>
<text text-anchor="middle" dy="1em" y="6">4000</text>
</g>
<g transform="translate(420, 25)" class="tick" style="opacity: 1;">
<line y1="0" y2="5"/>
<text text-anchor="middle" dy="1em" y="6">6000</text>
</g>
<g transform="translate(560, 25)" class="tick" style="opacity: 1;">
<line y1="0" y2="5"/>
<text text-anchor="middle" dy="1em" y="6">8000</text>
</g>
<g transform="translate(700, 25)" class="tick" style="opacity: 1;">
<line y1="0" y2="5"/>
<text text-anchor="middle" dy="1em" y="6">10000</text>
</g>
<g transform="translate(840, 25)" class="tick" style="opacity: 1;">
<line y1="0" y2="5"/>
<text text-anchor="middle" dy="1em" y="6">12000</text>
</g>
<g transform="translate(980, 25)" class="tick" style="opacity: 1;">
<line y1="0" y2="5"/>
<text text-anchor="middle" dy="1em" y="6">14000</text>
</g>
</g>
</svg>
Note the viewBox on the inner svg element scaling it as described. I tried this with the same scaling on the outer svg element, which also includes the axis, but when I do this, the image looks like this:
The axis labels are there, but they are also scaled, and a 10px font out of a 14000 pixel scaling is so small that you can't see it.
This is my solution to your question. I recommend reading about SVGPoint and createSVGPoint. I consider especially useful the information found in this book: Using SVG with CSS3 and HTML5: Vector Graphics for Web Design
function init(){
// a function called on resize
// the function get the new font-size and set the new value of the "font-size"
let fontSize = 25;
let newSize = getValue(fontSize);
// reset the font size
theText.setAttributeNS(null,"font-size",newSize.x)
}
setTimeout(function() {
init();
addEventListener('resize', init, false);
}, 15);
// a function used to recalculate the font size on resize
function getValue(size){
var p = svg.createSVGPoint();
p.x = size;
p.y = 0;
p = p.matrixTransform(svg.getScreenCTM().inverse());
return p
}
svg{border:1px solid;}
<svg id="svg" viewBox="0 0 1000 100">
<line x1="50" x2="950" y1="50" y2="50" stroke-width="20" stroke="skyblue" />
<line x1="500" x2="500" y1="30" y2="70" stroke-width="1" stroke="#999" />
<text id="theText" dominant-baseline="middle" text-anchor="middle" x="500" y="85" font-size="16" >middle</text>
</svg>
Update
Can you explain how this solution works?
There are comments in the code. I'm also adding a quote from Using SVG with CSS3 and HTML5: Vector Graphics for Web Design:
Every SVG element that can take a transform [....] has a getScreenCTM() method. The CMT stands for cumulative transformation matrix. The screen CMT [....] defines how you can convert points in that element coordinate system back into the original, unscaled, untransformed coordinate system of the document window. It includes transformations on this element and it's ancestors plus viewBox scaling.
I hope this is useful. However I think you need to read the whole chapter 18 in the book to fully understand the code.
Please give a Minimal, Complete, and Verifiable example.
There are two approaches to your vague/general scaling problem:
1) Put the scaling SVG inside a container SVG? Stacking; and never scale the container which houses the fonts, relative to the percentage of the child SVG within.
2) It states here that fonts can be scaled using absolute px or relative em units. Please see here for valid length.
I do not see if the rem unit can be set, but if so, try if that works?
(I work with SVGs on occassion and CSS a lot, so there may be gaps in my suggestions)

SVG transform-origin not working in Chrome

I have a SVG based app that makes heavy use of transformation such as translates, rotates and scales. While I have no issue in Firefox, in Chrome, the transform-origin property is not taken in account. It seems to apply the user-agent default value 0px 0px 0.
Here is an example (JSFiddle):
<svg width="400" height="400">
<defs>
<rect id="shape" width="200" height="200"/>
</defs>
<g transform="translate(100,100)">
<use xlink:href="#shape" style="stroke: lightgray; fill: transparent;"/>
<ellipse cx="100" cy="100" rx="3" ry="3" style="fill: black;"/>
<g transform="translate(0,0) scale(0.5) rotate(45)" style="transform-origin: 100px 100px;">
<use xlink:href="#shape" style="stroke: black; fill: transparent;"/>
</g>
</g>
</svg>
As you can see Chrome applies all transformation from top left corner of the shape regardless of the defined origin while Firefox respects the defined origin.
Am I missing something about how transform-origin works with SVG?
Does anyone actually found a way to fix this without compensating with translates?
I am answering to my own question in order to clarify entirely what is going on with transform-origin properties on the SVG 1.1 transform functions and how to overcome this issue in Chrome 48.
First of all, transform-origin is a pure CSS 3 property, it is not related to SVG 1.1 at all. Despite the fact that transform sounds a lot like transform-origin, they apply to different systems. transform exists in both CSS 3 and SVG 1.1 but have separate implementations. transform-origin only exists in CSS 3 and therefore it is not supposed to influence SVG 1.1. The fact that transform-origin has no influence on SVG in Chrome 48 is expected.
So why transform-origin does apply to SVG in Firefox 44? Well the reason is not exactly clear, but it seems that it is part of the ongoing effort from Mozilla to slowly bring support for SVG 2 in Firefox. Indeed with SVG 2, everything will become a CSS 3 transform (no separate implementation) and SVG will therefore get support for transform-origin. I found out about this in the excellent article about the SVG coordinate systems from Sara Soueidan.
Now how can that be overcome in Chrome 48. It is fairly simple but if you want to apply translate(), scale() and rotate() all the same time, you will still need to calculate the offset induced by the scaling and compensate it in your translation.
As Bobby Orndorff mentioned in his answer, it is actually possible to provide the center of rotation to the rotate() function by providing extra x and y parameters. This is already a great improvement. But unfortunately the scale() function does not support such a thing and will always scale from the top left corner of its parent. Therefore you will still have to correct your translation in order to simulate a scale around a center.
Here is the final solution that works on Chrome 48 and Firefox 44:
<svg width="400" height="400">
<defs>
<rect id="shape" width="200" height="200"/>
</defs>
<g transform="translate(100,100)">
<use xlink:href="#shape" style="stroke: lightgray; fill: transparent;"/>
<ellipse cx="100" cy="100" rx="3" ry="3" style="fill: black;"/>
<g transform="translate(50,50) scale(0.5) rotate(45, 100, 100)">
<use xlink:href="#shape" style="stroke: black; fill: transparent;"/>
</g>
</g>
</svg>
The example is mixed CSS transform-origin with a SVG transform. While CSS transform and SVG transform are similar, there are differences. For example, CSS transform can be 2D and 3D while SVG transform is only 2D. CSS transform rotate function accepts the angle as a number combined with an unit (e.g. degs, grad, rad, turn) while SVG transforms accepts the angle as a number (with implied unit of degrees) along with optional second and third parameters (x, y) representing the origin of rotation.
To get the example to work in FireFox and Chrome, you could use a CSS transform instead of a SVG transform. For example...
<svg width="400" height="400">
<defs>
<rect id="shape" width="200" height="200"/>
</defs>
<g transform="translate(100,100)">
<use xlink:href="#shape" style="stroke: lightgray; fill: transparent;"/>
<ellipse cx="100" cy="100" rx="3" ry="3" style="fill: black;"/>
<g style="transform: translate(0,0) scale(0.5) rotate(45deg); transform-origin: 100px 100px;">
<use xlink:href="#shape" style="stroke: black; fill: transparent;"/>
</g>
</g>
</svg>
To get the example to work in FireFox, Chrome, and IE, you could use the SVG transform rotate function with the optional second and third parameters instead of a CSS transform-origin. For example...
<svg width="400" height="400">
<defs>
<rect id="shape" width="200" height="200"/>
</defs>
<g transform="translate(100,100)">
<use xlink:href="#shape" style="stroke: lightgray; fill: transparent;"/>
<ellipse cx="100" cy="100" rx="3" ry="3" style="fill: black;"/>
<g transform="translate(0,0) scale(0.5) rotate(45,200,200)">
<use xlink:href="#shape" style="stroke: black; fill: transparent;"/>
</g>
</g>
</svg>

SVG text not centering firefox

I have a SVG logo with text, text is centered.
And this works fine in chrome and IE but not firefox, the text is slightly moved to the left.
<text transform="matrix(0.9287 0 0 1 60.9023 137.7646)">
<tspan x="0" y="0" fill="#FFFFFF" stroke="#FAFAF8" stroke-miterlimit="10" font-family="'Consolas'" font-size="71.5163" letter-spacing="9.691">SOC</tspan>
<tspan x="-24.809" y="85.819" fill="#FFFFFF" stroke="#FAFAF8" stroke-miterlimit="10" font-family="'Consolas'" font-size="71.5163" letter-spacing="9.691">KING</tspan>
</text>
Please help.
use text-anchor="middle" to center the text (then its x coordinate points to the center)
OK so I came up with a work around to get letter spacing the same in both chrome and firefox. What I did was add both to text-transform: letter-spacing="9.691" textLength="780px" and adjusted the x until both browsers displayed the text in the center of the image. Its not pretty but it works.
<text transform="matrix(0.9287 0 0 1 60.9023 137.7646)" letter-spacing="9.691" textLength="780px" font-weight="bold">
<tspan x="1" y="0" fill="#FFFFFF" font-family="'Consolas'" font-size="71.5163">SOC</tspan>
<tspan x="-21.8" y="85.819" fill="#FFFFFF" font-family="'Consolas'" font-size="71.5163">KING</tspan>
</text>