wpd-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 +182 -0
  3. package/dist/bytes/view.cjs +23 -0
  4. package/dist/bytes/view.d.cts +7 -0
  5. package/dist/bytes/view.d.ts +7 -0
  6. package/dist/bytes/view.js +19 -0
  7. package/dist/codec.cjs +23 -0
  8. package/dist/codec.d.cts +8 -0
  9. package/dist/codec.d.ts +8 -0
  10. package/dist/codec.js +21 -0
  11. package/dist/container/container.cjs +53 -0
  12. package/dist/container/container.d.cts +16 -0
  13. package/dist/container/container.d.ts +16 -0
  14. package/dist/container/container.js +50 -0
  15. package/dist/container/header.cjs +49 -0
  16. package/dist/container/header.d.cts +16 -0
  17. package/dist/container/header.d.ts +16 -0
  18. package/dist/container/header.js +45 -0
  19. package/dist/container/prefix.cjs +66 -0
  20. package/dist/container/prefix.d.cts +18 -0
  21. package/dist/container/prefix.d.ts +18 -0
  22. package/dist/container/prefix.js +61 -0
  23. package/dist/diagnostics-Cp0HKphg.d.cts +15 -0
  24. package/dist/diagnostics-Cp0HKphg.d.ts +15 -0
  25. package/dist/diagnostics.cjs +12 -0
  26. package/dist/diagnostics.d.cts +2 -0
  27. package/dist/diagnostics.d.ts +2 -0
  28. package/dist/diagnostics.js +10 -0
  29. package/dist/errors.cjs +31 -0
  30. package/dist/errors.d.cts +15 -0
  31. package/dist/errors.d.ts +15 -0
  32. package/dist/errors.js +27 -0
  33. package/dist/format.cjs +7 -0
  34. package/dist/format.d.cts +5 -0
  35. package/dist/format.d.ts +5 -0
  36. package/dist/format.js +5 -0
  37. package/dist/index.cjs +58 -0
  38. package/dist/index.d.cts +13 -0
  39. package/dist/index.d.ts +13 -0
  40. package/dist/index.js +13 -0
  41. package/dist/read.cjs +296 -0
  42. package/dist/read.d.cts +10 -0
  43. package/dist/read.d.ts +10 -0
  44. package/dist/read.js +294 -0
  45. package/dist/stream/attributes.cjs +46 -0
  46. package/dist/stream/attributes.d.cts +37 -0
  47. package/dist/stream/attributes.d.ts +37 -0
  48. package/dist/stream/attributes.js +41 -0
  49. package/dist/stream/characters.cjs +205 -0
  50. package/dist/stream/characters.d.cts +12 -0
  51. package/dist/stream/characters.d.ts +12 -0
  52. package/dist/stream/characters.js +199 -0
  53. package/dist/stream/eol.cjs +52 -0
  54. package/dist/stream/eol.d.cts +10 -0
  55. package/dist/stream/eol.d.ts +10 -0
  56. package/dist/stream/eol.js +46 -0
  57. package/dist/stream/tokenise.cjs +125 -0
  58. package/dist/stream/tokenise.d.cts +30 -0
  59. package/dist/stream/tokenise.d.ts +30 -0
  60. package/dist/stream/tokenise.js +121 -0
  61. package/package.json +96 -2
