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

font-patcher: Make Nerd Fonts Monospaced Again #764

Merged
merged 6 commits into from
Sep 6, 2022

Conversation

Finii
Copy link
Collaborator

@Finii Finii commented Jan 14, 2022

This reverts commit 59c45ba.

In the following The Commit means the commit that this PR want to revert (remove).

[why]
The Commit breaks the non-mono Nerd Fonts for a lot of people.
The issue which is the reason for This Commit was not very good documented and investigation can not be
seen.

From The Commit's TITLE it should have affected the 2048-em Symbols font only, but in fact all patched fonts were changed.
Maybe that was intended, maybe not.

[how]
This will make the advance width again equal for all glyphs.
This enables the use of the non-mono variant in more terminals.
For wider symbols a space is now (again) needed after the symbol.
That is expected by a lot applications.

Requirements / Checklist

What does this Pull Request (PR) do?

Make Non-Mono Nerd Fonts monospaced but allow added symbols to extend over the allotted space to the right (negative right side bearings).

How should this be manually tested?

Any background context you can provide?

The behavior has been changed with The Commit 59c45ba that has been written to fix this issue (at Arch) https://bugs.archlinux.org/task/66212.

Most of the fonts in patched-fonts/ were not updated after that change, so the problems with that change became not obvious until recently.

If it is a problem or not depends also on the used terminal application. Some scale individual glyphs (like Windows Terminal), some just write them out.

What has been the problem and why is the solution a problem

A User complained that in their use case (which is not described in detail, but can be in a non-terminal program that shows a status bar somewhere) the added wide symbols overlap with immediately following ordinary letters.

This is the case because obviously the program that shows the symbols and letters follows the advance width, which is the same for every symbol in a monospaced font. The bigger symbols extrude to the right more than the cursor will more, so this is expected.
This is the reason why applications that use the symbols typically add a blank after the symbol itself, to preserve monospace grid and prevent overlap. For example vim-devicons adds an ArtifactFix (i.e. blank ' ') when appropriate. Other programs like 'ls with filetype symbols' also add a blank space directly after a symbol that is to be displayed.

After That Commit the fonts are not monospaced anymore and terminal emulators do one of

  • reject the font outright (not monospaced = not usable)
  • rescale the too-big glyphs (effectively leaves the user with a badly rescaled version of Nerd Font Mono)
  • work in proportional mode (and thereby destroy the vertical alignment of tables that have different symbols with different widths on each line)

