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