use crate::reader::{
read_ascii_string, read_bytes, read_float32, read_float64, read_int32, read_int32_le,
read_signature, read_uint32, read_uint8, read_unicode_string,
read_unicode_string_with_length_le, PsdReader, ReadError, ReadResult,
};
use crate::writer::{
write_bytes, write_float64, write_int32, write_int32_le, write_signature,
write_uint32, write_uint8, write_unicode_string, write_unicode_string_with_padding,
write_unicode_string_without_length_le, PsdWriter,
};
pub const UNITS_MAP: &[(&str, &str)] = &[
("#Ang", "Angle"),
("#Rsl", "Density"),
("#Rlt", "Distance"),
("#Nne", "None"),
("#Prc", "Percent"),
("#Pxl", "Pixels"),
("#Mlm", "Millimeters"),
("#Pnt", "Points"),
("RrPi", "Picas"),
("RrIn", "Inches"),
("RrCm", "Centimeters"),
];
pub fn units_name_from_code(code: &str) -> Option<&'static str> {
UNITS_MAP
.iter()
.find(|(c, _)| *c == code)
.map(|(_, name)| *name)
}
pub fn units_code_from_name(name: &str) -> Option<&'static str> {
UNITS_MAP
.iter()
.find(|(_, n)| *n == name)
.map(|(code, _)| *code)
}
#[derive(Debug, Clone, PartialEq)]
pub struct UnitDoubleValue {
pub units: String,
pub value: f64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LargeInteger {
pub low: u32,
pub high: u32,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ClassStructure {
pub name: String,
pub class_id: String,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ObjectArrayItem {
pub type_: String,
pub values: Vec<f64>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PathValue {
pub sig: String,
pub path: String,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ReferenceItem {
Property(String),
Class(ClassStructure),
Enumerated(String),
Offset(u32),
Identifier(i32),
Index(i32),
Name(String),
}
#[derive(Debug, Clone, PartialEq)]
pub enum DescriptorValue {
Reference(Vec<ReferenceItem>),
Descriptor(Descriptor),
List(Vec<DescriptorValue>),
Double(f64),
UnitDouble(UnitDoubleValue),
Text(String),
Enum(String),
Integer(i32),
LargeInteger(LargeInteger),
Boolean(bool),
Class(ClassStructure),
Alias(String),
RawData(Vec<u8>),
ObjectArray(Vec<ObjectArrayItem>),
Path(PathValue),
}
impl DescriptorValue {
pub fn os_type(&self) -> &'static str {
match self {
DescriptorValue::Reference(_) => "obj ",
DescriptorValue::Descriptor(_) => "Objc",
DescriptorValue::List(_) => "VlLs",
DescriptorValue::Double(_) => "doub",
DescriptorValue::UnitDouble(_) => "UntF",
DescriptorValue::Text(_) => "TEXT",
DescriptorValue::Enum(_) => "enum",
DescriptorValue::Integer(_) => "long",
DescriptorValue::LargeInteger(_) => "comp",
DescriptorValue::Boolean(_) => "bool",
DescriptorValue::Class(_) => "type",
DescriptorValue::Alias(_) => "alis",
DescriptorValue::RawData(_) => "tdta",
DescriptorValue::ObjectArray(_) => "ObAr",
DescriptorValue::Path(_) => "Pth ",
}
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Descriptor {
pub name: String,
pub class_id: String,
pub items: Vec<(String, DescriptorValue)>,
}
impl Descriptor {
pub fn new(name: impl Into<String>, class_id: impl Into<String>) -> Descriptor {
Descriptor {
name: name.into(),
class_id: class_id.into(),
items: Vec::new(),
}
}
pub fn set(&mut self, key: impl Into<String>, value: DescriptorValue) -> &mut Self {
self.items.push((key.into(), value));
self
}
pub fn get(&self, key: &str) -> Option<&DescriptorValue> {
self.items.iter().find(|(k, _)| k == key).map(|(_, v)| v)
}
}
use crate::psd::{Cmyk, Color, Frgb, Grayscale, Hsb, Lab, Rgb, Units, UnitsValue};
pub fn units_to_str(u: Units) -> &'static str {
match u {
Units::Pixels => "Pixels",
Units::Points => "Points",
Units::Picas => "Picas",
Units::Millimeters => "Millimeters",
Units::Centimeters => "Centimeters",
Units::Inches => "Inches",
Units::None => "None",
Units::Density => "Density",
}
}
pub fn units_from_str(s: &str) -> ReadResult<Units> {
Ok(match s {
"Pixels" => Units::Pixels,
"Points" => Units::Points,
"Picas" => Units::Picas,
"Millimeters" => Units::Millimeters,
"Centimeters" => Units::Centimeters,
"Inches" => Units::Inches,
"None" => Units::None,
"Density" => Units::Density,
other => {
return Err(ReadError::StrictViolation(format!("Invalid units: {other}")));
}
})
}
pub fn units_angle(value: f64) -> DescriptorValue {
DescriptorValue::UnitDouble(UnitDoubleValue {
units: "Angle".to_string(),
value,
})
}
pub fn units_value(x: Option<&UnitsValue>) -> DescriptorValue {
match x {
None => DescriptorValue::UnitDouble(UnitDoubleValue {
units: "Pixels".to_string(),
value: 0.0,
}),
Some(v) => DescriptorValue::UnitDouble(UnitDoubleValue {
units: units_to_str(v.units).to_string(),
value: v.value,
}),
}
}
pub fn parse_units(v: &DescriptorValue) -> ReadResult<UnitsValue> {
match v {
DescriptorValue::UnitDouble(u) => Ok(UnitsValue {
units: units_from_str(&u.units)?,
value: u.value,
}),
other => Err(ReadError::StrictViolation(format!(
"Invalid units value: {other:?}"
))),
}
}
pub fn parse_units_or_number(v: &DescriptorValue) -> ReadResult<UnitsValue> {
match v {
DescriptorValue::UnitDouble(u) => Ok(UnitsValue {
units: units_from_str(&u.units)?,
value: u.value,
}),
DescriptorValue::Double(d) => Ok(UnitsValue {
units: Units::Pixels,
value: *d,
}),
DescriptorValue::Integer(i) => Ok(UnitsValue {
units: Units::Pixels,
value: *i as f64,
}),
other => Err(ReadError::StrictViolation(format!(
"Invalid units-or-number value: {other:?}"
))),
}
}
pub fn parse_angle(v: &DescriptorValue) -> ReadResult<f64> {
match v {
DescriptorValue::UnitDouble(u) if u.units == "Angle" => Ok(u.value),
DescriptorValue::UnitDouble(u) => {
Err(ReadError::StrictViolation(format!("Invalid units: {}", u.units)))
}
_ => Ok(0.0),
}
}
pub fn parse_percent(v: &DescriptorValue) -> ReadResult<f64> {
match v {
DescriptorValue::UnitDouble(u) if u.units == "Percent" => Ok(u.value / 100.0),
DescriptorValue::UnitDouble(u) => {
Err(ReadError::StrictViolation(format!("Invalid units: {}", u.units)))
}
_ => Ok(1.0),
}
}
pub fn parse_percent_or_angle(v: &DescriptorValue) -> ReadResult<f64> {
match v {
DescriptorValue::UnitDouble(u) if u.units == "Percent" => Ok(u.value / 100.0),
DescriptorValue::UnitDouble(u) if u.units == "Angle" => Ok(u.value / 360.0),
DescriptorValue::UnitDouble(u) => {
Err(ReadError::StrictViolation(format!("Invalid units: {}", u.units)))
}
_ => Ok(1.0),
}
}
fn color_double(d: &Descriptor, key: &str) -> f64 {
match d.get(key) {
Some(DescriptorValue::Double(v)) => *v,
Some(DescriptorValue::Integer(v)) => *v as f64,
_ => 0.0,
}
}
pub fn parse_color(d: &Descriptor) -> ReadResult<Color> {
if let Some(h) = d.get("H ") {
let h = parse_percent_or_angle(h)?;
Ok(Color::Hsb(Hsb {
h,
s: color_double(d, "Strt"),
b: color_double(d, "Brgh"),
}))
} else if d.get("Rd ").is_some() {
Ok(Color::Rgb(Rgb {
r: color_double(d, "Rd "),
g: color_double(d, "Grn "),
b: color_double(d, "Bl "),
}))
} else if d.get("Cyn ").is_some() {
Ok(Color::Cmyk(Cmyk {
c: color_double(d, "Cyn "),
m: color_double(d, "Mgnt"),
y: color_double(d, "Ylw "),
k: color_double(d, "Blck"),
}))
} else if d.get("Gry ").is_some() {
Ok(Color::Grayscale(Grayscale {
k: color_double(d, "Gry "),
}))
} else if d.get("Lmnc").is_some() {
Ok(Color::Lab(Lab {
l: color_double(d, "Lmnc"),
a: color_double(d, "A "),
b: color_double(d, "B "),
}))
} else if d.get("redFloat").is_some() {
Ok(Color::Frgb(Frgb {
fr: color_double(d, "redFloat"),
fg: color_double(d, "greenFloat"),
fb: color_double(d, "blueFloat"),
}))
} else {
Err(ReadError::StrictViolation(
"Unsupported color descriptor".to_string(),
))
}
}
pub fn serialize_color(color: Option<&Color>) -> Descriptor {
let mut d;
match color {
None => {
d = Descriptor::new("", "RGBC");
d.set("Rd ", DescriptorValue::Double(0.0));
d.set("Grn ", DescriptorValue::Double(0.0));
d.set("Bl ", DescriptorValue::Double(0.0));
}
Some(Color::Rgb(c)) => {
d = Descriptor::new("", "RGBC");
d.set("Rd ", DescriptorValue::Double(c.r));
d.set("Grn ", DescriptorValue::Double(c.g));
d.set("Bl ", DescriptorValue::Double(c.b));
}
Some(Color::Rgba(c)) => {
d = Descriptor::new("", "RGBC");
d.set("Rd ", DescriptorValue::Double(c.r));
d.set("Grn ", DescriptorValue::Double(c.g));
d.set("Bl ", DescriptorValue::Double(c.b));
}
Some(Color::Frgb(c)) => {
d = Descriptor::new("", "RGBC");
d.set("redFloat", DescriptorValue::Double(c.fr));
d.set("greenFloat", DescriptorValue::Double(c.fg));
d.set("blueFloat", DescriptorValue::Double(c.fb));
}
Some(Color::Hsb(c)) => {
d = Descriptor::new("", "HSBC");
d.set("H ", units_angle(c.h * 360.0));
d.set("Strt", DescriptorValue::Double(c.s));
d.set("Brgh", DescriptorValue::Double(c.b));
}
Some(Color::Cmyk(c)) => {
d = Descriptor::new("", "CMYC");
d.set("Cyn ", DescriptorValue::Double(c.c));
d.set("Mgnt", DescriptorValue::Double(c.m));
d.set("Ylw ", DescriptorValue::Double(c.y));
d.set("Blck", DescriptorValue::Double(c.k));
}
Some(Color::Lab(c)) => {
d = Descriptor::new("", "LABC");
d.set("Lmnc", DescriptorValue::Double(c.l));
d.set("A ", DescriptorValue::Double(c.a));
d.set("B ", DescriptorValue::Double(c.b));
}
Some(Color::Grayscale(c)) => {
d = Descriptor::new("", "GRYC");
d.set("Gry ", DescriptorValue::Double(c.k));
}
}
d
}
pub fn read_ascii_string_or_class_id(reader: &mut PsdReader) -> ReadResult<String> {
let length = read_int32(reader)?;
let len = if length == 0 { 4 } else { length as usize };
read_ascii_string(reader, len)
}
pub fn write_ascii_string_or_class_id(writer: &mut PsdWriter, value: &str) {
let char_count = value.chars().count();
if char_count == 4 && value != "warp" && value != "time" && value != "hold" && value != "list"
{
write_int32(writer, 0);
write_signature(writer, value);
} else {
write_int32(writer, char_count as i32);
for ch in value.chars() {
write_uint8(writer, (ch as u32) as u8);
}
}
}
pub fn read_class_structure(reader: &mut PsdReader) -> ReadResult<ClassStructure> {
let name = read_unicode_string(reader)?;
let class_id = read_ascii_string_or_class_id(reader)?;
Ok(ClassStructure { name, class_id })
}
pub fn write_class_structure(writer: &mut PsdWriter, name: &str, class_id: &str) {
write_unicode_string(writer, name);
write_ascii_string_or_class_id(writer, class_id);
}
pub fn read_reference_structure(reader: &mut PsdReader) -> ReadResult<Vec<ReferenceItem>> {
let items_count = read_int32(reader)?;
let mut items = Vec::new();
for _ in 0..items_count {
let type_ = read_signature(reader)?;
match type_.as_str() {
"prop" => {
read_class_structure(reader)?;
let key_id = read_ascii_string_or_class_id(reader)?;
items.push(ReferenceItem::Property(key_id));
}
"Clss" => {
items.push(ReferenceItem::Class(read_class_structure(reader)?));
}
"Enmr" => {
read_class_structure(reader)?;
let type_id = read_ascii_string_or_class_id(reader)?;
let value = read_ascii_string_or_class_id(reader)?;
items.push(ReferenceItem::Enumerated(format!("{}.{}", type_id, value)));
}
"rele" => {
read_class_structure(reader)?;
items.push(ReferenceItem::Offset(read_uint32(reader)?));
}
"Idnt" => {
items.push(ReferenceItem::Identifier(read_int32(reader)?));
}
"indx" => {
items.push(ReferenceItem::Index(read_int32(reader)?));
}
"name" => {
read_class_structure(reader)?;
items.push(ReferenceItem::Name(read_unicode_string(reader)?));
}
other => {
return Err(ReadError::StrictViolation(format!(
"Invalid descriptor reference type: {}",
other
)));
}
}
}
Ok(items)
}
pub fn write_reference_structure(writer: &mut PsdWriter, items: &[ReferenceItem]) {
write_int32(writer, items.len() as i32);
for item in items {
match item {
ReferenceItem::Enumerated(value) => {
write_signature(writer, "Enmr");
let mut parts = value.splitn(2, '.');
let type_id = parts.next().unwrap_or("");
let enum_value = parts.next().unwrap_or("");
write_class_structure(writer, "\0", type_id);
write_ascii_string_or_class_id(writer, type_id);
write_ascii_string_or_class_id(writer, enum_value);
}
ReferenceItem::Name(value) => {
write_signature(writer, "name");
write_class_structure(writer, "\0", "Lyr ");
write_unicode_string(writer, &format!("{}\0", value));
}
other => {
panic!("Invalid descriptor reference type for writing: {:?}", other);
}
}
}
}
pub fn read_os_type(reader: &mut PsdReader, type_: &str) -> ReadResult<DescriptorValue> {
match type_ {
"obj " => Ok(DescriptorValue::Reference(read_reference_structure(reader)?)),
"Objc" | "GlbO" => Ok(DescriptorValue::Descriptor(read_descriptor_structure(
reader,
)?)),
"VlLs" => {
let length = read_int32(reader)?;
let mut items = Vec::new();
for _ in 0..length {
let item_type = read_signature(reader)?;
items.push(read_os_type(reader, &item_type)?);
}
Ok(DescriptorValue::List(items))
}
"doub" => Ok(DescriptorValue::Double(read_float64(reader)?)),
"UntF" => {
let units_code = read_signature(reader)?;
let value = read_float64(reader)?;
let units = units_name_from_code(&units_code)
.ok_or_else(|| ReadError::StrictViolation(format!("Invalid units: {}", units_code)))?;
Ok(DescriptorValue::UnitDouble(UnitDoubleValue {
units: units.to_string(),
value,
}))
}
"UnFl" => {
let units_code = read_signature(reader)?;
let value = read_float32(reader)? as f64;
let units = units_name_from_code(&units_code)
.ok_or_else(|| ReadError::StrictViolation(format!("Invalid units: {}", units_code)))?;
Ok(DescriptorValue::UnitDouble(UnitDoubleValue {
units: units.to_string(),
value,
}))
}
"TEXT" => Ok(DescriptorValue::Text(read_unicode_string(reader)?)),
"enum" => {
let enum_type = read_ascii_string_or_class_id(reader)?;
let value = read_ascii_string_or_class_id(reader)?;
Ok(DescriptorValue::Enum(format!("{}.{}", enum_type, value)))
}
"long" => Ok(DescriptorValue::Integer(read_int32(reader)?)),
"comp" => {
let low = read_uint32(reader)?;
let high = read_uint32(reader)?;
Ok(DescriptorValue::LargeInteger(LargeInteger { low, high }))
}
"bool" => Ok(DescriptorValue::Boolean(read_uint8(reader)? != 0)),
"type" | "GlbC" => Ok(DescriptorValue::Class(read_class_structure(reader)?)),
"alis" => {
let length = read_int32(reader)?;
Ok(DescriptorValue::Alias(read_ascii_string(
reader,
length as usize,
)?))
}
"tdta" => {
let length = read_int32(reader)?;
Ok(DescriptorValue::RawData(read_bytes(reader, length as usize)?))
}
"ObAr" => {
let _version = read_int32(reader)?; let _name = read_unicode_string(reader)?; let _type = read_ascii_string_or_class_id(reader)?; let length = read_int32(reader)?;
let mut items = Vec::new();
for _ in 0..length {
let type1 = read_ascii_string_or_class_id(reader)?; let _unfl = read_signature(reader)?; let _units = read_signature(reader)?; let values_count = read_int32(reader)?;
let mut values = Vec::new();
for _ in 0..values_count {
values.push(read_float64(reader)?);
}
items.push(ObjectArrayItem {
type_: type1,
values,
});
}
Ok(DescriptorValue::ObjectArray(items))
}
"Pth " => {
let _length = read_int32(reader)?; let sig = read_signature(reader)?;
let _path_size = read_int32_le(reader)?; let chars_count = read_int32_le(reader)?;
let path = read_unicode_string_with_length_le(reader, chars_count as usize)?;
Ok(DescriptorValue::Path(PathValue { sig, path }))
}
other => Err(ReadError::StrictViolation(format!(
"Invalid TySh descriptor OSType: {} at {:x}",
other, reader.offset
))),
}
}
fn ob_ar_type_for_key(key: &str) -> Option<&'static str> {
match key {
"meshPoints" => Some("rationalPoint"),
"quiltSliceX" => Some("UntF"),
"quiltSliceY" => Some("UntF"),
_ => None,
}
}
pub fn write_os_type(writer: &mut PsdWriter, value: &DescriptorValue, key: &str) {
match value {
DescriptorValue::Reference(items) => {
write_reference_structure(writer, items);
}
DescriptorValue::Descriptor(desc) => {
write_descriptor_structure(writer, desc);
}
DescriptorValue::List(items) => {
write_int32(writer, items.len() as i32);
for item in items {
write_signature(writer, item.os_type());
write_os_type(writer, item, &format!("{}[]", key));
}
}
DescriptorValue::Double(v) => {
write_float64(writer, *v);
}
DescriptorValue::UnitDouble(u) => {
let code = units_code_from_name(&u.units)
.unwrap_or_else(|| panic!("Invalid units: {} in {}", u.units, key));
write_signature(writer, code);
write_float64(writer, u.value);
}
DescriptorValue::Text(v) => {
write_unicode_string_with_padding(writer, v);
}
DescriptorValue::Enum(v) => {
let mut parts = v.splitn(2, '.');
let type_ = parts.next().unwrap_or("");
let val = parts.next().unwrap_or("");
write_ascii_string_or_class_id(writer, type_);
write_ascii_string_or_class_id(writer, val);
}
DescriptorValue::Integer(v) => {
write_int32(writer, *v);
}
DescriptorValue::LargeInteger(li) => {
write_uint32(writer, li.low);
write_uint32(writer, li.high);
}
DescriptorValue::Boolean(v) => {
write_uint8(writer, if *v { 1 } else { 0 });
}
DescriptorValue::Class(c) => {
write_class_structure(writer, &c.name, &c.class_id);
}
DescriptorValue::Alias(s) => {
write_int32(writer, s.chars().count() as i32);
for ch in s.chars() {
write_uint8(writer, (ch as u32) as u8);
}
}
DescriptorValue::RawData(bytes) => {
write_int32(writer, bytes.len() as i32);
write_bytes(writer, Some(bytes));
}
DescriptorValue::ObjectArray(items) => {
write_int32(writer, 16); write_unicode_string_with_padding(writer, ""); let type_ = ob_ar_type_for_key(key)
.unwrap_or_else(|| panic!("Not implemented ObArType for: {}", key));
write_ascii_string_or_class_id(writer, type_);
write_int32(writer, items.len() as i32);
for item in items {
write_ascii_string_or_class_id(writer, &item.type_); write_signature(writer, "UnFl");
write_signature(writer, "#Pxl");
write_int32(writer, item.values.len() as i32);
for v in &item.values {
write_float64(writer, *v);
}
}
}
DescriptorValue::Path(p) => {
let length = 4 + 4 + 4 + p.path.chars().count() as i32 * 2;
write_int32(writer, length);
write_signature(writer, &p.sig);
write_int32_le(writer, length);
write_int32_le(writer, p.path.chars().count() as i32);
write_unicode_string_without_length_le(writer, &p.path);
}
}
}
pub fn read_descriptor_structure(reader: &mut PsdReader) -> ReadResult<Descriptor> {
let class = read_class_structure(reader)?;
let mut desc = Descriptor {
name: class.name,
class_id: class.class_id,
items: Vec::new(),
};
let items_count = read_uint32(reader)?;
for _ in 0..items_count {
let key = read_ascii_string_or_class_id(reader)?;
let type_ = read_signature(reader)?;
let data = read_os_type(reader, &type_)?;
desc.items.push((key, data));
}
Ok(desc)
}
pub fn write_descriptor_structure(writer: &mut PsdWriter, desc: &Descriptor) {
write_unicode_string_with_padding(writer, &desc.name);
write_ascii_string_or_class_id(writer, &desc.class_id);
write_uint32(writer, desc.items.len() as u32);
for (key, value) in &desc.items {
write_ascii_string_or_class_id(writer, key);
write_signature(writer, value.os_type());
write_os_type(writer, value, key);
}
}
pub fn read_descriptor(reader: &mut PsdReader) -> ReadResult<Descriptor> {
read_descriptor_structure(reader)
}
pub fn write_descriptor(writer: &mut PsdWriter, desc: &Descriptor) {
write_descriptor_structure(writer, desc);
}
pub fn read_version_and_descriptor(reader: &mut PsdReader) -> ReadResult<Descriptor> {
let version = read_uint32(reader)?;
if version != 16 {
return Err(ReadError::StrictViolation(format!(
"Invalid descriptor version: {}",
version
)));
}
read_descriptor_structure(reader)
}
pub fn write_version_and_descriptor(writer: &mut PsdWriter, desc: &Descriptor) {
write_uint32(writer, 16); write_descriptor_structure(writer, desc);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::writer::{create_writer, get_writer_buffer};
fn build_sample() -> Descriptor {
let mut nested = Descriptor::new("", "Pnt ");
nested.set("Hrzn", DescriptorValue::Double(12.5));
nested.set("Vrtc", DescriptorValue::Double(-7.0));
let list = DescriptorValue::List(vec![
DescriptorValue::Integer(1),
DescriptorValue::Integer(2),
DescriptorValue::Integer(3),
]);
let mut desc = Descriptor::new("layer name", "TxLr");
desc.set("Txt ", DescriptorValue::Text("Héllo 𝕎orld".to_string()));
desc.set("dbl ", DescriptorValue::Double(3.5));
desc.set(
"Opct",
DescriptorValue::UnitDouble(UnitDoubleValue {
units: "Percent".to_string(),
value: 75.0,
}),
);
desc.set("Ornt", DescriptorValue::Enum("Ornt.Hrzn".to_string()));
desc.set("long", DescriptorValue::Integer(-42));
desc.set("bool", DescriptorValue::Boolean(true));
desc.set("Objc", DescriptorValue::Descriptor(nested));
desc.set("VlLs", list);
desc
}
#[test]
fn round_trip_descriptor() {
let desc = build_sample();
let mut writer = create_writer(256);
write_descriptor(&mut writer, &desc);
let bytes = get_writer_buffer(&writer);
let mut reader = PsdReader::new(&bytes, None, None);
let read = read_descriptor(&mut reader).expect("read");
assert_eq!(read.name, "layer name");
assert_eq!(read.class_id, "TxLr");
assert_eq!(read, desc);
assert_eq!(reader.offset, bytes.len());
}
#[test]
fn round_trip_version_and_descriptor() {
let desc = build_sample();
let mut writer = create_writer(256);
write_version_and_descriptor(&mut writer, &desc);
let bytes = get_writer_buffer(&writer);
let mut reader = PsdReader::new(&bytes, None, None);
let read = read_version_and_descriptor(&mut reader).expect("read");
assert_eq!(read, desc);
}
#[test]
fn field_order_preserved() {
let desc = build_sample();
let expected_keys: Vec<&str> = desc.items.iter().map(|(k, _)| k.as_str()).collect();
let mut writer = create_writer(256);
write_descriptor(&mut writer, &desc);
let bytes = get_writer_buffer(&writer);
let mut reader = PsdReader::new(&bytes, None, None);
let read = read_descriptor(&mut reader).expect("read");
let read_keys: Vec<&str> = read.items.iter().map(|(k, _)| k.as_str()).collect();
assert_eq!(read_keys, expected_keys);
}
#[test]
fn unit_float_round_trip() {
let value = UnitDoubleValue {
units: "Pixels".to_string(),
value: 10.0,
};
let mut writer = create_writer(64);
write_signature(&mut writer, "#Pxl");
write_float64(&mut writer, value.value);
let bytes = get_writer_buffer(&writer);
let mut reader = PsdReader::new(&bytes, None, None);
let v = read_os_type(&mut reader, "UntF").expect("read untf");
assert_eq!(v, DescriptorValue::UnitDouble(value));
}
#[test]
fn ascii_string_or_class_id_encoding() {
let mut w1 = create_writer(32);
write_ascii_string_or_class_id(&mut w1, "TxLr");
let b1 = get_writer_buffer(&w1);
assert_eq!(&b1[0..4], &[0, 0, 0, 0]);
assert_eq!(&b1[4..8], b"TxLr");
let mut w2 = create_writer(32);
write_ascii_string_or_class_id(&mut w2, "list");
let b2 = get_writer_buffer(&w2);
assert_eq!(&b2[0..4], &[0, 0, 0, 4]);
assert_eq!(&b2[4..8], b"list");
for s in ["TxLr", "list", "longerKey"] {
let mut w = create_writer(32);
write_ascii_string_or_class_id(&mut w, s);
let b = get_writer_buffer(&w);
let mut r = PsdReader::new(&b, None, None);
assert_eq!(read_ascii_string_or_class_id(&mut r).unwrap(), s);
}
}
#[test]
fn raw_data_and_large_integer_round_trip() {
let mut desc = Descriptor::new("", "test");
desc.set("data", DescriptorValue::RawData(vec![1, 2, 3, 4, 5]));
desc.set(
"big ",
DescriptorValue::LargeInteger(LargeInteger {
low: 0xDEAD_BEEF,
high: 0x1234_5678,
}),
);
let mut writer = create_writer(64);
write_descriptor(&mut writer, &desc);
let bytes = get_writer_buffer(&writer);
let mut reader = PsdReader::new(&bytes, None, None);
let read = read_descriptor(&mut reader).expect("read");
assert_eq!(read, desc);
}
#[test]
fn units_table_complete() {
assert_eq!(UNITS_MAP.len(), 11);
for (code, name) in UNITS_MAP {
assert_eq!(units_name_from_code(code), Some(*name));
assert_eq!(units_code_from_name(name), Some(*code));
}
}
}