The original problem poster should maybe have changed that application to include the customary blank after symbols.
But then it looks like at least some problem commenter use the font in proportional mode like this
before_update
The numbers-and-letter part of the font in this example is proportional, and so the poster (rightfully?) expects proportional symbols. I assume the symbols are not patched in but come from the Symbols-Only font (as described in The Commit message 59c45ba.

Lets define some nomenclature:

  1. Nerd Font Mono: strictly monospaced font (i.e. all symbols fit into one 'space' (symbols scaled way down)
  2. Nerd Font Classic: monospaced font (all 'letters' monospaced, symbols have monospace advance, but extend to right)
  3. Nerd Font Proportional: monospaced letters, but proportional symbols (no negative bearings)

Nerd Fonts contained up to 59c45ba the fonts 1 and 2. Afterward the font-patch result is 1 and 3. This PR reverts / changes it back to 1 and 2. Typical use cases of Nerd Fonts expect the overwide symbols of 2 and handle them appropriately. Also 2 is useful in more terminal applications. People who want non-tiny symbols (i.e. not 1) need 2 or a proportional-font-allowed terminal to use 3.
Possibly the font 3 would also be nice to have (or at least allow users to self patch it with a command line option).

What are the relevant tickets (if any)?

The problem comes up in various guises, and the list is not complete.

Screenshots (if appropriate or helpful)

Font 1 example

Symbols scaled down to fit into one advance width (i.e. 1200 here), strictly monospaced, all glyphs have 1200 advance width.
Font taken from this repo, MASTER, patched-fonts/CascadiaCode/Regular/complete/Caskaydia\ Cove\ Regular\ Nerd\ Font\ Complete\ Mono.otf

image

Font 2 example

Symbols not scaled but changed advance width (i.e. 1200), monospaced, all glyphs have 1200 advance width, symbols have negative right bearing (extrude out to the right).
Font created from this repo, MASTER, fontforge patched-fonts/CascadiaCode/Regular/complete/Caskaydia\ Cove\ Regular\ Nerd\ Font\ Complete.otf patched-fonts/CascadiaCode/Regular/complete/Caskaydia\ Cove\ Regular\ Nerd\ Font\ Complete\ Mono.otf

image

Font 3 example

Symbols not scaled with original advance width (i.e. 2048 here), not strictly monospaced, all textual glyphs have 1200 advance width, symbols have any advance width.
Font taken from this repo, MASTER, patched-fonts/CascadiaCode/Regular/complete/Caskaydia\ Cove\ Regular\ Nerd\ Font\ Complete.otf

image

Edit: Make more clear what The Commit means and highlight the term as special

@Finii
Copy link
Collaborator Author

Finii commented Jan 14, 2022

Some additional thoughts:


vim-devicons adds an ArtifactFix

There is some back and forth about this, and at one point you (Ryan) say 'proper fix is with the actual font patcher'.
But is it? Given the variety of terminal emulators and how they handle the glyphs.


Maybe somehow related: #747 #748 (for some fonts)


#746 (comment) (on color changes?! 👀 )
#746 (comment) (@xsrvmy's take on font flavors)
#731 (comment) (my take on flavors)
#746 (comment) (my take on flavors 2)
#731 (comment) (adding space applications)
#731 (comment) (some font theory)
#731 (comment) (and the results explained)

@Finii
Copy link
Collaborator Author

Finii commented Jan 15, 2022

Here a visual representation of what the code does.

After this PR shown first

self.sourceFont.paste()
self.sourceFont[currentSourceFontGlyph].transform(align_matrix)
self.remove_glyph_neg_bearings(self.sourceFont[currentSourceFontGlyph])
self.set_glyph_width_mono(self.sourceFont[currentSourceFontGlyph])

Creates flavor 2

Wide Icon:
image

width advance left bear. right bear.
before 2048 2048 0 0
transformed 2048 1624 -424 0
de-bearinged 2048 2048 0 0
monospaced 2048 1200 0 -848

Small Icon:
image

width advance left bear. right bear.
before 496 2048 776 776
transformed 496 1624 352 776
de-bearinged 496 1624 352 776
monospaced 496 1200 352 352

And when we swap the last two lines, as done in 59c45ba

self.sourceFont.paste()
self.sourceFont[currentSourceFontGlyph].transform(align_matrix)
self.set_glyph_width_mono(self.sourceFont[currentSourceFontGlyph])
self.remove_glyph_neg_bearings(self.sourceFont[currentSourceFontGlyph])

Creates flavor 3

Wide Icon:

image

width advance left bear. right bear.
before 2048 2048 0 0
transformed 2048 1624 -424 0
monospaced 2048 1200 -424 -424
de-bearinged 2048 2048 0 0

Small Icon:

image

width advance left bear. right bear.
before 496 2048 776 776
transformed 496 1624 352 776
monospaced 496 1200 352 352
de-bearinged 496 1200 352 352

Comment

This is also to show that the code as it is does not really describe in a good way what is intended. because both set_glyph_width_mono() and remove_glyph_neg_bearings() change .width and .*bearings.
Implicitely, not in the code that one sees. But inside fontforge, because it HAS TO, becaue bearings + advance = width.
Maybe the code should be changed to be more specific about what it wants to achieve.

Appendix: Code

Code used to create the imagery above

diff --git a/font-patcher b/font-patcher
index 1c5c9693..991e33c1 100755
--- a/font-patcher
+++ b/font-patcher
@@ -809,12 +809,44 @@ class font_patcher:
                 if sym_attr['align'] == 'r':
                     x_align_distance += overlap_width
 
+            inserter = 0x5000 if currentSourceFontGlyph == 60036 else 0x5004
+            if currentSourceFontGlyph == 60036 or currentSourceFontGlyph == 58017:
+                print("\nA")
+                print(get_glyph_dimensions(self.sourceFont[currentSourceFontGlyph]))
+                print((self.sourceFont[currentSourceFontGlyph].width, self.sourceFont[currentSourceFontGlyph].left_side_bearing, self.sourceFont[currentSourceFontGlyph].right_side_bearing))
+                self.sourceFont.selection.select(currentSourceFontGlyph)
+                self.sourceFont.copy()
+                self.sourceFont.selection.select(inserter)
+                self.sourceFont.paste()
+                self.sourceFont[inserter].glyphname = "before"
+                inserter += 1
+
             align_matrix = psMat.translate(x_align_distance, y_align_distance)
             self.sourceFont[currentSourceFontGlyph].transform(align_matrix)
+            if currentSourceFontGlyph == 60036 or currentSourceFontGlyph == 58017:
+                print("A after transform")
+                print(get_glyph_dimensions(self.sourceFont[currentSourceFontGlyph]))
+                print((self.sourceFont[currentSourceFontGlyph].width, self.sourceFont[currentSourceFontGlyph].left_side_bearing, self.sourceFont[currentSourceFontGlyph].right_side_bearing))
+                self.sourceFont.selection.select(currentSourceFontGlyph)
+                self.sourceFont.copy()
+                self.sourceFont.selection.select(inserter)
+                self.sourceFont.paste()
+                self.sourceFont[inserter].glyphname = "transformed"
+                inserter += 1
 
             # Ensure after horizontal adjustments and centering that the glyph
             # does not overlap the bearings (edges)
             self.remove_glyph_neg_bearings(self.sourceFont[currentSourceFontGlyph])
+            if currentSourceFontGlyph == 60036 or currentSourceFontGlyph == 58017:
+                print("A after remove_glyph_neg_bearings")
+                print(get_glyph_dimensions(self.sourceFont[currentSourceFontGlyph]))
+                print((self.sourceFont[currentSourceFontGlyph].width, self.sourceFont[currentSourceFontGlyph].left_side_bearing, self.sourceFont[currentSourceFontGlyph].right_side_bearing))
+                self.sourceFont.selection.select(currentSourceFontGlyph)
+                self.sourceFont.copy()
+                self.sourceFont.selection.select(inserter)
+                self.sourceFont.paste()
+                self.sourceFont[inserter].glyphname = "de-bearinged"
+                inserter += 1
 
             # Needed for setting 'advance width' on each glyph so they do not overlap,
             # also ensures the font is considered monospaced on Windows by setting the
@@ -822,6 +854,16 @@ class font_patcher:
             # even the ones that are empty and didn't go through the scaling operations.
             # it should come after setting the glyph bearings
             self.set_glyph_width_mono(self.sourceFont[currentSourceFontGlyph])
+            if currentSourceFontGlyph == 60036 or currentSourceFontGlyph == 58017:
+                print("A after set_glyph_width_mono")
+                print(get_glyph_dimensions(self.sourceFont[currentSourceFontGlyph]))
+                print((self.sourceFont[currentSourceFontGlyph].width, self.sourceFont[currentSourceFontGlyph].left_side_bearing, self.sourceFont[currentSourceFontGlyph].right_side_bearing))
+                self.sourceFont.selection.select(currentSourceFontGlyph)
+                self.sourceFont.copy()
+                self.sourceFont.selection.select(inserter)
+                self.sourceFont.paste()
+                self.sourceFont[inserter].glyphname = "monospaced"
+                inserter += 1
 
         # end for
 

@necessary129
Copy link

Hi. Why is this not merged yet?

@Finii
Copy link
Collaborator Author

Finii commented Feb 3, 2022

After re-reading the description (topmost comment) it is a bit clumbsily formulated. Will fix that.

But most probably it is not merged because it has heavy implications that need time to evaluate.

AND a decision is needed which of the 3 font types we want to generate (flavor 1, 2, 3 ?)

Edit: Add 'AND' paragraph

Finii referenced this pull request Feb 7, 2022
[why]
For some powerline symbols we add a certain amount of overlap into the
previous or next character to cover up a small gap between the symbols
that otherwise can show up as ugly thin (usually colored) line.

But after we carefully design that glyph with a bit overlap (over-sized
and having negative bearings) we remove all bearings. That breaks of
course the glyph and no actual overlap on the left side happens.

[how]
Just do not remove negative bearings on overlap-enabled glyphs. As they
are rescaled in both directions anyhow all bearings are wanted and must
be kept.

Reported-by: Mihail Ivanchev <@MIvanchev>
Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
@Finii Finii mentioned this pull request Apr 25, 2022
4 tasks
@jirutka
Copy link

jirutka commented Jul 24, 2022

@Finii, can you please rebase this PR on top of the current master?

@Finii Finii force-pushed the bugfix/Make-NerdFonts-Monospaced-Again branch from 9b3caea to f7d1302 Compare July 25, 2022 11:22
@Finii
Copy link
Collaborator Author

Finii commented Jul 26, 2022

Rebased, force push.

@Finii
Copy link
Collaborator Author

Finii commented Sep 1, 2022

Rebase on master, force push.

Thanks to @saumyajyoti
#898 (comment) etc

Finii added a commit that referenced this pull request Sep 1, 2022
[why]
The commit
  59c45ba  Remove negative bearings on 2048-em glyphs
has been introduced to fix some problems with the symbols only font, at
least from the commit message.

That font is intended to be used in font-fallback situations, and so we
do not know the advance width of the current font anyhow. It does not
make sense to enforce an advance width with these.

[how]
Create the NerdFontsSymbolsOnly as if 59c45ba would be still in
effect, i.e. with variable advance width.

[note]
There have been a lot discussions about the reverted commit, some can be
found here:
* #900
* #764
* #731

Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
@Finii Finii force-pushed the bugfix/Make-NerdFonts-Monospaced-Again branch from 5045abe to c425a80 Compare September 1, 2022 07:05
Copy link

@saumyajyoti saumyajyoti left a comment

Choose a reason for hiding this comment

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

@Finii
Copy link
Collaborator Author

Finii commented Sep 1, 2022

Won't we need to update https://github.com/ryanoasis/nerd-fonts/blob/master/readme.md ?

Yes! I will add it.

This reverts commit 59c45ba.

[why]
The commit breaks the non-mono Nerd Fonts for a lot of people.
The issue was not very good documented and investigation can not be
seen.

For the TITLE it should have affected only the 2048-em Symbols only
font, but in fact all patched fonts were changed. Maybe that was
intended, maybe not.

[how]
This will make the advance width again equal for all glyphs.
This enables the use of the non-mono variant in more terminals.
For wider symbols a space is now (again) needed after the symbol.
That is expected by a lot applications.

[note]
See Pull Request 764 for more details.

See next commits for alternative solution for original problem.

Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
[why]
Theoretically we can produce 3 types on fonts:
* A: Strictly monospaced font
* B: Allow bigger symbols, but advance width still monospaced
* C: Allow bigger symbols, advance width will vary

All have their uses.

Historically Nerd Fonts produced A and B.
Then we had kind of a breaking change with 2.2.0 that it produces
now A and C.

The commit
  b9b7a5080  Revert "Remove negative bearings on 2048-em glyphs"
restores the old (pre 2.2.0) behavior. But the type C fonts can be
useful, so maybe we can have them as option?

[how]
Add commandline option to be able to create all fonts types:
* A: specify -s or --mono or --use-single-width-glyphs
* B: specify nothing
* C: specify --variable-width-glyphs

Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
[why]
The commit
  59c45ba  Remove negative bearings on 2048-em glyphs
has been introduced to fix some problems with the symbols only font, at
least from the commit message.

That font is intended to be used in font-fallback situations, and so we
do not know the advance width of the current font anyhow. It does not
make sense to enforce an advance width with these.

[how]
Create the NerdFontsSymbolsOnly as if 59c45ba would be still in
effect, i.e. with variable advance width.

[note]
There have been a lot discussions about the reverted commit, some can be
found here:
* #900
* #764
* #731

Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
Reported-by: Saumyajyoti Mukherjee <saumyajyoti>
Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
@Finii Finii force-pushed the bugfix/Make-NerdFonts-Monospaced-Again branch from c425a80 to f4d96f2 Compare September 1, 2022 10:46
@Finii Finii added this to the v2.3.0 milestone Sep 1, 2022
[why]
The params are half way handled as dict, but if unset it is an empty
string. That makes accessing it needlessly complicated.

[how]
With no functional change the params becomes now a dict, also when it
does not contain any particular information.

At the moment that seems not nessecary, as it can only contain one key:
'overlap'. We could also rename 'params' to 'overlap' and just store the
value.

But we keep the generic params dictionary as it might come in handy some
time in the far future when more parameters are added.

Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
[why]
For some powerline symbols we add a certain amount of overlap into the
previous or next character to cover up a small gap between the symbols
that otherwise can show up as ugly thin (usually colored) line.

But after we carefully design that glyph with a bit overlap (over-sized
and having negative bearings) we remove all bearings. That breaks of
course the glyph and no actual overlap on the left side happens.

[how]
Just do not remove negative bearings on overlap-enabled glyphs. As they
are rescaled in both directions anyhow all bearings are wanted and must
be kept.

Reported-by: Mihail Ivanchev <@MIvanchev>
Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
@Finii
Copy link
Collaborator Author

Finii commented Sep 6, 2022

Last 2 commits:
8f8776c
f67de4d

Incorporated part of #780 into this PR, because it is also needed to make the font correctly monospaced.

@Finii
Copy link
Collaborator Author

Finii commented Sep 6, 2022

Thanks to everyone who helped sorting out the actual problem(s) and solving this irritating issue :-)
As this is mainly a regression, it will go into master now instead into 2.3.0, I guess :-}

