summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-07-25 12:10:44 +0200
committerLaurenz <laurmaedje@gmail.com>2021-07-25 12:10:44 +0200
commitc97b3078eb39995528302100d4e8b60c032cadd3 (patch)
tree2242630b093ab96a04e2b2796ea0fda95147942b /src
parentec5384c97f24c3e6d8284926fd3e415f47fe2b04 (diff)
Merge font and par state into text state
Diffstat (limited to 'src')
-rw-r--r--src/exec/context.rs16
-rw-r--r--src/exec/mod.rs14
-rw-r--r--src/exec/state.rs141
-rw-r--r--src/layout/par.rs4
-rw-r--r--src/layout/shaping.rs10
-rw-r--r--src/library/layout.rs8
-rw-r--r--src/library/text.rs48
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);
})
}