10

I recently figured out how to use fontconfig on Linux to set system default fonts for serif, sans-serif and monospaced fonts; basically, you save an XML configuration file to ~/.config/fontconfig/fonts.conf with the following content:

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>

<match>
  <test qual="any" name="family"><string>serif</string></test>
  <edit name="family" binding="strong" mode="prepend_first">
    <string>Gentium</string>
    <string>Sun-ExtA</string>
    <string>HanaMinA</string>
    <string>HanaMinB</string>
    </edit>
  </match>

</fontconfig>

The binding="strong" mode="prepend_first" attributes ensure that the matching rules take precedence over other settings, and the sequence of font names ensure that where a font doesn't contain a given code point / character, the next font in the list is tried (this list applies top-to-bottom; IMHO it should really be a later-binds-stronger logic, but whatever).

The great thing about this configuration is that it works in text editors and terminal emulators alike.

However, there's still a nag: there are many cases where a given font does contain a given glyph, but another font would be preferrable for that codepoint; for example, Sun-ExtA is a great default font for CJK characters, but it also covers lots and lots of non-CJK characters and has a few problematic glyphs.

Suppose I don't like the appearance of 〇 U+3007 IDEOGRAPHIC NUMBER ZERO in Sun-ExtA and would rather use HanaMinA for it, how could I do that with fontconfig? Obviously I can't just prioritize the entry for HanaMinA over Sun-ExtA, as that would affect all of the glyphs that are contained in both fonts.

My hunch is that there should be a solution involving elements <charset> (according to the fontconfig user documentation, "This element holds at least one element of an Unicode code point or more") and/or <range> ("This element holds the two elements of a range representation"—presumably to denote a range of Unicode code points). I couldn't find a single example how to use these elements, though.

Is it possible to configure fontconfig to use a specific font for a single Unicode code point or a range of codepoints?

John Frazer
  • 690
  • 8
  • 14

2 Answers2

3

Inside a scan pattern, including a <minus> element in your <edit> tag allows you to subtract from the charset.

This was mostly designed for removing "bad" or buggy characters from a font, but going further, you can write a <test> that matches every font besides the one you want to use:

<match target="scan">
  <test name="family" compare="not_eq">
    <string>VL Gothic</string>
  </test>
  <edit name="charset" mode="assign">
    <minus>
      <name>charset</name>
      <range>
        <int>0x0021</int>
        <int>0x00FF</int>
      </range>
    </minus>
  </edit>
</match>

A similar configuration can also be used to remove entire langs from a font.

As far as I know this wasn't really documented anywhere before now, I found out about it from a redhat bug

Miss Blit
  • 436
  • 4
  • 10
  • 1
    First of all, this answer contains an error: fontconfig complains about missing "charset" around "range". And, unfortunately, it seems to either not work with fontconfig 2.13 or interfere with something else. All the applications (including browser and terminal emulator) still renders "removed" chars with font where they should be removed from :( – Vadim A. Misbakh-Soloviov Apr 15 '18 at 14:08
  • 1
    This snippet works for me, however the best I can get is a rectangle for the removed characters. Has anyone actually been able to make fontconfig fall back to another font for those? – VPhantom Jan 23 '19 at 18:49
  • 1
    For me, a similar case does not work with fontconfig 2.13.1 without adding `` tags around the `` section, i.e. `0x00210x00FF`, (Multiple ranges can also be used.) – gnucchi Feb 24 '19 at 21:44
  • I had to use `target="font"` instead of `"scan"` to make this work. – nialv7 Oct 06 '20 at 21:07
1

You can promote fonts for specific locales in fontconfig using:

   <match>
    <test name="lang">
      <string>[RFC-3066 language code]</string>
    </test>
    <test name="family">
      <string>[genericname]</string>
    </test>
    <edit name="family" mode="prepend">
      <string>[fontname]</string>
    </edit>
  </match>
   <alias>
    <family>[fontname]</family>
     <default>
      <family>[genericname]</family>
     </default>
   </alias>

Careful use of fontconfig priorities is required so the font is promoted before those you don't want and after common latin/greek/cyrillic fonts (since CJK latin glyphs tend to be horrible).

Of course, that supposes your software environment is able to signal fontconfig when you read/write in a locale that needs this override.

nim
  • 2,041
  • 10
  • 13