Please add <group> → <like>
This is a part of: #200
Preamble
The core feature provided by fontconfig is the ability to substitute fonts, when an exact match is not available. That requires tracking font family similarity. And, similarity is a two-way street.
However, the current similarity syntax is unidirectional, and subject to complex priorization rules. As a result, managing a big pool of fontconfig substitution rules, for example at the Linux distribution level, is a hard task.
The aim of the proposed syntax element is to create bi-directional links by default, with a clear priorization model that obviates the current requirement for fine-grained config file ordering.
Purpose
like
contains an ordered list of the font families which are similar to the family associated with the group
element.
Proposed syntax
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<!-- SPDX-License-Identifier: MIT -->
<fontconfig>
<group>
<target>@canonical_family_name@</target>
<like>
<family>@similar_family_name@</family>
<family>@generic_family_name@</family>
</like>
</group>
</fontconfig>
Behaviour
All the font families declared in a like
block, can be used as substitution fallbacks, when the files associated, directly or via merge
, with an group
target family, are insufficient to satisfy a query.
Unlike merge
, the font families listed in like
are not consumed by the rule and are still available as-is in font lists. Font families consumed by a merge
can not participate in a like
block.
The substitution rule is multi-directional:
- the font family associated with the
group
element, is also automatically declared as suitable substitute for all the families listed inlike
(reverse substitution) - all the families in
like
are also added as substitutes to one another (intra substitution)
Fontconfig should at least warn, when a like
block, contains families, that already have a substitution relationship, with a generic, that differs from the one used in the like
block.
Ordering
To avoid the problems induced by the current substitution syntax, like
comes with a strong ordering model. The substitution list for a specific font family is constructed the following way:
- all the non-generic families in its own
like
block, in their declaration order (merginggroup
blocks if several blocks for the same family exist) - all the non-generic families declared as suitable substitutes some other way, in declaration order (classical fontconfig substitution rules, reverse
group
substitution rules, intra-like
substitution rules, etc) - the generic font family its own
like
block (using multiple generics should be treated as an error, the whole point of generics is to force classification of fonts in distinct classes). - the first generic declared as suitable substitute for one of the other families, if no generic was declared in the family’s own
like
block
Rationale
The first consequence of this ordering model is that the group
element for a font family clearly owns the top of the substitution list for this font family, and everything else is just lower-priority additions.
There is no need to agonize how config files for different font families should be priorized.
There is no need to split rules for a specific font family in a high-priority config file with direct substitution rules, and a low priority file with indirect substitution rules.
Transition period
The second consequence of this ordering model, is that converting generics, to group
, is sufficient to take back control of default font ordering for a system, without needing to change anything else, and without losing the low priority additions provided by all the other fontconfig configuration files.
Past alternatives
The low-level elements of this proposal correspond to the classical:
- reverse generic substitution
<alias>
<family>@generic_family_name@</family>
<prefer>
<family>@canonical_family_name@</family>
</prefer>
</alias>
- direct generic substitution
<alias>
<family>@canonical_family_name@</family>
<default>
<family>@generic_family_name@</family>
</default>
</alias>
- direct substitution
<alias binding="same">
<family>>@canonical_family_name@</family>
<accept>
<family>@similar_family_name@</family>
</accept>
</alias>
- indirect substitution
<alias binding="same">
<family>@similar_family_name@</family>
<accept>
<family>@canonical_family_name@</family>
</accept>
</alias>
However, since this syntax forces separate declaration of all the substitution links, and separate declaration of every direction in those links, it is very easy for a human to lose track of all the declarations, even for a limited number of font families.
Moreover, the current declaration order logic of fontconfig, would require splitting those blocks in multiple configuration files, that correspond to the position of the link in each substitution chain. That is prohibitively complex, no one does that, that is one reason the substitution order is so suboptimal at a distribution level.
The new syntax proposal, just by making all substitution links multidirectional by default, and setting clear priorization rules, that depend on the intent of each configuration block, not on its position among a forest of other configuration files, makes substitution ordering manageable again.
Minimal real-world example
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<!-- SPDX-License-Identifier: MIT -->
<fontconfig>
<group>
<target>DejaVu Sans Mono</target>
<like>
<family>DejaVu LGC Sans Mono</family>
<family>Bitstream Vera Sans Mono</family>
<family>monospace</family>
</like>
</group>
</fontconfig>