mirror of
https://github.com/greenshot/greenshot
synced 2025-08-22 06:23:24 -07:00
Added gz support, and improved upon some additional code.
This commit is contained in:
parent
2f556b44d1
commit
84be12b826
7 changed files with 148 additions and 183 deletions
|
@ -10,4 +10,22 @@
|
|||
<Message Text="Processing: $(ProjectDir)$(ProjectName).Credentials.template" Importance="high"/>
|
||||
<TemplateFile Template="$(ProjectDir)$(ProjectName).Credentials.template" OutputFilename="$(ProjectDir)$(ProjectName).Credentials.cs" Tokens="@(Tokens)" />
|
||||
</Target>
|
||||
|
||||
<UsingTask TaskName="GZipTask" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
|
||||
<ParameterGroup>
|
||||
<InputFile ParameterType="System.String" Required="true" />
|
||||
<OutputFile ParameterType="System.String" Required="true" />
|
||||
</ParameterGroup>
|
||||
<Task>
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Using Namespace="System.IO.Compression" />
|
||||
<Code Type="Fragment" Language="cs"><![CDATA[
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(OutputFile));
|
||||
var data = File.ReadAllBytes(InputFile);
|
||||
using (var s = new FileStream(OutputFile, FileMode.Create))
|
||||
using (var gs = new GZipStream(s, CompressionMode.Compress, false))
|
||||
gs.Write(data, 0, data.Length);
|
||||
]]></Code>
|
||||
</Task>
|
||||
</UsingTask>
|
||||
</Project>
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Greenshot.Editor.Controls.Emoji;
|
||||
|
||||
/// <summary>
|
||||
/// This seems to enumerate multiple IEnumerables.
|
||||
/// TODO: Replace this with LINQ
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
internal sealed class ChunkHelper<T> : IEnumerable<IEnumerable<T>>
|
||||
{
|
||||
private readonly IEnumerable<T> _elements;
|
||||
private readonly int _size;
|
||||
private bool _hasMore;
|
||||
|
||||
public ChunkHelper(IEnumerable<T> elements, int size)
|
||||
{
|
||||
_elements = elements;
|
||||
_size = size;
|
||||
}
|
||||
|
||||
public IEnumerator<IEnumerable<T>> GetEnumerator()
|
||||
{
|
||||
using var enumerator = _elements.GetEnumerator();
|
||||
_hasMore = enumerator.MoveNext();
|
||||
while (_hasMore)
|
||||
{
|
||||
yield return GetNextBatch(enumerator).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<T> GetNextBatch(IEnumerator<T> enumerator)
|
||||
{
|
||||
for (int i = 0; i < _size; ++i)
|
||||
{
|
||||
yield return enumerator.Current;
|
||||
_hasMore = enumerator.MoveNext();
|
||||
if (!_hasMore)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
|
@ -11,15 +11,18 @@
|
|||
//
|
||||
|
||||
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;
|
||||
#if DEBUG
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using SixLabors.Fonts.Unicode;
|
||||
#endif
|
||||
|
||||
namespace Greenshot.Editor.Controls.Emoji
|
||||
{
|
||||
|
@ -28,27 +31,17 @@ namespace Greenshot.Editor.Controls.Emoji
|
|||
/// </summary>
|
||||
public static class EmojiData
|
||||
{
|
||||
private const string FilePath = "emojis.xml";
|
||||
private const string EmojisXmlFilePath = "emojis.xml";
|
||||
private const string EmojisTestFile = @"emoji-test.txt.gz";
|
||||
#if DEBUG
|
||||
private const string Adult = "(👨|👩)(🏻|🏼|🏽|🏾|🏿)?";
|
||||
private const string Child = "(👦|👧|👶)(🏻|🏼|🏽|🏾|🏿)?";
|
||||
private static readonly Regex MatchFamily = new($"{Adult}(\u200d{Adult})*(\u200d{Child})+");
|
||||
|
||||
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<string> SkinToneComponents = new List<string>
|
||||
private static readonly Regex MatchGroup = new(@"^# group: (.*)", RegexOptions.Compiled);
|
||||
private static readonly Regex MatchSubgroup = new(@"^# subgroup: (.*)", RegexOptions.Compiled);
|
||||
private static readonly Regex MatchSequence = new(@"^([0-9a-fA-F ]+[0-9a-fA-F]).*; *([-a-z]*) *# [^ ]* (E[0-9.]* )?(.*)", RegexOptions.Compiled);
|
||||
private static readonly List<string> SkinToneComponents = new()
|
||||
{
|
||||
"🏻", // light skin tone
|
||||
"🏼", // medium-light skin tone
|
||||
|
@ -57,7 +50,7 @@ namespace Greenshot.Editor.Controls.Emoji
|
|||
"🏿", // dark skin tone
|
||||
};
|
||||
|
||||
private static readonly List<string> HairStyleComponents = new List<string>
|
||||
private static readonly List<string> HairStyleComponents = new()
|
||||
{
|
||||
"🦰", // red hair
|
||||
"🦱", // curly hair
|
||||
|
@ -65,21 +58,43 @@ namespace Greenshot.Editor.Controls.Emoji
|
|||
"🦲", // bald
|
||||
};
|
||||
|
||||
private static string ToColonSyntax(string s)
|
||||
=> Regex.Replace(s.Trim().ToLowerInvariant(), "[^a-z0-9]+", "-");
|
||||
private static readonly Regex MatchSkinTone = new($"({string.Join("|", SkinToneComponents)})", RegexOptions.Compiled);
|
||||
private static readonly Regex MatchHairStyle = new($"({string.Join("|", HairStyleComponents)})", RegexOptions.Compiled);
|
||||
|
||||
#endif
|
||||
|
||||
public static Emojis Data { get; private set; } = new();
|
||||
|
||||
public static void Load()
|
||||
{
|
||||
var x = new XmlSerializer(typeof(Emojis));
|
||||
|
||||
if (File.Exists(EmojisXmlFilePath))
|
||||
{
|
||||
Data = (Emojis)x.Deserialize(new XmlTextReader(EmojisXmlFilePath));
|
||||
}
|
||||
#if RELEASE
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException($"Missing {EmojisXmlFilePath}, can't load ");
|
||||
}
|
||||
#elif DEBUG
|
||||
else
|
||||
{
|
||||
// To be removed
|
||||
ParseEmojiList();
|
||||
x.Serialize(new XmlTextWriter(EmojisXmlFilePath, Encoding.UTF8), Data);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if DEBUG
|
||||
private static string ToColonSyntax(string s) => Regex.Replace(s.Trim().ToLowerInvariant(), "[^a-z0-9]+", "-");
|
||||
|
||||
private static void ParseEmojiList()
|
||||
{
|
||||
var lookupByName = new Dictionary<string, Emojis.Emoji>();
|
||||
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<string, string>();
|
||||
var allText = new List<string>();
|
||||
|
@ -89,7 +104,7 @@ namespace Greenshot.Editor.Controls.Emoji
|
|||
|
||||
foreach (var line in EmojiDescriptionLines())
|
||||
{
|
||||
var m = matchGroup.Match(line);
|
||||
var m = MatchGroup.Match(line);
|
||||
if (m.Success)
|
||||
{
|
||||
currentGroup = new Emojis.Group { Name = m.Groups[1].ToString() };
|
||||
|
@ -97,51 +112,49 @@ namespace Greenshot.Editor.Controls.Emoji
|
|||
continue;
|
||||
}
|
||||
|
||||
m = matchSubgroup.Match(line);
|
||||
m = MatchSubgroup.Match(line);
|
||||
if (m.Success)
|
||||
{
|
||||
currentSubgroup = new Emojis.Group { Name = m.Groups[1].ToString() };
|
||||
currentGroup.SubGroups.Add(currentSubgroup);
|
||||
currentGroup?.SubGroups?.Add(currentSubgroup);
|
||||
continue;
|
||||
}
|
||||
|
||||
m = matchSequence.Match(line);
|
||||
if (m.Success)
|
||||
m = MatchSequence.Match(line);
|
||||
if (!m.Success)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
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;
|
||||
string text = string.Join("", sequence.Split(' ').Select(c => char.ConvertFromUtf32(Convert.ToInt32(c, 16))));
|
||||
bool hasModifier = 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
|
||||
if (!MatchFamily.Match(text).Success)
|
||||
{
|
||||
// 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) =>
|
||||
var regexText = MatchSkinTone.Replace(
|
||||
MatchHairStyle.Replace(text, (x) =>
|
||||
{
|
||||
has_modifier = true;
|
||||
hasModifier = true;
|
||||
hasNonfirstModifier |= x.Value != HairStyleComponents[0];
|
||||
return matchHairStyle.ToString();
|
||||
return MatchHairStyle.ToString();
|
||||
}), (x) =>
|
||||
{
|
||||
has_modifier = true;
|
||||
hasModifier = true;
|
||||
hasNonfirstModifier |= x.Value != SkinToneComponents[0];
|
||||
return matchSkinTone.ToString();
|
||||
return MatchSkinTone.ToString();
|
||||
});
|
||||
|
||||
if (!hasNonfirstModifier)
|
||||
{
|
||||
allText.Add(has_modifier ? regexText : text);
|
||||
allText.Add(hasModifier ? regexText : text);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,21 +174,20 @@ namespace Greenshot.Editor.Controls.Emoji
|
|||
|
||||
qualifiedLut[unqualified] = text;
|
||||
|
||||
var emoji = new Emojis.Emoji { Name = name, Text = 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))
|
||||
if (name.Contains(":") && lookupByName.TryGetValue(ToColonSyntax(name.Split(':')[0]), out var parentEmoji))
|
||||
{
|
||||
parent_emoji.Variations.Add(emoji);
|
||||
parentEmoji.Variations.Add(emoji);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentSubgroup.Emojis.Add(emoji);
|
||||
}
|
||||
currentSubgroup?.Emojis?.Add(emoji);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -186,14 +198,16 @@ namespace Greenshot.Editor.Controls.Emoji
|
|||
private static IEnumerable<string> EmojiDescriptionLines()
|
||||
{
|
||||
var exeDirectory = Path.GetDirectoryName(Assembly.GetCallingAssembly().Location);
|
||||
var emojiTestFile = Path.Combine(exeDirectory, @"emoji-test.txt");
|
||||
var emojiTestFile = Path.Combine(exeDirectory, EmojisTestFile);
|
||||
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 gzStream = new GZipStream(fileStream, CompressionMode.Decompress);
|
||||
using var streamReader = new StreamReader(fileStream);
|
||||
return streamReader.ReadToEnd().Split('\r', '\n');
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -42,14 +42,7 @@ public class Emojis
|
|||
[XmlElement(ElementName = "Emoji")]
|
||||
public List<Emoji> Emojis { get; set; } = new();
|
||||
|
||||
public IEnumerable<IEnumerable<Emoji>> EmojiChunkList => new ChunkHelper<Emoji>(EmojiList, 8);
|
||||
|
||||
public string Icon => SubGroups.FirstOrDefault()?.Emojis.FirstOrDefault()?.Text;
|
||||
|
||||
public IEnumerable<Emoji> EmojiList
|
||||
=> from s in SubGroups
|
||||
from e in s.Emojis
|
||||
select e;
|
||||
}
|
||||
|
||||
public class Emoji
|
||||
|
|
|
@ -153,7 +153,10 @@ namespace Greenshot.Editor.Drawing.Emoji
|
|||
protected override Image ComputeBitmap()
|
||||
{
|
||||
var iconSize = Math.Min(Bounds.Width, Bounds.Height);
|
||||
if (iconSize <= 0) return null;
|
||||
if (iconSize <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var image = EmojiRenderer.GetBitmap(Emoji, iconSize);
|
||||
if (RotationAngle != 0)
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Reflection;
|
||||
using SixLabors.Fonts;
|
||||
using SixLabors.ImageSharp;
|
||||
|
@ -35,9 +36,9 @@ namespace Greenshot.Editor.Drawing.Emoji
|
|||
/// </summary>
|
||||
internal static class EmojiRenderer
|
||||
{
|
||||
private static readonly FontCollection _fontCollection = new FontCollection();
|
||||
private static readonly FontCollection TwemojiFontCollection = new();
|
||||
|
||||
private static readonly Lazy<FontFamily> _twemojiFontFamily = new Lazy<FontFamily>(() =>
|
||||
private static readonly Lazy<FontFamily> TwemojiFontFamily = new(() =>
|
||||
{
|
||||
var exeDirectory = Path.GetDirectoryName(Assembly.GetCallingAssembly().Location);
|
||||
var twemojiFontFile = Path.Combine(exeDirectory, "TwemojiMozilla.ttf");
|
||||
|
@ -47,8 +48,8 @@ namespace Greenshot.Editor.Drawing.Emoji
|
|||
}
|
||||
|
||||
using var fileStream = new FileStream(twemojiFontFile, FileMode.Open, FileAccess.Read);
|
||||
_fontCollection.Add(fileStream);
|
||||
_fontCollection.TryGet("Twemoji Mozilla", out var fontFamily);
|
||||
TwemojiFontCollection.Add(fileStream);
|
||||
TwemojiFontCollection.TryGet("Twemoji Mozilla", out var fontFamily);
|
||||
return fontFamily;
|
||||
});
|
||||
|
||||
|
@ -62,7 +63,7 @@ namespace Greenshot.Editor.Drawing.Emoji
|
|||
|
||||
private static void RenderEmoji(string emoji, int iconSize, Image image)
|
||||
{
|
||||
var fontFamily = _twemojiFontFamily.Value;
|
||||
var fontFamily = TwemojiFontFamily.Value;
|
||||
var font = fontFamily.CreateFont(iconSize, FontStyle.Regular);
|
||||
var verticalOffset = font.Size * 0.045f;
|
||||
var textOptions = new TextOptions(font)
|
||||
|
|
|
@ -15,6 +15,11 @@
|
|||
<ProjectReference Include="..\Greenshot.Base\Greenshot.Base.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CompressEmojiTxtResource" BeforeTargets="BeforeBuild" Condition="!Exists('Controls\Emoji\emoji-test.txt.gz')">
|
||||
<GZipTask InputFile="Controls\Emoji\emoji-test.txt"
|
||||
OutputFile="Controls\Emoji\emoji-test.txt.gz" />
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Controls\BindableToolStripButton.cs">
|
||||
<SubType>Component</SubType>
|
||||
|
@ -89,9 +94,9 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ContentWithTargetPath Include="Controls\Emoji\emoji-test.txt">
|
||||
<ContentWithTargetPath Include="Controls\Emoji\emoji-test.txt.gz">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<TargetPath>emoji-test.txt</TargetPath>
|
||||
<TargetPath>emoji-test.txt.gz</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
<ContentWithTargetPath Include="Drawing\Emoji\TwemojiMozilla.ttf">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue