rtf-codec 0.0.0 → 1.0.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.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +208 -0
  3. package/dist/base64.cjs +66 -0
  4. package/dist/base64.d.cts +7 -0
  5. package/dist/base64.d.ts +7 -0
  6. package/dist/base64.js +62 -0
  7. package/dist/bytes.cjs +25 -0
  8. package/dist/bytes.d.cts +6 -0
  9. package/dist/bytes.d.ts +6 -0
  10. package/dist/bytes.js +22 -0
  11. package/dist/codec.cjs +29 -0
  12. package/dist/codec.d.cts +2308 -0
  13. package/dist/codec.d.ts +2308 -0
  14. package/dist/codec.js +26 -0
  15. package/dist/codepage.cjs +98 -0
  16. package/dist/codepage.d.cts +10 -0
  17. package/dist/codepage.d.ts +10 -0
  18. package/dist/codepage.js +92 -0
  19. package/dist/diagnostics-DdDFJLNO.d.cts +51 -0
  20. package/dist/diagnostics-DdDFJLNO.d.ts +51 -0
  21. package/dist/diagnostics.cjs +75 -0
  22. package/dist/diagnostics.d.cts +2 -0
  23. package/dist/diagnostics.d.ts +2 -0
  24. package/dist/diagnostics.js +67 -0
  25. package/dist/group.cjs +40 -0
  26. package/dist/group.d.cts +11 -0
  27. package/dist/group.d.ts +11 -0
  28. package/dist/group.js +38 -0
  29. package/dist/header.cjs +378 -0
  30. package/dist/header.d.cts +43 -0
  31. package/dist/header.d.ts +43 -0
  32. package/dist/header.js +376 -0
  33. package/dist/index.cjs +28 -0
  34. package/dist/index.d.cts +8 -0
  35. package/dist/index.d.ts +8 -0
  36. package/dist/index.js +8 -0
  37. package/dist/list-id.cjs +30 -0
  38. package/dist/list-id.d.cts +16 -0
  39. package/dist/list-id.d.ts +16 -0
  40. package/dist/list-id.js +27 -0
  41. package/dist/options.cjs +7 -0
  42. package/dist/options.d.cts +18 -0
  43. package/dist/options.d.ts +18 -0
  44. package/dist/options.js +5 -0
  45. package/dist/read.cjs +854 -0
  46. package/dist/read.d.cts +16 -0
  47. package/dist/read.d.ts +16 -0
  48. package/dist/read.js +852 -0
  49. package/dist/tokenize.cjs +155 -0
  50. package/dist/tokenize.d.cts +25 -0
  51. package/dist/tokenize.d.ts +25 -0
  52. package/dist/tokenize.js +154 -0
  53. package/dist/units.cjs +43 -0
  54. package/dist/units.d.cts +17 -0
  55. package/dist/units.d.ts +17 -0
  56. package/dist/units.js +29 -0
  57. package/dist/write.cjs +364 -0
  58. package/dist/write.d.cts +7 -0
  59. package/dist/write.d.ts +7 -0
  60. package/dist/write.js +362 -0
  61. package/package.json +95 -2
