mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-11 06:01:18 +00:00
Merge pull request #5468 from dotCooCoo/fix/vertical-path-upright-hit
Type: the stop inside an upright ligature on a vertical path follows its drawn cell
This commit is contained in:
@@ -382,6 +382,21 @@ impl Ctx<'_> {
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
// On a path, an upright glyph turns about its pen: its cell runs along the path between
|
||||
// where the glyph's transform sets the cell's foot and its top, moved by the shaper's
|
||||
// placement and a baseline shift, and slanted by the path's effect.
|
||||
let (width, width_start) = match (upright && self.on_path, g.sy) {
|
||||
(true, sy) if sy != 0.0 => {
|
||||
// In font units, with the Proportional Metrics change to its length.
|
||||
let (cell, top) = upright_extent(g);
|
||||
let proportional = g.vpal.filter(|_| g.face.vertical_glyph(g.gid).is_some()).map_or(0.0, |a| a.advance) / sy;
|
||||
let tangent = Vec2::new(angle.cos(), angle.sin());
|
||||
let along = |y: f64| (m * Point::new(0.0, y) - origin).dot(tangent);
|
||||
let (start, end) = (along(cell + proportional - top), along(-top));
|
||||
if start.is_finite() && end > start { (end - start, start) } else { (width, width_start) }
|
||||
}
|
||||
_ => (width, width_start),
|
||||
};
|
||||
if let Some(art) = g.inline.as_ref().and_then(|ib| ib.art) {
|
||||
self.out.inlines.push(InlineGlyph { run: g.run, byte: g.byte, glyph: self.out.glyphs.len(), xf: m, bounds: m.transform_rect_bbox(art) });
|
||||
}
|
||||
|
||||
@@ -166,7 +166,9 @@ pub struct PositionedGlyph {
|
||||
/// in text space: the font's placement of the glyph (`GPOS` x placement), less a leading space
|
||||
/// taken off, plus the manual kerning of right-to-left text, which moves the glyph right so that
|
||||
/// the space it adds lies on the glyph's left. 0 for a glyph standing upright or set across the
|
||||
/// column in vertical type, and for an inline graphic.
|
||||
/// column in vertical type, and for an inline graphic. An upright glyph in vertical type on a
|
||||
/// path turns about its pen, and its cell starts one cell length before the cell's top: 0.12 em
|
||||
/// before the pen without the font's vertical metrics.
|
||||
pub width_start: f64,
|
||||
}
|
||||
|
||||
|
||||
@@ -459,6 +459,66 @@ fn an_upright_ligature_takes_its_caret_down_its_cell() {
|
||||
assert!((at(900) - 0.5).abs() < 1e-9, "{}", at(900));
|
||||
}
|
||||
|
||||
/// On a path, an upright ligature in vertical type turns about its pen, so its cell runs from
|
||||
/// 0.12 em before the pen to 0.88 em after it, with or without the font's vertical metrics. The
|
||||
/// inner stop lies at the middle of that cell, and the first character's selection ends there.
|
||||
#[test]
|
||||
fn an_upright_ligature_on_a_path_takes_its_stop_in_its_turned_cell() {
|
||||
for (font, family) in [(ligature_caret_font(None), LIGATURE_CARET_FAMILY), (vertical_ligature_caret_font(900), VERTICAL_LIGATURE_CARET_FAMILY)] {
|
||||
let db = db_with(font);
|
||||
let mut line = BezPath::new();
|
||||
line.move_to((0.0, 0.0));
|
||||
line.line_to((1000.0, 0.0));
|
||||
let mut t = point("fi", family);
|
||||
t.vertical = true;
|
||||
t.runs[0].style.standard_vertical_roman = true;
|
||||
t.kind = TextKind::OnPath { path: PathData::from_bezpath(&line), start: 0.0, end: None };
|
||||
let l = layout(&db, &t);
|
||||
assert!(l.on_path);
|
||||
let Some(g) = l.glyphs.iter().find(|g| g.len == 2) else { panic!("f + i is one glyph: {:?}", l.glyphs) };
|
||||
assert_eq!(g.carets, vec![(1, 0.5)], "{family}");
|
||||
assert!((g.width - 100.0).abs() < 1e-9 && (g.width_start + 12.0).abs() < 1e-9, "{family}: {} {}", g.width, g.width_start);
|
||||
let x = |b| caret_position(&l, b).0.x - g.origin.x;
|
||||
assert!((x(1) - 38.0).abs() < 1e-6, "{family}: {}", x(1));
|
||||
let q = selection_quads(&l, 0, 1);
|
||||
assert_eq!(q.len(), 1, "{q:?}");
|
||||
assert!(((q[0][1] - q[0][0]).hypot() - 38.0).abs() < 1e-6, "{family}: {q:?}");
|
||||
// A baseline shift moves the turned glyph along the path, and its stop with it.
|
||||
let ink = g.outline.bounding_box().x0;
|
||||
t.runs[0].style.baseline_shift = 10.0;
|
||||
let l = layout(&db, &t);
|
||||
let Some(s) = l.glyphs.iter().find(|g| g.len == 2) else { panic!("f + i is one glyph: {:?}", l.glyphs) };
|
||||
let moved = s.outline.bounding_box().x0 - ink;
|
||||
assert!(moved.abs() > 1.0, "{family}: the shift moves the glyph along the path: {moved}");
|
||||
let stop = caret_position(&l, 1).0.x - s.origin.x;
|
||||
assert!((stop - 38.0 - moved).abs() < 1e-6, "{family}: {stop} against {}", 38.0 + moved);
|
||||
// Horizontal Scale widens the glyph across the path; its stop stays inside the glyph.
|
||||
t.runs[0].style.baseline_shift = 0.0;
|
||||
t.runs[0].style.h_scale = 200.0;
|
||||
let l = layout(&db, &t);
|
||||
let Some(s) = l.glyphs.iter().find(|g| g.len == 2) else { panic!("f + i is one glyph: {:?}", l.glyphs) };
|
||||
let bounds = s.outline.bounding_box();
|
||||
let stop = caret_position(&l, 1).0.x - s.origin.x;
|
||||
assert!(bounds.x0 < stop && stop < bounds.x1 && stop > 0.0, "{family}: {stop} outside {bounds:?}");
|
||||
t.runs[0].style.h_scale = 100.0;
|
||||
// Skew on a slanted path: the stop stays inside the drawn glyph along the path.
|
||||
let mut slant = BezPath::new();
|
||||
slant.move_to((0.0, 0.0));
|
||||
slant.line_to((1000.0, 1000.0));
|
||||
t.runs[0].style.baseline_shift = 0.0;
|
||||
t.kind = TextKind::OnPath { path: PathData::from_bezpath(&slant), start: 0.0, end: None };
|
||||
t.path_effect = vectorcraft_doc::PathEffect::Skew;
|
||||
let l = layout(&db, &t);
|
||||
let Some(s) = l.glyphs.iter().find(|g| g.len == 2) else { panic!("f + i is one glyph: {:?}", l.glyphs) };
|
||||
let tangent = Vec2::new(s.angle.cos(), s.angle.sin());
|
||||
let ink = s.outline.elements().iter().filter_map(|el| el.end_point()).map(|p| (p - s.origin).dot(tangent));
|
||||
let (lo, hi) = ink.fold((f64::INFINITY, f64::NEG_INFINITY), |(lo, hi), d| (lo.min(d), hi.max(d)));
|
||||
let (a, b) = caret_position(&l, 1);
|
||||
let stop = (a.midpoint(b) - s.origin).dot(tangent);
|
||||
assert!(lo < stop && stop < hi, "{family}: {stop} outside {lo}..{hi}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Tracking that makes an upright ligature's advance negative leaves it upright: it keeps its
|
||||
/// caret down its cell, and its cell starts where it does without tracking, with or without the
|
||||
/// font's vertical metrics.
|
||||
|
||||
Reference in New Issue
Block a user