SVG export: type with small capitals, superscript, subscript or tab leaders is written as outlines (#3869)

This commit is contained in:
bflatastic
2026-10-24 04:49:08 +07:00
parent 88884bcd01
commit 166afce360
2 changed files with 30 additions and 2 deletions
+24
View File
@@ -180,6 +180,30 @@ fn reopened_preserve_editing_svg_saves_as_the_full_document() {
let _ = std::fs::remove_dir_all(dir);
}
/// #3869: small capitals, superscript and subscript, and tab leaders, which SVG text can't draw,
/// are written as outlines (as vertical type and ruby are) rather than as plain text at full size;
/// type without them stays text.
#[test]
fn type_svg_text_cant_draw_is_written_as_outlines() {
let mut s = Session::new();
s.execute("file.new", &json!({"width": 500, "height": 200})).unwrap();
s.execute("text.create", &json!({"x": 50, "y": 100, "text": "Small caps x2 H2O", "size": 30})).unwrap();
let plain = svg(&mut s, json!({}));
assert!(plain.contains("<text"), "plain type stays text: {plain}");
for style in [json!({"smallCaps": true}), json!({"position": "superscript"}), json!({"position": "subscript"})] {
let mut t = Session::new();
t.execute("file.new", &json!({"width": 500, "height": 200})).unwrap();
let id = t.execute("text.create", &json!({"x": 50, "y": 100, "text": "Small caps x2 H2O", "size": 30})).unwrap()["id"].clone();
let mut p = style.clone();
p["id"] = id;
p["start"] = json!(0);
p["end"] = json!(5);
t.execute("text.setRangeStyle", &p).unwrap();
let out = svg(&mut t, json!({}));
assert!(!out.contains("<text") && out.contains("<path"), "{style}: {out}");
}
}
#[test]
fn save_writes_svg_and_remembers_its_options() {
let mut s = session(1);
+6 -2
View File
@@ -1863,8 +1863,12 @@ impl Writer<'_> {
let resolved = doc.inline_resolved(t);
let t = &*resolved;
let inline = t.runs.iter().any(|r| r.inline.is_some());
// Ruby is written as outlines with its base text: a reader's <text> can't place it.
let outlined = self.opts.outline_text || t.vertical || !t.ruby.is_empty() || (inline && matches!(t.kind, TextKind::OnPath { .. }));
// Ruby is written as outlines with its base text: a reader's <text> can't place it. So are
// small capitals, superscript and subscript and tab leaders, which SVG text doesn't draw (#3869).
let unwritable = t.runs.iter().any(|r| r.style.small_caps.is_some() || r.style.position != vectorcraft_doc::CharPosition::Normal)
|| std::iter::once(&t.para).chain(&t.paras).any(|p| p.tabs.iter().any(|tab| !tab.leader.is_empty()));
let outlined =
self.opts.outline_text || t.vertical || !t.ruby.is_empty() || (inline && matches!(t.kind, TextKind::OnPath { .. })) || unwritable;
let chars = |w: &mut Self, n: &Node| if outlined { w.text_outlines(n, t) } else { w.text(n, t) };
let painted = n.appearance.items.iter().any(|i| i.visible() && !i.paint().is_none());
// Live type with an inside or outside character stroke writes that stroke apart from the