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}