mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-29 04:07:12 -06:00
Merge pull request #782 from dotCooCoo/feat/package-font-resolution
Package: fonts found by any of their names are copied, and skipped fonts are named
This commit is contained in:
@@ -7,6 +7,7 @@ use serde_json::{Value, json};
|
||||
use vectorcraft_color::Paint;
|
||||
use vectorcraft_doc::{ColorMode, Document, Node, NodeId, NodeKind};
|
||||
use vectorcraft_geom::{Affine, Rect};
|
||||
use vectorcraft_text::embed::Embedding;
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -77,13 +78,17 @@ pub struct Section {
|
||||
pub rows: Vec<(String, String)>,
|
||||
}
|
||||
|
||||
/// What a font's `fsType` lets a copy of it do.
|
||||
/// What a font's `fsType` lets a copy of it do, read as exports read it ([`Embedding`]): the
|
||||
/// most permissive usage bit wins, and bitmap-only fonts may not be embedded.
|
||||
pub fn embedding_label(fs_type: u16) -> &'static str {
|
||||
match fs_type & 0x000f {
|
||||
0x0002 => "embedding not allowed",
|
||||
0x0004 => "embedding for preview and print",
|
||||
0x0008 => "embedding for editing",
|
||||
_ => "embedding allowed",
|
||||
if Embedding::from_fs_type(fs_type) == Embedding::Forbidden {
|
||||
"embedding not allowed"
|
||||
} else if fs_type & 0x0008 != 0 {
|
||||
"embedding for editing"
|
||||
} else if fs_type & 0x0004 != 0 {
|
||||
"embedding for preview and print"
|
||||
} else {
|
||||
"embedding allowed"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,13 +141,19 @@ fn sections(d: &Document, info: &Value, fonts: &BTreeSet<(String, String)>, imag
|
||||
fonts
|
||||
.iter()
|
||||
.map(|(family, style)| {
|
||||
let detail = match db.face(family, style) {
|
||||
Some(f) if f.family.eq_ignore_ascii_case(family) => {
|
||||
// Found by any of its names; a style the family lacks shows in another.
|
||||
let detail = match db.resolve(family, style) {
|
||||
Some((f, m)) if m != vectorcraft_text::FontMatch::Missing => {
|
||||
let file =
|
||||
f.path().and_then(|p| p.file_name()).map_or_else(|| "built in".into(), |n| n.to_string_lossy().into_owned());
|
||||
format!("{file}; {}", embedding_label(f.fs_type()))
|
||||
let found = format!("{file}; {}", embedding_label(f.fs_type()));
|
||||
if m == vectorcraft_text::FontMatch::Style {
|
||||
format!("substituted: shown in {} {} ({found})", f.family, f.style)
|
||||
} else {
|
||||
found
|
||||
}
|
||||
}
|
||||
Some(f) => format!("missing: shown in {} {}", f.family, f.style),
|
||||
Some((f, _)) => format!("missing: shown in {} {}", f.family, f.style),
|
||||
None => "missing".into(),
|
||||
};
|
||||
(format!("{family} {style}"), detail)
|
||||
@@ -396,6 +407,12 @@ mod tests {
|
||||
assert_eq!(embedding_label(0x0002), "embedding not allowed");
|
||||
assert_eq!(embedding_label(0x0004 | 0x0100), "embedding for preview and print");
|
||||
assert_eq!(embedding_label(0x0008), "embedding for editing");
|
||||
// As exports read it: bitmap embedding only, the restricted bit with reserved bit 0, the
|
||||
// most permissive of several usage bits.
|
||||
assert_eq!(embedding_label(0x0200), "embedding not allowed");
|
||||
assert_eq!(embedding_label(0x0003), "embedding not allowed");
|
||||
assert_eq!(embedding_label(0x0006), "embedding for preview and print");
|
||||
assert_eq!(embedding_label(0x000c), "embedding for editing");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -109,8 +109,15 @@ fn package(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
lines.push("FONTS".to_string());
|
||||
for (family, style) in super::fonts::used_fonts(&st.doc) {
|
||||
let font = format!("{family} {style}");
|
||||
let reason = match db.face(&family, &style) {
|
||||
Some(f) if f.family.eq_ignore_ascii_case(&family) && f.embeddable() => {
|
||||
// Found by any of its names, as the canvas draws it.
|
||||
let resolved = db.resolve(&family, &style).filter(|(_, m)| *m != vectorcraft_text::FontMatch::Missing);
|
||||
// A style the family lacks is shown in its closest style, whose file is copied.
|
||||
let shown = match &resolved {
|
||||
Some((f, vectorcraft_text::FontMatch::Style)) => format!(" (shown in {} {})", f.family, f.style),
|
||||
_ => String::new(),
|
||||
};
|
||||
let reason = match resolved {
|
||||
Some((f, _)) if f.embeddable() => {
|
||||
let data = f.file_data();
|
||||
let file = f.path().and_then(Path::file_name).map_or_else(
|
||||
|| format!("{}-{}.{}", f.family, f.style, font_ext(data)).replace(|c: char| !(c.is_alphanumeric() || "-_.".contains(c)), ""),
|
||||
@@ -121,13 +128,13 @@ fn package(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
entries.push((format!("Fonts/{file}"), data.to_vec()));
|
||||
fonts += 1;
|
||||
}
|
||||
lines.push(format!("{font} → Fonts/{file}"));
|
||||
lines.push(format!("{font}{shown} → Fonts/{file}"));
|
||||
continue;
|
||||
}
|
||||
Some(f) if f.family.eq_ignore_ascii_case(&family) => "its licence doesn't allow embedding",
|
||||
_ => "not available on this computer",
|
||||
Some(_) => "its licence doesn't allow embedding",
|
||||
None => "not available on this computer",
|
||||
};
|
||||
lines.push(format!("{font}: {reason}, not copied"));
|
||||
lines.push(format!("{font}{shown}: {reason}, not copied"));
|
||||
skipped.push(json!({ "font": font, "reason": reason }));
|
||||
}
|
||||
lines.push(String::new());
|
||||
|
||||
Reference in New Issue
Block a user