Add microtype options for par

This commit is contained in:
diquah 2025-04-10 19:01:02 -07:00
parent 422eb36f16
commit 07ba1c1636
4 changed files with 58 additions and 24 deletions

View File

@ -488,7 +488,16 @@ pub fn commit(
let stretchability = line.stretchability(); let stretchability = line.stretchability();
if remaining < Abs::zero() && shrinkability > Abs::zero() { if remaining < Abs::zero() && shrinkability > Abs::zero() {
// Attempt to reduce the length of the line, using shrinkability. // Attempt to reduce the length of the line, using shrinkability.
justification_ratio = (remaining / shrinkability).max(-1.0); let microjustifiables = line.microjustifiables();
extra_microjustification = (remaining / microjustifiables as f64)
.max(p.config.microtype.max_retract.abs);
justification_ratio = ((remaining
- extra_microjustification * microjustifiables as f64)
/ shrinkability)
.max(-1.0);
remaining = (remaining + shrinkability).min(Abs::zero()); remaining = (remaining + shrinkability).min(Abs::zero());
} else if line.justify && fr.is_zero() { } else if line.justify && fr.is_zero() {
// Attempt to increase the length of the line, using stretchability. // Attempt to increase the length of the line, using stretchability.
@ -502,8 +511,8 @@ pub fn commit(
if justifiables > 0 && remaining > Abs::zero() { if justifiables > 0 && remaining > Abs::zero() {
// Underfull line, distribute the extra space. // Underfull line, distribute the extra space.
extra_microjustification = extra_microjustification = (remaining / microjustifiables as f64)
(remaining / microjustifiables as f64).min(p.config.microjustification); .min(p.config.microtype.max_expand.abs);
extra_justification = (remaining extra_justification = (remaining
- extra_microjustification * microjustifiables as f64) - extra_microjustification * microjustifiables as f64)

View File

@ -17,8 +17,8 @@ use typst_library::foundations::{Packed, Resolve, Smart, StyleChain};
use typst_library::introspection::{Introspector, Locator, LocatorLink, SplitLocator}; use typst_library::introspection::{Introspector, Locator, LocatorLink, SplitLocator};
use typst_library::layout::{Abs, AlignElem, Dir, FixedAlignment, Fragment, Size}; use typst_library::layout::{Abs, AlignElem, Dir, FixedAlignment, Fragment, Size};
use typst_library::model::{ use typst_library::model::{
EnumElem, FirstLineIndent, Linebreaks, ListElem, ParElem, ParLine, ParLineMarker, EnumElem, FirstLineIndent, Linebreaks, ListElem, Microtype, ParElem, ParLine,
TermsElem, ParLineMarker, TermsElem,
}; };
use typst_library::routines::{Arenas, Pair, RealizationKind, Routines}; use typst_library::routines::{Arenas, Pair, RealizationKind, Routines};
use typst_library::text::{Costs, Lang, TextElem}; use typst_library::text::{Costs, Lang, TextElem};
@ -183,13 +183,13 @@ fn configuration(
situation: Option<ParSituation>, situation: Option<ParSituation>,
) -> Config { ) -> Config {
let justify = base.justify; let justify = base.justify;
let microjustification = ParElem::microjustification_in(shared); let microtype = ParElem::microtype_in(shared);
let font_size = TextElem::size_in(shared); let font_size = TextElem::size_in(shared);
let dir = TextElem::dir_in(shared); let dir = TextElem::dir_in(shared);
Config { Config {
justify, justify,
microjustification, microtype,
linebreaks: base.linebreaks.unwrap_or_else(|| { linebreaks: base.linebreaks.unwrap_or_else(|| {
if justify { if justify {
Linebreaks::Optimized Linebreaks::Optimized
@ -269,10 +269,8 @@ struct ConfigBase {
struct Config { struct Config {
/// Whether to justify text. /// Whether to justify text.
justify: bool, justify: bool,
/// The maximum allowed kerning adjustment for microjustification. /// The maximum allowed kerning adjustment for microjustification.
microjustification: Abs, microtype: Microtype,
/// How to determine line breaks. /// How to determine line breaks.
linebreaks: Linebreaks, linebreaks: Linebreaks,
/// The indent the first line of a paragraph should have. /// The indent the first line of a paragraph should have.

View File

@ -10,6 +10,7 @@ use ttf_parser::Tag;
use typst_library::engine::Engine; use typst_library::engine::Engine;
use typst_library::foundations::{Smart, StyleChain}; use typst_library::foundations::{Smart, StyleChain};
use typst_library::layout::{Abs, Dir, Em, Frame, FrameItem, Point, Size}; use typst_library::layout::{Abs, Dir, Em, Frame, FrameItem, Point, Size};
use typst_library::model::{Microtype, ParElem};
use typst_library::text::{ use typst_library::text::{
families, features, is_default_ignorable, variant, Font, FontFamily, FontVariant, families, features, is_default_ignorable, variant, Font, FontFamily, FontVariant,
Glyph, Lang, Region, TextEdgeBounds, TextElem, TextItem, Glyph, Lang, Region, TextEdgeBounds, TextElem, TextItem,
@ -152,7 +153,7 @@ impl ShapedGlyph {
|| self.c.is_ascii_digit() || 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; let width = self.x_advance;
if self.is_space() { if self.is_space() {
Adjustability { Adjustability {
@ -176,7 +177,10 @@ impl ShapedGlyph {
shrinkability: (width / 4.0, width / 4.0), shrinkability: (width / 4.0, width / 4.0),
} }
} else { } else {
Adjustability::default() Adjustability {
stretchability: (Em::zero(), microtype.max_expand.em),
shrinkability: (Em::zero(), microtype.max_retract.em),
}
} }
} }
@ -900,7 +904,7 @@ fn shape_segment<'a>(
x_advance, x_advance,
Adjustability::default().stretchability, Adjustability::default().stretchability,
), ),
is_microjustifiable: true, is_microjustifiable: false,
script, script,
}); });
} else { } else {
@ -1034,9 +1038,10 @@ fn track_and_space(ctx: &mut ShapingContext) {
/// and CJK punctuation adjustments according to Chinese Layout Requirements. /// and CJK punctuation adjustments according to Chinese Layout Requirements.
fn calculate_adjustability(ctx: &mut ShapingContext, lang: Lang, region: Option<Region>) { fn calculate_adjustability(ctx: &mut ShapingContext, lang: Lang, region: Option<Region>) {
let style = cjk_punct_style(lang, region); let style = cjk_punct_style(lang, region);
let microtype = ParElem::microtype_in(ctx.styles);
for glyph in &mut ctx.glyphs { 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(); let mut glyphs = ctx.glyphs.iter_mut().peekable();

View File

@ -138,16 +138,8 @@ pub struct ParElem {
#[default(false)] #[default(false)]
pub justify: bool, pub justify: bool,
/// The maximum amount of kerning that is allowed to be used to further /// Microtypographical settings that are used during justification.
/// justify an existing line. When this value is nonzero, additional pub microtype: Microtype,
/// justification will be applied to individual glyphs.
///
/// Note that microjustifications are applied only after a line of text has
/// been constructed. This means that the layout will *not* be affected by
/// microjustifications, but the internal kerning of a line will be.
#[resolve]
#[default(Em::new(0.0).into())]
pub microjustification: Length,
/// How to determine line breaks. /// How to determine line breaks.
/// ///
@ -238,6 +230,36 @@ impl ParElem {
type ParLine; 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<Microtype> 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. /// How to determine line breaks in a paragraph.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
pub enum Linebreaks { pub enum Linebreaks {