rtf-codec 1.0.0 → 1.1.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/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { rtfBytesFromLatin1 } from "./bytes.cjs";
2
2
  import { RtfBytesSchema, rtfCodec, rtfContentCodec } from "./codec.cjs";
3
- import { a as RtfDiagnosticSink, c as RtfNotAnRtfDocumentError, d as RtfWriteError, i as RtfDiagnosticSeverity, l as RtfParseError, n as RtfDiagnostic, o as RtfInputTooLargeError, r as RtfDiagnosticCodes, s as RtfNestingLimitExceededError, t as NOOP_RTF_DIAGNOSTIC_SINK, u as RtfUnsupportedDocumentKindError } from "./diagnostics-DdDFJLNO.cjs";
3
+ import { a as RtfDiagnosticSink, c as RtfNotAnRtfDocumentError, d as RtfWriteError, i as RtfDiagnosticSeverity, l as RtfParseError, n as RtfDiagnostic, o as RtfInputTooLargeError, r as RtfDiagnosticCodes, s as RtfNestingLimitExceededError, t as NOOP_RTF_DIAGNOSTIC_SINK, u as RtfUnsupportedDocumentKindError } from "./diagnostics-BgG_KAiN.cjs";
4
4
  import { DEFAULT_MAX_GROUP_DEPTH, DEFAULT_MAX_INPUT_BYTES, ReadRtfOptions, RtfLineEnding, WriteRtfOptions } from "./options.cjs";
5
5
  import { ReadRtfContentResult, ReadRtfResult, readRtf, readRtfContent } from "./read.cjs";
6
6
  import { writeRtf, writeRtfContent } from "./write.cjs";
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { rtfBytesFromLatin1 } from "./bytes.js";
2
2
  import { RtfBytesSchema, rtfCodec, rtfContentCodec } from "./codec.js";
3
- import { a as RtfDiagnosticSink, c as RtfNotAnRtfDocumentError, d as RtfWriteError, i as RtfDiagnosticSeverity, l as RtfParseError, n as RtfDiagnostic, o as RtfInputTooLargeError, r as RtfDiagnosticCodes, s as RtfNestingLimitExceededError, t as NOOP_RTF_DIAGNOSTIC_SINK, u as RtfUnsupportedDocumentKindError } from "./diagnostics-DdDFJLNO.js";
3
+ import { a as RtfDiagnosticSink, c as RtfNotAnRtfDocumentError, d as RtfWriteError, i as RtfDiagnosticSeverity, l as RtfParseError, n as RtfDiagnostic, o as RtfInputTooLargeError, r as RtfDiagnosticCodes, s as RtfNestingLimitExceededError, t as NOOP_RTF_DIAGNOSTIC_SINK, u as RtfUnsupportedDocumentKindError } from "./diagnostics-BgG_KAiN.js";
4
4
  import { DEFAULT_MAX_GROUP_DEPTH, DEFAULT_MAX_INPUT_BYTES, ReadRtfOptions, RtfLineEnding, WriteRtfOptions } from "./options.js";
5
5
  import { ReadRtfContentResult, ReadRtfResult, readRtf, readRtfContent } from "./read.js";
6
6
  import { writeRtf, writeRtfContent } from "./write.js";
@@ -1,4 +1,4 @@
1
- import { a as RtfDiagnosticSink } from "./diagnostics-DdDFJLNO.cjs";
1
+ import { a as RtfDiagnosticSink } from "./diagnostics-BgG_KAiN.cjs";
2
2
  //#region src/options.d.ts
3
3
  declare const DEFAULT_MAX_INPUT_BYTES: number;
4
4
  declare const DEFAULT_MAX_GROUP_DEPTH = 256;
package/dist/options.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as RtfDiagnosticSink } from "./diagnostics-DdDFJLNO.js";
1
+ import { a as RtfDiagnosticSink } from "./diagnostics-BgG_KAiN.js";
2
2
  //#region src/options.d.ts
3
3
  declare const DEFAULT_MAX_INPUT_BYTES: number;
4
4
  declare const DEFAULT_MAX_GROUP_DEPTH = 256;
package/dist/read.cjs CHANGED
@@ -2,9 +2,11 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_base64 = require("./base64.cjs");
3
3
  const require_diagnostics = require("./diagnostics.cjs");
4
4
  const require_bytes = require("./bytes.cjs");
