1 /* 2 * Copyright (c) 2015, 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 #include "jlong.h" 27 #include "sun_font_SunLayoutEngine.h" 28 29 #include "hb.h" 30 #include "hb-jdk.h" 31 #ifdef MACOSX 32 #include "hb-coretext.h" 33 #endif 34 #include <stdlib.h> 35 36 #if defined(__GNUC__) && __GNUC__ >= 4 37 #define HB_UNUSED __attribute__((unused)) 38 #else 39 #define HB_UNUSED 40 #endif 41 42 43 static hb_bool_t 44 hb_jdk_get_nominal_glyph (hb_font_t *font HB_UNUSED, 45 void *font_data, 46 hb_codepoint_t unicode, 47 hb_codepoint_t *glyph, 48 void *user_data HB_UNUSED) 49 { 50 51 JDKFontInfo *jdkFontInfo = (JDKFontInfo*)font_data; 52 JNIEnv* env = jdkFontInfo->env; 53 jobject font2D = jdkFontInfo->font2D; 54 *glyph = (hb_codepoint_t)env->CallIntMethod( 55 font2D, sunFontIDs.f2dCharToGlyphMID, unicode); 56 if (env->ExceptionOccurred()) 57 { 58 env->ExceptionClear(); 59 } 60 if ((int)*glyph < 0) { 61 *glyph = 0; 62 } 63 return (*glyph != 0); 64 } 65 66 static hb_bool_t 67 hb_jdk_get_variation_glyph (hb_font_t *font HB_UNUSED, 68 void *font_data, 69 hb_codepoint_t unicode, 70 hb_codepoint_t variation_selector, 71 hb_codepoint_t *glyph, 72 void *user_data HB_UNUSED) 73 { 74 75 JDKFontInfo *jdkFontInfo = (JDKFontInfo*)font_data; 76 JNIEnv* env = jdkFontInfo->env; 77 jobject font2D = jdkFontInfo->font2D; 78 *glyph = (hb_codepoint_t)env->CallIntMethod( 79 font2D, sunFontIDs.f2dCharToVariationGlyphMID, 80 unicode, variation_selector); 81 if (env->ExceptionOccurred()) 82 { 83 env->ExceptionClear(); 84 } 85 if ((int)*glyph < 0) { 86 *glyph = 0; 87 } 88 return (*glyph != 0); 89 } 90 91 static hb_position_t 92 hb_jdk_get_glyph_h_advance (hb_font_t *font HB_UNUSED, 93 void *font_data, 94 hb_codepoint_t glyph, 95 void *user_data HB_UNUSED) 96 { 97 98 float fadv = 0.0f; 99 if ((glyph & 0xfffe) == 0xfffe) { 100 return 0; // JDK uses this glyph code. 101 } 102 103 JDKFontInfo *jdkFontInfo = (JDKFontInfo*)font_data; 104 JNIEnv* env = jdkFontInfo->env; 105 jobject fontStrike = jdkFontInfo->fontStrike; 106 jobject pt = env->CallObjectMethod(fontStrike, 107 sunFontIDs.getGlyphMetricsMID, glyph); 108 109 if (pt == NULL) { 110 return 0; 111 } 112 fadv = env->GetFloatField(pt, sunFontIDs.xFID); 113 fadv *= jdkFontInfo->devScale; 114 env->DeleteLocalRef(pt); 115 116 return HBFloatToFixed(fadv); 117 } 118 119 static hb_position_t 120 hb_jdk_get_glyph_v_advance (hb_font_t *font HB_UNUSED, 121 void *font_data, 122 hb_codepoint_t glyph, 123 void *user_data HB_UNUSED) 124 { 125 126 float fadv = 0.0f; 127 if ((glyph & 0xfffe) == 0xfffe) { 128 return 0; // JDK uses this glyph code. 129 } 130 131 JDKFontInfo *jdkFontInfo = (JDKFontInfo*)font_data; 132 JNIEnv* env = jdkFontInfo->env; 133 jobject fontStrike = jdkFontInfo->fontStrike; 134 jobject pt = env->CallObjectMethod(fontStrike, 135 sunFontIDs.getGlyphMetricsMID, glyph); 136 137 if (pt == NULL) { 138 return 0; 139 } 140 fadv = env->GetFloatField(pt, sunFontIDs.yFID); 141 env->DeleteLocalRef(pt); 142 143 return HBFloatToFixed(fadv); 144 145 } 146 147 static hb_bool_t 148 hb_jdk_get_glyph_h_origin (hb_font_t *font HB_UNUSED, 149 void *font_data HB_UNUSED, 150 hb_codepoint_t glyph HB_UNUSED, 151 hb_position_t *x HB_UNUSED, 152 hb_position_t *y HB_UNUSED, 153 void *user_data HB_UNUSED) 154 { 155 /* We always work in the horizontal coordinates. */ 156 return true; 157 } 158 159 static hb_bool_t 160 hb_jdk_get_glyph_v_origin (hb_font_t *font HB_UNUSED, 161 void *font_data, 162 hb_codepoint_t glyph, 163 hb_position_t *x, 164 hb_position_t *y, 165 void *user_data HB_UNUSED) 166 { 167 return false; 168 } 169 170 static hb_position_t 171 hb_jdk_get_glyph_h_kerning (hb_font_t *font, 172 void *font_data, 173 hb_codepoint_t lejdk_glyph, 174 hb_codepoint_t right_glyph, 175 void *user_data HB_UNUSED) 176 { 177 /* Not implemented. This seems to be in the HB API 178 * as a way to fall back to Freetype's kerning support 179 * which could be based on some on-the fly glyph analysis. 180 * But more likely it reads the kern table. That is easy 181 * enough code to add if we find a need to fall back 182 * to that instead of using gpos. It seems like if 183 * there is a gpos table at all, the practice is to 184 * use that and ignore kern, no matter that gpos does 185 * not implement the kern feature. 186 */ 187 return 0; 188 } 189 190 static hb_position_t 191 hb_jdk_get_glyph_v_kerning (hb_font_t *font HB_UNUSED, 192 void *font_data HB_UNUSED, 193 hb_codepoint_t top_glyph HB_UNUSED, 194 hb_codepoint_t bottom_glyph HB_UNUSED, 195 void *user_data HB_UNUSED) 196 { 197 /* OpenType doesn't have vertical-kerning other than GPOS. */ 198 return 0; 199 } 200 201 static hb_bool_t 202 hb_jdk_get_glyph_extents (hb_font_t *font HB_UNUSED, 203 void *font_data, 204 hb_codepoint_t glyph, 205 hb_glyph_extents_t *extents, 206 void *user_data HB_UNUSED) 207 { 208 /* TODO */ 209 return false; 210 } 211 212 static hb_bool_t 213 hb_jdk_get_glyph_contour_point (hb_font_t *font HB_UNUSED, 214 void *font_data, 215 hb_codepoint_t glyph, 216 unsigned int point_index, 217 hb_position_t *x, 218 hb_position_t *y, 219 void *user_data HB_UNUSED) 220 { 221 if ((glyph & 0xfffe) == 0xfffe) { 222 *x = 0; *y = 0; 223 return true; 224 } 225 226 JDKFontInfo *jdkFontInfo = (JDKFontInfo*)font_data; 227 JNIEnv* env = jdkFontInfo->env; 228 jobject fontStrike = jdkFontInfo->fontStrike; 229 jobject pt = env->CallObjectMethod(fontStrike, 230 sunFontIDs.getGlyphPointMID, 231 glyph, point_index); 232 233 if (pt == NULL) { 234 *x = 0; *y = 0; 235 return true; 236 } 237 *x = HBFloatToFixed(env->GetFloatField(pt, sunFontIDs.xFID)); 238 *y = HBFloatToFixed(env->GetFloatField(pt, sunFontIDs.yFID)); 239 env->DeleteLocalRef(pt); 240 241 return true; 242 } 243 244 static hb_bool_t 245 hb_jdk_get_glyph_name (hb_font_t *font HB_UNUSED, 246 void *font_data, 247 hb_codepoint_t glyph, 248 char *name, unsigned int size, 249 void *user_data HB_UNUSED) 250 { 251 return false; 252 } 253 254 static hb_bool_t 255 hb_jdk_get_glyph_from_name (hb_font_t *font HB_UNUSED, 256 void *font_data, 257 const char *name, int len, 258 hb_codepoint_t *glyph, 259 void *user_data HB_UNUSED) 260 { 261 return false; 262 } 263 264 // remind : can we initialise this from the code we call 265 // from the class static method in Java to make it 266 // completely thread safe. 267 static hb_font_funcs_t * 268 _hb_jdk_get_font_funcs (void) 269 { 270 static hb_font_funcs_t *jdk_ffuncs = NULL; 271 hb_font_funcs_t *ff; 272 273 if (!jdk_ffuncs) { 274 ff = hb_font_funcs_create(); 275 276 hb_font_funcs_set_nominal_glyph_func(ff, hb_jdk_get_nominal_glyph, NULL, NULL); 277 hb_font_funcs_set_variation_glyph_func(ff, hb_jdk_get_variation_glyph, NULL, NULL); 278 hb_font_funcs_set_glyph_h_advance_func(ff, 279 hb_jdk_get_glyph_h_advance, NULL, NULL); 280 hb_font_funcs_set_glyph_v_advance_func(ff, 281 hb_jdk_get_glyph_v_advance, NULL, NULL); 282 hb_font_funcs_set_glyph_h_origin_func(ff, 283 hb_jdk_get_glyph_h_origin, NULL, NULL); 284 hb_font_funcs_set_glyph_v_origin_func(ff, 285 hb_jdk_get_glyph_v_origin, NULL, NULL); 286 hb_font_funcs_set_glyph_h_kerning_func(ff, 287 hb_jdk_get_glyph_h_kerning, NULL, NULL); 288 hb_font_funcs_set_glyph_v_kerning_func(ff, 289 hb_jdk_get_glyph_v_kerning, NULL, NULL); 290 hb_font_funcs_set_glyph_extents_func(ff, 291 hb_jdk_get_glyph_extents, NULL, NULL); 292 hb_font_funcs_set_glyph_contour_point_func(ff, 293 hb_jdk_get_glyph_contour_point, NULL, NULL); 294 hb_font_funcs_set_glyph_name_func(ff, 295 hb_jdk_get_glyph_name, NULL, NULL); 296 hb_font_funcs_set_glyph_from_name_func(ff, 297 hb_jdk_get_glyph_from_name, NULL, NULL); 298 hb_font_funcs_make_immutable(ff); // done setting functions. 299 jdk_ffuncs = ff; 300 } 301 return jdk_ffuncs; 302 } 303 304 static void _do_nothing(void) { 305 } 306 307 static void _free_nothing(void*) { 308 } 309 310 struct Font2DPtr { 311 JavaVM* vmPtr; 312 jweak font2DRef; 313 TTLayoutTableCache* layoutTables; 314 }; 315 316 static void cleanupFontInfo(void* data) { 317 Font2DPtr* fontInfo; 318 JNIEnv* env; 319 320 fontInfo = (Font2DPtr*) data; 321 fontInfo->vmPtr->GetEnv((void**)&env, JNI_VERSION_1_1); 322 env->DeleteWeakGlobalRef(fontInfo->font2DRef); 323 free(data); 324 } 325 326 static hb_blob_t * 327 reference_table(hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) { 328 329 Font2DPtr *fontInfo; 330 JNIEnv* env; 331 jobject font2D; 332 jsize length = 0; 333 void* buffer = NULL; 334 int cacheIdx = 0; 335 TTLayoutTableCache* layoutTables; 336 337 fontInfo = (Font2DPtr*)user_data; 338 fontInfo->vmPtr->GetEnv((void**)&env, JNI_VERSION_1_1); 339 if (env == NULL) { 340 return NULL; 341 } 342 font2D = fontInfo->font2DRef; 343 layoutTables = fontInfo->layoutTables; 344 345 // HB_TAG_NONE is 0 and is used to get the whole font file. 346 // It is not expected to be needed for JDK. 347 if (tag == 0 || layoutTables == NULL) { 348 return NULL; 349 } 350 351 for (cacheIdx=0; cacheIdx<LAYOUTCACHE_ENTRIES; cacheIdx++) { 352 if (tag == layoutTables->entries[cacheIdx].tag) break; 353 } 354 355 if (cacheIdx < LAYOUTCACHE_ENTRIES) { // if found 356 if (layoutTables->entries[cacheIdx].len != -1) { 357 length = layoutTables->entries[cacheIdx].len; 358 buffer = (void*)layoutTables->entries[cacheIdx].ptr; 359 } 360 } 361 362 if (buffer == NULL) { 363 jbyteArray tableBytes = (jbyteArray) 364 env->CallObjectMethod(font2D, sunFontIDs.getTableBytesMID, tag); 365 if (tableBytes == NULL) { 366 return NULL; 367 } 368 length = env->GetArrayLength(tableBytes); 369 buffer = calloc(length, sizeof(jbyte)); 370 env->GetByteArrayRegion(tableBytes, 0, length, (jbyte*)buffer); 371 372 if (cacheIdx >= LAYOUTCACHE_ENTRIES) { // not a cacheable table 373 return hb_blob_create((const char *)buffer, length, 374 HB_MEMORY_MODE_WRITABLE, 375 buffer, free); 376 } else { 377 layoutTables->entries[cacheIdx].len = length; 378 layoutTables->entries[cacheIdx].ptr = buffer; 379 } 380 } 381 382 return hb_blob_create((const char *)buffer, length, 383 HB_MEMORY_MODE_READONLY, 384 NULL, _free_nothing); 385 } 386 387 extern "C" { 388 389 /* 390 * Class: sun_font_SunLayoutEngine 391 * Method: createFace 392 * Signature: (Lsun/font/Font2D;ZJJ)J 393 */ 394 JNIEXPORT jlong JNICALL Java_sun_font_SunLayoutEngine_createFace(JNIEnv *env, 395 jclass cls, 396 jobject font2D, 397 jboolean aat, 398 jlong platformFontPtr, 399 jlong layoutTables) { 400 #ifdef MACOSX 401 if (aat && platformFontPtr) { 402 hb_face_t *face = hb_coretext_face_create((CGFontRef)platformFontPtr); 403 return ptr_to_jlong(face); 404 } 405 #endif 406 Font2DPtr *fi = (Font2DPtr*)malloc(sizeof(Font2DPtr)); 407 if (!fi) { 408 return 0; 409 } 410 JavaVM* vmPtr; 411 env->GetJavaVM(&vmPtr); 412 fi->vmPtr = vmPtr; 413 fi->layoutTables = (TTLayoutTableCache*)layoutTables; 414 fi->font2DRef = env->NewWeakGlobalRef(font2D); 415 if (!fi->font2DRef) { 416 free(fi); 417 return 0; 418 } 419 hb_face_t *face = hb_face_create_for_tables(reference_table, fi, 420 cleanupFontInfo); 421 return ptr_to_jlong(face); 422 } 423 424 /* 425 * Class: sun_font_SunLayoutEngine 426 * Method: disposeFace 427 * Signature: (J)V 428 */ 429 JNIEXPORT void JNICALL Java_sun_font_SunLayoutEngine_disposeFace(JNIEnv *env, 430 jclass cls, 431 jlong ptr) { 432 hb_face_t* face = (hb_face_t*) jlong_to_ptr(ptr); 433 hb_face_destroy(face); 434 } 435 436 } // extern "C" 437 438 static hb_font_t* _hb_jdk_font_create(hb_face_t* face, 439 JDKFontInfo *jdkFontInfo, 440 hb_destroy_func_t destroy) { 441 442 hb_font_t *font; 443 444 font = hb_font_create(face); 445 hb_font_set_funcs (font, 446 _hb_jdk_get_font_funcs (), 447 jdkFontInfo, (hb_destroy_func_t) _do_nothing); 448 hb_font_set_scale (font, 449 HBFloatToFixed(jdkFontInfo->ptSize*jdkFontInfo->devScale), 450 HBFloatToFixed(jdkFontInfo->ptSize*jdkFontInfo->devScale)); 451 return font; 452 } 453 454 #ifdef MACOSX 455 static hb_font_t* _hb_jdk_ct_font_create(hb_face_t* face, 456 JDKFontInfo *jdkFontInfo) { 457 458 hb_font_t *font = NULL; 459 font = hb_font_create(face); 460 hb_font_set_scale(font, 461 HBFloatToFixed(jdkFontInfo->ptSize), 462 HBFloatToFixed(jdkFontInfo->ptSize)); 463 return font; 464 } 465 #endif 466 467 hb_font_t* hb_jdk_font_create(hb_face_t* hbFace, 468 JDKFontInfo *jdkFontInfo, 469 hb_destroy_func_t destroy) { 470 #ifdef MACOSX 471 if (jdkFontInfo->aat && jdkFontInfo->nativeFont) { 472 return _hb_jdk_ct_font_create(hbFace, jdkFontInfo); 473 } 474 #endif 475 return _hb_jdk_font_create(hbFace, jdkFontInfo, destroy); 476 }