wpd-codec 3.5.1 → 3.5.3

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 (39) hide show
  1. package/dist/container/container.cjs +2 -3
  2. package/dist/container/container.js +2 -3
  3. package/dist/container/encryption.cjs +7 -3
  4. package/dist/container/encryption.d.cts +2 -1
  5. package/dist/container/encryption.d.ts +2 -1
  6. package/dist/container/encryption.js +7 -4
  7. package/dist/container/header.cjs +0 -1
  8. package/dist/container/header.js +0 -1
  9. package/dist/container/prefix.cjs +19 -16
  10. package/dist/container/prefix.js +19 -16
  11. package/dist/container/summary.cjs +10 -4
  12. package/dist/container/summary.js +10 -4
  13. package/dist/read.cjs +10 -17
  14. package/dist/read.d.cts +3 -1
  15. package/dist/read.d.ts +3 -1
  16. package/dist/read.js +9 -18
  17. package/dist/stream/box.cjs +61 -70
  18. package/dist/stream/box.js +61 -70
  19. package/dist/stream/characters.cjs +2 -0
  20. package/dist/stream/characters.d.cts +2 -1
  21. package/dist/stream/characters.d.ts +2 -1
  22. package/dist/stream/characters.js +2 -1
  23. package/dist/stream/formula.cjs +14 -16
  24. package/dist/stream/formula.js +15 -17
  25. package/dist/stream/image.cjs +55 -41
  26. package/dist/stream/image.d.cts +3 -1
  27. package/dist/stream/image.d.ts +3 -1
  28. package/dist/stream/image.js +55 -43
  29. package/dist/stream/ole.cjs +1 -1
  30. package/dist/stream/ole.js +2 -2
  31. package/dist/stream/style.cjs +15 -13
  32. package/dist/stream/style.js +15 -13
  33. package/dist/stream/table.cjs +47 -33
  34. package/dist/stream/table.d.cts +3 -2
  35. package/dist/stream/table.d.ts +3 -2
  36. package/dist/stream/table.js +48 -35
  37. package/dist/stream/wpg.cjs +38 -40
  38. package/dist/stream/wpg.js +38 -40
  39. package/package.json +3 -3
@@ -39,14 +39,13 @@ function unwrapContainer(bytes) {
39
39
  }
40
40
  function documentAreaEnd(bytes, header) {
41
41
  const { fileSize, documentAreaOffset } = header;
42
- if (fileSize > documentAreaOffset && fileSize <= bytes.length) return fileSize;
42
+ if (fileSize > documentAreaOffset) return Math.min(fileSize, bytes.length);
43
43
  return bytes.length;
44
44
  }
