1 /**
2 * @file lv_font_loader.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9
10 #include <stdint.h>
11 #include <stdbool.h>
12
13 #include "../lvgl.h"
14 #include "../lv_misc/lv_fs.h"
15 #include "lv_font_loader.h"
16
17 #if LV_USE_FILESYSTEM
18
19
20 /**********************
21 * TYPEDEFS
22 **********************/
23 typedef struct {
24 lv_fs_file_t * fp;
25 int8_t bit_pos;
26 uint8_t byte_value;
27 } bit_iterator_t;
28
29 typedef struct font_header_bin {
30 uint32_t version;
31 uint16_t tables_count;
32 uint16_t font_size;
33 uint16_t ascent;
34 int16_t descent;
35 uint16_t typo_ascent;
36 int16_t typo_descent;
37 uint16_t typo_line_gap;
38 int16_t min_y;
39 int16_t max_y;
40 uint16_t default_advance_width;
41 uint16_t kerning_scale;
42 uint8_t index_to_loc_format;
43 uint8_t glyph_id_format;
44 uint8_t advance_width_format;
45 uint8_t bits_per_pixel;
46 uint8_t xy_bits;
47 uint8_t wh_bits;
48 uint8_t advance_width_bits;
49 uint8_t compression_id;
50 uint8_t subpixels_mode;
51 uint8_t padding;
52 } font_header_bin_t;
53
54 typedef struct cmap_table_bin {
55 uint32_t data_offset;
56 uint32_t range_start;
57 uint16_t range_length;
58 uint16_t glyph_id_start;
59 uint16_t data_entries_count;
60 uint8_t format_type;
61 uint8_t padding;
62 } cmap_table_bin_t;
63
64
65 /**********************
66 * STATIC PROTOTYPES
67 **********************/
68 static bit_iterator_t init_bit_iterator(lv_fs_file_t * fp);
69 static bool lvgl_load_font(lv_fs_file_t * fp, lv_font_t * font);
70 int32_t load_kern(lv_fs_file_t * fp, lv_font_fmt_txt_dsc_t * font_dsc, uint8_t format, uint32_t start);
71
72 static int read_bits_signed(bit_iterator_t * it, int n_bits, lv_fs_res_t * res);
73 static int read_bits(bit_iterator_t * it, int n_bits, lv_fs_res_t * res);
74
75
76 /**********************
77 * MACROS
78 **********************/
79
80 /**********************
81 * GLOBAL FUNCTIONS
82 **********************/
83
84 /**
85 * Loads a `lv_font_t` object from a binary font file
86 * @param font_name filename where the font file is located
87 * @return a pointer to the font or NULL in case of error
88 */
lv_font_load(const char * font_name)89 lv_font_t * lv_font_load(const char * font_name)
90 {
91 bool success = false;
92
93 lv_font_t * font = lv_mem_alloc(sizeof(lv_font_t));
94 memset(font, 0, sizeof(lv_font_t));
95
96 lv_fs_file_t file;
97 lv_fs_res_t res = lv_fs_open(&file, font_name, LV_FS_MODE_RD);
98
99 if(res == LV_FS_RES_OK) {
100 success = lvgl_load_font(&file, font);
101 }
102
103 if(!success) {
104 LV_LOG_WARN("Error loading font file: %s\n", font_name);
105 /*
106 * When `lvgl_load_font` fails it can leak some pointers.
107 * All non-null pointers can be assumed as allocated and
108 * `lv_font_free` should free them correctly.
109 */
110 lv_font_free(font);
111 font = NULL;
112 }
113
114 lv_fs_close(&file);
115
116 return font;
117 }
118
119
120 /**
121 * Frees the memory allocated by the `lv_font_load()` function
122 * @param font lv_font_t object created by the lv_font_load function
123 */
lv_font_free(lv_font_t * font)124 void lv_font_free(lv_font_t * font)
125 {
126 if(NULL != font) {
127 lv_font_fmt_txt_dsc_t * dsc = (lv_font_fmt_txt_dsc_t *) font->dsc;
128
129 if(NULL != dsc) {
130
131 if(dsc->kern_classes == 0) {
132 lv_font_fmt_txt_kern_pair_t * kern_dsc =
133 (lv_font_fmt_txt_kern_pair_t *) dsc->kern_dsc;
134
135 if(NULL != kern_dsc) {
136 if(kern_dsc->glyph_ids)
137 lv_mem_free((void *) kern_dsc->glyph_ids);
138
139 if(kern_dsc->values)
140 lv_mem_free((void *) kern_dsc->values);
141
142 lv_mem_free((void *) kern_dsc);
143 }
144 }
145 else {
146 lv_font_fmt_txt_kern_classes_t * kern_dsc =
147 (lv_font_fmt_txt_kern_classes_t *) dsc->kern_dsc;
148
149 if(NULL != kern_dsc) {
150 if(kern_dsc->class_pair_values)
151 lv_mem_free((void *) kern_dsc->class_pair_values);
152
153 if(kern_dsc->left_class_mapping)
154 lv_mem_free((void *) kern_dsc->left_class_mapping);
155
156 if(kern_dsc->right_class_mapping)
157 lv_mem_free((void *) kern_dsc->right_class_mapping);
158
159 lv_mem_free((void *) kern_dsc);
160 }
161 }
162
163 lv_font_fmt_txt_cmap_t * cmaps =
164 (lv_font_fmt_txt_cmap_t *) dsc->cmaps;
165
166 if(NULL != cmaps) {
167 for(int i = 0; i < dsc->cmap_num; ++i) {
168 if(NULL != cmaps[i].glyph_id_ofs_list)
169 lv_mem_free((void *) cmaps[i].glyph_id_ofs_list);
170 if(NULL != cmaps[i].unicode_list)
171 lv_mem_free((void *) cmaps[i].unicode_list);
172 }
173 lv_mem_free(cmaps);
174 }
175
176 if(NULL != dsc->glyph_bitmap) {
177 lv_mem_free((void *) dsc->glyph_bitmap);
178 }
179 if(NULL != dsc->glyph_dsc) {
180 lv_mem_free((void *) dsc->glyph_dsc);
181 }
182 lv_mem_free(dsc);
183 }
184 lv_mem_free(font);
185 }
186 }
187
188
189 /**********************
190 * STATIC FUNCTIONS
191 **********************/
192
init_bit_iterator(lv_fs_file_t * fp)193 static bit_iterator_t init_bit_iterator(lv_fs_file_t * fp)
194 {
195 bit_iterator_t it;
196 it.fp = fp;
197 it.bit_pos = -1;
198 it.byte_value = 0;
199 return it;
200 }
201
read_bits(bit_iterator_t * it,int n_bits,lv_fs_res_t * res)202 static int read_bits(bit_iterator_t * it, int n_bits, lv_fs_res_t * res)
203 {
204 int value = 0;
205 while(n_bits--) {
206 it->byte_value = it->byte_value << 1;
207 it->bit_pos--;
208
209 if(it->bit_pos < 0) {
210 it->bit_pos = 7;
211 *res = lv_fs_read(it->fp, &(it->byte_value), 1, NULL);
212 if(*res != LV_FS_RES_OK) {
213 return -1;
214 }
215 }
216 int8_t bit = (it->byte_value & 0x80) ? 1 : 0;
217
218 value |= (bit << n_bits);
219 }
220 *res = LV_FS_RES_OK;
221 return value;
222 }
223
read_bits_signed(bit_iterator_t * it,int n_bits,lv_fs_res_t * res)224 static int read_bits_signed(bit_iterator_t * it, int n_bits, lv_fs_res_t * res)
225 {
226 int value = read_bits(it, n_bits, res);
227 if(value & (1 << (n_bits - 1))) {
228 for(int bit = n_bits; bit < 16; ++bit) {
229 value |= (1 << bit);
230 }
231 }
232 return value;
233 }
234
read_label(lv_fs_file_t * fp,int start,const char * label)235 static int read_label(lv_fs_file_t * fp, int start, const char * label)
236 {
237 lv_fs_seek(fp, start);
238
239 uint32_t length;
240 char buf[4];
241
242 if(lv_fs_read(fp, &length, 4, NULL) != LV_FS_RES_OK
243 || lv_fs_read(fp, buf, 4, NULL) != LV_FS_RES_OK
244 || memcmp(label, buf, 4) != 0) {
245 LV_LOG_WARN("Error reading '%s' label.", label);
246 return -1;
247 }
248
249 return length;
250 }
251
load_cmaps_tables(lv_fs_file_t * fp,lv_font_fmt_txt_dsc_t * font_dsc,uint32_t cmaps_start,cmap_table_bin_t * cmap_table)252 static bool load_cmaps_tables(lv_fs_file_t * fp, lv_font_fmt_txt_dsc_t * font_dsc,
253 uint32_t cmaps_start, cmap_table_bin_t * cmap_table)
254 {
255 for(unsigned int i = 0; i < font_dsc->cmap_num; ++i) {
256 if(lv_fs_read(fp, &cmap_table[i], sizeof(cmap_table_bin_t), NULL) != LV_FS_RES_OK) {
257 return false;
258 }
259
260 lv_font_fmt_txt_cmap_t * cmap = (lv_font_fmt_txt_cmap_t *) & (font_dsc->cmaps[i]);
261
262 cmap->range_start = cmap_table[i].range_start;
263 cmap->range_length = cmap_table[i].range_length;
264 cmap->glyph_id_start = cmap_table[i].glyph_id_start;
265 }
266
267 for(unsigned int i = 0; i < font_dsc->cmap_num; ++i) {
268 lv_fs_res_t res = lv_fs_seek(fp, cmaps_start + cmap_table[i].data_offset);
269 if(res != LV_FS_RES_OK) {
270 return false;
271 }
272
273 lv_font_fmt_txt_cmap_t * cmap = (lv_font_fmt_txt_cmap_t *) & (font_dsc->cmaps[i]);
274
275 switch(cmap_table[i].format_type) {
276 case 0: {
277 uint8_t ids_size = sizeof(uint8_t) * cmap_table[i].data_entries_count;
278 uint8_t * glyph_id_ofs_list = lv_mem_alloc(ids_size);
279
280 cmap->glyph_id_ofs_list = glyph_id_ofs_list;
281
282 if(lv_fs_read(fp, glyph_id_ofs_list, ids_size, NULL) != LV_FS_RES_OK) {
283 return false;
284 }
285
286 cmap->type = LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL;
287 cmap->list_length = cmap->range_length;
288 cmap->unicode_list = NULL;
289 break;
290 }
291 case 2:
292 cmap->type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY;
293 cmap->list_length = 0;
294 cmap->unicode_list = NULL;
295 cmap->glyph_id_ofs_list = NULL;
296 break;
297 case 1:
298 case 3: {
299 uint32_t list_size = sizeof(uint16_t) * cmap_table[i].data_entries_count;
300 uint16_t * unicode_list = (uint16_t *) lv_mem_alloc(list_size);
301
302 cmap->unicode_list = unicode_list;
303 cmap->list_length = cmap_table[i].data_entries_count;
304
305 if(lv_fs_read(fp, unicode_list, list_size, NULL) != LV_FS_RES_OK) {
306 return false;
307 }
308
309 if(cmap_table[i].format_type == 1) {
310 uint16_t * buf = lv_mem_alloc(sizeof(uint16_t) * cmap->list_length);
311
312 cmap->type = LV_FONT_FMT_TXT_CMAP_SPARSE_FULL;
313 cmap->glyph_id_ofs_list = buf;
314
315 if(lv_fs_read(fp, buf, sizeof(uint16_t) * cmap->list_length, NULL) != LV_FS_RES_OK) {
316 return false;
317 }
318 }
319 else {
320 cmap->type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY;
321 cmap->glyph_id_ofs_list = NULL;
322 }
323 break;
324 }
325 default:
326 LV_LOG_WARN("Unknown cmaps format type %d.", cmap_table[i].format_type);
327 return false;
328 }
329 }
330 return true;
331 }
332
load_cmaps(lv_fs_file_t * fp,lv_font_fmt_txt_dsc_t * font_dsc,uint32_t cmaps_start)333 static int32_t load_cmaps(lv_fs_file_t * fp, lv_font_fmt_txt_dsc_t * font_dsc, uint32_t cmaps_start)
334 {
335 int32_t cmaps_length = read_label(fp, cmaps_start, "cmap");
336 if(cmaps_length < 0) {
337 return -1;
338 }
339
340 uint32_t cmaps_subtables_count;
341 if(lv_fs_read(fp, &cmaps_subtables_count, sizeof(uint32_t), NULL) != LV_FS_RES_OK) {
342 return -1;
343 }
344
345 lv_font_fmt_txt_cmap_t * cmaps =
346 lv_mem_alloc(cmaps_subtables_count * sizeof(lv_font_fmt_txt_cmap_t));
347
348 memset(cmaps, 0, cmaps_subtables_count * sizeof(lv_font_fmt_txt_cmap_t));
349
350 font_dsc->cmaps = cmaps;
351 font_dsc->cmap_num = cmaps_subtables_count;
352
353 cmap_table_bin_t * cmaps_tables = lv_mem_alloc(sizeof(cmap_table_bin_t) * font_dsc->cmap_num);
354
355 bool success = load_cmaps_tables(fp, font_dsc, cmaps_start, cmaps_tables);
356
357 lv_mem_free(cmaps_tables);
358
359 return success ? cmaps_length : -1;
360 }
361
load_glyph(lv_fs_file_t * fp,lv_font_fmt_txt_dsc_t * font_dsc,uint32_t start,uint32_t * glyph_offset,uint32_t loca_count,font_header_bin_t * header)362 static int32_t load_glyph(lv_fs_file_t * fp, lv_font_fmt_txt_dsc_t * font_dsc,
363 uint32_t start, uint32_t * glyph_offset, uint32_t loca_count, font_header_bin_t * header)
364 {
365 int32_t glyph_length = read_label(fp, start, "glyf");
366 if(glyph_length < 0) {
367 return -1;
368 }
369
370 lv_font_fmt_txt_glyph_dsc_t * glyph_dsc = (lv_font_fmt_txt_glyph_dsc_t *)
371 lv_mem_alloc(loca_count * sizeof(lv_font_fmt_txt_glyph_dsc_t));
372
373 memset(glyph_dsc, 0, loca_count * sizeof(lv_font_fmt_txt_glyph_dsc_t));
374
375 font_dsc->glyph_dsc = glyph_dsc;
376
377 int cur_bmp_size = 0;
378
379 for(unsigned int i = 0; i < loca_count; ++i) {
380 lv_font_fmt_txt_glyph_dsc_t * gdsc = &glyph_dsc[i];
381
382 lv_fs_res_t res = lv_fs_seek(fp, start + glyph_offset[i]);
383 if(res != LV_FS_RES_OK) {
384 return -1;
385 }
386
387 bit_iterator_t bit_it = init_bit_iterator(fp);
388
389 if(header->advance_width_bits == 0) {
390 gdsc->adv_w = header->default_advance_width;
391 }
392 else {
393 gdsc->adv_w = read_bits(&bit_it, header->advance_width_bits, &res);
394 if(res != LV_FS_RES_OK) {
395 return -1;
396 }
397 }
398
399 if(header->advance_width_format == 0) {
400 gdsc->adv_w *= 16;
401 }
402
403 gdsc->ofs_x = read_bits_signed(&bit_it, header->xy_bits, &res);
404 if(res != LV_FS_RES_OK) {
405 return -1;
406 }
407
408 gdsc->ofs_y = read_bits_signed(&bit_it, header->xy_bits, &res);
409 if(res != LV_FS_RES_OK) {
410 return -1;
411 }
412
413 gdsc->box_w = read_bits(&bit_it, header->wh_bits, &res);
414 if(res != LV_FS_RES_OK) {
415 return -1;
416 }
417
418 gdsc->box_h = read_bits(&bit_it, header->wh_bits, &res);
419 if(res != LV_FS_RES_OK) {
420 return -1;
421 }
422
423 int nbits = header->advance_width_bits + 2 * header->xy_bits + 2 * header->wh_bits;
424 int next_offset = (i < loca_count - 1) ? glyph_offset[i + 1] : (uint32_t)(glyph_length - 1);
425 int bmp_size = next_offset - glyph_offset[i] - nbits / 8;
426
427 if(i == 0) {
428 gdsc->adv_w = 0;
429 gdsc->box_w = 0;
430 gdsc->box_h = 0;
431 gdsc->ofs_x = 0;
432 gdsc->ofs_y = 0;
433 }
434
435 gdsc->bitmap_index = cur_bmp_size;
436 if(gdsc->box_w * gdsc->box_h != 0) {
437 cur_bmp_size += bmp_size;
438 }
439 }
440
441 uint8_t * glyph_bmp = (uint8_t *) lv_mem_alloc(sizeof(uint8_t) * cur_bmp_size);
442
443 font_dsc->glyph_bitmap = glyph_bmp;
444
445 cur_bmp_size = 0;
446
447 for(unsigned int i = 1; i < loca_count; ++i) {
448 lv_fs_res_t res = lv_fs_seek(fp, start + glyph_offset[i]);
449 if(res != LV_FS_RES_OK) {
450 return -1;
451 }
452 bit_iterator_t bit_it = init_bit_iterator(fp);
453
454 int nbits = header->advance_width_bits + 2 * header->xy_bits + 2 * header->wh_bits;
455
456 read_bits(&bit_it, nbits, &res);
457 if(res != LV_FS_RES_OK) {
458 return -1;
459 }
460
461 if(glyph_dsc[i].box_w * glyph_dsc[i].box_h == 0) {
462 continue;
463 }
464
465 int next_offset = (i < loca_count - 1) ? glyph_offset[i + 1] : (uint32_t)(glyph_length - 1);
466 int bmp_size = next_offset - glyph_offset[i] - nbits / 8;
467
468 for(int k = 0; k < bmp_size; ++k) {
469 glyph_bmp[cur_bmp_size + k] = read_bits(&bit_it, 8, &res);
470 if(res != LV_FS_RES_OK) {
471 return -1;
472 }
473 }
474 cur_bmp_size += bmp_size;
475 }
476 return glyph_length;
477 }
478
479 /*
480 * Loads a `lv_font_t` from a binary file, given a `lv_fs_file_t`.
481 *
482 * Memory allocations on `lvgl_load_font` should be immediately zeroed and
483 * the pointer should be set on the `lv_font_t` data before any possible return.
484 *
485 * When something fails, it returns `false` and the memory on the `lv_font_t`
486 * still needs to be freed using `lv_font_free`.
487 *
488 * `lv_font_free` will assume that all non-null pointers are allocated and
489 * should be freed.
490 */
lvgl_load_font(lv_fs_file_t * fp,lv_font_t * font)491 static bool lvgl_load_font(lv_fs_file_t * fp, lv_font_t * font)
492 {
493 lv_font_fmt_txt_dsc_t * font_dsc = (lv_font_fmt_txt_dsc_t *)
494 lv_mem_alloc(sizeof(lv_font_fmt_txt_dsc_t));
495
496 memset(font_dsc, 0, sizeof(lv_font_fmt_txt_dsc_t));
497
498 font->dsc = font_dsc;
499
500 /* header */
501 int32_t header_length = read_label(fp, 0, "head");
502 if(header_length < 0) {
503 return false;
504 }
505
506 font_header_bin_t font_header;
507 if(lv_fs_read(fp, &font_header, sizeof(font_header_bin_t), NULL) != LV_FS_RES_OK) {
508 return false;
509 }
510
511 font->base_line = -font_header.descent;
512 font->line_height = font_header.ascent - font_header.descent;
513 font->get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt;
514 font->get_glyph_bitmap = lv_font_get_bitmap_fmt_txt;
515 font->subpx = font_header.subpixels_mode;
516
517 font_dsc->bpp = font_header.bits_per_pixel;
518 font_dsc->kern_scale = font_header.kerning_scale;
519 font_dsc->bitmap_format = font_header.compression_id;
520
521 /* cmaps */
522 uint32_t cmaps_start = header_length;
523 int32_t cmaps_length = load_cmaps(fp, font_dsc, cmaps_start);
524 if(cmaps_length < 0) {
525 return false;
526 }
527
528 /* loca */
529 uint32_t loca_start = cmaps_start + cmaps_length;
530 int32_t loca_length = read_label(fp, loca_start, "loca");
531 if(loca_length < 0) {
532 return false;
533 }
534
535 uint32_t loca_count;
536 if(lv_fs_read(fp, &loca_count, sizeof(uint32_t), NULL) != LV_FS_RES_OK) {
537 return false;
538 }
539
540 bool failed = false;
541 uint32_t * glyph_offset = lv_mem_alloc(sizeof(uint32_t) * (loca_count + 1));
542
543 for(unsigned int i = 0; i < loca_count; ++i) {
544 if(font_header.index_to_loc_format == 0) {
545 uint16_t offset;
546 if(lv_fs_read(fp, &offset, sizeof(uint16_t), NULL) != LV_FS_RES_OK) {
547 failed = true;
548 break;
549 }
550 glyph_offset[i] = offset;
551 }
552 else if(font_header.index_to_loc_format == 1) {
553 uint32_t offset;
554 if(lv_fs_read(fp, &offset, sizeof(uint32_t), NULL) != LV_FS_RES_OK) {
555 failed = true;
556 break;
557 }
558 glyph_offset[i] = offset;
559 }
560 else {
561 LV_LOG_WARN("Unknown index_to_loc_format: %d.", font_header.index_to_loc_format);
562 failed = true;
563 break;
564 }
565 }
566
567 if(failed) {
568 lv_mem_free(glyph_offset);
569 return false;
570 }
571
572 /* glyph */
573 uint32_t glyph_start = loca_start + loca_length;
574 int32_t glyph_length = load_glyph(
575 fp, font_dsc, glyph_start, glyph_offset, loca_count, &font_header);
576
577 lv_mem_free(glyph_offset);
578
579 if(glyph_length < 0) {
580 return false;
581 }
582
583 if(font_header.tables_count < 4) {
584 font_dsc->kern_dsc = NULL;
585 font_dsc->kern_classes = 0;
586 font_dsc->kern_scale = 0;
587 return true;
588 }
589
590 uint32_t kern_start = glyph_start + glyph_length;
591
592 int32_t kern_length = load_kern(fp, font_dsc, font_header.glyph_id_format, kern_start);
593
594 return kern_length >= 0;
595 }
596
load_kern(lv_fs_file_t * fp,lv_font_fmt_txt_dsc_t * font_dsc,uint8_t format,uint32_t start)597 int32_t load_kern(lv_fs_file_t * fp, lv_font_fmt_txt_dsc_t * font_dsc, uint8_t format, uint32_t start)
598 {
599 int32_t kern_length = read_label(fp, start, "kern");
600 if(kern_length < 0) {
601 return -1;
602 }
603
604 uint8_t kern_format_type;
605 int32_t padding;
606 if(lv_fs_read(fp, &kern_format_type, sizeof(uint8_t), NULL) != LV_FS_RES_OK ||
607 lv_fs_read(fp, &padding, 3 * sizeof(uint8_t), NULL) != LV_FS_RES_OK) {
608 return -1;
609 }
610
611 if(0 == kern_format_type) { /* sorted pairs */
612 lv_font_fmt_txt_kern_pair_t * kern_pair = lv_mem_alloc(sizeof(lv_font_fmt_txt_kern_pair_t));
613
614 memset(kern_pair, 0, sizeof(lv_font_fmt_txt_kern_pair_t));
615
616 font_dsc->kern_dsc = kern_pair;
617 font_dsc->kern_classes = 0;
618
619 uint32_t glyph_entries;
620 if(lv_fs_read(fp, &glyph_entries, sizeof(uint32_t), NULL) != LV_FS_RES_OK) {
621 return -1;
622 }
623
624 int ids_size;
625 if(format == 0) {
626 ids_size = sizeof(int8_t) * 2 * glyph_entries;
627 }
628 else {
629 ids_size = sizeof(int16_t) * 2 * glyph_entries;
630 }
631
632 uint8_t * glyph_ids = lv_mem_alloc(ids_size);
633 int8_t * values = lv_mem_alloc(glyph_entries);
634
635 kern_pair->glyph_ids_size = format;
636 kern_pair->pair_cnt = glyph_entries;
637 kern_pair->glyph_ids = glyph_ids;
638 kern_pair->values = values;
639
640 if(lv_fs_read(fp, glyph_ids, ids_size, NULL) != LV_FS_RES_OK) {
641 return -1;
642 }
643
644 if(lv_fs_read(fp, values, glyph_entries, NULL) != LV_FS_RES_OK) {
645 return -1;
646 }
647 }
648 else if(3 == kern_format_type) { /* array M*N of classes */
649
650 lv_font_fmt_txt_kern_classes_t * kern_classes = lv_mem_alloc(sizeof(lv_font_fmt_txt_kern_classes_t));
651
652 memset(kern_classes, 0, sizeof(lv_font_fmt_txt_kern_classes_t));
653
654 font_dsc->kern_dsc = kern_classes;
655 font_dsc->kern_classes = 1;
656
657 uint16_t kern_class_mapping_length;
658 uint8_t kern_table_rows;
659 uint8_t kern_table_cols;
660
661 if(lv_fs_read(fp, &kern_class_mapping_length, sizeof(uint16_t), NULL) != LV_FS_RES_OK ||
662 lv_fs_read(fp, &kern_table_rows, sizeof(uint8_t), NULL) != LV_FS_RES_OK ||
663 lv_fs_read(fp, &kern_table_cols, sizeof(uint8_t), NULL) != LV_FS_RES_OK) {
664 return -1;
665 }
666
667 int kern_values_length = sizeof(int8_t) * kern_table_rows * kern_table_cols;
668
669 uint8_t * kern_left = lv_mem_alloc(kern_class_mapping_length);
670 uint8_t * kern_right = lv_mem_alloc(kern_class_mapping_length);
671 int8_t * kern_values = lv_mem_alloc(kern_values_length);
672
673 kern_classes->left_class_mapping = kern_left;
674 kern_classes->right_class_mapping = kern_right;
675 kern_classes->left_class_cnt = kern_table_rows;
676 kern_classes->right_class_cnt = kern_table_cols;
677 kern_classes->class_pair_values = kern_values;
678
679 if(lv_fs_read(fp, kern_left, kern_class_mapping_length, NULL) != LV_FS_RES_OK ||
680 lv_fs_read(fp, kern_right, kern_class_mapping_length, NULL) != LV_FS_RES_OK ||
681 lv_fs_read(fp, kern_values, kern_values_length, NULL) != LV_FS_RES_OK) {
682 return -1;
683 }
684 }
685 else {
686 LV_LOG_WARN("Unknown kern_format_type: %d", kern_format_type);
687 return -1;
688 }
689
690 return kern_length;
691 }
692
693 #endif /*LV_USE_FILESYSTEM*/
694
695