microvg  3.0.0
microvg
LLVG_FONT_freetype.c
Go to the documentation of this file.
1 /*
2  * C
3  *
4  * Copyright 2020-2023 MicroEJ Corp. All rights reserved.
5  * Use of this source code is governed by a BSD-style license that can be found with this software.
6  */
7 
15 #include "microvg_configuration.h"
16 
17 #if defined VG_FEATURE_FONT && \
18  (defined VG_FEATURE_FONT_FREETYPE_VECTOR || defined VG_FEATURE_FONT_FREETYPE_BITMAP) && \
19  (VG_FEATURE_FONT == VG_FEATURE_FONT_FREETYPE_VECTOR || VG_FEATURE_FONT == VG_FEATURE_FONT_FREETYPE_BITMAP)
20 
21 // -----------------------------------------------------------------------------
22 // Includes
23 // -----------------------------------------------------------------------------
24 
25 #include <math.h>
26 
27 #include <aftypes.h>
28 
29 #include <LLVG_impl.h>
30 #include <LLVG_FONT_impl.h>
31 
32 #if defined (VG_FEATURE_FONT_EXTERNAL)
33 #include <LLEXT_RES_impl.h>
34 #include <freetype/internal/ftmemory.h>
35 #endif
36 
37 #include "microvg_font_freetype.h"
38 #include "microvg_helper.h"
39 #include "mej_math.h"
40 
41 // -----------------------------------------------------------------------------
42 // Macros and Defines
43 // -----------------------------------------------------------------------------
44 
45 /*
46  * @brief Computes the scale to apply to the font.
47  */
48 #define GET_SCALE(s,f) ((s) / (f)->units_per_EM)
49 
50 // -----------------------------------------------------------------------------
51 // Types
52 // -----------------------------------------------------------------------------
53 
54 /*
55  * @brief Structure to load a resource by calling SNIX_get_resource()
56  */
57 typedef struct {
58  void* data;
59  uint32_t size;
60 } SNIX_resource;
61 
62 // -----------------------------------------------------------------------------
63 // Extern functions
64 // -----------------------------------------------------------------------------
65 
66 /*
67  * @brief SNIX_get_resource() available since MicroEJ Architecture version 7.13
68  */
69 extern int32_t SNIX_get_resource(jchar* path, SNIX_resource* resource);
70 
71 // -----------------------------------------------------------------------------
72 // Internal function definitions
73 // -----------------------------------------------------------------------------
74 
75 /*
76  * @brief Disposes Freetype font when the associated Java object VectorFont is
77  * garbaged collected.
78  */
79 static void _dispose_registered_font(void* faceHandle);
80 
81 /*
82  * @brief Opens a font that has been loaded into memory.
83  *
84  * @see FT_New_Memory_Face in freetype.h
85  *
86  * @param[in] face: a handle to a new face object.
87  * @param[in] data: a pointer to the beginning of the font data.
88  * @param[in] length: the size of the memory chunk used by the font data.
89  *
90  * @return FreeType error code.
91  */
92 static FT_Error __load_memory_font(FT_Face* face, void* data, int length);
93 
94 /*
95  * @brief Opens a font that has been compiled with the application.
96  *
97  * @see FT_New_Memory_Face in freetype.h
98  *
99  * @param[in] face: a handle to a new face object.
100  * @param[in] font_name: a path to the font file.
101  *
102  * @return FreeType error code.
103  */
104 static FT_Error __load_internal_font(FT_Face* face, jchar* font_name);
105 
106 
107 #if defined (VG_FEATURE_FONT_EXTERNAL)
108 /*
109  * @brief Opens a font that has not been compiled with the application.
110  *
111  * @see FT_Open_Face in freetype.h
112  *
113  * @param[in] face: a handle to a new face object.
114  * @param[in] font_name: a path to the font file.
115  *
116  * @return FreeType error code.
117  */
118 static FT_Error __load_external_font(FT_Face* face, jchar* font_name);
119 
120 
121 
122 /*
123  * @brief Reads a chunk from an external resource.
124  *
125  * @see FT_Stream_IoFunc in ftsystem.h
126  *
127  * @param[in] stream: a handle to the source stream.
128  * @param[in] offset: the offset of read in stream.
129  * @param[in] buffer: the address of the read buffer.
130  * @param[in] count: the number of bytes to read from the stream.
131  *
132  * @return the number of bytes effectively read by the stream.
133  */
134 static unsigned long __read_external_resource(FT_Stream stream, unsigned long offset, unsigned char* buffer, unsigned long count);
135 
136 /*
137  * @brief Closes a given input stream.
138  *
139  * @see FT_Stream_IoFunc in ftsystem.h
140  *
141  * @param[in] stream: a handle to the target stream.
142  */
143 static void __close_external_resource(FT_Stream stream);
144 
145 #endif // VG_FEATURE_FONT_EXTERNAL
146 
147 // -----------------------------------------------------------------------------
148 // Global Variables
149 // -----------------------------------------------------------------------------
150 
151 FT_Library library;
152 FT_Renderer renderer;
153 
154 #if defined VG_FEATURE_FONT && defined VG_FEATURE_FONT_FREETYPE_VECTOR && (VG_FEATURE_FONT == VG_FEATURE_FONT_FREETYPE_VECTOR)
155 const char* renderer_name = "vglite_renderer";
156 #endif
157 #if defined VG_FEATURE_FONT && defined VG_FEATURE_FONT_FREETYPE_BITMAP && (VG_FEATURE_FONT == VG_FEATURE_FONT_FREETYPE_BITMAP)
158 const char* renderer_name = "smooth";
159 #endif
160 
161 // -----------------------------------------------------------------------------
162 // microvg_font_freetype.h functions
163 // -----------------------------------------------------------------------------
164 
165 // See the header file for the function documentation
166 void MICROVG_FONT_FREETYPE_initialize(void) {
167 
168  FT_Error error = FT_Init_FreeType(&library);
169  if(FT_ERR(Ok) == error) {
170  renderer = FT_RENDERER(FT_Get_Module(library, renderer_name));
171  if(0 != renderer) {
172  MEJ_LOG_INFO_MICROVG("Freetype renderer: %s\n", FT_MODULE_CLASS(renderer)->module_name);
173  }
174  else {
175  MEJ_LOG_ERROR_MICROVG("No renderer found with name %s\n", renderer_name);
176  }
177  }
178  else {
179  MEJ_LOG_ERROR_MICROVG("Internal freetype error initializing library, ID = %d\n",error);
180  }
181 }
182 
183 // -----------------------------------------------------------------------------
184 // LLVG_FONT_impl.h functions
185 // -----------------------------------------------------------------------------
186 
187 // See the header file for the function documentation
188 jint LLVG_FONT_IMPL_load_font(jchar* font_name, jboolean complex_layout) {
189 
190  FT_Face face = 0;
191  jint ret = LLVG_FONT_UNLOADED;
192 
193  // Check that complex layouter is available for complex text layout
194  if(complex_layout && !LLVG_FONT_IMPL_has_complex_layouter()){
195  ret = LLVG_FONT_UNLOADED;
196  }
197  else {
198  // try to load an internal resource
199  FT_Error error = __load_internal_font(&face, font_name);
200 
201 #if defined (VG_FEATURE_FONT_EXTERNAL)
202  if (FT_ERR(Cannot_Open_Resource) == error) {
203  // try to load an external resource
204  error = __load_external_font(&face, font_name);
205  }
206 #endif // VG_FEATURE_FONT_EXTERNAL
207 
208  if (FT_ERR(Ok) == error) {
209  // Use Unicode
210  FT_Select_Charmap(face , ft_encoding_unicode);
211  MEJ_LOG_INFO_MICROVG("Freetype font loaded: %s\n", (const char*)font_name);
212 
213  SNI_registerResource((void*)face, (SNI_closeFunction)&_dispose_registered_font, JNULL);
214 
215 #if defined (VG_FEATURE_FONT_COMPLEX_LAYOUT)
216  if(JTRUE == complex_layout){
217  face->face_flags |= FT_FACE_FLAG_COMPLEX_LAYOUT;
218  }
219 #endif // VG_FEATURE_FONT_COMPLEX_LAYOUT
220 
221  ret = (jint) face;
222  }
223  else if (FT_ERR(Cannot_Open_Resource) != error){
224  MEJ_LOG_ERROR_MICROVG("An error occurred during font loading: 0x%x, refer to fterrdef.h\n", error);
225  }
226  else {
227  MEJ_LOG_ERROR_MICROVG("Resource not found: %s\n", (const char*)font_name);
228  }
229 
230  }
231  return ret;
232 }
233 
234 // See the header file for the function documentation
235 jfloat LLVG_FONT_IMPL_string_width(jchar* text, jint faceHandle, jfloat size, jfloat letterSpacing) {
236 
237  jfloat ret;
238 
239  if (LLVG_FONT_UNLOADED == faceHandle) {
240  ret = (jfloat)LLVG_RESOURCE_CLOSED;
241  }
242  else {
243 
244  FT_Face face = (FT_Face) faceHandle;
245  float scaled_width = 0.f;
246 
247  if(size >= 0) {
248  float scale = GET_SCALE(size, face);
249 
250  int nb_chars = 0;
251  long unscaled_width = 0;
252 
253  // Layout variables
254  int glyph_index ; // current glyph index
255  int previous_glyph_index = 0; // previous glyph index for kerning
256 
257  int advance_x;
258  int previous_advance_x;
259  int previous_offset_x;
260  int advance_y;
261  int offset_x;
262  int offset_y;
263 
264  int length = (int)SNI_getArrayLength(text);
265  MICROVG_HELPER_layout_configure(faceHandle, text, length);
266 
267  while(MICROVG_HELPER_layout_load_glyph(&glyph_index, &advance_x, &advance_y, &offset_x, &offset_y)) {
268  // At that point the current glyph has been loaded by Freetype
269  if (0 == previous_glyph_index){
270  // first glyph: remove the first blank line
271  if(0 != face->glyph->metrics.width) {
272  unscaled_width -= face->glyph->metrics.horiBearingX;
273  }
274  else {
275  unscaled_width -= face->glyph->advance.x;
276  }
277  }
278 
279  unscaled_width += advance_x;
280  previous_glyph_index = glyph_index;
281  nb_chars ++;
282  // Last call to MICROVG_HELPER_layout_load_glyph clear advance_x and offset_x.
283  // We need to keep them for last glyph measurement
284  previous_advance_x = advance_x;
285  previous_offset_x = offset_x;
286  }
287 
288  // last glyph: remove the last blank line
289  if(0 != face->glyph->metrics.width) {
290  unscaled_width -= previous_advance_x;
291  unscaled_width += face->glyph->metrics.horiBearingX; // glyph's left blank line
292  unscaled_width += face->glyph->metrics.width; // glyph's width
293  unscaled_width += previous_offset_x; // glyph's offset_x
294  }
295  else {
296  if(0 != unscaled_width){
297  unscaled_width -= previous_advance_x;
298  }
299  }
300  scaled_width = ((scale * (float) unscaled_width) + (((float) nb_chars - 1) * letterSpacing));
301  }
302 
303  ret = scaled_width;
304  }
305 
306  return ret;
307 }
308 
309 // See the header file for the function documentation
310 jfloat LLVG_FONT_IMPL_string_height(jchar* text, jint faceHandle, jfloat size) {
311 
312  jfloat ret;
313 
314  if (LLVG_FONT_UNLOADED == faceHandle) {
315  ret = (jfloat)LLVG_RESOURCE_CLOSED;
316  }
317  else {
318 
319  FT_Face face = (FT_Face) faceHandle;
320  float scaled_height = 0.f;
321 
322  if(size >= 0) {
323  float scale = GET_SCALE(size, face);
324 
325  FT_Pos horiBearingYTop = 0;
326  FT_Pos horiBearingYBottom = 0;
327 
328  // Layout variables
329  int glyph_index ; // current glyph index
330  int previous_glyph_index = 0; // previous glyph index for kerning
331 
332  int advance_x;
333  int advance_y;
334  int offset_x;
335  int offset_y;
336 
337  int length = (int)SNI_getArrayLength(text);
338  MICROVG_HELPER_layout_configure(faceHandle, text, length);
339 
340  while(MICROVG_HELPER_layout_load_glyph(&glyph_index, &advance_x, &advance_y, &offset_x, &offset_y)) {
341  // At that point the current glyph has been loaded by Freetype
342 
343  FT_Pos yBottom = face->glyph->metrics.horiBearingY - face->glyph->metrics.height;
344  horiBearingYBottom = (0 == previous_glyph_index) ? yBottom : MEJ_MIN(yBottom, horiBearingYBottom);
345  horiBearingYTop = MEJ_MAX(face->glyph->metrics.horiBearingY, horiBearingYTop);
346 
347  previous_glyph_index = glyph_index;
348  }
349 
350  scaled_height = scale * (float) (horiBearingYTop - horiBearingYBottom);
351  }
352 
353  ret = scaled_height;
354  }
355 
356  return ret;
357 }
358 
359 // See the header file for the function documentation
360 jfloat LLVG_FONT_IMPL_get_baseline_position(jint faceHandle, jfloat size) {
361 
362  jfloat ret;
363 
364  if (LLVG_FONT_UNLOADED == faceHandle) {
365  ret = (jfloat)LLVG_RESOURCE_CLOSED;
366  }
367  else {
368 
369  FT_Face face = (FT_Face) faceHandle;
370  float advance_y = 0.f;
371 
372  if(size >= 0) {
373  float scale = GET_SCALE(size, face);
374  advance_y = (face->ascender * scale);
375  }
376  ret = advance_y;
377  }
378 
379  return ret;
380 }
381 
382 // See the header file for the function documentation
383 jfloat LLVG_FONT_IMPL_get_height(jint faceHandle, jfloat size) {
384 
385  jfloat ret;
386 
387  if (LLVG_FONT_UNLOADED == faceHandle) {
388  ret = (jfloat)LLVG_RESOURCE_CLOSED;
389  }
390  else {
391  FT_Face face = (FT_Face) faceHandle;
392  float scale = GET_SCALE(size, face);
393  ret = face->height * scale;
394  }
395 
396  return ret;
397 }
398 
399 // See the header file for the function documentation
400 void LLVG_FONT_IMPL_dispose(jint faceHandle) {
401  _dispose_registered_font((void*)faceHandle);
402 }
403 
404 // See the header file for the function documentation
405 bool LLVG_FONT_IMPL_has_complex_layouter(void){
406 #ifdef VG_FEATURE_FONT_COMPLEX_LAYOUT
407  return true;
408 #else
409  return false;
410 #endif
411 }
412 
413 // -----------------------------------------------------------------------------
414 // Internal functions
415 // -----------------------------------------------------------------------------
416 
417 static void _dispose_registered_font(void* faceHandle) {
418  FT_Face face = (FT_Face) faceHandle;
419 
420 #if defined (VG_FEATURE_FONT_EXTERNAL)
421  // FT_Done_Face() sets the stream to NULL: have to save it to close the
422  // external resource
423  FT_Stream stream = face->stream;
424 #endif // VG_FEATURE_FONT_EXTERNAL
425 
426  FT_Done_Face(face);
427 
428 #if defined (VG_FEATURE_FONT_EXTERNAL)
429  // frees the stream when the font is external (Freetype doesn't recommend
430  // to read the flag FT_FACE_FLAG_EXTERNAL_STREAM)
431  if (&__close_external_resource == stream->close) {
432  ft_mem_free(library->memory, stream);
433  }
434 #endif // VG_FEATURE_FONT_EXTERNAL
435 }
436 
437 static FT_Error __load_memory_font(FT_Face* face, void* data, int length) {
438  return FT_New_Memory_Face(library, data, length, 0, face);
439 }
440 
441 static FT_Error __load_internal_font(FT_Face* face, jchar* font_name) {
442  SNIX_resource font_resource;
443  FT_Error error;
444 
445  if (0 == SNIX_get_resource(font_name, &font_resource)) {
446  error = __load_memory_font(face, font_resource.data, font_resource.size);
447  }
448  else {
449  error = FT_ERR(Cannot_Open_Resource);
450  }
451  return error;
452 }
453 
454 #if defined (VG_FEATURE_FONT_EXTERNAL)
455 
456 static FT_Error __load_external_font(FT_Face* face, jchar* font_name) {
457 
458  FT_Error error;
459 
460  // try to load an external resource (respect LLEXT_RES_open path naming convention)
461  char const* ext_path = &(((char const*)(font_name))[1]) /* first '/' */;
462  RES_ID resource_id = LLEXT_RES_open(ext_path);
463 
464  if (0 <= resource_id) {
465 
466  int32_t resource_size = LLEXT_RES_available(resource_id);
467 
468  // check if the external resource is byte addressable
469  int32_t base_address = LLEXT_RES_getBaseAddress(resource_id);
470 
471  if (-1 != base_address) {
472 
473  // load the font as "memory" font
474  // cppcheck-suppress [misra-c2012-11.6] base_address is an address
475  error = __load_memory_font(face, (void*)base_address, resource_size);
476 
477  // we can close the resource because we know its address
478  LLEXT_RES_close(resource_id);
479  }
480  else {
481  // load the font as external font
482 
483  FT_Open_Args args;
484  args.flags = FT_OPEN_STREAM;
485  args.driver = NULL;
486  args.stream = (FT_StreamRec*)ft_mem_alloc(library->memory, sizeof(FT_StreamRec), &error);
487 
488  if (FT_ERR(Ok) == error) {
489 
490  args.stream->base = NULL;
491  args.stream->size = resource_size;
492  args.stream->pos = 0;
493  args.stream->descriptor.value = resource_id;
494  // args.stream->pathname = NULL; // not used
495  args.stream->read = &__read_external_resource;
496  args.stream->close = &__close_external_resource;
497 
498  error = FT_Open_Face(library, &args, 0, face);
499  }
500  else {
501  error = FT_ERR(Out_Of_Memory);
502  }
503  }
504  }
505  else {
506  error = FT_ERR(Cannot_Open_Resource);
507  }
508 
509  return error;
510 }
511 
512 static unsigned long __read_external_resource(FT_Stream stream, unsigned long offset, unsigned char* buffer, unsigned long count) {
513  int32_t size = count;
514  RES_ID resource_id = (RES_ID)(stream->descriptor.value);
515  LLEXT_RES_seek(resource_id, offset);
516  LLEXT_RES_read(resource_id, buffer, &size);
517  return size;
518 }
519 
520 static void __close_external_resource(FT_Stream stream) {
521  RES_ID resource_id = (RES_ID)(stream->descriptor.value);
522  LLEXT_RES_close(resource_id);
523 }
524 
525 #endif // VG_FEATURE_FONT_EXTERNAL
526 // cppcheck-suppress [misra-c2012-3.2]
527 #endif // defined VG_FEATURE_FONT && \
528  // (defined VG_FEATURE_FONT_FREETYPE_VECTOR || defined VG_FEATURE_FONT_FREETYPE_BITMAP) && \
529  // (VG_FEATURE_FONT == VG_FEATURE_FONT_FREETYPE_VECTOR || VG_FEATURE_FONT == VG_FEATURE_FONT_FREETYPE_BITMAP)
530 // -----------------------------------------------------------------------------
531 // EOF
532 // -----------------------------------------------------------------------------
533 
MicroEJ MicroVG library low level API: helper to implement library natives methods.
#define FT_FACE_FLAG_COMPLEX_LAYOUT
Freetype supplementary flag for complex layout Uses a free bit in freetype face flags to convey the c...
MicroEJ MicroVG library low level API: implementation over FreeType.
MicroEJ MicroVG library low level API: enable some features according to the hardware capacities...