Skip to content

Latest commit

 

History

History
750 lines (581 loc) · 28.1 KB

README_PSD.md

File metadata and controls

750 lines (581 loc) · 28.1 KB

PSD document format

This document describes structure of the Psd object used in readPsd and writePsd functions, you can see instructions on how to use these functions in our main README document

You can see examples of different PSD documents and their corresponding JSON data in our test folder. Each subfolder contains src.psd document and corresponding data.json file with object generated by our library. Additionally there is canvas.png file represending composite image data, thumb.png file representing thumbnail image data and layer-#.png files with image data of each layer.

Basic document structure

// example psd document structure
const psd: Psd = {
  "width": 300,
  "height": 200,
  "channels": 3,
  "bitsPerChannel": 8,
  "colorMode": 3,
  "canvas": <Canvas>,
  "children": [],
};
  • The width and height properties specify PSD document size in pixels. These values are required when writing a document.

  • channels property specifies number of color channels in the document, it's usually 3 channels (red, green, and blue, when document is in typical RGB color mode) Other color modes will have different channel count (grayscale - 1 channel, CMYK - 4 channels). This property can be ommited when writing, this library only supports RGB color mode with 3 channels at the moment.

  • bitsPerChannel property specifies number of bits per each color channel, this value will ba 1 for one bit bitmap color mode and 8 in all other cases as this library is not supporting 16 or 32 bit color channels at the moment. It can also be ommited when writing a document and default value of 8 will be assumed.

  • colorMode specifies color mode of the PSD document.

    Value is one of the numbers that can be matched to this enumerable:

    enum ColorMode {
      Bitmap = 0,
      Grayscale = 1,
      Indexed = 2,
      RGB = 3,
      CMYK = 4,
      Multichannel = 7,
      Duotone = 8,
      Lab = 9,
    }

    The library supports "Bitmap", "Grayscale" and "RGB" color modes at the moment. "Bitmap" and "Grayscale" colors will be converted to "RGB" colors when reading PSD file. Writing is only supported for "RGB" mode. The value can be ommited for writing and "RGB" color mode will be assumed.

  • canvas (or imageData) is a property containing bitmap with composite image data for the entire document. PSD file contains this extra bitmap additionally to bitmaps of each layer. canvas field will be an instance of HTMLCanvasElement (in browser environment) or Canvas object of node-canvas library (in nodejs environment).

    You can choose to instead use imageData field by choosing useImageData: true option when reading PSD file. In that can imageData will be an instance of ImageData object, containing width, height and data properties. This is useful if you want to use bitmap data directly, and not use it for drawing using canvas. Additionally this will preserve accurate color information as canvas element will premultiply image alpha which will change color values slightly.

    If you don't need to use this field you can specify skipCompositeImageData: true option, while reading PSD file, to skip reading this field and save on processing time and memory usage.

    This image data is optional in PSD file so it may be missing in some PSD files that opted to skip it.

    When writing you can provide either canvas or imageData property and the library will use the one you provide. You can also skip the field entirely and not write this data at all as it's not needed by Photohop to read the file. It might be used in some other application, for thumbnails or by some old versions of Adobe software, so you may want to still provide it to remain compatible with those programs.

    If you're generating your own PSD file and want to provide this composite image data you will have to generate one yourself by composing all layer image data and effects yourself, this library does not provide any utilities to generate this image data for you.

  • children list of layers and groups at the root of the document. see Layers and Groups

  • imageResources contains all document-wide parameters see Image Resouces

  • linkedFiles contains list of files linked in smart objects see Smart Objects

  • artboards contains global options for artboards. Artboards is a feature in new versions of Photoshop that lets you have multiple canvases in a single PSD document. The information about positioning, name and color of each artboard is stored inside each layer, in artboard property. This property will be absent if the document does not have any artboards specified. It can be ommited when writing.

    type Artboards = {
      count: number; // number of artboards in the document
      autoExpandOffset?: { horizontal: number; vertical: number; };
      origin?: { horizontal: number; vertical: number; };
      autoExpandEnabled?: boolean;
      autoNestEnabled?: boolean;
      autoPositionEnabled?: boolean;
      shrinkwrapOnSaveEnabled?: boolean;
      docDefaultNewArtboardBackgroundColor?: Color;
      docDefaultNewArtboardBackgroundType?: number;
    }
  • annotations contains array of annotations, this field will be absent if the document does not have any annotations. It can be ommited when writing. Sound annotations are not supported by this library right at the moment.

    Each annotation object has a following structure:

    interface Annotation {
      type: 'text' | 'sound';
      open: boolean;
      iconLocation: { left: number; top: number; right: number; bottom: number };
      popupLocation: { left: number; top: number; right: number; bottom: number };
      color: Color;
      author: string;
      name: string;
      date: string;
      data: string | Uint8Array; // annotation text or sound buffer
    }
  • globalLayerMaskInfo don't really know what this is, it can be ommited when writing.

  • filterMask don't really know what this is, it can be ommited when writing.

