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
50JNIEXPORT 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
90typedef 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 */
106static const char FONTKEY_NT[] =
107    "Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
108
109typedef struct CheckFamilyInfo {
110  wchar_t *family;
111  wchar_t* fullName;
112  int isDifferent;
113} CheckFamilyInfo;
114
115static 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 */
138static HDC screenDC = NULL;
139
140static 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 */
172static 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 */
255static 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 */
386static 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
411static 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/* Obtain all the fontname -> filename mappings.
515 * This is called once and the results returned to Java code which can
516 * use it for lookups to reduce or avoid the need to search font files.
517 */
518JNIEXPORT void JNICALL
519Java_sun_awt_Win32FontManager_populateFontFileNameMap0
520(JNIEnv *env, jclass obj, jobject fontToFileMap,
521 jobject fontToFamilyMap, jobject familyToFontListMap, jobject locale)
522{
523#define MAX_BUFFER (FILENAME_MAX+1)
524    const wchar_t wname[MAX_BUFFER];
525    const char data[MAX_BUFFER];
526
527    DWORD type;
528    LONG ret;
529    HKEY hkeyFonts;
530    DWORD dwNameSize;
531    DWORD dwDataValueSize;
532    DWORD nval;
533    DWORD dwNumValues, dwMaxValueNameLen, dwMaxValueDataLen;
534    DWORD numValues = 0;
535    jclass classIDHashMap;
536    jclass classIDString;
537    jmethodID putMID;
538    GdiFontMapInfo fmi;
539    LOGFONTW lfw;
540
541    /* Check we were passed all the maps we need, and do lookup of
542     * methods for JNI up-calls
543     */
544    if (fontToFileMap == NULL ||
545        fontToFamilyMap == NULL ||
546        familyToFontListMap == NULL) {
547        return;
548    }
549    classIDHashMap = (*env)->FindClass(env, "java/util/HashMap");
550    if (classIDHashMap == NULL) {
551        return;
552    }
553    putMID = (*env)->GetMethodID(env, classIDHashMap, "put",
554                 "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
555    if (putMID == NULL) {
556        return;
557    }
558
559    fmi.env = env;
560    fmi.fontToFamilyMap = fontToFamilyMap;
561    fmi.familyToFontListMap = familyToFontListMap;
562    fmi.putMID = putMID;
563    fmi.locale = locale;
564    fmi.containsKeyMID = (*env)->GetMethodID(env, classIDHashMap,
565                                             "containsKey",
566                                             "(Ljava/lang/Object;)Z");
567    if (fmi.containsKeyMID == NULL) {
568        return;
569    }
570
571    fmi.arrayListClass = (*env)->FindClass(env, "java/util/ArrayList");
572    if (fmi.arrayListClass == NULL) {
573        return;
574    }
575    fmi.arrayListCtr = (*env)->GetMethodID(env, fmi.arrayListClass,
576                                              "<init>", "(I)V");
577    if (fmi.arrayListCtr == NULL) {
578        return;
579    }
580    fmi.addMID = (*env)->GetMethodID(env, fmi.arrayListClass,
581                                     "add", "(Ljava/lang/Object;)Z");
582    if (fmi.addMID == NULL) {
583        return;
584    }
585
586    classIDString = (*env)->FindClass(env, "java/lang/String");
587    if (classIDString == NULL) {
588        return;
589    }
590    fmi.toLowerCaseMID =
591        (*env)->GetMethodID(env, classIDString, "toLowerCase",
592                            "(Ljava/util/Locale;)Ljava/lang/String;");
593    if (fmi.toLowerCaseMID == NULL) {
594        return;
595    }
596
597    screenDC = GetDC(NULL);
598    if (screenDC == NULL) {
599        return;
600    }
601
602    /* Enumerate fonts via GDI to build maps of fonts and families */
603    memset(&lfw, 0, sizeof(lfw));
604    lfw.lfCharSet = DEFAULT_CHARSET;  /* all charsets */
605    wcscpy(lfw.lfFaceName, L"");      /* one face per family (CHECK) */
606    EnumFontFamiliesExW(screenDC, &lfw,
607                        (FONTENUMPROCW)EnumFamilyNamesW,
608                        (LPARAM)(&fmi), 0L);
609
610    /* Use the windows registry to map font names to files */
611    ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
612                       FONTKEY_NT, 0L, KEY_READ, &hkeyFonts);
613    if (ret != ERROR_SUCCESS) {
614        ReleaseDC(NULL, screenDC);
615        screenDC = NULL;
616        return;
617    }
618
619    ret = RegQueryInfoKeyW(hkeyFonts, NULL, NULL, NULL, NULL, NULL, NULL,
620                           &dwNumValues, &dwMaxValueNameLen,
621                           &dwMaxValueDataLen, NULL, NULL);
622
623    if (ret != ERROR_SUCCESS ||
624        dwMaxValueNameLen >= MAX_BUFFER ||
625        dwMaxValueDataLen >= MAX_BUFFER) {
626        RegCloseKey(hkeyFonts);
627        ReleaseDC(NULL, screenDC);
628        screenDC = NULL;
629        return;
630    }
631    for (nval = 0; nval < dwNumValues; nval++ ) {
632        dwNameSize = MAX_BUFFER;
633        dwDataValueSize = MAX_BUFFER;
634        ret = RegEnumValueW(hkeyFonts, nval, (LPWSTR)wname, &dwNameSize,
635                            NULL, &type, (LPBYTE)data, &dwDataValueSize);
636
637        if (ret != ERROR_SUCCESS) {
638            break;
639        }
640        if (type != REG_SZ) { /* REG_SZ means a null-terminated string */
641            continue;
642        }
643
644        if (!RegistryToBaseTTNameW((LPWSTR)wname) ) {
645            /* If the filename ends with ".ttf" or ".otf" also accept it.
646             * Not expecting to need to do this for .ttc files.
647             * Also note this code is not mirrored in the "A" (win9x) path.
648             */
649            LPWSTR dot = wcsrchr((LPWSTR)data, L'.');
650            if (dot == NULL || ((wcsicmp(dot, L".ttf") != 0)
651                                  && (wcsicmp(dot, L".otf") != 0))) {
652                continue;  /* not a TT font... */
653            }
654        }
655        registerFontW(&fmi, fontToFileMap, (LPWSTR)wname, (LPWSTR)data);
656    }
657
658    RegCloseKey(hkeyFonts);
659    ReleaseDC(NULL, screenDC);
660    screenDC = NULL;
661}
662