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     DeleteLocalReference(env, fmi->family);
 309     if ((*env)->ExceptionCheck(env)) {
 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, familyLC);
 321         return 0;
 322     } else if (mapHasKey) {
 323         /* Delete the created references before return */
 324         DeleteLocalReference(env, familyLC);
 325         return 1;
 326     }
 327 
 328     fmi->list = (*env)->NewObject(env,
 329                                   fmi->arrayListClass, fmi->arrayListCtr, 4);
 330     if (fmi->list == NULL) {
 331         /* Delete the created references before return */
 332         DeleteLocalReference(env, familyLC);
 333         return 0;
 334     }
 335 
 336     (*env)->CallObjectMethod(env, fmi->familyToFontListMap,
 337                              fmi->putMID, familyLC, fmi->list);
 338     /* Delete the created reference after its usage */
 339     DeleteLocalReference(env, familyLC);
 340     if ((*env)->ExceptionCheck(env)) {
 341         /* Delete the created reference before return */
 342         DeleteLocalReference(env, fmi->list);
 343         return 0;
 344     }
 345 
 346     memset(&lfw, 0, sizeof(lfw));
 347     wcscpy(lfw.lfFaceName, lpelfe->elfLogFont.lfFaceName);
 348     lfw.lfCharSet = lpelfe->elfLogFont.lfCharSet;
 349     EnumFontFamiliesExW(screenDC, &lfw,
 350                         (FONTENUMPROCW)EnumFontFacesInFamilyProcW,
 351                         lParam, 0L);
 352 
 353     /* Delete the created reference after its usage in the enum function */
 354     DeleteLocalReference(env, fmi->list);
 355     return 1;
 356 }
 357 
 358 /* It looks like TrueType fonts have " (TrueType)" tacked on the end of their
 359  * name, so we can try to use that to distinguish TT from other fonts.
 360  * However if a program "installed" a font in the registry the key may
 361  * not include that. We could also try to "pass" fonts which have no "(..)"
 362  * at the end. But that turns out to pass a few .FON files that MS supply.
 363  * If there's no parenthesized type string, we could next try to infer
 364  * the file type from the file name extension. Since the MS entries that
 365  * have no type string are very few, and have odd names like "MS-DOS CP 437"
 366  * and would never return a Java Font anyway its currently OK to put these
 367  * in the font map, although clearly the returned names must never percolate
 368  * up into a list of available fonts returned to the application.
 369  * Additionally for TTC font files the key looks like
 370  * Font 1 & Font 2 (TrueType)
 371  * or sometimes even :
 372  * Font 1 & Font 2 & Font 3 (TrueType)
 373  * Also if a Font has a name for this locale that name also
 374  * exists in the registry using the appropriate platform encoding.
 375  * What do we do then?
 376  *
 377  * Note: OpenType fonts seems to have " (TrueType)" suffix on Vista
 378  *   but " (OpenType)" on XP.
 379  */
 380 static BOOL RegistryToBaseTTNameW(LPWSTR name) {
 381     static const wchar_t TTSUFFIX[] = L" (TrueType)";
 382     static const wchar_t OTSUFFIX[] = L" (OpenType)";
 383     size_t TTSLEN = wcslen(TTSUFFIX);
 384     wchar_t *suffix;
 385 
 386     size_t len = wcslen(name);
 387     if (len == 0) {
 388         return FALSE;
 389     }
 390     if (name[len-1] != L')') {
 391         return FALSE;
 392     }
 393     if (len <= TTSLEN) {
 394         return FALSE;
 395     }
 396     /* suffix length is the same for truetype and opentype fonts */
 397     suffix = name + (len - TTSLEN);
 398     if (wcscmp(suffix, TTSUFFIX) == 0 || wcscmp(suffix, OTSUFFIX) == 0) {
 399         suffix[0] = L'\0'; /* truncate name */
 400         return TRUE;
 401     }
 402     return FALSE;
 403 }
 404 
 405 static void registerFontW(GdiFontMapInfo *fmi, jobject fontToFileMap,
 406                           LPWSTR name, LPWSTR data) {
 407 
 408     wchar_t *ptr1, *ptr2;
 409     jstring fontStr;
 410     jstring fontStrLC;
 411     JNIEnv *env = fmi->env;
 412     size_t dslen = wcslen(data);
 413     jstring fileStr = (*env)->NewString(env, data, (jsize)dslen);
 414     if (fileStr == NULL) {
 415         (*env)->ExceptionClear(env);
 416         return;
 417     }
 418 
 419     /* TTC or ttc means it may be a collection. Need to parse out
 420      * multiple font face names separated by " & "
 421      * By only doing this for fonts which look like collections based on
 422      * file name we are adhering to MS recommendations for font file names
 423      * so it seems that we can be sure that this identifies precisely
 424      * the MS-supplied truetype collections.
 425      * This avoids any potential issues if a TTF file happens to have
 426      * a & in the font name (I can't find anything which prohibits this)
 427      * and also means we only parse the key in cases we know to be
 428      * worthwhile.
 429      */
 430 
 431     if ((data[dslen-1] == L'C' || data[dslen-1] == L'c') &&
 432         (ptr1 = wcsstr(name, L" & ")) != NULL) {
 433         ptr1+=3;
 434         while (ptr1 >= name) { /* marginally safer than while (true) */
 435             while ((ptr2 = wcsstr(ptr1, L" & ")) != NULL) {
 436                 ptr1 = ptr2+3;
 437             }
 438             fontStr = (*env)->NewString(env, ptr1, (jsize)wcslen(ptr1));
 439             if (fontStr == NULL) {
 440                 (*env)->ExceptionClear(env);
 441                 /* Delete the created reference before return */
 442                 DeleteLocalReference(env, fileStr);
 443                 return;
 444             }
 445 
 446             fontStrLC = (*env)->CallObjectMethod(env, fontStr,
 447                                                  fmi->toLowerCaseMID,
 448                                                  fmi->locale);
 449             /* Delete the created reference after its usage */
 450             DeleteLocalReference(env, fontStr);
 451             if ((*env)->ExceptionCheck(env)) {
 452                 /* Delete the created reference before return */
 453                 DeleteLocalReference(env, fileStr);
 454                 return;
 455             }
 456 
 457             (*env)->CallObjectMethod(env, fontToFileMap, fmi->putMID,
 458                                      fontStrLC, fileStr);
 459             /* Delete the reference after its usage */
 460             DeleteLocalReference(env, fontStrLC);
 461             if ((*env)->ExceptionCheck(env)) {
 462                 /* Delete the created reference before return */
 463                 DeleteLocalReference(env, fileStr);
 464                 return;
 465             }
 466 
 467             if (ptr1 == name) {
 468                 break;
 469             } else {
 470                 *(ptr1-3) = L'\0';
 471                 ptr1 = name;
 472             }
 473         }
 474     } else {
 475         fontStr = (*env)->NewString(env, name, (jsize)wcslen(name));
 476         if (fontStr == NULL) {
 477             (*env)->ExceptionClear(env);
 478             /* Delete the created reference before return */
 479             DeleteLocalReference(env, fileStr);
 480             return;
 481         }
 482 
 483         fontStrLC = (*env)->CallObjectMethod(env, fontStr,
 484                                            fmi->toLowerCaseMID, fmi->locale);
 485         /* Delete the created reference after its usage */
 486         DeleteLocalReference(env, fontStr);
 487         if ((*env)->ExceptionCheck(env)) {
 488             /* Delete the created reference before return */
 489             DeleteLocalReference(env, fileStr);
 490             return;
 491         }
 492 
 493         (*env)->CallObjectMethod(env, fontToFileMap, fmi->putMID,
 494                                  fontStrLC, fileStr);
 495         /* Delete the created reference after its usage */
 496         DeleteLocalReference(env, fontStrLC);
 497         if ((*env)->ExceptionCheck(env)) {
 498             /* Delete the created reference before return */
 499             DeleteLocalReference(env, fileStr);
 500             return;
 501         }
 502     }
 503 
 504     /* Delete the created reference after its usage */
 505     DeleteLocalReference(env, fileStr);
 506 }
 507 
 508 /* Obtain all the fontname -> filename mappings.
 509  * This is called once and the results returned to Java code which can
 510  * use it for lookups to reduce or avoid the need to search font files.
 511  */
 512 JNIEXPORT void JNICALL
 513 Java_sun_awt_Win32FontManager_populateFontFileNameMap0
 514 (JNIEnv *env, jclass obj, jobject fontToFileMap,
 515  jobject fontToFamilyMap, jobject familyToFontListMap, jobject locale)
 516 {
 517 #define MAX_BUFFER (FILENAME_MAX+1)
 518     const wchar_t wname[MAX_BUFFER];
 519     const char data[MAX_BUFFER];
 520 
 521     DWORD type;
 522     LONG ret;
 523     HKEY hkeyFonts;
 524     DWORD dwNameSize;
 525     DWORD dwDataValueSize;
 526     DWORD nval;
 527     DWORD dwNumValues, dwMaxValueNameLen, dwMaxValueDataLen;
 528     DWORD numValues = 0;
 529     jclass classIDHashMap;
 530     jclass classIDString;
 531     jmethodID putMID;
 532     GdiFontMapInfo fmi;
 533 
 534     /* Check we were passed all the maps we need, and do lookup of
 535      * methods for JNI up-calls
 536      */
 537     if (fontToFileMap == NULL ||
 538         fontToFamilyMap == NULL ||
 539         familyToFontListMap == NULL) {
 540         return;
 541     }
 542     classIDHashMap = (*env)->FindClass(env, "java/util/HashMap");
 543     if (classIDHashMap == NULL) {
 544         return;
 545     }
 546     putMID = (*env)->GetMethodID(env, classIDHashMap, "put",
 547                  "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
 548     if (putMID == NULL) {
 549         return;
 550     }
 551 
 552     fmi.env = env;
 553     fmi.fontToFamilyMap = fontToFamilyMap;
 554     fmi.familyToFontListMap = familyToFontListMap;
 555     fmi.putMID = putMID;
 556     fmi.locale = locale;
 557     fmi.containsKeyMID = (*env)->GetMethodID(env, classIDHashMap,
 558                                              "containsKey",
 559                                              "(Ljava/lang/Object;)Z");
 560     if (fmi.containsKeyMID == NULL) {
 561         return;
 562     }
 563 
 564     fmi.arrayListClass = (*env)->FindClass(env, "java/util/ArrayList");
 565     if (fmi.arrayListClass == NULL) {
 566         return;
 567     }
 568     fmi.arrayListCtr = (*env)->GetMethodID(env, fmi.arrayListClass,
 569                                               "<init>", "(I)V");
 570     if (fmi.arrayListCtr == NULL) {
 571         return;
 572     }
 573     fmi.addMID = (*env)->GetMethodID(env, fmi.arrayListClass,
 574                                      "add", "(Ljava/lang/Object;)Z");
 575     if (fmi.addMID == NULL) {
 576         return;
 577     }
 578 
 579     classIDString = (*env)->FindClass(env, "java/lang/String");
 580     if (classIDString == NULL) {
 581         return;
 582     }
 583     fmi.toLowerCaseMID =
 584         (*env)->GetMethodID(env, classIDString, "toLowerCase",
 585                             "(Ljava/util/Locale;)Ljava/lang/String;");
 586     if (fmi.toLowerCaseMID == NULL) {
 587         return;
 588     }
 589 
 590     screenDC = GetDC(NULL);
 591     if (screenDC == NULL) {
 592         return;
 593     }
 594 
 595     /* Enumerate fonts via GDI to build maps of fonts and families */
 596     LOGFONTW lfw;
 597     memset(&lfw, 0, sizeof(lfw));
 598     lfw.lfCharSet = DEFAULT_CHARSET;  /* all charsets */
 599     wcscpy(lfw.lfFaceName, L"");      /* one face per family (CHECK) */
 600     EnumFontFamiliesExW(screenDC, &lfw,
 601                         (FONTENUMPROCW)EnumFamilyNamesW,
 602                         (LPARAM)(&fmi), 0L);
 603 
 604     /* Use the windows registry to map font names to files */
 605     ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
 606                        FONTKEY_NT, 0L, KEY_READ, &hkeyFonts);
 607     if (ret != ERROR_SUCCESS) {
 608         ReleaseDC(NULL, screenDC);
 609         screenDC = NULL;
 610         return;
 611     }
 612 
 613     ret = RegQueryInfoKeyW(hkeyFonts, NULL, NULL, NULL, NULL, NULL, NULL,
 614                            &dwNumValues, &dwMaxValueNameLen,
 615                            &dwMaxValueDataLen, NULL, NULL);
 616 
 617     if (ret != ERROR_SUCCESS ||
 618         dwMaxValueNameLen >= MAX_BUFFER ||
 619         dwMaxValueDataLen >= MAX_BUFFER) {
 620         RegCloseKey(hkeyFonts);
 621         ReleaseDC(NULL, screenDC);
 622         screenDC = NULL;
 623         return;
 624     }
 625     for (nval = 0; nval < dwNumValues; nval++ ) {
 626         dwNameSize = MAX_BUFFER;
 627         dwDataValueSize = MAX_BUFFER;
 628         ret = RegEnumValueW(hkeyFonts, nval, (LPWSTR)wname, &dwNameSize,
 629                             NULL, &type, (LPBYTE)data, &dwDataValueSize);
 630 
 631         if (ret != ERROR_SUCCESS) {
 632             break;
 633         }
 634         if (type != REG_SZ) { /* REG_SZ means a null-terminated string */
 635             continue;
 636         }
 637 
 638         if (!RegistryToBaseTTNameW((LPWSTR)wname) ) {
 639             /* If the filename ends with ".ttf" or ".otf" also accept it.
 640              * Not expecting to need to do this for .ttc files.
 641              * Also note this code is not mirrored in the "A" (win9x) path.
 642              */
 643             LPWSTR dot = wcsrchr((LPWSTR)data, L'.');
 644             if (dot == NULL || ((wcsicmp(dot, L".ttf") != 0)
 645                                   && (wcsicmp(dot, L".otf") != 0))) {
 646                 continue;  /* not a TT font... */
 647             }
 648         }
 649         registerFontW(&fmi, fontToFileMap, (LPWSTR)wname, (LPWSTR)data);
 650     }
 651 
 652     RegCloseKey(hkeyFonts);
 653     ReleaseDC(NULL, screenDC);
 654     screenDC = NULL;
 655 }