Re: How can a font be blacklisted for some scripts

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Nicolas,

I think that I have a solution that will work for you. As part of a seperate problem that I was trying to solve, I came up with restricting some languages from particular fonts. This essentially blacklists particular fonts for showing up with particular languages. It works on the pretense that if the font does not include the language that the system is asking for then it will be excluded from being part of the possible candidates. To do this, before doing the actual compare in fontconfig, I remove the restricted languages from the list of languages the font claims to support. After the comparison, the language list is returned to it's original state.

The modified language list only happens for font sets such as Serif, Sans and Monospace. If a user explicitly asks for a particular font, then no restriction takes place.

I have included the diffs file for fontconfig 2.3.2, and an example of the entries needed in fonts.conf. Since it is possible for a font to appear in more than one alias, such as Chinese, Japanese and Korean fonts, etc, it may be necessary to distinguish which alias the restriction is for, such as restricting Sans Serif English characters in a Chinese font from the Serif alias.

<alias>
       <family>Serif</family>
       <restrict>
               <family>Lucida Bright</family>
               <lang>en</lang>
               <lang>de</lang>
       </restrict>
</alias>
<alias>
       <family>Monospace</family>
       <restrict>
               <family>Arial</family>
               <lang>en</lang>
       </restrict>
</alias>

Hope this solves your problem. Let me know. If it does, then I'll work with you to get it into the open-source fontconfig.

Jay Hobson



Nicolas Mailhot wrote:

Hi,

I'm running in the following problem. I am the Fedora Extras packager of
DejaVu, a FOSS family of typefaces with monthly releases and fast pace
of change.

Due to its FOSS nature and release early, release often process at one
point of time the fonts may not be suitable for intensive use with some
scripts. Moreover when unicode blocks are shared between scripts which
have different conventions on how the glyphs may be drawn, only one
script can be accommodated right now (even if both variants of the
glyphs are present in the font, as is the case today for Russian
cyrillic vs balkan cyrillic  and may be in the future for Arabic vs
Farsi).

So I need to provide an optional way to blacklist these fonts with some
scripts in fontconfig. Optional because we do need users to test and
report on unfinished blocks, and while splitting the fonts by unicode
blocks would be possible, it would be a nightmare from packaging and
user font management point of views. I absolutely refuse to be
responsible for DejaVu-script variants as we had Foo-encoding variants
in the bad old pre-unicode pre-fontconfig days, and have users manually
combine them in documents because the font tools are too dumb to do font
management transparently.

Moreover users of the more mature blocks are very happy with the fonts
and are lobbying to make it the default in various distribution (at the
Sans... aliases level).

Thanksfully Fedora Devel sports fontconfig 2.3.95 so dropping files
in /etc/fonts/conf.d is a possibility. The question right now is what to
put in the conf files. I'm afraid fontconfig documentation is somewhat
esoteric for people not working with it every days, and I couldn't find
a clean example of what we want on the net.

To blacklist the font with some scripts the following snippets have been
proposed :
===============
<match>
  <test name="lang">
     <string>fa</string>
  </test>
<edit name="family" mode="prepend" binding="same">
  <string>Roya</string>
</edit>
</match>
===============

=============== <match>
   <test name="lang"><string>fa</string></test>
   <test name="family"><string>DejaVu Sans</string></test>
   <edit name="family" mode="assign" binding="same">
     <string>Roya</string>
   </edit>
 </match>
===============

Which ones will work best ? Are there better way to do it ?

To make DejaVu the default in aliases I've been
processing /etc/fonts/fonts.conf via xslt so far it works but is a tad
complex maybe now there is a way to do it by dropping a file in conf.d ?

Regards,

------------------------------------------------------------------------

_______________________________________________
Fontconfig mailing list
Fontconfig@xxxxxxxxxxxxxxxxxxxxx
http://lists.freedesktop.org/mailman/listinfo/fontconfig

--- fcint.h.orig	Mon Jun 12 09:58:54 2006
+++ fcint.h	Mon Jun 12 10:06:24 2006
@@ -327,6 +327,21 @@
     FcChar32	*blanks;
 };
 
