pptx-ts 3.7.0 → 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-ZqAdVvHV.d.ts → pres-C6qgOFAa.d.ts} +1021 -1423
- package/dist/pres-C6qgOFAa.d.ts.map +1 -0
- package/dist/{presentation-DrJtj3-C.js → presentation-DGYlAWhl.js} +3208 -2727
- package/dist/presentation-DGYlAWhl.js.map +1 -0
- package/dist/{presentation-types-CDjScsev.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-DfBdDHre.d.ts +0 -829
- package/dist/diagnostics-DfBdDHre.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-CJFJaG6M.js +0 -1724
- package/dist/notes-CJFJaG6M.js.map +0 -1
- package/dist/package-CaOLXSNi.d.ts.map +0 -1
- package/dist/pres-BcBR3CSM.d.ts +0 -202
- package/dist/pres-BcBR3CSM.d.ts.map +0 -1
- package/dist/presentation-DrJtj3-C.js.map +0 -1
- package/dist/presentation-types-CDjScsev.d.ts.map +0 -1
- package/dist/shapes-BemroU-z.js.map +0 -1
- package/dist/slide-ZqAdVvHV.d.ts.map +0 -1
- package/dist/text-B8SLEaZx.js +0 -11327
- package/dist/text-B8SLEaZx.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,649 @@
|
|
|
1
|
+
import { l as warn, n as InvalidOptionError, u as warnOnce } from "./errors-C4oENPcu.js";
|
|
2
|
+
import { A as inchesToEmu, E as coordToEmu, F as EMU_PER_INCH, I as EMU_PER_POINT, S as MAX_COORDINATE_EMU, b as FIXED_PCT_PER_PERCENT, t as TABLE_CELL_MARGIN_DEFAULTS_EMU, v as ANGLE_UNITS_PER_DEGREE, w as PERCENT_SCALE } from "./table-cell-margins-EVg0EwxT.js";
|
|
3
|
+
//#region src/ooxml/ids.ts
|
|
4
|
+
/** `ST_SlideId` maximum, inclusive. The schema's `maxExclusive` is 2147483648. */
|
|
5
|
+
const MAX_SLIDE_ID = 2147483647;
|
|
6
|
+
/**
|
|
7
|
+
* `ST_SlideMasterId` minimum (0x80000000). `ST_SlideLayoutId` has the same floor, and the two draw
|
|
8
|
+
* from one id space that must be unique across the whole presentation.
|
|
9
|
+
*/
|
|
10
|
+
const MIN_SLIDE_MASTER_ID = 2147483648;
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/constants-internal.ts
|
|
13
|
+
const LAYOUT_IDX_SERIES_BASE = 2147483649;
|
|
14
|
+
const LINEH_MODIFIER = 1.67;
|
|
15
|
+
const DEF_CELL_BORDER = {
|
|
16
|
+
type: "solid",
|
|
17
|
+
color: "666666",
|
|
18
|
+
width: 1
|
|
19
|
+
};
|
|
20
|
+
const DEF_CELL_MARGIN_IN = [
|
|
21
|
+
TABLE_CELL_MARGIN_DEFAULTS_EMU.top / EMU_PER_INCH,
|
|
22
|
+
TABLE_CELL_MARGIN_DEFAULTS_EMU.right / EMU_PER_INCH,
|
|
23
|
+
TABLE_CELL_MARGIN_DEFAULTS_EMU.bottom / EMU_PER_INCH,
|
|
24
|
+
TABLE_CELL_MARGIN_DEFAULTS_EMU.left / EMU_PER_INCH
|
|
25
|
+
];
|
|
26
|
+
const DEF_CHART_BORDER = {
|
|
27
|
+
type: "solid",
|
|
28
|
+
color: "363636",
|
|
29
|
+
width: 1
|
|
30
|
+
};
|
|
31
|
+
const DEF_CHART_GRIDLINE = {
|
|
32
|
+
color: "888888",
|
|
33
|
+
dashType: "solid",
|
|
34
|
+
width: 1,
|
|
35
|
+
cap: "flat"
|
|
36
|
+
};
|
|
37
|
+
const DEF_FONT_COLOR = "000000";
|
|
38
|
+
const MAX_FONT_SIZE_PT = 4e3;
|
|
39
|
+
const DEF_PRES_LAYOUT = "LAYOUT_16x9";
|
|
40
|
+
const DEF_PRES_LAYOUT_NAME = "DEFAULT";
|
|
41
|
+
const DEF_SHAPE_LINE_COLOR = "333333";
|
|
42
|
+
const DEF_SHAPE_SHADOW = {
|
|
43
|
+
type: "outer",
|
|
44
|
+
blur: 3,
|
|
45
|
+
offset: 23e3 / EMU_PER_POINT,
|
|
46
|
+
angle: 90,
|
|
47
|
+
color: "000000",
|
|
48
|
+
_alpha: .35,
|
|
49
|
+
rotateWithShape: true
|
|
50
|
+
};
|
|
51
|
+
const DEF_SLIDE_MARGIN_IN = [
|
|
52
|
+
.5,
|
|
53
|
+
.5,
|
|
54
|
+
.5,
|
|
55
|
+
.5
|
|
56
|
+
];
|
|
57
|
+
const DEF_TEXT_SHADOW = {
|
|
58
|
+
type: "outer",
|
|
59
|
+
blur: 8,
|
|
60
|
+
offset: 4,
|
|
61
|
+
angle: 270,
|
|
62
|
+
color: "000000",
|
|
63
|
+
_alpha: .75
|
|
64
|
+
};
|
|
65
|
+
const DEF_TEXT_GLOW = {
|
|
66
|
+
size: 8,
|
|
67
|
+
color: "FFFFFF",
|
|
68
|
+
opacity: .75
|
|
69
|
+
};
|
|
70
|
+
const AXIS_ID_VALUE_PRIMARY = "2094734552";
|
|
71
|
+
const AXIS_ID_VALUE_SECONDARY = "2094734553";
|
|
72
|
+
const AXIS_ID_CATEGORY_PRIMARY = "2094734554";
|
|
73
|
+
const AXIS_ID_CATEGORY_SECONDARY = "2094734555";
|
|
74
|
+
const AXIS_ID_SERIES_PRIMARY = "2094734556";
|
|
75
|
+
const LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
|
|
76
|
+
const BARCHART_COLORS = [
|
|
77
|
+
"C0504D",
|
|
78
|
+
"4F81BD",
|
|
79
|
+
"9BBB59",
|
|
80
|
+
"8064A2",
|
|
81
|
+
"4BACC6",
|
|
82
|
+
"F79646",
|
|
83
|
+
"628FC6",
|
|
84
|
+
"C86360"
|
|
85
|
+
];
|
|
86
|
+
const PIECHART_COLORS = [
|
|
87
|
+
"5DA5DA",
|
|
88
|
+
"FAA43A",
|
|
89
|
+
"60BD68",
|
|
90
|
+
"F17CB0",
|
|
91
|
+
"B2912F",
|
|
92
|
+
"B276B2",
|
|
93
|
+
"DECF3F",
|
|
94
|
+
"F15854",
|
|
95
|
+
"A7A7A7"
|
|
96
|
+
];
|
|
97
|
+
const SLDNUMFLDID = "{F7021451-1387-4CA6-816F-3879F97B5CBC}";
|
|
98
|
+
const SLDNUM_PLACEHOLDER_TEXT = "‹#›";
|
|
99
|
+
/**
|
|
100
|
+
* The nine list levels' default left margins (`a:lvlNpPr/@marL`), half an inch apart.
|
|
101
|
+
*
|
|
102
|
+
* `p:txStyles`' body and other styles, `p:notesStyle` and the presentation's
|
|
103
|
+
* `p:defaultTextStyle` are four separate ladders in the file and one ladder in fact: the
|
|
104
|
+
* three modules that emit them each carried their own copy of these nine numbers, so a
|
|
105
|
+
* level's margin could only be changed in one of them by accident.
|
|
106
|
+
*/
|
|
107
|
+
const LEVEL_MARGINS_EMU = Array.from({ length: 9 }, (_, i) => i * (EMU_PER_INCH / 2));
|
|
108
|
+
/**
|
|
109
|
+
* The paragraph-property tail every default `a:lvlNpPr` carries, whatever else it sets.
|
|
110
|
+
*
|
|
111
|
+
* Spread it *after* the level's own attributes: `el()` emits in `Object.entries` order, so
|
|
112
|
+
* where these land in the tag is byte-significant, and every emitting site puts `marL` (and
|
|
113
|
+
* where it has one, `indent` and `algn`) ahead of them.
|
|
114
|
+
*
|
|
115
|
+
* `algn` is deliberately not in here even though three of the four sites write `algn="l"`.
|
|
116
|
+
* The fourth computes it per level — a configured master's title can be centred or right —
|
|
117
|
+
* and a spread that carried `algn` would silently overwrite that: a later key in an object
|
|
118
|
+
* literal replaces the value without moving the key, so the attribute order would still look
|
|
119
|
+
* right while the alignment was wrong. No showcase deck configures a master text style, so
|
|
120
|
+
* `byte-identity` cannot see that path; `master-text-styles.test.js` pins it instead.
|
|
121
|
+
*/
|
|
122
|
+
const LEVEL_PPR_TAIL = {
|
|
123
|
+
defTabSz: EMU_PER_INCH,
|
|
124
|
+
rtl: 0,
|
|
125
|
+
eaLnBrk: 1,
|
|
126
|
+
latinLnBrk: 0,
|
|
127
|
+
hangingPunct: 1
|
|
128
|
+
};
|
|
129
|
+
//#endregion
|
|
130
|
+
//#region src/units-internal.ts
|
|
131
|
+
/**
|
|
132
|
+
* Generator-side unit conversion — the lenient layer over `units.ts`.
|
|
133
|
+
*
|
|
134
|
+
* `units.ts` holds the strict, public primitives: one branded `Emu` type and converters that
|
|
135
|
+
* take a number and throw on anything they cannot represent. This module holds what the
|
|
136
|
+
* generators actually need on top of that: converters that accept the loose shapes the
|
|
137
|
+
* authoring API has always accepted (numeric strings, `"5in"`, `null`), that clamp to the
|
|
138
|
+
* ranges PowerPoint will load without offering to repair the file, and that warn about legacy
|
|
139
|
+
* inputs instead of failing.
|
|
140
|
+
*
|
|
141
|
+
* The split is deliberate: the leniency and the back-compat warnings are policy, not
|
|
142
|
+
* arithmetic, and policy should not be part of the published API. Nothing here is exported
|
|
143
|
+
* from an entrypoint.
|
|
144
|
+
*/
|
|
145
|
+
/**
|
|
146
|
+
* Resolve a user `Coord` (x/y/w/h) to EMU — the single user-coordinate → EMU boundary.
|
|
147
|
+
* - bare `number` → **inches** (no magnitude guessing); `"<n>%"` → percent of the slide axis;
|
|
148
|
+
* `"<n>in"`/`"<n>pt"`/`"<n>emu"` → explicit units (see {@link Coord} / {@link coordToEmu})
|
|
149
|
+
* - `null`/`undefined` → 0 (callers may omit a coordinate)
|
|
150
|
+
* - throws on a non-finite number rather than silently collapsing the object to zero size
|
|
151
|
+
* @param {Coord|null|undefined} size - user coordinate
|
|
152
|
+
* @param {'X' | 'Y'} xyDir - axis (selects slide width vs height for percentages)
|
|
153
|
+
* @param {PresLayout} layout - presentation layout (EMU dimensions)
|
|
154
|
+
* @returns {Emu} resolved EMU value
|
|
155
|
+
*/
|
|
156
|
+
function getSmartParseNumber(size, xyDir, layout) {
|
|
157
|
+
if (size === null || size === void 0) return 0;
|
|
158
|
+
if (typeof size === "number" && !Number.isFinite(size)) throw new InvalidOptionError("coord/non-finite", `Invalid ${xyDir || "coordinate"} value: expected a finite number but received ${String(size)}. This usually means a layout dimension was read from a missing property (e.g. \`layout.width\` returning \`undefined\`). Use \`slide.width\`/\`slide.height\` or \`STANDARD_LAYOUTS.<NAME>.width\`/\`.height\` (inches).`);
|
|
159
|
+
return coordToEmu(size, xyDir === "Y" ? layout.height : layout.width);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Convert inches into EMU.
|
|
163
|
+
* - accepts a number (inches) or a numeric/`"<n>in"` string
|
|
164
|
+
* - no magnitude guessing: values are always treated as inches (use {@link coordToEmu} for
|
|
165
|
+
* user coordinates that may carry other units)
|
|
166
|
+
* @param {number|string} inches - inches as number or string
|
|
167
|
+
* @returns {Emu} EMU value
|
|
168
|
+
*/
|
|
169
|
+
function inch2Emu(inches) {
|
|
170
|
+
if (typeof inches === "string") inches = Number(inches.replace(/in*/gi, ""));
|
|
171
|
+
return inchesToEmu(inches);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Convert a single `margin` component (table cell/table margin, or text-box/placeholder body
|
|
175
|
+
* inset) to EMU.
|
|
176
|
+
*
|
|
177
|
+
* Margins are INCHES, consistent with the positional API (`x`/`y`/`w`/`h`) and the value
|
|
178
|
+
* PowerPoint's own dialog shows (both the table cell-margin and the text-box internal-margin
|
|
179
|
+
* fields are inches). Historically the library read these as POINTS (table cells used a magnitude
|
|
180
|
+
* heuristic — `>= 1` points, `< 1` inches; text-box margins were straight points), so a legitimate
|
|
181
|
+
* fraction-of-an-inch value entered from the PowerPoint dialog became a tiny points value. Every
|
|
182
|
+
* value is now inches. A `>= 1` value is honored as inches but warns once, because it is almost
|
|
183
|
+
* certainly a legacy points value that should be divided by 72 (e.g. `10` points → `0.139` inches).
|
|
184
|
+
*
|
|
185
|
+
* Shared by every margin site so they stay in lockstep: the cell XML emitter and text-box/slide-
|
|
186
|
+
* number insets (`gen/slide/object.ts`), the autoPage row-height pass (`gen/table/autopage.ts`), and the measured-fit pass
|
|
187
|
+
* (`measure-fit`).
|
|
188
|
+
* @param {number} inches - margin component in inches
|
|
189
|
+
* @returns {Emu} EMU value
|
|
190
|
+
*/
|
|
191
|
+
function marginToEmu(inches) {
|
|
192
|
+
if (inches >= 1) warnOnce("margin/legacy-points", "margins (table cell and text-box) are interpreted as inches (matching the rest of the API and the PowerPoint dialog); a value >= 1 is likely a legacy points value — divide by 72 to convert (e.g. 10pt => 0.139in).");
|
|
193
|
+
return inch2Emu(inches);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* A `[Top, Right, Bottom, Left]` margin (inches) as the four text insets in EMU, or `null` when
|
|
197
|
+
* the caller stated no margin.
|
|
198
|
+
*
|
|
199
|
+
* A scalar broadcasts to all four sides. The index order is the whole point of naming this:
|
|
200
|
+
* the arrays are CSS order while `a:bodyPr`/`a:tcPr` name their insets left-first, so every
|
|
201
|
+
* site that mapped it by hand spelled the same 3/0/1/2 shuffle — the text box's `_bodyProp`,
|
|
202
|
+
* the slide-number placeholder's `a:bodyPr`, and a table cell's `marL/R/T/B`.
|
|
203
|
+
*
|
|
204
|
+
* Each component goes through {@link marginToEmu}, so the legacy-points warning still fires
|
|
205
|
+
* once for a value of an inch or more.
|
|
206
|
+
* @param margin - the caller's `margin`, a scalar or a `[T, R, B, L]` array of inches
|
|
207
|
+
*/
|
|
208
|
+
function resolveInsetsEmu(margin) {
|
|
209
|
+
if (typeof margin === "number") {
|
|
210
|
+
const all = marginToEmu(margin);
|
|
211
|
+
return {
|
|
212
|
+
l: all,
|
|
213
|
+
t: all,
|
|
214
|
+
r: all,
|
|
215
|
+
b: all
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
if (!Array.isArray(margin)) return null;
|
|
219
|
+
const at = (idx) => marginToEmu(margin[idx] ?? 0);
|
|
220
|
+
return {
|
|
221
|
+
l: at(3),
|
|
222
|
+
t: at(0),
|
|
223
|
+
r: at(1),
|
|
224
|
+
b: at(2)
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* One line of table text, in EMU: `fontSizePt * (LINEH_MODIFIER + lineWeight) / 100`.
|
|
229
|
+
*
|
|
230
|
+
* The auto-pager prices a row by counting lines and multiplying by this, and the measured-fit
|
|
231
|
+
* pass estimates a cell's height with the same product. Three sites wrote the expression by
|
|
232
|
+
* hand and one of them left the caller's `autoPageLineWeight` out, so the pager guarded a row
|
|
233
|
+
* against a line it then measured at a different height.
|
|
234
|
+
*
|
|
235
|
+
* @param fontSizePt - the resolved font size in points
|
|
236
|
+
* @param lineWeight - the caller's `autoPageLineWeight`, an addend on the modifier
|
|
237
|
+
*/
|
|
238
|
+
function autoPageLineHeightEmu(fontSizePt, lineWeight = 0) {
|
|
239
|
+
return inch2Emu(fontSizePt * (LINEH_MODIFIER + lineWeight) / 100);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* The four slide margins in inches, `[top, right, bottom, left]`, from the master's own margin
|
|
243
|
+
* and the caller's `slideMargin`.
|
|
244
|
+
*
|
|
245
|
+
* Precedence is caller, then master, then {@link DEF_SLIDE_MARGIN_IN}. A scalar broadcasts to
|
|
246
|
+
* all four sides; an array is taken as-is.
|
|
247
|
+
*
|
|
248
|
+
* Three sites derived this and they already disagreed. Two gated the master on
|
|
249
|
+
* `typeof !== 'undefined'` and coerced with `Number.isFinite(Number(m))`; the HTML path gated
|
|
250
|
+
* on truthiness and tested `Number.isFinite(m)` with no coercion. So a master with
|
|
251
|
+
* `_margin: 0` took the master branch in two of them and the caller branch in the third, and
|
|
252
|
+
* a master with `_margin: "0.25"` resolved in two and was ignored in the third.
|
|
253
|
+
*
|
|
254
|
+
* **The caller-first order was decided, the master-first one it replaced was not.** All three
|
|
255
|
+
* pre-consolidation sites read the master first, inherited from upstream, and the consolidation
|
|
256
|
+
* kept that order rather than deciding it -- so on a deck whose master defined a `margin`,
|
|
257
|
+
* `addTable(rows, { slideMargin: 2 })` was discarded with nothing said, which is the reverse of
|
|
258
|
+
* the rule stated everywhere else here: an explicit option beats an inherited one. The master's
|
|
259
|
+
* `margin` is now the deck-wide default a per-table `slideMargin` overrides. A master margin as
|
|
260
|
+
* a *floor* rather than a ceiling is a third reading, and if anyone needs it, it wants a stated
|
|
261
|
+
* option rather than a precedence accident.
|
|
262
|
+
*
|
|
263
|
+
* @param masterMargin - the master/layout's `_margin`, in inches
|
|
264
|
+
* @param slideMargin - the caller's `slideMargin`, in inches
|
|
265
|
+
*/
|
|
266
|
+
function resolveSlideMarginsInches(masterMargin, slideMargin) {
|
|
267
|
+
const broadcast = (value) => {
|
|
268
|
+
if (Array.isArray(value)) {
|
|
269
|
+
const [t = 0, r = 0, b = 0, l = 0] = value;
|
|
270
|
+
return [
|
|
271
|
+
t,
|
|
272
|
+
r,
|
|
273
|
+
b,
|
|
274
|
+
l
|
|
275
|
+
];
|
|
276
|
+
}
|
|
277
|
+
const n = Number(value);
|
|
278
|
+
return Number.isFinite(n) ? [
|
|
279
|
+
n,
|
|
280
|
+
n,
|
|
281
|
+
n,
|
|
282
|
+
n
|
|
283
|
+
] : null;
|
|
284
|
+
};
|
|
285
|
+
if (slideMargin !== void 0 && slideMargin !== null) {
|
|
286
|
+
const resolved = broadcast(slideMargin);
|
|
287
|
+
if (resolved) return resolved;
|
|
288
|
+
}
|
|
289
|
+
if (masterMargin !== void 0 && masterMargin !== null) {
|
|
290
|
+
const resolved = broadcast(masterMargin);
|
|
291
|
+
if (resolved) return resolved;
|
|
292
|
+
}
|
|
293
|
+
return [...DEF_SLIDE_MARGIN_IN];
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Resolve a table's column widths to EMU, the single source of truth shared by the
|
|
297
|
+
* table XML emitter and the measured-fit pass (so a fitted cell sees the same grid
|
|
298
|
+
* the renderer draws).
|
|
299
|
+
* - an explicit `colW` **array** is per-column inches (`inch2Emu`); a non-finite slot
|
|
300
|
+
* falls back to the even-distribution width
|
|
301
|
+
* - otherwise the table's already-resolved width (`totalWidthEmu`, EMU) is split evenly
|
|
302
|
+
* across `colCount` columns. (A scalar `colW` never reaches here — `addTableDefinition`
|
|
303
|
+
* converts it to `w` and clears `colW`.)
|
|
304
|
+
*
|
|
305
|
+
* IMPORTANT: the even path divides an **EMU** width. Passing the raw inches `options.w`
|
|
306
|
+
* instead (the historical bug) produced ~0-EMU columns (e.g. `w=9` → `gridCol w="3"`),
|
|
307
|
+
* collapsing auto-width tables to a sliver in PowerPoint/LibreOffice.
|
|
308
|
+
* @param {Coord[]|Coord|undefined} colW - explicit per-column inches, or scalar/undefined
|
|
309
|
+
* @param {number} totalWidthEmu - the table's resolved width in EMU
|
|
310
|
+
* @param {number} colCount - number of grid columns (counting colspans)
|
|
311
|
+
* @returns {number[]} per-column widths in EMU (length `colCount`)
|
|
312
|
+
*/
|
|
313
|
+
function resolveTableColWidthsEmu(colW, totalWidthEmu, colCount) {
|
|
314
|
+
if (!(colCount > 0)) return [];
|
|
315
|
+
const even = totalWidthEmu > 0 ? Math.round(totalWidthEmu / colCount) : EMU_PER_INCH;
|
|
316
|
+
if (Array.isArray(colW)) return Array.from({ length: colCount }, (_, i) => {
|
|
317
|
+
const n = colW[i];
|
|
318
|
+
if (typeof n === "number" && Number.isFinite(n)) return Math.round(inch2Emu(n));
|
|
319
|
+
if (n !== void 0 && n !== null) warnOnce("table/invalid-col-width", `colW entry ${String(n)} is not a number of inches; that column takes an even share of the table width instead.`);
|
|
320
|
+
return even;
|
|
321
|
+
});
|
|
322
|
+
return new Array(colCount).fill(even);
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* The slide width a table may occupy, in EMU: the slide, less the table's own left
|
|
326
|
+
* edge and the right slide margin.
|
|
327
|
+
*
|
|
328
|
+
* The width twin of the auto-pager's `calcSlideTabH`, and the same reading
|
|
329
|
+
* `addTableDefinition` applies when a table states neither `w` nor `colW`. The pager
|
|
330
|
+
* used to *add* the two margins here instead of subtracting them, which made its
|
|
331
|
+
* fallback width about one inch however wide the slide was, and every column a sliver.
|
|
332
|
+
*
|
|
333
|
+
* @param presLayout - the presentation layout, for the slide width
|
|
334
|
+
* @param xEmu - the table's resolved left edge in EMU; `0`/absent falls back to the left slide margin
|
|
335
|
+
* @param marginsIn - the four slide margins in inches, as the pager holds them
|
|
336
|
+
* @returns the usable width in EMU, never below zero
|
|
337
|
+
*/
|
|
338
|
+
function usableTableWidthEmu(presLayout, xEmu, marginsIn) {
|
|
339
|
+
const startIn = xEmu ? xEmu / EMU_PER_INCH : marginsIn[3];
|
|
340
|
+
return Math.max(0, presLayout.width - inch2Emu(startIn) - inch2Emu(marginsIn[1]));
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* The single rule for whether one `rowH` entry pins a table row, in inches.
|
|
344
|
+
*
|
|
345
|
+
* An entry pins its row when it reads as a **finite number greater than zero**. Everything
|
|
346
|
+
* else returns `null` — the row is sized from the table's own height, or grows to fit — and
|
|
347
|
+
* an entry that is present but unusable warns, because `rowH: [0, …]` and `rowH: [-1, …]`
|
|
348
|
+
* are things a caller wrote on purpose and none of the readings this replaced was what they
|
|
349
|
+
* meant. A missing slot is not that: an array with holes is how the auto-pager spells
|
|
350
|
+
* "this row is auto-height", so `undefined` is silent.
|
|
351
|
+
*
|
|
352
|
+
* Three call sites read `rowH` and they disagreed. The writer and the export-time fit pass
|
|
353
|
+
* both used a truthiness test, so `0` fell through to the even split of `h`;
|
|
354
|
+
* `pptx.tableLayout()` used `typeof === 'number'`, so `0` pinned the row at zero inches and
|
|
355
|
+
* was then rescued by a default-line fallback — reporting 0.2in against the file's 2.0in,
|
|
356
|
+
* and flagging it `heightExact`. A negative entry reached `<a:tr h="-914400">`.
|
|
357
|
+
*
|
|
358
|
+
* @param entry - one `rowH` array slot, or the scalar `rowH`
|
|
359
|
+
* @returns the pinned height in inches, or `null` when the row is not pinned by `rowH`
|
|
360
|
+
*/
|
|
361
|
+
function pinnedRowHeightInches(entry) {
|
|
362
|
+
if (entry === void 0 || entry === null) return null;
|
|
363
|
+
const inches = Number(entry);
|
|
364
|
+
if (Number.isFinite(inches) && inches > 0) return inches;
|
|
365
|
+
warnOnce("table/invalid-row-height", `rowH entry ${String(entry)} is not a positive number of inches; the row is sized from the table height instead.`);
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* A table cell's `[Top, Right, Bottom, Left]` margins in inches, defaulted and validated.
|
|
370
|
+
*
|
|
371
|
+
* Three sites spelled this out -- two of them character-identical, the third self-described as
|
|
372
|
+
* "mirroring" one of them -- and a fourth in the auto-pager gated each side on truthiness, so a
|
|
373
|
+
* cell stating `margin: [0, …]` fell through to the table's margin instead of taking its own
|
|
374
|
+
* zero. All of them swapped an unusable margin for the default in silence, while every sibling
|
|
375
|
+
* resolver in this file warns for the same class of input.
|
|
376
|
+
*
|
|
377
|
+
* `resolveInsetsEmu` above is deliberately NOT a fifth copy: it resolves a text box's
|
|
378
|
+
* `a:bodyPr` insets, where stating nothing means writing no attribute at all rather than
|
|
379
|
+
* taking a cell default, so its absent case has a different answer.
|
|
380
|
+
* @param margin - the caller's `margin`: a scalar, a `[T, R, B, L]` array, or nothing
|
|
381
|
+
* @returns four finite inch measures
|
|
382
|
+
*/
|
|
383
|
+
function resolveCellMarginsInches(margin) {
|
|
384
|
+
if (margin === void 0 || margin === null) return [...DEF_CELL_MARGIN_IN];
|
|
385
|
+
const usable = (value) => typeof value === "number" && Number.isFinite(value);
|
|
386
|
+
if (typeof margin === "number") {
|
|
387
|
+
if (usable(margin)) return [
|
|
388
|
+
margin,
|
|
389
|
+
margin,
|
|
390
|
+
margin,
|
|
391
|
+
margin
|
|
392
|
+
];
|
|
393
|
+
} else if (Array.isArray(margin) && margin.length === 4 && margin.every(usable)) return [
|
|
394
|
+
margin[0],
|
|
395
|
+
margin[1],
|
|
396
|
+
margin[2],
|
|
397
|
+
margin[3]
|
|
398
|
+
];
|
|
399
|
+
warnOnce("table/invalid-margin", `table margin ${JSON.stringify(margin)} is not a number of inches or a [top, right, bottom, left] array of four; using the default.`);
|
|
400
|
+
return [...DEF_CELL_MARGIN_IN];
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Resolve one table row's height to EMU — the row-height twin of
|
|
404
|
+
* {@link resolveTableColWidthsEmu}, shared by the table XML emitter, the export-time
|
|
405
|
+
* measured-fit pass and `pptx.tableLayout()` so a prediction cannot disagree with what the
|
|
406
|
+
* export bakes.
|
|
407
|
+
*
|
|
408
|
+
* - a `rowH` entry that {@link pinnedRowHeightInches} accepts pins the row
|
|
409
|
+
* - otherwise the table's already-resolved height (`totalHeightEmu`, EMU) is split evenly
|
|
410
|
+
* across `rowCount` rows
|
|
411
|
+
* - otherwise `null`: the row is auto-height and grows to fit its content
|
|
412
|
+
*
|
|
413
|
+
* @param rowH - the caller's `rowH`, scalar or per-row array
|
|
414
|
+
* @param rowIndex - which row is being resolved
|
|
415
|
+
* @param totalHeightEmu - the table's resolved height in EMU, or 0 when it has none
|
|
416
|
+
* @param rowCount - number of rows to split that height across
|
|
417
|
+
* @returns the row height in EMU, or `null` for an auto-height row
|
|
418
|
+
*/
|
|
419
|
+
function resolveTableRowHeightEmu(rowH, rowIndex, totalHeightEmu, rowCount) {
|
|
420
|
+
const pinned = pinnedRowHeightInches(Array.isArray(rowH) ? rowH[rowIndex] : rowH);
|
|
421
|
+
if (pinned !== null) return Math.round(inch2Emu(pinned));
|
|
422
|
+
return totalHeightEmu > 0 && rowCount > 0 ? Math.round(totalHeightEmu / rowCount) : null;
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Convert points to EMU, leniently: anything that does not read as a finite number — a `NaN`,
|
|
426
|
+
* an `Infinity`, a non-numeric string, `null` — becomes `0` rather than throwing.
|
|
427
|
+
*
|
|
428
|
+
* That is the whole difference from {@link pointsToEmu} in `units.ts`, which throws on the same
|
|
429
|
+
* input. Emitters use this one for the many optional size/width/offset options where a missing
|
|
430
|
+
* or malformed value should collapse the feature (a zero-width line, no shadow offset) rather
|
|
431
|
+
* than take the whole deck down.
|
|
432
|
+
*
|
|
433
|
+
* @param pt - points, as a number or a numeric string
|
|
434
|
+
* @returns the value in EMU, or `0` if it does not read as a finite number
|
|
435
|
+
*/
|
|
436
|
+
function ptsToEmuLenient(pt) {
|
|
437
|
+
const points = Number(pt);
|
|
438
|
+
return Number.isFinite(points) ? Math.round(points * EMU_PER_POINT) : 0;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* `fn(value)` for a numeric option the caller stated, `undefined` for one they did not.
|
|
442
|
+
*
|
|
443
|
+
* `null`, `undefined` and `0` are "not stated": an absent option and a zero one emit the same
|
|
444
|
+
* thing (no `rot` for `rotate: 0`, the default width for `width: 0`). Every other value reaches
|
|
445
|
+
* `fn`, `NaN` included, so the converter behind it decides what a value it cannot represent
|
|
446
|
+
* means. The same test used to be spelled as truthiness, and `NaN` is falsy: it was dropped or
|
|
447
|
+
* replaced by the default before the converter could refuse it, while `Infinity` on the same
|
|
448
|
+
* option threw.
|
|
449
|
+
* @param value - the caller's option
|
|
450
|
+
* @param fn - the conversion to apply when the option is stated
|
|
451
|
+
* @returns `fn(value)`, or `undefined` when the option is not stated
|
|
452
|
+
*/
|
|
453
|
+
function mapStated(value, fn) {
|
|
454
|
+
return value === null || value === void 0 || value === 0 ? void 0 : fn(value);
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Clamp a caller-supplied number into the range its schema type allows, warning when it had to
|
|
458
|
+
* move. Returns the value in the caller's own unit — each helper below applies its own
|
|
459
|
+
* arithmetic afterwards, because the inverting and non-inverting forms do not round alike.
|
|
460
|
+
*
|
|
461
|
+
* `NaN` throws rather than clamping: there is no nearest in-range value for it, and
|
|
462
|
+
* `Math.min`/`Math.max` propagate it straight through to the attribute (`val="NaN"`), which is
|
|
463
|
+
* exactly the degenerate output the range check exists to prevent. `Infinity` is not in that
|
|
464
|
+
* category — it clamps to the bound like any other out-of-range number, and warns.
|
|
465
|
+
*
|
|
466
|
+
* **This is the one policy for an out-of-range number**, and it is the policy
|
|
467
|
+
* `docs/contributing/development.md` ("Warn or throw?") describes: a finite value has a nearest legal
|
|
468
|
+
* neighbour, so the deck still comes out recognisable and the move is a warning; a value that
|
|
469
|
+
* is not a number at all has no neighbour, so the request is discarded and that throws.
|
|
470
|
+
* Rejecting a finite out-of-range value and emitting nothing is neither: it discards the request
|
|
471
|
+
* and reports it as a warning, which is the combination the rule exists to rule out.
|
|
472
|
+
*
|
|
473
|
+
* Most of what passes through here is a percentage, which is why the throw names one by
|
|
474
|
+
* default; `nonFiniteCode` is for the callers whose option is not one, such as a shadow's
|
|
475
|
+
* angle in degrees.
|
|
476
|
+
*
|
|
477
|
+
* @param value - the caller's value
|
|
478
|
+
* @param min - inclusive lower bound, in the caller's unit
|
|
479
|
+
* @param max - inclusive upper bound, in the caller's unit
|
|
480
|
+
* @param code - diagnostic code raised when the value is clamped
|
|
481
|
+
* @param label - option name as the caller spells it, opening the warning
|
|
482
|
+
* @param nonFiniteCode - error code thrown when the value is not a number at all
|
|
483
|
+
* @param report - how a clamp is reported: `once` dedupes the warning on its code and message, as
|
|
484
|
+
* a measure repeated across every run of a deck needs, and `range` states the range in the
|
|
485
|
+
* caller's wording (`'1-4000pt'`) rather than as the two bare bounds
|
|
486
|
+
*/
|
|
487
|
+
function clampRangedInput(value, min, max, code, label, nonFiniteCode = "percent/non-finite", report = {}) {
|
|
488
|
+
if (typeof value !== "number" || Number.isNaN(value)) throw new InvalidOptionError(nonFiniteCode, `${label} must be a number from ${min} to ${max}; received ${String(value)}.`);
|
|
489
|
+
const clamped = Math.min(max, Math.max(min, value));
|
|
490
|
+
if (clamped !== value) {
|
|
491
|
+
const message = `${label} ${value} is outside the valid range ${report.range ?? `${min}-${max}`}; using ${clamped}.`;
|
|
492
|
+
if (report.once) warnOnce(code, message);
|
|
493
|
+
else warn(code, message);
|
|
494
|
+
}
|
|
495
|
+
return clamped;
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Convert a transparency percentage (0-100) into a schema-valid `<a:alpha>` value
|
|
499
|
+
* (ST_PositiveFixedPercentage, 0-100000). Out-of-range transparency yields an
|
|
500
|
+
* alpha that PowerPoint rejects as needing repair, so clamp into range and warn.
|
|
501
|
+
*
|
|
502
|
+
* This is the **inverting** form: 0 transparency is full opacity (`val="100000"`). Use
|
|
503
|
+
* {@link percentToFixedPercent} where the option already reads as opacity.
|
|
504
|
+
*/
|
|
505
|
+
function transparencyToAlpha(transparency) {
|
|
506
|
+
const pct = clampRangedInput(transparency, 0, 100, "transparency/out-of-range", "transparency");
|
|
507
|
+
return Math.round((100 - pct) * FIXED_PCT_PER_PERCENT);
|
|
508
|
+
}
|
|
509
|
+
/** Convert an opacity (0-1) into a schema-valid `<a:alpha>` value (0-100000); clamps + warns on out-of-range input. */
|
|
510
|
+
function opacityToAlpha(opacity) {
|
|
511
|
+
return fractionToFixedPercent(opacity, "opacity/out-of-range", "opacity");
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Convert a percentage into a schema-valid fixed-percentage value (thousandths of a percent) —
|
|
515
|
+
* the **non-inverting** sibling of {@link transparencyToAlpha}, for options the caller already
|
|
516
|
+
* states as "how much", such as a chart series' fill opacity.
|
|
517
|
+
*
|
|
518
|
+
* `min`/`max` default to the 0-100 nearly every such option allows. They are parameters because
|
|
519
|
+
* the fixed-percentage attributes do not all share one range: `<a:buSzPct>` is
|
|
520
|
+
* ST_TextBulletSizePercent (25-400), so the bullet-size option needs its own bounds while
|
|
521
|
+
* keeping this function's policy.
|
|
522
|
+
* @param value - the caller's percentage
|
|
523
|
+
* @param code - diagnostic code raised when the value is clamped
|
|
524
|
+
* @param label - option name as the caller spells it, opening the warning
|
|
525
|
+
* @param min - inclusive lower bound in percent
|
|
526
|
+
* @param max - inclusive upper bound in percent
|
|
527
|
+
*/
|
|
528
|
+
function percentToFixedPercent(value, code, label, min = 0, max = 100) {
|
|
529
|
+
return Math.round(clampRangedInput(value, min, max, code, label) * FIXED_PCT_PER_PERCENT);
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Convert a 0-1 fraction into a schema-valid fixed-percentage value (0-100000), for the options
|
|
533
|
+
* spelled as a fraction rather than a percentage (an opacity, a luminance threshold).
|
|
534
|
+
* @param value - the caller's fraction
|
|
535
|
+
* @param code - diagnostic code raised when the value is clamped
|
|
536
|
+
* @param label - option name as the caller spells it, opening the warning
|
|
537
|
+
*/
|
|
538
|
+
function fractionToFixedPercent(value, code, label) {
|
|
539
|
+
return Math.round(clampRangedInput(value, 0, 1, code, label) * PERCENT_SCALE);
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Convert a length stated in points to EMU, clamped into the schema range of the attribute it is
|
|
543
|
+
* headed for.
|
|
544
|
+
*
|
|
545
|
+
* A number goes through {@link clampRangedInput}, the one policy for an out-of-range number: a
|
|
546
|
+
* value past either bound clamps to it and warns, `Infinity` included, and `NaN` throws
|
|
547
|
+
* `coord/non-finite`. Both of those used to collapse to `0` without a word, so `width: Infinity`
|
|
548
|
+
* became a zero-width line while `width: 5000` clamped and warned.
|
|
549
|
+
*
|
|
550
|
+
* A value that does not read as a number at all, a string such as `'wide'`, still collapses the
|
|
551
|
+
* feature to `0` via {@link ptsToEmuLenient}, because these are optional decorations on an object
|
|
552
|
+
* that still has to be emitted. A numeric string past the range clamps and warns as before.
|
|
553
|
+
*
|
|
554
|
+
* @param pts - the caller's length in points, or any of the loose shapes `ptsToEmuLenient` takes
|
|
555
|
+
* @param maxEmu - inclusive upper bound of the attribute's schema type, in EMU
|
|
556
|
+
* @param code - diagnostic code raised when the value is clamped
|
|
557
|
+
* @param label - option name as the caller spells it, opening the warning
|
|
558
|
+
* @param range - the valid range in points, as the warning states it
|
|
559
|
+
*/
|
|
560
|
+
function clampLengthEmu(pts, maxEmu, code, label, range) {
|
|
561
|
+
if (typeof pts === "number") return Math.round(clampRangedInput(pts, 0, maxEmu / EMU_PER_POINT, code, label, "coord/non-finite") * EMU_PER_POINT);
|
|
562
|
+
const raw = ptsToEmuLenient(pts);
|
|
563
|
+
const clamped = Math.min(maxEmu, Math.max(0, raw));
|
|
564
|
+
if (clamped !== raw) warn(code, `${label} ${pts} is outside the valid range ${range}; using ${clamped / EMU_PER_POINT}.`);
|
|
565
|
+
return clamped;
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Convert a line width (points) to EMU clamped into ST_LineWidth (0..20116800 EMU,
|
|
569
|
+
* i.e. 0-1584pt). Out-of-range widths make PowerPoint report the package as needing
|
|
570
|
+
* repair, so clamp into range and warn.
|
|
571
|
+
*
|
|
572
|
+
* `label` is what the refusal quotes back, the way {@link clampFontSizeSz} already names the nine
|
|
573
|
+
* options that reach it. Every caller used to be reported as `line width`, so a `NaN` error-bar
|
|
574
|
+
* `width` or a series `lineSize` threw "line width must be a number from 0 to 1584" and named
|
|
575
|
+
* nothing the caller had written.
|
|
576
|
+
* @param widthPts - the caller's width in points
|
|
577
|
+
* @param label - the option as the caller spells it, e.g. `errorBars.width`
|
|
578
|
+
*/
|
|
579
|
+
function lineWidthToEmu(widthPts, label = "line width") {
|
|
580
|
+
return clampLengthEmu(widthPts, 20116800, "line/width-out-of-range", label, "0-1584pt");
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Convert a shadow's blur radius or offset distance (points) to EMU clamped into
|
|
584
|
+
* ST_PositiveCoordinate (0..27273042316900 EMU, i.e. 0-2147483647pt) — the type both
|
|
585
|
+
* `blurRad` and `dist` carry on `a:outerShdw`/`a:innerShdw`.
|
|
586
|
+
*
|
|
587
|
+
* **The bound is the schema's, not the `0-100` / `0-200` those options document.** Those two
|
|
588
|
+
* numbers are the limits of PowerPoint's own spinners, and a blur past them is unusual rather
|
|
589
|
+
* than invalid: the file loads and paints. The one input that genuinely breaks the package is a
|
|
590
|
+
* *negative* one, because the type is unsigned — so that is what this moves, on the same
|
|
591
|
+
* reasoning `clampLineSpacingMultiplePct` in `gen/drawingml/clamp.ts` clamps to
|
|
592
|
+
* ST_TextSpacingPercent rather than to the `0.0-9.99` its own option describes. Only values
|
|
593
|
+
* PowerPoint reports as needing repair are worth moving.
|
|
594
|
+
*/
|
|
595
|
+
function positiveCoordinateEmu(pts, code, label) {
|
|
596
|
+
return clampLengthEmu(pts, MAX_COORDINATE_EMU, code, label, "0-2147483647pt");
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Convert degrees to a DrawingML angle (60000ths of a degree), **without** wrapping.
|
|
600
|
+
*
|
|
601
|
+
* Most angles in the format are not modular: a connection site at 400 degrees, a polar adjust
|
|
602
|
+
* handle whose range runs to 540, an arc that sweeps 400 degrees — each means something a
|
|
603
|
+
* reduction into 0..360 would destroy. Use this for those, and {@link convertRotationDegrees}
|
|
604
|
+
* for the ones that genuinely are a rotation.
|
|
605
|
+
*
|
|
606
|
+
* Non-finite input throws rather than reaching the attribute: `Math.round(Infinity * 60000)` is
|
|
607
|
+
* `Infinity`, which serializes as `ang="Infinity"` and makes PowerPoint offer to repair the file.
|
|
608
|
+
*
|
|
609
|
+
* @param d - degrees
|
|
610
|
+
* @param what - what is being converted, for the error message
|
|
611
|
+
* @param code - diagnostic code to throw under, where a caller has a more specific one
|
|
612
|
+
*/
|
|
613
|
+
function convertAngleUnits(d, what, code = "coord/non-finite") {
|
|
614
|
+
assertFiniteDegrees(d, what, code);
|
|
615
|
+
return Math.round(d * ANGLE_UNITS_PER_DEGREE);
|
|
616
|
+
}
|
|
617
|
+
function assertFiniteDegrees(d, what, code) {
|
|
618
|
+
if (typeof d !== "number" || !Number.isFinite(d)) throw new InvalidOptionError(code, `${what} must be a finite number of degrees; received ${String(d)}.`);
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Convert a shape or gradient rotation (degrees) to a DrawingML angle (60000ths of a degree).
|
|
622
|
+
*
|
|
623
|
+
* A rotation *is* modular — 800 degrees and 80 degrees point the same way — so the value is
|
|
624
|
+
* reduced with `% 360`. The reduction keeps the sign: `-45` stays `-45` rather than becoming
|
|
625
|
+
* `315`, because both are valid `ST_Angle`, PowerPoint writes negative rotations itself, and
|
|
626
|
+
* the read side reports back what was authored. Only an input outside -360..360 moves.
|
|
627
|
+
*
|
|
628
|
+
* @param d - degrees
|
|
629
|
+
* @returns the `rot` value in 60000ths of a degree
|
|
630
|
+
*/
|
|
631
|
+
function convertRotationDegrees(d) {
|
|
632
|
+
assertFiniteDegrees(d, "rotation", "coord/non-finite");
|
|
633
|
+
return Math.round(d % 360 * ANGLE_UNITS_PER_DEGREE);
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Convert a freeform arc angle (degrees) to an `<a:arcTo>` ST_AdjAngle value (60000ths).
|
|
637
|
+
* Unlike a shape rotation, a sweep is not modular: a 400 degree swAng draws a different
|
|
638
|
+
* arc than a 40 degree one, so the value is never wrapped into 0..360.
|
|
639
|
+
* @param {number} d degrees
|
|
640
|
+
* @param {'stAng' | 'swAng'} attr - attribute being emitted, for the error message
|
|
641
|
+
* @returns {number} ST_AdjAngle value (60000ths of a degree)
|
|
642
|
+
*/
|
|
643
|
+
function convertArcAngle(d, attr) {
|
|
644
|
+
return convertAngleUnits(d, `Arc ${attr}`, "geometry/arc-angle-non-finite");
|
|
645
|
+
}
|
|
646
|
+
//#endregion
|
|
647
|
+
export { DEF_CELL_BORDER as A, DEF_TEXT_SHADOW as B, usableTableWidthEmu as C, AXIS_ID_VALUE_PRIMARY as D, AXIS_ID_SERIES_PRIMARY as E, DEF_PRES_LAYOUT_NAME as F, MAX_FONT_SIZE_PT as G, LETTERS as H, DEF_SHAPE_LINE_COLOR as I, SLDNUM_PLACEHOLDER_TEXT as J, PIECHART_COLORS as K, DEF_SHAPE_SHADOW as L, DEF_CHART_GRIDLINE as M, DEF_FONT_COLOR as N, AXIS_ID_VALUE_SECONDARY as O, DEF_PRES_LAYOUT as P, DEF_SLIDE_MARGIN_IN as R, transparencyToAlpha as S, AXIS_ID_CATEGORY_SECONDARY as T, LEVEL_MARGINS_EMU as U, LAYOUT_IDX_SERIES_BASE as V, LEVEL_PPR_TAIL as W, MIN_SLIDE_MASTER_ID as X, MAX_SLIDE_ID as Y, resolveCellMarginsInches as _, convertRotationDegrees as a, resolveTableColWidthsEmu as b, inch2Emu as c, marginToEmu as d, opacityToAlpha as f, ptsToEmuLenient as g, positiveCoordinateEmu as h, convertArcAngle as i, DEF_CHART_BORDER as j, BARCHART_COLORS as k, lineWidthToEmu as l, pinnedRowHeightInches as m, clampRangedInput as n, fractionToFixedPercent as o, percentToFixedPercent as p, SLDNUMFLDID as q, convertAngleUnits as r, getSmartParseNumber as s, autoPageLineHeightEmu as t, mapStated as u, resolveInsetsEmu as v, AXIS_ID_CATEGORY_PRIMARY as w, resolveTableRowHeightEmu as x, resolveSlideMarginsInches as y, DEF_TEXT_GLOW as z };
|
|
648
|
+
|
|
649
|
+
//# sourceMappingURL=units-internal-XJlf-hOl.js.map
|