# psd_tools.api.layers

Layer module.

This module implements the high-level layer API for psd-tools, providing Pythonic interfaces for working with Photoshop layers. It defines the layer type hierarchy and common operations.

Key classes:

- `Layer`: Base class for all layer types

- `GroupMixin`: Mixin for layers that contain children (groups, documents)

- `Group`: Folder/group layer containing other layers

- `PixelLayer`: Regular raster layer with pixel data

- `TypeLayer`: Text layer with typography information

- `ShapeLayer`: Vector shape layer

- `SmartObjectLayer`: Embedded or linked smart object

- `AdjustmentLayer`: Non-destructive adjustment (curves, levels, etc.)

Layer hierarchy:

Layers are organized in a tree structure where groups can contain child layers. The `GroupMixin` provides iteration, indexing, and search capabilities:

    # Iterate through all layers
    for layer in psd:
        print(layer.name)

    # Access by index
    first_layer = psd[0]

    # Check if layer is a specific type
    if layer.kind == 'pixel':
        pixels = layer.numpy()

Common layer properties:

- `name`: Layer name

- `visible`: Visibility flag

- `opacity`: Opacity (0-255)

- `blend_mode`: Blend mode enum

- `bbox`: Bounding box (left, top, right, bottom)

- `width`, `height`: Dimensions

- `kind`: Layer type string (‘pixel’, ‘group’, ‘type’, etc.)

- `parent`: Parent layer or document

Layer operations:

- `composite()`: Render layer to PIL Image

- `numpy()`: Get pixel data as NumPy array

- `topil()`: Convert to PIL Image

- `has_mask()`: Check if layer has a mask

- `has_clip_layers()`: Check if layer has clipping mask

Example usage:

    from psd_tools import PSDImage

    psd = PSDImage.open('document.psd')

    # Access first layer
    layer = psd[0]

    # Modify layer properties
    layer.visible = False
    layer.opacity = 128
    layer.name = "New Name"

    # Get pixel data
    pixels = layer.numpy()  # NumPy array
    image = layer.topil()   # PIL Image

    # Work with groups
    for group in psd.descendants():
        if group.kind == 'group':
            print(f"Group: {group.name} with {len(group)} layers")

    # Composite specific layer
    rendered = layer.composite()
    rendered.save('layer.png')

Layer types are automatically determined from the underlying PSD structures and exposed through the `kind` property for easy type checking.

## Layer

