diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-07-25 12:10:44 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-07-25 12:10:44 +0200 |
| commit | c97b3078eb39995528302100d4e8b60c032cadd3 (patch) | |
| tree | 2242630b093ab96a04e2b2796ea0fda95147942b | |
| parent | ec5384c97f24c3e6d8284926fd3e415f47fe2b04 (diff) | |
Merge font and par state into text state
| -rw-r--r-- | src/exec/context.rs | 16 | ||||
| -rw-r--r-- | src/exec/mod.rs | 14 | ||||
| -rw-r--r-- | src/exec/state.rs | 141 | ||||
| -rw-r--r-- | src/layout/par.rs | 4 | ||||
| -rw-r--r-- | src/layout/shaping.rs | 10 | ||||
| -rw-r--r-- | src/library/layout.rs | 8 | ||||
| -rw-r--r-- | src/library/text.rs | 48 |
7 files changed, 122 insertions, 119 deletions
diff --git a/src/exec/context.rs b/src/exec/context.rs index 13e53b09..725907f2 100644 --- a/src/exec/context.rs +++ b/src/exec/context.rs @@ -76,10 +76,10 @@ impl ExecContext { /// Push text, but in monospace. pub fn push_monospace_text(&mut self, text: impl Into<EcoString>) { - let prev = Rc::clone(&self.state.font); - self.state.font_mut().monospace = true; + let prev = Rc::clone(&self.state.text); + self.state.text_mut().monospace = true; self.push_text(text); - self.state.font = prev; + self.state.text = prev; } /// Push a word space into the active paragraph. @@ -121,7 +121,7 @@ impl ExecContext { /// Apply a forced paragraph break. pub fn parbreak(&mut self) { - let amount = self.state.par.spacing.resolve(self.state.font.size); + let amount = self.state.text.par_spacing(); self.stack.finish_par(&self.state); self.stack.push_soft(StackChild::Spacing(amount)); } @@ -148,7 +148,7 @@ impl ExecContext { ParChild::Text( text.into(), self.state.aligns.cross, - Rc::clone(&self.state.font), + Rc::clone(&self.state.text), ) } } @@ -187,7 +187,7 @@ struct StackBuilder { impl StackBuilder { fn new(state: &State) -> Self { Self { - dirs: Gen::new(state.lang.dir, Dir::TTB), + dirs: Gen::new(state.dir, Dir::TTB), children: vec![], last: Last::None, par: ParBuilder::new(state), @@ -237,8 +237,8 @@ impl ParBuilder { fn new(state: &State) -> Self { Self { aligns: state.aligns, - dir: state.lang.dir, - line_spacing: state.par.leading.resolve(state.font.size), + dir: state.dir, + line_spacing: state.text.line_spacing(), children: vec![], last: Last::None, } diff --git a/src/exec/mod.rs b/src/exec/mod.rs index 76857b44..8b769def 100644 --- a/src/exec/mod.rs +++ b/src/exec/mod.rs @@ -57,8 +57,8 @@ impl ExecWithMap for SyntaxNode { Self::Space => ctx.push_word_space(), Self::Linebreak(_) => ctx.linebreak(), Self::Parbreak(_) => ctx.parbreak(), - Self::Strong(_) => ctx.state.font_mut().strong ^= true, - Self::Emph(_) => ctx.state.font_mut().emph ^= true, + Self::Strong(_) => ctx.state.text_mut().strong ^= true, + Self::Emph(_) => ctx.state.text_mut().emph ^= true, Self::Raw(n) => n.exec(ctx), Self::Heading(n) => n.exec_with_map(ctx, map), Self::List(n) => n.exec_with_map(ctx, map), @@ -87,10 +87,10 @@ impl ExecWithMap for HeadingNode { ctx.parbreak(); let snapshot = ctx.state.clone(); - let font = ctx.state.font_mut(); + let text = ctx.state.text_mut(); let upscale = 1.6 - 0.1 * self.level as f64; - font.size *= upscale; - font.strong = true; + text.size *= upscale; + text.strong = true; self.body.exec_with_map(ctx, map); ctx.state = snapshot; @@ -118,11 +118,11 @@ fn exec_item(ctx: &mut ExecContext, label: EcoString, body: &SyntaxTree, map: &E let label = ctx.exec_stack(|ctx| ctx.push_text(label)); let body = ctx.exec_tree_stack(body, map); let stack = StackNode { - dirs: Gen::new(Dir::TTB, ctx.state.lang.dir), + dirs: Gen::new(Dir::TTB, ctx.state.dir), aspect: None, children: vec![ StackChild::Any(label.into(), Gen::default()), - StackChild::Spacing(ctx.state.font.size / 2.0), + StackChild::Spacing(ctx.state.text.size / 2.0), StackChild::Any(body.into(), Gen::default()), ], }; diff --git a/src/exec/state.rs b/src/exec/state.rs index 624394ba..be70cb67 100644 --- a/src/exec/state.rs +++ b/src/exec/state.rs @@ -8,37 +8,33 @@ use crate::layout::Paint; use crate::paper::{Paper, PaperClass, PAPER_A4}; /// The execution state. -#[derive(Default, Debug, Clone, Eq, PartialEq, Hash)] +#[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct State { - /// The current language-related settings. - pub lang: LangState, - /// The current page settings. - pub page: PageState, - /// The current paragraph settings. - pub par: ParState, - /// The current font settings. - pub font: Rc<FontState>, + /// The direction for text and other inline objects. + pub dir: Dir, /// The current alignments of layouts in their parents. pub aligns: Gen<Align>, + /// The current page settings. + pub page: PageState, + /// The current text settings. + pub text: Rc<TextState>, } impl State { - /// Access the `font` state mutably. - pub fn font_mut(&mut self) -> &mut FontState { - Rc::make_mut(&mut self.font) + /// Access the `text` state mutably. + pub fn text_mut(&mut self) -> &mut TextState { + Rc::make_mut(&mut self.text) } } -/// Defines language properties. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub struct LangState { - /// The direction for text and other inline objects. - pub dir: Dir, -} - -impl Default for LangState { +impl Default for State { fn default() -> Self { - Self { dir: Dir::LTR } + Self { + dir: Dir::LTR, + aligns: Gen::splat(Align::Start), + page: PageState::default(), + text: Rc::new(TextState::default()), + } } } @@ -82,34 +78,14 @@ impl Default for PageState { } } -/// Defines paragraph properties. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub struct ParState { - /// The spacing between paragraphs (dependent on scaled font size). - pub spacing: Linear, - /// The spacing between lines (dependent on scaled font size). - pub leading: Linear, - /// The spacing between words (dependent on scaled font size). - // TODO: Don't ignore this. - pub word_spacing: Linear, -} - -impl Default for ParState { - fn default() -> Self { - Self { - spacing: Relative::new(1.0).into(), - leading: Relative::new(0.5).into(), - word_spacing: Relative::new(0.25).into(), - } - } -} - -/// Defines font properties. +/// Defines text properties. #[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct FontState { - /// A list of font families with generic class definitions. +pub struct TextState { + /// A list of font families with generic class definitions (the final + /// family list also depends on `monospace`). pub families: Rc<FamilyList>, - /// The selected font variant. + /// The selected font variant (the final variant also depends on `strong` + /// and `emph`). pub variant: FontVariant, /// Whether the strong toggle is active or inactive. This determines /// whether the next `*` adds or removes font weight. @@ -121,6 +97,13 @@ pub struct FontState { pub monospace: bool, /// The font size. pub size: Length, + /// The spacing between words (dependent on scaled font size). + // TODO: Don't ignore this. + pub word_spacing: Linear, + /// The spacing between lines (dependent on scaled font size). + pub line_spacing: Linear, + /// The spacing between paragraphs (dependent on scaled font size). + pub par_spacing: Linear, /// The top end of the text bounding box. pub top_edge: VerticalFontMetric, /// The bottom end of the text bounding box. @@ -135,13 +118,13 @@ pub struct FontState { pub overline: Option<Rc<LineState>>, } -impl FontState { - /// Access the `families` mutably. +impl TextState { + /// Access the `families` list mutably. pub fn families_mut(&mut self) -> &mut FamilyList { Rc::make_mut(&mut self.families) } - /// The canonical family iterator. + /// The resolved family iterator. pub fn families(&self) -> impl Iterator<Item = &str> + Clone { let head = if self.monospace { self.families.monospace.as_slice() @@ -151,7 +134,7 @@ impl FontState { head.iter().map(String::as_str).chain(self.families.iter()) } - /// The canonical variant with `strong` and `emph` factored in. + /// The resolved variant with `strong` and `emph` factored in. pub fn variant(&self) -> FontVariant { let mut variant = self.variant; @@ -169,9 +152,24 @@ impl FontState { variant } + + /// The resolved word spacing. + pub fn word_spacing(&self) -> Length { + self.word_spacing.resolve(self.size) + } + + /// The resolved line spacing. + pub fn line_spacing(&self) -> Length { + self.line_spacing.resolve(self.size) + } + + /// The resolved paragraph spacing. + pub fn par_spacing(&self) -> Length { + self.par_spacing.resolve(self.size) + } } -impl Default for FontState { +impl Default for TextState { fn default() -> Self { Self { families: Rc::new(FamilyList::default()), @@ -184,6 +182,9 @@ impl Default for FontState { emph: false, monospace: false, size: Length::pt(11.0), + word_spacing: Relative::new(0.25).into(), + line_spacing: Relative::new(0.5).into(), + par_spacing: Relative::new(1.0).into(), top_edge: VerticalFontMetric::CapHeight, bottom_edge: VerticalFontMetric::Baseline, fill: Paint::Color(Color::Rgba(RgbaColor::BLACK)), @@ -194,24 +195,6 @@ impl Default for FontState { } } -/// Describes a line that could be positioned over, under or on top of text. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub struct LineState { - /// Stroke color of the line. - /// - /// Defaults to the text color if `None`. - pub stroke: Option<Paint>, - /// Thickness of the line's stroke. Calling functions should attempt to - /// read this value from the appropriate font tables if this is `None`. - pub thickness: Option<Linear>, - /// Position of the line relative to the baseline. Calling functions should - /// attempt to read this value from the appropriate font tables if this is - /// `None`. - pub offset: Option<Linear>, - /// Amount that the line will be longer or shorter than its associated text. - pub extent: Linear, -} - /// Font family definitions. #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct FamilyList { @@ -276,3 +259,21 @@ impl Display for FontFamily { }) } } + +/// Describes a line that is positioned over, under or on top of text. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct LineState { + /// Stroke color of the line. + /// + /// Defaults to the text color if `None`. + pub stroke: Option<Paint>, + /// Thickness of the line's stroke. Calling functions should attempt to + /// read this value from the appropriate font tables if this is `None`. + pub thickness: Option<Linear>, + /// Position of the line relative to the baseline. Calling functions should + /// attempt to read this value from the appropriate font tables if this is + /// `None`. + pub offset: Option<Linear>, + /// Amount that the line will be longer or shorter than its associated text. + pub extent: Linear, +} diff --git a/src/layout/par.rs b/src/layout/par.rs index 8736452b..56847b9b 100644 --- a/src/layout/par.rs +++ b/src/layout/par.rs @@ -6,7 +6,7 @@ use xi_unicode::LineBreakIterator; use super::*; use crate::eco::EcoString; -use crate::exec::FontState; +use crate::exec::TextState; use crate::util::{RangeExt, SliceExt}; type Range = std::ops::Range<usize>; @@ -30,7 +30,7 @@ pub enum ParChild { /// Spacing between other nodes. Spacing(Length), /// A run of text and how to align it in its line. - Text(EcoString, Align, Rc<FontState>), + Text(EcoString, Align, Rc<TextState>), /// Any child node and how to align it in its line. Any(LayoutNode, Align), } diff --git a/src/layout/shaping.rs b/src/layout/shaping.rs index 3ede5122..0cfb01a8 100644 --- a/src/layout/shaping.rs +++ b/src/layout/shaping.rs @@ -5,7 +5,7 @@ use std::ops::Range; use rustybuzz::UnicodeBuffer; use super::{Element, Frame, Glyph, LayoutContext, Text}; -use crate::exec::{FontState, LineState}; +use crate::exec::{LineState, TextState}; use crate::font::{Face, FaceId, FontVariant, LineMetrics}; use crate::geom::{Dir, Length, Point, Size}; use crate::layout::Geometry; @@ -23,7 +23,7 @@ pub struct ShapedText<'a> { /// The text direction. pub dir: Dir, /// The properties used for font selection. - pub state: &'a FontState, + pub state: &'a TextState, /// The font size. pub size: Size, /// The baseline from the top of the frame. @@ -185,7 +185,7 @@ pub fn shape<'a>( ctx: &mut LayoutContext, text: &'a str, dir: Dir, - state: &'a FontState, + state: &'a TextState, ) -> ShapedText<'a> { let mut glyphs = vec![]; if !text.is_empty() { @@ -346,7 +346,7 @@ fn shape_segment<'a>( fn measure( ctx: &mut LayoutContext, glyphs: &[ShapedGlyph], - state: &FontState, + state: &TextState, ) -> (Size, Length) { let mut width = Length::zero(); let mut top = Length::zero(); @@ -386,7 +386,7 @@ fn decorate( pos: Point, width: Length, face_id: FaceId, - state: &FontState, + state: &TextState, ) { let mut apply = |substate: &LineState, metrics: fn(&Face) -> &LineMetrics| { let metrics = metrics(ctx.fonts.get(face_id)); diff --git a/src/library/layout.rs b/src/library/layout.rs index cbc5ff94..ef909bc3 100644 --- a/src/library/layout.rs +++ b/src/library/layout.rs @@ -96,7 +96,7 @@ fn spacing_impl(ctx: &mut EvalContext, args: &mut FuncArgs, axis: GenAxis) -> Va Value::template(move |ctx| { if let Some(linear) = spacing { // TODO: Should this really always be font-size relative? - let amount = linear.resolve(ctx.state.font.size); + let amount = linear.resolve(ctx.state.text.size); ctx.push_spacing(axis, amount); } }) @@ -124,7 +124,7 @@ pub fn align(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { Value::template(move |ctx| { if let Some(horizontal) = horizontal { - ctx.state.aligns.cross = horizontal.to_align(ctx.state.lang.dir); + ctx.state.aligns.cross = horizontal.to_align(ctx.state.dir); } if let Some(vertical) = vertical { @@ -260,7 +260,7 @@ pub fn stack(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { .collect(); ctx.push_into_stack(StackNode { - dirs: Gen::new(ctx.state.lang.dir, dir), + dirs: Gen::new(ctx.state.dir, dir), aspect: None, children, }); @@ -290,7 +290,7 @@ pub fn grid(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { .map(|child| ctx.exec_template_stack(child).into()) .collect(); - let cross_dir = column_dir.unwrap_or(ctx.state.lang.dir); + let cross_dir = column_dir.unwrap_or(ctx.state.dir); let main_dir = row_dir.unwrap_or(cross_dir.axis().other().dir(true)); ctx.push_into_stack(GridNode { diff --git a/src/library/text.rs b/src/library/text.rs index 14a0ee9a..5e3ce204 100644 --- a/src/library/text.rs +++ b/src/library/text.rs @@ -1,6 +1,6 @@ use std::convert::TryFrom; -use crate::exec::{FontState, LineState}; +use crate::exec::{LineState, TextState}; use crate::font::{FontStretch, FontStyle, FontWeight}; use crate::layout::Paint; @@ -28,50 +28,50 @@ pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { let body = args.expect::<Template>(ctx, "body").unwrap_or_default(); Value::template(move |ctx| { - let font = ctx.state.font_mut(); + let state = ctx.state.text_mut(); if let Some(linear) = size { - font.size = linear.resolve(font.size); + state.size = linear.resolve(state.size); } if let Some(FontDef(list)) = &list { - font.families_mut().list = list.clone(); + state.families_mut().list = list.clone(); } if let Some(style) = style { - font.variant.style = style; + state.variant.style = style; } if let Some(weight) = weight { - font.variant.weight = weight; + state.variant.weight = weight; } if let Some(stretch) = stretch { - font.variant.stretch = stretch; + state.variant.stretch = stretch; } if let Some(top_edge) = top_edge { - font.top_edge = top_edge; + state.top_edge = top_edge; } if let Some(bottom_edge) = bottom_edge { - font.bottom_edge = bottom_edge; + state.bottom_edge = bottom_edge; } if let Some(fill) = fill { - font.fill = Paint::Color(fill); + state.fill = Paint::Color(fill); } if let Some(FamilyDef(serif)) = &serif { - font.families_mut().serif = serif.clone(); + state.families_mut().serif = serif.clone(); } if let Some(FamilyDef(sans_serif)) = &sans_serif { - font.families_mut().sans_serif = sans_serif.clone(); + state.families_mut().sans_serif = sans_serif.clone(); } if let Some(FamilyDef(monospace)) = &monospace { - font.families_mut().monospace = monospace.clone(); + state.families_mut().monospace = monospace.clone(); } body.exec(ctx); @@ -131,22 +131,24 @@ dynamic! { /// `par`: Configure paragraphs. pub fn par(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { - let spacing = args.named(ctx, "spacing"); - let leading = args.named(ctx, "leading"); + let par_spacing = args.named(ctx, "spacing"); + let line_spacing = args.named(ctx, "leading"); let word_spacing = args.named(ctx, "word-spacing"); let body = args.expect::<Template>(ctx, "body").unwrap_or_default(); Value::template(move |ctx| { - if let Some(spacing) = spacing { - ctx.state.par.spacing = spacing; + let state = ctx.state.text_mut(); + + if let Some(par_spacing) = par_spacing { + state.par_spacing = par_spacing; } - if let Some(leading) = leading { - ctx.state.par.leading = leading; + if let Some(line_spacing) = line_spacing { + state.line_spacing = line_spacing; } if let Some(word_spacing) = word_spacing { - ctx.state.par.word_spacing = word_spacing; + state.word_spacing = word_spacing; } ctx.parbreak(); @@ -169,7 +171,7 @@ pub fn lang(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { Value::template(move |ctx| { if let Some(dir) = dir.or(iso) { - ctx.state.lang.dir = dir; + ctx.state.dir = dir; } ctx.parbreak(); @@ -204,7 +206,7 @@ pub fn overline(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { fn line_impl( ctx: &mut EvalContext, args: &mut FuncArgs, - substate: fn(&mut FontState) -> &mut Option<Rc<LineState>>, + substate: fn(&mut TextState) -> &mut Option<Rc<LineState>>, ) -> Value { let stroke = args.eat().or_else(|| args.named(ctx, "stroke")); let thickness = args.eat().or_else(|| args.named::<Linear>(ctx, "thickness")); @@ -223,7 +225,7 @@ fn line_impl( }); Value::template(move |ctx| { - *substate(ctx.state.font_mut()) = state.clone(); + *substate(ctx.state.text_mut()) = state.clone(); body.exec(ctx); }) } |
