fxsvgimage icon indicating copy to clipboard operation
fxsvgimage copied to clipboard

Decimal font sizes don't work on the 0.0-1.0 range

Open kxygk opened this issue 2 years ago • 6 comments

I have an SVGs of maps where the units are in degrees (and hence very small). This seems to make the library misbehave.

Here is a minimal example of the axis that I try to draw

<?xml version="1.0"?>
<svg height="1000.0" version="1.1" viewBox="0 0 5.0 5.0" width="1000" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg">
<g>
<g fill="none" stroke="none" />
<g stroke-width="0.005" stroke="black">
<line x1="0.50" x2="0.50" y1="5.00" y2="4.90" />
<line x1="1.50" x2="1.50" y1="5.00" y2="4.90" />
<line x1="2.50" x2="2.50" y1="5.00" y2="4.90" />
<line x1="3.50" x2="3.50" y1="5.00" y2="4.90" />
<line x1="4.50" x2="4.50" y1="5.00" y2="4.90" />
<g fill="black" font-family="Arial, sans-serif" font-size="0.05" stroke="none" text-anchor="start" transform="translate(0.0125 -0.025)">
<text x="0.50" y="5.00">97°</text>
<text x="1.50" y="5.00">98°</text>
<text x="2.50" y="5.00">99°</text>
<text x="3.50" y="5.00">100°</text>
<text x="4.50" y="5.00">101°</text>
</g>
<line x1="0.00" x2="5.00" y1="5.00" y2="5.00" />
</g>
<g stroke-width="0.005" stroke="black">
<line x1="0.00" x2="0.10" y1="5.00" y2="5.00" />
<line x1="0.00" x2="0.10" y1="4.00" y2="4.00" />
<line x1="0.00" x2="0.10" y1="3.00" y2="3.00" />
<line x1="0.00" x2="0.10" y1="2.00" y2="2.00" />
<line x1="0.00" x2="0.10" y1="1.00" y2="1.00" />
<line x1="0.00" x2="0.10" y1="0.00" y2="0.00" />
<g fill="black" font-family="Arial, sans-serif" font-size="0.05" stroke="none" text-anchor="start" transform="translate(0.025 0.00625)">
<text x="-0.00" y="5.05">6°</text>
<text x="-0.00" y="4.05">7°</text>
<text x="-0.00" y="3.05">8°</text>
<text x="-0.00" y="2.05">9°</text>
<text x="-0.00" y="1.05">10°</text>
<text x="-0.00" y="0.05">11°</text>
</g>
<line x1="0.00" x2="0.00" y1="5.00" y2="0.00" />
</g>
</g>
</svg>

I can pare it down further:

<?xml version="1.0"?>
<svg height="1000.0" version="1.1" viewBox="0 0 5.0 5.0" width="1000" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg">

<g>
<g fill="none" stroke="none" />
<g stroke-width="0.005" stroke="black">
<g fill="black" font-family="Arial, sans-serif" font-size="0.05" stroke="none" text-anchor="start" transform="translate(0.0125 -0.025)">
<text x="4.50" y="5.00">101°</text>
</g>
<line x1="0.00" x2="5.00" y1="5.00" y2="5.00" />
</g>
<g stroke-width="0.005" stroke="black">
<g fill="black" font-family="Arial, sans-serif" font-size="0.05" stroke="none" text-anchor="start" transform="translate(0.025 0.00625)">
<text x="-0.00" y="0.05">11°</text>
</g>
<line x1="0.00" x2="0.00" y1="5.00" y2="0.00" />
</g>
</g>
</svg>

The parameter font-size seems to work up to the value 1.0 and then subsequently the letters stop shrinking and start to move together and overlap

Thanks for the library! It seems to work instantaneously even with large SVGs I throw at it. Just a few weird rendering edge cases to iron out - and no more Batik! :D

Here is large complex SVG that also fails:

first-year

kxygk avatar Jan 22 '23 11:01 kxygk

