Parley API
Overview
Parley is a Rust library for rich text layout. It handles text shaping, line breaking, bidi resolution, styling, and cursor/selection management. For rendering, use parley_draw::GlyphRunBuilder with vello_cpu or another renderer implementing GlyphRenderer.
Brush type: Layout is generic over a Brush type. Any type implementing Clone + PartialEq + Default + Debug works. Define a custom struct wrapping your renderer's color type.
When to Use
- •Laying out styled text with fonts
- •Rendering glyphs via
parley_draw::GlyphRunBuilder - •Implementing text editors (cursor, selection)
- •Hit testing text coordinates
- •Line breaking and alignment
Quick Reference
| Task | Type/Method |
|---|---|
| Font database | FontContext::new() |
| Layout scratch space | LayoutContext::new() |
| Build with ranges | layout_cx.ranged_builder() |
| Build with tree | layout_cx.tree_builder() |
| Break lines | layout.break_all_lines(max_width) |
| Align text | layout.align(width, Alignment::*, AlignmentOptions::default()) |
| Iterate lines | layout.lines() |
| Get positioned glyphs | glyph_run.positioned_glyphs() |
| Render glyphs | GlyphRunBuilder::new(...).fill_glyphs(...) |
| Cursor from click | Cursor::from_point(&layout, x, y) |
| Select word | Selection::word_from_point(&layout, x, y) |
Core Pattern (with parley_draw + vello_cpu)
rust
use parley::{
Alignment, AlignmentOptions, FontContext, FontWeight, GenericFamily,
Layout, LayoutContext, LineHeight, PositionedLayoutItem, StyleProperty,
};
use parley_draw::{Glyph, GlyphCaches, GlyphRunBuilder};
use vello_cpu::{Pixmap, RenderContext, kurbo, peniko::Color};
// Custom brush type (required - wraps your renderer's color)
#[derive(Clone, Copy, Debug, PartialEq, Default)]
struct ColorBrush { color: Color }
// Create once per app/thread
let mut font_cx = FontContext::new();
let mut layout_cx = LayoutContext::new();
let mut glyph_caches = GlyphCaches::new();
// Build layout
let mut builder = layout_cx.ranged_builder(&mut font_cx, &text, 1.0, true);
builder.push_default(StyleProperty::Brush(ColorBrush { color: Color::BLACK }));
builder.push_default(GenericFamily::SystemUi);
builder.push_default(StyleProperty::FontSize(16.0));
let mut layout: Layout<ColorBrush> = builder.build(&text);
layout.break_all_lines(Some(400.0));
layout.align(Some(400.0), Alignment::Start, AlignmentOptions::default());
// Render using GlyphRunBuilder
for line in layout.lines() {
for item in line.items() {
let PositionedLayoutItem::GlyphRun(glyph_run) = item else { continue };
renderer.set_paint(glyph_run.style().brush.color);
let run = glyph_run.run();
GlyphRunBuilder::new(run.font().clone(), *renderer.transform(), &mut renderer)
.font_size(run.font_size())
.hint(true)
.normalized_coords(run.normalized_coords())
.fill_glyphs(
glyph_run.positioned_glyphs().map(|g| parley_draw::Glyph {
id: g.id, x: g.x, y: g.y,
}),
&mut glyph_caches,
);
}
}
glyph_caches.maintain(); // Call after rendering frame
// Output to PNG (vello_cpu)
let mut pixmap = Pixmap::new(width, height);
renderer.render_to_pixmap(&mut pixmap);
let png_data = pixmap.into_png().unwrap();
Two Builder Styles
RangedBuilder - Flat style spans
rust
let mut builder = layout_cx.ranged_builder(&mut font_cx, &text, scale, quantize); builder.push_default(StyleProperty::Brush(MyBrush::default())); builder.push_default(StyleProperty::FontSize(16.0)); builder.push(StyleProperty::FontWeight(FontWeight::new(700.0)), 0..10); let layout: Layout<MyBrush> = builder.build(&text);
TreeBuilder - Nested style hierarchy
rust
let root_style = TextStyle {
font_size: 16.0,
brush: MyBrush::default(),
..Default::default()
};
let mut builder = layout_cx.tree_builder(&mut font_cx, scale, quantize, &root_style);
builder.push_style_modification_span(&[StyleProperty::FontWeight(FontWeight::new(700.0))]);
builder.push_text("Bold ");
builder.pop_style_span();
builder.push_text("normal");
let (layout, text) = builder.build();
Style Properties
rust
StyleProperty::Brush(my_brush) // Custom Brush type StyleProperty::FontSize(24.0) StyleProperty::FontWeight(FontWeight::new(700.0)) // 100-900 StyleProperty::FontStyle(FontStyle::Italic) StyleProperty::Underline(true) StyleProperty::Strikethrough(true) StyleProperty::LineHeight(LineHeight::FontSizeRelative(1.5)) StyleProperty::LetterSpacing(2.0) StyleProperty::WordSpacing(4.0) StyleProperty::WordBreak(WordBreak::Normal) StyleProperty::OverflowWrap(OverflowWrap::Normal) GenericFamily::SystemUi // Converts to StyleProperty automatically
Cursor & Selection
rust
use parley::{Cursor, Selection, Affinity};
// Cursor from text position
let cursor = Cursor::from_byte_index(&layout, byte_index, Affinity::Downstream);
// Cursor from mouse click
let cursor = Cursor::from_point(&layout, x, y);
let text_pos = cursor.index();
// Navigation
let next = cursor.next_logical_word(&layout);
let prev = cursor.previous_logical_word(&layout);
// Selection
let selection = Selection::word_from_point(&layout, x, y);
let range = selection.text_range(); // Range<usize>
let boxes = selection.geometry(&layout); // Vec<(BoundingBox, line_index)>
Inline Boxes
rust
use parley::InlineBox;
builder.push_inline_box(InlineBox {
id: 1,
index: 5, // Insert at byte offset 5
width: 32.0,
height: 32.0,
});
Line Metrics
rust
for line in layout.lines() {
let metrics = line.metrics();
// metrics.ascent, metrics.descent, metrics.leading
// metrics.baseline, metrics.advance
// metrics.min_coord, metrics.max_coord
}
Common Mistakes
| Mistake | Fix |
|---|---|
| Using skrifa directly for rendering | Use parley_draw::GlyphRunBuilder |
Using [u8; 4] as Brush | Define custom struct implementing Clone+PartialEq+Default+Debug |
Forgetting glyph_caches.maintain() | Call after each frame to evict unused entries |
| Creating FontContext per layout | Create once, reuse |
Forgetting break_all_lines() | Call before rendering |
Using glyphs() instead of positioned_glyphs() | Use positioned_glyphs() for pre-computed positions |
Key Types
- •FontContext - Font database (create once)
- •LayoutContext - Scratch space (create once)
- •Layout<B> - Computed layout, generic over Brush type
- •GlyphCaches - Cache for glyph outlines/hints (create once, call
.maintain()) - •GlyphRunBuilder - Renders glyph runs via
GlyphRenderertrait - •Cursor / Selection - Text editing primitives