[Fontconfig] Re: fontconfig support to exclude glyphs from fonts

Jay Hobson Jay.Hobson at Sun.COM
Thu Aug 3 13:28:34 PDT 2006


Keith, Simos,

Both of you thought it would be useful to have a method to specify that 
a font has poor support for a particular language. This poor support for 
the language would cause other fonts with support for the language lower 
in the list to be selected in preference, but if no other font exists 
with support for the language, then the one with poor support would be 
selected. The attached patch solves this problem.

The following is an example of how to modify fonts.conf to use the path:

<alias>
    <family>Sans-serif</family>
    <poor>
       <family>DejaVu Sans</family>
       <lang>ar</lang>
    </poor>
<alias>

If a request for a font is made with one of the languages specified in 
the poor list, the match value for the font based on language is 
modified from a 0 for exact match, or 1 for same language different 
county to a 1.5. This indicates that it is worse than either of the two 
matching values, but better than a 2 which is language not present. This 
way, any font with better language matching values will be chosen first, 
but the 1.5 value still provides better support than fonts with a 2.

Let me know if there are other changes you would like or need.

Thanks,

Jay


Jay Hobson wrote:

> Keith Packard wrote:
>
>> On Thu, 2006-06-29 at 10:43 -0700, Jay Hobson wrote:
>>  
>>
>>> Keith,
>>>
>>> While I can see the need for a ban on unicode codepoint sets, I 
>>> think many end users will find that horribly difficult to use and 
>>> set up. With the patch that I am providing, you can accomplish the 
>>> same basic goal without nearly as much hardship. I suspect it is 
>>> easier to look up what languages a font supports, than what the 
>>> unicode codepoint set is.
>>>   
>>
>>
>> That's a good point. Having the ability to *also* edit the language set
>> offered by a font makes good sense.
>>
>> I think what I was focused on was the notion that we should (finally)
>> look at editing the data about the available fonts instead of trying to
>> work around broken data during the match phase.
>>
>>  
>>
>>> Additionally, if you ban an entire language from a font, then the 
>>> language will be displayed with a different font and have a better 
>>> chance of looking consistent. If you ban a codepoint set, unless you 
>>> get the entire language coverage, you run the risk of having two 
>>> fonts display your text, which would likely look bad.
>>>   
>>
>>
>> Right. Let's work on a font pattern editing mechanism and then do both
>> code point and language editing. That should fix both problems nicely.
>>
>> The main reason for a code point ban is to elide latin glyphs from
>> non-latin fonts -- TrueType nearly insists that all fonts include latin
>> glyphs, which makes no sense for many non-latin fonts, and which often
>> results in really ugly latin glyphs being included.
>>  
>>
> One interesting twist to this, and the reason that I designed the 
> patch the way that I did, is that some of these same fonts that 
> include Latin glyphs can have latin characters that are matched nicely 
> to the other languages contained in the font (not often). IE: size, 
> thickness, etc. However, the latin only contains either Serif or 
> Sans-Serif glyphs, and the other language is often needed for both 
> Sans and Serif coverage. This leaves a latin that might be nice to 
> have in one instance, and not in the other. Hence the reason that you 
> would want to dynamically enable and disable languages from a font. 
> For one alias it would be good, for the other, it would be bad. Right 
> now, there is no mechanism for detailing language coverage by alias. 
> The patch includes such a mechanism which adds some additional value 
> to fontconfig.
>
>> The language ban will work better where multiple languages share the
>> same code point range.
>>
>> Good ideas; let's see what we can manage.
>>
>>  
>>
>
>

-------------- next part --------------
--- fccfg.c.orig        Tue Jun 13 10:28:46 2006
+++ fccfg.c     Tue Jun 27 10:51:54 2006
@@ -117,6 +117,9 @@

     config->rescanTime = time(0);
     config->rescanInterval = 30;
+#ifdef HIDE
+    config->hideFont = NULL;
+#endif

     return config;

@@ -243,6 +246,9 @@
     for (set = FcSetSystem; set <= FcSetApplication; set++)
        if (config->fonts[set])
            FcFontSetDestroy (config->fonts[set]);
+#ifdef HIDE
+    FcHideDestroy (config->hideFont);
+#endif

     free (config);
     FcMemFree (FC_MEM_CONFIG, sizeof (FcConfig));
