/// <summary> /// Sets the formatting for the given range of characters in the line /// </summary> public void SetFormatting(int start, int end, GlyphFormat format, bool onlyChangeColor) { Vector4 bbColor = QuadBoard.GetQuadBoardColor(format.Data.Item4); for (int n = start; n <= end; n++) { if (onlyChangeColor) { var quadBoard = glyphBoards[n]; quadBoard.bbColor = bbColor; glyphBoards[n] = quadBoard; formattedGlyphs[n] = new FormattedGlyph(formattedGlyphs[n].glyph, format); } else if (!formattedGlyphs[n].format.Equals(format)) { IFontStyle fontStyle = FontManager.GetFontStyle(format.Data.Item3); float scale = format.Data.Item2 * fontStyle.FontScale; Glyph glyph = fontStyle[chars[n]]; Vector2 glyphSize = new Vector2(glyph.advanceWidth, fontStyle.Height) * scale; formattedGlyphs[n] = new FormattedGlyph(glyph, format); locData[n] = new GlyphLocData(glyph.MatFrame.Material.size * scale, glyphSize); glyphBoards[n] = glyph.GetQuadBoard(format, bbColor); } } }
/// <summary> /// Updates the position of the right character. /// </summary> private float UpdateCharOffset(Line line, int right, int left, Vector2 pos, float xAlign) { char ch = line.Chars[right]; FormattedGlyph formattedGlyph = line.FormattedGlyphs[right]; IFontStyle fontStyle = FontManager.GetFontStyle(formattedGlyph.format.StyleIndex); float textSize = formattedGlyph.format.TextSize, formatScale = textSize * fontStyle.FontScale, // Quick fix for CJK characters in Space Engineers font data cjkOffset = (formattedGlyph.format.StyleIndex.X == 0 && ch >= 0x4E00) ? (-4f * textSize) : 0f; // Kerning adjustment if (left >= 0) { GlyphFormat leftFmt = line.FormattedGlyphs[left].format, rightFmt = formattedGlyph.format; if (leftFmt.StyleIndex == rightFmt.StyleIndex && leftFmt.TextSize == rightFmt.TextSize) { pos.X += fontStyle.GetKerningAdjustment(line.Chars[left], ch) * formatScale; } } GlyphLocData locData = line.LocData[right]; line.SetOffsetAt(right, new Vector2() { X = pos.X + locData.bbSize.X * .5f + (formattedGlyph.glyph.leftSideBearing * formatScale) + xAlign, Y = pos.Y - (locData.bbSize.Y * .5f) + (fontStyle.BaseLine * formatScale) + cjkOffset }); return(pos.X + locData.chSize.X); }
/// <summary> /// Recalculates the width and height of the line. /// </summary> public void UpdateSize() { _size = Vector2.Zero; if (chars.Count > 0) { for (int n = 0; n < locData.Count; n++) { GlyphLocData sizeData = locData[n]; if (sizeData.chSize.Y > _size.Y) { _size.Y = sizeData.chSize.Y; } float chWidth = sizeData.chSize.X; if (chars[n] == '\t') { FormattedGlyph formattedGlyph = formattedGlyphs[n]; IFontStyle fontStyle = FontManager.GetFontStyle(formattedGlyph.format.StyleIndex); float scale = formattedGlyph.format.TextSize * fontStyle.FontScale; chWidth = formattedGlyphs[n].glyph.advanceWidth * scale; float rem = _size.X % chWidth; if (rem < chWidth * .8f) { chWidth -= rem; } else // if it's really close, just skip to the next stop { chWidth += (chWidth - rem); } sizeData.chSize.X = chWidth; sizeData.bbSize.X = chWidth; locData[n] = sizeData; } _size.X += chWidth; } } }
public IFormattedText FormatText(string text, FormatOptions formatOptions) { var colorStack = new Stack<Color>(); colorStack.Push(Color.White); var fontMetrics = m_registeredFonts[GetFontId(formatOptions.Font)]; var ansiFontMetrics = m_registeredFonts[GetFontId(formatOptions.AnsiFont)]; var ascentOffset = Math.Max(fontMetrics.m_ascentInPixels, ansiFontMetrics.m_ascentInPixels) - fontMetrics.m_ascentInPixels; var ansiAscentOffset = Math.Max(fontMetrics.m_ascentInPixels, ansiFontMetrics.m_ascentInPixels) - ansiFontMetrics.m_ascentInPixels; float currentX = 0; float currentY = 0; // ensures the last line string textCopy = text.EndsWith("\n") ? text : text + "\n"; var charArray = textCopy.ToArray(); var glyphs = new List<FormattedGlyph>(); var lines = new List<FormattedText.FormattedLine>(); var maxLineWidth = 0.0f; var lineHeight = Math.Max(fontMetrics.m_fontObject.Height, ansiFontMetrics.m_fontObject.Height); var lineSpacing = formatOptions.LineSpacing + lineHeight; for (int i = 0; i < charArray.Length; ++i) { var ch = charArray[i]; if (ch == ' ') { var fg = new FormattedGlyph { m_pos = new Vector2(currentX, IsAnsiChar(ch) ? ansiAscentOffset : ascentOffset), m_width = ansiFontMetrics.m_spaceWidth, m_color = colorStack.Peek(), m_glyph = ch, }; glyphs.Add(fg); currentX += fg.m_width; } else if (ch == '\x3000') { var fg = new FormattedGlyph { m_pos = new Vector2(currentX, IsAnsiChar(ch) ? ansiAscentOffset : ascentOffset), m_width = ansiFontMetrics.m_fullWidthSpaceWidth, m_color = colorStack.Peek(), m_glyph = ch, }; glyphs.Add(fg); currentX += fg.m_width; } else if (ch == '\t') { var fg = new FormattedGlyph { m_pos = new Vector2(currentX, IsAnsiChar(ch) ? ansiAscentOffset : ascentOffset), m_width = ansiFontMetrics.m_spaceWidth * formatOptions.TabSpaces, m_color = colorStack.Peek(), m_glyph = ch, }; glyphs.Add(fg); currentX += fg.m_width;; } else if (ch == '\n') { var line = new FormattedText.FormattedLine(); line.m_glyphs = glyphs.ToArray(); line.m_offset = new Vector2(0, currentY); var lineWidth = Math.Max(currentX - formatOptions.CharSpacing, 0); maxLineWidth = Math.Max(maxLineWidth, lineWidth); if (formatOptions.Alignment == Alignment.CenterTop || formatOptions.Alignment == Alignment.CenterMiddle || formatOptions.Alignment == Alignment.CenterBottom) { line.m_offset.X = -lineWidth * 0.5f; } else if (formatOptions.Alignment == Alignment.RightTop || formatOptions.Alignment == Alignment.RightMiddle || formatOptions.Alignment == Alignment.RightBottom) { line.m_offset.X = -lineWidth; } currentY += lineSpacing; currentX = 0; glyphs.Clear(); lines.Add(line); } else { if (!formatOptions.DisableRTF && ch == '[' && i + 1 < charArray.Length) { if (charArray[i + 1] != '[') { int j = i + 1; for (; j < charArray.Length; ++j) { if (charArray[j] == ']') { break; } } string token = new string(charArray, i + 1, j - i - 1); if (token.StartsWith("color:", StringComparison.InvariantCultureIgnoreCase)) { string colorCode = token.Substring(6); // use Style library color syntax var color = Style.Values.Color.Parse(colorCode); colorStack.Push(new Color(color.Red, color.Green, color.Blue, color.Alpha)); } else if (token == "/color" && colorStack.Count > 1) { colorStack.Pop(); } i = j; continue; } else { ++i; } } var glyphData = Load(ch, formatOptions); var fg = new FormattedGlyph { m_pos = new Vector2(currentX, IsAnsiChar(ch) ? ansiAscentOffset : ascentOffset), m_width = glyphData.m_glyphSize.Width, m_color = colorStack.Peek(), m_glyph = ch, }; glyphs.Add(fg); currentX += fg.m_width + formatOptions.CharSpacing; } } var offsetY = 0.0f; var textHeight = lines.Count > 0 ? lineSpacing * (lines.Count - 1) + lineHeight : 0; if (formatOptions.Alignment == Alignment.LeftMiddle || formatOptions.Alignment == Alignment.CenterMiddle || formatOptions.Alignment == Alignment.RightMiddle) { offsetY = -textHeight * 0.5f; } else if (formatOptions.Alignment == Alignment.LeftBottom || formatOptions.Alignment == Alignment.CenterBottom || formatOptions.Alignment == Alignment.RightBottom) { offsetY = -textHeight; } return new FormattedText { Text = text, FormatOptions = formatOptions, Offset = new Point(0, offsetY), Size = new Size(maxLineWidth, textHeight), RichTextFormat = !formatOptions.DisableRTF, m_lines = lines.ToArray() }; }