reamkit 1.20.0 → 1.22.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 (41) 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/crypto/offcrypto.d.ts +1 -0
  4. package/dist/esm/core/crypto/offcrypto.js +2 -1
  5. package/dist/esm/core/document-model/types.d.ts +60 -0
  6. package/dist/esm/core/drawingml/chart-parser.d.ts +2 -1
  7. package/dist/esm/core/drawingml/chart-parser.js +23 -1
  8. package/dist/esm/core/drawingml/colors.d.ts +27 -3
  9. package/dist/esm/core/drawingml/colors.js +30 -6
  10. package/dist/esm/core/drawingml/preset-geometry.js +236 -0
  11. package/dist/esm/core/metafile/emf.js +2 -1
  12. package/dist/esm/core/metafile/symbol-fonts.d.ts +42 -0
  13. package/dist/esm/core/metafile/symbol-fonts.js +154 -0
  14. package/dist/esm/core/metafile/wmf.js +35 -2
  15. package/dist/esm/core/vector.d.ts +5 -0
  16. package/dist/esm/excel/sheet-shape-parser.js +1 -12
  17. package/dist/esm/excel/xlsx-reader.d.ts +1 -1
  18. package/dist/esm/excel/xlsx-reader.js +3 -3
  19. package/dist/esm/index.d.ts +1 -0
  20. package/dist/esm/index.js +2 -1
  21. package/dist/esm/layout/page-doc.d.ts +38 -0
  22. package/dist/esm/layout/page-doc.js +34 -18
  23. package/dist/esm/layout/styled-layout.js +174 -49
  24. package/dist/esm/pdf/shading.d.ts +19 -0
  25. package/dist/esm/pdf/shading.js +84 -11
  26. package/dist/esm/pdf/styled-page-emitter.js +614 -444
  27. package/dist/esm/pptx/ole-preview.d.ts +10 -0
  28. package/dist/esm/pptx/ole-preview.js +52 -0
  29. package/dist/esm/pptx/placeholder-cascade.d.ts +69 -5
  30. package/dist/esm/pptx/placeholder-cascade.js +162 -25
  31. package/dist/esm/pptx/pptx-reader.js +247 -39
  32. package/dist/esm/pptx/slide-parser.d.ts +72 -10
  33. package/dist/esm/pptx/slide-parser.js +310 -82
  34. package/dist/esm/pptx/sp-helpers.js +19 -4
  35. package/dist/esm/pptx/table-style.d.ts +56 -0
  36. package/dist/esm/pptx/table-style.js +161 -0
  37. package/dist/esm/svg/svg-writer.js +53 -5
  38. package/dist/esm/word/docx-reader.js +2 -2
  39. package/dist/esm/word/drawing-parser.d.ts +34 -0
  40. package/dist/esm/word/drawing-parser.js +159 -13
  41. package/package.json +1 -1
