Skip to content

Anatomy of a PSD File

Ryan LeFevre edited this page Sep 12, 2014 · 12 revisions

The PSD file format is massive. It's the result of over 20 years of backward compatible changes and 14 major Photoshop releases. New features have been cleverly tacked on, and as a result, there are some redundancies and oddities. I've been working with the PSD format for over 2 years now, and there are still many details that I haven't explored yet.

That said, PSD files have a general structure that has existed for a long time. I'm going to attempt to explain the important and interesting parts without going into mundane details.

Let's get to it.

A Little Background on Binary Files

If you have ever worked with reading/writing/parsing binary files, or if you know how they work, just skip this whole section.

Binary files are a bit of a black box. If you try to open them in your text editor, you're going to see lots of strange symbols and characters. It looks like gibberish, but it's actually a bunch of 1's and 0's that follow a very strict specification. Your text editor is trying to interpret the 1's and 0's using a particular character set, such as UTF-8, but is failing miserably because the data isn't supposed to be human readable at all.

In order to read the data in the binary file, you need a specification, or a map of sorts. Of course, if you can't find it online, you can also reverse engineer it, but that's another story. Adobe has a public spec for the PSD file format available online. Unfortunately, despite its latest revision date, it is incomplete and even incorrect in some places. Some incredibly important stuff, which I'll get into below, is even marked as "Undocumented data". That said, if you take a quick glance at the page, you will get a gist of what a file spec looks like.

The Main Structure

The PSD format is broken up into 5 main components: the header, color mode data, image resources, layer and mask data, and image data, respectively. All of these sections are variable in size, but luckily the first 4 bytes of each contain their length (except for the header, because it starts at position 0).

Since each section starts with its length, this makes it easy for the parser to skip entire sections if needed. PSD.rb does this if you only need to read the image data. The length values can also be used for validation and error correction purposes. If you have a bug in your parser, you can easily compare where it thinks the end of the section is to where it should be and attempt to gracefully adjust.

Descriptors

Before we dive in, there is one important thing to know. Photoshop has an internal data type it refers to as a Descriptor. A Descriptor is very similar to an "object" or a "struct" in many programing languages. It's basically a complex data structure that that contains many different types of data and can be nested many levels deep. In Ruby, we can directly represent a Descriptor as a Hash, which is what PSD.rb does.

Every Descriptor starts with a name and ID, although their contents are optional and might be blank. This is followed by the number of items in the Descriptor.

Every data type in a Descriptor has a unique case-sensitive key that tells the parser how to proceed. There are 18 (known) data types in total:

  • bool = Boolean
  • type, GlbC = Class name
  • Objc, GlbO = A nested Descriptor
  • doub = Double
  • enum = Enumerated type, read as a String
  • alis = Alias, read as a String
  • Pth = File path
  • long = Long integer
  • comp = Large integer aka longlong
  • VlLs = List, can contain any type of data
  • ObAr = Object array, currently unsure how to parse
  • tdta = Raw data, simply read as byte string
  • obj = Reference, can exist as many different forms
    • Clss = null value
    • Enmr = Enumerated type
    • Idnt = Identifier, read as Integer
    • indx = Index, read as Integer
    • name = Reference name, read as String
    • rele = Offset, read as Integer
    • prop = Property, read as String
  • TEXT = Arbitrary String
  • UntF = Unit double
  • UnFl = Unit float

Header

The header is an incredibly important part of the PSD file. It contains information about the color mode, the color depth, the number of color channels, and the dimensions of the whole PSD. In other words, when you set the PSD to be RGB 8-bit color mode, this is stored in the header. This becomes especially important when you go to extract the full preview image from the PSD.

The header data looks like this:

{
 "sig"=>"8BPS",
 "version"=>1,
 "channels"=>3,
 "rows"=>600,
 "cols"=>900,
 "depth"=>8,
 "mode"=>3,
 "color_data_len"=>0
}

You'll notice that the "mode" is simply a number. This number maps to a specific color that is used for the entire document. Here are all of the possibilities:

[
  'Bitmap',
  'GrayScale',
  'IndexedColor',
  'RGBColor',
  'CMYKColor',
  'HSLColor',
  'HSBColor',
  'Multichannel',
  'Duotone',
  'LabColor',
  'Gray16',
  'RGB48',
  'Lab48',
  'CMYK64',
  'DeepMultichannel',
  'Duotone16'
]

The color mode, the depth, and the number of channels determine how we parse the image data. Color channels are the primary color components that are used to describe the color of each pixel in the image. This is a really confusing way of saying R, G, and B are the color channels for RGB. If the channel count is 4 for RGB, then it's really RGBA because it includes alphatransparency data. The same goes for Grayscale, CMYK, and so on.

Color Mode Data

This section actually only exists if your PSD file is set to Indexed or Duotone color. Because of this, I have not explored this section at all and PSD.rb does not support it yet.

If you have indexed color, this section contains the color table for the image. If you have duotone color, the contents are undocumented.

