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();
if remaining < Abs::zero() && shrinkability > Abs::zero() {
// 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());
} else if line.justify && fr.is_zero() {
// Attempt to increase the length of the line, using stretchability.
@ -502,8 +511,8 @@ pub fn commit(
if justifiables > 0 && remaining > Abs::zero() {
// Underfull line, distribute the extra space.
extra_microjustification =
(remaining / microjustifiables as f64).min(p.config.microjustification);
extra_microjustification = (remaining / microjustifiables as f64)
.min(p.config.microtype.max_expand.abs);
extra_justification = (remaining
- 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::layout::{Abs, AlignElem, Dir, FixedAlignment, Fragment, Size};
use typst_library::model::{
EnumElem, FirstLineIndent, Linebreaks, ListElem, ParElem, ParLine, ParLineMarker,
TermsElem,
EnumElem, FirstLineIndent, Linebreaks, ListElem, Microtype, ParElem, ParLine,
ParLineMarker, TermsElem,
};
use typst_library::routines::{Arenas, Pair, RealizationKind, Routines};
use typst_library::text::{Costs, Lang, TextElem};
@ -183,13 +183,13 @@ fn configuration(
situation: Option<ParSituation>,
) -> Config {
let justify = base.justify;
let microjustification = ParElem::microjustification_in(shared);
let microtype = ParElem::microtype_in(shared);
let font_size = TextElem::size_in(shared);
let dir = TextElem::dir_in(shared);
Config {
justify,
microjustification,
microtype,
linebreaks: base.linebreaks.unwrap_or_else(|| {
if justify {
Linebreaks::Optimized
@ -269,10 +269,8 @@ struct ConfigBase {
struct Config {
/// Whether to justify text.
justify: bool,
/// The maximum allowed kerning adjustment for microjustification.
microjustification: Abs,
microtype: Microtype,
/// How to determine line breaks.
linebreaks: Linebreaks,
/// 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::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,
@ -152,7 +153,7 @@ 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 {
@ -176,7 +177,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),
}
}
}
@ -900,7 +904,7 @@ fn shape_segment<'a>(
x_advance,
Adjustability::default().stretchability,
),
is_microjustifiable: true,
is_microjustifiable: false,
script,
});
} else {
@ -1034,9 +1038,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<Region>) {
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();

View File

@ -138,16 +138,8 @@ pub struct ParElem {
#[default(false)]
pub justify: bool,
/// The maximum amount of kerning that is allowed to be used to further
/// justify an existing line. When this value is nonzero, additional
/// 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,
/// Microtypographical settings that are used during justification.
pub microtype: Microtype,
/// How to determine line breaks.
///
@ -238,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<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.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Cast)]
pub enum Linebreaks {