TextFormatterTests.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Avalonia.Media;
  5. using Avalonia.Media.TextFormatting;
  6. using Avalonia.Media.TextFormatting.Unicode;
  7. using Avalonia.UnitTests;
  8. using Avalonia.Utilities;
  9. using Xunit;
  10. namespace Avalonia.Skia.UnitTests.Media.TextFormatting
  11. {
  12. public class TextFormatterTests
  13. {
  14. [Fact]
  15. public void Should_Format_TextRuns_With_Default_Style()
  16. {
  17. using (Start())
  18. {
  19. const string text = "0123456789";
  20. var defaultProperties =
  21. new GenericTextRunProperties(Typeface.Default, 12, foregroundBrush: Brushes.Black);
  22. var textSource = new SingleBufferTextSource(text, defaultProperties);
  23. var formatter = new TextFormatterImpl();
  24. var textLine = formatter.FormatLine(textSource, 0, double.PositiveInfinity,
  25. new GenericTextParagraphProperties(defaultProperties));
  26. Assert.Single(textLine.TextRuns);
  27. var textRun = textLine.TextRuns[0];
  28. Assert.Equal(defaultProperties.Typeface, textRun.Properties.Typeface);
  29. Assert.Equal(defaultProperties.ForegroundBrush, textRun.Properties.ForegroundBrush);
  30. Assert.Equal(text.Length, textRun.Text.Length);
  31. }
  32. }
  33. [Fact]
  34. public void Should_Format_TextRuns_With_Multiple_Buffers()
  35. {
  36. using (Start())
  37. {
  38. var defaultProperties =
  39. new GenericTextRunProperties(Typeface.Default, 12, foregroundBrush: Brushes.Black);
  40. var textSource = new MultiBufferTextSource(defaultProperties);
  41. var formatter = new TextFormatterImpl();
  42. var textLine = formatter.FormatLine(textSource, 0, double.PositiveInfinity,
  43. new GenericTextParagraphProperties(defaultProperties));
  44. Assert.Equal(5, textLine.TextRuns.Count);
  45. Assert.Equal(50, textLine.Length);
  46. }
  47. }
  48. [Fact]
  49. public void Should_Format_TextRuns_With_TextRunStyles()
  50. {
  51. using (Start())
  52. {
  53. const string text = "0123456789";
  54. var defaultProperties =
  55. new GenericTextRunProperties(Typeface.Default, 12, foregroundBrush: Brushes.Black);
  56. var GenericTextRunPropertiesRuns = new[]
  57. {
  58. new ValueSpan<TextRunProperties>(0, 3, defaultProperties),
  59. new ValueSpan<TextRunProperties>(3, 3,
  60. new GenericTextRunProperties(Typeface.Default, 13, foregroundBrush: Brushes.Black)),
  61. new ValueSpan<TextRunProperties>(6, 3,
  62. new GenericTextRunProperties(Typeface.Default, 14, foregroundBrush: Brushes.Black)),
  63. new ValueSpan<TextRunProperties>(9, 1, defaultProperties)
  64. };
  65. var textSource = new FormattedTextSource(text.AsMemory(), defaultProperties, GenericTextRunPropertiesRuns);
  66. var formatter = new TextFormatterImpl();
  67. var textLine = formatter.FormatLine(textSource, 0, double.PositiveInfinity,
  68. new GenericTextParagraphProperties(defaultProperties));
  69. Assert.Equal(text.Length, textLine.Length);
  70. for (var i = 0; i < GenericTextRunPropertiesRuns.Length; i++)
  71. {
  72. var GenericTextRunPropertiesRun = GenericTextRunPropertiesRuns[i];
  73. var textRun = textLine.TextRuns[i];
  74. Assert.Equal(GenericTextRunPropertiesRun.Length, textRun.Text.Length);
  75. }
  76. }
  77. }
  78. [Theory]
  79. [InlineData("0123", 1)]
  80. [InlineData("\r\n", 1)]
  81. [InlineData("👍b", 2)]
  82. [InlineData("a👍b", 3)]
  83. [InlineData("a👍子b", 4)]
  84. public void Should_Produce_Unique_Runs(string text, int numberOfRuns)
  85. {
  86. using (Start())
  87. {
  88. var defaultProperties = new GenericTextRunProperties(Typeface.Default);
  89. var textSource = new SingleBufferTextSource(text, defaultProperties);
  90. var formatter = new TextFormatterImpl();
  91. var textLine =
  92. formatter.FormatLine(textSource, 0, double.PositiveInfinity,
  93. new GenericTextParagraphProperties(defaultProperties));
  94. Assert.Equal(numberOfRuns, textLine.TextRuns.Count);
  95. }
  96. }
  97. [Fact]
  98. public void Should_Produce_A_Single_Fallback_Run()
  99. {
  100. using (Start())
  101. {
  102. var defaultProperties = new GenericTextRunProperties(Typeface.Default);
  103. const string text = "👍 👍 👍 👍";
  104. var textSource = new SingleBufferTextSource(text, defaultProperties);
  105. var formatter = new TextFormatterImpl();
  106. var textLine =
  107. formatter.FormatLine(textSource, 0, double.PositiveInfinity,
  108. new GenericTextParagraphProperties(defaultProperties));
  109. Assert.Equal(1, textLine.TextRuns.Count);
  110. }
  111. }
  112. [Fact]
  113. public void Should_Split_Run_On_Script()
  114. {
  115. using (Start())
  116. {
  117. const string text = "ABCDالدولي";
  118. var defaultProperties = new GenericTextRunProperties(Typeface.Default);
  119. var textSource = new SingleBufferTextSource(text, defaultProperties);
  120. var formatter = new TextFormatterImpl();
  121. var textLine =
  122. formatter.FormatLine(textSource, 0, double.PositiveInfinity,
  123. new GenericTextParagraphProperties(defaultProperties));
  124. var firstRun = textLine.TextRuns[0];
  125. Assert.Equal(4, firstRun.Text.Length);
  126. }
  127. }
  128. [InlineData("𐐷𐐷𐐷𐐷𐐷", 10, 1)]
  129. [InlineData("01234 56789 01234 56789", 6, 4)]
  130. [Theory]
  131. public void Should_Wrap_With_Overflow(string text, int expectedCharactersPerLine, int expectedNumberOfLines)
  132. {
  133. using (Start())
  134. {
  135. var defaultProperties = new GenericTextRunProperties(Typeface.Default);
  136. var textSource = new SingleBufferTextSource(text, defaultProperties);
  137. var formatter = new TextFormatterImpl();
  138. var numberOfLines = 0;
  139. var currentPosition = 0;
  140. while (currentPosition < text.Length)
  141. {
  142. var textLine =
  143. formatter.FormatLine(textSource, currentPosition, 1,
  144. new GenericTextParagraphProperties(defaultProperties, textWrap: TextWrapping.WrapWithOverflow));
  145. if (text.Length - currentPosition > expectedCharactersPerLine)
  146. {
  147. Assert.Equal(expectedCharactersPerLine, textLine.Length);
  148. }
  149. currentPosition += textLine.Length;
  150. numberOfLines++;
  151. }
  152. Assert.Equal(expectedNumberOfLines, numberOfLines);
  153. }
  154. }
  155. [InlineData("Whether to turn off HTTPS. This option only applies if Individual, " +
  156. "IndividualB2C, SingleOrg, or MultiOrg aren't used for &#8209;&#8209;auth."
  157. , "Noto Sans", 40)]
  158. [InlineData("01234 56789 01234 56789", "Noto Mono", 7)]
  159. [Theory]
  160. public void Should_Wrap(string text, string familyName, int numberOfCharactersPerLine)
  161. {
  162. using (Start())
  163. {
  164. var lineBreaker = new LineBreakEnumerator(text.AsMemory());
  165. var expected = new List<int>();
  166. while (lineBreaker.MoveNext())
  167. {
  168. expected.Add(lineBreaker.Current.PositionWrap - 1);
  169. }
  170. var typeface = new Typeface("resm:Avalonia.Skia.UnitTests.Assets?assembly=Avalonia.Skia.UnitTests#" +
  171. familyName);
  172. var defaultProperties = new GenericTextRunProperties(Typeface.Default);
  173. var textSource = new SingleBufferTextSource(text, defaultProperties);
  174. var formatter = new TextFormatterImpl();
  175. var glyph = typeface.GlyphTypeface.GetGlyph('a');
  176. var advance = typeface.GlyphTypeface.GetGlyphAdvance(glyph) *
  177. (12.0 / typeface.GlyphTypeface.DesignEmHeight);
  178. var paragraphWidth = advance * numberOfCharactersPerLine;
  179. var currentPosition = 0;
  180. while (currentPosition < text.Length)
  181. {
  182. var textLine =
  183. formatter.FormatLine(textSource, currentPosition, paragraphWidth,
  184. new GenericTextParagraphProperties(defaultProperties, textWrap: TextWrapping.Wrap));
  185. var end = textLine.FirstTextSourceIndex + textLine.Length - 1;
  186. Assert.True(expected.Contains(end));
  187. var index = expected.IndexOf(end);
  188. for (var i = 0; i <= index; i++)
  189. {
  190. expected.RemoveAt(0);
  191. }
  192. currentPosition += textLine.Length;
  193. }
  194. }
  195. }
  196. [Fact]
  197. public void Should_Produce_Fixed_Height_Lines()
  198. {
  199. using (Start())
  200. {
  201. const string text = "012345";
  202. var defaultProperties = new GenericTextRunProperties(Typeface.Default);
  203. var textSource = new SingleBufferTextSource(text, defaultProperties);
  204. var formatter = new TextFormatterImpl();
  205. var textLine =
  206. formatter.FormatLine(textSource, 0, double.PositiveInfinity,
  207. new GenericTextParagraphProperties(defaultProperties, lineHeight: 50));
  208. Assert.Equal(50, textLine.Height);
  209. }
  210. }
  211. [Fact]
  212. public void Should_Not_Produce_TextLine_Wider_Than_ParagraphWidth()
  213. {
  214. using (Start())
  215. {
  216. const string text =
  217. "Multiline TextBlock with TextWrapping.\r\rLorem ipsum dolor sit amet, consectetur adipiscing elit. " +
  218. "Vivamus magna. Cras in mi at felis aliquet congue. Ut a est eget ligula molestie gravida. Curabitur massa. " +
  219. "Donec eleifend, libero at sagittis mollis, tellus est malesuada tellus, at luctus turpis elit sit amet quam. " +
  220. "Vivamus pretium ornare est.";
  221. var defaultProperties = new GenericTextRunProperties(Typeface.Default);
  222. var paragraphProperties = new GenericTextParagraphProperties(defaultProperties, textWrap: TextWrapping.Wrap);
  223. var textSource = new SingleBufferTextSource(text, defaultProperties);
  224. var formatter = new TextFormatterImpl();
  225. var textSourceIndex = 0;
  226. while (textSourceIndex < text.Length)
  227. {
  228. var textLine =
  229. formatter.FormatLine(textSource, textSourceIndex, 200, paragraphProperties);
  230. Assert.True(textLine.Width <= 200);
  231. textSourceIndex += textLine.Length;
  232. }
  233. }
  234. }
  235. [Fact]
  236. public void Wrap_Should_Not_Produce_Empty_Lines()
  237. {
  238. using (Start())
  239. {
  240. const string text = "012345";
  241. var defaultProperties = new GenericTextRunProperties(Typeface.Default);
  242. var paragraphProperties = new GenericTextParagraphProperties(defaultProperties, textWrap: TextWrapping.Wrap);
  243. var textSource = new SingleBufferTextSource(text, defaultProperties);
  244. var formatter = new TextFormatterImpl();
  245. var textSourceIndex = 0;
  246. while (textSourceIndex < text.Length)
  247. {
  248. var textLine =
  249. formatter.FormatLine(textSource, textSourceIndex, 3, paragraphProperties);
  250. Assert.NotEqual(0, textLine.Length);
  251. textSourceIndex += textLine.Length;
  252. }
  253. Assert.Equal(text.Length, textSourceIndex);
  254. }
  255. }
  256. [InlineData("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor",
  257. new[] { "Lorem ipsum ", "dolor sit amet, ", "consectetur ", "adipisicing elit, ", "sed do eiusmod " })]
  258. [Theory]
  259. public void Should_Produce_Wrapped_And_Trimmed_Lines(string text, string[] expectedLines)
  260. {
  261. using (Start())
  262. {
  263. var typeface = new Typeface("Verdana");
  264. var defaultProperties = new GenericTextRunProperties(typeface, 32, foregroundBrush: Brushes.Black);
  265. var styleSpans = new[]
  266. {
  267. new ValueSpan<TextRunProperties>(0, 5,
  268. new GenericTextRunProperties(typeface, 48)),
  269. new ValueSpan<TextRunProperties>(6, 11,
  270. new GenericTextRunProperties(new Typeface("Verdana", weight: FontWeight.Bold), 32)),
  271. new ValueSpan<TextRunProperties>(28, 28,
  272. new GenericTextRunProperties(new Typeface("Verdana", FontStyle.Italic),32))
  273. };
  274. var textSource = new FormattedTextSource(text.AsMemory(), defaultProperties, styleSpans);
  275. var formatter = new TextFormatterImpl();
  276. var currentPosition = 0;
  277. var currentHeight = 0d;
  278. var currentLineIndex = 0;
  279. while (currentPosition < text.Length && currentLineIndex < expectedLines.Length)
  280. {
  281. var textLine =
  282. formatter.FormatLine(textSource, currentPosition, 300,
  283. new GenericTextParagraphProperties(defaultProperties, textWrap: TextWrapping.WrapWithOverflow));
  284. currentPosition += textLine.Length;
  285. if (textLine.Width > 300 || currentHeight + textLine.Height > 240)
  286. {
  287. textLine = textLine.Collapse(new TextTrailingWordEllipsis(new ReadOnlySlice<char>(new[] { TextTrimming.s_defaultEllipsisChar }), 300, defaultProperties));
  288. }
  289. currentHeight += textLine.Height;
  290. var currentText = text.Substring(textLine.FirstTextSourceIndex, textLine.Length);
  291. Assert.Equal(expectedLines[currentLineIndex], currentText);
  292. currentLineIndex++;
  293. }
  294. Assert.Equal(expectedLines.Length, currentLineIndex);
  295. }
  296. }
  297. [InlineData("0123456789", TextAlignment.Left, FlowDirection.LeftToRight)]
  298. [InlineData("0123456789", TextAlignment.Center, FlowDirection.LeftToRight)]
  299. [InlineData("0123456789", TextAlignment.Right, FlowDirection.LeftToRight)]
  300. [InlineData("0123456789", TextAlignment.Left, FlowDirection.RightToLeft)]
  301. [InlineData("0123456789", TextAlignment.Center, FlowDirection.RightToLeft)]
  302. [InlineData("0123456789", TextAlignment.Right, FlowDirection.RightToLeft)]
  303. [InlineData("שנבגק", TextAlignment.Left, FlowDirection.RightToLeft)]
  304. [InlineData("שנבגק", TextAlignment.Center, FlowDirection.RightToLeft)]
  305. [InlineData("שנבגק", TextAlignment.Right, FlowDirection.RightToLeft)]
  306. [Theory]
  307. public void Should_Align_TextLine(string text, TextAlignment textAlignment, FlowDirection flowDirection)
  308. {
  309. using (Start())
  310. {
  311. var defaultProperties = new GenericTextRunProperties(Typeface.Default);
  312. var paragraphProperties = new GenericTextParagraphProperties(flowDirection, textAlignment, true, true,
  313. defaultProperties, TextWrapping.NoWrap, 0, 0);
  314. var textSource = new SingleBufferTextSource(text, defaultProperties);
  315. var formatter = new TextFormatterImpl();
  316. var textLine =
  317. formatter.FormatLine(textSource, 0, 100, paragraphProperties);
  318. var expectedOffset = 0d;
  319. switch (textAlignment)
  320. {
  321. case TextAlignment.Center:
  322. expectedOffset = 50 - textLine.Width / 2;
  323. break;
  324. case TextAlignment.Right:
  325. expectedOffset = 100 - textLine.WidthIncludingTrailingWhitespace;
  326. break;
  327. }
  328. Assert.Equal(expectedOffset, textLine.Start);
  329. }
  330. }
  331. [Fact]
  332. public void Should_Wrap_Syriac()
  333. {
  334. using (Start())
  335. {
  336. const string text =
  337. "܀ ܁ ܂ ܃ ܄ ܅ ܆ ܇ ܈ ܉ ܊ ܋ ܌ ܍ ܏ ܐ ܑ ܒ ܓ ܔ ܕ ܖ ܗ ܘ ܙ ܚ ܛ ܜ ܝ ܞ ܟ ܠ ܡ ܢ ܣ ܤ ܥ ܦ ܧ ܨ ܩ ܪ ܫ ܬ ܰ ܱ ܲ ܳ ܴ ܵ ܶ ܷ ܸ ܹ ܺ ܻ ܼ ܽ ܾ ܿ ݀ ݁ ݂ ݃ ݄ ݅ ݆ ݇ ݈ ݉ ݊";
  338. var defaultProperties = new GenericTextRunProperties(Typeface.Default);
  339. var paragraphProperties =
  340. new GenericTextParagraphProperties(defaultProperties, textWrap: TextWrapping.Wrap);
  341. var textSource = new SingleBufferTextSource(text, defaultProperties);
  342. var formatter = new TextFormatterImpl();
  343. var textPosition = 87;
  344. TextLineBreak lastBreak = null;
  345. while (textPosition < text.Length)
  346. {
  347. var textLine =
  348. formatter.FormatLine(textSource, textPosition, 50, paragraphProperties, lastBreak);
  349. Assert.Equal(textLine.Length, textLine.TextRuns.Sum(x => x.TextSourceLength));
  350. textPosition += textLine.Length;
  351. lastBreak = textLine.TextLineBreak;
  352. }
  353. }
  354. }
  355. [Fact]
  356. public void Should_FormatLine_With_Emergency_Breaks()
  357. {
  358. using (Start())
  359. {
  360. var defaultProperties = new GenericTextRunProperties(Typeface.Default);
  361. var paragraphProperties = new GenericTextParagraphProperties(defaultProperties, textWrap: TextWrapping.Wrap);
  362. var textSource = new SingleBufferTextSource("0123456789_0123456789_0123456789_0123456789", defaultProperties);
  363. var formatter = new TextFormatterImpl();
  364. var textLine =
  365. formatter.FormatLine(textSource, 0, 33, paragraphProperties);
  366. Assert.NotNull(textLine.TextLineBreak?.RemainingRuns);
  367. }
  368. }
  369. [InlineData("פעילות הבינאום, W3C!")]
  370. [InlineData("abcABC")]
  371. [InlineData("זה כיף סתם לשמוע איך תנצח קרפד עץ טוב בגן")]
  372. [InlineData("טטטט abcDEF טטטט")]
  373. [Theory]
  374. public void Should_Not_Alter_TextRuns_After_TextStyles_Were_Applied(string text)
  375. {
  376. using (Start())
  377. {
  378. var formatter = new TextFormatterImpl();
  379. var defaultProperties = new GenericTextRunProperties(Typeface.Default);
  380. var paragraphProperties =
  381. new GenericTextParagraphProperties(defaultProperties, textWrap: TextWrapping.NoWrap);
  382. var foreground = new SolidColorBrush(Colors.Red).ToImmutable();
  383. var expectedTextLine = formatter.FormatLine(new SingleBufferTextSource(text, defaultProperties),
  384. 0, double.PositiveInfinity, paragraphProperties);
  385. var expectedRuns = expectedTextLine.TextRuns.Cast<ShapedTextCharacters>().ToList();
  386. var expectedGlyphs = expectedRuns.SelectMany(x => x.GlyphRun.GlyphIndices).ToList();
  387. for (var i = 0; i < text.Length; i++)
  388. {
  389. for (var j = 1; i + j < text.Length; j++)
  390. {
  391. var spans = new[]
  392. {
  393. new ValueSpan<TextRunProperties>(i, j,
  394. new GenericTextRunProperties(Typeface.Default, 12, foregroundBrush: foreground))
  395. };
  396. var textSource = new FormattedTextSource(text.AsMemory(), defaultProperties, spans);
  397. var textLine =
  398. formatter.FormatLine(textSource, 0, double.PositiveInfinity, paragraphProperties);
  399. var shapedRuns = textLine.TextRuns.Cast<ShapedTextCharacters>().ToList();
  400. var actualGlyphs = shapedRuns.SelectMany(x => x.GlyphRun.GlyphIndices).ToList();
  401. Assert.Equal(expectedGlyphs, actualGlyphs);
  402. }
  403. }
  404. }
  405. }
  406. [Fact]
  407. public void Should_FormatLine_With_DrawableRuns()
  408. {
  409. var defaultRunProperties = new GenericTextRunProperties(Typeface.Default, foregroundBrush: Brushes.Black);
  410. var paragraphProperties = new GenericTextParagraphProperties(defaultRunProperties);
  411. var textSource = new CustomTextSource("Hello World ->");
  412. using (Start())
  413. {
  414. var textLine =
  415. TextFormatter.Current.FormatLine(textSource, 0, double.PositiveInfinity, paragraphProperties);
  416. Assert.Equal(3, textLine.TextRuns.Count);
  417. Assert.True(textLine.TextRuns[1] is RectangleRun);
  418. }
  419. }
  420. [Fact]
  421. public void Should_Format_With_EndOfLineRun()
  422. {
  423. using (Start())
  424. {
  425. var defaultRunProperties = new GenericTextRunProperties(Typeface.Default);
  426. var paragraphProperties = new GenericTextParagraphProperties(defaultRunProperties);
  427. var textSource = new EndOfLineTextSource();
  428. var textLine =
  429. TextFormatter.Current.FormatLine(textSource, 0, double.PositiveInfinity, paragraphProperties);
  430. Assert.NotNull(textLine.TextLineBreak);
  431. Assert.Equal(TextRun.DefaultTextSourceLength, textLine.Length);
  432. }
  433. }
  434. private class EndOfLineTextSource : ITextSource
  435. {
  436. public TextRun GetTextRun(int textSourceIndex)
  437. {
  438. return new TextEndOfLine();
  439. }
  440. }
  441. private class CustomTextSource : ITextSource
  442. {
  443. private readonly string _text;
  444. public CustomTextSource(string text)
  445. {
  446. _text = text;
  447. }
  448. public TextRun GetTextRun(int textSourceIndex)
  449. {
  450. if (textSourceIndex >= _text.Length + TextRun.DefaultTextSourceLength + _text.Length)
  451. {
  452. return null;
  453. }
  454. if (textSourceIndex == _text.Length)
  455. {
  456. return new RectangleRun(new Rect(0, 0, 50, 50), Brushes.Green);
  457. }
  458. return new TextCharacters(_text.AsMemory(),
  459. new GenericTextRunProperties(Typeface.Default, foregroundBrush: Brushes.Black));
  460. }
  461. }
  462. private class RectangleRun : DrawableTextRun
  463. {
  464. private readonly Rect _rect;
  465. private readonly IBrush _fill;
  466. public RectangleRun(Rect rect, IBrush fill)
  467. {
  468. _rect = rect;
  469. _fill = fill;
  470. }
  471. public override Size Size => _rect.Size;
  472. public override double Baseline => 0;
  473. public override void Draw(DrawingContext drawingContext, Point origin)
  474. {
  475. using (drawingContext.PushPreTransform(Matrix.CreateTranslation(new Vector(origin.X, 0))))
  476. {
  477. drawingContext.FillRectangle(_fill, _rect);
  478. }
  479. }
  480. }
  481. public static IDisposable Start()
  482. {
  483. var disposable = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface
  484. .With(renderInterface: new PlatformRenderInterface(null),
  485. textShaperImpl: new TextShaperImpl()));
  486. AvaloniaLocator.CurrentMutable
  487. .Bind<FontManager>().ToConstant(new FontManager(new CustomFontManagerImpl()));
  488. return disposable;
  489. }
  490. }
  491. }