How to match multiple font names with a single element

fontconfig

If I want Arial when Helvetica or DejaVu Sans are requested, this configuration works fine:

<match target="pattern">
  <test name="family"><string>Helvetica</string></test>
  <edit binding="strong" mode="prepend" name="family">
    <string>Arial</string>
  </edit>
</match>

<match target="pattern">
  <test name="family"><string>DejaVu Sans</string></test>
  <edit binding="strong" mode="prepend" name="family">
    <string>Arial</string>
  </edit>
</match>

A quick test:

$ echo Terminus Helvetica | xargs -n1 fc-match
ter-x12n.pcf.gz: "Terminus" "Regular"
arial.ttf: "Arial" "Regular"

But it's way too verbose. The fontconfig manual mentions <or> element, but it lacks examples of how to use it properly. I've tried to search github
https://github.com/search?q=match+test+or+extension%3Aconf, which returns a bunch of examples very similar to:

<match target="font">
  <or>
    <test name="family"><string>Nasu</string></test>
    <test name="family"><string>NasuM</string></test>
  </or>
  <edit name="autohint"><bool>false</bool></edit>
</match>

Unfortunately, if I naively rewrite my configuration to

<match target="pattern">
  <or>
    <test name="family"><string>Helvetica</string></test>
    <test name="family"><string>DejaVu Sans</string></test>
  </or>
  <edit binding="strong" mode="prepend" name="family">
    <string>Arial</string>
  </edit>
</match>

that breaks all the matchings:

$ echo Terminus Helvetica | xargs -n1 fc-match
arial.ttf: "Arial" "Regular"
arial.ttf: "Arial" "Regular"

Best Answer

Unfortunately, there is no better solution than repeating the whole <match> element for every family name you want to match. This is documented in the fonts-conf(5) man page:

   <--
        The example of the requirements of OR operator;
        If the 'family' contains 'Courier New' OR 'Courier'
        add 'monospace' as the alternative
   -->
   <match target="pattern">
        <test name="family" compare="eq">
             <string>Courier New</string>
        </test>
        <edit name="family" mode="prepend">
             <string>monospace</string>
        </edit>
   </match>
   <match target="pattern">
        <test name="family" compare="eq">
             <string>Courier</string>
        </test>
        <edit name="family" mode="prepend">
             <string>monospace</string>
        </edit>
   </match>

Look at fontconfig bug #33644, where someone complained that the rule with multiple <string> elements inside the <test> element did not work as expected. That rule was very similar to your rule, which would look like this:

<match target="pattern"> <test name="family"> <string>Helvetica</string> <string>DejaVu Sans</string> </test> <edit binding="strong" mode="prepend" name="family"> <string>Arial</string> </edit> </match>

The problem with such rules is that if multiple values mentioned in the rule are present in the pattern, the rule would match the value which was specified earlier in the rule, even if the corresponding value appears after other matching values in the pattern; then mode="prepend" may not have the desired effect. For your example:

  • if the pattern contains “Helvetica, DejaVu Sans”, the rule would match “Helvetica” and result in “Arial, Helvetica, DejaVu Sans”;
  • if the pattern contains “DejaVu Sans, Helvetica”, the rule would still match “Helvetica” and result in “DejaVu Sans, Arial, Helvetica”, therefore Arial will not be selected as desired.

If you use separate rules, both of them will be applied in both cases, and you will get either “Arial, Helvetica, Arial, DejaVu Sans” or “Arial, DejaVu Sans, Arial, Helvetica”, therefore Arial would be selected in both cases.

In response to that bug report fontconfig was changed to print warnings when <test> elements with multiple values are found in the configuration, therefore you should not use this feature even in cases where it would work properly.

As for the <or> element, it works only in expressions, takes two or more boolean values and returns a boolean value, so it is not suited for your task.

Related Question