jdk/src/solaris/native/sun/awt/multi_font.c
author duke
Sat, 01 Dec 2007 00:00:00 +0000
changeset 2 90ce3da70b43
child 1175 026b52c440fe
permissions -rw-r--r--
Initial load

/*
 * Copyright 1996-2005 Sun Microsystems, Inc.  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.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

/*
 * These routines are used for display string with multi font.
 */

#ifdef HEADLESS
    #error This file should not be included in headless library
#endif

#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <jni.h>
#include <jni_util.h>
#include <jvm.h>
#ifndef XAWT
#include <Xm/Display.h>
#endif
#include "awt_Font.h"
#ifndef XAWT
#include "awt_Component.h"
#endif
#include "awt_MenuItem.h"
#include "awt_p.h"
#include "multi_font.h"

extern XFontStruct *loadFont(Display *, char *, int32_t);

extern struct FontIDs fontIDs;
extern struct MComponentPeerIDs mComponentPeerIDs;
extern struct MMenuItemPeerIDs mMenuItemPeerIDs;
extern struct PlatformFontIDs platformFontIDs;
extern struct MFontPeerIDs mFontPeerIDs;

/*
 * make string with str + string representation of num
 * This string is used as tag string of Motif Compound String and FontList.
 */
static void
makeTag(char *str, int32_t num, char *buf)
{
    int32_t len = strlen(str);

    strcpy(buf, str);
    buf[len] = '0' + num % 100;
    buf[len + 1] = '\0';
}
#ifndef XAWT
jobject
awtJNI_CreateAndSetGlobalRef(JNIEnv * env, jobject this)
{
    jobject gRef;

    gRef = (*env)->NewGlobalRef(env, this);

    JNU_SetLongFieldFromPtr(env, this, mComponentPeerIDs.jniGlobalRef, gRef);

    return gRef;
}

struct gRefStruct
{
    jobject gRef;
    struct gRefStruct *next;
};

static struct gRefStruct *gRefHead = NULL;
static struct gRefStruct *gRefTail = NULL;

/*
 * This function is called by components that
 * are being disposed. It used to invalidate
 * the global ref immediately, but the awt is
 * rather full of thread race conditions involving
 * component disposal and outstanding events.
 * Now we queue up 'to be deleted' global refs
 * as they come in, and don't invalidate them
 * until the X event queue is empty. Callers of
 * either of these functions _must_ have AWT_LOCK'd
 * before using them!
 */
void
awtJNI_DeleteGlobalRef(JNIEnv * env, jobject this)
{
    jobject gRef;
    struct gRefStruct *newGRef;
    struct gRefStruct *temp;

    gRef = (jobject)
        JNU_GetLongFieldAsPtr(env, this, mComponentPeerIDs.jniGlobalRef);
    JNU_SetLongFieldFromPtr(env, this, mComponentPeerIDs.jniGlobalRef, NULL);

    /*
     * Verra handy for tracking down race conditions. If you
     * have a peer getting called after its been disposed...
     */
    /* jio_fprintf(stderr,"%p\n",(void *)gRef); */

    newGRef = (struct gRefStruct *)malloc((size_t)sizeof(struct gRefStruct));

    if(newGRef == NULL)
        (*env)->DeleteGlobalRef(env, gRef);
    else
    {
        newGRef->gRef = gRef;
        newGRef->next = NULL;

        if(gRefHead == NULL)
        {
            gRefTail = newGRef;
            gRefHead = newGRef;
        }
        else
        {
            gRefTail->next = newGRef;
            gRefTail = newGRef;
        }
    }
}

