ppt-codec 1.5.0 → 1.7.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 (74) hide show
  1. package/README.md +91 -75
  2. package/dist/base64.cjs +48 -0
  3. package/dist/base64.d.cts +5 -0
  4. package/dist/base64.d.ts +5 -0
  5. package/dist/base64.js +46 -0
  6. package/dist/diagnostics-CqxAnubw.d.cts +16 -0
  7. package/dist/diagnostics-CqxAnubw.d.ts +16 -0
  8. package/dist/diagnostics.cjs +11 -0
  9. package/dist/diagnostics.d.cts +2 -0
  10. package/dist/diagnostics.d.ts +2 -0
  11. package/dist/diagnostics.js +9 -0
  12. package/dist/document/color-scheme.cjs +4 -0
  13. package/dist/document/color-scheme.d.cts +2 -1
  14. package/dist/document/color-scheme.d.ts +2 -1
  15. package/dist/document/color-scheme.js +4 -1
  16. package/dist/document/master-write.cjs +21 -16
  17. package/dist/document/master-write.d.cts +2 -1
  18. package/dist/document/master-write.d.ts +2 -1
  19. package/dist/document/master-write.js +21 -16
  20. package/dist/document/notes-write.cjs +15 -7
  21. package/dist/document/notes-write.d.cts +2 -1
  22. package/dist/document/notes-write.d.ts +2 -1
  23. package/dist/document/notes-write.js +15 -7
  24. package/dist/document/notes.cjs +4 -3
  25. package/dist/document/notes.js +4 -3
  26. package/dist/drawing/blips.cjs +81 -0
  27. package/dist/drawing/blips.d.cts +16 -0
  28. package/dist/drawing/blips.d.ts +16 -0
  29. package/dist/drawing/blips.js +77 -0
  30. package/dist/drawing/properties.cjs +102 -0
  31. package/dist/drawing/properties.d.cts +29 -0
  32. package/dist/drawing/properties.d.ts +29 -0
  33. package/dist/drawing/properties.js +87 -0
  34. package/dist/drawing/shapes-write.cjs +234 -17
  35. package/dist/drawing/shapes-write.d.cts +16 -4
  36. package/dist/drawing/shapes-write.d.ts +16 -4
  37. package/dist/drawing/shapes-write.js +237 -19
  38. package/dist/drawing/shapes.cjs +34 -4
  39. package/dist/drawing/shapes.d.cts +11 -2
  40. package/dist/drawing/shapes.d.ts +11 -2
  41. package/dist/drawing/shapes.js +35 -5
  42. package/dist/index.cjs +48 -3
  43. package/dist/index.d.cts +12 -8
  44. package/dist/index.d.ts +12 -8
  45. package/dist/index.js +11 -7
  46. package/dist/ole/embedded-write.cjs +66 -0
  47. package/dist/ole/embedded-write.d.cts +12 -0
  48. package/dist/ole/embedded-write.d.ts +12 -0
  49. package/dist/ole/embedded-write.js +63 -0
  50. package/dist/ole/embedded.cjs +66 -0
  51. package/dist/ole/embedded.d.cts +12 -0
  52. package/dist/ole/embedded.d.ts +12 -0
  53. package/dist/ole/embedded.js +63 -0
  54. package/dist/read.cjs +145 -22
  55. package/dist/read.d.cts +13 -5
  56. package/dist/read.d.ts +13 -5
  57. package/dist/read.js +148 -26
  58. package/dist/record/types.cjs +28 -0
  59. package/dist/record/types.d.cts +15 -1
  60. package/dist/record/types.d.ts +15 -1
  61. package/dist/record/types.js +15 -1
  62. package/dist/stream/current-user.cjs +1 -0
  63. package/dist/stream/current-user.d.cts +2 -1
  64. package/dist/stream/current-user.d.ts +2 -1
  65. package/dist/stream/current-user.js +1 -1
  66. package/dist/units.cjs +10 -0
  67. package/dist/units.d.cts +4 -1
  68. package/dist/units.d.ts +4 -1
  69. package/dist/units.js +8 -1
  70. package/dist/write.cjs +137 -38
  71. package/dist/write.d.cts +14 -5
  72. package/dist/write.d.ts +14 -5
  73. package/dist/write.js +137 -38
  74. package/package.json +2 -1
package/dist/read.cjs CHANGED
@@ -1,4 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_base64 = require("./base64.cjs");
2
3
  const require_errors = require("./errors.cjs");
3
4
  const require_record_tree = require("./record/tree.cjs");
4
5
  const require_record_types = require("./record/types.cjs");
@@ -11,21 +12,39 @@ const require_content = require("./content.cjs");
11
12
  const require_encryption = require("./encryption.cjs");
12
13
  const require_document_document_atom = require("./document/document-atom.cjs");
13
14
  const require_document_fonts = require("./document/fonts.cjs");
15
+ const require_drawing_blips = require("./drawing/blips.cjs");
16
+ require("./drawing/properties.cjs");
14
17
  const require_document_notes_list = require("./document/notes-list.cjs");
15
18
  const require_drawing_shapes = require("./drawing/shapes.cjs");
16
19
  const require_document_notes = require("./document/notes.cjs");
17
20
  const require_document_slide_list = require("./document/slide-list.cjs");
18
21
  const require_stream_current_user = require("./stream/current-user.cjs");
22
+ const require_ole_embedded = require("./ole/embedded.cjs");
19
23
  const require_stream_persist = require("./stream/persist.cjs");
20
24
  let archive_codec = require("archive-codec");
21
25
  let document_schema_js = require("document-schema.js");
22
26
  //#region src/read.ts
23
27
  const CURRENT_USER_STREAM = "Current User";
24
28
  const POWERPOINT_DOCUMENT_STREAM = "PowerPoint Document";
29
+ const PICTURES_STREAM = "Pictures";
25
30
  /** The [MS-OLEPS] Property Set Stream a .ppt's title/author/dates live in when present ([MS-OSHARED] 2.3.3.2.2) -- a genuinely optional stream, unlike the two above, since a valid PowerPoint binary document need not carry document properties at all. */
26
31
  const SUMMARY_INFORMATION_STREAM = "SummaryInformation";