@Finii Finii merged commit 6564e71 into master Sep 6, 2022
@Finii Finii deleted the bugfix/Make-NerdFonts-Monospaced-Again branch September 6, 2022 13:22
Finii added a commit that referenced this pull request Sep 7, 2022
Another bugfix release to fix the regression introduced with 2.2.0
that is fixed by #764. Also note #900.

Some other smaller PRs also pulled.

Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
Finii added a commit that referenced this pull request Feb 1, 2023
[why]
Some fonts have invalid (or unset) Panose flags. When we create a "Nerd
Font Mono" font the Panose proportion is set to 'monospace'. This
make the font selectable in certain applications that need monospaced
fonts.

After #764 the "Nerd Font" variant shall (again) be detected as
monospaced font, but the glyphs have a big right side bearing (hang into
the next 'cell'). So we need to set the Panose bits there also.

[how]
We already have a check if the font is propably monospaced, independent
from Panose. This is used to prevent --mono patching on originally
proportional fonts.

If we find out with that check that the font is (most probably)
monospaced we also set the appropriate bits in Panose; unless Panose has
valid values that contradict that change.

Fixes: #1098

Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
Finii added a commit to b-/nerd-fonts that referenced this pull request Feb 17, 2023
[why]
Some fonts have invalid (or unset) Panose flags. When we create a "Nerd
Font Mono" font the Panose proportion is set to 'monospace'. This
make the font selectable in certain applications that need monospaced
fonts.