void
awtJNI_DeleteGlobalMenuRef(JNIEnv * env, jobject this)
{
    jobject gRef;
    struct gRefStruct *newGRef;
    struct gRefStruct *temp;

    gRef = (jobject)
      JNU_GetLongFieldAsPtr(env, this, mMenuItemPeerIDs.jniGlobalRef);
    JNU_SetLongFieldFromPtr(env, this, mMenuItemPeerIDs.jniGlobalRef, NULL);

    /*
     * Verra handy for tracking down race conditions. If you
     * have a peer getting called after its been disposed...
     */
    /* jio_fprintf(stderr,"%p\n",(void *)gRef); */

    newGRef = (struct gRefStruct *)malloc((size_t)sizeof(struct gRefStruct));

    if(newGRef == NULL)
        (*env)->DeleteGlobalRef(env, gRef);
    else
    {
        newGRef->gRef = gRef;
        newGRef->next = NULL;

        if(gRefHead == NULL)
        {
            gRefTail = newGRef;
            gRefHead = newGRef;
        }
        else
        {
            gRefTail->next = newGRef;
            gRefTail = newGRef;
        }
    }
}

void awtJNI_CleanupGlobalRefs()
{
    struct gRefStruct *working,*next;
    JNIEnv *env;
    int32_t count = 0;

    if(gRefHead == NULL) {
        return;
    }

    env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);

    working = gRefHead;
    gRefHead = gRefTail = NULL;

    while(working != NULL)
    {
        count++;
        next = working->next;
        (*env)->DeleteGlobalRef(env, working->gRef);

        free((void *)working);

        working = next;
    }
}
#endif
static int32_t
awtJNI_GetFontDescriptorNumber(JNIEnv * env
                               ,jobject font
                               ,jobject fd)
{
    int32_t i = 0, num;
    /* initialize to NULL so that DeleteLocalRef will work. */
    jobjectArray componentFonts = NULL;
    jobject peer = NULL;
    jobject temp = NULL;
    jboolean validRet = JNI_FALSE;

    if ((*env)->EnsureLocalCapacity(env, 2) < 0)
        goto done;

    peer = (*env)->CallObjectMethod(env,font,fontIDs.getPeer);
    if (peer == NULL)
        goto done;

    componentFonts = (jobjectArray)
        (*env)->GetObjectField(env,peer,platformFontIDs.componentFonts);

    if (componentFonts == NULL)
        goto done;

    num = (*env)->GetArrayLength(env, componentFonts);

    for (i = 0; i < num; i++) {
        temp = (*env)->GetObjectArrayElement(env, componentFonts, i);

        if ((*env)->IsSameObject(env, fd, temp)) {
            validRet = JNI_TRUE;
            break;
        }
        (*env)->DeleteLocalRef(env, temp);
    }

 done:
    (*env)->DeleteLocalRef(env, peer);
    (*env)->DeleteLocalRef(env, componentFonts);

    if (validRet)
        return i;

    return 0;
}
#ifndef XAWT
jobject
awtJNI_GetFont(JNIEnv * env, jobject this)
{
    jobject target = NULL;
    jobject font = NULL;

    target = (*env)->GetObjectField(env, this, mComponentPeerIDs.target);
    // SECURITY: Must call _NoClientCode() methods to ensure that we
    //           are not invoking client code on the privileged thread
    font = JNU_CallMethodByName(env,
                                NULL,
                                target,
                                "getFont_NoClientCode",
                                "()Ljava/awt/Font;").l;
    (*env)->DeleteLocalRef(env, target);
    return font;
}
#endif
jobject
awtJNI_GetFMFont(JNIEnv * env, jobject this)
{
    return JNU_CallMethodByName(env, NULL, this, "getFont_NoClientCode",
                                "()Ljava/awt/Font;").l;
}

jboolean
awtJNI_IsMultiFont(JNIEnv * env, jobject this)
{
    jobject peer = NULL;
    jobject fontConfig = NULL;

    if (this == NULL) {
        return JNI_FALSE;
    }

    if ((*env)->EnsureLocalCapacity(env, 2) < 0) {
        return JNI_FALSE;
    }

    peer = (*env)->CallObjectMethod(env,this,fontIDs.getPeer);
    if (peer == NULL) {
        return JNI_FALSE;
    }

    fontConfig = (*env)->GetObjectField(env,peer,platformFontIDs.fontConfig);
    (*env)->DeleteLocalRef(env, peer);

    if (fontConfig == NULL) {
        return JNI_FALSE;
    }
    (*env)->DeleteLocalRef(env, fontConfig);

    return JNI_TRUE;
}

