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) {
|
||||
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) {
|
||||
|
@ -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::*;
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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()),
|
||||
],
|
||||
}
|
||||
});
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
30
src/lib.rs
30
src/lib.rs
@ -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")]
|
||||
|
@ -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(),
|
||||
}),
|
||||
});
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
|
@ -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]),
|
@ -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,
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user