docxmlater 5.0.0 → 5.1.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 +1 -1
- package/dist/core/Document.d.ts.map +1 -1
- package/dist/core/Document.js +23 -1
- package/dist/core/Document.js.map +1 -1
- package/dist/core/DocumentParser.d.ts.map +1 -1
- package/dist/core/DocumentParser.js +3 -2
- package/dist/core/DocumentParser.js.map +1 -1
- package/dist/elements/Footer.d.ts +3 -0
- package/dist/elements/Footer.d.ts.map +1 -1
- package/dist/elements/Footer.js +11 -0
- package/dist/elements/Footer.js.map +1 -1
- package/dist/elements/Header.d.ts +3 -0
- package/dist/elements/Header.d.ts.map +1 -1
- package/dist/elements/Header.js +11 -0
- package/dist/elements/Header.js.map +1 -1
- package/dist/elements/Image.d.ts +21 -18
- package/dist/elements/Image.d.ts.map +1 -1
- package/dist/elements/Image.js +377 -636
- package/dist/elements/Image.js.map +1 -1
- package/dist/elements/TableCell.d.ts +2 -1
- package/dist/elements/TableCell.d.ts.map +1 -1
- package/dist/elements/TableCell.js +8 -1
- package/dist/elements/TableCell.js.map +1 -1
- package/dist/xml/XMLBuilder.d.ts +7 -0
- package/dist/xml/XMLBuilder.d.ts.map +1 -1
- package/dist/xml/XMLBuilder.js +49 -0
- package/dist/xml/XMLBuilder.js.map +1 -1
- package/package.json +1 -1
package/dist/elements/Image.js
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Image = void 0;
|
|
4
4
|
const fs_1 = require("fs");
|
|
5
|
-
const
|
|
5
|
+
const logger_1 = require("../utils/logger");
|
|
6
6
|
const units_1 = require("../utils/units");
|
|
7
|
+
const XMLBuilder_1 = require("../xml/XMLBuilder");
|
|
7
8
|
class Image {
|
|
8
9
|
source;
|
|
9
10
|
width;
|
|
@@ -14,12 +15,36 @@ class Image {
|
|
|
14
15
|
imageData;
|
|
15
16
|
extension;
|
|
16
17
|
docPrId = 1;
|
|
18
|
+
dpi = 96;
|
|
17
19
|
effectExtent;
|
|
18
20
|
wrap;
|
|
19
21
|
position;
|
|
20
22
|
anchor;
|
|
21
23
|
crop;
|
|
22
24
|
effects;
|
|
25
|
+
rotation = 0;
|
|
26
|
+
border;
|
|
27
|
+
static async fromFile(path, properties = {}) {
|
|
28
|
+
const image = new Image({ source: path, ...properties });
|
|
29
|
+
await image.loadImageDataForDimensions();
|
|
30
|
+
return image;
|
|
31
|
+
}
|
|
32
|
+
static async fromBuffer(buffer, properties = {}) {
|
|
33
|
+
const image = new Image({ source: buffer, ...properties });
|
|
34
|
+
await image.loadImageDataForDimensions();
|
|
35
|
+
return image;
|
|
36
|
+
}
|
|
37
|
+
static async create(properties) {
|
|
38
|
+
if (Buffer.isBuffer(properties.source)) {
|
|
39
|
+
return Image.fromBuffer(properties.source, properties);
|
|
40
|
+
}
|
|
41
|
+
else if (typeof properties.source === 'string') {
|
|
42
|
+
return Image.fromFile(properties.source, properties);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
throw new Error('Invalid source: must be file path or Buffer');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
23
48
|
constructor(properties) {
|
|
24
49
|
this.source = properties.source;
|
|
25
50
|
this.description = properties.description || 'Image';
|
|
@@ -34,55 +59,86 @@ class Image {
|
|
|
34
59
|
this.anchor = properties.anchor;
|
|
35
60
|
this.crop = properties.crop;
|
|
36
61
|
this.effects = properties.effects;
|
|
62
|
+
this.dpi = 96;
|
|
37
63
|
}
|
|
38
64
|
async loadImageDataForDimensions() {
|
|
39
65
|
let tempData;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
66
|
+
try {
|
|
67
|
+
if (Buffer.isBuffer(this.source)) {
|
|
68
|
+
tempData = this.source;
|
|
69
|
+
}
|
|
70
|
+
else if (typeof this.source === 'string') {
|
|
45
71
|
await fs_1.promises.access(this.source);
|
|
46
72
|
tempData = await fs_1.promises.readFile(this.source);
|
|
47
73
|
}
|
|
48
|
-
|
|
49
|
-
|
|
74
|
+
if (tempData) {
|
|
75
|
+
this.imageData = tempData;
|
|
76
|
+
const dimensions = this.detectDimensions();
|
|
77
|
+
if (dimensions) {
|
|
78
|
+
this.dpi = this.detectDPI() || 96;
|
|
79
|
+
const emuPerInch = 914400;
|
|
80
|
+
const pixelsPerInch = this.dpi;
|
|
81
|
+
this.width = Math.round((dimensions.width / pixelsPerInch) * emuPerInch);
|
|
82
|
+
this.height = Math.round((dimensions.height / pixelsPerInch) * emuPerInch);
|
|
83
|
+
}
|
|
84
|
+
if (typeof this.source === 'string') {
|
|
85
|
+
this.imageData = undefined;
|
|
86
|
+
}
|
|
50
87
|
}
|
|
51
88
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (dimensions) {
|
|
56
|
-
this.width = dimensions.width;
|
|
57
|
-
this.height = dimensions.height;
|
|
58
|
-
}
|
|
59
|
-
if (typeof this.source === 'string') {
|
|
60
|
-
this.imageData = undefined;
|
|
61
|
-
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
logger_1.defaultLogger.error(`Failed to load image for dimensions: ${error instanceof Error ? error.message : String(error)}`);
|
|
91
|
+
throw new Error(`Image loading failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
62
92
|
}
|
|
63
93
|
}
|
|
64
94
|
async ensureDataLoaded() {
|
|
65
|
-
if (this.imageData)
|
|
95
|
+
if (this.imageData)
|
|
66
96
|
return;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
try {
|
|
97
|
+
try {
|
|
98
|
+
if (Buffer.isBuffer(this.source)) {
|
|
99
|
+
this.imageData = this.source;
|
|
100
|
+
}
|
|
101
|
+
else if (typeof this.source === 'string') {
|
|
73
102
|
await fs_1.promises.access(this.source);
|
|
74
103
|
this.imageData = await fs_1.promises.readFile(this.source);
|
|
75
104
|
}
|
|
76
|
-
|
|
77
|
-
throw new Error(
|
|
105
|
+
else {
|
|
106
|
+
throw new Error('Invalid image source');
|
|
78
107
|
}
|
|
79
108
|
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
logger_1.defaultLogger.error(`Failed to load image data: ${error instanceof Error ? error.message : String(error)}`);
|
|
111
|
+
throw new Error(`Image data loading failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
112
|
+
}
|
|
80
113
|
}
|
|
81
114
|
releaseData() {
|
|
82
115
|
if (typeof this.source === 'string') {
|
|
83
116
|
this.imageData = undefined;
|
|
84
117
|
}
|
|
85
118
|
}
|
|
119
|
+
validateImageData() {
|
|
120
|
+
if (!this.imageData || this.imageData.length === 0) {
|
|
121
|
+
return { valid: false, error: 'Empty image data' };
|
|
122
|
+
}
|
|
123
|
+
const signatures = {
|
|
124
|
+
png: [0x89, 0x50, 0x4E, 0x47],
|
|
125
|
+
jpg: [0xFF, 0xD8],
|
|
126
|
+
jpeg: [0xFF, 0xD8],
|
|
127
|
+
gif: [0x47, 0x49, 0x46],
|
|
128
|
+
bmp: [0x42, 0x4D],
|
|
129
|
+
tiff: [0x49, 0x49, 0x2A, 0x00],
|
|
130
|
+
tif: [0x49, 0x49, 0x2A, 0x00]
|
|
131
|
+
};
|
|
132
|
+
const sig = signatures[this.extension];
|
|
133
|
+
if (sig) {
|
|
134
|
+
for (let i = 0; i < sig.length; i++) {
|
|
135
|
+
if (this.imageData[i] !== sig[i]) {
|
|
136
|
+
return { valid: false, error: `Invalid ${this.extension.toUpperCase()} signature` };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return { valid: true };
|
|
141
|
+
}
|
|
86
142
|
detectExtension() {
|
|
87
143
|
if (typeof this.source === 'string') {
|
|
88
144
|
const match = this.source.match(/\.([a-z]+)$/i);
|
|
@@ -93,225 +149,138 @@ class Image {
|
|
|
93
149
|
return 'png';
|
|
94
150
|
}
|
|
95
151
|
detectDimensions() {
|
|
96
|
-
if (!this.imageData || this.imageData.length < 24)
|
|
152
|
+
if (!this.imageData || this.imageData.length < 24)
|
|
97
153
|
return null;
|
|
154
|
+
if (this.imageData[0] === 0x89 && this.imageData[1] === 0x50 && this.imageData[2] === 0x4e && this.imageData[3] === 0x47) {
|
|
155
|
+
return this.detectPngDimensions();
|
|
98
156
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
this.imageData[1] === 0x50 &&
|
|
102
|
-
this.imageData[2] === 0x4e &&
|
|
103
|
-
this.imageData[3] === 0x47) {
|
|
104
|
-
return this.detectPngDimensions();
|
|
105
|
-
}
|
|
106
|
-
if (this.imageData[0] === 0xff && this.imageData[1] === 0xd8) {
|
|
107
|
-
return this.detectJpegDimensions();
|
|
108
|
-
}
|
|
109
|
-
if (this.imageData[0] === 0x47 &&
|
|
110
|
-
this.imageData[1] === 0x49 &&
|
|
111
|
-
this.imageData[2] === 0x46) {
|
|
112
|
-
return this.detectGifDimensions();
|
|
113
|
-
}
|
|
114
|
-
if (this.imageData[0] === 0x42 && this.imageData[1] === 0x4d) {
|
|
115
|
-
return this.detectBmpDimensions();
|
|
116
|
-
}
|
|
117
|
-
if ((this.imageData[0] === 0x49 && this.imageData[1] === 0x49 && this.imageData[2] === 0x2a) ||
|
|
118
|
-
(this.imageData[0] === 0x4d && this.imageData[1] === 0x4d && this.imageData[2] === 0x00)) {
|
|
119
|
-
return this.detectTiffDimensions();
|
|
120
|
-
}
|
|
157
|
+
if (this.imageData[0] === 0xff && this.imageData[1] === 0xd8) {
|
|
158
|
+
return this.detectJpegDimensions();
|
|
121
159
|
}
|
|
122
|
-
|
|
160
|
+
if (this.imageData[0] === 0x47 && this.imageData[1] === 0x49 && this.imageData[2] === 0x46) {
|
|
161
|
+
return this.detectGifDimensions();
|
|
162
|
+
}
|
|
163
|
+
if (this.imageData[0] === 0x42 && this.imageData[1] === 0x4d) {
|
|
164
|
+
return this.detectBmpDimensions();
|
|
165
|
+
}
|
|
166
|
+
if ((this.imageData[0] === 0x49 && this.imageData[1] === 0x49 && this.imageData[2] === 0x2a) ||
|
|
167
|
+
(this.imageData[0] === 0x4d && this.imageData[1] === 0x4d && this.imageData[2] === 0x00)) {
|
|
168
|
+
return this.detectTiffDimensions();
|
|
123
169
|
}
|
|
124
170
|
return null;
|
|
125
171
|
}
|
|
126
172
|
detectPngDimensions() {
|
|
127
|
-
if (!this.imageData || this.imageData.length < 24)
|
|
128
|
-
return null;
|
|
129
|
-
}
|
|
130
|
-
try {
|
|
131
|
-
const width = this.imageData.readUInt32BE(16);
|
|
132
|
-
const height = this.imageData.readUInt32BE(20);
|
|
133
|
-
return {
|
|
134
|
-
width: Math.round((width / 96) * 914400),
|
|
135
|
-
height: Math.round((height / 96) * 914400),
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
catch (error) {
|
|
173
|
+
if (!this.imageData || this.imageData.length < 24)
|
|
139
174
|
return null;
|
|
140
|
-
|
|
175
|
+
const width = this.imageData.readUInt32BE(16);
|
|
176
|
+
const height = this.imageData.readUInt32BE(20);
|
|
177
|
+
return { width, height };
|
|
141
178
|
}
|
|
142
179
|
detectGifDimensions() {
|
|
143
|
-
if (!this.imageData || this.imageData.length < 10)
|
|
180
|
+
if (!this.imageData || this.imageData.length < 10)
|
|
144
181
|
return null;
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if (width > 0 && height > 0 && width < 65535 && height < 65535) {
|
|
150
|
-
return {
|
|
151
|
-
width: Math.round((width / 96) * 914400),
|
|
152
|
-
height: Math.round((height / 96) * 914400),
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
catch (error) {
|
|
157
|
-
}
|
|
182
|
+
const width = this.imageData.readUInt16LE(6);
|
|
183
|
+
const height = this.imageData.readUInt16LE(8);
|
|
184
|
+
if (width > 0 && height > 0)
|
|
185
|
+
return { width, height };
|
|
158
186
|
return null;
|
|
159
187
|
}
|
|
160
188
|
detectBmpDimensions() {
|
|
161
|
-
if (!this.imageData || this.imageData.length < 26)
|
|
189
|
+
if (!this.imageData || this.imageData.length < 26)
|
|
162
190
|
return null;
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if (width > 0 && height > 0 && width < 65535 && height < 65535) {
|
|
168
|
-
return {
|
|
169
|
-
width: Math.round((width / 96) * 914400),
|
|
170
|
-
height: Math.round((height / 96) * 914400),
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
catch (error) {
|
|
175
|
-
}
|
|
191
|
+
const width = this.imageData.readInt32LE(18);
|
|
192
|
+
const height = Math.abs(this.imageData.readInt32LE(22));
|
|
193
|
+
if (width > 0 && height > 0)
|
|
194
|
+
return { width, height };
|
|
176
195
|
return null;
|
|
177
196
|
}
|
|
178
197
|
detectTiffDimensions() {
|
|
179
|
-
if (!this.imageData || this.imageData.length < 14)
|
|
198
|
+
if (!this.imageData || this.imageData.length < 14)
|
|
180
199
|
return null;
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const value = isLittleEndian
|
|
204
|
-
? this.imageData.readUInt32LE(entryOffset + 8)
|
|
205
|
-
: this.imageData.readUInt32BE(entryOffset + 8);
|
|
206
|
-
if (tag === 256 || tag === 0x100) {
|
|
207
|
-
width = value;
|
|
208
|
-
}
|
|
209
|
-
else if (tag === 257 || tag === 0x101) {
|
|
210
|
-
height = value;
|
|
211
|
-
}
|
|
212
|
-
if (width > 0 && height > 0) {
|
|
213
|
-
break;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
if (width > 0 && height > 0 && width < 65535 && height < 65535) {
|
|
217
|
-
return {
|
|
218
|
-
width: Math.round((width / 96) * 914400),
|
|
219
|
-
height: Math.round((height / 96) * 914400),
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
catch (error) {
|
|
224
|
-
}
|
|
200
|
+
const isLittleEndian = this.imageData[0] === 0x49;
|
|
201
|
+
const ifdOffset = isLittleEndian ? this.imageData.readUInt32LE(4) : this.imageData.readUInt32BE(4);
|
|
202
|
+
if (ifdOffset + 14 > this.imageData.length)
|
|
203
|
+
return null;
|
|
204
|
+
const numEntries = isLittleEndian ? this.imageData.readUInt16LE(ifdOffset) : this.imageData.readUInt16BE(ifdOffset);
|
|
205
|
+
let width = 0;
|
|
206
|
+
let height = 0;
|
|
207
|
+
for (let i = 0; i < numEntries; i++) {
|
|
208
|
+
const entryOffset = ifdOffset + 2 + i * 12;
|
|
209
|
+
if (entryOffset + 12 > this.imageData.length)
|
|
210
|
+
break;
|
|
211
|
+
const tag = isLittleEndian ? this.imageData.readUInt16LE(entryOffset) : this.imageData.readUInt16BE(entryOffset);
|
|
212
|
+
const value = isLittleEndian ? this.imageData.readUInt32LE(entryOffset + 8) : this.imageData.readUInt32BE(entryOffset + 8);
|
|
213
|
+
if (tag === 256)
|
|
214
|
+
width = value;
|
|
215
|
+
if (tag === 257)
|
|
216
|
+
height = value;
|
|
217
|
+
if (width > 0 && height > 0)
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
if (width > 0 && height > 0)
|
|
221
|
+
return { width, height };
|
|
225
222
|
return null;
|
|
226
223
|
}
|
|
227
224
|
detectJpegDimensions() {
|
|
228
|
-
if (!this.imageData || this.imageData.length < 12)
|
|
225
|
+
if (!this.imageData || this.imageData.length < 12)
|
|
229
226
|
return null;
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (this.imageData[
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
if (marker === 0x00 || marker === 0xff) {
|
|
245
|
-
offset++;
|
|
246
|
-
continue;
|
|
247
|
-
}
|
|
248
|
-
const isSOF = (marker >= 0xc0 && marker <= 0xc3) ||
|
|
249
|
-
(marker >= 0xc5 && marker <= 0xc7) ||
|
|
250
|
-
(marker >= 0xc9 && marker <= 0xcb) ||
|
|
251
|
-
(marker >= 0xcd && marker <= 0xcf);
|
|
252
|
-
if (isSOF) {
|
|
253
|
-
if (offset + 9 > this.imageData.length) {
|
|
254
|
-
break;
|
|
255
|
-
}
|
|
256
|
-
const height = this.imageData.readUInt16BE(offset + 5);
|
|
257
|
-
const width = this.imageData.readUInt16BE(offset + 7);
|
|
258
|
-
if (width > 0 && height > 0 && width < 65535 && height < 65535) {
|
|
259
|
-
return {
|
|
260
|
-
width: Math.round((width / 96) * 914400),
|
|
261
|
-
height: Math.round((height / 96) * 914400),
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
if (marker === 0xda) {
|
|
266
|
-
break;
|
|
267
|
-
}
|
|
268
|
-
if (marker === 0xd9) {
|
|
269
|
-
break;
|
|
270
|
-
}
|
|
271
|
-
const standaloneMarker = (marker >= 0xd0 && marker <= 0xd9) || marker === 0x01;
|
|
272
|
-
if (standaloneMarker) {
|
|
273
|
-
offset += 2;
|
|
274
|
-
continue;
|
|
275
|
-
}
|
|
276
|
-
if (offset + 3 > this.imageData.length) {
|
|
277
|
-
break;
|
|
278
|
-
}
|
|
279
|
-
const segmentLength = this.imageData.readUInt16BE(offset + 2);
|
|
280
|
-
if (segmentLength < 2 || offset + 2 + segmentLength > this.imageData.length) {
|
|
227
|
+
let offset = 2;
|
|
228
|
+
while (offset < this.imageData.length - 1) {
|
|
229
|
+
if (this.imageData[offset] !== 0xff)
|
|
230
|
+
break;
|
|
231
|
+
const marker = this.imageData[offset + 1];
|
|
232
|
+
if (marker === undefined)
|
|
233
|
+
break;
|
|
234
|
+
if (marker === 0x00 || marker === 0xff) {
|
|
235
|
+
offset++;
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
const isSOF = (marker >= 0xc0 && marker <= 0xcf) && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc;
|
|
239
|
+
if (isSOF) {
|
|
240
|
+
if (offset + 9 > this.imageData.length)
|
|
281
241
|
break;
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
242
|
+
const height = this.imageData.readUInt16BE(offset + 5);
|
|
243
|
+
const width = this.imageData.readUInt16BE(offset + 7);
|
|
244
|
+
if (width > 0 && height > 0)
|
|
245
|
+
return { width, height };
|
|
246
|
+
}
|
|
247
|
+
if (marker === 0xda || marker === 0xd9)
|
|
248
|
+
break;
|
|
249
|
+
const segmentLength = this.imageData.readUInt16BE(offset + 2);
|
|
250
|
+
if (segmentLength < 2 || offset + 2 + segmentLength > this.imageData.length)
|
|
251
|
+
break;
|
|
252
|
+
offset += 2 + segmentLength;
|
|
287
253
|
}
|
|
288
254
|
return null;
|
|
289
255
|
}
|
|
290
256
|
async getImageDataAsync() {
|
|
291
257
|
await this.ensureDataLoaded();
|
|
292
|
-
if (this.imageData)
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
throw new Error('Failed to load image data');
|
|
258
|
+
if (!this.imageData)
|
|
259
|
+
throw new Error('Failed to load image data');
|
|
260
|
+
return this.imageData;
|
|
296
261
|
}
|
|
297
262
|
getImageData() {
|
|
298
|
-
if (!this.imageData)
|
|
299
|
-
throw new Error('Image data not loaded. '
|
|
300
|
-
'Call await image.ensureDataLoaded() or await imageManager.loadAllImageData() first.');
|
|
301
|
-
}
|
|
263
|
+
if (!this.imageData)
|
|
264
|
+
throw new Error('Image data not loaded. Call ensureDataLoaded first.');
|
|
302
265
|
return this.imageData;
|
|
303
266
|
}
|
|
304
267
|
getExtension() {
|
|
305
268
|
return this.extension;
|
|
306
269
|
}
|
|
270
|
+
getDPI() {
|
|
271
|
+
return this.dpi;
|
|
272
|
+
}
|
|
307
273
|
getWidth() {
|
|
308
274
|
return this.width;
|
|
309
275
|
}
|
|
310
276
|
getHeight() {
|
|
311
277
|
return this.height;
|
|
312
278
|
}
|
|
279
|
+
getImageDataSafe() {
|
|
280
|
+
return this.imageData ?? null;
|
|
281
|
+
}
|
|
313
282
|
setWidth(width, maintainAspectRatio = true) {
|
|
314
|
-
if (maintainAspectRatio) {
|
|
283
|
+
if (maintainAspectRatio && this.height > 0) {
|
|
315
284
|
const ratio = this.height / this.width;
|
|
316
285
|
this.height = Math.round(width * ratio);
|
|
317
286
|
}
|
|
@@ -319,7 +288,7 @@ class Image {
|
|
|
319
288
|
return this;
|
|
320
289
|
}
|
|
321
290
|
setHeight(height, maintainAspectRatio = true) {
|
|
322
|
-
if (maintainAspectRatio) {
|
|
291
|
+
if (maintainAspectRatio && this.width > 0) {
|
|
323
292
|
const ratio = this.width / this.height;
|
|
324
293
|
this.width = Math.round(height * ratio);
|
|
325
294
|
}
|
|
@@ -331,6 +300,14 @@ class Image {
|
|
|
331
300
|
this.height = height;
|
|
332
301
|
return this;
|
|
333
302
|
}
|
|
303
|
+
async updateImageData(newSource) {
|
|
304
|
+
this.source = newSource;
|
|
305
|
+
this.imageData = undefined;
|
|
306
|
+
await this.loadImageDataForDimensions();
|
|
307
|
+
if (typeof newSource === 'string')
|
|
308
|
+
this.extension = this.detectExtension();
|
|
309
|
+
this.dpi = this.detectDPI() || 96;
|
|
310
|
+
}
|
|
334
311
|
setRelationshipId(relationshipId) {
|
|
335
312
|
this.relationshipId = relationshipId;
|
|
336
313
|
return this;
|
|
@@ -350,17 +327,14 @@ class Image {
|
|
|
350
327
|
return this.description;
|
|
351
328
|
}
|
|
352
329
|
rotate(degrees) {
|
|
353
|
-
|
|
354
|
-
this.rotation
|
|
355
|
-
|
|
356
|
-
const temp = this.width;
|
|
357
|
-
this.width = this.height;
|
|
358
|
-
this.height = temp;
|
|
330
|
+
this.rotation = ((degrees % 360) + 360) % 360;
|
|
331
|
+
if (this.rotation === 90 || this.rotation === 270) {
|
|
332
|
+
[this.width, this.height] = [this.height, this.width];
|
|
359
333
|
}
|
|
360
334
|
return this;
|
|
361
335
|
}
|
|
362
336
|
getRotation() {
|
|
363
|
-
return this.rotation
|
|
337
|
+
return this.rotation;
|
|
364
338
|
}
|
|
365
339
|
setEffectExtent(left, top, right, bottom) {
|
|
366
340
|
this.effectExtent = { left, top, right, bottom };
|
|
@@ -370,14 +344,7 @@ class Image {
|
|
|
370
344
|
return this.effectExtent;
|
|
371
345
|
}
|
|
372
346
|
setWrap(type, side, distances) {
|
|
373
|
-
this.wrap = {
|
|
374
|
-
type,
|
|
375
|
-
side,
|
|
376
|
-
distanceTop: distances?.top,
|
|
377
|
-
distanceBottom: distances?.bottom,
|
|
378
|
-
distanceLeft: distances?.left,
|
|
379
|
-
distanceRight: distances?.right,
|
|
380
|
-
};
|
|
347
|
+
this.wrap = { type, side, ...distances };
|
|
381
348
|
return this;
|
|
382
349
|
}
|
|
383
350
|
getWrap() {
|
|
@@ -399,12 +366,7 @@ class Image {
|
|
|
399
366
|
}
|
|
400
367
|
setCrop(left, top, right, bottom) {
|
|
401
368
|
const clamp = (val) => Math.max(0, Math.min(100, val));
|
|
402
|
-
this.crop = {
|
|
403
|
-
left: clamp(left),
|
|
404
|
-
top: clamp(top),
|
|
405
|
-
right: clamp(right),
|
|
406
|
-
bottom: clamp(bottom),
|
|
407
|
-
};
|
|
369
|
+
this.crop = { left: clamp(left), top: clamp(top), right: clamp(right), bottom: clamp(bottom) };
|
|
408
370
|
return this;
|
|
409
371
|
}
|
|
410
372
|
getCrop() {
|
|
@@ -412,440 +374,219 @@ class Image {
|
|
|
412
374
|
}
|
|
413
375
|
setEffects(options) {
|
|
414
376
|
const clamp = (val) => val !== undefined ? Math.max(-100, Math.min(100, val)) : undefined;
|
|
415
|
-
this.effects = {
|
|
416
|
-
brightness: clamp(options.brightness),
|
|
417
|
-
contrast: clamp(options.contrast),
|
|
418
|
-
grayscale: options.grayscale,
|
|
419
|
-
};
|
|
377
|
+
this.effects = { brightness: clamp(options.brightness), contrast: clamp(options.contrast), grayscale: options.grayscale };
|
|
420
378
|
return this;
|
|
421
379
|
}
|
|
422
380
|
getEffects() {
|
|
423
381
|
return this.effects;
|
|
424
382
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
cx: this.width.toString(),
|
|
442
|
-
cy: this.height.toString(),
|
|
443
|
-
},
|
|
444
|
-
selfClosing: true,
|
|
445
|
-
});
|
|
446
|
-
const effectExt = this.effectExtent || { left: 0, top: 0, right: 0, bottom: 0 };
|
|
447
|
-
children.push({
|
|
448
|
-
name: 'wp:effectExtent',
|
|
449
|
-
attributes: {
|
|
450
|
-
l: effectExt.left.toString(),
|
|
451
|
-
t: effectExt.top.toString(),
|
|
452
|
-
r: effectExt.right.toString(),
|
|
453
|
-
b: effectExt.bottom.toString(),
|
|
454
|
-
},
|
|
455
|
-
selfClosing: true,
|
|
456
|
-
});
|
|
457
|
-
children.push({
|
|
458
|
-
name: 'wp:docPr',
|
|
459
|
-
attributes: {
|
|
460
|
-
id: this.docPrId.toString(),
|
|
461
|
-
name: this.name,
|
|
462
|
-
descr: this.description,
|
|
463
|
-
},
|
|
464
|
-
selfClosing: true,
|
|
465
|
-
});
|
|
466
|
-
children.push({
|
|
467
|
-
name: 'wp:cNvGraphicFramePr',
|
|
468
|
-
children: [
|
|
469
|
-
{
|
|
470
|
-
name: 'a:graphicFrameLocks',
|
|
471
|
-
attributes: {
|
|
472
|
-
'xmlns:a': 'http://schemas.openxmlformats.org/drawingml/2006/main',
|
|
473
|
-
noChangeAspect: '1',
|
|
474
|
-
},
|
|
475
|
-
selfClosing: true,
|
|
476
|
-
},
|
|
477
|
-
],
|
|
478
|
-
});
|
|
479
|
-
children.push(this.createGraphic());
|
|
480
|
-
return {
|
|
481
|
-
name: 'wp:inline',
|
|
482
|
-
attributes: {
|
|
483
|
-
distT: '0',
|
|
484
|
-
distB: '0',
|
|
485
|
-
distL: '0',
|
|
486
|
-
distR: '0',
|
|
487
|
-
},
|
|
488
|
-
children,
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
createGraphic() {
|
|
492
|
-
return {
|
|
493
|
-
name: 'a:graphic',
|
|
494
|
-
attributes: {
|
|
495
|
-
'xmlns:a': 'http://schemas.openxmlformats.org/drawingml/2006/main',
|
|
496
|
-
},
|
|
497
|
-
children: [
|
|
498
|
-
{
|
|
499
|
-
name: 'a:graphicData',
|
|
500
|
-
attributes: {
|
|
501
|
-
uri: 'http://schemas.openxmlformats.org/drawingml/2006/picture',
|
|
502
|
-
},
|
|
503
|
-
children: [this.createPicture()],
|
|
504
|
-
},
|
|
505
|
-
],
|
|
506
|
-
};
|
|
507
|
-
}
|
|
508
|
-
createPicture() {
|
|
509
|
-
return {
|
|
510
|
-
name: 'pic:pic',
|
|
511
|
-
attributes: {
|
|
512
|
-
'xmlns:pic': 'http://schemas.openxmlformats.org/drawingml/2006/picture',
|
|
513
|
-
},
|
|
514
|
-
children: [
|
|
515
|
-
{
|
|
516
|
-
name: 'pic:nvPicPr',
|
|
517
|
-
children: [
|
|
518
|
-
{
|
|
519
|
-
name: 'pic:cNvPr',
|
|
520
|
-
attributes: {
|
|
521
|
-
id: this.docPrId.toString(),
|
|
522
|
-
name: this.name,
|
|
523
|
-
descr: this.description,
|
|
524
|
-
},
|
|
525
|
-
selfClosing: true,
|
|
526
|
-
},
|
|
527
|
-
{
|
|
528
|
-
name: 'pic:cNvPicPr',
|
|
529
|
-
selfClosing: true,
|
|
530
|
-
},
|
|
531
|
-
],
|
|
532
|
-
},
|
|
533
|
-
{
|
|
534
|
-
name: 'pic:blipFill',
|
|
535
|
-
children: this.createBlipFillChildren(),
|
|
536
|
-
},
|
|
537
|
-
{
|
|
538
|
-
name: 'pic:spPr',
|
|
539
|
-
children: [
|
|
540
|
-
{
|
|
541
|
-
name: 'a:xfrm',
|
|
542
|
-
attributes: this.getRotation() > 0 ? { rot: (this.getRotation() * 60000).toString() } : undefined,
|
|
543
|
-
children: [
|
|
544
|
-
{
|
|
545
|
-
name: 'a:off',
|
|
546
|
-
attributes: {
|
|
547
|
-
x: '0',
|
|
548
|
-
y: '0',
|
|
549
|
-
},
|
|
550
|
-
selfClosing: true,
|
|
551
|
-
},
|
|
552
|
-
{
|
|
553
|
-
name: 'a:ext',
|
|
554
|
-
attributes: {
|
|
555
|
-
cx: this.width.toString(),
|
|
556
|
-
cy: this.height.toString(),
|
|
557
|
-
},
|
|
558
|
-
selfClosing: true,
|
|
559
|
-
},
|
|
560
|
-
],
|
|
561
|
-
},
|
|
562
|
-
{
|
|
563
|
-
name: 'a:prstGeom',
|
|
564
|
-
attributes: {
|
|
565
|
-
prst: 'rect',
|
|
566
|
-
},
|
|
567
|
-
children: [
|
|
568
|
-
{
|
|
569
|
-
name: 'a:avLst',
|
|
570
|
-
selfClosing: true,
|
|
571
|
-
},
|
|
572
|
-
],
|
|
573
|
-
},
|
|
574
|
-
],
|
|
575
|
-
},
|
|
576
|
-
],
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
createBlipFillChildren() {
|
|
580
|
-
const children = [];
|
|
581
|
-
const blipChildren = [];
|
|
582
|
-
if (this.crop) {
|
|
583
|
-
blipChildren.push({
|
|
584
|
-
name: 'a:srcRect',
|
|
585
|
-
attributes: {
|
|
586
|
-
l: Math.round(this.crop.left * 1000).toString(),
|
|
587
|
-
t: Math.round(this.crop.top * 1000).toString(),
|
|
588
|
-
r: Math.round(this.crop.right * 1000).toString(),
|
|
589
|
-
b: Math.round(this.crop.bottom * 1000).toString(),
|
|
590
|
-
},
|
|
591
|
-
selfClosing: true,
|
|
592
|
-
});
|
|
593
|
-
}
|
|
594
|
-
if (this.effects) {
|
|
595
|
-
const lumAttrs = {};
|
|
596
|
-
if (this.effects.brightness !== undefined) {
|
|
597
|
-
lumAttrs.bright = Math.round(this.effects.brightness * 1000).toString();
|
|
598
|
-
}
|
|
599
|
-
if (this.effects.contrast !== undefined) {
|
|
600
|
-
lumAttrs.contrast = Math.round(this.effects.contrast * 1000).toString();
|
|
601
|
-
}
|
|
602
|
-
if (Object.keys(lumAttrs).length > 0) {
|
|
603
|
-
blipChildren.push({
|
|
604
|
-
name: 'a:lum',
|
|
605
|
-
attributes: lumAttrs,
|
|
606
|
-
selfClosing: true,
|
|
607
|
-
});
|
|
383
|
+
detectDPI() {
|
|
384
|
+
if (!this.imageData)
|
|
385
|
+
return undefined;
|
|
386
|
+
try {
|
|
387
|
+
if (this.extension === 'png') {
|
|
388
|
+
const physIndex = this.imageData.indexOf(Buffer.from([0x70, 0x48, 0x59, 0x73]));
|
|
389
|
+
if (physIndex !== -1 && physIndex + 12 < this.imageData.length) {
|
|
390
|
+
const xPixelsPerMeter = this.imageData.readUInt32BE(physIndex + 4);
|
|
391
|
+
const yPixelsPerMeter = this.imageData.readUInt32BE(physIndex + 8);
|
|
392
|
+
const unit = this.imageData[physIndex + 12];
|
|
393
|
+
if (unit === 1) {
|
|
394
|
+
const dpiX = Math.round(xPixelsPerMeter * 0.0254);
|
|
395
|
+
const dpiY = Math.round(yPixelsPerMeter * 0.0254);
|
|
396
|
+
return Math.min(dpiX, dpiY);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
608
399
|
}
|
|
609
|
-
if (this.
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
400
|
+
else if (this.extension === 'jpg' || this.extension === 'jpeg') {
|
|
401
|
+
let offset = 2;
|
|
402
|
+
while (offset < this.imageData.length) {
|
|
403
|
+
if (this.imageData[offset] !== 0xFF)
|
|
404
|
+
break;
|
|
405
|
+
const marker = this.imageData[offset + 1];
|
|
406
|
+
if (marker === 0xE0) {
|
|
407
|
+
const length = this.imageData.readUInt16BE(offset + 2);
|
|
408
|
+
if (length >= 16 && this.imageData.slice(offset + 4, offset + 9).toString('ascii') === 'JFIF\0') {
|
|
409
|
+
const units = this.imageData[offset + 11];
|
|
410
|
+
const xDensity = this.imageData.readUInt16BE(offset + 12);
|
|
411
|
+
const yDensity = this.imageData.readUInt16BE(offset + 14);
|
|
412
|
+
if (units === 1)
|
|
413
|
+
return Math.min(xDensity, yDensity);
|
|
414
|
+
if (units === 2)
|
|
415
|
+
return Math.min(Math.round(xDensity * 2.54), Math.round(yDensity * 2.54));
|
|
416
|
+
}
|
|
417
|
+
offset += 2 + length;
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
offset += 2 + this.imageData.readUInt16BE(offset + 2);
|
|
421
|
+
}
|
|
614
422
|
}
|
|
615
423
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
424
|
+
catch (error) {
|
|
425
|
+
logger_1.defaultLogger.warn(`DPI detection failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
426
|
+
}
|
|
427
|
+
return undefined;
|
|
428
|
+
}
|
|
429
|
+
isFloating() {
|
|
430
|
+
return !!this.anchor || !!this.position;
|
|
431
|
+
}
|
|
432
|
+
floatTopLeft(marginTop = 0, marginLeft = 0) {
|
|
433
|
+
this.setPosition({ anchor: 'page', offset: marginLeft }, { anchor: 'page', offset: marginTop });
|
|
434
|
+
this.setAnchor({
|
|
435
|
+
behindDoc: false,
|
|
436
|
+
locked: false,
|
|
437
|
+
layoutInCell: true,
|
|
438
|
+
allowOverlap: true,
|
|
439
|
+
relativeHeight: 251658240
|
|
631
440
|
});
|
|
632
|
-
|
|
441
|
+
this.setWrap('square', 'bothSides');
|
|
442
|
+
return this;
|
|
633
443
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
444
|
+
floatTopRight(marginTop = 0, marginRight = 0) {
|
|
445
|
+
this.setPosition({ anchor: 'page', alignment: 'right', offset: -marginRight }, { anchor: 'page', offset: marginTop });
|
|
446
|
+
this.setAnchor({
|
|
637
447
|
behindDoc: false,
|
|
638
448
|
locked: false,
|
|
639
449
|
layoutInCell: true,
|
|
640
|
-
allowOverlap:
|
|
641
|
-
relativeHeight: 251658240
|
|
642
|
-
};
|
|
643
|
-
if (this.position) {
|
|
644
|
-
const posH = this.position.horizontal;
|
|
645
|
-
const posHChildren = [];
|
|
646
|
-
if (posH.offset !== undefined) {
|
|
647
|
-
posHChildren.push({
|
|
648
|
-
name: 'wp:posOffset',
|
|
649
|
-
children: [posH.offset.toString()],
|
|
650
|
-
});
|
|
651
|
-
}
|
|
652
|
-
else if (posH.alignment) {
|
|
653
|
-
posHChildren.push({
|
|
654
|
-
name: 'wp:align',
|
|
655
|
-
children: [posH.alignment],
|
|
656
|
-
});
|
|
657
|
-
}
|
|
658
|
-
children.push({
|
|
659
|
-
name: 'wp:positionH',
|
|
660
|
-
attributes: {
|
|
661
|
-
relativeFrom: posH.anchor,
|
|
662
|
-
},
|
|
663
|
-
children: posHChildren,
|
|
664
|
-
});
|
|
665
|
-
}
|
|
666
|
-
if (this.position) {
|
|
667
|
-
const posV = this.position.vertical;
|
|
668
|
-
const posVChildren = [];
|
|
669
|
-
if (posV.offset !== undefined) {
|
|
670
|
-
posVChildren.push({
|
|
671
|
-
name: 'wp:posOffset',
|
|
672
|
-
children: [posV.offset.toString()],
|
|
673
|
-
});
|
|
674
|
-
}
|
|
675
|
-
else if (posV.alignment) {
|
|
676
|
-
posVChildren.push({
|
|
677
|
-
name: 'wp:align',
|
|
678
|
-
children: [posV.alignment],
|
|
679
|
-
});
|
|
680
|
-
}
|
|
681
|
-
children.push({
|
|
682
|
-
name: 'wp:positionV',
|
|
683
|
-
attributes: {
|
|
684
|
-
relativeFrom: posV.anchor,
|
|
685
|
-
},
|
|
686
|
-
children: posVChildren,
|
|
687
|
-
});
|
|
688
|
-
}
|
|
689
|
-
children.push({
|
|
690
|
-
name: 'wp:extent',
|
|
691
|
-
attributes: {
|
|
692
|
-
cx: this.width.toString(),
|
|
693
|
-
cy: this.height.toString(),
|
|
694
|
-
},
|
|
695
|
-
selfClosing: true,
|
|
450
|
+
allowOverlap: true,
|
|
451
|
+
relativeHeight: 251658240
|
|
696
452
|
});
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
453
|
+
this.setWrap('square', 'bothSides');
|
|
454
|
+
return this;
|
|
455
|
+
}
|
|
456
|
+
floatCenter() {
|
|
457
|
+
this.setPosition({ anchor: 'page', alignment: 'center' }, { anchor: 'page', alignment: 'center' });
|
|
458
|
+
this.setAnchor({
|
|
459
|
+
behindDoc: false,
|
|
460
|
+
locked: false,
|
|
461
|
+
layoutInCell: true,
|
|
462
|
+
allowOverlap: true,
|
|
463
|
+
relativeHeight: 251658240
|
|
707
464
|
});
|
|
708
|
-
|
|
709
|
-
|
|
465
|
+
this.setWrap('square', 'bothSides');
|
|
466
|
+
return this;
|
|
467
|
+
}
|
|
468
|
+
setBehindText(behind = true) {
|
|
469
|
+
if (this.anchor) {
|
|
470
|
+
this.anchor.behindDoc = behind;
|
|
710
471
|
}
|
|
711
472
|
else {
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
473
|
+
this.setAnchor({
|
|
474
|
+
behindDoc: behind,
|
|
475
|
+
locked: false,
|
|
476
|
+
layoutInCell: true,
|
|
477
|
+
allowOverlap: true,
|
|
478
|
+
relativeHeight: 251658240
|
|
718
479
|
});
|
|
719
480
|
}
|
|
720
|
-
|
|
721
|
-
name: 'wp:docPr',
|
|
722
|
-
attributes: {
|
|
723
|
-
id: this.docPrId.toString(),
|
|
724
|
-
name: this.name,
|
|
725
|
-
descr: this.description,
|
|
726
|
-
},
|
|
727
|
-
selfClosing: true,
|
|
728
|
-
});
|
|
729
|
-
children.push({
|
|
730
|
-
name: 'wp:cNvGraphicFramePr',
|
|
731
|
-
children: [
|
|
732
|
-
{
|
|
733
|
-
name: 'a:graphicFrameLocks',
|
|
734
|
-
attributes: {
|
|
735
|
-
'xmlns:a': 'http://schemas.openxmlformats.org/drawingml/2006/main',
|
|
736
|
-
noChangeAspect: '1',
|
|
737
|
-
},
|
|
738
|
-
selfClosing: true,
|
|
739
|
-
},
|
|
740
|
-
],
|
|
741
|
-
});
|
|
742
|
-
children.push(this.createGraphic());
|
|
743
|
-
return {
|
|
744
|
-
name: 'wp:anchor',
|
|
745
|
-
attributes: {
|
|
746
|
-
distT: this.wrap?.distanceTop?.toString() || '0',
|
|
747
|
-
distB: this.wrap?.distanceBottom?.toString() || '0',
|
|
748
|
-
distL: this.wrap?.distanceLeft?.toString() || '0',
|
|
749
|
-
distR: this.wrap?.distanceRight?.toString() || '0',
|
|
750
|
-
simplePos: '0',
|
|
751
|
-
relativeHeight: anchorConfig.relativeHeight.toString(),
|
|
752
|
-
behindDoc: anchorConfig.behindDoc ? '1' : '0',
|
|
753
|
-
locked: anchorConfig.locked ? '1' : '0',
|
|
754
|
-
layoutInCell: anchorConfig.layoutInCell ? '1' : '0',
|
|
755
|
-
allowOverlap: anchorConfig.allowOverlap ? '1' : '0',
|
|
756
|
-
},
|
|
757
|
-
children,
|
|
758
|
-
};
|
|
759
|
-
}
|
|
760
|
-
createWrapElement() {
|
|
761
|
-
if (!this.wrap) {
|
|
762
|
-
return {
|
|
763
|
-
name: 'wp:wrapSquare',
|
|
764
|
-
attributes: {
|
|
765
|
-
wrapText: 'bothSides',
|
|
766
|
-
},
|
|
767
|
-
selfClosing: true,
|
|
768
|
-
};
|
|
769
|
-
}
|
|
770
|
-
const wrapAttributes = {
|
|
771
|
-
wrapText: this.wrap.side || 'bothSides',
|
|
772
|
-
};
|
|
773
|
-
if (this.wrap.distanceTop !== undefined) {
|
|
774
|
-
wrapAttributes.distT = this.wrap.distanceTop.toString();
|
|
775
|
-
}
|
|
776
|
-
if (this.wrap.distanceBottom !== undefined) {
|
|
777
|
-
wrapAttributes.distB = this.wrap.distanceBottom.toString();
|
|
778
|
-
}
|
|
779
|
-
if (this.wrap.distanceLeft !== undefined) {
|
|
780
|
-
wrapAttributes.distL = this.wrap.distanceLeft.toString();
|
|
781
|
-
}
|
|
782
|
-
if (this.wrap.distanceRight !== undefined) {
|
|
783
|
-
wrapAttributes.distR = this.wrap.distanceRight.toString();
|
|
784
|
-
}
|
|
785
|
-
const wrapElementName = (() => {
|
|
786
|
-
switch (this.wrap.type) {
|
|
787
|
-
case 'square':
|
|
788
|
-
return 'wp:wrapSquare';
|
|
789
|
-
case 'tight':
|
|
790
|
-
return 'wp:wrapTight';
|
|
791
|
-
case 'through':
|
|
792
|
-
return 'wp:wrapThrough';
|
|
793
|
-
case 'topAndBottom':
|
|
794
|
-
return 'wp:wrapTopAndBottom';
|
|
795
|
-
case 'none':
|
|
796
|
-
return 'wp:wrapNone';
|
|
797
|
-
default:
|
|
798
|
-
return 'wp:wrapSquare';
|
|
799
|
-
}
|
|
800
|
-
})();
|
|
801
|
-
return {
|
|
802
|
-
name: wrapElementName,
|
|
803
|
-
attributes: wrapAttributes,
|
|
804
|
-
selfClosing: true,
|
|
805
|
-
};
|
|
806
|
-
}
|
|
807
|
-
static async fromFile(filePath, width, height) {
|
|
808
|
-
const image = new Image({
|
|
809
|
-
source: filePath,
|
|
810
|
-
width,
|
|
811
|
-
height,
|
|
812
|
-
name: filePath.split(/[/\\]/).pop() || 'image',
|
|
813
|
-
});
|
|
814
|
-
if (!width || !height) {
|
|
815
|
-
try {
|
|
816
|
-
await image.loadImageDataForDimensions();
|
|
817
|
-
}
|
|
818
|
-
catch (error) {
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
return image;
|
|
481
|
+
return this;
|
|
822
482
|
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
width,
|
|
827
|
-
height,
|
|
828
|
-
name: `image.${extension}`,
|
|
829
|
-
});
|
|
830
|
-
image.extension = extension;
|
|
831
|
-
if (!width || !height) {
|
|
832
|
-
await image.loadImageDataForDimensions();
|
|
833
|
-
}
|
|
834
|
-
return image;
|
|
483
|
+
applyTwoPixelBlackBorder() {
|
|
484
|
+
this.border = { width: 2, color: '000000' };
|
|
485
|
+
return this;
|
|
835
486
|
}
|
|
836
|
-
|
|
837
|
-
const
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
487
|
+
toXML() {
|
|
488
|
+
const isFloating = this.isFloating();
|
|
489
|
+
const extent = XMLBuilder_1.XMLBuilder.cxCy('extent', this.width, this.height);
|
|
490
|
+
const blip = XMLBuilder_1.XMLBuilder.a('blip', { 'r:embed': this.relationshipId });
|
|
491
|
+
const spPrChildren = [extent];
|
|
492
|
+
spPrChildren.push(XMLBuilder_1.XMLBuilder.a('prstGeom', { prst: 'rect' }));
|
|
493
|
+
if (this.border) {
|
|
494
|
+
const pxToEmu = 9525;
|
|
495
|
+
const widthEmu = this.border.width * pxToEmu;
|
|
496
|
+
const ln = XMLBuilder_1.XMLBuilder.a('ln', { w: widthEmu.toString() }, [
|
|
497
|
+
XMLBuilder_1.XMLBuilder.a('solidFill', undefined, [
|
|
498
|
+
XMLBuilder_1.XMLBuilder.a('srgbClr', { val: this.border.color })
|
|
499
|
+
])
|
|
500
|
+
]);
|
|
501
|
+
spPrChildren.push(ln);
|
|
502
|
+
}
|
|
503
|
+
const graphicData = XMLBuilder_1.XMLBuilder.a('graphicData', { uri: 'http://schemas.openxmlformats.org/drawingml/2006/picture' }, [
|
|
504
|
+
XMLBuilder_1.XMLBuilder.pic('pic', undefined, [
|
|
505
|
+
XMLBuilder_1.XMLBuilder.pic('nvPicPr', undefined, [
|
|
506
|
+
XMLBuilder_1.XMLBuilder.pic('cNvPr', { id: this.docPrId, name: this.name, descr: this.description }),
|
|
507
|
+
XMLBuilder_1.XMLBuilder.pic('cNvPicPr')
|
|
508
|
+
]),
|
|
509
|
+
XMLBuilder_1.XMLBuilder.pic('blipFill', undefined, [blip]),
|
|
510
|
+
XMLBuilder_1.XMLBuilder.pic('spPr', undefined, spPrChildren)
|
|
511
|
+
])
|
|
512
|
+
]);
|
|
513
|
+
const graphic = XMLBuilder_1.XMLBuilder.a('graphic', undefined, [graphicData]);
|
|
514
|
+
if (isFloating) {
|
|
515
|
+
const positionHChildren = [];
|
|
516
|
+
if (this.position?.horizontal.alignment) {
|
|
517
|
+
positionHChildren.push(XMLBuilder_1.XMLBuilder.wp('align', undefined, [this.position.horizontal.alignment]));
|
|
518
|
+
}
|
|
519
|
+
else {
|
|
520
|
+
positionHChildren.push(XMLBuilder_1.XMLBuilder.wp('posOffset', undefined, [(this.position?.horizontal.offset || 0).toString()]));
|
|
521
|
+
}
|
|
522
|
+
const positionH = XMLBuilder_1.XMLBuilder.wp('positionH', { relativeFrom: this.position?.horizontal.anchor || 'page' }, positionHChildren);
|
|
523
|
+
const positionVChildren = [];
|
|
524
|
+
if (this.position?.vertical.alignment) {
|
|
525
|
+
positionVChildren.push(XMLBuilder_1.XMLBuilder.wp('align', undefined, [this.position.vertical.alignment]));
|
|
526
|
+
}
|
|
527
|
+
else {
|
|
528
|
+
positionVChildren.push(XMLBuilder_1.XMLBuilder.wp('posOffset', undefined, [(this.position?.vertical.offset || 0).toString()]));
|
|
529
|
+
}
|
|
530
|
+
const positionV = XMLBuilder_1.XMLBuilder.wp('positionV', { relativeFrom: this.position?.vertical.anchor || 'page' }, positionVChildren);
|
|
531
|
+
const anchorChildren = [
|
|
532
|
+
positionH,
|
|
533
|
+
positionV,
|
|
534
|
+
extent
|
|
535
|
+
];
|
|
536
|
+
if (this.wrap) {
|
|
537
|
+
const wrapAttrs = {};
|
|
538
|
+
if (this.wrap.distanceTop !== undefined)
|
|
539
|
+
wrapAttrs.distT = this.wrap.distanceTop;
|
|
540
|
+
if (this.wrap.distanceBottom !== undefined)
|
|
541
|
+
wrapAttrs.distB = this.wrap.distanceBottom;
|
|
542
|
+
if (this.wrap.distanceLeft !== undefined)
|
|
543
|
+
wrapAttrs.distL = this.wrap.distanceLeft;
|
|
544
|
+
if (this.wrap.distanceRight !== undefined)
|
|
545
|
+
wrapAttrs.distR = this.wrap.distanceRight;
|
|
546
|
+
if (this.wrap.side)
|
|
547
|
+
wrapAttrs.wrapText = this.wrap.side;
|
|
548
|
+
let wrapElementName;
|
|
549
|
+
switch (this.wrap.type) {
|
|
550
|
+
case 'square':
|
|
551
|
+
wrapElementName = 'wrapSquare';
|
|
552
|
+
break;
|
|
553
|
+
case 'tight':
|
|
554
|
+
wrapElementName = 'wrapTight';
|
|
555
|
+
break;
|
|
556
|
+
case 'through':
|
|
557
|
+
wrapElementName = 'wrapThrough';
|
|
558
|
+
break;
|
|
559
|
+
case 'topAndBottom':
|
|
560
|
+
wrapElementName = 'wrapTopAndBottom';
|
|
561
|
+
break;
|
|
562
|
+
case 'none':
|
|
563
|
+
wrapElementName = 'wrapNone';
|
|
564
|
+
break;
|
|
565
|
+
default: wrapElementName = 'wrapSquare';
|
|
566
|
+
}
|
|
567
|
+
anchorChildren.push(XMLBuilder_1.XMLBuilder.wp(wrapElementName, wrapAttrs));
|
|
568
|
+
}
|
|
569
|
+
anchorChildren.push(XMLBuilder_1.XMLBuilder.wp('docPr', { id: this.docPrId, name: this.name, descr: this.description }));
|
|
570
|
+
anchorChildren.push(graphic);
|
|
571
|
+
return XMLBuilder_1.XMLBuilder.w('drawing', undefined, [
|
|
572
|
+
XMLBuilder_1.XMLBuilder.wp('anchor', {
|
|
573
|
+
behindDoc: this.anchor?.behindDoc ? 1 : 0,
|
|
574
|
+
locked: this.anchor?.locked ? 1 : 0,
|
|
575
|
+
layoutInCell: this.anchor?.layoutInCell ? 1 : 0,
|
|
576
|
+
allowOverlap: this.anchor?.allowOverlap ? 1 : 0,
|
|
577
|
+
relativeHeight: this.anchor?.relativeHeight
|
|
578
|
+
}, anchorChildren)
|
|
579
|
+
]);
|
|
844
580
|
}
|
|
845
|
-
else
|
|
846
|
-
|
|
581
|
+
else {
|
|
582
|
+
return XMLBuilder_1.XMLBuilder.w('drawing', undefined, [
|
|
583
|
+
XMLBuilder_1.XMLBuilder.wp('inline', undefined, [
|
|
584
|
+
extent,
|
|
585
|
+
XMLBuilder_1.XMLBuilder.wp('docPr', { id: this.docPrId, name: this.name, descr: this.description }),
|
|
586
|
+
graphic
|
|
587
|
+
])
|
|
588
|
+
]);
|
|
847
589
|
}
|
|
848
|
-
return image;
|
|
849
590
|
}
|
|
850
591
|
}
|
|
851
592
|
exports.Image = Image;
|