From 3c862aad9f49be4b098cb679a67449c85b58f1f5 Mon Sep 17 00:00:00 2001
From: Akira TAGOH <akira@tagoh.org>
Date: Mon, 6 Dec 2010 12:38:18 +0900
Subject: [PATCH] Add editing langset feature.

The syntax to add any langset to the langset table looks like:

<match target="scan">
    <test name="family">
        <string>Buggy Sans</string>
    </test>
    <edit name="lang" mode="assign">
        <plus>
            <name>lang</name>
            <langset>
                <string>zh-cn</string>
                <string>zh-tw</string>
            </langset>
        </plus>
    </edit>
</match>

To remove any langset from the langset table:

<match target="scan">
    <test name="family">
        <string>Buggy Sans</string>
    </test>
    <edit name="lang" mode="assign">
        <minus>
            <name>lang</name>
            <langset>
                <string>ja</string>
            </langset>
        </minus>
    </edit>
</match>
---
 fontconfig/fontconfig.h |  9 +++++
 fonts.dtd               |  5 +--
 src/fccfg.c             | 24 +++++++++++++
 src/fcdbg.c             |  6 ++++
 src/fcint.h             |  3 +-
 src/fclang.c            | 62 ++++++++++++++++++++++++++++++++
 src/fcxml.c             | 80 +++++++++++++++++++++++++++++++++++++++++
 7 files changed, 186 insertions(+), 3 deletions(-)

diff --git a/fontconfig/fontconfig.h b/fontconfig/fontconfig.h
index 260955d..29a6ed4 100644
--- a/fontconfig/fontconfig.h
+++ b/fontconfig/fontconfig.h
@@ -584,6 +584,9 @@ FcLangSetCopy (const FcLangSet *ls);
 FcPublic FcBool
 FcLangSetAdd (FcLangSet *ls, const FcChar8 *lang);
 
+FcPublic FcBool
+FcLangSetDel (FcLangSet *ls, const FcChar8 *lang);
+
 FcPublic FcLangResult
 FcLangSetHasLang (const FcLangSet *ls, const FcChar8 *lang);
 
@@ -602,6 +605,12 @@ FcLangSetHash (const FcLangSet *ls);
 FcPublic FcStrSet *
 FcLangSetGetLangs (const FcLangSet *ls);
 
+FcLangSet *
+FcLangSetUnion (const FcLangSet *a, const FcLangSet *b);
+
+FcLangSet *
+FcLangSetSubtract (const FcLangSet *a, const FcLangSet *b);
+
 /* fclist.c */
 FcPublic FcObjectSet *
 FcObjectSetCreate (void);
diff --git a/fonts.dtd b/fonts.dtd
index 5f21e35..d9d4b22 100644
--- a/fonts.dtd
+++ b/fonts.dtd
@@ -107,7 +107,7 @@
 
 <!ELEMENT pattern (patelt)*>
 
-<!ENTITY % constant 'int|double|string|matrix|bool|charset|const'>
+<!ENTITY % constant 'int|double|string|matrix|bool|charset|langset|const'>
 
 <!ELEMENT patelt (%constant;)*>
 <!ATTLIST patelt