5
+ const require_units = require("./units.cjs");
6
+ const require_cell_format = require("./cell-format.cjs");
7
+ const require_constructs = require("./constructs.cjs");
5
8
  const require_codepage = require("./codepage.cjs");
6
9
  const require_group = require("./group.cjs");
7
- const require_units = require("./units.cjs");
8
10
  const require_header = require("./header.cjs");
9
11
  const require_list_id = require("./list-id.cjs");
10
12
  require("./options.cjs");
@@ -43,8 +45,8 @@ const DESTINATION_KINDS = /* @__PURE__ */ new Map([
43
45
  ["atndate", "skip"],
44
46
  ["atnparent", "skip"],
45
47
  ["atnicn", "skip"],
46
- ["bkmkstart", "skip"],
47
- ["bkmkend", "skip"],
48
+ ["bkmkstart", "bookmarkStart"],
49
+ ["bkmkend", "bookmarkEnd"],
48
50
  ["object", "skip"],
49
51
  ["objdata", "skip"],
50
52
  ["objclass", "skip"],
@@ -129,6 +131,12 @@ const ALIGNMENTS = /* @__PURE__ */ new Map([
129
131
  ["qj", "justify"]
130
132
  ]);
131
133
  const PICTURE_FORMATS = /* @__PURE__ */ new Map([["pngblip", "png"], ["jpegblip", "jpeg"]]);
134
+ const SECTION_BREAK_TYPES = /* @__PURE__ */ new Map([
135
+ ["sbknone", "continuous"],
136
+ ["sbkpage", void 0],
137
+ ["sbkeven", "evenPage"],
138
+ ["sbkodd", "oddPage"]
139
+ ]);
132
140
  function defaultCharacterState() {
133
141
  return {
134
142
  bold: false,
@@ -138,7 +146,8 @@ function defaultCharacterState() {
138
146
  hidden: false,
139
147
  fontIndex: void 0,
140
148
  sizeHalfPoints: 24,
141
- colorIndex: void 0
149
+ colorIndex: void 0,
150
+ revision: require_constructs.NO_REVISION
142
151
  };
143
152
  }
144
153
  function defaultParagraphState() {
@@ -166,6 +175,7 @@ function cloneGroupState(state) {
166
175
  para: { ...state.para },
167
176
  field: state.field,
168
177
  picture: state.picture,
178
+ bookmark: state.bookmark,
169
179
  inUnicodeWrapper: state.inUnicodeWrapper
170
180
  };
171
181
  }
@@ -178,7 +188,8 @@ function runKey(char, fontName, color, hyperlink) {
178
188
  fontName ?? "",
179
189
  String(char.sizeHalfPoints),
180
190
  color === void 0 ? "" : `${String(color.r)},${String(color.g)},${String(color.b)}`,
181
- hyperlink ?? ""
191
+ hyperlink ?? "",
192
+ JSON.stringify(char.revision)
182
193
  ].join("|");
183
194
  }
184
195
  const HYPERLINK_TARGET = /HYPERLINK\s+"([^"]*)"/i;
@@ -218,6 +229,28 @@ function skipUnicodeFallback(tokens, from, count) {
218
229
  textOffset
219
230
  };
220
231
  }