+#ifdef RESTRICT
+typedef struct _FcFontLang {
+    FcChar8             *family;        /* Name of font family */
+    FcStrSet            *langs;         /* list of restricted languages */
+    struct _FcFontLang  *next;          /* Next font family */
+} FcFontLang;
+
+typedef struct _FcRestrict {
+    FcChar8             *alias;         /* Name of alias Sans/Serif */
+    FcFontLang          *family;        /* Restricted languages struct */
+    FcFontLang          *az[26];        /* Quick pointers */
+    struct _FcRestrict  *next;          /* Next alias */
+} FcRestrict;
+#endif
+
 struct _FcConfig {
     /*
      * File names loaded from the configuration -- saved here as the
@@ -381,6 +396,16 @@
      */
     time_t	rescanTime;	    /* last time information was scanned */
     int		rescanInterval;	    /* interval between scans */
+#ifdef RESTRICT
+    /*
+     * 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.
+     */
+    FcRestrict *restrictFont;
+#endif
 };
  
 extern FcConfig	*_fcConfig;
@@ -645,6 +670,11 @@
 void
 FcEditDestroy (FcEdit *e);
 
+#ifdef RESTRICT
+void
+FcRestrictDestroy ( FcRestrict *r );
+#endif
+
 /* fcinit.c */
 
 void
@@ -676,6 +706,14 @@
 FcBool
 FcNameUnparseLangSet (FcStrBuf *buf, const FcLangSet *ls);
 
+#ifdef RESTRICT
+FcBool
+FcLangSetRemove (FcLangSet *ls, const FcChar8 *lang);
+
+FcBool
+FcLangSetRemoveLangs (FcLangSet *ls, FcStrSet *langs);
+#endif
+
 /* fclist.c */
 
 FcBool
--- fccfg.c.orig	Mon Jun 12 09:44:21 2006
+++ fccfg.c	Mon Jun 12 10:04:14 2006
@@ -115,6 +115,9 @@
 
     config->rescanTime = time(0);
     config->rescanInterval = 30;    
+#ifdef RESTRICT
+    config->restrictFont = NULL;
+#endif
     
     return config;
 
@@ -233,6 +236,9 @@
     for (set = FcSetSystem; set <= FcSetApplication; set++)
 	if (config->fonts[set])
 	    FcFontSetDestroy (config->fonts[set]);
+#ifdef RESTRICT
+    FcRestrictDestroy (config->restrictFont);
+#endif
 
     free (config);
     FcMemFree (FC_MEM_CONFIG, sizeof (FcConfig));
--- fclang.c.orig	Mon Jun 12 09:46:05 2006
+++ fclang.c	Mon Jun 12 10:30:09 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 RESTRICT
+#define FcLangSetBitUnset(ls, id)       ((ls)->map[(id)>>5] &= ~((FcChar32) 1 << ((id) & 0x1f)))
+#endif
 
 FcLangSet *
 FcFreeTypeLangSet (const FcCharSet  *charset, 
@@ -336,6 +339,36 @@
     return FcStrSetAdd (ls->extra, lang);
 }
 
+#ifdef RESTRICT
+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	Mon Jun 12 09:47:34 2006
+++ fcmatch.c	Mon Jun 12 10:07:45 2006
@@ -491,6 +491,100 @@
     return new;
 }
 
