reamkit 1.21.0 → 1.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/dist/esm/core/bytes.d.ts +22 -4
  2. package/dist/esm/core/bytes.js +73 -5
  3. package/dist/esm/core/converter/ream.d.ts +16 -0
  4. package/dist/esm/core/converter/ream.js +59 -9
  5. package/dist/esm/core/document-model/types.d.ts +78 -0
  6. package/dist/esm/core/drawingml/chart-geometry.js +11 -5
  7. package/dist/esm/core/drawingml/chart-parser.d.ts +2 -1
  8. package/dist/esm/core/drawingml/chart-parser.js +23 -1
  9. package/dist/esm/core/drawingml/colors.d.ts +30 -4
  10. package/dist/esm/core/drawingml/colors.js +45 -6
  11. package/dist/esm/core/drawingml/preset-geometry.js +236 -0
  12. package/dist/esm/core/drawingml/theme-parser.d.ts +38 -0
  13. package/dist/esm/core/drawingml/theme-parser.js +64 -1
  14. package/dist/esm/core/fonts/families.d.ts +10 -0
  15. package/dist/esm/core/fonts/families.js +44 -0
  16. package/dist/esm/core/fonts/index.d.ts +3 -2
  17. package/dist/esm/core/fonts/remote-fonts.d.ts +50 -0
  18. package/dist/esm/core/fonts/remote-fonts.js +114 -8
  19. package/dist/esm/core/fonts/scripts.d.ts +26 -0
  20. package/dist/esm/core/fonts/scripts.js +177 -0
  21. package/dist/esm/core/metafile/blit.d.ts +33 -0
  22. package/dist/esm/core/metafile/blit.js +67 -0
  23. package/dist/esm/core/metafile/dib.d.ts +61 -0
  24. package/dist/esm/core/metafile/dib.js +205 -0
  25. package/dist/esm/core/metafile/emf.js +139 -24
  26. package/dist/esm/core/metafile/picture.d.ts +12 -1
  27. package/dist/esm/core/metafile/symbol-fonts.d.ts +42 -0
  28. package/dist/esm/core/metafile/symbol-fonts.js +154 -0
  29. package/dist/esm/core/metafile/wmf.js +183 -10
  30. package/dist/esm/core/ole/escher-blip.d.ts +12 -0
  31. package/dist/esm/core/ole/escher-blip.js +88 -0
  32. package/dist/esm/{pdf-reader → core}/png-encode.js +1 -1
  33. package/dist/esm/core/vector.d.ts +5 -0
  34. package/dist/esm/excel/print-model.js +3 -1
  35. package/dist/esm/excel/sheet-shape-parser.js +1 -12
  36. package/dist/esm/excel/styles-parser.js +1 -1
  37. package/dist/esm/excel/xls/biff-reader.js +53 -18
  38. package/dist/esm/excel/xls/biff-styles.d.ts +5 -2
  39. package/dist/esm/excel/xls/biff-styles.js +9 -7
  40. package/dist/esm/excel/xls/escher.js +90 -24
  41. package/dist/esm/excel/xlsx-reader.d.ts +1 -1
  42. package/dist/esm/excel/xlsx-reader.js +3 -3
  43. package/dist/esm/layout/page-doc.d.ts +77 -0
  44. package/dist/esm/layout/page-doc.js +34 -18
  45. package/dist/esm/layout/styled-layout.d.ts +2 -2
  46. package/dist/esm/layout/styled-layout.js +467 -76
  47. package/dist/esm/pdf/shading.d.ts +19 -0
  48. package/dist/esm/pdf/shading.js +84 -11
  49. package/dist/esm/pdf/styled-page-emitter.js +647 -444
  50. package/dist/esm/pdf-reader/image-decode.js +1 -1
  51. package/dist/esm/pptx/embedded-fonts.d.ts +31 -0
  52. package/dist/esm/pptx/embedded-fonts.js +104 -0
  53. package/dist/esm/pptx/ole-preview.d.ts +10 -0
  54. package/dist/esm/pptx/ole-preview.js +52 -0
  55. package/dist/esm/pptx/placeholder-cascade.d.ts +136 -5
  56. package/dist/esm/pptx/placeholder-cascade.js +260 -27
  57. package/dist/esm/pptx/ppt/ppt-reader.js +120 -10
  58. package/dist/esm/pptx/ppt/ppt-text.d.ts +66 -1
  59. package/dist/esm/pptx/ppt/ppt-text.js +576 -137
  60. package/dist/esm/pptx/pptx-reader.d.ts +8 -0
  61. package/dist/esm/pptx/pptx-reader.js +271 -41
  62. package/dist/esm/pptx/preset-table-styles.d.ts +9 -0
  63. package/dist/esm/pptx/preset-table-styles.js +27 -0
  64. package/dist/esm/pptx/slide-parser.d.ts +78 -10
  65. package/dist/esm/pptx/slide-parser.js +396 -95
  66. package/dist/esm/pptx/sp-helpers.d.ts +2 -1
  67. package/dist/esm/pptx/sp-helpers.js +22 -6
  68. package/dist/esm/pptx/table-style.d.ts +64 -0
  69. package/dist/esm/pptx/table-style.js +203 -0
  70. package/dist/esm/svg/svg-writer.js +53 -5
  71. package/dist/esm/word/document-parser.d.ts +7 -1
  72. package/dist/esm/word/document-parser.js +6 -6
  73. package/dist/esm/word/docx-reader.js +8 -5
  74. package/dist/esm/word/docx-to-pdf.d.ts +8 -7
  75. package/dist/esm/word/docx-to-pdf.js +25 -7
  76. package/dist/esm/word/drawing-parser.d.ts +34 -0
  77. package/dist/esm/word/drawing-parser.js +173 -15
  78. package/dist/esm/word/numbering-parser.d.ts +2 -1
  79. package/dist/esm/word/numbering-parser.js +6 -6
  80. package/dist/esm/word/paragraph-properties.d.ts +2 -1
  81. package/dist/esm/word/paragraph-properties.js +2 -2
  82. package/dist/esm/word/run-properties.d.ts +5 -2
  83. package/dist/esm/word/run-properties.js +13 -8
  84. package/dist/esm/word/styles-parser.d.ts +2 -1
  85. package/dist/esm/word/styles-parser.js +12 -12
  86. package/dist/esm/word/table-parser.js +3 -3
  87. package/dist/esm/word/theme-fonts.d.ts +10 -0
  88. package/dist/esm/word/theme-fonts.js +28 -0
  89. package/package.json +1 -1
  90. /package/dist/esm/{pdf-reader → core}/png-encode.d.ts +0 -0
