@yft-design/psd-lib 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -0
- package/dist/abr.d.ts +143 -0
- package/dist/abr.js +300 -0
- package/dist/additionalInfo.d.ts +26 -0
- package/dist/additionalInfo.js +3891 -0
- package/dist/csh.d.ts +10 -0
- package/dist/csh.js +32 -0
- package/dist/descriptor.d.ts +566 -0
- package/dist/descriptor.js +1958 -0
- package/dist/effectsHelpers.d.ts +5 -0
- package/dist/effectsHelpers.js +280 -0
- package/dist/engineData.d.ts +2 -0
- package/dist/engineData.js +330 -0
- package/dist/engineData2.d.ts +2 -0
- package/dist/engineData2.js +405 -0
- package/dist/helpers.d.ts +89 -0
- package/dist/helpers.js +300 -0
- package/dist/imageResources.d.ts +17 -0
- package/dist/imageResources.js +1070 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +30 -0
- package/dist/initializeCanvas.d.ts +1 -0
- package/dist/initializeCanvas.js +21 -0
- package/dist/jpeg.d.ts +1 -0
- package/dist/jpeg.js +1017 -0
- package/dist/psd.d.ts +1822 -0
- package/dist/psd.js +7 -0
- package/dist/psdReader.d.ts +46 -0
- package/dist/psdReader.js +1188 -0
- package/dist/psdWriter.d.ts +33 -0
- package/dist/psdWriter.js +733 -0
- package/dist/text.d.ts +179 -0
- package/dist/text.js +556 -0
- package/dist/utf8.d.ts +4 -0
- package/dist/utf8.js +150 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# @yft-design/psd-lib
|
|
2
|
+
|
|
3
|
+
A fast PSD (Photoshop) file parser for Node.js and browsers.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @yft-design/psd-lib
|
|
9
|
+
# or
|
|
10
|
+
pnpm install @yft-design/psd-lib
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- Parse and write PSD files
|
|
16
|
+
- Support for layers, effects, text, and more
|
|
17
|
+
- Read ABR (brush) and CSH (custom shape) files
|
|
18
|
+
- TypeScript support
|
|
19
|
+
- Works in Node.js and browsers
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { readPsd, writePsd } from '@yft-design/psd-lib'
|
|
25
|
+
|
|
26
|
+
// Read a PSD file
|
|
27
|
+
const psd = readPsd(buffer)
|
|
28
|
+
|
|
29
|
+
// Access layers
|
|
30
|
+
console.log(psd.children) // layer tree
|
|
31
|
+
|
|
32
|
+
// Write a PSD file
|
|
33
|
+
const output = writePsd(psd)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## API
|
|
37
|
+
|
|
38
|
+
- `readPsd(buffer, options?)` - Parse a PSD file from ArrayBuffer
|
|
39
|
+
- `writePsd(psd, options?)` - Write a PSD to ArrayBuffer
|
|
40
|
+
- `writePsdUint8Array(psd, options?)` - Write a PSD to Uint8Array
|
|
41
|
+
- `writePsdBuffer(psd, options?)` - Write a PSD to Node.js Buffer
|
|
42
|
+
|
|
43
|
+
## License
|
|
44
|
+
|
|
45
|
+
MIT
|
package/dist/abr.d.ts
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { BlendMode, PatternInfo } from './psd';
|
|
2
|
+
export interface Abr {
|
|
3
|
+
brushes: Brush[];
|
|
4
|
+
samples: SampleInfo[];
|
|
5
|
+
patterns: PatternInfo[];
|
|
6
|
+
}
|
|
7
|
+
export interface SampleInfo {
|
|
8
|
+
id: string;
|
|
9
|
+
bounds: {
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
w: number;
|
|
13
|
+
h: number;
|
|
14
|
+
};
|
|
15
|
+
alpha: Uint8Array;
|
|
16
|
+
}
|
|
17
|
+
export interface BrushDynamics {
|
|
18
|
+
control: 'off' | 'fade' | 'pen pressure' | 'pen tilt' | 'stylus wheel' | 'initial direction' | 'direction' | 'initial rotation' | 'rotation';
|
|
19
|
+
steps: number;
|
|
20
|
+
jitter: number;
|
|
21
|
+
minimum: number;
|
|
22
|
+
}
|
|
23
|
+
export interface BrushShape {
|
|
24
|
+
name?: string;
|
|
25
|
+
size: number;
|
|
26
|
+
angle: number;
|
|
27
|
+
roundness: number;
|
|
28
|
+
hardness?: number;
|
|
29
|
+
spacingOn: boolean;
|
|
30
|
+
spacing: number;
|
|
31
|
+
flipX: boolean;
|
|
32
|
+
flipY: boolean;
|
|
33
|
+
sampledData?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface Brush {
|
|
36
|
+
name: string;
|
|
37
|
+
shape: BrushShape;
|
|
38
|
+
shapeDynamics?: {
|
|
39
|
+
sizeDynamics: BrushDynamics;
|
|
40
|
+
minimumDiameter: number;
|
|
41
|
+
tiltScale: number;
|
|
42
|
+
angleDynamics: BrushDynamics;
|
|
43
|
+
roundnessDynamics: BrushDynamics;
|
|
44
|
+
minimumRoundness: number;
|
|
45
|
+
flipX: boolean;
|
|
46
|
+
flipY: boolean;
|
|
47
|
+
brushProjection: boolean;
|
|
48
|
+
};
|
|
49
|
+
scatter?: {
|
|
50
|
+
bothAxes: boolean;
|
|
51
|
+
scatterDynamics: BrushDynamics;
|
|
52
|
+
countDynamics: BrushDynamics;
|
|
53
|
+
count: number;
|
|
54
|
+
};
|
|
55
|
+
texture?: {
|
|
56
|
+
id: string;
|
|
57
|
+
name: string;
|
|
58
|
+
invert: boolean;
|
|
59
|
+
scale: number;
|
|
60
|
+
brightness: number;
|
|
61
|
+
contrast: number;
|
|
62
|
+
blendMode: BlendMode;
|
|
63
|
+
depth: number;
|
|
64
|
+
depthMinimum: number;
|
|
65
|
+
depthDynamics: BrushDynamics;
|
|
66
|
+
};
|
|
67
|
+
dualBrush?: {
|
|
68
|
+
flip: boolean;
|
|
69
|
+
shape: BrushShape;
|
|
70
|
+
blendMode: BlendMode;
|
|
71
|
+
useScatter: boolean;
|
|
72
|
+
spacing: number;
|
|
73
|
+
count: number;
|
|
74
|
+
bothAxes: boolean;
|
|
75
|
+
countDynamics: BrushDynamics;
|
|
76
|
+
scatterDynamics: BrushDynamics;
|
|
77
|
+
};
|
|
78
|
+
colorDynamics?: {
|
|
79
|
+
foregroundBackground: BrushDynamics;
|
|
80
|
+
hue: number;
|
|
81
|
+
saturation: number;
|
|
82
|
+
brightness: number;
|
|
83
|
+
purity: number;
|
|
84
|
+
perTip: boolean;
|
|
85
|
+
};
|
|
86
|
+
transfer?: {
|
|
87
|
+
flowDynamics: BrushDynamics;
|
|
88
|
+
opacityDynamics: BrushDynamics;
|
|
89
|
+
wetnessDynamics: BrushDynamics;
|
|
90
|
+
mixDynamics: BrushDynamics;
|
|
91
|
+
};
|
|
92
|
+
brushPose?: {
|
|
93
|
+
overrideAngle: boolean;
|
|
94
|
+
overrideTiltX: boolean;
|
|
95
|
+
overrideTiltY: boolean;
|
|
96
|
+
overridePressure: boolean;
|
|
97
|
+
pressure: number;
|
|
98
|
+
tiltX: number;
|
|
99
|
+
tiltY: number;
|
|
100
|
+
angle: number;
|
|
101
|
+
};
|
|
102
|
+
noise: boolean;
|
|
103
|
+
wetEdges: boolean;
|
|
104
|
+
protectTexture?: boolean;
|
|
105
|
+
spacing: number;
|
|
106
|
+
brushGroup?: undefined;
|
|
107
|
+
interpretation?: boolean;
|
|
108
|
+
useBrushSize: boolean;
|
|
109
|
+
toolOptions?: {
|
|
110
|
+
type: 'brush' | 'mixer brush' | 'smudge brush';
|
|
111
|
+
brushPreset: boolean;
|
|
112
|
+
flow: number;
|
|
113
|
+
wetness?: number;
|
|
114
|
+
dryness?: number;
|
|
115
|
+
mix?: number;
|
|
116
|
+
smooth: number;
|
|
117
|
+
mode: BlendMode;
|
|
118
|
+
opacity: number;
|
|
119
|
+
smoothing: boolean;
|
|
120
|
+
smoothingValue: number;
|
|
121
|
+
smoothingRadiusMode: boolean;
|
|
122
|
+
smoothingCatchup: boolean;
|
|
123
|
+
smoothingCatchupAtEnd: boolean;
|
|
124
|
+
smoothingZoomCompensation: boolean;
|
|
125
|
+
pressureSmoothing: boolean;
|
|
126
|
+
usePressureOverridesSize: boolean;
|
|
127
|
+
usePressureOverridesOpacity: boolean;
|
|
128
|
+
useLegacy: boolean;
|
|
129
|
+
autoFill?: boolean;
|
|
130
|
+
autoClean?: boolean;
|
|
131
|
+
loadSolidColorOnly?: boolean;
|
|
132
|
+
sampleAllLayers?: boolean;
|
|
133
|
+
flowDynamics?: BrushDynamics;
|
|
134
|
+
opacityDynamics?: BrushDynamics;
|
|
135
|
+
sizeDynamics?: BrushDynamics;
|
|
136
|
+
smudgeFingerPainting?: boolean;
|
|
137
|
+
smudgeSampleAllLayers?: boolean;
|
|
138
|
+
strength?: number;
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
export declare function readAbr(buffer: ArrayBufferView, options?: {
|
|
142
|
+
logMissingFeatures?: boolean;
|
|
143
|
+
}): Abr;
|
package/dist/abr.js
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { BlnM, parseAngle, parsePercent, parseUnitsToNumber, readVersionAndDescriptor } from './descriptor';
|
|
2
|
+
import { checkSignature, createReader, readBytes, readDataRLE, readInt16, readInt32, readPascalString, readPattern, readSignature, readUint16, readUint32, readUint8, skipBytes } from './psdReader';
|
|
3
|
+
const dynamicsControl = ['off', 'fade', 'pen pressure', 'pen tilt', 'stylus wheel', 'initial direction', 'direction', 'initial rotation', 'rotation'];
|
|
4
|
+
const toBrushType = {
|
|
5
|
+
_: 'brush',
|
|
6
|
+
MixB: 'mixer brush',
|
|
7
|
+
SmTl: 'smudge brush',
|
|
8
|
+
// PbTl
|
|
9
|
+
// ErTl
|
|
10
|
+
};
|
|
11
|
+
function parseDynamics(desc) {
|
|
12
|
+
return {
|
|
13
|
+
control: dynamicsControl[desc.bVTy],
|
|
14
|
+
steps: desc.fStp,
|
|
15
|
+
jitter: parsePercent(desc.jitter),
|
|
16
|
+
minimum: parsePercent(desc['Mnm ']),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function parseBrushShape(desc) {
|
|
20
|
+
const shape = {
|
|
21
|
+
size: parseUnitsToNumber(desc.Dmtr, 'Pixels'),
|
|
22
|
+
angle: parseAngle(desc.Angl),
|
|
23
|
+
roundness: parsePercent(desc.Rndn),
|
|
24
|
+
spacingOn: desc.Intr,
|
|
25
|
+
spacing: parsePercent(desc.Spcn),
|
|
26
|
+
flipX: desc.flipX,
|
|
27
|
+
flipY: desc.flipY,
|
|
28
|
+
};
|
|
29
|
+
if (desc['Nm '])
|
|
30
|
+
shape.name = desc['Nm '];
|
|
31
|
+
if (desc.Hrdn)
|
|
32
|
+
shape.hardness = parsePercent(desc.Hrdn);
|
|
33
|
+
if (desc.sampledData)
|
|
34
|
+
shape.sampledData = desc.sampledData;
|
|
35
|
+
return shape;
|
|
36
|
+
}
|
|
37
|
+
export function readAbr(buffer, options = {}) {
|
|
38
|
+
const reader = createReader(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
39
|
+
const version = readInt16(reader);
|
|
40
|
+
const samples = [];
|
|
41
|
+
const brushes = [];
|
|
42
|
+
const patterns = [];
|
|
43
|
+
if (version === 1 || version === 2) {
|
|
44
|
+
throw new Error(`Unsupported ABR version (${version})`); // TODO: ...
|
|
45
|
+
}
|
|
46
|
+
else if (version === 6 || version === 7 || version === 9 || version === 10) {
|
|
47
|
+
const minorVersion = readInt16(reader);
|
|
48
|
+
if (minorVersion !== 1 && minorVersion !== 2)
|
|
49
|
+
throw new Error('Unsupported ABR minor version');
|
|
50
|
+
while (reader.offset < reader.view.byteLength) {
|
|
51
|
+
checkSignature(reader, '8BIM');
|
|
52
|
+
const type = readSignature(reader);
|
|
53
|
+
let size = readUint32(reader);
|
|
54
|
+
const end = reader.offset + size;
|
|
55
|
+
switch (type) {
|
|
56
|
+
case 'samp': {
|
|
57
|
+
while (reader.offset < end) {
|
|
58
|
+
let brushLength = readUint32(reader);
|
|
59
|
+
while (brushLength & 0b11)
|
|
60
|
+
brushLength++; // pad to 4 byte alignment
|
|
61
|
+
const brushEnd = reader.offset + brushLength;
|
|
62
|
+
const id = readPascalString(reader, 1);
|
|
63
|
+
// v1 - Skip the Int16 bounds rectangle and the unknown Int16.
|
|
64
|
+
// v2 - Skip the unknown bytes.
|
|
65
|
+
skipBytes(reader, minorVersion === 1 ? 10 : 264);
|
|
66
|
+
const y = readInt32(reader);
|
|
67
|
+
const x = readInt32(reader);
|
|
68
|
+
const h = readInt32(reader) - y;
|
|
69
|
+
const w = readInt32(reader) - x;
|
|
70
|
+
if (w <= 0 || h <= 0)
|
|
71
|
+
throw new Error('Invalid bounds');
|
|
72
|
+
const bithDepth = readInt16(reader);
|
|
73
|
+
const compression = readUint8(reader); // 0 - raw, 1 - RLE
|
|
74
|
+
const alpha = new Uint8Array(w * h);
|
|
75
|
+
if (bithDepth === 8) {
|
|
76
|
+
if (compression === 0) {
|
|
77
|
+
alpha.set(readBytes(reader, alpha.byteLength));
|
|
78
|
+
}
|
|
79
|
+
else if (compression === 1) {
|
|
80
|
+
readDataRLE(reader, { width: w, height: h, data: alpha }, w, h, bithDepth, 1, [0], false);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
throw new Error('Invalid compression');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else if (bithDepth === 16) {
|
|
87
|
+
if (compression === 0) {
|
|
88
|
+
for (let i = 0; i < alpha.byteLength; i++) {
|
|
89
|
+
alpha[i] = readUint16(reader) >> 8; // convert to 8bit values
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else if (compression === 1) {
|
|
93
|
+
throw new Error('not implemented (16bit RLE)'); // TODO: ...
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
throw new Error('Invalid compression');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
throw new Error('Invalid depth');
|
|
101
|
+
}
|
|
102
|
+
samples.push({ id, bounds: { x, y, w, h }, alpha });
|
|
103
|
+
reader.offset = brushEnd;
|
|
104
|
+
}
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
case 'desc': {
|
|
108
|
+
const desc = readVersionAndDescriptor(reader, true);
|
|
109
|
+
// console.log(require('util').inspect(desc, false, 99, true));
|
|
110
|
+
for (const brush of desc.Brsh) {
|
|
111
|
+
const b = {
|
|
112
|
+
name: brush['Nm '],
|
|
113
|
+
shape: parseBrushShape(brush.Brsh),
|
|
114
|
+
spacing: parsePercent(brush.Spcn),
|
|
115
|
+
// TODO: brushGroup ???
|
|
116
|
+
wetEdges: brush.Wtdg,
|
|
117
|
+
noise: brush.Nose,
|
|
118
|
+
// TODO: TxtC ??? smoothing / build-up ?
|
|
119
|
+
// TODO: 'Rpt ' ???
|
|
120
|
+
useBrushSize: brush.useBrushSize, // ???
|
|
121
|
+
};
|
|
122
|
+
if (brush.interpretation != null)
|
|
123
|
+
b.interpretation = brush.interpretation;
|
|
124
|
+
if (brush.protectTexture != null)
|
|
125
|
+
b.protectTexture = brush.protectTexture;
|
|
126
|
+
if (brush.useTipDynamics) {
|
|
127
|
+
b.shapeDynamics = {
|
|
128
|
+
tiltScale: parsePercent(brush.tiltScale),
|
|
129
|
+
sizeDynamics: parseDynamics(brush.szVr),
|
|
130
|
+
angleDynamics: parseDynamics(brush.angleDynamics),
|
|
131
|
+
roundnessDynamics: parseDynamics(brush.roundnessDynamics),
|
|
132
|
+
flipX: brush.flipX,
|
|
133
|
+
flipY: brush.flipY,
|
|
134
|
+
brushProjection: brush.brushProjection,
|
|
135
|
+
minimumDiameter: parsePercent(brush.minimumDiameter),
|
|
136
|
+
minimumRoundness: parsePercent(brush.minimumRoundness),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
if (brush.useScatter) {
|
|
140
|
+
b.scatter = {
|
|
141
|
+
count: brush['Cnt '],
|
|
142
|
+
bothAxes: brush.bothAxes,
|
|
143
|
+
countDynamics: parseDynamics(brush.countDynamics),
|
|
144
|
+
scatterDynamics: parseDynamics(brush.scatterDynamics),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
if (brush.useTexture && brush.Txtr) {
|
|
148
|
+
b.texture = {
|
|
149
|
+
id: brush.Txtr.Idnt,
|
|
150
|
+
name: brush.Txtr['Nm '],
|
|
151
|
+
blendMode: BlnM.decode(brush.textureBlendMode),
|
|
152
|
+
depth: parsePercent(brush.textureDepth),
|
|
153
|
+
depthMinimum: parsePercent(brush.minimumDepth),
|
|
154
|
+
depthDynamics: parseDynamics(brush.textureDepthDynamics),
|
|
155
|
+
scale: parsePercent(brush.textureScale),
|
|
156
|
+
invert: brush.InvT,
|
|
157
|
+
brightness: brush.textureBrightness,
|
|
158
|
+
contrast: brush.textureContrast,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
const db = brush.dualBrush;
|
|
162
|
+
if (db && db.useDualBrush) {
|
|
163
|
+
b.dualBrush = {
|
|
164
|
+
flip: db.Flip,
|
|
165
|
+
shape: parseBrushShape(db.Brsh),
|
|
166
|
+
blendMode: BlnM.decode(db.BlnM),
|
|
167
|
+
useScatter: db.useScatter,
|
|
168
|
+
spacing: parsePercent(db.Spcn),
|
|
169
|
+
count: db['Cnt '],
|
|
170
|
+
bothAxes: db.bothAxes,
|
|
171
|
+
countDynamics: parseDynamics(db.countDynamics),
|
|
172
|
+
scatterDynamics: parseDynamics(db.scatterDynamics),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
if (brush.useColorDynamics) {
|
|
176
|
+
b.colorDynamics = {
|
|
177
|
+
foregroundBackground: parseDynamics(brush.clVr),
|
|
178
|
+
hue: parsePercent(brush['H ']),
|
|
179
|
+
saturation: parsePercent(brush.Strt),
|
|
180
|
+
brightness: parsePercent(brush.Brgh),
|
|
181
|
+
purity: parsePercent(brush.purity),
|
|
182
|
+
perTip: brush.colorDynamicsPerTip,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
if (brush.usePaintDynamics) {
|
|
186
|
+
b.transfer = {
|
|
187
|
+
flowDynamics: parseDynamics(brush.prVr),
|
|
188
|
+
opacityDynamics: parseDynamics(brush.opVr),
|
|
189
|
+
wetnessDynamics: parseDynamics(brush.wtVr),
|
|
190
|
+
mixDynamics: parseDynamics(brush.mxVr),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
if (brush.useBrushPose) {
|
|
194
|
+
b.brushPose = {
|
|
195
|
+
overrideAngle: brush.overridePoseAngle,
|
|
196
|
+
overrideTiltX: brush.overridePoseTiltX,
|
|
197
|
+
overrideTiltY: brush.overridePoseTiltY,
|
|
198
|
+
overridePressure: brush.overridePosePressure,
|
|
199
|
+
pressure: parsePercent(brush.brushPosePressure),
|
|
200
|
+
tiltX: brush.brushPoseTiltX,
|
|
201
|
+
tiltY: brush.brushPoseTiltY,
|
|
202
|
+
angle: brush.brushPoseAngle,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
const to = brush.toolOptions;
|
|
206
|
+
if (to) {
|
|
207
|
+
b.toolOptions = {
|
|
208
|
+
type: toBrushType[to._classID] || 'brush',
|
|
209
|
+
brushPreset: to.brushPreset,
|
|
210
|
+
flow: to.flow ?? 100,
|
|
211
|
+
smooth: to.Smoo ?? 0,
|
|
212
|
+
mode: BlnM.decode(to['Md '] || 'BlnM.Nrml'), // sometimes mode is missing
|
|
213
|
+
opacity: to.Opct ?? 100,
|
|
214
|
+
smoothing: !!to.smoothing,
|
|
215
|
+
smoothingValue: to.smoothingValue || 0,
|
|
216
|
+
smoothingRadiusMode: !!to.smoothingRadiusMode,
|
|
217
|
+
smoothingCatchup: !!to.smoothingCatchup,
|
|
218
|
+
smoothingCatchupAtEnd: !!to.smoothingCatchupAtEnd,
|
|
219
|
+
smoothingZoomCompensation: !!to.smoothingZoomCompensation,
|
|
220
|
+
pressureSmoothing: !!to.pressureSmoothing,
|
|
221
|
+
usePressureOverridesSize: !!to.usePressureOverridesSize,
|
|
222
|
+
usePressureOverridesOpacity: !!to.usePressureOverridesOpacity,
|
|
223
|
+
useLegacy: !!to.useLegacy,
|
|
224
|
+
};
|
|
225
|
+
if (to.prVr)
|
|
226
|
+
b.toolOptions.flowDynamics = parseDynamics(to.prVr);
|
|
227
|
+
if (to.opVr)
|
|
228
|
+
b.toolOptions.opacityDynamics = parseDynamics(to.opVr);
|
|
229
|
+
if (to.szVr)
|
|
230
|
+
b.toolOptions.sizeDynamics = parseDynamics(to.szVr);
|
|
231
|
+
if ('wetness' in to)
|
|
232
|
+
b.toolOptions.wetness = to.wetness;
|
|
233
|
+
if ('dryness' in to)
|
|
234
|
+
b.toolOptions.dryness = to.dryness;
|
|
235
|
+
if ('mix' in to)
|
|
236
|
+
b.toolOptions.mix = to.mix;
|
|
237
|
+
if ('autoFill' in to)
|
|
238
|
+
b.toolOptions.autoFill = to.autoFill;
|
|
239
|
+
if ('autoClean' in to)
|
|
240
|
+
b.toolOptions.autoClean = to.autoClean;
|
|
241
|
+
if ('loadSolidColorOnly' in to)
|
|
242
|
+
b.toolOptions.loadSolidColorOnly = to.loadSolidColorOnly;
|
|
243
|
+
if ('sampleAllLayers' in to)
|
|
244
|
+
b.toolOptions.sampleAllLayers = to.sampleAllLayers;
|
|
245
|
+
if ('SmdF' in to)
|
|
246
|
+
b.toolOptions.smudgeFingerPainting = to.SmdF;
|
|
247
|
+
if ('SmdS' in to)
|
|
248
|
+
b.toolOptions.smudgeSampleAllLayers = to.SmdS;
|
|
249
|
+
if ('Prs ' in to)
|
|
250
|
+
b.toolOptions.strength = to['Prs '];
|
|
251
|
+
if ('SmdF' in to)
|
|
252
|
+
b.toolOptions.smudgeFingerPainting = to.SmdF;
|
|
253
|
+
if ('SmdS' in to)
|
|
254
|
+
b.toolOptions.smudgeSampleAllLayers = to.SmdS;
|
|
255
|
+
}
|
|
256
|
+
brushes.push(b);
|
|
257
|
+
}
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
case 'patt': {
|
|
261
|
+
if (reader.offset < end) { // TODO: check multiple patterns
|
|
262
|
+
patterns.push(readPattern(reader));
|
|
263
|
+
reader.offset = end;
|
|
264
|
+
}
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
case 'phry': {
|
|
268
|
+
// TODO: what is this ?
|
|
269
|
+
const desc = readVersionAndDescriptor(reader);
|
|
270
|
+
// example:
|
|
271
|
+
// hierarchy: [
|
|
272
|
+
// {
|
|
273
|
+
// 'Nm ': 'PRE_EXPORT ',
|
|
274
|
+
// zuid: '965209f2-6f35-9a40-aa43-485684382172'
|
|
275
|
+
// },
|
|
276
|
+
// {},
|
|
277
|
+
// ...
|
|
278
|
+
// ]
|
|
279
|
+
if (options.logMissingFeatures) {
|
|
280
|
+
if (desc.hierarchy?.length) {
|
|
281
|
+
// console.log('unhandled phry section', desc);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
default:
|
|
287
|
+
throw new Error(`Invalid brush type: ${type}`);
|
|
288
|
+
}
|
|
289
|
+
// align to 4 bytes
|
|
290
|
+
while (size % 4) {
|
|
291
|
+
reader.offset++;
|
|
292
|
+
size++;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
throw new Error(`Unsupported ABR version (${version})`);
|
|
298
|
+
}
|
|
299
|
+
return { samples, patterns, brushes };
|
|
300
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { LayerAdditionalInfo, BezierPath, Psd, WriteOptions, BooleanOperation, LayerEffectsInfo, LayerVectorMask } from './psd';
|
|
2
|
+
import { PsdReader } from './psdReader';
|
|
3
|
+
import { PsdWriter } from './psdWriter';
|
|
4
|
+
import type { InternalImageResources } from './imageResources';
|
|
5
|
+
export interface ExtendedWriteOptions extends WriteOptions {
|
|
6
|
+
layerIds: Set<number>;
|
|
7
|
+
layerToId: Map<any, number>;
|
|
8
|
+
}
|
|
9
|
+
type HasMethod = (target: LayerAdditionalInfo) => boolean;
|
|
10
|
+
type ReadMethod = (reader: PsdReader, target: LayerAdditionalInfo, left: () => number, psd: Psd, imageResources: InternalImageResources) => void;
|
|
11
|
+
type WriteMethod = (writer: PsdWriter, target: LayerAdditionalInfo, psd: Psd, options: ExtendedWriteOptions) => void;
|
|
12
|
+
export interface InfoHandler {
|
|
13
|
+
key: string;
|
|
14
|
+
has: HasMethod;
|
|
15
|
+
read: ReadMethod;
|
|
16
|
+
write: WriteMethod;
|
|
17
|
+
}
|
|
18
|
+
export declare const infoHandlers: InfoHandler[];
|
|
19
|
+
export declare const infoHandlersMap: {
|
|
20
|
+
[key: string]: InfoHandler;
|
|
21
|
+
};
|
|
22
|
+
export declare function readBezierKnot(reader: PsdReader, width: number, height: number): number[];
|
|
23
|
+
export declare const booleanOperations: BooleanOperation[];
|
|
24
|
+
export declare function readVectorMask(reader: PsdReader, vectorMask: LayerVectorMask, width: number, height: number, size: number): BezierPath[];
|
|
25
|
+
export declare function hasMultiEffects(effects: LayerEffectsInfo): boolean;
|
|
26
|
+
export {};
|