diff options
| author | Laurenz <laurmaedje@gmail.com> | 2019-06-17 10:08:16 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2019-06-17 10:08:16 +0200 |
| commit | b53ad6b1ec8b2fd05566a83c9b895f265e61d281 (patch) | |
| tree | 1c6d590ead7180fbef12915cfcd04418c9ea7902 /src/layout/text.rs | |
| parent | 236ebab23a106ca817de527ce6b6440d3b66c150 (diff) | |
Introduce flex layouting 🎈
Diffstat (limited to 'src/layout/text.rs')
| -rw-r--r-- | src/layout/text.rs | 243 |
1 files changed, 0 insertions, 243 deletions
diff --git a/src/layout/text.rs b/src/layout/text.rs deleted file mode 100644 index d1e2afd7..00000000 --- a/src/layout/text.rs +++ /dev/null @@ -1,243 +0,0 @@ -//! Layouting of text. - -use std::cell::Ref; -use std::mem; - -use smallvec::SmallVec; - -use crate::doc::TextAction; -use crate::font::{Font, FontQuery}; -use super::{Layouter, Layout, LayoutError, LayoutContext, LayoutResult, Size, Position}; - - -/// Layouts text within the constraints of a layouting context. -#[derive(Debug)] -pub struct TextLayouter<'a, 'p> { - ctx: &'a LayoutContext<'a, 'p>, - units: Vec<Unit>, - italic: bool, - bold: bool, -} - -/// A units that is arranged by the text layouter. -#[derive(Debug, Clone)] -enum Unit { - /// A paragraph. - Paragraph, - /// A space with its font index and width. - Space(usize, Size), - /// One logical tex unit. - Text(TextUnit), -} - -/// A logical unit of text (a word, syllable or a similar construct). -#[derive(Debug, Clone)] -struct TextUnit { - /// Contains pairs of (characters, font_index, char_width) for each character of the text. - chars_with_widths: SmallVec<[(char, usize, Size); 12]>, - /// The total width of the unit. - width: Size, -} - -impl<'a, 'p> TextLayouter<'a, 'p> { - /// Create a new text layouter. - pub fn new(ctx: &'a LayoutContext<'a, 'p>) -> TextLayouter<'a, 'p> { - TextLayouter { - ctx, - italic: false, - bold: false, - units: vec![], - } - } - - /// Add more text to the layout. - pub fn add_text(&mut self, text: &str) -> LayoutResult<()> { - let mut chars_with_widths = SmallVec::<[(char, usize, Size); 12]>::new(); - - // Find out which font to use for each character in the text and meanwhile calculate the - // width of the text. - let mut text_width = Size::zero(); - for c in text.chars() { - // Find out the width and add it to the total width. - let (index, font) = self.get_font_for(c)?; - let char_width = self.width_of(c, &font); - text_width += char_width; - - chars_with_widths.push((c, index, char_width)); - } - - self.units.push(Unit::Text(TextUnit { - chars_with_widths, - width: text_width, - })); - - Ok(()) - } - - /// Add a single space character. - pub fn add_space(&mut self) -> LayoutResult<()> { - let (index, font) = self.get_font_for(' ')?; - let width = self.width_of(' ', &font); - drop(font); - Ok(self.units.push(Unit::Space(index, width))) - } - - /// Start a new paragraph. - pub fn add_paragraph(&mut self) -> LayoutResult<()> { - Ok(self.units.push(Unit::Paragraph)) - } - - /// Enable or disable italics. - pub fn set_italic(&mut self, italic: bool) { - self.italic = italic; - } - - /// Enable or disable boldface. - pub fn set_bold(&mut self, bold: bool) { - self.bold = bold; - } - - /// Load a font that has the character we need. - fn get_font_for(&self, character: char) -> LayoutResult<(usize, Ref<Font>)> { - self.ctx.loader.get(FontQuery { - families: self.ctx.text_style.font_families.clone(), - italic: self.italic, - bold: self.bold, - character, - }).ok_or_else(|| LayoutError::NoSuitableFont(character)) - } - - /// The width of a char in a specific font. - fn width_of(&self, character: char, font: &Font) -> Size { - font.widths[font.map(character) as usize] * self.ctx.text_style.font_size - } -} - -impl Layouter for TextLayouter<'_, '_> { - fn finish(self) -> LayoutResult<Layout> { - TextFinisher { - actions: vec![], - buffered_text: String::new(), - current_width: Size::zero(), - active_font: std::usize::MAX, - max_width: self.ctx.max_extent.width, - layouter: self, - }.finish() - } -} - -/// Finishes a text layout by converting the text units into a stream of text actions. -#[derive(Debug)] -struct TextFinisher<'a, 'p> { - layouter: TextLayouter<'a, 'p>, - actions: Vec<TextAction>, - buffered_text: String, - current_width: Size, - active_font: usize, - max_width: Size, -} - -impl<'a, 'p> TextFinisher<'a, 'p> { - /// Finish the layout. - fn finish(mut self) -> LayoutResult<Layout> { - // Move the units out of the layouter leaving an empty vector in place. This is needed to - // move the units out into the for loop while keeping the borrow checker happy. - let mut units = Vec::new(); - mem::swap(&mut self.layouter.units, &mut units); - - // Move from the origin one line below because the y-component of the origin is the - // baseline. - self.move_newline(1.0); - - for unit in units { - match unit { - Unit::Paragraph => self.write_paragraph(), - Unit::Space(index, width) => self.write_space(index, width), - Unit::Text(text) => self.write_text_unit(text), - } - } - - self.write_buffered_text(); - - Ok(Layout { - extent: self.layouter.ctx.max_extent.clone(), - actions: self.actions, - }) - } - - /// Add a paragraph to the output. - fn write_paragraph(&mut self) { - self.write_buffered_text(); - self.move_newline(self.layouter.ctx.text_style.paragraph_spacing); - } - - /// Add a single space to the output if it is not eaten by a line break. - fn write_space(&mut self, font: usize, width: Size) { - if self.would_overflow(width) { - self.write_buffered_text(); - self.move_newline(1.0); - } else if self.current_width > Size::zero() { - if font != self.active_font { - self.write_buffered_text(); - self.set_font(font); - } - - self.buffered_text.push(' '); - self.current_width += width; - } - } - - /// Add a single unit of text without breaking it apart. - fn write_text_unit(&mut self, text: TextUnit) { - if self.would_overflow(text.width) { - self.write_buffered_text(); - self.move_newline(1.0); - } - - // Finally write the word. - for (c, font, width) in text.chars_with_widths { - if font != self.active_font { - // If we will change the font, first write the remaining things. - self.write_buffered_text(); - self.set_font(font); - } - - self.buffered_text.push(c); - self.current_width += width; - } - } - - /// Move to the next line. A factor of 1.0 uses the default line spacing. - fn move_newline(&mut self, factor: f32) { - let vertical = Size::from_points(self.layouter.ctx.text_style.font_size) - * self.layouter.ctx.text_style.line_spacing - * factor; - - self.actions.push(TextAction::MoveNewline(Position { - x: Size::zero(), - y: vertical - })); - - self.current_width = Size::zero(); - } - - /// Output a text action containing the buffered text and reset the buffer. - fn write_buffered_text(&mut self) { - if !self.buffered_text.is_empty() { - let mut buffered = String::new(); - mem::swap(&mut self.buffered_text, &mut buffered); - self.actions.push(TextAction::WriteText(buffered)); - } - } - - /// Output an action setting a new font and update the active font. - fn set_font(&mut self, index: usize) { - self.active_font = index; - self.actions.push(TextAction::SetFont(index, self.layouter.ctx.text_style.font_size)); - } - - /// Check whether additional text with the given width would overflow the current line. - fn would_overflow(&self, width: Size) -> bool { - self.current_width + width > self.max_width - } -} |
