Path: blob/master/src/java.desktop/windows/native/libfontmanager/fontpath.c
41149 views
/*1* Copyright (c) 1998, 2016, 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#include <windows.h>26#include <stdio.h>2728#include <jni.h>29#include <jni_util.h>30#include <sun_awt_Win32FontManager.h>3132#define BSIZE (max(512, MAX_PATH+1))3334/* Typically all local references held by a JNI function are automatically35* released by JVM when the function returns. However, there is a limit to the36* number of local references that can remain active. If the local references37* continue to grow, it could result in out of memory error. Henceforth, we38* invoke DeleteLocalRef on objects that are no longer needed for execution in39* the JNI function.40*/41#define DeleteLocalReference(env, jniRef) \42do { \43if (jniRef != NULL) { \44(*env)->DeleteLocalRef(env, jniRef); \45jniRef = NULL; \46} \47} while (0)4849JNIEXPORT jstring JNICALL Java_sun_awt_Win32FontManager_getFontPath(JNIEnv *env, jobject thiz, jboolean noType1)50{51char windir[BSIZE];52char sysdir[BSIZE];53char fontpath[BSIZE*2];54char *end;5556/* Locate fonts directories relative to the Windows System directory.57* If Windows System location is different than the user's window58* directory location, as in a shared Windows installation,59* return both locations as potential font directories60*/61GetSystemDirectory(sysdir, BSIZE);62end = strrchr(sysdir,'\\');63if (end && (stricmp(end,"\\System") || stricmp(end,"\\System32"))) {64*end = 0;65strcat(sysdir, "\\Fonts");66}6768GetWindowsDirectory(windir, BSIZE);69if (strlen(windir) > BSIZE-7) {70*windir = 0;71} else {72strcat(windir, "\\Fonts");73}7475strcpy(fontpath,sysdir);76if (stricmp(sysdir,windir)) {77strcat(fontpath,";");78strcat(fontpath,windir);79}8081return JNU_NewStringPlatform(env, fontpath);82}8384/* The code below is used to obtain information from the windows font APIS85* and registry on which fonts are available and what font files hold those86* fonts. The results are used to speed font lookup.87*/8889typedef struct GdiFontMapInfo {90JNIEnv *env;91jstring family;92jobject fontToFamilyMap;93jobject familyToFontListMap;94jobject list;95jmethodID putMID;96jmethodID containsKeyMID;97jclass arrayListClass;98jmethodID arrayListCtr;99jmethodID addMID;100jmethodID toLowerCaseMID;101jobject locale;102} GdiFontMapInfo;103104/* Registry entry for fonts */105static const char FONTKEY_NT[] =106"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";107108typedef struct CheckFamilyInfo {109wchar_t *family;110wchar_t* fullName;111int isDifferent;112} CheckFamilyInfo;113114static int CALLBACK CheckFontFamilyProcW(115ENUMLOGFONTEXW *lpelfe,116NEWTEXTMETRICEX *lpntme,117int FontType,118LPARAM lParam)119{120CheckFamilyInfo *info = (CheckFamilyInfo*)lParam;121info->isDifferent = wcscmp(lpelfe->elfLogFont.lfFaceName, info->family);122123/* if (!info->isDifferent) { */124/* wprintf(LFor font %s expected family=%s instead got %s\n", */125/* lpelfe->elfFullName, */126/* info->family, */127/* lpelfe->elfLogFont.lfFaceName); */128/* fflush(stdout); */129/* } */130return 0;131}132133/* This HDC is initialised and released in the populate family map134* JNI entry point, and used within the call which would otherwise135* create many DCs.136*/137static HDC screenDC = NULL;138139static int DifferentFamily(wchar_t *family, wchar_t* fullName) {140LOGFONTW lfw;141CheckFamilyInfo info;142143/* If fullName can't be stored in the struct, assume correct family */144if (wcslen((LPWSTR)fullName) >= LF_FACESIZE) {145return 0;146}147148memset(&info, 0, sizeof(CheckFamilyInfo));149info.family = family;150info.fullName = fullName;151info.isDifferent = 0;152153memset(&lfw, 0, sizeof(lfw));154wcscpy(lfw.lfFaceName, fullName);155lfw.lfCharSet = DEFAULT_CHARSET;156EnumFontFamiliesExW(screenDC, &lfw,157(FONTENUMPROCW)CheckFontFamilyProcW,158(LPARAM)(&info), 0L);159160return info.isDifferent;161}162163/* Callback for call to EnumFontFamiliesEx in the EnumFamilyNames function.164* Expects to be called once for each face name in the family specified165* in the call. We extract the full name for the font which is expected166* to be in the "system encoding" and create canonical and lower case167* Java strings for the name which are added to the maps. The lower case168* name is used as key to the family name value in the font to family map,169* the canonical name is one of the"list" of members of the family.170*/171static int CALLBACK EnumFontFacesInFamilyProcW(172ENUMLOGFONTEXW *lpelfe,173NEWTEXTMETRICEX *lpntme,174int FontType,175LPARAM lParam)176{177GdiFontMapInfo *fmi = (GdiFontMapInfo*)lParam;178JNIEnv *env = fmi->env;179jstring fullname, fullnameLC;180181/* Exceptions indicate critical errors such that program cannot continue182* with further execution. Henceforth, the function returns immediately183* on pending exceptions. In these situations, the function also returns184* 0 indicating windows API to stop further enumeration and callbacks.185*186* The JNI functions do not clear the pending exceptions. This allows the187* caller (Java code) to check and handle exceptions in the best possible188* way.189*/190if ((*env)->ExceptionCheck(env)) {191return 0;192}193194/* Both Vista and XP return DEVICE_FONTTYPE for OTF fonts */195if (FontType != TRUETYPE_FONTTYPE && FontType != DEVICE_FONTTYPE) {196return 1;197}198199/* Windows has font aliases and so may enumerate fonts from200* the aliased family if any actual font of that family is installed.201* To protect against it ignore fonts which aren't enumerated under202* their true family.203*/204if (DifferentFamily(lpelfe->elfLogFont.lfFaceName,205lpelfe->elfFullName)) {206return 1;207}208209fullname = (*env)->NewString(env, lpelfe->elfFullName,210(jsize)wcslen((LPWSTR)lpelfe->elfFullName));211if (fullname == NULL) {212(*env)->ExceptionClear(env);213return 1;214}215216(*env)->CallBooleanMethod(env, fmi->list, fmi->addMID, fullname);217if ((*env)->ExceptionCheck(env)) {218/* Delete the created reference before return */219DeleteLocalReference(env, fullname);220return 0;221}222223fullnameLC = (*env)->CallObjectMethod(env, fullname,224fmi->toLowerCaseMID, fmi->locale);225/* Delete the created reference after its usage */226DeleteLocalReference(env, fullname);227if ((*env)->ExceptionCheck(env)) {228return 0;229}230231(*env)->CallObjectMethod(env, fmi->fontToFamilyMap,232fmi->putMID, fullnameLC, fmi->family);233/* Delete the created reference after its usage */234DeleteLocalReference(env, fullnameLC);235if ((*env)->ExceptionCheck(env)) {236return 0;237}238239return 1;240}241242/* Callback for EnumFontFamiliesEx in populateFontFileNameMap.243* Expects to be called for every charset of every font family.244* If this is the first time we have been called for this family,245* add a new mapping to the familyToFontListMap from this family to a246* list of its members. To populate that list, further enumerate all faces247* in this family for the matched charset. This assumes that all fonts248* in a family support the same charset, which is a fairly safe assumption249* and saves time as the call we make here to EnumFontFamiliesEx will250* enumerate the members of this family just once each.251* Because we set fmi->list to be the newly created list the call back252* can safely add to that list without a search.253*/254static int CALLBACK EnumFamilyNamesW(255ENUMLOGFONTEXW *lpelfe, /* pointer to logical-font data */256NEWTEXTMETRICEX *lpntme, /* pointer to physical-font data */257int FontType, /* type of font */258LPARAM lParam ) /* application-defined data */259{260GdiFontMapInfo *fmi = (GdiFontMapInfo*)lParam;261JNIEnv *env = fmi->env;262jstring familyLC;263size_t slen;264LOGFONTW lfw;265jboolean mapHasKey;266267/* Exceptions indicate critical errors such that program cannot continue268* with further execution. Henceforth, the function returns immediately269* on pending exceptions. In these situations, the function also returns270* 0 indicating windows API to stop further enumeration and callbacks.271*272* The JNI functions do not clear the pending exceptions. This allows the273* caller (Java code) to check and handle exceptions in the best possible274* way.275*/276if ((*env)->ExceptionCheck(env)) {277return 0;278}279280/* Both Vista and XP return DEVICE_FONTTYPE for OTF fonts */281if (FontType != TRUETYPE_FONTTYPE && FontType != DEVICE_FONTTYPE) {282return 1;283}284/* wprintf(L"FAMILY=%s charset=%d FULL=%s\n", */285/* lpelfe->elfLogFont.lfFaceName, */286/* lpelfe->elfLogFont.lfCharSet, */287/* lpelfe->elfFullName); */288/* fflush(stdout); */289290/* Windows lists fonts which have a vmtx (vertical metrics) table twice.291* Once using their normal name, and again preceded by '@'. These appear292* in font lists in some windows apps, such as wordpad. We don't want293* these so we skip any font where the first character is '@'294*/295if (lpelfe->elfLogFont.lfFaceName[0] == L'@') {296return 1;297}298slen = wcslen(lpelfe->elfLogFont.lfFaceName);299fmi->family = (*env)->NewString(env,lpelfe->elfLogFont.lfFaceName, (jsize)slen);300if (fmi->family == NULL) {301(*env)->ExceptionClear(env);302return 1;303}304305familyLC = (*env)->CallObjectMethod(env, fmi->family,306fmi->toLowerCaseMID, fmi->locale);307/* Delete the created reference after its usage */308if ((*env)->ExceptionCheck(env)) {309DeleteLocalReference(env, fmi->family);310return 0;311}312313/* check if already seen this family with a different charset */314mapHasKey = (*env)->CallBooleanMethod(env,315fmi->familyToFontListMap,316fmi->containsKeyMID,317familyLC);318if ((*env)->ExceptionCheck(env)) {319/* Delete the created references before return */320DeleteLocalReference(env, fmi->family);321DeleteLocalReference(env, familyLC);322return 0;323} else if (mapHasKey) {324/* Delete the created references before return */325DeleteLocalReference(env, fmi->family);326DeleteLocalReference(env, familyLC);327return 1;328}329330fmi->list = (*env)->NewObject(env,331fmi->arrayListClass, fmi->arrayListCtr, 4);332if (fmi->list == NULL) {333/* Delete the created references before return */334DeleteLocalReference(env, fmi->family);335DeleteLocalReference(env, familyLC);336return 0;337}338339(*env)->CallObjectMethod(env, fmi->familyToFontListMap,340fmi->putMID, familyLC, fmi->list);341/* Delete the created reference after its usage */342DeleteLocalReference(env, familyLC);343if ((*env)->ExceptionCheck(env)) {344/* Delete the created reference before return */345DeleteLocalReference(env, fmi->family);346DeleteLocalReference(env, fmi->list);347return 0;348}349350memset(&lfw, 0, sizeof(lfw));351wcscpy(lfw.lfFaceName, lpelfe->elfLogFont.lfFaceName);352lfw.lfCharSet = lpelfe->elfLogFont.lfCharSet;353EnumFontFamiliesExW(screenDC, &lfw,354(FONTENUMPROCW)EnumFontFacesInFamilyProcW,355lParam, 0L);356357/* Delete the created reference after its usage in the enum function */358DeleteLocalReference(env, fmi->family);359DeleteLocalReference(env, fmi->list);360return 1;361}362363/* It looks like TrueType fonts have " (TrueType)" tacked on the end of their364* name, so we can try to use that to distinguish TT from other fonts.365* However if a program "installed" a font in the registry the key may366* not include that. We could also try to "pass" fonts which have no "(..)"367* at the end. But that turns out to pass a few .FON files that MS supply.368* If there's no parenthesized type string, we could next try to infer369* the file type from the file name extension. Since the MS entries that370* have no type string are very few, and have odd names like "MS-DOS CP 437"371* and would never return a Java Font anyway its currently OK to put these372* in the font map, although clearly the returned names must never percolate373* up into a list of available fonts returned to the application.374* Additionally for TTC font files the key looks like375* Font 1 & Font 2 (TrueType)376* or sometimes even :377* Font 1 & Font 2 & Font 3 (TrueType)378* Also if a Font has a name for this locale that name also379* exists in the registry using the appropriate platform encoding.380* What do we do then?381*382* Note: OpenType fonts seems to have " (TrueType)" suffix on Vista383* but " (OpenType)" on XP.384*/385static BOOL RegistryToBaseTTNameW(LPWSTR name) {386static const wchar_t TTSUFFIX[] = L" (TrueType)";387static const wchar_t OTSUFFIX[] = L" (OpenType)";388size_t TTSLEN = wcslen(TTSUFFIX);389wchar_t *suffix;390391size_t len = wcslen(name);392if (len == 0) {393return FALSE;394}395if (name[len-1] != L')') {396return FALSE;397}398if (len <= TTSLEN) {399return FALSE;400}401/* suffix length is the same for truetype and opentype fonts */402suffix = name + (len - TTSLEN);403if (wcscmp(suffix, TTSUFFIX) == 0 || wcscmp(suffix, OTSUFFIX) == 0) {404suffix[0] = L'\0'; /* truncate name */405return TRUE;406}407return FALSE;408}409410static void registerFontW(GdiFontMapInfo *fmi, jobject fontToFileMap,411LPWSTR name, LPWSTR data) {412413wchar_t *ptr1, *ptr2;414jstring fontStr;415jstring fontStrLC;416JNIEnv *env = fmi->env;417size_t dslen = wcslen(data);418jstring fileStr = (*env)->NewString(env, data, (jsize)dslen);419if (fileStr == NULL) {420(*env)->ExceptionClear(env);421return;422}423424/* TTC or ttc means it may be a collection. Need to parse out425* multiple font face names separated by " & "426* By only doing this for fonts which look like collections based on427* file name we are adhering to MS recommendations for font file names428* so it seems that we can be sure that this identifies precisely429* the MS-supplied truetype collections.430* This avoids any potential issues if a TTF file happens to have431* a & in the font name (I can't find anything which prohibits this)432* and also means we only parse the key in cases we know to be433* worthwhile.434*/435436if ((data[dslen-1] == L'C' || data[dslen-1] == L'c') &&437(ptr1 = wcsstr(name, L" & ")) != NULL) {438ptr1+=3;439while (ptr1 >= name) { /* marginally safer than while (true) */440while ((ptr2 = wcsstr(ptr1, L" & ")) != NULL) {441ptr1 = ptr2+3;442}443fontStr = (*env)->NewString(env, ptr1, (jsize)wcslen(ptr1));444if (fontStr == NULL) {445(*env)->ExceptionClear(env);446/* Delete the created reference before return */447DeleteLocalReference(env, fileStr);448return;449}450451fontStrLC = (*env)->CallObjectMethod(env, fontStr,452fmi->toLowerCaseMID,453fmi->locale);454/* Delete the created reference after its usage */455DeleteLocalReference(env, fontStr);456if ((*env)->ExceptionCheck(env)) {457/* Delete the created reference before return */458DeleteLocalReference(env, fileStr);459return;460}461462(*env)->CallObjectMethod(env, fontToFileMap, fmi->putMID,463fontStrLC, fileStr);464/* Delete the reference after its usage */465DeleteLocalReference(env, fontStrLC);466if ((*env)->ExceptionCheck(env)) {467/* Delete the created reference before return */468DeleteLocalReference(env, fileStr);469return;470}471472if (ptr1 == name) {473break;474} else {475*(ptr1-3) = L'\0';476ptr1 = name;477}478}479} else {480fontStr = (*env)->NewString(env, name, (jsize)wcslen(name));481if (fontStr == NULL) {482(*env)->ExceptionClear(env);483/* Delete the created reference before return */484DeleteLocalReference(env, fileStr);485return;486}487488fontStrLC = (*env)->CallObjectMethod(env, fontStr,489fmi->toLowerCaseMID, fmi->locale);490/* Delete the created reference after its usage */491DeleteLocalReference(env, fontStr);492if ((*env)->ExceptionCheck(env)) {493/* Delete the created reference before return */494DeleteLocalReference(env, fileStr);495return;496}497498(*env)->CallObjectMethod(env, fontToFileMap, fmi->putMID,499fontStrLC, fileStr);500/* Delete the created reference after its usage */501DeleteLocalReference(env, fontStrLC);502if ((*env)->ExceptionCheck(env)) {503/* Delete the created reference before return */504DeleteLocalReference(env, fileStr);505return;506}507}508509/* Delete the created reference after its usage */510DeleteLocalReference(env, fileStr);511}512513static void populateFontFileNameFromRegistryKey(HKEY regKey,514GdiFontMapInfo *fmi,515jobject fontToFileMap)516{517#define MAX_BUFFER (FILENAME_MAX+1)518const wchar_t wname[MAX_BUFFER];519const char data[MAX_BUFFER];520521DWORD type;522LONG ret;523HKEY hkeyFonts;524DWORD dwNameSize;525DWORD dwDataValueSize;526DWORD nval;527DWORD dwNumValues, dwMaxValueNameLen, dwMaxValueDataLen;528529/* Use the windows registry to map font names to files */530ret = RegOpenKeyEx(regKey,531FONTKEY_NT, 0L, KEY_READ, &hkeyFonts);532if (ret != ERROR_SUCCESS) {533return;534}535536ret = RegQueryInfoKeyW(hkeyFonts, NULL, NULL, NULL, NULL, NULL, NULL,537&dwNumValues, &dwMaxValueNameLen,538&dwMaxValueDataLen, NULL, NULL);539540if (ret != ERROR_SUCCESS ||541dwMaxValueNameLen >= MAX_BUFFER ||542dwMaxValueDataLen >= MAX_BUFFER) {543RegCloseKey(hkeyFonts);544return;545}546for (nval = 0; nval < dwNumValues; nval++ ) {547dwNameSize = MAX_BUFFER;548dwDataValueSize = MAX_BUFFER;549ret = RegEnumValueW(hkeyFonts, nval, (LPWSTR)wname, &dwNameSize,550NULL, &type, (LPBYTE)data, &dwDataValueSize);551552if (ret != ERROR_SUCCESS) {553break;554}555if (type != REG_SZ) { /* REG_SZ means a null-terminated string */556continue;557}558559if (!RegistryToBaseTTNameW((LPWSTR)wname) ) {560/* If the filename ends with ".ttf" or ".otf" also accept it.561* Not expecting to need to do this for .ttc files.562* Also note this code is not mirrored in the "A" (win9x) path.563*/564LPWSTR dot = wcsrchr((LPWSTR)data, L'.');565if (dot == NULL || ((wcsicmp(dot, L".ttf") != 0)566&& (wcsicmp(dot, L".otf") != 0))) {567continue; /* not a TT font... */568}569}570registerFontW(fmi, fontToFileMap, (LPWSTR)wname, (LPWSTR)data);571}572573RegCloseKey(hkeyFonts);574}575576/* Obtain all the fontname -> filename mappings.577* This is called once and the results returned to Java code which can578* use it for lookups to reduce or avoid the need to search font files.579*/580JNIEXPORT void JNICALL581Java_sun_awt_Win32FontManager_populateFontFileNameMap0582(JNIEnv *env, jclass obj, jobject fontToFileMap,583jobject fontToFamilyMap, jobject familyToFontListMap, jobject locale)584{585jclass classIDHashMap;586jclass classIDString;587jmethodID putMID;588GdiFontMapInfo fmi;589LOGFONTW lfw;590591/* Check we were passed all the maps we need, and do lookup of592* methods for JNI up-calls593*/594if (fontToFileMap == NULL ||595fontToFamilyMap == NULL ||596familyToFontListMap == NULL) {597return;598}599classIDHashMap = (*env)->FindClass(env, "java/util/HashMap");600if (classIDHashMap == NULL) {601return;602}603putMID = (*env)->GetMethodID(env, classIDHashMap, "put",604"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");605if (putMID == NULL) {606return;607}608609fmi.env = env;610fmi.fontToFamilyMap = fontToFamilyMap;611fmi.familyToFontListMap = familyToFontListMap;612fmi.putMID = putMID;613fmi.locale = locale;614fmi.containsKeyMID = (*env)->GetMethodID(env, classIDHashMap,615"containsKey",616"(Ljava/lang/Object;)Z");617if (fmi.containsKeyMID == NULL) {618return;619}620621fmi.arrayListClass = (*env)->FindClass(env, "java/util/ArrayList");622if (fmi.arrayListClass == NULL) {623return;624}625fmi.arrayListCtr = (*env)->GetMethodID(env, fmi.arrayListClass,626"<init>", "(I)V");627if (fmi.arrayListCtr == NULL) {628return;629}630fmi.addMID = (*env)->GetMethodID(env, fmi.arrayListClass,631"add", "(Ljava/lang/Object;)Z");632if (fmi.addMID == NULL) {633return;634}635636classIDString = (*env)->FindClass(env, "java/lang/String");637if (classIDString == NULL) {638return;639}640fmi.toLowerCaseMID =641(*env)->GetMethodID(env, classIDString, "toLowerCase",642"(Ljava/util/Locale;)Ljava/lang/String;");643if (fmi.toLowerCaseMID == NULL) {644return;645}646647screenDC = GetDC(NULL);648if (screenDC == NULL) {649return;650}651652/* Enumerate fonts via GDI to build maps of fonts and families */653memset(&lfw, 0, sizeof(lfw));654lfw.lfCharSet = DEFAULT_CHARSET; /* all charsets */655wcscpy(lfw.lfFaceName, L""); /* one face per family (CHECK) */656EnumFontFamiliesExW(screenDC, &lfw,657(FONTENUMPROCW)EnumFamilyNamesW,658(LPARAM)(&fmi), 0L);659/* Starting from Windows 10 Preview Build 17704660* fonts are installed into user's home folder by default,661* and are listed in user's registry section662*/663populateFontFileNameFromRegistryKey(HKEY_CURRENT_USER, &fmi, fontToFileMap);664populateFontFileNameFromRegistryKey(HKEY_LOCAL_MACHINE, &fmi, fontToFileMap);665666ReleaseDC(NULL, screenDC);667screenDC = NULL;668}669670671