ppt-codec 1.6.0 → 1.7.1

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 (42) hide show
  1. package/README.md +44 -40
  2. package/dist/document/master-write.cjs +1 -1
  3. package/dist/document/master-write.js +1 -1
  4. package/dist/document/notes-write.cjs +1 -1
  5. package/dist/document/notes-write.js +1 -1
  6. package/dist/drawing/properties.cjs +8 -0
  7. package/dist/drawing/properties.d.cts +5 -1
  8. package/dist/drawing/properties.d.ts +5 -1
  9. package/dist/drawing/properties.js +5 -1
  10. package/dist/drawing/shapes-write.cjs +37 -7
  11. package/dist/drawing/shapes-write.d.cts +1 -0
  12. package/dist/drawing/shapes-write.d.ts +1 -0
  13. package/dist/drawing/shapes-write.js +38 -8
  14. package/dist/index.cjs +10 -2
  15. package/dist/index.d.cts +6 -6
  16. package/dist/index.d.ts +6 -6
  17. package/dist/index.js +6 -6
  18. package/dist/ole/embedded-write.cjs +66 -0
  19. package/dist/ole/embedded-write.d.cts +12 -0
  20. package/dist/ole/embedded-write.d.ts +12 -0
  21. package/dist/ole/embedded-write.js +63 -0
  22. package/dist/ole/embedded.cjs +66 -0
  23. package/dist/ole/embedded.d.cts +12 -0
  24. package/dist/ole/embedded.d.ts +12 -0
  25. package/dist/ole/embedded.js +63 -0
  26. package/dist/read.cjs +52 -11
  27. package/dist/read.d.cts +12 -5
  28. package/dist/read.d.ts +12 -5
  29. package/dist/read.js +53 -12
  30. package/dist/stream/current-user.cjs +1 -0
  31. package/dist/stream/current-user.d.cts +2 -1
  32. package/dist/stream/current-user.d.ts +2 -1
  33. package/dist/stream/current-user.js +1 -1
  34. package/dist/units.cjs +10 -0
  35. package/dist/units.d.cts +4 -1
  36. package/dist/units.d.ts +4 -1
  37. package/dist/units.js +8 -1
  38. package/dist/write.cjs +41 -5
  39. package/dist/write.d.cts +6 -2
  40. package/dist/write.d.ts +6 -2
  41. package/dist/write.js +41 -5
  42. package/package.json +5 -5
package/README.md CHANGED
@@ -111,38 +111,41 @@ import { writePpt, writePptContent } from "ppt-codec";
111
111
  // The tree form: a document-schema.js DocumentTree in, real .ppt bytes out.
112
112
  const pptBytes = writePpt(tree);
113
113
 
114
- // The flat form: metadata plus ContentSlide[] in -- title/author/dates are
115
- // written to a real "\x05SummaryInformation" stream when metadata carries
116
- // any of them (see Metadata). An optional sink hears every block this
117
- // writer had to drop (an OLE embed, an unblippable image format, a second
118
- // table on one shape) instead of losing that information silently.
114
+ // The flat form: metadata plus ContentSlide[] in -- title/author/dates are written to a real "\x05SummaryInformation" stream when metadata carries any of them (see Metadata). An optional sink hears every block this writer had to drop (an unblippable image format, a second table on one shape, or an embeddedObject block with no serialiseEmbeddedObject port to turn its nested document into real bytes) instead of losing that information silently.
119
115
  const bytes = writePptContent(
120
116
  { metadata: {}, slides },
121
117
  { sink: (diagnostic) => console.warn(diagnostic.code, diagnostic.message) },
122
118
  );