jboolean
awtJNI_IsMultiFontMetrics(JNIEnv * env, jobject this)
{
    jobject peer = NULL;
    jobject fontConfig = NULL;
    jobject font = NULL;

    if (JNU_IsNull(env, this)) {
        return JNI_FALSE;
    }
    if ((*env)->EnsureLocalCapacity(env, 3) < 0) {
        return JNI_FALSE;
    }

    font = JNU_CallMethodByName(env, NULL, this, "getFont_NoClientCode",
                                "()Ljava/awt/Font;").l;
    if (JNU_IsNull(env, font)) {
        return JNI_FALSE;
    }

    peer = (*env)->CallObjectMethod(env,font,fontIDs.getPeer);
    (*env)->DeleteLocalRef(env, font);

    if (peer == NULL) {
        return JNI_FALSE;
    }

    fontConfig = (*env)->GetObjectField(env,peer,platformFontIDs.fontConfig);
    (*env)->DeleteLocalRef(env, peer);
    if (fontConfig == NULL) {
        return JNI_FALSE;
    }
    (*env)->DeleteLocalRef(env, fontConfig);

    return JNI_TRUE;
}
#ifndef XAWT
#ifdef __linux__
XmString
unicodeXmStringCreate(char* text, char* tag, int len) {
    XmString ret_val;
    XtProcessLock();
    ret_val = _XmStringNCreate (text, tag, len);
    XtProcessUnlock();
    return ret_val;
}
#endif

/*
 * Unicode to Motif Multi Font Compound String converter
 *
 * ASSUMES: We are not running on a privileged thread
 */
XmString
awtJNI_MakeMultiFontString(JNIEnv * env, jstring s, jobject font)
{
    XmString xmstr = NULL, xmtmp1, xmtmp2;
    jobjectArray dataArray = NULL;
    char *err = NULL;
    int32_t stringCount,i;
    int32_t fdnumber;
    struct FontData *fdata = awtJNI_GetFontData(env, font, &err);
    jobject fontDescriptor = NULL;
    jbyteArray data = NULL;
    char *stringData = NULL;
    char tag[BUFSIZ];

    if ((*env)->PushLocalFrame(env, 16) < 0)
        return NULL;

    if (!JNU_IsNull(env, s) && !JNU_IsNull(env, font)) {
        jobject peer;

        peer = (*env)->CallObjectMethod(env,font,fontIDs.getPeer);

        DASSERT(!awt_currentThreadIsPrivileged(env));
        dataArray =
            (*env)->CallObjectMethod(
                             env,
                             peer,
                             platformFontIDs.makeConvertedMultiFontString,
                             s);

        if ((*env)->ExceptionOccurred(env)) {
            (*env)->ExceptionDescribe(env);
            (*env)->ExceptionClear(env);

            (*env)->PopLocalFrame(env, NULL);
            return (XmString) NULL;
        }

        if(dataArray == NULL) {
            (*env)->PopLocalFrame(env, NULL);
            return (XmString) NULL;
        }
    } else {
        (*env)->PopLocalFrame(env, NULL);
        return (XmString) NULL;
    }

    stringCount = (*env)->GetArrayLength(env, dataArray);

    for (i = 0; i < stringCount; i+=2) {
        fontDescriptor = (*env)->GetObjectArrayElement(env, dataArray, i);
        data = (*env)->GetObjectArrayElement(env, dataArray, i + 1);

        /* Bail if we've finished */
        if(fontDescriptor == NULL || data == NULL)
            break;

        fdnumber = awtJNI_GetFontDescriptorNumber(env, font, fontDescriptor);
        fdata = awtJNI_GetFontData(env, font, &err);

        makeTag(fdata->flist[fdnumber].charset_name, fdnumber, tag);

        stringData = (char *)(*env)->GetPrimitiveArrayCritical(env, data, NULL);
        if(stringData != NULL) {
            unsigned char* buf = stringData;
            int len;
            char *offsetStringData;

            offsetStringData = stringData + (4 * sizeof(char));
#ifdef __linux__
            len = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
            /* Motif XmStringCreate() API requests "text must be a NULL-terminated
               string" and its implementation uses "strlen()" to calculate the length
               of the text string. Unfortunately when we deal with iso10646 font
               on linux, the "text" is requested to be encoded in UTF16, which has the
               posibility of including code points like "0xYY00" ("0xYY" + "0x00") that
               causes problem when XmStringCreate() calls _XmStringNCreate() without
               specifying a specific text lenth (see Motif XmString.c). The workaround is
               to call _XmStringNCreate() directly with specific text length at this
               cirsumstance.
            */
            if (strstr(fdata->flist[fdnumber].charset_name, "UnicodeBigUnmarked"))
                xmtmp1 = unicodeXmStringCreate(offsetStringData, tag, len);
            else
                xmtmp1 = XmStringCreate(offsetStringData, tag);
            if (xmstr == NULL)
                xmstr = xmtmp1;
            else {
                xmtmp2 = XmStringConcat(xmstr, xmtmp1);
                XmStringFree(xmtmp1);
                XmStringFree(xmstr);
                xmstr = xmtmp2;
            }
#else
            if(xmstr == NULL) {
                xmstr = XmStringCreate(offsetStringData, tag);
            }
            else {
                xmtmp1 = XmStringCreate(offsetStringData, tag);
                xmtmp2 = XmStringConcat(xmstr, xmtmp1);
                XmStringFree(xmtmp1);
                XmStringFree(xmstr);
                xmstr = xmtmp2;
            }
#endif
        }

        (*env)->ReleasePrimitiveArrayCritical(env, data, stringData, JNI_ABORT);
        (*env)->DeleteLocalRef(env, fontDescriptor);
        (*env)->DeleteLocalRef(env, data);
    }
    (*env)->PopLocalFrame(env, NULL);
    return xmstr;
}

