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