Make chinese justification less bad

This commit is contained in:
Laurenz 2022-04-07 12:51:03 +02:00
parent 3d52387eea
commit eb22eed31b
4 changed files with 34 additions and 19 deletions

View File

@ -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();
}
}

View File

@ -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::<Em>()
.resolve(self.styles.get(TextNode::SIZE))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -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%。近年來,中国大陆地区的维基百科编辑者正在迅速增加;