/*
 * Find the character encoding for a given font and register that encoding
 * with the given tag.  The encoding is the last two fields of the XLFD of
 * the font (converted to uppercase).
 */
static void registerEncoding(char *xlfd, char *tag)
{
    char *e = xlfd + strlen(xlfd);
    char *ret = NULL;

    do { --e; } while (e != xlfd && *e != '-');
    do { --e; } while (e != xlfd && *e != '-');
    if (e != xlfd) {
        char *encoding = strdup(++e);
        char *u = NULL;

        for (u = encoding; *u != '\0'; ++u) {
            if (islower(*u)) {
                *u = toupper(*u);
            }
        }

        /*
         * Motif will core dump on or otherwise mishandle unknown (or
         * non-standard) character encodings (in conversion to compound
         * text, bug 4122785).  Register Sun private encodings for
         * Symbol or dingbat fonts as ISO8859-1, which is a lie,
         * but produces predictable results.
         */
        if (strncmp(encoding, "SUN-", 4) == 0) {
                free(encoding);
                encoding = strdup("ISO8859-1");
        }
        ret = XmRegisterSegmentEncoding(tag, encoding);
        if (ret != NULL)
                XtFree(ret);
        free(encoding);
    }
}


XmFontList
awtJNI_GetFontList(JNIEnv * env, jobject font)
{
    int32_t i;
    XmFontListEntry fle;
    XmFontList fontlist;
    XFontStruct *xf = NULL;
    int32_t size;
    struct FontData *fdata = NULL;
    char *err = NULL, tag[BUFSIZ];

    fdata = awtJNI_GetFontData(env, font, &err);

    makeTag(fdata->flist[0].charset_name, 0, tag);

    size = (int32_t) (*env)->GetIntField(env, font, fontIDs.size);

    if (fdata->flist[0].load == 0) {
        xf = loadFont(awt_display, fdata->flist[0].xlfd, size * 10);

        if (xf == NULL) {
            /* printf("Cannot load font: %s\n", fdata->list[0].xlfd); */
        } else {
            fdata->flist[0].xfont = xf;
            fdata->flist[0].load = 1;

            if (xf->min_byte1 == 0 && xf->max_byte1 == 0)
                fdata->flist[0].index_length = 1;
            else
                fdata->flist[0].index_length = 2;
        }
    }
    registerEncoding(fdata->flist[0].xlfd, tag);
    fle = XmFontListEntryCreate(tag, XmFONT_IS_FONT,
                                (XtPointer) fdata->flist[0].xfont);

    fontlist = XmFontListAppendEntry(NULL, fle);
    /*
     * Some versions of motif have a bug in
     * XmFontListEntryFree() which causes it to free more than it
     * should.  Use XtFree() is used instead.  See O'Reilly's
     * Motif Reference Manual for more information.
     */
    XmFontListEntryFree(&fle);

    for (i = 1; i < fdata->charset_num; i++) {
        makeTag(fdata->flist[i].charset_name, i, tag);

        if (fdata->flist[i].load == 0) {
            xf = loadFont(awt_display, fdata->flist[i].xlfd, size * 10);

            if (xf == NULL) {
                /* printf("Cannot load font: %s\n", fdata->flist[0].xlfd); */
                continue;
            }
            fdata->flist[i].xfont = xf;
            fdata->flist[i].load = 1;
            if (xf->min_byte1 == 0 && xf->max_byte1 == 0) {
                fdata->flist[i].index_length = 1;
            } else {
                fdata->flist[i].index_length = 2;
            }
        }
        registerEncoding(fdata->flist[i].xlfd, tag);
        fle = XmFontListEntryCreate(tag, XmFONT_IS_FONT,
                                    (XtPointer) fdata->flist[i].xfont);
        fontlist = XmFontListAppendEntry(fontlist, fle);
        /*
         * Some versions of motif have a bug in
         * XmFontListEntryFree() which causes it to free more than it
         * should.  Use XtFree() instead.  See O'Reilly's
         * Motif Reference Manual for more information.
         */
        XmFontListEntryFree(&fle);
    }

    return fontlist;
}
#endif
/* #define FONT_DEBUG 2 */

