1 /*
   2  * Copyright (c) 1998, 2013, 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     if (fullname == NULL) {
 156         (*env)->ExceptionClear(env);
 157         return 1;
 158     }
 159     fullnameLC = (*env)->CallObjectMethod(env, fullname,
 160                                           fmi->toLowerCaseMID, fmi->locale);
 161     (*env)->CallBooleanMethod(env, fmi->list, fmi->addMID, fullname);
 162     (*env)->CallObjectMethod(env, fmi->fontToFamilyMap,
 163                              fmi->putMID, fullnameLC, fmi->family);
 164     return 1;
 165 }
 166 
 167 typedef struct CheckFamilyInfo {
 168   wchar_t *family;
 169   wchar_t* fullName;
 170   int isDifferent;
 171 } CheckFamilyInfo;
 172 
 173 static int CALLBACK CheckFontFamilyProcW(
 174   ENUMLOGFONTEXW *lpelfe,
 175   NEWTEXTMETRICEX *lpntme,
 176   int FontType,
 177   LPARAM lParam)
 178 {
 179     CheckFamilyInfo *info = (CheckFamilyInfo*)lParam;
 180     info->isDifferent = wcscmp(lpelfe->elfLogFont.lfFaceName, info->family);
 181 
 182 /*     if (!info->isDifferent) { */
 183 /*         wprintf(LFor font %s expected family=%s instead got %s\n", */
 184 /*                 lpelfe->elfFullName, */
 185 /*                 info->family, */
 186 /*                 lpelfe->elfLogFont.lfFaceName); */
 187 /*         fflush(stdout); */
 188 /*     } */
 189     return 0;
 190 }
 191 
 192 /* This HDC is initialised and released in the populate family map
 193  * JNI entry point, and used within the call which would otherwise
 194  * create many DCs.
 195  */
 196 static HDC screenDC = NULL;
 197 
 198 static int DifferentFamily(wchar_t *family, wchar_t* fullName) {
 199     LOGFONTW lfw;
 200     CheckFamilyInfo info;
 201 
 202     /* If fullName can't be stored in the struct, assume correct family */
 203     if (wcslen((LPWSTR)fullName) >= LF_FACESIZE) {
 204         return 0;
 205     }
 206 
 207     memset(&info, 0, sizeof(CheckFamilyInfo));
 208     info.family = family;
 209     info.fullName = fullName;
 210     info.isDifferent = 0;
 211 
 212     memset(&lfw, 0, sizeof(lfw));
 213     wcscpy(lfw.lfFaceName, fullName);
 214     lfw.lfCharSet = DEFAULT_CHARSET;
 215     EnumFontFamiliesExW(screenDC, &lfw,
 216                         (FONTENUMPROCW)CheckFontFamilyProcW,
 217                         (LPARAM)(&info), 0L);
 218 
 219     return info.isDifferent;
 220 }
 221 
 222 static int CALLBACK EnumFontFacesInFamilyProcW(
 223   ENUMLOGFONTEXW *lpelfe,
 224   NEWTEXTMETRICEX *lpntme,
 225   int FontType,
 226   LPARAM lParam)
 227 {
 228     GdiFontMapInfo *fmi = (GdiFontMapInfo*)lParam;
 229     JNIEnv *env = fmi->env;
 230     jstring fullname, fullnameLC;
 231 
 232     /* Both Vista and XP return DEVICE_FONTTYPE for OTF fonts */
 233     if (FontType != TRUETYPE_FONTTYPE && FontType != DEVICE_FONTTYPE) {
 234         return 1;
 235     }
 236 
 237     /* Windows has font aliases and so may enumerate fonts from
 238      * the aliased family if any actual font of that family is installed.
 239      * To protect against it ignore fonts which aren't enumerated under
 240      * their true family.
 241      */
 242     if (DifferentFamily(lpelfe->elfLogFont.lfFaceName,
 243                         lpelfe->elfFullName))  {
 244       return 1;
 245     }
 246 
 247     fullname = (*env)->NewString(env, lpelfe->elfFullName,
 248                                  (jsize)wcslen((LPWSTR)lpelfe->elfFullName));
 249     if (fullname == NULL) {
 250         (*env)->ExceptionClear(env);
 251         return 1;
 252     }
 253     fullnameLC = (*env)->CallObjectMethod(env, fullname,
 254                                           fmi->toLowerCaseMID, fmi->locale);
 255     (*env)->CallBooleanMethod(env, fmi->list, fmi->addMID, fullname);
 256     (*env)->CallObjectMethod(env, fmi->fontToFamilyMap,
 257                              fmi->putMID, fullnameLC, fmi->family);
 258     return 1;
 259 }
 260 
 261 /* Callback for EnumFontFamiliesEx in populateFontFileNameMap.
 262  * Expects to be called for every charset of every font family.
 263  * If this is the first time we have been called for this family,
 264  * add a new mapping to the familyToFontListMap from this family to a
 265  * list of its members. To populate that list, further enumerate all faces
 266  * in this family for the matched charset. This assumes that all fonts
 267  * in a family support the same charset, which is a fairly safe assumption
 268  * and saves time as the call we make here to EnumFontFamiliesEx will
 269  * enumerate the members of this family just once each.
 270  * Because we set fmi->list to be the newly created list the call back
 271  * can safely add to that list without a search.
 272  */
 273 static int CALLBACK EnumFamilyNamesA(
 274   ENUMLOGFONTEXA *lpelfe,    /* pointer to logical-font data */
 275   NEWTEXTMETRICEX *lpntme,   /* pointer to physical-font data */
 276   int FontType,              /* type of font */
 277   LPARAM lParam)             /* application-defined data */
 278 {
 279     GdiFontMapInfo *fmi = (GdiFontMapInfo*)lParam;
 280     JNIEnv *env = fmi->env;
 281     jstring familyLC;
 282     LOGFONTA lfa;
 283 
 284     /* Both Vista and XP return DEVICE_FONTTYPE for OTF fonts */
 285     if (FontType != TRUETYPE_FONTTYPE && FontType != DEVICE_FONTTYPE) {
 286         return 1;
 287     }
 288 
 289     /* Windows lists fonts which have a vmtx (vertical metrics) table twice.
 290      * Once using their normal name, and again preceded by '@'. These appear
 291      * in font lists in some windows apps, such as wordpad. We don't want
 292      * these so we skip any font where the first character is '@'
 293      */
 294     if (lpelfe->elfLogFont.lfFaceName[0] == '@') {
 295         return 1;
 296     }
 297     fmi->family = JNU_NewStringPlatform(env,lpelfe->elfLogFont.lfFaceName);
 298     if (fmi->family == NULL) {
 299         (*env)->ExceptionClear(env);
 300         return 1;
 301     }
 302     familyLC = (*env)->CallObjectMethod(env, fmi->family,
 303                                         fmi->toLowerCaseMID, fmi->locale);
 304     /* check if already seen this family with a different charset */
 305     if ((*env)->CallBooleanMethod(env,fmi->familyToFontListMap,
 306                                   fmi->containsKeyMID, familyLC)) {
 307         return 1;
 308     }
 309     fmi->list = (*env)->NewObject(env,
 310                                   fmi->arrayListClass, fmi->arrayListCtr, 4);
 311     if (fmi->list == NULL) {
 312         (*env)->ExceptionClear(env);
 313         return 1;
 314     }
 315     (*env)->CallObjectMethod(env, fmi->familyToFontListMap,
 316                              fmi->putMID, familyLC, fmi->list);
 317 
 318 /*  printf("FAMILY=%s\n", lpelfe->elfLogFont.lfFaceName);fflush(stdout); */
 319 
 320     memset(&lfa, 0, sizeof(lfa));
 321     strcpy(lfa.lfFaceName, lpelfe->elfLogFont.lfFaceName);
 322     lfa.lfCharSet = lpelfe->elfLogFont.lfCharSet;
 323     EnumFontFamiliesExA(screenDC, &lfa,
 324                         (FONTENUMPROCA)EnumFontFacesInFamilyProcA,
 325                         lParam, 0L);
 326     return 1;
 327 }
 328 
 329 static int CALLBACK EnumFamilyNamesW(
 330   ENUMLOGFONTEXW *lpelfe,    /* pointer to logical-font data */
 331   NEWTEXTMETRICEX *lpntme,  /* pointer to physical-font data */
 332   int FontType,             /* type of font */
 333   LPARAM lParam )           /* application-defined data */
 334 {
 335     GdiFontMapInfo *fmi = (GdiFontMapInfo*)lParam;
 336     JNIEnv *env = fmi->env;
 337     jstring familyLC;
 338     size_t slen;
 339     LOGFONTW lfw;
 340 
 341     /* Both Vista and XP return DEVICE_FONTTYPE for OTF fonts */
 342     if (FontType != TRUETYPE_FONTTYPE && FontType != DEVICE_FONTTYPE) {
 343         return 1;
 344     }
 345 /*     wprintf(L"FAMILY=%s charset=%d FULL=%s\n", */
 346 /*          lpelfe->elfLogFont.lfFaceName, */
 347 /*          lpelfe->elfLogFont.lfCharSet, */
 348 /*          lpelfe->elfFullName); */
 349 /*     fflush(stdout); */
 350 
 351     /* Windows lists fonts which have a vmtx (vertical metrics) table twice.
 352      * Once using their normal name, and again preceded by '@'. These appear
 353      * in font lists in some windows apps, such as wordpad. We don't want
 354      * these so we skip any font where the first character is '@'
 355      */
 356     if (lpelfe->elfLogFont.lfFaceName[0] == L'@') {
 357             return 1;
 358     }
 359     slen = wcslen(lpelfe->elfLogFont.lfFaceName);
 360     fmi->family = (*env)->NewString(env,lpelfe->elfLogFont.lfFaceName, (jsize)slen);
 361     if (fmi->family == NULL) {
 362         (*env)->ExceptionClear(env);
 363         return 1;
 364     }
 365     familyLC = (*env)->CallObjectMethod(env, fmi->family,
 366                                         fmi->toLowerCaseMID, fmi->locale);
 367     /* check if already seen this family with a different charset */
 368     if ((*env)->CallBooleanMethod(env,fmi->familyToFontListMap,
 369                                   fmi->containsKeyMID, familyLC)) {
 370         return 1;
 371     }
 372     fmi->list = (*env)->NewObject(env,
 373                                   fmi->arrayListClass, fmi->arrayListCtr, 4);
 374     if (fmi->list == NULL) {
 375         (*env)->ExceptionClear(env);
 376         return 1;
 377     }
 378     (*env)->CallObjectMethod(env, fmi->familyToFontListMap,
 379                              fmi->putMID, familyLC, fmi->list);
 380 
 381     memset(&lfw, 0, sizeof(lfw));
 382     wcscpy(lfw.lfFaceName, lpelfe->elfLogFont.lfFaceName);
 383     lfw.lfCharSet = lpelfe->elfLogFont.lfCharSet;
 384     EnumFontFamiliesExW(screenDC, &lfw,
 385                         (FONTENUMPROCW)EnumFontFacesInFamilyProcW,
 386                         lParam, 0L);
 387     return 1;
 388 }
 389 
 390 
 391 /* It looks like TrueType fonts have " (TrueType)" tacked on the end of their
 392  * name, so we can try to use that to distinguish TT from other fonts.
 393  * However if a program "installed" a font in the registry the key may
 394  * not include that. We could also try to "pass" fonts which have no "(..)"
 395  * at the end. But that turns out to pass a few .FON files that MS supply.
 396  * If there's no parenthesized type string, we could next try to infer
 397  * the file type from the file name extension. Since the MS entries that
 398  * have no type string are very few, and have odd names like "MS-DOS CP 437"
 399  * and would never return a Java Font anyway its currently OK to put these
 400  * in the font map, although clearly the returned names must never percolate
 401  * up into a list of available fonts returned to the application.
 402  * Additionally for TTC font files the key looks like
 403  * Font 1 & Font 2 (TrueType)
 404  * or sometimes even :
 405  * Font 1 & Font 2 & Font 3 (TrueType)
 406  * Also if a Font has a name for this locale that name also
 407  * exists in the registry using the appropriate platform encoding.
 408  * What do we do then?
 409  *
 410  * Note: OpenType fonts seems to have " (TrueType)" suffix on Vista
 411  *   but " (OpenType)" on XP.
 412  */
 413 
 414 static BOOL RegistryToBaseTTNameA(LPSTR name) {
 415     static const char TTSUFFIX[] = " (TrueType)";
 416     static const char OTSUFFIX[] = " (OpenType)";
 417     size_t TTSLEN = strlen(TTSUFFIX);
 418     char *suffix;
 419 
 420     size_t len = strlen(name);
 421     if (len == 0) {
 422         return FALSE;
 423     }
 424     if (name[len-1] != ')') {
 425         return FALSE;
 426     }
 427     if (len <= TTSLEN) {
 428         return FALSE;
 429     }
 430 
 431     /* suffix length is the same for truetype and opentype fonts */
 432     suffix = name + len - TTSLEN;
 433     if (strcmp(suffix, TTSUFFIX) == 0 || strcmp(suffix, OTSUFFIX) == 0) {
 434         suffix[0] = '\0'; /* truncate name */
 435         return TRUE;
 436     }
 437     return FALSE;
 438 }
 439 
 440 static BOOL RegistryToBaseTTNameW(LPWSTR name) {
 441     static const wchar_t TTSUFFIX[] = L" (TrueType)";
 442     static const wchar_t OTSUFFIX[] = L" (OpenType)";
 443     size_t TTSLEN = wcslen(TTSUFFIX);
 444     wchar_t *suffix;
 445 
 446     size_t len = wcslen(name);
 447     if (len == 0) {
 448         return FALSE;
 449     }
 450     if (name[len-1] != L')') {
 451         return FALSE;
 452     }
 453     if (len <= TTSLEN) {
 454         return FALSE;
 455     }
 456     /* suffix length is the same for truetype and opentype fonts */
 457     suffix = name + (len - TTSLEN);
 458     if (wcscmp(suffix, TTSUFFIX) == 0 || wcscmp(suffix, OTSUFFIX) == 0) {
 459         suffix[0] = L'\0'; /* truncate name */
 460         return TRUE;
 461     }
 462     return FALSE;
 463 }
 464 
 465 static void registerFontA(GdiFontMapInfo *fmi, jobject fontToFileMap,
 466                           LPCSTR name, LPCSTR data) {
 467     LPSTR ptr1, ptr2;
 468     jstring fontStr;
 469     JNIEnv *env = fmi->env;
 470     size_t dslen = strlen(data);
 471     jstring fileStr = JNU_NewStringPlatform(env, data);
 472     if (fileStr == NULL) {
 473         (*env)->ExceptionClear(env);
 474         return;
 475     }
 476 
 477     /* TTC or ttc means it may be a collection. Need to parse out
 478      * multiple font face names separated by " & "
 479      * By only doing this for fonts which look like collections based on
 480      * file name we are adhering to MS recommendations for font file names
 481      * so it seems that we can be sure that this identifies precisely
 482      * the MS-supplied truetype collections.
 483      * This avoids any potential issues if a TTF file happens to have
 484      * a & in the font name (I can't find anything which prohibits this)
 485      * and also means we only parse the key in cases we know to be
 486      * worthwhile.
 487      */
 488     if ((data[dslen-1] == 'C' || data[dslen-1] == 'c') &&
 489         (ptr1 = strstr(name, " & ")) != NULL) {
 490         ptr1+=3;
 491         while (ptr1 >= name) { /* marginally safer than while (true) */
 492             while ((ptr2 = strstr(ptr1, " & ")) != NULL) {
 493                     ptr1 = ptr2+3;
 494             }
 495             fontStr = JNU_NewStringPlatform(env, ptr1);
 496             if (fontStr == NULL) {
 497                 (*env)->ExceptionClear(env);
 498                 return;
 499             }
 500             fontStr = (*env)->CallObjectMethod(env, fontStr,
 501                                                fmi->toLowerCaseMID,
 502                                                fmi->locale);
 503             (*env)->CallObjectMethod(env, fontToFileMap, fmi->putMID,
 504                                      fontStr, fileStr);
 505             if (ptr1 == name) {
 506                 break;
 507             } else {
 508                 *(ptr1-3) ='\0';
 509                 ptr1 = (LPSTR)name;
 510             }
 511         }
 512     } else {
 513         fontStr = JNU_NewStringPlatform(env, name);
 514         if (fontStr == NULL) {
 515             (*env)->ExceptionClear(env);
 516             return;
 517         }
 518         fontStr = (*env)->CallObjectMethod(env, fontStr,
 519                                            fmi->toLowerCaseMID, fmi->locale);
 520         (*env)->CallObjectMethod(env, fontToFileMap, fmi->putMID,
 521                                  fontStr, fileStr);
 522     }
 523 }
 524 
 525 static void registerFontW(GdiFontMapInfo *fmi, jobject fontToFileMap,
 526                           LPWSTR name, LPWSTR data) {
 527 
 528     wchar_t *ptr1, *ptr2;
 529     jstring fontStr;
 530     JNIEnv *env = fmi->env;
 531     size_t dslen = wcslen(data);
 532     jstring fileStr = (*env)->NewString(env, data, (jsize)dslen);
 533     if (fileStr == NULL) {
 534         (*env)->ExceptionClear(env);
 535         return;
 536     }
 537 
 538     /* TTC or ttc means it may be a collection. Need to parse out
 539      * multiple font face names separated by " & "
 540      * By only doing this for fonts which look like collections based on
 541      * file name we are adhering to MS recommendations for font file names
 542      * so it seems that we can be sure that this identifies precisely
 543      * the MS-supplied truetype collections.
 544      * This avoids any potential issues if a TTF file happens to have
 545      * a & in the font name (I can't find anything which prohibits this)
 546      * and also means we only parse the key in cases we know to be
 547      * worthwhile.
 548      */
 549 
 550     if ((data[dslen-1] == L'C' || data[dslen-1] == L'c') &&
 551         (ptr1 = wcsstr(name, L" & ")) != NULL) {
 552         ptr1+=3;
 553         while (ptr1 >= name) { /* marginally safer than while (true) */
 554             while ((ptr2 = wcsstr(ptr1, L" & ")) != NULL) {
 555                 ptr1 = ptr2+3;
 556             }
 557             fontStr = (*env)->NewString(env, ptr1, (jsize)wcslen(ptr1));
 558             if (fontStr == NULL) {
 559                 (*env)->ExceptionClear(env);
 560                 return;
 561             }
 562             fontStr = (*env)->CallObjectMethod(env, fontStr,
 563                                                fmi->toLowerCaseMID,
 564                                                fmi->locale);
 565             (*env)->CallObjectMethod(env, fontToFileMap, fmi->putMID,
 566                                      fontStr, fileStr);
 567             if (ptr1 == name) {
 568                 break;
 569             } else {
 570                 *(ptr1-3) = L'\0';
 571                 ptr1 = name;
 572             }
 573         }
 574     } else {
 575         fontStr = (*env)->NewString(env, name, (jsize)wcslen(name));
 576         if (fontStr == NULL) {
 577             (*env)->ExceptionClear(env);
 578             return;
 579         }
 580         fontStr = (*env)->CallObjectMethod(env, fontStr,
 581                                            fmi->toLowerCaseMID, fmi->locale);
 582         (*env)->CallObjectMethod(env, fontToFileMap, fmi->putMID,
 583                                  fontStr, fileStr);
 584     }
 585 }
 586 
 587 /* Obtain all the fontname -> filename mappings.
 588  * This is called once and the results returned to Java code which can
 589  * use it for lookups to reduce or avoid the need to search font files.
 590  */
 591 JNIEXPORT void JNICALL
 592 Java_sun_awt_Win32FontManager_populateFontFileNameMap0
 593 (JNIEnv *env, jclass obj, jobject fontToFileMap,
 594  jobject fontToFamilyMap, jobject familyToFontListMap, jobject locale)
 595 {
 596 #define MAX_BUFFER (FILENAME_MAX+1)
 597     const wchar_t wname[MAX_BUFFER];
 598     const char cname[MAX_BUFFER];
 599     const char data[MAX_BUFFER];
 600 
 601     DWORD type;
 602     LONG ret;
 603     HKEY hkeyFonts;
 604     DWORD dwNameSize;
 605     DWORD dwDataValueSize;
 606     DWORD nval;
 607     LPCSTR fontKeyName;
 608     DWORD dwNumValues, dwMaxValueNameLen, dwMaxValueDataLen;
 609     DWORD numValues = 0;
 610     jclass classID;
 611     jmethodID putMID;
 612     GdiFontMapInfo fmi;
 613 
 614     /* Check we were passed all the maps we need, and do lookup of
 615      * methods for JNI up-calls
 616      */
 617     if (fontToFileMap == NULL ||
 618         fontToFamilyMap == NULL ||
 619         familyToFontListMap == NULL) {
 620         return;
 621     }
 622     classID = (*env)->FindClass(env, "java/util/HashMap");
 623     if (classID == NULL) {
 624         return;
 625     }
 626     putMID = (*env)->GetMethodID(env, classID, "put",
 627                  "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
 628     if (putMID == NULL) {
 629         return;
 630     }
 631 
 632     fmi.env = env;
 633     fmi.fontToFamilyMap = fontToFamilyMap;
 634     fmi.familyToFontListMap = familyToFontListMap;
 635     fmi.putMID = putMID;
 636     fmi.locale = locale;
 637     fmi.containsKeyMID = (*env)->GetMethodID(env, classID, "containsKey",
 638                                              "(Ljava/lang/Object;)Z");
 639     if (fmi.containsKeyMID == NULL) {
 640         return;
 641     }
 642 
 643     fmi.arrayListClass = (*env)->FindClass(env, "java/util/ArrayList");
 644     if (fmi.arrayListClass == NULL) {
 645         return;
 646     }
 647     fmi.arrayListCtr = (*env)->GetMethodID(env, fmi.arrayListClass,
 648                                               "<init>", "(I)V");
 649     if (fmi.arrayListCtr == NULL) {
 650         return;
 651     }
 652     fmi.addMID = (*env)->GetMethodID(env, fmi.arrayListClass,
 653                                      "add", "(Ljava/lang/Object;)Z");
 654     if (fmi.addMID == NULL) {
 655         return;
 656     }
 657     classID = (*env)->FindClass(env, "java/lang/String");
 658     if (classID == NULL) {
 659         return;
 660     }
 661     fmi.toLowerCaseMID =
 662         (*env)->GetMethodID(env, classID, "toLowerCase",
 663                             "(Ljava/util/Locale;)Ljava/lang/String;");
 664     if (fmi.toLowerCaseMID == NULL) {
 665         return;
 666     }
 667 
 668     screenDC = GetDC(NULL);
 669     if (screenDC == NULL) {
 670         return;
 671     }
 672     /* Enumerate fonts via GDI to build maps of fonts and families */
 673     if (IS_NT) {
 674         LOGFONTW lfw;
 675         memset(&lfw, 0, sizeof(lfw));
 676         lfw.lfCharSet = DEFAULT_CHARSET;  /* all charsets */
 677         wcscpy(lfw.lfFaceName, L"");      /* one face per family (CHECK) */
 678         EnumFontFamiliesExW(screenDC, &lfw,
 679                             (FONTENUMPROCW)EnumFamilyNamesW,
 680                             (LPARAM)(&fmi), 0L);
 681     } else {
 682         LOGFONT lfa;
 683         memset(&lfa, 0, sizeof(lfa));
 684         lfa.lfCharSet = DEFAULT_CHARSET; /* all charsets */
 685         strcpy(lfa.lfFaceName, "");      /* one face per family */
 686         ret = EnumFontFamiliesExA(screenDC, &lfa,
 687                             (FONTENUMPROCA)EnumFamilyNamesA,
 688                             (LPARAM)(&fmi), 0L);
 689     }
 690 
 691     /* Use the windows registry to map font names to files */
 692     fontKeyName = (IS_NT) ? FONTKEY_NT : FONTKEY_WIN;
 693     ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
 694                        fontKeyName, 0L, KEY_READ, &hkeyFonts);
 695     if (ret != ERROR_SUCCESS) {
 696         ReleaseDC(NULL, screenDC);
 697         screenDC = NULL;
 698         return;
 699     }
 700 
 701     if (IS_NT) {
 702         ret = RegQueryInfoKeyW(hkeyFonts, NULL, NULL, NULL, NULL, NULL, NULL,
 703                                &dwNumValues, &dwMaxValueNameLen,
 704                                &dwMaxValueDataLen, NULL, NULL);
 705     } else {
 706         ret = RegQueryInfoKeyA(hkeyFonts, NULL, NULL, NULL, NULL, NULL, NULL,
 707                                &dwNumValues, &dwMaxValueNameLen,
 708                                &dwMaxValueDataLen, NULL, NULL);
 709     }
 710     if (ret != ERROR_SUCCESS ||
 711         dwMaxValueNameLen >= MAX_BUFFER ||
 712         dwMaxValueDataLen >= MAX_BUFFER) {
 713         RegCloseKey(hkeyFonts);
 714         ReleaseDC(NULL, screenDC);
 715         screenDC = NULL;
 716         return;
 717     }
 718     for (nval = 0; nval < dwNumValues; nval++ ) {
 719         dwNameSize = MAX_BUFFER;
 720         dwDataValueSize = MAX_BUFFER;
 721         if (IS_NT) {
 722             ret = RegEnumValueW(hkeyFonts, nval, (LPWSTR)wname, &dwNameSize,
 723                                 NULL, &type, (LPBYTE)data, &dwDataValueSize);
 724         } else {
 725             ret = RegEnumValueA(hkeyFonts, nval, (LPSTR)cname, &dwNameSize,
 726                                 NULL, &type, (LPBYTE)data, &dwDataValueSize);
 727         }
 728         if (ret != ERROR_SUCCESS) {
 729             break;
 730         }
 731         if (type != REG_SZ) { /* REG_SZ means a null-terminated string */
 732             continue;
 733         }
 734         if (IS_NT) {
 735             if (!RegistryToBaseTTNameW((LPWSTR)wname) ) {
 736                 /* If the filename ends with ".ttf" or ".otf" also accept it.
 737                  * Not expecting to need to do this for .ttc files.
 738                  * Also note this code is not mirrored in the "A" (win9x) path.
 739                  */
 740                 LPWSTR dot = wcsrchr((LPWSTR)data, L'.');
 741                 if (dot == NULL || ((wcsicmp(dot, L".ttf") != 0)
 742                                       && (wcsicmp(dot, L".otf") != 0))) {
 743                     continue;  /* not a TT font... */
 744                 }
 745             }
 746             registerFontW(&fmi, fontToFileMap, (LPWSTR)wname, (LPWSTR)data);
 747         } else {
 748             if (!RegistryToBaseTTNameA((LPSTR)cname)) {
 749                 continue; /* not a TT font... */
 750             }
 751             registerFontA(&fmi, fontToFileMap, cname, (LPCSTR)data);
 752         }
 753     }
 754     RegCloseKey(hkeyFonts);
 755     ReleaseDC(NULL, screenDC);
 756     screenDC = NULL;
 757 }