@@ -1,5 +1,8 @@
1
1
  import { PathBuilder } from "../vector.js";
2
2
  import { applyTransform, cloneDc, colorRef, newDeviceContext } from "./picture.js";
3
+ import { cropDib, fadedDib, readDib } from "./dib.js";
4
+ import { makeBlitter } from "./blit.js";
5
+ import { fromSymbolFont } from "./symbol-fonts.js";
3
6
  //#region src/core/metafile/emf.ts
4
7
  /** Whether the bytes open with an EMF header (MS-EMF §2.3.4.2: type 1 + " EMF"). */
5
8
  function isEmf(bytes) {
@@ -20,11 +23,6 @@ var RECORD_NAMES = new Map([
20
23
  [74, "region paint"],
21
24
  [78, "masked blit"],
22
25
  [79, "parallelogram blit"],
23
- [80, "device bitmap"],
24
- [81, "bitmap"],
25
- [76, "bitmap"],
26
- [77, "bitmap"],
27
- [114, "alpha blit"],
28
26
  [116, "transparent blit"],
29
27
  [118, "gradient fill"]
30
28
  ]);
@@ -73,6 +71,34 @@ function readEmf(bytes) {
73
71
  };
74
72
  let building;
75
73
  const open = () => building ?? [];
74
+ const blitter = makeBlitter((prim) => prims.push(prim));
75
+ /**
76
+ * §2.3.1 — one blit record: the source bitmap into the destination
77
+ * rectangle, both stated in the record's own fields. The rectangle is mapped
78
+ * the way every other coordinate here is, so the picture lands in the same
79
+ * device frame as the paths around it.
80
+ */
81
+ const blit = (o) => {
82
+ const dib = o.cbBmi > 0 ? readDib(bytes, off + o.bmiAt, {
83
+ bitsAt: off + o.bitsAt,
84
+ cbBits: o.cbBits,
85
+ ...o.blend?.perPixel === true ? { alpha: true } : {}
86
+ }) : void 0;
87
+ if (!dib) {
88
+ skipped.add("bitmap");
89
+ return;
90
+ }
91
+ const s = o.src;
92
+ const part = s && (s.x !== 0 || s.y !== 0 || s.w !== dib.width || s.h !== dib.height) ? cropDib(dib, s.x, o.inBitmap === true && dib.bottomUp === true ? dib.height - s.y - s.h : s.y, s.w, s.h) : dib;
93
+ const a = px(o.dest.x, o.dest.y);
94
+ const b = px(o.dest.x + o.dest.w, o.dest.y + o.dest.h);
95
+ blitter.blit(o.blend && o.blend.alpha < 255 ? fadedDib(part, o.blend.alpha / 255) : part, {
96
+ x: Math.min(a.x, b.x),
97
+ y: Math.min(a.y, b.y),
98
+ width: Math.abs(b.x - a.x),
99
+ height: Math.abs(b.y - a.y)
100
+ }, o.rop);
101
+ };
76
102
  const strokeOf = () => {
77
103
  if (dc.pen.style === "none") return void 0;
78
104
  const w = dc.pen.widthLu <= 1 ? 1 : dc.pen.widthLu * Math.abs(sx());
@@ -371,7 +397,7 @@ function readEmf(bytes) {
371
397
  const font = dc.font;
372
398
  prims.push({
373
399
  kind: "text",
374
- text,
400
+ text: fromSymbolFont(text, font?.family),
375
401
  x: p.x,
376
402
  y: p.y,
377
403
  alignH: dc.alignH,
@@ -387,25 +413,114 @@ function readEmf(bytes) {
387
413
  }
388
414
  break;
389
415
  case 76:
390
- {
391
- const cbBmiSrc = u(88);
392
- const rop = u(40);
393
- if (cbBmiSrc === 0) {
394
- const x = at(24);
395
- const y = at(28);
396
- const w = at(32);
397
- const h = at(36);
398
- const hex = rop === 66 ? "000000" : rop === 16711778 ? "FFFFFF" : void 0;
399
- const brush = dc.brush;
400
- if (hex !== void 0) dc.brush = {
401
- kind: "brush",
402
- colorHex: hex,
403
- hollow: false
404
- };
405
- paint(rectSegments(x, y, x + w, y + h), true, false);
406
- dc.brush = brush;
407
- } else skipped.add("bitmap");
416
+ case 77: {
417
+ const cbBmiSrc = u(88);
418
+ const rop = u(40);
419
+ const x = at(24);
420
+ const y = at(28);
421
+ const w = at(32);
422
+ const h = at(36);
423
+ if (cbBmiSrc === 0) {
424
+ const hex = rop === 66 ? "000000" : rop === 16711778 ? "FFFFFF" : void 0;
425
+ const brush = dc.brush;
426
+ if (hex !== void 0) dc.brush = {
427
+ kind: "brush",
428
+ colorHex: hex,
429
+ hollow: false
430
+ };
431
+ paint(rectSegments(x, y, x + w, y + h), true, false);
432
+ dc.brush = brush;
433
+ break;
408
434
  }
435
+ blit({
436
+ bmiAt: u(84),
437
+ cbBmi: cbBmiSrc,
438
+ bitsAt: u(92),
439
+ cbBits: u(96),
440
+ dest: {
441
+ x,
442
+ y,
443
+ w,
444
+ h
445
+ },
446
+ src: {
447
+ x: at(44),
448
+ y: at(48),
449
+ w: type === 77 ? at(100) : w,
450
+ h: type === 77 ? at(104) : h
451
+ },
452
+ rop
453
+ });
454
+ break;
455
+ }
456
+ case 80:
457
+ blit({
458
+ bmiAt: u(48),
459
+ cbBmi: u(52),
460
+ bitsAt: u(56),
461
+ cbBits: u(60),
462
+ dest: {
463
+ x: at(24),
464
+ y: at(28),
465
+ w: at(40),
466
+ h: at(44)
467
+ },
468
+ src: {
469
+ x: at(32),
470
+ y: at(36),
471
+ w: at(40),
472
+ h: at(44)
473
+ },
474
+ inBitmap: true,
475
+ rop: 13369376
476
+ });
477
+ break;
478
+ case 81:
479
+ blit({
480
+ bmiAt: u(48),
481
+ cbBmi: u(52),
482
+ bitsAt: u(56),
483
+ cbBits: u(60),
484
+ dest: {
485
+ x: at(24),
486
+ y: at(28),
487
+ w: at(72),
488
+ h: at(76)
489
+ },
490
+ src: {
491
+ x: at(32),
492
+ y: at(36),
493
+ w: at(40),
494
+ h: at(44)
495
+ },
496
+ inBitmap: true,
497
+ rop: u(68)
498
+ });
499
+ break;
500
+ case 114:
501
+ blit({
502
+ bmiAt: u(84),
503
+ cbBmi: u(88),
504
+ bitsAt: u(92),
505
+ cbBits: u(96),
506
+ dest: {
507
+ x: at(24),
508
+ y: at(28),
509
+ w: at(32),
510
+ h: at(36)
511
+ },
512
+ src: {
513
+ x: at(44),
514
+ y: at(48),
515
+ w: at(100),
516
+ h: at(104)
517
+ },
518
+ blend: {
519
+ alpha: u(40) >>> 16 & 255,
520
+ perPixel: u(40) >>> 24 === 1
521
+ },
522
+ rop: 13369376
523
+ });
409
524
  break;
410
525
  default:
411
526
  {
@@ -74,7 +74,18 @@ export interface MetaFont {
74
74
  readonly italic?: boolean;
75
75
  readonly escapement?: number;
76
76
  }
77
- export type MetaObject = MetaPen | MetaBrush | MetaFont;
77
+ /**
78
+ * An object the reader does not model — a palette, a region, a pattern brush.
79
+ * It still OCCUPIES a slot in the metafile's object table: handles are indices
80
+ * into that table, so a creation the reader passed over silently shifted every
81
+ * one that followed. 23884's chart selects its coloured pens by handle, and one
82
+ * unrecorded palette put every selection one slot back — on the black pen the
83
+ * pens had displaced.
84
+ */
85
+ export interface MetaOther {
86
+ readonly kind: 'other';
87
+ }
88
+ export type MetaObject = MetaPen | MetaBrush | MetaFont | MetaOther;
78
89
  /** The device context: everything a record draws THROUGH. */
79
90
  export interface DeviceContext {
80
91
  pen: MetaPen;
@@ -0,0 +1,42 @@
1
+ import { VectorPath } from '../vector.js';
2
+ /**
3
+ * The geometry a symbol character DRAWS, for the handful that are plain shapes.
4
+ *
5
+ * Translating them to Unicode is not enough on its own: the substitute font a
6
+ * document renders with has no `●` either, and a missing glyph is a box with
7
+ * its code number in it — which is what 45541_Footer's table filled sixty-eight
8
+ * cells with. A shape this simple is better drawn than spelled.
9
+ *
10
+ * Measured off the fonts themselves: both fill the em square, one unit wide,
11
+ * from 0.2 em below the baseline to 0.8 em above it.
12
+ */
13
+ export type SymbolGeometry = 'circle' | 'square' | 'diamond';
14
+ /**
15
+ * The shapes a symbol string draws, one per character, or `undefined` when any
16
+ * character is not one of them — a string that mixes a pictogram with a circle
17
+ * stays text, which is the safer half of the trade.
18
+ *
19
+ * @param text The string as stored.
20
+ * @param family The typeface it is drawn with.
21
+ */
22
+ export declare function symbolGeometryOf(text: string, family: string | undefined): Array<SymbolGeometry> | undefined;
23
+ /**
24
+ * The outline of one symbol in a box, as the path segments a picture draws.
25
+ *
26
+ * @param shape Which symbol.
27
+ * @param left The box's left edge, in the metafile's logical units.
28
+ * @param top Its top edge (y grows DOWN, as a metafile has it).
29
+ * @param size The em, which is both the width and the height of the box.
30
+ * @returns The closed outline.
31
+ */
32
+ export declare function symbolOutline(shape: SymbolGeometry, left: number, top: number, size: number): VectorPath;
33
+ /** Whether a typeface's characters are symbols rather than letters. */
34
+ export declare function isSymbolFont(family: string | undefined): boolean;
35
+ /**
36
+ * Translate a string drawn in a symbol font to the Unicode that means the same.
37
+ *
38
+ * @param text The string as the metafile stored it.
39
+ * @param family The typeface it was drawn with.
40
+ * @returns The translated string; characters with no counterpart are kept.
41
+ */
42
+ export declare function fromSymbolFont(text: string, family: string | undefined): string;
@@ -0,0 +1,154 @@
1
+ import { PathBuilder } from "../vector.js";
2
+ //#region src/core/metafile/symbol-fonts.ts
3
+ /** Half a circle's control-point distance for a cubic quarter-arc. */
4
+ var KAPPA = .5522847498307936;
5
+ /**
6
+ * The shapes a symbol string draws, one per character, or `undefined` when any
7
+ * character is not one of them — a string that mixes a pictogram with a circle
8
+ * stays text, which is the safer half of the trade.
9
+ *
10
+ * @param text The string as stored.
11
+ * @param family The typeface it is drawn with.
12
+ */
13
+ function symbolGeometryOf(text, family) {
14
+ const table = family === void 0 ? void 0 : GEOMETRY_TABLES.get(normalize(family));
15
+ if (!table || text.length === 0) return void 0;
16
+ const out = [];
17
+ for (const ch of text) {
18
+ const code = ch.codePointAt(0) ?? 0;
19
+ const key = code >= 61440 && code <= 61695 ? code - 61440 : code;
20
+ const shape = table.get(key);
21
+ if (shape === void 0) return void 0;
22
+ out.push(shape);
23
+ }
24
+ return out;
25
+ }
26
+ /**
27
+ * The outline of one symbol in a box, as the path segments a picture draws.
28
+ *
29
+ * @param shape Which symbol.
30
+ * @param left The box's left edge, in the metafile's logical units.
31
+ * @param top Its top edge (y grows DOWN, as a metafile has it).
32
+ * @param size The em, which is both the width and the height of the box.
33
+ * @returns The closed outline.
34
+ */
35
+ function symbolOutline(shape, left, top, size) {
36
+ const b = new PathBuilder();
37
+ const r = size / 2;
38
+ const [cx, cy] = [left + r, top + r];
39
+ if (shape === "circle") {
40
+ const k = r * KAPPA;
41
+ return b.moveTo(cx, top).cubicTo(cx + k, top, left + size, cy - k, left + size, cy).cubicTo(left + size, cy + k, cx + k, top + size, cx, top + size).cubicTo(cx - k, top + size, left, cy + k, left, cy).cubicTo(left, cy - k, cx - k, top, cx, top).close().build();
42
+ }
43
+ if (shape === "diamond") return b.moveTo(cx, top).lineTo(left + size, cy).lineTo(cx, top + size).lineTo(left, cy).close().build();
44
+ return b.moveTo(left, top).lineTo(left + size, top).lineTo(left + size, top + size).lineTo(left, top + size).close().build();
45
+ }
46
+ /**
47
+ * Translate a string drawn in a symbol font to the Unicode that means the same.
48
+ *
49
+ * @param text The string as the metafile stored it.
50
+ * @param family The typeface it was drawn with.
51
+ * @returns The translated string; characters with no counterpart are kept.
52
+ */
53
+ function fromSymbolFont(text, family) {
54
+ const table = family === void 0 ? void 0 : SYMBOL_TABLES.get(normalize(family));
55
+ if (!table) return text;
56
+ let out = "";
57
+ for (const ch of text) {
58
+ const code = ch.codePointAt(0) ?? 0;
59
+ const key = code >= 61440 && code <= 61695 ? code - 61440 : code;
60
+ out += table.get(key) ?? ch;
61
+ }
62
+ return out;
63
+ }
64
+ function normalize(family) {
65
+ return family.trim().toLowerCase();
66
+ }
67
+ var WINGDINGS = new Map([
68
+ [108, "●"],
69
+ [109, "❍"],
70
+ [110, "■"],
71
+ [111, "□"],
72
+ [112, "❑"],
73
+ [113, "❒"],
74
+ [114, "▪"],
75
+ [115, "▪"],
76
+ [116, "▫"],
77
+ [117, "◆"],
78
+ [118, "❖"],
79
+ [119, "◇"],
80
+ [120, "✗"],
81
+ [159, "•"],
82
+ [167, "▪"],
83
+ [252, "✔"],
84
+ [253, "✘"],
85
+ [254, "☑"],
86
+ [255, "☒"]
87
+ ]);
88
+ var WEBDINGS = new Map([
89
+ [103, "■"],
90
+ [108, "■"],
91
+ [110, "●"],
92
+ [111, "○"],
93
+ [112, "□"],
94
+ [114, "◆"],
95
+ [115, "▲"],
96
+ [116, "▼"],
97
+ [161, "●"]
98
+ ]);
99
+ var SYMBOL = new Map([
100
+ [97, "α"],
101
+ [98, "β"],
102
+ [99, "χ"],
103
+ [100, "δ"],
104
+ [101, "ε"],
105
+ [102, "φ"],
106
+ [103, "γ"],
107
+ [104, "η"],
108
+ [108, "λ"],
109
+ [109, "μ"],
110
+ [112, "π"],
111
+ [114, "ρ"],
112
+ [115, "σ"],
113
+ [116, "τ"],
114
+ [119, "ω"],
115
+ [183, "•"],
116
+ [215, "×"],
117
+ [177, "±"],
118
+ [163, "≤"],
119
+ [179, "≥"],
120
+ [185, "≠"],
121
+ [187, "≈"]
122
+ ]);
123
+ var WINGDINGS_GEOMETRY = new Map([
124
+ [108, "circle"],
125
+ [110, "square"],
126
+ [111, "square"],
127
+ [114, "square"],
128
+ [115, "square"],
129
+ [117, "diamond"],
130
+ [119, "diamond"]
131
+ ]);
132
+ var GEOMETRY_TABLES = new Map([
133
+ ["wingdings", WINGDINGS_GEOMETRY],
134
+ ["wingdings 2", WINGDINGS_GEOMETRY],
135
+ ["wingdings 3", WINGDINGS_GEOMETRY],
136
+ ["webdings", new Map([
137
+ [103, "square"],
138
+ [108, "square"],
139
+ [110, "circle"]
140
+ ])],
141
+ ["monotype sorts", WINGDINGS_GEOMETRY],
142
+ ["zapfdingbats", WINGDINGS_GEOMETRY]
143
+ ]);
144
+ var SYMBOL_TABLES = new Map([
145
+ ["wingdings", WINGDINGS],
146
+ ["wingdings 2", WINGDINGS],
147
+ ["wingdings 3", WINGDINGS],
148
+ ["webdings", WEBDINGS],
149
+ ["symbol", SYMBOL],
150
+ ["monotype sorts", WINGDINGS],
151
+ ["zapfdingbats", WINGDINGS]
152
+ ]);
153
+ //#endregion
154
+ export { fromSymbolFont, symbolGeometryOf, symbolOutline };
@@ -1,5 +1,8 @@
1
1
  import { PathBuilder } from "../vector.js";
2
2
  import { cloneDc, colorRef, newDeviceContext } from "./picture.js";
3
+ import { cropDib, readDib } from "./dib.js";
4
+ import { makeBlitter } from "./blit.js";
5
+ import { fromSymbolFont, symbolGeometryOf, symbolOutline } from "./symbol-fonts.js";
3
6
  import { ellipseSegments } from "./emf.js";
4
7
  //#region src/core/metafile/wmf.ts
5
8
  var PLACEABLE_KEY = 2596720087;
@@ -13,9 +16,6 @@ function isWmf(bytes) {
13
16
  return (type === 1 || type === 2) && headerWords === 9;
14
17
  }
15
18
  var RECORD_NAMES = new Map([
16
- [2368, "bitmap"],
17
- [2881, "bitmap"],
18
- [3907, "bitmap"],
19
19
  [2338, "pie"],
20
20
  [2096, "chord"],
21
21
  [2071, "arc"],
@@ -60,6 +60,16 @@ function readWmf(bytes) {
60
60
  cx: 0,
61
61
  cy: 0
62
62
  };
63
+ let origin;
64
+ const emit = (prim) => {
65
+ origin ??= {
66
+ x: win.x,
67
+ y: win.y
68
+ };
69
+ const dx = origin.x - win.x;
70
+ const dy = origin.y - win.y;
71
+ prims.push(dx === 0 && dy === 0 ? prim : shiftPrim(prim, dx, dy));
72
+ };
63
73
  const strokeOf = () => {
64
74
  if (dc.pen.style === "none") return void 0;
65
75
  const w = Math.max(1, dc.pen.widthLu);
@@ -78,7 +88,7 @@ function readWmf(bytes) {
78
88
  const st = stroke ? strokeOf() : void 0;
79
89
  const fillHex = fill && !dc.brush.hollow ? dc.brush.colorHex : void 0;
80
90
  if (fillHex === void 0 && !st) return;
81
- prims.push({
91
+ emit({
82
92
  kind: "path",
83
93
  paths: [path],
84
94
  ...fillHex !== void 0 ? { fillColorHex: fillHex } : {},
@@ -86,6 +96,27 @@ function readWmf(bytes) {
86
96
  });
87
97
  };
88
98
  const rectSegments = (l, t, r, b) => new PathBuilder().moveTo(l, t).lineTo(r, t).lineTo(r, b).lineTo(l, b).close().build().segments;
99
+ const blitter = makeBlitter(emit);
100
+ /**
101
+ * §2.3.1 — one blit record: the packed bitmap the record ends with, into the
102
+ * destination rectangle its fields name. A WMF's coordinates are its own
103
+ * logical units, which is the frame the primitives are already in.
104
+ */
105
+ const blit = (o) => {
106
+ const dib = readDib(bytes, off + o.dibAt);
107
+ if (!dib) {
108
+ skipped.add("bitmap");
109
+ return;
110
+ }
111
+ const s = o.src;
112
+ const part = s.x !== 0 || s.y !== 0 || s.w !== dib.width || s.h !== dib.height ? cropDib(dib, s.x, dib.bottomUp === true ? dib.height - s.y - s.h : s.y, s.w, s.h) : dib;
113
+ blitter.blit(part, {
114
+ x: Math.min(o.dest.x, o.dest.x + o.dest.w),
115
+ y: Math.min(o.dest.y, o.dest.y + o.dest.h),
116
+ width: Math.abs(o.dest.w),
117
+ height: Math.abs(o.dest.h)
118
+ }, o.rop);
119
+ };
89
120
  let off = placeable ? 22 : 0;
90
121
  off += 18;
91
122
  let guard = 0;
@@ -134,6 +165,12 @@ function readWmf(bytes) {
134
165
  case 496:
135
166
  objects[pu(0)] = void 0;
136
167
  break;
168
+ case 247:
169
+ case 322:
170
+ case 505:
171
+ case 1791:
172
+ put({ kind: "other" });
173
+ break;
137
174
  case 762:
138
175
  put({
139
176
  kind: "pen",
@@ -222,6 +259,79 @@ function readWmf(bytes) {
222
259
  dc.brush = brush;
223
260
  }
224
261
  break;
262
+ case 2368:
263
+ case 2881:
264
+ case 3907:
265
+ {
266
+ const rop = p32(0);
267
+ const f = fn === 2368 ? {
268
+ dib: 22,
269
+ srcH: 4,
270
+ srcW: 5,
271
+ ySrc: 2,
272
+ xSrc: 3,
273
+ h: 4,
274
+ w: 5,
275
+ y: 6,
276
+ x: 7
277
+ } : fn === 2881 ? {
278
+ dib: 26,
279
+ srcH: 2,
280
+ srcW: 3,
281
+ ySrc: 4,
282
+ xSrc: 5,
283
+ h: 6,
284
+ w: 7,
285
+ y: 8,
286
+ x: 9
287
+ } : {
288
+ dib: 28,
289
+ srcH: 3,
290
+ srcW: 4,
291
+ ySrc: 5,
292
+ xSrc: 6,
293
+ h: 7,
294
+ w: 8,
295
+ y: 9,
296
+ x: 10
297
+ };
298
+ if (f.dib + 12 > size * 2) {
299
+ const hex = rop === 66 ? "000000" : rop === 16711778 ? "FFFFFF" : void 0;
300
+ const brush = dc.brush;
301
+ if (hex !== void 0) dc.brush = {
302
+ kind: "brush",
303
+ colorHex: hex,
304
+ hollow: false
305
+ };
306
+ const shift = fn === 2368 ? 1 : 0;
307
+ const [h, w, y, x] = [
308
+ p(f.h + shift),
309
+ p(f.w + shift),
310
+ p(f.y + shift),
311
+ p(f.x + shift)
312
+ ];
313
+ paint(rectSegments(x, y, x + w, y + h), true, false);
314
+ dc.brush = brush;
315
+ break;
316
+ }
317
+ blit({
318
+ dibAt: f.dib,
319
+ dest: {
320
+ x: p(f.x),
321
+ y: p(f.y),
322
+ w: p(f.w),
323
+ h: p(f.h)
324
+ },
325
+ src: {
326
+ x: p(f.xSrc),
327
+ y: p(f.ySrc),
328
+ w: p(f.srcW),
329
+ h: p(f.srcH)
330
+ },
331
+ rop
332
+ });
333
+ }
334
+ break;
225
335
  case 804:
226
336
  case 805:
227
337
  {
@@ -250,7 +360,7 @@ function readWmf(bytes) {
250
360
  const count = pu(0);
251
361
  const text = readString(bytes, off + 8, count, false);
252
362
  const words = count + 1 >> 1;
253
- pushText(prims, dc, text, p(1 + words + 1), p(1 + words));
363
+ pushText(emit, dc, text, p(1 + words + 1), p(1 + words));
254
364
  }
255
365
  break;
256
366
  case 2610:
@@ -260,7 +370,7 @@ function readWmf(bytes) {
260
370
  const count = pu(2);
261
371
  const hasRect = (pu(3) & 6) !== 0;
262
372
  const text = readString(bytes, off + 6 + (4 + (hasRect ? 4 : 0)) * 2, count, false);
263
- pushText(prims, dc, text, x, y);
373
+ pushText(emit, dc, text, x, y);
264
374
  }
265
375
  break;
266
376
  default:
@@ -287,16 +397,22 @@ function readWmf(bytes) {
287
397
  skipped: [...skipped]
288
398
  };
289
399
  }
290
- function pushText(prims, dc, text, x, y) {
400
+ function pushText(emit, dc, text, x, y) {
291
401
  if (text.trim() === "") return;
292
- prims.push({
402
+ const em = Math.abs(dc.font?.heightLu ?? 12);
403
+ const drawn = symbolPrims(text, dc, x, y, em);
404
+ if (drawn) {
405
+ for (const prim of drawn) emit(prim);
406
+ return;
407
+ }
408
+ emit({
293
409
  kind: "text",
294
- text,
410
+ text: fromSymbolFont(text, dc.font?.family),
295
411
  x,
296
412
  y,
297
413
  alignH: dc.alignH,
298
414
  alignBaseline: dc.alignBaseline,
299
- sizeLu: Math.abs(dc.font?.heightLu ?? 12),
415
+ sizeLu: em,
300
416
  colorHex: dc.textColorHex,
301
417
  ...dc.font?.family ? { fontFamily: dc.font.family } : {},
302
418
  ...dc.font?.bold ? { bold: true } : {},
@@ -304,6 +420,37 @@ function pushText(prims, dc, text, x, y) {
304
420
  ...dc.font?.escapement ? { escapement: dc.font.escapement } : {}
305
421
  });
306
422
  }
423
+ function shiftPrim(prim, dx, dy) {
424
+ if (prim.kind === "text") return {
425
+ ...prim,
426
+ x: prim.x + dx,
427
+ y: prim.y + dy
428
+ };
429
+ if (prim.kind === "image") return {
430
+ ...prim,
431
+ x: prim.x + dx,
432
+ y: prim.y + dy
433
+ };
434
+ return {
435
+ ...prim,
436
+ paths: prim.paths.map((path) => ({
437
+ ...path,
438
+ segments: path.segments.map((sg) => "x" in sg ? {
439
+ ...sg,
440
+ x: sg.x + dx,
441
+ y: sg.y + dy,
442
+ ..."x1" in sg ? {
443
+ x1: sg.x1 + dx,
444
+ y1: sg.y1 + dy
445
+ } : {},
446
+ ..."x2" in sg ? {
447
+ x2: sg.x2 + dx,
448
+ y2: sg.y2 + dy
449
+ } : {}
450
+ } : sg)
451
+ }))
452
+ };
453
+ }
307
454
  function penStyle(style) {
308
455
  switch (style & 15) {
309
456
  case 1: return "dash";
@@ -367,5 +514,31 @@ function polySegments(pts, closed) {
367
514
  if (closed) segs.push({ op: "close" });
368
515
  return segs;
369
516
  }
517
+ /**
518
+ * The symbols a text record draws, as filled outlines, or `undefined` when the
519
+ * string is not one the shapes cover.
520
+ *
521
+ * Both fonts advance one em per symbol and fill that em, sitting from 0.2 em
522
+ * below the baseline to 0.8 em above it — measured off Webdings itself.
523
+ *
524
+ * @param text The string as stored.
525
+ * @param dc The device context (font, colour, alignment).
526
+ * @param x The reference point's x, in logical units.
527
+ * @param y Its y — the text's top unless the context says baseline.
528
+ * @param em The font's height in logical units.
529
+ * @returns One filled path per character, or `undefined`.
530
+ */
531
+ function symbolPrims(text, dc, x, y, em) {
532
+ const shapes = symbolGeometryOf(text, dc.font?.family);
533
+ if (!shapes) return void 0;
534
+ const width = em * shapes.length;
535
+ const left = dc.alignH === "center" ? x - width / 2 : dc.alignH === "right" ? x - width : x;
536
+ const top = dc.alignBaseline ? y - em * .8 : y;
537
+ return shapes.map((shape, i) => ({
538
+ kind: "path",
539
+ paths: [symbolOutline(shape, left + i * em, top, em)],
540
+ fillColorHex: dc.textColorHex
541
+ }));
542
+ }
370
543
  //#endregion
371
544
  export { isWmf, readWmf };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The picture bytes inside one Escher BLIP record — a raster as it stands, or a
3
+ * metafile unpacked out of its header.
4
+ *
5
+ * @param type The blip record type (`0xF01A`…`0xF02A`).
6
+ * @param instance The record's `recInstance`, which says how many UIDs precede
7
+ * the payload.
8
+ * @param data The record's body, without the 8-byte header.
9
+ * @returns The bytes to hand the renderer, or `undefined` when the blip holds
10
+ * nothing this can read.
11
+ */
12
+ export declare function readEscherBlip(type: number, instance: number, data: Uint8Array): Uint8Array | undefined;