OSDN Git Service

絵文字からimg要素に変換する処理をEmojiFormatterからTweetFormatterに移行
authorKimura Youichi <kim.upsilon@bucyou.net>
Sat, 13 Oct 2018 14:11:16 +0000 (23:11 +0900)
committerKimura Youichi <kim.upsilon@bucyou.net>
Sat, 13 Oct 2018 15:47:58 +0000 (00:47 +0900)
OpenTween.Tests/EmojiFormatterTest.cs [deleted file]
OpenTween.Tests/TweetFormatterTest.cs
OpenTween/EmojiFormatter.cs [deleted file]
OpenTween/OpenTween.csproj
OpenTween/Tween.cs
OpenTween/TweetFormatter.cs
OpenTween/Twitter.cs
OpenTween/UserInfoDialog.cs

diff --git a/OpenTween.Tests/EmojiFormatterTest.cs b/OpenTween.Tests/EmojiFormatterTest.cs
deleted file mode 100644 (file)
index d2c80c2..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-// OpenTween - Client of Twitter
-// Copyright (c) 2015 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
-// All rights reserved.
-//
-// This file is part of OpenTween.
-//
-// 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 3 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 <http://www.gnu.org/licenses/>, or write to
-// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
-// Boston, MA 02110-1301, USA.
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Xunit;
-
-namespace OpenTween
-{
-    public class EmojiFormatterTest
-    {
-        [Fact]
-        public void ReplaceEmojiToImg_Test()
-        {
-            var origText = "©"; // U+00A9
-
-            var result = EmojiFormatter.ReplaceEmojiToImg(origText);
-            var expected = "<img class=\"emoji\" src=\"https://twemoji.maxcdn.com/2/72x72/a9.png\" alt=\"©\" />";
-
-            Assert.Equal(expected, result);
-        }
-
-        [Fact]
-        public void ReplaceEmojiToImg_VariationSelector_TextStyleTest()
-        {
-            // 異字体セレクタを使用して明示的にテキストスタイルで表示させている文字
-            var origText = "©\uFE0E"; // U+00A9 + U+FE0E (text style)
-
-            var result = EmojiFormatter.ReplaceEmojiToImg(origText);
-            var expected = "©\uFE0E";
-
-            Assert.Equal(expected, result);
-        }
-
-        [Fact]
-        public void ReplaceEmojiToImg_VariationSelector_EmojiStyleTest()
-        {
-            // 異字体セレクタを使用して明示的に絵文字スタイルで表示させている文字
-            var origText = "©\uFE0F"; // U+00A9 + U+FE0F (emoji style)
-
-            var result = EmojiFormatter.ReplaceEmojiToImg(origText);
-            var expected = "<img class=\"emoji\" src=\"https://twemoji.maxcdn.com/2/72x72/a9.png\" alt=\"©\" />";
-
-            Assert.Equal(expected, result);
-        }
-
-        [Fact]
-        public void ReplaceEmojiToImg_SurrogatePairTest()
-        {
-            var origText = "🍣"; // U+1F363
-
-            var result = EmojiFormatter.ReplaceEmojiToImg(origText);
-            var expected = "<img class=\"emoji\" src=\"https://twemoji.maxcdn.com/2/72x72/1f363.png\" alt=\"🍣\" />";
-
-            Assert.Equal(expected, result);
-        }
-
-        [Fact]
-        public void ReplaceEmojiToImg_CombiningCharacterTest()
-        {
-            var origText = "#⃣"; // U+0023 U+20E3 (合字)
-
-            var result = EmojiFormatter.ReplaceEmojiToImg(origText);
-            var expected = "<img class=\"emoji\" src=\"https://twemoji.maxcdn.com/2/72x72/23-20e3.png\" alt=\"#⃣\" />";
-
-            Assert.Equal(expected, result);
-        }
-
-        [Fact]
-        public void ReplaceEmojiToImg_Unicode8Test()
-        {
-            // Unicode 8.0 で追加された絵文字
-            var origText = "🌭"; // U+1F32D (HOT DOG)
-
-            var result = EmojiFormatter.ReplaceEmojiToImg(origText);
-            var expected = "<img class=\"emoji\" src=\"https://twemoji.maxcdn.com/2/72x72/1f32d.png\" alt=\"🌭\" />";
-
-            Assert.Equal(expected, result);
-        }
-
-        [Fact]
-        public void ReplaceEmojiToImg_Emoji50Test()
-        {
-            // Unicode 10.0/Emoji 5.0 で追加された絵文字
-            var origText = "🦒"; // U+1F992 (GIRAFFE)
-
-            var result = EmojiFormatter.ReplaceEmojiToImg(origText);
-            var expected = "<img class=\"emoji\" src=\"https://twemoji.maxcdn.com/2/72x72/1f992.png\" alt=\"🦒\" />";
-
-            Assert.Equal(expected, result);
-        }
-
-        [Fact]
-        public void ReplaceEmojiToImg_EmojiModifiers_CombiningTest()
-        {
-            // Emoji modifiers を使用した合字 (リガチャー)
-            var origText = "👦\U0001F3FF"; // U+1F466 (BOY) + U+1F3FF (EMOJI MODIFIER FITZPATRICK TYPE-6)
-
-            var result = EmojiFormatter.ReplaceEmojiToImg(origText);
-            var expected = "<img class=\"emoji\" src=\"https://twemoji.maxcdn.com/2/72x72/1f466-1f3ff.png\" alt=\"👦\U0001F3FF\" />";
-
-            Assert.Equal(expected, result);
-        }
-
-        [Fact]
-        public void ReplaceEmojiToImg_EmojiModifiers_SingleTest()
-        {
-            // Emoji modifiers は単体でも絵文字として表示される
-            var origText = "\U0001F3FF"; // U+1F3FB (EMOJI MODIFIER FITZPATRICK TYPE-6)
-
-            var result = EmojiFormatter.ReplaceEmojiToImg(origText);
-            var expected = "<img class=\"emoji\" src=\"https://twemoji.maxcdn.com/2/72x72/1f3ff.png\" alt=\"\U0001F3FF\" />";
-
-            Assert.Equal(expected, result);
-        }
-
-        [Fact]
-        public void ReplaceEmojiToImg_EmojiZWJSequenceTest()
-        {
-            // 複数の絵文字を U+200D (ZERO WIDTH JOINER) で繋げて表現する絵文字
-            var origText = "👨\u200D🎨"; // U+1F468 (MAN) + U+200D + U+1F3A8 (ARTIST PALETTE)
-
-            var result = EmojiFormatter.ReplaceEmojiToImg(origText);
-            var expected = "<img class=\"emoji\" src=\"https://twemoji.maxcdn.com/2/72x72/1f468-200d-1f3a8.png\" alt=\"👨\u200D🎨\" />";
-
-            Assert.Equal(expected, result);
-        }
-
-        [Fact]
-        public void ReplaceEmojiToImg_EmojiZWJSequenceWithVariationSelectorTest()
-        {
-            // 複数の絵文字を U+200D (ZERO WIDTH JOINER) で繋げて表現 + 異字体セレクタ U+FE0F を含む絵文字
-            // この場合は URL 生成時に異字体セレクタ U+FE0F を除去しない
-            var origText = "🏃\u200D♀\uFE0F"; // U+1F3C3 (RUNNER) + U+200D + U+2640 (FEMARE SIGN) + U+FE0F
-
-            var result = EmojiFormatter.ReplaceEmojiToImg(origText);
-            var expected = "<img class=\"emoji\" src=\"https://twemoji.maxcdn.com/2/72x72/1f3c3-200d-2640-fe0f.png\" alt=\"🏃\u200D♀\uFE0F\" />";
-
-            Assert.Equal(expected, result);
-        }
-
-        [Fact]
-        public void ReplaceEmojiToImg_NotEmojiTest()
-        {
-            var origText = "123ABC";
-
-            var result = EmojiFormatter.ReplaceEmojiToImg(origText);
-            var expected = "123ABC";
-
-            Assert.Equal(expected, result);
-        }
-
-        [Fact]
-        public void ReplaceEmojiToImg_HtmlTest()
-        {
-            // 属性内の絵文字は変換しない
-            var origText = "🐟<a href='http://xn--7c9bw4k.jp/' title='🍣.jp'>🍣.jp</a>🐡";
-
-            var result = EmojiFormatter.ReplaceEmojiToImg(origText);
-            var expected = "<img class=\"emoji\" src=\"https://twemoji.maxcdn.com/2/72x72/1f41f.png\" alt=\"🐟\" />" +
-                "<a href='http://xn--7c9bw4k.jp/' title='🍣.jp'>" +
-                "<img class=\"emoji\" src=\"https://twemoji.maxcdn.com/2/72x72/1f363.png\" alt=\"🍣\" />.jp" +
-                "</a>" +
-                "<img class=\"emoji\" src=\"https://twemoji.maxcdn.com/2/72x72/1f421.png\" alt=\"🐡\" />";
-
-            Assert.Equal(expected, result);
-        }
-    }
-}
index f74f836..2b9d3a3 100644 (file)
@@ -165,6 +165,24 @@ namespace OpenTween
             Assert.Equal(expected, TweetFormatter.AutoLinkHtml(text, entities));
         }
 
