From e59de77f96d43fa675c5c733ccf39ec2c22e949e Mon Sep 17 00:00:00 2001 From: Laurenz Date: Mon, 21 Dec 2020 00:40:09 +0100 Subject: [PATCH] =?UTF-8?q?Allow=20only=20a=20few=20predefined=20font=20cl?= =?UTF-8?q?asses=20in=20[font]=20=F0=9F=9A=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/eval/state.rs | 1 + src/library/insert.rs | 2 +- src/library/layout.rs | 54 +++++++++----- src/library/mod.rs | 2 +- src/library/style.rs | 122 ++++++++++++++++++------------- tests/typ/func-font-error.typ | 6 ++ tests/typ/func-font-fallback.typ | 2 +- 7 files changed, 117 insertions(+), 72 deletions(-) create mode 100644 tests/typ/func-font-error.typ diff --git a/src/eval/state.rs b/src/eval/state.rs index cb4a5eb93..3f3ac8e4c 100644 --- a/src/eval/state.rs +++ b/src/eval/state.rs @@ -150,6 +150,7 @@ fn default_font_families() -> FallbackTree { "serif" => ["source serif pro", "noto serif"], "sans-serif" => ["source sans pro", "noto sans"], "monospace" => ["source code pro", "noto sans mono"], + "emoji" => ["segoe ui emoji", "noto emoji"], "math" => ["latin modern math", "serif"], }, base: [ diff --git a/src/library/insert.rs b/src/library/insert.rs index a06cf1705..6f267b7af 100644 --- a/src/library/insert.rs +++ b/src/library/insert.rs @@ -7,7 +7,7 @@ use crate::prelude::*; /// `image`: Insert an image. /// /// # Positional arguments -/// - The path to the image (string) +/// - Path (`string`): The path to the image file. /// /// Supports PNG and JPEG files. pub fn image(mut args: Args, ctx: &mut EvalContext) -> Value { diff --git a/src/library/layout.rs b/src/library/layout.rs index 43826e245..c014cc349 100644 --- a/src/library/layout.rs +++ b/src/library/layout.rs @@ -9,15 +9,26 @@ use crate::prelude::*; /// `align`: Align content along the layouting axes. /// /// # Positional arguments -/// - At most two of `left`, `right`, `top`, `bottom`, `center`. -/// -/// When `center` is used as a positional argument, it is automatically inferred -/// which axis it should apply to depending on further arguments, defaulting -/// to the cross axis. +/// - first (optional, `Alignment`): An alignment for any of the two axes. +/// - second (optional, `Alignment`): An alignment for the other axis. /// /// # Keyword arguments -/// - `horizontal`: Any of `left`, `right` or `center`. -/// - `vertical`: Any of `top`, `bottom` or `center`. +/// - `horizontal` (`Alignment`): An alignment for the horizontal axis. +/// - `vertical` (`Alignment`): An alignment for the vertical axis. +/// +/// # Enumerations +/// - `Alignment` +/// - `left` +/// - `right` +/// - `top` +/// - `bottom` +/// - `center` +/// +/// # Notes +/// Which axis an alignment should apply to (main or cross) is inferred from +/// either the argument itself (for anything other than `center`) or from the +/// second argument if present, defaulting to the cross axis for a single +/// `center` alignment. pub fn align(mut args: Args, ctx: &mut EvalContext) -> Value { let snapshot = ctx.state.clone(); let body = args.find::(); @@ -178,8 +189,8 @@ impl Display for SpecAlign { /// `box`: Layout content into a box. /// /// # Keyword arguments -/// - `width`: The width of the box (length or relative to parent's width). -/// - `height`: The height of the box (length or relative to parent's height). +/// - `width` (`linear` relative to parent width): The width of the box. +/// - `height` (`linear` relative to parent height): The height of the box. pub fn boxed(mut args: Args, ctx: &mut EvalContext) -> Value { let snapshot = ctx.state.clone(); let body = args.find::().unwrap_or_default(); @@ -219,7 +230,7 @@ pub fn boxed(mut args: Args, ctx: &mut EvalContext) -> Value { /// `h`: Add horizontal spacing. /// /// # Positional arguments -/// - The spacing (length or relative to font size). +/// - Spacing (`linear` relative to font size): The amount of spacing. pub fn h(args: Args, ctx: &mut EvalContext) -> Value { spacing(args, ctx, SpecAxis::Horizontal) } @@ -227,7 +238,7 @@ pub fn h(args: Args, ctx: &mut EvalContext) -> Value { /// `v`: Add vertical spacing. /// /// # Positional arguments -/// - The spacing (length or relative to font size). +/// - Spacing (`linear` relative to font size): The amount of spacing. pub fn v(args: Args, ctx: &mut EvalContext) -> Value { spacing(args, ctx, SpecAxis::Vertical) } @@ -255,17 +266,20 @@ fn spacing(mut args: Args, ctx: &mut EvalContext, axis: SpecAxis) -> Value { /// `page`: Configure pages. /// /// # Positional arguments -/// - The name of a paper, e.g. `a4` (optional). +/// - Paper name (optional, `Paper`). /// /// # Keyword arguments -/// - `width`: The width of pages (length). -/// - `height`: The height of pages (length). -/// - `margins`: The margins for all sides (length or relative to side lengths). -/// - `left`: The left margin (length or relative to width). -/// - `right`: The right margin (length or relative to width). -/// - `top`: The top margin (length or relative to height). -/// - `bottom`: The bottom margin (length or relative to height). -/// - `flip`: Flips custom or paper-defined width and height (boolean). +/// - `width` (`length`): The width of pages. +/// - `height` (`length`): The height of pages. +/// - `margins` (`linear` relative to sides): The margins for all sides. +/// - `left` (`linear` relative to width): The left margin. +/// - `right` (`linear` relative to width): The right margin. +/// - `top` (`linear` relative to height): The top margin. +/// - `bottom` (`linear` relative to height): The bottom margin. +/// - `flip` (`bool`): Flips custom or paper-defined width and height. +/// +/// # Enumerations +/// - `Paper`: See [here](crate::paper) for a full list. pub fn page(mut args: Args, ctx: &mut EvalContext) -> Value { let snapshot = ctx.state.clone(); let body = args.find::(); diff --git a/src/library/mod.rs b/src/library/mod.rs index bd1feebb9..806b02759 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -12,7 +12,7 @@ use crate::eval::{Scope, ValueFunc}; macro_rules! std { ($($func:expr $(=> $name:expr)?),* $(,)?) => { - /// Create a scope with all standard library functions. + /// The scope containing all standard library functions. pub fn _std() -> Scope { let mut std = Scope::new(); $( diff --git a/src/library/style.rs b/src/library/style.rs index c641a598e..0cc40f94b 100644 --- a/src/library/style.rs +++ b/src/library/style.rs @@ -10,34 +10,59 @@ use crate::prelude::*; /// `font`: Configure the font. /// /// # Positional arguments -/// - The font size (optional, length or relative to current font size). -/// - All identifier and string arguments are interpreted as an ordered list of -/// fallback font families. -/// -/// An example invocation could look like this: -/// ```typst -/// [font: 12pt, Arial, "Noto Sans", sans-serif] -/// ``` +/// - Font size (optional, `linear` relative to current font size). +/// - Font families ... (optional, variadic, `Family`) /// /// # Keyword arguments -/// - `style` +/// - `style` (`Style`): The font style. +/// - `weight` (`Weight`): The font weight. +/// - `stretch` (`Stretch`): The font stretch. +/// - `serif` (`Family` or `dict` of type `Family`): The serif family. +/// - `sans-serif` (`Family` or `dict` of type `Family`): The new sansserif family. +/// - `monospace` (`Family` or `dict` of type `Family`): The monospace family. +/// - `emoji` (`Family` or `dict` of type `Family`): The emoji family. +/// - `math` (`Family` or `dict` of type `Family`): The math family. +/// +/// # Examples +/// Set font size and font families. +/// ```typst +/// [font: 12pt, "Arial", "Noto Sans", sans-serif] +/// ``` +/// +/// Redefine the default sans-serif family to a single font family. +/// ```typst +/// [font: sans-serif="Source Sans Pro"] +/// ``` +/// +/// Redefine the default emoji family with a fallback. +/// ```typst +/// [font: emoji=("Segoe UI Emoji", "Noto Emoji")] +/// ``` +/// +/// # Enumerations +/// - `Family` +/// - `serif` +/// - `sans-serif` +/// - `monospace` +/// - `emoji` +/// - `math` +/// - any string +/// - `Style` /// - `normal` /// - `italic` /// - `oblique` -/// -/// - `weight` -/// - `thin` or `hairline` (`100`) -/// - `extralight` (`200`) -/// - `light` (`300`) -/// - `regular` (`400`) -/// - `medium` (`500`) -/// - `semibold` (`600`) -/// - `bold` (`700`) -/// - `extrabold` (`800`) -/// - `black` (`900`) -/// - integer between `100` and `900` -/// -/// - `stretch` +/// - `Weight` +/// - `thin` or `hairline` (100) +/// - `extralight` (200) +/// - `light` (300) +/// - `regular` (400) +/// - `medium` (500) +/// - `semibold` (600) +/// - `bold` (700) +/// - `extrabold` (800) +/// - `black` (900) +/// - any integer between 100 and 900 +/// - `Stretch` /// - `ultra-condensed` /// - `extra-condensed` /// - `condensed` @@ -47,17 +72,6 @@ use crate::prelude::*; /// - `expanded` /// - `extra-expanded` /// - `ultra-expanded` -/// -/// - Any other keyword argument whose value is a dictionary of strings defines -/// a fallback class, for example: -/// ```typst -/// [font: serif = ("Source Serif Pro", "Noto Serif")] -/// ``` -/// This class can be used in the fallback list or other fallback classes as -/// long as the resulting fallback tree is acyclic. -/// ```typst -/// [font: "My Serif", serif] -/// ``` pub fn font(mut args: Args, ctx: &mut EvalContext) -> Value { let snapshot = ctx.state.clone(); let body = args.find::(); @@ -71,6 +85,14 @@ pub fn font(mut args: Args, ctx: &mut EvalContext) -> Value { } } + let mut needs_flattening = false; + let list: Vec<_> = args.find_all::().map(|s| s.to_lowercase()).collect(); + + if !list.is_empty() { + Rc::make_mut(&mut ctx.state.font.families).list = list; + needs_flattening = true; + } + if let Some(style) = args.get::<_, FontStyle>(ctx, "style") { ctx.state.font.variant.style = style; } @@ -83,21 +105,23 @@ pub fn font(mut args: Args, ctx: &mut EvalContext) -> Value { ctx.state.font.variant.stretch = stretch; } - let mut needs_flattening = false; - let list: Vec<_> = args.find_all::().map(|s| s.to_lowercase()).collect(); - if !list.is_empty() { - Rc::make_mut(&mut ctx.state.font.families).list = list; - needs_flattening = true; - } + struct FontList(Vec); - for (class, dict) in args.find_all_str::>() { - let fallback = Args(dict) + try_from_match!(FontList["font or list of fonts"] @ span: + Value::Str(v) => Self(vec![v.to_lowercase()]), + Value::Dict(v) => Self(Args(v.span_with(span)) .find_all::() .map(|s| s.to_lowercase()) - .collect(); + .collect() + ), + ); - Rc::make_mut(&mut ctx.state.font.families).update_class_list(class, fallback); - needs_flattening = true; + for &class in &["serif", "sans-serif", "monospace", "emoji", "math"] { + if let Some(list) = args.get::<_, FontList>(ctx, class) { + Rc::make_mut(&mut ctx.state.font.families) + .update_class_list(class.to_string(), list.0); + needs_flattening = true; + } } if needs_flattening { @@ -117,10 +141,10 @@ pub fn font(mut args: Args, ctx: &mut EvalContext) -> Value { /// `rgb`: Create an RGB(A) color. /// /// # Positional arguments -/// - The red component (float between 0.0 and 1.0). -/// - The green component (float between 0.0 and 1.0). -/// - The blue component (float between 0.0 and 1.0). -/// - The alpha component (optional, float between 0.0 and 1.0). +/// - Red component (`float` between 0.0 and 1.0). +/// - Green component (`float` between 0.0 and 1.0). +/// - Blue component (`float` between 0.0 and 1.0). +/// - Alpha component (optional, `float` between 0.0 and 1.0). pub fn rgb(mut args: Args, ctx: &mut EvalContext) -> Value { let r = args.need::<_, Spanned>(ctx, 0, "red component"); let g = args.need::<_, Spanned>(ctx, 1, "green component"); diff --git a/tests/typ/func-font-error.typ b/tests/typ/func-font-error.typ new file mode 100644 index 000000000..34ff58371 --- /dev/null +++ b/tests/typ/func-font-error.typ @@ -0,0 +1,6 @@ +// Test error cases of the `font` function. + +[font: something="invalid"] + +// compare-ref: false +// error: 3:8-3:27 unexpected argument diff --git a/tests/typ/func-font-fallback.typ b/tests/typ/func-font-fallback.typ index 587baed73..b6509e624 100644 --- a/tests/typ/func-font-fallback.typ +++ b/tests/typ/func-font-fallback.typ @@ -11,7 +11,7 @@ Emoji: 🏀 [font: math][Math: ∫ α + β ➗ 3] // Class redefinition. -[font: sans-serif=("Noto Emoji",)] +[font: sans-serif="Noto Emoji"] [font: sans-serif=("Archivo", sans-serif)] New sans-serif. 🚀