mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Move font family and refactor alignment
This commit is contained in:
parent
853361338b
commit
7d15dc634b
@ -1,8 +1,9 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::color::{Color, RgbaColor};
|
use crate::color::{Color, RgbaColor};
|
||||||
use crate::font::{FontStretch, FontStyle, FontVariant, FontWeight, VerticalFontMetric};
|
use crate::font::{
|
||||||
|
FontFamily, FontStretch, FontStyle, FontVariant, FontWeight, VerticalFontMetric,
|
||||||
|
};
|
||||||
use crate::geom::*;
|
use crate::geom::*;
|
||||||
use crate::layout::Paint;
|
use crate::layout::Paint;
|
||||||
use crate::paper::{PaperClass, PAPER_A4};
|
use crate::paper::{PaperClass, PAPER_A4};
|
||||||
@ -236,26 +237,6 @@ impl Default for FamilyList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A generic or named font family.
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
|
||||||
pub enum FontFamily {
|
|
||||||
Serif,
|
|
||||||
SansSerif,
|
|
||||||
Monospace,
|
|
||||||
Named(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for FontFamily {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
f.pad(match self {
|
|
||||||
Self::Serif => "serif",
|
|
||||||
Self::SansSerif => "sans-serif",
|
|
||||||
Self::Monospace => "monospace",
|
|
||||||
Self::Named(s) => s,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Describes a line that is positioned over, under or on top of text.
|
/// Describes a line that is positioned over, under or on top of text.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct LineState {
|
pub struct LineState {
|
||||||
|
20
src/font.rs
20
src/font.rs
@ -334,6 +334,26 @@ impl FaceId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A generic or named font family.
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
|
pub enum FontFamily {
|
||||||
|
Serif,
|
||||||
|
SansSerif,
|
||||||
|
Monospace,
|
||||||
|
Named(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for FontFamily {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
|
f.pad(match self {
|
||||||
|
Self::Serif => "serif",
|
||||||
|
Self::SansSerif => "sans-serif",
|
||||||
|
Self::Monospace => "monospace",
|
||||||
|
Self::Named(s) => s,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Properties of a single font face.
|
/// Properties of a single font face.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct FaceInfo {
|
pub struct FaceInfo {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// Where to align something along a directed axis.
|
/// Where to align something along an axis.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
pub enum Align {
|
pub enum Align {
|
||||||
/// Align at the start of the axis.
|
/// Align at the start of the axis.
|
||||||
@ -9,15 +9,27 @@ pub enum Align {
|
|||||||
Center,
|
Center,
|
||||||
/// Align at the end of the axis.
|
/// Align at the end of the axis.
|
||||||
End,
|
End,
|
||||||
|
/// Align at the left side of the axis.
|
||||||
|
Left,
|
||||||
|
/// Align at the right side of the axis.
|
||||||
|
Right,
|
||||||
|
/// Align at the top side of the axis.
|
||||||
|
Top,
|
||||||
|
/// Align at the bottom side of the axis.
|
||||||
|
Bottom,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Align {
|
impl Align {
|
||||||
/// Returns the position of this alignment in the given range.
|
/// The axis this alignment belongs to if it is specific.
|
||||||
pub fn resolve(self, dir: Dir, range: Range<Length>) -> Length {
|
pub fn axis(self) -> Option<SpecAxis> {
|
||||||
match if dir.is_positive() { self } else { self.inv() } {
|
match self {
|
||||||
Self::Start => range.start,
|
Self::Start => None,
|
||||||
Self::Center => (range.start + range.end) / 2.0,
|
Self::Center => None,
|
||||||
Self::End => range.end,
|
Self::End => None,
|
||||||
|
Self::Left => Some(SpecAxis::Horizontal),
|
||||||
|
Self::Right => Some(SpecAxis::Horizontal),
|
||||||
|
Self::Top => Some(SpecAxis::Vertical),
|
||||||
|
Self::Bottom => Some(SpecAxis::Vertical),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,6 +39,38 @@ impl Align {
|
|||||||
Self::Start => Self::End,
|
Self::Start => Self::End,
|
||||||
Self::Center => Self::Center,
|
Self::Center => Self::Center,
|
||||||
Self::End => Self::Start,
|
Self::End => Self::Start,
|
||||||
|
Self::Left => Self::Right,
|
||||||
|
Self::Right => Self::Left,
|
||||||
|
Self::Top => Self::Bottom,
|
||||||
|
Self::Bottom => Self::Top,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the position of this alignment in the given range.
|
||||||
|
pub fn resolve(self, dir: Dir, range: Range<Length>) -> Length {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
if let Some(axis) = self.axis() {
|
||||||
|
debug_assert_eq!(axis, dir.axis())
|
||||||
|
}
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Self::Start => {
|
||||||
|
if dir.is_positive() {
|
||||||
|
range.start
|
||||||
|
} else {
|
||||||
|
range.end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Center => (range.start + range.end) / 2.0,
|
||||||
|
Self::End => {
|
||||||
|
if dir.is_positive() {
|
||||||
|
range.end
|
||||||
|
} else {
|
||||||
|
range.start
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Left | Self::Top => range.start,
|
||||||
|
Self::Right | Self::Bottom => range.end,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -43,6 +87,10 @@ impl Display for Align {
|
|||||||
Self::Start => "start",
|
Self::Start => "start",
|
||||||
Self::Center => "center",
|
Self::Center => "center",
|
||||||
Self::End => "end",
|
Self::End => "end",
|
||||||
|
Self::Left => "left",
|
||||||
|
Self::Right => "right",
|
||||||
|
Self::Top => "top",
|
||||||
|
Self::Bottom => "bottom",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,8 +86,8 @@ pub fn ellipse(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
|||||||
|
|
||||||
/// `circle`: A circle with optional content.
|
/// `circle`: A circle with optional content.
|
||||||
pub fn circle(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
pub fn circle(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
||||||
let radius = args.named::<Length>(ctx, "radius").map(|r| 2.0 * Linear::from(r));
|
let diameter = args.named::<Length>(ctx, "radius").map(|r| 2.0 * Linear::from(r));
|
||||||
let width = radius.or_else(|| args.named(ctx, "width"));
|
let width = diameter.or_else(|| args.named(ctx, "width"));
|
||||||
let height = width.is_none().then(|| args.named(ctx, "height")).flatten();
|
let height = width.is_none().then(|| args.named(ctx, "height")).flatten();
|
||||||
let fill = args.named(ctx, "fill");
|
let fill = args.named(ctx, "fill");
|
||||||
let body = args.eat().unwrap_or_default();
|
let body = args.eat().unwrap_or_default();
|
||||||
|
@ -104,10 +104,10 @@ fn spacing_impl(ctx: &mut EvalContext, args: &mut FuncArgs, axis: GenAxis) -> Va
|
|||||||
|
|
||||||
/// `align`: Configure the alignment along the layouting axes.
|
/// `align`: Configure the alignment along the layouting axes.
|
||||||
pub fn align(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
pub fn align(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
||||||
let first = args.eat::<AlignValue>();
|
let first = args.eat::<Align>();
|
||||||
let second = args.eat::<AlignValue>();
|
let second = args.eat::<Align>();
|
||||||
let mut horizontal = args.named::<AlignValue>(ctx, "horizontal");
|
let mut horizontal = args.named(ctx, "horizontal");
|
||||||
let mut vertical = args.named::<AlignValue>(ctx, "vertical");
|
let mut vertical = args.named(ctx, "vertical");
|
||||||
let body = args.expect::<Template>(ctx, "body").unwrap_or_default();
|
let body = args.expect::<Template>(ctx, "body").unwrap_or_default();
|
||||||
|
|
||||||
for value in first.into_iter().chain(second) {
|
for value in first.into_iter().chain(second) {
|
||||||
@ -124,85 +124,18 @@ pub fn align(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
|||||||
|
|
||||||
Value::template(move |ctx| {
|
Value::template(move |ctx| {
|
||||||
if let Some(horizontal) = horizontal {
|
if let Some(horizontal) = horizontal {
|
||||||
ctx.state.aligns.cross = horizontal.to_align(ctx.state.dir);
|
ctx.state.aligns.cross = horizontal;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(vertical) = vertical {
|
if let Some(vertical) = vertical {
|
||||||
let new = vertical.to_align(Dir::TTB);
|
ctx.state.aligns.main = vertical;
|
||||||
if ctx.state.aligns.main != new {
|
ctx.parbreak();
|
||||||
ctx.state.aligns.main = new;
|
|
||||||
ctx.parbreak();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body.exec(ctx);
|
body.exec(ctx);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An alignment specifier passed to `align`.
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
|
||||||
pub(super) enum AlignValue {
|
|
||||||
Start,
|
|
||||||
Center,
|
|
||||||
End,
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
Top,
|
|
||||||
Bottom,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AlignValue {
|
|
||||||
fn axis(self) -> Option<SpecAxis> {
|
|
||||||
match self {
|
|
||||||
Self::Start => None,
|
|
||||||
Self::Center => None,
|
|
||||||
Self::End => None,
|
|
||||||
Self::Left => Some(SpecAxis::Horizontal),
|
|
||||||
Self::Right => Some(SpecAxis::Horizontal),
|
|
||||||
Self::Top => Some(SpecAxis::Vertical),
|
|
||||||
Self::Bottom => Some(SpecAxis::Vertical),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_align(self, dir: Dir) -> Align {
|
|
||||||
let side = |is_at_positive_start| {
|
|
||||||
if dir.is_positive() == is_at_positive_start {
|
|
||||||
Align::Start
|
|
||||||
} else {
|
|
||||||
Align::End
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match self {
|
|
||||||
Self::Start => Align::Start,
|
|
||||||
Self::Center => Align::Center,
|
|
||||||
Self::End => Align::End,
|
|
||||||
Self::Left => side(true),
|
|
||||||
Self::Right => side(false),
|
|
||||||
Self::Top => side(true),
|
|
||||||
Self::Bottom => side(false),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for AlignValue {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
f.pad(match self {
|
|
||||||
Self::Start => "start",
|
|
||||||
Self::Center => "center",
|
|
||||||
Self::End => "end",
|
|
||||||
Self::Left => "left",
|
|
||||||
Self::Right => "right",
|
|
||||||
Self::Top => "top",
|
|
||||||
Self::Bottom => "bottom",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dynamic! {
|
|
||||||
AlignValue: "alignment",
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `box`: Place content in a rectangular box.
|
/// `box`: Place content in a rectangular box.
|
||||||
pub fn boxed(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
pub fn boxed(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
||||||
let width = args.named(ctx, "width");
|
let width = args.named(ctx, "width");
|
||||||
@ -247,7 +180,7 @@ pub fn pad(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
|||||||
|
|
||||||
/// `stack`: Stack children along an axis.
|
/// `stack`: Stack children along an axis.
|
||||||
pub fn stack(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
pub fn stack(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
||||||
let dir = args.named::<Dir>(ctx, "dir").unwrap_or(Dir::TTB);
|
let dir = args.named(ctx, "dir").unwrap_or(Dir::TTB);
|
||||||
let children: Vec<_> = args.all().collect();
|
let children: Vec<_> = args.all().collect();
|
||||||
|
|
||||||
Value::template(move |ctx| {
|
Value::template(move |ctx| {
|
||||||
@ -272,10 +205,10 @@ pub fn grid(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
|||||||
let columns = args.named::<Tracks>(ctx, "columns").unwrap_or_default();
|
let columns = args.named::<Tracks>(ctx, "columns").unwrap_or_default();
|
||||||
let rows = args.named::<Tracks>(ctx, "rows").unwrap_or_default();
|
let rows = args.named::<Tracks>(ctx, "rows").unwrap_or_default();
|
||||||
|
|
||||||
let gutter_columns = args.named::<Tracks>(ctx, "gutter-columns");
|
let gutter_columns = args.named(ctx, "gutter-columns");
|
||||||
let gutter_rows = args.named::<Tracks>(ctx, "gutter-rows");
|
let gutter_rows = args.named(ctx, "gutter-rows");
|
||||||
let gutter = args
|
let gutter = args
|
||||||
.named::<Linear>(ctx, "gutter")
|
.named(ctx, "gutter")
|
||||||
.map(|v| vec![TrackSizing::Linear(v)])
|
.map(|v| vec![TrackSizing::Linear(v)])
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
@ -13,13 +13,13 @@ pub use layout::*;
|
|||||||
pub use text::*;
|
pub use text::*;
|
||||||
pub use utility::*;
|
pub use utility::*;
|
||||||
|
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::convert::TryFrom;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::color::{Color, RgbaColor};
|
use crate::color::{Color, RgbaColor};
|
||||||
use crate::eval::{EvalContext, FuncArgs, Scope, Template, Type, Value};
|
use crate::eval::{EvalContext, FuncArgs, Scope, Template, Type, Value};
|
||||||
use crate::exec::{Exec, FontFamily};
|
use crate::exec::Exec;
|
||||||
use crate::font::{FontStyle, FontWeight, VerticalFontMetric};
|
use crate::font::{FontFamily, FontStretch, FontStyle, FontWeight, VerticalFontMetric};
|
||||||
use crate::geom::*;
|
use crate::geom::*;
|
||||||
use crate::syntax::Spanned;
|
use crate::syntax::Spanned;
|
||||||
use crate::util::EcoString;
|
use crate::util::EcoString;
|
||||||
@ -71,17 +71,17 @@ pub fn new() -> Scope {
|
|||||||
std.def_const("forest", RgbaColor::new(0x43, 0xA1, 0x27, 0xFF));
|
std.def_const("forest", RgbaColor::new(0x43, 0xA1, 0x27, 0xFF));
|
||||||
|
|
||||||
// Arbitrary constants.
|
// Arbitrary constants.
|
||||||
std.def_const("start", AlignValue::Start);
|
|
||||||
std.def_const("center", AlignValue::Center);
|
|
||||||
std.def_const("end", AlignValue::End);
|
|
||||||
std.def_const("left", AlignValue::Left);
|
|
||||||
std.def_const("right", AlignValue::Right);
|
|
||||||
std.def_const("top", AlignValue::Top);
|
|
||||||
std.def_const("bottom", AlignValue::Bottom);
|
|
||||||
std.def_const("ltr", Dir::LTR);
|
std.def_const("ltr", Dir::LTR);
|
||||||
std.def_const("rtl", Dir::RTL);
|
std.def_const("rtl", Dir::RTL);
|
||||||
std.def_const("ttb", Dir::TTB);
|
std.def_const("ttb", Dir::TTB);
|
||||||
std.def_const("btt", Dir::BTT);
|
std.def_const("btt", Dir::BTT);
|
||||||
|
std.def_const("start", Align::Start);
|
||||||
|
std.def_const("center", Align::Center);
|
||||||
|
std.def_const("end", Align::End);
|
||||||
|
std.def_const("left", Align::Left);
|
||||||
|
std.def_const("right", Align::Right);
|
||||||
|
std.def_const("top", Align::Top);
|
||||||
|
std.def_const("bottom", Align::Bottom);
|
||||||
std.def_const("serif", FontFamily::Serif);
|
std.def_const("serif", FontFamily::Serif);
|
||||||
std.def_const("sans-serif", FontFamily::SansSerif);
|
std.def_const("sans-serif", FontFamily::SansSerif);
|
||||||
std.def_const("monospace", FontFamily::Monospace);
|
std.def_const("monospace", FontFamily::Monospace);
|
||||||
@ -102,3 +102,32 @@ pub fn new() -> Scope {
|
|||||||
dynamic! {
|
dynamic! {
|
||||||
Dir: "direction",
|
Dir: "direction",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dynamic! {
|
||||||
|
Align: "alignment",
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamic! {
|
||||||
|
FontFamily: "font family",
|
||||||
|
Value::Str(string) => Self::Named(string.to_lowercase()),
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamic! {
|
||||||
|
FontStyle: "font style",
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamic! {
|
||||||
|
FontWeight: "font weight",
|
||||||
|
Value::Int(number) => {
|
||||||
|
u16::try_from(number).map_or(Self::BLACK, Self::from_number)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamic! {
|
||||||
|
FontStretch: "font stretch",
|
||||||
|
Value::Relative(relative) => Self::from_ratio(relative.get() as f32),
|
||||||
|
}
|
||||||
|
|
||||||
|
dynamic! {
|
||||||
|
VerticalFontMetric: "vertical font metric",
|
||||||
|
}
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
use std::convert::TryFrom;
|
|
||||||
|
|
||||||
use crate::exec::{LineState, TextState};
|
use crate::exec::{LineState, TextState};
|
||||||
use crate::font::{FontStretch, FontStyle, FontWeight};
|
|
||||||
use crate::layout::Paint;
|
use crate::layout::Paint;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -15,7 +12,7 @@ pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
|||||||
Some(FontDef(families))
|
Some(FontDef(families))
|
||||||
};
|
};
|
||||||
|
|
||||||
let size = args.eat().or_else(|| args.named::<Linear>(ctx, "size"));
|
let size = args.eat::<Linear>().or_else(|| args.named(ctx, "size"));
|
||||||
let style = args.named(ctx, "style");
|
let style = args.named(ctx, "style");
|
||||||
let weight = args.named(ctx, "weight");
|
let weight = args.named(ctx, "weight");
|
||||||
let stretch = args.named(ctx, "stretch");
|
let stretch = args.named(ctx, "stretch");
|
||||||
@ -104,31 +101,6 @@ castable! {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
dynamic! {
|
|
||||||
FontFamily: "font family",
|
|
||||||
Value::Str(string) => Self::Named(string.to_lowercase()),
|
|
||||||
}
|
|
||||||
|
|
||||||
dynamic! {
|
|
||||||
FontStyle: "font style",
|
|
||||||
}
|
|
||||||
|
|
||||||
dynamic! {
|
|
||||||
FontWeight: "font weight",
|
|
||||||
Value::Int(number) => {
|
|
||||||
u16::try_from(number).map_or(Self::BLACK, Self::from_number)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
dynamic! {
|
|
||||||
FontStretch: "font stretch",
|
|
||||||
Value::Relative(relative) => Self::from_ratio(relative.get() as f32),
|
|
||||||
}
|
|
||||||
|
|
||||||
dynamic! {
|
|
||||||
VerticalFontMetric: "vertical font metric",
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `par`: Configure paragraphs.
|
/// `par`: Configure paragraphs.
|
||||||
pub fn par(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
pub fn par(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
||||||
let par_spacing = args.named(ctx, "spacing");
|
let par_spacing = args.named(ctx, "spacing");
|
||||||
@ -209,7 +181,7 @@ fn line_impl(
|
|||||||
substate: fn(&mut TextState) -> &mut Option<Rc<LineState>>,
|
substate: fn(&mut TextState) -> &mut Option<Rc<LineState>>,
|
||||||
) -> Value {
|
) -> Value {
|
||||||
let stroke = args.eat().or_else(|| args.named(ctx, "stroke"));
|
let stroke = args.eat().or_else(|| args.named(ctx, "stroke"));
|
||||||
let thickness = args.eat().or_else(|| args.named::<Linear>(ctx, "thickness"));
|
let thickness = args.eat::<Linear>().or_else(|| args.named(ctx, "thickness"));
|
||||||
let offset = args.named(ctx, "offset");
|
let offset = args.named(ctx, "offset");
|
||||||
let extent = args.named(ctx, "extent").unwrap_or_default();
|
let extent = args.named(ctx, "extent").unwrap_or_default();
|
||||||
let body = args.expect::<Template>(ctx, "body").unwrap_or_default();
|
let body = args.expect::<Template>(ctx, "body").unwrap_or_default();
|
||||||
|
@ -24,7 +24,7 @@ pub fn repr(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
|||||||
|
|
||||||
/// `len`: The length of a string, an array or a dictionary.
|
/// `len`: The length of a string, an array or a dictionary.
|
||||||
pub fn len(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
pub fn len(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
||||||
match args.expect::<Spanned<Value>>(ctx, "collection") {
|
match args.expect(ctx, "collection") {
|
||||||
Some(Spanned { v: Value::Str(v), .. }) => Value::Int(v.len() as i64),
|
Some(Spanned { v: Value::Str(v), .. }) => Value::Int(v.len() as i64),
|
||||||
Some(Spanned { v: Value::Array(v), .. }) => Value::Int(v.len() as i64),
|
Some(Spanned { v: Value::Array(v), .. }) => Value::Int(v.len() as i64),
|
||||||
Some(Spanned { v: Value::Dict(v), .. }) => Value::Int(v.len() as i64),
|
Some(Spanned { v: Value::Dict(v), .. }) => Value::Int(v.len() as i64),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user