XFontSet
awtJNI_MakeFontSet(JNIEnv * env, jobject font)
{
    jstring xlfd = NULL;
    char *xfontset = NULL;
    int32_t size;
    int32_t length = 0;
    char *realxlfd = NULL, *ptr = NULL, *prev = NULL;
    char **missing_list = NULL;
    int32_t missing_count;
    char *def_string = NULL;
    XFontSet xfs;
    jobject peer = NULL;
    jstring xfsname = NULL;
#ifdef FONT_DEBUG
    char xx[1024];
#endif

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

    size = (*env)->GetIntField(env, font, fontIDs.size) * 10;

    peer = (*env)->CallObjectMethod(env,font,fontIDs.getPeer);
    xfsname = (*env)->GetObjectField(env, peer, mFontPeerIDs.xfsname);

    if (JNU_IsNull(env, xfsname))
        xfontset = "";
    else
        xfontset = (char *)JNU_GetStringPlatformChars(env, xfsname, NULL);

    realxlfd = malloc(strlen(xfontset) + 50);

    prev = ptr = xfontset;
    while ((ptr = strstr(ptr, "%d"))) {
        char save = *(ptr + 2);

        *(ptr + 2) = '\0';
        jio_snprintf(realxlfd + length, strlen(xfontset) + 50 - length,
                     prev, size);
        length = strlen(realxlfd);
        *(ptr + 2) = save;

        prev = ptr + 2;
        ptr += 2;
    }
    strcpy(realxlfd + length, prev);

#ifdef FONT_DEBUG
    strcpy(xx, realxlfd);
#endif
    xfs = XCreateFontSet(awt_display, realxlfd, &missing_list,
                         &missing_count, &def_string);
#if FONT_DEBUG >= 2
    fprintf(stderr, "XCreateFontSet(%s)->0x%x\n", xx, xfs);
#endif

#if FONT_DEBUG
    if (missing_count != 0) {
        int32_t i;
        fprintf(stderr, "XCreateFontSet missing %d fonts:\n", missing_count);
        for (i = 0; i < missing_count; ++i) {
            fprintf(stderr, "\t\"%s\"\n", missing_list[i]);
        }
        fprintf(stderr, "  requested \"%s\"\n", xx);
#if FONT_DEBUG >= 3
        exit(-1);
#endif
    }
#endif

    free((void *)realxlfd);

    if (xfontset && !JNU_IsNull(env, xfsname))
        JNU_ReleaseStringPlatformChars(env, xfsname, (const char *) xfontset);

    (*env)->DeleteLocalRef(env, peer);
    (*env)->DeleteLocalRef(env, xfsname);
    return xfs;
}

