@@ -1066,6 +1066,15 @@ impl GridGlyphRasterizer {
10661066 style_flags : u8 ,
10671067 font_library : & FontLibrary ,
10681068 ) -> ( u32 , bool ) {
1069+ // Kitty Unicode placeholder cells (U+10EEEE) are rendered as
1070+ // image-overlay slices, not text. Resolve them to the primary
1071+ // font as if they were a space, so the run shapes them as an
1072+ // invisible space glyph instead of falling back to a notdef
1073+ // tofu box. Mirrors ghostty's `font/shaper/run.zig:328-335`.
1074+ if ch == rio_backend:: ansi:: kitty_virtual:: PLACEHOLDER {
1075+ return ( rio_backend:: sugarloaf:: font:: FONT_ID_REGULAR as u32 , false ) ;
1076+ }
1077+
10691078 // ASCII printable + regular style → always primary font, never
10701079 // emoji. Skips the FxHashMap lookup that dominates this fn's
10711080 // cost on terminal-typical content.
@@ -1372,6 +1381,17 @@ pub fn build_row_fg(
13721381 rasterizer. resolve_font ( ch, run_style_flags, font_library) ;
13731382 let run_start = x;
13741383
1384+ // Kitty Unicode placeholder shapes as a space — the cell
1385+ // joins the run, the shaper emits an invisible space glyph
1386+ // (no notdef tofu), and the kitty image overlay is drawn on
1387+ // top to fill the cell. Mirrors ghostty's
1388+ // `font/shaper/run.zig:264-267`.
1389+ let shape_ch = if ch == rio_backend:: ansi:: kitty_virtual:: PLACEHOLDER {
1390+ ' '
1391+ } else {
1392+ ch
1393+ } ;
1394+
13751395 #[ cfg( target_os = "macos" ) ]
13761396 {
13771397 rasterizer. run_utf16_scratch . clear ( ) ;
@@ -1382,12 +1402,12 @@ pub fn build_row_fg(
13821402 let mut buf = [ 0u16 ; 2 ] ;
13831403 rasterizer
13841404 . run_utf16_scratch
1385- . extend_from_slice ( ch . encode_utf16 ( & mut buf) ) ;
1405+ . extend_from_slice ( shape_ch . encode_utf16 ( & mut buf) ) ;
13861406 }
13871407 #[ cfg( not( target_os = "macos" ) ) ]
13881408 {
13891409 rasterizer. run_str_scratch . clear ( ) ;
1390- rasterizer. run_str_scratch . push ( ch ) ;
1410+ rasterizer. run_str_scratch . push ( shape_ch ) ;
13911411 }
13921412
13931413 // Extend the run while (font_id, style_flags) match.
@@ -1407,6 +1427,13 @@ pub fn build_row_fg(
14071427 if font_id2 != font_id {
14081428 break ;
14091429 }
1430+ // Same placeholder→space substitution as the run-start
1431+ // path above.
1432+ let shape_ch2 = if ch2 == rio_backend:: ansi:: kitty_virtual:: PLACEHOLDER {
1433+ ' '
1434+ } else {
1435+ ch2
1436+ } ;
14101437 #[ cfg( target_os = "macos" ) ]
14111438 {
14121439 rasterizer
@@ -1415,11 +1442,11 @@ pub fn build_row_fg(
14151442 let mut buf = [ 0u16 ; 2 ] ;
14161443 rasterizer
14171444 . run_utf16_scratch
1418- . extend_from_slice ( ch2 . encode_utf16 ( & mut buf) ) ;
1445+ . extend_from_slice ( shape_ch2 . encode_utf16 ( & mut buf) ) ;
14191446 }
14201447 #[ cfg( not( target_os = "macos" ) ) ]
14211448 {
1422- rasterizer. run_str_scratch . push ( ch2 ) ;
1449+ rasterizer. run_str_scratch . push ( shape_ch2 ) ;
14231450 }
14241451 end += 1 ;
14251452 }
0 commit comments