diff --git a/src/Greenshot.Editor/Controls/Emoji/ChunkHelper.cs b/src/Greenshot.Editor/Controls/Emoji/ChunkHelper.cs
new file mode 100644
index 000000000..d4aa24002
--- /dev/null
+++ b/src/Greenshot.Editor/Controls/Emoji/ChunkHelper.cs
@@ -0,0 +1,69 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: https://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Greenshot.Editor.Controls.Emoji;
+
+///
+/// This seems to enumerate multiple IEnumerables.
+/// TODO: Replace this with LINQ
+///
+///
+internal sealed class ChunkHelper : IEnumerable>
+{
+ private readonly IEnumerable _elements;
+ private readonly int _size;
+ private bool _hasMore;
+
+ public ChunkHelper(IEnumerable elements, int size)
+ {
+ _elements = elements;
+ _size = size;
+ }
+
+ public IEnumerator> GetEnumerator()
+ {
+ using var enumerator = _elements.GetEnumerator();
+ _hasMore = enumerator.MoveNext();
+ while (_hasMore)
+ {
+ yield return GetNextBatch(enumerator).ToList();
+ }
+ }
+
+ private IEnumerable GetNextBatch(IEnumerator enumerator)
+ {
+ for (int i = 0; i < _size; ++i)
+ {
+ yield return enumerator.Current;
+ _hasMore = enumerator.MoveNext();
+ if (!_hasMore)
+ {
+ yield break;
+ }
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+}
\ No newline at end of file
diff --git a/src/Greenshot.Editor/Controls/Emoji/EmojiControl.cs b/src/Greenshot.Editor/Controls/Emoji/EmojiControl.cs
new file mode 100644
index 000000000..0589c5ee1
--- /dev/null
+++ b/src/Greenshot.Editor/Controls/Emoji/EmojiControl.cs
@@ -0,0 +1,54 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel
+ *
+ * For more information see: https://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using Greenshot.Editor.Drawing.Emoji;
+
+namespace Greenshot.Editor.Controls.Emoji
+{
+ internal class EmojiControl : Image
+ {
+ public static readonly DependencyProperty EmojiProperty = DependencyProperty.Register("Emoji", typeof(string), typeof(EmojiControl), new PropertyMetadata(default(string), OnEmojiPropertyChanged));
+
+ private static void OnEmojiPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ ((EmojiControl)d).Source = null;
+ }
+
+ public string Emoji
+ {
+ get => (string)GetValue(EmojiProperty);
+ set => SetValue(EmojiProperty, value);
+ }
+
+ protected override void OnRender(DrawingContext dc)
+ {
+ if (Source == null && !string.IsNullOrEmpty(Emoji))
+ {
+ Source = EmojiRenderer.GetBitmapSource(Emoji, iconSize: 48);
+ }
+
+ base.OnRender(dc);
+ }
+ }
+}
diff --git a/src/Greenshot.Editor/Controls/Emoji/EmojiData.cs b/src/Greenshot.Editor/Controls/Emoji/EmojiData.cs
new file mode 100644
index 000000000..b053ab3f4
--- /dev/null
+++ b/src/Greenshot.Editor/Controls/Emoji/EmojiData.cs
@@ -0,0 +1,199 @@
+//
+// Emoji.Wpf — Emoji support for WPF
+//
+// Copyright © 2017—2021 Sam Hocevar
+//
+// This library is free software. It comes without any warranty, to
+// the extent permitted by applicable law. You can redistribute it
+// and/or modify it under the terms of the Do What the Fuck You Want
+// to Public License, Version 2, as published by the WTFPL Task Force.
+// See http://www.wtfpl.net/ for more details.
+//
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Xml;
+using System.Xml.Serialization;
+using SixLabors.Fonts.Unicode;
+
+namespace Greenshot.Editor.Controls.Emoji
+{
+ ///
+ /// This class processes the emoji-test.txt to generate a list of possible emoji depending ony skin tone and hairstyle.
+ ///
+ public static class EmojiData
+ {
+ private const string FilePath = "emojis.xml";
+
+ public static Emojis Data { get; private set; } = new Emojis();
+
+ public static void Load()
+ {
+ var x = new XmlSerializer(typeof(Emojis));
+
+ if (File.Exists(FilePath))
+ {
+ Data = (Emojis)x.Deserialize(new XmlTextReader(FilePath));
+ }
+ else
+ {
+ // To be removed
+ ParseEmojiList();
+ x.Serialize(new XmlTextWriter(FilePath, Encoding.UTF8), Data);
+ }
+ }
+
+ private static readonly List SkinToneComponents = new List
+ {
+ "🏻", // light skin tone
+ "🏼", // medium-light skin tone
+ "🏽", // medium skin tone
+ "🏾", // medium-dark skin tone
+ "🏿", // dark skin tone
+ };
+
+ private static readonly List HairStyleComponents = new List
+ {
+ "🦰", // red hair
+ "🦱", // curly hair
+ "🦳", // white hair
+ "🦲", // bald
+ };
+
+ private static string ToColonSyntax(string s)
+ => Regex.Replace(s.Trim().ToLowerInvariant(), "[^a-z0-9]+", "-");
+
+ private static void ParseEmojiList()
+ {
+ var lookupByName = new Dictionary();
+ var matchGroup = new Regex(@"^# group: (.*)");
+ var matchSubgroup = new Regex(@"^# subgroup: (.*)");
+ var matchSequence = new Regex(@"^([0-9a-fA-F ]+[0-9a-fA-F]).*; *([-a-z]*) *# [^ ]* (E[0-9.]* )?(.*)");
+ var matchSkinTone = new Regex($"({string.Join("|", SkinToneComponents)})");
+ var matchHairStyle = new Regex($"({string.Join("|", HairStyleComponents)})");
+
+ var adult = "(👨|👩)(🏻|🏼|🏽|🏾|🏿)?";
+ var child = "(👦|👧|👶)(🏻|🏼|🏽|🏾|🏿)?";
+ var matchFamily = new Regex($"{adult}(\u200d{adult})*(\u200d{child})+");
+
+ var qualifiedLut = new Dictionary();
+ var allText = new List();
+
+ Emojis.Group currentGroup = null;
+ Emojis.Group currentSubgroup = null;
+
+ foreach (var line in EmojiDescriptionLines())
+ {
+ var m = matchGroup.Match(line);
+ if (m.Success)
+ {
+ currentGroup = new Emojis.Group { Name = m.Groups[1].ToString() };
+ Data.Groups.Add(currentGroup);
+ continue;
+ }
+
+ m = matchSubgroup.Match(line);
+ if (m.Success)
+ {
+ currentSubgroup = new Emojis.Group { Name = m.Groups[1].ToString() };
+ currentGroup.SubGroups.Add(currentSubgroup);
+ continue;
+ }
+
+ m = matchSequence.Match(line);
+ if (m.Success)
+ {
+ string sequence = m.Groups[1].ToString();
+ string name = m.Groups[4].ToString();
+
+ string text = string.Join("", from n in sequence.Split(' ')
+ select char.ConvertFromUtf32(Convert.ToInt32(n, 16)));
+ bool has_modifier = false;
+
+ if (matchFamily.Match(text).Success)
+ {
+ // If this is a family emoji, no need to add it to our big matching
+ // regex, since the match_family regex is already included.
+ }
+ else
+ {
+ // Construct a regex to replace e.g. "🏻" with "(🏻|🏼|🏽|🏾|🏿)" in a big
+ // regex so that we can match all variations of this Emoji even if they are
+ // not in the standard.
+ bool hasNonfirstModifier = false;
+ var regexText = matchSkinTone.Replace(
+ matchHairStyle.Replace(text, (x) =>
+ {
+ has_modifier = true;
+ hasNonfirstModifier |= x.Value != HairStyleComponents[0];
+ return matchHairStyle.ToString();
+ }), (x) =>
+ {
+ has_modifier = true;
+ hasNonfirstModifier |= x.Value != SkinToneComponents[0];
+ return matchSkinTone.ToString();
+ });
+
+ if (!hasNonfirstModifier)
+ {
+ allText.Add(has_modifier ? regexText : text);
+ }
+ }
+
+ // If there is already a differently-qualified version of this character, skip it.
+ // FIXME: this only works well if fully-qualified appears first in the list.
+ var unqualified = text.Replace("\ufe0f", "");
+ if (qualifiedLut.ContainsKey(unqualified))
+ {
+ continue;
+ }
+
+ // Fix simple fully-qualified emojis
+ if (CodePoint.GetCodePointCount(text.AsSpan()) == 2)
+ {
+ text = text.TrimEnd('\ufe0f');
+ }
+
+ qualifiedLut[unqualified] = text;
+
+ var emoji = new Emojis.Emoji { Name = name, Text = text, };
+
+ lookupByName[ToColonSyntax(name)] = emoji;
+
+ // Get the left part of the name and check whether we’re a variation of an existing
+ // emoji. If so, append to that emoji. Otherwise, add to current subgroup.
+ // FIXME: does not work properly because variations can appear before the generic emoji
+ if (name.Contains(":") && lookupByName.TryGetValue(ToColonSyntax(name.Split(':')[0]), out var parent_emoji))
+ {
+ parent_emoji.Variations.Add(emoji);
+ }
+ else
+ {
+ currentSubgroup.Emojis.Add(emoji);
+ }
+ }
+ }
+
+ // Remove the Component group. Not sure we want to have the skin tones in the picker.
+ Data.Groups.RemoveAll(g => g.Name == "Component");
+ }
+
+ private static IEnumerable EmojiDescriptionLines()
+ {
+ var exeDirectory = Path.GetDirectoryName(Assembly.GetCallingAssembly().Location);
+ var emojiTestFile = Path.Combine(exeDirectory, @"emoji-test.txt");
+ if (!File.Exists(emojiTestFile))
+ {
+ throw new FileNotFoundException($"Can't find {emojiTestFile}, bad installation?");
+ }
+ using var fileStream = new FileStream(emojiTestFile, FileMode.Open, FileAccess.Read);
+ using var streamReader = new StreamReader(fileStream);
+ return streamReader.ReadToEnd().Split('\r', '\n');
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Greenshot.Editor/Controls/EmojiPicker.xaml b/src/Greenshot.Editor/Controls/Emoji/EmojiPicker.xaml
similarity index 93%
rename from src/Greenshot.Editor/Controls/EmojiPicker.xaml
rename to src/Greenshot.Editor/Controls/Emoji/EmojiPicker.xaml
index 011754560..ddbb78b2d 100644
--- a/src/Greenshot.Editor/Controls/EmojiPicker.xaml
+++ b/src/Greenshot.Editor/Controls/Emoji/EmojiPicker.xaml
@@ -4,7 +4,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:controls="clr-namespace:Greenshot.Editor.Controls"
+ xmlns:emoji="clr-namespace:Greenshot.Editor.Controls.Emoji"
Orientation="Horizontal"
mc:Ignorable="d">
@@ -36,7 +36,7 @@
@@ -48,7 +48,7 @@
x:Name="VariationButton" Background="Transparent" BorderBrush="Transparent"
Click="OnEmojiPicked" Focusable="False" ToolTip="{Binding Path=Name}">
-
-
+
-
+
-
+
diff --git a/src/Greenshot.Editor/Controls/EmojiPicker.xaml.cs b/src/Greenshot.Editor/Controls/Emoji/EmojiPicker.xaml.cs
similarity index 99%
rename from src/Greenshot.Editor/Controls/EmojiPicker.xaml.cs
rename to src/Greenshot.Editor/Controls/Emoji/EmojiPicker.xaml.cs
index 4e36b6339..35f346cd6 100644
--- a/src/Greenshot.Editor/Controls/EmojiPicker.xaml.cs
+++ b/src/Greenshot.Editor/Controls/Emoji/EmojiPicker.xaml.cs
@@ -17,6 +17,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
+using Greenshot.Editor.Controls.Emoji;
namespace Greenshot.Editor.Controls
{
diff --git a/src/Greenshot.Editor/Controls/Emoji/Emojis.cs b/src/Greenshot.Editor/Controls/Emoji/Emojis.cs
new file mode 100644
index 000000000..8ddf41e34
--- /dev/null
+++ b/src/Greenshot.Editor/Controls/Emoji/Emojis.cs
@@ -0,0 +1,70 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom
+ *
+ * For more information see: https://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Xml.Serialization;
+
+namespace Greenshot.Editor.Controls.Emoji;
+
+public class Emojis
+{
+ [XmlElement(ElementName = "Group")]
+ public List Groups { get; set; } = new();
+
+ public class Group
+ {
+ [XmlAttribute]
+ public string Name { get; set; }
+
+ [XmlElement(ElementName = "Group")]
+ public List SubGroups { get; set; } = new();
+
+ [XmlElement(ElementName = "Emoji")]
+ public List Emojis { get; set; } = new();
+
+ public IEnumerable> EmojiChunkList => new ChunkHelper(EmojiList, 8);
+
+ public string Icon => SubGroups.FirstOrDefault()?.Emojis.FirstOrDefault()?.Text;
+
+ public IEnumerable EmojiList
+ => from s in SubGroups
+ from e in s.Emojis
+ select e;
+ }
+
+ public class Emoji
+ {
+ [XmlAttribute]
+ public string Name { get; set; }
+
+ [XmlAttribute]
+ public string Text { get; set; }
+
+ [XmlArray]
+ public List Variations { get; set; } = new();
+
+ public bool HasVariations => Variations.Count > 0;
+
+ public IEnumerable AllVariations => HasVariations ? new[] { this }.Union(Variations) : Array.Empty();
+ }
+}
\ No newline at end of file
diff --git a/src/Greenshot.Editor/Resources/emoji-test.txt b/src/Greenshot.Editor/Controls/Emoji/emoji-test.txt
similarity index 100%
rename from src/Greenshot.Editor/Resources/emoji-test.txt
rename to src/Greenshot.Editor/Controls/Emoji/emoji-test.txt
diff --git a/src/Greenshot.Editor/Controls/EmojiControl.cs b/src/Greenshot.Editor/Controls/EmojiControl.cs
deleted file mode 100644
index c3fc09b6e..000000000
--- a/src/Greenshot.Editor/Controls/EmojiControl.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Media;
-using Greenshot.Editor.Drawing;
-
-namespace Greenshot.Editor.Controls
-{
- internal class EmojiControl : Image
- {
- public static readonly DependencyProperty EmojiProperty = DependencyProperty.Register("Emoji", typeof(string), typeof(EmojiControl), new PropertyMetadata(default(string), OnEmojiPropertyChanged));
-
- private static void OnEmojiPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- ((EmojiControl)d).Source = null;
- }
-
- public string Emoji
- {
- get { return (string)GetValue(EmojiProperty); }
- set { SetValue(EmojiProperty, value); }
- }
-
- protected override void OnRender(DrawingContext dc)
- {
- if (Source == null && !string.IsNullOrEmpty(Emoji))
- {
- Source = EmojiRenderer.GetBitmapSource(Emoji, iconSize: 48);
- }
-
- base.OnRender(dc);
- }
- }
-}
diff --git a/src/Greenshot.Editor/Controls/EmojiData.cs b/src/Greenshot.Editor/Controls/EmojiData.cs
deleted file mode 100644
index 0e1eb0039..000000000
--- a/src/Greenshot.Editor/Controls/EmojiData.cs
+++ /dev/null
@@ -1,262 +0,0 @@
-//
-// Emoji.Wpf — Emoji support for WPF
-//
-// Copyright © 2017—2021 Sam Hocevar
-//
-// This library is free software. It comes without any warranty, to
-// the extent permitted by applicable law. You can redistribute it
-// and/or modify it under the terms of the Do What the Fuck You Want
-// to Public License, Version 2, as published by the WTFPL Task Force.
-// See http://www.wtfpl.net/ for more details.
-//
-
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Xml;
-using System.Xml.Serialization;
-using SixLabors.Fonts.Unicode;
-
-namespace Greenshot.Editor.Controls
-{
- public static class EmojiData
- {
- private const string FilePath = "emojis.xml";
-
- public static Emojis Data { get; private set; } = new Emojis();
-
- public static void Load()
- {
- var x = new XmlSerializer(typeof(Emojis));
-
- if (File.Exists(FilePath))
- {
- Data = (Emojis)x.Deserialize(new XmlTextReader(FilePath));
- }
- else
- {
- // To be removed
- ParseEmojiList();
- x.Serialize(new XmlTextWriter(FilePath, Encoding.UTF8), Data);
- }
- }
-
- private static List SkinToneComponents = new List
- {
- "🏻", // light skin tone
- "🏼", // medium-light skin tone
- "🏽", // medium skin tone
- "🏾", // medium-dark skin tone
- "🏿", // dark skin tone
- };
-
- private static List HairStyleComponents = new List
- {
- "🦰", // red hair
- "🦱", // curly hair
- "🦳", // white hair
- "🦲", // bald
- };
-
- private static string ToColonSyntax(string s)
- => Regex.Replace(s.Trim().ToLowerInvariant(), "[^a-z0-9]+", "-");
-
- private static void ParseEmojiList()
- {
- var lookup_by_name = new Dictionary();
- var match_group = new Regex(@"^# group: (.*)");
- var match_subgroup = new Regex(@"^# subgroup: (.*)");
- var match_sequence = new Regex(@"^([0-9a-fA-F ]+[0-9a-fA-F]).*; *([-a-z]*) *# [^ ]* (E[0-9.]* )?(.*)");
- var match_skin_tone = new Regex($"({string.Join("|", SkinToneComponents)})");
- var match_hair_style = new Regex($"({string.Join("|", HairStyleComponents)})");
-
- var adult = "(👨|👩)(🏻|🏼|🏽|🏾|🏿)?";
- var child = "(👦|👧|👶)(🏻|🏼|🏽|🏾|🏿)?";
- var match_family = new Regex($"{adult}(\u200d{adult})*(\u200d{child})+");
-
- var qualified_lut = new Dictionary();
- var alltext = new List();
-
- Emojis.Group current_group = null;
- Emojis.Group current_subgroup = null;
-
- foreach (var line in EmojiDescriptionLines())
- {
- var m = match_group.Match(line);
- if (m.Success)
- {
- current_group = new Emojis.Group { Name = m.Groups[1].ToString() };
- Data.Groups.Add(current_group);
- continue;
- }
-
- m = match_subgroup.Match(line);
- if (m.Success)
- {
- current_subgroup = new Emojis.Group { Name = m.Groups[1].ToString() };
- current_group.SubGroups.Add(current_subgroup);
- continue;
- }
-
- m = match_sequence.Match(line);
- if (m.Success)
- {
- string sequence = m.Groups[1].ToString();
- string name = m.Groups[4].ToString();
-
- string text = string.Join("", from n in sequence.Split(' ')
- select char.ConvertFromUtf32(Convert.ToInt32(n, 16)));
- bool has_modifier = false;
-
- if (match_family.Match(text).Success)
- {
- // If this is a family emoji, no need to add it to our big matching
- // regex, since the match_family regex is already included.
- }
- else
- {
- // Construct a regex to replace e.g. "🏻" with "(🏻|🏼|🏽|🏾|🏿)" in a big
- // regex so that we can match all variations of this Emoji even if they are
- // not in the standard.
- bool has_nonfirst_modifier = false;
- var regex_text = match_skin_tone.Replace(
- match_hair_style.Replace(text, (x) =>
- {
- has_modifier = true;
- has_nonfirst_modifier |= x.Value != HairStyleComponents[0];
- return match_hair_style.ToString();
- }), (x) =>
- {
- has_modifier = true;
- has_nonfirst_modifier |= x.Value != SkinToneComponents[0];
- return match_skin_tone.ToString();
- });
-
- if (!has_nonfirst_modifier)
- alltext.Add(has_modifier ? regex_text : text);
- }
-
- // If there is already a differently-qualified version of this character, skip it.
- // FIXME: this only works well if fully-qualified appears first in the list.
- var unqualified = text.Replace("\ufe0f", "");
- if (qualified_lut.ContainsKey(unqualified))
- continue;
-
- // Fix simple fully-qualified emojis
- if (CodePoint.GetCodePointCount(text.AsSpan()) == 2)
- {
- text = text.TrimEnd('\ufe0f');
- }
-
- qualified_lut[unqualified] = text;
-
- var emoji = new Emojis.Emoji { Name = name, Text = text, };
-
- lookup_by_name[ToColonSyntax(name)] = emoji;
-
- // Get the left part of the name and check whether we’re a variation of an existing
- // emoji. If so, append to that emoji. Otherwise, add to current subgroup.
- // FIXME: does not work properly because variations can appear before the generic emoji
- if (name.Contains(":") && lookup_by_name.TryGetValue(ToColonSyntax(name.Split(':')[0]), out var parent_emoji))
- {
- parent_emoji.Variations.Add(emoji);
- }
- else
- current_subgroup.Emojis.Add(emoji);
- }
- }
-
- // Remove the Component group. Not sure we want to have the skin tones in the picker.
- Data.Groups.RemoveAll(g => g.Name == "Component");
- }
-
- private static IEnumerable EmojiDescriptionLines()
- {
- using var fileStream = new FileStream(Path.Combine(Path.GetDirectoryName(Assembly.GetCallingAssembly().Location), @"emoji-test.txt"), FileMode.Open, FileAccess.Read);
- using var streamReader = new StreamReader(fileStream);
- return streamReader.ReadToEnd().Split('\r', '\n');
- }
- }
-
- public class Emojis
- {
- [XmlElement(ElementName = "Group")]
- public List Groups { get; set; } = new();
-
- public class Group
- {
- [XmlAttribute]
- public string Name { get; set; }
-
- [XmlElement(ElementName = "Group")]
- public List SubGroups { get; set; } = new();
-
- [XmlElement(ElementName = "Emoji")]
- public List Emojis { get; set; } = new();
-
- public IEnumerable> EmojiChunkList => new ChunkHelper(EmojiList, 8);
-
- public string Icon => SubGroups.FirstOrDefault()?.Emojis.FirstOrDefault()?.Text;
-
- public IEnumerable EmojiList
- => from s in SubGroups
- from e in s.Emojis
- select e;
- }
-
- public class Emoji
- {
- [XmlAttribute]
- public string Name { get; set; }
-
- [XmlAttribute]
- public string Text { get; set; }
-
- [XmlArray]
- public List Variations { get; set; }
-
- public bool HasVariations => Variations.Count > 0;
-
- public IEnumerable AllVariations => HasVariations ? new[] { this }.Union(Variations) : Array.Empty();
- }
- }
-
- sealed class ChunkHelper : IEnumerable>
- {
- public ChunkHelper(IEnumerable elements, int size)
- {
- m_elements = elements;
- m_size = size;
- }
-
- public IEnumerator> GetEnumerator()
- {
- using var enumerator = m_elements.GetEnumerator();
- m_has_more = enumerator.MoveNext();
- while (m_has_more)
- yield return GetNextBatch(enumerator).ToList();
- }
-
- private IEnumerable GetNextBatch(IEnumerator enumerator)
- {
- for (int i = 0; i < m_size; ++i)
- {
- yield return enumerator.Current;
- m_has_more = enumerator.MoveNext();
- if (!m_has_more)
- yield break;
- }
- }
-
- IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
-
- private readonly IEnumerable m_elements;
- private readonly int m_size;
- private bool m_has_more;
- }
-}
\ No newline at end of file
diff --git a/src/Greenshot.Editor/Drawing/EmojiContainer.cs b/src/Greenshot.Editor/Drawing/Emoji/EmojiContainer.cs
similarity index 97%
rename from src/Greenshot.Editor/Drawing/EmojiContainer.cs
rename to src/Greenshot.Editor/Drawing/Emoji/EmojiContainer.cs
index 17b11cc8e..0e9eb0497 100644
--- a/src/Greenshot.Editor/Drawing/EmojiContainer.cs
+++ b/src/Greenshot.Editor/Drawing/Emoji/EmojiContainer.cs
@@ -30,13 +30,13 @@ using Greenshot.Editor.Controls;
using Greenshot.Editor.Helpers;
using Image = System.Drawing.Image;
-namespace Greenshot.Editor.Drawing
+namespace Greenshot.Editor.Drawing.Emoji
{
///
/// Description of EmojiContainer.
///
[Serializable]
- public class EmojiContainer : VectorGraphicsContainer, IEmojiContainer, IHaveScaleOptions
+ public sealed class EmojiContainer : VectorGraphicsContainer, IEmojiContainer, IHaveScaleOptions
{
[NonSerialized] private static EmojiContainer _currentContainer;
[NonSerialized] private static ElementHost _emojiPickerHost;
diff --git a/src/Greenshot.Editor/Drawing/Emoji/EmojiRenderer.cs b/src/Greenshot.Editor/Drawing/Emoji/EmojiRenderer.cs
new file mode 100644
index 000000000..c79442510
--- /dev/null
+++ b/src/Greenshot.Editor/Drawing/Emoji/EmojiRenderer.cs
@@ -0,0 +1,120 @@
+/*
+ * Greenshot - a free and open source screenshot tool
+ * Copyright (C) 2007-2021 Thomas Braun, Jens Klingen, Robin Krom, Francis Noel
+ *
+ * For more information see: https://getgreenshot.org/
+ * The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+using System;
+using System.IO;
+using System.Reflection;
+using SixLabors.Fonts;
+using SixLabors.ImageSharp;
+using SixLabors.ImageSharp.Drawing.Processing;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing;
+
+namespace Greenshot.Editor.Drawing.Emoji
+{
+ ///
+ /// This will render Emoji
+ ///
+ internal static class EmojiRenderer
+ {
+ private static readonly FontCollection _fontCollection = new FontCollection();
+
+ private static readonly Lazy _twemojiFontFamily = new Lazy(() =>
+ {
+ var exeDirectory = Path.GetDirectoryName(Assembly.GetCallingAssembly().Location);
+ var twemojiFontFile = Path.Combine(exeDirectory, "TwemojiMozilla.ttf");
+ if (!File.Exists(twemojiFontFile))
+ {
+ throw new FileNotFoundException($"Can't find {twemojiFontFile}, bad installation?");
+ }
+
+ using var fileStream = new FileStream(twemojiFontFile, FileMode.Open, FileAccess.Read);
+ _fontCollection.Add(fileStream);
+ _fontCollection.TryGet("Twemoji Mozilla", out var fontFamily);
+ return fontFamily;
+ });
+
+ public static Image GetImage(string emoji, int iconSize)
+ {
+ var image = new Image(iconSize, iconSize);
+
+ RenderEmoji(emoji, iconSize, image);
+ return image;
+ }
+
+ private static void RenderEmoji(string emoji, int iconSize, Image image)
+ {
+ var fontFamily = _twemojiFontFamily.Value;
+ var font = fontFamily.CreateFont(iconSize, FontStyle.Regular);
+ var verticalOffset = font.Size * 0.045f;
+ var textOptions = new TextOptions(font)
+ {
+ Origin = new PointF(font.Size / 2.0f, font.Size / 2.0f + verticalOffset),
+ HorizontalAlignment = HorizontalAlignment.Center,
+ VerticalAlignment = VerticalAlignment.Center
+ };
+
+ image.Mutate(x => x.DrawText(textOptions, emoji, Color.Black));
+ }
+
+ public static System.Drawing.Image GetBitmap(string emoji, int iconSize)
+ {
+ int width = iconSize;
+ int height = iconSize;
+
+ var bitmap = new System.Drawing.Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
+ var bitmapData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, width, height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
+
+ try
+ {
+ unsafe
+ {
+ var image = Image.WrapMemory((void*)bitmapData.Scan0, width: width, height: height);
+ RenderEmoji(emoji, iconSize, image);
+ }
+ }
+ finally
+ {
+ bitmap.UnlockBits(bitmapData);
+ }
+
+ return bitmap;
+ }
+
+ public static System.Windows.Media.Imaging.BitmapSource GetBitmapSource(string emoji, int iconSize)
+ {
+ var pixelFormat = System.Windows.Media.PixelFormats.Bgra32;
+ int width = iconSize;
+ int height = iconSize;
+ int stride = (width * pixelFormat.BitsPerPixel + 7) / 8;
+ byte[] pixels = new byte[stride * height];
+
+ var image = Image.WrapMemory(byteMemory: pixels, width: width, height: height);
+
+ RenderEmoji(emoji, iconSize, image);
+
+ var source = System.Windows.Media.Imaging.BitmapSource.Create(pixelWidth: width, pixelHeight: height, dpiX: 96, dpiY: 96, pixelFormat: pixelFormat, palette: null, pixels: pixels, stride: stride);
+ source.Freeze();
+
+ return source;
+ }
+ }
+}
diff --git a/src/Greenshot.Editor/Resources/TwemojiMozilla.ttf b/src/Greenshot.Editor/Drawing/Emoji/TwemojiMozilla.ttf
similarity index 100%
rename from src/Greenshot.Editor/Resources/TwemojiMozilla.ttf
rename to src/Greenshot.Editor/Drawing/Emoji/TwemojiMozilla.ttf
diff --git a/src/Greenshot.Editor/Drawing/EmojiRenderer.cs b/src/Greenshot.Editor/Drawing/EmojiRenderer.cs
deleted file mode 100644
index 37d827cb4..000000000
--- a/src/Greenshot.Editor/Drawing/EmojiRenderer.cs
+++ /dev/null
@@ -1,98 +0,0 @@
-using System;
-using System.Drawing;
-using System.Drawing.Imaging;
-using System.IO;
-using System.Reflection;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
-using SixLabors.Fonts;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Drawing.Processing;
-using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
-using FontFamily = SixLabors.Fonts.FontFamily;
-using FontStyle = SixLabors.Fonts.FontStyle;
-using Image = System.Drawing.Image;
-using PixelFormat = System.Drawing.Imaging.PixelFormat;
-using TextOptions = SixLabors.Fonts.TextOptions;
-
-namespace Greenshot.Editor.Drawing
-{
- internal static class EmojiRenderer
- {
- static FontCollection _fontCollection = new FontCollection();
-
- private static Lazy _twemoji = new Lazy(() =>
- {
- using var fileStream = new FileStream(Path.Combine(Path.GetDirectoryName(Assembly.GetCallingAssembly().Location), @"TwemojiMozilla.ttf"), FileMode.Open, FileAccess.Read);
- _fontCollection.Add(fileStream);
- _fontCollection.TryGet("Twemoji Mozilla", out var fontFamily);
- return fontFamily;
- });
-
- public static Image GetImage(string emoji, int iconSize)
- {
- var image = new Image(iconSize, iconSize);
-
- RenderEmoji(emoji, iconSize, image);
- return image;
- }
-
- private static void RenderEmoji(string emoji, int iconSize, SixLabors.ImageSharp.Image image)
- {
- var fontFamily = _twemoji.Value;
- var font = fontFamily.CreateFont(iconSize, FontStyle.Regular);
- var verticalOffset = font.Size * 0.045f;
- var textOptions = new TextOptions(font)
- {
- Origin = new SixLabors.ImageSharp.PointF(font.Size / 2.0f, font.Size / 2.0f + verticalOffset),
- HorizontalAlignment = HorizontalAlignment.Center,
- VerticalAlignment = VerticalAlignment.Center
- };
-
- image.Mutate(x => x.DrawText(textOptions, emoji, SixLabors.ImageSharp.Color.Black));
- }
-
- public static Image GetBitmap(string emoji, int iconSize)
- {
- int width = iconSize;
- int height = iconSize;
-
- var bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
- var bitmapData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
-
- try
- {
- unsafe
- {
- var image = SixLabors.ImageSharp.Image.WrapMemory((void*)bitmapData.Scan0, width: width, height: height);
- RenderEmoji(emoji, iconSize, image);
- }
- }
- finally
- {
- bitmap.UnlockBits(bitmapData);
- }
-
- return bitmap;
- }
-
- public static BitmapSource GetBitmapSource(string emoji, int iconSize)
- {
- var pixelFormat = PixelFormats.Bgra32;
- int width = iconSize;
- int height = iconSize;
- int stride = (width * pixelFormat.BitsPerPixel + 7) / 8;
- byte[] pixels = new byte[stride * height];
-
- var image = SixLabors.ImageSharp.Image.WrapMemory(byteMemory: pixels, width: width, height: height);
-
- RenderEmoji(emoji, iconSize, image);
-
- var source = BitmapSource.Create(pixelWidth: width, pixelHeight: height, dpiX: 96, dpiY: 96, pixelFormat: pixelFormat, palette: null, pixels: pixels, stride: stride);
- source.Freeze();
-
- return source;
- }
- }
-}
diff --git a/src/Greenshot.Editor/Drawing/Surface.cs b/src/Greenshot.Editor/Drawing/Surface.cs
index efb3ebdd8..ccf979661 100644
--- a/src/Greenshot.Editor/Drawing/Surface.cs
+++ b/src/Greenshot.Editor/Drawing/Surface.cs
@@ -39,6 +39,7 @@ using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Base.Interfaces.Drawing.Adorners;
using Greenshot.Editor.Configuration;
+using Greenshot.Editor.Drawing.Emoji;
using Greenshot.Editor.Drawing.Fields;
using Greenshot.Editor.Memento;
using log4net;
diff --git a/src/Greenshot.Editor/Forms/ImageEditorForm.Designer.cs b/src/Greenshot.Editor/Forms/ImageEditorForm.Designer.cs
index 34e7c55fc..2017a975c 100644
--- a/src/Greenshot.Editor/Forms/ImageEditorForm.Designer.cs
+++ b/src/Greenshot.Editor/Forms/ImageEditorForm.Designer.cs
@@ -22,9 +22,11 @@
using Greenshot.Base.Controls;
using Greenshot.Editor.Controls;
using Greenshot.Editor.Drawing;
+using Greenshot.Editor.Drawing.Emoji;
-namespace Greenshot.Editor.Forms {
- partial class ImageEditorForm {
+namespace Greenshot.Editor.Forms
+{
+ partial class ImageEditorForm {
///
/// Designer variable used to keep track of non-visual components.
///
diff --git a/src/Greenshot.Editor/Forms/ImageEditorForm.cs b/src/Greenshot.Editor/Forms/ImageEditorForm.cs
index 4da3ec385..c4f368da5 100644
--- a/src/Greenshot.Editor/Forms/ImageEditorForm.cs
+++ b/src/Greenshot.Editor/Forms/ImageEditorForm.cs
@@ -45,7 +45,7 @@ using Greenshot.Base.Interfaces;
using Greenshot.Base.Interfaces.Drawing;
using Greenshot.Base.Interfaces.Forms;
using Greenshot.Editor.Configuration;
-using Greenshot.Editor.Controls;
+using Greenshot.Editor.Controls.Emoji;
using Greenshot.Editor.Destinations;
using Greenshot.Editor.Drawing;
using Greenshot.Editor.Drawing.Fields;
diff --git a/src/Greenshot.Editor/Greenshot.Editor.csproj b/src/Greenshot.Editor/Greenshot.Editor.csproj
index a343853f4..6a3069383 100644
--- a/src/Greenshot.Editor/Greenshot.Editor.csproj
+++ b/src/Greenshot.Editor/Greenshot.Editor.csproj
@@ -89,11 +89,13 @@
-
+
PreserveNewest
-
-
+ emoji-test.txt
+
+
PreserveNewest
-
+ TwemojiMozilla.ttf
+
\ No newline at end of file