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