reamkit 1.22.0 → 1.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/core/bmp.d.ts +33 -0
- package/dist/esm/core/bmp.js +276 -0
- package/dist/esm/core/converter/ream.d.ts +16 -0
- package/dist/esm/core/converter/ream.js +59 -9
- package/dist/esm/core/document-model/types.d.ts +55 -0
- package/dist/esm/core/drawingml/chart-geometry.js +14 -7
- package/dist/esm/core/drawingml/colors.d.ts +4 -2
- package/dist/esm/core/drawingml/colors.js +15 -0
- package/dist/esm/core/drawingml/diagram/colors.d.ts +52 -0
- package/dist/esm/core/drawingml/diagram/colors.js +125 -0
- package/dist/esm/core/drawingml/diagram/data-model.d.ts +47 -0
- package/dist/esm/core/drawingml/diagram/data-model.js +119 -0
- package/dist/esm/core/drawingml/diagram/layout-engine.d.ts +46 -0
- package/dist/esm/core/drawingml/diagram/layout-engine.js +948 -0
- package/dist/esm/core/drawingml/diagram/run.d.ts +37 -0
- package/dist/esm/core/drawingml/diagram/run.js +50 -0
- package/dist/esm/core/drawingml/diagram/to-drawing.d.ts +16 -0
- package/dist/esm/core/drawingml/diagram/to-drawing.js +86 -0
- package/dist/esm/core/drawingml/preset-geometry.js +116 -6
- package/dist/esm/core/drawingml/text-warp.d.ts +58 -0
- package/dist/esm/core/drawingml/text-warp.js +355 -0
- package/dist/esm/core/drawingml/theme-parser.d.ts +38 -0
- package/dist/esm/core/drawingml/theme-parser.js +73 -4
- package/dist/esm/core/font/measure.d.ts +12 -0
- package/dist/esm/core/font/measure.js +36 -0
- package/dist/esm/core/fonts/families.d.ts +10 -0
- package/dist/esm/core/fonts/families.js +44 -0
- package/dist/esm/core/fonts/index.d.ts +3 -2
- package/dist/esm/core/fonts/remote-fonts.d.ts +50 -0
- package/dist/esm/core/fonts/remote-fonts.js +114 -8
- package/dist/esm/core/fonts/scripts.d.ts +26 -0
- package/dist/esm/core/fonts/scripts.js +177 -0
- package/dist/esm/core/images.d.ts +30 -2
- package/dist/esm/core/images.js +121 -5
- package/dist/esm/core/metafile/blit.d.ts +33 -0
- package/dist/esm/core/metafile/blit.js +67 -0
- package/dist/esm/core/metafile/dib.d.ts +61 -0
- package/dist/esm/core/metafile/dib.js +205 -0
- package/dist/esm/core/metafile/emf.js +280 -38
- package/dist/esm/core/metafile/picture.d.ts +61 -1
- package/dist/esm/core/metafile/picture.js +70 -1
- package/dist/esm/core/metafile/wmf.js +202 -13
- package/dist/esm/core/ole/escher-blip.d.ts +12 -0
- package/dist/esm/core/ole/escher-blip.js +98 -0
- package/dist/esm/{pdf-reader → core}/png-encode.js +1 -1
- package/dist/esm/excel/print-model.js +5 -3
- package/dist/esm/excel/sheet-drawing.js +1 -1
- package/dist/esm/excel/sheet-to-flow.js +16 -2
- package/dist/esm/excel/styles-parser.js +1 -1
- package/dist/esm/excel/xls/biff-reader.js +53 -18
- package/dist/esm/excel/xls/biff-styles.d.ts +5 -2
- package/dist/esm/excel/xls/biff-styles.js +9 -7
- package/dist/esm/excel/xls/escher.js +90 -24
- package/dist/esm/html/html-writer.js +2 -1
- package/dist/esm/layout/page-doc.d.ts +74 -0
- package/dist/esm/layout/page-doc.js +14 -1
- package/dist/esm/layout/styled-layout.d.ts +2 -2
- package/dist/esm/layout/styled-layout.js +475 -48
- package/dist/esm/pdf/shading.js +8 -6
- package/dist/esm/pdf/styled-page-emitter.js +77 -5
- package/dist/esm/pdf/vector-graphics.js +1 -1
- package/dist/esm/pdf-reader/image-decode.js +1 -1
- package/dist/esm/pptx/embedded-fonts.d.ts +31 -0
- package/dist/esm/pptx/embedded-fonts.js +104 -0
- package/dist/esm/pptx/placeholder-cascade.d.ts +73 -6
- package/dist/esm/pptx/placeholder-cascade.js +114 -18
- package/dist/esm/pptx/ppt/ppt-reader.js +191 -13
- package/dist/esm/pptx/ppt/ppt-text.d.ts +107 -1
- package/dist/esm/pptx/ppt/ppt-text.js +873 -144
- package/dist/esm/pptx/pptx-reader.d.ts +8 -0
- package/dist/esm/pptx/pptx-reader.js +117 -15
- package/dist/esm/pptx/preset-table-styles.d.ts +9 -0
- package/dist/esm/pptx/preset-table-styles.js +27 -0
- package/dist/esm/pptx/slide-parser.d.ts +11 -2
- package/dist/esm/pptx/slide-parser.js +203 -44
- package/dist/esm/pptx/sp-helpers.d.ts +9 -4
- package/dist/esm/pptx/sp-helpers.js +48 -9
- package/dist/esm/pptx/table-style.d.ts +16 -1
- package/dist/esm/pptx/table-style.js +104 -10
- package/dist/esm/svg/svg-writer.js +1 -0
- package/dist/esm/word/document-parser.d.ts +11 -2
- package/dist/esm/word/document-parser.js +10 -7
- package/dist/esm/word/docx-reader.js +32 -16
- package/dist/esm/word/docx-to-pdf.d.ts +8 -7
- package/dist/esm/word/docx-to-pdf.js +25 -7
- package/dist/esm/word/docx-writer.js +4 -0
- package/dist/esm/word/drawing-parser.d.ts +20 -0
- package/dist/esm/word/drawing-parser.js +67 -22
- package/dist/esm/word/numbering-parser.d.ts +2 -1
- package/dist/esm/word/numbering-parser.js +6 -6
- package/dist/esm/word/paragraph-properties.d.ts +2 -1
- package/dist/esm/word/paragraph-properties.js +2 -2
- package/dist/esm/word/run-properties.d.ts +5 -2
- package/dist/esm/word/run-properties.js +13 -8
- package/dist/esm/word/styles-parser.d.ts +2 -1
- package/dist/esm/word/styles-parser.js +12 -12
- package/dist/esm/word/table-parser.js +6 -3
- package/dist/esm/word/theme-fonts.d.ts +10 -0
- package/dist/esm/word/theme-fonts.js +28 -0
- package/package.json +1 -1
- /package/dist/esm/{pdf-reader → core}/png-encode.d.ts +0 -0
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { PathBuilder } from "../vector.js";
|
|
2
|
-
import { applyTransform, cloneDc, colorRef, newDeviceContext } from "./picture.js";
|
|
2
|
+
import { applyTransform, clippedAway, cloneDc, colorRef, intersectClip, newDeviceContext, primBounds } from "./picture.js";
|
|
3
|
+
import { cropDib, fadedDib, readDib } from "./dib.js";
|
|
4
|
+
import { makeBlitter } from "./blit.js";
|
|
3
5
|
import { fromSymbolFont } from "./symbol-fonts.js";
|
|
4
6
|
//#region src/core/metafile/emf.ts
|
|
5
7
|
/** Whether the bytes open with an EMF header (MS-EMF §2.3.4.2: type 1 + " EMF"). */
|
|
@@ -21,11 +23,6 @@ var RECORD_NAMES = new Map([
|
|
|
21
23
|
[74, "region paint"],
|
|
22
24
|
[78, "masked blit"],
|
|
23
25
|
[79, "parallelogram blit"],
|
|
24
|
-
[80, "device bitmap"],
|
|
25
|
-
[81, "bitmap"],
|
|
26
|
-
[76, "bitmap"],
|
|
27
|
-
[77, "bitmap"],
|
|
28
|
-
[114, "alpha blit"],
|
|
29
26
|
[116, "transparent blit"],
|
|
30
27
|
[118, "gradient fill"]
|
|
31
28
|
]);
|
|
@@ -74,9 +71,47 @@ function readEmf(bytes) {
|
|
|
74
71
|
};
|
|
75
72
|
let building;
|
|
76
73
|
const open = () => building ?? [];
|
|
74
|
+
const inFigure = () => {
|
|
75
|
+
const tail = building?.[building.length - 1];
|
|
76
|
+
return tail !== void 0 && tail.op !== "close";
|
|
77
|
+
};
|
|
78
|
+
const emit = (prim) => {
|
|
79
|
+
if (!clippedAway(dc, primBounds(prim))) prims.push(prim);
|
|
80
|
+
};
|
|
81
|
+
const blitter = makeBlitter(emit);
|
|
82
|
+
/**
|
|
83
|
+
* §2.3.1 — one blit record: the source bitmap into the destination
|
|
84
|
+
* rectangle, both stated in the record's own fields. The rectangle is mapped
|
|
85
|
+
* the way every other coordinate here is, so the picture lands in the same
|
|
86
|
+
* device frame as the paths around it.
|
|
87
|
+
*/
|
|
88
|
+
const blit = (o) => {
|
|
89
|
+
const dib = o.cbBmi > 0 ? readDib(bytes, off + o.bmiAt, {
|
|
90
|
+
bitsAt: off + o.bitsAt,
|
|
91
|
+
cbBits: o.cbBits,
|
|
92
|
+
...o.blend?.perPixel === true ? { alpha: true } : {}
|
|
93
|
+
}) : void 0;
|
|
94
|
+
if (!dib) {
|
|
95
|
+
skipped.add("bitmap");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const s = o.src;
|
|
99
|
+
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;
|
|
100
|
+
const a = px(o.dest.x, o.dest.y);
|
|
101
|
+
const b = px(o.dest.x + o.dest.w, o.dest.y + o.dest.h);
|
|
102
|
+
blitter.blit(o.blend && o.blend.alpha < 255 ? fadedDib(part, o.blend.alpha / 255) : part, {
|
|
103
|
+
x: Math.min(a.x, b.x),
|
|
104
|
+
y: Math.min(a.y, b.y),
|
|
105
|
+
width: Math.abs(b.x - a.x),
|
|
106
|
+
height: Math.abs(b.y - a.y)
|
|
107
|
+
}, o.rop);
|
|
108
|
+
};
|
|
77
109
|
const strokeOf = () => {
|
|
78
110
|
if (dc.pen.style === "none") return void 0;
|
|
79
|
-
const
|
|
111
|
+
const m = dc.transform;
|
|
112
|
+
const world = Math.sqrt(Math.abs(m[0] * m[3] - m[1] * m[2])) || 1;
|
|
113
|
+
const lu = dc.pen.widthLu * world;
|
|
114
|
+
const w = lu <= 1 ? 1 : lu * Math.abs(sx());
|
|
80
115
|
return {
|
|
81
116
|
colorHex: dc.pen.colorHex,
|
|
82
117
|
widthPt: w,
|
|
@@ -92,13 +127,22 @@ function readEmf(bytes) {
|
|
|
92
127
|
const st = stroke ? strokeOf() : void 0;
|
|
93
128
|
const fillHex = fill && !dc.brush.hollow ? dc.brush.colorHex : void 0;
|
|
94
129
|
if (fillHex === void 0 && !st) return;
|
|
95
|
-
|
|
130
|
+
emit({
|
|
96
131
|
kind: "path",
|
|
97
132
|
paths: [path],
|
|
98
133
|
...fillHex !== void 0 ? { fillColorHex: fillHex } : {},
|
|
99
134
|
...st ? { stroke: st } : {}
|
|
100
135
|
});
|
|
101
136
|
};
|
|
137
|
+
/** Two mapped corners → the rectangle they bound, in either order. */
|
|
138
|
+
function rectOf(a, b) {
|
|
139
|
+
return {
|
|
140
|
+
left: Math.min(a.x, b.x),
|
|
141
|
+
top: Math.min(a.y, b.y),
|
|
142
|
+
right: Math.max(a.x, b.x),
|
|
143
|
+
bottom: Math.max(a.y, b.y)
|
|
144
|
+
};
|
|
145
|
+
}
|
|
102
146
|
/** A closed rectangle, as the geometry records draw one. */
|
|
103
147
|
const rectSegments = (l, t, r, b) => {
|
|
104
148
|
const p0 = px(l, t);
|
|
@@ -151,6 +195,32 @@ function readEmf(bytes) {
|
|
|
151
195
|
};
|
|
152
196
|
haveView = true;
|
|
153
197
|
break;
|
|
198
|
+
case 30: {
|
|
199
|
+
const a = px(at(8), at(12));
|
|
200
|
+
const b = px(at(16), at(20));
|
|
201
|
+
dc = {
|
|
202
|
+
...dc,
|
|
203
|
+
clip: intersectClip(dc, rectOf(a, b))
|
|
204
|
+
};
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
case 75: {
|
|
208
|
+
const cbRgnData = u(8);
|
|
209
|
+
const mode = u(12);
|
|
210
|
+
if (cbRgnData === 0 && mode === 5) {
|
|
211
|
+
const { clip: _drop, ...rest } = dc;
|
|
212
|
+
dc = rest;
|
|
213
|
+
} else if (mode === 5 && cbRgnData >= 32 && u(20) === 1) {
|
|
214
|
+
const base = off + 16 + 32;
|
|
215
|
+
const a = px(v.getInt32(base, true), v.getInt32(base + 4, true));
|
|
216
|
+
const b = px(v.getInt32(base + 8, true), v.getInt32(base + 12, true));
|
|
217
|
+
dc = {
|
|
218
|
+
...dc,
|
|
219
|
+
clip: rectOf(a, b)
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
154
224
|
case 33:
|
|
155
225
|
stack.push(cloneDc(dc));
|
|
156
226
|
break;
|
|
@@ -256,7 +326,7 @@ function readEmf(bytes) {
|
|
|
256
326
|
dc.y = at(12);
|
|
257
327
|
const to = px(dc.x, dc.y);
|
|
258
328
|
if (building) {
|
|
259
|
-
if (
|
|
329
|
+
if (!inFigure()) building.push({
|
|
260
330
|
op: "move",
|
|
261
331
|
x: from.x,
|
|
262
332
|
y: from.y
|
|
@@ -302,6 +372,11 @@ function readEmf(bytes) {
|
|
|
302
372
|
case 44:
|
|
303
373
|
paint(rectSegments(at(8), at(12), at(16), at(20)), true, true);
|
|
304
374
|
break;
|
|
375
|
+
case 47:
|
|
376
|
+
case 46:
|
|
377
|
+
case 45:
|
|
378
|
+
paint(arcSegments(px, at(8), at(12), at(16), at(20), at(24), at(28), at(32), at(36), type), type !== 45, true);
|
|
379
|
+
break;
|
|
305
380
|
case 3:
|
|
306
381
|
case 4:
|
|
307
382
|
case 2:
|
|
@@ -320,10 +395,13 @@ function readEmf(bytes) {
|
|
|
320
395
|
const segs = polySegments(px, pts, {
|
|
321
396
|
closed,
|
|
322
397
|
bezier: type === 2 || type === 5 || type === 85 || type === 88,
|
|
323
|
-
...type === 5 || type === 6 || type === 88 || type === 89 ? {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
398
|
+
...type === 5 || type === 6 || type === 88 || type === 89 ? {
|
|
399
|
+
from: {
|
|
400
|
+
x: dc.x,
|
|
401
|
+
y: dc.y
|
|
402
|
+
},
|
|
403
|
+
joined: inFigure()
|
|
404
|
+
} : {}
|
|
327
405
|
});
|
|
328
406
|
if (pts.length > 0) {
|
|
329
407
|
dc.x = pts[pts.length - 1].x;
|
|
@@ -370,7 +448,7 @@ function readEmf(bytes) {
|
|
|
370
448
|
if (text.trim() !== "") {
|
|
371
449
|
const p = px(refX, refY);
|
|
372
450
|
const font = dc.font;
|
|
373
|
-
|
|
451
|
+
emit({
|
|
374
452
|
kind: "text",
|
|
375
453
|
text: fromSymbolFont(text, font?.family),
|
|
376
454
|
x: p.x,
|
|
@@ -388,25 +466,115 @@ function readEmf(bytes) {
|
|
|
388
466
|
}
|
|
389
467
|
break;
|
|
390
468
|
case 76:
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
469
|
+
case 77: {
|
|
470
|
+
const cbBmiSrc = u(88);
|
|
471
|
+
const rop = u(40);
|
|
472
|
+
const x = at(24);
|
|
473
|
+
const y = at(28);
|
|
474
|
+
const w = at(32);
|
|
475
|
+
const h = at(36);
|
|
476
|
+
if (cbBmiSrc === 0) {
|
|
477
|
+
const hex = rop === 66 ? "000000" : rop === 16711778 ? "FFFFFF" : void 0;
|
|
478
|
+
if (hex === void 0 && rop !== 15728673) break;
|
|
479
|
+
const brush = dc.brush;
|
|
480
|
+
if (hex !== void 0) dc.brush = {
|
|
481
|
+
kind: "brush",
|
|
482
|
+
colorHex: hex,
|
|
483
|
+
hollow: false
|
|
484
|
+
};
|
|
485
|
+
paint(rectSegments(x, y, x + w, y + h), true, false);
|
|
486
|
+
dc.brush = brush;
|
|
487
|
+
break;
|
|
409
488
|
}
|
|
489
|
+
blit({
|
|
490
|
+
bmiAt: u(84),
|
|
491
|
+
cbBmi: cbBmiSrc,
|
|
492
|
+
bitsAt: u(92),
|
|
493
|
+
cbBits: u(96),
|
|
494
|
+
dest: {
|
|
495
|
+
x,
|
|
496
|
+
y,
|
|
497
|
+
w,
|
|
498
|
+
h
|
|
499
|
+
},
|
|
500
|
+
src: {
|
|
501
|
+
x: at(44),
|
|
502
|
+
y: at(48),
|
|
503
|
+
w: type === 77 ? at(100) : w,
|
|
504
|
+
h: type === 77 ? at(104) : h
|
|
505
|
+
},
|
|
506
|
+
rop
|
|
507
|
+
});
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
case 80:
|
|
511
|
+
blit({
|
|
512
|
+
bmiAt: u(48),
|
|
513
|
+
cbBmi: u(52),
|
|
514
|
+
bitsAt: u(56),
|
|
515
|
+
cbBits: u(60),
|
|
516
|
+
dest: {
|
|
517
|
+
x: at(24),
|
|
518
|
+
y: at(28),
|
|
519
|
+
w: at(40),
|
|
520
|
+
h: at(44)
|
|
521
|
+
},
|
|
522
|
+
src: {
|
|
523
|
+
x: at(32),
|
|
524
|
+
y: at(36),
|
|
525
|
+
w: at(40),
|
|
526
|
+
h: at(44)
|
|
527
|
+
},
|
|
528
|
+
inBitmap: true,
|
|
529
|
+
rop: 13369376
|
|
530
|
+
});
|
|
531
|
+
break;
|
|
532
|
+
case 81:
|
|
533
|
+
blit({
|
|
534
|
+
bmiAt: u(48),
|
|
535
|
+
cbBmi: u(52),
|
|
536
|
+
bitsAt: u(56),
|
|
537
|
+
cbBits: u(60),
|
|
538
|
+
dest: {
|
|
539
|
+
x: at(24),
|
|
540
|
+
y: at(28),
|
|
541
|
+
w: at(72),
|
|
542
|
+
h: at(76)
|
|
543
|
+
},
|
|
544
|
+
src: {
|
|
545
|
+
x: at(32),
|
|
546
|
+
y: at(36),
|
|
547
|
+
w: at(40),
|
|
548
|
+
h: at(44)
|
|
549
|
+
},
|
|
550
|
+
inBitmap: true,
|
|
551
|
+
rop: u(68)
|
|
552
|
+
});
|
|
553
|
+
break;
|
|
554
|
+
case 114:
|
|
555
|
+
blit({
|
|
556
|
+
bmiAt: u(84),
|
|
557
|
+
cbBmi: u(88),
|
|
558
|
+
bitsAt: u(92),
|
|
559
|
+
cbBits: u(96),
|
|
560
|
+
dest: {
|
|
561
|
+
x: at(24),
|
|
562
|
+
y: at(28),
|
|
563
|
+
w: at(32),
|
|
564
|
+
h: at(36)
|
|
565
|
+
},
|
|
566
|
+
src: {
|
|
567
|
+
x: at(44),
|
|
568
|
+
y: at(48),
|
|
569
|
+
w: at(100),
|
|
570
|
+
h: at(104)
|
|
571
|
+
},
|
|
572
|
+
blend: {
|
|
573
|
+
alpha: u(40) >>> 16 & 255,
|
|
574
|
+
perPixel: u(40) >>> 24 === 1
|
|
575
|
+
},
|
|
576
|
+
rop: 13369376
|
|
577
|
+
});
|
|
410
578
|
break;
|
|
411
579
|
default:
|
|
412
580
|
{
|
|
@@ -557,12 +725,14 @@ function polySegments(map, pts, o) {
|
|
|
557
725
|
if (pts.length === 0) return segs;
|
|
558
726
|
let i = 0;
|
|
559
727
|
if (o.from) {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
728
|
+
if (o.joined !== true) {
|
|
729
|
+
const s = map(o.from.x, o.from.y);
|
|
730
|
+
segs.push({
|
|
731
|
+
op: "move",
|
|
732
|
+
x: s.x,
|
|
733
|
+
y: s.y
|
|
734
|
+
});
|
|
735
|
+
}
|
|
566
736
|
} else {
|
|
567
737
|
const s = map(pts[0].x, pts[0].y);
|
|
568
738
|
segs.push({
|
|
@@ -598,6 +768,78 @@ function polySegments(map, pts, o) {
|
|
|
598
768
|
return segs;
|
|
599
769
|
}
|
|
600
770
|
var KAPPA = .5522847498307936;
|
|
771
|
+
/**
|
|
772
|
+
* §2.3.5.2/.4/.13 — the wedge, chord or bare curve an `EMR_ARC`, `EMR_CHORD` or
|
|
773
|
+
* `EMR_PIE` draws.
|
|
774
|
+
*
|
|
775
|
+
* All three state the same thing: an ellipse inscribed in a box, and two points
|
|
776
|
+
* naming RAYS from its centre. The arc runs from where the first ray crosses
|
|
777
|
+
* the ellipse to where the second does, anticlockwise on the page — GDI's
|
|
778
|
+
* default direction — and the two records differ only in how they close: a pie
|
|
779
|
+
* back through the centre, a chord straight across, an arc not at all.
|
|
780
|
+
*
|
|
781
|
+
* Built in the metafile's own coordinates and mapped point by point, exactly as
|
|
782
|
+
* the ellipse and the polygons are: the mapping is affine, so a Bézier's
|
|
783
|
+
* control points survive it.
|
|
784
|
+
*
|
|
785
|
+
* @param map The metafile's logical → page mapping.
|
|
786
|
+
* @param l,t,r,b The box the ellipse is inscribed in.
|
|
787
|
+
* @param sx,sy The point naming the ray the arc starts at.
|
|
788
|
+
* @param ex,ey The point naming the ray it ends at.
|
|
789
|
+
* @param type The record type, which decides how the figure closes.
|
|
790
|
+
* @returns The path segments, in page coordinates.
|
|
791
|
+
*/
|
|
792
|
+
function arcSegments(map, l, t, r, b, sx, sy, ex, ey, type) {
|
|
793
|
+
const cx = (l + r) / 2;
|
|
794
|
+
const cy = (t + b) / 2;
|
|
795
|
+
const rx = (r - l) / 2;
|
|
796
|
+
const ry = (b - t) / 2;
|
|
797
|
+
if (!(rx > 0) || !(ry > 0)) return [];
|
|
798
|
+
const angleOf = (x, y) => Math.atan2((y - cy) / ry, (x - cx) / rx);
|
|
799
|
+
const start = angleOf(sx, sy);
|
|
800
|
+
let sweep = angleOf(ex, ey) - start;
|
|
801
|
+
if (sweep >= 0) sweep -= 2 * Math.PI;
|
|
802
|
+
const point = (a) => map(cx + rx * Math.cos(a), cy + ry * Math.sin(a));
|
|
803
|
+
const segs = [];
|
|
804
|
+
const from = point(start);
|
|
805
|
+
if (type === 47) {
|
|
806
|
+
const c = map(cx, cy);
|
|
807
|
+
segs.push({
|
|
808
|
+
op: "move",
|
|
809
|
+
x: c.x,
|
|
810
|
+
y: c.y
|
|
811
|
+
}, {
|
|
812
|
+
op: "line",
|
|
813
|
+
x: from.x,
|
|
814
|
+
y: from.y
|
|
815
|
+
});
|
|
816
|
+
} else segs.push({
|
|
817
|
+
op: "move",
|
|
818
|
+
x: from.x,
|
|
819
|
+
y: from.y
|
|
820
|
+
});
|
|
821
|
+
const steps = Math.max(1, Math.ceil(Math.abs(sweep) / (Math.PI / 2)));
|
|
822
|
+
const delta = sweep / steps;
|
|
823
|
+
const alpha = 4 / 3 * Math.tan(delta / 4);
|
|
824
|
+
for (let i = 0; i < steps; i++) {
|
|
825
|
+
const a0 = start + delta * i;
|
|
826
|
+
const a1 = a0 + delta;
|
|
827
|
+
const t0 = map(cx + rx * Math.cos(a0) - alpha * rx * Math.sin(a0), cy + ry * Math.sin(a0) + alpha * ry * Math.cos(a0));
|
|
828
|
+
const t1 = map(cx + rx * Math.cos(a1) + alpha * rx * Math.sin(a1), cy + ry * Math.sin(a1) - alpha * ry * Math.cos(a1));
|
|
829
|
+
const to = point(a1);
|
|
830
|
+
segs.push({
|
|
831
|
+
op: "cubic",
|
|
832
|
+
x1: t0.x,
|
|
833
|
+
y1: t0.y,
|
|
834
|
+
x2: t1.x,
|
|
835
|
+
y2: t1.y,
|
|
836
|
+
x: to.x,
|
|
837
|
+
y: to.y
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
if (type !== 45) segs.push({ op: "close" });
|
|
841
|
+
return segs;
|
|
842
|
+
}
|
|
601
843
|
function ellipseSegments(map, l, t, r, b) {
|
|
602
844
|
const cx = (l + r) / 2;
|
|
603
845
|
const cy = (t + b) / 2;
|
|
@@ -74,7 +74,18 @@ export interface MetaFont {
|
|
|
74
74
|
readonly italic?: boolean;
|
|
75
75
|
readonly escapement?: number;
|
|
76
76
|
}
|
|
77
|
-
|
|
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;
|
|
@@ -91,12 +102,61 @@ export interface DeviceContext {
|
|
|
91
102
|
y: number;
|
|
92
103
|
/** The world→page transform, as `[a, b, c, d, e, f]`. */
|
|
93
104
|
transform: readonly [number, number, number, number, number, number];
|
|
105
|
+
/**
|
|
106
|
+
* MS-EMF §2.3.2 / MS-WMF §2.3.2 — the region drawing is limited to, in
|
|
107
|
+
* logical units, when the metafile states a RECTANGULAR one. A primitive
|
|
108
|
+
* that falls wholly outside it is not drawn: an embedded workbook's preview
|
|
109
|
+
* clips its sheet to the used range, and unclipped we painted the whole
|
|
110
|
+
* empty grid around it.
|
|
111
|
+
*/
|
|
112
|
+
clip?: {
|
|
113
|
+
readonly left: number;
|
|
114
|
+
readonly top: number;
|
|
115
|
+
readonly right: number;
|
|
116
|
+
readonly bottom: number;
|
|
117
|
+
};
|
|
94
118
|
}
|
|
95
119
|
export declare const DEFAULT_PEN: MetaPen;
|
|
96
120
|
export declare const DEFAULT_BRUSH: MetaBrush;
|
|
97
121
|
/** A device context in its documented initial state (MS-WMF §3.1.5). */
|
|
98
122
|
export declare function newDeviceContext(): DeviceContext;
|
|
99
123
|
export declare function cloneDc(dc: DeviceContext): DeviceContext;
|
|
124
|
+
/**
|
|
125
|
+
* Whether a primitive bounded by `box` is wholly outside the device context's
|
|
126
|
+
* clip. Partial overlaps are kept whole — the reader discards what the clip
|
|
127
|
+
* excludes rather than cutting geometry, which is a missing cut, never wrong
|
|
128
|
+
* content.
|
|
129
|
+
*
|
|
130
|
+
* @param dc The device context, for its clip.
|
|
131
|
+
* @param box The primitive's bounds in logical units.
|
|
132
|
+
* @returns `true` when nothing of it would be drawn.
|
|
133
|
+
*/
|
|
134
|
+
export declare function clippedAway(dc: DeviceContext, box: {
|
|
135
|
+
left: number;
|
|
136
|
+
top: number;
|
|
137
|
+
right: number;
|
|
138
|
+
bottom: number;
|
|
139
|
+
}): boolean;
|
|
140
|
+
/** The intersection of a device context's clip with another rectangle. */
|
|
141
|
+
export declare function intersectClip(dc: DeviceContext, rect: {
|
|
142
|
+
left: number;
|
|
143
|
+
top: number;
|
|
144
|
+
right: number;
|
|
145
|
+
bottom: number;
|
|
146
|
+
}): NonNullable<DeviceContext['clip']>;
|
|
147
|
+
/**
|
|
148
|
+
* A primitive's bounds in the units it is stored in, for the clip test.
|
|
149
|
+
*
|
|
150
|
+
* @param prim The primitive.
|
|
151
|
+
* @returns Its bounding rectangle; a text run's is its reference point, since
|
|
152
|
+
* the box it fills is not known until it is measured.
|
|
153
|
+
*/
|
|
154
|
+
export declare function primBounds(prim: PicturePrim): {
|
|
155
|
+
left: number;
|
|
156
|
+
top: number;
|
|
157
|
+
right: number;
|
|
158
|
+
bottom: number;
|
|
159
|
+
};
|
|
100
160
|
/** A COLORREF (`0x00bbggrr`) as the 6-hex this codebase's colours use. */
|
|
101
161
|
export declare function colorRef(v: number): string;
|
|
102
162
|
/** Apply the world transform to a point. */
|
|
@@ -36,6 +36,75 @@ function newDeviceContext() {
|
|
|
36
36
|
function cloneDc(dc) {
|
|
37
37
|
return { ...dc };
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Whether a primitive bounded by `box` is wholly outside the device context's
|
|
41
|
+
* clip. Partial overlaps are kept whole — the reader discards what the clip
|
|
42
|
+
* excludes rather than cutting geometry, which is a missing cut, never wrong
|
|
43
|
+
* content.
|
|
44
|
+
*
|
|
45
|
+
* @param dc The device context, for its clip.
|
|
46
|
+
* @param box The primitive's bounds in logical units.
|
|
47
|
+
* @returns `true` when nothing of it would be drawn.
|
|
48
|
+
*/
|
|
49
|
+
function clippedAway(dc, box) {
|
|
50
|
+
const c = dc.clip;
|
|
51
|
+
if (!c) return false;
|
|
52
|
+
return box.right < c.left || box.left > c.right || box.bottom < c.top || box.top > c.bottom;
|
|
53
|
+
}
|
|
54
|
+
/** The intersection of a device context's clip with another rectangle. */
|
|
55
|
+
function intersectClip(dc, rect) {
|
|
56
|
+
const c = dc.clip;
|
|
57
|
+
if (!c) return rect;
|
|
58
|
+
return {
|
|
59
|
+
left: Math.max(c.left, rect.left),
|
|
60
|
+
top: Math.max(c.top, rect.top),
|
|
61
|
+
right: Math.min(c.right, rect.right),
|
|
62
|
+
bottom: Math.min(c.bottom, rect.bottom)
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A primitive's bounds in the units it is stored in, for the clip test.
|
|
67
|
+
*
|
|
68
|
+
* @param prim The primitive.
|
|
69
|
+
* @returns Its bounding rectangle; a text run's is its reference point, since
|
|
70
|
+
* the box it fills is not known until it is measured.
|
|
71
|
+
*/
|
|
72
|
+
function primBounds(prim) {
|
|
73
|
+
if (prim.kind === "text") return {
|
|
74
|
+
left: prim.x,
|
|
75
|
+
top: prim.y,
|
|
76
|
+
right: prim.x,
|
|
77
|
+
bottom: prim.y
|
|
78
|
+
};
|
|
79
|
+
if (prim.kind === "image") return {
|
|
80
|
+
left: prim.x,
|
|
81
|
+
top: prim.y,
|
|
82
|
+
right: prim.x + prim.width,
|
|
83
|
+
bottom: prim.y + prim.height
|
|
84
|
+
};
|
|
85
|
+
let left = Infinity;
|
|
86
|
+
let top = Infinity;
|
|
87
|
+
let right = -Infinity;
|
|
88
|
+
let bottom = -Infinity;
|
|
89
|
+
for (const path of prim.paths) for (const sg of path.segments) {
|
|
90
|
+
if (!("x" in sg)) continue;
|
|
91
|
+
left = Math.min(left, sg.x);
|
|
92
|
+
right = Math.max(right, sg.x);
|
|
93
|
+
top = Math.min(top, sg.y);
|
|
94
|
+
bottom = Math.max(bottom, sg.y);
|
|
95
|
+
}
|
|
96
|
+
return Number.isFinite(left) ? {
|
|
97
|
+
left,
|
|
98
|
+
top,
|
|
99
|
+
right,
|
|
100
|
+
bottom
|
|
101
|
+
} : {
|
|
102
|
+
left: 0,
|
|
103
|
+
top: 0,
|
|
104
|
+
right: 0,
|
|
105
|
+
bottom: 0
|
|
106
|
+
};
|
|
107
|
+
}
|
|
39
108
|
/** A COLORREF (`0x00bbggrr`) as the 6-hex this codebase's colours use. */
|
|
40
109
|
function colorRef(v) {
|
|
41
110
|
return [
|
|
@@ -52,4 +121,4 @@ function applyTransform(t, x, y) {
|
|
|
52
121
|
};
|
|
53
122
|
}
|
|
54
123
|
//#endregion
|
|
55
|
-
export { applyTransform, cloneDc, colorRef, newDeviceContext };
|
|
124
|
+
export { applyTransform, clippedAway, cloneDc, colorRef, intersectClip, newDeviceContext, primBounds };
|