In migrating the font configuration mechanism out of Xft and into a separate library, I have an opportunity to redefine the configuration language to make it easier for external agents to parse and modify.
I've decided to give XML a try and see how it looks; other applications have started migrating to this for configuration and it has a lot of appeal. Please find enclosed the proposed DTD and a sample font configuration file. They have only been run through xmllint --valid; I have not started on any related C code. For those of you with more XML experience than I (that shouldn't be hard, I just printed the XML spec this morning), please let me know if I'm doing things in silly or unconventional ways. Also, please let me know if using libxml is a bad idea; it seems a bit easier to use than expat. If this transition goes well, I may propose that the XFree86 server configuration file be transitioned to XML as well; the ability to have external agents edit the file without needing a complete understanding of the syntax should be very useful. Keith Packard XFree86 Core Team Compaq Cambridge Research Lab
<!-- This is the Document Type Definition for font configuration files --> <!ELEMENT fontconfig (dir | cache | include | include_if | match | alias)* > <!-- Add a directory that provides fonts --> <!ELEMENT dir EMPTY> <!ATTLIST dir dir CDATA #REQUIRED> <!-- Define the per-user file that holds cache font information. If the filename begins with '~', it is replaced with the users home directory path. --> <!ELEMENT cache EMPTY> <!ATTLIST cache file CDATA #REQUIRED> <!-- Reference another configuration file; note that this is another complete font configuration file and not just a file included by the XML parser. Set 'ignore_missing' to 'yes' if errors are to be ignored. If the filename begins with '~', it is replaced with the users home directory path. --> <!ELEMENT include EMPTY> <!ATTLIST include ignore_missing (no|yes) "no" file CDATA #REQUIRED> <!-- Aliases are just a special case for multiple match elements They are syntactically equivalent to: <match> <test name="family"> <string value=[family]/> </test> <edit name="family" mode="prepend"> <string value=[prefer]/> ... </edit> <edit name="family" mode="append"> <string value=[accept]/> ... </edit> <edit name="family" mode="append_last"> <string value=[default]/> ... </edit> </match> --> <!ELEMENT alias (prefer?, accept?, default?)> <!ATTLIST alias family CDATA #REQUIRED> <!ELEMENT prefer (family)*> <!ELEMENT accept (family)*> <!ELEMENT default (family)*> <!ELEMENT family EMPTY> <!ATTLIST family family CDATA #REQUIRED> <!ENTITY % expr 'number|string|name|matrix|boolean_or|boolean_and|eq|not_eq|less|less_eq|more|more_eq|plus|minus|times|divide|not|if'> <!-- Match and edit patterns. If 'target' is 'pattern', execute the match before selecting a font. if 'target' is 'font', execute the match on the result of a font selection. --> <!ELEMENT match (test*, edit*)> <!ATTLIST match target (pattern|font) "pattern"> <!-- Match a field in a pattern if 'qual' is 'any', then the match succeeds if any value in the field matches. if 'qual' is 'all', then the match succeeds only if all values match. --> <!ELEMENT test (%expr;)> <!ATTLIST test qual (any|all) "any" name CDATA #REQUIRED compare (eq|not_eq|less|less_eq|more|more_eq) "eq"> <!-- Edit a field in a pattern The enclosed values are used together to edit the list of values associated with 'name'. If 'name' matches one of those used in a test element for this match element: if 'mode' is 'assign', replace the matched value. if 'mode' is 'assign_replace', replace all of the values if 'mode' is 'prepend', insert before the matched value if 'mode' is 'append', insert after the matched value if 'mode' is 'prepend_first', insert before all of the values if 'mode' is 'append_last', insert after all of the values If 'name' doesn't match any of those used in a test element: if 'mode' is 'assign' or 'assign_replace, replace all of the values if 'mode' is 'prepend' or 'prepend_first', insert before all of the values if 'mode' is 'append' or 'append_last', insert after all of the values --> <!ELEMENT edit (%expr;)*> <!ATTLIST edit name CDATA #REQUIRED mode (assign|assign_replace|prepend|append|prepend_first|append_last) "assign"> <!-- Elements of expressions follow --> <!ELEMENT number EMPTY> <!ATTLIST number value CDATA #REQUIRED> <!ELEMENT string EMPTY> <!ATTLIST string value CDATA #REQUIRED> <!ELEMENT name EMPTY> <!ATTLIST name name CDATA #REQUIRED> <!ELEMENT matrix (number,number,number,number)> <!ELEMENT boolean_or (%expr;)*> <!ELEMENT boolean_and (%expr;)*> <!ELEMENT eq (%expr;)*> <!ELEMENT not_eq (%expr;)*> <!ELEMENT less (%expr;)*> <!ELEMENT less_eq (%expr;)*> <!ELEMENT more (%expr;)*> <!ELEMENT more_eq (%expr;)*> <!ELEMENT plus (%expr;)*> <!ELEMENT minus (%expr;)*> <!ELEMENT times (%expr;)*> <!ELEMENT divide (%expr;)*> <!ELEMENT not (%expr;)> <!ELEMENT if ((%expr;), (%expr;), (%expr;)?)>
<?xml version="1.0"?> <!DOCTYPE fontconfig SYSTEM "fonts.dtd"> <!-- /etc/fonts.conf file to configure system font access --> <fontconfig> <!-- Find true type fonts in this directory --> <dir dir="/usr/X11R6/lib/X11/fonts/truetype"/> <!-- Accept deprecated 'mono' alias, replacing it with 'monospace' --> <match target="pattern"> <test qual="any" name="family"><string value="mono"/></test> <edit name="family" mode="assign"><string value="monospace"/></edit> </match> <!-- Load per-user customization file --> <include ignore_missing="yes" file="~/.fonts.conf"/> <!-- Alias well known font names to available TrueType fonts --> <alias family="Times"> <prefer><family family="Times New Roman"/></prefer> <default><family family="serif"/></default> </alias> <alias family="Helvetica"> <prefer><family family="Verdana"/></prefer> <default><family family="sans"/></default> </alias> <alias family="Courier"> <prefer><family family="Courier New"/></prefer> <default><family family="monospace"/></default> </alias> <!-- Provide required aliases for standard names --> <alias family="serif"> <prefer><family family="Times New Roman"/></prefer> </alias> <alias family="sans"> <prefer><family family="Verdana"/></prefer> </alias> <alias family="monospace"> <prefer><family family="Andale Mono"/></prefer> </alias> </fontconfig>