ag-psd

repository·master·Indexed 20 days ago

https://github.com/agamnentzar/ag-psd

A JavaScript library for reading and writing Photoshop Document (PSD) files, compatible with Node.js and Browser environments. It allows developers to programmatically manipulate PSD structures and image data, providing functions like readPsd and writePsd. The library supports custom ReadOptions and WriteOptions to control data loading and generation, including support for text layers and Web Worker integration via OffscreenCanvas.

Tokens
24.6K
Snippets
68
Records
103
Agent score
71%

What's inside ag-psd

  1. Identify layer types by properties

    master

    Since layers are part of a polymorphic tree, you can determine a layer's specific type by checking for the existence of certain unique properties:

    • Group: Has a children property.
    • Text Layer: Has a text property.
    • Adjustment Layer: Has an adjustment property.
    • Smart Object Layer: Has a placedLayer property.
    • Vector Layer: Has a vectorMask property.
    • Bitmap Layer: A regular layer that has a canvas or imageData property (and lacks the above).

    Note: Many properties are shared across types. For example, almost any layer can have a mask property.

    // Complex parsing to identify specific layer types
    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
      }
    }
  2. Handle Color types in PSD files

    master

    PSD color fields use a Color union type. When writing, you can provide any supported format directly. When reading, you must check the object keys to determine which color space is being used.

    Supported Color Formats:

    • RGBA: { r, g, b, a } (0-255)
    • RGB: { r, g, b } (0-255)
    • FRGB: { fr, fg, fb } (0-1, can be > 1)
    • HSB: { h, s, b } (0-1)
    • CMYK: { c, m, y, k } (0-255)
    • LAB: { l, a, b } (l is 0-1, a and b are -1 to 1)
    • Grayscale: { k } (0-255)

    Reading Colors Pattern: To safely handle colors, use a type guard pattern checking for specific keys (e.g., 'l' for LAB, 'c' for CMYK, 'h' for HSB).

    // Writing a color
    strokeEffect.color = { h: 0.79, s: 0.54, b: 0.93 };
    
    // Reading a color
    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
    }
  3. How PSD layers and groups are structured

    master

    A PSD document follows a tree structure where the root object contains a children property. This property holds both regular layers and groups in order from top to bottom (as seen in Photoshop).

    Important for rendering: If you are drawing layer images to reconstruct a document image, you must draw them in reverse order of their appearance in the children array.

    Groups are distinguished from regular layers by the presence of a children property. Each group can contain nested layers and other groups.

    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: []
            }
          ]
        }
      ]
    }
  4. Configure Image Resources (Global Document Settings)

    master

    Image resources are global settings for a PSD document. These can be omitted when writing a file. Key settings include:

    • versionInfo: Metadata about the software that generated the file.
    • layerSelectionIds: A list of layer IDs that should be selected when the file is opened in Photoshop.
    • pixelAspectRatio: The aspect ratio of pixels (usually { "aspect": 1 }).
    • gridAndGuidesInformation: Contains grid (horizontal/vertical spacing) and guides (array of objects with location and direction).
    • resolutionInfo: Physical size settings including horizontalResolution, horizontalResolutionUnit ('PPI' or 'PPCM'), widthUnit ('Inches', 'Centimeters', 'Points', 'Picas', or 'Columns'), verticalResolution, verticalResolutionUnit, and heightUnit.
    • thumbnail: A Canvas element for the document thumbnail. If omitted during writing, you can use the generateThumbnail: true option in writePsd to auto-generate it from composite data.
    • thumbnailRaw: Used instead of thumbnail if the useRawThumbnail option is specified during reading.
    • xmpMetadata: XML description of file info.
    • iccUntaggedProfile: ICC profile data.
    • printInformation, printScale, and printFlags: Printing options.
    // Example resolutionInfo
    resolutionInfo: {
      "horizontalResolution": 72,
      "horizontalResolutionUnit": "PPI",
      "widthUnit": "Inches",
      "verticalResolution": 72,
      "verticalResolutionUnit": "PPI",
      "heightUnit": "Inches"
    }
    
    // Example gridAndGuidesInformation
    gridAndGuidesInformation = {
      "grid": {
        "horizontal": 576,
        "vertical": 576
      },
      "guides": [
        {
          "location": 531.4375,
          "direction": "vertical"
        }
      ]
    };
  5. Understand the Psd object structure

    master

    The Psd object is the core data structure used by readPsd and writePsd. It represents the entire PSD document, including dimensions, color mode, composite image data, and a hierarchy of layers or groups.

    Key Properties

    • width & height: Document size in pixels (required when writing).
    • channels: Number of color channels (e.g., 3 for RGB). The library currently only supports RGB with 3 channels for writing.
    • bitsPerChannel: Bits per channel (1 for bitmap, 8 for others). The library currently only supports 8-bit; 16 or 32-bit values will be converted to 8-bit if using canvas.
    • colorMode: The color mode of the document (see ColorMode enum).
    • canvas or imageData: The composite image data for the entire document.
    • children: An array of layers and groups at the root level.
    • artboards: Global options for artboards (if present).
    • annotations: An array of document annotations (if present).
    // example psd document structure
    const psd: Psd = {
      "width": 300,
      "height": 200,
      "channels": 3,
      "bitsPerChannel": 8,
      "colorMode": 3,
      "canvas": <Canvas>,
      "children": [],
    };
  6. Work with Smart Objects (Placed Layers)

    master

    Layers with a placedLayer property are treated as Smart Objects. To implement a Smart Object, you must link the layer to an entry in the PSD's linkedFiles array using a matching id.

    PlacedLayer Structure:

    • id: Must match an id in psd.linkedFiles.
    • placed: A unique identifier for the object.
    • type: One of 'unknown', 'vector', 'raster', or 'image stack'.
    • transform: An array of 8 numbers representing the x, y coordinates of the 4 corners of the transform box.
    • width/height: Target image dimensions.
    • resolution: A UnitsValue object.

    LinkedFile Structure (in psd.linkedFiles):

    • id: The unique identifier referenced by the PlacedLayer.
    • name: Filename (e.g., 'cat.png').
    • data: The file content as a Uint8Array.
    // Example Smart Object setup
    layer.placedLayer = {
      "id": "20953ddb-9391-11ec-b4f1-c15674f50bc4",
      "placed": "20953dda-9391-11ec-b4f1-c15674f50bc4",
      "type": "raster",
      "transform": [29, 28, 83, 28, 83, 82, 29, 82],
      "width": 32,
      "height": 32,
      "resolution": {
        "value": 299.99940490722656,
        "units": "Density"
      }
    };
    
    psd.linkedFiles = [
      {
        "id": "20953ddb-9391-11ec-b4f1-c15674f50bc4",
        "name": "cat.png",
        "data": fileContentsAsUint8Array
      }
    ];
  7. Understand ag-psd limitations

    master

    Before using ag-psd, be aware of the following constraints:

    • Color Modes: Does not support reading Indexed, CMYK, Multichannel, Duotone, or LAB (all are converted to RGB). Does not support writing any mode other than RGB.
    • Bit Depth: Does not support 16 bits per channel.
    • File Formats: Does not support Large Document Format (8BPB/PSB).
    • Features: No support for animations, color palettes, 3D effects, or some new Photoshop features.
    • Patterns: Limited support for document-level patterns (Patt/Pat2/Pat3). Does not support zip-compressed, Indexed, 16-bit, or Pattern Overlay layer effects.
    • Text Layers: Implementation is incomplete. Writing vertical text may break files. Does not support Paragraph/Character styles. The library does not redraw bitmap data for text layers; updating text via the API requires the user to manually update image data or re-save in Photoshop to avoid warnings.
    • Image Data: The library does not automatically redraw layer/composite data when blending options, vector data, or text options are changed. Users must handle these updates manually.
  8. Modify PSD documents safely

    master

    The general workflow for modifying a PSD is to read the document, apply updates, and write it back.

    Important: Avoiding Image Corruption When reading and writing the same document, automatic alpha channel pre-multiplication during canvas loading can corrupt image data. To prevent this, use the useImageData: true option in readPsd to work with raw image data.

    Important: Visual Consistency ag-psd does not automatically regenerate composite images or thumbnails when you change visual properties (like layer order, blending modes, or layer content). If you modify these, you must manually update:

    • psd.canvas or psd.imageData (for the composite image)
    • psd.imageResources.thumbnail or psd.imageResources.thumbnailRaw (for the thumbnail)

    If you don't need to support file previews in tools like Adobe Bridge, you can omit the thumbnail entirely.

    const psd = readPsd(inputBuffer, { useImageData: true });
    
    // TODO: update psd document here
    
    const outuptBuffer = writePsd(psd); 
  9. Use ag-psd in the Browser

    master

    In the browser, you can use the library via standard imports or a bundled script.

    Reading via XMLHttpRequest:

    import { readPsd } from 'ag-psd';
    
    const xhr = new XMLHttpRequest();
    xhr.open('GET', 'my-file.psd', true);
    xhr.responseType = 'arraybuffer';
    xhr.addEventListener('load', function () {
      const psd = readPsd(xhr.response);
      document.body.appendChild(psd.children[0].canvas);
    }, false);
    xhr.send();

    Writing via FileSaver.js:

    import { writePsd } from 'ag-psd';
    
    const psd = { width: 300, height: 200, children: [{ name: 'Layer #1' }] };
    const buffer = writePsd(psd);
    const blob = new Blob([buffer], { type: 'application/octet-stream' });
    saveAs(blob, 'my-file.psd');

    Using the Bundle: Include the bundle in your HTML:

    <script src="node_modules/ag-psd/dist/bundle.js"></script>
    <script>
      var readPsd = agPsd.readPsd;
    </script>
    import { readPsd } from 'ag-psd';
    
    const xhr = new XMLHttpRequest();
    xhr.open('GET', 'my-file.psd', true);
    xhr.responseType = 'arraybuffer';
    xhr.addEventListener('load', function () {
      const buffer = xhr.response;
      const psd = readPsd(buffer);
    
      console.log(psd);
    
      document.body.appendChild(psd.children[0].canvas);
    }, false);
    xhr.send();
  10. Update existing text layers

    master

    To update text in an existing PSD, read the file, modify the text.text property, and optionally clear the old canvas data to prevent conflicts.

    Crucial: Forcing Photoshop to Redraw When you update text data without providing new image data, Photoshop may show an error. To mitigate this, use the invalidateTextLayers: true option in writePsd. This prompts Photoshop to redraw the text layer from the text data upon loading. If you don't use this, the layer might display outdated or broken image data.

    const psd = readPsd(inputBuffer);
    
    // assuming first layer is the one you want to update and has text already present
    psd.children[0].text.text = 'New text here';
    
    // optionally remove outdated image data
    psd.children[0].canvas = undefined;
    
    // needs `invalidateTextLayers` option to force Photoshop to redraw text layer on load
    const outuptBuffer = writePsd(psd, { invalidateTextLayers: true }); 
  11. Handle composite image data via canvas or imageData

    master

    You can access the composite image data of a PSD document using either the canvas property or the imageData property.

    Using canvas (Default)

    • Provides an HTMLCanvasElement (browser) or node-canvas object (Node.js).
    • Note: For 16-bit or 32-bit documents, data is converted to an 8-bit canvas, which results in precision loss.
    • Set the useImageData: true option when calling readPsd.
    • Provides an ImageData object containing width, height, and data.
    • Benefits: Bypasses alpha premultiplication (preserving accurate color) and preserves bit depth precision.
    • Bit Depth Support:
      • 16-bit: imageData contains a Uint16Array (values 0-65535).
      • 32-bit: imageData contains a Float32Array (values 0-1 in linear color space).

    Optimization

    • Use skipCompositeImageData: true during reading to skip this field and save memory/processing time if you don't need the composite image.

    Writing PSDs

    • You can provide either canvas or imageData when writing. You may also omit it entirely.