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 
 267     /* Exceptions indicate critical errors such that program cannot continue
 268      * with further execution. Henceforth, the function returns immediately
 269      * on pending exceptions. In these situations, the function also returns
 270      * 0 indicating windows API to stop further enumeration and callbacks.
 271      *
 272      * The JNI functions do not clear the pending exceptions. This allows the
 273      * caller (Java code) to check and handle exceptions in the best possible
 274      * way.
 275      */
 276     if ((*env)->ExceptionCheck(env)) {
 277         return 0;
 278     }
 279 
 280     /* Both Vista and XP return DEVICE_FONTTYPE for OTF fonts */
 281     if (FontType != TRUETYPE_FONTTYPE && FontType != DEVICE_FONTTYPE) {
 282         return 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); */
 289 
 290     /* Windows lists fonts which have a vmtx (vertical metrics) table twice.
 291      * Once using their normal name, and again preceded by '@'. These appear
 292      * in font lists in some windows apps, such as wordpad. We don't want
 293      * these so we skip any font where the first character is '@'
 294      */
 295     if (lpelfe->elfLogFont.lfFaceName[0] == L'@') {
 296             return 1;
 297     }
 298     slen = wcslen(lpelfe->elfLogFont.lfFaceName);
 299     fmi->family = (*env)->NewString(env,lpelfe->elfLogFont.lfFaceName, (jsize)slen);
 300     if (fmi->family == NULL) {
 301         (*env)->ExceptionClear(env);
 302         return 1;
 303     }
 304 
 305     familyLC = (*env)->CallObjectMethod(env, fmi->family,
 306                                         fmi->toLowerCaseMID, fmi->locale);
 307     /* Delete the created reference after its usage */
 308     if ((*env)->ExceptionCheck(env)) {
 309         DeleteLocalReference(env, fmi->family);
 310         return 0;
 311     }
 312 
 313     /* check if already seen this family with a different charset */
 314     jboolean mapHasKey = (*env)->CallBooleanMethod(env,
 315                                                    fmi->familyToFontListMap,
 316                                                    fmi->containsKeyMID,
 317                                                    familyLC);
 318     if ((*env)->ExceptionCheck(env)) {
 319         /* Delete the created references before return */
 320         DeleteLocalReference(env, fmi->family);
 321         DeleteLocalReference(env, familyLC);
 322         return 0;
 323     } else if (mapHasKey) {
 324         /* Delete the created references before return */
 325         DeleteLocalReference(env, fmi->family);
 326         DeleteLocalReference(env, familyLC);
 327         return 1;
 328     }
 329 
 330     fmi->list = (*env)->NewObject(env,
 331                                   fmi->arrayListClass, fmi->arrayListCtr, 4);
 332     if (fmi->list == NULL) {
 333         /* Delete the created references before return */
 334         DeleteLocalReference(env, fmi->family);
 335         DeleteLocalReference(env, familyLC);
 336         return 0;
 337     }
 338 
 339     (*env)->CallObjectMethod(env, fmi->familyToFontListMap,
 340                              fmi->putMID, familyLC, fmi->list);
 341     /* Delete the created reference after its usage */
 342     DeleteLocalReference(env, familyLC);
 343     if ((*env)->ExceptionCheck(env)) {
 344         /* Delete the created reference before return */
 345         DeleteLocalReference(env, fmi->family);
 346         DeleteLocalReference(env, fmi->list);
 347         return 0;
 348     }
 349 
 350     memset(&lfw, 0, sizeof(lfw));
 351     wcscpy(lfw.lfFaceName, lpelfe->elfLogFont.lfFaceName);
 352     lfw.lfCharSet = lpelfe->elfLogFont.lfCharSet;
 353     EnumFontFamiliesExW(screenDC, &lfw,
 354                         (FONTENUMPROCW)EnumFontFacesInFamilyProcW,
 355                         lParam, 0L);
 356 
 357     /* Delete the created reference after its usage in the enum function */
 358     DeleteLocalReference(env, fmi->family);
 359     DeleteLocalReference(env, fmi->list);
 360     return 1;
 361 }
 362 
 363 /* It looks like TrueType fonts have " (TrueType)" tacked on the end of their
 364  * 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 may
 366  * 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 infer
 369  * the file type from the file name extension. Since the MS entries that
 370  * 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 these
 372  * in the font map, although clearly the returned names must never percolate
 373  * up into a list of available fonts returned to the application.
 374  * Additionally for TTC font files the key looks like
 375  * 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 also
 379  * 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 Vista
 383  *   but " (OpenType)" on XP.
 384  */
 385 static BOOL RegistryToBaseTTNameW(LPWSTR name) {
 386     static const wchar_t TTSUFFIX[] = L" (TrueType)";
 387     static const wchar_t OTSUFFIX[] = L" (OpenType)";
 388     size_t TTSLEN = wcslen(TTSUFFIX);
 389     wchar_t *suffix;
 390 
 391     size_t len = wcslen(name);
 392     if (len == 0) {
 393         return FALSE;
 394     }
 395     if (name[len-1] != L')') {
 396         return FALSE;
 397     }
 398     if (len <= TTSLEN) {
 399         return FALSE;
 400     }
 401     /* suffix length is the same for truetype and opentype fonts */
 402     suffix = name + (len - TTSLEN);
 403     if (wcscmp(suffix, TTSUFFIX) == 0 || wcscmp(suffix, OTSUFFIX) == 0) {
 404         suffix[0] = L'\0'; /* truncate name */
 405         return TRUE;
 406     }
 407     return FALSE;
 408 }
 409 
 410 static void registerFontW(GdiFontMapInfo *fmi, jobject fontToFileMap,
 411                           LPWSTR name, LPWSTR data) {
 412 
 413     wchar_t *ptr1, *ptr2;
 414     jstring fontStr;
 415     jstring fontStrLC;
 416     JNIEnv *env = fmi->env;
 417     size_t dslen = wcslen(data);
 418     jstring fileStr = (*env)->NewString(env, data, (jsize)dslen);
 419     if (fileStr == NULL) {
 420         (*env)->ExceptionClear(env);
 421         return;
 422     }
 423 
 424     /* TTC or ttc means it may be a collection. Need to parse out
 425      * multiple font face names separated by " & "
 426      * By only doing this for fonts which look like collections based on
 427      * file name we are adhering to MS recommendations for font file names
 428      * so it seems that we can be sure that this identifies precisely
 429      * the MS-supplied truetype collections.
 430      * This avoids any potential issues if a TTF file happens to have
 431      * 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 be
 433      * worthwhile.
 434      */
 435 
 436     if ((data[dslen-1] == L'C' || data[dslen-1] == L'c') &&
 437         (ptr1 = wcsstr(name, L" & ")) != NULL) {
 438         ptr1+=3;
 439         while (ptr1 >= name) { /* marginally safer than while (true) */
 440             while ((ptr2 = wcsstr(ptr1, L" & ")) != NULL) {
 441                 ptr1 = ptr2+3;
 442             }
 443             fontStr = (*env)->NewString(env, ptr1, (jsize)wcslen(ptr1));
 444             if (fontStr == NULL) {
 445                 (*env)->ExceptionClear(env);
 446                 /* Delete the created reference before return */
 447                 DeleteLocalReference(env, fileStr);
 448                 return;
 449             }
 450 
 451             fontStrLC = (*env)->CallObjectMethod(env, fontStr,
 452                                                  fmi->toLowerCaseMID,
 453                                                  fmi->locale);
 454             /* Delete the created reference after its usage */
 455             DeleteLocalReference(env, fontStr);
 456             if ((*env)->ExceptionCheck(env)) {
 457                 /* Delete the created reference before return */
 458                 DeleteLocalReference(env, fileStr);
 459                 return;
 460             }
 461 
 462             (*env)->CallObjectMethod(env, fontToFileMap, fmi->putMID,
 463                                      fontStrLC, fileStr);
 464             /* Delete the reference after its usage */
 465             DeleteLocalReference(env, fontStrLC);
 466             if ((*env)->ExceptionCheck(env)) {
 467                 /* Delete the created reference before return */
 468                 DeleteLocalReference(env, fileStr);
 469                 return;
 470             }
 471 
 472             if (ptr1 == name) {
 473                 break;
 474             } else {
 475                 *(ptr1-3) = L'\0';
 476                 ptr1 = name;
 477             }
 478         }
 479     } else {
 480         fontStr = (*env)->NewString(env, name, (jsize)wcslen(name));
 481         if (fontStr == NULL) {
 482             (*env)->ExceptionClear(env);
 483             /* Delete the created reference before return */
 484             DeleteLocalReference(env, fileStr);
 485             return;
 486         }
 487 
 488         fontStrLC = (*env)->CallObjectMethod(env, fontStr,
 489                                            fmi->toLowerCaseMID, fmi->locale);
 490         /* Delete the created reference after its usage */
 491         DeleteLocalReference(env, fontStr);
 492         if ((*env)->ExceptionCheck(env)) {
 493             /* Delete the created reference before return */
 494             DeleteLocalReference(env, fileStr);
 495             return;
 496         }
 497 
 498         (*env)->CallObjectMethod(env, fontToFileMap, fmi->putMID,
 499                                  fontStrLC, fileStr);
 500         /* Delete the created reference after its usage */
 501         DeleteLocalReference(env, fontStrLC);
 502         if ((*env)->ExceptionCheck(env)) {
 503             /* Delete the created reference before return */
 504             DeleteLocalReference(env, fileStr);
 505             return;
 506         }
 507     }
 508 
 509     /* Delete the created reference after its usage */
 510     DeleteLocalReference(env, fileStr);
 511 }
 512 
 513 /* Obtain all the fontname -> filename mappings.
 514  * This is called once and the results returned to Java code which can
 515  * use it for lookups to reduce or avoid the need to search font files.
 516  */
 517 JNIEXPORT void JNICALL
 518 Java_sun_awt_Win32FontManager_populateFontFileNameMap0
 519 (JNIEnv *env, jclass obj, jobject fontToFileMap,
 520  jobject fontToFamilyMap, jobject familyToFontListMap, jobject locale)
 521 {
 522 #define MAX_BUFFER (FILENAME_MAX+1)
 523     const wchar_t wname[MAX_BUFFER];
 524     const char data[MAX_BUFFER];
 525 
 526     DWORD type;
 527     LONG ret;
 528     HKEY hkeyFonts;
 529     DWORD dwNameSize;
 530     DWORD dwDataValueSize;
 531     DWORD nval;
 532     DWORD dwNumValues, dwMaxValueNameLen, dwMaxValueDataLen;
 533     DWORD numValues = 0;
 534     jclass classIDHashMap;
 535     jclass classIDString;
 536     jmethodID putMID;
 537     GdiFontMapInfo fmi;
 538 
 539     /* Check we were passed all the maps we need, and do lookup of
 540      * methods for JNI up-calls
 541      */
 542     if (fontToFileMap == NULL ||
 543         fontToFamilyMap == NULL ||
 544         familyToFontListMap == NULL) {
 545         return;
 546     }
 547     classIDHashMap = (*env)->FindClass(env, "java/util/HashMap");
 548     if (classIDHashMap == NULL) {
 549         return;
 550     }
 551     putMID = (*env)->GetMethodID(env, classIDHashMap, "put",
 552                  "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
 553     if (putMID == NULL) {
 554         return;
 555     }
 556 
 557     fmi.env = env;
 558     fmi.fontToFamilyMap = fontToFamilyMap;
 559     fmi.familyToFontListMap = familyToFontListMap;
 560     fmi.putMID = putMID;
 561     fmi.locale = locale;
 562     fmi.containsKeyMID = (*env)->GetMethodID(env, classIDHashMap,
 563                                              "containsKey",
 564                                              "(Ljava/lang/Object;)Z");
 565     if (fmi.containsKeyMID == NULL) {
 566         return;
 567     }
 568 
 569     fmi.arrayListClass = (*env)->FindClass(env, "java/util/ArrayList");
 570     if (fmi.arrayListClass == NULL) {
 571         return;
 572     }
 573     fmi.arrayListCtr = (*env)->GetMethodID(env, fmi.arrayListClass,
 574                                               "<init>", "(I)V");
 575     if (fmi.arrayListCtr == NULL) {
 576         return;
 577     }
 578     fmi.addMID = (*env)->GetMethodID(env, fmi.arrayListClass,
 579                                      "add", "(Ljava/lang/Object;)Z");
 580     if (fmi.addMID == NULL) {
 581         return;
 582     }
 583 
 584     classIDString = (*env)->FindClass(env, "java/lang/String");
 585     if (classIDString == NULL) {
 586         return;
 587     }
 588     fmi.toLowerCaseMID =
 589         (*env)->GetMethodID(env, classIDString, "toLowerCase",
 590                             "(Ljava/util/Locale;)Ljava/lang/String;");
 591     if (fmi.toLowerCaseMID == NULL) {
 592         return;
 593     }
 594 
 595     screenDC = GetDC(NULL);
 596     if (screenDC == NULL) {
 597         return;
 598     }
 599 
 600     /* Enumerate fonts via GDI to build maps of fonts and families */
 601     LOGFONTW lfw;
 602     memset(&lfw, 0, sizeof(lfw));
 603     lfw.lfCharSet = DEFAULT_CHARSET;  /* all charsets */
 604     wcscpy(lfw.lfFaceName, L"");      /* one face per family (CHECK) */
 605     EnumFontFamiliesExW(screenDC, &lfw,
 606                         (FONTENUMPROCW)EnumFamilyNamesW,
 607                         (LPARAM)(&fmi), 0L);
 608 
 609     /* Use the windows registry to map font names to files */
 610     ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
 611                        FONTKEY_NT, 0L, KEY_READ, &hkeyFonts);
 612     if (ret != ERROR_SUCCESS) {
 613         ReleaseDC(NULL, screenDC);
 614         screenDC = NULL;
 615         return;
 616     }
 617 
 618     ret = RegQueryInfoKeyW(hkeyFonts, NULL, NULL, NULL, NULL, NULL, NULL,
 619                            &dwNumValues, &dwMaxValueNameLen,
 620                            &dwMaxValueDataLen, NULL, NULL);
 621 
 622     if (ret != ERROR_SUCCESS ||
 623         dwMaxValueNameLen >= MAX_BUFFER ||
 624         dwMaxValueDataLen >= MAX_BUFFER) {
 625         RegCloseKey(hkeyFonts);
 626         ReleaseDC(NULL, screenDC);
 627         screenDC = NULL;
 628         return;
 629     }
 630     for (nval = 0; nval < dwNumValues; nval++ ) {
 631         dwNameSize = MAX_BUFFER;
 632         dwDataValueSize = MAX_BUFFER;
 633         ret = RegEnumValueW(hkeyFonts, nval, (LPWSTR)wname, &dwNameSize,
 634                             NULL, &type, (LPBYTE)data, &dwDataValueSize);
 635 
 636         if (ret != ERROR_SUCCESS) {
 637             break;
 638         }
 639         if (type != REG_SZ) { /* REG_SZ means a null-terminated string */
 640             continue;
 641         }
 642 
 643         if (!RegistryToBaseTTNameW((LPWSTR)wname) ) {
 644             /* If the filename ends with ".ttf" or ".otf" also accept it.
 645              * Not expecting to need to do this for .ttc files.
 646              * Also note this code is not mirrored in the "A" (win9x) path.
 647              */
 648             LPWSTR dot = wcsrchr((LPWSTR)data, L'.');
 649             if (dot == NULL || ((wcsicmp(dot, L".ttf") != 0)
 650                                   && (wcsicmp(dot, L".otf") != 0))) {
 651                 continue;  /* not a TT font... */
 652             }
 653         }
 654         registerFontW(&fmi, fontToFileMap, (LPWSTR)wname, (LPWSTR)data);
 655     }
 656 
 657     RegCloseKey(hkeyFonts);
 658     ReleaseDC(NULL, screenDC);
 659     screenDC = NULL;
 660 }