diff --git a/crates/typst-layout/src/inline/shaping.rs b/crates/typst-layout/src/inline/shaping.rs index 8236d1e36..55a1db4bc 100644 --- a/crates/typst-layout/src/inline/shaping.rs +++ b/crates/typst-layout/src/inline/shaping.rs @@ -10,6 +10,7 @@ use ttf_parser::Tag; use typst_library::engine::Engine; use typst_library::foundations::{Smart, StyleChain}; use typst_library::layout::{Abs, Dir, Em, Frame, FrameItem, Point, Size}; +use typst_library::model::{Microtype, ParElem}; use typst_library::text::{ families, features, is_default_ignorable, variant, Font, FontFamily, FontVariant, Glyph, Lang, Region, TextEdgeBounds, TextElem, TextItem, @@ -145,7 +146,11 @@ impl ShapedGlyph { || self.c.is_ascii_digit() } - pub fn base_adjustability(&self, style: CjkPunctStyle) -> Adjustability { + pub fn base_adjustability( + &self, + style: CjkPunctStyle, + microtype: Microtype, + ) -> Adjustability { let width = self.x_advance; if self.is_space() { Adjustability { @@ -169,7 +174,10 @@ impl ShapedGlyph { shrinkability: (width / 4.0, width / 4.0), } } else { - Adjustability::default() + Adjustability { + stretchability: (Em::zero(), microtype.max_expand.em), + shrinkability: (Em::zero(), microtype.max_retract.em), + } } } @@ -1013,9 +1021,10 @@ fn track_and_space(ctx: &mut ShapingContext) { /// and CJK punctuation adjustments according to Chinese Layout Requirements. fn calculate_adjustability(ctx: &mut ShapingContext, lang: Lang, region: Option) { let style = cjk_punct_style(lang, region); + let microtype = ParElem::microtype_in(ctx.styles); for glyph in &mut ctx.glyphs { - glyph.adjustability = glyph.base_adjustability(style); + glyph.adjustability = glyph.base_adjustability(style, microtype); } let mut glyphs = ctx.glyphs.iter_mut().peekable(); diff --git a/crates/typst-library/src/model/par.rs b/crates/typst-library/src/model/par.rs index cf31b5195..1afa29f1f 100644 --- a/crates/typst-library/src/model/par.rs +++ b/crates/typst-library/src/model/par.rs @@ -138,6 +138,9 @@ pub struct ParElem { #[default(false)] pub justify: bool, + /// Microtypographical settings that are used during justification. + pub microtype: Microtype, + /// How to determine line breaks. /// /// When this property is set to `{auto}`, its default value, optimized line @@ -227,6 +230,36 @@ impl ParElem { type ParLine; } +/// Configuration for microtypographical settings to be used during +/// justification. +#[derive(Debug, Default, Copy, Clone, PartialEq, Hash)] +pub struct Microtype { + /// How much a glyph is allowed to translate into its neighbor. + pub max_retract: Length, + /// How much a glyph is allowed to translate away from its neighbor. + pub max_expand: Length, +} + +cast! { + Microtype, + self => Value::Dict(self.into()), + mut dict: Dict => { + let max_retract = dict.take("max-retract")?.cast()?; + let max_expand = dict.take("max-expand")?.cast()?; + dict.finish(&["max-retract", "max-expand"])?; + Self { max_retract, max_expand } + }, +} + +impl From for Dict { + fn from(microtype: Microtype) -> Self { + dict! { + "max-retract" => microtype.max_retract, + "max-expand" => microtype.max_expand, + } + } +} + /// How to determine line breaks in a paragraph. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)] pub enum Linebreaks {