45
45
  function openWpdDocument(input, options = {}) {
46
46
  const { bytes: wrapped, compound, oleObjectStreams } = unwrapContainer(toArrayBufferBacked(input));
47
47
  const header = require_container_header.readFileHeader(wrapped, options);
48
- const suppliedPassword = options.password === "" ? void 0 : options.password;
49
- const bytes = header.encryption !== 0 && suppliedPassword !== void 0 ? require_container_encryption.decryptWpdDocument(wrapped, header, suppliedPassword) : wrapped;
48
+ const bytes = header.encryption !== 0 && options.password !== void 0 ? require_container_encryption.decryptWpdDocument(wrapped, header, options.password) : wrapped;
50
49
  return {
51
50
  header,
52
51
  packets: require_container_prefix.readPrefixPackets(bytes, header),
@@ -38,14 +38,13 @@ function unwrapContainer(bytes) {
38
38
  }
39
39
  function documentAreaEnd(bytes, header) {
40
40
  const { fileSize, documentAreaOffset } = header;
41
- if (fileSize > documentAreaOffset && fileSize <= bytes.length) return fileSize;
41
+ if (fileSize > documentAreaOffset) return Math.min(fileSize, bytes.length);
42
42
  return bytes.length;
43
43
  }
44
44
  function openWpdDocument(input, options = {}) {
45
45
  const { bytes: wrapped, compound, oleObjectStreams } = unwrapContainer(toArrayBufferBacked(input));
46
46
  const header = readFileHeader(wrapped, options);
47
- const suppliedPassword = options.password === "" ? void 0 : options.password;
48
- const bytes = header.encryption !== 0 && suppliedPassword !== void 0 ? decryptWpdDocument(wrapped, header, suppliedPassword) : wrapped;
47
+ const bytes = header.encryption !== 0 && options.password !== void 0 ? decryptWpdDocument(wrapped, header, options.password) : wrapped;
49
48
  return {
50
49
  header,
51
50
  packets: readPrefixPackets(bytes, header),
@@ -20,6 +20,11 @@ function wpdPasswordChecksum16(normalised) {
20
20
  }
21
21
  return checksum;
22
22
  }
23
+ function passwordByteAt(normalised, relative) {
24
+ const value = normalised[relative % normalised.length];
25
+ if (value === void 0) throw new require_errors.WpdFormatError("The password normalised to no bytes, which the cipher cannot key with.");
26
+ return value;
27
+ }
23
28
  function applyWpdStandardEncryption(bytes, normalised, startOffset) {
24
29
  if (normalised.length === 0) throw new require_errors.WpdFormatError("The WordPerfect cipher is keyed by the password's own bytes, so an empty password decrypts nothing.");
25
30
  const maskBase = normalised.length + 1 & 255;
@@ -27,10 +32,8 @@ function applyWpdStandardEncryption(bytes, normalised, startOffset) {
27
32
  output.set(bytes.subarray(0, startOffset));
28
33
  for (let pos = startOffset; pos < bytes.length; pos++) {
29
34
  const relative = pos - startOffset;
30
- const passwordByte = normalised[relative % normalised.length];
31
- if (passwordByte === void 0) throw new require_errors.WpdFormatError("The password normalised to no bytes, which the cipher cannot key with.");
32
35
  const mask = maskBase + relative & 255;
33
- output[pos] = require_bytes_view.byteAt(bytes, pos) ^ passwordByte ^ mask;
36
+ output[pos] = require_bytes_view.byteAt(bytes, pos) ^ passwordByteAt(normalised, relative) ^ mask;
34
37
  }
35
38
  return output;
36
39
  }
@@ -56,4 +59,5 @@ exports.applyWpdStandardEncryption = applyWpdStandardEncryption;
56
59
  exports.decryptWpdDocument = decryptWpdDocument;
57
60
  exports.encryptWpdDocumentForTests = encryptWpdDocumentForTests;
58
61
  exports.normaliseWpdPassword = normaliseWpdPassword;
62
+ exports.passwordByteAt = passwordByteAt;
59
63
  exports.wpdPasswordChecksum16 = wpdPasswordChecksum16;
@@ -2,8 +2,9 @@ import { i as WpdFileHeader } from "../header-Ca8QZVe3.cjs";
2
2
  //#region src/container/encryption.d.ts
3
3
  declare function normaliseWpdPassword(password: string): number[];
4
4
  declare function wpdPasswordChecksum16(normalised: readonly number[]): number;
5
+ declare function passwordByteAt(normalised: readonly number[], relative: number): number;
5
6
  declare function applyWpdStandardEncryption(bytes: Uint8Array, normalised: readonly number[], startOffset: number): Uint8Array<ArrayBuffer>;
6
7
  declare function decryptWpdDocument(bytes: Uint8Array<ArrayBuffer>, header: WpdFileHeader, password: string): Uint8Array<ArrayBuffer>;
7
8
  declare function encryptWpdDocumentForTests(bytes: Uint8Array, password: string): Uint8Array<ArrayBuffer>;
8
9
  //#endregion
9
- export { applyWpdStandardEncryption, decryptWpdDocument, encryptWpdDocumentForTests, normaliseWpdPassword, wpdPasswordChecksum16 };
10
+ export { applyWpdStandardEncryption, decryptWpdDocument, encryptWpdDocumentForTests, normaliseWpdPassword, passwordByteAt, wpdPasswordChecksum16 };
@@ -2,8 +2,9 @@ import { i as WpdFileHeader } from "../header-Ca8QZVe3.js";
2
2
  //#region src/container/encryption.d.ts
3
3
  declare function normaliseWpdPassword(password: string): number[];
4
4
  declare function wpdPasswordChecksum16(normalised: readonly number[]): number;
5
+ declare function passwordByteAt(normalised: readonly number[], relative: number): number;
5
6
  declare function applyWpdStandardEncryption(bytes: Uint8Array, normalised: readonly number[], startOffset: number): Uint8Array<ArrayBuffer>;
6
7
  declare function decryptWpdDocument(bytes: Uint8Array<ArrayBuffer>, header: WpdFileHeader, password: string): Uint8Array<ArrayBuffer>;
7
8
  declare function encryptWpdDocumentForTests(bytes: Uint8Array, password: string): Uint8Array<ArrayBuffer>;
8
9
  //#endregion
9
- export { applyWpdStandardEncryption, decryptWpdDocument, encryptWpdDocumentForTests, normaliseWpdPassword, wpdPasswordChecksum16 };
10
+ export { applyWpdStandardEncryption, decryptWpdDocument, encryptWpdDocumentForTests, normaliseWpdPassword, passwordByteAt, wpdPasswordChecksum16 };
@@ -19,6 +19,11 @@ function wpdPasswordChecksum16(normalised) {
19
19
  }
20
20
  return checksum;
21
21
  }
22
+ function passwordByteAt(normalised, relative) {
23
+ const value = normalised[relative % normalised.length];
24
+ if (value === void 0) throw new WpdFormatError("The password normalised to no bytes, which the cipher cannot key with.");
25
+ return value;
26
+ }
22
27
  function applyWpdStandardEncryption(bytes, normalised, startOffset) {
23
28
  if (normalised.length === 0) throw new WpdFormatError("The WordPerfect cipher is keyed by the password's own bytes, so an empty password decrypts nothing.");
24
29
  const maskBase = normalised.length + 1 & 255;
@@ -26,10 +31,8 @@ function applyWpdStandardEncryption(bytes, normalised, startOffset) {
26
31
  output.set(bytes.subarray(0, startOffset));
27
32
  for (let pos = startOffset; pos < bytes.length; pos++) {
28
33
  const relative = pos - startOffset;
29
- const passwordByte = normalised[relative % normalised.length];
30
- if (passwordByte === void 0) throw new WpdFormatError("The password normalised to no bytes, which the cipher cannot key with.");
31
34
  const mask = maskBase + relative & 255;
32
- output[pos] = byteAt(bytes, pos) ^ passwordByte ^ mask;
35
+ output[pos] = byteAt(bytes, pos) ^ passwordByteAt(normalised, relative) ^ mask;
33
36
  }
34
37
  return output;
35
38
  }
@@ -51,4 +54,4 @@ function encryptWpdDocumentForTests(bytes, password) {
51
54
  return output;
52
55
  }
53
56
  //#endregion
54
- export { applyWpdStandardEncryption, decryptWpdDocument, encryptWpdDocumentForTests, normaliseWpdPassword, wpdPasswordChecksum16 };
57
+ export { applyWpdStandardEncryption, decryptWpdDocument, encryptWpdDocumentForTests, normaliseWpdPassword, passwordByteAt, wpdPasswordChecksum16 };
@@ -13,7 +13,6 @@ const PRODUCT_TYPE_WORDPERFECT = 1;
13
13
  const DOCUMENT_FILE_TYPES = [10, 36];
14
14
  const MAJOR_VERSION_WP6_THROUGH_X6 = 2;
15
15
  function hasWordPerfectFileId(bytes) {
16
- if (bytes.length < WPD_FILE_ID.length) return false;
17
16
  return WPD_FILE_ID.every((expected, index) => bytes[index] === expected);
18
17
  }
19
18
  function readFileHeader(bytes, options = {}) {
@@ -12,7 +12,6 @@ const PRODUCT_TYPE_WORDPERFECT = 1;
12
12
  const DOCUMENT_FILE_TYPES = [10, 36];
13
13
  const MAJOR_VERSION_WP6_THROUGH_X6 = 2;
14
14
  function hasWordPerfectFileId(bytes) {
15
- if (bytes.length < WPD_FILE_ID.length) return false;
16
15
  return WPD_FILE_ID.every((expected, index) => bytes[index] === expected);
17
16
  }
18
17
  function readFileHeader(bytes, options = {}) {
@@ -52,24 +52,27 @@ const TYPEFACE_NAME_LENGTH_OFFSET = 22;
52
52
  const TYPEFACE_NAME_OFFSET = 24;
53
53
  const PACKET_TYPE_GENERAL_WP_TEXT = 8;
54
54
  function readGeneralWpTextBlocks(bytes) {
55
- if (bytes.length < 4) return;
56
- const blockCount = require_bytes_view.uint16At(bytes, 0);
57
- const firstBlockOffset = require_bytes_view.uint16At(bytes, 2);
58
- if (blockCount === 0) return;
59
- if (4 + blockCount * 2 > bytes.length) return;
60
- let totalSize = 0;
61
- for (let index = 0; index < blockCount; index += 1) totalSize += require_bytes_view.uint16At(bytes, 4 + index * 2);
62
- const end = firstBlockOffset + totalSize;
63
- if (firstBlockOffset < 0 || end > bytes.length) return;
64
- return bytes.subarray(firstBlockOffset, end);
55
+ try {
56
+ const blockCount = require_bytes_view.uint16At(bytes, 0);
57
+ const firstBlockOffset = require_bytes_view.uint16At(bytes, 2);
58
+ if (blockCount === 0) return;
59
+ let totalSize = 0;
60
+ for (let index = 0; index < blockCount; index += 1) totalSize += require_bytes_view.uint16At(bytes, 4 + index * 2);
61
+ const end = firstBlockOffset + totalSize;
62
+ if (end > bytes.length) return;
63
+ return bytes.subarray(firstBlockOffset, end);
64
+ } catch {
65
+ return;
66
+ }
65
67
  }
66
68
  function readTypefaceName(packet) {
67
- if (packet.length < TYPEFACE_NAME_OFFSET) return;
68
- const nameLength = require_bytes_view.uint16At(packet, TYPEFACE_NAME_LENGTH_OFFSET);
69
- const available = Math.min(nameLength, packet.length - TYPEFACE_NAME_OFFSET);
70
- if (available <= 0) return;
71
- const { text } = require_stream_characters.decodeWordString(packet, TYPEFACE_NAME_OFFSET, Math.floor(available / 2));
72
- return text.length > 0 ? text : void 0;
69
+ try {
70
+ const nameLength = require_bytes_view.uint16At(packet, TYPEFACE_NAME_LENGTH_OFFSET);
71
+ const { text } = require_stream_characters.decodeWordString(packet, TYPEFACE_NAME_OFFSET, Math.floor(nameLength / 2));
72
+ return text.length > 0 ? text : void 0;
73
+ } catch {
74
+ return;
75
+ }
73
76
  }
74
77
  //#endregion
75
78
  exports.PACKET_TYPE_DESIRED_FONT_DESCRIPTOR = PACKET_TYPE_DESIRED_FONT_DESCRIPTOR;
@@ -51,24 +51,27 @@ const TYPEFACE_NAME_LENGTH_OFFSET = 22;
51
51
  const TYPEFACE_NAME_OFFSET = 24;
52
52
  const PACKET_TYPE_GENERAL_WP_TEXT = 8;
53
53
  function readGeneralWpTextBlocks(bytes) {
54
- if (bytes.length < 4) return;
55
- const blockCount = uint16At(bytes, 0);
56
- const firstBlockOffset = uint16At(bytes, 2);
57
- if (blockCount === 0) return;
58
- if (4 + blockCount * 2 > bytes.length) return;
59
- let totalSize = 0;
60
- for (let index = 0; index < blockCount; index += 1) totalSize += uint16At(bytes, 4 + index * 2);
61
- const end = firstBlockOffset + totalSize;
62
- if (firstBlockOffset < 0 || end > bytes.length) return;
63
- return bytes.subarray(firstBlockOffset, end);
54
+ try {
55
+ const blockCount = uint16At(bytes, 0);
56
+ const firstBlockOffset = uint16At(bytes, 2);
57
+ if (blockCount === 0) return;
58
+ let totalSize = 0;
59
+ for (let index = 0; index < blockCount; index += 1) totalSize += uint16At(bytes, 4 + index * 2);
60
+ const end = firstBlockOffset + totalSize;
61
+ if (end > bytes.length) return;
62
+ return bytes.subarray(firstBlockOffset, end);
63
+ } catch {
64
+ return;
65
+ }
64
66
  }
65
67
  function readTypefaceName(packet) {
66
- if (packet.length < TYPEFACE_NAME_OFFSET) return;
67
- const nameLength = uint16At(packet, TYPEFACE_NAME_LENGTH_OFFSET);
68
- const available = Math.min(nameLength, packet.length - TYPEFACE_NAME_OFFSET);
69
- if (available <= 0) return;
70
- const { text } = decodeWordString(packet, TYPEFACE_NAME_OFFSET, Math.floor(available / 2));
71
- return text.length > 0 ? text : void 0;
68
+ try {
69
+ const nameLength = uint16At(packet, TYPEFACE_NAME_LENGTH_OFFSET);
70
+ const { text } = decodeWordString(packet, TYPEFACE_NAME_OFFSET, Math.floor(nameLength / 2));
71
+ return text.length > 0 ? text : void 0;
72
+ } catch {
73
+ return;
74
+ }
72
75
  }
73
76
  //#endregion
74
77
  export { PACKET_TYPE_DESIRED_FONT_DESCRIPTOR, PACKET_TYPE_GENERAL_WP_TEXT, WPD_INDEX_RECORD_SIZE, packetByPrefixId, readGeneralWpTextBlocks, readPrefixPackets, readTypefaceName };
@@ -35,10 +35,16 @@ function readDocumentSummary(packet) {
35
35
  const metadata = {};
36
36
  let cursor = 0;
37
37
  for (let group = 0; group < MAX_SUMMARY_GROUPS; group += 1) {
38
- if (cursor + GROUP_HEADER_SIZE > packet.length) break;
39
- const size = require_bytes_view.uint16At(packet, cursor);
40
- const tag = require_bytes_view.uint16At(packet, cursor + 2);
41
- const type = require_bytes_view.uint16At(packet, cursor + 4);
38
+ let size;
39
+ let tag;
40
+ let type;
41
+ try {
42
+ size = require_bytes_view.uint16At(packet, cursor);
43
+ tag = require_bytes_view.uint16At(packet, cursor + 2);
44
+ type = require_bytes_view.uint16At(packet, cursor + 4);
45
+ } catch {
46
+ break;
47
+ }
42
48
  if (size < GROUP_HEADER_SIZE || cursor + size > packet.length) break;
43
49
  const availableWords = Math.floor((size - GROUP_HEADER_SIZE) / 2);
44
50
  const name = require_stream_characters.decodeWordString(packet, cursor + GROUP_HEADER_SIZE, availableWords);
@@ -34,10 +34,16 @@ function readDocumentSummary(packet) {
34
34
  const metadata = {};
35
35
  let cursor = 0;
36
36
  for (let group = 0; group < MAX_SUMMARY_GROUPS; group += 1) {
37
- if (cursor + GROUP_HEADER_SIZE > packet.length) break;
38
- const size = uint16At(packet, cursor);
39
- const tag = uint16At(packet, cursor + 2);
40
- const type = uint16At(packet, cursor + 4);
37
+ let size;
38
+ let tag;
39
+ let type;
40
+ try {
41
+ size = uint16At(packet, cursor);
42
+ tag = uint16At(packet, cursor + 2);
43
+ type = uint16At(packet, cursor + 4);
44
+ } catch {
45
+ break;
46
+ }
41
47
  if (size < GROUP_HEADER_SIZE || cursor + size > packet.length) break;
42
48
  const availableWords = Math.floor((size - GROUP_HEADER_SIZE) / 2);
43
49
  const name = decodeWordString(packet, cursor + GROUP_HEADER_SIZE, availableWords);
package/dist/read.cjs CHANGED
@@ -1,4 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_errors = require("./errors.cjs");
2
3
  const require_bytes_view = require("./bytes/view.cjs");
3
4
  const require_bytes_base64 = require("./bytes/base64.cjs");
4
5
  const require_stream_furniture = require("./stream/furniture.cjs");
@@ -87,6 +88,10 @@ function flushRun(state) {
87
88
  state.runs.push(buildRun(state));
88
89
  state.text = "";
89
90
  }
91
+ const UNREACHABLE_CHARACTER_MAPPING_MESSAGE = "A single-byte document-area character had no character mapping, which the tokeniser's own byte range should make unreachable.";
92
+ function assertDefined(value, message) {
93
+ if (value === void 0) throw new require_errors.WpdFormatError(message);
94
+ }
90
95
  function targetBlocks(state) {
91
96
  return state.table === void 0 ? state.blocks : state.table.cellBlocks;
92
97
  }
@@ -120,10 +125,7 @@ function flushParagraphIfContent(state, sink) {
120
125
  flushParagraph(state, sink);
121
126
  }
122
127
  function effectiveStyle(state) {
123
- for (let index = state.styleScopes.length - 1; index >= 0; index -= 1) {
124
- const semantics = state.styleScopes[index]?.semantics;
125
- if (semantics !== void 0) return semantics;
126
- }
128
+ return state.styleScopes.findLast((scope) => scope.semantics !== void 0)?.semantics;
127
129
  }
128
130
  function appendText(state, text) {
129
131
  if (state.skipDepth > 0 || state.numberDisplayDepth > 0) return;
@@ -272,7 +274,6 @@ function applySingleByteFunction(state, code, sink) {
272
274
  case END_OF_TEXT_TO_SKIP:
273
275
  state.skipDepth = Math.max(0, state.skipDepth - 1);
274
276
  return;
275
- default: return;
276
277
  }
277
278
  }
278
279
  function applyFontFaceChange(state, prefixIds, container, sink) {
@@ -380,7 +381,7 @@ function applyStyleGroup(state, token, container, sink) {
380
381
  function applyDisplayNumberGroup(state, token, sink) {
381
382
  if (require_stream_style.isParagraphNumberDisplayOn(token.subgroup)) {
382
383
  const level = require_stream_style.readDisplayNumberLevel(token.nonDeletable);
383
- if (level !== void 0 && state.pendingListLevel === void 0) state.pendingListLevel = level;
384
+ state.pendingListLevel ??= level;
384
385
  state.numberDisplayDepth += 1;
385
386
  reportOnce(state, sink, require_diagnostics.WpdDiagnosticCodes.OutlineNumberRegenerated, "An outline number's rendered digits were replaced by the list membership that regenerates them.");
386
387
  return;
@@ -433,7 +434,6 @@ function applyCharacterGroup(state, token, container, sink) {
433
434
  };
434
435
  return;
435
436
  }
436
- default: return;
437
437
  }
438
438
  }
439
439
  function applyHeaderFooterGroup(state, token, container, sink) {
@@ -749,7 +749,6 @@ function applyVariableFunction(state, token, container, sink) {
749
749
  case BOX_GROUP:
750
750
  applyBoxGroup(state, token, container, sink);
751
751
  return;
752
- default: return;
753
752
  }
754
753
  }
755
754
  function applyFixedFunction(state, token, sink) {
@@ -785,14 +784,7 @@ function applyToken(state, token, container, sink) {
785
784
  switch (token.kind) {
786
785
  case "character": {
787
786
  const character = require_stream_characters.decodeSingleByteCharacter(token.byte);
788
- if (character === void 0) {
789
- sink({
790
- code: require_diagnostics.WpdDiagnosticCodes.UnmappedCharacter,
791
- message: `Byte ${token.byte} in the document area has no character mapping and was rendered as U+FFFD.`
792
- });
793
- appendText(state, "�");
794
- return;
795
- }
787
+ assertDefined(character, UNREACHABLE_CHARACTER_MAPPING_MESSAGE);
796
788
  appendText(state, character);
797
789
  return;
798
790
  }
@@ -934,7 +926,6 @@ function readWpd(bytes, options = {}) {
934
926
  marker: note.marker,
935
927
  blocks: note.blocks
936
928
  }]));
937
- if (Object.keys(attachments).length === 0 && notes.length === 0) return assembled;
938
929
  return {
939
930
  ...assembled,
940
931
  ...Object.keys(attachments).length > 0 ? { attachments: {
@@ -948,5 +939,7 @@ function readWpd(bytes, options = {}) {
948
939
  };
949
940
  }
950
941
  //#endregion
942
+ exports.UNREACHABLE_CHARACTER_MAPPING_MESSAGE = UNREACHABLE_CHARACTER_MAPPING_MESSAGE;
943
+ exports.assertDefined = assertDefined;
951
944
  exports.readWpd = readWpd;
952
945
  exports.readWpdContent = readWpdContent;
package/dist/read.d.cts CHANGED
@@ -10,7 +10,9 @@ interface WpdNoteDefinition {
10
10
  readonly marker: string;
11
11
  readonly blocks: readonly ContentBlock[];
12
12
  }
13
+ declare const UNREACHABLE_CHARACTER_MAPPING_MESSAGE = "A single-byte document-area character had no character mapping, which the tokeniser's own byte range should make unreachable.";
14
+ declare function assertDefined<T>(value: T | undefined, message: string): asserts value is T;
13
15
  declare function readWpdContent(bytes: Uint8Array, options?: ReadWpdOptions): ContentDocument;
14
16
  declare function readWpd(bytes: Uint8Array, options?: ReadWpdOptions): DocumentTree;
15
17
  //#endregion
16
- export { ReadWpdOptions, WpdNoteDefinition, readWpd, readWpdContent };
18
+ export { ReadWpdOptions, UNREACHABLE_CHARACTER_MAPPING_MESSAGE, WpdNoteDefinition, assertDefined, readWpd, readWpdContent };
package/dist/read.d.ts CHANGED
@@ -10,7 +10,9 @@ interface WpdNoteDefinition {
10
10
  readonly marker: string;
11
11
  readonly blocks: readonly ContentBlock[];
12
12
  }
13
+ declare const UNREACHABLE_CHARACTER_MAPPING_MESSAGE = "A single-byte document-area character had no character mapping, which the tokeniser's own byte range should make unreachable.";
14
+ declare function assertDefined<T>(value: T | undefined, message: string): asserts value is T;
13
15
  declare function readWpdContent(bytes: Uint8Array, options?: ReadWpdOptions): ContentDocument;
14
16
  declare function readWpd(bytes: Uint8Array, options?: ReadWpdOptions): DocumentTree;
15
17
  //#endregion
16
- export { ReadWpdOptions, WpdNoteDefinition, readWpd, readWpdContent };
18
+ export { ReadWpdOptions, UNREACHABLE_CHARACTER_MAPPING_MESSAGE, WpdNoteDefinition, assertDefined, readWpd, readWpdContent };
package/dist/read.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { WpdFormatError } from "./errors.js";
1
2
  import { uint16At } from "./bytes/view.js";
2
3
  import { bytesToBase64 } from "./bytes/base64.js";
3
4
  import { readFurnitureClaim } from "./stream/furniture.js";
@@ -86,6 +87,10 @@ function flushRun(state) {
86
87
  state.runs.push(buildRun(state));
87
88
  state.text = "";
88
89
  }
90
+ const UNREACHABLE_CHARACTER_MAPPING_MESSAGE = "A single-byte document-area character had no character mapping, which the tokeniser's own byte range should make unreachable.";
91
+ function assertDefined(value, message) {
92
+ if (value === void 0) throw new WpdFormatError(message);
93
+ }
89
94
  function targetBlocks(state) {
90
95
  return state.table === void 0 ? state.blocks : state.table.cellBlocks;
91
96
  }
@@ -119,10 +124,7 @@ function flushParagraphIfContent(state, sink) {
119
124
  flushParagraph(state, sink);
120
125
  }
121
126
  function effectiveStyle(state) {
122
- for (let index = state.styleScopes.length - 1; index >= 0; index -= 1) {
123
- const semantics = state.styleScopes[index]?.semantics;
124
- if (semantics !== void 0) return semantics;
125
- }
127
+ return state.styleScopes.findLast((scope) => scope.semantics !== void 0)?.semantics;
126
128
  }
127
129
  function appendText(state, text) {
128
130
  if (state.skipDepth > 0 || state.numberDisplayDepth > 0) return;
@@ -271,7 +273,6 @@ function applySingleByteFunction(state, code, sink) {
271
273
  case END_OF_TEXT_TO_SKIP:
272
274
  state.skipDepth = Math.max(0, state.skipDepth - 1);
273
275
  return;
274
- default: return;
275
276
  }
276
277
  }
277
278
  function applyFontFaceChange(state, prefixIds, container, sink) {
@@ -379,7 +380,7 @@ function applyStyleGroup(state, token, container, sink) {
379
380
  function applyDisplayNumberGroup(state, token, sink) {
380
381
  if (isParagraphNumberDisplayOn(token.subgroup)) {
381
382
  const level = readDisplayNumberLevel(token.nonDeletable);
382
- if (level !== void 0 && state.pendingListLevel === void 0) state.pendingListLevel = level;
383
+ state.pendingListLevel ??= level;
383
384
  state.numberDisplayDepth += 1;
384
385
  reportOnce(state, sink, WpdDiagnosticCodes.OutlineNumberRegenerated, "An outline number's rendered digits were replaced by the list membership that regenerates them.");
385
386
  return;
@@ -432,7 +433,6 @@ function applyCharacterGroup(state, token, container, sink) {
432
433
  };
433
434
  return;
434
435
  }
435
- default: return;
436
436
  }
437
437
  }
438
438
  function applyHeaderFooterGroup(state, token, container, sink) {
@@ -748,7 +748,6 @@ function applyVariableFunction(state, token, container, sink) {
748
748
  case BOX_GROUP:
749
749
  applyBoxGroup(state, token, container, sink);
750
750
  return;
751
- default: return;
752
751
  }
753
752
  }
754
753
  function applyFixedFunction(state, token, sink) {
@@ -784,14 +783,7 @@ function applyToken(state, token, container, sink) {
784
783
  switch (token.kind) {
785
784
  case "character": {
786
785
  const character = decodeSingleByteCharacter(token.byte);
787
- if (character === void 0) {
788
- sink({
789
- code: WpdDiagnosticCodes.UnmappedCharacter,
790
- message: `Byte ${token.byte} in the document area has no character mapping and was rendered as U+FFFD.`
791
- });
792
- appendText(state, "�");
793
- return;
794
- }
786
+ assertDefined(character, UNREACHABLE_CHARACTER_MAPPING_MESSAGE);
795
787
  appendText(state, character);
796
788
  return;
797
789
  }
@@ -933,7 +925,6 @@ function readWpd(bytes, options = {}) {
933
925
  marker: note.marker,
934
926
  blocks: note.blocks
935
927
  }]));
936
- if (Object.keys(attachments).length === 0 && notes.length === 0) return assembled;
937
928
  return {
938
929
  ...assembled,
939
930
  ...Object.keys(attachments).length > 0 ? { attachments: {
@@ -947,4 +938,4 @@ function readWpd(bytes, options = {}) {
947
938
  };
948
939
  }
949
940
  //#endregion
950
- export { readWpd, readWpdContent };
941
+ export { UNREACHABLE_CHARACTER_MAPPING_MESSAGE, assertDefined, readWpd, readWpdContent };
@@ -8,83 +8,74 @@ const OVERRIDE_BIT_HTML = 7;
8
8
  const OVERRIDE_FLAGS_OFFSET = 18;
9
9
  const FIRST_OVERRIDE_BLOCK_OFFSET = 20;
10
10
  function walkOverrideBlocks(nonDeletable) {
11
- if (nonDeletable.length < FIRST_OVERRIDE_BLOCK_OFFSET) return;
12
- const flags = require_bytes_view.uint16At(nonDeletable, OVERRIDE_FLAGS_OFFSET);
13
- const blocks = /* @__PURE__ */ new Map();
14
- let cursor = FIRST_OVERRIDE_BLOCK_OFFSET;
15
- for (let bit = 15; bit >= 5; bit -= 1) {
16
- if ((flags & 1 << bit) === 0) continue;
17
- if (bit === OVERRIDE_BIT_HTML) continue;
18
- if (cursor + 2 > nonDeletable.length) return;
19
- const size = require_bytes_view.uint16At(nonDeletable, cursor);
20
- cursor += 2;
21
- if (cursor + size > nonDeletable.length) return;
22
- blocks.set(bit, nonDeletable.subarray(cursor, cursor + size));
23
- cursor += size;
11
+ try {
12
+ const flags = require_bytes_view.uint16At(nonDeletable, OVERRIDE_FLAGS_OFFSET);
13
+ const blocks = /* @__PURE__ */ new Map();
14
+ let cursor = FIRST_OVERRIDE_BLOCK_OFFSET;
15
+ for (let bit = 15; bit >= 5; bit -= 1) {
16
+ if ((flags & 1 << bit) === 0) continue;
17
+ if (bit === OVERRIDE_BIT_HTML) continue;
18
+ const size = require_bytes_view.uint16At(nonDeletable, cursor);
19
+ cursor += 2;
20
+ if (cursor + size > nonDeletable.length) return;
21
+ blocks.set(bit, nonDeletable.subarray(cursor, cursor + size));
22
+ cursor += size;
23
+ }
24
+ return {
25
+ flags,
26
+ blocks
27
+ };
28
+ } catch {
29
+ return;
24
30
  }
25
- return {
26
- flags,
27
- blocks
28
- };
29
31
  }
30
32
  function readContentType(contentBlock) {
31
- if (contentBlock.length < 2) return;
32
- const flags = require_bytes_view.uint16At(contentBlock, 0);
33
- let cursor = 2;
34
- if ((flags & 32768) !== 0) {
35
- if (cursor + 2 > contentBlock.length) return;
36
- cursor += 2;
33
+ try {
34
+ const flags = require_bytes_view.uint16At(contentBlock, 0);
35
+ let cursor = 2;
36
+ if ((flags & 32768) !== 0) cursor += 2;
37
+ if ((flags & 16384) === 0) return;
38
+ return contentBlock[cursor];
39
+ } catch {
40
+ return;
37
41
  }
38
- if ((flags & 16384) === 0) return;
39
- return contentBlock[cursor];
40
42
  }
41
43
  function readPositionOverride(positionBlock) {
42
- if (positionBlock.length < 2) return;
43
- const flags = require_bytes_view.uint16At(positionBlock, 0);
44
- let cursor = 2;
45
- let widthWpu;
46
- let heightWpu;
47
- let xWpu;
48
- let yWpu;
49
- const need = (bytes) => cursor + bytes <= positionBlock.length;
50
- if ((flags & 32768) !== 0) {
51
- if (!need(2)) return void 0;
52
- cursor += 2;
53
- }
54
- if ((flags & 16384) !== 0) {
55
- if (!need(2)) return void 0;
56
- cursor += 2;
57
- }
58
- if ((flags & 8192) !== 0) {
59
- if (!need(5)) return void 0;
60
- const horizontalFlags = positionBlock[cursor];
61
- const offset = require_bytes_view.uint16At(positionBlock, cursor + 1);
62
- if (horizontalFlags !== void 0 && (horizontalFlags & 3) === 0) xWpu = offset;
63
- cursor += 5;
64
- }
65
- if ((flags & 4096) !== 0) {
66
- if (!need(3)) return void 0;
67
- const verticalFlags = positionBlock[cursor];
68
- const offset = require_bytes_view.uint16At(positionBlock, cursor + 1);
69
- if (verticalFlags !== void 0 && (verticalFlags & 3) === 0) yWpu = offset;
70
- cursor += 3;
71
- }
72
- if ((flags & 2048) !== 0) {
73
- if (!need(3)) return void 0;
74
- widthWpu = require_bytes_view.uint16At(positionBlock, cursor + 1);
75
- cursor += 3;
76
- }
77
- if ((flags & 1024) !== 0) {
78
- if (!need(3)) return void 0;
79
- heightWpu = require_bytes_view.uint16At(positionBlock, cursor + 1);
80
- cursor += 3;
44
+ try {
45
+ const flags = require_bytes_view.uint16At(positionBlock, 0);
46
+ let cursor = 2;
47
+ let widthWpu;
48
+ let heightWpu;
49
+ let xWpu;
50
+ let yWpu;
51
+ if ((flags & 32768) !== 0) cursor += 2;
52
+ if ((flags & 16384) !== 0) cursor += 2;
53
+ if ((flags & 8192) !== 0) {
54
+ const horizontalFlags = positionBlock[cursor];
55
+ const offset = require_bytes_view.uint16At(positionBlock, cursor + 1);
56
+ if (horizontalFlags !== void 0 && (horizontalFlags & 3) === 0) xWpu = offset;
57
+ cursor += 5;
58
+ }
59
+ if ((flags & 4096) !== 0) {
60
+ const verticalFlags = positionBlock[cursor];
61
+ const offset = require_bytes_view.uint16At(positionBlock, cursor + 1);
62
+ if (verticalFlags !== void 0 && (verticalFlags & 3) === 0) yWpu = offset;
63
+ cursor += 3;
64
+ }
65
+ if ((flags & 2048) !== 0) {
66
+ widthWpu = require_bytes_view.uint16At(positionBlock, cursor + 1);
67
+ cursor += 3;
68
+ }
69
+ if ((flags & 1024) !== 0) heightWpu = require_bytes_view.uint16At(positionBlock, cursor + 1);
70
+ return {
71
+ widthWpu,
72
+ heightWpu,
73
+ xWpu,
74
+ yWpu
75
+ };
76
+ } catch {
77
+ return;
81
78
  }
82
- return {
83
- widthWpu,
84
- heightWpu,
85
- xWpu,
86
- yWpu
87
- };
88
79
  }
89
80
  const BOX_CONTENT_TYPE_TEXT = 1;
90
81
  const BOX_CONTENT_TYPE_LINKED_TEXT = 2;