mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-24 02:16:56 -06:00
Merge #1142: Write a final Greek sigma in lower-case text layout (@Silvertree2010)
This commit is contained in:
@@ -1035,6 +1035,18 @@ fn is_emoji(c: char) -> bool {
|
||||
matches!(c, '\u{1F1E6}'..='\u{1F1FF}' | '\u{1F300}'..='\u{1FAFF}')
|
||||
}
|
||||
|
||||
/// The lower case of the capital sigma at `i`: the final form ς or σ, chosen from the letters
|
||||
/// around it in the whole text (as lower-casing the whole string would), not from the lone char.
|
||||
fn lower_sigma(chars: &[char], i: usize) -> char {
|
||||
// Enough context to step over any realistic run of combining marks next to the sigma.
|
||||
const CONTEXT: usize = 32;
|
||||
let before: String = chars.get(i.saturating_sub(CONTEXT)..i).unwrap_or(&[]).iter().collect();
|
||||
let after: String = chars.iter().skip(i + 1).take(CONTEXT).collect();
|
||||
let lower = format!("{before}\u{3A3}{after}").to_lowercase();
|
||||
// Only the sigma's lower case depends on context, so the text before it keeps its length.
|
||||
lower.get(before.to_lowercase().len()..).and_then(|s| s.chars().next()).unwrap_or('σ')
|
||||
}
|
||||
|
||||
/// One run of chars shaped with a single face and style.
|
||||
struct Segment<'a> {
|
||||
start: usize,
|
||||
@@ -1065,6 +1077,8 @@ fn shape_segment(chars: &[char], seg: &Segment, face: &Arc<Face>, features: &[Fe
|
||||
for u in c.to_uppercase() {
|
||||
mapped.push((u, src, true));
|
||||
}
|
||||
} else if style.case == TextCase::Lower && *c == 'Σ' {
|
||||
mapped.push((lower_sigma(chars, src), src, false));
|
||||
} else {
|
||||
let mut buf = [0u8; 4];
|
||||
for u in style.case.apply_after(c.encode_utf8(&mut buf), prev).chars() {
|
||||
|
||||
@@ -497,3 +497,25 @@ fn the_caret_after_a_line_break_sits_on_the_next_line() {
|
||||
let l = layout(&TextData::new("ab\n"), None, None);
|
||||
assert_eq!(l.caret(3), (0.0, 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lower_case_writes_final_sigma() {
|
||||
if !have_fonts() {
|
||||
return;
|
||||
}
|
||||
let gids = |t: &TextData| layout(t, None, None).glyphs.iter().map(|g| (g.gid, g.char_start)).collect::<Vec<_>>();
|
||||
let want = gids(&TextData::new("οδος ας"));
|
||||
let Some(face) = layout(&TextData::new("ς"), None, None).glyphs.first().map(|g| g.face.clone()) else { return };
|
||||
if face.glyph_for('σ') == face.glyph_for('ς') {
|
||||
eprintln!("face has no distinct final sigma; skipping");
|
||||
return;
|
||||
}
|
||||
let mut t = TextData::new("ΟΔΟΣ ΑΣ");
|
||||
t.style.case = figcraft_doc::TextCase::Lower;
|
||||
assert_eq!(gids(&t), want, "one run");
|
||||
// The word split across two styled runs: the final form is still chosen from the whole text.
|
||||
let mut other = t.style.clone();
|
||||
other.font_size += 4.0;
|
||||
t.runs.push(figcraft_doc::StyleRun { start: 3, end: 4, style: other });
|
||||
assert_eq!(gids(&t).iter().map(|g| g.0).collect::<Vec<_>>(), want.iter().map(|g| g.0).collect::<Vec<_>>(), "split runs");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user