Bitmaps and image data

Image data can be accessed from psd.canvas or layer.canvas fields by default. These fields store regular HTMLCanvasElement (or node-canvas object when running in node.js). For 16bit and 32 bit documents image data will be converted to regular 8bit canvas (this will result in a loss of data, use useImageData option if you want to preserve precission of color data).

If useImageData option is set to true in read options then the image data will be available in psd.imageData and layer.imageData fields instead. Using image data option gives you direct access to pixel data without having to go through the canvas object, which bypasses alpha premultiplication and convertion from 16/32bit image data to 8bit canvas data.

For 16bit documents imageData fields will contain pixel data as Uint16Array, the values ranging from 0 to 65535.

For 32bit documents imageData fields will contain pixel data as Float32Array, the values ranging from 0 to 1. 32bit values are in linear color space (as oposed to gamma corrected sRGB color). In order to convert the values to regular sRGB color space the values need to be gamma-corrected by using following conversion code (except alpha channel):

// convert 32bit linear to 8bit sRGB
destination[i] = Math.round(Math.pow(source[i], 1.0 / 2.2) * 255);

// convert 8bit sRGB to 32bit linear
destination[i] = Math.pow(source[i] / 255, 2.2);

Layers and Groups

Psd document object has children property that contains all root level layers and groups in order from top to bottom as they appear in Photoshop (take note that if you want to draw the layer images to generate document image then you need to draw them in reverse order). The children property will contain both regular layers and groups. Each group will have children property, containing all the layers and groups that are inside that group. So the document will have a tree structure like this:

var psd = {
  // ... other fields
  children: [
    {
      name: "layer 1",
      // ... other fields
    }
    {
      name: "group 1",
      // ... other fields
      children: [
        {
          name: "layer 2, inside group 1",
          // ... other fields
        },
        {
          name: "group 2, inside group 1",
          // ... other fields
          children: []
        }
      ]
    }
  ]
}

Layer types

You can distinguish between different layer types by checking which properties thay have set on them. If a layer has children property set it meas the it's a group, if it has text property it's a text layer and so on. If you're only interested in the basic parsing of layers and want to just extract image data or layer parameter a simple parsing like this can be enough:

// simple parsing
function parseLayer(layer) {
  if ('children' in layer) {
    // group
    layer.children.forEach(parseLayer);
  } else if (layer.canvas) {
    // regular layer with canvas
  } else {
    // empty or special layer
  }
}

If you need to know type of each layer, something like this could be a good approach:

// complex parsing
function parseLayer(layer) {
  if ('children' in layer) {
    // group
    layer.children.forEach(parseLayer);
  } else if ('text' in layer) {
    // text layer
  } else if ('adjustment' in layer) {
    // adjustment layer
  } else if ('placedLayer' in layer) {
    // smart object layer
  } else if ('vectorMask' in layer) {
    // vector layer
  } else {
    // bitmap layer
  }
}

But thake into account that a lot of properties are shared for different types of layers. Any layer can have a mask property for example.

Layer

Example layer structure:

