Skip to content

Modernize fixed pitch flag computation#5506

Merged
skef merged 3 commits intofontforge:masterfrom
iorsh:mono_flag
Jan 1, 2025
Merged

Modernize fixed pitch flag computation#5506
skef merged 3 commits intofontforge:masterfrom
iorsh:mono_flag

Conversation

@iorsh
Copy link
Copy Markdown
Contributor

@iorsh iorsh commented Dec 5, 2024

This PR allows monospace fonts with modern features

  • Zero-width glyphs are allowed, such as control characters or diacritical marks
  • Dual-width glyphs are alowed, such as emojis or kanij

Fixes #5489

@iorsh
Copy link
Copy Markdown
Contributor Author

iorsh commented Dec 5, 2024

CI is a gift that keeps on giving... Libspiro is broken on Windows.

@JoesCat, do you know just in case what could have happened?

@iorsh
Copy link
Copy Markdown
Contributor Author

iorsh commented Dec 6, 2024

Looks like the CI issue is not about Spiro, but rather broken pacman - see jtanx/fontforgebuilds#22

@JoesCat
Copy link
Copy Markdown
Contributor

JoesCat commented Dec 6, 2024 via email

@JoesCat
Copy link
Copy Markdown
Contributor

JoesCat commented Dec 7, 2024

mingw may also be gradually sliding away from 32bit. I noted reading a request to upgrade something to help with gimp3 which wasn't built for 32bit, but was for 64bit.

Comment thread fontforge/splinesave.c Outdated
dummynotdef.layer_cnt = sf->layer_cnt;
dummynotdef.layers = calloc(sf->layer_cnt,sizeof(Layer));
dummynotdef.width = SFOneWidth(sf);
dummynotdef.width = CIDOneWidth(sf);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're using this function in these updated cases, should the final function name be SFOneWidth again?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, renamed.

@skef skef merged commit c5eb85b into fontforge:master Jan 1, 2025
@iorsh iorsh deleted the mono_flag branch January 1, 2025 16:55
Finii added a commit to Finii/fontforge that referenced this pull request Dec 7, 2025
[why]
When generating a monospaced (i.e. isFixedPitch) font we assume that all
glyphs have the same width, all glyphs expect the first three magic ones
(`.notdef` (ID0), `.null` (ID1), and `nonmarkingreturn` (ID2)).

Because `isFixedPitch` means that all glyphs have the same width it is
hardcoded that we just include the width of ID3 - all following glyphs
will get the same width. This is done to reduce the mtx table size.

There are two reaons why this is not correct.

a)

If the font comes with a glyph `NULL` at ID1 (which is allowed) we will
move the 'unexpected' `NULL` further on, and insert a new `.null`.

The font will end with this glyph order:
`.notdef` (ID0), `.null` (ID1), `nonmarkingreturn` (ID2), `NULL` (ID3), ...

But the width of `NULL` is zero, and so all width in the font will be
zero. Instead of hard-coding 'ID3' as first real width we would need to
check if it a glyph with a width.

Of course we could also correct out auto-inserted magic glyphs and just
keep the name `NULL` for ID1. Albeight that might also be desirable, it
is not a complete solution, because:

b)

With the 'new' (modern?) way to determine isFixedPitch (footnotes),
especially with allowing two width in a fixed pitch font (one "1 cell"
wide and one "2 cell" wide width), we need to check and possibly
encode all widths.

Example follows, InputMono-Regular.ttf is used with ttx.

Original situation before read in:

    <GlyphOrder>
      <GlyphID id="0" name=".notdef"/>
      <GlyphID id="1" name="NULL"/>
      <GlyphID id="2" name="nonmarkingreturn"/>
      <GlyphID id="3" name="space"/>
      <GlyphID id="4" name="A"/>

After we exported the font:

    <GlyphOrder>
      <GlyphID id="0" name=".notdef"/>
      <GlyphID id="1" name=".null"/>
      <GlyphID id="2" name="nonmarkingreturn"/>
      <GlyphID id="3" name="NULL"/>
      <GlyphID id="4" name="space"/>
      <GlyphID id="5" name="exclam"/>

Note that NULL is moved down to index 3 (and all following glyphs are
reordered).

The current code hard codes that ID3 shall be the last entry in mtx, and
`NULL` has a width of zero in the example.

Furthermore there are glyphs of zero width or the "2-width" width, that
also all will loose their values (originals following):

    <mtx name="caron" width="700" lsb="139"/>
    <mtx name="caroncmb" width="0" lsb="-211"/>
    <mtx name="caroncmb.salt" width="0" lsb="-114"/>
    <mtx name="ccaron" width="700" lsb="100"/>

Note the cmbs with a zero width and a negative lsb. Even if we correct
the `lasthwidth` to for example 4 we would loose all differentiation on
widths:

    <mtx name="caron" width="700" lsb="139"/>
    <mtx name="caroncmb" width="700" lsb="-211"/>
    <mtx name="caroncmb.salt" width="700" lsb="-114"/>
    <mtx name="ccaron" width="700" lsb="100"/>

[how]
We can not assume any width structure on a `isFixedPitch` font, and the
mtx table can not be shortened any more rigurous than usual, meaning we
walk backwards through the glyphs and find the last change in width,
dropping all numbers in the end that stayed the same.

`FigureFullMetricsEnd()` already find the optimally short end for the
mtx table while making sure to keep all widths.

[footnotes]
The 'modern' isFixedPitch is introduced with commit:

  c5eb85b `Modernize fixed pitch flag computation` (fontforge#5506)

The bug turned up in these issues:

ryanoasis/nerd-fonts#1948
https://gitlab.archlinux.org/archlinux/packaging/packages/ttf-input-nerd/-/issues/2

Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
@Finii
Copy link
Copy Markdown
Contributor

Finii commented Dec 8, 2025

This PR allows monospace fonts with modern features

* Zero-width glyphs are allowed, such as control characters or diacritical marks

* Dual-width glyphs are alowed, such as emojis or kanij

Well, the different-from-monospaced-width widths are not written out to a ttf file; otf works.
tottf does not allow different width for individual glyphs if isFixedPitch is active.

tottf exports only the widths of the first 4 glyphs, beginning with the 3 magic glyphs.

So even if the 4th glyph has 'normal' width, there will be no zero and no dual width glyphs in ttfs.
The PR above handles the more severe case with the 4th glyph has zero width 😬 and of course also the general case.

Edit: Add last paragraph

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

Successfully merging this pull request may close these issues.

Ufo3 export of monospace fonts does not set postisfixedpitch to true

4 participants