summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-04-07 12:51:03 +0200
committerLaurenz <laurmaedje@gmail.com>2022-04-07 12:51:03 +0200
commiteb22eed31b08874fbbbee68d2ae59f0d02f9e95d (patch)
treec8f4926beee8fc5756eac9b8f6f63cafeb154cf4
parent3d52387eea321e94c13b61666f7a758052b20c5d (diff)
Make chinese justification less bad
-rw-r--r--src/library/text/par.rs28
-rw-r--r--src/library/text/shaping.rs19
-rw-r--r--tests/ref/text/justify.pngbin11714 -> 29155 bytes
-rw-r--r--tests/typ/text/justify.typ6
4 files changed, 34 insertions, 19 deletions
diff --git a/src/library/text/par.rs b/src/library/text/par.rs
index 67357135..1695e010 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 66936792..d398e56d 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::<Em>()
.resolve(self.styles.get(TextNode::SIZE))
diff --git a/tests/ref/text/justify.png b/tests/ref/text/justify.png
index 90f84bb6..aa01016f 100644
--- a/tests/ref/text/justify.png
+++ b/tests/ref/text/justify.png
Binary files differ
diff --git a/tests/typ/text/justify.typ b/tests/typ/text/justify.typ
index eb8feb61..d19249ea 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%。近年來,中国大陆地区的维基百科编辑者正在迅速增加;