Image Resources

The Resources section is mostly boring settings, but there are a few incredibly important parts to pay attention to. It's basically an array of various program settings and metadata that exist on a per-PSD basis. A lot of the settings are things such as "Auto Save File Path" and "Timeline Information".

All resources start with a signature and unique ID, followed by an optional name and the size of the resource data. The unique ID tells the parser how to proceed.

Layer Comps

One incredibly important section (#1065) stores all the layer comps in the document. The layer comp data is stored in a descriptor, and looks like this once parsed:

{
  :class=>{:name=>"", :id=>"CompList"},
  "list"=> [
    {
      :class=>{:name=>"", :id=>"Comp"},
      "Nm  "=>"Version A",
      "compID"=>692243163,
      "capturedInfo"=>1
    },
    {
      :class=>{:name=>"", :id=>"Comp"},
      "Nm  "=>"Version B",
      "compID"=>725235304,
      "capturedInfo"=>1
    },
    {
      :class=>{:name=>"", :id=>"Comp"},
      "Nm  "=>"Version C",
      "compID"=>730932877,
      "capturedInfo"=>1
    }
  ]
}

You can see that each layer comp has a unique ID compID, it's name Nm , and a key called capturedInfo. The capturedInfo key is a flag that indicates what layer properties the layer comp tracks. In this case, "1" means that the layer comp only tracks the visibility of each layer. Each layer stores which comps it belongs to, which is mentioned below.

Layers & Masks

The layers & masks section is typically the largest of the five. It contains information about each layer, including the flattened image data (more on that below), as well as the global mask. Layers are stored in bottom-up order meaning the layer with the lowest z-index is read first and ends with the top-most layer. PSD.rb reverses the order of the layers when building the tree in order to match the representation shown in the Photoshop layer browser.

Although you can group layers in nested folders in Photoshop, they are actually stored in a flattened representation. A group is really two special types of layers that act as section dividers. The first type of group layer denotes the start of a group and contains its name. The other type denotes the end of a group and contains almost no data. This was done in order to maintain backwards compatibility since layers were added in version 3.0 and groups weren't added until version 8.0 (CS).

If you have a document tree that looks like this:

Layer 1
Group 1
  Layer 2
  Group 2
    Layer 3

Then it is represented internally like this:

Layer 1
Group 1 start
Layer 2
Group 2 start
Layer 3
Group 2 end
Group 1 end

The Layer

Layers contain a lot of data. So much that it's a bit overwelming at first. We'll go though each section one at a time in the order that they occur to try and simplify it a bit.

Position and Channels

The layers position within the document is stored using the top, left, bottom, and right coordinates of it's bounding box, respectively. Using these coordinates, you can easily calculate the dimensions of the layer as well.

The channel data determines how many image data channels exist for the embedded bitmap layer data. Each channel specifies an ID, which identifies the channel, as well as a length.

Blend Modes

The blend mode describes how the layer is blended with the layers below it. In Photoshop, this exists as part of the layer browser. Each blend mode has a unique identifier that is used to determine which type the layer uses. For example: norm is normal, lite is lighten, mul is multiply, and so on. Don't ask me why multiply isn't mult because I have no idea either.

It also stores the layers opacity as well as the layers visiblity, which can be toggled in the layer browser. If this layer is the start of a group, then all descendants are technically hidden as well, even though their visibiltiy flag does not need to reflect this.

Mask

The mask section doesn't store the actual mask data, but it does store some important information about it. If the layer has no mask, the mask size will be 0 and there will be no further data for this section.

If the mask size is greater than zero, then the top, left, bottom, and right coordinates of the mask are stored next along with the default color.

Blending Ranges

To be completely honest, I'm not entirely sure what the blending ranges represent. What I do know is that there are both greyscale and per-channel blending ranges. A blending range consists of two black values and two white values each for the source and destination of the range.

Layer Name

The layer name is actually stored in two locations in any remotely recent version of Photoshop. At this location, the layer name is encoded in MacRoman format. Later on, it is stored as UTF-16. This section is left purely for backwards compatibility.

Additional Information

The rest of the layer is filled with various data blocks that describe additional information about the layer. There are quite a few of these, and each one is parsed differently. PSD.rb knows how to parse 23 of them at the time of this writing. I believe there are over 70 in total that exist. Some are documented, a few are partially documented, and some are completely undocumented.

Rather than document every single one of these, we'll just touch on a few interesting ones. If you're curious about any that aren't mentioned here, I highly recommend checking out the source code for it.

Layer Section Divider

This block is integral to describing the organizational structure of the PSD document. This is an additional info block and not built elsewhere into the document because, as mentioned earlier, PSDs didn't always have groups or even layers.

There are four different types that exist: layer, open folder, closed folder, and a bounding section divider. The open and closed folder types denote the start of a folder. The bounding section divider denotes the end of a folder.

This info block also sometimes stores the blending mode for the layer. This takes precendence over the blend mode previously parsed.