reamkit 1.16.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/bidi/segments.js +1 -2
- package/dist/esm/core/converter/project.js +5 -1
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +526 -8
- package/dist/esm/core/drawingml/chart-geometry.d.ts +8 -12
- package/dist/esm/core/drawingml/chart-geometry.js +58 -9
- package/dist/esm/core/drawingml/chart-parser.js +44 -5
- package/dist/esm/core/drawingml/colors.d.ts +1 -1
- package/dist/esm/core/drawingml/colors.js +27 -1
- package/dist/esm/core/drawingml/preset-geometry.js +233 -0
- package/dist/esm/core/drawingml/shape-render.d.ts +12 -0
- package/dist/esm/core/drawingml/shape-render.js +116 -3
- package/dist/esm/core/drawingml/theme-parser.d.ts +10 -0
- package/dist/esm/core/drawingml/theme-parser.js +19 -1
- package/dist/esm/core/font/measure.d.ts +5 -0
- package/dist/esm/core/font/measure.js +21 -1
- package/dist/esm/core/images.d.ts +2 -2
- package/dist/esm/core/images.js +219 -0
- package/dist/esm/core/ir/flow.d.ts +23 -1
- 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/numbering/apply.d.ts +5 -3
- package/dist/esm/core/numbering/apply.js +42 -11
- package/dist/esm/core/numbering/index.js +2 -0
- package/dist/esm/core/numbering/state.d.ts +13 -1
- package/dist/esm/core/numbering/state.js +256 -15
- package/dist/esm/core/po-helpers.js +16 -1
- package/dist/esm/core/style-cascade/resolver.js +66 -11
- package/dist/esm/core/style-cascade/table.js +35 -11
- package/dist/esm/core/style-cascade/types.d.ts +42 -1
- package/dist/esm/core/style-cascade/types.js +4 -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 +22 -0
- package/dist/esm/excel/print-model.js +4 -2
- package/dist/esm/excel/sheet-to-flow.js +10 -8
- package/dist/esm/excel/worksheet-parser.js +1 -1
- package/dist/esm/html/html-writer.js +20 -5
- package/dist/esm/layout/page-doc.d.ts +107 -4
- package/dist/esm/layout/styled-layout.d.ts +44 -2
- package/dist/esm/layout/styled-layout.js +2330 -283
- package/dist/esm/pdf/shading.js +76 -6
- package/dist/esm/pdf/styled-page-emitter.js +294 -45
- package/dist/esm/pdf/vector-graphics.d.ts +29 -2
- package/dist/esm/pdf/vector-graphics.js +96 -12
- package/dist/esm/pdf-reader/image-decode.js +7 -75
- package/dist/esm/pptx/pptx-reader.js +1 -0
- package/dist/esm/pptx/slide-parser.d.ts +21 -4
- package/dist/esm/pptx/slide-parser.js +86 -23
- package/dist/esm/svg/svg-writer.js +2 -2
- package/dist/esm/word/document-parser.d.ts +96 -7
- package/dist/esm/word/document-parser.js +540 -116
- package/dist/esm/word/docx-reader.js +135 -43
- package/dist/esm/word/docx-writer.js +6 -1
- package/dist/esm/word/drawing-parser.d.ts +58 -5
- package/dist/esm/word/drawing-parser.js +1632 -63
- package/dist/esm/word/index.d.ts +4 -4
- package/dist/esm/word/numbering-parser.d.ts +2 -1
- package/dist/esm/word/numbering-parser.js +107 -21
- package/dist/esm/word/paragraph-properties.d.ts +11 -1
- package/dist/esm/word/paragraph-properties.js +219 -10
- package/dist/esm/word/run-properties.js +45 -1
- package/dist/esm/word/settings-parser.d.ts +24 -0
- package/dist/esm/word/settings-parser.js +31 -5
- package/dist/esm/word/shading.d.ts +11 -0
- package/dist/esm/word/shading.js +64 -0
- package/dist/esm/word/styles-parser.js +8 -2
- package/dist/esm/word/table-parser.d.ts +9 -1
- package/dist/esm/word/table-parser.js +182 -30
- package/package.json +1 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** One decoded TIFF image: 8-bit chunky samples plus an optional alpha plane. */
|
|
2
|
+
export interface DecodedTiff {
|
|
3
|
+
readonly width: number;
|
|
4
|
+
readonly height: number;
|
|
5
|
+
/** Samples per pixel in {@link data} — 1 (grey), 3 (RGB) or 4 (CMYK). */
|
|
6
|
+
readonly channels: 1 | 3 | 4;
|
|
7
|
+
/** Row-major 8-bit samples, `width * height * channels` long. */
|
|
8
|
+
readonly data: Uint8Array;
|
|
9
|
+
/** One byte per pixel when the file carries an alpha channel. */
|
|
10
|
+
readonly alpha?: Uint8Array;
|
|
11
|
+
}
|
|
12
|
+
/** Whether the bytes open with a TIFF header (§2: `II*\0` or `MM\0*`). */
|
|
13
|
+
export declare function isTiff(bytes: Uint8Array): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Decode the first image of a baseline TIFF into 8-bit chunky samples.
|
|
16
|
+
*
|
|
17
|
+
* @throws Error when the file is malformed or uses a feature outside baseline
|
|
18
|
+
* TIFF (planar separation, JPEG/fax compression, floating-point samples).
|
|
19
|
+
*/
|
|
20
|
+
export declare function decodeTiff(bytes: Uint8Array): DecodedTiff;
|
|
@@ -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 };
|
|
@@ -60,6 +60,23 @@ export interface ShapeGradient {
|
|
|
60
60
|
readonly kind: 'linear' | 'radial';
|
|
61
61
|
/** Linear only: gradient direction in degrees clockwise (0 = left→right). */
|
|
62
62
|
readonly angle?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Radial only: where the first stop sits inside the box, as fractions of it
|
|
65
|
+
* (`{x: 0, y: 0}` is the top-left corner). Absent means the middle, which is
|
|
66
|
+
* where DrawingML's own `a:path` puts it; VML aims it at a corner instead
|
|
67
|
+
* (`@focus` with an `@angle`), and fill.docx's page sweep starts in one.
|
|
68
|
+
*/
|
|
69
|
+
readonly center?: {
|
|
70
|
+
readonly x: number;
|
|
71
|
+
readonly y: number;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Radial only: the shape of the sweep's contours. Circles by default, which
|
|
75
|
+
* is what DrawingML's `a:path path="circle"` draws; VML's `gradientRadial`
|
|
76
|
+
* grows RECTANGLES out to the box instead, and so does the page background
|
|
77
|
+
* of fill.docx.
|
|
78
|
+
*/
|
|
79
|
+
readonly sweep?: 'circle' | 'rect';
|
|
63
80
|
readonly stops: ReadonlyArray<GradientStop>;
|
|
64
81
|
}
|
|
65
82
|
/**
|
|
@@ -80,6 +97,11 @@ export interface VectorShape {
|
|
|
80
97
|
* over `fillColorHex`.
|
|
81
98
|
*/
|
|
82
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;
|
|
83
105
|
/** Stroke description. Omitted = no stroke. */
|
|
84
106
|
readonly stroke?: StrokeStyle;
|
|
85
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"
|
|
@@ -1237,7 +1238,7 @@ function mapBorderStyle(style) {
|
|
|
1237
1238
|
sizeEighthPt: 18
|
|
1238
1239
|
};
|
|
1239
1240
|
case "dashed": return {
|
|
1240
|
-
style: "
|
|
1241
|
+
style: "dashSmallGap",
|
|
1241
1242
|
sizeEighthPt: 6
|
|
1242
1243
|
};
|
|
1243
1244
|
case "dashDot": return {
|
|
@@ -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({
|
|
@@ -6,6 +6,14 @@ import { buildHeaderFooterContent } from "./header-footer.js";
|
|
|
6
6
|
//#region src/excel/sheet-to-flow.ts
|
|
7
7
|
var HEADER_REL = "_xlsxHeaderDefault";
|
|
8
8
|
var FOOTER_REL = "_xlsxFooterDefault";
|
|
9
|
+
var PAGE_BREAK_PARAGRAPH = {
|
|
10
|
+
properties: {
|
|
11
|
+
pageBreakBefore: true,
|
|
12
|
+
spacingLine: pt(0),
|
|
13
|
+
spacingLineRule: "exact"
|
|
14
|
+
},
|
|
15
|
+
runs: []
|
|
16
|
+
};
|
|
9
17
|
/**
|
|
10
18
|
* Project a {@link SheetDoc} into a {@link FlowDoc} (E-SHEET SA2): each grid sheet
|
|
11
19
|
* becomes flow blocks (a table + chart/picture/shape/slicer frames, then comment
|
|
@@ -61,10 +69,7 @@ function projectSheetDoc(sheet, options = {}) {
|
|
|
61
69
|
sheetSections.push(sheetSection);
|
|
62
70
|
if (printed > 0) body.push({
|
|
63
71
|
kind: "paragraph",
|
|
64
|
-
paragraph:
|
|
65
|
-
properties: { pageBreakBefore: true },
|
|
66
|
-
runs: []
|
|
67
|
-
}
|
|
72
|
+
paragraph: PAGE_BREAK_PARAGRAPH
|
|
68
73
|
});
|
|
69
74
|
const drawings = [];
|
|
70
75
|
for (const ref of ws.charts ?? []) drawings.push({
|
|
@@ -96,10 +101,7 @@ function projectSheetDoc(sheet, options = {}) {
|
|
|
96
101
|
for (let band = 0; band < drawingBands.length; band++) {
|
|
97
102
|
if (band > 0) drawings.push({
|
|
98
103
|
kind: "paragraph",
|
|
99
|
-
paragraph:
|
|
100
|
-
properties: { pageBreakBefore: true },
|
|
101
|
-
runs: []
|
|
102
|
-
}
|
|
104
|
+
paragraph: PAGE_BREAK_PARAGRAPH
|
|
103
105
|
});
|
|
104
106
|
for (const shape of drawingBands[band]) drawings.push({
|
|
105
107
|
kind: "shape",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { INDEXED_COLORS } from "../core/indexed-colors.js";
|
|
2
|
+
import { resolveInternalEntities } from "../core/opc/xml-entities.js";
|
|
2
3
|
import { parseCellRef } from "./cell-reference.js";
|
|
3
4
|
import { parseAreaRef } from "./defined-name-ref.js";
|
|
4
|
-
import { resolveInternalEntities } from "../core/opc/xml-entities.js";
|
|
5
5
|
import { decodeXstring } from "./escaped-text.js";
|
|
6
6
|
import { parseDxf } from "./styles-parser.js";
|
|
7
7
|
import { XMLParser } from "fast-xml-parser";
|
|
@@ -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";
|
|
@@ -106,7 +106,11 @@ function collectReferencedAnchors(body) {
|
|
|
106
106
|
for (const el of els) if (el.kind === "paragraph") {
|
|
107
107
|
for (const r of el.paragraph.runs) if (r.anchor !== void 0) out.add(r.anchor);
|
|
108
108
|
} else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
|
|
109
|
-
else if (el.kind === "shape"
|
|
109
|
+
else if (el.kind === "shape") visitShape(el.shape);
|
|
110
|
+
};
|
|
111
|
+
const visitShape = (shape) => {
|
|
112
|
+
if (shape.text) visit(shape.text.content);
|
|
113
|
+
for (const child of shape.children ?? []) visitShape(child.shape);
|
|
110
114
|
};
|
|
111
115
|
visit(body);
|
|
112
116
|
return out;
|
|
@@ -122,7 +126,11 @@ function collectNoteNumbers(flow) {
|
|
|
122
126
|
if (r.commentRef !== void 0 && !comments.has(r.commentRef)) comments.set(r.commentRef, comments.size + 1);
|
|
123
127
|
}
|
|
124
128
|
else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
|
|
125
|
-
else if (el.kind === "shape"
|
|
129
|
+
else if (el.kind === "shape") visitShape(el.shape);
|
|
130
|
+
};
|
|
131
|
+
const visitShape = (shape) => {
|
|
132
|
+
if (shape.text) visit(shape.text.content);
|
|
133
|
+
for (const child of shape.children ?? []) visitShape(child.shape);
|
|
126
134
|
};
|
|
127
135
|
visit(flow.body);
|
|
128
136
|
return {
|
|
@@ -274,6 +282,11 @@ function hexToRgbCss(hex) {
|
|
|
274
282
|
return `${String(n >> 16 & 255)},${String(n >> 8 & 255)},${String(n & 255)}`;
|
|
275
283
|
}
|
|
276
284
|
function emitShapeBlock(out, shape, ctx) {
|
|
285
|
+
const children = shape.children ?? [];
|
|
286
|
+
if (!(children.length > 0 && shape.fill.kind === "none" && shape.line === void 0 && (shape.text?.content.length ?? 0) === 0)) emitOneShape(out, shape, ctx);
|
|
287
|
+
for (const child of children) emitShapeBlock(out, child.shape, ctx);
|
|
288
|
+
}
|
|
289
|
+
function emitOneShape(out, shape, ctx) {
|
|
277
290
|
const w = shape.width;
|
|
278
291
|
const h = shape.height;
|
|
279
292
|
const paths = buildShapePaths(shape.geometry, w, h);
|
|
@@ -620,7 +633,7 @@ function pieIconSvg(fill, color, empty) {
|
|
|
620
633
|
function pushBorder(css, side, border) {
|
|
621
634
|
if (!border || border.style === "none") return;
|
|
622
635
|
const width = border.width ?? .5;
|
|
623
|
-
const style = border.style === "double" ? "double" : border.style === "dotted" ? "dotted" : border.style === "dashed" ? "dashed" : "solid";
|
|
636
|
+
const style = border.style === "double" ? "double" : border.style === "dotted" ? "dotted" : border.style === "dashed" || border.style === "dashSmallGap" ? "dashed" : "solid";
|
|
624
637
|
css.push(`border-${side}:${fmt(width)}pt ${style} #${border.colorHex ?? "000000"}`);
|
|
625
638
|
}
|
|
626
639
|
function emitImageBlock(out, image, ctx) {
|
|
@@ -638,7 +651,9 @@ function emitImageBlock(out, image, ctx) {
|
|
|
638
651
|
var MIME_BY_FORMAT = {
|
|
639
652
|
jpeg: "image/jpeg",
|
|
640
653
|
png: "image/png",
|
|
641
|
-
jpeg2000: "image/jp2"
|
|
654
|
+
jpeg2000: "image/jp2",
|
|
655
|
+
gif: "image/gif",
|
|
656
|
+
tiff: "image/tiff"
|
|
642
657
|
};
|
|
643
658
|
function dataUri(resource, store) {
|
|
644
659
|
if (resource === void 0) return void 0;
|