Skip to content

Instantly share code, notes, and snippets.

@ernstki
Last active April 30, 2024 10:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ernstki/2ae279ad89888e4099c9852bf0ba5d11 to your computer and use it in GitHub Desktop.
Save ernstki/2ae279ad89888e4099c9852bf0ba5d11 to your computer and use it in GitHub Desktop.
Easily disable fonts that aren't in your language, for Debian and derivatives (and possibly other Linux distros)
#!/usr/bin/env perl
##
## Usage: fc-reject.pl > ~/.config/fontconfig/conf.d/88-reject.conf
## Author: Kevin Ernst <ernstki -at- mail.uc.edu>
## License: MIT or CC-BY-SA-4.0, at your option
## Source: https://gist.github.com/ernstki/2ae279ad89888e4099c9852bf0ba5d11
##
use v5.12;
use warnings;
use autodie;
my $mylang = 'en'; # use '(en|lang1|lang2)' for other languages
my $rejectfam = 'Noto'; # use '(Noto|Other Family|Another Family)' for others
my $rejectlist = {};
open my $fh, '-|', 'fc-cat';
while (<$fh>) {
if (/.* "([^:,]+).*:lang=([^:]*):.*/) {
my ($name, $lang) = ($1, $2);
if ($name =~ /$rejectfam/ and (!$lang or $lang !~ /$mylang/)) {
$rejectlist->{$name} = 1;
}
}
}
close $fh;
print <<EOF;
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<selectfont>
<rejectfont>
EOF
foreach my $name (keys %$rejectlist) {
print <<EOF;
<pattern>
<patelt name="family">
<string>$name</string>
</patelt>
</pattern>
EOF
}
print <<EOF;
</rejectfont>
</selectfont>
</fontconfig>
EOF
@ernstki
Copy link
Author

ernstki commented Sep 14, 2023

Noto is an impressive accomplishment, no doubt. And it's a noble thing to want to enable all the people of the world to see their native writing systems accurately represented on their computers, by default, out of the box.

Sadly, the technical implementation (at least for Debian-derived Linux distros) leaves a lot to be desired, because of the hundreds of separate fonts that comprise Noto. These clog up font menus and make selecting from the fonts for the languages you actually use more of a chore.

This script allows you to selectively disable fonts that don't support your language(s), but easily re-enable them if necessary. While this is technically possible with point-and-click interfaces like Font Manager, it can take hundreds of clicks to get them all.1

It's a much better solution than what is commonly recommended, which is to remove a bunch of font packages, upon which other packages often depend. This can result in a broken system, or at least accidental removal of some applications that you didn't intend to remove.

Installation

GIST='https://gist.githubusercontent.com/ernstki/2ae279ad89888e4099c9852bf0ba5d11/raw/fc-reject.pl'
curl -Lo fc-reject.pl $GIST || wget -O fc-reject.pl $GIST
mkdir -p ~/.config/fontconfig/conf.d
perl fc-reject.pl > ~/.config/fontconfig/conf.d/88-reject.conf

A warning message of

Fontconfig error: "/home/YOU/.config/fontconfig/conf.d/88-reject.conf", line 1: no element found

appears to be harmless.2 It's probably due to fontconfig reading the file the instant it's created, before it has any content.

This will hide any fonts in the $rejectfam regular expression which don't specify $mylang (also a regexp) as one of their supported languages. For me that was 120 fonts that I don't have to scroll through anymore, in janky font selection drop-downs like GIMP's or LibreOffice's—quite a relief!

If you wish to name the file something else besides 88-reject.conf, just make sure it ends in .conf and begins with two digits. This is a requirement for fontconfig to find it.

Testing if it worked

You can see whether or not it worked by grepping the output of fc-list for fonts that you're sure should be excluded, like Linear A.

fc-list | grep 'Linear A'

There should be no output, or at least no Noto fonts in the output.

