reamkit 1.9.0 → 1.11.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/README.md +11 -5
- package/dist/esm/core/converter/facade.js +2 -0
- package/dist/esm/core/converter/ream.js +1 -0
- package/dist/esm/core/font/measure.d.ts +1 -1
- package/dist/esm/core/font/measure.js +5 -3
- package/dist/esm/core/font/ttf-parser.d.ts +9 -0
- package/dist/esm/core/font/ttf-parser.js +27 -2
- package/dist/esm/core/fonts/provider.d.ts +2 -1
- package/dist/esm/core/fonts/provider.js +2 -1
- package/dist/esm/core/fonts/remote-fonts.d.ts +1 -1
- package/dist/esm/core/fonts/remote-fonts.js +32 -11
- package/dist/esm/core/line-breaker/greedy.d.ts +2 -0
- package/dist/esm/core/line-breaker/greedy.js +67 -0
- package/dist/esm/core/line-breaker/index.d.ts +1 -0
- package/dist/esm/core/line-breaker/knuth-plass.d.ts +1 -0
- package/dist/esm/core/line-breaker/knuth-plass.js +3 -3
- package/dist/esm/excel/xlsx-to-pdf.js +1 -1
- package/dist/esm/layout/page-doc.d.ts +2 -0
- package/dist/esm/layout/styled-layout.d.ts +2 -0
- package/dist/esm/layout/styled-layout.js +45 -10
- package/dist/esm/pptx/placeholder-cascade.d.ts +9 -0
- package/dist/esm/pptx/placeholder-cascade.js +77 -0
- package/dist/esm/pptx/pptx-reader.d.ts +4 -0
- package/dist/esm/pptx/pptx-reader.js +213 -0
- package/dist/esm/pptx/slide-parser.d.ts +18 -0
- package/dist/esm/pptx/slide-parser.js +359 -0
- package/dist/esm/pptx/sp-helpers.d.ts +17 -0
- package/dist/esm/pptx/sp-helpers.js +60 -0
- package/dist/esm/word/docx-to-pdf.js +2 -2
- package/dist/esm/word/drawing-parser.d.ts +4 -0
- package/dist/esm/word/drawing-parser.js +1 -1
- package/package.json +4 -2
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { pt } from "../core/ir/units.js";
|
|
2
|
+
import { ResourceStore } from "../core/ir/resources.js";
|
|
3
|
+
import { FEATURES } from "../core/ir/features.js";
|
|
4
|
+
import { EMPTY_STYLE_SHEET, resolveBodyStyles } from "../core/style-cascade/resolver.js";
|
|
5
|
+
import "../core/style-cascade/index.js";
|
|
6
|
+
import { bytesInclude } from "../core/bytes.js";
|
|
7
|
+
import { poAttr, poChildren, poIntAttr, poIs } from "../core/po-helpers.js";
|
|
8
|
+
import { DEFAULT_THEME_PALETTE, defaultColorResolver, makeColorResolver } from "../core/drawingml/colors.js";
|
|
9
|
+
import { parseChart, withChartColorStyle } from "../core/drawingml/chart-parser.js";
|
|
10
|
+
import { parseTheme } from "../core/drawingml/theme-parser.js";
|
|
11
|
+
import { OpcPackage } from "../core/opc/package.js";
|
|
12
|
+
import "../core/opc/index.js";
|
|
13
|
+
import { buildPlaceholderCascade } from "./placeholder-cascade.js";
|
|
14
|
+
import { backdropElement, parseBackgroundFill, parseSlideShapes } from "./slide-parser.js";
|
|
15
|
+
import { XMLParser } from "fast-xml-parser";
|
|
16
|
+
//#region src/pptx/pptx-reader.ts
|
|
17
|
+
var EMU_PER_PT = 12700;
|
|
18
|
+
var DEFAULT_CX = 9144e3;
|
|
19
|
+
var DEFAULT_CY = 6858e3;
|
|
20
|
+
var decoder = new TextDecoder();
|
|
21
|
+
var parser = new XMLParser({
|
|
22
|
+
ignoreAttributes: false,
|
|
23
|
+
attributeNamePrefix: "@_",
|
|
24
|
+
preserveOrder: true,
|
|
25
|
+
parseAttributeValue: false,
|
|
26
|
+
parseTagValue: false,
|
|
27
|
+
trimValues: false
|
|
28
|
+
});
|
|
29
|
+
function readPptx(bytes) {
|
|
30
|
+
const losses = [];
|
|
31
|
+
const pkg = OpcPackage.open(bytes);
|
|
32
|
+
const presPath = pkg.getMainDocumentPath();
|
|
33
|
+
const presData = pkg.getPart(presPath);
|
|
34
|
+
const resources = new ResourceStore();
|
|
35
|
+
const charts = /* @__PURE__ */ new Map();
|
|
36
|
+
let cx = DEFAULT_CX;
|
|
37
|
+
let cy = DEFAULT_CY;
|
|
38
|
+
const slideParts = [];
|
|
39
|
+
if (presData) {
|
|
40
|
+
const pres = parser.parse(decoder.decode(presData)).find((n) => poIs(n, "p:presentation"));
|
|
41
|
+
const kids = pres ? poChildren(pres) : [];
|
|
42
|
+
const sldSz = kids.find((c) => poIs(c, "p:sldSz"));
|
|
43
|
+
cx = (sldSz ? poIntAttr(sldSz, "cx") : void 0) || DEFAULT_CX;
|
|
44
|
+
cy = (sldSz ? poIntAttr(sldSz, "cy") : void 0) || DEFAULT_CY;
|
|
45
|
+
const slideRelById = new Map(pkg.getPartRelationships(presPath).filter((r) => r.type.endsWith("/slide")).map((r) => [r.id, r]));
|
|
46
|
+
const lst = kids.find((c) => poIs(c, "p:sldIdLst"));
|
|
47
|
+
const ids = lst ? poChildren(lst).filter((c) => poIs(c, "p:sldId")) : [];
|
|
48
|
+
for (const sldId of ids) {
|
|
49
|
+
const rid = poAttr(sldId, "r:id");
|
|
50
|
+
const rel = rid !== void 0 ? slideRelById.get(rid) : void 0;
|
|
51
|
+
const part = rel ? pkg.resolveRelatedPart(presPath, rel) : void 0;
|
|
52
|
+
if (part) slideParts.push(part);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (slideParts.length === 0) losses.push({
|
|
56
|
+
severity: "dropped",
|
|
57
|
+
feature: FEATURES.text,
|
|
58
|
+
detail: "presentation has no slides"
|
|
59
|
+
});
|
|
60
|
+
const slideCount = Math.max(1, slideParts.length);
|
|
61
|
+
const pageW = pt(cx / EMU_PER_PT);
|
|
62
|
+
const pageH = pt(cy / EMU_PER_PT);
|
|
63
|
+
const stylesByLayout = /* @__PURE__ */ new Map();
|
|
64
|
+
const body = [];
|
|
65
|
+
for (let i = 0; i < slideCount; i++) {
|
|
66
|
+
body.push({
|
|
67
|
+
kind: "paragraph",
|
|
68
|
+
paragraph: {
|
|
69
|
+
properties: i > 0 ? { pageBreakBefore: true } : {},
|
|
70
|
+
runs: [{
|
|
71
|
+
text: "",
|
|
72
|
+
properties: {}
|
|
73
|
+
}]
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
const part = slideParts[i];
|
|
77
|
+
if (part) {
|
|
78
|
+
const styles = slideStylesFor(pkg, part.path, stylesByLayout);
|
|
79
|
+
const ctx = {
|
|
80
|
+
...styles.cascade ? { cascade: styles.cascade } : {},
|
|
81
|
+
colors: styles.colors,
|
|
82
|
+
resolveImage: makeSlideImageResolver(pkg, part.path, resources),
|
|
83
|
+
resolveChart: makeSlideChartResolver(pkg, part.path, charts, styles.colors),
|
|
84
|
+
resolveHyperlink: makeHyperlinkResolver(pkg, part.path)
|
|
85
|
+
};
|
|
86
|
+
body.push(...parseSlide(part.data, ctx, styles.background, pageW, pageH));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const section = {
|
|
90
|
+
pageSize: {
|
|
91
|
+
width: pageW,
|
|
92
|
+
height: pageH
|
|
93
|
+
},
|
|
94
|
+
margins: {
|
|
95
|
+
top: pt(0),
|
|
96
|
+
right: pt(0),
|
|
97
|
+
bottom: pt(0),
|
|
98
|
+
left: pt(0)
|
|
99
|
+
},
|
|
100
|
+
headers: [],
|
|
101
|
+
footers: []
|
|
102
|
+
};
|
|
103
|
+
return {
|
|
104
|
+
doc: {
|
|
105
|
+
kind: "flow",
|
|
106
|
+
body: resolveBodyStyles([...body], EMPTY_STYLE_SHEET),
|
|
107
|
+
sections: [],
|
|
108
|
+
section,
|
|
109
|
+
styles: EMPTY_STYLE_SHEET,
|
|
110
|
+
resources,
|
|
111
|
+
...charts.size > 0 ? { charts } : {}
|
|
112
|
+
},
|
|
113
|
+
losses
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function parseXml(data) {
|
|
117
|
+
return parser.parse(decoder.decode(data));
|
|
118
|
+
}
|
|
119
|
+
function parseSlide(data, ctx, inheritedBg, pageW, pageH) {
|
|
120
|
+
const sld = parseXml(data).find((n) => poIs(n, "p:sld"));
|
|
121
|
+
const cSld = sld ? poChildren(sld).find((c) => poIs(c, "p:cSld")) : void 0;
|
|
122
|
+
const colors = ctx.colors ?? defaultColorResolver;
|
|
123
|
+
const bgNode = cSld ? poChildren(cSld).find((c) => poIs(c, "p:bg")) : void 0;
|
|
124
|
+
const bg = (bgNode ? parseBackgroundFill(bgNode, colors) : void 0) ?? inheritedBg;
|
|
125
|
+
const spTree = cSld ? poChildren(cSld).find((c) => poIs(c, "p:spTree")) : void 0;
|
|
126
|
+
const out = [];
|
|
127
|
+
if (bg) out.push(backdropElement(bg, pageW, pageH));
|
|
128
|
+
if (spTree) out.push(...parseSlideShapes(spTree, ctx));
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
131
|
+
function partBackground(tree, root, colors) {
|
|
132
|
+
const sld = tree.find((n) => poIs(n, root));
|
|
133
|
+
const cSld = sld ? poChildren(sld).find((c) => poIs(c, "p:cSld")) : void 0;
|
|
134
|
+
const bg = cSld ? poChildren(cSld).find((c) => poIs(c, "p:bg")) : void 0;
|
|
135
|
+
return bg ? parseBackgroundFill(bg, colors) : void 0;
|
|
136
|
+
}
|
|
137
|
+
function makeSlideImageResolver(pkg, slidePath, resources) {
|
|
138
|
+
const cache = /* @__PURE__ */ new Map();
|
|
139
|
+
return (relId) => {
|
|
140
|
+
if (cache.has(relId)) return cache.get(relId);
|
|
141
|
+
const rel = pkg.getPartRelationships(slidePath).find((r) => r.id === relId);
|
|
142
|
+
const resolved = rel ? pkg.resolveRelatedPart(slidePath, rel) : void 0;
|
|
143
|
+
const id = resolved ? resources.put(resolved.data) : void 0;
|
|
144
|
+
cache.set(relId, id);
|
|
145
|
+
return id;
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function makeHyperlinkResolver(pkg, slidePath) {
|
|
149
|
+
return (relId) => {
|
|
150
|
+
const rel = pkg.getPartRelationships(slidePath).find((r) => r.id === relId);
|
|
151
|
+
return rel && rel.targetMode === "External" ? rel.target : void 0;
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function makeSlideChartResolver(pkg, slidePath, charts, colors) {
|
|
155
|
+
const cache = /* @__PURE__ */ new Map();
|
|
156
|
+
return (relId) => {
|
|
157
|
+
if (cache.has(relId)) return cache.get(relId);
|
|
158
|
+
const rel = pkg.getPartRelationships(slidePath).find((r) => r.id === relId);
|
|
159
|
+
const resolved = rel ? pkg.resolveRelatedPart(slidePath, rel) : void 0;
|
|
160
|
+
const chart = resolved ? parseChart(resolved.data, colors) : null;
|
|
161
|
+
let key;
|
|
162
|
+
if (chart && resolved) {
|
|
163
|
+
key = `${slidePath}!${relId}`;
|
|
164
|
+
charts.set(key, withChartColorStyle(chart, pkg, resolved.path, colors));
|
|
165
|
+
}
|
|
166
|
+
cache.set(relId, key);
|
|
167
|
+
return key;
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function slideStylesFor(pkg, slidePath, cache) {
|
|
171
|
+
const layoutRel = pkg.getPartRelationships(slidePath).find((r) => r.type.endsWith("/slideLayout"));
|
|
172
|
+
const layout = layoutRel ? pkg.resolveRelatedPart(slidePath, layoutRel) : void 0;
|
|
173
|
+
if (!layout) return { colors: defaultColorResolver };
|
|
174
|
+
const cached = cache.get(layout.path);
|
|
175
|
+
if (cached) return cached;
|
|
176
|
+
const masterRel = pkg.getPartRelationships(layout.path).find((r) => r.type.endsWith("/slideMaster"));
|
|
177
|
+
const master = masterRel ? pkg.resolveRelatedPart(layout.path, masterRel) : void 0;
|
|
178
|
+
const colors = master ? deckColorResolver(pkg, master.path) : defaultColorResolver;
|
|
179
|
+
const layoutTree = parseXml(layout.data);
|
|
180
|
+
const masterTree = master ? parseXml(master.data) : void 0;
|
|
181
|
+
const cascade = buildPlaceholderCascade(layoutTree, masterTree, colors);
|
|
182
|
+
const background = partBackground(layoutTree, "p:sldLayout", colors) ?? (masterTree ? partBackground(masterTree, "p:sldMaster", colors) : void 0);
|
|
183
|
+
const styles = {
|
|
184
|
+
cascade,
|
|
185
|
+
colors,
|
|
186
|
+
...background ? { background } : {}
|
|
187
|
+
};
|
|
188
|
+
cache.set(layout.path, styles);
|
|
189
|
+
return styles;
|
|
190
|
+
}
|
|
191
|
+
function deckColorResolver(pkg, masterPath) {
|
|
192
|
+
const themeRel = pkg.getPartRelationships(masterPath).find((r) => r.type.endsWith("/theme"));
|
|
193
|
+
const theme = themeRel ? pkg.resolveRelatedPart(masterPath, themeRel) : void 0;
|
|
194
|
+
if (!theme) return defaultColorResolver;
|
|
195
|
+
const palette = new Map(DEFAULT_THEME_PALETTE);
|
|
196
|
+
for (const [slot, hex] of parseTheme(theme.data)) palette.set(slot, hex);
|
|
197
|
+
return makeColorResolver(palette);
|
|
198
|
+
}
|
|
199
|
+
var pptxReader = {
|
|
200
|
+
id: "pptx",
|
|
201
|
+
produces: "flow",
|
|
202
|
+
supports: new Set([
|
|
203
|
+
FEATURES.text,
|
|
204
|
+
FEATURES.images,
|
|
205
|
+
FEATURES.shapes,
|
|
206
|
+
FEATURES.charts,
|
|
207
|
+
FEATURES.tables
|
|
208
|
+
]),
|
|
209
|
+
sniff: (bytes) => bytes[0] === 80 && bytes[1] === 75 && bytesInclude(bytes, "ppt/presentation.xml"),
|
|
210
|
+
read: (bytes) => readPptx(bytes)
|
|
211
|
+
};
|
|
212
|
+
//#endregion
|
|
213
|
+
export { pptxReader };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { BodyElement, ShapeFill } from '../core/document-model/index.js';
|
|
2
|
+
import { ColorResolver } from '../core/drawingml/colors.js';
|
|
3
|
+
import { Pt, ResourceId } from '../core/ir/index.js';
|
|
4
|
+
import { PoNode } from '../core/po-helpers.js';
|
|
5
|
+
import { PlaceholderCascade } from './placeholder-cascade.js';
|
|
6
|
+
import { ShapeBoxEmu } from './sp-helpers.js';
|
|
7
|
+
export interface SlideContext {
|
|
8
|
+
readonly cascade?: PlaceholderCascade;
|
|
9
|
+
readonly resolveImage?: (relId: string) => ResourceId | undefined;
|
|
10
|
+
readonly resolveChart?: (relId: string) => string | undefined;
|
|
11
|
+
readonly colors?: ColorResolver;
|
|
12
|
+
readonly resolveHyperlink?: (relId: string) => string | undefined;
|
|
13
|
+
}
|
|
14
|
+
type GroupTransform = (box: ShapeBoxEmu) => ShapeBoxEmu;
|
|
15
|
+
export declare function parseSlideShapes(container: PoNode, ctx?: SlideContext, transform?: GroupTransform): Array<BodyElement>;
|
|
16
|
+
export declare function parseBackgroundFill(bg: PoNode, colors: ColorResolver): ShapeFill | undefined;
|
|
17
|
+
export declare function backdropElement(fill: ShapeFill, widthPt: Pt, heightPt: Pt): BodyElement;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { emuToPt } from "../core/ir/units.js";
|
|
2
|
+
import { poAttr, poChildren, poFindDescendant, poIntAttr, poIs, poText } from "../core/po-helpers.js";
|
|
3
|
+
import { defaultColorResolver, resolveColorNode } from "../core/drawingml/colors.js";
|
|
4
|
+
import { parseCustGeom, parseFill, parseLine, parsePrstGeom } from "../word/drawing-parser.js";
|
|
5
|
+
import { boxFromXfrm, parsePh, parseXfrmBox, rPrToRunProps } from "./sp-helpers.js";
|
|
6
|
+
//#region src/pptx/slide-parser.ts
|
|
7
|
+
var CHART_URI = "http://schemas.openxmlformats.org/drawingml/2006/chart";
|
|
8
|
+
var TABLE_URI = "http://schemas.openxmlformats.org/drawingml/2006/table";
|
|
9
|
+
var ALGN_TO_ALIGNMENT = {
|
|
10
|
+
l: "left",
|
|
11
|
+
ctr: "center",
|
|
12
|
+
r: "right",
|
|
13
|
+
just: "both",
|
|
14
|
+
dist: "distribute"
|
|
15
|
+
};
|
|
16
|
+
var RECT_GEOMETRY = {
|
|
17
|
+
kind: "preset",
|
|
18
|
+
preset: "rect",
|
|
19
|
+
adjust: /* @__PURE__ */ new Map()
|
|
20
|
+
};
|
|
21
|
+
var IDENTITY_TRANSFORM = (box) => box;
|
|
22
|
+
function parseSlideShapes(container, ctx = {}, transform = IDENTITY_TRANSFORM) {
|
|
23
|
+
const out = [];
|
|
24
|
+
for (const child of poChildren(container)) if (poIs(child, "p:sp")) {
|
|
25
|
+
const shape = parseSp(child, ctx, transform);
|
|
26
|
+
if (shape) out.push({
|
|
27
|
+
kind: "shape",
|
|
28
|
+
shape
|
|
29
|
+
});
|
|
30
|
+
} else if (poIs(child, "p:pic")) {
|
|
31
|
+
const image = parsePic(child, ctx, transform);
|
|
32
|
+
if (image) out.push({
|
|
33
|
+
kind: "image",
|
|
34
|
+
image
|
|
35
|
+
});
|
|
36
|
+
} else if (poIs(child, "p:graphicFrame")) {
|
|
37
|
+
const el = parseGraphicFrame(child, ctx, transform);
|
|
38
|
+
if (el) out.push(el);
|
|
39
|
+
} else if (poIs(child, "p:grpSp")) out.push(...parseSlideShapes(child, ctx, composeGroupTransform(child, transform)));
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
function composeGroupTransform(grpSp, parent) {
|
|
43
|
+
const grpSpPr = poChildren(grpSp).find((c) => poIs(c, "p:grpSpPr"));
|
|
44
|
+
const xfrm = grpSpPr ? poChildren(grpSpPr).find((c) => poIs(c, "a:xfrm")) : void 0;
|
|
45
|
+
if (!xfrm) return parent;
|
|
46
|
+
const off = poChildren(xfrm).find((c) => poIs(c, "a:off"));
|
|
47
|
+
const ext = poChildren(xfrm).find((c) => poIs(c, "a:ext"));
|
|
48
|
+
const chOff = poChildren(xfrm).find((c) => poIs(c, "a:chOff"));
|
|
49
|
+
const chExt = poChildren(xfrm).find((c) => poIs(c, "a:chExt"));
|
|
50
|
+
const extCx = ext ? poIntAttr(ext, "cx") : void 0;
|
|
51
|
+
const extCy = ext ? poIntAttr(ext, "cy") : void 0;
|
|
52
|
+
const chExtCx = chExt ? poIntAttr(chExt, "cx") : void 0;
|
|
53
|
+
const chExtCy = chExt ? poIntAttr(chExt, "cy") : void 0;
|
|
54
|
+
if (!extCx || !extCy || !chExtCx || !chExtCy) return parent;
|
|
55
|
+
const offX = (off ? poIntAttr(off, "x") : void 0) ?? 0;
|
|
56
|
+
const offY = (off ? poIntAttr(off, "y") : void 0) ?? 0;
|
|
57
|
+
const chOffX = (chOff ? poIntAttr(chOff, "x") : void 0) ?? 0;
|
|
58
|
+
const chOffY = (chOff ? poIntAttr(chOff, "y") : void 0) ?? 0;
|
|
59
|
+
const sx = extCx / chExtCx;
|
|
60
|
+
const sy = extCy / chExtCy;
|
|
61
|
+
return (box) => parent({
|
|
62
|
+
x: offX + (box.x - chOffX) * sx,
|
|
63
|
+
y: offY + (box.y - chOffY) * sy,
|
|
64
|
+
cx: box.cx * sx,
|
|
65
|
+
cy: box.cy * sy
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function floatAt(box) {
|
|
69
|
+
return {
|
|
70
|
+
wrap: "none",
|
|
71
|
+
posH: {
|
|
72
|
+
relativeFrom: "page",
|
|
73
|
+
offsetPt: emuToPt(box.x)
|
|
74
|
+
},
|
|
75
|
+
posV: {
|
|
76
|
+
relativeFrom: "page",
|
|
77
|
+
offsetPt: emuToPt(box.y)
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function parseSp(sp, ctx, transform) {
|
|
82
|
+
const ph = parsePh(sp);
|
|
83
|
+
const colors = ctx.colors ?? defaultColorResolver;
|
|
84
|
+
const spPr = poChildren(sp).find((c) => poIs(c, "p:spPr"));
|
|
85
|
+
let own = parseXfrmBox(spPr);
|
|
86
|
+
if (!own && ph && ctx.cascade) own = ctx.cascade.geometryFor(ph);
|
|
87
|
+
if (!own) return void 0;
|
|
88
|
+
const box = transform(own);
|
|
89
|
+
const txBody = poChildren(sp).find((c) => poIs(c, "p:txBody"));
|
|
90
|
+
const text = txBody ? parseTxBody(txBody, ph, ctx.cascade, colors, ctx.resolveHyperlink) : void 0;
|
|
91
|
+
const geometry = parseGeometry(spPr);
|
|
92
|
+
const fill = spPr ? parseFill(spPr, colors) : { kind: "none" };
|
|
93
|
+
const line = spPr ? parseLine(spPr, colors) : void 0;
|
|
94
|
+
const visibleLine = line !== void 0 && line.fill !== "none";
|
|
95
|
+
if (!text && fill.kind === "none" && !visibleLine) return void 0;
|
|
96
|
+
return {
|
|
97
|
+
float: floatAt(box),
|
|
98
|
+
width: emuToPt(box.cx),
|
|
99
|
+
height: emuToPt(box.cy),
|
|
100
|
+
geometry,
|
|
101
|
+
fill,
|
|
102
|
+
...line ? { line } : {},
|
|
103
|
+
...text ? { text } : {},
|
|
104
|
+
paragraphProperties: {}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function parseGraphicFrame(gf, ctx, transform) {
|
|
108
|
+
const own = boxFromXfrm(poChildren(gf).find((c) => poIs(c, "p:xfrm")));
|
|
109
|
+
if (!own) return void 0;
|
|
110
|
+
const box = transform(own);
|
|
111
|
+
const graphicData = poFindDescendant(gf, "a:graphicData");
|
|
112
|
+
const uri = graphicData ? poAttr(graphicData, "uri") : void 0;
|
|
113
|
+
if (uri === CHART_URI) {
|
|
114
|
+
const cChart = poFindDescendant(gf, "c:chart");
|
|
115
|
+
const relId = cChart ? poAttr(cChart, "id") : void 0;
|
|
116
|
+
const key = relId !== void 0 ? ctx.resolveChart?.(relId) : void 0;
|
|
117
|
+
if (key === void 0) return void 0;
|
|
118
|
+
return {
|
|
119
|
+
kind: "chart",
|
|
120
|
+
chart: {
|
|
121
|
+
float: floatAt(box),
|
|
122
|
+
chartRelId: key,
|
|
123
|
+
width: emuToPt(box.cx),
|
|
124
|
+
height: emuToPt(box.cy),
|
|
125
|
+
paragraphProperties: {}
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
if (uri === TABLE_URI) {
|
|
130
|
+
const tbl = poFindDescendant(gf, "a:tbl");
|
|
131
|
+
return tbl ? {
|
|
132
|
+
kind: "table",
|
|
133
|
+
table: parseTable(tbl, ctx.colors ?? defaultColorResolver)
|
|
134
|
+
} : void 0;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function parseBackgroundFill(bg, colors) {
|
|
138
|
+
const bgPr = poChildren(bg).find((c) => poIs(c, "p:bgPr"));
|
|
139
|
+
if (bgPr) {
|
|
140
|
+
const fill = parseFill(bgPr, colors);
|
|
141
|
+
return fill.kind !== "none" ? fill : void 0;
|
|
142
|
+
}
|
|
143
|
+
const bgRef = poChildren(bg).find((c) => poIs(c, "p:bgRef"));
|
|
144
|
+
if (bgRef) for (const c of poChildren(bgRef)) {
|
|
145
|
+
const hex = resolveColorNode(c, colors);
|
|
146
|
+
if (hex) return {
|
|
147
|
+
kind: "solid",
|
|
148
|
+
colorHex: hex
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function backdropElement(fill, widthPt, heightPt) {
|
|
153
|
+
return {
|
|
154
|
+
kind: "shape",
|
|
155
|
+
shape: {
|
|
156
|
+
float: {
|
|
157
|
+
wrap: "none",
|
|
158
|
+
behind: true,
|
|
159
|
+
posH: {
|
|
160
|
+
relativeFrom: "page",
|
|
161
|
+
offsetPt: emuToPt(0)
|
|
162
|
+
},
|
|
163
|
+
posV: {
|
|
164
|
+
relativeFrom: "page",
|
|
165
|
+
offsetPt: emuToPt(0)
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
width: widthPt,
|
|
169
|
+
height: heightPt,
|
|
170
|
+
geometry: RECT_GEOMETRY,
|
|
171
|
+
fill,
|
|
172
|
+
paragraphProperties: {}
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function parseGeometry(spPr) {
|
|
177
|
+
if (!spPr) return RECT_GEOMETRY;
|
|
178
|
+
const prst = poChildren(spPr).find((c) => poIs(c, "a:prstGeom"));
|
|
179
|
+
if (prst) return parsePrstGeom(prst);
|
|
180
|
+
const cust = poChildren(spPr).find((c) => poIs(c, "a:custGeom"));
|
|
181
|
+
if (cust) return parseCustGeom(cust);
|
|
182
|
+
return RECT_GEOMETRY;
|
|
183
|
+
}
|
|
184
|
+
function parsePic(pic, ctx, transform) {
|
|
185
|
+
const own = parseXfrmBox(poChildren(pic).find((c) => poIs(c, "p:spPr")));
|
|
186
|
+
if (!own) return void 0;
|
|
187
|
+
const box = transform(own);
|
|
188
|
+
const blipFill = poChildren(pic).find((c) => poIs(c, "p:blipFill"));
|
|
189
|
+
const blip = blipFill ? poChildren(blipFill).find((c) => poIs(c, "a:blip")) : void 0;
|
|
190
|
+
const relId = blip ? poAttr(blip, "embed") : void 0;
|
|
191
|
+
const resource = relId !== void 0 ? ctx.resolveImage?.(relId) : void 0;
|
|
192
|
+
const altText = picAltText(pic);
|
|
193
|
+
return {
|
|
194
|
+
float: floatAt(box),
|
|
195
|
+
...resource !== void 0 ? { resource } : {},
|
|
196
|
+
width: emuToPt(box.cx),
|
|
197
|
+
height: emuToPt(box.cy),
|
|
198
|
+
paragraphProperties: {},
|
|
199
|
+
...altText ? { altText } : {}
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function picAltText(pic) {
|
|
203
|
+
const nvPicPr = poChildren(pic).find((c) => poIs(c, "p:nvPicPr"));
|
|
204
|
+
const cNvPr = nvPicPr ? poChildren(nvPicPr).find((c) => poIs(c, "p:cNvPr")) : void 0;
|
|
205
|
+
const descr = cNvPr ? poAttr(cNvPr, "descr") : void 0;
|
|
206
|
+
const title = cNvPr ? poAttr(cNvPr, "title") : void 0;
|
|
207
|
+
return (descr ?? title)?.trim() || void 0;
|
|
208
|
+
}
|
|
209
|
+
function txBodyParagraphs(txBody, ph, cascade, colors, resolveLink) {
|
|
210
|
+
const content = [];
|
|
211
|
+
const counters = [];
|
|
212
|
+
for (const child of poChildren(txBody)) {
|
|
213
|
+
if (!poIs(child, "a:p")) continue;
|
|
214
|
+
content.push({
|
|
215
|
+
kind: "paragraph",
|
|
216
|
+
paragraph: parseSlideParagraph(child, ph, cascade, colors, resolveLink, counters)
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
return content;
|
|
220
|
+
}
|
|
221
|
+
function parseTxBody(txBody, ph, cascade, colors, resolveLink) {
|
|
222
|
+
const content = txBodyParagraphs(txBody, ph, cascade, colors, resolveLink);
|
|
223
|
+
if (content.length === 0) return void 0;
|
|
224
|
+
const bodyPr = poChildren(txBody).find((c) => poIs(c, "a:bodyPr"));
|
|
225
|
+
const lIns = bodyPr ? poIntAttr(bodyPr, "lIns") : void 0;
|
|
226
|
+
const tIns = bodyPr ? poIntAttr(bodyPr, "tIns") : void 0;
|
|
227
|
+
const rIns = bodyPr ? poIntAttr(bodyPr, "rIns") : void 0;
|
|
228
|
+
const bIns = bodyPr ? poIntAttr(bodyPr, "bIns") : void 0;
|
|
229
|
+
const a = bodyPr ? poAttr(bodyPr, "anchor") : void 0;
|
|
230
|
+
const anchor = a === "ctr" ? "ctr" : a === "b" ? "b" : a === "t" ? "t" : void 0;
|
|
231
|
+
return {
|
|
232
|
+
content,
|
|
233
|
+
...lIns !== void 0 ? { insetLeft: emuToPt(lIns) } : {},
|
|
234
|
+
...tIns !== void 0 ? { insetTop: emuToPt(tIns) } : {},
|
|
235
|
+
...rIns !== void 0 ? { insetRight: emuToPt(rIns) } : {},
|
|
236
|
+
...bIns !== void 0 ? { insetBottom: emuToPt(bIns) } : {},
|
|
237
|
+
...anchor ? { anchor } : {}
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
function parseSlideParagraph(aP, ph, cascade, colors, resolveLink, counters) {
|
|
241
|
+
const pPr = poChildren(aP).find((c) => poIs(c, "a:pPr"));
|
|
242
|
+
const level = (pPr ? poIntAttr(pPr, "lvl") : void 0) ?? 0;
|
|
243
|
+
const defaults = ph && cascade ? cascade.defaultsFor(ph, level) : {};
|
|
244
|
+
const algn = pPr ? poAttr(pPr, "algn") : void 0;
|
|
245
|
+
const alignment = algn !== void 0 ? ALGN_TO_ALIGNMENT[algn] : void 0;
|
|
246
|
+
const runs = [];
|
|
247
|
+
const marker = bulletMarker(pPr, level, counters);
|
|
248
|
+
if (marker !== void 0) runs.push({
|
|
249
|
+
text: marker,
|
|
250
|
+
properties: defaults,
|
|
251
|
+
listMarker: true
|
|
252
|
+
});
|
|
253
|
+
for (const child of poChildren(aP)) if (poIs(child, "a:r") || poIs(child, "a:fld")) {
|
|
254
|
+
const run = parseSlideRun(child, defaults, colors, resolveLink);
|
|
255
|
+
if (run) runs.push(run);
|
|
256
|
+
}
|
|
257
|
+
const marL = pPr ? poIntAttr(pPr, "marL") : void 0;
|
|
258
|
+
const indent = pPr ? poIntAttr(pPr, "indent") : void 0;
|
|
259
|
+
const indentLeft = marL !== void 0 ? emuToPt(marL) : level > 0 ? emuToPt(level * 457200) : void 0;
|
|
260
|
+
return {
|
|
261
|
+
properties: {
|
|
262
|
+
...alignment ? { alignment } : {},
|
|
263
|
+
...indentLeft !== void 0 ? { indentLeft } : {},
|
|
264
|
+
...indent !== void 0 ? { indentFirstLine: emuToPt(indent) } : {}
|
|
265
|
+
},
|
|
266
|
+
runs
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function bulletMarker(pPr, level, counters) {
|
|
270
|
+
if (!pPr) return void 0;
|
|
271
|
+
if (poChildren(pPr).some((c) => poIs(c, "a:buNone"))) return void 0;
|
|
272
|
+
const buChar = poChildren(pPr).find((c) => poIs(c, "a:buChar"));
|
|
273
|
+
if (buChar) return `${poAttr(buChar, "char") ?? "•"} `;
|
|
274
|
+
const buAuto = poChildren(pPr).find((c) => poIs(c, "a:buAutoNum"));
|
|
275
|
+
if (!buAuto) return void 0;
|
|
276
|
+
const type = poAttr(buAuto, "type") ?? "arabicPeriod";
|
|
277
|
+
const startAt = poIntAttr(buAuto, "startAt") ?? 1;
|
|
278
|
+
const prev = counters[level];
|
|
279
|
+
const n = (prev === void 0 ? startAt - 1 : prev) + 1;
|
|
280
|
+
counters[level] = n;
|
|
281
|
+
counters.length = level + 1;
|
|
282
|
+
return `${n}${autoNumSuffix(type)} `;
|
|
283
|
+
}
|
|
284
|
+
function autoNumSuffix(type) {
|
|
285
|
+
if (type.endsWith("ParenR") || type.endsWith("ParenBoth")) return ")";
|
|
286
|
+
if (type.endsWith("Period")) return ".";
|
|
287
|
+
return "";
|
|
288
|
+
}
|
|
289
|
+
function parseSlideRun(node, defaults, colors, resolveLink) {
|
|
290
|
+
const t = poChildren(node).find((c) => poIs(c, "a:t"));
|
|
291
|
+
const text = t ? poText(t) : "";
|
|
292
|
+
if (text.length === 0) return void 0;
|
|
293
|
+
const rPr = poChildren(node).find((c) => poIs(c, "a:rPr"));
|
|
294
|
+
const hlink = rPr ? poChildren(rPr).find((c) => poIs(c, "a:hlinkClick")) : void 0;
|
|
295
|
+
const linkId = hlink ? poAttr(hlink, "id") : void 0;
|
|
296
|
+
const href = linkId !== void 0 ? resolveLink?.(linkId) : void 0;
|
|
297
|
+
return {
|
|
298
|
+
text,
|
|
299
|
+
properties: {
|
|
300
|
+
...defaults,
|
|
301
|
+
...rPrToRunProps(rPr, colors)
|
|
302
|
+
},
|
|
303
|
+
...href ? { href } : {}
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
function parseTable(tbl, colors) {
|
|
307
|
+
const grid = [];
|
|
308
|
+
const tblGrid = poChildren(tbl).find((c) => poIs(c, "a:tblGrid"));
|
|
309
|
+
if (tblGrid) {
|
|
310
|
+
for (const col of poChildren(tblGrid)) if (poIs(col, "a:gridCol")) grid.push(emuToPt(poIntAttr(col, "w") ?? 0));
|
|
311
|
+
}
|
|
312
|
+
const rows = [];
|
|
313
|
+
for (const tr of poChildren(tbl)) if (poIs(tr, "a:tr")) rows.push(parseTableRow(tr, colors));
|
|
314
|
+
return {
|
|
315
|
+
properties: {},
|
|
316
|
+
grid,
|
|
317
|
+
rows
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
function parseTableRow(tr, colors) {
|
|
321
|
+
const h = poIntAttr(tr, "h");
|
|
322
|
+
const cells = [];
|
|
323
|
+
for (const tc of poChildren(tr)) {
|
|
324
|
+
if (!poIs(tc, "a:tc")) continue;
|
|
325
|
+
if (poAttr(tc, "hMerge") === "1") continue;
|
|
326
|
+
cells.push(parseTableCell(tc, colors));
|
|
327
|
+
}
|
|
328
|
+
return {
|
|
329
|
+
properties: { ...h !== void 0 ? { height: emuToPt(h) } : {} },
|
|
330
|
+
cells
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
function parseTableCell(tc, colors) {
|
|
334
|
+
const txBody = poChildren(tc).find((c) => poIs(c, "a:txBody"));
|
|
335
|
+
const content = txBody ? txBodyParagraphs(txBody, void 0, void 0, colors, void 0) : [];
|
|
336
|
+
const gridSpan = poIntAttr(tc, "gridSpan");
|
|
337
|
+
const rowSpan = poIntAttr(tc, "rowSpan");
|
|
338
|
+
const merge = rowSpan !== void 0 && rowSpan > 1 ? "start" : poAttr(tc, "vMerge") === "1" ? "middle" : void 0;
|
|
339
|
+
const tcPr = poChildren(tc).find((c) => poIs(c, "a:tcPr"));
|
|
340
|
+
const shadingHex = tcPr ? cellFillHex(tcPr, colors) : void 0;
|
|
341
|
+
return {
|
|
342
|
+
properties: {
|
|
343
|
+
...gridSpan !== void 0 && gridSpan > 1 ? { colSpan: gridSpan } : {},
|
|
344
|
+
...merge ? { merge } : {},
|
|
345
|
+
...shadingHex ? { shading: { colorHex: shadingHex } } : {}
|
|
346
|
+
},
|
|
347
|
+
content
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
function cellFillHex(tcPr, colors) {
|
|
351
|
+
const solidFill = poChildren(tcPr).find((c) => poIs(c, "a:solidFill"));
|
|
352
|
+
if (!solidFill) return void 0;
|
|
353
|
+
for (const c of poChildren(solidFill)) {
|
|
354
|
+
const hex = resolveColorNode(c, colors);
|
|
355
|
+
if (hex) return hex;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
//#endregion
|
|
359
|
+
export { backdropElement, parseBackgroundFill, parseSlideShapes };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { RunProperties } from '../core/document-model/index.js';
|
|
2
|
+
import { ColorResolver } from '../core/drawingml/colors.js';
|
|
3
|
+
import { PoNode } from '../core/po-helpers.js';
|
|
4
|
+
export interface PlaceholderRef {
|
|
5
|
+
readonly type?: string;
|
|
6
|
+
readonly idx?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ShapeBoxEmu {
|
|
9
|
+
readonly x: number;
|
|
10
|
+
readonly y: number;
|
|
11
|
+
readonly cx: number;
|
|
12
|
+
readonly cy: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function parsePh(sp: PoNode): PlaceholderRef | undefined;
|
|
15
|
+
export declare function boxFromXfrm(xfrm: PoNode | undefined): ShapeBoxEmu | undefined;
|
|
16
|
+
export declare function parseXfrmBox(spPr: PoNode | undefined): ShapeBoxEmu | undefined;
|
|
17
|
+
export declare function rPrToRunProps(rPr: PoNode | undefined, colors: ColorResolver): RunProperties;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { pt } from "../core/ir/units.js";
|
|
2
|
+
import { poAttr, poChildren, poIntAttr, poIs } from "../core/po-helpers.js";
|
|
3
|
+
import { resolveColorNode } from "../core/drawingml/colors.js";
|
|
4
|
+
//#region src/pptx/sp-helpers.ts
|
|
5
|
+
var isTrue = (v) => v === "1" || v === "true" || v === "on";
|
|
6
|
+
function parsePh(sp) {
|
|
7
|
+
const nvSpPr = poChildren(sp).find((c) => poIs(c, "p:nvSpPr"));
|
|
8
|
+
const nvPr = nvSpPr ? poChildren(nvSpPr).find((c) => poIs(c, "p:nvPr")) : void 0;
|
|
9
|
+
const ph = nvPr ? poChildren(nvPr).find((c) => poIs(c, "p:ph")) : void 0;
|
|
10
|
+
if (!ph) return void 0;
|
|
11
|
+
const type = poAttr(ph, "type");
|
|
12
|
+
const idx = poAttr(ph, "idx");
|
|
13
|
+
return {
|
|
14
|
+
...type !== void 0 ? { type } : {},
|
|
15
|
+
...idx !== void 0 ? { idx } : {}
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function boxFromXfrm(xfrm) {
|
|
19
|
+
if (!xfrm) return void 0;
|
|
20
|
+
const off = poChildren(xfrm).find((c) => poIs(c, "a:off"));
|
|
21
|
+
const ext = poChildren(xfrm).find((c) => poIs(c, "a:ext"));
|
|
22
|
+
const cx = ext ? poIntAttr(ext, "cx") : void 0;
|
|
23
|
+
const cy = ext ? poIntAttr(ext, "cy") : void 0;
|
|
24
|
+
if (cx === void 0 || cy === void 0 || cx <= 0 || cy <= 0) return void 0;
|
|
25
|
+
return {
|
|
26
|
+
x: (off ? poIntAttr(off, "x") : void 0) ?? 0,
|
|
27
|
+
y: (off ? poIntAttr(off, "y") : void 0) ?? 0,
|
|
28
|
+
cx,
|
|
29
|
+
cy
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function parseXfrmBox(spPr) {
|
|
33
|
+
return boxFromXfrm(spPr ? poChildren(spPr).find((c) => poIs(c, "a:xfrm")) : void 0);
|
|
34
|
+
}
|
|
35
|
+
function rPrToRunProps(rPr, colors) {
|
|
36
|
+
if (!rPr) return {};
|
|
37
|
+
const sz = poIntAttr(rPr, "sz");
|
|
38
|
+
const u = poAttr(rPr, "u");
|
|
39
|
+
const colorHex = solidFillColor(rPr, colors);
|
|
40
|
+
const latin = poChildren(rPr).find((c) => poIs(c, "a:latin"));
|
|
41
|
+
const typeface = latin ? poAttr(latin, "typeface")?.trim() : void 0;
|
|
42
|
+
return {
|
|
43
|
+
...isTrue(poAttr(rPr, "b")) ? { bold: true } : {},
|
|
44
|
+
...isTrue(poAttr(rPr, "i")) ? { italic: true } : {},
|
|
45
|
+
...u !== void 0 && u !== "none" ? { underline: "single" } : {},
|
|
46
|
+
...sz !== void 0 ? { fontSizePt: pt(sz / 100) } : {},
|
|
47
|
+
...colorHex ? { colorHex } : {},
|
|
48
|
+
...typeface ? { fontFamily: { ascii: typeface } } : {}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function solidFillColor(rPr, colors) {
|
|
52
|
+
const solidFill = poChildren(rPr).find((c) => poIs(c, "a:solidFill"));
|
|
53
|
+
if (!solidFill) return void 0;
|
|
54
|
+
for (const c of poChildren(solidFill)) {
|
|
55
|
+
const hex = resolveColorNode(c, colors);
|
|
56
|
+
if (hex) return hex;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
export { boxFromXfrm, parsePh, parseXfrmBox, rPrToRunProps };
|
|
@@ -49,7 +49,7 @@ function prepareDocxStyledRender(docx, options) {
|
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
function detectDocxFamilyKeys(docx) {
|
|
52
|
-
const keys = new Set(["
|
|
52
|
+
const keys = new Set(["arimo"]);
|
|
53
53
|
let pkg;
|
|
54
54
|
try {
|
|
55
55
|
pkg = OpcPackage.open(docx);
|
|
@@ -111,7 +111,7 @@ async function resolveDocxAutoFonts(docx, options = {}) {
|
|
|
111
111
|
...fetchOpt
|
|
112
112
|
});
|
|
113
113
|
registriesByFamily.set(key, FontRegistry.fromBytes(bytes));
|
|
114
|
-
if (key === "
|
|
114
|
+
if (key === "arimo" || !baseBytes) baseBytes = bytes;
|
|
115
115
|
}
|
|
116
116
|
return {
|
|
117
117
|
fonts: baseBytes,
|