123
119
  ```
124
120
 
125
- `writePptStreams(document)` is the same write one level down, returning the two [MS-PPT] streams without wrapping them in a compound file — the mirror of `readPptStreams`, for a caller assembling its own container. Every function throws `PptUnsupportedContentError` (not `PptFormatError`, which is reserved for malformed bytes on the read side) when asked to write content outside this writer's scope: a document that is not a presentation, or slides that do not all share one size (`[MS-PPT]`'s `DocumentAtom` states exactly one slide size for the whole presentation). A block kind this writer does not represent (an embedded object, a construct marker, an image in a format with no blip token) is not an error — it is dropped from the written text body with a diagnostic naming it (see [`src/diagnostics.ts`](src/diagnostics.ts)), the same documented-gap convention [What it does not read yet](#what-it-does-not-read-yet) already uses for the reader's own unsupported constructs.
121
+ `writePptStreams(document)` is the same write one level down, returning the two [MS-PPT] streams without wrapping them in a compound file — the mirror of `readPptStreams`, for a caller assembling its own container. Every function throws `PptUnsupportedContentError` (not `PptFormatError`, which is reserved for malformed bytes on the read side) when asked to write content outside this writer's scope: a document that is not a presentation, or slides that do not all share one size (`[MS-PPT]`'s `DocumentAtom` states exactly one slide size for the whole presentation). A block kind this writer does not represent (a construct marker, an image in a format with no blip token, or an embeddedObject block whose nested document `serialiseEmbeddedObject` declines or was never supplied for) is, by default, not an error — it is dropped from the written text body with a diagnostic naming it (see [`src/diagnostics.ts`](src/diagnostics.ts)), the same documented-gap convention [What it does not read yet](#what-it-does-not-read-yet) already uses for the reader's own unsupported constructs. `WritePptOptions.onUnwritableBlock: 'throw'` turns exactly that drop into a thrown `PptUnsupportedContentError` instead — see [Silent drop, or a thrown error](#silent-drop-or-a-thrown-error) for why `'drop'` stays the default rather than converging on doc-codec's own throw-always convention.
122
+
123
+ ### Silent drop, or a thrown error
124
+
125
+ `doc-codec` throws for a block kind its own writer cannot express; this package's writer, by default, drops the block and names it through the diagnostic sink instead — a real, deliberate divergence between two codecs in the same family, not an oversight ([ExaDev/documents.js#1188](https://github.com/ExaDev/documents.js/issues/1188), split into its own decision as [#1220](https://github.com/ExaDev/documents.js/issues/1220)). The two packages' own upstream differs in kind, not degree: `documents.js`'s PDF-to-ppt and odp-to-ppt reconstruction is this package's primary caller today, and it routinely hands this writer content the binary PPT format simply has no spelling for at all (an unrecognised alignment value, a construct marker, an OLE object with no serialiser port supplied) — not as a rare malformed-input edge case a bug would explain, but as the ordinary shape of reconstructing a narrower target format from a richer source. Converging on `doc-codec`'s own default would turn "this slide's chart degrades to geometry" into "the whole presentation fails to convert" for every one of those callers, a severe regression imposed on working code rather than a bug fixed in it.
126
+
127
+ `WritePptOptions.onUnwritableBlock` is the caller's own choice between the two policies rather than a permanent split: `'drop'` (the default, and every existing caller's own unchanged behaviour) keeps writing the rest of the shape and names the drop through the sink; `'throw'` raises a `PptUnsupportedContentError` naming the identical block and reason the sink would otherwise merely report, for a caller that would rather fail the whole conversion than ship a file quietly missing content it was asked to carry. The option covers a genuine block-level omission only — `PptDiagnosticCodes.TABLE_SPAN_DROPPED` (a merged cell's `colSpan`/`rowSpan` narrowed to one column/row, since this format's own tables carry no merge record at all) is a lossy narrowing of content that is still written, not an omission of it, and always stays sink-only regardless of this option; conflating the two would make `'throw'` fail a conversion over a table that wrote completely, just with a merge visually flattened.
126
128
 
127
129
  ## What it reads
128
130
 
129
131
  The whole path from a file's first byte to a slide's text, record by record:
130
132
 
131
- | Layer | Records |
132
- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
133
- | Container | The `Current User` and `PowerPoint Document` streams, read through `archive-codec`'s bounded [MS-CFB] reader. |
134
- | Record framing | The generic 8-byte `RecordHeader`, the container/atom distinction, sibling sequences, child walks, and typed-descendant search — shared with [MS-ODRAW]'s records, which carry the identical header. |
135
- | Edit resolution | `CurrentUserAtom` (including its encrypted/plaintext `headerToken`), the `UserEditAtom` chain, `PersistDirectoryAtom`/`PersistDirectoryEntry`'s packed 20-bit/12-bit run form, and the oldest-first directory construction whose later entries supersede earlier ones — [MS-PPT] 2.1.2's own "live record" process, Part 1. |
136
- | Encryption | `DocumentEncryptionAtom` (`UserEditAtom.encryptSessionPersistIdRef` → the persist directory), [MS-OFFCRYPTO] 2.3.5's RC4 CryptoAPI scheme — see [Encryption](#encryption). |
137
- | Document | `DocumentContainer` → `DocumentAtom` (slide size, in master units), `DocumentTextInfoContainer`'s `FontCollectionContainer`/`FontEntityAtom` typeface names and its own document-default `TextMasterStyleAtom`, and `SlideListWithTextContainer` (distinguished from the master and notes lists by `recInstance`, which does not run in the order the names suggest). |
138
- | Masters | `MasterListWithTextContainer` → `MasterPersistAtom` → the persist directory → each `MainMasterContainer`'s own `TextMasterStyleAtom` items and `SlideSchemeColorSchemeAtom` — a real file can carry more than one master, and `SlideAtom.masterIdRef` decides which one a given slide actually follows. |
139
- | Slides | `SlidePersistAtom` → the persist directory → each `SlideContainer`'s own `SlideAtom` (`masterIdRef`, `notesIdRef`) and drawing, and the placeholder texts the slide list carries for it. |
140
- | Speaker notes | `NotesListWithTextContainer` (the third of the three containers sharing `RT_SlideListWithText`) → `NotesPersistAtom` → the persist directory → each `NotesContainer`, and the `NotesAtom.slideIdRef` naming the presentation slide those notes belong to. The text comes from the notes slide's own drawing, since the notes list — unlike the slide list — carries no texts for an `OutlineTextRefAtom` to reach into. |
141
- | Drawing | `DrawingContainer` → `OfficeArtDgContainer` → the `OfficeArtSpgrContainer`/`OfficeArtSpContainer` tree, `OfficeArtFSP`'s group/patriarch/deleted flags, `OfficeArtClientAnchor` in both its 8-byte `SmallRectStruct` and 16-byte `RectStruct` spellings, `OfficeArtChildAnchor` mapped through nested `OfficeArtFSPGR` group coordinate systems, and a shape's own `OfficeArtFOPT` rotation property (`PROPERTY_ROTATION`, an [MS-OSHARED] 2.2.1.6 Fixed Point). |
142
- | Pictures | The document-wide `OfficeArtBStoreContainer` (`OfficeArtFBSE` entries, each an inline or `Pictures`-stream-offset blip), resolved through a picture shape's own `pib` property; only the two `MSOBLIPTYPE` tokens `document-schema.js`'s `ContentImageBlock` can hold losslessly (`0x05` JPEG, `0x06` PNG) decode to an image block, sized to the shape's own frame. An unresolvable pib (past the store's end, an empty slot, or a WMF/EMF/TIFF/DIB blip this package does not decode) keeps the shape with empty content rather than dropping the shape. |
143
- | Tables | A table group's own grid, recovered from its cells' rectangles rather than from any row/column record — the format states none: row and column boundaries are the cells' own distinct tops and lefts, a cell lands at the intersection of its own top and left, and a real producer's degenerate zero-width/zero-height gridline shapes are excluded from the grid by that same geometry check rather than treated as cells. |
144
- | Text | `OfficeArtClientTextbox`, `TextHeaderAtom`, `TextCharsAtom` (UTF-16) and `TextBytesAtom` (one byte per character), `OutlineTextRefAtom` indirection into the slide list, and the paragraph split on the stored `\r`. |
145
- | Formatting | `StyleTextPropAtom`: `TextPFRun`/`TextPFException` (indent level, alignment, line spacing, space before/after, left margin, and first-line indent) and `TextCFRun`/`TextCFException` (bold, italic, underline, shadow, emboss, typeface reference, size in points, and a `ColorIndexStruct` colour literal sRGB or a colour-scheme slot reference), each read in the spec's **declared field order** rather than its mask-bit order — the two differ, and following the mask-bit order desynchronises every field after the first divergence. A field a run states neither directly nor at all resolves against the applicable master's own cascade and colour scheme — see [Master and colour inheritance](#master-and-colour-inheritance). |
133
+ | Layer | Records |
134
+ | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
135
+ | Container | The `Current User` and `PowerPoint Document` streams, read through `archive-codec`'s bounded [MS-CFB] reader. |
136
+ | Record framing | The generic 8-byte `RecordHeader`, the container/atom distinction, sibling sequences, child walks, and typed-descendant search — shared with [MS-ODRAW]'s records, which carry the identical header. |
137
+ | Edit resolution | `CurrentUserAtom` (including its encrypted/plaintext `headerToken`), the `UserEditAtom` chain, `PersistDirectoryAtom`/`PersistDirectoryEntry`'s packed 20-bit/12-bit run form, and the oldest-first directory construction whose later entries supersede earlier ones — [MS-PPT] 2.1.2's own "live record" process, Part 1. |
138
+ | Encryption | `DocumentEncryptionAtom` (`UserEditAtom.encryptSessionPersistIdRef` → the persist directory), [MS-OFFCRYPTO] 2.3.5's RC4 CryptoAPI scheme — see [Encryption](#encryption). |
139
+ | Document | `DocumentContainer` → `DocumentAtom` (slide size, in master units), `DocumentTextInfoContainer`'s `FontCollectionContainer`/`FontEntityAtom` typeface names and its own document-default `TextMasterStyleAtom`, and `SlideListWithTextContainer` (distinguished from the master and notes lists by `recInstance`, which does not run in the order the names suggest). |
140
+ | Masters | `MasterListWithTextContainer` → `MasterPersistAtom` → the persist directory → each `MainMasterContainer`'s own `TextMasterStyleAtom` items and `SlideSchemeColorSchemeAtom` — a real file can carry more than one master, and `SlideAtom.masterIdRef` decides which one a given slide actually follows. |
141
+ | Slides | `SlidePersistAtom` → the persist directory → each `SlideContainer`'s own `SlideAtom` (`masterIdRef`, `notesIdRef`) and drawing, and the placeholder texts the slide list carries for it. |
142
+ | Speaker notes | `NotesListWithTextContainer` (the third of the three containers sharing `RT_SlideListWithText`) → `NotesPersistAtom` → the persist directory → each `NotesContainer`, and the `NotesAtom.slideIdRef` naming the presentation slide those notes belong to. The text comes from the notes slide's own drawing, since the notes list — unlike the slide list — carries no texts for an `OutlineTextRefAtom` to reach into. |
143
+ | Drawing | `DrawingContainer` → `OfficeArtDgContainer` → the `OfficeArtSpgrContainer`/`OfficeArtSpContainer` tree, `OfficeArtFSP`'s group/patriarch/deleted flags, `OfficeArtClientAnchor` in both its 8-byte `SmallRectStruct` and 16-byte `RectStruct` spellings, `OfficeArtChildAnchor` mapped through nested `OfficeArtFSPGR` group coordinate systems, a shape's own `OfficeArtFOPT` rotation property (`PROPERTY_ROTATION`, an [MS-OSHARED] 2.2.1.6 Fixed Point), and its four text-inset properties (`dxTextLeft`/`dyTextTop`/`dxTextRight`/`dyTextBottom`, each an EMU value read independently — a shape stating only one override still gets PowerPoint's own default on the other three). |
144
+ | Pictures | The document-wide `OfficeArtBStoreContainer` (`OfficeArtFBSE` entries, each an inline or `Pictures`-stream-offset blip), resolved through a picture shape's own `pib` property; only the two `MSOBLIPTYPE` tokens `document-schema.js`'s `ContentImageBlock` can hold losslessly (`0x05` JPEG, `0x06` PNG) decode to an image block, sized to the shape's own frame. An unresolvable pib (past the store's end, an empty slot, or a WMF/EMF/TIFF/DIB blip this package does not decode) keeps the shape with empty content rather than dropping the shape. |
145
+ | Tables | A table group's own grid, recovered from its cells' rectangles rather than from any row/column record — the format states none: row and column boundaries are the cells' own distinct tops and lefts, a cell lands at the intersection of its own top and left, and a real producer's degenerate zero-width/zero-height gridline shapes are excluded from the grid by that same geometry check rather than treated as cells. |
146
+ | OLE embeddings | A shape's `OfficeArtClientData` `ExObjRefAtom` → the document's `ExObjListContainer` → `ExOleEmbedContainer` (`ExOleObjAtom`'s own `persistIdRef`, and an optional `ProgIDAtom`) the persist directory → an `ExOleObjStg` persist object, decompressed (zlib/[RFC1950]) when `rh.recInstance` says so — recovering the embedded object's own raw [MS-CFB] compound-file bytes with no further "Package"-stream wrapper. This package cannot decode those bytes into a real nested document itself (see [What it does not read yet](#what-it-does-not-read-yet)), so the recovery stops at the bytes/progId pair unless a caller injects `ReadPptOptions.decodeEmbeddedObject` — `documents.js` wires one from `doc-codec`/`xls-codec`/its own `ppt-codec` adapter. |
147
+ | Text | `OfficeArtClientTextbox`, `TextHeaderAtom`, `TextCharsAtom` (UTF-16) and `TextBytesAtom` (one byte per character), `OutlineTextRefAtom` indirection into the slide list, and the paragraph split on the stored `\r`. |
148
+ | Formatting | `StyleTextPropAtom`: `TextPFRun`/`TextPFException` (indent level, alignment, line spacing, space before/after, left margin, and first-line indent) and `TextCFRun`/`TextCFException` (bold, italic, underline, shadow, emboss, typeface reference, size in points, and a `ColorIndexStruct` colour — literal sRGB or a colour-scheme slot reference), each read in the spec's **declared field order** rather than its mask-bit order — the two differ, and following the mask-bit order desynchronises every field after the first divergence. A field a run states neither directly nor at all resolves against the applicable master's own cascade and colour scheme — see [Master and colour inheritance](#master-and-colour-inheritance). |
146
149
 
147
150
  Geometry is converted from master units (1/576 inch) to points on the way out, so a slide's `size` and every shape's `frame` are in the same unit the shared schema uses everywhere else.
148
151
 
@@ -151,8 +154,8 @@ Geometry is converted from master units (1/576 inch) to points on the way out, s
151
154
  Each of these is a real construct of the format that this package currently ignores or cannot represent — not a claim that it does not exist:
152
155
 
153
156
  - **`DocumentSummaryInformation`'s extended and user-defined properties** (company, manager, custom properties) — a genuinely different stream from the one [Metadata](#metadata) covers, not attempted at all.
154
- - **Per-shape text insets.** Every shape reports PowerPoint's own defaults (0.1 inch left and right, 0.05 inch top and bottom); a per-shape override lives in the shape's `OfficeArtFOPT` property table, which is not read.
155
- - **OLE embeddings.** An embedded or linked OLE object still reads as a shape with geometry and no blocks `ExObjListContainer` and the `ExOleObjStg` persist objects are not walked. Pictures and tables are read (see [Pictures](#what-it-reads) and [Tables](#what-it-reads) in the table above); only OLE remains geometry-only.
157
+ - **A linked (as opposed to embedded) OLE object.** `ExOleLinkContainer` names an external file this package has no path to resolve independently of the host document, so a linked object's shape reads with geometry and no blocks — the identical "no recovery path, no entry" degrade an unresolvable persist reference already gets. An embedded OLE object's own linkage and storage recovery is real — see [OLE embeddings](#what-it-reads) in the table above.
158
+ - **Decoding an OLE-embedded object's own nested content, without a caller-supplied port.** This package depends on no sibling format codec (`doc-codec`/`xls-codec`/itself, or `ooxml.js`), so it cannot turn an embedded object's recovered `[MS-CFB]` bytes into a real nested `ContentDocument` on its own the same architectural boundary `ooxml.js`'s own embedded-object recovery states for a classic-binary `.bin` payload it finds no `Package` stream in. `readPptContent`/`readPpt`'s `ReadPptOptions.decodeEmbeddedObject` is the injected port a caller holding every codec — `documents.js` — supplies; without one, an OLE-embedded shape's blocks stay whatever its own picture/text already give it (the same degrade a decode failure, an unrecognised progId, or a missing persist entry all produce), never a document this package invented or guessed at.
156
159
  - **Image formats beyond PNG and JPEG.** A blip in any other `MSOBLIPTYPE` (WMF/EMF metafile, PICT, a raw DIB, TIFF) reads as no image at all, keeping the shape's geometry with empty content — the same convention an unresolvable pib already uses.
157
160
  - **Cell merges in a read table.** [MS-PPT]'s own table shapes are a strict grid of one shape per cell with no merge record at all (merged cells arrived only with the 2010 XML format), so a read table never states `colSpan`/`rowSpan`.
158
161
  - **Shapes with no anchor.** A shape carrying neither an `OfficeArtClientAnchor` nor an `OfficeArtChildAnchor` is dropped, because `ContentShape` has no way to say "positioned, but unknown where".
@@ -166,20 +169,21 @@ Each of these is a real construct of the format that this package currently igno
166
169
 
167
170
  The whole path from a `ContentSlide[]` to a real `.ppt` file's bytes, mirroring the read-side table above in the opposite direction:
168
171
 
169
- | Layer | Records |
170
- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
171
- | Container | The `Current User` and `PowerPoint Document` streams, wrapped in a real [MS-CFB] compound file through `archive-codec`'s conformant writer. |
172
- | Record framing | The generic 8-byte `RecordHeader`, atom and container builders — `record/write.ts`, shared by every writer module below and by this package's own test fixtures. |
173
- | Edit resolution | A single-edit persist layer: one `CurrentUserAtom` pointing at one `UserEditAtom` pointing at one `PersistDirectoryAtom` whose entries name the stream offset of the document container, the master, every slide container and every notes container — never an incremental append, since nothing about this writer's own output needs a second generation of any object. |
174
- | Document | `DocumentContainer` → `DocumentAtom` (one slide size, in master units, taken from the input's own first slide and required to match every other slide — see below), an `Environment`/`FontCollectionContainer` built from every distinct `fontFamily` a run names, a `MasterListWithTextContainer` naming the one master, a `SlideListWithTextContainer` carrying one `SlidePersistAtom` per slide with no placeholder texts, and — only when some slide has notes — a `NotesListWithTextContainer`. |
175
- | Master | One minimal `MainMasterContainer`: its own `SlideAtom`, the title/body/notes `TextMasterStyleAtom` items [MS-PPT] 2.5.3 requires (each stating `cLevels` 0, so every level falls through to the document's own text styles), the five `SL_TitleBody` placeholder shapes a main master must carry, and a default `SlideSchemeColorSchemeAtom`. It exists because speaker notes need it — see below. |
176
- | Slides | One `SlideContainer` per input slide, each opening with a `SlideAtom` that names the master it follows and, when the slide has notes, the notes slide holding them, then its own `DrawingContainer`. |
177
- | Speaker notes | One `NotesContainer` per slide that actually has notes — a `NotesAtom` naming that slide, then a `DrawingContainer` whose single text box carries the notes, one paragraph per line, then the `SlideSchemeColorSchemeAtom` [MS-PPT] 2.5.6 requires of one — the notes slide's own `NotesAtom.slideFlags` leaves `fMasterScheme` clear, so it inherits no scheme and has to state one. A slide with no notes gets no notes slide at all rather than an empty one. |
178
- | Drawing | `OfficeArtDgContainer` → one `OfficeArtSpgrContainer` (the patriarch group every real drawing carries) → one plain `OfficeArtSpContainer` per shape, each anchored in slide coordinates via a 32-bit `OfficeArtClientAnchor` (`RectStruct`, never the 16-bit `SmallRectStruct`), and a shape's own `rotationDeg` as an `OfficeArtFOPT` `PROPERTY_ROTATION` entry — no grouping otherwise, beyond the table group below. |
179
- | Pictures | The first `png`/`jpeg` image block on a shape becomes that shape's one blip-store reference (`blipIndexOf`, feeding the document-wide `OfficeArtBStoreContainer` every picture shares); an image in any other format, or a second image on a shape whose single blip reference an earlier one already claimed, is dropped with a diagnostic (`ppt/image-dropped`) rather than silently discarded. |
180
- | Tables | A table block turns its whole shape into a table group in the spelling a real PowerPoint-authored file carries (confirmed against Microsoft Office PowerPoint's own output and Apache POI's `table_test.ppt` fixture): an `OfficeArtFSPGR` child coordinate system identical to the shape's own client anchor, `fIsTable`/`tableRowProperties` in the tertiary property table, and one plain text-box shape per cell at its own `OfficeArtChildAnchor` grid position — the same grid `tableBlockFor` reads back. A cell's `colSpan`/`rowSpan` is dropped with a diagnostic (`ppt/table-span-dropped`), since the format has no merge record to state it in. |
181
- | Text | Every shape carries its own text directly on its `OfficeArtClientTextbox` (`TextHeaderAtom` + a UTF-16 `TextCharsAtom`) rather than through the `OutlineTextRefAtom` placeholder indirection into the slide list — a plain text box is all this writer produces, so there is no separate placeholder text to route through the document's own slide list. |
182
- | Formatting | `StyleTextPropAtom`: one `TextPFRun` per paragraph (indent level, alignment, line spacing, space before/after, left margin, and first-line indent) and one `TextCFRun` per character run (bold, italic, underline, a font-collection reference, size in points, and a literal sRGB `ColorIndexStruct` colour), fields written in the identical spec-declared order `readTextPFException`/`readTextCFException` parse them in. |
172
+ | Layer | Records |
173
+ | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
174
+ | Container | The `Current User` and `PowerPoint Document` streams, wrapped in a real [MS-CFB] compound file through `archive-codec`'s conformant writer. |
175
+ | Record framing | The generic 8-byte `RecordHeader`, atom and container builders — `record/write.ts`, shared by every writer module below and by this package's own test fixtures. |
176
+ | Edit resolution | A single-edit persist layer: one `CurrentUserAtom` pointing at one `UserEditAtom` pointing at one `PersistDirectoryAtom` whose entries name the stream offset of the document container, the master, every slide container and every notes container — never an incremental append, since nothing about this writer's own output needs a second generation of any object. |
177
+ | Document | `DocumentContainer` → `DocumentAtom` (one slide size, in master units, taken from the input's own first slide and required to match every other slide — see below), an `Environment`/`FontCollectionContainer` built from every distinct `fontFamily` a run names, a `MasterListWithTextContainer` naming the one master, a `SlideListWithTextContainer` carrying one `SlidePersistAtom` per slide with no placeholder texts, and — only when some slide has notes — a `NotesListWithTextContainer`. |
178
+ | Master | One minimal `MainMasterContainer`: its own `SlideAtom`, the title/body/notes `TextMasterStyleAtom` items [MS-PPT] 2.5.3 requires (each stating `cLevels` 0, so every level falls through to the document's own text styles), the five `SL_TitleBody` placeholder shapes a main master must carry, and a default `SlideSchemeColorSchemeAtom`. It exists because speaker notes need it — see below. |
179
+ | Slides | One `SlideContainer` per input slide, each opening with a `SlideAtom` that names the master it follows and, when the slide has notes, the notes slide holding them, then its own `DrawingContainer`. |
180
+ | Speaker notes | One `NotesContainer` per slide that actually has notes — a `NotesAtom` naming that slide, then a `DrawingContainer` whose single text box carries the notes, one paragraph per line, then the `SlideSchemeColorSchemeAtom` [MS-PPT] 2.5.6 requires of one — the notes slide's own `NotesAtom.slideFlags` leaves `fMasterScheme` clear, so it inherits no scheme and has to state one. A slide with no notes gets no notes slide at all rather than an empty one. |
181
+ | Drawing | `OfficeArtDgContainer` → one `OfficeArtSpgrContainer` (the patriarch group every real drawing carries) → one plain `OfficeArtSpContainer` per shape, each anchored in slide coordinates via a 32-bit `OfficeArtClientAnchor` (`RectStruct`, never the 16-bit `SmallRectStruct`), a shape's own `rotationDeg` as an `OfficeArtFOPT` `PROPERTY_ROTATION` entry, and whichever of its four text insets differ from the default its own picture-ness implies, each as its own `dxTextLeft`/`dyTextTop`/`dxTextRight`/`dyTextBottom` property — no grouping otherwise, beyond the table group below. |
182
+ | Pictures | The first `png`/`jpeg` image block on a shape becomes that shape's one blip-store reference (`blipIndexOf`, feeding the document-wide `OfficeArtBStoreContainer` every picture shares); an image in any other format, or a second image on a shape whose single blip reference an earlier one already claimed, is dropped with a diagnostic (`ppt/image-dropped`) rather than silently discarded. |
183
+ | Tables | A table block turns its whole shape into a table group in the spelling a real PowerPoint-authored file carries (confirmed against Microsoft Office PowerPoint's own output and Apache POI's `table_test.ppt` fixture): an `OfficeArtFSPGR` child coordinate system identical to the shape's own client anchor, `fIsTable`/`tableRowProperties` in the tertiary property table, and one plain text-box shape per cell at its own `OfficeArtChildAnchor` grid position — the same grid `tableBlockFor` reads back. A cell's `colSpan`/`rowSpan` is dropped with a diagnostic (`ppt/table-span-dropped`), since the format has no merge record to state it in. |
184
+ | OLE embeddings | A shape whose blocks carry an `embeddedObject` block gets a real `ExObjRefAtom` in its own `OfficeArtClientData`, naming a fresh entry in the document's own `ExObjListContainer` (`ExOleEmbedContainer`: `ExOleEmbedAtom`, `ExOleObjAtom`, and — for a `wordprocessing`/`spreadsheet` object kind, the two this package can name a real legacy-Office ProgID for — a `ProgIDAtom`) whose `persistIdRef` names a fresh `ExOleObjStgUncompressedAtom` persist object, only when `WritePptOptions.serialiseEmbeddedObject` (this package has no sibling-codec writer of its own to serialise the nested document with the write-side mirror of the read-side decode port above) actually recovers `[MS-CFB]` bytes for that object's own nested document; a shape whose embed the port declines (no port supplied, or a document kind — `formula`/`drawing` — it cannot serialise) writes with no `clientData` at all, identical to a shape that never carried an embeddedObject block in the first place the same silent-drop policy every other unwritable block already gets (see [What it does not write yet](#what-it-does-not-write-yet)). |
185
+ | Text | Every shape carries its own text directly on its `OfficeArtClientTextbox` (`TextHeaderAtom` + a UTF-16 `TextCharsAtom`) rather than through the `OutlineTextRefAtom` placeholder indirection into the slide list a plain text box is all this writer produces, so there is no separate placeholder text to route through the document's own slide list. |
186
+ | Formatting | `StyleTextPropAtom`: one `TextPFRun` per paragraph (indent level, alignment, line spacing, space before/after, left margin, and first-line indent) and one `TextCFRun` per character run (bold, italic, underline, a font-collection reference, size in points, and a literal sRGB `ColorIndexStruct` colour), fields written in the identical spec-declared order `readTextPFException`/`readTextCFException` parse them in. |
183
187
 
184
188
  Geometry is converted from points to master units on the way in, rounding to the nearest whole master unit (1/576 inch) — the format's own smallest unit of length.
185
189
 
@@ -205,10 +209,10 @@ The round trip above proves the reader and writer agree with each other. Speaker
205
209
 
206
210
  Each of these is either a real construct this writer deliberately does not attempt (a smaller, genuinely correct core rather than a larger, unreliable one — see the two tables above for exactly what it does write), or a `ContentShape`/`ContentParagraph`/`ContentRun` field this writer's own OfficeArt shape tree has nowhere to carry:
207
211
 
208
- - **OLE embeddings.** A shape whose blocks include an `embeddedObject` block drops it from the written text body, with a `ppt/block-dropped` diagnostic naming it see [Writing a document](#writing-a-document) rather than attempting an OLE object shape. Pictures and tables are written (see [Pictures](#what-it-writes) and [Tables](#what-it-writes) in the table above).
212
+ - **An OLE embedding's own nested content, without a caller-supplied serialiser.** This package cannot itself turn a nested `ContentDocument` back into `[MS-CFB]` bytes (it depends on no sibling format codec — the write-side mirror of the read-side decode gap above), so `WritePptOptions.serialiseEmbeddedObject` is the injected port a caller holding every codec supplies; without one, a shape's `embeddedObject` block writes with no `clientData` and no `ExOleObjStg` persist object, silently, matching every other block kind this writer cannot express (see [What it writes](#what-it-writes) for what a supplied port produces).
209
213
  - **Shapes with no text.** Written with a client anchor and no `OfficeArtClientTextbox` at all, matching how the reader represents one (`blocks: []`); nothing is lost, since there was nothing to write.
210
214
  - **Grouped shapes beyond a table, and any coordinate system beyond a plain `OfficeArtClientAnchor` or the table group's own `OfficeArtFSPGR`.** Every non-table shape this writer emits is an ungrouped rectangle in slide coordinates; there is no general `OfficeArtChildAnchor`/`OfficeArtFSPGR` group nesting outside the one a table block itself produces. `ContentShape.rotationDeg` is written (see [Drawing](#what-it-writes) in the table above).
211
- - **Per-shape text insets, autofit, and paint order.** `ContentShape.insetLeftPt`/`insetTopPt`/`insetRightPt`/`insetBottomPt`, `fontScale`, `lineSpacingReduction`, and `paintOrder` have no `OfficeArtFOPT` property table to land in, since this writer does not build one.
215
+ - **Autofit and paint order.** `ContentShape.fontScale`, `lineSpacingReduction`, and `paintOrder` have no `OfficeArtFOPT` property table entry this writer states (per-shape text insets are written — see [Drawing](#what-it-writes) in the table above).
212
216
  - **Master content, layouts, and scheme colours.** A `MainMasterContainer` and its `MasterListWithTextContainer` are written, but only as the minimum [MS-PPT] requires of one (see [Why a writer of plain text-box slides writes a master slide](#why-a-writer-of-plain-text-box-slides-writes-a-master-slide)): its five placeholder shapes carry no text, its `TextMasterStyleAtom` items state no style level of their own, and its `SlideSchemeColorSchemeAtom` is a fixed default rather than anything the input chose. There are still no slide layouts, and every character run's colour must already be a literal, since no scheme is there to resolve one against.
213
217
  - **Notes masters, and a notes page geometry of its own.** No `NotesContainer` is written for the notes master, and `DocumentAtom.notesMasterPersistIdRef` stays 0, so each notes slide inherits nothing (its `NotesAtom.slideFlags` is clear) and states the same fixed default `SlideSchemeColorSchemeAtom` the master does rather than a scheme of the input's choosing. The notes page is the same size as the slide, because `ContentSlide` carries no notes-page geometry to state a different one from, and the notes text box is placed in the lower half of it.
214
218
  - **Hyperlinks, bullets, and list numbering identity.** `ContentRun.hyperlink`, `ContentParagraph.list.numId`/`checked`/`itemId`, and `pageBreakBefore`/`pageBreakAfter` have no [MS-PPT] field this writer populates; `alignment`, `list.level` (as a `TextPFException` indent level), `spacingBeforePt`/`spacingAfterPt`/`lineSpacing`/`indentLeftPt`/`indentFirstLinePt` round-trip.
@@ -293,7 +297,7 @@ The [MS-CFB] container beneath the format is the one piece not hand-written agai
293
297
 
294
298
  - Worker-isomorphic (see the [family-wide convention](../../README.md#conventions)): runtime `src/` must not import `node:*`, a bare Node builtin, or use the `Buffer` global — enforced by a `no-restricted-imports`/`no-restricted-globals` ESLint rule and exercised in CI by running a suite inside an actual `workerd` isolate (`pnpm test:workers`). Test files under `src/**/*.test.ts` and `src/test-support/` are exempt and may use Node APIs for fixtures.
295
299
  - Only `src/index.ts` may be named `index.*` — a custom ESLint rule (`local/no-non-barrel-index`) rejects any other module using an `index` basename, since that would be a hidden entry point the `exports` map in `package.json` doesn't advertise.
296
- - Every structural failure throws `PptFormatError` rather than degrading: a malformed file fails whole, never returning a partial slide list that looks complete. On the write side, content this writer cannot express throws `PptUnsupportedContentError` rather than silently substituting or dropping it except a block kind outside this writer's scope (an image, a table, a construct marker), which is excluded from the written text body by design and documented as such, the same convention the reader already applies to its own unsupported constructs.
300
+ - Every structural failure throws `PptFormatError` rather than degrading: a malformed file fails whole, never returning a partial slide list that looks complete. On the write side, content genuinely outside this writer's own scope (a document that is not a presentation, slides that disagree on size) throws `PptUnsupportedContentError`. A block kind this writer cannot represent at all (an image, a table, a construct marker) is different: by default it is excluded from the written text body with a diagnostic naming it, never approximated, and `WritePptOptions.onUnwritableBlock: 'throw'` lets a caller opt into raising `PptUnsupportedContentError` for that case too — see [Silent drop, or a thrown error](#silent-drop-or-a-thrown-error) for why the default stays a diagnosed drop rather than a throw.
297
301
 
298
302
  ## Specification references
299
303
 
@@ -3,8 +3,8 @@ const require_record_types = require("../record/types.cjs");
3
3
  require("../text/atoms.cjs");
4
4
  const require_record_write = require("../record/write.cjs");
5
5
  const require_document_color_scheme_write = require("./color-scheme-write.cjs");
6
- const require_drawing_shapes_write = require("../drawing/shapes-write.cjs");
7
6
  const require_read = require("../read.cjs");
7
+ const require_drawing_shapes_write = require("../drawing/shapes-write.cjs");
8
8
  //#region src/document/master-write.ts
9
9
  const SL_TITLE_BODY = 1;
10
10
  const SL_BLANK = 16;
@@ -2,8 +2,8 @@ import { OfficeArtClientData, RT_MainMaster, RT_PlaceholderAtom, RT_SlideAtom, R
2
2
  import "../text/atoms.js";
3
3
  import { concatBytes, i32le, u16le, u32le, u8, writeAtom, writeContainer } from "../record/write.js";
4
4
  import { writeSlideSchemeColorSchemeAtom } from "./color-scheme-write.js";
5
- import { writeSlideDrawing } from "../drawing/shapes-write.js";
6
5
  import { DEFAULT_INSET_LEFT_RIGHT_PT, DEFAULT_INSET_TOP_BOTTOM_PT } from "../read.js";
6
+ import { writeSlideDrawing } from "../drawing/shapes-write.js";
7
7
  //#region src/document/master-write.ts
8
8
  const SL_TITLE_BODY = 1;
9
9
  const SL_BLANK = 16;
@@ -2,8 +2,8 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_record_types = require("../record/types.cjs");
3
3
  const require_record_write = require("../record/write.cjs");
4
4
  const require_document_color_scheme_write = require("./color-scheme-write.cjs");
5
- const require_drawing_shapes_write = require("../drawing/shapes-write.cjs");
6
5
  const require_read = require("../read.cjs");
6
+ const require_drawing_shapes_write = require("../drawing/shapes-write.cjs");
7
7
  //#region src/document/notes-write.ts
8
8
  const NOTES_BODY_TOP_FRACTION = .5;
9
9
  const NOTES_BODY_MARGIN_FRACTION = .05;
@@ -1,8 +1,8 @@
1
1
  import { RT_Notes, RT_NotesAtom } from "../record/types.js";
2
2
  import { concatBytes, u16le, u32le, writeAtom, writeContainer } from "../record/write.js";
3
3
  import { writeSlideSchemeColorSchemeAtom } from "./color-scheme-write.js";
4
- import { writeSlideDrawing } from "../drawing/shapes-write.js";
5
4
  import { DEFAULT_INSET_LEFT_RIGHT_PT, DEFAULT_INSET_TOP_BOTTOM_PT } from "../read.js";
5
+ import { writeSlideDrawing } from "../drawing/shapes-write.js";
6
6
  //#region src/document/notes-write.ts
7
7
  const NOTES_BODY_TOP_FRACTION = .5;
8
8
  const NOTES_BODY_MARGIN_FRACTION = .05;
@@ -11,6 +11,10 @@ const PROPERTY_ROTATION = 4;
11
11
  const PROPERTY_PIB = 260;
12
12
  const PROPERTY_TABLE_PROPERTIES = 927;
13
13
  const PROPERTY_TABLE_ROW_PROPERTIES = 928;
14
+ const PROPERTY_DX_TEXT_LEFT = 129;
15
+ const PROPERTY_DY_TEXT_TOP = 130;
16
+ const PROPERTY_DX_TEXT_RIGHT = 131;
17
+ const PROPERTY_DY_TEXT_BOTTOM = 132;
14
18
  const FIXED_POINT_ONE = 65536;
15
19
  function degreesToFixedPoint(degrees) {
16
20
  return Math.round(degrees * FIXED_POINT_ONE);
@@ -81,6 +85,10 @@ function writeShapePropertyTable(recType, entries) {
81
85
  });
82
86
  }
83
87
  //#endregion
88
+ exports.PROPERTY_DX_TEXT_LEFT = PROPERTY_DX_TEXT_LEFT;
89
+ exports.PROPERTY_DX_TEXT_RIGHT = PROPERTY_DX_TEXT_RIGHT;
90
+ exports.PROPERTY_DY_TEXT_BOTTOM = PROPERTY_DY_TEXT_BOTTOM;
91
+ exports.PROPERTY_DY_TEXT_TOP = PROPERTY_DY_TEXT_TOP;
84
92
  exports.PROPERTY_PIB = PROPERTY_PIB;
85
93
  exports.PROPERTY_ROTATION = PROPERTY_ROTATION;
86
94
  exports.PROPERTY_TABLE_PROPERTIES = PROPERTY_TABLE_PROPERTIES;
@@ -4,6 +4,10 @@ declare const PROPERTY_ROTATION = 4;
4
4
  declare const PROPERTY_PIB = 260;
5
5
  declare const PROPERTY_TABLE_PROPERTIES = 927;
6
6
  declare const PROPERTY_TABLE_ROW_PROPERTIES = 928;
7
+ declare const PROPERTY_DX_TEXT_LEFT = 129;
8
+ declare const PROPERTY_DY_TEXT_TOP = 130;
9
+ declare const PROPERTY_DX_TEXT_RIGHT = 131;
10
+ declare const PROPERTY_DY_TEXT_BOTTOM = 132;
7
11
  declare function degreesToFixedPoint(degrees: number): number;
8
12
  declare function fixedPointToDegrees(raw: number): number;
9
13
  declare const TABLE_FLAG_IS_TABLE: number;
@@ -22,4 +26,4 @@ interface WritableShapeProperty {
22
26
  }
23
27
  declare function writeShapePropertyTable(recType: number, entries: readonly WritableShapeProperty[]): Uint8Array<ArrayBuffer>;
24
28
  //#endregion
25
- export { PROPERTY_PIB, PROPERTY_ROTATION, PROPERTY_TABLE_PROPERTIES, PROPERTY_TABLE_ROW_PROPERTIES, ShapeProperty, TABLE_FLAG_IS_TABLE, WritableShapeProperty, degreesToFixedPoint, fixedPointToDegrees, readIMsoArray, readShapeProperties, writeIMsoArray, writeShapePropertyTable };
29
+ export { PROPERTY_DX_TEXT_LEFT, PROPERTY_DX_TEXT_RIGHT, PROPERTY_DY_TEXT_BOTTOM, PROPERTY_DY_TEXT_TOP, PROPERTY_PIB, PROPERTY_ROTATION, PROPERTY_TABLE_PROPERTIES, PROPERTY_TABLE_ROW_PROPERTIES, ShapeProperty, TABLE_FLAG_IS_TABLE, WritableShapeProperty, degreesToFixedPoint, fixedPointToDegrees, readIMsoArray, readShapeProperties, writeIMsoArray, writeShapePropertyTable };
@@ -4,6 +4,10 @@ declare const PROPERTY_ROTATION = 4;
4
4
  declare const PROPERTY_PIB = 260;
5
5
  declare const PROPERTY_TABLE_PROPERTIES = 927;
6
6
  declare const PROPERTY_TABLE_ROW_PROPERTIES = 928;
7
+ declare const PROPERTY_DX_TEXT_LEFT = 129;
8
+ declare const PROPERTY_DY_TEXT_TOP = 130;
9
+ declare const PROPERTY_DX_TEXT_RIGHT = 131;
10
+ declare const PROPERTY_DY_TEXT_BOTTOM = 132;
7
11
  declare function degreesToFixedPoint(degrees: number): number;
8
12
  declare function fixedPointToDegrees(raw: number): number;
9
13
  declare const TABLE_FLAG_IS_TABLE: number;
@@ -22,4 +26,4 @@ interface WritableShapeProperty {
22
26
  }
23
27
  declare function writeShapePropertyTable(recType: number, entries: readonly WritableShapeProperty[]): Uint8Array<ArrayBuffer>;
24
28
  //#endregion
25
- export { PROPERTY_PIB, PROPERTY_ROTATION, PROPERTY_TABLE_PROPERTIES, PROPERTY_TABLE_ROW_PROPERTIES, ShapeProperty, TABLE_FLAG_IS_TABLE, WritableShapeProperty, degreesToFixedPoint, fixedPointToDegrees, readIMsoArray, readShapeProperties, writeIMsoArray, writeShapePropertyTable };
29
+ export { PROPERTY_DX_TEXT_LEFT, PROPERTY_DX_TEXT_RIGHT, PROPERTY_DY_TEXT_BOTTOM, PROPERTY_DY_TEXT_TOP, PROPERTY_PIB, PROPERTY_ROTATION, PROPERTY_TABLE_PROPERTIES, PROPERTY_TABLE_ROW_PROPERTIES, ShapeProperty, TABLE_FLAG_IS_TABLE, WritableShapeProperty, degreesToFixedPoint, fixedPointToDegrees, readIMsoArray, readShapeProperties, writeIMsoArray, writeShapePropertyTable };
@@ -10,6 +10,10 @@ const PROPERTY_ROTATION = 4;
10
10
  const PROPERTY_PIB = 260;
11
11
  const PROPERTY_TABLE_PROPERTIES = 927;
12
12
  const PROPERTY_TABLE_ROW_PROPERTIES = 928;
13
+ const PROPERTY_DX_TEXT_LEFT = 129;
14
+ const PROPERTY_DY_TEXT_TOP = 130;
15
+ const PROPERTY_DX_TEXT_RIGHT = 131;
16
+ const PROPERTY_DY_TEXT_BOTTOM = 132;
13
17
  const FIXED_POINT_ONE = 65536;
14
18
  function degreesToFixedPoint(degrees) {
15
19
  return Math.round(degrees * FIXED_POINT_ONE);
@@ -80,4 +84,4 @@ function writeShapePropertyTable(recType, entries) {
80
84
  });
81
85
  }
82
86
  //#endregion
83
- export { PROPERTY_PIB, PROPERTY_ROTATION, PROPERTY_TABLE_PROPERTIES, PROPERTY_TABLE_ROW_PROPERTIES, TABLE_FLAG_IS_TABLE, degreesToFixedPoint, fixedPointToDegrees, readIMsoArray, readShapeProperties, writeIMsoArray, writeShapePropertyTable };
87
+ export { PROPERTY_DX_TEXT_LEFT, PROPERTY_DX_TEXT_RIGHT, PROPERTY_DY_TEXT_BOTTOM, PROPERTY_DY_TEXT_TOP, PROPERTY_PIB, PROPERTY_ROTATION, PROPERTY_TABLE_PROPERTIES, PROPERTY_TABLE_ROW_PROPERTIES, TABLE_FLAG_IS_TABLE, degreesToFixedPoint, fixedPointToDegrees, readIMsoArray, readShapeProperties, writeIMsoArray, writeShapePropertyTable };
@@ -1,4 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_errors = require("../errors.cjs");
2
3
  const require_record_types = require("../record/types.cjs");
3
4
  const require_text_atoms = require("../text/atoms.cjs");
4
5
  const require_units = require("../units.cjs");
@@ -7,8 +8,31 @@ const require_diagnostics = require("../diagnostics.cjs");
7
8
  const require_record_write = require("../record/write.cjs");
8
9
  const require_drawing_blips = require("./blips.cjs");
9
10
  const require_drawing_properties = require("./properties.cjs");
11
+ const require_read = require("../read.cjs");
10
12
  const require_text_style_write = require("../text/style-write.cjs");
11
13
  //#region src/drawing/shapes-write.ts
14
+ function insetProperties(shape, isPicture) {
15
+ const defaultLeftRight = isPicture ? 0 : require_read.DEFAULT_INSET_LEFT_RIGHT_PT;
16
+ const defaultTopBottom = isPicture ? 0 : require_read.DEFAULT_INSET_TOP_BOTTOM_PT;
17
+ const entries = [];
18
+ if (shape.insetLeftPt !== defaultLeftRight) entries.push({
19
+ opid: 129,
20
+ op: require_units.pointsToEmu(shape.insetLeftPt)
21
+ });
22
+ if (shape.insetTopPt !== defaultTopBottom) entries.push({
23
+ opid: 130,
24
+ op: require_units.pointsToEmu(shape.insetTopPt)
25
+ });
26
+ if (shape.insetRightPt !== defaultLeftRight) entries.push({
27
+ opid: 131,
28
+ op: require_units.pointsToEmu(shape.insetRightPt)
29
+ });
30
+ if (shape.insetBottomPt !== defaultTopBottom) entries.push({
31
+ opid: 132,
32
+ op: require_units.pointsToEmu(shape.insetBottomPt)
33
+ });
34
+ return entries;
35
+ }
12
36
  const FSP_CHILD = 2;
13
37
  const PATRIARCH_SPID = 1;
14
38
  const FIRST_CONTENT_SPID = 2;
@@ -36,6 +60,7 @@ function writeShapeProperties(shape, pib) {
36
60
  op: pib,
37
61
  fBid: true
38
62
  });
63
+ entries.push(...insetProperties(shape, pib !== void 0));
39
64
  return entries.length === 0 ? void 0 : require_drawing_properties.writeShapePropertyTable(require_record_types.OfficeArtFOPT, entries);
40
65
  }
41
66
  function writeClientTextbox(textBlocks, fontIndexOf) {
@@ -62,25 +87,30 @@ function writeShape(spid, shape, pib, textBlocks, fontIndexOf, clientData) {
62
87
  function writePatriarch() {
63
88
  return require_record_write.writeContainer(require_record_types.OfficeArtSpContainer, [require_record_write.writeAtom(require_record_types.OfficeArtFSPGR, /* @__PURE__ */ new Uint8Array(16), { recVer: 1 }), writeFsp(PATRIARCH_SPID, 5)]);
64
89
  }
65
- function planShapeBlocks(blocks, context) {
90
+ function reportDrop(context, diagnostic) {
91
+ context.sink(diagnostic);
92
+ if (context.strict) throw new require_errors.PptUnsupportedContentError(diagnostic.message);
93
+ }
94
+ function planShapeBlocks(blocks, context, hasOleClientData) {
66
95
  const textBlocks = [];
67
96
  let pib;
68
97
  let table;
69
98
  const drop = (block, reason) => {
70
- context.sink({
99
+ reportDrop(context, {
71
100
  code: require_diagnostics.PptDiagnosticCodes.BLOCK_DROPPED,
72
101
  severity: "warning",
73
102
  message: `${context.location}: ${reason}`
74
103
  });
75
104
  };
76
105
  for (const block of blocks) {
106
+ if (block.kind === "embeddedObject" && hasOleClientData) continue;
77
107
  if (block.kind === "paragraph") {
78
108
  textBlocks.push(block);
79
109
  continue;
80
110
  }
81
111
  if (block.kind === "image") {
82
112
  if (!require_drawing_blips.isBlipFormat(block.format)) {
83
- context.sink({
113
+ reportDrop(context, {
84
114
  code: require_diagnostics.PptDiagnosticCodes.IMAGE_DROPPED,
85
115
  severity: "warning",
86
116
  message: `${context.location}: an image block in format '${block.format}' is dropped; MSOBLIPTYPE gives this writer a blip record for PNG and JPEG only`
@@ -88,7 +118,7 @@ function planShapeBlocks(blocks, context) {
88
118
  continue;
89
119
  }
90
120
  if (pib !== void 0) {
91
- context.sink({
121
+ reportDrop(context, {
92
122
  code: require_diagnostics.PptDiagnosticCodes.IMAGE_DROPPED,
93
123
  severity: "warning",
94
124
  message: `${context.location}: a second image block is dropped; a shape carries exactly one blip-store reference, and an earlier image already consumed it`
@@ -110,7 +140,7 @@ function planShapeBlocks(blocks, context) {
110
140
  }
111
141
  if (table !== void 0) {
112
142
  for (const block of textBlocks) drop(block, "a 'paragraph' block is dropped; a shape carrying a table becomes a table group, which holds its text in cells rather than a text body of its own");
113
- if (pib !== void 0) context.sink({
143
+ if (pib !== void 0) reportDrop(context, {
114
144
  code: require_diagnostics.PptDiagnosticCodes.IMAGE_DROPPED,
115
145
  severity: "warning",
116
146
  message: `${context.location}: an image block is dropped; a shape carrying a table becomes a table group, which has no blip reference of its own`
@@ -201,7 +231,7 @@ function writeTableGroup(spid, shape, table, context) {
201
231
  message: `${context.location}: a table cell's rowSpan ${String(cell.rowSpan)} is dropped; a PowerPoint 97-2003 table is a strict grid of shapes with no merge records, so the cell is written one row tall`
