reamkit 1.23.0 → 1.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/core/bmp.d.ts +33 -0
- package/dist/esm/core/bmp.js +276 -0
- package/dist/esm/core/document-model/types.d.ts +37 -0
- package/dist/esm/core/drawingml/chart-geometry.js +3 -2
- package/dist/esm/core/drawingml/diagram/colors.d.ts +52 -0
- package/dist/esm/core/drawingml/diagram/colors.js +125 -0
- package/dist/esm/core/drawingml/diagram/data-model.d.ts +47 -0
- package/dist/esm/core/drawingml/diagram/data-model.js +119 -0
- package/dist/esm/core/drawingml/diagram/layout-engine.d.ts +46 -0
- package/dist/esm/core/drawingml/diagram/layout-engine.js +948 -0
- package/dist/esm/core/drawingml/diagram/run.d.ts +37 -0
- package/dist/esm/core/drawingml/diagram/run.js +50 -0
- package/dist/esm/core/drawingml/diagram/to-drawing.d.ts +16 -0
- package/dist/esm/core/drawingml/diagram/to-drawing.js +86 -0
- package/dist/esm/core/drawingml/preset-geometry.js +116 -6
- package/dist/esm/core/drawingml/text-warp.d.ts +58 -0
- package/dist/esm/core/drawingml/text-warp.js +355 -0
- package/dist/esm/core/drawingml/theme-parser.js +9 -3
- package/dist/esm/core/font/measure.d.ts +12 -0
- package/dist/esm/core/font/measure.js +36 -0
- package/dist/esm/core/images.d.ts +30 -2
- package/dist/esm/core/images.js +121 -5
- package/dist/esm/core/metafile/emf.js +144 -16
- package/dist/esm/core/metafile/picture.d.ts +49 -0
- package/dist/esm/core/metafile/picture.js +70 -1
- package/dist/esm/core/metafile/wmf.js +53 -4
- package/dist/esm/core/ole/escher-blip.js +11 -1
- package/dist/esm/excel/print-model.js +2 -2
- package/dist/esm/excel/sheet-drawing.js +1 -1
- package/dist/esm/excel/sheet-to-flow.js +16 -2
- package/dist/esm/html/html-writer.js +2 -1
- package/dist/esm/layout/page-doc.d.ts +35 -0
- package/dist/esm/layout/page-doc.js +14 -1
- package/dist/esm/layout/styled-layout.js +176 -15
- package/dist/esm/pdf/shading.js +8 -6
- package/dist/esm/pdf/styled-page-emitter.js +42 -3
- package/dist/esm/pdf/vector-graphics.js +1 -1
- package/dist/esm/pdf-reader/image-decode.js +1 -1
- package/dist/esm/pptx/ppt/ppt-reader.js +95 -27
- package/dist/esm/pptx/ppt/ppt-text.d.ts +41 -0
- package/dist/esm/pptx/ppt/ppt-text.js +327 -37
- package/dist/esm/pptx/pptx-reader.js +90 -10
- package/dist/esm/pptx/slide-parser.d.ts +4 -1
- package/dist/esm/pptx/slide-parser.js +99 -13
- package/dist/esm/pptx/sp-helpers.d.ts +7 -3
- package/dist/esm/pptx/sp-helpers.js +45 -7
- package/dist/esm/pptx/table-style.d.ts +7 -0
- package/dist/esm/pptx/table-style.js +59 -7
- package/dist/esm/svg/svg-writer.js +1 -0
- package/dist/esm/word/document-parser.d.ts +4 -1
- package/dist/esm/word/document-parser.js +4 -1
- package/dist/esm/word/docx-reader.js +26 -13
- package/dist/esm/word/docx-writer.js +4 -0
- package/dist/esm/word/drawing-parser.d.ts +20 -0
- package/dist/esm/word/drawing-parser.js +37 -4
- package/dist/esm/word/table-parser.js +3 -0
- package/package.json +1 -1
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { decodeBmp, isBmp } from "../../core/bmp.js";
|
|
2
|
+
import { encodePng } from "../../core/png-encode.js";
|
|
1
3
|
import { fromSymbolFont } from "../../core/metafile/symbol-fonts.js";
|
|
2
4
|
import { isCfb, openCfb } from "../../core/ole/cfb.js";
|
|
3
5
|
import { readEscherBlip } from "../../core/ole/escher-blip.js";
|
|
@@ -18,6 +20,7 @@ var RT_OUTLINE_TEXT_REF_ATOM = 3998;
|
|
|
18
20
|
var RT_TEXT_MASTER_STYLE_ATOM = 4003;
|
|
19
21
|
var RT_SLIDE_ATOM = 1007;
|
|
20
22
|
var RT_MAIN_MASTER = 1016;
|
|
23
|
+
var RT_ENVIRONMENT = 1010;
|
|
21
24
|
var RT_FONT_COLLECTION = 2005;
|
|
22
25
|
var RT_FONT_ENTITY_ATOM = 4023;
|
|
23
26
|
var RT_COLOR_SCHEME_ATOM = 2032;
|
|
@@ -33,11 +36,31 @@ var FBT_SPGR_CONTAINER = 61443;
|
|
|
33
36
|
var FBT_SPGR = 61449;
|
|
34
37
|
var FBT_CHILD_ANCHOR = 61455;
|
|
35
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;
|
|
36
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;
|
|
37
59
|
var PROP_FILL_TYPE = 384;
|
|
38
60
|
var PROP_FILL_COLOR = 385;
|
|
39
61
|
var PROP_FILL_BACK_COLOR = 387;
|
|
40
62
|
var PROP_FILL_ANGLE = 395;
|
|
63
|
+
var PROP_FILL_FOCUS = 396;
|
|
41
64
|
var PROP_FILL_BLIP = 390;
|
|
42
65
|
var PROP_GTEXT_UNICODE = 192;
|
|
43
66
|
var PROP_GTEXT_SIZE = 195;
|
|
@@ -215,8 +238,10 @@ function readSlideList(stream, docData, persist, img) {
|
|
|
215
238
|
persist,
|
|
216
239
|
stream,
|
|
217
240
|
byMasterId: buildMasterIdMap(docData),
|
|
218
|
-
cache: /* @__PURE__ */ new Map()
|
|
241
|
+
cache: /* @__PURE__ */ new Map(),
|
|
242
|
+
documentStyles: parseDocumentTextStyles(docData, fonts)
|
|
219
243
|
};
|
|
244
|
+
const deckFurniture = headersFooters(docData, HF_INSTANCE_SLIDES);
|
|
220
245
|
const flush = () => {
|
|
221
246
|
if (pendingRef === void 0) return;
|
|
222
247
|
const slideOff = persist.get(pendingRef);
|
|
@@ -232,7 +257,12 @@ function readSlideList(stream, docData, persist, img) {
|
|
|
232
257
|
const flat = texts.flat();
|
|
233
258
|
const content = isSlide ? slideShapes(slideRec.data, img, texts, env, defaults) : { shapes: flat.some((p) => paragraphText(p).length > 0) ? [{ paragraphs: flat }] : [] };
|
|
234
259
|
const background = isSlide ? masterBackground(slideRec.data, masters, img) ?? content.background : void 0;
|
|
235
|
-
const
|
|
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) : [];
|
|
236
266
|
slides.push({
|
|
237
267
|
shapes: [...decoration, ...content.shapes],
|
|
238
268
|
...background ? { background } : {}
|
|
@@ -270,7 +300,7 @@ function collectParagraphs(d, depth, defaults, env) {
|
|
|
270
300
|
}
|
|
271
301
|
return out;
|
|
272
302
|
}
|
|
273
|
-
var TEXT_TYPE_OTHER =
|
|
303
|
+
var TEXT_TYPE_OTHER = 4;
|
|
274
304
|
var levelStyles = (defaults, textType) => defaults ? (level) => defaults(textType, level) : void 0;
|
|
275
305
|
function findFollowingStyle(recs, i) {
|
|
276
306
|
for (let j = i + 1; j < recs.length; j++) {
|
|
@@ -362,7 +392,7 @@ function collectShapeContainers(d, img, out, depth, env, outline, defaults, grou
|
|
|
362
392
|
if (depth > MAX_DEPTH) return;
|
|
363
393
|
for (const r of records(d)) {
|
|
364
394
|
if (r.type === FBT_SPGR_CONTAINER) {
|
|
365
|
-
collectShapeContainers(r.data, img, out, depth + 1, env, outline, defaults, groupFrame(r.data) ?? group);
|
|
395
|
+
collectShapeContainers(r.data, img, out, depth + 1, env, outline, defaults, groupFrame(r.data, group) ?? group);
|
|
366
396
|
continue;
|
|
367
397
|
}
|
|
368
398
|
if (r.type === FBT_SP_CONTAINER) {
|
|
@@ -372,6 +402,7 @@ function collectShapeContainers(d, img, out, depth, env, outline, defaults, grou
|
|
|
372
402
|
let shapeType = 0;
|
|
373
403
|
let fspFlags = 0;
|
|
374
404
|
let fillColorHex;
|
|
405
|
+
let backColorHex;
|
|
375
406
|
let lineColorHex;
|
|
376
407
|
let gradient;
|
|
377
408
|
let geometry;
|
|
@@ -382,13 +413,19 @@ function collectShapeContainers(d, img, out, depth, env, outline, defaults, grou
|
|
|
382
413
|
let tileSizePt;
|
|
383
414
|
let gtext;
|
|
384
415
|
let placeholder = false;
|
|
416
|
+
let placeholderId;
|
|
417
|
+
let pictureCrop;
|
|
418
|
+
let pictureTransparentHex;
|
|
385
419
|
for (const child of records(r.data)) if (child.type === FBT_FSP) {
|
|
386
420
|
shapeType = child.instance;
|
|
387
421
|
fspFlags = child.data.length >= 8 ? u32(child.data, 4) : 0;
|
|
388
422
|
} else if (child.type === FBT_CLIENT_ANCHOR) rectPt = parseAnchor(child.data);
|
|
389
423
|
else if (child.type === FBT_CHILD_ANCHOR && group) rectPt = childRect(child.data, group);
|
|
390
|
-
else if (child.type === FBT_CLIENT_DATA)
|
|
391
|
-
|
|
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);
|
|
392
429
|
else if (child.type === FBT_OPT) {
|
|
393
430
|
pib = optProperty(child.data, child.instance, PROP_PIB);
|
|
394
431
|
fillColorHex = stated(child.data, child.instance, PROP_FILL_BOOLS, FILL_F_FILLED) ? optColor(child.data, child.instance, PROP_FILL_COLOR, scheme) : void 0;
|
|
@@ -397,24 +434,35 @@ function collectShapeContainers(d, img, out, depth, env, outline, defaults, grou
|
|
|
397
434
|
geometry = parseFreeformGeometry(child.data, child.instance);
|
|
398
435
|
fillType = optProperty(child.data, child.instance, PROP_FILL_TYPE);
|
|
399
436
|
fillBlip = optProperty(child.data, child.instance, PROP_FILL_BLIP);
|
|
437
|
+
backColorHex = optColor(child.data, child.instance, PROP_FILL_BACK_COLOR, scheme);
|
|
400
438
|
tileSizePt = parseTileSize(child.data, child.instance);
|
|
401
439
|
gtext = parseWordArt(child.data, child.instance, fillColorHex);
|
|
402
440
|
inlineFill = optComplex(child.data, child.instance, PROP_FILL_BLIP);
|
|
403
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;
|
|
404
445
|
}
|
|
405
446
|
const blip = (index) => {
|
|
406
447
|
if (index === void 0 || index < 1 || index > img.foDelays.length) return void 0;
|
|
407
448
|
const bytes = readBlipBytes(img.pictures, img.foDelays[index - 1]);
|
|
408
449
|
return bytes ? { bytes } : void 0;
|
|
409
450
|
};
|
|
410
|
-
const
|
|
411
|
-
const
|
|
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;
|
|
412
455
|
if ((fspFlags & FSP_FLAG_BACKGROUND) !== 0) {
|
|
413
456
|
const bg = slideBackground(fillColorHex, gradient, fillType, fillImage, tiled);
|
|
414
457
|
if (bg && !out.background) out.background = bg;
|
|
415
458
|
continue;
|
|
416
459
|
}
|
|
417
|
-
const
|
|
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;
|
|
418
466
|
const textParas = gtext ?? (paragraphs?.some((p) => paragraphText(p).length > 0) === true ? paragraphs : void 0) ? gtext ? [gtext] : paragraphs : void 0;
|
|
419
467
|
const decorative = (fspFlags & (FSP_FLAG_PATRIARCH | 1025)) === 0;
|
|
420
468
|
const autoShape = !gtext && !image && rectPt && (shapeType > 0 || geometry) && decorative && (fillColorHex || lineColorHex || fillImage) ? {
|
|
@@ -427,10 +475,11 @@ function collectShapeContainers(d, img, out, depth, env, outline, defaults, grou
|
|
|
427
475
|
...fillImage && tiled && tileSizePt ? { tileSizePt } : {},
|
|
428
476
|
...geometry ? { geometry } : {}
|
|
429
477
|
} : void 0;
|
|
430
|
-
if (textParas || image || autoShape) out.shapes.push({
|
|
478
|
+
if (textParas || image || autoShape || placeholderId === PLACEHOLDER_DATE || placeholderId === PLACEHOLDER_SLIDE_NUMBER || placeholderId === PLACEHOLDER_FOOTER) out.shapes.push({
|
|
431
479
|
...rectPt ? { rectPt } : {},
|
|
432
480
|
...gtext ? { wordArt: true } : {},
|
|
433
481
|
...placeholder ? { placeholder: true } : {},
|
|
482
|
+
...placeholderId !== void 0 ? { placeholderId } : {},
|
|
434
483
|
...textParas ? { paragraphs: textParas } : {},
|
|
435
484
|
...image ? { image } : {},
|
|
436
485
|
...autoShape ? { autoShape } : {}
|
|
@@ -457,6 +506,23 @@ function parseFontCollection(docData) {
|
|
|
457
506
|
}
|
|
458
507
|
return out;
|
|
459
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
|
+
}
|
|
460
526
|
function parseMasterTextStyles(masterData, fonts) {
|
|
461
527
|
const out = /* @__PURE__ */ new Map();
|
|
462
528
|
for (const rec of records(masterData)) {
|
|
@@ -488,7 +554,12 @@ function readMasterLevels(d, levels, prefix, fonts) {
|
|
|
488
554
|
...cf.props,
|
|
489
555
|
...pf.align !== void 0 ? { align: pf.align } : {},
|
|
490
556
|
...pf.bullet !== void 0 ? { bullet: pf.bullet } : {},
|
|
491
|
-
...pf.bulletOn !== void 0 ? { bulletOn: pf.bulletOn } : {}
|
|
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 } : {}
|
|
492
563
|
});
|
|
493
564
|
}
|
|
494
565
|
return {
|
|
@@ -497,10 +568,10 @@ function readMasterLevels(d, levels, prefix, fonts) {
|
|
|
497
568
|
};
|
|
498
569
|
}
|
|
499
570
|
var TEXT_TYPE_FALLBACK = new Map([
|
|
500
|
-
[
|
|
501
|
-
[
|
|
502
|
-
[
|
|
503
|
-
[
|
|
571
|
+
[5, 1],
|
|
572
|
+
[6, 0],
|
|
573
|
+
[7, 1],
|
|
574
|
+
[8, 1]
|
|
504
575
|
]);
|
|
505
576
|
function masterDefaults(chain, scheme) {
|
|
506
577
|
return (textType, level) => {
|
|
@@ -510,7 +581,7 @@ function masterDefaults(chain, scheme) {
|
|
|
510
581
|
const levels = t === void 0 ? void 0 : styles.get(t);
|
|
511
582
|
const at = levels?.[Math.min(level, levels.length - 1)];
|
|
512
583
|
if (!at) continue;
|
|
513
|
-
const { colorHex: _c, colorIndex: _i, bullet: _b, bulletOn: _o, ...metrics } = at;
|
|
584
|
+
const { colorHex: _c, colorIndex: _i, bullet: _b, bulletOn: _o, spaceBeforePt: _sb, spaceAfterPt: _sa, lineSpacing: _ls, leftMarginPt: _lm, indentPt: _ip, ...metrics } = at;
|
|
514
585
|
merged = {
|
|
515
586
|
...t === textType ? at : metrics,
|
|
516
587
|
...merged
|
|
@@ -545,12 +616,83 @@ function slideFlag(slideData, flag) {
|
|
|
545
616
|
const slideAtom = findChild(slideData, (r) => r.type === RT_SLIDE_ATOM);
|
|
546
617
|
return slideAtom !== void 0 && slideAtom.length >= 22 && (u16(slideAtom, 20) & flag) !== 0;
|
|
547
618
|
}
|
|
548
|
-
function masterShapes(slideData, ctx, img) {
|
|
619
|
+
function masterShapes(slideData, ctx, img, furniture, defaults) {
|
|
549
620
|
if (!slideFlag(slideData, SLIDE_FLAG_MASTER_OBJECTS)) return [];
|
|
550
621
|
const master = masterOf(slideData, ctx);
|
|
551
622
|
if (!master) return [];
|
|
552
623
|
const scheme = resolveScheme(master.data, ctx);
|
|
553
|
-
return collectShapes(master.data, img, scheme ? { scheme } : {}).shapes.filter((s) => s.
|
|
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
|
+
}
|
|
554
696
|
}
|
|
555
697
|
function masterBackground(slideData, ctx, img) {
|
|
556
698
|
if (!slideFlag(slideData, SLIDE_FLAG_MASTER_BACKGROUND)) return void 0;
|
|
@@ -562,6 +704,7 @@ function masterBackground(slideData, ctx, img) {
|
|
|
562
704
|
function masterChain(slideData, ctx, scheme, fonts) {
|
|
563
705
|
const chain = [];
|
|
564
706
|
if (!ctx) return masterDefaults(chain, scheme);
|
|
707
|
+
const behind = ctx.documentStyles;
|
|
565
708
|
let data = slideData;
|
|
566
709
|
const seen = /* @__PURE__ */ new Set();
|
|
567
710
|
for (let hop = 0; hop < 4 && data; hop++) {
|
|
@@ -582,9 +725,10 @@ function masterChain(slideData, ctx, scheme, fonts) {
|
|
|
582
725
|
}
|
|
583
726
|
data = rec.type === RT_SLIDE ? rec.data : void 0;
|
|
584
727
|
}
|
|
728
|
+
if (behind.size > 0) chain.push(behind);
|
|
585
729
|
return masterDefaults(chain, scheme);
|
|
586
730
|
}
|
|
587
|
-
function groupFrame(spgrData) {
|
|
731
|
+
function groupFrame(spgrData, parent) {
|
|
588
732
|
const sp = [...records(spgrData)].find((r) => r.type === FBT_SP_CONTAINER);
|
|
589
733
|
if (!sp) return void 0;
|
|
590
734
|
let box;
|
|
@@ -599,6 +743,7 @@ function groupFrame(spgrData) {
|
|
|
599
743
|
h: i32(child.data, 12) - y
|
|
600
744
|
};
|
|
601
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);
|
|
602
747
|
if (!box || !rect || box.w <= 0 || box.h <= 0) return void 0;
|
|
603
748
|
return {
|
|
604
749
|
rect,
|
|
@@ -613,7 +758,7 @@ function childRect(d, g) {
|
|
|
613
758
|
const y2 = g.rect.y + (i32(d, 12) - g.y) / g.h * g.rect.h;
|
|
614
759
|
const w = x2 - x1;
|
|
615
760
|
const h = y2 - y1;
|
|
616
|
-
return w > 0
|
|
761
|
+
return (w > 0 || h > 0) && w >= 0 && h >= 0 && Math.abs(x1) < 5e3 && Math.abs(y1) < 5e3 ? {
|
|
617
762
|
x: x1,
|
|
618
763
|
y: y1,
|
|
619
764
|
w,
|
|
@@ -667,6 +812,63 @@ function inlineBlip(blob) {
|
|
|
667
812
|
const bytes = readEscherBlip(u16(blob, 2), u16(blob, 0) >> 4, blob.subarray(8));
|
|
668
813
|
return bytes ? { bytes } : void 0;
|
|
669
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
|
+
}
|
|
670
872
|
function slideBackground(fillColorHex, gradient, fillType, image, tiled) {
|
|
671
873
|
if (image) return {
|
|
672
874
|
image,
|
|
@@ -708,6 +910,32 @@ function stated(d, count, propId, flag) {
|
|
|
708
910
|
if (bools === void 0) return true;
|
|
709
911
|
return (bools & flag << 16) !== 0 ? (bools & flag) !== 0 : true;
|
|
710
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
|
+
}
|
|
711
939
|
function optProperty(d, count, wantId) {
|
|
712
940
|
for (let i = 0; i < count && i * 6 + 6 <= d.length; i++) {
|
|
713
941
|
const id = u16(d, i * 6);
|
|
@@ -879,11 +1107,15 @@ function parseFillGradient(d, count, fromHex, scheme) {
|
|
|
879
1107
|
const toHex = optColor(d, count, PROP_FILL_BACK_COLOR, scheme) ?? "FFFFFF";
|
|
880
1108
|
if (fromHex === void 0) return void 0;
|
|
881
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;
|
|
882
1113
|
return {
|
|
883
1114
|
fromHex,
|
|
884
1115
|
toHex,
|
|
885
|
-
angleDeg
|
|
886
|
-
radial: type === 5 || type === 6
|
|
1116
|
+
angleDeg,
|
|
1117
|
+
radial: type === 5 || type === 6,
|
|
1118
|
+
...focusPct !== 0 ? { focusPct } : {}
|
|
887
1119
|
};
|
|
888
1120
|
}
|
|
889
1121
|
function optColor(d, count, wantId, scheme) {
|
|
@@ -941,37 +1173,47 @@ function paragraphText(p) {
|
|
|
941
1173
|
function buildStyledParagraphs(text, styleData, defaults, env) {
|
|
942
1174
|
const style = styleData ? parseStyleTextProp(styleData, text.length, env) : void 0;
|
|
943
1175
|
const charProps = style ? expandCharProps(style.charRuns, text.length) : void 0;
|
|
944
|
-
const
|
|
1176
|
+
const paraProps = style ? expandParaProps(style.paraRuns, text.length) : void 0;
|
|
945
1177
|
const paras = [];
|
|
946
1178
|
let runs = [];
|
|
947
1179
|
let cur = "";
|
|
948
1180
|
let curProps;
|
|
949
|
-
let
|
|
1181
|
+
let paraStart = 0;
|
|
950
1182
|
const pushRun = () => {
|
|
951
1183
|
if (cur.length > 0) runs.push(toRun(cur, curProps));
|
|
952
1184
|
cur = "";
|
|
953
1185
|
};
|
|
954
|
-
const endParagraph = () => {
|
|
1186
|
+
const endParagraph = (end) => {
|
|
955
1187
|
pushRun();
|
|
956
|
-
const meta =
|
|
1188
|
+
const meta = paraProps?.[paraStart];
|
|
957
1189
|
const lvl = defaults?.(meta?.level ?? 0);
|
|
958
1190
|
const align = meta?.align ?? lvl?.align;
|
|
959
1191
|
const on = meta?.bulletOn ?? lvl?.bulletOn;
|
|
960
1192
|
const char = meta?.bullet ?? lvl?.bullet;
|
|
961
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;
|
|
962
1199
|
paras.push({
|
|
963
1200
|
runs: lvl ? runs.map((r) => inherit(r, lvl)) : runs,
|
|
964
1201
|
...align !== void 0 ? { align } : {},
|
|
965
1202
|
...meta?.level !== void 0 ? { level: meta.level } : {},
|
|
966
|
-
...bullet !== void 0 ? { bullet } : {}
|
|
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 } : {}
|
|
967
1209
|
});
|
|
968
1210
|
runs = [];
|
|
969
|
-
|
|
1211
|
+
paraStart = end + 1;
|
|
970
1212
|
};
|
|
971
1213
|
for (let i = 0; i < text.length; i++) {
|
|
972
1214
|
const c = text.charCodeAt(i);
|
|
973
1215
|
if (c === 13) {
|
|
974
|
-
endParagraph();
|
|
1216
|
+
endParagraph(i);
|
|
975
1217
|
continue;
|
|
976
1218
|
}
|
|
977
1219
|
const props = charProps?.[i];
|
|
@@ -980,7 +1222,7 @@ function buildStyledParagraphs(text, styleData, defaults, env) {
|
|
|
980
1222
|
if (c === 11 || c === 10 || c === 9) cur += " ";
|
|
981
1223
|
else if (c !== 65279 && c >= 32) cur += text[i];
|
|
982
1224
|
}
|
|
983
|
-
endParagraph();
|
|
1225
|
+
endParagraph(text.length);
|
|
984
1226
|
if (paras.length > 1 && paragraphText(paras[paras.length - 1]).length === 0) paras.pop();
|
|
985
1227
|
return paras;
|
|
986
1228
|
}
|
|
@@ -1018,6 +1260,13 @@ function expandCharProps(charRuns, textLen) {
|
|
|
1018
1260
|
while (out.length < textLen) out.push(charRuns[charRuns.length - 1] ?? { count: 0 });
|
|
1019
1261
|
return out;
|
|
1020
1262
|
}
|
|
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
|
+
}
|
|
1021
1270
|
function parseStyleTextProp(data, textLen, env) {
|
|
1022
1271
|
const target = textLen + 1;
|
|
1023
1272
|
let off = 0;
|
|
@@ -1033,7 +1282,12 @@ function parseStyleTextProp(data, textLen, env) {
|
|
|
1033
1282
|
level,
|
|
1034
1283
|
...pf.align !== void 0 ? { align: pf.align } : {},
|
|
1035
1284
|
...pf.bullet !== void 0 ? { bullet: pf.bullet } : {},
|
|
1036
|
-
...pf.bulletOn !== void 0 ? { bulletOn: pf.bulletOn } : {}
|
|
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 } : {}
|
|
1037
1291
|
});
|
|
1038
1292
|
consumed += count;
|
|
1039
1293
|
if (count <= 0) break;
|
|
@@ -1056,12 +1310,28 @@ function parseStyleTextProp(data, textLen, env) {
|
|
|
1056
1310
|
charRuns
|
|
1057
1311
|
} : void 0;
|
|
1058
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
|
+
}
|
|
1059
1324
|
function readPfException(data, start) {
|
|
1060
1325
|
const mask = u32(data, start);
|
|
1061
1326
|
let off = start + 4;
|
|
1062
1327
|
let align;
|
|
1063
1328
|
let bulletOn;
|
|
1064
1329
|
let bullet;
|
|
1330
|
+
let leftMarginPt;
|
|
1331
|
+
let indentPt;
|
|
1332
|
+
let lineSpacing;
|
|
1333
|
+
let spaceBeforePt;
|
|
1334
|
+
let spaceAfterPt;
|
|
1065
1335
|
if ((mask & 15) !== 0) {
|
|
1066
1336
|
bulletOn = (u16(data, off) & 1) !== 0;
|
|
1067
1337
|
off += 2;
|
|
@@ -1077,11 +1347,26 @@ function readPfException(data, start) {
|
|
|
1077
1347
|
align = u16(data, off);
|
|
1078
1348
|
off += 2;
|
|
1079
1349
|
}
|
|
1080
|
-
if ((mask & 4096) !== 0)
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
if ((mask &
|
|
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
|
+
}
|
|
1085
1370
|
if ((mask & 32768) !== 0) off += 2;
|
|
1086
1371
|
if ((mask & 1048576) !== 0) off += 2 + (off + 2 <= data.length ? u16(data, off) : 0) * 4;
|
|
1087
1372
|
if ((mask & 65536) !== 0) off += 2;
|
|
@@ -1091,7 +1376,12 @@ function readPfException(data, start) {
|
|
|
1091
1376
|
off,
|
|
1092
1377
|
...align !== void 0 ? { align } : {},
|
|
1093
1378
|
...bullet !== void 0 ? { bullet } : {},
|
|
1094
|
-
...bulletOn !== void 0 ? { bulletOn } : {}
|
|
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 } : {}
|
|
1095
1385
|
};
|
|
1096
1386
|
}
|
|
1097
1387
|
function bulletCharacter(code) {
|