diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-03-10 10:20:01 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-03-10 10:20:01 +0100 |
| commit | bbb9ed07ffe8a2a0ea0a232f6cfc52f82f7f7afe (patch) | |
| tree | 2b106b63e104652b66efc74a0ffa3080a8b0a134 | |
| parent | b2b8d37ce03de60582230e03c03efa356b6f31d3 (diff) | |
Better line spacing calculations ↕
- Only add line spacing between lines. Previously, line spacing was added below
every line, making `#box[word]` higher than just `word`.
- Compute box height of text as `ascender - descender` so that the full word is
contained in the box.
| -rw-r--r-- | src/exec/state.rs | 2 | ||||
| -rw-r--r-- | src/export/pdf.rs | 2 | ||||
| -rw-r--r-- | src/layout/par.rs | 8 | ||||
| -rw-r--r-- | src/shaping.rs | 57 | ||||
| -rw-r--r-- | tests/ref/comment.png | bin | 716 -> 746 bytes | |||
| -rw-r--r-- | tests/ref/control/for.png | bin | 2762 -> 2787 bytes | |||
| -rw-r--r-- | tests/ref/control/if.png | bin | 1689 -> 1727 bytes | |||
| -rw-r--r-- | tests/ref/control/invalid.png | bin | 3700 -> 3772 bytes | |||
| -rw-r--r-- | tests/ref/control/let.png | bin | 2050 -> 2089 bytes | |||
| -rw-r--r-- | tests/ref/control/while.png | bin | 886 -> 929 bytes | |||
| -rw-r--r-- | tests/ref/expr/array.png | bin | 3448 -> 3317 bytes | |||
| -rw-r--r-- | tests/ref/expr/block-invalid.png | bin | 506 -> 522 bytes | |||
| -rw-r--r-- | tests/ref/expr/block.png | bin | 1423 -> 1453 bytes | |||
| -rw-r--r-- | tests/ref/expr/call-invalid.png | bin | 2944 -> 2789 bytes | |||
| -rw-r--r-- | tests/ref/expr/call.png | bin | 6143 -> 5863 bytes | |||
| -rw-r--r-- | tests/ref/expr/dict.png | bin | 1651 -> 1627 bytes | |||
| -rw-r--r-- | tests/ref/expr/ops.png | bin | 807 -> 827 bytes | |||
| -rw-r--r-- | tests/ref/full/coma.png | bin | 59535 -> 59486 bytes | |||
| -rw-r--r-- | tests/ref/library/box.png | bin | 2663 -> 2510 bytes | |||
| -rw-r--r-- | tests/ref/library/font.png | bin | 4484 -> 4554 bytes | |||
| -rw-r--r-- | tests/ref/library/hv.png | bin | 3898 -> 3960 bytes | |||
| -rw-r--r-- | tests/ref/library/image.png | bin | 220343 -> 219392 bytes | |||
| -rw-r--r-- | tests/ref/library/page.png | bin | 8276 -> 8417 bytes | |||
| -rw-r--r-- | tests/ref/library/pagebreak.png | bin | 814 -> 840 bytes | |||
| -rw-r--r-- | tests/ref/markup/emph.png | bin | 3060 -> 3079 bytes | |||
| -rw-r--r-- | tests/ref/markup/escape.png | bin | 4190 -> 4197 bytes | |||
| -rw-r--r-- | tests/ref/markup/heading.png | bin | 4623 -> 4656 bytes | |||
| -rw-r--r-- | tests/ref/markup/linebreak.png | bin | 3765 -> 3864 bytes | |||
| -rw-r--r-- | tests/ref/markup/nbsp.png | bin | 1745 -> 1818 bytes | |||
| -rw-r--r-- | tests/ref/markup/raw.png | bin | 7334 -> 7282 bytes | |||
| -rw-r--r-- | tests/ref/markup/strong.png | bin | 2799 -> 2847 bytes | |||
| -rw-r--r-- | tests/ref/repr.png | bin | 10420 -> 10208 bytes | |||
| -rw-r--r-- | tests/ref/spacing.png | bin | 5455 -> 5494 bytes | |||
| -rw-r--r-- | tests/ref/text.png | bin | 1853 -> 1880 bytes | |||
| -rw-r--r-- | tests/typ/full/coma.typ | 2 | ||||
| -rw-r--r-- | tests/typ/library/box.typ | 12 | ||||
| -rw-r--r-- | tests/typ/repr.typ | 3 | ||||
| -rw-r--r-- | tests/typeset.rs | 2 |
38 files changed, 51 insertions, 37 deletions
diff --git a/src/exec/state.rs b/src/exec/state.rs index 22839c54..416b5d08 100644 --- a/src/exec/state.rs +++ b/src/exec/state.rs @@ -93,7 +93,7 @@ impl Default for ParState { fn default() -> Self { Self { word_spacing: Relative::new(0.25).into(), - line_spacing: Relative::new(0.2).into(), + line_spacing: Linear::ZERO, par_spacing: Relative::new(0.5).into(), } } diff --git a/src/export/pdf.rs b/src/export/pdf.rs index 9f355278..c30222b2 100644 --- a/src/export/pdf.rs +++ b/src/export/pdf.rs @@ -195,7 +195,7 @@ impl<'a> PdfExporter<'a> { } let x = pos.x.to_pt() as f32; - let y = (page.size.height - pos.y - size).to_pt() as f32; + let y = (page.size.height - pos.y).to_pt() as f32; text.matrix(1.0, 0.0, 0.0, 1.0, x, y); text.show(&shaped.encode_glyphs_be()); } diff --git a/src/layout/par.rs b/src/layout/par.rs index 7d876fc1..45494dec 100644 --- a/src/layout/par.rs +++ b/src/layout/par.rs @@ -163,13 +163,17 @@ impl<'a> ParLayouter<'a> { output.push_frame(pos, frame); } + // Add line spacing, but only between lines. + if !self.lines.is_empty() { + self.lines_size.main += self.par.line_spacing; + *self.areas.current.get_mut(self.main) -= self.par.line_spacing; + } + // Update metrics of the whole paragraph. self.lines.push((self.lines_size.main, output, self.line_ruler)); self.lines_size.main += full_size.main; - self.lines_size.main += self.par.line_spacing; self.lines_size.cross = self.lines_size.cross.max(full_size.cross); *self.areas.current.get_mut(self.main) -= full_size.main; - *self.areas.current.get_mut(self.main) -= self.par.line_spacing; // Reset metrics for the single line. self.line_size = Gen::ZERO; diff --git a/src/shaping.rs b/src/shaping.rs index 722d103a..a8d2b2bf 100644 --- a/src/shaping.rs +++ b/src/shaping.rs @@ -70,6 +70,8 @@ pub fn shape( let mut frame = Frame::new(Size::new(Length::ZERO, font_size)); let mut shaped = Shaped::new(FaceId::MAX, font_size); let mut offset = Length::ZERO; + let mut ascender = Length::ZERO; + let mut descender = Length::ZERO; // Create an iterator with conditional direction. let mut forwards = text.chars(); @@ -84,47 +86,56 @@ pub fn shape( let query = FaceQuery { fallback: fallback.iter(), variant, c }; if let Some(id) = loader.query(query) { let face = loader.face(id).get(); - let (glyph, width) = match lookup_glyph(face, c, font_size) { + let (glyph, width) = match lookup_glyph(face, c) { Some(v) => v, None => continue, }; - // Flush the buffer if we change the font face. - if shaped.face != id && !shaped.text.is_empty() { - let pos = Point::new(frame.size.width, Length::ZERO); - frame.push(pos, Element::Text(shaped)); - frame.size.width += offset; - shaped = Shaped::new(FaceId::MAX, font_size); + let units_per_em = f64::from(face.units_per_em().unwrap_or(1000)); + let convert = |units| units / units_per_em * font_size; + + // Flush the buffer and reset the metrics if we use a new font face. + if shaped.face != id { + place(&mut frame, shaped, offset, ascender, descender); + + shaped = Shaped::new(id, font_size); offset = Length::ZERO; + ascender = convert(f64::from(face.ascender())); + descender = convert(f64::from(face.descender())); } - shaped.face = id; shaped.text.push(c); shaped.glyphs.push(glyph); shaped.offsets.push(offset); - offset += width; + offset += convert(f64::from(width)); } } // Flush the last buffered parts of the word. - if !shaped.text.is_empty() { - let pos = Point::new(frame.size.width, Length::ZERO); - frame.push(pos, Element::Text(shaped)); - frame.size.width += offset; - } + place(&mut frame, shaped, offset, ascender, descender); frame } -/// Looks up the glyph for `c` and returns its index alongside its width at the -/// given `size`. -fn lookup_glyph(face: &Face, c: char, size: Length) -> Option<(GlyphId, Length)> { +/// Look up the glyph for `c` and returns its index alongside its advance width. +fn lookup_glyph(face: &Face, c: char) -> Option<(GlyphId, u16)> { let glyph = face.glyph_index(c)?; - - // Determine the width of the char. - let units_per_em = face.units_per_em().unwrap_or(1000) as f64; - let width_units = face.glyph_hor_advance(glyph)? as f64; - let width = width_units / units_per_em * size; - + let width = face.glyph_hor_advance(glyph)?; Some((glyph, width)) } + +/// Place shaped text into a frame. +fn place( + frame: &mut Frame, + shaped: Shaped, + offset: Length, + ascender: Length, + descender: Length, +) { + if !shaped.text.is_empty() { + let pos = Point::new(frame.size.width, ascender); + frame.push(pos, Element::Text(shaped)); + frame.size.width += offset; + frame.size.height = frame.size.height.max(ascender - descender); + } +} diff --git a/tests/ref/comment.png b/tests/ref/comment.png Binary files differindex 9cee51b6..1fad97fe 100644 --- a/tests/ref/comment.png +++ b/tests/ref/comment.png diff --git a/tests/ref/control/for.png b/tests/ref/control/for.png Binary files differindex f0d4a5cd..d04ea62c 100644 --- a/tests/ref/control/for.png +++ b/tests/ref/control/for.png diff --git a/tests/ref/control/if.png b/tests/ref/control/if.png Binary files differindex 04724e1f..99bde49e 100644 --- a/tests/ref/control/if.png +++ b/tests/ref/control/if.png diff --git a/tests/ref/control/invalid.png b/tests/ref/control/invalid.png Binary files differindex c9822eeb..ff4c6987 100644 --- a/tests/ref/control/invalid.png +++ b/tests/ref/control/invalid.png diff --git a/tests/ref/control/let.png b/tests/ref/control/let.png Binary files differindex 693a8d4f..5fb1c992 100644 --- a/tests/ref/control/let.png +++ b/tests/ref/control/let.png diff --git a/tests/ref/control/while.png b/tests/ref/control/while.png Binary files differindex 55a6ed80..890131f1 100644 --- a/tests/ref/control/while.png +++ b/tests/ref/control/while.png diff --git a/tests/ref/expr/array.png b/tests/ref/expr/array.png Binary files differindex 44331d9f..784553b3 100644 --- a/tests/ref/expr/array.png +++ b/tests/ref/expr/array.png diff --git a/tests/ref/expr/block-invalid.png b/tests/ref/expr/block-invalid.png Binary files differindex 11899d86..b380defc 100644 --- a/tests/ref/expr/block-invalid.png +++ b/tests/ref/expr/block-invalid.png diff --git a/tests/ref/expr/block.png b/tests/ref/expr/block.png Binary files differindex ffa59568..d5ad52ae 100644 --- a/tests/ref/expr/block.png +++ b/tests/ref/expr/block.png diff --git a/tests/ref/expr/call-invalid.png b/tests/ref/expr/call-invalid.png Binary files differindex 0d3783a8..88f13780 100644 --- a/tests/ref/expr/call-invalid.png +++ b/tests/ref/expr/call-invalid.png diff --git a/tests/ref/expr/call.png b/tests/ref/expr/call.png Binary files differindex 39941778..7197bb90 100644 --- a/tests/ref/expr/call.png +++ b/tests/ref/expr/call.png diff --git a/tests/ref/expr/dict.png b/tests/ref/expr/dict.png Binary files differindex 351e0498..28a7cb3b 100644 --- a/tests/ref/expr/dict.png +++ b/tests/ref/expr/dict.png diff --git a/tests/ref/expr/ops.png b/tests/ref/expr/ops.png Binary files differindex fb717dd0..95a24c0e 100644 --- a/tests/ref/expr/ops.png +++ b/tests/ref/expr/ops.png diff --git a/tests/ref/full/coma.png b/tests/ref/full/coma.png Binary files differindex ed849404..f5db682d 100644 --- a/tests/ref/full/coma.png +++ b/tests/ref/full/coma.png diff --git a/tests/ref/library/box.png b/tests/ref/library/box.png Binary files differindex e8513d70..2804f8e9 100644 --- a/tests/ref/library/box.png +++ b/tests/ref/library/box.png diff --git a/tests/ref/library/font.png b/tests/ref/library/font.png Binary files differindex 92b5b7fc..8e611b16 100644 --- a/tests/ref/library/font.png +++ b/tests/ref/library/font.png diff --git a/tests/ref/library/hv.png b/tests/ref/library/hv.png Binary files differindex 13a352a4..0842d409 100644 --- a/tests/ref/library/hv.png +++ b/tests/ref/library/hv.png diff --git a/tests/ref/library/image.png b/tests/ref/library/image.png Binary files differindex 70f263e6..af497c8d 100644 --- a/tests/ref/library/image.png +++ b/tests/ref/library/image.png diff --git a/tests/ref/library/page.png b/tests/ref/library/page.png Binary files differindex 1ddeeb3d..1537fad4 100644 --- a/tests/ref/library/page.png +++ b/tests/ref/library/page.png diff --git a/tests/ref/library/pagebreak.png b/tests/ref/library/pagebreak.png Binary files differindex dfb9dcaa..b1ee0eef 100644 --- a/tests/ref/library/pagebreak.png +++ b/tests/ref/library/pagebreak.png diff --git a/tests/ref/markup/emph.png b/tests/ref/markup/emph.png Binary files differindex aec7fefb..f002610e 100644 --- a/tests/ref/markup/emph.png +++ b/tests/ref/markup/emph.png diff --git a/tests/ref/markup/escape.png b/tests/ref/markup/escape.png Binary files differindex 54e61201..3fa44c6d 100644 --- a/tests/ref/markup/escape.png +++ b/tests/ref/markup/escape.png diff --git a/tests/ref/markup/heading.png b/tests/ref/markup/heading.png Binary files differindex b4aa1bbd..9f2ce6ec 100644 --- a/tests/ref/markup/heading.png +++ b/tests/ref/markup/heading.png diff --git a/tests/ref/markup/linebreak.png b/tests/ref/markup/linebreak.png Binary files differindex dda5efa6..fec65248 100644 --- a/tests/ref/markup/linebreak.png +++ b/tests/ref/markup/linebreak.png diff --git a/tests/ref/markup/nbsp.png b/tests/ref/markup/nbsp.png Binary files differindex ad7591ac..3e1f0ee7 100644 --- a/tests/ref/markup/nbsp.png +++ b/tests/ref/markup/nbsp.png diff --git a/tests/ref/markup/raw.png b/tests/ref/markup/raw.png Binary files differindex c0bf0160..ce833428 100644 --- a/tests/ref/markup/raw.png +++ b/tests/ref/markup/raw.png diff --git a/tests/ref/markup/strong.png b/tests/ref/markup/strong.png Binary files differindex cd6d670d..ac38c436 100644 --- a/tests/ref/markup/strong.png +++ b/tests/ref/markup/strong.png diff --git a/tests/ref/repr.png b/tests/ref/repr.png Binary files differindex 54a1d240..21d7af27 100644 --- a/tests/ref/repr.png +++ b/tests/ref/repr.png diff --git a/tests/ref/spacing.png b/tests/ref/spacing.png Binary files differindex a8086177..8bdc9885 100644 --- a/tests/ref/spacing.png +++ b/tests/ref/spacing.png diff --git a/tests/ref/text.png b/tests/ref/text.png Binary files differindex 31d0b45e..38ae364d 100644 --- a/tests/ref/text.png +++ b/tests/ref/text.png diff --git a/tests/typ/full/coma.typ b/tests/typ/full/coma.typ index 39404a35..d324627f 100644 --- a/tests/typ/full/coma.typ +++ b/tests/typ/full/coma.typ @@ -1,5 +1,5 @@ // Configuration with `page` and `font` functions. -#page(width: 450pt, height: 380pt, margins: 1cm) +#page(width: 450pt, margins: 1cm) // There are variables and they can take normal values like strings, ... #let city = "Berlin" diff --git a/tests/typ/library/box.typ b/tests/typ/library/box.typ index 10e2c93e..7ce56859 100644 --- a/tests/typ/library/box.typ +++ b/tests/typ/library/box.typ @@ -1,15 +1,15 @@ // Test the box function. --- -#page("a7", flip: true) +#page("a8", flip: true) // Box with fixed width, should have text height. -#box(width: 2cm, color: #9650D6)[A] +#box(width: 2cm, color: #9650D6)[Legal] Sometimes there is no box. // Box with fixed height, should span line. -#box(height: 2cm, width: 100%, color: #734CED)[B] +#box(height: 1cm, width: 100%, color: #734CED)[B] // Empty box with fixed width and height. #box(width: 6cm, height: 12pt, color: #CB4CED) @@ -18,6 +18,6 @@ Sometimes there is no box. #box(width: 2in, color: #ff0000) // These are in a row! -#box(width: 1in, height: 10pt, color: #D6CD67) -#box(width: 1in, height: 10pt, color: #EDD466) -#box(width: 1in, height: 10pt, color: #E3BE62) +#box(width: 0.5in, height: 10pt, color: #D6CD67) +#box(width: 0.5in, height: 10pt, color: #EDD466) +#box(width: 0.5in, height: 10pt, color: #E3BE62) diff --git a/tests/typ/repr.typ b/tests/typ/repr.typ index 6eead75e..96eb710e 100644 --- a/tests/typ/repr.typ +++ b/tests/typ/repr.typ @@ -32,9 +32,8 @@ {12e1pt} \ {2.5rad} \ {45deg} \ - // Not in monospace via repr. -#repr(45deg) +#repr(45deg) \ --- // Colors. diff --git a/tests/typeset.rs b/tests/typeset.rs index c56655f5..232dfa5c 100644 --- a/tests/typeset.rs +++ b/tests/typeset.rs @@ -393,7 +393,7 @@ fn draw_text(env: &Env, canvas: &mut Canvas, pos: Point, shaped: &Shaped) { let units_per_em = face.units_per_em().unwrap_or(1000); let x = (pos.x + offset).to_pt() as f32; - let y = (pos.y + shaped.font_size).to_pt() as f32; + let y = pos.y.to_pt() as f32; let scale = (shaped.font_size / units_per_em as f64).to_pt() as f32; let mut builder = WrappedPathBuilder(PathBuilder::new()); |