--- fcint.h.orig        Tue Jun 13 10:29:50 2006
+++ fcint.h     Thu Aug  3 12:04:23 2006
@@ -369,6 +369,23 @@
     FcChar32   *blanks;
 };

+#ifdef HIDE
+typedef struct _FcFontLang {
+    FcChar8             *family;        /* Name of font family */
+    FcStrSet            *hides;         /* list of hidden languages */
+    FcStrSet            *only;          /* list of only allowed languages */
+    FcStrSet            *poor;         /* list of poor quality languages */
+    struct _FcFontLang  *next;          /* Next font family */
+} FcFontLang;
+
+typedef struct _FcHide {
+    FcChar8         *alias;         /* Name of alias Sans/Serif */
+    FcFontLang      *family;        /* Hidden languages struct */
+    FcFontLang      *az[26];        /* Quick pointers */
+    struct _FcHide  *next;          /* Next alias */
+} FcHide;
+#endif
+
 struct _FcConfig {
     /*
      * File names loaded from the configuration -- saved here as the
@@ -423,6 +440,16 @@
      */
     time_t     rescanTime;         /* last time information was scanned */
     int                rescanInterval;     /* interval between scans */
+#ifdef HIDE
+    /*
+     * Languages can be selectively turned off for some fonts to allow
+     * fonts to be used for specific languages only even if the font
+     * supports the language. This allows best fonts to be used per
+     * language where two fonts that both support the same language
+     * cannot be placed in the preference list appropriately.
+     */
+    FcHide *hideFont;
+#endif
 };

 extern FcConfig        *_fcConfig;
@@ -747,6 +774,11 @@
 void
 FcEditDestroy (FcEdit *e);

+#ifdef HIDE
+void
+FcHideDestroy ( FcHide *r );
+#endif
+
 /* fcinit.c */

 void
@@ -778,6 +810,14 @@
 FcBool
 FcNameUnparseLangSet (FcStrBuf *buf, const FcLangSet *ls);

+#ifdef HIDE
+FcBool
+FcLangSetRemove (FcLangSet *ls, const FcChar8 *lang);
+
+FcBool
+FcLangSetRemoveLangs (FcLangSet *ls, FcStrSet *langs);
+#endif
+
 void
 FcLangSetNewBank (void);

--- fclang.c.orig       Tue Jun 13 10:28:40 2006
+++ fclang.c    Tue Jun 27 10:55:02 2006
@@ -43,6 +43,9 @@

 #define FcLangSetBitSet(ls, id)        ((ls)->map[(id)>>5] |= ((FcChar32) 1 << ((id) & 0x1f)))
 #define FcLangSetBitGet(ls, id) (((ls)->map[(id)>>5] >> ((id) & 0x1f)) & 1)
+#ifdef HIDE
+#define FcLangSetBitUnset(ls, id)       ((ls)->map[(id)>>5] &= ~((FcChar32) 1 << ((id) & 0x1f)))
+#endif

 static FcBool langsets_populated = FcFalse;

@@ -355,6 +358,36 @@
     return FcStrSetAdd (ls->extra, lang);
 }

+#ifdef HIDE
+FcBool
+FcLangSetRemove (FcLangSet *ls, const FcChar8 *lang)
+{
+    int id;
+
+    id = FcLangSetIndex (lang);
+    if (id >= 0)
+    {
+        FcLangSetBitUnset (ls, id);
+        return FcTrue;
+    }
+    if (ls->extra)
+        return FcStrSetDel (ls->extra, lang);
+    return FcFalse;
+}
+
+FcBool
+FcLangSetRemoveLangs (FcLangSet *ls, FcStrSet *langs)
+{
+    int i;
+
+    for ( i = 0; i < langs->num; i++ )
+    {
+        FcLangSetRemove ( ls, langs->strs[i] );
+    }
+    return FcTrue;
+}
+#endif
+
 FcLangResult
 FcLangSetHasLang (const FcLangSet *ls, const FcChar8 *lang)
 {
--- fcmatch.c.orig      Tue Jun 13 10:28:35 2006
+++ fcmatch.c   Thu Aug  3 12:05:04 2006
@@ -465,7 +465,7 @@
     FcPatternElt    *fe, *pe;
     FcValue        v;
     FcResult       result;
-
+
     new = FcPatternCreate ();
     if (!new)
        return 0;
@@ -502,6 +502,158 @@
     return new;
 }

