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