ppt-codec 0.0.0 → 1.0.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.
- package/LICENSE +21 -0
- package/README.md +174 -0
- package/dist/content.cjs +72 -0
- package/dist/content.d.cts +6 -0
- package/dist/content.d.ts +6 -0
- package/dist/content.js +71 -0
- package/dist/document/document-atom.cjs +27 -0
- package/dist/document/document-atom.d.cts +17 -0
- package/dist/document/document-atom.d.ts +17 -0
- package/dist/document/document-atom.js +26 -0
- package/dist/document/fonts.cjs +29 -0
- package/dist/document/fonts.d.cts +5 -0
- package/dist/document/fonts.d.ts +5 -0
- package/dist/document/fonts.js +28 -0
- package/dist/document/slide-list.cjs +40 -0
- package/dist/document/slide-list.d.cts +14 -0
- package/dist/document/slide-list.d.ts +14 -0
- package/dist/document/slide-list.js +39 -0
- package/dist/drawing/shapes.cjs +109 -0
- package/dist/drawing/shapes.d.cts +16 -0
- package/dist/drawing/shapes.d.ts +16 -0
- package/dist/drawing/shapes.js +108 -0
- package/dist/errors.cjs +17 -0
- package/dist/errors.d.cts +9 -0
- package/dist/errors.d.ts +9 -0
- package/dist/errors.js +15 -0
- package/dist/header-C5eAd16_.d.cts +13 -0
- package/dist/header-C5eAd16_.d.ts +13 -0
- package/dist/index.cjs +112 -0
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +16 -0
- package/dist/read.cjs +119 -0
- package/dist/read.d.cts +13 -0
- package/dist/read.d.ts +13 -0
- package/dist/read.js +114 -0
- package/dist/record/header.cjs +25 -0
- package/dist/record/header.d.cts +2 -0
- package/dist/record/header.d.ts +2 -0
- package/dist/record/header.js +21 -0
- package/dist/record/tree.cjs +55 -0
- package/dist/record/tree.d.cts +2 -0
- package/dist/record/tree.d.ts +2 -0
- package/dist/record/tree.js +49 -0
- package/dist/record/types.cjs +87 -0
- package/dist/record/types.d.cts +45 -0
- package/dist/record/types.d.ts +45 -0
- package/dist/record/types.js +45 -0
- package/dist/stream/current-user.cjs +49 -0
- package/dist/stream/current-user.d.cts +11 -0
- package/dist/stream/current-user.d.ts +11 -0
- package/dist/stream/current-user.js +46 -0
- package/dist/stream/persist.cjs +71 -0
- package/dist/stream/persist.d.cts +20 -0
- package/dist/stream/persist.d.ts +20 -0
- package/dist/stream/persist.js +67 -0
- package/dist/text/atoms.cjs +70 -0
- package/dist/text/atoms.d.cts +22 -0
- package/dist/text/atoms.d.ts +22 -0
- package/dist/text/atoms.js +56 -0
- package/dist/text/style.cjs +173 -0
- package/dist/text/style.d.cts +39 -0
- package/dist/text/style.d.ts +39 -0
- package/dist/text/style.js +165 -0
- package/dist/tree-Du_LXAF0.d.cts +17 -0
- package/dist/tree-PMcPNgd-.d.ts +17 -0
- package/dist/units.cjs +12 -0
- package/dist/units.d.cts +6 -0
- package/dist/units.d.ts +6 -0
- package/dist/units.js +9 -0
- package/package.json +93 -2
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Joseph Mearman
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# ppt-codec
|
|
2
|
+
|
|
3
|
+
[](https://github.com/ExaDev/documents.js/tree/main/packages/ppt-codec) [](https://www.npmjs.com/package/ppt-codec) [](https://www.npmjs.com/package/ppt-codec) [](https://github.com/ExaDev/documents.js/actions)
|
|
4
|
+
|
|
5
|
+
> A hand-written reader for the PowerPoint 97-2003 binary file format (`.ppt`, [MS-PPT]), producing the same `document-schema.js` presentation content model `ooxml.js`'s pptx support and `odf.js`'s odp support both target. Worker-isomorphic: the same code runs under Node and inside a Cloudflare Workers isolate.
|
|
6
|
+
|
|
7
|
+
Created for [documents.js#817](https://github.com/ExaDev/documents.js/issues/817), part of the legacy-binary-formats epic [#85](https://github.com/ExaDev/documents.js/issues/85). Nothing in the ecosystem read a pre-2007 PowerPoint file: `ooxml.js` reads the XML-based pptx that replaced it, and the two formats share no structure at all beyond both being containers.
|
|
8
|
+
|
|
9
|
+
## Status
|
|
10
|
+
|
|
11
|
+
**Under active development. The read path for slide text and geometry is built and tested; there is no write path.** What that means concretely is set out in [What it reads](#what-it-reads) and [What it does not read yet](#what-it-does-not-read-yet) below — both lists are exhaustive rather than illustrative, so a caller can tell from this page alone whether the format's own feature it cares about is covered.
|
|
12
|
+
|
|
13
|
+
## Why the format is shaped the way it is
|
|
14
|
+
|
|
15
|
+
Unlike Word's and Excel's binary formats, whose content is a flat stream of records, a `.ppt` file's content is a **tree** of records, and the tree is not even the whole story:
|
|
16
|
+
|
|
17
|
+
- The file is an [MS-CFB] compound file, whose `PowerPoint Document` stream holds the records and whose `Current User` stream holds a single atom pointing into it.
|
|
18
|
+
- Every record — [MS-PPT]'s own and the [MS-ODRAW] drawing records nested inside it — carries the identical 8-byte header: a 16-bit word packing `recVer` (4 bits) and `recInstance` (12 bits), then `recType` and `recLen`. `recVer == 0xF` marks a container, whose data is more records; anything else marks an atom, whose data is fields. That one distinction is what makes the format a tree rather than a stream, and it is also what lets an unknown record be skipped by seeking `recLen` bytes past its header.
|
|
19
|
+
- The stream is **append-only across edits**. Saving a presentation can append a new _user edit_ rather than rewriting the file, so the same stream can hold several generations of the same slide. Which copy is live is decided by the `Current User` stream's `offsetToCurrentEdit`, the `UserEditAtom` chain it starts, and the persist directory those edits build up — a later edit's directory entry supersedes an earlier one's for the same persist identifier. A reader that simply scanned the stream for `RT_Slide` records would find superseded slides and have no way to tell them from live ones.
|
|
20
|
+
- Slide **placeholder** text is not stored on the slide. A title or body shape's `OfficeArtClientTextbox` holds an `OutlineTextRefAtom` — an index into the text records the _document's_ slide list carries for that slide. Only a plain text box stores its own text.
|
|
21
|
+
|
|
22
|
+
## Getting started
|
|
23
|
+
|
|
24
|
+
Requires Node.js `>=20` and pnpm `11.6.0`.
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
pnpm install
|
|
28
|
+
pnpm build # tsdown -> dist/ (ESM + CJS + .d.ts, one file set per src module)
|
|
29
|
+
pnpm typecheck # tsc -p tsconfig.json && tsc -p tsconfig.node.json, then attw --pack
|
|
30
|
+
pnpm lint # eslint . --fix --cache --max-warnings 0
|
|
31
|
+
pnpm test # vitest run --project unit
|
|
32
|
+
pnpm test:watch # vitest --project unit
|
|
33
|
+
pnpm test:workers # vitest run --config vitest.workers.config.ts, inside a real Cloudflare Workers (workerd) isolate
|
|
34
|
+
pnpm test:smoke # builds dist/, then loads the built ESM and CJS barrels and every advertised deep import
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
To run a single test file, pass its path to vitest directly, e.g. `pnpm exec vitest run src/text/style.test.ts`.
|
|
38
|
+
|
|
39
|
+
## Reading a document
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { readPpt, readPptContent } from "ppt-codec";
|
|
43
|
+
|
|
44
|
+
// The tree form: a document-schema.js DocumentTree, the same artefact
|
|
45
|
+
// ooxml.js's readPptx and odf.js's readOdp produce for their own formats.
|
|
46
|
+
const tree = readPpt(pptBytes);
|
|
47
|
+
|
|
48
|
+
// The flat form: metadata plus ContentSlide[], matching the shape
|
|
49
|
+
// readPptxContent and readOdpContent return.
|
|
50
|
+
const { metadata, slides } = readPptContent(pptBytes);
|
|
51
|
+
for (const slide of slides) {
|
|
52
|
+
slide.size; // { widthPt, heightPt }
|
|
53
|
+
for (const shape of slide.shapes) {
|
|
54
|
+
shape.frame; // { xPt, yPt, widthPt, heightPt }
|
|
55
|
+
shape.blocks; // ContentParagraph[], each with its own ContentRun[]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`readPptStreams(currentUserStream, powerPointDocumentStream)` is the same read one level down, for a caller that already holds the two streams — the compound file beneath them is `archive-codec`'s business, and separating the two is what lets every record-level behaviour be tested without a container around it.
|
|
61
|
+
|
|
62
|
+
## What it reads
|
|
63
|
+
|
|
64
|
+
The whole path from a file's first byte to a slide's text, record by record:
|
|
65
|
+
|
|
66
|
+
| Layer | Records |
|
|
67
|
+
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
68
|
+
| Container | The `Current User` and `PowerPoint Document` streams, read through `archive-codec`'s bounded [MS-CFB] reader. |
|
|
69
|
+
| 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. |
|
|
70
|
+
| 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. |
|
|
71
|
+
| Document | `DocumentContainer` → `DocumentAtom` (slide size, in master units), `DocumentTextInfoContainer`'s `FontCollectionContainer`/`FontEntityAtom` typeface names, and `SlideListWithTextContainer` (distinguished from the master and notes lists by `recInstance`, which does not run in the order the names suggest). |
|
|
72
|
+
| Slides | `SlidePersistAtom` → the persist directory → each `SlideContainer`, and the placeholder texts the slide list carries for it. |
|
|
73
|
+
| 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, and `OfficeArtChildAnchor` mapped through nested `OfficeArtFSPGR` group coordinate systems. |
|
|
74
|
+
| 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`. |
|
|
75
|
+
| Formatting | `StyleTextPropAtom`: `TextPFRun`/`TextPFException` (indent level, alignment) and `TextCFRun`/`TextCFException` (bold, italic, underline, shadow, emboss, typeface reference, size in points, and a `ColorIndexStruct` colour when it is a literal sRGB value), 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. |
|
|
76
|
+
|
|
77
|
+
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.
|
|
78
|
+
|
|
79
|
+
## What it does not read yet
|
|
80
|
+
|
|
81
|
+
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:
|
|
82
|
+
|
|
83
|
+
- **Writing.** There is no write path at all: this package reads `.ppt` and does not produce it.
|
|
84
|
+
- **Encrypted documents.** Recognised and refused by name (`PptEncryptedError`) rather than misparsed, but not decrypted.
|
|
85
|
+
- **Speaker notes.** Every slide's `notes` is `""`. Notes live in their own `NotesContainer` persist objects reached through the document's notes list, which is not yet walked.
|
|
86
|
+
- **Document metadata.** `metadata` is always `{}`. Document properties live in the compound file's own `SummaryInformation` stream ([MS-OSHARED]), not in any [MS-PPT] record.
|
|
87
|
+
- **Master and layout inheritance.** A run that states no size, typeface, or weight inherits it from the master's `TextMasterStyleAtom`; this reader reports such a property as absent rather than resolving the cascade, so a run's formatting is what the slide itself states and no more.
|
|
88
|
+
- **Scheme colours.** A `ColorIndexStruct` naming a colour-scheme slot (rather than a literal sRGB value) yields no colour, because resolving it needs the slide's `SlideSchemeColorSchemeAtom`.
|
|
89
|
+
- **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.
|
|
90
|
+
- **Images, tables, and OLE embeddings.** A picture shape, a table object, and an embedded or linked OLE object all read as a shape with geometry and no blocks. `ExObjListContainer` and the `ExOleObjStg` persist objects are not walked.
|
|
91
|
+
- **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".
|
|
92
|
+
- **Hyperlinks, bullets, spacing and margins.** `InteractiveInfo`/`TextInteractiveInfoAtom`, `TextPFException`'s bullet fields, and its `lineSpacing`/`spaceBefore`/`spaceAfter`/`leftMargin`/`indent` are parsed past correctly but not surfaced.
|
|
93
|
+
- **Animations, transitions, comments, headers and footers, and the metacharacter atoms** (slide number, date, header, footer).
|
|
94
|
+
- **Alignment values the shared schema has no name for.** `Tx_ALIGNDistributed`, `Tx_ALIGNThaiDistributed` and `Tx_ALIGNJustifyLow` map to no alignment rather than being rounded to `justify`.
|
|
95
|
+
- **The soft line break.** U+000B inside a paragraph is converted to a newline, an inference from the spec's own worked examples rather than a rule it states; the specification publishes no table of the special characters a text body may hold.
|
|
96
|
+
|
|
97
|
+
## Architecture
|
|
98
|
+
|
|
99
|
+
Every module is importable by package-relative path as well as through the barrel — `tsdown` builds one dist file per src module (`root: 'src'`, the layout every sibling codec ships), and `package.json`'s `./*` exports wildcard maps each subpath onto it:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import { readRecordAt } from "ppt-codec/record/tree";
|
|
103
|
+
import { readStyleTextPropAtom } from "ppt-codec/text/style";
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
| Module | What it owns |
|
|
107
|
+
| ------------------------ | -------------------------------------------------------------------------------------------------------------------- |
|
|
108
|
+
| `record/header` | The generic 8-byte record header and the container/atom distinction. |
|
|
109
|
+
| `record/types` | The `RecordType` values this reader dispatches on, plus the [MS-ODRAW] types the drawing walk crosses into. |
|
|
110
|
+
| `record/tree` | Offset-addressed records, sibling sequences, child walks, typed-descendant search. |
|
|
111
|
+
| `stream/current-user` | `CurrentUserAtom`: where the live edit is, and whether the file is encrypted. |
|
|
112
|
+
| `stream/persist` | `UserEditAtom`, `PersistDirectoryAtom`, and the persist directory the edit chain builds. |
|
|
113
|
+
| `document/document-atom` | `DocumentAtom`: slide and notes sizes, master persist references. |
|
|
114
|
+
| `document/fonts` | The font collection, resolved to typeface names a `FontIndexRef` indexes. |
|
|
115
|
+
| `document/slide-list` | `SlideListWithTextContainer`: each slide's persist reference and its placeholder texts. |
|
|
116
|
+
| `drawing/shapes` | The OfficeArt shape tree, flattened, with every anchor resolved into slide coordinates through its enclosing groups. |
|
|
117
|
+
| `text/atoms` | The two text-body spellings, the text-type enumeration, and the paragraph split. |
|
|
118
|
+
| `text/style` | `StyleTextPropAtom`'s two run arrays and their mask-driven exception structures. |
|
|
119
|
+
| `content` | The mapping of PowerPoint's character-counted runs onto the schema's paragraph-owned runs. |
|
|
120
|
+
| `read` | The whole pipeline, and the `readPpt`/`readPptContent`/`readPptStreams` surface. |
|
|
121
|
+
| `units` | Master units to points. |
|
|
122
|
+
| `errors` | `PptFormatError` for malformed input, `PptEncryptedError` for well-formed input this package cannot decrypt. |
|
|
123
|
+
|
|
124
|
+
### Every fixture is built from the specification, not captured
|
|
125
|
+
|
|
126
|
+
There is no `.ppt` file anywhere in this package's tests. Every fixture is assembled byte by byte from [MS-PPT]'s own field-layout tables, through the builders in `src/test-support/` — including a whole synthetic presentation and a minimal [MS-CFB] writer, so the end-to-end suite exercises the real offset arithmetic (the persist directory, the edit chain, every cross-stream reference) rather than a stubbed one. That is deliberate: a fixture built from the spec's field tables states what the parser is being held to, whereas a captured file would only state what one producer happened to emit, and could not be reduced to the single record under test.
|
|
127
|
+
|
|
128
|
+
### What it deliberately does not depend on
|
|
129
|
+
|
|
130
|
+
The [MS-CFB] container beneath the format is the one piece not hand-written again: `archive-codec` already owns bounded compound-file reading for the family, and a second implementation here would be exactly the duplication that package's extraction exists to prevent. Everything above it — the record tree, the persist layer, the OfficeArt walk, the text and formatting model — is hand-written against the published specification, the same bet every sibling codec here makes against a heavyweight format library.
|
|
131
|
+
|
|
132
|
+
## Conventions
|
|
133
|
+
|
|
134
|
+
- 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.
|
|
135
|
+
- 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.
|
|
136
|
+
- Every structural failure throws `PptFormatError` rather than degrading: a malformed file fails whole, never returning a partial slide list that looks complete.
|
|
137
|
+
|
|
138
|
+
## Specification references
|
|
139
|
+
|
|
140
|
+
Every field layout in this package is taken from a specification page, cited in the source at the point it is used. The load-bearing ones:
|
|
141
|
+
|
|
142
|
+
- [[MS-PPT]: PowerPoint (.ppt) Binary File Format](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/6be79dde-33c1-4c1b-8ccc-4b2301c08662)
|
|
143
|
+
- [[MS-PPT] 2.1.2: PowerPoint Document Stream](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/1fc22d56-28f9-4818-bd45-67c2bf721ccf) — the "live record" process this reader implements
|
|
144
|
+
- [[MS-PPT] 2.3.1: RecordHeader](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/df201194-0cd0-4dfb-bf10-eea353d8eabc)
|
|
145
|
+
- [[MS-PPT] 2.3.2: CurrentUserAtom](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/940d5700-e4d7-4fc0-ab48-fed5dbc48bc1)
|
|
146
|
+
- [[MS-PPT] 2.3.3: UserEditAtom](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/3ffb3fab-95de-4873-98aa-d508fbbac981) and [2.3.5: PersistDirectoryEntry](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/6214b5a6-7ca2-4a86-8a0e-5fd3d3eff1c9)
|
|
147
|
+
- [[MS-PPT] 2.4.1: DocumentContainer](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/6254c4d1-5217-4e16-b20d-c04ddcce31c9) and [2.4.2: DocumentAtom](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/121f2728-3497-4a0a-829e-6f416fee2ee6)
|
|
148
|
+
- [[MS-PPT] 2.4.14.3: SlideListWithTextContainer](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/307e6d12-7304-47a8-acbd-3e7b8041ad3c) and [2.4.14.5: SlidePersistAtom](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/48dce412-9692-4f93-aeb7-3d9fdd3a0a5a)
|
|
149
|
+
- [[MS-PPT] 2.5.1: SlideContainer](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/4cac0976-73d0-4ab3-a70b-e98b3cf1c312) and [2.5.13: DrawingContainer](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/0595b49f-da96-4402-b353-1f766e9d548f)
|
|
150
|
+
- [[MS-PPT] 2.7.1: OfficeArtClientAnchor](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/37ee18c7-3c7c-4adc-91fb-cb3b01789d72), [SmallRectStruct](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/e47cb973-8480-4995-90b2-008bcb2ffc65), [RectStruct](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/8a58e3ae-2682-42d0-82cd-a41c2999584e)
|
|
151
|
+
- [[MS-PPT] 2.9.76: OfficeArtClientTextbox](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/f50070dd-a4dc-4edd-a446-c4fcc5c80ace), [TextHeaderAtom](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/08d31a66-0750-4009-b416-49f2871cd178), [TextCharsAtom](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/a3c5c8d5-e530-4167-a242-7743bc99aeac), [TextBytesAtom](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/80aae34b-2699-43fa-9e6a-c560ae790cd7)
|
|
152
|
+
- [[MS-PPT]: StyleTextPropAtom](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/a9a5fa71-238d-491e-acc7-fa1fffd5f100), [TextPFException](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/c15a13b3-db2c-4b50-a7e6-08045581a663), [PFMasks](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/2a02831a-088b-44e7-84c9-c185ab314a71), [TextCFException](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/c75024a2-14cb-4d7d-9964-bdab2fcd9d93), [CFMasks](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/bbca8581-d011-4293-a375-b209523cf962), [CFStyle](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/3ea010b9-0ef9-4c05-9982-618130ca66cd), [ColorIndexStruct](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-ppt/5d6b0509-f3c7-435f-9bf4-6f1fc5f8293c)
|
|
153
|
+
- [[MS-ODRAW]: Office Drawing Binary File Format](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/8560795e-7759-4745-838f-f7f2ef2f1872) — [OfficeArtSpContainer](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/16194cb9-b4b0-476c-9678-a6ac1f06b034), [OfficeArtFSP](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/8a7e7be3-0582-4461-9400-29d7eda8497d), [OfficeArtFSPGR](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/82d2d6a1-3a7a-4d15-9803-33145a76545a), [OfficeArtChildAnchor](https://learn.microsoft.com/en-us/openspecs/office_file_formats/ms-odraw/33a44593-02df-4684-ab35-5a7c4a9bcaac)
|
|
154
|
+
- [[MS-CFB]: Compound File Binary File Format](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-cfb/53989ce4-7b05-4f8d-829b-d08d6148375b) — the container, read through `archive-codec`
|
|
155
|
+
|
|
156
|
+
## Install
|
|
157
|
+
|
|
158
|
+
```sh
|
|
159
|
+
pnpm add ppt-codec
|
|
160
|
+
# or
|
|
161
|
+
npm install ppt-codec
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Release and publishing
|
|
165
|
+
|
|
166
|
+
Release, CI, and commit-message conventions are all workspace-wide, not package-local — see the [monorepo root README](../../README.md#releases) for the mechanism.
|
|
167
|
+
|
|
168
|
+
## Contributing
|
|
169
|
+
|
|
170
|
+
Conventional Commits, enforced workspace-wide by commitlint through a root `commit-msg` hook. Work inside `packages/ppt-codec/`; see [CONTRIBUTING.md](../../CONTRIBUTING.md) for the shared git hooks and history conventions.
|
|
171
|
+
|
|
172
|
+
## License
|
|
173
|
+
|
|
174
|
+
MIT
|
package/dist/content.cjs
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_text_atoms = require("./text/atoms.cjs");
|
|
3
|
+
require("./text/style.cjs");
|
|
4
|
+
//#region src/content.ts
|
|
5
|
+
const BYTE_MAX = 255;
|
|
6
|
+
function mapAlignment(alignment) {
|
|
7
|
+
switch (alignment) {
|
|
8
|
+
case 0: return "left";
|
|
9
|
+
case 1: return "center";
|
|
10
|
+
case 2: return "right";
|
|
11
|
+
case 3: return "justify";
|
|
12
|
+
default: return;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function mapColor(color) {
|
|
16
|
+
if (color === void 0) return;
|
|
17
|
+
return {
|
|
18
|
+
r: color.red / BYTE_MAX,
|
|
19
|
+
g: color.green / BYTE_MAX,
|
|
20
|
+
b: color.blue / BYTE_MAX
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function toExtents(runs) {
|
|
24
|
+
const extents = [];
|
|
25
|
+
let at = 0;
|
|
26
|
+
for (const run of runs) {
|
|
27
|
+
extents.push({
|
|
28
|
+
start: at,
|
|
29
|
+
end: at + run.count,
|
|
30
|
+
properties: run.properties
|
|
31
|
+
});
|
|
32
|
+
at += run.count;
|
|
33
|
+
}
|
|
34
|
+
return extents;
|
|
35
|
+
}
|
|
36
|
+
function runFrom(text, properties, fontNames) {
|
|
37
|
+
return {
|
|
38
|
+
text,
|
|
39
|
+
bold: properties.bold,
|
|
40
|
+
italic: properties.italic,
|
|
41
|
+
underline: properties.underline,
|
|
42
|
+
fontFamily: properties.fontRef === void 0 ? void 0 : fontNames[properties.fontRef],
|
|
43
|
+
sizePt: properties.sizePt,
|
|
44
|
+
color: mapColor(properties.color)
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function buildParagraphs(text, style, fontNames) {
|
|
48
|
+
const paragraphExtents = toExtents(style.paragraphRuns);
|
|
49
|
+
const characterExtents = toExtents(style.characterRuns);
|
|
50
|
+
return require_text_atoms.splitParagraphs(text).map((paragraph) => {
|
|
51
|
+
const end = paragraph.start + paragraph.text.length;
|
|
52
|
+
const paragraphProperties = paragraphExtents.find((extent) => paragraph.start >= extent.start && paragraph.start < extent.end)?.properties;
|
|
53
|
+
const runs = [];
|
|
54
|
+
for (const extent of characterExtents) {
|
|
55
|
+
const from = Math.max(extent.start, paragraph.start);
|
|
56
|
+
const to = Math.min(extent.end, end);
|
|
57
|
+
if (from >= to) continue;
|
|
58
|
+
runs.push(runFrom(paragraph.text.slice(from - paragraph.start, to - paragraph.start), extent.properties, fontNames));
|
|
59
|
+
}
|
|
60
|
+
if (runs.length === 0 && paragraph.text.length > 0) runs.push({ text: paragraph.text });
|
|
61
|
+
const alignment = mapAlignment(paragraphProperties?.alignment);
|
|
62
|
+
const indentLevel = paragraphProperties?.indentLevel ?? 0;
|
|
63
|
+
return {
|
|
64
|
+
kind: "paragraph",
|
|
65
|
+
runs,
|
|
66
|
+
alignment,
|
|
67
|
+
list: indentLevel > 0 ? { level: indentLevel } : void 0
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
exports.buildParagraphs = buildParagraphs;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { StyleTextProps } from "./text/style.cjs";
|
|
2
|
+
import { ContentParagraph } from "document-schema.js";
|
|
3
|
+
//#region src/content.d.ts
|
|
4
|
+
declare function buildParagraphs(text: string, style: StyleTextProps, fontNames: readonly string[]): ContentParagraph[];
|
|
5
|
+
//#endregion
|
|
6
|
+
export { buildParagraphs };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { StyleTextProps } from "./text/style.js";
|
|
2
|
+
import { ContentParagraph } from "document-schema.js";
|
|
3
|
+
//#region src/content.d.ts
|
|
4
|
+
declare function buildParagraphs(text: string, style: StyleTextProps, fontNames: readonly string[]): ContentParagraph[];
|
|
5
|
+
//#endregion
|
|
6
|
+
export { buildParagraphs };
|
package/dist/content.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { splitParagraphs } from "./text/atoms.js";
|
|
2
|
+
import "./text/style.js";
|
|
3
|
+
//#region src/content.ts
|
|
4
|
+
const BYTE_MAX = 255;
|
|
5
|
+
function mapAlignment(alignment) {
|
|
6
|
+
switch (alignment) {
|
|
7
|
+
case 0: return "left";
|
|
8
|
+
case 1: return "center";
|
|
9
|
+
case 2: return "right";
|
|
10
|
+
case 3: return "justify";
|
|
11
|
+
default: return;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function mapColor(color) {
|
|
15
|
+
if (color === void 0) return;
|
|
16
|
+
return {
|
|
17
|
+
r: color.red / BYTE_MAX,
|
|
18
|
+
g: color.green / BYTE_MAX,
|
|
19
|
+
b: color.blue / BYTE_MAX
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function toExtents(runs) {
|
|
23
|
+
const extents = [];
|
|
24
|
+
let at = 0;
|
|
25
|
+
for (const run of runs) {
|
|
26
|
+
extents.push({
|
|
27
|
+
start: at,
|
|
28
|
+
end: at + run.count,
|
|
29
|
+
properties: run.properties
|
|
30
|
+
});
|
|
31
|
+
at += run.count;
|
|
32
|
+
}
|
|
33
|
+
return extents;
|
|
34
|
+
}
|
|
35
|
+
function runFrom(text, properties, fontNames) {
|
|
36
|
+
return {
|
|
37
|
+
text,
|
|
38
|
+
bold: properties.bold,
|
|
39
|
+
italic: properties.italic,
|
|
40
|
+
underline: properties.underline,
|
|
41
|
+
fontFamily: properties.fontRef === void 0 ? void 0 : fontNames[properties.fontRef],
|
|
42
|
+
sizePt: properties.sizePt,
|
|
43
|
+
color: mapColor(properties.color)
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function buildParagraphs(text, style, fontNames) {
|
|
47
|
+
const paragraphExtents = toExtents(style.paragraphRuns);
|
|
48
|
+
const characterExtents = toExtents(style.characterRuns);
|
|
49
|
+
return splitParagraphs(text).map((paragraph) => {
|
|
50
|
+
const end = paragraph.start + paragraph.text.length;
|
|
51
|
+
const paragraphProperties = paragraphExtents.find((extent) => paragraph.start >= extent.start && paragraph.start < extent.end)?.properties;
|
|
52
|
+
const runs = [];
|
|
53
|
+
for (const extent of characterExtents) {
|
|
54
|
+
const from = Math.max(extent.start, paragraph.start);
|
|
55
|
+
const to = Math.min(extent.end, end);
|
|
56
|
+
if (from >= to) continue;
|
|
57
|
+
runs.push(runFrom(paragraph.text.slice(from - paragraph.start, to - paragraph.start), extent.properties, fontNames));
|
|
58
|
+
}
|
|
59
|
+
if (runs.length === 0 && paragraph.text.length > 0) runs.push({ text: paragraph.text });
|
|
60
|
+
const alignment = mapAlignment(paragraphProperties?.alignment);
|
|
61
|
+
const indentLevel = paragraphProperties?.indentLevel ?? 0;
|
|
62
|
+
return {
|
|
63
|
+
kind: "paragraph",
|
|
64
|
+
runs,
|
|
65
|
+
alignment,
|
|
66
|
+
list: indentLevel > 0 ? { level: indentLevel } : void 0
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
//#endregion
|
|
71
|
+
export { buildParagraphs };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_errors = require("../errors.cjs");
|
|
3
|
+
const require_record_types = require("../record/types.cjs");
|
|
4
|
+
//#region src/document/document-atom.ts
|
|
5
|
+
const DOCUMENT_ATOM_LEN = 40;
|
|
6
|
+
function readDocumentAtom(record) {
|
|
7
|
+
if (record.header.recType !== 1001) throw new require_errors.PptFormatError(`expected RT_DocumentAtom (0x${require_record_types.RT_DocumentAtom.toString(16)}), found record type 0x${record.header.recType.toString(16)}`);
|
|
8
|
+
if (record.data.length < DOCUMENT_ATOM_LEN) throw new require_errors.PptFormatError(`DocumentAtom carries ${record.data.length} bytes, fewer than the mandated 0x${DOCUMENT_ATOM_LEN.toString(16)}`);
|
|
9
|
+
const { data } = record;
|
|
10
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
11
|
+
return {
|
|
12
|
+
slideSize: {
|
|
13
|
+
x: view.getInt32(0, true),
|
|
14
|
+
y: view.getInt32(4, true)
|
|
15
|
+
},
|
|
16
|
+
notesSize: {
|
|
17
|
+
x: view.getInt32(8, true),
|
|
18
|
+
y: view.getInt32(12, true)
|
|
19
|
+
},
|
|
20
|
+
notesMasterPersistIdRef: view.getUint32(24, true),
|
|
21
|
+
handoutMasterPersistIdRef: view.getUint32(28, true),
|
|
22
|
+
firstSlideNumber: view.getUint16(32, true),
|
|
23
|
+
slideSizeType: view.getUint16(34, true)
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
exports.readDocumentAtom = readDocumentAtom;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { t as PptRecord } from "../tree-Du_LXAF0.cjs";
|
|
2
|
+
//#region src/document/document-atom.d.ts
|
|
3
|
+
interface PointStruct {
|
|
4
|
+
readonly x: number;
|
|
5
|
+
readonly y: number;
|
|
6
|
+
}
|
|
7
|
+
interface DocumentAtom {
|
|
8
|
+
readonly slideSize: PointStruct;
|
|
9
|
+
readonly notesSize: PointStruct;
|
|
10
|
+
readonly notesMasterPersistIdRef: number;
|
|
11
|
+
readonly handoutMasterPersistIdRef: number;
|
|
12
|
+
readonly firstSlideNumber: number;
|
|
13
|
+
readonly slideSizeType: number;
|
|
14
|
+
}
|
|
15
|
+
declare function readDocumentAtom(record: PptRecord): DocumentAtom;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { DocumentAtom, PointStruct, readDocumentAtom };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { t as PptRecord } from "../tree-PMcPNgd-.js";
|
|
2
|
+
//#region src/document/document-atom.d.ts
|
|
3
|
+
interface PointStruct {
|
|
4
|
+
readonly x: number;
|
|
5
|
+
readonly y: number;
|
|
6
|
+
}
|
|
7
|
+
interface DocumentAtom {
|
|
8
|
+
readonly slideSize: PointStruct;
|
|
9
|
+
readonly notesSize: PointStruct;
|
|
10
|
+
readonly notesMasterPersistIdRef: number;
|
|
11
|
+
readonly handoutMasterPersistIdRef: number;
|
|
12
|
+
readonly firstSlideNumber: number;
|
|
13
|
+
readonly slideSizeType: number;
|
|
14
|
+
}
|
|
15
|
+
declare function readDocumentAtom(record: PptRecord): DocumentAtom;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { DocumentAtom, PointStruct, readDocumentAtom };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { PptFormatError } from "../errors.js";
|
|
2
|
+
import { RT_DocumentAtom } from "../record/types.js";
|
|
3
|
+
//#region src/document/document-atom.ts
|
|
4
|
+
const DOCUMENT_ATOM_LEN = 40;
|
|
5
|
+
function readDocumentAtom(record) {
|
|
6
|
+
if (record.header.recType !== 1001) throw new PptFormatError(`expected RT_DocumentAtom (0x${RT_DocumentAtom.toString(16)}), found record type 0x${record.header.recType.toString(16)}`);
|
|
7
|
+
if (record.data.length < DOCUMENT_ATOM_LEN) throw new PptFormatError(`DocumentAtom carries ${record.data.length} bytes, fewer than the mandated 0x${DOCUMENT_ATOM_LEN.toString(16)}`);
|
|
8
|
+
const { data } = record;
|
|
9
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
10
|
+
return {
|
|
11
|
+
slideSize: {
|
|
12
|
+
x: view.getInt32(0, true),
|
|
13
|
+
y: view.getInt32(4, true)
|
|
14
|
+
},
|
|
15
|
+
notesSize: {
|
|
16
|
+
x: view.getInt32(8, true),
|
|
17
|
+
y: view.getInt32(12, true)
|
|
18
|
+
},
|
|
19
|
+
notesMasterPersistIdRef: view.getUint32(24, true),
|
|
20
|
+
handoutMasterPersistIdRef: view.getUint32(28, true),
|
|
21
|
+
firstSlideNumber: view.getUint16(32, true),
|
|
22
|
+
slideSizeType: view.getUint16(34, true)
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { readDocumentAtom };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_record_tree = require("../record/tree.cjs");
|
|
3
|
+
const require_record_types = require("../record/types.cjs");
|
|
4
|
+
//#region src/document/fonts.ts
|
|
5
|
+
const FACE_NAME_BYTES = 64;
|
|
6
|
+
function readFaceName(record) {
|
|
7
|
+
if (record.data.length < FACE_NAME_BYTES) return;
|
|
8
|
+
const view = new DataView(record.data.buffer, record.data.byteOffset, record.data.byteLength);
|
|
9
|
+
let name = "";
|
|
10
|
+
for (let at = 0; at < FACE_NAME_BYTES; at += 2) {
|
|
11
|
+
const unit = view.getUint16(at, true);
|
|
12
|
+
if (unit === 0) break;
|
|
13
|
+
name += String.fromCharCode(unit);
|
|
14
|
+
}
|
|
15
|
+
return name;
|
|
16
|
+
}
|
|
17
|
+
function readFontNames(environment) {
|
|
18
|
+
const collection = require_record_tree.findChild(require_record_tree.childRecords(environment), require_record_types.RT_FontCollection);
|
|
19
|
+
if (collection === void 0) return [];
|
|
20
|
+
const names = [];
|
|
21
|
+
for (const entry of require_record_tree.childRecords(collection)) {
|
|
22
|
+
if (entry.header.recType !== 4023) continue;
|
|
23
|
+
const name = readFaceName(entry);
|
|
24
|
+
if (name !== void 0) names.push(name);
|
|
25
|
+
}
|
|
26
|
+
return names;
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
exports.readFontNames = readFontNames;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { childRecords, findChild } from "../record/tree.js";
|
|
2
|
+
import { RT_FontCollection } from "../record/types.js";
|
|
3
|
+
//#region src/document/fonts.ts
|
|
4
|
+
const FACE_NAME_BYTES = 64;
|
|
5
|
+
function readFaceName(record) {
|
|
6
|
+
if (record.data.length < FACE_NAME_BYTES) return;
|
|
7
|
+
const view = new DataView(record.data.buffer, record.data.byteOffset, record.data.byteLength);
|
|
8
|
+
let name = "";
|
|
9
|
+
for (let at = 0; at < FACE_NAME_BYTES; at += 2) {
|
|
10
|
+
const unit = view.getUint16(at, true);
|
|
11
|
+
if (unit === 0) break;
|
|
12
|
+
name += String.fromCharCode(unit);
|
|
13
|
+
}
|
|
14
|
+
return name;
|
|
15
|
+
}
|
|
16
|
+
function readFontNames(environment) {
|
|
17
|
+
const collection = findChild(childRecords(environment), RT_FontCollection);
|
|
18
|
+
if (collection === void 0) return [];
|
|
19
|
+
const names = [];
|
|
20
|
+
for (const entry of childRecords(collection)) {
|
|
21
|
+
if (entry.header.recType !== 4023) continue;
|
|
22
|
+
const name = readFaceName(entry);
|
|
23
|
+
if (name !== void 0) names.push(name);
|
|
24
|
+
}
|
|
25
|
+
return names;
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { readFontNames };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_errors = require("../errors.cjs");
|
|
3
|
+
const require_record_tree = require("../record/tree.cjs");
|
|
4
|
+
const require_record_types = require("../record/types.cjs");
|
|
5
|
+
const require_text_atoms = require("../text/atoms.cjs");
|
|
6
|
+
//#region src/document/slide-list.ts
|
|
7
|
+
const SLIDE_PERSIST_ATOM_LEN = 20;
|
|
8
|
+
function readSlideListWithText(listContainer) {
|
|
9
|
+
if (listContainer.header.recType !== 4080) throw new require_errors.PptFormatError(`expected RT_SlideListWithText (0x${require_record_types.RT_SlideListWithText.toString(16)}), found record type 0x${listContainer.header.recType.toString(16)}`);
|
|
10
|
+
const slides = [];
|
|
11
|
+
let currentSlide;
|
|
12
|
+
let currentText;
|
|
13
|
+
for (const record of require_record_tree.childRecords(listContainer)) {
|
|
14
|
+
if (record.header.recType === 1011) {
|
|
15
|
+
if (record.data.length < SLIDE_PERSIST_ATOM_LEN) throw new require_errors.PptFormatError(`SlidePersistAtom at offset ${record.offset} carries ${record.data.length} bytes, fewer than the mandated 0x${SLIDE_PERSIST_ATOM_LEN.toString(16)}`);
|
|
16
|
+
const view = new DataView(record.data.buffer, record.data.byteOffset, record.data.byteLength);
|
|
17
|
+
currentSlide = {
|
|
18
|
+
persistIdRef: view.getUint32(0, true),
|
|
19
|
+
slideId: view.getUint32(12, true),
|
|
20
|
+
texts: []
|
|
21
|
+
};
|
|
22
|
+
currentText = void 0;
|
|
23
|
+
slides.push(currentSlide);
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (record.header.recType === 3999) {
|
|
27
|
+
if (currentSlide === void 0) throw new require_errors.PptFormatError(`TextHeaderAtom at offset ${record.offset} precedes any SlidePersistAtom, so it belongs to no slide`);
|
|
28
|
+
currentText = {
|
|
29
|
+
textType: require_text_atoms.readTextHeaderAtom(record),
|
|
30
|
+
records: []
|
|
31
|
+
};
|
|
32
|
+
currentSlide.texts.push(currentText);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
currentText?.records.push(record);
|
|
36
|
+
}
|
|
37
|
+
return slides;
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
exports.readSlideListWithText = readSlideListWithText;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { t as PptRecord } from "../tree-Du_LXAF0.cjs";
|
|
2
|
+
//#region src/document/slide-list.d.ts
|
|
3
|
+
interface OutlineText {
|
|
4
|
+
readonly textType: number;
|
|
5
|
+
readonly records: readonly PptRecord[];
|
|
6
|
+
}
|
|
7
|
+
interface SlidePersist {
|
|
8
|
+
readonly persistIdRef: number;
|
|
9
|
+
readonly slideId: number;
|
|
10
|
+
readonly texts: readonly OutlineText[];
|
|
11
|
+
}
|
|
12
|
+
declare function readSlideListWithText(listContainer: PptRecord): SlidePersist[];
|
|
13
|
+
//#endregion
|
|
14
|
+
export { OutlineText, SlidePersist, readSlideListWithText };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { t as PptRecord } from "../tree-PMcPNgd-.js";
|
|
2
|
+
//#region src/document/slide-list.d.ts
|
|
3
|
+
interface OutlineText {
|
|
4
|
+
readonly textType: number;
|
|
5
|
+
readonly records: readonly PptRecord[];
|
|
6
|
+
}
|
|
7
|
+
interface SlidePersist {
|
|
8
|
+
readonly persistIdRef: number;
|
|
9
|
+
readonly slideId: number;
|
|
10
|
+
readonly texts: readonly OutlineText[];
|
|
11
|
+
}
|
|
12
|
+
declare function readSlideListWithText(listContainer: PptRecord): SlidePersist[];
|
|
13
|
+
//#endregion
|
|
14
|
+
export { OutlineText, SlidePersist, readSlideListWithText };
|