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