diff --git a/src/exec/state.rs b/src/exec/state.rs index 6f900b546..24e4b9f37 100644 --- a/src/exec/state.rs +++ b/src/exec/state.rs @@ -165,17 +165,17 @@ impl Default for FontState { /// Describes a line that could be positioned over, under or on top of text. #[derive(Debug, Copy, Clone, PartialEq, Hash)] pub struct LineState { + /// Stroke color of the line. Defaults to the text color if `None`. + pub stroke: Option, /// Thickness of the line's stroke. Calling functions should attempt to /// read this value from the appropriate font tables if this is `None`. - pub strength: Option, + pub thickness: Option, /// Position of the line relative to the baseline. Calling functions should /// attempt to read this value from the appropriate font tables if this is /// `None`. - pub position: Option, + pub offset: Option, /// Amount that the line will be longer or shorter than its associated text. pub extent: Linear, - /// Color of the line. Will default to text color if `None`. - pub fill: Option, } /// Font family definitions. diff --git a/src/layout/shaping.rs b/src/layout/shaping.rs index 2496aae0f..c37300f7e 100644 --- a/src/layout/shaping.rs +++ b/src/layout/shaping.rs @@ -396,23 +396,24 @@ fn decorate( let mut apply = |substate: &LineState, metrics: fn(&Face) -> &LineMetrics| { let metrics = metrics(&ctx.cache.font.get(face_id)); - let strength = substate - .strength + let stroke = substate.stroke.unwrap_or(state.fill); + + let thickness = substate + .thickness .map(|s| s.resolve(state.size)) .unwrap_or(metrics.strength.to_length(state.size)); - let position = substate - .position + let offset = substate + .offset .map(|s| s.resolve(state.size)) - .unwrap_or(metrics.position.to_length(state.size)); + .unwrap_or(-metrics.position.to_length(state.size)); let extent = substate.extent.resolve(state.size); - let fill = substate.fill.unwrap_or(state.fill); - let pos = Point::new(pos.x - extent, pos.y - position); + let pos = Point::new(pos.x - extent, pos.y + offset); let target = Point::new(width + 2.0 * extent, Length::zero()); - let shape = Shape::Line(target, strength); - let element = Element::Geometry(shape, fill); + let shape = Shape::Line(target, thickness); + let element = Element::Geometry(shape, stroke); frame.push(pos, element); }; diff --git a/src/library/text.rs b/src/library/text.rs index bf0d49a49..9c2863bc9 100644 --- a/src/library/text.rs +++ b/src/library/text.rs @@ -6,14 +6,20 @@ use super::*; /// `font`: Configure the font. pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { - let list = args.named(ctx, "family"); - let size = args.named::(ctx, "size"); + let families = args.all(ctx); + let list = if families.is_empty() { + args.named(ctx, "family") + } else { + Some(FontDef(families)) + }; + + let size = args.eat(ctx).or_else(|| args.named::(ctx, "size")); let style = args.named(ctx, "style"); let weight = args.named(ctx, "weight"); let stretch = args.named(ctx, "stretch"); let top_edge = args.named(ctx, "top-edge"); let bottom_edge = args.named(ctx, "bottom-edge"); - let color = args.named(ctx, "color"); + let fill = args.named(ctx, "fill"); let serif = args.named(ctx, "serif"); let sans_serif = args.named(ctx, "sans-serif"); let monospace = args.named(ctx, "monospace"); @@ -50,8 +56,8 @@ pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { font.bottom_edge = bottom_edge; } - if let Some(color) = color { - font.fill = Fill::Color(color); + if let Some(fill) = fill { + font.fill = Fill::Color(fill); } if let Some(FamilyDef(serif)) = &serif { @@ -229,19 +235,19 @@ fn line_impl( args: &mut FuncArgs, substate: fn(&mut FontState) -> &mut Option>, ) -> Value { - let color = args.named(ctx, "color"); - let position = args.named(ctx, "position"); - let strength = args.named::(ctx, "strength"); + let stroke = args.eat(ctx).or_else(|| args.named(ctx, "stroke")); + let thickness = args.eat(ctx).or_else(|| args.named::(ctx, "thickness")); + let offset = args.named(ctx, "offset"); let extent = args.named(ctx, "extent").unwrap_or_default(); let body = args.expect::(ctx, "body").unwrap_or_default(); // Suppress any existing strikethrough if strength is explicitly zero. - let state = strength.map_or(true, |s| !s.is_zero()).then(|| { + let state = thickness.map_or(true, |s| !s.is_zero()).then(|| { Rc::new(LineState { - strength, - position, + stroke: stroke.map(Fill::Color), + thickness, + offset, extent, - fill: color.map(Fill::Color), }) }); diff --git a/tests/ref/text/decorations.png b/tests/ref/text/decorations.png index 1bde2dd4f..e3ca1be5e 100644 Binary files a/tests/ref/text/decorations.png and b/tests/ref/text/decorations.png differ diff --git a/tests/typ/code/call-wide.typ b/tests/typ/code/call-wide.typ index 996a052d1..1ad4995df 100644 --- a/tests/typ/code/call-wide.typ +++ b/tests/typ/code/call-wide.typ @@ -2,8 +2,8 @@ --- // Test multiple wide calls in separate expressions. -#font!(color: eastern) - First -#font!(color: forest) - Second +#font!(fill: eastern) - First +#font!(fill: forest) - Second --- // Test in heading. diff --git a/tests/typ/insert/circle.typ b/tests/typ/insert/circle.typ index fbb45435f..8c35b9887 100644 --- a/tests/typ/insert/circle.typ +++ b/tests/typ/insert/circle.typ @@ -30,7 +30,7 @@ Expanded by height. // Test relative sizing. #rect(width: 100%, height: 50pt, fill: rgb("aaa"))[ #align!(center, center) - #font!(color: white) + #font!(fill: white) #circle(radius: 10pt, fill: eastern)[A] #circle(height: 60%, fill: eastern)[B] #circle(width: 20% + 20pt, fill: eastern)[C] diff --git a/tests/typ/insert/square.typ b/tests/typ/insert/square.typ index f9efb98ed..d003635df 100644 --- a/tests/typ/insert/square.typ +++ b/tests/typ/insert/square.typ @@ -5,7 +5,7 @@ Auto-sized square. \ #square(fill: eastern)[ #align!(center) #pad!(5pt) - #font!(color: white, weight: bold) + #font!(fill: white, weight: bold) Typst ] diff --git a/tests/typ/text/bidi.typ b/tests/typ/text/bidi.typ index 26099b91b..0aab90797 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!(family: ("EB Garamond", "Noto Serif Hebrew")) +#font!("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!(family: ("EB Garamond", "Noto Sans Arabic")) +#font!("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!(family: ("EB Garamond", "Noto Serif Hebrew")) +#font!("EB Garamond", "Noto Serif Hebrew") #lang!("he") {text} #lang!("de") {text} --- // Test embedding up to level 4 with isolates. -#font!(family: ("EB Garamond", "Noto Serif Hebrew", "Twitter Color Emoji")) +#font!("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!(family: ("Noto Sans Arabic", "EB Garamond")) +#font!("Noto Sans Arabic", "EB Garamond") #lang!("ar") Life المطر هو الحياة \ الحياة تمطر is rain. --- // Test spacing. -#font!(family: ("EB Garamond", "Noto Serif Hebrew")) +#font!("EB Garamond", "Noto Serif Hebrew") L #h(1cm) ריווחR \ Lריווח #h(1cm) R --- // Test inline object. -#font!(family: ("Noto Serif Hebrew", "EB Garamond")) +#font!("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 2da3f1a1d..b6c3b3f9b 100644 --- a/tests/typ/text/chinese.typ +++ b/tests/typ/text/chinese.typ @@ -1,7 +1,7 @@ // Test chinese text from Wikipedia. --- -#font!(family: "Noto Serif CJK SC") +#font!("Noto Serif CJK SC") 是美国广播公司电视剧《迷失》第3季的第22和23集,也是全剧的第71集和72集 由执行制作人戴蒙·林道夫和卡尔顿·库斯编剧,导演则是另一名执行制作人杰克·本德 diff --git a/tests/typ/text/decorations.typ b/tests/typ/text/decorations.typ index 247178b29..3741dc194 100644 --- a/tests/typ/text/decorations.typ +++ b/tests/typ/text/decorations.typ @@ -3,14 +3,17 @@ --- #strike[Statements dreamt up by the utterly deranged.] -Sometimes, we work #strike(extent: 5%, strength: 10pt)[in secret]. There might -be #strike(extent: 5%, strength: 10pt, color: rgb("abcdef88"))[redacted] things. +Sometimes, we work #strike(10pt, extent: 5%)[in secret]. +There might be #strike(stroke: rgb("abcdef88"), thickness: 10pt, extent: 5%)[redacted] +things. + +#underline(offset: 5pt)[Further below.] --- -#underline(color: rgb("fc0030"))[Critical information is conveyed here.] -#underline[Still important, but not #underline(strength: 0pt)[mission ]critical.] +#underline(rgb("fc0030"))[Critical information is conveyed here.] +#underline[Still important, but not #underline(0pt)[mission ]critical.] -#font(color: rgb("fc0030"), underline[Change with the wind.]) +#font(fill: rgb("fc0030"), underline[Change with the wind.]) --- #overline(underline[Running amongst the wolves.]) diff --git a/tests/typ/text/font.typ b/tests/typ/text/font.typ index 9bfeb6849..f765c600c 100644 --- a/tests/typ/text/font.typ +++ b/tests/typ/text/font.typ @@ -2,8 +2,8 @@ --- // Set same font size in three different ways. -#font(size: 22pt)[A] -#font(size: 200%)[A] +#font(22pt)[A] +#font(200%)[A] #font(size: 16.5pt + 50%)[A] // Do nothing. @@ -25,12 +25,12 @@ Emoji: 🐪, 🌋, 🏞 // Math. -#font(family: "Latin Modern Math")[ +#font("Latin Modern Math")[ ∫ 𝛼 + 3𝛽 d𝑡 ] // Colors. -#font(color: eastern)[This is #font(color: rgb("FA644B"))[way more] colorful.] +#font(fill: eastern)[This is #font(fill: rgb("FA644B"))[way more] colorful.] --- // Test top and bottom edge. @@ -50,8 +50,8 @@ Emoji: 🐪, 🌋, 🏞 // Test class definitions. #font!(sans-serif: "PT Sans") #font(family: sans-serif)[Sans-serif.] \ -#font(family: monospace)[Monospace.] \ -#font(family: monospace, monospace: ("Nope", "Latin Modern Math"))[Math.] +#font(monospace)[Monospace.] \ +#font(monospace, monospace: ("Nope", "Latin Modern Math"))[Math.] --- // Ref: false @@ -72,3 +72,9 @@ Emoji: 🐪, 🌋, 🏞 // Error: 7-27 unexpected argument #font(something: "invalid")[] + +// Error: 13-23 unexpected argument +#font(12pt, size: 10pt)[] + +// Error: 16-35 unexpected argument +#font("Arial", family: "Helvetica")[] diff --git a/tests/typ/text/shaping.typ b/tests/typ/text/shaping.typ index 98630ea9b..23156aa4c 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!(family: "Noto Sans Arabic") +#font!("Noto Sans Arabic") دع النص يمطر عليك // This should form a three-member family. -#font!(family: "Twitter Color Emoji") +#font!("Twitter Color Emoji") 👩‍👩‍👦 🤚🏿 // These two shouldn't be affected by a zero-width joiner. @@ -20,7 +20,7 @@ Le fira --- // Test font fallback. -#font!(family: ("EB Garamond", "Noto Sans Arabic", "Twitter Color Emoji")) +#font!("EB Garamond", "Noto Sans Arabic", "Twitter Color Emoji") // Font fallback for emoji. A😀B @@ -40,6 +40,6 @@ A🐈中文B --- // Test reshaping. -#font!(family: "Noto Serif Hebrew") +#font!("Noto Serif Hebrew") #lang!("he") ס \ טֶ diff --git a/tests/typ/text/whitespace.typ b/tests/typ/text/whitespace.typ index 29c3e9906..418c3a129 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(family: "PT Sans")[ ]B +A#font("PT Sans")[ ]B --- // Test font change after space. -Left #font(family: "PT Sans")[Right]. +Left #font("PT Sans")[Right]. --- // Test that space at start of line is not trimmed.