+#ifdef RESTRICT
+static FcRestrict *
+FcRestrictGetList (FcConfig *config, FcPattern *p)
+{
+    FcRestrict *r = NULL;
+
+    if ( config->restrictFont )
+    {
+        FcPatternElt *pe    = FcPatternFindElt (p, "family");
+
+        r = config->restrictFont;
+
+        /*
+         * Find the base family (alias) and see if it is in the
+         * restrict list
+         */
+        if ( pe )
+        {
+            FcValueList *vlist = pe->values;
+
+            while ( vlist->next )
+                vlist = vlist->next;
+
+            while ( r )
+            {
+                if ( !strcasecmp ( (char *)r->alias, (char *)vlist->value.u.s ))                    break;
+                else
+                    r = r->next;
+            }
+        }
+
+        /*
+         * At this point, we have the list of restricted fonts
+         * for the alias. We now need to check and see if the
+         * font we are comparing is on the restrict list.
+         */
+    }
+
+    return r;
+}
+
+static int
+FcRestrictFont (FcRestrict *r, FcPatternElt *pe, FcPattern *fnt, FcPatternElt **ppe, FcLangSet **pls)
+{
+    FcFontLang *fl;
+    int         ret = 0;
+
+    if ( r && pe )
+    {
+        FcLangSet  *ls;
+        int        pos = (int)(FcToLower(pe->values->value.u.s[0])) - 'a';
+
+        fl = r->az[pos];
+
+        while ( fl )
+        {
+            FcValueList *vlist = pe->values;
+            int          cmp   = strcasecmp ( (char *)fl->family,
+                                              (char *)vlist->value.u.s );
+
+            if ( !cmp )
+                break;
+            else if ( cmp > 0 )
+                fl = NULL;
+            else
+                fl = fl->next;
+        }
+
+        if ( fl )
+        {
+            *ppe = FcPatternFindElt (fnt, "lang");
+            if ( *ppe )
+            {
+                ret = 1;
+                if ( (*ppe)->values->value.type == FcTypeLangSet )
+                {
+                    ls = FcLangSetCopy ( (*ppe)->values->value.u.l );
+                    FcLangSetRemoveLangs ( ls, fl->langs );
+
+                    (*pls) = (FcLangSet *)(*ppe)->values->value.u.l;
+                    (*ppe)->values->value.u.l = ls;
+                }
+                else
+                {
+
+                }
+            }
+        }
+    }
+
+    return ret;
+}
+#endif
+
 static void
 FcChangeFormat ( FcPattern *p )
 {
@@ -543,6 +637,9 @@
     FcPattern	    *best;
     int		    i;
     int		    set;
+#ifdef RESTRICT
+    FcRestrict      *r = NULL;
+#endif
 
     FcChangeFormat ( p );
 
@@ -563,6 +660,11 @@
 	    return 0;
 	}
     }
