+ return FcTrue;
+}
+
+static FcBool
+interpret_filter (FcFormatContext *c,
+ FcPattern *pat,
+ FcStrBuf *buf)
+{
+ FcObjectSet *os;
+ FcPattern *subpat;
+
+ if (!expect_char (c, '+'))
+ return FcFalse;
+
+ os = FcObjectSetCreate ();
+ if (!os)
+ return FcFalse;
+
+ do
+ {
+ if (!read_elt_name_to_scratch (c) ||
+ !FcObjectSetAdd (os, (const char *) c->scratch))
+ {
+ FcObjectSetDestroy (os);
+ return FcFalse;
+ }
+ }
+ while (consume_char (c, ','));
+
+ subpat = FcPatternFilter (pat, os);
+ FcObjectSetDestroy (os);
+
+ if (!subpat ||
+ !interpret_subexpr (c, subpat, buf))
+ return FcFalse;
+
+ FcPatternDestroy (subpat);
+ return FcTrue;
+}
+
+static FcBool
+interpret_delete (FcFormatContext *c,
+ FcPattern *pat,
+ FcStrBuf *buf)
+{
+ FcPattern *subpat;
+
+ if (!expect_char (c, '-'))
+ return FcFalse;
+
+ subpat = FcPatternDuplicate (pat);
+ if (!subpat)
+ return FcFalse;
+
+ do
+ {
+ if (!read_elt_name_to_scratch (c))
+ {
+ FcPatternDestroy (subpat);
+ return FcFalse;
+ }
+
+ FcPatternDel (subpat, (const char *) c->scratch);
+ }
+ while (consume_char (c, ','));
+
+ if (!interpret_subexpr (c, subpat, buf))
+ return FcFalse;
+
+ FcPatternDestroy (subpat);
+ return FcTrue;
+}
+
+static FcBool
+interpret_percent (FcFormatContext *c,
+ FcPattern *pat,
+ FcStrBuf *buf)
+{
+ int width, before;
+
+ if (!expect_char (c, '%'))
+ return FcFalse;
+
+ if (consume_char (c, '%')) /* "%%" */
+ {
+ FcStrBufChar (buf, '%');
+ return FcTrue;
+ }
+
+ /* parse an optional width specifier */
+ width = strtol ((const char *) c->format, (char **) &c->format, 10);
+
+ before = buf->len;
+
+ if (!expect_char (c, '{'))
+ return FcFalse;
+
+ switch (*c->format) {
+
+ case '{':
+ /* subexpression */
+ if (!interpret_subexpr (c, pat, buf))
+ return FcFalse;
+ break;
+
+ case '+':
+ /* filtering pattern elements */
+ if (!interpret_filter (c, pat, buf))
+ return FcFalse;
+ break;
+
+ case '-':
+ /* deleting pattern elements */
+ if (!interpret_delete (c, pat, buf))
+ return FcFalse;
+ break;
+
+ default:
+ /* simple tag */
+ if (!interpret_simple_tag (c, pat, buf))
+ return FcFalse;
+ break;
+
+ }
+