1 /*
   2  * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include <windows.h>
  27 #include <stdio.h>
  28 
  29 #include <jni.h>
  30 #include <jni_util.h>
  31 #include <sun_awt_Win32FontManager.h>
  32 
  33 #define BSIZE (max(512, MAX_PATH+1))
  34 
  35 /* Typically all local references held by a JNI function are automatically
  36  * released by JVM when the function returns. However, there is a limit to the
  37  * number of local references that can remain active. If the local references
  38  * continue to grow, it could result in out of memory error. Henceforth, we
  39  * invoke DeleteLocalRef on objects that are no longer needed for execution in
  40  * the JNI function.
  41  */
  42 #define DeleteLocalReference(env, jniRef) \
  43     do { \
  44         if (jniRef != NULL) { \
  45             (*env)->DeleteLocalRef(env, jniRef); \
  46             jniRef = NULL; \
  47         } \
  48     } while (0)
  49 
  50 JNIEXPORT jstring JNICALL Java_sun_awt_Win32FontManager_getFontPath(JNIEnv *env, jobject thiz, jboolean noType1)
  51 {
  52     char windir[BSIZE];
  53     char sysdir[BSIZE];
  54     char fontpath[BSIZE*2];
  55     char *end;
  56 
  57     /* Locate fonts directories relative to the Windows System directory.
  58      * If Windows System location is different than the user's window
  59      * directory location, as in a shared Windows installation,
  60      * return both locations as potential font directories
  61      */
  62     GetSystemDirectory(sysdir, BSIZE);
  63     end = strrchr(sysdir,'\\');
  64     if (end && (stricmp(end,"\\System") || stricmp(end,"\\System32"))) {
  65         *end = 0;
  66          strcat(sysdir, "\\Fonts");
  67     }
  68 
  69     GetWindowsDirectory(windir, BSIZE);
  70     if (strlen(windir) > BSIZE-7) {
  71         *windir = 0;
  72     } else {
  73         strcat(windir, "\\Fonts");
  74     }
  75 
  76     strcpy(fontpath,sysdir);
  77     if (stricmp(sysdir,windir)) {
  78         strcat(fontpath,";");
  79         strcat(fontpath,windir);
  80     }
  81 
  82     return JNU_NewStringPlatform(env, fontpath);
  83 }
  84 
  85 /* The code below is used to obtain information from the windows font APIS
  86  * and registry on which fonts are available and what font files hold those
  87  * fonts. The results are used to speed font lookup.
  88  */
  89 
  90 typedef struct GdiFontMapInfo {
  91     JNIEnv *env;
  92     jstring family;
  93     jobject fontToFamilyMap;
  94     jobject familyToFontListMap;
  95     jobject list;
  96     jmethodID putMID;
  97     jmethodID containsKeyMID;
  98     jclass arrayListClass;
  99     jmethodID arrayListCtr;
 100     jmethodID addMID;
 101     jmethodID toLowerCaseMID;
 102     jobject locale;
 103 } GdiFontMapInfo;
 104 
 105 /* Registry entry for fonts */
 106 static const char FONTKEY_NT[] =
 107     "Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
 108 
 109 typedef struct CheckFamilyInfo {
 110   wchar_t *family;
 111   wchar_t* fullName;
 112   int isDifferent;
 113 } CheckFamilyInfo;
 114 
 115 static int CALLBACK CheckFontFamilyProcW(
 116   ENUMLOGFONTEXW *lpelfe,
 117   NEWTEXTMETRICEX *lpntme,
 118   int FontType,
 119   LPARAM lParam)
 120 {
 121     CheckFamilyInfo *info = (CheckFamilyInfo*)lParam;
 122     info->isDifferent = wcscmp(lpelfe->elfLogFont.lfFaceName, info->family);
 123 
 124 /*     if (!info->isDifferent) { */
 125 /*         wprintf(LFor font %s expected family=%s instead got %s\n", */
 126 /*                 lpelfe->elfFullName, */
 127 /*                 info->family, */
 128 /*                 lpelfe->elfLogFont.lfFaceName); */
 129 /*         fflush(stdout); */
 130 /*     } */
 131     return 0;
 132 }
 133 
 134 /* This HDC is initialised and released in the populate family map
 135  * JNI entry point, and used within the call which would otherwise
 136  * create many DCs.
 137  */
 138 static HDC screenDC = NULL;
 139 
 140 static int DifferentFamily(wchar_t *family, wchar_t* fullName) {
 141     LOGFONTW lfw;
 142     CheckFamilyInfo info;
 143 
 144     /* If fullName can't be stored in the struct, assume correct family */
 145     if (wcslen((LPWSTR)fullName) >= LF_FACESIZE) {
 146         return 0;
 147     }
 148 
 149     memset(&info, 0, sizeof(CheckFamilyInfo));
 150     info.family = family;
 151     info.fullName = fullName;
 152     info.isDifferent = 0;
 153 
 154     memset(&lfw, 0, sizeof(lfw));
 155     wcscpy(lfw.lfFaceName, fullName);
 156     lfw.lfCharSet = DEFAULT_CHARSET;
 157     EnumFontFamiliesExW(screenDC, &lfw,
 158                         (FONTENUMPROCW)CheckFontFamilyProcW,
 159                         (LPARAM)(&info), 0L);
 160 
 161     return info.isDifferent;
 162 }
 163 
 164 /* Callback for call to EnumFontFamiliesEx in the EnumFamilyNames function.
 165  * Expects to be called once for each face name in the family specified
 166  * in the call. We extract the full name for the font which is expected
 167  * to be in the "system encoding" and create canonical and lower case
 168  * Java strings for the name which are added to the maps. The lower case
 169  * name is used as key to the family name value in the font to family map,
 170  * the canonical name is one of the"list" of members of the family.
 171  */
 172 static int CALLBACK EnumFontFacesInFamilyProcW(
 173   ENUMLOGFONTEXW *lpelfe,
 174   NEWTEXTMETRICEX *lpntme,
 175   int FontType,
 176   LPARAM lParam)
 177 {
 178     GdiFontMapInfo *fmi = (GdiFontMapInfo*)lParam;
 179     JNIEnv *env = fmi->env;
 180     jstring fullname, fullnameLC;
 181 
 182     /* Exceptions indicate critical errors such that program cannot continue
 183      * with further execution. Henceforth, the function returns immediately
 184      * on pending exceptions. In these situations, the function also returns
 185      * 0 indicating windows API to stop further enumeration and callbacks.
 186      *
 187      * The JNI functions do not clear the pending exceptions. This allows the
 188      * caller (Java code) to check and handle exceptions in the best possible
 189      * way.
 190      */
 191     if ((*env)->ExceptionCheck(env)) {
 192         return 0;
 193     }
 194 
 195     /* Both Vista and XP return DEVICE_FONTTYPE for OTF fonts */
 196     if (FontType != TRUETYPE_FONTTYPE && FontType != DEVICE_FONTTYPE) {
 197         return 1;
 198     }
 199 
 200     /* Windows has font aliases and so may enumerate fonts from
 201      * the aliased family if any actual font of that family is installed.
 202      * To protect against it ignore fonts which aren't enumerated under
 203      * their true family.
 204      */
 205     if (DifferentFamily(lpelfe->elfLogFont.lfFaceName,
 206                         lpelfe->elfFullName))  {
 207       return 1;
 208     }
 209 
 210     fullname = (*env)->NewString(env, lpelfe->elfFullName,
 211                                  (jsize)wcslen((LPWSTR)lpelfe->elfFullName));
 212     if (fullname == NULL) {
 213         (*env)->ExceptionClear(env);
 214         return 1;
 215     }
 216 
 217     (*env)->CallBooleanMethod(env, fmi->list, fmi->addMID, fullname);
 218     if ((*env)->ExceptionCheck(env)) {
 219         /* Delete the created reference before return */
 220         DeleteLocalReference(env, fullname);
 221         return 0;
 222     }
 223 
 224     fullnameLC = (*env)->CallObjectMethod(env, fullname,
 225                                           fmi->toLowerCaseMID, fmi->locale);
 226     /* Delete the created reference after its usage */
 227     DeleteLocalReference(env, fullname);
 228     if ((*env)->ExceptionCheck(env)) {
 229         return 0;
 230     }
 231 
 232     (*env)->CallObjectMethod(env, fmi->fontToFamilyMap,
 233                              fmi->putMID, fullnameLC, fmi->family);
 234     /* Delete the created reference after its usage */
 235     DeleteLocalReference(env, fullnameLC);
 236     if ((*env)->ExceptionCheck(env)) {
 237         return 0;
 238     }
 239 
 240     return 1;
 241 }
 242 
 243 /* Callback for EnumFontFamiliesEx in populateFontFileNameMap.
 244  * Expects to be called for every charset of every font family.
 245  * If this is the first time we have been called for this family,
 246  * add a new mapping to the familyToFontListMap from this family to a
 247  * list of its members. To populate that list, further enumerate all faces
 248  * in this family for the matched charset. This assumes that all fonts
 249  * in a family support the same charset, which is a fairly safe assumption
 250  * and saves time as the call we make here to EnumFontFamiliesEx will
 251  * enumerate the members of this family just once each.
 252  * Because we set fmi->list to be the newly created list the call back
 253  * can safely add to that list without a search.
 254  */
 255 static int CALLBACK EnumFamilyNamesW(
 256   ENUMLOGFONTEXW *lpelfe,    /* pointer to logical-font data */
 257   NEWTEXTMETRICEX *lpntme,  /* pointer to physical-font data */
 258   int FontType,             /* type of font */
 259   LPARAM lParam )           /* application-defined data */
 260 {
 261     GdiFontMapInfo *fmi = (GdiFontMapInfo*)lParam;
 262     JNIEnv *env = fmi->env;
 263     jstring familyLC;
 264     size_t slen;
 265     LOGFONTW lfw;
 266     jboolean mapHasKey;
 267 
 268     /* Exceptions indicate critical errors such that program cannot continue
 269      * with further execution. Henceforth, the function returns immediately
 270      * on pending exceptions. In these situations, the function also returns
 271      * 0 indicating windows API to stop further enumeration and callbacks.
 272      *
 273      * The JNI functions do not clear the pending exceptions. This allows the
 274      * caller (Java code) to check and handle exceptions in the best possible
 275      * way.
 276      */
 277     if ((*env)->ExceptionCheck(env)) {
 278         return 0;
 279     }
 280 
 281     /* Both Vista and XP return DEVICE_FONTTYPE for OTF fonts */
 282     if (FontType != TRUETYPE_FONTTYPE && FontType != DEVICE_FONTTYPE) {
 283         return 1;
 284     }
 285 /*     wprintf(L"FAMILY=%s charset=%d FULL=%s\n", */
 286 /*          lpelfe->elfLogFont.lfFaceName, */
 287 /*          lpelfe->elfLogFont.lfCharSet, */
 288 /*          lpelfe->elfFullName); */
 289 /*     fflush(stdout); */
 290 
 291     /* Windows lists fonts which have a vmtx (vertical metrics) table twice.
 292      * Once using their normal name, and again preceded by '@'. These appear
 293      * in font lists in some windows apps, such as wordpad. We don't want
 294      * these so we skip any font where the first character is '@'
 295      */
 296     if (lpelfe->elfLogFont.lfFaceName[0] == L'@') {
 297             return 1;
 298     }
 299     slen = wcslen(lpelfe->elfLogFont.lfFaceName);
 300     fmi->family = (*env)->NewString(env,lpelfe->elfLogFont.lfFaceName, (jsize)slen);
 301     if (fmi->family == NULL) {
 302         (*env)->ExceptionClear(env);
 303         return 1;
 304     }
 305 
 306     familyLC = (*env)->CallObjectMethod(env, fmi->family,
 307                                         fmi->toLowerCaseMID, fmi->locale);
 308     /* Delete the created reference after its usage */
 309     if ((*env)->ExceptionCheck(env)) {
 310         DeleteLocalReference(env, fmi->family);
 311         return 0;
 312     }
 313 
 314     /* check if already seen this family with a different charset */
 315     mapHasKey = (*env)->CallBooleanMethod(env,
 316                                           fmi->familyToFontListMap,
 317                                           fmi->containsKeyMID,
 318                                           familyLC);
 319     if ((*env)->ExceptionCheck(env)) {
 320         /* Delete the created references before return */
 321         DeleteLocalReference(env, fmi->family);
 322         DeleteLocalReference(env, familyLC);
 323         return 0;
 324     } else if (mapHasKey) {
 325         /* Delete the created references before return */
 326         DeleteLocalReference(env, fmi->family);
 327         DeleteLocalReference(env, familyLC);
 328         return 1;
 329     }
 330 
 331     fmi->list = (*env)->NewObject(env,
 332                                   fmi->arrayListClass, fmi->arrayListCtr, 4);
 333     if (fmi->list == NULL) {
 334         /* Delete the created references before return */
 335         DeleteLocalReference(env, fmi->family);
 336         DeleteLocalReference(env, familyLC);
 337         return 0;
 338     }
 339 
 340     (*env)->CallObjectMethod(env, fmi->familyToFontListMap,
 341                              fmi->putMID, familyLC, fmi->list);
 342     /* Delete the created reference after its usage */
 343     DeleteLocalReference(env, familyLC);
 344     if ((*env)->ExceptionCheck(env)) {
 345         /* Delete the created reference before return */
 346         DeleteLocalReference(env, fmi->family);
 347         DeleteLocalReference(env, fmi->list);
 348         return 0;
 349     }
 350 
 351     memset(&lfw, 0, sizeof(lfw));
 352     wcscpy(lfw.lfFaceName, lpelfe->elfLogFont.lfFaceName);
 353     lfw.lfCharSet = lpelfe->elfLogFont.lfCharSet;
 354     EnumFontFamiliesExW(screenDC, &lfw,
 355                         (FONTENUMPROCW)EnumFontFacesInFamilyProcW,
 356                         lParam, 0L);
 357 
 358     /* Delete the created reference after its usage in the enum function */
 359     DeleteLocalReference(env, fmi->family);
 360     DeleteLocalReference(env, fmi->list);
 361     return 1;
 362 }
 363 
 364 /* It looks like TrueType fonts have " (TrueType)" tacked on the end of their
 365  * name, so we can try to use that to distinguish TT from other fonts.
 366  * However if a program "installed" a font in the registry the key may
 367  * not include that. We could also try to "pass" fonts which have no "(..)"
 368  * at the end. But that turns out to pass a few .FON files that MS supply.
 369  * If there's no parenthesized type string, we could next try to infer
 370  * the file type from the file name extension. Since the MS entries that
 371  * have no type string are very few, and have odd names like "MS-DOS CP 437"
 372  * and would never return a Java Font anyway its currently OK to put these
 373  * in the font map, although clearly the returned names must never percolate
 374  * up into a list of available fonts returned to the application.
 375  * Additionally for TTC font files the key looks like
 376  * Font 1 & Font 2 (TrueType)
 377  * or sometimes even :
 378  * Font 1 & Font 2 & Font 3 (TrueType)
 379  * Also if a Font has a name for this locale that name also
 380  * exists in the registry using the appropriate platform encoding.
 381  * What do we do then?
 382  *
 383  * Note: OpenType fonts seems to have " (TrueType)" suffix on Vista
 384  *   but " (OpenType)" on XP.
 385  */
 386 static BOOL RegistryToBaseTTNameW(LPWSTR name) {
 387     static const wchar_t TTSUFFIX[] = L" (TrueType)";
 388     static const wchar_t OTSUFFIX[] = L" (OpenType)";
 389     size_t TTSLEN = wcslen(TTSUFFIX);
 390     wchar_t *suffix;
 391 
 392     size_t len = wcslen(name);
 393     if (len == 0) {
 394         return FALSE;
 395     }
 396     if (name[len-1] != L')') {
 397         return FALSE;
 398     }
 399     if (len <= TTSLEN) {
 400         return FALSE;
 401     }
 402     /* suffix length is the same for truetype and opentype fonts */
 403     suffix = name + (len - TTSLEN);
 404     if (wcscmp(suffix, TTSUFFIX) == 0 || wcscmp(suffix, OTSUFFIX) == 0) {
 405         suffix[0] = L'\0'; /* truncate name */
 406         return TRUE;
 407     }
 408     return FALSE;
 409 }
 410 
 411 static void registerFontW(GdiFontMapInfo *fmi, jobject fontToFileMap,
 412                           LPWSTR name, LPWSTR data) {
 413 
 414     wchar_t *ptr1, *ptr2;
 415     jstring fontStr;
 416     jstring fontStrLC;
 417     JNIEnv *env = fmi->env;
 418     size_t dslen = wcslen(data);
 419     jstring fileStr = (*env)->NewString(env, data, (jsize)dslen);
 420     if (fileStr == NULL) {
 421         (*env)->ExceptionClear(env);
 422         return;
 423     }
 424 
 425     /* TTC or ttc means it may be a collection. Need to parse out
 426      * multiple font face names separated by " & "
 427      * By only doing this for fonts which look like collections based on
 428      * file name we are adhering to MS recommendations for font file names
 429      * so it seems that we can be sure that this identifies precisely
 430      * the MS-supplied truetype collections.
 431      * This avoids any potential issues if a TTF file happens to have
 432      * a & in the font name (I can't find anything which prohibits this)
 433      * and also means we only parse the key in cases we know to be
 434      * worthwhile.
 435      */
 436 
 437     if ((data[dslen-1] == L'C' || data[dslen-1] == L'c') &&
 438         (ptr1 = wcsstr(name, L" & ")) != NULL) {
 439         ptr1+=3;
 440         while (ptr1 >= name) { /* marginally safer than while (true) */
 441             while ((ptr2 = wcsstr(ptr1, L" & ")) != NULL) {
 442                 ptr1 = ptr2+3;
 443             }
 444             fontStr = (*env)->NewString(env, ptr1, (jsize)wcslen(ptr1));
 445             if (fontStr == NULL) {
 446                 (*env)->ExceptionClear(env);
 447                 /* Delete the created reference before return */
 448                 DeleteLocalReference(env, fileStr);
 449                 return;
 450             }
 451 
 452             fontStrLC = (*env)->CallObjectMethod(env, fontStr,
 453                                                  fmi->toLowerCaseMID,
 454                                                  fmi->locale);
 455             /* Delete the created reference after its usage */
 456             DeleteLocalReference(env, fontStr);
 457             if ((*env)->ExceptionCheck(env)) {
 458                 /* Delete the created reference before return */
 459                 DeleteLocalReference(env, fileStr);
 460                 return;
 461             }
 462 
 463             (*env)->CallObjectMethod(env, fontToFileMap, fmi->putMID,
 464                                      fontStrLC, fileStr);
 465             /* Delete the reference after its usage */
 466             DeleteLocalReference(env, fontStrLC);
 467             if ((*env)->ExceptionCheck(env)) {
 468                 /* Delete the created reference before return */
 469                 DeleteLocalReference(env, fileStr);
 470                 return;
 471             }
 472 
 473             if (ptr1 == name) {
 474                 break;
 475             } else {
 476                 *(ptr1-3) = L'\0';
 477                 ptr1 = name;
 478             }
 479         }
 480     } else {
 481         fontStr = (*env)->NewString(env, name, (jsize)wcslen(name));
 482         if (fontStr == NULL) {
 483             (*env)->ExceptionClear(env);
 484             /* Delete the created reference before return */
 485             DeleteLocalReference(env, fileStr);
 486             return;
 487         }
 488 
 489         fontStrLC = (*env)->CallObjectMethod(env, fontStr,
 490                                            fmi->toLowerCaseMID, fmi->locale);
 491         /* Delete the created reference after its usage */
 492         DeleteLocalReference(env, fontStr);
 493         if ((*env)->ExceptionCheck(env)) {
 494             /* Delete the created reference before return */
 495             DeleteLocalReference(env, fileStr);
 496             return;
 497         }
 498 
 499         (*env)->CallObjectMethod(env, fontToFileMap, fmi->putMID,
 500                                  fontStrLC, fileStr);
 501         /* Delete the created reference after its usage */
 502         DeleteLocalReference(env, fontStrLC);
 503         if ((*env)->ExceptionCheck(env)) {
 504             /* Delete the created reference before return */
 505             DeleteLocalReference(env, fileStr);
 506             return;
 507         }
 508     }
 509 
 510     /* Delete the created reference after its usage */
 511     DeleteLocalReference(env, fileStr);
 512 }
 513 
 514 static void populateFontFileNameFromRegistryKey(HKEY regKey, GdiFontMapInfo *fmi, jobject fontToFileMap)
 515 {
 516 #define MAX_BUFFER (FILENAME_MAX+1)
 517     const wchar_t wname[MAX_BUFFER];
 518     const char data[MAX_BUFFER];
 519 
 520     DWORD type;
 521     LONG ret;
 522     HKEY hkeyFonts;
 523     DWORD dwNameSize;
 524     DWORD dwDataValueSize;
 525     DWORD nval;
 526     DWORD dwNumValues, dwMaxValueNameLen, dwMaxValueDataLen;
 527 
 528     /* Use the windows registry to map font names to files */
 529     ret = RegOpenKeyEx(regKey,
 530                        FONTKEY_NT, 0L, KEY_READ, &hkeyFonts);
 531     if (ret != ERROR_SUCCESS) {
 532         return;
 533     }
 534 
 535     ret = RegQueryInfoKeyW(hkeyFonts, NULL, NULL, NULL, NULL, NULL, NULL,
 536                            &dwNumValues, &dwMaxValueNameLen,
 537                            &dwMaxValueDataLen, NULL, NULL);
 538 
 539     if (ret != ERROR_SUCCESS ||
 540         dwMaxValueNameLen >= MAX_BUFFER ||
 541         dwMaxValueDataLen >= MAX_BUFFER) {
 542         RegCloseKey(hkeyFonts);
 543         return;
 544     }
 545     for (nval = 0; nval < dwNumValues; nval++ ) {
 546         dwNameSize = MAX_BUFFER;
 547         dwDataValueSize = MAX_BUFFER;
 548         ret = RegEnumValueW(hkeyFonts, nval, (LPWSTR)wname, &dwNameSize,
 549                             NULL, &type, (LPBYTE)data, &dwDataValueSize);
 550 
 551         if (ret != ERROR_SUCCESS) {
 552             break;
 553         }
 554         if (type != REG_SZ) { /* REG_SZ means a null-terminated string */
 555             continue;
 556         }
 557 
 558         if (!RegistryToBaseTTNameW((LPWSTR)wname) ) {
 559             /* If the filename ends with ".ttf" or ".otf" also accept it.
 560              * Not expecting to need to do this for .ttc files.
 561              * Also note this code is not mirrored in the "A" (win9x) path.
 562              */
 563             LPWSTR dot = wcsrchr((LPWSTR)data, L'.');
 564             if (dot == NULL || ((wcsicmp(dot, L".ttf") != 0)
 565                                   && (wcsicmp(dot, L".otf") != 0))) {
 566                 continue;  /* not a TT font... */
 567             }
 568         }
 569         registerFontW(fmi, fontToFileMap, (LPWSTR)wname, (LPWSTR)data);
 570     }
 571 
 572     RegCloseKey(hkeyFonts);
 573 }
 574 
 575 /* Obtain all the fontname -> filename mappings.
 576  * This is called once and the results returned to Java code which can
 577  * use it for lookups to reduce or avoid the need to search font files.
 578  */
 579 JNIEXPORT void JNICALL
 580 Java_sun_awt_Win32FontManager_populateFontFileNameMap0
 581 (JNIEnv *env, jclass obj, jobject fontToFileMap,
 582  jobject fontToFamilyMap, jobject familyToFontListMap, jobject locale)
 583 {
 584     jclass classIDHashMap;
 585     jclass classIDString;
 586     jmethodID putMID;
 587     GdiFontMapInfo fmi;
 588     LOGFONTW lfw;
 589 
 590     /* Check we were passed all the maps we need, and do lookup of
 591      * methods for JNI up-calls
 592      */
 593     if (fontToFileMap == NULL ||
 594         fontToFamilyMap == NULL ||
 595         familyToFontListMap == NULL) {
 596         return;
 597     }
 598     classIDHashMap = (*env)->FindClass(env, "java/util/HashMap");
 599     if (classIDHashMap == NULL) {
 600         return;
 601     }
 602     putMID = (*env)->GetMethodID(env, classIDHashMap, "put",
 603                  "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
 604     if (putMID == NULL) {
 605         return;
 606     }
 607 
 608     fmi.env = env;
 609     fmi.fontToFamilyMap = fontToFamilyMap;
 610     fmi.familyToFontListMap = familyToFontListMap;
 611     fmi.putMID = putMID;
 612     fmi.locale = locale;
 613     fmi.containsKeyMID = (*env)->GetMethodID(env, classIDHashMap,
 614                                              "containsKey",
 615                                              "(Ljava/lang/Object;)Z");
 616     if (fmi.containsKeyMID == NULL) {
 617         return;
 618     }
 619 
 620     fmi.arrayListClass = (*env)->FindClass(env, "java/util/ArrayList");
 621     if (fmi.arrayListClass == NULL) {
 622         return;
 623     }
 624     fmi.arrayListCtr = (*env)->GetMethodID(env, fmi.arrayListClass,
 625                                               "<init>", "(I)V");
 626     if (fmi.arrayListCtr == NULL) {
 627         return;
 628     }
 629     fmi.addMID = (*env)->GetMethodID(env, fmi.arrayListClass,
 630                                      "add", "(Ljava/lang/Object;)Z");
 631     if (fmi.addMID == NULL) {
 632         return;
 633     }
 634 
 635     classIDString = (*env)->FindClass(env, "java/lang/String");
 636     if (classIDString == NULL) {
 637         return;
 638     }
 639     fmi.toLowerCaseMID =
 640         (*env)->GetMethodID(env, classIDString, "toLowerCase",
 641                             "(Ljava/util/Locale;)Ljava/lang/String;");
 642     if (fmi.toLowerCaseMID == NULL) {
 643         return;
 644     }
 645 
 646     screenDC = GetDC(NULL);
 647     if (screenDC == NULL) {
 648         return;
 649     }
 650 
 651     /* Enumerate fonts via GDI to build maps of fonts and families */
 652     memset(&lfw, 0, sizeof(lfw));
 653     lfw.lfCharSet = DEFAULT_CHARSET;  /* all charsets */
 654     wcscpy(lfw.lfFaceName, L"");      /* one face per family (CHECK) */
 655     EnumFontFamiliesExW(screenDC, &lfw,
 656                         (FONTENUMPROCW)EnumFamilyNamesW,
 657                         (LPARAM)(&fmi), 0L);
 658 
 659     /* Starting from Windows 10 Preview Build 17704, when a user installs non-system fonts,
 660      * then by default they are installed in a new per-user location as specified in a
 661      * per user registry entry.
 662      */
 663     populateFontFileNameFromRegistryKey(HKEY_CURRENT_USER, &fmi, fontToFileMap);
 664     populateFontFileNameFromRegistryKey(HKEY_LOCAL_MACHINE, &fmi, fontToFileMap);
 665 
 666     ReleaseDC(NULL, screenDC);
 667     screenDC = NULL;
 668 }