alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (awt_Font.cpp)

This example Java source code file (awt_Font.cpp) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

bool, csegtable, dassert, dword, false, hfont, jnicall, jniexport, null, try, ushort, verify

The awt_Font.cpp Java example source code

/*
 * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

#include "awt.h"
#include <math.h>
#include "jlong.h"
#include "awt_Font.h"
#include "awt_Toolkit.h"

#include "java_awt_Font.h"
#include "java_awt_FontMetrics.h"
#include "java_awt_Dimension.h"

#include "sun_awt_FontDescriptor.h"
#include "sun_awt_windows_WDefaultFontCharset.h"
#include "sun_awt_windows_WFontPeer.h"
#include "awt_Component.h"
#include "Disposer.h"

/* IMPORTANT! Read the README.JNI file for notes on JNI converted AWT code.
 */

AwtFontCache fontCache;

extern jboolean IsMultiFont(JNIEnv *env, jobject obj)
{
    if (obj == NULL) {
        return JNI_FALSE;
    }
    if (env->EnsureLocalCapacity(2))
        return JNI_FALSE;
    jobject peer = env->CallObjectMethod(obj, AwtFont::peerMID);
    if (peer == NULL) {
        return JNI_FALSE;
    }
    jobject fontConfig = env->GetObjectField(peer, AwtFont::fontConfigID);
    jboolean result = fontConfig != NULL;
    env->DeleteLocalRef(peer);
    env->DeleteLocalRef(fontConfig);
    return result;
}

extern jstring GetTextComponentFontName(JNIEnv *env, jobject font)
{
    DASSERT(font != NULL);
    if (env->EnsureLocalCapacity(2)) {
        return NULL;
    }
    jobject peer = env->CallObjectMethod(font, AwtFont::peerMID);
    DASSERT(peer != NULL);
    jstring textComponentFontName =
            (jstring) env->GetObjectField(peer, AwtFont::textComponentFontNameID);
    env->DeleteLocalRef(peer);
    return textComponentFontName;
}

/************************************************************************
 * AwtFont fields
 */

/* sun.awt.windows.WFontMetrics fields */
jfieldID AwtFont::widthsID;
jfieldID AwtFont::ascentID;
jfieldID AwtFont::descentID;
jfieldID AwtFont::leadingID;
jfieldID AwtFont::heightID;
jfieldID AwtFont::maxAscentID;
jfieldID AwtFont::maxDescentID;
jfieldID AwtFont::maxHeightID;
jfieldID AwtFont::maxAdvanceID;

/* java.awt.FontDescriptor fields */
jfieldID AwtFont::nativeNameID;
jfieldID AwtFont::useUnicodeID;

/* java.awt.Font fields */
jfieldID AwtFont::pDataID;
jfieldID AwtFont::nameID;
jfieldID AwtFont::sizeID;
jfieldID AwtFont::styleID;

/* java.awt.FontMetrics fields */
jfieldID AwtFont::fontID;

/* sun.awt.PlatformFont fields */
jfieldID AwtFont::fontConfigID;
jfieldID AwtFont::componentFontsID;

/* sun.awt.windows.WFontPeer fields */
jfieldID AwtFont::textComponentFontNameID;

/* sun.awt.windows.WDefaultFontCharset fields */
jfieldID AwtFont::fontNameID;

/* java.awt.Font methods */
jmethodID AwtFont::peerMID;

/* sun.awt.PlatformFont methods */
jmethodID AwtFont::makeConvertedMultiFontStringMID;

/* sun.awt.PlatformFont methods */
jmethodID AwtFont::getFontMID;

/* java.awt.FontMetrics methods */
jmethodID AwtFont::getHeightMID;


/************************************************************************
 * AwtFont methods
 */
AwtFont::AwtFont(int num, JNIEnv *env, jobject javaFont)
{
    if (num == 0) {  // not multi-font
        num = 1;
    }

    m_hFontNum = num;
    m_hFont = new HFONT[num];

    for (int i = 0; i < num; i++) {
        m_hFont[i] = NULL;
    }

    m_textInput = -1;
    m_ascent = -1;
    m_overhang = 0;
}

AwtFont::~AwtFont()
{
    delete[] m_hFont;
}

void AwtFont::Dispose() {
    for (int i = 0; i < m_hFontNum; i++) {
        HFONT font = m_hFont[i];
        if (font != NULL && fontCache.Search(font)) {
            fontCache.Remove(font);
            /*  NOTE: delete of windows HFONT happens in FontCache::Remove
                      only when the final reference to the font is disposed */
        } else if (font != NULL) {
            // if font was not in cache, its not shared and we delete it now
            DASSERT(::GetObjectType(font) == OBJ_FONT);
            VERIFY(::DeleteObject(font));
        }
        m_hFont[i] = NULL;
    }

    AwtObject::Dispose();
}

static void pDataDisposeMethod(JNIEnv *env, jlong pData)
{
    TRY_NO_VERIFY;

    AwtObject::_Dispose((PDATA)pData);

    CATCH_BAD_ALLOC;
}

AwtFont* AwtFont::GetFont(JNIEnv *env, jobject font,
                          jint angle, jfloat awScale)
{
    jlong pData = env->GetLongField(font, AwtFont::pDataID);
    AwtFont* awtFont = (AwtFont*)jlong_to_ptr(pData);

    if (awtFont != NULL) {
        return awtFont;
    }

    awtFont = Create(env, font, angle, awScale);

    env->SetLongField(font, AwtFont::pDataID,
        reinterpret_cast<jlong>(awtFont));
    return awtFont;
}

// Get suitable CHARSET from charset string provided by font configuration.
static int GetNativeCharset(LPCWSTR name)
{
    if (wcsstr(name, L"ANSI_CHARSET"))
        return ANSI_CHARSET;
    if (wcsstr(name, L"DEFAULT_CHARSET"))
        return DEFAULT_CHARSET;
    if (wcsstr(name, L"SYMBOL_CHARSET") || wcsstr(name, L"WingDings"))
        return SYMBOL_CHARSET;
    if (wcsstr(name, L"SHIFTJIS_CHARSET"))
        return SHIFTJIS_CHARSET;
    if (wcsstr(name, L"GB2312_CHARSET"))
        return GB2312_CHARSET;
    if (wcsstr(name, L"HANGEUL_CHARSET"))
        return HANGEUL_CHARSET;
    if (wcsstr(name, L"CHINESEBIG5_CHARSET"))
        return CHINESEBIG5_CHARSET;
    if (wcsstr(name, L"OEM_CHARSET"))
        return OEM_CHARSET;
    if (wcsstr(name, L"JOHAB_CHARSET"))
        return JOHAB_CHARSET;
    if (wcsstr(name, L"HEBREW_CHARSET"))
        return HEBREW_CHARSET;
    if (wcsstr(name, L"ARABIC_CHARSET"))
        return ARABIC_CHARSET;
    if (wcsstr(name, L"GREEK_CHARSET"))
        return GREEK_CHARSET;
    if (wcsstr(name, L"TURKISH_CHARSET"))
        return TURKISH_CHARSET;
    if (wcsstr(name, L"VIETNAMESE_CHARSET"))
        return VIETNAMESE_CHARSET;
    if (wcsstr(name, L"THAI_CHARSET"))
        return THAI_CHARSET;
    if (wcsstr(name, L"EASTEUROPE_CHARSET"))
        return EASTEUROPE_CHARSET;
    if (wcsstr(name, L"RUSSIAN_CHARSET"))
        return RUSSIAN_CHARSET;
    if (wcsstr(name, L"MAC_CHARSET"))
        return MAC_CHARSET;
    if (wcsstr(name, L"BALTIC_CHARSET"))
        return BALTIC_CHARSET;
    return ANSI_CHARSET;
}

