Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

should decompose components with different 2x2 transforms when building VF #595

Open
cjdunn opened this issue Nov 14, 2019 · 12 comments
Open

Comments

@cjdunn
Copy link

cjdunn commented Nov 14, 2019

Example: the /Otildeacute glyph in the Black named instance doesn't match the same glyph in the Black UFO master when it uses a nested component. But when the nested component (tildecomb_acutecomb.case) is decomposed, the glyph in the var font matches what's in the master. This seems like a bug. (screenshots below for reference)

As a designer it's better to keep the sources with high level information (ie /tildecomb_acutecomb.case is made up of /tildecomb and /acutecomb.case). And I would expect that a Black named instance in the var font should match the static font generated from that Black UFO master, especially when there is only one axis and only two masters.

I understand that components can't be nested in the final font. But it seems that fontmake could do a better job of decomposing the nested component on generate so the named instance matches the master and we can leave the sources as is for future editing. Thanks! (the example here is from the Alegreya project for @davelab6 and @m4rc1e )

UFO_master
fontmake_compare

@djrrb
Copy link

djrrb commented Nov 14, 2019

I have experienced similar issues with Josefin Sans, where components of components were getting offset, and simply decomposing solved the issue.

davelab6/josefinsans#4
davelab6/josefinsans#14

I’d love to retain components of components in the sources if possible. Many thanks!

@anthrotype
Copy link
Member

anthrotype commented Nov 15, 2019

I cloned the fonts from https://github.com/TypeNetwork/Alegreya-Sans and built with current fontmake v2.0.4 but I cannot reproduce this issue. Did you maybe commit a workaround already?

The glyph "Otildeacute" is composed of component glyphs "O" and "tildecomb_acutecomb.case", which in turn is composed from "tildcomb_acutecomb" (the latter is a simple outline glyph).
When I render the Otildecomb glyph in the VF and static instance fonts, it looks correct in both.

A simple reproducer is worth more than a thousand words.

@cjdunn
Copy link
Author

cjdunn commented Nov 15, 2019

@anthrotype Take a look at: https://github.com/TypeNetwork/Alegreya/tree/fea-next-master
(Alegreya not Alegreya Sans)

@anthrotype
Copy link
Member

I see what's going on. The issue is not to do with nested components.
Basically, you can't have glyphs containing components that have different scale/slant/rotation between different masters of a variable font; only the placement of a component can vary across masters: i.e. the component's X and Y offset. Here's the relevant bit of the OT spec:

Adjustment deltas do not have any effect on scaling or 2 × 2 transformations applied to a component. The deltas only affect the positioning of the component.

https://docs.microsoft.com/en-us/typography/opentype/spec/gvar#point-numbers-and-processing-for-composite-glyphs

In your font you have the tildecomb_acutecomb.case glyph that looks like this in the Alegreya-Regular.ufo:

<glyph name="tildecomb_acutecomb.case" format="2">
  <outline>
    <component base="tildecomb" yOffset="140"/>
    <component base="acutecomb.case" xScale="0.9986" xyScale="-0.0523" yxScale="0.0523" yScale="0.9986" xOffset="-75" yOffset="139"/>
  </outline>
</glyph>

and the same glyph in the bolder master is:

<glyph name="tildecomb_acutecomb.case" format="2">
  <outline>
    <component base="tildecomb" xScale="0.9259" yScale="0.9259" xOffset="15" yOffset="189"/>
    <component base="acutecomb.case" xOffset="-21" yOffset="180"/>
  </outline>
</glyph>

So it's a tecnical limitation of the font format that requires such composite glyphs with complex transformations beyond simple XY offsets to be decomposed.

I suppose fontmake could be smarter and decompose these components on-the-fly before they reach varLib (in a ufo2ft filter).

The reason this works when generating static instances is because when we interpolate UFOs with fontMath, the latter also interpolates the 2x2 transform matrix, not just the component's offsets.

@anthrotype anthrotype changed the title nested component issue should decompose components with different 2x2 transforms when building VF Nov 15, 2019
@m4rc1e
Copy link
Contributor

m4rc1e commented Nov 15, 2019

Thanks Cosimo! that makes sense.

@cjdunn
Copy link
Author

