Skip to content

Commit 186bfbe

Browse files
tomcurSoundcreates
andauthored
Add parley_engine usage example (#742)
LLM Contributions: Review Based on #739. This adds a brief introduction and a doctest usage example to `parley_engine`. @Soundcreates proposed the initial wording in #739. I've rebased that to be on top of recent API changes, plus made the doctest compiled (but `no_run`, as otherwise a font needs to be available, which I'm not sure is straightforward with a doctest). Closes #714. **Changelog: None** --------- Co-authored-by: Shantanav <shantanav7@gmail.com>
1 parent 1eaefea commit 186bfbe

2 files changed

Lines changed: 146 additions & 2 deletions

File tree

parley_engine/README.md

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,75 @@ See https://linebender.org/blog/doc-include/ for related discussion. -->
2424

2525
<!-- cargo-rdme start -->
2626

27-
Parley Engine provides low level APIs for implementing text layout.
27+
Parley Engine provides low level APIs for shaping paragraphs of text.
28+
29+
## Usage
30+
31+
Use [`Analyzer`], [`Analysis`], [`Shaper`] and [`ShapedText`] to shape a paragraph of text into
32+
glyphs. Correct reshaping of lines is in progress; in the meantime you can break text at
33+
[`Atom`] or [`ShapedCluster`][crate::shape::ShapedCluster] boundaries.
34+
35+
Text analysis is performed before shaping, and the same source string must be passed to each
36+
stage.
37+
38+
Higher-level users may prefer using [`parley`][parley], which uses this crate and implements
39+
layout and styling.
40+
41+
```rust
42+
let mut analysis = Analysis::default();
43+
let mut analyzer = Analyzer::default();
44+
let mut shaped_text = ShapedText::default();
45+
let mut shaper = Shaper::default();
46+
47+
let text = "The quick brown ثعلب jumps over the lazy dog.";
48+
let char_count = text.chars().count();
49+
let char_style_indices = vec![0; char_count];
50+
51+
analyzer.analyze(text, &AnalysisOptions::default(), &mut analysis);
52+
shaper.shape_text(
53+
text,
54+
&analysis,
55+
&char_style_indices,
56+
[Item {
57+
char_end: char_count.try_into().unwrap(),
58+
options: ShapeOptions {
59+
font_size: 16.0,
60+
language: None,
61+
features: &[],
62+
variations: &[],
63+
},
64+
}],
65+
select_font, // Selects fonts covering each cluster.
66+
&mut shaped_text,
67+
);
68+
69+
for (run_idx, run) in shaped_text.runs().iter().enumerate() {
70+
let slice = shaped_text.run_slice(run_idx as u32);
71+
// You can, for example, measure grapheme advances for hit-testing or
72+
// placing carets.
73+
for atom in slice.atoms_start() {
74+
for grapheme in atom.graphemes_start() {
75+
std::dbg!(grapheme);
76+
}
77+
}
78+
79+
// Or get glyphs for rendering (for simplicity, this iterates clusters in
80+
// logical order, but for rendering you'd want to reorder runs and clusters
81+
// according to their `run.bidi_level`).
82+
for cluster in slice.shaped_clusters_range() {
83+
for glyph in slice.shaped_cluster_glyphs(cluster) {
84+
std::dbg!(glyph);
85+
}
86+
}
87+
}
88+
```
2889

2990
## Features
3091

3192
- `std` (enabled by default): This is currently unused and is provided for forward compatibility.
3293

94+
[parley]: https://docs.rs/parley
95+
3396
<!-- cargo-rdme end -->
3497

3598
## Minimum supported Rust Version (MSRV)

parley_engine/src/lib.rs

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,92 @@
11
// Copyright 2025 the Parley Authors
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33

4-
//! Parley Engine provides low level APIs for implementing text layout.
4+
//! Parley Engine provides low level APIs for shaping paragraphs of text.
5+
//!
6+
//! ## Usage
7+
//!
8+
//! Use [`Analyzer`], [`Analysis`], [`Shaper`] and [`ShapedText`] to shape a paragraph of text into
9+
//! glyphs. Correct reshaping of lines is in progress; in the meantime you can break text at
10+
//! [`Atom`] or [`ShapedCluster`][crate::shape::ShapedCluster] boundaries.
11+
//!
12+
//! Text analysis is performed before shaping, and the same source string must be passed to each
13+
//! stage.
14+
//!
15+
//! Higher-level users may prefer using [`parley`][parley], which uses this crate and implements
16+
//! layout and styling.
17+
//!
18+
//! ```rust,no_run
19+
//! # // We only compile this doctest because we don't have a font available.
20+
//! # use parley_engine::{Analysis, AnalysisOptions, Analyzer, FontInstance, FontSelector, ShapedText, ShapeOptions, Shaper};
21+
//! # use parley_engine::shape::CharCluster;
22+
//! # use parley_engine::itemize::{Item, Segment};
23+
//! #
24+
//! # struct NoFont;
25+
//! # impl FontSelector for NoFont {
26+
//! # fn select_font(
27+
//! # &mut self,
28+
//! # _segment: &Segment,
29+
//! # _options: &ShapeOptions<'_>,
30+
//! # _cluster: &mut CharCluster,
31+
//! # ) -> Option<FontInstance> {
32+
//! # unimplemented!()
33+
//! # }
34+
//! # }
35+
//! #
36+
//! # let select_font = NoFont;
37+
//! let mut analysis = Analysis::default();
38+
//! let mut analyzer = Analyzer::default();
39+
//! let mut shaped_text = ShapedText::default();
40+
//! let mut shaper = Shaper::default();
41+
//!
42+
//! let text = "The quick brown ثعلب jumps over the lazy dog.";
43+
//! let char_count = text.chars().count();
44+
//! let char_style_indices = vec![0; char_count];
45+
//!
46+
//! analyzer.analyze(text, &AnalysisOptions::default(), &mut analysis);
47+
//! shaper.shape_text(
48+
//! text,
49+
//! &analysis,
50+
//! &char_style_indices,
51+
//! [Item {
52+
//! char_end: char_count.try_into().unwrap(),
53+
//! options: ShapeOptions {
54+
//! font_size: 16.0,
55+
//! language: None,
56+
//! features: &[],
57+
//! variations: &[],
58+
//! },
59+
//! }],
60+
//! select_font, // Selects fonts covering each cluster.
61+
//! &mut shaped_text,
62+
//! );
63+
//!
64+
//! for (run_idx, run) in shaped_text.runs().iter().enumerate() {
65+
//! let slice = shaped_text.run_slice(run_idx as u32);
66+
//! // You can, for example, measure grapheme advances for hit-testing or
67+
//! // placing carets.
68+
//! for atom in slice.atoms_start() {
69+
//! for grapheme in atom.graphemes_start() {
70+
//! std::dbg!(grapheme);
71+
//! }
72+
//! }
73+
//!
74+
//! // Or get glyphs for rendering (for simplicity, this iterates clusters in
75+
//! // logical order, but for rendering you'd want to reorder runs and clusters
76+
//! // according to their `run.bidi_level`).
77+
//! for cluster in slice.shaped_clusters_range() {
78+
//! for glyph in slice.shaped_cluster_glyphs(cluster) {
79+
//! std::dbg!(glyph);
80+
//! }
81+
//! }
82+
//! }
83+
//! ```
584
//!
685
//! ## Features
786
//!
887
//! - `std` (enabled by default): This is currently unused and is provided for forward compatibility.
88+
//!
89+
//! [parley]: https://docs.rs/parley
990
1091
// LINEBENDER LINT SET - lib.rs - v3
1192
// See https://linebender.org/wiki/canonical-lints/

0 commit comments

Comments
 (0)