style(src-tauri): cargo fmt

Pure mechanical rustfmt reformat (default config, no rustfmt.toml).
This commit is contained in:
soubarna
2026-10-05 17:19:13 +05:30
parent e1031b3ecc
commit b6cf46b883
27 changed files with 4430 additions and 1042 deletions
+368 -90
View File
@@ -9,7 +9,9 @@
//! (`fvar`, `CFF2`: only the default instance would embed), files without outlines, and fonts whose OS/2 `fsType`
//! forbids embedding. The sfnt is parsed by hand (no font crate is available offline); only the tables needed
//! to describe the font are read.
use super::raw::{decode_header_path, header_map, raw_body, required_header, write_atomic, Headers};
use super::raw::{
decode_header_path, header_map, raw_body, required_header, write_atomic, Headers,
};
use crate::AppState;
use flate2::read::ZlibDecoder;
use rusqlite::{params, Connection, OptionalExtension};
@@ -119,7 +121,10 @@ impl<'a> Sfnt<'a> {
let off = be32(data, rec + 8).ok_or("The font file is truncated")? as usize;
let len = be32(data, rec + 12).ok_or("The font file is truncated")? as usize;
if off.checked_add(len).map_or(true, |end| end > data.len()) {
return Err(format!("Table '{}' lies outside the file", String::from_utf8_lossy(&tag)));
return Err(format!(
"Table '{}' lies outside the file",
String::from_utf8_lossy(&tag)
));
}
tables.push((tag, off, len));
}
@@ -135,7 +140,10 @@ impl<'a> Sfnt<'a> {
}
fn utf16be(b: &[u8]) -> String {
let units: Vec<u16> = b.chunks_exact(2).map(|c| u16::from_be_bytes([c[0], c[1]])).collect();
let units: Vec<u16> = b
.chunks_exact(2)
.map(|c| u16::from_be_bytes([c[0], c[1]]))
.collect();
String::from_utf16_lossy(&units)
}
@@ -201,7 +209,11 @@ impl<'a> Cmap<'a> {
best = Some((rank, offset, format));
}
}
best.map(|(_, offset, format)| Self { table, offset, format })
best.map(|(_, offset, format)| Self {
table,
offset,
format,
})
}
fn has(&self, cp: u32) -> bool {
@@ -283,7 +295,9 @@ fn woff_to_sfnt(data: &[u8]) -> Result<Vec<u8>, String> {
let comp = be32(data, rec + 8).ok_or_else(bad)? as usize;
let orig = be32(data, rec + 12).ok_or_else(bad)? as usize;
let checksum = be32(data, rec + 16).ok_or_else(bad)?;
let stored = data.get(off..off.checked_add(comp).ok_or_else(bad)?).ok_or_else(bad)?;
let stored = data
.get(off..off.checked_add(comp).ok_or_else(bad)?)
.ok_or_else(bad)?;
total += (orig + 3) & !3;
if orig > MAX_FONT_BYTES || total > MAX_FONT_BYTES {
return Err("The font expands to more than the 16 MB limit".into());
@@ -293,7 +307,10 @@ fn woff_to_sfnt(data: &[u8]) -> Result<Vec<u8>, String> {
} else if comp < orig {
let mut out = Vec::with_capacity(orig);
// One byte over the stated size detects a table that lies about it, without trusting it for allocation.
ZlibDecoder::new(stored).take(orig as u64 + 1).read_to_end(&mut out).map_err(|_| bad())?;
ZlibDecoder::new(stored)
.take(orig as u64 + 1)
.read_to_end(&mut out)
.map_err(|_| bad())?;
if out.len() != orig {
return Err(bad());
}
@@ -301,7 +318,11 @@ fn woff_to_sfnt(data: &[u8]) -> Result<Vec<u8>, String> {
} else {
return Err(bad());
};
entries.push(Entry { tag, checksum, bytes });
entries.push(Entry {
tag,
checksum,
bytes,
});
}
entries.sort_by_key(|e| e.tag);
@@ -372,12 +393,18 @@ pub fn inspect_bytes(input: &[u8]) -> Result<(Vec<u8>, FontInspection), String>
}
let cff = font.table(b"CFF ").is_some();
if !cff && (font.table(b"glyf").is_none() || font.table(b"loca").is_none()) {
return Err("The font has no glyph outlines (no glyf or CFF table), so it cannot be embedded.".into());
return Err(
"The font has no glyph outlines (no glyf or CFF table), so it cannot be embedded."
.into(),
);
}
let head = font.table(b"head").ok_or("The font has no head table")?;
let cmap_table = font.table(b"cmap").ok_or("The font has no cmap table")?;
let name = font.table(b"name").ok_or("The font has no name table")?;
let num_glyphs = font.table(b"maxp").and_then(|m| be16(m, 4)).ok_or("The font has no usable maxp table")?;
let num_glyphs = font
.table(b"maxp")
.and_then(|m| be16(m, 4))
.ok_or("The font has no usable maxp table")?;
let os2 = font.table(b"OS/2");
let fs_type = os2.and_then(|t| be16(t, 8)).unwrap_or(0);
@@ -413,7 +440,11 @@ pub fn inspect_bytes(input: &[u8]) -> Result<(Vec<u8>, FontInspection), String>
let fs_selection = os2.and_then(|t| be16(t, 62)).unwrap_or(0);
let mac_style = be16(head, 44).unwrap_or(0);
// fsSelection bit 0 = italic, bit 9 = oblique; head.macStyle bit 1 = italic.
let italic = if os2.map_or(false, |t| t.len() >= 64) { fs_selection & 0x0201 != 0 } else { mac_style & 0x2 != 0 };
let italic = if os2.map_or(false, |t| t.len() >= 64) {
fs_selection & 0x0201 != 0
} else {
mac_style & 0x2 != 0
};
let cmap = Cmap::new(cmap_table).ok_or("The font has no usable Unicode character map")?;
let has_rupee = cmap.has(0x20B9);
@@ -428,9 +459,14 @@ pub fn inspect_bytes(input: &[u8]) -> Result<(Vec<u8>, FontInspection), String>
warnings.push("The font has no rupee sign (\u{20B9}); amounts fall back to IBM Plex Sans for that glyph.".to_string());
}
let family = name_string(name, 16).or_else(|| name_string(name, 1)).unwrap_or_default();
let subfamily = name_string(name, 17).or_else(|| name_string(name, 2)).unwrap_or_default();
let full_name = name_string(name, 4).unwrap_or_else(|| format!("{family} {subfamily}").trim().to_string());
let family = name_string(name, 16)
.or_else(|| name_string(name, 1))
.unwrap_or_default();
let subfamily = name_string(name, 17)
.or_else(|| name_string(name, 2))
.unwrap_or_default();
let full_name =
name_string(name, 4).unwrap_or_else(|| format!("{family} {subfamily}").trim().to_string());
let postscript_name = name_string(name, 6).unwrap_or_default();
if family.is_empty() && full_name.is_empty() {
return Err("The font has no readable name".into());
@@ -460,7 +496,10 @@ pub fn inspect_bytes(input: &[u8]) -> Result<(Vec<u8>, FontInspection), String>
// storage
fn font_path(local_dir: &Path, sha256: &str, format: &str) -> Result<PathBuf, String> {
let valid = sha256.len() == 64 && sha256.bytes().all(|b| matches!(b, b'0'..=b'9' | b'a'..=b'f'));
let valid = sha256.len() == 64
&& sha256
.bytes()
.all(|b| matches!(b, b'0'..=b'9' | b'a'..=b'f'));
if !valid {
return Err("The stored font hash is malformed".to_string());
}
@@ -493,19 +532,31 @@ fn map_row(r: &rusqlite::Row) -> rusqlite::Result<UserFont> {
}
fn get_font(conn: &Connection, id: i64) -> Result<Option<UserFont>, String> {
conn.query_row(&format!("SELECT {COLS} FROM user_fonts WHERE id = ?1"), params![id], map_row)
.optional()
.map_err(|e| e.to_string())
conn.query_row(
&format!("SELECT {COLS} FROM user_fonts WHERE id = ?1"),
params![id],
map_row,
)
.optional()
.map_err(|e| e.to_string())
}
pub fn list_user_fonts_impl(conn: &Connection, include_hidden: bool) -> Result<Vec<UserFont>, String> {
pub fn list_user_fonts_impl(
conn: &Connection,
include_hidden: bool,
) -> Result<Vec<UserFont>, String> {
let sql = format!(
"SELECT {COLS} FROM user_fonts {} ORDER BY face, weight, style, id",
if include_hidden { "" } else { "WHERE hidden = 0" }
if include_hidden {
""
} else {
"WHERE hidden = 0"
}
);
let mut stmt = conn.prepare(&sql).map_err(|e| e.to_string())?;
let rows = stmt.query_map([], map_row).map_err(|e| e.to_string())?;
rows.collect::<Result<Vec<_>, _>>().map_err(|e| e.to_string())
rows.collect::<Result<Vec<_>, _>>()
.map_err(|e| e.to_string())
}
/// Whether any invoice's frozen render prefs name this font file.
@@ -531,7 +582,11 @@ pub struct ImportRequest<'a> {
fn clean_file_name(raw: &str) -> String {
let base = raw.rsplit(['/', '\\']).next().unwrap_or(raw);
let cleaned: String = base.chars().filter(|c| !c.is_control()).take(120).collect();
if cleaned.trim().is_empty() { "font".to_string() } else { cleaned }
if cleaned.trim().is_empty() {
"font".to_string()
} else {
cleaned
}
}
pub fn import_font_impl(
@@ -541,7 +596,10 @@ pub fn import_font_impl(
req: &ImportRequest,
) -> Result<UserFont, String> {
if !FACES.contains(&req.face) {
return Err(format!("Fonts can be imported for {} only", FACES.join(", ")));
return Err(format!(
"Fonts can be imported for {} only",
FACES.join(", ")
));
}
if !req.licence_acknowledged {
return Err("Confirm that you hold a licence for this font before importing it".into());
@@ -567,7 +625,9 @@ pub fn import_font_impl(
let path = font_path(local_dir, &info.sha256, &info.format)?;
std::fs::create_dir_all(path.parent().expect("font path has a parent"))
.map_err(|e| format!("Could not create the fonts folder: {e}"))?;
let present = std::fs::metadata(&path).map(|m| m.len() == sfnt.len() as u64).unwrap_or(false);
let present = std::fs::metadata(&path)
.map(|m| m.len() == sfnt.len() as u64)
.unwrap_or(false);
if !present {
write_atomic(&path, &sfnt)?;
}
@@ -628,12 +688,21 @@ pub fn import_font_impl(
fn retire(conn: &Connection, local_dir: &Path, id: i64) -> Result<Option<PathBuf>, String> {
let font = get_font(conn, id)?.ok_or_else(|| "Font not found".to_string())?;
if referenced_by_invoice(conn, &font.sha256)? {
conn.execute("UPDATE user_fonts SET hidden = 1 WHERE id = ?1", params![id]).map_err(|e| e.to_string())?;
conn.execute(
"UPDATE user_fonts SET hidden = 1 WHERE id = ?1",
params![id],
)
.map_err(|e| e.to_string())?;
return Ok(None);
}
conn.execute("DELETE FROM user_fonts WHERE id = ?1", params![id]).map_err(|e| e.to_string())?;
conn.execute("DELETE FROM user_fonts WHERE id = ?1", params![id])
.map_err(|e| e.to_string())?;
let shared: i64 = conn
.query_row("SELECT COUNT(*) FROM user_fonts WHERE sha256 = ?1", params![font.sha256], |r| r.get(0))
.query_row(
"SELECT COUNT(*) FROM user_fonts WHERE sha256 = ?1",
params![font.sha256],
|r| r.get(0),
)
.map_err(|e| e.to_string())?;
if shared > 0 {
return Ok(None);
@@ -641,11 +710,19 @@ fn retire(conn: &Connection, local_dir: &Path, id: i64) -> Result<Option<PathBuf
font_path(local_dir, &font.sha256, &font.format).map(Some)
}
pub fn remove_user_font_impl(conn: &mut Connection, local_dir: &Path, id: i64) -> Result<RemoveOutcome, String> {
pub fn remove_user_font_impl(
conn: &mut Connection,
local_dir: &Path,
id: i64,
) -> Result<RemoveOutcome, String> {
let tx = conn.transaction().map_err(|e| e.to_string())?;
let file = retire(&tx, local_dir, id)?;
let still_there: bool = tx
.query_row("SELECT EXISTS(SELECT 1 FROM user_fonts WHERE id = ?1)", params![id], |r| r.get::<_, i64>(0))
.query_row(
"SELECT EXISTS(SELECT 1 FROM user_fonts WHERE id = ?1)",
params![id],
|r| r.get::<_, i64>(0),
)
.map_err(|e| e.to_string())?
!= 0;
tx.commit().map_err(|e| e.to_string())?;
@@ -653,13 +730,24 @@ pub fn remove_user_font_impl(conn: &mut Connection, local_dir: &Path, id: i64) -
// Best effort, as in import: the row is already gone.
let _ = std::fs::remove_file(path);
}
Ok(RemoveOutcome { deleted: !still_there, hidden: still_there })
Ok(RemoveOutcome {
deleted: !still_there,
hidden: still_there,
})
}
/// The stored sfnt for a font file, hidden or not (an issued invoice must still be able to render with it).
pub fn user_font_bytes_impl(conn: &Connection, local_dir: &Path, sha256: &str) -> Result<Vec<u8>, String> {
pub fn user_font_bytes_impl(
conn: &Connection,
local_dir: &Path,
sha256: &str,
) -> Result<Vec<u8>, String> {
let format: Option<String> = conn
.query_row("SELECT format FROM user_fonts WHERE sha256 = ?1 LIMIT 1", params![sha256], |r| r.get(0))
.query_row(
"SELECT format FROM user_fonts WHERE sha256 = ?1 LIMIT 1",
params![sha256],
|r| r.get(0),
)
.optional()
.map_err(|e| e.to_string())?;
let format = format.ok_or_else(|| "This font is not in the font library".to_string())?;
@@ -677,20 +765,43 @@ pub fn user_font_bytes_impl(conn: &Connection, local_dir: &Path, sha256: &str) -
Ok(bytes)
}
fn parse_import_headers<'a>(headers: &'a Headers, file_name: &'a str) -> Result<ImportRequest<'a>, String> {
fn parse_import_headers<'a>(
headers: &'a Headers,
file_name: &'a str,
) -> Result<ImportRequest<'a>, String> {
let face = required_header(headers, "x-face")?;
let weight = match headers.get("x-weight").map(|v| v.trim()).filter(|v| !v.is_empty()) {
let weight = match headers
.get("x-weight")
.map(|v| v.trim())
.filter(|v| !v.is_empty())
{
None => None,
Some(v) => Some(v.parse::<u16>().map_err(|_| "x-weight must be a number".to_string())?),
Some(v) => Some(
v.parse::<u16>()
.map_err(|_| "x-weight must be a number".to_string())?,
),
};
let italic = match headers.get("x-style").map(|v| v.trim()).filter(|v| !v.is_empty()) {
let italic = match headers
.get("x-style")
.map(|v| v.trim())
.filter(|v| !v.is_empty())
{
None => None,
Some("normal") => Some(false),
Some("italic") => Some(true),
Some(_) => return Err("x-style must be normal or italic".into()),
};
let licence_acknowledged = headers.get("x-licence-ack").map(|v| v.trim() == "true").unwrap_or(false);
Ok(ImportRequest { face, file_name, weight, italic, licence_acknowledged })
let licence_acknowledged = headers
.get("x-licence-ack")
.map(|v| v.trim() == "true")
.unwrap_or(false);
Ok(ImportRequest {
face,
file_name,
weight,
italic,
licence_acknowledged,
})
}
// The commands are async so parsing, hashing and file I/O of a multi-MB font stay off the main thread.
@@ -701,10 +812,18 @@ pub async fn inspect_font(request: Request<'_>) -> Result<FontInspection, String
}
#[tauri::command]
pub async fn import_font(request: Request<'_>, state: State<'_, AppState>) -> Result<UserFont, String> {
pub async fn import_font(
request: Request<'_>,
state: State<'_, AppState>,
) -> Result<UserFont, String> {
let bytes = raw_body(&request)?;
let headers = header_map(&request);
let file_name = decode_header_path(headers.get("x-file-name").map(String::as_str).unwrap_or("font"))?;
let file_name = decode_header_path(
headers
.get("x-file-name")
.map(String::as_str)
.unwrap_or("font"),
)?;
let face = decode_header_path(required_header(&headers, "x-face")?)?;
let mut headers = headers;
headers.insert("x-face".to_string(), face);
@@ -714,19 +833,28 @@ pub async fn import_font(request: Request<'_>, state: State<'_, AppState>) -> Re
}
#[tauri::command]
pub async fn list_user_fonts(include_hidden: Option<bool>, state: State<'_, AppState>) -> Result<Vec<UserFont>, String> {
pub async fn list_user_fonts(
include_hidden: Option<bool>,
state: State<'_, AppState>,
) -> Result<Vec<UserFont>, String> {
let conn = state.db.lock().map_err(|e| e.to_string())?;
list_user_fonts_impl(&conn, include_hidden.unwrap_or(false))
}
#[tauri::command]
pub async fn get_user_font_bytes(sha256: String, state: State<'_, AppState>) -> Result<Response, String> {
pub async fn get_user_font_bytes(
sha256: String,
state: State<'_, AppState>,
) -> Result<Response, String> {
let conn = state.db.lock().map_err(|e| e.to_string())?;
user_font_bytes_impl(&conn, &state.local_data_dir, &sha256).map(Response::new)
}
#[tauri::command]
pub async fn remove_user_font(id: i64, state: State<'_, AppState>) -> Result<RemoveOutcome, String> {
pub async fn remove_user_font(
id: i64,
state: State<'_, AppState>,
) -> Result<RemoveOutcome, String> {
let mut conn = state.db.lock().map_err(|e| e.to_string())?;
remove_user_font_impl(&mut conn, &state.local_data_dir, id)
}
@@ -740,7 +868,9 @@ mod tests {
use tempfile::tempdir;
fn jost(name: &str) -> Vec<u8> {
let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("../public/fonts/jost").join(name);
let path = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../public/fonts/jost")
.join(name);
std::fs::read(path).unwrap()
}
fn regular() -> Vec<u8> {
@@ -781,7 +911,11 @@ mod tests {
let mut enc = ZlibEncoder::new(Vec::new(), Compression::default());
enc.write_all(raw).unwrap();
let z = enc.finish().unwrap();
blobs.push(if z.len() < raw.len() { (*tag, z, raw.len()) } else { (*tag, raw.to_vec(), raw.len()) });
blobs.push(if z.len() < raw.len() {
(*tag, z, raw.len())
} else {
(*tag, raw.to_vec(), raw.len())
});
}
let mut out = vec![0u8; 44 + 20 * n];
out[0..4].copy_from_slice(b"wOFF");
@@ -803,7 +937,13 @@ mod tests {
}
fn request<'a>(face: &'a str, ack: bool) -> ImportRequest<'a> {
ImportRequest { face, file_name: "C:\\fonts\\Jost-Regular.ttf", weight: None, italic: None, licence_acknowledged: ack }
ImportRequest {
face,
file_name: "C:\\fonts\\Jost-Regular.ttf",
weight: None,
italic: None,
licence_acknowledged: ack,
}
}
#[test]
@@ -821,9 +961,17 @@ mod tests {
assert_eq!(info.sha256, sha256_hex(&regular()));
assert_eq!(info.warnings.len(), 1, "{:?}", info.warnings);
assert!(info.warnings[0].contains("rupee"));
let poppins = std::fs::read(Path::new(env!("CARGO_MANIFEST_DIR")).join("../public/fonts/poppins/Poppins-Regular.ttf")).unwrap();
let poppins = std::fs::read(
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../public/fonts/poppins/Poppins-Regular.ttf"),
)
.unwrap();
let (_, with_rupee) = inspect_bytes(&poppins).unwrap();
assert!(with_rupee.has_rupee && with_rupee.warnings.is_empty(), "{:?}", with_rupee.warnings);
assert!(
with_rupee.has_rupee && with_rupee.warnings.is_empty(),
"{:?}",
with_rupee.warnings
);
assert_eq!(inspect_bytes(&bold()).unwrap().1.weight, 700);
}
@@ -835,7 +983,9 @@ mod tests {
let mut ttc = regular();
ttc[0..4].copy_from_slice(b"ttcf");
assert!(inspect_bytes(&ttc).unwrap_err().contains("collections"));
assert!(inspect_bytes(b"<!doctype html><html></html>").unwrap_err().contains("not a TrueType"));
assert!(inspect_bytes(b"<!doctype html><html></html>")
.unwrap_err()
.contains("not a TrueType"));
assert!(inspect_bytes(b"short").is_err());
let mut big = regular();
big.resize(MAX_FONT_BYTES + 1, 0);
@@ -856,7 +1006,9 @@ mod tests {
fn fs_type_rules() {
let mut restricted = regular();
set_fs_type(&mut restricted, FS_RESTRICTED);
assert!(inspect_bytes(&restricted).unwrap_err().contains("restricted"));
assert!(inspect_bytes(&restricted)
.unwrap_err()
.contains("restricted"));
// A more permissive bit alongside wins (least restrictive).
let mut both = regular();
@@ -865,11 +1017,21 @@ mod tests {
let mut preview = regular();
set_fs_type(&mut preview, FS_PREVIEW_PRINT);
assert!(inspect_bytes(&preview).unwrap().1.warnings.iter().any(|w| w.contains("preview and print")));
assert!(inspect_bytes(&preview)
.unwrap()
.1
.warnings
.iter()
.any(|w| w.contains("preview and print")));
let mut no_subset = regular();
set_fs_type(&mut no_subset, FS_NO_SUBSETTING);
assert!(inspect_bytes(&no_subset).unwrap().1.warnings.iter().any(|w| w.contains("subset")));
assert!(inspect_bytes(&no_subset)
.unwrap()
.1
.warnings
.iter()
.any(|w| w.contains("subset")));
let mut bitmap = regular();
set_fs_type(&mut bitmap, FS_BITMAP_ONLY);
@@ -880,13 +1042,18 @@ mod tests {
fn refuses_a_font_without_outlines() {
let mut font = regular();
rename_table(&mut font, b"glyf", b"XXXX");
assert!(inspect_bytes(&font).unwrap_err().contains("no glyph outlines"));
assert!(inspect_bytes(&font)
.unwrap_err()
.contains("no glyph outlines"));
}
#[test]
fn unpacks_woff_to_the_same_font() {
let woff = to_woff(&regular());
assert!(woff.len() < regular().len(), "the fixture should actually compress");
assert!(
woff.len() < regular().len(),
"the fixture should actually compress"
);
let (sfnt, info) = inspect_bytes(&woff).unwrap();
assert_eq!(info.source_format, "woff");
assert_eq!(info.family, "Jost");
@@ -897,7 +1064,12 @@ mod tests {
let unpacked = Sfnt::parse(&sfnt).unwrap();
assert_eq!(original.tables.len(), unpacked.tables.len());
for (tag, _, _) in &original.tables {
assert_eq!(original.table(tag), unpacked.table(tag), "table {}", String::from_utf8_lossy(tag));
assert_eq!(
original.table(tag),
unpacked.table(tag),
"table {}",
String::from_utf8_lossy(tag)
);
}
let mut damaged = woff.clone();
let len = damaged.len();
@@ -909,17 +1081,28 @@ mod tests {
fn import_stores_content_addressed_and_round_trips() {
let dir = tempdir().unwrap();
let mut conn = crate::db::open_in_memory().unwrap();
let row = import_font_impl(&mut conn, dir.path(), &regular(), &request("Gotham", true)).unwrap();
let row =
import_font_impl(&mut conn, dir.path(), &regular(), &request("Gotham", true)).unwrap();
assert_eq!(row.face, "Gotham");
assert_eq!((row.weight, row.style.as_str(), row.format.as_str()), (400, "normal", "ttf"));
assert_eq!(row.file_name, "Jost-Regular.ttf", "only the base name is kept");
assert_eq!(
(row.weight, row.style.as_str(), row.format.as_str()),
(400, "normal", "ttf")
);
assert_eq!(
row.file_name, "Jost-Regular.ttf",
"only the base name is kept"
);
assert!(!row.hidden && row.licence_ack_at.len() > 10);
let path = dir.path().join("fonts").join(format!("{}.ttf", row.sha256));
assert_eq!(std::fs::read(&path).unwrap(), regular());
assert_eq!(user_font_bytes_impl(&conn, dir.path(), &row.sha256).unwrap(), regular());
assert_eq!(
user_font_bytes_impl(&conn, dir.path(), &row.sha256).unwrap(),
regular()
);
// Importing the same file again is a no-op.
let again = import_font_impl(&mut conn, dir.path(), &regular(), &request("Gotham", true)).unwrap();
let again =
import_font_impl(&mut conn, dir.path(), &regular(), &request("Gotham", true)).unwrap();
assert_eq!(again.id, row.id);
assert_eq!(list_user_fonts_impl(&conn, false).unwrap().len(), 1);
@@ -933,18 +1116,32 @@ mod tests {
fn import_overrides_and_guards() {
let dir = tempdir().unwrap();
let mut conn = crate::db::open_in_memory().unwrap();
assert!(import_font_impl(&mut conn, dir.path(), &regular(), &request("Gotham", false))
.unwrap_err()
.contains("licence"));
assert!(import_font_impl(&mut conn, dir.path(), &regular(), &request("Montserrat", true))
.unwrap_err()
.contains("Now, Gotham, Open Sauce One"));
assert!(!dir.path().join("fonts").exists(), "a refused import writes nothing");
assert!(
import_font_impl(&mut conn, dir.path(), &regular(), &request("Gotham", false))
.unwrap_err()
.contains("licence")
);
assert!(import_font_impl(
&mut conn,
dir.path(),
&regular(),
&request("Montserrat", true)
)
.unwrap_err()
.contains("Now, Gotham, Open Sauce One"));
assert!(
!dir.path().join("fonts").exists(),
"a refused import writes nothing"
);
let mut woff2 = regular();
woff2[0..4].copy_from_slice(b"wOF2");
assert!(import_font_impl(&mut conn, dir.path(), &woff2, &request("Now", true)).is_err());
let req = ImportRequest { weight: Some(300), italic: Some(true), ..request("Open Sauce One", true) };
let req = ImportRequest {
weight: Some(300),
italic: Some(true),
..request("Open Sauce One", true)
};
let row = import_font_impl(&mut conn, dir.path(), &regular(), &req).unwrap();
assert_eq!((row.weight, row.style.as_str()), (300, "italic"));
// The table itself refuses other faces.
@@ -962,15 +1159,23 @@ mod tests {
fn replacing_a_slot_removes_the_old_unreferenced_font() {
let dir = tempdir().unwrap();
let mut conn = crate::db::open_in_memory().unwrap();
let first = import_font_impl(&mut conn, dir.path(), &regular(), &request("Gotham", true)).unwrap();
let first =
import_font_impl(&mut conn, dir.path(), &regular(), &request("Gotham", true)).unwrap();
// Same slot, different file (the bold file declared as 400).
let req = ImportRequest { weight: Some(400), ..request("Gotham", true) };
let req = ImportRequest {
weight: Some(400),
..request("Gotham", true)
};
let second = import_font_impl(&mut conn, dir.path(), &bold(), &req).unwrap();
assert_ne!(first.sha256, second.sha256);
let rows = list_user_fonts_impl(&conn, true).unwrap();
assert_eq!(rows.len(), 1);
assert_eq!(rows[0].sha256, second.sha256);
assert!(!dir.path().join("fonts").join(format!("{}.ttf", first.sha256)).exists());
assert!(!dir
.path()
.join("fonts")
.join(format!("{}.ttf", first.sha256))
.exists());
}
fn issue_invoice_with_prefs(conn: &Connection, prefs: &str) {
@@ -986,11 +1191,22 @@ mod tests {
fn remove_deletes_when_unreferenced() {
let dir = tempdir().unwrap();
let mut conn = crate::db::open_in_memory().unwrap();
let row = import_font_impl(&mut conn, dir.path(), &regular(), &request("Now", true)).unwrap();
let row =
import_font_impl(&mut conn, dir.path(), &regular(), &request("Now", true)).unwrap();
let out = remove_user_font_impl(&mut conn, dir.path(), row.id).unwrap();
assert_eq!(out, RemoveOutcome { deleted: true, hidden: false });
assert_eq!(
out,
RemoveOutcome {
deleted: true,
hidden: false
}
);
assert!(list_user_fonts_impl(&conn, true).unwrap().is_empty());
assert!(!dir.path().join("fonts").join(format!("{}.ttf", row.sha256)).exists());
assert!(!dir
.path()
.join("fonts")
.join(format!("{}.ttf", row.sha256))
.exists());
assert!(user_font_bytes_impl(&conn, dir.path(), &row.sha256).is_err());
assert!(remove_user_font_impl(&mut conn, dir.path(), row.id).is_err());
}
@@ -999,20 +1215,42 @@ mod tests {
fn remove_hides_a_font_an_issued_invoice_references() {
let dir = tempdir().unwrap();
let mut conn = crate::db::open_in_memory().unwrap();
let row = import_font_impl(&mut conn, dir.path(), &regular(), &request("Gotham", true)).unwrap();
let row =
import_font_impl(&mut conn, dir.path(), &regular(), &request("Gotham", true)).unwrap();
issue_invoice_with_prefs(
&conn,
&format!(r#"{{"version":1,"templateId":"purple-pop","fonts":{{"Gotham":["{}"]}}}}"#, row.sha256),
&format!(
r#"{{"version":1,"templateId":"purple-pop","fonts":{{"Gotham":["{}"]}}}}"#,
row.sha256
),
);
let out = remove_user_font_impl(&mut conn, dir.path(), row.id).unwrap();
assert_eq!(out, RemoveOutcome { deleted: false, hidden: true });
assert_eq!(
out,
RemoveOutcome {
deleted: false,
hidden: true
}
);
// Gone from the visible list, but kept and still served so the invoice can re-render.
assert!(list_user_fonts_impl(&conn, false).unwrap().is_empty());
assert_eq!(list_user_fonts_impl(&conn, true).unwrap().len(), 1);
assert_eq!(user_font_bytes_impl(&conn, dir.path(), &row.sha256).unwrap(), regular());
assert_eq!(
user_font_bytes_impl(&conn, dir.path(), &row.sha256).unwrap(),
regular()
);
// The slot is free again: importing a new font for it works, and the hidden one stays.
let fresh = import_font_impl(&mut conn, dir.path(), &bold(), &ImportRequest { weight: Some(400), ..request("Gotham", true) }).unwrap();
let fresh = import_font_impl(
&mut conn,
dir.path(),
&bold(),
&ImportRequest {
weight: Some(400),
..request("Gotham", true)
},
)
.unwrap();
assert_ne!(fresh.sha256, row.sha256);
assert_eq!(list_user_fonts_impl(&conn, true).unwrap().len(), 2);
}
@@ -1021,10 +1259,27 @@ mod tests {
fn replacing_a_referenced_font_hides_it_instead_of_deleting() {
let dir = tempdir().unwrap();
let mut conn = crate::db::open_in_memory().unwrap();
let row = import_font_impl(&mut conn, dir.path(), &regular(), &request("Gotham", true)).unwrap();
issue_invoice_with_prefs(&conn, &format!(r#"{{"fonts":{{"Gotham":["{}"]}}}}"#, row.sha256));
import_font_impl(&mut conn, dir.path(), &bold(), &ImportRequest { weight: Some(400), ..request("Gotham", true) }).unwrap();
assert!(dir.path().join("fonts").join(format!("{}.ttf", row.sha256)).exists());
let row =
import_font_impl(&mut conn, dir.path(), &regular(), &request("Gotham", true)).unwrap();
issue_invoice_with_prefs(
&conn,
&format!(r#"{{"fonts":{{"Gotham":["{}"]}}}}"#, row.sha256),
);
import_font_impl(
&mut conn,
dir.path(),
&bold(),
&ImportRequest {
weight: Some(400),
..request("Gotham", true)
},
)
.unwrap();
assert!(dir
.path()
.join("fonts")
.join(format!("{}.ttf", row.sha256))
.exists());
let all = list_user_fonts_impl(&conn, true).unwrap();
assert_eq!(all.len(), 2);
assert_eq!(all.iter().filter(|f| f.hidden).count(), 1);
@@ -1034,22 +1289,38 @@ mod tests {
fn a_file_shared_by_two_faces_survives_removing_one() {
let dir = tempdir().unwrap();
let mut conn = crate::db::open_in_memory().unwrap();
let a = import_font_impl(&mut conn, dir.path(), &regular(), &request("Gotham", true)).unwrap();
let a =
import_font_impl(&mut conn, dir.path(), &regular(), &request("Gotham", true)).unwrap();
let b = import_font_impl(&mut conn, dir.path(), &regular(), &request("Now", true)).unwrap();
assert_eq!(a.sha256, b.sha256);
remove_user_font_impl(&mut conn, dir.path(), a.id).unwrap();
assert!(dir.path().join("fonts").join(format!("{}.ttf", a.sha256)).exists());
assert!(dir
.path()
.join("fonts")
.join(format!("{}.ttf", a.sha256))
.exists());
remove_user_font_impl(&mut conn, dir.path(), b.id).unwrap();
assert!(!dir.path().join("fonts").join(format!("{}.ttf", a.sha256)).exists());
assert!(!dir
.path()
.join("fonts")
.join(format!("{}.ttf", a.sha256))
.exists());
}
#[test]
fn bytes_are_verified_and_the_hash_shape_checked() {
let dir = tempdir().unwrap();
let mut conn = crate::db::open_in_memory().unwrap();
let row = import_font_impl(&mut conn, dir.path(), &regular(), &request("Now", true)).unwrap();
std::fs::write(dir.path().join("fonts").join(format!("{}.ttf", row.sha256)), b"tampered").unwrap();
assert!(user_font_bytes_impl(&conn, dir.path(), &row.sha256).unwrap_err().contains("corrupted"));
let row =
import_font_impl(&mut conn, dir.path(), &regular(), &request("Now", true)).unwrap();
std::fs::write(
dir.path().join("fonts").join(format!("{}.ttf", row.sha256)),
b"tampered",
)
.unwrap();
assert!(user_font_bytes_impl(&conn, dir.path(), &row.sha256)
.unwrap_err()
.contains("corrupted"));
assert!(font_path(dir.path(), "../../etc/passwd", "ttf").is_err());
}
@@ -1061,11 +1332,18 @@ mod tests {
h.insert("x-weight".into(), "700".into());
h.insert("x-style".into(), "italic".into());
let req = parse_import_headers(&h, "f.ttf").unwrap();
assert_eq!((req.weight, req.italic, req.licence_acknowledged), (Some(700), Some(true), true));
assert_eq!(
(req.weight, req.italic, req.licence_acknowledged),
(Some(700), Some(true), true)
);
h.insert("x-style".into(), "oblique".into());
assert!(parse_import_headers(&h, "f.ttf").is_err());
h.remove("x-licence-ack");
h.insert("x-style".into(), "".into());
assert!(!parse_import_headers(&h, "f.ttf").unwrap().licence_acknowledged);
assert!(
!parse_import_headers(&h, "f.ttf")
.unwrap()
.licence_acknowledged
);
}
}