+#ifdef HIDE
+static FcHide *
+FcHideGetList (FcConfig *config, FcPattern *p)
+{
+    FcHide *r = NULL;
+
+    if ( config->hideFont )
+    {
+        FcPatternElt *pe    = FcPatternFindElt (p, "family");
+
+        r = config->hideFont;
+
+        /*
+         * Find the base family (alias) and see if it is in the
+         * hide list
+         */
+        if ( pe )
+        {
+            FcValueListPtr v1;
+            FcValueList   *v1_ptrU;
+           int            found = 0;
+
+           while ( r )
+           {
+                for ( v1 = pe->values, v1_ptrU = FcValueListPtrU(v1);
+                      v1_ptrU;
+                      v1 = v1_ptrU->next, v1_ptrU = FcValueListPtrU(v1))
+               {
+                   const FcChar8* v1_string = fc_value_string ( &(v1_ptrU->value) );
+                    if ( !strcasecmp ( (char *)r->alias, (char *)v1_string ))
+                        found = 1;
+               }
+
+               if ( !found )
+                    r = r->next;
+               else
+                   break;
+           }
+        }
+
+        /*
+         * At this point, we have the list of hidden fonts
+         * for the alias. We now need to check and see if the
+         * font we are comparing is on the hide list.
+         */
+    }
+    return r;
+}
+
+static FcPattern *
+FcHideFont (FcHide *r, FcPatternElt *pe, FcPattern *fnt, FcLangSet **ls)
+{
+    FcPattern  *new = NULL;
+    FcFontLang *fl;
+
+    if ( r && pe )
+    {
+        FcValueListPtr ev        = pe->values;
+        FcValueList   *ev_ptrU   = FcValueListPtrU(ev);
+       const FcChar8 *ev_string = fc_value_string(& (ev_ptrU->value) );
+        int            pos       = (int)(FcToLower(*ev_string)) - (int)'a';
+
+        fl = r->az[pos];
+
+       /*
+        * Check to see if the family is in the hide list for this alias
+        */
+        while ( fl )
+        {
+            int cmp = strcasecmp ( (char *)fl->family, (char *)ev_string );
+
+            if ( !cmp )
+                break;
+            else if ( cmp > 0 )
+                fl = NULL;
+            else
+                fl = fl->next;
+        }
+
+       /*
+        * If the family is to be language restricted, then copy the pattern
+        * and modify the language support list
+        */
+        if ( fl )
+        {
+            FcPatternElt *ppe;
+
+           new = FcPatternDuplicate (fnt);
+           if ( new )
+           {
+               ppe = FcPatternFindElt (new, "lang");
+                if ( ppe )
+                {
+                   FcValueList *fv_ptrU = FcValueListPtrU(ppe->values);
+                    if ( fv_ptrU->value.type == FcTypeLangSet )
+                    {
+                        int i;
+                       if ( fl->hides )
+                       {
+                            FcLangSetRemoveLangs ( (FcLangSet *)fv_ptrU->value.u.l, fl->hides );
+                       }
+                       if ( fl->only )
+                       {
+                           FcLangSetDestroy ( (FcLangSet *)fv_ptrU->value.u.l );
+                           fv_ptrU->value.u.l = FcLangSetCreate ();
+                           for ( i = 0; i < fl->only->num; i++ )
+                               FcLangSetAdd ( (FcLangSet *)fv_ptrU->value.u.l,
+                                               fl->only->strs[i] );
+                       }
+                       if ( fl->poor )
+                       {
+                           *ls = FcLangSetCreate ();
+                           for ( i = 0; i < fl->poor->num; i++ )
+                               FcLangSetAdd ( (FcLangSet *)*ls,
+                                               fl->poor->strs[i] );
+                       }
+                    }
+                    else
+                    {
+                       /*
+                        * Don't know what to do with this yet
+                        */
+                    }
+                }
+               else
+               {
+                   FcPatternDestroy (new);
+                   new = NULL;
+               }
+           }
+        }
+    }
+
+    return new;
+}
+
+static double
+FcHidePoorLangs ( FcValue *value, FcLangSet *ls )
+{
+    double v = 0;
+    switch (value->type) {
+    case FcTypeLangSet:
+       v = FcLangSetCompare (ls, value->u.l);
+       break;
+    case FcTypeString:
+       v = FcLangSetHasLang (ls, value->u.s);
+       break;
+    }
+    return ( v != 2.0 ) * 1.5;
+}
+#endif
+
 FcPattern *
 FcFontSetMatch (FcConfig    *config,
                FcFontSet   **sets,
@@ -526,7 +678,11 @@
     int                    pat_elt;
     int                    *match_blocked;
     int                    block_start;
+#ifdef HIDE
+    FcHide          *r = NULL;
+#endif

+
     if (!nsets || !sets || !p)
     {
        *result = FcResultNoMatch;
@@ -548,6 +704,10 @@
        }
     }