AwtFont* AwtFont::Create(JNIEnv *env, jobject font, jint angle, jfloat awScale)
{
    int fontSize = env->GetIntField(font, AwtFont::sizeID);
    int fontStyle = env->GetIntField(font, AwtFont::styleID);

    AwtFont* awtFont = NULL;
    jobjectArray compFont = NULL;
    int cfnum;

    try {
        if (env->EnsureLocalCapacity(3) < 0)
            return 0;

        if (IsMultiFont(env, font)) {
            compFont = GetComponentFonts(env, font);
            cfnum = env->GetArrayLength(compFont);
        } else {
            compFont = NULL;
            cfnum = 0;
        }

        LPCWSTR wName;

        awtFont = new AwtFont(cfnum, env, font);

        awtFont->textAngle = angle;
        awtFont->awScale = awScale;

        if (cfnum > 0) {
            // Ask peer class for the text component font name
            jstring jTextComponentFontName = GetTextComponentFontName(env, font);
            LPCWSTR textComponentFontName = JNU_GetStringPlatformChars(env, jTextComponentFontName, NULL);

            awtFont->m_textInput = -1;
            for (int i = 0; i < cfnum; i++) {
                // nativeName is a pair of platform fontname and its charset
                // tied with a comma; "Times New Roman,ANSI_CHARSET".
                jobject fontDescriptor = env->GetObjectArrayElement(compFont,
                                                                    i);
                jstring nativeName =
                    (jstring)env->GetObjectField(fontDescriptor,
                                                 AwtFont::nativeNameID);
                wName = JNU_GetStringPlatformChars(env, nativeName, NULL);
                DASSERT(wName);

                //On NT platforms, if the font is not Symbol or Dingbats
                //use "W" version of Win32 APIs directly, info the FontDescription
                //no need to convert characters from Unicode to locale encodings.
                if (GetNativeCharset(wName) != SYMBOL_CHARSET) {
                    env->SetBooleanField(fontDescriptor, AwtFont::useUnicodeID, TRUE);
                }

                // Check to see if this font is suitable for input
                // on AWT TextComponent
                if ((awtFont->m_textInput == -1) &&
                        (textComponentFontName != NULL) &&
                        (wcscmp(wName, textComponentFontName) == 0)) {
                    awtFont->m_textInput = i;
                }
                HFONT hfonttmp = CreateHFont(const_cast<LPWSTR>(wName), fontStyle, fontSize,
                                             angle, awScale);
                awtFont->m_hFont[i] = hfonttmp;

                JNU_ReleaseStringPlatformChars(env, nativeName, wName);

                env->DeleteLocalRef(fontDescriptor);
                env->DeleteLocalRef(nativeName);
            }
            if (awtFont->m_textInput == -1) {
                // no text component font was identified, so default
                // to first component
                awtFont->m_textInput = 0;
            }

            JNU_ReleaseStringPlatformChars(env, jTextComponentFontName, textComponentFontName);
            env->DeleteLocalRef(jTextComponentFontName);
        } else {
            // Instantiation for English version.
            jstring fontName = (jstring)env->GetObjectField(font,
                                                            AwtFont::nameID);
            wName = JNU_GetStringPlatformChars(env, fontName, NULL);

            WCHAR* wEName;
            if (!wcscmp(wName, L"Helvetica") || !wcscmp(wName, L"SansSerif")) {
                wEName = L"Arial";
            } else if (!wcscmp(wName, L"TimesRoman") ||
                       !wcscmp(wName, L"Serif")) {
                wEName = L"Times New Roman";
            } else if (!wcscmp(wName, L"Courier") ||
                       !wcscmp(wName, L"Monospaced")) {
                wEName = L"Courier New";
            } else if (!wcscmp(wName, L"Dialog")) {
                wEName = L"MS Sans Serif";
            } else if (!wcscmp(wName, L"DialogInput")) {
                wEName = L"MS Sans Serif";
            } else if (!wcscmp(wName, L"ZapfDingbats")) {
                wEName = L"WingDings";
            } else
                wEName = L"Arial";

            awtFont->m_textInput = 0;
            awtFont->m_hFont[0] = CreateHFont(wEName, fontStyle, fontSize,
                                              angle, awScale);

            JNU_ReleaseStringPlatformChars(env, fontName, wName);

            env->DeleteLocalRef(fontName);
        }
        /* The several callers of this method also set the pData field.
         * That's unnecessary but harmless duplication. However we definitely
         * want only one disposer record.
         */
        env->SetLongField(font, AwtFont::pDataID,
        reinterpret_cast<jlong>(awtFont));
        Disposer_AddRecord(env, font, pDataDisposeMethod,
                       reinterpret_cast<jlong>(awtFont));
    } catch (...) {
        env->DeleteLocalRef(compFont);
        throw;
    }

    env->DeleteLocalRef(compFont);
    return awtFont;
}

static void strip_tail(wchar_t* text, wchar_t* tail) { // strips tail and any possible whitespace before it from the end of text
    if (wcslen(text)<=wcslen(tail)) {
        return;
    }
    wchar_t* p = text+wcslen(text)-wcslen(tail);
    if (!wcscmp(p, tail)) {
        while(p>text && iswspace(*(p-1)))
            p--;
        *p = 0;
    }

}

static HFONT CreateHFont_sub(LPCWSTR name, int style, int height,
                             int angle=0, float awScale=1.0f)
{
    LOGFONTW logFont;

    logFont.lfWidth = 0;
    logFont.lfEscapement = angle;
    logFont.lfOrientation = angle;
    logFont.lfUnderline = FALSE;
    logFont.lfStrikeOut = FALSE;
    logFont.lfCharSet = GetNativeCharset(name);
    if (angle == 0 && awScale == 1.0f) {
        logFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
    } else {
        logFont.lfOutPrecision = OUT_TT_ONLY_PRECIS;
    }
    logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
    logFont.lfQuality = DEFAULT_QUALITY;
    logFont.lfPitchAndFamily = DEFAULT_PITCH;

    // Set style
    logFont.lfWeight = (style & java_awt_Font_BOLD) ? FW_BOLD : FW_NORMAL;
    logFont.lfItalic = (style & java_awt_Font_ITALIC) != 0;
    logFont.lfUnderline = 0;//(style & java_awt_Font_UNDERLINE) != 0;

    // Get point size
    logFont.lfHeight = -height;

    // Set font name
    WCHAR tmpname[80];
    wcscpy(tmpname, name);
    WCHAR* delimit = wcschr(tmpname, L',');
    if (delimit != NULL)
        *delimit = L'\0';  // terminate the string after the font name.
    // strip "Bold" and "Italic" from the end of the name
    strip_tail(tmpname,L""); //strip possible trailing whitespace
    strip_tail(tmpname,L"Italic");
    strip_tail(tmpname,L"Bold");
    wcscpy(&(logFont.lfFaceName[0]), tmpname);
    HFONT hFont = ::CreateFontIndirect(&logFont);
    DASSERT(hFont != NULL);
    // get a expanded or condensed version if its specified.
    if (awScale != 1.0f) {
        HDC hDC = ::GetDC(0);
        HFONT oldFont = (HFONT)::SelectObject(hDC, hFont);
        TEXTMETRIC tm;
        DWORD avgWidth;
        GetTextMetrics(hDC, &tm);
        oldFont = (HFONT)::SelectObject(hDC, oldFont);
        if (oldFont != NULL) { // should be the same as hFont
            VERIFY(::DeleteObject(oldFont));
        }
        avgWidth = tm.tmAveCharWidth;
        logFont.lfWidth = (LONG)((fabs)(avgWidth*awScale));
        hFont = ::CreateFontIndirect(&logFont);
        DASSERT(hFont != NULL);
        VERIFY(::ReleaseDC(0, hDC));
    }

    return hFont;
}

