public CompiledMarkup Compile(string text, float width)
        {
            var compiledMarkupText = new CompiledMarkup();

            Compile(text, width, compiledMarkupText);
            return(compiledMarkupText);
        }
 private Vector2 WrapLine(Vector2 position, List <ICompiledElement> lineBuffer, out float currentLineHeight, CompiledMarkup compiledMarkup)
 {
     currentLineHeight = 0;
     for (var i = 0; i < lineBuffer.Count; i++)
     {
         currentLineHeight = Math.Max(currentLineHeight, lineBuffer[i].Size.Y);
     }
     for (var i = 0; i < lineBuffer.Count; i++)
     {
         lineBuffer[i].Position = new Vector2(lineBuffer[i].Position.X, position.Y + currentLineHeight / 2f);
     }
     compiledMarkup.AddRange(lineBuffer);
     lineBuffer.Clear();
     position.X  = 0;
     position.Y += currentLineHeight;
     return(position);
 }
        public void Compile(string text, float width, CompiledMarkup compiledMarkup)
        {
            var   reader            = XmlReader.Create(new StringReader(text));
            var   position          = Vector2.Zero;
            var   formatingStack    = new Stack <FormatInstruction>();
            var   conditionalsStack = new Stack <bool>();
            var   lineBuffer        = new List <ICompiledElement>();
            float currentLineHeight;
            float currentTotalHeight = 0;
            var   maxLineWidth       = float.MinValue;
            float currentLineWidth   = 0;

            while (reader.Read())
            {
                switch (reader.NodeType)
                {
                case XmlNodeType.Element:
                    switch (reader.Name)
                    {
                    case "text": {
                        SpriteFont font;
                        var        s = reader.GetAttribute("font");
                        if (!string.IsNullOrEmpty(s))
                        {
                            font = fontResolver.Invoke(s);
                        }
                        else if (formatingStack.Count > 0)
                        {
                            font = formatingStack.Peek().Font;
                        }
                        else
                        {
                            throw new InvalidOperationException("Need a font.");
                        }

                        Color color;
                        s = reader.GetAttribute("color");
                        if (!string.IsNullOrEmpty(s))
                        {
                            color = ToColor(s);
                        }
                        else if (formatingStack.Count > 0)
                        {
                            color = formatingStack.Peek().Color;
                        }
                        else
                        {
                            throw new InvalidOperationException("Need a color.");
                        }
                        formatingStack.Push(new FormatInstruction(font, color));
                    }
                    break;

                    case "if": {
                        var clause    = reader.GetAttribute("clause");
                        var condition = conditionalResolver.Invoke(clause);
                        conditionalsStack.Push(condition);
                    }
                    break;

                    case "br": {
                        if (lineBuffer.Count > 0)
                        {
                            position            = WrapLine(position, lineBuffer, out currentLineHeight, compiledMarkup);
                            currentTotalHeight += currentLineHeight;
                        }
                        else
                        {
                            position.Y         += formatingStack.Peek().Font.LineSpacing;
                            currentTotalHeight += formatingStack.Peek().Font.LineSpacing;
                        }
                        currentLineWidth = 0;
                    }
                    break;

                    case "img": {
                        if (conditionalsStack.Count != 0 && !conditionalsStack.Peek())
                        {
                            break;
                        }
                        var imgSrc = reader.GetAttribute("src");
                        var color  = Color.White;
                        var s      = reader.GetAttribute("color");
                        if (!string.IsNullOrEmpty(s))
                        {
                            color = ToColor(s);
                        }
                        var image = imageResolver.Invoke(imgSrc);
                        if (position.X + image.Width > width)
                        {
                            position            = WrapLine(position, lineBuffer, out currentLineHeight, compiledMarkup);
                            currentTotalHeight += currentLineHeight;
                        }
                        lineBuffer.Add(new CompiledImageElement(image, color, position));
                        position.X       += image.Width;
                        currentLineWidth += image.Width;
                    }
                    break;
                    }
                    break;

                case XmlNodeType.Text: {
                    if (conditionalsStack.Count != 0 && !conditionalsStack.Peek())
                    {
                        break;
                    }
                    var currentFormatting = formatingStack.Peek();
                    var str    = reader.Value;
                    var re     = new Regex(@"\s+");
                    var words  = re.Split(str);
                    var spaceX = currentFormatting.Font.MeasureString(" ").X;
                    for (var i = 0; i < words.Length; i++)
                    {
                        var word   = words[i];
                        var wordSz = currentFormatting.Font.MeasureString(word);
                        if (position.X + wordSz.X > width)
                        {
                            position            = WrapLine(position, lineBuffer, out currentLineHeight, compiledMarkup);
                            currentTotalHeight += currentLineHeight;
                            maxLineWidth        = Math.Max(maxLineWidth, currentLineWidth);
                            currentLineWidth    = 0;
                        }

                        lineBuffer.Add(new CompiledTextElement(word, position, currentFormatting));
                        position.X       += wordSz.X;
                        currentLineWidth += wordSz.X;
                        if (i < words.Length - 1)
                        {
                            position.X       += spaceX;
                            currentLineWidth += spaceX;
                        }
                    }
                }
                break;

                case XmlNodeType.EndElement: {
                    switch (reader.Name)
                    {
                    case "text":
                        formatingStack.Pop();
                        break;

                    case "if":
                        conditionalsStack.Pop();
                        break;
                    }
                }
                break;
                }
            }
            if (lineBuffer.Count > 0)
            {
                WrapLine(position, lineBuffer, out currentLineHeight, compiledMarkup);
                foreach (var element in lineBuffer)
                {
                    element.Position = new Vector2(element.Position.X, position.Y + currentLineHeight / 2f);
                }
                maxLineWidth        = Math.Max(maxLineWidth, currentLineWidth);
                currentTotalHeight += currentLineHeight;
                compiledMarkup.Size = new Vector2(maxLineWidth, currentTotalHeight);
                compiledMarkup.AddRange(lineBuffer);
                lineBuffer.Clear();
            }
            compiledMarkup.Text = text;
        }