2022-10-06 13:47:25 -06:00
use super ::{ Font , FontLineIndex , FontShapeGlyph , FontShapeWord , FontShapeLine , FontShapeSpan } ;
2022-10-05 09:16:51 -06:00
pub struct FontMatches < ' a > {
pub fonts : Vec < & ' a Font < ' a > > ,
}
impl < ' a > FontMatches < ' a > {
2022-10-07 20:50:52 -06:00
fn shape_word ( & self , font_i : usize , line : & str , start_word : usize , end_word : usize , span_rtl : bool , blank : bool ) -> FontShapeWord {
2022-10-06 13:47:25 -06:00
let word = & line [ start_word .. end_word ] ;
2022-10-05 09:16:51 -06:00
2022-10-07 14:20:26 -06:00
let font_scale = self . fonts [ font_i ] . rustybuzz . units_per_em ( ) as f32 ;
2022-10-05 09:16:51 -06:00
2022-10-07 14:20:26 -06:00
let mut buffer = rustybuzz ::UnicodeBuffer ::new ( ) ;
2022-10-07 19:44:41 -06:00
buffer . set_direction ( if span_rtl {
rustybuzz ::Direction ::RightToLeft
} else {
rustybuzz ::Direction ::LeftToRight
} ) ;
2022-10-07 14:20:26 -06:00
buffer . push_str ( word ) ;
buffer . guess_segment_properties ( ) ;
2022-10-07 16:14:26 -06:00
let rtl = match buffer . direction ( ) {
rustybuzz ::Direction ::RightToLeft = > true ,
//TODO: other directions?
_ = > false ,
} ;
2022-10-07 19:44:41 -06:00
assert_eq! ( rtl , span_rtl ) ;
2022-10-07 16:14:26 -06:00
if font_i = = 0 {
2022-10-07 20:50:52 -06:00
println! (
" Word {}{}: '{}' " ,
if rtl { " RTL " } else { " LTR " } ,
if blank { " BLANK " } else { " " } ,
word
) ;
2022-10-07 16:14:26 -06:00
}
2022-10-05 09:16:51 -06:00
2022-10-07 14:20:26 -06:00
let glyph_buffer = rustybuzz ::shape ( & self . fonts [ font_i ] . rustybuzz , & [ ] , buffer ) ;
let glyph_infos = glyph_buffer . glyph_infos ( ) ;
let glyph_positions = glyph_buffer . glyph_positions ( ) ;
2022-10-05 09:16:51 -06:00
2022-10-07 14:20:26 -06:00
let mut missing = Vec ::new ( ) ;
let mut glyphs = Vec ::with_capacity ( glyph_infos . len ( ) ) ;
for ( info , pos ) in glyph_infos . iter ( ) . zip ( glyph_positions . iter ( ) ) {
let x_advance = pos . x_advance as f32 / font_scale ;
let y_advance = pos . y_advance as f32 / font_scale ;
let x_offset = pos . x_offset as f32 / font_scale ;
let y_offset = pos . y_offset as f32 / font_scale ;
//println!(" {:?} {:?}", info, pos);
if info . glyph_id = = 0 {
missing . push ( start_word + info . cluster as usize ) ;
2022-10-05 09:16:51 -06:00
}
2022-10-07 14:20:26 -06:00
#[ cfg(feature = " ab_glyph " ) ]
let inner = ab_glyph ::GlyphId ( info . glyph_id as u16 ) ;
#[ cfg(feature = " rusttype " ) ]
let inner = font . rusttype . glyph ( rusttype ::GlyphId ( info . glyph_id as u16 ) ) ;
#[ cfg(feature = " swash " ) ]
let inner = info . glyph_id as swash ::GlyphId ;
glyphs . push ( FontShapeGlyph {
start : start_word + info . cluster as usize ,
end : end_word , // Set later
x_advance ,
y_advance ,
x_offset ,
y_offset ,
#[ cfg(feature = " ab_glyph " ) ]
font : & self . fonts [ font_i ] . ab_glyph ,
#[ cfg(feature = " swash " ) ]
font : & self . fonts [ font_i ] . swash ,
inner ,
} ) ;
}
// Adjust end of glyphs
2022-10-07 14:54:40 -06:00
if rtl {
for i in 1 .. glyphs . len ( ) {
let next_start = glyphs [ i - 1 ] . start ;
let next_end = glyphs [ i - 1 ] . end ;
let prev = & mut glyphs [ i ] ;
if prev . start = = next_start {
prev . end = next_end ;
} else {
prev . end = next_start ;
2022-10-07 14:20:26 -06:00
}
2022-10-07 14:54:40 -06:00
}
} else {
for i in ( 1 .. glyphs . len ( ) ) . rev ( ) {
let next_start = glyphs [ i ] . start ;
let next_end = glyphs [ i ] . end ;
let prev = & mut glyphs [ i - 1 ] ;
if prev . start = = next_start {
prev . end = next_end ;
} else {
prev . end = next_start ;
2022-10-07 14:20:26 -06:00
}
2022-10-07 14:54:40 -06:00
}
2022-10-07 14:20:26 -06:00
}
2022-10-05 09:16:51 -06:00
2022-10-07 14:20:26 -06:00
//TODO: improve performance!
if ! missing . is_empty ( ) & & font_i + 1 < self . fonts . len ( ) {
2022-10-07 16:14:26 -06:00
let mut missing_i = 0 ;
while missing_i < missing . len ( ) {
let mut missing_glyph = missing [ missing_i ] ;
missing_i + = 1 ;
let mut start_glyph = missing_glyph ;
2022-10-06 16:09:43 -06:00
2022-10-07 14:20:26 -06:00
// Find beginning of glyphs to replace
let mut i = 0 ;
while i < glyphs . len ( ) {
if glyphs [ i ] . start = = start_glyph {
break ;
}
i + = 1 ;
}
2022-10-05 09:16:51 -06:00
2022-10-07 14:20:26 -06:00
// Remove all matching glyphs and find end
let mut end_glyph = start_glyph ;
while i < glyphs . len ( ) {
2022-10-07 16:14:26 -06:00
if glyphs [ i ] . start = = missing_glyph {
2022-10-07 14:20:26 -06:00
let glyph = glyphs . remove ( i ) ;
2022-10-07 16:14:26 -06:00
if glyph . start < start_glyph {
start_glyph = glyph . start ;
}
2022-10-07 14:20:26 -06:00
if glyph . end > end_glyph {
end_glyph = glyph . end ;
}
2022-10-07 16:14:26 -06:00
} else if missing_i < missing . len ( ) {
// Combine repeated missing items
if glyphs [ i ] . start = = missing [ missing_i ] {
missing_glyph = missing [ missing_i ] ;
missing_i + = 1 ;
} else {
break ;
}
2022-10-07 14:20:26 -06:00
} else {
break ;
}
}
2022-10-07 20:50:52 -06:00
let mut fb_word = self . shape_word ( font_i + 1 , line , start_glyph , end_glyph , span_rtl , blank ) ;
2022-10-07 16:14:26 -06:00
2022-10-07 14:20:26 -06:00
// Insert all matching glyphs
2022-10-07 14:54:40 -06:00
// println!("Locate fallback for {},{} '{}' from font {} to font {}", start_glyph, end_glyph, &line[start_glyph..end_glyph], font_i + 1, font_i);
2022-10-07 14:20:26 -06:00
let mut j = 0 ;
while j < fb_word . glyphs . len ( ) {
if fb_word . glyphs [ j ] . start > = start_glyph & & fb_word . glyphs [ j ] . end < = end_glyph {
2022-10-07 14:54:40 -06:00
// println!("Copy fallback for {},{} '{}' from font {} cluster {} to font {}", start_glyph, end_glyph, &line[start_glyph..end_glyph], font_i + 1, fb_word.glyphs[j].start, font_i);
2022-10-07 14:20:26 -06:00
glyphs . insert ( i , fb_word . glyphs . remove ( j ) ) ;
i + = 1 ;
} else {
j + = 1 ;
}
}
2022-10-05 09:16:51 -06:00
}
}
2022-10-07 14:20:26 -06:00
/*
for glyph in glyphs . iter ( ) {
println! ( " ' {} ': {} , {} , {} , {} " , & line [ glyph . start .. glyph . end ] , glyph . x_advance , glyph . y_advance , glyph . x_offset , glyph . y_offset ) ;
2022-10-05 09:16:51 -06:00
}
2022-10-07 14:20:26 -06:00
* /
2022-10-05 09:16:51 -06:00
2022-10-07 20:50:52 -06:00
FontShapeWord { blank , glyphs }
2022-10-05 09:16:51 -06:00
}
2022-10-07 19:44:41 -06:00
fn shape_span ( & self , line : & str , start_span : usize , end_span : usize , line_rtl : bool , span_rtl : bool ) -> FontShapeSpan {
2022-10-06 13:47:25 -06:00
let span = & line [ start_span .. end_span ] ;
2022-10-05 09:16:51 -06:00
2022-10-07 16:14:26 -06:00
println! ( " Span {} : ' {} ' " , if span_rtl { " RTL " } else { " LTR " } , span ) ;
2022-10-06 16:09:43 -06:00
2022-10-07 16:14:26 -06:00
let mut words = Vec ::new ( ) ;
2022-10-07 20:50:52 -06:00
let mut start_word = 0 ;
for ( end_lb , _ ) in unicode_linebreak ::linebreaks ( span ) {
let mut start_lb = end_lb ;
2022-10-10 13:13:04 -06:00
for ( i , c ) in span [ start_word .. end_lb ] . char_indices ( ) {
if start_word + i = = end_lb {
break ;
} else if c . is_whitespace ( ) {
start_lb = start_word + i ;
2022-10-07 20:50:52 -06:00
}
}
2022-10-10 13:13:04 -06:00
if start_word < start_lb {
words . push ( self . shape_word ( 0 , line , start_span + start_word , start_span + start_lb , span_rtl , false ) ) ;
}
2022-10-07 20:50:52 -06:00
if start_lb < end_lb {
words . push ( self . shape_word ( 0 , line , start_span + start_lb , start_span + end_lb , span_rtl , true ) ) ;
}
start_word = end_lb ;
2022-10-06 16:09:43 -06:00
}
2022-10-07 19:44:41 -06:00
// Reverse glyphs in RTL lines
if line_rtl {
2022-10-07 19:15:02 -06:00
for word in words . iter_mut ( ) {
word . glyphs . reverse ( ) ;
}
}
2022-10-07 19:44:41 -06:00
// Reverse words in spans that do not match line direction
if line_rtl ! = span_rtl {
2022-10-06 16:09:43 -06:00
words . reverse ( ) ;
}
2022-10-05 09:16:51 -06:00
2022-10-06 13:47:25 -06:00
FontShapeSpan {
2022-10-06 16:09:43 -06:00
rtl : span_rtl ,
2022-10-06 13:47:25 -06:00
words ,
}
}
2022-10-05 09:16:51 -06:00
2022-10-06 13:47:25 -06:00
pub fn shape_line ( & self , line_i : FontLineIndex , line : & str ) -> FontShapeLine {
let mut spans = Vec ::new ( ) ;
2022-10-05 09:16:51 -06:00
let bidi = unicode_bidi ::BidiInfo ::new ( line , None ) ;
let rtl = if bidi . paragraphs . is_empty ( ) {
false
} else {
assert_eq! ( bidi . paragraphs . len ( ) , 1 ) ;
2022-10-06 13:47:25 -06:00
let para_info = & bidi . paragraphs [ 0 ] ;
2022-10-07 19:44:41 -06:00
let line_rtl = para_info . level . is_rtl ( ) ;
2022-10-06 13:47:25 -06:00
2022-10-07 19:44:41 -06:00
println! ( " Line {} : ' {} ' " , if line_rtl { " RTL " } else { " LTR " } , line ) ;
2022-10-07 16:14:26 -06:00
2022-10-06 13:47:25 -06:00
let paragraph = unicode_bidi ::Paragraph ::new ( & bidi , & para_info ) ;
let mut start = 0 ;
2022-10-07 19:44:41 -06:00
let mut span_rtl = line_rtl ;
2022-10-06 13:47:25 -06:00
for i in paragraph . para . range . clone ( ) {
let next_rtl = paragraph . info . levels [ i ] . is_rtl ( ) ;
if span_rtl ! = next_rtl {
2022-10-07 19:44:41 -06:00
spans . push ( self . shape_span ( line , start , i , line_rtl , span_rtl ) ) ;
2022-10-06 13:47:25 -06:00
span_rtl = next_rtl ;
start = i ;
}
}
2022-10-07 19:44:41 -06:00
spans . push ( self . shape_span ( line , start , line . len ( ) , line_rtl , span_rtl ) ) ;
2022-10-06 13:47:25 -06:00
2022-10-07 19:44:41 -06:00
line_rtl
2022-10-05 09:16:51 -06:00
} ;
FontShapeLine {
2022-10-05 11:28:05 -06:00
line_i ,
2022-10-05 09:16:51 -06:00
rtl ,
spans ,
}
}
}