@file-viewer/doc 2.1.10

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.
@@ -0,0 +1,192 @@
1
+ import { BinaryReader } from '../core/binary.js';
2
+ import { dataUrlFromBytes, slugify, uniqueId } from '../core/utils.js';
3
+ function startsWith(bytes, signature, offset = 0) {
4
+ if (offset + signature.length > bytes.length)
5
+ return false;
6
+ for (let i = 0; i < signature.length; i += 1) {
7
+ if (bytes[offset + i] !== signature[i])
8
+ return false;
9
+ }
10
+ return true;
11
+ }
12
+ export function detectImageSegment(bytes) {
13
+ const sigs = [
14
+ { mime: 'image/png', magic: [0x89, 0x50, 0x4e, 0x47], end: findPngEnd },
15
+ { mime: 'image/jpeg', magic: [0xff, 0xd8, 0xff], end: findJpegEnd },
16
+ { mime: 'image/gif', magic: [0x47, 0x49, 0x46, 0x38], end: findGifEnd },
17
+ { mime: 'image/bmp', magic: [0x42, 0x4d], end: findBmpEnd },
18
+ { mime: 'image/tiff', magic: [0x49, 0x49, 0x2a, 0x00], end: null },
19
+ { mime: 'image/tiff', magic: [0x4d, 0x4d, 0x00, 0x2a], end: null },
20
+ { mime: 'image/emf', magic: [0x01, 0x00, 0x00, 0x00], end: findEmfEnd },
21
+ { mime: 'image/wmf', magic: [0xd7, 0xcd, 0xc6, 0x9a], end: null },
22
+ ];
23
+ for (let i = 0; i < bytes.length - 4; i += 1) {
24
+ for (const sig of sigs) {
25
+ if (startsWith(bytes, sig.magic, i)) {
26
+ const end = sig.end ? sig.end(bytes, i) : bytes.length;
27
+ return { mime: sig.mime, start: i, end: end || bytes.length };
28
+ }
29
+ }
30
+ }
31
+ return null;
32
+ }
33
+ function findPngEnd(bytes, start) {
34
+ for (let i = start + 8; i + 8 < bytes.length; i += 1) {
35
+ if (startsWith(bytes, [0x49, 0x45, 0x4e, 0x44], i))
36
+ return i + 8;
37
+ }
38
+ return bytes.length;
39
+ }
40
+ function findJpegEnd(bytes, start) {
41
+ for (let i = start + 2; i + 1 < bytes.length; i += 1) {
42
+ if (bytes[i] === 0xff && bytes[i + 1] === 0xd9)
43
+ return i + 2;
44
+ }
45
+ return bytes.length;
46
+ }
47
+ function findGifEnd(bytes, start) {
48
+ for (let i = start + 6; i < bytes.length; i += 1) {
49
+ if (bytes[i] === 0x3b)
50
+ return i + 1;
51
+ }
52
+ return bytes.length;
53
+ }
54
+ function findBmpEnd(bytes, start) {
55
+ if (start + 6 <= bytes.length) {
56
+ const size = new BinaryReader(bytes.subarray(start)).u32(2);
57
+ if (size > 0 && start + size <= bytes.length)
58
+ return start + size;
59
+ }
60
+ return bytes.length;
61
+ }
62
+ function findEmfEnd(bytes, start) {
63
+ if (start + 48 <= bytes.length) {
64
+ const reader = new BinaryReader(bytes.subarray(start));
65
+ const size = reader.u32(40);
66
+ if (size > 0 && start + size <= bytes.length)
67
+ return start + size;
68
+ }
69
+ return bytes.length;
70
+ }
71
+ export function extractPictureAsset(dataStreamBytes, pictureOffset, options = {}) {
72
+ if (!dataStreamBytes || pictureOffset == null || pictureOffset < 0 || pictureOffset + 68 > dataStreamBytes.length)
73
+ return null;
74
+ const reader = new BinaryReader(dataStreamBytes);
75
+ const lcb = reader.i32(pictureOffset);
76
+ const cbHeader = reader.u16(pictureOffset + 4);
77
+ const total = lcb > 0 && pictureOffset + lcb <= dataStreamBytes.length
78
+ ? lcb
79
+ : Math.min(dataStreamBytes.length - pictureOffset, options.maxPictureBytes || 8 * 1024 * 1024);
80
+ const pictureChunk = dataStreamBytes.subarray(pictureOffset, pictureOffset + total);
81
+ const bodyStart = Math.min(cbHeader || 68, pictureChunk.length);
82
+ const segment = detectImageSegment(pictureChunk.subarray(bodyStart));
83
+ if (!segment) {
84
+ return {
85
+ id: uniqueId('asset-img'),
86
+ type: 'image',
87
+ mime: 'application/octet-stream',
88
+ bytes: pictureChunk,
89
+ dataUrl: dataUrlFromBytes(pictureChunk, 'application/octet-stream'),
90
+ meta: { pictureOffset, lcb, cbHeader },
91
+ };
92
+ }
93
+ const start = bodyStart + segment.start;
94
+ const end = Math.min(bodyStart + segment.end, pictureChunk.length);
95
+ const imageBytes = pictureChunk.subarray(start, end);
96
+ return {
97
+ id: uniqueId('asset-img'),
98
+ type: 'image',
99
+ mime: segment.mime,
100
+ bytes: imageBytes,
101
+ dataUrl: dataUrlFromBytes(imageBytes, segment.mime),
102
+ meta: { pictureOffset, lcb, cbHeader },
103
+ };
104
+ }
105
+ function readCString(bytes, offset) {
106
+ let end = offset;
107
+ while (end < bytes.length && bytes[end] !== 0)
108
+ end += 1;
109
+ const value = new TextDecoder('windows-1252').decode(bytes.subarray(offset, end));
110
+ return { value, nextOffset: end + 1 };
111
+ }
112
+ export function parseOle10Native(streamBytes) {
113
+ const reader = new BinaryReader(streamBytes);
114
+ const variants = [4, 6];
115
+ for (const start of variants) {
116
+ try {
117
+ let offset = start;
118
+ const label = readCString(streamBytes, offset);
119
+ offset = label.nextOffset;
120
+ const originalPath = readCString(streamBytes, offset);
121
+ offset = originalPath.nextOffset;
122
+ const tempPath = readCString(streamBytes, offset);
123
+ offset = tempPath.nextOffset;
124
+ if (offset + 4 > streamBytes.length)
125
+ continue;
126
+ const dataSize = reader.u32(offset);
127
+ offset += 4;
128
+ if (dataSize > 0 && offset + dataSize <= streamBytes.length) {
129
+ const bytes = streamBytes.subarray(offset, offset + dataSize);
130
+ return { label: label.value, originalPath: originalPath.value, tempPath: tempPath.value, dataSize, bytes };
131
+ }
132
+ }
133
+ catch {
134
+ // try next variant
135
+ }
136
+ }
137
+ return null;
138
+ }
139
+ function readObjectStorage(cfb, entry) {
140
+ const streams = cfb.listChildren(entry).filter((child) => child.objectType === 2 || child.objectType === 5);
141
+ const lower = new Map(streams.map((child) => [child.name.toLowerCase(), child]));
142
+ const objInfo = lower.get('\u0003objinfo') || lower.get('\x03objinfo') || lower.get(String.fromCharCode(3) + 'objinfo');
143
+ const ole10 = lower.get('\u0001ole10native') || lower.get('\x01ole10native') || lower.get(String.fromCharCode(1) + 'ole10native');
144
+ const packageStream = lower.get('package') || lower.get('contents') || lower.get('content');
145
+ const info = { entry, streams, displayName: entry.name, attachment: null, objectData: null };
146
+ if (ole10) {
147
+ const bytes = cfb.getStream(ole10) || new Uint8Array(0);
148
+ const nativeInfo = parseOle10Native(bytes);
149
+ if (nativeInfo) {
150
+ const name = nativeInfo.label || nativeInfo.originalPath.split(/[\\/]/).pop() || `${entry.name}.bin`;
151
+ const attachment = {
152
+ id: uniqueId('asset-ole'),
153
+ type: 'attachment',
154
+ name,
155
+ mime: 'application/octet-stream',
156
+ bytes: nativeInfo.bytes,
157
+ dataUrl: dataUrlFromBytes(nativeInfo.bytes, 'application/octet-stream'),
158
+ meta: nativeInfo,
159
+ };
160
+ info.attachment = attachment;
161
+ return info;
162
+ }
163
+ }
164
+ if (packageStream) {
165
+ const bytes = cfb.getStream(packageStream) || new Uint8Array(0);
166
+ const attachment = {
167
+ id: uniqueId('asset-pkg'),
168
+ type: 'attachment',
169
+ name: `${slugify(entry.name)}.bin`,
170
+ mime: 'application/octet-stream',
171
+ bytes,
172
+ dataUrl: dataUrlFromBytes(bytes, 'application/octet-stream'),
173
+ meta: { stream: packageStream.name },
174
+ };
175
+ info.attachment = attachment;
176
+ }
177
+ if (objInfo) {
178
+ info.objectData = cfb.getStream(objInfo) || new Uint8Array(0);
179
+ }
180
+ return info;
181
+ }
182
+ export function extractObjectPool(cfb) {
183
+ const objectPoolEntry = cfb.getEntry('/ObjectPool');
184
+ if (!objectPoolEntry)
185
+ return new Map();
186
+ const storages = cfb.listChildren(objectPoolEntry).filter((entry) => entry.objectType === 1);
187
+ const map = new Map();
188
+ for (const storage of storages) {
189
+ map.set(storage.name, readObjectStorage(cfb, storage));
190
+ }
191
+ return map;
192
+ }
@@ -0,0 +1,8 @@
1
+ import type { MsDocParseOptions, MsDocParseResult } from '../types.js';
2
+ /**
3
+ * Main MS-DOC entry point.
4
+ * It parses the OLE container, restores text through the piece table, resolves
5
+ * paragraph/character/table properties, and finally produces a normalized AST
6
+ * that the HTML renderer can consume.
7
+ */
8
+ export declare function parseMsDoc(input: ArrayBuffer | Uint8Array | ArrayBufferView, options?: MsDocParseOptions): MsDocParseResult;