pptx-ts 0.0.1 → 4.0.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/README.md +116 -269
- package/dist/autopage-odPz7zZO.js +536 -0
- package/dist/autopage-odPz7zZO.js.map +1 -0
- package/dist/binary-string-CKVAYI3M.js +24 -0
- package/dist/binary-string-CKVAYI3M.js.map +1 -0
- package/dist/body-insets-Dw_BeWD_.js +54 -0
- package/dist/body-insets-Dw_BeWD_.js.map +1 -0
- package/dist/browser.d.ts +29 -9
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +41 -14
- package/dist/browser.js.map +1 -1
- package/dist/clip-Bl01BPcN.d.ts +67 -0
- package/dist/clip-Bl01BPcN.d.ts.map +1 -0
- package/dist/diagnostics-D8ot4Ei7.d.ts +244 -0
- package/dist/diagnostics-D8ot4Ei7.d.ts.map +1 -0
- package/dist/embedded-fonts-DarT9aoa.d.ts +20 -0
- package/dist/embedded-fonts-DarT9aoa.d.ts.map +1 -0
- package/dist/entry-compose-DCnvBZz2.d.ts +1046 -0
- package/dist/entry-compose-DCnvBZz2.d.ts.map +1 -0
- package/dist/{enums-CvRYniUA.d.ts → enums-BUKTUB6V.d.ts} +21 -13
- package/dist/{enums-CvRYniUA.d.ts.map → enums-BUKTUB6V.d.ts.map} +1 -1
- package/dist/{errors-Bvln0H-7.js → errors-C4oENPcu.js} +92 -2
- package/dist/errors-C4oENPcu.js.map +1 -0
- package/dist/families.d.ts +260 -0
- package/dist/families.d.ts.map +1 -0
- package/dist/families.js +57 -0
- package/dist/families.js.map +1 -0
- package/dist/{fit-BKs0zxMI.js → fit-Cb7naFj6.js} +249 -167
- package/dist/fit-Cb7naFj6.js.map +1 -0
- package/dist/font-metrics-v5fn7s7e.d.ts +613 -0
- package/dist/font-metrics-v5fn7s7e.d.ts.map +1 -0
- package/dist/grid-DikTjFey.js +187 -0
- package/dist/grid-DikTjFey.js.map +1 -0
- package/dist/{html-dom-B0I0QSbU.js → html-dom-Iuog2wdK.js} +121 -91
- package/dist/html-dom-Iuog2wdK.js.map +1 -0
- package/dist/html.d.ts +6 -6
- package/dist/html.d.ts.map +1 -1
- package/dist/html.js +7 -6
- package/dist/html.js.map +1 -1
- package/dist/image-size-CvN2iCA4.js +1611 -0
- package/dist/image-size-CvN2iCA4.js.map +1 -0
- package/dist/index.d.ts +24 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -11
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +40 -31
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +40 -48
- package/dist/inspect.js.map +1 -1
- package/dist/math.d.ts +7 -5
- package/dist/math.d.ts.map +1 -1
- package/dist/math.js +16 -7
- package/dist/math.js.map +1 -1
- package/dist/measure.d.ts +12 -225
- package/dist/measure.d.ts.map +1 -1
- package/dist/measure.js +3 -3
- package/dist/node.d.ts +22 -8
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +27 -7
- package/dist/node.js.map +1 -1
- package/dist/notes-Dh12JAbn.js +3264 -0
- package/dist/notes-Dh12JAbn.js.map +1 -0
- package/dist/options-internal-WqgMf720.js +43 -0
- package/dist/options-internal-WqgMf720.js.map +1 -0
- package/dist/{package-CaOLXSNi.d.ts → package-J49Ck6S2.d.ts} +27 -12
- package/dist/package-J49Ck6S2.d.ts.map +1 -0
- package/dist/playbtn-CUqQdoDf.js +22 -0
- package/dist/playbtn-CUqQdoDf.js.map +1 -0
- package/dist/{slide-js0QpRV-.d.ts → pres-C6qgOFAa.d.ts} +1021 -1370
- package/dist/pres-C6qgOFAa.d.ts.map +1 -0
- package/dist/{presentation-Cp7vzlAw.js → presentation-DGYlAWhl.js} +3208 -2727
- package/dist/presentation-DGYlAWhl.js.map +1 -0
- package/dist/{presentation-types-DP_8jog7.d.ts → presentation-types-8jLQHI4M.d.ts} +1070 -683
- package/dist/presentation-types-8jLQHI4M.d.ts.map +1 -0
- package/dist/read.d.ts +9 -9
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js +4 -4
- package/dist/script.d.ts +80 -66
- package/dist/script.d.ts.map +1 -1
- package/dist/script.js +2399 -1139
- package/dist/script.js.map +1 -1
- package/dist/{shapes-BemroU-z.js → shapes-re0KyQk_.js} +2807 -1609
- package/dist/shapes-re0KyQk_.js.map +1 -0
- package/dist/slide-Dx2ZeVZ6.d.ts +1256 -0
- package/dist/slide-Dx2ZeVZ6.d.ts.map +1 -0
- package/dist/slide-size-BalTjFuv.js +88 -0
- package/dist/slide-size-BalTjFuv.js.map +1 -0
- package/dist/{units-internal-Dg95w_YD.js → table-cell-margins-EVg0EwxT.js} +235 -273
- package/dist/table-cell-margins-EVg0EwxT.js.map +1 -0
- package/dist/table-dom-BTDP3lEP.js +21 -0
- package/dist/table-dom-BTDP3lEP.js.map +1 -0
- package/dist/text-KnLOtEEu.js +3980 -0
- package/dist/text-KnLOtEEu.js.map +1 -0
- package/dist/text-anchor-CrYXAfUu.js +176 -0
- package/dist/text-anchor-CrYXAfUu.js.map +1 -0
- package/dist/transition-speed-kUEWDWTx.js +14 -0
- package/dist/transition-speed-kUEWDWTx.js.map +1 -0
- package/dist/units-internal-XJlf-hOl.js +649 -0
- package/dist/units-internal-XJlf-hOl.js.map +1 -0
- package/dist/zip-C0leZZMj.d.ts +67 -0
- package/dist/zip-C0leZZMj.d.ts.map +1 -0
- package/dist/zip-CKd8ihcf.js +177 -0
- package/dist/zip-CKd8ihcf.js.map +1 -0
- package/dist/zip.d.ts +3 -68
- package/dist/zip.js +3 -163
- package/dist/zoom-cX9sllpH.js +8865 -0
- package/dist/zoom-cX9sllpH.js.map +1 -0
- package/package.json +106 -19
- package/skills/ts-pptx-upstream/SKILL.md +58 -57
- package/dist/autopage-DkjYZUFD.js +0 -423
- package/dist/autopage-DkjYZUFD.js.map +0 -1
- package/dist/diagnostics-Du_s75MT.d.ts +0 -829
- package/dist/diagnostics-Du_s75MT.d.ts.map +0 -1
- package/dist/errors-Bvln0H-7.js.map +0 -1
- package/dist/errors-PflMPWBn.d.ts +0 -182
- package/dist/errors-PflMPWBn.d.ts.map +0 -1
- package/dist/fit-BKs0zxMI.js.map +0 -1
- package/dist/html-dom-B0I0QSbU.js.map +0 -1
- package/dist/namespaces-BVnNu7sg.js +0 -666
- package/dist/namespaces-BVnNu7sg.js.map +0 -1
- package/dist/notes-RLCwdlTE.js +0 -1703
- package/dist/notes-RLCwdlTE.js.map +0 -1
- package/dist/package-CaOLXSNi.d.ts.map +0 -1
- package/dist/pres-C5__xkDh.d.ts +0 -202
- package/dist/pres-C5__xkDh.d.ts.map +0 -1
- package/dist/presentation-Cp7vzlAw.js.map +0 -1
- package/dist/presentation-types-DP_8jog7.d.ts.map +0 -1
- package/dist/shapes-BemroU-z.js.map +0 -1
- package/dist/slide-js0QpRV-.d.ts.map +0 -1
- package/dist/text-CyetbF6p.js +0 -11278
- package/dist/text-CyetbF6p.js.map +0 -1
- package/dist/units-1kRzYShk.js +0 -231
- package/dist/units-1kRzYShk.js.map +0 -1
- package/dist/units-internal-Dg95w_YD.js.map +0 -1
- package/dist/zip.d.ts.map +0 -1
- package/dist/zip.js.map +0 -1
|
@@ -0,0 +1,1611 @@
|
|
|
1
|
+
import { n as InvalidOptionError, u as warnOnce } from "./errors-C4oENPcu.js";
|
|
2
|
+
import { w as PERCENT_SCALE } from "./table-cell-margins-EVg0EwxT.js";
|
|
3
|
+
import { t as bytesToBinaryString } from "./binary-string-CKVAYI3M.js";
|
|
4
|
+
//#region src/media/image-formats.ts
|
|
5
|
+
/**
|
|
6
|
+
* Every recognised format, keyed by its canonical extension. Iteration order is sniffing order;
|
|
7
|
+
* no two signatures overlap, so it only has to be stable, not carefully chosen.
|
|
8
|
+
*/
|
|
9
|
+
const IMAGE_FORMATS = Object.freeze({
|
|
10
|
+
png: {
|
|
11
|
+
ext: "png",
|
|
12
|
+
contentType: "image/png",
|
|
13
|
+
altExts: [],
|
|
14
|
+
filenameExt: "png",
|
|
15
|
+
magic: (b) => b.length >= 8 && b[0] === 137 && b[1] === 80 && b[2] === 78 && b[3] === 71
|
|
16
|
+
},
|
|
17
|
+
jpeg: {
|
|
18
|
+
ext: "jpeg",
|
|
19
|
+
contentType: "image/jpeg",
|
|
20
|
+
altExts: ["jpg"],
|
|
21
|
+
filenameExt: "jpg",
|
|
22
|
+
magic: (b) => b.length >= 3 && b[0] === 255 && b[1] === 216 && b[2] === 255
|
|
23
|
+
},
|
|
24
|
+
gif: {
|
|
25
|
+
ext: "gif",
|
|
26
|
+
contentType: "image/gif",
|
|
27
|
+
altExts: [],
|
|
28
|
+
filenameExt: "gif",
|
|
29
|
+
magic: (b) => b.length >= 4 && b[0] === 71 && b[1] === 73 && b[2] === 70 && b[3] === 56
|
|
30
|
+
},
|
|
31
|
+
bmp: {
|
|
32
|
+
ext: "bmp",
|
|
33
|
+
contentType: "image/bmp",
|
|
34
|
+
altExts: [],
|
|
35
|
+
filenameExt: "bmp",
|
|
36
|
+
magic: (b) => b.length >= 2 && b[0] === 66 && b[1] === 77
|
|
37
|
+
},
|
|
38
|
+
tiff: {
|
|
39
|
+
ext: "tiff",
|
|
40
|
+
contentType: "image/tiff",
|
|
41
|
+
altExts: ["tif"],
|
|
42
|
+
filenameExt: "tif",
|
|
43
|
+
magic: (b) => b.length >= 4 && (b[0] === 73 && b[1] === 73 && b[2] === 42 && b[3] === 0 || b[0] === 77 && b[1] === 77 && b[2] === 0 && b[3] === 42)
|
|
44
|
+
},
|
|
45
|
+
webp: {
|
|
46
|
+
ext: "webp",
|
|
47
|
+
contentType: "image/webp",
|
|
48
|
+
altExts: [],
|
|
49
|
+
filenameExt: "webp",
|
|
50
|
+
magic: (b) => b.length >= 12 && b[0] === 82 && b[1] === 73 && b[2] === 70 && b[3] === 70 && b[8] === 87 && b[9] === 69 && b[10] === 66 && b[11] === 80
|
|
51
|
+
},
|
|
52
|
+
svg: {
|
|
53
|
+
ext: "svg",
|
|
54
|
+
contentType: "image/svg+xml",
|
|
55
|
+
altExts: [],
|
|
56
|
+
filenameExt: "svg",
|
|
57
|
+
magic: null
|
|
58
|
+
},
|
|
59
|
+
emf: {
|
|
60
|
+
ext: "emf",
|
|
61
|
+
contentType: "image/x-emf",
|
|
62
|
+
altExts: [],
|
|
63
|
+
filenameExt: "emf",
|
|
64
|
+
magic: null
|
|
65
|
+
},
|
|
66
|
+
wmf: {
|
|
67
|
+
ext: "wmf",
|
|
68
|
+
contentType: "image/x-wmf",
|
|
69
|
+
altExts: [],
|
|
70
|
+
filenameExt: "wmf",
|
|
71
|
+
magic: null
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
const BY_EXTENSION = new Map(Object.values(IMAGE_FORMATS).flatMap((f) => [f.ext, ...f.altExts].map((e) => [e, f])));
|
|
75
|
+
const BY_CONTENT_TYPE = new Map(Object.values(IMAGE_FORMATS).map((f) => [f.contentType, f]));
|
|
76
|
+
/** The format a caller's file extension names, or `null` for one the registry does not carry. */
|
|
77
|
+
function imageFormatForExtension(extn) {
|
|
78
|
+
return BY_EXTENSION.get((extn || "").toLowerCase()) ?? null;
|
|
79
|
+
}
|
|
80
|
+
/** The format a content type names, or `null` for one the registry does not carry. */
|
|
81
|
+
function imageFormatForContentType(contentType) {
|
|
82
|
+
return BY_CONTENT_TYPE.get((contentType || "").toLowerCase()) ?? null;
|
|
83
|
+
}
|
|
84
|
+
/** The format these leading bytes are the header of, or `null` when nothing matches. */
|
|
85
|
+
function imageFormatForBytes(bytes) {
|
|
86
|
+
if (!bytes) return null;
|
|
87
|
+
for (const format of Object.values(IMAGE_FORMATS)) if (format.magic?.(bytes)) return format;
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/media/base64.ts
|
|
92
|
+
/**
|
|
93
|
+
* Base64 payload handling for media parts — isomorphic across Node and browsers
|
|
94
|
+
* (`TextEncoder`/`atob`/`btoa` only, no `Buffer`).
|
|
95
|
+
*/
|
|
96
|
+
/**
|
|
97
|
+
* Encode raw SVG markup as a base64 `image/svg+xml` data URI.
|
|
98
|
+
* - lets callers pass inline SVG to `addImage({ svg })` without hand-rolling base64
|
|
99
|
+
* - isomorphic and UTF-8 safe: uses the global `TextEncoder`/`btoa` (Node and browsers)
|
|
100
|
+
* @param {string} svg - SVG markup, e.g. `'<svg ...>...</svg>'`
|
|
101
|
+
* @returns {string} a `data:image/svg+xml;base64,...` URI
|
|
102
|
+
*/
|
|
103
|
+
function svgMarkupToDataUri(svg) {
|
|
104
|
+
return `data:image/svg+xml;base64,${bytesToBase64(new TextEncoder().encode(svg))}`;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Encode raw bytes as base64 (no `data:` prefix) using only `btoa`, so the result is the same
|
|
108
|
+
* on Node, in a browser, and in a runtime that has neither `Buffer` nor `FileReader`.
|
|
109
|
+
* @param {Uint8Array} bytes - the payload
|
|
110
|
+
* @returns {string} base64 text
|
|
111
|
+
*/
|
|
112
|
+
function bytesToBase64(bytes) {
|
|
113
|
+
return btoa(bytesToBinaryString(bytes));
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Present a freshly loaded media payload as a `data:` URI.
|
|
117
|
+
*
|
|
118
|
+
* A {@link RuntimeAdapter.loadMedia} implementation returns whichever encoding its host
|
|
119
|
+
* makes cheap — the browser adapter a full URI, because `FileReader.readAsDataURL` is what
|
|
120
|
+
* decodes a blob without blocking; Node and the neutral adapter raw base64, because neither
|
|
121
|
+
* has a `FileReader`. This is where the two become one, so nothing downstream of the load
|
|
122
|
+
* has to ask which it got.
|
|
123
|
+
*
|
|
124
|
+
* The mime label is documentary: {@link decodeBase64ToBytes} ignores it, and the one consumer
|
|
125
|
+
* that needs a real URI (`image.src`, in the browser adapter's SVG preview) is handed the
|
|
126
|
+
* adapter's own. It is taken from the rel anyway rather than hard-coded, so an already-correct
|
|
127
|
+
* label is not overwritten with a wrong one.
|
|
128
|
+
* @param {string} payload - what the adapter returned: raw base64, or an already-formed URI
|
|
129
|
+
* @param {string} contentType - the rel's content type, e.g. `image/png`
|
|
130
|
+
* @returns {string} a `data:` URI
|
|
131
|
+
*/
|
|
132
|
+
function toMediaDataUri(payload, contentType) {
|
|
133
|
+
return payload.startsWith("data:") ? payload : `data:${contentType};base64,${payload}`;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Where the payload begins in an inline `data:` URI, or `-1` when the value carries no
|
|
137
|
+
* `base64,` header and is therefore raw base64 already.
|
|
138
|
+
*
|
|
139
|
+
* The one reading of that header, because the two functions below used to spell it separately and
|
|
140
|
+
* disagree: {@link hasBase64Header} lower-cased before looking, {@link decodeBase64ToBytes} did
|
|
141
|
+
* not. RFC 2397 does not case the `;base64` token, so `data:image/png;BASE64,…` passed every
|
|
142
|
+
* definer's check and then decoded from index 0, which `atob` rejects. The media part was written
|
|
143
|
+
* empty, with nothing said. One search means the two can no longer answer differently.
|
|
144
|
+
*
|
|
145
|
+
* Matched with a regular expression rather than by lower-casing and indexing, because
|
|
146
|
+
* `toLowerCase` is not length-preserving for every code point and a shifted index would slice the
|
|
147
|
+
* payload apart.
|
|
148
|
+
*/
|
|
149
|
+
function base64PayloadStart(data) {
|
|
150
|
+
const match = /base64,/i.exec(data);
|
|
151
|
+
return match ? match.index + match[0].length : -1;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Decode a base64 image payload (raw base64 or a `data:` URI) to bytes.
|
|
155
|
+
* - tolerant of the `data:[mime];base64,` prefix and of whitespace in the payload
|
|
156
|
+
* @param {string} b64 - base64 string or data URI
|
|
157
|
+
* @returns {Uint8Array | null} decoded bytes, or `null` when the payload is empty/undecodable
|
|
158
|
+
*/
|
|
159
|
+
function decodeBase64ToBytes(b64) {
|
|
160
|
+
if (!b64) return null;
|
|
161
|
+
const start = base64PayloadStart(b64);
|
|
162
|
+
const payload = (start >= 0 ? b64.slice(start) : b64).replace(/\s/g, "");
|
|
163
|
+
if (!payload) return null;
|
|
164
|
+
try {
|
|
165
|
+
const binary = atob(payload);
|
|
166
|
+
const bytes = new Uint8Array(binary.length);
|
|
167
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
168
|
+
return bytes;
|
|
169
|
+
} catch {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Whether an inline payload carries the `base64,` header the definers ask image and media bytes
|
|
175
|
+
* to arrive with. A value that is not a string has none.
|
|
176
|
+
* @param data - the caller's `data`
|
|
177
|
+
*/
|
|
178
|
+
function hasBase64Header(data) {
|
|
179
|
+
return typeof data === "string" && base64PayloadStart(data) >= 0;
|
|
180
|
+
}
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region src/ooxml/namespaces.ts
|
|
183
|
+
/**
|
|
184
|
+
* OOXML namespace URIs, by their canonical prefix.
|
|
185
|
+
*
|
|
186
|
+
* A namespace URI is a fact about the format, not about reading or writing one, so it belongs
|
|
187
|
+
* here rather than on either side — the same reasoning `rel-types.ts` states at length, and for
|
|
188
|
+
* the same failure mode: these are long, near-identical strings, and a typo does not throw. It
|
|
189
|
+
* silently matches nothing on the read side and produces a part PowerPoint ignores on the write
|
|
190
|
+
* side.
|
|
191
|
+
*
|
|
192
|
+
* The table lived in `read/oxml/dom.ts` and could not be reached from `gen/`, because that
|
|
193
|
+
* module imports `@xmldom/xmldom` at module scope and pulling an XML DOM into the write-only
|
|
194
|
+
* bundle is a real cost. So the write side hand-wrote the URIs instead: three separate `A_NS`
|
|
195
|
+
* constants and seven copies of the same `xmlns:a`/`xmlns:r`/`xmlns:p` triple. This module has
|
|
196
|
+
* **no runtime imports**, and no module beside it imports from `gen/` or `read/`, so both sides can
|
|
197
|
+
* reach it and neither pays for the other's dependencies. The two that fail loudly reach outside
|
|
198
|
+
* `ooxml/` for that alone: `sequence.ts` for `errors.ts`, and `check-enum.ts` for `errors.ts` and
|
|
199
|
+
* `diagnostics.ts`. `read/oxml/dom.ts` re-exports it, so no read-side import path changed.
|
|
200
|
+
*/
|
|
201
|
+
/** Canonical OOXML prefix → namespace URI registry. */
|
|
202
|
+
const OOXML_NS = Object.freeze({
|
|
203
|
+
a: "http://schemas.openxmlformats.org/drawingml/2006/main",
|
|
204
|
+
a14: "http://schemas.microsoft.com/office/drawing/2010/main",
|
|
205
|
+
asvg: "http://schemas.microsoft.com/office/drawing/2016/SVG/main",
|
|
206
|
+
c: "http://schemas.openxmlformats.org/drawingml/2006/chart",
|
|
207
|
+
c15: "http://schemas.microsoft.com/office/drawing/2012/chart",
|
|
208
|
+
cp: "http://schemas.openxmlformats.org/package/2006/metadata/core-properties",
|
|
209
|
+
ct: "http://schemas.openxmlformats.org/package/2006/content-types",
|
|
210
|
+
cx: "http://schemas.microsoft.com/office/drawing/2014/chartex",
|
|
211
|
+
dc: "http://purl.org/dc/elements/1.1/",
|
|
212
|
+
dcterms: "http://purl.org/dc/terms/",
|
|
213
|
+
dgm: "http://schemas.openxmlformats.org/drawingml/2006/diagram",
|
|
214
|
+
dsp: "http://schemas.microsoft.com/office/drawing/2008/diagram",
|
|
215
|
+
ep: "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties",
|
|
216
|
+
m: "http://schemas.openxmlformats.org/officeDocument/2006/math",
|
|
217
|
+
mc: "http://schemas.openxmlformats.org/markup-compatibility/2006",
|
|
218
|
+
p: "http://schemas.openxmlformats.org/presentationml/2006/main",
|
|
219
|
+
p14: "http://schemas.microsoft.com/office/powerpoint/2010/main",
|
|
220
|
+
p15: "http://schemas.microsoft.com/office/powerpoint/2012/main",
|
|
221
|
+
p159: "http://schemas.microsoft.com/office/powerpoint/2015/9/main",
|
|
222
|
+
p188: "http://schemas.microsoft.com/office/powerpoint/2018/8/main",
|
|
223
|
+
pr: "http://schemas.openxmlformats.org/package/2006/relationships",
|
|
224
|
+
r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
|
225
|
+
vt: "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"
|
|
226
|
+
});
|
|
227
|
+
/**
|
|
228
|
+
* `a:graphicData/@uri` for a DrawingML table.
|
|
229
|
+
*
|
|
230
|
+
* The other three payloads a `p:graphicFrame` can hold name a URI that is also a namespace, so
|
|
231
|
+
* they read it off {@link OOXML_NS} (`c`, `cx`, `dgm`). A table's does not: nothing binds a
|
|
232
|
+
* prefix to it, and the table elements themselves are in the `a` namespace. So it lives beside
|
|
233
|
+
* the registry rather than in it, which keeps every entry there a prefix a document can declare.
|
|
234
|
+
*/
|
|
235
|
+
const TABLE_GRAPHIC_DATA_URI = "http://schemas.openxmlformats.org/drawingml/2006/table";
|
|
236
|
+
/**
|
|
237
|
+
* The `xmlns` declarations every PresentationML part root carries, in the order the emitters
|
|
238
|
+
* have always written them.
|
|
239
|
+
*
|
|
240
|
+
* Spread this **first** in a root element's attribute map: `el()` serializes attributes in
|
|
241
|
+
* object-key order, so the three keys have to keep the positions the literals occupied or the
|
|
242
|
+
* emitted bytes move. A root with extra attributes (`show` on `p:sld`, `def` on `a:tblStyleLst`)
|
|
243
|
+
* lists them after the spread, which is where they already were.
|
|
244
|
+
*/
|
|
245
|
+
const PML_ROOT_NS = Object.freeze({
|
|
246
|
+
"xmlns:a": OOXML_NS.a,
|
|
247
|
+
"xmlns:r": OOXML_NS.r,
|
|
248
|
+
"xmlns:p": OOXML_NS.p
|
|
249
|
+
});
|
|
250
|
+
//#endregion
|
|
251
|
+
//#region src/ooxml/rel-types.ts
|
|
252
|
+
/**
|
|
253
|
+
* OPC relationship type URIs and part content types, and the schema-URI bases they are built from.
|
|
254
|
+
*
|
|
255
|
+
* These are long, near-identical strings that differ in one path segment, which makes a typo
|
|
256
|
+
* both easy to write and invisible on review — a wrong URI does not throw, it silently matches
|
|
257
|
+
* nothing on the read side and produces a part PowerPoint ignores or rejects on the write side.
|
|
258
|
+
*
|
|
259
|
+
* **Why this module is neither `gen/` nor `read/`.** Both halves need the same URIs, and for a
|
|
260
|
+
* while each kept its own copy (`gen/oxml/schema-uris.ts`, `read/api/rel-types.ts`), with a note
|
|
261
|
+
* in both saying a shared home was "a separate decision". It is this module: a rel type is a fact
|
|
262
|
+
* about the OOXML package format, not about writing or reading one, so neither side should own it.
|
|
263
|
+
* A divergence between the two copies would mean the writer emitting a rel the reader cannot find
|
|
264
|
+
* — a round-trip bug with no compile-time signal.
|
|
265
|
+
*
|
|
266
|
+
* Only types used by more than one module live here. A rel type exactly one module cares about
|
|
267
|
+
* (`tags`, `oleObject`, the modern comments part, …) stays declared next to the code that reads or
|
|
268
|
+
* emits it — hoisting those would trade a definition you can see for one you have to go and find,
|
|
269
|
+
* and buy nothing. A writer and a reader of the same part count as two modules even when each
|
|
270
|
+
* names the string once: that pair is exactly the divergence this module exists to prevent.
|
|
271
|
+
*
|
|
272
|
+
* The same reasoning covers **part content types**, which are the same kind of fact and fail the
|
|
273
|
+
* same silent way, including every entry the writer's `[Content_Types].xml` declares.
|
|
274
|
+
*/
|
|
275
|
+
/**
|
|
276
|
+
* Prefix for `.rels` `Type` URIs — append the type name (`slide`, `image`, `theme`, …).
|
|
277
|
+
* Exported for the write side, which builds one-off rel types at their call sites.
|
|
278
|
+
*/
|
|
279
|
+
const OFFICE_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/";
|
|
280
|
+
/** `xmlns` of every `<Relationships>` part. Also the prefix of the package-scoped rel types. */
|
|
281
|
+
const PACKAGE_REL_NS = OOXML_NS.pr;
|
|
282
|
+
/**
|
|
283
|
+
* Prefix for the Microsoft rel types this library emits and reads. Exported for the same reason
|
|
284
|
+
* as {@link OFFICE_REL}: a rel type only one module names is built from it at that module's call site.
|
|
285
|
+
*/
|
|
286
|
+
const MS_REL = "http://schemas.microsoft.com/office/";
|
|
287
|
+
/** `p:sldIdLst` → a slide part. */
|
|
288
|
+
const SLIDE_REL = OFFICE_REL + "slide";
|
|
289
|
+
/** A slide → its layout; a master → each of its layouts. */
|
|
290
|
+
const SLIDE_LAYOUT_REL = OFFICE_REL + "slideLayout";
|
|
291
|
+
/** A layout → its master; the presentation → each master. */
|
|
292
|
+
const SLIDE_MASTER_REL = OFFICE_REL + "slideMaster";
|
|
293
|
+
/** A slide → its notes slide. */
|
|
294
|
+
const NOTES_SLIDE_REL = OFFICE_REL + "notesSlide";
|
|
295
|
+
/** A notes slide → the notes master; the presentation → the notes master. */
|
|
296
|
+
const NOTES_MASTER_REL = OFFICE_REL + "notesMaster";
|
|
297
|
+
/** A master or notes master → its theme. Layouts and slides inherit it rather than holding one. */
|
|
298
|
+
const THEME_REL = OFFICE_REL + "theme";
|
|
299
|
+
/** The package root → `ppt/presentation.xml`. The entry point to everything above. */
|
|
300
|
+
const OFFICE_DOCUMENT_REL = OFFICE_REL + "officeDocument";
|
|
301
|
+
/** Any part → an image in `ppt/media/`. */
|
|
302
|
+
const IMAGE_REL = OFFICE_REL + "image";
|
|
303
|
+
/** Any part → an external or internal jump target. External ones carry `TargetMode="External"`. */
|
|
304
|
+
const HYPERLINK_REL = OFFICE_REL + "hyperlink";
|
|
305
|
+
/** The presentation → `ppt/tableStyles.xml`. */
|
|
306
|
+
const TABLE_STYLES_REL = OFFICE_REL + "tableStyles";
|
|
307
|
+
/** The package root → `docProps/core.xml`. Package-scoped, so not under {@link OFFICE_REL}. */
|
|
308
|
+
const CORE_PROPS_REL = PACKAGE_REL_NS + "/metadata/core-properties";
|
|
309
|
+
/** The package root → `docProps/app.xml`. */
|
|
310
|
+
const EXTENDED_PROPS_REL = OFFICE_REL + "extended-properties";
|
|
311
|
+
/** The package root → `docProps/custom.xml`. */
|
|
312
|
+
const CUSTOM_PROPS_REL = OFFICE_REL + "custom-properties";
|
|
313
|
+
/** A slide → its legacy comments part (`ppt/comments/commentN.xml`). */
|
|
314
|
+
const COMMENTS_REL = OFFICE_REL + "comments";
|
|
315
|
+
/** The presentation → the deck-wide legacy comment-author registry (`ppt/commentAuthors.xml`). */
|
|
316
|
+
const COMMENT_AUTHORS_REL = OFFICE_REL + "commentAuthors";
|
|
317
|
+
/** The presentation → the 2018 comment-author registry (`ppt/authors.xml`), a Microsoft rel type. */
|
|
318
|
+
const MODERN_COMMENT_AUTHORS_REL = MS_REL + "2018/10/relationships/authors";
|
|
319
|
+
/** The presentation → `ppt/presProps.xml`. */
|
|
320
|
+
const PRES_PROPS_REL = OFFICE_REL + "presProps";
|
|
321
|
+
/** The presentation → `ppt/viewProps.xml`. */
|
|
322
|
+
const VIEW_PROPS_REL = OFFICE_REL + "viewProps";
|
|
323
|
+
/** The presentation → the handout master. */
|
|
324
|
+
const HANDOUT_MASTER_REL = OFFICE_REL + "handoutMaster";
|
|
325
|
+
/** A part → a theme override, which replaces the deck theme for that part alone. */
|
|
326
|
+
const THEME_OVERRIDE_REL = OFFICE_REL + "themeOverride";
|
|
327
|
+
/** The presentation → an embedded font part, one per face. */
|
|
328
|
+
const FONT_REL = OFFICE_REL + "font";
|
|
329
|
+
/** A slide → an audio blob in `ppt/media/`. Paired with {@link MS_MEDIA_REL} on the same target. */
|
|
330
|
+
const AUDIO_REL = OFFICE_REL + "audio";
|
|
331
|
+
/** A slide → a video blob in `ppt/media/`, or an online-video placeholder. Paired with {@link MS_MEDIA_REL}. */
|
|
332
|
+
const VIDEO_REL = OFFICE_REL + "video";
|
|
333
|
+
/**
|
|
334
|
+
* The MS-2007 `media` rel every audio/video shape carries alongside the ECMA one, pointing at the
|
|
335
|
+
* same target. It is what `<p14:media r:embed>` resolves, so a media shape needs both.
|
|
336
|
+
*/
|
|
337
|
+
const MS_MEDIA_REL = MS_REL + "2007/relationships/media";
|
|
338
|
+
/** A slide → an embedded 3D model (`.glb`). Note `2017/06`, not the `2017` of the model3d namespace. */
|
|
339
|
+
const MODEL3D_REL = MS_REL + "2017/06/relationships/model3d";
|
|
340
|
+
/** A slide → a classic (`c:`) chart part. */
|
|
341
|
+
const CHART_REL = OFFICE_REL + "chart";
|
|
342
|
+
/** A slide → a chartEx (`cx:`, Office 2016) chart part — an MS rel, not the ECMA `chart` one. */
|
|
343
|
+
const CHARTEX_REL = MS_REL + "2014/relationships/chartEx";
|
|
344
|
+
/** A chart part → its mandatory style sidecar. */
|
|
345
|
+
const CHART_STYLE_REL = MS_REL + "2011/relationships/chartStyle";
|
|
346
|
+
/** A chart part → its mandatory colour-style sidecar. */
|
|
347
|
+
const CHART_COLOR_STYLE_REL = MS_REL + "2011/relationships/chartColorStyle";
|
|
348
|
+
/** Any part → an embedded OPC package (an Office file, itself a zip): a chart's workbook, an OLE payload. */
|
|
349
|
+
const PACKAGE_REL = OFFICE_REL + "package";
|
|
350
|
+
/**
|
|
351
|
+
* Root of the ECMA-376 content types. Exported for a content type only one module names, which is
|
|
352
|
+
* built from it at that module's call site; a shared one is a constant below.
|
|
353
|
+
*/
|
|
354
|
+
const OD_CONTENT = "application/vnd.openxmlformats-officedocument.";
|
|
355
|
+
/** Root of the OPC package-level content types. Private: callers want one of the constants below. */
|
|
356
|
+
const PACKAGE_CONTENT = "application/vnd.openxmlformats-package.";
|
|
357
|
+
/** Root of the Microsoft Office content types the chartEx family uses. Private. */
|
|
358
|
+
const MS_OFFICE_CONTENT = "application/vnd.ms-office.";
|
|
359
|
+
/** Root of the PowerPoint-specific Microsoft content types. Exported for the same reason as {@link OD_CONTENT}. */
|
|
360
|
+
const MS_POWERPOINT_CONTENT = "application/vnd.ms-powerpoint.";
|
|
361
|
+
/** `ppt/slides/slideN.xml`. */
|
|
362
|
+
const SLIDE_CONTENT_TYPE = OD_CONTENT + "presentationml.slide+xml";
|
|
363
|
+
/** `ppt/slideMasters/slideMasterN.xml`. */
|
|
364
|
+
const SLIDE_MASTER_CONTENT_TYPE = OD_CONTENT + "presentationml.slideMaster+xml";
|
|
365
|
+
/** `ppt/slideLayouts/slideLayoutN.xml`. */
|
|
366
|
+
const SLIDE_LAYOUT_CONTENT_TYPE = OD_CONTENT + "presentationml.slideLayout+xml";
|
|
367
|
+
/** `ppt/notesSlides/notesSlideN.xml`. */
|
|
368
|
+
const NOTES_SLIDE_CONTENT_TYPE = OD_CONTENT + "presentationml.notesSlide+xml";
|
|
369
|
+
/** `ppt/notesMasters/notesMasterN.xml`. */
|
|
370
|
+
const NOTES_MASTER_CONTENT_TYPE = OD_CONTENT + "presentationml.notesMaster+xml";
|
|
371
|
+
/** `ppt/theme/themeN.xml`. Not `presentationml.`: a theme is a DrawingML part. */
|
|
372
|
+
const THEME_CONTENT_TYPE = OD_CONTENT + "theme+xml";
|
|
373
|
+
/** `ppt/tableStyles.xml`. */
|
|
374
|
+
const TABLE_STYLES_CONTENT_TYPE = OD_CONTENT + "presentationml.tableStyles+xml";
|
|
375
|
+
/** `ppt/presentation.xml` in an editable `.pptx`. A `.potx` template's main part is a different one. */
|
|
376
|
+
const PRESENTATION_MAIN_CONTENT_TYPE = OD_CONTENT + "presentationml.presentation.main+xml";
|
|
377
|
+
/** Every `.rels` part. Declared once as a `Default` for the `rels` extension, never as an Override. */
|
|
378
|
+
const RELATIONSHIPS_CONTENT_TYPE = PACKAGE_CONTENT + "relationships+xml";
|
|
379
|
+
/** `ppt/presProps.xml`. */
|
|
380
|
+
const PRES_PROPS_CONTENT_TYPE = OD_CONTENT + "presentationml.presProps+xml";
|
|
381
|
+
/** `ppt/viewProps.xml`. */
|
|
382
|
+
const VIEW_PROPS_CONTENT_TYPE = OD_CONTENT + "presentationml.viewProps+xml";
|
|
383
|
+
/** `ppt/comments/commentN.xml`, a legacy comments part. */
|
|
384
|
+
const COMMENTS_CONTENT_TYPE = OD_CONTENT + "presentationml.comments+xml";
|
|
385
|
+
/** `ppt/commentAuthors.xml`. */
|
|
386
|
+
const COMMENT_AUTHORS_CONTENT_TYPE = OD_CONTENT + "presentationml.commentAuthors+xml";
|
|
387
|
+
/** `docProps/core.xml`. Package-scoped, so not under the officedocument prefix. */
|
|
388
|
+
const CORE_PROPS_CONTENT_TYPE = PACKAGE_CONTENT + "core-properties+xml";
|
|
389
|
+
/** `docProps/app.xml`. */
|
|
390
|
+
const EXTENDED_PROPS_CONTENT_TYPE = OD_CONTENT + "extended-properties+xml";
|
|
391
|
+
/** `docProps/custom.xml`. */
|
|
392
|
+
const CUSTOM_PROPS_CONTENT_TYPE = OD_CONTENT + "custom-properties+xml";
|
|
393
|
+
/** `ppt/charts/chartN.xml`, a classic (`c:`) chart. */
|
|
394
|
+
const CHART_CONTENT_TYPE = OD_CONTENT + "drawingml.chart+xml";
|
|
395
|
+
/** `ppt/charts/chartExN.xml`. A chartEx part carries a Microsoft content type, not an `openxmlformats` one. */
|
|
396
|
+
const CHARTEX_CONTENT_TYPE = MS_OFFICE_CONTENT + "chartex+xml";
|
|
397
|
+
/** A chart's mandatory `style{N}.xml` sidecar. */
|
|
398
|
+
const CHART_STYLE_CONTENT_TYPE = MS_OFFICE_CONTENT + "chartstyle+xml";
|
|
399
|
+
/** A chart's mandatory `colors{N}.xml` sidecar. */
|
|
400
|
+
const CHART_COLOR_STYLE_CONTENT_TYPE = MS_OFFICE_CONTENT + "chartcolorstyle+xml";
|
|
401
|
+
/** An embedded `.xlsx` workbook: a chart's data, or an OLE payload. */
|
|
402
|
+
const XLSX_CONTENT_TYPE = OD_CONTENT + "spreadsheetml.sheet";
|
|
403
|
+
/**
|
|
404
|
+
* Every kind of part a page points at without owning it.
|
|
405
|
+
*
|
|
406
|
+
* Two questions read this table, under different keys. Copying a page asks whether a relationship's
|
|
407
|
+
* target may be shared by the copies rather than copied (every row, by `relType`). Pruning after a
|
|
408
|
+
* slide is removed asks whether a part is deck chrome that stays even while nothing reaches it (the
|
|
409
|
+
* `deck` rows, by `contentType`). They were two hand-kept lists, and the two author registries were
|
|
410
|
+
* on the first and missing from the second.
|
|
411
|
+
*/
|
|
412
|
+
const SHARED_PARTS = [
|
|
413
|
+
{
|
|
414
|
+
relType: SLIDE_LAYOUT_REL,
|
|
415
|
+
contentType: SLIDE_LAYOUT_CONTENT_TYPE,
|
|
416
|
+
scope: "deck"
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
relType: SLIDE_MASTER_REL,
|
|
420
|
+
contentType: SLIDE_MASTER_CONTENT_TYPE,
|
|
421
|
+
scope: "deck"
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
relType: NOTES_MASTER_REL,
|
|
425
|
+
contentType: NOTES_MASTER_CONTENT_TYPE,
|
|
426
|
+
scope: "deck"
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
relType: THEME_REL,
|
|
430
|
+
contentType: THEME_CONTENT_TYPE,
|
|
431
|
+
scope: "deck"
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
relType: THEME_OVERRIDE_REL,
|
|
435
|
+
contentType: OD_CONTENT + "themeOverride+xml",
|
|
436
|
+
scope: "deck"
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
relType: HANDOUT_MASTER_REL,
|
|
440
|
+
contentType: OD_CONTENT + "presentationml.handoutMaster+xml",
|
|
441
|
+
scope: "deck"
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
relType: PRES_PROPS_REL,
|
|
445
|
+
contentType: PRES_PROPS_CONTENT_TYPE,
|
|
446
|
+
scope: "deck"
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
relType: VIEW_PROPS_REL,
|
|
450
|
+
contentType: VIEW_PROPS_CONTENT_TYPE,
|
|
451
|
+
scope: "deck"
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
relType: TABLE_STYLES_REL,
|
|
455
|
+
contentType: TABLE_STYLES_CONTENT_TYPE,
|
|
456
|
+
scope: "deck"
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
relType: OFFICE_DOCUMENT_REL,
|
|
460
|
+
contentType: PRESENTATION_MAIN_CONTENT_TYPE,
|
|
461
|
+
scope: "deck"
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
relType: COMMENT_AUTHORS_REL,
|
|
465
|
+
contentType: COMMENT_AUTHORS_CONTENT_TYPE,
|
|
466
|
+
scope: "deck"
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
relType: MODERN_COMMENT_AUTHORS_REL,
|
|
470
|
+
contentType: "application/vnd.ms-powerpoint.authors+xml",
|
|
471
|
+
scope: "deck"
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
relType: IMAGE_REL,
|
|
475
|
+
contentType: null,
|
|
476
|
+
scope: "media"
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
relType: AUDIO_REL,
|
|
480
|
+
contentType: null,
|
|
481
|
+
scope: "media"
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
relType: VIDEO_REL,
|
|
485
|
+
contentType: null,
|
|
486
|
+
scope: "media"
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
relType: MS_MEDIA_REL,
|
|
490
|
+
contentType: null,
|
|
491
|
+
scope: "media"
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
relType: MODEL3D_REL,
|
|
495
|
+
contentType: null,
|
|
496
|
+
scope: "media"
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
relType: FONT_REL,
|
|
500
|
+
contentType: null,
|
|
501
|
+
scope: "media"
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
relType: SLIDE_REL,
|
|
505
|
+
contentType: null,
|
|
506
|
+
scope: "page"
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
relType: HYPERLINK_REL,
|
|
510
|
+
contentType: null,
|
|
511
|
+
scope: "page"
|
|
512
|
+
}
|
|
513
|
+
];
|
|
514
|
+
//#endregion
|
|
515
|
+
//#region src/ooxml/st-enums.ts
|
|
516
|
+
/**
|
|
517
|
+
* `ST_` simple-type enumerations from ECMA-376, declared once as the single source of both the
|
|
518
|
+
* TypeScript union a caller is checked against and the runtime list a value is validated against.
|
|
519
|
+
*
|
|
520
|
+
* **The problem this solves.** Each of these used to exist twice: a hand-written string union in
|
|
521
|
+
* `src/types/` (what the compiler enforces) and a hand-written array in `src/gen/` or `src/read/`
|
|
522
|
+
* (what the validator checks at run time). Nothing tied the two together, so adding a member to
|
|
523
|
+
* one and not the other produced either a value TypeScript accepts and the validator drops, or a
|
|
524
|
+
* value the validator passes and the type rejects. Two of them had already diverged in *order*,
|
|
525
|
+
* which is harmless, but nothing would have caught a divergence in *content*.
|
|
526
|
+
*
|
|
527
|
+
* **The shape.** Every entry is a `readonly` tuple declared `as const`, with its union derived via
|
|
528
|
+
* `(typeof X)[number]`. Widening the tuple widens the union in the same edit — they cannot drift.
|
|
529
|
+
* This is the idiom `EXTRA_SHAPE_PRESETS` already used; this module brings the stragglers onto it,
|
|
530
|
+
* and holds a few closed schema lists that are not `ST_` types, such as `EMBEDDED_FONT_SLOTS`.
|
|
531
|
+
*
|
|
532
|
+
* **Why this module is neither `gen/` nor `read/`.** `src/types/` needs the unions for the public
|
|
533
|
+
* write API, `src/gen/` needs the tuples to vet a value before emitting it, and `src/read/` needs
|
|
534
|
+
* them to vet a value before writing it into a loaded part. It is a fact about the schema, so it
|
|
535
|
+
* belongs to none of the three.
|
|
536
|
+
*
|
|
537
|
+
* Values are listed in **schema declaration order**, which is the order the diagnostics print
|
|
538
|
+
* when they tell a caller what is legal.
|
|
539
|
+
*
|
|
540
|
+
* **The chart option bag reads its enumerations from here**: `BarDirection`, `BarGrouping`,
|
|
541
|
+
* `Bar3DShape`, `LegendPosition`, `DisplayBlanksAs`, `DataLabelPosition` and `LineDataSymbol`
|
|
542
|
+
* first, then the axis and error-bar ones. They were the half of this consolidation that went
|
|
543
|
+
* unfinished for a while. `src/types/chart.ts`
|
|
544
|
+
* hand-wrote four of those unions beside the option that takes them and typed the other three as
|
|
545
|
+
* bare `string`, so `barDir` carried no compile-time check at all, and `dataLabelPosition`'s
|
|
546
|
+
* hand-written union was missing `inBase` — a value the validator accepts on a stacked bar, so
|
|
547
|
+
* the two spellings had already diverged in content. Both are gone: for every option here, the
|
|
548
|
+
* type a caller is checked against and the tuple the validator checks are one declaration.
|
|
549
|
+
*/
|
|
550
|
+
/** `ST_TextAutonumberScheme` — the numbering an auto-numbered bullet counts in (`a:buAutoNum/@type`). */
|
|
551
|
+
const TEXT_AUTONUM_SCHEMES = [
|
|
552
|
+
"alphaLcParenBoth",
|
|
553
|
+
"alphaUcParenBoth",
|
|
554
|
+
"alphaLcParenR",
|
|
555
|
+
"alphaUcParenR",
|
|
556
|
+
"alphaLcPeriod",
|
|
557
|
+
"alphaUcPeriod",
|
|
558
|
+
"arabicParenBoth",
|
|
559
|
+
"arabicParenR",
|
|
560
|
+
"arabicPeriod",
|
|
561
|
+
"arabicPlain",
|
|
562
|
+
"romanLcParenBoth",
|
|
563
|
+
"romanUcParenBoth",
|
|
564
|
+
"romanLcParenR",
|
|
565
|
+
"romanUcParenR",
|
|
566
|
+
"romanLcPeriod",
|
|
567
|
+
"romanUcPeriod",
|
|
568
|
+
"circleNumDbPlain",
|
|
569
|
+
"circleNumWdBlackPlain",
|
|
570
|
+
"circleNumWdWhitePlain",
|
|
571
|
+
"arabicDbPeriod",
|
|
572
|
+
"arabicDbPlain",
|
|
573
|
+
"ea1ChsPeriod",
|
|
574
|
+
"ea1ChsPlain",
|
|
575
|
+
"ea1ChtPeriod",
|
|
576
|
+
"ea1ChtPlain",
|
|
577
|
+
"ea1JpnChsDbPeriod",
|
|
578
|
+
"ea1JpnKorPlain",
|
|
579
|
+
"ea1JpnKorPeriod",
|
|
580
|
+
"arabic1Minus",
|
|
581
|
+
"arabic2Minus",
|
|
582
|
+
"hebrew2Minus",
|
|
583
|
+
"thaiAlphaPeriod",
|
|
584
|
+
"thaiAlphaParenR",
|
|
585
|
+
"thaiAlphaParenBoth",
|
|
586
|
+
"thaiNumPeriod",
|
|
587
|
+
"thaiNumParenR",
|
|
588
|
+
"thaiNumParenBoth",
|
|
589
|
+
"hindiAlphaPeriod",
|
|
590
|
+
"hindiNumPeriod",
|
|
591
|
+
"hindiNumParenR",
|
|
592
|
+
"hindiAlpha1Period"
|
|
593
|
+
];
|
|
594
|
+
/** `ST_TextStrikeType` — a run's strikethrough (`a:rPr/@strike`). */
|
|
595
|
+
const TEXT_STRIKE_TYPES = [
|
|
596
|
+
"noStrike",
|
|
597
|
+
"sngStrike",
|
|
598
|
+
"dblStrike"
|
|
599
|
+
];
|
|
600
|
+
/** `ST_TextCapsType` — a run's capitalization (`a:rPr/@cap`). */
|
|
601
|
+
const TEXT_CAPS_TYPES = [
|
|
602
|
+
"none",
|
|
603
|
+
"small",
|
|
604
|
+
"all"
|
|
605
|
+
];
|
|
606
|
+
/** `ST_TextUnderlineType` — a run's underline (`a:rPr/@u`). */
|
|
607
|
+
const TEXT_UNDERLINE_TYPES = [
|
|
608
|
+
"none",
|
|
609
|
+
"words",
|
|
610
|
+
"sng",
|
|
611
|
+
"dbl",
|
|
612
|
+
"heavy",
|
|
613
|
+
"dotted",
|
|
614
|
+
"dottedHeavy",
|
|
615
|
+
"dash",
|
|
616
|
+
"dashHeavy",
|
|
617
|
+
"dashLong",
|
|
618
|
+
"dashLongHeavy",
|
|
619
|
+
"dotDash",
|
|
620
|
+
"dotDashHeavy",
|
|
621
|
+
"dotDotDash",
|
|
622
|
+
"dotDotDashHeavy",
|
|
623
|
+
"wavy",
|
|
624
|
+
"wavyHeavy",
|
|
625
|
+
"wavyDbl"
|
|
626
|
+
];
|
|
627
|
+
/** `ST_TextTabAlignType` — a tab stop's alignment (`a:tab/@algn`). */
|
|
628
|
+
const TEXT_TAB_ALIGN_TYPES = [
|
|
629
|
+
"l",
|
|
630
|
+
"ctr",
|
|
631
|
+
"r",
|
|
632
|
+
"dec"
|
|
633
|
+
];
|
|
634
|
+
/**
|
|
635
|
+
* `ST_SchemeColorVal` — a theme colour reference (`a:schemeClr/@val`). Wider than
|
|
636
|
+
* {@link THEME_COLOR_SLOTS}: it adds the `bg1`/`tx1`/`bg2`/`tx2` aliases the colour map resolves,
|
|
637
|
+
* and `phClr`, the colour a theme style-matrix entry is applied in.
|
|
638
|
+
*/
|
|
639
|
+
const SCHEME_COLOR_VALUES = [
|
|
640
|
+
"bg1",
|
|
641
|
+
"tx1",
|
|
642
|
+
"bg2",
|
|
643
|
+
"tx2",
|
|
644
|
+
"accent1",
|
|
645
|
+
"accent2",
|
|
646
|
+
"accent3",
|
|
647
|
+
"accent4",
|
|
648
|
+
"accent5",
|
|
649
|
+
"accent6",
|
|
650
|
+
"hlink",
|
|
651
|
+
"folHlink",
|
|
652
|
+
"phClr",
|
|
653
|
+
"dk1",
|
|
654
|
+
"lt1",
|
|
655
|
+
"dk2",
|
|
656
|
+
"lt2"
|
|
657
|
+
];
|
|
658
|
+
/**
|
|
659
|
+
* The base slide transitions: the `p:` type elements of `CT_SlideTransition`'s choice, in its
|
|
660
|
+
* declaration order. The writer emits the type as `<p:TYPE/>`, so this is also the list a type is
|
|
661
|
+
* checked against before it becomes an element name, and the list the script converter uses to
|
|
662
|
+
* decide which read transitions the write API can express. PowerPoint's modern transitions live in
|
|
663
|
+
* other namespaces and are not here.
|
|
664
|
+
*/
|
|
665
|
+
const TRANSITION_TYPES = [
|
|
666
|
+
"blinds",
|
|
667
|
+
"checker",
|
|
668
|
+
"circle",
|
|
669
|
+
"dissolve",
|
|
670
|
+
"comb",
|
|
671
|
+
"cover",
|
|
672
|
+
"cut",
|
|
673
|
+
"diamond",
|
|
674
|
+
"fade",
|
|
675
|
+
"newsflash",
|
|
676
|
+
"plus",
|
|
677
|
+
"pull",
|
|
678
|
+
"push",
|
|
679
|
+
"random",
|
|
680
|
+
"randomBar",
|
|
681
|
+
"split",
|
|
682
|
+
"strips",
|
|
683
|
+
"wedge",
|
|
684
|
+
"wheel",
|
|
685
|
+
"wipe",
|
|
686
|
+
"zoom"
|
|
687
|
+
];
|
|
688
|
+
/**
|
|
689
|
+
* The attributes each of {@link TRANSITION_TYPES} declares, which is every key a transition's
|
|
690
|
+
* `variant` can write. Seven are `CT_Empty` and take none.
|
|
691
|
+
*/
|
|
692
|
+
const TRANSITION_VARIANT_ATTRIBUTES = {
|
|
693
|
+
blinds: ["dir"],
|
|
694
|
+
checker: ["dir"],
|
|
695
|
+
circle: [],
|
|
696
|
+
dissolve: [],
|
|
697
|
+
comb: ["dir"],
|
|
698
|
+
cover: ["dir"],
|
|
699
|
+
cut: ["thruBlk"],
|
|
700
|
+
diamond: [],
|
|
701
|
+
fade: ["thruBlk"],
|
|
702
|
+
newsflash: [],
|
|
703
|
+
plus: [],
|
|
704
|
+
pull: ["dir"],
|
|
705
|
+
push: ["dir"],
|
|
706
|
+
random: [],
|
|
707
|
+
randomBar: ["dir"],
|
|
708
|
+
split: ["orient", "dir"],
|
|
709
|
+
strips: ["dir"],
|
|
710
|
+
wedge: [],
|
|
711
|
+
wheel: ["spokes"],
|
|
712
|
+
wipe: ["dir"],
|
|
713
|
+
zoom: ["dir"]
|
|
714
|
+
};
|
|
715
|
+
/**
|
|
716
|
+
* `ST_BevelPresetType` — the bevel profile applied to a 3-D edge.
|
|
717
|
+
* @see ECMA-376 Part 1 §20.1.10.9
|
|
718
|
+
*/
|
|
719
|
+
const BEVEL_PRESETS = [
|
|
720
|
+
"relaxedInset",
|
|
721
|
+
"circle",
|
|
722
|
+
"slope",
|
|
723
|
+
"cross",
|
|
724
|
+
"angle",
|
|
725
|
+
"softRound",
|
|
726
|
+
"convex",
|
|
727
|
+
"coolSlant",
|
|
728
|
+
"divot",
|
|
729
|
+
"riblet",
|
|
730
|
+
"hardEdge",
|
|
731
|
+
"artDeco"
|
|
732
|
+
];
|
|
733
|
+
/**
|
|
734
|
+
* `ST_PresetMaterialType` — how a 3-D surface responds to light.
|
|
735
|
+
* @see ECMA-376 Part 1 §20.1.10.50
|
|
736
|
+
*/
|
|
737
|
+
const PRESET_MATERIALS = [
|
|
738
|
+
"legacyMatte",
|
|
739
|
+
"legacyPlastic",
|
|
740
|
+
"legacyMetal",
|
|
741
|
+
"legacyWireframe",
|
|
742
|
+
"matte",
|
|
743
|
+
"plastic",
|
|
744
|
+
"metal",
|
|
745
|
+
"warmMatte",
|
|
746
|
+
"translucentPowder",
|
|
747
|
+
"powder",
|
|
748
|
+
"dkEdge",
|
|
749
|
+
"softEdge",
|
|
750
|
+
"clear",
|
|
751
|
+
"flat",
|
|
752
|
+
"softmetal"
|
|
753
|
+
];
|
|
754
|
+
/**
|
|
755
|
+
* `ST_LightRigType` — the preset lighting scene.
|
|
756
|
+
* @see ECMA-376 Part 1 §20.1.10.35
|
|
757
|
+
*/
|
|
758
|
+
const LIGHT_RIGS = [
|
|
759
|
+
"legacyFlat1",
|
|
760
|
+
"legacyFlat2",
|
|
761
|
+
"legacyFlat3",
|
|
762
|
+
"legacyFlat4",
|
|
763
|
+
"legacyNormal1",
|
|
764
|
+
"legacyNormal2",
|
|
765
|
+
"legacyNormal3",
|
|
766
|
+
"legacyNormal4",
|
|
767
|
+
"legacyHarsh1",
|
|
768
|
+
"legacyHarsh2",
|
|
769
|
+
"legacyHarsh3",
|
|
770
|
+
"legacyHarsh4",
|
|
771
|
+
"threePt",
|
|
772
|
+
"balanced",
|
|
773
|
+
"soft",
|
|
774
|
+
"harsh",
|
|
775
|
+
"flood",
|
|
776
|
+
"contrasting",
|
|
777
|
+
"morning",
|
|
778
|
+
"sunrise",
|
|
779
|
+
"sunset",
|
|
780
|
+
"chilly",
|
|
781
|
+
"freezing",
|
|
782
|
+
"flat",
|
|
783
|
+
"twoPt",
|
|
784
|
+
"glow",
|
|
785
|
+
"brightRoom"
|
|
786
|
+
];
|
|
787
|
+
/**
|
|
788
|
+
* `ST_LightRigDirection` — where the light rig sits relative to the scene.
|
|
789
|
+
* @see ECMA-376 Part 1 §20.1.10.33
|
|
790
|
+
*/
|
|
791
|
+
const LIGHT_RIG_DIRECTIONS = [
|
|
792
|
+
"tl",
|
|
793
|
+
"t",
|
|
794
|
+
"tr",
|
|
795
|
+
"l",
|
|
796
|
+
"r",
|
|
797
|
+
"bl",
|
|
798
|
+
"b",
|
|
799
|
+
"br"
|
|
800
|
+
];
|
|
801
|
+
/**
|
|
802
|
+
* `ST_TextHorzOverflowType` — `a:bodyPr/@horzOverflow` and `a:tcPr/@horzOverflow`.
|
|
803
|
+
* @see ECMA-376 Part 1 §20.1.10.68
|
|
804
|
+
*/
|
|
805
|
+
const TEXT_HORZ_OVERFLOW = ["clip", "overflow"];
|
|
806
|
+
/**
|
|
807
|
+
* `ST_TextAnchoringType` — `a:bodyPr/@anchor` and `a:tcPr/@anchor`.
|
|
808
|
+
* @see ECMA-376 Part 1 §20.1.10.60
|
|
809
|
+
*/
|
|
810
|
+
const TEXT_ANCHORS = [
|
|
811
|
+
"t",
|
|
812
|
+
"ctr",
|
|
813
|
+
"b",
|
|
814
|
+
"just",
|
|
815
|
+
"dist"
|
|
816
|
+
];
|
|
817
|
+
/**
|
|
818
|
+
* `ST_TextVerticalType` — `a:bodyPr/@vert` and `a:tcPr/@vert`.
|
|
819
|
+
* @see ECMA-376 Part 1 §20.1.10.83
|
|
820
|
+
*/
|
|
821
|
+
const TEXT_VERTICAL = [
|
|
822
|
+
"horz",
|
|
823
|
+
"vert",
|
|
824
|
+
"vert270",
|
|
825
|
+
"wordArtVert",
|
|
826
|
+
"eaVert",
|
|
827
|
+
"mongolianVert",
|
|
828
|
+
"wordArtVertRtl"
|
|
829
|
+
];
|
|
830
|
+
/**
|
|
831
|
+
* `ST_LineEndType` — the arrowhead at either end of a stroke (`a:headEnd/@type`,
|
|
832
|
+
* `a:tailEnd/@type`).
|
|
833
|
+
* @see ECMA-376 Part 1 §20.1.10.33
|
|
834
|
+
*/
|
|
835
|
+
const LINE_END_TYPES = [
|
|
836
|
+
"none",
|
|
837
|
+
"triangle",
|
|
838
|
+
"stealth",
|
|
839
|
+
"diamond",
|
|
840
|
+
"oval",
|
|
841
|
+
"arrow"
|
|
842
|
+
];
|
|
843
|
+
/**
|
|
844
|
+
* The four face slots of `CT_EmbeddedFontListEntry`, in schema child order (`p:font` first, then
|
|
845
|
+
* these). Not an `ST_` type, but a closed, ordered set the schema declares, like
|
|
846
|
+
* {@link THEME_COLOR_SLOTS}. The read and write sides iterate it so their emitters agree on
|
|
847
|
+
* element order, and `sequence.ts` derives `EMBEDDED_FONT_ENTRY_SEQUENCE` from it, so the order
|
|
848
|
+
* the serializer writes and the order the insertion points are computed from are one fact.
|
|
849
|
+
* `embedded-fonts.ts` re-exports it.
|
|
850
|
+
*/
|
|
851
|
+
const EMBEDDED_FONT_SLOTS = [
|
|
852
|
+
"regular",
|
|
853
|
+
"bold",
|
|
854
|
+
"italic",
|
|
855
|
+
"boldItalic"
|
|
856
|
+
];
|
|
857
|
+
/**
|
|
858
|
+
* `ST_TextShapeType` — the preset path text is bent along (`a:prstTxWarp/@prst`), which
|
|
859
|
+
* PowerPoint calls WordArt.
|
|
860
|
+
*
|
|
861
|
+
* `textNoShape` is the identity member: it is legal, and it warps nothing. The library does not
|
|
862
|
+
* narrow this set — every member is a shape PowerPoint draws.
|
|
863
|
+
* @see ECMA-376 Part 1 §20.1.10.76
|
|
864
|
+
*/
|
|
865
|
+
const TEXT_SHAPE_TYPES = [
|
|
866
|
+
"textNoShape",
|
|
867
|
+
"textPlain",
|
|
868
|
+
"textStop",
|
|
869
|
+
"textTriangle",
|
|
870
|
+
"textTriangleInverted",
|
|
871
|
+
"textChevron",
|
|
872
|
+
"textChevronInverted",
|
|
873
|
+
"textRingInside",
|
|
874
|
+
"textRingOutside",
|
|
875
|
+
"textArchUp",
|
|
876
|
+
"textArchDown",
|
|
877
|
+
"textCircle",
|
|
878
|
+
"textButton",
|
|
879
|
+
"textArchUpPour",
|
|
880
|
+
"textArchDownPour",
|
|
881
|
+
"textCirclePour",
|
|
882
|
+
"textButtonPour",
|
|
883
|
+
"textCurveUp",
|
|
884
|
+
"textCurveDown",
|
|
885
|
+
"textCanUp",
|
|
886
|
+
"textCanDown",
|
|
887
|
+
"textWave1",
|
|
888
|
+
"textWave2",
|
|
889
|
+
"textDoubleWave1",
|
|
890
|
+
"textWave4",
|
|
891
|
+
"textInflate",
|
|
892
|
+
"textDeflate",
|
|
893
|
+
"textInflateBottom",
|
|
894
|
+
"textDeflateBottom",
|
|
895
|
+
"textInflateTop",
|
|
896
|
+
"textDeflateTop",
|
|
897
|
+
"textDeflateInflate",
|
|
898
|
+
"textDeflateInflateDeflate",
|
|
899
|
+
"textFadeRight",
|
|
900
|
+
"textFadeLeft",
|
|
901
|
+
"textFadeUp",
|
|
902
|
+
"textFadeDown",
|
|
903
|
+
"textSlantUp",
|
|
904
|
+
"textSlantDown",
|
|
905
|
+
"textCascadeUp",
|
|
906
|
+
"textCascadeDown"
|
|
907
|
+
];
|
|
908
|
+
/**
|
|
909
|
+
* `ST_PresetLineDashVal` — the dash pattern of a line or border (`a:prstDash/@val`).
|
|
910
|
+
*
|
|
911
|
+
* The `sys*` members are the "system" dashes whose exact dot/gap ratio the renderer picks; the
|
|
912
|
+
* rest are defined by the spec. Both `BorderProps.dashType` and `ShapeLineProps.dashType` accept
|
|
913
|
+
* this whole set, so a caller who knows one knows the other.
|
|
914
|
+
* @see ECMA-376 Part 1 §20.1.10.49
|
|
915
|
+
*/
|
|
916
|
+
const PRESET_LINE_DASHES = [
|
|
917
|
+
"solid",
|
|
918
|
+
"dot",
|
|
919
|
+
"dash",
|
|
920
|
+
"lgDash",
|
|
921
|
+
"dashDot",
|
|
922
|
+
"lgDashDot",
|
|
923
|
+
"lgDashDotDot",
|
|
924
|
+
"sysDash",
|
|
925
|
+
"sysDot",
|
|
926
|
+
"sysDashDot",
|
|
927
|
+
"sysDashDotDot"
|
|
928
|
+
];
|
|
929
|
+
/**
|
|
930
|
+
* `ST_BarDir` — whether a bar/column plot runs horizontally or vertically (`c:barDir/@val`).
|
|
931
|
+
* @see ECMA-376 Part 1 §21.2.3.3
|
|
932
|
+
*/
|
|
933
|
+
const BAR_DIRECTIONS = ["bar", "col"];
|
|
934
|
+
/**
|
|
935
|
+
* `ST_BarGrouping` — how a bar/column plot stacks its series (`c:grouping/@val` inside
|
|
936
|
+
* `c:barChart`/`c:bar3DChart`).
|
|
937
|
+
* @see ECMA-376 Part 1 §21.2.3.4
|
|
938
|
+
*/
|
|
939
|
+
const BAR_GROUPINGS = [
|
|
940
|
+
"percentStacked",
|
|
941
|
+
"clustered",
|
|
942
|
+
"standard",
|
|
943
|
+
"stacked"
|
|
944
|
+
];
|
|
945
|
+
/**
|
|
946
|
+
* `ST_Grouping` — the same decision for the plots that have no clustered form
|
|
947
|
+
* (`c:areaChart`, `c:lineChart`).
|
|
948
|
+
* @see ECMA-376 Part 1 §21.2.3.17
|
|
949
|
+
*/
|
|
950
|
+
const GROUPINGS = [
|
|
951
|
+
"percentStacked",
|
|
952
|
+
"standard",
|
|
953
|
+
"stacked"
|
|
954
|
+
];
|
|
955
|
+
/**
|
|
956
|
+
* `ST_Shape` — the solid a 3-D bar is drawn as (`c:shape/@val`).
|
|
957
|
+
* @see ECMA-376 Part 1 §21.2.3.35
|
|
958
|
+
*/
|
|
959
|
+
const BAR_3D_SHAPES = [
|
|
960
|
+
"cone",
|
|
961
|
+
"coneToMax",
|
|
962
|
+
"box",
|
|
963
|
+
"cylinder",
|
|
964
|
+
"pyramid",
|
|
965
|
+
"pyramidToMax"
|
|
966
|
+
];
|
|
967
|
+
/**
|
|
968
|
+
* `ST_RadarStyle` — how a radar plot is drawn (`c:radarStyle/@val`).
|
|
969
|
+
* @see ECMA-376 Part 1 §21.2.3.33
|
|
970
|
+
*/
|
|
971
|
+
const RADAR_STYLES = [
|
|
972
|
+
"standard",
|
|
973
|
+
"marker",
|
|
974
|
+
"filled"
|
|
975
|
+
];
|
|
976
|
+
/**
|
|
977
|
+
* This library's own names for two of the three radar styles, mapped to the wire spelling each
|
|
978
|
+
* one means. They are the PowerPoint UI's words ("Radar", "Radar with Markers"), which is why
|
|
979
|
+
* they were chosen; `filled` needed no rename and is already the schema's own spelling, which is
|
|
980
|
+
* what made the other two read as typos rather than as a second vocabulary.
|
|
981
|
+
*
|
|
982
|
+
* Both spellings are accepted on the public option and normalized to the wire one at definition
|
|
983
|
+
* time. Before that, `radarStyle: 'marker'` — the schema's word, and the obvious guess for anyone
|
|
984
|
+
* reading a chart part — failed the enum check and silently fell back to `standard`, so a caller
|
|
985
|
+
* asking for markers got a plain radar.
|
|
986
|
+
*/
|
|
987
|
+
const RADAR_STYLE_ALIASES = {
|
|
988
|
+
radar: "standard",
|
|
989
|
+
markers: "marker"
|
|
990
|
+
};
|
|
991
|
+
/**
|
|
992
|
+
* The alias spellings alone, for the definer's enum check. Derived from the map rather than
|
|
993
|
+
* written out again — the whole point of this module is that the list a value is validated
|
|
994
|
+
* against and the union it is typed against cannot drift apart.
|
|
995
|
+
*/
|
|
996
|
+
const RADAR_STYLE_ALIAS_NAMES = Object.keys(RADAR_STYLE_ALIASES);
|
|
997
|
+
/**
|
|
998
|
+
* `ST_LegendPos` — where the legend sits relative to the plot area (`c:legendPos/@val`).
|
|
999
|
+
* @see ECMA-376 Part 1 §21.2.3.24
|
|
1000
|
+
*/
|
|
1001
|
+
const LEGEND_POSITIONS = [
|
|
1002
|
+
"b",
|
|
1003
|
+
"tr",
|
|
1004
|
+
"l",
|
|
1005
|
+
"r",
|
|
1006
|
+
"t"
|
|
1007
|
+
];
|
|
1008
|
+
/**
|
|
1009
|
+
* `ST_DispBlanksAs` — what a plot draws where a value is missing (`c:dispBlanksAs/@val`).
|
|
1010
|
+
* @see ECMA-376 Part 1 §21.2.3.10
|
|
1011
|
+
*/
|
|
1012
|
+
const DISPLAY_BLANKS_AS = [
|
|
1013
|
+
"span",
|
|
1014
|
+
"gap",
|
|
1015
|
+
"zero"
|
|
1016
|
+
];
|
|
1017
|
+
/**
|
|
1018
|
+
* `ST_MarkerStyle`, **narrowed** to what the library authors (`c:symbol/@val`).
|
|
1019
|
+
*
|
|
1020
|
+
* The schema also has `plus`, `star`, `x`, `picture` and `auto`. Neither PowerPoint 2013 nor
|
|
1021
|
+
* PowerPoint Online draws the first three, `picture` needs a `c:spPr` blip this API has no
|
|
1022
|
+
* spelling for, and `auto` is what leaving the option off already means — so the accepted set is
|
|
1023
|
+
* deliberately smaller than the type. Widen it only with evidence that the member renders.
|
|
1024
|
+
* @see ECMA-376 Part 1 §21.2.3.27
|
|
1025
|
+
*/
|
|
1026
|
+
const LINE_DATA_SYMBOLS = [
|
|
1027
|
+
"circle",
|
|
1028
|
+
"dash",
|
|
1029
|
+
"diamond",
|
|
1030
|
+
"dot",
|
|
1031
|
+
"none",
|
|
1032
|
+
"square",
|
|
1033
|
+
"triangle"
|
|
1034
|
+
];
|
|
1035
|
+
/** `ST_TickLblPos` — where an axis draws its tick labels (`c:tickLblPos/@val`). */
|
|
1036
|
+
const TICK_LABEL_POSITIONS = [
|
|
1037
|
+
"high",
|
|
1038
|
+
"low",
|
|
1039
|
+
"nextTo",
|
|
1040
|
+
"none"
|
|
1041
|
+
];
|
|
1042
|
+
/** `ST_Orientation` — which way an axis runs (`c:orientation/@val`). */
|
|
1043
|
+
const AXIS_ORIENTATIONS = ["maxMin", "minMax"];
|
|
1044
|
+
/** `ST_TickMark` — an axis' major or minor tick marks (`c:majorTickMark/@val`, `c:minorTickMark/@val`). */
|
|
1045
|
+
const TICK_MARKS = [
|
|
1046
|
+
"cross",
|
|
1047
|
+
"in",
|
|
1048
|
+
"none",
|
|
1049
|
+
"out"
|
|
1050
|
+
];
|
|
1051
|
+
/** `ST_CrossBetween` — whether a value axis crosses its category axis between or on categories (`c:crossBetween/@val`). */
|
|
1052
|
+
const CROSS_BETWEEN = ["between", "midCat"];
|
|
1053
|
+
/** `ST_BuiltInUnit` — the unit a value axis displays its labels in (`c:builtInUnit/@val`). */
|
|
1054
|
+
const BUILT_IN_UNITS = [
|
|
1055
|
+
"hundreds",
|
|
1056
|
+
"thousands",
|
|
1057
|
+
"tenThousands",
|
|
1058
|
+
"hundredThousands",
|
|
1059
|
+
"millions",
|
|
1060
|
+
"tenMillions",
|
|
1061
|
+
"hundredMillions",
|
|
1062
|
+
"billions",
|
|
1063
|
+
"trillions"
|
|
1064
|
+
];
|
|
1065
|
+
/** `ST_TimeUnit` — a date axis' base, major or minor time unit (`c:baseTimeUnit/@val` and its two siblings). */
|
|
1066
|
+
const TIME_UNITS = [
|
|
1067
|
+
"days",
|
|
1068
|
+
"months",
|
|
1069
|
+
"years"
|
|
1070
|
+
];
|
|
1071
|
+
/** `ST_ErrDir` — the axis an error bar measures along (`c:errDir/@val`). */
|
|
1072
|
+
const ERROR_BAR_DIRECTIONS = ["x", "y"];
|
|
1073
|
+
/** `ST_ErrBarType` — which sides of a point draw an error bar (`c:errBarType/@val`). */
|
|
1074
|
+
const ERROR_BAR_TYPES = [
|
|
1075
|
+
"both",
|
|
1076
|
+
"minus",
|
|
1077
|
+
"plus"
|
|
1078
|
+
];
|
|
1079
|
+
/** `ST_ErrValType` — how an error bar's amount is read (`c:errValType/@val`). */
|
|
1080
|
+
const ERROR_BAR_VALUE_TYPES = [
|
|
1081
|
+
"cust",
|
|
1082
|
+
"fixedVal",
|
|
1083
|
+
"percentage",
|
|
1084
|
+
"stdDev",
|
|
1085
|
+
"stdErr"
|
|
1086
|
+
];
|
|
1087
|
+
/**
|
|
1088
|
+
* The twelve theme colour slots (`a:clrScheme` children) a `p:clrMap` token can point at.
|
|
1089
|
+
*
|
|
1090
|
+
* Not an `ST_` type — these are element names inside `a:CT_ColorScheme`, which the schema
|
|
1091
|
+
* declares as a fixed sequence rather than an enumeration. The effect is the same: a closed,
|
|
1092
|
+
* ordered set of twelve names, in the order the scheme declares them.
|
|
1093
|
+
* @see ECMA-376 Part 1 §20.1.6.2
|
|
1094
|
+
*/
|
|
1095
|
+
const THEME_COLOR_SLOTS = [
|
|
1096
|
+
"dk1",
|
|
1097
|
+
"lt1",
|
|
1098
|
+
"dk2",
|
|
1099
|
+
"lt2",
|
|
1100
|
+
"accent1",
|
|
1101
|
+
"accent2",
|
|
1102
|
+
"accent3",
|
|
1103
|
+
"accent4",
|
|
1104
|
+
"accent5",
|
|
1105
|
+
"accent6",
|
|
1106
|
+
"hlink",
|
|
1107
|
+
"folHlink"
|
|
1108
|
+
];
|
|
1109
|
+
/**
|
|
1110
|
+
* The twelve colour-map tokens (`p:clrMap` attributes), in schema attribute order.
|
|
1111
|
+
*
|
|
1112
|
+
* A slide's `schemeClr val="tx1"` names a *token*, not a slot: the master's `p:clrMap` is the
|
|
1113
|
+
* indirection that sends it to a {@link ThemeColorSlot}. Which is why the two lists look alike
|
|
1114
|
+
* and are not the same list — `bg1`/`tx1` are tokens, `lt1`/`dk1` are slots.
|
|
1115
|
+
* @see ECMA-376 Part 1 §19.3.1.6
|
|
1116
|
+
*/
|
|
1117
|
+
const COLOR_MAP_TOKENS = [
|
|
1118
|
+
"bg1",
|
|
1119
|
+
"tx1",
|
|
1120
|
+
"bg2",
|
|
1121
|
+
"tx2",
|
|
1122
|
+
"accent1",
|
|
1123
|
+
"accent2",
|
|
1124
|
+
"accent3",
|
|
1125
|
+
"accent4",
|
|
1126
|
+
"accent5",
|
|
1127
|
+
"accent6",
|
|
1128
|
+
"hlink",
|
|
1129
|
+
"folHlink"
|
|
1130
|
+
];
|
|
1131
|
+
/**
|
|
1132
|
+
* The identity colour map: the `p:clrMap` this library emits on every master and notes master,
|
|
1133
|
+
* and the one the script printer recognises so it can skip printing a map that is the default.
|
|
1134
|
+
*
|
|
1135
|
+
* "Identity" up to the light/dark naming: each token points at the slot of the same name, with
|
|
1136
|
+
* `bg`/`tx` reading as light/dark. A deck that maps them anywhere else — swapping the light and
|
|
1137
|
+
* dark slots is the usual case — resolves every `schemeClr` in the deck differently.
|
|
1138
|
+
*
|
|
1139
|
+
* Typed `Record<ColorMapToken, ThemeColorSlot>` so a token added to {@link COLOR_MAP_TOKENS}
|
|
1140
|
+
* without a mapping, or a slot misspelled, is a compile error rather than a theme that opens
|
|
1141
|
+
* fine and paints one script in the wrong colour. Declared in the same order as the tokens,
|
|
1142
|
+
* because it is emitted by iterating this object and the attribute order is part of the bytes.
|
|
1143
|
+
*/
|
|
1144
|
+
const DEFAULT_COLOR_MAP = {
|
|
1145
|
+
bg1: "lt1",
|
|
1146
|
+
tx1: "dk1",
|
|
1147
|
+
bg2: "lt2",
|
|
1148
|
+
tx2: "dk2",
|
|
1149
|
+
accent1: "accent1",
|
|
1150
|
+
accent2: "accent2",
|
|
1151
|
+
accent3: "accent3",
|
|
1152
|
+
accent4: "accent4",
|
|
1153
|
+
accent5: "accent5",
|
|
1154
|
+
accent6: "accent6",
|
|
1155
|
+
hlink: "hlink",
|
|
1156
|
+
folHlink: "folHlink"
|
|
1157
|
+
};
|
|
1158
|
+
//#endregion
|
|
1159
|
+
//#region src/hex-color.ts
|
|
1160
|
+
/**
|
|
1161
|
+
* ts-pptx: hex colour text, in the one spelling both halves use.
|
|
1162
|
+
*
|
|
1163
|
+
* A caller may write a colour with or without a leading `#`, and the library accepts both
|
|
1164
|
+
* everywhere — which means every site that validates or parses one strips it first. Those
|
|
1165
|
+
* strips had drifted into three spellings across six sites, and one of them was not
|
|
1166
|
+
* anchored: `.replace('#', '')` takes the `#` out of `'FF00#0'` and hands `'FF000'` on, which
|
|
1167
|
+
* then fails the six-digit test and takes a different branch than the anchored forms would.
|
|
1168
|
+
* No caller is known to reach that, which is exactly why it wants one spelling rather than a
|
|
1169
|
+
* test at each site.
|
|
1170
|
+
*
|
|
1171
|
+
* This lives at the root rather than under `gen/` or `read/` because both halves need it:
|
|
1172
|
+
* the emitters strip on the way in, the readers strip on the way back out — and for the same
|
|
1173
|
+
* reason the *validation* belongs here too. `REGEX_HEX_COLOR` used to live in
|
|
1174
|
+
* `constants-internal.ts`, which is a write-side module the read path cannot import, so the read
|
|
1175
|
+
* path re-spelled the regex; so did one write-side site. Six sites, three spellings of one fact.
|
|
1176
|
+
*/
|
|
1177
|
+
/**
|
|
1178
|
+
* A colour written as six hex digits, `#` already stripped.
|
|
1179
|
+
*
|
|
1180
|
+
* Not exported: {@link isHexColor} is the whole of what the call sites want, and a shared mutable
|
|
1181
|
+
* `RegExp` object is a worse export than a predicate.
|
|
1182
|
+
*/
|
|
1183
|
+
const REGEX_HEX_COLOR = /^[0-9a-fA-F]{6}$/;
|
|
1184
|
+
/** An 8-hex RGBA colour, `#` already stripped — {@link splitRgbaHex}'s test. */
|
|
1185
|
+
const REGEX_ARGB_HEX = /^[0-9a-fA-F]{8}$/;
|
|
1186
|
+
/** Strip a single leading `#` from a colour, leaving any other character alone. */
|
|
1187
|
+
function stripHash(value) {
|
|
1188
|
+
return value.startsWith("#") ? value.slice(1) : value;
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Is `value` a 6-hex RGB colour? A leading `#` is stripped first, so both spellings agree.
|
|
1192
|
+
*
|
|
1193
|
+
* This is the *shape* test only. Whether the caller may also name a scheme colour there is the
|
|
1194
|
+
* call site's question, and each still asks it: `createColorElement` falls back to the default
|
|
1195
|
+
* font colour, `makeXmlTheme` keeps the Office default and warns, `normalizeHex` throws.
|
|
1196
|
+
* @param value - the caller's colour, with or without a leading `#`
|
|
1197
|
+
*/
|
|
1198
|
+
function isHexColor(value) {
|
|
1199
|
+
return REGEX_HEX_COLOR.test(stripHash(value));
|
|
1200
|
+
}
|
|
1201
|
+
/**
|
|
1202
|
+
* Is `value` an 8-hex RGBA colour? A leading `#` is stripped first.
|
|
1203
|
+
*
|
|
1204
|
+
* The 8-digit form is RGB followed by one alpha byte; {@link splitRgbaHex} is what takes it apart.
|
|
1205
|
+
* @param value - the caller's colour, with or without a leading `#`
|
|
1206
|
+
*/
|
|
1207
|
+
function isArgbHex(value) {
|
|
1208
|
+
return REGEX_ARGB_HEX.test(stripHash(value));
|
|
1209
|
+
}
|
|
1210
|
+
/**
|
|
1211
|
+
* Split an 8-hex RGBA colour into its 6-hex RGB and a 0–1 alpha; anything else passes through
|
|
1212
|
+
* unchanged with no alpha.
|
|
1213
|
+
*
|
|
1214
|
+
* `#` is stripped first, so both spellings reach the same answer. Two sites parsed this by
|
|
1215
|
+
* hand — `createColorElement`, which turns the alpha into an `<a:alpha>` sibling, and
|
|
1216
|
+
* `normalizeShadowOptions`, which turns it into a shadow's `_alpha` — and they divided by 255 at
|
|
1217
|
+
* different points in their arithmetic.
|
|
1218
|
+
* @param value - the caller's colour, with or without a leading `#`
|
|
1219
|
+
*/
|
|
1220
|
+
function splitRgbaHex(value) {
|
|
1221
|
+
const hex = stripHash(value);
|
|
1222
|
+
if (!isArgbHex(hex)) return { rgb: hex };
|
|
1223
|
+
return {
|
|
1224
|
+
rgb: hex.slice(0, 6),
|
|
1225
|
+
alpha: parseInt(hex.slice(6, 8), 16) / 255
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
//#endregion
|
|
1229
|
+
//#region src/ooxml/xsd-boolean.ts
|
|
1230
|
+
/**
|
|
1231
|
+
* Parsing an `xsd:boolean` attribute value.
|
|
1232
|
+
*
|
|
1233
|
+
* **Why a function at all.** `xsd:boolean` has four lexical forms — `1`, `0`, `true`, `false` —
|
|
1234
|
+
* and OOXML attributes typed with it accept every one of them. PowerPoint writes only `1` and
|
|
1235
|
+
* `0`, so a hand-rolled `=== '1'` test passes against every deck this library produces and
|
|
1236
|
+
* against every deck PowerPoint produces, and silently misreads the other producers. That is the
|
|
1237
|
+
* worst shape a bug can have: invisible to the fixtures, visible only on a customer's file.
|
|
1238
|
+
*
|
|
1239
|
+
* **Why this module is neither `gen/` nor `read/`.** The lexical space of a schema type is a fact
|
|
1240
|
+
* about the schema, like `sequence.ts`'s document order and `body-insets.ts`'s defaults. The
|
|
1241
|
+
* parser lived in `read/oxml/dom.ts` because reading is where most of the demand is, and three
|
|
1242
|
+
* private copies had collapsed into it — but `read/oxml/dom.ts` imports `@xmldom/xmldom`, so
|
|
1243
|
+
* anything outside `read/` that wants this five-line function has to take a DOM dependency to get
|
|
1244
|
+
* it, or write a fourth copy. `script/from-read/detect.ts` hit exactly that and wrote the copy.
|
|
1245
|
+
*
|
|
1246
|
+
* The DOM-typed convenience wrapper stays behind in `read/oxml/dom.ts` as `boolAttr`, because
|
|
1247
|
+
* *that* one really is about reading an element.
|
|
1248
|
+
*
|
|
1249
|
+
* **The write direction is here too**, and it is where the interesting distinction lives: an
|
|
1250
|
+
* emitter writing a boolean has THREE useful shapes, not one — see {@link xsdBool} and
|
|
1251
|
+
* {@link xsdBoolIfTrue}. Sixty-odd inline ternaries spelled them in three ways with nothing
|
|
1252
|
+
* naming which was which.
|
|
1253
|
+
*/
|
|
1254
|
+
/**
|
|
1255
|
+
* Parse an `xsd:boolean` OOXML attribute (`1`/`0`/`true`/`false`); else `null`.
|
|
1256
|
+
*
|
|
1257
|
+
* `null` for an absent *or* unparseable value is deliberate and load-bearing at the call sites:
|
|
1258
|
+
* several attributes have a schema default of `true`, so they test `!== false` rather than
|
|
1259
|
+
* `=== true` and an unset attribute has to be distinguishable from an explicit `0`.
|
|
1260
|
+
*/
|
|
1261
|
+
function boolValue(value) {
|
|
1262
|
+
if (value === "1" || value === "true") return true;
|
|
1263
|
+
if (value === "0" || value === "false") return false;
|
|
1264
|
+
return null;
|
|
1265
|
+
}
|
|
1266
|
+
/**
|
|
1267
|
+
* A boolean as the `1`/`0` an `xsd:boolean` attribute takes.
|
|
1268
|
+
*
|
|
1269
|
+
* Writes the attribute either way, so `false` is *stated*. Use this wherever the schema
|
|
1270
|
+
* default is `true`, or wherever the absence of the attribute would mean something other
|
|
1271
|
+
* than `false` — a value inherited from a list style, a layout or the theme.
|
|
1272
|
+
* @param value - the resolved boolean
|
|
1273
|
+
*/
|
|
1274
|
+
function xsdBool(value) {
|
|
1275
|
+
return value ? "1" : "0";
|
|
1276
|
+
}
|
|
1277
|
+
/**
|
|
1278
|
+
* A boolean as `1`, or `null` so the attribute is left out entirely.
|
|
1279
|
+
*
|
|
1280
|
+
* **Correct only where the schema default is `false`.** It collapses "false" and "unset" into
|
|
1281
|
+
* one output, which is the two-spellings-for-three-states shape the API policy warns about: the
|
|
1282
|
+
* next attribute with a `true` default written this way reads as correct and is wrong. Every
|
|
1283
|
+
* current caller is on a `false`-defaulting attribute, so the collapse costs nothing today —
|
|
1284
|
+
* naming it is what makes the next one visible.
|
|
1285
|
+
* @param value - the resolved boolean
|
|
1286
|
+
*/
|
|
1287
|
+
function xsdBoolIfTrue(value) {
|
|
1288
|
+
return value ? "1" : null;
|
|
1289
|
+
}
|
|
1290
|
+
//#endregion
|
|
1291
|
+
//#region src/ooxml/check-enum.ts
|
|
1292
|
+
/**
|
|
1293
|
+
* Check a value against its `ST_` union, reporting and dropping it when outside (the write-path
|
|
1294
|
+
* policy — see the module note).
|
|
1295
|
+
*
|
|
1296
|
+
* `undefined`/`null` returns `null` without reporting: an unset option is not an invalid one, and
|
|
1297
|
+
* it is by far the common case.
|
|
1298
|
+
*
|
|
1299
|
+
* The offending value is interpolated into the message deliberately. {@link warnOnce} dedups on
|
|
1300
|
+
* code **and** message, so keeping the value in the text is what lets a second, *different* bad
|
|
1301
|
+
* value under the same code still get reported.
|
|
1302
|
+
* @param value - the value the caller supplied, if any
|
|
1303
|
+
* @param valid - the enum's members
|
|
1304
|
+
* @param code - the condition to report
|
|
1305
|
+
* @param label - how to name the offending option in the message, e.g. ``'table cell: cell3D `preset`'``
|
|
1306
|
+
* @param detail - extra structured context to merge into the diagnostic's `detail`
|
|
1307
|
+
* @returns the value when legal, else `null`
|
|
1308
|
+
*/
|
|
1309
|
+
function checkEnumOrWarn(value, valid, code, label, detail) {
|
|
1310
|
+
if (value === void 0 || value === null) return null;
|
|
1311
|
+
if (valid.includes(value)) return value;
|
|
1312
|
+
warnOnce(code, `${label} value \`${String(value)}\` is not valid and is ignored — use one of ${valid.join(", ")}.`, {
|
|
1313
|
+
received: value,
|
|
1314
|
+
valid,
|
|
1315
|
+
...detail
|
|
1316
|
+
});
|
|
1317
|
+
return null;
|
|
1318
|
+
}
|
|
1319
|
+
/**
|
|
1320
|
+
* Check a value against its `ST_` union, throwing when outside (the read/edit-path policy — see
|
|
1321
|
+
* the module note).
|
|
1322
|
+
* @param value - the value the caller asked for
|
|
1323
|
+
* @param valid - the enum's members
|
|
1324
|
+
* @param attribute - the attribute's name, for the message
|
|
1325
|
+
* @param code - the condition to report
|
|
1326
|
+
* @returns `value`, when it is legal
|
|
1327
|
+
*/
|
|
1328
|
+
function checkEnumOrThrow(value, valid, attribute, code) {
|
|
1329
|
+
if (valid.includes(value)) return value;
|
|
1330
|
+
throw new InvalidOptionError(code, `Invalid ${attribute}: ${JSON.stringify(value)}. Expected one of: ${valid.join(", ")}.`);
|
|
1331
|
+
}
|
|
1332
|
+
//#endregion
|
|
1333
|
+
//#region src/ooxml/line-cap.ts
|
|
1334
|
+
/**
|
|
1335
|
+
* Every `cap` the write API names and the token it writes. `satisfies` over `Record<LineCap, …>`
|
|
1336
|
+
* makes a member added to `LineCap` without a token a compile error, and `ST_LineCap` has exactly
|
|
1337
|
+
* these three members, so the inverse covers every token too.
|
|
1338
|
+
*/
|
|
1339
|
+
const TOKEN_BY_LINE_CAP = {
|
|
1340
|
+
flat: "flat",
|
|
1341
|
+
square: "sq",
|
|
1342
|
+
round: "rnd"
|
|
1343
|
+
};
|
|
1344
|
+
/** The `cap` token a `LineCap` writes, or `undefined` for a value the write API does not name. */
|
|
1345
|
+
function lineCapToken(cap) {
|
|
1346
|
+
return Object.hasOwn(TOKEN_BY_LINE_CAP, cap) ? TOKEN_BY_LINE_CAP[cap] : void 0;
|
|
1347
|
+
}
|
|
1348
|
+
/** A `cap` token back to the `LineCap` that authors it. */
|
|
1349
|
+
const LINE_CAP_BY_TOKEN = Object.fromEntries(Object.entries(TOKEN_BY_LINE_CAP).map(([cap, token]) => [token, cap]));
|
|
1350
|
+
//#endregion
|
|
1351
|
+
//#region src/ooxml/text-align.ts
|
|
1352
|
+
/**
|
|
1353
|
+
* Every `align` the write API names and the token it writes. `satisfies` over `Record<HAlign, …>`
|
|
1354
|
+
* makes a member added to `HAlign` without a token a compile error. `justLow`, `dist` and
|
|
1355
|
+
* `thaiDist` have no `align` spelling.
|
|
1356
|
+
*/
|
|
1357
|
+
const ALGN_BY_ALIGN = {
|
|
1358
|
+
left: "l",
|
|
1359
|
+
center: "ctr",
|
|
1360
|
+
right: "r",
|
|
1361
|
+
justify: "just"
|
|
1362
|
+
};
|
|
1363
|
+
/** The `algn` token an `align` value writes, or `undefined` for a value the write API does not name. */
|
|
1364
|
+
function textAlignToken(align) {
|
|
1365
|
+
return Object.hasOwn(ALGN_BY_ALIGN, align) ? ALGN_BY_ALIGN[align] : void 0;
|
|
1366
|
+
}
|
|
1367
|
+
/** An `algn` token back to the `align` a generated script writes. */
|
|
1368
|
+
const HALIGN_BY_TEXT_ALIGN = Object.fromEntries(Object.entries(ALGN_BY_ALIGN).map(([align, token]) => [token, align]));
|
|
1369
|
+
//#endregion
|
|
1370
|
+
//#region src/media/image-size.ts
|
|
1371
|
+
/**
|
|
1372
|
+
* Intrinsic image geometry: reading a natural pixel size out of header bytes, and the
|
|
1373
|
+
* `<a:srcRect>` crop math that fits that natural size into a display box.
|
|
1374
|
+
*
|
|
1375
|
+
* Shared by both sides of the library — the write path (`addImage({ sizing })`) and the read
|
|
1376
|
+
* API's `Picture.setImage({ fit })` — which is why it lives outside `gen/` and `read/`.
|
|
1377
|
+
* Everything here is synchronous and pure: headers only, never pixel decoding.
|
|
1378
|
+
*/
|
|
1379
|
+
/**
|
|
1380
|
+
* Read the intrinsic dimensions of an image from its header bytes.
|
|
1381
|
+
* - synchronous: parses only file-format headers, never decodes pixels
|
|
1382
|
+
* - raster: PNG, JPEG, GIF, BMP, TIFF, and WebP (VP8 / VP8L / VP8X) — natural pixels
|
|
1383
|
+
* - vector: SVG — intrinsic size from the root `<svg>` width/height or viewBox
|
|
1384
|
+
* - unrecognized formats return `null` (no measurable intrinsic size)
|
|
1385
|
+
*
|
|
1386
|
+
* Used by image `sizing: 'cover' | 'contain'` to compute an aspect-correct
|
|
1387
|
+
* `<a:srcRect>` crop from the *natural* image ratio rather than the displayed box.
|
|
1388
|
+
* @param {string} dataB64 - base64 image payload or `data:` URI
|
|
1389
|
+
* @returns {{ w: number, h: number } | null} natural size, or `null` when unmeasurable
|
|
1390
|
+
*/
|
|
1391
|
+
function getImageSizeFromBase64(dataB64) {
|
|
1392
|
+
const b = decodeBase64ToBytes(dataB64);
|
|
1393
|
+
return b ? getImageSizeFromBytes(b) : null;
|
|
1394
|
+
}
|
|
1395
|
+
/**
|
|
1396
|
+
* Read the intrinsic dimensions of an image from raw header bytes — the
|
|
1397
|
+
* byte-level core shared by {@link getImageSizeFromBase64} and the read API's
|
|
1398
|
+
* `Picture.setImage({ fit })`, which already holds the media bytes.
|
|
1399
|
+
* @param {Uint8Array} b - image bytes
|
|
1400
|
+
* @returns {{ w: number, h: number } | null} natural size, or `null` when unmeasurable
|
|
1401
|
+
*/
|
|
1402
|
+
function getImageSizeFromBytes(b) {
|
|
1403
|
+
if (!b || b.length < 24) return null;
|
|
1404
|
+
const u = (n) => b[n] ?? 0;
|
|
1405
|
+
if (IMAGE_FORMATS.png.magic?.(b)) {
|
|
1406
|
+
const w = u(16) << 24 | u(17) << 16 | u(18) << 8 | u(19);
|
|
1407
|
+
const h = u(20) << 24 | u(21) << 16 | u(22) << 8 | u(23);
|
|
1408
|
+
return w > 0 && h > 0 ? {
|
|
1409
|
+
w,
|
|
1410
|
+
h
|
|
1411
|
+
} : null;
|
|
1412
|
+
}
|
|
1413
|
+
if (IMAGE_FORMATS.gif.magic?.(b)) {
|
|
1414
|
+
const w = u(6) | u(7) << 8;
|
|
1415
|
+
const h = u(8) | u(9) << 8;
|
|
1416
|
+
return w > 0 && h > 0 ? {
|
|
1417
|
+
w,
|
|
1418
|
+
h
|
|
1419
|
+
} : null;
|
|
1420
|
+
}
|
|
1421
|
+
if (IMAGE_FORMATS.bmp.magic?.(b)) {
|
|
1422
|
+
if ((u(14) | u(15) << 8 | u(16) << 16 | u(17) << 24) === 12) {
|
|
1423
|
+
const w = u(18) | u(19) << 8;
|
|
1424
|
+
const h = u(20) | u(21) << 8;
|
|
1425
|
+
return w > 0 && h > 0 ? {
|
|
1426
|
+
w,
|
|
1427
|
+
h
|
|
1428
|
+
} : null;
|
|
1429
|
+
}
|
|
1430
|
+
if (b.length < 26) return null;
|
|
1431
|
+
const w = u(18) | u(19) << 8 | u(20) << 16 | u(21) << 24;
|
|
1432
|
+
const h = u(22) | u(23) << 8 | u(24) << 16 | u(25) << 24;
|
|
1433
|
+
const aw = Math.abs(w);
|
|
1434
|
+
const ah = Math.abs(h);
|
|
1435
|
+
return aw > 0 && ah > 0 ? {
|
|
1436
|
+
w: aw,
|
|
1437
|
+
h: ah
|
|
1438
|
+
} : null;
|
|
1439
|
+
}
|
|
1440
|
+
if (IMAGE_FORMATS.webp.magic?.(b)) {
|
|
1441
|
+
const fourCC = String.fromCharCode(u(12), u(13), u(14), u(15));
|
|
1442
|
+
if (fourCC === "VP8 " && b.length >= 30) {
|
|
1443
|
+
const w = (u(26) | u(27) << 8) & 16383;
|
|
1444
|
+
const h = (u(28) | u(29) << 8) & 16383;
|
|
1445
|
+
return w > 0 && h > 0 ? {
|
|
1446
|
+
w,
|
|
1447
|
+
h
|
|
1448
|
+
} : null;
|
|
1449
|
+
}
|
|
1450
|
+
if (fourCC === "VP8L" && b.length >= 25) {
|
|
1451
|
+
const bits = u(21) | u(22) << 8 | u(23) << 16 | u(24) << 24;
|
|
1452
|
+
const w = (bits & 16383) + 1;
|
|
1453
|
+
const h = (bits >> 14 & 16383) + 1;
|
|
1454
|
+
return w > 0 && h > 0 ? {
|
|
1455
|
+
w,
|
|
1456
|
+
h
|
|
1457
|
+
} : null;
|
|
1458
|
+
}
|
|
1459
|
+
if (fourCC === "VP8X" && b.length >= 30) {
|
|
1460
|
+
const w = (u(24) | u(25) << 8 | u(26) << 16) + 1;
|
|
1461
|
+
const h = (u(27) | u(28) << 8 | u(29) << 16) + 1;
|
|
1462
|
+
return w > 0 && h > 0 ? {
|
|
1463
|
+
w,
|
|
1464
|
+
h
|
|
1465
|
+
} : null;
|
|
1466
|
+
}
|
|
1467
|
+
return null;
|
|
1468
|
+
}
|
|
1469
|
+
if (IMAGE_FORMATS.jpeg.magic?.(b)) {
|
|
1470
|
+
let i = 2;
|
|
1471
|
+
while (i + 9 < b.length) {
|
|
1472
|
+
if (b[i] !== 255) {
|
|
1473
|
+
i++;
|
|
1474
|
+
continue;
|
|
1475
|
+
}
|
|
1476
|
+
const marker = u(i + 1);
|
|
1477
|
+
if (marker === 255) {
|
|
1478
|
+
i++;
|
|
1479
|
+
continue;
|
|
1480
|
+
}
|
|
1481
|
+
if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) {
|
|
1482
|
+
const h = u(i + 5) << 8 | u(i + 6);
|
|
1483
|
+
const w = u(i + 7) << 8 | u(i + 8);
|
|
1484
|
+
return w > 0 && h > 0 ? {
|
|
1485
|
+
w,
|
|
1486
|
+
h
|
|
1487
|
+
} : null;
|
|
1488
|
+
}
|
|
1489
|
+
if (marker >= 208 && marker <= 217 || marker === 1) {
|
|
1490
|
+
i += 2;
|
|
1491
|
+
continue;
|
|
1492
|
+
}
|
|
1493
|
+
const segLen = u(i + 2) << 8 | u(i + 3);
|
|
1494
|
+
if (segLen < 2) break;
|
|
1495
|
+
i += 2 + segLen;
|
|
1496
|
+
}
|
|
1497
|
+
return null;
|
|
1498
|
+
}
|
|
1499
|
+
if (IMAGE_FORMATS.tiff.magic(b)) {
|
|
1500
|
+
const little = b[0] === 73;
|
|
1501
|
+
const u16 = (n) => little ? u(n) | u(n + 1) << 8 : u(n) << 8 | u(n + 1);
|
|
1502
|
+
const u32 = (n) => little ? (u(n) | u(n + 1) << 8 | u(n + 2) << 16 | u(n + 3) << 24) >>> 0 : (u(n) << 24 | u(n + 1) << 16 | u(n + 2) << 8 | u(n + 3)) >>> 0;
|
|
1503
|
+
const ifd = u32(4);
|
|
1504
|
+
if (ifd + 2 > b.length) return null;
|
|
1505
|
+
const entries = u16(ifd);
|
|
1506
|
+
let w = 0;
|
|
1507
|
+
let h = 0;
|
|
1508
|
+
for (let i = 0; i < entries; i++) {
|
|
1509
|
+
const entry = ifd + 2 + i * 12;
|
|
1510
|
+
if (entry + 12 > b.length) return null;
|
|
1511
|
+
const tag = u16(entry);
|
|
1512
|
+
if (tag !== 256 && tag !== 257) continue;
|
|
1513
|
+
const value = u16(entry + 2) === 3 ? u16(entry + 8) : u32(entry + 8);
|
|
1514
|
+
if (tag === 256) w = value;
|
|
1515
|
+
else h = value;
|
|
1516
|
+
}
|
|
1517
|
+
return w > 0 && h > 0 ? {
|
|
1518
|
+
w,
|
|
1519
|
+
h
|
|
1520
|
+
} : null;
|
|
1521
|
+
}
|
|
1522
|
+
const text = utf8Decode(b);
|
|
1523
|
+
if (/<svg[\s>]/i.test(text)) return getSvgSizeFromMarkup(text);
|
|
1524
|
+
return null;
|
|
1525
|
+
}
|
|
1526
|
+
/**
|
|
1527
|
+
* Compute the `<a:srcRect>` crop percentages (each in 1/1000 of a percent, the
|
|
1528
|
+
* OOXML unit) for fitting an image of natural size `img` into a display `box`,
|
|
1529
|
+
* assuming the cropped region is then stretched to fill the box (`<a:stretch>`).
|
|
1530
|
+
*
|
|
1531
|
+
* - `cover`: fill the box, cropping the overflowing axis (positive l/r or t/b)
|
|
1532
|
+
* - `contain`: fit inside the box, letterboxing the short axis (negative l/r or t/b)
|
|
1533
|
+
*
|
|
1534
|
+
* Single source of truth for the crop math shared by the write side
|
|
1535
|
+
* (`ImageSizingXml`) and the read API's `Picture.setImage({ fit })`. `l`/`r` and
|
|
1536
|
+
* `t`/`b` are symmetric (centered crop).
|
|
1537
|
+
*
|
|
1538
|
+
* There is no fit for a side that is not greater than zero: a zero-size box gave every percentage
|
|
1539
|
+
* as `NaN`, and a zero-width one gave `-Infinity`, both written straight into the part.
|
|
1540
|
+
* @param {'cover' | 'contain'} type - fit mode
|
|
1541
|
+
* @param {{ w: number, h: number }} img - natural image pixel size
|
|
1542
|
+
* @param {{ w: number, h: number }} box - displayed frame size (any consistent unit)
|
|
1543
|
+
* @returns {{ l: number, r: number, t: number, b: number } | null} srcRect percentages, or `null`
|
|
1544
|
+
* when any of the four sizes is not greater than zero
|
|
1545
|
+
*/
|
|
1546
|
+
function fitSrcRectPercents(type, img, box) {
|
|
1547
|
+
if (!(img.w > 0 && img.h > 0 && box.w > 0 && box.h > 0)) return null;
|
|
1548
|
+
const imgRatio = img.h / img.w;
|
|
1549
|
+
const boxRatio = box.h / box.w;
|
|
1550
|
+
let width;
|
|
1551
|
+
let height;
|
|
1552
|
+
if (type === "cover") {
|
|
1553
|
+
const isBoxBased = boxRatio > imgRatio;
|
|
1554
|
+
width = isBoxBased ? box.h / imgRatio : box.w;
|
|
1555
|
+
height = isBoxBased ? box.h : box.w * imgRatio;
|
|
1556
|
+
} else {
|
|
1557
|
+
const widthBased = boxRatio > imgRatio;
|
|
1558
|
+
width = widthBased ? box.w : box.h / imgRatio;
|
|
1559
|
+
height = widthBased ? box.w * imgRatio : box.h;
|
|
1560
|
+
}
|
|
1561
|
+
const hz = Math.round(PERCENT_SCALE * .5 * (1 - box.w / width));
|
|
1562
|
+
const vz = Math.round(PERCENT_SCALE * .5 * (1 - box.h / height));
|
|
1563
|
+
return {
|
|
1564
|
+
l: hz,
|
|
1565
|
+
r: hz,
|
|
1566
|
+
t: vz,
|
|
1567
|
+
b: vz
|
|
1568
|
+
};
|
|
1569
|
+
}
|
|
1570
|
+
/**
|
|
1571
|
+
* Read the intrinsic size of an SVG document from its root `<svg>` element.
|
|
1572
|
+
* Follows the SVG sizing model: an explicit absolute `width`/`height` pair wins;
|
|
1573
|
+
* otherwise the `viewBox` width/height defines the size (and thus aspect ratio).
|
|
1574
|
+
* Percentage or missing `width`/`height` fall through to `viewBox`.
|
|
1575
|
+
* @param {string} svg - SVG markup
|
|
1576
|
+
* @returns {{ w: number, h: number } | null} intrinsic size, or `null` when undeterminable
|
|
1577
|
+
*/
|
|
1578
|
+
function getSvgSizeFromMarkup(svg) {
|
|
1579
|
+
const openTag = /<svg\b[^>]*>/i.exec(svg)?.[0];
|
|
1580
|
+
if (!openTag) return null;
|
|
1581
|
+
const attr = (name) => new RegExp(`(?:^|\\s)${name}\\s*=\\s*(["'])(.*?)\\1`, "i").exec(openTag)?.[2] ?? null;
|
|
1582
|
+
const absLength = (val) => {
|
|
1583
|
+
if (val == null || /%\s*$/.test(val)) return NaN;
|
|
1584
|
+
const m = /^\s*\+?(\d*\.?\d+)/.exec(val);
|
|
1585
|
+
return m ? parseFloat(m[1] ?? "") : NaN;
|
|
1586
|
+
};
|
|
1587
|
+
let w = absLength(attr("width"));
|
|
1588
|
+
let h = absLength(attr("height"));
|
|
1589
|
+
if (!(w > 0 && h > 0)) {
|
|
1590
|
+
const vb = attr("viewBox");
|
|
1591
|
+
const p = vb ? vb.trim().split(/[\s,]+/).map(Number) : [];
|
|
1592
|
+
const vw = p[2];
|
|
1593
|
+
const vh = p[3];
|
|
1594
|
+
if (p.length === 4 && vw != null && vh != null && vw > 0 && vh > 0) {
|
|
1595
|
+
w = vw;
|
|
1596
|
+
h = vh;
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
return w > 0 && h > 0 ? {
|
|
1600
|
+
w,
|
|
1601
|
+
h
|
|
1602
|
+
} : null;
|
|
1603
|
+
}
|
|
1604
|
+
/** Decode UTF-8 bytes to a string, isomorphic across Node and browsers. */
|
|
1605
|
+
function utf8Decode(bytes) {
|
|
1606
|
+
return new TextDecoder().decode(bytes);
|
|
1607
|
+
}
|
|
1608
|
+
//#endregion
|
|
1609
|
+
export { TICK_MARKS as $, THEME_REL as $t, GROUPINGS as A, NOTES_MASTER_CONTENT_TYPE as At, RADAR_STYLE_ALIAS_NAMES as B, PRES_PROPS_CONTENT_TYPE as Bt, CROSS_BETWEEN as C, HYPERLINK_REL as Ct, ERROR_BAR_DIRECTIONS as D, MS_MEDIA_REL as Dt, EMBEDDED_FONT_SLOTS as E, MODERN_COMMENT_AUTHORS_REL as Et, LINE_END_TYPES as F, OFFICE_DOCUMENT_REL as Ft, TEXT_HORZ_OVERFLOW as G, SLIDE_LAYOUT_CONTENT_TYPE as Gt, TEXT_ANCHORS as H, RELATIONSHIPS_CONTENT_TYPE as Ht, PRESET_LINE_DASHES as I, OFFICE_REL as It, TEXT_TAB_ALIGN_TYPES as J, SLIDE_MASTER_REL as Jt, TEXT_SHAPE_TYPES as K, SLIDE_LAYOUT_REL as Kt, PRESET_MATERIALS as L, PACKAGE_REL as Lt, LIGHT_RIGS as M, NOTES_SLIDE_CONTENT_TYPE as Mt, LIGHT_RIG_DIRECTIONS as N, NOTES_SLIDE_REL as Nt, ERROR_BAR_TYPES as O, MS_POWERPOINT_CONTENT as Ot, LINE_DATA_SYMBOLS as P, OD_CONTENT as Pt, TICK_LABEL_POSITIONS as Q, THEME_CONTENT_TYPE as Qt, RADAR_STYLES as R, PACKAGE_REL_NS as Rt, COLOR_MAP_TOKENS as S, FONT_REL as St, DISPLAY_BLANKS_AS as T, MODEL3D_REL as Tt, TEXT_AUTONUM_SCHEMES as U, SHARED_PARTS as Ut, SCHEME_COLOR_VALUES as V, PRES_PROPS_REL as Vt, TEXT_CAPS_TYPES as W, SLIDE_CONTENT_TYPE as Wt, TEXT_VERTICAL as X, TABLE_STYLES_CONTENT_TYPE as Xt, TEXT_UNDERLINE_TYPES as Y, SLIDE_REL as Yt, THEME_COLOR_SLOTS as Z, TABLE_STYLES_REL as Zt, BAR_3D_SHAPES as _, CORE_PROPS_REL as _t, textAlignToken as a, PML_ROOT_NS as an, CHARTEX_REL as at, BEVEL_PRESETS as b, EXTENDED_PROPS_CONTENT_TYPE as bt, checkEnumOrThrow as c, decodeBase64ToBytes as cn, CHART_CONTENT_TYPE as ct, xsdBool as d, toMediaDataUri as dn, CHART_STYLE_REL as dt, VIDEO_REL as en, TIME_UNITS as et, xsdBoolIfTrue as f, imageFormatForBytes as fn, COMMENTS_CONTENT_TYPE as ft, AXIS_ORIENTATIONS as g, CORE_PROPS_CONTENT_TYPE as gt, stripHash as h, COMMENT_AUTHORS_REL as ht, HALIGN_BY_TEXT_ALIGN as i, OOXML_NS as in, CHARTEX_CONTENT_TYPE as it, LEGEND_POSITIONS as j, NOTES_MASTER_REL as jt, ERROR_BAR_VALUE_TYPES as k, MS_REL as kt, checkEnumOrWarn as l, hasBase64Header as ln, CHART_REL as lt, splitRgbaHex as m, imageFormatForExtension as mn, COMMENT_AUTHORS_CONTENT_TYPE as mt, getImageSizeFromBase64 as n, VIEW_PROPS_REL as nn, TRANSITION_VARIANT_ATTRIBUTES as nt, LINE_CAP_BY_TOKEN as o, TABLE_GRAPHIC_DATA_URI as on, CHART_COLOR_STYLE_CONTENT_TYPE as ot, isHexColor as p, imageFormatForContentType as pn, COMMENTS_REL as pt, TEXT_STRIKE_TYPES as q, SLIDE_MASTER_CONTENT_TYPE as qt, getImageSizeFromBytes as r, XLSX_CONTENT_TYPE as rn, AUDIO_REL as rt, lineCapToken as s, bytesToBase64 as sn, CHART_COLOR_STYLE_REL as st, fitSrcRectPercents as t, VIEW_PROPS_CONTENT_TYPE as tn, TRANSITION_TYPES as tt, boolValue as u, svgMarkupToDataUri as un, CHART_STYLE_CONTENT_TYPE as ut, BAR_DIRECTIONS as v, CUSTOM_PROPS_CONTENT_TYPE as vt, DEFAULT_COLOR_MAP as w, IMAGE_REL as wt, BUILT_IN_UNITS as x, EXTENDED_PROPS_REL as xt, BAR_GROUPINGS as y, CUSTOM_PROPS_REL as yt, RADAR_STYLE_ALIASES as z, PRESENTATION_MAIN_CONTENT_TYPE as zt };
|
|
1610
|
+
|
|
1611
|
+
//# sourceMappingURL=image-size-CvN2iCA4.js.map
|