fontconfig/src/fccfg.c

3263 lines
70 KiB
C
Raw Permalink Normal View History

2002-02-15 00:34:13 +01:00
/*
2008-08-12 22:34:24 +02:00
* fontconfig/src/fccfg.c
2002-02-15 00:34:13 +01:00
*
2004-12-07 02:14:46 +01:00
* Copyright © 2000 Keith Packard
2002-02-15 00:34:13 +01:00
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of the author(s) not be used in
2002-02-15 00:34:13 +01:00
* advertising or publicity pertaining to distribution of the software without
* specific, written prior permission. The authors make no
2002-02-15 00:34:13 +01:00
* representations about the suitability of this software for any purpose. It
* is provided "as is" without express or implied warranty.
*
* THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
2002-02-15 00:34:13 +01:00
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
2002-02-15 00:34:13 +01:00
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
2012-10-07 23:02:50 +02:00
/* Objects MT-safe for readonly access. */
#include "fcint.h"
#ifdef HAVE_DIRENT_H
#include <dirent.h>
#endif
#include <sys/types.h>
2002-02-15 00:34:13 +01:00
#if defined (_WIN32) && !defined (R_OK)
#define R_OK 4
#endif
2020-06-25 19:38:48 +02:00
#if defined(_WIN32) && !defined(S_ISFIFO)
#define S_ISFIFO(m) 0
#endif
2012-10-07 23:02:50 +02:00
static FcConfig *_fcConfig; /* MT-safe */
static FcMutex *_lock;
static void
lock_config (void)
{
FcMutex *lock;
retry:
lock = fc_atomic_ptr_get (&_lock);
if (!lock)
{
lock = (FcMutex *) malloc (sizeof (FcMutex));
FcMutexInit (lock);
if (!fc_atomic_ptr_cmpexch (&_lock, NULL, lock))
{
FcMutexFinish (lock);
free (lock);
goto retry;
}
FcMutexLock (lock);
/* Initialize random state */
FcRandom ();
return;
}
FcMutexLock (lock);
}
static void
unlock_config (void)
{
FcMutex *lock;
lock = fc_atomic_ptr_get (&_lock);
FcMutexUnlock (lock);
}
static void
free_lock (void)
{
FcMutex *lock;
lock = fc_atomic_ptr_get (&_lock);
if (lock && fc_atomic_ptr_cmpexch (&_lock, lock, NULL))
{
FcMutexFinish (lock);
free (lock);
}
}
2012-10-07 23:02:50 +02:00
static FcConfig *
FcConfigEnsure (void)
{
FcConfig *config;
retry:
2012-10-07 23:02:50 +02:00
config = fc_atomic_ptr_get (&_fcConfig);
if (!config)
2012-10-07 23:02:50 +02:00
{
config = FcInitLoadConfigAndFonts ();
if (!config || !fc_atomic_ptr_cmpexch (&_fcConfig, NULL, config)) {
if (config)
FcConfigDestroy (config);
goto retry;
2012-10-07 23:02:50 +02:00
}
}
return config;
}
static void
FcDestroyAsRule (void *data)
{
FcRuleDestroy (data);
}
static void
FcDestroyAsRuleSet (void *data)
{
FcRuleSetDestroy (data);
}
2012-10-07 23:02:50 +02:00
FcBool
FcConfigInit (void)
{
return FcConfigEnsure () ? FcTrue : FcFalse;
}
void
FcConfigFini (void)
{
2012-10-07 23:02:50 +02:00
FcConfig *cfg = fc_atomic_ptr_get (&_fcConfig);
if (cfg && fc_atomic_ptr_cmpexch (&_fcConfig, cfg, NULL))
FcConfigDestroy (cfg);
free_lock ();
}
2002-02-15 00:34:13 +01:00
FcConfig *
FcConfigCreate (void)
{
FcSetName set;
FcConfig *config;
FcMatchKind k;
FcBool err = FcFalse;
2002-02-15 00:34:13 +01:00
config = malloc (sizeof (FcConfig));
if (!config)
goto bail0;
2010-04-12 18:18:50 +02:00
config->configDirs = FcStrSetCreate ();
if (!config->configDirs)
2002-02-15 00:34:13 +01:00
goto bail1;
2010-04-12 18:18:50 +02:00
Replace UUID file mechanism with per-directory 'map' attribute [v2] The UUID files would be placed in each font directory to provide the unique cache name, independent of path, for that directory. The UUID files are undesireable for a couple of reasons: 1) They must be placed in the font directories to be useful. This requires modifying the font directories themselves, introducing potential visible timestamp changes when running multiple applications, and makes the cache processing inconsistent between applications with permission to write to the font directories and applications without such permission. 2) The UUID contents were generated randomly, which makes the font cache not reproducible across multiple runs. One proposed fix for 2) is to make the UUID dependent on the font directory path, but once we do that, we can simply use the font directory path itself as the key as the original MD5-based font cache naming mechanism did. The goal of the UUID file mechanism was to fix startup time of flatpaks; as the font path names inside the flatpak did not match the font path names in the base system, the font cache would need to be reconstructed the first time the flatpak was launched. The new mechanism for doing this is to allow each '<dir>' element in the configuration include a 'map' attribute. When looking for a cache file for a particular directory, if the directory name starts with the contents of the <dir> element, that portion of the name will be replaced with the value of the 'map' attribute. Outside of the flatpak, nothing need change -- fontconfig will build cache files using real directory names. Inside the flatpak, the custom fonts.conf file will now include mappings such as this: <dir map="/usr/share/fonts">/run/host/fonts</dir> When scanning the directory /run/host/fonts/ttf, fontconfig will use the name /usr/share/fonts/ttf as the source for building the cache file name. The existing FC_FILE replacement code used for the UUID-based implementation continues to correctly adapt font path names seen by applications. v2: Leave FcDirCacheCreateUUID stub around to avoid removing public API function. Document 'map' attribute of <dir> element in fontconfig-user.sgml Suggested-by: Akira TAGOH <akira@tagoh.org> Signed-off-by: Keith Packard <keithp@keithp.com>
2018-10-30 00:39:05 +01:00
config->configMapDirs = FcStrSetCreate();
if (!config->configMapDirs)
goto bail1_5;
config->configFiles = FcStrSetCreate ();
2002-02-15 00:34:13 +01:00
if (!config->configFiles)
goto bail2;
2010-04-12 18:18:50 +02:00
config->fontDirs = FcStrSetCreate ();
if (!config->fontDirs)
goto bail3;
2010-04-12 18:18:50 +02:00
config->acceptGlobs = FcStrSetCreate ();
if (!config->acceptGlobs)
goto bail4;
config->rejectGlobs = FcStrSetCreate ();
if (!config->rejectGlobs)
goto bail5;
config->acceptPatterns = FcFontSetCreate ();
if (!config->acceptPatterns)
goto bail6;
2010-04-12 18:18:50 +02:00
config->rejectPatterns = FcFontSetCreate ();
if (!config->rejectPatterns)
goto bail7;
config->cacheDirs = FcStrSetCreate ();
if (!config->cacheDirs)
goto bail8;
2010-04-12 18:18:50 +02:00
for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
{
config->subst[k] = FcPtrListCreate (FcDestroyAsRuleSet);
if (!config->subst[k])
err = FcTrue;
}
if (err)
goto bail9;
2002-02-15 00:34:13 +01:00
config->maxObjects = 0;
for (set = FcSetSystem; set <= FcSetApplication; set++)
config->fonts[set] = 0;
config->rescanTime = time(0);
2010-04-12 18:18:50 +02:00
config->rescanInterval = 30;
2008-08-23 00:08:07 +02:00
config->expr_pool = NULL;
config->sysRoot = FcStrRealPath ((const FcChar8 *) getenv("FONTCONFIG_SYSROOT"));
config->rulesetList = FcPtrListCreate (FcDestroyAsRuleSet);
if (!config->rulesetList)
goto bail9;
config->availConfigFiles = FcStrSetCreate ();
if (!config->availConfigFiles)
goto bail10;
FcRefInit (&config->ref, 1);
2010-04-12 18:18:50 +02:00
2002-02-15 00:34:13 +01:00
return config;
bail10:
FcPtrListDestroy (config->rulesetList);
bail9:
for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
if (config->subst[k])
FcPtrListDestroy (config->subst[k]);
FcStrSetDestroy (config->cacheDirs);
bail8:
FcFontSetDestroy (config->rejectPatterns);
bail7:
FcFontSetDestroy (config->acceptPatterns);
bail6:
FcStrSetDestroy (config->rejectGlobs);
bail5:
FcStrSetDestroy (config->acceptGlobs);
bail4:
FcStrSetDestroy (config->fontDirs);
2002-02-15 00:34:13 +01:00
bail3:
FcStrSetDestroy (config->configFiles);
2002-02-15 00:34:13 +01:00
bail2:
Replace UUID file mechanism with per-directory 'map' attribute [v2] The UUID files would be placed in each font directory to provide the unique cache name, independent of path, for that directory. The UUID files are undesireable for a couple of reasons: 1) They must be placed in the font directories to be useful. This requires modifying the font directories themselves, introducing potential visible timestamp changes when running multiple applications, and makes the cache processing inconsistent between applications with permission to write to the font directories and applications without such permission. 2) The UUID contents were generated randomly, which makes the font cache not reproducible across multiple runs. One proposed fix for 2) is to make the UUID dependent on the font directory path, but once we do that, we can simply use the font directory path itself as the key as the original MD5-based font cache naming mechanism did. The goal of the UUID file mechanism was to fix startup time of flatpaks; as the font path names inside the flatpak did not match the font path names in the base system, the font cache would need to be reconstructed the first time the flatpak was launched. The new mechanism for doing this is to allow each '<dir>' element in the configuration include a 'map' attribute. When looking for a cache file for a particular directory, if the directory name starts with the contents of the <dir> element, that portion of the name will be replaced with the value of the 'map' attribute. Outside of the flatpak, nothing need change -- fontconfig will build cache files using real directory names. Inside the flatpak, the custom fonts.conf file will now include mappings such as this: <dir map="/usr/share/fonts">/run/host/fonts</dir> When scanning the directory /run/host/fonts/ttf, fontconfig will use the name /usr/share/fonts/ttf as the source for building the cache file name. The existing FC_FILE replacement code used for the UUID-based implementation continues to correctly adapt font path names seen by applications. v2: Leave FcDirCacheCreateUUID stub around to avoid removing public API function. Document 'map' attribute of <dir> element in fontconfig-user.sgml Suggested-by: Akira TAGOH <akira@tagoh.org> Signed-off-by: Keith Packard <keithp@keithp.com>
2018-10-30 00:39:05 +01:00
FcStrSetDestroy (config->configMapDirs);
bail1_5:
FcStrSetDestroy (config->configDirs);
2002-02-15 00:34:13 +01:00
bail1:
free (config);
bail0:
return 0;
}
static FcFileTime
FcConfigNewestFile (FcStrSet *files)
{
FcStrList *list = FcStrListCreate (files);
FcFileTime newest = { 0, FcFalse };
FcChar8 *file;
struct stat statb;
if (list)
{
while ((file = FcStrListNext (list)))
if (FcStat (file, &statb) == 0)
if (!newest.set || statb.st_mtime - newest.time > 0)
{
newest.set = FcTrue;
newest.time = statb.st_mtime;
}
FcStrListDone (list);
}
return newest;
}
FcBool
FcConfigUptoDate (FcConfig *config)
{
FcFileTime config_time, config_dir_time, font_time;
time_t now = time(0);
FcBool ret = FcTrue;
config = FcConfigReference (config);
if (!config)
return FcFalse;
config_time = FcConfigNewestFile (config->configFiles);
config_dir_time = FcConfigNewestFile (config->configDirs);
font_time = FcConfigNewestFile (config->fontDirs);
if ((config_time.set && config_time.time - config->rescanTime > 0) ||
(config_dir_time.set && (config_dir_time.time - config->rescanTime) > 0) ||
(font_time.set && (font_time.time - config->rescanTime) > 0))
{
/* We need to check for potential clock problems here (OLPC ticket #6046) */
if ((config_time.set && (config_time.time - now) > 0) ||
(config_dir_time.set && (config_dir_time.time - now) > 0) ||
(font_time.set && (font_time.time - now) > 0))
{
fprintf (stderr,
"Fontconfig warning: Directory/file mtime in the future. New fonts may not be detected.\n");
config->rescanTime = now;
goto bail;
}
else
{
ret = FcFalse;
goto bail;
}
}
config->rescanTime = now;
bail:
FcConfigDestroy (config);
return ret;
}
FcExpr *
FcConfigAllocExpr (FcConfig *config)
{
if (!config->expr_pool || config->expr_pool->next == config->expr_pool->end)
{
FcExprPage *new_page;
new_page = malloc (sizeof (FcExprPage));
if (!new_page)
return 0;
new_page->next_page = config->expr_pool;
new_page->next = new_page->exprs;
config->expr_pool = new_page;
}
return config->expr_pool->next++;
}
2008-08-23 00:08:07 +02:00
FcConfig *
FcConfigReference (FcConfig *config)
{
if (!config)
{
/* lock during obtaining the value from _fcConfig and count up refcount there,
* there are the race between them.
*/
lock_config ();
retry:
config = fc_atomic_ptr_get (&_fcConfig);
2008-08-23 00:08:07 +02:00
if (!config)
{
unlock_config ();
2008-08-23 00:08:07 +02:00
config = FcInitLoadConfigAndFonts ();
if (!config)
goto retry;
lock_config ();
if (!fc_atomic_ptr_cmpexch (&_fcConfig, NULL, config))
{
FcConfigDestroy (config);
goto retry;
}
}
FcRefInc (&config->ref);
unlock_config ();
}
else
FcRefInc (&config->ref);
2008-08-23 00:08:07 +02:00
return config;
}
2002-02-15 00:34:13 +01:00
void
FcConfigDestroy (FcConfig *config)
{
FcSetName set;
FcExprPage *page;
FcMatchKind k;
2002-02-15 00:34:13 +01:00
if (config)
{
if (FcRefDec (&config->ref) != 1)
return;
2008-08-23 00:08:07 +02:00
(void) fc_atomic_ptr_cmpexch (&_fcConfig, config, NULL);
FcStrSetDestroy (config->configDirs);
FcStrSetDestroy (config->configMapDirs);
FcStrSetDestroy (config->fontDirs);
FcStrSetDestroy (config->cacheDirs);
FcStrSetDestroy (config->configFiles);
FcStrSetDestroy (config->acceptGlobs);
FcStrSetDestroy (config->rejectGlobs);
FcFontSetDestroy (config->acceptPatterns);
FcFontSetDestroy (config->rejectPatterns);
for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
FcPtrListDestroy (config->subst[k]);
FcPtrListDestroy (config->rulesetList);
FcStrSetDestroy (config->availConfigFiles);
for (set = FcSetSystem; set <= FcSetApplication; set++)
if (config->fonts[set])
FcFontSetDestroy (config->fonts[set]);
page = config->expr_pool;
while (page)
{
FcExprPage *next = page->next_page;
free (page);
page = next;
}
if (config->sysRoot)
FcStrFree (config->sysRoot);
free (config);
}
2002-02-15 00:34:13 +01:00
}
/*
* Add cache to configuration, adding fonts and directories
2002-02-15 00:34:13 +01:00
*/
FcBool
2010-04-12 18:18:50 +02:00
FcConfigAddCache (FcConfig *config, FcCache *cache,
FcSetName set, FcStrSet *dirSet, FcChar8 *forDir)
2002-02-15 00:34:13 +01:00
{
FcFontSet *fs;
intptr_t *dirs;
int i;
FcBool relocated = FcFalse;
if (strcmp ((char *)FcCacheDir(cache), (char *)forDir) != 0)
relocated = FcTrue;
2002-02-15 00:34:13 +01:00
/*
* Add fonts
*/
fs = FcCacheSet (cache);
if (fs)
{
int nref = 0;
for (i = 0; i < fs->nfont; i++)
{
FcPattern *font = FcFontSetFont (fs, i);
FcChar8 *font_file;
FcChar8 *relocated_font_file = NULL;
if (FcPatternObjectGetString (font, FC_FILE_OBJECT,
0, &font_file) == FcResultMatch)
{
if (relocated)
{
FcChar8 *slash = FcStrLastSlash (font_file);
relocated_font_file = FcStrBuildFilename (forDir, slash + 1, NULL);
font_file = relocated_font_file;
}
/*
* Check to see if font is banned by filename
*/
if (!FcConfigAcceptFilename (config, font_file))
{
free (relocated_font_file);
continue;
}
}
/*
* Check to see if font is banned by pattern
*/
if (!FcConfigAcceptFont (config, font))
{
free (relocated_font_file);
continue;
}
if (relocated_font_file)
{
font = FcPatternCacheRewriteFile (font, cache, relocated_font_file);
free (relocated_font_file);
}
if (FcFontSetAdd (config->fonts[set], font))
nref++;
}
FcDirCacheReference (cache, nref);
}
/*
* Add directories
*/
dirs = FcCacheDirs (cache);
if (dirs)
2002-02-15 00:34:13 +01:00
{
for (i = 0; i < cache->dirs_count; i++)
{
const FcChar8 *dir = FcCacheSubdir (cache, i);
FcChar8 *s = NULL;
if (relocated)
{
FcChar8 *base = FcStrBasename (dir);
dir = s = FcStrBuildFilename (forDir, base, NULL);
FcStrFree (base);
}
if (FcConfigAcceptFilename (config, dir))
FcStrSetAddFilename (dirSet, dir);
if (s)
FcStrFree (s);
}
}
return FcTrue;
}
static FcBool
FcConfigAddDirList (FcConfig *config, FcSetName set, FcStrSet *dirSet)
{
FcStrList *dirlist;
FcChar8 *dir;
FcCache *cache;
2010-04-12 18:18:50 +02:00
dirlist = FcStrListCreate (dirSet);
if (!dirlist)
return FcFalse;
while ((dir = FcStrListNext (dirlist)))
{
if (FcDebug () & FC_DBG_FONTSET)
printf ("adding fonts from %s\n", dir);
cache = FcDirCacheRead (dir, FcFalse, config);
if (!cache)
continue;
FcConfigAddCache (config, cache, set, dirSet, dir);
FcDirCacheUnload (cache);
}
FcStrListDone (dirlist);
return FcTrue;
}
/*
* Scan the current list of directories in the configuration
* and build the set of available fonts.
*/
2005-11-29 01:21:05 +01:00
FcBool
FcConfigBuildFonts (FcConfig *config)
{
FcFontSet *fonts;
FcBool ret = FcTrue;
config = FcConfigReference (config);
if (!config)
return FcFalse;
fonts = FcFontSetCreate ();
if (!fonts)
{
ret = FcFalse;
goto bail;
}
2010-04-12 18:18:50 +02:00
FcConfigSetFonts (config, fonts, FcSetSystem);
2010-04-12 18:18:50 +02:00
if (!FcConfigAddDirList (config, FcSetSystem, config->fontDirs))
{
ret = FcFalse;
goto bail;
}
2002-02-15 00:34:13 +01:00
if (FcDebug () & FC_DBG_FONTSET)
FcFontSetPrint (fonts);
bail:
FcConfigDestroy (config);
return ret;
2002-02-15 00:34:13 +01:00
}
FcBool
FcConfigSetCurrent (FcConfig *config)
{
2012-10-07 23:02:50 +02:00
FcConfig *cfg;
if (config)
{
if (!config->fonts[FcSetSystem])
if (!FcConfigBuildFonts (config))
return FcFalse;
FcRefInc (&config->ref);
}
lock_config ();
2012-10-07 23:02:50 +02:00
retry:
cfg = fc_atomic_ptr_get (&_fcConfig);
if (config == cfg)
{
unlock_config ();
if (config)
FcConfigDestroy (config);
return FcTrue;
}
2002-02-15 00:34:13 +01:00
2012-10-07 23:02:50 +02:00
if (!fc_atomic_ptr_cmpexch (&_fcConfig, cfg, config))
goto retry;
unlock_config ();
2012-10-07 23:02:50 +02:00
if (cfg)
FcConfigDestroy (cfg);
2002-02-15 00:34:13 +01:00
return FcTrue;
}
FcConfig *
FcConfigGetCurrent (void)
{
2012-10-07 23:02:50 +02:00
return FcConfigEnsure ();
2002-02-15 00:34:13 +01:00
}
FcBool
FcConfigAddConfigDir (FcConfig *config,
const FcChar8 *d)
2002-02-15 00:34:13 +01:00
{
return FcStrSetAddFilename (config->configDirs, d);
}
2002-02-15 00:34:13 +01:00
FcStrList *
FcConfigGetConfigDirs (FcConfig *config)
{
FcStrList *ret;
config = FcConfigReference (config);
if (!config)
return NULL;
ret = FcStrListCreate (config->configDirs);
FcConfigDestroy (config);
return ret;
}
FcBool
FcConfigAddFontDir (FcConfig *config,
Replace UUID file mechanism with per-directory 'map' attribute [v2] The UUID files would be placed in each font directory to provide the unique cache name, independent of path, for that directory. The UUID files are undesireable for a couple of reasons: 1) They must be placed in the font directories to be useful. This requires modifying the font directories themselves, introducing potential visible timestamp changes when running multiple applications, and makes the cache processing inconsistent between applications with permission to write to the font directories and applications without such permission. 2) The UUID contents were generated randomly, which makes the font cache not reproducible across multiple runs. One proposed fix for 2) is to make the UUID dependent on the font directory path, but once we do that, we can simply use the font directory path itself as the key as the original MD5-based font cache naming mechanism did. The goal of the UUID file mechanism was to fix startup time of flatpaks; as the font path names inside the flatpak did not match the font path names in the base system, the font cache would need to be reconstructed the first time the flatpak was launched. The new mechanism for doing this is to allow each '<dir>' element in the configuration include a 'map' attribute. When looking for a cache file for a particular directory, if the directory name starts with the contents of the <dir> element, that portion of the name will be replaced with the value of the 'map' attribute. Outside of the flatpak, nothing need change -- fontconfig will build cache files using real directory names. Inside the flatpak, the custom fonts.conf file will now include mappings such as this: <dir map="/usr/share/fonts">/run/host/fonts</dir> When scanning the directory /run/host/fonts/ttf, fontconfig will use the name /usr/share/fonts/ttf as the source for building the cache file name. The existing FC_FILE replacement code used for the UUID-based implementation continues to correctly adapt font path names seen by applications. v2: Leave FcDirCacheCreateUUID stub around to avoid removing public API function. Document 'map' attribute of <dir> element in fontconfig-user.sgml Suggested-by: Akira TAGOH <akira@tagoh.org> Signed-off-by: Keith Packard <keithp@keithp.com>
2018-10-30 00:39:05 +01:00
const FcChar8 *d,
const FcChar8 *m,
const FcChar8 *salt)
{
2019-04-02 12:00:17 +02:00
if (FcDebug() & FC_DBG_CACHE)
{
if (m)
{
printf ("%s -> %s%s%s%s\n", d, m, salt ? " (salt: " : "", salt ? (const char *)salt : "", salt ? ")" : "");
2019-04-02 12:00:17 +02:00
}
else if (salt)
{
printf ("%s%s%s%s\n", d, salt ? " (salt: " : "", salt ? (const char *)salt : "", salt ? ")" : "");
2019-04-02 12:00:17 +02:00
}
}
return FcStrSetAddFilenamePairWithSalt (config->fontDirs, d, m, salt);
2002-02-15 00:34:13 +01:00
}
FcBool
FcConfigResetFontDirs (FcConfig *config)
{
2019-04-02 12:00:17 +02:00
if (FcDebug() & FC_DBG_CACHE)
{
printf ("Reset font directories!\n");
}
return FcStrSetDeleteAll (config->fontDirs);
}
FcStrList *
FcConfigGetFontDirs (FcConfig *config)
2002-02-15 00:34:13 +01:00
{
FcStrList *ret;
config = FcConfigReference (config);
2002-02-15 00:34:13 +01:00
if (!config)
return NULL;
ret = FcStrListCreate (config->fontDirs);
FcConfigDestroy (config);
return ret;
2002-02-15 00:34:13 +01:00
}
Replace UUID file mechanism with per-directory 'map' attribute [v2] The UUID files would be placed in each font directory to provide the unique cache name, independent of path, for that directory. The UUID files are undesireable for a couple of reasons: 1) They must be placed in the font directories to be useful. This requires modifying the font directories themselves, introducing potential visible timestamp changes when running multiple applications, and makes the cache processing inconsistent between applications with permission to write to the font directories and applications without such permission. 2) The UUID contents were generated randomly, which makes the font cache not reproducible across multiple runs. One proposed fix for 2) is to make the UUID dependent on the font directory path, but once we do that, we can simply use the font directory path itself as the key as the original MD5-based font cache naming mechanism did. The goal of the UUID file mechanism was to fix startup time of flatpaks; as the font path names inside the flatpak did not match the font path names in the base system, the font cache would need to be reconstructed the first time the flatpak was launched. The new mechanism for doing this is to allow each '<dir>' element in the configuration include a 'map' attribute. When looking for a cache file for a particular directory, if the directory name starts with the contents of the <dir> element, that portion of the name will be replaced with the value of the 'map' attribute. Outside of the flatpak, nothing need change -- fontconfig will build cache files using real directory names. Inside the flatpak, the custom fonts.conf file will now include mappings such as this: <dir map="/usr/share/fonts">/run/host/fonts</dir> When scanning the directory /run/host/fonts/ttf, fontconfig will use the name /usr/share/fonts/ttf as the source for building the cache file name. The existing FC_FILE replacement code used for the UUID-based implementation continues to correctly adapt font path names seen by applications. v2: Leave FcDirCacheCreateUUID stub around to avoid removing public API function. Document 'map' attribute of <dir> element in fontconfig-user.sgml Suggested-by: Akira TAGOH <akira@tagoh.org> Signed-off-by: Keith Packard <keithp@keithp.com>
2018-10-30 00:39:05 +01:00
static FcBool
FcConfigPathStartsWith(const FcChar8 *path,
const FcChar8 *start)
{
int len = strlen((char *) start);
if (strncmp((char *) path, (char *) start, len) != 0)
return FcFalse;
switch (path[len]) {
case '\0':
case FC_DIR_SEPARATOR:
return FcTrue;
default:
return FcFalse;
}
}
FcChar8 *
FcConfigMapFontPath(FcConfig *config,
const FcChar8 *path)
{
FcStrList *list;
FcChar8 *dir;
const FcChar8 *map, *rpath;
FcChar8 *retval;
Replace UUID file mechanism with per-directory 'map' attribute [v2] The UUID files would be placed in each font directory to provide the unique cache name, independent of path, for that directory. The UUID files are undesireable for a couple of reasons: 1) They must be placed in the font directories to be useful. This requires modifying the font directories themselves, introducing potential visible timestamp changes when running multiple applications, and makes the cache processing inconsistent between applications with permission to write to the font directories and applications without such permission. 2) The UUID contents were generated randomly, which makes the font cache not reproducible across multiple runs. One proposed fix for 2) is to make the UUID dependent on the font directory path, but once we do that, we can simply use the font directory path itself as the key as the original MD5-based font cache naming mechanism did. The goal of the UUID file mechanism was to fix startup time of flatpaks; as the font path names inside the flatpak did not match the font path names in the base system, the font cache would need to be reconstructed the first time the flatpak was launched. The new mechanism for doing this is to allow each '<dir>' element in the configuration include a 'map' attribute. When looking for a cache file for a particular directory, if the directory name starts with the contents of the <dir> element, that portion of the name will be replaced with the value of the 'map' attribute. Outside of the flatpak, nothing need change -- fontconfig will build cache files using real directory names. Inside the flatpak, the custom fonts.conf file will now include mappings such as this: <dir map="/usr/share/fonts">/run/host/fonts</dir> When scanning the directory /run/host/fonts/ttf, fontconfig will use the name /usr/share/fonts/ttf as the source for building the cache file name. The existing FC_FILE replacement code used for the UUID-based implementation continues to correctly adapt font path names seen by applications. v2: Leave FcDirCacheCreateUUID stub around to avoid removing public API function. Document 'map' attribute of <dir> element in fontconfig-user.sgml Suggested-by: Akira TAGOH <akira@tagoh.org> Signed-off-by: Keith Packard <keithp@keithp.com>
2018-10-30 00:39:05 +01:00
list = FcConfigGetFontDirs(config);
if (!list)
return 0;
while ((dir = FcStrListNext(list)))
if (FcConfigPathStartsWith(path, dir))
break;
FcStrListDone(list);
if (!dir)
return 0;
map = FcStrTripleSecond(dir);
Replace UUID file mechanism with per-directory 'map' attribute [v2] The UUID files would be placed in each font directory to provide the unique cache name, independent of path, for that directory. The UUID files are undesireable for a couple of reasons: 1) They must be placed in the font directories to be useful. This requires modifying the font directories themselves, introducing potential visible timestamp changes when running multiple applications, and makes the cache processing inconsistent between applications with permission to write to the font directories and applications without such permission. 2) The UUID contents were generated randomly, which makes the font cache not reproducible across multiple runs. One proposed fix for 2) is to make the UUID dependent on the font directory path, but once we do that, we can simply use the font directory path itself as the key as the original MD5-based font cache naming mechanism did. The goal of the UUID file mechanism was to fix startup time of flatpaks; as the font path names inside the flatpak did not match the font path names in the base system, the font cache would need to be reconstructed the first time the flatpak was launched. The new mechanism for doing this is to allow each '<dir>' element in the configuration include a 'map' attribute. When looking for a cache file for a particular directory, if the directory name starts with the contents of the <dir> element, that portion of the name will be replaced with the value of the 'map' attribute. Outside of the flatpak, nothing need change -- fontconfig will build cache files using real directory names. Inside the flatpak, the custom fonts.conf file will now include mappings such as this: <dir map="/usr/share/fonts">/run/host/fonts</dir> When scanning the directory /run/host/fonts/ttf, fontconfig will use the name /usr/share/fonts/ttf as the source for building the cache file name. The existing FC_FILE replacement code used for the UUID-based implementation continues to correctly adapt font path names seen by applications. v2: Leave FcDirCacheCreateUUID stub around to avoid removing public API function. Document 'map' attribute of <dir> element in fontconfig-user.sgml Suggested-by: Akira TAGOH <akira@tagoh.org> Signed-off-by: Keith Packard <keithp@keithp.com>
2018-10-30 00:39:05 +01:00
if (!map)
return 0;
rpath = path + strlen ((char *) dir);
while (*rpath == '/')
rpath++;
retval = FcStrBuildFilename(map, rpath, NULL);
if (retval)
{
size_t len = strlen ((const char *) retval);
while (len > 0 && retval[len-1] == '/')
len--;
/* trim the last slash */
retval[len] = 0;
}
return retval;
Replace UUID file mechanism with per-directory 'map' attribute [v2] The UUID files would be placed in each font directory to provide the unique cache name, independent of path, for that directory. The UUID files are undesireable for a couple of reasons: 1) They must be placed in the font directories to be useful. This requires modifying the font directories themselves, introducing potential visible timestamp changes when running multiple applications, and makes the cache processing inconsistent between applications with permission to write to the font directories and applications without such permission. 2) The UUID contents were generated randomly, which makes the font cache not reproducible across multiple runs. One proposed fix for 2) is to make the UUID dependent on the font directory path, but once we do that, we can simply use the font directory path itself as the key as the original MD5-based font cache naming mechanism did. The goal of the UUID file mechanism was to fix startup time of flatpaks; as the font path names inside the flatpak did not match the font path names in the base system, the font cache would need to be reconstructed the first time the flatpak was launched. The new mechanism for doing this is to allow each '<dir>' element in the configuration include a 'map' attribute. When looking for a cache file for a particular directory, if the directory name starts with the contents of the <dir> element, that portion of the name will be replaced with the value of the 'map' attribute. Outside of the flatpak, nothing need change -- fontconfig will build cache files using real directory names. Inside the flatpak, the custom fonts.conf file will now include mappings such as this: <dir map="/usr/share/fonts">/run/host/fonts</dir> When scanning the directory /run/host/fonts/ttf, fontconfig will use the name /usr/share/fonts/ttf as the source for building the cache file name. The existing FC_FILE replacement code used for the UUID-based implementation continues to correctly adapt font path names seen by applications. v2: Leave FcDirCacheCreateUUID stub around to avoid removing public API function. Document 'map' attribute of <dir> element in fontconfig-user.sgml Suggested-by: Akira TAGOH <akira@tagoh.org> Signed-off-by: Keith Packard <keithp@keithp.com>
2018-10-30 00:39:05 +01:00
}
const FcChar8 *
FcConfigMapSalt (FcConfig *config,
const FcChar8 *path)
{
FcStrList *list;
FcChar8 *dir;
list = FcConfigGetFontDirs (config);
if (!list)
return NULL;
while ((dir = FcStrListNext (list)))
if (FcConfigPathStartsWith (path, dir))
break;
FcStrListDone (list);
if (!dir)
return NULL;
return FcStrTripleThird (dir);
}
FcBool
FcConfigAddCacheDir (FcConfig *config,
const FcChar8 *d)
{
return FcStrSetAddFilename (config->cacheDirs, d);
}
FcStrList *
FcConfigGetCacheDirs (FcConfig *config)
{
FcStrList *ret;
config = FcConfigReference (config);
if (!config)
return NULL;
ret = FcStrListCreate (config->cacheDirs);
FcConfigDestroy (config);
return ret;
}
2010-04-12 18:18:50 +02:00
2002-02-15 00:34:13 +01:00
FcBool
FcConfigAddConfigFile (FcConfig *config,
const FcChar8 *f)
2002-02-15 00:34:13 +01:00
{
FcBool ret;
FcChar8 *file = FcConfigGetFilename (config, f);
2010-04-12 18:18:50 +02:00
2002-02-15 00:34:13 +01:00
if (!file)
return FcFalse;
2010-04-12 18:18:50 +02:00
ret = FcStrSetAdd (config->configFiles, file);
FcStrFree (file);
return ret;
2002-02-15 00:34:13 +01:00
}
FcStrList *
2002-02-15 00:34:13 +01:00
FcConfigGetConfigFiles (FcConfig *config)
{
FcStrList *ret;
config = FcConfigReference (config);
2002-02-15 00:34:13 +01:00
if (!config)
return NULL;
ret = FcStrListCreate (config->configFiles);
FcConfigDestroy (config);
return ret;
2002-02-15 00:34:13 +01:00
}
FcChar8 *
2012-12-30 04:32:56 +01:00
FcConfigGetCache (FcConfig *config FC_UNUSED)
2002-02-15 00:34:13 +01:00
{
return NULL;
2002-02-15 00:34:13 +01:00
}
FcFontSet *
FcConfigGetFonts (FcConfig *config,
FcSetName set)
{
if (!config)
{
config = FcConfigGetCurrent ();
if (!config)
return 0;
}
return config->fonts[set];
}
void
FcConfigSetFonts (FcConfig *config,
FcFontSet *fonts,
FcSetName set)
{
if (config->fonts[set])
FcFontSetDestroy (config->fonts[set]);
config->fonts[set] = fonts;
}
FcBlanks *
FcBlanksCreate (void)
{
/* Deprecated. */
return NULL;
}
void
2017-08-04 17:36:12 +02:00
FcBlanksDestroy (FcBlanks *b FC_UNUSED)
{
/* Deprecated. */
}
FcBool
2017-08-04 17:36:12 +02:00
FcBlanksAdd (FcBlanks *b FC_UNUSED, FcChar32 ucs4 FC_UNUSED)
{
/* Deprecated. */
return FcFalse;
}
FcBool
2017-08-04 17:36:12 +02:00
FcBlanksIsMember (FcBlanks *b FC_UNUSED, FcChar32 ucs4 FC_UNUSED)
{
/* Deprecated. */
return FcFalse;
}
2002-02-15 00:34:13 +01:00
FcBlanks *
2017-08-04 17:36:12 +02:00
FcConfigGetBlanks (FcConfig *config FC_UNUSED)
2002-02-15 00:34:13 +01:00
{
/* Deprecated. */
return NULL;
2002-02-15 00:34:13 +01:00
}
FcBool
2017-08-04 17:36:12 +02:00
FcConfigAddBlank (FcConfig *config FC_UNUSED,
FcChar32 blank FC_UNUSED)
2002-02-15 00:34:13 +01:00
{
/* Deprecated. */
return FcFalse;
2002-02-15 00:34:13 +01:00
}
int
FcConfigGetRescanInterval (FcConfig *config)
{
int ret;
config = FcConfigReference (config);
if (!config)
return 0;
ret = config->rescanInterval;
FcConfigDestroy (config);
return ret;
}
FcBool
FcConfigSetRescanInterval (FcConfig *config, int rescanInterval)
{
config = FcConfigReference (config);
if (!config)
return FcFalse;
config->rescanInterval = rescanInterval;
FcConfigDestroy (config);
return FcTrue;
}
/*
* A couple of typos escaped into the library
*/
int
FcConfigGetRescanInverval (FcConfig *config)
{
return FcConfigGetRescanInterval (config);
}
FcBool
FcConfigSetRescanInverval (FcConfig *config, int rescanInterval)
{
return FcConfigSetRescanInterval (config, rescanInterval);
}
2002-02-15 00:34:13 +01:00
FcBool
FcConfigAddRule (FcConfig *config,
FcRule *rule,
2002-02-15 00:34:13 +01:00
FcMatchKind kind)
{
/* deprecated */
return FcFalse;
2002-02-15 00:34:13 +01:00
}
static FcValue
FcConfigPromote (FcValue v, FcValue u, FcValuePromotionBuffer *buf)
2002-02-15 00:34:13 +01:00
{
switch (v.type)
2002-02-15 00:34:13 +01:00
{
case FcTypeInteger:
v.type = FcTypeDouble;
v.u.d = (double) v.u.i;
/* Fallthrough */
case FcTypeDouble:
if (u.type == FcTypeRange && buf)
{
v.u.r = FcRangePromote (v.u.d, buf);
v.type = FcTypeRange;
}
break;
case FcTypeVoid:
if (u.type == FcTypeMatrix)
{
v.u.m = &FcIdentityMatrix;
v.type = FcTypeMatrix;
}
else if (u.type == FcTypeLangSet && buf)
{
v.u.l = FcLangSetPromote (NULL, buf);
v.type = FcTypeLangSet;
}
else if (u.type == FcTypeCharSet && buf)
{
v.u.c = FcCharSetPromote (buf);
v.type = FcTypeCharSet;
}
break;
case FcTypeString:
if (u.type == FcTypeLangSet && buf)
{
v.u.l = FcLangSetPromote (v.u.s, buf);
v.type = FcTypeLangSet;
}
break;
default:
break;
}
2002-02-15 00:34:13 +01:00
return v;
}
FcBool
FcConfigCompareValue (const FcValue *left_o,
unsigned int op_,
const FcValue *right_o)
2002-02-15 00:34:13 +01:00
{
FcValue left;
FcValue right;
FcBool ret = FcFalse;
FcOp op = FC_OP_GET_OP (op_);
int flags = FC_OP_GET_FLAGS (op_);
FcValuePromotionBuffer buf1, buf2;
2010-04-12 18:18:50 +02:00
if (left_o->type != right_o->type)
2002-02-15 00:34:13 +01:00
{
left = FcValueCanonicalize(left_o);
right = FcValueCanonicalize(right_o);
left = FcConfigPromote (left, right, &buf1);
right = FcConfigPromote (right, left, &buf2);
left_o = &left;
right_o = &right;
if (left_o->type != right_o->type)
{
if (op == FcOpNotEqual || op == FcOpNotContains)
ret = FcTrue;
return ret;
}
2002-02-15 00:34:13 +01:00
}
switch (left_o->type) {
case FcTypeUnknown:
break; /* No way to guess how to compare for this object */
case FcTypeInteger: {
int l = left_o->u.i;
int r = right_o->u.i;
switch ((int) op) {
case FcOpEqual:
case FcOpContains:
case FcOpListing:
ret = l == r;
break;
case FcOpNotEqual:
case FcOpNotContains:
ret = l != r;
break;
case FcOpLess:
ret = l < r;
break;
case FcOpLessEqual:
ret = l <= r;
break;
case FcOpMore:
ret = l > r;
break;
case FcOpMoreEqual:
ret = l >= r;
break;
default:
break;
}
break;
}
case FcTypeDouble: {
double l = left_o->u.d;
double r = right_o->u.d;
switch ((int) op) {
case FcOpEqual:
case FcOpContains:
case FcOpListing:
ret = l == r;
break;
case FcOpNotEqual:
case FcOpNotContains:
ret = l != r;
break;
case FcOpLess:
ret = l < r;
break;
case FcOpLessEqual:
ret = l <= r;
break;
case FcOpMore:
ret = l > r;
break;
case FcOpMoreEqual:
ret = l >= r;
break;
default:
break;
}
break;
}
case FcTypeBool: {
FcBool l = left_o->u.b;
FcBool r = right_o->u.b;
switch ((int) op) {
case FcOpEqual:
ret = l == r;
break;
case FcOpContains:
case FcOpListing:
ret = l == r || l >= FcDontCare;
break;
case FcOpNotEqual:
ret = l != r;
break;
case FcOpNotContains:
ret = !(l == r || l >= FcDontCare);
break;
case FcOpLess:
ret = l != r && r >= FcDontCare;
break;
case FcOpLessEqual:
ret = l == r || r >= FcDontCare;
break;
case FcOpMore:
ret = l != r && l >= FcDontCare;
break;
case FcOpMoreEqual:
ret = l == r || l >= FcDontCare;
break;
default:
break;
}
break;
}
case FcTypeString: {
const FcChar8 *l = FcValueString (left_o);
const FcChar8 *r = FcValueString (right_o);
switch ((int) op) {
case FcOpEqual:
case FcOpListing:
if (flags & FcOpFlagIgnoreBlanks)
ret = FcStrCmpIgnoreBlanksAndCase (l, r) == 0;
else
ret = FcStrCmpIgnoreCase (l, r) == 0;
break;
case FcOpContains:
ret = FcStrStrIgnoreCase (l, r) != 0;
break;
case FcOpNotEqual:
if (flags & FcOpFlagIgnoreBlanks)
ret = FcStrCmpIgnoreBlanksAndCase (l, r) != 0;
else
ret = FcStrCmpIgnoreCase (l, r) != 0;
break;
case FcOpNotContains:
ret = FcStrStrIgnoreCase (l, r) == 0;
break;
default:
break;
}
break;
}
case FcTypeMatrix: {
switch ((int) op) {
case FcOpEqual:
case FcOpContains:
case FcOpListing:
ret = FcMatrixEqual (left_o->u.m, right_o->u.m);
break;
case FcOpNotEqual:
case FcOpNotContains:
ret = !FcMatrixEqual (left_o->u.m, right_o->u.m);
break;
default:
break;
}
break;
}
case FcTypeCharSet: {
const FcCharSet *l = FcValueCharSet (left_o);
const FcCharSet *r = FcValueCharSet (right_o);
switch ((int) op) {
case FcOpContains:
case FcOpListing:
/* left contains right if right is a subset of left */
ret = FcCharSetIsSubset (r, l);
break;
case FcOpNotContains:
/* left contains right if right is a subset of left */
ret = !FcCharSetIsSubset (r, l);
break;
case FcOpEqual:
ret = FcCharSetEqual (l, r);
break;
case FcOpNotEqual:
ret = !FcCharSetEqual (l, r);
break;
default:
break;
}
break;
}
case FcTypeLangSet: {
const FcLangSet *l = FcValueLangSet (left_o);
const FcLangSet *r = FcValueLangSet (right_o);
switch ((int) op) {
case FcOpContains:
case FcOpListing:
ret = FcLangSetContains (l, r);
break;
case FcOpNotContains:
ret = !FcLangSetContains (l, r);
break;
case FcOpEqual:
ret = FcLangSetEqual (l, r);
break;
case FcOpNotEqual:
ret = !FcLangSetEqual (l, r);
break;
default:
break;
}
break;
}
case FcTypeVoid:
switch ((int) op) {
case FcOpEqual:
case FcOpContains:
case FcOpListing:
ret = FcTrue;
break;
default:
break;
}
break;
case FcTypeFTFace:
switch ((int) op) {
case FcOpEqual:
case FcOpContains:
case FcOpListing:
ret = left_o->u.f == right_o->u.f;
break;
case FcOpNotEqual:
case FcOpNotContains:
ret = left_o->u.f != right_o->u.f;
break;
default:
break;
}
break;
case FcTypeRange: {
const FcRange *l = FcValueRange (left_o);
const FcRange *r = FcValueRange (right_o);
ret = FcRangeCompare (op, l, r);
break;
2002-02-15 00:34:13 +01:00
}
}
2002-02-15 00:34:13 +01:00
return ret;
}
#define _FcDoubleFloor(d) ((int) (d))
#define _FcDoubleCeil(d) ((double) (int) (d) == (d) ? (int) (d) : (int) ((d) + 1))
#define FcDoubleFloor(d) ((d) >= 0 ? _FcDoubleFloor(d) : -_FcDoubleCeil(-(d)))
#define FcDoubleCeil(d) ((d) >= 0 ? _FcDoubleCeil(d) : -_FcDoubleFloor(-(d)))
#define FcDoubleRound(d) FcDoubleFloor ((d) + 0.5)
#define FcDoubleTrunc(d) ((d) >= 0 ? _FcDoubleFloor (d) : -_FcDoubleFloor (-(d)))
2002-02-15 00:34:13 +01:00
static FcValue
FcConfigEvaluate (FcPattern *p, FcPattern *p_pat, FcMatchKind kind, FcExpr *e)
2002-02-15 00:34:13 +01:00
{
FcValue v, vl, vr, vle, vre;
2002-02-15 00:34:13 +01:00
FcMatrix *m;
FcChar8 *str;
FcOp op = FC_OP_GET_OP (e->op);
FcValuePromotionBuffer buf1, buf2;
2010-04-12 18:18:50 +02:00
2012-12-30 04:11:09 +01:00
switch ((int) op) {
2002-02-15 00:34:13 +01:00
case FcOpInteger:
v.type = FcTypeInteger;
v.u.i = e->u.ival;
break;
case FcOpDouble:
v.type = FcTypeDouble;
v.u.d = e->u.dval;
break;
case FcOpString:
v.type = FcTypeString;
2009-06-05 22:49:07 +02:00
v.u.s = e->u.sval;
v = FcValueSave (v);
2002-02-15 00:34:13 +01:00
break;
case FcOpMatrix:
{
FcMatrix m;
FcValue xx, xy, yx, yy;
2012-12-30 04:11:09 +01:00
v.type = FcTypeMatrix;
xx = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->xx), v, NULL);
xy = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->xy), v, NULL);
yx = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->yx), v, NULL);
yy = FcConfigPromote (FcConfigEvaluate (p, p_pat, kind, e->u.mexpr->yy), v, NULL);
if (xx.type == FcTypeDouble && xy.type == FcTypeDouble &&
yx.type == FcTypeDouble && yy.type == FcTypeDouble)
{
m.xx = xx.u.d;
m.xy = xy.u.d;
m.yx = yx.u.d;
m.yy = yy.u.d;
v.u.m = &m;
}
else
v.type = FcTypeVoid;
v = FcValueSave (v);
}
2002-02-15 00:34:13 +01:00
break;
case FcOpCharSet:
v.type = FcTypeCharSet;
v.u.c = e->u.cval;
2002-02-15 00:34:13 +01:00
v = FcValueSave (v);
break;
case FcOpLangSet:
v.type = FcTypeLangSet;
v.u.l = e->u.lval;
v = FcValueSave (v);
break;
case FcOpRange:
v.type = FcTypeRange;
v.u.r = e->u.rval;
v = FcValueSave (v);
break;
2002-02-15 00:34:13 +01:00
case FcOpBool:
v.type = FcTypeBool;
v.u.b = e->u.bval;
break;
case FcOpField:
if (kind == FcMatchFont && e->u.name.kind == FcMatchPattern)
{
if (FcResultMatch != FcPatternObjectGet (p_pat, e->u.name.object, 0, &v))
v.type = FcTypeVoid;
}
else if (kind == FcMatchPattern && e->u.name.kind == FcMatchFont)
{
fprintf (stderr,
"Fontconfig warning: <name> tag has target=\"font\" in a <match target=\"pattern\">.\n");
2002-02-15 00:34:13 +01:00
v.type = FcTypeVoid;
}
else
{
if (FcResultMatch != FcPatternObjectGet (p, e->u.name.object, 0, &v))
v.type = FcTypeVoid;
}
v = FcValueSave (v);
2002-02-15 00:34:13 +01:00
break;
case FcOpConst:
if (FcNameConstant (e->u.constant, &v.u.i))
v.type = FcTypeInteger;
else
v.type = FcTypeVoid;
break;
case FcOpQuest:
vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
2002-02-15 00:34:13 +01:00
if (vl.type == FcTypeBool)
{
if (vl.u.b)
v = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right->u.tree.left);
2002-02-15 00:34:13 +01:00
else
v = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right->u.tree.right);
2002-02-15 00:34:13 +01:00
}
else
v.type = FcTypeVoid;
FcValueDestroy (vl);
break;
case FcOpEqual:
2002-02-15 00:34:13 +01:00
case FcOpNotEqual:
case FcOpLess:
case FcOpLessEqual:
case FcOpMore:
case FcOpMoreEqual:
case FcOpContains:
case FcOpNotContains:
case FcOpListing:
vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
vr = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right);
v.type = FcTypeBool;
v.u.b = FcConfigCompareValue (&vl, e->op, &vr);
FcValueDestroy (vl);
FcValueDestroy (vr);
break;
case FcOpOr:
case FcOpAnd:
2002-02-15 00:34:13 +01:00
case FcOpPlus:
case FcOpMinus:
case FcOpTimes:
case FcOpDivide:
vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
vr = FcConfigEvaluate (p, p_pat, kind, e->u.tree.right);
vle = FcConfigPromote (vl, vr, &buf1);
vre = FcConfigPromote (vr, vle, &buf2);
if (vle.type == vre.type)
2002-02-15 00:34:13 +01:00
{
switch ((int) vle.type) {
2002-02-15 00:34:13 +01:00
case FcTypeDouble:
2012-12-30 04:11:09 +01:00
switch ((int) op) {
case FcOpPlus:
2002-02-15 00:34:13 +01:00
v.type = FcTypeDouble;
v.u.d = vle.u.d + vre.u.d;
2002-02-15 00:34:13 +01:00
break;
case FcOpMinus:
v.type = FcTypeDouble;
v.u.d = vle.u.d - vre.u.d;
2002-02-15 00:34:13 +01:00
break;
case FcOpTimes:
v.type = FcTypeDouble;
v.u.d = vle.u.d * vre.u.d;
2002-02-15 00:34:13 +01:00
break;
case FcOpDivide:
v.type = FcTypeDouble;
v.u.d = vle.u.d / vre.u.d;
2002-02-15 00:34:13 +01:00
break;
default:
2010-04-12 18:18:50 +02:00
v.type = FcTypeVoid;
2002-02-15 00:34:13 +01:00
break;
}
if (v.type == FcTypeDouble &&
v.u.d == (double) (int) v.u.d)
{
v.type = FcTypeInteger;
v.u.i = (int) v.u.d;
}
break;
case FcTypeBool:
2012-12-30 04:11:09 +01:00
switch ((int) op) {
2002-02-15 00:34:13 +01:00
case FcOpOr:
v.type = FcTypeBool;
v.u.b = vle.u.b || vre.u.b;
2002-02-15 00:34:13 +01:00
break;
case FcOpAnd:
v.type = FcTypeBool;
v.u.b = vle.u.b && vre.u.b;
2002-02-15 00:34:13 +01:00
break;
default:
2010-04-12 18:18:50 +02:00
v.type = FcTypeVoid;
2002-02-15 00:34:13 +01:00
break;
}
break;
case FcTypeString:
2012-12-30 04:11:09 +01:00
switch ((int) op) {
2002-02-15 00:34:13 +01:00
case FcOpPlus:
v.type = FcTypeString;
str = FcStrPlus (vle.u.s, vre.u.s);
2013-01-02 09:06:15 +01:00
v.u.s = FcStrdup (str);
FcStrFree (str);
if (!v.u.s)
2002-02-15 00:34:13 +01:00
v.type = FcTypeVoid;
break;
default:
v.type = FcTypeVoid;
break;
}
break;
2002-02-15 00:34:13 +01:00
case FcTypeMatrix:
2012-12-30 04:11:09 +01:00
switch ((int) op) {
2002-02-15 00:34:13 +01:00
case FcOpTimes:
v.type = FcTypeMatrix;
m = malloc (sizeof (FcMatrix));
if (m)
{
FcMatrixMultiply (m, vle.u.m, vre.u.m);
v.u.m = m;
2002-02-15 00:34:13 +01:00
}
else
{
v.type = FcTypeVoid;
}
break;
default:
v.type = FcTypeVoid;
break;
}
break;
case FcTypeCharSet:
2012-12-30 04:11:09 +01:00
switch ((int) op) {
case FcOpPlus:
v.type = FcTypeCharSet;
v.u.c = FcCharSetUnion (vle.u.c, vre.u.c);
if (!v.u.c)
v.type = FcTypeVoid;
break;
case FcOpMinus:
v.type = FcTypeCharSet;
v.u.c = FcCharSetSubtract (vle.u.c, vre.u.c);
if (!v.u.c)
v.type = FcTypeVoid;
break;
default:
v.type = FcTypeVoid;
break;
}
break;
case FcTypeLangSet:
2012-12-30 04:11:09 +01:00
switch ((int) op) {
case FcOpPlus:
v.type = FcTypeLangSet;
v.u.l = FcLangSetUnion (vle.u.l, vre.u.l);
if (!v.u.l)
v.type = FcTypeVoid;
break;
case FcOpMinus:
v.type = FcTypeLangSet;
v.u.l = FcLangSetSubtract (vle.u.l, vre.u.l);
if (!v.u.l)
v.type = FcTypeVoid;
break;
default:
v.type = FcTypeVoid;
break;
}
break;
2002-02-15 00:34:13 +01:00
default:
v.type = FcTypeVoid;
break;
}
}
else
v.type = FcTypeVoid;
FcValueDestroy (vl);
FcValueDestroy (vr);
break;
case FcOpNot:
vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
2012-12-30 04:11:09 +01:00
switch ((int) vl.type) {
2002-02-15 00:34:13 +01:00
case FcTypeBool:
v.type = FcTypeBool;
v.u.b = !vl.u.b;
break;
default:
v.type = FcTypeVoid;
break;
}
FcValueDestroy (vl);
break;
case FcOpFloor:
vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
2012-12-30 04:11:09 +01:00
switch ((int) vl.type) {
case FcTypeInteger:
v = vl;
break;
case FcTypeDouble:
v.type = FcTypeInteger;
v.u.i = FcDoubleFloor (vl.u.d);
break;
default:
v.type = FcTypeVoid;
break;
}
FcValueDestroy (vl);
break;
case FcOpCeil:
vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
2012-12-30 04:11:09 +01:00
switch ((int) vl.type) {
case FcTypeInteger:
v = vl;
break;
case FcTypeDouble:
v.type = FcTypeInteger;
v.u.i = FcDoubleCeil (vl.u.d);
break;
default:
v.type = FcTypeVoid;
break;
}
FcValueDestroy (vl);
break;
case FcOpRound:
vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
2012-12-30 04:11:09 +01:00
switch ((int) vl.type) {
case FcTypeInteger:
v = vl;
break;
case FcTypeDouble:
v.type = FcTypeInteger;
v.u.i = FcDoubleRound (vl.u.d);
break;
default:
v.type = FcTypeVoid;
break;
}
FcValueDestroy (vl);
break;
case FcOpTrunc:
vl = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
2012-12-30 04:11:09 +01:00
switch ((int) vl.type) {
case FcTypeInteger:
v = vl;
break;
case FcTypeDouble:
v.type = FcTypeInteger;
v.u.i = FcDoubleTrunc (vl.u.d);
break;
default:
v.type = FcTypeVoid;
break;
}
FcValueDestroy (vl);
break;
2002-02-15 00:34:13 +01:00
default:
v.type = FcTypeVoid;
break;
}
return v;
}
/* The bulk of the time in FcConfigSubstitute is spent walking
* lists of family names. We speed this up with a hash table.
* Since we need to take the ignore-blanks option into account,
* we use two separate hash tables.
*/
typedef struct
{
int count;
} FamilyTableEntry;
typedef struct
{
FcHashTable *family_blank_hash;
FcHashTable *family_hash;
} FamilyTable;
static FcBool
FamilyTableLookup (FamilyTable *table,
FcOp _op,
const FcChar8 *s)
{
FamilyTableEntry *fe;
int flags = FC_OP_GET_FLAGS (_op);
FcHashTable *hash;
if (flags & FcOpFlagIgnoreBlanks)
hash = table->family_blank_hash;
else
hash = table->family_hash;
return FcHashTableFind (hash, (const void *)s, (void **)&fe);
}
static void
FamilyTableAdd (FamilyTable *table,
FcValueListPtr values)
{
FcValueListPtr ll;
for (ll = values; ll; ll = FcValueListNext (ll))
{
const FcChar8 *s = FcValueString (&ll->value);
FamilyTableEntry *fe;
if (!FcHashTableFind (table->family_hash, (const void *)s, (void **)&fe))
{
fe = malloc (sizeof (FamilyTableEntry));
fe->count = 0;
FcHashTableAdd (table->family_hash, (void *)s, fe);
}
fe->count++;
if (!FcHashTableFind (table->family_blank_hash, (const void *)s, (void **)&fe))
{
fe = malloc (sizeof (FamilyTableEntry));
fe->count = 0;
FcHashTableAdd (table->family_blank_hash, (void *)s, fe);
}
fe->count++;
}
}
static void
FamilyTableDel (FamilyTable *table,
const FcChar8 *s)
{
FamilyTableEntry *fe;
if (FcHashTableFind (table->family_hash, (void *)s, (void **)&fe))
{
fe->count--;
if (fe->count == 0)
FcHashTableRemove (table->family_hash, (void *)s);
}
if (FcHashTableFind (table->family_blank_hash, (void *)s, (void **)&fe))
{
fe->count--;
if (fe->count == 0)
FcHashTableRemove (table->family_blank_hash, (void *)s);
}
}
Fix a problem in FcConfigSubstitute We were using the family names from the pattern without copying, and this was leading to a valgrind warning: ==53167== Invalid read of size 1 ==53167== at 0x58B0238: FcStrCaseWalkerNextNonBlank (fcstr.c:198) ==53167== by 0x58B0238: FcStrCaseWalkerNextNonBlank (fcstr.c:186) ==53167== by 0x58B02C7: FcStrCmpIgnoreBlanksAndCase (fcstr.c:281) ==53167== by 0x58A4D44: FcHashTableFind (fchash.c:109) ==53167== by 0x5895E76: FamilyTableAdd (fccfg.c:1634) ==53167== by 0x589646A: FcConfigAdd.isra.0 (fccfg.c:1823) ==53167== by 0x58988CF: IA__FcConfigSubstituteWithPat.part.0 (fccfg.c:2228) ==53167== by 0x55F4F1A: pango_cairo_fc_font_map_fontset_key_substitute (pangocairo-fcfontmap.c:106) ==53167== by 0x5B88AF6: pango_fc_default_substitute (pangofc-fontmap.c:1795) ==53167== by 0x5B88D15: pango_fc_font_map_get_patterns (pangofc-fontmap.c:1850) ==53167== by 0x5B88FC7: pango_fc_font_map_load_fontset (pangofc-fontmap.c:1952) ==53167== by 0x5623627: pango_font_map_load_fontset (pango-fontmap.c:161) ==53167== by 0x5621743: pango_context_get_metrics (pango-context.c:1782) ==53167== Address 0x150d3450 is 0 bytes inside a block of size 10 free'd ==53167== at 0x483B9F5: free (vg_replace_malloc.c:538) ==53167== by 0x58ABE70: FcValueListDestroy (fcpat.c:147) ==53167== by 0x5898A08: IA__FcConfigSubstituteWithPat.part.0 (fccfg.c:2203) ==53167== by 0x55F4F1A: pango_cairo_fc_font_map_fontset_key_substitute (pangocairo-fcfontmap.c:106) ==53167== by 0x5B88AF6: pango_fc_default_substitute (pangofc-fontmap.c:1795) ==53167== by 0x5B88D15: pango_fc_font_map_get_patterns (pangofc-fontmap.c:1850) ==53167== by 0x5B88FC7: pango_fc_font_map_load_fontset (pangofc-fontmap.c:1952) ==53167== by 0x5623627: pango_font_map_load_fontset (pango-fontmap.c:161) ==53167== by 0x5621743: pango_context_get_metrics (pango-context.c:1782) Use copies of the strings as keys in the hash table to avoid this.
2020-08-30 18:03:04 +02:00
static FcBool
copy_string (const void *src, void **dest)
{
*dest = strdup ((char *)src);
return FcTrue;
}
static void
FamilyTableInit (FamilyTable *table,
FcPattern *p)
{
FcPatternElt *e;
table->family_blank_hash = FcHashTableCreate ((FcHashFunc)FcStrHashIgnoreBlanksAndCase,
(FcCompareFunc)FcStrCmpIgnoreBlanksAndCase,
Fix a problem in FcConfigSubstitute We were using the family names from the pattern without copying, and this was leading to a valgrind warning: ==53167== Invalid read of size 1 ==53167== at 0x58B0238: FcStrCaseWalkerNextNonBlank (fcstr.c:198) ==53167== by 0x58B0238: FcStrCaseWalkerNextNonBlank (fcstr.c:186) ==53167== by 0x58B02C7: FcStrCmpIgnoreBlanksAndCase (fcstr.c:281) ==53167== by 0x58A4D44: FcHashTableFind (fchash.c:109) ==53167== by 0x5895E76: FamilyTableAdd (fccfg.c:1634) ==53167== by 0x589646A: FcConfigAdd.isra.0 (fccfg.c:1823) ==53167== by 0x58988CF: IA__FcConfigSubstituteWithPat.part.0 (fccfg.c:2228) ==53167== by 0x55F4F1A: pango_cairo_fc_font_map_fontset_key_substitute (pangocairo-fcfontmap.c:106) ==53167== by 0x5B88AF6: pango_fc_default_substitute (pangofc-fontmap.c:1795) ==53167== by 0x5B88D15: pango_fc_font_map_get_patterns (pangofc-fontmap.c:1850) ==53167== by 0x5B88FC7: pango_fc_font_map_load_fontset (pangofc-fontmap.c:1952) ==53167== by 0x5623627: pango_font_map_load_fontset (pango-fontmap.c:161) ==53167== by 0x5621743: pango_context_get_metrics (pango-context.c:1782) ==53167== Address 0x150d3450 is 0 bytes inside a block of size 10 free'd ==53167== at 0x483B9F5: free (vg_replace_malloc.c:538) ==53167== by 0x58ABE70: FcValueListDestroy (fcpat.c:147) ==53167== by 0x5898A08: IA__FcConfigSubstituteWithPat.part.0 (fccfg.c:2203) ==53167== by 0x55F4F1A: pango_cairo_fc_font_map_fontset_key_substitute (pangocairo-fcfontmap.c:106) ==53167== by 0x5B88AF6: pango_fc_default_substitute (pangofc-fontmap.c:1795) ==53167== by 0x5B88D15: pango_fc_font_map_get_patterns (pangofc-fontmap.c:1850) ==53167== by 0x5B88FC7: pango_fc_font_map_load_fontset (pangofc-fontmap.c:1952) ==53167== by 0x5623627: pango_font_map_load_fontset (pango-fontmap.c:161) ==53167== by 0x5621743: pango_context_get_metrics (pango-context.c:1782) Use copies of the strings as keys in the hash table to avoid this.
2020-08-30 18:03:04 +02:00
(FcCopyFunc)copy_string,
NULL,
Fix a problem in FcConfigSubstitute We were using the family names from the pattern without copying, and this was leading to a valgrind warning: ==53167== Invalid read of size 1 ==53167== at 0x58B0238: FcStrCaseWalkerNextNonBlank (fcstr.c:198) ==53167== by 0x58B0238: FcStrCaseWalkerNextNonBlank (fcstr.c:186) ==53167== by 0x58B02C7: FcStrCmpIgnoreBlanksAndCase (fcstr.c:281) ==53167== by 0x58A4D44: FcHashTableFind (fchash.c:109) ==53167== by 0x5895E76: FamilyTableAdd (fccfg.c:1634) ==53167== by 0x589646A: FcConfigAdd.isra.0 (fccfg.c:1823) ==53167== by 0x58988CF: IA__FcConfigSubstituteWithPat.part.0 (fccfg.c:2228) ==53167== by 0x55F4F1A: pango_cairo_fc_font_map_fontset_key_substitute (pangocairo-fcfontmap.c:106) ==53167== by 0x5B88AF6: pango_fc_default_substitute (pangofc-fontmap.c:1795) ==53167== by 0x5B88D15: pango_fc_font_map_get_patterns (pangofc-fontmap.c:1850) ==53167== by 0x5B88FC7: pango_fc_font_map_load_fontset (pangofc-fontmap.c:1952) ==53167== by 0x5623627: pango_font_map_load_fontset (pango-fontmap.c:161) ==53167== by 0x5621743: pango_context_get_metrics (pango-context.c:1782) ==53167== Address 0x150d3450 is 0 bytes inside a block of size 10 free'd ==53167== at 0x483B9F5: free (vg_replace_malloc.c:538) ==53167== by 0x58ABE70: FcValueListDestroy (fcpat.c:147) ==53167== by 0x5898A08: IA__FcConfigSubstituteWithPat.part.0 (fccfg.c:2203) ==53167== by 0x55F4F1A: pango_cairo_fc_font_map_fontset_key_substitute (pangocairo-fcfontmap.c:106) ==53167== by 0x5B88AF6: pango_fc_default_substitute (pangofc-fontmap.c:1795) ==53167== by 0x5B88D15: pango_fc_font_map_get_patterns (pangofc-fontmap.c:1850) ==53167== by 0x5B88FC7: pango_fc_font_map_load_fontset (pangofc-fontmap.c:1952) ==53167== by 0x5623627: pango_font_map_load_fontset (pango-fontmap.c:161) ==53167== by 0x5621743: pango_context_get_metrics (pango-context.c:1782) Use copies of the strings as keys in the hash table to avoid this.
2020-08-30 18:03:04 +02:00
free,
free);
table->family_hash = FcHashTableCreate ((FcHashFunc)FcStrHashIgnoreCase,
(FcCompareFunc)FcStrCmpIgnoreCase,
Fix a problem in FcConfigSubstitute We were using the family names from the pattern without copying, and this was leading to a valgrind warning: ==53167== Invalid read of size 1 ==53167== at 0x58B0238: FcStrCaseWalkerNextNonBlank (fcstr.c:198) ==53167== by 0x58B0238: FcStrCaseWalkerNextNonBlank (fcstr.c:186) ==53167== by 0x58B02C7: FcStrCmpIgnoreBlanksAndCase (fcstr.c:281) ==53167== by 0x58A4D44: FcHashTableFind (fchash.c:109) ==53167== by 0x5895E76: FamilyTableAdd (fccfg.c:1634) ==53167== by 0x589646A: FcConfigAdd.isra.0 (fccfg.c:1823) ==53167== by 0x58988CF: IA__FcConfigSubstituteWithPat.part.0 (fccfg.c:2228) ==53167== by 0x55F4F1A: pango_cairo_fc_font_map_fontset_key_substitute (pangocairo-fcfontmap.c:106) ==53167== by 0x5B88AF6: pango_fc_default_substitute (pangofc-fontmap.c:1795) ==53167== by 0x5B88D15: pango_fc_font_map_get_patterns (pangofc-fontmap.c:1850) ==53167== by 0x5B88FC7: pango_fc_font_map_load_fontset (pangofc-fontmap.c:1952) ==53167== by 0x5623627: pango_font_map_load_fontset (pango-fontmap.c:161) ==53167== by 0x5621743: pango_context_get_metrics (pango-context.c:1782) ==53167== Address 0x150d3450 is 0 bytes inside a block of size 10 free'd ==53167== at 0x483B9F5: free (vg_replace_malloc.c:538) ==53167== by 0x58ABE70: FcValueListDestroy (fcpat.c:147) ==53167== by 0x5898A08: IA__FcConfigSubstituteWithPat.part.0 (fccfg.c:2203) ==53167== by 0x55F4F1A: pango_cairo_fc_font_map_fontset_key_substitute (pangocairo-fcfontmap.c:106) ==53167== by 0x5B88AF6: pango_fc_default_substitute (pangofc-fontmap.c:1795) ==53167== by 0x5B88D15: pango_fc_font_map_get_patterns (pangofc-fontmap.c:1850) ==53167== by 0x5B88FC7: pango_fc_font_map_load_fontset (pangofc-fontmap.c:1952) ==53167== by 0x5623627: pango_font_map_load_fontset (pango-fontmap.c:161) ==53167== by 0x5621743: pango_context_get_metrics (pango-context.c:1782) Use copies of the strings as keys in the hash table to avoid this.
2020-08-30 18:03:04 +02:00
(FcCopyFunc)copy_string,
NULL,
Fix a problem in FcConfigSubstitute We were using the family names from the pattern without copying, and this was leading to a valgrind warning: ==53167== Invalid read of size 1 ==53167== at 0x58B0238: FcStrCaseWalkerNextNonBlank (fcstr.c:198) ==53167== by 0x58B0238: FcStrCaseWalkerNextNonBlank (fcstr.c:186) ==53167== by 0x58B02C7: FcStrCmpIgnoreBlanksAndCase (fcstr.c:281) ==53167== by 0x58A4D44: FcHashTableFind (fchash.c:109) ==53167== by 0x5895E76: FamilyTableAdd (fccfg.c:1634) ==53167== by 0x589646A: FcConfigAdd.isra.0 (fccfg.c:1823) ==53167== by 0x58988CF: IA__FcConfigSubstituteWithPat.part.0 (fccfg.c:2228) ==53167== by 0x55F4F1A: pango_cairo_fc_font_map_fontset_key_substitute (pangocairo-fcfontmap.c:106) ==53167== by 0x5B88AF6: pango_fc_default_substitute (pangofc-fontmap.c:1795) ==53167== by 0x5B88D15: pango_fc_font_map_get_patterns (pangofc-fontmap.c:1850) ==53167== by 0x5B88FC7: pango_fc_font_map_load_fontset (pangofc-fontmap.c:1952) ==53167== by 0x5623627: pango_font_map_load_fontset (pango-fontmap.c:161) ==53167== by 0x5621743: pango_context_get_metrics (pango-context.c:1782) ==53167== Address 0x150d3450 is 0 bytes inside a block of size 10 free'd ==53167== at 0x483B9F5: free (vg_replace_malloc.c:538) ==53167== by 0x58ABE70: FcValueListDestroy (fcpat.c:147) ==53167== by 0x5898A08: IA__FcConfigSubstituteWithPat.part.0 (fccfg.c:2203) ==53167== by 0x55F4F1A: pango_cairo_fc_font_map_fontset_key_substitute (pangocairo-fcfontmap.c:106) ==53167== by 0x5B88AF6: pango_fc_default_substitute (pangofc-fontmap.c:1795) ==53167== by 0x5B88D15: pango_fc_font_map_get_patterns (pangofc-fontmap.c:1850) ==53167== by 0x5B88FC7: pango_fc_font_map_load_fontset (pangofc-fontmap.c:1952) ==53167== by 0x5623627: pango_font_map_load_fontset (pango-fontmap.c:161) ==53167== by 0x5621743: pango_context_get_metrics (pango-context.c:1782) Use copies of the strings as keys in the hash table to avoid this.
2020-08-30 18:03:04 +02:00
free,
free);
e = FcPatternObjectFindElt (p, FC_FAMILY_OBJECT);
if (e)
FamilyTableAdd (table, FcPatternEltValues (e));
}
static void
FamilyTableClear (FamilyTable *table)
{
if (table->family_blank_hash)
FcHashTableDestroy (table->family_blank_hash);
if (table->family_hash)
FcHashTableDestroy (table->family_hash);
}
2002-02-15 00:34:13 +01:00
static FcValueList *
FcConfigMatchValueList (FcPattern *p,
FcPattern *p_pat,
FcMatchKind kind,
2002-02-15 00:34:13 +01:00
FcTest *t,
FcValueList *values,
FamilyTable *table)
2002-02-15 00:34:13 +01:00
{
FcValueList *ret = 0;
FcExpr *e = t->expr;
FcValue value;
FcValueList *v;
FcOp op;
2010-04-12 18:18:50 +02:00
while (e)
2002-02-15 00:34:13 +01:00
{
/* Compute the value of the match expression */
if (FC_OP_GET_OP (e->op) == FcOpComma)
2002-02-15 00:34:13 +01:00
{
value = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
e = e->u.tree.right;
2002-02-15 00:34:13 +01:00
}
else
{
value = FcConfigEvaluate (p, p_pat, kind, e);
e = 0;
}
if (t->object == FC_FAMILY_OBJECT && table)
{
op = FC_OP_GET_OP (t->op);
if (op == FcOpEqual || op == FcOpListing)
{
if (!FamilyTableLookup (table, t->op, FcValueString (&value)))
{
ret = 0;
goto done;
}
}
if (op == FcOpNotEqual && t->qual == FcQualAll)
{
ret = 0;
if (!FamilyTableLookup (table, t->op, FcValueString (&value)))
{
ret = values;
}
goto done;
}
}
for (v = values; v; v = FcValueListNext(v))
{
/* Compare the pattern value to the match expression value */
if (FcConfigCompareValue (&v->value, t->op, &value))
2002-02-15 00:34:13 +01:00
{
if (!ret)
ret = v;
if (t->qual != FcQualAll)
break;
}
else
{
if (t->qual == FcQualAll)
{
ret = 0;
break;
}
2002-02-15 00:34:13 +01:00
}
}
done:
FcValueDestroy (value);
2002-02-15 00:34:13 +01:00
}
return ret;
}
static FcValueList *
FcConfigValues (FcPattern *p, FcPattern *p_pat, FcMatchKind kind, FcExpr *e, FcValueBinding binding)
2002-02-15 00:34:13 +01:00
{
FcValueList *l;
2010-04-12 18:18:50 +02:00
2002-02-15 00:34:13 +01:00
if (!e)
return 0;
l = (FcValueList *) malloc (sizeof (FcValueList));
if (!l)
return 0;
if (FC_OP_GET_OP (e->op) == FcOpComma)
2002-02-15 00:34:13 +01:00
{
l->value = FcConfigEvaluate (p, p_pat, kind, e->u.tree.left);
l->next = FcConfigValues (p, p_pat, kind, e->u.tree.right, binding);
2002-02-15 00:34:13 +01:00
}
else
{
l->value = FcConfigEvaluate (p, p_pat, kind, e);
l->next = NULL;
2002-02-15 00:34:13 +01:00
}
2002-07-31 03:36:37 +02:00
l->binding = binding;
if (l->value.type == FcTypeVoid)
2002-02-15 00:34:13 +01:00
{
FcValueList *next = FcValueListNext(l);
Add functionality to allow fontconfig data structure serialization. This patch allows the fundamental fontconfig data structures to be serialized. I've converted everything from FcPattern down to be able to use *Ptr objects, which can be either static or dynamic (using a union which either contains a pointer or an index) and replaced storage of pointers in the heap with the appropriate *Ptr object. I then changed all writes of pointers to the heap with a *CreateDynamic call, which creates a dynamic Ptr object pointing to the same object as before. This way, the fundamental fontconfig semantics should be unchanged; I did not have to change external signatures this way, although I did change some internal signatures. When given a *Ptr object, just run *U to get back to a normal pointer; it gives the right answer regardless of whether we're using static or dynamic storage. I've also implemented a Fc*Serialize call. Calling FcFontSetSerialize converts the dynamic FcFontSets contained in the config object to static FcFontSets and also converts its dependencies (e.g. everything you'd need to write to disk) to static objects. Note that you have to call Fc*PrepareSerialize first; this call will count the number of objects that actually needs to be allocated, so that we can avoid realloc. The Fc*Serialize calls then check the static pointers for nullness, and allocate the buffers if necessary. I've tested the execution of fc-list and fc-match after Fc*Serialize and they appear to work the same way.
2005-06-28 05:41:02 +02:00
free (l);
l = next;
2002-02-15 00:34:13 +01:00
}
2002-02-15 00:34:13 +01:00
return l;
}
static FcBool
Add functionality to allow fontconfig data structure serialization. This patch allows the fundamental fontconfig data structures to be serialized. I've converted everything from FcPattern down to be able to use *Ptr objects, which can be either static or dynamic (using a union which either contains a pointer or an index) and replaced storage of pointers in the heap with the appropriate *Ptr object. I then changed all writes of pointers to the heap with a *CreateDynamic call, which creates a dynamic Ptr object pointing to the same object as before. This way, the fundamental fontconfig semantics should be unchanged; I did not have to change external signatures this way, although I did change some internal signatures. When given a *Ptr object, just run *U to get back to a normal pointer; it gives the right answer regardless of whether we're using static or dynamic storage. I've also implemented a Fc*Serialize call. Calling FcFontSetSerialize converts the dynamic FcFontSets contained in the config object to static FcFontSets and also converts its dependencies (e.g. everything you'd need to write to disk) to static objects. Note that you have to call Fc*PrepareSerialize first; this call will count the number of objects that actually needs to be allocated, so that we can avoid realloc. The Fc*Serialize calls then check the static pointers for nullness, and allocate the buffers if necessary. I've tested the execution of fc-list and fc-match after Fc*Serialize and they appear to work the same way.
2005-06-28 05:41:02 +02:00
FcConfigAdd (FcValueListPtr *head,
2002-02-15 00:34:13 +01:00
FcValueList *position,
FcBool append,
FcValueList *new,
FcObject object,
FamilyTable *table)
2002-02-15 00:34:13 +01:00
{
FcValueListPtr *prev, l, last, v;
FcValueBinding sameBinding;
2010-04-12 18:18:50 +02:00
/*
* Make sure the stored type is valid for built-in objects
*/
for (l = new; l != NULL; l = FcValueListNext (l))
{
if (!FcObjectValidType (object, l->value.type))
{
fprintf (stderr,
"Fontconfig warning: FcPattern object %s does not accept value", FcObjectName (object));
FcValuePrintFile (stderr, l->value);
fprintf (stderr, "\n");
if (FcDebug () & FC_DBG_EDIT)
{
printf ("Not adding\n");
}
return FcFalse;
}
}
if (object == FC_FAMILY_OBJECT && table)
{
FamilyTableAdd (table, new);
}
if (position)
sameBinding = position->binding;
else
sameBinding = FcValueBindingWeak;
for (v = new; v != NULL; v = FcValueListNext(v))
if (v->binding == FcValueBindingSame)
v->binding = sameBinding;
2002-02-15 00:34:13 +01:00
if (append)
{
if (position)
prev = &position->next;
else
2010-04-12 18:18:50 +02:00
for (prev = head; *prev != NULL;
prev = &(*prev)->next)
2002-02-15 00:34:13 +01:00
;
}
else
{
if (position)
{
2010-04-12 18:18:50 +02:00
for (prev = head; *prev != NULL;
prev = &(*prev)->next)
2002-02-15 00:34:13 +01:00
{
if (*prev == position)
2002-02-15 00:34:13 +01:00
break;
}
}
else
prev = head;
if (FcDebug () & FC_DBG_EDIT)
{
if (*prev == NULL)
2002-02-15 00:34:13 +01:00
printf ("position not on list\n");
}
}
if (FcDebug () & FC_DBG_EDIT)
{
printf ("%s list before ", append ? "Append" : "Prepend");
FcValueListPrintWithPosition (*head, *prev);
2002-02-15 00:34:13 +01:00
printf ("\n");
}
2010-04-12 18:18:50 +02:00
2002-02-15 00:34:13 +01:00
if (new)
{
last = new;
while (last->next != NULL)
last = last->next;
2010-04-12 18:18:50 +02:00
last->next = *prev;
*prev = new;
2002-02-15 00:34:13 +01:00
}
2010-04-12 18:18:50 +02:00
2002-02-15 00:34:13 +01:00
if (FcDebug () & FC_DBG_EDIT)
{
printf ("%s list after ", append ? "Append" : "Prepend");
FcValueListPrint (*head);
printf ("\n");
}
2010-04-12 18:18:50 +02:00
2002-02-15 00:34:13 +01:00
return FcTrue;
}
static void
Add functionality to allow fontconfig data structure serialization. This patch allows the fundamental fontconfig data structures to be serialized. I've converted everything from FcPattern down to be able to use *Ptr objects, which can be either static or dynamic (using a union which either contains a pointer or an index) and replaced storage of pointers in the heap with the appropriate *Ptr object. I then changed all writes of pointers to the heap with a *CreateDynamic call, which creates a dynamic Ptr object pointing to the same object as before. This way, the fundamental fontconfig semantics should be unchanged; I did not have to change external signatures this way, although I did change some internal signatures. When given a *Ptr object, just run *U to get back to a normal pointer; it gives the right answer regardless of whether we're using static or dynamic storage. I've also implemented a Fc*Serialize call. Calling FcFontSetSerialize converts the dynamic FcFontSets contained in the config object to static FcFontSets and also converts its dependencies (e.g. everything you'd need to write to disk) to static objects. Note that you have to call Fc*PrepareSerialize first; this call will count the number of objects that actually needs to be allocated, so that we can avoid realloc. The Fc*Serialize calls then check the static pointers for nullness, and allocate the buffers if necessary. I've tested the execution of fc-list and fc-match after Fc*Serialize and they appear to work the same way.
2005-06-28 05:41:02 +02:00
FcConfigDel (FcValueListPtr *head,
FcValueList *position,
FcObject object,
FamilyTable *table)
2002-02-15 00:34:13 +01:00
{
Add functionality to allow fontconfig data structure serialization. This patch allows the fundamental fontconfig data structures to be serialized. I've converted everything from FcPattern down to be able to use *Ptr objects, which can be either static or dynamic (using a union which either contains a pointer or an index) and replaced storage of pointers in the heap with the appropriate *Ptr object. I then changed all writes of pointers to the heap with a *CreateDynamic call, which creates a dynamic Ptr object pointing to the same object as before. This way, the fundamental fontconfig semantics should be unchanged; I did not have to change external signatures this way, although I did change some internal signatures. When given a *Ptr object, just run *U to get back to a normal pointer; it gives the right answer regardless of whether we're using static or dynamic storage. I've also implemented a Fc*Serialize call. Calling FcFontSetSerialize converts the dynamic FcFontSets contained in the config object to static FcFontSets and also converts its dependencies (e.g. everything you'd need to write to disk) to static objects. Note that you have to call Fc*PrepareSerialize first; this call will count the number of objects that actually needs to be allocated, so that we can avoid realloc. The Fc*Serialize calls then check the static pointers for nullness, and allocate the buffers if necessary. I've tested the execution of fc-list and fc-match after Fc*Serialize and they appear to work the same way.
2005-06-28 05:41:02 +02:00
FcValueListPtr *prev;
2002-02-15 00:34:13 +01:00
if (object == FC_FAMILY_OBJECT && table)
{
FamilyTableDel (table, FcValueString (&position->value));
}
for (prev = head; *prev != NULL; prev = &(*prev)->next)
2002-02-15 00:34:13 +01:00
{
if (*prev == position)
2002-02-15 00:34:13 +01:00
{
*prev = position->next;
position->next = NULL;
FcValueListDestroy (position);
2002-02-15 00:34:13 +01:00
break;
}
}
}
static void
FcConfigPatternAdd (FcPattern *p,
FcObject object,
2002-02-15 00:34:13 +01:00
FcValueList *list,
FcBool append,
FamilyTable *table)
2002-02-15 00:34:13 +01:00
{
if (list)
{
FcPatternElt *e = FcPatternObjectInsertElt (p, object);
2010-04-12 18:18:50 +02:00
2002-02-15 00:34:13 +01:00
if (!e)
return;
FcConfigAdd (&e->values, 0, append, list, object, table);
2002-02-15 00:34:13 +01:00
}
}
/*
* Delete all values associated with a field
*/
static void
FcConfigPatternDel (FcPattern *p,
FcObject object,
FamilyTable *table)
2002-02-15 00:34:13 +01:00
{
FcPatternElt *e = FcPatternObjectFindElt (p, object);
2002-02-15 00:34:13 +01:00
if (!e)
return;
while (e->values != NULL)
FcConfigDel (&e->values, e->values, object, table);
2002-02-15 00:34:13 +01:00
}
static void
FcConfigPatternCanon (FcPattern *p,
FcObject object)
2002-02-15 00:34:13 +01:00
{
FcPatternElt *e = FcPatternObjectFindElt (p, object);
2002-02-15 00:34:13 +01:00
if (!e)
return;
if (e->values == NULL)
FcPatternObjectDel (p, object);
2002-02-15 00:34:13 +01:00
}
FcBool
FcConfigSubstituteWithPat (FcConfig *config,
FcPattern *p,
FcPattern *p_pat,
FcMatchKind kind)
2002-02-15 00:34:13 +01:00
{
FcValue v;
FcPtrList *s;
FcPtrListIter iter, iter2;
FcRule *r;
FcRuleSet *rs;
FcValueList *l, **value = NULL, *vl;
FcPattern *m;
FcStrSet *strs;
FcObject object = FC_INVALID_OBJECT;
FcPatternElt **elt = NULL, *e;
2013-08-05 13:04:13 +02:00
int i, nobjs;
FcBool retval = FcTrue;
2013-08-31 03:43:13 +02:00
FcTest **tst = NULL;
FamilyTable data;
FamilyTable *table = &data;
2013-08-05 13:04:13 +02:00
if (kind < FcMatchKindBegin || kind >= FcMatchKindEnd)
return FcFalse;
config = FcConfigReference (config);
if (!config)
return FcFalse;
s = config->subst[kind];
if (kind == FcMatchPattern)
{
strs = FcGetDefaultLangs ();
if (strs)
{
FcStrList *l = FcStrListCreate (strs);
FcChar8 *lang;
FcValue v;
FcLangSet *lsund = FcLangSetCreate ();
FcLangSetAdd (lsund, (const FcChar8 *)"und");
FcStrSetDestroy (strs);
while (l && (lang = FcStrListNext (l)))
{
FcPatternElt *e = FcPatternObjectFindElt (p, FC_LANG_OBJECT);
if (e)
{
FcValueListPtr ll;
for (ll = FcPatternEltValues (e); ll; ll = FcValueListNext (ll))
{
FcValue vv = FcValueCanonicalize (&ll->value);
if (vv.type == FcTypeLangSet)
{
FcLangSet *ls = FcLangSetCreate ();
FcBool b;
FcLangSetAdd (ls, lang);
b = FcLangSetContains (vv.u.l, ls);
FcLangSetDestroy (ls);
if (b)
goto bail_lang;
if (FcLangSetContains (vv.u.l, lsund))
goto bail_lang;
}
else
{
if (FcStrCmpIgnoreCase (vv.u.s, lang) == 0)
goto bail_lang;
if (FcStrCmpIgnoreCase (vv.u.s, (const FcChar8 *)"und") == 0)
goto bail_lang;
}
}
}
v.type = FcTypeString;
v.u.s = lang;
FcPatternObjectAddWithBinding (p, FC_LANG_OBJECT, v, FcValueBindingWeak, FcTrue);
}
bail_lang:
FcStrListDone (l);
FcLangSetDestroy (lsund);
}
if (FcPatternObjectGet (p, FC_PRGNAME_OBJECT, 0, &v) == FcResultNoMatch)
{
FcChar8 *prgname = FcGetPrgname ();
if (prgname)
FcPatternObjectAddString (p, FC_PRGNAME_OBJECT, prgname);
}
}
2013-08-05 13:04:13 +02:00
nobjs = FC_MAX_BASE_OBJECT + config->maxObjects + 2;
value = (FcValueList **) malloc (SIZEOF_VOID_P * nobjs);
if (!value)
{
retval = FcFalse;
goto bail1;
}
elt = (FcPatternElt **) malloc (SIZEOF_VOID_P * nobjs);
if (!elt)
{
retval = FcFalse;
goto bail1;
}
2013-08-31 03:43:13 +02:00
tst = (FcTest **) malloc (SIZEOF_VOID_P * nobjs);
if (!tst)
{
retval = FcFalse;
goto bail1;
}
2013-08-05 13:04:13 +02:00
2002-02-15 00:34:13 +01:00
if (FcDebug () & FC_DBG_EDIT)
{
printf ("FcConfigSubstitute ");
FcPatternPrint (p);
}
FamilyTableInit (&data, p);
FcPtrListIterInit (s, &iter);
for (; FcPtrListIterIsValid (s, &iter); FcPtrListIterNext (s, &iter))
2002-02-15 00:34:13 +01:00
{
rs = (FcRuleSet *) FcPtrListIterGetValue (s, &iter);
if (FcDebug () & FC_DBG_EDIT)
{
printf ("\nRule Set: %s\n", rs->name);
}
FcPtrListIterInit (rs->subst[kind], &iter2);
for (; FcPtrListIterIsValid (rs->subst[kind], &iter2); FcPtrListIterNext (rs->subst[kind], &iter2))
2002-02-15 00:34:13 +01:00
{
r = (FcRule *) FcPtrListIterGetValue (rs->subst[kind], &iter2);
for (i = 0; i < nobjs; i++)
{
elt[i] = NULL;
value[i] = NULL;
tst[i] = NULL;
}
for (; r; r = r->next)
{
switch (r->type) {
case FcRuleUnknown:
/* shouldn't be reached */
break;
case FcRuleTest:
object = FC_OBJ_ID (r->u.test->object);
/*
* Check the tests to see if
* they all match the pattern
*/
if (FcDebug () & FC_DBG_EDIT)
{
printf ("FcConfigSubstitute test ");
FcTestPrint (r->u.test);
}
if (kind == FcMatchFont && r->u.test->kind == FcMatchPattern)
{
m = p_pat;
table = NULL;
}
else
{
m = p;
table = &data;
}
if (m)
e = FcPatternObjectFindElt (m, r->u.test->object);
else
e = NULL;
/* different 'kind' won't be the target of edit */
if (!elt[object] && kind == r->u.test->kind)
{
elt[object] = e;
tst[object] = r->u.test;
}
/*
* If there's no such field in the font,
* then FcQualAll matches while FcQualAny does not
*/
if (!e)
{
if (r->u.test->qual == FcQualAll)
{
value[object] = NULL;
continue;
}
else
{
if (FcDebug () & FC_DBG_EDIT)
printf ("No match\n");
goto bail;
}
}
/*
* Check to see if there is a match, mark the location
* to apply match-relative edits
*/
vl = FcConfigMatchValueList (m, p_pat, kind, r->u.test, e->values, table);
/* different 'kind' won't be the target of edit */
if (!value[object] && kind == r->u.test->kind)
value[object] = vl;
if (!vl ||
(r->u.test->qual == FcQualFirst && vl != e->values) ||
(r->u.test->qual == FcQualNotFirst && vl == e->values))
{
if (FcDebug () & FC_DBG_EDIT)
printf ("No match\n");
goto bail;
}
break;
case FcRuleEdit:
object = FC_OBJ_ID (r->u.edit->object);
if (FcDebug () & FC_DBG_EDIT)
{
printf ("Substitute ");
FcEditPrint (r->u.edit);
printf ("\n\n");
}
2002-02-15 00:34:13 +01:00
/*
* Evaluate the list of expressions
2002-02-15 00:34:13 +01:00
*/
l = FcConfigValues (p, p_pat, kind, r->u.edit->expr, r->u.edit->binding);
if (tst[object] && (tst[object]->kind == FcMatchFont || kind == FcMatchPattern))
elt[object] = FcPatternObjectFindElt (p, tst[object]->object);
switch (FC_OP_GET_OP (r->u.edit->op)) {
case FcOpAssign:
/*
* If there was a test, then replace the matched
* value with the new list of values
*/
if (value[object])
{
FcValueList *thisValue = value[object];
FcValueList *nextValue = l;
/*
* Append the new list of values after the current value
*/
FcConfigAdd (&elt[object]->values, thisValue, FcTrue, l, r->u.edit->object, table);
/*
* Delete the marked value
*/
if (thisValue)
FcConfigDel (&elt[object]->values, thisValue, object, table);
/*
* Adjust a pointer into the value list to ensure
* future edits occur at the same place
*/
value[object] = nextValue;
break;
}
/* fall through ... */
case FcOpAssignReplace:
/*
* Delete all of the values and insert
* the new set
*/
FcConfigPatternDel (p, r->u.edit->object, table);
FcConfigPatternAdd (p, r->u.edit->object, l, FcTrue, table);
/*
* Adjust a pointer into the value list as they no
* longer point to anything valid
*/
value[object] = NULL;
break;
case FcOpPrepend:
if (value[object])
{
FcConfigAdd (&elt[object]->values, value[object], FcFalse, l, r->u.edit->object, table);
break;
}
/* fall through ... */
case FcOpPrependFirst:
FcConfigPatternAdd (p, r->u.edit->object, l, FcFalse, table);
break;
case FcOpAppend:
if (value[object])
{
FcConfigAdd (&elt[object]->values, value[object], FcTrue, l, r->u.edit->object, table);
break;
}
/* fall through ... */
case FcOpAppendLast:
FcConfigPatternAdd (p, r->u.edit->object, l, FcTrue, table);
break;
case FcOpDelete:
if (value[object])
{
FcConfigDel (&elt[object]->values, value[object], object, table);
2018-07-19 05:08:34 +02:00
FcValueListDestroy (l);
break;
}
/* fall through ... */
case FcOpDeleteAll:
FcConfigPatternDel (p, r->u.edit->object, table);
2018-07-19 05:08:34 +02:00
FcValueListDestroy (l);
break;
default:
FcValueListDestroy (l);
break;
2002-02-15 00:34:13 +01:00
}
/*
* Now go through the pattern and eliminate
* any properties without data
*/
FcConfigPatternCanon (p, r->u.edit->object);
if (FcDebug () & FC_DBG_EDIT)
{
printf ("FcConfigSubstitute edit");
FcPatternPrint (p);
}
2002-02-15 00:34:13 +01:00
break;
}
}
bail:;
2002-02-15 00:34:13 +01:00
}
}
if (FcDebug () & FC_DBG_EDIT)
{
printf ("FcConfigSubstitute done");
FcPatternPrint (p);
}
2013-08-05 13:04:13 +02:00
bail1:
FamilyTableClear (&data);
2013-08-05 13:04:13 +02:00
if (elt)
free (elt);
if (value)
free (value);
2013-08-31 03:43:13 +02:00
if (tst)
free (tst);
FcConfigDestroy (config);
2013-08-05 13:04:13 +02:00
return retval;
2002-02-15 00:34:13 +01:00
}
FcBool
FcConfigSubstitute (FcConfig *config,
FcPattern *p,
FcMatchKind kind)
{
return FcConfigSubstituteWithPat (config, p, 0, kind);
}
#if defined (_WIN32)
2012-10-07 23:46:12 +02:00
static FcChar8 fontconfig_path[1000] = ""; /* MT-dontcare */
FcChar8 fontconfig_instprefix[1000] = ""; /* MT-dontcare */
# if (defined (PIC) || defined (DLL_EXPORT))
2013-01-03 00:35:56 +01:00
BOOL WINAPI
DllMain (HINSTANCE hinstDLL,
DWORD fdwReason,
LPVOID lpvReserved);
BOOL WINAPI
DllMain (HINSTANCE hinstDLL,
DWORD fdwReason,
LPVOID lpvReserved)
{
FcChar8 *p;
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
2012-06-13 13:01:30 +02:00
if (!GetModuleFileName ((HMODULE) hinstDLL, (LPCH) fontconfig_path,
sizeof (fontconfig_path)))
break;
/* If the fontconfig DLL is in a "bin" or "lib" subfolder,
* assume it's a Unix-style installation tree, and use
* "etc/fonts" in there as FONTCONFIG_PATH. Otherwise use the
* folder where the DLL is as FONTCONFIG_PATH.
*/
2012-06-13 13:01:30 +02:00
p = (FcChar8 *) strrchr ((const char *) fontconfig_path, '\\');
if (p)
{
*p = '\0';
2012-06-13 13:01:30 +02:00
p = (FcChar8 *) strrchr ((const char *) fontconfig_path, '\\');
if (p && (FcStrCmpIgnoreCase (p + 1, (const FcChar8 *) "bin") == 0 ||
FcStrCmpIgnoreCase (p + 1, (const FcChar8 *) "lib") == 0))
*p = '\0';
strcat ((char *) fontconfig_instprefix, (char *) fontconfig_path);
2012-06-13 13:01:30 +02:00
strcat ((char *) fontconfig_path, "\\etc\\fonts");
}
else
fontconfig_path[0] = '\0';
2010-04-12 18:18:50 +02:00
break;
}
return TRUE;
}
# endif /* !PIC */
#undef FONTCONFIG_PATH
#define FONTCONFIG_PATH fontconfig_path
#endif /* !_WIN32 */
2002-02-15 00:34:13 +01:00
#ifndef FONTCONFIG_FILE
#define FONTCONFIG_FILE "fonts.conf"
#endif
static FcChar8 *
FcConfigFileExists (const FcChar8 *dir, const FcChar8 *file)
2002-02-15 00:34:13 +01:00
{
FcChar8 *path;
int size, osize;
2002-02-15 00:34:13 +01:00
if (!dir)
dir = (FcChar8 *) "";
osize = strlen ((char *) dir) + 1 + strlen ((char *) file) + 1;
/*
* workaround valgrind warning because glibc takes advantage of how it knows memory is
* allocated to implement strlen by reading in groups of 4
*/
size = (osize + 3) & ~3;
path = malloc (size);
2002-02-15 00:34:13 +01:00
if (!path)
return 0;
2002-03-01 02:00:54 +01:00
strcpy ((char *) path, (const char *) dir);
/* make sure there's a single separator */
#ifdef _WIN32
if ((!path[0] || (path[strlen((char *) path)-1] != '/' &&
path[strlen((char *) path)-1] != '\\')) &&
!(file[0] == '/' ||
file[0] == '\\' ||
(isalpha (file[0]) && file[1] == ':' && (file[2] == '/' || file[2] == '\\'))))
strcat ((char *) path, "\\");
#else
if ((!path[0] || path[strlen((char *) path)-1] != '/') && file[0] != '/')
strcat ((char *) path, "/");
else
osize--;
#endif
strcat ((char *) path, (char *) file);
2002-02-15 00:34:13 +01:00
if (access ((char *) path, R_OK) == 0)
2002-02-15 00:34:13 +01:00
return path;
2010-04-12 18:18:50 +02:00
FcStrFree (path);
2002-02-15 00:34:13 +01:00
return 0;
}
static FcChar8 **
2002-02-15 00:34:13 +01:00
FcConfigGetPath (void)
{
FcChar8 **path;
FcChar8 *env, *e, *colon;
FcChar8 *dir;
2002-02-15 00:34:13 +01:00
int npath;
int i;
npath = 2; /* default dir + null */
env = (FcChar8 *) getenv ("FONTCONFIG_PATH");
2002-02-15 00:34:13 +01:00
if (env)
{
e = env;
npath++;
while (*e)
if (*e++ == FC_SEARCH_PATH_SEPARATOR)
2002-02-15 00:34:13 +01:00
npath++;
}
path = calloc (npath, sizeof (FcChar8 *));
2002-02-15 00:34:13 +01:00
if (!path)
goto bail0;
i = 0;
if (env)
{
e = env;
2010-04-12 18:18:50 +02:00
while (*e)
2002-02-15 00:34:13 +01:00
{
colon = (FcChar8 *) strchr ((char *) e, FC_SEARCH_PATH_SEPARATOR);
2002-02-15 00:34:13 +01:00
if (!colon)
colon = e + strlen ((char *) e);
2002-02-15 00:34:13 +01:00
path[i] = malloc (colon - e + 1);
if (!path[i])
goto bail1;
2002-03-01 02:00:54 +01:00
strncpy ((char *) path[i], (const char *) e, colon - e);
2002-02-15 00:34:13 +01:00
path[i][colon - e] = '\0';
if (*colon)
e = colon + 1;
else
e = colon;
i++;
}
}
2010-04-12 18:18:50 +02:00
#ifdef _WIN32
if (fontconfig_path[0] == '\0')
{
2008-08-12 20:10:03 +02:00
char *p;
2012-06-13 13:01:30 +02:00
if(!GetModuleFileName(NULL, (LPCH) fontconfig_path, sizeof(fontconfig_path)))
goto bail1;
2012-06-13 13:01:30 +02:00
p = strrchr ((const char *) fontconfig_path, '\\');
if (p) *p = '\0';
2012-06-13 13:01:30 +02:00
strcat ((char *) fontconfig_path, "\\fonts");
}
#endif
dir = (FcChar8 *) FONTCONFIG_PATH;
path[i] = malloc (strlen ((char *) dir) + 1);
2002-02-15 00:34:13 +01:00
if (!path[i])
goto bail1;
2002-03-01 02:00:54 +01:00
strcpy ((char *) path[i], (const char *) dir);
2002-02-15 00:34:13 +01:00
return path;
bail1:
for (i = 0; path[i]; i++)
free (path[i]);
free (path);
bail0:
return 0;
}
static void
FcConfigFreePath (FcChar8 **path)
2002-02-15 00:34:13 +01:00
{
FcChar8 **p;
2002-02-15 00:34:13 +01:00
for (p = path; *p; p++)
free (*p);
free (path);
}
2012-10-07 23:46:12 +02:00
static FcBool _FcConfigHomeEnabled = FcTrue; /* MT-goodenough */
FcChar8 *
FcConfigHome (void)
{
if (_FcConfigHomeEnabled)
{
char *home = getenv ("HOME");
#ifdef _WIN32
if (home == NULL)
home = getenv ("USERPROFILE");
#endif
return (FcChar8 *) home;
}
return 0;
}
FcChar8 *
FcConfigXdgCacheHome (void)
{
const char *env = getenv ("XDG_CACHE_HOME");
FcChar8 *ret = NULL;
if (!_FcConfigHomeEnabled)
return NULL;
if (env && env[0])
ret = FcStrCopy ((const FcChar8 *)env);
else
{
const FcChar8 *home = FcConfigHome ();
size_t len = home ? strlen ((const char *)home) : 0;
ret = malloc (len + 7 + 1);
if (ret)
{
2018-07-19 09:50:20 +02:00
if (home)
memcpy (ret, home, len);
memcpy (&ret[len], FC_DIR_SEPARATOR_S ".cache", 7);
ret[len + 7] = 0;
}
}
return ret;
}
FcChar8 *
FcConfigXdgConfigHome (void)
{
const char *env = getenv ("XDG_CONFIG_HOME");
FcChar8 *ret = NULL;
if (!_FcConfigHomeEnabled)
return NULL;
if (env)
ret = FcStrCopy ((const FcChar8 *)env);
else
{
const FcChar8 *home = FcConfigHome ();
size_t len = home ? strlen ((const char *)home) : 0;
ret = malloc (len + 8 + 1);
if (ret)
{
2018-07-19 09:50:20 +02:00
if (home)
memcpy (ret, home, len);
memcpy (&ret[len], FC_DIR_SEPARATOR_S ".config", 8);
ret[len + 8] = 0;
}
}
return ret;
}
FcChar8 *
FcConfigXdgDataHome (void)
{
const char *env = getenv ("XDG_DATA_HOME");
FcChar8 *ret = NULL;
if (!_FcConfigHomeEnabled)
return NULL;
if (env)
ret = FcStrCopy ((const FcChar8 *)env);
else
{
const FcChar8 *home = FcConfigHome ();
size_t len = home ? strlen ((const char *)home) : 0;
ret = malloc (len + 13 + 1);
if (ret)
{
2018-07-19 09:50:20 +02:00
if (home)
memcpy (ret, home, len);
memcpy (&ret[len], FC_DIR_SEPARATOR_S ".local" FC_DIR_SEPARATOR_S "share", 13);
ret[len + 13] = 0;
}
}
return ret;
}
FcStrSet *
FcConfigXdgDataDirs (void)
{
const char *env = getenv ("XDG_DATA_DIRS");
FcStrSet *ret = FcStrSetCreate ();
if (env)
{
FcChar8 *ee, *e = ee = FcStrCopy ((const FcChar8 *) env);
/* We don't intentionally use FC_SEARCH_PATH_SEPARATOR here because of:
* The directories in $XDG_DATA_DIRS should be seperated with a colon ':'.
* in doc.
*/
while (e)
{
FcChar8 *p = (FcChar8 *) strchr ((const char *) e, ':');
FcChar8 *s;
size_t len;
if (!p)
{
s = FcStrCopy (e);
e = NULL;
}
else
{
*p = 0;
s = FcStrCopy (e);
e = p + 1;
}
len = strlen ((const char *) s);
if (s[len - 1] == FC_DIR_SEPARATOR)
{
do
{
len--;
}
while (len > 1 && s[len - 1] == FC_DIR_SEPARATOR);
s[len] = 0;
}
FcStrSetAdd (ret, s);
FcStrFree (s);
}
FcStrFree (ee);
}
else
{
/* From spec doc at https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables
*
* If $XDG_DATA_DIRS is either not set or empty, a value equal to /usr/local/share/:/usr/share/ should be used.
*/
FcStrSetAdd (ret, (const FcChar8 *) "/usr/local/share");
FcStrSetAdd (ret, (const FcChar8 *) "/usr/share");
}
return ret;
}
FcBool
FcConfigEnableHome (FcBool enable)
{
FcBool prev = _FcConfigHomeEnabled;
_FcConfigHomeEnabled = enable;
return prev;
}
FcChar8 *
FcConfigGetFilename (FcConfig *config,
const FcChar8 *url)
2002-02-15 00:34:13 +01:00
{
FcChar8 *file, *dir, **path, **p;
const FcChar8 *sysroot;
config = FcConfigReference (config);
if (!config)
return NULL;
sysroot = FcConfigGetSysRoot (config);
2002-02-15 00:34:13 +01:00
if (!url || !*url)
{
url = (FcChar8 *) getenv ("FONTCONFIG_FILE");
2002-02-15 00:34:13 +01:00
if (!url)
url = (FcChar8 *) FONTCONFIG_FILE;
2002-02-15 00:34:13 +01:00
}
file = 0;
if (FcStrIsAbsoluteFilename(url))
{
if (sysroot)
{
size_t len = strlen ((const char *) sysroot);
/* Workaround to avoid adding sysroot repeatedly */
if (strncmp ((const char *) url, (const char *) sysroot, len) == 0)
sysroot = NULL;
}
file = FcConfigFileExists (sysroot, url);
goto bail;
}
if (*url == '~')
{
dir = FcConfigHome ();
2002-02-15 00:34:13 +01:00
if (dir)
{
FcChar8 *s;
if (sysroot)
s = FcStrBuildFilename (sysroot, dir, NULL);
else
s = dir;
file = FcConfigFileExists (s, url + 1);
if (sysroot)
FcStrFree (s);
}
2002-02-15 00:34:13 +01:00
else
file = 0;
}
else
{
path = FcConfigGetPath ();
if (!path)
{
file = NULL;
goto bail;
}
for (p = path; *p; p++)
{
FcChar8 *s;
if (sysroot)
s = FcStrBuildFilename (sysroot, *p, NULL);
else
s = *p;
file = FcConfigFileExists (s, url);
if (sysroot)
FcStrFree (s);
if (file)
break;
}
FcConfigFreePath (path);
}
bail:
FcConfigDestroy (config);
2002-02-15 00:34:13 +01:00
return file;
}
FcChar8 *
FcConfigFilename (const FcChar8 *url)
{
return FcConfigGetFilename (NULL, url);
}
FcChar8 *
FcConfigRealFilename (FcConfig *config,
const FcChar8 *url)
{
FcChar8 *n = FcConfigGetFilename (config, url);
if (n)
{
FcChar8 buf[FC_PATH_MAX];
ssize_t len;
struct stat sb;
if ((len = FcReadLink (n, buf, sizeof (buf) - 1)) != -1)
{
buf[len] = 0;
/* We try to pick up a config from FONTCONFIG_FILE
* when url is null. don't try to address the real filename
* if it is a named pipe.
*/
if (!url && FcStat (n, &sb) == 0 && S_ISFIFO (sb.st_mode))
return n;
else if (!FcStrIsAbsoluteFilename (buf))
{
FcChar8 *dirname = FcStrDirname (n);
FcStrFree (n);
if (!dirname)
return NULL;
FcChar8 *path = FcStrBuildFilename (dirname, buf, NULL);
FcStrFree (dirname);
if (!path)
return NULL;
n = FcStrCanonFilename (path);
FcStrFree (path);
}
else
{
FcStrFree (n);
n = FcStrdup (buf);
}
}
}
return n;
}
2002-02-15 00:34:13 +01:00
/*
* Manage the application-specific fonts
*/
FcBool
FcConfigAppFontAddFile (FcConfig *config,
const FcChar8 *file)
2002-02-15 00:34:13 +01:00
{
FcFontSet *set;
FcStrSet *subdirs;
FcStrList *sublist;
FcChar8 *subdir;
FcBool ret = FcTrue;
2002-02-15 00:34:13 +01:00
config = FcConfigReference (config);
2002-02-15 00:34:13 +01:00
if (!config)
return FcFalse;
2002-02-15 00:34:13 +01:00
subdirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
if (!subdirs)
{
ret = FcFalse;
goto bail;
}
2010-04-12 18:18:50 +02:00
2002-02-15 00:34:13 +01:00
set = FcConfigGetFonts (config, FcSetApplication);
if (!set)
{
set = FcFontSetCreate ();
if (!set)
{
FcStrSetDestroy (subdirs);
ret = FcFalse;
goto bail;
}
2002-02-15 00:34:13 +01:00
FcConfigSetFonts (config, set, FcSetApplication);
}
if (!FcFileScanConfig (set, subdirs, file, config))
{
FcStrSetDestroy (subdirs);
ret = FcFalse;
goto bail;
}
if ((sublist = FcStrListCreate (subdirs)))
{
while ((subdir = FcStrListNext (sublist)))
{
FcConfigAppFontAddDir (config, subdir);
}
FcStrListDone (sublist);
}
FcStrSetDestroy (subdirs);
bail:
FcConfigDestroy (config);
return ret;
2002-02-15 00:34:13 +01:00
}
FcBool
FcConfigAppFontAddDir (FcConfig *config,
const FcChar8 *dir)
2002-02-15 00:34:13 +01:00
{
FcFontSet *set;
FcStrSet *dirs;
FcBool ret = FcTrue;
2010-04-12 18:18:50 +02:00
config = FcConfigReference (config);
2002-02-15 00:34:13 +01:00
if (!config)
return FcFalse;
dirs = FcStrSetCreateEx (FCSS_GROW_BY_64);
if (!dirs)
{
ret = FcFalse;
goto bail;
}
2010-04-12 18:18:50 +02:00
2002-02-15 00:34:13 +01:00
set = FcConfigGetFonts (config, FcSetApplication);
if (!set)
{
set = FcFontSetCreate ();
if (!set)
{
FcStrSetDestroy (dirs);
ret = FcFalse;
goto bail;
}
2002-02-15 00:34:13 +01:00
FcConfigSetFonts (config, set, FcSetApplication);
}
2010-04-12 18:18:50 +02:00
FcStrSetAddFilename (dirs, dir);
2010-04-12 18:18:50 +02:00
if (!FcConfigAddDirList (config, FcSetApplication, dirs))
{
FcStrSetDestroy (dirs);
ret = FcFalse;
goto bail;
}
FcStrSetDestroy (dirs);
bail:
FcConfigDestroy (config);
return ret;
2002-02-15 00:34:13 +01:00
}
void
FcConfigAppFontClear (FcConfig *config)
{
config = FcConfigReference (config);
if (!config)
return;
2002-02-15 00:34:13 +01:00
FcConfigSetFonts (config, 0, FcSetApplication);
FcConfigDestroy (config);
2002-02-15 00:34:13 +01:00
}
/*
* Manage filename-based font source selectors
*/
FcBool
FcConfigGlobAdd (FcConfig *config,
const FcChar8 *glob,
FcBool accept)
{
FcStrSet *set = accept ? config->acceptGlobs : config->rejectGlobs;
FcChar8 *realglob = FcStrCopyFilename(glob);
if (!realglob)
return FcFalse;
FcBool ret = FcStrSetAdd (set, realglob);
FcStrFree(realglob);
return ret;
}
static FcBool
FcConfigGlobsMatch (const FcStrSet *globs,
const FcChar8 *string)
{
int i;
for (i = 0; i < globs->num; i++)
if (FcStrGlobMatch (globs->strs[i], string))
return FcTrue;
return FcFalse;
}
FcBool
FcConfigAcceptFilename (FcConfig *config,
const FcChar8 *filename)
{
if (FcConfigGlobsMatch (config->acceptGlobs, filename))
return FcTrue;
if (FcConfigGlobsMatch (config->rejectGlobs, filename))
return FcFalse;
return FcTrue;
}
/*
* Manage font-pattern based font source selectors
*/
FcBool
FcConfigPatternsAdd (FcConfig *config,
FcPattern *pattern,
FcBool accept)
{
FcFontSet *set = accept ? config->acceptPatterns : config->rejectPatterns;
return FcFontSetAdd (set, pattern);
}
static FcBool
FcConfigPatternsMatch (const FcFontSet *patterns,
const FcPattern *font)
{
int i;
2010-04-12 18:18:50 +02:00
for (i = 0; i < patterns->nfont; i++)
if (FcListPatternMatchAny (patterns->fonts[i], font))
return FcTrue;
return FcFalse;
}
FcBool
FcConfigAcceptFont (FcConfig *config,
const FcPattern *font)
{
if (FcConfigPatternsMatch (config->acceptPatterns, font))
return FcTrue;
if (FcConfigPatternsMatch (config->rejectPatterns, font))
return FcFalse;
return FcTrue;
}
const FcChar8 *
FcConfigGetSysRoot (const FcConfig *config)
{
if (!config)
{
config = FcConfigGetCurrent ();
if (!config)
return NULL;
}
return config->sysRoot;
}
void
FcConfigSetSysRoot (FcConfig *config,
const FcChar8 *sysroot)
{
FcChar8 *s = NULL;
FcBool init = FcFalse;
int nretry = 3;
retry:
if (!config)
{
/* We can't use FcConfigGetCurrent() here to ensure
* the sysroot is set prior to initialize FcConfig,
* to avoid loading caches from non-sysroot dirs.
* So postpone the initialization later.
*/
config = fc_atomic_ptr_get (&_fcConfig);
if (!config)
{
config = FcConfigCreate ();
if (!config)
return;
init = FcTrue;
}
}
if (sysroot)
{
s = FcStrRealPath (sysroot);
if (!s)
return;
}
if (config->sysRoot)
FcStrFree (config->sysRoot);
config->sysRoot = s;
if (init)
{
config = FcInitLoadOwnConfigAndFonts (config);
if (!config)
{
/* Something failed. this is usually unlikely. so retrying */
init = FcFalse;
if (--nretry == 0)
{
fprintf (stderr, "Fontconfig warning: Unable to initialize config and retry limit exceeded. sysroot functionality may not work as expected.\n");
return;
}
goto retry;
}
FcConfigSetCurrent (config);
/* FcConfigSetCurrent() increases the refcount.
* decrease it here to avoid the memory leak.
*/
FcConfigDestroy (config);
}
}
FcRuleSet *
FcRuleSetCreate (const FcChar8 *name)
{
FcRuleSet *ret = (FcRuleSet *) malloc (sizeof (FcRuleSet));
FcMatchKind k;
const FcChar8 *p;
if (!name)
p = (const FcChar8 *)"";
else
p = name;
if (ret)
{
ret->name = FcStrdup (p);
ret->description = NULL;
ret->domain = NULL;
for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
ret->subst[k] = FcPtrListCreate (FcDestroyAsRule);
FcRefInit (&ret->ref, 1);
}
return ret;
}
void
FcRuleSetDestroy (FcRuleSet *rs)
{
FcMatchKind k;
if (!rs)
return;
if (FcRefDec (&rs->ref) != 1)
return;
if (rs->name)
FcStrFree (rs->name);
if (rs->description)
FcStrFree (rs->description);
if (rs->domain)
FcStrFree (rs->domain);
for (k = FcMatchKindBegin; k < FcMatchKindEnd; k++)
FcPtrListDestroy (rs->subst[k]);
free (rs);
}
void
FcRuleSetReference (FcRuleSet *rs)
{
if (!FcRefIsConst (&rs->ref))
FcRefInc (&rs->ref);
}
void
FcRuleSetEnable (FcRuleSet *rs,
FcBool flag)
{
if (rs)
{
rs->enabled = flag;
/* XXX: we may want to provide a feature
* to enable/disable rulesets through API
* in the future?
*/
}
}
void
FcRuleSetAddDescription (FcRuleSet *rs,
const FcChar8 *domain,
const FcChar8 *description)
{
if (rs->domain)
FcStrFree (rs->domain);
if (rs->description)
FcStrFree (rs->description);
rs->domain = domain ? FcStrdup (domain) : NULL;
rs->description = description ? FcStrdup (description) : NULL;
}
int
FcRuleSetAdd (FcRuleSet *rs,
FcRule *rule,
FcMatchKind kind)
{
FcPtrListIter iter;
FcRule *r;
int n = 0, ret;
if (!rs ||
kind < FcMatchKindBegin || kind >= FcMatchKindEnd)
return -1;
FcPtrListIterInitAtLast (rs->subst[kind], &iter);
if (!FcPtrListIterAdd (rs->subst[kind], &iter, rule))
return -1;
for (r = rule; r; r = r->next)
{
switch (r->type)
{
case FcRuleTest:
2018-07-19 09:55:40 +02:00
if (r->u.test)
{
if (r->u.test->kind == FcMatchDefault)
r->u.test->kind = kind;
if (n < r->u.test->object)
n = r->u.test->object;
}
break;
case FcRuleEdit:
if (n < r->u.edit->object)
n = r->u.edit->object;
break;
default:
break;
}
}
if (FcDebug () & FC_DBG_EDIT)
{
printf ("Add Rule(kind:%d, name: %s) ", kind, rs->name);
FcRulePrint (rule);
}
ret = FC_OBJ_ID (n) - FC_MAX_BASE_OBJECT;
return ret < 0 ? 0 : ret;
}
void
FcConfigFileInfoIterInit (FcConfig *config,
FcConfigFileInfoIter *iter)
{
FcConfig *c;
FcPtrListIter *i = (FcPtrListIter *)iter;
if (!config)
c = FcConfigGetCurrent ();
else
c = config;
FcPtrListIterInit (c->rulesetList, i);
}
FcBool
FcConfigFileInfoIterNext (FcConfig *config,
FcConfigFileInfoIter *iter)
{
FcConfig *c;
FcPtrListIter *i = (FcPtrListIter *)iter;
if (!config)
c = FcConfigGetCurrent ();
else
c = config;
if (FcPtrListIterIsValid (c->rulesetList, i))
{
FcPtrListIterNext (c->rulesetList, i);
}
else
return FcFalse;
return FcTrue;
}
FcBool
FcConfigFileInfoIterGet (FcConfig *config,
FcConfigFileInfoIter *iter,
FcChar8 **name,
FcChar8 **description,
FcBool *enabled)
{
FcConfig *c;
FcRuleSet *r;
FcPtrListIter *i = (FcPtrListIter *)iter;
if (!config)
c = FcConfigGetCurrent ();
else
c = config;
if (!FcPtrListIterIsValid (c->rulesetList, i))
return FcFalse;
r = FcPtrListIterGetValue (c->rulesetList, i);
if (name)
*name = FcStrdup (r->name && r->name[0] ? r->name : (const FcChar8 *) "fonts.conf");
if (description)
*description = FcStrdup (!r->description ? _("No description") :
dgettext (r->domain ? (const char *) r->domain : GETTEXT_PACKAGE "-conf",
(const char *) r->description));
if (enabled)
*enabled = r->enabled;
return FcTrue;
}
#define __fccfg__
#include "fcaliastail.h"
#undef __fccfg__