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