27
32
  const DEFAULT_INSET_LEFT_RIGHT_PT = 7.2;
28
33
  const DEFAULT_INSET_TOP_BOTTOM_PT = 3.6;
34
+ function insetsForShape(properties, isPicture) {
35
+ const defaultLeftRight = isPicture ? 0 : DEFAULT_INSET_LEFT_RIGHT_PT;
36
+ const defaultTopBottom = isPicture ? 0 : DEFAULT_INSET_TOP_BOTTOM_PT;
37
+ const emuOrDefault = (opid, fallback) => {
38
+ const property = properties.get(opid);
39
+ return property === void 0 ? fallback : require_units.emuToPoints(property.value);
40
+ };
41
+ return {
42
+ insetLeftPt: emuOrDefault(129, defaultLeftRight),
43
+ insetTopPt: emuOrDefault(130, defaultTopBottom),
44
+ insetRightPt: emuOrDefault(131, defaultLeftRight),
45
+ insetBottomPt: emuOrDefault(132, defaultTopBottom)
46
+ };
47
+ }
29
48
  const NO_STYLE = {
30
49
  paragraphRuns: [],
31
50
  characterRuns: []
@@ -55,14 +74,73 @@ function textRecordsFor(clientTextbox, persist) {
55
74
  records: outlineText.records
56
75
  };
57
76
  }