class psd_tools.api.layers.Layer(parent: GroupMixin, record: LayerRecord, channels: ChannelDataList)
  property bbox: tuple[int, int, int, int]

    (left, top, right, bottom) tuple.

  property blend_mode: BlendMode

    Blend mode of this layer. Writable.

    Example:

        from psd_tools.constants import BlendMode
        if layer.blend_mode == BlendMode.NORMAL:
            layer.blend_mode = BlendMode.SCREEN

    Returns:

      `BlendMode`.

  property bottom: int

    Bottom coordinate.

    Returns:

      int

  property clip_layers: list[Self]

    Clip layers associated with this layer.

    Returns:

      list of layers

  property clipping: bool

    Clipping flag for this layer. Writable.

    Returns:

      bool

  property clipping_layer: bool

    Deprecated. Use clipping property instead.

  composite(viewport: tuple[int, int, int, int] | None = None, force: bool = False, color: float | tuple[float, ...] | ndarray = 1.0, alpha: float | ndarray = 0.0, layer_filter: Callable | None = None, apply_icc: bool = True) → Image | None

    Composite layer and masks (mask, vector mask, and clipping layers).

    Parameters:

      - viewport – Viewport bounding box specified by (x1, y1, x2, y2) tuple. Default is the layer’s bbox.

      - force – Boolean flag to force vector drawing.

      - color – Backdrop color specified by scalar or tuple of scalar. The color value should be in [0.0, 1.0]. For example, (1., 0., 0.) specifies red in RGB color mode.

      - alpha – Backdrop alpha in [0.0, 1.0].

      - layer_filter – Callable that takes a layer as argument and returns whether if the layer is composited. Default is `is_visible()`.

    Returns:

      `PIL.Image.Image` or None.

  create_mask(image: Image, top: int | None = None, left: int | None = None, compression: Compression = Compression.RLE) → Mask

    Create a pixel mask on this layer from a PIL Image.

    If the image has an alpha channel (e.g. RGBA, LA), the alpha channel is used as the mask data. Otherwise the image is converted to grayscale (`L` mode). White (255) means fully unmasked, black (0) means fully masked.

    Parameters:

      - image – Source `Image` for the mask.

      - top – Top offset of the mask. Defaults to the layer’s top.

      - left – Left offset of the mask. Defaults to the layer’s left.

      - compression – Compression algorithm for the mask data.

    Returns:

      The new `Mask`.

    Raises:

      ValueError – If the layer already has a mask.

  delete_layer() → Self

    Deprecated: Use layer.parent.remove(layer) instead.

  property effects: Effects

    Layer effects.

    Returns:

      `Effects`

  property fill_opacity: int

    Fill opacity of this layer in [0, 255] range. Writable.

    Returns:

      int

  has_clip_layers(visible: bool = False) → bool

    Returns True if the layer has associated clipping.

    Parameters:

      visible – If True, check for visible clipping layers.

    Returns:

      bool

  has_effects(enabled: bool = True, name: str | None = None) → bool

    Returns True if the layer has effects.

    Parameters:

      - enabled – If True, check for enabled effects.

      - name – If given, check for specific effect type.

    Returns:

      bool

  has_mask() → bool

    Returns True if the layer has a mask.

    Returns:

      bool

  has_origination() → bool

    Returns True if the layer has live shape properties.

    Returns:

      bool

  has_pixels() → bool

    Returns True if the layer has associated pixels. When this is True, topil method returns `PIL.Image.Image`.

    Returns:

      bool

  has_stroke() → bool

    Returns True if the shape has a stroke.

  has_vector_mask() → bool

    Returns True if the layer has a vector mask.

    Returns:

      bool

  property height: int

    Height of the layer.

    Returns:

      int

  is_group() → bool

    Return True if the layer is a group.

    Returns:

      bool

  is_visible() → bool

    Layer visibility. Takes group visibility in account.

    Returns:

      bool

  property kind: str

    Kind of this layer, such as group, pixel, shape, type, smartobject, or psdimage. Class name without layer suffix.

    Returns:

      str

  property layer_id: int

    Layer ID.

    Returns:

      int layer id. if the layer is not assigned an id, -1.

  property left: int

    Left coordinate. Writable.

    Returns:

      int

  lock(lock_flags: int = ProtectedFlags.COMPLETE) → None

    Locks a layer accordind to the combination of flags.

    Parameters:

      lockflags – An integer representing the locking state

    Example using the constants of ProtectedFlags and bitwise or operation to lock both pixels and positions:

        layer.lock(ProtectedFlags.COMPOSITE | ProtectedFlags.POSITION)

  property mask: Mask | None

    Returns mask associated with this layer.

    Returns:

      `Mask` or None

  move_down(offset: int = 1) → Self

    Moves the layer down a certain offset within the group the layer is in.

    Parameters:

      offset – The number of positions to move the layer down (can be negative).

    Raises:

      - ValueError – If layer has no parent or parent is not a group

      - IndexError – If the new index is out of bounds

    Returns:

      self

  move_to_group(group: GroupMixin) → Self

    Deprecated: Use group.append(layer) instead.

    Parameters:

      group – The group the current layer will be moved into.

  move_up(offset: int = 1) → Self

    Moves the layer up a certain offset within the group the layer is in.

    Parameters:

      offset – The number of positions to move the layer up (can be negative).

    Raises:

      - ValueError – If layer has no parent or parent is not a group

      - IndexError – If the new index is out of bounds

    Returns:

      self

  property name: str

    Layer name. Writable.

    Returns:

      str

  next_sibling(visible: bool = False) → Self | None

    Next sibling of this layer.

  numpy(channel: str | None = None, real_mask: bool = True) → ndarray | None

    Get NumPy array of the layer.

    Parameters:

      channel – Which channel to return, can be ‘color’, ‘shape’, ‘alpha’, or ‘mask’. Default is ‘color+alpha’.

    Returns:

      `numpy.ndarray` or None if there is no pixel.

  property offset: tuple[int, int]

    (left, top) tuple. Writable.

    Returns:

      tuple

  property opacity: int

    Opacity of this layer in [0, 255] range. Writable.

    Returns:

      int

  property origination: list[Origination]

    Property for a list of live shapes or a line.

    Some of the vector masks have associated live shape properties, that are Photoshop feature to handle primitive shapes such as a rectangle, an ellipse, or a line. Vector masks without live shape properties are plain path objects.

    See `psd_tools.api.shape`.

    Returns:

      List of `Invalidated`, `Rectangle`, `RoundedRectangle`, `Ellipse`, or `Line`.

  property parent: GroupMixinProtocol | None

    Parent of this layer.

  previous_sibling(visible: bool = False) → Self | None

    Previous sibling of this layer.

  property reference_point: tuple[float, float]

    Reference point of this layer as (x, y) tuple in the canvas coordinates. Writable.

    Reference point is used for transformations such as rotation and scaling.

    Returns:

      (x, y) tuple

  remove_mask() → None

    Remove the pixel mask from this layer.

    Raises:

      ValueError – If the layer does not have a mask.

  property right: int

    Right coordinate.

    Returns:

      int

  property sheet_color: SheetColorType

    Color label of this layer in the Photoshop layers panel. Writable.

    Returns:

      `SheetColorType`

  property size: tuple[int, int]

    (width, height) tuple.

    Returns:

      tuple

  property stroke: Stroke | None

    Property for strokes.

  property tagged_blocks: TaggedBlocks

    Layer tagged blocks that is a dict-like container of settings.

    See `psd_tools.constants.Tag` for available keys.

    Returns:

      `TaggedBlocks`.

    Example:

        from psd_tools.constants import Tag
        metadata = layer.tagged_blocks.get_data(Tag.METADATA_SETTING)

  property top: int

    Top coordinate. Writable.

    Returns:

      int

  topil(channel: int | None = None, apply_icc: bool = True) → Image | None

    Get PIL Image of the layer.

    Parameters:

      - channel – Which channel to return; e.g., 0 for ‘R’ channel in RGB image. See `ChannelID`. When None, the method returns all the channels supported by PIL modes.

      - apply_icc – Whether to apply ICC profile conversion to sRGB.

    Returns:

      `PIL.Image.Image`, or None if the layer has no pixels.

    Example:

        from psd_tools.constants import ChannelID

        image = layer.topil()
        red = layer.topil(ChannelID.CHANNEL_0)
        alpha = layer.topil(ChannelID.TRANSPARENCY_MASK)

    Note

    Not all of the PSD image modes are supported in `PIL.Image.Image`. For example, ‘CMYK’ mode cannot include alpha channel in PIL. In this case, topil drops alpha channel.

  update_mask(image: Image, top: int | None = None, left: int | None = None, compression: Compression = Compression.RLE) → Mask

    Update the pixel mask of this layer with a new image.

    If the image has an alpha channel (e.g. RGBA, LA), the alpha channel is used as the mask data. Otherwise the image is converted to grayscale (`L` mode). White (255) means fully unmasked, black (0) means fully masked.

    Parameters:

      - image – New source `Image` for the mask.

      - top – New top offset of the mask. Defaults to current mask top.

      - left – New left offset of the mask. Defaults to current mask left.

      - compression – Compression algorithm for the mask data.

    Returns:

      The updated `Mask`.

    Raises:

      ValueError – If the layer does not have a mask.

  property vector_mask: VectorMask | None

    Returns vector mask associated with this layer.

    Returns:

      `VectorMask` or None

  property visible: bool

    Layer visibility. Doesn’t take group visibility in account. Writable.

    Returns:

      bool

  property width: int

    Width of the layer.

    Returns:

      int

