@midscene/shared 0.7.2 → 0.7.3-beta-20241104100519.0
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/dist/browser/constants.js +41 -0
- package/dist/{types → browser}/fs.d.ts +2 -2
- package/dist/browser/fs.js +62 -0
- package/dist/{types → browser}/img.d.ts +23 -9
- package/dist/browser/img.js +395 -0
- package/dist/browser/index.d.ts +3 -0
- package/dist/browser/index.js +26 -0
- package/dist/browser/utils.d.ts +4 -0
- package/dist/browser/utils.js +34 -0
- package/dist/es/constants.d.ts +13 -0
- package/dist/es/fs.d.ts +14 -0
- package/dist/es/fs.js +17 -14
- package/dist/es/img.d.ts +143 -0
- package/dist/es/img.js +271 -187
- package/dist/es/index.d.ts +3 -0
- package/dist/es/index.js +1 -3
- package/dist/es/utils.d.ts +4 -0
- package/dist/es/utils.js +13 -0
- package/dist/lib/constants.d.ts +13 -0
- package/dist/lib/fs.d.ts +14 -0
- package/dist/lib/fs.js +18 -25
- package/dist/lib/img.d.ts +143 -0
- package/dist/lib/img.js +270 -185
- package/dist/lib/index.d.ts +3 -0
- package/dist/lib/index.js +1 -3
- package/dist/lib/utils.d.ts +4 -0
- package/dist/lib/utils.js +39 -0
- package/dist/types/index.d.ts +2 -2
- package/package.json +37 -20
- package/src/fs/index.ts +16 -12
- package/src/img/box-select.ts +185 -170
- package/src/img/get-jimp.ts +11 -0
- package/src/img/index.ts +2 -0
- package/src/img/info.ts +24 -6
- package/src/img/jimp.d.ts +4 -0
- package/src/img/transform.ts +31 -9
- package/src/index.ts +1 -3
- package/src/utils.ts +10 -0
- /package/dist/{types → browser}/constants.d.ts +0 -0
package/package.json
CHANGED
|
@@ -1,46 +1,63 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@midscene/shared",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3-beta-20241104100519.0",
|
|
4
4
|
"repository": "https://github.com/web-infra-dev/midscene",
|
|
5
5
|
"homepage": "https://midscenejs.com/",
|
|
6
|
-
"types": "./
|
|
6
|
+
"types": "./src/index.ts",
|
|
7
|
+
"type": "commonjs",
|
|
7
8
|
"main": "./dist/lib/index.js",
|
|
8
9
|
"module": "./dist/es/index.js",
|
|
9
10
|
"exports": {
|
|
10
11
|
".": {
|
|
11
|
-
"types": "./dist/
|
|
12
|
-
"
|
|
13
|
-
"
|
|
12
|
+
"types": "./dist/lib/index.d.ts",
|
|
13
|
+
"require": "./dist/lib/index.js",
|
|
14
|
+
"import": "./dist/es/index.js"
|
|
14
15
|
},
|
|
15
16
|
"./constants": {
|
|
16
|
-
"types": "./dist/
|
|
17
|
-
"
|
|
18
|
-
"
|
|
17
|
+
"types": "./dist/lib/constants.d.ts",
|
|
18
|
+
"require": "./dist/lib/constants.js",
|
|
19
|
+
"import": "./dist/es/constants.js"
|
|
20
|
+
},
|
|
21
|
+
"./fs": {
|
|
22
|
+
"types": "./dist/lib/fs.d.ts",
|
|
23
|
+
"require": "./dist/lib/fs.js",
|
|
24
|
+
"import": "./dist/es/fs.js"
|
|
19
25
|
},
|
|
20
26
|
"./img": {
|
|
21
|
-
"types": "./dist/
|
|
22
|
-
"
|
|
23
|
-
"
|
|
27
|
+
"types": "./dist/lib/img.d.ts",
|
|
28
|
+
"require": "./dist/lib/img.js",
|
|
29
|
+
"import": "./dist/es/img.js"
|
|
24
30
|
},
|
|
25
|
-
"./
|
|
26
|
-
"types": "./dist/
|
|
27
|
-
"
|
|
28
|
-
"
|
|
31
|
+
"./browser/img": {
|
|
32
|
+
"types": "./dist/browser/img.d.ts",
|
|
33
|
+
"require": "./dist/browser/img.js",
|
|
34
|
+
"import": "./dist/browser/img.js"
|
|
35
|
+
},
|
|
36
|
+
"./utils": {
|
|
37
|
+
"types": "./dist/lib/utils.d.ts",
|
|
38
|
+
"require": "./dist/lib/utils.js",
|
|
39
|
+
"import": "./dist/es/utils.js"
|
|
29
40
|
}
|
|
30
41
|
},
|
|
31
42
|
"typesVersions": {
|
|
32
43
|
"*": {
|
|
33
44
|
".": [
|
|
34
|
-
"./dist/
|
|
45
|
+
"./dist/lib/index.d.ts"
|
|
35
46
|
],
|
|
36
47
|
"constants": [
|
|
37
|
-
"./dist/
|
|
48
|
+
"./dist/lib/constants.d.ts"
|
|
38
49
|
],
|
|
39
50
|
"img": [
|
|
40
|
-
"./dist/
|
|
51
|
+
"./dist/lib/img.d.ts"
|
|
52
|
+
],
|
|
53
|
+
"browser/img": [
|
|
54
|
+
"./dist/browser/img.d.ts"
|
|
41
55
|
],
|
|
42
56
|
"fs": [
|
|
43
|
-
"./dist/
|
|
57
|
+
"./dist/lib/fs.d.ts"
|
|
58
|
+
],
|
|
59
|
+
"utils": [
|
|
60
|
+
"./dist/lib/utils.d.ts"
|
|
44
61
|
]
|
|
45
62
|
}
|
|
46
63
|
},
|
|
@@ -54,7 +71,7 @@
|
|
|
54
71
|
},
|
|
55
72
|
"peerDependencies": {},
|
|
56
73
|
"devDependencies": {
|
|
57
|
-
"@modern-js/module-tools": "2.
|
|
74
|
+
"@modern-js/module-tools": "2.60.6",
|
|
58
75
|
"typescript": "~5.0.4",
|
|
59
76
|
"@types/node": "^18.0.0",
|
|
60
77
|
"rimraf": "~3.0.2",
|
package/src/fs/index.ts
CHANGED
|
@@ -8,25 +8,29 @@ interface PkgInfo {
|
|
|
8
8
|
dir: string;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
const pkgCacheMap: Record<string, PkgInfo> = {};
|
|
12
|
+
const ifInBrowser = typeof window !== 'undefined';
|
|
13
|
+
export function getRunningPkgInfo(dir?: string): PkgInfo | null {
|
|
14
|
+
if (ifInBrowser) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
const dirToCheck = dir || process.cwd();
|
|
18
|
+
if (pkgCacheMap[dirToCheck]) {
|
|
19
|
+
return pkgCacheMap[dirToCheck];
|
|
15
20
|
}
|
|
16
21
|
|
|
17
|
-
const pkgDir = findNearestPackageJson(
|
|
18
|
-
|
|
19
|
-
const pkgJsonFile = join(pkgDir, 'package.json');
|
|
22
|
+
const pkgDir = findNearestPackageJson(dirToCheck);
|
|
23
|
+
const pkgJsonFile = pkgDir ? join(pkgDir, 'package.json') : null;
|
|
20
24
|
|
|
21
|
-
if (pkgJsonFile) {
|
|
25
|
+
if (pkgDir && pkgJsonFile) {
|
|
22
26
|
const { name, version } = JSON.parse(readFileSync(pkgJsonFile, 'utf-8'));
|
|
23
|
-
|
|
24
|
-
return
|
|
27
|
+
pkgCacheMap[dirToCheck] = { name, version, dir: pkgDir };
|
|
28
|
+
return pkgCacheMap[dirToCheck];
|
|
25
29
|
}
|
|
26
30
|
return {
|
|
27
|
-
name: 'midscene-unknown-
|
|
31
|
+
name: 'midscene-unknown-package-name',
|
|
28
32
|
version: '0.0.0',
|
|
29
|
-
dir:
|
|
33
|
+
dir: dirToCheck,
|
|
30
34
|
};
|
|
31
35
|
}
|
|
32
36
|
|
package/src/img/box-select.ts
CHANGED
|
@@ -1,231 +1,246 @@
|
|
|
1
1
|
import assert from 'node:assert';
|
|
2
|
-
import { Buffer } from 'node:buffer';
|
|
2
|
+
import type { Buffer } from 'node:buffer';
|
|
3
3
|
import type { Rect } from '@/types';
|
|
4
|
-
import Jimp from 'jimp';
|
|
4
|
+
import type Jimp from 'jimp';
|
|
5
5
|
import type { NodeType } from '../constants';
|
|
6
|
+
import getJimp from './get-jimp';
|
|
7
|
+
import { bufferFromBase64, imageInfo, imageInfoOfBase64 } from './index';
|
|
6
8
|
|
|
7
9
|
// Define picture path
|
|
8
10
|
type ElementType = {
|
|
9
|
-
locator
|
|
11
|
+
locator?: string;
|
|
10
12
|
|
|
11
13
|
rect: Rect;
|
|
12
14
|
|
|
13
|
-
center
|
|
15
|
+
center?: [number, number];
|
|
14
16
|
|
|
15
|
-
id
|
|
17
|
+
id?: string;
|
|
16
18
|
|
|
17
19
|
indexId: number;
|
|
18
20
|
|
|
19
|
-
attributes
|
|
21
|
+
attributes?: {
|
|
20
22
|
nodeType: NodeType;
|
|
21
23
|
[key: string]: string;
|
|
22
24
|
};
|
|
23
25
|
};
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
let cachedFont: any = null;
|
|
28
|
+
|
|
29
|
+
const createSvgOverlay = async (
|
|
26
30
|
elements: Array<ElementType>,
|
|
27
31
|
imageWidth: number,
|
|
28
32
|
imageHeight: number,
|
|
29
|
-
) => {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
imageWidth: number,
|
|
33
|
-
imageHeight: number,
|
|
34
|
-
) => {
|
|
35
|
-
const image = new Jimp(imageWidth, imageHeight, 0x00000000);
|
|
36
|
-
|
|
37
|
-
// Define color array
|
|
38
|
-
const colors = [
|
|
39
|
-
{ rect: 0xff0000ff, text: 0xffffffff }, // red, white
|
|
40
|
-
// { rect: 0x0000ffff, text: 0xffffffff }, // blue, white
|
|
41
|
-
// { rect: 0x8b4513ff, text: 0xffffffff }, // brown, white
|
|
42
|
-
];
|
|
33
|
+
): Promise<Jimp> => {
|
|
34
|
+
const Jimp = await getJimp();
|
|
35
|
+
const image = new Jimp(imageWidth, imageHeight, 0x00000000);
|
|
43
36
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
37
|
+
// Define color array
|
|
38
|
+
const colors = [
|
|
39
|
+
{ rect: 0xff0000ff, text: 0xffffffff }, // red, white
|
|
40
|
+
// { rect: 0x0000ffff, text: 0xffffffff }, // blue, white
|
|
41
|
+
// { rect: 0x8b4513ff, text: 0xffffffff }, // brown, white
|
|
42
|
+
];
|
|
48
43
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
width: Math.min(
|
|
54
|
-
imageWidth - element.rect.left,
|
|
55
|
-
element.rect.width + boxPadding * 2,
|
|
56
|
-
),
|
|
57
|
-
height: Math.min(
|
|
58
|
-
imageHeight - element.rect.top,
|
|
59
|
-
element.rect.height + boxPadding * 2,
|
|
60
|
-
),
|
|
61
|
-
};
|
|
44
|
+
const boxPadding = 5;
|
|
45
|
+
for (let index = 0; index < elements.length; index++) {
|
|
46
|
+
const element = elements[index];
|
|
47
|
+
const color = colors[index % colors.length];
|
|
62
48
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
this.bitmap.data[idx + 0] = (color.rect >> 24) & 0xff; // R
|
|
77
|
-
this.bitmap.data[idx + 1] = (color.rect >> 16) & 0xff; // G
|
|
78
|
-
this.bitmap.data[idx + 2] = (color.rect >> 8) & 0xff; // B
|
|
79
|
-
this.bitmap.data[idx + 3] = color.rect & 0xff; // A
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
);
|
|
49
|
+
// Add 5px padding to the rect
|
|
50
|
+
const paddedRect = {
|
|
51
|
+
left: Math.max(0, element.rect.left - boxPadding),
|
|
52
|
+
top: Math.max(0, element.rect.top - boxPadding),
|
|
53
|
+
width: Math.min(
|
|
54
|
+
imageWidth - element.rect.left,
|
|
55
|
+
element.rect.width + boxPadding * 2,
|
|
56
|
+
),
|
|
57
|
+
height: Math.min(
|
|
58
|
+
imageHeight - element.rect.top,
|
|
59
|
+
element.rect.height + boxPadding * 2,
|
|
60
|
+
),
|
|
61
|
+
};
|
|
83
62
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
63
|
+
// Draw rectangle
|
|
64
|
+
image.scan(
|
|
65
|
+
paddedRect.left,
|
|
66
|
+
paddedRect.top,
|
|
67
|
+
paddedRect.width,
|
|
68
|
+
paddedRect.height,
|
|
69
|
+
function (x, y, idx) {
|
|
70
|
+
if (
|
|
71
|
+
x === paddedRect.left ||
|
|
72
|
+
x === paddedRect.left + paddedRect.width - 1 ||
|
|
73
|
+
y === paddedRect.top ||
|
|
74
|
+
y === paddedRect.top + paddedRect.height - 1
|
|
75
|
+
) {
|
|
76
|
+
this.bitmap.data[idx + 0] = (color.rect >> 24) & 0xff; // R
|
|
77
|
+
this.bitmap.data[idx + 1] = (color.rect >> 16) & 0xff; // G
|
|
78
|
+
this.bitmap.data[idx + 2] = (color.rect >> 8) & 0xff; // B
|
|
79
|
+
this.bitmap.data[idx + 3] = color.rect & 0xff; // A
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
);
|
|
91
83
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
x < otherElement.rect.left + otherElement.rect.width &&
|
|
100
|
-
x + rectWidth > otherElement.rect.left &&
|
|
101
|
-
y < otherElement.rect.top + otherElement.rect.height &&
|
|
102
|
-
y + rectHeight > otherElement.rect.top
|
|
103
|
-
);
|
|
104
|
-
});
|
|
105
|
-
};
|
|
84
|
+
// Calculate text position
|
|
85
|
+
const textWidth = element.indexId.toString().length * 8;
|
|
86
|
+
const textHeight = 12;
|
|
87
|
+
const rectWidth = textWidth + 5;
|
|
88
|
+
const rectHeight = textHeight + 4;
|
|
89
|
+
let rectX = paddedRect.left - rectWidth;
|
|
90
|
+
let rectY = paddedRect.top + paddedRect.height / 2 - textHeight / 2 - 2;
|
|
106
91
|
|
|
107
|
-
|
|
108
|
-
|
|
92
|
+
// Check if this new position overlaps with any existing boxes
|
|
93
|
+
// Function to check if a given position overlaps with any existing boxes
|
|
94
|
+
const checkOverlap = (x: number, y: number) => {
|
|
95
|
+
// Check against all previously processed elements
|
|
96
|
+
return elements.slice(0, index).some((otherElement) => {
|
|
97
|
+
// Check if the rectangles overlap
|
|
109
98
|
return (
|
|
110
|
-
x
|
|
111
|
-
x + rectWidth
|
|
112
|
-
y
|
|
113
|
-
y + rectHeight
|
|
99
|
+
x < otherElement.rect.left + otherElement.rect.width &&
|
|
100
|
+
x + rectWidth > otherElement.rect.left &&
|
|
101
|
+
y < otherElement.rect.top + otherElement.rect.height &&
|
|
102
|
+
y + rectHeight > otherElement.rect.top
|
|
114
103
|
);
|
|
115
|
-
};
|
|
104
|
+
});
|
|
105
|
+
};
|
|
116
106
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
107
|
+
// Function to check if a given position is within the image bounds
|
|
108
|
+
const isWithinBounds = (x: number, y: number) => {
|
|
109
|
+
return (
|
|
110
|
+
x >= 0 &&
|
|
111
|
+
x + rectWidth <= imageWidth &&
|
|
112
|
+
y >= 0 &&
|
|
113
|
+
y + rectHeight <= imageHeight
|
|
114
|
+
);
|
|
115
|
+
};
|
|
120
116
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
isWithinBounds(paddedRect.left, paddedRect.top - rectHeight - 2)
|
|
125
|
-
) {
|
|
126
|
-
rectX = paddedRect.left;
|
|
127
|
-
rectY = paddedRect.top - rectHeight - 2;
|
|
128
|
-
}
|
|
129
|
-
// Check bottom position
|
|
130
|
-
else if (
|
|
131
|
-
!checkOverlap(
|
|
132
|
-
paddedRect.left,
|
|
133
|
-
paddedRect.top + paddedRect.height + 2,
|
|
134
|
-
) &&
|
|
135
|
-
isWithinBounds(
|
|
136
|
-
paddedRect.left,
|
|
137
|
-
paddedRect.top + paddedRect.height + 2,
|
|
138
|
-
)
|
|
139
|
-
) {
|
|
140
|
-
rectX = paddedRect.left;
|
|
141
|
-
rectY = paddedRect.top + paddedRect.height + 2;
|
|
142
|
-
}
|
|
143
|
-
// Check right position
|
|
144
|
-
else if (
|
|
145
|
-
!checkOverlap(
|
|
146
|
-
paddedRect.left + paddedRect.width + 2,
|
|
147
|
-
paddedRect.top,
|
|
148
|
-
) &&
|
|
149
|
-
isWithinBounds(paddedRect.left + paddedRect.width + 2, paddedRect.top)
|
|
150
|
-
) {
|
|
151
|
-
rectX = paddedRect.left + paddedRect.width + 2;
|
|
152
|
-
rectY = paddedRect.top;
|
|
153
|
-
}
|
|
154
|
-
// If all sides are overlapped or out of bounds, place it inside the box at the top
|
|
155
|
-
else {
|
|
156
|
-
rectX = paddedRect.left;
|
|
157
|
-
rectY = paddedRect.top + 2;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
// Note: If the original left position doesn't overlap and is within bounds, we keep it as is
|
|
117
|
+
// Check left side (original position)
|
|
118
|
+
if (checkOverlap(rectX, rectY) || !isWithinBounds(rectX, rectY)) {
|
|
119
|
+
// If the original position overlaps or is out of bounds, try alternative positions
|
|
161
120
|
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
121
|
+
// Check top position
|
|
122
|
+
if (
|
|
123
|
+
!checkOverlap(paddedRect.left, paddedRect.top - rectHeight - 2) &&
|
|
124
|
+
isWithinBounds(paddedRect.left, paddedRect.top - rectHeight - 2)
|
|
125
|
+
) {
|
|
126
|
+
rectX = paddedRect.left;
|
|
127
|
+
rectY = paddedRect.top - rectHeight - 2;
|
|
128
|
+
}
|
|
129
|
+
// Check bottom position
|
|
130
|
+
else if (
|
|
131
|
+
!checkOverlap(
|
|
132
|
+
paddedRect.left,
|
|
133
|
+
paddedRect.top + paddedRect.height + 2,
|
|
134
|
+
) &&
|
|
135
|
+
isWithinBounds(paddedRect.left, paddedRect.top + paddedRect.height + 2)
|
|
136
|
+
) {
|
|
137
|
+
rectX = paddedRect.left;
|
|
138
|
+
rectY = paddedRect.top + paddedRect.height + 2;
|
|
139
|
+
}
|
|
140
|
+
// Check right position
|
|
141
|
+
else if (
|
|
142
|
+
!checkOverlap(paddedRect.left + paddedRect.width + 2, paddedRect.top) &&
|
|
143
|
+
isWithinBounds(paddedRect.left + paddedRect.width + 2, paddedRect.top)
|
|
144
|
+
) {
|
|
145
|
+
rectX = paddedRect.left + paddedRect.width + 2;
|
|
146
|
+
rectY = paddedRect.top;
|
|
147
|
+
}
|
|
148
|
+
// If all sides are overlapped or out of bounds, place it inside the box at the top
|
|
149
|
+
else {
|
|
150
|
+
rectX = paddedRect.left;
|
|
151
|
+
rectY = paddedRect.top + 2;
|
|
152
|
+
}
|
|
183
153
|
}
|
|
154
|
+
// Note: If the original left position doesn't overlap and is within bounds, we keep it as is
|
|
184
155
|
|
|
185
|
-
|
|
186
|
-
|
|
156
|
+
// Draw text background
|
|
157
|
+
image.scan(rectX, rectY, rectWidth, rectHeight, function (x, y, idx) {
|
|
158
|
+
this.bitmap.data[idx + 0] = (color.rect >> 24) & 0xff; // R
|
|
159
|
+
this.bitmap.data[idx + 1] = (color.rect >> 16) & 0xff; // G
|
|
160
|
+
this.bitmap.data[idx + 2] = (color.rect >> 8) & 0xff; // B
|
|
161
|
+
this.bitmap.data[idx + 3] = color.rect & 0xff; // A
|
|
162
|
+
});
|
|
163
|
+
// Draw text (simplified, as Jimp doesn't have built-in text drawing)
|
|
164
|
+
try {
|
|
165
|
+
cachedFont = cachedFont || (await Jimp.loadFont(Jimp.FONT_SANS_16_WHITE));
|
|
166
|
+
} catch (error) {
|
|
167
|
+
console.error('Error loading font', error);
|
|
168
|
+
}
|
|
169
|
+
image.print(
|
|
170
|
+
cachedFont,
|
|
171
|
+
rectX,
|
|
172
|
+
rectY,
|
|
173
|
+
{
|
|
174
|
+
text: element.indexId.toString(),
|
|
175
|
+
alignmentX: Jimp.HORIZONTAL_ALIGN_CENTER,
|
|
176
|
+
alignmentY: Jimp.VERTICAL_ALIGN_MIDDLE,
|
|
177
|
+
},
|
|
178
|
+
rectWidth,
|
|
179
|
+
rectHeight,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
187
182
|
|
|
188
|
-
return
|
|
183
|
+
return image;
|
|
189
184
|
};
|
|
190
185
|
|
|
191
186
|
export const compositeElementInfoImg = async (options: {
|
|
192
187
|
inputImgBase64: string;
|
|
193
188
|
elementsPositionInfo: Array<ElementType>;
|
|
189
|
+
size?: { width: number; height: number };
|
|
194
190
|
}) => {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
191
|
+
assert(options.inputImgBase64, 'inputImgBase64 is required');
|
|
192
|
+
let width = 0;
|
|
193
|
+
let height = 0;
|
|
194
|
+
let jimpImage: Jimp;
|
|
195
|
+
|
|
196
|
+
const Jimp = await getJimp();
|
|
197
|
+
|
|
198
|
+
if (options.size) {
|
|
199
|
+
width = options.size.width;
|
|
200
|
+
height = options.size.height;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (!width || !height) {
|
|
204
|
+
const info = await imageInfoOfBase64(options.inputImgBase64);
|
|
205
|
+
width = info.width;
|
|
206
|
+
height = info.height;
|
|
207
|
+
jimpImage = info.jimpImage;
|
|
208
|
+
} else {
|
|
209
|
+
const imageBuffer = await bufferFromBase64(options.inputImgBase64);
|
|
210
|
+
jimpImage = await Jimp.read(imageBuffer);
|
|
211
|
+
}
|
|
199
212
|
|
|
200
213
|
if (!width || !height) {
|
|
201
214
|
throw Error('Image processing failed because width or height is undefined');
|
|
202
215
|
}
|
|
203
216
|
|
|
204
|
-
|
|
205
|
-
const svgOverlay = await createSvgOverlay(
|
|
206
|
-
elementsPositionInfo,
|
|
207
|
-
width,
|
|
208
|
-
height,
|
|
209
|
-
);
|
|
217
|
+
const { elementsPositionInfo } = options;
|
|
210
218
|
|
|
211
|
-
|
|
219
|
+
const result = await Promise.resolve(jimpImage)
|
|
212
220
|
.then(async (image: Jimp) => {
|
|
221
|
+
// Create svg overlay
|
|
222
|
+
const svgOverlay = await createSvgOverlay(
|
|
223
|
+
elementsPositionInfo,
|
|
224
|
+
width,
|
|
225
|
+
height,
|
|
226
|
+
);
|
|
213
227
|
const svgImage = await Jimp.read(svgOverlay);
|
|
214
|
-
|
|
228
|
+
const compositeImage = await image.composite(svgImage, 0, 0, {
|
|
215
229
|
mode: Jimp.BLEND_SOURCE_OVER,
|
|
216
230
|
opacitySource: 1,
|
|
217
231
|
opacityDest: 1,
|
|
218
232
|
});
|
|
233
|
+
return compositeImage;
|
|
219
234
|
})
|
|
220
|
-
.then((compositeImage: Jimp) => {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
.then((buffer: Buffer) => {
|
|
224
|
-
return buffer.toString('base64');
|
|
235
|
+
.then(async (compositeImage: Jimp) => {
|
|
236
|
+
const base64 = await compositeImage.getBase64Async(Jimp.MIME_PNG);
|
|
237
|
+
return base64;
|
|
225
238
|
})
|
|
226
239
|
.catch((error: unknown) => {
|
|
227
240
|
throw error;
|
|
228
241
|
});
|
|
242
|
+
|
|
243
|
+
return result;
|
|
229
244
|
};
|
|
230
245
|
|
|
231
246
|
export const processImageElementInfo = async (options: {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type Jimp from 'jimp/browser/lib/jimp.js';
|
|
2
|
+
|
|
3
|
+
const ifInBrowser = typeof window !== 'undefined';
|
|
4
|
+
export default async function getJimp(): Promise<typeof Jimp> {
|
|
5
|
+
if (ifInBrowser) {
|
|
6
|
+
await import('jimp/browser/lib/jimp.js');
|
|
7
|
+
return (window as any).Jimp;
|
|
8
|
+
}
|
|
9
|
+
// return Jimp;
|
|
10
|
+
return (await import('jimp')).default;
|
|
11
|
+
}
|
package/src/img/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export {
|
|
2
2
|
imageInfo,
|
|
3
3
|
imageInfoOfBase64,
|
|
4
|
+
bufferFromBase64,
|
|
4
5
|
base64Encoded,
|
|
5
6
|
base64ToPngFormat,
|
|
6
7
|
} from './info';
|
|
@@ -8,6 +9,7 @@ export {
|
|
|
8
9
|
trimImage,
|
|
9
10
|
calculateNewDimensions,
|
|
10
11
|
resizeImg,
|
|
12
|
+
resizeImgBase64,
|
|
11
13
|
transformImgPathToBase64,
|
|
12
14
|
saveBase64Image,
|
|
13
15
|
} from './transform';
|
package/src/img/info.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import assert from 'node:assert';
|
|
2
2
|
import { Buffer } from 'node:buffer';
|
|
3
3
|
import { readFileSync } from 'node:fs';
|
|
4
|
-
import Jimp from 'jimp';
|
|
4
|
+
import type Jimp from 'jimp';
|
|
5
|
+
import getJimp from './get-jimp';
|
|
5
6
|
|
|
6
7
|
export interface Size {
|
|
7
8
|
width: number;
|
|
8
9
|
height: number;
|
|
9
10
|
}
|
|
10
11
|
|
|
12
|
+
export interface ImageInfo extends Size {
|
|
13
|
+
jimpImage: Jimp;
|
|
14
|
+
}
|
|
15
|
+
|
|
11
16
|
/**
|
|
12
17
|
* Retrieves the dimensions of an image asynchronously
|
|
13
18
|
*
|
|
@@ -15,12 +20,17 @@ export interface Size {
|
|
|
15
20
|
* @returns A Promise that resolves to an object containing the width and height of the image
|
|
16
21
|
* @throws Error if the image data is invalid
|
|
17
22
|
*/
|
|
18
|
-
export async function imageInfo(
|
|
23
|
+
export async function imageInfo(
|
|
24
|
+
image: string | Buffer | Jimp,
|
|
25
|
+
): Promise<ImageInfo> {
|
|
26
|
+
const Jimp = await getJimp();
|
|
19
27
|
let jimpImage: Jimp;
|
|
20
28
|
if (typeof image === 'string') {
|
|
21
29
|
jimpImage = await Jimp.read(image);
|
|
22
30
|
} else if (Buffer.isBuffer(image)) {
|
|
23
31
|
jimpImage = await Jimp.read(image);
|
|
32
|
+
} else if (image instanceof Jimp) {
|
|
33
|
+
jimpImage = image;
|
|
24
34
|
} else {
|
|
25
35
|
throw new Error('Invalid image input: must be a string path or a Buffer');
|
|
26
36
|
}
|
|
@@ -29,7 +39,7 @@ export async function imageInfo(image: string | Buffer): Promise<Size> {
|
|
|
29
39
|
width && height,
|
|
30
40
|
`Invalid image: ${typeof image === 'string' ? image : 'Buffer'}`,
|
|
31
41
|
);
|
|
32
|
-
return { width, height };
|
|
42
|
+
return { width, height, jimpImage };
|
|
33
43
|
}
|
|
34
44
|
|
|
35
45
|
/**
|
|
@@ -39,10 +49,18 @@ export async function imageInfo(image: string | Buffer): Promise<Size> {
|
|
|
39
49
|
* @returns A Promise that resolves to an object containing the width and height of the image
|
|
40
50
|
* @throws Error if the image data is invalid
|
|
41
51
|
*/
|
|
42
|
-
export async function imageInfoOfBase64(
|
|
43
|
-
|
|
52
|
+
export async function imageInfoOfBase64(
|
|
53
|
+
imageBase64: string,
|
|
54
|
+
): Promise<ImageInfo> {
|
|
55
|
+
// const base64Data = imageBase64.replace(/^data:image\/\w+;base64,/, '');
|
|
44
56
|
// Call the imageInfo function to get the dimensions of the image
|
|
45
|
-
|
|
57
|
+
const buffer = await bufferFromBase64(imageBase64);
|
|
58
|
+
return imageInfo(buffer);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export async function bufferFromBase64(imageBase64: string): Promise<Buffer> {
|
|
62
|
+
const base64Data = imageBase64.replace(/^data:image\/\w+;base64,/, '');
|
|
63
|
+
return Buffer.from(base64Data, 'base64');
|
|
46
64
|
}
|
|
47
65
|
|
|
48
66
|
/**
|