Compare commits
1 Commits
main
...
fix/sym-fo
| Author | SHA1 | Date |
|---|---|---|
|
|
670befa919 |
|
|
@ -201,6 +201,9 @@ struct Style {
|
||||||
/// The `a:ea` and `a:cs` families (theme references resolved), if the document names one.
|
/// The `a:ea` and `a:cs` families (theme references resolved), if the document names one.
|
||||||
ea: Option<String>,
|
ea: Option<String>,
|
||||||
cs: Option<String>,
|
cs: Option<String>,
|
||||||
|
/// The `a:sym` family: private-use characters (U+F000–U+F0FF, as PowerPoint stores symbol-font
|
||||||
|
/// characters) are drawn in it.
|
||||||
|
sym: Option<String>,
|
||||||
/// Heading font (`+mj-…`): the theme's major font set applies.
|
/// Heading font (`+mj-…`): the theme's major font set applies.
|
||||||
major: bool,
|
major: bool,
|
||||||
/// Pair kerning on: `a:rPr/@kern` is set and the size reaches it.
|
/// Pair kerning on: `a:rPr/@kern` is set and the size reaches it.
|
||||||
|
|
@ -315,6 +318,7 @@ fn make_style(ctx: &Ctx, r: &RunProps, scale: f64, prompt: Option<Rgba>) -> Styl
|
||||||
lang: r.lang.clone().filter(|l| !l.is_empty()),
|
lang: r.lang.clone().filter(|l| !l.is_empty()),
|
||||||
ea,
|
ea,
|
||||||
cs,
|
cs,
|
||||||
|
sym: r.font_sym.as_deref().map(|f| ctx.master.theme.font(f)).filter(|f| !f.trim().is_empty()),
|
||||||
major,
|
major,
|
||||||
kern: r.kern.is_some_and(|k| k > 0.0 && size + 1e-6 >= k * scale),
|
kern: r.kern.is_some_and(|k| k > 0.0 && size + 1e-6 >= k * scale),
|
||||||
}
|
}
|
||||||
|
|
@ -588,9 +592,18 @@ fn shape_cells(ctx: &Ctx, text: &str, char_style: &[usize], styles: &[Style], fa
|
||||||
// Emoji, and pictographs asked to look like text (U+FE0E), pick their own face.
|
// Emoji, and pictographs asked to look like text (U+FE0E), pick their own face.
|
||||||
let neutral =
|
let neutral =
|
||||||
cluster.iter().all(|c| script::script_of(*c).is_none()) && !deckcraft_fonts::is_emoji_cluster(cluster) && !cluster.contains(&'\u{FE0E}');
|
cluster.iter().all(|c| script::script_of(*c).is_none()) && !deckcraft_fonts::is_emoji_cluster(cluster) && !cluster.contains(&'\u{FE0E}');
|
||||||
let face = match prev.as_ref() {
|
// A symbol-font character (U+F0xx) with an `a:sym` font draws in that font when it's there.
|
||||||
Some(p) if neutral && deckcraft_fonts::covers_cluster(p, cluster) && char_style.get(a.wrapping_sub(1)) == Some(&si) => p.clone(),
|
let sym_face = st
|
||||||
_ => {
|
.sym
|
||||||
|
.as_deref()
|
||||||
|
.filter(|_| cluster.first().is_some_and(|c| ('\u{F000}'..='\u{F0FF}').contains(c)))
|
||||||
|
.filter(|f| db.has_family(f))
|
||||||
|
.map(|f| db.face(f, st.style_name))
|
||||||
|
.filter(|f| deckcraft_fonts::covers_cluster(f, cluster));
|
||||||
|
let face = match (sym_face, prev.as_ref()) {
|
||||||
|
(Some(f), _) => f,
|
||||||
|
(None, Some(p)) if neutral && deckcraft_fonts::covers_cluster(p, cluster) && char_style.get(a.wrapping_sub(1)) == Some(&si) => p.clone(),
|
||||||
|
(None, _) => {
|
||||||
let chain = chains.entry((si, slot, sc)).or_insert_with(|| face_chain(ctx, st, slot, sc));
|
let chain = chains.entry((si, slot, sc)).or_insert_with(|| face_chain(ctx, st, slot, sc));
|
||||||
db.cascade(cluster, chain, st.style_name)
|
db.cascade(cluster, chain, st.style_name)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -759,3 +759,25 @@ fn symbol_char_maps_symbol_font_codes() {
|
||||||
assert_eq!(symbol_char(&face, "Wingdings", '\u{F06E}'), Some('\u{25A0}'));
|
assert_eq!(symbol_char(&face, "Wingdings", '\u{F06E}'), Some('\u{25A0}'));
|
||||||
assert_eq!(symbol_char(&face, "Wingdings", '\u{100}'), None, "not a symbol code");
|
assert_eq!(symbol_char(&face, "Wingdings", '\u{100}'), None, "not a symbol code");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn symbol_font_characters_draw_in_the_sym_font() {
|
||||||
|
// PowerPoint stores a Symbol-font character as U+F0xx with `<a:sym typeface="Symbol"/>`:
|
||||||
|
// U+F0DE is the Symbol font's double arrow (⇒). It must come from that font, not from
|
||||||
|
// whichever installed font happens to map U+F0DE (Wingdings draws an arrow at another angle).
|
||||||
|
let symbol = "Symbol";
|
||||||
|
if !FontDb::global().has_family(symbol) {
|
||||||
|
eprintln!("skipped: no Symbol font installed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let (p, mut sh) = setup("x\u{F0DE}y", 600.0, 300.0);
|
||||||
|
let para = &mut sh.text.as_mut().unwrap().paragraphs[0];
|
||||||
|
para.props.bullet = Some(Bullet::None);
|
||||||
|
para.runs[0].props.font = Some("Arial".into());
|
||||||
|
para.runs[0].props.font_sym = Some(symbol.into());
|
||||||
|
let l = lay(&p, &sh, 600.0, 300.0);
|
||||||
|
let families: Vec<(String, (usize, usize))> = l.runs.iter().map(|r| (r.face.family.clone(), r.chars)).collect();
|
||||||
|
assert!(families.iter().any(|(f, c)| f.eq_ignore_ascii_case(symbol) && c.0 == 1), "{families:?}");
|
||||||
|
// The letters around it keep the run's font.
|
||||||
|
assert!(families.iter().all(|(f, c)| c.0 == 1 || !f.eq_ignore_ascii_case(symbol)), "{families:?}");
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue