reamkit 1.17.0 → 1.18.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/esm/core/document-model/types.d.ts +53 -1
- package/dist/esm/core/images.d.ts +2 -2
- package/dist/esm/core/images.js +38 -0
- package/dist/esm/core/lzw.d.ts +17 -0
- package/dist/esm/core/lzw.js +83 -0
- package/dist/esm/core/metafile/emf.d.ts +16 -0
- package/dist/esm/core/metafile/emf.js +635 -0
- package/dist/esm/core/metafile/picture.d.ts +106 -0
- package/dist/esm/core/metafile/picture.js +55 -0
- package/dist/esm/core/metafile/wmf.d.ts +9 -0
- package/dist/esm/core/metafile/wmf.js +371 -0
- package/dist/esm/core/tiff.d.ts +20 -0
- package/dist/esm/core/tiff.js +339 -0
- package/dist/esm/core/vector.d.ts +5 -0
- package/dist/esm/excel/print-model.js +3 -1
- package/dist/esm/html/html-writer.js +3 -2
- package/dist/esm/layout/page-doc.d.ts +46 -3
- package/dist/esm/layout/styled-layout.js +486 -81
- package/dist/esm/pdf/styled-page-emitter.js +78 -3
- package/dist/esm/pdf/vector-graphics.d.ts +1 -1
- package/dist/esm/pdf/vector-graphics.js +2 -1
- package/dist/esm/pdf-reader/image-decode.js +7 -75
- package/dist/esm/svg/svg-writer.js +1 -1
- package/dist/esm/word/document-parser.js +3 -0
- package/dist/esm/word/drawing-parser.d.ts +5 -0
- package/dist/esm/word/drawing-parser.js +95 -12
- package/package.json +1 -1
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import { lzwDecodeMsb } from "./lzw.js";
|
|
2
|
+
import { unzlibSync } from "fflate";
|
|
3
|
+
//#region src/core/tiff.ts
|
|
4
|
+
var MAX_PIXELS = 4e7;
|
|
5
|
+
var TAG = {
|
|
6
|
+
width: 256,
|
|
7
|
+
height: 257,
|
|
8
|
+
bitsPerSample: 258,
|
|
9
|
+
compression: 259,
|
|
10
|
+
photometric: 262,
|
|
11
|
+
fillOrder: 266,
|
|
12
|
+
stripOffsets: 273,
|
|
13
|
+
samplesPerPixel: 277,
|
|
14
|
+
rowsPerStrip: 278,
|
|
15
|
+
stripByteCounts: 279,
|
|
16
|
+
planarConfig: 284,
|
|
17
|
+
predictor: 317,
|
|
18
|
+
colorMap: 320,
|
|
19
|
+
tileWidth: 322,
|
|
20
|
+
tileLength: 323,
|
|
21
|
+
tileOffsets: 324,
|
|
22
|
+
tileByteCounts: 325,
|
|
23
|
+
extraSamples: 338,
|
|
24
|
+
sampleFormat: 339
|
|
25
|
+
};
|
|
26
|
+
/** Whether the bytes open with a TIFF header (§2: `II*\0` or `MM\0*`). */
|
|
27
|
+
function isTiff(bytes) {
|
|
28
|
+
if (bytes.length < 8) return false;
|
|
29
|
+
const le = bytes[0] === 73 && bytes[1] === 73;
|
|
30
|
+
const be = bytes[0] === 77 && bytes[1] === 77;
|
|
31
|
+
if (!le && !be) return false;
|
|
32
|
+
return (le ? bytes[2] | bytes[3] << 8 : bytes[2] << 8 | bytes[3]) === 42;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Decode the first image of a baseline TIFF into 8-bit chunky samples.
|
|
36
|
+
*
|
|
37
|
+
* @throws Error when the file is malformed or uses a feature outside baseline
|
|
38
|
+
* TIFF (planar separation, JPEG/fax compression, floating-point samples).
|
|
39
|
+
*/
|
|
40
|
+
function decodeTiff(bytes) {
|
|
41
|
+
if (!isTiff(bytes)) throw new Error("TIFF: not a TIFF file");
|
|
42
|
+
const le = bytes[0] === 73;
|
|
43
|
+
const r = new Reader(bytes, le);
|
|
44
|
+
const ifd = r.readIfd(r.u32(4));
|
|
45
|
+
const width = num(ifd, TAG.width, 0);
|
|
46
|
+
const height = num(ifd, TAG.height, 0);
|
|
47
|
+
if (width <= 0 || height <= 0) throw new Error("TIFF: no image dimensions");
|
|
48
|
+
if (width * height > MAX_PIXELS) throw new Error("TIFF: image too large to decode");
|
|
49
|
+
const samples = num(ifd, TAG.samplesPerPixel, 1);
|
|
50
|
+
const bitsList = list(ifd, TAG.bitsPerSample, [1]);
|
|
51
|
+
const bits = bitsList[0] ?? 1;
|
|
52
|
+
if (bitsList.some((b) => b !== bits)) throw new Error("TIFF: mixed sample depths");
|
|
53
|
+
if (![
|
|
54
|
+
1,
|
|
55
|
+
2,
|
|
56
|
+
4,
|
|
57
|
+
8,
|
|
58
|
+
16
|
|
59
|
+
].includes(bits)) throw new Error(`TIFF: ${String(bits)}-bit samples`);
|
|
60
|
+
if (num(ifd, TAG.planarConfig, 1) !== 1) throw new Error("TIFF: planar separation");
|
|
61
|
+
if (list(ifd, TAG.sampleFormat, [1]).some((f) => f !== 1 && f !== 2)) throw new Error("TIFF: non-integer samples");
|
|
62
|
+
const photometric = num(ifd, TAG.photometric, samples >= 3 ? 2 : 1);
|
|
63
|
+
const compression = num(ifd, TAG.compression, 1);
|
|
64
|
+
const predictor = num(ifd, TAG.predictor, 1);
|
|
65
|
+
if (predictor !== 1 && predictor !== 2) throw new Error("TIFF: floating-point predictor");
|
|
66
|
+
const blocks = blockLayout(ifd, width, height);
|
|
67
|
+
const bytesPerRow = Math.ceil(blocks.blockWidth * samples * bits / 8);
|
|
68
|
+
const raw = new Uint8Array(width * height * samples * (bits === 16 ? 2 : 1));
|
|
69
|
+
const rawRowBytes = width * samples * (bits === 16 ? 2 : 1);
|
|
70
|
+
const blockBytes = bytesPerRow * blocks.blockHeight;
|
|
71
|
+
for (const b of blocks.blocks) {
|
|
72
|
+
const count = b.byteCount > 0 ? b.byteCount : blockBytes;
|
|
73
|
+
const rows = unpredict(decodeBlock(bytes.subarray(b.offset, b.offset + count), compression, blockBytes), predictor, bytesPerRow, samples, bits, le);
|
|
74
|
+
for (let y = 0; y < blocks.blockHeight; y++) {
|
|
75
|
+
const destY = b.y + y;
|
|
76
|
+
if (destY >= height) break;
|
|
77
|
+
const src = y * bytesPerRow;
|
|
78
|
+
if (src >= rows.length) break;
|
|
79
|
+
const row = expandRow(rows.subarray(src, src + bytesPerRow), bits, blocks.blockWidth * samples);
|
|
80
|
+
const keep = Math.min(row.length, (width - b.x) * samples * (bits === 16 ? 2 : 1));
|
|
81
|
+
raw.set(row.subarray(0, keep), destY * rawRowBytes + b.x * samples * (bits === 16 ? 2 : 1));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return toChannels(bits === 16 ? highBytes(raw, le) : raw, {
|
|
85
|
+
width,
|
|
86
|
+
height,
|
|
87
|
+
samples,
|
|
88
|
+
bits,
|
|
89
|
+
photometric,
|
|
90
|
+
palette: list(ifd, TAG.colorMap, []),
|
|
91
|
+
extra: list(ifd, TAG.extraSamples, [])
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
var TYPE_SIZE = new Map([
|
|
95
|
+
[1, 1],
|
|
96
|
+
[2, 1],
|
|
97
|
+
[3, 2],
|
|
98
|
+
[4, 4],
|
|
99
|
+
[5, 8],
|
|
100
|
+
[6, 1],
|
|
101
|
+
[7, 1],
|
|
102
|
+
[8, 2],
|
|
103
|
+
[9, 4],
|
|
104
|
+
[10, 8],
|
|
105
|
+
[11, 4],
|
|
106
|
+
[12, 8]
|
|
107
|
+
]);
|
|
108
|
+
var Reader = class {
|
|
109
|
+
constructor(b, le) {
|
|
110
|
+
this.b = b;
|
|
111
|
+
this.le = le;
|
|
112
|
+
}
|
|
113
|
+
u16(o) {
|
|
114
|
+
if (o + 2 > this.b.length) throw new Error("TIFF: truncated");
|
|
115
|
+
return this.le ? this.b[o] | this.b[o + 1] << 8 : this.b[o] << 8 | this.b[o + 1];
|
|
116
|
+
}
|
|
117
|
+
u32(o) {
|
|
118
|
+
if (o + 4 > this.b.length) throw new Error("TIFF: truncated");
|
|
119
|
+
return this.le ? (this.b[o] | this.b[o + 1] << 8 | this.b[o + 2] << 16) + this.b[o + 3] * 16777216 : (this.b[o + 3] | this.b[o + 2] << 8 | this.b[o + 1] << 16) + this.b[o] * 16777216;
|
|
120
|
+
}
|
|
121
|
+
/** §2 — the directory at `offset`: a count, then 12-byte entries. */
|
|
122
|
+
readIfd(offset) {
|
|
123
|
+
const count = this.u16(offset);
|
|
124
|
+
const out = /* @__PURE__ */ new Map();
|
|
125
|
+
for (let i = 0; i < count; i++) {
|
|
126
|
+
const e = offset + 2 + i * 12;
|
|
127
|
+
const tag = this.u16(e);
|
|
128
|
+
const type = this.u16(e + 2);
|
|
129
|
+
const n = this.u32(e + 4);
|
|
130
|
+
const size = TYPE_SIZE.get(type) ?? 0;
|
|
131
|
+
if (size === 0 || n > 1e6) continue;
|
|
132
|
+
const at = size * n <= 4 ? e + 8 : this.u32(e + 8);
|
|
133
|
+
out.set(tag, {
|
|
134
|
+
type,
|
|
135
|
+
count: n,
|
|
136
|
+
values: this.readValues(type, n, at)
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return out;
|
|
140
|
+
}
|
|
141
|
+
readValues(type, count, at) {
|
|
142
|
+
const size = TYPE_SIZE.get(type) ?? 1;
|
|
143
|
+
const out = [];
|
|
144
|
+
for (let i = 0; i < count; i++) {
|
|
145
|
+
const o = at + i * size;
|
|
146
|
+
if (o + size > this.b.length) break;
|
|
147
|
+
switch (type) {
|
|
148
|
+
case 1:
|
|
149
|
+
case 2:
|
|
150
|
+
case 6:
|
|
151
|
+
case 7:
|
|
152
|
+
out.push(this.b[o]);
|
|
153
|
+
break;
|
|
154
|
+
case 3:
|
|
155
|
+
out.push(this.u16(o));
|
|
156
|
+
break;
|
|
157
|
+
case 8: {
|
|
158
|
+
const v = this.u16(o);
|
|
159
|
+
out.push(v > 32767 ? v - 65536 : v);
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
case 4:
|
|
163
|
+
out.push(this.u32(o));
|
|
164
|
+
break;
|
|
165
|
+
case 9: {
|
|
166
|
+
const v = this.u32(o);
|
|
167
|
+
out.push(v > 2147483647 ? v - 4294967296 : v);
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
case 5:
|
|
171
|
+
case 10: {
|
|
172
|
+
const d = this.u32(o + 4);
|
|
173
|
+
out.push(d === 0 ? 0 : this.u32(o) / d);
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
default: out.push(0);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return out;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
function num(ifd, tag, fallback) {
|
|
183
|
+
return ifd.get(tag)?.values[0] ?? fallback;
|
|
184
|
+
}
|
|
185
|
+
function list(ifd, tag, fallback) {
|
|
186
|
+
const e = ifd.get(tag);
|
|
187
|
+
return e && e.values.length > 0 ? e.values : fallback;
|
|
188
|
+
}
|
|
189
|
+
function blockLayout(ifd, width, height) {
|
|
190
|
+
const tileW = num(ifd, TAG.tileWidth, 0);
|
|
191
|
+
const tileH = num(ifd, TAG.tileLength, 0);
|
|
192
|
+
if (tileW > 0 && tileH > 0) {
|
|
193
|
+
const offsets = list(ifd, TAG.tileOffsets, []);
|
|
194
|
+
const counts = list(ifd, TAG.tileByteCounts, []);
|
|
195
|
+
const across = Math.ceil(width / tileW);
|
|
196
|
+
return {
|
|
197
|
+
blocks: offsets.map((offset, i) => ({
|
|
198
|
+
offset,
|
|
199
|
+
byteCount: counts[i] ?? 0,
|
|
200
|
+
x: i % across * tileW,
|
|
201
|
+
y: Math.floor(i / across) * tileH
|
|
202
|
+
})),
|
|
203
|
+
blockWidth: tileW,
|
|
204
|
+
blockHeight: tileH
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
const rows = num(ifd, TAG.rowsPerStrip, height) || height;
|
|
208
|
+
const offsets = list(ifd, TAG.stripOffsets, []);
|
|
209
|
+
const counts = list(ifd, TAG.stripByteCounts, []);
|
|
210
|
+
if (offsets.length === 0) throw new Error("TIFF: no image data");
|
|
211
|
+
return {
|
|
212
|
+
blocks: offsets.map((offset, i) => ({
|
|
213
|
+
offset,
|
|
214
|
+
byteCount: counts[i] ?? 0,
|
|
215
|
+
x: 0,
|
|
216
|
+
y: i * rows
|
|
217
|
+
})),
|
|
218
|
+
blockWidth: width,
|
|
219
|
+
blockHeight: rows
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function decodeBlock(data, compression, expected) {
|
|
223
|
+
switch (compression) {
|
|
224
|
+
case 1: return data;
|
|
225
|
+
case 5: return lzwDecodeMsb(data, { limit: expected });
|
|
226
|
+
case 8:
|
|
227
|
+
case 32946: try {
|
|
228
|
+
return unzlibSync(data);
|
|
229
|
+
} catch {
|
|
230
|
+
throw new Error("TIFF: corrupt Deflate strip");
|
|
231
|
+
}
|
|
232
|
+
case 32773: return packBits(data, expected);
|
|
233
|
+
default: throw new Error(`TIFF: compression ${String(compression)}`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function packBits(data, expected) {
|
|
237
|
+
const out = new Uint8Array(expected);
|
|
238
|
+
let o = 0;
|
|
239
|
+
for (let i = 0; i < data.length && o < expected;) {
|
|
240
|
+
const n = data[i++];
|
|
241
|
+
if (n === 128) continue;
|
|
242
|
+
if (n < 128) {
|
|
243
|
+
const run = Math.min(n + 1, data.length - i, expected - o);
|
|
244
|
+
out.set(data.subarray(i, i + run), o);
|
|
245
|
+
i += n + 1;
|
|
246
|
+
o += run;
|
|
247
|
+
} else {
|
|
248
|
+
const byte = data[i++] ?? 0;
|
|
249
|
+
const run = Math.min(257 - n, expected - o);
|
|
250
|
+
out.fill(byte, o, o + run);
|
|
251
|
+
o += run;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return out.subarray(0, o);
|
|
255
|
+
}
|
|
256
|
+
function unpredict(data, predictor, rowBytes, samples, bits, le) {
|
|
257
|
+
if (predictor !== 2 || rowBytes <= 0) return data;
|
|
258
|
+
if (bits !== 8 && bits !== 16) return data;
|
|
259
|
+
const out = data.slice();
|
|
260
|
+
const rows = Math.floor(out.length / rowBytes);
|
|
261
|
+
if (bits === 8) {
|
|
262
|
+
for (let r = 0; r < rows; r++) {
|
|
263
|
+
const off = r * rowBytes;
|
|
264
|
+
for (let i = samples; i < rowBytes; i++) out[off + i] = out[off + i] + out[off + i - samples] & 255;
|
|
265
|
+
}
|
|
266
|
+
return out;
|
|
267
|
+
}
|
|
268
|
+
const stride = samples * 2;
|
|
269
|
+
const hi = le ? 1 : 0;
|
|
270
|
+
const lo = le ? 0 : 1;
|
|
271
|
+
for (let r = 0; r < rows; r++) {
|
|
272
|
+
const off = r * rowBytes;
|
|
273
|
+
for (let i = stride; i + 1 < rowBytes; i += 2) {
|
|
274
|
+
const prev = out[off + i - stride + hi] << 8 | out[off + i - stride + lo];
|
|
275
|
+
const v = (out[off + i + hi] << 8 | out[off + i + lo]) + prev & 65535;
|
|
276
|
+
out[off + i + hi] = v >> 8;
|
|
277
|
+
out[off + i + lo] = v & 255;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return out;
|
|
281
|
+
}
|
|
282
|
+
function expandRow(row, bits, samplesInRow) {
|
|
283
|
+
if (bits >= 8) return row;
|
|
284
|
+
const out = new Uint8Array(samplesInRow);
|
|
285
|
+
const max = (1 << bits) - 1;
|
|
286
|
+
for (let i = 0; i < samplesInRow; i++) {
|
|
287
|
+
const bit = i * bits;
|
|
288
|
+
const byte = row[bit >> 3] ?? 0;
|
|
289
|
+
const shift = 8 - bits - (bit & 7);
|
|
290
|
+
out[i] = Math.round((byte >> shift & max) * 255 / max);
|
|
291
|
+
}
|
|
292
|
+
return out;
|
|
293
|
+
}
|
|
294
|
+
function highBytes(raw, le) {
|
|
295
|
+
const out = new Uint8Array(raw.length >> 1);
|
|
296
|
+
const hi = le ? 1 : 0;
|
|
297
|
+
for (let i = 0; i < out.length; i++) out[i] = raw[i * 2 + hi];
|
|
298
|
+
return out;
|
|
299
|
+
}
|
|
300
|
+
function toChannels(eight, info) {
|
|
301
|
+
const pixels = info.width * info.height;
|
|
302
|
+
const { photometric, samples } = info;
|
|
303
|
+
if (photometric === 3) {
|
|
304
|
+
const rgb = new Uint8Array(pixels * 3);
|
|
305
|
+
const entries = info.palette.length / 3;
|
|
306
|
+
for (let i = 0; i < pixels; i++) {
|
|
307
|
+
const idx = eight[i * samples] ?? 0;
|
|
308
|
+
const e = info.bits < 8 ? Math.round(idx * ((1 << info.bits) - 1) / 255) : idx;
|
|
309
|
+
rgb[i * 3] = (info.palette[e] ?? 0) >> 8;
|
|
310
|
+
rgb[i * 3 + 1] = (info.palette[entries + e] ?? 0) >> 8;
|
|
311
|
+
rgb[i * 3 + 2] = (info.palette[entries * 2 + e] ?? 0) >> 8;
|
|
312
|
+
}
|
|
313
|
+
return {
|
|
314
|
+
width: info.width,
|
|
315
|
+
height: info.height,
|
|
316
|
+
channels: 3,
|
|
317
|
+
data: rgb
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
const color = photometric === 5 ? 4 : photometric === 2 ? 3 : 1;
|
|
321
|
+
if (samples < color) throw new Error("TIFF: too few samples for its colour");
|
|
322
|
+
const alphaAt = info.extra.length > 0 && samples > color ? color : -1;
|
|
323
|
+
const data = samples === color ? eight.subarray(0, pixels * color) : new Uint8Array(pixels * color);
|
|
324
|
+
const alpha = alphaAt >= 0 ? new Uint8Array(pixels) : void 0;
|
|
325
|
+
if (samples !== color) for (let i = 0; i < pixels; i++) {
|
|
326
|
+
for (let c = 0; c < color; c++) data[i * color + c] = eight[i * samples + c] ?? 0;
|
|
327
|
+
if (alpha) alpha[i] = eight[i * samples + alphaAt] ?? 255;
|
|
328
|
+
}
|
|
329
|
+
if (photometric === 0) for (let i = 0; i < data.length; i++) data[i] = 255 - data[i];
|
|
330
|
+
return {
|
|
331
|
+
width: info.width,
|
|
332
|
+
height: info.height,
|
|
333
|
+
channels: color === 4 ? 4 : color === 3 ? 3 : 1,
|
|
334
|
+
data,
|
|
335
|
+
...alpha ? { alpha } : {}
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
//#endregion
|
|
339
|
+
export { decodeTiff, isTiff };
|
|
@@ -97,6 +97,11 @@ export interface VectorShape {
|
|
|
97
97
|
* over `fillColorHex`.
|
|
98
98
|
*/
|
|
99
99
|
readonly fillGradient?: ShapeGradient;
|
|
100
|
+
/**
|
|
101
|
+
* §20.1.2.3.1 — the fill's opacity, `0..1`. Absent is opaque. Writers without
|
|
102
|
+
* transparency paint `fillColorHex` at full strength.
|
|
103
|
+
*/
|
|
104
|
+
readonly fillAlpha?: number;
|
|
100
105
|
/** Stroke description. Omitted = no stroke. */
|
|
101
106
|
readonly stroke?: StrokeStyle;
|
|
102
107
|
/**
|
|
@@ -873,6 +873,7 @@ function worksheetToBody(worksheet, sharedStrings, styles, date1904, print) {
|
|
|
873
873
|
const rowHeightTwips = scaled && baseRowProps !== void 0 ? Math.max(1, Math.round(baseRowProps.heightTwips * printScale)) : baseRowProps?.heightTwips;
|
|
874
874
|
const isTitleRow = print.titleRows !== void 0 && absR >= print.titleRows.startRow && absR <= print.titleRows.endRow;
|
|
875
875
|
const rowProps = {
|
|
876
|
+
cantSplit: true,
|
|
876
877
|
...rowHeightTwips !== void 0 ? {
|
|
877
878
|
height: twipsToPt(rowHeightTwips),
|
|
878
879
|
heightRule: baseRowProps?.heightRule ?? "atLeast"
|
|
@@ -1736,7 +1737,8 @@ function slicerTable(slicer) {
|
|
|
1736
1737
|
};
|
|
1737
1738
|
const rowProps = {
|
|
1738
1739
|
height: pt(SLICER_ROW_PT),
|
|
1739
|
-
heightRule: "atLeast"
|
|
1740
|
+
heightRule: "atLeast",
|
|
1741
|
+
cantSplit: true
|
|
1740
1742
|
};
|
|
1741
1743
|
const rows = [];
|
|
1742
1744
|
rows.push({
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { FEATURES } from "../core/ir/features.js";
|
|
2
2
|
import { detectImageFormat } from "../core/images.js";
|
|
3
|
+
import { PathBuilder, flipTransform, svgPathData } from "../core/vector.js";
|
|
3
4
|
import { EMPTY_STYLE_SHEET, resolveParagraphProperties, resolveRunProperties } from "../core/style-cascade/resolver.js";
|
|
4
5
|
import "../core/style-cascade/index.js";
|
|
5
|
-
import { PathBuilder, flipTransform, svgPathData } from "../core/vector.js";
|
|
6
6
|
import { buildSparkline } from "../core/drawingml/sparkline-geometry.js";
|
|
7
7
|
import { arcPoint, arcToBeziers } from "../core/arc-to-bezier.js";
|
|
8
8
|
import { buildChartScene } from "../core/drawingml/chart-geometry.js";
|
|
@@ -652,7 +652,8 @@ var MIME_BY_FORMAT = {
|
|
|
652
652
|
jpeg: "image/jpeg",
|
|
653
653
|
png: "image/png",
|
|
654
654
|
jpeg2000: "image/jp2",
|
|
655
|
-
gif: "image/gif"
|
|
655
|
+
gif: "image/gif",
|
|
656
|
+
tiff: "image/tiff"
|
|
656
657
|
};
|
|
657
658
|
function dataUri(resource, store) {
|
|
658
659
|
if (resource === void 0) return void 0;
|
|
@@ -2,8 +2,9 @@ import { BorderStyle, ImageCrop, PictureOutline, ShapeShadow } from '../core/doc
|
|
|
2
2
|
import { Pt, ResourceId, ResourceStore } from '../core/ir/index.js';
|
|
3
3
|
import { FontMeasure, ParsedTtf } from '../core/font/index.js';
|
|
4
4
|
import { ResolvedParagraphProperties, ResolvedRunProperties } from '../core/style-cascade/index.js';
|
|
5
|
-
import { PathSegment, VectorPath, VectorShape } from '../core/vector.js';
|
|
5
|
+
import { PathSegment, StrokeStyle, VectorPath, VectorShape } from '../core/vector.js';
|
|
6
6
|
import { PreparedImage } from '../core/images.js';
|
|
7
|
+
import { MetaPicture } from '../core/metafile/picture.js';
|
|
7
8
|
/** A font bound into a {@link LaidOutDocument}: the parsed face plus what layout/emit need from it. */
|
|
8
9
|
export interface FontResource {
|
|
9
10
|
readonly resourceName: string;
|
|
@@ -84,6 +85,17 @@ export interface ImageToken {
|
|
|
84
85
|
readonly outline?: PictureOutline;
|
|
85
86
|
/** §20.1.8.40 — the drop shadow under the picture, when it casts one. */
|
|
86
87
|
readonly shadow?: ShapeShadow;
|
|
88
|
+
/** §14.1.2.10 — the contrast/brightness wash the picture is drawn through. */
|
|
89
|
+
readonly wash?: {
|
|
90
|
+
readonly gain: number;
|
|
91
|
+
readonly black: number;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* MS-EMF / MS-WMF — the picture to DRAW in this token's box, for an inline
|
|
95
|
+
* metafile: it has no raster to place, so the emitter plays its primitives
|
|
96
|
+
* where the token stands.
|
|
97
|
+
*/
|
|
98
|
+
readonly metafile?: MetafileDrawing;
|
|
87
99
|
/** §20.1.8.55 `a:srcRect` — the part of the source the frame shows. */
|
|
88
100
|
readonly crop?: ImageCrop;
|
|
89
101
|
/** §20.1.7.6 — degrees clockwise about the box's centre. */
|
|
@@ -175,13 +187,39 @@ export interface Line {
|
|
|
175
187
|
readonly metricDescentPt?: number;
|
|
176
188
|
}
|
|
177
189
|
/** An image bound into a {@link LaidOutDocument}: its resource name plus the decoded/validated bytes. */
|
|
190
|
+
/**
|
|
191
|
+
* MS-EMF / MS-WMF — a metafile picture, ready to draw: paths and words in a
|
|
192
|
+
* local y-up frame whose origin is the picture box's bottom-left corner. The
|
|
193
|
+
* same vocabulary a chart's layout carries, because a metafile draws the same
|
|
194
|
+
* two things.
|
|
195
|
+
*/
|
|
196
|
+
export interface MetafileDrawing {
|
|
197
|
+
readonly shapes: ReadonlyArray<{
|
|
198
|
+
readonly paths: ReadonlyArray<VectorPath>;
|
|
199
|
+
readonly fillColorHex?: string;
|
|
200
|
+
readonly stroke?: StrokeStyle;
|
|
201
|
+
}>;
|
|
202
|
+
readonly texts: ReadonlyArray<{
|
|
203
|
+
readonly line: Line;
|
|
204
|
+
readonly x: number;
|
|
205
|
+
readonly y: number;
|
|
206
|
+
readonly rotationDeg?: number;
|
|
207
|
+
}>;
|
|
208
|
+
}
|
|
178
209
|
export interface ImageResource {
|
|
179
210
|
readonly resourceName: string;
|
|
180
211
|
/**
|
|
181
212
|
* Decoded/validated at layout time (the probe); the emit phase replays it
|
|
182
|
-
* without touching the source bytes again.
|
|
213
|
+
* without touching the source bytes again. Absent for a METAFILE, which has
|
|
214
|
+
* no raster to embed: it is drawn as the primitives `metafile` holds.
|
|
183
215
|
*/
|
|
184
|
-
readonly prepared
|
|
216
|
+
readonly prepared?: PreparedImage;
|
|
217
|
+
/**
|
|
218
|
+
* MS-EMF / MS-WMF — the picture read out of the resource, in its own logical
|
|
219
|
+
* units. A metafile is a little drawing program, not a raster; the layout
|
|
220
|
+
* turns these into the same primitives a chart is made of.
|
|
221
|
+
*/
|
|
222
|
+
readonly metafile?: MetaPicture;
|
|
185
223
|
}
|
|
186
224
|
/**
|
|
187
225
|
* The fields every {@link PageItem} shares (ir-design §6, frozen at stage 6.4):
|
|
@@ -271,6 +309,11 @@ export interface ImageItem extends PageItemBase {
|
|
|
271
309
|
readonly imageResourceName: string;
|
|
272
310
|
/** §20.1.8.55 `a:srcRect` — the part of the source the frame shows. */
|
|
273
311
|
readonly crop?: ImageCrop;
|
|
312
|
+
/** §14.1.2.10 — the contrast/brightness wash the picture is drawn through. */
|
|
313
|
+
readonly wash?: {
|
|
314
|
+
readonly gain: number;
|
|
315
|
+
readonly black: number;
|
|
316
|
+
};
|
|
274
317
|
/** §20.1.7.6 — degrees clockwise about the box's centre. */
|
|
275
318
|
readonly rotationDeg?: number;
|
|
276
319
|
/** §20.1.7.6 — the picture drawn mirrored in its box. */
|