Path: blob/master/src/java.desktop/unix/native/common/awt/fontpath.c
66645 views
/*1* Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#if defined(__linux__)26#include <string.h>27#endif /* __linux__ */28#include <stdio.h>29#include <stdlib.h>30#include <strings.h>31#include <sys/types.h>32#include <sys/stat.h>33#include <sys/mman.h>34#include <fcntl.h>35#include <unistd.h>3637#include <jni.h>38#include <jni_util.h>39#include <jvm_md.h>40#include <sizecalc.h>41#ifndef HEADLESS42#include <X11/Xlib.h>43#include <awt.h>44#else45/* locks ought to be included from awt.h */46#define AWT_LOCK()47#define AWT_UNLOCK()48#endif /* !HEADLESS */4950#if defined(__linux__) && !defined(MAP_FAILED)51#define MAP_FAILED ((caddr_t)-1)52#endif5354#ifndef HEADLESS55extern Display *awt_display;56#endif /* !HEADLESS */5758#define FONTCONFIG_DLL_VERSIONED VERSIONED_JNI_LIB_NAME("fontconfig", "1")59#define FONTCONFIG_DLL JNI_LIB_NAME("fontconfig")6061#define MAXFDIRS 512 /* Max number of directories that contain fonts */6263#if defined( __linux__)64/* All the known interesting locations we have discovered on65* various flavors of Linux66*/67static char *fullLinuxFontPath[] = {68"/usr/X11R6/lib/X11/fonts/TrueType", /* RH 7.1+ */69"/usr/X11R6/lib/X11/fonts/truetype", /* SuSE */70"/usr/X11R6/lib/X11/fonts/tt",71"/usr/X11R6/lib/X11/fonts/TTF",72"/usr/X11R6/lib/X11/fonts/OTF", /* RH 9.0 (but empty!) */73"/usr/share/fonts/ja/TrueType", /* RH 7.2+ */74"/usr/share/fonts/truetype",75"/usr/share/fonts/ko/TrueType", /* RH 9.0 */76"/usr/share/fonts/zh_CN/TrueType", /* RH 9.0 */77"/usr/share/fonts/zh_TW/TrueType", /* RH 9.0 */78"/var/lib/defoma/x-ttcidfont-conf.d/dirs/TrueType", /* Debian */79"/usr/X11R6/lib/X11/fonts/Type1",80"/usr/share/fonts/default/Type1", /* RH 9.0 */81NULL, /* terminates the list */82};83#elif defined(_AIX)84static char *fullAixFontPath[] = {85"/usr/lpp/X11/lib/X11/fonts/Type1", /* from X11.fnt.iso_T1 */86"/usr/lpp/X11/lib/X11/fonts/TrueType", /* from X11.fnt.ucs.ttf */87NULL, /* terminates the list */88};89#endif9091static char **getFontConfigLocations();9293typedef struct {94const char *name[MAXFDIRS];95int num;96} fDirRecord, *fDirRecordPtr;9798#ifndef HEADLESS99100/*101* Returns True if display is local, False of it's remote.102*/103jboolean isDisplayLocal(JNIEnv *env) {104static jboolean isLocal = False;105static jboolean isLocalSet = False;106jboolean ret;107108if (! isLocalSet) {109jclass geCls = (*env)->FindClass(env, "java/awt/GraphicsEnvironment");110CHECK_NULL_RETURN(geCls, JNI_FALSE);111jmethodID getLocalGE = (*env)->GetStaticMethodID(env, geCls,112"getLocalGraphicsEnvironment",113"()Ljava/awt/GraphicsEnvironment;");114CHECK_NULL_RETURN(getLocalGE, JNI_FALSE);115jobject ge = (*env)->CallStaticObjectMethod(env, geCls, getLocalGE);116JNU_CHECK_EXCEPTION_RETURN(env, JNI_FALSE);117118jclass sgeCls = (*env)->FindClass(env,119"sun/java2d/SunGraphicsEnvironment");120CHECK_NULL_RETURN(sgeCls, JNI_FALSE);121if ((*env)->IsInstanceOf(env, ge, sgeCls)) {122jmethodID isDisplayLocal = (*env)->GetMethodID(env, sgeCls,123"isDisplayLocal",124"()Z");125JNU_CHECK_EXCEPTION_RETURN(env, JNI_FALSE);126isLocal = (*env)->CallBooleanMethod(env, ge, isDisplayLocal);127JNU_CHECK_EXCEPTION_RETURN(env, JNI_FALSE);128} else {129isLocal = True;130}131isLocalSet = True;132}133134return isLocal;135}136137static char **getX11FontPath ()138{139char **x11Path, **fontdirs;140int i, pos, slen, nPaths, numDirs;141142x11Path = XGetFontPath (awt_display, &nPaths);143144/* This isn't ever going to be perfect: the font path may contain145* much we aren't interested in, but the cost should be moderate146* Exclude all directories that contain the strings "Speedo","/F3/",147* "75dpi", "100dpi", "misc" or "bitmap", or don't begin with a "/",148* the last of which should exclude font servers.149* Also exclude the user specific ".gnome*" directories which150* aren't going to contain the system fonts we need.151* Hopefully we are left only with Type1 and TrueType directories.152* It doesn't matter much if there are extraneous directories, it'll just153* cost us a little wasted effort upstream.154*/155fontdirs = (char**)calloc(nPaths+1, sizeof(char*));156if (fontdirs == NULL) {157return NULL;158}159pos = 0;160for (i=0; i < nPaths; i++) {161if (x11Path[i][0] != '/') {162continue;163}164if (strstr(x11Path[i], "/75dpi") != NULL) {165continue;166}167if (strstr(x11Path[i], "/100dpi") != NULL) {168continue;169}170if (strstr(x11Path[i], "/misc") != NULL) {171continue;172}173if (strstr(x11Path[i], "/Speedo") != NULL) {174continue;175}176if (strstr(x11Path[i], ".gnome") != NULL) {177continue;178}179fontdirs[pos] = strdup(x11Path[i]);180slen = strlen(fontdirs[pos]);181if (slen > 0 && fontdirs[pos][slen-1] == '/') {182fontdirs[pos][slen-1] = '\0'; /* null out trailing "/" */183}184pos++;185}186187XFreeFontPath(x11Path);188if (pos == 0) {189free(fontdirs);190fontdirs = NULL;191}192return fontdirs;193}194195196#endif /* !HEADLESS */197198#if defined(__linux__)199/* from awt_LoadLibrary.c */200JNIEXPORT jboolean JNICALL AWTIsHeadless();201#endif202203/* This eliminates duplicates, at a non-linear but acceptable cost204* since the lists are expected to be reasonably short, and then205* deletes references to non-existent directories, and returns206* a single path consisting of unique font directories.207*/208static char* mergePaths(char **p1, char **p2, char **p3, jboolean noType1) {209210int len1=0, len2=0, len3=0, totalLen=0, numDirs=0,211currLen, i, j, found, pathLen=0;212char **ptr, **fontdirs;213char *fontPath = NULL;214215if (p1 != NULL) {216ptr = p1;217while (*ptr++ != NULL) len1++;218}219if (p2 != NULL) {220ptr = p2;221222while (*ptr++ != NULL) len2++;223}224if (p3 != NULL) {225ptr = p3;226while (*ptr++ != NULL) len3++;227}228totalLen = len1+len2+len3;229fontdirs = (char**)calloc(totalLen, sizeof(char*));230if (fontdirs == NULL) {231return NULL;232}233234for (i=0; i < len1; i++) {235if (noType1 && strstr(p1[i], "Type1") != NULL) {236continue;237}238fontdirs[numDirs++] = p1[i];239}240241currLen = numDirs; /* only compare against previous path dirs */242for (i=0; i < len2; i++) {243if (noType1 && strstr(p2[i], "Type1") != NULL) {244continue;245}246found = 0;247for (j=0; j < currLen; j++) {248if (strcmp(fontdirs[j], p2[i]) == 0) {249found = 1;250break;251}252}253if (!found) {254fontdirs[numDirs++] = p2[i];255}256}257258currLen = numDirs; /* only compare against previous path dirs */259for (i=0; i < len3; i++) {260if (noType1 && strstr(p3[i], "Type1") != NULL) {261continue;262}263found = 0;264for (j=0; j < currLen; j++) {265if (strcmp(fontdirs[j], p3[i]) == 0) {266found = 1;267break;268}269}270if (!found) {271fontdirs[numDirs++] = p3[i];272}273}274275/* Now fontdirs contains unique dirs and numDirs records how many.276* What we don't know is if they all exist. On reflection I think277* this isn't an issue, so for now I will return all these locations,278* converted to one string */279for (i=0; i<numDirs; i++) {280pathLen += (strlen(fontdirs[i]) + 1);281}282if (pathLen > 0 && (fontPath = malloc(pathLen))) {283*fontPath = '\0';284for (i = 0; i<numDirs; i++) {285if (i != 0) {286strcat(fontPath, ":");287}288strcat(fontPath, fontdirs[i]);289}290}291free (fontdirs);292293return fontPath;294}295296/*297* The goal of this function is to find all "system" fonts which298* are needed by the JRE to display text in supported locales etc, and299* to support APIs which allow users to enumerate all system fonts and use300* them from their Java applications.301* The preferred mechanism is now using the new "fontconfig" library302* This exists on newer versions of Linux and Solaris (S10 and above)303* The library is dynamically located. The results are merged with304* a set of "known" locations and with the X11 font path, if running in305* a local X11 environment.306* The hardwired paths are built into the JDK binary so as new font locations307* are created on a host plaform for them to be located by the JRE they will308* need to be added ito the host's font configuration database, typically309* /etc/fonts/local.conf, and to ensure that directory contains a fonts.dir310* NB: Fontconfig also depends heavily for performance on the host O/S311* maintaining up to date caches.312* This is consistent with the requirements of the desktop environments313* on these OSes.314* This also frees us from X11 APIs as JRE is required to function in315* a "headless" mode where there is no Xserver.316*/317static char *getPlatformFontPathChars(JNIEnv *env, jboolean noType1, jboolean isX11) {318319char **fcdirs = NULL, **x11dirs = NULL, **knowndirs = NULL, *path = NULL;320321// mod: NULL -> FALLBACK322/* As of 1.5 we try to use fontconfig on both Solaris and Linux.323* If its not available FALLBACK is returned.324*/325fcdirs = getFontConfigLocations();326327#if defined(__linux__)328knowndirs = fullLinuxFontPath;329#elif defined(_AIX)330knowndirs = fullAixFontPath;331#endif332/* REMIND: this code requires to be executed when the GraphicsEnvironment333* is already initialised. That is always true, but if it were not so,334* this code could throw an exception and the fontpath would fail to335* be initialised.336*/337#ifndef HEADLESS338if (isX11) { // The following only works in an x11 environment.339#if defined(__linux__)340/* There's no headless build on linux ... */341if (!AWTIsHeadless()) { /* .. so need to call a function to check */342#endif343/* Using the X11 font path to locate font files is now a fallback344* useful only if fontconfig failed, or is incomplete. So we could345* remove this code completely and the consequences should be rare346* and non-fatal. If this happens, then the calling Java code can347* be modified to no longer require that the AWT lock (the X11GE)348* be initialised prior to calling this code.349*/350AWT_LOCK();351if (isDisplayLocal(env)) {352x11dirs = getX11FontPath();353}354AWT_UNLOCK();355#if defined(__linux__)356}357#endif358}359#endif /* !HEADLESS */360path = mergePaths(fcdirs, x11dirs, knowndirs, noType1);361if (fcdirs != NULL) {362char **p = fcdirs;363while (*p != NULL) free(*p++);364free(fcdirs);365}366367if (x11dirs != NULL) {368char **p = x11dirs;369while (*p != NULL) free(*p++);370free(x11dirs);371}372373return path;374}375376JNIEXPORT jstring JNICALL Java_sun_awt_FcFontManager_getFontPathNative377(JNIEnv *env, jobject thiz, jboolean noType1, jboolean isX11) {378jstring ret;379static char *ptr = NULL; /* retain result across calls */380381if (ptr == NULL) {382ptr = getPlatformFontPathChars(env, noType1, isX11);383}384ret = (*env)->NewStringUTF(env, ptr);385return ret;386}387388#include <dlfcn.h>389390#include <fontconfig/fontconfig.h>391392393static void* openFontConfig() {394395char *homeEnv;396static char *homeEnvStr = "HOME="; /* must be static */397void* libfontconfig = NULL;398399/* Private workaround to not use fontconfig library.400* May be useful during testing/debugging401*/402char *useFC = getenv("USE_J2D_FONTCONFIG");403if (useFC != NULL && !strcmp(useFC, "no")) {404return NULL;405}406407#if defined(_AIX)408/* On AIX, fontconfig is not a standard package supported by IBM.409* instead it has to be installed from the "AIX Toolbox for Linux Applications"410* site http://www-03.ibm.com/systems/power/software/aix/linux/toolbox/alpha.html411* and will be installed under /opt/freeware/lib/libfontconfig.a.412* Notice that the archive contains the real 32- and 64-bit shared libraries.413* We first try to load 'libfontconfig.so' from the default library path in the414* case the user has installed a private version of the library and if that415* doesn't succeed, we try the version from /opt/freeware/lib/libfontconfig.a416*/417libfontconfig = dlopen("libfontconfig.so", RTLD_LOCAL|RTLD_LAZY);418if (libfontconfig == NULL) {419libfontconfig = dlopen("/opt/freeware/lib/libfontconfig.a(libfontconfig.so.1)", RTLD_MEMBER|RTLD_LOCAL|RTLD_LAZY);420if (libfontconfig == NULL) {421return NULL;422}423}424#else425/* 64 bit sparc should pick up the right version from the lib path.426* New features may be added to libfontconfig, this is expected to427* be compatible with old features, but we may need to start428* distinguishing the library version, to know whether to expect429* certain symbols - and functionality - to be available.430* Also add explicit search for .so.1 in case .so symlink doesn't exist.431*/432libfontconfig = dlopen(FONTCONFIG_DLL_VERSIONED, RTLD_LOCAL|RTLD_LAZY);433if (libfontconfig == NULL) {434libfontconfig = dlopen(FONTCONFIG_DLL, RTLD_LOCAL|RTLD_LAZY);435if (libfontconfig == NULL) {436return NULL;437}438}439#endif440441/* Version 1.0 of libfontconfig crashes if HOME isn't defined in442* the environment. This should generally never happen, but we can't443* control it, and can't control the version of fontconfig, so iff444* its not defined we set it to an empty value which is sufficient445* to prevent a crash. I considered unsetting it before exit, but446* it doesn't appear to work on Solaris, so I will leave it set.447*/448homeEnv = getenv("HOME");449if (homeEnv == NULL) {450putenv(homeEnvStr);451}452453return libfontconfig;454}455456typedef void* (FcFiniFuncType)();457458static void closeFontConfig(void* libfontconfig, jboolean fcFini) {459460/* NB FcFini is not in (eg) the Solaris 10 version of fontconfig. Its not461* clear if this means we are really leaking resources in those cases462* but it seems we should call this function when its available.463* But since the Swing GTK code may be still accessing the lib, its probably464* safest for now to just let this "leak" rather than potentially465* concurrently free global data still in use by other code.466*/467#if 0468if (fcFini) { /* release resources */469FcFiniFuncType FcFini = (FcFiniFuncType)dlsym(libfontconfig, "FcFini");470471if (FcFini != NULL) {472(*FcFini)();473}474}475#endif476dlclose(libfontconfig);477}478479typedef FcConfig* (*FcInitLoadConfigFuncType)();480typedef FcPattern* (*FcPatternBuildFuncType)(FcPattern *orig, ...);481typedef FcObjectSet* (*FcObjectSetFuncType)(const char *first, ...);482typedef FcFontSet* (*FcFontListFuncType)(FcConfig *config,483FcPattern *p,484FcObjectSet *os);485typedef FcResult (*FcPatternGetBoolFuncType)(const FcPattern *p,486const char *object,487int n,488FcBool *b);489typedef FcResult (*FcPatternGetIntegerFuncType)(const FcPattern *p,490const char *object,491int n,492int *i);493typedef FcResult (*FcPatternGetStringFuncType)(const FcPattern *p,494const char *object,495int n,496FcChar8 ** s);497typedef FcChar8* (*FcStrDirnameFuncType)(const FcChar8 *file);498typedef void (*FcPatternDestroyFuncType)(FcPattern *p);499typedef void (*FcObjectSetDestroyFuncType)(FcObjectSet *os);500typedef void (*FcFontSetDestroyFuncType)(FcFontSet *s);501typedef FcPattern* (*FcNameParseFuncType)(const FcChar8 *name);502typedef FcBool (*FcPatternAddStringFuncType)(FcPattern *p,503const char *object,504const FcChar8 *s);505typedef void (*FcDefaultSubstituteFuncType)(FcPattern *p);506typedef FcBool (*FcConfigSubstituteFuncType)(FcConfig *config,507FcPattern *p,508FcMatchKind kind);509typedef FcPattern* (*FcFontMatchFuncType)(FcConfig *config,510FcPattern *p,511FcResult *result);512typedef FcFontSet* (*FcFontSetCreateFuncType)();513typedef FcBool (*FcFontSetAddFuncType)(FcFontSet *s, FcPattern *font);514515typedef FcResult (*FcPatternGetCharSetFuncType)(FcPattern *p,516const char *object,517int n,518FcCharSet **c);519typedef FcFontSet* (*FcFontSortFuncType)(FcConfig *config,520FcPattern *p,521FcBool trim,522FcCharSet **csp,523FcResult *result);524typedef FcCharSet* (*FcCharSetUnionFuncType)(const FcCharSet *a,525const FcCharSet *b);526typedef FcCharSet* (*FcCharSetDestroyFuncType)(FcCharSet *fcs);527typedef FcChar32 (*FcCharSetSubtractCountFuncType)(const FcCharSet *a,528const FcCharSet *b);529530typedef int (*FcGetVersionFuncType)();531532typedef FcStrList* (*FcConfigGetCacheDirsFuncType)(FcConfig *config);533typedef FcChar8* (*FcStrListNextFuncType)(FcStrList *list);534typedef FcChar8* (*FcStrListDoneFuncType)(FcStrList *list);535536// mod: fallback directories537static char **getFallbackFontLocations() {538539char **fontdirs = (char**)calloc(3, sizeof(char*));540fontdirs[0] = (char *)calloc(1, 4096);541fontdirs[1] = (char *)calloc(1, 40);542sprintf(fontdirs[0], "%s/lib/fonts", getenv("JAVA_HOME"));543sprintf(fontdirs[1], "%s", "/System/Library/Fonts/UnicodeSupport");544return fontdirs;545546}547548static char **getFontConfigLocations() {549550char **fontdirs;551int numdirs = 0;552FcInitLoadConfigFuncType FcInitLoadConfig;553FcPatternBuildFuncType FcPatternBuild;554FcObjectSetFuncType FcObjectSetBuild;555FcFontListFuncType FcFontList;556FcPatternGetStringFuncType FcPatternGetString;557FcStrDirnameFuncType FcStrDirname;558FcPatternDestroyFuncType FcPatternDestroy;559FcObjectSetDestroyFuncType FcObjectSetDestroy;560FcFontSetDestroyFuncType FcFontSetDestroy;561562FcConfig *fontconfig;563FcPattern *pattern;564FcObjectSet *objset;565FcFontSet *fontSet;566FcStrList *strList;567FcChar8 *str;568int i, f, found, len=0;569char **fontPath;570571void* libfontconfig = openFontConfig();572573if (libfontconfig == NULL) {574return getFallbackFontLocations();575// original: NULL576}577578FcPatternBuild =579(FcPatternBuildFuncType)dlsym(libfontconfig, "FcPatternBuild");580FcObjectSetBuild =581(FcObjectSetFuncType)dlsym(libfontconfig, "FcObjectSetBuild");582FcFontList =583(FcFontListFuncType)dlsym(libfontconfig, "FcFontList");584FcPatternGetString =585(FcPatternGetStringFuncType)dlsym(libfontconfig, "FcPatternGetString");586FcStrDirname =587(FcStrDirnameFuncType)dlsym(libfontconfig, "FcStrDirname");588FcPatternDestroy =589(FcPatternDestroyFuncType)dlsym(libfontconfig, "FcPatternDestroy");590FcObjectSetDestroy =591(FcObjectSetDestroyFuncType)dlsym(libfontconfig, "FcObjectSetDestroy");592FcFontSetDestroy =593(FcFontSetDestroyFuncType)dlsym(libfontconfig, "FcFontSetDestroy");594595if (FcPatternBuild == NULL ||596FcObjectSetBuild == NULL ||597FcPatternGetString == NULL ||598FcFontList == NULL ||599FcStrDirname == NULL ||600FcPatternDestroy == NULL ||601FcObjectSetDestroy == NULL ||602FcFontSetDestroy == NULL) { /* problem with the library: return. */603closeFontConfig(libfontconfig, JNI_FALSE);604return NULL;605}606607/* Make calls into the fontconfig library to build a search for608* outline fonts, and to get the set of full file paths from the matches.609* This set is returned from the call to FcFontList(..)610* We allocate an array of char* pointers sufficient to hold all611* the matches + 1 extra which ensures there will be a NULL after all612* valid entries.613* We call FcStrDirname strip the file name from the path, and614* check if we have yet seen this directory. If not we add a pointer to615* it into our array of char*. Note that FcStrDirname returns newly616* allocated storage so we can use this in the return char** value.617* Finally we clean up, freeing allocated resources, and return the618* array of unique directories.619*/620pattern = (*FcPatternBuild)(NULL, FC_OUTLINE, FcTypeBool, FcTrue, NULL);621objset = (*FcObjectSetBuild)(FC_FILE, NULL);622fontSet = (*FcFontList)(NULL, pattern, objset);623if (fontSet == NULL) {624/* FcFontList() may return NULL if fonts are not installed. */625fontdirs = NULL;626} else {627fontdirs = (char**)calloc(fontSet->nfont+1, sizeof(char*));628if (fontdirs == NULL) {629(*FcFontSetDestroy)(fontSet);630goto cleanup;631}632for (f=0; f < fontSet->nfont; f++) {633FcChar8 *file;634FcChar8 *dir;635if ((*FcPatternGetString)(fontSet->fonts[f], FC_FILE, 0, &file) ==636FcResultMatch) {637dir = (*FcStrDirname)(file);638found = 0;639for (i=0;i<numdirs; i++) {640if (strcmp(fontdirs[i], (char*)dir) == 0) {641found = 1;642break;643}644}645if (!found) {646fontdirs[numdirs++] = (char*)dir;647} else {648free((char*)dir);649}650}651}652/* Free fontset if one was returned */653(*FcFontSetDestroy)(fontSet);654}655656cleanup:657/* Free memory and close the ".so" */658(*FcObjectSetDestroy)(objset);659(*FcPatternDestroy)(pattern);660closeFontConfig(libfontconfig, JNI_TRUE);661return fontdirs;662}663664/* These are copied from sun.awt.SunHints.665* Consider initialising them as ints using JNI for more robustness.666*/667#define TEXT_AA_OFF 1668#define TEXT_AA_ON 2669#define TEXT_AA_LCD_HRGB 4670#define TEXT_AA_LCD_HBGR 5671#define TEXT_AA_LCD_VRGB 6672#define TEXT_AA_LCD_VBGR 7673674JNIEXPORT jint JNICALL675Java_sun_font_FontConfigManager_getFontConfigAASettings676(JNIEnv *env, jclass obj, jstring localeStr, jstring fcNameStr) {677678FcNameParseFuncType FcNameParse;679FcPatternAddStringFuncType FcPatternAddString;680FcConfigSubstituteFuncType FcConfigSubstitute;681FcDefaultSubstituteFuncType FcDefaultSubstitute;682FcFontMatchFuncType FcFontMatch;683FcPatternGetBoolFuncType FcPatternGetBool;684FcPatternGetIntegerFuncType FcPatternGetInteger;685FcPatternDestroyFuncType FcPatternDestroy;686687FcPattern *pattern, *matchPattern;688FcResult result;689FcBool antialias = FcFalse;690int rgba = 0;691const char *locale=NULL, *fcName=NULL;692void* libfontconfig;693694if (fcNameStr == NULL || localeStr == NULL) {695return -1;696}697698fcName = (*env)->GetStringUTFChars(env, fcNameStr, 0);699if (fcName == NULL) {700return -1;701}702locale = (*env)->GetStringUTFChars(env, localeStr, 0);703704if ((libfontconfig = openFontConfig()) == NULL) {705(*env)->ReleaseStringUTFChars(env, fcNameStr, (const char*)fcName);706if (locale) {707(*env)->ReleaseStringUTFChars(env, localeStr,(const char*)locale);708}709return -1;710}711712FcNameParse = (FcNameParseFuncType)dlsym(libfontconfig, "FcNameParse");713FcPatternAddString =714(FcPatternAddStringFuncType)dlsym(libfontconfig, "FcPatternAddString");715FcConfigSubstitute =716(FcConfigSubstituteFuncType)dlsym(libfontconfig, "FcConfigSubstitute");717FcDefaultSubstitute = (FcDefaultSubstituteFuncType)718dlsym(libfontconfig, "FcDefaultSubstitute");719FcFontMatch = (FcFontMatchFuncType)dlsym(libfontconfig, "FcFontMatch");720FcPatternGetBool = (FcPatternGetBoolFuncType)721dlsym(libfontconfig, "FcPatternGetBool");722FcPatternGetInteger = (FcPatternGetIntegerFuncType)723dlsym(libfontconfig, "FcPatternGetInteger");724FcPatternDestroy =725(FcPatternDestroyFuncType)dlsym(libfontconfig, "FcPatternDestroy");726727if (FcNameParse == NULL ||728FcPatternAddString == NULL ||729FcConfigSubstitute == NULL ||730FcDefaultSubstitute == NULL ||731FcFontMatch == NULL ||732FcPatternGetBool == NULL ||733FcPatternGetInteger == NULL ||734FcPatternDestroy == NULL) { /* problem with the library: return. */735736(*env)->ReleaseStringUTFChars(env, fcNameStr, (const char*)fcName);737if (locale) {738(*env)->ReleaseStringUTFChars(env, localeStr,(const char*)locale);739}740closeFontConfig(libfontconfig, JNI_FALSE);741return -1;742}743744745pattern = (*FcNameParse)((FcChar8 *)fcName);746if (locale != NULL) {747(*FcPatternAddString)(pattern, FC_LANG, (unsigned char*)locale);748}749(*FcConfigSubstitute)(NULL, pattern, FcMatchPattern);750(*FcDefaultSubstitute)(pattern);751matchPattern = (*FcFontMatch)(NULL, pattern, &result);752/* Perhaps should call FcFontRenderPrepare() here as some pattern753* elements might change as a result of that call, but I'm not seeing754* any difference in testing.755*/756if (matchPattern) {757(*FcPatternGetBool)(matchPattern, FC_ANTIALIAS, 0, &antialias);758(*FcPatternGetInteger)(matchPattern, FC_RGBA, 0, &rgba);759(*FcPatternDestroy)(matchPattern);760}761(*FcPatternDestroy)(pattern);762763(*env)->ReleaseStringUTFChars(env, fcNameStr, (const char*)fcName);764if (locale) {765(*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);766}767closeFontConfig(libfontconfig, JNI_TRUE);768769if (antialias == FcFalse) {770return TEXT_AA_OFF;771} else if (rgba <= FC_RGBA_UNKNOWN || rgba >= FC_RGBA_NONE) {772return TEXT_AA_ON;773} else {774switch (rgba) {775case FC_RGBA_RGB : return TEXT_AA_LCD_HRGB;776case FC_RGBA_BGR : return TEXT_AA_LCD_HBGR;777case FC_RGBA_VRGB : return TEXT_AA_LCD_VRGB;778case FC_RGBA_VBGR : return TEXT_AA_LCD_VBGR;779default : return TEXT_AA_LCD_HRGB; // should not get here.780}781}782}783784JNIEXPORT jint JNICALL785Java_sun_font_FontConfigManager_getFontConfigVersion786(JNIEnv *env, jclass obj) {787788void* libfontconfig;789FcGetVersionFuncType FcGetVersion;790int version = 0;791792if ((libfontconfig = openFontConfig()) == NULL) {793return 0;794}795796FcGetVersion = (FcGetVersionFuncType)dlsym(libfontconfig, "FcGetVersion");797798if (FcGetVersion == NULL) {799closeFontConfig(libfontconfig, JNI_FALSE);800return 0;801}802version = (*FcGetVersion)();803closeFontConfig(libfontconfig, JNI_FALSE);804805return version;806}807808809JNIEXPORT void JNICALL810Java_sun_font_FontConfigManager_getFontConfig811(JNIEnv *env, jclass obj, jstring localeStr, jobject fcInfoObj,812jobjectArray fcCompFontArray, jboolean includeFallbacks) {813814FcNameParseFuncType FcNameParse;815FcPatternAddStringFuncType FcPatternAddString;816FcConfigSubstituteFuncType FcConfigSubstitute;817FcDefaultSubstituteFuncType FcDefaultSubstitute;818FcFontMatchFuncType FcFontMatch;819FcPatternGetStringFuncType FcPatternGetString;820FcPatternDestroyFuncType FcPatternDestroy;821FcPatternGetCharSetFuncType FcPatternGetCharSet;822FcFontSortFuncType FcFontSort;823FcFontSetDestroyFuncType FcFontSetDestroy;824FcCharSetUnionFuncType FcCharSetUnion;825FcCharSetDestroyFuncType FcCharSetDestroy;826FcCharSetSubtractCountFuncType FcCharSetSubtractCount;827FcGetVersionFuncType FcGetVersion;828FcConfigGetCacheDirsFuncType FcConfigGetCacheDirs;829FcStrListNextFuncType FcStrListNext;830FcStrListDoneFuncType FcStrListDone;831832int i, arrlen;833jobject fcCompFontObj;834jstring fcNameStr, jstr;835const char *locale, *fcName;836FcPattern *pattern;837FcResult result;838void* libfontconfig;839jfieldID fcNameID, fcFirstFontID, fcAllFontsID, fcVersionID, fcCacheDirsID;840jfieldID familyNameID, styleNameID, fullNameID, fontFileID;841jmethodID fcFontCons;842char* debugMinGlyphsStr = getenv("J2D_DEBUG_MIN_GLYPHS");843jclass fcInfoClass;844jclass fcCompFontClass;845jclass fcFontClass;846847CHECK_NULL(fcInfoObj);848CHECK_NULL(fcCompFontArray);849850fcInfoClass =851(*env)->FindClass(env, "sun/font/FontConfigManager$FontConfigInfo");852CHECK_NULL(fcInfoClass);853fcCompFontClass =854(*env)->FindClass(env, "sun/font/FontConfigManager$FcCompFont");855CHECK_NULL(fcCompFontClass);856fcFontClass =857(*env)->FindClass(env, "sun/font/FontConfigManager$FontConfigFont");858CHECK_NULL(fcFontClass);859860861CHECK_NULL(fcVersionID = (*env)->GetFieldID(env, fcInfoClass, "fcVersion", "I"));862CHECK_NULL(fcCacheDirsID = (*env)->GetFieldID(env, fcInfoClass, "cacheDirs",863"[Ljava/lang/String;"));864CHECK_NULL(fcNameID = (*env)->GetFieldID(env, fcCompFontClass,865"fcName", "Ljava/lang/String;"));866CHECK_NULL(fcFirstFontID = (*env)->GetFieldID(env, fcCompFontClass, "firstFont",867"Lsun/font/FontConfigManager$FontConfigFont;"));868CHECK_NULL(fcAllFontsID = (*env)->GetFieldID(env, fcCompFontClass, "allFonts",869"[Lsun/font/FontConfigManager$FontConfigFont;"));870CHECK_NULL(fcFontCons = (*env)->GetMethodID(env, fcFontClass, "<init>", "()V"));871CHECK_NULL(familyNameID = (*env)->GetFieldID(env, fcFontClass,872"familyName", "Ljava/lang/String;"));873CHECK_NULL(styleNameID = (*env)->GetFieldID(env, fcFontClass,874"styleStr", "Ljava/lang/String;"));875CHECK_NULL(fullNameID = (*env)->GetFieldID(env, fcFontClass,876"fullName", "Ljava/lang/String;"));877CHECK_NULL(fontFileID = (*env)->GetFieldID(env, fcFontClass,878"fontFile", "Ljava/lang/String;"));879880if ((libfontconfig = openFontConfig()) == NULL) {881return;882}883884FcNameParse = (FcNameParseFuncType)dlsym(libfontconfig, "FcNameParse");885FcPatternAddString =886(FcPatternAddStringFuncType)dlsym(libfontconfig, "FcPatternAddString");887FcConfigSubstitute =888(FcConfigSubstituteFuncType)dlsym(libfontconfig, "FcConfigSubstitute");889FcDefaultSubstitute = (FcDefaultSubstituteFuncType)890dlsym(libfontconfig, "FcDefaultSubstitute");891FcFontMatch = (FcFontMatchFuncType)dlsym(libfontconfig, "FcFontMatch");892FcPatternGetString =893(FcPatternGetStringFuncType)dlsym(libfontconfig, "FcPatternGetString");894FcPatternDestroy =895(FcPatternDestroyFuncType)dlsym(libfontconfig, "FcPatternDestroy");896FcPatternGetCharSet =897(FcPatternGetCharSetFuncType)dlsym(libfontconfig,898"FcPatternGetCharSet");899FcFontSort =900(FcFontSortFuncType)dlsym(libfontconfig, "FcFontSort");901FcFontSetDestroy =902(FcFontSetDestroyFuncType)dlsym(libfontconfig, "FcFontSetDestroy");903FcCharSetUnion =904(FcCharSetUnionFuncType)dlsym(libfontconfig, "FcCharSetUnion");905FcCharSetDestroy =906(FcCharSetDestroyFuncType)dlsym(libfontconfig, "FcCharSetDestroy");907FcCharSetSubtractCount =908(FcCharSetSubtractCountFuncType)dlsym(libfontconfig,909"FcCharSetSubtractCount");910FcGetVersion = (FcGetVersionFuncType)dlsym(libfontconfig, "FcGetVersion");911912if (FcNameParse == NULL ||913FcPatternAddString == NULL ||914FcConfigSubstitute == NULL ||915FcDefaultSubstitute == NULL ||916FcFontMatch == NULL ||917FcPatternGetString == NULL ||918FcPatternDestroy == NULL ||919FcPatternGetCharSet == NULL ||920FcFontSetDestroy == NULL ||921FcCharSetUnion == NULL ||922FcCharSetDestroy == NULL ||923FcGetVersion == NULL ||924FcCharSetSubtractCount == NULL) {/* problem with the library: return.*/925closeFontConfig(libfontconfig, JNI_FALSE);926return;927}928929(*env)->SetIntField(env, fcInfoObj, fcVersionID, (*FcGetVersion)());930931/* Optionally get the cache dir locations. This isn't932* available until v 2.4.x, but this is OK since on those later versions933* we can check the time stamps on the cache dirs to see if we934* are out of date. There are a couple of assumptions here. First935* that the time stamp on the directory changes when the contents are936* updated. Secondly that the locations don't change. The latter is937* most likely if a new version of fontconfig is installed, but we also938* invalidate the cache if we detect that. Arguably even that is "rare",939* and most likely is tied to an OS upgrade which gets a new file anyway.940*/941FcConfigGetCacheDirs =942(FcConfigGetCacheDirsFuncType)dlsym(libfontconfig,943"FcConfigGetCacheDirs");944FcStrListNext =945(FcStrListNextFuncType)dlsym(libfontconfig, "FcStrListNext");946FcStrListDone =947(FcStrListDoneFuncType)dlsym(libfontconfig, "FcStrListDone");948if (FcStrListNext != NULL && FcStrListDone != NULL &&949FcConfigGetCacheDirs != NULL) {950951FcStrList* cacheDirs;952FcChar8* cacheDir;953int cnt = 0;954jobject cacheDirArray =955(*env)->GetObjectField(env, fcInfoObj, fcCacheDirsID);956int max = (*env)->GetArrayLength(env, cacheDirArray);957958cacheDirs = (*FcConfigGetCacheDirs)(NULL);959if (cacheDirs != NULL) {960while ((cnt < max) && (cacheDir = (*FcStrListNext)(cacheDirs))) {961jstr = (*env)->NewStringUTF(env, (const char*)cacheDir);962if (IS_NULL(jstr)) {963(*FcStrListDone)(cacheDirs);964return;965}966(*env)->SetObjectArrayElement(env, cacheDirArray, cnt++, jstr);967(*env)->DeleteLocalRef(env, jstr);968}969(*FcStrListDone)(cacheDirs);970}971}972973locale = (*env)->GetStringUTFChars(env, localeStr, 0);974if (locale == NULL) {975(*env)->ExceptionClear(env);976JNU_ThrowOutOfMemoryError(env, "Could not create locale");977return;978}979980arrlen = (*env)->GetArrayLength(env, fcCompFontArray);981for (i=0; i<arrlen; i++) {982FcFontSet* fontset;983int fn, j, fontCount, nfonts;984unsigned int minGlyphs;985FcChar8 **family, **styleStr, **fullname, **file;986jarray fcFontArr = NULL;987FcCharSet *unionCharset = NULL;988FcCharSet *prevUnionCharset = NULL;989990fcCompFontObj = (*env)->GetObjectArrayElement(env, fcCompFontArray, i);991fcNameStr =992(jstring)((*env)->GetObjectField(env, fcCompFontObj, fcNameID));993fcName = (*env)->GetStringUTFChars(env, fcNameStr, 0);994if (fcName == NULL) {995(*env)->DeleteLocalRef(env, fcCompFontObj);996(*env)->DeleteLocalRef(env, fcNameStr);997continue;998}999pattern = (*FcNameParse)((FcChar8 *)fcName);1000(*env)->ReleaseStringUTFChars(env, fcNameStr, (const char*)fcName);1001(*env)->DeleteLocalRef(env, fcNameStr);1002if (pattern == NULL) {1003closeFontConfig(libfontconfig, JNI_FALSE);1004if (locale) {1005(*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);1006}1007return;1008}10091010/* locale may not usually be necessary as fontconfig appears to apply1011* this anyway based on the user's environment. However we want1012* to use the value of the JDK startup locale so this should take1013* care of it.1014*/1015if (locale != NULL) {1016(*FcPatternAddString)(pattern, FC_LANG, (unsigned char*)locale);1017}1018(*FcConfigSubstitute)(NULL, pattern, FcMatchPattern);1019(*FcDefaultSubstitute)(pattern);1020fontset = (*FcFontSort)(NULL, pattern, FcTrue, NULL, &result);1021if (fontset == NULL) {1022(*FcPatternDestroy)(pattern);1023closeFontConfig(libfontconfig, JNI_FALSE);1024if (locale) {1025(*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);1026}1027return;1028}10291030/* fontconfig returned us "nfonts". If we are just getting the1031* first font, we set nfont to zero. Otherwise we use "nfonts".1032* Next create separate C arrrays of length nfonts for family file etc.1033* Inspect the returned fonts and the ones we like (adds enough glyphs)1034* are added to the arrays and we increment 'fontCount'.1035*/1036nfonts = fontset->nfont;1037family = (FcChar8**)calloc(nfonts, sizeof(FcChar8*));1038styleStr = (FcChar8**)calloc(nfonts, sizeof(FcChar8*));1039fullname = (FcChar8**)calloc(nfonts, sizeof(FcChar8*));1040file = (FcChar8**)calloc(nfonts, sizeof(FcChar8*));1041if (family == NULL || styleStr == NULL ||1042fullname == NULL || file == NULL) {1043if (family != NULL) {1044free(family);1045}1046if (styleStr != NULL) {1047free(styleStr);1048}1049if (fullname != NULL) {1050free(fullname);1051}1052if (file != NULL) {1053free(file);1054}1055(*FcPatternDestroy)(pattern);1056(*FcFontSetDestroy)(fontset);1057closeFontConfig(libfontconfig, JNI_FALSE);1058if (locale) {1059(*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);1060}1061return;1062}1063fontCount = 0;1064minGlyphs = 20;1065if (debugMinGlyphsStr != NULL) {1066int val = minGlyphs;1067sscanf(debugMinGlyphsStr, "%5d", &val);1068if (val >= 0 && val <= 65536) {1069minGlyphs = val;1070}1071}10721073for (j=0; j<nfonts; j++) {1074FcPattern *fontPattern = fontset->fonts[j];1075FcChar8 *fontformat;1076FcCharSet *charset = NULL;10771078fontformat = NULL;1079(*FcPatternGetString)(fontPattern, FC_FONTFORMAT, 0, &fontformat);1080/* We only want TrueType fonts but some Linuxes still depend1081* on Type 1 fonts for some Locale support, so we'll allow1082* them there.1083*/1084if (fontformat != NULL1085&& (strcmp((char*)fontformat, "TrueType") != 0)1086#if defined(__linux__) || defined(_AIX)1087&& (strcmp((char*)fontformat, "Type 1") != 0)1088&& (strcmp((char*)fontformat, "CFF") != 0)1089#endif1090) {1091continue;1092}1093result = (*FcPatternGetCharSet)(fontPattern,1094FC_CHARSET, 0, &charset);1095if (result != FcResultMatch) {1096free(family);1097free(fullname);1098free(styleStr);1099free(file);1100(*FcPatternDestroy)(pattern);1101(*FcFontSetDestroy)(fontset);1102if (prevUnionCharset != NULL) {1103(*FcCharSetDestroy)(prevUnionCharset);1104}1105closeFontConfig(libfontconfig, JNI_FALSE);1106if (locale) {1107(*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);1108}1109return;1110}11111112/* We don't want 20 or 30 fonts, so once we hit 10 fonts,1113* then require that they really be adding value. Too many1114* adversely affects load time for minimal value-add.1115* This is still likely far more than we've had in the past.1116*/1117if (j==10) {1118minGlyphs = 50;1119}1120if (unionCharset == NULL) {1121unionCharset = charset;1122} else {1123if ((*FcCharSetSubtractCount)(charset, unionCharset)1124> minGlyphs) {1125unionCharset = (* FcCharSetUnion)(unionCharset, charset);1126if (prevUnionCharset != NULL) {1127(*FcCharSetDestroy)(prevUnionCharset);1128}1129prevUnionCharset = unionCharset;1130} else {1131continue;1132}1133}11341135fontCount++; // found a font we will use.1136(*FcPatternGetString)(fontPattern, FC_FILE, 0, &file[j]);1137(*FcPatternGetString)(fontPattern, FC_FAMILY, 0, &family[j]);1138(*FcPatternGetString)(fontPattern, FC_STYLE, 0, &styleStr[j]);1139(*FcPatternGetString)(fontPattern, FC_FULLNAME, 0, &fullname[j]);1140if (!includeFallbacks) {1141break;1142}1143if (fontCount == 254) {1144break; // CompositeFont will only use up to 254 slots from here.1145}1146}11471148// Release last instance of CharSet union1149if (prevUnionCharset != NULL) {1150(*FcCharSetDestroy)(prevUnionCharset);1151}11521153/* Once we get here 'fontCount' is the number of returned fonts1154* we actually want to use, so we create 'fcFontArr' of that length.1155* The non-null entries of "family[]" etc are those fonts.1156* Then loop again over all nfonts adding just those non-null ones1157* to 'fcFontArr'. If its null (we didn't want the font)1158* then we don't enter the main body.1159* So we should never get more than 'fontCount' entries.1160*/1161if (includeFallbacks) {1162fcFontArr =1163(*env)->NewObjectArray(env, fontCount, fcFontClass, NULL);1164if (IS_NULL(fcFontArr)) {1165free(family);1166free(fullname);1167free(styleStr);1168free(file);1169(*FcPatternDestroy)(pattern);1170(*FcFontSetDestroy)(fontset);1171closeFontConfig(libfontconfig, JNI_FALSE);1172if (locale) {1173(*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);1174}1175return;1176}1177(*env)->SetObjectField(env,fcCompFontObj, fcAllFontsID, fcFontArr);1178}1179fn=0;11801181for (j=0;j<nfonts;j++) {1182if (family[j] != NULL) {1183jobject fcFont =1184(*env)->NewObject(env, fcFontClass, fcFontCons);1185if (IS_NULL(fcFont)) break;1186jstr = (*env)->NewStringUTF(env, (const char*)family[j]);1187if (IS_NULL(jstr)) break;1188(*env)->SetObjectField(env, fcFont, familyNameID, jstr);1189(*env)->DeleteLocalRef(env, jstr);1190if (file[j] != NULL) {1191jstr = (*env)->NewStringUTF(env, (const char*)file[j]);1192if (IS_NULL(jstr)) break;1193(*env)->SetObjectField(env, fcFont, fontFileID, jstr);1194(*env)->DeleteLocalRef(env, jstr);1195}1196if (styleStr[j] != NULL) {1197jstr = (*env)->NewStringUTF(env, (const char*)styleStr[j]);1198if (IS_NULL(jstr)) break;1199(*env)->SetObjectField(env, fcFont, styleNameID, jstr);1200(*env)->DeleteLocalRef(env, jstr);1201}1202if (fullname[j] != NULL) {1203jstr = (*env)->NewStringUTF(env, (const char*)fullname[j]);1204if (IS_NULL(jstr)) break;1205(*env)->SetObjectField(env, fcFont, fullNameID, jstr);1206(*env)->DeleteLocalRef(env, jstr);1207}1208if (fn==0) {1209(*env)->SetObjectField(env, fcCompFontObj,1210fcFirstFontID, fcFont);1211}1212if (includeFallbacks) {1213(*env)->SetObjectArrayElement(env, fcFontArr, fn++,fcFont);1214} else {1215(*env)->DeleteLocalRef(env, fcFont);1216break;1217}1218(*env)->DeleteLocalRef(env, fcFont);1219}1220}1221if (includeFallbacks) {1222(*env)->DeleteLocalRef(env, fcFontArr);1223}1224(*env)->DeleteLocalRef(env, fcCompFontObj);1225(*FcFontSetDestroy)(fontset);1226(*FcPatternDestroy)(pattern);1227free(family);1228free(styleStr);1229free(fullname);1230free(file);1231}12321233/* release resources and close the ".so" */12341235if (locale) {1236(*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);1237}1238closeFontConfig(libfontconfig, JNI_TRUE);1239}124012411242