1/*
2 * Copyright (c) 1996, 2014, 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/*
27 * These routines are used for display string with multi font.
28 */
29
30#ifdef HEADLESS
31    #error This file should not be included in headless library
32#endif
33
34#include <stdlib.h>
35#include <string.h>
36#include <math.h>
37#include <ctype.h>
38#include <jni.h>
39#include <jni_util.h>
40#include <jvm.h>
41#include "awt_Font.h"
42#include "awt_p.h"
43#include "multi_font.h"
44
45extern XFontStruct *loadFont(Display *, char *, int32_t);
46
47extern struct FontIDs fontIDs;
48extern struct PlatformFontIDs platformFontIDs;
49extern struct XFontPeerIDs xFontPeerIDs;
50
51/*
52 * make string with str + string representation of num
53 * This string is used as tag string of Motif Compound String and FontList.
54 */
55static void
56makeTag(char *str, int32_t num, char *buf)
57{
58    int32_t len = strlen(str);
59
60    strcpy(buf, str);
61    buf[len] = '0' + num % 100;
62    buf[len + 1] = '\0';
63}
64
65static int32_t
66awtJNI_GetFontDescriptorNumber(JNIEnv * env
67                               ,jobject font
68                               ,jobject fd)
69{
70    int32_t i = 0, num;
71    /* initialize to NULL so that DeleteLocalRef will work. */
72    jobjectArray componentFonts = NULL;
73    jobject peer = NULL;
74    jobject temp = NULL;
75    jboolean validRet = JNI_FALSE;
76
77    if ((*env)->EnsureLocalCapacity(env, 2) < 0 || (*env)->ExceptionCheck(env))
78        goto done;
79
80    peer = (*env)->CallObjectMethod(env,font,fontIDs.getPeer);
81    if (peer == NULL)
82        goto done;
83
84    componentFonts = (jobjectArray)
85        (*env)->GetObjectField(env,peer,platformFontIDs.componentFonts);
86
87    if (componentFonts == NULL)
88        goto done;
89
90    num = (*env)->GetArrayLength(env, componentFonts);
91
92    for (i = 0; i < num; i++) {
93        temp = (*env)->GetObjectArrayElement(env, componentFonts, i);
94
95        if ((*env)->IsSameObject(env, fd, temp)) {
96            validRet = JNI_TRUE;
97            break;
98        }
99        (*env)->DeleteLocalRef(env, temp);
100    }
101
102 done:
103    (*env)->DeleteLocalRef(env, peer);
104    (*env)->DeleteLocalRef(env, componentFonts);
105
106    if (validRet)
107        return i;
108
109    return 0;
110}
111
112jobject
113awtJNI_GetFMFont(JNIEnv * env, jobject this)
114{
115    return JNU_CallMethodByName(env, NULL, this, "getFont_NoClientCode",
116                                "()Ljava/awt/Font;").l;
117}
118
119jboolean
120awtJNI_IsMultiFont(JNIEnv * env, jobject this)
121{
122    jobject peer = NULL;
123    jobject fontConfig = NULL;
124
125    if (this == NULL) {
126        return JNI_FALSE;
127    }
128
129    if ((*env)->EnsureLocalCapacity(env, 2) < 0) {
130        return JNI_FALSE;
131    }
132
133    peer = (*env)->CallObjectMethod(env,this,fontIDs.getPeer);
134    if (peer == NULL) {
135        return JNI_FALSE;
136    }
137
138    fontConfig = (*env)->GetObjectField(env,peer,platformFontIDs.fontConfig);
139    (*env)->DeleteLocalRef(env, peer);
140
141    if (fontConfig == NULL) {
142        return JNI_FALSE;
143    }
144    (*env)->DeleteLocalRef(env, fontConfig);
145
146    return JNI_TRUE;
147}
148
149jboolean
150awtJNI_IsMultiFontMetrics(JNIEnv * env, jobject this)
151{
152    jobject peer = NULL;
153    jobject fontConfig = NULL;
154    jobject font = NULL;
155
156    if (JNU_IsNull(env, this)) {
157        return JNI_FALSE;
158    }
159    if ((*env)->EnsureLocalCapacity(env, 3) < 0) {
160        return JNI_FALSE;
161    }
162
163    font = JNU_CallMethodByName(env, NULL, this, "getFont_NoClientCode",
164                                "()Ljava/awt/Font;").l;
165    if (JNU_IsNull(env, font) || (*env)->ExceptionCheck(env)) {
166        return JNI_FALSE;
167    }
168
169    peer = (*env)->CallObjectMethod(env,font,fontIDs.getPeer);
170    (*env)->DeleteLocalRef(env, font);
171
172    if (peer == NULL) {
173        return JNI_FALSE;
174    }
175
176    fontConfig = (*env)->GetObjectField(env,peer,platformFontIDs.fontConfig);
177    (*env)->DeleteLocalRef(env, peer);
178    if (fontConfig == NULL) {
179        return JNI_FALSE;
180    }
181    (*env)->DeleteLocalRef(env, fontConfig);
182
183    return JNI_TRUE;
184}
185
186/* #define FONT_DEBUG 2 */
187
188XFontSet
189awtJNI_MakeFontSet(JNIEnv * env, jobject font)
190{
191    jstring xlfd = NULL;
192    char *xfontset = NULL;
193    int32_t size;
194    int32_t length = 0;
195    char *realxlfd = NULL, *ptr = NULL, *prev = NULL;
196    char **missing_list = NULL;
197    int32_t missing_count;
198    char *def_string = NULL;
199    XFontSet xfs;
200    jobject peer = NULL;
201    jstring xfsname = NULL;
202#ifdef FONT_DEBUG
203    char xx[1024];
204#endif
205
206    if ((*env)->EnsureLocalCapacity(env, 2) < 0)
207        return 0;
208
209    size = (*env)->GetIntField(env, font, fontIDs.size) * 10;
210
211    peer = (*env)->CallObjectMethod(env,font,fontIDs.getPeer);
212    xfsname = (*env)->GetObjectField(env, peer, xFontPeerIDs.xfsname);
213
214    if (JNU_IsNull(env, xfsname))
215        xfontset = "";
216    else
217        xfontset = (char *)JNU_GetStringPlatformChars(env, xfsname, NULL);
218
219    realxlfd = malloc(strlen(xfontset) + 50);
220
221    prev = ptr = xfontset;
222    while ((ptr = strstr(ptr, "%d"))) {
223        char save = *(ptr + 2);
224
225        *(ptr + 2) = '\0';
226        jio_snprintf(realxlfd + length, strlen(xfontset) + 50 - length,
227                     prev, size);
228        length = strlen(realxlfd);
229        *(ptr + 2) = save;
230
231        prev = ptr + 2;
232        ptr += 2;
233    }
234    strcpy(realxlfd + length, prev);
235
236#ifdef FONT_DEBUG
237    strcpy(xx, realxlfd);
238#endif
239    xfs = XCreateFontSet(awt_display, realxlfd, &missing_list,
240                         &missing_count, &def_string);
241#if FONT_DEBUG >= 2
242    fprintf(stderr, "XCreateFontSet(%s)->0x%x\n", xx, xfs);
243#endif
244
245#if FONT_DEBUG
246    if (missing_count != 0) {
247        int32_t i;
248        fprintf(stderr, "XCreateFontSet missing %d fonts:\n", missing_count);
249        for (i = 0; i < missing_count; ++i) {
250            fprintf(stderr, "\t\"%s\"\n", missing_list[i]);
251        }
252        fprintf(stderr, "  requested \"%s\"\n", xx);
253#if FONT_DEBUG >= 3
254        exit(-1);
255#endif
256    }
257#endif
258
259    free((void *)realxlfd);
260
261    if (xfontset && !JNU_IsNull(env, xfsname))
262        JNU_ReleaseStringPlatformChars(env, xfsname, (const char *) xfontset);
263
264    (*env)->DeleteLocalRef(env, peer);
265    (*env)->DeleteLocalRef(env, xfsname);
266    return xfs;
267}
268
269/*
270 * get multi font string width with multiple X11 font
271 *
272 * ASSUMES: We are not running on a privileged thread
273 */
274int32_t
275awtJNI_GetMFStringWidth(JNIEnv * env, jcharArray s, int offset, int sLength, jobject font)
276{
277    char *err = NULL;
278    unsigned char *stringData = NULL;
279    char *offsetStringData = NULL;
280    int32_t stringCount, i;
281    int32_t size;
282    struct FontData *fdata = NULL;
283    jobject fontDescriptor = NULL;
284    jbyteArray data = NULL;
285    int32_t j;
286    int32_t width = 0;
287    int32_t length;
288    XFontStruct *xf = NULL;
289    jobjectArray dataArray = NULL;
290    if ((*env)->EnsureLocalCapacity(env, 3) < 0)
291        return 0;
292
293    if (!JNU_IsNull(env, s) && !JNU_IsNull(env, font))
294    {
295        jobject peer;
296        peer = (*env)->CallObjectMethod(env,font,fontIDs.getPeer);
297
298        dataArray = (*env)->CallObjectMethod(
299                                 env,
300                                 peer,
301                                 platformFontIDs.makeConvertedMultiFontChars,
302                                 s, offset, sLength);
303
304        if ((*env)->ExceptionOccurred(env))
305        {
306            (*env)->ExceptionDescribe(env);
307            (*env)->ExceptionClear(env);
308        }
309
310        (*env)->DeleteLocalRef(env, peer);
311
312        if(dataArray == NULL)
313        {
314            return 0;
315        }
316    } else {
317        return 0;
318    }
319
320    fdata = awtJNI_GetFontData(env, font, &err);
321    if ((*env)->ExceptionCheck(env)) {
322        (*env)->DeleteLocalRef(env, dataArray);
323        return 0;
324    }
325
326    stringCount = (*env)->GetArrayLength(env, dataArray);
327
328    size = (*env)->GetIntField(env, font, fontIDs.size);
329
330    for (i = 0; i < stringCount; i+=2)
331    {
332        fontDescriptor = (*env)->GetObjectArrayElement(env, dataArray, i);
333        data = (*env)->GetObjectArrayElement(env, dataArray, i + 1);
334
335        /* Bail if we've finished */
336        if (fontDescriptor == NULL || data == NULL) {
337            (*env)->DeleteLocalRef(env, fontDescriptor);
338            (*env)->DeleteLocalRef(env, data);
339            break;
340        }
341
342        j = awtJNI_GetFontDescriptorNumber(env, font, fontDescriptor);
343        if ((*env)->ExceptionCheck(env)) {
344            (*env)->DeleteLocalRef(env, fontDescriptor);
345            (*env)->DeleteLocalRef(env, data);
346            break;
347        }
348
349        if (fdata->flist[j].load == 0) {
350            xf = loadFont(awt_display,
351                          fdata->flist[j].xlfd, size * 10);
352            if (xf == NULL) {
353                (*env)->DeleteLocalRef(env, fontDescriptor);
354                (*env)->DeleteLocalRef(env, data);
355                continue;
356            }
357            fdata->flist[j].load = 1;
358            fdata->flist[j].xfont = xf;
359            if (xf->min_byte1 == 0 && xf->max_byte1 == 0)
360                fdata->flist[j].index_length = 1;
361            else
362                fdata->flist[j].index_length = 2;
363        }
364        xf = fdata->flist[j].xfont;
365
366        stringData =
367            (unsigned char *)(*env)->GetPrimitiveArrayCritical(env, data,NULL);
368        if (stringData == NULL) {
369            (*env)->DeleteLocalRef(env, fontDescriptor);
370            (*env)->DeleteLocalRef(env, data);
371            (*env)->ExceptionClear(env);
372            JNU_ThrowOutOfMemoryError(env, "Could not get string data");
373            break;
374        }
375
376        length = (stringData[0] << 24) | (stringData[1] << 16) |
377            (stringData[2] << 8) | stringData[3];
378        offsetStringData = (char *)(stringData + (4 * sizeof(char)));
379
380        if (fdata->flist[j].index_length == 2) {
381            width += XTextWidth16(xf, (XChar2b *)offsetStringData, length/2);
382        } else {
383            width += XTextWidth(xf, offsetStringData, length);
384        }
385
386        (*env)->ReleasePrimitiveArrayCritical(env, data, stringData, JNI_ABORT);
387        (*env)->DeleteLocalRef(env, fontDescriptor);
388        (*env)->DeleteLocalRef(env, data);
389    }
390    (*env)->DeleteLocalRef(env, dataArray);
391
392    return width;
393}
394