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.
Files changed (57) hide show
  1. package/dist/esm/core/bmp.d.ts +33 -0
  2. package/dist/esm/core/bmp.js +276 -0
  3. package/dist/esm/core/document-model/types.d.ts +37 -0
  4. package/dist/esm/core/drawingml/chart-geometry.js +3 -2
  5. package/dist/esm/core/drawingml/diagram/colors.d.ts +52 -0
  6. package/dist/esm/core/drawingml/diagram/colors.js +125 -0
  7. package/dist/esm/core/drawingml/diagram/data-model.d.ts +47 -0
  8. package/dist/esm/core/drawingml/diagram/data-model.js +119 -0
  9. package/dist/esm/core/drawingml/diagram/layout-engine.d.ts +46 -0
  10. package/dist/esm/core/drawingml/diagram/layout-engine.js +948 -0
  11. package/dist/esm/core/drawingml/diagram/run.d.ts +37 -0
  12. package/dist/esm/core/drawingml/diagram/run.js +50 -0
  13. package/dist/esm/core/drawingml/diagram/to-drawing.d.ts +16 -0
  14. package/dist/esm/core/drawingml/diagram/to-drawing.js +86 -0
  15. package/dist/esm/core/drawingml/preset-geometry.js +116 -6
  16. package/dist/esm/core/drawingml/text-warp.d.ts +58 -0
  17. package/dist/esm/core/drawingml/text-warp.js +355 -0
  18. package/dist/esm/core/drawingml/theme-parser.js +9 -3
  19. package/dist/esm/core/font/measure.d.ts +12 -0
  20. package/dist/esm/core/font/measure.js +36 -0
  21. package/dist/esm/core/images.d.ts +30 -2
  22. package/dist/esm/core/images.js +121 -5
  23. package/dist/esm/core/metafile/emf.js +144 -16
  24. package/dist/esm/core/metafile/picture.d.ts +49 -0
  25. package/dist/esm/core/metafile/picture.js +70 -1
  26. package/dist/esm/core/metafile/wmf.js +53 -4
  27. package/dist/esm/core/ole/escher-blip.js +11 -1
  28. package/dist/esm/excel/print-model.js +2 -2
  29. package/dist/esm/excel/sheet-drawing.js +1 -1
  30. package/dist/esm/excel/sheet-to-flow.js +16 -2
  31. package/dist/esm/html/html-writer.js +2 -1
  32. package/dist/esm/layout/page-doc.d.ts +35 -0
  33. package/dist/esm/layout/page-doc.js +14 -1
  34. package/dist/esm/layout/styled-layout.js +176 -15
  35. package/dist/esm/pdf/shading.js +8 -6
  36. package/dist/esm/pdf/styled-page-emitter.js +42 -3
  37. package/dist/esm/pdf/vector-graphics.js +1 -1
  38. package/dist/esm/pdf-reader/image-decode.js +1 -1
  39. package/dist/esm/pptx/ppt/ppt-reader.js +95 -27
  40. package/dist/esm/pptx/ppt/ppt-text.d.ts +41 -0
  41. package/dist/esm/pptx/ppt/ppt-text.js +327 -37
  42. package/dist/esm/pptx/pptx-reader.js +90 -10
  43. package/dist/esm/pptx/slide-parser.d.ts +4 -1
  44. package/dist/esm/pptx/slide-parser.js +99 -13
  45. package/dist/esm/pptx/sp-helpers.d.ts +7 -3
  46. package/dist/esm/pptx/sp-helpers.js +45 -7
  47. package/dist/esm/pptx/table-style.d.ts +7 -0
  48. package/dist/esm/pptx/table-style.js +59 -7
  49. package/dist/esm/svg/svg-writer.js +1 -0
  50. package/dist/esm/word/document-parser.d.ts +4 -1
  51. package/dist/esm/word/document-parser.js +4 -1
  52. package/dist/esm/word/docx-reader.js +26 -13
  53. package/dist/esm/word/docx-writer.js +4 -0
  54. package/dist/esm/word/drawing-parser.d.ts +20 -0
  55. package/dist/esm/word/drawing-parser.js +37 -4
  56. package/dist/esm/word/table-parser.js +3 -0
  57. 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 decoration = isSlide ? masterShapes(slideRec.data, masters, img) : [];
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 = 3;
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) placeholder = findChild(child.data, (c) => c.type === RT_PLACEHOLDER_ATOM) !== void 0;
391
- else if (child.type === FBT_CLIENT_TEXTBOX) paragraphs = clientTextboxParagraphs(child.data, outline, defaults, env);
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 tiled = fillType === 2;
411
- const fillImage = fillType === 2 || fillType === 3 ? blip(fillBlip) ?? inlineBlip(inlineFill) : void 0;
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 image = blip(pib) ?? inlineBlip(inlinePic);
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
- [4, 1],
501
- [5, 0],
502
- [6, 1],
503
- [7, 1]
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.placeholder !== true && s.rectPt !== void 0);
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 && h > 0 && Math.abs(x1) < 5e3 && Math.abs(y1) < 5e3 ? {
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: raw === void 0 ? 90 : 90 - (raw | 0) / 65536,
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 paraRuns = style?.paraRuns ?? [];
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 paraIndex = 0;
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 = paraRuns[paraIndex];
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
- paraIndex++;
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) off += 2;
1081
- if ((mask & 8192) !== 0) off += 2;
1082
- if ((mask & 16384) !== 0) off += 2;
1083
- if ((mask & 256) !== 0) off += 2;
1084
- if ((mask & 1024) !== 0) off += 2;
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) {