FormattedTextImplTests.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. using Avalonia.Media;
  2. using Avalonia.Platform;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Globalization;
  6. using System.Linq;
  7. using System.Text;
  8. using Xunit;
  9. #if AVALONIA_SKIA
  10. namespace Avalonia.Skia.RenderTests
  11. #else
  12. using Avalonia.Direct2D1.RenderTests;
  13. namespace Avalonia.Direct2D1.RenderTests.Media
  14. #endif
  15. {
  16. public class FormattedTextImplTests : TestBase
  17. {
  18. private const string FontName = "Courier New";
  19. private const double FontSize = 12;
  20. private const double MediumFontSize = 18;
  21. private const double BigFontSize = 32;
  22. private const double FontSizeHeight = 13.594;//real value 13.59375
  23. private const string stringword = "word";
  24. private const string stringmiddle = "The quick brown fox jumps over the lazy dog";
  25. private const string stringmiddle2lines = "The quick brown fox\njumps over the lazy dog";
  26. private const string stringmiddle3lines = "01234567\n\n0123456789";
  27. private const string stringmiddlenewlines = "012345678\r 1234567\r\n 12345678\n0123456789";
  28. private const string stringlong =
  29. "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus magna. Cras in mi at felis " +
  30. "aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. Donec eleifend, libero" +
  31. " at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. Vivamus " +
  32. "pretium ornare est.";
  33. public FormattedTextImplTests()
  34. : base(@"Media\FormattedText")
  35. {
  36. }
  37. private IFormattedTextImpl Create(string text,
  38. string fontFamily,
  39. double fontSize,
  40. FontStyle fontStyle,
  41. TextAlignment textAlignment,
  42. FontWeight fontWeight,
  43. TextWrapping wrapping,
  44. double widthConstraint)
  45. {
  46. var r = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
  47. return r.CreateFormattedText(text,
  48. FontManager.Current.GetOrAddTypeface(fontFamily, fontStyle, fontWeight),
  49. fontSize,
  50. textAlignment,
  51. wrapping,
  52. widthConstraint == -1 ? Size.Infinity : new Size(widthConstraint, double.PositiveInfinity),
  53. null);
  54. }
  55. private IFormattedTextImpl Create(string text, double fontSize)
  56. {
  57. return Create(text, FontName, fontSize,
  58. FontStyle.Normal, TextAlignment.Left,
  59. FontWeight.Normal, TextWrapping.NoWrap,
  60. -1);
  61. }
  62. private IFormattedTextImpl Create(string text, double fontSize, TextAlignment alignment, double widthConstraint)
  63. {
  64. return Create(text, FontName, fontSize,
  65. FontStyle.Normal, alignment,
  66. FontWeight.Normal, TextWrapping.NoWrap,
  67. widthConstraint);
  68. }
  69. private IFormattedTextImpl Create(string text, double fontSize, TextWrapping wrap, double widthConstraint)
  70. {
  71. return Create(text, FontName, fontSize,
  72. FontStyle.Normal, TextAlignment.Left,
  73. FontWeight.Normal, wrap,
  74. widthConstraint);
  75. }
  76. [Theory]
  77. [InlineData("", FontSize, 0, FontSizeHeight)]
  78. [InlineData("x", FontSize, 7.20, FontSizeHeight)]
  79. [InlineData(stringword, FontSize, 28.80, FontSizeHeight)]
  80. [InlineData(stringmiddle, FontSize, 309.65, FontSizeHeight)]
  81. [InlineData(stringmiddle, MediumFontSize, 464.48, 20.391)]
  82. [InlineData(stringmiddle, BigFontSize, 825.73, 36.25)]
  83. [InlineData(stringmiddle2lines, FontSize, 165.63, 2 * FontSizeHeight)]
  84. [InlineData(stringmiddle2lines, MediumFontSize, 248.44, 2 * 20.391)]
  85. [InlineData(stringmiddle2lines, BigFontSize, 441.67, 2 * 36.25)]
  86. [InlineData(stringlong, FontSize, 2160.35, FontSizeHeight)]
  87. [InlineData(stringmiddlenewlines, FontSize, 72.01, 4 * FontSizeHeight)]
  88. public void Should_Measure_String_Correctly(string input, double fontSize, double expWidth, double expHeight)
  89. {
  90. var fmt = Create(input, fontSize);
  91. var size = fmt.Bounds.Size;
  92. Assert.Equal(expWidth, size.Width, 2);
  93. Assert.Equal(expHeight, size.Height, 2);
  94. var linesHeight = fmt.GetLines().Sum(l => l.Height);
  95. Assert.Equal(expHeight, linesHeight, 2);
  96. }
  97. [Theory]
  98. [InlineData("", 1, -1, TextWrapping.NoWrap)]
  99. [InlineData("x", 1, -1, TextWrapping.NoWrap)]
  100. [InlineData(stringword, 1, -1, TextWrapping.NoWrap)]
  101. [InlineData(stringmiddle, 1, -1, TextWrapping.NoWrap)]
  102. [InlineData(stringmiddle, 3, 150, TextWrapping.Wrap)]
  103. [InlineData(stringmiddle2lines, 2, -1, TextWrapping.NoWrap)]
  104. [InlineData(stringmiddle2lines, 3, 150, TextWrapping.Wrap)]
  105. [InlineData(stringlong, 1, -1, TextWrapping.NoWrap)]
  106. [InlineData(stringlong, 18, 150, TextWrapping.Wrap)]
  107. [InlineData(stringmiddlenewlines, 4, -1, TextWrapping.NoWrap)]
  108. [InlineData(stringmiddlenewlines, 4, 150, TextWrapping.Wrap)]
  109. public void Should_Break_Lines_String_Correctly(string input,
  110. int linesCount,
  111. double widthConstraint,
  112. TextWrapping wrap)
  113. {
  114. var fmt = Create(input, FontSize, wrap, widthConstraint);
  115. var constrained = fmt;
  116. var lines = constrained.GetLines().ToArray();
  117. Assert.Equal(linesCount, lines.Count());
  118. }
  119. [Theory]
  120. [InlineData("x", 0, 0, true, false, 0)]
  121. [InlineData(stringword, -1, -1, false, false, 0)]
  122. [InlineData(stringword, 25, 13, true, false, 3)]
  123. [InlineData(stringword, 28.70, 13.5, true, true, 3)]
  124. [InlineData(stringword, 30, 13, false, true, 3)]
  125. [InlineData(stringword + "\r\n", 30, 13, false, false, 4)]
  126. [InlineData(stringword + "\r\nnext", 30, 13, false, false, 4)]
  127. [InlineData(stringword, 300, 13, false, true, 3)]
  128. [InlineData(stringword + "\r\n", 300, 13, false, false, 4)]
  129. [InlineData(stringword + "\r\nnext", 300, 13, false, false, 4)]
  130. [InlineData(stringword, 300, 300, false, true, 3)]
  131. //TODO: Direct2D implementation return textposition 6
  132. //but the text is 6 length, can't find the logic for me it should be 5
  133. //[InlineData(stringword + "\r\n", 300, 300, false, false, 6)]
  134. [InlineData(stringword + "\r\nnext", 300, 300, false, true, 9)]
  135. [InlineData(stringword + "\r\nnext", 300, 25, false, true, 9)]
  136. [InlineData(stringword, 28, 15, false, true, 3)]
  137. [InlineData(stringword, 30, 15, false, true, 3)]
  138. [InlineData(stringmiddle3lines, 30, 15, false, false, 9)]
  139. [InlineData(stringmiddle3lines, 500, 13, false, false, 8)]
  140. [InlineData(stringmiddle3lines, 30, 25, false, false, 9)]
  141. [InlineData(stringmiddle3lines, -1, 30, false, false, 10)]
  142. public void Should_HitTestPoint_Correctly(string input,
  143. double x, double y,
  144. bool isInside, bool isTrailing, int pos)
  145. {
  146. var fmt = Create(input, FontSize);
  147. var htRes = fmt.HitTestPoint(new Point(x, y));
  148. Assert.Equal(pos, htRes.TextPosition);
  149. Assert.Equal(isInside, htRes.IsInside);
  150. Assert.Equal(isTrailing, htRes.IsTrailing);
  151. }
  152. [Theory]
  153. [InlineData("", 0, 0, 0, 0, FontSizeHeight)]
  154. [InlineData("x", 0, 0, 0, 7.20, FontSizeHeight)]
  155. [InlineData("x", -1, 7.20, 0, 0, FontSizeHeight)]
  156. [InlineData(stringword, 3, 21.60, 0, 7.20, FontSizeHeight)]
  157. [InlineData(stringword, 4, 21.60 + 7.20, 0, 0, FontSizeHeight)]
  158. [InlineData(stringmiddlenewlines, 10, 0, FontSizeHeight, 7.20, FontSizeHeight)]
  159. [InlineData(stringmiddlenewlines, 15, 36.01, FontSizeHeight, 7.20, FontSizeHeight)]
  160. [InlineData(stringmiddlenewlines, 20, 0, 2 * FontSizeHeight, 7.20, FontSizeHeight)]
  161. [InlineData(stringmiddlenewlines, -1, 72.01, 3 * FontSizeHeight, 0, FontSizeHeight)]
  162. public void Should_HitTestPosition_Correctly(string input,
  163. int index, double x, double y, double width, double height)
  164. {
  165. var fmt = Create(input, FontSize);
  166. var r = fmt.HitTestTextPosition(index);
  167. Assert.Equal(x, r.X, 2);
  168. Assert.Equal(y, r.Y, 2);
  169. Assert.Equal(width, r.Width, 2);
  170. Assert.Equal(height, r.Height, 2);
  171. }
  172. [Theory]
  173. [InlineData("x", 0, 200, 200 - 7.20, 0, 7.20, FontSizeHeight)]
  174. [InlineData(stringword, 0, 200, 171.20, 0, 7.20, FontSizeHeight)]
  175. [InlineData(stringword, 3, 200, 200 - 7.20, 0, 7.20, FontSizeHeight)]
  176. public void Should_HitTestPosition_RigthAlign_Correctly(
  177. string input, int index, double widthConstraint,
  178. double x, double y, double width, double height)
  179. {
  180. //parse expected
  181. var fmt = Create(input, FontSize, TextAlignment.Right, widthConstraint);
  182. var constrained = fmt;
  183. var r = constrained.HitTestTextPosition(index);
  184. Assert.Equal(x, r.X, 2);
  185. Assert.Equal(y, r.Y, 2);
  186. Assert.Equal(width, r.Width, 2);
  187. Assert.Equal(height, r.Height, 2);
  188. }
  189. [Theory]
  190. [InlineData("x", 0, 200, 100 - 7.20 / 2, 0, 7.20, FontSizeHeight)]
  191. [InlineData(stringword, 0, 200, 85.6, 0, 7.20, FontSizeHeight)]
  192. [InlineData(stringword, 3, 200, 100 + 7.20, 0, 7.20, FontSizeHeight)]
  193. public void Should_HitTestPosition_CenterAlign_Correctly(
  194. string input, int index, double widthConstraint,
  195. double x, double y, double width, double height)
  196. {
  197. //parse expected
  198. var fmt = Create(input, FontSize, TextAlignment.Center, widthConstraint);
  199. var constrained = fmt;
  200. var r = constrained.HitTestTextPosition(index);
  201. Assert.Equal(x, r.X, 2);
  202. Assert.Equal(y, r.Y, 2);
  203. Assert.Equal(width, r.Width, 2);
  204. Assert.Equal(height, r.Height, 2);
  205. }
  206. [Theory]
  207. [InlineData("x", 0, 1, "0,0,7.20,13.59")]
  208. [InlineData(stringword, 0, 4, "0,0,28.80,13.59")]
  209. [InlineData(stringmiddlenewlines, 10, 10, "0,13.59,57.61,13.59")]
  210. [InlineData(stringmiddlenewlines, 10, 20, "0,13.59,57.61,13.59;0,27.19,64.81,13.59")]
  211. [InlineData(stringmiddlenewlines, 10, 15, "0,13.59,57.61,13.59;0,27.19,36.01,13.59")]
  212. [InlineData(stringmiddlenewlines, 15, 15, "36.01,13.59,21.60,13.59;0,27.19,64.81,13.59")]
  213. public void Should_HitTestRange_Correctly(string input,
  214. int index, int length,
  215. string expectedRects)
  216. {
  217. //parse expected result
  218. var rects = expectedRects.Split(';').Select(s =>
  219. {
  220. double[] v = s.Split(',')
  221. .Select(sd => double.Parse(sd, CultureInfo.InvariantCulture)).ToArray();
  222. return new Rect(v[0], v[1], v[2], v[3]);
  223. }).ToArray();
  224. var fmt = Create(input, FontSize);
  225. var htRes = fmt.HitTestTextRange(index, length).ToArray();
  226. Assert.Equal(rects.Length, htRes.Length);
  227. for (int i = 0; i < rects.Length; i++)
  228. {
  229. var exr = rects[i];
  230. var r = htRes[i];
  231. Assert.Equal(exr.X, r.X, 2);
  232. Assert.Equal(exr.Y, r.Y, 2);
  233. Assert.Equal(exr.Width, r.Width, 2);
  234. Assert.Equal(exr.Height, r.Height, 2);
  235. }
  236. }
  237. }
  238. }