summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-09-19 10:28:06 +0200
committerLaurenz <laurmaedje@gmail.com>2022-09-19 10:28:06 +0200
commitb98004330bf3ef372a0f0ef5c7daf1f96fd7873a (patch)
tree693b78cabbbbb5424b538be9cd79544fa1f48569
parent0d12f2ab23177642eef2e6bb9c583cdd0c743b33 (diff)
Handle non-breaking spaces during justification
-rw-r--r--src/library/text/shaping.rs21
-rw-r--r--tests/ref/text/linebreak.pngbin18957 -> 18981 bytes
-rw-r--r--tests/ref/text/shorthands.pngbin4375 -> 4842 bytes
-rw-r--r--tests/typ/text/shorthands.typ8
-rw-r--r--tools/support/typst.tmLanguage.json4
5 files changed, 25 insertions, 8 deletions
diff --git a/src/library/text/shaping.rs b/src/library/text/shaping.rs
index 9465fbd9..1bac626d 100644
--- a/src/library/text/shaping.rs
+++ b/src/library/text/shaping.rs
@@ -58,12 +58,12 @@ pub struct ShapedGlyph {
impl ShapedGlyph {
/// Whether the glyph is a space.
pub fn is_space(&self) -> bool {
- self.c == ' '
+ matches!(self.c, ' ' | '\u{00A0}' | ' ')
}
/// Whether the glyph is justifiable.
pub fn is_justifiable(&self) -> bool {
- matches!(self.c, ' ' | ',' | ' ' | '。' | '、')
+ self.is_space() || matches!(self.c, ',' | '。' | '、')
}
}
@@ -508,12 +508,14 @@ fn track_and_space(ctx: &mut ShapingContext) {
.get(TextNode::SPACING)
.map(|abs| Em::from_length(abs, ctx.size));
- if tracking.is_zero() && spacing.is_one() {
- return;
- }
-
let mut glyphs = ctx.glyphs.iter_mut().peekable();
while let Some(glyph) = glyphs.next() {
+ // Make non-breaking space same width as normal space.
+ if glyph.c == '\u{00A0}' {
+ let face = ctx.fonts.get(glyph.face_id);
+ glyph.x_advance -= nbsp_delta(face).unwrap_or_default();
+ }
+
if glyph.is_space() {
glyph.x_advance = spacing.relative_to(glyph.x_advance);
}
@@ -524,6 +526,13 @@ fn track_and_space(ctx: &mut ShapingContext) {
}
}
+/// Difference between non-breaking and normal space.
+fn nbsp_delta(face: &Face) -> Option<Em> {
+ let space = face.ttf().glyph_index(' ')?.0;
+ let nbsp = face.ttf().glyph_index('\u{00A0}')?.0;
+ Some(face.advance(nbsp)? - face.advance(space)?)
+}
+
/// Resolve the font variant with `BOLD` and `ITALIC` factored in.
pub fn variant(styles: StyleChain) -> FontVariant {
let mut variant = FontVariant::new(
diff --git a/tests/ref/text/linebreak.png b/tests/ref/text/linebreak.png
index 1a3f49df..77eef20e 100644
--- a/tests/ref/text/linebreak.png
+++ b/tests/ref/text/linebreak.png
Binary files differ
diff --git a/tests/ref/text/shorthands.png b/tests/ref/text/shorthands.png
index c21d49df..ddb679f5 100644
--- a/tests/ref/text/shorthands.png
+++ b/tests/ref/text/shorthands.png
Binary files differ
diff --git a/tests/typ/text/shorthands.typ b/tests/typ/text/shorthands.typ
index 2efd1ad1..5c94dab0 100644
--- a/tests/typ/text/shorthands.typ
+++ b/tests/typ/text/shorthands.typ
@@ -4,6 +4,14 @@
The non-breaking~space does work.
---
+// Make sure non-breaking and normal space always
+// have the same width. Even if the font decided
+// differently.
+#set text("Latin Modern Roman")
+a b \
+a~b
+
+---
- En dash: --
- Em dash: ---
diff --git a/tools/support/typst.tmLanguage.json b/tools/support/typst.tmLanguage.json
index 8e1a66c4..36ffade7 100644
--- a/tools/support/typst.tmLanguage.json
+++ b/tools/support/typst.tmLanguage.json
@@ -128,7 +128,7 @@
"captures": { "1": { "name": "punctuation.definition.label.typst" } }
},
{
- "begin": "(#)(pub|let|set|show|wrap)\\b",
+ "begin": "(#)(pub|let|set|rule|select|show|wrap)\\b",
"end": "\n|(;)|(?=])",
"beginCaptures": {
"0": { "name": "keyword.other.typst" },
@@ -239,7 +239,7 @@
},
{
"name": "keyword.other.typst",
- "match": "\\b(pub|let|set|show|wrap|as|in|from)\\b"
+ "match": "\\b(pub|let|set|rule|select|show|wrap|as|in|from)\\b"
},
{
"name": "keyword.control.conditional.typst",