package/dist/read.cjs ADDED
@@ -0,0 +1,296 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_bytes_view = require("./bytes/view.cjs");
3
+ const require_stream_characters = require("./stream/characters.cjs");
4
+ const require_container_prefix = require("./container/prefix.cjs");
5
+ const require_container_container = require("./container/container.cjs");
6
+ const require_diagnostics = require("./diagnostics.cjs");
7
+ const require_stream_attributes = require("./stream/attributes.cjs");
8
+ const require_stream_eol = require("./stream/eol.cjs");
9
+ const require_stream_tokenise = require("./stream/tokenise.cjs");
10
+ let document_schema_js = require("document-schema.js");
11
+ //#region src/read.ts
12
+ const SOFT_SPACE = 128;
13
+ const HARD_SPACE = 129;
14
+ const SOFT_HYPHEN_IN_LINE = 130;
15
+ const SOFT_HYPHEN_AT_END_OF_LINE = 131;
16
+ const HARD_HYPHEN_IN_LINE = 132;
17
+ const AUTO_HYPHEN_AT_END_OF_LINE = 133;
18
+ const INVISIBLE_RETURN_IN_LINE = 134;
19
+ const DORMANT_HARD_RETURN = 135;
20
+ const SOFT_END_OF_CENTER_ALIGN = 136;
21
+ const HARD_END_OF_CENTER_ALIGN = 137;
22
+ const START_OF_TEXT_TO_SKIP = 141;
23
+ const END_OF_TEXT_TO_SKIP = 142;
24
+ const PARAGRAPH_GROUP = 211;
25
+ const PARAGRAPH_SET_JUSTIFICATION = 5;
26
+ const CHARACTER_GROUP = 212;
27
+ const CHARACTER_COLOR = 24;
28
+ const CHARACTER_FONT_FACE_CHANGE = 26;
29
+ const CHARACTER_FONT_SIZE_CHANGE = 27;
30
+ const EXTENDED_CHARACTER = 240;
31
+ const JUSTIFICATION = [
32
+ "left",
33
+ "justify",
34
+ "center",
35
+ "right",
36
+ "justify",
37
+ "left"
38
+ ];
39
+ const THREE_THOUSAND_SIX_HUNDREDTHS_PER_POINT = 50;
40
+ const COLOR_COMPONENT_MAX = 255;
41
+ const DEFAULT_MARGIN_PT = 72;
42
+ function sameAttributes(a, b) {
43
+ return a.bold === b.bold && a.italic === b.italic && a.underline === b.underline && a.strike === b.strike;
44
+ }
45
+ function buildRun(state) {
46
+ return {
47
+ text: state.text,
48
+ ...state.attributes.bold ? { bold: true } : {},
49
+ ...state.attributes.italic ? { italic: true } : {},
50
+ ...state.attributes.underline ? { underline: true } : {},
51
+ ...state.attributes.strike ? { strike: true } : {},
52
+ ...state.fontFamily === void 0 ? {} : { fontFamily: state.fontFamily },
53
+ ...state.sizePt === void 0 ? {} : { sizePt: state.sizePt },
54
+ ...state.color === void 0 ? {} : { color: state.color }
55
+ };
56
+ }
57
+ function flushRun(state) {
58
+ if (state.text.length === 0) return;
59
+ state.runs.push(buildRun(state));
60
+ state.text = "";
61
+ }
62
+ function flushParagraph(state) {
63
+ flushRun(state);
64
+ const paragraph = {
65
+ kind: "paragraph",
66
+ runs: state.runs.splice(0, state.runs.length),
67
+ ...state.alignment === void 0 ? {} : { alignment: state.alignment }
68
+ };
69
+ state.blocks.push(paragraph);
70
+ }
71
+ function appendText(state, text) {
72
+ if (state.skipDepth > 0) return;
73
+ state.text += text;
74
+ }
75
+ function applyEolMapping(state, mapping, sink) {
76
+ switch (mapping) {
77
+ case "ignore": return;
78
+ case "space":
79
+ appendText(state, " ");
80
+ return;
81
+ case "hardReturn":
82
+ flushParagraph(state);
83
+ return;
84
+ case "hardEndOfColumn":
85
+ sink({
86
+ code: require_diagnostics.WpdDiagnosticCodes.ColumnBreakFlattened,
87
+ message: "A column break became a paragraph break."
88
+ });
89
+ flushParagraph(state);
90
+ return;
91
+ case "hardEndOfPage":
92
+ flushParagraph(state);
93
+ state.blocks.push({ kind: "pageBreak" });
94
+ return;
95
+ case "tableCell":
96
+ case "tableRow":
97
+ case "hardTableRow":
98
+ case "tableOff":
99
+ if (!state.tableReported) {
100
+ state.tableReported = true;
101
+ sink({
102
+ code: require_diagnostics.WpdDiagnosticCodes.TableFlattened,
103
+ message: "This document contains a table; its cells and rows became paragraphs, and the table structure was not reconstructed."
104
+ });
105
+ }
106
+ flushParagraph(state);
107
+ return;
108
+ }
109
+ }
110
+ function applySingleByteFunction(state, code, sink) {
111
+ if (require_stream_eol.isSingleByteEol(code)) {
112
+ const mapping = require_stream_eol.eolMappingForSubfunction(require_stream_eol.subfunctionForSingleByteEol(code));
113
+ if (mapping !== void 0) applyEolMapping(state, mapping, sink);
114
+ return;
115
+ }
116
+ switch (code) {
117
+ case SOFT_SPACE:
118
+ appendText(state, " ");
119
+ return;
120
+ case HARD_SPACE:
121
+ appendText(state, "\xA0");
122
+ return;
123
+ case SOFT_HYPHEN_IN_LINE:
124
+ case INVISIBLE_RETURN_IN_LINE: return;
125
+ case SOFT_HYPHEN_AT_END_OF_LINE:
126
+ case HARD_HYPHEN_IN_LINE:
127
+ case AUTO_HYPHEN_AT_END_OF_LINE:
128
+ appendText(state, "-");
129
+ return;
130
+ case DORMANT_HARD_RETURN:
131
+ flushParagraph(state);
132
+ return;
133
+ case SOFT_END_OF_CENTER_ALIGN:
134
+ appendText(state, " ");
135
+ return;
136
+ case HARD_END_OF_CENTER_ALIGN:
137
+ flushParagraph(state);
138
+ return;
139
+ case START_OF_TEXT_TO_SKIP:
140
+ state.skipDepth += 1;
141
+ return;
142
+ case END_OF_TEXT_TO_SKIP:
143
+ state.skipDepth = Math.max(0, state.skipDepth - 1);
144
+ return;
145
+ default: return;
146
+ }
147
+ }
148
+ function applyFontFaceChange(state, prefixIds, container, sink) {
149
+ const prefixId = prefixIds[0];
150
+ if (prefixId === void 0) return;
151
+ const packet = require_container_prefix.packetByPrefixId(container.packets, prefixId);
152
+ if (packet === void 0) {
153
+ sink({
154
+ code: require_diagnostics.WpdDiagnosticCodes.MissingPrefixPacket,
155
+ message: `A font face change names prefix ID ${prefixId}, which this document's index does not carry.`
156
+ });
157
+ return;
158
+ }
159
+ if (packet.packetType !== 85) return;
160
+ const typeface = require_container_prefix.readTypefaceName(packet.bytes);
161
+ if (typeface === void 0) return;
162
+ flushRun(state);
163
+ state.fontFamily = typeface;
164
+ }
165
+ function applyVariableFunction(state, token, container, sink) {
166
+ if (token.group === 208) {
167
+ const mapping = require_stream_eol.eolMappingForSubfunction(token.subgroup);
168
+ if (mapping !== void 0) applyEolMapping(state, mapping, sink);
169
+ return;
170
+ }
171
+ if (token.group === PARAGRAPH_GROUP && token.subgroup === PARAGRAPH_SET_JUSTIFICATION) {
172
+ const mode = token.nonDeletable[0];
173
+ if (mode !== void 0) state.alignment = JUSTIFICATION[mode];
174
+ return;
175
+ }
176
+ if (token.group !== CHARACTER_GROUP) return;
177
+ switch (token.subgroup) {
178
+ case CHARACTER_FONT_FACE_CHANGE:
179
+ applyFontFaceChange(state, token.prefixIds, container, sink);
180
+ return;
181
+ case CHARACTER_FONT_SIZE_CHANGE: {
182
+ if (token.nonDeletable.length < 2) return;
183
+ const sizePt = require_bytes_view.uint16At(token.nonDeletable, 0) / THREE_THOUSAND_SIX_HUNDREDTHS_PER_POINT;
184
+ if (sizePt <= 0) return;
185
+ flushRun(state);
186
+ state.sizePt = sizePt;
187
+ return;
188
+ }
189
+ case CHARACTER_COLOR: {
190
+ const [r, g, b] = token.nonDeletable;
191
+ if (r === void 0 || g === void 0 || b === void 0) return;
192
+ flushRun(state);
193
+ state.color = {
194
+ r: r / COLOR_COMPONENT_MAX,
195
+ g: g / COLOR_COMPONENT_MAX,
196
+ b: b / COLOR_COMPONENT_MAX
197
+ };
198
+ return;
199
+ }
200
+ default: return;
201
+ }
202
+ }
203
+ function applyFixedFunction(state, token, sink) {
204
+ if (token.code === EXTENDED_CHARACTER) {
205
+ const characterNumber = token.data[0];
206
+ const characterSet = token.data[1];
207
+ if (characterNumber === void 0 || characterSet === void 0) return;
208
+ const decoded = require_stream_characters.decodeWpCharacter(characterSet, characterNumber);
209
+ if (decoded === void 0) {
210
+ sink({
211
+ code: require_diagnostics.WpdDiagnosticCodes.UnmappedCharacter,
212
+ message: `Character ${characterNumber} of WordPerfect character set ${characterSet} has no mapping in this package and was rendered as U+FFFD.`
213
+ });
214
+ appendText(state, "�");
215
+ return;
216
+ }
217
+ appendText(state, decoded);
218
+ return;
219
+ }
220
+ if (token.code !== 242 && token.code !== 243) return;
221
+ const payload = token.data[0];
222
+ if (payload === void 0) return;
223
+ const { attribute, ignore } = require_stream_attributes.decodeAttributeByte(payload);
224
+ if (ignore) return;
225
+ if (token.code === 242) state.activeAttributes.add(attribute);
226
+ else state.activeAttributes.delete(attribute);
227
+ const next = require_stream_attributes.runAttributesFrom(state.activeAttributes);
228
+ if (sameAttributes(next, state.attributes)) return;
229
+ flushRun(state);
230
+ state.attributes = next;
231
+ }
232
+ function foldTokens(tokens, container, sink) {
233
+ const state = {
234
+ blocks: [],
235
+ runs: [],
236
+ text: "",
237
+ activeAttributes: /* @__PURE__ */ new Set(),
238
+ attributes: require_stream_attributes.runAttributesFrom(/* @__PURE__ */ new Set()),
239
+ fontFamily: void 0,
240
+ sizePt: void 0,
241
+ color: void 0,
242
+ alignment: void 0,
243
+ skipDepth: 0,
244
+ tableReported: false
245
+ };
246
+ for (const token of tokens) switch (token.kind) {
247
+ case "character": {
248
+ const character = require_stream_characters.decodeSingleByteCharacter(token.byte);
249
+ if (character === void 0) {
250
+ sink({
251
+ code: require_diagnostics.WpdDiagnosticCodes.UnmappedCharacter,
252
+ message: `Byte ${token.byte} in the document area has no character mapping and was rendered as U+FFFD.`
253
+ });
254
+ appendText(state, "�");
255
+ break;
256
+ }
257
+ appendText(state, character);
258
+ break;
259
+ }
260
+ case "singleByteFunction":
261
+ applySingleByteFunction(state, token.code, sink);
262
+ break;
263
+ case "variableFunction":
264
+ applyVariableFunction(state, token, container, sink);
265
+ break;
266
+ case "fixedFunction": applyFixedFunction(state, token, sink);
267
+ }
268
+ flushRun(state);
269
+ if (state.runs.length > 0) flushParagraph(state);
270
+ return state.blocks;
271
+ }
272
+ function readWpdContent(bytes, options = {}) {
273
+ const sink = options.sink ?? require_diagnostics.NOOP_WPD_DIAGNOSTIC_SINK;
274
+ const container = require_container_container.openWpdDocument(bytes);
275
+ const blocks = foldTokens(require_stream_tokenise.tokeniseDocumentArea(container.bytes, container.documentAreaOffset, container.documentAreaEnd), container, sink);
276
+ return {
277
+ kind: "wordprocessing",
278
+ metadata: {},
279
+ sections: [{
280
+ pageSize: document_schema_js.PAGE_SIZE_LETTER,
281
+ margins: {
282
+ topPt: DEFAULT_MARGIN_PT,
283
+ rightPt: DEFAULT_MARGIN_PT,
284
+ bottomPt: DEFAULT_MARGIN_PT,
285
+ leftPt: DEFAULT_MARGIN_PT
286
+ },
287
+ blocks
288
+ }]
289
+ };
290
+ }
291
+ function readWpd(bytes, options = {}) {
292
+ return (0, document_schema_js.assembleTree)(readWpdContent(bytes, options));
293
+ }
294
+ //#endregion
295
+ exports.readWpd = readWpd;
296
+ exports.readWpdContent = readWpdContent;
@@ -0,0 +1,10 @@
1
+ import { i as WpdDiagnosticSink } from "./diagnostics-Cp0HKphg.cjs";
2
+ import { ContentDocument, DocumentTree } from "document-schema.js";
3
+ //#region src/read.d.ts
4
+ interface ReadWpdOptions {
5
+ readonly sink?: WpdDiagnosticSink;
6
+ }
7
+ declare function readWpdContent(bytes: Uint8Array, options?: ReadWpdOptions): ContentDocument;
8
+ declare function readWpd(bytes: Uint8Array, options?: ReadWpdOptions): DocumentTree;
9
+ //#endregion
10
+ export { ReadWpdOptions, readWpd, readWpdContent };
package/dist/read.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { i as WpdDiagnosticSink } from "./diagnostics-Cp0HKphg.js";
2
+ import { ContentDocument, DocumentTree } from "document-schema.js";
3
+ //#region src/read.d.ts
4
+ interface ReadWpdOptions {
5
+ readonly sink?: WpdDiagnosticSink;
6
+ }
7
+ declare function readWpdContent(bytes: Uint8Array, options?: ReadWpdOptions): ContentDocument;
8
+ declare function readWpd(bytes: Uint8Array, options?: ReadWpdOptions): DocumentTree;
9
+ //#endregion
10
+ export { ReadWpdOptions, readWpd, readWpdContent };
package/dist/read.js ADDED
@@ -0,0 +1,294 @@
1
+ import { uint16At } from "./bytes/view.js";
2
+ import { decodeSingleByteCharacter, decodeWpCharacter } from "./stream/characters.js";
3
+ import { packetByPrefixId, readTypefaceName } from "./container/prefix.js";
4
+ import { openWpdDocument } from "./container/container.js";
5
+ import { NOOP_WPD_DIAGNOSTIC_SINK, WpdDiagnosticCodes } from "./diagnostics.js";
6
+ import { decodeAttributeByte, runAttributesFrom } from "./stream/attributes.js";
7
+ import { eolMappingForSubfunction, isSingleByteEol, subfunctionForSingleByteEol } from "./stream/eol.js";
8
+ import { tokeniseDocumentArea } from "./stream/tokenise.js";
9
+ import { PAGE_SIZE_LETTER, assembleTree } from "document-schema.js";
10
+ //#region src/read.ts
11
+ const SOFT_SPACE = 128;
12
+ const HARD_SPACE = 129;
13
+ const SOFT_HYPHEN_IN_LINE = 130;
14
+ const SOFT_HYPHEN_AT_END_OF_LINE = 131;
15
+ const HARD_HYPHEN_IN_LINE = 132;
16
+ const AUTO_HYPHEN_AT_END_OF_LINE = 133;
17
+ const INVISIBLE_RETURN_IN_LINE = 134;
18
+ const DORMANT_HARD_RETURN = 135;
19
+ const SOFT_END_OF_CENTER_ALIGN = 136;
20
+ const HARD_END_OF_CENTER_ALIGN = 137;
21
+ const START_OF_TEXT_TO_SKIP = 141;
22
+ const END_OF_TEXT_TO_SKIP = 142;
23
+ const PARAGRAPH_GROUP = 211;
24
+ const PARAGRAPH_SET_JUSTIFICATION = 5;
25
+ const CHARACTER_GROUP = 212;
26
+ const CHARACTER_COLOR = 24;
27
+ const CHARACTER_FONT_FACE_CHANGE = 26;
28
+ const CHARACTER_FONT_SIZE_CHANGE = 27;
29
+ const EXTENDED_CHARACTER = 240;
30
+ const JUSTIFICATION = [
31
+ "left",
32
+ "justify",
33
+ "center",
34
+ "right",
35
+ "justify",
36
+ "left"
37
+ ];
38
+ const THREE_THOUSAND_SIX_HUNDREDTHS_PER_POINT = 50;
39
+ const COLOR_COMPONENT_MAX = 255;
40
+ const DEFAULT_MARGIN_PT = 72;
41
+ function sameAttributes(a, b) {
42
+ return a.bold === b.bold && a.italic === b.italic && a.underline === b.underline && a.strike === b.strike;
43
+ }
44
+ function buildRun(state) {
45
+ return {
46
+ text: state.text,
47
+ ...state.attributes.bold ? { bold: true } : {},
48
+ ...state.attributes.italic ? { italic: true } : {},
49
+ ...state.attributes.underline ? { underline: true } : {},
50
+ ...state.attributes.strike ? { strike: true } : {},
51
+ ...state.fontFamily === void 0 ? {} : { fontFamily: state.fontFamily },
52
+ ...state.sizePt === void 0 ? {} : { sizePt: state.sizePt },
53
+ ...state.color === void 0 ? {} : { color: state.color }
54
+ };
55
+ }
56
+ function flushRun(state) {
57
+ if (state.text.length === 0) return;
58
+ state.runs.push(buildRun(state));
59
+ state.text = "";
60
+ }
61
+ function flushParagraph(state) {
62
+ flushRun(state);
63
+ const paragraph = {
64
+ kind: "paragraph",
65
+ runs: state.runs.splice(0, state.runs.length),
66
+ ...state.alignment === void 0 ? {} : { alignment: state.alignment }
67
+ };
68
+ state.blocks.push(paragraph);
69
+ }
70
+ function appendText(state, text) {
71
+ if (state.skipDepth > 0) return;
72
+ state.text += text;
73
+ }
74
+ function applyEolMapping(state, mapping, sink) {
75
+ switch (mapping) {
76
+ case "ignore": return;
77
+ case "space":
78
+ appendText(state, " ");
79
+ return;
80
+ case "hardReturn":
81
+ flushParagraph(state);
82
+ return;
83
+ case "hardEndOfColumn":
84
+ sink({
85
+ code: WpdDiagnosticCodes.ColumnBreakFlattened,
86
+ message: "A column break became a paragraph break."
87
+ });
88
+ flushParagraph(state);
89
+ return;
90
+ case "hardEndOfPage":
91
+ flushParagraph(state);
92
+ state.blocks.push({ kind: "pageBreak" });
93
+ return;
94
+ case "tableCell":
95
+ case "tableRow":
96
+ case "hardTableRow":
97
+ case "tableOff":
98
+ if (!state.tableReported) {
99
+ state.tableReported = true;
100
+ sink({
101
+ code: WpdDiagnosticCodes.TableFlattened,
102
+ message: "This document contains a table; its cells and rows became paragraphs, and the table structure was not reconstructed."
103
+ });
104
+ }
105
+ flushParagraph(state);
106
+ return;
107
+ }
108
+ }
109
+ function applySingleByteFunction(state, code, sink) {
110
+ if (isSingleByteEol(code)) {
111
+ const mapping = eolMappingForSubfunction(subfunctionForSingleByteEol(code));
112
+ if (mapping !== void 0) applyEolMapping(state, mapping, sink);
113
+ return;
114
+ }
115
+ switch (code) {
116
+ case SOFT_SPACE:
117
+ appendText(state, " ");
118
+ return;
119
+ case HARD_SPACE:
120
+ appendText(state, "\xA0");
121
+ return;
122
+ case SOFT_HYPHEN_IN_LINE:
123
+ case INVISIBLE_RETURN_IN_LINE: return;
124
+ case SOFT_HYPHEN_AT_END_OF_LINE:
125
+ case HARD_HYPHEN_IN_LINE:
126
+ case AUTO_HYPHEN_AT_END_OF_LINE:
127
+ appendText(state, "-");
128
+ return;
129
+ case DORMANT_HARD_RETURN:
130
+ flushParagraph(state);
131
+ return;
132
+ case SOFT_END_OF_CENTER_ALIGN:
133
+ appendText(state, " ");
134
+ return;
135
+ case HARD_END_OF_CENTER_ALIGN:
136
+ flushParagraph(state);
137
+ return;
138
+ case START_OF_TEXT_TO_SKIP:
139
+ state.skipDepth += 1;
140
+ return;
141
+ case END_OF_TEXT_TO_SKIP:
142
+ state.skipDepth = Math.max(0, state.skipDepth - 1);
143
+ return;
144
+ default: return;
145
+ }
146
+ }
147
+ function applyFontFaceChange(state, prefixIds, container, sink) {
148
+ const prefixId = prefixIds[0];
149
+ if (prefixId === void 0) return;
150
+ const packet = packetByPrefixId(container.packets, prefixId);
151
+ if (packet === void 0) {
152
+ sink({
153
+ code: WpdDiagnosticCodes.MissingPrefixPacket,
154
+ message: `A font face change names prefix ID ${prefixId}, which this document's index does not carry.`
155
+ });
156
+ return;
157
+ }
158
+ if (packet.packetType !== 85) return;
159
+ const typeface = readTypefaceName(packet.bytes);
160
+ if (typeface === void 0) return;
161
+ flushRun(state);
162
+ state.fontFamily = typeface;
163
+ }
164
+ function applyVariableFunction(state, token, container, sink) {
165
+ if (token.group === 208) {
166
+ const mapping = eolMappingForSubfunction(token.subgroup);
167
+ if (mapping !== void 0) applyEolMapping(state, mapping, sink);
168
+ return;
169
+ }
170
+ if (token.group === PARAGRAPH_GROUP && token.subgroup === PARAGRAPH_SET_JUSTIFICATION) {
171
+ const mode = token.nonDeletable[0];
172
+ if (mode !== void 0) state.alignment = JUSTIFICATION[mode];
173
+ return;
174
+ }
175
+ if (token.group !== CHARACTER_GROUP) return;
176
+ switch (token.subgroup) {
177
+ case CHARACTER_FONT_FACE_CHANGE:
178
+ applyFontFaceChange(state, token.prefixIds, container, sink);
179
+ return;
180
+ case CHARACTER_FONT_SIZE_CHANGE: {
181
+ if (token.nonDeletable.length < 2) return;
182
+ const sizePt = uint16At(token.nonDeletable, 0) / THREE_THOUSAND_SIX_HUNDREDTHS_PER_POINT;
183
+ if (sizePt <= 0) return;
184
+ flushRun(state);
185
+ state.sizePt = sizePt;
186
+ return;
187
+ }
188
+ case CHARACTER_COLOR: {
189
+ const [r, g, b] = token.nonDeletable;
190
+ if (r === void 0 || g === void 0 || b === void 0) return;
191
+ flushRun(state);
192
+ state.color = {
193
+ r: r / COLOR_COMPONENT_MAX,
194
+ g: g / COLOR_COMPONENT_MAX,
195
+ b: b / COLOR_COMPONENT_MAX
196
+ };
197
+ return;
198
+ }
199
+ default: return;
200
+ }
201
+ }
202
+ function applyFixedFunction(state, token, sink) {
203
+ if (token.code === EXTENDED_CHARACTER) {
204
+ const characterNumber = token.data[0];
205
+ const characterSet = token.data[1];
206
+ if (characterNumber === void 0 || characterSet === void 0) return;
207
+ const decoded = decodeWpCharacter(characterSet, characterNumber);
208
+ if (decoded === void 0) {
209
+ sink({
210
+ code: WpdDiagnosticCodes.UnmappedCharacter,
211
+ message: `Character ${characterNumber} of WordPerfect character set ${characterSet} has no mapping in this package and was rendered as U+FFFD.`
212
+ });
213
+ appendText(state, "�");
214
+ return;
215
+ }
216
+ appendText(state, decoded);
217
+ return;
218
+ }
219
+ if (token.code !== 242 && token.code !== 243) return;
220
+ const payload = token.data[0];
221
+ if (payload === void 0) return;
222
+ const { attribute, ignore } = decodeAttributeByte(payload);
223
+ if (ignore) return;
224
+ if (token.code === 242) state.activeAttributes.add(attribute);
225
+ else state.activeAttributes.delete(attribute);
226
+ const next = runAttributesFrom(state.activeAttributes);
227
+ if (sameAttributes(next, state.attributes)) return;
228
+ flushRun(state);
229
+ state.attributes = next;
230
+ }
231
+ function foldTokens(tokens, container, sink) {
232
+ const state = {
233
+ blocks: [],
234
+ runs: [],
235
+ text: "",
236
+ activeAttributes: /* @__PURE__ */ new Set(),
237
+ attributes: runAttributesFrom(/* @__PURE__ */ new Set()),
238
+ fontFamily: void 0,
239
+ sizePt: void 0,
240
+ color: void 0,
241
+ alignment: void 0,
242
+ skipDepth: 0,
243
+ tableReported: false
244
+ };
245
+ for (const token of tokens) switch (token.kind) {
246
+ case "character": {
247
+ const character = decodeSingleByteCharacter(token.byte);
248
+ if (character === void 0) {
249
+ sink({
250
+ code: WpdDiagnosticCodes.UnmappedCharacter,
251
+ message: `Byte ${token.byte} in the document area has no character mapping and was rendered as U+FFFD.`
252
+ });
253
+ appendText(state, "�");
254
+ break;
255
+ }
256
+ appendText(state, character);
257
+ break;
258
+ }
259
+ case "singleByteFunction":
260
+ applySingleByteFunction(state, token.code, sink);
261
+ break;
262
+ case "variableFunction":
263
+ applyVariableFunction(state, token, container, sink);
264
+ break;
265
+ case "fixedFunction": applyFixedFunction(state, token, sink);
266
+ }
267
+ flushRun(state);
268
+ if (state.runs.length > 0) flushParagraph(state);
269
+ return state.blocks;
270
+ }
271
+ function readWpdContent(bytes, options = {}) {
272
+ const sink = options.sink ?? NOOP_WPD_DIAGNOSTIC_SINK;
273
+ const container = openWpdDocument(bytes);
274
+ const blocks = foldTokens(tokeniseDocumentArea(container.bytes, container.documentAreaOffset, container.documentAreaEnd), container, sink);
275
+ return {
276
+ kind: "wordprocessing",
277
+ metadata: {},
278
+ sections: [{
279
+ pageSize: PAGE_SIZE_LETTER,
280
+ margins: {
281
+ topPt: DEFAULT_MARGIN_PT,
282
+ rightPt: DEFAULT_MARGIN_PT,
283
+ bottomPt: DEFAULT_MARGIN_PT,
284
+ leftPt: DEFAULT_MARGIN_PT
285
+ },
286
+ blocks
287
+ }]
288
+ };
289
+ }
290
+ function readWpd(bytes, options = {}) {
291
+ return assembleTree(readWpdContent(bytes, options));
292
+ }
293
+ //#endregion
294
+ export { readWpd, readWpdContent };
@@ -0,0 +1,46 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/stream/attributes.ts
3
+ const ATTRIBUTE_ON = 242;
4
+ const ATTRIBUTE_OFF = 243;
5
+ const ATTRIBUTE_IGNORE_BIT = 128;
6
+ const ATTRIBUTE_NUMBER_MASK = 63;
7
+ const WpdAttribute = {
8
+ ExtraLarge: 0,
9
+ VeryLarge: 1,
10
+ Large: 2,
11
+ SmallPrint: 3,
12
+ FinePrint: 4,
13
+ Superscript: 5,
14
+ Subscript: 6,
15
+ Outline: 7,
16
+ Italics: 8,
17
+ Shadow: 9,
18
+ Redline: 10,
19
+ DoubleUnderline: 11,
20
+ Bold: 12,
21
+ Strikeout: 13,
22
+ Underline: 14,
23
+ SmallCaps: 15,
24
+ Blink: 16,
25
+ ReverseVideo: 17
26
+ };
27
+ function decodeAttributeByte(payload) {
28
+ return {
29
+ attribute: payload & ATTRIBUTE_NUMBER_MASK,
30
+ ignore: (payload & ATTRIBUTE_IGNORE_BIT) !== 0
31
+ };
32
+ }
33
+ function runAttributesFrom(active) {
34
+ return {
35
+ bold: active.has(WpdAttribute.Bold),
36
+ italic: active.has(WpdAttribute.Italics),
37
+ underline: active.has(WpdAttribute.Underline) || active.has(WpdAttribute.DoubleUnderline),
38
+ strike: active.has(WpdAttribute.Strikeout)
39
+ };
40
+ }
41
+ //#endregion
42
+ exports.ATTRIBUTE_OFF = ATTRIBUTE_OFF;
43
+ exports.ATTRIBUTE_ON = ATTRIBUTE_ON;
44
+ exports.WpdAttribute = WpdAttribute;
45
+ exports.decodeAttributeByte = decodeAttributeByte;
46
+ exports.runAttributesFrom = runAttributesFrom;
@@ -0,0 +1,37 @@
1
+ //#region src/stream/attributes.d.ts
2
+ declare const ATTRIBUTE_ON = 242;
3
+ declare const ATTRIBUTE_OFF = 243;
4
+ declare const WpdAttribute: {
5
+ readonly ExtraLarge: 0;
6
+ readonly VeryLarge: 1;
7
+ readonly Large: 2;
8
+ readonly SmallPrint: 3;
9
+ readonly FinePrint: 4;
10
+ readonly Superscript: 5;
11
+ readonly Subscript: 6;
12
+ readonly Outline: 7;
13
+ readonly Italics: 8;
14
+ readonly Shadow: 9;
15
+ readonly Redline: 10;
16
+ readonly DoubleUnderline: 11;
17
+ readonly Bold: 12;
18
+ readonly Strikeout: 13;
19
+ readonly Underline: 14;
20
+ readonly SmallCaps: 15;
21
+ readonly Blink: 16;
22
+ readonly ReverseVideo: 17;
23
+ };
24
+ interface WpdAttributeCode {
25
+ readonly attribute: number;
26
+ readonly ignore: boolean;
27
+ }
28
+ declare function decodeAttributeByte(payload: number): WpdAttributeCode;
29
+ interface WpdRunAttributes {
30
+ readonly bold: boolean;
31
+ readonly italic: boolean;
32
+ readonly underline: boolean;
33
+ readonly strike: boolean;
34
+ }
35
+ declare function runAttributesFrom(active: ReadonlySet<number>): WpdRunAttributes;
36
+ //#endregion
37
+ export { ATTRIBUTE_OFF, ATTRIBUTE_ON, WpdAttribute, WpdAttributeCode, WpdRunAttributes, decodeAttributeByte, runAttributesFrom };