I will look at the second problem next, but for the fonts, it's because the library does not correctly parse font sizes. It is expected that:

  • For SVG, if a is provided without a unit identifier (e.g., an unqualified number such as 128), the SVG user agent processes the as a height value in the current user coordinate system.
  • If a is provided with one of the unit identifiers (e.g., 12pt or 10%), then the SVG user agent converts the into a corresponding value in the current user coordinate system by applying the rules described in Units.

I don't really take into account the first case correctly by considering it is a height in the current viewBox. I will fix the library to do that.

hervegirod avatar Feb 04 '23 15:02 hervegirod

I partly fixed the first problem in my local repo, but fixing completely this problem will involve an extra step because JavaFX does not layout the characters of a String correctly if the font is very small. I will need to write each character one by one if the font size is below a certain value.

hervegirod avatar Feb 07 '23 09:02 hervegirod

Sorry, I embarrassingly somehow missed the notification for your first response. So at the end of the day, from what I understood - the font size, and the text overlap are two separate issues?

It's a good point about the units. It was definitely an oversight on my part.. I'd just kept everything unitless and tweaked it till it looked good in Firefox - so it's possible I did something not very kosher.

I just did a quick test of qualifying the units. I tried to manually change the two instances of font-size="0.05" to be font-size="0.05px" and font-size="0.05pt" - but I don't see any change in the render on my end. It's possible I misunderstood and that this too needs your local changes as well.

I'll try to play around with it a bit more tomorrow. I wonder if I can work around it in my svg generation pipeline with a scale transform instead of setting the font size to this tiny value. Especially if this is a weird corner case you're not particularly interested in fixing :))

kxygk avatar Feb 07 '23 17:02 kxygk

Hello again, I will look at your updated example (font-size="0.05px" and font-size="0.05pt") with my changes to see if they "fix" the problem. However I think that the change I envisioned with very small fonts. I think that it's not too hard to fix on my side, I will look at that ASAP.

hervegirod avatar Feb 08 '23 16:02 hervegirod

Hopefully I'm not abusing the SVG spec here :)

I'd just note that while the SVG has units in degrees, and hence some thing like the font size end up with very small numerical values, the final render is always upscaled. So I'm not trying to render a very small image with tiny small text

The top level <svg> tag is something like

<svg height="1000.0" 
     version="1.1" 
     viewBox="0 0 5.0 5.0" 
     width="1000" 
     xmlns:xlink="http://www.w3.org/1999/xlink" 
     xmlns="http://www.w3.org/2000/svg">

the viewBox part is in effect specifying the dimension of the view area in degrees

While I could see maybe JFX hitting some edge case when rendering microscopic text - but that shouldn't be happening here. It might be architecturally challenging/irritating b/c you need to propagate all the scaling correctly and render at the very end

I did manage to bypass the issue by scaling the text instead of using a small font size.

If before I had

<g fill="black" 
   font-family="Arial, sans-serif" 
   font-size="0.05" 
   stroke="none" 
   text-anchor="start" 
   transform="translate(0.0125 -0.025)">
    <text x="4.50" 
          y="5.00">
    101°
    </text>
</g>

I could rewrite this as

<g fill="black" 
   font-family="Arial, sans-serif" 
   font-size="5px" 
   stroke="none" 
   text-anchor="start" 
   transform="translate(0.0125 -0.025)">
<text x="450.0" 
      y="500.0" 
      transform="scale(0.01)">101°</text>
</g>

Now font-size is a more reasonable 5pt and fxsvgimage renders everything correctly.

The downside is that this scaling is applied to the coordinates as well. So I then needed to descale the x/y. The coordinates went from being 4.5 and 5.0 degrees to being 450 and 500 .. milidegrees? haha

So the result is a little goofy but it works :))

I don't the fxsvgimage internals, but my guess is that this font-size problem is fixeable - just about reordering the scaling/rendering internally somewhere.

Maybe there is some other way to work around this problem though. Not sure :)

kxygk avatar Feb 09 '23 09:02 kxygk

Curiously I've also rerun this using the FranzXaver svg->jfx library and seems to also blows up the text in an identical way. I'm not super familiar with its internals - I thought it uses Batik, but the output doesn't look like a raster image

kxygk avatar Feb 22 '23 09:02 kxygk