use crate::helpers::LARGE_ADDITIONAL_INFO_KEYS;
use crate::psd::{LayerAdditionalInfo, ReadOptions, WriteOptions};
use crate::reader::{PsdReader, ReadResult};
use crate::writer::{write_section, write_signature, PsdWriter};
pub mod metadata_keys;
pub mod adjustment_keys;
pub mod effects_keys;
pub mod misc_keys;
pub mod smart_object_keys;
pub mod text_keys;
pub mod vector_keys;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Group {
Metadata,
Text,
Effects,
SmartObject,
Vector,
Adjustment,
Misc,
}
#[derive(Debug, Clone, Copy)]
pub struct KeyHandler {
pub key: &'static str,
pub group: Group,
pub four_bytes: bool,
pub write_total_length: bool,
}
const fn h(key: &'static str, group: Group) -> KeyHandler {
KeyHandler { key, group, four_bytes: false, write_total_length: true }
}
const fn h4(key: &'static str, group: Group) -> KeyHandler {
KeyHandler { key, group, four_bytes: true, write_total_length: true }
}
const fn h4_no_total(key: &'static str, group: Group) -> KeyHandler {
KeyHandler { key, group, four_bytes: true, write_total_length: false }
}
const fn h_no_total(key: &'static str, group: Group) -> KeyHandler {
KeyHandler { key, group, four_bytes: false, write_total_length: false }
}
pub const HANDLERS: &[KeyHandler] = &[
h("TySh", Group::Text), h("SoCo", Group::Vector),
h4("GdFl", Group::Vector),
h("PtFl", Group::Vector),
h4("vscg", Group::Vector),
h4("vmsk", Group::Vector),
h("vowv", Group::Vector), h4("vogk", Group::Vector),
h4("lmfx", Group::Effects),
h4("lrFX", Group::Effects),
h4("luni", Group::Metadata),
h("lnsr", Group::Metadata),
h("lyid", Group::Metadata),
h("lsct", Group::Metadata),
h("clbl", Group::Metadata),
h("infx", Group::Metadata),
h("knko", Group::Metadata),
h("lmgm", Group::Metadata),
h("lspf", Group::Metadata),
h("lclr", Group::Metadata),
h("shmd", Group::Misc),
h("PxSc", Group::SmartObject),
h("vstk", Group::Vector),
h4("artb", Group::Misc),
h("sn2P", Group::Misc),
h4("PlLd", Group::SmartObject),
h4("SoLd", Group::SmartObject),
h("fxrp", Group::Metadata),
h("Lr16", Group::Misc),
h("Lr32", Group::Misc),
h("LMsk", Group::Misc),
h("Patt", Group::SmartObject),
h("Patt", Group::SmartObject), h4_no_total("CAI ", Group::Misc),
h4_no_total("CAI ", Group::Misc), h4_no_total("OCIO", Group::Misc),
h4("GenI", Group::Misc),
h4("Anno", Group::Misc),
h4("lnk2", Group::SmartObject), h("lnkE", Group::SmartObject), h("pths", Group::Vector),
h("lyvr", Group::Metadata),
h("lfxs", Group::Effects),
h("brit", Group::Adjustment),
h("levl", Group::Adjustment),
h4("curv", Group::Adjustment),
h("expA", Group::Adjustment),
h4("vibA", Group::Adjustment),
h("hue2", Group::Adjustment),
h("blnc", Group::Adjustment),
h4("blwh", Group::Adjustment),
h("phfl", Group::Adjustment),
h("mixr", Group::Adjustment),
h("clrL", Group::Adjustment),
h("nvrt", Group::Adjustment),
h("post", Group::Adjustment),
h("thrs", Group::Adjustment),
h4("grdm", Group::Adjustment),
h("selc", Group::Adjustment),
h4("CgEd", Group::Adjustment),
h4_no_total("Txt2", Group::Text), h4("FEid", Group::Misc),
h("FMsk", Group::Misc),
h("artd", Group::Misc),
h("lfx2", Group::Effects),
h4_no_total("cinf", Group::Misc),
h_no_total("extn", Group::Misc),
h("iOpa", Group::Metadata),
h("brst", Group::Metadata),
h("tsly", Group::Metadata),
];
pub const ALIASES: &[(&str, &str)] = &[
("vsms", "vmsk"),
("vmsk", "vsms"), ("lsdk", "lsct"),
("SoLE", "SoLd"),
("Pat2", "Patt"),
("Pat3", "Patt"),
("lnkD", "lnk2"),
("lnk3", "lnk2"),
("FXid", "FEid"),
];
pub fn alias_target(key: &str) -> &str {
for (alias, target) in ALIASES {
if *alias == key {
return target;
}
}
key
}
pub fn group_for_key(key: &str) -> Option<Group> {
let target = alias_target(key);
HANDLERS.iter().find(|h| h.key == target).map(|h| h.group)
}
pub fn is_large_key(key: &str) -> bool {
LARGE_ADDITIONAL_INFO_KEYS.contains(&key)
}
pub struct ReadCtx<'a> {
pub options: &'a ReadOptions,
pub large: bool,
}
pub struct WriteCtx<'a> {
pub options: &'a WriteOptions,
pub psb: bool,
pub layer_ids: std::collections::HashSet<u32>,
}
impl<'a> WriteCtx<'a> {
pub fn new(options: &'a WriteOptions, psb: bool) -> Self {
WriteCtx { options, psb, layer_ids: std::collections::HashSet::new() }
}
}
pub fn read_additional_info_key(
key: &str,
reader: &mut PsdReader,
info: &mut LayerAdditionalInfo,
left: &dyn Fn(&PsdReader) -> usize,
ctx: &mut ReadCtx,
) -> ReadResult<bool> {
let group = match group_for_key(key) {
Some(g) => g,
None => return Ok(false),
};
let result = match group {
Group::Metadata => metadata_keys::read(key, reader, info, left, ctx)?,
Group::Text => text_keys::read(key, reader, info, left, ctx)?,
Group::Effects => effects_keys::read(key, reader, info, left, ctx)?,
Group::SmartObject => smart_object_keys::read(key, reader, info, left, ctx)?,
Group::Vector => vector_keys::read(key, reader, info, left, ctx)?,
Group::Adjustment => adjustment_keys::read(key, reader, info, left, ctx)?,
Group::Misc => misc_keys::read(key, reader, info, left, ctx)?,
};
Ok(result.is_some())
}
pub fn write_additional_info(
writer: &mut PsdWriter,
info: &LayerAdditionalInfo,
ctx: &mut WriteCtx,
) {
for handler in HANDLERS {
let mut key = handler.key;
if key == "Txt2" && ctx.options.invalidate_text_layers == Some(true) {
continue;
}
if key == "vmsk" && ctx.psb {
key = "vsms";
}
let has = group_has(handler.group, key, info);
if has != Some(true) {
continue;
}
let large = ctx.psb && is_large_key(key);
let round = if handler.four_bytes { 4 } else { 2 };
write_signature(writer, if large { "8B64" } else { "8BIM" });
write_signature(writer, key);
let write_total_length = handler.write_total_length;
write_section(
writer,
round,
|w| {
group_write(handler.group, key, w, info, ctx);
},
write_total_length,
large,
);
}
}
fn group_has(group: Group, key: &str, info: &LayerAdditionalInfo) -> Option<bool> {
match group {
Group::Metadata => metadata_keys::has(key, info),
Group::Text => text_keys::has(key, info),
Group::Effects => effects_keys::has(key, info),
Group::SmartObject => smart_object_keys::has(key, info),
Group::Vector => vector_keys::has(key, info),
Group::Adjustment => adjustment_keys::has(key, info),
Group::Misc => misc_keys::has(key, info),
}
}
fn group_write(
group: Group,
key: &str,
writer: &mut PsdWriter,
info: &LayerAdditionalInfo,
ctx: &mut WriteCtx,
) {
let handled = match group {
Group::Metadata => metadata_keys::write(key, writer, info, ctx),
Group::Text => text_keys::write(key, writer, info, ctx),
Group::Effects => effects_keys::write(key, writer, info, ctx),
Group::SmartObject => smart_object_keys::write(key, writer, info, ctx),
Group::Vector => vector_keys::write(key, writer, info, ctx),
Group::Adjustment => adjustment_keys::write(key, writer, info, ctx),
Group::Misc => misc_keys::write(key, writer, info, ctx),
};
debug_assert!(handled.is_some(), "group did not handle write for key {key}");
}