+
+        [Fact]
+        public void FormatEmojiEntity_Test()
+        {
+            var text = "🍣";
+            var entities = new[]
+            {
+                new TwitterEntityEmoji
+                {
+                    Indices = new[] { 0, 1 },
+                    Text = "🍣",
+                    Url = "https://twemoji.maxcdn.com/2/72x72/1f363.png",
+                },
+            };
+
+            var expected = "<img class=\"emoji\" src=\"https://twemoji.maxcdn.com/2/72x72/1f363.png\" alt=\"🍣\" />";
+            Assert.Equal(expected, TweetFormatter.AutoLinkHtml(text, entities));
+        }
         [Fact]
         public void AutoLinkHtml_EntityNullTest()
         {
diff --git a/OpenTween/EmojiFormatter.cs b/OpenTween/EmojiFormatter.cs
deleted file mode 100644 (file)
index 7724b73..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-// OpenTween - Client of Twitter
-// Copyright (c) 2015 takke (@takke) <http://takke.jp/>
-// All rights reserved.
-//
-// This file is part of OpenTween.
-//
-// 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 3 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 <http://www.gnu.org/licenses/>, or write to
-// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
-// Boston, MA 02110-1301, USA.
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Threading.Tasks;
-
-namespace OpenTween
-{
-    /// <summary>
-    /// ツイート内の絵文字を img タグに書き換えるクラス
-    /// </summary>
-    public static class EmojiFormatter
-    {
-        public static string ReplaceEmojiToImg(string text)
-        {
-            // HTMLタグを壊さないように <...> の外側のみを置換する
-            return Regex.Replace(text, @"^[^<]+$|^[^<]+(?:<)|(?:>)[^<]+(?:<)|(?:>)[^<]+$", m =>
-            {
-                return TweetExtractor.EmojiPattern.Replace(m.Value, ReplaceEmojiEntity);
-            });
-        }
-
-        private static string ReplaceEmojiEntity(System.Text.RegularExpressions.Match m)
-        {
-            string input = m.Value;
-            string codes = "";
-
-            // 異字体セレクタ U+FE0F (emoji style) は除去する (ZWJ を含まない場合のみ)
-            if (!input.Contains('\u200D'))
-                input = input.Replace("\uFE0F", "");
-
-            for (var i = 0; i < input.Length; i += char.IsSurrogatePair(input, i) ? 2 : 1)
-            {
-                var codepoint = char.ConvertToUtf32(input, i);
-
-                if (i > 0)
-                {
-                    codes += "-";
-                }
-
-                codes += string.Format("{0:x}", codepoint);
-            }
-
-            return "<img class=\"emoji\" src=\"https://twemoji.maxcdn.com/2/72x72/" + codes + ".png\" alt=\"" + input + "\" />";
-        }
-    }
-}
index 13a0044..a02dc7e 100644 (file)
     <Compile Include="Connection\OAuthUtility.cs" />
     <Compile Include="Connection\TwitterApiConnection.cs" />
     <Compile Include="DateTimeUtc.cs" />
-    <Compile Include="EmojiFormatter.cs" />
     <Compile Include="EventViewerDialog.cs">
       <SubType>Form</SubType>
     </Compile>
index 8478e08..9dbcbcb 100644 (file)
@@ -5635,12 +5635,7 @@ namespace OpenTween
         }
 
         public string createDetailHtml(string orgdata)
