Fix Title Case restarting mid-word across styled runs in SVG export

Found while testing crates/svg/src/lib.rs:589 (also :604 and :611)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
NetterSilver
2026-10-21 00:31:03 +02:00
co-authored by Claude Haiku 5.5
parent d9c924c3ba
commit 59f3b3b733
2 changed files with 23 additions and 2 deletions
+4 -2
View File
@@ -586,7 +586,8 @@ impl W<'_> {
}
let end = glyphs.get(j).map_or(line.char_end, |g| g.char_start).max(start).min(chars.len());
let text: String = chars.get(start.min(end)..end).unwrap_or(&[]).iter().collect();
let text = st.case.apply(text.trim_end_matches(['\n', '\r']));
let prev = start.checked_sub(1).and_then(|k| chars.get(k)).copied();
let text = st.case.apply_after(text.trim_end_matches(['\n', '\r']), prev);
let own = match layout.styles.get(style).and_then(|s| s.fills.as_ref()) {
Some(fills) => {
let a = fills.iter().find_map(|p| self.paint_attr(p, n.width, n.height)).unwrap_or_else(|| "fill=\"none\"".into());
@@ -601,7 +602,8 @@ impl W<'_> {
continue;
}
let text: String = chars.get(line.char_start..line.char_end.min(chars.len())).unwrap_or(&[]).iter().collect();
let text = st.case.apply(text.trim_end_matches(['\n', '\r']));
let prev = line.char_start.checked_sub(1).and_then(|k| chars.get(k)).copied();
let text = st.case.apply_after(text.trim_end_matches(['\n', '\r']), prev);
let (x, y) = layout.glyphs.get(line.glyph_start).map_or((0.0, line.baseline), |g| (g.x, g.y));
let _ = write!(spans, "<tspan x=\"{}\" y=\"{}\">{}</tspan>", num(x), num(y), esc(&text));
}
+19
View File
@@ -116,6 +116,25 @@ fn text_run_fill_survives_live_text_export() {
assert!(svg.contains("fill=\"#00FF00\"") && svg.contains(">b</tspan>") && svg.contains(">a</tspan>"), "{svg}");
}
#[test]
fn title_case_does_not_restart_words_across_fill_runs_in_live_text() {
use figcraft_doc::{StyleRun, TextCase, TextData};
figcraft_text::scan_system_fonts();
let mut d = Document::new("t");
let page = d.first_page().unwrap();
let mut text = TextData::new("hello");
text.style.font_size = 24.0;
text.style.case = TextCase::Title;
let mut style = text.style.clone();
style.fills = Some(vec![Paint::solid(Color::from_hex("00FF00").unwrap())]);
text.runs.push(StyleRun { start: 2, end: 3, style });
text.normalize();
let id = d.create(page, NodeKind::Text(text), "Text", 0.0, 0.0, 200.0, 40.0).unwrap();
d.get_mut(id).unwrap().fills = vec![Paint::solid(Color::BLACK)];
let svg = export_svg(&d, &[id], &SvgOptions { outline_text: false, ..SvgOptions::default() }).unwrap();
assert!(svg.contains(">He</tspan>") && svg.contains(">l</tspan>") && svg.contains(">lo</tspan>"), "{svg}");
}
#[test]
fn point_joins_equal_to_the_layer_join_keep_a_plain_stroke() {
use figcraft_doc::{StrokeJoin, VectorNetwork};