/*
 * get multi font string width with multiple X11 font
 *
 * ASSUMES: We are not running on a privileged thread
 */
int32_t
awtJNI_GetMFStringWidth(JNIEnv * env, jcharArray s, int offset, int sLength, jobject font)
{
    char *err = NULL;
    unsigned char *stringData = NULL;
    char *offsetStringData = NULL;
    int32_t stringCount, i;
    int32_t size;
    struct FontData *fdata = NULL;
    jobject fontDescriptor = NULL;
    jbyteArray data = NULL;
    int32_t j;
    int32_t width = 0;
    int32_t length;
    XFontStruct *xf = NULL;
    jobjectArray dataArray = NULL;
#ifndef XAWT
    DASSERT(!awt_currentThreadIsPrivileged(env));
#endif
    if ((*env)->EnsureLocalCapacity(env, 3) < 0)
        return 0;

    if (!JNU_IsNull(env, s) && !JNU_IsNull(env, font))
    {
        jobject peer;
        peer = (*env)->CallObjectMethod(env,font,fontIDs.getPeer);

        dataArray = (*env)->CallObjectMethod(
                                 env,
                                 peer,
                                 platformFontIDs.makeConvertedMultiFontChars,
                                 s, offset, sLength);

        if ((*env)->ExceptionOccurred(env))
        {
            (*env)->ExceptionDescribe(env);
            (*env)->ExceptionClear(env);
        }

        (*env)->DeleteLocalRef(env, peer);

        if(dataArray == NULL)
        {
            return 0;
        }
    } else {
        return 0;
    }

    fdata = awtJNI_GetFontData(env, font, &err);

    stringCount = (*env)->GetArrayLength(env, dataArray);

    size = (*env)->GetIntField(env, font, fontIDs.size);

    for (i = 0; i < stringCount; i+=2)
    {
        fontDescriptor = (*env)->GetObjectArrayElement(env, dataArray, i);
        data = (*env)->GetObjectArrayElement(env, dataArray, i + 1);

        /* Bail if we've finished */
        if (fontDescriptor == NULL || data == NULL) {
            (*env)->DeleteLocalRef(env, fontDescriptor);
            (*env)->DeleteLocalRef(env, data);
            break;
        }

        j = awtJNI_GetFontDescriptorNumber(env, font, fontDescriptor);

        if (fdata->flist[j].load == 0) {
            xf = loadFont(awt_display,
                          fdata->flist[j].xlfd, size * 10);
            if (xf == NULL) {
                (*env)->DeleteLocalRef(env, fontDescriptor);
                (*env)->DeleteLocalRef(env, data);
                continue;
            }
            fdata->flist[j].load = 1;
            fdata->flist[j].xfont = xf;
            if (xf->min_byte1 == 0 && xf->max_byte1 == 0)
                fdata->flist[j].index_length = 1;
            else
                fdata->flist[j].index_length = 2;
        }
        xf = fdata->flist[j].xfont;

        stringData =
            (unsigned char *)(*env)->GetPrimitiveArrayCritical(env, data,NULL);
        length = (stringData[0] << 24) | (stringData[1] << 16) |
            (stringData[2] << 8) | stringData[3];
        offsetStringData = (char *)(stringData + (4 * sizeof(char)));

        if (fdata->flist[j].index_length == 2) {
            width += XTextWidth16(xf, (XChar2b *)offsetStringData, length/2);
        } else {
            width += XTextWidth(xf, offsetStringData, length);
        }

        (*env)->ReleasePrimitiveArrayCritical(env, data, stringData, JNI_ABORT);
        (*env)->DeleteLocalRef(env, fontDescriptor);
        (*env)->DeleteLocalRef(env, data);
    }
    (*env)->DeleteLocalRef(env, dataArray);

    return width;
}