## Artboard

class psd_tools.api.layers.Artboard(parent: GroupMixin, record: LayerRecord, channels: ChannelDataList)

  Artboard is a special kind of group that has a pre-defined viewbox.

  property bbox: tuple[int, int, int, int]

    (left, top, right, bottom) tuple.

  property bottom: int

    Bottom coordinate (computed from children, read-only).

  composite(viewport: tuple[int, int, int, int] | None = None, force: bool = False, color: float | tuple[float, ...] | ndarray | None = None, alpha: float | ndarray | None = None, layer_filter: Callable | None = None, apply_icc: bool = True) → Image | None

    Composite layer, automatically applying the artboard’s background color.

    When either `color` or `alpha` is not explicitly provided, reads the artboard background from the ARTBOARD_DATA tagged block (`artboardBackgroundType` and `Clr` descriptor) and uses the missing value or values as the compositing backdrop.

  property left: int

    Left coordinate (computed from children, read-only).

  property right: int

    Right coordinate (computed from children, read-only).

  property top: int

    Top coordinate (computed from children, read-only).

## Group

class psd_tools.api.layers.Group(parent: GroupMixin, record: LayerRecord, channels: ChannelDataList)

  Group of layers.

  Example:

      group = psd[1]
      for layer in group:
          if layer.kind == 'pixel':
              print(layer.name)

  property blend_mode: BlendMode

    Blend mode of this layer. Writable.

  property bottom: int

    Bottom coordinate (computed from children, read-only).

  property clipping: bool

    Clipping flag for this layer. Writable.

    Returns:

      bool

  composite(viewport: tuple[int, int, int, int] | None = None, force: bool = False, color: float | tuple[float, ...] | ndarray = 1.0, alpha: float | ndarray = 0.0, layer_filter: Callable | None = None, apply_icc: bool = True) → Image | None

    Composite layer and masks (mask, vector mask, and clipping layers).

    Parameters:

      - viewport – Viewport bounding box specified by (x1, y1, x2, y2) tuple. Default is the layer’s bbox.

      - force – Boolean flag to force vector drawing.

      - color – Backdrop color specified by scalar or tuple of scalar. The color value should be in [0.0, 1.0]. For example, (1., 0., 0.) specifies red in RGB color mode.

      - alpha – Backdrop alpha in [0.0, 1.0].

      - layer_filter – Callable that takes a layer as argument and returns whether if the layer is composited. Default is `is_visible()`.

    Returns:

      `PIL.Image.Image`.

  static extract_bbox(layers: Sequence[Layer] | GroupMixin, include_invisible: bool = False, include_clipping: bool = False) → tuple[int, int, int, int]

    Returns a bounding box for `layers` or (0, 0, 0, 0) if the layers have no bounding box.

    Parameters:

      - layers – sequence of layers or a group.

      - include_invisible – include invisible layers in calculation.

      - include_clipping – include clipping layers in calculation. Defaults to False to match visible pixel bounds.

    Returns:

      tuple of four int

  classmethod group_layers(parent: GroupMixin, layers: Sequence[Layer], name: str = 'Group', open_folder: bool = True) → Self

    Deprecated: Use `psdimage.create_group(layer_list, name)` instead.

    Parameters:

      - parent – The parent group to add the newly created Group object into.

      - layers – The layers to group. Can by any subclass of `Layer`

      - name – The display name of the group. Default to “Group”.

      - open_folder – Boolean defining whether the folder will be open or closed in photoshop. Default to True.

    Returns:

      A `Group`

    Raises:

      ValueError – If layers is empty

  is_group() → bool

    Return True if the layer is a group.

    Returns:

      bool

  property left: int

    Left coordinate (computed from children, read-only).

  classmethod new(parent: GroupMixin, name: str = 'Group', open_folder: bool = True) → Self

    Create a new Group object with minimal records and data channels and metadata to properly include the group in the PSD file.

    Parameters:

      - name – The display name of the group. Default to “Group”.

      - open_folder – Boolean defining whether the folder will be open or closed in photoshop. Default to True.

      - parent – Optional parent folder to move the newly created group into.

    Returns:

      A `Group` object

    Raises:

      ValueError – If parent is None

  property open_folder: bool

    Returns True if the group is an open folder.

    Returns:

      bool

  property right: int

    Right coordinate (computed from children, read-only).

  property top: int

    Top coordinate (computed from children, read-only).

