diff --git a/src/library/text/par.rs b/src/library/text/par.rs index 673571350..1695e010b 100644 --- a/src/library/text/par.rs +++ b/src/library/text/par.rs @@ -310,15 +310,15 @@ impl<'a> Line<'a> { self.items().nth(index) } - // How many spaces the line contains. - fn spaces(&self) -> usize { - let mut spaces = 0; + // How many justifiable glyphs the line contains. + fn justifiables(&self) -> usize { + let mut count = 0; for item in self.items() { if let ParItem::Text(shaped) = item { - spaces += shaped.spaces(); + count += shaped.justifiables(); } } - spaces + count } /// How much of the line is stretchable spaces. @@ -509,11 +509,11 @@ fn linebreak_optimized<'a>( // Cost parameters. const HYPH_COST: Cost = 0.5; const CONSECUTIVE_DASH_COST: Cost = 30.0; - const MAX_COST: Cost = 10_000.0; + const MAX_COST: Cost = 1_000_000.0; const MIN_COST: Cost = -MAX_COST; const MIN_RATIO: f64 = -0.15; - // Density parameters. + let em = styles.get(TextNode::SIZE); let justify = styles.get(ParNode::JUSTIFY); // Dynamic programming table. @@ -537,11 +537,15 @@ fn linebreak_optimized<'a>( // Determine how much the line's spaces would need to be stretched // to make it the desired width. - let mut ratio = (width - attempt.size.x) / attempt.stretch(); + let delta = width - attempt.size.x; + let mut ratio = delta / attempt.stretch(); if ratio.is_infinite() { - ratio = ratio.signum() * MAX_COST; + ratio = delta / (em / 2.0); } + // At some point, it doesn't matter any more. + ratio = ratio.min(10.0); + // Determine the cost of the line. let mut cost = if ratio < if justify { MIN_RATIO } else { 0.0 } { // The line is overfull. This is the case if @@ -863,9 +867,9 @@ fn commit( && line.range.end < line.bidi.text.len() && line.fr.is_zero()) { - let spaces = line.spaces(); - if spaces > 0 { - justification = remaining / spaces as f64; + let justifiables = line.justifiables(); + if justifiables > 0 { + justification = remaining / justifiables as f64; remaining = Length::zero(); } } diff --git a/src/library/text/shaping.rs b/src/library/text/shaping.rs index 66936792a..d398e56de 100644 --- a/src/library/text/shaping.rs +++ b/src/library/text/shaping.rs @@ -50,10 +50,15 @@ pub struct ShapedGlyph { } impl ShapedGlyph { - /// Whether the glyph is a justifiable space. + /// Whether the glyph is a space. pub fn is_space(&self) -> bool { self.c == ' ' } + + /// Whether the glyph is justifiable. + pub fn is_justifiable(&self) -> bool { + matches!(self.c, ' ' | ',' | ' ' | '。' | '、') + } } /// A side you can go toward. @@ -68,7 +73,7 @@ impl<'a> ShapedText<'a> { /// Build the shaped text's frame. /// /// The `justification` defines how much extra advance width each - /// [space glyph](ShapedGlyph::is_space) will get. + /// [justifiable glyph](ShapedGlyph::is_justifiable) will get. pub fn build(&self, fonts: &FontStore, justification: Length) -> Frame { let mut offset = Length::zero(); let mut frame = Frame::new(self.size); @@ -84,7 +89,7 @@ impl<'a> ShapedText<'a> { .map(|glyph| Glyph { id: glyph.glyph_id, x_advance: glyph.x_advance - + if glyph.is_space() { + + if glyph.is_justifiable() { frame.size.x += justification; Em::from_length(justification, size) } else { @@ -115,16 +120,16 @@ impl<'a> ShapedText<'a> { frame } - /// How many spaces the text contains. - pub fn spaces(&self) -> usize { - self.glyphs.iter().filter(|g| g.is_space()).count() + /// How many justifiable glyphs the text contains. + pub fn justifiables(&self) -> usize { + self.glyphs.iter().filter(|g| g.is_justifiable()).count() } /// The width of the spaces in the text. pub fn stretch(&self) -> Length { self.glyphs .iter() - .filter(|g| g.is_space()) + .filter(|g| g.is_justifiable()) .map(|g| g.x_advance) .sum::() .resolve(self.styles.get(TextNode::SIZE)) diff --git a/tests/ref/text/justify.png b/tests/ref/text/justify.png index 90f84bb6e..aa01016f8 100644 Binary files a/tests/ref/text/justify.png and b/tests/ref/text/justify.png differ diff --git a/tests/typ/text/justify.typ b/tests/typ/text/justify.typ index eb8feb61b..d19249ead 100644 --- a/tests/typ/text/justify.typ +++ b/tests/typ/text/justify.typ @@ -19,3 +19,9 @@ First line indents and hyphenation play nicely with justified text. #set par(justify: true) A B C \ D + +--- +// Test that justificating chinese text is at least a bit sensible. +#set page(width: 200pt) +#set par(justify: true) +中文维基百科使用汉字书写,汉字是汉族或华人的共同文字,是中国大陆、新加坡、马来西亚、台湾、香港、澳门的唯一官方文字或官方文字之一。25.9%,而美国和荷兰则分別占13.7%及8.2%。近年來,中国大陆地区的维基百科编辑者正在迅速增加;