{
  "top": 0,
  "left": 0,
  "bottom": 200,
  "right": 300,
  "blendMode": "normal",
  "opacity": 1,
  "clipping": false,
  "timestamp": 1448235572.7235785,
  "transparencyProtected": true,
  "protected": {
    "transparency": true,
    "composite": false,
    "position": true
  },
  "hidden": false,
  "name": "Background",
  "nameSource": "bgnd",
  "id": 1,
  "layerColor": "none",
  "blendClippendElements": true,
  "blendInteriorElements": false,
  "knockout": false,
  "referencePoint": {
    "x": 0,
    "y": 0
  },
  "canvas": <Canvas>
},
  • top, left, bottom, right properties specify location of layer image data withing the document. top specifies offset in pixel of the top of layer image data from the top edge of the document, bottom specifies offset of the bottom of the layer image data from the top adge of the document and similar for left and right.

    This is necessary as layer image data can be smaller or large than the document size. This can cause in some cases the values of these fields to be negative. A value of left: -100 means that the layer image data starts 100 pixels outside the left document edge. This can happen if you move the layer left beyond document edge in Photoshop.

    top and left values can be ommited when writing and will be assumed to be 0. bottom and right values can be ommited and will always be ignored when writing and will instead be calculated from canvas (or imageData) width and height.

  • blendMode is a layer blending mode and will be one of the following values:

    type BlendMode = 'pass through' | 'normal' | 'dissolve' | 'darken' |
    'multiply' | 'color burn' | 'linear burn' | 'darker color' | 'lighten'|
    'screen' | 'color dodge' | 'linear dodge' | 'lighter color' | 'overlay' |
    'soft light' | 'hard light' | 'vivid light' | 'linear light' | 'pin light' |
    'hard mix' | 'difference' | 'exclusion' | 'subtract' | 'divide' | 'hue' |
    'saturation' | 'color' | 'luminosity'

    These values correspond to naming in layer blend mode dropdown. If ommited a value of normal will be assumed.

  • canvas (or imageData) see canvas property description in Basic document structure

    Vector, text and smart object layers still have image data with pregenerated bitmap. You also need to provide that image data when writing PSD files.

    Reading this property can be skipped if skipLayerImageData: true option is passed to readPsd function. This can be done to save on memory usage and processing time if layer image data is not needed.

  • opacity specifies level of layer transparency (the value range is from 0 to 1). Can be ommited when writing, a default value of 1 will be assumed.

  • clipping indicates if layer clipping is enabled (if enabled the layer is clipped to the layer below it). Can be ommited when writing, a default value of false will be assumed.

  • timestamp timestamp of last time layer was modified (in unix time). Can be ommited when writing.

  • transparencyProtected and protected properties specify status of various locks that you can set for each layer.

    • protected.position indicates if moving layer is locked
    • protected.composite indicates if drawing on the layer is locked
    • protected.transparency indicates if drawing transparent pixels are locked

    If the transparencyProtected is true but protected.transparency is false it means that the layer has "lock all" enabled. Both this fields can be ommited when writing, a values of false will be assumed for all of the ommited fields.

  • hidden indicates if the layer is hidden. Can be ommited when writing, a value of false will be assumed.

  • name name of the layer, can be any unicode string, can be empty. Can be ommited when writing, a value of '' will be assumed.

  • nameSource internal Photoshop information, can be ignored and ommited when writing.

  • id unique ID number for layer, it may be missing in some PSD files. Can be ommited when writing, but if provided it has to be a unique number, if duplicate IDs are found they will be changed to a unique ones when writing the file.

  • layerColor color of the layer in layer list, property will be missing or will be one of the following values:

    type LayerColor = 'none' | 'red' | 'orange' | 'yellow' | 'green' | 'blue' | 'violet' | 'gray'

  • version unknown functionality

  • referencePoint unknown functionality

  • mask Layer mask, property has the following structure.

    interface LayerMaskData {
      top?: number;
      left?: number;
      bottom?: number;
      right?: number;
      defaultColor?: number;
      disabled?: boolean;
      positionRelativeToLayer?: boolean;
      fromVectorData?: boolean; // set to true if the mask is generated from vector data, false if it's a bitmap provided by user
      userMaskDensity?: number;
      userMaskFeather?: number; // px
      vectorMaskDensity?: number;
      vectorMaskFeather?: number;
      canvas?: HTMLCanvasElement;
      imageData?: ImageData;
    }

    Similar to layer image data this data has top, left, bottom and right offsets specified.

    fromVectorData Specifies if the mask image data was generated from vectorMask, if fromVectorData is set to false and both vectorMask and mask properties are present it means that the layer has 2 active masks, bitmap mask and a vector mask.

    mask property will be missing when layer has no mask, it can be ommited when writing.

  • effects Object describing layer "Blending Options"

    Effects property follows the following structure:

    interface LayerEffectsInfo {
      disabled?: boolean; // indicates if all effects are disabled
      scale?: number;
      dropShadow?: LayerEffectShadow[];
      innerShadow?: LayerEffectShadow[];
      outerGlow?: LayerEffectsOuterGlow;
      innerGlow?: LayerEffectInnerGlow;
      bevel?: LayerEffectBevel;
      solidFill?: LayerEffectSolidFill[];
      satin?: LayerEffectSatin;
      stroke?: LayerEffectStroke[];
      gradientOverlay?: LayerEffectGradientOverlay[];
      patternOverlay?: LayerEffectPatternOverlay; // not supported yet
    }

    Some of the effects (specified here as arrays) can be specified multiple times. This is new feature, only supported in more recent versions of Photoshop.

    This property is not present if there are not blending options set for the layer. This property can be ommited when writing.

  • text text properties of text layer

    TODO

  • patterns not supported yet

  • vectorFill Fill color, gradient or pattern for the vector mask. Use type field to distinguish between different vector fill types, like this:

    switch (vectorFill.type) {
      case 'color':
        // solid color fill ({ color: Color } type)
        break;
      case 'solid':
        // solid gradient fill (EffectSolidGradient type)
        break;
      case 'noise':
        // noise gradient fill (EffectNoiseGradient type)
        break;
      case 'pattern':
        // pattern fill (EffectPattern type)
        break;
    }
  • vectorStroke Vector stroke parameters for the vector mask. This field also contains parameters related to vectorFill. This property has following structure:

    type VectorStroke = {
      	strokeEnabled?: boolean; // vector drawing has stroke
      	fillEnabled?: boolean; // vector drawing has fill (specified by vectorFill property)
      	lineWidth?: UnitsValue;
      	lineDashOffset?: UnitsValue;
      	miterLimit?: number;
      	lineCapType?: LineCapType;
      	lineJoinType?: LineJoinType;
      	lineAlignment?: LineAlignment;
      	scaleLock?: boolean;
      	strokeAdjust?: boolean;
      	lineDashSet?: UnitsValue[];
      	blendMode?: BlendMode;
      	opacity?: number;
      	content?: VectorContent; // stroke color, gradient or pattern (see `vectorFill` field for more information)
      	resolution?: number;
      }
  • vectorMask Specifies vector mask used by vectorFill and vectorStroke to draw vector images.

    TODO: expand this description

  • usingAlignedRendering unknown functionality

  • pathList not supported yet

  • adjustment indicates that the layer is an adjustment layer. Adjustment layer do not have image data. This property is not present on non-adjustment layers.

    Use type field of the adjustment object to distinguish between different adjustment layer types.

    switch (adjustment.type) {
      case 'brightness/contrast':
        // handle BrightnessAdjustment layer
        break;
      case 'levels':
        // handle LevelsAdjustment layer
        break;
      // ... other cases ...
    }

    see all Adjustment layer types in psd.ts file

  • placedLayer indicates that this is smart object layer, see Smart Objects section for more information. This property is only present on smart object layers.

  • vectorOrigination TODO

  • compositorUsed internal photoshop information

  • artboard Artboard location and parameters, this property is only present when using artboards. Artbord is object is following this structure:

    type Artboard = {
      rect: { top: number; left: number; bottom: number; right: number; };
      guideIndices?: any[];
      presetName?: string;
      color?: Color;
      backgroundType?: number;
    }

  • Advanced blending options

    • fillOpacity"Fill Opacity" level (value ranges from 0 to 1)

    • knockout "Knockout" value

    • blendClippendElements "Blend Clipped Layers as Group" value

    • blendInteriorElements "Blend Interior Effects as Group" value

    • transparencyShapesLayer "Transparency Shapes Layer" value

  • engineData internal text information

