Please add <group>
Preamble
The fontconfig configuration language was intended as a way to perform declarative configuration. It has since morphed in a complete low-level xml programming language.
The result does not scale to human capabilities or to the level of complexity of current font projects. As a result, humans and apps are giving up on fontconfig as a way to manage their font files, and are reverting to manual assemblage based on human interpretation of font names.
Please add the group
top-level configuration element, to help assembling simply and safely modern complex font families.
Purpose
The group
element is intended to be complete enough a single group
declaration is sufficient to describe:
- how to assemble a font family
- and how this family relates with other font families,
- in a syntax as compact and simple as possible
The main objective is to eradicate the maintenance problems created by the current spread of low-level declaration blocks, over multiple files, that are never properly prioritized and synchronized.
It has the same objective as ISO 14496-28 in issue #33, in fonctonfig-friendly syntax. Since issue #33 was requested by Google for Noto, the interest in such a construct is shared by distributions, foundries and app downstreams.
Exemples
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<!-- SPDX-License-Identifier: MIT -->
<fontconfig>
<group>
<target>Tribun ADF Std</target>
<like>
<family>serif</family>
</like>
</group>
</fontconfig>
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<!-- SPDX-License-Identifier: MIT -->
<fontconfig>
<group>
<generic>serif</generic>
<like>
<family>DejaVu Serif</family>
<family>Noto Serif</family>
</like>
</group>
</fontconfig>
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<!-- SPDX-License-Identifier: MIT -->
<fontconfig>
<group>
<target>GFS Didot</target>
<fullname>GFS Didot Bold Italic</fullname>
<fullname>GFS Didot Bold</fullname>
<fullname>GFS Didot Italic</fullname>
<fullname>GFS Didot Regular</fullname>
<like>
<family>GFS Didot Classic</family>
<family>serif</family>
</like>
</group>
</fontconfig>
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<!-- SPDX-License-Identifier: MIT -->
<fontconfig>
<group>
<target>DejaVu Serif</target>
<match>
<family>DejaVu Serif</family>
<change>
<style>Book</style>
<into>Regular</into>
</change>
</match>
<family>DejaVu Math TeX Gyre</family>
<like>
<family>Bitstream Prima Serif</family>
<family>BPG 2017 DejaVu Serif</family>
<family>DejaVu LGC Serif</family>
<family>Verajja Serif</family>
<family>Olwen Serif</family>
<family>SUSE Serif</family>
<family>Bitstream Vera Serif</family>
<family>serif</family>
</like>
</group>
</fontconfig>
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<!-- SPDX-License-Identifier: MIT -->
<fontconfig>
<group>
<target>Droid Sans</target>
<match>
<family>Droid Sans Hebrew</family>
<good-lang>he</good-lang>
<good-lang>yi</good-lang>
</match>
<match>
<family>Droid Sans Armenian</family>
<good-lang>hy</good-lang>
</match>
<match>
<family>Droid Sans Georgian</family>
<good-lang>ka</good-lang>
</match>
<match>
<family>Droid Sans Ethiopic</family>
<good-lang>am</good-lang>
<good-lang>byn</good-lang>
<good-lang>gez</good-lang>
<good-lang>sid</good-lang>
<good-lang>ti-er</good-lang>
<good-lang>ti-et</good-lang>
<good-lang>tig</good-lang>
<good-lang>wal</good-lang>
</match>
<match>
<family>Droid Sans Devanagari</family>
<good-lang>hi</good-lang>
<good-lang>mr</good-lang>
<good-lang>kok</good-lang>
<good-lang>ks@devanagari</good-lang>
<good-lang>sd@devanagari</good-lang>
<good-lang>mai</good-lang>
<good-lang>ne</good-lang>
<good-lang>bh</good-lang>
<good-lang>bho</good-lang>
<good-lang>brx</good-lang>
<good-lang>doi</good-lang>
<good-lang>hne</good-lang>
<good-lang>sa</good-lang>
<good-lang>sat</good-lang>
</match>
<match>
<family>Droid Sans Tamil</family>
<good-lang>ta</good-lang>
</match>
<match>
<family>Droid Sans Thai</family>
<good-lang>th</good-lang>
</match>
<match>
<family>Droid Arabic Kufi</family>
<good-lang>ar</good-lang>
</match>
<match>
<family>Droid Sans Japanese</family>
<good-lang>ja</good-lang>
</match>
<match>
<family>Droid Sans Fallback</family>
</match>
<like>
<family>Droid Sans Arabic</family>
<family>Noto Sans</family>
<family>sans-serif</family>
</like>
</group>
</fontconfig>
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<!-- SPDX-License-Identifier: MIT -->
<fontconfig>
<group>
<target>IBM Plex Sans</target>
<match>
<family>IBM Plex Sans Condensed</family>
<style-prefix>Condensed</style-prefix>
</match>
<family>IBM Plex Sans Var</family>
<match>
<family>IBM Plex Sans Arabic</family>
<good-lang>ar</good-lang>
</match>
<match>
<family>IBM Plex Sans Hebrew</family>
<good-lang>he</good-lang>
<good-lang>yi</good-lang>
</match>
<match>
<family>IBM Plex Sans Thai</family>
<good-lang>th</good-lang>
</match>
<match>
<family>IBM Plex Sans Devanagari</family>
<good-lang>hi</good-lang>
<good-lang>mr</good-lang>
<good-lang>kok</good-lang>
<good-lang>ks@devanagari</good-lang>
<good-lang>sd@devanagari</good-lang>
<good-lang>mai</good-lang>
<good-lang>ne</good-lang>
<good-lang>bh</good-lang>
<good-lang>bho</good-lang>
<good-lang>brx</good-lang>
<good-lang>doi</good-lang>
<good-lang>hne</good-lang>
<good-lang>sa</good-lang>
<good-lang>sat</good-lang>
</match>
<axis>
<name>width</name>
<step>
<name>Condensed</name>
<value>85</value>
</step>
<step>
<name>Regular</name>
<value>100</value>
</step>
</axis>
<axis>
<name>weight</name>
<step>
<name>Thin</name>
<value>0</value>
</step>
<step>
<name>ExtraLight</name>
<value>40</value>
</step>
<step>
<name>Light</name>
<value>50</value>
</step>
<step>
<name>Regular</name>
<value>80</value>
</step>
<step>
<name>Text</name>
<value>90</value>
</step>
<step>
<name>Medium</name>
<value>100</value>
</step>
<step>
<name>SemiBold</name>
<value>180</value>
</step>
<step>
<name>Bold</name>
<value>200</value>
</step>
</axis>
<axis>
<name>slant</name>
<step>
<name>Regular</name>
<value>0</value>
</step>
<step>
<name>Italic</name>
<value>100</value>
</step>
</axis>
<like>
<family>IBM Plex Sans Thai Looped</family>
<family>sans-serif</family>
</like>
</group>
</fontconfig>
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<!-- SPDX-License-Identifier: MIT -->
<fontconfig>
<group>
<target>PT Sans</target>
<match>
<family>PT Sans</family>
<good-lang>ru</good-lang>
</match>
<match>
<family>PT Sans Narrow</family>
<style-prefix>Narrow</style-prefix>
<good-lang>ru</good-lang>
</match>
<axis>
<name>optical size</name>
<step>
<name>Caption</name>
<value>8</name>
<match>
<family>PT Sans Caption</family>
<good-lang>ru</good-lang>
</match>
</step>
</axis>
<like>
<family>sans-serif</family>
</like>
</group>
</fontconfig>
YAML examples
Since this is a new autonomous top-level element, it might be smarter to skip the XML step and define it directly in YAML. That would be easier on humans, and easier to integrate (for example, in Ansible-driven tests).
Here are the same examples in YAML syntax
---
# SPDX-License-Identifier: MIT
- family: Tribun ADF Std:
- like:
- serif
...
---
# SPDX-License-Identifier: MIT
- serif:
- generic
- like:
- DejaVu Serif
- Noto Serif
...
---
# SPDX-License-Identifier: MIT
- GFS Didot:
- fullname: GFS Didot Bold Italic
- fullname: GFS Didot Bold
- fullname: GFS Didot Italic
- fullname: GFS Didot Regular
- like:
- GFS Didot Classic
- serif
...
---
# SPDX-License-Identifier: MIT
- DejaVu Serif:
- family: DejaVu Serif
new-styles: {Book: Regular}
- family: DejaVu Math TeX Gyre
- like:
- Prima Serif
- BPG 2017 DejaVu Serif
- DejaVu LGC Serif
- Verajja Serif
- Olwen Serif
- SUSE Serif
- Bitstream Vera Serif
- serif
...
---
# SPDX-License-Identifier: MIT
---
- Droid Sans:
- family: Droid Sans Hebrew
good-langs: [he, yi]
- family: Droid Sans Armenian
good-langs: [hy]
- family: Droid Sans Georgian
good-langs: [ka]
- family: Droid Sans Ethiopic
good-langs: [am, byn, gez, sid, ti-er, ti-et, tig, wal]
- family: Droid Sans Devanagari
good-langs: [hi, mr, kok, ks@devanagari, sd@devanagari, mai, ne, bh, bho,
brx, doi, hne, sa, sat]
- family: Droid Sans Tamil
good-langs: [ta]
- family: Droid Sans Thai
good-langs: [th]
- family: Droid Arabic Kufi
good-langs: [ar]
- family: Droid Sans Japanese
good-langs: [ja]
- family: Droid Sans Fallback
- like:
- Droid Sans Arabic
- Noto Sans
- sans-serif
...
---
# SPDX-License-Identifier: MIT
- IBM Plex Sans:
- family: IBM Plex Sans Condensed
style-prefix: Condensed
- family: IBM Plex Sans Var
- family: IBM Plex Sans Arabic
good-langs: [ar]
- family: IBM Plex Sans Hebrew
good-langs: [he, yi]
- family: IBM Plex Sans Thai
good-langs: [th]
- family: IBM Plex Sans Devanagari
good-langs: [hi, mr, kok, ks@devanagari, sd@devanagari, mai, ne, bh, bho,
brx, doi, hne, sa, sat]
- axes:
- width: {Condensed: 85, Regular: 100}
- weight: {Thin: 0, ExtraLight: 40, Light: 50, Regular: 80, Text: 90,
Medium: 100, SemiBold: 180, Bold: 200 }
- slant: {Regular: 0, Italic: 100}
- like:
- IBM Plex Sans Thai Looped
- sans-serif
...
---
# SPDX-License-Identifier: MIT
- PT Sans:
- family: PT Sans
good-langs: [ru]
- family: PT Sans Narrow
style-prefix: Narrow
good-langs: [ru]
- axes:
- optical size:
- Caption:
- 8
- family: PT Sans Caption
good-langs: [ru]
- like:
- sans-serif
...
Complete Grammar
grammar {
start = Fontconfig
Fontconfig = element fontconfig { Group* }
Group = element group {
(Target | Generic) &
Family* & Fullname* & Match* &
Axis* &
Like?
}
Target = element target { text }
Generic = element generic { text }
Family = element family { text }
FullName = element fullname { text }
Match = element match { MatchContent }
MatchContent = {
(Fullname | Family | Style | Styles)+ &
(Change | StylePrefix)? &
GoodLang* &
BadLang*
}
Style = element style { text }
Styles = element styles { Style+ }
Change = element change { Style, Into }
Into = element into { text }
StylePrefix = element style-prefix { text }
GoodLang = element good-lang { text }
BadLang = element bad-lang { text }
Axis = element axis { AxisContent }
AxisContent = {
(Name | Tag) &
Step+
}
Name = element name { text }
Tag = element tag { text }
Step = element step { StepContent }
StepContent = {
Name &
Value? &
Family* & FullName* & Match*
}
Value = element value { text }
Like = element like { Family* }
}