Skip to content

Commit 6b946c6

Browse files
replace psd-tools with psd-ts
1 parent 60afc5d commit 6b946c6

File tree

94 files changed

+2487
-1373
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+2487
-1373
lines changed

packages/octopus-psd/examples/node/convert-debug.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ async function convertDir(dirPath: string) {
7979
}
8080
}
8181

82-
async function convert(locations: string[]) {
82+
export async function convert(locations: string[]) {
8383
for (const location of locations) {
8484
if (await isDirectory(location)) {
8585
await convertDir(location)

packages/octopus-psd/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"clean": "rimraf ./node_modules ./lib ./dist ./workdir ./test/integration/report ./jsdoc",
1111
"convert:debug": "yarn build && node ./lib/examples/node/convert-debug.js",
1212
"convert:local": "yarn build && node ./lib/examples/node/convert-local.js",
13+
"convert:bulk": "yarn clean:workdir && yarn build && node ./lib/examples/node/convert-bulk.js",
1314
"prepack": "rimraf ./lib && yarn run build",
1415
"test": "yarn test:unit && yarn test:integration",
1516
"test:unit": "yarn build && yarn node --experimental-vm-modules $(yarn bin jest)",
@@ -26,12 +27,12 @@
2627
"@opendesign/octopus-ts": "3.0.0-alpha.38",
2728
"@types/chalk": "^2.2.0",
2829
"@types/lodash": "^4.14.178",
29-
"@types/pino": "^6.3.3",
3030
"@types/rimraf": "^3.0.2",
3131
"@types/uuid": "^8.3.1",
3232
"chalk": "^4.1.2",
3333
"dotenv": "16.0.0",
3434
"image-size": "^1.0.1",
35+
"jimp": "^0.16.2",
3536
"lodash": "^4.17.21",
3637
"paper": "0.12.15",
3738
"pino": "^6.7.0",
@@ -41,6 +42,8 @@
4142
"uuid": "^8.3.2"
4243
},
4344
"devDependencies": {
45+
"@types/pino-pretty": "4.7.5",
46+
"@types/pino-std-serializers": "2.4.1",
4447
"@types/jest": "^27.4.0",
4548
"@types/node": "*",
4649
"handlebars": "^4.7.7",

packages/octopus-psd/pattern-01.psd

794 KB
Binary file not shown.

packages/octopus-psd/src/entities/octopus/octopus-component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export class OctopusComponent {
2222
constructor(options: OctopusComponentOptions) {
2323
this._sourceComponent = options.sourceComponent
2424
this._designConverter = options.designConverter
25+
2526
this._layers = createOctopusLayers(this.sourceComponent.layers, this)
2627
}
2728

packages/octopus-psd/src/entities/octopus/octopus-effect-bevel-emboss.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ export class OctopusEffectBevelEmboss extends OctopusEffectBase {
1414
protected _parentLayer: OctopusLayerBase
1515
private _bevelEmboss: SourceEffectBevelEmboss
1616

17+
static BEVEL_EMBOSS_TYPE_MAP = {
18+
OtrB: 'outerBevel',
19+
InrB: 'innerBevel',
20+
Embs: 'emboss',
21+
PlEb: 'pillowEmboss',
22+
strokeEmboss: 'strokeEmboss',
23+
} as const
24+
1725
constructor(options: OctopusEffectBevelEmbossOptions) {
1826
super(options)
1927
this._parentLayer = options.parentLayer

packages/octopus-psd/src/entities/octopus/octopus-effect-fill-gradient.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ export class OctopusEffectFillGradient {
3737
private _isStroke: boolean
3838

3939
static GRADIENT_TYPE_MAP = {
40-
linear: 'LINEAR',
41-
radial: 'RADIAL',
40+
Lnr: 'LINEAR',
41+
Rdl: 'RADIAL',
4242
Angl: 'ANGULAR',
4343
Dmnd: 'DIAMOND',
44-
reflected: 'REFLECTED',
44+
Rflc: 'REFLECTED',
4545
} as const
4646

4747
constructor(options: OctopusFillGradientOptions) {

packages/octopus-psd/src/entities/octopus/octopus-effect-fill.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export class OctopusEffectFill {
4545
}
4646

4747
private get _imageName(): string {
48-
return `${this._fill?.pattern?.ID}.png`
48+
return `${this._fill?.pattern?.Idnt}.png`
4949
}
5050

5151
private get _imagePath(): string | undefined {
@@ -66,7 +66,7 @@ export class OctopusEffectFill {
6666
const image = this._image
6767
const { width, height } = image ?? {}
6868
if (width === undefined || height === undefined) {
69-
logger.warn('Unknown image', { image, id: this._fill?.pattern?.ID })
69+
logger.warn('Unknown image', { image, id: this._fill?.pattern?.Idnt })
7070
return null
7171
}
7272
const matrix = createMatrix(width, 0, 0, height, ...this._offset)
@@ -77,7 +77,7 @@ export class OctopusEffectFill {
7777

7878
convert(): Octopus['Fill'] | null {
7979
const fill = this._fill
80-
if (!fill.enabled) return null
80+
if (!fill?.enabled) return null
8181
switch (this.fillType) {
8282
case 'GRADIENT': {
8383
const parentLayer = this._parentLayer

packages/octopus-psd/src/entities/octopus/octopus-effect-overlay-pattern.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export class OctopusEffectOverlayPattern extends OctopusEffectBase {
3737
}
3838

3939
private get _imageName(): string {
40-
return `${this._fill?.pattern?.ID}.png`
40+
return `${this._fill?.pattern?.Idnt}.png`
4141
}
4242

4343
private get _imagePath(): string | undefined {
@@ -63,7 +63,7 @@ export class OctopusEffectOverlayPattern extends OctopusEffectBase {
6363
const image = this._image
6464
const { width, height } = image ?? {}
6565
if (width === undefined || height === undefined) {
66-
logger.warn('Unknown image', { image, id: this._fill?.pattern?.ID })
66+
logger.warn('Unknown image', { image, id: this._fill?.pattern?.Idnt })
6767
return null
6868
}
6969
const matrix = createMatrix(width, 0, 0, height, ...this._offset)

packages/octopus-psd/src/entities/octopus/octopus-layer-mask-group.ts

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { OctopusLayerShape } from './octopus-layer-shape.js'
1010
import type { OctopusLayer } from '../../factories/create-octopus-layer'
1111
import type { SourceLayer } from '../../factories/create-source-layer'
1212
import type { Octopus } from '../../typings/octopus'
13-
import type { RawLayerLayer, RawLayerShape, RawPath } from '../../typings/raw'
13+
import type { RawLayerLayer, RawLayerShape } from '../../typings/raw'
1414
import type { SourceBounds, SourceColor } from '../../typings/source'
1515
import type { SourceLayerLayer } from '../source/source-layer-layer'
1616
import type { SourceLayerShape } from '../source/source-layer-shape'
@@ -95,9 +95,24 @@ export class OctopusLayerMaskGroup {
9595
if (!bitmapMask) return octopusLayer
9696
const { width, height } = octopusLayer.parentComponent.dimensions
9797
const bounds = { left: 0, right: width, top: 0, bottom: height }
98-
const raw: RawLayerLayer = { type: 'layer', bitmapBounds: bounds, bounds, visible: false, imageName: bitmapMask }
99-
const maskSourceLayer = createSourceLayer({ layer: raw, parent: sourceLayer?.parent }) as SourceLayerLayer
98+
99+
const raw: RawLayerLayer = {
100+
addedType: 'layer',
101+
width: bounds.right - bounds.left,
102+
height: bounds.bottom - bounds.top,
103+
top: bounds.top,
104+
left: bounds.left,
105+
isHidden: true,
106+
parsedProperties: {
107+
lyid: bitmapMask.replace('.png', ''),
108+
},
109+
}
110+
const maskSourceLayer = createSourceLayer({
111+
layer: raw,
112+
parent: sourceLayer?.parent,
113+
}) as SourceLayerLayer
100114
const maskAdapter = new OctopusLayerShapeLayerAdapter({ parent, sourceLayer: maskSourceLayer })
115+
101116
const mask = new OctopusLayerShape({ parent, sourceLayer, adapter: maskAdapter })
102117
return new OctopusLayerMaskGroup({
103118
parent,
@@ -115,10 +130,20 @@ export class OctopusLayerMaskGroup {
115130
parent,
116131
}: CreateWrapMaskOptions<T>): OctopusLayerMaskGroup | T {
117132
const path = sourceLayer.path
133+
118134
if (!path) return octopusLayer
119-
const raw: RawLayerShape = { type: 'shapeLayer', visible: false, path: path.raw as RawPath }
120-
const maskSourceLayer = createSourceLayer({ layer: raw, parent: sourceLayer?.parent }) as SourceLayerShape
135+
const raw: RawLayerShape = {
136+
addedType: 'shapeLayer',
137+
isHidden: true,
138+
parsedProperties: { vmsk: path.vectorMaskSetting, vogk: path.vectorOriginationData },
139+
}
140+
141+
const maskSourceLayer = createSourceLayer({
142+
layer: raw,
143+
parent: sourceLayer?.parent,
144+
}) as SourceLayerShape
121145
const maskAdapter = new OctopusLayerShapeShapeAdapter({ parent, sourceLayer: maskSourceLayer })
146+
122147
const mask = new OctopusLayerShape({ parent, sourceLayer, adapter: maskAdapter })
123148
const id = `${octopusLayer.id}-ShapeMask`
124149
return new OctopusLayerMaskGroup({ id, parent, mask, maskBasis: 'BODY', layers: [octopusLayer] })

packages/octopus-psd/src/entities/octopus/octopus-layer-shape-shape-path.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ export class OctopusLayerShapeShapePath {
9696
private _getPathRectangle(pathComponents: SourcePathComponent[]): Octopus['PathRectangle'] {
9797
const rect = pathComponents[0]
9898
const { bottom, left, right, top } = rect.origin.bounds
99+
99100
const [layerTx, layerTy] = this._parentLayer.layerTranslation
100101
const tx = left - layerTx
101102
const ty = top - layerTy

packages/octopus-psd/src/entities/octopus/octopus-manifest.ts

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import path from 'path'
22

3-
import { asString } from '@opendesign/octopus-common/dist/utils/as.js'
4-
import { traverseAndFind } from '@opendesign/octopus-common/dist/utils/common.js'
3+
import { asArray, asString } from '@opendesign/octopus-common/dist/utils/as.js'
4+
5+
import { getFontProperties } from '../../utils/text.js'
56

67
import type { OctopusPSDConverter } from '../..'
78
import type { Manifest } from '../../typings/manifest'
9+
import type { EngineData, NodeChildWithProps, ParsedPsd } from '../../typings/raw'
810
import type { SourceBounds } from '../../typings/source'
911
import type { SourceComponent } from '../source/source-component'
1012
import type { SourceDesign } from '../source/source-design'
@@ -106,22 +108,46 @@ export class OctopusManifest {
106108
}
107109
}
108110

109-
private _getComponentAssetsFonts(raw: Record<string, unknown>): string[] {
110-
const entries = traverseAndFind(raw, (obj: unknown) => Object(obj)?.fontPostScriptName)
111-
return [...new Set(entries)] as string[]
111+
private _getFontNames(engineData: EngineData | undefined): string[] {
112+
const { ResourceDict } = engineData ?? {}
113+
const { FontSet } = ResourceDict ?? {}
114+
const { RunArray } = engineData?.EngineDict?.StyleRun ?? {}
115+
const fontSet = asArray(FontSet)
116+
const runArray = asArray(RunArray)
117+
118+
return runArray.map(({ StyleSheet }) => {
119+
return getFontProperties(fontSet, StyleSheet?.StyleSheetData).fontName
120+
})
121+
}
122+
123+
private _getComponentAssetsFonts(
124+
raw: ParsedPsd | NodeChildWithProps,
125+
fontsSet: Set<string> = new Set()
126+
): Set<string> {
127+
if ('textProperties' in raw) {
128+
const fonts = this._getFontNames(raw.textProperties)
129+
fonts.forEach((fontName) => fontsSet.add(fontName))
130+
}
131+
132+
if ('children' in raw) {
133+
raw.children?.forEach((child) => this._getComponentAssetsFonts(child, fontsSet))
134+
}
135+
136+
return fontsSet
112137
}
113138

114139
private _getComponentAssets(targetComponent: SourceComponent): Manifest['Assets'] | null {
115140
const raw = targetComponent?.raw
116141
if (!raw) return null
117-
118142
const images: Manifest['AssetImage'][] = this._sourceDesign.images.map((image) => {
119143
const path = this.getExportedRelativeImageByName(image.name) ?? ''
120144
const location: Manifest['ResourceLocation'] = { type: 'RELATIVE', path }
121145
return { location, refId: image.name }
122146
})
123147

124-
const fonts: Manifest['AssetFont'][] = this._getComponentAssetsFonts(raw).map((font) => ({ name: font }))
148+
const fonts: Manifest['AssetFont'][] = Array.from(this._getComponentAssetsFonts(raw as ParsedPsd)).map((font) => ({
149+
name: font,
150+
}))
125151

126152
return {
127153
...(images.length ? { images } : null),

packages/octopus-psd/src/entities/source/source-component.ts

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,48 @@ import { asArray, asFiniteNumber } from '@opendesign/octopus-common/dist/utils/a
22
import { push } from '@opendesign/octopus-common/dist/utils/common.js'
33

44
import { createSourceLayer } from '../../factories/create-source-layer.js'
5-
import { getArtboardColor, getBoundsFor, isArtboard } from '../../utils/source.js'
5+
import { getArtboardColor, getBoundsFor, getLayerBounds } from '../../utils/source.js'
66
import { SourceEntity } from './source-entity.js'
77

88
import type { SourceLayer } from '../../factories/create-source-layer'
9-
import type { RawComponent, RawLayer } from '../../typings/raw'
9+
import type { NodeChildWithProps, ParsedPsd, RawColor } from '../../typings/raw'
1010
import type { SourceBounds, SourceColor } from '../../typings/source'
11+
import type { SourceDesign } from './source-design.js'
1112

1213
export type SourceComponentOptions = {
13-
raw: RawComponent & RawLayer
1414
isPasteboard?: boolean
15+
raw: ParsedPsd | NodeChildWithProps
16+
parent: SourceDesign
1517
}
1618

1719
export class SourceComponent extends SourceEntity {
18-
protected _rawValue: RawComponent & RawLayer
20+
protected _rawValue: NodeChildWithProps | ParsedPsd
1921
private _layers: SourceLayer[]
2022
private _isPasteboard: boolean
23+
private _parent: SourceDesign
2124

2225
static DEFAULT_ID = 'pasteboard-1'
2326
static DEFAULT_NAME = 'Pasteboard'
2427

25-
constructor({ raw, isPasteboard }: SourceComponentOptions) {
28+
constructor({ isPasteboard, raw, parent }: SourceComponentOptions) {
2629
super(raw)
2730
this._layers = this._initLayers()
2831
this._isPasteboard = isPasteboard ?? false
32+
this._parent = parent
2933
}
3034

3135
private _initLayers() {
32-
const layers = asArray(this._rawValue?.layers).reduce((layers: SourceLayer[], layer: RawLayer) => {
33-
const sourceLayer = createSourceLayer({ layer, parent: this })
36+
const layers = asArray(this._rawValue?.children).reduce((layers: SourceLayer[], layer) => {
37+
const sourceLayer = createSourceLayer({
38+
layer: layer as unknown as NodeChildWithProps,
39+
parent: this,
40+
})
3441
return sourceLayer ? push(layers, sourceLayer) : layers
3542
}, [])
3643
return layers
3744
}
3845

39-
get raw(): RawComponent & RawLayer {
46+
get raw(): NodeChildWithProps | ParsedPsd {
4047
return this._rawValue
4148
}
4249

@@ -45,12 +52,19 @@ export class SourceComponent extends SourceEntity {
4552
}
4653

4754
get bounds(): SourceBounds {
48-
const artboardRect = this._rawValue.artboard?.artboardRect
49-
return artboardRect ? getBoundsFor(artboardRect) : getBoundsFor(this._rawValue.bounds)
55+
const artboardRect = this._rawValue.parsedProperties?.artb?.artboardRect
56+
57+
return artboardRect
58+
? getBoundsFor(artboardRect)
59+
: this._rawValue?.type === 'Psd'
60+
? getBoundsFor({ Rght: this._rawValue?.width, Btom: this._rawValue?.height })
61+
: getLayerBounds(this._rawValue)
5062
}
5163

5264
get id(): string {
53-
return this._rawValue.id !== undefined ? String(this._rawValue.id) : SourceComponent.DEFAULT_ID
65+
return this._rawValue?.parsedProperties?.lyid !== undefined
66+
? String(this._rawValue.parsedProperties?.lyid)
67+
: SourceComponent.DEFAULT_ID
5468
}
5569

5670
get name(): string {
@@ -62,18 +76,34 @@ export class SourceComponent extends SourceEntity {
6276
}
6377

6478
get isArtboard(): boolean {
65-
return isArtboard(this._rawValue)
79+
return Boolean(this._rawValue?.parsedProperties?.artb)
6680
}
6781

6882
get artboardColor(): SourceColor | null {
69-
return getArtboardColor(this._rawValue)
83+
return getArtboardColor(this.artboardBackgroundType, this.rawArtboardColor)
84+
}
85+
86+
get artboardBackgroundType(): number | undefined {
87+
return this._rawValue?.parsedProperties?.artb?.artboardBackgroundType
7088
}
7189

72-
get resolution(): number | undefined {
73-
return this._rawValue.resolution
90+
get rawArtboardColor(): RawColor | undefined {
91+
return this._rawValue?.parsedProperties?.artb?.Clr
7492
}
7593

7694
get globalLightAngle(): number {
77-
return asFiniteNumber(this._rawValue.globalLight?.angle, 0)
95+
if ('globalLightAngle' in this._rawValue) {
96+
return asFiniteNumber(this._rawValue.globalLightAngle, 0)
97+
}
98+
99+
return 0
100+
}
101+
102+
get documentWidth(): number {
103+
return this._parent.documentWidth
104+
}
105+
106+
get documentHeight(): number {
107+
return this._parent.documentHeight
78108
}
79109
}

0 commit comments

Comments
 (0)