232
+ function insertConstructMarkers(blocks, extents) {
233
+ if (extents.length === 0) return [...blocks];
234
+ const ordered = [...extents].sort((left, right) => left.startIndex - right.startIndex || right.endIndex - left.endIndex);
235
+ const closing = [...ordered].reverse();
236
+ const out = [];
237
+ for (let index = 0; index <= blocks.length; index += 1) {
238
+ for (const extent of closing) if (extent.endIndex === index) out.push({ kind: "constructEnd" });
239
+ for (const extent of ordered) if (extent.startIndex === index) out.push({
240
+ kind: "constructStart",
241
+ descriptor: extent.descriptor
242
+ });
243
+ const block = blocks[index];
244
+ if (block !== void 0) out.push(block);
245
+ }
246
+ return out;
247
+ }
248
+ function horizontalSpanAt(definitions, index) {
249
+ if (definitions[index]?.horizontalMergeFirst !== true) return 1;
250
+ let span = 1;
251
+ while (definitions[index + span]?.horizontalMergeContinuation === true) span += 1;
252
+ return span;
253
+ }
221
254
  var ContentBuilder = class {
222
255
  header;
223
256
  sink;
@@ -227,16 +260,63 @@ var ContentBuilder = class {
227
260
  pendingRunKey;
228
261
  pendingRunText = "";
229
262
  pendingRunFields = {};
263
+ runProvenance = [];
264
+ pendingRunProvenance = [];
230
265
  tableRows = [];
231
266
  tableColumnRights = [];
232
267
  rowCells = [];
233
268
  cellBlocks = [];
234
269
  pendingCellRights = [];
270
+ pendingCellDefinitions = [];
271
+ pendingCell = require_cell_format.newPendingCell();
235
272
  rowLeftTwips = 0;
273
+ paragraphSerial = 0;
274
+ openBookmarks = /* @__PURE__ */ new Map();
275
+ sectionBlockExtents = [];
276
+ cellBlockExtents = [];
277
+ pendingRunConstructs = [];
278
+ closingBookmarks = [];
236
279
  constructor(header, sink) {
237
280
  this.header = header;
238
281
  this.sink = sink;
239
282
  }
283
+ startBookmark(bookmark, para) {
284
+ this.flushRun();
285
+ const name = bookmark.name;
286
+ if (name.length === 0) return;
287
+ this.openBookmarks.set(name, {
288
+ descriptor: require_constructs.bookmarkAnchorDescriptor(name, bookmark.columnFirst === void 0 && bookmark.columnLast === void 0 ? void 0 : {
289
+ first: bookmark.columnFirst,
290
+ last: bookmark.columnLast
291
+ }),
292
+ paragraphSerial: this.paragraphSerial,
293
+ runIndex: this.runs.length,
294
+ inTable: para.inTable,
295
+ blockIndex: void 0
296
+ });
297
+ }
298
+ endBookmark(name) {
299
+ this.flushRun();
300
+ const open = this.openBookmarks.get(name);
301
+ if (open === void 0) {
302
+ this.sink({
303
+ code: require_diagnostics.RtfDiagnosticCodes.BOOKMARK_UNPAIRED,
304
+ severity: "warning",
305
+ message: `a \\bkmkend named '${name}' has no matching \\bkmkstart, so no anchor construct is produced for it`
306
+ });
307
+ return;
308
+ }
309
+ this.openBookmarks.delete(name);
310
+ if (open.paragraphSerial === this.paragraphSerial) {
311
+ this.pendingRunConstructs.push({
312
+ descriptor: open.descriptor,
313
+ startRun: open.runIndex,
314
+ endRun: this.runs.length
315
+ });
316
+ return;
317
+ }
318
+ this.closingBookmarks.push(open);
319
+ }
240
320
  appendText(text, char, hyperlink) {
241
321
  if (text.length === 0 || char.hidden) return;
242
322
  const fontName = char.fontIndex === void 0 ? void 0 : this.header.fonts.get(char.fontIndex)?.name;
@@ -246,32 +326,73 @@ var ContentBuilder = class {
246
326
  this.flushRun();
247
327
  this.pendingRunKey = key;
248
328
  this.pendingRunFields = buildRunFields(char, fontName, color, hyperlink);
329
+ this.pendingRunProvenance = require_constructs.provenanceDescriptors(char.revision, this.header.revisionAuthors);
249
330
  }
250
331
  this.pendingRunText += text;
251
332
  }
252
333
  flushRun() {
253
- if (this.pendingRunText.length > 0) this.runs.push({
254
- ...this.pendingRunFields,
255
- text: this.pendingRunText
256
- });
334
+ if (this.pendingRunText.length > 0) {
335
+ this.runs.push({
336
+ ...this.pendingRunFields,
337
+ text: this.pendingRunText
338
+ });
339
+ this.runProvenance.push(this.pendingRunProvenance);
340
+ }
257
341
  this.pendingRunText = "";
258
342
  this.pendingRunKey = void 0;
259
343
  this.pendingRunFields = {};
344
+ this.pendingRunProvenance = [];
260
345
  }
261
346
  endParagraph(para, force) {
262
347
  this.flushRun();
263
348
  if (!force && this.runs.length === 0) return;
264
349
  const target = para.inTable ? this.cellBlocks : this.blocks;
265
350
  if (!para.inTable) this.closeTable();
351
+ const blockIndex = target.length;
266
352
  target.push(this.buildParagraph(para));
267
353
  this.runs = [];
354
+ this.runProvenance = [];
355
+ this.resolveBookmarkPositions(para, blockIndex);
356
+ this.paragraphSerial += 1;
357
+ }
358
+ resolveBookmarkPositions(para, blockIndex) {
359
+ for (const open of this.openBookmarks.values()) if (open.blockIndex === void 0 && open.paragraphSerial === this.paragraphSerial) open.blockIndex = blockIndex;
360
+ this.flushClosingBookmarks(para.inTable, blockIndex + 1);
361
+ }
362
+ flushClosingBookmarks(inTable, endIndex) {
363
+ if (this.closingBookmarks.length === 0) return;
364
+ const target = inTable ? this.cellBlockExtents : this.sectionBlockExtents;
365
+ for (const closing of this.closingBookmarks) {
366
+ if (closing.inTable !== inTable) {
367
+ this.sink({
368
+ code: require_diagnostics.RtfDiagnosticCodes.BOOKMARK_UNPAIRED,
369
+ severity: "warning",
370
+ message: `the bookmark '${closing.descriptor.name}' spans a table cell boundary; a construct extent cannot straddle two block lists, so no anchor construct is produced for it`
371
+ });
372
+ continue;
373
+ }
374
+ target.push({
375
+ descriptor: closing.descriptor,
376
+ startIndex: closing.blockIndex ?? Math.max(0, endIndex - 1),
377
+ endIndex
378
+ });
379
+ }
380
+ this.closingBookmarks = [];
381
+ }
382
+ takeRunConstructs() {
383
+ const extents = [...this.pendingRunConstructs, ...require_constructs.coalesceRunConstructs(this.runProvenance)].filter((extent) => extent.endRun <= this.runs.length);
384
+ this.pendingRunConstructs = [];
385
+ this.runProvenance = [];
386
+ return extents.sort((left, right) => left.startRun - right.startRun || left.endRun - right.endRun);
268
387
  }
269
388
  buildParagraph(para) {
270
389
  const style = para.styleIndex === void 0 ? void 0 : this.header.styles.get(para.styleIndex);
271
390
  const headingLevel = para.outlineLevel === void 0 ? style?.headingLevel : para.outlineLevel + 1;
391
+ const constructs = this.takeRunConstructs();
272
392
  const paragraph = {
273
393
  kind: "paragraph",
274
- runs: this.runs
394
+ runs: this.runs,
395
+ ...constructs.length === 0 ? {} : { constructs }
275
396
  };
276
397
  const withStyle = style?.name === void 0 || style.name.length === 0 ? paragraph : {
277
398
  ...paragraph,
@@ -315,18 +436,27 @@ var ContentBuilder = class {
315
436
  }
316
437
  startRowDefinition() {
317
438
  this.pendingCellRights = [];
439
+ this.pendingCellDefinitions = [];
440
+ this.pendingCell = require_cell_format.newPendingCell();
318
441
  this.rowLeftTwips = 0;
319
442
  }
320
443
  setRowLeft(twips) {
321
444
  this.rowLeftTwips = twips;
322
445
  }
446
+ applyCellDefinition(name, param) {
447
+ return require_cell_format.applyCellDefinitionControlWord(name, param, this.pendingCell);
448
+ }
323
449
  addCellBoundary(rightTwips) {
324
450
  this.pendingCellRights.push(rightTwips);
451
+ this.pendingCellDefinitions.push(this.pendingCell);
452
+ this.pendingCell = require_cell_format.newPendingCell();
325
453
  }
326
454
  endCell(para) {
327
455
  this.endParagraph(para, false);
328
- this.rowCells.push({ blocks: this.cellBlocks });
456
+ this.flushClosingBookmarks(true, this.cellBlocks.length);
457
+ this.rowCells.push({ blocks: insertConstructMarkers(this.cellBlocks, this.cellBlockExtents) });
329
458
  this.cellBlocks = [];
459
+ this.cellBlockExtents = [];
330
460
  }
331
461
  endRow(para) {
332
462
  if (this.cellBlocks.length > 0 || this.pendingRunText.length > 0) this.endCell(para);
@@ -338,21 +468,75 @@ var ContentBuilder = class {
338
468
  });
339
469
  return;
340
470
  }
341
- this.tableRows.push({ cells: this.rowCells });
471
+ this.tableRows.push({
472
+ cells: this.rowCells,
473
+ definitions: this.pendingCellDefinitions
474
+ });
342
475
  if (this.tableColumnRights.length === 0) this.tableColumnRights = [...this.pendingCellRights];
343
476
  this.rowCells = [];
344
477
  }
345
478
  closeTable() {
346
479
  if (this.tableRows.length === 0) return;
347
- const columnCount = Math.max(...this.tableRows.map((row) => row.cells.length));
480
+ const rows = this.resolveRows();
481
+ const columnCount = Math.max(this.tableColumnRights.length, ...rows.map((row) => row.cells.reduce((total, cell) => total + (cell.colSpan ?? 1), 0)));
348
482
  this.blocks.push({
349
483
  kind: "table",
350
- rows: this.tableRows,
484
+ rows,
351
485
  columnWidthsPt: this.columnWidths(columnCount)
352
486
  });
353
487
  this.tableRows = [];
354
488
  this.tableColumnRights = [];
355
489
  }
490
+ resolveRows() {
491
+ const rows = this.tableRows;
492
+ const columnIndices = rows.map((row) => {
493
+ const indices = [];
494
+ let column = 0;
495
+ for (const [index] of row.cells.entries()) {
496
+ indices.push(column);
497
+ column += horizontalSpanAt(row.definitions, index);
498
+ }
499
+ return indices;
500
+ });
501
+ return rows.map((row, rowIndex) => ({ cells: row.cells.map((cell, cellIndex) => ({
502
+ cell,
503
+ cellIndex
504
+ })).filter(({ cellIndex }) => row.definitions[cellIndex]?.horizontalMergeContinuation !== true).map(({ cell, cellIndex }) => {
505
+ const definition = row.definitions[cellIndex];
506
+ if (definition?.verticalMergeContinuation === true) return { blocks: [] };
507
+ const colSpan = horizontalSpanAt(row.definitions, cellIndex);
508
+ const column = columnIndices[rowIndex]?.[cellIndex];
509
+ let rowSpan = 1;
510
+ if (definition?.verticalMergeFirst === true && column !== void 0) for (let next = rowIndex + 1; next < rows.length; next += 1) {
511
+ const matchIndex = columnIndices[next]?.indexOf(column) ?? -1;
512
+ if ((matchIndex === -1 ? void 0 : rows[next]?.definitions[matchIndex])?.verticalMergeContinuation !== true) break;
513
+ rowSpan += 1;
514
+ }
515
+ const borders = this.resolveBorders(definition);
516
+ const background = definition?.backgroundIndex === void 0 ? void 0 : this.header.colors[definition.backgroundIndex];
517
+ return {
518
+ blocks: cell.blocks,
519
+ ...colSpan > 1 ? { colSpan } : {},
520
+ ...rowSpan > 1 ? { rowSpan } : {},
521
+ ...background === void 0 ? {} : { background },
522
+ ...borders === void 0 ? {} : { borders }
523
+ };
524
+ }) }));
525
+ }
526
+ resolveBorders(definition) {
527
+ if (definition === void 0) return;
528
+ const colorAt = (index) => this.header.colors[index];
529
+ const present = [
530
+ "top",
531
+ "left",
532
+ "bottom",
533
+ "right"
534
+ ].map((side) => {
535
+ const pending = definition.borders[side];
536
+ return [side, pending === void 0 ? void 0 : require_cell_format.resolveBorder(pending, colorAt)];
537
+ }).filter((entry) => entry[1] !== void 0);
538
+ return present.length === 0 ? void 0 : Object.fromEntries(present);
539
+ }
356
540
  columnWidths(columnCount) {
357
541
  const rights = this.tableColumnRights;
358
542
  const widths = [];
@@ -376,22 +560,36 @@ var ContentBuilder = class {
376
560
  this.flushRun();
377
561
  this.blocks.push(block);
378
562
  }
379
- endSection(pageSize, margins, para) {
563
+ endSection(section, para) {
380
564
  this.endParagraph(para, false);
381
565
  this.closeTable();
382
- if (this.blocks.length === 0 && this.sections.length > 0) return;
566
+ this.flushClosingBookmarks(false, this.blocks.length);
567
+ this.reportUnclosedBookmarks();
568
+ const blocks = insertConstructMarkers(this.blocks, this.sectionBlockExtents);
569
+ this.sectionBlockExtents = [];
570
+ if (blocks.length === 0 && this.sections.length > 0) {
571
+ this.blocks = [];
572
+ return;
573
+ }
383
574
  this.sections.push({
384
- pageSize,
385
- margins,
386
- blocks: this.blocks
575
+ ...sectionGeometry(section),
576
+ ...section.breakType === void 0 ? {} : { breakType: section.breakType },
577
+ blocks
387
578
  });
388
579
  this.blocks = [];
389
580
  }
390
- finish(metadata, pageSize, margins, para) {
391
- this.endSection(pageSize, margins, para);
581
+ reportUnclosedBookmarks() {
582
+ for (const open of this.openBookmarks.values()) this.sink({
583
+ code: require_diagnostics.RtfDiagnosticCodes.BOOKMARK_UNPAIRED,
584
+ severity: "warning",
585
+ message: `the bookmark '${open.descriptor.name}' has no matching \\bkmkend within its own block flow, so no anchor construct is produced for it`
586
+ });
587
+ this.openBookmarks.clear();
588
+ }
589
+ finish(metadata, section, para) {
590
+ this.endSection(section, para);
392
591
  if (this.sections.length === 0) this.sections.push({
393
- pageSize,
394
- margins,
592
+ ...sectionGeometry(section),
395
593
  blocks: []
396
594
  });
397
595
  return {
@@ -401,6 +599,31 @@ var ContentBuilder = class {
401
599
  };
402
600
  }
403
601
  };
602
+ function sectionGeometry(section) {
603
+ return {
604
+ pageSize: {
605
+ widthPt: require_units.twipsToPoints(section.paperWidthTwips),
606
+ heightPt: require_units.twipsToPoints(section.paperHeightTwips)
607
+ },
608
+ margins: {
609
+ topPt: require_units.twipsToPoints(section.marginTopTwips),
610
+ rightPt: require_units.twipsToPoints(section.marginRightTwips),
611
+ bottomPt: require_units.twipsToPoints(section.marginBottomTwips),
612
+ leftPt: require_units.twipsToPoints(section.marginLeftTwips)
613
+ }
614
+ };
615
+ }
616
+ function defaultSectionState(header) {
617
+ return {
618
+ paperWidthTwips: header.page.paperWidthTwips,
619
+ paperHeightTwips: header.page.paperHeightTwips,
620
+ marginLeftTwips: header.page.marginLeftTwips,
621
+ marginRightTwips: header.page.marginRightTwips,
622
+ marginTopTwips: header.page.marginTopTwips,
623
+ marginBottomTwips: header.page.marginBottomTwips,
624
+ breakType: void 0
625
+ };
626
+ }
404
627
  function buildRunFields(char, fontName, color, hyperlink) {
405
628
  return {
406
629
  ...char.bold ? { bold: true } : {},
@@ -493,16 +716,7 @@ function readRtfDetail(input, options) {
493
716
  assertRtfHeaderPresent(tokens);
494
717
  const header = require_header.readRtfHeader(tokens, sink);
495
718
  const builder = new ContentBuilder(header, sink);
496
- const pageSize = {
497
- widthPt: require_units.twipsToPoints(header.page.paperWidthTwips),
498
- heightPt: require_units.twipsToPoints(header.page.paperHeightTwips)
499
- };
500
- const margins = {
501
- topPt: require_units.twipsToPoints(header.page.marginTopTwips),
502
- rightPt: require_units.twipsToPoints(header.page.marginRightTwips),
503
- bottomPt: require_units.twipsToPoints(header.page.marginBottomTwips),
504
- leftPt: require_units.twipsToPoints(header.page.marginLeftTwips)
505
- };
719
+ const section = defaultSectionState(header);
506
720
  const root = {
507
721
  destination: "body",
508
722
  uc: 1,
@@ -510,6 +724,7 @@ function readRtfDetail(input, options) {
510
724
  para: defaultParagraphState(),
511
725
  field: void 0,
512
726
  picture: void 0,
727
+ bookmark: void 0,
513
728
  inUnicodeWrapper: false
514
729
  };
515
730
  const stack = [root];
@@ -530,7 +745,14 @@ function readRtfDetail(input, options) {
530
745
  builder.appendText(text, state.char, hyperlink);
531
746
  return;
532
747
  }
533
- if (state.destination === "fieldInstruction" && state.field !== void 0) state.field.instruction += text;
748
+ if (state.destination === "fieldInstruction" && state.field !== void 0) {
749
+ state.field.instruction += text;
750
+ return;
751
+ }
752
+ if ((state.destination === "bookmarkStart" || state.destination === "bookmarkEnd") && state.bookmark !== void 0) {
753
+ state.bookmark.name += text;
754
+ return;
755
+ }
534
756
  };
535
757
  let index = 0;
536
758
  let textOffset = 0;
@@ -568,6 +790,11 @@ function readRtfDetail(input, options) {
568
790
  child.destination = kind;
569
791
  if (head.destination === "field") child.field = { instruction: "" };
570
792
  if (kind === "picture") child.picture = defaultPictureState();
793
+ if (kind === "bookmarkStart" || kind === "bookmarkEnd") child.bookmark = {
794
+ name: "",
795
+ columnFirst: void 0,
796
+ columnLast: void 0
797
+ };
571
798
  index = head.contentStart;
572
799
  } else index += 1;
573
800
  stack.push(child);
@@ -581,6 +808,14 @@ function readRtfDetail(input, options) {
581
808
  const image = buildPicture(state.picture, sink);
582
809
  if (image !== void 0) builder.addBlock(image);
583
810
  }
811
+ if (state.bookmark !== void 0) {
812
+ const bookmark = {
813
+ ...state.bookmark,
814
+ name: state.bookmark.name.trim()
815
+ };
816
+ if (state.destination === "bookmarkStart") builder.startBookmark(bookmark, state.para);
817
+ else if (state.destination === "bookmarkEnd") builder.endBookmark(bookmark.name);
818
+ }
584
819
  if (stack.length > 1) {
585
820
  stack.pop();
586
821
  const parent = stack[stack.length - 1];
@@ -643,7 +878,7 @@ function readRtfDetail(input, options) {
643
878
  continue;
644
879
  }
645
880
  flushBytes();
646
- applyControlWord(token.name, token.param, state, builder, header, sink);
881
+ applyControlWord(token.name, token.param, state, builder, header, section, sink);
647
882
  index += 1;
648
883
  }
649
884
  flushBytes();
@@ -653,7 +888,7 @@ function readRtfDetail(input, options) {
653
888
  message: `${String(stack.length - 1)} group(s) were still open at the end of the input; each is treated as closing there`
654
889
  });
655
890
  return {
656
- document: builder.finish(header.metadata, pageSize, margins, root.para),
891
+ document: builder.finish(header.metadata, section, root.para),
657
892
  diagnostics
658
893
  };
659
894
  }
@@ -730,6 +965,78 @@ function applyCharacterControlWord(name, param, state, header) {
730
965
  case "uc":
731
966
  if (param !== void 0 && param >= 0) state.uc = param;
732
967
  return true;
968
+ case "revised":
969
+ state.char.revision = {
970
+ ...state.char.revision,
971
+ revised: toggleValue(param)
972
+ };
973
+ return true;
974
+ case "revauth":
975
+ state.char.revision = {
976
+ ...state.char.revision,
977
+ revisedAuthor: param
978
+ };
979
+ return true;
980
+ case "revdttm":
981
+ state.char.revision = {
982
+ ...state.char.revision,
983
+ revisedDateTime: param
984
+ };
985
+ return true;
986
+ case "deleted":
987
+ state.char.revision = {
988
+ ...state.char.revision,
989
+ deleted: toggleValue(param)
990
+ };
991
+ return true;
992
+ case "revauthdel":
993
+ state.char.revision = {
994
+ ...state.char.revision,
995
+ deletedAuthor: param
996
+ };
997
+ return true;
998
+ case "revdttmdel":
999
+ state.char.revision = {
1000
+ ...state.char.revision,
1001
+ deletedDateTime: param
1002
+ };
1003
+ return true;
1004
+ case "mvf":
1005
+ state.char.revision = {
1006
+ ...state.char.revision,
1007
+ moved: toggleValue(param) ? "moveFrom" : void 0
1008
+ };
1009
+ return true;
1010
+ case "mvt":
1011
+ state.char.revision = {
1012
+ ...state.char.revision,
1013
+ moved: toggleValue(param) ? "moveTo" : void 0
1014
+ };
1015
+ return true;
1016
+ case "mvauth":
1017
+ state.char.revision = {
1018
+ ...state.char.revision,
1019
+ movedAuthor: param
1020
+ };
1021
+ return true;
1022
+ case "mvdate":
1023
+ state.char.revision = {
1024
+ ...state.char.revision,
1025
+ movedDateTime: param
1026
+ };
1027
+ return true;
1028
+ case "crauth":
1029
+ state.char.revision = {
1030
+ ...state.char.revision,
1031
+ formatAuthor: param
1032
+ };
1033
+ return true;
1034
+ case "crdate":
1035
+ state.char.revision = {
1036
+ ...state.char.revision,
1037
+ formatDateTime: param
1038
+ };
1039
+ return true;
733
1040
  default:
734
1041
  if (name.startsWith("ul") && name !== "ulc") {
735
1042
  state.char.underline = toggleValue(param);
@@ -788,7 +1095,48 @@ function applyParagraphControlWord(name, param, state) {
788
1095
  default: return false;
789
1096
  }
790
1097
  }
791
- function applyStructureControlWord(name, param, state, builder, sink) {
1098
+ function applySectionControlWord(name, param, section, header, sink) {
1099
+ if (name === "sectd") {
1100
+ Object.assign(section, defaultSectionState(header));
1101
+ return true;
1102
+ }
1103
+ if (name === "sbkcol") {
1104
+ sink({
1105
+ code: require_diagnostics.RtfDiagnosticCodes.SECTION_BREAK_UNREPRESENTED,
1106
+ severity: "info",
1107
+ message: "\\sbkcol starts the section at a new column; ContentSection.breakType names page-level breaks only, so the break kind is dropped and the section itself is kept"
1108
+ });
1109
+ section.breakType = void 0;
1110
+ return true;
1111
+ }
1112
+ if (SECTION_BREAK_TYPES.has(name)) {
1113
+ section.breakType = SECTION_BREAK_TYPES.get(name);
1114
+ return true;
1115
+ }
1116
+ if (param === void 0) return false;
1117
+ switch (name) {
1118
+ case "pgwsxn":
1119
+ section.paperWidthTwips = param;
1120
+ return true;
1121
+ case "pghsxn":
1122
+ section.paperHeightTwips = param;
1123
+ return true;
1124
+ case "marglsxn":
1125
+ section.marginLeftTwips = param;
1126
+ return true;
1127
+ case "margrsxn":
1128
+ section.marginRightTwips = param;
1129
+ return true;
1130
+ case "margtsxn":
1131
+ section.marginTopTwips = param;
1132
+ return true;
1133
+ case "margbsxn":
1134
+ section.marginBottomTwips = param;
1135
+ return true;
1136
+ default: return false;
1137
+ }
1138
+ }
1139
+ function applyStructureControlWord(name, param, state, builder, section, sink) {
792
1140
  switch (name) {
793
1141
  case "par":
794
1142
  builder.endParagraph(state.para, true);
@@ -824,20 +1172,29 @@ function applyStructureControlWord(name, param, state, builder, sink) {
824
1172
  return true;
825
1173
  case "sect":
826
1174
  builder.endParagraph(state.para, true);
1175
+ builder.endSection(section, state.para);
827
1176
  return true;
828
- case "sectd": return true;
829
1177
  default: return false;
830
1178
  }
831
1179
  }
832
- function applyControlWord(name, param, state, builder, header, sink) {
1180
+ function applyControlWord(name, param, state, builder, header, section, sink) {
833
1181
  const picture = state.picture;
834
1182
  if (state.destination === "picture" && picture !== void 0) {
835
1183
  applyPictureControlWord(name, param, picture);
836
1184
  return;
837
1185
  }
1186
+ const bookmark = state.bookmark;
1187
+ if (bookmark !== void 0 && state.destination === "bookmarkStart") {
1188
+ if (name === "bkmkcolf") bookmark.columnFirst = param;
1189
+ else if (name === "bkmkcoll") bookmark.columnLast = param;
1190
+ return;
1191
+ }
1192
+ if (state.destination === "bookmarkEnd") return;
838
1193
  if (applyCharacterControlWord(name, param, state, header)) return;
1194
+ if (builder.applyCellDefinition(name, param)) return;
839
1195
  if (applyParagraphControlWord(name, param, state)) return;
840
- applyStructureControlWord(name, param, state, builder, sink);
1196
+ if (applySectionControlWord(name, param, section, header, sink)) return;
1197
+ applyStructureControlWord(name, param, state, builder, section, sink);
841
1198
  }
842
1199
  function readRtfContent(input, options = {}) {
843
1200
  return readRtfDetail(typeof input === "string" ? require_bytes.rtfBytesFromLatin1(input) : input, options);