[geary/wip/3.32-avatars: 47/50] Make avatar code a bit more robust in the face of bad input



commit 76ee07595d2676f6bb3ff79359f79519457f8470
Author: Michael Gratton <mike vee net>
Date:   Sat Mar 9 19:59:25 2019 +1100

    Make avatar code a bit more robust in the face of bad input
    
    Don't try to dray null initials, actually return null per API contract
    when no initials can be found, don't include non-alphanumerics in the
    initials. Add test case.

 src/client/util/util-avatar.vala       | 68 ++++++++++++++++++++++------------
 test/client/util/util-avatar-test.vala | 34 +++++++++++++++++
 test/meson.build                       |  1 +
 test/test-client.vala                  |  1 +
 4 files changed, 80 insertions(+), 24 deletions(-)
---
diff --git a/src/client/util/util-avatar.vala b/src/client/util/util-avatar.vala
index 21ce4ab8..d6c0ff6b 100644
--- a/src/client/util/util-avatar.vala
+++ b/src/client/util/util-avatar.vala
@@ -7,41 +7,45 @@
 
 namespace Util.Avatar {
 
-    // The following was ported from code written by Felipe Borges for
+    // The following was based on code written by Felipe Borges for
     // gnome-control-enter in panels/user-accounts/user-utils.c commit
     // 02c288ab6f069a0c106323a93400f192a63cb67e. The copyright in that
     // file is: "Copyright 2009-2010  Red Hat, Inc,"
 
     public Gdk.Pixbuf generate_user_picture(string name, int size) {
-        string initials = extract_initials_from_name(name);
-        string font = "Sans %d".printf((int) GLib.Math.ceil(size / 2.5));
-        Gdk.RGBA color = get_color_for_name(name);
-
         Cairo.Surface surface = new Cairo.ImageSurface(
             Cairo.Format.ARGB32, size, size
         );
         Cairo.Context cr = new Cairo.Context(surface);
         cr.rectangle(0, 0, size, size);
+
+        /* Fill the background with a colour for the name */
+        Gdk.RGBA color = get_color_for_name(name);
         cr.set_source_rgb(
             color.red / 255.0, color.green / 255.0, color.blue / 255.0
         );
         cr.fill();
 
         /* Draw the initials on top */
-        cr.set_source_rgb(1.0, 1.0, 1.0);
-        Pango.Layout layout = Pango.cairo_create_layout(cr);
-        layout.set_text(initials, -1);
-        layout.set_font_description(Pango.FontDescription.from_string(font));
-
-        int width, height;
-        layout.get_size(out width, out height);
-        cr.translate(size / 2, size / 2);
-        cr.move_to(
-            -((double) width / Pango.SCALE) / 2,
-            -((double) height / Pango.SCALE) / 2
-        );
-        Pango.cairo_show_layout(cr, layout);
-        
+        string? initials = extract_initials_from_name(name);
+        if (initials != null) {
+            string font = "Sans %d".printf((int) GLib.Math.ceil(size / 2.5));
+
+            cr.set_source_rgb(1.0, 1.0, 1.0);
+            Pango.Layout layout = Pango.cairo_create_layout(cr);
+            layout.set_text(initials, -1);
+            layout.set_font_description(Pango.FontDescription.from_string(font));
+
+            int width, height;
+            layout.get_size(out width, out height);
+            cr.translate(size / 2, size / 2);
+            cr.move_to(
+                -((double) width / Pango.SCALE) / 2,
+                -((double) height / Pango.SCALE) / 2
+            );
+            Pango.cairo_show_layout(cr, layout);
+        }
+
         return Gdk.pixbuf_get_from_surface(
             surface, 0, 0, size, size
         );
@@ -72,13 +76,29 @@ namespace Util.Avatar {
         string? initials = null;
         if (normalized != "") {
             GLib.StringBuilder buf = new GLib.StringBuilder();
-            buf.append_unichar(normalized.get_char(0));
+            unichar c = 0;
+            int index  = 0;
+
+            // Get the first alphanumeric char of the string
+            for (int i = 0; normalized.get_next_char(ref index, out c); i++) {
+                if (c.isalnum()) {
+                    buf.append_unichar(c);
+                    break;
+                }
+            }
+
+            // Get the first alphanumeric char of the last word of the string
+            index = normalized.last_index_of_char(' ');
+            for (int i = 0; normalized.get_next_char(ref index, out c); i++) {
+                if (c.isalnum()) {
+                    buf.append_unichar(c);
+                    break;
+                }
+            }
 
-            int index = normalized.last_index_of_char(' ');
-            if (index != -1 && (index + 1) < normalized.length) {
-                buf.append_unichar(normalized.get_char(index + 1));
+            if (buf.data.length > 0) {
+                initials = (string) buf.data;
             }
-            initials = (string) buf.data;
         }
         return initials;
     }
diff --git a/test/client/util/util-avatar-test.vala b/test/client/util/util-avatar-test.vala
new file mode 100644
index 00000000..6fe612c3
--- /dev/null
+++ b/test/client/util/util-avatar-test.vala
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2019 Michael Gratton <mike vee net>
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+public class Util.Avatar.Test : TestCase {
+
+    public Test() {
+        base("UtilAvatarTest");
+        add_test("extract_initials", extract_initials);
+    }
+
+    public void extract_initials() throws GLib.Error {
+        assert_string("A", extract_initials_from_name("aardvark"));
+        assert_string("AB", extract_initials_from_name("aardvark baardvark"));
+        assert_string("AB", extract_initials_from_name("aardvark  baardvark"));
+        assert_string("AC", extract_initials_from_name("aardvark baardvark caardvark"));
+
+        assert_string("A", extract_initials_from_name("!aardvark"));
+        assert_string("AB", extract_initials_from_name("aardvark !baardvark"));
+        assert_string("AC", extract_initials_from_name("aardvark baardvark !caardvark"));
+
+        assert_true(extract_initials_from_name("") == null);
+        assert_true(extract_initials_from_name(" ") == null);
+        assert_true(extract_initials_from_name("  ") == null);
+        assert_true(extract_initials_from_name("!") == null);
+        assert_true(extract_initials_from_name("!!") == null);
+        assert_true(extract_initials_from_name("! !") == null);
+        assert_true(extract_initials_from_name("! !!") == null);
+    }
+
+}
diff --git a/test/meson.build b/test/meson.build
index 8061c9cc..6054796a 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -75,6 +75,7 @@ geary_test_client_sources = [
   'client/components/client-web-view-test.vala',
   'client/components/client-web-view-test-case.vala',
   'client/composer/composer-web-view-test.vala',
+  'client/util/util-avatar-test.vala',
 
   'js/client-page-state-test.vala',
   'js/composer-page-state-test.vala',
diff --git a/test/test-client.vala b/test/test-client.vala
index 15e43abe..3386d183 100644
--- a/test/test-client.vala
+++ b/test/test-client.vala
@@ -43,6 +43,7 @@ int main(string[] args) {
     client.add_suite(new ClientWebViewTest().get_suite());
     client.add_suite(new ComposerWebViewTest().get_suite());
     client.add_suite(new ConfigurationTest().get_suite());
+    client.add_suite(new Util.Avatar.Test().get_suite());
 
     TestSuite js = new TestSuite("js");
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]