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