@openpresentation/opf-pptx 0.11.8 → 0.11.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -39,8 +39,20 @@ export interface MediaProvenanceDiagnostic { code: "media-provenance-omitted"; p
39
39
  export interface ChartDataUnplottableDiagnostic { code: "chart-data-unplottable"; path: string; message: string; reason: "data-not-inline" | "no-rows" | "no-columns" | "single-column-not-numeric" }
40
40
  /** Content with no PowerPoint export (an empty table, an unsupported payload) is replaced by a plain-language placeholder frame. */
41
41
  export interface ContentPlaceholderDiagnostic { code: "content-placeholder"; path: string; message: string; reason: "table-has-no-rows" | "unsupported-payload" }
42
- /** An image that has no embeddable raster. `reason: "unsupported-format"`: the embedded bytes are no readable PNG, JPEG, GIF or WebP (an SVG, for example); the preview's placeholder was exported instead (or no watermark, or the background colour). `strictAssets` throws `unsupported-image-dimensions` instead. */
43
- export interface UnresolvedAssetDiagnostic { code: "unresolved-asset"; path: string; message: string; reason?: "unsupported-format" }
42
+ /**
43
+ * An image that cannot be exported as a picture: the preview's placeholder was exported instead (or no watermark, or the background colour).
44
+ * `reason: "unsupported-format"`: the embedded bytes are no readable PNG, JPEG, GIF, WebP or SVG. For an SVG (a native SVG picture over a PNG fallback):
45
+ * `"svg-malformed"` (not well-formed XML, or no `xmlns` SVG root), `"svg-no-size"` (no width and height and no viewBox), `"svg-too-large"` (over 8 MiB),
46
+ * `"svg-unsafe"` (a DOCTYPE with external or markup entities), `"svg-rasterizer-unavailable"` (no `options.svgRasterizer` and `@openpresentation/opf-render`
47
+ * is not installed; always the case in a browser build without `svgRasterizer`) `"svg-render-failed"` (the rasterizer threw or returned no PNG) or `"svg-unreadable"` (a local `.svg` path that could not be read).
48
+ * `strictAssets` throws `unsupported-image-dimensions` for an unreadable raster, `invalid-svg-image` for the SVG content reasons above (and `svg-unreadable`), `svg-rasterizer-unavailable` for a
49
+ * missing rasterizer and `svg-render-failed` for a failed one instead.
50
+ */
51
+ export interface UnresolvedAssetDiagnostic { code: "unresolved-asset"; path: string; message: string; reason?: "unsupported-format" | "svg-malformed" | "svg-no-size" | "svg-too-large" | "svg-unsafe" | "svg-rasterizer-unavailable" | "svg-render-failed" | "svg-unreadable" }
52
+ /** An SVG picture had scripts, `foreignObject`, event handlers, references outside the file, `@import` rules or a DOCTYPE; they were removed from the embedded SVG (on import too). Nothing in an SVG is run or fetched. */
53
+ export interface SvgSanitizedDiagnostic { code: "svg-sanitized"; path: string; message: string }
54
+ /** An SVG slide image with a duotone recolor or a non-rectangular shape exports as its PNG raster, not as a native SVG picture, so the effect applies as in the preview (PowerPoint applies opacity, grayscale and a border to an SVG picture, and those stay native). */
55
+ export interface SvgImageRasterizedDiagnostic { code: "svg-image-rasterized"; path: string; message: string }
44
56
  export interface WatermarkNotExportedDiagnostic { code: "watermark-not-exported"; path: string; message: string }
45
57
  /**
46
58
  * The chart data was reshaped to export a native chart: a single value column was plotted against row numbers; a one-series construct
@@ -82,10 +94,18 @@ export interface ToPptxOptions {
82
94
  /** Match preview/pagination clearance around supplied vector text outlines; default 1. */
83
95
  textRasterPadding?: number;
84
96
  /** Layout diagnostics, `media-provenance-omitted` when video data cannot be stored, plus `unresolved-font-scheme` (once per reference path) when a font-scheme id matches no record and the default `aptos` scheme is used as the base. */
85
- onDiagnostic?: (diagnostic: LayoutDiagnostic | FontSchemeDiagnostic | MediaProvenanceDiagnostic | ChartDataUnplottableDiagnostic | ChartDataAdaptedDiagnostic | ChartMapGeodataDiagnostic | ContentPlaceholderDiagnostic | UnresolvedAssetDiagnostic | WatermarkNotExportedDiagnostic) => void;
97
+ onDiagnostic?: (diagnostic: LayoutDiagnostic | FontSchemeDiagnostic | MediaProvenanceDiagnostic | ChartDataUnplottableDiagnostic | ChartDataAdaptedDiagnostic | ChartMapGeodataDiagnostic | ContentPlaceholderDiagnostic | UnresolvedAssetDiagnostic | WatermarkNotExportedDiagnostic | SvgSanitizedDiagnostic | SvgImageRasterizedDiagnostic) => void;
86
98
  baseDir?: string;
87
99
  compressionLevel?: number;
88
100
  imageResolver?: (src: string, context: ImageResolverContext) => ImageResolverResult | Promise<ImageResolverResult | null | undefined> | null | undefined;
101
+ /**
102
+ * Draws the PNG fallback of an SVG picture (the raster older viewers show; PowerPoint 2016 and Microsoft 365 draw the SVG itself). It receives the
103
+ * sanitized SVG text and the pixel size to draw (the SVG's own aspect, about 192 dpi of the displayed size) and returns PNG bytes with a transparent
104
+ * background. It must be deterministic for the export to be. Default in Node: opf-render's `svgToPng` (resvg with the bundled fonts), loaded only when an
105
+ * SVG is exported (optional peer `@openpresentation/opf-render`). Without it, and without opf-render, an SVG picture exports as the "Image unavailable"
106
+ * placeholder with an `unresolved-asset` diagnostic (`reason: "svg-rasterizer-unavailable"`); a browser build has no default.
107
+ */
108
+ svgRasterizer?: (svg: string, size: { width: number; height: number; scale: number; text: boolean }) => Uint8Array | Promise<Uint8Array>;
89
109
  seed?: number;
90
110
  strictAssets?: boolean;
91
111
  timestamp?: string;
@@ -119,6 +139,12 @@ export interface FromPptxOptions {
119
139
  catalogs?: Record<string, { records?: unknown[] } | unknown[]>;
120
140
  /** The export's records for document `catalogs.<kind>.source` URLs (as in ToPptxOptions). Currently used for socialPlatforms. */
121
141
  catalogSources?: Record<string, { records?: unknown[] } | unknown[]>;
142
+ /**
143
+ * Opt in to raw per-shape signals (see PptxSignals). `true` uses the default limits; an object lowers or raises them.
144
+ * With it, `fromPptx` resolves to `{document, signals}`; without it (the default, also `false`/`null`) it resolves
145
+ * to the OPF document exactly as before. The document is the same either way.
146
+ */
147
+ signals?: boolean | PptxSignalOptions | null;
122
148
  }
123
149
 
124
150
  export declare class OPFPptxError extends Error {
@@ -131,7 +157,263 @@ export declare class OPFPptxError extends Error {
131
157
 
132
158
  export declare function toPptx(input: unknown, options?: ToPptxOptions): Promise<Uint8Array>;
133
159
 
134
- export declare function fromPptx(input: Uint8Array | ArrayBuffer, options?: FromPptxOptions): Promise<Record<string, unknown>>;
160
+ export declare function fromPptx(input: Uint8Array | ArrayBuffer, options?: Omit<FromPptxOptions, "signals"> & { signals?: false | null }): Promise<Record<string, unknown>>;
161
+ export declare function fromPptx(input: Uint8Array | ArrayBuffer, options: Omit<FromPptxOptions, "signals"> & { signals: true | PptxSignalOptions }): Promise<PptxImportWithSignals>;
162
+ export declare function fromPptx(input: Uint8Array | ArrayBuffer, options?: FromPptxOptions): Promise<Record<string, unknown> | PptxImportWithSignals>;
163
+
164
+ // ---------------------------------------------------------------------------
165
+ // Import signals: `fromPptx(bytes, {signals: true})`.
166
+ //
167
+ // Raw, deterministic facts about every shape of a deck, for hosts that classify or restructure imported
168
+ // decks (a model, a rules engine, a person). Nothing here is inferred from meaning: it is read from the package
169
+ // with PowerPoint's inheritance applied (shape, layout, master, presentation defaults, theme). It is plain
170
+ // JSON and bounded by PptxSignalOptions. Omitted fields mean "not stated"; defaults are omitted too
171
+ // (`bold`, `italic`, `monospace` absent = false, `weight` absent = 400, `level` absent = 0, `align` absent = "left",
172
+ // `bullet` absent = none).
173
+
174
+ /** Bounds on the signals. Each is an integer from 1 up to the cap in parentheses; the defaults are DEFAULT_SIGNAL_LIMITS. */
175
+ export interface PptxSignalOptions {
176
+ /** Slides reported (5000). The document always has every slide. Default 300. */
177
+ maxSlides?: number;
178
+ /** Shapes reported per slide, groups and their members each counting one (5000). Default 300. */
179
+ maxShapesPerSlide?: number;
180
+ /** Shapes reported over the whole deck (100000). Default 5000. */
181
+ maxTotalShapes?: number;
182
+ /** Paragraphs reported per shape (5000). Default 200. */
183
+ maxParagraphsPerShape?: number;
184
+ /** Characters of text reported per shape (200000). Default 8000. */
185
+ maxTextCharsPerShape?: number;
186
+ /** Characters of shape and table text reported over the whole deck (5000000). Default 400000. */
187
+ maxTextCharsTotal?: number;
188
+ /** Table cells reported per table (10000). Default 400. */
189
+ maxTableCells?: number;
190
+ /** Characters reported per table cell (5000). Default 200. */
191
+ maxTableCellChars?: number;
192
+ /** Group nesting levels walked (32). Default 12. */
193
+ maxGroupDepth?: number;
194
+ }
195
+ export declare const DEFAULT_SIGNAL_LIMITS: Readonly<Required<PptxSignalOptions>>;
196
+ /** The `version` of the PptxSignals format; it changes only when a field is removed or its meaning changes. */
197
+ export declare const SIGNALS_VERSION: 1;
198
+ /** True for a family known to be fixed-width: the list the `monospace` flag uses. */
199
+ export declare function isMonospaceFamily(family: string | undefined): boolean;
200
+
201
+ export interface PptxImportWithSignals {
202
+ /** The same OPF document `fromPptx` returns without the option. */
203
+ document: Record<string, unknown>;
204
+ signals: PptxSignals;
205
+ }
206
+
207
+ export interface PptxSignals {
208
+ version: 1;
209
+ deck: {
210
+ slideCount: number;
211
+ /** Slide size in EMU and in reference px (96 per inch, 9525 EMU per px). */
212
+ dimensions?: {widthEmu: number; heightEmu: number; widthPx: number; heightPx: number};
213
+ referencePxPerInch: 96;
214
+ theme: {
215
+ colorSchemeName?: string;
216
+ fontSchemeName?: string;
217
+ majorFont?: string;
218
+ minorFont?: string;
219
+ /** The twelve theme slots (dark1, light1, dark2, light2, accent1-6, hyperlink, followedHyperlink) as #RRGGBB. */
220
+ colors: Record<string, string>;
221
+ };
222
+ /** Whether the deck carries OPF provenance tags (an OPF export): structure then also round-trips exactly. */
223
+ provenance: {opf: boolean; opfTaggedSlides: number};
224
+ };
225
+ /** The limits in force, so a consumer knows what a missing shape or truncated text means. */
226
+ limits: Required<PptxSignalOptions>;
227
+ slides: PptxSlideSignals[];
228
+ /** Present when a bound dropped content: `slides` = slides not reported, `text` = the deck-wide text budget ran out. */
229
+ truncated?: {slides?: number; text?: true};
230
+ }
231
+
232
+ export interface PptxSlideSignals {
233
+ /** Slide position, the same index as `document.slides`. */
234
+ index: number;
235
+ /** Package part, for example "ppt/slides/slide1.xml". */
236
+ part: string;
237
+ name?: string;
238
+ hidden?: true;
239
+ layout: {part: string | null; name: string | null; type?: string};
240
+ /** `theme` is the master's theme name. */
241
+ master: {part: string | null; name: string | null; theme: string | null};
242
+ /** "match" / "changed" for a slide that carries an OPF slide tag, "untagged" otherwise. */
243
+ provenance: "match" | "changed" | "untagged";
244
+ /** Every shape in z-order (document order, back to front), groups followed by their members. */
245
+ shapes: PptxShapeSignal[];
246
+ stats: {shapes: number; text: number; pictures: number; tables: number; charts: number; groups: number};
247
+ /** Counts of what a bound dropped on this slide: shapes, depth (group members below maxGroupDepth), paragraphs, text, table. */
248
+ truncated?: Partial<Record<"shapes" | "depth" | "paragraphs" | "text" | "table", number>>;
249
+ }
250
+
251
+ export type PptxShapeKind = "text" | "shape" | "placeholder" | "picture" | "media" | "table" | "chart" | "smartart" | "ole" | "group" | "connector" | "line" | "unknown";
252
+
253
+ export interface PptxBox {
254
+ /** Reference px (96 per inch), two decimals, in slide coordinates (group transforms applied). */
255
+ px: {x: number; y: number; w: number; h: number};
256
+ /** The same box in EMU, rounded. */
257
+ emu: {x: number; y: number; w: number; h: number};
258
+ /** "own" the shape's xfrm; "layout" / "master" an inherited placeholder box. */
259
+ source: "own" | "layout" | "master";
260
+ /** True when an enclosing group is rotated or flipped: the box ignores that rotation. */
261
+ approximate?: true;
262
+ }
263
+
264
+ export interface PptxColor {
265
+ /** #RRGGBB after theme and colour-map resolution (luminance and alpha modifiers applied). Absent when it cannot be resolved. */
266
+ color?: string;
267
+ /** The theme slot the colour was named by (for example "accent1", "tx1"). */
268
+ colorRef?: string;
269
+ /** Opacity below 1. */
270
+ alpha?: number;
271
+ }
272
+
273
+ export interface PptxRunSignal extends PptxColor {
274
+ text: string;
275
+ /** Effective Latin family; theme font tokens resolved. */
276
+ font?: string;
277
+ /** Points. */
278
+ size?: number;
279
+ bold?: true;
280
+ italic?: true;
281
+ /** CSS weight: 700 for bold, or the weight a family name states ("Segoe UI Semibold" = 600). Absent for 400. */
282
+ weight?: number;
283
+ underline?: true;
284
+ strike?: true;
285
+ baseline?: "superscript" | "subscript";
286
+ caps?: "all" | "small";
287
+ /** True when the font is a known fixed-width family. */
288
+ monospace?: true;
289
+ /** External hyperlink target. */
290
+ link?: string;
291
+ /** A hyperlink with a PowerPoint action rather than a URL. */
292
+ linkAction?: string;
293
+ /** Field type (slidenum, datetime1, ...); `text` is its cached value. */
294
+ field?: string;
295
+ /** A soft line break; `text` is a newline. */
296
+ lineBreak?: true;
297
+ }
298
+
299
+ export interface PptxParagraphSignal {
300
+ text: string;
301
+ /** Indent level, 0-8; absent = 0. */
302
+ level?: number;
303
+ /** Absent = left. */
304
+ align?: "center" | "right" | "justify" | "distributed";
305
+ /** Effective bullet (inherited from the list style when the paragraph states none); absent = none. */
306
+ bullet?: {kind: "char"; char?: string} | {kind: "number"; scheme: string; startAt?: number} | {kind: "picture"};
307
+ marginLeftPx?: number;
308
+ indentPx?: number;
309
+ lineSpacingPct?: number;
310
+ lineSpacingPt?: number;
311
+ spaceBeforePt?: number;
312
+ spaceBeforePct?: number;
313
+ spaceAfterPt?: number;
314
+ spaceAfterPct?: number;
315
+ /** Adjacent runs with the same effective style are merged. */
316
+ runs: PptxRunSignal[];
317
+ /** True when a bound cut `text` and `runs`. */
318
+ truncated?: true;
319
+ }
320
+
321
+ export interface PptxTextSignal {
322
+ paragraphs: PptxParagraphSignal[];
323
+ /** Paragraph count before any bound. */
324
+ paragraphCount: number;
325
+ /** Characters before any bound. */
326
+ chars: number;
327
+ maxFontSize?: number;
328
+ dominantFont?: string;
329
+ dominantSize?: number;
330
+ /** Share (0-1) of non-space characters set in monospace families; absent when none are. */
331
+ monospaceShare?: number;
332
+ bulletedParagraphs?: number;
333
+ anchor?: "top" | "middle" | "bottom";
334
+ autofit?: "shrink" | "resize" | "none";
335
+ wrap?: "none";
336
+ vertical?: string;
337
+ columns?: number;
338
+ }
339
+
340
+ export interface PptxFillSignal extends PptxColor {
341
+ /** "style" is a theme fill style reference (`styleIndex`) with the shape's colour. */
342
+ kind: "none" | "solid" | "gradient" | "picture" | "pattern" | "group" | "style";
343
+ source?: "placeholder" | "style";
344
+ styleIndex?: number;
345
+ stops?: Array<PptxColor & {position: number}>;
346
+ angle?: number;
347
+ pattern?: string;
348
+ }
349
+
350
+ export interface PptxOutlineSignal extends PptxColor {
351
+ kind: "none" | "solid" | "gradient" | "pattern" | "inherited" | "style";
352
+ source?: "placeholder" | "style";
353
+ styleIndex?: number;
354
+ widthPt?: number;
355
+ dash?: string;
356
+ headArrow?: string;
357
+ tailArrow?: string;
358
+ }
359
+
360
+ /** Where a shape went in the OPF document `fromPptx` returned. */
361
+ export interface PptxOpfLink {
362
+ /**
363
+ * "block" fed a block (`path`, `blockType`); "title", "subtitle" and "tag" fed that slide field. Other roles name a shape the
364
+ * importer consumed without a block of its own: "furniture" (footer, date, number, section), "slide-image", "watermark", "logo",
365
+ * and the members of an OPF-tagged group ("code", "metric", "quote", "timeline", "media", "card-frame").
366
+ */
367
+ role: string;
368
+ /** Path in the document, for example "slides.2.blocks.1", "slides.0.title", or "slides.1.left" after a tagged round trip. */
369
+ path?: string;
370
+ blockType?: string;
371
+ }
372
+
373
+ export interface PptxShapeSignal {
374
+ /** "s" plus the z-order index; stable under lower limits. */
375
+ id: string;
376
+ /** 0 = back. */
377
+ zOrder: number;
378
+ /** The shape's `id` in the slide XML. */
379
+ nativeId?: number;
380
+ name: string;
381
+ kind: PptxShapeKind;
382
+ /** The id of the enclosing group, or null. */
383
+ parent: string | null;
384
+ /** Group nesting depth, 0 at the slide. */
385
+ depth: number;
386
+ /** Member ids, for a group. */
387
+ children?: string[];
388
+ /** Alt text and title. */
389
+ alt?: string;
390
+ title?: string;
391
+ hidden?: true;
392
+ /** The shape carries OPF tags (an OPF export, edited or not). */
393
+ opfTagged?: true;
394
+ /** Placeholder type ("title", "body", "obj", "sldNum", ...), idx and size. */
395
+ placeholder?: {type: string; idx?: number; size?: string; orientation?: string};
396
+ /** Null when no box is stated or inherited. The frame before rotation. */
397
+ box: PptxBox | null;
398
+ /** Degrees clockwise. */
399
+ rotation?: number;
400
+ flipH?: true;
401
+ flipV?: true;
402
+ geometry?: string;
403
+ fill?: PptxFillSignal;
404
+ outline?: PptxOutlineSignal;
405
+ text?: PptxTextSignal;
406
+ picture?: {part?: string; mediaType?: string; bytes?: number; naturalPx?: {w: number; h: number}; /** Percent cropped per side. */ crop?: {l: number; t: number; r: number; b: number}; tiled?: true; unresolved?: true};
407
+ mediaKind?: "video" | "audio";
408
+ table?: {rows: number; columns: number; columnWidthsPx?: number[]; firstRow: boolean; firstColumn: boolean; bandedRows: boolean; merged?: true; cells: string[][]; truncated?: true};
409
+ chart?: {extended: boolean; part?: string; chartTypes?: string[]; seriesCount?: number; pointCount?: number; barDirection?: string; grouping?: string; title?: string; unreadable?: true};
410
+ graphicType?: string;
411
+ /** The link to the imported OPF; null when the importer made nothing of this shape (a connector, a member of an unsupported frame). */
412
+ opf: PptxOpfLink | null;
413
+ /** Set when this shape's XML could not be read; the rest of the deck is unaffected. */
414
+ unreadable?: string;
415
+ }
416
+
135
417
 
136
418
  export interface TypefaceEntry {
137
419
  /** Part path; parts of nested packages use "outer.xlsx!/inner/part.xml". */