diff --git a/src/eval/value.rs b/src/eval/value.rs index b29d01f32..dda1c3e1e 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -662,7 +662,7 @@ impl From for Value { } } -/// Mark a type as a [`Value`]. +/// Make a type castable from a value. /// /// Given a type `T`, this implements the following traits: /// - [`Type`] for `T`, @@ -684,7 +684,7 @@ impl From for Value { /// This would allow the type `FontFamily` to be cast from: /// - a [`Value::Any`] variant already containing a `FontFamily`, /// - a string, producing a named font family. -macro_rules! value { +macro_rules! castable { ($type:ty: $type_name:literal $(, $pattern:pat => $out:expr)* diff --git a/src/exec/state.rs b/src/exec/state.rs index fa16004ed..6f900b546 100644 --- a/src/exec/state.rs +++ b/src/exec/state.rs @@ -216,9 +216,9 @@ impl Default for FamilyList { Self { list: vec![FontFamily::Serif], serif: vec!["eb garamond".into()], - sans_serif: vec![/* TODO */], + sans_serif: vec!["pt sans".into()], monospace: vec!["inconsolata".into()], - base: vec!["twitter color emoji".into()], + base: vec!["twitter color emoji".into(), "latin modern math".into()], } } } diff --git a/src/library/align.rs b/src/library/align.rs index 507bc939e..c0ed0416a 100644 --- a/src/library/align.rs +++ b/src/library/align.rs @@ -123,6 +123,6 @@ impl Display for AlignValue { } } -value! { +castable! { AlignValue: "alignment", } diff --git a/src/library/font.rs b/src/library/font.rs index 8d76328f2..3f816c6dd 100644 --- a/src/library/font.rs +++ b/src/library/font.rs @@ -6,20 +6,20 @@ use super::*; /// `font`: Configure the font. /// /// # Positional parameters -/// - Font size: optional, of type `linear` relative to current font size. -/// - Font families: variadic, of type `font-family`. /// - Body: optional, of type `template`. /// /// # Named parameters +/// - Font size: `size`, of type `linear` relative to current font size. +/// - Font families: `family`, `font-family`, `string` or `array`. /// - Font Style: `style`, of type `font-style`. /// - Font Weight: `weight`, of type `font-weight`. /// - Font Stretch: `stretch`, of type `relative`, between 0.5 and 2.0. /// - Top edge of the font: `top-edge`, of type `vertical-font-metric`. /// - Bottom edge of the font: `bottom-edge`, of type `vertical-font-metric`. /// - Color the glyphs: `color`, of type `color`. -/// - Serif family definition: `serif`, of type `font-family-definition`. -/// - Sans-serif family definition: `sans-serif`, of type `font-family-definition`. -/// - Monospace family definition: `monospace`, of type `font-family-definition`. +/// - Serif family definition: `serif`, of type `family-def`. +/// - Sans-serif family definition: `sans-serif`, of type `family-def`. +/// - Monospace family definition: `monospace`, of type `family-def`. /// /// # Return value /// A template that configures font properties. The effect is scoped to the body @@ -31,9 +31,9 @@ use super::*; /// - `sans-serif` /// - `monospace` /// - coerces from `string` -/// - Type `font-family-definition` +/// - Type `family-def` /// - coerces from `string` -/// - coerces from `array` +/// - coerces from `array` of `string` /// - Type `font-style` /// - `normal` /// - `italic` @@ -49,8 +49,8 @@ use super::*; /// - `baseline` /// - `descender` pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { - let size = args.eat::(ctx); - let list = args.all::(ctx); + let list = args.named(ctx, "family"); + let size = args.named::(ctx, "size"); let style = args.named(ctx, "style"); let weight = args.named(ctx, "weight"); let stretch = args.named(ctx, "stretch"); @@ -70,7 +70,7 @@ pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { font.size = linear.resolve(font.size); } - if !list.is_empty() { + if let Some(FontDef(list)) = &list { font.families_mut().list = list.clone(); } @@ -98,15 +98,15 @@ pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { font.fill = Fill::Color(color); } - if let Some(FontFamilies(serif)) = &serif { + if let Some(FamilyDef(serif)) = &serif { font.families_mut().serif = serif.clone(); } - if let Some(FontFamilies(sans_serif)) = &sans_serif { + if let Some(FamilyDef(sans_serif)) = &sans_serif { font.families_mut().sans_serif = sans_serif.clone(); } - if let Some(FontFamilies(monospace)) = &monospace { + if let Some(FamilyDef(monospace)) = &monospace { font.families_mut().monospace = monospace.clone(); } @@ -117,12 +117,25 @@ pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { }) } -/// A list of font family names. -#[derive(Debug, Clone, PartialEq)] -struct FontFamilies(Vec); +#[derive(Debug)] +struct FontDef(Vec); -value! { - FontFamilies: "string or array of strings", +castable! { + FontDef: "font family or array of font families", + Value::Str(string) => Self(vec![FontFamily::Named(string.to_lowercase())]), + Value::Array(values) => Self(values + .into_iter() + .filter_map(|v| v.cast().ok()) + .collect() + ), + #(family: FontFamily) => Self(vec![family]), +} + +#[derive(Debug)] +struct FamilyDef(Vec); + +castable! { + FamilyDef: "string or array of strings", Value::Str(string) => Self(vec![string.to_lowercase()]), Value::Array(values) => Self(values .into_iter() @@ -132,16 +145,16 @@ value! { ), } -value! { +castable! { FontFamily: "font family", Value::Str(string) => Self::Named(string.to_lowercase()) } -value! { +castable! { FontStyle: "font style", } -value! { +castable! { FontWeight: "font weight", Value::Int(number) => { let [min, max] = [Self::THIN, Self::BLACK]; @@ -161,7 +174,7 @@ value! { }, } -value! { +castable! { FontStretch: "font stretch", Value::Relative(relative) => { let [min, max] = [Self::ULTRA_CONDENSED, Self::ULTRA_EXPANDED]; @@ -182,6 +195,6 @@ value! { }, } -value! { +castable! { VerticalFontMetric: "vertical font metric", } diff --git a/src/library/grid.rs b/src/library/grid.rs index 9efa687ca..c2e765141 100644 --- a/src/library/grid.rs +++ b/src/library/grid.rs @@ -70,7 +70,7 @@ pub fn grid(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { /// Defines size of rows and columns in a grid. type Tracks = Vec; -value! { +castable! { Tracks: "array of `auto`s, linears, and fractionals", Value::Int(count) => vec![TrackSizing::Auto; count.max(0) as usize], Value::Array(values) => values @@ -79,7 +79,7 @@ value! { .collect(), } -value! { +castable! { TrackSizing: "`auto`, linear, or fractional", Value::Auto => TrackSizing::Auto, Value::Length(v) => TrackSizing::Linear(v.into()), diff --git a/src/library/mod.rs b/src/library/mod.rs index 6c7360813..4911e5c86 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -110,6 +110,6 @@ pub fn new() -> Scope { std } -value! { +castable! { Dir: "direction" } diff --git a/tests/typ/code/call.typ b/tests/typ/code/call.typ index 76953580f..5351eb290 100644 --- a/tests/typ/code/call.typ +++ b/tests/typ/code/call.typ @@ -24,7 +24,7 @@ #test(alias(alias), "function") // Library function `font` returns template. -#test(type(font(12pt)), "template") +#test(type(font(size: 12pt)), "template") --- // Callee expressions. diff --git a/tests/typ/text/bidi.typ b/tests/typ/text/bidi.typ index 078cb1f98..2e7236ca0 100644 --- a/tests/typ/text/bidi.typ +++ b/tests/typ/text/bidi.typ @@ -3,7 +3,7 @@ --- // Test reordering with different top-level paragraph directions. #let text = [Text טֶקסט] -#font("EB Garamond", "Noto Serif Hebrew") +#font(family: ("EB Garamond", "Noto Serif Hebrew")) #lang("he") {text} #lang("de") {text} @@ -11,7 +11,7 @@ // Test that consecutive, embedded LTR runs stay LTR. // Here, we have two runs: "A" and italic "B". #let text = [أنت A_B_مطرC] -#font("EB Garamond", "Noto Sans Arabic") +#font(family: ("EB Garamond", "Noto Sans Arabic")) #lang("ar") {text} #lang("de") {text} @@ -19,32 +19,32 @@ // Test that consecutive, embedded RTL runs stay RTL. // Here, we have three runs: "גֶ", bold "שֶׁ", and "ם". #let text = [Aגֶ*שֶׁ*םB] -#font("EB Garamond", "Noto Serif Hebrew") +#font(family: ("EB Garamond", "Noto Serif Hebrew")) #lang("he") {text} #lang("de") {text} --- // Test embedding up to level 4 with isolates. -#font("EB Garamond", "Noto Serif Hebrew", "Twitter Color Emoji") +#font(family: ("EB Garamond", "Noto Serif Hebrew", "Twitter Color Emoji")) #lang(dir: rtl) א\u{2066}A\u{2067}Bב\u{2069}? --- // Test hard line break (leads to two paragraphs in unicode-bidi). -#font("Noto Sans Arabic", "EB Garamond") +#font(family: ("Noto Sans Arabic", "EB Garamond")) #lang("ar") Life المطر هو الحياة \ الحياة تمطر is rain. --- // Test spacing. -#font("EB Garamond", "Noto Serif Hebrew") +#font(family: ("EB Garamond", "Noto Serif Hebrew")) L #h(1cm) ריווחR \ Lריווח #h(1cm) R --- // Test inline object. -#font("Noto Serif Hebrew", "EB Garamond") +#font(family: ("Noto Serif Hebrew", "EB Garamond")) #lang("he") קרנפיםRh#image("../../res/rhino.png", height: 11pt)inoחיים diff --git a/tests/typ/text/chinese.typ b/tests/typ/text/chinese.typ index 0800a2209..3fe790eac 100644 --- a/tests/typ/text/chinese.typ +++ b/tests/typ/text/chinese.typ @@ -1,7 +1,7 @@ // Test chinese text from Wikipedia. --- -#font("Noto Serif CJK SC") +#font(family: "Noto Serif CJK SC") 是美国广播公司电视剧《迷失》第3季的第22和23集,也是全剧的第71集和72集 由执行制作人戴蒙·林道夫和卡尔顿·库斯编剧,导演则是另一名执行制作人杰克·本德 diff --git a/tests/typ/text/font.typ b/tests/typ/text/font.typ index 317037e2c..f3a9495bd 100644 --- a/tests/typ/text/font.typ +++ b/tests/typ/text/font.typ @@ -2,9 +2,9 @@ --- // Set same font size in three different ways. -#font(22pt)[A] -#font(200%)[A] -#font(16.5pt + 50%)[A] +#font(size: 22pt)[A] +#font(size: 200%)[A] +#font(size: 16.5pt + 50%)[A] // Do nothing. #font[Normal] @@ -19,13 +19,13 @@ #font(stretch: 50%)[Condensed] // Set family. -#font("PT Sans")[Sans serif] +#font(family: "PT Sans")[Sans serif] // Emoji. Emoji: 🐪, 🌋, 🏞 // Math. -#font("Latin Modern Math")[ +#font(family: "Latin Modern Math")[ ∫ 𝛼 + 3𝛽 d𝑡 ] @@ -49,9 +49,9 @@ Emoji: 🐪, 🌋, 🏞 --- // Test class definitions. #font(sans-serif: "PT Sans") -#font(sans-serif)[Sans-serif.] \ -#font(monospace)[Monospace.] \ -#font(monospace, monospace: ("Nope", "Latin Modern Math"))[Math.] +#font(family: sans-serif)[Sans-serif.] \ +#font(family: monospace)[Monospace.] \ +#font(family: monospace, monospace: ("Nope", "Latin Modern Math"))[Math.] --- // Ref: false diff --git a/tests/typ/text/shaping.typ b/tests/typ/text/shaping.typ index ba543e715..9c6f2966e 100644 --- a/tests/typ/text/shaping.typ +++ b/tests/typ/text/shaping.typ @@ -7,11 +7,11 @@ Le fira // This should just shape nicely. -#font("Noto Sans Arabic") +#font(family: "Noto Sans Arabic") دع النص يمطر عليك // This should form a three-member family. -#font("Twitter Color Emoji") +#font(family: "Twitter Color Emoji") 👩‍👩‍👦 🤚🏿 // These two shouldn't be affected by a zero-width joiner. @@ -20,7 +20,7 @@ Le fira --- // Test font fallback. -#font("EB Garamond", "Noto Sans Arabic", "Twitter Color Emoji") +#font(family: ("EB Garamond", "Noto Sans Arabic", "Twitter Color Emoji")) // Font fallback for emoji. A😀B @@ -40,6 +40,6 @@ A🐈中文B --- // Test reshaping. -#font("Noto Serif Hebrew") +#font(family: "Noto Serif Hebrew") #lang("he") ס \ טֶ diff --git a/tests/typ/text/whitespace.typ b/tests/typ/text/whitespace.typ index 418c3a129..29c3e9906 100644 --- a/tests/typ/text/whitespace.typ +++ b/tests/typ/text/whitespace.typ @@ -36,11 +36,11 @@ A #for _ in (none,) {"B"}C --- // Test that a run consisting only of whitespace isn't trimmed. -A#font("PT Sans")[ ]B +A#font(family: "PT Sans")[ ]B --- // Test font change after space. -Left #font("PT Sans")[Right]. +Left #font(family: "PT Sans")[Right]. --- // Test that space at start of line is not trimmed.