-        {
-            if (SettingManager.Local.UseTwemoji)
-                orgdata = EmojiFormatter.ReplaceEmojiToImg(orgdata);
-
-            return detailHtmlFormatHeader + orgdata + detailHtmlFormatFooter;
-        }
+            => detailHtmlFormatHeader + orgdata + detailHtmlFormatFooter;
 
         private Task DispSelectedPost()
             => this.DispSelectedPost(false);
index fc6f28a..733141d 100644 (file)
@@ -26,6 +26,7 @@ using System.Linq;
 using System.Text;
 using System.Text.RegularExpressions;
 using OpenTween.Api.DataModel;
+using OpenTween.Setting;
 
 namespace OpenTween
 {
@@ -75,6 +76,8 @@ namespace OpenTween
                     yield return FormatHashtagEntity(targetText, hashtagEntity);
                 else if (entity is TwitterEntityMention mentionEntity)
                     yield return FormatMentionEntity(targetText, mentionEntity);
+                else if (entity is TwitterEntityEmoji emojiEntity)
+                    yield return FormatEmojiEntity(targetText, emojiEntity);
                 else
                     yield return t(e(targetText));
 
@@ -158,6 +161,14 @@ namespace OpenTween
         private static string FormatMentionEntity(string targetText, TwitterEntityMention entity)
             => "<a class=\"mention\" href=\"https://twitter.com/" + eu(entity.ScreenName) + "\">" + t(e(targetText)) + "</a>";
 
+        private static string FormatEmojiEntity(string targetText, TwitterEntityEmoji entity)
+        {
+            if (!SettingManager.Local.UseTwemoji)
+                return t(e(targetText));
+
+            return "<img class=\"emoji\" src=\"" + e(entity.Url) + "\" alt=\"" + e(entity.Text) + "\" />";
+        }
+
         // 長いのでエイリアスとして e(...), eu(...), t(...) でエスケープできるようにする
         private static Func<string, string> e = EscapeHtml;
         private static Func<string, string> eu = Uri.EscapeDataString;
index fced075..dd75edb 100644 (file)
@@ -1633,8 +1633,10 @@ namespace OpenTween
 
         internal static string CreateHtmlAnchor(string text, TwitterEntities entities, TwitterQuotedStatusPermalink quotedStatusLink)
         {
+            var mergedEntities = entities.Concat(TweetExtractor.ExtractEmojiEntities(text));
+
             // PostClass.ExpandedUrlInfo を使用して非同期に URL 展開を行うためここでは expanded_url を使用しない
-            text = TweetFormatter.AutoLinkHtml(text, entities, keepTco: true);
+            text = TweetFormatter.AutoLinkHtml(text, mergedEntities, keepTco: true);
 
             text = Regex.Replace(text, "(^|[^a-zA-Z0-9_/&##@@>=.~])(sm|nm)([0-9]{1,10})", "$1<a href=\"http://www.nicovideo.jp/watch/$2$3\">$2$3</a>");
             text = PreProcessUrl(text); //IDN置換
index e474395..7903c48 100644 (file)
@@ -168,10 +168,12 @@ namespace OpenTween
                     entity.ExpandedUrl = await ShortUrl.Instance.ExpandUrlAsync(entity.ExpandedUrl);
 
                 // user.entities には urls 以外のエンティティが含まれていないため、テキストをもとに生成する
-                entities.Hashtags = TweetExtractor.ExtractHashtagEntities(descriptionText).ToArray();
-                entities.UserMentions = TweetExtractor.ExtractMentionEntities(descriptionText).ToArray();
+                var mergedEntities = entities.Urls.AsEnumerable<TwitterEntity>()
+                    .Concat(TweetExtractor.ExtractHashtagEntities(descriptionText))
+                    .Concat(TweetExtractor.ExtractMentionEntities(descriptionText))
+                    .Concat(TweetExtractor.ExtractEmojiEntities(descriptionText));
 
-                var html = TweetFormatter.AutoLinkHtml(descriptionText, entities);
+                var html = TweetFormatter.AutoLinkHtml(descriptionText, mergedEntities);
                 html = this.mainForm.createDetailHtml(html);
 
                 if (cancellationToken.IsCancellationRequested)
@@ -247,7 +249,9 @@ namespace OpenTween
                 foreach (var entity in entities.Urls)
                     entity.ExpandedUrl = await ShortUrl.Instance.ExpandUrlAsync(entity.ExpandedUrl);
 
-                var html = TweetFormatter.AutoLinkHtml(status.FullText, entities);
+                var mergedEntities = entities.Concat(TweetExtractor.ExtractEmojiEntities(status.FullText));
+
+                var html = TweetFormatter.AutoLinkHtml(status.FullText, mergedEntities);
                 html = this.mainForm.createDetailHtml(html +
                     " Posted at " + MyCommon.DateTimeParse(status.CreatedAt).ToLocalTimeString() +
                     " via " + status.Source);