Skip to content

Commit

Permalink
feature: Implement additional ImageResources (#55)
Browse files Browse the repository at this point in the history
* [feature] Implement missing ImageResources

* Add tests for extra properties

* Simplify readFixedPoint32Bit computation

* Make DimensionUnit, ResolutionUnit part of API
  • Loading branch information
scoiatael authored Oct 19, 2022
1 parent 0f99674 commit 1d1e702
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 2 deletions.
13 changes: 13 additions & 0 deletions packages/psd/src/classes/Psd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
Guide,
ImageData,
ParsingResult,
ResolutionInfo,
ResourceType,
} from "../interfaces";
import {parse} from "../methods";
Expand All @@ -33,6 +34,9 @@ export class Psd extends Synthesizable implements NodeBase<never, NodeChild> {
public readonly guides: Guide[] = [];
public readonly slices: Slice[] = [];
public readonly icc_profile?: Uint8Array = undefined;
public readonly globalLightAngle?: number = undefined;
public readonly globalLightAltitude?: number = undefined;
public readonly resolutionInfo?: ResolutionInfo = undefined;

static parse(buffer: ArrayBuffer): Psd {
const parsingResult = parse(buffer);
Expand All @@ -59,6 +63,15 @@ export class Psd extends Synthesizable implements NodeBase<never, NodeChild> {
// see https://github.com/webtoon/psd/issues/46#issuecomment-1210726858
this.icc_profile = resource.resource;
break;
case ResourceType.GlobalLightAltitude:
this.globalLightAltitude = resource.resource;
break;
case ResourceType.GlobalLightAngle:
this.globalLightAngle = resource.resource;
break;
case ResourceType.ResolutionInfo:
this.resolutionInfo = resource.resource;
break;
}
}
}
Expand Down
9 changes: 8 additions & 1 deletion packages/psd/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@
import {Psd} from "./classes";

export type {Group, Layer, Node, NodeChild, NodeParent, Slice} from "./classes";
export {ColorMode, Depth, GuideDirection, SliceOrigin} from "./interfaces";
export {
ColorMode,
Depth,
GuideDirection,
SliceOrigin,
DimensionUnit,
ResolutionUnit,
} from "./interfaces";
export type {Guide} from "./interfaces";

export default Psd;
11 changes: 11 additions & 0 deletions packages/psd/src/interfaces/resources/GlobalLight.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// @webtoon/psd
// Copyright 2021-present NAVER WEBTOON
// MIT License

import {ImageResourceBlockBase} from "./ImageResourceBlockBase";
import {ResourceType} from "./ResourceType";

export type GlobalLightResourceBlock = ImageResourceBlockBase<
ResourceType.GlobalLightAltitude | ResourceType.GlobalLightAngle,
number
>;
32 changes: 32 additions & 0 deletions packages/psd/src/interfaces/resources/ResolutionInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// @webtoon/psd
// Copyright 2021-present NAVER WEBTOON
// MIT License

import {ImageResourceBlockBase} from "./ImageResourceBlockBase";
import {ResourceType} from "./ResourceType";

export enum ResolutionUnit {
PixelsPerInch = 1,
PixelsPerCM = 2,
}

export enum DimensionUnit {
Inch = 1,
CM = 2,
Point = 3, // 72 points == 1 inch
Pica = 4, // 6 pica == 1 inch
Column = 5,
}

export interface ResolutionInfo {
horizontal: number;
horizontalUnit: ResolutionUnit;
widthUnit: DimensionUnit;
vertical: number;
verticalUnit: ResolutionUnit;
heightUnit: DimensionUnit;
}
export type ResolutionInfoResourceBlock = ImageResourceBlockBase<
ResourceType.ResolutionInfo,
ResolutionInfo
>;
3 changes: 3 additions & 0 deletions packages/psd/src/interfaces/resources/ResourceType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@ export enum ResourceType {
GridAndGuides = 1032,
Slices = 1050,
ICCProfile = 1039,
GlobalLightAngle = 1037,
GlobalLightAltitude = 1049,
ResolutionInfo = 1005,
}
6 changes: 6 additions & 0 deletions packages/psd/src/interfaces/resources/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,20 @@ export * from "./ResourceType";
export * from "./SlicesResourceBlock";
export * from "./UnknownResourceBlock";
export * from "./ICCProfileResourceBlock";
export * from "./GlobalLight";
export * from "./ResolutionInfo";

