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