cjdunn commented Nov 15, 2019

@anthrotype thank you for taking a look at this. The fact that they get decomposed in the end isn't the problem for me, and I understand that's a limitation of the format.

fontmake could be smarter and decompose these components on-the-fly before they reach varLib (in a ufo2ft filter).

Yes, this would be amazing, thank you!

@anthrotype
Copy link
Member

btw, it's not the fact that there is any transformation, but that the transformations differ across the masters. If they are the same, i think it should be ok, as the transform will be written in the default glyf table, and the gvar will take care of shifting components around. Although that should probably be tested.

@anthrotype
Copy link
Member

i'm curious, do you know if Glyphs.app decomposes them when generating a VF?

@cjdunn
Copy link
Author

cjdunn commented Nov 15, 2019

I haven't been generating any VFs with Glyphs.app so I'm not sure.

@laerm0
Copy link

laerm0 commented Nov 16, 2019

You can add a custom parameter in Glyphs' instance panel that goes when exporting VFs (Decompose Components). It requests you add a list of which glyphs to decompose, which is helpful IMO.

@anthrotype
Copy link
Member

anthrotype commented Nov 18, 2019

Thanks Micah! That reminded me that fontmake also supports specifying a ufo2ft filter to decompose specific composite glyphs at build time.
ufo2ft filters are still not well documented (though @madig had been working on a PR googlefonts/ufo2ft#286).

But basically, in your specific case, you can have fontmake decompose "tildecomb_acutecomb.case" (and similar glyphs with complex transforms) in Alegreya-Regular.ufo and Alegreya-Bold.ufo by adding a "com.github.googlei18n.ufo2ft.filters" key in the UFOs lib.plist, containing a "decomposeComponents" filter like this one:

diff --git a/sources/Alegreya-Bold.ufo/lib.plist b/sources/Alegreya-Bold.ufo/lib.plist
index 76f20563..6716983b 100644
--- a/sources/Alegreya-Bold.ufo/lib.plist
+++ b/sources/Alegreya-Bold.ufo/lib.plist
@@ -5963,5 +5963,18 @@
     </dict>
     <key>width</key>
     <integer>30</integer>
+    <key>com.github.googlei18n.ufo2ft.filters</key>
+    <array>
+      <dict>
+        <key>name</key>
+        <string>decomposeComponents</string>
+        <key>include</key>
+        <array>
+          <string>tildecomb_acutecomb.case</string>
+        </array>
+      </dict>
+    </array>
   </dict>
 </plist>
diff --git a/sources/Alegreya-Regular.ufo/lib.plist b/sources/Alegreya-Regular.ufo/lib.plist
index 4d25f045..48a6e0dd 100644
--- a/sources/Alegreya-Regular.ufo/lib.plist
+++ b/sources/Alegreya-Regular.ufo/lib.plist
@@ -5967,5 +5967,18 @@
     </dict>
     <key>width</key>
     <integer>84</integer>
+    <key>com.github.googlei18n.ufo2ft.filters</key>
+    <array>
+      <dict>
+        <key>name</key>
+        <string>decomposeComponents</string>
+        <key>include</key>
+        <array>
+          <string>tildecomb_acutecomb.case</string>
+        </array>
+      </dict>
+    </array>
   </dict>
 </plist>

Normally the decomposeComponents filter runs by default on all fonts (we need to decompose mixed contour+component glyphs). Here we make a second pass but only on a particular selection of glyphs, those specified in the include array.

Of couse, with this manual approach the font developer will have to get/know the list of glyphs with transforms that require decomposition. Ideally fontmake should do that automatically when building a VF.

For a Glyphs-based build workflow, the same thing can be achieved by adding a "Filter" or ("PreFilter") custom parameter named "Decompose Components" to both the Regular and Bold masters (not to the instances, otherwise the VF will not see them, since it is built from the masters), with an include: tildecomb_acutecomb.case, ... list of glyphs to decompose.
Fontmake (via glyphsLib) knows how to convert this into the equivalent ufo2ft filter.

@raphlinus
Copy link

I ran into this while doing release work on Inconsolata, and ended up writing a quick script to detect mismatched 2x2 matrices and decompose just those components. I hope others find it useful:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants