@@ -48,6 +48,7 @@ static inline void free_memory(lvfontio_ondiskfont_t *self, void *ptr) {
4848
4949// Forward declarations for helper functions
5050static int16_t find_codepoint_slot (lvfontio_ondiskfont_t * self , uint32_t codepoint );
51+ static bool slot_has_active_full_width_partner (lvfontio_ondiskfont_t * self , uint16_t slot );
5152static uint16_t find_free_slot (lvfontio_ondiskfont_t * self , uint32_t codepoint );
5253static FRESULT read_bits (FIL * file , size_t num_bits , uint8_t * byte_val , uint8_t * remaining_bits , uint32_t * result );
5354static FRESULT read_glyph_dimensions (FIL * file , lvfontio_ondiskfont_t * self , uint32_t * advance_width , int32_t * bbox_x , int32_t * bbox_y , uint32_t * bbox_w , uint32_t * bbox_h , uint8_t * byte_val , uint8_t * remaining_bits );
@@ -224,7 +225,12 @@ static bool load_font_header(lvfontio_ondiskfont_t *self, FIL *file, size_t *max
224225
225226 // Throw away the bitmap bits.
226227 read_bits (file , self -> header .bits_per_pixel * bbox_w * bbox_h , & byte_val , & remaining_bits , NULL );
227- if (advances [0 ] == glyph_advance ) {
228+
229+ if (glyph_advance == 0 ) {
230+ // Ignore zero-advance glyphs when inferring the terminal cell width.
231+ // Some fonts include placeholders/control glyphs with zero advance,
232+ // which would otherwise skew default_advance_width too small.
233+ } else if (advances [0 ] == glyph_advance ) {
228234 advance_count [0 ]++ ;
229235 } else if (advances [1 ] == glyph_advance ) {
230236 advance_count [1 ]++ ;
@@ -257,6 +263,12 @@ static bool load_font_header(lvfontio_ondiskfont_t *self, FIL *file, size_t *max
257263 * max_slots = advance_count [0 ] + advance_count [1 ];
258264 }
259265
266+ if (self -> header .default_advance_width == 0 ) {
267+ self -> header .default_advance_width = 1 ;
268+ }
269+ if (* max_slots == 0 ) {
270+ * max_slots = 1 ;
271+ }
260272 found_glyf = true;
261273 }
262274
@@ -344,8 +356,9 @@ static int32_t get_char_id(lvfontio_ondiskfont_t *self, uint32_t codepoint) {
344356 for (size_t j = 0 ; j < self -> cmap_ranges [i ].entries_count ; j ++ ) {
345357 // Read code point at the index
346358 uint16_t candidate_codepoint_delta ;
347- res = f_read (& self -> file , & candidate_codepoint_delta , 2 , NULL );
348- if (res != FR_OK ) {
359+ UINT bytes_read ;
360+ res = f_read (& self -> file , & candidate_codepoint_delta , 2 , & bytes_read );
361+ if (res != FR_OK || bytes_read < 2 ) {
349362 return -1 ;
350363 }
351364
@@ -574,18 +587,20 @@ int16_t common_hal_lvfontio_ondiskfont_cache_glyph(lvfontio_ondiskfont_t *self,
574587 // Check if already cached
575588 int16_t existing_slot = find_codepoint_slot (self , codepoint );
576589 if (existing_slot >= 0 ) {
577- // Glyph is already cached, increment reference count
590+ // Glyph is already cached, increment reference count(s).
578591 self -> reference_counts [existing_slot ]++ ;
579592
580593 // Check if this is a full-width character by looking for a second slot
581- // with the same codepoint right after this one
594+ // with the same codepoint right after this one, wrapping at the end.
595+ uint16_t next_slot = (existing_slot + 1 ) % self -> max_glyphs ;
596+ bool cached_is_full_width = self -> codepoints [next_slot ] == codepoint ;
597+
598+ if (cached_is_full_width ) {
599+ self -> reference_counts [next_slot ]++ ;
600+ }
601+
582602 if (is_full_width != NULL ) {
583- if (existing_slot + 1 < self -> max_glyphs &&
584- self -> codepoints [existing_slot + 1 ] == codepoint ) {
585- * is_full_width = true;
586- } else {
587- * is_full_width = false;
588- }
603+ * is_full_width = cached_is_full_width ;
589604 }
590605
591606 return existing_slot ;
@@ -722,12 +737,37 @@ static int16_t find_codepoint_slot(lvfontio_ondiskfont_t *self, uint32_t codepoi
722737 for (uint16_t i = 0 ; i < self -> max_glyphs ; i ++ ) {
723738 int16_t slot = (i + offset ) % self -> max_glyphs ;
724739 if (self -> codepoints [slot ] == codepoint ) {
740+ // If this is the second slot of a full-width glyph pair, return the
741+ // first slot so callers always get a canonical index.
742+ if (slot > 0 && self -> codepoints [slot - 1 ] == codepoint ) {
743+ return slot - 1 ;
744+ }
725745 return slot ;
726746 }
727747 }
728748 return -1 ;
729749}
730750
751+ static bool slot_has_active_full_width_partner (lvfontio_ondiskfont_t * self , uint16_t slot ) {
752+ uint32_t codepoint = self -> codepoints [slot ];
753+ if (codepoint == LVFONTIO_INVALID_CODEPOINT ) {
754+ return false;
755+ }
756+
757+ // Don't evict one half of a full-width pair while the other half is still in use.
758+ uint16_t prev_slot = (slot + self -> max_glyphs - 1 ) % self -> max_glyphs ;
759+ uint16_t next_slot = (slot + 1 ) % self -> max_glyphs ;
760+
761+ if (self -> codepoints [prev_slot ] == codepoint && self -> reference_counts [prev_slot ] > 0 ) {
762+ return true;
763+ }
764+ if (self -> codepoints [next_slot ] == codepoint && self -> reference_counts [next_slot ] > 0 ) {
765+ return true;
766+ }
767+
768+ return false;
769+ }
770+
731771static uint16_t find_free_slot (lvfontio_ondiskfont_t * self , uint32_t codepoint ) {
732772 size_t offset = codepoint % self -> max_glyphs ;
733773
@@ -739,10 +779,11 @@ static uint16_t find_free_slot(lvfontio_ondiskfont_t *self, uint32_t codepoint)
739779 }
740780 }
741781
742- // If none found, look for slots with zero reference count, starting at the offset
782+ // If none found, look for slots with zero reference count, starting at the offset.
783+ // Avoid reusing one half of an active full-width glyph pair.
743784 for (uint16_t i = 0 ; i < self -> max_glyphs ; i ++ ) {
744785 int16_t slot = (i + offset ) % self -> max_glyphs ;
745- if (self -> reference_counts [slot ] == 0 ) {
786+ if (self -> reference_counts [slot ] == 0 && ! slot_has_active_full_width_partner ( self , slot ) ) {
746787 return slot ;
747788 }
748789 }
0 commit comments