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; }