Group

Example group structure:

{
  "top": 0,
  "left": 0,
  "bottom": 0,
  "right": 0,
  "blendMode": "normal",
  "opacity": 1,
  "clipping": false,
  "timestamp": 1450198418.5245173,
  "transparencyProtected": false,
  "hidden": false,
  "name": "Group 1",
  "nameSource": "lset",
  "id": 5,
  "sectionDivider": {
    "type": 1,
    "key": "pass",
    "subType": 0
  },
  "protected": {
    "transparency": false,
    "composite": false,
    "position": false
  },
  "layerColor": "none",
  "referencePoint": {
    "x": 0,
    "y": 0
  },
  "opened": true,
  "children": [
    // layers here
  ]
}

Groups don't have canvas / imageData property, as you can't draw directly on a group. Groups don't have any other special layer property like text, vectorFill, adjustment or placedLayer.

Group-only fields:

  • children contains list of all layers in this group. Cannot be ommited when writing, without this field a group will be assumed to be a regular layer. Use empty children array when writing empty group.

  • opened indicates if the group is expanded or collapsed in the layer list. If ommited a default values of true will be assumed.

  • sectionDivider internal Photoshop property, can be ignored and ommited when writing.

Image Resources

Image resources are global document settings. Any of these settings can be ommited when writing. PSD file can have following global options:

  • versionInfo Version information of Program that generated the PSD file, example value:

    versionInfo = {
      "hasRealMergedData": true,
      "writerName": "Adobe Photoshop",
      "readerName": "Adobe Photoshop CS6",
      "fileVersion": 1
    }
  • layerSelectionIds list of layer IDs of selected layers, these layers will be selected when you open PSD document in Photoshop.

  • pixelAspectRatio Specifies aspect ratio of the PSD document pixels, almost always 1

    pixelAspectRatio = {
      "aspect": 1
    }
  • gridAndGuidesInformation Information about document guides

    Example value:

    gridAndGuidesInformation = {
      "grid": {
        "horizontal": 576,
        "vertical": 576
      },
      "guides": [
        {
          "location": 531.4375,
          "direction": "vertical" // "horizontal" or "vertical"
        },
        // ... more guides here ...
      ]
    };
  • resolutionInfo Image resolution info, specifies physical size of the image pixels. Example value:

    resolutionInfo: {
      "horizontalResolution": 72,
      "horizontalResolutionUnit": "PPI", // 'PPI' (pixels per inch) or 'PPCM' (pixels per centimeter)
      "widthUnit": "Inches",
      "verticalResolution": 72,
      "verticalResolutionUnit": "PPI",
      "heightUnit": "Inches" // 'Inches', 'Centimeters', 'Points', 'Picas' or 'Columns'
    },
  • thumbnail Canvas element with thumbnail image for the document, this property can be missing in some PSD files. This property can be ommited when writing if there's no need to support tumbnails, this field can be automatically generated from composite image data if generateThumbnail: true option is passed to writePsd function.

  • thumbnailRaw This property will be used instead if useRawThumbnail option is specitied when reading PSD file.

  • xmpMetadata XMP metadata. File info as XML description. See http://www.adobe.com/devnet/xmp/

  • iccUntaggedProfile ICC Untagged Profile

  • printInformation, printScale and printFlags Specifies options for printing the document.

  • layerState TODO

  • layersGroup TODO

  • layerGroupsEnabledId TODO

  • alphaIdentifiers TODO

  • alphaChannelNames TODO

  • globalAngle TODO

  • globalAltitude TODO

  • urlsList TODO

  • captionDigest TODO

  • backgroundColor TODO

  • idsSeedNumber TODO

  • pathSelectionState TODO

  • imageReadyVariables TODO

  • imageReadyDataSets TODO

