Skip to content

Commit

Permalink
feat!: parse layer hidden, transparency lock flags
Browse files Browse the repository at this point in the history
- Add `layer.isHidden` and `layer.isTransparencyLocked` flags
  (`isHidden` was previously `visible`, and was not exposed via
  TypeScript)
- Improve logic for parsing layer flags (use bitwise OR, not toString)
- Fix bug where the `isHidden` flag (previously `visible`) was being
  parsed incorrectly
- Add test cases for the new flags (and update fixture)
  • Loading branch information
Yehyoung Kang authored and pastelmind committed Jun 20, 2022
1 parent 86af282 commit 1caf69b
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 39 deletions.
8 changes: 8 additions & 0 deletions packages/psd/src/classes/Layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ export class Layer
return this.parent.composedOpacity * (this.opacity / 255);
}

get isHidden(): boolean {
return this.layerFrame.layerProperties.hidden;
}

get isTransparencyLocked(): boolean {
return this.layerFrame.layerProperties.transparencyLocked;
}

/**
* If this layer is a text layer, this property retrieves its text content.
* Otherwise, this property is `undefined`.
Expand Down
14 changes: 9 additions & 5 deletions packages/psd/src/sections/LayerAndMaskInformation/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
import {
AdditionalLayerInfo,
BlendMode,
ChannelBytes,
ChannelKind,
Clipping,
GroupDivider,
ChannelBytes,
} from "../../interfaces";

export interface LayerRecord {
Expand All @@ -18,7 +18,8 @@ export interface LayerRecord {
left: number;
bottom: number;
right: number;
visible: boolean;
hidden: boolean;
transparencyLocked: boolean;
opacity: number;
clipping: Clipping;
blendMode: BlendMode;
Expand Down Expand Up @@ -46,7 +47,8 @@ export interface LayerProperties {
left: number;
bottom: number;
right: number;
visible: boolean;
hidden: boolean;
transparencyLocked: boolean;
opacity: number;
clippingMask: Clipping;
blendMode: BlendMode;
Expand All @@ -67,7 +69,8 @@ export const createLayerProperties = (
right,
opacity,
clipping: clippingMask,
visible,
hidden,
transparencyLocked,
blendMode,
layerText,
} = layerRecord;
Expand All @@ -80,7 +83,8 @@ export const createLayerProperties = (
right,
opacity,
clippingMask,
visible,
hidden,
transparencyLocked,
blendMode,
groupId,
text: layerText,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@

import {
AdditionalLayerInfo,
AliKey,
BlendMode,
ChannelKind,
ChannelBytes,
Clipping,
ChannelCompression,
ChannelKind,
Clipping,
DescriptorValueType,
FileVersionSpec,
matchBlendMode,
matchClipping,
matchChannelCompression,
AliKey,
DescriptorValueType,
matchClipping,
} from "../../interfaces";
import {Cursor, InvalidBlendingModeSignature} from "../../utils";
import {LayerRecord, LayerChannels} from "./interfaces";
import {readAdditionalLayerInfo} from "./AdditionalLayerInfo";
import {LayerChannels, LayerRecord} from "./interfaces";

const EXPECTED_BLENDING_MODE_SIGNATURE = "8BIM";

Expand Down Expand Up @@ -99,7 +99,7 @@ function readLayerRecord(
const blendMode: BlendMode = matchBlendMode(cursor.readString(4));
const opacity = cursor.read("u8");
const clipping: Clipping = matchClipping(cursor.read("u8"));
const visible = readLayerFlag(cursor);
const {hidden, transparencyLocked} = readLayerFlags(cursor);

// Skip parsing filter information
cursor.pass(1);
Expand Down Expand Up @@ -166,7 +166,8 @@ function readLayerRecord(
left,
bottom,
right,
visible,
hidden,
transparencyLocked,
opacity,
clipping,
blendMode,
Expand Down Expand Up @@ -199,13 +200,16 @@ function readLayerRectangle(cursor: Cursor): [number, number, number, number] {
return [top, left, bottom, right];
}

function readLayerFlag(cursor: Cursor): boolean {
// There are better ways of parsing a bitfield...
// TODO: Rewrite this
const flags = cursor.read("u8").toString(2).padStart(8, "0");
const visible = flags[7];

return visible === "0";
function readLayerFlags(cursor: Cursor): {
hidden: boolean;
transparencyLocked: boolean;
} {
const flags = cursor.read("u8");
return {
transparencyLocked: Boolean(flags & 0x1),
// Adobe's docs say this means "visible", but this actually marks "hidden" layers
hidden: Boolean(flags & 0x2),
};
}

function calcLayerHeight(layerRecord: LayerRecord): number {
Expand Down
32 changes: 32 additions & 0 deletions packages/psd/tests/integration/example.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,4 +265,36 @@ describe.each([
expect(psd.layers[12].text).toBeUndefined();
expect(psd.layers[13].text).toBeUndefined();
});

it("should correctly parse layer 'hidden' status", () => {
expect(psd.layers[0].isHidden).toBe(false);
expect(psd.layers[1].isHidden).toBe(false);
expect(psd.layers[2].isHidden).toBe(false);
expect(psd.layers[3].isHidden).toBe(false);
expect(psd.layers[4].isHidden).toBe(false);
expect(psd.layers[5].isHidden).toBe(false);
expect(psd.layers[6].isHidden).toBe(false);
expect(psd.layers[7].isHidden).toBe(false);
expect(psd.layers[8].isHidden).toBe(false);
expect(psd.layers[9].isHidden).toBe(true);
expect(psd.layers[10].isHidden).toBe(false);
expect(psd.layers[11].isHidden).toBe(false);
expect(psd.layers[12].isHidden).toBe(false);
});

it("should correctly parse layer transparency lock status", () => {
expect(psd.layers[0].isTransparencyLocked).toBe(false);
expect(psd.layers[1].isTransparencyLocked).toBe(false);
expect(psd.layers[2].isTransparencyLocked).toBe(false);
expect(psd.layers[3].isTransparencyLocked).toBe(false);
expect(psd.layers[4].isTransparencyLocked).toBe(false);
expect(psd.layers[5].isTransparencyLocked).toBe(false);
expect(psd.layers[6].isTransparencyLocked).toBe(false);
expect(psd.layers[7].isTransparencyLocked).toBe(true);
expect(psd.layers[8].isTransparencyLocked).toBe(false);
expect(psd.layers[9].isTransparencyLocked).toBe(false);
expect(psd.layers[10].isTransparencyLocked).toBe(false);
expect(psd.layers[11].isTransparencyLocked).toBe(false);
expect(psd.layers[12].isTransparencyLocked).toBe(false);
});
});
Binary file modified packages/psd/tests/integration/fixtures/example/example.psb
Binary file not shown.
Binary file modified packages/psd/tests/integration/fixtures/example/example.psd
Binary file not shown.
20 changes: 1 addition & 19 deletions packages/psd/tests/integration/fixtures/example/imageData

Large diffs are not rendered by default.

0 comments on commit 1caf69b

Please sign in to comment.