1 /*
   2  * Copyright (c) 1998, 2010, 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 
  36 JNIEXPORT jstring JNICALL Java_sun_awt_Win32FontManager_getFontPath(JNIEnv *env, jobject thiz, jboolean noType1)
  37 {
  38     char windir[BSIZE];
  39     char sysdir[BSIZE];
  40     char fontpath[BSIZE*2];
  41     char *end;
  42 
  43     /* Locate fonts directories relative to the Windows System directory.
  44      * If Windows System location is different than the user's window
  45      * directory location, as in a shared Windows installation,
  46      * return both locations as potential font directories
  47      */
  48     GetSystemDirectory(sysdir, BSIZE);
  49     end = strrchr(sysdir,'\\');
  50     if (end && (stricmp(end,"\\System") || stricmp(end,"\\System32"))) {
  51         *end = 0;
  52          strcat(sysdir, "\\Fonts");
  53     }
  54 
  55     GetWindowsDirectory(windir, BSIZE);
  56     if (strlen(windir) > BSIZE-7) {
  57         *windir = 0;
  58     } else {
  59         strcat(windir, "\\Fonts");
  60     }
  61 
  62     strcpy(fontpath,sysdir);
  63     if (stricmp(sysdir,windir)) {
  64         strcat(fontpath,";");
  65         strcat(fontpath,windir);
  66     }
  67 
  68     return JNU_NewStringPlatform(env, fontpath);
  69 }
  70 
  71 /* The code below is used to obtain information from the windows font APIS
  72  * and registry on which fonts are available and what font files hold those
  73  * fonts. The results are used to speed font lookup.
  74  */
  75 
  76 typedef struct GdiFontMapInfo {
  77     JNIEnv *env;
  78     jstring family;
  79     jobject fontToFamilyMap;
  80     jobject familyToFontListMap;
  81     jobject list;
  82     jmethodID putMID;
  83     jmethodID containsKeyMID;
  84     jclass arrayListClass;
  85     jmethodID arrayListCtr;
  86     jmethodID addMID;
  87     jmethodID toLowerCaseMID;
  88     jobject locale;
  89 } GdiFontMapInfo;
  90 
  91 /* IS_NT means NT or later OSes which support Unicode.
  92  * We have to painfully deal with the ASCII and non-ASCII case we
  93  * we really want to get the font names as unicode wherever possible.
  94  * UNICODE_OS is 0 to mean uninitialised, 1 to mean not a unicode OS,
  95  * 2 to mean a unicode OS.
  96  */
  97 
  98 #define UC_UNKNOWN 0
  99 #define UC_NO     1
 100 #define UC_YES    2
 101 static int UNICODE_OS = UC_UNKNOWN;
 102 static int GetOSVersion () {
 103     OSVERSIONINFO vinfo;
 104     vinfo.dwOSVersionInfoSize = sizeof(vinfo);
 105     GetVersionEx(&vinfo);
 106     if ((int)vinfo.dwMajorVersion > 4) {
 107         UNICODE_OS = UC_YES;
 108     } else if ((int)vinfo.dwMajorVersion < 4) {
 109         UNICODE_OS = UC_NO;
 110     } else {
 111         if ((int)vinfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
 112             UNICODE_OS = UC_NO;
 113         } else {
 114             UNICODE_OS = UC_YES;
 115         }
 116     }
 117     return UNICODE_OS;
 118 }
 119 
 120 #define IS_NT ((UNICODE_OS == UC_UNKNOWN) \
 121    ? (GetOSVersion() == UC_YES) : (UNICODE_OS == UC_YES))
 122 
 123 /* NT is W2K & XP. WIN is Win9x */
 124 static const char FONTKEY_NT[] =
 125     "Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
 126 static const char FONTKEY_WIN[] =
 127     "Software\\Microsoft\\Windows\\CurrentVersion\\Fonts";
 128 
 129 /* Callback for call to EnumFontFamiliesEx in the EnumFamilyNames function.
 130  * Expects to be called once for each face name in the family specified
 131  * in the call. We extract the full name for the font which is expected
 132  * to be in the "system encoding" and create canonical and lower case
 133  * Java strings for the name which are added to the maps. The lower case
 134  * name is used as key to the family name value in the font to family map,
 135  * the canonical name is one of the"list" of members of the family.
 136  */
 137 static int CALLBACK EnumFontFacesInFamilyProcA(
 138   ENUMLOGFONTEXA *lpelfe,
 139   NEWTEXTMETRICEX *lpntme,
 140   int FontType,
 141   LPARAM lParam )
 142 {
 143     GdiFontMapInfo *fmi = (GdiFontMapInfo*)lParam;
 144     JNIEnv *env = fmi->env;
 145     jstring fullname, fullnameLC;
 146 
 147     /* Both Vista and XP return DEVICE_FONTTYPE for OTF fonts */
 148     if (FontType != TRUETYPE_FONTTYPE && FontType != DEVICE_FONTTYPE) {
 149         return 1;
 150     }
 151 
 152     /* printf("FULL=%s\n",lpelfe->elfFullName);fflush(stdout);  */
 153 
 154     fullname = JNU_NewStringPlatform(env, lpelfe->elfFullName);
 155     fullnameLC = (*env)->CallObjectMethod(env, fullname,
 156                                           fmi->toLowerCaseMID, fmi->locale);
 157     (*env)->CallBooleanMethod(env, fmi->list, fmi->addMID, fullname);
 158     (*env)->CallObjectMethod(env, fmi->fontToFamilyMap,
 159                              fmi->putMID, fullnameLC, fmi->family);
 160     return 1;
 161 }
 162 
 163 typedef struct CheckFamilyInfo {
 164   wchar_t *family;
 165   wchar_t* fullName;
 166   int isDifferent;
 167 } CheckFamilyInfo;
 168 
 169 static int CALLBACK CheckFontFamilyProcW(
 170   ENUMLOGFONTEXW *lpelfe,
 171   NEWTEXTMETRICEX *lpntme,
 172   int FontType,
 173   LPARAM lParam)
 174 {
 175     CheckFamilyInfo *info = (CheckFamilyInfo*)lParam;
 176     info->isDifferent = wcscmp(lpelfe->elfLogFont.lfFaceName, info->family);
 177 
 178 /*     if (!info->isDifferent) { */
 179 /*         wprintf(LFor font %s expected family=%s instead got %s\n", */
 180 /*                 lpelfe->elfFullName, */
 181 /*                 info->family, */
 182 /*                 lpelfe->elfLogFont.lfFaceName); */
 183 /*         fflush(stdout); */
 184 /*     } */
 185     return 0;
 186 }
 187 
 188 static int DifferentFamily(wchar_t *family, wchar_t* fullName) {
 189     LOGFONTW lfw;
 190     CheckFamilyInfo info;
 191 
 192     /* If fullName can't be stored in the struct, assume correct family */
 193     if (wcslen((LPWSTR)fullName) >= LF_FACESIZE) {
 194         return 0;
 195     }
 196 
 197     memset(&info, 0, sizeof(CheckFamilyInfo));
 198     info.family = family;
 199     info.fullName = fullName;
 200     info.isDifferent = 0;
 201 
 202     memset(&lfw, 0, sizeof(lfw));
 203     wcscpy(lfw.lfFaceName, fullName);
 204     lfw.lfCharSet = DEFAULT_CHARSET;
 205     EnumFontFamiliesExW(GetDC(NULL), &lfw,
 206                         (FONTENUMPROCW)CheckFontFamilyProcW,
 207                         (LPARAM)(&info), 0L);
 208 
 209     return info.isDifferent;
 210 }
 211 
 212 static int CALLBACK EnumFontFacesInFamilyProcW(
 213   ENUMLOGFONTEXW *lpelfe,
 214   NEWTEXTMETRICEX *lpntme,
 215   int FontType,
 216   LPARAM lParam)
 217 {
 218     GdiFontMapInfo *fmi = (GdiFontMapInfo*)lParam;
 219     JNIEnv *env = fmi->env;
 220     jstring fullname, fullnameLC;
 221 
 222     /* Both Vista and XP return DEVICE_FONTTYPE for OTF fonts */
 223     if (FontType != TRUETYPE_FONTTYPE && FontType != DEVICE_FONTTYPE) {
 224         return 1;
 225     }
 226 
 227     /* Windows has font aliases and so may enumerate fonts from
 228      * the aliased family if any actual font of that family is installed.
 229      * To protect against it ignore fonts which aren't enumerated under
 230      * their true family.
 231      */
 232     if (DifferentFamily(lpelfe->elfLogFont.lfFaceName,
 233                         lpelfe->elfFullName))  {
 234       return 1;
 235     }
 236 
 237     fullname = (*env)->NewString(env, lpelfe->elfFullName,
 238                                  wcslen((LPWSTR)lpelfe->elfFullName));
 239     fullnameLC = (*env)->CallObjectMethod(env, fullname,
 240                                           fmi->toLowerCaseMID, fmi->locale);
 241     (*env)->CallBooleanMethod(env, fmi->list, fmi->addMID, fullname);
 242     (*env)->CallObjectMethod(env, fmi->fontToFamilyMap,
 243                              fmi->putMID, fullnameLC, fmi->family);
 244     return 1;
 245 }
 246 
 247 /* Callback for EnumFontFamiliesEx in populateFontFileNameMap.
 248  * Expects to be called for every charset of every font family.
 249  * If this is the first time we have been called for this family,
 250  * add a new mapping to the familyToFontListMap from this family to a
 251  * list of its members. To populate that list, further enumerate all faces
 252  * in this family for the matched charset. This assumes that all fonts
 253  * in a family support the same charset, which is a fairly safe assumption
 254  * and saves time as the call we make here to EnumFontFamiliesEx will
 255  * enumerate the members of this family just once each.
 256  * Because we set fmi->list to be the newly created list the call back
 257  * can safely add to that list without a search.
 258  */
 259 static int CALLBACK EnumFamilyNamesA(
 260   ENUMLOGFONTEXA *lpelfe,    /* pointer to logical-font data */
 261   NEWTEXTMETRICEX *lpntme,   /* pointer to physical-font data */
 262   int FontType,              /* type of font */
 263   LPARAM lParam)             /* application-defined data */
 264 {
 265     GdiFontMapInfo *fmi = (GdiFontMapInfo*)lParam;
 266     JNIEnv *env = fmi->env;
 267     jstring familyLC;
 268     LOGFONTA lfa;
 269 
 270     /* Both Vista and XP return DEVICE_FONTTYPE for OTF fonts */
 271     if (FontType != TRUETYPE_FONTTYPE && FontType != DEVICE_FONTTYPE) {
 272         return 1;
 273     }
 274 
 275     /* Windows lists fonts which have a vmtx (vertical metrics) table twice.
 276      * Once using their normal name, and again preceded by '@'. These appear
 277      * in font lists in some windows apps, such as wordpad. We don't want
 278      * these so we skip any font where the first character is '@'
 279      */
 280     if (lpelfe->elfLogFont.lfFaceName[0] == '@') {
 281         return 1;
 282     }
 283     fmi->family = JNU_NewStringPlatform(env,lpelfe->elfLogFont.lfFaceName);
 284     familyLC = (*env)->CallObjectMethod(env, fmi->family,
 285                                         fmi->toLowerCaseMID, fmi->locale);
 286     /* check if already seen this family with a different charset */
 287     if ((*env)->CallBooleanMethod(env,fmi->familyToFontListMap,
 288                                   fmi->containsKeyMID, familyLC)) {
 289         return 1;
 290     }
 291     fmi->list = (*env)->NewObject(env,
 292                                   fmi->arrayListClass, fmi->arrayListCtr, 4);
 293 
 294     (*env)->CallObjectMethod(env, fmi->familyToFontListMap,
 295                              fmi->putMID, familyLC, fmi->list);
 296 
 297 /*  printf("FAMILY=%s\n", lpelfe->elfLogFont.lfFaceName);fflush(stdout); */
 298 
 299     memset(&lfa, 0, sizeof(lfa));
 300     strcpy(lfa.lfFaceName, lpelfe->elfLogFont.lfFaceName);
 301     lfa.lfCharSet = lpelfe->elfLogFont.lfCharSet;
 302     EnumFontFamiliesExA(GetDC(NULL), &lfa,
 303                         (FONTENUMPROCA)EnumFontFacesInFamilyProcA,
 304                         lParam, 0L);
 305     return 1;
 306 }
 307 
 308 static int CALLBACK EnumFamilyNamesW(
 309   ENUMLOGFONTEXW *lpelfe,    /* pointer to logical-font data */
 310   NEWTEXTMETRICEX *lpntme,  /* pointer to physical-font data */
 311   int FontType,             /* type of font */
 312   LPARAM lParam )           /* application-defined data */
 313 {
 314     GdiFontMapInfo *fmi = (GdiFontMapInfo*)lParam;
 315     JNIEnv *env = fmi->env;
 316     jstring familyLC;
 317     int slen;
 318     LOGFONTW lfw;
 319 
 320     /* Both Vista and XP return DEVICE_FONTTYPE for OTF fonts */
 321     if (FontType != TRUETYPE_FONTTYPE && FontType != DEVICE_FONTTYPE) {
 322         return 1;
 323     }
 324 /*     wprintf(L"FAMILY=%s charset=%d FULL=%s\n", */
 325 /*          lpelfe->elfLogFont.lfFaceName, */
 326 /*          lpelfe->elfLogFont.lfCharSet, */
 327 /*          lpelfe->elfFullName); */
 328 /*     fflush(stdout); */
 329 
 330     /* Windows lists fonts which have a vmtx (vertical metrics) table twice.
 331      * Once using their normal name, and again preceded by '@'. These appear
 332      * in font lists in some windows apps, such as wordpad. We don't want
 333      * these so we skip any font where the first character is '@'
 334      */
 335     if (lpelfe->elfLogFont.lfFaceName[0] == L'@') {
 336             return 1;
 337     }
 338     slen = wcslen(lpelfe->elfLogFont.lfFaceName);
 339     fmi->family = (*env)->NewString(env,lpelfe->elfLogFont.lfFaceName, slen);
 340     familyLC = (*env)->CallObjectMethod(env, fmi->family,
 341                                         fmi->toLowerCaseMID, fmi->locale);
 342     /* check if already seen this family with a different charset */
 343     if ((*env)->CallBooleanMethod(env,fmi->familyToFontListMap,
 344                                   fmi->containsKeyMID, familyLC)) {
 345         return 1;
 346     }
 347     fmi->list = (*env)->NewObject(env,
 348                                   fmi->arrayListClass, fmi->arrayListCtr, 4);
 349 
 350     (*env)->CallObjectMethod(env, fmi->familyToFontListMap,
 351                              fmi->putMID, familyLC, fmi->list);
 352 
 353     memset(&lfw, 0, sizeof(lfw));
 354     wcscpy(lfw.lfFaceName, lpelfe->elfLogFont.lfFaceName);
 355     lfw.lfCharSet = lpelfe->elfLogFont.lfCharSet;
 356     EnumFontFamiliesExW(GetDC(NULL), &lfw,
 357                         (FONTENUMPROCW)EnumFontFacesInFamilyProcW,
 358                         lParam, 0L);
 359     return 1;
 360 }
 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 parenthesised 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 
 386 static BOOL RegistryToBaseTTNameA(LPSTR name) {
 387     static const char TTSUFFIX[] = " (TrueType)";
 388     static const char OTSUFFIX[] = " (OpenType)";
 389     int TTSLEN = strlen(TTSUFFIX);
 390     char *suffix;
 391 
 392     int len = strlen(name);
 393     if (len == 0) {
 394         return FALSE;
 395     }
 396     if (name[len-1] != ')') {
 397         return FALSE;
 398     }
 399     if (len <= TTSLEN) {
 400         return FALSE;
 401     }
 402 
 403     /* suffix length is the same for truetype and opentype fonts */
 404     suffix = name + len - TTSLEN;
 405     if (strcmp(suffix, TTSUFFIX) == 0 || strcmp(suffix, OTSUFFIX) == 0) {
 406         suffix[0] = '\0'; /* truncate name */
 407         return TRUE;
 408     }
 409     return FALSE;
 410 }
 411 
 412 static BOOL RegistryToBaseTTNameW(LPWSTR name) {
 413     static const wchar_t TTSUFFIX[] = L" (TrueType)";
 414     static const wchar_t OTSUFFIX[] = L" (OpenType)";
 415     int TTSLEN = wcslen(TTSUFFIX);
 416     wchar_t *suffix;
 417 
 418     int len = wcslen(name);
 419     if (len == 0) {
 420         return FALSE;
 421     }
 422     if (name[len-1] != L')') {
 423         return FALSE;
 424     }
 425     if (len <= TTSLEN) {
 426         return FALSE;
 427     }
 428     /* suffix length is the same for truetype and opentype fonts */
 429     suffix = name + (len - TTSLEN);
 430     if (wcscmp(suffix, TTSUFFIX) == 0 || wcscmp(suffix, OTSUFFIX) == 0) {
 431         suffix[0] = L'\0'; /* truncate name */
 432         return TRUE;
 433     }
 434     return FALSE;
 435 }
 436 
 437 static void registerFontA(GdiFontMapInfo *fmi, jobject fontToFileMap,
 438                           LPCSTR name, LPCSTR data) {
 439     LPSTR ptr1, ptr2;
 440     jstring fontStr;
 441     JNIEnv *env = fmi->env;
 442     int dslen = strlen(data);
 443     jstring fileStr = JNU_NewStringPlatform(env, data);
 444 
 445     /* TTC or ttc means it may be a collection. Need to parse out
 446      * multiple font face names separated by " & "
 447      * By only doing this for fonts which look like collections based on
 448      * file name we are adhering to MS recommendations for font file names
 449      * so it seems that we can be sure that this identifies precisely
 450      * the MS-supplied truetype collections.
 451      * This avoids any potential issues if a TTF file happens to have
 452      * a & in the font name (I can't find anything which prohibits this)
 453      * and also means we only parse the key in cases we know to be
 454      * worthwhile.
 455      */
 456     if ((data[dslen-1] == 'C' || data[dslen-1] == 'c') &&
 457         (ptr1 = strstr(name, " & ")) != NULL) {
 458         ptr1+=3;
 459         while (ptr1 >= name) { /* marginally safer than while (true) */
 460             while ((ptr2 = strstr(ptr1, " & ")) != NULL) {
 461                     ptr1 = ptr2+3;
 462             }
 463             fontStr = JNU_NewStringPlatform(env, ptr1);
 464             fontStr = (*env)->CallObjectMethod(env, fontStr,
 465                                                fmi->toLowerCaseMID,
 466                                                fmi->locale);
 467             (*env)->CallObjectMethod(env, fontToFileMap, fmi->putMID,
 468                                      fontStr, fileStr);
 469             if (ptr1 == name) {
 470                 break;
 471             } else {
 472                 *(ptr1-3) ='\0';
 473                 ptr1 = (LPSTR)name;
 474             }
 475         }
 476     } else {
 477         fontStr = JNU_NewStringPlatform(env, name);
 478         fontStr = (*env)->CallObjectMethod(env, fontStr,
 479                                            fmi->toLowerCaseMID, fmi->locale);
 480         (*env)->CallObjectMethod(env, fontToFileMap, fmi->putMID,
 481                                  fontStr, fileStr);
 482     }
 483 }
 484 
 485 static void registerFontW(GdiFontMapInfo *fmi, jobject fontToFileMap,
 486                           LPWSTR name, LPWSTR data) {
 487 
 488     wchar_t *ptr1, *ptr2;
 489     jstring fontStr;
 490     JNIEnv *env = fmi->env;
 491     int dslen = wcslen(data);
 492     jstring fileStr = (*env)->NewString(env, data, dslen);
 493 
 494     /* TTC or ttc means it may be a collection. Need to parse out
 495      * multiple font face names separated by " & "
 496      * By only doing this for fonts which look like collections based on
 497      * file name we are adhering to MS recommendations for font file names
 498      * so it seems that we can be sure that this identifies precisely
 499      * the MS-supplied truetype collections.
 500      * This avoids any potential issues if a TTF file happens to have
 501      * a & in the font name (I can't find anything which prohibits this)
 502      * and also means we only parse the key in cases we know to be
 503      * worthwhile.
 504      */
 505 
 506     if ((data[dslen-1] == L'C' || data[dslen-1] == L'c') &&
 507         (ptr1 = wcsstr(name, L" & ")) != NULL) {
 508         ptr1+=3;
 509         while (ptr1 >= name) { /* marginally safer than while (true) */
 510             while ((ptr2 = wcsstr(ptr1, L" & ")) != NULL) {
 511                 ptr1 = ptr2+3;
 512             }
 513             fontStr = (*env)->NewString(env, ptr1, wcslen(ptr1));
 514             fontStr = (*env)->CallObjectMethod(env, fontStr,
 515                                                fmi->toLowerCaseMID,
 516                                                fmi->locale);
 517             (*env)->CallObjectMethod(env, fontToFileMap, fmi->putMID,
 518                                      fontStr, fileStr);
 519             if (ptr1 == name) {
 520                 break;
 521             } else {
 522                 *(ptr1-3) = L'\0';
 523                 ptr1 = name;
 524             }
 525         }
 526     } else {
 527         fontStr = (*env)->NewString(env, name, wcslen(name));
 528         fontStr = (*env)->CallObjectMethod(env, fontStr,
 529                                            fmi->toLowerCaseMID, fmi->locale);
 530         (*env)->CallObjectMethod(env, fontToFileMap, fmi->putMID,
 531                                  fontStr, fileStr);
 532     }
 533 }
 534 
 535 /* Obtain all the fontname -> filename mappings.
 536  * This is called once and the results returned to Java code which can
 537  * use it for lookups to reduce or avoid the need to search font files.
 538  */
 539 JNIEXPORT void JNICALL
 540 Java_sun_awt_Win32FontManager_populateFontFileNameMap0
 541 (JNIEnv *env, jclass obj, jobject fontToFileMap,
 542  jobject fontToFamilyMap, jobject familyToFontListMap, jobject locale)
 543 {
 544 #define MAX_BUFFER (FILENAME_MAX+1)
 545     const wchar_t wname[MAX_BUFFER];
 546     const char cname[MAX_BUFFER];
 547     const char data[MAX_BUFFER];
 548 
 549     DWORD type;
 550     LONG ret;
 551     HKEY hkeyFonts;
 552     DWORD dwNameSize;
 553     DWORD dwDataValueSize;
 554     DWORD nval;
 555     LPCSTR fontKeyName;
 556     DWORD dwNumValues, dwMaxValueNameLen, dwMaxValueDataLen;
 557     DWORD numValues = 0;
 558     jclass classID;
 559     jmethodID putMID;
 560     GdiFontMapInfo fmi;
 561 
 562     /* Check we were passed all the maps we need, and do lookup of
 563      * methods for JNI up-calls
 564      */
 565     if (fontToFileMap == NULL ||
 566         fontToFamilyMap == NULL ||
 567         familyToFontListMap == NULL) {
 568         return;
 569     }
 570     classID = (*env)->FindClass(env, "java/util/HashMap");
 571     if (classID == NULL) {
 572         return;
 573     }
 574     putMID = (*env)->GetMethodID(env, classID, "put",
 575                  "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
 576     if (putMID == NULL) {
 577         return;
 578     }
 579 
 580     fmi.env = env;
 581     fmi.fontToFamilyMap = fontToFamilyMap;
 582     fmi.familyToFontListMap = familyToFontListMap;
 583     fmi.putMID = putMID;
 584     fmi.locale = locale;
 585     fmi.containsKeyMID = (*env)->GetMethodID(env, classID, "containsKey",
 586                                              "(Ljava/lang/Object;)Z");
 587     if (fmi.containsKeyMID == NULL) {
 588         return;
 589     }
 590 
 591     fmi.arrayListClass = (*env)->FindClass(env, "java/util/ArrayList");
 592     if (fmi.arrayListClass == NULL) {
 593         return;
 594     }
 595     fmi.arrayListCtr = (*env)->GetMethodID(env, fmi.arrayListClass,
 596                                               "<init>", "(I)V");
 597     if (fmi.arrayListCtr == NULL) {
 598         return;
 599     }
 600     fmi.addMID = (*env)->GetMethodID(env, fmi.arrayListClass,
 601                                      "add", "(Ljava/lang/Object;)Z");
 602     if (fmi.addMID == NULL) {
 603         return;
 604     }
 605     classID = (*env)->FindClass(env, "java/lang/String");
 606     if (classID == NULL) {
 607         return;
 608     }
 609     fmi.toLowerCaseMID =
 610         (*env)->GetMethodID(env, classID, "toLowerCase",
 611                             "(Ljava/util/Locale;)Ljava/lang/String;");
 612     if (fmi.toLowerCaseMID == NULL) {
 613         return;
 614     }
 615 
 616     /* Enumerate fonts via GDI to build maps of fonts and families */
 617     if (IS_NT) {
 618         LOGFONTW lfw;
 619         memset(&lfw, 0, sizeof(lfw));
 620         lfw.lfCharSet = DEFAULT_CHARSET;  /* all charsets */
 621         wcscpy(lfw.lfFaceName, L"");      /* one face per family (CHECK) */
 622         EnumFontFamiliesExW(GetDC(NULL), &lfw,
 623                             (FONTENUMPROCW)EnumFamilyNamesW,
 624                             (LPARAM)(&fmi), 0L);
 625     } else {
 626         LOGFONT lfa;
 627         memset(&lfa, 0, sizeof(lfa));
 628         lfa.lfCharSet = DEFAULT_CHARSET; /* all charsets */
 629         strcpy(lfa.lfFaceName, "");      /* one face per family */
 630         ret = EnumFontFamiliesExA(GetDC(NULL), &lfa,
 631                             (FONTENUMPROCA)EnumFamilyNamesA,
 632                             (LPARAM)(&fmi), 0L);
 633     }
 634 
 635     /* Use the windows registry to map font names to files */
 636     fontKeyName = (IS_NT) ? FONTKEY_NT : FONTKEY_WIN;
 637     ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
 638                        fontKeyName, 0L, KEY_READ, &hkeyFonts);
 639     if (ret != ERROR_SUCCESS) {
 640         return;
 641     }
 642 
 643     if (IS_NT) {
 644         ret = RegQueryInfoKeyW(hkeyFonts, NULL, NULL, NULL, NULL, NULL, NULL,
 645                                &dwNumValues, &dwMaxValueNameLen,
 646                                &dwMaxValueDataLen, NULL, NULL);
 647     } else {
 648         ret = RegQueryInfoKeyA(hkeyFonts, NULL, NULL, NULL, NULL, NULL, NULL,
 649                                &dwNumValues, &dwMaxValueNameLen,
 650                                &dwMaxValueDataLen, NULL, NULL);
 651     }
 652     if (ret != ERROR_SUCCESS ||
 653         dwMaxValueNameLen >= MAX_BUFFER ||
 654         dwMaxValueDataLen >= MAX_BUFFER) {
 655         RegCloseKey(hkeyFonts);
 656         return;
 657     }
 658     for (nval = 0; nval < dwNumValues; nval++ ) {
 659         dwNameSize = MAX_BUFFER;
 660         dwDataValueSize = MAX_BUFFER;
 661         if (IS_NT) {
 662             ret = RegEnumValueW(hkeyFonts, nval, (LPWSTR)wname, &dwNameSize,
 663                                 NULL, &type, (LPBYTE)data, &dwDataValueSize);
 664         } else {
 665             ret = RegEnumValueA(hkeyFonts, nval, (LPSTR)cname, &dwNameSize,
 666                                 NULL, &type, (LPBYTE)data, &dwDataValueSize);
 667         }
 668         if (ret != ERROR_SUCCESS) {
 669             break;
 670         }
 671         if (type != REG_SZ) { /* REG_SZ means a null-terminated string */
 672             continue;
 673         }
 674         if (IS_NT) {
 675             if (!RegistryToBaseTTNameW((LPWSTR)wname) ) {
 676                 /* If the filename ends with ".ttf" or ".otf" also accept it.
 677                  * Not expecting to need to do this for .ttc files.
 678                  * Also note this code is not mirrored in the "A" (win9x) path.
 679                  */
 680                 LPWSTR dot = wcsrchr((LPWSTR)data, L'.');
 681                 if (dot == NULL || ((wcsicmp(dot, L".ttf") != 0)
 682                                       && (wcsicmp(dot, L".otf") != 0))) {
 683                     continue;  /* not a TT font... */
 684                 }
 685             }
 686             registerFontW(&fmi, fontToFileMap, (LPWSTR)wname, (LPWSTR)data);
 687         } else {
 688             if (!RegistryToBaseTTNameA((LPSTR)cname)) {
 689                 continue; /* not a TT font... */
 690             }
 691             registerFontA(&fmi, fontToFileMap, cname, (LPCSTR)data);
 692         }
 693     }
 694     RegCloseKey(hkeyFonts);
 695 }