+
+#ifdef RESTRICT
+    r = FcRestrictGetList ( config, p );
+#endif
+
     for (set = 0; set < nsets; set++)
     {
 	s = sets[set];
@@ -570,6 +672,12 @@
 	    continue;
 	for (f = 0; f < s->nfont; f++)
 	{
+#ifdef RESTRICT
+            FcLangSet    *oldLangSet = NULL;
+            FcPatternElt *pe = FcPatternFindElt (s->fonts[f], "family"), *pe2;
+            int    restrictFont = FcRestrictFont ( r, pe, s->fonts[f], &pe2, &oldLangSet);
+            int    res = 1;
+#endif
 	    if (FcDebug () & FC_DBG_MATCHV)
 	    {
 		printf ("Font %d ", f);
@@ -576,7 +684,22 @@
 		FcPatternPrint (s->fonts[f]);
 	    }
 	    if (!FcCompare (p, s->fonts[f], score, result))
-		return 0;
+#ifdef RESTRICT
+		res = 0;
+
+            if ( restrictFont )
+            {
+                if ( pe2->values->value.type == FcTypeLangSet )
+                {
+                    FcLangSetDestroy ((FcLangSet *) pe2->values->value.u.l );
+                    pe2->values->value.u.l = oldLangSet;
+                }
+            }
+
+            if ( !res )
+#endif
+                return 0;
+
 	    if (FcDebug () & FC_DBG_MATCHV)
 	    {
 		printf ("Score");
@@ -731,7 +854,11 @@
     int		    nPatternLang;
     int    	    *patternLangSat;
     FcValue	    patternLang;
+#ifdef RESTRICT
+    FcRestrict      *r = NULL;
+#endif
 
+
     FcChangeFormat ( p );
 
     if (FcDebug () & FC_DBG_MATCH)
@@ -766,6 +893,11 @@
     
     new = nodes;
     nodep = nodeps;
+
+#ifdef RESTRICT
+    r = FcRestrictGetList ( config, p );
+#endif
+
     for (set = 0; set < nsets; set++)
     {
 	s = sets[set];
@@ -773,6 +905,12 @@
 	    continue;
 	for (f = 0; f < s->nfont; f++)
 	{
+#ifdef RESTRICT
+            FcLangSet    *oldLangSet = NULL;
+            FcPatternElt *pe = FcPatternFindElt (s->fonts[f], "family"), *pe2;
+            int    restrictFont = FcRestrictFont ( r, pe, s->fonts[f], &pe2, &oldLangSet);
+            int    res = 1;
+#endif
 	    if (FcDebug () & FC_DBG_MATCHV)
 	    {
 		printf ("Font %d ", f);
@@ -780,7 +918,22 @@
 	    }
 	    new->pattern = s->fonts[f];
 	    if (!FcCompare (p, new->pattern, new->score, result))
-		goto bail1;
+#ifdef RESTRICT
+		res = 0;
+
+            if ( restrictFont )
+            {
+                if ( pe2->values->value.type == FcTypeLangSet )
+                {
+                    FcLangSetDestroy ((FcLangSet *) pe2->values->value.u.l );
+                    pe2->values->value.u.l = oldLangSet;
+                }
+            }
+
+            if ( !res )
+#endif
+                goto bail1;
+
 	    if (FcDebug () & FC_DBG_MATCHV)
 	    {
 		printf ("Score");
--- fcxml.c.orig	Mon Jun 12 09:53:00 2006
+++ fcxml.c	Mon Jun 12 10:04:14 2006
@@ -284,6 +284,11 @@
     FcElementDefault,
     FcElementFamily,
 
+#ifdef RESTRICT
+    FcElementRestrict,
+    FcElementLang,
+#endif
+
     FcElementSelectfont,
     FcElementAcceptfont,
     FcElementRejectfont,
@@ -347,6 +352,11 @@
 	{ "default",	FcElementDefault },
 	{ "family",	FcElementFamily },
 
+#ifdef RESTRICT
+        { "restrict",   FcElementRestrict },
+        { "lang",       FcElementLang },
+#endif
+
 	{ "selectfont",	FcElementSelectfont },
 	{ "acceptfont",	FcElementAcceptfont },
 	{ "rejectfont",	FcElementRejectfont },
@@ -415,6 +425,11 @@
     FcVStackPrefer,
     FcVStackAccept,
     FcVStackDefault,
+
+#ifdef RESTRICT
+    FcVStackRestrict,
+    FcVStackLang,
+#endif
     
     FcVStackInteger,
     FcVStackDouble,
@@ -711,6 +726,9 @@
 	case FcVStackField:
 	case FcVStackConstant:
 	case FcVStackGlob:
+#ifdef RESTRICT
+        case FcVStackLang:
+#endif
 	    FcStrFree (vstack->u.string);
 	    break;
 	case FcVStackPattern:
@@ -718,6 +736,9 @@
 	    break;
 	case FcVStackInteger:
 	case FcVStackDouble:
+#ifdef RESTRICT
+        case FcVStackRestrict:
+#endif
 	    break;
 	case FcVStackMatrix:
 	    FcMatrixFree (vstack->u.matrix);
@@ -1326,7 +1347,195 @@
 	FcVStackPushExpr (parse, FcVStackFamily, expr);
 }
 
+#ifdef RESTRICT
 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
+FcParseRestrict (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 restrict");
+    }
+    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->langs       = lang;
+            fl->next        = NULL;
+        }
+    }
+    if ( fl )
+    {
+        expr = FcExprCreateInteger ((int)fl);
+        if (expr)
+            FcVStackPushExpr (parse, FcVStackRestrict, expr);
+    }
+}
+
+static void
+FcFontLangDestroy(FcFontLang *fl)
+{
+    FcFontLang *cur = fl;
+
+    while (fl)
+    {
+        cur = fl;
+        fl  = fl->next;
+
+        free (cur->family);
+        FcStrSetDestroy (cur->langs);
+        free (cur);
+    }
+}
+
+void
+FcRestrictDestroy ( FcRestrict *r )
+{
+    if ( r )
+    {
+        if ( r->alias )
+            free ( r->alias );
+        FcFontLangDestroy ( r->family );
+        FcRestrictDestroy ( r->next );
+        free ( r );
+    }
+}
+
+static void
+FcRestrictCreate(FcConfigParse *parse, FcFontLang *fl, FcExpr *family)
+{
+    FcRestrict *new = (FcRestrict *)malloc ( sizeof ( FcRestrict ));
+
+    if ( new )
+    {
+        int len = strlen ( (char *)family->u.sval );
+        int i;
+
+        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;
+        }
+        new->family = NULL;
+        new->next   = parse->config->restrictFont;
+        parse->config->restrictFont = new;
+
+        /*
+         * Add FontLang structure in alphabetical order based on family
+         * to the restrict list
+         */
+        while ( fl )
+        {
+            FcFontLang *next = fl->next;
+
+            fl->next = NULL;
+
+            if ( !new->family )
+                new->family = fl;
+            else
+            {
+                FcFontLang *ptr  = new->family;
+                FcFontLang *last = NULL;
+
+                while ( ptr )
+                {
+                    if ( strcasecmp ( (char *)fl->family, (char *)ptr->family) < 0)
+                    {
+                        fl->next = ptr;
+                        if ( !last )
+                            new->family = fl;
+                        else
+                            last->next = fl;
+                        break;
+                    }
+                    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;
+        }
+    }
+}
+#endif
+
+static void
 FcParseAlias (FcConfigParse *parse)
 {
     FcExpr	*family = 0, *accept = 0, *prefer = 0, *def = 0, *new = 0;
@@ -1333,6 +1542,10 @@
     FcEdit	*edit = 0, *next;
     FcVStack	*vstack;
     FcTest	*test;
+#ifdef RESTRICT
+    FcFontLang  *fl = NULL;
+    FcFontLang  *tmp;
+#endif
 
     while ((vstack = FcVStackPop (parse))) 
     {
@@ -1354,6 +1567,15 @@
 		vstack->tag = FcVStackNone;
 	    }
 	    break;
+#ifdef RESTRICT
+        case FcVStackRestrict:
+            tmp = (FcFontLang *)(vstack->u.expr->u.ival);
+            FcExprDestroy (vstack->u.expr);
+            tmp->next = fl;
+            fl = tmp;
+            vstack->tag = FcVStackNone;
+            break;
+#endif
 	case FcVStackPrefer:
 	    if (prefer)
 		FcExprDestroy (prefer);
@@ -1427,6 +1649,13 @@
 	else
 	    FcExprDestroy (def);
     }
+#ifdef RESTRICT
+    if (fl)
+    {
+        FcRestrictCreate(parse, fl, family);
+        FcExprDestroy (family);
+    }
+#endif
     if (edit)
     {
 	test = FcTestCreate (parse, FcMatchPattern,
@@ -1454,6 +1683,9 @@
 	break;
     case FcVStackString:
     case FcVStackFamily:
+#ifdef RESTRICT
+    case FcVStackLang:
+#endif
 	expr = FcExprCreateString (vstack->u.string);
 	break;
     case FcVStackField:
@@ -2077,6 +2309,14 @@
     case FcElementFamily:
 	FcParseFamily (parse);
 	break;
+#ifdef RESTRICT
+    case FcElementRestrict:
+        FcParseRestrict (parse, FcVStackLang);
+        break;
+    case FcElementLang:
+        FcParseLang (parse);
+        break;
+#endif
 
     case FcElementTest:
 	FcParseTest (parse);
_______________________________________________
Fontconfig mailing list
Fontconfig@xxxxxxxxxxxxxxxxxxxxx
http://lists.freedesktop.org/mailman/listinfo/fontconfig

[Index of Archives]     [Fedora Fonts]     [Fedora Users]     [Fedora Cloud]     [Kernel]     [Fedora Packaging]     [Fedora Desktop]     [PAM]     [Gimp Graphics Editor]     [Yosemite News]

  Powered by Linux