Jay Hobson
2006-Jun-28 16:18 UTC
[Fontconfig] Re: fontconfig support to exclude glyphs from fonts
Keith, I have been working with Nicolas Mailhot to provide a solution to allow DejaVu to be added to fontconfig enabling support for some languages while excluding support for others. I have attached a patch which works with fontconfig-2.3.95. The patch adds two new tags to supported XML tag list. The tags work in conjuction with other tags already present in the library. The tags require fonts.conf or its included counterparts to be amended, detailing languages to restrict in specified families. The first added tag "hide" documents which languages are restricted from use for the family. The second tag "only" documents which languages the family is allowed to support. The patch works by copying the pattern out of the cache, then modifying the local copy of the pattern to remove either a list of languages to be hidden, or deleting the language set, creating a new one and adding only the supported languages. This new pattern is then used for comparison for a match, after which point it is deleted. This patch only affects the comparison for families within an alias. If the user requests a specific family by name, no modification of the language set will occur. Additionally, as some families may be used in more than one alias and need differing support for each, the alias name is required, and modifications to the language set needs to be dynamic. The key benefits for this patch are the ability to use fonts that are works in progress, broken, or have poor quality glyphs for particular languages. This patch can also be used to allow fonts to be placed higher in the alias list with support for only targeted languages without interfering with fonts intended for other language support. Syntax for the new commands is shown in the following example: <alias> <family>Sans-serif</family> <only> <family>Arial</family> <lang>en</lang> <lang>de</lang> </only> <hide> <family>DejaVuSans</family> <lang>ar</lang> </hide> </alias> <alias> <family>Serif</family> <hide> <family>Times</family> <lang>en</lang> <lang>de</lang> <lang>es</lang> </hide> </alias> The above XML would only allow Arial to be used for English and German, disallow DejaVu for Arabic, and disallow Times with English, German and Spanish. I am interested in your comments/questions and willing to work with you to make this a part of the fontconfig library. Thanks, Jay -------------- next part -------------- A non-text attachment was scrubbed... Name: fontconfig-2.3.95.hide.patch Type: text/x-patch Size: 22916 bytes Desc: not available Url : http://lists.freedesktop.org/archives/fontconfig/attachments/20060628/05724042/fontconfig-2.3.95.hide.bin
Keith Packard
2006-Jun-28 20:05 UTC
[Fontconfig] Re: fontconfig support to exclude glyphs from fonts
On Wed, 2006-06-28 at 16:15 -0700, Jay Hobson wrote:> The patch adds two new tags to supported XML tag list. The tags work > in conjuction with other tags already present in the library. The tags > require fonts.conf or its included counterparts to be amended, detailing > languages to restrict in specified families. The first added tag "hide" > documents which languages are restricted from use for the family. The > second tag "only" documents which languages the family is allowed to > support.We''ve been discussing this here at Guadec and decided that what we really want to do is ban unicode codepoint sets from certain fonts. As fontconfig computes said ranges, it should be reasonably easy to add a match/edit phase to font scanning and edit the unicode codepoint set before creating the cache file. It means that changes to this would require manual regeneration of the cache, but it also means that the glyphs wouldn''t ever be shown from those fonts as higher level libraries use that codepoint set to select fonts for each glyph. -- keith.packard@intel.com -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 189 bytes Desc: This is a digitally signed message part Url : http://lists.freedesktop.org/archives/fontconfig/attachments/20060629/157edb44/attachment-0001.pgp
Nicolas Mailhot
2006-Jun-28 23:28 UTC
[Fontconfig] Re: fontconfig support to exclude glyphs from fonts
Le jeudi 29 juin 2006 ? 05:03 +0200, Keith Packard a ?crit :> On Wed, 2006-06-28 at 16:15 -0700, Jay Hobson wrote: > > > The patch adds two new tags to supported XML tag list. The tags work > > in conjuction with other tags already present in the library. The tags > > require fonts.conf or its included counterparts to be amended, detailing > > languages to restrict in specified families. The first added tag "hide" > > documents which languages are restricted from use for the family. The > > second tag "only" documents which languages the family is allowed to > > support. > > We''ve been discussing this here at Guadec and decided that what we > really want to do is ban unicode codepoint sets from certain fonts. As > fontconfig computes said ranges, it should be reasonably easy to add a > match/edit phase to font scanning and edit the unicode codepoint set > before creating the cache file. It means that changes to this would > require manual regeneration of the cache, but it also means that the > glyphs wouldn''t ever be shown from those fonts as higher level libraries > use that codepoint set to select fonts for each glyph.While fine-tuning blacklisting by codepoint sets would be even better, I think Jay''s choice of doing it only in aliasing context with specific langs should not be totally dropped : - if a user specifically requests a glyph using an exact font name (not an alias) who are we to refuse it? (in an alias context with all the fallbacks of course the problem is different) - also, due to some glyphs being shared by several cultural areas which do not agree on their drawing, you need to take the lang used into account when blacklisting (that is until libs like pango implement locl and all fonts are changed to put the right variants in the same places) However for my short-term needs any kind of blacklisting will work, so please work help Jay modify his patch till it''s ready for inclusion. Regards, -- Nicolas Mailhot -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: application/pgp-signature Size: 197 bytes Desc: Ceci est une partie de message =?ISO-8859-1?Q?num=E9riquement?= =?ISO-8859-1?Q?_sign=E9e?Url : http://lists.freedesktop.org/archives/fontconfig/attachments/20060629/7c4504eb/attachment.pgp
Jay Hobson
2006-Jun-29 09:44 UTC
[Fontconfig] Re: fontconfig support to exclude glyphs from fonts
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. 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. I would be happy to help work on a unicode codepoint set ban, but I would like to see this patch, or something similar go into fontconfig as well. Thanks, Jay Keith Packard wrote:>On Wed, 2006-06-28 at 16:15 -0700, Jay Hobson wrote: > > > >> The patch adds two new tags to supported XML tag list. The tags work >>in conjuction with other tags already present in the library. The tags >>require fonts.conf or its included counterparts to be amended, detailing >>languages to restrict in specified families. The first added tag "hide" >>documents which languages are restricted from use for the family. The >>second tag "only" documents which languages the family is allowed to >>support. >> >> > >We''ve been discussing this here at Guadec and decided that what we >really want to do is ban unicode codepoint sets from certain fonts. As >fontconfig computes said ranges, it should be reasonably easy to add a >match/edit phase to font scanning and edit the unicode codepoint set >before creating the cache file. It means that changes to this would >require manual regeneration of the cache, but it also means that the >glyphs wouldn''t ever be shown from those fonts as higher level libraries >use that codepoint set to select fonts for each glyph. > > > >------------------------------------------------------------------------ > >_______________________________________________ >Fontconfig mailing list >Fontconfig@lists.freedesktop.org >http://lists.freedesktop.org/mailman/listinfo/fontconfig > >
Jay Hobson
2006-Jun-29 12:06 UTC
[Fontconfig] Re: fontconfig support to exclude glyphs from fonts
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. > > >
Jay Hobson
2006-Aug-03 12:30 UTC
[Fontconfig] Re: fontconfig support to exclude glyphs from fonts
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);