There may be rare cases (which I haven't encountered) where you need to regenerate fontconfig's caches from scratch. If you run into that situation

fc-cache -rv

should do the trick for that.

Undoing / disabling what the script does

To undo what the script has done simply remove the .conf file. Changes take effect immediately. Some programs may need to be restarted, though.

You can momentarily disable the config (re-enabling all the excluded fonts) by renaming it to not have a .conf extension, like so:

# e.g., for the Bash shell
mv ~/.config/fontconfig/conf.d/88-reject.conf{,.disabled}

Incidentally, visiting the Wikipedia article for Linear A is an effective test to see if Noto is doing its job. That is, if the font is properly installed, you should see "no tofu" (no Unicode replacement symbols) in that article.

See also

Footnotes

  1. …at least with the version of Font Manager that's in the LTS repositories as of this writing

  2. If it really bothers you, call the file whatever.conf.new and then move it to whatever.conf as a separate step.

@wdoekes
Copy link

wdoekes commented Apr 30, 2024

On Ubuntu 22.04 you might want to re-enable the emoji by removing Noto Color Emoji from the output. Otherwise you won't even see the 👍 in GitHub.

And maybe Noto Music and Noto Sans Symbols and Noto Sans Symbols2 -- although I'm not sure how often you run into those.

As for the harmless warning, I suggest:

fc-reject.pl > ~/.config/fontconfig/conf.d/88-reject.conf.new &&
  mv ~/.config/fontconfig/conf.d/88-reject.conf{.new,}

The atomic mv ensures that only the complete file is read.

Thanks for this gist, @ernstki !

My changes:

--- fc-reject.pl	2024-04-30 12:15:16.573580587 +0200
+++ /usr/local/bin/remove-noto-fonts	2024-04-30 12:18:46.764030790 +0200
@@ -1,6 +1,6 @@
 #!/usr/bin/env perl
 ##
-##  Usage:    fc-reject.pl > ~/.config/fontconfig/conf.d/88-reject.conf
+##  Usage:    fc-reject.pl > ~/.config/fontconfig/conf.d/88-reject.conf.new && mv ~/.config/fontconfig/conf.d/88-reject.conf{.new,}
 ##  Author:   Kevin Ernst <ernstki -at- mail.uc.edu>
 ##  License:  MIT or CC-BY-SA-4.0, at your option
 ##  Source:   https://gist.github.com/ernstki/2ae279ad89888e4099c9852bf0ba5d11
@@ -9,17 +9,30 @@ use v5.12;
 use warnings;
 use autodie;
 
-my $mylang = 'en';       # use '(en|lang1|lang2)' for other languages
-my $rejectfam = 'Noto';  # use '(Noto|Other Family|Another Family)' for others
+my $mylang = 'en|nl|und-zsye';  # use 'en|lang1|lang2' for other languages
+                                # 'und-zsye' is for Emoji
+my $rejectfam = '^Noto';        # use '^(Noto|Other Family|Another Family)' for others
+my $keepfam = '^Noto Music';	# use '^(Noto Music|Others to keep)' for others
 my $rejectlist = {};
 
 open my $fh, '-|', 'fc-cat';
 while (<$fh>) {
-    if (/.* "([^:,]+).*:lang=([^:]*):.*/) {
-        my ($name, $lang) = ($1, $2);
-        if ($name =~ /$rejectfam/ and (!$lang or $lang !~ /$mylang/)) {
-            $rejectlist->{$name} = 1;
-        }
+    if (/.* "([^:,]+).*:lang=([^:]*)/) {
+        my ($name, @langs) = ($1, split(/\|/, $2));
+        if ($name =~ /$rejectfam/) {
+	    my $keep = 0;
+	    foreach my $lang (@langs) {
+		if ($lang =~ /^($mylang)$/) {
+		    $keep = 1;
+		}
+	    }
+	    if ($name =~ /$keepfam/) {
+		$keep = 1;
+	    }
+	    if ($keep == 0) {
+		$rejectlist->{$name} = 1;
+	    }
+	}
     }
 }
 close $fh;
@@ -32,7 +45,7 @@ print <<EOF;
     <rejectfont>
 EOF
 
-foreach my $name (keys %$rejectlist) {
+foreach my $name (sort keys %$rejectlist) {
     print <<EOF;
       <pattern>
         <patelt name="family">

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment