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) {
let (mut ctx, id) = context();
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) {

View File

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

View File

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

View File

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

View File

@ -59,7 +59,7 @@ impl FromStr for RgbaColor {
/// - `7a03c2` (without 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> {
let hex_str = hex_str.strip_prefix('#').unwrap_or(hex_str);
if !hex_str.is_ascii() {

View File

@ -6,7 +6,7 @@ use unicode_bidi::{BidiInfo, Level};
use xi_unicode::LineBreakIterator;
use super::*;
use crate::eval::FontState;
use crate::style::TextStyle;
use crate::util::{EcoString, RangeExt, SliceExt};
type Range = std::ops::Range<usize>;
@ -29,7 +29,7 @@ pub enum ParChild {
/// Spacing between other nodes.
Spacing(Linear),
/// 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(LayoutNode, Align, Vec<Decoration>),
}
@ -139,11 +139,11 @@ impl<'a> ParLayouter<'a> {
items.push(ParItem::Spacing(resolved));
ranges.push(range);
}
ParChild::Text(_, align, state, decos) => {
ParChild::Text(_, align, style, decos) => {
// TODO: Also split by language and script.
for (subrange, dir) in split_runs(&bidi, range) {
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));
ranges.push(subrange);
}

View File

@ -4,9 +4,9 @@ use std::ops::Range;
use rustybuzz::UnicodeBuffer;
use super::{Element, Frame, Glyph, LayoutContext, Text};
use crate::eval::FontState;
use crate::font::{Face, FaceId, FontVariant};
use crate::geom::{Dir, Em, Length, Point, Size};
use crate::style::TextStyle;
use crate::util::SliceExt;
/// Shape text into [`ShapedText`].
@ -14,7 +14,7 @@ pub fn shape<'a>(
ctx: &mut LayoutContext,
text: &'a str,
dir: Dir,
state: &'a FontState,
style: &'a TextStyle,
) -> ShapedText<'a> {
let mut glyphs = vec![];
if !text.is_empty() {
@ -24,19 +24,19 @@ pub fn shape<'a>(
0,
text,
dir,
state.size,
state.variant(),
state.families(),
style.size,
style.variant(),
style.families(),
None,
);
}
let (size, baseline) = measure(ctx, &glyphs, state);
let (size, baseline) = measure(ctx, &glyphs, style);
ShapedText {
text,
dir,
state,
style,
size,
baseline,
glyphs: Cow::Owned(glyphs),
@ -54,7 +54,7 @@ pub struct ShapedText<'a> {
/// The text direction.
pub dir: Dir,
/// The properties used for font selection.
pub state: &'a FontState,
pub style: &'a TextStyle,
/// The font size.
pub size: Size,
/// The baseline from the top of the frame.
@ -93,9 +93,9 @@ impl<'a> ShapedText<'a> {
let mut text = Text {
face_id,
size: self.state.size,
size: self.style.size,
width: Length::zero(),
fill: self.state.fill,
fill: self.style.fill,
glyphs: vec![],
};
@ -123,17 +123,17 @@ impl<'a> ShapedText<'a> {
text_range: Range<usize>,
) -> ShapedText<'a> {
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 {
text: &self.text[text_range],
dir: self.dir,
state: self.state,
style: self.style,
size,
baseline,
glyphs: Cow::Borrowed(glyphs),
}
} 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(
ctx: &mut LayoutContext,
glyphs: &[ShapedGlyph],
state: &FontState,
style: &TextStyle,
) -> (Size, Length) {
let mut width = Length::zero();
let mut top = Length::zero();
@ -342,15 +342,15 @@ fn measure(
// Expand top and bottom by reading the face's vertical metrics.
let mut expand = |face: &Face| {
top.set_max(face.vertical_metric(state.top_edge, state.size));
bottom.set_max(-face.vertical_metric(state.bottom_edge, state.size));
top.set_max(face.vertical_metric(style.top_edge, style.size));
bottom.set_max(-face.vertical_metric(style.bottom_edge, style.size));
};
if glyphs.is_empty() {
// When there are no glyphs, we just use the vertical metrics of the
// first available font.
for family in state.families() {
if let Some(face_id) = ctx.fonts.select(family, state.variant) {
for family in style.families() {
if let Some(face_id) = ctx.fonts.select(family, style.variant) {
expand(ctx.fonts.get(face_id));
break;
}
@ -361,7 +361,7 @@ fn measure(
expand(face);
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
//! [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
//! [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
//! self-contained and order-independent and thus much better suited for
//! layouting than the raw markup.
@ -18,7 +18,6 @@
//! - **Exporting:** The finished layout can be exported into a supported
//! format. Currently, the only supported output format is [PDF].
//!
//! [tokens]: parse::Tokens
//! [parsed]: parse::parse
//! [markup]: syntax::Markup
@ -40,16 +39,16 @@ pub mod image;
pub mod layout;
pub mod library;
pub mod loading;
pub mod paper;
pub mod parse;
pub mod source;
pub mod style;
pub mod syntax;
pub mod util;
use std::rc::Rc;
use crate::diag::TypResult;
use crate::eval::{Module, Scope, State};
use crate::eval::{Module, Scope};
use crate::font::FontStore;
use crate::image::ImageStore;
#[cfg(feature = "layout-cache")]
@ -57,6 +56,7 @@ use crate::layout::{EvictionPolicy, LayoutCache};
use crate::layout::{Frame, LayoutTree};
use crate::loading::Loader;
use crate::source::{SourceId, SourceStore};
use crate::style::Style;
use crate::syntax::Markup;
/// The core context which holds the loader, configuration and cached artifacts.
@ -74,8 +74,8 @@ pub struct Context {
pub layouts: LayoutCache,
/// The standard library scope.
std: Scope,
/// The default state.
state: State,
/// The default style.
style: Style,
}
impl Context {
@ -94,9 +94,9 @@ impl Context {
&self.std
}
/// A read-only reference to the state.
pub fn state(&self) -> &State {
&self.state
/// A read-only reference to the style.
pub fn style(&self) -> &Style {
&self.style
}
/// 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.
pub fn execute(&mut self, id: SourceId) -> TypResult<LayoutTree> {
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.
@ -139,7 +139,7 @@ impl Context {
/// This struct is created by [`Context::builder`].
pub struct ContextBuilder {
std: Option<Scope>,
state: Option<State>,
style: Option<Style>,
#[cfg(feature = "layout-cache")]
policy: EvictionPolicy,
#[cfg(feature = "layout-cache")]
@ -155,8 +155,8 @@ impl ContextBuilder {
}
/// The initial properties for page size, font selection and so on.
pub fn state(mut self, state: State) -> Self {
self.state = Some(state);
pub fn style(mut self, style: Style) -> Self {
self.style = Some(style);
self
}
@ -188,7 +188,7 @@ impl ContextBuilder {
#[cfg(feature = "layout-cache")]
layouts: LayoutCache::new(self.policy, self.max_size),
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 {
Self {
std: None,
state: None,
style: None,
#[cfg(feature = "layout-cache")]
policy: EvictionPolicy::default(),
#[cfg(feature = "layout-cache")]

View File

@ -61,12 +61,12 @@ fn rect_impl(
fill: Option<Color>,
body: Template,
) -> Value {
Value::Template(Template::from_inline(move |state| {
Value::Template(Template::from_inline(move |style| {
let mut node = LayoutNode::new(FixedNode {
width,
height,
aspect,
child: body.to_stack(state).into(),
child: body.to_stack(style).into(),
});
if let Some(fill) = fill {
@ -114,7 +114,7 @@ fn ellipse_impl(
fill: Option<Color>,
body: Template,
) -> 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
// perfectly into the ellipse.
const PAD: f64 = 0.5 - SQRT_2 / 4.0;
@ -125,7 +125,7 @@ fn ellipse_impl(
aspect,
child: LayoutNode::new(PadNode {
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 crate::layout::{FixedNode, GridNode, PadNode, StackChild, StackNode, TrackSizing};
use crate::paper::{Paper, PaperClass};
use crate::style::{Paper, PaperClass};
/// `page`: Configure pages.
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 flip = args.named("flip")?;
ctx.template.modify(move |state| {
let page = state.page_mut();
ctx.template.modify(move |style| {
let page = style.page_mut();
if let Some(paper) = paper {
page.class = paper.class();
@ -98,13 +98,13 @@ pub fn align(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
}
let realign = |template: &mut Template| {
template.modify(move |state| {
template.modify(move |style| {
if let Some(horizontal) = horizontal {
state.aligns.inline = horizontal;
style.aligns.inline = horizontal;
}
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 height = args.named("height")?;
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 {
width,
height,
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.
pub fn block(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
let body: Template = args.expect("body")?;
Ok(Value::Template(Template::from_block(move |state| {
body.to_stack(state)
Ok(Value::Template(Template::from_block(move |style| {
body.to_stack(style)
})))
}
@ -181,10 +181,10 @@ pub fn pad(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
bottom.or(all).unwrap_or_default(),
);
Ok(Value::Template(Template::from_block(move |state| {
Ok(Value::Template(Template::from_block(move |style| {
PadNode {
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 list: Vec<Child> = args.all().collect();
Ok(Value::Template(Template::from_block(move |state| {
let mut dirs = Gen::new(None, dir).unwrap_or(state.dirs);
Ok(Value::Template(Template::from_block(move |style| {
let mut dirs = Gen::new(style.dir, dir.unwrap_or(Dir::TTB));
// If the directions become aligned, fix up the inline direction since
// that's the one that is not user-defined.
if dirs.block.axis() == dirs.inline.axis() {
dirs.inline = state.dirs.block;
if dirs.inline.axis() == dirs.block.axis() {
dirs.inline = Dir::TTB;
}
// 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
// different from the state axes).
let mut aligns = state.aligns;
if dirs.block.axis() == state.dirs.inline.axis() {
// different from the style axes).
let mut aligns = style.aligns;
if dirs.inline.axis() != style.dir.axis() {
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));
}
let node = template.to_stack(state).into();
let node = template.to_stack(style).into();
children.push(StackChild::Any(node, aligns));
delayed = spacing;
}
@ -293,10 +293,12 @@ pub fn grid(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
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
// 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() {
let target = if column_dir.is_some() {
&mut dirs.block
@ -304,15 +306,15 @@ pub fn grid(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
&mut dirs.inline
};
*target = if target.axis() == state.dirs.inline.axis() {
state.dirs.block
*target = if target.axis() == style.dir.axis() {
Dir::TTB
} else {
state.dirs.inline
style.dir
};
}
let children =
children.iter().map(|child| child.to_stack(&state).into()).collect();
children.iter().map(|child| child.to_stack(&style).into()).collect();
GridNode {
dirs,

View File

@ -17,10 +17,11 @@ use std::convert::TryFrom;
use std::rc::Rc;
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::geom::*;
use crate::layout::LayoutNode;
use crate::style::Style;
use crate::syntax::{Span, Spanned};
/// 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 body = args.eat::<Template>();
let f = move |state: &mut State| {
let font = state.font_mut();
let f = move |style_: &mut Style| {
let text = style_.text_mut();
if let Some(size) = size {
font.size = size.resolve(font.size);
text.size = size.resolve(text.size);
}
if let Some(style) = style {
font.variant.style = style;
text.variant.style = style;
}
if let Some(weight) = weight {
font.variant.weight = weight;
text.variant.weight = weight;
}
if let Some(stretch) = stretch {
font.variant.stretch = stretch;
text.variant.stretch = stretch;
}
if let Some(top_edge) = top_edge {
font.top_edge = top_edge;
text.top_edge = top_edge;
}
if let Some(bottom_edge) = bottom_edge {
font.bottom_edge = bottom_edge;
text.bottom_edge = bottom_edge;
}
if let Some(fill) = fill {
font.fill = Paint::Color(fill);
text.fill = Paint::Color(fill);
}
if let Some(FontDef(list)) = &list {
font.families_mut().list = list.clone();
text.families_mut().list = list.clone();
}
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 {
font.families_mut().sans_serif = sans_serif.clone();
text.families_mut().sans_serif = sans_serif.clone();
}
if let Some(FamilyDef(monospace)) = &monospace {
font.families_mut().monospace = monospace.clone();
text.families_mut().monospace = monospace.clone();
}
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 line_spacing = args.named("leading")?;
ctx.template.modify(move |state| {
let par = state.par_mut();
ctx.template.modify(move |style| {
let par = style.par_mut();
if let Some(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 {
ctx.template.modify(move |state| state.dirs.inline = dir);
ctx.template.modify(move |style| style.dir = dir);
}
ctx.template.parbreak();

View File

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

View File

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

View File

@ -10,7 +10,7 @@ use ttf_parser::{GlyphId, OutlineBuilder};
use walkdir::WalkDir;
use typst::diag::Error;
use typst::eval::{State, Value};
use typst::eval::Value;
use typst::font::Face;
use typst::geom::{
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::parse::Scanner;
use typst::source::SourceFile;
use typst::style::Style;
use typst::syntax::{Pos, Span};
use typst::Context;
@ -62,10 +63,10 @@ fn main() {
// We want to have "unbounded" pages, so we allow them to be infinitely
// large and fit them to match their content.
let mut state = State::default();
state.page_mut().size = Size::new(Length::pt(120.0), Length::inf());
state.page_mut().margins = Sides::splat(Some(Length::pt(10.0).into()));
state.font_mut().size = Length::pt(10.0);
let mut style = Style::default();
style.page_mut().size = Size::new(Length::pt(120.0), Length::inf());
style.page_mut().margins = Sides::splat(Some(Length::pt(10.0).into()));
style.text_mut().size = Length::pt(10.0);
// Hook up an assert function into the global scope.
let mut std = typst::library::new();
@ -83,7 +84,7 @@ fn main() {
// Create loader and context.
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.
let mut ok = true;