Smart Objects

Layers with placedLayer property are smart object layers. placedLayer property has the following structure:

interface PlacedLayer {
	id: string; // id of linked image file (psd.linkedFiles)
	placed?: string; // unique id
	type: PlacedLayerType;
	transform: number[]; // x, y of 4 corners of the transform
	nonAffineTransform?: number[]; // x, y of 4 corners of the transform
	width?: number;
	height?: number;
	resolution?: UnitsValue;
	warp?: Warp;
	crop?: number;
}

The Psd object has linkedFiles property, that specifies list of all files linked in smart objects. Each linked file has following structure:

interface LinkedFile {
	id: string;
	name: string;
	type?: string;
	creator?: string;
	data?: Uint8Array;
	time?: Date; // for external files
	childDocumentID?: string;
	assetModTime?: number;
	assetLockedState?: number;
}

id field in PlacedLayer refers to the id in LinkedFile. Example values:

  layer.placedLayer = {
    "id": "20953ddb-9391-11ec-b4f1-c15674f50bc4", // id that matches linkedFiles ID
    "placed": "20953dda-9391-11ec-b4f1-c15674f50bc4", // unique id for this object
    "type": "raster", // one of the 'unknown', 'vector', 'raster' or 'image stack'
    "transform": [ // x, y of 4 corners of the transform box
      29,
      28,
      83,
      28,
      83,
      82,
      29,
      82
    ],
    "width": 32, // width and height of the target image
    "height": 32,
    "resolution": {
      "value": 299.99940490722656,
      "units": "Density"
    }
  };

  psd.linkedFiles = [
    {
      "id": "20953ddb-9391-11ec-b4f1-c15674f50bc4",
      "name": "cat.png"
      "data": fileContentsAsUint8Array,
    }
  ];

