Просмотр исходного кода

wallet: fix bug in text shaping algo and add more robust unit test

darkfi 1 год назад
Родитель
Сommit
4963d95668
2 измененных файлов с 129 добавлено и 16 удалено
  1. 128 15
      bin/darkwallet/src/text/core.rs
  2. 1 1
      bin/darkwallet/src/ui/emoji_picker/mod.rs

+ 128 - 15
bin/darkwallet/src/text/core.rs

@@ -138,10 +138,9 @@ impl ShapedGlyphs {
         for glyph in glyphs {
         for glyph in glyphs {
             // We have a glyph. Lets consume tail.
             // We have a glyph. Lets consume tail.
             // We continue while the glyphs are before this glyph's end.
             // We continue while the glyphs are before this glyph's end.
-            while let Some(tail_glyph) = tail_iter.peek() {
-                if tail_glyph.cluster_end > glyph.cluster_end {
-                    break
-                }
+            while let Some(tail_glyph) = tail_iter.peek() &&
+                tail_glyph.cluster_start < glyph.cluster_end
+            {
                 tail_iter.next();
                 tail_iter.next();
             }
             }
 
 
@@ -186,6 +185,16 @@ fn count_leading_null_glyphs(glyphs: &Vec<GlyphInfo>) -> usize {
     cnt
     cnt
 }
 }
 
 
+/*
+fn print_glyphs(ctx: &str, glyphs: &Vec<GlyphInfo>) {
+    println!("{} ------------------", ctx);
+    for (i, glyph) in glyphs.iter().enumerate() {
+        println!("{i}: {}/{} [{}, {}]", glyph.face_idx, glyph.id, glyph.cluster_start, glyph.cluster_end);
+    }
+    println!("---------------------");
+}
+*/
+
 fn face_shape(face: &mut FreetypeFace, text: &str, off: usize, face_idx: usize) -> Vec<GlyphInfo> {
 fn face_shape(face: &mut FreetypeFace, text: &str, off: usize, face_idx: usize) -> Vec<GlyphInfo> {
     let mut glyphs: Vec<GlyphInfo> = vec![];
     let mut glyphs: Vec<GlyphInfo> = vec![];
     for (i, hbinf) in harfbuzz_shape(face, text).enumerate() {
     for (i, hbinf) in harfbuzz_shape(face, text).enumerate() {
@@ -236,6 +245,7 @@ pub(super) fn shape(faces: &mut Vec<FreetypeFace>, text: &str) -> Vec<GlyphInfo>
             let remain_text = &text[cluster_start..];
             let remain_text = &text[cluster_start..];
             let glyphs = face_shape(&mut faces[face_idx], remain_text, cluster_start, face_idx);
             let glyphs = face_shape(&mut faces[face_idx], remain_text, cluster_start, face_idx);
 
 
+            // We weren't successful shaping with this fallback font, so skip over these glyphs.
             let leading_zeros = count_leading_null_glyphs(&glyphs);
             let leading_zeros = count_leading_null_glyphs(&glyphs);
             last_idx = off + leading_zeros;
             last_idx = off + leading_zeros;
 
 
@@ -252,8 +262,7 @@ pub(super) fn shape(faces: &mut Vec<FreetypeFace>, text: &str) -> Vec<GlyphInfo>
 mod tests {
 mod tests {
     use super::*;
     use super::*;
 
 
-    #[test]
-    fn shape_test() {
+    fn load_faces() -> Vec<FreetypeFace> {
         let ftlib = ft::Library::init().unwrap();
         let ftlib = ft::Library::init().unwrap();
 
 
         let mut faces = vec![];
         let mut faces = vec![];
@@ -269,23 +278,127 @@ mod tests {
         //let face = ftlib.new_memory_face2(font_data, 0).unwrap();
         //let face = ftlib.new_memory_face2(font_data, 0).unwrap();
         //faces.push(face);
         //faces.push(face);
 
 
-        //let text = "\u{01f3f3}\u{fe0f}\u{200d}\u{26a7}\u{fe0f} hello";
-        //let text = "i am a \u{01f3f3}\u{fe0f}\u{200d}\u{26a7}\u{fe0f} ally";
-        //let text = "hel \u{01f3f3}\u{fe0f}\u{200d}\u{26a7}\u{fe0f} 123 X\u{01f44d}\u{01f3fe}X br";
-        //let text = "日本語";
-        //let text = "hel 日本語\u{01f3f3}\u{fe0f}\u{200d}\u{26a7}\u{fe0f} ally";
+        faces
+    }
 
 
+    #[test]
+    fn simple_shape_test() {
+        let mut faces = load_faces();
         let text = "\u{01f3f3}\u{fe0f}\u{200d}\u{26a7}\u{fe0f}";
         let text = "\u{01f3f3}\u{fe0f}\u{200d}\u{26a7}\u{fe0f}";
         let glyphs = shape(&mut faces, text);
         let glyphs = shape(&mut faces, text);
+
         assert_eq!(glyphs.len(), 1);
         assert_eq!(glyphs.len(), 1);
         assert_eq!(glyphs[0].face_idx, 1);
         assert_eq!(glyphs[0].face_idx, 1);
         assert_eq!(glyphs[0].id, 1895);
         assert_eq!(glyphs[0].id, 1895);
         assert_eq!(glyphs[0].cluster_start, 0);
         assert_eq!(glyphs[0].cluster_start, 0);
         assert_eq!(glyphs[0].cluster_end, 16);
         assert_eq!(glyphs[0].cluster_end, 16);
+    }
+
+    #[test]
+    fn simple_double_shape_test() {
+        let mut faces = load_faces();
+        let text =
+            "\u{01f3f3}\u{fe0f}\u{200d}\u{26a7}\u{fe0f}\u{01f3f3}\u{fe0f}\u{200d}\u{26a7}\u{fe0f}";
+        let glyphs = shape(&mut faces, text);
+
+        assert_eq!(glyphs.len(), 2);
+        assert_eq!(glyphs[0].face_idx, 1);
+        assert_eq!(glyphs[0].id, 1895);
+        assert_eq!(glyphs[0].cluster_start, 0);
+        assert_eq!(glyphs[0].cluster_end, 16);
+        assert_eq!(glyphs[1].face_idx, 1);
+        assert_eq!(glyphs[1].id, 1895);
+        assert_eq!(glyphs[1].cluster_start, 16);
+        assert_eq!(glyphs[1].cluster_end, 32);
+    }
 
 
-        //for g in shape(&mut faces, text) {
-        //    let substr = &text[g.cluster_start..g.cluster_end];
-        //    println!("{}/{}: [{}, {}], '{}'", g.face_idx, g.id, g.cluster_start, g.cluster_end, substr);
-        //}
+    #[test]
+    fn mixed_shape_test() {
+        //let text = "日本語";
+        //let text = "hel 日本語\u{01f3f3}\u{fe0f}\u{200d}\u{26a7}\u{fe0f} ally";
+
+        let mut faces = load_faces();
+        let text = "hel \u{01f3f3}\u{fe0f}\u{200d}\u{26a7}\u{fe0f} 123 X\u{01f44d}\u{01f3fe}X br";
+        let glyphs = shape(&mut faces, text);
+
+        assert_eq!(glyphs[0].face_idx, 0);
+        assert_eq!(glyphs[0].id, 11);
+        assert_eq!(glyphs[0].cluster_start, 0);
+        assert_eq!(glyphs[0].cluster_end, 1);
+        assert_eq!(glyphs[1].face_idx, 0);
+        assert_eq!(glyphs[1].id, 6);
+        assert_eq!(glyphs[1].cluster_start, 1);
+        assert_eq!(glyphs[1].cluster_end, 2);
+        assert_eq!(glyphs[1].cluster_start, glyphs[0].cluster_end);
+        assert_eq!(glyphs[2].face_idx, 0);
+        assert_eq!(glyphs[2].id, 15);
+        assert_eq!(glyphs[2].cluster_start, 2);
+        assert_eq!(glyphs[2].cluster_end, 3);
+        assert_eq!(glyphs[2].cluster_start, glyphs[1].cluster_end);
+        assert_eq!(glyphs[3].face_idx, 0);
+        assert_eq!(glyphs[3].id, 1099);
+        assert_eq!(glyphs[3].cluster_start, 3);
+        assert_eq!(glyphs[3].cluster_end, 4);
+        assert_eq!(glyphs[3].cluster_start, glyphs[2].cluster_end);
+        assert_eq!(glyphs[4].face_idx, 1);
+        assert_eq!(glyphs[4].id, 1895);
+        assert_eq!(glyphs[4].cluster_start, 4);
+        assert_eq!(glyphs[4].cluster_end, 20);
+        assert_eq!(glyphs[4].cluster_start, glyphs[3].cluster_end);
+        assert_eq!(glyphs[5].face_idx, 0);
+        assert_eq!(glyphs[5].id, 1099);
+        assert_eq!(glyphs[5].cluster_start, 20);
+        assert_eq!(glyphs[5].cluster_end, 21);
+        assert_eq!(glyphs[5].cluster_start, glyphs[4].cluster_end);
+        assert_eq!(glyphs[6].face_idx, 0);
+        assert_eq!(glyphs[6].id, 59);
+        assert_eq!(glyphs[6].cluster_start, 21);
+        assert_eq!(glyphs[6].cluster_end, 22);
+        assert_eq!(glyphs[6].cluster_start, glyphs[5].cluster_end);
+        assert_eq!(glyphs[7].face_idx, 0);
+        assert_eq!(glyphs[7].id, 60);
+        assert_eq!(glyphs[7].cluster_start, 22);
+        assert_eq!(glyphs[7].cluster_end, 23);
+        assert_eq!(glyphs[7].cluster_start, glyphs[6].cluster_end);
+        assert_eq!(glyphs[8].face_idx, 0);
+        assert_eq!(glyphs[8].id, 61);
+        assert_eq!(glyphs[8].cluster_start, 23);
+        assert_eq!(glyphs[8].cluster_end, 24);
+        assert_eq!(glyphs[8].cluster_start, glyphs[7].cluster_end);
+        assert_eq!(glyphs[9].face_idx, 0);
+        assert_eq!(glyphs[9].id, 1099);
+        assert_eq!(glyphs[9].cluster_start, 24);
+        assert_eq!(glyphs[9].cluster_end, 25);
+        assert_eq!(glyphs[9].cluster_start, glyphs[8].cluster_end);
+        assert_eq!(glyphs[10].face_idx, 0);
+        assert_eq!(glyphs[10].id, 53);
+        assert_eq!(glyphs[10].cluster_start, 25);
+        assert_eq!(glyphs[10].cluster_end, 26);
+        assert_eq!(glyphs[10].cluster_start, glyphs[9].cluster_end);
+        assert_eq!(glyphs[11].face_idx, 1);
+        assert_eq!(glyphs[11].id, 1955);
+        assert_eq!(glyphs[11].cluster_start, 26);
+        assert_eq!(glyphs[11].cluster_end, 34);
+        assert_eq!(glyphs[11].cluster_start, glyphs[10].cluster_end);
+        assert_eq!(glyphs[12].face_idx, 0);
+        assert_eq!(glyphs[12].id, 53);
+        assert_eq!(glyphs[12].cluster_start, 34);
+        assert_eq!(glyphs[12].cluster_end, 35);
+        assert_eq!(glyphs[12].cluster_start, glyphs[11].cluster_end);
+        assert_eq!(glyphs[13].face_idx, 0);
+        assert_eq!(glyphs[13].id, 1099);
+        assert_eq!(glyphs[13].cluster_start, 35);
+        assert_eq!(glyphs[13].cluster_end, 36);
+        assert_eq!(glyphs[13].cluster_start, glyphs[12].cluster_end);
+        assert_eq!(glyphs[14].face_idx, 0);
+        assert_eq!(glyphs[14].id, 3);
+        assert_eq!(glyphs[14].cluster_start, 36);
+        assert_eq!(glyphs[14].cluster_end, 37);
+        assert_eq!(glyphs[14].cluster_start, glyphs[13].cluster_end);
+        assert_eq!(glyphs[15].face_idx, 0);
+        assert_eq!(glyphs[15].id, 21);
+        assert_eq!(glyphs[15].cluster_start, 37);
+        assert_eq!(glyphs[15].cluster_end, 38);
+        assert_eq!(glyphs[15].cluster_start, glyphs[14].cluster_end);
     }
     }
 }
 }

+ 1 - 1
bin/darkwallet/src/ui/emoji_picker/mod.rs

@@ -203,7 +203,7 @@ impl EmojiPicker {
         let emojis_len = emoji::EMOJI_LIST.len() as f32;
         let emojis_len = emoji::EMOJI_LIST.len() as f32;
         let emoji_size = self.emoji_size.get();
         let emoji_size = self.emoji_size.get();
         let cols = self.emojis_per_line();
         let cols = self.emojis_per_line();
-        let rows = (emojis_len / cols).floor();
+        let rows = (emojis_len / cols).ceil();
 
 
         let rect_h = self.rect.get().h;
         let rect_h = self.rect.get().h;
         let height = rows * emoji_size;
         let height = rows * emoji_size;