mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-11 02:19:56 -06:00
Type: guard Touch Type movement at direction boundaries
This commit is contained in:
@@ -48,7 +48,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Touch Type",
|
||||
[],
|
||||
None,
|
||||
"{id, start: byte (a character of it), dx?: pt, dy?: pt, hScale?: %, vScale?: %, rotation?: deg} change one character (a ligature, one glyph for several characters, as a whole) as a Touch Type tool drag does, as one undo step: dx moves it along its line in reading order by changing the kerning of the character before it on the line and its own kerning, and the characters after it stay where they are (an error for the first logical character of a line; supported in either reading direction; limited to what kerning from -1000 to 10000 can give); dy raises it (baseline shift, up from its line whatever its rotation, also for the first character of a line and a right-to-left character); dx and dy are document points; hScale (including the object transform), vScale and rotation set its scales and rotation as text.setRangeStyle does. The kerning of the two characters also keeps the character and the ones after it where they were along the line when the font kerned them with their neighbors; a wider or narrower character moves the ones after it by the change in its width → {id, start, end} (the character's bytes)",
|
||||
"{id, start: byte (a character of it), dx?: pt, dy?: pt, hScale?: %, vScale?: %, rotation?: deg} change one character (a ligature, one glyph for several characters, as a whole) as a Touch Type tool drag does, as one undo step: dx moves it along its line in reading order by changing the kerning of the character before it on the line and its own kerning, and the characters after it stay where they are (an error for the first logical character of a line or at a reading-direction boundary; supported in either reading direction; limited to what kerning from -1000 to 10000 can give); dy raises it (baseline shift, up from its line whatever its rotation, also for the first character of a line and a right-to-left character); dx and dy are document points; hScale (including the object transform), vScale and rotation set its scales and rotation as text.setRangeStyle does. The kerning of the two characters also keeps the character and the ones after it where they were along the line when the font kerned them with their neighbors; a wider or narrower character moves the ones after it by the change in its width → {id, start, end} (the character's bytes)",
|
||||
has_doc,
|
||||
touch_type
|
||||
),
|
||||
@@ -1018,7 +1018,7 @@ fn touch_type(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let doc_len = |v: Vec2| linear(t.xf, v).hypot() / v.hypot();
|
||||
let limit = 1296.0 / points;
|
||||
// A list marker's glyphs (length 0) stand for no character.
|
||||
let prev = lay.glyphs.iter().find(|h| h.line == g.line && h.len > 0 && h.byte + h.len == a);
|
||||
let prev = lay.glyphs.iter().find(|h| h.line == g.line && h.len > 0 && h.byte + h.len == a && h.rtl == g.rtl);
|
||||
let pr = prev.map(|h| (h.byte, h.byte + h.len));
|
||||
let db = vectorcraft_text::FontDb::global();
|
||||
let em = glyph_em(db, &t, g).ok_or_else(|| bad(C, "the character's size can't be measured"))?;
|
||||
|
||||
@@ -627,6 +627,33 @@ fn touch_type_moves_one_character_in_centered_and_right_aligned_lines() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn touch_type_centered_ltr_scale_keeps_the_expected_width_change() {
|
||||
let mut s = session();
|
||||
let id = NodeId(
|
||||
s.execute("text.create", &json!({"x": 200, "y": 100, "text": "Type", "size": 40, "justify": "center"})).unwrap()["id"].as_u64().unwrap(),
|
||||
);
|
||||
let before = glyph_pens(&s, id);
|
||||
s.execute("text.touchType", &json!({"id": id.0, "start": 1, "hScale": 150})).unwrap();
|
||||
let after = glyph_pens(&s, id);
|
||||
let previous = moved_by(&before, &after, 0).0;
|
||||
let following = moved_by(&before, &after, 2).0;
|
||||
assert!(previous < -1.0, "centered line shifts left: {previous}");
|
||||
assert!(following > 1.0, "the wider character shifts following text right: {following}");
|
||||
assert!((following + previous).abs() < 0.1, "width growth is shared around the center: {previous}, {following}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn touch_type_refuses_horizontal_move_across_a_direction_boundary() {
|
||||
let mut s = session();
|
||||
let id = text(&mut s, "abc אבג");
|
||||
let before = obj(&s, id);
|
||||
let result = s.execute("text.touchType", &json!({"id": id.0, "start": 4, "dx": 5}));
|
||||
assert!(result.is_err(), "a mixed-direction boundary has no safe preceding glyph for kerning");
|
||||
assert_eq!(obj(&s, id), before, "a refused move leaves the text unchanged");
|
||||
s.execute("text.touchType", &json!({"id": id.0, "start": 4, "dy": 3})).unwrap();
|
||||
}
|
||||
|
||||
/// `dy` moves a rotated character that many document points up from its line, also in type
|
||||
/// scaled unevenly.
|
||||
#[test]
|
||||
|
||||
@@ -184,7 +184,7 @@ fn char_box(t: &TextObject, lay: &TextLayout, gi: usize) -> Option<CharBox> {
|
||||
up: unit(linear(m * g.unturned, Vec2::new(0.0, -1.0))),
|
||||
ex,
|
||||
ey,
|
||||
kerned: lay.glyphs.iter().any(|h| h.line == g.line && h.len > 0 && h.byte + h.len == g.byte),
|
||||
kerned: lay.glyphs.iter().any(|h| h.line == g.line && h.len > 0 && h.byte + h.len == g.byte && h.rtl == g.rtl),
|
||||
style: t.effective_char_style(&style)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -417,3 +417,15 @@ fn touch_type_rtl_middle_character_can_drag_and_nudge_along_its_line() {
|
||||
tool.target = Some((id, 0));
|
||||
assert!(tool.key(&cx, ToolKey::Left, Mods::default()).is_empty(), "the first logical character has no preceding character to kern");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn touch_type_direction_boundary_offers_only_vertical_movement() {
|
||||
let text = TextObject::point(Point::new(100.0, 200.0), "abc אבג", CharStyle { size: 40.0, ..Default::default() });
|
||||
let lay = vectorcraft_text::layout(FontDb::global(), &text);
|
||||
let at = lay.glyphs.iter().position(|g| g.byte == 4).unwrap();
|
||||
let b = char_box(&text, &lay, at).unwrap();
|
||||
assert!(!b.kerned, "the previous logical character has a different reading direction");
|
||||
let params = TouchTypeTool::nudge_params(NodeId(1), &text, &b, -5.0, -3.0);
|
||||
assert!(params.get("dx").is_none(), "{params}");
|
||||
assert!(params.get("dy").is_some(), "{params}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user