Welcome to mirror list, hosted at ThFree Co, Russian Federation.

dev.gajim.org/gajim/gajim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gajim/gtk/avatar.py20
-rw-r--r--gajim/gtk/util.py18
-rw-r--r--test/no_gui/__init__.py1
-rw-r--r--test/no_gui/test_gtk_util.py71
4 files changed, 92 insertions, 18 deletions
diff --git a/gajim/gtk/avatar.py b/gajim/gtk/avatar.py
index dd07872f2..3cdb37a09 100644
--- a/gajim/gtk/avatar.py
+++ b/gajim/gtk/avatar.py
@@ -40,8 +40,8 @@ from gajim.common.const import AvatarSize
from gajim.common.const import StyleAttr
from .const import DEFAULT_WORKSPACE_COLOR
-from .emoji_data_gtk import get_emoji_data
from .util import get_contact_color
+from .util import get_first_graphemes
from .util import load_icon_surface
from .util import load_pixbuf
from .util import scale_with_ratio
@@ -58,23 +58,7 @@ AvatarCacheT = dict[Union[JID, str], dict[tuple[int, int, Optional[str]],
def generate_avatar_letter(text: str) -> str:
- if not text:
- return ''
-
- if text[0].isalpha():
- return text[0].upper()
-
- emoji_data = get_emoji_data()
-
- # Max (arbitrary) length for emoji ZJW sequences: 11
- for length in range(11, 0, -1):
- prefix = text[:length]
- for entries in emoji_data.values():
- for emoji in entries.values():
- if prefix == emoji:
- return prefix
-
- return text[0].upper()
+ return get_first_graphemes(text.lstrip(), 1).upper()
def generate_avatar(letters: str,
diff --git a/gajim/gtk/util.py b/gajim/gtk/util.py
index de2cf18d2..b2e79d44c 100644
--- a/gajim/gtk/util.py
+++ b/gajim/gtk/util.py
@@ -850,6 +850,24 @@ def make_pango_attributes(block: PlainBlock) -> Pango.AttrList:
return attrlist
+_grapheme_buffer = Gtk.TextBuffer()
+
+
+def get_first_graphemes(text: str, n: int) -> str:
+ # This should be possible with lower-level APIs like Pango.break_* or
+ # Pango.get_log_attrs, but their Python bindings seem totally broken.
+ # The re-use of one global buffer is to mitigate very probable memory leaks.
+ _grapheme_buffer.set_text(text)
+ cursor = _grapheme_buffer.get_start_iter()
+ cursor.forward_cursor_positions(n)
+ return _grapheme_buffer.get_slice(
+ _grapheme_buffer.get_start_iter(), cursor, False)
+
+
+def get_first_grapheme(text: str) -> str:
+ return get_first_graphemes(text, 1)
+
+
def get_style_attribute_with_name(name: str) -> Pango.Attribute:
if name == 'strong':
return Pango.attr_weight_new(Pango.Weight.BOLD)
diff --git a/test/no_gui/__init__.py b/test/no_gui/__init__.py
index 1513837b3..1a26b1822 100644
--- a/test/no_gui/__init__.py
+++ b/test/no_gui/__init__.py
@@ -5,6 +5,7 @@ def require_versions():
'GLib': '2.0',
'Gio': '2.0',
'Gtk': '3.0',
+ 'GtkSource': '4',
'GObject': '2.0',
'Pango': '1.0'})
diff --git a/test/no_gui/test_gtk_util.py b/test/no_gui/test_gtk_util.py
new file mode 100644
index 000000000..3c57d3265
--- /dev/null
+++ b/test/no_gui/test_gtk_util.py
@@ -0,0 +1,71 @@
+import unittest
+
+from gajim import gui
+gui.init('gtk')
+
+from gajim.gtk.util import get_first_grapheme
+
+
+class Test(unittest.TestCase):
+ def test_get_first_grapheme(self):
+ self.assertEqual(
+ get_first_grapheme(''), '', '<empty string>')
+ self.assertEqual(
+ get_first_grapheme('a'), 'a', 'a')
+ self.assertEqual(
+ get_first_grapheme('ab'), 'a', 'ab -> a')
+
+ über = 'u\u0308ber'
+ self.assertEqual(
+ get_first_grapheme(über), 'u\u0308', über + ' -> ü')
+
+ woman = '\U0001F469'
+ zwj = '\u200D'
+ vs16 = '\uFE0F'
+ fitz4 = '\U0001F3FD'
+
+ farmeress = f'{woman}{zwj}\U0001F33E{vs16}'
+ self.assertEqual(
+ get_first_grapheme(farmeress), farmeress, '👩‍🌾️')
+
+ longass = f'{woman}{fitz4}{zwj}\u2764{vs16}{zwj}\U0001F468{fitz4}'
+ self.assertEqual(
+ get_first_grapheme(longass), longass, '👩🏽‍❤️‍👨🏽')
+
+ # The following are from
+ # https://www.unicode.org/reports/tr29/#Table_Sample_Grapheme_Clusters
+
+ hangul_gag = '\u1100\u1161\u11A8'
+ self.assertEqual(
+ get_first_grapheme(hangul_gag), hangul_gag, '각')
+
+ tamil_ni = '\u0BA8\u0BBF'
+ self.assertEqual(
+ get_first_grapheme(tamil_ni), tamil_ni, 'நி')
+
+ # Fails 🤷 (returns the first char)
+ #thai_kam = '\u0E01\u0E33'
+ #self.assertEqual(
+ # get_first_grapheme(thai_kam), thai_kam, 'กำ')
+
+ devanagari_ssi = '\u0937\u093F'
+ self.assertEqual(
+ get_first_grapheme(devanagari_ssi), devanagari_ssi, 'षि')
+
+ # Only in some locales (e.g., Slovak):
+ #self.assertEqual(
+ # get_first_grapheme('ch'), 'ch', 'ch -> ch')
+ # Actually, Gtk.TextIter.forward_cursor_position() doesn't seem to use
+ # tailored algorithms anyway, so even with LANG=sk_SK.UTF-8 this
+ # returns 'c', not 'ch'.
+
+ # In most locales (say, any western one):
+ devanagari_kshi = '\u0915\u094D' + devanagari_ssi
+ self.assertEqual(
+ get_first_grapheme(devanagari_kshi), '\u0915\u094D', 'क्षि -> क् ')
+ # This probably won't fail on *any* locale, ever, again because the
+ # implementaion doesn't seem locale-specific.
+
+
+if __name__ == '__main__':
+ unittest.main()