/// <summary> /// Function to perform word wrapping on a string based on this font. /// </summary> /// <param name="text">The text to word wrap.</param> /// <param name="wordWrapWidth">The maximum width, in pixels, that must be met for word wrapping to occur.</param> /// <returns>The string with word wrapping.</returns> /// <remarks> /// <para> /// The <paramref name="wordWrapWidth"/> is the maximum number of pixels required for word wrapping, if an individual font glyph cell width (the <see cref="GorgonGlyph.Offset"/> + /// <see cref="GorgonGlyph.Advance"/>) exceeds that of the <paramref name="wordWrapWidth"/>, then the parameter value is updated to glyph cell width. /// </para> /// </remarks> public string WordWrap(string text, float wordWrapWidth) { if (string.IsNullOrEmpty(text)) { return(text); } var wordText = new StringBuilder(text); if (!Glyphs.TryGetValue(Info.DefaultCharacter, out GorgonGlyph defaultGlyph)) { throw new GorgonException(GorgonResult.CannotEnumerate, string.Format(Resources.GORGFX_ERR_FONT_DEFAULT_CHAR_NOT_VALID, Info.DefaultCharacter)); } int maxLength = wordText.Length; int index = 0; float position = 0.0f; bool firstChar = true; while (index < maxLength) { char character = wordText[index]; // Don't count newline or carriage return. if ((character == '\n') || (character == '\r')) { firstChar = true; position = 0; ++index; continue; } if (!Glyphs.TryGetValue(character, out GorgonGlyph glyph)) { glyph = defaultGlyph; } float glyphCellWidth = glyph.Advance; if (firstChar) { glyphCellWidth += glyph.Offset.X; firstChar = false; } // If we're using kerning, then adjust for the kerning value. if ((Info.UseKerningPairs) && (index < maxLength - 1)) { if (KerningPairs.TryGetValue(new GorgonKerningPair(character, wordText[index + 1]), out int kernValue)) { glyphCellWidth += kernValue; } } position += glyphCellWidth; // Update the word wrap boundary if the cell size exceeds it. if (glyphCellWidth > wordWrapWidth) { wordWrapWidth = glyphCellWidth; } // We're not at the break yet. if (position < wordWrapWidth) { ++index; continue; } int whiteSpaceIndex = index; // If we hit the max width, then we need to find the previous whitespace and inject a newline. while ((whiteSpaceIndex <= index) && (whiteSpaceIndex >= 0)) { char breakChar = wordText[whiteSpaceIndex]; if ((char.IsWhiteSpace(breakChar)) && (breakChar != '\n') && (breakChar != '\r')) { index = whiteSpaceIndex; break; } --whiteSpaceIndex; } // If we're at the beginning, then we cannot wrap this text, so we'll break it at the border specified. if (index != whiteSpaceIndex) { if (index != 0) { wordText.Insert(index, '\n'); maxLength = wordText.Length; ++index; } position = 0; firstChar = true; // Move to next character. ++index; continue; } // Extract the space. wordText[whiteSpaceIndex] = '\n'; position = 0; firstChar = true; index = whiteSpaceIndex + 1; } return(wordText.ToString()); }
/// <summary> /// Function to measure the width of an individual line of text. /// </summary> /// <param name="line">The line to measure.</param> /// <param name="useOutline"><b>true</b> to use the font outline, <b>false</b> to disregard it.</param> /// <returns>The width of the line.</returns> private float GetLineWidth(string line, bool useOutline) { float size = 0; bool firstChar = true; if (!Glyphs.TryGetValue(Info.DefaultCharacter, out GorgonGlyph defaultGlyph)) { throw new GorgonException(GorgonResult.CannotEnumerate, string.Format(Resources.GORGFX_ERR_FONT_DEFAULT_CHAR_NOT_VALID, Info.DefaultCharacter)); } for (int i = 0; i < line.Length; i++) { char character = line[i]; if (!Glyphs.TryGetValue(character, out GorgonGlyph glyph)) { glyph = defaultGlyph; } // Skip out on carriage returns and newlines. if ((character == '\r') || (character == '\n')) { continue; } // Whitespace will use the glyph width. if (char.IsWhiteSpace(character)) { size += glyph.Advance; continue; } // Include the initial offset. if (firstChar) { size += (useOutline && glyph.OutlineCoordinates.Width > 0) ? glyph.OutlineOffset.X : glyph.Offset.X; firstChar = false; } size += glyph.Advance; if (!Info.UseKerningPairs) { continue; } if ((i == line.Length - 1) || (KerningPairs.Count == 0)) { continue; } var kerning = new GorgonKerningPair(character, line[i + 1]); if (KerningPairs.TryGetValue(kerning, out int kernAmount)) { size += kernAmount; } } return(size); }