doc-codec 2.8.5 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -23
- package/dist/fib/write.cjs +8 -0
- package/dist/fib/write.d.cts +16 -0
- package/dist/fib/write.d.ts +16 -0
- package/dist/fib/write.js +8 -0
- package/dist/headers-footers.cjs +5 -3
- package/dist/headers-footers.d.cts +5 -1
- package/dist/headers-footers.d.ts +5 -1
- package/dist/headers-footers.js +4 -4
- package/dist/index.cjs +7 -0
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3 -2
- package/dist/read.cjs +18 -1
- package/dist/read.js +18 -1
- package/dist/subdocument-write.cjs +108 -0
- package/dist/subdocument-write.d.cts +42 -0
- package/dist/subdocument-write.d.ts +42 -0
- package/dist/subdocument-write.js +104 -0
- package/dist/subdocument.cjs +8 -1
- package/dist/subdocument.js +8 -1
- package/dist/table/write.cjs +20 -1
- package/dist/table/write.js +20 -1
- package/dist/write.cjs +35 -5
- package/dist/write.d.cts +11 -2
- package/dist/write.d.ts +11 -2
- package/dist/write.js +35 -5
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -24,30 +24,30 @@ Built and shipped, on the read side:
|
|
|
24
24
|
- **Document metadata** — `title`/`subject`/`author`/`keywords`/`createdIso`/`modifiedIso` read from a `"\x05SummaryInformation"` stream when one is present (see [Metadata](#metadata)); `comments` and `lastPrintedIso` remain unread, since `LayoutMetadata` has no field for either.
|
|
25
25
|
- **RC4-encrypted and XOR-obfuscated documents, decrypted for real (ExaDev/documents.js#1113, #922)** — `readDocContent`/`readDocStreams` take an optional `password`, verified against the Table stream's own `EncryptionHeader` for RC4, or against `FibBase.lKey` directly for XOR obfuscation (see [Encryption](#encryption)). RC4 CryptoAPI stays refused.
|
|
26
26
|
- **Numbering definitions** — `readDocContent`'s own `numbering` field: every list's glyph/format, level-text template, and start-at value, resolved from `PlfLst`/`PlfLfo` and keyed by the same `listId` a paragraph's `ContentParagraph.list.numId` already carries. See [Numbering definitions](#numbering-definitions), which also covers `writeDocContent`'s own inverse.
|
|
27
|
-
- **Every section's own page size and margins** — `prop/sep.ts`'s `readAllSectionProperties` resolves every `Sed` `PlcfSed` carries, each into its own `ContentSection.pageSize`/`.margins` (`sprmSXaPage`/`sprmSYaPage`/`sprmSDxaLeft`/`sprmSDxaRight`/`sprmSDyaTop`/`sprmSDyaBottom`), falling back field by field to Word's own new-document default (US Letter, one-inch margins) for a document carrying no `PlcfSed` at all, or for any one sprm a section leaves unstated. `read.ts`'s `splitIntoSections` groups the main document's own paragraphs by which section each falls in, per [MS-DOC] 2.8.26's own "each CP specifies the beginning of a range of text ... that constitutes a section" — a genuinely multi-section `.doc` now reads as multiple `ContentSection`s, each with its own real page geometry, rather than the whole document collapsing onto the first section's.
|
|
28
|
-
- **Footnotes, endnotes, and comments, as plain text** — `notes.ts`'s `readNoteBodies` resolves `PlcffndTxt`/`PlcfendTxt`/`PlcfandTxt` (each a CP-only plex dividing its own subdocument into stories, [MS-DOC]'s own "each CP ... specifies the beginning of a story ... the story ends immediately prior to the next CP") into `DocContent.footnotes`/`.endnotes`/`.comments`: one `{id, text}`/`{id, text}` entry per story, `id` a synthetic one-based ordinal, matching the same simplification `ooxml.js`'s own `DocxDocument.footnotes`/`.endnotes`/`.comments` already make for the identical constructs (plain text, no paragraph/run structure). A story's own trailing guard paragraph mark ("not considered part of the story contents", [MS-DOC]'s own words) is excluded from its text; a genuinely empty story reads as `""`.
|
|
27
|
+
- **Every section's own page size and margins** — `prop/sep.ts`'s `readAllSectionProperties` resolves every `Sed` `PlcfSed` carries, each into its own `ContentSection.pageSize`/`.margins` (`sprmSXaPage`/`sprmSYaPage`/`sprmSDxaLeft`/`sprmSDxaRight`/`sprmSDyaTop`/`sprmSDyaBottom`), falling back field by field to Word's own new-document default (US Letter, one-inch margins) for a document carrying no `PlcfSed` at all, or for any one sprm a section leaves unstated. `read.ts`'s `splitIntoSections` groups the main document's own paragraphs by which section each falls in, per [MS-DOC] 2.8.26's own "each CP specifies the beginning of a range of text ... that constitutes a section" — a genuinely multi-section `.doc` now reads as multiple `ContentSection`s, each with its own real page geometry, rather than the whole document collapsing onto the first section's. A 0x000C where no section ends reads as a real `pageBreak` block attached to the paragraph it terminates (`read.ts`'s `markManualPageBreaks`) — [MS-DOC]'s own PlcfSed.aCP text distinguishes the format's two spellings of that one character outright ("An end-of-section character (0x0C) which occurs at a CP and which is not the last character in a section specifies a manual page break"), and `writeDocContent` writes that exact shape back (see [Writing](#writing)).
|
|
28
|
+
- **Footnotes, endnotes, and comments, as plain text** — `notes.ts`'s `readNoteBodies` resolves `PlcffndTxt`/`PlcfendTxt`/`PlcfandTxt` (each a CP-only plex dividing its own subdocument into stories, [MS-DOC]'s own "each CP ... specifies the beginning of a story ... the story ends immediately prior to the next CP") into `DocContent.footnotes`/`.endnotes`/`.comments`: one `{id, text}`/`{id, text}` entry per story, `id` a synthetic one-based ordinal, matching the same simplification `ooxml.js`'s own `DocxDocument.footnotes`/`.endnotes`/`.comments` already make for the identical constructs (plain text, no paragraph/run structure). A story's own trailing guard paragraph mark ("not considered part of the story contents", [MS-DOC]'s own words) is excluded from its text when the story ends in one — and a story that ends with a content paragraph of its own is kept whole, the spelling a real producer writes for note stories (a LibreOffice-authored single-paragraph footnote ends at its own content mark with no separate guard; an earlier unconditional drop read every such note as `""` and lost a multi-paragraph note's last paragraph). A genuinely empty story reads as `""`.
|
|
29
29
|
- **Headers and footers, as real block flow** — `headers-footers.ts`'s `readHeaderFooterStories` resolves `Plcfhdd` (the identical CP-only-plex shape as the note-text plexes above) into `DocContent.headerFooterStories`: one entry per non-empty story, each carrying which section it belongs to, which of the six fixed slots it is (`evenHeader`/`oddHeader`/`evenFooter`/`oddFooter`/`firstHeader`/`firstFooter`, [MS-DOC]'s own fixed per-section story order), and a real `ContentBlock[]` — paragraphs and tables, read through the identical pipeline the main document uses, rather than plain text. An empty story ([MS-DOC]'s own "the beginning CP has the same value as the next CP") is left out of the array entirely, since it means "reuse the previous section's header/footer of this kind" rather than a genuinely blank one. The six footnote/endnote-separator stories Plcfhdd carries ahead of every section's own six are not read: neither this package's schema nor `ooxml.js`'s `DocxDocument` has anywhere to put a separator story.
|
|
30
30
|
- **Inline pictures** — `pictures.ts`'s `readInlinePicture` resolves a picture character (`U+0001`, `sprmCFSpec` applied) through its own Chpx's `sprmCPicLocation` operand: an offset into the container's `"Data"` stream where a `PICF` ([MS-DOC] 68-byte type/size/border header) is followed, for every producer this reader has seen, by a real `OfficeArtInlineSpContainer` ([MS-ODRAW] 2.2.15) regardless of `PICF.mfpf.mm`'s own value. The container's own `shape` (an `OfficeArtSpContainer`) is skipped whole by its own record header's `recLen`, and the first blip record in `rgfb` immediately after it is decoded when it is `OfficeArtBlipJPEG`/`OfficeArtBlipPNG` — the two raster formats `document-schema.js`'s `ContentImageBlock` can hold losslessly — into a real inline image, sized from `PICMID`'s own `dxaGoal`/`dyaGoal` scaled by `mx`/`my`. A paragraph containing a picture anchor is split around it into a real `ContentBlock[]` (`text/paragraphs.ts`'s `buildParagraphBlocks`), since an inline image is block-level in `document-schema.js`'s own model rather than a run property — mirroring how `ooxml.js`'s own docx reader splits a paragraph around a mid-run page break, paragraph-level formatting carried unchanged onto every split piece. See [Images](#images) for what this does not read.
|
|
31
31
|
|
|
32
32
|
Built and shipped, on the write side — see [Writing](#writing) for the full scope statement:
|
|
33
33
|
|
|
34
|
-
- **`writeDocContent`** — a `'wordprocessing'` `ContentDocument` (any number of sections, each of paragraphs, tables, and
|
|
34
|
+
- **`writeDocContent`** — a `'wordprocessing'` `ContentDocument` (any number of sections, each of paragraphs, tables, inline images, and page breaks), widened by the optional story fields a `DocContent` read carries (footnotes/endnotes/comments as plain-text bodies, header/footer stories as real block flow — see [Writing](#writing)'s stories paragraph), to genuine [MS-DOC] bytes: a real piece table, real `ChpxFkp`/`PapxFkp` pages (splitting across as many as a document's own formatting needs, not just the common one-page case), a real style sheet minting one entry per distinct `styleId`/`headingLevel` a document's paragraphs actually use (`style/stsh.ts`'s `buildStshForStyles`, [ExaDev/documents.js#1059](https://github.com/ExaDev/documents.js/issues/1059)), a font table when a run names one, real `PlfLst`/`PlfLfo` numbering tables when at least one paragraph carries `list` (see [Numbering definitions](#numbering-definitions)), a real `PlcfSed`/`Sepx` array stating every section's own page size and margins (`ExaDev/documents.js#971`, see [Sections](#sections)), the four story subdocuments and their boundary plexes when the input carries stories (`subdocument-write.ts`, see [Subdocuments](#subdocuments)), a `"Data"` stream carrying every inline picture's own real `PICF`/`OfficeArtInlineSpContainer`/blip bytes (`ExaDev/documents.js#971`, see [Images](#images)), and a `"\x05SummaryInformation"` stream when the input's metadata carries anything that stream can hold (see [Metadata](#metadata)) — wrapped in a real [MS-CFB] compound file via `archive-codec`'s `writeCompoundFile`. A `ContentTable` block is expanded by `table/write.ts`'s `flattenSectionBlocks` into the same flat paragraph sequence every other block already is (see [Tables](#tables)), so table paragraphs flow through the identical `ChpxFkp`/`PapxFkp` paging as every other paragraph rather than a separate table-only path. Each cell's own `background` and `borders` ride along into the row's TAP (see [Cell decoration](#cell-decoration)). A `pageBreak` block is written as the format's own manual-page-break spelling of 0x000C (see [Writing](#writing)).
|
|
35
35
|
- Every property `writeDocContent` writes is verified by reading it back through this package's own `readDocContent` (`src/write.test.ts`), and additionally against a real, independent [MS-DOC] implementation: LibreOffice opened, rendered, and re-exported a `writeDocContent` sample without error or content loss, including bold/italic/underline/strike/size/colour/font-family runs, paragraph alignment and indentation, non-Latin-1 and non-BMP text (accented Latin, CJK, an emoji surrogate pair), a section's own page size and all four margins (a non-default 600x800pt page with a 90/54/45/36pt left/right/top/bottom margin, confirmed against LibreOffice's own `fo:page-width`/`fo:page-height`/`fo:margin-*` export to the exact point), and a table — recognised as a genuine `table:table`, its row/column/cell structure and both horizontal and vertical merges intact, matching real `table:number-columns-spanned`/`table:number-rows-spanned` attributes and `table:covered-table-cell` elements, and each cell's own background fill and per-side borders recovered with their exact colours, exactly as [Tables](#tables) confirms in full.
|
|
36
36
|
|
|
37
37
|
**Not built, and not approximated, on either side.** Each of these is a genuine layer of [MS-DOC] that this package does not implement; none is silently faked, and a document using one reads (or fails to write) as though it did not:
|
|
38
38
|
|
|
39
|
-
| Absent | Consequence
|
|
40
|
-
| ----------------------------------------------------------------------------------------- |
|
|
41
|
-
| **Nested tables, on write** | `readDocContent` recurses into a table nested inside a table cell to whatever depth the file states (see the read-side Tables bullet above and [Tables](#tables)). `writeDocContent` still refuses a `ContentTable` block found inside a table cell's own blocks: it has no depth-aware sgc-1/sgc-5 grpprl encoding for anything past depth 1, and writing one incorrectly would silently corrupt the nested table's own structure rather than fail loudly.
|
|
42
|
-
| **Floating/anchored drawn objects, text boxes, and every picture format beyond JPEG/PNG** | An inline picture (`U+0001`) round-trips when its own blip is `OfficeArtBlipJPEG`/`OfficeArtBlipPNG` (see the read-side Inline pictures bullet above, `writeDocContent`'s own `Data`-stream bullet, and [Images](#images) — `ExaDev/documents.js#971`). A floating or anchored drawn object (`U+0008`, `PlcfSpa`) is a genuinely different structure — an arbitrary `OfficeArtSpContainer` shape (which may or may not be a picture at all), reached through a document-level drawing tree (`OfficeArtDgContainer`) rather than a single inline blip — and is not resolved on read, nor written: `document-schema.js` has no block kind for a floating/anchored image distinct from an inline `ContentImageBlock` in the first place. A text box rides the identical drawing layer (`PlcfTxbxTxt`, itself anchored via a drawn object), so it is likewise not read or written. `ContentImageBlock.format`'s `svg`/`gif` members, and any blip format other than JPEG/PNG on read (a WMF/EMF/PICT metafile, a raw DIB, TIFF), have no `OfficeArtBlip` type this package's own writer can fabricate or its own reader can decode, so `readInlinePicture` returns `undefined` for one rather than mis-decoding it, and `writeDocContent` throws `DocUnsupportedError` naming the unwritable format rather than fabricating a blip its own reader could not read back.
|
|
43
|
-
| **A style's own formatting on write** | A style's own property sets (`STD.grLPUpxSw`) are resolved and folded on read ([ExaDev/documents.js#1005](https://github.com/ExaDev/documents.js/issues/1005)): a paragraph's formatting is its style's own grpprlPapx/grpprlChpx (walked up the full `istdBase` inheritance chain, most-specific style winning) plus its own direct exceptions on top, and a run's own referenced character style (`sprmCIstd`) folds in the same way. `writeDocContent` mints a real STSH entry for every distinct `styleId`/`headingLevel` a document's paragraphs use ([ExaDev/documents.js#1059](https://github.com/ExaDev/documents.js/issues/1059)), so style IDENTITY round-trips -- but every minted entry carries an empty `grLPUpxSw` (no formatting of its own), since doc-codec's flat `ContentDocument` has no style-vs-direct-formatting split to draw a real one from: every property this writer emits is already, unconditionally, a direct exception. A style's own inherited formatting therefore never round-trips through a write -- only its name and istd position do.
|
|
44
|
-
| **Table and numbering style formatting** | `STD.grLPUpxSw` is resolved only for paragraph and character styles (`stk` 1/2, see the row above) -- a table style's `StkTableGRLPUPX` (its own `TAPX` plus `CNFC`-conditional per-region formatting, [MS-DOC] 2.4.6.6's own table-specific steps 6-9) and a numbering style's `StkListGRLPUPX` are neither read nor written. A table applying a named table style, or a paragraph using a numbering style rather than direct list formatting, reports none of the formatting either would supply.
|
|
45
|
-
| **
|
|
46
|
-
| **Extended and user-defined document properties** | `title`/`subject`/`author`/`keywords`/`createdIso`/`modifiedIso` are read from and written to a `"\x05SummaryInformation"` stream when present (see [Metadata](#metadata)); the sibling `"\x05DocumentSummaryInformation"` stream (company, manager, and custom user-defined properties) is not read or written at all.
|
|
47
|
-
| **Encryption** | RC4-encrypted ([MS-DOC] 2.2.6.2, ExaDev/documents.js#1113) and XOR-obfuscated (2.2.6.1, #922) documents are both decrypted given a password -- `readDocContent`/`readDocStreams` take an optional `password`, verified against the Table stream's own EncryptionHeader for RC4, or against `FibBase.lKey` directly for XOR obfuscation (see [Encryption](#encryption) below). RC4 CryptoAPI (a different, newer EncryptionHeader shape) still refuses with a `DocUnsupportedError` rather than being read as plaintext or misread as the wrong scheme. `writeDocContent` never encrypts.
|
|
48
|
-
| **`sprmPHugePapx` / `sprmPTableProps`** | Paragraph properties stored indirectly in the Data stream are not followed, so such a paragraph reads with fewer properties than it states. [MS-DOC] 2.4.3's own Overview of Tables text names `sprmPTableProps` as a real, legal alternative to `sprmTDefTable` some applications process — but a real producer's row mark is not shown to prefer it: a genuine LibreOffice-authored `.doc` table's own row mark states its TAP through the identical direct `sprmTDefTable` this package's reader and writer already use (confirmed by parsing a LibreOffice 26.2.5.2-authored table's raw `PapxFkp` bytes; see [ExaDev/documents.js#892](https://github.com/ExaDev/documents.js/issues/892)), matching 2.4.3's own compatibility guidance ("An application SHOULD use sprmTDefTable to define table cells for applications that do not process sprmPTableProps"). `writeDocContent` never writes an indirect Papx.
|
|
49
|
-
| **Hyperlinks and fields** |
|
|
50
|
-
| **Every FIB field beyond what this package's own reader needs** | `writeDocContent` populates only the fc/lcb pairs its own reader consults (the style sheet, the two property bin tables, the Clx, the font table). Roughly 140 other `FibRgFcLcb97` pairs — `SttbfAssoc`, `Dop`, the printer-driver structures among them — are left zero, which is the format's own "undefined, MUST be ignored" contract for most of them, but not a certification that every third-party [MS-DOC] reader accepts the result; see `fib/write.ts`'s own note.
|
|
39
|
+
| Absent | Consequence |
|
|
40
|
+
| ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
41
|
+
| **Nested tables, on write** | `readDocContent` recurses into a table nested inside a table cell to whatever depth the file states (see the read-side Tables bullet above and [Tables](#tables)). `writeDocContent` still refuses a `ContentTable` block found inside a table cell's own blocks: it has no depth-aware sgc-1/sgc-5 grpprl encoding for anything past depth 1, and writing one incorrectly would silently corrupt the nested table's own structure rather than fail loudly. |
|
|
42
|
+
| **Floating/anchored drawn objects, text boxes, and every picture format beyond JPEG/PNG** | An inline picture (`U+0001`) round-trips when its own blip is `OfficeArtBlipJPEG`/`OfficeArtBlipPNG` (see the read-side Inline pictures bullet above, `writeDocContent`'s own `Data`-stream bullet, and [Images](#images) — `ExaDev/documents.js#971`). A floating or anchored drawn object (`U+0008`, `PlcfSpa`) is a genuinely different structure — an arbitrary `OfficeArtSpContainer` shape (which may or may not be a picture at all), reached through a document-level drawing tree (`OfficeArtDgContainer`) rather than a single inline blip — and is not resolved on read, nor written: `document-schema.js` has no block kind for a floating/anchored image distinct from an inline `ContentImageBlock` in the first place. A text box rides the identical drawing layer (`PlcfTxbxTxt`, itself anchored via a drawn object), so it is likewise not read or written. `ContentImageBlock.format`'s `svg`/`gif` members, and any blip format other than JPEG/PNG on read (a WMF/EMF/PICT metafile, a raw DIB, TIFF), have no `OfficeArtBlip` type this package's own writer can fabricate or its own reader can decode, so `readInlinePicture` returns `undefined` for one rather than mis-decoding it, and `writeDocContent` throws `DocUnsupportedError` naming the unwritable format rather than fabricating a blip its own reader could not read back. |
|
|
43
|
+
| **A style's own formatting on write** | A style's own property sets (`STD.grLPUpxSw`) are resolved and folded on read ([ExaDev/documents.js#1005](https://github.com/ExaDev/documents.js/issues/1005)): a paragraph's formatting is its style's own grpprlPapx/grpprlChpx (walked up the full `istdBase` inheritance chain, most-specific style winning) plus its own direct exceptions on top, and a run's own referenced character style (`sprmCIstd`) folds in the same way. `writeDocContent` mints a real STSH entry for every distinct `styleId`/`headingLevel` a document's paragraphs use ([ExaDev/documents.js#1059](https://github.com/ExaDev/documents.js/issues/1059)), so style IDENTITY round-trips -- but every minted entry carries an empty `grLPUpxSw` (no formatting of its own), since doc-codec's flat `ContentDocument` has no style-vs-direct-formatting split to draw a real one from: every property this writer emits is already, unconditionally, a direct exception. A style's own inherited formatting therefore never round-trips through a write -- only its name and istd position do. |
|
|
44
|
+
| **Table and numbering style formatting** | `STD.grLPUpxSw` is resolved only for paragraph and character styles (`stk` 1/2, see the row above) -- a table style's `StkTableGRLPUPX` (its own `TAPX` plus `CNFC`-conditional per-region formatting, [MS-DOC] 2.4.6.6's own table-specific steps 6-9) and a numbering style's `StkListGRLPUPX` are neither read nor written. A table applying a named table style, or a paragraph using a numbering style rather than direct list formatting, reports none of the formatting either would supply. |
|
|
45
|
+
| **Note references and separators, on write; text boxes, on either side** | Footnote/endnote/comment BODIES and full header/footer story block flow are now written (see [Writing](#writing)'s stories paragraph and `subdocument-write.ts`) — but the REFERENCE side is not: `PlcffndRef`/`PlcfandRef`/`PlcfendRef` and the `U+0002`/`U+0005` reference characters in the main document have no model to write from (the reader drops the anchors and keys note bodies by document-order ordinals), so a written note story carries no reference and a real producer renders it as unreferenced content rather than a live footnote — LibreOffice opens such a file without error and resolves its header/footer stories fully, while ignoring the unreferenced note stories. The six footnote/endnote-separator stories `Plcfhdd` leads with are written as genuinely empty stories, matching that neither side models one. Text boxes are not read or written in either direction: `PlcfTxbxTxt` divides its own subdocument into stories the identical way the note-text plexes above do, but a text box's own anchor rides the drawn-object mechanism ([Images](#images)'s own scope note), which this package does not resolve. |
|
|
46
|
+
| **Extended and user-defined document properties** | `title`/`subject`/`author`/`keywords`/`createdIso`/`modifiedIso` are read from and written to a `"\x05SummaryInformation"` stream when present (see [Metadata](#metadata)); the sibling `"\x05DocumentSummaryInformation"` stream (company, manager, and custom user-defined properties) is not read or written at all. |
|
|
47
|
+
| **Encryption** | RC4-encrypted ([MS-DOC] 2.2.6.2, ExaDev/documents.js#1113) and XOR-obfuscated (2.2.6.1, #922) documents are both decrypted given a password -- `readDocContent`/`readDocStreams` take an optional `password`, verified against the Table stream's own EncryptionHeader for RC4, or against `FibBase.lKey` directly for XOR obfuscation (see [Encryption](#encryption) below). RC4 CryptoAPI (a different, newer EncryptionHeader shape) still refuses with a `DocUnsupportedError` rather than being read as plaintext or misread as the wrong scheme. `writeDocContent` never encrypts. |
|
|
48
|
+
| **`sprmPHugePapx` / `sprmPTableProps`** | Paragraph properties stored indirectly in the Data stream are not followed, so such a paragraph reads with fewer properties than it states. [MS-DOC] 2.4.3's own Overview of Tables text names `sprmPTableProps` as a real, legal alternative to `sprmTDefTable` some applications process — but a real producer's row mark is not shown to prefer it: a genuine LibreOffice-authored `.doc` table's own row mark states its TAP through the identical direct `sprmTDefTable` this package's reader and writer already use (confirmed by parsing a LibreOffice 26.2.5.2-authored table's raw `PapxFkp` bytes; see [ExaDev/documents.js#892](https://github.com/ExaDev/documents.js/issues/892)), matching 2.4.3's own compatibility guidance ("An application SHOULD use sprmTDefTable to define table cells for applications that do not process sprmPTableProps"). `writeDocContent` never writes an indirect Papx. |
|
|
49
|
+
| **Hyperlinks and fields** | Fields are resolved, not modelled: everything between a field-begin (`U+0013`) and a field-separator (`U+0014`) is the instruction and is dropped; the result between the separator and the field-end (`U+0015`) is kept as ordinary run text (see [What is converted](#what-is-converted)) — and that resolved result text is what `writeDocContent` writes, so the field layer genuinely has nothing left to invert: no field structure exists in the model, and a field's displayed content round-trips as the plain runs it already read as. A hyperlink is a field in [MS-DOC] plus two further unimplemented layers — the field-begin character is tracked by a `Plcfld` in the Table stream, and the URL itself lives in a [MS-OSHARED] hyperlink property set whose `dwApp` names an index into that `Plcfld` ([MS-DOC] 2.4.7's own association rule) — none of which this package reads or writes in either direction, so a `ContentRun.hyperlink` a caller hands the writer is dropped (its text preserved, its URL lost) rather than approximated: writing a bare `HYPERLINK` field without the property layer would emit bytes this package's own reader cannot read back — the round-trip bar every other property this writer emits is held to — and even LibreOffice's own `.doc` export drops the URL of an ODT hyperlink entirely (its re-exported text carries no link at all), so there is no producer spelling to mirror that this package's own model could verify. Reference characters (footnote/comment/annotation anchors) are likewise dropped as plain text or structure, per the read-side rule. |
|
|
50
|
+
| **Every FIB field beyond what this package's own reader needs** | `writeDocContent` populates only the fc/lcb pairs its own reader consults (the style sheet, the two property bin tables, the Clx, the font table, the numbering tables, and the four story plexes when the document carries stories). Roughly 140 other `FibRgFcLcb97` pairs — `SttbfAssoc`, `Dop`, the printer-driver structures among them — are left zero, which is the format's own "undefined, MUST be ignored" contract for most of them, but not a certification that every third-party [MS-DOC] reader accepts the result; see `fib/write.ts`'s own note. |
|
|
51
51
|
|
|
52
52
|
One construct is refused rather than mis-read: a `sprmPChgTabs` whose `cb` is the `255` sentinel encodes its own length as a formula over tab-stop counts this package does not parse, and its length is needed to find the next `Prl`. Rather than guess and silently mis-read every property after it, `operandSize` throws.
|
|
53
53
|
|
|
@@ -170,7 +170,7 @@ Only page size and the four margins are resolved, matching what `document-schema
|
|
|
170
170
|
|
|
171
171
|
## Subdocuments
|
|
172
172
|
|
|
173
|
-
The main document is only the first of several logical text ranges [MS-DOC] 2.4.1 concatenates into one CP space, in this fixed order: the main document (`0` to `ccpText`), the footnote document (`ccpFtn` characters), the header document (`ccpHdd`), the comment/annotation document (`ccpAtn`), the endnote document (`ccpEdn`), and the textbox documents (`ccpTxbx`/`ccpHdrTxbx`, not read at all — see below). `subdocument.ts`'s `readSubdocumentStories` is the one function every subdocument this package reads goes through: it reads a subdocument's own CP range via the identical `readTextRange`/`readParagraphs` pipeline the main document uses, then splits the resulting flat paragraph sequence by a boundary plex's own CPs (`text/paragraphs.ts`'s `splitEntriesByBoundaries`) into one group per story. `PlcffndTxt`/`PlcfandTxt`/`PlcfendTxt`/`Plcfhdd` all share the identical shape and the identical two conventions this one function handles once: "the story is considered empty" when two consecutive CPs are equal (skipped past, never assigned any entry — a run of several empty stories in a row is handled correctly, not just a single one), and a trailing CP the structure's own page states is "undefined and MUST be ignored" (the last group `readSubdocumentStories` produces is always dropped). A non-empty story's own final paragraph
|
|
173
|
+
The main document is only the first of several logical text ranges [MS-DOC] 2.4.1 concatenates into one CP space, in this fixed order: the main document (`0` to `ccpText`), the footnote document (`ccpFtn` characters), the header document (`ccpHdd`), the comment/annotation document (`ccpAtn`), the endnote document (`ccpEdn`), and the textbox documents (`ccpTxbx`/`ccpHdrTxbx`, not read at all — see below). `subdocument.ts`'s `readSubdocumentStories` is the one function every subdocument this package reads goes through: it reads a subdocument's own CP range via the identical `readTextRange`/`readParagraphs` pipeline the main document uses, then splits the resulting flat paragraph sequence by a boundary plex's own CPs (`text/paragraphs.ts`'s `splitEntriesByBoundaries`) into one group per story. `PlcffndTxt`/`PlcfandTxt`/`PlcfendTxt`/`Plcfhdd` all share the identical shape and the identical two conventions this one function handles once: "the story is considered empty" when two consecutive CPs are equal (skipped past, never assigned any entry — a run of several empty stories in a row is handled correctly, not just a single one), and a trailing CP the structure's own page states is "undefined and MUST be ignored" (the last group `readSubdocumentStories` produces is always dropped). A non-empty story's own final paragraph is dropped only when it is a bare paragraph mark with no content of its own — the guard "not considered part of the story contents" — because the two legal spellings differ exactly there: a header story ends with its content's own final mark PLUS that guard ([MS-DOC]'s Headers page: "if the story contents require a paragraph mark themselves, a second paragraph mark MUST be used"), while a real producer's note stories end at their own content mark with no second one (confirmed against a LibreOffice-authored `.doc`, and matching PlcffndTxt's own "The range of text MUST end in character 0x0D immediately before the next CP", which that content mark already satisfies). An earlier unconditional drop of every story's final paragraph read the second spelling as though it were the first, losing a single-paragraph note entirely and a multi-paragraph note's last paragraph.
|
|
174
174
|
|
|
175
175
|
**Footnotes, endnotes, and comments** (`notes.ts`) resolve to `DocContent.footnotes`/`.endnotes`/`.comments`: one `{id, text}` entry per story, `id` a synthetic one-based ordinal in document order (this reader does not resolve `PlcffndRef`/`PlcfandRef`/`PlcfendRef`'s own reference data — the auto-numbering/custom-symbol state a real footnote mark carries — only the order its own text plex already states), `text` the story's own paragraphs joined by `\n`. This is a deliberate simplification matching `ooxml.js`'s own `DocxDocument.footnotes`/`.endnotes`/`.comments`, which carry the identical flat-text shape for the identical constructs rather than real block flow.
|
|
176
176
|
|
|
@@ -178,7 +178,7 @@ The main document is only the first of several logical text ranges [MS-DOC] 2.4.
|
|
|
178
178
|
|
|
179
179
|
**Not read at all: text boxes.** `PlcfTxbxTxt`/`PlcfHdrtxbxTxt` divide their own subdocument into stories the identical way, but a text box's own anchor is a drawn object (`U+0008`, `PlcfSpa`) — the same drawing-layer mechanism [Images](#images) below does not resolve — so there is no way to reach a text box's own story from the main document's text at all without that layer, and this package does not attempt it.
|
|
180
180
|
|
|
181
|
-
|
|
181
|
+
This subdocument side IS written, symmetrically with the read: `writeDocContent`'s input is widened by the same four fields `DocContent` carries (`WritableDocContent`, each optional — a plain `ContentDocument` writes no subdocument at all), and `subdocument-write.ts` appends the footnote, header, comment, and endnote subdocuments after the main document's own text in [MS-DOC]'s own concatenation order, each with its genuine boundary plex and its `ccp` in the FIB. See [Writing](#writing)'s stories paragraph for the layout facts and the one deliberate absence (note references).
|
|
182
182
|
|
|
183
183
|
## Images
|
|
184
184
|
|
|
@@ -227,16 +227,20 @@ XOR obfuscation (Method 2) needs `archive-codec`'s own `createXorObfuscationKey`
|
|
|
227
227
|
|
|
228
228
|
## Writing
|
|
229
229
|
|
|
230
|
-
`writeDocContent` takes a `'wordprocessing'` `ContentDocument` with one or more sections, an optional `WriteDocContentOptions`, and produces real [MS-DOC] bytes wrapped in a real [MS-CFB] compound file, inverting every read-side structure listed above: a real piece table (`text/piece-table-write.ts`, always one uncompressed 16-bit piece — see [Why always uncompressed](#why-the-writer-always-writes-uncompressed-text)), `Sprm`-encoded grpprls for each run's and paragraph's own direct formatting (`prop/chp-write.ts`, `prop/pap-write.ts`), `ChpxFkp`/`PapxFkp` pages packed and split across as many 512-byte pages as the content needs (`prop/fkp-write.ts`), a real style sheet minting one entry per distinct `styleId`/`headingLevel` a document's paragraphs actually use (`style/stsh.ts`'s `buildStshForStyles`, [ExaDev/documents.js#1059](https://github.com/ExaDev/documents.js/issues/1059) — identity only, no formatting of a style's own; `FibRgFcLcb97.lcbStshf` "MUST be a nonzero value", so a document is never written without one, even one carrying zero styles, though this package's own reader tolerates a missing one), a font table when at least one run names a font (`style/fonts.ts`), a real `PlcfSed`/`Sepx` array with one entry per section (`prop/sep-write.ts`'s `buildPlcfSed`, `ExaDev/documents.js#971` — see [Sections](#sections)), and a `"Data"` stream carrying every inline picture's own real bytes when the document has at least one (`pictures-write.ts`'s `buildInlinePicture`, `data-stream.ts`'s `DataStreamBuilder`, `ExaDev/documents.js#971` — see [Images](#images)). `WriteDocContentOptions.onWarning`, when given, is called for a non-fatal write-time degradation this writer chooses over throwing — today, only the lost-boundary fallback's own per-row budget check (see [Tables](#tables)'s own note on [ExaDev/documents.js#1013](https://github.com/ExaDev/documents.js/issues/1013)) — but that is not a guarantee the write itself goes on to succeed: a row whose own assigned lost boundaries cannot be trimmed down to a split that fits at all still reports one warning, describing its boundaries as unrecoverable and its fully-unsplit encoding as the fallback being attempted — and, should that unsplit encoding also overflow the row's own byte budget once `buildPapxPages` actually packs it, `writeDocContent` can still throw further down the same pipeline, after any rows still to come have had their own chance to report a warning: a caller can genuinely see this warning followed by a hard failure, but not necessarily right away, and not necessarily for the row whose warning it just read, since `buildPapxPages` packs paragraphs in document order and can fail first on an earlier row that also overflowed. Every genuine refusal this writer makes outright — the kinds named in the table below — still throws `DocFormatError`/`DocUnsupportedError` directly, with no warning first.
|
|
230
|
+
`writeDocContent` takes a `'wordprocessing'` `ContentDocument` with one or more sections, an optional `WriteDocContentOptions`, and produces real [MS-DOC] bytes wrapped in a real [MS-CFB] compound file, inverting every read-side structure listed above: a real piece table (`text/piece-table-write.ts`, always one uncompressed 16-bit piece — see [Why always uncompressed](#why-the-writer-always-writes-uncompressed-text)), `Sprm`-encoded grpprls for each run's and paragraph's own direct formatting (`prop/chp-write.ts`, `prop/pap-write.ts`), `ChpxFkp`/`PapxFkp` pages packed and split across as many 512-byte pages as the content needs (`prop/fkp-write.ts`), a real style sheet minting one entry per distinct `styleId`/`headingLevel` a document's paragraphs actually use (`style/stsh.ts`'s `buildStshForStyles`, [ExaDev/documents.js#1059](https://github.com/ExaDev/documents.js/issues/1059) — identity only, no formatting of a style's own; `FibRgFcLcb97.lcbStshf` "MUST be a nonzero value", so a document is never written without one, even one carrying zero styles, though this package's own reader tolerates a missing one), a font table when at least one run names a font (`style/fonts.ts`), a real `PlcfSed`/`Sepx` array with one entry per section (`prop/sep-write.ts`'s `buildPlcfSed`, `ExaDev/documents.js#971` — see [Sections](#sections)), the four story subdocuments and their boundary plexes when the input carries stories (`subdocument-write.ts` — see the stories paragraph below and [Subdocuments](#subdocuments)), and a `"Data"` stream carrying every inline picture's own real bytes when the document has at least one (`pictures-write.ts`'s `buildInlinePicture`, `data-stream.ts`'s `DataStreamBuilder`, `ExaDev/documents.js#971` — see [Images](#images)). The input is a `WritableDocContent` — a `ContentDocument` widened by the same optional story fields `DocContent` reads (see the stories paragraph below) — so a plain `ContentDocument` still writes exactly as it always did. `WriteDocContentOptions.onWarning`, when given, is called for a non-fatal write-time degradation this writer chooses over throwing — today, only the lost-boundary fallback's own per-row budget check (see [Tables](#tables)'s own note on [ExaDev/documents.js#1013](https://github.com/ExaDev/documents.js/issues/1013)) — but that is not a guarantee the write itself goes on to succeed: a row whose own assigned lost boundaries cannot be trimmed down to a split that fits at all still reports one warning, describing its boundaries as unrecoverable and its fully-unsplit encoding as the fallback being attempted — and, should that unsplit encoding also overflow the row's own byte budget once `buildPapxPages` actually packs it, `writeDocContent` can still throw further down the same pipeline, after any rows still to come have had their own chance to report a warning: a caller can genuinely see this warning followed by a hard failure, but not necessarily right away, and not necessarily for the row whose warning it just read, since `buildPapxPages` packs paragraphs in document order and can fail first on an earlier row that also overflowed. Every genuine refusal this writer makes outright — the kinds named in the table below — still throws `DocFormatError`/`DocUnsupportedError` directly, with no warning first.
|
|
231
231
|
|
|
232
232
|
Character properties this writer converts, the exact inverse of [What is converted](#what-is-converted)'s character table above: `bold`, `italic`, `strike`, `underline` (as `kulSingle`, the only style a plain boolean can express), `sizePt`, `color` (via `sprmCCv`'s exact `COLORREF`, never the lossy 17-entry `sprmCIco` palette), and `fontFamily`. Paragraph properties: `alignment` (the four `ST_Jc`-aligned values this package's reader itself maps — `left`/`center`/`right`/`justify`), `indentLeftPt`, `indentRightPt`, `indentFirstLinePt`, `spacingBeforePt`, `spacingAfterPt`, `lineSpacing` (only `LSPD`'s multiplier form, matching the reader), `pageBreakBefore`, `styleId`/`headingLevel` (via the minted STSH entry above), and `list` membership (`sprmPIlfo`/`sprmPIlvl` — see [Numbering definitions](#numbering-definitions) for the numbering tables this depends on).
|
|
233
233
|
|
|
234
|
+
**A `pageBreak` block is written as the format's own manual page break** — the end-of-section character (0x000C) placed where no section ends, the exact shape [MS-DOC]'s own PlcfSed.aCP text defines: "An end-of-section character (0x0C) which occurs at a CP and which is not the last character in a section specifies a manual page break." The two spellings of that one character are distinguished purely by PlcfSed's own boundaries, so read.ts's `markManualPageBreaks` decodes a 0x000C-terminated paragraph whose end CP is not a following section's start CP into `[paragraph, pageBreak]`, and `table/write.ts`'s `appendPageBreak` writes the inverse: a page break retargets the preceding paragraph's own terminator from an ordinary paragraph mark to 0x000C, which is why the ordinary case (`[paragraph alpha, pageBreak, paragraph beta]`) round-trips exactly with no stray empty paragraph — the break rides a paragraph terminator the format already requires there. A page break with no ordinary paragraph before it to carry it (a section's first block, directly after a table's row mark, or directly after another page break) becomes its own empty 0x000C-terminated paragraph instead, visible in the round trip as a `[paragraph "", pageBreak]` pair the input did not state: a page break in [MS-DOC] always terminates SOME paragraph, so a modelled break with nothing before it necessarily mints one — a faithful statement of the format's own limit rather than a loss. Verified against a real, independent [MS-DOC] implementation: LibreOffice opens a `writeDocContent` sample carrying a page break between two paragraphs and re-exports it with a genuine `fo:break-before="page"` on the paragraph after the break, both paragraphs' text intact.
|
|
235
|
+
|
|
236
|
+
**Story subdocuments are written, symmetrically with the read** — `writeDocContent`'s input is widened from a bare `ContentDocument` to a `WritableDocContent`: the same four story fields `readDocContent`'s own `DocContent` output carries (`footnotes`/`endnotes`/`comments` as `{id, text}` bodies, `headerFooterStories` as real block flow), each optional, exactly the input shape `ooxml.js`'s own `DocxContent` established for the identical constructs — so a genuine `DocContent` assigns straight across and a plain `ContentDocument` writes no subdocument at all. `subdocument-write.ts` appends the footnote, header, comment, and endnote subdocuments after the main document's own text in [MS-DOC]'s own concatenation order, states each one's `ccp` in the FIB, and writes each one's genuine CP-only boundary plex (`PlcffndTxt`/`PlcfHdd`/`PlcfandTxt`/`PlcfendTxt`). Every layout fact is the one a real producer writes, each confirmed against a LibreOffice-authored `.doc` before being implemented: each non-empty story's span ends with its content's own final mark plus the one guard mark [MS-DOC]'s Headers page mandates, the subdocument carries exactly one further paragraph mark beyond the last story (what makes each plex's own "second-to-last CP MUST be equal to ccp - 1" satisfiable), note text is split into paragraphs at the same newlines the reader joined on (so a note whose text ends in `\n` round-trips exactly, its trailing empty paragraph protected by the writer's separate guard), a header story flattens through the identical `flattenSectionBlocks` the main document uses (tables and inline pictures included, sharing the one `"Data"` stream), an absent slot stays a genuinely empty story — the "reuse the previous section's header/footer of this kind" meaning the reader gives one — while a story the model carries is written as present even when its blocks flatten to nothing, and a story naming a section the document does not have, or a second story for one slot, throws `DocFormatError`. The one deliberate absence is the REFERENCE side: `PlcffndRef`/`PlcfandRef`/`PlcfendRef` and the `U+0002`/`U+0005` reference characters in the main document are not written, because the model carries no reference positions to write them from (the reader drops the anchors; note `id`s are document-order ordinals) — a written note story is honestly unreferenced content rather than a fake live footnote. Verified against a real, independent [MS-DOC] implementation: LibreOffice opens a `writeDocContent` sample carrying footnotes, endnotes, comments, a header, and a footer without error, resolves the header and footer stories in full (their text appears in the re-exported master page's `style:header`/`style:footer`), and ignores the unreferenced note stories; a full read→write→read cycle over a LibreOffice-authored `.doc` reproduces every note body and every (section, slot) story exactly.
|
|
237
|
+
|
|
234
238
|
**Deliberately not handled**, beyond what the read-side scope table above already states applies to both directions:
|
|
235
239
|
|
|
236
|
-
| Absent | Consequence
|
|
237
|
-
| ---------------------------------------------- |
|
|
238
|
-
| **Non-paragraph, non-table, non-image blocks** |
|
|
239
|
-
| **An empty `ContentSection.blocks`** | Written as a single paragraph with no runs — [MS-DOC] 2.4.2 requires the Main Document's own text to end in a paragraph mark, so an otherwise-empty section still needs one to hold it, exactly as a real producer's own blank document has one.
|
|
240
|
+
| Absent | Consequence |
|
|
241
|
+
| ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
242
|
+
| **Non-paragraph, non-table, non-image blocks** | An `embeddedObject` or construct-boundary marker (`constructStart`/`constructEnd`) block throws `DocUnsupportedError` naming its own `kind` — `ContentTable`, `ContentImageBlock`, and `pageBreak` are now written (see [Tables](#tables), [Images](#images), and the page-break paragraph below); a non-paragraph block found inside one of a table's own cells throws the identical error. An embedded object rides the same `PlcfSpa`/`OfficeArtDgContainer` drawing-tree mechanism this package's reader does not resolve at all (see the drawn-objects row above); a construct-boundary marker has no [MS-DOC] representation of its own to target until the cross-format `document-schema.js` design work tracked on [ExaDev/documents.js#1122](https://github.com/ExaDev/documents.js/issues/1122) lands. |
|
|
243
|
+
| **An empty `ContentSection.blocks`** | Written as a single paragraph with no runs — [MS-DOC] 2.4.2 requires the Main Document's own text to end in a paragraph mark, so an otherwise-empty section still needs one to hold it, exactly as a real producer's own blank document has one. |
|
|
240
244
|
|
|
241
245
|
### Why the writer always writes uncompressed text
|
|
242
246
|
|
|
@@ -291,6 +295,7 @@ The modules layer in the order [MS-DOC]'s own algorithms chain:
|
|
|
291
295
|
| `src/pictures-write.ts` | The inverse of `pictures.ts`: a `ContentImageBlock` to its own `PICF`/`OfficeArtInlineSpContainer`/blip bytes and `sprmCPicLocation` grpprl (see [Images](#images)). |
|
|
292
296
|
| `src/data-stream.ts` | Accumulates every inline picture's own bytes across a whole write into one real `"Data"` stream, handing each one back the offset it landed at. |
|
|
293
297
|
| `src/prop/sep-write.ts` | The inverse of `prop/sep.ts`: a document's own sections to a real `PlcfSed`/`Sepx` array, one entry per section (see [Sections](#sections)). |
|
|
298
|
+
| `src/subdocument-write.ts` | The inverse of `subdocument.ts`/`notes.ts`/`headers-footers.ts`: the footnote, header, comment, and endnote subdocuments and their CP-only boundary plexes (see [Subdocuments](#subdocuments)). |
|
|
294
299
|
| `src/write.ts` | The whole write chain, from a `ContentDocument` to real [MS-DOC] bytes in a real [MS-CFB] compound file. |
|
|
295
300
|
|
|
296
301
|
### Why the piece table gets the most attention
|
|
@@ -343,7 +348,7 @@ Every structure is tested against bytes hand-assembled from [MS-DOC]'s own field
|
|
|
343
348
|
|
|
344
349
|
The writer is verified the opposite way: `src/write.test.ts` reads every document `writeDocContent` produces back through this package's own `readDocContent`, including cases that force `ChpxFkp`/`PapxFkp` page-splitting (150 distinctly-formatted runs, 60 distinctly-indented paragraphs) rather than relying only on the common one-page case, a dedicated `describe("writeDocContent numbering")` block covering a multi-level list's own `format`/`text` per level, a bulleted list's glyph, separate-`ilfo` minting for separate `numId`s, a no-lists document writing no numbering tables at all, and a list membership inside a table cell (see [Numbering definitions](#numbering-definitions)), and a dedicated `describe("writeDocContent tables")` block covering row/column/cell round-tripping, a multi-paragraph cell, row height, a horizontally merged cell's `colSpan`, a vertically merged cell's `rowSpan`, the lost-boundary fallback recovering `colSpan`/`columnWidthsPt` for a single-row merge and for a multi-row table that merges the identical boundary in every row ([ExaDev/documents.js#992](https://github.com/ExaDev/documents.js/issues/992)), that same fallback's own per-row budget check degrading gracefully via `onWarning` instead of throwing once a row's assigned split would overflow its row-ending mark -- a two-row table sitting exactly at the 21-physical-cell ceiling versus one column past it (only the over-budget row trims, so the other row's `#992` recovery survives, and the trimmed row itself still recovers all but one of its own assigned boundaries rather than losing all of them), a single-row table one column past that same ceiling recovering all but one of its own boundaries the identical way with no sibling row to share the work with, and a single-row table whose full assigned split would need 64 physical cells -- one past `TDefTableOperand`'s own hard `NumberOfColumns` ceiling ([MS-DOC] 2.9.321's own "MUST NOT exceed 63", not 2.4.3's separate "between 1 and 63 table cells" limit) -- trimming down to the row-ending mark's own byte-budget ceiling instead of throwing the way this writer used to before that check ran ahead of `encodeTableRowGrpprl`'s own unconditional throw past it ([ExaDev/documents.js#1013](https://github.com/ExaDev/documents.js/issues/1013)), the nested-table refusal, and a `describe("cell decoration")` block round-tripping a solid background fill, a genuine two-colour pattern fill (a percentage grey, a crosshatch -- ExaDev/documents.js#951), all four borders at different styles/widths/colours, a partially bordered cell, a cell with no decoration at all (which must emit none), decoration on a merged cell, and a colour the `Ico` palette cannot hold. `src/table/decoration.test.ts` covers the same vocabulary one layer down, against bytes hand-built from the specification's own field tables -- including every encoding this package's own writer never emits, which a round trip therefore cannot reach: both no-border spellings, each `BrcType` family's collapse onto `ContentStrokeStyle`, the art-border and automatic-colour cases, `ipatSolid`, every pattern `IPAT_TO_PATTERN_TYPE` names (percentages and stripe/cross families alike) alongside `ipatNil` and an `ipatPctNew*` value with no `ST_Shd` equivalent, the `DocUnsupportedError` a SpreadsheetML-only pattern name throws on write, `Shd80`, and the second and third shading arrays a row too wide for one `PapxInFkp` record can never exercise end to end. Beyond the committed suite, a `writeDocContent` sample carrying every character and paragraph property this writer supports was opened, rendered, and re-exported by a real, independent [MS-DOC] implementation — LibreOffice — without error or visible content loss, confirming those bytes are genuinely conformant to a reader this package did not write, not merely self-consistent with its own. Table samples were checked the same way and now pass in both directions -- plain, vertically merged, horizontally merged, merged both ways, and decorated with cell fills and per-side borders -- after [ExaDev/documents.js#892](https://github.com/ExaDev/documents.js/issues/892) and [#895](https://github.com/ExaDev/documents.js/issues/895) were each root-caused by comparing this writer's own bytes against a genuine LibreOffice-authored `.doc`; see [Tables](#tables) for the full findings.
|
|
345
350
|
|
|
346
|
-
A `describe("writeDocContent multiple sections")` block (`ExaDev/documents.js#971`) covers two and three sections each round-tripping their own distinct page size and margins independently, and a non-final section whose own last block is a table still closing on a genuine paragraph mark before its end-of-section character, rather than landing on the table's own row-ending mark. A `describe("writeDocContent inline pictures")` block covers a PNG and a JPEG image round-tripping their own raw bytes/format/size, a paragraph's own text splitting around an inline picture into separate blocks, more than one picture landing at distinct offsets in the same `"Data"` stream, an unwritable format (`svg`) throwing `DocUnsupportedError`, and no `"Data"` stream at all being written for a picture-free document.
|
|
351
|
+
A `describe("writeDocContent multiple sections")` block (`ExaDev/documents.js#971`) covers two and three sections each round-tripping their own distinct page size and margins independently, and a non-final section whose own last block is a table still closing on a genuine paragraph mark before its end-of-section character, rather than landing on the table's own row-ending mark. A `describe("writeDocContent page breaks")` block covers the ordinary `[paragraph, pageBreak, paragraph]` round trip (including the terminated paragraph's own formatting surviving), a second consecutive break, a leading break, a break directly after a table (never retargeting the row mark), and a break as a non-final section's last block — alongside `read.test.ts`'s own page-break tests over hand-built bytes, which pin the read-side distinction (a 0x000C at a PlcfSed boundary is a section end and gains no block; one elsewhere is a `pageBreak`) against the specification's own PlcfSed.aCP sentence. The write side is additionally verified against a real, independent [MS-DOC] implementation: LibreOffice opens a written page-break sample and re-exports a genuine `fo:break-before="page"` on the paragraph after the break. A `describe("writeDocContent stories")` block covers footnote/endnote/comment bodies round-tripping as plain text (a note carrying newlines and a trailing empty paragraph included, which is what the writer's separate guard mark exists to protect), a document stating no stories writing no subdocument at all, header/footer stories round-tripping per (section, slot) with absent slots staying absent, a header story carrying a table through the identical table pipeline, a story whose blocks flatten to nothing reading back as present-but-blank, the `DocFormatError` refusals for a story naming a section the document does not have and for two stories sharing a slot, and a full `DocContent` re-writen unchanged — the read side of that last case additionally run over a genuine LibreOffice-authored `.doc` in [Writing](#writing)'s stories paragraph. A `describe("writeDocContent inline pictures")` block covers a PNG and a JPEG image round-tripping their own raw bytes/format/size, a paragraph's own text splitting around an inline picture into separate blocks, more than one picture landing at distinct offsets in the same `"Data"` stream, an unwritable format (`svg`) throwing `DocUnsupportedError`, and no `"Data"` stream at all being written for a picture-free document. The inline-picture block has not itself been checked against a real, independent [MS-DOC] implementation the way [Tables](#tables) and [Numbering definitions](#numbering-definitions) have -- that is the next thing worth doing here, mirroring how each of those was itself first verified by this package's own round trip alone.
|
|
347
352
|
|
|
348
353
|
A real-producer corpus layer exists: `pnpm test:corpus` runs the gitignored `test/corpus/` suite against LibreOffice-produced Word 97 documents generated by `scripts/generate-corpus.mjs` (flat-ODT and packaged-ODT sources spanning paragraphs, runs, headings, lists, tables with merges, images, and sections, converted headlessly through Writer's own export filter). That corpus already paid for itself once: it exposed that the reader's inline-picture locator assumed Word's OfficeArt wrapper nesting, which LibreOffice does not follow, and the locator now validates blip candidates by their payload file signatures instead. What a LibreOffice-produced corpus does not prove remains stated plainly: it is a real application's Word 97 spelling, not Word 1997-2007's own.
|
|
349
354
|
|
package/dist/fib/write.cjs
CHANGED
|
@@ -24,6 +24,10 @@ function buildFib(spec) {
|
|
|
24
24
|
};
|
|
25
25
|
lw(require_fib_offsets.LW_OFFSET.cbMac, spec.cbMac);
|
|
26
26
|
lw(require_fib_offsets.LW_OFFSET.ccpText, spec.ccpText);
|
|
27
|
+
lw(require_fib_offsets.LW_OFFSET.ccpFtn, spec.ccpFtn);
|
|
28
|
+
lw(require_fib_offsets.LW_OFFSET.ccpHdd, spec.ccpHdd);
|
|
29
|
+
lw(require_fib_offsets.LW_OFFSET.ccpAtn, spec.ccpAtn);
|
|
30
|
+
lw(require_fib_offsets.LW_OFFSET.ccpEdn, spec.ccpEdn);
|
|
27
31
|
view.setUint16(152, CB_RG_FC_LCB_WORD_97, true);
|
|
28
32
|
const pair = (index, fc, lcb) => {
|
|
29
33
|
const offset = 154 + index * 4;
|
|
@@ -38,6 +42,10 @@ function buildFib(spec) {
|
|
|
38
42
|
pair(require_fib_offsets.FC_LCB_VALUE_INDEX.fcPlcfSed, spec.fcPlcfSed, spec.lcbPlcfSed);
|
|
39
43
|
pair(require_fib_offsets.FC_LCB_VALUE_INDEX.fcPlfLst, spec.fcPlfLst, spec.lcbPlfLst);
|
|
40
44
|
pair(require_fib_offsets.FC_LCB_VALUE_INDEX.fcPlfLfo, spec.fcPlfLfo, spec.lcbPlfLfo);
|
|
45
|
+
pair(require_fib_offsets.FC_LCB_VALUE_INDEX.fcPlcffndTxt, spec.fcPlcffndTxt, spec.lcbPlcffndTxt);
|
|
46
|
+
pair(require_fib_offsets.FC_LCB_VALUE_INDEX.fcPlcfHdd, spec.fcPlcfHdd, spec.lcbPlcfHdd);
|
|
47
|
+
pair(require_fib_offsets.FC_LCB_VALUE_INDEX.fcPlcfandTxt, spec.fcPlcfandTxt, spec.lcbPlcfandTxt);
|
|
48
|
+
pair(require_fib_offsets.FC_LCB_VALUE_INDEX.fcPlcfendTxt, spec.fcPlcfendTxt, spec.lcbPlcfendTxt);
|
|
41
49
|
if (require_bytes.readUint16LE(bytes, 0) !== 42476) throw new require_errors.DocFormatError("buildFib produced a Fib whose own wIdent does not read back as 0xA5EC; this is an internal defect, not an input error");
|
|
42
50
|
return bytes;
|
|
43
51
|
}
|
package/dist/fib/write.d.cts
CHANGED
|
@@ -20,6 +20,22 @@ interface FibWriteSpec {
|
|
|
20
20
|
readonly lcbPlfLst: number;
|
|
21
21
|
readonly fcPlfLfo: number;
|
|
22
22
|
readonly lcbPlfLfo: number;
|
|
23
|
+
/** The footnote subdocument's own character count, and its PlcffndTxt -- 0/0 when the document carries no footnotes (see subdocument-write.ts). */
|
|
24
|
+
readonly ccpFtn: number;
|
|
25
|
+
readonly fcPlcffndTxt: number;
|
|
26
|
+
readonly lcbPlcffndTxt: number;
|
|
27
|
+
/** The header subdocument's own character count, and its Plcfhdd -- 0/0 when the document carries no header/footer stories. */
|
|
28
|
+
readonly ccpHdd: number;
|
|
29
|
+
readonly fcPlcfHdd: number;
|
|
30
|
+
readonly lcbPlcfHdd: number;
|
|
31
|
+
/** The comment subdocument's own character count, and its PlcfandTxt -- 0/0 when the document carries no comments. */
|
|
32
|
+
readonly ccpAtn: number;
|
|
33
|
+
readonly fcPlcfandTxt: number;
|
|
34
|
+
readonly lcbPlcfandTxt: number;
|
|
35
|
+
/** The endnote subdocument's own character count, and its PlcfendTxt -- 0/0 when the document carries no endnotes. */
|
|
36
|
+
readonly ccpEdn: number;
|
|
37
|
+
readonly fcPlcfendTxt: number;
|
|
38
|
+
readonly lcbPlcfendTxt: number;
|
|
23
39
|
}
|
|
24
40
|
declare function buildFib(spec: FibWriteSpec): Uint8Array<ArrayBuffer>;
|
|
25
41
|
//#endregion
|
package/dist/fib/write.d.ts
CHANGED
|
@@ -20,6 +20,22 @@ interface FibWriteSpec {
|
|
|
20
20
|
readonly lcbPlfLst: number;
|
|
21
21
|
readonly fcPlfLfo: number;
|
|
22
22
|
readonly lcbPlfLfo: number;
|
|
23
|
+
/** The footnote subdocument's own character count, and its PlcffndTxt -- 0/0 when the document carries no footnotes (see subdocument-write.ts). */
|
|
24
|
+
readonly ccpFtn: number;
|
|
25
|
+
readonly fcPlcffndTxt: number;
|
|
26
|
+
readonly lcbPlcffndTxt: number;
|
|
27
|
+
/** The header subdocument's own character count, and its Plcfhdd -- 0/0 when the document carries no header/footer stories. */
|
|
28
|
+
readonly ccpHdd: number;
|
|
29
|
+
readonly fcPlcfHdd: number;
|
|
30
|
+
readonly lcbPlcfHdd: number;
|
|
31
|
+
/** The comment subdocument's own character count, and its PlcfandTxt -- 0/0 when the document carries no comments. */
|
|
32
|
+
readonly ccpAtn: number;
|
|
33
|
+
readonly fcPlcfandTxt: number;
|
|
34
|
+
readonly lcbPlcfandTxt: number;
|
|
35
|
+
/** The endnote subdocument's own character count, and its PlcfendTxt -- 0/0 when the document carries no endnotes. */
|
|
36
|
+
readonly ccpEdn: number;
|
|
37
|
+
readonly fcPlcfendTxt: number;
|
|
38
|
+
readonly lcbPlcfendTxt: number;
|
|
23
39
|
}
|
|
24
40
|
declare function buildFib(spec: FibWriteSpec): Uint8Array<ArrayBuffer>;
|
|
25
41
|
//#endregion
|
package/dist/fib/write.js
CHANGED
|
@@ -23,6 +23,10 @@ function buildFib(spec) {
|
|
|
23
23
|
};
|
|
24
24
|
lw(LW_OFFSET.cbMac, spec.cbMac);
|
|
25
25
|
lw(LW_OFFSET.ccpText, spec.ccpText);
|
|
26
|
+
lw(LW_OFFSET.ccpFtn, spec.ccpFtn);
|
|
27
|
+
lw(LW_OFFSET.ccpHdd, spec.ccpHdd);
|
|
28
|
+
lw(LW_OFFSET.ccpAtn, spec.ccpAtn);
|
|
29
|
+
lw(LW_OFFSET.ccpEdn, spec.ccpEdn);
|
|
26
30
|
view.setUint16(152, CB_RG_FC_LCB_WORD_97, true);
|
|
27
31
|
const pair = (index, fc, lcb) => {
|
|
28
32
|
const offset = 154 + index * 4;
|
|
@@ -37,6 +41,10 @@ function buildFib(spec) {
|
|
|
37
41
|
pair(FC_LCB_VALUE_INDEX.fcPlcfSed, spec.fcPlcfSed, spec.lcbPlcfSed);
|
|
38
42
|
pair(FC_LCB_VALUE_INDEX.fcPlfLst, spec.fcPlfLst, spec.lcbPlfLst);
|
|
39
43
|
pair(FC_LCB_VALUE_INDEX.fcPlfLfo, spec.fcPlfLfo, spec.lcbPlfLfo);
|
|
44
|
+
pair(FC_LCB_VALUE_INDEX.fcPlcffndTxt, spec.fcPlcffndTxt, spec.lcbPlcffndTxt);
|
|
45
|
+
pair(FC_LCB_VALUE_INDEX.fcPlcfHdd, spec.fcPlcfHdd, spec.lcbPlcfHdd);
|
|
46
|
+
pair(FC_LCB_VALUE_INDEX.fcPlcfandTxt, spec.fcPlcfandTxt, spec.lcbPlcfandTxt);
|
|
47
|
+
pair(FC_LCB_VALUE_INDEX.fcPlcfendTxt, spec.fcPlcfendTxt, spec.lcbPlcfendTxt);
|
|
40
48
|
if (readUint16LE(bytes, 0) !== 42476) throw new DocFormatError("buildFib produced a Fib whose own wIdent does not read back as 0xA5EC; this is an internal defect, not an input error");
|
|
41
49
|
return bytes;
|
|
42
50
|
}
|
package/dist/headers-footers.cjs
CHANGED
|
@@ -2,7 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
const require_subdocument = require("./subdocument.cjs");
|
|
3
3
|
const require_table_read = require("./table/read.cjs");
|
|
4
4
|
//#region src/headers-footers.ts
|
|
5
|
-
/** The six per-section slots' own order within each of Plcfhdd's per-section groups, [MS-DOC]'s own Headers page: "The stories within each group MUST appear in the following order." */
|
|
5
|
+
/** The six per-section slots' own order within each of Plcfhdd's per-section groups, [MS-DOC]'s own Headers page: "The stories within each group MUST appear in the following order." Exported because subdocument-write.ts builds Plcfhdd in the identical order -- one source of it, so the two directions cannot drift. */
|
|
6
6
|
const SLOT_ORDER = [
|
|
7
7
|
"evenHeader",
|
|
8
8
|
"oddHeader",
|
|
@@ -11,11 +11,11 @@ const SLOT_ORDER = [
|
|
|
11
11
|
"firstHeader",
|
|
12
12
|
"firstFooter"
|
|
13
13
|
];
|
|
14
|
-
/** The six fixed footnote/endnote-separator stories preceding every section's own six -- footnote separator, footnote continuation separator, footnote continuation notice, endnote separator, endnote continuation separator, endnote continuation notice -- carried by Plcfhdd but not read here, since neither this package's own schema nor ooxml.js's DocxDocument has anywhere to put a separator story. */
|
|
14
|
+
/** The six fixed footnote/endnote-separator stories preceding every section's own six -- footnote separator, footnote continuation separator, footnote continuation notice, endnote separator, endnote continuation separator, endnote continuation notice -- carried by Plcfhdd but not read here, since neither this package's own schema nor ooxml.js's DocxDocument has anywhere to put a separator story. Exported for subdocument-write.ts, which writes the same six slots as genuinely empty stories. */
|
|
15
15
|
const FIXED_SEPARATOR_STORY_COUNT = 6;
|
|
16
16
|
function readHeaderFooterStories(wordDocument, table, pieceTable, context, fib, sectionCount) {
|
|
17
17
|
const headerStartCp = fib.ccpText + fib.ccpFtn;
|
|
18
|
-
const perSectionStories = require_subdocument.readSubdocumentStories(wordDocument, table, pieceTable, context, headerStartCp, fib.ccpHdd, fib.fcPlcfHdd, fib.lcbPlcfHdd, "Plcfhdd").slice(
|
|
18
|
+
const perSectionStories = require_subdocument.readSubdocumentStories(wordDocument, table, pieceTable, context, headerStartCp, fib.ccpHdd, fib.fcPlcfHdd, fib.lcbPlcfHdd, "Plcfhdd").slice(6);
|
|
19
19
|
const stories = [];
|
|
20
20
|
for (let section = 0; section < sectionCount; section += 1) for (let slotIndex = 0; slotIndex < SLOT_ORDER.length; slotIndex += 1) {
|
|
21
21
|
const entries = perSectionStories[section * SLOT_ORDER.length + slotIndex];
|
|
@@ -31,4 +31,6 @@ function readHeaderFooterStories(wordDocument, table, pieceTable, context, fib,
|
|
|
31
31
|
return stories;
|
|
32
32
|
}
|
|
33
33
|
//#endregion
|
|
34
|
+
exports.FIXED_SEPARATOR_STORY_COUNT = FIXED_SEPARATOR_STORY_COUNT;
|
|
35
|
+
exports.SLOT_ORDER = SLOT_ORDER;
|
|
34
36
|
exports.readHeaderFooterStories = readHeaderFooterStories;
|
|
@@ -11,6 +11,10 @@ interface HeaderFooterStory {
|
|
|
11
11
|
readonly blocks: readonly ContentBlock[];
|
|
12
12
|
}
|
|
13
13
|
type HeaderFooterStories = readonly HeaderFooterStory[];
|
|
14
|
+
/** The six per-section slots' own order within each of Plcfhdd's per-section groups, [MS-DOC]'s own Headers page: "The stories within each group MUST appear in the following order." Exported because subdocument-write.ts builds Plcfhdd in the identical order -- one source of it, so the two directions cannot drift. */
|
|
15
|
+
declare const SLOT_ORDER: readonly HeaderFooterSlot[];
|
|
16
|
+
/** The six fixed footnote/endnote-separator stories preceding every section's own six -- footnote separator, footnote continuation separator, footnote continuation notice, endnote separator, endnote continuation separator, endnote continuation notice -- carried by Plcfhdd but not read here, since neither this package's own schema nor ooxml.js's DocxDocument has anywhere to put a separator story. Exported for subdocument-write.ts, which writes the same six slots as genuinely empty stories. */
|
|
17
|
+
declare const FIXED_SEPARATOR_STORY_COUNT = 6;
|
|
14
18
|
declare function readHeaderFooterStories(wordDocument: Uint8Array, table: Uint8Array, pieceTable: PieceTable, context: ReadContext, fib: Fib, sectionCount: number): HeaderFooterStories;
|
|
15
19
|
//#endregion
|
|
16
|
-
export { HeaderFooterSlot, HeaderFooterStories, HeaderFooterStory, readHeaderFooterStories };
|
|
20
|
+
export { FIXED_SEPARATOR_STORY_COUNT, HeaderFooterSlot, HeaderFooterStories, HeaderFooterStory, SLOT_ORDER, readHeaderFooterStories };
|
|
@@ -11,6 +11,10 @@ interface HeaderFooterStory {
|
|
|
11
11
|
readonly blocks: readonly ContentBlock[];
|
|
12
12
|
}
|
|
13
13
|
type HeaderFooterStories = readonly HeaderFooterStory[];
|
|
14
|
+
/** The six per-section slots' own order within each of Plcfhdd's per-section groups, [MS-DOC]'s own Headers page: "The stories within each group MUST appear in the following order." Exported because subdocument-write.ts builds Plcfhdd in the identical order -- one source of it, so the two directions cannot drift. */
|
|
15
|
+
declare const SLOT_ORDER: readonly HeaderFooterSlot[];
|
|
16
|
+
/** The six fixed footnote/endnote-separator stories preceding every section's own six -- footnote separator, footnote continuation separator, footnote continuation notice, endnote separator, endnote continuation separator, endnote continuation notice -- carried by Plcfhdd but not read here, since neither this package's own schema nor ooxml.js's DocxDocument has anywhere to put a separator story. Exported for subdocument-write.ts, which writes the same six slots as genuinely empty stories. */
|
|
17
|
+
declare const FIXED_SEPARATOR_STORY_COUNT = 6;
|
|
14
18
|
declare function readHeaderFooterStories(wordDocument: Uint8Array, table: Uint8Array, pieceTable: PieceTable, context: ReadContext, fib: Fib, sectionCount: number): HeaderFooterStories;
|
|
15
19
|
//#endregion
|
|
16
|
-
export { HeaderFooterSlot, HeaderFooterStories, HeaderFooterStory, readHeaderFooterStories };
|
|
20
|
+
export { FIXED_SEPARATOR_STORY_COUNT, HeaderFooterSlot, HeaderFooterStories, HeaderFooterStory, SLOT_ORDER, readHeaderFooterStories };
|
package/dist/headers-footers.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readSubdocumentStories } from "./subdocument.js";
|
|
2
2
|
import { assembleBlocks } from "./table/read.js";
|
|
3
3
|
//#region src/headers-footers.ts
|
|
4
|
-
/** The six per-section slots' own order within each of Plcfhdd's per-section groups, [MS-DOC]'s own Headers page: "The stories within each group MUST appear in the following order." */
|
|
4
|
+
/** The six per-section slots' own order within each of Plcfhdd's per-section groups, [MS-DOC]'s own Headers page: "The stories within each group MUST appear in the following order." Exported because subdocument-write.ts builds Plcfhdd in the identical order -- one source of it, so the two directions cannot drift. */
|
|
5
5
|
const SLOT_ORDER = [
|
|
6
6
|
"evenHeader",
|
|
7
7
|
"oddHeader",
|
|
@@ -10,11 +10,11 @@ const SLOT_ORDER = [
|
|
|
10
10
|
"firstHeader",
|
|
11
11
|
"firstFooter"
|
|
12
12
|
];
|
|
13
|
-
/** The six fixed footnote/endnote-separator stories preceding every section's own six -- footnote separator, footnote continuation separator, footnote continuation notice, endnote separator, endnote continuation separator, endnote continuation notice -- carried by Plcfhdd but not read here, since neither this package's own schema nor ooxml.js's DocxDocument has anywhere to put a separator story. */
|
|
13
|
+
/** The six fixed footnote/endnote-separator stories preceding every section's own six -- footnote separator, footnote continuation separator, footnote continuation notice, endnote separator, endnote continuation separator, endnote continuation notice -- carried by Plcfhdd but not read here, since neither this package's own schema nor ooxml.js's DocxDocument has anywhere to put a separator story. Exported for subdocument-write.ts, which writes the same six slots as genuinely empty stories. */
|
|
14
14
|
const FIXED_SEPARATOR_STORY_COUNT = 6;
|
|
15
15
|
function readHeaderFooterStories(wordDocument, table, pieceTable, context, fib, sectionCount) {
|
|
16
16
|
const headerStartCp = fib.ccpText + fib.ccpFtn;
|
|
17
|
-
const perSectionStories = readSubdocumentStories(wordDocument, table, pieceTable, context, headerStartCp, fib.ccpHdd, fib.fcPlcfHdd, fib.lcbPlcfHdd, "Plcfhdd").slice(
|
|
17
|
+
const perSectionStories = readSubdocumentStories(wordDocument, table, pieceTable, context, headerStartCp, fib.ccpHdd, fib.fcPlcfHdd, fib.lcbPlcfHdd, "Plcfhdd").slice(6);
|
|
18
18
|
const stories = [];
|
|
19
19
|
for (let section = 0; section < sectionCount; section += 1) for (let slotIndex = 0; slotIndex < SLOT_ORDER.length; slotIndex += 1) {
|
|
20
20
|
const entries = perSectionStories[section * SLOT_ORDER.length + slotIndex];
|
|
@@ -30,4 +30,4 @@ function readHeaderFooterStories(wordDocument, table, pieceTable, context, fib,
|
|
|
30
30
|
return stories;
|
|
31
31
|
}
|
|
32
32
|
//#endregion
|
|
33
|
-
export { readHeaderFooterStories };
|
|
33
|
+
export { FIXED_SEPARATOR_STORY_COUNT, SLOT_ORDER, readHeaderFooterStories };
|
package/dist/index.cjs
CHANGED
|
@@ -28,6 +28,7 @@ const require_prop_sep = require("./prop/sep.cjs");
|
|
|
28
28
|
const require_prop_sep_write = require("./prop/sep-write.cjs");
|
|
29
29
|
const require_style_fonts = require("./style/fonts.cjs");
|
|
30
30
|
const require_list_numbering = require("./list/numbering.cjs");
|
|
31
|
+
const require_subdocument_write = require("./subdocument-write.cjs");
|
|
31
32
|
const require_notes = require("./notes.cjs");
|
|
32
33
|
const require_read = require("./read.cjs");
|
|
33
34
|
const require_write = require("./write.cjs");
|
|
@@ -56,6 +57,7 @@ exports.FIB_W_IDENT = require_fib_offsets.FIB_W_IDENT;
|
|
|
56
57
|
exports.FIELD_BEGIN = require_text_special.FIELD_BEGIN;
|
|
57
58
|
exports.FIELD_END = require_text_special.FIELD_END;
|
|
58
59
|
exports.FIELD_SEPARATOR = require_text_special.FIELD_SEPARATOR;
|
|
60
|
+
exports.FIXED_SEPARATOR_STORY_COUNT = require_headers_footers.FIXED_SEPARATOR_STORY_COUNT;
|
|
59
61
|
exports.FKP_PAGE_SIZE = require_prop_fkp.FKP_PAGE_SIZE;
|
|
60
62
|
exports.FOOTNOTE_REFERENCE = require_text_special.FOOTNOTE_REFERENCE;
|
|
61
63
|
exports.INLINE_PICTURE = require_text_special.INLINE_PICTURE;
|
|
@@ -66,6 +68,7 @@ exports.PARAGRAPH_MARK = require_text_special.PARAGRAPH_MARK;
|
|
|
66
68
|
exports.PropertyBinTable = require_prop_fkp.PropertyBinTable;
|
|
67
69
|
exports.SECTION_MARK = require_text_special.SECTION_MARK;
|
|
68
70
|
exports.SGC = require_prop_sprm.SGC;
|
|
71
|
+
exports.SLOT_ORDER = require_headers_footers.SLOT_ORDER;
|
|
69
72
|
exports.STI_USER_DEFINED = require_style_stsh.STI_USER_DEFINED;
|
|
70
73
|
exports.STK = require_style_stsh.STK;
|
|
71
74
|
exports.SUMMARY_INFORMATION_STREAM = require_detect.SUMMARY_INFORMATION_STREAM;
|
|
@@ -78,10 +81,13 @@ exports.base64ToBytes = require_base64.base64ToBytes;
|
|
|
78
81
|
exports.buildChpxPages = require_prop_fkp_write.buildChpxPages;
|
|
79
82
|
exports.buildFib = require_fib_write.buildFib;
|
|
80
83
|
exports.buildFontTable = require_style_fonts.buildFontTable;
|
|
84
|
+
exports.buildHeaderSubdocument = require_subdocument_write.buildHeaderSubdocument;
|
|
85
|
+
exports.buildNoteSubdocument = require_subdocument_write.buildNoteSubdocument;
|
|
81
86
|
exports.buildPapxPages = require_prop_fkp_write.buildPapxPages;
|
|
82
87
|
exports.buildPlcfSed = require_prop_sep_write.buildPlcfSed;
|
|
83
88
|
exports.buildPropertyBinTable = require_prop_fkp_write.buildPropertyBinTable;
|
|
84
89
|
exports.buildSepx = require_prop_sep_write.buildSepx;
|
|
90
|
+
exports.buildStorySubdocuments = require_subdocument_write.buildStorySubdocuments;
|
|
85
91
|
exports.buildStshForStyles = require_style_stsh.buildStshForStyles;
|
|
86
92
|
exports.buildTextClx = require_text_piece_table_write.buildTextClx;
|
|
87
93
|
exports.bytesToBase64 = require_base64.bytesToBase64;
|
|
@@ -101,6 +107,7 @@ exports.isAnchorOnly = require_text_special.isAnchorOnly;
|
|
|
101
107
|
exports.isDocBytes = require_detect.isDocBytes;
|
|
102
108
|
exports.layoutMetadataToSummaryInformation = require_metadata.layoutMetadataToSummaryInformation;
|
|
103
109
|
exports.operandSize = require_prop_sprm.operandSize;
|
|
110
|
+
exports.paragraphCharacters = require_subdocument_write.paragraphCharacters;
|
|
104
111
|
exports.parseChpxFkp = require_prop_fkp.parseChpxFkp;
|
|
105
112
|
exports.parseClx = require_text_piece_table.parseClx;
|
|
106
113
|
exports.parseFib = require_fib_fib.parseFib;
|
package/dist/index.d.cts
CHANGED
|
@@ -13,7 +13,7 @@ import { n as applyParagraphSprms, t as ParagraphProperties } from "./pap-BQ2E17
|
|
|
13
13
|
import { a as StyleSheet, c as parseStsh, i as Style, l as resolveStyleFormatting, n as STI_USER_DEFINED, o as buildStshForStyles, r as STK, s as headingLevelFromIstd, t as ResolvedStyleFormatting } from "./stsh-CK59x6XG.cjs";
|
|
14
14
|
import { i as splitEntriesByBoundaries, n as ReadContext, r as readParagraphs, t as ParagraphEntry } from "./paragraphs-DQUp8_HB.cjs";
|
|
15
15
|
import { a as parseClx, i as characterSize, n as PieceTable, r as characterOffset, t as Piece } from "./piece-table-CA18mhjv.cjs";
|
|
16
|
-
import { HeaderFooterSlot, HeaderFooterStories, HeaderFooterStory, readHeaderFooterStories } from "./headers-footers.cjs";
|
|
16
|
+
import { FIXED_SEPARATOR_STORY_COUNT, HeaderFooterSlot, HeaderFooterStories, HeaderFooterStory, SLOT_ORDER, readHeaderFooterStories } from "./headers-footers.cjs";
|
|
17
17
|
import { Plc, findLargestAtMost, parsePlc } from "./plc.cjs";
|
|
18
18
|
import { layoutMetadataToSummaryInformation } from "./metadata.cjs";
|
|
19
19
|
import { buildTextClx } from "./text/piece-table-write.cjs";
|
|
@@ -27,7 +27,8 @@ import { buildPlcfSed, buildSepx, encodeSectionGrpprl } from "./prop/sep-write.c
|
|
|
27
27
|
import { buildFontTable, parseFontTable } from "./style/fonts.cjs";
|
|
28
28
|
import { NUMBER_FORMAT_BY_NFC, NumberingDefinition, NumberingDefinitions, NumberingLevel, readNumberingDefinitions } from "./list/numbering.cjs";
|
|
29
29
|
import { readSubdocumentStories, storyText } from "./subdocument.cjs";
|
|
30
|
+
import { StorySubdocument, StorySubdocuments, buildHeaderSubdocument, buildNoteSubdocument, buildStorySubdocuments, paragraphCharacters } from "./subdocument-write.cjs";
|
|
30
31
|
import { Comment, Footnote, NoteBodies, readNoteBodies } from "./notes.cjs";
|
|
31
32
|
import { DocContent, DocStreams, readDocContent, readDocStreams } from "./read.cjs";
|
|
32
|
-
import { WriteDocContentOptions, writeDocContent } from "./write.cjs";
|
|
33
|
-
export { ANNOTATION_REFERENCE, CELL_MARK, COMPRESSED_CHARACTER_MAP, CharacterProperties, ChpxFkp, ChpxRunToWrite, Comment, DATA_STREAM, DRAWN_OBJECT, DataStreamBuilder, DocContent, DocFormatError, DocSectionProperties, DocStreams, DocUnsupportedError, FC_LCB_VALUE_INDEX, FIB_BASE_FLAG, FIB_BASE_SIZE, FIB_CB_RG_FC_LCB_OFFSET, FIB_CSLW_OFFSET, FIB_CSLW_REQUIRED, FIB_CSW_REQUIRED, FIB_FC_LCB_BLOB_OFFSET, FIB_LKEY_OFFSET, FIB_RG_LW_OFFSET, FIB_RG_LW_SIZE, FIB_RG_W_OFFSET, FIB_RG_W_SIZE, FIB_W_IDENT, FIELD_BEGIN, FIELD_END, FIELD_SEPARATOR, FKP_PAGE_SIZE, FOOTNOTE_REFERENCE, Fib, FibWriteSpec, Footnote, HeaderFooterSlot, HeaderFooterStories, HeaderFooterStory, INLINE_PICTURE, LINE_BREAK, LW_OFFSET, NUMBER_FORMAT_BY_NFC, NoteBodies, NumberingDefinition, NumberingDefinitions, NumberingLevel, PARAGRAPH_MARK, PapxFkp, PapxLookup, PapxParagraphToWrite, PapxRecord, ParagraphEntry, ParagraphProperties, Piece, PieceTable, Plc, Prl, PropertyBinTable, ReadContext, ResolvedStyleFormatting, SECTION_MARK, SGC, STI_USER_DEFINED, STK, SUMMARY_INFORMATION_STREAM, SYMBOL_ANCHOR, SectionProperties, Sprm, Style, StyleSheet, TextRange, WORD_DOCUMENT_STREAM, WriteDocContentOptions, applyCharacterSprms, applyParagraphSprms, applySectionSprms, base64ToBytes, buildChpxPages, buildFib, buildFontTable, buildPapxPages, buildPlcfSed, buildPropertyBinTable, buildSepx, buildStshForStyles, buildTextClx, bytesToBase64, characterIstdFromGrpprl, characterOffset, characterSize, decodeSprm, encodeCharacterGrpprl, encodeParagraphGrpprl, encodeSectionGrpprl, endsParagraph, findLargestAtMost, firstFcOfPage, fitsAloneOnPapxPage, headingLevelFromIstd, isAnchorOnly, isDocBytes, layoutMetadataToSummaryInformation, operandSize, parseChpxFkp, parseClx, parseFib, parseFontTable, parsePapxFkp, parsePlc, parseStsh, peekFibBaseFlags, readAllSectionProperties, readDocContent, readDocStreams, readGrpprl, readHeaderFooterStories, readInt16LE, readInt32LE, readNoteBodies, readNumberingDefinitions, readParagraphs, readSubdocumentStories, readTextRange, readUint16LE, readUint32LE, readUint8, resolveStyleFormatting, slice, splitEntriesByBoundaries, storyText, tableStreamName, writeDocContent };
|
|
33
|
+
import { WritableDocContent, WriteDocContentOptions, writeDocContent } from "./write.cjs";
|
|
34
|
+
export { ANNOTATION_REFERENCE, CELL_MARK, COMPRESSED_CHARACTER_MAP, CharacterProperties, ChpxFkp, ChpxRunToWrite, Comment, DATA_STREAM, DRAWN_OBJECT, DataStreamBuilder, DocContent, DocFormatError, DocSectionProperties, DocStreams, DocUnsupportedError, FC_LCB_VALUE_INDEX, FIB_BASE_FLAG, FIB_BASE_SIZE, FIB_CB_RG_FC_LCB_OFFSET, FIB_CSLW_OFFSET, FIB_CSLW_REQUIRED, FIB_CSW_REQUIRED, FIB_FC_LCB_BLOB_OFFSET, FIB_LKEY_OFFSET, FIB_RG_LW_OFFSET, FIB_RG_LW_SIZE, FIB_RG_W_OFFSET, FIB_RG_W_SIZE, FIB_W_IDENT, FIELD_BEGIN, FIELD_END, FIELD_SEPARATOR, FIXED_SEPARATOR_STORY_COUNT, FKP_PAGE_SIZE, FOOTNOTE_REFERENCE, Fib, FibWriteSpec, Footnote, HeaderFooterSlot, HeaderFooterStories, HeaderFooterStory, INLINE_PICTURE, LINE_BREAK, LW_OFFSET, NUMBER_FORMAT_BY_NFC, NoteBodies, NumberingDefinition, NumberingDefinitions, NumberingLevel, PARAGRAPH_MARK, PapxFkp, PapxLookup, PapxParagraphToWrite, PapxRecord, ParagraphEntry, ParagraphProperties, Piece, PieceTable, Plc, Prl, PropertyBinTable, ReadContext, ResolvedStyleFormatting, SECTION_MARK, SGC, SLOT_ORDER, STI_USER_DEFINED, STK, SUMMARY_INFORMATION_STREAM, SYMBOL_ANCHOR, SectionProperties, Sprm, StorySubdocument, StorySubdocuments, Style, StyleSheet, TextRange, WORD_DOCUMENT_STREAM, WritableDocContent, WriteDocContentOptions, applyCharacterSprms, applyParagraphSprms, applySectionSprms, base64ToBytes, buildChpxPages, buildFib, buildFontTable, buildHeaderSubdocument, buildNoteSubdocument, buildPapxPages, buildPlcfSed, buildPropertyBinTable, buildSepx, buildStorySubdocuments, buildStshForStyles, buildTextClx, bytesToBase64, characterIstdFromGrpprl, characterOffset, characterSize, decodeSprm, encodeCharacterGrpprl, encodeParagraphGrpprl, encodeSectionGrpprl, endsParagraph, findLargestAtMost, firstFcOfPage, fitsAloneOnPapxPage, headingLevelFromIstd, isAnchorOnly, isDocBytes, layoutMetadataToSummaryInformation, operandSize, paragraphCharacters, parseChpxFkp, parseClx, parseFib, parseFontTable, parsePapxFkp, parsePlc, parseStsh, peekFibBaseFlags, readAllSectionProperties, readDocContent, readDocStreams, readGrpprl, readHeaderFooterStories, readInt16LE, readInt32LE, readNoteBodies, readNumberingDefinitions, readParagraphs, readSubdocumentStories, readTextRange, readUint16LE, readUint32LE, readUint8, resolveStyleFormatting, slice, splitEntriesByBoundaries, storyText, tableStreamName, writeDocContent };
|
package/dist/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { n as applyParagraphSprms, t as ParagraphProperties } from "./pap-iWFIpC
|
|
|
13
13
|
import { a as StyleSheet, c as parseStsh, i as Style, l as resolveStyleFormatting, n as STI_USER_DEFINED, o as buildStshForStyles, r as STK, s as headingLevelFromIstd, t as ResolvedStyleFormatting } from "./stsh-Bj0YSEOp.js";
|
|
14
14
|
import { i as splitEntriesByBoundaries, n as ReadContext, r as readParagraphs, t as ParagraphEntry } from "./paragraphs-s2lJ6w2I.js";
|
|
15
15
|
import { a as parseClx, i as characterSize, n as PieceTable, r as characterOffset, t as Piece } from "./piece-table-CA18mhjv.js";
|
|
16
|
-
import { HeaderFooterSlot, HeaderFooterStories, HeaderFooterStory, readHeaderFooterStories } from "./headers-footers.js";
|
|
16
|
+
import { FIXED_SEPARATOR_STORY_COUNT, HeaderFooterSlot, HeaderFooterStories, HeaderFooterStory, SLOT_ORDER, readHeaderFooterStories } from "./headers-footers.js";
|
|
17
17
|
import { Plc, findLargestAtMost, parsePlc } from "./plc.js";
|
|
18
18
|
import { layoutMetadataToSummaryInformation } from "./metadata.js";
|
|
19
19
|
import { buildTextClx } from "./text/piece-table-write.js";
|
|
@@ -27,7 +27,8 @@ import { buildPlcfSed, buildSepx, encodeSectionGrpprl } from "./prop/sep-write.j
|
|
|
27
27
|
import { buildFontTable, parseFontTable } from "./style/fonts.js";
|
|
28
28
|
import { NUMBER_FORMAT_BY_NFC, NumberingDefinition, NumberingDefinitions, NumberingLevel, readNumberingDefinitions } from "./list/numbering.js";
|
|
29
29
|
import { readSubdocumentStories, storyText } from "./subdocument.js";
|
|
30
|
+
import { StorySubdocument, StorySubdocuments, buildHeaderSubdocument, buildNoteSubdocument, buildStorySubdocuments, paragraphCharacters } from "./subdocument-write.js";
|
|
30
31
|
import { Comment, Footnote, NoteBodies, readNoteBodies } from "./notes.js";
|
|
31
32
|
import { DocContent, DocStreams, readDocContent, readDocStreams } from "./read.js";
|
|
32
|
-
import { WriteDocContentOptions, writeDocContent } from "./write.js";
|
|
33
|
-
export { ANNOTATION_REFERENCE, CELL_MARK, COMPRESSED_CHARACTER_MAP, CharacterProperties, ChpxFkp, ChpxRunToWrite, Comment, DATA_STREAM, DRAWN_OBJECT, DataStreamBuilder, DocContent, DocFormatError, DocSectionProperties, DocStreams, DocUnsupportedError, FC_LCB_VALUE_INDEX, FIB_BASE_FLAG, FIB_BASE_SIZE, FIB_CB_RG_FC_LCB_OFFSET, FIB_CSLW_OFFSET, FIB_CSLW_REQUIRED, FIB_CSW_REQUIRED, FIB_FC_LCB_BLOB_OFFSET, FIB_LKEY_OFFSET, FIB_RG_LW_OFFSET, FIB_RG_LW_SIZE, FIB_RG_W_OFFSET, FIB_RG_W_SIZE, FIB_W_IDENT, FIELD_BEGIN, FIELD_END, FIELD_SEPARATOR, FKP_PAGE_SIZE, FOOTNOTE_REFERENCE, Fib, FibWriteSpec, Footnote, HeaderFooterSlot, HeaderFooterStories, HeaderFooterStory, INLINE_PICTURE, LINE_BREAK, LW_OFFSET, NUMBER_FORMAT_BY_NFC, NoteBodies, NumberingDefinition, NumberingDefinitions, NumberingLevel, PARAGRAPH_MARK, PapxFkp, PapxLookup, PapxParagraphToWrite, PapxRecord, ParagraphEntry, ParagraphProperties, Piece, PieceTable, Plc, Prl, PropertyBinTable, ReadContext, ResolvedStyleFormatting, SECTION_MARK, SGC, STI_USER_DEFINED, STK, SUMMARY_INFORMATION_STREAM, SYMBOL_ANCHOR, SectionProperties, Sprm, Style, StyleSheet, TextRange, WORD_DOCUMENT_STREAM, WriteDocContentOptions, applyCharacterSprms, applyParagraphSprms, applySectionSprms, base64ToBytes, buildChpxPages, buildFib, buildFontTable, buildPapxPages, buildPlcfSed, buildPropertyBinTable, buildSepx, buildStshForStyles, buildTextClx, bytesToBase64, characterIstdFromGrpprl, characterOffset, characterSize, decodeSprm, encodeCharacterGrpprl, encodeParagraphGrpprl, encodeSectionGrpprl, endsParagraph, findLargestAtMost, firstFcOfPage, fitsAloneOnPapxPage, headingLevelFromIstd, isAnchorOnly, isDocBytes, layoutMetadataToSummaryInformation, operandSize, parseChpxFkp, parseClx, parseFib, parseFontTable, parsePapxFkp, parsePlc, parseStsh, peekFibBaseFlags, readAllSectionProperties, readDocContent, readDocStreams, readGrpprl, readHeaderFooterStories, readInt16LE, readInt32LE, readNoteBodies, readNumberingDefinitions, readParagraphs, readSubdocumentStories, readTextRange, readUint16LE, readUint32LE, readUint8, resolveStyleFormatting, slice, splitEntriesByBoundaries, storyText, tableStreamName, writeDocContent };
|
|
33
|
+
import { WritableDocContent, WriteDocContentOptions, writeDocContent } from "./write.js";
|
|
34
|
+
export { ANNOTATION_REFERENCE, CELL_MARK, COMPRESSED_CHARACTER_MAP, CharacterProperties, ChpxFkp, ChpxRunToWrite, Comment, DATA_STREAM, DRAWN_OBJECT, DataStreamBuilder, DocContent, DocFormatError, DocSectionProperties, DocStreams, DocUnsupportedError, FC_LCB_VALUE_INDEX, FIB_BASE_FLAG, FIB_BASE_SIZE, FIB_CB_RG_FC_LCB_OFFSET, FIB_CSLW_OFFSET, FIB_CSLW_REQUIRED, FIB_CSW_REQUIRED, FIB_FC_LCB_BLOB_OFFSET, FIB_LKEY_OFFSET, FIB_RG_LW_OFFSET, FIB_RG_LW_SIZE, FIB_RG_W_OFFSET, FIB_RG_W_SIZE, FIB_W_IDENT, FIELD_BEGIN, FIELD_END, FIELD_SEPARATOR, FIXED_SEPARATOR_STORY_COUNT, FKP_PAGE_SIZE, FOOTNOTE_REFERENCE, Fib, FibWriteSpec, Footnote, HeaderFooterSlot, HeaderFooterStories, HeaderFooterStory, INLINE_PICTURE, LINE_BREAK, LW_OFFSET, NUMBER_FORMAT_BY_NFC, NoteBodies, NumberingDefinition, NumberingDefinitions, NumberingLevel, PARAGRAPH_MARK, PapxFkp, PapxLookup, PapxParagraphToWrite, PapxRecord, ParagraphEntry, ParagraphProperties, Piece, PieceTable, Plc, Prl, PropertyBinTable, ReadContext, ResolvedStyleFormatting, SECTION_MARK, SGC, SLOT_ORDER, STI_USER_DEFINED, STK, SUMMARY_INFORMATION_STREAM, SYMBOL_ANCHOR, SectionProperties, Sprm, StorySubdocument, StorySubdocuments, Style, StyleSheet, TextRange, WORD_DOCUMENT_STREAM, WritableDocContent, WriteDocContentOptions, applyCharacterSprms, applyParagraphSprms, applySectionSprms, base64ToBytes, buildChpxPages, buildFib, buildFontTable, buildHeaderSubdocument, buildNoteSubdocument, buildPapxPages, buildPlcfSed, buildPropertyBinTable, buildSepx, buildStorySubdocuments, buildStshForStyles, buildTextClx, bytesToBase64, characterIstdFromGrpprl, characterOffset, characterSize, decodeSprm, encodeCharacterGrpprl, encodeParagraphGrpprl, encodeSectionGrpprl, endsParagraph, findLargestAtMost, firstFcOfPage, fitsAloneOnPapxPage, headingLevelFromIstd, isAnchorOnly, isDocBytes, layoutMetadataToSummaryInformation, operandSize, paragraphCharacters, parseChpxFkp, parseClx, parseFib, parseFontTable, parsePapxFkp, parsePlc, parseStsh, peekFibBaseFlags, readAllSectionProperties, readDocContent, readDocStreams, readGrpprl, readHeaderFooterStories, readInt16LE, readInt32LE, readNoteBodies, readNumberingDefinitions, readParagraphs, readSubdocumentStories, readTextRange, readUint16LE, readUint32LE, readUint8, resolveStyleFormatting, slice, splitEntriesByBoundaries, storyText, tableStreamName, writeDocContent };
|