mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 17:56:50 -06:00
Add a generic family to the font-family of SVG export and CSS (#3135)
This commit is contained in:
committed by
bflatastic
parent
7c47692b32
commit
0f855be77f
@@ -48,7 +48,7 @@ fn type_gives_font_properties_and_gradients_css_gradients() {
|
||||
let mut s = session();
|
||||
let t = run(&mut s, "text.create", json!({"x": 20, "y": 50, "text": "Title", "size": 24}))["id"].as_u64().unwrap();
|
||||
let text = css(&run(&mut s, "css.selection", json!({"ids": [t]}))).to_string();
|
||||
for decl in ["font-family: 'Source Sans 3';", "font-size: 24px;", "color: #"] {
|
||||
for decl in ["font-family: 'Source Sans 3', sans-serif;", "font-size: 24px;", "color: #"] {
|
||||
assert!(text.contains(decl), "{decl} in {text}");
|
||||
}
|
||||
run(&mut s, "shape.rectangle", json!({"x": 0, "y": 100, "width": 100, "height": 50}));
|
||||
|
||||
+17
-1
@@ -85,6 +85,22 @@ pub(crate) fn font_stretch(st: &CharStyle) -> Option<f64> {
|
||||
st.font_axes.get("wdth").copied().filter(|w| w.is_finite() && *w > 0.0)
|
||||
}
|
||||
|
||||
/// The generic family to list after `family`, with its leading comma, so a viewer without the font draws
|
||||
/// the kind of face it was (the font's class, as the font menu's filters know it) rather than its default
|
||||
/// serif. Nothing when `family` is a generic family itself.
|
||||
fn generic_fallback(family: &str) -> &'static str {
|
||||
const GENERIC: [&str; 5] = ["serif", "sans-serif", "monospace", "cursive", "fantasy"];
|
||||
if GENERIC.iter().any(|g| family.trim().eq_ignore_ascii_case(g)) {
|
||||
return "";
|
||||
}
|
||||
match vectorcraft_text::FontDb::global().family_traits(family).class {
|
||||
vectorcraft_text::FontClass::Serif => ", serif",
|
||||
vectorcraft_text::FontClass::Monospaced => ", monospace",
|
||||
vectorcraft_text::FontClass::Script => ", cursive",
|
||||
_ => ", sans-serif",
|
||||
}
|
||||
}
|
||||
|
||||
/// The font of character style `st`: family, size (the vertical scale; writers stretch the
|
||||
/// horizontal one), weight and style, and its Variable Font settings, lengths written by `len`.
|
||||
/// The weight is the style's number (600 for Semibold, 300 for Light) or the `wght` setting,
|
||||
@@ -98,7 +114,7 @@ pub(crate) fn font_props(st: &CharStyle, len: &dyn Fn(f64) -> String) -> Props {
|
||||
} else {
|
||||
st.font_family.clone()
|
||||
};
|
||||
p.push(("font-family", fam));
|
||||
p.push(("font-family", format!("{fam}{}", generic_fallback(&st.font_family))));
|
||||
p.push(("font-size", len(st.size * st.v_scale / 100.0)));
|
||||
let (weight, italic) = font_descriptor(st);
|
||||
match weight {
|
||||
|
||||
@@ -163,7 +163,7 @@ fn type_gives_font_properties() {
|
||||
let id = add(&mut d, n);
|
||||
let r = one(&d, id, &CssOptions::default());
|
||||
assert_eq!(r.selector, ".type");
|
||||
assert_eq!(prop(&r, "font-family"), Some("'Source Sans 3'"));
|
||||
assert_eq!(prop(&r, "font-family"), Some("'Source Sans 3', sans-serif"));
|
||||
assert_eq!(prop(&r, "font-size"), Some("18px"));
|
||||
assert_eq!(prop(&r, "font-weight"), Some("bold"));
|
||||
assert_eq!(prop(&r, "font-style"), Some("italic"));
|
||||
@@ -281,9 +281,28 @@ fn groups_stand_for_their_objects_and_hidden_ones_are_left_out() {
|
||||
assert_eq!(sheet.text(), sheet.rules[0].text());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn font_family_ends_with_a_generic_family_for_viewers_without_the_font() {
|
||||
let family = |name: &str| {
|
||||
let mut d = doc();
|
||||
let st = CharStyle { font_family: name.into(), size: 18.0, ..CharStyle::default() };
|
||||
let n = text(&mut d, "Hello", st);
|
||||
let id = add(&mut d, n);
|
||||
prop(&one(&d, id, &CssOptions::default()), "font-family").map(str::to_string)
|
||||
};
|
||||
// Source Sans 3 is a sans face; an unknown family gets the same default; a generic family stands alone.
|
||||
assert_eq!(family("Source Sans 3").as_deref(), Some("'Source Sans 3', sans-serif"));
|
||||
assert_eq!(family("No Such Font").as_deref(), Some("'No Such Font', sans-serif"));
|
||||
assert_eq!(family("serif").as_deref(), Some("serif"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn font_family_with_quote_or_backslash_is_escaped() {
|
||||
for (family, want) in [("Bob's Font", r"'Bob\'s Font'"), (r"Back\slash Font", r"'Back\\slash Font'"), ("Bob's", r"'Bob\'s'")] {
|
||||
for (family, want) in [
|
||||
("Bob's Font", r"'Bob\'s Font', sans-serif"),
|
||||
(r"Back\slash Font", r"'Back\\slash Font', sans-serif"),
|
||||
("Bob's", r"'Bob\'s', sans-serif"),
|
||||
] {
|
||||
let mut d = doc();
|
||||
let st = CharStyle { font_family: family.into(), size: 18.0, ..CharStyle::default() };
|
||||
let n = text(&mut d, "Hello", st);
|
||||
|
||||
@@ -300,7 +300,7 @@ fn lines_without_moved_characters_are_written_as_before() {
|
||||
}
|
||||
let open = |x: &str, anchor: &str| {
|
||||
format!(
|
||||
r##"<text transform="matrix(1 0 0 1 20 20)" x="{x}" y="12" xml:space="preserve" font-family="'Source Sans 3'" font-size="12" fill="#000000"{anchor}>"##
|
||||
r##"<text transform="matrix(1 0 0 1 20 20)" x="{x}" y="12" xml:space="preserve" font-family="'Source Sans 3', sans-serif" font-size="12" fill="#000000"{anchor}>"##
|
||||
)
|
||||
};
|
||||
let words =
|
||||
|
||||
Reference in New Issue
Block a user