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.
Files changed (80) hide show
  1. package/README.md +34 -8
  2. package/dist/esm/core/converter/facade.d.ts +8 -1
  3. package/dist/esm/core/converter/facade.js +34 -8
  4. package/dist/esm/core/converter/ream.d.ts +1 -0
  5. package/dist/esm/core/converter/ream.js +13 -8
  6. package/dist/esm/core/document-model/types.d.ts +2 -0
  7. package/dist/esm/core/ir/features.d.ts +1 -0
  8. package/dist/esm/core/ir/features.js +1 -0
  9. package/dist/esm/core/ir/sheet.d.ts +42 -3
  10. package/dist/esm/core/ole/cfb.d.ts +19 -0
  11. package/dist/esm/core/ole/cfb.js +158 -0
  12. package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
  13. package/dist/esm/core/spreadsheet-model/types.d.ts +81 -1
  14. package/dist/esm/excel/activex-parser.d.ts +9 -0
  15. package/dist/esm/excel/activex-parser.js +188 -0
  16. package/dist/esm/excel/comments-parser.d.ts +4 -0
  17. package/dist/esm/excel/comments-parser.js +104 -0
  18. package/dist/esm/excel/conditional-format.d.ts +5 -3
  19. package/dist/esm/excel/conditional-format.js +248 -12
  20. package/dist/esm/excel/form-control-parser.d.ts +6 -0
  21. package/dist/esm/excel/form-control-parser.js +29 -0
  22. package/dist/esm/excel/formula/context.d.ts +11 -0
  23. package/dist/esm/excel/formula/dates.d.ts +11 -0
  24. package/dist/esm/excel/formula/dates.js +46 -0
  25. package/dist/esm/excel/formula/eval.d.ts +11 -0
  26. package/dist/esm/excel/formula/eval.js +196 -0
  27. package/dist/esm/excel/formula/functions.d.ts +7 -0
  28. package/dist/esm/excel/formula/functions.js +1359 -0
  29. package/dist/esm/excel/formula/index.d.ts +14 -0
  30. package/dist/esm/excel/formula/index.js +17 -0
  31. package/dist/esm/excel/formula/lexer.d.ts +8 -0
  32. package/dist/esm/excel/formula/lexer.js +163 -0
  33. package/dist/esm/excel/formula/parser.d.ts +57 -0
  34. package/dist/esm/excel/formula/parser.js +274 -0
  35. package/dist/esm/excel/formula/value.d.ts +52 -0
  36. package/dist/esm/excel/formula/value.js +102 -0
  37. package/dist/esm/excel/header-footer.d.ts +2 -0
  38. package/dist/esm/excel/header-footer.js +127 -0
  39. package/dist/esm/excel/index.d.ts +1 -1
  40. package/dist/esm/excel/number-format.d.ts +2 -0
  41. package/dist/esm/excel/number-format.js +5 -1
  42. package/dist/esm/excel/print-model.d.ts +7 -2
  43. package/dist/esm/excel/print-model.js +123 -16
  44. package/dist/esm/excel/shared-strings-parser.d.ts +6 -1
  45. package/dist/esm/excel/shared-strings-parser.js +91 -12
  46. package/dist/esm/excel/sheet-drawing.d.ts +13 -1
  47. package/dist/esm/excel/sheet-drawing.js +47 -14
  48. package/dist/esm/excel/sheet-shape-parser.d.ts +4 -0
  49. package/dist/esm/excel/sheet-shape-parser.js +96 -0
  50. package/dist/esm/excel/sheet-to-flow.d.ts +4 -1
  51. package/dist/esm/excel/sheet-to-flow.js +170 -3
  52. package/dist/esm/excel/styles-parser.js +37 -0
  53. package/dist/esm/excel/worksheet-parser.js +191 -1
  54. package/dist/esm/excel/xls/biff-chart.d.ts +8 -0
  55. package/dist/esm/excel/xls/biff-chart.js +121 -0
  56. package/dist/esm/excel/xls/biff-reader.d.ts +4 -0
  57. package/dist/esm/excel/xls/biff-reader.js +1418 -0
  58. package/dist/esm/excel/xls/biff-styles.d.ts +8 -0
  59. package/dist/esm/excel/xls/biff-styles.js +299 -0
  60. package/dist/esm/excel/xls/escher.d.ts +24 -0
  61. package/dist/esm/excel/xls/escher.js +171 -0
  62. package/dist/esm/excel/xls/xls-reader.d.ts +3 -0
  63. package/dist/esm/excel/xls/xls-reader.js +30 -0
  64. package/dist/esm/excel/xlsx-reader.d.ts +2 -1
  65. package/dist/esm/excel/xlsx-reader.js +134 -16
  66. package/dist/esm/excel/xlsx-to-pdf.d.ts +1 -0
  67. package/dist/esm/excel/xlsx-to-pdf.js +2 -2
  68. package/dist/esm/excel/xlsx-writer.js +12 -2
  69. package/dist/esm/layout/styled-layout.js +29 -0
  70. package/dist/esm/pptx/ppt/ppt-reader.d.ts +4 -0
  71. package/dist/esm/pptx/ppt/ppt-reader.js +337 -0
  72. package/dist/esm/pptx/ppt/ppt-text.d.ts +69 -0
  73. package/dist/esm/pptx/ppt/ppt-text.js +756 -0
  74. package/dist/esm/pptx/slide-parser.d.ts +4 -2
  75. package/dist/esm/pptx/slide-parser.js +1 -1
  76. package/dist/esm/word/doc/doc-reader.d.ts +4 -0
  77. package/dist/esm/word/doc/doc-reader.js +421 -0
  78. package/dist/esm/word/doc/doc-text.d.ts +79 -0
  79. package/dist/esm/word/doc/doc-text.js +706 -0
  80. 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) for (const ref of parseSheetDrawing(drawing.data, drawing.path, pkg, worksheet)) {
65
- if (!chartData.has(ref.chartPartPath)) {
66
- const chartXml = pkg.getPart(ref.chartPartPath);
67
- const parsed = chartXml ? parseChart(chartXml, resolveColor) : null;
68
- if (!parsed) continue;
69
- chartData.set(ref.chartPartPath, withChartColorStyle(parsed, pkg, ref.chartPartPath, resolveColor));
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: new ResourceStore(),
278
+ resources,
161
279
  ...info ? { info } : {}
162
280
  };
163
281
  }
@@ -7,6 +7,7 @@ export interface ConvertXlsxOptions extends Omit<StyledRenderOptions, 'registry'
7
7
  readonly fontFamily?: string;
8
8
  readonly fontFetch?: FetchLike;
9
9
  readonly embedSource?: boolean;
10
+ readonly now?: Date;
10
11
  readonly signature?: SignatureOptions;
11
12
  }
12
13
  /**
@@ -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,4 @@
1
+ import { DocumentReader, ReadResult } from '../../core/ir/adapters.js';
2
+ import { FlowDoc } from '../../core/ir/flow.js';
3
+ export declare function readPpt(bytes: Uint8Array): ReadResult<FlowDoc>;
4
+ export declare const pptReader: DocumentReader<FlowDoc>;
@@ -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 };