1 /*
   2  * Copyright (c) 1996, 2008, 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 "awt.h"
  27 #include <math.h>
  28 #include "jlong.h"
  29 #include "awt_Font.h"
  30 #include "awt_Toolkit.h"
  31 
  32 #include "java_awt_Font.h"
  33 #include "java_awt_FontMetrics.h"
  34 #include "java_awt_Dimension.h"
  35 
  36 #include "sun_awt_FontDescriptor.h"
  37 #include "sun_awt_windows_WDefaultFontCharset.h"
  38 #include "sun_awt_windows_WFontPeer.h"
  39 #include "awt_Component.h"
  40 #include "Disposer.h"
  41 
  42 /* IMPORTANT! Read the README.JNI file for notes on JNI converted AWT code.
  43  */
  44 
  45 AwtFontCache fontCache;
  46 
  47 extern jboolean IsMultiFont(JNIEnv *env, jobject obj)
  48 {
  49     if (obj == NULL) {
  50         return JNI_FALSE;
  51     }
  52     if (env->EnsureLocalCapacity(2))
  53         return JNI_FALSE;
  54     jobject peer = env->CallObjectMethod(obj, AwtFont::peerMID);
  55     if (peer == NULL) {
  56         return JNI_FALSE;
  57     }
  58     jobject fontConfig = env->GetObjectField(peer, AwtFont::fontConfigID);
  59     jboolean result = fontConfig != NULL;
  60     env->DeleteLocalRef(peer);
  61     env->DeleteLocalRef(fontConfig);
  62     return result;
  63 }
  64 
  65 extern jstring GetTextComponentFontName(JNIEnv *env, jobject font)
  66 {
  67     DASSERT(font != NULL);
  68     if (env->EnsureLocalCapacity(2)) {
  69         return NULL;
  70     }
  71     jobject peer = env->CallObjectMethod(font, AwtFont::peerMID);
  72     DASSERT(peer != NULL);
  73     jstring textComponentFontName =
  74             (jstring) env->GetObjectField(peer, AwtFont::textComponentFontNameID);
  75     env->DeleteLocalRef(peer);
  76     return textComponentFontName;
  77 }
  78 
  79 /************************************************************************
  80  * AwtFont fields
  81  */
  82 
  83 /* sun.awt.windows.WFontMetrics fields */
  84 jfieldID AwtFont::widthsID;
  85 jfieldID AwtFont::ascentID;
  86 jfieldID AwtFont::descentID;
  87 jfieldID AwtFont::leadingID;
  88 jfieldID AwtFont::heightID;
  89 jfieldID AwtFont::maxAscentID;
  90 jfieldID AwtFont::maxDescentID;
  91 jfieldID AwtFont::maxHeightID;
  92 jfieldID AwtFont::maxAdvanceID;
  93 
  94 /* java.awt.FontDescriptor fields */
  95 jfieldID AwtFont::nativeNameID;
  96 jfieldID AwtFont::useUnicodeID;
  97 
  98 /* java.awt.Font fields */
  99 jfieldID AwtFont::pDataID;
 100 jfieldID AwtFont::nameID;
 101 jfieldID AwtFont::sizeID;
 102 jfieldID AwtFont::styleID;
 103 
 104 /* java.awt.FontMetrics fields */
 105 jfieldID AwtFont::fontID;
 106 
 107 /* sun.awt.PlatformFont fields */
 108 jfieldID AwtFont::fontConfigID;
 109 jfieldID AwtFont::componentFontsID;
 110 
 111 /* sun.awt.windows.WFontPeer fields */
 112 jfieldID AwtFont::textComponentFontNameID;
 113 
 114 /* sun.awt.windows.WDefaultFontCharset fields */
 115 jfieldID AwtFont::fontNameID;
 116 
 117 /* java.awt.Font methods */
 118 jmethodID AwtFont::peerMID;
 119 
 120 /* sun.awt.PlatformFont methods */
 121 jmethodID AwtFont::makeConvertedMultiFontStringMID;
 122 
 123 /* sun.awt.PlatformFont methods */
 124 jmethodID AwtFont::getFontMID;
 125 
 126 /* java.awt.FontMetrics methods */
 127 jmethodID AwtFont::getHeightMID;
 128 
 129 
 130 /************************************************************************
 131  * AwtFont methods
 132  */
 133 AwtFont::AwtFont(int num, JNIEnv *env, jobject javaFont)
 134 {
 135     if (num == 0) {  // not multi-font
 136         num = 1;
 137     }
 138 
 139     m_hFontNum = num;
 140     m_hFont = new HFONT[num];
 141 
 142     for (int i = 0; i < num; i++) {
 143         m_hFont[i] = NULL;
 144     }
 145 
 146     m_textInput = -1;
 147     m_ascent = -1;
 148     m_overhang = 0;
 149 }
 150 
 151 AwtFont::~AwtFont()
 152 {
 153 }
 154 
 155 void AwtFont::Dispose() {
 156     for (int i = 0; i < m_hFontNum; i++) {
 157         HFONT font = m_hFont[i];
 158         if (font != NULL && fontCache.Search(font)) {
 159             fontCache.Remove(font);
 160             /*  NOTE: delete of windows HFONT happens in FontCache::Remove
 161                       only when the final reference to the font is disposed */
 162         } else if (font != NULL) {
 163          // if font was not in cache, its not shared and we delete it now
 164            VERIFY(::DeleteObject(font));
 165         }
 166     }
 167     delete[] m_hFont;
 168 
 169     AwtObject::Dispose();
 170 }
 171 
 172 static void pDataDisposeMethod(JNIEnv *env, jlong pData)
 173 {
 174     TRY_NO_VERIFY;
 175 
 176     AwtObject::_Dispose((PDATA)pData);
 177 
 178     CATCH_BAD_ALLOC;
 179 }
 180 
 181 AwtFont* AwtFont::GetFont(JNIEnv *env, jobject font,
 182                           jint angle, jfloat awScale)
 183 {
 184     jlong pData = env->GetLongField(font, AwtFont::pDataID);
 185     AwtFont* awtFont = (AwtFont*)jlong_to_ptr(pData);
 186 
 187     if (awtFont != NULL) {
 188         return awtFont;
 189     }
 190 
 191     awtFont = Create(env, font, angle, awScale);
 192 
 193     env->SetLongField(font, AwtFont::pDataID,
 194         reinterpret_cast<jlong>(awtFont));
 195     return awtFont;
 196 }
 197 
 198 // Get suitable CHARSET from charset string provided by font configuration.
 199 static int GetNativeCharset(LPCWSTR name)
 200 {
 201     if (wcsstr(name, L"ANSI_CHARSET"))
 202         return ANSI_CHARSET;
 203     if (wcsstr(name, L"DEFAULT_CHARSET"))
 204         return DEFAULT_CHARSET;
 205     if (wcsstr(name, L"SYMBOL_CHARSET") || wcsstr(name, L"WingDings"))
 206         return SYMBOL_CHARSET;
 207     if (wcsstr(name, L"SHIFTJIS_CHARSET"))
 208         return SHIFTJIS_CHARSET;
 209     if (wcsstr(name, L"GB2312_CHARSET"))
 210         return GB2312_CHARSET;
 211     if (wcsstr(name, L"HANGEUL_CHARSET"))
 212         return HANGEUL_CHARSET;
 213     if (wcsstr(name, L"CHINESEBIG5_CHARSET"))
 214         return CHINESEBIG5_CHARSET;
 215     if (wcsstr(name, L"OEM_CHARSET"))
 216         return OEM_CHARSET;
 217     if (wcsstr(name, L"JOHAB_CHARSET"))
 218         return JOHAB_CHARSET;
 219     if (wcsstr(name, L"HEBREW_CHARSET"))
 220         return HEBREW_CHARSET;
 221     if (wcsstr(name, L"ARABIC_CHARSET"))
 222         return ARABIC_CHARSET;
 223     if (wcsstr(name, L"GREEK_CHARSET"))
 224         return GREEK_CHARSET;
 225     if (wcsstr(name, L"TURKISH_CHARSET"))
 226         return TURKISH_CHARSET;
 227     if (wcsstr(name, L"VIETNAMESE_CHARSET"))
 228         return VIETNAMESE_CHARSET;
 229     if (wcsstr(name, L"THAI_CHARSET"))
 230         return THAI_CHARSET;
 231     if (wcsstr(name, L"EASTEUROPE_CHARSET"))
 232         return EASTEUROPE_CHARSET;
 233     if (wcsstr(name, L"RUSSIAN_CHARSET"))
 234         return RUSSIAN_CHARSET;
 235     if (wcsstr(name, L"MAC_CHARSET"))
 236         return MAC_CHARSET;
 237     if (wcsstr(name, L"BALTIC_CHARSET"))
 238         return BALTIC_CHARSET;
 239     return ANSI_CHARSET;
 240 }
 241 
 242 AwtFont* AwtFont::Create(JNIEnv *env, jobject font, jint angle, jfloat awScale)
 243 {
 244     int fontSize = env->GetIntField(font, AwtFont::sizeID);
 245     int fontStyle = env->GetIntField(font, AwtFont::styleID);
 246 
 247     AwtFont* awtFont = NULL;
 248     jobjectArray compFont = NULL;
 249     int cfnum;
 250 
 251     try {
 252         if (env->EnsureLocalCapacity(3) < 0)
 253             return 0;
 254 
 255         if (IsMultiFont(env, font)) {
 256             compFont = GetComponentFonts(env, font);
 257             cfnum = env->GetArrayLength(compFont);
 258         } else {
 259             compFont = NULL;
 260             cfnum = 0;
 261         }
 262 
 263         LPCWSTR wName;
 264 
 265         awtFont = new AwtFont(cfnum, env, font);
 266 
 267         awtFont->textAngle = angle;
 268         awtFont->awScale = awScale;
 269 
 270         if (cfnum > 0) {
 271             // Ask peer class for the text component font name
 272             jstring jTextComponentFontName = GetTextComponentFontName(env, font);
 273             LPCWSTR textComponentFontName = JNU_GetStringPlatformChars(env, jTextComponentFontName, NULL);
 274 
 275             awtFont->m_textInput = -1;
 276             for (int i = 0; i < cfnum; i++) {
 277                 // nativeName is a pair of platform fontname and its charset
 278                 // tied with a comma; "Times New Roman,ANSI_CHARSET".
 279                 jobject fontDescriptor = env->GetObjectArrayElement(compFont,
 280                                                                     i);
 281                 jstring nativeName =
 282                     (jstring)env->GetObjectField(fontDescriptor,
 283                                                  AwtFont::nativeNameID);
 284                 wName = JNU_GetStringPlatformChars(env, nativeName, NULL);
 285                 DASSERT(wName);
 286 
 287                 //On NT platforms, if the font is not Symbol or Dingbats
 288                 //use "W" version of Win32 APIs directly, info the FontDescription
 289                 //no need to convert characters from Unicode to locale encodings.
 290                 if (GetNativeCharset(wName) != SYMBOL_CHARSET) {
 291                     env->SetBooleanField(fontDescriptor, AwtFont::useUnicodeID, TRUE);
 292                 }
 293 
 294                 // Check to see if this font is suitable for input
 295                 // on AWT TextComponent
 296                 if ((awtFont->m_textInput == -1) &&
 297                         (textComponentFontName != NULL) &&
 298                         (wcscmp(wName, textComponentFontName) == 0)) {
 299                     awtFont->m_textInput = i;
 300                 }
 301                 HFONT hfonttmp = CreateHFont(const_cast<LPWSTR>(wName), fontStyle, fontSize,
 302                                              angle, awScale);
 303                 awtFont->m_hFont[i] = hfonttmp;
 304 
 305                 JNU_ReleaseStringPlatformChars(env, nativeName, wName);
 306 
 307                 env->DeleteLocalRef(fontDescriptor);
 308                 env->DeleteLocalRef(nativeName);
 309             }
 310             if (awtFont->m_textInput == -1) {
 311                 // no text component font was identified, so default
 312                 // to first component
 313                 awtFont->m_textInput = 0;
 314             }
 315 
 316             JNU_ReleaseStringPlatformChars(env, jTextComponentFontName, textComponentFontName);
 317             env->DeleteLocalRef(jTextComponentFontName);
 318         } else {
 319             // Instantiation for English version.
 320             jstring fontName = (jstring)env->GetObjectField(font,
 321                                                             AwtFont::nameID);
 322             wName = JNU_GetStringPlatformChars(env, fontName, NULL);
 323 
 324             WCHAR* wEName;
 325             if (!wcscmp(wName, L"Helvetica") || !wcscmp(wName, L"SansSerif")) {
 326                 wEName = L"Arial";
 327             } else if (!wcscmp(wName, L"TimesRoman") ||
 328                        !wcscmp(wName, L"Serif")) {
 329                 wEName = L"Times New Roman";
 330             } else if (!wcscmp(wName, L"Courier") ||
 331                        !wcscmp(wName, L"Monospaced")) {
 332                 wEName = L"Courier New";
 333             } else if (!wcscmp(wName, L"Dialog")) {
 334                 wEName = L"MS Sans Serif";
 335             } else if (!wcscmp(wName, L"DialogInput")) {
 336                 wEName = L"MS Sans Serif";
 337             } else if (!wcscmp(wName, L"ZapfDingbats")) {
 338                 wEName = L"WingDings";
 339             } else
 340                 wEName = L"Arial";
 341 
 342             awtFont->m_textInput = 0;
 343             awtFont->m_hFont[0] = CreateHFont(wEName, fontStyle, fontSize,
 344                                               angle, awScale);
 345 
 346             JNU_ReleaseStringPlatformChars(env, fontName, wName);
 347 
 348             env->DeleteLocalRef(fontName);
 349         }
 350         /* The several callers of this method also set the pData field.
 351          * That's unnecessary but harmless duplication. However we definitely
 352          * want only one disposer record.
 353          */
 354         env->SetLongField(font, AwtFont::pDataID,
 355         reinterpret_cast<jlong>(awtFont));
 356         Disposer_AddRecord(env, font, pDataDisposeMethod,
 357                        reinterpret_cast<jlong>(awtFont));
 358     } catch (...) {
 359         env->DeleteLocalRef(compFont);
 360         throw;
 361     }
 362 
 363     env->DeleteLocalRef(compFont);
 364     return awtFont;
 365 }
 366 
 367 int CALLBACK FindFamilyName (ENUMLOGFONTEX *lpelfe,
 368           NEWTEXTMETRICEX *lpntme, int FontType, LPARAM lParam)
 369 {
 370     if(_tcsstr((LPTSTR)lParam, lpelfe->elfLogFont.lfFaceName)) {
 371         _tcscpy((LPTSTR)lParam, lpelfe->elfLogFont.lfFaceName);
 372         return 0;
 373     } else {
 374         return 1;
 375     }
 376 }
 377 
 378 static void strip_tail(wchar_t* text, wchar_t* tail) { // strips tail and any possible whitespace before it from the end of text
 379     if (wcslen(text)<=wcslen(tail)) {
 380         return;
 381     }
 382     wchar_t* p = text+wcslen(text)-wcslen(tail);
 383     if (!wcscmp(p, tail)) {
 384         while(p>text && iswspace(*(p-1)))
 385             p--;
 386         *p = 0;
 387     }
 388 
 389 }
 390 
 391 static HFONT CreateHFont_sub(LPCWSTR name, int style, int height,
 392                              int angle=0, float awScale=1.0f)
 393 {
 394     LOGFONTW logFont;
 395 
 396     logFont.lfWidth = 0;
 397     logFont.lfEscapement = angle;
 398     logFont.lfOrientation = angle;
 399     logFont.lfUnderline = FALSE;
 400     logFont.lfStrikeOut = FALSE;
 401     logFont.lfCharSet = GetNativeCharset(name);
 402     if (angle == 0 && awScale == 1.0f) {
 403         logFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
 404     } else {
 405         logFont.lfOutPrecision = OUT_TT_ONLY_PRECIS;
 406     }
 407     logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
 408     logFont.lfQuality = DEFAULT_QUALITY;
 409     logFont.lfPitchAndFamily = DEFAULT_PITCH;
 410 
 411     // Set style
 412     logFont.lfWeight = (style & java_awt_Font_BOLD) ? FW_BOLD : FW_NORMAL;
 413     logFont.lfItalic = (style & java_awt_Font_ITALIC) != 0;
 414     logFont.lfUnderline = 0;//(style & java_awt_Font_UNDERLINE) != 0;
 415 
 416     // Get point size
 417     logFont.lfHeight = -height;
 418 
 419     // Set font name
 420     WCHAR tmpname[80];
 421     wcscpy(tmpname, name);
 422     WCHAR* delimit = wcschr(tmpname, L',');
 423     if (delimit != NULL)
 424         *delimit = L'\0';  // terminate the string after the font name.
 425     // strip "Bold" and "Italic" from the end of the name
 426     strip_tail(tmpname,L""); //strip possible trailing whitespace
 427     strip_tail(tmpname,L"Italic");
 428     strip_tail(tmpname,L"Bold");
 429     wcscpy(&(logFont.lfFaceName[0]), tmpname);
 430     HFONT hFont = ::CreateFontIndirect(&logFont);
 431     DASSERT(hFont != NULL);
 432     // get a expanded or condensed version if its specified.
 433     if (awScale != 1.0f) {
 434         HDC hDC = ::GetDC(0);
 435         HFONT oldFont = (HFONT)::SelectObject(hDC, hFont);
 436         TEXTMETRIC tm;
 437         DWORD avgWidth;
 438         GetTextMetrics(hDC, &tm);
 439         oldFont = (HFONT)::SelectObject(hDC, oldFont);
 440         if (oldFont != NULL) { // should be the same as hFont
 441             VERIFY(::DeleteObject(oldFont));
 442         }
 443         avgWidth = tm.tmAveCharWidth;
 444         logFont.lfWidth = (LONG)((fabs)(avgWidth*awScale));
 445         hFont = ::CreateFontIndirect(&logFont);
 446         DASSERT(hFont != NULL);
 447         VERIFY(::ReleaseDC(0, hDC));
 448     }
 449 
 450     return hFont;
 451 }
 452 
 453 HFONT AwtFont::CreateHFont(WCHAR* name, int style, int height,
 454                            int angle, float awScale)
 455 {
 456     WCHAR longName[80];
 457     // 80 > (max face name(=30) + strlen("CHINESEBIG5_CHARSET"))
 458     // longName doesn't have to be printable.  So, it is OK not to convert.
 459 
 460     wsprintf(longName, L"%ls-%d-%d", name, style, height);
 461 
 462     HFONT hFont = NULL;
 463 
 464     // only cache & share unrotated, unexpanded/uncondensed fonts
 465     if (angle == 0 && awScale == 1.0f) {
 466         hFont = fontCache.Lookup(longName);
 467         if (hFont != NULL) {
 468             fontCache.IncRefCount(hFont);
 469             return hFont;
 470         }
 471     }
 472 
 473     hFont = CreateHFont_sub(name, style, height, angle, awScale);
 474     if (angle == 0 && awScale == 1.0f) {
 475         fontCache.Add(longName, hFont);
 476     }
 477     return hFont;
 478 }
 479 
 480 void AwtFont::Cleanup()
 481 {
 482     fontCache.Clear();
 483 }
 484 
 485 void AwtFont::SetupAscent(AwtFont* font)
 486 {
 487     HDC hDC = ::GetDC(0);
 488     DASSERT(hDC != NULL);
 489     HGDIOBJ oldFont = ::SelectObject(hDC, font->GetHFont());
 490 
 491     TEXTMETRIC metrics;
 492     VERIFY(::GetTextMetrics(hDC, &metrics));
 493     font->SetAscent(metrics.tmAscent);
 494 
 495     ::SelectObject(hDC, oldFont);
 496     VERIFY(::ReleaseDC(0, hDC));
 497 }
 498 
 499 void AwtFont::LoadMetrics(JNIEnv *env, jobject fontMetrics)
 500 {
 501     if (env->EnsureLocalCapacity(3) < 0)
 502         return;
 503     jintArray widths = env->NewIntArray(256);
 504     if (widths == NULL) {
 505         /* no local refs to delete yet. */
 506         return;
 507     }
 508     jobject font = env->GetObjectField(fontMetrics, AwtFont::fontID);
 509     AwtFont* awtFont = AwtFont::GetFont(env, font);
 510 
 511     HDC hDC = ::GetDC(0);
 512     DASSERT(hDC != NULL);
 513 
 514     HGDIOBJ oldFont = ::SelectObject(hDC, awtFont->GetHFont());
 515     TEXTMETRIC metrics;
 516     VERIFY(::GetTextMetrics(hDC, &metrics));
 517 
 518     awtFont->m_ascent = metrics.tmAscent;
 519 
 520     int ascent = metrics.tmAscent;
 521     int descent = metrics.tmDescent;
 522     int leading = metrics.tmExternalLeading;
 523     env->SetIntField(fontMetrics, AwtFont::ascentID, ascent);
 524     env->SetIntField(fontMetrics, AwtFont::descentID, descent);
 525     env->SetIntField(fontMetrics, AwtFont::leadingID, leading);
 526     env->SetIntField(fontMetrics, AwtFont::heightID, metrics.tmAscent +
 527                      metrics.tmDescent + leading);
 528     env->SetIntField(fontMetrics, AwtFont::maxAscentID, ascent);
 529     env->SetIntField(fontMetrics, AwtFont::maxDescentID, descent);
 530 
 531     int maxHeight =  ascent + descent + leading;
 532     env->SetIntField(fontMetrics, AwtFont::maxHeightID, maxHeight);
 533 
 534     int maxAdvance = metrics.tmMaxCharWidth;
 535     env->SetIntField(fontMetrics, AwtFont::maxAdvanceID, maxAdvance);
 536 
 537     awtFont->m_overhang = metrics.tmOverhang;
 538 
 539     jint intWidths[256];
 540     memset(intWidths, 0, 256 * sizeof(int));
 541     VERIFY(::GetCharWidth(hDC, metrics.tmFirstChar,
 542                           min(metrics.tmLastChar, 255),
 543                           (int *)&intWidths[metrics.tmFirstChar]));
 544     env->SetIntArrayRegion(widths, 0, 256, intWidths);
 545     env->SetObjectField(fontMetrics, AwtFont::widthsID, widths);
 546 
 547     // Get font metrics on remaining fonts (if multifont).
 548     for (int j = 1; j < awtFont->GetHFontNum(); j++) {
 549         ::SelectObject(hDC, awtFont->GetHFont(j));
 550         VERIFY(::GetTextMetrics(hDC, &metrics));
 551         env->SetIntField(fontMetrics, AwtFont::maxAscentID,
 552                          ascent = max(ascent, metrics.tmAscent));
 553         env->SetIntField(fontMetrics, AwtFont::maxDescentID,
 554                          descent = max(descent, metrics.tmDescent));
 555         env->SetIntField(fontMetrics, AwtFont::maxHeightID,
 556                          maxHeight = max(maxHeight,
 557                                          metrics.tmAscent +
 558                                          metrics.tmDescent +
 559                                          metrics.tmExternalLeading));
 560         env->SetIntField(fontMetrics, AwtFont::maxAdvanceID,
 561                          maxAdvance = max(maxAdvance, metrics.tmMaxCharWidth));
 562     }
 563 
 564     VERIFY(::SelectObject(hDC, oldFont));
 565     VERIFY(::ReleaseDC(0, hDC));
 566     env->DeleteLocalRef(font);
 567     env->DeleteLocalRef(widths);
 568 }
 569 
 570 SIZE AwtFont::TextSize(AwtFont* font, int columns, int rows)
 571 {
 572     HDC hDC = ::GetDC(0);
 573     DASSERT(hDC != NULL);
 574     HGDIOBJ oldFont = ::SelectObject(hDC, (font == NULL)
 575                                            ? ::GetStockObject(SYSTEM_FONT)
 576                                            : font->GetHFont());
 577 
 578     SIZE size;
 579     VERIFY(::GetTextExtentPoint(hDC,
 580         TEXT("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 52, &size));
 581 
 582     VERIFY(::SelectObject(hDC, oldFont));
 583     VERIFY(::ReleaseDC(0, hDC));
 584 
 585     size.cx = size.cx * columns / 52;
 586     size.cy = size.cy * rows;
 587     return size;
 588 }
 589 
 590 int AwtFont::getFontDescriptorNumber(JNIEnv *env, jobject font,
 591                                      jobject fontDescriptor)
 592 {
 593     int i, num;
 594     jobject refFontDescriptor;
 595     jobjectArray array;
 596 
 597     if (env->EnsureLocalCapacity(2) < 0)
 598         return 0;
 599 
 600     if (IsMultiFont(env, font)) {
 601         array = GetComponentFonts(env, font);
 602         num = env->GetArrayLength(array);
 603     } else {
 604         array = NULL;
 605         num = 0;
 606     }
 607 
 608     for (i = 0; i < num; i++){
 609         // Trying to identify the same FontDescriptor by comparing the
 610         // pointers.
 611         refFontDescriptor = env->GetObjectArrayElement(array, i);
 612         if (env->IsSameObject(refFontDescriptor, fontDescriptor)) {
 613             env->DeleteLocalRef(refFontDescriptor);
 614             env->DeleteLocalRef(array);
 615             return i;
 616         }
 617         env->DeleteLocalRef(refFontDescriptor);
 618     }
 619     env->DeleteLocalRef(array);
 620     return 0;   // Not found.  Use default.
 621 }
 622 
 623 /*
 624  * This is a faster version of the same function, which does most of
 625  * the work in Java.
 626  */
 627 SIZE  AwtFont::DrawStringSize_sub(jstring str, HDC hDC,
 628                                   jobject font, long x, long y, BOOL draw,
 629                                   UINT codePage)
 630 {
 631     SIZE size, temp;
 632     size.cx = size.cy = 0;
 633 
 634     if (str == NULL) {
 635         return size;
 636     }
 637 
 638     JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
 639     if (env->EnsureLocalCapacity(3) < 0)
 640         return size;
 641     jobjectArray array = 0;
 642 
 643     int arrayLength = 0;
 644 
 645     if (env->GetStringLength(str) == 0) {
 646         return size;
 647     }
 648 
 649     //Init AwtFont object, which will "create" a AwtFont object if necessry,
 650     //before calling makeconvertedMultiFontString(), otherwise, the FontDescriptor's
 651     //"useUnicode" field might not be initialized correctly (font in Menu Component,
 652     //for example").
 653     AwtFont* awtFont = AwtFont::GetFont(env, font);
 654 
 655     if (IsMultiFont(env, font)) {
 656         jobject peer = env->CallObjectMethod(font, AwtFont::peerMID);
 657         array =  (jobjectArray)(env->CallObjectMethod(
 658         peer, AwtFont::makeConvertedMultiFontStringMID, str));
 659         DASSERT(!safe_ExceptionOccurred(env));
 660 
 661         if (array != NULL) {
 662             arrayLength = env->GetArrayLength(array);
 663         }
 664         env->DeleteLocalRef(peer);
 665     } else {
 666         array = NULL;
 667         arrayLength = 0;
 668     }
 669 
 670     HFONT oldFont = (HFONT)::SelectObject(hDC, awtFont->GetHFont());
 671 
 672     if (arrayLength == 0) {
 673         int length = env->GetStringLength(str);
 674         LPCWSTR strW = JNU_GetStringPlatformChars(env, str, NULL);
 675         VERIFY(::SelectObject(hDC, awtFont->GetHFont()));
 676         if (AwtComponent::GetRTLReadingOrder()){
 677             VERIFY(!draw || ::ExtTextOut(hDC, x, y, ETO_RTLREADING, NULL,
 678                                           strW, length, NULL));
 679         } else {
 680             VERIFY(!draw || ::TextOut(hDC, x, y, strW, length));
 681         }
 682         VERIFY(::GetTextExtentPoint32(hDC, strW, length, &size));
 683         JNU_ReleaseStringPlatformChars(env, str, strW);
 684     } else {
 685         for (int i = 0; i < arrayLength; i = i + 2) {
 686             jobject fontDescriptor = env->GetObjectArrayElement(array, i);
 687             if (fontDescriptor == NULL) {
 688                 break;
 689             }
 690 
 691             jbyteArray convertedBytes =
 692                 (jbyteArray)env->GetObjectArrayElement(array, i + 1);
 693             if (convertedBytes == NULL) {
 694                 env->DeleteLocalRef(fontDescriptor);
 695                 break;
 696             }
 697 
 698             int fdIndex = getFontDescriptorNumber(env, font, fontDescriptor);
 699             VERIFY(::SelectObject(hDC, awtFont->GetHFont(fdIndex)));
 700 
 701             /*
 702              * The strange-looking code that follows this comment is
 703              * the result of upstream optimizations. In the array of
 704              * alternating font descriptor and buffers, the buffers
 705              * contain their length in the first four bytes, a la
 706              * Pascal arrays.
 707              *
 708              * Note: the buffer MUST be unsigned, or VC++ will sign
 709              * extend buflen and bad things will happen.
 710              */
 711             unsigned char* buffer = NULL;
 712             jboolean unicodeUsed = env->GetBooleanField(fontDescriptor, AwtFont::useUnicodeID);
 713             try {
 714                 buffer = (unsigned char *)
 715                     env->GetPrimitiveArrayCritical(convertedBytes, 0);
 716                 int buflen = (buffer[0] << 24) | (buffer[1] << 16) |
 717                     (buffer[2] << 8) | buffer[3];
 718 
 719                 DASSERT(buflen >= 0);
 720 
 721                 /*
 722                  * the offsetBuffer, on the other hand, must be signed because
 723                  * TextOutA and GetTextExtentPoint32A expect it.
 724                  */
 725                 char* offsetBuffer = (char *)(buffer + 4);
 726 
 727                 if (unicodeUsed) {
 728                     VERIFY(!draw || ::TextOutW(hDC, x, y, (LPCWSTR)offsetBuffer, buflen / 2));
 729                     VERIFY(::GetTextExtentPoint32W(hDC, (LPCWSTR)offsetBuffer, buflen / 2, &temp));
 730                 }
 731                 else {
 732                     VERIFY(!draw || ::TextOutA(hDC, x, y, offsetBuffer, buflen));
 733                     VERIFY(::GetTextExtentPoint32A(hDC, offsetBuffer, buflen, &temp));
 734                 }
 735             } catch (...) {
 736                 if (buffer != NULL) {
 737                     env->ReleasePrimitiveArrayCritical(convertedBytes, buffer,
 738                                                        0);
 739                 }
 740                 throw;
 741             }
 742             env->ReleasePrimitiveArrayCritical(convertedBytes, buffer, 0);
 743 
 744             if (awtFont->textAngle == 0) {
 745                 x += temp.cx;
 746             } else {
 747                // account for rotation of the text used in 2D printing.
 748                double degrees = 360.0 - (awtFont->textAngle/10.0);
 749                double rads = degrees/(180.0/3.1415926535);
 750                double dx = temp.cx * cos(rads);
 751                double dy = temp.cx * sin(rads);
 752                x += (long)floor(dx+0.5);
 753                y += (long)floor(dy+0.5);
 754             }
 755             size.cx += temp.cx;
 756             size.cy = (size.cy < temp.cy) ? temp.cy : size.cy;
 757             env->DeleteLocalRef(fontDescriptor);
 758             env->DeleteLocalRef(convertedBytes);
 759         }
 760     }
 761     env->DeleteLocalRef(array);
 762 
 763     VERIFY(::SelectObject(hDC, oldFont));
 764     return size;
 765 }
 766 
 767 /************************************************************************
 768  * WFontMetrics native methods
 769  */
 770 
 771 extern "C" {
 772 
 773 /*
 774  * Class:     sun_awt_windows_WFontMetrics
 775  * Method:    stringWidth
 776  * Signature: (Ljava/lang/String;)I
 777  */
 778 JNIEXPORT jint JNICALL
 779 Java_sun_awt_windows_WFontMetrics_stringWidth(JNIEnv *env, jobject self,
 780                                               jstring str)
 781 {
 782     TRY;
 783 
 784     if (str == NULL) {
 785         JNU_ThrowNullPointerException(env, "str argument");
 786         return NULL;
 787     }
 788     HDC hDC = ::GetDC(0);    DASSERT(hDC != NULL);
 789 
 790     jobject font = env->GetObjectField(self, AwtFont::fontID);
 791 
 792     long ret = AwtFont::getMFStringWidth(hDC, font, str);
 793     VERIFY(::ReleaseDC(0, hDC));
 794     return ret;
 795 
 796     CATCH_BAD_ALLOC_RET(0);
 797 }
 798 
 799 /*
 800  * Class:     sun_awt_windows_WFontMetrics
 801  * Method:    charsWidth
 802  * Signature: ([CII)I
 803  */
 804 JNIEXPORT jint JNICALL
 805 Java_sun_awt_windows_WFontMetrics_charsWidth(JNIEnv *env, jobject self,
 806                                              jcharArray str,
 807                                              jint off, jint len)
 808 {
 809     TRY;
 810 
 811     if (str == NULL) {
 812         JNU_ThrowNullPointerException(env, "str argument");
 813         return NULL;
 814     }
 815     if ((len < 0) || (off < 0) || (len + off > (env->GetArrayLength(str)))) {
 816         JNU_ThrowArrayIndexOutOfBoundsException(env, "off/len argument");
 817         return NULL;
 818     }
 819 
 820     jchar *strp = new jchar[len];
 821     env->GetCharArrayRegion(str, off, len, strp);
 822     jstring jstr = env->NewString(strp, len);
 823     jint result = Java_sun_awt_windows_WFontMetrics_stringWidth(env, self,
 824                                                                 jstr);
 825     delete [] strp;
 826     return result;
 827 
 828     CATCH_BAD_ALLOC_RET(0);
 829 }
 830 
 831 
 832 /*
 833  * Class:     sun_awt_windows_WFontMetrics
 834  * Method:    bytesWidth
 835  * Signature: ([BII)I
 836  */
 837 JNIEXPORT jint JNICALL
 838 Java_sun_awt_windows_WFontMetrics_bytesWidth(JNIEnv *env, jobject self,
 839                                              jbyteArray str,
 840                                              jint off, jint len)
 841 {
 842     TRY;
 843 
 844     if (str == NULL) {
 845         JNU_ThrowNullPointerException(env, "bytes argument");
 846         return NULL;
 847     }
 848     if ((len < 0) || (off < 0) || (len + off > (env->GetArrayLength(str)))) {
 849         JNU_ThrowArrayIndexOutOfBoundsException(env, "off or len argument");
 850         return NULL;
 851     }
 852     char *pStrBody = NULL;
 853     jint result = 0;
 854     try {
 855         jintArray array = (jintArray)env->GetObjectField(self,
 856                                                          AwtFont::widthsID);
 857         pStrBody = (char *)env->GetPrimitiveArrayCritical(str, 0);
 858         char *pStr = pStrBody + off;
 859 
 860         jint *widths = NULL;
 861         try {
 862             widths = (jint *)env->GetPrimitiveArrayCritical(array, 0);
 863 
 864             for (; len; len--) {
 865                 result += widths[*pStr++];
 866             }
 867         } catch (...) {
 868             if (widths != NULL) {
 869                 env->ReleasePrimitiveArrayCritical(array, widths, 0);
 870             }
 871             throw;
 872         }
 873 
 874         env->ReleasePrimitiveArrayCritical(array, widths, 0);
 875 
 876     } catch (...) {
 877         if (pStrBody != NULL) {
 878             env->ReleasePrimitiveArrayCritical(str, pStrBody, 0);
 879         }
 880         throw;
 881     }
 882 
 883     env->ReleasePrimitiveArrayCritical(str, pStrBody, 0);
 884     return result;
 885 
 886     CATCH_BAD_ALLOC_RET(0);
 887 }
 888 
 889 
 890 /*
 891  * Class:     sun_awt_windows_WFontMetrics
 892  * Method:    init
 893  * Signature: ()V
 894  */
 895 JNIEXPORT void JNICALL
 896 Java_sun_awt_windows_WFontMetrics_init(JNIEnv *env, jobject self)
 897 {
 898     TRY;
 899 
 900     jobject font = env->GetObjectField(self, AwtFont::fontID);
 901     if (font == NULL) {
 902         JNU_ThrowNullPointerException(env, "fontMetrics' font");
 903         return;
 904     }
 905     // This local variable is unused. Is there some subtle side-effect here?
 906     jlong pData = env->GetLongField(font, AwtFont::pDataID);
 907 
 908     AwtFont::LoadMetrics(env, self);
 909 
 910     CATCH_BAD_ALLOC;
 911 }
 912 
 913 
 914 /*
 915  * Class:     sun_awt_windows_WFontMetrics
 916  * Method:    initIDs
 917  * Signature: ()V
 918  */
 919 JNIEXPORT void JNICALL
 920 Java_sun_awt_windows_WFontMetrics_initIDs(JNIEnv *env, jclass cls)
 921 {
 922     TRY;
 923 
 924     AwtFont::widthsID = env->GetFieldID(cls, "widths", "[I");
 925     AwtFont::ascentID = env->GetFieldID(cls, "ascent", "I");
 926     AwtFont::descentID = env->GetFieldID(cls, "descent", "I");
 927     AwtFont::leadingID = env->GetFieldID(cls, "leading", "I");
 928     AwtFont::heightID = env->GetFieldID(cls, "height", "I");
 929     AwtFont::maxAscentID = env->GetFieldID(cls, "maxAscent", "I");
 930     AwtFont::maxDescentID = env->GetFieldID(cls, "maxDescent", "I");
 931     AwtFont::maxHeightID = env->GetFieldID(cls, "maxHeight", "I");
 932     AwtFont::maxAdvanceID = env->GetFieldID(cls, "maxAdvance", "I");
 933 
 934     DASSERT(AwtFont::widthsID != NULL);
 935     DASSERT(AwtFont::ascentID != NULL);
 936     DASSERT(AwtFont::descentID != NULL);
 937     DASSERT(AwtFont::leadingID != NULL);
 938     DASSERT(AwtFont::heightID != NULL);
 939     DASSERT(AwtFont::maxAscentID != NULL);
 940     DASSERT(AwtFont::maxDescentID != NULL);
 941     DASSERT(AwtFont::maxHeightID != NULL);
 942     DASSERT(AwtFont::maxAdvanceID != NULL);
 943 
 944     CATCH_BAD_ALLOC;
 945 }
 946 
 947 } /* extern "C" */
 948 
 949 
 950 /************************************************************************
 951  * java.awt.Font native methods
 952  */
 953 
 954 extern "C" {
 955 
 956 JNIEXPORT void JNICALL
 957 Java_java_awt_Font_initIDs(JNIEnv *env, jclass cls)
 958 {
 959     TRY;
 960 
 961     AwtFont::peerMID = env->GetMethodID(cls, "getPeer",
 962                                         "()Ljava/awt/peer/FontPeer;");
 963     AwtFont::pDataID = env->GetFieldID(cls, "pData", "J");
 964     AwtFont::nameID = env->GetFieldID(cls, "name", "Ljava/lang/String;");
 965     AwtFont::sizeID = env->GetFieldID(cls, "size", "I");
 966     AwtFont::styleID = env->GetFieldID(cls, "style", "I");
 967 
 968     AwtFont::getFontMID =
 969       env->GetStaticMethodID(cls, "getFont",
 970                              "(Ljava/lang/String;)Ljava/awt/Font;");
 971 
 972     DASSERT(AwtFont::peerMID != NULL);
 973     DASSERT(AwtFont::pDataID != NULL);
 974     DASSERT(AwtFont::nameID != NULL);
 975     DASSERT(AwtFont::sizeID != NULL);
 976     DASSERT(AwtFont::styleID != NULL);
 977 
 978     DASSERT(AwtFont::getFontMID != NULL);
 979 
 980     CATCH_BAD_ALLOC;
 981 }
 982 
 983 } /* extern "C" */
 984 
 985 
 986 /************************************************************************
 987  * java.awt.FontMetric native methods
 988  */
 989 
 990 extern "C" {
 991 
 992 JNIEXPORT void JNICALL
 993 Java_java_awt_FontMetrics_initIDs(JNIEnv *env, jclass cls)
 994 {
 995     TRY;
 996 
 997     AwtFont::fontID = env->GetFieldID(cls, "font", "Ljava/awt/Font;");
 998     AwtFont::getHeightMID = env->GetMethodID(cls, "getHeight", "()I");
 999 
1000     DASSERT(AwtFont::fontID);
1001     DASSERT(AwtFont::getHeightMID);
1002 
1003     CATCH_BAD_ALLOC;
1004 }
1005 
1006 } /* extern "C" */
1007 
1008 /************************************************************************
1009  * sun.awt.FontDescriptor native methods
1010  */
1011 
1012 extern "C" {
1013 
1014 JNIEXPORT void JNICALL
1015 Java_sun_awt_FontDescriptor_initIDs(JNIEnv *env, jclass cls)
1016 {
1017     TRY;
1018 
1019     AwtFont::nativeNameID = env->GetFieldID(cls, "nativeName",
1020                                             "Ljava/lang/String;");
1021     AwtFont::useUnicodeID = env->GetFieldID(cls, "useUnicode", "Z");
1022 
1023     DASSERT(AwtFont::nativeNameID != NULL);
1024     DASSERT(AwtFont::useUnicodeID != NULL);
1025 
1026     CATCH_BAD_ALLOC;
1027 }
1028 
1029 } /* extern "C" */
1030 
1031 
1032 /************************************************************************
1033  * sun.awt.PlatformFont native methods
1034  */
1035 
1036 extern "C" {
1037 
1038 JNIEXPORT void JNICALL
1039 Java_sun_awt_PlatformFont_initIDs(JNIEnv *env, jclass cls)
1040 {
1041     TRY;
1042 
1043     AwtFont::fontConfigID = env->GetFieldID(cls, "fontConfig", "Lsun/awt/FontConfiguration;");
1044     AwtFont::componentFontsID =
1045         env->GetFieldID(cls, "componentFonts", "[Lsun/awt/FontDescriptor;");
1046     AwtFont::makeConvertedMultiFontStringMID =
1047         env->GetMethodID(cls, "makeConvertedMultiFontString",
1048                          "(Ljava/lang/String;)[Ljava/lang/Object;");
1049 
1050     DASSERT(AwtFont::makeConvertedMultiFontStringMID != NULL);
1051     DASSERT(AwtFont::componentFontsID != NULL);
1052     DASSERT(AwtFont::fontConfigID != NULL);
1053 
1054     CATCH_BAD_ALLOC;
1055 }
1056 
1057 } /* extern "C" */
1058 
1059 
1060 /************************************************************************
1061  * sun.awt.windows.WFontPeer native methods
1062  */
1063 
1064 extern "C" {
1065 
1066 JNIEXPORT void JNICALL
1067 Java_sun_awt_windows_WFontPeer_initIDs(JNIEnv *env, jclass cls)
1068 {
1069     TRY;
1070 
1071     AwtFont::textComponentFontNameID = env->GetFieldID(cls, "textComponentFontName", "Ljava/lang/String;");
1072 
1073     DASSERT(AwtFont::textComponentFontNameID != NULL);
1074 
1075     CATCH_BAD_ALLOC;
1076 }
1077 
1078 } /* extern "C" */
1079 
1080 
1081 /************************************************************************
1082  * FontCache methods
1083  */
1084 
1085 void AwtFontCache::Add(WCHAR* name, HFONT font)
1086 {
1087     fontCache.m_head = new Item(name, font, fontCache.m_head);
1088 }
1089 
1090 HFONT AwtFontCache::Lookup(WCHAR* name)
1091 {
1092     Item* item = fontCache.m_head;
1093     Item* lastItem = NULL;
1094 
1095     while (item != NULL) {
1096         if (wcscmp(item->name, name) == 0) {
1097             return item->font;
1098         }
1099         lastItem = item;
1100         item = item->next;
1101     }
1102     return NULL;
1103 }
1104 
1105 BOOL AwtFontCache::Search(HFONT font)
1106 {
1107     Item* item = fontCache.m_head;
1108 
1109     while (item != NULL) {
1110         if (item->font == font) {
1111             return TRUE;
1112         }
1113         item = item->next;
1114     }
1115     return FALSE;
1116 }
1117 
1118 void AwtFontCache::Remove(HFONT font)
1119 {
1120     Item* item = fontCache.m_head;
1121     Item* lastItem = NULL;
1122 
1123     while (item != NULL) {
1124         if (item->font == font) {
1125             if (DecRefCount(item) <= 0){
1126                 if (lastItem == NULL) {
1127                     fontCache.m_head = item->next;
1128                 } else {
1129                 lastItem->next = item->next;
1130                 }
1131              delete item;
1132              }
1133              return;
1134         }
1135         lastItem = item;
1136         item = item->next;
1137     }
1138 }
1139 
1140 void AwtFontCache::Clear()
1141 {
1142     Item* item = m_head;
1143     Item* next;
1144 
1145     while (item != NULL) {
1146         next = item->next;
1147         delete item;
1148         item = next;
1149     }
1150 
1151     m_head = NULL;
1152 }
1153 
1154 /* NOTE: In the interlock calls below the return value is different
1155          depending on which version of windows. However, all versions
1156          return a 0 or less than value when the count gets there. Only
1157          under NT 4.0 & 98 does the value actaully represent the new value. */
1158 
1159 void AwtFontCache::IncRefCount(HFONT hFont){
1160     Item* item = fontCache.m_head;
1161 
1162     while (item != NULL){
1163 
1164         if (item->font == hFont){
1165             IncRefCount(item);
1166             return;
1167         }
1168         item = item->next;
1169     }
1170 }
1171 
1172 LONG AwtFontCache::IncRefCount(Item* item){
1173     LONG    newVal;
1174 
1175     if(NULL != item){
1176         newVal = InterlockedIncrement((long*)&item->refCount);
1177     }
1178     return(newVal);
1179 }
1180 
1181 LONG AwtFontCache::DecRefCount(Item* item){
1182     LONG    newVal;
1183 
1184     if(NULL != item){
1185         newVal = InterlockedDecrement((long*)&item->refCount);
1186     }
1187     return(newVal);
1188 }
1189 
1190 AwtFontCache::Item::Item(const WCHAR* s, HFONT f, AwtFontCache::Item* n )
1191 {
1192     name = _wcsdup(s);
1193     font = f;
1194     next = n;
1195     refCount = 1;
1196 }
1197 
1198 AwtFontCache::Item::~Item() {
1199   VERIFY(::DeleteObject(font));
1200   free(name);
1201 }
1202 
1203 /////////////////////////////////////////////////////////////////////////////
1204 // for canConvert native method of WDefaultFontCharset
1205 
1206 class CSegTableComponent
1207 {
1208 public:
1209     CSegTableComponent();
1210     virtual ~CSegTableComponent();
1211     virtual void Create(LPCWSTR name);
1212     virtual BOOL In(USHORT iChar) { DASSERT(FALSE); return FALSE; };
1213     LPWSTR GetFontName(){
1214         DASSERT(m_lpszFontName != NULL); return m_lpszFontName;
1215     };
1216 
1217 private:
1218     LPWSTR m_lpszFontName;
1219 };
1220 
1221 CSegTableComponent::CSegTableComponent()
1222 {
1223     m_lpszFontName = NULL;
1224 }
1225 
1226 CSegTableComponent::~CSegTableComponent()
1227 {
1228     if (m_lpszFontName != NULL) {
1229         free(m_lpszFontName);
1230         m_lpszFontName = NULL;
1231     }
1232 }
1233 
1234 void CSegTableComponent::Create(LPCWSTR name)
1235 {
1236     if (m_lpszFontName != NULL) {
1237         free(m_lpszFontName);
1238         m_lpszFontName = NULL;
1239     }
1240     m_lpszFontName = _wcsdup(name);
1241     DASSERT(m_lpszFontName);
1242 }
1243 
1244 #define CMAPHEX 0x70616d63 // = "cmap" (reversed)
1245 
1246 // CSegTable: Segment table describing character coverage for a font
1247 class CSegTable : public CSegTableComponent
1248 {
1249 public:
1250     CSegTable();
1251     virtual ~CSegTable();
1252     virtual BOOL In(USHORT iChar);
1253     BOOL HasCmap();
1254     virtual BOOL IsEUDC() { DASSERT(FALSE); return FALSE; };
1255 
1256 protected:
1257     virtual void GetData(DWORD dwOffset, LPVOID lpData, DWORD cbData) {
1258         DASSERT(FALSE); };
1259     void MakeTable();
1260     static void SwapShort(USHORT& p);
1261     static void SwapULong(ULONG& p);
1262 
1263 private:
1264     USHORT m_cSegCount;     // number of segments
1265     PUSHORT m_piStart;      // pointer to array of starting values
1266     PUSHORT m_piEnd;        // pointer to array of ending values (inclusive)
1267     USHORT m_cSeg;          // current segment (cache)
1268 };
1269 
1270 CSegTable::CSegTable()
1271 {
1272     m_cSegCount = 0;
1273     m_piStart = NULL;
1274     m_piEnd = NULL;
1275     m_cSeg = 0;
1276 }
1277 
1278 CSegTable::~CSegTable()
1279 {
1280     if (m_piStart != NULL)
1281         delete[] m_piStart;
1282     if (m_piEnd != NULL)
1283         delete[] m_piEnd;
1284 }
1285 
1286 #define OFFSETERROR 0
1287 
1288 void CSegTable::MakeTable()
1289 {
1290 typedef struct tagTABLE{
1291     USHORT  platformID;
1292     USHORT  encodingID;
1293     ULONG   offset;
1294 } TABLE, *PTABLE;
1295 
1296 typedef struct tagSUBTABLE{
1297     USHORT  format;
1298     USHORT  length;
1299     USHORT  version;
1300     USHORT  segCountX2;
1301     USHORT  searchRange;
1302     USHORT  entrySelector;
1303     USHORT  rangeShift;
1304 } SUBTABLE, *PSUBTABLE;
1305 
1306     USHORT aShort[2];
1307     (void) GetData(0, aShort, sizeof(aShort));
1308     USHORT nTables = aShort[1];
1309     SwapShort(nTables);
1310 
1311     // allocate buffer to hold encoding tables
1312     DWORD cbData = nTables * sizeof(TABLE);
1313     PTABLE pTables = new TABLE[nTables];
1314     PTABLE pTable = pTables;
1315 
1316     // get array of encoding tables.
1317     (void) GetData(4, (PBYTE) pTable, cbData);
1318 
1319     ULONG offsetFormat4 = OFFSETERROR;
1320     USHORT i;
1321     for (i = 0; i < nTables; i++) {
1322         SwapShort(pTable->encodingID);
1323         SwapShort(pTable->platformID);
1324         //for a Unicode font for Windows, platformID == 3, encodingID == 1
1325         if ((pTable->platformID == 3)&&(pTable->encodingID == 1)) {
1326             offsetFormat4 = pTable->offset;
1327             SwapULong(offsetFormat4);
1328             break;
1329         }
1330         pTable++;
1331     }
1332     delete[] pTables;
1333     if (offsetFormat4 == OFFSETERROR) {
1334         return;
1335     }
1336 //    DASSERT(offsetFormat4 != OFFSETERROR);
1337 
1338     SUBTABLE subTable;
1339     (void) GetData(offsetFormat4, &subTable, sizeof(SUBTABLE));
1340     SwapShort(subTable.format);
1341     SwapShort(subTable.segCountX2);
1342     DASSERT(subTable.format == 4);
1343 
1344     m_cSegCount = subTable.segCountX2/2;
1345 
1346     // read in the array of segment end values
1347     m_piEnd = new USHORT[m_cSegCount];
1348 
1349     ULONG offset = offsetFormat4
1350         + sizeof(SUBTABLE); //skip constant # bytes in subtable
1351     cbData = m_cSegCount * sizeof(USHORT);
1352     (void) GetData(offset, m_piEnd, cbData);
1353     for (i = 0; i < m_cSegCount; i++)
1354         SwapShort(m_piEnd[i]);
1355     DASSERT(m_piEnd[m_cSegCount-1] == 0xffff);
1356 
1357     // read in the array of segment start values
1358     try {
1359         m_piStart = new USHORT[m_cSegCount];
1360     } catch (std::bad_alloc&) {
1361         delete [] m_piEnd;
1362         m_piEnd = NULL;
1363         throw;
1364     }
1365 
1366     offset += cbData        //skip SegEnd array
1367         + sizeof(USHORT);   //skip reservedPad
1368     (void) GetData(offset, m_piStart, cbData);
1369     for (i = 0; i < m_cSegCount; i++)
1370         SwapShort(m_piStart[i]);
1371     DASSERT(m_piStart[m_cSegCount-1] == 0xffff);
1372 }
1373 
1374 BOOL CSegTable::In(USHORT iChar)
1375 {
1376     if (!HasCmap()) {
1377         return FALSE;
1378     }
1379 //    DASSERT(m_piStart);
1380 //    DASSERT(m_piEnd);
1381 
1382     if (iChar > m_piEnd[m_cSeg]) {
1383         for (; (m_cSeg < m_cSegCount)&&(iChar > m_piEnd[m_cSeg]); m_cSeg++);
1384     } else if (iChar < m_piStart[m_cSeg]) {
1385         for (; (m_cSeg > 0)&&(iChar < m_piStart[m_cSeg]); m_cSeg--);
1386     }
1387 
1388     if ((iChar <= m_piEnd[m_cSeg])&&(iChar >= m_piStart[m_cSeg])&&(iChar != 0xffff))
1389         return TRUE;
1390     else
1391         return FALSE;
1392 }
1393 
1394 inline BOOL CSegTable::HasCmap()
1395 {
1396     return (((m_piEnd)&&(m_piStart)) ? TRUE : FALSE);
1397 }
1398 
1399 inline void CSegTable::SwapShort(USHORT& p)
1400 {
1401     SHORT temp;
1402 
1403     temp = (SHORT)(HIBYTE(p) + (LOBYTE(p) << 8));
1404     p = temp;
1405 }
1406 
1407 inline void CSegTable::SwapULong(ULONG& p)
1408 {
1409     ULONG temp;
1410 
1411     temp = (LONG) ((BYTE) p);
1412     temp <<= 8;
1413     p >>= 8;
1414 
1415     temp += (LONG) ((BYTE) p);
1416     temp <<= 8;
1417     p >>= 8;
1418 
1419     temp += (LONG) ((BYTE) p);
1420     temp <<= 8;
1421     p >>= 8;
1422 
1423     temp += (LONG) ((BYTE) p);
1424     p = temp;
1425 }
1426 
1427 class CStdSegTable : public CSegTable
1428 {
1429 public:
1430     CStdSegTable();
1431     virtual ~CStdSegTable();
1432     BOOL IsEUDC() { return FALSE; };
1433     virtual void Create(LPCWSTR name);
1434 
1435 protected:
1436     void GetData(DWORD dwOffset, LPVOID lpData, DWORD cbData);
1437 
1438 private:
1439     HDC m_hTmpDC;
1440 };
1441 
1442 CStdSegTable::CStdSegTable()
1443 {
1444     m_hTmpDC = NULL;
1445 }
1446 
1447 CStdSegTable::~CStdSegTable()
1448 {
1449     DASSERT(m_hTmpDC == NULL);
1450 }
1451 
1452 inline void CStdSegTable::GetData(DWORD dwOffset,
1453                                   LPVOID lpData, DWORD cbData)
1454 {
1455     DASSERT(m_hTmpDC);
1456     DWORD nBytes =
1457         ::GetFontData(m_hTmpDC, CMAPHEX, dwOffset, lpData, cbData);
1458     DASSERT(nBytes != GDI_ERROR);
1459 }
1460 
1461 void CStdSegTable::Create(LPCWSTR name)
1462 {
1463     CSegTableComponent::Create(name);
1464 
1465     HWND hWnd = ::GetDesktopWindow();
1466     DASSERT(hWnd);
1467     m_hTmpDC = ::GetWindowDC(hWnd);
1468     DASSERT(m_hTmpDC);
1469 
1470     HFONT hFont = CreateHFont_sub(name, 0, 20);
1471 
1472     HFONT hOldFont = (HFONT)::SelectObject(m_hTmpDC, hFont);
1473     DASSERT(hOldFont);
1474 
1475     (void) MakeTable();
1476 
1477     VERIFY(::SelectObject(m_hTmpDC, hOldFont));
1478     VERIFY(::DeleteObject(hFont));
1479     VERIFY(::ReleaseDC(hWnd, m_hTmpDC) != 0);
1480     m_hTmpDC = NULL;
1481 }
1482 
1483 class CEUDCSegTable : public CSegTable
1484 {
1485 public:
1486     CEUDCSegTable();
1487     virtual ~CEUDCSegTable();
1488     BOOL IsEUDC() { return TRUE; };
1489     virtual void Create(LPCWSTR name);
1490 
1491 protected:
1492     void GetData(DWORD dwOffset, LPVOID lpData, DWORD cbData);
1493 
1494 private:
1495     HANDLE m_hTmpFile;
1496     ULONG m_hTmpCMapOffset;
1497 };
1498 
1499 CEUDCSegTable::CEUDCSegTable()
1500 {
1501     m_hTmpFile = NULL;
1502     m_hTmpCMapOffset = 0;
1503 }
1504 
1505 CEUDCSegTable::~CEUDCSegTable()
1506 {
1507     DASSERT(m_hTmpFile == NULL);
1508     DASSERT(m_hTmpCMapOffset == 0);
1509 }
1510 
1511 inline void CEUDCSegTable::GetData(DWORD dwOffset,
1512                                    LPVOID lpData, DWORD cbData)
1513 {
1514     DASSERT(m_hTmpFile);
1515     DASSERT(m_hTmpCMapOffset);
1516     ::SetFilePointer(m_hTmpFile, m_hTmpCMapOffset + dwOffset,
1517         NULL, FILE_BEGIN);
1518     DWORD dwRead;
1519     VERIFY(::ReadFile(m_hTmpFile, lpData, cbData, &dwRead, NULL));
1520     DASSERT(dwRead == cbData);
1521 }
1522 
1523 void CEUDCSegTable::Create(LPCWSTR name)
1524 {
1525 typedef struct tagHEAD{
1526     FIXED   sfnt_version;
1527     USHORT  numTables;
1528     USHORT  searchRange;
1529     USHORT  entrySelector;
1530     USHORT  rangeShift;
1531 } HEAD, *PHEAD;
1532 
1533 typedef struct tagENTRY{
1534     ULONG   tag;
1535     ULONG   checkSum;
1536     ULONG   offset;
1537     ULONG   length;
1538 } ENTRY, *PENTRY;
1539 
1540     CSegTableComponent::Create(name);
1541 
1542     // create EUDC font file and make EUDCSegTable
1543     // after wrapper function for CreateFileW, we use only CreateFileW
1544     m_hTmpFile = ::CreateFile(name, GENERIC_READ,
1545                                FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1546     if (m_hTmpFile == INVALID_HANDLE_VALUE){
1547         m_hTmpFile = NULL;
1548         return;
1549     }
1550 
1551     HEAD head;
1552     DWORD dwRead;
1553     VERIFY(::ReadFile(m_hTmpFile, &head, sizeof(head), &dwRead, NULL));
1554     DASSERT(dwRead == sizeof(HEAD));
1555     SwapShort(head.numTables);
1556     ENTRY entry;
1557     for (int i = 0; i < head.numTables; i++){
1558         VERIFY(::ReadFile(m_hTmpFile, &entry, sizeof(entry), &dwRead, NULL));
1559         DASSERT(dwRead == sizeof(ENTRY));
1560         if (entry.tag == CMAPHEX)
1561             break;
1562     }
1563     DASSERT(entry.tag == CMAPHEX);
1564     SwapULong(entry.offset);
1565     m_hTmpCMapOffset = entry.offset;
1566 
1567     (void) MakeTable();
1568 
1569     m_hTmpCMapOffset = 0;
1570     VERIFY(::CloseHandle(m_hTmpFile));
1571     m_hTmpFile = NULL;
1572 }
1573 
1574 class CSegTableManagerComponent
1575 {
1576 public:
1577     CSegTableManagerComponent();
1578     ~CSegTableManagerComponent();
1579 
1580 protected:
1581     void MakeBiggerTable();
1582     CSegTableComponent **m_tables;
1583     int m_nTable;
1584     int m_nMaxTable;
1585 };
1586 
1587 #define TABLENUM 20
1588 
1589 CSegTableManagerComponent::CSegTableManagerComponent()
1590 {
1591     m_nTable = 0;
1592     m_nMaxTable = TABLENUM;
1593     m_tables = new CSegTableComponent*[m_nMaxTable];
1594 }
1595 
1596 CSegTableManagerComponent::~CSegTableManagerComponent()
1597 {
1598     for (int i = 0; i < m_nTable; i++) {
1599         DASSERT(m_tables[i]);
1600         delete m_tables[i];
1601     }
1602     delete [] m_tables;
1603     m_tables = NULL;
1604 }
1605 
1606 void CSegTableManagerComponent::MakeBiggerTable()
1607 {
1608     CSegTableComponent **tables =
1609         new CSegTableComponent*[m_nMaxTable + TABLENUM];
1610 
1611     for (int i = 0; i < m_nMaxTable; i++)
1612         tables[i] = m_tables[i];
1613 
1614     delete[] m_tables;
1615 
1616     m_tables = tables;
1617     m_nMaxTable += TABLENUM;
1618 }
1619 
1620 class CSegTableManager : public CSegTableManagerComponent
1621 {
1622 public:
1623     CSegTable* GetTable(LPCWSTR lpszFontName, BOOL fEUDC);
1624 };
1625 
1626 CSegTable* CSegTableManager::GetTable(LPCWSTR lpszFontName, BOOL fEUDC)
1627 {
1628     for (int i = 0; i < m_nTable; i++) {
1629         if ((((CSegTable*)m_tables[i])->IsEUDC() == fEUDC) &&
1630             (wcscmp(m_tables[i]->GetFontName(),lpszFontName) == 0))
1631             return (CSegTable*) m_tables[i];
1632     }
1633 
1634     if (m_nTable == m_nMaxTable) {
1635         (void) MakeBiggerTable();
1636     }
1637     DASSERT(m_nTable < m_nMaxTable);
1638 
1639     if (!fEUDC) {
1640         m_tables[m_nTable] = new CStdSegTable;
1641     } else {
1642         m_tables[m_nTable] = new CEUDCSegTable;
1643     }
1644     m_tables[m_nTable]->Create(lpszFontName);
1645     return (CSegTable*) m_tables[m_nTable++];
1646 }
1647 
1648 CSegTableManager g_segTableManager;
1649 
1650 class CCombinedSegTable : public CSegTableComponent
1651 {
1652 public:
1653     CCombinedSegTable();
1654     void Create(LPCWSTR name);
1655     BOOL In(USHORT iChar);
1656 
1657 private:
1658     LPSTR GetCodePageSubkey();
1659     void GetEUDCFileName(LPWSTR lpszFileName, int cchFileName);
1660     static char m_szCodePageSubkey[16];
1661     static WCHAR m_szDefaultEUDCFile[_MAX_PATH];
1662     static BOOL m_fEUDCSubKeyExist;
1663     static BOOL m_fTTEUDCFileExist;
1664     CStdSegTable* m_pStdSegTable;
1665     CEUDCSegTable* m_pEUDCSegTable;
1666 };
1667 
1668 char CCombinedSegTable::m_szCodePageSubkey[16] = "";
1669 
1670 WCHAR CCombinedSegTable::m_szDefaultEUDCFile[_MAX_PATH] = L"";
1671 
1672 BOOL CCombinedSegTable::m_fEUDCSubKeyExist = TRUE;
1673 
1674 BOOL CCombinedSegTable::m_fTTEUDCFileExist = TRUE;
1675 
1676 CCombinedSegTable::CCombinedSegTable()
1677 {
1678     m_pStdSegTable = NULL;
1679     m_pEUDCSegTable = NULL;
1680 }
1681 
1682 #include <locale.h>
1683 LPSTR CCombinedSegTable::GetCodePageSubkey()
1684 {
1685     if (strlen(m_szCodePageSubkey) > 0) {
1686         return m_szCodePageSubkey;
1687     }
1688 
1689     LPSTR lpszLocale = setlocale(LC_CTYPE, "");
1690     // cf lpszLocale = "Japanese_Japan.932"
1691     if (lpszLocale == NULL) {
1692         return NULL;
1693     }
1694     LPSTR lpszCP = strchr(lpszLocale, (int) '.');
1695     if (lpszCP == NULL) {
1696         return NULL;
1697     }
1698     lpszCP++; // cf lpszCP = "932"
1699 
1700     char szSubKey[80];
1701     strcpy(szSubKey, "EUDC\\");
1702     strcpy(&(szSubKey[strlen(szSubKey)]), lpszCP);
1703     strcpy(m_szCodePageSubkey, szSubKey);
1704     return m_szCodePageSubkey;
1705 }
1706 
1707 void CCombinedSegTable::GetEUDCFileName(LPWSTR lpszFileName, int cchFileName)
1708 {
1709     if (m_fEUDCSubKeyExist == FALSE)
1710         return;
1711 
1712     // get filename of typeface-specific TureType EUDC font
1713     LPSTR lpszSubKey = GetCodePageSubkey();
1714     if (lpszSubKey == NULL) {
1715         m_fEUDCSubKeyExist = FALSE;
1716         return; // can not get codepage information
1717     }
1718     HKEY hRootKey = HKEY_CURRENT_USER;
1719     HKEY hKey;
1720     LONG lRet = ::RegOpenKeyExA(hRootKey, lpszSubKey, 0, KEY_ALL_ACCESS, &hKey);
1721     if (lRet != ERROR_SUCCESS) {
1722         m_fEUDCSubKeyExist = FALSE;
1723         return; // no EUDC font
1724     }
1725 
1726     // get EUDC font file name
1727     WCHAR szFamilyName[80];
1728     wcscpy(szFamilyName, GetFontName());
1729     WCHAR* delimit = wcschr(szFamilyName, L',');
1730     if (delimit != NULL)
1731         *delimit = L'\0';
1732     DWORD dwType;
1733     UCHAR szFileName[_MAX_PATH];
1734     ::ZeroMemory(szFileName, sizeof(szFileName));
1735     DWORD dwBytes = sizeof(szFileName);
1736     // try Typeface-specific EUDC font
1737     char szTmpName[80];
1738     VERIFY(::WideCharToMultiByte(CP_ACP, 0, szFamilyName, -1,
1739         szTmpName, sizeof(szTmpName), NULL, NULL));
1740     LONG lStatus = ::RegQueryValueExA(hKey, (LPCSTR) szTmpName,
1741         NULL, &dwType, szFileName, &dwBytes);
1742     BOOL fUseDefault = FALSE;
1743     if (lStatus != ERROR_SUCCESS){ // try System default EUDC font
1744         if (m_fTTEUDCFileExist == FALSE)
1745             return;
1746         if (wcslen(m_szDefaultEUDCFile) > 0) {
1747             wcscpy(lpszFileName, m_szDefaultEUDCFile);
1748             return;
1749         }
1750         char szDefault[] = "SystemDefaultEUDCFont";
1751         lStatus = ::RegQueryValueExA(hKey, (LPCSTR) szDefault,
1752             NULL, &dwType, szFileName, &dwBytes);
1753         fUseDefault = TRUE;
1754         if (lStatus != ERROR_SUCCESS) {
1755             m_fTTEUDCFileExist = FALSE;
1756             // This font is associated with no EUDC font
1757             // and there is no system default EUDC font
1758             return;
1759         }
1760     }
1761 
1762     if (strcmp((LPCSTR) szFileName, "userfont.fon") == 0) {
1763         // This font is associated with no EUDC font
1764         // and the system default EUDC font is not TrueType
1765         m_fTTEUDCFileExist = FALSE;
1766         return;
1767     }
1768 
1769     DASSERT(strlen((LPCSTR)szFileName) > 0);
1770     VERIFY(::MultiByteToWideChar(CP_ACP, 0,
1771         (LPCSTR)szFileName, -1, lpszFileName, cchFileName) != 0);
1772     if (fUseDefault)
1773         wcscpy(m_szDefaultEUDCFile, lpszFileName);
1774 }
1775 
1776 void CCombinedSegTable::Create(LPCWSTR name)
1777 {
1778     CSegTableComponent::Create(name);
1779 
1780     m_pStdSegTable =
1781         (CStdSegTable*) g_segTableManager.GetTable(name, FALSE/*not EUDC*/);
1782     WCHAR szEUDCFileName[_MAX_PATH];
1783     ::ZeroMemory(szEUDCFileName, sizeof(szEUDCFileName));
1784     (void) GetEUDCFileName(szEUDCFileName,
1785         sizeof(szEUDCFileName)/sizeof(WCHAR));
1786     if (wcslen(szEUDCFileName) > 0) {
1787         m_pEUDCSegTable = (CEUDCSegTable*) g_segTableManager.GetTable(
1788             szEUDCFileName, TRUE/*EUDC*/);
1789         if (m_pEUDCSegTable->HasCmap() == FALSE)
1790             m_pEUDCSegTable = NULL;
1791     }
1792 }
1793 
1794 BOOL CCombinedSegTable::In(USHORT iChar)
1795 {
1796     DASSERT(m_pStdSegTable);
1797     if (m_pStdSegTable->In(iChar))
1798         return TRUE;
1799 
1800     if (m_pEUDCSegTable != NULL)
1801         return m_pEUDCSegTable->In(iChar);
1802 
1803     return FALSE;
1804 }
1805 
1806 class CCombinedSegTableManager : public CSegTableManagerComponent
1807 {
1808 public:
1809     CCombinedSegTable* GetTable(LPCWSTR lpszFontName);
1810 };
1811 
1812 CCombinedSegTable* CCombinedSegTableManager::GetTable(LPCWSTR lpszFontName)
1813 {
1814     for (int i = 0; i < m_nTable; i++) {
1815         if (wcscmp(m_tables[i]->GetFontName(),lpszFontName) == 0)
1816             return (CCombinedSegTable*) m_tables[i];
1817     }
1818 
1819     if (m_nTable == m_nMaxTable) {
1820         (void) MakeBiggerTable();
1821     }
1822     DASSERT(m_nTable < m_nMaxTable);
1823 
1824     m_tables[m_nTable] = new CCombinedSegTable;
1825     m_tables[m_nTable]->Create(lpszFontName);
1826 
1827     return (CCombinedSegTable*) m_tables[m_nTable++];
1828 }
1829 
1830 
1831 /************************************************************************
1832  * WDefaultFontCharset native methos
1833  */
1834 
1835 extern "C" {
1836 
1837 JNIEXPORT void JNICALL
1838 Java_sun_awt_windows_WDefaultFontCharset_initIDs(JNIEnv *env, jclass cls)
1839 {
1840     TRY;
1841 
1842     AwtFont::fontNameID = env->GetFieldID(cls, "fontName",
1843                                           "Ljava/lang/String;");
1844     DASSERT(AwtFont::fontNameID != NULL);
1845 
1846     CATCH_BAD_ALLOC;
1847 }
1848 
1849 
1850 /*
1851  * !!!!!!!!!!!!!!!!!!!! this does not work. I am not sure why, but
1852  * when active, this will reliably crash HJ, with no hope of debugging
1853  * for java.  It doesn't seem to crash the _g version.
1854  * !!!!!!!!!!!!!!!!!!!!!!!!!!!!
1855  *
1856  * I suspect may be running out of C stack: see alloca in
1857  * JNI_GET_STRING, the alloca in it.
1858  *
1859  * (the method is prefixed with XXX so that the linker won't find it) */
1860 JNIEXPORT jboolean JNICALL
1861 Java_sun_awt_windows_WDefaultFontCharset_canConvert(JNIEnv *env, jobject self,
1862                                                     jchar ch)
1863 {
1864     TRY;
1865 
1866     static CCombinedSegTableManager tableManager;
1867 
1868     jstring fontName = (jstring)env->GetObjectField(self, AwtFont::fontNameID);
1869     DASSERT(fontName != NULL);
1870     LPCWSTR fontNameW = JNU_GetStringPlatformChars(env, fontName, NULL);
1871     CCombinedSegTable* pTable = tableManager.GetTable(fontNameW);
1872     JNU_ReleaseStringPlatformChars(env, fontName, fontNameW);
1873     return (pTable->In((USHORT) ch) ? JNI_TRUE : JNI_FALSE);
1874 
1875     CATCH_BAD_ALLOC_RET(FALSE);
1876 }
1877 
1878 } /* extern "C" */