58
- function blocksFor(clientTextbox, persist, fontNames, masterInfo) {
77
+ function blocksFor(clientTextbox, context) {
59
78
  if (clientTextbox === void 0) return [];
60
- const { textType, records } = textRecordsFor(clientTextbox, persist);
79
+ const { textType, records } = textRecordsFor(clientTextbox, context.persist);
61
80
  const text = require_text_atoms.readTextBody(records);
62
81
  if (text === void 0) return [];
63
82
  const styleRecord = require_record_tree.findChild(records, require_record_types.RT_StyleTextPropAtom);
64
83
  const style = styleRecord === void 0 ? NO_STYLE : require_text_style.readStyleTextPropAtom(styleRecord, require_text_atoms.characterCountOf(text));
65
- return require_content.buildParagraphs(text, style, fontNames, masterInfo.styles, textType, masterInfo.colorScheme);
84
+ return require_content.buildParagraphs(text, style, context.fontNames, context.masterInfo.styles, textType, context.masterInfo.colorScheme);
85
+ }
86
+ function imageBlocksFor(pibProperty, context, widthPt, heightPt) {
87
+ if (pibProperty === void 0) return [];
88
+ const blip = require_drawing_blips.blipForPib(context.blips, pibProperty.value);
89
+ if (blip === void 0) return [];
90
+ return [{
91
+ kind: "image",
92
+ format: blip.format,
93
+ base64: require_base64.bytesToBase64(blip.bytes),
94
+ widthPt,
95
+ heightPt
96
+ }];
97
+ }
98
+ function embeddedObjectBlocksFor(clientData, context, frame) {
99
+ if (clientData === void 0 || context.decodeEmbeddedObject === void 0) return [];
100
+ const exObjId = require_ole_embedded.readExObjIdRef(clientData);
101
+ if (exObjId === void 0) return [];
102
+ const embed = context.externalOleEmbeds.get(exObjId);
103
+ if (embed === void 0) return [];
104
+ const storageBytes = require_ole_embedded.resolveOleObjectStorage(context.streamBytes, context.directory, embed.persistIdRef);
105
+ if (storageBytes === void 0) return [];
106
+ const decoded = context.decodeEmbeddedObject(storageBytes, embed.progId);
107
+ if (decoded === void 0) return [];
108
+ return [{
109
+ kind: "embeddedObject",
110
+ objectKind: decoded.objectKind,
111
+ document: decoded.document,
112
+ frame
113
+ }];
114
+ }
115
+ function tableBlockFor(table, context) {
116
+ const placed = table.cells.flatMap((cell) => cell.anchor === void 0 || cell.anchor.right <= cell.anchor.left || cell.anchor.bottom <= cell.anchor.top ? [] : [{
117
+ cell,
118
+ anchor: cell.anchor
119
+ }]);
120
+ const rowTops = [...new Set(placed.map((entry) => entry.anchor.top))].sort((a, b) => a - b);
121
+ const columnLefts = [...new Set(placed.map((entry) => entry.anchor.left))].sort((a, b) => a - b);
122
+ const rightmost = Math.max(...placed.map((entry) => entry.anchor.right), ...columnLefts);
123
+ const columnWidthsPt = columnLefts.map((left, index) => {
124
+ const right = columnLefts.at(index + 1) ?? rightmost;
125
+ return require_units.masterUnitsToPoints(right - left);
126
+ });
127
+ return {
128
+ kind: "table",
129
+ rows: rowTops.map((top) => {
130
+ const inRow = placed.filter((entry) => entry.anchor.top === top).sort((a, b) => a.anchor.left - b.anchor.left);
131
+ const bottom = Math.max(...inRow.map((entry) => entry.anchor.bottom), top);
132
+ const cells = columnLefts.map((left) => {
133
+ const at = inRow.find((entry) => entry.anchor.left === left);
134
+ return at === void 0 ? { blocks: [] } : { blocks: blocksFor(at.cell.clientTextbox, context) };
135
+ });
136
+ const heightPt = require_units.masterUnitsToPoints(bottom - top);
137
+ return heightPt > 0 ? {
138
+ cells,
139
+ heightPt
140
+ } : { cells };
141
+ }),
142
+ columnWidthsPt
143
+ };
66
144
  }
67
145
  function readNotesBySlideId(streamBytes, directory, notesList) {
68
146
  const notes = /* @__PURE__ */ new Map();
@@ -74,7 +152,7 @@ function readNotesBySlideId(streamBytes, directory, notesList) {
74
152
  return notes;
75
153
  }
76
154
  function colorSchemeFor(slideChildren, master) {
77
- const ownScheme = require_record_tree.findChild(slideChildren, require_record_types.RT_ColorSchemeAtom);
155
+ const ownScheme = require_document_color_scheme.findSlideSchemeColorSchemeAtom(slideChildren);
78
156
  return ownScheme === void 0 ? master.colorScheme : require_document_color_scheme.readSlideSchemeColorSchemeAtom(ownScheme);
79
157
  }
80
158
  function readMastersById(streamBytes, directory, masterList, documentDefault) {
@@ -86,7 +164,7 @@ function readMastersById(streamBytes, directory, masterList, documentDefault) {
86
164
  const masterChildren = require_record_tree.childRecords(masterContainer);
87
165
  const masterAtoms = masterChildren.filter((record) => record.header.recType === require_record_types.RT_TextMasterStyleAtom).map(require_text_style.readTextMasterStyleAtom);
88
166
  const styles = require_document_master.buildMasterStyleTable(masterAtoms, documentDefault);
89
- const colorSchemeRecord = require_record_tree.findChild(masterChildren, require_record_types.RT_ColorSchemeAtom);
167
+ const colorSchemeRecord = require_document_color_scheme.findSlideSchemeColorSchemeAtom(masterChildren);
90
168
  if (colorSchemeRecord === void 0) throw new require_errors.PptFormatError(`MainMasterContainer for master ${persist.slideId} has no SlideSchemeColorSchemeAtom, which [MS-PPT] 2.5.3 requires`);
91
169
  mastersById.set(persist.slideId, {
92
170
  styles,
@@ -95,7 +173,8 @@ function readMastersById(streamBytes, directory, masterList, documentDefault) {
95
173
  }
96
174
  return mastersById;
97
175
  }
98
- function readSlide(streamBytes, directory, persist, size, fontNames, notes, mastersById) {
176
+ function readSlide(base, size, notes, mastersById) {
177
+ const { streamBytes, directory, persist } = base;
99
178
  const slideContainer = require_stream_persist.resolvePersistObject(streamBytes, directory, persist.persistIdRef, `SlidePersistAtom for slide ${persist.slideId}`);
100
179
  if (slideContainer.header.recType !== 1006) throw new require_errors.PptFormatError(`persist object ${persist.persistIdRef} is record type 0x${slideContainer.header.recType.toString(16)}, not the RT_Slide (0x${require_record_types.RT_Slide.toString(16)}) its SlidePersistAtom promised`);
101
180
  const slideChildren = require_record_tree.childRecords(slideContainer);
@@ -104,28 +183,60 @@ function readSlide(streamBytes, directory, persist, size, fontNames, notes, mast
104
183
  const { masterIdRef } = require_document_master.readSlideAtom(slideAtomRecord);
105
184
  const master = mastersById.get(masterIdRef);
106
185
  if (master === void 0) throw new require_errors.PptFormatError(`slide ${persist.slideId}'s own SlideAtom names masterIdRef ${masterIdRef}, which the master list does not contain`);
107
- const masterInfo = {
108
- styles: master.styles,
109
- colorScheme: colorSchemeFor(slideChildren, master)
186
+ const context = {
187
+ ...base,
188
+ masterInfo: {
189
+ styles: master.styles,
190
+ colorScheme: colorSchemeFor(slideChildren, master)
191
+ }
110
192
  };
111
193
  const drawing = require_record_tree.findChild(slideChildren, require_record_types.RT_Drawing);
112
194
  const shapes = [];
113
195
  for (const shape of drawing === void 0 ? [] : require_drawing_shapes.readDrawingShapes(drawing)) {
196
+ if (!("clientTextbox" in shape)) {
197
+ const tableLeft = require_units.masterUnitsToPoints(shape.anchor.left);
198
+ const tableTop = require_units.masterUnitsToPoints(shape.anchor.top);
199
+ shapes.push({
200
+ frame: {
201
+ xPt: tableLeft,
202
+ yPt: tableTop,
203
+ widthPt: Math.max(0, require_units.masterUnitsToPoints(shape.anchor.right) - tableLeft),
204
+ heightPt: Math.max(0, require_units.masterUnitsToPoints(shape.anchor.bottom) - tableTop)
205
+ },
206
+ ...shape.rotationDeg === void 0 ? {} : { rotationDeg: shape.rotationDeg },
207
+ insetLeftPt: DEFAULT_INSET_LEFT_RIGHT_PT,
208
+ insetTopPt: DEFAULT_INSET_TOP_BOTTOM_PT,
209
+ insetRightPt: DEFAULT_INSET_LEFT_RIGHT_PT,
210
+ insetBottomPt: DEFAULT_INSET_TOP_BOTTOM_PT,
211
+ blocks: [tableBlockFor(shape, context)]
212
+ });
213
+ continue;
214
+ }
114
215
  if (shape.anchor === void 0) continue;
115
216
  const left = require_units.masterUnitsToPoints(shape.anchor.left);
116
217
  const top = require_units.masterUnitsToPoints(shape.anchor.top);
218
+ const widthPt = Math.max(0, require_units.masterUnitsToPoints(shape.anchor.right) - left);
219
+ const heightPt = Math.max(0, require_units.masterUnitsToPoints(shape.anchor.bottom) - top);
220
+ const isPicture = shape.properties.get(260) !== void 0;
117
221
  shapes.push({
118
222
  frame: {
119
223
  xPt: left,
120
224
  yPt: top,
121
- widthPt: Math.max(0, require_units.masterUnitsToPoints(shape.anchor.right) - left),
122
- heightPt: Math.max(0, require_units.masterUnitsToPoints(shape.anchor.bottom) - top)
225
+ widthPt,
226
+ heightPt
123
227
  },
124
- insetLeftPt: DEFAULT_INSET_LEFT_RIGHT_PT,
125
- insetTopPt: DEFAULT_INSET_TOP_BOTTOM_PT,
126
- insetRightPt: DEFAULT_INSET_LEFT_RIGHT_PT,
127
- insetBottomPt: DEFAULT_INSET_TOP_BOTTOM_PT,
128
- blocks: blocksFor(shape.clientTextbox, persist, fontNames, masterInfo)
228
+ ...shape.rotationDeg === void 0 ? {} : { rotationDeg: shape.rotationDeg },
229
+ ...insetsForShape(shape.properties, isPicture),
230
+ blocks: [
231
+ ...imageBlocksFor(shape.properties.get(260), context, widthPt, heightPt),
232
+ ...blocksFor(shape.clientTextbox, context),
233
+ ...embeddedObjectBlocksFor(shape.clientData, context, {
234
+ xPt: left,
235
+ yPt: top,
236
+ widthPt,
237
+ heightPt
238
+ })
239
+ ]
129
240
  });
130
241
  }
131
242
  return {
@@ -134,7 +245,7 @@ function readSlide(streamBytes, directory, persist, size, fontNames, notes, mast
134
245
  notes
135
246
  };
136
247
  }
137
- function readPptStreams(currentUserStream, powerPointDocumentStream, password) {
248
+ function readPptStreams(currentUserStream, powerPointDocumentStream, password, picturesStream, options) {
138
249
  const currentUser = require_stream_current_user.readCurrentUserAtom(currentUserStream);
139
250
  const { directory, currentEdit } = require_stream_persist.buildPersistDirectory(powerPointDocumentStream, currentUser.offsetToCurrentEdit);
140
251
  let streamBytes = powerPointDocumentStream;
@@ -162,14 +273,25 @@ function readPptStreams(currentUserStream, powerPointDocumentStream, password) {
162
273
  const slideList = listWithInstance(0);
163
274
  const persists = slideList === void 0 ? [] : require_document_slide_list.readSlideListWithText(slideList);
164
275
  const notesBySlideId = readNotesBySlideId(streamBytes, directory, listWithInstance(2));
276
+ const blips = require_drawing_blips.readBlipStore(documentContainer, currentUser.encrypted ? void 0 : picturesStream);
277
+ const externalOleEmbeds = require_ole_embedded.readExternalOleEmbeds(children);
165
278
  return {
166
279
  metadata: {},
167
- slides: persists.map((persist) => readSlide(streamBytes, directory, persist, size, fontNames, notesBySlideId.get(persist.slideId) ?? "", mastersById))
280
+ slides: persists.map((persist) => readSlide({
281
+ streamBytes,
282
+ directory,
283
+ persist,
284
+ fontNames,
285
+ blips,
286
+ externalOleEmbeds,
287
+ decodeEmbeddedObject: options?.decodeEmbeddedObject
288
+ }, size, notesBySlideId.get(persist.slideId) ?? "", mastersById))
168
289
  };
169
290
  }
170
- function readPptContent(bytes, password) {
291
+ function readPptContent(bytes, password, options) {
171
292
  const streams = (0, archive_codec.readCompoundFile)(bytes);
172
- const document = readPptStreams(requireStream(streams, CURRENT_USER_STREAM), requireStream(streams, POWERPOINT_DOCUMENT_STREAM), password);
293
+ const pictures = streams.find((stream) => stream.path === PICTURES_STREAM);
294
+ const document = readPptStreams(requireStream(streams, CURRENT_USER_STREAM), requireStream(streams, POWERPOINT_DOCUMENT_STREAM), password, pictures === void 0 ? void 0 : pictures.bytes, options);
173
295
  const metadataStream = streams.find((stream) => stream.path === SUMMARY_INFORMATION_STREAM);
174
296
  if (metadataStream === void 0) return document;
175
297
  return {
@@ -177,8 +299,8 @@ function readPptContent(bytes, password) {
177
299
  metadata: (0, archive_codec.summaryInformationToLayoutMetadata)((0, archive_codec.readSummaryInformation)(metadataStream.bytes))
178
300
  };
179
301
  }
180
- function readPpt(bytes, password) {
181
- const { metadata, slides } = readPptContent(bytes, password);
302
+ function readPpt(bytes, password, options) {
303
+ const { metadata, slides } = readPptContent(bytes, password, options);
182
304
  const document = {
183
305
  kind: "presentation",
184
306
  metadata,
@@ -190,6 +312,7 @@ function readPpt(bytes, password) {
190
312
  exports.CURRENT_USER_STREAM = CURRENT_USER_STREAM;
191
313
  exports.DEFAULT_INSET_LEFT_RIGHT_PT = DEFAULT_INSET_LEFT_RIGHT_PT;
192
314
  exports.DEFAULT_INSET_TOP_BOTTOM_PT = DEFAULT_INSET_TOP_BOTTOM_PT;
315
+ exports.PICTURES_STREAM = PICTURES_STREAM;
193
316
  exports.POWERPOINT_DOCUMENT_STREAM = POWERPOINT_DOCUMENT_STREAM;
194
317
  exports.SUMMARY_INFORMATION_STREAM = SUMMARY_INFORMATION_STREAM;
195
318
  exports.readPpt = readPpt;
package/dist/read.d.cts CHANGED
@@ -1,17 +1,25 @@
1
- import { ContentSlide, DocumentTree, LayoutMetadata } from "document-schema.js";
1
+ import { ContentDocument, ContentEmbeddedObjectKind, ContentSlide, DocumentTree, LayoutMetadata } from "document-schema.js";
2
2
  //#region src/read.d.ts
3
3
  declare const CURRENT_USER_STREAM = "Current User";
4
4
  declare const POWERPOINT_DOCUMENT_STREAM = "PowerPoint Document";
5
+ declare const PICTURES_STREAM = "Pictures";
5
6
  /** The [MS-OLEPS] Property Set Stream a .ppt's title/author/dates live in when present ([MS-OSHARED] 2.3.3.2.2) -- a genuinely optional stream, unlike the two above, since a valid PowerPoint binary document need not carry document properties at all. */
6
7
  declare const SUMMARY_INFORMATION_STREAM = "SummaryInformation";
7
8
  declare const DEFAULT_INSET_LEFT_RIGHT_PT: number;
8
9
  declare const DEFAULT_INSET_TOP_BOTTOM_PT: number;
10
+ type DecodeEmbeddedObjectPort = (storageBytes: Uint8Array<ArrayBuffer>, progId: string | undefined) => {
11
+ readonly objectKind: ContentEmbeddedObjectKind;
12
+ readonly document: ContentDocument;
13
+ } | undefined;
14
+ interface ReadPptOptions {
15
+ readonly decodeEmbeddedObject?: DecodeEmbeddedObjectPort;
16
+ }
9
17
  interface PptDocument {
10
18
  readonly metadata: LayoutMetadata;
11
19
  readonly slides: readonly ContentSlide[];
12
20
  }
13
- declare function readPptStreams(currentUserStream: Uint8Array<ArrayBuffer>, powerPointDocumentStream: Uint8Array<ArrayBuffer>, password?: string): PptDocument;
14
- declare function readPptContent(bytes: Uint8Array<ArrayBuffer>, password?: string): PptDocument;
15
- declare function readPpt(bytes: Uint8Array<ArrayBuffer>, password?: string): DocumentTree;
21
+ declare function readPptStreams(currentUserStream: Uint8Array<ArrayBuffer>, powerPointDocumentStream: Uint8Array<ArrayBuffer>, password?: string, picturesStream?: Uint8Array<ArrayBuffer>, options?: ReadPptOptions): PptDocument;
22
+ declare function readPptContent(bytes: Uint8Array<ArrayBuffer>, password?: string, options?: ReadPptOptions): PptDocument;
23
+ declare function readPpt(bytes: Uint8Array<ArrayBuffer>, password?: string, options?: ReadPptOptions): DocumentTree;
16
24
  //#endregion
17
- export { CURRENT_USER_STREAM, DEFAULT_INSET_LEFT_RIGHT_PT, DEFAULT_INSET_TOP_BOTTOM_PT, POWERPOINT_DOCUMENT_STREAM, PptDocument, SUMMARY_INFORMATION_STREAM, readPpt, readPptContent, readPptStreams };
25
+ export { CURRENT_USER_STREAM, DEFAULT_INSET_LEFT_RIGHT_PT, DEFAULT_INSET_TOP_BOTTOM_PT, DecodeEmbeddedObjectPort, PICTURES_STREAM, POWERPOINT_DOCUMENT_STREAM, PptDocument, ReadPptOptions, SUMMARY_INFORMATION_STREAM, readPpt, readPptContent, readPptStreams };
package/dist/read.d.ts CHANGED
@@ -1,17 +1,25 @@
1
- import { ContentSlide, DocumentTree, LayoutMetadata } from "document-schema.js";
1
+ import { ContentDocument, ContentEmbeddedObjectKind, ContentSlide, DocumentTree, LayoutMetadata } from "document-schema.js";
2
2
  //#region src/read.d.ts
3
3
  declare const CURRENT_USER_STREAM = "Current User";
4
4
  declare const POWERPOINT_DOCUMENT_STREAM = "PowerPoint Document";
5
+ declare const PICTURES_STREAM = "Pictures";
5
6
  /** The [MS-OLEPS] Property Set Stream a .ppt's title/author/dates live in when present ([MS-OSHARED] 2.3.3.2.2) -- a genuinely optional stream, unlike the two above, since a valid PowerPoint binary document need not carry document properties at all. */
6
7
  declare const SUMMARY_INFORMATION_STREAM = "SummaryInformation";
7
8
  declare const DEFAULT_INSET_LEFT_RIGHT_PT: number;
8
9
  declare const DEFAULT_INSET_TOP_BOTTOM_PT: number;
10
+ type DecodeEmbeddedObjectPort = (storageBytes: Uint8Array<ArrayBuffer>, progId: string | undefined) => {
11
+ readonly objectKind: ContentEmbeddedObjectKind;
12
+ readonly document: ContentDocument;
13
+ } | undefined;
14
+ interface ReadPptOptions {
15
+ readonly decodeEmbeddedObject?: DecodeEmbeddedObjectPort;
16
+ }
9
17
  interface PptDocument {
10
18
  readonly metadata: LayoutMetadata;
11
19
  readonly slides: readonly ContentSlide[];
12
20
  }
13
- declare function readPptStreams(currentUserStream: Uint8Array<ArrayBuffer>, powerPointDocumentStream: Uint8Array<ArrayBuffer>, password?: string): PptDocument;
14
- declare function readPptContent(bytes: Uint8Array<ArrayBuffer>, password?: string): PptDocument;
15
- declare function readPpt(bytes: Uint8Array<ArrayBuffer>, password?: string): DocumentTree;
21
+ declare function readPptStreams(currentUserStream: Uint8Array<ArrayBuffer>, powerPointDocumentStream: Uint8Array<ArrayBuffer>, password?: string, picturesStream?: Uint8Array<ArrayBuffer>, options?: ReadPptOptions): PptDocument;
22
+ declare function readPptContent(bytes: Uint8Array<ArrayBuffer>, password?: string, options?: ReadPptOptions): PptDocument;
23
+ declare function readPpt(bytes: Uint8Array<ArrayBuffer>, password?: string, options?: ReadPptOptions): DocumentTree;
16
24
  //#endregion
17
- export { CURRENT_USER_STREAM, DEFAULT_INSET_LEFT_RIGHT_PT, DEFAULT_INSET_TOP_BOTTOM_PT, POWERPOINT_DOCUMENT_STREAM, PptDocument, SUMMARY_INFORMATION_STREAM, readPpt, readPptContent, readPptStreams };
25
+ export { CURRENT_USER_STREAM, DEFAULT_INSET_LEFT_RIGHT_PT, DEFAULT_INSET_TOP_BOTTOM_PT, DecodeEmbeddedObjectPort, PICTURES_STREAM, POWERPOINT_DOCUMENT_STREAM, PptDocument, ReadPptOptions, SUMMARY_INFORMATION_STREAM, readPpt, readPptContent, readPptStreams };
package/dist/read.js CHANGED
@@ -1,30 +1,49 @@
1
+ import { bytesToBase64 } from "./base64.js";
1
2
  import { PptEncryptedError, PptFormatError } from "./errors.js";
2
3
  import { childRecords, findChild } from "./record/tree.js";
3
- import { RT_ColorSchemeAtom, RT_Document, RT_DocumentAtom, RT_Drawing, RT_Environment, RT_MainMaster, RT_OutlineTextRefAtom, RT_Slide, RT_SlideAtom, RT_StyleTextPropAtom, RT_TextHeaderAtom, RT_TextMasterStyleAtom } from "./record/types.js";
4
+ import { RT_Document, RT_DocumentAtom, RT_Drawing, RT_Environment, RT_MainMaster, RT_OutlineTextRefAtom, RT_Slide, RT_SlideAtom, RT_StyleTextPropAtom, RT_TextHeaderAtom, RT_TextMasterStyleAtom } from "./record/types.js";
4
5
  import { readStyleTextPropAtom, readTextMasterStyleAtom } from "./text/style.js";
5
6
  import { characterCountOf, readTextBody, readTextHeaderAtom } from "./text/atoms.js";
6
- import { masterUnitsToPoints } from "./units.js";
7
- import { readSlideSchemeColorSchemeAtom } from "./document/color-scheme.js";
7
+ import { emuToPoints, masterUnitsToPoints } from "./units.js";
8
+ import { findSlideSchemeColorSchemeAtom, readSlideSchemeColorSchemeAtom } from "./document/color-scheme.js";
8
9
  import { buildMasterStyleTable, readSlideAtom } from "./document/master.js";
9
10
  import { buildParagraphs } from "./content.js";
10
11
  import { decryptPptDocumentStream } from "./encryption.js";
11
12
  import { readDocumentAtom } from "./document/document-atom.js";
12
13
  import { readFontNames } from "./document/fonts.js";
14
+ import { blipForPib, readBlipStore } from "./drawing/blips.js";
15
+ import "./drawing/properties.js";
13
16
  import { readNotesListWithText } from "./document/notes-list.js";
14
17
  import { readDrawingShapes } from "./drawing/shapes.js";
15
18
  import { readNotesContainerAtom, readNotesText } from "./document/notes.js";
16
19
  import { readSlideListWithText } from "./document/slide-list.js";
17
20
  import { readCurrentUserAtom } from "./stream/current-user.js";
21
+ import { readExObjIdRef, readExternalOleEmbeds, resolveOleObjectStorage } from "./ole/embedded.js";
18
22
  import { buildPersistDirectory, resolvePersistObject } from "./stream/persist.js";
19
23
  import { readCompoundFile, readSummaryInformation, summaryInformationToLayoutMetadata } from "archive-codec";
20
24
  import { assembleTree } from "document-schema.js";
21
25
  //#region src/read.ts
22
26
  const CURRENT_USER_STREAM = "Current User";
23
27
  const POWERPOINT_DOCUMENT_STREAM = "PowerPoint Document";
28
+ const PICTURES_STREAM = "Pictures";
24
29
  /** The [MS-OLEPS] Property Set Stream a .ppt's title/author/dates live in when present ([MS-OSHARED] 2.3.3.2.2) -- a genuinely optional stream, unlike the two above, since a valid PowerPoint binary document need not carry document properties at all. */
25
30
  const SUMMARY_INFORMATION_STREAM = "SummaryInformation";
26
31
  const DEFAULT_INSET_LEFT_RIGHT_PT = 7.2;
27
32
  const DEFAULT_INSET_TOP_BOTTOM_PT = 3.6;
33
+ function insetsForShape(properties, isPicture) {
34
+ const defaultLeftRight = isPicture ? 0 : DEFAULT_INSET_LEFT_RIGHT_PT;
35
+ const defaultTopBottom = isPicture ? 0 : DEFAULT_INSET_TOP_BOTTOM_PT;
36
+ const emuOrDefault = (opid, fallback) => {
37
+ const property = properties.get(opid);
38
+ return property === void 0 ? fallback : emuToPoints(property.value);
39
+ };
40
+ return {
41
+ insetLeftPt: emuOrDefault(129, defaultLeftRight),
42
+ insetTopPt: emuOrDefault(130, defaultTopBottom),
43
+ insetRightPt: emuOrDefault(131, defaultLeftRight),
44
+ insetBottomPt: emuOrDefault(132, defaultTopBottom)
45
+ };
46
+ }
28
47
  const NO_STYLE = {
29
48
  paragraphRuns: [],
30
49
  characterRuns: []
@@ -54,14 +73,73 @@ function textRecordsFor(clientTextbox, persist) {
54
73
  records: outlineText.records
55
74
  };
56
75
  }
57
- function blocksFor(clientTextbox, persist, fontNames, masterInfo) {
76
+ function blocksFor(clientTextbox, context) {
58
77
  if (clientTextbox === void 0) return [];
59
- const { textType, records } = textRecordsFor(clientTextbox, persist);
78
+ const { textType, records } = textRecordsFor(clientTextbox, context.persist);
60
79
  const text = readTextBody(records);
61
80
  if (text === void 0) return [];
62
81
  const styleRecord = findChild(records, RT_StyleTextPropAtom);
63
82
  const style = styleRecord === void 0 ? NO_STYLE : readStyleTextPropAtom(styleRecord, characterCountOf(text));
64
- return buildParagraphs(text, style, fontNames, masterInfo.styles, textType, masterInfo.colorScheme);
83
+ return buildParagraphs(text, style, context.fontNames, context.masterInfo.styles, textType, context.masterInfo.colorScheme);
84
+ }
85
+ function imageBlocksFor(pibProperty, context, widthPt, heightPt) {
86
+ if (pibProperty === void 0) return [];
87
+ const blip = blipForPib(context.blips, pibProperty.value);
88
+ if (blip === void 0) return [];
89
+ return [{
90
+ kind: "image",
91
+ format: blip.format,
92
+ base64: bytesToBase64(blip.bytes),
93
+ widthPt,
94
+ heightPt
95
+ }];
96
+ }
97
+ function embeddedObjectBlocksFor(clientData, context, frame) {
98
+ if (clientData === void 0 || context.decodeEmbeddedObject === void 0) return [];
99
+ const exObjId = readExObjIdRef(clientData);
100
+ if (exObjId === void 0) return [];
101
+ const embed = context.externalOleEmbeds.get(exObjId);
102
+ if (embed === void 0) return [];
103
+ const storageBytes = resolveOleObjectStorage(context.streamBytes, context.directory, embed.persistIdRef);
104
+ if (storageBytes === void 0) return [];
105
+ const decoded = context.decodeEmbeddedObject(storageBytes, embed.progId);
106
+ if (decoded === void 0) return [];
107
+ return [{
108
+ kind: "embeddedObject",
109
+ objectKind: decoded.objectKind,
110
+ document: decoded.document,
111
+ frame
112
+ }];
113
+ }
114
+ function tableBlockFor(table, context) {
115
+ const placed = table.cells.flatMap((cell) => cell.anchor === void 0 || cell.anchor.right <= cell.anchor.left || cell.anchor.bottom <= cell.anchor.top ? [] : [{
116
+ cell,
117
+ anchor: cell.anchor
118
+ }]);
119
+ const rowTops = [...new Set(placed.map((entry) => entry.anchor.top))].sort((a, b) => a - b);
120
+ const columnLefts = [...new Set(placed.map((entry) => entry.anchor.left))].sort((a, b) => a - b);
121
+ const rightmost = Math.max(...placed.map((entry) => entry.anchor.right), ...columnLefts);
122
+ const columnWidthsPt = columnLefts.map((left, index) => {
123
+ const right = columnLefts.at(index + 1) ?? rightmost;
124
+ return masterUnitsToPoints(right - left);
125
+ });
126
+ return {
127
+ kind: "table",
128
+ rows: rowTops.map((top) => {
129
+ const inRow = placed.filter((entry) => entry.anchor.top === top).sort((a, b) => a.anchor.left - b.anchor.left);
130
+ const bottom = Math.max(...inRow.map((entry) => entry.anchor.bottom), top);
131
+ const cells = columnLefts.map((left) => {
132
+ const at = inRow.find((entry) => entry.anchor.left === left);
133
+ return at === void 0 ? { blocks: [] } : { blocks: blocksFor(at.cell.clientTextbox, context) };
134
+ });
135
+ const heightPt = masterUnitsToPoints(bottom - top);
136
+ return heightPt > 0 ? {
137
+ cells,
138
+ heightPt
139
+ } : { cells };
140
+ }),
141
+ columnWidthsPt
142
+ };
65
143
  }
66
144
  function readNotesBySlideId(streamBytes, directory, notesList) {
67
145
  const notes = /* @__PURE__ */ new Map();
@@ -73,7 +151,7 @@ function readNotesBySlideId(streamBytes, directory, notesList) {
73
151
  return notes;
74
152
  }
75
153
  function colorSchemeFor(slideChildren, master) {
76
- const ownScheme = findChild(slideChildren, RT_ColorSchemeAtom);
154
+ const ownScheme = findSlideSchemeColorSchemeAtom(slideChildren);
77
155
  return ownScheme === void 0 ? master.colorScheme : readSlideSchemeColorSchemeAtom(ownScheme);
78
156
  }
79
157
  function readMastersById(streamBytes, directory, masterList, documentDefault) {
@@ -85,7 +163,7 @@ function readMastersById(streamBytes, directory, masterList, documentDefault) {
85
163
  const masterChildren = childRecords(masterContainer);
86
164
  const masterAtoms = masterChildren.filter((record) => record.header.recType === RT_TextMasterStyleAtom).map(readTextMasterStyleAtom);
87
165
  const styles = buildMasterStyleTable(masterAtoms, documentDefault);
88
- const colorSchemeRecord = findChild(masterChildren, RT_ColorSchemeAtom);
166
+ const colorSchemeRecord = findSlideSchemeColorSchemeAtom(masterChildren);
89
167
  if (colorSchemeRecord === void 0) throw new PptFormatError(`MainMasterContainer for master ${persist.slideId} has no SlideSchemeColorSchemeAtom, which [MS-PPT] 2.5.3 requires`);
90
168
  mastersById.set(persist.slideId, {
91
169
  styles,
@@ -94,7 +172,8 @@ function readMastersById(streamBytes, directory, masterList, documentDefault) {
94
172
  }
95
173
  return mastersById;
96
174
  }
97
- function readSlide(streamBytes, directory, persist, size, fontNames, notes, mastersById) {
175
+ function readSlide(base, size, notes, mastersById) {
176
+ const { streamBytes, directory, persist } = base;
98
177
  const slideContainer = resolvePersistObject(streamBytes, directory, persist.persistIdRef, `SlidePersistAtom for slide ${persist.slideId}`);
99
178
  if (slideContainer.header.recType !== 1006) throw new PptFormatError(`persist object ${persist.persistIdRef} is record type 0x${slideContainer.header.recType.toString(16)}, not the RT_Slide (0x${RT_Slide.toString(16)}) its SlidePersistAtom promised`);
100
179
  const slideChildren = childRecords(slideContainer);
@@ -103,28 +182,60 @@ function readSlide(streamBytes, directory, persist, size, fontNames, notes, mast
103
182
  const { masterIdRef } = readSlideAtom(slideAtomRecord);
104
183
  const master = mastersById.get(masterIdRef);
105
184
  if (master === void 0) throw new PptFormatError(`slide ${persist.slideId}'s own SlideAtom names masterIdRef ${masterIdRef}, which the master list does not contain`);
106
- const masterInfo = {
107
- styles: master.styles,
108
- colorScheme: colorSchemeFor(slideChildren, master)
185
+ const context = {
186
+ ...base,
187
+ masterInfo: {
188
+ styles: master.styles,
189
+ colorScheme: colorSchemeFor(slideChildren, master)
190
+ }
109
191
  };
110
192
  const drawing = findChild(slideChildren, RT_Drawing);
111
193
  const shapes = [];
112
194
  for (const shape of drawing === void 0 ? [] : readDrawingShapes(drawing)) {
195
+ if (!("clientTextbox" in shape)) {
196
+ const tableLeft = masterUnitsToPoints(shape.anchor.left);
197
+ const tableTop = masterUnitsToPoints(shape.anchor.top);
198
+ shapes.push({
199
+ frame: {
200
+ xPt: tableLeft,
201
+ yPt: tableTop,
202
+ widthPt: Math.max(0, masterUnitsToPoints(shape.anchor.right) - tableLeft),
203
+ heightPt: Math.max(0, masterUnitsToPoints(shape.anchor.bottom) - tableTop)
204
+ },
205
+ ...shape.rotationDeg === void 0 ? {} : { rotationDeg: shape.rotationDeg },
206
+ insetLeftPt: DEFAULT_INSET_LEFT_RIGHT_PT,
207
+ insetTopPt: DEFAULT_INSET_TOP_BOTTOM_PT,
208
+ insetRightPt: DEFAULT_INSET_LEFT_RIGHT_PT,
209
+ insetBottomPt: DEFAULT_INSET_TOP_BOTTOM_PT,
210
+ blocks: [tableBlockFor(shape, context)]
211
+ });
212
+ continue;
213
+ }
113
214
  if (shape.anchor === void 0) continue;
114
215
  const left = masterUnitsToPoints(shape.anchor.left);
115
216
  const top = masterUnitsToPoints(shape.anchor.top);
217
+ const widthPt = Math.max(0, masterUnitsToPoints(shape.anchor.right) - left);
218
+ const heightPt = Math.max(0, masterUnitsToPoints(shape.anchor.bottom) - top);
219
+ const isPicture = shape.properties.get(260) !== void 0;
116
220
  shapes.push({
117
221
  frame: {
118
222
  xPt: left,
119
223
  yPt: top,
120
- widthPt: Math.max(0, masterUnitsToPoints(shape.anchor.right) - left),
121
- heightPt: Math.max(0, masterUnitsToPoints(shape.anchor.bottom) - top)
224
+ widthPt,
225
+ heightPt
122
226
  },
123
- insetLeftPt: DEFAULT_INSET_LEFT_RIGHT_PT,
124
- insetTopPt: DEFAULT_INSET_TOP_BOTTOM_PT,
125
- insetRightPt: DEFAULT_INSET_LEFT_RIGHT_PT,
126
- insetBottomPt: DEFAULT_INSET_TOP_BOTTOM_PT,
127
- blocks: blocksFor(shape.clientTextbox, persist, fontNames, masterInfo)
227
+ ...shape.rotationDeg === void 0 ? {} : { rotationDeg: shape.rotationDeg },
228
+ ...insetsForShape(shape.properties, isPicture),
229
+ blocks: [
230
+ ...imageBlocksFor(shape.properties.get(260), context, widthPt, heightPt),
231
+ ...blocksFor(shape.clientTextbox, context),
232
+ ...embeddedObjectBlocksFor(shape.clientData, context, {
233
+ xPt: left,
234
+ yPt: top,
235
+ widthPt,
236
+ heightPt
237
+ })
238
+ ]
128
239
  });
129
240
  }
130
241
  return {
@@ -133,7 +244,7 @@ function readSlide(streamBytes, directory, persist, size, fontNames, notes, mast
133
244
  notes
134
245
  };
135
246
  }
136
- function readPptStreams(currentUserStream, powerPointDocumentStream, password) {
247
+ function readPptStreams(currentUserStream, powerPointDocumentStream, password, picturesStream, options) {
137
248
  const currentUser = readCurrentUserAtom(currentUserStream);
138
249
  const { directory, currentEdit } = buildPersistDirectory(powerPointDocumentStream, currentUser.offsetToCurrentEdit);
139
250
  let streamBytes = powerPointDocumentStream;
@@ -161,14 +272,25 @@ function readPptStreams(currentUserStream, powerPointDocumentStream, password) {
161
272
  const slideList = listWithInstance(0);
162
273
  const persists = slideList === void 0 ? [] : readSlideListWithText(slideList);
163
274
  const notesBySlideId = readNotesBySlideId(streamBytes, directory, listWithInstance(2));
275
+ const blips = readBlipStore(documentContainer, currentUser.encrypted ? void 0 : picturesStream);
276
+ const externalOleEmbeds = readExternalOleEmbeds(children);
164
277
  return {
165
278
  metadata: {},
166
- slides: persists.map((persist) => readSlide(streamBytes, directory, persist, size, fontNames, notesBySlideId.get(persist.slideId) ?? "", mastersById))
279
+ slides: persists.map((persist) => readSlide({
280
+ streamBytes,
281
+ directory,
282
+ persist,
283
+ fontNames,
284
+ blips,
285
+ externalOleEmbeds,
286
+ decodeEmbeddedObject: options?.decodeEmbeddedObject
287
+ }, size, notesBySlideId.get(persist.slideId) ?? "", mastersById))
167
288
  };
168
289
  }
169
- function readPptContent(bytes, password) {
290
+ function readPptContent(bytes, password, options) {
170
291
  const streams = readCompoundFile(bytes);
171
- const document = readPptStreams(requireStream(streams, CURRENT_USER_STREAM), requireStream(streams, POWERPOINT_DOCUMENT_STREAM), password);
292
+ const pictures = streams.find((stream) => stream.path === PICTURES_STREAM);
293
+ const document = readPptStreams(requireStream(streams, CURRENT_USER_STREAM), requireStream(streams, POWERPOINT_DOCUMENT_STREAM), password, pictures === void 0 ? void 0 : pictures.bytes, options);
172
294
  const metadataStream = streams.find((stream) => stream.path === SUMMARY_INFORMATION_STREAM);
173
295
  if (metadataStream === void 0) return document;
174
296
  return {
@@ -176,8 +298,8 @@ function readPptContent(bytes, password) {
176
298
  metadata: summaryInformationToLayoutMetadata(readSummaryInformation(metadataStream.bytes))
177
299
  };
178
300
  }
179
- function readPpt(bytes, password) {
180
- const { metadata, slides } = readPptContent(bytes, password);
301
+ function readPpt(bytes, password, options) {
302
+ const { metadata, slides } = readPptContent(bytes, password, options);
181
303
  const document = {
182
304
  kind: "presentation",
183
305
  metadata,
@@ -186,4 +308,4 @@ function readPpt(bytes, password) {
186
308
  return assembleTree(document);
187
309
  }
188
310
  //#endregion
189
- export { CURRENT_USER_STREAM, DEFAULT_INSET_LEFT_RIGHT_PT, DEFAULT_INSET_TOP_BOTTOM_PT, POWERPOINT_DOCUMENT_STREAM, SUMMARY_INFORMATION_STREAM, readPpt, readPptContent, readPptStreams };
311
+ export { CURRENT_USER_STREAM, DEFAULT_INSET_LEFT_RIGHT_PT, DEFAULT_INSET_TOP_BOTTOM_PT, PICTURES_STREAM, POWERPOINT_DOCUMENT_STREAM, SUMMARY_INFORMATION_STREAM, readPpt, readPptContent, readPptStreams };