Units value

Some fields in the PSD document specity a value with units, those fields use UnitsValue type:

interface UnitsValue {
	units: Units;
	value: number;
}

value fields can be any number value, units fields has to be one of the supported units: "Pixels", "Points", "Picas", "Millimeters", "Centimeters", "Inches", "None", "Density". Some fields only support some of the units, check with Photoshop for supported units.

Example values:

var distance = { value: 5, units: "Pixels" };
var lineWidth = { value: 3, units: "Points" };

Colors

Many fields in PSD file support passing color in different color formats (RGBA, RGB, HSB, CMYK, LAB, Grayscale) Those fields will use Color type which is a union of all of these color formats. The color types have following structure:

type RGBA = { r: number; g: number; b: number; a: number; }; // values from 0 to 255
type RGB = { r: number; g: number; b: number; }; // values from 0 to 255
type FRGB = { fr: number; fg: number; fb: number; }; // values from 0 to 1 (can be above 1)
type HSB = { h: number; s: number; b: number; }; // values from 0 to 1
type CMYK = { c: number; m: number; y: number; k: number; }; // values from 0 to 255
type LAB = { l: number; a: number; b: number; }; // values `l` from 0 to 1; `a` and `b` from -1 to 1
type Grayscale = { k: number }; // values from 0 to 255
type Color = RGBA | RGB | FRGB | HSB | CMYK | LAB | Grayscale;

When you want to set field with a Color type, it's pretty straightforward, you can just choose any of the formats you like and set it on the field:

strokeEffect.color = { h: 0.79, s: 0.54, b: 0.93 };

Reading a color field is more complicated, you need to check what color format the field is in and then run correct code to handle it, here's how to do it for all color types:

if ('l' in color) {
  // color is LAB
} else if ('c' in color) {
  // color is CMYK
} else if ('h' in color) {
  // color is HSB
} else if ('k' in color) {
  // color is Grayscale
} else if ('a' in color) {
  // color is RGBA
} else if ('rf' in color) {
  // color is FRGB
} else {
  // color is RGB
}

If you expect the fields to be in one specific format you can just verify that it's correct and throw an error if it's a format that you didn't expect:

// handle only RGB colors
if ('r' in color && !('a' in color)) {
  // color is RGB
} else {
  throw new Error('Invalid color');
}