mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-08 11:52:10 +00:00
Merge pull request #5620 from dotCooCoo/fix/upright-vertical-baseline-shift
Type: baseline shift moves an upright glyph across the column in vertical type
This commit is contained in:
@@ -327,10 +327,14 @@ impl Ctx<'_> {
|
||||
} else if self.vertical && stands_upright(g) {
|
||||
let next = self.out.glyphs.len();
|
||||
let after_base = self.out.glyphs.last().is_some_and(|p| p.byte == g.byte && p.line == line && p.len > 0);
|
||||
// Baseline shift moves it across the column at any Character Rotation: the shift is
|
||||
// taken off before the turn and put back after it.
|
||||
let shift = pre * Point::new(0.0, -g.bshift) - pre * Point::ZERO;
|
||||
let (shift, unshift) = (Affine::translate(shift), Affine::translate(-shift));
|
||||
if let Some((_, _, turn, after)) = cluster.filter(|c| c.0 == g.byte && c.1 == next && after_base && g.len > 0) {
|
||||
// A later glyph of an upright glyph's cluster: set after it as the shaper placed
|
||||
// it, and turned with it.
|
||||
m = turn * Affine::translate((after - origin.x, 0.0)) * m;
|
||||
m = shift * turn * Affine::translate((after - origin.x, 0.0)) * unshift * m;
|
||||
self.upright_cluster = Some((g.byte, next + 1, turn, after));
|
||||
} else {
|
||||
// Turned about the centre of its cell, which the column's centre line runs through:
|
||||
@@ -342,7 +346,7 @@ impl Ctx<'_> {
|
||||
// turning point instead would move it across the column too).
|
||||
let turn = Affine::translate((-g.lead, 0.0))
|
||||
* Affine::rotate_about(-std::f64::consts::FRAC_PI_2, Point::new(origin.x + cell * 0.5, origin.y - upright_centre(g, cell, em)));
|
||||
m = turn * m;
|
||||
m = shift * turn * unshift * m;
|
||||
// Its own advance across, from where the cell centres it: where the shaper set the
|
||||
// next glyph of its cluster.
|
||||
let across = g.face.advance(g.gid) * g.sx;
|
||||
|
||||
@@ -343,6 +343,41 @@ fn tracking_spaces_upright_marks_without_moving_them_off_the_centre_line() {
|
||||
assert!((step - 24.0).abs() < 1e-6, "one em plus 200/1000 em of tracking: {step}");
|
||||
}
|
||||
|
||||
/// Baseline shift moves an upright glyph across the column, as it moves a letter lying on its
|
||||
/// side, at any Character Rotation, and leaves its neighbors where they are. The glyph turns about
|
||||
/// its shifted pen position (`xf * pivot`), and `xf * unturned` places the shifted glyph without
|
||||
/// its rotation.
|
||||
#[test]
|
||||
fn baseline_shift_moves_an_upright_glyph_across_the_column() {
|
||||
let at = |byte: usize, rotation: f64, shift: f64| {
|
||||
let mut t = vertical("§l§");
|
||||
edit::style_range(&mut t.runs, byte, byte + "§l§"[byte..].chars().next().map_or(1, char::len_utf8), |s| {
|
||||
s.rotation = rotation;
|
||||
s.baseline_shift = shift;
|
||||
});
|
||||
layout(FontDb::global(), &t)
|
||||
};
|
||||
let centre = |l: &TextLayout, byte: usize| l.glyphs.iter().find(|g| g.byte == byte).map(|g| g.outline.bounding_box().center()).unwrap();
|
||||
// The letter lying on its side moves across the column by the shift.
|
||||
let across = centre(&at(2, 0.0, 10.0), 2) - centre(&at(2, 0.0, 0.0), 2);
|
||||
assert!((across.x.abs() - 10.0).abs() < 1e-6 && across.y.abs() < 1e-6, "{across:?}");
|
||||
for rotation in [0.0, 30.0, 90.0, -90.0] {
|
||||
let (plain, shifted) = (at(3, rotation, 0.0), at(3, rotation, 10.0));
|
||||
let moved = centre(&shifted, 3) - centre(&plain, 3);
|
||||
assert!((moved - across).hypot() < 1e-6, "{rotation}°: {moved:?}, across {across:?}");
|
||||
for byte in [0, 2] {
|
||||
assert!((centre(&shifted, byte) - centre(&plain, byte)).hypot() < 1e-6, "{rotation}°: byte {byte} moved");
|
||||
}
|
||||
let (p, s) = (plain.glyphs.iter().find(|g| g.byte == 3).unwrap(), shifted.glyphs.iter().find(|g| g.byte == 3).unwrap());
|
||||
let turn_at = s.xf * s.pivot - p.xf * p.pivot;
|
||||
assert!((turn_at - across).hypot() < 1e-6, "{rotation}°: turns about a point moved by {turn_at:?}");
|
||||
let unrotated = at(3, 0.0, 10.0);
|
||||
let u = unrotated.glyphs.iter().find(|g| g.byte == 3).unwrap();
|
||||
let (a, b) = ((s.xf * s.unturned).as_coeffs(), u.xf.as_coeffs());
|
||||
assert!(a.iter().zip(b).all(|(a, b)| (a - b).abs() < 1e-6), "{rotation}°: {a:?} vs {b:?}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Character panel menu › Tate-chu-yoko: a marked run stands across one em of the column, including
|
||||
/// a number longer than three digits and Latin letters. Unmarked text keeps the automatic rule
|
||||
/// (two or three digits only). Horizontal type stores the mark and does not move the letters.
|
||||
|
||||
Reference in New Issue
Block a user