mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-10 06:31:19 +00:00
Glyphs: the Glyphs query reports the fallback glyph of an overflowing character
When the requested font has no glyph for a character, the text draws it from a fallback face, and text.glyphAlternates reports that glyph. For a character that overflows its area, the query shaped the character alone but kept only a glyph of the requested face, so it reported the requested face's default glyph (0). It now takes the first glyph drawn for the character when the requested face draws none, as it does for laid-out text.
This commit is contained in:
@@ -1203,8 +1203,9 @@ fn own_glyph(
|
||||
/// The glyph of `face` drawn for the character `ch` at `bytes` of `t` when the character is shaped
|
||||
/// alone in the writing direction of `t` and the direction its paragraph sets it in, with the rest
|
||||
/// of its grapheme in the grapheme's own styles: the glyph of a character that is not laid out
|
||||
/// (overflowing text).
|
||||
fn shaped_alone(t: &TextObject, ch: char, bytes: &std::ops::Range<usize>, face: &vectorcraft_text::FontFace) -> Option<u32> {
|
||||
/// (overflowing text). Also the first glyph drawn for the character, which can be in a fallback
|
||||
/// face.
|
||||
fn shaped_alone(t: &TextObject, ch: char, bytes: &std::ops::Range<usize>, face: &vectorcraft_text::FontFace) -> (Option<u32>, Option<u32>) {
|
||||
use vectorcraft_doc::ParaDirection;
|
||||
let plain = t.plain_text();
|
||||
let end = edit::next_cluster(&plain, bytes.start).max(bytes.end);
|
||||
@@ -1218,7 +1219,8 @@ fn shaped_alone(t: &TextObject, ch: char, bytes: &std::ops::Range<usize>, face:
|
||||
let rtl = vectorcraft_text::char_is_rtl(plain.get(start..stop).unwrap_or_default(), t.para_at(paragraph).direction, bytes.start - start);
|
||||
single.para.direction = Some(if rtl { ParaDirection::RightToLeft } else { ParaDirection::LeftToRight });
|
||||
let lay = vectorcraft_text::layout(vectorcraft_text::FontDb::global(), &single);
|
||||
own_glyph(&single.plain_text(), &lay, ch, &(0..bytes.len()), face).flatten()
|
||||
let first = lay.glyphs.iter().find(|g| g.byte == 0 && g.len > 0).map(|g| g.gid);
|
||||
(own_glyph(&single.plain_text(), &lay, ch, &(0..bytes.len()), face).flatten(), first)
|
||||
}
|
||||
|
||||
fn set_glyph_alternate(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
@@ -1253,7 +1255,7 @@ fn set_glyph_alternate(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let mut probe = t.clone();
|
||||
edit::style_range(&mut probe.runs, bytes.start, bytes.end, |st| st.glyph_alternate = None);
|
||||
let lay = vectorcraft_text::layout(vectorcraft_text::FontDb::global(), &probe);
|
||||
own_glyph(&plain, &lay, ch, &bytes, &face).unwrap_or_else(|| shaped_alone(&probe, ch, &bytes, &face))
|
||||
own_glyph(&plain, &lay, ch, &bytes, &face).unwrap_or_else(|| shaped_alone(&probe, ch, &bytes, &face).0)
|
||||
});
|
||||
let substituted = unchosen.is_some_and(|g| g != default);
|
||||
let choice = match gid.filter(|g| *g != default || substituted) {
|
||||
@@ -1304,10 +1306,14 @@ fn glyph_alternates(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let lay = vectorcraft_text::layout(vectorcraft_text::FontDb::global(), &t);
|
||||
let first = lay.glyphs.iter().find(|g| g.byte <= bytes.start && bytes.start < g.byte.saturating_add(g.len)).map(|g| g.gid);
|
||||
// A character that is not laid out (overflowing text) reports the glyph it is drawn with when
|
||||
// it is shaped alone, as `text.setGlyphAlternate` finds it.
|
||||
// it is shaped alone, as `text.setGlyphAlternate` finds it; a fallback face's glyph when it is
|
||||
// drawn in one.
|
||||
let chosen = match own_glyph(&t.plain_text(), &lay, ch, &bytes, &face) {
|
||||
Some(own) => own.or(first),
|
||||
None => shaped_alone(&t, ch, &bytes, &face),
|
||||
None => {
|
||||
let (own, first) = shaped_alone(&t, ch, &bytes, &face);
|
||||
own.or(first)
|
||||
}
|
||||
}
|
||||
.unwrap_or(default);
|
||||
let alternates: Vec<Value> = face.alternates(ch).iter().map(|a| json!({"gid": a.gid, "feature": String::from_utf8_lossy(&a.feature)})).collect();
|
||||
|
||||
@@ -2793,4 +2793,10 @@ fn glyph_alternates_query_reports_the_fallback_glyph_that_is_drawn() {
|
||||
let query = s.execute("text.glyphAlternates", &json!({"id": id.0, "start": 0})).unwrap();
|
||||
assert_eq!(query["chosen"], json!(glyph.gid), "the query reports the visible fallback glyph");
|
||||
assert!(query["alternates"].as_array().unwrap().is_empty());
|
||||
|
||||
let area = json!({"x": 0, "y": 300, "text": ch.to_string(), "font": alternates_family(true), "area": {"width": 200, "height": 2}});
|
||||
let boxed = NodeId(s.execute("text.create", &area).unwrap()["id"].as_u64().unwrap());
|
||||
assert!(vectorcraft_text::layout(db, &obj(&s, boxed)).glyphs.is_empty(), "the text overflows");
|
||||
let query = s.execute("text.glyphAlternates", &json!({"id": boxed.0, "start": 0})).unwrap();
|
||||
assert_eq!(query["chosen"], json!(glyph.gid), "an overflowing character reports the fallback glyph it is drawn with");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user