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