From 2d33393df967bbe55646b839e188c04380d823fe Mon Sep 17 00:00:00 2001 From: Malo <57839069+MDLC01@users.noreply.github.com> Date: Thu, 23 Jan 2025 19:24:35 +0100 Subject: [PATCH] Add support for `c2sc` OpenType feature in `smallcaps` (#5655) --- .../typst-library/src/model/bibliography.rs | 6 ++- crates/typst-library/src/text/mod.rs | 10 +++-- crates/typst-library/src/text/smallcaps.rs | 35 ++++++++++++++---- tests/ref/smallcaps-all.png | Bin 0 -> 512 bytes tests/suite/text/smallcaps.typ | 4 ++ 5 files changed, 42 insertions(+), 13 deletions(-) create mode 100644 tests/ref/smallcaps-all.png diff --git a/crates/typst-library/src/model/bibliography.rs b/crates/typst-library/src/model/bibliography.rs index 95db8a222..762a97fd9 100644 --- a/crates/typst-library/src/model/bibliography.rs +++ b/crates/typst-library/src/model/bibliography.rs @@ -38,7 +38,8 @@ use crate::model::{ }; use crate::routines::{EvalMode, Routines}; use crate::text::{ - FontStyle, Lang, LocalName, Region, SubElem, SuperElem, TextElem, WeightDelta, + FontStyle, Lang, LocalName, Region, Smallcaps, SubElem, SuperElem, TextElem, + WeightDelta, }; use crate::World; @@ -1046,7 +1047,8 @@ fn apply_formatting(mut content: Content, format: &hayagriva::Formatting) -> Con match format.font_variant { citationberg::FontVariant::Normal => {} citationberg::FontVariant::SmallCaps => { - content = content.styled(TextElem::set_smallcaps(true)); + content = + content.styled(TextElem::set_smallcaps(Some(Smallcaps::Minuscules))); } } diff --git a/crates/typst-library/src/text/mod.rs b/crates/typst-library/src/text/mod.rs index 6cca24587..edbd24139 100644 --- a/crates/typst-library/src/text/mod.rs +++ b/crates/typst-library/src/text/mod.rs @@ -755,11 +755,10 @@ pub struct TextElem { #[ghost] pub case: Option, - /// Whether small capital glyphs should be used. ("smcp") + /// Whether small capital glyphs should be used. ("smcp", "c2sc") #[internal] - #[default(false)] #[ghost] - pub smallcaps: bool, + pub smallcaps: Option, } impl TextElem { @@ -1249,8 +1248,11 @@ pub fn features(styles: StyleChain) -> Vec { } // Features that are off by default in Harfbuzz are only added if enabled. - if TextElem::smallcaps_in(styles) { + if let Some(sc) = TextElem::smallcaps_in(styles) { feat(b"smcp", 1); + if sc == Smallcaps::All { + feat(b"c2sc", 1); + } } if TextElem::alternates_in(styles) { diff --git a/crates/typst-library/src/text/smallcaps.rs b/crates/typst-library/src/text/smallcaps.rs index 1e88974f5..924a45e8c 100644 --- a/crates/typst-library/src/text/smallcaps.rs +++ b/crates/typst-library/src/text/smallcaps.rs @@ -12,11 +12,11 @@ use crate::text::TextElem; /// ``` /// /// # Smallcaps fonts -/// By default, this enables the OpenType `smcp` feature for the font. Not all -/// fonts support this feature. Sometimes smallcaps are part of a dedicated -/// font. This is, for example, the case for the _Latin Modern_ family of fonts. -/// In those cases, you can use a show-set rule to customize the appearance of -/// the text in smallcaps: +/// By default, this uses the `smcp` and `c2sc` OpenType features on the font. +/// Not all fonts support these features. Sometimes, smallcaps are part of a +/// dedicated font. This is, for example, the case for the _Latin Modern_ family +/// of fonts. In those cases, you can use a show-set rule to customize the +/// appearance of the text in smallcaps: /// /// ```typ /// #show smallcaps: set text(font: "Latin Modern Roman Caps") @@ -45,6 +45,17 @@ use crate::text::TextElem; /// ``` #[elem(title = "Small Capitals", Show)] pub struct SmallcapsElem { + /// Whether to turn uppercase letters into small capitals as well. + /// + /// Unless overridden by a show rule, this enables the `c2sc` OpenType + /// feature. + /// + /// ```example + /// #smallcaps(all: true)[UNICEF] is an + /// agency of #smallcaps(all: true)[UN]. + /// ``` + #[default(false)] + pub all: bool, /// The content to display in small capitals. #[required] pub body: Content, @@ -52,7 +63,17 @@ pub struct SmallcapsElem { impl Show for Packed { #[typst_macros::time(name = "smallcaps", span = self.span())] - fn show(&self, _: &mut Engine, _: StyleChain) -> SourceResult { - Ok(self.body.clone().styled(TextElem::set_smallcaps(true))) + fn show(&self, _: &mut Engine, styles: StyleChain) -> SourceResult { + let sc = if self.all(styles) { Smallcaps::All } else { Smallcaps::Minuscules }; + Ok(self.body.clone().styled(TextElem::set_smallcaps(Some(sc)))) } } + +/// What becomes small capitals. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum Smallcaps { + /// Minuscules become small capitals. + Minuscules, + /// All letters become small capitals. + All, +} diff --git a/tests/ref/smallcaps-all.png b/tests/ref/smallcaps-all.png new file mode 100644 index 0000000000000000000000000000000000000000..f3be53f8290996cd1ba6dccc177412d3631f12d5 GIT binary patch literal 512 zcmV+b0{{JqP)mHk zcidp#+I|6{I1D$=34uHCR1nB!vl-BImi4Tj2EP7l_VPP_lwP-XQ3wo8cIZ0cyO;7c zDq)M~q(=b?R~N5=b&OS=GXi1GZT4s0SFUB2C}0W5XUmj>!SzQZZ1Oh4mn#|+0B{!V z4mtRzA3?DD8DL_u`xA~%I0b+j419x4V zz*DF3|G`6521LRVJ_tPjeFHWoVXb9M9K5~fA^9Y3W!n4}qs+J&5duGVovtxQ&pWEU zX|={(_wMS;+e!G?z$J|~JIrF~5(394T#um)O(#M{%gGpz`_ekt+PO7DyB&yF5dyOd zgVm|R-^+vS!T=Smm@_#zQE7;RFQlM!5P$2+hz*n=K=*5-6k1+DnGx}efk*E*5mo9w z{OGB2O|W2b0`R$Zie$MhoG30S4$FW@Si%xM&hQR$(n#()v83?;0000