| File: Text\AttributedTextExtensions.cs | Web Access |
| Project: src\src\Graphics\src\Graphics\Graphics.csproj (Microsoft.Maui.Graphics) |
using System; using System.Collections.Generic; using System.IO; using System.Linq; using Microsoft.Maui.Graphics.Text; namespace Microsoft.Maui.Graphics.Text { public static class AttributedTextExtensions { public static IAttributedText Optimize(this IAttributedText attributedText) { if (attributedText?.Text == null) return null; if (attributedText is AbstractAttributedText abstractAttributedText && abstractAttributedText.Optimal) return attributedText; var start = 0; var attributeIndex = 0; var text = attributedText.Text; var length = text.Length; var runs = new List<IAttributedTextRun>(); attributedText.CreateParagraphRun(start, length, runs, attributeIndex); return new AttributedText(text, runs, true); } internal static List<IAttributedTextRun> OptimizeRuns(this IAttributedText attributedText) { if (attributedText?.Text == null) return null; if (attributedText is AbstractAttributedText abstractAttributedText && abstractAttributedText.Optimal) { if (attributedText.Runs == null) return null; if (attributedText.Runs is List<IAttributedTextRun> list) return list; return attributedText.Runs.ToList(); } var start = 0; var attributeIndex = 0; var text = attributedText.Text; var length = text.Length; var runs = new List<IAttributedTextRun>(); attributedText.CreateParagraphRun(start, length, runs, attributeIndex); return runs; } public static IReadOnlyList<IAttributedText> CreateParagraphs(this IAttributedText attributedText) { if (attributedText?.Text == null) return null; List<IAttributedText> paragraphs = new List<IAttributedText>(); int start = 0; int attributeIndex = 0; using (var sr = new StringReader(attributedText.Text)) { string line; while ((line = sr.ReadLine()) != null) { var length = line.Length; var runs = new List<IAttributedTextRun>(); attributeIndex = attributedText.CreateParagraphRun(start, length, runs, attributeIndex); var paragraph = new AttributedText(line, runs); paragraphs.Add(paragraph); start += length + 1; } } return paragraphs; } public static int CreateParagraphRun( this IAttributedText text, int start, int length, IList<IAttributedTextRun> runs, int startIndexForSearch = 0) { // If the text doesn't have any runs, then we can simply return if (text.Runs == null || text.Runs.Count == 0) return 0; // If we've already reached the end of the runs, we can simply return if (!(startIndexForSearch < text.Runs.Count)) return startIndexForSearch; var end = start + length; var index = startIndexForSearch; do { var run = text.Runs[index]; // If the run is after the end index, then we can go ahead and return if (end < run.Start) return index; if (run.Intersects(start, length)) { if (start == run.Start) { var paragraphStart = run.Start - start; var paragraphLength = Math.Min(run.Length, length); runs.Add(new AttributedTextRun(paragraphStart, paragraphLength, run.Attributes)); // If the length of the run is the same as the paragraph, then we know // that the next run (if any) will apply to to the next paragraph. if (run.Length == length) return index + 1; // If the run is longer than the line, then we know that the attributes from this run // will also apply to the next paragraph. if (run.Length > length) return index; // If the run length is less than the length of the line, then the next run may apply // to this line, so continue } else if (end == run.GetEnd()) { var paragraphStart = Math.Max(run.Start - start, 0); var paragraphLength = Math.Min(run.Length, end - paragraphStart); runs.Add(new AttributedTextRun(paragraphStart, paragraphLength, run.Attributes)); // Since the run and the line have the same end, we know that the next run (if any) will // apply to to the next paragraph. return index + 1; } else { var paragraphStart = Math.Max(run.Start - start, 0); var paragraphLength = Math.Min(run.Length, length - paragraphStart); runs.Add(new AttributedTextRun(paragraphStart, paragraphLength, run.Attributes)); } } index++; } while (index < text.Runs.Count); return index; } public static IList<AttributedTextBlock> CreateBlocks(this IAttributedText text) { if (text?.Text == null) return null; var blocks = new List<AttributedTextBlock>(); var start = 0; var end = text.Text.Length; if (text.Runs?.Count > 0) { foreach (var run in text.Runs) { if (start < run.Start) { var noAttrLength = run.Start - start; var noAttrValue = text.Text.Substring(start, noAttrLength); blocks.Add(new AttributedTextBlock(noAttrValue, null)); start = run.Start; } var length = run.Length; if (length > 0) { var value = text.Text.Substring(start, length); blocks.Add(new AttributedTextBlock(value, run.Attributes)); start = run.GetEnd(); } #if DEBUG else System.Diagnostics.Debug.WriteLine("Length should not be less then 0"); #endif } } if (start < end) { var value = text.Text.Substring(start); blocks.Add(new AttributedTextBlock(value, null)); } return blocks; } } }