Integration: #787 refuses bitmap-only fonts for embedding, and styles name the face's numeric weight

This commit is contained in:
aiongg
2026-10-13 19:05:28 +00:00
parent 11e4a7f60e
commit 8f54d1cce4
3 changed files with 41 additions and 10 deletions
+31 -1
View File
@@ -156,7 +156,7 @@ fn embedded_fonts(d: &designcraft_doc::Document, warnings: &mut Vec<String>) ->
let face = db.face(&family, &style);
if !face.family.eq_ignore_ascii_case(&family) {
warnings.push(format!("font `{family} {style}` is not available; not embedded"));
} else if face.restricted_licence() {
} else if face.restricted_licence() || face.bitmap_only() {
warnings.push(format!("font `{family} {style}`: its licence doesn't allow embedding; not embedded"));
} else if face.data().starts_with(b"ttcf") {
warnings.push(format!("font `{family} {style}` is part of a font collection; not embedded"));
@@ -811,6 +811,36 @@ mod text_tests {
assert!(html.contains("\"Source Sans 3\", sans-serif;"));
}
/// A font whose licence allows embedding bitmaps only (fsType bit 9) is not packaged whole,
/// and a Semibold style asks for the Semibold face's weight, not Bold's.
#[test]
fn epub_embedding_follows_bitmap_only_licences_and_weights() {
use designcraft_fonts::testing::{font_with, with_fs_type};
const BITMAP: &str = "DocFont Epub Bitmap Only";
for fs_type in [0x0200, 0x0208] {
designcraft_fonts::FontDb::global()
.add_font(with_fs_type(font_with(&format!("{BITMAP} {fs_type:04x}"), &['W']).unwrap(), fs_type).unwrap());
}
let mut s = Session::new();
s.execute("file.new", &json!({"pages": 1})).unwrap();
for fs_type in [0x0200, 0x0208] {
let family = format!("{BITMAP} {fs_type:04x}");
s.execute("style.paragraph.create", &json!({"name": family, "chars": {"fontFamily": family, "fontStyle": "Regular"}})).unwrap();
}
s.execute("style.paragraph.create", &json!({"name": "Semi", "chars": {"fontFamily": "Source Sans 3", "fontStyle": "Semibold"}})).unwrap();
s.execute("frame.create", &json!({"rect": [72, 72, 400, 200], "content": "text", "text": "Words", "caret": false})).unwrap();
let r = s.execute("file.exportEpub", &json!({"embedFonts": true})).unwrap();
let mut z = unzip(r["base64"].as_str().unwrap());
let css = read(&mut z, "OEBPS/style.css");
assert!(!css.contains(&format!("@font-face {{ font-family: \"{BITMAP}")), "{css}");
let warnings = r["warnings"].to_string();
for fs_type in [0x0200, 0x0208] {
assert!(warnings.contains(&format!("{BITMAP} {fs_type:04x} Regular`: its licence doesn't allow embedding")), "{warnings}");
}
let semi = css.lines().find(|l| l.contains(".semi {")).unwrap_or_default();
assert!(semi.contains("font-weight: 600;"), "{css}");
}
#[test]
fn flattener_rasterises_spreads_with_transparency() {
let mut s = Session::new();
+3 -9
View File
@@ -349,15 +349,9 @@ fn hex(doc: &Document, swatch: &str, tint: f32) -> Option<String> {
fn char_css(doc: &Document, c: &CharProps, base_size: f64, generics: &BTreeMap<String, String>, out: &mut String) {
let _ = write!(out, "font-family: \"{}\", {}; ", css_string(&c.font_family), generic_family(&c.font_family, generics));
let st = c.font_style.to_lowercase();
if st.contains("bold") {
out.push_str("font-weight: bold; ");
} else if st.contains("semibold") {
out.push_str("font-weight: 600; ");
}
if st.contains("italic") {
out.push_str("font-style: italic; ");
}
// The face's own weight, so an embedded Semibold or Light face is the one chosen.
let (weight, style) = css_weight_style(&c.font_style);
let _ = write!(out, "font-weight: {weight}; font-style: {style}; ");
let _ = write!(out, "font-size: {:.3}em; ", c.size / base_size);
if c.tracking != 0.0 {
let _ = write!(out, "letter-spacing: {:.3}em; ", c.tracking / 1000.0);
+7
View File
@@ -358,6 +358,13 @@ impl FontFace {
let fs_type = self.skrifa().and_then(|f| f.os2().ok()).map_or(0, |t| t.fs_type());
fs_type & 0x0002 != 0 && fs_type & 0x000C == 0
}
/// Does the font's licence allow embedding bitmaps only? OS/2 `fsType` bit 9: the outlines
/// may not be passed on, so the font file can't be packaged whole.
pub fn bitmap_only(&self) -> bool {
use skrifa::raw::TableProvider;
self.skrifa().and_then(|f| f.os2().ok()).is_some_and(|t| t.fs_type() & 0x0200 != 0)
}
}
#[cfg(not(target_arch = "wasm32"))]