@@ -117,6 +117,242 @@ function presetPaths(preset, w, h, adjust) {
117
117
  case "leftArrow": return [blockArrow(w, h, adjust, "left")];
118
118
  case "upArrow": return [blockArrow(w, h, adjust, "up")];
119
119
  case "downArrow": return [blockArrow(w, h, adjust, "down")];
120
+ case "leftRightArrow": {
121
+ const ss = Math.min(w, h);
122
+ const t = clamp(frac(adjust, "adj1", 5e4), 0, 1) * ss;
123
+ const hl = clamp(frac(adjust, "adj2", 5e4), 0, 1) * ss;
124
+ const [sb, st] = [(h - t) / 2, (h + t) / 2];
125
+ return [polygon([
126
+ [0, h / 2],
127
+ [hl, h],
128
+ [hl, st],
129
+ [w - hl, st],
130
+ [w - hl, h],
131
+ [w, h / 2],
132
+ [w - hl, 0],
133
+ [w - hl, sb],
134
+ [hl, sb],
135
+ [hl, 0]
136
+ ])];
137
+ }
138
+ case "notchedRightArrow": {
139
+ const ss = Math.min(w, h);
140
+ const t = clamp(frac(adjust, "adj1", 5e4), 0, 1) * ss;
141
+ const hl = clamp(frac(adjust, "adj2", 5e4), 0, 1) * ss;
142
+ const [sb, st] = [(h - t) / 2, (h + t) / 2];
143
+ const notch = Math.min(hl, w / 2);
144
+ return [polygon([
145
+ [0, sb],
146
+ [w - hl, sb],
147
+ [w - hl, 0],
148
+ [w, h / 2],
149
+ [w - hl, h],
150
+ [w - hl, st],
151
+ [0, st],
152
+ [notch, h / 2]
153
+ ])];
154
+ }
155
+ case "stripedRightArrow": {
156
+ const ss = Math.min(w, h);
157
+ const t = clamp(frac(adjust, "adj1", 5e4), 0, 1) * ss;
158
+ const hl = clamp(frac(adjust, "adj2", 5e4), 0, 1) * ss;
159
+ const [sb, st] = [(h - t) / 2, (h + t) / 2];
160
+ const unit = ss / 12;
161
+ return [
162
+ polygon([
163
+ [0, sb],
164
+ [unit, sb],
165
+ [unit, st],
166
+ [0, st]
167
+ ]),
168
+ polygon([
169
+ [unit * 2, sb],
170
+ [unit * 4, sb],
171
+ [unit * 4, st],
172
+ [unit * 2, st]
173
+ ]),
174
+ polygon([
175
+ [unit * 5, sb],
176
+ [w - hl, sb],
177
+ [w - hl, 0],
178
+ [w, h / 2],
179
+ [w - hl, h],
180
+ [w - hl, st],
181
+ [unit * 5, st]
182
+ ])
183
+ ];
184
+ }
185
+ case "bentUpArrow": {
186
+ const ss = Math.min(w, h);
187
+ const t = clamp(frac(adjust, "adj2", 25e3), 0, 1) * ss;
188
+ const hw = clamp(frac(adjust, "adj3", 25e3), 0, 1) * ss * 2;
189
+ const hh = Math.min(hw, h);
190
+ const cx = w - hw / 2;
191
+ return [polygon([
192
+ [0, 0],
193
+ [cx + t / 2, 0],
194
+ [cx + t / 2, h - hh],
195
+ [w, h - hh],
196
+ [cx, h],
197
+ [cx - hw / 2, h - hh],
198
+ [cx - t / 2, h - hh],
199
+ [cx - t / 2, t],
200
+ [0, t]
201
+ ])];
202
+ }
203
+ case "chevron":
204
+ case "homePlate": {
205
+ const ss = Math.min(w, h);
206
+ const x = Math.min(clamp(frac(adjust, "adj", 5e4), 0, 1) * ss, w);
207
+ const points = [
208
+ [0, 0],
209
+ [w - x, 0],
210
+ [w, h / 2],
211
+ [w - x, h],
212
+ [0, h]
213
+ ];
214
+ if (preset === "chevron") points.push([x, h / 2]);
215
+ return [polygon(points)];
216
+ }
217
+ case "corner": {
218
+ const ss = Math.min(w, h);
219
+ const th = clamp(frac(adjust, "adj1", 5e4), 0, 1) * ss;
220
+ const tw = clamp(frac(adjust, "adj2", 5e4), 0, 1) * ss;
221
+ return [polygon([
222
+ [0, 0],
223
+ [w, 0],
224
+ [w, th],
225
+ [tw, th],
226
+ [tw, h],
227
+ [0, h]
228
+ ])];
229
+ }
230
+ case "pie":
231
+ case "blockArc": {
232
+ const [rx, ry] = [w / 2, h / 2];
233
+ const start = -((adjust.get("adj1") ?? 0) / 6e4) * (Math.PI / 180);
234
+ let sweep = -((adjust.get("adj2") ?? (preset === "pie" ? 162e5 : 108e5)) / 6e4) * (Math.PI / 180) - start;
235
+ if (sweep > 0) sweep -= 2 * Math.PI;
236
+ const at = (a, r) => [rx + rx * r * Math.cos(a), ry + ry * r * Math.sin(a)];
237
+ const b = new PathBuilder();
238
+ const [sx0, sy0] = at(start, 1);
239
+ b.moveTo(sx0, sy0).append(arcToBeziers(rx, ry, rx, ry, start, sweep));
240
+ if (preset === "pie") return [b.lineTo(rx, ry).close().build()];
241
+ const inner = 1 - clamp(frac(adjust, "adj3", 25e3), 0, 1);
242
+ const [ix, iy] = at(start + sweep, inner);
243
+ return [b.lineTo(ix, iy).append(arcToBeziers(rx, ry, rx * inner, ry * inner, start + sweep, -sweep)).close().build()];
244
+ }
245
+ case "gear6":
246
+ case "gear9": {
247
+ const teeth = preset === "gear6" ? 6 : 9;
248
+ const ss = Math.min(w, h);
249
+ const depth = clamp(frac(adjust, "adj1", 15e3), 0, .4) * ss;
250
+ const [cx, cy] = [w / 2, h / 2];
251
+ const outer = Math.min(w, h) / 2;
252
+ const root = outer - depth;
253
+ const half = Math.PI / teeth / 2.6;
254
+ const pts = [];
255
+ for (let i = 0; i < teeth; i++) {
256
+ const a = i * 2 * Math.PI / teeth;
257
+ const gap = Math.PI / teeth;
258
+ pts.push([cx + root * Math.cos(a - gap + half), cy + root * Math.sin(a - gap + half)]);
259
+ pts.push([cx + outer * Math.cos(a - half), cy + outer * Math.sin(a - half)]);
260
+ pts.push([cx + outer * Math.cos(a + half), cy + outer * Math.sin(a + half)]);
261
+ pts.push([cx + root * Math.cos(a + gap - half), cy + root * Math.sin(a + gap - half)]);
262
+ }
263
+ return [polygon(pts)];
264
+ }
265
+ case "circularArrow":
266
+ case "leftCircularArrow": {
267
+ const ss = Math.min(w, h);
268
+ const t = clamp(frac(adjust, "adj1", 12500), .02, .45) * ss;
269
+ const [rx, ry] = [Math.max(t, w / 2 - t), Math.max(t, h / 2 - t)];
270
+ const [cx, cy] = [w / 2, h / 2];
271
+ const deg = (v) => -(v / 6e4) * (Math.PI / 180);
272
+ const tail = deg(adjust.get("adj3") ?? (preset === "circularArrow" ? 20457681 : 125e5));
273
+ let sweep = deg(adjust.get("adj2") ?? (preset === "circularArrow" ? 1142319 : 1142319)) - tail;
274
+ const turn = preset === "circularArrow" ? -1 : 1;
275
+ while (sweep * turn < 0) sweep += turn * 2 * Math.PI;
276
+ while (Math.abs(sweep) > 2 * Math.PI) sweep -= turn * 2 * Math.PI;
277
+ const headSweep = turn * Math.min(Math.abs(sweep) / 3, .35);
278
+ const bandEnd = tail + sweep - headSweep;
279
+ const at = (a, r) => [clamp(cx + (rx + r) * Math.cos(a), 0, w), clamp(cy + (ry + r) * Math.sin(a), 0, h)];
280
+ const b = new PathBuilder();
281
+ const [ox, oy] = at(tail, t / 2);
282
+ b.moveTo(ox, oy).append(arcToBeziers(cx, cy, rx + t / 2, ry + t / 2, tail, bandEnd - tail).map((seg) => seg.op === "cubic" ? {
283
+ op: "cubic",
284
+ x1: clamp(seg.x1, 0, w),
285
+ y1: clamp(seg.y1, 0, h),
286
+ x2: clamp(seg.x2, 0, w),
287
+ y2: clamp(seg.y2, 0, h),
288
+ x: clamp(seg.x, 0, w),
289
+ y: clamp(seg.y, 0, h)
290
+ } : seg));
291
+ const [hx1, hy1] = at(bandEnd, t);
292
+ const [tipX, tipY] = at(tail + sweep, 0);
293
+ const [hx2, hy2] = at(bandEnd, -t);
294
+ const [ix, iy] = at(bandEnd, -t / 2);
295
+ return [b.lineTo(hx1, hy1).lineTo(tipX, tipY).lineTo(hx2, hy2).lineTo(ix, iy).append(arcToBeziers(cx, cy, rx - t / 2, ry - t / 2, bandEnd, tail - bandEnd)).close().build()];
296
+ }
297
+ case "actionButtonBlank": return [{ segments: roundRectSegments(w, h, Math.min(w, h) * .1) }];
298
+ case "flowChartMerge": return [polygon([
299
+ [0, h],
300
+ [w, h],
301
+ [w / 2, 0]
302
+ ])];
303
+ case "flowChartExtract": return [polygon([
304
+ [0, 0],
305
+ [w, 0],
306
+ [w / 2, h]
307
+ ])];
308
+ case "flowChartDelay": return [new PathBuilder().moveTo(0, 0).lineTo(w / 2, 0).append(arcToBeziers(w / 2, h / 2, w / 2, h / 2, -Math.PI / 2, Math.PI)).lineTo(0, h).close().build()];
309
+ case "flowChartMagneticDisk": {
310
+ const ry = h / 6;
311
+ return [new PathBuilder().moveTo(0, h - ry).append(arcToBeziers(w / 2, h - ry, w / 2, ry, Math.PI, -2 * Math.PI)).moveTo(0, h - ry).lineTo(0, ry).append(arcToBeziers(w / 2, ry, w / 2, ry, Math.PI, -Math.PI)).lineTo(w, h - ry).build()];
312
+ }
313
+ case "flowChartMagneticTape": {
314
+ const foot = h / 8;
315
+ return [{ segments: ellipseSegments(w, h) }, polygon([
316
+ [w / 2, 0],
317
+ [w, 0],
318
+ [w, foot],
319
+ [w / 2, foot]
320
+ ])];
321
+ }
322
+ case "flowChartPunchedTape": {
323
+ const wave = h / 5;
324
+ return [new PathBuilder().moveTo(0, wave).cubicTo(w / 4, wave * 2, w * 3 / 4, 0, w, wave).lineTo(w, h - wave).cubicTo(w * 3 / 4, h - wave * 2, w / 4, h, 0, h - wave).close().build()];
325
+ }
326
+ case "wedgeRoundRectCallout": {
327
+ const r = clamp(frac(adjust, "adj3", 16667), 0, .5) * Math.min(w, h);
328
+ const tipX = w / 2 + frac(adjust, "adj1", -20833) * w;
329
+ const tipY = h / 2 - frac(adjust, "adj2", 62500) * h;
330
+ const [cx, cy] = [w / 2, h / 2];
331
+ const len = Math.hypot(tipX - cx, tipY - cy) || 1;
332
+ const [ux, uy] = [(tipX - cx) / len, (tipY - cy) / len];
333
+ const base = Math.min(w, h) / 6;
334
+ return [{ segments: roundRectSegments(w, h, r) }, polygon([
335
+ [cx - uy * base, cy + ux * base],
336
+ [cx + uy * base, cy - ux * base],
337
+ [tipX, tipY]
338
+ ])];
339
+ }
340
+ case "leftBracket":
341
+ case "rightBracket":
342
+ case "leftBrace":
343
+ case "rightBrace": {
344
+ const brace = preset === "leftBrace" || preset === "rightBrace";
345
+ const r = Math.min(clamp(frac(adjust, "adj1", 8333), 0, .5) * h, brace ? w / 2 : w);
346
+ const mirrored = preset === "rightBracket" || preset === "rightBrace";
347
+ const x = (v) => mirrored ? w - v : v;
348
+ const spine = brace ? r : 0;
349
+ const b = new PathBuilder().moveTo(x(w), 0).lineTo(x(spine + r), 0).lineTo(x(spine), r);
350
+ if (brace) {
351
+ const mid = clamp(frac(adjust, "adj2", 5e4), 0, 1) * h;
352
+ b.lineTo(x(spine), Math.max(r, mid - r)).lineTo(x(0), mid).lineTo(x(spine), Math.min(h - r, mid + r));
353
+ }
354
+ return [b.lineTo(x(spine), h - r).lineTo(x(spine + r), h).lineTo(x(w), h).build()];
355
+ }
120
356
  default: return null;
121
357
  }
