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 (int i = 0; i < lineBuffer.Count; i++)
     {
         currentLineHeight = Math.Max(currentLineHeight, lineBuffer[i].Size.Y);
     }
     for (int 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)
        {
            XmlReader reader            = XmlReader.Create(new StringReader(text));
            Vector2   position          = Vector2.Zero;
            var       formatingStack    = new Stack <FormatInstruction>();
            var       conditionalsStack = new Stack <bool>();
            var       lineBuffer        = new List <ICompiledElement>();
            float     currentLineHeight;
            float     currentTotalHeight = 0;
            float     maxLineWidth       = float.MinValue;
            float     currentLineWidth   = 0;

            while (reader.Read())
            {
                switch (reader.NodeType)
                {
                case XmlNodeType.Element:
                    switch (reader.Name)
                    {
                    case "font": {
                        SpriteFont font;
                        string     s = reader.GetAttribute("face");
                        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": {
                        string clause    = reader.GetAttribute("clause");
                        bool   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;
                        }
                        maxLineWidth     = Math.Max(maxLineWidth, currentLineWidth);
                        currentLineWidth = 0;
                    }
                    break;

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

                    case "nbsp": {
                        FormatInstruction currentFormatting = formatingStack.Peek();
                        float             spaceX            = currentFormatting.Font.MeasureString(" ").X;
                        if (position.X + spaceX < width)
                        {
                            position.X       += spaceX;
                            currentLineWidth += spaceX;
                        }
                        break;
                    }
                    }
                    break;

                case XmlNodeType.Text: {
                    if (conditionalsStack.Count != 0 && !conditionalsStack.Peek())
                    {
                        break;
                    }
                    FormatInstruction currentFormatting = formatingStack.Peek();
                    string            str = reader.Value;
                    var      re           = new Regex(@"\s+");
                    string[] words        = re.Split(str);
                    float    spaceX       = currentFormatting.Font.MeasureString(" ").X;
                    for (int i = 0; i < words.Length; i++)
                    {
                        string  word   = words[i];
                        Vector2 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 "font":
                        formatingStack.Pop();
                        break;

                    case "if":
                        conditionalsStack.Pop();
                        break;
                    }
                }
                break;
                }
            }
            if (lineBuffer.Count > 0)
            {
                WrapLine(position, lineBuffer, out currentLineHeight, compiledMarkup);
                for (int i = 0; i < lineBuffer.Count; i++)
                {
                    ICompiledElement element = lineBuffer[i];
                    element.Position = new Vector2(element.Position.X, position.Y + currentLineHeight / 2f);
                }
                currentTotalHeight += currentLineHeight;
                maxLineWidth        = Math.Max(maxLineWidth, currentLineWidth);
                compiledMarkup.AddRange(lineBuffer);
                lineBuffer.Clear();
            }

            compiledMarkup.Size = new Vector2(maxLineWidth, currentTotalHeight);
            compiledMarkup.Text = text;
        }