@@ -122,7 +122,7 @@
 <!ELEMENT family (#PCDATA)>
 <!ATTLIST family xml:space (default|preserve) 'preserve'>
 
-<!ENTITY % expr 'int|double|string|matrix|bool|charset
+<!ENTITY % expr 'int|double|string|matrix|bool|charset|langset
 		|name|const
 		|or|and|eq|not_eq|less|less_eq|more|more_eq|contains|not_contains
 		|plus|minus|times|divide|not|if|floor|ceil|round|trunc'>
@@ -198,6 +198,7 @@
 <!ELEMENT bool (#PCDATA)>
 <!ELEMENT charset (int|range)*>
 <!ELEMENT range (int,int)>
+<!ELEMENT langset (string)*>
 <!ELEMENT name (#PCDATA)>
 <!ATTLIST name xml:space (default|preserve) 'preserve'>
 <!ELEMENT const (#PCDATA)>
diff --git a/src/fccfg.c b/src/fccfg.c
index ad9f7d2..6812781 100644
--- a/src/fccfg.c
+++ b/src/fccfg.c
@@ -897,6 +897,11 @@ FcConfigEvaluate (FcPattern *p, FcExpr *e)
 	v.u.c = e->u.cval;
 	v = FcValueSave (v);
 	break;
+    case FcOpLangSet:
+	v.type = FcTypeLangSet;
+	v.u.l = e->u.lval;
+	v = FcValueSave (v);
+	break;
     case FcOpBool:
 	v.type = FcTypeBool;
 	v.u.b = e->u.bval;
@@ -1055,6 +1060,25 @@ FcConfigEvaluate (FcPattern *p, FcExpr *e)
 		    break;
 		}
 		break;
+	    case FcTypeLangSet:
+		switch (e->op) {
+		case FcOpPlus:
+		    v.type = FcTypeLangSet;
+		    v.u.l = FcLangSetUnion (vl.u.l, vr.u.l);
+		    if (!v.u.l)
+			v.type = FcTypeVoid;
+		    break;
+		case FcOpMinus:
+		    v.type = FcTypeLangSet;
+		    v.u.l = FcLangSetSubtract (vl.u.l, vr.u.l);
+		    if (!v.u.l)
+			v.type = FcTypeVoid;
+		    break;
+		default:
+		    v.type = FcTypeVoid;
+		    break;
+		}
+		break;
 	    default:
 		v.type = FcTypeVoid;
 		break;
diff --git a/src/fcdbg.c b/src/fcdbg.c
index 79e1953..cf2ff08 100644
--- a/src/fcdbg.c
+++ b/src/fcdbg.c
@@ -163,6 +163,7 @@ FcOpPrint (FcOp op)
     case FcOpRange: printf ("Range"); break;
     case FcOpBool: printf ("Bool"); break;
     case FcOpCharSet: printf ("CharSet"); break;
+    case FcOpLangSet: printf ("LangSet"); break;
     case FcOpField: printf ("Field"); break;
     case FcOpConst: printf ("Const"); break;
     case FcOpAssign: printf ("Assign"); break;
@@ -214,6 +215,11 @@ FcExprPrint (const FcExpr *expr)
     case FcOpRange: break;
     case FcOpBool: printf ("%s", expr->u.bval ? "true" : "false"); break;
     case FcOpCharSet: printf ("charset\n"); break;
+    case FcOpLangSet:
+	printf ("langset:");
+	FcLangSetPrint(expr->u.lval);
+	printf ("\n");
+	break;
     case FcOpNil: printf ("nil\n"); break;
     case FcOpField: printf ("%s", FcObjectName(expr->u.object)); break;
     case FcOpConst: printf ("%s", expr->u.constant); break;
diff --git a/src/fcint.h b/src/fcint.h
index 9519fff..3da6ec4 100644
--- a/src/fcint.h
+++ b/src/fcint.h
@@ -216,7 +216,7 @@ struct _FcPattern {
 				 fs->fonts[i])
 						
 typedef enum _FcOp {
-    FcOpInteger, FcOpDouble, FcOpString, FcOpMatrix, FcOpRange, FcOpBool, FcOpCharSet,
+    FcOpInteger, FcOpDouble, FcOpString, FcOpMatrix, FcOpRange, FcOpBool, FcOpCharSet, FcOpLangSet,
     FcOpNil,
     FcOpField, FcOpConst,
     FcOpAssign, FcOpAssignReplace,
@@ -239,6 +239,7 @@ typedef struct _FcExpr {
 	FcMatrix    *mval;
 	FcBool	    bval;
 	FcCharSet   *cval;
+	FcLangSet   *lval;
 	FcObject    object;
 	FcChar8	    *constant;
 	struct {
diff --git a/src/fclang.c b/src/fclang.c
index 1c78328..be42b58 100644
--- a/src/fclang.c
+++ b/src/fclang.c
@@ -71,6 +71,20 @@ FcLangSetBitGet (const FcLangSet *ls,
   return ((ls->map[bucket] >> (id & 0x1f)) & 1) ? FcTrue : FcFalse;
 }
 
+static void
+FcLangSetBitReset (FcLangSet    *ls,
+		   unsigned int  id)
+{
+  int bucket;
+
+  id = fcLangCharSetIndices[id];
+  bucket = id >> 5;
+  if (bucket >= ls->map_size)
+    return; /* shouldn't happen really */
+
+  ls->map[bucket] &= ~((FcChar32) 1 << (id & 0x1f));
+}
+
 FcLangSet *
 FcFreeTypeLangSet (const FcCharSet  *charset,
 		   const FcChar8    *exclusiveLang)
@@ -400,6 +414,23 @@ FcLangSetAdd (FcLangSet *ls, const FcChar8 *lang)
     return FcStrSetAdd (ls->extra, lang);
 }
 
+FcBool
+FcLangSetDel (FcLangSet *ls, const FcChar8 *lang)
+{
+    int	id;
+
+    id = FcLangSetIndex (lang);
+    if (id >= 0)
+    {
+	FcLangSetBitReset (ls, id);
+    }
+    else if (ls->extra)
+    {
+	FcStrSetDel (ls->extra, lang);
+    }
+    return FcTrue;
+}
+
 FcLangResult
 FcLangSetHasLang (const FcLangSet *ls, const FcChar8 *lang)
 {
@@ -818,6 +849,37 @@ FcLangSetGetLangs (const FcLangSet *ls)
     return langs;
 }
 
+static FcLangSet *
+FcLangSetOperate(const FcLangSet	*a,
+		 const FcLangSet	*b,
+		 FcBool			(*func) (FcLangSet 	*ls,
+						 const FcChar8	*s))
+{
+    FcLangSet	*langset = FcLangSetCopy (a);
+    FcStrList	*sl = FcStrListCreate (FcLangSetGetLangs (b));
+    FcChar8	*str;
+
+    while ((str = FcStrListNext (sl)))
+    {
+	func (langset, str);
+    }
+    FcStrListDone (sl);
+
+    return langset;
+}
+
+FcLangSet *
+FcLangSetUnion (const FcLangSet *a, const FcLangSet *b)
+{
+    return FcLangSetOperate(a, b, FcLangSetAdd);
+}
+
+FcLangSet *
+FcLangSetSubtract (const FcLangSet *a, const FcLangSet *b)
+{
+    return FcLangSetOperate(a, b, FcLangSetDel);
+}
+
 #define __fclang__
 #include "fcaliastail.h"
 #include "fcftaliastail.h"
diff --git a/src/fcxml.c b/src/fcxml.c
index 4d07f9d..1aa3e4c 100644
--- a/src/fcxml.c
+++ b/src/fcxml.c
@@ -145,6 +145,18 @@ FcExprCreateCharSet (FcConfig *config, FcCharSet *charset)
     return e;
 }
 
+static FcExpr *
+FcExprCreateLangSet (FcConfig *config, FcLangSet *langset)
+{
+    FcExpr *e = FcConfigAllocExpr (config);
+    if (e)
+    {
+	e->op = FcOpLangSet;
+	e->u.lval = FcLangSetCopy (langset);
+    }
+    return e;
+}
+
 static FcExpr *
 FcExprCreateField (FcConfig *config, const char *field)
 {
@@ -202,6 +214,9 @@ FcExprDestroy (FcExpr *e)
     case FcOpCharSet:
 	FcCharSetDestroy (e->u.cval);
 	break;
+    case FcOpLangSet:
+	FcLangSetDestroy (e->u.lval);
+	break;
     case FcOpBool:
 	break;
     case FcOpField:
@@ -294,6 +309,7 @@ typedef enum _FcElement {
     FcElementRange,
     FcElementBool,
     FcElementCharSet,
+    FcElementLangSet,
     FcElementName,
     FcElementConst,
     FcElementOr,
@@ -356,6 +372,7 @@ static const struct {
     { "range",		FcElementRange },
     { "bool",		FcElementBool },
     { "charset",	FcElementCharSet },
+    { "langset",	FcElementLangSet },
     { "name",		FcElementName },
     { "const",		FcElementConst },
     { "or",		FcElementOr },
@@ -420,6 +437,7 @@ typedef enum _FcVStackTag {
     FcVStackRange,
     FcVStackBool,
     FcVStackCharSet,
+    FcVStackLangSet,
 
     FcVStackTest,
     FcVStackExpr,
@@ -439,6 +457,7 @@ typedef struct _FcVStack {
 	FcRange		range;
 	FcBool		bool_;
 	FcCharSet	*charset;
+	FcLangSet	*langset;
 
 	FcTest		*test;
 	FcQual		qual;
@@ -571,6 +590,9 @@ FcTypecheckExpr (FcConfigParse *parse, FcExpr *expr, FcType type)
     case FcOpCharSet:
 	FcTypecheckValue (parse, FcTypeCharSet, type);
 	break;
+    case FcOpLangSet:
+	FcTypecheckValue (parse, FcTypeLangSet, type);
+	break;
     case FcOpNil:
 	break;
     case FcOpField:
@@ -798,6 +820,20 @@ FcVStackPushCharSet (FcConfigParse *parse, FcCharSet *charset)
     return FcTrue;
 }
 
+static FcBool
+FcVStackPushLangSet (FcConfigParse *parse, FcLangSet *langset)
+{
+    FcVStack	*vstack;
+    if (!langset)
+	return FcFalse;
+    vstack = FcVStackCreateAndPush (parse);
+    if (!vstack)
+	return FcFalse;
+    vstack->u.langset = langset;
+    vstack->tag = FcVStackLangSet;
+    return FcTrue;
+}
+
 static FcBool
 FcVStackPushTest (FcConfigParse *parse, FcTest *test)
 {
@@ -895,6 +931,9 @@ FcVStackPopAndDestroy (FcConfigParse *parse)
     case FcVStackCharSet:
 	FcCharSetDestroy (vstack->u.charset);
 	break;
+    case FcVStackLangSet:
+	FcLangSetDestroy (vstack->u.langset);
+	break;
     case FcVStackTest:
 	FcTestDestroy (vstack->u.test);
 	break;
@@ -1411,6 +1450,36 @@ FcParseCharSet (FcConfigParse *parse)
 	    FcCharSetDestroy (charset);
 }
 
+static void
+FcParseLangSet (FcConfigParse *parse)
+{
+    FcVStack	*vstack;
+    FcLangSet	*langset = FcLangSetCreate ();
+    int n = 0;
+
+    while ((vstack = FcVStackPeek (parse)))
+    {
+	switch (vstack->tag) {
+	case FcVStackString:
+	    if (!FcLangSetAdd (langset, vstack->u.string))
+	    {
+		FcConfigMessage (parse, FcSevereWarning, "invalid langset: %s", vstack->u.string);
+	    }
+	    else
+		n++;
+	    break;
+	default:
+		FcConfigMessage (parse, FcSevereError, "invalid element in langset");
+		break;
+	}
+	FcVStackPopAndDestroy (parse);
+    }
+    if (n > 0)
+	    FcVStackPushLangSet (parse, langset);
+    else
+	    FcLangSetDestroy (langset);
+}
+
 static FcBool
 FcConfigLexBinding (FcConfigParse   *parse,
 		    const FcChar8   *binding_string,
@@ -1665,6 +1734,9 @@ FcPopExpr (FcConfigParse *parse)
     case FcVStackCharSet:
 	expr = FcExprCreateCharSet (parse->config, vstack->u.charset);
 	break;
+    case FcVStackLangSet:
+	expr = FcExprCreateLangSet (parse->config, vstack->u.langset);
+	break;
     case FcVStackTest:
 	break;
     case FcVStackExpr:
@@ -2086,6 +2158,11 @@ FcPopValue (FcConfigParse *parse)
 	if (value.u.c)
 	    value.type = FcTypeCharSet;
 	break;
+    case FcVStackLangSet:
+	value.u.l = FcLangSetCopy (vstack->u.langset);
+	if (value.u.l)
+	    value.type = FcTypeLangSet;
+	break;
     default:
 	FcConfigMessage (parse, FcSevereWarning, "unknown pattern element %d",
 			 vstack->tag);
@@ -2360,6 +2437,9 @@ FcEndElement(void *userData, const XML_Char *name)
     case FcElementCharSet:
 	FcParseCharSet (parse);
 	break;
+    case FcElementLangSet:
+	FcParseLangSet (parse);
+	break;
     case FcElementSelectfont:
 	break;
     case FcElementAcceptfont:
-- 
2.39.5