File: Text\XmlAttributedTextReader.cs
Web Access
Project: src\src\Graphics\src\Graphics\Graphics.csproj (Microsoft.Maui.Graphics)
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Xml;
using Microsoft.Maui.Graphics.Text;
using XmlNames = Microsoft.Maui.Graphics.Text.XmlAttributedTextNames;
 
namespace Microsoft.Maui.Graphics.Text
{
	public class XmlAttributedTextReader
	{
		private XmlReader _reader;
 
		private StringWriter _writer;
		private readonly List<IAttributedTextRun> _runs = new List<IAttributedTextRun>();
		private bool _inContent;
		private bool _contentEncoded;
 
		public IAttributedText Read(string text)
		{
			using (var stringReader = new StringReader(text))
			{
				return Read(stringReader);
			}
		}
 
		public IAttributedText Read(TextReader reader)
		{
			_writer = new StringWriter();
			_runs.Clear();
 
			using (_reader = XmlReader.Create(reader))
			{
				try
				{
					while (_reader.Read())
					{
						switch (_reader.NodeType)
						{
							case XmlNodeType.CDATA:
								HandleText();
								break;
							case XmlNodeType.Text:
								HandleText();
								break;
							case XmlNodeType.Element:
								ElementStarted();
								break;
							case XmlNodeType.EndElement:
								ElementEnded();
								break;
						}
					}
				}
				catch (XmlException)
				{
					_writer.Write(reader.ReadToEnd());
				}
			}
 
			var text = _writer.ToString();
			var textLength = text.Length;
			_runs.Optimize(textLength);
 
			return new AttributedText(text, _runs, true);
		}
 
		protected void ElementStarted()
		{
			string elementName = _reader.Name;
 
			if (XmlNames.Content.Equals(elementName, StringComparison.Ordinal))
			{
				_inContent = true;
				_contentEncoded = ReadBool(XmlNames.Encoded);
			}
			else if (XmlNames.Run.Equals(elementName, StringComparison.Ordinal))
			{
				ReadRun();
			}
		}
 
		protected void ElementEnded()
		{
			string elementName = _reader.Name;
			if (XmlNames.Content.Equals(elementName, StringComparison.Ordinal))
				_inContent = false;
		}
 
		private void HandleText()
		{
			if (_inContent)
			{
				if (_contentEncoded)
				{
					byte[] bytes = Convert.FromBase64String(_reader.Value);
					string text = Encoding.UTF8.GetString(bytes, 0, bytes.Length);
					_writer.Write(text);
				}
				else
				{
					_writer.Write(_reader.Value);
				}
			}
		}
 
		private void ReadRun()
		{
			TextAttributes attributes = new TextAttributes();
 
			var start = ReadInt(XmlNames.Start, 0);
			var length = ReadInt(XmlNames.Length, 0);
 
			if (_reader.HasAttributes)
			{
				_reader.MoveToElement();
				while (_reader.MoveToNextAttribute())
				{
					var attributeName = _reader.Name;
					var attributeValue = _reader.Value;
 
					if (!(XmlNames.Start.Equals(attributeName, StringComparison.Ordinal) || XmlNames.Length.Equals(attributeName, StringComparison.Ordinal)))
					{
						if (Enum.TryParse(attributeName, out TextAttribute key))
							attributes[key] = attributeValue;
					}
				}
 
				_reader.MoveToElement();
			}
 
			var run = new AttributedTextRun(start, length, attributes);
			_runs.Add(run);
		}
 
		private bool ReadBool(string attribute)
		{
			var value = _reader.GetAttribute(attribute);
			if (value != null)
				return ParseBool(value);
 
			return false;
		}
 
		private bool ParseBool(string value)
		{
			if (value != null)
			{
				if (bool.TryParse(value, out var boolValue))
					return boolValue;
			}
 
			return false;
		}
 
		private int ReadInt(string attribute, int defaultValue)
		{
			var value = _reader.GetAttribute(attribute);
			if (value != null)
				return ParseInt(value, defaultValue);
 
			return defaultValue;
		}
 
		private int ParseInt(string value, int defaultValue)
		{
			if (value != null)
			{
				if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var intValue))
					return intValue;
			}
 
			return defaultValue;
		}
	}
}