mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-27 03:06:57 -06:00
Fix #3395: color book library tabs use file name
## Summary Swatch library panels title imported and User Defined .acb color books from the file name (without the extension), matching Illustrator. Host temporary import paths still fall back to the book title when copying into the user library folder (#2996). ## Tests - `color_book_tab_titles_use_the_file_name` covers user-folder scan, load by path, and copyToUser keeping the source file name. - Existing `copying_a_color_book_from_a_temporary_path_uses_its_title` unchanged. Closes storytold/vectorcraft#3395 Co-authored-by: nrq6993-a11y <nrq6993-a11y@users.noreply.github.com>
This commit is contained in:
committed by
bflatastic
co-authored by
nrq6993-a11y
parent
15b041be81
commit
3d4d34fc15
@@ -162,7 +162,7 @@ impl<L: LibraryFile> Libraries<L> {
|
||||
let file = file_name(&path);
|
||||
let read = || crate::file_access::unconfined(|| read_file(&path)).ok().and_then(|b| L::read(&b, stem(&file)).ok());
|
||||
let Some(lib) = crate::guard::catch_panic(read).ok().flatten() else { continue };
|
||||
let info = LibraryInfo { id: format!("user/{file}"), name: lib.name().to_string(), category: "user" };
|
||||
let info = LibraryInfo { id: format!("user/{file}"), name: library_tab_name(&file, lib.name()), category: "user" };
|
||||
self.extra.push(Extra { info, path: Some(path), lib: Arc::new(lib) });
|
||||
}
|
||||
}
|
||||
@@ -245,7 +245,7 @@ impl<L: LibraryFile> Libraries<L> {
|
||||
if let Some(e) = path.and_then(user) {
|
||||
return Ok((e.info.clone(), e.lib.clone()));
|
||||
}
|
||||
let info = LibraryInfo { id: format!("loaded/{}", path.unwrap_or(&file)), name: lib.name().to_string(), category: "loaded" };
|
||||
let info = LibraryInfo { id: format!("loaded/{}", path.unwrap_or(&file)), name: library_tab_name(&file, lib.name()), category: "loaded" };
|
||||
let lib = Arc::new(lib);
|
||||
self.extra.retain(|x| x.info.id != info.id);
|
||||
self.extra.push(Extra { info: info.clone(), path: path.map(str::to_string), lib: lib.clone() });
|
||||
@@ -278,9 +278,10 @@ impl<L: LibraryFile> Libraries<L> {
|
||||
Some(p) => {
|
||||
let ext = extension(&p);
|
||||
let base = file_name(&p);
|
||||
// Color books (.acb) name the tab from the book title inside the file; a sandbox
|
||||
// copy of an import often lands as tmp….tmp.acb (#2996).
|
||||
let file = if ext == "acb" || temporary_library_file_name(&base) { library_file_name(lib.name(), &ext) } else { base };
|
||||
// A sandbox copy of an import often lands as tmp….tmp.acb (#2996): store it under the
|
||||
// book title. A normal .acb path keeps its file name so the tab matches Illustrator
|
||||
// (#3395).
|
||||
let file = if temporary_library_file_name(&base) { library_file_name(lib.name(), &ext) } else { base };
|
||||
(file, read_file(&p)?)
|
||||
}
|
||||
None => (library_file_name(lib.name(), ext), encode(&lib)?),
|
||||
@@ -318,6 +319,13 @@ fn temporary_library_file_name(name: &str) -> bool {
|
||||
lower.contains(".tmp.")
|
||||
}
|
||||
|
||||
/// The name library panels and menus show: color books (.acb) use the file name without its
|
||||
/// extension (Illustrator), except when the path is a host temporary (#2996), then the book title.
|
||||
fn library_tab_name(file: &str, lib_name: &str) -> String {
|
||||
let base = file_name(file);
|
||||
if extension(&base) == "acb" && !temporary_library_file_name(&base) { stem(&base).to_string() } else { lib_name.to_string() }
|
||||
}
|
||||
|
||||
/// The file name of library `name` with extension `ext`: characters file systems reject become
|
||||
/// `-`, and dots and spaces at either end are dropped. A name with nothing left is `Library`.
|
||||
pub fn library_file_name(name: &str, ext: &str) -> String {
|
||||
|
||||
@@ -468,6 +468,52 @@ fn copying_a_color_book_from_a_temporary_path_uses_its_title() {
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
/// Color books in the user library folder and opened by path show the file name as the tab title
|
||||
/// (#3395), not only the title stored inside the book.
|
||||
#[test]
|
||||
fn color_book_tab_titles_use_the_file_name() {
|
||||
fn acb_bytes(title: &str) -> Vec<u8> {
|
||||
fn acb_str(out: &mut Vec<u8>, s: &str) {
|
||||
let units: Vec<u16> = s.encode_utf16().collect();
|
||||
out.extend((units.len() as u32).to_be_bytes());
|
||||
units.iter().for_each(|u| out.extend(u.to_be_bytes()));
|
||||
}
|
||||
let mut bytes = b"8BCB".to_vec();
|
||||
bytes.extend(1u16.to_be_bytes());
|
||||
bytes.extend(3000u16.to_be_bytes());
|
||||
for s in [title, "PANTONE ", " C", ""] {
|
||||
acb_str(&mut bytes, s);
|
||||
}
|
||||
bytes.extend(1u16.to_be_bytes());
|
||||
bytes.extend(7u16.to_be_bytes());
|
||||
bytes.extend(0u16.to_be_bytes());
|
||||
bytes.extend(0u16.to_be_bytes());
|
||||
acb_str(&mut bytes, "185");
|
||||
bytes.extend(b"TST001");
|
||||
bytes.extend([228, 0, 43]);
|
||||
bytes.extend(b"spflspot");
|
||||
bytes
|
||||
}
|
||||
let dir = temp_dir("acb-name");
|
||||
let user = dir.join("user");
|
||||
let elsewhere = dir.join("elsewhere");
|
||||
std::fs::create_dir_all(&user).unwrap();
|
||||
std::fs::create_dir_all(&elsewhere).unwrap();
|
||||
let on_disk = user.join("PANTONE Solid Coated.acb");
|
||||
std::fs::write(&on_disk, acb_bytes("Internal Book Title")).unwrap();
|
||||
let named = elsewhere.join("Brand Colors.acb");
|
||||
std::fs::write(&named, acb_bytes("Another Internal Title")).unwrap();
|
||||
|
||||
let mut s = session();
|
||||
s.swatch_libraries.set_user_dir(Some(user.to_string_lossy().to_string()));
|
||||
assert_eq!(run(&mut s, "swatch.library.get", json!({ "library": "user/PANTONE Solid Coated.acb" }))["name"], "PANTONE Solid Coated");
|
||||
let loaded = run(&mut s, "swatch.library.load", json!({ "path": named.to_string_lossy() }))["library"].clone();
|
||||
assert_eq!(run(&mut s, "swatch.library.get", json!({ "library": loaded }))["name"], "Brand Colors");
|
||||
let r = run(&mut s, "swatch.library.copyToUser", json!({ "library": loaded }));
|
||||
assert_eq!((r["library"].as_str(), r["name"].as_str(), r["copied"].as_bool()), (Some("user/Brand Colors.acb"), Some("Brand Colors"), Some(true)));
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_library_group_stays_one_group_when_a_swatch_has_its_name() {
|
||||
use vectorcraft_testkit::ase::{Block, ase};
|
||||
|
||||
Reference in New Issue
Block a user