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 
  45 extern XFontStruct *loadFont(Display *, char *, int32_t);
  46 
  47 extern struct FontIDs fontIDs;
  48 extern struct PlatformFontIDs platformFontIDs;
  49 extern 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  */
  55 static void
  56 makeTag(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 
  65 static int32_t
  66 awtJNI_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 
 112 jobject
 113 awtJNI_GetFMFont(JNIEnv * env, jobject this)
 114 {
 115     return JNU_CallMethodByName(env, NULL, this, "getFont_NoClientCode",
 116                                 "()Ljava/awt/Font;").l;
 117 }
 118 
 119 jboolean
 120 awtJNI_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 
 149 jboolean
 150 awtJNI_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 
 188 XFontSet
 189 awtJNI_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  */
 274 int32_t
 275 awtJNI_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 }