@midscene/shared 0.5.2-beta-20241010035503.0 → 0.5.2
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/es/img.js +78 -65
- package/dist/lib/img.js +80 -65
- package/dist/types/img.d.ts +24 -9
- package/package.json +1 -1
- package/src/img/box-select.ts +102 -87
- package/src/img/index.ts +7 -2
- package/src/img/info.ts +4 -0
package/dist/es/img.js
CHANGED
|
@@ -36,6 +36,9 @@ function base64Encoded(image, withHeader = true) {
|
|
|
36
36
|
}
|
|
37
37
|
throw new Error("unsupported image type");
|
|
38
38
|
}
|
|
39
|
+
function base64ToPngFormat(base64) {
|
|
40
|
+
return `data:image/png;base64,${base64}`;
|
|
41
|
+
}
|
|
39
42
|
|
|
40
43
|
// src/img/transform.ts
|
|
41
44
|
import { Buffer as Buffer2 } from "buffer";
|
|
@@ -115,21 +118,34 @@ var createSvgOverlay = (elements, imageWidth, imageHeight) => {
|
|
|
115
118
|
const createPngOverlay = async (elements2, imageWidth2, imageHeight2) => {
|
|
116
119
|
const image = new Jimp3(imageWidth2, imageHeight2, 0);
|
|
117
120
|
const colors = [
|
|
118
|
-
{ rect:
|
|
119
|
-
//
|
|
120
|
-
{ rect:
|
|
121
|
-
// brown, white
|
|
121
|
+
{ rect: 4278190335, text: 4294967295 }
|
|
122
|
+
// red, white
|
|
123
|
+
// { rect: 0x0000ffff, text: 0xffffffff }, // blue, white
|
|
124
|
+
// { rect: 0x8b4513ff, text: 0xffffffff }, // brown, white
|
|
122
125
|
];
|
|
126
|
+
const boxPadding = 5;
|
|
123
127
|
for (let index = 0; index < elements2.length; index++) {
|
|
124
128
|
const element = elements2[index];
|
|
125
129
|
const color = colors[index % colors.length];
|
|
130
|
+
const paddedRect = {
|
|
131
|
+
left: Math.max(0, element.rect.left - boxPadding),
|
|
132
|
+
top: Math.max(0, element.rect.top - boxPadding),
|
|
133
|
+
width: Math.min(
|
|
134
|
+
imageWidth2 - element.rect.left,
|
|
135
|
+
element.rect.width + boxPadding * 2
|
|
136
|
+
),
|
|
137
|
+
height: Math.min(
|
|
138
|
+
imageHeight2 - element.rect.top,
|
|
139
|
+
element.rect.height + boxPadding * 2
|
|
140
|
+
)
|
|
141
|
+
};
|
|
126
142
|
image.scan(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
143
|
+
paddedRect.left,
|
|
144
|
+
paddedRect.top,
|
|
145
|
+
paddedRect.width,
|
|
146
|
+
paddedRect.height,
|
|
131
147
|
function(x, y, idx) {
|
|
132
|
-
if (x ===
|
|
148
|
+
if (x === paddedRect.left || x === paddedRect.left + paddedRect.width - 1 || y === paddedRect.top || y === paddedRect.top + paddedRect.height - 1) {
|
|
133
149
|
this.bitmap.data[idx + 0] = color.rect >> 24 & 255;
|
|
134
150
|
this.bitmap.data[idx + 1] = color.rect >> 16 & 255;
|
|
135
151
|
this.bitmap.data[idx + 2] = color.rect >> 8 & 255;
|
|
@@ -137,15 +153,15 @@ var createSvgOverlay = (elements, imageWidth, imageHeight) => {
|
|
|
137
153
|
}
|
|
138
154
|
}
|
|
139
155
|
);
|
|
140
|
-
const textWidth = element.
|
|
156
|
+
const textWidth = element.indexId.toString().length * 8;
|
|
141
157
|
const textHeight = 12;
|
|
142
158
|
const rectWidth = textWidth + 5;
|
|
143
159
|
const rectHeight = textHeight + 4;
|
|
144
|
-
let rectX =
|
|
145
|
-
let rectY =
|
|
160
|
+
let rectX = paddedRect.left - rectWidth;
|
|
161
|
+
let rectY = paddedRect.top + paddedRect.height / 2 - textHeight / 2 - 2;
|
|
146
162
|
if (rectX < 0) {
|
|
147
|
-
rectX =
|
|
148
|
-
rectY =
|
|
163
|
+
rectX = paddedRect.left;
|
|
164
|
+
rectY = paddedRect.top - rectHeight;
|
|
149
165
|
}
|
|
150
166
|
image.scan(rectX, rectY, rectWidth, rectHeight, function(x, y, idx) {
|
|
151
167
|
this.bitmap.data[idx + 0] = color.rect >> 24 & 255;
|
|
@@ -159,7 +175,7 @@ var createSvgOverlay = (elements, imageWidth, imageHeight) => {
|
|
|
159
175
|
rectX,
|
|
160
176
|
rectY,
|
|
161
177
|
{
|
|
162
|
-
text: element.
|
|
178
|
+
text: element.indexId.toString(),
|
|
163
179
|
alignmentX: Jimp3.HORIZONTAL_ALIGN_CENTER,
|
|
164
180
|
alignmentY: Jimp3.VERTICAL_ALIGN_MIDDLE
|
|
165
181
|
},
|
|
@@ -171,63 +187,60 @@ var createSvgOverlay = (elements, imageWidth, imageHeight) => {
|
|
|
171
187
|
};
|
|
172
188
|
return createPngOverlay(elements, imageWidth, imageHeight);
|
|
173
189
|
};
|
|
174
|
-
var
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
const imageBuffer = Buffer3.from(base64Image, "base64");
|
|
190
|
+
var compositeElementInfoImg = async (options) => {
|
|
191
|
+
const { inputImgBase64, elementsPositionInfo } = options;
|
|
192
|
+
const imageBuffer = Buffer3.from(inputImgBase64, "base64");
|
|
178
193
|
const image = await Jimp3.read(imageBuffer);
|
|
179
194
|
const { width, height } = image.bitmap;
|
|
180
|
-
if (width
|
|
181
|
-
|
|
182
|
-
options.elementsPositionInfo,
|
|
183
|
-
width,
|
|
184
|
-
height
|
|
185
|
-
);
|
|
186
|
-
const svgOverlayWithoutText = await createSvgOverlay(
|
|
187
|
-
options.elementsPositionInfoWithoutText,
|
|
188
|
-
width,
|
|
189
|
-
height
|
|
190
|
-
);
|
|
191
|
-
const compositeElementInfoImgBase64 = await Jimp3.read(imageBuffer).then(async (image2) => {
|
|
192
|
-
const svgImage = await Jimp3.read(svgOverlay);
|
|
193
|
-
return image2.composite(svgImage, 0, 0, {
|
|
194
|
-
mode: Jimp3.BLEND_SOURCE_OVER,
|
|
195
|
-
opacitySource: 1,
|
|
196
|
-
opacityDest: 1
|
|
197
|
-
});
|
|
198
|
-
}).then((compositeImage) => {
|
|
199
|
-
return compositeImage.getBufferAsync(Jimp3.MIME_PNG);
|
|
200
|
-
}).then((buffer) => {
|
|
201
|
-
return buffer.toString("base64");
|
|
202
|
-
}).catch((error) => {
|
|
203
|
-
throw error;
|
|
204
|
-
});
|
|
205
|
-
const compositeElementInfoImgWithoutTextBase64 = await Jimp3.read(
|
|
206
|
-
imageBuffer
|
|
207
|
-
).then(async (image2) => {
|
|
208
|
-
const svgImage = await Jimp3.read(svgOverlayWithoutText);
|
|
209
|
-
return image2.composite(svgImage, 0, 0, {
|
|
210
|
-
mode: Jimp3.BLEND_SOURCE_OVER,
|
|
211
|
-
opacitySource: 1,
|
|
212
|
-
opacityDest: 1
|
|
213
|
-
});
|
|
214
|
-
}).then((compositeImage) => {
|
|
215
|
-
return compositeImage.getBufferAsync(Jimp3.MIME_PNG);
|
|
216
|
-
}).then((buffer) => {
|
|
217
|
-
return buffer.toString("base64");
|
|
218
|
-
}).catch((error) => {
|
|
219
|
-
throw error;
|
|
220
|
-
});
|
|
221
|
-
return {
|
|
222
|
-
compositeElementInfoImgBase64,
|
|
223
|
-
compositeElementInfoImgWithoutTextBase64
|
|
224
|
-
};
|
|
195
|
+
if (!width || !height) {
|
|
196
|
+
throw Error("Image processing failed because width or height is undefined");
|
|
225
197
|
}
|
|
226
|
-
|
|
198
|
+
const svgOverlay = await createSvgOverlay(
|
|
199
|
+
elementsPositionInfo,
|
|
200
|
+
width,
|
|
201
|
+
height
|
|
202
|
+
);
|
|
203
|
+
return await Jimp3.read(imageBuffer).then(async (image2) => {
|
|
204
|
+
const svgImage = await Jimp3.read(svgOverlay);
|
|
205
|
+
return image2.composite(svgImage, 0, 0, {
|
|
206
|
+
mode: Jimp3.BLEND_SOURCE_OVER,
|
|
207
|
+
opacitySource: 1,
|
|
208
|
+
opacityDest: 1
|
|
209
|
+
});
|
|
210
|
+
}).then((compositeImage) => {
|
|
211
|
+
return compositeImage.getBufferAsync(Jimp3.MIME_PNG);
|
|
212
|
+
}).then((buffer) => {
|
|
213
|
+
return buffer.toString("base64");
|
|
214
|
+
}).catch((error) => {
|
|
215
|
+
throw error;
|
|
216
|
+
});
|
|
217
|
+
};
|
|
218
|
+
var processImageElementInfo = async (options) => {
|
|
219
|
+
const base64Image = options.inputImgBase64.split(";base64,").pop();
|
|
220
|
+
assert2(base64Image, "base64Image is undefined");
|
|
221
|
+
const [
|
|
222
|
+
compositeElementInfoImgBase64,
|
|
223
|
+
compositeElementInfoImgWithoutTextBase64
|
|
224
|
+
] = await Promise.all([
|
|
225
|
+
compositeElementInfoImg({
|
|
226
|
+
inputImgBase64: options.inputImgBase64,
|
|
227
|
+
elementsPositionInfo: options.elementsPositionInfo
|
|
228
|
+
}),
|
|
229
|
+
compositeElementInfoImg({
|
|
230
|
+
inputImgBase64: options.inputImgBase64,
|
|
231
|
+
elementsPositionInfo: options.elementsPositionInfoWithoutText
|
|
232
|
+
})
|
|
233
|
+
]);
|
|
234
|
+
return {
|
|
235
|
+
compositeElementInfoImgBase64,
|
|
236
|
+
compositeElementInfoImgWithoutTextBase64
|
|
237
|
+
};
|
|
227
238
|
};
|
|
228
239
|
export {
|
|
229
240
|
base64Encoded,
|
|
241
|
+
base64ToPngFormat,
|
|
230
242
|
calculateNewDimensions,
|
|
243
|
+
compositeElementInfoImg,
|
|
231
244
|
imageInfo,
|
|
232
245
|
imageInfoOfBase64,
|
|
233
246
|
processImageElementInfo,
|
package/dist/lib/img.js
CHANGED
|
@@ -31,7 +31,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var img_exports = {};
|
|
32
32
|
__export(img_exports, {
|
|
33
33
|
base64Encoded: () => base64Encoded,
|
|
34
|
+
base64ToPngFormat: () => base64ToPngFormat,
|
|
34
35
|
calculateNewDimensions: () => calculateNewDimensions,
|
|
36
|
+
compositeElementInfoImg: () => compositeElementInfoImg,
|
|
35
37
|
imageInfo: () => imageInfo,
|
|
36
38
|
imageInfoOfBase64: () => imageInfoOfBase64,
|
|
37
39
|
processImageElementInfo: () => processImageElementInfo,
|
|
@@ -80,6 +82,9 @@ function base64Encoded(image, withHeader = true) {
|
|
|
80
82
|
}
|
|
81
83
|
throw new Error("unsupported image type");
|
|
82
84
|
}
|
|
85
|
+
function base64ToPngFormat(base64) {
|
|
86
|
+
return `data:image/png;base64,${base64}`;
|
|
87
|
+
}
|
|
83
88
|
|
|
84
89
|
// src/img/transform.ts
|
|
85
90
|
var import_node_buffer2 = require("buffer");
|
|
@@ -159,21 +164,34 @@ var createSvgOverlay = (elements, imageWidth, imageHeight) => {
|
|
|
159
164
|
const createPngOverlay = async (elements2, imageWidth2, imageHeight2) => {
|
|
160
165
|
const image = new import_jimp3.default(imageWidth2, imageHeight2, 0);
|
|
161
166
|
const colors = [
|
|
162
|
-
{ rect:
|
|
163
|
-
//
|
|
164
|
-
{ rect:
|
|
165
|
-
// brown, white
|
|
167
|
+
{ rect: 4278190335, text: 4294967295 }
|
|
168
|
+
// red, white
|
|
169
|
+
// { rect: 0x0000ffff, text: 0xffffffff }, // blue, white
|
|
170
|
+
// { rect: 0x8b4513ff, text: 0xffffffff }, // brown, white
|
|
166
171
|
];
|
|
172
|
+
const boxPadding = 5;
|
|
167
173
|
for (let index = 0; index < elements2.length; index++) {
|
|
168
174
|
const element = elements2[index];
|
|
169
175
|
const color = colors[index % colors.length];
|
|
176
|
+
const paddedRect = {
|
|
177
|
+
left: Math.max(0, element.rect.left - boxPadding),
|
|
178
|
+
top: Math.max(0, element.rect.top - boxPadding),
|
|
179
|
+
width: Math.min(
|
|
180
|
+
imageWidth2 - element.rect.left,
|
|
181
|
+
element.rect.width + boxPadding * 2
|
|
182
|
+
),
|
|
183
|
+
height: Math.min(
|
|
184
|
+
imageHeight2 - element.rect.top,
|
|
185
|
+
element.rect.height + boxPadding * 2
|
|
186
|
+
)
|
|
187
|
+
};
|
|
170
188
|
image.scan(
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
189
|
+
paddedRect.left,
|
|
190
|
+
paddedRect.top,
|
|
191
|
+
paddedRect.width,
|
|
192
|
+
paddedRect.height,
|
|
175
193
|
function(x, y, idx) {
|
|
176
|
-
if (x ===
|
|
194
|
+
if (x === paddedRect.left || x === paddedRect.left + paddedRect.width - 1 || y === paddedRect.top || y === paddedRect.top + paddedRect.height - 1) {
|
|
177
195
|
this.bitmap.data[idx + 0] = color.rect >> 24 & 255;
|
|
178
196
|
this.bitmap.data[idx + 1] = color.rect >> 16 & 255;
|
|
179
197
|
this.bitmap.data[idx + 2] = color.rect >> 8 & 255;
|
|
@@ -181,15 +199,15 @@ var createSvgOverlay = (elements, imageWidth, imageHeight) => {
|
|
|
181
199
|
}
|
|
182
200
|
}
|
|
183
201
|
);
|
|
184
|
-
const textWidth = element.
|
|
202
|
+
const textWidth = element.indexId.toString().length * 8;
|
|
185
203
|
const textHeight = 12;
|
|
186
204
|
const rectWidth = textWidth + 5;
|
|
187
205
|
const rectHeight = textHeight + 4;
|
|
188
|
-
let rectX =
|
|
189
|
-
let rectY =
|
|
206
|
+
let rectX = paddedRect.left - rectWidth;
|
|
207
|
+
let rectY = paddedRect.top + paddedRect.height / 2 - textHeight / 2 - 2;
|
|
190
208
|
if (rectX < 0) {
|
|
191
|
-
rectX =
|
|
192
|
-
rectY =
|
|
209
|
+
rectX = paddedRect.left;
|
|
210
|
+
rectY = paddedRect.top - rectHeight;
|
|
193
211
|
}
|
|
194
212
|
image.scan(rectX, rectY, rectWidth, rectHeight, function(x, y, idx) {
|
|
195
213
|
this.bitmap.data[idx + 0] = color.rect >> 24 & 255;
|
|
@@ -203,7 +221,7 @@ var createSvgOverlay = (elements, imageWidth, imageHeight) => {
|
|
|
203
221
|
rectX,
|
|
204
222
|
rectY,
|
|
205
223
|
{
|
|
206
|
-
text: element.
|
|
224
|
+
text: element.indexId.toString(),
|
|
207
225
|
alignmentX: import_jimp3.default.HORIZONTAL_ALIGN_CENTER,
|
|
208
226
|
alignmentY: import_jimp3.default.VERTICAL_ALIGN_MIDDLE
|
|
209
227
|
},
|
|
@@ -215,64 +233,61 @@ var createSvgOverlay = (elements, imageWidth, imageHeight) => {
|
|
|
215
233
|
};
|
|
216
234
|
return createPngOverlay(elements, imageWidth, imageHeight);
|
|
217
235
|
};
|
|
218
|
-
var
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
const imageBuffer = import_node_buffer3.Buffer.from(base64Image, "base64");
|
|
236
|
+
var compositeElementInfoImg = async (options) => {
|
|
237
|
+
const { inputImgBase64, elementsPositionInfo } = options;
|
|
238
|
+
const imageBuffer = import_node_buffer3.Buffer.from(inputImgBase64, "base64");
|
|
222
239
|
const image = await import_jimp3.default.read(imageBuffer);
|
|
223
240
|
const { width, height } = image.bitmap;
|
|
224
|
-
if (width
|
|
225
|
-
|
|
226
|
-
options.elementsPositionInfo,
|
|
227
|
-
width,
|
|
228
|
-
height
|
|
229
|
-
);
|
|
230
|
-
const svgOverlayWithoutText = await createSvgOverlay(
|
|
231
|
-
options.elementsPositionInfoWithoutText,
|
|
232
|
-
width,
|
|
233
|
-
height
|
|
234
|
-
);
|
|
235
|
-
const compositeElementInfoImgBase64 = await import_jimp3.default.read(imageBuffer).then(async (image2) => {
|
|
236
|
-
const svgImage = await import_jimp3.default.read(svgOverlay);
|
|
237
|
-
return image2.composite(svgImage, 0, 0, {
|
|
238
|
-
mode: import_jimp3.default.BLEND_SOURCE_OVER,
|
|
239
|
-
opacitySource: 1,
|
|
240
|
-
opacityDest: 1
|
|
241
|
-
});
|
|
242
|
-
}).then((compositeImage) => {
|
|
243
|
-
return compositeImage.getBufferAsync(import_jimp3.default.MIME_PNG);
|
|
244
|
-
}).then((buffer) => {
|
|
245
|
-
return buffer.toString("base64");
|
|
246
|
-
}).catch((error) => {
|
|
247
|
-
throw error;
|
|
248
|
-
});
|
|
249
|
-
const compositeElementInfoImgWithoutTextBase64 = await import_jimp3.default.read(
|
|
250
|
-
imageBuffer
|
|
251
|
-
).then(async (image2) => {
|
|
252
|
-
const svgImage = await import_jimp3.default.read(svgOverlayWithoutText);
|
|
253
|
-
return image2.composite(svgImage, 0, 0, {
|
|
254
|
-
mode: import_jimp3.default.BLEND_SOURCE_OVER,
|
|
255
|
-
opacitySource: 1,
|
|
256
|
-
opacityDest: 1
|
|
257
|
-
});
|
|
258
|
-
}).then((compositeImage) => {
|
|
259
|
-
return compositeImage.getBufferAsync(import_jimp3.default.MIME_PNG);
|
|
260
|
-
}).then((buffer) => {
|
|
261
|
-
return buffer.toString("base64");
|
|
262
|
-
}).catch((error) => {
|
|
263
|
-
throw error;
|
|
264
|
-
});
|
|
265
|
-
return {
|
|
266
|
-
compositeElementInfoImgBase64,
|
|
267
|
-
compositeElementInfoImgWithoutTextBase64
|
|
268
|
-
};
|
|
241
|
+
if (!width || !height) {
|
|
242
|
+
throw Error("Image processing failed because width or height is undefined");
|
|
269
243
|
}
|
|
270
|
-
|
|
244
|
+
const svgOverlay = await createSvgOverlay(
|
|
245
|
+
elementsPositionInfo,
|
|
246
|
+
width,
|
|
247
|
+
height
|
|
248
|
+
);
|
|
249
|
+
return await import_jimp3.default.read(imageBuffer).then(async (image2) => {
|
|
250
|
+
const svgImage = await import_jimp3.default.read(svgOverlay);
|
|
251
|
+
return image2.composite(svgImage, 0, 0, {
|
|
252
|
+
mode: import_jimp3.default.BLEND_SOURCE_OVER,
|
|
253
|
+
opacitySource: 1,
|
|
254
|
+
opacityDest: 1
|
|
255
|
+
});
|
|
256
|
+
}).then((compositeImage) => {
|
|
257
|
+
return compositeImage.getBufferAsync(import_jimp3.default.MIME_PNG);
|
|
258
|
+
}).then((buffer) => {
|
|
259
|
+
return buffer.toString("base64");
|
|
260
|
+
}).catch((error) => {
|
|
261
|
+
throw error;
|
|
262
|
+
});
|
|
263
|
+
};
|
|
264
|
+
var processImageElementInfo = async (options) => {
|
|
265
|
+
const base64Image = options.inputImgBase64.split(";base64,").pop();
|
|
266
|
+
(0, import_node_assert2.default)(base64Image, "base64Image is undefined");
|
|
267
|
+
const [
|
|
268
|
+
compositeElementInfoImgBase64,
|
|
269
|
+
compositeElementInfoImgWithoutTextBase64
|
|
270
|
+
] = await Promise.all([
|
|
271
|
+
compositeElementInfoImg({
|
|
272
|
+
inputImgBase64: options.inputImgBase64,
|
|
273
|
+
elementsPositionInfo: options.elementsPositionInfo
|
|
274
|
+
}),
|
|
275
|
+
compositeElementInfoImg({
|
|
276
|
+
inputImgBase64: options.inputImgBase64,
|
|
277
|
+
elementsPositionInfo: options.elementsPositionInfoWithoutText
|
|
278
|
+
})
|
|
279
|
+
]);
|
|
280
|
+
return {
|
|
281
|
+
compositeElementInfoImgBase64,
|
|
282
|
+
compositeElementInfoImgWithoutTextBase64
|
|
283
|
+
};
|
|
271
284
|
};
|
|
272
285
|
// Annotate the CommonJS export names for ESM import in node:
|
|
273
286
|
0 && (module.exports = {
|
|
274
287
|
base64Encoded,
|
|
288
|
+
base64ToPngFormat,
|
|
275
289
|
calculateNewDimensions,
|
|
290
|
+
compositeElementInfoImg,
|
|
276
291
|
imageInfo,
|
|
277
292
|
imageInfoOfBase64,
|
|
278
293
|
processImageElementInfo,
|
package/dist/types/img.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
2
|
import { NodeType } from './constants.js';
|
|
3
3
|
|
|
4
|
-
interface Size {
|
|
4
|
+
interface Size$1 {
|
|
5
5
|
width: number;
|
|
6
6
|
height: number;
|
|
7
7
|
}
|
|
@@ -12,7 +12,7 @@ interface Size {
|
|
|
12
12
|
* @returns A Promise that resolves to an object containing the width and height of the image
|
|
13
13
|
* @throws Error if the image data is invalid
|
|
14
14
|
*/
|
|
15
|
-
declare function imageInfo(image: string | Buffer): Promise<Size>;
|
|
15
|
+
declare function imageInfo(image: string | Buffer): Promise<Size$1>;
|
|
16
16
|
/**
|
|
17
17
|
* Retrieves the dimensions of an image from a base64-encoded string
|
|
18
18
|
*
|
|
@@ -20,7 +20,7 @@ declare function imageInfo(image: string | Buffer): Promise<Size>;
|
|
|
20
20
|
* @returns A Promise that resolves to an object containing the width and height of the image
|
|
21
21
|
* @throws Error if the image data is invalid
|
|
22
22
|
*/
|
|
23
|
-
declare function imageInfoOfBase64(imageBase64: string): Promise<Size>;
|
|
23
|
+
declare function imageInfoOfBase64(imageBase64: string): Promise<Size$1>;
|
|
24
24
|
/**
|
|
25
25
|
* Encodes an image file to a base64 encoded string
|
|
26
26
|
*
|
|
@@ -32,6 +32,7 @@ declare function imageInfoOfBase64(imageBase64: string): Promise<Size>;
|
|
|
32
32
|
* @throws When the image type is not supported, an error will be thrown
|
|
33
33
|
*/
|
|
34
34
|
declare function base64Encoded(image: string, withHeader?: boolean): string;
|
|
35
|
+
declare function base64ToPngFormat(base64: string): string;
|
|
35
36
|
|
|
36
37
|
/**
|
|
37
38
|
* Saves a Base64-encoded image to a file
|
|
@@ -91,17 +92,31 @@ declare function trimImage(image: string | Buffer): Promise<{
|
|
|
91
92
|
height: number;
|
|
92
93
|
} | null>;
|
|
93
94
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
interface Point {
|
|
96
|
+
left: number;
|
|
97
|
+
top: number;
|
|
98
|
+
}
|
|
99
|
+
interface Size {
|
|
97
100
|
width: number;
|
|
98
101
|
height: number;
|
|
99
|
-
|
|
102
|
+
}
|
|
103
|
+
type Rect = Point & Size;
|
|
104
|
+
|
|
105
|
+
type ElementType = {
|
|
106
|
+
locator: string;
|
|
107
|
+
rect: Rect;
|
|
108
|
+
center: [number, number];
|
|
109
|
+
id: string;
|
|
110
|
+
indexId: number;
|
|
100
111
|
attributes: {
|
|
101
|
-
[key: string]: string;
|
|
102
112
|
nodeType: NodeType;
|
|
113
|
+
[key: string]: string;
|
|
103
114
|
};
|
|
104
115
|
};
|
|
116
|
+
declare const compositeElementInfoImg: (options: {
|
|
117
|
+
inputImgBase64: string;
|
|
118
|
+
elementsPositionInfo: Array<ElementType>;
|
|
119
|
+
}) => Promise<string>;
|
|
105
120
|
declare const processImageElementInfo: (options: {
|
|
106
121
|
inputImgBase64: string;
|
|
107
122
|
elementsPositionInfo: Array<ElementType>;
|
|
@@ -111,4 +126,4 @@ declare const processImageElementInfo: (options: {
|
|
|
111
126
|
compositeElementInfoImgWithoutTextBase64: string;
|
|
112
127
|
}>;
|
|
113
128
|
|
|
114
|
-
export { base64Encoded, calculateNewDimensions, imageInfo, imageInfoOfBase64, processImageElementInfo, resizeImg, saveBase64Image, transformImgPathToBase64, trimImage };
|
|
129
|
+
export { base64Encoded, base64ToPngFormat, calculateNewDimensions, compositeElementInfoImg, imageInfo, imageInfoOfBase64, processImageElementInfo, resizeImg, saveBase64Image, transformImgPathToBase64, trimImage };
|
package/package.json
CHANGED
package/src/img/box-select.ts
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import assert from 'node:assert';
|
|
2
2
|
import { Buffer } from 'node:buffer';
|
|
3
|
+
import type { Rect } from '@/types';
|
|
3
4
|
import Jimp from 'jimp';
|
|
4
5
|
import type { NodeType } from '../constants';
|
|
5
6
|
|
|
6
7
|
// Define picture path
|
|
7
8
|
type ElementType = {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
locator: string;
|
|
10
|
+
|
|
11
|
+
rect: Rect;
|
|
12
|
+
|
|
13
|
+
center: [number, number];
|
|
14
|
+
|
|
15
|
+
id: string;
|
|
16
|
+
|
|
17
|
+
indexId: number;
|
|
18
|
+
|
|
13
19
|
attributes: {
|
|
14
|
-
[key: string]: string;
|
|
15
20
|
nodeType: NodeType;
|
|
21
|
+
[key: string]: string;
|
|
16
22
|
};
|
|
17
23
|
};
|
|
18
24
|
|
|
@@ -30,26 +36,42 @@ const createSvgOverlay = (
|
|
|
30
36
|
|
|
31
37
|
// Define color array
|
|
32
38
|
const colors = [
|
|
33
|
-
{ rect:
|
|
34
|
-
{ rect:
|
|
39
|
+
{ rect: 0xff0000ff, text: 0xffffffff }, // red, white
|
|
40
|
+
// { rect: 0x0000ffff, text: 0xffffffff }, // blue, white
|
|
41
|
+
// { rect: 0x8b4513ff, text: 0xffffffff }, // brown, white
|
|
35
42
|
];
|
|
36
43
|
|
|
44
|
+
const boxPadding = 5;
|
|
37
45
|
for (let index = 0; index < elements.length; index++) {
|
|
38
46
|
const element = elements[index];
|
|
39
47
|
const color = colors[index % colors.length];
|
|
40
48
|
|
|
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
|
+
};
|
|
62
|
+
|
|
41
63
|
// Draw rectangle
|
|
42
64
|
image.scan(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
65
|
+
paddedRect.left,
|
|
66
|
+
paddedRect.top,
|
|
67
|
+
paddedRect.width,
|
|
68
|
+
paddedRect.height,
|
|
47
69
|
function (x, y, idx) {
|
|
48
70
|
if (
|
|
49
|
-
x ===
|
|
50
|
-
x ===
|
|
51
|
-
y ===
|
|
52
|
-
y ===
|
|
71
|
+
x === paddedRect.left ||
|
|
72
|
+
x === paddedRect.left + paddedRect.width - 1 ||
|
|
73
|
+
y === paddedRect.top ||
|
|
74
|
+
y === paddedRect.top + paddedRect.height - 1
|
|
53
75
|
) {
|
|
54
76
|
this.bitmap.data[idx + 0] = (color.rect >> 24) & 0xff; // R
|
|
55
77
|
this.bitmap.data[idx + 1] = (color.rect >> 16) & 0xff; // G
|
|
@@ -60,17 +82,17 @@ const createSvgOverlay = (
|
|
|
60
82
|
);
|
|
61
83
|
|
|
62
84
|
// Calculate text position
|
|
63
|
-
const textWidth = element.
|
|
85
|
+
const textWidth = element.indexId.toString().length * 8;
|
|
64
86
|
const textHeight = 12;
|
|
65
87
|
const rectWidth = textWidth + 5;
|
|
66
88
|
const rectHeight = textHeight + 4;
|
|
67
|
-
let rectX =
|
|
68
|
-
let rectY =
|
|
89
|
+
let rectX = paddedRect.left - rectWidth;
|
|
90
|
+
let rectY = paddedRect.top + paddedRect.height / 2 - textHeight / 2 - 2;
|
|
69
91
|
|
|
70
92
|
// Check if obscured by the left
|
|
71
93
|
if (rectX < 0) {
|
|
72
|
-
rectX =
|
|
73
|
-
rectY =
|
|
94
|
+
rectX = paddedRect.left;
|
|
95
|
+
rectY = paddedRect.top - rectHeight;
|
|
74
96
|
}
|
|
75
97
|
|
|
76
98
|
// Draw text background
|
|
@@ -87,7 +109,7 @@ const createSvgOverlay = (
|
|
|
87
109
|
rectX,
|
|
88
110
|
rectY,
|
|
89
111
|
{
|
|
90
|
-
text: element.
|
|
112
|
+
text: element.indexId.toString(),
|
|
91
113
|
alignmentX: Jimp.HORIZONTAL_ALIGN_CENTER,
|
|
92
114
|
alignmentY: Jimp.VERTICAL_ALIGN_MIDDLE,
|
|
93
115
|
},
|
|
@@ -102,78 +124,71 @@ const createSvgOverlay = (
|
|
|
102
124
|
return createPngOverlay(elements, imageWidth, imageHeight);
|
|
103
125
|
};
|
|
104
126
|
|
|
105
|
-
export const
|
|
127
|
+
export const compositeElementInfoImg = async (options: {
|
|
106
128
|
inputImgBase64: string;
|
|
107
129
|
elementsPositionInfo: Array<ElementType>;
|
|
108
|
-
elementsPositionInfoWithoutText: Array<ElementType>;
|
|
109
130
|
}) => {
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
assert(base64Image, 'base64Image is undefined');
|
|
113
|
-
|
|
114
|
-
const imageBuffer = Buffer.from(base64Image, 'base64');
|
|
131
|
+
const { inputImgBase64, elementsPositionInfo } = options;
|
|
132
|
+
const imageBuffer = Buffer.from(inputImgBase64, 'base64');
|
|
115
133
|
const image = await Jimp.read(imageBuffer);
|
|
116
134
|
const { width, height } = image.bitmap;
|
|
117
135
|
|
|
118
|
-
if (width
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
options.elementsPositionInfo,
|
|
122
|
-
width,
|
|
123
|
-
height,
|
|
124
|
-
);
|
|
125
|
-
const svgOverlayWithoutText = await createSvgOverlay(
|
|
126
|
-
options.elementsPositionInfoWithoutText,
|
|
127
|
-
width,
|
|
128
|
-
height,
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
// Composite picture
|
|
132
|
-
const compositeElementInfoImgBase64 = await Jimp.read(imageBuffer)
|
|
133
|
-
.then(async (image: Jimp) => {
|
|
134
|
-
const svgImage = await Jimp.read(svgOverlay);
|
|
135
|
-
return image.composite(svgImage, 0, 0, {
|
|
136
|
-
mode: Jimp.BLEND_SOURCE_OVER,
|
|
137
|
-
opacitySource: 1,
|
|
138
|
-
opacityDest: 1,
|
|
139
|
-
});
|
|
140
|
-
})
|
|
141
|
-
.then((compositeImage: Jimp) => {
|
|
142
|
-
return compositeImage.getBufferAsync(Jimp.MIME_PNG);
|
|
143
|
-
})
|
|
144
|
-
.then((buffer: Buffer) => {
|
|
145
|
-
return buffer.toString('base64');
|
|
146
|
-
})
|
|
147
|
-
.catch((error: unknown) => {
|
|
148
|
-
throw error;
|
|
149
|
-
});
|
|
136
|
+
if (!width || !height) {
|
|
137
|
+
throw Error('Image processing failed because width or height is undefined');
|
|
138
|
+
}
|
|
150
139
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
})
|
|
166
|
-
.then((buffer: Buffer) => {
|
|
167
|
-
return buffer.toString('base64');
|
|
168
|
-
})
|
|
169
|
-
.catch((error: unknown) => {
|
|
170
|
-
throw error;
|
|
140
|
+
// Create svg overlay
|
|
141
|
+
const svgOverlay = await createSvgOverlay(
|
|
142
|
+
elementsPositionInfo,
|
|
143
|
+
width,
|
|
144
|
+
height,
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
return await Jimp.read(imageBuffer)
|
|
148
|
+
.then(async (image: Jimp) => {
|
|
149
|
+
const svgImage = await Jimp.read(svgOverlay);
|
|
150
|
+
return image.composite(svgImage, 0, 0, {
|
|
151
|
+
mode: Jimp.BLEND_SOURCE_OVER,
|
|
152
|
+
opacitySource: 1,
|
|
153
|
+
opacityDest: 1,
|
|
171
154
|
});
|
|
155
|
+
})
|
|
156
|
+
.then((compositeImage: Jimp) => {
|
|
157
|
+
return compositeImage.getBufferAsync(Jimp.MIME_PNG);
|
|
158
|
+
})
|
|
159
|
+
.then((buffer: Buffer) => {
|
|
160
|
+
return buffer.toString('base64');
|
|
161
|
+
})
|
|
162
|
+
.catch((error: unknown) => {
|
|
163
|
+
throw error;
|
|
164
|
+
});
|
|
165
|
+
};
|
|
172
166
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
167
|
+
export const processImageElementInfo = async (options: {
|
|
168
|
+
inputImgBase64: string;
|
|
169
|
+
elementsPositionInfo: Array<ElementType>;
|
|
170
|
+
elementsPositionInfoWithoutText: Array<ElementType>;
|
|
171
|
+
}) => {
|
|
172
|
+
// Get the size of the original image
|
|
173
|
+
const base64Image = options.inputImgBase64.split(';base64,').pop();
|
|
174
|
+
assert(base64Image, 'base64Image is undefined');
|
|
175
|
+
|
|
176
|
+
const [
|
|
177
|
+
compositeElementInfoImgBase64,
|
|
178
|
+
compositeElementInfoImgWithoutTextBase64,
|
|
179
|
+
] = await Promise.all([
|
|
180
|
+
compositeElementInfoImg({
|
|
181
|
+
inputImgBase64: options.inputImgBase64,
|
|
182
|
+
elementsPositionInfo: options.elementsPositionInfo,
|
|
183
|
+
}),
|
|
184
|
+
compositeElementInfoImg({
|
|
185
|
+
inputImgBase64: options.inputImgBase64,
|
|
186
|
+
elementsPositionInfo: options.elementsPositionInfoWithoutText,
|
|
187
|
+
}),
|
|
188
|
+
]);
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
compositeElementInfoImgBase64,
|
|
192
|
+
compositeElementInfoImgWithoutTextBase64,
|
|
193
|
+
};
|
|
179
194
|
};
|
package/src/img/index.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {
|
|
2
|
+
imageInfo,
|
|
3
|
+
imageInfoOfBase64,
|
|
4
|
+
base64Encoded,
|
|
5
|
+
base64ToPngFormat,
|
|
6
|
+
} from './info';
|
|
2
7
|
export {
|
|
3
8
|
trimImage,
|
|
4
9
|
calculateNewDimensions,
|
|
@@ -6,4 +11,4 @@ export {
|
|
|
6
11
|
transformImgPathToBase64,
|
|
7
12
|
saveBase64Image,
|
|
8
13
|
} from './transform';
|
|
9
|
-
export { processImageElementInfo } from './box-select';
|
|
14
|
+
export { processImageElementInfo, compositeElementInfoImg } from './box-select';
|