import {GridAndGuidesResourceBlock} from "./GridAndGuidesResourceBlock";
import {SlicesResourceBlock} from "./SlicesResourceBlock";
import {UnknownResourceBlock} from "./UnknownResourceBlock";
import {ICCProfileResourceBlock} from "./ICCProfileResourceBlock";
import {GlobalLightResourceBlock} from "./GlobalLight";
import {ResolutionInfoResourceBlock} from "./ResolutionInfo";

export type ImageResourceBlock =
| GridAndGuidesResourceBlock
| SlicesResourceBlock
| ICCProfileResourceBlock
| GlobalLightResourceBlock
| ResolutionInfoResourceBlock
| UnknownResourceBlock;
10 changes: 10 additions & 0 deletions packages/psd/src/sections/ImageResource/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {ImageResourceBlock, ResourceType} from "../../interfaces";
import {Cursor, equals, InvalidResourceSignature} from "../../utils";
import {readGridAndGuides} from "./readGridAndGuides";
import {readICCProfile} from "./readICCProfile";
import {readResolutionInfo} from "./readResolutionInfo";
import {readSlices} from "./readSlices";

const EXPECTED_RESOURCE_BLOCK_SIGNATURE = [56, 66, 73, 77];
Expand Down Expand Up @@ -56,6 +57,15 @@ function readResourceBlock(cursor: Cursor): ImageResourceBlock {
case ResourceType.ICCProfile:
resource = readICCProfile(cursor, expectedDataEnd);
break;
case ResourceType.ResolutionInfo:
resource = readResolutionInfo(cursor);
break;
case ResourceType.GlobalLightAltitude:
resource = cursor.read("i32");
break;
case ResourceType.GlobalLightAngle:
resource = cursor.read("i32");
break;
default:
// For now, ignore resource types that we don't support;
// there are too many, and we can't support all of them now.
Expand Down
27 changes: 27 additions & 0 deletions packages/psd/src/sections/ImageResource/readResolutionInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// @webtoon/psd
// Copyright 2021-present NAVER WEBTOON
// MIT License

import {ResolutionInfoResourceBlock} from "../../interfaces";
import {Cursor} from "../../utils";

export function readResolutionInfo(
cursor: Cursor
): ResolutionInfoResourceBlock["resource"] {
const horizontal = cursor.readFixedPoint32bit();
const horizontalUnit = cursor.read("u16");
const widthUnit = cursor.read("u16");

const vertical = cursor.readFixedPoint32bit();
const verticalUnit = cursor.read("u16");
const heightUnit = cursor.read("u16");

return {
horizontal,
horizontalUnit,
widthUnit,
vertical,
verticalUnit,
heightUnit,
};
}
10 changes: 10 additions & 0 deletions packages/psd/src/utils/bytes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,16 @@ export class Cursor {
return result;
}

/**
* Reads unsigned 4-byte fixed-point number.
* 32 bits in 16.16 setup
* https://github.com/meltingice/psd.js/blob/333dd1467452a3353018c2856e3e4fb0e07d0025/lib/psd/resources/resolution_info.coffee#L10
*/
readFixedPoint32bit(): number {
const int = this.read("u32");
return int / (1 << 16);
}

/**
* Decodes an "ID string", which is a compact string format used in
* Descriptors.
Expand Down
18 changes: 17 additions & 1 deletion packages/psd/tests/integration/example.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ import * as path from "path";
import {beforeAll, describe, expect, it} from "vitest";

import type Psd from "../../src/index";
import PSD, {ColorMode, GuideDirection, SliceOrigin} from "../../src/index";
import PSD, {
ColorMode,
GuideDirection,
SliceOrigin,
DimensionUnit,
ResolutionUnit,
} from "../../src/index";

const FIXTURE_DIR = path.join(__dirname, "fixtures/example");

Expand Down Expand Up @@ -39,6 +45,16 @@ describe.each([
expect(psd.channelCount).toBe(3); // RGB image has three channels
expect(psd.colorMode).toBe(ColorMode.Rgb);
expect(psd.opacity).toBe(255);
expect(psd.globalLightAngle).toBe(90);
expect(psd.globalLightAltitude).toBe(30);
expect(psd.resolutionInfo).toStrictEqual({
heightUnit: DimensionUnit.CM,
horizontal: 300,
horizontalUnit: ResolutionUnit.PixelsPerInch,
vertical: 300,
verticalUnit: ResolutionUnit.PixelsPerInch,
widthUnit: DimensionUnit.CM,
});
});

it("should parse all layers and layer groups", () => {
Expand Down

0 comments on commit 1d1e702

Please sign in to comment.