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