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