reamkit 1.16.0 → 1.18.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/bidi/segments.js +1 -2
- package/dist/esm/core/converter/project.js +5 -1
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +526 -8
- package/dist/esm/core/drawingml/chart-geometry.d.ts +8 -12
- package/dist/esm/core/drawingml/chart-geometry.js +58 -9
- package/dist/esm/core/drawingml/chart-parser.js +44 -5
- package/dist/esm/core/drawingml/colors.d.ts +1 -1
- package/dist/esm/core/drawingml/colors.js +27 -1
- package/dist/esm/core/drawingml/preset-geometry.js +233 -0
- package/dist/esm/core/drawingml/shape-render.d.ts +12 -0
- package/dist/esm/core/drawingml/shape-render.js +116 -3
- package/dist/esm/core/drawingml/theme-parser.d.ts +10 -0
- package/dist/esm/core/drawingml/theme-parser.js +19 -1
- package/dist/esm/core/font/measure.d.ts +5 -0
- package/dist/esm/core/font/measure.js +21 -1
- package/dist/esm/core/images.d.ts +2 -2
- package/dist/esm/core/images.js +219 -0
- package/dist/esm/core/ir/flow.d.ts +23 -1
- package/dist/esm/core/lzw.d.ts +17 -0
- package/dist/esm/core/lzw.js +83 -0
- package/dist/esm/core/metafile/emf.d.ts +16 -0
- package/dist/esm/core/metafile/emf.js +635 -0
- package/dist/esm/core/metafile/picture.d.ts +106 -0
- package/dist/esm/core/metafile/picture.js +55 -0
- package/dist/esm/core/metafile/wmf.d.ts +9 -0
- package/dist/esm/core/metafile/wmf.js +371 -0
- package/dist/esm/core/numbering/apply.d.ts +5 -3
- package/dist/esm/core/numbering/apply.js +42 -11
- package/dist/esm/core/numbering/index.js +2 -0
- package/dist/esm/core/numbering/state.d.ts +13 -1
- package/dist/esm/core/numbering/state.js +256 -15
- package/dist/esm/core/po-helpers.js +16 -1
- package/dist/esm/core/style-cascade/resolver.js +66 -11
- package/dist/esm/core/style-cascade/table.js +35 -11
- package/dist/esm/core/style-cascade/types.d.ts +42 -1
- package/dist/esm/core/style-cascade/types.js +4 -0
- package/dist/esm/core/tiff.d.ts +20 -0
- package/dist/esm/core/tiff.js +339 -0
- package/dist/esm/core/vector.d.ts +22 -0
- package/dist/esm/excel/print-model.js +4 -2
- package/dist/esm/excel/sheet-to-flow.js +10 -8
- package/dist/esm/excel/worksheet-parser.js +1 -1
- package/dist/esm/html/html-writer.js +20 -5
- package/dist/esm/layout/page-doc.d.ts +107 -4
- package/dist/esm/layout/styled-layout.d.ts +44 -2
- package/dist/esm/layout/styled-layout.js +2330 -283
- package/dist/esm/pdf/shading.js +76 -6
- package/dist/esm/pdf/styled-page-emitter.js +294 -45
- package/dist/esm/pdf/vector-graphics.d.ts +29 -2
- package/dist/esm/pdf/vector-graphics.js +96 -12
- package/dist/esm/pdf-reader/image-decode.js +7 -75
- package/dist/esm/pptx/pptx-reader.js +1 -0
- package/dist/esm/pptx/slide-parser.d.ts +21 -4
- package/dist/esm/pptx/slide-parser.js +86 -23
- package/dist/esm/svg/svg-writer.js +2 -2
- package/dist/esm/word/document-parser.d.ts +96 -7
- package/dist/esm/word/document-parser.js +540 -116
- package/dist/esm/word/docx-reader.js +135 -43
- package/dist/esm/word/docx-writer.js +6 -1
- package/dist/esm/word/drawing-parser.d.ts +58 -5
- package/dist/esm/word/drawing-parser.js +1632 -63
- package/dist/esm/word/index.d.ts +4 -4
- package/dist/esm/word/numbering-parser.d.ts +2 -1
- package/dist/esm/word/numbering-parser.js +107 -21
- package/dist/esm/word/paragraph-properties.d.ts +11 -1
- package/dist/esm/word/paragraph-properties.js +219 -10
- package/dist/esm/word/run-properties.js +45 -1
- package/dist/esm/word/settings-parser.d.ts +24 -0
- package/dist/esm/word/settings-parser.js +31 -5
- package/dist/esm/word/shading.d.ts +11 -0
- package/dist/esm/word/shading.js +64 -0
- package/dist/esm/word/styles-parser.js +8 -2
- package/dist/esm/word/table-parser.d.ts +9 -1
- package/dist/esm/word/table-parser.js +182 -30
- package/package.json +1 -1
|
@@ -14,25 +14,81 @@ import "../core/vector.js";
|
|
|
14
14
|
* named `/ExtGState` (its constant alpha) from the page's resources.
|
|
15
15
|
* @returns The content-stream operator lines.
|
|
16
16
|
*/
|
|
17
|
-
|
|
17
|
+
/**
|
|
18
|
+
* §20.1.8.40 `blurRad` — how a blurred shadow is drawn: as `count` copies of
|
|
19
|
+
* the silhouette, each at `alpha` transparency, growing through the blur's
|
|
20
|
+
* width. Shared with the emitter that registers the /ExtGState for it, so the
|
|
21
|
+
* alpha it registers is the one the layers are drawn at.
|
|
22
|
+
*
|
|
23
|
+
* @param shadow The shadow (its `blurPt` and `alpha` are read).
|
|
24
|
+
* @returns The number of layers and the transparency of each.
|
|
25
|
+
*/
|
|
26
|
+
function shadowBlurLayers(shadow) {
|
|
27
|
+
if (!(shadow.blurPt > 0)) return {
|
|
28
|
+
count: 1,
|
|
29
|
+
alpha: shadow.alpha
|
|
30
|
+
};
|
|
31
|
+
const count = Math.min(8, Math.max(2, Math.round(shadow.blurPt / 2)));
|
|
32
|
+
return {
|
|
33
|
+
count,
|
|
34
|
+
alpha: shadow.alpha / count
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function shadowBbox(shape) {
|
|
38
|
+
let minX = Infinity;
|
|
39
|
+
let minY = Infinity;
|
|
40
|
+
let maxX = -Infinity;
|
|
41
|
+
let maxY = -Infinity;
|
|
42
|
+
for (const path of shape.paths) for (const seg of path.segments) {
|
|
43
|
+
if (seg.op === "close") continue;
|
|
44
|
+
minX = Math.min(minX, seg.x);
|
|
45
|
+
minY = Math.min(minY, seg.y);
|
|
46
|
+
maxX = Math.max(maxX, seg.x);
|
|
47
|
+
maxY = Math.max(maxY, seg.y);
|
|
48
|
+
}
|
|
49
|
+
return Number.isFinite(minX) && maxX > minX && maxY > minY ? {
|
|
50
|
+
minX,
|
|
51
|
+
minY,
|
|
52
|
+
maxX,
|
|
53
|
+
maxY
|
|
54
|
+
} : void 0;
|
|
55
|
+
}
|
|
56
|
+
function emitVectorShape(shape, patternName, alphaStateName, fillAlphaStateName) {
|
|
18
57
|
const out = [];
|
|
19
58
|
const [a, b, c, d, e, f] = shape.transform;
|
|
20
59
|
const shadow = shape.shadow;
|
|
21
60
|
if (shadow) {
|
|
22
|
-
out.push("q");
|
|
23
|
-
if (alphaStateName !== void 0) out.push(`/${alphaStateName} gs`);
|
|
24
|
-
out.push(`${num(a)} ${num(b)} ${num(c)} ${num(d)} ${num(e + shadow.dxPt)} ${num(f - shadow.dyPt)} cm`);
|
|
25
61
|
const [sr, sg, sb] = hexToRgb01(shadow.colorHex);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
62
|
+
const bbox = shadowBbox(shape);
|
|
63
|
+
const scale = Math.sqrt(Math.abs(a * d - b * c)) || 1;
|
|
64
|
+
const { count } = shadowBlurLayers(shadow);
|
|
65
|
+
for (let i = 0; i < count; i++) {
|
|
66
|
+
out.push("q");
|
|
67
|
+
if (alphaStateName !== void 0) out.push(`/${alphaStateName} gs`);
|
|
68
|
+
out.push(`${num(a)} ${num(b)} ${num(c)} ${num(d)} ${num(e + shadow.dxPt)} ${num(f - shadow.dyPt)} cm`);
|
|
69
|
+
if (count > 1 && bbox) {
|
|
70
|
+
const local = shadow.blurPt / scale;
|
|
71
|
+
const grow = -local / 2 + (i + .5) * local / count;
|
|
72
|
+
const halfW = Math.max((bbox.maxX - bbox.minX) / 2, 1e-6);
|
|
73
|
+
const halfH = Math.max((bbox.maxY - bbox.minY) / 2, 1e-6);
|
|
74
|
+
const cx = (bbox.minX + bbox.maxX) / 2;
|
|
75
|
+
const cy = (bbox.minY + bbox.maxY) / 2;
|
|
76
|
+
const sx = Math.max((halfW + grow) / halfW, 0);
|
|
77
|
+
const sy = Math.max((halfH + grow) / halfH, 0);
|
|
78
|
+
out.push(`${num(sx)} 0 0 ${num(sy)} ${num(cx * (1 - sx))} ${num(cy * (1 - sy))} cm`);
|
|
79
|
+
}
|
|
80
|
+
out.push(`${num(sr)} ${num(sg)} ${num(sb)} rg`);
|
|
81
|
+
let shadowEvenodd = false;
|
|
82
|
+
for (const path of shape.paths) {
|
|
83
|
+
if (path.fillRule === "evenodd") shadowEvenodd = true;
|
|
84
|
+
for (const seg of path.segments) out.push(emitSegment(seg));
|
|
85
|
+
}
|
|
86
|
+
out.push(shadowEvenodd ? "f*" : "f");
|
|
87
|
+
out.push("Q");
|
|
31
88
|
}
|
|
32
|
-
out.push(shadowEvenodd ? "f*" : "f");
|
|
33
|
-
out.push("Q");
|
|
34
89
|
}
|
|
35
90
|
out.push("q");
|
|
91
|
+
if (fillAlphaStateName !== void 0) out.push(`/${fillAlphaStateName} gs`);
|
|
36
92
|
out.push(`${num(a)} ${num(b)} ${num(c)} ${num(d)} ${num(e)} ${num(f)} cm`);
|
|
37
93
|
const stroke = shape.stroke;
|
|
38
94
|
if (stroke) {
|
|
@@ -58,6 +114,34 @@ function emitVectorShape(shape, patternName, alphaStateName) {
|
|
|
58
114
|
out.push("Q");
|
|
59
115
|
return out;
|
|
60
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* The operators that make `paths` the current CLIP, under `transform` — the
|
|
119
|
+
* same local→y-up-page matrix {@link emitVectorShape} takes. Emitted inside the
|
|
120
|
+
* caller's own `q`…`Q`, and followed by the inverse matrix so what comes after
|
|
121
|
+
* is back in page coordinates.
|
|
122
|
+
*
|
|
123
|
+
* @param paths The clip outline, in the shape's local frame.
|
|
124
|
+
* @param transform The local→page matrix.
|
|
125
|
+
* @returns The operators: `cm`, the path, `W n`, and the inverse `cm`.
|
|
126
|
+
*/
|
|
127
|
+
function emitClipPath(paths, transform) {
|
|
128
|
+
const [a, b, c, d, e, f] = transform;
|
|
129
|
+
const det = a * d - b * c;
|
|
130
|
+
if (det === 0) return [];
|
|
131
|
+
const out = [`${num(a)} ${num(b)} ${num(c)} ${num(d)} ${num(e)} ${num(f)} cm`];
|
|
132
|
+
let evenodd = false;
|
|
133
|
+
for (const path of paths) {
|
|
134
|
+
if (path.fillRule === "evenodd") evenodd = true;
|
|
135
|
+
for (const seg of path.segments) out.push(emitSegment(seg));
|
|
136
|
+
}
|
|
137
|
+
out.push(evenodd ? "W* n" : "W n");
|
|
138
|
+
const ia = d / det;
|
|
139
|
+
const ib = -b / det;
|
|
140
|
+
const ic = -c / det;
|
|
141
|
+
const id = a / det;
|
|
142
|
+
out.push(`${num(ia)} ${num(ib)} ${num(ic)} ${num(id)} ${num(-(e * ia + f * ic))} ${num(-(e * ib + f * id))} cm`);
|
|
143
|
+
return out;
|
|
144
|
+
}
|
|
61
145
|
function emitSegment(seg) {
|
|
62
146
|
switch (seg.op) {
|
|
63
147
|
case "move": return `${num(seg.x)} ${num(seg.y)} m`;
|
|
@@ -87,4 +171,4 @@ function hexToRgb01(hex) {
|
|
|
87
171
|
];
|
|
88
172
|
}
|
|
89
173
|
//#endregion
|
|
90
|
-
export { emitVectorShape };
|
|
174
|
+
export { emitClipPath, emitVectorShape, shadowBlurLayers };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PDF_NULL, PdfHexString, PdfName, PdfStream } from "../pdf/objects.js";
|
|
2
|
+
import { lzwDecodeMsb } from "../core/lzw.js";
|
|
2
3
|
import { reversePredictor } from "./predictor.js";
|
|
3
4
|
import { decodeCcitt } from "./ccitt.js";
|
|
4
5
|
import { encodePng } from "./png-encode.js";
|
|
@@ -342,9 +343,7 @@ function decodeChain(file, stream, filters) {
|
|
|
342
343
|
return;
|
|
343
344
|
}
|
|
344
345
|
else if (f === "LZWDecode" || f === "LZW") {
|
|
345
|
-
|
|
346
|
-
if (!dec) return void 0;
|
|
347
|
-
data = dec;
|
|
346
|
+
data = lzwDecode(data, lzwEarlyChange(file, stream.dict));
|
|
348
347
|
mayPredict = true;
|
|
349
348
|
} else if (f === "RunLengthDecode" || f === "RL") data = runLengthDecode(data);
|
|
350
349
|
else if (f === "ASCII85Decode" || f === "A85") data = ascii85Decode(data);
|
|
@@ -359,7 +358,7 @@ function applyChainExceptLast(filters, raw) {
|
|
|
359
358
|
if (f === "FlateDecode" || f === "Fl") try {
|
|
360
359
|
data = unzlibSync(data);
|
|
361
360
|
} catch {}
|
|
362
|
-
else if (f === "LZWDecode" || f === "LZW") data = lzwDecode(data, 1)
|
|
361
|
+
else if (f === "LZWDecode" || f === "LZW") data = lzwDecode(data, 1);
|
|
363
362
|
else if (f === "RunLengthDecode" || f === "RL") data = runLengthDecode(data);
|
|
364
363
|
else if (f === "ASCII85Decode" || f === "A85") data = ascii85Decode(data);
|
|
365
364
|
else if (f === "ASCIIHexDecode" || f === "AHx") data = asciiHexDecode(data);
|
|
@@ -380,77 +379,10 @@ function applyPredictor(file, d, data) {
|
|
|
380
379
|
}
|
|
381
380
|
var MAX_LZW_OUT = MAX_PIXELS * 4;
|
|
382
381
|
function lzwDecode(data, earlyChange) {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
if (outLen + e.length > out.length) {
|
|
388
|
-
let cap = out.length * 2;
|
|
389
|
-
while (cap < outLen + e.length) cap *= 2;
|
|
390
|
-
const grown = new Uint8Array(cap);
|
|
391
|
-
grown.set(out.subarray(0, outLen));
|
|
392
|
-
out = grown;
|
|
393
|
-
}
|
|
394
|
-
out.set(e, outLen);
|
|
395
|
-
outLen += e.length;
|
|
396
|
-
return true;
|
|
397
|
-
};
|
|
398
|
-
let bitBuffer = 0;
|
|
399
|
-
let bitCount = 0;
|
|
400
|
-
let pos = 0;
|
|
401
|
-
let codeLength = 9;
|
|
402
|
-
const readCode = () => {
|
|
403
|
-
while (bitCount < codeLength) {
|
|
404
|
-
if (pos >= data.length) return -1;
|
|
405
|
-
bitBuffer = (bitBuffer << 8 | data[pos++]) >>> 0;
|
|
406
|
-
bitCount += 8;
|
|
407
|
-
}
|
|
408
|
-
bitCount -= codeLength;
|
|
409
|
-
return bitBuffer >>> bitCount & (1 << codeLength) - 1;
|
|
410
|
-
};
|
|
411
|
-
const dict = new Array(4096);
|
|
412
|
-
let nextCode = 258;
|
|
413
|
-
let prev = -1;
|
|
414
|
-
const reset = () => {
|
|
415
|
-
for (let i = 0; i < 256; i++) dict[i] = Uint8Array.of(i);
|
|
416
|
-
nextCode = 258;
|
|
417
|
-
codeLength = 9;
|
|
418
|
-
prev = -1;
|
|
419
|
-
};
|
|
420
|
-
reset();
|
|
421
|
-
for (;;) {
|
|
422
|
-
const code = readCode();
|
|
423
|
-
if (code < 0 || code === 257) break;
|
|
424
|
-
if (code === 256) {
|
|
425
|
-
reset();
|
|
426
|
-
continue;
|
|
427
|
-
}
|
|
428
|
-
if (prev < 0) {
|
|
429
|
-
const first = dict[code];
|
|
430
|
-
if (!first || !emit(first)) break;
|
|
431
|
-
prev = code;
|
|
432
|
-
continue;
|
|
433
|
-
}
|
|
434
|
-
const prevEntry = dict[prev];
|
|
435
|
-
let entry = code < nextCode ? dict[code] : void 0;
|
|
436
|
-
if (!entry) {
|
|
437
|
-
entry = new Uint8Array(prevEntry.length + 1);
|
|
438
|
-
entry.set(prevEntry);
|
|
439
|
-
entry[prevEntry.length] = prevEntry[0];
|
|
440
|
-
}
|
|
441
|
-
if (!emit(entry)) break;
|
|
442
|
-
if (nextCode < 4096) {
|
|
443
|
-
const added = new Uint8Array(prevEntry.length + 1);
|
|
444
|
-
added.set(prevEntry);
|
|
445
|
-
added[prevEntry.length] = entry[0];
|
|
446
|
-
dict[nextCode++] = added;
|
|
447
|
-
if (nextCode + earlyChange === 512) codeLength = 10;
|
|
448
|
-
else if (nextCode + earlyChange === 1024) codeLength = 11;
|
|
449
|
-
else if (nextCode + earlyChange === 2048) codeLength = 12;
|
|
450
|
-
}
|
|
451
|
-
prev = code;
|
|
452
|
-
}
|
|
453
|
-
return out.subarray(0, outLen);
|
|
382
|
+
return lzwDecodeMsb(data, {
|
|
383
|
+
earlyChange,
|
|
384
|
+
limit: MAX_LZW_OUT
|
|
385
|
+
});
|
|
454
386
|
}
|
|
455
387
|
function lzwEarlyChange(file, d) {
|
|
456
388
|
const ec = decodeParmsOf(file, d)?.get("EarlyChange");
|
|
@@ -73,16 +73,33 @@ export declare function diagramTransform(spTree: PoNode, frame: ShapeBoxEmu): Gr
|
|
|
73
73
|
* @param where Optional location tag for the loss report (e.g. `slide 3`).
|
|
74
74
|
*/
|
|
75
75
|
export declare function noDiagramOverrideLoss(where?: string): Loss;
|
|
76
|
+
/** One SmartArt node: the box it occupies in the target space, and its shape. */
|
|
77
|
+
export interface DiagramNode {
|
|
78
|
+
readonly box: ShapeBoxEmu;
|
|
79
|
+
readonly shape: ShapeBlock;
|
|
80
|
+
}
|
|
76
81
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
82
|
+
* Read a SmartArt drawing override (a `dsp:spTree`) into its nodes. The `dsp:`
|
|
83
|
+
* wrapper holds an ordinary `a:` `spPr`/`txBody`, so the shared DrawingML
|
|
79
84
|
* readers apply unchanged. Shared by pptx and docx (E-SMARTART); diagrams carry
|
|
80
85
|
* no placeholder cascade.
|
|
81
86
|
*
|
|
87
|
+
* @param spTree The diagram drawing's `dsp:spTree`.
|
|
88
|
+
* @param transform Maps each shape's diagram-space box to the target space.
|
|
89
|
+
* @param colors The colour resolver for the shapes' fills/strokes/text.
|
|
90
|
+
* @param resolveLink A run hyperlink resolver, or `undefined`.
|
|
91
|
+
* @param resolveImage Resolves a picture fill's `r:embed` against the DRAWING
|
|
92
|
+
* part's own relationships, or `undefined`.
|
|
93
|
+
* @returns The diagram's visible nodes, in drawing order.
|
|
94
|
+
*/
|
|
95
|
+
export declare function parseDiagramNodes(spTree: PoNode, transform: GroupTransform, colors: ColorResolver, resolveLink: LinkResolver, resolveImage?: (relId: string) => ResourceId | undefined): Array<DiagramNode>;
|
|
96
|
+
/**
|
|
97
|
+
* The same nodes as free-standing floating shapes — what a slide wants, where
|
|
98
|
+
* every shape is anchored to the page.
|
|
99
|
+
*
|
|
82
100
|
* @param spTree The diagram drawing's `dsp:spTree`.
|
|
83
101
|
* @param transform Maps each shape's diagram-space box to the target space.
|
|
84
|
-
* @param makeFloat Anchors a box
|
|
85
|
-
* relative for an inline docx diagram.
|
|
102
|
+
* @param makeFloat Anchors a node's box.
|
|
86
103
|
* @param colors The colour resolver for the shapes' fills/strokes/text.
|
|
87
104
|
* @param resolveLink A run hyperlink resolver, or `undefined`.
|
|
88
105
|
* @returns The diagram's visible shapes as positioned {@link ShapeBlock}s.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { emuToPt, pt } from "../core/ir/units.js";
|
|
2
2
|
import { FEATURES } from "../core/ir/features.js";
|
|
3
|
-
import { poAttr, poChildren, poFindDescendant, poIntAttr, poIs, poText } from "../core/po-helpers.js";
|
|
3
|
+
import { poAttr, poChildren, poFindDescendant, poIntAttr, poIs, poTag, poText } from "../core/po-helpers.js";
|
|
4
4
|
import { defaultColorResolver, resolveColorNode } from "../core/drawingml/colors.js";
|
|
5
5
|
import { boxFromXfrm, parsePh, parseXfrmBox, rPrToRunProps } from "./sp-helpers.js";
|
|
6
|
-
import { parseCustGeom, parseFill, parseLine, parsePrstGeom } from "../word/drawing-parser.js";
|
|
6
|
+
import { parseCustGeom, parseFill, parseLine, parsePrstGeom, parseShadow, parseXfrm } from "../word/drawing-parser.js";
|
|
7
7
|
//#region src/pptx/slide-parser.ts
|
|
8
8
|
var CHART_URI = "http://schemas.openxmlformats.org/drawingml/2006/chart";
|
|
9
9
|
var TABLE_URI = "http://schemas.openxmlformats.org/drawingml/2006/table";
|
|
@@ -206,20 +206,20 @@ function noDiagramOverrideLoss(where) {
|
|
|
206
206
|
};
|
|
207
207
|
}
|
|
208
208
|
/**
|
|
209
|
-
*
|
|
210
|
-
*
|
|
209
|
+
* Read a SmartArt drawing override (a `dsp:spTree`) into its nodes. The `dsp:`
|
|
210
|
+
* wrapper holds an ordinary `a:` `spPr`/`txBody`, so the shared DrawingML
|
|
211
211
|
* readers apply unchanged. Shared by pptx and docx (E-SMARTART); diagrams carry
|
|
212
212
|
* no placeholder cascade.
|
|
213
213
|
*
|
|
214
|
-
* @param spTree
|
|
215
|
-
* @param transform
|
|
216
|
-
* @param
|
|
217
|
-
*
|
|
218
|
-
* @param
|
|
219
|
-
*
|
|
220
|
-
* @returns The diagram's visible
|
|
214
|
+
* @param spTree The diagram drawing's `dsp:spTree`.
|
|
215
|
+
* @param transform Maps each shape's diagram-space box to the target space.
|
|
216
|
+
* @param colors The colour resolver for the shapes' fills/strokes/text.
|
|
217
|
+
* @param resolveLink A run hyperlink resolver, or `undefined`.
|
|
218
|
+
* @param resolveImage Resolves a picture fill's `r:embed` against the DRAWING
|
|
219
|
+
* part's own relationships, or `undefined`.
|
|
220
|
+
* @returns The diagram's visible nodes, in drawing order.
|
|
221
221
|
*/
|
|
222
|
-
function
|
|
222
|
+
function parseDiagramNodes(spTree, transform, colors, resolveLink, resolveImage) {
|
|
223
223
|
const out = [];
|
|
224
224
|
for (const sp of poChildren(spTree)) {
|
|
225
225
|
if (!poIs(sp, "dsp:sp")) continue;
|
|
@@ -228,26 +228,89 @@ function parseDiagramDrawing(spTree, transform, makeFloat, colors, resolveLink)
|
|
|
228
228
|
if (!own) continue;
|
|
229
229
|
const box = transform(own);
|
|
230
230
|
const txBody = poChildren(sp).find((c) => poIs(c, "dsp:txBody"));
|
|
231
|
-
const
|
|
231
|
+
const parsed = txBody ? parseTxBody(txBody, void 0, void 0, colors, resolveLink) : void 0;
|
|
232
|
+
const text = parsed ? withDiagramFontColor(parsed, sp, colors) : void 0;
|
|
232
233
|
const geometry = parseGeometry(spPr);
|
|
233
|
-
const fill = spPr ? parseFill(spPr, colors) : { kind: "none" };
|
|
234
|
+
const fill = spPr ? parseFill(spPr, colors, resolveImage) : { kind: "none" };
|
|
234
235
|
const line = spPr ? parseLine(spPr, colors) : void 0;
|
|
236
|
+
const shadow = spPr ? parseShadow(spPr, colors) : void 0;
|
|
235
237
|
const visibleLine = line !== void 0 && line.fill !== "none";
|
|
236
238
|
if (!text && fill.kind === "none" && !visibleLine) continue;
|
|
239
|
+
const xfrm = spPr ? poChildren(spPr).find((c) => poIs(c, "a:xfrm")) : void 0;
|
|
240
|
+
const spin = xfrm ? parseXfrm(xfrm) : void 0;
|
|
241
|
+
const txBox = (spin?.rotation60k ?? 0) % 216e5 !== 0 ? boxFromXfrm(poChildren(sp).find((c) => poIs(c, "dsp:txXfrm"))) : void 0;
|
|
237
242
|
out.push({
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
243
|
+
box,
|
|
244
|
+
shape: {
|
|
245
|
+
width: emuToPt(box.cx),
|
|
246
|
+
height: emuToPt(box.cy),
|
|
247
|
+
geometry,
|
|
248
|
+
fill,
|
|
249
|
+
...line ? { line } : {},
|
|
250
|
+
...text && !txBox ? { text } : {},
|
|
251
|
+
...spin && Object.keys(spin).length > 0 ? { transform: spin } : {},
|
|
252
|
+
...shadow ? { shadow } : {},
|
|
253
|
+
paragraphProperties: {}
|
|
254
|
+
}
|
|
246
255
|
});
|
|
256
|
+
if (text && txBox) {
|
|
257
|
+
const placed = transform(txBox);
|
|
258
|
+
out.push({
|
|
259
|
+
box: placed,
|
|
260
|
+
shape: {
|
|
261
|
+
width: emuToPt(placed.cx),
|
|
262
|
+
height: emuToPt(placed.cy),
|
|
263
|
+
geometry: RECT_GEOMETRY,
|
|
264
|
+
fill: { kind: "none" },
|
|
265
|
+
text,
|
|
266
|
+
paragraphProperties: {}
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
247
270
|
}
|
|
248
271
|
return out;
|
|
249
272
|
}
|
|
250
273
|
/**
|
|
274
|
+
* The same nodes as free-standing floating shapes — what a slide wants, where
|
|
275
|
+
* every shape is anchored to the page.
|
|
276
|
+
*
|
|
277
|
+
* @param spTree The diagram drawing's `dsp:spTree`.
|
|
278
|
+
* @param transform Maps each shape's diagram-space box to the target space.
|
|
279
|
+
* @param makeFloat Anchors a node's box.
|
|
280
|
+
* @param colors The colour resolver for the shapes' fills/strokes/text.
|
|
281
|
+
* @param resolveLink A run hyperlink resolver, or `undefined`.
|
|
282
|
+
* @returns The diagram's visible shapes as positioned {@link ShapeBlock}s.
|
|
283
|
+
*/
|
|
284
|
+
function parseDiagramDrawing(spTree, transform, makeFloat, colors, resolveLink) {
|
|
285
|
+
return parseDiagramNodes(spTree, transform, colors, resolveLink).map(({ box, shape }) => ({
|
|
286
|
+
...shape,
|
|
287
|
+
float: makeFloat(box)
|
|
288
|
+
}));
|
|
289
|
+
}
|
|
290
|
+
function withDiagramFontColor(text, sp, colors) {
|
|
291
|
+
const style = poChildren(sp).find((c) => poIs(c, "dsp:style"));
|
|
292
|
+
const ref = style ? poChildren(style).find((c) => poIs(c, "a:fontRef")) : void 0;
|
|
293
|
+
const child = ref ? poChildren(ref).find((c) => poTag(c) !== void 0) : void 0;
|
|
294
|
+
const colorHex = child ? resolveColorNode(child, colors) : void 0;
|
|
295
|
+
if (colorHex === void 0) return text;
|
|
296
|
+
return {
|
|
297
|
+
...text,
|
|
298
|
+
content: text.content.map((block) => block.kind === "paragraph" ? {
|
|
299
|
+
...block,
|
|
300
|
+
paragraph: {
|
|
301
|
+
...block.paragraph,
|
|
302
|
+
runs: block.paragraph.runs.map((run) => run.properties.colorHex === void 0 ? {
|
|
303
|
+
...run,
|
|
304
|
+
properties: {
|
|
305
|
+
...run.properties,
|
|
306
|
+
colorHex
|
|
307
|
+
}
|
|
308
|
+
} : run)
|
|
309
|
+
}
|
|
310
|
+
} : block)
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
251
314
|
* `p:bg` → the background fill, or `undefined` when none/unsupported. `p:bgPr`
|
|
252
315
|
* carries the fill directly (`a:solidFill`/`a:gradFill` — the same children
|
|
253
316
|
* `parseFill` reads; a picture background is not yet supported); `p:bgRef` (a
|
|
@@ -513,4 +576,4 @@ function cellFillHex(tcPr, colors) {
|
|
|
513
576
|
}
|
|
514
577
|
}
|
|
515
578
|
//#endregion
|
|
516
|
-
export { backdropElement, diagramTransform, noDiagramOverrideLoss, parseBackgroundFill,
|
|
579
|
+
export { backdropElement, diagramTransform, noDiagramOverrideLoss, parseBackgroundFill, parseDiagramNodes, parseGeometry, parseSlideShapes, parseTxBody };
|
|
@@ -87,7 +87,7 @@ function emitPage(out, page, laid, losses, idc) {
|
|
|
87
87
|
x2,
|
|
88
88
|
yBottom
|
|
89
89
|
];
|
|
90
|
-
out.push(`<line x1="${fmt(ax)}" y1="${fmt(ay)}" x2="${fmt(bx)}" y2="${fmt(by)}" stroke="#${b.borderColorHex}" stroke-width="${fmt(b.borderSizePt)}"${b.borderStyle === "dashed" ?
|
|
90
|
+
out.push(`<line x1="${fmt(ax)}" y1="${fmt(ay)}" x2="${fmt(bx)}" y2="${fmt(by)}" stroke="#${b.borderColorHex}" stroke-width="${fmt(b.borderSizePt)}"${b.borderStyle === "dashed" ? " stroke-dasharray=\"8 2.5\"" : b.borderStyle === "dashSmallGap" ? " stroke-dasharray=\"3 1\"" : b.borderStyle === "dotted" ? " stroke-dasharray=\"0.5 1\"" : ""}/>`);
|
|
91
91
|
}
|
|
92
92
|
for (const sh of plan.shapes) emitShape(out, sh.shape, idc);
|
|
93
93
|
for (const t of plan.lines) emitTextLine(out, t, losses, idc);
|
|
@@ -156,7 +156,7 @@ function imageHref(resourceName, laid) {
|
|
|
156
156
|
for (const [resourceId, res] of laid.imageResources) {
|
|
157
157
|
if (res.resourceName !== resourceName) continue;
|
|
158
158
|
const bytes = laid.resources.get(resourceId);
|
|
159
|
-
if (!bytes) return void 0;
|
|
159
|
+
if (!bytes || !res.prepared) return void 0;
|
|
160
160
|
return `data:${res.prepared.mimeType};base64,${toBase64(bytes)}`;
|
|
161
161
|
}
|
|
162
162
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { BodyElement, Comment, Section, SectionProperties } from '../core/document-model/index.js';
|
|
1
|
+
import { BodyElement, Comment, RunProperties, Section, SectionProperties, ShapeFill } from '../core/document-model/index.js';
|
|
2
2
|
import { ColorResolver } from '../core/drawingml/colors.js';
|
|
3
3
|
import { Loss, ResourceId } from '../core/ir/index.js';
|
|
4
4
|
import { PoNode } from '../core/po-helpers.js';
|
|
5
|
+
import { ThemeStyles } from './drawing-parser.js';
|
|
5
6
|
/**
|
|
6
7
|
* Resolves a drawing relationship id to a content-addressed `ResourceId` —
|
|
7
8
|
* supplied by the converter (which owns the OPC package and the `ResourceStore`).
|
|
@@ -9,6 +10,15 @@ import { PoNode } from '../core/po-helpers.js';
|
|
|
9
10
|
export type ImageResolver = (relId: string) => ResourceId | undefined;
|
|
10
11
|
/** Resolves a `w:hyperlink` `r:id` to its external target URL. */
|
|
11
12
|
export type HyperlinkResolver = (relId: string) => string | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* A SmartArt diagram's pre-rendered drawing override: its `dsp:spTree`, plus a
|
|
15
|
+
* resolver for the picture fills its nodes name — those relationships belong to
|
|
16
|
+
* the drawing part, not to the part that references the diagram.
|
|
17
|
+
*/
|
|
18
|
+
export interface ResolvedDiagram {
|
|
19
|
+
readonly spTree: PoNode;
|
|
20
|
+
readonly resolveImage?: ImageResolver;
|
|
21
|
+
}
|
|
12
22
|
/**
|
|
13
23
|
* Document-wide resolvers every nested parser needs — one context object instead
|
|
14
24
|
* of threading a parameter pair through ten signatures (oop-design §8).
|
|
@@ -25,10 +35,28 @@ export interface ParseContext {
|
|
|
25
35
|
readonly resolveHyperlink?: HyperlinkResolver;
|
|
26
36
|
/**
|
|
27
37
|
* SmartArt: a data relationship id (`dgm:relIds` `@r:dm`) → the diagram's
|
|
28
|
-
* pre-rendered drawing override
|
|
29
|
-
*
|
|
38
|
+
* pre-rendered drawing override, or `undefined` when the file ships none
|
|
39
|
+
* (E-SMARTART SA2).
|
|
40
|
+
*/
|
|
41
|
+
readonly resolveDiagram?: (relId: string) => ResolvedDiagram | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* §21.2 a `c:chart` `@r:id` → the chart part's path, the key the reader files
|
|
44
|
+
* parsed charts under. Relationship ids are scoped to their owning part, so a
|
|
45
|
+
* footer's `rId1` and the body's `rId1` are different charts. Absent ⇒ the
|
|
46
|
+
* raw rel id is kept.
|
|
47
|
+
*/
|
|
48
|
+
readonly resolveChartPart?: (relId: string) => string | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* §20.1.4.2.19 — the theme's `a:lnStyleLst` widths in points, which a
|
|
51
|
+
* gallery-styled shape's `a:lnRef idx` indexes for its outline weight.
|
|
30
52
|
*/
|
|
31
|
-
readonly
|
|
53
|
+
readonly themeLineWidths?: ReadonlyArray<number>;
|
|
54
|
+
/**
|
|
55
|
+
* §20.1.4.1.14/§20.1.4.1.15 — the theme's format scheme: the fill, background
|
|
56
|
+
* fill and effect styles a `<wps:style>` reference indexes for the fill and
|
|
57
|
+
* the shadow a gallery-drawn shape wears.
|
|
58
|
+
*/
|
|
59
|
+
readonly themeStyles?: ThemeStyles;
|
|
32
60
|
/**
|
|
33
61
|
* Sink for graceful-degradation notices (E-SMARTART SA3): a SmartArt with no
|
|
34
62
|
* drawing override records a dropped-feature {@link Loss} rather than vanishing.
|
|
@@ -41,6 +69,12 @@ export interface ParseContext {
|
|
|
41
69
|
* reference is readonly; its contents mutate during the walk (CM2c).
|
|
42
70
|
*/
|
|
43
71
|
readonly openCommentRanges?: Set<string>;
|
|
72
|
+
/**
|
|
73
|
+
* §17.3.1.1/§17.3.1.3 — what `w:beforeAutospacing`/`w:afterAutospacing`
|
|
74
|
+
* resolve to for THIS document: 14pt when it states no compatibility mode,
|
|
75
|
+
* and nothing (the default) for every Word 2007-or-later one.
|
|
76
|
+
*/
|
|
77
|
+
readonly autoSpacingPt?: number;
|
|
44
78
|
}
|
|
45
79
|
/** The default {@link ParseContext} — just the default colour resolver. */
|
|
46
80
|
export declare const DEFAULT_PARSE_CONTEXT: ParseContext;
|
|
@@ -53,8 +87,55 @@ export declare const DEFAULT_PARSE_CONTEXT: ParseContext;
|
|
|
53
87
|
* @param ctx The document-wide parse context.
|
|
54
88
|
* @returns The body elements; empty when the `w:body` is absent.
|
|
55
89
|
*/
|
|
56
|
-
export declare function parseDocument(documentXml: Uint8Array, ctx?: ParseContext): Array<BodyElement>;
|
|
57
|
-
/**
|
|
90
|
+
export declare function parseDocument(documentXml: Uint8Array, ctx?: ParseContext, blocks?: BlockCounter): Array<BodyElement>;
|
|
91
|
+
/**
|
|
92
|
+
* §17.2.1 `w:background` — the colour the whole page is painted, as a six-digit
|
|
93
|
+
* hex. Read from `@w:color` (Word's own attribute), falling back to the VML
|
|
94
|
+
* `v:background @fillcolor` beside it. `auto` and a gradient-only background
|
|
95
|
+
* (which carries no flat colour of its own) both come back undefined.
|
|
96
|
+
*
|
|
97
|
+
* @param documentXml The raw `document.xml` bytes.
|
|
98
|
+
* @returns The colour, or undefined when the document names none.
|
|
99
|
+
*/
|
|
100
|
+
export declare function parseBackgroundColor(documentXml: Uint8Array): string | undefined;
|
|
101
|
+
/**
|
|
102
|
+
* §17.2.1 — the page background as the FILL it is. `@w:color` is only the flat
|
|
103
|
+
* fallback: the `v:background` beside it carries the gradient or the picture
|
|
104
|
+
* Word actually paints, and both references draw those. fill.docx runs a
|
|
105
|
+
* five-stop radial sweep where we painted its fallback navy, and
|
|
106
|
+
* tdf126533_pageBitmap.docx papers the page with an image.
|
|
107
|
+
*
|
|
108
|
+
* @param documentXml The raw `document.xml` bytes.
|
|
109
|
+
* @param resolveImage Resolver for a picture background's `r:id`.
|
|
110
|
+
* @returns The fill, or undefined when the document names no background.
|
|
111
|
+
*/
|
|
112
|
+
export declare function parseBackgroundFill(documentXml: Uint8Array, resolveImage?: (relId: string) => ResourceId | undefined): ShapeFill | undefined;
|
|
113
|
+
/**
|
|
114
|
+
* Lines a parsed body up with anything counted in SOURCE blocks. `index[i]` is
|
|
115
|
+
* the ordinal of the `w:p`/`w:tbl` that produced body element `i`; `next` is the
|
|
116
|
+
* running count. A paragraph carrying anchored drawings produces several
|
|
117
|
+
* elements, so the two counts diverge — and {@link parseSections} counts blocks.
|
|
118
|
+
*/
|
|
119
|
+
export interface BlockCounter {
|
|
120
|
+
readonly index: Array<number>;
|
|
121
|
+
next: number;
|
|
122
|
+
}
|
|
123
|
+
/** A fresh {@link BlockCounter}. */
|
|
124
|
+
export declare function newBlockCounter(): BlockCounter;
|
|
125
|
+
/**
|
|
126
|
+
* Translate a section boundary counted in source blocks into one counted in body
|
|
127
|
+
* elements: the number of elements that came from a block before `blockEnd`.
|
|
128
|
+
*
|
|
129
|
+
* @param blocks The counter filled while parsing the body.
|
|
130
|
+
* @param blockEnd The section's `endIndex`, in source blocks.
|
|
131
|
+
* @returns The matching body-element index.
|
|
132
|
+
*/
|
|
133
|
+
export declare function bodyIndexForBlock(blocks: BlockCounter, blockEnd: number): number;
|
|
134
|
+
/**
|
|
135
|
+
* The empty {@link SectionProperties} fallback (no headers/footers). §17.6.13 —
|
|
136
|
+
* on the page a document with no `w:sectPr` of its own is made on, which is the
|
|
137
|
+
* same US Letter a section with no `w:pgSz` gets.
|
|
138
|
+
*/
|
|
58
139
|
export declare const EMPTY_SECTION: SectionProperties;
|
|
59
140
|
/**
|
|
60
141
|
* Parse the document's final section properties (ECMA-376 Part 1 §17.6.17 — the
|
|
@@ -96,7 +177,15 @@ export declare function parseHeaderFooter(xml: Uint8Array, ctx?: ParseContext):
|
|
|
96
177
|
* @param ctx The document-wide parse context.
|
|
97
178
|
* @returns The parsed body elements.
|
|
98
179
|
*/
|
|
99
|
-
export declare function parseBodyElements(children: ReadonlyArray<PoNode>, ctx?: ParseContext): Array<BodyElement>;
|
|
180
|
+
export declare function parseBodyElements(children: ReadonlyArray<PoNode>, ctx?: ParseContext, blocks?: BlockCounter, sdtRunProps?: RunProperties): Array<BodyElement>;
|
|
181
|
+
/**
|
|
182
|
+
* §17.5.2.28 `w:sdt/w:sdtPr/w:rPr` — the run properties a content control
|
|
183
|
+
* lends what it holds, when it states any.
|
|
184
|
+
*
|
|
185
|
+
* @param sdt The `w:sdt` element.
|
|
186
|
+
* @returns The properties, or `undefined` when the control states none.
|
|
187
|
+
*/
|
|
188
|
+
export declare function sdtRunProperties(sdt: PoNode): RunProperties | undefined;
|
|
100
189
|
/**
|
|
101
190
|
* Parse `footnotes.xml` / `endnotes.xml` (§17.11) into note content by id. The
|
|
102
191
|
* separator / continuationSeparator / continuationNotice stubs (negative ids or
|