mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-12 11:13:55 +00:00
* craft-affinity: bounded reader for native Affinity documents, moved from VectorCraft The reader of .af, .afdesign, .afphoto and .afpub files (archive, object stream and document model) as it is in storytold/vectorcraft at 6e873b80 (crates/affinity), under a neutral name. Its corpus tests read the pinned public documents scripts/fetch-affinity-corpus.sh fetches. * craft-affinity: fold in what only PhotoCraft's copy of the reader had From storytold/photocraft at 63b641b5 (crates/affinity), onto the VectorCraft copy: - Special shapes: clouds, hearts, cogs, callouts, arrows, double stars, tears, crescents, diamonds and segments come in as their own outlines, not their bounding ellipse. - Null slots of object lists are charged to the document's work before a list is scanned, on every visit of a shared object (children, attachments, effects, compound operands, page lists, paint lists, gradient colours, text blocks and runs, table cells, tile lists). - A colour or gradient position record is decoded only as far as its components are read. - 16-bit and 8-bit planes are interleaved through tables, not an f64 for each pixel. - Limits::max_pixel_bytes: the caller sets what the pixel layers of one import may hold. - Pixels::cmyka8_to_rgba8 for a caller without colour management. The crate README and docs describe the model on its own terms. * craft-affinity: a callout with a right-to-left box no longer panics; an oversized shape imports as its box Ported from storytold/photocraft#10473 by DJHoltkamp. A callout rectangle clamped its tail with clamp(x0, x1), which panics when the box is stored right to left. A box with finite corners whose size overflows gave infinite or NaN outline points; it now imports as its box with a warning. Also drops the inflated doc.dat once it is parsed.
36 lines
1.6 KiB
Rust
36 lines
1.6 KiB
Rust
//! The pinned public corpus: the folder `AFFINITY_CORPUS` names, filled by
|
|
//! `scripts/fetch-affinity-corpus.sh`. Every file `corpus.tsv` lists must be there unmodified.
|
|
//! Without the variable the corpus tests are skipped.
|
|
#![allow(dead_code)]
|
|
|
|
use std::path::PathBuf;
|
|
|
|
use sha2::{Digest, Sha256};
|
|
|
|
/// `sha256 name licence url` rows.
|
|
pub const MANIFEST: &str = include_str!("corpus.tsv");
|
|
|
|
/// The names of the pinned files.
|
|
pub fn names() -> impl Iterator<Item = &'static str> {
|
|
MANIFEST.lines().filter_map(|l| l.split('\t').nth(1))
|
|
}
|
|
|
|
pub fn pinned() -> Option<PathBuf> {
|
|
let Some(dir) = std::env::var_os("AFFINITY_CORPUS").map(PathBuf::from) else {
|
|
eprintln!("Affinity corpus not given: run scripts/fetch-affinity-corpus.sh and set AFFINITY_CORPUS to its folder");
|
|
return None;
|
|
};
|
|
let mut count = 0;
|
|
for line in MANIFEST.lines().filter(|l| !l.trim().is_empty()) {
|
|
let mut cols = line.split('\t');
|
|
let (Some(digest), Some(name)) = (cols.next(), cols.next()) else { panic!("malformed corpus row: {line}") };
|
|
assert_eq!(digest.len(), 64, "invalid SHA256 for {name}");
|
|
let bytes = std::fs::read(dir.join(name)).unwrap_or_else(|e| panic!("missing pinned Affinity file {name}: {e}"));
|
|
let hex: String = Sha256::digest(&bytes).iter().map(|b| format!("{b:02x}")).collect();
|
|
assert_eq!(hex, digest, "{name}: pinned corpus modified; run scripts/fetch-affinity-corpus.sh");
|
|
count += 1;
|
|
}
|
|
assert!(count >= 4, "the pinned manifest must include the four Affinity 3 .af fixtures");
|
|
Some(dir)
|
|
}
|