mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Rename State
to Style
and move it into its own module
This commit is contained in:
parent
d4cc8c775d
commit
9ac125dea8
@ -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) {
|
||||||
|
@ -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::*;
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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()),
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -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() {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
30
src/lib.rs
30
src/lib.rs
@ -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")]
|
||||||
|
@ -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(),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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.
|
||||||
|
@ -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();
|
||||||
|
@ -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]),
|
@ -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> {
|
||||||
_ => None,
|
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) => {
|
(@$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,
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user