rawpsd/lib.rs
1//! rawpsd is a library that handles loading PSD data into a list of minimally-processed in-memory structs. It does not have any opinions about what features PSD files should or do use, or how to interpret those features. Compressed data is decompressed, and some redundant pieces of data like ascii and unicode names stored together are only returned once instead of twice, but aside from things like that, rawpsd is minimally opinionated and tries to just tell you what the PSD file itself says. For example, strings are left as strings instead of being transformed into enums.
2//!
3//! Comparison with other crates:
4//! - `psd`: The `psd` crate's API makes it impossible to figure out the exact layer group hierarchy, so you can only use it on very simple PSDs.
5//! - `zune-psd`: Doesn't actually support the psd format, just gets the embedded thumbnail.
6//!
7//! rawpsd draws a compatibility support line at Photoshop CS6, the last non-subscription version of Photoshop. Features only supported by newer versions are unlikely to be supported.
8//!
9//! rawpsd currently only supports 8-bit RGB, CMYK, and Grayscale PSDs. This is the vast majority of PSD files that can be found in the wild. It does not yet support the large document PSB format variant.
10//!
11//! rawpsd's docs do not document the entire PSD format, not even its capabilities. You will need to occasionally reference <https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/> and manually poke at PSD files in a hex editor to take full advantage of rawpsd.
12//!
13//! You want [parse_layer_records] and [parse_psd_metadata].
14//!
15//! Example:
16//!
17//!```rs
18//!let data = std::fs::read("data/test.psd").expect("Failed to open test.psd");
19//!
20//!if let Ok(layers) = parse_layer_records(&data)
21//!{
22//! for mut layer in layers
23//! {
24//! // Don't spew tons of image data bytes to stdout; we just want to see the metadata.
25//! layer.image_data_rgba = vec!();
26//! layer.image_data_k = vec!();
27//! layer.image_data_mask = vec!();
28//! println!("{:?}", layer);
29//! }
30//!}
31//!```
32
33#![allow(clippy::vec_init_then_push)] // wrong problem domain. more readable this way
34#![allow(clippy::manual_range_contains)] // bad idiom
35#![allow(clippy::field_reassign_with_default)] // bad idiom
36
37#![cfg_attr(not(any(test, feature = "serde_support", feature = "debug_spew")), no_std)]
38extern crate alloc;
39use alloc::string::{String, ToString};
40use alloc::vec;
41use alloc::vec::Vec;
42use alloc::boxed::Box;
43use alloc::format;
44
45#[derive(Clone, Debug, Default)]
46struct SliceCursor<'a>
47{
48 pub (crate) buf : &'a [u8],
49 pub (crate) pos : usize,
50}
51
52impl<'a> SliceCursor<'a>
53{
54 pub (crate) fn new(buf : &'a [u8]) -> Self
55 {
56 Self { buf, pos: 0 }
57 }
58
59 pub (crate) fn position(&self) -> u64 { self.pos as u64 }
60 pub (crate) fn set_position(&mut self, pos : u64) { self.pos = pos as usize }
61
62 pub (crate) fn read_exact(&mut self, out : &mut [u8]) -> Result<(), String>
63 {
64 let remaining = self.buf.len().saturating_sub(self.pos);
65 if out.len() > remaining
66 {
67 return Err("Unexpeted end of stream".to_string());
68 }
69 out.copy_from_slice(&self.buf[self.pos..self.pos + out.len()]);
70 self.pos += out.len();
71 Ok(())
72 }
73
74 pub (crate) fn read_to_end(&mut self, out : &mut Vec<u8>) -> Result<usize, String>
75 {
76 let remaining = self.buf.len().saturating_sub(self.pos);
77 out.reserve(remaining);
78 out.extend_from_slice(&self.buf[self.pos..]);
79 self.pos = self.buf.len();
80 Ok(remaining)
81 }
82
83 pub fn take(&mut self, n : u64) -> Self
84 {
85 Self { buf : &self.buf[self.pos..self.pos + n as usize], pos : 0 }
86 }
87
88 pub fn take_rest(&mut self) -> Self
89 {
90 Self { buf : &self.buf[self.pos..], pos : 0 }
91 }
92
93}
94
95use alloc::collections::BTreeMap;
96
97/// PSD Class Descriptor object data. Only used by certain PSD features.
98///
99/// Some PSD format features use a dynamic meta-object format instead of feature-specific data encoding; that information is what this type is responsible for holding.
100#[non_exhaustive]
101#[derive(Clone, Debug, Default)]
102pub enum DescItem
103{
104 #[allow(non_camel_case_types)]
105 long(i32),
106 #[allow(non_camel_case_types)]
107 doub(f64),
108 /// Float that carries unit system metadata. The string specifies the unit system. Examples of unit systems are "#Ang" and "#Pxl".
109 UntF(String, f64),
110 #[allow(non_camel_case_types)]
111 bool(bool),
112 TEXT(String),
113 /// When rawpsd ran into an error while parsing the data that goes here: what kind of error was it?
114 Err(String),
115 /// Entire sub-object.
116 Objc(Box<Descriptor>),
117 #[allow(non_camel_case_types)]
118 /// Enums, which are stringly typed in PSDs.
119 _enum(String, String),
120 /// Variable-length list.
121 VlLs(Vec<DescItem>),
122 /// Dummy non-data data.
123 #[default] Xxx
124}
125
126impl DescItem
127{
128 /// Get the given item if the enum is of that kind, otherwise panic.
129 pub fn long(&self) -> i32 { match self { DescItem::long(x) => *x, _ => panic!(), } }
130 /// Get the given item if the enum is of that kind, otherwise panic.
131 pub fn doub(&self) -> f64 { match self { DescItem::doub(x) => *x, _ => panic!(), } }
132 /// Get the given item if the enum is of that kind, otherwise panic.
133 pub fn bool(&self) -> bool { match self { DescItem::bool(x) => *x, _ => panic!(), } }
134 /// Get the given item if the enum is of that kind, otherwise panic.
135 pub fn _enum(&self) -> (String, String) { match self { DescItem::_enum(y, x) => (y.clone(), x.clone()), _ => panic!(), } }
136 #[allow(non_snake_case)]
137 /// Get the given item if the enum is of that kind, otherwise panic.
138 pub fn UntF(&self) -> (String, f64) { match self { DescItem::UntF(y, x) => (y.clone(), *x), _ => panic!(), } }
139 #[allow(non_snake_case)]
140 /// Get the given item if the enum is of that kind, otherwise panic.
141 pub fn Objc(&self) -> Box<Descriptor> { match self { DescItem::Objc(x) => x.clone(), _ => panic!(), } }
142 #[allow(non_snake_case)]
143 /// Get the given item if the enum is of that kind, otherwise panic.
144 pub fn TEXT(&self) -> String { match self { DescItem::TEXT(x) => x.clone(), _ => panic!(), } }
145 #[allow(non_snake_case)]
146 /// Get the given item if the enum is of that kind, otherwise panic.
147 pub fn VlLs(&self) -> Vec<DescItem> { match self { DescItem::VlLs(x) => x.clone(), _ => panic!(), } }
148}
149
150type Descriptor = (String, Vec<(String, DescItem)>);
151
152#[cfg(feature = "serde_support")]
153use serde::{Serialize, Deserialize};
154#[cfg(feature = "serde_support")]
155#[derive(Clone, Debug, Default, Serialize, Deserialize)]
156/// Metadata about where a mask attached to an object physically is and how to interpret it.
157///
158/// Stability promise: Every field in this struct will always be public. This struct is safe to initialize with `{ ..., ..Default::default() }`.
159///
160/// This struct is general purpose enough that you might want to use it in your code directly instead of making a newtype. If you do, and you need to serde it, enable the `serde_support` feature. The serde format of this struct is not guaranteed to be stable between minor versions or patch versions; if you use the `serde_support` feature and need to ensure compatibility between different builds of your code, pin `rawpsd` to a specific exact version. Otherwise, make a newtype.
161#[non_exhaustive]
162pub struct MaskInfo {
163 /// Mask global X position.
164 pub x : i32,
165 /// Mask global Y position.
166 pub y : i32,
167 /// Mask image data width.
168 pub w : u32,
169 /// Mask image data height.
170 pub h : u32,
171 /// Default color of the mask outside of its explicit image area. Must be 0 or 255.
172 pub default_color : u8,
173 /// Is the mask flagged as moving along with the layer it's attached to? Does not affect the encoding of the x/y coordinates. I think.
174 pub relative : bool,
175 /// Is the mask disabled, i.e. marked as currently having no effect?
176 pub disabled : bool,
177 /// Is the mask marked as inverted, i.e. the default color and image data should be treated as being the opposite color (white <-> black)?
178 pub invert : bool,
179}
180
181#[cfg(not(feature = "serde_support"))]
182#[derive(Clone, Debug, Default)]
183/// Metadata about where a mask attached to an object physically is and how to interpret it.
184///
185/// Stability promise: Every field in this struct will always be public. This struct is safe to initialize with `{ ..., ..Default::default() }`.
186///
187/// This struct is general purpose enough that you might want to use it in your code directly instead of making a newtype. If you do, and you need to serde it, enable the `serde_support` feature. The serde format of this struct is not guaranteed to be stable between minor versions or patch versions; if you use the `serde_support` feature and need to ensure compatibility between different builds of your code, pin `rawpsd` to a specific exact version. Otherwise, make a newtype.
188#[non_exhaustive]
189pub struct MaskInfo {
190 /// Mask global X position.
191 pub x : i32,
192 /// Mask global Y position.
193 pub y : i32,
194 /// Mask image data width.
195 pub w : u32,
196 /// Mask image data height.
197 pub h : u32,
198 /// Default color of the mask outside of its explicit image area. Must be 0 or 255.
199 pub default_color : u8,
200 /// Is the mask flagged as moving along with the layer it's attached to? Does not affect the encoding of the x/y coordinates. I think.
201 pub relative : bool,
202 /// Is the mask disabled, i.e. marked as currently having no effect?
203 pub disabled : bool,
204 /// Is the mask marked as inverted, i.e. the default color and image data should be treated as being the opposite color (white <-> black)?
205 pub invert : bool,
206}
207
208/// Dummy struct to keep the main docs from being bloated. See [LayerInfo::blend_mode].
209///
210/// Normal blend modes:
211/// ```text
212/// "pass" => "Normal", // "Pass through" mode for groups. Does not behave as a normal blend mode. Affects composition pipeline behavior.
213/// "norm" => "Normal",
214/// "diss" => "Dissolve",
215/// "dark" => "Darken",
216/// "mul " => "Multiply",
217/// "idiv" => "Color Burn",
218/// "lbrn" => "Linear Burn",
219/// "dkCl" => "Darken",
220/// "lite" => "Lighten",
221/// "scrn" => "Screen",
222/// "div " => "Color Dodge",
223/// "lddg" => "Add",
224/// "lgCl" => "Lighten",
225/// "over" => "Overlay",
226/// "sLit" => "Soft Light",
227/// "hLit" => "Hard Light",
228/// "vLit" => "Vivid Light",
229/// "lLit" => "Linear Light",
230/// "pLit" => "Pin Light",
231/// "hMix" => "Hard Mix",
232/// "diff" => "Difference",
233/// "smud" => "Exclusion",
234/// "fsub" => "Subtract",
235/// "fdiv" => "Divide",
236/// "hue " => "Hue",
237/// "sat " => "Saturation",
238/// "colr" => "Color",
239/// "lum " => "Luminosity",
240/// _ => "Normal",
241/// ```
242/// Blend modes as found in certain Class Descriptor objects in certain effect/filter-related features:
243/// ```text
244/// "Nrml" => "Normal",
245/// "Dslv" => "Dissolve",
246/// "Drkn" => "Darken",
247/// "Mltp" => "Multiply",
248/// "CBrn" => "Color Burn",
249/// "linearBurn" => "Linear Burn",
250/// "darkerColor" => "Darken",
251/// "Lghn" => "Lighten",
252/// "Scrn" => "Screen",
253/// "CDdg" => "Color Dodge",
254/// "linearDodge" => "Add",
255/// "lighterColor" => "Lighten",
256/// "Ovrl" => "Overlay",
257/// "SftL" => "Soft Light",
258/// "HrdL" => "Hard Light",
259/// "vividLight" => "Vivid Light",
260/// "linearLight" => "Linear Light",
261/// "pinLight" => "Pin Light",
262/// "hardMix" => "Hard Mix",
263/// "Dfrn" => "Difference",
264/// "Xclu" => "Exclusion",
265/// "blendSubtraction" => "Subtract",
266/// "blendDivide" => "Divide",
267/// "H " => "Hue",
268/// "Strt" => "Saturation",
269/// "Clr " => "Color",
270/// "Lmns" => "Luminosity",
271/// _ => "Normal",
272///```
273pub struct BlendModeDocs { _no_init : core::marker::PhantomData<()>, }
274
275#[non_exhaustive]
276#[derive(Clone, Debug, Default)]
277/// Describes a single layer stack entry.
278///
279/// This data is very unorganized, and you should not use it directly in your application. You should move it out into your own types.
280///
281/// Returned from [parse_layer_records].
282pub struct LayerInfo {
283 /// Name of the layer.
284 pub name : String,
285 /// Normal opacity of the layer.
286 pub opacity : f32,
287 /// Photoshop has separate "opacity" and "fill" sliders.
288 ///
289 /// Fill opacity behaves differently for certain blend modes and when layer effects are involved.
290 pub fill_opacity : f32,
291 /// Blend mode stored as a string. See [BlendModeDocs].
292 pub blend_mode : String,
293 /// Global X position of the layer, based on the top left of the canvas. Can be negative. Ignored for groups.
294 pub x : i32,
295 /// Global Y position of the layer, based on the top left of the canvas. Can be negative. Ignored for groups.
296 pub y : i32,
297 /// Layer image data width.
298 pub w : u32,
299 /// Layer image data height.
300 pub h : u32,
301 /// Number of channels in the image data.
302 pub image_channel_count : u16,
303 /// Four channels worth of image data. Can be RGBA or CMYA, sometimes with fewer channels. This is non-planar: a single full RGBA pixel is 4 consecutive bytes.
304 pub image_data_rgba : Vec<u8>,
305 /// The K channel of CMYK image data, if present.
306 pub image_data_k : Vec<u8>,
307 /// Whether the second channel of the RGBA data came from the PSD file (true) or was synthesized (false).
308 ///
309 /// If the PSD is malformed and has multiple channels of the same type, this flag might be incorrect.
310 /// But on well-formed PSDs, it's always correct.
311 pub image_data_has_g : bool,
312 /// Whether the third channel of the RGBA data came from the PSD file (true) or was synthesized (false).
313 ///
314 /// If the PSD is malformed and has multiple channels of the same type, this flag might be incorrect.
315 /// But on well-formed PSDs, it's always correct.
316 pub image_data_has_b : bool,
317 /// Whether the fourth channel of the RGBA data came from the PSD file (true) or was synthesized (false).
318 ///
319 /// If the PSD is malformed and has multiple channels of the same type, this flag might be incorrect.
320 /// But on well-formed PSDs, it's always correct.
321 pub image_data_has_a : bool,
322 /// Number of channels in the mask image data. They are stored planar (all of ch1, then all of ch2, etc), not interleaved like RGBA.
323 pub mask_channel_count : u16,
324 /// Where is the mask, and how do you interpret it?
325 pub mask_info : MaskInfo,
326 // TODO
327 //pub global_mask_opacity : u16,
328 //pub global_mask_kind : u16,
329 /// Actual mask data. Again, this is planar, unlike RGBA.
330 pub image_data_mask : Vec<u8>,
331 /// If this is a group opener, is the group expanded?
332 pub group_expanded : bool,
333 /// Is this a group opener?
334 pub group_opener : bool,
335 /// Is this a group closer?
336 pub group_closer : bool,
337 /// PSD layers have a "transparency shapes layer" flag. This is the inverse of that flag, i.e. "true" means "the transparency-shapes-layer flag is disabled". This flag state is funny and does weird things to some blend modes and layer effects.
338 pub funny_flag : bool,
339 /// Does this layer have the "clipping mask" flag enabled?
340 pub is_clipped : bool,
341 /// Is this layer alpha locked?
342 pub is_alpha_locked : bool,
343 /// Is this layer visible?
344 pub is_visible : bool,
345 /// Is this an adjustment layer, and if so, what kind? Blank if not an adjustment layer.
346 pub adjustment_type : String,
347 /// Pile of raw, flattened adjustment layer metadata. Search `// Read adjustment data.` in the [source code](https://docs.rs/crate/rawpsd/latest/source/src/lib.rs) and read down from there to see how each adjustment's data is flattened.
348 pub adjustment_info : Vec<f32>,
349 /// Some adjustments use class descriptors instead of "hardcoded" data. Those adjustments get their data here.
350 pub adjustment_desc : Option<Descriptor>,
351 /// What effects, if any, does this layer have attached to it?
352 pub effects_desc : Option<Descriptor>,
353}
354
355fn read_u8(cursor: &mut SliceCursor) -> Result<u8, String>
356{
357 let mut buf = [0; 1];
358 cursor.read_exact(&mut buf).map_err(|x| x.to_string())?;
359 Ok(buf[0])
360}
361
362fn read_u16(cursor: &mut SliceCursor) -> Result<u16, String>
363{
364 let mut buf = [0; 2];
365 cursor.read_exact(&mut buf).map_err(|x| x.to_string())?;
366 Ok(u16::from_be_bytes(buf))
367}
368
369fn read_u32(cursor: &mut SliceCursor) -> Result<u32, String>
370{
371 let mut buf = [0; 4];
372 cursor.read_exact(&mut buf).map_err(|x| x.to_string())?;
373 Ok(u32::from_be_bytes(buf))
374}
375
376fn read_b4(cursor: &mut SliceCursor) -> Result<[u8; 4], String>
377{
378 let mut buf = [0; 4];
379 cursor.read_exact(&mut buf).map_err(|x| x.to_string())?;
380 Ok(buf)
381}
382
383fn read_i32(cursor: &mut SliceCursor) -> Result<i32, String>
384{
385 let mut buf = [0; 4];
386 cursor.read_exact(&mut buf).map_err(|x| x.to_string())?;
387 Ok(i32::from_be_bytes(buf))
388}
389
390fn read_f64(cursor: &mut SliceCursor) -> Result<f64, String>
391{
392 let mut buf = [0; 8];
393 cursor.read_exact(&mut buf).map_err(|x| x.to_string())?;
394 Ok(f64::from_be_bytes(buf))
395}
396
397/// Parses just the frontmost metadata at the start of a PSD file.
398///
399/// You will need to use both this and [parse_layer_records].
400pub fn parse_psd_metadata(data : &[u8]) -> Result<PsdMetadata, String>
401{
402 let mut cursor = SliceCursor::new(data);
403
404 let signature = read_b4(&mut cursor)?;
405 if signature != [0x38, 0x42, 0x50, 0x53]
406 {
407 return Err("Invalid PSD signature".to_string());
408 }
409
410 let version = read_u16(&mut cursor)?;
411 if version != 1
412 {
413 return Err("Unsupported PSD version".to_string());
414 }
415
416 cursor.set_position(cursor.position() + 6);
417
418 let channel_count = read_u16(&mut cursor)?;
419 let height = read_u32(&mut cursor)?;
420 let width = read_u32(&mut cursor)?;
421 let depth = read_u16(&mut cursor)?;
422 let color_mode = read_u16(&mut cursor)?;
423
424 Ok(PsdMetadata
425 {
426 width,
427 height,
428 channel_count,
429 depth,
430 color_mode,
431 })
432}
433/// Decompress a packbits image data buffer into a vec, appending to the vec.
434///
435/// On success, returns `Ok(size)`.
436///
437/// Panics if there isn't enough data.
438///
439/// PSD files generally use compression on their image data. This decompresses it into a vec, bytewise.
440pub fn append_img_data(cursor : &[u8], output : &mut Vec<u8>, size : u64, h : u64) -> Result<usize, String>
441{
442 let mut _cursor = SliceCursor::new(cursor);
443 let cursor = &mut _cursor;
444 //println!("starting at: {:X}\t", cursor.position());
445 let mode = read_u16(cursor)?;
446 if mode == 0
447 {
448 cursor.take(size).read_to_end(output).map_err(|x| x.to_string())?;
449 }
450 else if mode == 1
451 {
452 let mut c2 = cursor.clone();
453 c2.set_position(c2.position() + h * 2);
454 for _ in 0..h
455 {
456 //println!("at: {:X} - {:X}\t", cursor.position(), c2.position());
457 let len = read_u16(cursor)?;
458 let start = c2.position();
459 // FIXME: ignore overflow and pad out underflow?
460 while c2.position() < start + len as u64
461 {
462 let n = read_u8(&mut c2)? as i8;
463 if n >= 0
464 {
465 c2.take(n as u64 + 1).read_to_end(output).map_err(|x| x.to_string())?;
466 }
467 else if n != -128
468 {
469 output.extend(core::iter::repeat_n(read_u8(&mut c2)?, (1 - n as i64) as usize));
470 }
471 }
472 }
473 cursor.set_position(c2.position());
474 }
475 else
476 {
477 return Err("unsupported compression format".to_string());
478 }
479 Ok(cursor.position() as usize)
480}
481/// Decompress a packbits image data buffer into a slice, writing into the slice in-place. `stride` can be used to control how far apart to write each byte.
482///
483/// On success, returns `Ok(size)`.
484///
485/// Panics if the slice isn't big enough or there isn't enough data.
486///
487/// PSD files generally use compression on their image data. This decompresses it into a slice, bytewise.
488pub fn copy_img_data(cursor : &[u8], output : &mut [u8], stride : usize, size : u64, h : u64) -> Result<usize, String>
489{
490 let mut _cursor = SliceCursor::new(cursor);
491 let cursor = &mut _cursor;
492 //println!("pos... 0x{:X}", cursor.position());
493 let pos = cursor.position();
494 let mode = read_u16(cursor)?;
495 //println!("size... 0x{:X}", size as usize - 2);
496 if mode == 0
497 {
498 for i in 0..size as usize - 2
499 {
500 output[i*stride] = read_u8(cursor)?;
501 }
502 }
503 else if mode == 1
504 {
505 let mut c2 = cursor.clone();
506 c2.set_position(c2.position() + h * 2);
507 let mut i = 0;
508 let mut j = 2;
509 for _ in 0..h
510 {
511 let _i2 = i;
512 //print!("at: {:X} - {:X}\t", cursor.position(), c2.position());
513 let len = read_u16(cursor)?;
514 j += 2;
515 let start = c2.position();
516 // FIXME: ignore overflow and pad out underflow?
517 while c2.position() - start < len as u64
518 {
519 let n = read_u8(&mut c2)? as i8;
520 j += 1;
521 if n >= 0
522 {
523 for _ in 0..n as u64 + 1
524 {
525 let c = read_u8(&mut c2)?;
526 if i*stride < output.len()
527 {
528 output[i*stride] = c;
529 }
530 i += 1;
531 j += 1;
532 }
533 }
534 else if n != -128
535 {
536 let c = read_u8(&mut c2)?;
537 for _ in 0..1 - n as i64
538 {
539 if i*stride < output.len()
540 {
541 output[i*stride] = c;
542 }
543 i += 1;
544 }
545 j += 1;
546 }
547 }
548 //println!("effective w: {}", i - _i2);
549 c2.set_position(start + len as u64);
550 }
551 if j != size
552 {
553 return Err("Desynchronized while reading image data".to_string());
554 }
555 }
556 else
557 {
558 return Err(format!("unsupported compression format {} at 0x{:X}", mode, pos));
559 }
560 Ok(size as usize)
561}
562/// Parses the layer records out of a PSD file, producing a bottom-to-top list.
563///
564/// PSD data is compressed and poorly-ordered, so it's very rare to benefit from streaming loading, even for performance. Therefore, to keep things simple, the input is a slice instead of a streaming trait.
565///
566/// PSD doesn't store its layer data in a tree; instead, it uses start-of-group and end-of-group nodes in a list to indicate tree structure.
567///
568/// On failure, returns all the layers that have been parsed *so far, from the bottom* (PSD files are stored bottom-up), and a string describing the error. The incomplete list of parsed layers is unlikely to be useful for any application, but it may be useful for debugging.
569///
570/// You will need to use both this and [parse_psd_metadata].
571pub fn parse_layer_records(data : &[u8]) -> Result<Vec<LayerInfo>, (Vec<LayerInfo>, String)>
572{
573 let mut layers = Vec::new();
574 let ret = parse_layer_records_impl(data, &mut layers);
575 match ret
576 {
577 Ok(_) => Ok(layers),
578 Err(err) => Err((layers, err)),
579 }
580}
581fn parse_layer_records_impl(data : &[u8], layers : &mut Vec<LayerInfo>) -> Result<(), String>
582{
583 let metadata = parse_psd_metadata(data)?;
584 if metadata.depth != 8
585 {
586 return Err("Only PSDs in 8-bit RGB, CMYK, or Grayscale mode are currently supported.".to_string());
587 }
588 // TODO
589 if metadata.color_mode != 1 && metadata.color_mode != 3 && metadata.color_mode != 4
590 {
591 return Err("Only PSDs in 8-bit RGB, CMYK, or Grayscale mode are currently supported.".to_string());
592 }
593
594 let mut cursor = SliceCursor::new(data);
595 cursor.set_position(26);
596
597 let color_mode_length = read_u32(&mut cursor)? as u64;
598 cursor.set_position(cursor.position() + color_mode_length);
599
600 let image_resources_length = read_u32(&mut cursor)? as u64;
601 cursor.set_position(cursor.position() + image_resources_length);
602
603 let layer_mask_info_length = read_u32(&mut cursor)? as u64;
604 let _layer_mask_info_end = cursor.position() + layer_mask_info_length;
605
606 let layer_info_length = read_u32(&mut cursor)? as u64;
607 let _layer_info_end = cursor.position() + layer_info_length;
608
609 let layer_count = read_u16(&mut cursor)? as i16;
610 let layer_count = layer_count.abs(); // If negative, transparency info exists
611
612 #[cfg(feature = "debug_spew")]
613 println!("starting at {:X}", cursor.position());
614
615 let mut idata_c = SliceCursor::new(data);
616 idata_c.set_position(cursor.position());
617
618 for _i in 0..layer_count
619 {
620 //println!("{}", _i);
621 read_i32(&mut idata_c)?;
622 read_i32(&mut idata_c)?;
623 read_i32(&mut idata_c)?;
624 read_i32(&mut idata_c)?;
625 let image_channel_count = read_u16(&mut idata_c)? as u64;
626 idata_c.set_position(idata_c.position() + 6*image_channel_count + 4 + 4 + 4);
627 let idat_len = read_u32(&mut idata_c)? as u64;
628 idata_c.set_position(idata_c.position() + idat_len);
629 }
630
631 for _ in 0..layer_count
632 {
633 let top = read_i32(&mut cursor)?;
634 let left = read_i32(&mut cursor)?;
635 let bottom = read_i32(&mut cursor)?;
636 let right = read_i32(&mut cursor)?;
637
638 let x = left;
639 let y = top;
640 let w = (right - left) as u32;
641 let h = (bottom - top) as u32;
642
643 let image_channel_count = read_u16(&mut cursor)?;
644 //println!("chan count {}", image_channel_count);
645
646 let channel_info_start = cursor.position();
647
648 cursor.set_position(channel_info_start);
649 let mut image_data_rgba : Vec<u8> = vec![255u8; (w * h * 4) as usize];
650 let mut image_data_k : Vec<u8> = vec!();
651 let mut image_data_mask : Vec<u8> = vec!();
652
653 let mut _rgba_count = 0;
654 let mut has_g = false;
655 let mut has_b = false;
656 let mut has_a = false;
657 let mut aux_count = 0;
658
659 let mut cdat_cursor = cursor.clone();
660
661 let mut has_neg2 = false;
662 let mut has_neg3 = false;
663 for _ in 0..image_channel_count
664 {
665 let channel_id = read_u16(&mut cursor)? as i16;
666 let _channel_length = read_u32(&mut cursor)? as usize;
667 has_neg2 = has_neg2 || channel_id == -2;
668 has_neg3 = has_neg3 || channel_id == -3;
669 }
670
671 let blend_mode_signature = read_b4(&mut cursor)?;
672 if blend_mode_signature != [0x38, 0x42, 0x49, 0x4D]
673 {
674 return Err("Invalid blend mode magic signature".to_string());
675 }
676
677 let blend_mode_key = read_b4(&mut cursor)?;
678 let blend_mode = String::from_utf8_lossy(&blend_mode_key).to_string();
679
680 let opacity = read_u8(&mut cursor)? as f32 / 255.0;
681 #[cfg(feature = "debug_spew")]
682 println!("opacity: {}", opacity * 100.0);
683 let clipping = read_u8(&mut cursor)?;
684 let flags = read_u8(&mut cursor)?;
685 let _filler = read_u8(&mut cursor)?;
686
687 let exdat_len = read_u32(&mut cursor)? as u64;
688 let exdat_start = cursor.position();
689
690 let maskdat_len = read_u32(&mut cursor)? as u64;
691 let maskdat_start = cursor.position();
692
693 // FIXME: support maskdat_len == 0 case
694 let mtop = read_i32(&mut cursor)?;
695 let mleft = read_i32(&mut cursor)?;
696 let mbottom = read_i32(&mut cursor)?;
697 let mright = read_i32(&mut cursor)?;
698 let mut mask_info = MaskInfo::default();
699 mask_info.x = mleft;
700 mask_info.y = mtop;
701 mask_info.w = (mright - mleft) as u32;
702 mask_info.h = (mbottom - mtop) as u32;
703 mask_info.default_color = read_u8(&mut cursor)?;
704 let mflags = read_u8(&mut cursor)?;
705 mask_info.relative = (mflags & 1) != 0;
706 mask_info.disabled = (mflags & 2) != 0;
707 mask_info.invert = (mflags & 4) != 0;
708
709 cursor.set_position(maskdat_start + maskdat_len);
710
711 for _ in 0..image_channel_count
712 {
713 let channel_id = read_u16(&mut cdat_cursor)? as i16;
714 has_g |= channel_id == 1;
715 has_b |= channel_id == 2;
716 has_a |= channel_id == -1;
717 let channel_length = read_u32(&mut cdat_cursor)? as usize;
718 #[cfg(feature = "debug_spew")]
719 println!("channel... {} {} at 0x{:X}", channel_id, channel_length, idata_c.position());
720 if channel_id >= -1 && channel_id <= 2
721 {
722 _rgba_count += 1;
723 let pos = if channel_id >= 0 { channel_id } else { 3 } as usize;
724 #[cfg(feature = "debug_spew")]
725 println!("{} {} {} {}", w, h, pos, channel_length);
726 if channel_length > 2
727 {
728 let progress = copy_img_data(idata_c.take_rest().buf, &mut image_data_rgba[pos..], 4, channel_length as u64, h as u64)?;
729 idata_c.pos += progress;
730 }
731 else
732 {
733 idata_c.set_position(idata_c.position() + 2);
734 }
735 }
736 else if channel_id == 3 // CMYK's K
737 {
738 if channel_length > 2
739 {
740 let progress = append_img_data(idata_c.take_rest().buf, &mut image_data_k, channel_length as u64, h as u64)?;
741 idata_c.pos += progress;
742 }
743 else
744 {
745 idata_c.set_position(idata_c.position() + 2);
746 }
747 }
748 else
749 {
750 #[cfg(feature = "debug_spew")]
751 println!("mask... {} {} {}", mask_info.w, mask_info.h, channel_length);
752 aux_count += 1;
753 if aux_count > 1
754 {
755 idata_c.set_position(idata_c.position() + channel_length as u64);
756 }
757 else if channel_length > 2
758 {
759 #[cfg(feature = "debug_spew")]
760 println!("adding mask data...");
761 let progress = append_img_data(idata_c.take_rest().buf, &mut image_data_mask, channel_length as u64, mask_info.h as u64)?;
762 idata_c.pos += progress;
763 }
764 else
765 {
766 idata_c.set_position(idata_c.position() + 2);
767 }
768 }
769 }
770
771 let blendat_len = read_u32(&mut cursor)? as u64;
772 cursor.set_position(cursor.position() + blendat_len);
773
774 let mut name_len = read_u8(&mut cursor)?;
775 let orig_namelen = name_len;
776 while (name_len + 1) % 4 != 0
777 {
778 name_len += 1;
779 }
780 let mut name = vec![0; name_len as usize];
781 cursor.read_exact(&mut name[..]).map_err(|x| x.to_string())?;
782 let name = String::from_utf8_lossy(&name[..orig_namelen as usize]).to_string();
783
784 let mut layer = LayerInfo {
785 name,
786 opacity,
787 fill_opacity : 1.0,
788 blend_mode,
789 x,
790 y,
791 w,
792 h,
793 image_channel_count,
794 image_data_rgba,
795 image_data_k,
796 image_data_has_g : has_g,
797 image_data_has_b : has_b,
798 image_data_has_a : has_a,
799 mask_channel_count : aux_count,
800 mask_info,
801 image_data_mask,
802 group_expanded : false,
803 group_opener : false,
804 group_closer : false,
805 funny_flag : false,
806 is_clipped : clipping != 0,
807 is_alpha_locked : (flags & 1) != 0,
808 is_visible : (flags & 2) == 0,
809 adjustment_type : "".to_string(),
810 adjustment_info : vec!(),
811 adjustment_desc : None,
812 effects_desc : None,
813 };
814
815 //println!("--- {:X}", cursor.position());
816
817 while cursor.position() < exdat_start + exdat_len
818 {
819 let sig = read_b4(&mut cursor)?;
820 if sig != [0x38, 0x42, 0x49, 0x4D]
821 {
822 return Err("Invalid extended data magic signature".to_string());
823 }
824
825 let name = read_b4(&mut cursor)?;
826 let name = String::from_utf8_lossy(&name).to_string();
827
828 let len = read_u32(&mut cursor)? as u64;
829 //println!("?? {}", len);
830 let start = cursor.position();
831
832 #[cfg(feature = "debug_spew")]
833 println!("reading metadata.... {}", name.as_str());
834
835 fn read_descriptor(c : &mut SliceCursor) -> Result<Descriptor, String>
836 {
837 // skip name. usually/often blank
838 let n = read_u32(c)? as u64;
839 c.set_position(c.position() + n * 2);
840
841 let mut idlen = read_u32(c)?;
842 if idlen == 0 { idlen = 4; }
843 let mut id = vec![0; idlen as usize];
844 c.read_exact(&mut id).map_err(|x| x.to_string())?;
845 let id = String::from_utf8_lossy(&id).to_string();
846
847 let mut data = vec!();
848
849 let itemcount = read_u32(c)?;
850
851 for _ in 0..itemcount
852 {
853 let mut namelen = read_u32(c)?;
854 if namelen == 0 { namelen = 4; }
855 let mut name = vec![0; namelen as usize];
856 c.read_exact(&mut name).map_err(|x| x.to_string())?;
857 let name = String::from_utf8_lossy(&name).to_string();
858
859 fn read_key(c : &mut SliceCursor) -> Result<DescItem, String>
860 {
861 let id = read_b4(c)?;
862 let id = String::from_utf8_lossy(&id).to_string();
863
864 Ok(match id.as_str()
865 {
866 "long" => DescItem::long(read_i32(c)?),
867 "doub" => DescItem::doub(read_f64(c)?),
868 "Objc" => DescItem::Objc(Box::new(read_descriptor(c)?)),
869 "bool" => DescItem::bool(read_u8(c)? != 0),
870 "TEXT" =>
871 {
872 let len = read_u32(c)? as u64;
873 let mut text = vec![0; len as usize];
874 for i in 0..len
875 {
876 text[i as usize] = read_u16(c)?;
877 }
878 let text = String::from_utf16_lossy(&text).trim_end_matches('\0').to_string();
879 DescItem::TEXT(text)
880 }
881 "UntF" =>
882 {
883 let typ = read_b4(c)?;
884 let typ = String::from_utf8_lossy(&typ).to_string();
885
886 DescItem::UntF(typ, read_f64(c)?)
887 }
888 "enum" =>
889 {
890 let mut len = read_u32(c)?;
891 if len == 0 { len = 4; }
892 let mut name1 = vec![0; len as usize];
893 c.read_exact(&mut name1).map_err(|x| x.to_string())?;
894 let name1 = String::from_utf8_lossy(&name1).to_string();
895
896 let mut len = read_u32(c)?;
897 if len == 0 { len = 4; }
898 let mut name2 = vec![0; len as usize];
899 c.read_exact(&mut name2).map_err(|x| x.to_string())?;
900 let name2 = String::from_utf8_lossy(&name2).to_string();
901
902 DescItem::_enum(name1, name2)
903 }
904 "VlLs" =>
905 {
906 let len = read_u32(c)?;
907 let mut ret = vec!();
908 for _ in 0..len
909 {
910 ret.push(read_key(c)?);
911 }
912 DescItem::VlLs(ret)
913 }
914 _ =>
915 {
916 #[cfg(feature = "debug_spew")]
917 println!("!!! errant descriptor subobject type... {}", id);
918 DescItem::Err(format!("!!! errant descriptor subobject type... {}", id))
919 }
920 })
921 }
922
923 data.push((name, read_key(c)?));
924 }
925
926 Ok((id, data))
927 }
928
929 // This comment must stay here: it is a ctrl+f anchor.
930 // Read adjustment data.
931 match name.as_str()
932 {
933 "lsct" =>
934 {
935 let kind = read_u32(&mut cursor)? as u64;
936 layer.group_expanded = kind == 1;
937 layer.group_opener = kind == 1 || kind == 2;
938 layer.group_closer = kind == 3;
939 if kind == 1 || kind == 2
940 {
941 #[cfg(feature = "debug_spew")]
942 println!("group opener!");
943 }
944 if kind == 3
945 {
946 #[cfg(feature = "debug_spew")]
947 println!("group closer!");
948 }
949 }
950 "luni" =>
951 {
952 let len = read_u32(&mut cursor)? as u64;
953 let mut name = vec![0; len as usize];
954 for i in 0..len
955 {
956 name[i as usize] = read_u16(&mut cursor)?;
957 }
958 layer.name = String::from_utf16_lossy(&name).to_string();
959 }
960 "tsly" =>
961 {
962 let thing = read_u8(&mut cursor)?;
963 layer.funny_flag = thing == 0;
964 #[cfg(feature = "debug_spew")]
965 println!("{}", layer.funny_flag);
966 }
967 "iOpa" =>
968 {
969 layer.fill_opacity = read_u8(&mut cursor)? as f32 / 255.0;
970 }
971 "lfx2" =>
972 {
973 if read_u32(&mut cursor)? == 0 && read_u32(&mut cursor)? == 16
974 {
975 layer.effects_desc = Some(read_descriptor(&mut cursor)?);
976 }
977 else
978 {
979 read_descriptor(&mut cursor)?;
980 }
981 }
982 // adjustment layers
983 "post" =>
984 {
985 let mut data = vec!();
986 data.push(read_u16(&mut cursor)? as f32); // number of levels
987 layer.adjustment_type = name.clone();
988 layer.adjustment_info = data;
989 }
990 "nvrt" =>
991 {
992 layer.adjustment_type = name.clone();
993 layer.adjustment_info = vec!();
994 }
995 "brit" =>
996 {
997 let mut data = vec!();
998 data.push(read_u16(&mut cursor)? as f32); // brightness
999 data.push(read_u16(&mut cursor)? as f32); // contrast
1000 data.push(read_u16(&mut cursor)? as f32); // "Mean value for brightness and contrast"
1001 data.push(read_u8(&mut cursor)? as f32); // "Lab color only"
1002 data.push(1.0); // legacy mode
1003 layer.adjustment_type = name.clone();
1004 layer.adjustment_info = data;
1005 }
1006 "thrs" =>
1007 {
1008 let mut data = vec!();
1009 data.push(read_u16(&mut cursor)? as f32);
1010 layer.adjustment_type = name.clone();
1011 layer.adjustment_info = data;
1012 }
1013 "hue2" =>
1014 {
1015 let mut data = vec!();
1016
1017 read_u16(&mut cursor)?; // version
1018 data.push(read_u8(&mut cursor)? as f32); // if 1, is absolute/colorization (rather than relative)
1019 read_u8(&mut cursor)?;
1020
1021 // "colorization"
1022 data.push(read_u16(&mut cursor)? as i16 as f32); // hue
1023 data.push(read_u16(&mut cursor)? as i16 as f32); // sat
1024 data.push(read_u16(&mut cursor)? as i16 as f32); // lightness (-1 to +1)
1025
1026 // "master"
1027 data.push(read_u16(&mut cursor)? as i16 as f32); // hue
1028 data.push(read_u16(&mut cursor)? as i16 as f32); // sat
1029 data.push(read_u16(&mut cursor)? as i16 as f32); // lightness (-1 to +1)
1030
1031 // todo: read hextant values?
1032
1033 layer.adjustment_type = name.clone();
1034 layer.adjustment_info = data;
1035 }
1036 "levl" =>
1037 {
1038 let mut data = vec!();
1039
1040 if read_u16(&mut cursor)? != 2
1041 {
1042 return Err("Ran into an unsupported subdata version".to_string());
1043 }
1044 for _ in 0..28
1045 {
1046 data.push(read_u16(&mut cursor)? as f32 / 255.0); // in floor
1047 data.push(read_u16(&mut cursor)? as f32 / 255.0); // in ceil
1048 data.push(read_u16(&mut cursor)? as f32 / 255.0); // out floor
1049 data.push(read_u16(&mut cursor)? as f32 / 255.0); // out ceil
1050 data.push(read_u16(&mut cursor)? as f32 / 100.0); // gamma
1051 }
1052 layer.adjustment_type = name.clone();
1053 layer.adjustment_info = data;
1054 }
1055 "curv" =>
1056 {
1057 let mut data = vec!();
1058
1059 read_u8(&mut cursor)?;
1060 if read_u16(&mut cursor)? != 1
1061 {
1062 return Err("Ran into an unsupported subdata version".to_string());
1063 }
1064 let enabled = read_u32(&mut cursor)?;
1065
1066 for i in 0..32
1067 {
1068 if (enabled & (1u32 << i)) != 0
1069 {
1070 let n = read_u16(&mut cursor)?;
1071 data.push(n as f32); // number of points
1072 for _ in 0..n
1073 {
1074 let y = read_u16(&mut cursor)? as f32 / 255.0;
1075 data.push(read_u16(&mut cursor)? as f32 / 255.0); // x
1076 data.push(y); // y
1077 }
1078 }
1079 else
1080 {
1081 data.push(0.0); // number of points
1082 }
1083 }
1084 layer.adjustment_type = name.clone();
1085 layer.adjustment_info = data;
1086 }
1087 "blwh" =>
1088 {
1089 if read_u32(&mut cursor)? != 16
1090 {
1091 return Err("Ran into an unsupported subdata version".to_string());
1092 }
1093 layer.adjustment_type = name.clone();
1094 layer.adjustment_desc = Some(read_descriptor(&mut cursor)?);
1095 }
1096 "CgEd" =>
1097 {
1098 if read_u32(&mut cursor)? != 16
1099 {
1100 return Err("Ran into an unsupported subdata version".to_string());
1101 }
1102 //layer.adjustment_type = name.clone();
1103 //layer.adjustment_type = "brit".to_string();
1104 let temp = read_descriptor(&mut cursor)?.1;
1105 #[cfg(feature = "debug_spew")]
1106 println!("{:?}", temp);
1107 let mut n = BTreeMap::new();
1108 for t in temp
1109 {
1110 n.insert(t.0, t.1);
1111 }
1112 #[cfg(feature = "debug_spew")]
1113 println!("{:?}", n);
1114 //("null", [("Vrsn", long(1)), ("Brgh", long(9)), ("Cntr", long(30)), ("means", long(127)), ("Lab ", bool(false)), ("useLegacy", bool(true)), ("Auto", bool(true))])
1115 let mut data = vec!();
1116 data.push(n.get("Brgh").ok_or("Malformed data structure".to_string())?.long() as f32);
1117 data.push(n.get("Cntr").ok_or("Malformed data structure".to_string())?.long() as f32);
1118 data.push(n.get("means").ok_or("Malformed data structure".to_string())?.long() as f32);
1119 data.push(n.get("Lab ").ok_or("Malformed data structure".to_string())?.bool() as u8 as f32);
1120 data.push(n.get("useLegacy").ok_or("Malformed data structure".to_string())?.bool() as u8 as f32);
1121 #[cfg(feature = "debug_spew")]
1122 println!("??????????? {:?}", data);
1123 layer.adjustment_info = data;
1124 }
1125 _ => {}
1126 }
1127 cursor.set_position(start + len);
1128 }
1129 //println!("{:X} {:X}", cursor.position(), exdat_start + exdat_len);
1130 if cursor.position() != exdat_start + exdat_len
1131 {
1132 return Err("Desynchronized while reading or skipping extra data".to_string());
1133 }
1134
1135 #[cfg(feature = "debug_spew")]
1136 println!("added layer with name {}", layer.name);
1137 layers.push(layer);
1138 }
1139
1140 Ok(())
1141}
1142
1143#[non_exhaustive]
1144#[derive(Debug, PartialEq)]
1145/// File-wide PSD header metadata.
1146///
1147/// Returned from [parse_psd_metadata].
1148pub struct PsdMetadata {
1149 /// Canvas width in pixels.
1150 pub width: u32,
1151 /// Canvas height in pixels.
1152 pub height: u32,
1153 /// PSD-wide color mode constant. See <https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_pgfId-1055726>
1154 pub color_mode: u16,
1155 /// Color depth in bytes. Only 8-bit (1-byte) images are currently supported.
1156 pub depth: u16,
1157 /// Number of channels in the PSD file's colorspace, including alpha. Only Y/YA, RGB/RGBA, and CMYK/CMYKA images are currently supported.
1158 pub channel_count: u16,
1159}
1160
1161#[cfg(test)]
1162mod tests {
1163 use super::*;
1164 #[test]
1165 fn test()
1166 {
1167 let data = std::fs::read("data/test.psd").expect("Failed to open test.psd");
1168
1169 if let Ok(layers) = parse_layer_records(&data)
1170 {
1171 for mut layer in layers
1172 {
1173 // Don't spew tons of image data bytes to stdout; we just want to see the metadata.
1174 layer.image_data_rgba = vec!();
1175 layer.image_data_k = vec!();
1176 layer.image_data_mask = vec!();
1177 println!("{:?}", layer);
1178 }
1179 }
1180
1181 println!("-----");
1182
1183 let data = std::fs::read("data/test2.psd").expect("Failed to open test2.psd");
1184
1185 if let Ok(layers) = parse_layer_records(&data)
1186 {
1187 for mut layer in layers
1188 {
1189 layer.image_data_rgba = vec!();
1190 layer.image_data_k = vec!();
1191 layer.image_data_mask = vec!();
1192 println!("{:?}", layer);
1193 }
1194 }
1195 }
1196}