+#ifdef HIDE
+    r = FcHideGetList ( config, p );
+#endif
+
     sets_offset = (int *)calloc(nsets, sizeof (int));

     nfonts = 0;
@@ -630,11 +790,24 @@
                {
                    int             cand_elt;
                    FcPatternElt    *cand_elts;
+#ifdef HIDE
+                    FcPatternElt *pe = FcPatternFindElt (s->fonts[f], "family");
+                   FcPattern    *hideFont = NULL;
+                   FcLangSet    *ls       = NULL;
+#endif

+
                    if (match_blocked[f + sets_offset[set]] == 1)
                        continue;

                    score = 1e99;
+#ifdef HIDE
+                   if ( scoring_index == MATCH_LANG_INDEX )
+                        hideFont = FcHideFont ( r, pe, s->fonts[f], &ls);
+                   if ( hideFont )
+                       cand_elts = FcPatternEltU (hideFont->elts );
+                   else
+#endif
                    cand_elts = FcPatternEltU(s->fonts[f]->elts);

                    /* Look for the appropriate element in this candidate
@@ -651,6 +824,12 @@
                                 v2 = v2_ptrU->next, v2_ptrU = FcValueListPtrU(v2))
                            {
                                double v = (matcher->compare)(&v1_ptrU->value, &v2_ptrU->value);
+#ifdef HIDE
+                               if ( ls )
+                               {
+                                   v = FcHidePoorLangs (&v1_ptrU->value, ls);
+                               }
+#endif

                                if (v < 0)
                                {
@@ -657,6 +836,12 @@
                                    *result = FcResultTypeMismatch;
                                    free (match_blocked);
                                    free (sets_offset);
+#ifdef HIDE
+                                    if ( hideFont )
+                                       FcPatternDestroy ( hideFont );
+                                   if ( ls )
+                                       FcLangSetDestroy ( ls );
+#endif
                                    return 0;
                                }

@@ -673,6 +858,13 @@
                        }
                    }

+#ifdef HIDE
+                   if ( hideFont )
+                       FcPatternDestroy ( hideFont );
+                   if ( ls )
+                       FcLangSetDestroy ( ls );
+#endif
+
                    /* We had no matching, just try the next one */
                    if (score == 1e99)
                    {
@@ -867,6 +1059,9 @@
     int                    nPatternLang;
     FcBool         *patternLangSat;
     FcValue        patternLang;
+#ifdef HIDE
+    FcHide          *r = NULL;
+#endif

     if (FcDebug () & FC_DBG_MATCH)
     {
@@ -900,6 +1095,11 @@

     new = nodes;
     nodep = nodeps;
+
+#ifdef HIDE
+    r = FcHideGetList ( config, p );
+#endif
+
     for (set = 0; set < nsets; set++)
     {
        s = sets[set];
@@ -907,14 +1107,44 @@
            continue;
        for (f = 0; f < s->nfont; f++)
        {
+#ifdef HIDE
+           FcLangSet    *ls = NULL;
+            FcPatternElt *pe = FcPatternFindElt (s->fonts[f], "family");
+            FcPattern    *hideFont = FcHideFont ( r, pe, s->fonts[f], &ls);
+           int           res = 1;
+#endif
            if (FcDebug () & FC_DBG_MATCHV)
            {
                printf ("Font %d ", f);
                FcPatternPrint (s->fonts[f]);
            }
+#ifdef HIDE
+           if ( hideFont )
+               new->pattern = hideFont;
+           else
+#endif
            new->pattern = s->fonts[f];
            if (!FcCompare (p, new->pattern, new->score, result))
+#ifdef HIDE
+                res = 0;
+
+           if ( hideFont )
+           {
+               FcPatternDestroy ( hideFont );
+               new->pattern = s->fonts[f];
+           }
+
+           if ( ls )
+           {
+               if ( FcPatternGet (p, FC_LANG, i, &patternLang) == FcResultMatch )
+                   new->score[MATCH_LANG_INDEX] = FcHidePoorLangs ( &patternLang,ls );
+               FcLangSetDestroy ( ls );
+           }
+
+            if ( !res )
+#endif
                goto bail1;
+
            if (FcDebug () & FC_DBG_MATCHV)
            {
                printf ("Score");
--- fcxml.c.orig        Tue Jun 13 10:28:29 2006
+++ fcxml.c     Thu Aug  3 12:05:30 2006
@@ -299,6 +299,13 @@
     FcElementDefault,
     FcElementFamily,

+#ifdef HIDE
+    FcElementPoor,
+    FcElementOnly,
+    FcElementHide,
+    FcElementLang,
+#endif
+
     FcElementSelectfont,
     FcElementAcceptfont,
     FcElementRejectfont,
@@ -359,6 +366,13 @@
     { "default",       FcElementDefault },
     { "family",                FcElementFamily },

+#ifdef HIDE
+    { "poor",          FcElementPoor },
+    { "only",          FcElementOnly },
+    { "hide",          FcElementHide },
+    { "lang",          FcElementLang },
+#endif
+
     { "selectfont",    FcElementSelectfont },
     { "acceptfont",    FcElementAcceptfont },
     { "rejectfont",    FcElementRejectfont },
@@ -430,6 +444,13 @@
     FcVStackPrefer,
     FcVStackAccept,
     FcVStackDefault,
+
+#ifdef HIDE
+    FcVStackPoor,
+    FcVStackOnly,
+    FcVStackHide,
+    FcVStackLang,
+#endif

     FcVStackInteger,
     FcVStackDouble,
@@ -730,6 +751,9 @@
        case FcVStackField:
        case FcVStackConstant:
        case FcVStackGlob:
+#ifdef HIDE
+        case FcVStackLang:
+#endif
            FcStrFree (vstack->u.string);
            break;
        case FcVStackPattern:
@@ -737,6 +761,11 @@
            break;
        case FcVStackInteger:
        case FcVStackDouble:
+#ifdef HIDE
+        case FcVStackPoor:
+        case FcVStackOnly:
+        case FcVStackHide:
+#endif
            break;
        case FcVStackMatrix:
            FcMatrixFree (vstack->u.matrix);
@@ -1345,7 +1374,435 @@
        FcVStackPushExpr (parse, FcVStackFamily, expr);
 }

+#ifdef HIDE
 static void
+FcParseLang (FcConfigParse *parse)
+{
+    FcChar8 *s;
+    FcExpr  *expr;
+
+    if (!parse->pstack)
+        return;
+    s = FcStrBufDone (&parse->pstack->str);
+    if (!s)
+    {
+        FcConfigMessage (parse, FcSevereError, "out of memory");
+        return;
+    }
+    expr = FcExprCreateString (s);
+    FcStrFree (s);
+    if (expr)
+        FcVStackPushExpr (parse, FcVStackLang, expr);
+}
+
+static void
+FcParseOnly (FcConfigParse *parse, FcVStackTag tag)
+{
+    FcExpr      *family = 0;
+    FcStrSet    *lang = 0;
+    FcVStack    *vstack;
+    FcFontLang  *fl = NULL;
+    FcExpr      *expr;
+
+    while ((vstack = FcVStackPop (parse)))
+    {
+        switch (vstack->tag) {
+        case FcVStackFamily:
+            if (family)
+                FcExprDestroy (family);
+            family = vstack->u.expr;
+            vstack->tag = FcVStackNone;
+            break;
+        case FcVStackLang:
+            if (!lang)
+                lang = FcStrSetCreate ();
+            FcStrSetAdd (lang, vstack->u.expr->u.sval);
+            break;
+        default:
+            FcConfigMessage (parse, FcSevereWarning, "bad alias");
+            break;
+        }
+        FcVStackDestroy (vstack);
+    }
+    if (!family)
+    {
+        FcConfigMessage (parse, FcSevereError, "missing family in only");
+    }
+    if ( lang )
+    {
+        fl = (FcFontLang *)malloc (sizeof (FcFontLang));
+
+        if ( fl )
+        {
+            int len = strlen ( (char *)family->u.sval );
+            fl->family = (FcChar8 *)malloc ( sizeof ( char ) * ( len + 1 ));
+            strncpy ( (char *)fl->family, (char *)family->u.sval, len );
+            fl->family[len] = '\0';
+            fl->hides       = NULL;
+           fl->only        = lang;
+           fl->poor        = NULL;
+            fl->next        = NULL;
+        }
+    }
+    if ( fl )
+    {
+        expr = FcExprCreateNil ();
+        if (expr)
+       {
+           expr->u.sval = (FcChar8 *)fl;
+            FcVStackPushExpr (parse, FcVStackOnly, expr);
+       }
+    }
+}
+
+static void
+FcParsePoor (FcConfigParse *parse, FcVStackTag tag)
+{
+    FcExpr      *family = 0;
+    FcStrSet    *lang = 0;
+    FcVStack    *vstack;
+    FcFontLang  *fl = NULL;
+    FcExpr      *expr;
+
+    while ((vstack = FcVStackPop (parse)))
+    {
+        switch (vstack->tag) {
+        case FcVStackFamily:
+            if (family)
+                FcExprDestroy (family);
+            family = vstack->u.expr;
+            vstack->tag = FcVStackNone;
+            break;
+        case FcVStackLang:
+            if (!lang)
+                lang = FcStrSetCreate ();
+            FcStrSetAdd (lang, vstack->u.expr->u.sval);
+            break;
+        default:
+            FcConfigMessage (parse, FcSevereWarning, "bad alias");
+            break;
+        }
+        FcVStackDestroy (vstack);
+    }
+    if (!family)
+    {
+        FcConfigMessage (parse, FcSevereError, "missing family in poor");
+    }
+    if ( lang )
+    {
+        fl = (FcFontLang *)malloc (sizeof (FcFontLang));
+
+        if ( fl )
+        {
+            int len = strlen ( (char *)family->u.sval );
+            fl->family = (FcChar8 *)malloc ( sizeof ( char ) * ( len + 1 ));
+            strncpy ( (char *)fl->family, (char *)family->u.sval, len );
+            fl->family[len] = '\0';
+            fl->hides       = NULL;
+            fl->only        = NULL;
+            fl->poor        = lang;
+            fl->next        = NULL;
+        }
+    }
+    if ( fl )
+    {
+        expr = FcExprCreateNil ();
+        if (expr)
+       {
+           expr->u.sval = (FcChar8 *)fl;
+            FcVStackPushExpr (parse, FcVStackPoor, expr);
+       }
+    }
+}
+
+static void
+FcParseHide (FcConfigParse *parse, FcVStackTag tag)
+{
+    FcExpr      *family = 0;
+    FcStrSet    *lang = 0;
+    FcVStack    *vstack;
+    FcFontLang  *fl = NULL;
+    FcExpr      *expr;
+
+    while ((vstack = FcVStackPop (parse)))
+    {
+        switch (vstack->tag) {
+        case FcVStackFamily:
+            if (family)
+                FcExprDestroy (family);
+            family = vstack->u.expr;
+            vstack->tag = FcVStackNone;
+            break;
+        case FcVStackLang:
+            if (!lang)
+                lang = FcStrSetCreate ();
+            FcStrSetAdd (lang, vstack->u.expr->u.sval);
+            break;
+        default:
+            FcConfigMessage (parse, FcSevereWarning, "bad alias");
+            break;
+        }
+        FcVStackDestroy (vstack);
+    }
+    if (!family)
+    {
+        FcConfigMessage (parse, FcSevereError, "missing family in hide");
+    }
+    if ( lang )
+    {
+        fl = (FcFontLang *)malloc (sizeof (FcFontLang));
+
+        if ( fl )
+        {
+            int len = strlen ( (char *)family->u.sval );
+            fl->family = (FcChar8 *)malloc ( sizeof ( char ) * ( len + 1 ));
+            strncpy ( (char *)fl->family, (char *)family->u.sval, len );
+            fl->family[len] = '\0';
+            fl->hides       = lang;
+            fl->only        = NULL;
+            fl->poor        = NULL;
+            fl->next        = NULL;
+        }
+    }
+    if ( fl )
+    {
+        expr = FcExprCreateNil ();
+        if (expr)
+       {
+           expr->u.sval = (FcChar8 *)fl;
+            FcVStackPushExpr (parse, FcVStackHide, expr);
+       }
+    }
+}
+
+static void
+FcFontLangDestroy(FcFontLang *fl)
+{
+    FcFontLang *cur = fl;
+
+    while (fl)
+    {
+        cur = fl;
+        fl  = fl->next;
+        free (cur->family);
+       if ( cur->hides )
+            FcStrSetDestroy (cur->hides);
+       if ( cur->only )
+            FcStrSetDestroy (cur->only);
+       if ( cur->poor )
+            FcStrSetDestroy (cur->poor);
+        free (cur);
+    }
+}
+
+void
+FcHideDestroy ( FcHide *r )
+{
+    if ( r )
+    {
+        if ( r->alias )
+            free ( r->alias );
+        FcFontLangDestroy ( r->family );
+        FcHideDestroy ( r->next );
+        free ( r );
+    }
+}
+
+void
+FcHideShowLangs ( FcFontLang *fl )
+{
+    int i;
+
+    if ( fl->hides )
+    {
+       printf ( "hides: " );
+        for ( i = 0; i < fl->hides->num; i++ )
+           printf ( " %s,", fl->hides->strs[i]);
+    }
+    if ( fl->only )
+    {
+       printf ( "only allows: " );
+       for ( i = 0; i < fl->only->num; i++ )
+           printf ( " %s", fl->only->strs[i]);
+    }
+    if ( fl->poor )
+    {
+       printf ( "poor: " );
+       for ( i = 0; i < fl->poor->num; i++ )
+           printf ( " %s", fl->poor->strs[i]);
+    }
+    printf("\n");
+}
+
+static int
+FcHideCreate(FcConfigParse *parse, FcFontLang *fl, FcExpr *family)
+{
+    FcHide *new = parse->config->hideFont;
+    int         i;
+
+    /*
+     * Check to see if user has already hidden something in this alias
+     */
+    while ( new != NULL )
+    {
+       if ( !strcasecmp ( (char *)new->alias, (char *)family->u.sval ))
+           break;
+       else
+           new = new->next;
+    }
+
+    /*
+     * If no alias hides exist, then create an FcHide structure and
+     * fill in the base values.
+     */
+    if ( !new )
+    {
+        new = (FcHide *)malloc ( sizeof ( FcHide ));
+        if ( new )
+       {
+            int len = strlen ( (char *)family->u.sval );
+            new->alias  = (FcChar8 *)malloc ( sizeof ( char ) * ( len + 1 ));
+            if ( new->alias )
+            {
+                strncpy ((char *)new->alias, (char *)family->u.sval, len);
+                new->alias[len] = '\0';
+            }
+            else
+            {
+                free (new);
+                return 0;
+            }
+            new->family = NULL;
+            new->next   = parse->config->hideFont;
+            parse->config->hideFont = new;
+       }
+       else
+           return 0;
+    }
+
+    /*
+     * Add FontLang structure in alphabetical order based on family
+     * to the hide list
+     */
+    while ( fl )
+    {
+        FcFontLang *next = fl->next;
+
+        fl->next = NULL;
+
+       /*
+        * If no hidden families exist, then just add this one
+        */
+        if ( !new->family )
+       {
+            new->family = fl;
+       }
+        else
+        {
+            FcFontLang *ptr  = new->family;
+            FcFontLang *last = NULL;
+
+           /*
+            * Check to see where to enter hidden family, or if it already
+            * exists
+            */
+            while ( ptr )
+            {
+               int cmp = strcasecmp ( (char *)fl->family, (char *)ptr->family);
+               /*
+                * Find out if new family is before current alphabetically
+                */
+                if ( cmp < 0 )
+                {
+                    fl->next = ptr;
+                    if ( !last )
+                        new->family = fl;
+                    else
+                        last->next = fl;
+                    break;
+                }
+               /*
+                * If it already exists, then just add new lang(s)
+                */
+                else if ( cmp == 0 )
+               {
+                   /*
+                    * Check to see if we have a family with only langs and
+                    * hidden langs. Return error. Otherwise, add langs to list
+                    */
+                   if (( fl->hides && ptr->only ) ||
+                       ( fl->only && ptr->hides ))
+                       return 1;
+                   if ( fl->hides )
+                   {
+                       if ( !ptr->hides )
+                           ptr->hides = FcStrSetCreate ();
+                       for ( i = 0; i < fl->hides->num; i++ )
+                       {
+                           if ( !FcStrSetMember ( ptr->hides, fl->hides->strs[i] ))
+                               FcStrSetAdd ( ptr->hides, fl->hides->strs[i] );
+                       }
+                   }
+                   if ( fl->only )
+                   {
+                       if ( !ptr->only )
+                           ptr->only = FcStrSetCreate ();
+                       for ( i = 0; i < fl->only->num; i++ )
+                       {
+                           if ( !FcStrSetMember ( ptr->only, fl->only->strs[i] ))
+                               FcStrSetAdd ( ptr->only, fl->only->strs[i] );
+                       }
+                   }
+                   if ( fl->poor )
+                   {
+                       if ( !ptr->only )
+                           ptr->poor = FcStrSetCreate ();
+                       for ( i = 0; i < fl->poor->num; i++ )
+                       {
+                           if ( !FcStrSetMember ( ptr->poor, fl->poor->strs[i] ))
+                               FcStrSetAdd ( ptr->poor, fl->poor->strs[i] );
+                       }
+                   }
+                   FcFontLangDestroy ( fl );
+                   break;
+               }
+               /*
+                * If it is after, then continue moving down the list
+                */
+               else
+                {
+                    last = ptr;
+                    ptr = ptr->next;
+
+                    if ( !ptr )
+                   {
+                        last->next = fl;
+                   }
+                }
+            }
+        }
+        fl = next;
+    }
+
+    for ( i = 0; i < 26; i++ )
+        new->az[i] = NULL;
+
+    fl = new->family;
+    while ( fl )
+    {
+        int pos = (int)(fl->family[0]) - (int)'A';
+        if ( !new->az[pos] )
+        {
+            new->az[pos] = fl;
+        }
+        fl = fl->next;
+    }
+
+    return 0;
+}
+#endif
+
+static void
 FcParseAlias (FcConfigParse *parse)
 {
     FcExpr     *family = 0, *accept = 0, *prefer = 0, *def = 0, *new = 0;
@@ -1352,6 +1809,10 @@
     FcEdit     *edit = 0, *next;
     FcVStack   *vstack;
     FcTest     *test;
+#ifdef HIDE
+    FcFontLang  *langs = NULL;
+    FcFontLang  *tmp;
+#endif

     while ((vstack = FcVStackPop (parse)))
     {
@@ -1373,6 +1834,17 @@
                vstack->tag = FcVStackNone;
            }
            break;
+#ifdef HIDE
+       case FcVStackPoor:
+       case FcVStackOnly:
+        case FcVStackHide:
+            tmp = (FcFontLang *)(vstack->u.expr->u.sval);
+            FcExprDestroy (vstack->u.expr);
+            tmp->next = langs;
+            langs = tmp;
+            vstack->tag = FcVStackNone;
+            break;
+#endif
        case FcVStackPrefer:
            if (prefer)
                FcExprDestroy (prefer);
@@ -1446,6 +1918,13 @@
        else
            FcExprDestroy (def);
     }
+#ifdef HIDE
+    if (langs)
+    {
+        if ( FcHideCreate(parse, langs, family))
+           FcConfigMessage (parse, FcSevereError, "Cannot include hide and only for same family");
+    }
+#endif
     if (edit)
     {
        test = FcTestCreate (parse, FcMatchPattern,
@@ -1473,6 +1952,9 @@
        break;
     case FcVStackString:
     case FcVStackFamily:
+#ifdef HIDE
+    case FcVStackLang:
+#endif
        expr = FcExprCreateString (vstack->u.string);
        break;
     case FcVStackField:
@@ -2099,6 +2581,20 @@
     case FcElementFamily:
        FcParseFamily (parse);
        break;
+#ifdef HIDE
+    case FcElementPoor:
+       FcParsePoor (parse, FcVStackLang);
+       break;
+    case FcElementOnly:
+       FcParseOnly (parse, FcVStackLang);
+       break;
+    case FcElementHide:
+        FcParseHide (parse, FcVStackLang);
+        break;
+    case FcElementLang:
+        FcParseLang (parse);
+        break;
+#endif

     case FcElementTest:
        FcParseTest (parse);



More information about the Fontconfig mailing list