# 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](/README.md#functions)

You can see examples of different PSD documents and their corresponding JSON data in our [test folder](/test/read). 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

```ts
// 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.

  ![](/files/width-height.png)

- `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.

  ![](/files/color-mode.png)

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

  ```ts
  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](#layers-and-groups)

- `imageResources` contains all document-wide parameters [see Image Resouces](#image-resources)

- `linkedFiles` contains list of files linked in smart objects [see Smart Objects](#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.

  ```ts
  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.

  ![](/files/annotations.png)

  Each annotation object has a following structure:

  ```ts
  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):

```js
// 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:

```js
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:

```js
// 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:

```ts
// 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:

```js
{
  "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:

  ```ts
  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.

  [](/files/blend-modes.png)

- `canvas` (or `imageData`) see `canvas` property description in [Basic document structure](#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.

  ![](/files/opacity.png)

- `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.

  ![](/files/clipping.png)
  
  ![](/files/clipping-2.png)

- `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.

  ![](/files/locks.png)

  - `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.

  ![](/files/hidden.png)

- `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:

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

  ![](/files/layer-color.png)

- `version` _unknown functionality_

- `referencePoint` _unknown functionality_

- `mask` Layer mask, property has the following structure.

  ```ts
  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;
  }
  ```

  ![](/files/mask.png)

  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"

  ![](/files/effects.png)
  
  ![](/files/effects-2.png)

  Effects property follows the following structure:

  ```ts
  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:

  ```ts
  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:

  ```ts
  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.

  ![](/files/adjustment.png)
  
  Use `type` field of the adjustment object to distinguish between different adjustment layer types.
  
  ```ts
  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](/src/psd.ts)

- `placedLayer` indicates that this is smart object layer, see [Smart Objects section](#smart-objects) 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:

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

  ![](/files/artboards.png)

- **Advanced blending options**

  ![](/files/advanced-blending-options.png)

  - `fillOpacity`"Fill Opacity" level (value ranges from 0 to 1)
  
    ![](/files/fill-opacity.png)
  - `knockout` "Knockout" value
  - `blendClippendElements` "Blend Clipped Layers as Group" value
  - `blendInteriorElements` "Blend Interior Effects as Group" value
  - `transparencyShapesLayer` "Transparency Shapes Layer" value
  - `linkGroup` and `linkGroupEnabled` Layers with the same `linkGroup` value are linked together for moving/transforming. `0` value means no linking is active.
  
    ![](/files/linked-layers.png)

- `engineData` internal text information

### Group

Example group structure:

```js
{
  "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.

  ![](/files/group-open.png)

- `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:

  ```js
  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

  ```js
  pixelAspectRatio = {
    "aspect": 1
  }
  ```

- `gridAndGuidesInformation` Information about document guides

  ![](/files/guides.png)
  
  Example value:
  
  ```js
  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:

  ```js
  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:

```ts
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:

```ts
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:

```js
  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:

```ts
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:

```ts
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:

```ts
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:

```ts
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:

```ts
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:

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