1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
//! §17.3.2.35 / §17.3.1.13: the unit that inter-character spacing is applied
//! *between*.
//!
//! Two unrelated features insert horizontal space inside a run:
//!
//! ```xml
//! <w:rPr><w:spacing w:val="40"/></w:rPr> <!-- §17.3.2.35: +2pt everywhere -->
//! <w:pPr><w:jc w:val="distribute"/></w:pPr> <!-- §17.3.1.13: share the line's slack -->
//! ```
//!
//! They differ in where the amount comes from — `w:spacing` is authored, while
//! `distribute` is derived per line from the width left over — but they land in
//! the same place, add up (`font.char_spacing + distribution_extra`), and reach
//! the painter as the single `char_spacing` field of `DrawCommand::Text`.
//!
//! So both need one answer to one question: **where may space go?**
//!
//! # The unit is a grapheme cluster
//!
//! This module owns that answer, and it is the UAX #29 extended grapheme
//! cluster. [`unit_count`] says how many helpings of spacing a run earns;
//! [`units`] says which substrings the painter draws between them. Layout and
//! paint call the same two functions, which is the whole reason the module
//! exists — a run that measures to one width and paints at another leaves its
//! underline, its run border and its hyperlink rect behind.
//!
//! Counting Unicode **scalars** instead, which is what this code did until
//! issue #82, breaks any cluster spelled with more than one:
//!
//! | Text | Scalars | Clusters | Scalar counting drew |
//! |---|---|---|---|
//! | `e` + `U+0301` | 2 | 1 | the acute accent a spacing step right of its `e` |
//! | `1` `U+FE0F` `U+20E3` | 3 | 1 | a digit, a gap, and a bare keycap ring |
//! | `U+1F1E9` `U+1F1EA` | 2 | 1 | two lettered squares instead of a German flag |
//! | `U+0915` `U+094D` `U+0937` | 3 | 1 | a conjunct pulled apart at its virama |
//!
//! # Who asks
//!
//! | Caller | Uses it for |
//! |---|---|
//! | `layout::measurer::measure` | §17.3.2.35 — adds `char_spacing × unit_count` to the measured width |
//! | `layout::paragraph::line_emit::distribution_unit_count` | §17.3.1.13 — how many units a fragment contributes, hence how many gaps a line has to fill |
//! | `render::painter`, the `char_spacing != 0` arm | draws one unit at a time, advancing by the unit's own advance plus `char_spacing` |
//! | `layout::fragment::split` | per-unit fragments for an over-wide word |
//!
//! The last is not spacing at all, and is governed here for the same reason:
//! splitting an over-wide word per *scalar* gave the line-fitter permission to
//! break between a letter and its own accent and carry the mark to the next
//! line.
//!
//! # Why not the shaped cluster
//!
//! A shaping engine reports finer, script-aware boundaries, and issue #82 asks
//! for them. For **most** text they would be boundaries the painter cannot
//! honour: `draw_str` and `TextBlob::from_str` map codepoints to glyphs through
//! the cmap alone, with no GSUB, and that is still the path every Latin,
//! Cyrillic, Greek, CJK, Hebrew and Thai run takes.
//!
//! Since issue #131 it is no longer the path *every* run takes. A run in a
//! cursive-joining script — the ones [`crate::render::shape::needs_shaping`]
//! picks out — is shaped through HarfBuzz and painted with `draw_glyphs_at`,
//! so for those runs a shaped cluster is a boundary the painter could honour.
//! This module does not yet offer one, and the consequence is stated at the
//! seam that decides it (`layout::fragment::shape`): §17.3.2.35 `w:spacing` and
//! §17.3.1.13 `distribute` are **not applied to a shaped run**, because
//! inserting space at grapheme-cluster boundaries that shaping has since
//! ligated across would put it in the wrong places, and neither measurement
//! nor paint adds it.
//!
//! So the seam this module's own doc used to point at is now half-crossed. What
//! remains for issue #82 is the other half: make the unit here the shaped
//! cluster for a shaped run, and every caller above stays as it is. Indic
//! reordering waits on the same change — README tracks it as its own row, and
//! that is why it is not simply another entry in
//! [`crate::render::shape::needs_shaping`]'s predicate.
use UnicodeSegmentation;
/// True when one byte of `text` is exactly one spacing unit, so the grapheme
/// segmenter can be skipped.
///
/// `\r` is excluded even though it is ASCII: UAX #29 GB3 keeps CRLF together as
/// a *single* cluster. Text reaching layout has its C0 controls stripped
/// (`fragment::text::emit_text_fragments`), but this module is also called from
/// the painter and must not inherit that assumption.
/// The spacing units of `text`, in order — the substrings that spacing is
/// inserted *between*, and that the painter draws one at a time.
+ '_
/// How many spacing units `text` holds — i.e. how many times a per-unit amount
/// is added when the run is measured.