mirror of
https://github.com/storytold/wordcraft.git
synced 2026-10-28 12:27:11 -06:00
Merge PR #2331: fix(mathml): support linethickness=none and menclose notations
crates/formats/src/mathml.rs: menclose keeps main's box-with-strikes reading (the writer emits "box horizontalstrike") and adds the PR's radical, actuarial, cross and case-insensitive notations.
This commit is contained in:
@@ -578,8 +578,11 @@ fn elem(e: &El, depth: usize, budget: &mut usize) -> Arg {
|
||||
vec![MNode::Run(MRun { text: t, nor: true, ..Default::default() })]
|
||||
}
|
||||
"mfrac" => {
|
||||
let thin = e.attr("linethickness").is_some_and(|t| t.trim().trim_end_matches(|c: char| c.is_alphabetic()).trim() == "0");
|
||||
let kind = if e.attr("bevelled") == Some("true") {
|
||||
let thin = e.attr("linethickness").is_some_and(|t| {
|
||||
let s = t.trim();
|
||||
s.eq_ignore_ascii_case("none") || s.trim_end_matches(|c: char| c.is_alphabetic() || c == '%').trim() == "0"
|
||||
});
|
||||
let kind = if e.attr("bevelled").is_some_and(|b| b.eq_ignore_ascii_case("true")) {
|
||||
FracKind::Skewed
|
||||
} else if thin {
|
||||
FracKind::NoBar
|
||||
@@ -743,19 +746,29 @@ fn elem(e: &El, depth: usize, budget: &mut usize) -> Arg {
|
||||
"mphantom" => vec![MNode::Phant { show: false, zero_wid: false, zero_asc: false, zero_desc: false, e: children(e, d, budget) }],
|
||||
"menclose" => {
|
||||
let n = e.attr("notation").unwrap_or("longdiv");
|
||||
let has = |k: &str| n.split_whitespace().any(|w| w == k);
|
||||
let has = |k: &str| n.split_whitespace().any(|w| w.eq_ignore_ascii_case(k));
|
||||
if has("radical") {
|
||||
return vec![MNode::Rad { deg: Vec::new(), deg_hide: true, e: children(e, d, budget) }];
|
||||
}
|
||||
// An actuarial symbol is the top and right edges.
|
||||
let actuarial = has("actuarial");
|
||||
let is_box = has("box") || has("roundedbox") || has("circle");
|
||||
let has_border = is_box || has("top") || has("bottom") || has("left") || has("right");
|
||||
let has_strike = has("horizontalstrike") || has("verticalstrike") || has("updiagonalstrike") || has("downdiagonalstrike");
|
||||
if has_border || has_strike {
|
||||
let (top, right) = (has("top") || actuarial, has("right") || actuarial);
|
||||
let has_border = is_box || top || has("bottom") || has("left") || right;
|
||||
let strike = [
|
||||
has("horizontalstrike"),
|
||||
has("verticalstrike"),
|
||||
has("updiagonalstrike") || has("cross"),
|
||||
has("downdiagonalstrike") || has("cross"),
|
||||
];
|
||||
if has_border || strike.iter().any(|&s| s) {
|
||||
let hide = if is_box {
|
||||
[false; 4]
|
||||
} else if has_border {
|
||||
[!has("top"), !has("bottom"), !has("left"), !has("right")]
|
||||
[!top, !has("bottom"), !has("left"), !right]
|
||||
} else {
|
||||
[true; 4]
|
||||
};
|
||||
let strike = [has("horizontalstrike"), has("verticalstrike"), has("updiagonalstrike"), has("downdiagonalstrike")];
|
||||
return vec![MNode::BorderBox { hide, strike, e: children(e, d, budget) }];
|
||||
}
|
||||
children(e, d, budget)
|
||||
@@ -985,4 +998,46 @@ mod tests {
|
||||
let m = to_mathml(&n, false, "z");
|
||||
assert!(m.ends_with("</math>"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mfrac_linethickness_none_and_menclose_notations() {
|
||||
let el_frac = El {
|
||||
name: "mfrac".into(),
|
||||
attrs: vec![("linethickness".into(), "none".into())],
|
||||
children: vec![
|
||||
Item::El(El { name: "mi".into(), attrs: vec![], children: vec![Item::Text("n".into())] }),
|
||||
Item::El(El { name: "mi".into(), attrs: vec![], children: vec![Item::Text("k".into())] }),
|
||||
],
|
||||
};
|
||||
let mut budget = MAX_NODES;
|
||||
let tree_frac = elem(&el_frac, 0, &mut budget);
|
||||
assert!(matches!(tree_frac.as_slice(), [MNode::Frac { kind: FracKind::NoBar, .. }]), "{tree_frac:?}");
|
||||
|
||||
let el_strike = El {
|
||||
name: "menclose".into(),
|
||||
attrs: vec![("notation".into(), "horizontalstrike".into())],
|
||||
children: vec![Item::El(El { name: "mi".into(), attrs: vec![], children: vec![Item::Text("x".into())] })],
|
||||
};
|
||||
let mut budget = MAX_NODES;
|
||||
let tree_strike = elem(&el_strike, 0, &mut budget);
|
||||
assert!(matches!(tree_strike.as_slice(), [MNode::BorderBox { strike: [true, false, false, false], .. }]), "{tree_strike:?}");
|
||||
|
||||
let el_cross = El {
|
||||
name: "menclose".into(),
|
||||
attrs: vec![("notation".into(), "cross".into())],
|
||||
children: vec![Item::El(El { name: "mi".into(), attrs: vec![], children: vec![Item::Text("x".into())] })],
|
||||
};
|
||||
let mut budget = MAX_NODES;
|
||||
let tree_cross = elem(&el_cross, 0, &mut budget);
|
||||
assert!(matches!(tree_cross.as_slice(), [MNode::BorderBox { strike: [false, false, true, true], .. }]), "{tree_cross:?}");
|
||||
|
||||
let el_rad = El {
|
||||
name: "menclose".into(),
|
||||
attrs: vec![("notation".into(), "radical".into())],
|
||||
children: vec![Item::El(El { name: "mi".into(), attrs: vec![], children: vec![Item::Text("x".into())] })],
|
||||
};
|
||||
let mut budget = MAX_NODES;
|
||||
let tree_rad = elem(&el_rad, 0, &mut budget);
|
||||
assert!(matches!(tree_rad.as_slice(), [MNode::Rad { deg_hide: true, .. }]), "{tree_rad:?}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user