Rename State to Style and move it into its own module

This commit is contained in:
Laurenz 2021-10-10 20:54:13 +02:00
parent d4cc8c775d
commit 9ac125dea8
15 changed files with 249 additions and 243 deletions

View File

@ -60,7 +60,7 @@ fn bench_eval(iai: &mut Iai) {
fn bench_to_tree(iai: &mut Iai) { fn bench_to_tree(iai: &mut Iai) {
let (mut ctx, id) = context(); let (mut ctx, id) = context();
let module = ctx.evaluate(id).unwrap(); let module = ctx.evaluate(id).unwrap();
iai.run(|| module.template.to_tree(ctx.state())); iai.run(|| module.template.to_tree(ctx.style()));
} }
fn bench_layout(iai: &mut Iai) { fn bench_layout(iai: &mut Iai) {

View File

@ -12,7 +12,6 @@ mod capture;
mod function; mod function;
mod ops; mod ops;
mod scope; mod scope;
mod state;
mod template; mod template;
mod walk; mod walk;
@ -22,7 +21,6 @@ pub use capture::*;
pub use dict::*; pub use dict::*;
pub use function::*; pub use function::*;
pub use scope::*; pub use scope::*;
pub use state::*;
pub use template::*; pub use template::*;
pub use value::*; pub use value::*;
pub use walk::*; pub use walk::*;

View File

@ -4,13 +4,14 @@ use std::mem;
use std::ops::{Add, AddAssign}; use std::ops::{Add, AddAssign};
use std::rc::Rc; use std::rc::Rc;
use super::{State, Str}; use super::Str;
use crate::diag::StrResult; use crate::diag::StrResult;
use crate::geom::{Align, Dir, Gen, GenAxis, Length, Linear, Sides, Size}; use crate::geom::{Align, Dir, Gen, GenAxis, Length, Linear, Sides, Size};
use crate::layout::{ use crate::layout::{
Decoration, LayoutNode, LayoutTree, PadNode, PageRun, ParChild, ParNode, StackChild, Decoration, LayoutNode, LayoutTree, PadNode, PageRun, ParChild, ParNode, StackChild,
StackNode, StackNode,
}; };
use crate::style::Style;
use crate::util::EcoString; use crate::util::EcoString;
/// A template value: `[*Hi* there]`. /// A template value: `[*Hi* there]`.
@ -33,15 +34,15 @@ enum TemplateNode {
/// Spacing. /// Spacing.
Spacing(GenAxis, Linear), Spacing(GenAxis, Linear),
/// An inline node builder. /// An inline node builder.
Inline(Rc<dyn Fn(&State) -> LayoutNode>, Vec<Decoration>), Inline(Rc<dyn Fn(&Style) -> LayoutNode>, Vec<Decoration>),
/// An block node builder. /// An block node builder.
Block(Rc<dyn Fn(&State) -> LayoutNode>), Block(Rc<dyn Fn(&Style) -> LayoutNode>),
/// Save the current state. /// Save the current style.
Save, Save,
/// Restore the last saved state. /// Restore the last saved style.
Restore, Restore,
/// A function that can modify the current state. /// A function that can modify the current style.
Modify(Rc<dyn Fn(&mut State)>), Modify(Rc<dyn Fn(&mut Style)>),
} }
impl Template { impl Template {
@ -53,7 +54,7 @@ impl Template {
/// Create a template from a builder for an inline-level node. /// Create a template from a builder for an inline-level node.
pub fn from_inline<F, T>(f: F) -> Self pub fn from_inline<F, T>(f: F) -> Self
where where
F: Fn(&State) -> T + 'static, F: Fn(&Style) -> T + 'static,
T: Into<LayoutNode>, T: Into<LayoutNode>,
{ {
let node = TemplateNode::Inline(Rc::new(move |s| f(s).into()), vec![]); let node = TemplateNode::Inline(Rc::new(move |s| f(s).into()), vec![]);
@ -63,7 +64,7 @@ impl Template {
/// Create a template from a builder for a block-level node. /// Create a template from a builder for a block-level node.
pub fn from_block<F, T>(f: F) -> Self pub fn from_block<F, T>(f: F) -> Self
where where
F: Fn(&State) -> T + 'static, F: Fn(&Style) -> T + 'static,
T: Into<LayoutNode>, T: Into<LayoutNode>,
{ {
let node = TemplateNode::Block(Rc::new(move |s| f(s).into())); let node = TemplateNode::Block(Rc::new(move |s| f(s).into()));
@ -98,7 +99,7 @@ impl Template {
/// Add text, but in monospace. /// Add text, but in monospace.
pub fn monospace(&mut self, text: impl Into<EcoString>) { pub fn monospace(&mut self, text: impl Into<EcoString>) {
self.save(); self.save();
self.modify(|state| state.font_mut().monospace = true); self.modify(|style| style.text_mut().monospace = true);
self.text(text); self.text(text);
self.restore(); self.restore();
} }
@ -126,16 +127,16 @@ impl Template {
self.make_mut().push(TemplateNode::Save); self.make_mut().push(TemplateNode::Save);
} }
/// Ensure that later nodes are untouched by state modifications made since /// Ensure that later nodes are untouched by style modifications made since
/// the last snapshot. /// the last snapshot.
pub fn restore(&mut self) { pub fn restore(&mut self) {
self.make_mut().push(TemplateNode::Restore); self.make_mut().push(TemplateNode::Restore);
} }
/// Modify the state. /// Modify the style.
pub fn modify<F>(&mut self, f: F) pub fn modify<F>(&mut self, f: F)
where where
F: Fn(&mut State) + 'static, F: Fn(&mut Style) + 'static,
{ {
self.make_mut().push(TemplateNode::Modify(Rc::new(f))); self.make_mut().push(TemplateNode::Modify(Rc::new(f)));
} }
@ -143,7 +144,7 @@ impl Template {
/// Return a new template which is modified from start to end. /// Return a new template which is modified from start to end.
pub fn modified<F>(self, f: F) -> Self pub fn modified<F>(self, f: F) -> Self
where where
F: Fn(&mut State) + 'static, F: Fn(&mut Style) + 'static,
{ {
let mut wrapper = Self::new(); let mut wrapper = Self::new();
wrapper.save(); wrapper.save();
@ -153,18 +154,18 @@ impl Template {
wrapper wrapper
} }
/// Build the stack node resulting from instantiating the template in the /// Build the stack node resulting from instantiating the template with the
/// given state. /// given style.
pub fn to_stack(&self, state: &State) -> StackNode { pub fn to_stack(&self, style: &Style) -> StackNode {
let mut builder = Builder::new(state, false); let mut builder = Builder::new(style, false);
builder.template(self); builder.template(self);
builder.build_stack() builder.build_stack()
} }
/// Build the layout tree resulting from instantiating the template in the /// Build the layout tree resulting from instantiating the template with the
/// given state. /// given style.
pub fn to_tree(&self, state: &State) -> LayoutTree { pub fn to_tree(&self, style: &Style) -> LayoutTree {
let mut builder = Builder::new(state, true); let mut builder = Builder::new(style, true);
builder.template(self); builder.template(self);
builder.build_tree() builder.build_tree()
} }
@ -238,10 +239,10 @@ impl Add<Template> for Str {
/// Transforms from template to layout representation. /// Transforms from template to layout representation.
struct Builder { struct Builder {
/// The active state. /// The current style.
state: State, style: Style,
/// Snapshots of the state. /// Snapshots of the style.
snapshots: Vec<State>, snapshots: Vec<Style>,
/// The tree of finished page runs. /// The tree of finished page runs.
tree: LayoutTree, tree: LayoutTree,
/// When we are building the top-level layout trees, this contains metrics /// When we are building the top-level layout trees, this contains metrics
@ -252,14 +253,14 @@ struct Builder {
} }
impl Builder { impl Builder {
/// Create a new builder with a base state. /// Create a new builder with a base style.
fn new(state: &State, pages: bool) -> Self { fn new(style: &Style, pages: bool) -> Self {
Self { Self {
state: state.clone(), style: style.clone(),
snapshots: vec![], snapshots: vec![],
tree: LayoutTree { runs: vec![] }, tree: LayoutTree { runs: vec![] },
page: pages.then(|| PageBuilder::new(state, true)), page: pages.then(|| PageBuilder::new(style, true)),
stack: StackBuilder::new(state), stack: StackBuilder::new(style),
} }
} }
@ -273,11 +274,11 @@ impl Builder {
/// Build a template node. /// Build a template node.
fn node(&mut self, node: &TemplateNode) { fn node(&mut self, node: &TemplateNode) {
match node { match node {
TemplateNode::Save => self.snapshots.push(self.state.clone()), TemplateNode::Save => self.snapshots.push(self.style.clone()),
TemplateNode::Restore => { TemplateNode::Restore => {
let state = self.snapshots.pop().unwrap(); let style = self.snapshots.pop().unwrap();
let newpage = state.page != self.state.page; let newpage = style.page != self.style.page;
self.state = state; self.style = style;
if newpage { if newpage {
self.pagebreak(true, false); self.pagebreak(true, false);
} }
@ -288,9 +289,9 @@ impl Builder {
TemplateNode::Pagebreak(keep) => self.pagebreak(*keep, true), TemplateNode::Pagebreak(keep) => self.pagebreak(*keep, true),
TemplateNode::Text(text, decos) => self.text(text, decos), TemplateNode::Text(text, decos) => self.text(text, decos),
TemplateNode::Spacing(axis, amount) => self.spacing(*axis, *amount), TemplateNode::Spacing(axis, amount) => self.spacing(*axis, *amount),
TemplateNode::Inline(f, decos) => self.inline(f(&self.state), decos), TemplateNode::Inline(f, decos) => self.inline(f(&self.style), decos),
TemplateNode::Block(f) => self.block(f(&self.state)), TemplateNode::Block(f) => self.block(f(&self.style)),
TemplateNode::Modify(f) => f(&mut self.state), TemplateNode::Modify(f) => f(&mut self.style),
} }
} }
@ -306,16 +307,16 @@ impl Builder {
/// Apply a forced paragraph break. /// Apply a forced paragraph break.
fn parbreak(&mut self) { fn parbreak(&mut self) {
let amount = self.state.par_spacing(); let amount = self.style.par_spacing();
self.stack.finish_par(&self.state); self.stack.finish_par(&self.style);
self.stack.push_soft(StackChild::Spacing(amount.into())); self.stack.push_soft(StackChild::Spacing(amount.into()));
} }
/// Apply a forced page break. /// Apply a forced page break.
fn pagebreak(&mut self, keep: bool, hard: bool) { fn pagebreak(&mut self, keep: bool, hard: bool) {
if let Some(builder) = &mut self.page { if let Some(builder) = &mut self.page {
let page = mem::replace(builder, PageBuilder::new(&self.state, hard)); let page = mem::replace(builder, PageBuilder::new(&self.style, hard));
let stack = mem::replace(&mut self.stack, StackBuilder::new(&self.state)); let stack = mem::replace(&mut self.stack, StackBuilder::new(&self.style));
self.tree.runs.extend(page.build(stack.build(), keep)); self.tree.runs.extend(page.build(stack.build(), keep));
} }
} }
@ -327,14 +328,14 @@ impl Builder {
/// Push an inline node into the active paragraph. /// Push an inline node into the active paragraph.
fn inline(&mut self, node: impl Into<LayoutNode>, decos: &[Decoration]) { fn inline(&mut self, node: impl Into<LayoutNode>, decos: &[Decoration]) {
let align = self.state.aligns.inline; let align = self.style.aligns.inline;
self.stack.par.push(ParChild::Any(node.into(), align, decos.to_vec())); self.stack.par.push(ParChild::Any(node.into(), align, decos.to_vec()));
} }
/// Push a block node into the active stack, finishing the active paragraph. /// Push a block node into the active stack, finishing the active paragraph.
fn block(&mut self, node: impl Into<LayoutNode>) { fn block(&mut self, node: impl Into<LayoutNode>) {
self.parbreak(); self.parbreak();
let aligns = self.state.aligns; let aligns = self.style.aligns;
self.stack.push(StackChild::Any(node.into(), aligns)); self.stack.push(StackChild::Any(node.into(), aligns));
self.parbreak(); self.parbreak();
} }
@ -343,7 +344,7 @@ impl Builder {
fn spacing(&mut self, axis: GenAxis, amount: Linear) { fn spacing(&mut self, axis: GenAxis, amount: Linear) {
match axis { match axis {
GenAxis::Block => { GenAxis::Block => {
self.stack.finish_par(&self.state); self.stack.finish_par(&self.style);
self.stack.push_hard(StackChild::Spacing(amount)); self.stack.push_hard(StackChild::Spacing(amount));
} }
GenAxis::Inline => { GenAxis::Inline => {
@ -365,8 +366,8 @@ impl Builder {
self.tree self.tree
} }
/// Construct a text node with the given text and settings from the active /// Construct a text node with the given text and settings from the current
/// state. /// style.
fn make_text_node( fn make_text_node(
&self, &self,
text: impl Into<EcoString>, text: impl Into<EcoString>,
@ -374,8 +375,8 @@ impl Builder {
) -> ParChild { ) -> ParChild {
ParChild::Text( ParChild::Text(
text.into(), text.into(),
self.state.aligns.inline, self.style.aligns.inline,
Rc::clone(&self.state.font), Rc::clone(&self.style.text),
decos, decos,
) )
} }
@ -388,10 +389,10 @@ struct PageBuilder {
} }
impl PageBuilder { impl PageBuilder {
fn new(state: &State, hard: bool) -> Self { fn new(style: &Style, hard: bool) -> Self {
Self { Self {
size: state.page.size, size: style.page.size,
padding: state.page.margins(), padding: style.page.margins(),
hard, hard,
} }
} }
@ -413,12 +414,12 @@ struct StackBuilder {
} }
impl StackBuilder { impl StackBuilder {
fn new(state: &State) -> Self { fn new(style: &Style) -> Self {
Self { Self {
dirs: state.dirs, dirs: Gen::new(style.dir, Dir::TTB),
children: vec![], children: vec![],
last: Last::None, last: Last::None,
par: ParBuilder::new(state), par: ParBuilder::new(style),
} }
} }
@ -436,8 +437,8 @@ impl StackBuilder {
self.children.push(child); self.children.push(child);
} }
fn finish_par(&mut self, state: &State) { fn finish_par(&mut self, style: &Style) {
let par = mem::replace(&mut self.par, ParBuilder::new(state)); let par = mem::replace(&mut self.par, ParBuilder::new(style));
if let Some(par) = par.build() { if let Some(par) = par.build() {
self.push(par); self.push(par);
} }
@ -462,11 +463,11 @@ struct ParBuilder {
} }
impl ParBuilder { impl ParBuilder {
fn new(state: &State) -> Self { fn new(style: &Style) -> Self {
Self { Self {
aligns: state.aligns, aligns: style.aligns,
dir: state.dirs.inline, dir: style.dir,
line_spacing: state.line_spacing(), line_spacing: style.line_spacing(),
children: vec![], children: vec![],
last: Last::None, last: Last::None,
} }

View File

@ -2,7 +2,7 @@ use std::rc::Rc;
use super::{Eval, EvalContext, Str, Template, Value}; use super::{Eval, EvalContext, Str, Template, Value};
use crate::diag::TypResult; use crate::diag::TypResult;
use crate::geom::Gen; use crate::geom::{Dir, Gen};
use crate::layout::{ParChild, ParNode, StackChild, StackNode}; use crate::layout::{ParChild, ParNode, StackChild, StackNode};
use crate::syntax::*; use crate::syntax::*;
use crate::util::BoolExt; use crate::util::BoolExt;
@ -28,8 +28,8 @@ impl Walk for MarkupNode {
Self::Space => ctx.template.space(), Self::Space => ctx.template.space(),
Self::Linebreak(_) => ctx.template.linebreak(), Self::Linebreak(_) => ctx.template.linebreak(),
Self::Parbreak(_) => ctx.template.parbreak(), Self::Parbreak(_) => ctx.template.parbreak(),
Self::Strong(_) => ctx.template.modify(|s| s.font_mut().strong.flip()), Self::Strong(_) => ctx.template.modify(|s| s.text_mut().strong.flip()),
Self::Emph(_) => ctx.template.modify(|s| s.font_mut().emph.flip()), Self::Emph(_) => ctx.template.modify(|s| s.text_mut().emph.flip()),
Self::Text(text) => ctx.template.text(text), Self::Text(text) => ctx.template.text(text),
Self::Raw(raw) => raw.walk(ctx)?, Self::Raw(raw) => raw.walk(ctx)?,
Self::Heading(heading) => heading.walk(ctx)?, Self::Heading(heading) => heading.walk(ctx)?,
@ -73,11 +73,11 @@ impl Walk for HeadingNode {
ctx.template.parbreak(); ctx.template.parbreak();
ctx.template.save(); ctx.template.save();
ctx.template.modify(move |state| { ctx.template.modify(move |style| {
let font = state.font_mut(); let text = style.text_mut();
let upscale = 1.6 - 0.1 * level as f64; let upscale = 1.6 - 0.1 * level as f64;
font.size *= upscale; text.size *= upscale;
font.strong = true; text.strong = true;
}); });
ctx.template += body; ctx.template += body;
ctx.template.restore(); ctx.template.restore();
@ -105,23 +105,23 @@ impl Walk for EnumNode {
} }
fn walk_item(ctx: &mut EvalContext, label: Str, body: Template) { fn walk_item(ctx: &mut EvalContext, label: Str, body: Template) {
ctx.template += Template::from_block(move |state| { ctx.template += Template::from_block(move |style| {
let label = ParNode { let label = ParNode {
dir: state.dirs.inline, dir: style.dir,
line_spacing: state.line_spacing(), line_spacing: style.line_spacing(),
children: vec![ParChild::Text( children: vec![ParChild::Text(
(&label).into(), (&label).into(),
state.aligns.inline, style.aligns.inline,
Rc::clone(&state.font), Rc::clone(&style.text),
vec![], vec![],
)], )],
}; };
StackNode { StackNode {
dirs: Gen::new(state.dirs.block, state.dirs.inline), dirs: Gen::new(Dir::TTB, style.dir),
children: vec![ children: vec![
StackChild::Any(label.into(), Gen::default()), StackChild::Any(label.into(), Gen::default()),
StackChild::Spacing((state.font.size / 2.0).into()), StackChild::Spacing((style.text.size / 2.0).into()),
StackChild::Any(body.to_stack(&state).into(), Gen::default()), StackChild::Any(body.to_stack(&style).into(), Gen::default()),
], ],
} }
}); });

View File

@ -59,7 +59,7 @@ impl FromStr for RgbaColor {
/// - `7a03c2` (without alpha), /// - `7a03c2` (without alpha),
/// - `abcdefff` (with alpha). /// - `abcdefff` (with alpha).
/// ///
/// Both lower and upper case is fine. /// The hashtag is optional and both lower and upper case are fine.
fn from_str(hex_str: &str) -> Result<Self, Self::Err> { fn from_str(hex_str: &str) -> Result<Self, Self::Err> {
let hex_str = hex_str.strip_prefix('#').unwrap_or(hex_str); let hex_str = hex_str.strip_prefix('#').unwrap_or(hex_str);
if !hex_str.is_ascii() { if !hex_str.is_ascii() {

View File

@ -6,7 +6,7 @@ use unicode_bidi::{BidiInfo, Level};
use xi_unicode::LineBreakIterator; use xi_unicode::LineBreakIterator;
use super::*; use super::*;
use crate::eval::FontState; use crate::style::TextStyle;
use crate::util::{EcoString, RangeExt, SliceExt}; use crate::util::{EcoString, RangeExt, SliceExt};
type Range = std::ops::Range<usize>; type Range = std::ops::Range<usize>;
@ -29,7 +29,7 @@ pub enum ParChild {
/// Spacing between other nodes. /// Spacing between other nodes.
Spacing(Linear), Spacing(Linear),
/// A run of text and how to align it in its line. /// A run of text and how to align it in its line.
Text(EcoString, Align, Rc<FontState>, Vec<Decoration>), Text(EcoString, Align, Rc<TextStyle>, Vec<Decoration>),
/// Any child node and how to align it in its line. /// Any child node and how to align it in its line.
Any(LayoutNode, Align, Vec<Decoration>), Any(LayoutNode, Align, Vec<Decoration>),
} }
@ -139,11 +139,11 @@ impl<'a> ParLayouter<'a> {
items.push(ParItem::Spacing(resolved)); items.push(ParItem::Spacing(resolved));
ranges.push(range); ranges.push(range);
} }
ParChild::Text(_, align, state, decos) => { ParChild::Text(_, align, style, decos) => {
// TODO: Also split by language and script. // TODO: Also split by language and script.
for (subrange, dir) in split_runs(&bidi, range) { for (subrange, dir) in split_runs(&bidi, range) {
let text = &bidi.text[subrange.clone()]; let text = &bidi.text[subrange.clone()];
let shaped = shape(ctx, text, dir, state); let shaped = shape(ctx, text, dir, style);
items.push(ParItem::Text(shaped, *align, decos)); items.push(ParItem::Text(shaped, *align, decos));
ranges.push(subrange); ranges.push(subrange);
} }

View File

@ -4,9 +4,9 @@ use std::ops::Range;
use rustybuzz::UnicodeBuffer; use rustybuzz::UnicodeBuffer;
use super::{Element, Frame, Glyph, LayoutContext, Text}; use super::{Element, Frame, Glyph, LayoutContext, Text};
use crate::eval::FontState;
use crate::font::{Face, FaceId, FontVariant}; use crate::font::{Face, FaceId, FontVariant};
use crate::geom::{Dir, Em, Length, Point, Size}; use crate::geom::{Dir, Em, Length, Point, Size};
use crate::style::TextStyle;
use crate::util::SliceExt; use crate::util::SliceExt;
/// Shape text into [`ShapedText`]. /// Shape text into [`ShapedText`].
@ -14,7 +14,7 @@ pub fn shape<'a>(
ctx: &mut LayoutContext, ctx: &mut LayoutContext,
text: &'a str, text: &'a str,
dir: Dir, dir: Dir,
state: &'a FontState, style: &'a TextStyle,
) -> ShapedText<'a> { ) -> ShapedText<'a> {
let mut glyphs = vec![]; let mut glyphs = vec![];
if !text.is_empty() { if !text.is_empty() {
@ -24,19 +24,19 @@ pub fn shape<'a>(
0, 0,
text, text,
dir, dir,
state.size, style.size,
state.variant(), style.variant(),
state.families(), style.families(),
None, None,
); );
} }
let (size, baseline) = measure(ctx, &glyphs, state); let (size, baseline) = measure(ctx, &glyphs, style);
ShapedText { ShapedText {
text, text,
dir, dir,
state, style,
size, size,
baseline, baseline,
glyphs: Cow::Owned(glyphs), glyphs: Cow::Owned(glyphs),
@ -54,7 +54,7 @@ pub struct ShapedText<'a> {
/// The text direction. /// The text direction.
pub dir: Dir, pub dir: Dir,
/// The properties used for font selection. /// The properties used for font selection.
pub state: &'a FontState, pub style: &'a TextStyle,
/// The font size. /// The font size.
pub size: Size, pub size: Size,
/// The baseline from the top of the frame. /// The baseline from the top of the frame.
@ -93,9 +93,9 @@ impl<'a> ShapedText<'a> {
let mut text = Text { let mut text = Text {
face_id, face_id,
size: self.state.size, size: self.style.size,
width: Length::zero(), width: Length::zero(),
fill: self.state.fill, fill: self.style.fill,
glyphs: vec![], glyphs: vec![],
}; };
@ -123,17 +123,17 @@ impl<'a> ShapedText<'a> {
text_range: Range<usize>, text_range: Range<usize>,
) -> ShapedText<'a> { ) -> ShapedText<'a> {
if let Some(glyphs) = self.slice_safe_to_break(text_range.clone()) { if let Some(glyphs) = self.slice_safe_to_break(text_range.clone()) {
let (size, baseline) = measure(ctx, glyphs, self.state); let (size, baseline) = measure(ctx, glyphs, self.style);
Self { Self {
text: &self.text[text_range], text: &self.text[text_range],
dir: self.dir, dir: self.dir,
state: self.state, style: self.style,
size, size,
baseline, baseline,
glyphs: Cow::Borrowed(glyphs), glyphs: Cow::Borrowed(glyphs),
} }
} else { } else {
shape(ctx, &self.text[text_range], self.dir, self.state) shape(ctx, &self.text[text_range], self.dir, self.style)
} }
} }
@ -334,7 +334,7 @@ fn shape_segment<'a>(
fn measure( fn measure(
ctx: &mut LayoutContext, ctx: &mut LayoutContext,
glyphs: &[ShapedGlyph], glyphs: &[ShapedGlyph],
state: &FontState, style: &TextStyle,
) -> (Size, Length) { ) -> (Size, Length) {
let mut width = Length::zero(); let mut width = Length::zero();
let mut top = Length::zero(); let mut top = Length::zero();
@ -342,15 +342,15 @@ fn measure(
// Expand top and bottom by reading the face's vertical metrics. // Expand top and bottom by reading the face's vertical metrics.
let mut expand = |face: &Face| { let mut expand = |face: &Face| {
top.set_max(face.vertical_metric(state.top_edge, state.size)); top.set_max(face.vertical_metric(style.top_edge, style.size));
bottom.set_max(-face.vertical_metric(state.bottom_edge, state.size)); bottom.set_max(-face.vertical_metric(style.bottom_edge, style.size));
}; };
if glyphs.is_empty() { if glyphs.is_empty() {
// When there are no glyphs, we just use the vertical metrics of the // When there are no glyphs, we just use the vertical metrics of the
// first available font. // first available font.
for family in state.families() { for family in style.families() {
if let Some(face_id) = ctx.fonts.select(family, state.variant) { if let Some(face_id) = ctx.fonts.select(family, style.variant) {
expand(ctx.fonts.get(face_id)); expand(ctx.fonts.get(face_id));
break; break;
} }
@ -361,7 +361,7 @@ fn measure(
expand(face); expand(face);
for glyph in group { for glyph in group {
width += glyph.x_advance.to_length(state.size); width += glyph.x_advance.to_length(style.size);
} }
} }
} }

View File

@ -8,7 +8,7 @@
//! - **Evaluation:** The next step is to [evaluate] the markup. This produces a //! - **Evaluation:** The next step is to [evaluate] the markup. This produces a
//! [module], consisting of a scope of values that were exported by the code //! [module], consisting of a scope of values that were exported by the code
//! and a template with the contents of the module. This template can be //! and a template with the contents of the module. This template can be
//! [instantiated] in a state to produce a layout tree, a high-level, fully //! [instantiated] with a style to produce a layout tree, a high-level, fully
//! styled representation of the document. The nodes of this tree are //! styled representation of the document. The nodes of this tree are
//! self-contained and order-independent and thus much better suited for //! self-contained and order-independent and thus much better suited for
//! layouting than the raw markup. //! layouting than the raw markup.
@ -18,7 +18,6 @@
//! - **Exporting:** The finished layout can be exported into a supported //! - **Exporting:** The finished layout can be exported into a supported
//! format. Currently, the only supported output format is [PDF]. //! format. Currently, the only supported output format is [PDF].
//! //!
//! [tokens]: parse::Tokens //! [tokens]: parse::Tokens
//! [parsed]: parse::parse //! [parsed]: parse::parse
//! [markup]: syntax::Markup //! [markup]: syntax::Markup
@ -40,16 +39,16 @@ pub mod image;
pub mod layout; pub mod layout;
pub mod library; pub mod library;
pub mod loading; pub mod loading;
pub mod paper;
pub mod parse; pub mod parse;
pub mod source; pub mod source;
pub mod style;
pub mod syntax; pub mod syntax;
pub mod util; pub mod util;
use std::rc::Rc; use std::rc::Rc;
use crate::diag::TypResult; use crate::diag::TypResult;
use crate::eval::{Module, Scope, State}; use crate::eval::{Module, Scope};
use crate::font::FontStore; use crate::font::FontStore;
use crate::image::ImageStore; use crate::image::ImageStore;
#[cfg(feature = "layout-cache")] #[cfg(feature = "layout-cache")]
@ -57,6 +56,7 @@ use crate::layout::{EvictionPolicy, LayoutCache};
use crate::layout::{Frame, LayoutTree}; use crate::layout::{Frame, LayoutTree};
use crate::loading::Loader; use crate::loading::Loader;
use crate::source::{SourceId, SourceStore}; use crate::source::{SourceId, SourceStore};
use crate::style::Style;
use crate::syntax::Markup; use crate::syntax::Markup;
/// The core context which holds the loader, configuration and cached artifacts. /// The core context which holds the loader, configuration and cached artifacts.
@ -74,8 +74,8 @@ pub struct Context {
pub layouts: LayoutCache, pub layouts: LayoutCache,
/// The standard library scope. /// The standard library scope.
std: Scope, std: Scope,
/// The default state. /// The default style.
state: State, style: Style,
} }
impl Context { impl Context {
@ -94,9 +94,9 @@ impl Context {
&self.std &self.std
} }
/// A read-only reference to the state. /// A read-only reference to the style.
pub fn state(&self) -> &State { pub fn style(&self) -> &Style {
&self.state &self.style
} }
/// Parse a source file and return the resulting markup. /// Parse a source file and return the resulting markup.
@ -113,7 +113,7 @@ impl Context {
/// Execute a source file and produce the resulting layout tree. /// Execute a source file and produce the resulting layout tree.
pub fn execute(&mut self, id: SourceId) -> TypResult<LayoutTree> { pub fn execute(&mut self, id: SourceId) -> TypResult<LayoutTree> {
let module = self.evaluate(id)?; let module = self.evaluate(id)?;
Ok(module.template.to_tree(&self.state)) Ok(module.template.to_tree(&self.style))
} }
/// Typeset a source file into a collection of layouted frames. /// Typeset a source file into a collection of layouted frames.
@ -139,7 +139,7 @@ impl Context {
/// This struct is created by [`Context::builder`]. /// This struct is created by [`Context::builder`].
pub struct ContextBuilder { pub struct ContextBuilder {
std: Option<Scope>, std: Option<Scope>,
state: Option<State>, style: Option<Style>,
#[cfg(feature = "layout-cache")] #[cfg(feature = "layout-cache")]
policy: EvictionPolicy, policy: EvictionPolicy,
#[cfg(feature = "layout-cache")] #[cfg(feature = "layout-cache")]
@ -155,8 +155,8 @@ impl ContextBuilder {
} }
/// The initial properties for page size, font selection and so on. /// The initial properties for page size, font selection and so on.
pub fn state(mut self, state: State) -> Self { pub fn style(mut self, style: Style) -> Self {
self.state = Some(state); self.style = Some(style);
self self
} }
@ -188,7 +188,7 @@ impl ContextBuilder {
#[cfg(feature = "layout-cache")] #[cfg(feature = "layout-cache")]
layouts: LayoutCache::new(self.policy, self.max_size), layouts: LayoutCache::new(self.policy, self.max_size),
std: self.std.unwrap_or(library::new()), std: self.std.unwrap_or(library::new()),
state: self.state.unwrap_or_default(), style: self.style.unwrap_or_default(),
} }
} }
} }
@ -197,7 +197,7 @@ impl Default for ContextBuilder {
fn default() -> Self { fn default() -> Self {
Self { Self {
std: None, std: None,
state: None, style: None,
#[cfg(feature = "layout-cache")] #[cfg(feature = "layout-cache")]
policy: EvictionPolicy::default(), policy: EvictionPolicy::default(),
#[cfg(feature = "layout-cache")] #[cfg(feature = "layout-cache")]

View File

@ -61,12 +61,12 @@ fn rect_impl(
fill: Option<Color>, fill: Option<Color>,
body: Template, body: Template,
) -> Value { ) -> Value {
Value::Template(Template::from_inline(move |state| { Value::Template(Template::from_inline(move |style| {
let mut node = LayoutNode::new(FixedNode { let mut node = LayoutNode::new(FixedNode {
width, width,
height, height,
aspect, aspect,
child: body.to_stack(state).into(), child: body.to_stack(style).into(),
}); });
if let Some(fill) = fill { if let Some(fill) = fill {
@ -114,7 +114,7 @@ fn ellipse_impl(
fill: Option<Color>, fill: Option<Color>,
body: Template, body: Template,
) -> Value { ) -> Value {
Value::Template(Template::from_inline(move |state| { Value::Template(Template::from_inline(move |style| {
// This padding ratio ensures that the rectangular padded region fits // This padding ratio ensures that the rectangular padded region fits
// perfectly into the ellipse. // perfectly into the ellipse.
const PAD: f64 = 0.5 - SQRT_2 / 4.0; const PAD: f64 = 0.5 - SQRT_2 / 4.0;
@ -125,7 +125,7 @@ fn ellipse_impl(
aspect, aspect,
child: LayoutNode::new(PadNode { child: LayoutNode::new(PadNode {
padding: Sides::splat(Relative::new(PAD).into()), padding: Sides::splat(Relative::new(PAD).into()),
child: body.to_stack(state).into(), child: body.to_stack(style).into(),
}), }),
}); });

View File

@ -1,6 +1,6 @@
use super::*; use super::*;
use crate::layout::{FixedNode, GridNode, PadNode, StackChild, StackNode, TrackSizing}; use crate::layout::{FixedNode, GridNode, PadNode, StackChild, StackNode, TrackSizing};
use crate::paper::{Paper, PaperClass}; use crate::style::{Paper, PaperClass};
/// `page`: Configure pages. /// `page`: Configure pages.
pub fn page(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> { pub fn page(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
@ -21,8 +21,8 @@ pub fn page(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
let bottom = args.named("bottom")?; let bottom = args.named("bottom")?;
let flip = args.named("flip")?; let flip = args.named("flip")?;
ctx.template.modify(move |state| { ctx.template.modify(move |style| {
let page = state.page_mut(); let page = style.page_mut();
if let Some(paper) = paper { if let Some(paper) = paper {
page.class = paper.class(); page.class = paper.class();
@ -98,13 +98,13 @@ pub fn align(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
} }
let realign = |template: &mut Template| { let realign = |template: &mut Template| {
template.modify(move |state| { template.modify(move |style| {
if let Some(horizontal) = horizontal { if let Some(horizontal) = horizontal {
state.aligns.inline = horizontal; style.aligns.inline = horizontal;
} }
if let Some(vertical) = vertical { if let Some(vertical) = vertical {
state.aligns.block = vertical; style.aligns.block = vertical;
} }
}); });
@ -147,12 +147,12 @@ pub fn boxed(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
let width = args.named("width")?; let width = args.named("width")?;
let height = args.named("height")?; let height = args.named("height")?;
let body: Template = args.eat().unwrap_or_default(); let body: Template = args.eat().unwrap_or_default();
Ok(Value::Template(Template::from_inline(move |state| { Ok(Value::Template(Template::from_inline(move |style| {
FixedNode { FixedNode {
width, width,
height, height,
aspect: None, aspect: None,
child: body.to_stack(state).into(), child: body.to_stack(style).into(),
} }
}))) })))
} }
@ -160,8 +160,8 @@ pub fn boxed(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
/// `block`: Place content in a block. /// `block`: Place content in a block.
pub fn block(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { pub fn block(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
let body: Template = args.expect("body")?; let body: Template = args.expect("body")?;
Ok(Value::Template(Template::from_block(move |state| { Ok(Value::Template(Template::from_block(move |style| {
body.to_stack(state) body.to_stack(style)
}))) })))
} }
@ -181,10 +181,10 @@ pub fn pad(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
bottom.or(all).unwrap_or_default(), bottom.or(all).unwrap_or_default(),
); );
Ok(Value::Template(Template::from_block(move |state| { Ok(Value::Template(Template::from_block(move |style| {
PadNode { PadNode {
padding, padding,
child: body.to_stack(&state).into(), child: body.to_stack(&style).into(),
} }
}))) })))
} }
@ -208,20 +208,20 @@ pub fn stack(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
let spacing = args.named("spacing")?; let spacing = args.named("spacing")?;
let list: Vec<Child> = args.all().collect(); let list: Vec<Child> = args.all().collect();
Ok(Value::Template(Template::from_block(move |state| { Ok(Value::Template(Template::from_block(move |style| {
let mut dirs = Gen::new(None, dir).unwrap_or(state.dirs); let mut dirs = Gen::new(style.dir, dir.unwrap_or(Dir::TTB));
// If the directions become aligned, fix up the inline direction since // If the directions become aligned, fix up the inline direction since
// that's the one that is not user-defined. // that's the one that is not user-defined.
if dirs.block.axis() == dirs.inline.axis() { if dirs.inline.axis() == dirs.block.axis() {
dirs.inline = state.dirs.block; dirs.inline = Dir::TTB;
} }
// Use the current alignments for all children, but take care to apply // Use the current alignments for all children, but take care to apply
// them to the correct axes (by swapping them if the stack axes are // them to the correct axes (by swapping them if the stack axes are
// different from the state axes). // different from the style axes).
let mut aligns = state.aligns; let mut aligns = style.aligns;
if dirs.block.axis() == state.dirs.inline.axis() { if dirs.inline.axis() != style.dir.axis() {
aligns = Gen::new(aligns.block, aligns.inline); aligns = Gen::new(aligns.block, aligns.inline);
} }
@ -240,7 +240,7 @@ pub fn stack(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
children.push(StackChild::Spacing(v)); children.push(StackChild::Spacing(v));
} }
let node = template.to_stack(state).into(); let node = template.to_stack(style).into();
children.push(StackChild::Any(node, aligns)); children.push(StackChild::Any(node, aligns));
delayed = spacing; delayed = spacing;
} }
@ -293,10 +293,12 @@ pub fn grid(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
let children: Vec<Template> = args.all().collect(); let children: Vec<Template> = args.all().collect();
Ok(Value::Template(Template::from_block(move |state| { Ok(Value::Template(Template::from_block(move |style| {
// If the directions become aligned, try to fix up the direction which // If the directions become aligned, try to fix up the direction which
// is not user-defined. // is not user-defined.
let mut dirs = Gen::new(column_dir, row_dir).unwrap_or(state.dirs); let mut dirs =
Gen::new(column_dir.unwrap_or(style.dir), row_dir.unwrap_or(Dir::TTB));
if dirs.block.axis() == dirs.inline.axis() { if dirs.block.axis() == dirs.inline.axis() {
let target = if column_dir.is_some() { let target = if column_dir.is_some() {
&mut dirs.block &mut dirs.block
@ -304,15 +306,15 @@ pub fn grid(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
&mut dirs.inline &mut dirs.inline
}; };
*target = if target.axis() == state.dirs.inline.axis() { *target = if target.axis() == style.dir.axis() {
state.dirs.block Dir::TTB
} else { } else {
state.dirs.inline style.dir
}; };
} }
let children = let children =
children.iter().map(|child| child.to_stack(&state).into()).collect(); children.iter().map(|child| child.to_stack(&style).into()).collect();
GridNode { GridNode {
dirs, dirs,

View File

@ -17,10 +17,11 @@ use std::convert::TryFrom;
use std::rc::Rc; use std::rc::Rc;
use crate::diag::{At, TypResult}; use crate::diag::{At, TypResult};
use crate::eval::{Args, Array, EvalContext, Scope, State, Str, Template, Value}; use crate::eval::{Args, Array, EvalContext, Scope, Str, Template, Value};
use crate::font::{FontFamily, FontStretch, FontStyle, FontWeight, VerticalFontMetric}; use crate::font::{FontFamily, FontStretch, FontStyle, FontWeight, VerticalFontMetric};
use crate::geom::*; use crate::geom::*;
use crate::layout::LayoutNode; use crate::layout::LayoutNode;
use crate::style::Style;
use crate::syntax::{Span, Spanned}; use crate::syntax::{Span, Spanned};
/// Construct a scope containing all standard library definitions. /// Construct a scope containing all standard library definitions.

View File

@ -48,55 +48,55 @@ pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
let fallback = args.named("fallback")?; let fallback = args.named("fallback")?;
let body = args.eat::<Template>(); let body = args.eat::<Template>();
let f = move |state: &mut State| { let f = move |style_: &mut Style| {
let font = state.font_mut(); let text = style_.text_mut();
if let Some(size) = size { if let Some(size) = size {
font.size = size.resolve(font.size); text.size = size.resolve(text.size);
} }
if let Some(style) = style { if let Some(style) = style {
font.variant.style = style; text.variant.style = style;
} }
if let Some(weight) = weight { if let Some(weight) = weight {
font.variant.weight = weight; text.variant.weight = weight;
} }
if let Some(stretch) = stretch { if let Some(stretch) = stretch {
font.variant.stretch = stretch; text.variant.stretch = stretch;
} }
if let Some(top_edge) = top_edge { if let Some(top_edge) = top_edge {
font.top_edge = top_edge; text.top_edge = top_edge;
} }
if let Some(bottom_edge) = bottom_edge { if let Some(bottom_edge) = bottom_edge {
font.bottom_edge = bottom_edge; text.bottom_edge = bottom_edge;
} }
if let Some(fill) = fill { if let Some(fill) = fill {
font.fill = Paint::Color(fill); text.fill = Paint::Color(fill);
} }
if let Some(FontDef(list)) = &list { if let Some(FontDef(list)) = &list {
font.families_mut().list = list.clone(); text.families_mut().list = list.clone();
} }
if let Some(FamilyDef(serif)) = &serif { if let Some(FamilyDef(serif)) = &serif {
font.families_mut().serif = serif.clone(); text.families_mut().serif = serif.clone();
} }
if let Some(FamilyDef(sans_serif)) = &sans_serif { if let Some(FamilyDef(sans_serif)) = &sans_serif {
font.families_mut().sans_serif = sans_serif.clone(); text.families_mut().sans_serif = sans_serif.clone();
} }
if let Some(FamilyDef(monospace)) = &monospace { if let Some(FamilyDef(monospace)) = &monospace {
font.families_mut().monospace = monospace.clone(); text.families_mut().monospace = monospace.clone();
} }
if let Some(fallback) = fallback { if let Some(fallback) = fallback {
font.fallback = fallback; text.fallback = fallback;
} }
}; };
@ -113,8 +113,8 @@ pub fn par(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
let par_spacing = args.named("spacing")?; let par_spacing = args.named("spacing")?;
let line_spacing = args.named("leading")?; let line_spacing = args.named("leading")?;
ctx.template.modify(move |state| { ctx.template.modify(move |style| {
let par = state.par_mut(); let par = style.par_mut();
if let Some(par_spacing) = par_spacing { if let Some(par_spacing) = par_spacing {
par.par_spacing = par_spacing; par.par_spacing = par_spacing;
@ -144,7 +144,7 @@ pub fn lang(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
}; };
if let Some(dir) = dir { if let Some(dir) = dir {
ctx.template.modify(move |state| state.dirs.inline = dir); ctx.template.modify(move |style| style.dir = dir);
} }
ctx.template.parbreak(); ctx.template.parbreak();

View File

@ -1,68 +1,73 @@
//! Style properties.
mod paper;
pub use paper::*;
use std::rc::Rc; use std::rc::Rc;
use crate::font::{ use crate::font::{
FontFamily, FontStretch, FontStyle, FontVariant, FontWeight, VerticalFontMetric, FontFamily, FontStretch, FontStyle, FontVariant, FontWeight, VerticalFontMetric,
}; };
use crate::geom::*; use crate::geom::*;
use crate::paper::{PaperClass, ISO_A4};
/// Defines an set of properties a template can be instantiated with. /// Defines a set of properties a template can be instantiated with.
#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct State { pub struct Style {
/// The direction for text and other inline objects. /// The direction for text and other inline objects.
pub dirs: Gen<Dir>, pub dir: Dir,
/// The alignments of layouts in their parents. /// The alignments of layouts in their parents.
pub aligns: Gen<Align>, pub aligns: Gen<Align>,
/// The page settings. /// The page settings.
pub page: Rc<PageState>, pub page: Rc<PageStyle>,
/// The paragraph settings. /// The paragraph settings.
pub par: Rc<ParState>, pub par: Rc<ParStyle>,
/// The font settings. /// The current text settings.
pub font: Rc<FontState>, pub text: Rc<TextStyle>,
} }
impl State { impl Style {
/// Access the `page` state mutably. /// Access the `page` style mutably.
pub fn page_mut(&mut self) -> &mut PageState { pub fn page_mut(&mut self) -> &mut PageStyle {
Rc::make_mut(&mut self.page) Rc::make_mut(&mut self.page)
} }
/// Access the `par` state mutably. /// Access the `par` style mutably.
pub fn par_mut(&mut self) -> &mut ParState { pub fn par_mut(&mut self) -> &mut ParStyle {
Rc::make_mut(&mut self.par) Rc::make_mut(&mut self.par)
} }
/// Access the `font` state mutably. /// Access the `text` style mutably.
pub fn font_mut(&mut self) -> &mut FontState { pub fn text_mut(&mut self) -> &mut TextStyle {
Rc::make_mut(&mut self.font) Rc::make_mut(&mut self.text)
} }
/// The resolved line spacing. /// The resolved line spacing.
pub fn line_spacing(&self) -> Length { pub fn line_spacing(&self) -> Length {
self.par.line_spacing.resolve(self.font.size) self.par.line_spacing.resolve(self.text.size)
} }
/// The resolved paragraph spacing. /// The resolved paragraph spacing.
pub fn par_spacing(&self) -> Length { pub fn par_spacing(&self) -> Length {
self.par.par_spacing.resolve(self.font.size) self.par.par_spacing.resolve(self.text.size)
} }
} }
impl Default for State { impl Default for Style {
fn default() -> Self { fn default() -> Self {
Self { Self {
dirs: Gen::new(Dir::LTR, Dir::TTB), dir: Dir::LTR,
aligns: Gen::splat(Align::Start), aligns: Gen::splat(Align::Start),
page: Rc::new(PageState::default()), page: Rc::new(PageStyle::default()),
par: Rc::new(ParState::default()), par: Rc::new(ParStyle::default()),
font: Rc::new(FontState::default()), text: Rc::new(TextStyle::default()),
} }
} }
} }
/// Defines page properties. /// Defines style properties of pages.
#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct PageState { pub struct PageStyle {
/// The class of this page. /// The class of this page.
pub class: PaperClass, pub class: PaperClass,
/// The width and height of the page. /// The width and height of the page.
@ -72,7 +77,7 @@ pub struct PageState {
pub margins: Sides<Option<Linear>>, pub margins: Sides<Option<Linear>>,
} }
impl PageState { impl PageStyle {
/// The resolved margins. /// The resolved margins.
pub fn margins(&self) -> Sides<Linear> { pub fn margins(&self) -> Sides<Linear> {
let default = self.class.default_margins(); let default = self.class.default_margins();
@ -85,9 +90,9 @@ impl PageState {
} }
} }
impl Default for PageState { impl Default for PageStyle {
fn default() -> Self { fn default() -> Self {
let paper = ISO_A4; let paper = Paper::ISO_A4;
Self { Self {
class: paper.class(), class: paper.class(),
size: paper.size(), size: paper.size(),
@ -96,16 +101,16 @@ impl Default for PageState {
} }
} }
/// Defines paragraph properties. /// Defines style properties of paragraphs.
#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct ParState { pub struct ParStyle {
/// The spacing between paragraphs (dependent on scaled font size). /// The spacing between paragraphs (dependent on scaled font size).
pub par_spacing: Linear, pub par_spacing: Linear,
/// The spacing between lines (dependent on scaled font size). /// The spacing between lines (dependent on scaled font size).
pub line_spacing: Linear, pub line_spacing: Linear,
} }
impl Default for ParState { impl Default for ParStyle {
fn default() -> Self { fn default() -> Self {
Self { Self {
par_spacing: Relative::new(1.2).into(), par_spacing: Relative::new(1.2).into(),
@ -114,9 +119,9 @@ impl Default for ParState {
} }
} }
/// Defines font properties. /// Defines style properties of text.
#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct FontState { pub struct TextStyle {
/// The font size. /// The font size.
pub size: Length, pub size: Length,
/// The selected font variant (the final variant also depends on `strong` /// The selected font variant (the final variant also depends on `strong`
@ -130,7 +135,7 @@ pub struct FontState {
pub fill: Paint, pub fill: Paint,
/// A list of font families with generic class definitions (the final /// A list of font families with generic class definitions (the final
/// family list also depends on `monospace`). /// family list also depends on `monospace`).
pub families: Rc<FamilyState>, pub families: Rc<FamilyStyle>,
/// Whether 300 extra font weight should be added to what is defined by the /// Whether 300 extra font weight should be added to what is defined by the
/// `variant`. /// `variant`.
pub strong: bool, pub strong: bool,
@ -142,7 +147,7 @@ pub struct FontState {
pub fallback: bool, pub fallback: bool,
} }
impl FontState { impl TextStyle {
/// The resolved variant with `strong` and `emph` factored in. /// The resolved variant with `strong` and `emph` factored in.
pub fn variant(&self) -> FontVariant { pub fn variant(&self) -> FontVariant {
let mut variant = self.variant; let mut variant = self.variant;
@ -188,13 +193,13 @@ impl FontState {
head.iter().chain(core).chain(tail).map(String::as_str) head.iter().chain(core).chain(tail).map(String::as_str)
} }
/// Access the `families` state mutably. /// Access the `families` style mutably.
pub fn families_mut(&mut self) -> &mut FamilyState { pub fn families_mut(&mut self) -> &mut FamilyStyle {
Rc::make_mut(&mut self.families) Rc::make_mut(&mut self.families)
} }
} }
impl Default for FontState { impl Default for TextStyle {
fn default() -> Self { fn default() -> Self {
Self { Self {
size: Length::pt(11.0), size: Length::pt(11.0),
@ -206,7 +211,7 @@ impl Default for FontState {
top_edge: VerticalFontMetric::CapHeight, top_edge: VerticalFontMetric::CapHeight,
bottom_edge: VerticalFontMetric::Baseline, bottom_edge: VerticalFontMetric::Baseline,
fill: Paint::Color(Color::Rgba(RgbaColor::BLACK)), fill: Paint::Color(Color::Rgba(RgbaColor::BLACK)),
families: Rc::new(FamilyState::default()), families: Rc::new(FamilyStyle::default()),
strong: false, strong: false,
emph: false, emph: false,
monospace: false, monospace: false,
@ -215,9 +220,9 @@ impl Default for FontState {
} }
} }
/// Font family definitions. /// Font list with family definitions.
#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct FamilyState { pub struct FamilyStyle {
/// The user-defined list of font families. /// The user-defined list of font families.
pub list: Rc<Vec<FontFamily>>, pub list: Rc<Vec<FontFamily>>,
/// Definition of serif font families. /// Definition of serif font families.
@ -230,7 +235,7 @@ pub struct FamilyState {
pub base: Rc<Vec<String>>, pub base: Rc<Vec<String>>,
} }
impl Default for FamilyState { impl Default for FamilyStyle {
fn default() -> Self { fn default() -> Self {
Self { Self {
list: Rc::new(vec![FontFamily::SansSerif]), list: Rc::new(vec![FontFamily::SansSerif]),

View File

@ -1,5 +1,3 @@
//! Predefined papers.
use crate::geom::{Length, Linear, Relative, Sides, Size}; use crate::geom::{Length, Linear, Relative, Sides, Size};
/// Specification of a paper. /// Specification of a paper.
@ -13,23 +11,6 @@ pub struct Paper {
height: f64, height: f64,
} }
impl Paper {
/// The paper with the given name.
pub fn from_name(name: &str) -> Option<Self> {
parse_paper(name)
}
/// The class of the paper.
pub fn class(self) -> PaperClass {
self.class
}
/// The size of the paper.
pub fn size(self) -> Size {
Size::new(Length::mm(self.width), Length::mm(self.height))
}
}
/// Defines default margins for a class of related papers. /// Defines default margins for a class of related papers.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum PaperClass { pub enum PaperClass {
@ -57,21 +38,38 @@ impl PaperClass {
macro_rules! papers { macro_rules! papers {
($(($var:ident: $class:ident, $width:expr, $height: expr, $($pats:tt)*))*) => { ($(($var:ident: $class:ident, $width:expr, $height: expr, $($pats:tt)*))*) => {
$(papers!(@$var, stringify!($($pats)*), $class, $width, $height);)* impl Paper {
/// Parse a paper from its name.
fn parse_paper(paper: &str) -> Option<Paper> { ///
match paper.to_lowercase().as_str() { /// Both lower and upper case are fine.
$($($pats)* => Some($var),)* pub fn from_name(name: &str) -> Option<Self> {
match name.to_lowercase().as_str() {
$($($pats)* => Some(Self::$var),)*
_ => None, _ => None,
} }
} }
/// The class of the paper.
pub fn class(self) -> PaperClass {
self.class
}
/// The size of the paper.
pub fn size(self) -> Size {
Size::new(Length::mm(self.width), Length::mm(self.height))
}
}
/// Predefined papers.
///
/// Each paper is parsable from its name in kebab-case.
impl Paper {
$(papers!(@$var, stringify!($($pats)*), $class, $width, $height);)*
}
}; };
(@$var:ident, $names:expr, $class:ident, $width:expr, $height:expr) => { (@$var:ident, $names:expr, $class:ident, $width:expr, $height:expr) => {
#[doc = "Paper with name `"] pub const $var: Self = Self {
#[doc = $names]
#[doc = "`."]
pub const $var: Paper = Paper {
class: PaperClass::$class, class: PaperClass::$class,
width: $width, width: $width,
height: $height, height: $height,

View File

@ -10,7 +10,7 @@ use ttf_parser::{GlyphId, OutlineBuilder};
use walkdir::WalkDir; use walkdir::WalkDir;
use typst::diag::Error; use typst::diag::Error;
use typst::eval::{State, Value}; use typst::eval::Value;
use typst::font::Face; use typst::font::Face;
use typst::geom::{ use typst::geom::{
self, Color, Length, Paint, PathElement, Point, RgbaColor, Sides, Size, self, Color, Length, Paint, PathElement, Point, RgbaColor, Sides, Size,
@ -22,6 +22,7 @@ use typst::layout::{layout, Element, Frame, Geometry, Text};
use typst::loading::FsLoader; use typst::loading::FsLoader;
use typst::parse::Scanner; use typst::parse::Scanner;
use typst::source::SourceFile; use typst::source::SourceFile;
use typst::style::Style;
use typst::syntax::{Pos, Span}; use typst::syntax::{Pos, Span};
use typst::Context; use typst::Context;
@ -62,10 +63,10 @@ fn main() {
// We want to have "unbounded" pages, so we allow them to be infinitely // We want to have "unbounded" pages, so we allow them to be infinitely
// large and fit them to match their content. // large and fit them to match their content.
let mut state = State::default(); let mut style = Style::default();
state.page_mut().size = Size::new(Length::pt(120.0), Length::inf()); style.page_mut().size = Size::new(Length::pt(120.0), Length::inf());
state.page_mut().margins = Sides::splat(Some(Length::pt(10.0).into())); style.page_mut().margins = Sides::splat(Some(Length::pt(10.0).into()));
state.font_mut().size = Length::pt(10.0); style.text_mut().size = Length::pt(10.0);
// Hook up an assert function into the global scope. // Hook up an assert function into the global scope.
let mut std = typst::library::new(); let mut std = typst::library::new();
@ -83,7 +84,7 @@ fn main() {
// Create loader and context. // Create loader and context.
let loader = FsLoader::new().with_path(FONT_DIR).wrap(); let loader = FsLoader::new().with_path(FONT_DIR).wrap();
let mut ctx = Context::builder().std(std).state(state).build(loader); let mut ctx = Context::builder().std(std).style(style).build(loader);
// Run all the tests. // Run all the tests.
let mut ok = true; let mut ok = true;