@@ -0,0 +1,378 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_bytes = require("./bytes.cjs");
3
+ const require_codepage = require("./codepage.cjs");
4
+ const require_group = require("./group.cjs");
5
+ const require_units = require("./units.cjs");
6
+ //#region src/header.ts
7
+ const RGB_MAX = 255;
8
+ const FONT_FAMILIES = /* @__PURE__ */ new Set([
9
+ "fnil",
10
+ "froman",
11
+ "fswiss",
12
+ "fmodern",
13
+ "fscript",
14
+ "fdecor",
15
+ "ftech",
16
+ "fbidi"
17
+ ]);
18
+ const HEADING_STYLE_NAME = /^heading[ -]?([1-9])$/i;
19
+ const HEADER_DESTINATIONS = /* @__PURE__ */ new Set([
20
+ "fonttbl",
21
+ "colortbl",
22
+ "stylesheet",
23
+ "listtable",
24
+ "listoverridetable",
25
+ "info",
26
+ "filetbl",
27
+ "revtbl",
28
+ "rsidtbl",
29
+ "generator",
30
+ "pgptbl",
31
+ "themedata",
32
+ "colorschememapping",
33
+ "latentstyles",
34
+ "listtextoverride",
35
+ "xmlnstbl",
36
+ "wgrffmtfilter",
37
+ "datastore",
38
+ "protusertbl",
39
+ "mmathPr",
40
+ "stylerestrictions",
41
+ "userprops"
42
+ ]);
43
+ function collectPlainText(tokens, start, end, codepage, sink) {
44
+ const pending = [];
45
+ let out = "";
46
+ const flush = () => {
47
+ if (pending.length === 0) return;
48
+ out += require_codepage.decodeCodepageBytes(Uint8Array.from(pending), codepage, sink);
49
+ pending.length = 0;
50
+ };
51
+ for (let index = start; index < end; index += 1) {
52
+ const token = tokens[index];
53
+ if (token === void 0) break;
54
+ if (token.kind === "groupStart") {
55
+ flush();
56
+ index = require_group.matchingGroupEnd(tokens, index);
57
+ continue;
58
+ }
59
+ if (token.kind === "text") {
60
+ require_bytes.appendBytes(pending, token.bytes);
61
+ continue;
62
+ }
63
+ if (token.kind === "hex") {
64
+ pending.push(token.byte);
65
+ continue;
66
+ }
67
+ if (token.kind === "controlWord" && token.name === "u") {
68
+ flush();
69
+ const code = token.param;
70
+ if (code !== void 0) out += String.fromCodePoint(code < 0 ? code + 65536 : code);
71
+ }
72
+ }
73
+ flush();
74
+ return out.replace(/;\s*$/, "").trim();
75
+ }
76
+ function parseFontTable(tokens, contentStart, end, documentCodepage, fonts, sink) {
77
+ let index = contentStart;
78
+ let sawBracedEntry = false;
79
+ while (index < end) {
80
+ if (tokens[index]?.kind !== "groupStart") {
81
+ index += 1;
82
+ continue;
83
+ }
84
+ sawBracedEntry = true;
85
+ const entryEnd = Math.min(require_group.matchingGroupEnd(tokens, index), end);
86
+ readFontInfo(tokens, index + 1, entryEnd, documentCodepage, fonts, sink);
87
+ index = entryEnd + 1;
88
+ }
89
+ if (!sawBracedEntry) readFontInfo(tokens, contentStart, end, documentCodepage, fonts, sink);
90
+ }
91
+ function readFontInfo(tokens, start, end, documentCodepage, fonts, sink) {
92
+ let number;
93
+ let family;
94
+ let charsetPage;
95
+ let explicitPage;
96
+ let nameStart = start;
97
+ for (let index = start; index < end; index += 1) {
98
+ const token = tokens[index];
99
+ if (token === void 0) break;
100
+ if (token.kind !== "controlWord") continue;
101
+ if (token.name === "f" && token.param !== void 0) {
102
+ number = token.param;
103
+ nameStart = index + 1;
104
+ continue;
105
+ }
106
+ if (FONT_FAMILIES.has(token.name)) {
107
+ family = token.name.slice(1);
108
+ nameStart = index + 1;
109
+ continue;
110
+ }
111
+ if (token.name === "fcharset" && token.param !== void 0) {
112
+ charsetPage = require_codepage.codepageForFontCharset(token.param);
113
+ nameStart = index + 1;
114
+ continue;
115
+ }
116
+ if (token.name === "cpg" && token.param !== void 0) {
117
+ explicitPage = token.param;
118
+ nameStart = index + 1;
119
+ continue;
120
+ }
121
+ if (token.name === "fprq" || token.name === "fbias") nameStart = index + 1;
122
+ }
123
+ if (number === void 0) return;
124
+ const name = collectPlainText(tokens, nameStart, end, explicitPage ?? charsetPage ?? documentCodepage, sink);
125
+ fonts.set(number, {
126
+ name,
127
+ family,
128
+ codepage: explicitPage ?? charsetPage
129
+ });
130
+ }
131
+ function parseColorTable(tokens, contentStart, end, colors) {
132
+ let red;
133
+ let green;
134
+ let blue;
135
+ const finishEntry = () => {
136
+ colors.push(red === void 0 && green === void 0 && blue === void 0 ? void 0 : {
137
+ r: (red ?? 0) / RGB_MAX,
138
+ g: (green ?? 0) / RGB_MAX,
139
+ b: (blue ?? 0) / RGB_MAX
140
+ });
141
+ red = void 0;
142
+ green = void 0;
143
+ blue = void 0;
144
+ };
145
+ for (let index = contentStart; index < end; index += 1) {
146
+ const token = tokens[index];
147
+ if (token === void 0) break;
148
+ if (token.kind === "controlWord") {
149
+ if (token.name === "red") red = token.param ?? 0;
150
+ else if (token.name === "green") green = token.param ?? 0;
151
+ else if (token.name === "blue") blue = token.param ?? 0;
152
+ continue;
153
+ }
154
+ if (token.kind === "text") {
155
+ for (const byte of token.bytes) if (byte === 59) finishEntry();
156
+ }
157
+ }
158
+ }
159
+ function parseStyleSheet(tokens, contentStart, end, documentCodepage, styles, sink) {
160
+ for (let index = contentStart; index < end; index += 1) {
161
+ if (tokens[index]?.kind !== "groupStart") continue;
162
+ const entryEnd = Math.min(require_group.matchingGroupEnd(tokens, index), end);
163
+ readStyle(tokens, index, entryEnd, documentCodepage, styles, sink);
164
+ index = entryEnd;
165
+ }
166
+ }
167
+ function readStyle(tokens, start, end, documentCodepage, styles, sink) {
168
+ const head = require_group.groupHead(tokens, start);
169
+ if (head.ignorable) return;
170
+ let handle = 0;
171
+ let outlineLevel;
172
+ let nameStart = head.contentStart;
173
+ for (let index = start + 1; index < end; index += 1) {
174
+ const token = tokens[index];
175
+ if (token === void 0) break;
176
+ if (token.kind === "groupStart") {
177
+ index = Math.min(require_group.matchingGroupEnd(tokens, index), end);
178
+ nameStart = index + 1;
179
+ continue;
180
+ }
181
+ if (token.kind !== "controlWord") continue;
182
+ if (token.name === "s" && token.param !== void 0) handle = token.param;
183
+ else if (token.name === "outlinelevel" && token.param !== void 0) outlineLevel = token.param;
184
+ nameStart = index + 1;
185
+ }
186
+ const name = collectPlainText(tokens, nameStart, end, documentCodepage, sink);
187
+ const fromName = HEADING_STYLE_NAME.exec(name)?.[1];
188
+ const headingLevel = outlineLevel === void 0 ? fromName === void 0 ? void 0 : Number(fromName) : outlineLevel + 1;
189
+ styles.set(handle, {
190
+ name,
191
+ headingLevel
192
+ });
193
+ }
194
+ function parseListTable(tokens, contentStart, end, listsById) {
195
+ for (let index = contentStart; index < end; index += 1) {
196
+ if (tokens[index]?.kind !== "groupStart") continue;
197
+ const entryEnd = Math.min(require_group.matchingGroupEnd(tokens, index), end);
198
+ if (require_group.groupHead(tokens, index).destination === "list") readListDefinition(tokens, index, entryEnd, listsById);
199
+ index = entryEnd;
200
+ }
201
+ }
202
+ function readListDefinition(tokens, start, end, listsById) {
203
+ const levels = [];
204
+ let listId;
205
+ for (let index = start + 1; index < end; index += 1) {
206
+ const token = tokens[index];
207
+ if (token === void 0) break;
208
+ if (token.kind === "groupStart") {
209
+ const inner = Math.min(require_group.matchingGroupEnd(tokens, index), end);
210
+ if (require_group.groupHead(tokens, index).destination === "listlevel") levels.push(readListLevel(tokens, index, inner));
211
+ index = inner;
212
+ continue;
213
+ }
214
+ if (token.kind === "controlWord" && token.name === "listid" && token.param !== void 0) listId = token.param;
215
+ }
216
+ if (listId === void 0) return;
217
+ listsById.set(listId, { levels });
218
+ return listId;
219
+ }
220
+ function readListLevel(tokens, start, end) {
221
+ let numberFormat = 0;
222
+ let startAt = 1;
223
+ for (let index = start + 1; index < end; index += 1) {
224
+ const token = tokens[index];
225
+ if (token === void 0) break;
226
+ if (token.kind === "groupStart") {
227
+ index = Math.min(require_group.matchingGroupEnd(tokens, index), end);
228
+ continue;
229
+ }
230
+ if (token.kind !== "controlWord") continue;
231
+ if (token.name === "levelnfc" && token.param !== void 0) numberFormat = token.param;
232
+ else if (token.name === "levelstartat" && token.param !== void 0) startAt = token.param;
233
+ }
234
+ return {
235
+ numberFormat,
236
+ startAt
237
+ };
238
+ }
239
+ function parseListOverrideTable(tokens, contentStart, end, overrides) {
240
+ for (let index = contentStart; index < end; index += 1) {
241
+ if (tokens[index]?.kind !== "groupStart") continue;
242
+ const entryEnd = Math.min(require_group.matchingGroupEnd(tokens, index), end);
243
+ let listId;
244
+ let overrideIndex;
245
+ for (let inner = index + 1; inner < entryEnd; inner += 1) {
246
+ const child = tokens[inner];
247
+ if (child?.kind === "groupStart") {
248
+ inner = Math.min(require_group.matchingGroupEnd(tokens, inner), entryEnd);
249
+ continue;
250
+ }
251
+ if (child?.kind !== "controlWord" || child.param === void 0) continue;
252
+ if (child.name === "listid") listId = child.param;
253
+ else if (child.name === "ls") overrideIndex = child.param;
254
+ }
255
+ if (listId !== void 0 && overrideIndex !== void 0) overrides.set(overrideIndex, listId);
256
+ index = entryEnd;
257
+ }
258
+ }
259
+ function parseInfoGroup(tokens, contentStart, end, codepage, sink) {
260
+ const metadata = {};
261
+ for (let index = contentStart; index < end; index += 1) {
262
+ if (tokens[index]?.kind !== "groupStart") continue;
263
+ const fieldEnd = Math.min(require_group.matchingGroupEnd(tokens, index), end);
264
+ const head = require_group.groupHead(tokens, index);
265
+ const value = collectPlainText(tokens, head.contentStart, fieldEnd, codepage, sink);
266
+ if (value.length > 0) {
267
+ if (head.destination === "title") metadata.title = value;
268
+ else if (head.destination === "author") metadata.author = value;
269
+ else if (head.destination === "subject") metadata.subject = value;
270
+ else if (head.destination === "keywords") metadata.keywords = value.split(/[;,]\s*/).filter((k) => k.length > 0);
271
+ else if (head.destination === "operator") metadata.creator = value;
272
+ }
273
+ index = fieldEnd;
274
+ }
275
+ return metadata;
276
+ }
277
+ function readRtfHeader(tokens, sink) {
278
+ const fonts = /* @__PURE__ */ new Map();
279
+ const colors = [];
280
+ const styles = /* @__PURE__ */ new Map();
281
+ const listsById = /* @__PURE__ */ new Map();
282
+ const overrides = /* @__PURE__ */ new Map();
283
+ const page = {
284
+ paperWidthTwips: require_units.DEFAULT_PAPER_WIDTH_TWIPS,
285
+ paperHeightTwips: require_units.DEFAULT_PAPER_HEIGHT_TWIPS,
286
+ marginLeftTwips: require_units.DEFAULT_MARGIN_LEFT_TWIPS,
287
+ marginRightTwips: require_units.DEFAULT_MARGIN_RIGHT_TWIPS,
288
+ marginTopTwips: require_units.DEFAULT_MARGIN_TOP_TWIPS,
289
+ marginBottomTwips: require_units.DEFAULT_MARGIN_BOTTOM_TWIPS
290
+ };
291
+ let metadata = {};
292
+ let codepage = require_codepage.DEFAULT_CODEPAGE;
293
+ let defaultFontIndex;
294
+ let bodyStartIndex = 0;
295
+ const fileGroupEnd = require_group.matchingGroupEnd(tokens, 0);
296
+ for (let index = 0; index < fileGroupEnd; index += 1) {
297
+ const token = tokens[index];
298
+ if (token?.kind === "groupStart" && index > 0) {
299
+ index = Math.min(require_group.matchingGroupEnd(tokens, index), fileGroupEnd);
300
+ continue;
301
+ }
302
+ if (token?.kind !== "controlWord") continue;
303
+ const charsetPage = require_codepage.DOCUMENT_CHARSET_CODEPAGES.get(token.name);
304
+ if (charsetPage !== void 0) {
305
+ codepage = charsetPage;
306
+ continue;
307
+ }
308
+ if (token.param === void 0) continue;
309
+ switch (token.name) {
310
+ case "ansicpg":
311
+ codepage = token.param;
312
+ break;
313
+ case "deff":
314
+ defaultFontIndex = token.param;
315
+ break;
316
+ case "paperw":
317
+ page.paperWidthTwips = token.param;
318
+ break;
319
+ case "paperh":
320
+ page.paperHeightTwips = token.param;
321
+ break;
322
+ case "margl":
323
+ page.marginLeftTwips = token.param;
324
+ break;
325
+ case "margr":
326
+ page.marginRightTwips = token.param;
327
+ break;
328
+ case "margt":
329
+ page.marginTopTwips = token.param;
330
+ break;
331
+ case "margb": page.marginBottomTwips = token.param;
332
+ }
333
+ }
334
+ for (let index = 1; index < fileGroupEnd; index += 1) {
335
+ if (tokens[index]?.kind !== "groupStart") continue;
336
+ const groupEnd = Math.min(require_group.matchingGroupEnd(tokens, index), fileGroupEnd);
337
+ const head = require_group.groupHead(tokens, index);
338
+ switch (head.destination) {
339
+ case "fonttbl":
340
+ parseFontTable(tokens, head.contentStart, groupEnd, codepage, fonts, sink);
341
+ break;
342
+ case "colortbl":
343
+ parseColorTable(tokens, head.contentStart, groupEnd, colors);
344
+ break;
345
+ case "stylesheet":
346
+ parseStyleSheet(tokens, head.contentStart, groupEnd, codepage, styles, sink);
347
+ break;
348
+ case "listtable":
349
+ parseListTable(tokens, head.contentStart, groupEnd, listsById);
350
+ break;
351
+ case "listoverridetable":
352
+ parseListOverrideTable(tokens, head.contentStart, groupEnd, overrides);
353
+ break;
354
+ case "info": metadata = parseInfoGroup(tokens, head.contentStart, groupEnd, codepage, sink);
355
+ }
356
+ if (head.destination !== void 0 && HEADER_DESTINATIONS.has(head.destination)) bodyStartIndex = Math.max(bodyStartIndex, groupEnd + 1);
357
+ index = groupEnd;
358
+ }
359
+ const lists = /* @__PURE__ */ new Map();
360
+ for (const [overrideIndex, listId] of overrides) {
361
+ const list = listsById.get(listId);
362
+ if (list !== void 0) lists.set(overrideIndex, list);
363
+ }
364
+ return {
365
+ codepage,
366
+ defaultFontIndex,
367
+ fonts,
368
+ colors,
369
+ styles,
370
+ lists,
371
+ page,
372
+ metadata,
373
+ bodyStartIndex
374
+ };
375
+ }
376
+ //#endregion
377
+ exports.HEADER_DESTINATIONS = HEADER_DESTINATIONS;
378
+ exports.readRtfHeader = readRtfHeader;
@@ -0,0 +1,43 @@
1
+ import { a as RtfDiagnosticSink } from "./diagnostics-DdDFJLNO.cjs";
2
+ import { RtfToken } from "./tokenize.cjs";
3
+ import { Color, LayoutMetadata } from "document-schema.js";
4
+ //#region src/header.d.ts
5
+ interface RtfFontEntry {
6
+ readonly name: string;
7
+ readonly family: string | undefined;
8
+ readonly codepage: number | undefined;
9
+ }
10
+ interface RtfStyleEntry {
11
+ readonly name: string;
12
+ readonly headingLevel: number | undefined;
13
+ }
14
+ interface RtfListLevel {
15
+ readonly numberFormat: number;
16
+ readonly startAt: number;
17
+ }
18
+ interface RtfListEntry {
19
+ readonly levels: readonly RtfListLevel[];
20
+ }
21
+ interface RtfPageGeometry {
22
+ readonly paperWidthTwips: number;
23
+ readonly paperHeightTwips: number;
24
+ readonly marginLeftTwips: number;
25
+ readonly marginRightTwips: number;
26
+ readonly marginTopTwips: number;
27
+ readonly marginBottomTwips: number;
28
+ }
29
+ interface RtfHeader {
30
+ readonly codepage: number;
31
+ readonly defaultFontIndex: number | undefined;
32
+ readonly fonts: ReadonlyMap<number, RtfFontEntry>;
33
+ readonly colors: readonly (Color | undefined)[];
34
+ readonly styles: ReadonlyMap<number, RtfStyleEntry>;
35
+ readonly lists: ReadonlyMap<number, RtfListEntry>;
36
+ readonly page: RtfPageGeometry;
37
+ readonly metadata: LayoutMetadata;
38
+ readonly bodyStartIndex: number;
39
+ }
40
+ declare const HEADER_DESTINATIONS: ReadonlySet<string>;
41
+ declare function readRtfHeader(tokens: readonly RtfToken[], sink: RtfDiagnosticSink): RtfHeader;
42
+ //#endregion
43
+ export { HEADER_DESTINATIONS, RtfFontEntry, RtfHeader, RtfListEntry, RtfListLevel, RtfPageGeometry, RtfStyleEntry, readRtfHeader };
@@ -0,0 +1,43 @@
1
+ import { a as RtfDiagnosticSink } from "./diagnostics-DdDFJLNO.js";
2
+ import { RtfToken } from "./tokenize.js";
3
+ import { Color, LayoutMetadata } from "document-schema.js";
4
+ //#region src/header.d.ts
5
+ interface RtfFontEntry {
6
+ readonly name: string;
7
+ readonly family: string | undefined;
8
+ readonly codepage: number | undefined;
9
+ }
10
+ interface RtfStyleEntry {
11
+ readonly name: string;
12
+ readonly headingLevel: number | undefined;
13
+ }
14
+ interface RtfListLevel {
15
+ readonly numberFormat: number;
16
+ readonly startAt: number;
17
+ }
18
+ interface RtfListEntry {
19
+ readonly levels: readonly RtfListLevel[];
20
+ }
21
+ interface RtfPageGeometry {
22
+ readonly paperWidthTwips: number;
23
+ readonly paperHeightTwips: number;
24
+ readonly marginLeftTwips: number;
25
+ readonly marginRightTwips: number;
26
+ readonly marginTopTwips: number;
27
+ readonly marginBottomTwips: number;
28
+ }
29
+ interface RtfHeader {
30
+ readonly codepage: number;
31
+ readonly defaultFontIndex: number | undefined;
32
+ readonly fonts: ReadonlyMap<number, RtfFontEntry>;
33
+ readonly colors: readonly (Color | undefined)[];
34
+ readonly styles: ReadonlyMap<number, RtfStyleEntry>;
35
+ readonly lists: ReadonlyMap<number, RtfListEntry>;
36
+ readonly page: RtfPageGeometry;
37
+ readonly metadata: LayoutMetadata;
38
+ readonly bodyStartIndex: number;
39
+ }
40
+ declare const HEADER_DESTINATIONS: ReadonlySet<string>;
41
+ declare function readRtfHeader(tokens: readonly RtfToken[], sink: RtfDiagnosticSink): RtfHeader;
42
+ //#endregion
43
+ export { HEADER_DESTINATIONS, RtfFontEntry, RtfHeader, RtfListEntry, RtfListLevel, RtfPageGeometry, RtfStyleEntry, readRtfHeader };