doc-codec 2.6.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/write.cjs CHANGED
@@ -1,5 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_errors = require("./errors.cjs");
3
+ const require_data_stream = require("./data-stream.cjs");
3
4
  const require_detect = require("./detect.cjs");
4
5
  require("./prop/fkp.cjs");
5
6
  const require_style_stsh = require("./style/stsh.cjs");
@@ -22,16 +23,22 @@ const TEXT_FC = 1024;
22
23
  const BYTES_PER_CHARACTER = 2;
23
24
  function writeDocContent(document, options = {}) {
24
25
  if (document.kind !== "wordprocessing") throw new require_errors.DocUnsupportedError(`doc-codec writes wordprocessing documents only; got a '${document.kind}' document`);
25
- if (document.sections.length !== 1) throw new require_errors.DocUnsupportedError(`doc-codec's reader never distinguishes more than one section within a document (see README's "Section properties" scope note): writeDocContent refuses ${document.sections.length} sections rather than silently merging their content into what would read back as one`);
26
- const [section] = document.sections;
27
- if (section === void 0) throw new require_errors.DocFormatError("a wordprocessing document must carry a section");
28
- const writeParagraphs = require_table_write.flattenSectionBlocks(section.blocks, options.onWarning);
29
- if (writeParagraphs[writeParagraphs.length - 1]?.terminator !== 13) writeParagraphs.push({
30
- runs: [],
31
- properties: {},
32
- extraGrpprl: [],
33
- terminator: 13
26
+ if (document.sections.length === 0) throw new require_errors.DocFormatError("a wordprocessing document must carry at least one section");
27
+ const dataStream = new require_data_stream.DataStreamBuilder();
28
+ const sectionParagraphLists = document.sections.map((section, index) => {
29
+ const paragraphs = require_table_write.flattenSectionBlocks(section.blocks, dataStream, options.onWarning);
30
+ closeSection(paragraphs, index === document.sections.length - 1 ? 13 : 12);
31
+ return paragraphs;
34
32
  });
33
+ const sectionStartIndices = [];
34
+ {
35
+ let runningIndex = 0;
36
+ for (const paragraphs of sectionParagraphLists) {
37
+ sectionStartIndices.push(runningIndex);
38
+ runningIndex += paragraphs.length;
39
+ }
40
+ }
41
+ const writeParagraphs = sectionParagraphLists.flat();
35
42
  const FIRST_NON_HEADING_ISTD = 10;
36
43
  const MAX_HEADING_ISTD = 9;
37
44
  const styleNames = /* @__PURE__ */ new Map();
@@ -72,9 +79,9 @@ function writeDocContent(document, options = {}) {
72
79
  };
73
80
  const numberingTables = require_list_numbering_write.buildNumberingTables(listUsage.definitions);
74
81
  const formatted = writeParagraphs.map((entry) => ({
75
- runs: entry.runs.map((run) => ({
76
- text: run.text,
77
- grpprl: require_prop_chp_write.encodeCharacterGrpprl(run, fontIndexOf)
82
+ runs: entry.runs.map((writeRun) => ({
83
+ text: writeRun.run.text,
84
+ grpprl: [...require_prop_chp_write.encodeCharacterGrpprl(writeRun.run, fontIndexOf), ...writeRun.extraGrpprl]
78
85
  })),
79
86
  grpprl: [...require_prop_pap_write.encodeParagraphGrpprl(entry.properties, ilfoOf), ...entry.extraGrpprl]
80
87
  }));
@@ -132,9 +139,20 @@ function writeDocContent(document, options = {}) {
132
139
  };
133
140
  });
134
141
  const papxPages = require_prop_fkp_write.buildPapxPages(papxParagraphSpecs, textFcLim);
135
- const sepx = require_prop_sep_write.buildSepx(require_prop_sep_write.encodeSectionGrpprl(section));
136
- const fcSepx = (papxPageStart + papxPages.length) * 512;
137
- const wordDocument = new Uint8Array(fcSepx + sepx.length);
142
+ const sepxPageStart = (papxPageStart + papxPages.length) * 512;
143
+ const sepxList = document.sections.map((section) => require_prop_sep_write.buildSepx(require_prop_sep_write.encodeSectionGrpprl(section)));
144
+ const fcSepxList = [];
145
+ {
146
+ let sepxCursor = sepxPageStart;
147
+ for (const sepx of sepxList) {
148
+ fcSepxList.push(sepxCursor);
149
+ sepxCursor += sepx.length;
150
+ }
151
+ }
152
+ const lastFcSepx = fcSepxList[fcSepxList.length - 1];
153
+ const lastSepx = sepxList[sepxList.length - 1];
154
+ if (lastFcSepx === void 0 || lastSepx === void 0) throw new require_errors.DocFormatError("internal defect: writeDocContent built an empty section list despite the earlier at-least-one-section guard");
155
+ const wordDocument = new Uint8Array(lastFcSepx + lastSepx.length);
138
156
  const wordView = new DataView(wordDocument.buffer);
139
157
  for (let index = 0; index < text.length; index += 1) wordView.setUint16(characterFc(index), text.charCodeAt(index), true);
140
158
  chpxPages.forEach((page, index) => {
@@ -143,13 +161,22 @@ function writeDocContent(document, options = {}) {
143
161
  papxPages.forEach((page, index) => {
144
162
  wordDocument.set(page, (papxPageStart + index) * 512);
145
163
  });
146
- wordDocument.set(sepx, fcSepx);
164
+ sepxList.forEach((sepx, index) => {
165
+ const fcSepx = fcSepxList[index];
166
+ if (fcSepx === void 0) throw new require_errors.DocFormatError(`internal defect: writeDocContent lost section ${String(index)}'s own Sepx placement`);
167
+ wordDocument.set(sepx, fcSepx);
168
+ });
169
+ const sectionStartCps = sectionStartIndices.map((paragraphIndex, index) => {
170
+ const startCp = paragraphStarts[paragraphIndex];
171
+ if (startCp === void 0) throw new require_errors.DocFormatError(`internal defect: writeDocContent lost section ${String(index)}'s own start CP`);
172
+ return startCp;
173
+ });
147
174
  const clx = require_text_piece_table_write.buildTextClx(text.length, TEXT_FC);
148
175
  const chpxBinTable = require_prop_fkp_write.buildPropertyBinTable([...chpxPages.map(require_prop_fkp_write.firstFcOfPage), textFcLim], chpxPages.map((_, index) => chpxPageStart + index));
149
176
  const papxBinTable = require_prop_fkp_write.buildPropertyBinTable([...papxPages.map(require_prop_fkp_write.firstFcOfPage), textFcLim], papxPages.map((_, index) => papxPageStart + index));
150
177
  const stsh = require_style_stsh.buildStshForStyles(styleNames);
151
178
  const fontTable = fontNames.length > 0 ? require_style_fonts.buildFontTable(fontNames) : void 0;
152
- const plcfSed = require_prop_sep_write.buildPlcfSed(text.length, fcSepx);
179
+ const plcfSed = require_prop_sep_write.buildPlcfSed(sectionStartCps, text.length, fcSepxList);
153
180
  let cursor = 0;
154
181
  const place = (bytes) => {
155
182
  const offset = cursor;
@@ -207,11 +234,32 @@ function writeDocContent(document, options = {}) {
207
234
  path: require_detect.SUMMARY_INFORMATION_STREAM,
208
235
  bytes: (0, archive_codec.writeSummaryInformationStream)(require_metadata.layoutMetadataToSummaryInformation(document.metadata))
209
236
  });
237
+ const dataStreamBytes = dataStream.build();
238
+ if (dataStreamBytes.length > 0) streams.push({
239
+ path: require_detect.DATA_STREAM,
240
+ bytes: dataStreamBytes
241
+ });
210
242
  return (0, archive_codec.writeCompoundFile)(streams);
211
243
  }
212
244
  function sameGrpprl(a, b) {
213
245
  if (a === void 0 || b === void 0) return a === b;
214
246
  return a.length === b.length && a.every((byte, index) => byte === b[index]);
215
247
  }
248
+ function closeSection(paragraphs, terminator) {
249
+ if (paragraphs[paragraphs.length - 1]?.terminator !== 13) paragraphs.push({
250
+ runs: [],
251
+ properties: {},
252
+ extraGrpprl: [],
253
+ terminator: 13
254
+ });
255
+ if (terminator === 13) return;
256
+ const index = paragraphs.length - 1;
257
+ const target = paragraphs[index];
258
+ if (target === void 0) throw new require_errors.DocFormatError("internal defect: closeSection lost its own just-ensured trailing paragraph");
259
+ paragraphs[index] = {
260
+ ...target,
261
+ terminator
262
+ };
263
+ }
216
264
  //#endregion
217
265
  exports.writeDocContent = writeDocContent;
package/dist/write.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { n as WriteWarning } from "./write-SN_aIwyF.cjs";
1
+ import { r as WriteWarning } from "./write-CMB3qF4i.cjs";
2
2
  import { ContentDocument } from "document-schema.js";
3
3
  //#region src/write.d.ts
4
4
  interface WriteDocContentOptions {
package/dist/write.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { n as WriteWarning } from "./write-SN_aIwyF.js";
1
+ import { r as WriteWarning } from "./write-t4rFgnA-.js";
2
2
  import { ContentDocument } from "document-schema.js";
3
3
  //#region src/write.d.ts
4
4
  interface WriteDocContentOptions {
package/dist/write.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { DocFormatError, DocUnsupportedError } from "./errors.js";
2
- import { SUMMARY_INFORMATION_STREAM, WORD_DOCUMENT_STREAM } from "./detect.js";
2
+ import { DataStreamBuilder } from "./data-stream.js";
3
+ import { DATA_STREAM, SUMMARY_INFORMATION_STREAM, WORD_DOCUMENT_STREAM } from "./detect.js";
3
4
  import "./prop/fkp.js";
4
5
  import { buildStshForStyles } from "./style/stsh.js";
5
6
  import "./text/special.js";
@@ -21,16 +22,22 @@ const TEXT_FC = 1024;
21
22
  const BYTES_PER_CHARACTER = 2;
22
23
  function writeDocContent(document, options = {}) {
23
24
  if (document.kind !== "wordprocessing") throw new DocUnsupportedError(`doc-codec writes wordprocessing documents only; got a '${document.kind}' document`);
24
- if (document.sections.length !== 1) throw new DocUnsupportedError(`doc-codec's reader never distinguishes more than one section within a document (see README's "Section properties" scope note): writeDocContent refuses ${document.sections.length} sections rather than silently merging their content into what would read back as one`);
25
- const [section] = document.sections;
26
- if (section === void 0) throw new DocFormatError("a wordprocessing document must carry a section");
27
- const writeParagraphs = flattenSectionBlocks(section.blocks, options.onWarning);
28
- if (writeParagraphs[writeParagraphs.length - 1]?.terminator !== 13) writeParagraphs.push({
29
- runs: [],
30
- properties: {},
31
- extraGrpprl: [],
32
- terminator: 13
25
+ if (document.sections.length === 0) throw new DocFormatError("a wordprocessing document must carry at least one section");
26
+ const dataStream = new DataStreamBuilder();
27
+ const sectionParagraphLists = document.sections.map((section, index) => {
28
+ const paragraphs = flattenSectionBlocks(section.blocks, dataStream, options.onWarning);
29
+ closeSection(paragraphs, index === document.sections.length - 1 ? 13 : 12);
30
+ return paragraphs;
33
31
  });
32
+ const sectionStartIndices = [];
33
+ {
34
+ let runningIndex = 0;
35
+ for (const paragraphs of sectionParagraphLists) {
36
+ sectionStartIndices.push(runningIndex);
37
+ runningIndex += paragraphs.length;
38
+ }
39
+ }
40
+ const writeParagraphs = sectionParagraphLists.flat();
34
41
  const FIRST_NON_HEADING_ISTD = 10;
35
42
  const MAX_HEADING_ISTD = 9;
36
43
  const styleNames = /* @__PURE__ */ new Map();
@@ -71,9 +78,9 @@ function writeDocContent(document, options = {}) {
71
78
  };
72
79
  const numberingTables = buildNumberingTables(listUsage.definitions);
73
80
  const formatted = writeParagraphs.map((entry) => ({
74
- runs: entry.runs.map((run) => ({
75
- text: run.text,
76
- grpprl: encodeCharacterGrpprl(run, fontIndexOf)
81
+ runs: entry.runs.map((writeRun) => ({
82
+ text: writeRun.run.text,
83
+ grpprl: [...encodeCharacterGrpprl(writeRun.run, fontIndexOf), ...writeRun.extraGrpprl]
77
84
  })),
78
85
  grpprl: [...encodeParagraphGrpprl(entry.properties, ilfoOf), ...entry.extraGrpprl]
79
86
  }));
@@ -131,9 +138,20 @@ function writeDocContent(document, options = {}) {
131
138
  };
132
139
  });
133
140
  const papxPages = buildPapxPages(papxParagraphSpecs, textFcLim);
134
- const sepx = buildSepx(encodeSectionGrpprl(section));
135
- const fcSepx = (papxPageStart + papxPages.length) * 512;
136
- const wordDocument = new Uint8Array(fcSepx + sepx.length);
141
+ const sepxPageStart = (papxPageStart + papxPages.length) * 512;
142
+ const sepxList = document.sections.map((section) => buildSepx(encodeSectionGrpprl(section)));
143
+ const fcSepxList = [];
144
+ {
145
+ let sepxCursor = sepxPageStart;
146
+ for (const sepx of sepxList) {
147
+ fcSepxList.push(sepxCursor);
148
+ sepxCursor += sepx.length;
149
+ }
150
+ }
151
+ const lastFcSepx = fcSepxList[fcSepxList.length - 1];
152
+ const lastSepx = sepxList[sepxList.length - 1];
153
+ if (lastFcSepx === void 0 || lastSepx === void 0) throw new DocFormatError("internal defect: writeDocContent built an empty section list despite the earlier at-least-one-section guard");
154
+ const wordDocument = new Uint8Array(lastFcSepx + lastSepx.length);
137
155
  const wordView = new DataView(wordDocument.buffer);
138
156
  for (let index = 0; index < text.length; index += 1) wordView.setUint16(characterFc(index), text.charCodeAt(index), true);
139
157
  chpxPages.forEach((page, index) => {
@@ -142,13 +160,22 @@ function writeDocContent(document, options = {}) {
142
160
  papxPages.forEach((page, index) => {
143
161
  wordDocument.set(page, (papxPageStart + index) * 512);
144
162
  });
145
- wordDocument.set(sepx, fcSepx);
163
+ sepxList.forEach((sepx, index) => {
164
+ const fcSepx = fcSepxList[index];
165
+ if (fcSepx === void 0) throw new DocFormatError(`internal defect: writeDocContent lost section ${String(index)}'s own Sepx placement`);
166
+ wordDocument.set(sepx, fcSepx);
167
+ });
168
+ const sectionStartCps = sectionStartIndices.map((paragraphIndex, index) => {
169
+ const startCp = paragraphStarts[paragraphIndex];
170
+ if (startCp === void 0) throw new DocFormatError(`internal defect: writeDocContent lost section ${String(index)}'s own start CP`);
171
+ return startCp;
172
+ });
146
173
  const clx = buildTextClx(text.length, TEXT_FC);
147
174
  const chpxBinTable = buildPropertyBinTable([...chpxPages.map(firstFcOfPage), textFcLim], chpxPages.map((_, index) => chpxPageStart + index));
148
175
  const papxBinTable = buildPropertyBinTable([...papxPages.map(firstFcOfPage), textFcLim], papxPages.map((_, index) => papxPageStart + index));
149
176
  const stsh = buildStshForStyles(styleNames);
150
177
  const fontTable = fontNames.length > 0 ? buildFontTable(fontNames) : void 0;
151
- const plcfSed = buildPlcfSed(text.length, fcSepx);
178
+ const plcfSed = buildPlcfSed(sectionStartCps, text.length, fcSepxList);
152
179
  let cursor = 0;
153
180
  const place = (bytes) => {
154
181
  const offset = cursor;
@@ -206,11 +233,32 @@ function writeDocContent(document, options = {}) {
206
233
  path: SUMMARY_INFORMATION_STREAM,
207
234
  bytes: writeSummaryInformationStream(layoutMetadataToSummaryInformation$1(document.metadata))
208
235
  });
236
+ const dataStreamBytes = dataStream.build();
237
+ if (dataStreamBytes.length > 0) streams.push({
238
+ path: DATA_STREAM,
239
+ bytes: dataStreamBytes
240
+ });
209
241
  return writeCompoundFile(streams);
210
242
  }
211
243
  function sameGrpprl(a, b) {
212
244
  if (a === void 0 || b === void 0) return a === b;
213
245
  return a.length === b.length && a.every((byte, index) => byte === b[index]);
214
246
  }
247
+ function closeSection(paragraphs, terminator) {
248
+ if (paragraphs[paragraphs.length - 1]?.terminator !== 13) paragraphs.push({
249
+ runs: [],
250
+ properties: {},
251
+ extraGrpprl: [],
252
+ terminator: 13
253
+ });
254
+ if (terminator === 13) return;
255
+ const index = paragraphs.length - 1;
256
+ const target = paragraphs[index];
257
+ if (target === void 0) throw new DocFormatError("internal defect: closeSection lost its own just-ensured trailing paragraph");
258
+ paragraphs[index] = {
259
+ ...target,
260
+ terminator
261
+ };
262
+ }
215
263
  //#endregion
216
264
  export { writeDocContent };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "doc-codec",
3
- "version": "2.6.0",
3
+ "version": "2.7.0",
4
4
  "description": "A hand-written reader for the Word Binary File Format ([MS-DOC], .doc) against the shared document-schema.js content pivot: FIB parsing, piece-table text reconstruction, and CHPX/PAPX formatting exceptions.",
5
5
  "type": "module",
6
6
  "repository": {