]> git.wh0rd.org - fontconfig.git/commitdiff
Add FcMatchScan to resolve Delicious font matching issues (bug #6769)
authorKeith Packard <keithp@neko.keithp.com>
Sun, 3 Sep 2006 00:52:12 +0000 (17:52 -0700)
committerKeith Packard <keithp@neko.keithp.com>
Sun, 3 Sep 2006 00:52:12 +0000 (17:52 -0700)
The Delicious family includes one named Delicious Heavy, a bold variant
which is unfortunately marked as having normal weight. Because the family
name is 'Delicious', fontconfig accidentally selects this font instead of
the normal weight variant. The fix here rewrites the scanned data by running
the scanned pattern through a new substitution sequence tagged with
<match target=scan>; a sample for the Delicious family is included to
demonstrate how it works (and fix Delicious at the same time).

Also added was a new match predicate -- the 'decorative' predicate which is
automatically detected in fonts by searching style names for key decorative
phrases like SmallCaps, Shadow, Embosed and Antiqua. Suggestions for
additional decorative key words are welcome. This should have little effect
on font matching except when two fonts share the same characteristics except
for this value.

13 files changed:
conf.d/Makefile.am
doc/fontconfig-user.sgml
fontconfig/fontconfig.h
fonts.dtd
src/fccfg.c
src/fcdbg.c
src/fcdefault.c
src/fcdir.c
src/fcfreetype.c
src/fcint.h
src/fcmatch.c
src/fcname.c
src/fcxml.c

index 443d8a9d12a8ffe8c5d5bd2646e69e1e06a766b2..98e8dbd75030416c2a1cfef7f5d84695b3041ea7 100644 (file)
@@ -24,6 +24,7 @@
 CONF_FILES = \
        10LohitGujarati.conf \
        10-fonts-persian.conf \
+       60-delicious.conf \
        autohint.conf \
        no-bitmaps.conf \
        no-sub-pixel.conf \
index 32a637c69a37c0146ca12835793220493a086da5..a1f6b39b807cc3422a6501bbfed73e7ba0d2e0cd 100644 (file)
@@ -382,11 +382,13 @@ This element holds first a (possibly empty) list of <sgmltag>test</> elements an
 a (possibly empty) list of <sgmltag>edit</> elements.  Patterns which match all of the
 tests are subjected to all the edits.  If 'target' is set to "font" instead
 of the default "pattern", then this element applies to the font name
-resulting from a match rather than a font pattern to be matched.
+resulting from a match rather than a font pattern to be matched. If 'target'
+is set to "scan", then this element applies when the font is scanned to
+build the fontconfig database.
   </para></refsect2>
   <refsect2><title><sgmltag>test qual="any" name="property" target="default" compare="eq"</></title><para>
 This element contains a single value which is compared with the target
-('pattern', 'font' or 'default') property "property" (substitute any of the property names seen 
+('pattern', 'font', 'scan' or 'default') property "property" (substitute any of the property names seen 
 above). 'compare' can be one of "eq", "not_eq", "less", "less_eq", "more", or
 "more_eq".  'qual' may either be the default, "any", in which case the match
 succeeds if any value associated with the property matches the test value, or
index feb781ed8157708ffc9d5712a6606db74e534302..15e8f7fffba0d8cece4fe9efc718d367d0a0ac5b 100644 (file)
@@ -103,6 +103,7 @@ typedef int         FcBool;
 #define FC_FONTFORMAT      "fontformat"        /* String */
 #define FC_EMBOLDEN        "embolden"          /* Bool - true if emboldening needed*/
 #define FC_EMBEDDED_BITMAP  "embeddedbitmap"   /* Bool - true to enable embedded bitmaps */
+#define FC_DECORATIVE      "decorative"        /* Bool - true if style is a decorative variant */
 
 #define FC_CACHE_SUFFIX                    ".cache-"FC_CACHE_VERSION
 #define FC_DIR_CACHE_FILE          "fonts.cache-"FC_CACHE_VERSION
@@ -236,7 +237,7 @@ typedef struct _FcObjectSet {
 } FcObjectSet;
     
 typedef enum _FcMatchKind {
-    FcMatchPattern, FcMatchFont
+    FcMatchPattern, FcMatchFont, FcMatchScan
 } FcMatchKind;
 
 typedef enum _FcLangResult {
index cd016d3ca9035a10750eccd58b16504bf739c003..06f1a1241a99434ba3e702de34954977e75dc4c7 100644 (file)
--- a/fonts.dtd
+++ b/fonts.dtd
 -->
 <!ELEMENT match (test*, edit*)>
 <!ATTLIST match
-         target (pattern|font) "pattern">
+         target (pattern|font|scan) "pattern">
 
 <!--
     Match a field in a pattern
     For match elements with target=font, if test 'target' is 'pattern',
     then the test is applied to the pattern used in matching rather than
     to the resulting font.
+
+    Match elements with target=scan are applied as fonts are scanned.
+    They edit the pattern generated from the scanned font and affect
+    what the fontconfig database contains.
 -->
 <!ELEMENT test (%expr;)*>
 <!ATTLIST test 
index cec0e4a795b2d6b82ce0989a6b0b1cff4e20ad98..3cea2c9406940244facfb3b3ccfb28f8085648d8 100644 (file)
@@ -561,14 +561,23 @@ FcConfigAddEdit (FcConfig *config,
     FcTest     *t;
     int                num;
 
+    switch (kind) {
+    case FcMatchPattern:
+       prev = &config->substPattern;
+       break;
+    case FcMatchFont:
+       prev = &config->substFont;
+       break;
+    case FcMatchScan:
+       prev = &config->substScan;
+       break;
+    default:
+       return FcFalse;
+    }
     subst = (FcSubst *) malloc (sizeof (FcSubst));
     if (!subst)
        return FcFalse;
     FcMemAlloc (FC_MEM_SUBST, sizeof (FcSubst));
-    if (kind == FcMatchPattern)
-       prev = &config->substPattern;
-    else
-       prev = &config->substFont;
     for (; *prev; prev = &(*prev)->next);
     *prev = subst;
     subst->next = 0;
@@ -1290,6 +1299,20 @@ FcConfigSubstituteWithPat (FcConfig    *config,
            return FcFalse;
     }
 
+    switch (kind) {
+    case FcMatchPattern:
+       s = config->substPattern;
+       break;
+    case FcMatchFont:
+       s = config->substFont;
+       break;
+    case FcMatchScan:
+       s = config->substScan;
+       break;
+    default:
+       return FcFalse;
+    }
+
     st = (FcSubState *) malloc (config->maxObjects * sizeof (FcSubState));
     if (!st && config->maxObjects)
        return FcFalse;
@@ -1300,10 +1323,6 @@ FcConfigSubstituteWithPat (FcConfig    *config,
        printf ("FcConfigSubstitute ");
        FcPatternPrint (p);
     }
-    if (kind == FcMatchPattern)
-       s = config->substPattern;
-    else
-       s = config->substFont;
     for (; s; s = s->next)
     {
        /*
index c82dd3aa2a76ae4d077d40696118be4486a323c8..98b57e6c38b16de02745b68a3a00d0f0a25c6f19 100644 (file)
@@ -103,6 +103,7 @@ FcCharSetPrint (const FcCharSet *c)
     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);
@@ -113,12 +114,15 @@ FcCharSetPrint (const FcCharSet *c)
                i, numbers[i], leaves[i], 
                (intptr_t) FcOffsetToPtr (leaves, leaves[i], FcCharLeaf));
     }
+#endif
                
     for (i = 0; i < c->num; i++)
     {
        intptr_t        leaf_offset = leaves[i];
        FcCharLeaf      *leaf = FcOffsetToPtr (leaves, leaf_offset, FcCharLeaf);
        
+       if (i)
+           printf ("\t");
        printf ("%04x:", numbers[i]);
        for (j = 0; j < 256/32; j++)
            printf (" %08x", leaf->map[j]);
@@ -323,6 +327,9 @@ FcTestPrint (const FcTest *test)
     case FcMatchFont:
        printf ("font ");
        break;
+    case FcMatchScan:
+       printf ("scan ");
+       break;
     }
     switch (test->qual) {
     case FcQualAny:
index b582310c2361f5338f7ada5b4f918520b227fd14..b69de308843e5d61ac0b7f98800ee28aa4947a35 100644 (file)
@@ -34,6 +34,7 @@ static const struct {
     { FC_AUTOHINT_OBJECT,         FcFalse      },  /* FC_LOAD_FORCE_AUTOHINT */
     { FC_GLOBAL_ADVANCE_OBJECT,    FcTrue      },  /* !FC_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH */
     { FC_EMBEDDED_BITMAP_OBJECT,   FcTrue      },  /* !FC_LOAD_NO_BITMAP */
+    { FC_DECORATIVE_OBJECT,       FcFalse      },
 };
 
 #define NUM_FC_BOOL_DEFAULTS   (int) (sizeof FcBoolDefaults / sizeof FcBoolDefaults[0])
index 8da50e93c282f75eeb8c60f23783861e4bfcafd0..a55c5fd3a47f5d301f58887c6d141b26d663683a 100644 (file)
@@ -61,15 +61,31 @@ FcFileScanFontConfig (FcFontSet             *set,
        font = FcFreeTypeQuery (file, id, blanks, &count);
        if (FcDebug () & FC_DBG_SCAN)
            printf ("done\n");
+
+       /*
+        * Edit pattern with user-defined rules
+        */
+       if (config && !FcConfigSubstituteWithPat (config, font, NULL, FcMatchScan))
+       {
+           FcPatternDestroy (font);
+           font = NULL;
+           ret = FcFalse;
+       }
+
        /*
         * Add the font
         */
        if (font && (!config || FcConfigAcceptFont (config, font)))
        {
+           if (FcDebug() & FC_DBG_SCANV)
+           {
+               printf ("Final font pattern:\n");
+               FcPatternPrint (font);
+           }
            if (!FcFontSetAdd (set, font))
            {
                FcPatternDestroy (font);
-               font = 0;
+               font = NULL;
                ret = FcFalse;
            }
        }
index 082d17bfb8865010c5ac38fdda737f572ca11f62..3cf1686d96c3f3b5a48ecebc1c5e9295af94e9fa 100644 (file)
@@ -954,9 +954,10 @@ static const FcStringConst  weightConsts[] = {
     { (FC8) "demibold",                FC_WEIGHT_DEMIBOLD },
     { (FC8) "demi",            FC_WEIGHT_DEMIBOLD },
     { (FC8) "semibold",                FC_WEIGHT_SEMIBOLD },
-    { (FC8) "bold",            FC_WEIGHT_BOLD },
     { (FC8) "extrabold",       FC_WEIGHT_EXTRABOLD },
+    { (FC8) "superbold",       FC_WEIGHT_EXTRABOLD },
     { (FC8) "ultrabold",       FC_WEIGHT_ULTRABOLD },
+    { (FC8) "bold",            FC_WEIGHT_BOLD },
     { (FC8) "black",           FC_WEIGHT_BLACK },
     { (FC8) "heavy",           FC_WEIGHT_HEAVY },
 };
@@ -985,6 +986,7 @@ static const FcStringConst  widthConsts[] = {
 
 static const FcStringConst  slantConsts[] = {
     { (FC8) "italic",          FC_SLANT_ITALIC },
+    { (FC8) "kursiv",          FC_SLANT_ITALIC },
     { (FC8) "oblique",         FC_SLANT_OBLIQUE },
 };
 
@@ -993,6 +995,20 @@ static const FcStringConst  slantConsts[] = {
 #define FcIsSlant(s)       FcStringIsConst(s,slantConsts,NUM_SLANT_CONSTS)
 #define FcContainsSlant(s)  FcStringContainsConst (s,slantConsts,NUM_SLANT_CONSTS)
 
+static const FcStringConst  decorativeConsts[] = {
+    { (FC8) "shadow",          FcTrue },
+    { (FC8) "smallcaps",       FcTrue },
+    { (FC8) "antiqua",         FcTrue },
+    { (FC8) "romansc",         FcTrue },
+    { (FC8) "embosed",         FcTrue },
+    { (FC8) "romansmallcaps",  FcTrue },
+};
+
+#define NUM_DECORATIVE_CONSTS  (int) (sizeof (decorativeConsts) / sizeof (decorativeConsts[0]))
+
+#define FcIsDecorative(s)   FcStringIsConst(s,decorativeConsts,NUM_DECORATIVE_CONSTS)
+#define FcContainsDecorative(s)        FcStringContainsConst (s,decorativeConsts,NUM_DECORATIVE_CONSTS)
+
 static double
 FcGetPixelSize (FT_Face face, int i)
 {
@@ -1038,6 +1054,7 @@ FcFreeTypeQuery (const FcChar8    *file,
     int                    slant = -1;
     int                    weight = -1;
     int                    width = -1;
+    FcBool         decorative = FcFalse;
     int                    i;
     FcCharSet      *cs;
     FcLangSet      *ls;
@@ -1413,6 +1430,9 @@ FcFreeTypeQuery (const FcChar8    *file,
            weight = FC_WEIGHT_EXTRABOLD;
        else if (os2->usWeightClass < 950)
            weight = FC_WEIGHT_BLACK;
+       if ((FcDebug() & FC_DBG_SCANV) && weight != -1)
+           printf ("\tos2 weight class %d maps to weight %d\n",
+                   os2->usWeightClass, weight);
 
        switch (os2->usWidthClass) {
        case 1: width = FC_WIDTH_ULTRACONDENSED; break;
@@ -1425,6 +1445,9 @@ FcFreeTypeQuery (const FcChar8    *file,
        case 8: width = FC_WIDTH_EXTRAEXPANDED; break;
        case 9: width = FC_WIDTH_ULTRAEXPANDED; break;
        }
+       if ((FcDebug() & FC_DBG_SCANV) && width != -1)
+           printf ("\tos2 width class %d maps to width %d\n",
+                   os2->usWidthClass, width);
     }
     if (os2 && (complex = FcFontCapabilities(face)))
     {
@@ -1540,6 +1563,12 @@ FcFreeTypeQuery (const FcChar8   *file,
            if (FcDebug() & FC_DBG_SCANV)
                printf ("\tStyle %s maps to slant %d\n", style, slant);
        }
+       if (decorative == FcFalse)
+       {
+           decorative = FcContainsDecorative (style) > 0;
+           if (FcDebug() & FC_DBG_SCANV)
+               printf ("\tStyle %s maps to decorative %d\n", style, decorative);
+       }
     }
     /*
      * Pull default values from the FreeType flags if more
@@ -1577,6 +1606,9 @@ FcFreeTypeQuery (const FcChar8    *file,
     if (!FcPatternAddString (pat, FC_FOUNDRY, foundry))
        goto bail1;
 
+    if (!FcPatternAddBool (pat, FC_DECORATIVE, decorative))
+       goto bail1;
+
     /*
      * Compute the unicode coverage for the font
      */
index 78d17e788924d76405e2e85756c6b4d95d03423d..3b9f3c852697c7259a00ce006a21cf8278a67028 100644 (file)
@@ -452,6 +452,7 @@ struct _FcConfig {
      */
     FcSubst    *substPattern;      /* substitutions for patterns */
     FcSubst    *substFont;         /* substitutions for fonts */
+    FcSubst    *substScan;         /* substitutions for scanned fonts */
     int                maxObjects;         /* maximum number of tests in all substs */
     /*
      * List of patterns used to control font file selection
@@ -875,6 +876,7 @@ FcListPatternMatchAny (const FcPattern *p,
 #define FC_FONTFORMAT_OBJECT   37
 #define FC_EMBOLDEN_OBJECT     38
 #define FC_EMBEDDED_BITMAP_OBJECT      39
+#define FC_DECORATIVE_OBJECT   40
 
 FcBool
 FcNameBool (const FcChar8 *v, FcBool *result);
index e7a70ef1f13bd6390fa44a803777f4606476ab71..96587cbc310d41ac6aef92dc683ad5910f015dd5 100644 (file)
@@ -225,24 +225,29 @@ static FcMatcher _FcMatchers [] = {
 #define MATCH_WIDTH        9
 #define MATCH_WIDTH_INDEX   10
     
-    { FC_ANTIALIAS_OBJECT,     FcCompareBool,  11, 11 },
-#define MATCH_ANTIALIAS            10
-#define MATCH_ANTIALIAS_INDEX      11
+    { FC_DECORATIVE_OBJECT,    FcCompareBool,          11, 11 },
+#define MATCH_DECORATIVE       11
+#define MATCH_DECORATIVE_INDEX 12
+
+    { FC_ANTIALIAS_OBJECT,     FcCompareBool,          12, 12 },
+    
+#define MATCH_ANTIALIAS                    11
+#define MATCH_ANTIALIAS_INDEX      12
     
-    { FC_RASTERIZER_OBJECT,    FcCompareString,        12, 12 },
-#define MATCH_RASTERIZER    11
-#define MATCH_RASTERIZER_INDEX    12
+    { FC_RASTERIZER_OBJECT,    FcCompareString,        13, 13 },
+#define MATCH_RASTERIZER           12
+#define MATCH_RASTERIZER_INDEX     12
 
-    { FC_OUTLINE_OBJECT,       FcCompareBool,  13, 13 },
-#define MATCH_OUTLINE      12
-#define MATCH_OUTLINE_INDEX        13
+    { FC_OUTLINE_OBJECT,       FcCompareBool,          14, 14 },
+#define MATCH_OUTLINE              13
+#define MATCH_OUTLINE_INDEX        14
 
-    { FC_FONTVERSION_OBJECT,   FcCompareNumber,        14, 14 },
-#define MATCH_FONTVERSION   13
-#define MATCH_FONTVERSION_INDEX   14
+    { FC_FONTVERSION_OBJECT,   FcCompareNumber,        15, 15 },
+#define MATCH_FONTVERSION          14
+#define MATCH_FONTVERSION_INDEX            15
 };
 
-#define NUM_MATCH_VALUES    15
+#define NUM_MATCH_VALUES    16
 
 static FcMatcher*
 FcObjectToMatcher (FcObject object)
@@ -279,6 +284,8 @@ FcObjectToMatcher (FcObject object)
        i = MATCH_RASTERIZER; break;
     case FC_OUTLINE_OBJECT:
        i = MATCH_OUTLINE; break;
+    case FC_DECORATIVE_OBJECT:
+       i = MATCH_DECORATIVE; break;
     }
 
     if (i < 0)
index f55190d9e85bde1da58313e7a53b37213c64c533..6f74bb14f6beea23fbbbd3a4ecd35123ef98a3d7 100644 (file)
@@ -73,7 +73,8 @@ static const FcObjectType _FcBaseObjectTypes[] = {
     { FC_CAPABILITY,   FcTypeString },
     { FC_FONTFORMAT,   FcTypeString },
     { FC_EMBOLDEN,     FcTypeBool },
-    { FC_EMBEDDED_BITMAP,   FcTypeBool }, /* 39 */
+    { FC_EMBEDDED_BITMAP,   FcTypeBool },
+    { FC_DECORATIVE,   FcTypeBool }, /* 40 */
 };
 
 #define NUM_OBJECT_TYPES    (sizeof _FcBaseObjectTypes / sizeof _FcBaseObjectTypes[0])
@@ -390,6 +391,7 @@ static const FcConstant _FcBaseConstants[] = {
     { (FcChar8 *) "extrabold",     "weight",   FC_WEIGHT_EXTRABOLD, },
     { (FcChar8 *) "ultrabold",     "weight",   FC_WEIGHT_EXTRABOLD, },
     { (FcChar8 *) "black",         "weight",   FC_WEIGHT_BLACK, },
+    { (FcChar8 *) "heavy",         "weight",   FC_WEIGHT_HEAVY, },
 
     { (FcChar8 *) "roman",         "slant",    FC_SLANT_ROMAN, },
     { (FcChar8 *) "italic",        "slant",    FC_SLANT_ITALIC, },
@@ -421,6 +423,18 @@ static const FcConstant _FcBaseConstants[] = {
     { (FcChar8 *) "hintslight",            "hintstyle",   FC_HINT_SLIGHT },
     { (FcChar8 *) "hintmedium",            "hintstyle",   FC_HINT_MEDIUM },
     { (FcChar8 *) "hintfull",      "hintstyle",   FC_HINT_FULL },
+
+    { (FcChar8 *) "antialias",     "antialias",    FcTrue },
+    { (FcChar8 *) "hinting",       "hinting",      FcTrue },
+    { (FcChar8 *) "verticallayout", "verticallayout",  FcTrue },
+    { (FcChar8 *) "autohint",      "autohint",     FcTrue },
+    { (FcChar8 *) "globaladvance",  "globaladvance",   FcTrue },
+    { (FcChar8 *) "outline",       "outline",      FcTrue },
+    { (FcChar8 *) "scalable",      "scalable",     FcTrue },
+    { (FcChar8 *) "minspace",      "minspace",     FcTrue },
+    { (FcChar8 *) "embolden",      "embolden",     FcTrue },
+    { (FcChar8 *) "embeddedbitmap", "embeddedbitmap",  FcTrue },
+    { (FcChar8 *) "decorative",            "decorative",   FcTrue },
 };
 
 #define NUM_FC_CONSTANTS   (sizeof _FcBaseConstants/sizeof _FcBaseConstants[0])
@@ -660,7 +674,7 @@ FcNameParse (const FcChar8 *name)
                for (;;)
                {
                    name = FcNameFindNext (name, ":,", save, &delim);
-                   if (t && strcmp (t->object, _FcBaseObjectTypes[0].object))
+                   if (t)
                    {
                        v = FcNameConvert (t->type, save, &m);
                        if (!FcPatternAdd (pat, t->object, v, FcTrue))
@@ -696,8 +710,20 @@ FcNameParse (const FcChar8 *name)
            {
                if ((c = FcNameGetConstant (save)))
                {
-                   if (!FcPatternAddInteger (pat, c->object, c->value))
-                       goto bail2;
+                   t = FcNameGetObjectType ((char *) c->object);
+                   switch (t->type) {
+                   case FcTypeInteger:
+                   case FcTypeDouble:
+                       if (!FcPatternAddInteger (pat, c->object, c->value))
+                           goto bail2;
+                       break;
+                   case FcTypeBool:
+                       if (!FcPatternAddBool (pat, c->object, c->value))
+                           goto bail2;
+                       break;
+                   default:
+                       break;
+                   }
                }
            }
        }
index 5b7e19118fefb79bb8fb45fa905f8fd4dbbe2bb3..d91e52275161bab71913ac24bb3c12b0e6b44e4c 100644 (file)
@@ -1667,6 +1667,8 @@ FcParseTest (FcConfigParse *parse)
            kind = FcMatchPattern;
        else if (!strcmp ((char *) kind_string, "font"))
            kind = FcMatchFont;
+       else if (!strcmp ((char *) kind_string, "scan"))
+           kind = FcMatchScan;
        else if (!strcmp ((char *) kind_string, "default"))
            kind = FcMatchDefault;
        else
@@ -1821,6 +1823,8 @@ FcParseMatch (FcConfigParse *parse)
            kind = FcMatchPattern;
        else if (!strcmp ((char *) kind_name, "font"))
            kind = FcMatchFont;
+       else if (!strcmp ((char *) kind_name, "scan"))
+           kind = FcMatchScan;
        else
        {
            FcConfigMessage (parse, FcSevereWarning, "invalid match target \"%s\"", kind_name);