HFONT AwtFont::CreateHFont(WCHAR* name, int style, int height,
                           int angle, float awScale)
{
    WCHAR longName[80];
    // 80 > (max face name(=30) + strlen("CHINESEBIG5_CHARSET"))
    // longName doesn't have to be printable.  So, it is OK not to convert.

    wsprintf(longName, L"%ls-%d-%d", name, style, height);

    HFONT hFont = NULL;

    // only cache & share unrotated, unexpanded/uncondensed fonts
    if (angle == 0 && awScale == 1.0f) {
        hFont = fontCache.Lookup(longName);
        if (hFont != NULL) {
            fontCache.IncRefCount(hFont);
            return hFont;
        }
    }

    hFont = CreateHFont_sub(name, style, height, angle, awScale);
    if (angle == 0 && awScale == 1.0f) {
        fontCache.Add(longName, hFont);
    }
    return hFont;
}

void AwtFont::Cleanup()
{
    fontCache.Clear();
}

void AwtFont::SetupAscent(AwtFont* font)
{
    HDC hDC = ::GetDC(0);
    DASSERT(hDC != NULL);
    HGDIOBJ oldFont = ::SelectObject(hDC, font->GetHFont());

    TEXTMETRIC metrics;
    VERIFY(::GetTextMetrics(hDC, &metrics));
    font->SetAscent(metrics.tmAscent);

    ::SelectObject(hDC, oldFont);
    VERIFY(::ReleaseDC(0, hDC));
}

void AwtFont::LoadMetrics(JNIEnv *env, jobject fontMetrics)
{
    if (env->EnsureLocalCapacity(3) < 0)
        return;
    jintArray widths = env->NewIntArray(256);
    if (widths == NULL) {
        /* no local refs to delete yet. */
        return;
    }
    jobject font = env->GetObjectField(fontMetrics, AwtFont::fontID);
    AwtFont* awtFont = AwtFont::GetFont(env, font);

    if (!awtFont) {
        /* failed to get font */
        return;
    }

    HDC hDC = ::GetDC(0);
    DASSERT(hDC != NULL);

    HGDIOBJ oldFont = ::SelectObject(hDC, awtFont->GetHFont());
    TEXTMETRIC metrics;
    VERIFY(::GetTextMetrics(hDC, &metrics));

    awtFont->m_ascent = metrics.tmAscent;

    int ascent = metrics.tmAscent;
    int descent = metrics.tmDescent;
    int leading = metrics.tmExternalLeading;
    env->SetIntField(fontMetrics, AwtFont::ascentID, ascent);
    env->SetIntField(fontMetrics, AwtFont::descentID, descent);
    env->SetIntField(fontMetrics, AwtFont::leadingID, leading);
    env->SetIntField(fontMetrics, AwtFont::heightID, metrics.tmAscent +
                     metrics.tmDescent + leading);
    env->SetIntField(fontMetrics, AwtFont::maxAscentID, ascent);
    env->SetIntField(fontMetrics, AwtFont::maxDescentID, descent);

    int maxHeight =  ascent + descent + leading;
    env->SetIntField(fontMetrics, AwtFont::maxHeightID, maxHeight);

    int maxAdvance = metrics.tmMaxCharWidth;
    env->SetIntField(fontMetrics, AwtFont::maxAdvanceID, maxAdvance);

    awtFont->m_overhang = metrics.tmOverhang;

    jint intWidths[256];
    memset(intWidths, 0, 256 * sizeof(int));
    VERIFY(::GetCharWidth(hDC, metrics.tmFirstChar,
                          min(metrics.tmLastChar, 255),
                          (int *)&intWidths[metrics.tmFirstChar]));
    env->SetIntArrayRegion(widths, 0, 256, intWidths);
    env->SetObjectField(fontMetrics, AwtFont::widthsID, widths);

    // Get font metrics on remaining fonts (if multifont).
    for (int j = 1; j < awtFont->GetHFontNum(); j++) {
        ::SelectObject(hDC, awtFont->GetHFont(j));
        VERIFY(::GetTextMetrics(hDC, &metrics));
        env->SetIntField(fontMetrics, AwtFont::maxAscentID,
                         ascent = max(ascent, metrics.tmAscent));
        env->SetIntField(fontMetrics, AwtFont::maxDescentID,
                         descent = max(descent, metrics.tmDescent));
        env->SetIntField(fontMetrics, AwtFont::maxHeightID,
                         maxHeight = max(maxHeight,
                                         metrics.tmAscent +
                                         metrics.tmDescent +
                                         metrics.tmExternalLeading));
        env->SetIntField(fontMetrics, AwtFont::maxAdvanceID,
                         maxAdvance = max(maxAdvance, metrics.tmMaxCharWidth));
    }

    VERIFY(::SelectObject(hDC, oldFont));
    VERIFY(::ReleaseDC(0, hDC));
    env->DeleteLocalRef(font);
    env->DeleteLocalRef(widths);
}

