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/write.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { base64ToBytes, bytesToHex } from "./base64.js";
|
|
2
2
|
import { RtfDiagnosticCodes, RtfUnsupportedDocumentKindError } from "./diagnostics.js";
|
|
3
3
|
import { pointsToHalfPoints, pointsToTwips } from "./units.js";
|
|
4
|
+
import { borderControlWords } from "./cell-format.js";
|
|
5
|
+
import { bookmarkResidueControlWords, dttmFromIso, isBookmarkAnchor } from "./constructs.js";
|
|
4
6
|
import { parseRtfListNumId } from "./list-id.js";
|
|
5
7
|
import { clampHeadingLevel, colorToRgbHex, flattenTree } from "document-schema.js";
|
|
6
8
|
//#region src/write.ts
|
|
@@ -11,28 +13,47 @@ const LIST_MARKER_HANG_TWIPS = 360;
|
|
|
11
13
|
const BULLET_LEVEL_TEXT = "\\'01\\u183 ?";
|
|
12
14
|
const ARABIC_LEVEL_TEXT = "\\'02\\'00.";
|
|
13
15
|
const OUTPUT_CODEPAGE = 1252;
|
|
16
|
+
const SECTION_BREAK_CONTROL_WORDS = /* @__PURE__ */ new Map([
|
|
17
|
+
["continuous", "\\sbknone"],
|
|
18
|
+
["evenPage", "\\sbkeven"],
|
|
19
|
+
["oddPage", "\\sbkodd"]
|
|
20
|
+
]);
|
|
14
21
|
const ALIGNMENT_CONTROL_WORDS = /* @__PURE__ */ new Map([
|
|
15
22
|
["left", "\\ql"],
|
|
16
23
|
["center", "\\qc"],
|
|
17
24
|
["right", "\\qr"],
|
|
18
25
|
["justify", "\\qj"]
|
|
19
26
|
]);
|
|
27
|
+
const UNKNOWN_REVISION_AUTHOR = "Unknown";
|
|
20
28
|
const DEFAULT_FONT_NAME = "Times New Roman";
|
|
21
29
|
function collectTables(document) {
|
|
22
30
|
const fonts = /* @__PURE__ */ new Map([[DEFAULT_FONT_NAME, 0]]);
|
|
23
31
|
const colors = /* @__PURE__ */ new Map();
|
|
24
32
|
const headingStyles = /* @__PURE__ */ new Map();
|
|
25
33
|
const lists = /* @__PURE__ */ new Map();
|
|
34
|
+
const revisionAuthors = /* @__PURE__ */ new Map([[UNKNOWN_REVISION_AUTHOR, 0]]);
|
|
35
|
+
const noteDescriptor = (descriptor) => {
|
|
36
|
+
if (descriptor.kind !== "provenance") return;
|
|
37
|
+
const author = descriptor.author;
|
|
38
|
+
if (author !== void 0 && !revisionAuthors.has(author)) revisionAuthors.set(author, revisionAuthors.size);
|
|
39
|
+
};
|
|
40
|
+
const noteColor = (color) => {
|
|
41
|
+
if (color === void 0) return;
|
|
42
|
+
const hex = colorToRgbHex(color);
|
|
43
|
+
if (!colors.has(hex)) colors.set(hex, colors.size + 1);
|
|
44
|
+
};
|
|
26
45
|
const noteRun = (run) => {
|
|
27
46
|
if (run.fontFamily !== void 0 && !fonts.has(run.fontFamily)) fonts.set(run.fontFamily, fonts.size);
|
|
28
|
-
|
|
29
|
-
const hex = colorToRgbHex(run.color);
|
|
30
|
-
if (!colors.has(hex)) colors.set(hex, colors.size + 1);
|
|
31
|
-
}
|
|
47
|
+
noteColor(run.color);
|
|
32
48
|
};
|
|
33
49
|
const noteBlock = (block) => {
|
|
50
|
+
if (block.kind === "constructStart") {
|
|
51
|
+
noteDescriptor(block.descriptor);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
34
54
|
if (block.kind === "paragraph") {
|
|
35
55
|
for (const run of block.runs) noteRun(run);
|
|
56
|
+
for (const extent of block.constructs ?? []) noteDescriptor(extent.descriptor);
|
|
36
57
|
if (block.headingLevel !== void 0) {
|
|
37
58
|
const level = clampHeadingLevel(block.headingLevel);
|
|
38
59
|
if (!headingStyles.has(level)) headingStyles.set(level, level);
|
|
@@ -50,14 +71,19 @@ function collectTables(document) {
|
|
|
50
71
|
}
|
|
51
72
|
return;
|
|
52
73
|
}
|
|
53
|
-
if (block.kind === "table") for (const row of block.rows) for (const cell of row.cells)
|
|
74
|
+
if (block.kind === "table") for (const row of block.rows) for (const cell of row.cells) {
|
|
75
|
+
noteColor(cell.background);
|
|
76
|
+
for (const side of CELL_BORDER_ORDER) noteColor(cell.borders?.[side]?.color);
|
|
77
|
+
for (const inner of cell.blocks) noteBlock(inner);
|
|
78
|
+
}
|
|
54
79
|
};
|
|
55
80
|
if (document.kind === "wordprocessing") for (const section of document.sections) for (const block of section.blocks) noteBlock(block);
|
|
56
81
|
return {
|
|
57
82
|
fonts,
|
|
58
83
|
colors,
|
|
59
84
|
headingStyles,
|
|
60
|
-
lists
|
|
85
|
+
lists,
|
|
86
|
+
revisionAuthors
|
|
61
87
|
};
|
|
62
88
|
}
|
|
63
89
|
function escapeText(text) {
|
|
@@ -94,11 +120,76 @@ function escapeText(text) {
|
|
|
94
120
|
}
|
|
95
121
|
return out;
|
|
96
122
|
}
|
|
123
|
+
function bookmarkStartGroup(descriptor) {
|
|
124
|
+
if (!isBookmarkAnchor(descriptor)) return "";
|
|
125
|
+
return `{\\*\\bkmkstart${bookmarkResidueControlWords(descriptor)} ${escapeText(descriptor.name)}}`;
|
|
126
|
+
}
|
|
127
|
+
const CHREV_CONTROL_WORDS = {
|
|
128
|
+
insertion: {
|
|
129
|
+
flag: "\\revised",
|
|
130
|
+
author: "revauth",
|
|
131
|
+
date: "revdttm"
|
|
132
|
+
},
|
|
133
|
+
deletion: {
|
|
134
|
+
flag: "\\deleted",
|
|
135
|
+
author: "revauthdel",
|
|
136
|
+
date: "revdttmdel"
|
|
137
|
+
},
|
|
138
|
+
moveFrom: {
|
|
139
|
+
flag: "\\mvf",
|
|
140
|
+
author: "mvauth",
|
|
141
|
+
date: "mvdate"
|
|
142
|
+
},
|
|
143
|
+
moveTo: {
|
|
144
|
+
flag: "\\mvt",
|
|
145
|
+
author: "mvauth",
|
|
146
|
+
date: "mvdate"
|
|
147
|
+
},
|
|
148
|
+
formatChange: {
|
|
149
|
+
flag: "",
|
|
150
|
+
author: "crauth",
|
|
151
|
+
date: "crdate"
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
function revisionsCovering(extents, index) {
|
|
155
|
+
return extents.filter((extent) => extent.descriptor.kind === "provenance" && extent.startRun <= index && index < extent.endRun).map((extent) => extent.descriptor).filter((descriptor) => descriptor.kind === "provenance");
|
|
156
|
+
}
|
|
157
|
+
const CELL_BORDER_ORDER = [
|
|
158
|
+
"top",
|
|
159
|
+
"left",
|
|
160
|
+
"bottom",
|
|
161
|
+
"right"
|
|
162
|
+
];
|
|
163
|
+
function verticalMergeCoverage(table) {
|
|
164
|
+
const covered = table.rows.map((row) => row.cells.map(() => false));
|
|
165
|
+
for (const [rowIndex, row] of table.rows.entries()) for (const [cellIndex, cell] of row.cells.entries()) {
|
|
166
|
+
const rowSpan = cell.rowSpan ?? 1;
|
|
167
|
+
for (let next = 1; next < rowSpan; next += 1) {
|
|
168
|
+
const target = covered[rowIndex + next];
|
|
169
|
+
if (target !== void 0 && cellIndex < target.length) target[cellIndex] = true;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return covered;
|
|
173
|
+
}
|
|
174
|
+
function nameOf(descriptor) {
|
|
175
|
+
return isBookmarkAnchor(descriptor) ? descriptor.name : "";
|
|
176
|
+
}
|
|
177
|
+
function describeConstructGap(descriptor) {
|
|
178
|
+
switch (descriptor.kind) {
|
|
179
|
+
case "contentControl": return "structured-document-tag equivalent; its own \\*\\formfield production is a narrower construct this writer does not yet mint";
|
|
180
|
+
case "provenance": return "block-scoped revision mark: its <chrev> production is a character property, so a tracked change reaches RTF only as a run-level extent";
|
|
181
|
+
case "anchor": return `spelling for a '${descriptor.anchorType}' anchor, whose body would need the note or annotation destination this reader does not place`;
|
|
182
|
+
case "field": return "block-scoped field: a field is a character-stream construct, written from a run's own hyperlink rather than from a block marker";
|
|
183
|
+
case "link": return "block-scoped link; an external target rides ContentRun.hyperlink instead";
|
|
184
|
+
default: return "equivalent construct";
|
|
185
|
+
}
|
|
186
|
+
}
|
|
97
187
|
var RtfWriter = class {
|
|
98
188
|
tables;
|
|
99
189
|
sink;
|
|
100
190
|
lineEnding;
|
|
101
191
|
out = "";
|
|
192
|
+
openConstructs = [];
|
|
102
193
|
constructor(tables, sink, lineEnding) {
|
|
103
194
|
this.tables = tables;
|
|
104
195
|
this.sink = sink;
|
|
@@ -115,10 +206,12 @@ var RtfWriter = class {
|
|
|
115
206
|
}
|
|
116
207
|
writeHeader(document) {
|
|
117
208
|
this.raw(`{\\rtf1\\ansi\\ansicpg${String(OUTPUT_CODEPAGE)}\\deff0\\uc1`);
|
|
209
|
+
this.writeDocumentGeometry(document);
|
|
118
210
|
this.writeFontTable();
|
|
119
211
|
this.writeColorTable();
|
|
120
212
|
this.writeStyleSheet();
|
|
121
213
|
this.writeListTables();
|
|
214
|
+
this.writeRevisionTable();
|
|
122
215
|
this.writeInfoGroup(document);
|
|
123
216
|
this.line("");
|
|
124
217
|
}
|
|
@@ -164,6 +257,12 @@ var RtfWriter = class {
|
|
|
164
257
|
for (const entry of entries) this.raw(`{\\listoverride\\listid${String(1e3 + entry.index)}\\listoverridecount0\\ls${String(entry.index)}}`);
|
|
165
258
|
this.raw("}");
|
|
166
259
|
}
|
|
260
|
+
writeRevisionTable() {
|
|
261
|
+
if (this.tables.revisionAuthors.size <= 1) return;
|
|
262
|
+
this.raw("{\\*\\revtbl");
|
|
263
|
+
for (const [author] of [...this.tables.revisionAuthors].sort((left, right) => left[1] - right[1])) this.raw(`{${escapeText(author)};}`);
|
|
264
|
+
this.raw("}");
|
|
265
|
+
}
|
|
167
266
|
writeInfoGroup(document) {
|
|
168
267
|
const { title, author, subject, keywords } = document.metadata;
|
|
169
268
|
const fields = [];
|
|
@@ -173,9 +272,14 @@ var RtfWriter = class {
|
|
|
173
272
|
if (keywords !== void 0 && keywords.length > 0) fields.push(`{\\keywords ${escapeText(keywords.join("; "))}}`);
|
|
174
273
|
if (fields.length > 0) this.raw(`{\\info${fields.join("")}}`);
|
|
175
274
|
}
|
|
275
|
+
writeDocumentGeometry(document) {
|
|
276
|
+
const first = document.sections[0];
|
|
277
|
+
if (first === void 0) return;
|
|
278
|
+
this.raw(`\\paperw${String(pointsToTwips(first.pageSize.widthPt))}\\paperh${String(pointsToTwips(first.pageSize.heightPt))}\\margl${String(pointsToTwips(first.margins.leftPt))}\\margr${String(pointsToTwips(first.margins.rightPt))}\\margt${String(pointsToTwips(first.margins.topPt))}\\margb${String(pointsToTwips(first.margins.bottomPt))}`);
|
|
279
|
+
}
|
|
176
280
|
writeSection(section, isFirst) {
|
|
177
281
|
if (!isFirst) this.line("\\sect");
|
|
178
|
-
this.line(`\\sectd\\
|
|
282
|
+
this.line(`\\sectd${SECTION_BREAK_CONTROL_WORDS.get(section.breakType ?? "") ?? ""}\\pgwsxn${String(pointsToTwips(section.pageSize.widthPt))}\\pghsxn${String(pointsToTwips(section.pageSize.heightPt))}\\marglsxn${String(pointsToTwips(section.margins.leftPt))}\\margrsxn${String(pointsToTwips(section.margins.rightPt))}\\margtsxn${String(pointsToTwips(section.margins.topPt))}\\margbsxn${String(pointsToTwips(section.margins.bottomPt))}`);
|
|
179
283
|
this.writeBlocks(section.blocks);
|
|
180
284
|
}
|
|
181
285
|
writeBlocks(blocks) {
|
|
@@ -203,24 +307,52 @@ var RtfWriter = class {
|
|
|
203
307
|
});
|
|
204
308
|
return;
|
|
205
309
|
case "constructStart":
|
|
310
|
+
this.openConstruct(block.descriptor);
|
|
311
|
+
return;
|
|
206
312
|
case "constructEnd":
|
|
207
|
-
this.
|
|
208
|
-
code: RtfDiagnosticCodes.CONSTRUCT_UNREPRESENTED,
|
|
209
|
-
severity: "warning",
|
|
210
|
-
message: `a ${block.kind} boundary marker is dropped; this writer emits no RTF construct for the fidelity-construct vocabulary`
|
|
211
|
-
});
|
|
313
|
+
this.closeConstruct();
|
|
212
314
|
return;
|
|
213
315
|
default: return;
|
|
214
316
|
}
|
|
215
317
|
}
|
|
318
|
+
openConstruct(descriptor) {
|
|
319
|
+
if (!isBookmarkAnchor(descriptor)) {
|
|
320
|
+
this.sink({
|
|
321
|
+
code: RtfDiagnosticCodes.CONSTRUCT_UNREPRESENTED,
|
|
322
|
+
severity: "warning",
|
|
323
|
+
message: `a ${descriptor.kind} construct is dropped: RTF has no ${describeConstructGap(descriptor)}`
|
|
324
|
+
});
|
|
325
|
+
this.openConstructs.push(void 0);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
this.openConstructs.push(descriptor.name);
|
|
329
|
+
this.line(bookmarkStartGroup(descriptor));
|
|
330
|
+
}
|
|
331
|
+
closeConstruct() {
|
|
332
|
+
const name = this.openConstructs.pop();
|
|
333
|
+
if (name !== void 0) this.line(`{\\*\\bkmkend ${escapeText(name)}}`);
|
|
334
|
+
}
|
|
216
335
|
writeParagraph(paragraph, inTable) {
|
|
217
336
|
this.raw("\\pard\\plain");
|
|
218
337
|
if (inTable) this.raw("\\intbl");
|
|
219
338
|
this.raw(this.paragraphProperties(paragraph));
|
|
220
339
|
this.raw(" ");
|
|
221
|
-
|
|
340
|
+
const bookmarks = (paragraph.constructs ?? []).filter((extent) => isBookmarkAnchor(extent.descriptor));
|
|
341
|
+
const revisions = (paragraph.constructs ?? []).filter((extent) => extent.descriptor.kind === "provenance");
|
|
342
|
+
for (const [index, run] of paragraph.runs.entries()) {
|
|
343
|
+
this.writeRunBoundaries(bookmarks, index);
|
|
344
|
+
this.writeRun(run, revisionsCovering(revisions, index));
|
|
345
|
+
}
|
|
346
|
+
this.writeRunBoundaries(bookmarks, paragraph.runs.length);
|
|
222
347
|
if (!inTable) this.line("\\par");
|
|
223
348
|
}
|
|
349
|
+
writeRunBoundaries(extents, position) {
|
|
350
|
+
for (const extent of extents) if (extent.endRun === position && extent.startRun !== position) this.raw(`{\\*\\bkmkend ${escapeText(nameOf(extent.descriptor))}}`);
|
|
351
|
+
for (const extent of extents) if (extent.startRun === position) {
|
|
352
|
+
this.raw(bookmarkStartGroup(extent.descriptor));
|
|
353
|
+
if (extent.endRun === position) this.raw(`{\\*\\bkmkend ${escapeText(nameOf(extent.descriptor))}}`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
224
356
|
paragraphProperties(paragraph) {
|
|
225
357
|
let out = "";
|
|
226
358
|
const level = paragraph.headingLevel === void 0 ? void 0 : clampHeadingLevel(paragraph.headingLevel);
|
|
@@ -250,8 +382,8 @@ var RtfWriter = class {
|
|
|
250
382
|
if (paragraph.pageBreakBefore === true) out += "\\pagebb";
|
|
251
383
|
return out;
|
|
252
384
|
}
|
|
253
|
-
writeRun(run) {
|
|
254
|
-
const properties = this.runProperties(run);
|
|
385
|
+
writeRun(run, revisions = []) {
|
|
386
|
+
const properties = this.runProperties(run) + this.revisionProperties(revisions);
|
|
255
387
|
const body = `${properties}${properties.length > 0 ? " " : ""}${escapeText(run.text)}`;
|
|
256
388
|
if (run.hyperlink === void 0) {
|
|
257
389
|
this.raw(`{${body}}`);
|
|
@@ -259,6 +391,19 @@ var RtfWriter = class {
|
|
|
259
391
|
}
|
|
260
392
|
this.raw(`{\\field{\\*\\fldinst{HYPERLINK "${escapeText(run.hyperlink)}"}}{\\fldrslt{${body}}}}`);
|
|
261
393
|
}
|
|
394
|
+
revisionProperties(revisions) {
|
|
395
|
+
let out = "";
|
|
396
|
+
for (const descriptor of revisions) {
|
|
397
|
+
const author = descriptor.author;
|
|
398
|
+
const authorIndex = author === void 0 ? void 0 : this.tables.revisionAuthors.get(author);
|
|
399
|
+
const dttm = descriptor.dateIso === void 0 ? void 0 : dttmFromIso(descriptor.dateIso);
|
|
400
|
+
const words = CHREV_CONTROL_WORDS[descriptor.change];
|
|
401
|
+
out += words.flag;
|
|
402
|
+
if (authorIndex !== void 0) out += `\\${words.author}${String(authorIndex)}`;
|
|
403
|
+
if (dttm !== void 0) out += `\\${words.date}${String(dttm)}`;
|
|
404
|
+
}
|
|
405
|
+
return out;
|
|
406
|
+
}
|
|
262
407
|
runProperties(run) {
|
|
263
408
|
let out = "";
|
|
264
409
|
const fontIndex = run.fontFamily === void 0 ? void 0 : this.tables.fonts.get(run.fontFamily);
|
|
@@ -274,28 +419,51 @@ var RtfWriter = class {
|
|
|
274
419
|
return out;
|
|
275
420
|
}
|
|
276
421
|
writeTable(table) {
|
|
277
|
-
|
|
422
|
+
const covered = verticalMergeCoverage(table);
|
|
423
|
+
for (const [rowIndex, row] of table.rows.entries()) {
|
|
278
424
|
let right = 0;
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
425
|
+
let column = 0;
|
|
426
|
+
const definitions = [];
|
|
427
|
+
const marks = [];
|
|
428
|
+
for (const [cellIndex, cell] of row.cells.entries()) {
|
|
429
|
+
const colSpan = cell.colSpan ?? 1;
|
|
430
|
+
for (let offset = 0; offset < colSpan; offset += 1) {
|
|
431
|
+
right += pointsToTwips(table.columnWidthsPt[column] ?? 0);
|
|
432
|
+
column += 1;
|
|
433
|
+
definitions.push(this.cellDefinition(cell, covered[rowIndex]?.[cellIndex] === true, colSpan > 1 ? offset === 0 ? "mergeFirst" : "mergeContinuation" : "single", right));
|
|
434
|
+
marks.push({
|
|
435
|
+
cell,
|
|
436
|
+
empty: offset > 0
|
|
437
|
+
});
|
|
438
|
+
}
|
|
283
439
|
}
|
|
284
|
-
const rowDefinition = `\\trowd\\trgaph108\\trleft0${
|
|
440
|
+
const rowDefinition = `\\trowd\\trgaph108\\trleft0${definitions.join("")}`;
|
|
285
441
|
this.line(rowDefinition);
|
|
286
|
-
for (const
|
|
287
|
-
|
|
288
|
-
code: RtfDiagnosticCodes.CELL_BORDER_DROPPED,
|
|
289
|
-
severity: "info",
|
|
290
|
-
message: "a table cell's borders or background are dropped; this writer emits cell boundaries only"
|
|
291
|
-
});
|
|
292
|
-
this.writeCellBlocks(cell.blocks);
|
|
442
|
+
for (const mark of marks) {
|
|
443
|
+
this.writeCellBlocks(mark.empty ? [] : mark.cell.blocks);
|
|
293
444
|
this.raw("\\cell");
|
|
294
445
|
}
|
|
295
446
|
this.line(`${rowDefinition}\\row`);
|
|
296
447
|
}
|
|
297
448
|
this.line("\\pard");
|
|
298
449
|
}
|
|
450
|
+
cellDefinition(cell, isVerticalContinuation, horizontal, rightTwips) {
|
|
451
|
+
let out = "";
|
|
452
|
+
if (isVerticalContinuation) out += "\\clvmrg";
|
|
453
|
+
else if ((cell.rowSpan ?? 1) > 1) out += "\\clvmgf";
|
|
454
|
+
if (horizontal === "mergeFirst") out += "\\clmgf";
|
|
455
|
+
else if (horizontal === "mergeContinuation") return `${out}\\clmrg\\cellx${String(rightTwips)}`;
|
|
456
|
+
const borders = cell.borders;
|
|
457
|
+
if (borders !== void 0) for (const side of CELL_BORDER_ORDER) {
|
|
458
|
+
const border = borders[side];
|
|
459
|
+
if (border !== void 0) out += borderControlWords(side, border, colorIndexOf(border.color, this.tables.colors));
|
|
460
|
+
}
|
|
461
|
+
if (cell.background !== void 0) {
|
|
462
|
+
const index = colorIndexOf(cell.background, this.tables.colors);
|
|
463
|
+
if (index !== void 0) out += `\\clcbpat${String(index)}`;
|
|
464
|
+
}
|
|
465
|
+
return `${out}\\cellx${String(rightTwips)}`;
|
|
466
|
+
}
|
|
299
467
|
writeCellBlocks(blocks) {
|
|
300
468
|
const paragraphs = blocks.filter((block) => block.kind === "paragraph");
|
|
301
469
|
if (paragraphs.length === 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rtf-codec",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Hand-written Rich Text Format (RTF 1.9.1) reader and writer against the shared document-schema.js content pivot.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"license": "MIT",
|
|
79
79
|
"packageManager": "pnpm@11.6.0",
|
|
80
80
|
"dependencies": {
|
|
81
|
-
"document-schema.js": "^5.
|
|
81
|
+
"document-schema.js": "^5.5.1",
|
|
82
82
|
"zod": "^4.4.3"
|
|
83
83
|
},
|
|
84
84
|
"devDependencies": {
|