[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