reamkit 1.23.0 → 1.24.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/bmp.d.ts +33 -0
- package/dist/esm/core/bmp.js +276 -0
- package/dist/esm/core/document-model/types.d.ts +37 -0
- package/dist/esm/core/drawingml/chart-geometry.js +3 -2
- package/dist/esm/core/drawingml/diagram/colors.d.ts +52 -0
- package/dist/esm/core/drawingml/diagram/colors.js +125 -0
- package/dist/esm/core/drawingml/diagram/data-model.d.ts +47 -0
- package/dist/esm/core/drawingml/diagram/data-model.js +119 -0
- package/dist/esm/core/drawingml/diagram/layout-engine.d.ts +46 -0
- package/dist/esm/core/drawingml/diagram/layout-engine.js +948 -0
- package/dist/esm/core/drawingml/diagram/run.d.ts +37 -0
- package/dist/esm/core/drawingml/diagram/run.js +50 -0
- package/dist/esm/core/drawingml/diagram/to-drawing.d.ts +16 -0
- package/dist/esm/core/drawingml/diagram/to-drawing.js +86 -0
- package/dist/esm/core/drawingml/preset-geometry.js +116 -6
- package/dist/esm/core/drawingml/text-warp.d.ts +58 -0
- package/dist/esm/core/drawingml/text-warp.js +355 -0
- package/dist/esm/core/drawingml/theme-parser.js +9 -3
- package/dist/esm/core/font/measure.d.ts +12 -0
- package/dist/esm/core/font/measure.js +36 -0
- package/dist/esm/core/images.d.ts +30 -2
- package/dist/esm/core/images.js +121 -5
- package/dist/esm/core/metafile/emf.js +144 -16
- package/dist/esm/core/metafile/picture.d.ts +49 -0
- package/dist/esm/core/metafile/picture.js +70 -1
- package/dist/esm/core/metafile/wmf.js +53 -4
- package/dist/esm/core/ole/escher-blip.js +11 -1
- package/dist/esm/excel/print-model.js +2 -2
- package/dist/esm/excel/sheet-drawing.js +1 -1
- package/dist/esm/excel/sheet-to-flow.js +16 -2
- package/dist/esm/html/html-writer.js +2 -1
- package/dist/esm/layout/page-doc.d.ts +35 -0
- package/dist/esm/layout/page-doc.js +14 -1
- package/dist/esm/layout/styled-layout.js +176 -15
- package/dist/esm/pdf/shading.js +8 -6
- package/dist/esm/pdf/styled-page-emitter.js +42 -3
- package/dist/esm/pdf/vector-graphics.js +1 -1
- package/dist/esm/pdf-reader/image-decode.js +1 -1
- package/dist/esm/pptx/ppt/ppt-reader.js +95 -27
- package/dist/esm/pptx/ppt/ppt-text.d.ts +41 -0
- package/dist/esm/pptx/ppt/ppt-text.js +327 -37
- package/dist/esm/pptx/pptx-reader.js +90 -10
- package/dist/esm/pptx/slide-parser.d.ts +4 -1
- package/dist/esm/pptx/slide-parser.js +99 -13
- package/dist/esm/pptx/sp-helpers.d.ts +7 -3
- package/dist/esm/pptx/sp-helpers.js +45 -7
- package/dist/esm/pptx/table-style.d.ts +7 -0
- package/dist/esm/pptx/table-style.js +59 -7
- package/dist/esm/svg/svg-writer.js +1 -0
- package/dist/esm/word/document-parser.d.ts +4 -1
- package/dist/esm/word/document-parser.js +4 -1
- package/dist/esm/word/docx-reader.js +26 -13
- package/dist/esm/word/docx-writer.js +4 -0
- package/dist/esm/word/drawing-parser.d.ts +20 -0
- package/dist/esm/word/drawing-parser.js +37 -4
- package/dist/esm/word/table-parser.js +3 -0
- package/package.json +1 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** One decoded bitmap: chunky 8-bit samples, plus alpha when the file carries it. */
|
|
2
|
+
export interface DecodedBmp {
|
|
3
|
+
readonly width: number;
|
|
4
|
+
readonly height: number;
|
|
5
|
+
/** Row-major RGB samples, `width * height * 3` long. */
|
|
6
|
+
readonly data: Uint8Array;
|
|
7
|
+
/** One byte per pixel, present only when the file states real transparency. */
|
|
8
|
+
readonly alpha?: Uint8Array;
|
|
9
|
+
/** `biXPelsPerMeter`/`biYPelsPerMeter` turned into pixels per inch. */
|
|
10
|
+
readonly dpiX?: number;
|
|
11
|
+
readonly dpiY?: number;
|
|
12
|
+
}
|
|
13
|
+
/** Whether these bytes open with the `BM` signature of a bitmap FILE. */
|
|
14
|
+
export declare function isBmp(bytes: Uint8Array): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* MS-ODRAW §2.2.28 — a `BlipDIB`'s payload is a bitmap without its 14-byte
|
|
17
|
+
* BITMAPFILEHEADER, which the Escher record makes redundant. Put it back.
|
|
18
|
+
*
|
|
19
|
+
* @param dib The bytes from the DIB header onwards.
|
|
20
|
+
* @returns A complete BMP file, or `undefined` when the header does not read
|
|
21
|
+
* as a bitmap at all.
|
|
22
|
+
*/
|
|
23
|
+
export declare function dibToBmp(dib: Uint8Array): Uint8Array | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Decode a Windows bitmap into 8-bit RGB samples.
|
|
26
|
+
*
|
|
27
|
+
* @param bytes A complete BMP file (`BM` signature included).
|
|
28
|
+
* @returns The samples, their size and any alpha channel.
|
|
29
|
+
* @throws Error when the file is malformed, or carries a bitmap this does not
|
|
30
|
+
* read — a JPEG or PNG smuggled inside a DIB (`BI_JPEG`/`BI_PNG`),
|
|
31
|
+
* which is a whole other file wearing a bitmap header.
|
|
32
|
+
*/
|
|
33
|
+
export declare function decodeBmp(bytes: Uint8Array): DecodedBmp;
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
//#region src/core/bmp.ts
|
|
2
|
+
var FILE_HEADER_BYTES = 14;
|
|
3
|
+
var CORE_HEADER_BYTES = 12;
|
|
4
|
+
var INFO_HEADER_BYTES = 40;
|
|
5
|
+
var MAX_PIXELS = 4e7;
|
|
6
|
+
var BI_RGB = 0;
|
|
7
|
+
var BI_RLE8 = 1;
|
|
8
|
+
var BI_RLE4 = 2;
|
|
9
|
+
var BI_BITFIELDS = 3;
|
|
10
|
+
var BI_ALPHABITFIELDS = 6;
|
|
11
|
+
/** Whether these bytes open with the `BM` signature of a bitmap FILE. */
|
|
12
|
+
function isBmp(bytes) {
|
|
13
|
+
return bytes.length >= FILE_HEADER_BYTES && bytes[0] === 66 && bytes[1] === 77;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* MS-ODRAW §2.2.28 — a `BlipDIB`'s payload is a bitmap without its 14-byte
|
|
17
|
+
* BITMAPFILEHEADER, which the Escher record makes redundant. Put it back.
|
|
18
|
+
*
|
|
19
|
+
* @param dib The bytes from the DIB header onwards.
|
|
20
|
+
* @returns A complete BMP file, or `undefined` when the header does not read
|
|
21
|
+
* as a bitmap at all.
|
|
22
|
+
*/
|
|
23
|
+
function dibToBmp(dib) {
|
|
24
|
+
const head = readDibHeader(dib, 0);
|
|
25
|
+
if (!head) return void 0;
|
|
26
|
+
const offBits = FILE_HEADER_BYTES + head.headerBytes + head.maskBytes + head.paletteBytes;
|
|
27
|
+
const out = new Uint8Array(FILE_HEADER_BYTES + dib.length);
|
|
28
|
+
const view = new DataView(out.buffer);
|
|
29
|
+
out[0] = 66;
|
|
30
|
+
out[1] = 77;
|
|
31
|
+
view.setUint32(2, out.length, true);
|
|
32
|
+
view.setUint32(10, offBits, true);
|
|
33
|
+
out.set(dib, FILE_HEADER_BYTES);
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Decode a Windows bitmap into 8-bit RGB samples.
|
|
38
|
+
*
|
|
39
|
+
* @param bytes A complete BMP file (`BM` signature included).
|
|
40
|
+
* @returns The samples, their size and any alpha channel.
|
|
41
|
+
* @throws Error when the file is malformed, or carries a bitmap this does not
|
|
42
|
+
* read — a JPEG or PNG smuggled inside a DIB (`BI_JPEG`/`BI_PNG`),
|
|
43
|
+
* which is a whole other file wearing a bitmap header.
|
|
44
|
+
*/
|
|
45
|
+
function decodeBmp(bytes) {
|
|
46
|
+
if (!isBmp(bytes)) throw new Error("BMP: not a bitmap");
|
|
47
|
+
const file = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
48
|
+
const head = readDibHeader(bytes, FILE_HEADER_BYTES);
|
|
49
|
+
if (!head) throw new Error("BMP: unreadable header");
|
|
50
|
+
const { width, height, topDown, bitCount, compression } = head;
|
|
51
|
+
if (width <= 0 || height <= 0) throw new Error("BMP: empty bitmap");
|
|
52
|
+
if (width * height > MAX_PIXELS) throw new Error("BMP: image too large");
|
|
53
|
+
if (compression === 4 || compression === 5) throw new Error("BMP: embedded JPEG/PNG bitmaps are not supported");
|
|
54
|
+
const stated = file.getUint32(10, true);
|
|
55
|
+
const computed = FILE_HEADER_BYTES + head.headerBytes + head.maskBytes + head.paletteBytes;
|
|
56
|
+
const pixelsAt = stated >= computed && stated < bytes.length ? stated : computed;
|
|
57
|
+
const palette = readPalette(bytes, FILE_HEADER_BYTES + head.headerBytes, head);
|
|
58
|
+
const count = width * height;
|
|
59
|
+
const data = new Uint8Array(count * 3);
|
|
60
|
+
const rowOf = (y) => topDown ? y : height - 1 - y;
|
|
61
|
+
if (compression === BI_RLE8 || compression === BI_RLE4) {
|
|
62
|
+
const indices = decodeRle(bytes.subarray(pixelsAt), width, height, compression === BI_RLE4);
|
|
63
|
+
for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {
|
|
64
|
+
const at = (rowOf(y) * width + x) * 3;
|
|
65
|
+
const p = (indices[y * width + x] ?? 0) * 3;
|
|
66
|
+
data[at] = palette[p] ?? 0;
|
|
67
|
+
data[at + 1] = palette[p + 1] ?? 0;
|
|
68
|
+
data[at + 2] = palette[p + 2] ?? 0;
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
width,
|
|
72
|
+
height,
|
|
73
|
+
data,
|
|
74
|
+
...density(head)
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const stride = ((width * bitCount + 31) / 32 | 0) * 4;
|
|
78
|
+
const alpha = head.alphaMask !== 0 ? new Uint8Array(count) : void 0;
|
|
79
|
+
const shifts = {
|
|
80
|
+
r: maskShift(head.redMask),
|
|
81
|
+
g: maskShift(head.greenMask),
|
|
82
|
+
b: maskShift(head.blueMask),
|
|
83
|
+
a: maskShift(head.alphaMask)
|
|
84
|
+
};
|
|
85
|
+
for (let y = 0; y < height; y++) {
|
|
86
|
+
const row = pixelsAt + y * stride;
|
|
87
|
+
if (row + stride > bytes.length + stride) break;
|
|
88
|
+
for (let x = 0; x < width; x++) {
|
|
89
|
+
const at = (rowOf(y) * width + x) * 3;
|
|
90
|
+
if (bitCount <= 8) {
|
|
91
|
+
const p = paletteIndex(bytes, row, x, bitCount) * 3;
|
|
92
|
+
data[at] = palette[p] ?? 0;
|
|
93
|
+
data[at + 1] = palette[p + 1] ?? 0;
|
|
94
|
+
data[at + 2] = palette[p + 2] ?? 0;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (bitCount === 24) {
|
|
98
|
+
const o = row + x * 3;
|
|
99
|
+
data[at] = bytes[o + 2] ?? 0;
|
|
100
|
+
data[at + 1] = bytes[o + 1] ?? 0;
|
|
101
|
+
data[at + 2] = bytes[o] ?? 0;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const raw = bitCount === 16 ? (bytes[row + x * 2] ?? 0) | (bytes[row + x * 2 + 1] ?? 0) << 8 : ((bytes[row + x * 4] ?? 0) | (bytes[row + x * 4 + 1] ?? 0) << 8 | (bytes[row + x * 4 + 2] ?? 0) << 16 | (bytes[row + x * 4 + 3] ?? 0) << 24) >>> 0;
|
|
105
|
+
data[at] = channel(raw, head.redMask, shifts.r);
|
|
106
|
+
data[at + 1] = channel(raw, head.greenMask, shifts.g);
|
|
107
|
+
data[at + 2] = channel(raw, head.blueMask, shifts.b);
|
|
108
|
+
if (alpha) alpha[rowOf(y) * width + x] = channel(raw, head.alphaMask, shifts.a);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const opaque = alpha?.every((v) => v === 0) ?? true;
|
|
112
|
+
return {
|
|
113
|
+
width,
|
|
114
|
+
height,
|
|
115
|
+
data,
|
|
116
|
+
...alpha && !opaque ? { alpha } : {},
|
|
117
|
+
...density(head)
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function readDibHeader(bytes, at) {
|
|
121
|
+
if (bytes.length < at + 4) return void 0;
|
|
122
|
+
const v = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
123
|
+
const headerBytes = v.getUint32(at, true);
|
|
124
|
+
const core = headerBytes === CORE_HEADER_BYTES;
|
|
125
|
+
if (!core && headerBytes < INFO_HEADER_BYTES) return void 0;
|
|
126
|
+
if (bytes.length < at + headerBytes) return void 0;
|
|
127
|
+
const width = core ? v.getInt16(at + 4, true) : v.getInt32(at + 4, true);
|
|
128
|
+
const rawHeight = core ? v.getInt16(at + 6, true) : v.getInt32(at + 8, true);
|
|
129
|
+
const planes = core ? v.getUint16(at + 6, true) : v.getUint16(at + 12, true);
|
|
130
|
+
const bitCount = core ? v.getUint16(at + 8, true) : v.getUint16(at + 14, true);
|
|
131
|
+
const compression = core ? BI_RGB : v.getUint32(at + 16, true);
|
|
132
|
+
if (planes !== 1) return void 0;
|
|
133
|
+
if (![
|
|
134
|
+
1,
|
|
135
|
+
2,
|
|
136
|
+
4,
|
|
137
|
+
8,
|
|
138
|
+
16,
|
|
139
|
+
24,
|
|
140
|
+
32
|
|
141
|
+
].includes(bitCount)) return void 0;
|
|
142
|
+
if (compression > 6) return void 0;
|
|
143
|
+
if (width <= 0 || rawHeight === 0) return void 0;
|
|
144
|
+
const clrUsed = core ? 0 : v.getUint32(at + 32, true);
|
|
145
|
+
const inHeader = headerBytes >= 52;
|
|
146
|
+
const maskBytes = !inHeader && (compression === BI_BITFIELDS || compression === BI_ALPHABITFIELDS) ? compression === BI_ALPHABITFIELDS ? 16 : 12 : 0;
|
|
147
|
+
const maskAt = inHeader ? at + 40 : at + headerBytes;
|
|
148
|
+
const stated = compression === BI_BITFIELDS || compression === BI_ALPHABITFIELDS || inHeader;
|
|
149
|
+
const readMask = (i) => stated && bytes.length >= maskAt + (i + 1) * 4 ? v.getUint32(maskAt + i * 4, true) : 0;
|
|
150
|
+
const defaults = bitCount === 16 ? [
|
|
151
|
+
31744,
|
|
152
|
+
992,
|
|
153
|
+
31
|
|
154
|
+
] : [
|
|
155
|
+
16711680,
|
|
156
|
+
65280,
|
|
157
|
+
255
|
|
158
|
+
];
|
|
159
|
+
const red = readMask(0) || defaults[0];
|
|
160
|
+
const green = readMask(1) || defaults[1];
|
|
161
|
+
const blue = readMask(2) || defaults[2];
|
|
162
|
+
const alphaMask = headerBytes >= 56 && bytes.length >= maskAt + 16 ? v.getUint32(maskAt + 12, true) : 0;
|
|
163
|
+
const paletteEntryBytes = core ? 3 : 4;
|
|
164
|
+
return {
|
|
165
|
+
headerBytes,
|
|
166
|
+
maskBytes,
|
|
167
|
+
paletteBytes: (bitCount <= 8 ? clrUsed || 1 << bitCount : 0) * paletteEntryBytes,
|
|
168
|
+
paletteEntryBytes,
|
|
169
|
+
width,
|
|
170
|
+
height: Math.abs(rawHeight),
|
|
171
|
+
topDown: rawHeight < 0,
|
|
172
|
+
bitCount,
|
|
173
|
+
compression,
|
|
174
|
+
redMask: red,
|
|
175
|
+
greenMask: green,
|
|
176
|
+
blueMask: blue,
|
|
177
|
+
alphaMask,
|
|
178
|
+
pxPerMeterX: core ? 0 : v.getInt32(at + 24, true),
|
|
179
|
+
pxPerMeterY: core ? 0 : v.getInt32(at + 28, true)
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/** The palette as flat RGB triples, whichever entry width the header uses. */
|
|
183
|
+
function readPalette(bytes, at, head) {
|
|
184
|
+
const entries = head.paletteBytes / head.paletteEntryBytes;
|
|
185
|
+
const out = new Uint8Array(entries * 3);
|
|
186
|
+
for (let i = 0; i < entries; i++) {
|
|
187
|
+
const o = at + i * head.paletteEntryBytes;
|
|
188
|
+
out[i * 3] = bytes[o + 2] ?? 0;
|
|
189
|
+
out[i * 3 + 1] = bytes[o + 1] ?? 0;
|
|
190
|
+
out[i * 3 + 2] = bytes[o] ?? 0;
|
|
191
|
+
}
|
|
192
|
+
return out;
|
|
193
|
+
}
|
|
194
|
+
/** The palette index of one pixel of a 1/2/4/8-bit row. */
|
|
195
|
+
function paletteIndex(bytes, row, x, bitCount) {
|
|
196
|
+
if (bitCount === 8) return bytes[row + x] ?? 0;
|
|
197
|
+
const perByte = 8 / bitCount;
|
|
198
|
+
return (bytes[row + (x / perByte | 0)] ?? 0) >> 8 - bitCount * (x % perByte + 1) & (1 << bitCount) - 1;
|
|
199
|
+
}
|
|
200
|
+
/** How far right a mask's field sits, so its bits can be read off. */
|
|
201
|
+
function maskShift(mask) {
|
|
202
|
+
if (mask === 0) return 0;
|
|
203
|
+
let shift = 0;
|
|
204
|
+
while ((mask >>> shift & 1) === 0) shift++;
|
|
205
|
+
return shift;
|
|
206
|
+
}
|
|
207
|
+
/** One channel of a packed pixel, stretched to the full byte range. */
|
|
208
|
+
function channel(raw, mask, shift) {
|
|
209
|
+
if (mask === 0) return 0;
|
|
210
|
+
const width = (mask >>> shift).toString(2).length;
|
|
211
|
+
const value = (raw & mask) >>> shift;
|
|
212
|
+
const max = (1 << width) - 1;
|
|
213
|
+
return max === 0 ? 0 : Math.round(value * 255 / max);
|
|
214
|
+
}
|
|
215
|
+
/** §biXPelsPerMeter — the resolution the bitmap claims, in pixels per inch. */
|
|
216
|
+
function density(head) {
|
|
217
|
+
if (head.pxPerMeterX <= 0 || head.pxPerMeterY <= 0) return {};
|
|
218
|
+
return {
|
|
219
|
+
dpiX: head.pxPerMeterX * .0254,
|
|
220
|
+
dpiY: head.pxPerMeterY * .0254
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* §BI_RLE8 / §BI_RLE4 — run-length rows of palette indices.
|
|
225
|
+
*
|
|
226
|
+
* Each pair is either a run (a non-zero count and the index to repeat) or an
|
|
227
|
+
* escape (a zero count and a code): 0 ends the row, 1 ends the bitmap, 2 is a
|
|
228
|
+
* delta that skips forward, and anything else is that many literal pixels,
|
|
229
|
+
* padded to a two-byte boundary.
|
|
230
|
+
*
|
|
231
|
+
* @param data The bytes from the first run onwards.
|
|
232
|
+
* @param width The bitmap's width in pixels.
|
|
233
|
+
* @param height Its height in rows.
|
|
234
|
+
* @param four Whether this is the four-bit form, which packs two indices per byte.
|
|
235
|
+
* @returns One palette index per pixel, top row first.
|
|
236
|
+
*/
|
|
237
|
+
function decodeRle(data, width, height, four) {
|
|
238
|
+
const out = new Uint8Array(width * height);
|
|
239
|
+
let x = 0;
|
|
240
|
+
let y = 0;
|
|
241
|
+
let at = 0;
|
|
242
|
+
const put = (index) => {
|
|
243
|
+
if (x < width && y < height) out[y * width + x] = index;
|
|
244
|
+
x++;
|
|
245
|
+
};
|
|
246
|
+
while (at + 1 < data.length && y < height) {
|
|
247
|
+
const count = data[at];
|
|
248
|
+
const value = data[at + 1];
|
|
249
|
+
at += 2;
|
|
250
|
+
if (count > 0) {
|
|
251
|
+
for (let i = 0; i < count; i++) put(four ? i % 2 === 0 ? value >> 4 : value & 15 : value);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (value === 0) {
|
|
255
|
+
x = 0;
|
|
256
|
+
y++;
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
if (value === 1) break;
|
|
260
|
+
if (value === 2) {
|
|
261
|
+
x += data[at] ?? 0;
|
|
262
|
+
y += data[at + 1] ?? 0;
|
|
263
|
+
at += 2;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
const bytesUsed = four ? (value + 1) / 2 | 0 : value;
|
|
267
|
+
for (let i = 0; i < value; i++) {
|
|
268
|
+
const byte = data[at + (four ? i / 2 | 0 : i)] ?? 0;
|
|
269
|
+
put(four ? i % 2 === 0 ? byte >> 4 : byte & 15 : byte);
|
|
270
|
+
}
|
|
271
|
+
at += bytesUsed + bytesUsed % 2;
|
|
272
|
+
}
|
|
273
|
+
return out;
|
|
274
|
+
}
|
|
275
|
+
//#endregion
|
|
276
|
+
export { decodeBmp, dibToBmp, isBmp };
|
|
@@ -787,6 +787,13 @@ export interface CellProperties {
|
|
|
787
787
|
* is a word-processor table's default.
|
|
788
788
|
*/
|
|
789
789
|
readonly verticalAlign?: 'top' | 'center' | 'bottom';
|
|
790
|
+
/**
|
|
791
|
+
* §17.4.71 `w:textDirection` / §21.1.3.17 `a:tcPr@vert` — the cell's text is
|
|
792
|
+
* turned a quarter: `vert` reads top-to-bottom (turned clockwise), `vert270`
|
|
793
|
+
* bottom-to-top. Its lines then run along the cell's HEIGHT and stack across
|
|
794
|
+
* its width, which is what makes a narrow header column readable.
|
|
795
|
+
*/
|
|
796
|
+
readonly textDirection?: 'vert' | 'vert270';
|
|
790
797
|
}
|
|
791
798
|
/** §17.4.81 `w:trPr` — a table row's properties: height, split/header flags. */
|
|
792
799
|
export interface RowProperties {
|
|
@@ -1043,6 +1050,16 @@ export interface ShapeFill {
|
|
|
1043
1050
|
readonly sx: number;
|
|
1044
1051
|
readonly sy: number;
|
|
1045
1052
|
};
|
|
1053
|
+
/**
|
|
1054
|
+
* Where the grid of copies is anchored. DrawingML pins it to the shape's
|
|
1055
|
+
* top-left corner (§20.1.8.58 `a:tile @algn`, which defaults to `tl`); an
|
|
1056
|
+
* MS-ODRAW texture fill (§2.3.7.13 `fillOriginX`/`fillOriginY`, both
|
|
1057
|
+
* defaulting to nothing) centres it on the shape instead. It only shows when
|
|
1058
|
+
* the copies do not divide the box evenly — and it shows most when ONE copy
|
|
1059
|
+
* is larger than the box, where the difference is which part of the picture
|
|
1060
|
+
* is visible at all.
|
|
1061
|
+
*/
|
|
1062
|
+
readonly tileFromCentre?: boolean;
|
|
1046
1063
|
/**
|
|
1047
1064
|
* §20.1.8.14 `a:blipFill` — the picture painted across the shape's box. A
|
|
1048
1065
|
* DrawingML picture IS a shape with one of these, which is how a `pic:pic`
|
|
@@ -1162,6 +1179,26 @@ export interface ShapeTextBody {
|
|
|
1162
1179
|
* WordArt is set at whatever size fills the box it was drawn in.
|
|
1163
1180
|
*/
|
|
1164
1181
|
readonly fitToBox?: boolean;
|
|
1182
|
+
/**
|
|
1183
|
+
* §20.1.9.10 `a:prstTxWarp` — DrawingML WordArt: the preset curve the text is
|
|
1184
|
+
* bent through, and the `a:avLst` `adj` guide when the file states one. The
|
|
1185
|
+
* warped text does not wrap and is stretched to fill the shape's box, so the
|
|
1186
|
+
* size its runs state stops deciding how large it is drawn. `textNoShape` is
|
|
1187
|
+
* not carried: it is the enumeration's "no warp" member, and a body under it
|
|
1188
|
+
* is an ordinary text box.
|
|
1189
|
+
*/
|
|
1190
|
+
readonly warp?: {
|
|
1191
|
+
readonly preset: string;
|
|
1192
|
+
readonly adjust?: number;
|
|
1193
|
+
};
|
|
1194
|
+
/**
|
|
1195
|
+
* §20.1.10.42 `a:normAutofit` — the text SHRINKS to fit the box, and only
|
|
1196
|
+
* shrinks: a size that already fits is left alone. PowerPoint writes the
|
|
1197
|
+
* scale it settled on into `@fontScale` and that is applied at parse; this
|
|
1198
|
+
* is for the text that arrives without one, which is every box of a diagram
|
|
1199
|
+
* laid out from its layout part rather than from a cached drawing.
|
|
1200
|
+
*/
|
|
1201
|
+
readonly shrinkToFit?: boolean;
|
|
1165
1202
|
/**
|
|
1166
1203
|
* `wps:txbx @id` / `wps:linkedTxbx @id @seq` — the chain of boxes this one
|
|
1167
1204
|
* belongs to. Text that overruns a box continues in the next of its chain;
|
|
@@ -875,9 +875,10 @@ function buildPieScene(chart, wPt, hPt, measure) {
|
|
|
875
875
|
});
|
|
876
876
|
const mid = ang + sweep / 2;
|
|
877
877
|
const pct = Math.round(v / total * 100);
|
|
878
|
+
const own = chart.showValues ? fmtDataLabel(chart, v) : `${pct}%`;
|
|
878
879
|
const custom = series.pointLabels?.find((l) => l.idx === i)?.text;
|
|
879
|
-
if (custom !== void 0 || pct >= 5) labels.push({
|
|
880
|
-
text: custom ??
|
|
880
|
+
if (custom !== void 0 || chart.showValues || pct >= 5) labels.push({
|
|
881
|
+
text: custom ?? own,
|
|
881
882
|
x: cx + Math.cos(mid) * labelR,
|
|
882
883
|
y: cy + Math.sin(mid) * labelR - 9 / 3,
|
|
883
884
|
sizePt: 9,
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { PoNode } from '../../po-helpers.js';
|
|
2
|
+
/** The colours part, as the drawing writer asks it. */
|
|
3
|
+
export declare class DiagramColors {
|
|
4
|
+
private readonly byLabel;
|
|
5
|
+
constructor(tree: ReadonlyArray<PoNode>);
|
|
6
|
+
/**
|
|
7
|
+
* The `a:solidFill` for a box, or undefined when the part states none.
|
|
8
|
+
*
|
|
9
|
+
* @param label The layout node's `styleLbl`.
|
|
10
|
+
* @param index The box's place in the run of siblings.
|
|
11
|
+
*/
|
|
12
|
+
fill(label: string | undefined, index: number): string | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Whether the part states this label at all.
|
|
15
|
+
*
|
|
16
|
+
* A label it names but gives no fill — `revTx`, the reversed text of a
|
|
17
|
+
* process arrow — is a box with NO fill, which is not the same as a file
|
|
18
|
+
* that ships no colours and falls back to one accent for everything.
|
|
19
|
+
*/
|
|
20
|
+
knows(label: string | undefined): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* The `a:ln` for a box's OUTLINE, where the part states one.
|
|
23
|
+
*
|
|
24
|
+
* A follower box is often a near-white wash with the node's own accent drawn
|
|
25
|
+
* round it (`conFgAcc1` is `lt1` at 90% inside an `accent1` line): without the
|
|
26
|
+
* line it is a white rectangle on a white slide and reads as nothing at all.
|
|
27
|
+
*
|
|
28
|
+
* @param label The layout node's `styleLbl`.
|
|
29
|
+
* @param index The box's place in the run of siblings.
|
|
30
|
+
*/
|
|
31
|
+
line(label: string | undefined, index: number): string | undefined;
|
|
32
|
+
/** The `a:solidFill` for a box's TEXT, where the part overrides the style. */
|
|
33
|
+
textFill(label: string | undefined, index: number): string | undefined;
|
|
34
|
+
private pick;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* §21.4.2.20 `dgm:pt/dgm:spPr` — the formatting the AUTHOR set on one point,
|
|
38
|
+
* which beats whatever the colours part gives that point's style label.
|
|
39
|
+
*
|
|
40
|
+
* A cycle-matrix whose second quarter was recoloured by hand states it here and
|
|
41
|
+
* nowhere else: read only from the colours part, all four quarters came out the
|
|
42
|
+
* one accent the label names, and the orange quarter and the orange outline
|
|
43
|
+
* round its callout both vanished.
|
|
44
|
+
*
|
|
45
|
+
* @param spPr The point's `dgm:spPr`, when it has one.
|
|
46
|
+
* @returns The fill and the outline it overrides, each as DrawingML, when it
|
|
47
|
+
* overrides them.
|
|
48
|
+
*/
|
|
49
|
+
export declare function pointPaint(spPr: PoNode | undefined): {
|
|
50
|
+
fill?: string;
|
|
51
|
+
line?: string;
|
|
52
|
+
};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { poAttr, poChildren, poIs, poTag } from "../../po-helpers.js";
|
|
2
|
+
//#region src/core/drawingml/diagram/colors.ts
|
|
3
|
+
/** The colours part, as the drawing writer asks it. */
|
|
4
|
+
var DiagramColors = class {
|
|
5
|
+
byLabel = /* @__PURE__ */ new Map();
|
|
6
|
+
constructor(tree) {
|
|
7
|
+
const def = findDescendant(tree, "dgm:colorsDef");
|
|
8
|
+
for (const lbl of def ? poChildren(def) : []) {
|
|
9
|
+
if (!poIs(lbl, "dgm:styleLbl")) continue;
|
|
10
|
+
const name = poAttr(lbl, "name");
|
|
11
|
+
if (name === void 0) continue;
|
|
12
|
+
const kids = poChildren(lbl);
|
|
13
|
+
this.byLabel.set(name, {
|
|
14
|
+
...run(kids, "dgm:fillClrLst", "fill"),
|
|
15
|
+
...run(kids, "dgm:linClrLst", "line"),
|
|
16
|
+
...run(kids, "dgm:txFillClrLst", "txFill")
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The `a:solidFill` for a box, or undefined when the part states none.
|
|
22
|
+
*
|
|
23
|
+
* @param label The layout node's `styleLbl`.
|
|
24
|
+
* @param index The box's place in the run of siblings.
|
|
25
|
+
*/
|
|
26
|
+
fill(label, index) {
|
|
27
|
+
return solid(this.pick(label, "fill", index));
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Whether the part states this label at all.
|
|
31
|
+
*
|
|
32
|
+
* A label it names but gives no fill — `revTx`, the reversed text of a
|
|
33
|
+
* process arrow — is a box with NO fill, which is not the same as a file
|
|
34
|
+
* that ships no colours and falls back to one accent for everything.
|
|
35
|
+
*/
|
|
36
|
+
knows(label) {
|
|
37
|
+
return label !== void 0 && this.byLabel.has(label);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The `a:ln` for a box's OUTLINE, where the part states one.
|
|
41
|
+
*
|
|
42
|
+
* A follower box is often a near-white wash with the node's own accent drawn
|
|
43
|
+
* round it (`conFgAcc1` is `lt1` at 90% inside an `accent1` line): without the
|
|
44
|
+
* line it is a white rectangle on a white slide and reads as nothing at all.
|
|
45
|
+
*
|
|
46
|
+
* @param label The layout node's `styleLbl`.
|
|
47
|
+
* @param index The box's place in the run of siblings.
|
|
48
|
+
*/
|
|
49
|
+
line(label, index) {
|
|
50
|
+
const fill = solid(this.pick(label, "line", index));
|
|
51
|
+
return fill === void 0 ? void 0 : `<a:ln>${fill}</a:ln>`;
|
|
52
|
+
}
|
|
53
|
+
/** The `a:solidFill` for a box's TEXT, where the part overrides the style. */
|
|
54
|
+
textFill(label, index) {
|
|
55
|
+
return solid(this.pick(label, "txFill", index));
|
|
56
|
+
}
|
|
57
|
+
pick(label, which, index) {
|
|
58
|
+
const colors = (label === void 0 ? void 0 : this.byLabel.get(label)?.[which])?.colors ?? [];
|
|
59
|
+
if (colors.length === 0) return void 0;
|
|
60
|
+
return colors[(index % colors.length + colors.length) % colors.length];
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
function run(kids, tag, as) {
|
|
64
|
+
const list = kids.find((c) => poIs(c, tag));
|
|
65
|
+
const colors = list ? poChildren(list).filter((c) => poTag(c)?.endsWith("Clr") === true) : [];
|
|
66
|
+
if (colors.length === 0) return {};
|
|
67
|
+
return { [as]: {
|
|
68
|
+
meth: poAttr(list, "meth") ?? "repeat",
|
|
69
|
+
colors
|
|
70
|
+
} };
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* §21.4.2.20 `dgm:pt/dgm:spPr` — the formatting the AUTHOR set on one point,
|
|
74
|
+
* which beats whatever the colours part gives that point's style label.
|
|
75
|
+
*
|
|
76
|
+
* A cycle-matrix whose second quarter was recoloured by hand states it here and
|
|
77
|
+
* nowhere else: read only from the colours part, all four quarters came out the
|
|
78
|
+
* one accent the label names, and the orange quarter and the orange outline
|
|
79
|
+
* round its callout both vanished.
|
|
80
|
+
*
|
|
81
|
+
* @param spPr The point's `dgm:spPr`, when it has one.
|
|
82
|
+
* @returns The fill and the outline it overrides, each as DrawingML, when it
|
|
83
|
+
* overrides them.
|
|
84
|
+
*/
|
|
85
|
+
function pointPaint(spPr) {
|
|
86
|
+
const kids = poChildren(spPr);
|
|
87
|
+
const fillNode = kids.find((c) => poIs(c, "a:solidFill") || poIs(c, "a:noFill") || poIs(c, "a:gradFill"));
|
|
88
|
+
const colorIn = (node) => poChildren(node).find((c) => poTag(c)?.endsWith("Clr") === true);
|
|
89
|
+
const fill = poIs(fillNode, "a:noFill") ? "<a:noFill/>" : poIs(fillNode, "a:solidFill") ? solid(colorIn(fillNode)) : void 0;
|
|
90
|
+
const ln = kids.find((c) => poIs(c, "a:ln"));
|
|
91
|
+
const lnColor = solid(colorIn(ln ? poChildren(ln).find((c) => poIs(c, "a:solidFill")) : void 0));
|
|
92
|
+
const w = poAttr(ln, "w");
|
|
93
|
+
const line = ln === void 0 ? void 0 : `<a:ln${w !== void 0 ? ` w="${esc(w)}"` : ""}>${lnColor ?? ""}</a:ln>`;
|
|
94
|
+
return {
|
|
95
|
+
...fill !== void 0 ? { fill } : {},
|
|
96
|
+
...line !== void 0 ? { line } : {}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function solid(color) {
|
|
100
|
+
const xml = color === void 0 ? "" : colorXml(color);
|
|
101
|
+
return xml === "" ? void 0 : `<a:solidFill>${xml}</a:solidFill>`;
|
|
102
|
+
}
|
|
103
|
+
function colorXml(color) {
|
|
104
|
+
const tag = poTag(color);
|
|
105
|
+
if (tag === void 0) return "";
|
|
106
|
+
const val = poAttr(color, "val");
|
|
107
|
+
const mods = poChildren(color).map((m) => {
|
|
108
|
+
const t = poTag(m);
|
|
109
|
+
const v = poAttr(m, "val");
|
|
110
|
+
return t === void 0 || v === void 0 ? "" : `<${t} val="${esc(v)}"/>`;
|
|
111
|
+
}).join("");
|
|
112
|
+
return `${val === void 0 ? `<${tag}>` : `<${tag} val="${esc(val)}">`}${mods}</${tag}>`;
|
|
113
|
+
}
|
|
114
|
+
function esc(s) {
|
|
115
|
+
return s.replace(/&/gu, "&").replace(/</gu, "<").replace(/"/gu, """);
|
|
116
|
+
}
|
|
117
|
+
function findDescendant(tree, tag) {
|
|
118
|
+
for (const node of tree) {
|
|
119
|
+
if (poIs(node, tag)) return node;
|
|
120
|
+
const found = findDescendant(poChildren(node), tag);
|
|
121
|
+
if (found) return found;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
125
|
+
export { DiagramColors, pointPaint };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { PoNode } from '../../po-helpers.js';
|
|
2
|
+
/** §21.4.2.27 `dgm:pt@type` — what a point is in the model. */
|
|
3
|
+
export type PointType = 'doc' | 'node' | 'asst' | 'parTrans' | 'sibTrans' | 'pres';
|
|
4
|
+
/** One point of the data model. */
|
|
5
|
+
export interface DiagramPoint {
|
|
6
|
+
readonly id: string;
|
|
7
|
+
readonly type: PointType;
|
|
8
|
+
/** `dgm:t` — the point's text, as the `a:txBody` every other reader takes. */
|
|
9
|
+
readonly text?: PoNode;
|
|
10
|
+
/** `dgm:spPr` — the formatting the user overrode on this node. */
|
|
11
|
+
readonly spPr?: PoNode;
|
|
12
|
+
/** `dgm:prSet` — presentation properties (placeholder text, custom sizes). */
|
|
13
|
+
readonly prSet?: PoNode;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The data part as the layout engine asks about it: the point tree recovered
|
|
17
|
+
* from the connection list, plus the transition points that go with each edge.
|
|
18
|
+
*/
|
|
19
|
+
export declare class DiagramData {
|
|
20
|
+
private readonly byId;
|
|
21
|
+
private readonly kids;
|
|
22
|
+
/** The document point every layout starts from. */
|
|
23
|
+
readonly root: DiagramPoint | undefined;
|
|
24
|
+
/** §21.4.2.4 `dgm:bg` — the fill painted behind the whole diagram. */
|
|
25
|
+
readonly background: PoNode | undefined;
|
|
26
|
+
constructor(tree: ReadonlyArray<PoNode>);
|
|
27
|
+
/** The point with this id, if the model holds one. */
|
|
28
|
+
point(id: string): DiagramPoint | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* The children of a point, in the order the connections give them.
|
|
31
|
+
*
|
|
32
|
+
* @param id The parent's model id.
|
|
33
|
+
* @param ptType Which kind of child to return — `node` skips the
|
|
34
|
+
* transitions, `sibTrans` returns them instead.
|
|
35
|
+
*/
|
|
36
|
+
children(id: string, ptType?: 'node' | 'sibTrans' | 'parTrans'): Array<DiagramPoint>;
|
|
37
|
+
/**
|
|
38
|
+
* How many generations of `node` points the model holds below its root.
|
|
39
|
+
*
|
|
40
|
+
* §21.4.3.3 — a layout branches on this (`func="maxDepth" op="gte" val="2"`):
|
|
41
|
+
* the same part lays a chevron out as one box when the nodes have no
|
|
42
|
+
* children and as a labelled box over a body when they do.
|
|
43
|
+
*/
|
|
44
|
+
get depth(): number;
|
|
45
|
+
/** The plain text of a point's `dgm:t`, paragraphs joined by newlines. */
|
|
46
|
+
static textOf(pt: DiagramPoint | undefined): string;
|
|
47
|
+
}
|