reamkit 1.21.0 → 1.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/core/bytes.d.ts +22 -4
- package/dist/esm/core/bytes.js +73 -5
- package/dist/esm/core/converter/ream.d.ts +16 -0
- package/dist/esm/core/converter/ream.js +59 -9
- package/dist/esm/core/document-model/types.d.ts +78 -0
- package/dist/esm/core/drawingml/chart-geometry.js +11 -5
- package/dist/esm/core/drawingml/chart-parser.d.ts +2 -1
- package/dist/esm/core/drawingml/chart-parser.js +23 -1
- package/dist/esm/core/drawingml/colors.d.ts +30 -4
- package/dist/esm/core/drawingml/colors.js +45 -6
- package/dist/esm/core/drawingml/preset-geometry.js +236 -0
- package/dist/esm/core/drawingml/theme-parser.d.ts +38 -0
- package/dist/esm/core/drawingml/theme-parser.js +64 -1
- package/dist/esm/core/fonts/families.d.ts +10 -0
- package/dist/esm/core/fonts/families.js +44 -0
- package/dist/esm/core/fonts/index.d.ts +3 -2
- package/dist/esm/core/fonts/remote-fonts.d.ts +50 -0
- package/dist/esm/core/fonts/remote-fonts.js +114 -8
- package/dist/esm/core/fonts/scripts.d.ts +26 -0
- package/dist/esm/core/fonts/scripts.js +177 -0
- package/dist/esm/core/metafile/blit.d.ts +33 -0
- package/dist/esm/core/metafile/blit.js +67 -0
- package/dist/esm/core/metafile/dib.d.ts +61 -0
- package/dist/esm/core/metafile/dib.js +205 -0
- package/dist/esm/core/metafile/emf.js +139 -24
- package/dist/esm/core/metafile/picture.d.ts +12 -1
- package/dist/esm/core/metafile/symbol-fonts.d.ts +42 -0
- package/dist/esm/core/metafile/symbol-fonts.js +154 -0
- package/dist/esm/core/metafile/wmf.js +183 -10
- package/dist/esm/core/ole/escher-blip.d.ts +12 -0
- package/dist/esm/core/ole/escher-blip.js +88 -0
- package/dist/esm/{pdf-reader → core}/png-encode.js +1 -1
- package/dist/esm/core/vector.d.ts +5 -0
- package/dist/esm/excel/print-model.js +3 -1
- package/dist/esm/excel/sheet-shape-parser.js +1 -12
- package/dist/esm/excel/styles-parser.js +1 -1
- package/dist/esm/excel/xls/biff-reader.js +53 -18
- package/dist/esm/excel/xls/biff-styles.d.ts +5 -2
- package/dist/esm/excel/xls/biff-styles.js +9 -7
- package/dist/esm/excel/xls/escher.js +90 -24
- package/dist/esm/excel/xlsx-reader.d.ts +1 -1
- package/dist/esm/excel/xlsx-reader.js +3 -3
- package/dist/esm/layout/page-doc.d.ts +77 -0
- package/dist/esm/layout/page-doc.js +34 -18
- package/dist/esm/layout/styled-layout.d.ts +2 -2
- package/dist/esm/layout/styled-layout.js +467 -76
- package/dist/esm/pdf/shading.d.ts +19 -0
- package/dist/esm/pdf/shading.js +84 -11
- package/dist/esm/pdf/styled-page-emitter.js +647 -444
- package/dist/esm/pdf-reader/image-decode.js +1 -1
- package/dist/esm/pptx/embedded-fonts.d.ts +31 -0
- package/dist/esm/pptx/embedded-fonts.js +104 -0
- package/dist/esm/pptx/ole-preview.d.ts +10 -0
- package/dist/esm/pptx/ole-preview.js +52 -0
- package/dist/esm/pptx/placeholder-cascade.d.ts +136 -5
- package/dist/esm/pptx/placeholder-cascade.js +260 -27
- package/dist/esm/pptx/ppt/ppt-reader.js +120 -10
- package/dist/esm/pptx/ppt/ppt-text.d.ts +66 -1
- package/dist/esm/pptx/ppt/ppt-text.js +576 -137
- package/dist/esm/pptx/pptx-reader.d.ts +8 -0
- package/dist/esm/pptx/pptx-reader.js +271 -41
- package/dist/esm/pptx/preset-table-styles.d.ts +9 -0
- package/dist/esm/pptx/preset-table-styles.js +27 -0
- package/dist/esm/pptx/slide-parser.d.ts +78 -10
- package/dist/esm/pptx/slide-parser.js +396 -95
- package/dist/esm/pptx/sp-helpers.d.ts +2 -1
- package/dist/esm/pptx/sp-helpers.js +22 -6
- package/dist/esm/pptx/table-style.d.ts +64 -0
- package/dist/esm/pptx/table-style.js +203 -0
- package/dist/esm/svg/svg-writer.js +53 -5
- package/dist/esm/word/document-parser.d.ts +7 -1
- package/dist/esm/word/document-parser.js +6 -6
- package/dist/esm/word/docx-reader.js +8 -5
- package/dist/esm/word/docx-to-pdf.d.ts +8 -7
- package/dist/esm/word/docx-to-pdf.js +25 -7
- package/dist/esm/word/drawing-parser.d.ts +34 -0
- package/dist/esm/word/drawing-parser.js +173 -15
- package/dist/esm/word/numbering-parser.d.ts +2 -1
- package/dist/esm/word/numbering-parser.js +6 -6
- package/dist/esm/word/paragraph-properties.d.ts +2 -1
- package/dist/esm/word/paragraph-properties.js +2 -2
- package/dist/esm/word/run-properties.d.ts +5 -2
- package/dist/esm/word/run-properties.js +13 -8
- package/dist/esm/word/styles-parser.d.ts +2 -1
- package/dist/esm/word/styles-parser.js +12 -12
- package/dist/esm/word/table-parser.js +3 -3
- package/dist/esm/word/theme-fonts.d.ts +10 -0
- package/dist/esm/word/theme-fonts.js +28 -0
- package/package.json +1 -1
- /package/dist/esm/{pdf-reader → core}/png-encode.d.ts +0 -0
|
@@ -1,6 +1,46 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { emuToPt, pt } from "../core/ir/units.js";
|
|
2
|
+
import { fromSymbolFont } from "../core/metafile/symbol-fonts.js";
|
|
3
|
+
import { poAttr, poChildren, poIntAttr, poIs } from "../core/po-helpers.js";
|
|
4
|
+
import { resolveColorNode } from "../core/drawingml/colors.js";
|
|
2
5
|
import { parsePh, parseXfrmBox, rPrToRunProps } from "./sp-helpers.js";
|
|
3
6
|
//#region src/pptx/placeholder-cascade.ts
|
|
7
|
+
/**
|
|
8
|
+
* What an `a:pPr` (or an `a:lvlNpPr`, the same vocabulary) says about its
|
|
9
|
+
* bullet — each part on its own, because a paragraph may restate the SIZE and
|
|
10
|
+
* leave the character to its level.
|
|
11
|
+
*
|
|
12
|
+
* @param pPr The paragraph-properties node.
|
|
13
|
+
* @param colors The colour resolver, for `a:buClr`.
|
|
14
|
+
* @returns What it states, or `undefined` when it says nothing about bullets.
|
|
15
|
+
*/
|
|
16
|
+
function parseBullet(pPr, colors) {
|
|
17
|
+
if (!pPr) return void 0;
|
|
18
|
+
const child = (tag) => poChildren(pPr).find((c) => poIs(c, tag));
|
|
19
|
+
const pct = poIntAttr(child("a:buSzPct"), "val");
|
|
20
|
+
const pts = poIntAttr(child("a:buSzPts"), "val");
|
|
21
|
+
const clr = child("a:buClr");
|
|
22
|
+
const colorHex = clr && colors ? poChildren(clr).map((c) => resolveColorNode(c, colors)).find((hex) => hex !== void 0) : void 0;
|
|
23
|
+
const size = {
|
|
24
|
+
...pct !== void 0 ? { sizePct: pct / 1e5 } : {},
|
|
25
|
+
...pts !== void 0 ? { sizePts: pts / 100 } : {},
|
|
26
|
+
...colorHex !== void 0 ? { colorHex } : {}
|
|
27
|
+
};
|
|
28
|
+
const buChar = child("a:buChar");
|
|
29
|
+
const buAuto = child("a:buAutoNum");
|
|
30
|
+
const kind = poChildren(pPr).some((c) => poIs(c, "a:buNone")) ? { kind: "none" } : buChar ? {
|
|
31
|
+
kind: "char",
|
|
32
|
+
char: fromSymbolFont(poAttr(buChar, "char") ?? "•", poAttr(child("a:buFont"), "typeface"))
|
|
33
|
+
} : buAuto ? {
|
|
34
|
+
kind: "autoNum",
|
|
35
|
+
type: poAttr(buAuto, "type") ?? "arabicPeriod",
|
|
36
|
+
startAt: poIntAttr(buAuto, "startAt") ?? 1
|
|
37
|
+
} : void 0;
|
|
38
|
+
const out = {
|
|
39
|
+
...size,
|
|
40
|
+
...kind
|
|
41
|
+
};
|
|
42
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
43
|
+
}
|
|
4
44
|
function categoryOf(type) {
|
|
5
45
|
if (type === "title" || type === "ctrTitle") return "title";
|
|
6
46
|
if (type === void 0 || type === "body" || type === "subTitle" || type === "obj") return "body";
|
|
@@ -15,27 +55,221 @@ function categoryOf(type) {
|
|
|
15
55
|
* @param layoutTree The parsed `p:sldLayout` part.
|
|
16
56
|
* @param masterTree The parsed `p:sldMaster` part, when present.
|
|
17
57
|
* @param colors The deck's colour resolver, for scheme colours in the text styles.
|
|
58
|
+
* @param deckDefaults The presentation's own default text style, as the floor.
|
|
59
|
+
* @param themeFonts The theme's font scheme, for the `+mn-lt` tokens.
|
|
18
60
|
*/
|
|
19
|
-
function buildPlaceholderCascade(layoutTree, masterTree, colors) {
|
|
20
|
-
const layoutPhs = collectPlaceholders(layoutTree, "p:sldLayout");
|
|
21
|
-
const masterPhs = masterTree ? collectPlaceholders(masterTree, "p:sldMaster") : [];
|
|
22
|
-
const txStyles = masterTree ? collectTxStyles(masterTree, colors) : {
|
|
61
|
+
function buildPlaceholderCascade(layoutTree, masterTree, colors, deckDefaults = [], themeFonts) {
|
|
62
|
+
const layoutPhs = collectPlaceholders(layoutTree, "p:sldLayout", colors, themeFonts);
|
|
63
|
+
const masterPhs = masterTree ? collectPlaceholders(masterTree, "p:sldMaster", colors, themeFonts) : [];
|
|
64
|
+
const txStyles = masterTree ? collectTxStyles(masterTree, colors, themeFonts) : {
|
|
23
65
|
title: [],
|
|
24
66
|
body: [],
|
|
25
67
|
other: []
|
|
26
68
|
};
|
|
69
|
+
const styleAt = (ph, level) => {
|
|
70
|
+
const layers = [at(deckDefaults, level)];
|
|
71
|
+
if (ph) layers.push(at(txStyles[categoryOf(ph.type)], level), at(matchPlaceholder(masterPhs, ph)?.levels ?? [], level), at(matchPlaceholder(layoutPhs, ph)?.levels ?? [], level));
|
|
72
|
+
const merged = layers.reduce(mergeLevels, EMPTY_LEVEL);
|
|
73
|
+
return merged.spacingPct === void 0 ? merged : withResolvedSpacing(merged);
|
|
74
|
+
};
|
|
27
75
|
return {
|
|
28
76
|
geometryFor(ph) {
|
|
29
77
|
return matchPlaceholder(layoutPhs, ph)?.box ?? matchPlaceholder(masterPhs, ph)?.box;
|
|
30
78
|
},
|
|
31
79
|
defaultsFor(ph, level) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
80
|
+
return styleAt(ph, level).run;
|
|
81
|
+
},
|
|
82
|
+
paragraphDefaultsFor(ph, level) {
|
|
83
|
+
return styleAt(ph, level).paragraph;
|
|
84
|
+
},
|
|
85
|
+
bulletFor(ph, level) {
|
|
86
|
+
return styleAt(ph, level).bullet;
|
|
87
|
+
},
|
|
88
|
+
anchorFor(ph) {
|
|
89
|
+
return matchPlaceholder(layoutPhs, ph)?.anchor ?? matchPlaceholder(masterPhs, ph)?.anchor;
|
|
90
|
+
},
|
|
91
|
+
shapePropsFor(ph) {
|
|
92
|
+
return [matchPlaceholder(layoutPhs, ph)?.spPr, matchPlaceholder(masterPhs, ph)?.spPr].filter((n) => n !== void 0);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
var EMPTY_LEVEL = {
|
|
97
|
+
run: {},
|
|
98
|
+
paragraph: {}
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* §21.1.2.2.9/.10 — the space a paragraph states as a FRACTION of a line, in
|
|
102
|
+
* points.
|
|
103
|
+
*
|
|
104
|
+
* A "line" here is the paragraph's own text at its natural height, which is the
|
|
105
|
+
* size times the same 1.2 the layout gives an unstated line. A level that
|
|
106
|
+
* states the fraction and leaves the size to another level is resolved once
|
|
107
|
+
* they have merged, which is why this is not read where the fraction is.
|
|
108
|
+
*
|
|
109
|
+
* @param pct The fraction, `0..n` (20% arrives as 0.2).
|
|
110
|
+
* @param sizePt The paragraph's resolved font size.
|
|
111
|
+
* @returns The distance, or `undefined` when the size is not known.
|
|
112
|
+
*/
|
|
113
|
+
function spacingFromLineFraction(pct, sizePt) {
|
|
114
|
+
if (pct === void 0 || sizePt === void 0) return void 0;
|
|
115
|
+
return pt(pct * sizePt * NATURAL_LINE);
|
|
116
|
+
}
|
|
117
|
+
/** What the layout gives a line of text with nothing said about its height. */
|
|
118
|
+
var NATURAL_LINE = 1.2;
|
|
119
|
+
function withResolvedSpacing(level) {
|
|
120
|
+
const size = level.run.fontSizePt;
|
|
121
|
+
const before = spacingFromLineFraction(level.spacingPct?.before, size);
|
|
122
|
+
const after = spacingFromLineFraction(level.spacingPct?.after, size);
|
|
123
|
+
if (before === void 0 && after === void 0) return level;
|
|
124
|
+
return {
|
|
125
|
+
...level,
|
|
126
|
+
paragraph: {
|
|
127
|
+
...level.paragraph,
|
|
128
|
+
...before !== void 0 ? { spacingBefore: before } : {},
|
|
129
|
+
...after !== void 0 ? { spacingAfter: after } : {}
|
|
35
130
|
}
|
|
36
131
|
};
|
|
37
132
|
}
|
|
38
|
-
|
|
133
|
+
/** The style at `level`, clamped to the list — a deeper level keeps the last. */
|
|
134
|
+
function at(levels, level) {
|
|
135
|
+
if (levels.length === 0) return EMPTY_LEVEL;
|
|
136
|
+
return levels[Math.min(Math.max(level, 0), levels.length - 1)] ?? EMPTY_LEVEL;
|
|
137
|
+
}
|
|
138
|
+
/** `next` over `base` — a layer states only what it changes. */
|
|
139
|
+
function mergeLevels(base, next) {
|
|
140
|
+
const bullet = base.bullet || next.bullet ? {
|
|
141
|
+
...base.bullet,
|
|
142
|
+
...next.bullet
|
|
143
|
+
} : void 0;
|
|
144
|
+
const spacingPct = base.spacingPct || next.spacingPct ? {
|
|
145
|
+
...base.spacingPct,
|
|
146
|
+
...next.spacingPct
|
|
147
|
+
} : void 0;
|
|
148
|
+
return {
|
|
149
|
+
run: {
|
|
150
|
+
...base.run,
|
|
151
|
+
...next.run
|
|
152
|
+
},
|
|
153
|
+
paragraph: {
|
|
154
|
+
...base.paragraph,
|
|
155
|
+
...next.paragraph
|
|
156
|
+
},
|
|
157
|
+
...bullet ? { bullet } : {},
|
|
158
|
+
...spacingPct ? { spacingPct } : {}
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* `a:lstStyle` (or a `p:txStyles` family) → its nine levels. Shared by the
|
|
163
|
+
* deck's default text style, the master's families and a prototype's own list,
|
|
164
|
+
* which are the same vocabulary written in different places.
|
|
165
|
+
*
|
|
166
|
+
* @param list The `a:lstStyle` / `p:titleStyle` / … node.
|
|
167
|
+
* @param colors The deck's colour resolver.
|
|
168
|
+
* @param prefix The level element prefix: `a:lvl` here, always.
|
|
169
|
+
* @returns The nine levels, or an empty list when there is no such node.
|
|
170
|
+
*/
|
|
171
|
+
function parseLevelStyles(list, colors, themeFonts) {
|
|
172
|
+
if (!list) return [];
|
|
173
|
+
const out = [];
|
|
174
|
+
for (let lvl = 1; lvl <= 9; lvl++) {
|
|
175
|
+
const lvlPr = poChildren(list).find((c) => poIs(c, `a:lvl${String(lvl)}pPr`));
|
|
176
|
+
const defRPr = lvlPr ? poChildren(lvlPr).find((c) => poIs(c, "a:defRPr")) : void 0;
|
|
177
|
+
const bullet = parseBullet(lvlPr, colors);
|
|
178
|
+
const spacingPct = spacingFractions(lvlPr);
|
|
179
|
+
out.push({
|
|
180
|
+
run: rPrToRunProps(defRPr, colors, themeFonts),
|
|
181
|
+
paragraph: pPrToParagraphProps(lvlPr),
|
|
182
|
+
...bullet ? { bullet } : {},
|
|
183
|
+
...spacingPct ? { spacingPct } : {}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
return out;
|
|
187
|
+
}
|
|
188
|
+
var ALGN = {
|
|
189
|
+
l: "left",
|
|
190
|
+
ctr: "center",
|
|
191
|
+
r: "right",
|
|
192
|
+
just: "both",
|
|
193
|
+
dist: "distribute"
|
|
194
|
+
};
|
|
195
|
+
/**
|
|
196
|
+
* A level's paragraph shape — §21.1.2.2.7 `@algn`, `@marL`/`@indent`, and the
|
|
197
|
+
* space around and between its lines.
|
|
198
|
+
*
|
|
199
|
+
* Both spacings come in two spellings: `a:spcPts` is a distance (hundredths of
|
|
200
|
+
* a point) and `a:spcPct` a FRACTION of a line (thousandths of a percent). Only
|
|
201
|
+
* the distance was read, so a deck that spaces its bullets the usual way — by
|
|
202
|
+
* fraction — set them solid.
|
|
203
|
+
*/
|
|
204
|
+
function pPrToParagraphProps(lvlPr) {
|
|
205
|
+
if (!lvlPr) return {};
|
|
206
|
+
const algn = poAttr(lvlPr, "algn");
|
|
207
|
+
const marL = poIntAttr(lvlPr, "marL");
|
|
208
|
+
const indent = poIntAttr(lvlPr, "indent");
|
|
209
|
+
return {
|
|
210
|
+
...algn !== void 0 && ALGN[algn] ? { alignment: ALGN[algn] } : {},
|
|
211
|
+
...marL !== void 0 ? { indentLeft: emuToPt(marL) } : {},
|
|
212
|
+
...indent !== void 0 ? { indentFirstLine: emuToPt(indent) } : {},
|
|
213
|
+
...spacingPt(lvlPr, "a:spcBef", "spacingBefore"),
|
|
214
|
+
...spacingPt(lvlPr, "a:spcAft", "spacingAfter"),
|
|
215
|
+
...lineSpacing(lvlPr)
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* §21.1.2.2.5 `a:lnSpc` — the height of each line: a fraction of the natural
|
|
220
|
+
* one (`a:spcPct`) or a distance (`a:spcPts`).
|
|
221
|
+
*
|
|
222
|
+
* The model states a multiple the way §17.3.1.33 does — 12pt under the `auto`
|
|
223
|
+
* rule IS single — so a fraction needs no font size to express, which is why
|
|
224
|
+
* this resolves here and the paragraph SPACING has to wait for the size.
|
|
225
|
+
*
|
|
226
|
+
* @param pPr The paragraph properties (a level's or a paragraph's own).
|
|
227
|
+
* @returns The line spacing, or nothing when the node states none.
|
|
228
|
+
*/
|
|
229
|
+
function lineSpacing(pPr) {
|
|
230
|
+
const holder = pPr ? poChildren(pPr).find((c) => poIs(c, "a:lnSpc")) : void 0;
|
|
231
|
+
if (!holder) return {};
|
|
232
|
+
const pctVal = poIntAttr(poChildren(holder).find((c) => poIs(c, "a:spcPct")), "val");
|
|
233
|
+
if (pctVal !== void 0 && pctVal > 0) return {
|
|
234
|
+
spacingLine: pt(pctVal / 1e5 * 12),
|
|
235
|
+
spacingLineRule: "auto"
|
|
236
|
+
};
|
|
237
|
+
const ptsVal = poIntAttr(poChildren(holder).find((c) => poIs(c, "a:spcPts")), "val");
|
|
238
|
+
if (ptsVal !== void 0 && ptsVal > 0) return {
|
|
239
|
+
spacingLine: pt(ptsVal / 100),
|
|
240
|
+
spacingLineRule: "exact"
|
|
241
|
+
};
|
|
242
|
+
return {};
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* §21.1.2.2.9/.10 — the fraction of a line a paragraph puts before and after
|
|
246
|
+
* itself, when it states one. The distance form is read beside it; this is what
|
|
247
|
+
* the caller must still resolve against the size.
|
|
248
|
+
*
|
|
249
|
+
* @param pPr The paragraph properties.
|
|
250
|
+
* @returns The two fractions (`0..n`), each present only when stated.
|
|
251
|
+
*/
|
|
252
|
+
function spacingFractions(pPr) {
|
|
253
|
+
const frac = (tag) => {
|
|
254
|
+
const holder = pPr ? poChildren(pPr).find((c) => poIs(c, tag)) : void 0;
|
|
255
|
+
const pctNode = holder ? poChildren(holder).find((c) => poIs(c, "a:spcPct")) : void 0;
|
|
256
|
+
const val = pctNode ? poIntAttr(pctNode, "val") : void 0;
|
|
257
|
+
return val === void 0 || val < 0 ? void 0 : val / 1e5;
|
|
258
|
+
};
|
|
259
|
+
const [before, after] = [frac("a:spcBef"), frac("a:spcAft")];
|
|
260
|
+
if (before === void 0 && after === void 0) return void 0;
|
|
261
|
+
return {
|
|
262
|
+
...before !== void 0 ? { before } : {},
|
|
263
|
+
...after !== void 0 ? { after } : {}
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
function spacingPt(lvlPr, tag, key) {
|
|
267
|
+
const holder = poChildren(lvlPr).find((c) => poIs(c, tag));
|
|
268
|
+
const pts = holder ? poChildren(holder).find((c) => poIs(c, "a:spcPts")) : void 0;
|
|
269
|
+
const val = pts ? poIntAttr(pts, "val") : void 0;
|
|
270
|
+
return val === void 0 ? {} : { [key]: pt(val / 100) };
|
|
271
|
+
}
|
|
272
|
+
function collectPlaceholders(tree, root, colors, themeFonts) {
|
|
39
273
|
const sld = tree.find((n) => poIs(n, root));
|
|
40
274
|
const cSld = sld ? poChildren(sld).find((c) => poIs(c, "p:cSld")) : void 0;
|
|
41
275
|
const spTree = cSld ? poChildren(cSld).find((c) => poIs(c, "p:spTree")) : void 0;
|
|
@@ -45,10 +279,19 @@ function collectPlaceholders(tree, root) {
|
|
|
45
279
|
if (!poIs(sp, "p:sp")) continue;
|
|
46
280
|
const ref = parsePh(sp);
|
|
47
281
|
if (!ref) continue;
|
|
48
|
-
const
|
|
282
|
+
const spPr = poChildren(sp).find((c) => poIs(c, "p:spPr"));
|
|
283
|
+
const box = parseXfrmBox(spPr);
|
|
284
|
+
const txBody = poChildren(sp).find((c) => poIs(c, "p:txBody"));
|
|
285
|
+
const lstStyle = txBody ? poChildren(txBody).find((c) => poIs(c, "a:lstStyle")) : void 0;
|
|
286
|
+
const bodyPr = txBody ? poChildren(txBody).find((c) => poIs(c, "a:bodyPr")) : void 0;
|
|
287
|
+
const a = bodyPr ? poAttr(bodyPr, "anchor") : void 0;
|
|
288
|
+
const anchor = a === "ctr" || a === "b" || a === "t" ? a : void 0;
|
|
49
289
|
out.push({
|
|
50
290
|
ref,
|
|
51
|
-
...box ? { box } : {}
|
|
291
|
+
...box ? { box } : {},
|
|
292
|
+
...spPr ? { spPr } : {},
|
|
293
|
+
levels: parseLevelStyles(lstStyle, colors, themeFonts),
|
|
294
|
+
...anchor ? { anchor } : {}
|
|
52
295
|
});
|
|
53
296
|
}
|
|
54
297
|
return out;
|
|
@@ -63,25 +306,15 @@ function matchPlaceholder(list, ph) {
|
|
|
63
306
|
const cat = categoryOf(ph.type);
|
|
64
307
|
return list.find((p) => categoryOf(p.ref.type) === cat);
|
|
65
308
|
}
|
|
66
|
-
function collectTxStyles(masterTree, colors) {
|
|
309
|
+
function collectTxStyles(masterTree, colors, themeFonts) {
|
|
67
310
|
const sld = masterTree.find((n) => poIs(n, "p:sldMaster"));
|
|
68
311
|
const txStyles = sld ? poChildren(sld).find((c) => poIs(c, "p:txStyles")) : void 0;
|
|
312
|
+
const family = (tag) => parseLevelStyles(txStyles ? poChildren(txStyles).find((c) => poIs(c, tag)) : void 0, colors, themeFonts);
|
|
69
313
|
return {
|
|
70
|
-
title:
|
|
71
|
-
body:
|
|
72
|
-
other:
|
|
314
|
+
title: family("p:titleStyle"),
|
|
315
|
+
body: family("p:bodyStyle"),
|
|
316
|
+
other: family("p:otherStyle")
|
|
73
317
|
};
|
|
74
318
|
}
|
|
75
|
-
function levelStyles(txStyles, tag, colors) {
|
|
76
|
-
const style = txStyles ? poChildren(txStyles).find((c) => poIs(c, tag)) : void 0;
|
|
77
|
-
if (!style) return [];
|
|
78
|
-
const out = [];
|
|
79
|
-
for (let lvl = 1; lvl <= 9; lvl++) {
|
|
80
|
-
const lvlPr = poChildren(style).find((c) => poIs(c, `a:lvl${lvl}pPr`));
|
|
81
|
-
const defRPr = lvlPr ? poChildren(lvlPr).find((c) => poIs(c, "a:defRPr")) : void 0;
|
|
82
|
-
out.push(rPrToRunProps(defRPr, colors));
|
|
83
|
-
}
|
|
84
|
-
return out;
|
|
85
|
-
}
|
|
86
319
|
//#endregion
|
|
87
|
-
export { buildPlaceholderCascade };
|
|
320
|
+
export { buildPlaceholderCascade, lineSpacing, parseBullet, parseLevelStyles, spacingFractions, spacingFromLineFraction };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { pt } from "../../core/ir/units.js";
|
|
2
2
|
import { ResourceStore } from "../../core/ir/resources.js";
|
|
3
3
|
import { FEATURES } from "../../core/ir/features.js";
|
|
4
|
+
import { isEmf } from "../../core/metafile/emf.js";
|
|
5
|
+
import { isWmf } from "../../core/metafile/wmf.js";
|
|
4
6
|
import { EMPTY_STYLE_SHEET, resolveBodyStyles } from "../../core/style-cascade/resolver.js";
|
|
5
7
|
import "../../core/style-cascade/index.js";
|
|
6
8
|
import { isCfb, openCfb } from "../../core/ole/cfb.js";
|
|
@@ -48,7 +50,7 @@ function readPpt(bytes) {
|
|
|
48
50
|
return {
|
|
49
51
|
doc: {
|
|
50
52
|
kind: "flow",
|
|
51
|
-
body: resolveBodyStyles(buildBody(content, resources), EMPTY_STYLE_SHEET),
|
|
53
|
+
body: resolveBodyStyles(buildBody(content, resources, width, height), EMPTY_STYLE_SHEET),
|
|
52
54
|
sections: [],
|
|
53
55
|
section: {
|
|
54
56
|
pageSize: {
|
|
@@ -70,19 +72,23 @@ function readPpt(bytes) {
|
|
|
70
72
|
losses: [content.encrypted ? PPT_ENCRYPTED_LOSS : PPT_TEXT_LOSS]
|
|
71
73
|
};
|
|
72
74
|
}
|
|
73
|
-
function buildBody(content, resources) {
|
|
75
|
+
function buildBody(content, resources, pageW, pageH) {
|
|
74
76
|
const body = [];
|
|
75
77
|
content.slides.forEach((slide, slideIndex) => {
|
|
76
78
|
const inFlowParas = [];
|
|
77
79
|
const inFlowImages = [];
|
|
78
80
|
const floats = [];
|
|
81
|
+
if (slide.background) {
|
|
82
|
+
const backdrop = slideBackdrop(slide.background, resources, pageW, pageH);
|
|
83
|
+
if (backdrop) floats.push(backdrop);
|
|
84
|
+
}
|
|
79
85
|
for (const shape of slide.shapes) if (shape.rectPt) {
|
|
80
|
-
if (shape.
|
|
86
|
+
if (shape.autoShape) floats.push(positionedAutoShape(shape.rectPt, shape.autoShape, resources));
|
|
81
87
|
if (shape.image) {
|
|
82
88
|
const block = positionedImage(shape.rectPt, shape.image, resources);
|
|
83
89
|
if (block) floats.push(block);
|
|
84
90
|
}
|
|
85
|
-
if (shape.
|
|
91
|
+
if (shape.paragraphs?.some((p) => paragraphText(p).length > 0)) floats.push(positionedTextShape(shape.rectPt, shape.paragraphs, shape.wordArt === true));
|
|
86
92
|
} else {
|
|
87
93
|
for (const para of shape.paragraphs ?? []) if (paragraphText(para).length > 0) inFlowParas.push(flowParagraph(para));
|
|
88
94
|
if (shape.image) {
|
|
@@ -122,10 +128,80 @@ function flowParagraph(para) {
|
|
|
122
128
|
kind: "paragraph",
|
|
123
129
|
paragraph: {
|
|
124
130
|
properties: toParaProperties(para, false),
|
|
125
|
-
runs: para
|
|
131
|
+
runs: bulletedRuns(para)
|
|
126
132
|
}
|
|
127
133
|
};
|
|
128
134
|
}
|
|
135
|
+
function bulletedRuns(para) {
|
|
136
|
+
const runs = para.runs.map(toRun);
|
|
137
|
+
if (para.bullet === void 0) return runs;
|
|
138
|
+
const { bold: _b, italic: _i, ...rest } = runs[0]?.properties ?? {};
|
|
139
|
+
return [{
|
|
140
|
+
text: `${para.bullet} `,
|
|
141
|
+
properties: rest
|
|
142
|
+
}, ...runs];
|
|
143
|
+
}
|
|
144
|
+
function slideBackdrop(bg, resources, pageW, pageH) {
|
|
145
|
+
const fill = backdropFill(bg, resources);
|
|
146
|
+
if (!fill) return void 0;
|
|
147
|
+
return {
|
|
148
|
+
kind: "shape",
|
|
149
|
+
shape: {
|
|
150
|
+
float: {
|
|
151
|
+
wrap: "none",
|
|
152
|
+
behind: true,
|
|
153
|
+
posH: {
|
|
154
|
+
relativeFrom: "page",
|
|
155
|
+
offsetPt: pt(0)
|
|
156
|
+
},
|
|
157
|
+
posV: {
|
|
158
|
+
relativeFrom: "page",
|
|
159
|
+
offsetPt: pt(0)
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
width: pageW,
|
|
163
|
+
height: pageH,
|
|
164
|
+
geometry: {
|
|
165
|
+
kind: "preset",
|
|
166
|
+
preset: "rect",
|
|
167
|
+
adjust: /* @__PURE__ */ new Map()
|
|
168
|
+
},
|
|
169
|
+
fill,
|
|
170
|
+
paragraphProperties: {}
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
function backdropFill(bg, resources) {
|
|
175
|
+
if (bg.image) return {
|
|
176
|
+
kind: "picture",
|
|
177
|
+
imageResource: resources.put(bg.image.bytes),
|
|
178
|
+
...bg.imageTiled ? { tiled: true } : {}
|
|
179
|
+
};
|
|
180
|
+
if (bg.gradient) {
|
|
181
|
+
const stops = [{
|
|
182
|
+
offset: 0,
|
|
183
|
+
colorHex: bg.gradient.fromHex
|
|
184
|
+
}, {
|
|
185
|
+
offset: 1,
|
|
186
|
+
colorHex: bg.gradient.toHex
|
|
187
|
+
}];
|
|
188
|
+
return {
|
|
189
|
+
kind: "gradient",
|
|
190
|
+
gradient: bg.gradient.radial ? {
|
|
191
|
+
kind: "radial",
|
|
192
|
+
stops
|
|
193
|
+
} : {
|
|
194
|
+
kind: "linear",
|
|
195
|
+
angle: bg.gradient.angleDeg,
|
|
196
|
+
stops
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
return bg.fillColorHex ? {
|
|
201
|
+
kind: "solid",
|
|
202
|
+
colorHex: bg.fillColorHex
|
|
203
|
+
} : void 0;
|
|
204
|
+
}
|
|
129
205
|
function floatAt(rect) {
|
|
130
206
|
return {
|
|
131
207
|
wrap: "none",
|
|
@@ -139,7 +215,7 @@ function floatAt(rect) {
|
|
|
139
215
|
}
|
|
140
216
|
};
|
|
141
217
|
}
|
|
142
|
-
function positionedTextShape(rect, paragraphs) {
|
|
218
|
+
function positionedTextShape(rect, paragraphs, centred = false) {
|
|
143
219
|
return {
|
|
144
220
|
kind: "shape",
|
|
145
221
|
shape: {
|
|
@@ -154,14 +230,14 @@ function positionedTextShape(rect, paragraphs) {
|
|
|
154
230
|
fill: { kind: "none" },
|
|
155
231
|
text: {
|
|
156
232
|
content: paragraphs.filter((p) => paragraphText(p).length > 0).map(flowParagraph),
|
|
157
|
-
anchor: "t"
|
|
233
|
+
anchor: centred ? "ctr" : "t"
|
|
158
234
|
},
|
|
159
235
|
paragraphProperties: {}
|
|
160
236
|
}
|
|
161
237
|
};
|
|
162
238
|
}
|
|
163
239
|
function positionedImage(rect, image, resources) {
|
|
164
|
-
if (!imagePixelSize(image.bytes)) return void 0;
|
|
240
|
+
if (!isMetafile(image.bytes) && !imagePixelSize(image.bytes)) return void 0;
|
|
165
241
|
return {
|
|
166
242
|
kind: "image",
|
|
167
243
|
image: {
|
|
@@ -214,9 +290,36 @@ function toCustomGeometry(g) {
|
|
|
214
290
|
commands
|
|
215
291
|
};
|
|
216
292
|
}
|
|
217
|
-
function positionedAutoShape(rect, auto) {
|
|
293
|
+
function positionedAutoShape(rect, auto, resources) {
|
|
218
294
|
const line = isLineShape(auto.shapeType);
|
|
219
|
-
const fill = auto.
|
|
295
|
+
const fill = line ? { kind: "none" } : auto.image ? {
|
|
296
|
+
kind: "picture",
|
|
297
|
+
imageResource: resources.put(auto.image.bytes),
|
|
298
|
+
...auto.imageTiled ? { tiled: true } : {},
|
|
299
|
+
...auto.tileSizePt ? { tileSizePt: auto.tileSizePt } : {}
|
|
300
|
+
} : auto.gradient ? {
|
|
301
|
+
kind: "gradient",
|
|
302
|
+
gradient: auto.gradient.radial ? {
|
|
303
|
+
kind: "radial",
|
|
304
|
+
stops: [{
|
|
305
|
+
offset: 0,
|
|
306
|
+
colorHex: auto.gradient.fromHex
|
|
307
|
+
}, {
|
|
308
|
+
offset: 1,
|
|
309
|
+
colorHex: auto.gradient.toHex
|
|
310
|
+
}]
|
|
311
|
+
} : {
|
|
312
|
+
kind: "linear",
|
|
313
|
+
angle: auto.gradient.angleDeg,
|
|
314
|
+
stops: [{
|
|
315
|
+
offset: 0,
|
|
316
|
+
colorHex: auto.gradient.fromHex
|
|
317
|
+
}, {
|
|
318
|
+
offset: 1,
|
|
319
|
+
colorHex: auto.gradient.toHex
|
|
320
|
+
}]
|
|
321
|
+
}
|
|
322
|
+
} : auto.fillColorHex ? {
|
|
220
323
|
kind: "solid",
|
|
221
324
|
colorHex: auto.fillColorHex
|
|
222
325
|
} : { kind: "none" };
|
|
@@ -257,6 +360,9 @@ function inFlowImage(image, resources) {
|
|
|
257
360
|
}
|
|
258
361
|
};
|
|
259
362
|
}
|
|
363
|
+
function isMetafile(d) {
|
|
364
|
+
return isEmf(d) || isWmf(d);
|
|
365
|
+
}
|
|
260
366
|
function imagePixelSize(d) {
|
|
261
367
|
if (d.length >= 24 && d[0] === 137 && d[1] === 80 && d[2] === 78 && d[3] === 71) {
|
|
262
368
|
const w = u32be(d, 16);
|
|
@@ -298,6 +404,10 @@ function toRun(r) {
|
|
|
298
404
|
...r.italic ? { italic: true } : {},
|
|
299
405
|
...underline ? { underline } : {},
|
|
300
406
|
...r.sizePt ? { fontSizePt: pt(r.sizePt) } : {},
|
|
407
|
+
...r.fontFamily ? { fontFamily: {
|
|
408
|
+
ascii: r.fontFamily,
|
|
409
|
+
hAnsi: r.fontFamily
|
|
410
|
+
} } : {},
|
|
301
411
|
...r.colorHex ? { colorHex: r.colorHex } : {}
|
|
302
412
|
};
|
|
303
413
|
return {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/** A run of uniformly-formatted text within a paragraph (PPT-2). */
|
|
2
2
|
export interface PptRun {
|
|
3
3
|
readonly text: string;
|
|
4
|
+
/** The typeface the run names, resolved through the deck's font collection. */
|
|
5
|
+
readonly fontFamily?: string;
|
|
4
6
|
readonly bold?: boolean;
|
|
5
7
|
readonly italic?: boolean;
|
|
6
8
|
readonly underline?: boolean;
|
|
@@ -20,6 +22,12 @@ export interface PptParagraph {
|
|
|
20
22
|
readonly align?: number;
|
|
21
23
|
/** The 0-based outline/indent level. */
|
|
22
24
|
readonly level?: number;
|
|
25
|
+
/**
|
|
26
|
+
* §2.9.20 — the bullet character this paragraph is marked with, already
|
|
27
|
+
* translated out of whatever symbol font stated it. Absent when the paragraph
|
|
28
|
+
* carries none.
|
|
29
|
+
*/
|
|
30
|
+
readonly bullet?: string;
|
|
23
31
|
}
|
|
24
32
|
/**
|
|
25
33
|
* An embedded picture referenced by a slide shape — the raw image bytes pulled
|
|
@@ -80,6 +88,35 @@ export interface PptAutoShape {
|
|
|
80
88
|
readonly fillColorHex?: string;
|
|
81
89
|
/** The resolved line colour as 6-hex RGB, when present. */
|
|
82
90
|
readonly lineColorHex?: string;
|
|
91
|
+
/**
|
|
92
|
+
* MS-ODRAW §2.3.7.1 — the two-colour sweep a `fillType` of 4..8 asks for.
|
|
93
|
+
* `fillColor` is one end and `fillBackColor` the other; `fillAngle` is where
|
|
94
|
+
* it runs. Read as its fillColor alone, 41246-2's title slide came out a flat
|
|
95
|
+
* teal where every reader fades it to white.
|
|
96
|
+
*/
|
|
97
|
+
readonly gradient?: {
|
|
98
|
+
readonly fromHex: string;
|
|
99
|
+
readonly toHex: string;
|
|
100
|
+
readonly angleDeg: number;
|
|
101
|
+
readonly radial: boolean;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* §2.3.7.1 MSOFILLTYPE 3 — a picture stretched over the shape, its blip named
|
|
105
|
+
* by `fillBlip`. A `.ppt` table is a group of cell rectangles, and a cell with
|
|
106
|
+
* a picture background states it exactly this way.
|
|
107
|
+
*/
|
|
108
|
+
readonly image?: PptImage;
|
|
109
|
+
/** §2.3.7.1 MSOFILLTYPE 2 — the picture REPEATS at its own size, not stretched. */
|
|
110
|
+
readonly imageTiled?: boolean;
|
|
111
|
+
/**
|
|
112
|
+
* §2.3.7.11/.12 `fillWidth` / `fillHeight` — the size one copy occupies, in
|
|
113
|
+
* points, when the shape states it rather than leaving the tile at the
|
|
114
|
+
* picture's own size.
|
|
115
|
+
*/
|
|
116
|
+
readonly tileSizePt?: {
|
|
117
|
+
readonly widthPt: number;
|
|
118
|
+
readonly heightPt: number;
|
|
119
|
+
};
|
|
83
120
|
readonly geometry?: PptCustomGeometry;
|
|
84
121
|
}
|
|
85
122
|
/**
|
|
@@ -89,13 +126,41 @@ export interface PptAutoShape {
|
|
|
89
126
|
*/
|
|
90
127
|
export interface PptShape {
|
|
91
128
|
readonly rectPt?: PptRect;
|
|
129
|
+
/**
|
|
130
|
+
* §2.3.22 — the shape IS a piece of WordArt: its text is the shape, centred
|
|
131
|
+
* in its box rather than flowed from the top. Drawn as an autoshape it was a
|
|
132
|
+
* coloured rectangle where the reference has a word.
|
|
133
|
+
*/
|
|
134
|
+
readonly wordArt?: boolean;
|
|
135
|
+
/**
|
|
136
|
+
* Whether the shape is a placeholder (§2.9.24). On a MASTER that means it is
|
|
137
|
+
* a prototype, not decoration: drawn on the slides that follow it, every one
|
|
138
|
+
* of them would carry "Click to edit Master title style".
|
|
139
|
+
*/
|
|
140
|
+
readonly placeholder?: boolean;
|
|
92
141
|
readonly paragraphs?: ReadonlyArray<PptParagraph>;
|
|
93
142
|
readonly image?: PptImage;
|
|
94
143
|
readonly autoShape?: PptAutoShape;
|
|
95
144
|
}
|
|
96
|
-
/**
|
|
145
|
+
/**
|
|
146
|
+
* A slide's background (PPT-12) — the fill on the shape MS-ODRAW marks with
|
|
147
|
+
* `fBackground`. A `.ppt` states it as a shape like any other; drawn as one it
|
|
148
|
+
* would be a rectangle among the content, and dropped it leaves a black slide
|
|
149
|
+
* white.
|
|
150
|
+
*/
|
|
151
|
+
export interface PptBackground {
|
|
152
|
+
/** A solid fill's colour, as 6-hex RGB. */
|
|
153
|
+
readonly fillColorHex?: string;
|
|
154
|
+
/** A shaded fill, in the same terms an autoshape's is (PPT-9). */
|
|
155
|
+
readonly gradient?: PptAutoShape['gradient'];
|
|
156
|
+
/** A picture fill, stretched over the slide (or tiled across it). */
|
|
157
|
+
readonly image?: PptImage;
|
|
158
|
+
readonly imageTiled?: boolean;
|
|
159
|
+
}
|
|
160
|
+
/** One slide: its shapes in document order, over its background. */
|
|
97
161
|
export interface PptSlide {
|
|
98
162
|
readonly shapes: ReadonlyArray<PptShape>;
|
|
163
|
+
readonly background?: PptBackground;
|
|
99
164
|
}
|
|
100
165
|
/** The extracted content of a `.ppt`: its slides, deck page size and encryption flag. */
|
|
101
166
|
export interface PptContent {
|