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