## PixelLayer

class psd_tools.api.layers.PixelLayer(parent: GroupMixin, record: LayerRecord, channels: ChannelDataList)

  Layer that has rasterized image in pixels.

  Example:

      assert layer.kind == 'pixel':
      image = layer.composite()
      image.save('layer.png')

  classmethod frompil(image: Image, parent: GroupMixin, name: str = 'Layer', top: int = 0, left: int = 0, compression: Compression = Compression.RLE, **kwargs: Any) → PixelLayer

    Create a PixelLayer from a PIL image for a given psd file.

    Parameters:

      - image – The `Image` object to convert to photoshop

      - parent – The parent group or PSDImage this layer belongs to.

      - name – The name of the layer. Defaults to “Layer”

      - top – Pixelwise offset from the top of the canvas for the new layer.

      - left – Pixelwise offset from the left of the canvas for the new layer.

      - compression – Compression algorithm to use for the data.

    Returns:

      A `PixelLayer` object

    Raises:

      - TypeError – If image is not a PIL Image

      - ValueError – If parent is None

    Note

    If the image has an alpha channel and the parent PSD mode supports layer transparency (e.g. RGBA, LA), the alpha is stored as the layer transparency channel and no extra mask is created. For PSD modes that do not carry a transparency band (e.g. RGB, CMYK), the alpha channel is instead stored as a pixel mask (`USER_LAYER_MASK`).

