1 /*
   2  * Copyright (c) 2009, 2015, 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 #ifdef WIN32
  27 
  28 #include <windows.h>
  29 #include <stdio.h>
  30 #include <stddef.h>
  31 #include <stdlib.h>
  32 
  33 #include <jni.h>
  34 #include <com_sun_javafx_font_PrismFontFactory.h>
  35 
  36 #define BSIZE (max(512, MAX_PATH+1))
  37 
  38 JNIEXPORT jbyteArray JNICALL
  39 Java_com_sun_javafx_font_PrismFontFactory_getFontPath(JNIEnv *env, jobject thiz)
  40 {
  41     char windir[BSIZE];
  42     char sysdir[BSIZE];
  43     char fontpath[BSIZE*2];
  44     char *end;
  45     jbyteArray byteArrObj;
  46     int pathLen;
  47     unsigned char *data;
  48 
  49     /* Locate fonts directories relative to the Windows System directory.
  50      * If Windows System location is different than the user's window
  51      * directory location, as in a shared Windows installation,
  52      * return both locations as potential font directories
  53      */
  54     GetSystemDirectory(sysdir, BSIZE);
  55     end = strrchr(sysdir,'\\');
  56     if (end && (stricmp(end,"\\System") || stricmp(end,"\\System32"))) {
  57         *end = 0;
  58          strcat(sysdir, "\\Fonts");
  59     }
  60 
  61     GetWindowsDirectory(windir, BSIZE);
  62     if (strlen(windir) > BSIZE-7) {
  63         *windir = 0;
  64     } else {
  65         strcat(windir, "\\Fonts");
  66     }
  67 
  68     strcpy(fontpath,sysdir);
  69     if (stricmp(sysdir,windir)) {
  70         strcat(fontpath,";");
  71         strcat(fontpath,windir);
  72     }
  73 
  74     pathLen = strlen(fontpath);
  75 
  76     byteArrObj = (*env)->NewByteArray(env, pathLen);
  77     if (byteArrObj == NULL) {
  78         return (jbyteArray)NULL;
  79     }
  80     data = (*env)->GetByteArrayElements(env, byteArrObj, NULL);
  81     if (data == NULL) {
  82         return byteArrObj;
  83     }
  84     memcpy(data, fontpath, pathLen);
  85     (*env)->ReleaseByteArrayElements(env, byteArrObj, (jbyte*) data, (jint)0);
  86 
  87     return byteArrObj;
  88 }
  89 
  90 /* The code below is used to obtain information from the windows font APIS
  91  * and registry on which fonts are available and what font files hold those
  92  * fonts. The results are used to speed font lookup.
  93  */
  94 
  95 typedef struct GdiFontMapInfo {
  96     JNIEnv *env;
  97     jstring family;
  98     jobject fontToFamilyMap;
  99     jobject familyToFontListMap;
 100     jobject list;
 101     jmethodID putMID;
 102     jmethodID containsKeyMID;
 103     jclass arrayListClass;
 104     jmethodID arrayListCtr;
 105     jmethodID addMID;
 106     jmethodID toLowerCaseMID;
 107     jobject locale;
 108     HDC screenDC;
 109 } GdiFontMapInfo;
 110 
 111 /* NT is W2K & XP, Vista, Win 7 etc. ie anything later than win9x */
 112 static const char FONTKEY_NT[] =
 113     "Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
 114 
 115 
 116 typedef struct CheckFamilyInfo {
 117   wchar_t *family;
 118   wchar_t* fullName;
 119   int isDifferent;
 120 } CheckFamilyInfo;
 121 
 122 static int CALLBACK CheckFontFamilyProcW(
 123   ENUMLOGFONTEXW *lpelfe,
 124   NEWTEXTMETRICEX *lpntme,
 125   int FontType,
 126   LPARAM lParam)
 127 {
 128     CheckFamilyInfo *info = (CheckFamilyInfo*)lParam;
 129     info->isDifferent = wcscmp(lpelfe->elfLogFont.lfFaceName, info->family);
 130 
 131 /*     if (!info->isDifferent) { */
 132 /*         wprintf(LFor font %s expected family=%s instead got %s\n", */
 133 /*                 lpelfe->elfFullName, */
 134 /*                 info->family, */
 135 /*                 lpelfe->elfLogFont.lfFaceName); */
 136 /*         fflush(stdout); */
 137 /*     } */
 138     return 0;
 139 }
 140 
 141 static int DifferentFamily(wchar_t *family, wchar_t* fullName,
 142                            GdiFontMapInfo *fmi) {
 143     LOGFONTW lfw;
 144     CheckFamilyInfo info;
 145 
 146     /* If fullName can't be stored in the struct, assume correct family */
 147     if (wcslen((LPWSTR)fullName) >= LF_FACESIZE) {
 148         return 0;
 149     }
 150 
 151     memset(&info, 0, sizeof(CheckFamilyInfo));
 152     info.family = family;
 153     info.fullName = fullName;
 154     info.isDifferent = 0;
 155 
 156     memset(&lfw, 0, sizeof(lfw));
 157     wcscpy(lfw.lfFaceName, fullName);
 158     lfw.lfCharSet = DEFAULT_CHARSET;
 159     EnumFontFamiliesExW(fmi->screenDC, &lfw,
 160                         (FONTENUMPROCW)CheckFontFamilyProcW,
 161                         (LPARAM)(&info), 0L);
 162 
 163     return info.isDifferent;
 164 }
 165 
 166 /* Callback for call to EnumFontFamiliesEx in the EnumFamilyNames function.
 167  * Expects to be called once for each face name in the family specified
 168  * in the call. We extract the full name for the font which is expected
 169  * to be in the "system encoding" and create canonical and lower case
 170  * Java strings for the name which are added to the maps. The lower case
 171  * name is used as key to the family name value in the font to family map,
 172  * the canonical name is one of the"list" of members of the family.
 173  */
 174 static int CALLBACK EnumFontFacesInFamilyProcW(
 175   ENUMLOGFONTEXW *lpelfe,
 176   NEWTEXTMETRICEX *lpntme,
 177   int FontType,
 178   LPARAM lParam)
 179 {
 180     GdiFontMapInfo *fmi = (GdiFontMapInfo*)lParam;
 181     JNIEnv *env = fmi->env;
 182     jstring fullname, fullnameLC;
 183 
 184     /* Both Vista and XP return DEVICE_FONTTYPE for OTF fonts */
 185     if (FontType != TRUETYPE_FONTTYPE && FontType != DEVICE_FONTTYPE) {
 186         return 1;
 187     }
 188 
 189     /* Windows has font aliases and so may enumerate fonts from
 190      * the aliased family if any actual font of that family is installed.
 191      * To protect against it ignore fonts which aren't enumerated under
 192      * their true family.
 193      */
 194     if (DifferentFamily(lpelfe->elfLogFont.lfFaceName,
 195                         lpelfe->elfFullName, fmi))  {
 196       return 1;
 197     }
 198 
 199     fullname = (*env)->NewString(env, lpelfe->elfFullName,
 200                                  wcslen((LPWSTR)lpelfe->elfFullName));
 201     if (fullname == NULL) {
 202         (*env)->ExceptionClear(env);
 203         return 1;
 204     }
 205 
 206     fullnameLC = (*env)->CallObjectMethod(env, fullname,
 207                                           fmi->toLowerCaseMID, fmi->locale);
 208     (*env)->CallBooleanMethod(env, fmi->list, fmi->addMID, fullname);
 209     (*env)->CallObjectMethod(env, fmi->fontToFamilyMap,
 210                              fmi->putMID, fullnameLC, fmi->family);
 211     return 1;
 212 }
 213 
 214 /* Callback for EnumFontFamiliesEx in populateFontFileNameMap.
 215  * Expects to be called for every charset of every font family.
 216  * If this is the first time we have been called for this family,
 217  * add a new mapping to the familyToFontListMap from this family to a
 218  * list of its members. To populate that list, further enumerate all faces
 219  * in this family for the matched charset. This assumes that all fonts
 220  * in a family support the same charset, which is a fairly safe assumption
 221  * and saves time as the call we make here to EnumFontFamiliesEx will
 222  * enumerate the members of this family just once each.
 223  * Because we set fmi->list to be the newly created list the call back
 224  * can safely add to that list without a search.
 225  */
 226 static int CALLBACK EnumFamilyNamesW(
 227   ENUMLOGFONTEXW *lpelfe,    /* pointer to logical-font data */
 228   NEWTEXTMETRICEX *lpntme,  /* pointer to physical-font data */
 229   int FontType,             /* type of font */
 230   LPARAM lParam )           /* application-defined data */
 231 {
 232     GdiFontMapInfo *fmi = (GdiFontMapInfo*)lParam;
 233     JNIEnv *env = fmi->env;
 234     jstring familyLC;
 235     int slen;
 236     LOGFONTW lfw;
 237 
 238     /* Both Vista and XP return DEVICE_FONTTYPE for OTF fonts */
 239     if (FontType != TRUETYPE_FONTTYPE && FontType != DEVICE_FONTTYPE) {
 240         return 1;
 241     }
 242 /*     wprintf(L"FAMILY=%s charset=%d FULL=%s\n", */
 243 /*          lpelfe->elfLogFont.lfFaceName, */
 244 /*          lpelfe->elfLogFont.lfCharSet, */
 245 /*          lpelfe->elfFullName); */
 246 /*     fflush(stdout); */
 247 
 248     /* Windows lists fonts which have a vmtx (vertical metrics) table twice.
 249      * Once using their normal name, and again preceded by '@'. These appear
 250      * in font lists in some windows apps, such as wordpad. We don't want
 251      * these so we skip any font where the first character is '@'
 252      */
 253     if (lpelfe->elfLogFont.lfFaceName[0] == L'@') {
 254             return 1;
 255     }
 256     slen = wcslen(lpelfe->elfLogFont.lfFaceName);
 257     fmi->family = (*env)->NewString(env,lpelfe->elfLogFont.lfFaceName, slen);
 258     if (fmi->family == NULL) {
 259         (*env)->ExceptionClear(env);
 260         return 1;
 261     }
 262     familyLC = (*env)->CallObjectMethod(env, fmi->family,
 263                                         fmi->toLowerCaseMID, fmi->locale);
 264     /* check if already seen this family with a different charset */
 265     if ((*env)->CallBooleanMethod(env,fmi->familyToFontListMap,
 266                                   fmi->containsKeyMID, familyLC)) {
 267         return 1;
 268     }
 269     fmi->list = (*env)->NewObject(env,
 270                                   fmi->arrayListClass, fmi->arrayListCtr, 4);
 271     if (fmi->list == NULL) {
 272         (*env)->ExceptionClear(env);
 273         return 1;
 274     }
 275     (*env)->CallObjectMethod(env, fmi->familyToFontListMap,
 276                              fmi->putMID, familyLC, fmi->list);
 277 
 278     memset(&lfw, 0, sizeof(lfw));
 279     wcscpy(lfw.lfFaceName, lpelfe->elfLogFont.lfFaceName);
 280     lfw.lfCharSet = lpelfe->elfLogFont.lfCharSet;
 281     EnumFontFamiliesExW(fmi->screenDC, &lfw,
 282                         (FONTENUMPROCW)EnumFontFacesInFamilyProcW,
 283                         lParam, 0L);
 284     return 1;
 285 }
 286 
 287 
 288 /* It looks like TrueType fonts have " (TrueType)" tacked on the end of their
 289  * name, so we can try to use that to distinguish TT from other fonts.
 290  * However if a program "installed" a font in the registry the key may
 291  * not include that. We could also try to "pass" fonts which have no "(..)"
 292  * at the end. But that turns out to pass a few .FON files that MS supply.
 293  * If there's no parenthesised type string, we could next try to infer
 294  * the file type from the file name extension. Since the MS entries that
 295  * have no type string are very few, and have odd names like "MS-DOS CP 437"
 296  * and would never return a Java Font anyway its currently OK to put these
 297  * in the font map, although clearly the returned names must never percolate
 298  * up into a list of available fonts returned to the application.
 299  * Additionally for TTC font files the key looks like
 300  * Font 1 & Font 2 (TrueType)
 301  * or sometimes even :
 302  * Font 1 & Font 2 & Font 3 (TrueType)
 303  * Also if a Font has a name for this locale that name also
 304  * exists in the registry using the appropriate platform encoding.
 305  * What do we do then?
 306  *
 307  * Note: OpenType fonts seems to have " (TrueType)" suffix on Vista
 308  *   but " (OpenType)" on XP.
 309  */
 310 static BOOL RegistryToBaseTTNameW(LPWSTR name) {
 311     static const wchar_t TTSUFFIX[] = L" (TrueType)";
 312     static const wchar_t OTSUFFIX[] = L" (OpenType)";
 313     int TTSLEN = wcslen(TTSUFFIX);
 314     wchar_t *suffix;
 315 
 316     int len = wcslen(name);
 317     if (len == 0) {
 318         return FALSE;
 319     }
 320     if (name[len-1] != L')') {
 321         return FALSE;
 322     }
 323     if (len <= TTSLEN) {
 324         return FALSE;
 325     }
 326     /* suffix length is the same for truetype and opentype fonts */
 327     suffix = name + (len - TTSLEN);
 328     // REMIND : renable OpenType (.otf) some day.
 329     if (wcscmp(suffix, TTSUFFIX) == 0 /*|| wcscmp(suffix, OTSUFFIX) == 0*/) {
 330         suffix[0] = L'\0'; /* truncate name */
 331         return TRUE;
 332     }
 333     return FALSE;
 334 }
 335 
 336 static void registerFontW(GdiFontMapInfo *fmi, jobject fontToFileMap,
 337                           LPWSTR name, LPWSTR data) {
 338 
 339     wchar_t *ptr1, *ptr2;
 340     jstring fontStr;
 341     JNIEnv *env = fmi->env;
 342     int dslen = wcslen(data);
 343     jstring fileStr = (*env)->NewString(env, data, dslen);
 344     if (fileStr == NULL) {
 345         (*env)->ExceptionClear(env);
 346         return;
 347     }
 348 
 349     /* TTC or ttc means it may be a collection. Need to parse out
 350      * multiple font face names separated by " & "
 351      * By only doing this for fonts which look like collections based on
 352      * file name we are adhering to MS recommendations for font file names
 353      * so it seems that we can be sure that this identifies precisely
 354      * the MS-supplied truetype collections.
 355      * This avoids any potential issues if a TTF file happens to have
 356      * a & in the font name (I can't find anything which prohibits this)
 357      * and also means we only parse the key in cases we know to be
 358      * worthwhile.
 359      */
 360 
 361     if ((data[dslen-1] == L'C' || data[dslen-1] == L'c') &&
 362         (ptr1 = wcsstr(name, L" & ")) != NULL) {
 363         ptr1+=3;
 364         while (ptr1 >= name) { /* marginally safer than while (true) */
 365             while ((ptr2 = wcsstr(ptr1, L" & ")) != NULL) {
 366                 ptr1 = ptr2+3;
 367             }
 368             fontStr = (*env)->NewString(env, ptr1, wcslen(ptr1));
 369             if (fontStr == NULL) {
 370                 (*env)->ExceptionClear(env);
 371                 return;
 372             }
 373             fontStr = (*env)->CallObjectMethod(env, fontStr,
 374                                                fmi->toLowerCaseMID,
 375                                                fmi->locale);
 376             (*env)->CallObjectMethod(env, fontToFileMap, fmi->putMID,
 377                                      fontStr, fileStr);
 378             if (ptr1 == name) {
 379                 break;
 380             } else {
 381                 *(ptr1-3) = L'\0';
 382                 ptr1 = name;
 383             }
 384         }
 385     } else {
 386         fontStr = (*env)->NewString(env, name, wcslen(name));
 387         if (fontStr == NULL) {
 388             (*env)->ExceptionClear(env);
 389             return;
 390         }
 391         fontStr = (*env)->CallObjectMethod(env, fontStr,
 392                                            fmi->toLowerCaseMID, fmi->locale);
 393         (*env)->CallObjectMethod(env, fontToFileMap, fmi->putMID,
 394                                  fontStr, fileStr);
 395     }
 396 }
 397 
 398 /* Obtain all the fontname -> filename mappings.
 399  * This is called once and the results returned to Java code which can
 400  * use it for lookups to reduce or avoid the need to search font files.
 401  */
 402 JNIEXPORT void JNICALL
 403 Java_com_sun_javafx_font_PrismFontFactory_populateFontFileNameMap
 404 (JNIEnv *env, jclass obj, jobject fontToFileMap,
 405  jobject fontToFamilyMap, jobject familyToFontListMap, jobject locale)
 406 {
 407 #define MAX_BUFFER (FILENAME_MAX+1)
 408     const wchar_t wname[MAX_BUFFER];
 409     const char data[MAX_BUFFER];
 410 
 411     DWORD type;
 412     LONG ret;
 413     HKEY hkeyFonts;
 414     DWORD dwNameSize;
 415     DWORD dwDataValueSize;
 416     DWORD nval;
 417     LPCSTR fontKeyName;
 418     DWORD dwNumValues, dwMaxValueNameLen, dwMaxValueDataLen;
 419     DWORD numValues = 0;
 420     jclass classID;
 421     jmethodID putMID;
 422     GdiFontMapInfo fmi;
 423     LOGFONTW lfw;
 424 
 425     /* Check we were passed all the maps we need, and do lookup of
 426      * methods for JNI up-calls
 427      */
 428     if (fontToFileMap == NULL ||
 429         fontToFamilyMap == NULL ||
 430         familyToFontListMap == NULL) {
 431         return;
 432     }
 433     classID = (*env)->FindClass(env, "java/util/HashMap");
 434     if (classID == NULL) {
 435         return;
 436     }
 437     putMID = (*env)->GetMethodID(env, classID, "put",
 438                  "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
 439     if (putMID == NULL) {
 440         return;
 441     }
 442 
 443     fmi.env = env;
 444     fmi.fontToFamilyMap = fontToFamilyMap;
 445     fmi.familyToFontListMap = familyToFontListMap;
 446     fmi.putMID = putMID;
 447     fmi.locale = locale;
 448     fmi.containsKeyMID = (*env)->GetMethodID(env, classID, "containsKey",
 449                                              "(Ljava/lang/Object;)Z");
 450     if (fmi.containsKeyMID == NULL) {
 451         return;
 452     }
 453 
 454     fmi.arrayListClass = (*env)->FindClass(env, "java/util/ArrayList");
 455     if (fmi.arrayListClass == NULL) {
 456         return;
 457     }
 458     fmi.arrayListCtr = (*env)->GetMethodID(env, fmi.arrayListClass,
 459                                               "<init>", "(I)V");
 460     if (fmi.arrayListCtr == NULL) {
 461         return;
 462     }
 463     fmi.addMID = (*env)->GetMethodID(env, fmi.arrayListClass,
 464                                      "add", "(Ljava/lang/Object;)Z");
 465     if (fmi.addMID == NULL) {
 466         return;
 467     }
 468     classID = (*env)->FindClass(env, "java/lang/String");
 469     if (classID == NULL) {
 470         return;
 471     }
 472     fmi.toLowerCaseMID =
 473         (*env)->GetMethodID(env, classID, "toLowerCase",
 474                             "(Ljava/util/Locale;)Ljava/lang/String;");
 475     if (fmi.toLowerCaseMID == NULL) {
 476         return;
 477     }
 478 
 479     /* This HDC is initialised and released in this populate family map
 480      * JNI entry point, and used within the call which would otherwise
 481      * create many DCs.
 482      */
 483     fmi.screenDC = GetDC(NULL);
 484     if (fmi.screenDC == NULL) {
 485         return;
 486     }
 487 
 488     /* Enumerate fonts via GDI to build maps of fonts and families */
 489     memset(&lfw, 0, sizeof(lfw));
 490     lfw.lfCharSet = DEFAULT_CHARSET;  /* all charsets */
 491     wcscpy(lfw.lfFaceName, L"");      /* one face per family (CHECK) */
 492     EnumFontFamiliesExW(fmi.screenDC, &lfw,
 493                         (FONTENUMPROCW)EnumFamilyNamesW,
 494                         (LPARAM)(&fmi), 0L);
 495 
 496     /* Use the windows registry to map font names to files */
 497     fontKeyName =  FONTKEY_NT;
 498     ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
 499                        fontKeyName, 0L, KEY_READ, &hkeyFonts);
 500     if (ret != ERROR_SUCCESS) {
 501         ReleaseDC(NULL, fmi.screenDC);
 502         fmi.screenDC = NULL;
 503         return;
 504     }
 505 
 506     ret = RegQueryInfoKeyW(hkeyFonts, NULL, NULL, NULL, NULL, NULL, NULL,
 507                            &dwNumValues, &dwMaxValueNameLen,
 508                            &dwMaxValueDataLen, NULL, NULL);
 509     
 510     if (ret != ERROR_SUCCESS ||
 511         dwMaxValueNameLen >= MAX_BUFFER ||
 512         dwMaxValueDataLen >= MAX_BUFFER) {
 513         RegCloseKey(hkeyFonts);
 514         ReleaseDC(NULL, fmi.screenDC);
 515         fmi.screenDC = NULL;
 516         return;
 517     }
 518     for (nval = 0; nval < dwNumValues; nval++ ) {
 519         dwNameSize = MAX_BUFFER;
 520         dwDataValueSize = MAX_BUFFER;
 521             ret = RegEnumValueW(hkeyFonts, nval, (LPWSTR)wname, &dwNameSize,
 522                                 NULL, &type, (LPBYTE)data, &dwDataValueSize);
 523         if (ret != ERROR_SUCCESS) {
 524             break;
 525         }
 526         if (type != REG_SZ) { /* REG_SZ means a null-terminated string */
 527             continue;
 528         }
 529             if (!RegistryToBaseTTNameW((LPWSTR)wname) ) {
 530                 /* If the filename ends with ".ttf" or ".otf" also accept it.
 531                  * REMIND : in fact not accepting .otf's for now as the
 532                  * upstream code isn't expecting them.
 533                  * Not expecting to need to do this for .ttc files.
 534                  * Also note this code is not mirrored in the "A" (win9x) path.
 535                  */
 536                 LPWSTR dot = wcsrchr((LPWSTR)data, L'.');
 537                 if (dot == NULL || ((wcsicmp(dot, L".ttf") != 0)
 538                                     /* && (wcsicmp(dot, L".otf") != 0) */)) {
 539                     continue;  /* not a TT font... */
 540                 }
 541             }
 542             registerFontW(&fmi, fontToFileMap, (LPWSTR)wname, (LPWSTR)data);
 543     }
 544     RegCloseKey(hkeyFonts);
 545     ReleaseDC(NULL, fmi.screenDC);
 546     fmi.screenDC = NULL;
 547 }
 548 
 549 JNIEXPORT jstring JNICALL
 550 Java_com_sun_javafx_font_PrismFontFactory_regReadFontLink(JNIEnv *env, jclass obj, jstring lpFontName)
 551 {
 552     LONG lResult;
 553     BYTE* buf;
 554     DWORD dwBufSize = sizeof(buf);
 555     DWORD dwType = REG_MULTI_SZ;
 556     HKEY hKey;
 557     LPCWSTR fontpath = NULL;
 558     jstring linkStr;
 559 
 560     LPWSTR lpSubKey = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontLink\\SystemLink";
 561     lResult = RegOpenKeyExW (HKEY_LOCAL_MACHINE, lpSubKey, 0, KEY_READ, &hKey);
 562     if (lResult != ERROR_SUCCESS) 
 563     {
 564         return (jstring)NULL;
 565     } 
 566 
 567     fontpath = (*env)->GetStringChars(env, lpFontName, (jboolean*) NULL);
 568 
 569     //get the buffer size
 570     lResult = RegQueryValueExW(hKey, fontpath, 0, &dwType, NULL, &dwBufSize);
 571     if ((lResult == ERROR_SUCCESS) && (dwBufSize > 0)) {
 572         buf = malloc( dwBufSize );
 573         if (buf == NULL) {
 574             (*env)->ReleaseStringChars(env, lpFontName, fontpath);
 575             RegCloseKey (hKey);
 576             return (jstring)NULL;
 577         }
 578         lResult = RegQueryValueExW(hKey, fontpath, 0, &dwType, (BYTE*)buf, 
 579                                    &dwBufSize);
 580         (*env)->ReleaseStringChars(env, lpFontName, fontpath);
 581         RegCloseKey (hKey);
 582         
 583         if (lResult != ERROR_SUCCESS) {
 584             free(buf);
 585             return (jstring)NULL;                       
 586         }       
 587     } else {
 588         return (jstring)NULL;
 589     }
 590 
 591     linkStr = (*env)->NewString(env, (LPWSTR)buf, dwBufSize/sizeof(WCHAR));
 592     free(buf);
 593     return linkStr;
 594 }
 595 
 596 
 597 typedef  unsigned short  LANGID;
 598 
 599 
 600 #define LANGID_JA_JP   0x411
 601 #define LANGID_ZH_CN   0x0804
 602 #define LANGID_ZH_SG   0x1004
 603 #define LANGID_ZH_TW   0x0404
 604 #define LANGID_ZH_HK   0x0c04
 605 #define LANGID_ZH_MO   0x1404
 606 #define LANGID_KO_KR   0x0412
 607 #define LANGID_US      0x409
 608 
 609 static const wchar_t EUDCKEY_JA_JP[] = L"EUDC\\932";
 610 static const wchar_t EUDCKEY_ZH_CN[] = L"EUDC\\936";
 611 static const wchar_t EUDCKEY_ZH_TW[] = L"EUDC\\950";
 612 static const wchar_t EUDCKEY_KO_KR[] = L"EUDC\\949";
 613 static const wchar_t EUDCKEY_DEFAULT[] = L"EUDC\\1252";
 614 
 615 
 616 JNIEXPORT jstring JNICALL
 617 Java_com_sun_javafx_font_PrismFontFactory_getEUDCFontFile(JNIEnv *env, jclass cl) {
 618     int    rc;
 619     HKEY   key;
 620     DWORD  type;
 621     WCHAR  fontPathBuf[MAX_PATH + 1];
 622     DWORD  fontPathLen = MAX_PATH + 1;
 623     WCHAR  tmpPath[MAX_PATH + 1];
 624     LPWSTR fontPath = fontPathBuf;
 625     LPWSTR eudcKey = NULL;
 626 
 627     LANGID langID = GetSystemDefaultLangID();
 628 
 629     //lookup for encoding ID, EUDC only supported in
 630     //codepage 932, 936, 949, 950 (and unicode)
 631     if (langID == LANGID_JA_JP) {
 632         eudcKey = EUDCKEY_JA_JP;
 633     } else if (langID == LANGID_ZH_CN || langID == LANGID_ZH_SG) {
 634         eudcKey = EUDCKEY_ZH_CN;
 635     } else if (langID == LANGID_ZH_HK || langID == LANGID_ZH_TW ||
 636                langID == LANGID_ZH_MO) {
 637         eudcKey = EUDCKEY_ZH_TW;
 638     } else if (langID == LANGID_KO_KR) {
 639         eudcKey = EUDCKEY_KO_KR;
 640     } else if (langID == LANGID_US) {
 641         eudcKey = EUDCKEY_DEFAULT;
 642     } else {
 643         return NULL;
 644     }
 645 
 646     rc = RegOpenKeyExW(HKEY_CURRENT_USER, eudcKey, 0, KEY_READ, &key);
 647     if (rc != ERROR_SUCCESS) {
 648         return NULL;
 649     }
 650     rc = RegQueryValueExW(key,
 651                          L"SystemDefaultEUDCFont",
 652                          0,
 653                          &type,
 654                          (LPBYTE)fontPath,
 655                          &fontPathLen);
 656     RegCloseKey(key);
 657     fontPathLen /= sizeof(WCHAR);
 658     if (rc != ERROR_SUCCESS || type != REG_SZ ||
 659         (fontPathLen > MAX_PATH)) {
 660         return NULL;
 661     }
 662 
 663     fontPath[fontPathLen] = L'\0';
 664     if (wcsstr(fontPath, L"%SystemRoot%") == fontPath) {
 665         //if the fontPath includes %SystemRoot%
 666         LPWSTR systemRoot = _wgetenv(L"SystemRoot");
 667         // Subtract 12, being the length of "SystemRoot".
 668         if ((systemRoot == NULL) || 
 669            (fontPathLen-12 +wcslen(systemRoot) > MAX_PATH)) {
 670                 return NULL;
 671         }
 672         wcscpy(tmpPath, systemRoot);
 673         wcscat(tmpPath, (wchar_t *)(fontPath+12));
 674         fontPath = tmpPath;
 675         fontPathLen = wcslen(tmpPath);
 676 
 677     } else if (wcscmp(fontPath, L"EUDC.TTE") == 0) {
 678         //else to see if it only inludes "EUDC.TTE"
 679         WCHAR systemRoot[MAX_PATH];
 680         UINT ret = GetWindowsDirectoryW(systemRoot, MAX_PATH); 
 681         if ( ret != 0) {
 682             if (ret + 16 > MAX_PATH) {
 683                 return NULL;
 684             }
 685             wcscpy(fontPath, systemRoot);
 686             wcscat(fontPath, L"\\FONTS\\EUDC.TTE");
 687             fontPathLen = wcslen(fontPath);
 688         }
 689         else {
 690             return NULL;
 691         }
 692     }
 693     return (*env)->NewString(env, (LPWSTR)fontPath, fontPathLen);
 694 }
 695 
 696 static BOOL getSysParams(NONCLIENTMETRICSW* ncmetrics) {
 697 
 698     OSVERSIONINFOEX osvi;
 699     int cbsize;
 700 
 701     ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
 702     osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
 703     if (!(GetVersionEx((OSVERSIONINFO *)&osvi))) {
 704         return FALSE;
 705     }
 706   
 707     // See JDK bug 6944516: specify correct size for ncmetrics on Windows XP
 708     // Microsoft recommend to subtract the size of the 'iPaddedBorderWidth'
 709     // field when running on XP. Yuck.
 710     if (osvi.dwMajorVersion < 6) { // 5 is XP, 6 is Vista.
 711         cbsize = offsetof(NONCLIENTMETRICSW, iPaddedBorderWidth);
 712     } else {
 713         cbsize = sizeof(*ncmetrics);
 714     }
 715     ZeroMemory(ncmetrics, cbsize);
 716     ncmetrics->cbSize = cbsize;
 717 
 718     return SystemParametersInfoW(SPI_GETNONCLIENTMETRICS,
 719                                  ncmetrics->cbSize, ncmetrics, FALSE);
 720 }
 721 
 722 
 723 /*
 724  * Class:     Java_com_sun_javafx_font_PrismFontFactory
 725  * Method:    getLCDContrastWin32
 726  * Signature: ()I
 727  */
 728 JNIEXPORT jint JNICALL Java_com_sun_javafx_font_PrismFontFactory_getLCDContrastWin32
 729   (JNIEnv *env, jobject klass) {
 730 
 731     unsigned int fontSmoothingContrast;
 732     static const int fontSmoothingContrastDefault = 1300;
 733 
 734     return SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0,
 735         &fontSmoothingContrast, 0) ? fontSmoothingContrast : fontSmoothingContrastDefault;
 736 }
 737 
 738 JNIEXPORT jint JNICALL
 739 Java_com_sun_javafx_font_PrismFontFactory_getSystemFontSizeNative(JNIEnv *env, jclass cl)
 740 {
 741     NONCLIENTMETRICSW ncmetrics;
 742 
 743     if (getSysParams(&ncmetrics)) {
 744         return -ncmetrics.lfMessageFont.lfHeight;
 745     } else {
 746         return 12;
 747     }
 748 }
 749 
 750 JNIEXPORT jstring JNICALL
 751 Java_com_sun_javafx_font_PrismFontFactory_getSystemFontNative(JNIEnv *env, jclass cl) {
 752 
 753     NONCLIENTMETRICSW ncmetrics;
 754 
 755     if (getSysParams(&ncmetrics)) {
 756         int len = wcslen(ncmetrics.lfMessageFont.lfFaceName);
 757         return (*env)->NewString(env, ncmetrics.lfMessageFont.lfFaceName, len);
 758     } else {
 759         return NULL;
 760     }
 761 }
 762 
 763 
 764 JNIEXPORT jshort JNICALL
 765 Java_com_sun_javafx_font_PrismFontFactory_getSystemLCID(JNIEnv *env, jclass cl)
 766 {
 767     LCID lcid = GetSystemDefaultLCID();
 768     DWORD value;
 769 
 770     int ret = GetLocaleInfoW(lcid,
 771                              LOCALE_ILANGUAGE | LOCALE_RETURN_NUMBER,
 772                              (LPTSTR)&value,
 773                              sizeof(value) / sizeof(TCHAR));
 774     return (jshort)value;
 775 }
 776 
 777 #endif /* WIN32 */