After ryanoasis#764 the "Nerd Font" variant shall (again) be detected as
monospaced font, but the glyphs have a big right side bearing (hang into
the next 'cell'). So we need to set the Panose bits there also.

[how]
We already have a check if the font is propably monospaced, independent
from Panose. This is used to prevent --mono patching on originally
proportional fonts.

If we find out with that check that the font is (most probably)
monospaced we also set the appropriate bits in Panose; unless Panose has
valid values that contradict that change.

Fixes: ryanoasis#1098

Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
LNKLEO pushed a commit to LNKLEO/Nerd that referenced this pull request Nov 24, 2023
…-Monospaced-Again

font-patcher: Make Nerd Fonts Monospaced Again
LNKLEO pushed a commit to LNKLEO/Nerd that referenced this pull request Nov 24, 2023
Another bugfix release to fix the regression introduced with 2.2.0
that is fixed by ryanoasis#764. Also note ryanoasis#900.

Some other smaller PRs also pulled.

Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
LNKLEO pushed a commit to LNKLEO/Nerd that referenced this pull request Nov 24, 2023
[why]
Some fonts have invalid (or unset) Panose flags. When we create a "Nerd
Font Mono" font the Panose proportion is set to 'monospace'. This
make the font selectable in certain applications that need monospaced
fonts.

After ryanoasis#764 the "Nerd Font" variant shall (again) be detected as
monospaced font, but the glyphs have a big right side bearing (hang into
the next 'cell'). So we need to set the Panose bits there also.

[how]
We already have a check if the font is propably monospaced, independent
from Panose. This is used to prevent --mono patching on originally
proportional fonts.

If we find out with that check that the font is (most probably)
monospaced we also set the appropriate bits in Panose; unless Panose has
valid values that contradict that change.

Fixes: ryanoasis#1098

Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
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.

4 participants