fontconfig: Branch 'main' - 2 commits
GitLab Mirror
gitlab-mirror at kemper.freedesktop.org
Fri Aug 9 14:31:11 UTC 2024
doc/fcconfig.fncs | 31 ++++++++++++++++-
fontconfig/fontconfig.h | 13 +++++++
src/fccfg.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++--
src/fcinit.c | 27 ++++++++++++--
src/fcint.h | 6 ++-
test/Makefile.am | 4 ++
test/run-test-conf.sh | 1
test/test-conf.c | 64 ++++++++++++++++++++++++++++++++---
test/test-filter.c | 59 ++++++++++++++++++++++++++++++++
test/test-filter.json | 65 +++++++++++++++++++++++++++++++++++
10 files changed, 342 insertions(+), 15 deletions(-)
New commits:
commit bd83c04aa6f3cb864ba60dc5eaf2b41c4c269c63
Merge: be2a400 94614ac
Author: Akira TAGOH <akira at tagoh.org>
Date: Fri Aug 9 14:31:08 2024 +0000
Merge branch 'filter-fs' into 'main'
Add FcConfigSetFontSetFilter
See merge request fontconfig/fontconfig!328
commit 94614ac73cf412e0d43ecc31dcce57071cfa6be4
Author: Akira TAGOH <akira at tagoh.org>
Date: Fri Aug 9 22:04:28 2024 +0900
Add FcConfigSetFontSetFilter
To pre-filtering when loading fonts from caches.
FcFontSet in FcConfig will be rebuilt if FcConfig is
already initialized.
diff --git a/doc/fcconfig.fncs b/doc/fcconfig.fncs
index 49e691e..0a072af 100644
--- a/doc/fcconfig.fncs
+++ b/doc/fcconfig.fncs
@@ -71,6 +71,36 @@ in <parameter>config</parameter> since 2.12.0, returning FcFalse if that call fa
Returns the current default configuration.
@@
+ at RET@ FcConfig *
+ at FUNC@ FcConfigSetFontSetFilter
+ at TYPE1@ FcConfig * @ARG1@ config
+ at TYPE2@ FcFilterFontSetFunc% @ARG2@ filter_func
+ at TYPE3@ FcDestroyFunc% @ARG3@ destroy_data_func
+ at TYPE4@ void * @ARG4@ user_data
+ at PURPOSE@ Set a predicate function to filter fontsets
+ at DESC@
+Sets <parameter>filter_func</parameter> as a predicate function and filter out
+fontsets in <parameter>config</parameter> as desired.
+<parameter>filter_func</parameter> will be called with a font pattern and
+<parameter>user_data</parameter> only when loading caches.
+When <parameter>config</parameter> is going to be destroyed,
+<parameter>user_data</parameter> will be destroyed through
+<parameter>destroy_data_func</parameter> if it is set.
+ at SINCE@ 2.16.0
+@@
+
+ at RET@ FcBool
+ at FUNC@ FcConfigAcceptFilter
+ at TYPE1@ FcConfig * @ARG1@ config
+ at TYPE2@ const FcPattern * @ARG2@ font
+ at PURPOSE@ Test whether the given pattern matches filter
+ at DESC@
+This triggers a predicate function set by <function>FcConfigSetFontSetFilter</function>
+and return FcTrue if <parameter>font</parameter> matches something they expect.
+otherwise FcFalse.
+ at SINCE@ 2.16.0
+@@
+
@RET@ FcBool
@FUNC@ FcConfigUptoDate
@TYPE1@ FcConfig * @ARG1@ config
@@ -514,4 +544,3 @@ in configuration file. This function tries to match 'pat' with them and
return FcFalse if 'pat' is rejected, otherwise FcTrue.
@SINCE@ 2.15.1
@@
-
diff --git a/fontconfig/fontconfig.h b/fontconfig/fontconfig.h
index 47d7b83..d7df876 100644
--- a/fontconfig/fontconfig.h
+++ b/fontconfig/fontconfig.h
@@ -337,6 +337,9 @@ typedef struct _FcStrSet FcStrSet;
typedef struct _FcCache FcCache;
+typedef void (* FcDestroyFunc) (void *data);
+typedef FcBool (* FcFilterFontSetFunc) (const FcPattern *font, void *user_data);
+
_FCFUNCPROTOBEGIN
/* fcblanks.c */
@@ -457,6 +460,10 @@ FcPublic FcBool
FcConfigAcceptFont (FcConfig *config,
const FcPattern *font);
+FcPublic FcBool
+FcConfigAcceptFilter (FcConfig *config,
+ const FcPattern *font);
+
FcPublic FcBool
FcConfigAppFontAddFile (FcConfig *config,
const FcChar8 *file);
@@ -486,6 +493,12 @@ FcPublic void
FcConfigSetSysRoot (FcConfig *config,
const FcChar8 *sysroot);
+FcPublic FcConfig *
+FcConfigSetFontSetFilter (FcConfig *config,
+ FcFilterFontSetFunc filter_func,
+ FcDestroyFunc destroy_data_func,
+ void *user_data);
+
FcPublic void
FcConfigFileInfoIterInit (FcConfig *config,
FcConfigFileInfoIter *iter);
diff --git a/src/fccfg.c b/src/fccfg.c
index 41ee611..d1f5856 100644
--- a/src/fccfg.c
+++ b/src/fccfg.c
@@ -25,6 +25,7 @@
/* Objects MT-safe for readonly access. */
#include "fcint.h"
+#include "fontconfig/fontconfig.h"
#ifdef HAVE_DIRENT_H
#include <dirent.h>
#endif
@@ -202,6 +203,10 @@ FcConfigCreate (void)
if (!config->availConfigFiles)
goto bail10;
+ config->filter_func = NULL;
+ config->filter_data = NULL;
+ config->destroy_data_func = NULL;
+
FcRefInit (&config->ref, 1);
return config;
@@ -390,6 +395,9 @@ FcConfigDestroy (FcConfig *config)
if (config->sysRoot)
FcStrFree (config->sysRoot);
+ if (config->filter_data && config->destroy_data_func)
+ config->destroy_data_func (config->filter_data);
+
free (config);
}
}
@@ -453,10 +461,18 @@ FcConfigAddCache (FcConfig *config, FcCache *cache,
continue;
}
+ /*
+ * Check to see if font is banned by client
+ */
+ if (!FcConfigAcceptFilter (config, font))
+ {
+ free (relocated_font_file);
+ continue;
+ }
if (relocated_font_file)
{
- font = FcPatternCacheRewriteFile (font, cache, relocated_font_file);
- free (relocated_font_file);
+ font = FcPatternCacheRewriteFile (font, cache, relocated_font_file);
+ free (relocated_font_file);
}
if (FcFontSetAdd (config->fonts[set], font))
@@ -811,6 +827,63 @@ FcConfigSetFonts (FcConfig *config,
config->fonts[set] = fonts;
}
+FcConfig *
+FcConfigSetFontSetFilter (FcConfig *config,
+ FcFilterFontSetFunc filter_func,
+ FcDestroyFunc destroy_data_func,
+ void *user_data)
+{
+ FcBool rebuild = FcFalse;
+
+ if (!config)
+ {
+ /* Do not use FcConfigEnsure() here for optimization */
+ retry:
+ config = fc_atomic_ptr_get (&_fcConfig);
+ if (!config)
+ config = FcConfigCreate ();
+ else
+ rebuild = FcTrue;
+ }
+ else
+ rebuild = FcTrue;
+ if (config->filter_data == user_data &&
+ config->filter_func == filter_func)
+ {
+ /* No need to update */
+ rebuild = FcFalse;
+ }
+ else
+ {
+ if (config->filter_data && config->destroy_data_func)
+ {
+ config->destroy_data_func (config->filter_data);
+ }
+ config->filter_func = filter_func;
+ config->destroy_data_func = destroy_data_func;
+ config->filter_data = user_data;
+ }
+
+ if (rebuild)
+ {
+ /* Rebuild FontSet */
+ FcConfigBuildFonts (config);
+ }
+ else
+ {
+ /* Initialize FcConfig with regular procedure */
+ config = FcInitLoadOwnConfigAndFonts (config);
+
+ if (!config || !fc_atomic_ptr_cmpexch (&_fcConfig, NULL, config))
+ {
+ if (config)
+ FcConfigDestroy (config);
+ goto retry;
+ }
+ }
+
+ return config;
+}
FcBlanks *
FcBlanksCreate (void)
@@ -2985,6 +3058,16 @@ FcConfigAcceptFont (FcConfig *config,
return FcTrue;
}
+FcBool
+FcConfigAcceptFilter (FcConfig *config,
+ const FcPattern *font)
+{
+ if (config && config->filter_func)
+ {
+ return config->filter_func (font, config->filter_data);
+ }
+ return FcTrue;
+}
const FcChar8 *
FcConfigGetSysRoot (const FcConfig *config)
{
diff --git a/src/fcinit.c b/src/fcinit.c
index c05cdc5..e267821 100644
--- a/src/fcinit.c
+++ b/src/fcinit.c
@@ -65,6 +65,26 @@ bail0:
return 0;
}
+static FcConfig *
+FcInitFallbackConfigWithFilter (FcConfig *config, const FcChar8 *sysroot)
+{
+ FcConfig *fallback = FcInitFallbackConfig (sysroot);
+
+ /* Copy filter data */
+ fallback->filter_func = config->filter_func;
+ fallback->filter_data = config->filter_data;
+ fallback->destroy_data_func = config->destroy_data_func;
+ config->filter_func = NULL;
+ config->filter_data = NULL;
+ config->destroy_data_func = NULL;
+ /* Rebuild fontset */
+ FcConfigBuildFonts (fallback);
+
+ FcConfigDestroy (config);
+
+ return fallback;
+}
+
int
FcGetVersion (void)
{
@@ -89,9 +109,7 @@ FcInitLoadOwnConfig (FcConfig *config)
if (!FcConfigParseAndLoad (config, 0, FcTrue))
{
const FcChar8 *sysroot = FcConfigGetSysRoot (config);
- FcConfig *fallback = FcInitFallbackConfig (sysroot);
-
- FcConfigDestroy (config);
+ FcConfig *fallback = FcInitFallbackConfigWithFilter (config, sysroot);
return fallback;
}
@@ -144,8 +162,7 @@ FcInitLoadOwnConfig (FcConfig *config)
"Fontconfig error: out of memory");
if (prefix)
FcStrFree (prefix);
- fallback = FcInitFallbackConfig (sysroot);
- FcConfigDestroy (config);
+ fallback = FcInitFallbackConfigWithFilter (config, sysroot);
return fallback;
}
diff --git a/src/fcint.h b/src/fcint.h
index 86676b3..8a3c0ac 100644
--- a/src/fcint.h
+++ b/src/fcint.h
@@ -328,8 +328,6 @@ typedef struct _FcEdit {
FcValueBinding binding;
} FcEdit;
-typedef void (* FcDestroyFunc) (void *data);
-
typedef struct _FcPtrList FcPtrList;
/* need to sync with FcConfigFileInfoIter at fontconfig.h */
typedef struct _FcPtrListIter {
@@ -580,6 +578,10 @@ struct _FcConfig {
FcChar8 *sysRoot; /* override the system root directory */
FcStrSet *availConfigFiles; /* config files available */
FcPtrList *rulesetList; /* List of rulesets being installed */
+
+ FcFilterFontSetFunc filter_func; /* A predicate function to filter out config->fonts */
+ FcDestroyFunc destroy_data_func; /* A callback function to destroy config->filter_data */
+ void *filter_data; /* An user data to be used for filter_func */
};
typedef struct _FcFileTime {
diff --git a/test/Makefile.am b/test/Makefile.am
index 3b79c78..3b4e3e8 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -49,6 +49,7 @@ TESTDATA = \
test-70-no-bitmaps-and-emoji.json \
test-70-no-bitmaps-except-emoji.json \
test-90-synthetic.json \
+ test-filter.json \
test-issue-286.json \
test-style-match.json \
$(NULL)
@@ -176,6 +177,9 @@ check_PROGRAMS += test-family-matching
test_family_matching_LDADD = $(top_builddir)/src/libfontconfig.la
TESTS += test-family-matching
+check_PROGRAMS += test-filter
+test_filter_LDADD = $(top_builddir)/src/libfontconfig.la
+
EXTRA_DIST=run-test.sh run-test-conf.sh wrapper-script.sh $(TESTDATA) out.expected-long-family-names out.expected-no-long-family-names
CLEANFILES = \
diff --git a/test/run-test-conf.sh b/test/run-test-conf.sh
index cc41185..88407cd 100644
--- a/test/run-test-conf.sh
+++ b/test/run-test-conf.sh
@@ -53,6 +53,7 @@ done
for i in \
test-issue-286.json \
test-style-match.json \
+ test-filter.json \
; do
echo $RUNNER $TESTDIR/$i ...
$RUNNER $TESTDIR/../conf.d/10-autohint.conf $TESTDIR/$i
diff --git a/test/test-conf.c b/test/test-conf.c
index 8b298ef..b5f702b 100644
--- a/test/test-conf.c
+++ b/test/test-conf.c
@@ -278,7 +278,7 @@ build_pattern (json_object *obj)
}
static FcFontSet *
-build_fs (FcConfig *config, json_object *obj)
+build_fs (FcConfig *config, json_object *obj, FcBool filter)
{
FcFontSet *fs = FcFontSetCreate ();
int i, n;
@@ -292,7 +292,8 @@ build_fs (FcConfig *config, json_object *obj)
if (json_object_get_type (o) != json_type_object)
continue;
pat = build_pattern (o);
- if (FcConfigAcceptFont (config, pat))
+ if (FcConfigAcceptFont (config, pat) &&
+ (!filter || FcConfigAcceptFilter (config, pat)))
FcFontSetAdd (fs, pat);
else
FcPatternDestroy(pat);
@@ -301,19 +302,71 @@ build_fs (FcConfig *config, json_object *obj)
return fs;
}
+static FcBool
+filter_func (const FcPattern *f, void *user_data)
+{
+ FcPattern *filter = (FcPattern *)user_data;
+ FcPatternIter iter;
+ FcBool ret = FcTrue;
+
+ FcPatternIterStart (filter, &iter);
+ if (!(ret = FcPatternIterIsValid (filter, &iter)))
+ goto bail;
+ do
+ {
+ const char *obj = FcPatternIterGetObject (filter, &iter);
+ int i, n = FcPatternIterValueCount(filter, &iter);
+
+ for (i = 0; i < n; i++)
+ {
+ FcValue v, v2;
+ FcValueBinding b;
+
+ if (FcPatternIterGetValue (filter, &iter, i, &v, &b) != FcResultMatch)
+ {
+ ret = FcFalse;
+ goto bail;
+ }
+ if (FcPatternGet (f, obj, 0, &v2) != FcResultMatch)
+ {
+ ret = FcFalse;
+ goto bail;
+ }
+ if (!FcValueEqual (v, v2))
+ {
+ ret = FcFalse;
+ goto bail;
+ }
+ }
+ } while (FcPatternIterNext (filter, &iter));
+bail:
+ return ret;
+}
+
static FcBool
build_fonts (FcConfig *config, json_object *root)
{
- json_object *fonts;
+ json_object *fonts, *filter;
FcFontSet *fs;
+ FcPattern *filterpat;
+ if (json_object_object_get_ex (root, "filter", &filter))
+ {
+ if (json_object_get_type (filter) != json_type_object)
+ {
+ fprintf (stderr, "W: Invalid filter defined\n");
+ return FcFalse;
+ }
+ filterpat = build_pattern (filter);
+ FcConfigSetFontSetFilter(config, filter_func, (FcDestroyFunc)FcPatternDestroy, filterpat);
+ }
if (!json_object_object_get_ex (root, "fonts", &fonts) ||
json_object_get_type (fonts) != json_type_array)
{
fprintf (stderr, "W: No fonts defined\n");
return FcFalse;
}
- fs = build_fs (config, fonts);
+ fs = build_fs (config, fonts, FcTrue);
/* FcConfigSetFonts (config, fs, FcSetSystem); */
if (config->fonts[FcSetSystem])
FcFontSetDestroy (config->fonts[FcSetSystem]);
@@ -341,6 +394,7 @@ run_test (FcConfig *config, json_object *root)
json_object_iter iter;
FcPattern *query = NULL;
FcPattern *result = NULL;
+ FcPattern *filterpat = NULL;
FcFontSet *result_fs = NULL;
const char *method = NULL;
@@ -388,7 +442,7 @@ run_test (FcConfig *config, json_object *root)
}
if (result_fs)
FcFontSetDestroy (result_fs);
- result_fs = build_fs (config, iter.val);
+ result_fs = build_fs (config, iter.val, FcFalse);
}
else if (strcmp (iter.key, "$comment") == 0)
{
diff --git a/test/test-filter.c b/test/test-filter.c
new file mode 100644
index 0000000..9eebdf0
--- /dev/null
+++ b/test/test-filter.c
@@ -0,0 +1,59 @@
+#include <stdio.h>
+#include <fontconfig/fontconfig.h>
+#include <time.h>
+
+static FcBool
+filter (const FcPattern *f, void *user_data)
+{
+ FcChar8 *s = NULL;
+
+ if (FcPatternGetString (f, FC_FONT_WRAPPER, 0, &s) == FcResultMatch)
+ {
+ /* accept "SFNT" only */
+ if (FcStrCmp (s, (FcChar8 *)"SFNT") == 0)
+ return FcTrue;
+ }
+ return FcFalse;
+}
+
+int
+main (void)
+{
+ FcPattern *p;
+ FcObjectSet *os;
+ FcFontSet *fs;
+ int i, ret = 0;
+ FcChar8 *s = NULL, *f;
+
+ FcConfigSetFontSetFilter(NULL, filter, NULL, NULL);
+ p = FcPatternCreate ();
+ os = FcObjectSetBuild (FC_FAMILY, FC_STYLE, FC_FILE, FC_FONT_WRAPPER, NULL);
+ fs = FcFontList (NULL, p, os);
+ FcObjectSetDestroy (os);
+ FcPatternDestroy (p);
+
+ printf ("%d matched\n", fs->nfont);
+ for (i = 0; i < fs->nfont; i++)
+ {
+ if (FcPatternGetString (fs->fonts[i], FC_FONT_WRAPPER, 0, &s) == FcResultMatch)
+ {
+ f = FcPatternFormat (fs->fonts[i], (FcChar8 *)"%{=fclist}\n");
+ printf ("%s", f);
+ FcStrFree (f);
+ if (FcStrCmp (s, (FcChar8 *)"SFNT") != 0)
+ {
+ printf ("failed:\n");
+ fail:
+ ret = 1;
+ }
+ }
+ else
+ {
+ printf ("no font wrapper\n");
+ goto fail;
+ }
+ }
+ FcFontSetDestroy (fs);
+
+ return ret;
+}
diff --git a/test/test-filter.json b/test/test-filter.json
new file mode 100644
index 0000000..25bce57
--- /dev/null
+++ b/test/test-filter.json
@@ -0,0 +1,65 @@
+{
+ "fonts": [
+ {
+ "family": [
+ "Foo"
+ ],
+ "style": [
+ "Regular"
+ ],
+ "file": "/path/to/Foo.ttf",
+ "fontwrapper": "SFNT"
+ },
+ {
+ "family": [
+ "Bar"
+ ],
+ "style": [
+ "Regular"
+ ],
+ "file": "/path/to/Bar.otf",
+ "fontwrapper": "CFF"
+ },
+ {
+ "family": [
+ "Baz"
+ ],
+ "style": [
+ "Regular"
+ ],
+ "file": "/path/to/Baz.woff",
+ "fontwrapper": "WOFF"
+ },
+ {
+ "family": [
+ "Blah"
+ ],
+ "style": [
+ "Regular"
+ ],
+ "file": "/path/to/Baz.bdf"
+ }
+ ],
+ "filter": {
+ "fontwrapper": "SFNT"
+ },
+ "tests": [
+ {
+ "method": "list",
+ "query": {
+ },
+ "result_fs": [
+ {
+ "family": [
+ "Foo"
+ ],
+ "style": [
+ "Regular"
+ ],
+ "file": "/path/to/Foo.ttf",
+ "fontwrapper": "SFNT"
+ }
+ ]
+ }
+ ]
+}
More information about the Fontconfig
mailing list