202
232
  });
203
233
  const paragraphs = cell.blocks.filter((block) => block.kind === "paragraph");
204
- for (const block of cell.blocks) if (block.kind !== "paragraph") context.sink({
234
+ for (const block of cell.blocks) if (block.kind !== "paragraph") reportDrop(context, {
205
235
  code: require_diagnostics.PptDiagnosticCodes.BLOCK_DROPPED,
206
236
  severity: "warning",
207
237
  message: `${context.location}: a '${block.kind}' block inside a table cell is dropped; a table cell in this format is a plain text-box shape with no property table or object reference of its own`
@@ -222,7 +252,7 @@ function writeDrawing(shapes, context) {
222
252
  let shapeCount = 1;
223
253
  const shapeContainers = [];
224
254
  for (const entry of shapes) {
225
- const plan = planShapeBlocks(entry.shape.blocks, context);
255
+ const plan = planShapeBlocks(entry.shape.blocks, context, entry.clientData !== void 0);
226
256
  if (plan.table !== void 0) {
227
257
  const group = writeTableGroup(nextSpid, entry.shape, plan.table, context);
228
258
  nextSpid += group.shapeCount;
@@ -14,6 +14,7 @@ interface DrawingWriteContext {
14
14
  readonly fontIndexOf: (family: string) => number;
15
15
  readonly blipIndexOf: (image: ContentImageBlock) => number;
16
16
  readonly sink: PptDiagnosticSink;
17
+ readonly strict: boolean;
17
18
  readonly location: string;
18
19
  }
19
20
  declare function writeSlideDrawing(shapes: readonly DrawingShape[], context: DrawingWriteContext): DrawingWritten;
@@ -14,6 +14,7 @@ interface DrawingWriteContext {
14
14
  readonly fontIndexOf: (family: string) => number;
15
15
  readonly blipIndexOf: (image: ContentImageBlock) => number;
16
16
  readonly sink: PptDiagnosticSink;
17
+ readonly strict: boolean;
17
18
  readonly location: string;
18
19
  }
19
20
  declare function writeSlideDrawing(shapes: readonly DrawingShape[], context: DrawingWriteContext): DrawingWritten;