SIZE AwtFont::TextSize(AwtFont* font, int columns, int rows)
{
    HDC hDC = ::GetDC(0);
    DASSERT(hDC != NULL);
    HGDIOBJ oldFont = ::SelectObject(hDC, (font == NULL)
                                           ? ::GetStockObject(SYSTEM_FONT)
                                           : font->GetHFont());

    SIZE size;
    VERIFY(::GetTextExtentPoint(hDC,
        TEXT("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 52, &size));

    VERIFY(::SelectObject(hDC, oldFont));
    VERIFY(::ReleaseDC(0, hDC));

    size.cx = size.cx * columns / 52;
    size.cy = size.cy * rows;
    return size;
}

int AwtFont::getFontDescriptorNumber(JNIEnv *env, jobject font,
                                     jobject fontDescriptor)
{
    int i, num;
    jobject refFontDescriptor;
    jobjectArray array;

    if (env->EnsureLocalCapacity(2) < 0)
        return 0;

    if (IsMultiFont(env, font)) {
        array = GetComponentFonts(env, font);
        num = env->GetArrayLength(array);
    } else {
        array = NULL;
        num = 0;
    }

    for (i = 0; i < num; i++){
        // Trying to identify the same FontDescriptor by comparing the
        // pointers.
        refFontDescriptor = env->GetObjectArrayElement(array, i);
        if (env->IsSameObject(refFontDescriptor, fontDescriptor)) {
            env->DeleteLocalRef(refFontDescriptor);
            env->DeleteLocalRef(array);
            return i;
        }
        env->DeleteLocalRef(refFontDescriptor);
    }
    env->DeleteLocalRef(array);
    return 0;   // Not found.  Use default.
}

/*
 * This is a faster version of the same function, which does most of
 * the work in Java.
 */
SIZE  AwtFont::DrawStringSize_sub(jstring str, HDC hDC,
                                  jobject font, long x, long y, BOOL draw,
                                  UINT codePage)
{
    SIZE size, temp;
    size.cx = size.cy = 0;

    if (str == NULL) {
        return size;
    }

    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
    if (env->EnsureLocalCapacity(3) < 0)
        return size;
    jobjectArray array = 0;

    int arrayLength = 0;

    if (env->GetStringLength(str) == 0) {
        return size;
    }

    //Init AwtFont object, which will "create" a AwtFont object if necessry,
    //before calling makeconvertedMultiFontString(), otherwise, the FontDescriptor's
    //"useUnicode" field might not be initialized correctly (font in Menu Component,
    //for example").
    AwtFont* awtFont = AwtFont::GetFont(env, font);

    if (IsMultiFont(env, font)) {
        jobject peer = env->CallObjectMethod(font, AwtFont::peerMID);
        array =  (jobjectArray)(env->CallObjectMethod(
        peer, AwtFont::makeConvertedMultiFontStringMID, str));
        DASSERT(!safe_ExceptionOccurred(env));

        if (array != NULL) {
            arrayLength = env->GetArrayLength(array);
        }
        env->DeleteLocalRef(peer);
    } else {
        array = NULL;
        arrayLength = 0;
    }

    HFONT oldFont = (HFONT)::SelectObject(hDC, awtFont->GetHFont());

    if (arrayLength == 0) {
        int length = env->GetStringLength(str);
        LPCWSTR strW = JNU_GetStringPlatformChars(env, str, NULL);
        VERIFY(::SelectObject(hDC, awtFont->GetHFont()));
        if (AwtComponent::GetRTLReadingOrder()){
            VERIFY(!draw || ::ExtTextOut(hDC, x, y, ETO_RTLREADING, NULL,
                                          strW, length, NULL));
        } else {
            VERIFY(!draw || ::TextOut(hDC, x, y, strW, length));
        }
        VERIFY(::GetTextExtentPoint32(hDC, strW, length, &size));
        JNU_ReleaseStringPlatformChars(env, str, strW);
    } else {
        for (int i = 0; i < arrayLength; i = i + 2) {
            jobject fontDescriptor = env->GetObjectArrayElement(array, i);
            if (fontDescriptor == NULL) {
                break;
            }

            jbyteArray convertedBytes =
                (jbyteArray)env->GetObjectArrayElement(array, i + 1);
            if (convertedBytes == NULL) {
                env->DeleteLocalRef(fontDescriptor);
                break;
            }

            int fdIndex = getFontDescriptorNumber(env, font, fontDescriptor);
            VERIFY(::SelectObject(hDC, awtFont->GetHFont(fdIndex)));

            /*
             * The strange-looking code that follows this comment is
             * the result of upstream optimizations. In the array of
             * alternating font descriptor and buffers, the buffers
             * contain their length in the first four bytes, a la
             * Pascal arrays.
             *
             * Note: the buffer MUST be unsigned, or VC++ will sign
             * extend buflen and bad things will happen.
             */
            unsigned char* buffer = NULL;
            jboolean unicodeUsed = env->GetBooleanField(fontDescriptor, AwtFont::useUnicodeID);
            try {
                buffer = (unsigned char *)
                    env->GetPrimitiveArrayCritical(convertedBytes, 0);
                int buflen = (buffer[0] << 24) | (buffer[1] << 16) |
                    (buffer[2] << 8) | buffer[3];

                DASSERT(buflen >= 0);

                /*
                 * the offsetBuffer, on the other hand, must be signed because
                 * TextOutA and GetTextExtentPoint32A expect it.
                 */
                char* offsetBuffer = (char *)(buffer + 4);

                if (unicodeUsed) {
                    VERIFY(!draw || ::TextOutW(hDC, x, y, (LPCWSTR)offsetBuffer, buflen / 2));
                    VERIFY(::GetTextExtentPoint32W(hDC, (LPCWSTR)offsetBuffer, buflen / 2, &temp));
                }
                else {
                    VERIFY(!draw || ::TextOutA(hDC, x, y, offsetBuffer, buflen));
                    VERIFY(::GetTextExtentPoint32A(hDC, offsetBuffer, buflen, &temp));
                }
            } catch (...) {
                if (buffer != NULL) {
                    env->ReleasePrimitiveArrayCritical(convertedBytes, buffer,
                                                       0);
                }
                throw;
            }
            env->ReleasePrimitiveArrayCritical(convertedBytes, buffer, 0);

            if (awtFont->textAngle == 0) {
                x += temp.cx;
            } else {
               // account for rotation of the text used in 2D printing.
               double degrees = 360.0 - (awtFont->textAngle/10.0);
               double rads = degrees/(180.0/3.1415926535);
               double dx = temp.cx * cos(rads);
               double dy = temp.cx * sin(rads);
               x += (long)floor(dx+0.5);
               y += (long)floor(dy+0.5);
            }
            size.cx += temp.cx;
            size.cy = (size.cy < temp.cy) ? temp.cy : size.cy;
            env->DeleteLocalRef(fontDescriptor);
            env->DeleteLocalRef(convertedBytes);
        }
    }
    env->DeleteLocalRef(array);

    VERIFY(::SelectObject(hDC, oldFont));
    return size;
}

/************************************************************************
 * WFontMetrics native methods
 */

extern "C" {

/*
 * Class:     sun_awt_windows_WFontMetrics
 * Method:    stringWidth
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL
Java_sun_awt_windows_WFontMetrics_stringWidth(JNIEnv *env, jobject self,
                                              jstring str)
{
    TRY;

    if (str == NULL) {
        JNU_ThrowNullPointerException(env, "str argument");
        return NULL;
    }
    HDC hDC = ::GetDC(0);    DASSERT(hDC != NULL);

    jobject font = env->GetObjectField(self, AwtFont::fontID);

    long ret = AwtFont::getMFStringWidth(hDC, font, str);
    VERIFY(::ReleaseDC(0, hDC));
    return ret;

    CATCH_BAD_ALLOC_RET(0);
}

/*
 * Class:     sun_awt_windows_WFontMetrics
 * Method:    charsWidth
 * Signature: ([CII)I
 */
JNIEXPORT jint JNICALL
Java_sun_awt_windows_WFontMetrics_charsWidth(JNIEnv *env, jobject self,
                                             jcharArray str,
                                             jint off, jint len)
{
    TRY;

    if (str == NULL) {
        JNU_ThrowNullPointerException(env, "str argument");
        return NULL;
    }
    if ((len < 0) || (off < 0) || (len + off > (env->GetArrayLength(str)))) {
        JNU_ThrowArrayIndexOutOfBoundsException(env, "off/len argument");
        return NULL;
    }

    jchar *strp = new jchar[len];
    env->GetCharArrayRegion(str, off, len, strp);
    jstring jstr = env->NewString(strp, len);
    jint result = Java_sun_awt_windows_WFontMetrics_stringWidth(env, self,
                                                                jstr);
    delete [] strp;
    return result;

    CATCH_BAD_ALLOC_RET(0);
}


/*
 * Class:     sun_awt_windows_WFontMetrics
 * Method:    bytesWidth
 * Signature: ([BII)I
 */
JNIEXPORT jint JNICALL
Java_sun_awt_windows_WFontMetrics_bytesWidth(JNIEnv *env, jobject self,
                                             jbyteArray str,
                                             jint off, jint len)
{
    TRY;

    if (str == NULL) {
        JNU_ThrowNullPointerException(env, "bytes argument");
        return NULL;
    }
    if ((len < 0) || (off < 0) || (len + off > (env->GetArrayLength(str)))) {
        JNU_ThrowArrayIndexOutOfBoundsException(env, "off or len argument");
        return NULL;
    }
    char *pStrBody = NULL;
    jint result = 0;
    try {
        jintArray array = (jintArray)env->GetObjectField(self,
                                                         AwtFont::widthsID);
        pStrBody = (char *)env->GetPrimitiveArrayCritical(str, 0);
        char *pStr = pStrBody + off;

        jint *widths = NULL;
        try {
            widths = (jint *)env->GetPrimitiveArrayCritical(array, 0);

            for (; len; len--) {
                result += widths[*pStr++];
            }
        } catch (...) {
            if (widths != NULL) {
                env->ReleasePrimitiveArrayCritical(array, widths, 0);
            }
            throw;
        }

        env->ReleasePrimitiveArrayCritical(array, widths, 0);

    } catch (...) {
        if (pStrBody != NULL) {
            env->ReleasePrimitiveArrayCritical(str, pStrBody, 0);
        }
        throw;
    }

    env->ReleasePrimitiveArrayCritical(str, pStrBody, 0);
    return result;

    CATCH_BAD_ALLOC_RET(0);
}


/*
 * Class:     sun_awt_windows_WFontMetrics
 * Method:    init
 * Signature: ()V
 */
JNIEXPORT void JNICALL
Java_sun_awt_windows_WFontMetrics_init(JNIEnv *env, jobject self)
{
    TRY;

    jobject font = env->GetObjectField(self, AwtFont::fontID);
    if (font == NULL) {
        JNU_ThrowNullPointerException(env, "fontMetrics' font");
        return;
    }
    // This local variable is unused. Is there some subtle side-effect here?
    jlong pData = env->GetLongField(font, AwtFont::pDataID);

    AwtFont::LoadMetrics(env, self);

    CATCH_BAD_ALLOC;
}


/*
 * Class:     sun_awt_windows_WFontMetrics
 * Method:    initIDs
 * Signature: ()V
 */
JNIEXPORT void JNICALL
Java_sun_awt_windows_WFontMetrics_initIDs(JNIEnv *env, jclass cls)
{
    TRY;

    AwtFont::widthsID = env->GetFieldID(cls, "widths", "[I");
    AwtFont::ascentID = env->GetFieldID(cls, "ascent", "I");
    AwtFont::descentID = env->GetFieldID(cls, "descent", "I");
    AwtFont::leadingID = env->GetFieldID(cls, "leading", "I");
    AwtFont::heightID = env->GetFieldID(cls, "height", "I");
    AwtFont::maxAscentID = env->GetFieldID(cls, "maxAscent", "I");
    AwtFont::maxDescentID = env->GetFieldID(cls, "maxDescent", "I");
    AwtFont::maxHeightID = env->GetFieldID(cls, "maxHeight", "I");
    AwtFont::maxAdvanceID = env->GetFieldID(cls, "maxAdvance", "I");

    DASSERT(AwtFont::widthsID != NULL);
    DASSERT(AwtFont::ascentID != NULL);
    DASSERT(AwtFont::descentID != NULL);
    DASSERT(AwtFont::leadingID != NULL);
    DASSERT(AwtFont::heightID != NULL);
    DASSERT(AwtFont::maxAscentID != NULL);
    DASSERT(AwtFont::maxDescentID != NULL);
    DASSERT(AwtFont::maxHeightID != NULL);
    DASSERT(AwtFont::maxAdvanceID != NULL);

    CATCH_BAD_ALLOC;
}

} /* extern "C" */


/************************************************************************
 * java.awt.Font native methods
 */

extern "C" {

JNIEXPORT void JNICALL
Java_java_awt_Font_initIDs(JNIEnv *env, jclass cls)
{
    TRY;

    AwtFont::peerMID = env->GetMethodID(cls, "getPeer",
                                        "()Ljava/awt/peer/FontPeer;");
    AwtFont::pDataID = env->GetFieldID(cls, "pData", "J");
    AwtFont::nameID = env->GetFieldID(cls, "name", "Ljava/lang/String;");
    AwtFont::sizeID = env->GetFieldID(cls, "size", "I");
    AwtFont::styleID = env->GetFieldID(cls, "style", "I");

    AwtFont::getFontMID =
      env->GetStaticMethodID(cls, "getFont",
                             "(Ljava/lang/String;)Ljava/awt/Font;");

    DASSERT(AwtFont::peerMID != NULL);
    DASSERT(AwtFont::pDataID != NULL);
    DASSERT(AwtFont::nameID != NULL);
    DASSERT(AwtFont::sizeID != NULL);
    DASSERT(AwtFont::styleID != NULL);

    DASSERT(AwtFont::getFontMID != NULL);

    CATCH_BAD_ALLOC;
}

} /* extern "C" */


/************************************************************************
 * java.awt.FontMetric native methods
 */

extern "C" {

JNIEXPORT void JNICALL
Java_java_awt_FontMetrics_initIDs(JNIEnv *env, jclass cls)
{
    TRY;

    AwtFont::fontID = env->GetFieldID(cls, "font", "Ljava/awt/Font;");
    AwtFont::getHeightMID = env->GetMethodID(cls, "getHeight", "()I");

    DASSERT(AwtFont::fontID);
    DASSERT(AwtFont::getHeightMID);

    CATCH_BAD_ALLOC;
}

} /* extern "C" */

/************************************************************************
 * sun.awt.FontDescriptor native methods
 */

extern "C" {

JNIEXPORT void JNICALL
Java_sun_awt_FontDescriptor_initIDs(JNIEnv *env, jclass cls)
{
    TRY;

    AwtFont::nativeNameID = env->GetFieldID(cls, "nativeName",
                                            "Ljava/lang/String;");
    AwtFont::useUnicodeID = env->GetFieldID(cls, "useUnicode", "Z");

    DASSERT(AwtFont::nativeNameID != NULL);
    DASSERT(AwtFont::useUnicodeID != NULL);

    CATCH_BAD_ALLOC;
}

} /* extern "C" */


/************************************************************************
 * sun.awt.PlatformFont native methods
 */

extern "C" {

JNIEXPORT void JNICALL
Java_sun_awt_PlatformFont_initIDs(JNIEnv *env, jclass cls)
{
    TRY;

    AwtFont::fontConfigID = env->GetFieldID(cls, "fontConfig", "Lsun/awt/FontConfiguration;");
    AwtFont::componentFontsID =
        env->GetFieldID(cls, "componentFonts", "[Lsun/awt/FontDescriptor;");
    AwtFont::makeConvertedMultiFontStringMID =
        env->GetMethodID(cls, "makeConvertedMultiFontString",
                         "(Ljava/lang/String;)[Ljava/lang/Object;");

    DASSERT(AwtFont::makeConvertedMultiFontStringMID != NULL);
    DASSERT(AwtFont::componentFontsID != NULL);
    DASSERT(AwtFont::fontConfigID != NULL);

    CATCH_BAD_ALLOC;
}

} /* extern "C" */


/************************************************************************
 * sun.awt.windows.WFontPeer native methods
 */

extern "C" {

JNIEXPORT void JNICALL
Java_sun_awt_windows_WFontPeer_initIDs(JNIEnv *env, jclass cls)
{
    TRY;

    AwtFont::textComponentFontNameID = env->GetFieldID(cls, "textComponentFontName", "Ljava/lang/String;");

    DASSERT(AwtFont::textComponentFontNameID != NULL);

    CATCH_BAD_ALLOC;
}

} /* extern "C" */


/************************************************************************
 * FontCache methods
 */

void AwtFontCache::Add(WCHAR* name, HFONT font)
{
    fontCache.m_head = new Item(name, font, fontCache.m_head);
}

HFONT AwtFontCache::Lookup(WCHAR* name)
{
    Item* item = fontCache.m_head;
    Item* lastItem = NULL;

    while (item != NULL) {
        if (wcscmp(item->name, name) == 0) {
            return item->font;
        }
        lastItem = item;
        item = item->next;
    }
    return NULL;
}

BOOL AwtFontCache::Search(HFONT font)
{
    Item* item = fontCache.m_head;

    while (item != NULL) {
        if (item->font == font) {
            return TRUE;
        }
        item = item->next;
    }
    return FALSE;
}

void AwtFontCache::Remove(HFONT font)
{
    Item* item = fontCache.m_head;
    Item* lastItem = NULL;

    while (item != NULL) {
        if (item->font == font) {
            if (DecRefCount(item) <= 0){
                if (lastItem == NULL) {
                    fontCache.m_head = item->next;
                } else {
                lastItem->next = item->next;
                }
             delete item;
             }
             return;
        }
        lastItem = item;
        item = item->next;
    }
}

void AwtFontCache::Clear()
{
    Item* item = m_head;
    Item* next;

    while (item != NULL) {
        next = item->next;
        delete item;
        item = next;
    }

    m_head = NULL;
}

/* NOTE: In the interlock calls below the return value is different
         depending on which version of windows. However, all versions
         return a 0 or less than value when the count gets there. Only
         under NT 4.0 & 98 does the value actaully represent the new value. */

void AwtFontCache::IncRefCount(HFONT hFont){
    Item* item = fontCache.m_head;

    while (item != NULL){

        if (item->font == hFont){
            IncRefCount(item);
            return;
        }
        item = item->next;
    }
}

LONG AwtFontCache::IncRefCount(Item* item){
    LONG    newVal;

    if(NULL != item){
        newVal = InterlockedIncrement((long*)&item->refCount);
    }
    return(newVal);
}

LONG AwtFontCache::DecRefCount(Item* item){
    LONG    newVal;

    if(NULL != item){
        newVal = InterlockedDecrement((long*)&item->refCount);
    }
    return(newVal);
}

AwtFontCache::Item::Item(const WCHAR* s, HFONT f, AwtFontCache::Item* n )
{
    name = _wcsdup(s);
    font = f;
    next = n;
    refCount = 1;
}

AwtFontCache::Item::~Item() {
  VERIFY(::DeleteObject(font));
  free(name);
}

/////////////////////////////////////////////////////////////////////////////
// for canConvert native method of WDefaultFontCharset

class CSegTableComponent
{
public:
    CSegTableComponent();
    virtual ~CSegTableComponent();
    virtual void Create(LPCWSTR name);
    virtual BOOL In(USHORT iChar) { DASSERT(FALSE); return FALSE; };
    LPWSTR GetFontName(){
        DASSERT(m_lpszFontName != NULL); return m_lpszFontName;
    };

private:
    LPWSTR m_lpszFontName;
};

CSegTableComponent::CSegTableComponent()
{
    m_lpszFontName = NULL;
}

CSegTableComponent::~CSegTableComponent()
{
    if (m_lpszFontName != NULL) {
        free(m_lpszFontName);
        m_lpszFontName = NULL;
    }
}

void CSegTableComponent::Create(LPCWSTR name)
{
    if (m_lpszFontName != NULL) {
        free(m_lpszFontName);
        m_lpszFontName = NULL;
    }
    m_lpszFontName = _wcsdup(name);
    DASSERT(m_lpszFontName);
}

#define CMAPHEX 0x70616d63 // = "cmap" (reversed)

// CSegTable: Segment table describing character coverage for a font
class CSegTable : public CSegTableComponent
{
public:
    CSegTable();
    virtual ~CSegTable();
    virtual BOOL In(USHORT iChar);
    BOOL HasCmap();
    virtual BOOL IsEUDC() { DASSERT(FALSE); return FALSE; };

protected:
    virtual void GetData(DWORD dwOffset, LPVOID lpData, DWORD cbData) {
        DASSERT(FALSE); };
    void MakeTable();
    static void SwapShort(USHORT& p);
    static void SwapULong(ULONG& p);

private:
    USHORT m_cSegCount;     // number of segments
    PUSHORT m_piStart;      // pointer to array of starting values
    PUSHORT m_piEnd;        // pointer to array of ending values (inclusive)
    USHORT m_cSeg;          // current segment (cache)
};

CSegTable::CSegTable()
{
    m_cSegCount = 0;
    m_piStart = NULL;
    m_piEnd = NULL;
    m_cSeg = 0;
}

CSegTable::~CSegTable()
{
    if (m_piStart != NULL)
        delete[] m_piStart;
    if (m_piEnd != NULL)
        delete[] m_piEnd;
}

#define OFFSETERROR 0

void CSegTable::MakeTable()
{
typedef struct tagTABLE{
    USHORT  platformID;
    USHORT  encodingID;
    ULONG   offset;
} TABLE, *PTABLE;

typedef struct tagSUBTABLE{
    USHORT  format;
    USHORT  length;
    USHORT  version;
    USHORT  segCountX2;
    USHORT  searchRange;
    USHORT  entrySelector;
    USHORT  rangeShift;
} SUBTABLE, *PSUBTABLE;

    USHORT aShort[2];
    (void) GetData(0, aShort, sizeof(aShort));
    USHORT nTables = aShort[1];
    SwapShort(nTables);

    // allocate buffer to hold encoding tables
    DWORD cbData = nTables * sizeof(TABLE);
    PTABLE pTables = new TABLE[nTables];
    PTABLE pTable = pTables;

    // get array of encoding tables.
    (void) GetData(4, (PBYTE) pTable, cbData);

    ULONG offsetFormat4 = OFFSETERROR;
    USHORT i;
    for (i = 0; i < nTables; i++) {
        SwapShort(pTable->encodingID);
        SwapShort(pTable->platformID);
        //for a Unicode font for Windows, platformID == 3, encodingID == 1
        if ((pTable->platformID == 3)&&(pTable->encodingID == 1)) {
            offsetFormat4 = pTable->offset;
            SwapULong(offsetFormat4);
            break;
        }
        pTable++;
    }
    delete[] pTables;
    if (offsetFormat4 == OFFSETERROR) {
        return;
    }
//    DASSERT(offsetFormat4 != OFFSETERROR);

    SUBTABLE subTable;
    (void) GetData(offsetFormat4, &subTable, sizeof(SUBTABLE));
    SwapShort(subTable.format);
    SwapShort(subTable.segCountX2);
    DASSERT(subTable.format == 4);

    m_cSegCount = subTable.segCountX2/2;

    // read in the array of segment end values
    m_piEnd = new USHORT[m_cSegCount];

    ULONG offset = offsetFormat4
        + sizeof(SUBTABLE); //skip constant # bytes in subtable
    cbData = m_cSegCount * sizeof(USHORT);
    (void) GetData(offset, m_piEnd, cbData);
    for (i = 0; i < m_cSegCount; i++)
        SwapShort(m_piEnd[i]);
    DASSERT(m_piEnd[m_cSegCount-1] == 0xffff);

    // read in the array of segment start values
    try {
        m_piStart = new USHORT[m_cSegCount];
    } catch (std::bad_alloc&) {
        delete [] m_piEnd;
        m_piEnd = NULL;
        throw;
    }

    offset += cbData        //skip SegEnd array
        + sizeof(USHORT);   //skip reservedPad
    (void) GetData(offset, m_piStart, cbData);
    for (i = 0; i < m_cSegCount; i++)
        SwapShort(m_piStart[i]);
    DASSERT(m_piStart[m_cSegCount-1] == 0xffff);
}

BOOL CSegTable::In(USHORT iChar)
{
    if (!HasCmap()) {
        return FALSE;
    }
//    DASSERT(m_piStart);
//    DASSERT(m_piEnd);

    if (iChar > m_piEnd[m_cSeg]) {
        for (; (m_cSeg < m_cSegCount)&&(iChar > m_piEnd[m_cSeg]); m_cSeg++);
    } else if (iChar < m_piStart[m_cSeg]) {
        for (; (m_cSeg > 0)&&(iChar < m_piStart[m_cSeg]); m_cSeg--);
    }

    if ((iChar <= m_piEnd[m_cSeg])&&(iChar >= m_piStart[m_cSeg])&&(iChar != 0xffff))
        return TRUE;
    else
        return FALSE;
}

inline BOOL CSegTable::HasCmap()
{
    return (((m_piEnd)&&(m_piStart)) ? TRUE : FALSE);
}

inline void CSegTable::SwapShort(USHORT& p)
{
    SHORT temp;

    temp = (SHORT)(HIBYTE(p) + (LOBYTE(p) << 8));
    p = temp;
}

inline void CSegTable::SwapULong(ULONG& p)
{
    ULONG temp;

    temp = (LONG) ((BYTE) p);
    temp <<= 8;
    p >>= 8;

    temp += (LONG) ((BYTE) p);
    temp <<= 8;
    p >>= 8;

    temp += (LONG) ((BYTE) p);
    temp <<= 8;
    p >>= 8;

    temp += (LONG) ((BYTE) p);
    p = temp;
}

class CStdSegTable : public CSegTable
{
public:
    CStdSegTable();
    virtual ~CStdSegTable();
    BOOL IsEUDC() { return FALSE; };
    virtual void Create(LPCWSTR name);

protected:
    void GetData(DWORD dwOffset, LPVOID lpData, DWORD cbData);

private:
    HDC m_hTmpDC;
};

CStdSegTable::CStdSegTable()
{
    m_hTmpDC = NULL;
}

CStdSegTable::~CStdSegTable()
{
    DASSERT(m_hTmpDC == NULL);
}

inline void CStdSegTable::GetData(DWORD dwOffset,
                                  LPVOID lpData, DWORD cbData)
{
    DASSERT(m_hTmpDC);
    DWORD nBytes =
        ::GetFontData(m_hTmpDC, CMAPHEX, dwOffset, lpData, cbData);
    DASSERT(nBytes != GDI_ERROR);
}

void CStdSegTable::Create(LPCWSTR name)
{
    CSegTableComponent::Create(name);

    HWND hWnd = ::GetDesktopWindow();
    DASSERT(hWnd);
    m_hTmpDC = ::GetWindowDC(hWnd);
    DASSERT(m_hTmpDC);

    HFONT hFont = CreateHFont_sub(name, 0, 20);

    HFONT hOldFont = (HFONT)::SelectObject(m_hTmpDC, hFont);
    DASSERT(hOldFont);

    (void) MakeTable();

    VERIFY(::SelectObject(m_hTmpDC, hOldFont));
    VERIFY(::DeleteObject(hFont));
    VERIFY(::ReleaseDC(hWnd, m_hTmpDC) != 0);
    m_hTmpDC = NULL;
}

class CEUDCSegTable : public CSegTable
{
public:
    CEUDCSegTable();
    virtual ~CEUDCSegTable();
    BOOL IsEUDC() { return TRUE; };
    virtual void Create(LPCWSTR name);

protected:
    void GetData(DWORD dwOffset, LPVOID lpData, DWORD cbData);

private:
    HANDLE m_hTmpFile;
    ULONG m_hTmpCMapOffset;
};

CEUDCSegTable::CEUDCSegTable()
{
    m_hTmpFile = NULL;
    m_hTmpCMapOffset = 0;
}

CEUDCSegTable::~CEUDCSegTable()
{
    DASSERT(m_hTmpFile == NULL);
    DASSERT(m_hTmpCMapOffset == 0);
}

inline void CEUDCSegTable::GetData(DWORD dwOffset,
                                   LPVOID lpData, DWORD cbData)
{
    DASSERT(m_hTmpFile);
    DASSERT(m_hTmpCMapOffset);
    ::SetFilePointer(m_hTmpFile, m_hTmpCMapOffset + dwOffset,
        NULL, FILE_BEGIN);
    DWORD dwRead;
    VERIFY(::ReadFile(m_hTmpFile, lpData, cbData, &dwRead, NULL));
    DASSERT(dwRead == cbData);
}

void CEUDCSegTable::Create(LPCWSTR name)
{
typedef struct tagHEAD{
    FIXED   sfnt_version;
    USHORT  numTables;
    USHORT  searchRange;
    USHORT  entrySelector;
    USHORT  rangeShift;
} HEAD, *PHEAD;

typedef struct tagENTRY{
    ULONG   tag;
    ULONG   checkSum;
    ULONG   offset;
    ULONG   length;
} ENTRY, *PENTRY;

    CSegTableComponent::Create(name);

    // create EUDC font file and make EUDCSegTable
    // after wrapper function for CreateFileW, we use only CreateFileW
    m_hTmpFile = ::CreateFile(name, GENERIC_READ,
                               FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (m_hTmpFile == INVALID_HANDLE_VALUE){
        m_hTmpFile = NULL;
        return;
    }

    HEAD head;
    DWORD dwRead;
    VERIFY(::ReadFile(m_hTmpFile, &head, sizeof(head), &dwRead, NULL));
    DASSERT(dwRead == sizeof(HEAD));
    SwapShort(head.numTables);
    ENTRY entry;
    for (int i = 0; i < head.numTables; i++){
        VERIFY(::ReadFile(m_hTmpFile, &entry, sizeof(entry), &dwRead, NULL));
        DASSERT(dwRead == sizeof(ENTRY));
        if (entry.tag == CMAPHEX)
            break;
    }
    DASSERT(entry.tag == CMAPHEX);
    SwapULong(entry.offset);
    m_hTmpCMapOffset = entry.offset;

    (void) MakeTable();

    m_hTmpCMapOffset = 0;
    VERIFY(::CloseHandle(m_hTmpFile));
    m_hTmpFile = NULL;
}

class CSegTableManagerComponent
{
public:
    CSegTableManagerComponent();
    ~CSegTableManagerComponent();

protected:
    void MakeBiggerTable();
    CSegTableComponent **m_tables;
    int m_nTable;
    int m_nMaxTable;
};

#define TABLENUM 20

CSegTableManagerComponent::CSegTableManagerComponent()
{
    m_nTable = 0;
    m_nMaxTable = TABLENUM;
    m_tables = new CSegTableComponent*[m_nMaxTable];
}

CSegTableManagerComponent::~CSegTableManagerComponent()
{
    for (int i = 0; i < m_nTable; i++) {
        DASSERT(m_tables[i]);
        delete m_tables[i];
    }
    delete [] m_tables;
    m_tables = NULL;
}

void CSegTableManagerComponent::MakeBiggerTable()
{
    CSegTableComponent **tables =
        new CSegTableComponent*[m_nMaxTable + TABLENUM];

    for (int i = 0; i < m_nMaxTable; i++)
        tables[i] = m_tables[i];

    delete[] m_tables;

    m_tables = tables;
    m_nMaxTable += TABLENUM;
}

class CSegTableManager : public CSegTableManagerComponent
{
public:
    CSegTable* GetTable(LPCWSTR lpszFontName, BOOL fEUDC);
};

CSegTable* CSegTableManager::GetTable(LPCWSTR lpszFontName, BOOL fEUDC)
{
    for (int i = 0; i < m_nTable; i++) {
        if ((((CSegTable*)m_tables[i])->IsEUDC() == fEUDC) &&
            (wcscmp(m_tables[i]->GetFontName(),lpszFontName) == 0))
            return (CSegTable*) m_tables[i];
    }

    if (m_nTable == m_nMaxTable) {
        (void) MakeBiggerTable();
    }
    DASSERT(m_nTable < m_nMaxTable);

    if (!fEUDC) {
        m_tables[m_nTable] = new CStdSegTable;
    } else {
        m_tables[m_nTable] = new CEUDCSegTable;
    }
    m_tables[m_nTable]->Create(lpszFontName);
    return (CSegTable*) m_tables[m_nTable++];
}

CSegTableManager g_segTableManager;

class CCombinedSegTable : public CSegTableComponent
{
public:
    CCombinedSegTable();
    void Create(LPCWSTR name);
    BOOL In(USHORT iChar);

private:
    LPSTR GetCodePageSubkey();
    void GetEUDCFileName(LPWSTR lpszFileName, int cchFileName);
    static char m_szCodePageSubkey[16];
    static WCHAR m_szDefaultEUDCFile[_MAX_PATH];
    static BOOL m_fEUDCSubKeyExist;
    static BOOL m_fTTEUDCFileExist;
    CStdSegTable* m_pStdSegTable;
    CEUDCSegTable* m_pEUDCSegTable;
};

char CCombinedSegTable::m_szCodePageSubkey[16] = "";

WCHAR CCombinedSegTable::m_szDefaultEUDCFile[_MAX_PATH] = L"";

BOOL CCombinedSegTable::m_fEUDCSubKeyExist = TRUE;

BOOL CCombinedSegTable::m_fTTEUDCFileExist = TRUE;

CCombinedSegTable::CCombinedSegTable()
{
    m_pStdSegTable = NULL;
    m_pEUDCSegTable = NULL;
}

#include <locale.h>
LPSTR CCombinedSegTable::GetCodePageSubkey()
{
    if (strlen(m_szCodePageSubkey) > 0) {
        return m_szCodePageSubkey;
    }

    LPSTR lpszLocale = setlocale(LC_CTYPE, "");
    // cf lpszLocale = "Japanese_Japan.932"
    if (lpszLocale == NULL) {
        return NULL;
    }
    LPSTR lpszCP = strchr(lpszLocale, (int) '.');
    if (lpszCP == NULL) {
        return NULL;
    }
    lpszCP++; // cf lpszCP = "932"

    char szSubKey[80];
    strcpy(szSubKey, "EUDC\\");
    strcpy(&(szSubKey[strlen(szSubKey)]), lpszCP);
    strcpy(m_szCodePageSubkey, szSubKey);
    return m_szCodePageSubkey;
}

void CCombinedSegTable::GetEUDCFileName(LPWSTR lpszFileName, int cchFileName)
{
    if (m_fEUDCSubKeyExist == FALSE)
        return;

    // get filename of typeface-specific TureType EUDC font
    LPSTR lpszSubKey = GetCodePageSubkey();
    if (lpszSubKey == NULL) {
        m_fEUDCSubKeyExist = FALSE;
        return; // can not get codepage information
    }
    HKEY hRootKey = HKEY_CURRENT_USER;
    HKEY hKey;
    LONG lRet = ::RegOpenKeyExA(hRootKey, lpszSubKey, 0, KEY_ALL_ACCESS, &hKey);
    if (lRet != ERROR_SUCCESS) {
        m_fEUDCSubKeyExist = FALSE;
        return; // no EUDC font
    }

    // get EUDC font file name
    WCHAR szFamilyName[80];
    wcscpy(szFamilyName, GetFontName());
    WCHAR* delimit = wcschr(szFamilyName, L',');
    if (delimit != NULL)
        *delimit = L'\0';
    DWORD dwType;
    UCHAR szFileName[_MAX_PATH];
    ::ZeroMemory(szFileName, sizeof(szFileName));
    DWORD dwBytes = sizeof(szFileName);
    // try Typeface-specific EUDC font
    char szTmpName[80];
    VERIFY(::WideCharToMultiByte(CP_ACP, 0, szFamilyName, -1,
        szTmpName, sizeof(szTmpName), NULL, NULL));
    LONG lStatus = ::RegQueryValueExA(hKey, (LPCSTR) szTmpName,
        NULL, &dwType, szFileName, &dwBytes);
    BOOL fUseDefault = FALSE;
    if (lStatus != ERROR_SUCCESS){ // try System default EUDC font
        if (m_fTTEUDCFileExist == FALSE)
            return;
        if (wcslen(m_szDefaultEUDCFile) > 0) {
            wcscpy(lpszFileName, m_szDefaultEUDCFile);
            return;
        }
        char szDefault[] = "SystemDefaultEUDCFont";
        lStatus = ::RegQueryValueExA(hKey, (LPCSTR) szDefault,
            NULL, &dwType, szFileName, &dwBytes);
        fUseDefault = TRUE;
        if (lStatus != ERROR_SUCCESS) {
            m_fTTEUDCFileExist = FALSE;
            // This font is associated with no EUDC font
            // and there is no system default EUDC font
            return;
        }
    }

    if (strcmp((LPCSTR) szFileName, "userfont.fon") == 0) {
        // This font is associated with no EUDC font
        // and the system default EUDC font is not TrueType
        m_fTTEUDCFileExist = FALSE;
        return;
    }

    DASSERT(strlen((LPCSTR)szFileName) > 0);
    VERIFY(::MultiByteToWideChar(CP_ACP, 0,
        (LPCSTR)szFileName, -1, lpszFileName, cchFileName) != 0);
    if (fUseDefault)
        wcscpy(m_szDefaultEUDCFile, lpszFileName);
}

void CCombinedSegTable::Create(LPCWSTR name)
{
    CSegTableComponent::Create(name);

    m_pStdSegTable =
        (CStdSegTable*) g_segTableManager.GetTable(name, FALSE/*not EUDC*/);
    WCHAR szEUDCFileName[_MAX_PATH];
    ::ZeroMemory(szEUDCFileName, sizeof(szEUDCFileName));
    (void) GetEUDCFileName(szEUDCFileName,
        sizeof(szEUDCFileName)/sizeof(WCHAR));
    if (wcslen(szEUDCFileName) > 0) {
        m_pEUDCSegTable = (CEUDCSegTable*) g_segTableManager.GetTable(
            szEUDCFileName, TRUE/*EUDC*/);
        if (m_pEUDCSegTable->HasCmap() == FALSE)
            m_pEUDCSegTable = NULL;
    }
}

BOOL CCombinedSegTable::In(USHORT iChar)
{
    DASSERT(m_pStdSegTable);
    if (m_pStdSegTable->In(iChar))
        return TRUE;

    if (m_pEUDCSegTable != NULL)
        return m_pEUDCSegTable->In(iChar);

    return FALSE;
}

class CCombinedSegTableManager : public CSegTableManagerComponent
{
public:
    CCombinedSegTable* GetTable(LPCWSTR lpszFontName);
};

CCombinedSegTable* CCombinedSegTableManager::GetTable(LPCWSTR lpszFontName)
{
    for (int i = 0; i < m_nTable; i++) {
        if (wcscmp(m_tables[i]->GetFontName(),lpszFontName) == 0)
            return (CCombinedSegTable*) m_tables[i];
    }

    if (m_nTable == m_nMaxTable) {
        (void) MakeBiggerTable();
    }
    DASSERT(m_nTable < m_nMaxTable);

    m_tables[m_nTable] = new CCombinedSegTable;
    m_tables[m_nTable]->Create(lpszFontName);

    return (CCombinedSegTable*) m_tables[m_nTable++];
}


/************************************************************************
 * WDefaultFontCharset native methos
 */

extern "C" {

JNIEXPORT void JNICALL
Java_sun_awt_windows_WDefaultFontCharset_initIDs(JNIEnv *env, jclass cls)
{
    TRY;

    AwtFont::fontNameID = env->GetFieldID(cls, "fontName",
                                          "Ljava/lang/String;");
    DASSERT(AwtFont::fontNameID != NULL);

    CATCH_BAD_ALLOC;
}


/*
 * !!!!!!!!!!!!!!!!!!!! this does not work. I am not sure why, but
 * when active, this will reliably crash HJ, with no hope of debugging
 * for java.  It doesn't seem to crash the _g version.
 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!
 *
 * I suspect may be running out of C stack: see alloca in
 * JNI_GET_STRING, the alloca in it.
 *
 * (the method is prefixed with XXX so that the linker won't find it) */
JNIEXPORT jboolean JNICALL
Java_sun_awt_windows_WDefaultFontCharset_canConvert(JNIEnv *env, jobject self,
                                                    jchar ch)
{
    TRY;

    static CCombinedSegTableManager tableManager;

    jstring fontName = (jstring)env->GetObjectField(self, AwtFont::fontNameID);
    DASSERT(fontName != NULL);
    LPCWSTR fontNameW = JNU_GetStringPlatformChars(env, fontName, NULL);
    CCombinedSegTable* pTable = tableManager.GetTable(fontNameW);
    JNU_ReleaseStringPlatformChars(env, fontName, fontNameW);
    return (pTable->In((USHORT) ch) ? JNI_TRUE : JNI_FALSE);

    CATCH_BAD_ALLOC_RET(FALSE);
}

} /* extern "C" */

Other Java examples (source code examples)

Here is a short list of links related to this Java awt_Font.cpp source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.