Class Paragraph
A paragraph of styled text, prepared once to be laid out at any width: styles resolved, bidi levels and break opportunities found, text itemized into runs and each run shaped. Immutable and safe to share between threads.
public sealed class Paragraph
- Inheritance
-
Paragraph
- Inherited Members
Examples
// Font families come from a collection; later families cover what the first lacks.
FontCollection fonts = new();
fonts.AddFile(System.IO.Path.Combine(folder, "NotoSans-Regular.ttf"));
fonts.AddFile(System.IO.Path.Combine(folder, "NotoSansArabic-Regular.ttf"));
fonts.TryGetFamily("Noto Sans", out FontFamily latin);
fonts.TryGetFamily("Noto Sans Arabic", out FontFamily arabic);
// Styles nest: the inner span keeps the outer one's size and adds a color and an underline.
RichText text = new RichTextBuilder()
.Append("Peace is ")
.Push(new TextStyle { Size = 20 })
.Append("\u0633\u0644\u0627\u0645 ")
.Push(new TextStyle { Color = new Rgba8(200, 30, 30, 255), Decoration = new TextDecoration() })
.Append("in Arabic")
.Pop()
.Pop()
.AddPlaceholder(new Placeholder(16, 16, PlaceholderAlignment.Middle))
.ToRichText();
ParagraphStyle style = new()
{
DefaultStyle = new TextStyle { FontFamilies = [latin, arabic], Size = 16, LineHeight = LineHeight.Multiple(1.4f) },
Align = TextAlign.Justify,
LineBreaking = new LineBreakOptions(WordBreak: WordBreak.Normal),
};
// Shaped once: lay it out at any width later.
Paragraph paragraph = Paragraph.Create(text, style);
Remarks
Runs split where the bidi level, the script, the font (after fallback) or a style field that changes shaping changes; paint fields never split a run. Hard breaks, other control characters and placeholders are runs of their own without glyphs.
Properties
RichText
Gets the styled text the paragraph was made from.
public RichText RichText { get; }
Property Value
Style
Gets the paragraph style.
public ParagraphStyle Style { get; }
Property Value
Text
Gets the text.
public string Text { get; }
Property Value
Methods
Create(RichText, ParagraphStyle)
Prepares a paragraph of styled text.
public static Paragraph Create(RichText text, ParagraphStyle style)
Parameters
textRichTextThe styled text.
styleParagraphStyleThe paragraph style; text and spans take unset fields from its DefaultStyle.
Returns
- Paragraph
The paragraph.
Examples
// Font families come from a collection; later families cover what the first lacks.
FontCollection fonts = new();
fonts.AddFile(System.IO.Path.Combine(folder, "NotoSans-Regular.ttf"));
fonts.AddFile(System.IO.Path.Combine(folder, "NotoSansArabic-Regular.ttf"));
fonts.TryGetFamily("Noto Sans", out FontFamily latin);
fonts.TryGetFamily("Noto Sans Arabic", out FontFamily arabic);
// Styles nest: the inner span keeps the outer one's size and adds a color and an underline.
RichText text = new RichTextBuilder()
.Append("Peace is ")
.Push(new TextStyle { Size = 20 })
.Append("\u0633\u0644\u0627\u0645 ")
.Push(new TextStyle { Color = new Rgba8(200, 30, 30, 255), Decoration = new TextDecoration() })
.Append("in Arabic")
.Pop()
.Pop()
.AddPlaceholder(new Placeholder(16, 16, PlaceholderAlignment.Middle))
.ToRichText();
ParagraphStyle style = new()
{
DefaultStyle = new TextStyle { FontFamilies = [latin, arabic], Size = 16, LineHeight = LineHeight.Multiple(1.4f) },
Align = TextAlign.Justify,
LineBreaking = new LineBreakOptions(WordBreak: WordBreak.Normal),
};
// Shaped once: lay it out at any width later.
Paragraph paragraph = Paragraph.Create(text, style);
Exceptions
- ArgumentNullException
textorstyleis null.- ArgumentException
Some text has no font families in its style or the default style.
Create(string, ParagraphStyle)
Prepares a plain-text paragraph in the paragraph's default style.
public static Paragraph Create(string text, ParagraphStyle style)
Parameters
textstringThe text.
styleParagraphStyleThe paragraph style; its DefaultStyle must name font families.
Returns
- Paragraph
The paragraph.
Examples
// Font families come from a collection; later families cover what the first lacks.
FontCollection fonts = new();
fonts.AddFile(System.IO.Path.Combine(folder, "NotoSans-Regular.ttf"));
fonts.AddFile(System.IO.Path.Combine(folder, "NotoSansArabic-Regular.ttf"));
fonts.TryGetFamily("Noto Sans", out FontFamily latin);
fonts.TryGetFamily("Noto Sans Arabic", out FontFamily arabic);
// Styles nest: the inner span keeps the outer one's size and adds a color and an underline.
RichText text = new RichTextBuilder()
.Append("Peace is ")
.Push(new TextStyle { Size = 20 })
.Append("\u0633\u0644\u0627\u0645 ")
.Push(new TextStyle { Color = new Rgba8(200, 30, 30, 255), Decoration = new TextDecoration() })
.Append("in Arabic")
.Pop()
.Pop()
.AddPlaceholder(new Placeholder(16, 16, PlaceholderAlignment.Middle))
.ToRichText();
ParagraphStyle style = new()
{
DefaultStyle = new TextStyle { FontFamilies = [latin, arabic], Size = 16, LineHeight = LineHeight.Multiple(1.4f) },
Align = TextAlign.Justify,
LineBreaking = new LineBreakOptions(WordBreak: WordBreak.Normal),
};
// Shaped once: lay it out at any width later.
Paragraph paragraph = Paragraph.Create(text, style);
Exceptions
- ArgumentNullException
textorstyleis null.- ArgumentException
No font families are given.
CreateFlow()
Starts laying the paragraph out one line at a time, each at its own width.
public LineFlow CreateFlow()
Returns
- LineFlow
A flow positioned at the first line.
Examples
// One line at a time, each at its own width: two columns of four lines, the second wrapping around an image.
LineFlow flow = paragraph.CreateFlow();
List<TextLine> first = [], second = [];
while (first.Count < 4 && flow.NextLine(150) is TextLine line)
{
first.Add(line);
}
while (flow.NextLine(second.Count < 2 ? 90 : 150) is TextLine line)
{
second.Add(line); // narrower beside the image
}
GetStyle(int)
Gets the text style a run with StyleIndex (or a decoration with StyleIndex) was laid out in: the default style for 0, else that span's style with the default's values filled in where it sets none.
public TextStyle GetStyle(int styleIndex)
Parameters
Returns
- TextStyle
The style.
Exceptions
- ArgumentOutOfRangeException
styleIndexis not a style of this paragraph.
Layout(float)
Lays the paragraph out at a width.
public TextLayout Layout(float width)
Parameters
widthfloatThe layout box's width in canvas units; PositiveInfinity for no wrapping, in which case the box is as wide as the longest line.
Returns
- TextLayout
The layout.
Examples
ParagraphStyle style = new() { DefaultStyle = new TextStyle { FontFamilies = [family], Size = 16 } };
Paragraph paragraph = Paragraph.Create("A paragraph is shaped once and laid out at any width.", style);
// Each width gives an immutable layout: lines of glyph runs, positioned from the box's top-left corner.
TextLayout narrow = paragraph.Layout(120);
TextLayout wide = paragraph.Layout(400);
foreach (TextLine line in narrow.Lines)
{
string text = paragraph.Text[line.Start..line.End]; // the line's text, its trailing space included
float baseline = line.Baseline; // y of the baseline
foreach (LayoutRun run in line.Runs)
{
GlyphRun? glyphs = run.Glyphs; // glyphs at their layout positions
}
}
// No wrapping: the box is as wide as the longest line.
TextLayout unbounded = paragraph.Layout(float.PositiveInfinity);
Exceptions
- ArgumentOutOfRangeException
widthis negative or not a number.