## ShapeLayer

class psd_tools.api.layers.ShapeLayer(*args: Any)

  Layer that has drawing in vector mask.

  property bbox: tuple[int, int, int, int]

    (left, top, right, bottom) tuple.

  property bottom: int

    Bottom coordinate.

    Returns:

      int

  property left: int

    Left coordinate. Writable.

    Returns:

      int

  property right: int

    Right coordinate.

    Returns:

      int

  property top: int

    Top coordinate. Writable.

    Returns:

      int

## SmartObjectLayer

class psd_tools.api.layers.SmartObjectLayer(parent: GroupMixin, record: LayerRecord, channels: ChannelDataList)

  Layer that inserts external data.

  Use `smart_object` attribute to get the external data. See `SmartObject`.

  Example:

      import io
      so = layer.smart_object
      if so.detected_filetype == 'jpg':
          # Use open(external_dir=...) when processing untrusted PSD files.
          with so.open() as f:
              image = Image.open(io.BytesIO(f.read()))

  property smart_object: SmartObject

    Associated smart object.

    Returns:

      `SmartObject`.

## TypeLayer

class psd_tools.api.layers.TypeLayer(*args: Any)

  Layer that has text and styling information for fonts or paragraphs.

  Text is accessible at `text` property. Styling information for paragraphs is in `engine_dict`. Document styling information such as font list is is `resource_dict`.

  Currently, textual information is read-only.

  Example:

      if layer.kind == 'type':
          print(layer.text)
          print(layer.engine_dict['StyleRun'])

          # Extract font for each substring in the text.
          text = layer.engine_dict['Editor']['Text'].value
          fontset = layer.resource_dict['FontSet']
          runlength = layer.engine_dict['StyleRun']['RunLengthArray']
          rundata = layer.engine_dict['StyleRun']['RunArray']
          index = 0
          for length, style in zip(runlength, rundata):
              substring = text[index:index + length]
              stylesheet = style['StyleSheet']['StyleSheetData']
              font = fontset[stylesheet['Font']]
              print('%r gets %s' % (substring, font))
              index += length

  property document_resources: Dict

    Resource set relevant to the document.

  property engine_dict: Dict

    Styling information dict.

  property font_names: list[str]

    List of PostScript font names used in this text layer.

    Convenience shortcut for:

        [font.postscript_name for font in layer.typesetting.fonts]

  property resource_dict: Dict

    Resource set.

  property text: str

    Text in the layer. Read-only.

    Note

    New-line character in Photoshop is ‘\r’.

  property text_type: TextType | None

    Text type. Read-only.

    Returns:

      - `psd_tools.constants.TextType.POINT` for point type text

          (also known as character type)

      - `psd_tools.constants.TextType.PARAGRAPH` for paragraph type text

          (also known as area type)

      - None if text type cannot be determined or information is unavailable

    See `psd_tools.constants.TextType`.

  property transform: tuple[float, float, float, float, float, float]

    Matrix (xx, xy, yx, yy, tx, ty) applies affine transformation.

  property typesetting: TypeSetting

    Structured typographic data.

    Returns a `TypeSetting` object that provides Pythonic access to fonts, paragraphs, styled runs, and default styles without navigating raw engine data dicts.

    Example:

        ts = layer.typesetting
        for paragraph in ts:
            print(paragraph.style.justification)
            for run in paragraph.runs:
                print(run.text, run.style.font_name, run.style.font_size)

    See also: `engine_dict`, `resource_dict` for raw data.

  property warp: DescriptorBlock | None

    Warp configuration.