122
358
  }
@@ -1,5 +1,6 @@
1
1
  import { PathBuilder } from "../vector.js";
2
2
  import { applyTransform, cloneDc, colorRef, newDeviceContext } from "./picture.js";
3
+ import { fromSymbolFont } from "./symbol-fonts.js";
3
4
  //#region src/core/metafile/emf.ts
4
5
  /** Whether the bytes open with an EMF header (MS-EMF §2.3.4.2: type 1 + " EMF"). */
5
6
  function isEmf(bytes) {
@@ -371,7 +372,7 @@ function readEmf(bytes) {
371
372
  const font = dc.font;
372
373
  prims.push({
373
374
  kind: "text",
374
- text,
375
+ text: fromSymbolFont(text, font?.family),
375
376
  x: p.x,
376
377
  y: p.y,
377
378
  alignH: dc.alignH,
@@ -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,6 @@
1
1
  import { PathBuilder } from "../vector.js";
2
2
  import { cloneDc, colorRef, newDeviceContext } from "./picture.js";
3
+ import { fromSymbolFont, symbolGeometryOf, symbolOutline } from "./symbol-fonts.js";
3
4
  import { ellipseSegments } from "./emf.js";
4
5
  //#region src/core/metafile/wmf.ts
5
6
  var PLACEABLE_KEY = 2596720087;
@@ -289,14 +290,20 @@ function readWmf(bytes) {
289
290
  }
290
291
  function pushText(prims, dc, text, x, y) {
291
292
  if (text.trim() === "") return;
293
+ const em = Math.abs(dc.font?.heightLu ?? 12);
294
+ const drawn = symbolPrims(text, dc, x, y, em);
295
+ if (drawn) {
296
+ prims.push(...drawn);
297
+ return;
298
+ }
292
299
  prims.push({
293
300
  kind: "text",
294
- text,
301
+ text: fromSymbolFont(text, dc.font?.family),
295
302
  x,
296
303
  y,
297
304
  alignH: dc.alignH,
298
305
  alignBaseline: dc.alignBaseline,
299
- sizeLu: Math.abs(dc.font?.heightLu ?? 12),
306
+ sizeLu: em,
300
307
  colorHex: dc.textColorHex,
301
308
  ...dc.font?.family ? { fontFamily: dc.font.family } : {},
302
309
  ...dc.font?.bold ? { bold: true } : {},
@@ -367,5 +374,31 @@ function polySegments(pts, closed) {
367
374
  if (closed) segs.push({ op: "close" });
368
375
  return segs;
369
376
  }
377
+ /**
378
+ * The symbols a text record draws, as filled outlines, or `undefined` when the
379
+ * string is not one the shapes cover.
380
+ *
381
+ * Both fonts advance one em per symbol and fill that em, sitting from 0.2 em
382
+ * below the baseline to 0.8 em above it — measured off Webdings itself.
383
+ *
384
+ * @param text The string as stored.
385
+ * @param dc The device context (font, colour, alignment).
386
+ * @param x The reference point's x, in logical units.
387
+ * @param y Its y — the text's top unless the context says baseline.
388
+ * @param em The font's height in logical units.
389
+ * @returns One filled path per character, or `undefined`.
390
+ */
391
+ function symbolPrims(text, dc, x, y, em) {
392
+ const shapes = symbolGeometryOf(text, dc.font?.family);
393
+ if (!shapes) return void 0;
394
+ const width = em * shapes.length;
395
+ const left = dc.alignH === "center" ? x - width / 2 : dc.alignH === "right" ? x - width : x;
396
+ const top = dc.alignBaseline ? y - em * .8 : y;
397
+ return shapes.map((shape, i) => ({
398
+ kind: "path",
399
+ paths: [symbolOutline(shape, left + i * em, top, em)],
400
+ fillColorHex: dc.textColorHex
401
+ }));
402
+ }
370
403
  //#endregion
371
404
  export { isWmf, readWmf };
@@ -49,6 +49,11 @@ export interface GradientStop {
49
49
  readonly offset: number;
50
50
  /** Stop colour as 6-hex, no leading `#`. */
51
51
  readonly colorHex: string;
52
+ /**
53
+ * §20.1.2.3.1 `a:alpha` — how opaque THIS stop is, `0..1`; absent is opaque.
54
+ * The colour beside it is the stop's own, not composited over the paper.
55
+ */
56
+ readonly alpha?: number;
52
57
  }
53
58
  /**
54
59
  * A gradient fill — the format-agnostic vocabulary shared by the shape model
@@ -1,6 +1,6 @@
1
1
  import { emuToPt, pt } from "../core/ir/units.js";
2
2
  import { poAttr, poChildren, poFirstChild, poIntAttr, poIs, poTag, poText } from "../core/po-helpers.js";
3
- import { applyColorMods, resolveColorNode } from "../core/drawingml/colors.js";
3
+ import { placeholderColors, resolveColorNode } from "../core/drawingml/colors.js";
4
4
  import { parseFill, parseLine, parseShadow, parseXfrm, shadowFromOuterShdw } from "../word/drawing-parser.js";
5
5
  import { parseGeometry, parseTxBody } from "../pptx/slide-parser.js";
6
6
  import { parseXml } from "../pptx/pptx-reader.js";
@@ -387,17 +387,6 @@ function styleFill(sp, ns, colors, themeFillStyles) {
387
387
  } : themed;
388
388
  }
389
389
  /**
390
- * §20.1.4.2.10 — inside a theme's style list, `phClr` stands for "the colour the
391
- * reference names". Every other colour resolves as it always did.
392
- *
393
- * @param colors The workbook's own resolver.
394
- * @param phHex The colour the `a:fillRef` / `a:lnRef` carries.
395
- * @returns A resolver that answers `phClr` with that colour, transforms and all.
396
- */
397
- function placeholderColors(colors, phHex) {
398
- return (raw) => "scheme" in raw && raw.scheme === "phClr" ? applyColorMods(phHex, raw.mods ?? []) : colors(raw);
399
- }
400
- /**
401
390
  * The shadow `<xdr:style><a:effectRef>` names, if it names one.
402
391
  *
403
392
  * §20.1.4.2.8, the same mechanism as the fill and the outline beside it: a
@@ -29,7 +29,7 @@ export declare function readXlsx(xlsx: Uint8Array, options?: ProjectSheetOptions
29
29
  */
30
30
  export declare function readXlsxToSheetDoc(xlsx: Uint8Array): SheetDoc;
31
31
  /**
32
- * The OOXML `.xlsx` {@link DocumentReader}: sniffs the OPC ZIP for
32
+ * The OOXML `.xlsx` {@link DocumentReader}: sniffs the OPC ZIP's directory for
33
33
  * `xl/workbook.xml` and reads it into a {@link SheetDoc}. The reader's native
34
34
  * tree is the SheetDoc; the facade/Ream project it to a {@link FlowDoc} for
35
35
  * rendering (E-SHEET SB1).
@@ -1,7 +1,7 @@
1
1
  import { ResourceStore } from "../core/ir/resources.js";
2
2
  import { FEATURES } from "../core/ir/features.js";
3
3
  import { detectImageFormat } from "../core/images.js";
4
- import { bytesInclude, bytesIncludePartName } from "../core/bytes.js";
4
+ import { bytesInclude, packageHasPart } from "../core/bytes.js";
5
5
  import { DEFAULT_THEME_PALETTE, makeColorResolver } from "../core/drawingml/colors.js";
6
6
  import { parseChart, withChartColorStyle } from "../core/drawingml/chart-parser.js";
7
7
  import { parseTheme, parseThemeEffectStyles, parseThemeFillStyles, parseThemeLineWidths } from "../core/drawingml/theme-parser.js";
@@ -651,7 +651,7 @@ function lighten(hex, amount) {
651
651
  return `${hx(ch(16))}${hx(ch(8))}${hx(ch(0))}`;
652
652
  }
653
653
  /**
654
- * The OOXML `.xlsx` {@link DocumentReader}: sniffs the OPC ZIP for
654
+ * The OOXML `.xlsx` {@link DocumentReader}: sniffs the OPC ZIP's directory for
655
655
  * `xl/workbook.xml` and reads it into a {@link SheetDoc}. The reader's native
656
656
  * tree is the SheetDoc; the facade/Ream project it to a {@link FlowDoc} for
657
657
  * rendering (E-SHEET SB1).
@@ -660,7 +660,7 @@ var xlsxReader = {
660
660
  id: "xlsx",
661
661
  produces: "sheet",
662
662
  supports: new Set([FEATURES.text, FEATURES.tables]),
663
- sniff: (bytes) => bytes[0] === 80 && bytes[1] === 75 && bytesIncludePartName(bytes, "xl/workbook.xml"),
663
+ sniff: (bytes) => bytes[0] === 80 && bytes[1] === 75 && packageHasPart(bytes, "xl/workbook.xml"),
664
664
  read: (bytes) => ({
665
665
  doc: readXlsxToSheetDoc(bytes),
666
666
  losses: []
@@ -40,6 +40,7 @@ export { docxReader, readDocx } from './word/docx-reader.js';
40
40
  export { xlsxReader, readXlsx } from './excel/xlsx-reader.js';
41
41
  export { Ream } from './core/converter/ream.js';
42
42
  export type { ReamConvertOptions, ReamParseOptions, ReamTarget } from './core/converter/ream.js';
43
+ export { isEncryptedPackage, WrongPasswordError } from './core/crypto/offcrypto.js';
43
44
  export { createConverter } from './core/converter/facade.js';
44
45
  export type { Converter, ConvertOptions, ConvertResult, CreateConverterOptions, } from './core/converter/facade.js';
45
46
  export type { PageItem, PageItemBase, TextLineItem, BorderItem, FillItem, ImageItem, ShapeItem, LaidOutPage, } from './layout/page-doc.js';
package/dist/esm/index.js CHANGED
@@ -19,5 +19,6 @@ import { docxWriter, writeDocx } from "./word/docx-writer.js";
19
19
  import { htmlWriter, writeHtml } from "./html/html-writer.js";
20
20
  import { svgWriter, writeSvg } from "./svg/svg-writer.js";
21
21
  import { createConverter } from "./core/converter/facade.js";
22
+ import { WrongPasswordError, isEncryptedPackage } from "./core/crypto/offcrypto.js";
22
23
  import { Ream } from "./core/converter/ream.js";
23
- export { ConversionLossError, FEATURES, FontRegistry, NO_FONT, Ream, ResourceStore, callerFontProvider, chainProviders, createConverter, createHyphenator, createLanguageHyphenator, docxReader, docxWriter, eighthPtToPt, embeddedDocFontProvider, emuToPt, fetchFontSet, formatLoss, getHyphenator, halfPtToPt, htmlWriter, inchToPt, isEmbeddingRestricted, layoutStyledDocument, localFontProvider, mmToPt, parseTtf, pt, pxToPt, readDocx, readOs2FsType, readXlsx, remoteFontProvider, renderStyledPdf, resolveFamilyKey, signPdf, splitPatternBundle, subsetTtf, svgWriter, twipsToPt, writeDocx, writeHtml, writeSvg, xlsxReader };
24
+ export { ConversionLossError, FEATURES, FontRegistry, NO_FONT, Ream, ResourceStore, WrongPasswordError, callerFontProvider, chainProviders, createConverter, createHyphenator, createLanguageHyphenator, docxReader, docxWriter, eighthPtToPt, embeddedDocFontProvider, emuToPt, fetchFontSet, formatLoss, getHyphenator, halfPtToPt, htmlWriter, inchToPt, isEmbeddingRestricted, isEncryptedPackage, layoutStyledDocument, localFontProvider, mmToPt, parseTtf, pt, pxToPt, readDocx, readOs2FsType, readXlsx, remoteFontProvider, renderStyledPdf, resolveFamilyKey, signPdf, splitPatternBundle, subsetTtf, svgWriter, twipsToPt, writeDocx, writeHtml, writeSvg, xlsxReader };
@@ -242,6 +242,24 @@ export interface PageItemBase {
242
242
  * is treated as an artifact. Ignored when not tagging.
243
243
  */
244
244
  readonly structId?: number;
245
+ /**
246
+ * §20.4.2.3 `@behindDoc` — the item is BEHIND the page's content: a
247
+ * watermark, a slide's backdrop. Such items paint before everything else, in
248
+ * their own order. Without that they rode the ordinary passes, where every
249
+ * shape paints after every image — so a slide's white backdrop covered the
250
+ * photograph the slide is made of (corpus: tdf156808, tdf157635, tdf156856).
251
+ */
252
+ readonly behind?: boolean;
253
+ /**
254
+ * The picture this item belongs to, when it is part of one. A metafile is a
255
+ * list of drawing orders, and text among them is BOTH over what came before
256
+ * and under what comes after: an embedded diagram writes a label, lays a
257
+ * panel over it and writes the label again, offset, as its own drop shadow.
258
+ * Painted in the ordinary passes — every shape, then every line — the buried
259
+ * copy would show through the panel. Items sharing this id are held out of
260
+ * those passes and painted together, in list order.
261
+ */
262
+ readonly pictureId?: number;
245
263
  /**
246
264
  * Tagged PDF: explicitly mark this item as a pagination artifact (running
247
265
  * header/footer, §14.8.2.2.2). Distinguishes header/footer text from
@@ -314,6 +332,19 @@ export interface ImageItem extends PageItemBase {
314
332
  readonly gain: number;
315
333
  readonly black: number;
316
334
  };
335
+ /**
336
+ * §20.1.8.4 `a:alphaModFix` — how opaque the picture is drawn, 0…1. A slide
337
+ * backed by a photograph at 70 % shows a pale wash of it, not the photograph.
338
+ */
339
+ readonly alpha?: number;
340
+ /**
341
+ * §20.1.8.23 `a:duotone` — the picture painted between two colours instead of
342
+ * its own: its dark end is the first, its light end the second.
343
+ */
344
+ readonly duotone?: {
345
+ readonly shadowHex: string;
346
+ readonly highlightHex: string;
347
+ };
317
348
  /** §20.1.7.6 — degrees clockwise about the box's centre. */
318
349
  readonly rotationDeg?: number;
319
350
  /** §20.1.7.6 — the picture drawn mirrored in its box. */
@@ -340,11 +371,18 @@ export interface ShapeItem extends PageItemBase {
340
371
  /** A positioned item on a laid-out page: a text line, border, fill, image, or vector shape. */
341
372
  export type PageItem = TextLineItem | BorderItem | FillItem | ImageItem | ShapeItem;
342
373
  export interface PagePaintPlan {
374
+ /** What sits behind the page's content, in its own order — painted first. */
375
+ readonly behind: ReadonlyArray<PageItem>;
343
376
  readonly fills: ReadonlyArray<FillItem>;
344
377
  readonly images: ReadonlyArray<ImageItem>;
345
378
  readonly borders: ReadonlyArray<BorderItem>;
346
379
  readonly shapes: ReadonlyArray<ShapeItem>;
347
380
  readonly lines: ReadonlyArray<TextLineItem>;
381
+ /**
382
+ * Pictures, each as its own list in the order the picture draws — one entry
383
+ * per {@link PageItemBase.pictureId}. They paint where the shape pass does.
384
+ */
385
+ readonly pictures: ReadonlyArray<ReadonlyArray<PageItem>>;
348
386
  }
349
387
  export declare function paintPlan(commands: ReadonlyArray<PageItem>): PagePaintPlan;
350
388
  export interface LaidOutDocument {