reamkit 1.22.0 → 1.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/core/bmp.d.ts +33 -0
- package/dist/esm/core/bmp.js +276 -0
- package/dist/esm/core/converter/ream.d.ts +16 -0
- package/dist/esm/core/converter/ream.js +59 -9
- package/dist/esm/core/document-model/types.d.ts +55 -0
- package/dist/esm/core/drawingml/chart-geometry.js +14 -7
- package/dist/esm/core/drawingml/colors.d.ts +4 -2
- package/dist/esm/core/drawingml/colors.js +15 -0
- package/dist/esm/core/drawingml/diagram/colors.d.ts +52 -0
- package/dist/esm/core/drawingml/diagram/colors.js +125 -0
- package/dist/esm/core/drawingml/diagram/data-model.d.ts +47 -0
- package/dist/esm/core/drawingml/diagram/data-model.js +119 -0
- package/dist/esm/core/drawingml/diagram/layout-engine.d.ts +46 -0
- package/dist/esm/core/drawingml/diagram/layout-engine.js +948 -0
- package/dist/esm/core/drawingml/diagram/run.d.ts +37 -0
- package/dist/esm/core/drawingml/diagram/run.js +50 -0
- package/dist/esm/core/drawingml/diagram/to-drawing.d.ts +16 -0
- package/dist/esm/core/drawingml/diagram/to-drawing.js +86 -0
- package/dist/esm/core/drawingml/preset-geometry.js +116 -6
- package/dist/esm/core/drawingml/text-warp.d.ts +58 -0
- package/dist/esm/core/drawingml/text-warp.js +355 -0
- package/dist/esm/core/drawingml/theme-parser.d.ts +38 -0
- package/dist/esm/core/drawingml/theme-parser.js +73 -4
- package/dist/esm/core/font/measure.d.ts +12 -0
- package/dist/esm/core/font/measure.js +36 -0
- 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/images.d.ts +30 -2
- package/dist/esm/core/images.js +121 -5
- 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 +280 -38
- package/dist/esm/core/metafile/picture.d.ts +61 -1
- package/dist/esm/core/metafile/picture.js +70 -1
- package/dist/esm/core/metafile/wmf.js +202 -13
- package/dist/esm/core/ole/escher-blip.d.ts +12 -0
- package/dist/esm/core/ole/escher-blip.js +98 -0
- package/dist/esm/{pdf-reader → core}/png-encode.js +1 -1
- package/dist/esm/excel/print-model.js +5 -3
- package/dist/esm/excel/sheet-drawing.js +1 -1
- package/dist/esm/excel/sheet-to-flow.js +16 -2
- 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/html/html-writer.js +2 -1
- package/dist/esm/layout/page-doc.d.ts +74 -0
- package/dist/esm/layout/page-doc.js +14 -1
- package/dist/esm/layout/styled-layout.d.ts +2 -2
- package/dist/esm/layout/styled-layout.js +475 -48
- package/dist/esm/pdf/shading.js +8 -6
- package/dist/esm/pdf/styled-page-emitter.js +77 -5
- package/dist/esm/pdf/vector-graphics.js +1 -1
- 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/placeholder-cascade.d.ts +73 -6
- package/dist/esm/pptx/placeholder-cascade.js +114 -18
- package/dist/esm/pptx/ppt/ppt-reader.js +191 -13
- package/dist/esm/pptx/ppt/ppt-text.d.ts +107 -1
- package/dist/esm/pptx/ppt/ppt-text.js +873 -144
- package/dist/esm/pptx/pptx-reader.d.ts +8 -0
- package/dist/esm/pptx/pptx-reader.js +117 -15
- 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 +11 -2
- package/dist/esm/pptx/slide-parser.js +203 -44
- package/dist/esm/pptx/sp-helpers.d.ts +9 -4
- package/dist/esm/pptx/sp-helpers.js +48 -9
- package/dist/esm/pptx/table-style.d.ts +16 -1
- package/dist/esm/pptx/table-style.js +104 -10
- package/dist/esm/svg/svg-writer.js +1 -0
- package/dist/esm/word/document-parser.d.ts +11 -2
- package/dist/esm/word/document-parser.js +10 -7
- package/dist/esm/word/docx-reader.js +32 -16
- 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/docx-writer.js +4 -0
- package/dist/esm/word/drawing-parser.d.ts +20 -0
- package/dist/esm/word/drawing-parser.js +67 -22
- 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 +6 -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,4 +1,8 @@
|
|
|
1
|
+
import { decodeBmp, isBmp } from "../../core/bmp.js";
|
|
2
|
+
import { encodePng } from "../../core/png-encode.js";
|
|
3
|
+
import { fromSymbolFont } from "../../core/metafile/symbol-fonts.js";
|
|
1
4
|
import { isCfb, openCfb } from "../../core/ole/cfb.js";
|
|
5
|
+
import { readEscherBlip } from "../../core/ole/escher-blip.js";
|
|
2
6
|
//#region src/pptx/ppt/ppt-text.ts
|
|
3
7
|
var RT_DOCUMENT = 1e3;
|
|
4
8
|
var RT_DOCUMENT_ATOM = 1001;
|
|
@@ -12,8 +16,13 @@ var RT_TEXT_HEADER_ATOM = 3999;
|
|
|
12
16
|
var RT_TEXT_CHARS_ATOM = 4e3;
|
|
13
17
|
var RT_STYLE_TEXT_PROP_ATOM = 4001;
|
|
14
18
|
var RT_TEXT_BYTES_ATOM = 4008;
|
|
19
|
+
var RT_OUTLINE_TEXT_REF_ATOM = 3998;
|
|
20
|
+
var RT_TEXT_MASTER_STYLE_ATOM = 4003;
|
|
15
21
|
var RT_SLIDE_ATOM = 1007;
|
|
16
22
|
var RT_MAIN_MASTER = 1016;
|
|
23
|
+
var RT_ENVIRONMENT = 1010;
|
|
24
|
+
var RT_FONT_COLLECTION = 2005;
|
|
25
|
+
var RT_FONT_ENTITY_ATOM = 4023;
|
|
17
26
|
var RT_COLOR_SCHEME_ATOM = 2032;
|
|
18
27
|
var FBT_BSTORE_CONTAINER = 61441;
|
|
19
28
|
var FBT_BSE = 61447;
|
|
@@ -22,9 +31,48 @@ var FBT_FSP = 61450;
|
|
|
22
31
|
var FBT_OPT = 61451;
|
|
23
32
|
var FBT_CLIENT_TEXTBOX = 61453;
|
|
24
33
|
var FBT_CLIENT_ANCHOR = 61456;
|
|
34
|
+
var FBT_CLIENT_DATA = 61457;
|
|
35
|
+
var FBT_SPGR_CONTAINER = 61443;
|
|
36
|
+
var FBT_SPGR = 61449;
|
|
37
|
+
var FBT_CHILD_ANCHOR = 61455;
|
|
38
|
+
var RT_PLACEHOLDER_ATOM = 3011;
|
|
39
|
+
var RT_HEADERS_FOOTERS = 4057;
|
|
40
|
+
var RT_HEADERS_FOOTERS_ATOM = 4058;
|
|
41
|
+
var RT_CSTRING = 4026;
|
|
42
|
+
var HF_INSTANCE_PER_SLIDE = 0;
|
|
43
|
+
var HF_INSTANCE_SLIDES = 3;
|
|
44
|
+
var HF_HAS_DATE = 1;
|
|
45
|
+
var HF_HAS_USER_DATE = 4;
|
|
46
|
+
var HF_HAS_SLIDE_NUMBER = 8;
|
|
47
|
+
var HF_HAS_FOOTER = 32;
|
|
48
|
+
var PLACEHOLDER_DATE = 7;
|
|
49
|
+
var PLACEHOLDER_SLIDE_NUMBER = 8;
|
|
50
|
+
var PLACEHOLDER_FOOTER = 9;
|
|
51
|
+
var SCHEME_TEXT_AND_LINES = 1;
|
|
25
52
|
var PROP_PIB = 260;
|
|
53
|
+
var PROP_CROP_TOP = 256;
|
|
54
|
+
var PROP_CROP_BOTTOM = 257;
|
|
55
|
+
var PROP_CROP_LEFT = 258;
|
|
56
|
+
var PROP_CROP_RIGHT = 259;
|
|
57
|
+
var PROP_PICTURE_TRANSPARENT = 263;
|
|
58
|
+
var PICTURE_TRANSPARENT_NONE = 4294967295;
|
|
59
|
+
var PROP_FILL_TYPE = 384;
|
|
26
60
|
var PROP_FILL_COLOR = 385;
|
|
61
|
+
var PROP_FILL_BACK_COLOR = 387;
|
|
62
|
+
var PROP_FILL_ANGLE = 395;
|
|
63
|
+
var PROP_FILL_FOCUS = 396;
|
|
64
|
+
var PROP_FILL_BLIP = 390;
|
|
65
|
+
var PROP_GTEXT_UNICODE = 192;
|
|
66
|
+
var PROP_GTEXT_SIZE = 195;
|
|
67
|
+
var PROP_GTEXT_FONT = 197;
|
|
68
|
+
var PROP_FILL_WIDTH = 393;
|
|
69
|
+
var PROP_FILL_HEIGHT = 394;
|
|
70
|
+
var EMU_PER_POINT = 12700;
|
|
27
71
|
var PROP_LINE_COLOR = 448;
|
|
72
|
+
var PROP_FILL_BOOLS = 447;
|
|
73
|
+
var PROP_LINE_BOOLS = 511;
|
|
74
|
+
var FILL_F_FILLED = 16;
|
|
75
|
+
var LINE_F_LINE = 8;
|
|
28
76
|
var PROP_GEO_LEFT = 320;
|
|
29
77
|
var PROP_GEO_TOP = 321;
|
|
30
78
|
var PROP_GEO_RIGHT = 322;
|
|
@@ -37,8 +85,10 @@ var COLORREF_SCHEME_INDEX = 8;
|
|
|
37
85
|
var COLORREF_SYS_INDEX = 16;
|
|
38
86
|
var FBSE_FODELAY_OFFSET = 28;
|
|
39
87
|
var FSP_FLAG_PATRIARCH = 4;
|
|
88
|
+
var FSP_FLAG_BACKGROUND = 1024;
|
|
40
89
|
var TOKEN_ENCRYPTED = 4090610911;
|
|
41
90
|
var SLWT_INSTANCE_SLIDES = 0;
|
|
91
|
+
var SLWT_INSTANCE_MASTERS = 1;
|
|
42
92
|
var MAX_EDITS = 4096;
|
|
43
93
|
var MAX_DEPTH = 24;
|
|
44
94
|
function recordAt(stream, off) {
|
|
@@ -127,19 +177,19 @@ function extractPptContent(bytes) {
|
|
|
127
177
|
};
|
|
128
178
|
}
|
|
129
179
|
function slideHasContent(s) {
|
|
130
|
-
return s.shapes.some((sh) => shapeHasText(sh) || sh.image !== void 0 || sh.autoShape !== void 0);
|
|
180
|
+
return s.background !== void 0 || s.shapes.some((sh) => shapeHasText(sh) || sh.image !== void 0 || sh.autoShape !== void 0);
|
|
131
181
|
}
|
|
132
182
|
function shapeHasText(sh) {
|
|
133
183
|
return sh.paragraphs?.some((p) => paragraphText(p).length > 0) ?? false;
|
|
134
184
|
}
|
|
135
|
-
function slideShapes(slideData, img, outline,
|
|
136
|
-
const
|
|
137
|
-
if (!shapes.some(shapeHasText)) {
|
|
138
|
-
const inline = collectParagraphs(slideData, 0);
|
|
139
|
-
const text = inline.some((p) => paragraphText(p).length > 0) ? inline : outline;
|
|
140
|
-
if (text.some((p) => paragraphText(p).length > 0)) shapes.push({ paragraphs: text });
|
|
185
|
+
function slideShapes(slideData, img, outline, env, defaults) {
|
|
186
|
+
const content = collectShapes(slideData, img, env, outline, defaults);
|
|
187
|
+
if (!content.shapes.some(shapeHasText)) {
|
|
188
|
+
const inline = collectParagraphs(slideData, 0, defaults, env);
|
|
189
|
+
const text = inline.some((p) => paragraphText(p).length > 0) ? inline : outline.flat();
|
|
190
|
+
if (text.some((p) => paragraphText(p).length > 0)) content.shapes.push({ paragraphs: text });
|
|
141
191
|
}
|
|
142
|
-
return
|
|
192
|
+
return content;
|
|
143
193
|
}
|
|
144
194
|
function buildPersistDirectory(stream, currentEditOffset) {
|
|
145
195
|
const persist = /* @__PURE__ */ new Map();
|
|
@@ -178,42 +228,80 @@ function mergePersistDirectory(stream, offset, persist) {
|
|
|
178
228
|
}
|
|
179
229
|
}
|
|
180
230
|
function readSlideList(stream, docData, persist, img) {
|
|
231
|
+
const fonts = parseFontCollection(docData);
|
|
181
232
|
const slwt = findChild(docData, (r) => r.type === RT_SLIDE_LIST_WITH_TEXT && r.instance === SLWT_INSTANCE_SLIDES);
|
|
182
233
|
if (!slwt) return [];
|
|
183
234
|
const slides = [];
|
|
184
235
|
let outline = [];
|
|
185
236
|
let pendingRef;
|
|
237
|
+
const masters = {
|
|
238
|
+
persist,
|
|
239
|
+
stream,
|
|
240
|
+
byMasterId: buildMasterIdMap(docData),
|
|
241
|
+
cache: /* @__PURE__ */ new Map(),
|
|
242
|
+
documentStyles: parseDocumentTextStyles(docData, fonts)
|
|
243
|
+
};
|
|
244
|
+
const deckFurniture = headersFooters(docData, HF_INSTANCE_SLIDES);
|
|
186
245
|
const flush = () => {
|
|
187
246
|
if (pendingRef === void 0) return;
|
|
188
247
|
const slideOff = persist.get(pendingRef);
|
|
189
248
|
const slideRec = slideOff !== void 0 ? recordAt(stream, slideOff) : void 0;
|
|
190
|
-
const
|
|
191
|
-
|
|
249
|
+
const isSlide = slideRec !== void 0 && slideRec.type === RT_SLIDE;
|
|
250
|
+
const scheme = isSlide ? resolveScheme(slideRec.data, masters) : void 0;
|
|
251
|
+
const env = {
|
|
252
|
+
...scheme ? { scheme } : {},
|
|
253
|
+
...fonts.length > 0 ? { fonts } : {}
|
|
254
|
+
};
|
|
255
|
+
const defaults = isSlide ? masterChain(slideRec.data, masters, scheme, fonts) : void 0;
|
|
256
|
+
const texts = outline.map((e) => styledParagraphs(e.rec, e.style, levelStyles(defaults, e.textType), env));
|
|
257
|
+
const flat = texts.flat();
|
|
258
|
+
const content = isSlide ? slideShapes(slideRec.data, img, texts, env, defaults) : { shapes: flat.some((p) => paragraphText(p).length > 0) ? [{ paragraphs: flat }] : [] };
|
|
259
|
+
const background = isSlide ? masterBackground(slideRec.data, masters, img) ?? content.background : void 0;
|
|
260
|
+
const own = isSlide ? headersFooters(slideRec.data, HF_INSTANCE_PER_SLIDE) : void 0;
|
|
261
|
+
const furniture = own ?? deckFurniture ? {
|
|
262
|
+
...own ?? deckFurniture,
|
|
263
|
+
number: slides.length + 1
|
|
264
|
+
} : void 0;
|
|
265
|
+
const decoration = isSlide ? masterShapes(slideRec.data, masters, img, furniture, defaults) : [];
|
|
266
|
+
slides.push({
|
|
267
|
+
shapes: [...decoration, ...content.shapes],
|
|
268
|
+
...background ? { background } : {}
|
|
269
|
+
});
|
|
192
270
|
outline = [];
|
|
193
271
|
pendingRef = void 0;
|
|
194
272
|
};
|
|
195
273
|
const recs = [...records(slwt)];
|
|
274
|
+
let textType = TEXT_TYPE_OTHER;
|
|
196
275
|
for (let i = 0; i < recs.length; i++) {
|
|
197
276
|
const rec = recs[i];
|
|
198
277
|
if (rec.type === RT_SLIDE_PERSIST_ATOM) {
|
|
199
278
|
flush();
|
|
200
279
|
pendingRef = rec.data.length >= 4 ? u32(rec.data, 0) : void 0;
|
|
201
|
-
} else if (rec.type ===
|
|
280
|
+
} else if (rec.type === RT_TEXT_HEADER_ATOM) textType = rec.data.length >= 4 ? u32(rec.data, 0) : TEXT_TYPE_OTHER;
|
|
281
|
+
else if (rec.type === RT_TEXT_CHARS_ATOM || rec.type === RT_TEXT_BYTES_ATOM) outline.push({
|
|
282
|
+
textType,
|
|
283
|
+
rec,
|
|
284
|
+
style: findFollowingStyle(recs, i)
|
|
285
|
+
});
|
|
202
286
|
}
|
|
203
287
|
flush();
|
|
204
288
|
return slides;
|
|
205
289
|
}
|
|
206
|
-
function collectParagraphs(d, depth) {
|
|
290
|
+
function collectParagraphs(d, depth, defaults, env) {
|
|
207
291
|
if (depth > MAX_DEPTH) return [];
|
|
208
292
|
const out = [];
|
|
209
293
|
const recs = [...records(d)];
|
|
294
|
+
let textType = TEXT_TYPE_OTHER;
|
|
210
295
|
for (let i = 0; i < recs.length; i++) {
|
|
211
296
|
const rec = recs[i];
|
|
212
|
-
if (rec.type ===
|
|
213
|
-
else if (rec.
|
|
297
|
+
if (rec.type === RT_TEXT_HEADER_ATOM) textType = rec.data.length >= 4 ? u32(rec.data, 0) : TEXT_TYPE_OTHER;
|
|
298
|
+
else if (rec.type === RT_TEXT_CHARS_ATOM || rec.type === RT_TEXT_BYTES_ATOM) out.push(...styledParagraphs(rec, findFollowingStyle(recs, i), levelStyles(defaults, textType), env));
|
|
299
|
+
else if (rec.isContainer) out.push(...collectParagraphs(rec.data, depth + 1, defaults, env));
|
|
214
300
|
}
|
|
215
301
|
return out;
|
|
216
302
|
}
|
|
303
|
+
var TEXT_TYPE_OTHER = 4;
|
|
304
|
+
var levelStyles = (defaults, textType) => defaults ? (level) => defaults(textType, level) : void 0;
|
|
217
305
|
function findFollowingStyle(recs, i) {
|
|
218
306
|
for (let j = i + 1; j < recs.length; j++) {
|
|
219
307
|
const t = recs[j].type;
|
|
@@ -221,8 +309,8 @@ function findFollowingStyle(recs, i) {
|
|
|
221
309
|
if (t === RT_TEXT_CHARS_ATOM || t === RT_TEXT_BYTES_ATOM || t === RT_TEXT_HEADER_ATOM) break;
|
|
222
310
|
}
|
|
223
311
|
}
|
|
224
|
-
function styledParagraphs(textRec, styleData) {
|
|
225
|
-
return buildStyledParagraphs(textRec.type === RT_TEXT_CHARS_ATOM ? decodeUtf16(textRec.data) : decodeCp1252(textRec.data), styleData);
|
|
312
|
+
function styledParagraphs(textRec, styleData, defaults, env) {
|
|
313
|
+
return buildStyledParagraphs(textRec.type === RT_TEXT_CHARS_ATOM ? decodeUtf16(textRec.data) : decodeCp1252(textRec.data), styleData, defaults, env);
|
|
226
314
|
}
|
|
227
315
|
function slidesByScan(stream, img) {
|
|
228
316
|
const slides = [];
|
|
@@ -231,8 +319,14 @@ function slidesByScan(stream, img) {
|
|
|
231
319
|
}
|
|
232
320
|
function collectSlides(d, out, img, depth) {
|
|
233
321
|
if (depth > MAX_DEPTH) return;
|
|
234
|
-
for (const rec of records(d)) if (rec.type === RT_SLIDE)
|
|
235
|
-
|
|
322
|
+
for (const rec of records(d)) if (rec.type === RT_SLIDE) {
|
|
323
|
+
const scheme = resolveScheme(rec.data);
|
|
324
|
+
const content = slideShapes(rec.data, img, [], scheme ? { scheme } : {});
|
|
325
|
+
out.push({
|
|
326
|
+
shapes: content.shapes,
|
|
327
|
+
...content.background ? { background: content.background } : {}
|
|
328
|
+
});
|
|
329
|
+
} else if (rec.isContainer) collectSlides(rec.data, out, img, depth + 1);
|
|
236
330
|
}
|
|
237
331
|
function findChild(d, pred) {
|
|
238
332
|
for (const rec of records(d)) if (pred(rec)) return rec.data;
|
|
@@ -288,53 +382,500 @@ function findDescendantContainer(d, type, depth) {
|
|
|
288
382
|
}
|
|
289
383
|
}
|
|
290
384
|
}
|
|
291
|
-
function collectShapes(slideData, img,
|
|
292
|
-
const out = [];
|
|
293
|
-
collectShapeContainers(slideData, img, out, 0,
|
|
385
|
+
function collectShapes(slideData, img, env, outline = [], defaults) {
|
|
386
|
+
const out = { shapes: [] };
|
|
387
|
+
collectShapeContainers(slideData, img, out, 0, env, outline, defaults);
|
|
294
388
|
return out;
|
|
295
389
|
}
|
|
296
|
-
function collectShapeContainers(d, img, out, depth,
|
|
390
|
+
function collectShapeContainers(d, img, out, depth, env, outline, defaults, group) {
|
|
391
|
+
const scheme = env.scheme;
|
|
297
392
|
if (depth > MAX_DEPTH) return;
|
|
298
|
-
for (const r of records(d))
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
let shapeType = 0;
|
|
303
|
-
let fspFlags = 0;
|
|
304
|
-
let fillColorHex;
|
|
305
|
-
let lineColorHex;
|
|
306
|
-
let geometry;
|
|
307
|
-
for (const child of records(r.data)) if (child.type === FBT_FSP) {
|
|
308
|
-
shapeType = child.instance;
|
|
309
|
-
fspFlags = child.data.length >= 8 ? u32(child.data, 4) : 0;
|
|
310
|
-
} else if (child.type === FBT_CLIENT_ANCHOR) rectPt = parseAnchor(child.data);
|
|
311
|
-
else if (child.type === FBT_CLIENT_TEXTBOX) paragraphs = collectParagraphs(child.data, 0);
|
|
312
|
-
else if (child.type === FBT_OPT) {
|
|
313
|
-
pib = optProperty(child.data, child.instance, PROP_PIB);
|
|
314
|
-
fillColorHex = optColor(child.data, child.instance, PROP_FILL_COLOR, scheme);
|
|
315
|
-
lineColorHex = optColor(child.data, child.instance, PROP_LINE_COLOR, scheme);
|
|
316
|
-
geometry = parseFreeformGeometry(child.data, child.instance);
|
|
393
|
+
for (const r of records(d)) {
|
|
394
|
+
if (r.type === FBT_SPGR_CONTAINER) {
|
|
395
|
+
collectShapeContainers(r.data, img, out, depth + 1, env, outline, defaults, groupFrame(r.data, group) ?? group);
|
|
396
|
+
continue;
|
|
317
397
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
398
|
+
if (r.type === FBT_SP_CONTAINER) {
|
|
399
|
+
let rectPt;
|
|
400
|
+
let paragraphs;
|
|
401
|
+
let pib;
|
|
402
|
+
let shapeType = 0;
|
|
403
|
+
let fspFlags = 0;
|
|
404
|
+
let fillColorHex;
|
|
405
|
+
let backColorHex;
|
|
406
|
+
let lineColorHex;
|
|
407
|
+
let gradient;
|
|
408
|
+
let geometry;
|
|
409
|
+
let fillType;
|
|
410
|
+
let fillBlip;
|
|
411
|
+
let inlineFill;
|
|
412
|
+
let inlinePic;
|
|
413
|
+
let tileSizePt;
|
|
414
|
+
let gtext;
|
|
415
|
+
let placeholder = false;
|
|
416
|
+
let placeholderId;
|
|
417
|
+
let pictureCrop;
|
|
418
|
+
let pictureTransparentHex;
|
|
419
|
+
for (const child of records(r.data)) if (child.type === FBT_FSP) {
|
|
420
|
+
shapeType = child.instance;
|
|
421
|
+
fspFlags = child.data.length >= 8 ? u32(child.data, 4) : 0;
|
|
422
|
+
} else if (child.type === FBT_CLIENT_ANCHOR) rectPt = parseAnchor(child.data);
|
|
423
|
+
else if (child.type === FBT_CHILD_ANCHOR && group) rectPt = childRect(child.data, group);
|
|
424
|
+
else if (child.type === FBT_CLIENT_DATA) {
|
|
425
|
+
const ph = findChild(child.data, (c) => c.type === RT_PLACEHOLDER_ATOM);
|
|
426
|
+
placeholder = ph !== void 0;
|
|
427
|
+
placeholderId = ph !== void 0 && ph.length > 4 ? ph[4] : void 0;
|
|
428
|
+
} else if (child.type === FBT_CLIENT_TEXTBOX) paragraphs = clientTextboxParagraphs(child.data, outline, defaults, env);
|
|
429
|
+
else if (child.type === FBT_OPT) {
|
|
430
|
+
pib = optProperty(child.data, child.instance, PROP_PIB);
|
|
431
|
+
fillColorHex = stated(child.data, child.instance, PROP_FILL_BOOLS, FILL_F_FILLED) ? optColor(child.data, child.instance, PROP_FILL_COLOR, scheme) : void 0;
|
|
432
|
+
lineColorHex = stated(child.data, child.instance, PROP_LINE_BOOLS, LINE_F_LINE) ? optColor(child.data, child.instance, PROP_LINE_COLOR, scheme) : void 0;
|
|
433
|
+
gradient = parseFillGradient(child.data, child.instance, fillColorHex, scheme);
|
|
434
|
+
geometry = parseFreeformGeometry(child.data, child.instance);
|
|
435
|
+
fillType = optProperty(child.data, child.instance, PROP_FILL_TYPE);
|
|
436
|
+
fillBlip = optProperty(child.data, child.instance, PROP_FILL_BLIP);
|
|
437
|
+
backColorHex = optColor(child.data, child.instance, PROP_FILL_BACK_COLOR, scheme);
|
|
438
|
+
tileSizePt = parseTileSize(child.data, child.instance);
|
|
439
|
+
gtext = parseWordArt(child.data, child.instance, fillColorHex);
|
|
440
|
+
inlineFill = optComplex(child.data, child.instance, PROP_FILL_BLIP);
|
|
441
|
+
inlinePic = optComplex(child.data, child.instance, PROP_PIB);
|
|
442
|
+
pictureCrop = parsePictureCrop(child.data, child.instance);
|
|
443
|
+
const transparent = optProperty(child.data, child.instance, PROP_PICTURE_TRANSPARENT);
|
|
444
|
+
pictureTransparentHex = transparent !== void 0 && transparent !== PICTURE_TRANSPARENT_NONE ? optColor(child.data, child.instance, PROP_PICTURE_TRANSPARENT, scheme) : void 0;
|
|
445
|
+
}
|
|
446
|
+
const blip = (index) => {
|
|
447
|
+
if (index === void 0 || index < 1 || index > img.foDelays.length) return void 0;
|
|
448
|
+
const bytes = readBlipBytes(img.pictures, img.foDelays[index - 1]);
|
|
449
|
+
return bytes ? { bytes } : void 0;
|
|
450
|
+
};
|
|
451
|
+
const pattern = fillType === 1 ? patternFill(blip(fillBlip) ?? inlineBlip(inlineFill), fillColorHex, backColorHex) : void 0;
|
|
452
|
+
const tiled = fillType === 2 || pattern !== void 0;
|
|
453
|
+
const fillImage = pattern ?? (fillType === 2 || fillType === 3 ? blip(fillBlip) ?? inlineBlip(inlineFill) : void 0);
|
|
454
|
+
if (pattern) tileSizePt = PATTERN_TILE_PT;
|
|
455
|
+
if ((fspFlags & FSP_FLAG_BACKGROUND) !== 0) {
|
|
456
|
+
const bg = slideBackground(fillColorHex, gradient, fillType, fillImage, tiled);
|
|
457
|
+
if (bg && !out.background) out.background = bg;
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
const picture = blip(pib) ?? inlineBlip(inlinePic);
|
|
461
|
+
const image = picture ? {
|
|
462
|
+
...picture,
|
|
463
|
+
...pictureCrop ? { crop: pictureCrop } : {},
|
|
464
|
+
...pictureTransparentHex !== void 0 ? { transparentHex: pictureTransparentHex } : {}
|
|
465
|
+
} : void 0;
|
|
466
|
+
const textParas = gtext ?? (paragraphs?.some((p) => paragraphText(p).length > 0) === true ? paragraphs : void 0) ? gtext ? [gtext] : paragraphs : void 0;
|
|
467
|
+
const decorative = (fspFlags & (FSP_FLAG_PATRIARCH | 1025)) === 0;
|
|
468
|
+
const autoShape = !gtext && !image && rectPt && (shapeType > 0 || geometry) && decorative && (fillColorHex || lineColorHex || fillImage) ? {
|
|
469
|
+
shapeType,
|
|
470
|
+
...fillColorHex ? { fillColorHex } : {},
|
|
471
|
+
...lineColorHex ? { lineColorHex } : {},
|
|
472
|
+
...gradient ? { gradient } : {},
|
|
473
|
+
...fillImage ? { image: fillImage } : {},
|
|
474
|
+
...fillImage && tiled ? { imageTiled: true } : {},
|
|
475
|
+
...fillImage && tiled && tileSizePt ? { tileSizePt } : {},
|
|
476
|
+
...geometry ? { geometry } : {}
|
|
477
|
+
} : void 0;
|
|
478
|
+
if (textParas || image || autoShape || placeholderId === PLACEHOLDER_DATE || placeholderId === PLACEHOLDER_SLIDE_NUMBER || placeholderId === PLACEHOLDER_FOOTER) out.shapes.push({
|
|
479
|
+
...rectPt ? { rectPt } : {},
|
|
480
|
+
...gtext ? { wordArt: true } : {},
|
|
481
|
+
...placeholder ? { placeholder: true } : {},
|
|
482
|
+
...placeholderId !== void 0 ? { placeholderId } : {},
|
|
483
|
+
...textParas ? { paragraphs: textParas } : {},
|
|
484
|
+
...image ? { image } : {},
|
|
485
|
+
...autoShape ? { autoShape } : {}
|
|
486
|
+
});
|
|
487
|
+
} else if (r.isContainer) collectShapeContainers(r.data, img, out, depth + 1, env, outline, defaults, group);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
function parseFontCollection(docData) {
|
|
491
|
+
const out = [];
|
|
492
|
+
const coll = findDescendantContainer(docData, RT_FONT_COLLECTION, 0);
|
|
493
|
+
if (!coll) return out;
|
|
494
|
+
let ordinal = 0;
|
|
495
|
+
for (const rec of records(coll)) {
|
|
496
|
+
if (rec.type !== RT_FONT_ENTITY_ATOM || rec.data.length < 2) continue;
|
|
497
|
+
const index = rec.instance > 0 || ordinal === 0 ? rec.instance : ordinal;
|
|
498
|
+
let name = "";
|
|
499
|
+
for (let i = 0; i < 32 && i * 2 + 1 < rec.data.length; i++) {
|
|
500
|
+
const cu = u16(rec.data, i * 2);
|
|
501
|
+
if (cu === 0) break;
|
|
502
|
+
name += String.fromCharCode(cu);
|
|
322
503
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
504
|
+
out[index] = name;
|
|
505
|
+
ordinal++;
|
|
506
|
+
}
|
|
507
|
+
return out;
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* §2.9.3 — the DECK's own text styles, from the document's text info.
|
|
511
|
+
*
|
|
512
|
+
* A master states the styles its placeholders take; the style a TEXT BOX drawn
|
|
513
|
+
* on a slide takes — `Tx_TYPE_OTHER` — is stated once for the whole document
|
|
514
|
+
* and lives here instead. 38256.ppt's four captions are all of that kind, and
|
|
515
|
+
* without it they were set in the outline's 32pt Arial rather than the sizes
|
|
516
|
+
* and faces they were typed in.
|
|
517
|
+
*
|
|
518
|
+
* @param docData The DocumentContainer's body.
|
|
519
|
+
* @param fonts The deck's typefaces, by the index a run names.
|
|
520
|
+
* @returns The level styles, by text type — in practice the one for `other`.
|
|
521
|
+
*/
|
|
522
|
+
function parseDocumentTextStyles(docData, fonts) {
|
|
523
|
+
const env = findDescendantContainer(docData, RT_ENVIRONMENT, 0);
|
|
524
|
+
return env ? parseMasterTextStyles(env, fonts) : /* @__PURE__ */ new Map();
|
|
525
|
+
}
|
|
526
|
+
function parseMasterTextStyles(masterData, fonts) {
|
|
527
|
+
const out = /* @__PURE__ */ new Map();
|
|
528
|
+
for (const rec of records(masterData)) {
|
|
529
|
+
if (rec.type !== RT_TEXT_MASTER_STYLE_ATOM || rec.data.length < 2) continue;
|
|
530
|
+
const levels = Math.min(u16(rec.data, 0), 5);
|
|
531
|
+
const first = readMasterLevels(rec.data, levels, rec.instance >= 4 ? 2 : 0, fonts);
|
|
532
|
+
const exact = (r) => r.styles.length === levels && r.off === rec.data.length;
|
|
533
|
+
let best = first;
|
|
534
|
+
if (!exact(first)) {
|
|
535
|
+
const other = readMasterLevels(rec.data, levels, rec.instance >= 4 ? 0 : 2, fonts);
|
|
536
|
+
if (exact(other)) best = other;
|
|
537
|
+
}
|
|
538
|
+
if (best.styles.length > 0) out.set(rec.instance, best.styles);
|
|
539
|
+
}
|
|
540
|
+
return out;
|
|
541
|
+
}
|
|
542
|
+
function readMasterLevels(d, levels, prefix, fonts) {
|
|
543
|
+
const styles = [];
|
|
544
|
+
let off = 2;
|
|
545
|
+
for (let i = 0; i < levels; i++) {
|
|
546
|
+
off += prefix;
|
|
547
|
+
if (off + 4 > d.length) break;
|
|
548
|
+
const pf = readPfException(d, off);
|
|
549
|
+
if (pf.off + 4 > d.length) break;
|
|
550
|
+
const cf = readCfException(d, pf.off, void 0, fonts);
|
|
551
|
+
if (cf.off > d.length) break;
|
|
552
|
+
off = cf.off;
|
|
553
|
+
styles.push({
|
|
554
|
+
...cf.props,
|
|
555
|
+
...pf.align !== void 0 ? { align: pf.align } : {},
|
|
556
|
+
...pf.bullet !== void 0 ? { bullet: pf.bullet } : {},
|
|
557
|
+
...pf.bulletOn !== void 0 ? { bulletOn: pf.bulletOn } : {},
|
|
558
|
+
...pf.leftMarginPt !== void 0 ? { leftMarginPt: pf.leftMarginPt } : {},
|
|
559
|
+
...pf.indentPt !== void 0 ? { indentPt: pf.indentPt } : {},
|
|
560
|
+
...pf.lineSpacing !== void 0 ? { lineSpacing: pf.lineSpacing } : {},
|
|
561
|
+
...pf.spaceBeforePt !== void 0 ? { spaceBeforePt: pf.spaceBeforePt } : {},
|
|
562
|
+
...pf.spaceAfterPt !== void 0 ? { spaceAfterPt: pf.spaceAfterPt } : {}
|
|
336
563
|
});
|
|
337
|
-
}
|
|
564
|
+
}
|
|
565
|
+
return {
|
|
566
|
+
styles,
|
|
567
|
+
off
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
var TEXT_TYPE_FALLBACK = new Map([
|
|
571
|
+
[5, 1],
|
|
572
|
+
[6, 0],
|
|
573
|
+
[7, 1],
|
|
574
|
+
[8, 1]
|
|
575
|
+
]);
|
|
576
|
+
function masterDefaults(chain, scheme) {
|
|
577
|
+
return (textType, level) => {
|
|
578
|
+
const types = [textType, TEXT_TYPE_FALLBACK.get(textType)];
|
|
579
|
+
let merged;
|
|
580
|
+
for (const styles of chain) for (const t of types) {
|
|
581
|
+
const levels = t === void 0 ? void 0 : styles.get(t);
|
|
582
|
+
const at = levels?.[Math.min(level, levels.length - 1)];
|
|
583
|
+
if (!at) continue;
|
|
584
|
+
const { colorHex: _c, colorIndex: _i, bullet: _b, bulletOn: _o, spaceBeforePt: _sb, spaceAfterPt: _sa, lineSpacing: _ls, leftMarginPt: _lm, indentPt: _ip, ...metrics } = at;
|
|
585
|
+
merged = {
|
|
586
|
+
...t === textType ? at : metrics,
|
|
587
|
+
...merged
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
if (!merged || merged.colorHex !== void 0 || merged.colorIndex === void 0) return merged;
|
|
591
|
+
const slot = scheme?.[merged.colorIndex];
|
|
592
|
+
return slot === void 0 ? merged : {
|
|
593
|
+
...merged,
|
|
594
|
+
colorHex: slot
|
|
595
|
+
};
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
function buildMasterIdMap(docData) {
|
|
599
|
+
const out = /* @__PURE__ */ new Map();
|
|
600
|
+
const slwt = findChild(docData, (r) => r.type === RT_SLIDE_LIST_WITH_TEXT && r.instance === SLWT_INSTANCE_MASTERS);
|
|
601
|
+
if (!slwt) return out;
|
|
602
|
+
for (const rec of records(slwt)) if (rec.type === RT_SLIDE_PERSIST_ATOM && rec.data.length >= 16) out.set(u32(rec.data, 12), u32(rec.data, 0));
|
|
603
|
+
return out;
|
|
604
|
+
}
|
|
605
|
+
var SLIDE_FLAG_MASTER_OBJECTS = 1;
|
|
606
|
+
var SLIDE_FLAG_MASTER_BACKGROUND = 4;
|
|
607
|
+
function masterOf(slideData, ctx) {
|
|
608
|
+
const slideAtom = findChild(slideData, (r) => r.type === RT_SLIDE_ATOM);
|
|
609
|
+
if (!slideAtom || slideAtom.length < 22) return void 0;
|
|
610
|
+
const id = u32(slideAtom, 12);
|
|
611
|
+
const off = ctx.persist.get(ctx.byMasterId.get(id) ?? id);
|
|
612
|
+
const rec = off !== void 0 ? recordAt(ctx.stream, off) : void 0;
|
|
613
|
+
return rec && (rec.type === RT_MAIN_MASTER || rec.type === RT_SLIDE) ? rec : void 0;
|
|
614
|
+
}
|
|
615
|
+
function slideFlag(slideData, flag) {
|
|
616
|
+
const slideAtom = findChild(slideData, (r) => r.type === RT_SLIDE_ATOM);
|
|
617
|
+
return slideAtom !== void 0 && slideAtom.length >= 22 && (u16(slideAtom, 20) & flag) !== 0;
|
|
618
|
+
}
|
|
619
|
+
function masterShapes(slideData, ctx, img, furniture, defaults) {
|
|
620
|
+
if (!slideFlag(slideData, SLIDE_FLAG_MASTER_OBJECTS)) return [];
|
|
621
|
+
const master = masterOf(slideData, ctx);
|
|
622
|
+
if (!master) return [];
|
|
623
|
+
const scheme = resolveScheme(master.data, ctx);
|
|
624
|
+
return collectShapes(master.data, img, scheme ? { scheme } : {}, [], defaults).shapes.filter((s) => s.rectPt !== void 0).flatMap((s) => {
|
|
625
|
+
if (s.placeholder !== true) return [s];
|
|
626
|
+
const text = furniture ? furnitureText(s.placeholderId, furniture) : void 0;
|
|
627
|
+
return text === void 0 ? [] : [{
|
|
628
|
+
...s,
|
|
629
|
+
paragraphs: [furnitureParagraph(s, text, scheme?.[SCHEME_TEXT_AND_LINES])]
|
|
630
|
+
}];
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* §2.4.15/§2.4.16 — the string one furniture placeholder shows, or `undefined`
|
|
635
|
+
* when this deck does not show that one at all.
|
|
636
|
+
*
|
|
637
|
+
* A date box is only drawn when the deck states the date ITSELF: `fHasTodayDate`
|
|
638
|
+
* asks for the day the file is opened, and a converter that answered it would
|
|
639
|
+
* put a different date in the file every time it ran.
|
|
640
|
+
*/
|
|
641
|
+
function furnitureText(id, hf) {
|
|
642
|
+
if (id === PLACEHOLDER_FOOTER) return (hf.mask & HF_HAS_FOOTER) !== 0 ? (hf.footer ?? "") || void 0 : void 0;
|
|
643
|
+
if (id === PLACEHOLDER_SLIDE_NUMBER) return (hf.mask & HF_HAS_SLIDE_NUMBER) !== 0 ? String(hf.number) : void 0;
|
|
644
|
+
if (id === PLACEHOLDER_DATE) return (hf.mask & HF_HAS_DATE) !== 0 && (hf.mask & HF_HAS_USER_DATE) !== 0 ? (hf.userDate ?? "") || void 0 : void 0;
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* The furniture's words, in the formatting the master's own box carries.
|
|
648
|
+
*
|
|
649
|
+
* A `.ppt` master keeps no prompt text in these boxes, so there is usually no
|
|
650
|
+
* run to copy from and the paragraph takes the master's defaults; where one IS
|
|
651
|
+
* there, its size, colour and alignment are what PowerPoint draws the footer in.
|
|
652
|
+
*/
|
|
653
|
+
function furnitureParagraph(shape, text, textColorHex) {
|
|
654
|
+
const model = shape.paragraphs?.find((p) => p.runs.length > 0);
|
|
655
|
+
const first = model?.runs[0];
|
|
656
|
+
return {
|
|
657
|
+
...model ? { ...model } : {},
|
|
658
|
+
runs: [{
|
|
659
|
+
...textColorHex !== void 0 ? { colorHex: textColorHex } : {},
|
|
660
|
+
...first ?? {},
|
|
661
|
+
text
|
|
662
|
+
}]
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* §2.4.15 `HeadersFootersContainer` — the strings a deck shows on every slide,
|
|
667
|
+
* and which of them it shows.
|
|
668
|
+
*
|
|
669
|
+
* They live nowhere near the boxes that draw them: the geometry is a
|
|
670
|
+
* placeholder on the MASTER and the words are here, so a reader that has one
|
|
671
|
+
* without the other draws an empty band. 37625.ppt's every slide is signed
|
|
672
|
+
* "Transport CDM Workshop" and dated "26 August 2004" this way.
|
|
673
|
+
*
|
|
674
|
+
* @param data A DocumentContainer's or SlideContainer's records.
|
|
675
|
+
* @param instance Which container to read — the deck's or the slide's own.
|
|
676
|
+
* @returns The mask and the strings, or `undefined` when there is no container.
|
|
677
|
+
*/
|
|
678
|
+
function headersFooters(data, instance) {
|
|
679
|
+
for (const rec of records(data)) {
|
|
680
|
+
if (rec.type !== RT_HEADERS_FOOTERS || rec.instance !== instance) continue;
|
|
681
|
+
let mask = 0;
|
|
682
|
+
let userDate;
|
|
683
|
+
let footer;
|
|
684
|
+
for (const child of records(rec.data)) if (child.type === RT_HEADERS_FOOTERS_ATOM && child.data.length >= 4) mask = child.data[2] | child.data[3] << 8;
|
|
685
|
+
else if (child.type === RT_CSTRING) {
|
|
686
|
+
const s = utf16Property(child.data);
|
|
687
|
+
if (child.instance === 0) userDate = s;
|
|
688
|
+
else if (child.instance === 2) footer = s;
|
|
689
|
+
}
|
|
690
|
+
return {
|
|
691
|
+
mask,
|
|
692
|
+
...userDate !== void 0 ? { userDate } : {},
|
|
693
|
+
...footer !== void 0 ? { footer } : {}
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
function masterBackground(slideData, ctx, img) {
|
|
698
|
+
if (!slideFlag(slideData, SLIDE_FLAG_MASTER_BACKGROUND)) return void 0;
|
|
699
|
+
const master = masterOf(slideData, ctx);
|
|
700
|
+
if (!master) return void 0;
|
|
701
|
+
const scheme = resolveScheme(master.data, ctx);
|
|
702
|
+
return collectShapes(master.data, img, scheme ? { scheme } : {}).background;
|
|
703
|
+
}
|
|
704
|
+
function masterChain(slideData, ctx, scheme, fonts) {
|
|
705
|
+
const chain = [];
|
|
706
|
+
if (!ctx) return masterDefaults(chain, scheme);
|
|
707
|
+
const behind = ctx.documentStyles;
|
|
708
|
+
let data = slideData;
|
|
709
|
+
const seen = /* @__PURE__ */ new Set();
|
|
710
|
+
for (let hop = 0; hop < 4 && data; hop++) {
|
|
711
|
+
const slideAtom = findChild(data, (r) => r.type === RT_SLIDE_ATOM);
|
|
712
|
+
if (!slideAtom || slideAtom.length < 16) break;
|
|
713
|
+
const masterId = u32(slideAtom, 12);
|
|
714
|
+
if (seen.has(masterId)) break;
|
|
715
|
+
seen.add(masterId);
|
|
716
|
+
const cached = ctx.cache.get(masterId);
|
|
717
|
+
const off = ctx.persist.get(ctx.byMasterId.get(masterId) ?? masterId);
|
|
718
|
+
const rec = off !== void 0 ? recordAt(ctx.stream, off) : void 0;
|
|
719
|
+
if (!rec || rec.type !== RT_MAIN_MASTER && rec.type !== RT_SLIDE) break;
|
|
720
|
+
if (cached) chain.push(cached);
|
|
721
|
+
else {
|
|
722
|
+
const styles = parseMasterTextStyles(rec.data, fonts);
|
|
723
|
+
ctx.cache.set(masterId, styles);
|
|
724
|
+
chain.push(styles);
|
|
725
|
+
}
|
|
726
|
+
data = rec.type === RT_SLIDE ? rec.data : void 0;
|
|
727
|
+
}
|
|
728
|
+
if (behind.size > 0) chain.push(behind);
|
|
729
|
+
return masterDefaults(chain, scheme);
|
|
730
|
+
}
|
|
731
|
+
function groupFrame(spgrData, parent) {
|
|
732
|
+
const sp = [...records(spgrData)].find((r) => r.type === FBT_SP_CONTAINER);
|
|
733
|
+
if (!sp) return void 0;
|
|
734
|
+
let box;
|
|
735
|
+
let rect;
|
|
736
|
+
for (const child of records(sp.data)) if (child.type === FBT_SPGR && child.data.length >= 16) {
|
|
737
|
+
const x = i32(child.data, 0);
|
|
738
|
+
const y = i32(child.data, 4);
|
|
739
|
+
box = {
|
|
740
|
+
x,
|
|
741
|
+
y,
|
|
742
|
+
w: i32(child.data, 8) - x,
|
|
743
|
+
h: i32(child.data, 12) - y
|
|
744
|
+
};
|
|
745
|
+
} else if (child.type === FBT_CLIENT_ANCHOR) rect = parseAnchor(child.data);
|
|
746
|
+
else if (child.type === FBT_CHILD_ANCHOR && parent !== void 0 && rect === void 0) rect = childRect(child.data, parent);
|
|
747
|
+
if (!box || !rect || box.w <= 0 || box.h <= 0) return void 0;
|
|
748
|
+
return {
|
|
749
|
+
rect,
|
|
750
|
+
...box
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
function childRect(d, g) {
|
|
754
|
+
if (d.length < 16) return void 0;
|
|
755
|
+
const x1 = g.rect.x + (i32(d, 0) - g.x) / g.w * g.rect.w;
|
|
756
|
+
const y1 = g.rect.y + (i32(d, 4) - g.y) / g.h * g.rect.h;
|
|
757
|
+
const x2 = g.rect.x + (i32(d, 8) - g.x) / g.w * g.rect.w;
|
|
758
|
+
const y2 = g.rect.y + (i32(d, 12) - g.y) / g.h * g.rect.h;
|
|
759
|
+
const w = x2 - x1;
|
|
760
|
+
const h = y2 - y1;
|
|
761
|
+
return (w > 0 || h > 0) && w >= 0 && h >= 0 && Math.abs(x1) < 5e3 && Math.abs(y1) < 5e3 ? {
|
|
762
|
+
x: x1,
|
|
763
|
+
y: y1,
|
|
764
|
+
w,
|
|
765
|
+
h
|
|
766
|
+
} : void 0;
|
|
767
|
+
}
|
|
768
|
+
function clientTextboxParagraphs(d, outline, defaults, env) {
|
|
769
|
+
const own = collectParagraphs(d, 0, defaults, env);
|
|
770
|
+
if (own.some((p) => paragraphText(p).length > 0)) return own;
|
|
771
|
+
const ref = findChild(d, (r) => r.type === RT_OUTLINE_TEXT_REF_ATOM);
|
|
772
|
+
if (!ref || ref.length < 4) return own;
|
|
773
|
+
return [...outline[u32(ref, 0)] ?? []];
|
|
774
|
+
}
|
|
775
|
+
function parseWordArt(d, count, colorHex) {
|
|
776
|
+
const text = utf16Property(optComplex(d, count, PROP_GTEXT_UNICODE));
|
|
777
|
+
if (text === void 0 || text.trim() === "") return void 0;
|
|
778
|
+
const raw = optProperty(d, count, PROP_GTEXT_SIZE);
|
|
779
|
+
const sizePt = raw === void 0 ? void 0 : Math.round(raw / 65536 * 10) / 10;
|
|
780
|
+
const font = utf16Property(optComplex(d, count, PROP_GTEXT_FONT));
|
|
781
|
+
return {
|
|
782
|
+
runs: [{
|
|
783
|
+
text,
|
|
784
|
+
...sizePt !== void 0 && sizePt > 0 ? { sizePt } : {},
|
|
785
|
+
...colorHex ? { colorHex } : {},
|
|
786
|
+
...font ? { fontFamily: font } : {}
|
|
787
|
+
}],
|
|
788
|
+
align: 1
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
function utf16Property(blob) {
|
|
792
|
+
if (!blob || blob.length < 2) return void 0;
|
|
793
|
+
let out = "";
|
|
794
|
+
for (let i = 0; i * 2 + 1 < blob.length; i++) {
|
|
795
|
+
const cu = u16(blob, i * 2);
|
|
796
|
+
if (cu === 0) break;
|
|
797
|
+
out += String.fromCharCode(cu);
|
|
798
|
+
}
|
|
799
|
+
return out.length > 0 ? out : void 0;
|
|
800
|
+
}
|
|
801
|
+
function parseTileSize(d, count) {
|
|
802
|
+
const w = signedLong(optProperty(d, count, PROP_FILL_WIDTH));
|
|
803
|
+
const h = signedLong(optProperty(d, count, PROP_FILL_HEIGHT));
|
|
804
|
+
if (w === void 0 || h === void 0 || w <= 0 || h <= 0) return void 0;
|
|
805
|
+
return {
|
|
806
|
+
widthPt: w / EMU_PER_POINT,
|
|
807
|
+
heightPt: h / EMU_PER_POINT
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
function inlineBlip(blob) {
|
|
811
|
+
if (!blob || blob.length < 8) return void 0;
|
|
812
|
+
const bytes = readEscherBlip(u16(blob, 2), u16(blob, 0) >> 4, blob.subarray(8));
|
|
813
|
+
return bytes ? { bytes } : void 0;
|
|
814
|
+
}
|
|
815
|
+
var PATTERN_TILE_PT = {
|
|
816
|
+
widthPt: 576 / 96,
|
|
817
|
+
heightPt: 576 / 96
|
|
818
|
+
};
|
|
819
|
+
/**
|
|
820
|
+
* §2.3.7.1 MSOFILLTYPE 1 — the picture a PATTERN fill repeats.
|
|
821
|
+
*
|
|
822
|
+
* The pattern itself is a one-bit bitmap in the picture store, and it carries
|
|
823
|
+
* no colour of its own: its set bits are the shape's `fillColor` and its clear
|
|
824
|
+
* bits its `fillBackColor`. So it is not a picture to be drawn but a stencil to
|
|
825
|
+
* be painted through, and the two colours are recorded nowhere else. 37625.ppt
|
|
826
|
+
* rules its every slide with a pale blue grid this way, and unread the whole
|
|
827
|
+
* deck came out on white.
|
|
828
|
+
*
|
|
829
|
+
* The result is handed back as an ordinary tiled picture: the tile path is
|
|
830
|
+
* already there and already tested, and a pattern IS a tiled picture once its
|
|
831
|
+
* two colours are in it.
|
|
832
|
+
*
|
|
833
|
+
* @param mono The pattern bitmap from the store, when it resolves.
|
|
834
|
+
* @param fgHex The shape's `fillColor` — what the set bits are painted in.
|
|
835
|
+
* @param bgHex Its `fillBackColor`; white when the shape states none.
|
|
836
|
+
* @returns The recoloured tile as a PNG, or `undefined` when the blip is not a
|
|
837
|
+
* bitmap this can read.
|
|
838
|
+
*/
|
|
839
|
+
function patternFill(mono, fgHex, bgHex) {
|
|
840
|
+
if (!mono || !isBmp(mono.bytes)) return void 0;
|
|
841
|
+
let decoded;
|
|
842
|
+
try {
|
|
843
|
+
decoded = decodeBmp(mono.bytes);
|
|
844
|
+
} catch {
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
const fg = rgb(fgHex ?? "000000");
|
|
848
|
+
const bg = rgb(bgHex ?? "FFFFFF");
|
|
849
|
+
const { width, height, data } = decoded;
|
|
850
|
+
const out = new Uint8Array(width * height * 3);
|
|
851
|
+
for (let i = 0; i < width * height; i++) {
|
|
852
|
+
const c = data[i * 3] + data[i * 3 + 1] + data[i * 3 + 2] < 384 ? fg : bg;
|
|
853
|
+
out[i * 3] = c[0];
|
|
854
|
+
out[i * 3 + 1] = c[1];
|
|
855
|
+
out[i * 3 + 2] = c[2];
|
|
856
|
+
}
|
|
857
|
+
return { bytes: encodePng(width, height, "rgb", out) };
|
|
858
|
+
}
|
|
859
|
+
/** `RRGGBB` → its three bytes. */
|
|
860
|
+
function rgb(hex) {
|
|
861
|
+
const n = Number.parseInt(hex, 16);
|
|
862
|
+
return Number.isFinite(n) ? [
|
|
863
|
+
n >> 16 & 255,
|
|
864
|
+
n >> 8 & 255,
|
|
865
|
+
n & 255
|
|
866
|
+
] : [
|
|
867
|
+
0,
|
|
868
|
+
0,
|
|
869
|
+
0
|
|
870
|
+
];
|
|
871
|
+
}
|
|
872
|
+
function slideBackground(fillColorHex, gradient, fillType, image, tiled) {
|
|
873
|
+
if (image) return {
|
|
874
|
+
image,
|
|
875
|
+
...tiled ? { imageTiled: true } : {}
|
|
876
|
+
};
|
|
877
|
+
if (gradient) return { gradient };
|
|
878
|
+
if (fillColorHex) return { fillColorHex };
|
|
338
879
|
}
|
|
339
880
|
function parseAnchor(data) {
|
|
340
881
|
let top;
|
|
@@ -364,6 +905,37 @@ function parseAnchor(data) {
|
|
|
364
905
|
h
|
|
365
906
|
};
|
|
366
907
|
}
|
|
908
|
+
function stated(d, count, propId, flag) {
|
|
909
|
+
const bools = optProperty(d, count, propId);
|
|
910
|
+
if (bools === void 0) return true;
|
|
911
|
+
return (bools & flag << 16) !== 0 ? (bools & flag) !== 0 : true;
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* §2.3.23 `cropFrom*` — the four edge crops, as fractions of the source.
|
|
915
|
+
*
|
|
916
|
+
* Each is a SIGNED 16.16 fixed-point fraction, so a negative one extends the
|
|
917
|
+
* picture past its box rather than cutting into it. A crop that leaves nothing
|
|
918
|
+
* of an axis is not one — 23884's globe keeps a ninth of its width and a
|
|
919
|
+
* seventh of its height, but zero would divide the fitting by nothing.
|
|
920
|
+
*
|
|
921
|
+
* @param d The OPT record's body.
|
|
922
|
+
* @param count The number of fixed entries it declares.
|
|
923
|
+
* @returns The four fractions, or `undefined` when the shape states no crop.
|
|
924
|
+
*/
|
|
925
|
+
function parsePictureCrop(d, count) {
|
|
926
|
+
const frac = (id) => {
|
|
927
|
+
const raw = optProperty(d, count, id);
|
|
928
|
+
return raw === void 0 ? 0 : (raw | 0) / 65536;
|
|
929
|
+
};
|
|
930
|
+
const crop = {
|
|
931
|
+
left: frac(PROP_CROP_LEFT),
|
|
932
|
+
top: frac(PROP_CROP_TOP),
|
|
933
|
+
right: frac(PROP_CROP_RIGHT),
|
|
934
|
+
bottom: frac(PROP_CROP_BOTTOM)
|
|
935
|
+
};
|
|
936
|
+
if (crop.left === 0 && crop.top === 0 && crop.right === 0 && crop.bottom === 0) return void 0;
|
|
937
|
+
return 1 - crop.left - crop.right > 0 && 1 - crop.top - crop.bottom > 0 ? crop : void 0;
|
|
938
|
+
}
|
|
367
939
|
function optProperty(d, count, wantId) {
|
|
368
940
|
for (let i = 0; i < count && i * 6 + 6 <= d.length; i++) {
|
|
369
941
|
const id = u16(d, i * 6);
|
|
@@ -375,11 +947,29 @@ function optComplex(d, count, wantId) {
|
|
|
375
947
|
for (let i = 0; i < count && i * 6 + 6 <= d.length; i++) {
|
|
376
948
|
const id = u16(d, i * 6);
|
|
377
949
|
if ((id & 32768) === 0) continue;
|
|
378
|
-
const len = u32(d, i * 6 + 2);
|
|
950
|
+
const len = arrayBlobLength(d, offset, id & 16383, u32(d, i * 6 + 2));
|
|
379
951
|
if ((id & 16383) === wantId) return offset + len <= d.length && len > 0 ? d.subarray(offset, offset + len) : void 0;
|
|
380
952
|
offset += len;
|
|
381
953
|
}
|
|
382
954
|
}
|
|
955
|
+
var ARRAY_PROPS = new Set([
|
|
956
|
+
325,
|
|
957
|
+
326,
|
|
958
|
+
327,
|
|
959
|
+
329,
|
|
960
|
+
330,
|
|
961
|
+
337,
|
|
962
|
+
338,
|
|
963
|
+
339
|
|
964
|
+
]);
|
|
965
|
+
function arrayBlobLength(d, offset, id, len) {
|
|
966
|
+
if (!ARRAY_PROPS.has(id) || offset + 6 > d.length) return len;
|
|
967
|
+
const nElems = u16(d, offset);
|
|
968
|
+
const raw = u16(d, offset + 4);
|
|
969
|
+
const signed = raw >= 32768 ? raw - 65536 : raw;
|
|
970
|
+
const size = signed < 0 ? -signed >> 2 : signed;
|
|
971
|
+
return size > 0 && nElems > 0 && nElems * size === len ? len + 6 : len;
|
|
972
|
+
}
|
|
383
973
|
function arrayHeader(blob) {
|
|
384
974
|
if (blob.length < 6) return void 0;
|
|
385
975
|
const raw = u16(blob, 4);
|
|
@@ -501,6 +1091,33 @@ var SYS_COLORS = new Map([
|
|
|
501
1091
|
[23, "000000"],
|
|
502
1092
|
[24, "FFFFE1"]
|
|
503
1093
|
]);
|
|
1094
|
+
/**
|
|
1095
|
+
* MS-ODRAW §2.3.7.1 `fillType` — the sweep a shaded fill asks for, or
|
|
1096
|
+
* `undefined` when the shape is filled with one flat colour (type 0) or with
|
|
1097
|
+
* something this reader does not draw (a picture, a texture, a pattern).
|
|
1098
|
+
*
|
|
1099
|
+
* @param d The OPT record's data.
|
|
1100
|
+
* @param count Its entry count (the record's instance).
|
|
1101
|
+
* @param fromHex The resolved `fillColor` — one end of the sweep.
|
|
1102
|
+
* @param scheme The slide's colour scheme, for a scheme-relative far end.
|
|
1103
|
+
*/
|
|
1104
|
+
function parseFillGradient(d, count, fromHex, scheme) {
|
|
1105
|
+
const type = optProperty(d, count, PROP_FILL_TYPE);
|
|
1106
|
+
if (type === void 0 || type < 4 || type > 8) return void 0;
|
|
1107
|
+
const toHex = optColor(d, count, PROP_FILL_BACK_COLOR, scheme) ?? "FFFFFF";
|
|
1108
|
+
if (fromHex === void 0) return void 0;
|
|
1109
|
+
const raw = optProperty(d, count, PROP_FILL_ANGLE);
|
|
1110
|
+
const angleDeg = raw === void 0 ? 90 : 90 - (raw | 0) / 65536;
|
|
1111
|
+
const focus = optProperty(d, count, PROP_FILL_FOCUS);
|
|
1112
|
+
const focusPct = focus === void 0 ? 0 : (focus | 0) << 16 >> 16;
|
|
1113
|
+
return {
|
|
1114
|
+
fromHex,
|
|
1115
|
+
toHex,
|
|
1116
|
+
angleDeg,
|
|
1117
|
+
radial: type === 5 || type === 6,
|
|
1118
|
+
...focusPct !== 0 ? { focusPct } : {}
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
504
1121
|
function optColor(d, count, wantId, scheme) {
|
|
505
1122
|
const v = optProperty(d, count, wantId);
|
|
506
1123
|
if (v === void 0) return void 0;
|
|
@@ -512,13 +1129,14 @@ function optColor(d, count, wantId, scheme) {
|
|
|
512
1129
|
}
|
|
513
1130
|
if (flags === COLORREF_SYS_INDEX) return SYS_COLORS.get(v & 65535);
|
|
514
1131
|
}
|
|
515
|
-
function resolveScheme(slideData,
|
|
1132
|
+
function resolveScheme(slideData, ctx) {
|
|
516
1133
|
let schemeData = findColorScheme(slideData);
|
|
517
1134
|
const slideAtom = findChild(slideData, (r) => r.type === RT_SLIDE_ATOM);
|
|
518
|
-
if (slideAtom && slideAtom.length >= 22 &&
|
|
1135
|
+
if (slideAtom && slideAtom.length >= 22 && ctx) {
|
|
519
1136
|
if ((u16(slideAtom, 20) & SLIDE_FLAG_MASTER_SCHEME) !== 0) {
|
|
520
|
-
const
|
|
521
|
-
const
|
|
1137
|
+
const id = u32(slideAtom, 12);
|
|
1138
|
+
const masterOff = ctx.persist.get(ctx.byMasterId.get(id) ?? id);
|
|
1139
|
+
const masterRec = masterOff !== void 0 ? recordAt(ctx.stream, masterOff) : void 0;
|
|
522
1140
|
if (masterRec && masterRec.type === RT_MAIN_MASTER) {
|
|
523
1141
|
const masterScheme = findColorScheme(masterRec.data);
|
|
524
1142
|
if (masterScheme) schemeData = masterScheme;
|
|
@@ -541,59 +1159,61 @@ function parseColorScheme(d) {
|
|
|
541
1159
|
}
|
|
542
1160
|
function readBlipBytes(pictures, foDelay) {
|
|
543
1161
|
if (foDelay < 0 || foDelay + 8 > pictures.length) return void 0;
|
|
1162
|
+
const verInstance = u16(pictures, foDelay);
|
|
1163
|
+
const type = u16(pictures, foDelay + 2);
|
|
544
1164
|
const recLen = u32(pictures, foDelay + 4);
|
|
545
1165
|
const start = foDelay + 8;
|
|
546
1166
|
const blip = pictures.subarray(start, Math.min(pictures.length, start + recLen));
|
|
547
|
-
|
|
548
|
-
for (let off = 0; off < limit; off++) for (const magic of IMAGE_MAGICS) if (matchesMagic(blip, off, magic)) return blip.subarray(off);
|
|
549
|
-
}
|
|
550
|
-
var IMAGE_MAGICS = [[
|
|
551
|
-
137,
|
|
552
|
-
80,
|
|
553
|
-
78,
|
|
554
|
-
71
|
|
555
|
-
], [
|
|
556
|
-
255,
|
|
557
|
-
216,
|
|
558
|
-
255
|
|
559
|
-
]];
|
|
560
|
-
function matchesMagic(d, off, magic) {
|
|
561
|
-
if (off + magic.length > d.length) return false;
|
|
562
|
-
for (let i = 0; i < magic.length; i++) if (d[off + i] !== magic[i]) return false;
|
|
563
|
-
return true;
|
|
1167
|
+
return readEscherBlip(type, verInstance >> 4, blip);
|
|
564
1168
|
}
|
|
565
1169
|
/** The concatenated plain text of a paragraph's runs. */
|
|
566
1170
|
function paragraphText(p) {
|
|
567
1171
|
return p.runs.map((r) => r.text).join("");
|
|
568
1172
|
}
|
|
569
|
-
function buildStyledParagraphs(text, styleData) {
|
|
570
|
-
const style = styleData ? parseStyleTextProp(styleData, text.length) : void 0;
|
|
1173
|
+
function buildStyledParagraphs(text, styleData, defaults, env) {
|
|
1174
|
+
const style = styleData ? parseStyleTextProp(styleData, text.length, env) : void 0;
|
|
571
1175
|
const charProps = style ? expandCharProps(style.charRuns, text.length) : void 0;
|
|
572
|
-
const
|
|
1176
|
+
const paraProps = style ? expandParaProps(style.paraRuns, text.length) : void 0;
|
|
573
1177
|
const paras = [];
|
|
574
1178
|
let runs = [];
|
|
575
1179
|
let cur = "";
|
|
576
1180
|
let curProps;
|
|
577
|
-
let
|
|
1181
|
+
let paraStart = 0;
|
|
578
1182
|
const pushRun = () => {
|
|
579
1183
|
if (cur.length > 0) runs.push(toRun(cur, curProps));
|
|
580
1184
|
cur = "";
|
|
581
1185
|
};
|
|
582
|
-
const endParagraph = () => {
|
|
1186
|
+
const endParagraph = (end) => {
|
|
583
1187
|
pushRun();
|
|
584
|
-
const meta =
|
|
1188
|
+
const meta = paraProps?.[paraStart];
|
|
1189
|
+
const lvl = defaults?.(meta?.level ?? 0);
|
|
1190
|
+
const align = meta?.align ?? lvl?.align;
|
|
1191
|
+
const on = meta?.bulletOn ?? lvl?.bulletOn;
|
|
1192
|
+
const char = meta?.bullet ?? lvl?.bullet;
|
|
1193
|
+
const bullet = on === false ? void 0 : on === true ? char ?? "•" : char;
|
|
1194
|
+
const leftMarginPt = meta?.leftMarginPt ?? lvl?.leftMarginPt;
|
|
1195
|
+
const indentPt = meta?.indentPt ?? lvl?.indentPt;
|
|
1196
|
+
const lineSpacing = meta?.lineSpacing ?? lvl?.lineSpacing;
|
|
1197
|
+
const spaceBeforePt = meta?.spaceBeforePt ?? lvl?.spaceBeforePt;
|
|
1198
|
+
const spaceAfterPt = meta?.spaceAfterPt ?? lvl?.spaceAfterPt;
|
|
585
1199
|
paras.push({
|
|
586
|
-
runs,
|
|
587
|
-
...
|
|
588
|
-
...meta?.level !== void 0 ? { level: meta.level } : {}
|
|
1200
|
+
runs: lvl ? runs.map((r) => inherit(r, lvl)) : runs,
|
|
1201
|
+
...align !== void 0 ? { align } : {},
|
|
1202
|
+
...meta?.level !== void 0 ? { level: meta.level } : {},
|
|
1203
|
+
...bullet !== void 0 ? { bullet } : {},
|
|
1204
|
+
...leftMarginPt !== void 0 ? { leftMarginPt } : {},
|
|
1205
|
+
...indentPt !== void 0 ? { indentPt } : {},
|
|
1206
|
+
...lineSpacing !== void 0 ? { lineSpacing } : {},
|
|
1207
|
+
...spaceBeforePt !== void 0 ? { spaceBeforePt } : {},
|
|
1208
|
+
...spaceAfterPt !== void 0 ? { spaceAfterPt } : {}
|
|
589
1209
|
});
|
|
590
1210
|
runs = [];
|
|
591
|
-
|
|
1211
|
+
paraStart = end + 1;
|
|
592
1212
|
};
|
|
593
1213
|
for (let i = 0; i < text.length; i++) {
|
|
594
1214
|
const c = text.charCodeAt(i);
|
|
595
1215
|
if (c === 13) {
|
|
596
|
-
endParagraph();
|
|
1216
|
+
endParagraph(i);
|
|
597
1217
|
continue;
|
|
598
1218
|
}
|
|
599
1219
|
const props = charProps?.[i];
|
|
@@ -602,10 +1222,21 @@ function buildStyledParagraphs(text, styleData) {
|
|
|
602
1222
|
if (c === 11 || c === 10 || c === 9) cur += " ";
|
|
603
1223
|
else if (c !== 65279 && c >= 32) cur += text[i];
|
|
604
1224
|
}
|
|
605
|
-
endParagraph();
|
|
1225
|
+
endParagraph(text.length);
|
|
606
1226
|
if (paras.length > 1 && paragraphText(paras[paras.length - 1]).length === 0) paras.pop();
|
|
607
1227
|
return paras;
|
|
608
1228
|
}
|
|
1229
|
+
function inherit(run, lvl) {
|
|
1230
|
+
const sizePt = run.sizePt ?? lvl.sizePt;
|
|
1231
|
+
const colorHex = run.colorHex ?? lvl.colorHex;
|
|
1232
|
+
const fontFamily = run.fontFamily ?? lvl.fontFamily;
|
|
1233
|
+
return {
|
|
1234
|
+
...run,
|
|
1235
|
+
...sizePt !== void 0 ? { sizePt } : {},
|
|
1236
|
+
...colorHex !== void 0 ? { colorHex } : {},
|
|
1237
|
+
...fontFamily !== void 0 ? { fontFamily } : {}
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
609
1240
|
function toRun(text, props) {
|
|
610
1241
|
if (!props) return { text };
|
|
611
1242
|
return {
|
|
@@ -614,13 +1245,14 @@ function toRun(text, props) {
|
|
|
614
1245
|
...props.italic ? { italic: true } : {},
|
|
615
1246
|
...props.underline ? { underline: true } : {},
|
|
616
1247
|
...props.sizePt ? { sizePt: props.sizePt } : {},
|
|
617
|
-
...props.colorHex ? { colorHex: props.colorHex } : {}
|
|
1248
|
+
...props.colorHex ? { colorHex: props.colorHex } : {},
|
|
1249
|
+
...props.fontFamily ? { fontFamily: props.fontFamily } : {}
|
|
618
1250
|
};
|
|
619
1251
|
}
|
|
620
1252
|
function sameProps(a, b) {
|
|
621
1253
|
if (a === b) return true;
|
|
622
1254
|
if (!a || !b) return false;
|
|
623
|
-
return !!a.bold === !!b.bold && !!a.italic === !!b.italic && !!a.underline === !!b.underline && a.sizePt === b.sizePt && a.colorHex === b.colorHex;
|
|
1255
|
+
return !!a.bold === !!b.bold && !!a.italic === !!b.italic && !!a.underline === !!b.underline && a.sizePt === b.sizePt && a.colorHex === b.colorHex && a.fontFamily === b.fontFamily;
|
|
624
1256
|
}
|
|
625
1257
|
function expandCharProps(charRuns, textLen) {
|
|
626
1258
|
const out = [];
|
|
@@ -628,7 +1260,14 @@ function expandCharProps(charRuns, textLen) {
|
|
|
628
1260
|
while (out.length < textLen) out.push(charRuns[charRuns.length - 1] ?? { count: 0 });
|
|
629
1261
|
return out;
|
|
630
1262
|
}
|
|
631
|
-
|
|
1263
|
+
/** The same, for the paragraph runs: the run that covers each character. */
|
|
1264
|
+
function expandParaProps(paraRuns, textLen) {
|
|
1265
|
+
const out = [];
|
|
1266
|
+
for (const run of paraRuns) for (let i = 0; i < run.count && out.length < textLen; i++) out.push(run);
|
|
1267
|
+
while (out.length < textLen) out.push(paraRuns[paraRuns.length - 1]);
|
|
1268
|
+
return out;
|
|
1269
|
+
}
|
|
1270
|
+
function parseStyleTextProp(data, textLen, env) {
|
|
632
1271
|
const target = textLen + 1;
|
|
633
1272
|
let off = 0;
|
|
634
1273
|
const paraRuns = [];
|
|
@@ -636,32 +1275,19 @@ function parseStyleTextProp(data, textLen) {
|
|
|
636
1275
|
while (consumed < target && off + 10 <= data.length) {
|
|
637
1276
|
const count = u32(data, off);
|
|
638
1277
|
const level = u16(data, off + 4);
|
|
639
|
-
const
|
|
640
|
-
off
|
|
641
|
-
let align;
|
|
642
|
-
if ((mask & 15) !== 0) off += 2;
|
|
643
|
-
if ((mask & 128) !== 0) off += 2;
|
|
644
|
-
if ((mask & 16) !== 0) off += 2;
|
|
645
|
-
if ((mask & 64) !== 0) off += 2;
|
|
646
|
-
if ((mask & 32) !== 0) off += 4;
|
|
647
|
-
if ((mask & 2048) !== 0) {
|
|
648
|
-
align = u16(data, off);
|
|
649
|
-
off += 2;
|
|
650
|
-
}
|
|
651
|
-
if ((mask & 4096) !== 0) off += 2;
|
|
652
|
-
if ((mask & 8192) !== 0) off += 2;
|
|
653
|
-
if ((mask & 16384) !== 0) off += 2;
|
|
654
|
-
if ((mask & 256) !== 0) off += 2;
|
|
655
|
-
if ((mask & 1024) !== 0) off += 2;
|
|
656
|
-
if ((mask & 32768) !== 0) off += 2;
|
|
657
|
-
if ((mask & 1048576) !== 0) off += 2 + (off + 2 <= data.length ? u16(data, off) : 0) * 4;
|
|
658
|
-
if ((mask & 65536) !== 0) off += 2;
|
|
659
|
-
if ((mask & 917504) !== 0) off += 2;
|
|
660
|
-
if ((mask & 2097152) !== 0) off += 2;
|
|
1278
|
+
const pf = readPfException(data, off + 6);
|
|
1279
|
+
off = pf.off;
|
|
661
1280
|
paraRuns.push({
|
|
662
1281
|
count,
|
|
663
1282
|
level,
|
|
664
|
-
...align !== void 0 ? { align } : {}
|
|
1283
|
+
...pf.align !== void 0 ? { align: pf.align } : {},
|
|
1284
|
+
...pf.bullet !== void 0 ? { bullet: pf.bullet } : {},
|
|
1285
|
+
...pf.bulletOn !== void 0 ? { bulletOn: pf.bulletOn } : {},
|
|
1286
|
+
...pf.leftMarginPt !== void 0 ? { leftMarginPt: pf.leftMarginPt } : {},
|
|
1287
|
+
...pf.indentPt !== void 0 ? { indentPt: pf.indentPt } : {},
|
|
1288
|
+
...pf.lineSpacing !== void 0 ? { lineSpacing: pf.lineSpacing } : {},
|
|
1289
|
+
...pf.spaceBeforePt !== void 0 ? { spaceBeforePt: pf.spaceBeforePt } : {},
|
|
1290
|
+
...pf.spaceAfterPt !== void 0 ? { spaceAfterPt: pf.spaceAfterPt } : {}
|
|
665
1291
|
});
|
|
666
1292
|
consumed += count;
|
|
667
1293
|
if (count <= 0) break;
|
|
@@ -670,31 +1296,12 @@ function parseStyleTextProp(data, textLen) {
|
|
|
670
1296
|
consumed = 0;
|
|
671
1297
|
while (consumed < target && off + 8 <= data.length) {
|
|
672
1298
|
const count = u32(data, off);
|
|
673
|
-
const
|
|
674
|
-
off
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
if ((mask & 1) !== 0 && (style & 1) !== 0) run.bold = true;
|
|
680
|
-
if ((mask & 2) !== 0 && (style & 2) !== 0) run.italic = true;
|
|
681
|
-
if ((mask & 4) !== 0 && (style & 4) !== 0) run.underline = true;
|
|
682
|
-
}
|
|
683
|
-
if ((mask & 65536) !== 0) off += 2;
|
|
684
|
-
if ((mask & 2097152) !== 0) off += 2;
|
|
685
|
-
if ((mask & 4194304) !== 0) off += 2;
|
|
686
|
-
if ((mask & 8388608) !== 0) off += 2;
|
|
687
|
-
if ((mask & 131072) !== 0) {
|
|
688
|
-
const sz = u16(data, off);
|
|
689
|
-
off += 2;
|
|
690
|
-
if (sz > 0) run.sizePt = sz;
|
|
691
|
-
}
|
|
692
|
-
if ((mask & 262144) !== 0) {
|
|
693
|
-
if (off + 4 <= data.length && data[off + 3] === 254) run.colorHex = hex2(data[off]) + hex2(data[off + 1]) + hex2(data[off + 2]);
|
|
694
|
-
off += 4;
|
|
695
|
-
}
|
|
696
|
-
if ((mask & 524288) !== 0) off += 2;
|
|
697
|
-
charRuns.push(run);
|
|
1299
|
+
const cf = readCfException(data, off + 4, env?.scheme, env?.fonts);
|
|
1300
|
+
off = cf.off;
|
|
1301
|
+
charRuns.push({
|
|
1302
|
+
count,
|
|
1303
|
+
...cf.props
|
|
1304
|
+
});
|
|
698
1305
|
consumed += count;
|
|
699
1306
|
if (count <= 0) break;
|
|
700
1307
|
}
|
|
@@ -703,6 +1310,128 @@ function parseStyleTextProp(data, textLen) {
|
|
|
703
1310
|
charRuns
|
|
704
1311
|
} : void 0;
|
|
705
1312
|
}
|
|
1313
|
+
/**
|
|
1314
|
+
* §2.9.32 ParaSpacing → points, or `undefined` when the value is a percentage.
|
|
1315
|
+
*
|
|
1316
|
+
* Negative states a DISTANCE in master units, 576 to the inch. Positive states
|
|
1317
|
+
* a percentage of the line's height, which is not a distance this reader can
|
|
1318
|
+
* work out — the line has not been set yet — so it is left for the paragraph to
|
|
1319
|
+
* inherit nothing rather than guessed at.
|
|
1320
|
+
*/
|
|
1321
|
+
function spacingPt(value) {
|
|
1322
|
+
return value < 0 ? -value / 8 : void 0;
|
|
1323
|
+
}
|
|
1324
|
+
function readPfException(data, start) {
|
|
1325
|
+
const mask = u32(data, start);
|
|
1326
|
+
let off = start + 4;
|
|
1327
|
+
let align;
|
|
1328
|
+
let bulletOn;
|
|
1329
|
+
let bullet;
|
|
1330
|
+
let leftMarginPt;
|
|
1331
|
+
let indentPt;
|
|
1332
|
+
let lineSpacing;
|
|
1333
|
+
let spaceBeforePt;
|
|
1334
|
+
let spaceAfterPt;
|
|
1335
|
+
if ((mask & 15) !== 0) {
|
|
1336
|
+
bulletOn = (u16(data, off) & 1) !== 0;
|
|
1337
|
+
off += 2;
|
|
1338
|
+
}
|
|
1339
|
+
if ((mask & 128) !== 0) {
|
|
1340
|
+
bullet = bulletCharacter(u16(data, off));
|
|
1341
|
+
off += 2;
|
|
1342
|
+
}
|
|
1343
|
+
if ((mask & 16) !== 0) off += 2;
|
|
1344
|
+
if ((mask & 64) !== 0) off += 2;
|
|
1345
|
+
if ((mask & 32) !== 0) off += 4;
|
|
1346
|
+
if ((mask & 2048) !== 0) {
|
|
1347
|
+
align = u16(data, off);
|
|
1348
|
+
off += 2;
|
|
1349
|
+
}
|
|
1350
|
+
if ((mask & 4096) !== 0) {
|
|
1351
|
+
lineSpacing = i16(data, off);
|
|
1352
|
+
off += 2;
|
|
1353
|
+
}
|
|
1354
|
+
if ((mask & 8192) !== 0) {
|
|
1355
|
+
spaceBeforePt = spacingPt(i16(data, off));
|
|
1356
|
+
off += 2;
|
|
1357
|
+
}
|
|
1358
|
+
if ((mask & 16384) !== 0) {
|
|
1359
|
+
spaceAfterPt = spacingPt(i16(data, off));
|
|
1360
|
+
off += 2;
|
|
1361
|
+
}
|
|
1362
|
+
if ((mask & 256) !== 0) {
|
|
1363
|
+
leftMarginPt = u16(data, off) / 8;
|
|
1364
|
+
off += 2;
|
|
1365
|
+
}
|
|
1366
|
+
if ((mask & 1024) !== 0) {
|
|
1367
|
+
indentPt = u16(data, off) / 8;
|
|
1368
|
+
off += 2;
|
|
1369
|
+
}
|
|
1370
|
+
if ((mask & 32768) !== 0) off += 2;
|
|
1371
|
+
if ((mask & 1048576) !== 0) off += 2 + (off + 2 <= data.length ? u16(data, off) : 0) * 4;
|
|
1372
|
+
if ((mask & 65536) !== 0) off += 2;
|
|
1373
|
+
if ((mask & 917504) !== 0) off += 2;
|
|
1374
|
+
if ((mask & 2097152) !== 0) off += 2;
|
|
1375
|
+
return {
|
|
1376
|
+
off,
|
|
1377
|
+
...align !== void 0 ? { align } : {},
|
|
1378
|
+
...bullet !== void 0 ? { bullet } : {},
|
|
1379
|
+
...bulletOn !== void 0 ? { bulletOn } : {},
|
|
1380
|
+
...leftMarginPt !== void 0 ? { leftMarginPt } : {},
|
|
1381
|
+
...indentPt !== void 0 ? { indentPt } : {},
|
|
1382
|
+
...lineSpacing !== void 0 ? { lineSpacing } : {},
|
|
1383
|
+
...spaceBeforePt !== void 0 ? { spaceBeforePt } : {},
|
|
1384
|
+
...spaceAfterPt !== void 0 ? { spaceAfterPt } : {}
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1387
|
+
function bulletCharacter(code) {
|
|
1388
|
+
if (code === 0) return void 0;
|
|
1389
|
+
const ch = String.fromCharCode(code);
|
|
1390
|
+
return code < 8192 || code >= 61440 && code <= 61695 ? fromSymbolFont(ch, "Wingdings") : ch;
|
|
1391
|
+
}
|
|
1392
|
+
function readCfException(data, start, scheme, fonts) {
|
|
1393
|
+
const mask = u32(data, start);
|
|
1394
|
+
let off = start + 4;
|
|
1395
|
+
const props = {};
|
|
1396
|
+
if ((mask & 16055) !== 0) {
|
|
1397
|
+
const style = u16(data, off);
|
|
1398
|
+
off += 2;
|
|
1399
|
+
if ((mask & 1) !== 0 && (style & 1) !== 0) props.bold = true;
|
|
1400
|
+
if ((mask & 2) !== 0 && (style & 2) !== 0) props.italic = true;
|
|
1401
|
+
if ((mask & 4) !== 0 && (style & 4) !== 0) props.underline = true;
|
|
1402
|
+
}
|
|
1403
|
+
if ((mask & 65536) !== 0) {
|
|
1404
|
+
props.fontRef = u16(data, off);
|
|
1405
|
+
off += 2;
|
|
1406
|
+
}
|
|
1407
|
+
if ((mask & 2097152) !== 0) off += 2;
|
|
1408
|
+
if ((mask & 4194304) !== 0) off += 2;
|
|
1409
|
+
if ((mask & 8388608) !== 0) off += 2;
|
|
1410
|
+
if ((mask & 131072) !== 0) {
|
|
1411
|
+
const sz = u16(data, off);
|
|
1412
|
+
off += 2;
|
|
1413
|
+
if (sz > 0) props.sizePt = sz;
|
|
1414
|
+
}
|
|
1415
|
+
if ((mask & 262144) !== 0) {
|
|
1416
|
+
const index = off + 4 <= data.length ? data[off + 3] : void 0;
|
|
1417
|
+
if (index === 254) props.colorHex = hex2(data[off]) + hex2(data[off + 1]) + hex2(data[off + 2]);
|
|
1418
|
+
else if (index !== void 0 && index < 8) {
|
|
1419
|
+
props.colorIndex = index;
|
|
1420
|
+
const slot = scheme?.[index];
|
|
1421
|
+
if (slot !== void 0) props.colorHex = slot;
|
|
1422
|
+
}
|
|
1423
|
+
off += 4;
|
|
1424
|
+
}
|
|
1425
|
+
if ((mask & 524288) !== 0) off += 2;
|
|
1426
|
+
if (props.fontRef !== void 0) {
|
|
1427
|
+
const name = fonts?.[props.fontRef];
|
|
1428
|
+
if (name !== void 0 && name.length > 0) props.fontFamily = name;
|
|
1429
|
+
}
|
|
1430
|
+
return {
|
|
1431
|
+
off,
|
|
1432
|
+
props
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
706
1435
|
function hex2(n) {
|
|
707
1436
|
return n.toString(16).padStart(2, "0").toUpperCase();
|
|
708
1437
|
}
|