fritzing / fritzing-app

Fritzing desktop application
http://fritzing.org
Other
4.07k stars 838 forks source link

Fritzing ignores `dominant-baseline` and `dy` attributes in SVG `text` elements. #3909

Open JC3 opened 3 years ago

JC3 commented 3 years ago

Build: Version 0.9.8 (bCD-92-0-b3d4fefa 2021-08-03) 64 [Qt 5.15.2] Operating System: Windows 10

Description

It seems that Fritzing ignores a few attributes related to vertical text alignment when rendering SVGs, in particular:

Workaround

Lack of dy can be somewhat worked around by using transform(translate(0,dy)) instead, although transformations don't accept units (like em) in SVG 1.1 so you'll have to calculate the values. Lack of dominant-baseline does not have a direct workaround transform can still be used if you manually calculate the values you're looking for. In both workarounds, the values you use would also be specific to the font family/size, and so would not be responsive to font changes.

Consequences

One consequence of these values being ignored is that it becomes difficult to vertically center text on a point in a convenient way. This, combined with lack of vertical alignment specification in the design guide (see also #3908) leads to frequent inconsistencies in text alignment. Also, I've noticed that a lot of older parts don't quite use the design guide's font sizes for various labels, so an indirect consequence here is that if we were ever to go through the parts lib and adjust font sizes for consistency, it will take a lot of effort to update parts with hard-coded vertical centering offsets, since those values would all have to be recalculated by hand.

And of course the obvious consequence is that it's a pain to vertically center text, which is a bummer because vertically centered text looks way better on pin and part labels.

Example

Consider the following test SVG (gist):

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg x="0" y="0" width="2in" height="1.0in" viewBox="0 -36 144 72" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <rect x=".25" y="-35.75" width="143.5" height="71.5" fill="none" stroke-width="0.5" stroke="#aaaaaa"/>
  <g font-family="'Droid Sans'" font-size="7.2">
    <g transform="translate(0,-18)">
      <line x1="0" y1="0" x2="144" y2="0" stroke-width="0.1" stroke="#ff0000"/>
      <text x="18" y="0" text-anchor="middle">ÄgrTtQq</text>
      <text x="54" y="0" dominant-baseline="central" text-anchor="middle">ÄgrTtQq</text>
      <text x="90" y="0" dominant-baseline="middle" text-anchor="middle">ÄgrTtQq</text>
      <text x="126" y="0" dominant-baseline="hanging" text-anchor="middle">ÄgrTtQq</text>
    </g>
    <!-- droid sans: x-height ratio .536 (/2=.268), X-height ratio .714 (/2=.357) -->
    <g transform="translate(0,0)">
      <line x1="0" y1="0" x2="144" y2="0" stroke-width="0.1" stroke="#ff0000"/>
      <text x="54" y="0" dy="2.5704" text-anchor="middle">ÄgrTtQq</text>
      <text x="90" y="0" dy="0.357em" text-anchor="middle">ÄgrTtQq</text>
      <text x="126" y="0" transform="translate(0,2.5704)" text-anchor="middle">ÄgrTtQq</text>
    </g>
    <g transform="translate(0,18)">
      <line x1="0" y1="0" x2="144" y2="0" stroke-width="0.1" stroke="#ff0000"/>
      <text x="18" y="0" dy="0.5ex" text-anchor="middle">ÄgrTtQq</text>
      <text x="54" y="0" dy="1.9296" text-anchor="middle">ÄgrTtQq</text>
      <text x="90" y="0" dy="0.268em" text-anchor="middle">ÄgrTtQq</text>
      <text x="126" y="0" transform="translate(0,1.9296)" text-anchor="middle">ÄgrTtQq</text>
    </g>
  </g>
</svg>

Here is a side-by-side comparison:

Correct Fritzing
exampleok examplefritzing

This shows dy and dominant-baseline being ignored, and also confirms the use of hard-coded values in transform as a workaround.

JC3 commented 3 years ago

Some extra notes about that workaround if anybody wants this info:

FYI you can calculate x height ratios at https://jkorpela.fi/x-height.html. You can do X height ratios there too if you edit the page and replace the big lowercase x with a capital X. Here's the values I observed for Droid Sans and OCR A:

Font x-height ratio (half) X-height ratio (half)
Droid Sans 0.536 (0.2680) 0.714 (0.3570)
OCRA 0.569 (0.2845) 0.780 (0.3900)
OCR A 0.471 (0.2355) 0.649 (0.3245)

The x/X heights there are expressed as a fraction of 1em (i.e. a fraction of the font-size):

image

So if you want to vertically center a text item, offset it by fontsize ratio / 2* (use x height to center in lowercase midline, X height to use the capital midline).

Example: Droid Sans, font size 8, centered on lowercase midline: Use 0.5ex or 0.268em (regardless of font size) or 2.144 for this specific size.

KjellMorgenstern commented 3 years ago

Won't fix. Support for this attribute would have to be added here: https://github.com/qt/qtsvg Since it is difficult to get fixes in there, I would however consider using a patched version of the library.

For a (partial) overview of supported features, see https://razrfalcon.github.io/resvg-test-suite/svg-support-table.html

JC3 commented 3 years ago

Won't fix. Support for this attribute would have to be added here: https://github.com/qt/qtsvg Since it is difficult to get fixes in there ...

Oh, yeah. If it's in QtSvg, then it's definitely not officially happening: AFAIK they've only got static 1.2 Tiny support; and I doubt they'd accept a partial implementation of a single feature from full SVG. I also can't see them moving towards full SVG any time soon, for a lot of reasons.

That said I don't know if there's any relevant changes in Qt6. But that doesn't really help Fritzing anyways atm so whatever.

I would however consider using a patched version of the library.

Yeah that'd probably be the most feasible route. Fsv "most feasible"...

vanepp commented 3 years ago

If we are going for a custom library version the solution I would like to see (which is obviously not what the svg standard says!) is the second and third lines trailing AgrTtQq move up to be identical to the one before it in vertical position. While probably not correct according to the standard, it would be better for Fritzing as it makes part labels more consistent. At present part labels move up or down (or left or right if rotated) depending on what characters are in them, which is very inconvenient. I would much rather have labels that are centered on the middle point of the height so no matter the characters in it the line of text is at one level (and can be placed in the center of a pin and doesn't change vertical position based on text.) This however is something that likely only Fritzing (and not general graphics) wants to do, and likely the only way to achieve it would be a custom library.

KjellMorgenstern commented 3 years ago

@vanepp Can you show a case where the label content influences its position (other from the obvious change in the label length)? That sound like a different and much easier to fix issue.

KjellMorgenstern commented 3 years ago

@JC3 Not really an issue, but I think jkoperla.fi reports the wrong height for "OCR A" (it seems to fallback to something similar to OpenSans)

vanepp commented 3 years ago

"@vanepp Can you show a case where the label content influences its position (other from the obvious change in the label length)? "

There is an example (and some other possible issues) in this forum post

https://forum.fritzing.org/t/centering-text-vertically-in-an-svg/13186

I eventually decided I didn't know enough about svgs to be able to do anything useful here except manually position the text where I wanted it and redo it manually when the text changed. What I would find desirable is to be able to center (and have it automatically recenter when the text changes) a line of text so the center of the text is always in the center of the pin. Unsurprisingly that doesn't seem to be an option in the svg standard (it appears to be another special case when doing engineering drawings!)