conf.d/48-guessfamily.conf | 188 ++++++++++++++ conf.d/49-sansserif.conf | 96 ++++++- conf.d/Makefile.am | 1 conf.d/meson.build | 1 configure.ac | 2 doc/fontconfig-devel.sgml | 2 doc/fontconfig-user.sgml | 14 + fc-fontations/meson.build | 4 fc-fontations/names.rs | 36 ++ fontconfig/fontconfig.h.in | 17 + meson.build | 2 src/fccfg.c | 2 src/fcdbg.c | 530 ++++++++++++++++++++++++------------------ src/fcfreetype.c | 31 ++ src/fcint.h | 44 +++ src/fcmatch.c | 1 src/fcname.c | 14 + src/fcobjs.h | 1 src/fcxml.c | 81 +++++- test/test-48-guessfamily.json | 92 +++++++ test/test-49-sansserif.json | 66 +++++ test/test-conf.c | 107 +++++--- 22 files changed, 1030 insertions(+), 302 deletions(-) New commits: commit d2dbd81c37f2ae8da4e779e56758a57172512ad8 Merge: f5eed02 1741ec6 Author: Akira TAGOH <akira@xxxxxxxxx> Date: Tue Sep 9 09:43:50 2025 +0000 Merge branch 'generic-family' into 'main' Add genericfamily object in FcPattern Closes #457 See merge request fontconfig/fontconfig!412 commit 1741ec63c007f00b5ba9f3d3773ea7f3cb3c7cba Author: Akira TAGOH <akira@xxxxxxxxx> Date: Tue Sep 2 12:23:10 2025 +0900 Bump the cache version again To make sure genericfamily object is available in a cache. diff --git a/configure.ac b/configure.ac index 578a894..06dbc57 100644 --- a/configure.ac +++ b/configure.ac @@ -77,7 +77,7 @@ AC_DEFINE_UNQUOTED([FC_VERSION_MINOR], [$minor], [minor version]) AC_DEFINE_UNQUOTED([FC_VERSION_MICRO], [$revision], [revision]) dnl cache version -CACHE_VERSION=10 +CACHE_VERSION=11 AC_SUBST(CACHE_VERSION) dnl libtool versioning diff --git a/meson.build b/meson.build index 63a2ca8..4483f6f 100644 --- a/meson.build +++ b/meson.build @@ -25,7 +25,7 @@ curversion = fc_version_minor - 1 libversion = '@0@.@1@.0'.format(soversion, curversion) defversion = '@0@.@1@'.format(curversion, fc_version_micro) osxversion = curversion + 1 -cacheversion = '10' +cacheversion = '11' freetype_req = '>= 21.0.15' freetype_req_cmake = '>= 2.8.1' commit d7299f4d1162858cc4eb7cc74d36ee0df8c0fe8f Author: Akira TAGOH <akira@xxxxxxxxx> Date: Mon Sep 1 23:37:13 2025 +0900 Get out from FcConfigAdd immediately if no valid pointer given diff --git a/src/fccfg.c b/src/fccfg.c index 2e67688..ffd06b0 100644 --- a/src/fccfg.c +++ b/src/fccfg.c @@ -1790,6 +1790,8 @@ FcConfigAdd (FcValueListPtr *head, FcValueListPtr *prev, l, last; FcValueBinding sameBinding; + if (!newp) + return FcFalse; if (position) sameBinding = position->binding; else commit b7f6ac384405be7fedeaac1dd5edafb87026339d Author: Akira TAGOH <akira@xxxxxxxxx> Date: Tue Sep 2 12:00:22 2025 +0900 Add xsi:nil attribute support to limited elements This change allows us to make it easy to create a condition for missing objects. For example, if one wants to add something only when missing family object: <match> <test qual="all" name="family" compare="contains"> <string xsi:nil="true"/> </test> <edit name="foo"> <string>foo</string> </edit> </match> $ fc-pattern -c sans Pattern has 3 elts (size 16) family: "sans"(s) lang: "en"(w) prgname: "fc-pattern"(s) $ fc-pattern -c Pattern has 3 elts (size 16) lang: "en"(w) prgname: "fc-pattern"(s) foo: "foo"(w) This will be applied to string, int, double, bool, charset, langset, and const only. Changelog: added diff --git a/conf.d/48-guessfamily.conf b/conf.d/48-guessfamily.conf index 3304d7d..dbdabe9 100644 --- a/conf.d/48-guessfamily.conf +++ b/conf.d/48-guessfamily.conf @@ -10,6 +10,14 @@ <edit name="family" mode="append_last"> <string>monospace</string> </edit> + </match> + <match target="pattern"> + <test name="family" compare="contains"> + <string>mono</string> + </test> + <test qual="all" name="genericfamily" compare="contains"> + <const xsi:nil="true"/> + </test> <edit name="genericfamily" mode="append"> <const>monospace</const> </edit> @@ -19,9 +27,20 @@ <test name="family" compare="contains"> <string>sans</string> </test> + <test qual="all" name="family" compare="not_contains"> + <string>mono</string> + </test> <edit name="family" mode="append_last"> <string>sans-serif</string> </edit> + </match> + <match target="pattern"> + <test name="family" compare="contains"> + <string>sans</string> + </test> + <test qual="all" name="genericfamily" compare="contains"> + <const xsi:nil="true"/> + </test> <edit name="genericfamily" mode="append"> <const>sans-serif</const> </edit> @@ -31,9 +50,23 @@ <test name="family" compare="contains"> <string>serif</string> </test> + <test qual="all" name="family" compare="not_eq"> + <string>sans-serif</string> + </test> <edit name="family" mode="append_last"> <string>serif</string> </edit> + </match> + <match target="pattern"> + <test name="family" compare="contains"> + <string>serif</string> + </test> + <test qual="all" name="family" compare="not_eq"> + <string>sans-serif</string> + </test> + <test qual="all" name="genericfamily" compare="contains"> + <const xsi:nil="true"/> + </test> <edit name="genericfamily" mode="append"> <const>serif</const> </edit> @@ -46,6 +79,14 @@ <edit name="family" mode="append_last"> <string>emoji</string> </edit> + </match> + <match target="pattern"> + <test name="family" compare="contains"> + <string>emoji</string> + </test> + <test qual="all" name="genericfamily" compare="contains"> + <const xsi:nil="true"/> + </test> <edit name="genericfamily" mode="append"> <const>emoji</const> </edit> @@ -58,6 +99,14 @@ <edit name="family" mode="append_last"> <string>math</string> </edit> + </match> + <match target="pattern"> + <test name="family" compare="contains"> + <string>math</string> + </test> + <test qual="all" name="genericfamily" compare="contains"> + <const xsi:nil="true"/> + </test> <edit name="genericfamily" mode="append"> <const>math</const> </edit> @@ -67,6 +116,9 @@ <test name="family" compare="contains"> <string>system-ui</string> </test> + <test qual="all" name="genericfamily" compare="contains"> + <const xsi:nil="true"/> + </test> <edit name="genericfamily" mode="append"> <const>system-ui</const> </edit> @@ -76,6 +128,9 @@ <test name="family" compare="contains"> <string>cursive</string> </test> + <test qual="all" name="genericfamily" compare="contains"> + <const xsi:nil="true"/> + </test> <edit name="genericfamily" mode="append"> <const>cursive</const> </edit> @@ -85,6 +140,9 @@ <test name="family" compare="contains"> <string>fantasy</string> </test> + <test qual="all" name="genericfamily" compare="contains"> + <const xsi:nil="true"/> + </test> <edit name="genericfamily" mode="append"> <const>fantasy</const> </edit> @@ -94,6 +152,9 @@ <test name="family" compare="contains"> <string>ui-sans-serif</string> </test> + <test qual="all" name="genericfamily" compare="contains"> + <const xsi:nil="true"/> + </test> <edit name="genericfamily" mode="append"> <const>ui-sans-serif</const> </edit> @@ -103,6 +164,9 @@ <test name="family" compare="contains"> <string>ui-serif</string> </test> + <test qual="all" name="genericfamily" compare="contains"> + <const xsi:nil="true"/> + </test> <edit name="genericfamily" mode="append"> <const>ui-serif</const> </edit> @@ -112,6 +176,9 @@ <test name="family" compare="contains"> <string>ui-monospace</string> </test> + <test qual="all" name="genericfamily" compare="contains"> + <const xsi:nil="true"/> + </test> <edit name="genericfamily" mode="append"> <const>ui-monospace</const> </edit> @@ -121,6 +188,9 @@ <test name="family" compare="contains"> <string>ui-rounded</string> </test> + <test qual="all" name="genericfamily" compare="contains"> + <const xsi:nil="true"/> + </test> <edit name="genericfamily" mode="append"> <const>ui-rounded</const> </edit> @@ -130,6 +200,9 @@ <test name="family" compare="contains"> <string>fangsong</string> </test> + <test qual="all" name="genericfamily" compare="contains"> + <const xsi:nil="true"/> + </test> <edit name="genericfamily" mode="append"> <const>fangsong</const> </edit> diff --git a/conf.d/49-sansserif.conf b/conf.d/49-sansserif.conf index db040e1..dac60d5 100644 --- a/conf.d/49-sansserif.conf +++ b/conf.d/49-sansserif.conf @@ -48,6 +48,24 @@ <!-- If the font still has no generic name, add sans-serif --> + <match target="pattern"> + <test qual="all" name="family" compare="not_eq"> + <string>sans-serif</string> + </test> + <test qual="all" name="family" compare="not_eq"> + <string>serif</string> + </test> + <test qual="all" name="family" compare="not_eq"> + <string>monospace</string> + </test> + <test qual="all" name="genericfamily" compare="contains"> + <const xsi:nil="true" /> + </test> + <edit name="genericfamily" mode="append"> + <const>sans-serif</const> + </edit> + </match> + <match target="pattern"> <test qual="all" name="family" compare="not_eq"> <string>sans-serif</string> diff --git a/src/fcxml.c b/src/fcxml.c index 2db7578..edb5e8f 100644 --- a/src/fcxml.c +++ b/src/fcxml.c @@ -102,6 +102,16 @@ FcRuleDestroy (FcRule *rule) FcRuleDestroy (n); } +static FcExpr * +FcExprCreateNil (FcConfig *config) +{ + FcExpr *e = FcConfigAllocExpr (config); + if (e) { + e->op = FcOpNil; + } + return e; +} + static FcExpr * FcExprCreateInteger (FcConfig *config, int i) { @@ -530,7 +540,8 @@ typedef enum _FcVStackTag { FcVStackTest, FcVStackExpr, - FcVStackEdit + FcVStackEdit, + FcVStackNil, } FcVStackTag; typedef struct _FcVStack { @@ -580,6 +591,9 @@ typedef enum _FcConfigSeverity { FcSevereError } FcConfigSeverity; +static FcBool +FcConfigLexBool (FcConfigParse *parse, const FcChar8 *bool_); + static void FcConfigMessage (FcConfigParse *parse, FcConfigSeverity severe, const char *fmt, ...) { @@ -858,6 +872,18 @@ FcVStackCreateAndPush (FcConfigParse *parse) return newp; } +static FcBool +FcVStackPushNil (FcConfigParse *parse) +{ + FcVStack *vstack = FcVStackCreateAndPush (parse); + + if (!vstack) + return FcFalse; + vstack->tag = FcVStackNil; + + return FcTrue; +} + static FcBool FcVStackPushString (FcConfigParse *parse, FcVStackTag tag, FcChar8 *string) { @@ -1054,6 +1080,7 @@ FcVStackPopAndDestroy (FcConfigParse *parse) break; case FcVStackInteger: case FcVStackDouble: + case FcVStackNil: break; case FcVStackMatrix: FcExprMatrixFreeShallow (vstack->u.matrix); @@ -1407,6 +1434,23 @@ FcParseRescan (FcConfigParse *parse) } } +static FcBool +FcParseNil (FcConfigParse *parse) +{ + const FcChar8 *nil; + + if (!parse->pstack) + return FcFalse; + nil = FcConfigGetAttribute (parse, "xsi:nil"); + if (!nil) + return FcFalse; + if (!FcConfigLexBool (parse, nil)) + return FcFalse; + FcVStackPushNil (parse); + + return FcTrue; +} + static void FcParseInt (FcConfigParse *parse) { @@ -1420,12 +1464,15 @@ FcParseInt (FcConfigParse *parse) FcConfigMessage (parse, FcSevereError, "out of memory"); return; } + if (FcParseNil (parse)) + goto bail; end = 0; l = (int)strtol ((char *)s, (char **)&end, 0); if (end != s + strlen ((char *)s)) FcConfigMessage (parse, FcSevereError, "\"%s\": not a valid integer", s); else FcVStackPushInteger (parse, l); + bail: FcStrBufDestroy (&parse->pstack->str); } @@ -1506,12 +1553,15 @@ FcParseDouble (FcConfigParse *parse) FcConfigMessage (parse, FcSevereError, "out of memory"); return; } + if (FcParseNil (parse)) + goto bail; end = 0; d = FcStrtod ((char *)s, (char **)&end); if (end != s + strlen ((char *)s)) FcConfigMessage (parse, FcSevereError, "\"%s\": not a valid double", s); else FcVStackPushDouble (parse, d); + bail: FcStrBufDestroy (&parse->pstack->str); } @@ -1527,6 +1577,10 @@ FcParseString (FcConfigParse *parse, FcVStackTag tag) FcConfigMessage (parse, FcSevereError, "out of memory"); return; } + if (FcParseNil (parse)) { + FcStrFree (s); + return; + } if (!FcVStackPushString (parse, tag, s)) FcStrFree (s); } @@ -1666,12 +1720,15 @@ FcParseBool (FcConfigParse *parse) if (!parse->pstack) return; + if (FcParseNil (parse)) + goto bail; s = FcStrBufDoneStatic (&parse->pstack->str); if (!s) { FcConfigMessage (parse, FcSevereError, "out of memory"); return; } FcVStackPushBool (parse, FcConfigLexBool (parse, s)); + bail: FcStrBufDestroy (&parse->pstack->str); } @@ -1683,6 +1740,8 @@ FcParseCharSet (FcConfigParse *parse) FcChar32 i, begin, end; int n = 0; + if (FcParseNil (parse)) + goto bail; while ((vstack = FcVStackPeek (parse))) { switch ((int)vstack->tag) { case FcVStackInteger: @@ -1710,6 +1769,7 @@ FcParseCharSet (FcConfigParse *parse) } FcVStackPopAndDestroy (parse); } + bail: if (n > 0) FcVStackPushCharSet (parse, charset); else @@ -1723,6 +1783,8 @@ FcParseLangSet (FcConfigParse *parse) FcLangSet *langset = FcLangSetCreate(); int n = 0; + if (FcParseNil (parse)) + goto bail; while ((vstack = FcVStackPeek (parse))) { switch ((int)vstack->tag) { case FcVStackString: @@ -1737,6 +1799,7 @@ FcParseLangSet (FcConfigParse *parse) } FcVStackPopAndDestroy (parse); } + bail: if (n > 0) FcVStackPushLangSet (parse, langset); else @@ -2088,6 +2151,9 @@ FcPopExpr (FcConfigParse *parse) break; case FcVStackEdit: break; + case FcVStackNil: + expr = FcExprCreateNil (parse->config); + break; default: break; } @@ -2322,8 +2388,7 @@ FcParseInclude (FcConfigParse *parse) goto bail; } attr = FcConfigGetAttribute (parse, "ignore_missing"); - if (attr && FcConfigLexBool (parse, (FcChar8 *)attr) == FcTrue) - ignore_missing = FcTrue; + ignore_missing = attr ? FcConfigLexBool (parse, (FcChar8 *)attr) : FcFalse; /* deprecated attribute has ever been used to mark * old configuration path as deprecated. * We don't have any code for it but just keep it for @@ -2480,15 +2545,9 @@ FcParseTest (FcConfigParse *parse) } iblanks_string = FcConfigGetAttribute (parse, "ignore-blanks"); if (iblanks_string) { - FcBool f = FcFalse; - - if (!FcNameBool (iblanks_string, &f)) { - FcConfigMessage (parse, - FcSevereWarning, - "invalid test ignore-blanks \"%s\"", iblanks_string); - } - if (f) + if (FcConfigLexBool (parse, iblanks_string)) { flags |= FcOpFlagIgnoreBlanks; + } } expr = FcPopBinary (parse, FcOpComma); if (!expr) { commit 21f65e1c4eac5ea215f5438cd1d0cd28a89f37cf Author: Akira TAGOH <akira@xxxxxxxxx> Date: Mon Sep 1 17:42:46 2025 +0900 Add genericfamily object in FcPattern This aims to filter out easily with generic-family and avoid misselection for unknown-classified family name. We still need a generic-family in family object to struct a font list though, the change in 49-sansserif.conf tries to complement family from genericfamily object. Fixes https://gitlab.freedesktop.org/fontconfig/fontconfig/-/issues/457 Changelog: added diff --git a/conf.d/48-guessfamily.conf b/conf.d/48-guessfamily.conf index aaf8527..3304d7d 100644 --- a/conf.d/48-guessfamily.conf +++ b/conf.d/48-guessfamily.conf @@ -2,43 +2,136 @@ <!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd"> <fontconfig> <description>Guess a generic-family for substitution</description> - <!-- sans-serif --> + <!-- monospace --> <match target="pattern"> - <test qual="all" name="family" compare="not_eq"> - <string>sans-serif</string> + <test name="family" compare="contains"> + <string>mono</string> </test> + <edit name="family" mode="append_last"> + <string>monospace</string> + </edit> + <edit name="genericfamily" mode="append"> + <const>monospace</const> + </edit> + </match> + <!-- sans-serif --> + <match target="pattern"> <test name="family" compare="contains"> <string>sans</string> </test> <edit name="family" mode="append_last"> <string>sans-serif</string> </edit> + <edit name="genericfamily" mode="append"> + <const>sans-serif</const> + </edit> </match> <!-- serif --> <match target="pattern"> - <test qual="all" name="family" compare="not_eq"> - <string>sans-serif</string> - </test> - <test qual="all" name="family" compare="not_eq"> - <string>serif</string> - </test> <test name="family" compare="contains"> <string>serif</string> </test> <edit name="family" mode="append_last"> <string>serif</string> </edit> + <edit name="genericfamily" mode="append"> + <const>serif</const> + </edit> </match> - <!-- monospace --> + <!-- emoji --> <match target="pattern"> - <test qual="all" name="family" compare="not_eq"> - <string>monospace</string> + <test name="family" compare="contains"> + <string>emoji</string> </test> + <edit name="family" mode="append_last"> + <string>emoji</string> + </edit> + <edit name="genericfamily" mode="append"> + <const>emoji</const> + </edit> + </match> + <!-- math --> + <match target="pattern"> <test name="family" compare="contains"> - <string>mono</string> + <string>math</string> </test> <edit name="family" mode="append_last"> - <string>monospace</string> + <string>math</string> + </edit> + <edit name="genericfamily" mode="append"> + <const>math</const> + </edit> + </match> + <!-- system-ui --> + <match target="pattern"> + <test name="family" compare="contains"> + <string>system-ui</string> + </test> + <edit name="genericfamily" mode="append"> + <const>system-ui</const> + </edit> + </match> + <!-- cursive --> + <match target="pattern"> + <test name="family" compare="contains"> + <string>cursive</string> + </test> + <edit name="genericfamily" mode="append"> + <const>cursive</const> + </edit> + </match> + <!-- fantasy --> + <match target="pattern"> + <test name="family" compare="contains"> + <string>fantasy</string> + </test> + <edit name="genericfamily" mode="append"> + <const>fantasy</const> + </edit> + </match> + <!-- ui-sans-serif --> + <match target="pattern"> + <test name="family" compare="contains"> + <string>ui-sans-serif</string> + </test> + <edit name="genericfamily" mode="append"> + <const>ui-sans-serif</const> + </edit> + </match> + <!-- ui-serif --> + <match target="pattern"> + <test name="family" compare="contains"> + <string>ui-serif</string> + </test> + <edit name="genericfamily" mode="append"> + <const>ui-serif</const> + </edit> + </match> + <!-- ui-monospace --> + <match target="pattern"> + <test name="family" compare="contains"> + <string>ui-monospace</string> + </test> + <edit name="genericfamily" mode="append"> + <const>ui-monospace</const> + </edit> + </match> + <!-- ui-rounded --> + <match target="pattern"> + <test name="family" compare="contains"> + <string>ui-rounded</string> + </test> + <edit name="genericfamily" mode="append"> + <const>ui-rounded</const> + </edit> + </match> + <!-- fangsong --> + <match target="pattern"> + <test name="family" compare="contains"> + <string>fangsong</string> + </test> + <edit name="genericfamily" mode="append"> + <const>fangsong</const> </edit> </match> </fontconfig> diff --git a/conf.d/49-sansserif.conf b/conf.d/49-sansserif.conf index 6cc3a1c..db040e1 100644 --- a/conf.d/49-sansserif.conf +++ b/conf.d/49-sansserif.conf @@ -2,21 +2,65 @@ <!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd"> <fontconfig> <description>Add sans-serif to the family when no generic name</description> -<!-- - If the font still has no generic name, add sans-serif - --> - <match target="pattern"> - <test qual="all" name="family" compare="not_eq"> - <string>sans-serif</string> - </test> - <test qual="all" name="family" compare="not_eq"> - <string>serif</string> - </test> - <test qual="all" name="family" compare="not_eq"> - <string>monospace</string> - </test> - <edit name="family" mode="append_last"> - <string>sans-serif</string> - </edit> - </match> + + <!-- Add family from genericfamly --> + <match target="pattern"> + <test name="genericfamily" compare="eq"> + <const>serif</const> + </test> + <edit name="family" mode="prepend"> + <string>serif</string> + </edit> + </match> + <match target="pattern"> + <test name="genericfamily" compare="eq"> + <const>sans-serif</const> + </test> + <edit name="family" mode="prepend"> + <string>sans-serif</string> + </edit> + </match> + <match target="pattern"> + <test name="genericfamily" compare="eq"> + <const>monospace</const> + </test> + <edit name="family" mode="prepend"> + <string>monospace</string> + </edit> + </match> + <match target="pattern"> + <test name="genericfamily" compare="eq"> + <const>emoji</const> + </test> + <edit name="family" mode="prepend"> + <string>emoji</string> + </edit> + </match> + <match target="pattern"> + <test name="genericfamily" compare="eq"> + <const>math</const> + </test> + <edit name="family" mode="prepend"> + <string>math</string> + </edit> + </match> + + <!-- + If the font still has no generic name, add sans-serif + --> + <match target="pattern"> + <test qual="all" name="family" compare="not_eq"> + <string>sans-serif</string> + </test> + <test qual="all" name="family" compare="not_eq"> + <string>serif</string> + </test> + <test qual="all" name="family" compare="not_eq"> + <string>monospace</string> + </test> + <edit name="family" mode="append_last"> + <string>sans-serif</string> + </edit> + </match> + </fontconfig> diff --git a/conf.d/Makefile.am b/conf.d/Makefile.am index 6a27cf6..bae53d7 100644 --- a/conf.d/Makefile.am +++ b/conf.d/Makefile.am @@ -40,6 +40,7 @@ CONF_LINKS = \ 40-nonlatin.conf \ 45-generic.conf \ 45-latin.conf \ + 48-guessfamily.conf \ 48-spacing.conf \ 49-sansserif.conf \ 50-user.conf \ diff --git a/conf.d/meson.build b/conf.d/meson.build index 5d15532..3656705 100644 --- a/conf.d/meson.build +++ b/conf.d/meson.build @@ -57,6 +57,7 @@ conf_links = [ '40-nonlatin.conf', '45-generic.conf', '45-latin.conf', + '48-guessfamily.conf', '48-spacing.conf', '49-sansserif.conf', '50-user.conf', diff --git a/doc/fontconfig-devel.sgml b/doc/fontconfig-devel.sgml index 30b8b40..0c854d6 100644 --- a/doc/fontconfig-devel.sgml +++ b/doc/fontconfig-devel.sgml @@ -218,6 +218,8 @@ convenience for the application's rendering mechanism. desktop FC_DESKTOP_NAME String Current desktop name namedinstance FC_NAMED_INSTANCE Bool Whether font is a named instance fontwrapper FC_FONT_WRAPPER String The font wrapper format + genericfamily FC_GENERIC_FAMILY Int Type of Generic Family corresponding + to FC_FAMILY_* constants </programlisting> </sect2> </sect1> diff --git a/doc/fontconfig-user.sgml b/doc/fontconfig-user.sgml index 853ead1..60dd337 100644 --- a/doc/fontconfig-user.sgml +++ b/doc/fontconfig-user.sgml @@ -152,6 +152,7 @@ namedinstance Bool Whether font is a named instance fontwrapper String The font wrapper format, current values are WOFF, WOFF2, SFNT for any other SFNT font, and CFF for standalone CFF fonts. +genericfamily Int Type of Generic Family </programlisting> </refsect2> <refsect2> @@ -563,6 +564,19 @@ hintnone hintstyle 0 hintslight hintstyle 1 hintmedium hintstyle 2 hintfull hintstyle 3 +serif genericfamily 1 +sans-serif genericfamily 2 +monospace genericfamily 3 +cursive genericfamily 4 +fantasy genericfamily 5 +system-ui genericfamily 6 +ui-serif genericfamily 7 +ui-sans-serif genericfamily 8 +ui-monospace genericfamily 9 +ui-rounded genericfamily 10 +emoji genericfamily 11 +math genericfamily 12 +fangsong genericfamily 13 </programlisting> </para> </refsect2> diff --git a/fc-fontations/meson.build b/fc-fontations/meson.build index 116cad8..b816edd 100644 --- a/fc-fontations/meson.build +++ b/fc-fontations/meson.build @@ -9,7 +9,7 @@ if (fontations.enabled()) include_directories: incbase, args: [ '--merge-extern-blocks', - '--allowlist-item=(FcCharSet.*|FC_(SLANT|WEIGHT|WIDTH)_.*|FcFontSet(Add|Create|Destroy).*|FcLangSet(Create|Destroy|Copy|Add|HasLang)|FcWeightFromOpenType.*|FC_DUAL|FC_MONO)', + '--allowlist-item=(FcCharSet.*|FC_(SLANT|WEIGHT|WIDTH)_.*|FcFontSet(Add|Create|Destroy).*|FcLangSet(Create|Destroy|Copy|Add|HasLang)|FcWeightFromOpenType.*|FC_DUAL|FC_MONO|FC_FAMILY_(UNKNOWN|SANS|SERIF|MONO|EMOJI|MATH))', '--raw-line=#![allow(nonstandard_style,unused)]', ], # FC_NO_MT=1 is added here to reduce required headers in bindings generation. @@ -73,4 +73,4 @@ if (fontations.enabled()) ) -endif \ No newline at end of file +endif diff --git a/fc-fontations/names.rs b/fc-fontations/names.rs index c390434..ff42278 100644 --- a/fc-fontations/names.rs +++ b/fc-fontations/names.rs @@ -27,7 +27,12 @@ use skrifa::{string::StringId, MetadataProvider}; use fcint_bindings::{ FC_FAMILYLANG_OBJECT, FC_FAMILY_OBJECT, FC_FULLNAMELANG_OBJECT, FC_FULLNAME_OBJECT, - FC_INVALID_OBJECT, FC_POSTSCRIPT_NAME_OBJECT, FC_STYLELANG_OBJECT, FC_STYLE_OBJECT, + FC_GENERIC_FAMILY_OBJECT, FC_INVALID_OBJECT, FC_POSTSCRIPT_NAME_OBJECT, FC_STYLELANG_OBJECT, + FC_STYLE_OBJECT, +}; +use fontconfig_bindings::{ + FC_FAMILY_EMOJI, FC_FAMILY_MATH, FC_FAMILY_MONO, FC_FAMILY_SANS, FC_FAMILY_SERIF, + FC_FAMILY_UNKNOWN, }; use crate::{name_records::FcSortedNameRecords, FcPatternBuilder, InstanceMode, PatternElement}; @@ -138,6 +143,23 @@ fn mangle_full_name_for_named_instance(font: &FontRef, named_instance_id: i32) - CString::new(full_name + &subfam).ok() } +fn get_generic_family(s: &str) -> i32 { + [ + ("mono", FC_FAMILY_MONO), + ("sans", FC_FAMILY_SANS), + ("serif", FC_FAMILY_SERIF), + ("emoji", FC_FAMILY_EMOJI), + ("math", FC_FAMILY_MATH), + ] + .into_iter() + .find_map(|(font_sub_name, generic_family_id)| { + s.to_lowercase() + .contains(font_sub_name) + .then_some(generic_family_id) + }) + .unwrap_or(FC_FAMILY_UNKNOWN) as i32 +} + pub fn add_names(font: &FontRef, instance_mode: InstanceMode, pattern: &mut FcPatternBuilder) { let mut already_encountered_names: HashSet<(i32, String)> = HashSet::new(); let name_table = font.name(); @@ -145,6 +167,7 @@ pub fn add_names(font: &FontRef, instance_mode: InstanceMode, pattern: &mut FcPa return; } let name_table = name_table.unwrap(); + let mut generic_family = FC_FAMILY_UNKNOWN as i32; for name_record in FcSortedNameRecords::new(&name_table) { let string_id = name_record.name_id(); @@ -193,6 +216,13 @@ pub fn add_names(font: &FontRef, instance_mode: InstanceMode, pattern: &mut FcPa _ => name, }; + if object_ids.0 == FC_FAMILY_OBJECT as i32 { + if let Some(s) = &name { + if generic_family == FC_FAMILY_UNKNOWN as i32 { + generic_family = get_generic_family(s.as_c_str().to_str().unwrap()); + } + } + } if let (Some(name), Some(language)) = (name, language) { let normalized_name = normalize_name(&name); if already_encountered_names.contains(&(object_ids.0, normalized_name.clone())) { @@ -207,4 +237,8 @@ pub fn add_names(font: &FontRef, instance_mode: InstanceMode, pattern: &mut FcPa } } } + pattern.append_element(PatternElement::new( + FC_GENERIC_FAMILY_OBJECT as i32, + generic_family.into(), + )); } diff --git a/fontconfig/fontconfig.h.in b/fontconfig/fontconfig.h.in index e8944f9..775a6bf 100644 --- a/fontconfig/fontconfig.h.in +++ b/fontconfig/fontconfig.h.in @@ -133,6 +133,7 @@ typedef int FcBool; #define FC_DESKTOP_NAME "desktop" /* String */ #define FC_NAMED_INSTANCE "namedinstance" /* Bool - true if font is named instance */ #define FC_FONT_WRAPPER "fontwrapper" /* String */ +#define FC_GENERIC_FAMILY "genericfamily" /* Integer */ #define FC_CACHE_SUFFIX ".cache-" FC_CACHE_VERSION #define FC_DIR_CACHE_FILE "fonts.cache-" FC_CACHE_VERSION @@ -203,6 +204,22 @@ typedef int FcBool; #define FC_LCD_LIGHT 2 #define FC_LCD_LEGACY 3 +/* Generic family */ +#define FC_FAMILY_UNKNOWN 0 +#define FC_FAMILY_SERIF 1 +#define FC_FAMILY_SANS 2 +#define FC_FAMILY_MONO 3 +#define FC_FAMILY_CURSIVE 4 +#define FC_FAMILY_FANTASY 5 +#define FC_FAMILY_SYSTEM_UI 6 +#define FC_FAMILY_UI_SERIF 7 +#define FC_FAMILY_UI_SANS 8 +#define FC_FAMILY_UI_MONO 9 +#define FC_FAMILY_UI_ROUNDED 10 +#define FC_FAMILY_EMOJI 11 +#define FC_FAMILY_MATH 12 +#define FC_FAMILY_FANGSONG 13 + typedef enum _FcType { FcTypeUnknown = -1, FcTypeVoid, diff --git a/src/fcdbg.c b/src/fcdbg.c index 97e5669..b0e342d 100644 --- a/src/fcdbg.c +++ b/src/fcdbg.c @@ -28,53 +28,52 @@ #include <stdlib.h> static void -_FcValuePrintFile (FILE *f, const FcValue v) +_FcValuePrintFile (FILE *stream, const FcValue v) { switch (v.type) { case FcTypeUnknown: - fprintf (f, "<unknown>"); + fprintf (stream, "<unknown>"); break; case FcTypeVoid: - fprintf (f, "<void>"); + fprintf (stream, "<void>"); break; case FcTypeInteger: - fprintf (f, "%d(i)", v.u.i); + fprintf (stream, "%d(i)", v.u.i); break; case FcTypeDouble: - fprintf (f, "%g(f)", v.u.d); + fprintf (stream, "%g(f)", v.u.d); break; case FcTypeString: - fprintf (f, "\"%s\"", v.u.s); + fprintf (stream, "\"%s\"", v.u.s); break; case FcTypeBool: - fprintf (f, + fprintf (stream, v.u.b == FcTrue ? "True" : v.u.b == FcFalse ? "False" : "DontCare"); break; case FcTypeMatrix: - fprintf (f, "[%g %g; %g %g]", v.u.m->xx, v.u.m->xy, v.u.m->yx, v.u.m->yy); + fprintf (stream, "[%g %g; %g %g]", v.u.m->xx, v.u.m->xy, v.u.m->yx, v.u.m->yy); break; - case FcTypeCharSet: /* XXX */ - if (f == stdout) - FcCharSetPrint (v.u.c); + case FcTypeCharSet: + FcCharSetPrintFile (stream, v.u.c); break; case FcTypeLangSet: FcLangSetPrint (v.u.l); break; case FcTypeFTFace: - fprintf (f, "face"); + fprintf (stream, "face"); break; case FcTypeRange: - fprintf (f, "[%g %g]", v.u.r->begin, v.u.r->end); + fprintf (stream, "[%g %g]", v.u.r->begin, v.u.r->end); break; } } void -FcValuePrintFile (FILE *f, const FcValue v) +FcValuePrintFile (FILE *stream, const FcValue v) { - fprintf (f, " "); - _FcValuePrintFile (f, v); + fprintf (stream, " "); + _FcValuePrintFile (stream, v); } void @@ -85,132 +84,171 @@ FcValuePrint (const FcValue v) } void -FcValuePrintWithPosition (const FcValue v, FcBool show_pos_mark) +FcValuePrintFileWithPosition (FILE *stream, const FcValue v, FcBool show_pos_mark) { if (show_pos_mark) - printf (" [marker] "); + fprintf (stream, " [marker] "); else - printf (" "); - _FcValuePrintFile (stdout, v); + fprintf (stream, " "); + _FcValuePrintFile (stream, v); +} + +void +FcValuePrintWithPosition (const FcValue v, FcBool show_pos_mark) +{ + FcValuePrintFileWithPosition (stdout, v, show_pos_mark); } static void -FcValueBindingPrint (const FcValueListPtr l) +FcValueBindingPrintFile (FILE *stream, const FcValueListPtr l) { switch (l->binding) { case FcValueBindingWeak: - printf ("(w)"); + fprintf (stream, "(w)"); break; case FcValueBindingStrong: - printf ("(s)"); + fprintf (stream, "(s)"); break; case FcValueBindingSame: - printf ("(=)"); + fprintf (stream, "(=)"); break; default: /* shouldn't be reached */ - printf ("(?)"); + fprintf (stream, "(?)"); break; } } void -FcValueListPrintWithPosition (FcValueListPtr l, const FcValueListPtr pos) +FcValueListPrintFileWithPosition (FILE *stream, FcValueListPtr l, const FcValueListPtr pos) { for (; l != NULL; l = FcValueListNext (l)) { - FcValuePrintWithPosition (FcValueCanonicalize (&l->value), pos != NULL && l == pos); - FcValueBindingPrint (l); + FcValuePrintFileWithPosition (stream, FcValueCanonicalize (&l->value), pos != NULL && l == pos); + FcValueBindingPrintFile (stream, l); } if (!pos) - printf (" [marker]"); + fprintf (stream, " [marker]"); } void -FcValueListPrint (FcValueListPtr l) +FcValueListPrintWithPosition (FcValueListPtr l, const FcValueListPtr pos) +{ + FcValueListPrintFileWithPosition(stdout, l, pos); +} + +void +FcValueListPrintFile (FILE *stream, FcValueListPtr l) { for (; l != NULL; l = FcValueListNext (l)) { - FcValuePrint (FcValueCanonicalize (&l->value)); - FcValueBindingPrint (l); + FcValuePrintFile (stream, FcValueCanonicalize (&l->value)); + FcValueBindingPrintFile (stream, l); } } void -FcLangSetPrint (const FcLangSet *ls) +FcValueListPrint (FcValueListPtr l) +{ + FcValueListPrintFile (stdout, l); +} + +void +FcLangSetPrintFile (FILE *stream, const FcLangSet *ls) { FcStrBuf buf; FcChar8 init_buf[1024]; FcStrBufInit (&buf, init_buf, sizeof (init_buf)); if (FcNameUnparseLangSet (&buf, ls) && FcStrBufChar (&buf, '\0')) - printf ("%s", buf.buf); + fprintf (stream, "%s", buf.buf); else - printf ("langset (alloc error)"); + fprintf (stream, "langset (alloc error)"); FcStrBufDestroy (&buf); } void -FcCharSetPrint (const FcCharSet *c) +FcLangSetPrint (const FcLangSet *ls) +{ + FcLangSetPrintFile (stdout, ls); +} + +void +FcCharSetPrintFile (FILE *stream, const FcCharSet *c) { int i, j; intptr_t *leaves = FcCharSetLeaves (c); FcChar16 *numbers = FcCharSetNumbers (c); #if 0 - printf ("CharSet 0x%x\n", (intptr_t) c); - printf ("Leaves: +%d = 0x%x\n", c->leaves_offset, (intptr_t) leaves); - printf ("Numbers: +%d = 0x%x\n", c->numbers_offset, (intptr_t) numbers); + fprintf (stream, "CharSet 0x%x\n", (intptr_t) c); + fprintf (stream, "Leaves: +%d = 0x%x\n", c->leaves_offset, (intptr_t) leaves); + fprintf (stream, "Numbers: +%d = 0x%x\n", c->numbers_offset, (intptr_t) numbers); for (i = 0; i < c->num; i++) { - printf ("Page %d: %04x +%d = 0x%x\n", - i, numbers[i], leaves[i], - (intptr_t) FcOffsetToPtr (leaves, leaves[i], FcCharLeaf)); + fprintf (stream, "Page %d: %04x +%d = 0x%x\n", + i, numbers[i], leaves[i], + (intptr_t) FcOffsetToPtr (leaves, leaves[i], FcCharLeaf)); } #endif - printf ("\n"); + fprintf (stream, "\n"); for (i = 0; i < c->num; i++) { intptr_t leaf_offset = leaves[i]; FcCharLeaf *leaf = FcOffsetToPtr (leaves, leaf_offset, FcCharLeaf); - printf ("\t"); - printf ("%04x:", numbers[i]); + fprintf (stream, "\t"); + fprintf (stream, "%04x:", numbers[i]); for (j = 0; j < 256 / 32; j++) - printf (" %08x", leaf->map[j]); - printf ("\n"); + fprintf (stream, " %08x", leaf->map[j]); + fprintf (stream, "\n"); } } void -FcPatternPrint (const FcPattern *p) +FcCharSetPrint (const FcCharSet *c) +{ + FcCharSetPrintFile (stdout, c); +} + +void +FcPatternPrintFile (FILE *stream, const FcPattern *p) { FcPatternIter iter; if (!p) { - printf ("Null pattern\n"); + fprintf (stream, "Null pattern\n"); return; } - printf ("Pattern has %d elts (size %d)\n", FcPatternObjectCount (p), p->size); + fprintf (stream, "Pattern has %d elts (size %d)\n", FcPatternObjectCount (p), p->size); FcPatternIterStart (p, &iter); do { - printf ("\t%s:", FcPatternIterGetObject (p, &iter)); - FcValueListPrint (FcPatternIterGetValues (p, &iter)); - printf ("\n"); + fprintf (stream, "\t%s:", FcPatternIterGetObject (p, &iter)); + FcValueListPrintFile (stream, FcPatternIterGetValues (p, &iter)); + fprintf (stream, "\n"); } while (FcPatternIterNext (p, &iter)); - printf ("\n"); + fprintf (stream, "\n"); } -#define FcOpFlagsPrint(_o_) \ - { \ - int f = FC_OP_GET_FLAGS (_o_); \ - if (f & FcOpFlagIgnoreBlanks) \ - printf ("(ignore blanks)"); \ +void +FcPatternPrint (const FcPattern *p) +{ + FcPatternPrintFile (stdout, p); +} + +#define FcOpFlagsPrintFile(_f_, _o_) \ + { \ + int f = FC_OP_GET_FLAGS (_o_); \ + if (f & FcOpFlagIgnoreBlanks) \ + fprintf (_f_, "(ignore blanks)"); \ } +#define FcOpFlagsPrint(_o_) FcOpFlagsPrintFile(stdout, _o_) + void -FcPatternPrint2 (FcPattern *pp1, - FcPattern *pp2, - const FcObjectSet *os) +FcPatternPrint2File (FILE *stream, + FcPattern *pp1, + FcPattern *pp2, + const FcObjectSet *os) { int i, j, k, pos; FcPatternElt *e1, *e2; @@ -223,8 +261,8 @@ FcPatternPrint2 (FcPattern *pp1, p1 = pp1; p2 = pp2; } - printf ("Pattern has %d elts (size %d), %d elts (size %d)\n", - p1->num, p1->size, p2->num, p2->size); + fprintf (stream, "Pattern has %d elts (size %d), %d elts (size %d)\n", + p1->num, p1->size, p2->num, p2->size); for (i = 0, j = 0; i < p1->num; i++) { e1 = &FcPatternElts (p1)[i]; e2 = &FcPatternElts (p2)[j]; @@ -233,25 +271,25 @@ FcPatternPrint2 (FcPattern *pp1, if (pos >= 0) { for (k = j; k < pos; k++) { e2 = &FcPatternElts (p2)[k]; - printf ("\t%s: (None) -> ", FcObjectName (e2->object)); - FcValueListPrint (FcPatternEltValues (e2)); - printf ("\n"); + fprintf (stream, "\t%s: (None) -> ", FcObjectName (e2->object)); + FcValueListPrintFile (stream, FcPatternEltValues (e2)); + fprintf (stream, "\n"); } j = pos; goto cont; } else { - printf ("\t%s:", FcObjectName (e1->object)); - FcValueListPrint (FcPatternEltValues (e1)); - printf (" -> (None)\n"); + fprintf (stream, "\t%s:", FcObjectName (e1->object)); + FcValueListPrintFile (stream, FcPatternEltValues (e1)); + fprintf (stream, " -> (None)\n"); } } else { cont: - printf ("\t%s:", FcObjectName (e1->object)); - FcValueListPrint (FcPatternEltValues (e1)); - printf (" -> "); + fprintf (stream, "\t%s:", FcObjectName (e1->object)); + FcValueListPrintFile (stream, FcPatternEltValues (e1)); + fprintf (stream, " -> "); e2 = &FcPatternElts (p2)[j]; - FcValueListPrint (FcPatternEltValues (e2)); - printf ("\n"); + FcValueListPrintFile (stream, FcPatternEltValues (e2)); + fprintf (stream, "\n"); j++; } } @@ -259,9 +297,9 @@ FcPatternPrint2 (FcPattern *pp1, for (k = j; k < p2->num; k++) { e2 = &FcPatternElts (p2)[k]; if (FcObjectName (e2->object)) { - printf ("\t%s: (None) -> ", FcObjectName (e2->object)); - FcValueListPrint (FcPatternEltValues (e2)); - printf ("\n"); + fprintf (stream, "\t%s: (None) -> ", FcObjectName (e2->object)); + FcValueListPrintFile (stream, FcPatternEltValues (e2)); + fprintf (stream, "\n"); } } } @@ -272,115 +310,129 @@ FcPatternPrint2 (FcPattern *pp1, } void -FcOpPrint (FcOp op_) +FcPatternPrint2 (FcPattern *pp1, + FcPattern *pp2, + const FcObjectSet *os) +{ + FcPatternPrint2File (stdout, pp1, pp2, os); +} + +void +FcOpPrintFile (FILE *stream, FcOp op_) { FcOp op = FC_OP_GET_OP (op_); switch (op) { - case FcOpInteger: printf ("Integer"); break; - case FcOpDouble: printf ("Double"); break; - case FcOpString: printf ("String"); break; - case FcOpMatrix: printf ("Matrix"); break; - case FcOpRange: printf ("Range"); break; - case FcOpBool: printf ("Bool"); break; - case FcOpCharSet: printf ("CharSet"); break; - case FcOpLangSet: printf ("LangSet"); break; - case FcOpField: printf ("Field"); break; - case FcOpConst: printf ("Const"); break; - case FcOpAssign: printf ("Assign"); break; - case FcOpAssignReplace: printf ("AssignReplace"); break; - case FcOpPrepend: printf ("Prepend"); break; - case FcOpPrependFirst: printf ("PrependFirst"); break; - case FcOpAppend: printf ("Append"); break; - case FcOpAppendLast: printf ("AppendLast"); break; - case FcOpDelete: printf ("Delete"); break; - case FcOpDeleteAll: printf ("DeleteAll"); break; - case FcOpQuest: printf ("Quest"); break; - case FcOpOr: printf ("Or"); break; - case FcOpAnd: printf ("And"); break; + case FcOpInteger: fprintf (stream, "Integer"); break; + case FcOpDouble: fprintf (stream, "Double"); break; + case FcOpString: fprintf (stream, "String"); break; + case FcOpMatrix: fprintf (stream, "Matrix"); break; + case FcOpRange: fprintf (stream, "Range"); break; + case FcOpBool: fprintf (stream, "Bool"); break; + case FcOpCharSet: fprintf (stream, "CharSet"); break; + case FcOpLangSet: fprintf (stream, "LangSet"); break; + case FcOpField: fprintf (stream, "Field"); break; + case FcOpConst: fprintf (stream, "Const"); break; + case FcOpAssign: fprintf (stream, "Assign"); break; + case FcOpAssignReplace: fprintf (stream, "AssignReplace"); break; + case FcOpPrepend: fprintf (stream, "Prepend"); break; + case FcOpPrependFirst: fprintf (stream, "PrependFirst"); break; + case FcOpAppend: fprintf (stream, "Append"); break; + case FcOpAppendLast: fprintf (stream, "AppendLast"); break; + case FcOpDelete: fprintf (stream, "Delete"); break; + case FcOpDeleteAll: fprintf (stream, "DeleteAll"); break; + case FcOpQuest: fprintf (stream, "Quest"); break; + case FcOpOr: fprintf (stream, "Or"); break; + case FcOpAnd: fprintf (stream, "And"); break; case FcOpEqual: - printf ("Equal"); - FcOpFlagsPrint (op_); + fprintf (stream, "Equal"); + FcOpFlagsPrintFile (stream, op_); break; case FcOpNotEqual: - printf ("NotEqual"); - FcOpFlagsPrint (op_); + fprintf (stream, "NotEqual"); + FcOpFlagsPrintFile (stream, op_); break; - case FcOpLess: printf ("Less"); break; - case FcOpLessEqual: printf ("LessEqual"); break; - case FcOpMore: printf ("More"); break; - case FcOpMoreEqual: printf ("MoreEqual"); break; - case FcOpContains: printf ("Contains"); break; - case FcOpNotContains: printf ("NotContains"); break; - case FcOpPlus: printf ("Plus"); break; - case FcOpMinus: printf ("Minus"); break; - case FcOpTimes: printf ("Times"); break; - case FcOpDivide: printf ("Divide"); break; - case FcOpNot: printf ("Not"); break; - case FcOpNil: printf ("Nil"); break; - case FcOpComma: printf ("Comma"); break; - case FcOpFloor: printf ("Floor"); break; - case FcOpCeil: printf ("Ceil"); break; - case FcOpRound: printf ("Round"); break; - case FcOpTrunc: printf ("Trunc"); break; + case FcOpLess: fprintf (stream, "Less"); break; + case FcOpLessEqual: fprintf (stream, "LessEqual"); break; + case FcOpMore: fprintf (stream, "More"); break; + case FcOpMoreEqual: fprintf (stream, "MoreEqual"); break; + case FcOpContains: fprintf (stream, "Contains"); break; + case FcOpNotContains: fprintf (stream, "NotContains"); break; + case FcOpPlus: fprintf (stream, "Plus"); break; + case FcOpMinus: fprintf (stream, "Minus"); break; + case FcOpTimes: fprintf (stream, "Times"); break; + case FcOpDivide: fprintf (stream, "Divide"); break; + case FcOpNot: fprintf (stream, "Not"); break; + case FcOpNil: fprintf (stream, "Nil"); break; + case FcOpComma: fprintf (stream, "Comma"); break; + case FcOpFloor: fprintf (stream, "Floor"); break; + case FcOpCeil: fprintf (stream, "Ceil"); break; + case FcOpRound: fprintf (stream, "Round"); break; + case FcOpTrunc: fprintf (stream, "Trunc"); break; case FcOpListing: - printf ("Listing"); - FcOpFlagsPrint (op_); + fprintf (stream, "Listing"); + FcOpFlagsPrintFile (stream, op_); break; - case FcOpInvalid: printf ("Invalid"); break; + case FcOpInvalid: fprintf (stream, "Invalid"); break; } } void -FcExprPrint (const FcExpr *expr) +FcOpPrint (FcOp op_) +{ + FcOpPrintFile (stdout, op_); +} + +void +FcExprPrintFile (FILE *stream, const FcExpr *expr) { if (!expr) - printf ("none"); + fprintf (stream, "none"); else switch (FC_OP_GET_OP (expr->op)) { - case FcOpInteger: printf ("%d", expr->u.ival); break; - case FcOpDouble: printf ("%g", expr->u.dval); break; - case FcOpString: printf ("\"%s\"", expr->u.sval); break; + case FcOpInteger: fprintf (stream, "%d", expr->u.ival); break; + case FcOpDouble: fprintf (stream, "%g", expr->u.dval); break; + case FcOpString: fprintf (stream, "\"%s\"", expr->u.sval); break; case FcOpMatrix: - printf ("["); - FcExprPrint (expr->u.mexpr->xx); - printf (" "); - FcExprPrint (expr->u.mexpr->xy); - printf ("; "); - FcExprPrint (expr->u.mexpr->yx); - printf (" "); - FcExprPrint (expr->u.mexpr->yy); - printf ("]"); + fprintf (stream, "["); + FcExprPrintFile (stream, expr->u.mexpr->xx); + fprintf (stream, " "); + FcExprPrintFile (stream, expr->u.mexpr->xy); + fprintf (stream, "; "); + FcExprPrintFile (stream, expr->u.mexpr->yx); + fprintf (stream, " "); + FcExprPrintFile (stream, expr->u.mexpr->yy); + fprintf (stream, "]"); break; case FcOpRange: - printf ("(%g, %g)", expr->u.rval->begin, expr->u.rval->end); + fprintf (stream, "(%g, %g)", expr->u.rval->begin, expr->u.rval->end); break; - case FcOpBool: printf ("%s", expr->u.bval ? "true" : "false"); break; - case FcOpCharSet: printf ("charset\n"); break; + case FcOpBool: fprintf (stream, "%s", expr->u.bval ? "true" : "false"); break; + case FcOpCharSet: fprintf (stream, "charset\n"); break; case FcOpLangSet: - printf ("langset:"); - FcLangSetPrint (expr->u.lval); - printf ("\n"); + fprintf (stream, "langset:"); + FcLangSetPrintFile (stream, expr->u.lval); + fprintf (stream, "\n"); break; - case FcOpNil: printf ("nil\n"); break; + case FcOpNil: fprintf (stream, "nil\n"); break; case FcOpField: - printf ("%s ", FcObjectName (expr->u.name.object)); + fprintf (stream, "%s ", FcObjectName (expr->u.name.object)); switch ((int)expr->u.name.kind) { case FcMatchPattern: - printf ("(pattern) "); + fprintf (stream, "(pattern) "); break; case FcMatchFont: - printf ("(font) "); + fprintf (stream, "(font) "); break; } break; - case FcOpConst: printf ("%s", expr->u.constant); break; + case FcOpConst: fprintf (stream, "%s", expr->u.constant); break; case FcOpQuest: - FcExprPrint (expr->u.tree.left); - printf (" quest "); - FcExprPrint (expr->u.tree.right->u.tree.left); - printf (" colon "); - FcExprPrint (expr->u.tree.right->u.tree.right); + FcExprPrintFile (stream, expr->u.tree.left); + fprintf (stream, " quest "); + FcExprPrintFile (stream, expr->u.tree.right->u.tree.left); + fprintf (stream, " colon "); + FcExprPrintFile (stream, expr->u.tree.right->u.tree.right); break; case FcOpAssign: case FcOpAssignReplace: @@ -404,81 +456,87 @@ FcExprPrint (const FcExpr *expr) case FcOpTimes: case FcOpDivide: case FcOpComma: - FcExprPrint (expr->u.tree.left); - printf (" "); + FcExprPrintFile (stream, expr->u.tree.left); + fprintf (stream, " "); switch (FC_OP_GET_OP (expr->op)) { - case FcOpAssign: printf ("Assign"); break; - case FcOpAssignReplace: printf ("AssignReplace"); break; - case FcOpPrependFirst: printf ("PrependFirst"); break; - case FcOpPrepend: printf ("Prepend"); break; - case FcOpAppend: printf ("Append"); break; - case FcOpAppendLast: printf ("AppendLast"); break; - case FcOpOr: printf ("Or"); break; - case FcOpAnd: printf ("And"); break; + case FcOpAssign: fprintf (stream, "Assign"); break; + case FcOpAssignReplace: fprintf (stream, "AssignReplace"); break; + case FcOpPrependFirst: fprintf (stream, "PrependFirst"); break; + case FcOpPrepend: fprintf (stream, "Prepend"); break; + case FcOpAppend: fprintf (stream, "Append"); break; + case FcOpAppendLast: fprintf (stream, "AppendLast"); break; + case FcOpOr: fprintf (stream, "Or"); break; + case FcOpAnd: fprintf (stream, "And"); break; case FcOpEqual: - printf ("Equal"); - FcOpFlagsPrint (expr->op); + fprintf (stream, "Equal"); + FcOpFlagsPrintFile (stream, expr->op); break; case FcOpNotEqual: - printf ("NotEqual"); - FcOpFlagsPrint (expr->op); + fprintf (stream, "NotEqual"); + FcOpFlagsPrintFile (stream, expr->op); break; - case FcOpLess: printf ("Less"); break; - case FcOpLessEqual: printf ("LessEqual"); break; - case FcOpMore: printf ("More"); break; - case FcOpMoreEqual: printf ("MoreEqual"); break; - case FcOpContains: printf ("Contains"); break; + case FcOpLess: fprintf (stream, "Less"); break; + case FcOpLessEqual: fprintf (stream, "LessEqual"); break; + case FcOpMore: fprintf (stream, "More"); break; + case FcOpMoreEqual: fprintf (stream, "MoreEqual"); break; + case FcOpContains: fprintf (stream, "Contains"); break; case FcOpListing: - printf ("Listing"); - FcOpFlagsPrint (expr->op); + fprintf (stream, "Listing"); + FcOpFlagsPrintFile (stream, expr->op); break; - case FcOpNotContains: printf ("NotContains"); break; - case FcOpPlus: printf ("Plus"); break; - case FcOpMinus: printf ("Minus"); break; - case FcOpTimes: printf ("Times"); break; - case FcOpDivide: printf ("Divide"); break; - case FcOpComma: printf ("Comma"); break; + case FcOpNotContains: fprintf (stream, "NotContains"); break; + case FcOpPlus: fprintf (stream, "Plus"); break; + case FcOpMinus: fprintf (stream, "Minus"); break; + case FcOpTimes: fprintf (stream, "Times"); break; + case FcOpDivide: fprintf (stream, "Divide"); break; + case FcOpComma: fprintf (stream, "Comma"); break; default: break; } - printf (" "); - FcExprPrint (expr->u.tree.right); + fprintf (stream, " "); + FcExprPrintFile (stream, expr->u.tree.right); break; case FcOpNot: - printf ("Not "); - FcExprPrint (expr->u.tree.left); + fprintf (stream, "Not "); + FcExprPrintFile (stream, expr->u.tree.left); break; case FcOpFloor: - printf ("Floor "); - FcExprPrint (expr->u.tree.left); + fprintf (stream, "Floor "); + FcExprPrintFile (stream, expr->u.tree.left); break; case FcOpCeil: - printf ("Ceil "); - FcExprPrint (expr->u.tree.left); + fprintf (stream, "Ceil "); + FcExprPrintFile (stream, expr->u.tree.left); break; case FcOpRound: - printf ("Round "); - FcExprPrint (expr->u.tree.left); + fprintf (stream, "Round "); + FcExprPrintFile (stream, expr->u.tree.left); break; case FcOpTrunc: - printf ("Trunc "); - FcExprPrint (expr->u.tree.left); + fprintf (stream, "Trunc "); + FcExprPrintFile (stream, expr->u.tree.left); break; - case FcOpInvalid: printf ("Invalid"); break; + case FcOpInvalid: fprintf (stream, "Invalid"); break; } } void -FcTestPrint (const FcTest *test) +FcExprPrint (const FcExpr *expr) +{ + FcExprPrintFile (stdout, expr); +} + +void +FcTestPrintFile (FILE *stream, const FcTest *test) { switch (test->kind) { case FcMatchPattern: - printf ("pattern "); + fprintf (stream, "pattern "); break; case FcMatchFont: - printf ("font "); + fprintf (stream, "font "); break; case FcMatchScan: - printf ("scan "); + fprintf (stream, "scan "); break; case FcMatchKindEnd: /* shouldn't be reached */ @@ -486,36 +544,48 @@ FcTestPrint (const FcTest *test) } switch (test->qual) { case FcQualAny: - printf ("any "); + fprintf (stream, "any "); break; case FcQualAll: - printf ("all "); + fprintf (stream, "all "); break; case FcQualFirst: - printf ("first "); + fprintf (stream, "first "); break; case FcQualNotFirst: - printf ("not_first "); + fprintf (stream, "not_first "); break; } - printf ("%s ", FcObjectName (test->object)); - FcOpPrint (test->op); - printf (" "); - FcExprPrint (test->expr); - printf ("\n"); + fprintf (stream, "%s ", FcObjectName (test->object)); + FcOpPrintFile (stream, test->op); + fprintf (stream, " "); + FcExprPrintFile (stream, test->expr); + fprintf (stream, "\n"); +} + +void +FcTestPrint (const FcTest *test) +{ + FcTestPrintFile (stdout, test); +} + +void +FcEditPrintFile (FILE *stream, const FcEdit *edit) +{ + fprintf (stream, "Edit %s ", FcObjectName (edit->object)); + FcOpPrintFile (stream, edit->op); + fprintf (stream, " "); + FcExprPrintFile (stream, edit->expr); } void FcEditPrint (const FcEdit *edit) { - printf ("Edit %s ", FcObjectName (edit->object)); - FcOpPrint (edit->op); - printf (" "); - FcExprPrint (edit->expr); + FcEditPrintFile (stdout, edit); } void -FcRulePrint (const FcRule *rule) +FcRulePrintFile (FILE *stream, const FcRule *rule) { FcRuleType last_type = FcRuleUnknown; const FcRule *r; @@ -524,44 +594,56 @@ FcRulePrint (const FcRule *rule) if (last_type != r->type) { switch (r->type) { case FcRuleTest: - printf ("[test]\n"); + fprintf (stream, "[test]\n"); break; case FcRuleEdit: - printf ("[edit]\n"); + fprintf (stream, "[edit]\n"); break; default: break; } last_type = r->type; } - printf ("\t"); + fprintf (stream, "\t"); switch (r->type) { case FcRuleTest: - FcTestPrint (r->u.test); + FcTestPrintFile (stream, r->u.test); break; case FcRuleEdit: - FcEditPrint (r->u.edit); - printf (";\n"); + FcEditPrintFile (stream, r->u.edit); + fprintf (stream, ";\n"); break; default: break; } } - printf ("\n"); + fprintf (stream, "\n"); } void -FcFontSetPrint (const FcFontSet *s) +FcRulePrint (const FcRule *rule) +{ + FcRulePrintFile (stdout, rule); +} + +void +FcFontSetPrintFile (FILE *stream, const FcFontSet *s) { int i; - printf ("FontSet %d of %d\n", s->nfont, s->sfont); + fprintf (stream, "FontSet %d of %d\n", s->nfont, s->sfont); for (i = 0; i < s->nfont; i++) { - printf ("Font %d ", i); - FcPatternPrint (s->fonts[i]); + fprintf (stream, "Font %d ", i); + FcPatternPrintFile (stream, s->fonts[i]); } } +void +FcFontSetPrint (const FcFontSet *s) +{ + FcFontSetPrintFile (stdout, s); +} + int FcDebugVal; void @@ -572,7 +654,7 @@ FcInitDebug (void) e = getenv ("FC_DEBUG"); if (e) { - printf ("FC_DEBUG=%s\n", e); + fprintf (stderr, "FC_DEBUG=%s\n", e); FcDebugVal = atoi (e); if (FcDebugVal < 0) FcDebugVal = 0; diff --git a/src/fcfreetype.c b/src/fcfreetype.c index aa1c0e2..9a7f1a0 100644 --- a/src/fcfreetype.c +++ b/src/fcfreetype.c @@ -2074,6 +2074,37 @@ FcFreeTypeQueryFaceInternal (const FT_Face face, if (!FcPatternObjectAddString (pat, FC_FONT_WRAPPER_OBJECT, wrapper)) goto bail2; + { + FcPatternElt *elt; + FcValueListPtr l; + int generic_family = FC_FAMILY_UNKNOWN; + + elt = FcPatternObjectFindElt (pat, FC_FAMILY_OBJECT); + for (l = FcPatternEltValues (elt); l; l = FcValueListNext (l)) { + FcValue v = FcValueCanonicalize (&l->value); + + if (v.type == FcTypeString) { + if (FcStrStrIgnoreCase (v.u.s, (FcChar8 *)"mono")) { + generic_family = FC_FAMILY_MONO; + break; + } else if (FcStrStrIgnoreCase (v.u.s, (FcChar8 *)"sans")) { + generic_family = FC_FAMILY_SANS; + break; + } else if (FcStrStrIgnoreCase (v.u.s, (FcChar8 *)"serif")) { + generic_family = FC_FAMILY_SERIF; + break; + } else if (FcStrStrIgnoreCase (v.u.s, (FcChar8 *)"emoji")) { + generic_family = FC_FAMILY_EMOJI; + break; + } else if (FcStrStrIgnoreCase (v.u.s, (FcChar8 *)"math")) { + generic_family = FC_FAMILY_MATH; + break; + } + } + } + FcPatternObjectAddInteger(pat, FC_GENERIC_FAMILY_OBJECT, generic_family); + } + /* * Drop our reference to the charset */ diff --git a/src/fcint.h b/src/fcint.h index 9f03329..6ffd2c2 100644 --- a/src/fcint.h +++ b/src/fcint.h @@ -910,41 +910,83 @@ FcReadLink (const FcChar8 *pathname, /* fcdbg.c */ FcPrivate void -FcValuePrintFile (FILE *f, const FcValue v); +FcValuePrintFile (FILE *stream, const FcValue v); + +FcPrivate void +FcValuePrintFileWithPosition (FILE *stream, const FcValue v, FcBool show_pos_mark); FcPrivate void FcValuePrintWithPosition (const FcValue v, FcBool show_pos_mark); +FcPrivate void +FcValueListPrintFileWithPosition (FILE *stream, FcValueListPtr l, const FcValueListPtr pos); + FcPrivate void FcValueListPrintWithPosition (FcValueListPtr l, const FcValueListPtr pos); +FcPrivate void +FcValueListPrintFile (FILE *stream, FcValueListPtr l); + FcPrivate void FcValueListPrint (FcValueListPtr l); +FcPrivate void +FcLangSetPrintFile (FILE *stream, const FcLangSet *ls); + FcPrivate void FcLangSetPrint (const FcLangSet *ls); +FcPrivate void +FcOpPrintFile (FILE *stream, FcOp op_); + FcPrivate void FcOpPrint (FcOp op); +FcPrivate void +FcTestPrintFile (FILE *stream, const FcTest *test); + FcPrivate void FcTestPrint (const FcTest *test); +FcPrivate void +FcExprPrintFile (FILE *stream, const FcExpr *expr); + FcPrivate void FcExprPrint (const FcExpr *expr); +FcPrivate void +FcEditPrintFile (FILE *stream, const FcEdit *edit); + FcPrivate void FcEditPrint (const FcEdit *edit); +FcPrivate void +FcRulePrintFile (FILE *stream, const FcRule *rule); + FcPrivate void FcRulePrint (const FcRule *rule); +FcPrivate void +FcCharSetPrintFile (FILE *stream, const FcCharSet *c); + FcPrivate void FcCharSetPrint (const FcCharSet *c); +FcPrivate void +FcPatternPrintFile (FILE *stream, const FcPattern *p); + +FcPrivate void +FcPatternPrint2File (FILE *stream, + FcPattern *pp1, + FcPattern *pp2, + const FcObjectSet *os); + FcPrivate void FcPatternPrint2 (FcPattern *p1, FcPattern *p2, const FcObjectSet *os); +FcPrivate void +FcFontSetPrintFile (FILE *stream, const FcFontSet *s); + extern FcPrivate int FcDebugVal; #define FcDebug() (FcDebugVal) diff --git a/src/fcmatch.c b/src/fcmatch.c index d8523a1..6f71566 100644 --- a/src/fcmatch.c +++ b/src/fcmatch.c @@ -328,6 +328,7 @@ typedef enum _FcMatcherPriority { PRI1 (COLOR), PRI1 (FOUNDRY), PRI1 (CHARSET), + PRI1 (GENERIC_FAMILY), PRI_FAMILY_STRONG, PRI_POSTSCRIPT_NAME_STRONG, PRI1 (LANG), diff --git a/src/fcname.c b/src/fcname.c index fc173d7..4739885 100644 --- a/src/fcname.c +++ b/src/fcname.c @@ -203,6 +203,20 @@ static const FcConstant _FcBaseConstants[] = { { (FC8) "lcddefault", "lcdfilter", FC_LCD_DEFAULT }, { (FC8) "lcdlight", "lcdfilter", FC_LCD_LIGHT }, { (FC8) "lcdlegacy", "lcdfilter", FC_LCD_LEGACY }, + + { (FC8) "serif", "genericfamily", FC_FAMILY_SERIF }, + { (FC8) "sans-serif", "genericfamily", FC_FAMILY_SANS }, + { (FC8) "monospace", "genericfamily", FC_FAMILY_MONO }, + { (FC8) "cursive", "genericfamily", FC_FAMILY_CURSIVE }, + { (FC8) "fantasy", "genericfamily", FC_FAMILY_FANTASY }, + { (FC8) "system-ui", "genericfamily", FC_FAMILY_SYSTEM_UI }, + { (FC8) "ui-serif", "genericfamily", FC_FAMILY_UI_SERIF }, + { (FC8) "ui-sans-serif", "genericfamily", FC_FAMILY_UI_SANS }, + { (FC8) "ui-monospace", "genericfamily", FC_FAMILY_UI_MONO }, + { (FC8) "ui-rounded", "genericfamily", FC_FAMILY_UI_ROUNDED }, + { (FC8) "emoji", "genericfamily", FC_FAMILY_EMOJI }, + { (FC8) "math", "genericfamily", FC_FAMILY_MATH }, + { (FC8) "fangsong", "genericfamily", FC_FAMILY_FANGSONG }, }; #define NUM_FC_CONSTANTS (sizeof _FcBaseConstants / sizeof _FcBaseConstants[0]) diff --git a/src/fcobjs.h b/src/fcobjs.h index bc17a73..1de0ad0 100644 --- a/src/fcobjs.h +++ b/src/fcobjs.h @@ -77,4 +77,5 @@ FC_OBJECT (ORDER, FcTypeInteger, FcCompareNumber) FC_OBJECT (DESKTOP_NAME, FcTypeString, NULL) FC_OBJECT (NAMED_INSTANCE, FcTypeBool, FcCompareBool) FC_OBJECT (FONT_WRAPPER, FcTypeString, FcCompareString) +FC_OBJECT (GENERIC_FAMILY, FcTypeInteger, FcCompareNumber) /* ^-------------- Add new objects here. */ diff --git a/test/test-48-guessfamily.json b/test/test-48-guessfamily.json index 63b92d7..7dedbf8 100644 --- a/test/test-48-guessfamily.json +++ b/test/test-48-guessfamily.json @@ -1,6 +1,87 @@ { - "fonts": [], + "fonts": [ + { + "family": "Liberation Mono", + "style": "Regular", + "file": "/path/to/LiberationMono-Regular.ttf", + "fontversion": 1, + "genericfamily": 3 + }, + { + "family": "Liberation Sans", + "style": "Regular", + "file": "/path/to/LiberationSans-Regular.ttf", + "fontversion": 1, + "genericfamily": 2 + }, + { + "family": "Liberation Serif", + "style": "Regular", + "file": "/path/to/LiberationSerif-Regular.ttf", + "fontversion": 1, + "genericfamily": 1 + }, + { + "family": "Noto Sans Mono", + "style": "Regular", + "file": "/path/to/NotoSansMono-Regular.ttf", + "fontversion": 1, + "genericfamily": 3 + }, + ], "tests": [ + { + "method": "match", + "query": { + "family": "mono" + }, + "result": { + "family": "Liberation Mono", + "style": "Regular", + "file": "/path/to/LiberationMono-Regular.ttf", + "fontversion": 1, + "genericfamily": 3 + } + }, + { + "method": "match", + "query": { + "family": "sans" + }, + "result": { + "family": "Liberation Sans", + "style": "Regular", + "file": "/path/to/LiberationSans-Regular.ttf", + "fontversion": 1, + "genericfamily": 2 + } + }, + { + "method": "match", + "query": { + "family": "serif" + }, + "result": { + "family": "Liberation Serif", + "style": "Regular", + "file": "/path/to/LiberationSerif-Regular.ttf", + "fontversion": 1, + "genericfamily": 1 + } + }, + { + "method": "match", + "query": { + "genericfamily": "sans-serif" + }, + "result": { + "family": "Liberation Sans", + "style": "Regular", + "file": "/path/to/LiberationSans-Regular.ttf", + "fontversion": 1, + "genericfamily": 2 + } + }, { "method": "pattern", "query": { @@ -27,6 +108,15 @@ "result": { "family": ["Foo Serif", "serif"] } + }, + { + "method": "pattern", + "query": { + "family": "Noto Sans Mono", + }, + "result": { + "family": ["Noto Sans Mono", "monospace"] + } } ] } diff --git a/test/test-49-sansserif.json b/test/test-49-sansserif.json new file mode 100644 index 0000000..80b10ae --- /dev/null +++ b/test/test-49-sansserif.json @@ -0,0 +1,66 @@ +{ + "fonts": [ + ], + "tests": [ + { + "method": "pattern", + "query": { + "genericfamily": "sans-serif" + }, + "result": { + "genericfamily": "sans-serif", + "family": ["sans-serif"] + } + }, + { + "method": "pattern", + "query": { + "genericfamily": "monospace" + }, + "result": { + "genericfamily": "monospace", + "family": ["monospace"] + } + }, + { + "method": "pattern", + "query": { + "genericfamily": "serif" + }, + "result": { + "genericfamily": "serif", + "family": ["serif"] + } + }, + { + "method": "pattern", + "query": { + "genericfamily": "emoji" + }, + "result": { + "genericfamily": "emoji", + "family": ["emoji", "sans-serif"] + } + }, + { + "method": "pattern", + "query": { + "genericfamily": "math" + }, + "result": { + "genericfamily": "math", + "family": ["math", "sans-serif"] + } + }, + { + "method": "pattern", + "query": { + "genericfamily": ["monospace", "sans-serif"] + }, + "result": { + "genericfamily": ["monospace", "sans-serif"], + "family": ["monospace", "sans-serif"] + } + } + ] +} diff --git a/test/test-conf.c b/test/test-conf.c index 1b5aa22..2eef27a 100644 --- a/test/test-conf.c +++ b/test/test-conf.c @@ -22,6 +22,10 @@ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + #include <fontconfig/fontconfig.h> #include <json.h> @@ -181,28 +185,58 @@ build_pattern (json_object *obj) v.type = FcTypeVoid; } continue; + } else if (fc_o && fc_o->type == FcTypeInteger) { + for (i = 0; i < n; i++) { + o = json_object_array_get_idx (iter.val, i); + type = json_object_get_type (o); + if (type == json_type_string) { + const FcConstant *c = FcNameGetConstant ((const FcChar8 *)json_object_get_string (o)); + if (!c) { + fprintf (stderr, "E: value is not a known constant\n"); + fprintf (stderr, " key: %s\n", iter.key); + fprintf (stderr, " val: %s (idx: %d)\n", json_object_get_string (iter.val), i); + continue; + } + if (strcmp (c->object, iter.key) != 0) { + fprintf (stderr, "E: value is a constant of different object\n"); + fprintf (stderr, " key: %s\n", iter.key); + fprintf (stderr, " val: %s (idx: %d)\n", json_object_get_string (iter.val), i); + fprintf (stderr, " key implied by value: %s\n", c->object); + continue; + } + v.u.i = c->value; + } else if (type != json_type_int) { + fprintf (stderr, "E: unable to convert to int\n"); + continue; + } else { + v.u.i = json_object_get_int(o); + } + v.type = FcTypeInteger; + FcPatternAdd (pat, iter.key, v, FcTrue); + v.type = FcTypeVoid; + } } else { FcLangSet *ls = FcLangSetCreate(); if (!ls) { - fprintf (stderr, "E: failed to create langset\n"); - continue; + fprintf (stderr, "E: failed to create langset\n"); + continue; } v.type = FcTypeLangSet; v.u.l = ls; destroy_v = FcTrue; for (i = 0; i < n; i++) { - o = json_object_array_get_idx (iter.val, i); - type = json_object_get_type (o); - if (type != json_type_string) { + o = json_object_array_get_idx (iter.val, i); + type = json_object_get_type (o); + if (type != json_type_string) { fprintf (stderr, "E: langset value not string\n"); FcValueDestroy (v); continue; - } - if (FcLangSetAdd (ls, (const FcChar8 *)json_object_get_string (o)) == FcFalse) { + } + if (FcLangSetAdd (ls, (const FcChar8 *)json_object_get_string (o)) == FcFalse) { fprintf (stderr, "E: failed to add to langset\n"); FcValueDestroy (v); continue; - } + } } } } else if (type == json_type_double || type == json_type_int) { @@ -565,8 +599,8 @@ process_pattern (FcConfig *config, FcPattern *query, FcPattern *result) { - FcPatternIter iter; - int x, vc, fail = 0; + FcPatternIter iter1, iter2; + int vc1, vc2, fail = 0, i; if (!query) { fprintf (stderr, "E: no query defined.\n"); @@ -580,32 +614,41 @@ process_pattern (FcConfig *config, } FcConfigSubstitute (config, query, FcMatchPattern); - FcPatternIterStart (result, &iter); + FcPatternIterStart (query, &iter1); + FcPatternIterStart (result, &iter2); do { - vc = FcPatternIterValueCount (result, &iter); - for (x = 0; x < vc; x++) { - FcValue vr, vp; + const char *obj = FcPatternIterGetObject (result, &iter2); - if (FcPatternIterGetValue (result, &iter, x, &vr, NULL) != FcResultMatch) { - fprintf (stderr, "E: unable to obtain a value from the expected result\n"); - fail++; - goto bail; - } - if (FcPatternGet (query, FcPatternIterGetObject (result, &iter), x, &vp) != FcResultMatch) { - vp.type = FcTypeVoid; - } - if (!FcValueEqual (vp, vr)) { - printf ("E: failed to compare %s:\n", FcPatternIterGetObject (result, &iter)); - printf (" actual result:"); - FcValuePrint (vp); - printf ("\n expected result:"); - FcValuePrint (vr); - printf ("\n"); - fail++; - goto bail; + if (!FcPatternFindIter (query, &iter1, obj)) { + fprintf (stderr, "E: object (%s) not found in actual result\n", obj); + } else { + vc1 = FcPatternIterValueCount (query, &iter1); + vc2 = FcPatternIterValueCount (result, &iter2); + if (vc1 != vc2 || !FcPatternIterEqual (query, &iter1, result, &iter2)) { + FcValue v1, v2; + FcValueBinding b1, b2; + + fprintf (stderr, "E: object (%s) mismatched:\n", obj); + fprintf (stderr, " actual result: %d\n ", vc1); + for (i = 0; i < vc1; i++) { + if (FcPatternIterGetValue (query, &iter1, i, &v1, &b1) != FcResultMatch) + v1.type = FcTypeVoid; + FcValuePrint (v1); + fprintf (stderr, " "); + } + fprintf (stderr, "\n"); + fprintf (stderr, " expected result: %d\n ", vc2); + for (i = 0; i < vc1; i++) { + if (FcPatternIterGetValue (result, &iter2, i, &v2, &b2) != FcResultMatch) + v2.type = FcTypeVoid; + FcValuePrint (v2); + fprintf (stderr, " "); + } + fail++; + goto bail; } } - } while (FcPatternIterNext (result, &iter)); + } while (FcPatternIterNext (result, &iter2)); bail: return fail; }