reamkit 1.13.0 → 1.15.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 +34 -8
- package/dist/esm/core/converter/facade.d.ts +8 -1
- package/dist/esm/core/converter/facade.js +34 -8
- package/dist/esm/core/converter/ream.d.ts +1 -0
- package/dist/esm/core/converter/ream.js +13 -8
- package/dist/esm/core/document-model/types.d.ts +2 -0
- package/dist/esm/core/ir/features.d.ts +1 -0
- package/dist/esm/core/ir/features.js +1 -0
- package/dist/esm/core/ir/sheet.d.ts +42 -3
- package/dist/esm/core/ole/cfb.d.ts +19 -0
- package/dist/esm/core/ole/cfb.js +158 -0
- package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
- package/dist/esm/core/spreadsheet-model/types.d.ts +81 -1
- package/dist/esm/excel/activex-parser.d.ts +9 -0
- package/dist/esm/excel/activex-parser.js +188 -0
- package/dist/esm/excel/comments-parser.d.ts +4 -0
- package/dist/esm/excel/comments-parser.js +104 -0
- package/dist/esm/excel/conditional-format.d.ts +5 -3
- package/dist/esm/excel/conditional-format.js +248 -12
- package/dist/esm/excel/form-control-parser.d.ts +6 -0
- package/dist/esm/excel/form-control-parser.js +29 -0
- package/dist/esm/excel/formula/context.d.ts +11 -0
- package/dist/esm/excel/formula/dates.d.ts +11 -0
- package/dist/esm/excel/formula/dates.js +46 -0
- package/dist/esm/excel/formula/eval.d.ts +11 -0
- package/dist/esm/excel/formula/eval.js +196 -0
- package/dist/esm/excel/formula/functions.d.ts +7 -0
- package/dist/esm/excel/formula/functions.js +1359 -0
- package/dist/esm/excel/formula/index.d.ts +14 -0
- package/dist/esm/excel/formula/index.js +17 -0
- package/dist/esm/excel/formula/lexer.d.ts +8 -0
- package/dist/esm/excel/formula/lexer.js +163 -0
- package/dist/esm/excel/formula/parser.d.ts +57 -0
- package/dist/esm/excel/formula/parser.js +274 -0
- package/dist/esm/excel/formula/value.d.ts +52 -0
- package/dist/esm/excel/formula/value.js +102 -0
- package/dist/esm/excel/header-footer.d.ts +2 -0
- package/dist/esm/excel/header-footer.js +127 -0
- package/dist/esm/excel/index.d.ts +1 -1
- package/dist/esm/excel/number-format.d.ts +2 -0
- package/dist/esm/excel/number-format.js +5 -1
- package/dist/esm/excel/print-model.d.ts +7 -2
- package/dist/esm/excel/print-model.js +123 -16
- package/dist/esm/excel/shared-strings-parser.d.ts +6 -1
- package/dist/esm/excel/shared-strings-parser.js +91 -12
- package/dist/esm/excel/sheet-drawing.d.ts +13 -1
- package/dist/esm/excel/sheet-drawing.js +47 -14
- package/dist/esm/excel/sheet-shape-parser.d.ts +4 -0
- package/dist/esm/excel/sheet-shape-parser.js +96 -0
- package/dist/esm/excel/sheet-to-flow.d.ts +4 -1
- package/dist/esm/excel/sheet-to-flow.js +170 -3
- package/dist/esm/excel/styles-parser.js +37 -0
- package/dist/esm/excel/worksheet-parser.js +191 -1
- package/dist/esm/excel/xls/biff-chart.d.ts +8 -0
- package/dist/esm/excel/xls/biff-chart.js +121 -0
- package/dist/esm/excel/xls/biff-reader.d.ts +4 -0
- package/dist/esm/excel/xls/biff-reader.js +1418 -0
- package/dist/esm/excel/xls/biff-styles.d.ts +8 -0
- package/dist/esm/excel/xls/biff-styles.js +299 -0
- package/dist/esm/excel/xls/escher.d.ts +24 -0
- package/dist/esm/excel/xls/escher.js +171 -0
- package/dist/esm/excel/xls/xls-reader.d.ts +3 -0
- package/dist/esm/excel/xls/xls-reader.js +30 -0
- package/dist/esm/excel/xlsx-reader.d.ts +2 -1
- package/dist/esm/excel/xlsx-reader.js +134 -16
- package/dist/esm/excel/xlsx-to-pdf.d.ts +1 -0
- package/dist/esm/excel/xlsx-to-pdf.js +2 -2
- package/dist/esm/excel/xlsx-writer.js +12 -2
- package/dist/esm/layout/styled-layout.js +29 -0
- package/dist/esm/pptx/ppt/ppt-reader.d.ts +4 -0
- package/dist/esm/pptx/ppt/ppt-reader.js +337 -0
- package/dist/esm/pptx/ppt/ppt-text.d.ts +69 -0
- package/dist/esm/pptx/ppt/ppt-text.js +756 -0
- package/dist/esm/pptx/slide-parser.d.ts +4 -2
- package/dist/esm/pptx/slide-parser.js +1 -1
- package/dist/esm/word/doc/doc-reader.d.ts +4 -0
- package/dist/esm/word/doc/doc-reader.js +421 -0
- package/dist/esm/word/doc/doc-text.d.ts +79 -0
- package/dist/esm/word/doc/doc-text.js +706 -0
- package/package.json +1 -1
|
@@ -8,6 +8,7 @@ import { isOoxmlRel } from "../core/opc/relationship-types.js";
|
|
|
8
8
|
import { OpcPackage } from "../core/opc/package.js";
|
|
9
9
|
import { parseCoreProperties } from "../core/opc/core-properties.js";
|
|
10
10
|
import "../core/opc/index.js";
|
|
11
|
+
import { parseAreaRef } from "./defined-name-ref.js";
|
|
11
12
|
import { parseSharedStrings } from "./shared-strings-parser.js";
|
|
12
13
|
import { parseWorkbook } from "./workbook-parser.js";
|
|
13
14
|
import { parseWorksheet } from "./worksheet-parser.js";
|
|
@@ -18,6 +19,10 @@ import { parseSheetDrawing } from "./sheet-drawing.js";
|
|
|
18
19
|
import { parseTablePartFull } from "./table-parser.js";
|
|
19
20
|
import { parsePivotTablePart } from "./pivot-table-parser.js";
|
|
20
21
|
import { parseSlicerCachePart, parseSlicerPart } from "./slicer-parser.js";
|
|
22
|
+
import { parseLegacyComments, parsePersons, parseThreadedComments } from "./comments-parser.js";
|
|
23
|
+
import { parseFormControlProps } from "./form-control-parser.js";
|
|
24
|
+
import { activeXBinRelId, activeXType, parseActiveX, parseActiveXBin } from "./activex-parser.js";
|
|
25
|
+
import { parseSheetShapes } from "./sheet-shape-parser.js";
|
|
21
26
|
import { projectSheetDoc } from "./sheet-to-flow.js";
|
|
22
27
|
//#region src/excel/xlsx-reader.ts
|
|
23
28
|
var WORKBOOK_PART = "xl/workbook.xml";
|
|
@@ -26,10 +31,12 @@ var STYLES_PART = "xl/styles.xml";
|
|
|
26
31
|
var CORE_PROPS_PART = "docProps/core.xml";
|
|
27
32
|
var SLICER_REL_TAIL = "/slicer";
|
|
28
33
|
var SLICER_CACHE_REL_TAIL = "/slicerCache";
|
|
34
|
+
var THREADED_COMMENTS_REL_TAIL = "/threadedComment";
|
|
35
|
+
var PERSON_REL_TAIL = "/person";
|
|
29
36
|
var MAX_SLICER_ITEMS = 256;
|
|
30
|
-
function readXlsx(xlsx) {
|
|
37
|
+
function readXlsx(xlsx, options = {}) {
|
|
31
38
|
return {
|
|
32
|
-
doc: projectSheetDoc(readXlsxToSheetDoc(xlsx)),
|
|
39
|
+
doc: projectSheetDoc(readXlsxToSheetDoc(xlsx), options),
|
|
33
40
|
losses: []
|
|
34
41
|
};
|
|
35
42
|
}
|
|
@@ -40,11 +47,21 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
40
47
|
const { sheets, date1904, definedNames } = parseWorkbook(workbookData);
|
|
41
48
|
if (sheets.length === 0) throw new Error("xlsx has no sheets");
|
|
42
49
|
const sharedStringsData = pkg.getPart(SHARED_STRINGS_PART);
|
|
43
|
-
const sharedStrings = sharedStringsData ? parseSharedStrings(sharedStringsData) :
|
|
50
|
+
const { texts: sharedStrings, runs: sharedStringRuns } = sharedStringsData ? parseSharedStrings(sharedStringsData) : {
|
|
51
|
+
texts: [],
|
|
52
|
+
runs: []
|
|
53
|
+
};
|
|
44
54
|
const stylesData = pkg.getPart(STYLES_PART);
|
|
45
55
|
const styles = stylesData ? parseXlsxStyles(stylesData) : EMPTY_XLSX_STYLES;
|
|
46
56
|
const workbookRels = pkg.getPartRelationships(WORKBOOK_PART);
|
|
57
|
+
const persons = /* @__PURE__ */ new Map();
|
|
58
|
+
for (const rel of workbookRels) {
|
|
59
|
+
if (!rel.type.endsWith(PERSON_REL_TAIL)) continue;
|
|
60
|
+
const part = pkg.resolveRelatedPart(WORKBOOK_PART, rel);
|
|
61
|
+
if (part) for (const [id, name] of parsePersons(part.data)) persons.set(id, name);
|
|
62
|
+
}
|
|
47
63
|
const chartData = /* @__PURE__ */ new Map();
|
|
64
|
+
const resources = new ResourceStore();
|
|
48
65
|
const palette = buildThemePalette(pkg, workbookRels);
|
|
49
66
|
const resolveColor = makeColorResolver(palette);
|
|
50
67
|
const sheetsOut = [];
|
|
@@ -58,21 +75,39 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
58
75
|
if (!resolved) continue;
|
|
59
76
|
const worksheet = parseWorksheet(resolved.data);
|
|
60
77
|
const charts = [];
|
|
78
|
+
const images = [];
|
|
79
|
+
let shapes;
|
|
61
80
|
if (worksheet.drawingRelId) {
|
|
62
81
|
const drawingRel = pkg.getPartRelationships(resolved.path).find((r) => r.id === worksheet.drawingRelId);
|
|
63
82
|
const drawing = drawingRel ? pkg.resolveRelatedPart(resolved.path, drawingRel) : void 0;
|
|
64
|
-
if (drawing)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
83
|
+
if (drawing) {
|
|
84
|
+
const { charts: chartRefs, pictures } = parseSheetDrawing(drawing.data, drawing.path, pkg, worksheet);
|
|
85
|
+
for (const ref of chartRefs) {
|
|
86
|
+
if (!chartData.has(ref.chartPartPath)) {
|
|
87
|
+
const chartXml = pkg.getPart(ref.chartPartPath);
|
|
88
|
+
const parsed = chartXml ? parseChart(chartXml, resolveColor) : null;
|
|
89
|
+
if (!parsed) continue;
|
|
90
|
+
chartData.set(ref.chartPartPath, withChartColorStyle(parsed, pkg, ref.chartPartPath, resolveColor));
|
|
91
|
+
}
|
|
92
|
+
charts.push({
|
|
93
|
+
chartPartPath: ref.chartPartPath,
|
|
94
|
+
widthPt: ref.widthPt,
|
|
95
|
+
heightPt: ref.heightPt
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
for (const pic of pictures) {
|
|
99
|
+
const bytes = pkg.getPart(pic.imagePartPath);
|
|
100
|
+
if (!bytes) continue;
|
|
101
|
+
images.push({
|
|
102
|
+
resourceId: resources.put(bytes),
|
|
103
|
+
widthPt: pic.widthPt,
|
|
104
|
+
heightPt: pic.heightPt
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
if (bytesInclude(drawing.data, ":sp>") || bytesInclude(drawing.data, ":sp ")) {
|
|
108
|
+
const parsed = parseSheetShapes(drawing.data, worksheet, resolveColor);
|
|
109
|
+
if (parsed.length > 0) shapes = parsed;
|
|
70
110
|
}
|
|
71
|
-
charts.push({
|
|
72
|
-
chartPartPath: ref.chartPartPath,
|
|
73
|
-
widthPt: ref.widthPt,
|
|
74
|
-
heightPt: ref.heightPt
|
|
75
|
-
});
|
|
76
111
|
}
|
|
77
112
|
}
|
|
78
113
|
let tables;
|
|
@@ -118,6 +153,82 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
118
153
|
}
|
|
119
154
|
if (resolvedPivots.length > 0) pivotTables = resolvedPivots;
|
|
120
155
|
}
|
|
156
|
+
let hyperlinks;
|
|
157
|
+
if (worksheet.hyperlinks && worksheet.hyperlinks.length > 0) {
|
|
158
|
+
const wsRels = pkg.getPartRelationships(resolved.path);
|
|
159
|
+
const resolvedLinks = [];
|
|
160
|
+
for (const h of worksheet.hyperlinks) {
|
|
161
|
+
if (h.relId === void 0) continue;
|
|
162
|
+
const rel = wsRels.find((r) => r.id === h.relId);
|
|
163
|
+
if (!rel || rel.targetMode !== "External" || !rel.target) continue;
|
|
164
|
+
const area = parseAreaRef(h.ref);
|
|
165
|
+
if (!area) continue;
|
|
166
|
+
resolvedLinks.push({
|
|
167
|
+
ref: {
|
|
168
|
+
startColumn: area.startColumn,
|
|
169
|
+
startRow: area.startRow,
|
|
170
|
+
endColumn: area.endColumn,
|
|
171
|
+
endRow: area.endRow
|
|
172
|
+
},
|
|
173
|
+
url: rel.target
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
if (resolvedLinks.length > 0) hyperlinks = resolvedLinks;
|
|
177
|
+
}
|
|
178
|
+
let comments;
|
|
179
|
+
{
|
|
180
|
+
const wsRels = pkg.getPartRelationships(resolved.path);
|
|
181
|
+
const resolvedComments = [];
|
|
182
|
+
for (const rel of wsRels) {
|
|
183
|
+
if (!isOoxmlRel(rel.type, "comments")) continue;
|
|
184
|
+
const part = pkg.resolveRelatedPart(resolved.path, rel);
|
|
185
|
+
if (part) resolvedComments.push(...parseLegacyComments(part.data));
|
|
186
|
+
}
|
|
187
|
+
for (const rel of wsRels) {
|
|
188
|
+
if (!rel.type.endsWith(THREADED_COMMENTS_REL_TAIL)) continue;
|
|
189
|
+
const part = pkg.resolveRelatedPart(resolved.path, rel);
|
|
190
|
+
if (part) resolvedComments.push(...parseThreadedComments(part.data, persons));
|
|
191
|
+
}
|
|
192
|
+
if (resolvedComments.length > 0) comments = resolvedComments;
|
|
193
|
+
}
|
|
194
|
+
let formControls;
|
|
195
|
+
if (worksheet.formControls && worksheet.formControls.length > 0) {
|
|
196
|
+
const wsRels = pkg.getPartRelationships(resolved.path);
|
|
197
|
+
const resolvedControls = [];
|
|
198
|
+
for (const fc of worksheet.formControls) {
|
|
199
|
+
const rel = wsRels.find((r) => r.id === fc.relId);
|
|
200
|
+
const part = rel ? pkg.resolveRelatedPart(resolved.path, rel) : void 0;
|
|
201
|
+
const props = part ? parseFormControlProps(part.data) : {};
|
|
202
|
+
resolvedControls.push({
|
|
203
|
+
...fc.name ? { name: fc.name } : {},
|
|
204
|
+
...props
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
if (resolvedControls.length > 0) formControls = resolvedControls;
|
|
208
|
+
}
|
|
209
|
+
let activeXControls;
|
|
210
|
+
if (worksheet.oleObjects && worksheet.oleObjects.length > 0) {
|
|
211
|
+
const wsRels = pkg.getPartRelationships(resolved.path);
|
|
212
|
+
const resolvedAx = [];
|
|
213
|
+
for (const ole of worksheet.oleObjects) {
|
|
214
|
+
const rel = wsRels.find((r) => r.id === ole.relId);
|
|
215
|
+
const part = rel ? pkg.resolveRelatedPart(resolved.path, rel) : void 0;
|
|
216
|
+
const props = part ? parseActiveX(part.data) : {};
|
|
217
|
+
let binProps = {};
|
|
218
|
+
if (part) {
|
|
219
|
+
const binRelId = activeXBinRelId(part.data);
|
|
220
|
+
const binRel = binRelId ? pkg.getPartRelationships(part.path).find((r) => r.id === binRelId) : void 0;
|
|
221
|
+
const binPart = binRel ? pkg.resolveRelatedPart(part.path, binRel) : void 0;
|
|
222
|
+
if (binPart) binProps = parseActiveXBin(binPart.data);
|
|
223
|
+
}
|
|
224
|
+
resolvedAx.push({
|
|
225
|
+
type: activeXType(ole.progId),
|
|
226
|
+
...binProps,
|
|
227
|
+
...props
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
if (resolvedAx.length > 0) activeXControls = resolvedAx;
|
|
231
|
+
}
|
|
121
232
|
const grid = tables || pivotTables ? {
|
|
122
233
|
...worksheet,
|
|
123
234
|
...tables ? { tables } : {},
|
|
@@ -126,7 +237,13 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
126
237
|
sheetsOut.push({
|
|
127
238
|
name: sheet.name,
|
|
128
239
|
grid,
|
|
129
|
-
...charts.length > 0 ? { charts } : {}
|
|
240
|
+
...charts.length > 0 ? { charts } : {},
|
|
241
|
+
...images.length > 0 ? { images } : {},
|
|
242
|
+
...shapes ? { shapes } : {},
|
|
243
|
+
...hyperlinks ? { hyperlinks } : {},
|
|
244
|
+
...comments ? { comments } : {},
|
|
245
|
+
...formControls ? { formControls } : {},
|
|
246
|
+
...activeXControls ? { activeXControls } : {}
|
|
130
247
|
});
|
|
131
248
|
}
|
|
132
249
|
if (pendingSlicers.length > 0) {
|
|
@@ -154,10 +271,11 @@ function readXlsxToSheetDoc(xlsx) {
|
|
|
154
271
|
sheets: sheetsOut,
|
|
155
272
|
styles,
|
|
156
273
|
sharedStrings,
|
|
274
|
+
...sharedStringRuns.some((r) => r !== void 0) ? { sharedStringRuns } : {},
|
|
157
275
|
definedNames,
|
|
158
276
|
date1904,
|
|
159
277
|
...chartData.size > 0 ? { chartData } : {},
|
|
160
|
-
resources
|
|
278
|
+
resources,
|
|
161
279
|
...info ? { info } : {}
|
|
162
280
|
};
|
|
163
281
|
}
|
|
@@ -45,8 +45,8 @@ function prepareXlsxStyledRender(xlsx, options) {
|
|
|
45
45
|
const fonts = options.fonts ?? (options.fontBytes ? { regular: options.fontBytes } : void 0);
|
|
46
46
|
if (!fonts) throw new Error("convertXlsxToPdfSync requires options.fonts or options.fontBytes");
|
|
47
47
|
const registry = FontRegistry.fromBytes(fonts);
|
|
48
|
-
const { doc: flow } = readXlsx(xlsx);
|
|
49
|
-
const { fontBytes: _ignoreA, fonts: _ignoreB, section: sectionOverride, info: callerInfo, embedSource, attachments: callerAttachments, signature: _ignoreSig, layoutProfile: _ignoreProfile, ...renderOptions } = options;
|
|
48
|
+
const { doc: flow } = readXlsx(xlsx, options.now !== void 0 ? { now: options.now } : {});
|
|
49
|
+
const { fontBytes: _ignoreA, fonts: _ignoreB, section: sectionOverride, info: callerInfo, embedSource, now: _ignoreNow, attachments: callerAttachments, signature: _ignoreSig, layoutProfile: _ignoreProfile, ...renderOptions } = options;
|
|
50
50
|
const section = sectionOverride ?? flow.section;
|
|
51
51
|
const info = flow.info || callerInfo ? {
|
|
52
52
|
...flow.info,
|
|
@@ -230,6 +230,16 @@ function cfRuleXml(rule) {
|
|
|
230
230
|
case "colorScale": return `<cfRule type="colorScale"${p}><colorScale>` + rule.cfvos.map(cfvoXml).join("") + rule.colorsHex.map((c) => `<color rgb="FF${c}"/>`).join("") + "</colorScale></cfRule>";
|
|
231
231
|
case "dataBar": return `<cfRule type="dataBar"${p}><dataBar` + (rule.minLength !== void 0 ? ` minLength="${rule.minLength}"` : "") + (rule.maxLength !== void 0 ? ` maxLength="${rule.maxLength}"` : "") + ">" + rule.cfvos.map(cfvoXml).join("") + `<color rgb="FF${rule.colorHex}"/></dataBar></cfRule>`;
|
|
232
232
|
case "iconSet": return `<cfRule type="iconSet"${p}><iconSet iconSet="${rule.iconSet}"` + (rule.reverse ? " reverse=\"1\"" : "") + ">" + rule.cfvos.map(cfvoXml).join("") + "</iconSet></cfRule>";
|
|
233
|
+
case "top10": return `<cfRule type="top10"${p} dxfId="${rule.dxfId}" rank="${rule.rank}"` + (rule.percent ? " percent=\"1\"" : "") + (rule.bottom ? " bottom=\"1\"" : "") + "/>";
|
|
234
|
+
case "aboveAverage": return `<cfRule type="aboveAverage"${p} dxfId="${rule.dxfId}"` + (rule.aboveAverage ? "" : " aboveAverage=\"0\"") + (rule.equalAverage ? " equalAverage=\"1\"" : "") + (rule.stdDev !== void 0 ? ` stdDev="${rule.stdDev}"` : "") + "/>";
|
|
235
|
+
case "duplicateValues":
|
|
236
|
+
case "uniqueValues": return `<cfRule type="${rule.type}"${p} dxfId="${rule.dxfId}"/>`;
|
|
237
|
+
case "containsText":
|
|
238
|
+
case "notContainsText":
|
|
239
|
+
case "beginsWith":
|
|
240
|
+
case "endsWith": return `<cfRule type="${rule.type}"${p} operator="${rule.type}" dxfId="${rule.dxfId}" text="${escapeAttr(rule.text)}">` + (rule.formula !== void 0 ? `<formula>${escapeText(rule.formula)}</formula>` : "") + "</cfRule>";
|
|
241
|
+
case "expression": return `<cfRule type="expression"${p} dxfId="${rule.dxfId}"><formula>${escapeText(rule.formula)}</formula></cfRule>`;
|
|
242
|
+
case "timePeriod": return `<cfRule type="timePeriod"${p} timePeriod="${rule.timePeriod}" dxfId="${rule.dxfId}">` + (rule.formula !== void 0 ? `<formula>${escapeText(rule.formula)}</formula>` : "") + "</cfRule>";
|
|
233
243
|
}
|
|
234
244
|
}
|
|
235
245
|
function cfvoXml(cfvo) {
|
|
@@ -313,7 +323,7 @@ function patternFill(f) {
|
|
|
313
323
|
return `<patternFill${f.patternType ? ` patternType="${f.patternType}"` : ""}>${inner}</patternFill>`;
|
|
314
324
|
}
|
|
315
325
|
function borderXml(b) {
|
|
316
|
-
return `<border>${edgeXml("left", b.left)}${edgeXml("right", b.right)}${edgeXml("top", b.top)}${edgeXml("bottom", b.bottom)}</border>`;
|
|
326
|
+
return `<border${b.diagonal ? `${b.diagonalUp ? " diagonalUp=\"1\"" : ""}${b.diagonalDown ? " diagonalDown=\"1\"" : ""}` : ""}>${edgeXml("left", b.left)}${edgeXml("right", b.right)}${edgeXml("top", b.top)}${edgeXml("bottom", b.bottom)}${edgeXml("diagonal", b.diagonal)}</border>`;
|
|
317
327
|
}
|
|
318
328
|
function edgeXml(name, edge) {
|
|
319
329
|
if (!edge || !edge.style) return `<${name}/>`;
|
|
@@ -323,7 +333,7 @@ function edgeXml(name, edge) {
|
|
|
323
333
|
function cellXfXml(xf) {
|
|
324
334
|
const apply = applyAttr("applyNumberFormat", xf.applyNumberFormat) + applyAttr("applyFont", xf.applyFont) + applyAttr("applyFill", xf.applyFill) + applyAttr("applyBorder", xf.applyBorder) + applyAttr("applyAlignment", xf.applyAlignment);
|
|
325
335
|
const a = xf.alignment;
|
|
326
|
-
const alignment = a ? `<alignment${a.horizontal ? ` horizontal="${a.horizontal}"` : ""}${a.vertical ? ` vertical="${a.vertical}"` : ""}${a.wrapText ? " wrapText=\"1\"" : ""}/>` : "";
|
|
336
|
+
const alignment = a ? `<alignment${a.horizontal ? ` horizontal="${a.horizontal}"` : ""}${a.vertical ? ` vertical="${a.vertical}"` : ""}${a.wrapText ? " wrapText=\"1\"" : ""}${a.textRotation !== void 0 ? ` textRotation="${a.textRotation}"` : ""}${a.indent !== void 0 ? ` indent="${a.indent}"` : ""}${a.shrinkToFit ? " shrinkToFit=\"1\"" : ""}/>` : "";
|
|
327
337
|
return `<xf numFmtId="${xf.numFmtId}" fontId="${xf.fontId}" fillId="${xf.fillId}" borderId="${xf.borderId}"${apply}>` + alignment + "</xf>";
|
|
328
338
|
}
|
|
329
339
|
function applyAttr(name, value) {
|
|
@@ -2430,6 +2430,35 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
|
|
|
2430
2430
|
}
|
|
2431
2431
|
}
|
|
2432
2432
|
emitCellBorders(out, cell, cellX, rowBottom, row.heightPt, pageHeight, row.rowIdx, row.rowCount, colCount);
|
|
2433
|
+
const diagDown = cell.borders.diagonalDown;
|
|
2434
|
+
const diagUp = cell.borders.diagonalUp;
|
|
2435
|
+
if ((diagDown || diagUp) && cell.mergeRole !== "middle" && cell.mergeRole !== "end") {
|
|
2436
|
+
const transform = flipTransform([
|
|
2437
|
+
1,
|
|
2438
|
+
0,
|
|
2439
|
+
0,
|
|
2440
|
+
1,
|
|
2441
|
+
cellX,
|
|
2442
|
+
rowBottom
|
|
2443
|
+
], pageHeight);
|
|
2444
|
+
const w = cell.widthPt;
|
|
2445
|
+
const h = row.heightPt;
|
|
2446
|
+
const pushDiagonal = (b, y0, y1) => {
|
|
2447
|
+
out.push({
|
|
2448
|
+
type: "shape",
|
|
2449
|
+
shape: {
|
|
2450
|
+
paths: [new PathBuilder().moveTo(0, y0).lineTo(w, y1).build()],
|
|
2451
|
+
stroke: {
|
|
2452
|
+
colorHex: b.colorHex ?? "000000",
|
|
2453
|
+
widthPt: b.width ?? .75
|
|
2454
|
+
},
|
|
2455
|
+
transform
|
|
2456
|
+
}
|
|
2457
|
+
});
|
|
2458
|
+
};
|
|
2459
|
+
if (diagDown) pushDiagonal(diagDown, h, 0);
|
|
2460
|
+
if (diagUp) pushDiagonal(diagUp, 0, h);
|
|
2461
|
+
}
|
|
2433
2462
|
if (cell.mergeRole === "middle" || cell.mergeRole === "end") continue;
|
|
2434
2463
|
let textY = rowTop - cell.padTopPt;
|
|
2435
2464
|
for (const line of cell.lines) {
|
|
@@ -0,0 +1,337 @@
|
|
|
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 { isCfb, openCfb } from "../../core/ole/cfb.js";
|
|
7
|
+
import { extractPptContent, paragraphText } from "./ppt-text.js";
|
|
8
|
+
//#region src/pptx/ppt/ppt-reader.ts
|
|
9
|
+
var ZERO_WIDTH_SPACE = "";
|
|
10
|
+
var DEFAULT_SLIDE_W = 720;
|
|
11
|
+
var DEFAULT_SLIDE_H = 540;
|
|
12
|
+
var SLIDE_MARGIN = 36;
|
|
13
|
+
var LEVEL_INDENT_PT = 18;
|
|
14
|
+
function looksLikePpt(bytes) {
|
|
15
|
+
if (!isCfb(bytes)) return false;
|
|
16
|
+
try {
|
|
17
|
+
return openCfb(bytes).hasStream("PowerPoint Document");
|
|
18
|
+
} catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
var PPT_TEXT_LOSS = {
|
|
23
|
+
severity: "degraded",
|
|
24
|
+
feature: FEATURES.text,
|
|
25
|
+
detail: "legacy .ppt: each slide's text (with run formatting — bold/italic/underline/size/colour — and paragraph alignment/indent), embedded images, per-shape placement and decorative autoshapes (preset or exact freeform geometry with literal, scheme- or system-resolved fill/line colours) are read into one page per slide (re-save as .pptx for full fidelity)"
|
|
26
|
+
};
|
|
27
|
+
var PPT_ENCRYPTED_LOSS = {
|
|
28
|
+
severity: "dropped",
|
|
29
|
+
feature: FEATURES.text,
|
|
30
|
+
detail: "legacy .ppt is encrypted/obfuscated — its text cannot be read"
|
|
31
|
+
};
|
|
32
|
+
function readPpt(bytes) {
|
|
33
|
+
const content = extractPptContent(bytes);
|
|
34
|
+
const width = pt(content.slideWidthPt ?? DEFAULT_SLIDE_W);
|
|
35
|
+
const height = pt(content.slideHeightPt ?? DEFAULT_SLIDE_H);
|
|
36
|
+
const resources = new ResourceStore();
|
|
37
|
+
return {
|
|
38
|
+
doc: {
|
|
39
|
+
kind: "flow",
|
|
40
|
+
body: resolveBodyStyles(buildBody(content, resources), EMPTY_STYLE_SHEET),
|
|
41
|
+
sections: [],
|
|
42
|
+
section: {
|
|
43
|
+
pageSize: {
|
|
44
|
+
width,
|
|
45
|
+
height
|
|
46
|
+
},
|
|
47
|
+
margins: {
|
|
48
|
+
top: pt(SLIDE_MARGIN),
|
|
49
|
+
right: pt(SLIDE_MARGIN),
|
|
50
|
+
bottom: pt(SLIDE_MARGIN),
|
|
51
|
+
left: pt(SLIDE_MARGIN)
|
|
52
|
+
},
|
|
53
|
+
headers: [],
|
|
54
|
+
footers: []
|
|
55
|
+
},
|
|
56
|
+
styles: EMPTY_STYLE_SHEET,
|
|
57
|
+
resources
|
|
58
|
+
},
|
|
59
|
+
losses: [content.encrypted ? PPT_ENCRYPTED_LOSS : PPT_TEXT_LOSS]
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function buildBody(content, resources) {
|
|
63
|
+
const body = [];
|
|
64
|
+
content.slides.forEach((slide, slideIndex) => {
|
|
65
|
+
const inFlowParas = [];
|
|
66
|
+
const inFlowImages = [];
|
|
67
|
+
const floats = [];
|
|
68
|
+
for (const shape of slide.shapes) if (shape.rectPt) {
|
|
69
|
+
if (shape.paragraphs?.some((p) => paragraphText(p).length > 0)) floats.push(positionedTextShape(shape.rectPt, shape.paragraphs));
|
|
70
|
+
if (shape.image) {
|
|
71
|
+
const block = positionedImage(shape.rectPt, shape.image, resources);
|
|
72
|
+
if (block) floats.push(block);
|
|
73
|
+
}
|
|
74
|
+
if (shape.autoShape) floats.push(positionedAutoShape(shape.rectPt, shape.autoShape));
|
|
75
|
+
} else {
|
|
76
|
+
for (const para of shape.paragraphs ?? []) if (paragraphText(para).length > 0) inFlowParas.push(flowParagraph(para));
|
|
77
|
+
if (shape.image) {
|
|
78
|
+
const block = inFlowImage(shape.image, resources);
|
|
79
|
+
if (block) inFlowImages.push(block);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
pushSlide(body, [...inFlowParas, ...inFlowImages], floats, slideIndex > 0);
|
|
83
|
+
});
|
|
84
|
+
if (body.length === 0) body.push(anchorParagraph(false));
|
|
85
|
+
return body;
|
|
86
|
+
}
|
|
87
|
+
function pushSlide(body, inFlow, floats, breakBefore) {
|
|
88
|
+
if (inFlow.length === 0 && floats.length === 0) {
|
|
89
|
+
body.push(anchorParagraph(breakBefore));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (inFlow.length > 0) {
|
|
93
|
+
const first = inFlow[0];
|
|
94
|
+
if (breakBefore && first.kind === "paragraph") inFlow[0] = {
|
|
95
|
+
kind: "paragraph",
|
|
96
|
+
paragraph: {
|
|
97
|
+
...first.paragraph,
|
|
98
|
+
properties: {
|
|
99
|
+
...first.paragraph.properties,
|
|
100
|
+
pageBreakBefore: true
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
else if (breakBefore) body.push(anchorParagraph(true));
|
|
105
|
+
body.push(...inFlow);
|
|
106
|
+
} else body.push(anchorParagraph(breakBefore));
|
|
107
|
+
body.push(...floats);
|
|
108
|
+
}
|
|
109
|
+
function flowParagraph(para) {
|
|
110
|
+
return {
|
|
111
|
+
kind: "paragraph",
|
|
112
|
+
paragraph: {
|
|
113
|
+
properties: toParaProperties(para, false),
|
|
114
|
+
runs: para.runs.map(toRun)
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function floatAt(rect) {
|
|
119
|
+
return {
|
|
120
|
+
wrap: "none",
|
|
121
|
+
posH: {
|
|
122
|
+
relativeFrom: "page",
|
|
123
|
+
offsetPt: pt(rect.x)
|
|
124
|
+
},
|
|
125
|
+
posV: {
|
|
126
|
+
relativeFrom: "page",
|
|
127
|
+
offsetPt: pt(rect.y)
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function positionedTextShape(rect, paragraphs) {
|
|
132
|
+
return {
|
|
133
|
+
kind: "shape",
|
|
134
|
+
shape: {
|
|
135
|
+
float: floatAt(rect),
|
|
136
|
+
width: pt(rect.w),
|
|
137
|
+
height: pt(rect.h),
|
|
138
|
+
geometry: {
|
|
139
|
+
kind: "preset",
|
|
140
|
+
preset: "rect",
|
|
141
|
+
adjust: /* @__PURE__ */ new Map()
|
|
142
|
+
},
|
|
143
|
+
fill: { kind: "none" },
|
|
144
|
+
text: {
|
|
145
|
+
content: paragraphs.filter((p) => paragraphText(p).length > 0).map(flowParagraph),
|
|
146
|
+
anchor: "t"
|
|
147
|
+
},
|
|
148
|
+
paragraphProperties: {}
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function positionedImage(rect, image, resources) {
|
|
153
|
+
if (!imagePixelSize(image.bytes)) return void 0;
|
|
154
|
+
return {
|
|
155
|
+
kind: "image",
|
|
156
|
+
image: {
|
|
157
|
+
float: floatAt(rect),
|
|
158
|
+
resource: resources.put(image.bytes),
|
|
159
|
+
width: pt(rect.w),
|
|
160
|
+
height: pt(rect.h),
|
|
161
|
+
paragraphProperties: {}
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
var SHAPE_PRESETS = new Map([
|
|
166
|
+
[1, "rect"],
|
|
167
|
+
[2, "roundRect"],
|
|
168
|
+
[3, "ellipse"],
|
|
169
|
+
[4, "diamond"],
|
|
170
|
+
[5, "triangle"],
|
|
171
|
+
[6, "rtTriangle"],
|
|
172
|
+
[7, "parallelogram"],
|
|
173
|
+
[8, "trapezoid"],
|
|
174
|
+
[9, "hexagon"],
|
|
175
|
+
[10, "octagon"],
|
|
176
|
+
[13, "line"],
|
|
177
|
+
[20, "line"]
|
|
178
|
+
]);
|
|
179
|
+
function isLineShape(shapeType) {
|
|
180
|
+
return shapeType === 13 || shapeType >= 20 && shapeType <= 49;
|
|
181
|
+
}
|
|
182
|
+
function toCustomGeometry(g) {
|
|
183
|
+
const commands = g.commands.map((c) => c.kind === "move" ? {
|
|
184
|
+
cmd: "move",
|
|
185
|
+
x: c.x,
|
|
186
|
+
y: c.y
|
|
187
|
+
} : c.kind === "line" ? {
|
|
188
|
+
cmd: "line",
|
|
189
|
+
x: c.x,
|
|
190
|
+
y: c.y
|
|
191
|
+
} : c.kind === "cubic" ? {
|
|
192
|
+
cmd: "cubic",
|
|
193
|
+
x1: c.x1,
|
|
194
|
+
y1: c.y1,
|
|
195
|
+
x2: c.x2,
|
|
196
|
+
y2: c.y2,
|
|
197
|
+
x: c.x,
|
|
198
|
+
y: c.y
|
|
199
|
+
} : { cmd: "close" });
|
|
200
|
+
return {
|
|
201
|
+
pathWidth: g.pathWidth,
|
|
202
|
+
pathHeight: g.pathHeight,
|
|
203
|
+
commands
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
function positionedAutoShape(rect, auto) {
|
|
207
|
+
const line = isLineShape(auto.shapeType);
|
|
208
|
+
const fill = auto.fillColorHex && !line ? {
|
|
209
|
+
kind: "solid",
|
|
210
|
+
colorHex: auto.fillColorHex
|
|
211
|
+
} : { kind: "none" };
|
|
212
|
+
const stroke = auto.lineColorHex ? {
|
|
213
|
+
colorHex: auto.lineColorHex,
|
|
214
|
+
fill: "solid"
|
|
215
|
+
} : line ? { fill: "solid" } : void 0;
|
|
216
|
+
return {
|
|
217
|
+
kind: "shape",
|
|
218
|
+
shape: {
|
|
219
|
+
float: floatAt(rect),
|
|
220
|
+
width: pt(rect.w),
|
|
221
|
+
height: pt(rect.h),
|
|
222
|
+
geometry: auto.geometry ? {
|
|
223
|
+
kind: "custom",
|
|
224
|
+
custom: toCustomGeometry(auto.geometry)
|
|
225
|
+
} : {
|
|
226
|
+
kind: "preset",
|
|
227
|
+
preset: SHAPE_PRESETS.get(auto.shapeType) ?? "rect",
|
|
228
|
+
adjust: /* @__PURE__ */ new Map()
|
|
229
|
+
},
|
|
230
|
+
fill,
|
|
231
|
+
...stroke ? { line: stroke } : {},
|
|
232
|
+
paragraphProperties: {}
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
function inFlowImage(image, resources) {
|
|
237
|
+
const size = imagePixelSize(image.bytes);
|
|
238
|
+
if (!size) return void 0;
|
|
239
|
+
return {
|
|
240
|
+
kind: "image",
|
|
241
|
+
image: {
|
|
242
|
+
resource: resources.put(image.bytes),
|
|
243
|
+
width: pt(size.w * .75),
|
|
244
|
+
height: pt(size.h * .75),
|
|
245
|
+
paragraphProperties: {}
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
function imagePixelSize(d) {
|
|
250
|
+
if (d.length >= 24 && d[0] === 137 && d[1] === 80 && d[2] === 78 && d[3] === 71) {
|
|
251
|
+
const w = u32be(d, 16);
|
|
252
|
+
const h = u32be(d, 20);
|
|
253
|
+
return w > 0 && h > 0 ? {
|
|
254
|
+
w,
|
|
255
|
+
h
|
|
256
|
+
} : void 0;
|
|
257
|
+
}
|
|
258
|
+
if (d.length >= 4 && d[0] === 255 && d[1] === 216) {
|
|
259
|
+
let off = 2;
|
|
260
|
+
while (off + 9 < d.length) {
|
|
261
|
+
if (d[off] !== 255) {
|
|
262
|
+
off++;
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
const marker = d[off + 1];
|
|
266
|
+
if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) {
|
|
267
|
+
const h = d[off + 5] << 8 | d[off + 6];
|
|
268
|
+
const w = d[off + 7] << 8 | d[off + 8];
|
|
269
|
+
return w > 0 && h > 0 ? {
|
|
270
|
+
w,
|
|
271
|
+
h
|
|
272
|
+
} : void 0;
|
|
273
|
+
}
|
|
274
|
+
const len = d[off + 2] << 8 | d[off + 3];
|
|
275
|
+
if (len < 2) break;
|
|
276
|
+
off += 2 + len;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function u32be(d, o) {
|
|
281
|
+
return (d[o] << 24 | d[o + 1] << 16 | d[o + 2] << 8 | d[o + 3]) >>> 0;
|
|
282
|
+
}
|
|
283
|
+
function toRun(r) {
|
|
284
|
+
const underline = r.underline ? "single" : void 0;
|
|
285
|
+
const properties = {
|
|
286
|
+
...r.bold ? { bold: true } : {},
|
|
287
|
+
...r.italic ? { italic: true } : {},
|
|
288
|
+
...underline ? { underline } : {},
|
|
289
|
+
...r.sizePt ? { fontSizePt: pt(r.sizePt) } : {},
|
|
290
|
+
...r.colorHex ? { colorHex: r.colorHex } : {}
|
|
291
|
+
};
|
|
292
|
+
return {
|
|
293
|
+
text: r.text,
|
|
294
|
+
properties
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
function toParaProperties(p, breakBefore) {
|
|
298
|
+
const alignment = p.align !== void 0 ? alignmentFrom(p.align) : void 0;
|
|
299
|
+
const level = p.level ?? 0;
|
|
300
|
+
return {
|
|
301
|
+
...breakBefore ? { pageBreakBefore: true } : {},
|
|
302
|
+
...alignment ? { alignment } : {},
|
|
303
|
+
...level > 0 ? { indentLeft: pt(level * LEVEL_INDENT_PT) } : {}
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
function alignmentFrom(a) {
|
|
307
|
+
switch (a) {
|
|
308
|
+
case 1: return "center";
|
|
309
|
+
case 2: return "right";
|
|
310
|
+
case 3:
|
|
311
|
+
case 6: return "both";
|
|
312
|
+
case 4:
|
|
313
|
+
case 5: return "distribute";
|
|
314
|
+
default: return;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
function anchorParagraph(breakBefore) {
|
|
318
|
+
return {
|
|
319
|
+
kind: "paragraph",
|
|
320
|
+
paragraph: {
|
|
321
|
+
properties: breakBefore ? { pageBreakBefore: true } : {},
|
|
322
|
+
runs: [{
|
|
323
|
+
text: ZERO_WIDTH_SPACE,
|
|
324
|
+
properties: {}
|
|
325
|
+
}]
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
var pptReader = {
|
|
330
|
+
id: "ppt",
|
|
331
|
+
produces: "flow",
|
|
332
|
+
supports: new Set([FEATURES.text]),
|
|
333
|
+
sniff: looksLikePpt,
|
|
334
|
+
read: (bytes) => readPpt(bytes)
|
|
335
|
+
};
|
|
336
|
+
//#endregion
|
|
337
|
+
export { pptReader };
|