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