1use crate::fonts::{FONT_REF_LEN, FONT_REF_SYSTEM, FontRefs};
22use crate::protocol::ProtocolError;
23use crate::schema::{OverflowWrap, TypeRole, WordBreak};
24use crate::spans::{SPAN_FONT_ENTRY_LEN, SPAN_FONT_OFFSET_AT, SPAN_LEN, TextSpans};
25use std::marker::PhantomData;
26
27pub const MEASURE_RECORD_LEN: usize = 72;
29pub const MEASURE_RESULT_LEN: usize = 32;
31
32pub const MEASURE_TEXT: u16 = 1;
34pub const MEASURE_NODE: u16 = 2;
36
37pub const MEASURE_KIND_AT: usize = 0;
39
40pub const TEXT_TYPE_ROLE_AT: usize = 2;
41pub const TEXT_TEXT_OFFSET_AT: usize = 4;
42pub const TEXT_TEXT_LENGTH_AT: usize = 8;
43pub const TEXT_SPANS_OFFSET_AT: usize = 12;
44pub const TEXT_SPANS_COUNT_AT: usize = 16;
45pub const TEXT_SPAN_FONTS_OFFSET_AT: usize = 20;
46pub const TEXT_SPAN_FONTS_COUNT_AT: usize = 24;
47pub const TEXT_FONT_OFFSET_AT: usize = 28;
48pub const TEXT_FONT_COUNT_AT: usize = 32;
49pub const TEXT_FONT_SIZE_AT: usize = 36;
50pub const TEXT_FONT_WEIGHT_AT: usize = 40;
51pub const TEXT_ITALIC_AT: usize = 42;
52pub const TEXT_WRAP_AT: usize = 43;
53pub const TEXT_LETTER_SPACING_AT: usize = 44;
54pub const TEXT_LINE_HEIGHT_AT: usize = 48;
55pub const TEXT_MAX_LINES_AT: usize = 52;
56pub const TEXT_TAB_SIZE_AT: usize = 56;
57pub const TEXT_WORD_BREAK_AT: usize = 57;
58pub const TEXT_OVERFLOW_WRAP_AT: usize = 58;
59pub const TEXT_ABSOLUTE_SIZE_AT: usize = 59;
60pub const TEXT_CONSTRAINT_AT: usize = 60;
61pub const TEXT_WIDTH_AT: usize = 64;
62pub const TEXT_ZOOM_AT: usize = 68;
63
64pub const NODE_ID_AT: usize = 4;
65pub const NODE_MIN_WIDTH_AT: usize = 8;
66pub const NODE_MAX_WIDTH_AT: usize = 12;
67pub const NODE_MIN_HEIGHT_AT: usize = 16;
68pub const NODE_MAX_HEIGHT_AT: usize = 20;
69pub const NODE_ZOOM_AT: usize = 24;
70
71pub const CONSTRAINT_MIN_CONTENT: u32 = 1;
73pub const CONSTRAINT_MAX_CONTENT: u32 = 2;
75pub const CONSTRAINT_AT_MOST: u32 = 3;
77
78pub const RESULT_WIDTH_AT: usize = 0;
80pub const RESULT_HEIGHT_AT: usize = 4;
81pub const RESULT_FIRST_BASELINE_AT: usize = 8;
82pub const RESULT_LAST_BASELINE_AT: usize = 12;
83pub const RESULT_LAST_LINE_WIDTH_AT: usize = 16;
84pub const RESULT_LINE_COUNT_AT: usize = 20;
85pub const RESULT_FLAGS_AT: usize = 24;
86pub const RESULT_STATUS_AT: usize = 28;
87
88pub const FLAG_TRUNCATED: u32 = 1;
90
91pub const RESULT_OK: u32 = 0;
92pub const RESULT_UNKNOWN_NODE: u32 = 1;
95pub const RESULT_MALFORMED: u32 = 2;
97
98pub const MEASURE_UNREADABLE: i32 = -1;
100pub const MEASURE_OFF_UI_THREAD: i32 = -4;
103pub const MEASURE_UNAVAILABLE: i32 = -5;
107
108pub(crate) const SCHEMA_DESCRIPTOR: &str = concat!(
111 "measure=v1;record=72;result=32;",
112 "text=kind@0,type_role@2,text@4,spans@12,span_fonts@20,font@28,font_size@36,",
113 "font_weight@40,italic@42,wrap@43,letter_spacing@44,line_height@48,max_lines@52,",
114 "tab_size@56,word_break@57,overflow_wrap@58,absolute_size@59,constraint@60,width@64,",
115 "zoom@68;",
116 "node=kind@0,node@4,min_width@8,max_width@12,min_height@16,max_height@20,zoom@24;",
117 "result=width@0,height@4,first_baseline@8,last_baseline@12,last_line_width@16,",
118 "line_count@20,flags@24,status@28;",
119 "fontref=12,kind@0,value@4,length@8;spanfont=12,span@0,offset@4,count@8;",
120);
121
122#[repr(C)]
124#[derive(Clone, Copy, Debug, Default, PartialEq)]
125pub struct MeasureResult {
126 pub width: f32,
127 pub height: f32,
128 pub first_baseline: f32,
130 pub last_baseline: f32,
131 pub last_line_width: f32,
134 pub line_count: u32,
136 pub flags: u32,
137 pub status: u32,
138}
139
140const _: () = assert!(std::mem::size_of::<MeasureResult>() == MEASURE_RESULT_LEN);
141
142impl MeasureResult {
143 pub const REFUSED: Self = Self {
146 width: 0.0,
147 height: 0.0,
148 first_baseline: f32::NAN,
149 last_baseline: f32::NAN,
150 last_line_width: f32::NAN,
151 line_count: 0,
152 flags: 0,
153 status: RESULT_MALFORMED,
154 };
155
156 pub fn status(&self) -> MeasureStatus {
157 match self.status {
158 RESULT_OK => MeasureStatus::Ok,
159 RESULT_UNKNOWN_NODE => MeasureStatus::UnknownNode,
160 RESULT_MALFORMED => MeasureStatus::Malformed,
161 other => MeasureStatus::Other(other),
162 }
163 }
164
165 pub fn truncated(&self) -> bool {
167 self.flags & FLAG_TRUNCATED != 0
168 }
169}
170
171#[derive(Clone, Copy, Debug, Eq, PartialEq)]
173pub enum MeasureStatus {
174 Ok,
175 UnknownNode,
176 Malformed,
177 Other(u32),
179}
180
181#[derive(Clone, Copy, Debug, PartialEq)]
183pub enum TextConstraint {
184 MinContent,
186 MaxContent,
188 AtMost(f32),
190}
191
192#[derive(Clone, Debug, PartialEq)]
198pub struct TextStyle {
199 pub type_role: TypeRole,
200 pub font: FontRefs,
202 pub font_size: Option<f32>,
204 pub font_weight: Option<u16>,
206 pub italic: bool,
207 pub letter_spacing: Option<f32>,
209 pub line_height: Option<f32>,
211 pub max_lines: Option<u32>,
213 pub wrap: bool,
215 pub tab_size: u8,
217 pub word_break: Option<WordBreak>,
218 pub overflow_wrap: Option<OverflowWrap>,
219 pub absolute_size: bool,
221}
222
223impl Default for TextStyle {
224 fn default() -> Self {
225 Self {
226 type_role: TypeRole::Body,
227 font: FontRefs::default(),
228 font_size: None,
229 font_weight: None,
230 italic: false,
231 letter_spacing: None,
232 line_height: None,
233 max_lines: None,
234 wrap: true,
235 tab_size: 8,
236 word_break: None,
237 overflow_wrap: None,
238 absolute_size: false,
239 }
240 }
241}
242
243impl TextStyle {
244 pub fn role(role: TypeRole) -> Self {
246 Self {
247 type_role: role,
248 ..Self::default()
249 }
250 }
251}
252
253#[derive(Clone, Copy, Debug, PartialEq)]
255pub struct NodeConstraints {
256 pub min_width: f32,
257 pub max_width: f32,
258 pub min_height: f32,
259 pub max_height: f32,
260}
261
262impl NodeConstraints {
263 pub const UNBOUNDED: Self = Self {
265 min_width: 0.0,
266 max_width: f32::INFINITY,
267 min_height: 0.0,
268 max_height: f32::INFINITY,
269 };
270
271 pub const fn width(width: f32) -> Self {
273 Self {
274 min_width: width,
275 max_width: width,
276 min_height: 0.0,
277 max_height: f32::INFINITY,
278 }
279 }
280}
281
282#[derive(Clone, Copy, Debug)]
284struct PendingRecord {
285 bytes: [u8; MEASURE_RECORD_LEN],
286}
287
288#[derive(Debug, Default)]
293pub struct MeasureRequests {
294 records: Vec<PendingRecord>,
295 payload: Vec<u8>,
298 payload_patches: Vec<u32>,
301 buffer: Vec<u8>,
303 zoom: Option<f32>,
305}
306
307impl MeasureRequests {
308 pub fn new() -> Self {
309 Self::default()
310 }
311
312 pub fn set_zoom(&mut self, zoom: f32) {
320 self.zoom = Some(zoom);
321 }
322
323 pub fn clear(&mut self) {
325 self.records.clear();
326 self.payload.clear();
327 self.payload_patches.clear();
328 self.buffer.clear();
329 }
330
331 pub fn len(&self) -> usize {
333 self.records.len()
334 }
335
336 pub fn is_empty(&self) -> bool {
337 self.records.is_empty()
338 }
339
340 pub fn text(
343 &mut self,
344 text: &str,
345 spans: &TextSpans,
346 style: &TextStyle,
347 constraint: TextConstraint,
348 ) -> usize {
349 let mut record = [0u8; MEASURE_RECORD_LEN];
350 put_u16(&mut record, MEASURE_KIND_AT, MEASURE_TEXT);
351 put_u16(&mut record, TEXT_TYPE_ROLE_AT, style.type_role as u16);
352
353 let text_at = self.payload.len() as u32;
354 self.payload.extend_from_slice(text.as_bytes());
355 put_u32(&mut record, TEXT_TEXT_OFFSET_AT, text_at);
356 put_u32(&mut record, TEXT_TEXT_LENGTH_AT, text.len() as u32);
357
358 let runs = spans.as_bytes();
359 let spans_at = self.payload.len() as u32;
360 self.payload.extend_from_slice(runs);
361 put_u32(&mut record, TEXT_SPANS_OFFSET_AT, spans_at);
362 put_u32(
363 &mut record,
364 TEXT_SPANS_COUNT_AT,
365 (runs.len() / SPAN_LEN) as u32,
366 );
367
368 let (entries_at, entries) = self.copy_span_font_table(spans.font_table());
371 put_u32(&mut record, TEXT_SPAN_FONTS_OFFSET_AT, entries_at);
372 put_u32(&mut record, TEXT_SPAN_FONTS_COUNT_AT, entries);
373 let (font_at, fonts) = self.copy_font_list(style.font.as_bytes());
374 put_u32(&mut record, TEXT_FONT_OFFSET_AT, font_at);
375 put_u32(&mut record, TEXT_FONT_COUNT_AT, fonts);
376
377 put_f32(
378 &mut record,
379 TEXT_FONT_SIZE_AT,
380 style.font_size.unwrap_or(0.0),
381 );
382 put_u16(
383 &mut record,
384 TEXT_FONT_WEIGHT_AT,
385 style.font_weight.unwrap_or(0),
386 );
387 record[TEXT_ITALIC_AT] = u8::from(style.italic);
388 record[TEXT_WRAP_AT] = u8::from(style.wrap);
389 put_f32(
390 &mut record,
391 TEXT_LETTER_SPACING_AT,
392 style.letter_spacing.unwrap_or(f32::NAN),
393 );
394 put_f32(
395 &mut record,
396 TEXT_LINE_HEIGHT_AT,
397 style.line_height.unwrap_or(f32::NAN),
398 );
399 put_u32(&mut record, TEXT_MAX_LINES_AT, style.max_lines.unwrap_or(0));
400 record[TEXT_TAB_SIZE_AT] = style.tab_size;
401 record[TEXT_WORD_BREAK_AT] = style.word_break.map_or(0, |value| value as u8);
402 record[TEXT_OVERFLOW_WRAP_AT] = style.overflow_wrap.map_or(0, |value| value as u8);
403 record[TEXT_ABSOLUTE_SIZE_AT] = u8::from(style.absolute_size);
404 let (constraint, width) = match constraint {
405 TextConstraint::MinContent => (CONSTRAINT_MIN_CONTENT, 0.0),
406 TextConstraint::MaxContent => (CONSTRAINT_MAX_CONTENT, 0.0),
407 TextConstraint::AtMost(width) => (CONSTRAINT_AT_MOST, width),
408 };
409 put_u32(&mut record, TEXT_CONSTRAINT_AT, constraint);
410 put_f32(&mut record, TEXT_WIDTH_AT, width);
411 put_f32(&mut record, TEXT_ZOOM_AT, self.zoom.unwrap_or(0.0));
412 self.records.push(PendingRecord { bytes: record });
413 self.records.len() - 1
414 }
415
416 pub fn node(&mut self, node_id: u32, constraints: NodeConstraints) -> usize {
419 let mut record = [0u8; MEASURE_RECORD_LEN];
420 put_u16(&mut record, MEASURE_KIND_AT, MEASURE_NODE);
421 put_u32(&mut record, NODE_ID_AT, node_id);
422 put_f32(&mut record, NODE_MIN_WIDTH_AT, constraints.min_width);
423 put_f32(&mut record, NODE_MAX_WIDTH_AT, constraints.max_width);
424 put_f32(&mut record, NODE_MIN_HEIGHT_AT, constraints.min_height);
425 put_f32(&mut record, NODE_MAX_HEIGHT_AT, constraints.max_height);
426 put_f32(&mut record, NODE_ZOOM_AT, self.zoom.unwrap_or(0.0));
427 self.records.push(PendingRecord { bytes: record });
428 self.records.len() - 1
429 }
430
431 fn copy_font_list(&mut self, blob: &[u8]) -> (u32, u32) {
435 let at = self.payload.len();
436 if blob.len() < 4 {
437 return (at as u32, 0);
438 }
439 let count = read_u32(blob, 0);
440 self.payload.extend_from_slice(&blob[4..]);
441 let moved = (at as u32).wrapping_sub(4);
443 self.move_font_records(at, count, moved);
444 (at as u32, count)
445 }
446
447 fn copy_span_font_table(&mut self, table: &[u8]) -> (u32, u32) {
450 let at = self.payload.len();
451 if table.len() < 4 {
452 return (at as u32, 0);
453 }
454 let entries = read_u32(table, 0);
455 self.payload.extend_from_slice(&table[4..]);
456 let moved = (at as u32).wrapping_sub(4);
457 for entry in 0..entries as usize {
458 let field = at + entry * SPAN_FONT_ENTRY_LEN + SPAN_FONT_OFFSET_AT;
459 if field + 4 > self.payload.len() {
460 break;
461 }
462 let list = read_u32(&self.payload, field).wrapping_add(moved);
463 self.payload[field..field + 4].copy_from_slice(&list.to_le_bytes());
464 self.payload_patches.push(field as u32);
465 let count = read_u32(&self.payload, at + entry * SPAN_FONT_ENTRY_LEN + 8);
466 self.move_font_records(list as usize, count, moved);
467 }
468 (at as u32, entries)
469 }
470
471 fn move_font_records(&mut self, at: usize, count: u32, moved: u32) {
474 for index in 0..count as usize {
475 let record = at + index * FONT_REF_LEN;
476 if record + FONT_REF_LEN > self.payload.len() {
477 break;
478 }
479 if read_u32(&self.payload, record) != FONT_REF_SYSTEM {
480 continue;
481 }
482 let field = record + 4;
483 let name = read_u32(&self.payload, field).wrapping_add(moved);
484 self.payload[field..field + 4].copy_from_slice(&name.to_le_bytes());
485 self.payload_patches.push(field as u32);
486 }
487 }
488
489 pub fn encoded(&mut self) -> &[u8] {
492 self.finish().0
493 }
494
495 fn finish(&mut self) -> (&[u8], u32) {
498 let base = (self.records.len() * MEASURE_RECORD_LEN) as u32;
499 self.buffer.clear();
500 self.buffer.reserve(base as usize + self.payload.len());
501 for record in &self.records {
502 let mut bytes = record.bytes;
503 if u16::from_le_bytes([bytes[0], bytes[1]]) == MEASURE_TEXT {
504 for field in [
505 TEXT_TEXT_OFFSET_AT,
506 TEXT_SPANS_OFFSET_AT,
507 TEXT_SPAN_FONTS_OFFSET_AT,
508 TEXT_FONT_OFFSET_AT,
509 ] {
510 let value = read_u32(&bytes, field) + base;
511 put_u32(&mut bytes, field, value);
512 }
513 }
514 self.buffer.extend_from_slice(&bytes);
515 }
516 let payload_at = self.buffer.len();
517 self.buffer.extend_from_slice(&self.payload);
518 for &patch in &self.payload_patches {
519 let at = payload_at + patch as usize;
520 let value = read_u32(&self.buffer, at) + base;
521 self.buffer[at..at + 4].copy_from_slice(&value.to_le_bytes());
522 }
523 (&self.buffer, self.records.len() as u32)
524 }
525}
526
527pub struct Measurer {
532 _on_this_thread: PhantomData<*const ()>,
533}
534
535#[derive(Clone, Debug, Eq, PartialEq)]
537pub enum MeasureError {
538 OutsideHostCall,
540 Refused(ProtocolError),
543}
544
545pub fn measurer() -> Result<Measurer, MeasureError> {
548 if crate::boundary::in_host_call() {
549 Ok(Measurer {
550 _on_this_thread: PhantomData,
551 })
552 } else {
553 Err(MeasureError::OutsideHostCall)
554 }
555}
556
557impl Measurer {
558 pub fn measure(
566 &mut self,
567 requests: &mut MeasureRequests,
568 results: &mut Vec<MeasureResult>,
569 ) -> Result<(), MeasureError> {
570 let (buffer, count) = requests.finish();
571 results.clear();
572 results.resize(count as usize, MeasureResult::default());
573 self.measure_encoded(buffer, count, results)
574 }
575
576 pub fn measure_encoded(
579 &mut self,
580 buffer: &[u8],
581 count: u32,
582 results: &mut [MeasureResult],
583 ) -> Result<(), MeasureError> {
584 if results.len() < count as usize {
585 return Err(MeasureError::Refused(ProtocolError::LengthOverflow));
586 }
587 if count == 0 {
588 return Ok(());
589 }
590 let length = u32::try_from(buffer.len())
591 .map_err(|_| MeasureError::Refused(ProtocolError::LengthOverflow))?;
592 let status =
593 crate::boundary::renderer_measure(buffer.as_ptr(), length, count, results.as_mut_ptr());
594 if status < 0 {
595 for result in results.iter_mut().take(count as usize) {
596 *result = MeasureResult::REFUSED;
597 }
598 report_refusal(status);
599 return Err(MeasureError::Refused(ProtocolError::MeasureRefused(status)));
600 }
601 Ok(())
602 }
603}
604
605fn report_refusal(status: i32) {
609 use std::sync::atomic::{AtomicU32, Ordering};
610 static SAID: AtomicU32 = AtomicU32::new(0);
611 let bit = 1u32 << (status.unsigned_abs().min(31));
612 if SAID.fetch_or(bit, Ordering::Relaxed) & bit != 0 {
613 return;
614 }
615 let reason = match status {
616 MEASURE_UNREADABLE => "the request buffer could not be read",
617 MEASURE_OFF_UI_THREAD => "the call was made off the Renderer's UI thread",
618 MEASURE_UNAVAILABLE => {
619 "the Renderer has nothing to measure with yet (no first frame, or no renderer)"
620 }
621 _ => "the Renderer gave no reason",
622 };
623 eprintln!(
624 "compose-rust: a measure call was refused with status {status}: {reason}. Every \
625 size in it is zero and the layout carried on."
626 );
627}
628
629fn put_u16(bytes: &mut [u8], at: usize, value: u16) {
630 bytes[at..at + 2].copy_from_slice(&value.to_le_bytes());
631}
632
633fn put_u32(bytes: &mut [u8], at: usize, value: u32) {
634 bytes[at..at + 4].copy_from_slice(&value.to_le_bytes());
635}
636
637fn put_f32(bytes: &mut [u8], at: usize, value: f32) {
638 bytes[at..at + 4].copy_from_slice(&value.to_bits().to_le_bytes());
639}
640
641fn read_u32(bytes: &[u8], at: usize) -> u32 {
642 u32::from_le_bytes([bytes[at], bytes[at + 1], bytes[at + 2], bytes[at + 3]])
643}
644
645pub const FAKE_FONT_SIZE: f32 = 14.0;
656
657#[doc(hidden)]
665pub unsafe extern "C" fn fake_measure(
666 requests: *const u8,
667 length: u32,
668 count: u32,
669 results: *mut MeasureResult,
670) -> i32 {
671 if !crate::boundary::in_host_call() {
672 return MEASURE_OFF_UI_THREAD;
673 }
674 if requests.is_null() || results.is_null() {
675 return MEASURE_UNREADABLE;
676 }
677 let buffer = unsafe { std::slice::from_raw_parts(requests, length as usize) };
679 let results = unsafe { std::slice::from_raw_parts_mut(results, count as usize) };
681 fake_measure_slices(buffer, results)
682}
683
684#[doc(hidden)]
686pub fn fake_measure_slices(buffer: &[u8], results: &mut [MeasureResult]) -> i32 {
687 let count = results.len();
688 if buffer.len() < count * MEASURE_RECORD_LEN {
689 return MEASURE_UNREADABLE;
690 }
691 for (index, result) in results.iter_mut().enumerate() {
692 let record = &buffer[index * MEASURE_RECORD_LEN..(index + 1) * MEASURE_RECORD_LEN];
693 *result = fake_one(buffer, record);
694 }
695 0
696}
697
698fn fake_one(buffer: &[u8], record: &[u8]) -> MeasureResult {
699 let malformed = MeasureResult {
700 status: RESULT_MALFORMED,
701 ..MeasureResult::REFUSED
702 };
703 match u16::from_le_bytes([record[0], record[1]]) {
704 MEASURE_NODE => MeasureResult {
705 status: RESULT_UNKNOWN_NODE,
706 ..MeasureResult::REFUSED
707 },
708 MEASURE_TEXT => {
709 let at = read_u32(record, TEXT_TEXT_OFFSET_AT) as usize;
710 let length = read_u32(record, TEXT_TEXT_LENGTH_AT) as usize;
711 let Some(text) = at
712 .checked_add(length)
713 .and_then(|end| buffer.get(at..end))
714 .and_then(|bytes| std::str::from_utf8(bytes).ok())
715 else {
716 return malformed;
717 };
718 let size = f32::from_bits(read_u32(record, TEXT_FONT_SIZE_AT));
719 let size = if size > 0.0 { size } else { FAKE_FONT_SIZE };
720 let line_height = f32::from_bits(read_u32(record, TEXT_LINE_HEIGHT_AT));
721 let line_height = if line_height.is_nan() || line_height <= 0.0 {
722 size * 1.25
723 } else {
724 line_height
725 };
726 let max_lines = read_u32(record, TEXT_MAX_LINES_AT);
727 let wrap = record[TEXT_WRAP_AT] != 0;
728 let advance = size * 0.5;
729 let width = f32::from_bits(read_u32(record, TEXT_WIDTH_AT));
730 let lines = match read_u32(record, TEXT_CONSTRAINT_AT) {
731 CONSTRAINT_MIN_CONTENT => {
732 fake_lines(text, if wrap { 0.0 } else { f32::INFINITY }, advance)
733 }
734 CONSTRAINT_MAX_CONTENT => fake_lines(text, f32::INFINITY, advance),
735 CONSTRAINT_AT_MOST if width >= 0.0 => {
736 fake_lines(text, if wrap { width } else { f32::INFINITY }, advance)
737 }
738 _ => return malformed,
739 };
740 let shown = if max_lines == 0 {
741 lines.len()
742 } else {
743 lines.len().min(max_lines as usize)
744 };
745 let widest = lines[..shown].iter().copied().fold(0.0f32, f32::max);
746 MeasureResult {
747 width: widest,
748 height: shown as f32 * line_height,
749 first_baseline: size,
750 last_baseline: (shown.max(1) - 1) as f32 * line_height + size,
751 last_line_width: lines[shown.max(1) - 1],
752 line_count: shown as u32,
753 flags: if shown < lines.len() {
754 FLAG_TRUNCATED
755 } else {
756 0
757 },
758 status: RESULT_OK,
759 }
760 }
761 _ => malformed,
762 }
763}
764
765fn fake_lines(text: &str, width: f32, advance: f32) -> Vec<f32> {
768 let mut lines = Vec::new();
769 for paragraph in text.split('\n') {
770 let mut line = 0usize;
771 for word in paragraph.split(' ') {
772 let word_length = word.chars().count();
773 let joined = if line == 0 {
774 word_length
775 } else {
776 line + 1 + word_length
777 };
778 if line != 0 && joined as f32 * advance > width {
779 lines.push(line as f32 * advance);
780 line = word_length;
781 } else {
782 line = joined;
783 }
784 }
785 lines.push(line as f32 * advance);
786 }
787 lines
788}