@l1yp/file-viewer 0.1.4 → 0.1.5
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 +2 -0
- package/dist/components/AnsiText.d.ts +47 -0
- package/dist/components/AnsiText.js +41 -0
- package/dist/components/AnsiText.js.map +1 -0
- package/dist/components/CodeView.js +1 -1
- package/dist/components/CodeView.js.map +1 -1
- package/dist/components/CodeView.vue_vue_type_script_setup_true_lang.js +80 -71
- package/dist/components/CodeView.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/DocxEmbeddedFiles.js +9 -0
- package/dist/components/DocxEmbeddedFiles.js.map +1 -0
- package/dist/components/DocxEmbeddedFiles.vue.d.ts +15 -0
- package/dist/components/DocxEmbeddedFiles.vue_vue_type_script_setup_true_lang.js +69 -0
- package/dist/components/DocxEmbeddedFiles.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/DocxViewer.js +1 -1
- package/dist/components/DocxViewer.js.map +1 -1
- package/dist/components/DocxViewer.vue_vue_type_script_setup_true_lang.js +180 -50
- package/dist/components/DocxViewer.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/FileViewerBody.js +1 -1
- package/dist/components/FileViewerBody.js.map +1 -1
- package/dist/components/FileViewerBody.vue.d.ts +1 -0
- package/dist/components/FileViewerBody.vue_vue_type_script_setup_true_lang.js +117 -114
- package/dist/components/FileViewerBody.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/FileViewerPanel.js +1 -1
- package/dist/components/FileViewerPanel.js.map +1 -1
- package/dist/components/FileViewerPanel.vue_vue_type_script_setup_true_lang.js +18 -18
- package/dist/components/FileViewerPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/LogViewer.js +1 -1
- package/dist/components/LogViewer.js.map +1 -1
- package/dist/components/LogViewer.vue.d.ts +3 -1
- package/dist/components/LogViewer.vue_vue_type_script_setup_true_lang.js +284 -280
- package/dist/components/LogViewer.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/lib/ansi.d.ts +44 -0
- package/dist/lib/ansi.js +224 -0
- package/dist/lib/ansi.js.map +1 -0
- package/dist/lib/cfb.d.ts +37 -0
- package/dist/lib/cfb.js +112 -0
- package/dist/lib/cfb.js.map +1 -0
- package/dist/lib/docxEmbeddedDom.d.ts +22 -0
- package/dist/lib/docxEmbeddedDom.js +128 -0
- package/dist/lib/docxEmbeddedDom.js.map +1 -0
- package/dist/lib/docxEmbeddings.d.ts +41 -0
- package/dist/lib/docxEmbeddings.js +226 -0
- package/dist/lib/docxEmbeddings.js.map +1 -0
- package/dist/lib/msi.js +62 -154
- package/dist/lib/msi.js.map +1 -1
- package/dist/lib/olePackage.d.ts +8 -0
- package/dist/lib/olePackage.js +37 -0
- package/dist/lib/olePackage.js.map +1 -0
- package/dist/lib/pptxFirstSlide.d.ts +11 -0
- package/dist/lib/pptxFirstSlide.js +34 -0
- package/dist/lib/pptxFirstSlide.js.map +1 -0
- package/dist/style.css +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { extractZipEntry as e, readZip as t } from "./zip.js";
|
|
2
|
+
import { extractOlePackage as n } from "./olePackage.js";
|
|
3
|
+
//#region src/lib/docxEmbeddings.ts
|
|
4
|
+
var r = "word/document.xml", i = "word/_rels/document.xml.rels", a = "[Content_Types].xml", o = 100 * 1024 * 1024, s = 20 * 1024 * 1024, c = {
|
|
5
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
6
|
+
pdf: "application/pdf",
|
|
7
|
+
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
8
|
+
xls: "application/vnd.ms-excel",
|
|
9
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
10
|
+
};
|
|
11
|
+
function l(e, t) {
|
|
12
|
+
let n = new DOMParser().parseFromString(e, "application/xml"), r = Array.from(n.getElementsByTagNameNS("*", "parsererror"))[0];
|
|
13
|
+
if (r) throw Error(`${t} XML 无法解析:${r.textContent?.trim() || "格式错误"}`);
|
|
14
|
+
return n;
|
|
15
|
+
}
|
|
16
|
+
function u(e, t) {
|
|
17
|
+
return Array.from(e.getElementsByTagNameNS("*", t));
|
|
18
|
+
}
|
|
19
|
+
function d(e, t) {
|
|
20
|
+
return Array.from(e.attributes).find((e) => e.localName === t)?.value ?? "";
|
|
21
|
+
}
|
|
22
|
+
function f(e) {
|
|
23
|
+
return new TextDecoder("utf-8").decode(e);
|
|
24
|
+
}
|
|
25
|
+
function p(e) {
|
|
26
|
+
let t = e.lastIndexOf(".");
|
|
27
|
+
return t > 0 && t < e.length - 1 ? e.slice(t + 1).toLowerCase() : "";
|
|
28
|
+
}
|
|
29
|
+
function m(e) {
|
|
30
|
+
return e.slice(e.lastIndexOf("/") + 1);
|
|
31
|
+
}
|
|
32
|
+
function h(e) {
|
|
33
|
+
for (let t of e) if (t.charCodeAt(0) < 32) return !0;
|
|
34
|
+
return !1;
|
|
35
|
+
}
|
|
36
|
+
function g(e) {
|
|
37
|
+
let t = "";
|
|
38
|
+
for (let n of e) t += n.charCodeAt(0) < 32 || "<>:\"/\\|?*".includes(n) ? "_" : n;
|
|
39
|
+
return t;
|
|
40
|
+
}
|
|
41
|
+
function _(e, t) {
|
|
42
|
+
let n = t.split(/[?#]/, 1)[0]?.replace(/\\/g, "/") ?? "";
|
|
43
|
+
if (!n || h(n)) return "";
|
|
44
|
+
let r = n.startsWith("/") ? [] : e.split("/").slice(0, -1);
|
|
45
|
+
for (let e of n.split("/")) {
|
|
46
|
+
let t;
|
|
47
|
+
try {
|
|
48
|
+
t = decodeURIComponent(e);
|
|
49
|
+
} catch {
|
|
50
|
+
return "";
|
|
51
|
+
}
|
|
52
|
+
if (!(!t || t === ".")) {
|
|
53
|
+
if (t === "..") {
|
|
54
|
+
if (r.length === 0) return "";
|
|
55
|
+
r.pop();
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (t.includes("/") || t.includes("\\")) return "";
|
|
59
|
+
r.push(t);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return r.join("/");
|
|
63
|
+
}
|
|
64
|
+
function v(e) {
|
|
65
|
+
return u(e, "t").map((e) => e.textContent ?? "").join("").replace(/\s+/g, " ").trim();
|
|
66
|
+
}
|
|
67
|
+
function y(e) {
|
|
68
|
+
let t = e.replace(/\s+/g, " ").trim();
|
|
69
|
+
return /^(?:点击|双击).*(?:查看|打开).*(?:文件|表格)/.test(t) ? "" : t.slice(0, 100);
|
|
70
|
+
}
|
|
71
|
+
function b(e, t) {
|
|
72
|
+
if (!e) return t;
|
|
73
|
+
let n = g(e).replace(/[. ]+$/g, "").trim().slice(0, 120);
|
|
74
|
+
if (!n) return t;
|
|
75
|
+
let r = p(t);
|
|
76
|
+
return r && !n.toLowerCase().endsWith(`.${r}`) ? `${n}.${r}` : n;
|
|
77
|
+
}
|
|
78
|
+
function x(e) {
|
|
79
|
+
let t = l(e, "DOCX relationship"), n = /* @__PURE__ */ new Map();
|
|
80
|
+
for (let e of u(t, "Relationship")) {
|
|
81
|
+
let t = e.getAttribute("Id") ?? "", r = e.getAttribute("Target") ?? "";
|
|
82
|
+
!t || !r || n.set(t, {
|
|
83
|
+
target: r,
|
|
84
|
+
type: e.getAttribute("Type") ?? "",
|
|
85
|
+
external: (e.getAttribute("TargetMode") ?? "").toLowerCase() === "external"
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return n;
|
|
89
|
+
}
|
|
90
|
+
function S(e) {
|
|
91
|
+
let t = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map();
|
|
92
|
+
if (!e) return {
|
|
93
|
+
defaults: t,
|
|
94
|
+
overrides: n
|
|
95
|
+
};
|
|
96
|
+
let r = l(e, "DOCX content types");
|
|
97
|
+
for (let e of u(r, "Default")) {
|
|
98
|
+
let n = (e.getAttribute("Extension") ?? "").toLowerCase(), r = e.getAttribute("ContentType") ?? "";
|
|
99
|
+
n && r && t.set(n, r);
|
|
100
|
+
}
|
|
101
|
+
for (let e of u(r, "Override")) {
|
|
102
|
+
let t = (e.getAttribute("PartName") ?? "").replace(/^\/+/, ""), r = e.getAttribute("ContentType") ?? "";
|
|
103
|
+
t && r && n.set(t, r);
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
defaults: t,
|
|
107
|
+
overrides: n
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function C(e, t) {
|
|
111
|
+
let n = p(e);
|
|
112
|
+
return t.overrides.get(e) ?? t.defaults.get(n) ?? c[n] ?? "application/octet-stream";
|
|
113
|
+
}
|
|
114
|
+
function w(e) {
|
|
115
|
+
let t = e.toLowerCase();
|
|
116
|
+
return t === "powerpoint.show.12" || t === "powerpoint.slide.12" ? {
|
|
117
|
+
extension: "pptx",
|
|
118
|
+
contentType: c.pptx,
|
|
119
|
+
label: "PowerPoint 演示文稿"
|
|
120
|
+
} : t === "excel.sheet.12" ? {
|
|
121
|
+
extension: "xlsx",
|
|
122
|
+
contentType: c.xlsx,
|
|
123
|
+
label: "Excel 工作簿"
|
|
124
|
+
} : t === "word.document.12" ? {
|
|
125
|
+
extension: "docx",
|
|
126
|
+
contentType: c.docx,
|
|
127
|
+
label: "Word 文档"
|
|
128
|
+
} : null;
|
|
129
|
+
}
|
|
130
|
+
function T(e, t) {
|
|
131
|
+
let n = e;
|
|
132
|
+
for (; n;) {
|
|
133
|
+
if (n.localName === t) return n;
|
|
134
|
+
n = n.parentElement;
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
function E(e, t) {
|
|
139
|
+
let n = e.match(RegExp(`(?:^|;)\\s*${t}\\s*:\\s*([0-9]+(?:\\.[0-9]+)?)(pt|px|in|cm|mm)`, "i"));
|
|
140
|
+
if (!n) return;
|
|
141
|
+
let r = Number(n[1]), i = n[2]?.toLowerCase(), a = i === "pt" ? r : i === "px" ? r * .75 : i === "in" ? r * 72 : i === "cm" ? r * 72 / 2.54 : r * 72 / 25.4;
|
|
142
|
+
return Number.isFinite(a) && a > 0 && a <= 2e3 ? a : void 0;
|
|
143
|
+
}
|
|
144
|
+
function D(e, t, n, i) {
|
|
145
|
+
let a = T(e, "object");
|
|
146
|
+
if (!a) return;
|
|
147
|
+
let o = u(a, "imagedata")[0], s = o ? d(o, "id") : "", c = t.get(s);
|
|
148
|
+
if (!s || !c || c.external || !c.type.toLowerCase().endsWith("/image")) return;
|
|
149
|
+
let l = _(r, c.target), f = n.get(l);
|
|
150
|
+
if (!l || !f) return;
|
|
151
|
+
let p = u(a, "shape")[0]?.getAttribute("style") ?? "";
|
|
152
|
+
return {
|
|
153
|
+
path: l,
|
|
154
|
+
size: f.size,
|
|
155
|
+
contentType: C(l, i),
|
|
156
|
+
widthPt: E(p, "width"),
|
|
157
|
+
heightPt: E(p, "height"),
|
|
158
|
+
entry: f
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function O(e, t) {
|
|
162
|
+
return e.length >= 8 && e[0] === 137 && e[1] === 80 && e[2] === 78 && e[3] === 71 ? "image/png" : e.length >= 3 && e[0] === 255 && e[1] === 216 && e[2] === 255 ? "image/jpeg" : e.length >= 6 && e[0] === 71 && e[1] === 73 && e[2] === 70 && e[3] === 56 ? "image/gif" : e.length >= 2 && e[0] === 66 && e[1] === 77 ? "image/bmp" : e.length >= 12 && e[0] === 82 && e[1] === 73 && e[2] === 70 && e[3] === 70 && e[8] === 87 && e[9] === 69 && e[10] === 66 && e[11] === 80 ? "image/webp" : t.startsWith("image/") ? t : "application/octet-stream";
|
|
163
|
+
}
|
|
164
|
+
async function k(n) {
|
|
165
|
+
let o = t(n), s = new Map(o.filter((e) => !e.isDir).map((e) => [e.path, e])), c = s.get(r), p = s.get(i);
|
|
166
|
+
if (!c || !p) return [];
|
|
167
|
+
let [h, g, T] = await Promise.all([
|
|
168
|
+
e(n, c).then(f),
|
|
169
|
+
e(n, p).then(f),
|
|
170
|
+
s.has(a) ? e(n, s.get(a)).then(f) : Promise.resolve(null)
|
|
171
|
+
]), E = l(h, "DOCX document"), O = x(g), k = S(T), A = [], j = /* @__PURE__ */ new Set(), M = "";
|
|
172
|
+
for (let e of u(E, "p")) {
|
|
173
|
+
let t = v(e);
|
|
174
|
+
for (let n of u(e, "OLEObject")) {
|
|
175
|
+
let e = d(n, "id"), i = O.get(e);
|
|
176
|
+
if (!e || !i || i.external) continue;
|
|
177
|
+
let a = i.type.toLowerCase(), o = a.endsWith("/package"), c = a.endsWith("/oleobject");
|
|
178
|
+
if (!o && !c) continue;
|
|
179
|
+
let l = n.getAttribute("ProgID") ?? d(n, "ProgID"), u = c ? w(l) : null;
|
|
180
|
+
if (c && !u) continue;
|
|
181
|
+
let f = _(r, i.target), p = s.get(f);
|
|
182
|
+
if (!f || !p || j.has(f)) continue;
|
|
183
|
+
j.add(f);
|
|
184
|
+
let h = m(f), g = u ? `${u.label}.${u.extension}` : h, v = y(t || (u ? u.label : M));
|
|
185
|
+
A.push({
|
|
186
|
+
id: e,
|
|
187
|
+
label: v,
|
|
188
|
+
name: b(v, g),
|
|
189
|
+
originalName: g,
|
|
190
|
+
path: f,
|
|
191
|
+
size: p.size,
|
|
192
|
+
contentType: u?.contentType ?? C(f, k),
|
|
193
|
+
storage: u ? "ole-package" : "package",
|
|
194
|
+
preview: D(n, O, s, k),
|
|
195
|
+
entry: p
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
t && (M = t);
|
|
199
|
+
}
|
|
200
|
+
return A;
|
|
201
|
+
}
|
|
202
|
+
async function A(t, r) {
|
|
203
|
+
if (r.size > o) throw Error(`内嵌文件超过 ${o / 1024 / 1024} MiB,已拒绝在浏览器中展开`);
|
|
204
|
+
let i = await e(t, r.entry);
|
|
205
|
+
return r.storage === "ole-package" ? n(i, o) : i;
|
|
206
|
+
}
|
|
207
|
+
async function j(t, n) {
|
|
208
|
+
let r = n.preview;
|
|
209
|
+
if (!r) return null;
|
|
210
|
+
if (r.size > s) throw Error(`内嵌预览图超过 ${s / 1024 / 1024} MiB,已跳过正文预览`);
|
|
211
|
+
let i = await e(t, r.entry), a = O(i, r.contentType);
|
|
212
|
+
return [
|
|
213
|
+
"image/png",
|
|
214
|
+
"image/jpeg",
|
|
215
|
+
"image/gif",
|
|
216
|
+
"image/bmp",
|
|
217
|
+
"image/webp"
|
|
218
|
+
].includes(a) ? {
|
|
219
|
+
bytes: i,
|
|
220
|
+
contentType: a
|
|
221
|
+
} : null;
|
|
222
|
+
}
|
|
223
|
+
//#endregion
|
|
224
|
+
export { A as extractDocxEmbeddedFile, j as extractDocxEmbeddedPreview, k as readDocxEmbeddedFiles };
|
|
225
|
+
|
|
226
|
+
//# sourceMappingURL=docxEmbeddings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docxEmbeddings.js","names":[],"sources":["../../src/lib/docxEmbeddings.ts"],"sourcesContent":["import { extractZipEntry, readZip, type ZipEntry } from '@/lib/zip'\nimport { extractOlePackage } from '@/lib/olePackage'\n\nconst DOCUMENT_PART = 'word/document.xml'\nconst DOCUMENT_RELS_PART = 'word/_rels/document.xml.rels'\nconst CONTENT_TYPES_PART = '[Content_Types].xml'\nconst MAX_EMBEDDED_FILE_BYTES = 100 * 1024 * 1024\nconst MAX_PREVIEW_IMAGE_BYTES = 20 * 1024 * 1024\n\nconst MIME_BY_EXTENSION: Record<string, string> = {\n docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n pdf: 'application/pdf',\n pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n xls: 'application/vnd.ms-excel',\n xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n}\n\nexport interface DocxEmbeddedFile {\n /** Stable relationship id inside word/document.xml. */\n id: string\n /** Human-facing caption inferred from the nearest preceding Word paragraph. */\n label: string\n /** Friendly download name (caption + original extension when a caption exists). */\n name: string\n /** Original package basename, e.g. Microsoft_Excel_Worksheet1.xlsx. */\n originalName: string\n /** Resolved package path inside the DOCX ZIP. */\n path: string\n size: number\n contentType: string\n storage: 'package' | 'ole-package'\n /** Word/WPS-generated visual snapshot for the embedded object, when present. */\n preview?: DocxEmbeddedPreview\n /** Internal ZIP descriptor retained so extraction stays on-demand. */\n entry: ZipEntry\n}\n\nexport interface DocxEmbeddedPreview {\n path: string\n size: number\n contentType: string\n widthPt?: number\n heightPt?: number\n entry: ZipEntry\n}\n\nexport interface ExtractedDocxPreview {\n bytes: Uint8Array\n contentType: string\n}\n\ninterface Relationship {\n target: string\n type: string\n external: boolean\n}\n\ninterface OlePackageKind {\n extension: string\n contentType: string\n label: string\n}\n\nfunction parseXml(xml: string, label: string): XMLDocument {\n const document = new DOMParser().parseFromString(xml, 'application/xml')\n const error = Array.from(document.getElementsByTagNameNS('*', 'parsererror'))[0]\n if (error) throw new Error(`${label} XML 无法解析:${error.textContent?.trim() || '格式错误'}`)\n return document\n}\n\nfunction elements(root: Document | Element, localName: string): Element[] {\n return Array.from(root.getElementsByTagNameNS('*', localName))\n}\n\nfunction attrByLocalName(element: Element, localName: string): string {\n return (\n Array.from(element.attributes).find((attribute) => attribute.localName === localName)?.value ??\n ''\n )\n}\n\nfunction decodeEntry(bytes: Uint8Array): string {\n return new TextDecoder('utf-8').decode(bytes)\n}\n\nfunction extension(name: string): string {\n const i = name.lastIndexOf('.')\n return i > 0 && i < name.length - 1 ? name.slice(i + 1).toLowerCase() : ''\n}\n\nfunction basename(path: string): string {\n return path.slice(path.lastIndexOf('/') + 1)\n}\n\nfunction hasControlCharacters(value: string): boolean {\n for (const character of value) if (character.charCodeAt(0) < 0x20) return true\n return false\n}\n\nfunction sanitizeFileName(value: string): string {\n const invalid = '<>:\"/\\\\|?*'\n let result = ''\n for (const character of value) {\n result += character.charCodeAt(0) < 0x20 || invalid.includes(character) ? '_' : character\n }\n return result\n}\n\n/** Resolve an OPC relationship target relative to its source part without allowing it above the package root. */\nfunction resolvePartTarget(sourcePart: string, target: string): string {\n const raw = target.split(/[?#]/, 1)[0]?.replace(/\\\\/g, '/') ?? ''\n if (!raw || hasControlCharacters(raw)) return ''\n const parts = raw.startsWith('/') ? [] : sourcePart.split('/').slice(0, -1)\n for (const encoded of raw.split('/')) {\n let segment: string\n try {\n segment = decodeURIComponent(encoded)\n } catch {\n return ''\n }\n if (!segment || segment === '.') continue\n if (segment === '..') {\n if (parts.length === 0) return ''\n parts.pop()\n continue\n }\n if (segment.includes('/') || segment.includes('\\\\')) return ''\n parts.push(segment)\n }\n return parts.join('/')\n}\n\nfunction paragraphText(paragraph: Element): string {\n return elements(paragraph, 't')\n .map((node) => node.textContent ?? '')\n .join('')\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\nfunction cleanLabel(value: string): string {\n const label = value.replace(/\\s+/g, ' ').trim()\n if (/^(?:点击|双击).*(?:查看|打开).*(?:文件|表格)/.test(label)) return ''\n return label.slice(0, 100)\n}\n\nfunction friendlyName(label: string, originalName: string): string {\n if (!label) return originalName\n const safe = sanitizeFileName(label)\n .replace(/[. ]+$/g, '')\n .trim()\n .slice(0, 120)\n if (!safe) return originalName\n const ext = extension(originalName)\n return ext && !safe.toLowerCase().endsWith(`.${ext}`) ? `${safe}.${ext}` : safe\n}\n\nfunction readRelationships(xml: string): Map<string, Relationship> {\n const document = parseXml(xml, 'DOCX relationship')\n const out = new Map<string, Relationship>()\n for (const node of elements(document, 'Relationship')) {\n const id = node.getAttribute('Id') ?? ''\n const target = node.getAttribute('Target') ?? ''\n if (!id || !target) continue\n out.set(id, {\n target,\n type: node.getAttribute('Type') ?? '',\n external: (node.getAttribute('TargetMode') ?? '').toLowerCase() === 'external',\n })\n }\n return out\n}\n\nfunction readContentTypes(xml: string | null): {\n defaults: Map<string, string>\n overrides: Map<string, string>\n} {\n const defaults = new Map<string, string>()\n const overrides = new Map<string, string>()\n if (!xml) return { defaults, overrides }\n const document = parseXml(xml, 'DOCX content types')\n for (const node of elements(document, 'Default')) {\n const ext = (node.getAttribute('Extension') ?? '').toLowerCase()\n const type = node.getAttribute('ContentType') ?? ''\n if (ext && type) defaults.set(ext, type)\n }\n for (const node of elements(document, 'Override')) {\n const path = (node.getAttribute('PartName') ?? '').replace(/^\\/+/, '')\n const type = node.getAttribute('ContentType') ?? ''\n if (path && type) overrides.set(path, type)\n }\n return { defaults, overrides }\n}\n\nfunction contentTypeFor(path: string, contentTypes: ReturnType<typeof readContentTypes>): string {\n const ext = extension(path)\n return (\n contentTypes.overrides.get(path) ??\n contentTypes.defaults.get(ext) ??\n MIME_BY_EXTENSION[ext] ??\n 'application/octet-stream'\n )\n}\n\nfunction olePackageKind(programId: string): OlePackageKind | null {\n const normalized = programId.toLowerCase()\n if (normalized === 'powerpoint.show.12' || normalized === 'powerpoint.slide.12') {\n return {\n extension: 'pptx',\n contentType: MIME_BY_EXTENSION.pptx,\n label: 'PowerPoint 演示文稿',\n }\n }\n if (normalized === 'excel.sheet.12') {\n return { extension: 'xlsx', contentType: MIME_BY_EXTENSION.xlsx, label: 'Excel 工作簿' }\n }\n if (normalized === 'word.document.12') {\n return { extension: 'docx', contentType: MIME_BY_EXTENSION.docx, label: 'Word 文档' }\n }\n return null\n}\n\nfunction ancestorByLocalName(element: Element, localName: string): Element | null {\n let current: Element | null = element\n while (current) {\n if (current.localName === localName) return current\n current = current.parentElement\n }\n return null\n}\n\nfunction vmlLengthInPoints(style: string, property: 'width' | 'height'): number | undefined {\n const match = style.match(\n new RegExp(`(?:^|;)\\\\s*${property}\\\\s*:\\\\s*([0-9]+(?:\\\\.[0-9]+)?)(pt|px|in|cm|mm)`, 'i'),\n )\n if (!match) return undefined\n const value = Number(match[1])\n const unit = match[2]?.toLowerCase()\n const points =\n unit === 'pt'\n ? value\n : unit === 'px'\n ? value * 0.75\n : unit === 'in'\n ? value * 72\n : unit === 'cm'\n ? (value * 72) / 2.54\n : (value * 72) / 25.4\n return Number.isFinite(points) && points > 0 && points <= 2000 ? points : undefined\n}\n\nfunction embeddedPreview(\n object: Element,\n relationships: ReadonlyMap<string, Relationship>,\n byPath: ReadonlyMap<string, ZipEntry>,\n contentTypes: ReturnType<typeof readContentTypes>,\n): DocxEmbeddedPreview | undefined {\n const wrapper = ancestorByLocalName(object, 'object')\n if (!wrapper) return undefined\n const imageData = elements(wrapper, 'imagedata')[0]\n const relationshipId = imageData ? attrByLocalName(imageData, 'id') : ''\n const relationship = relationships.get(relationshipId)\n if (\n !relationshipId ||\n !relationship ||\n relationship.external ||\n !relationship.type.toLowerCase().endsWith('/image')\n )\n return undefined\n const path = resolvePartTarget(DOCUMENT_PART, relationship.target)\n const entry = byPath.get(path)\n if (!path || !entry) return undefined\n const shape = elements(wrapper, 'shape')[0]\n const style = shape?.getAttribute('style') ?? ''\n return {\n path,\n size: entry.size,\n contentType: contentTypeFor(path, contentTypes),\n widthPt: vmlLengthInPoints(style, 'width'),\n heightPt: vmlLengthInPoints(style, 'height'),\n entry,\n }\n}\n\nfunction sniffImageContentType(bytes: Uint8Array, declared: string): string {\n if (\n bytes.length >= 8 &&\n bytes[0] === 0x89 &&\n bytes[1] === 0x50 &&\n bytes[2] === 0x4e &&\n bytes[3] === 0x47\n )\n return 'image/png'\n if (bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff)\n return 'image/jpeg'\n if (\n bytes.length >= 6 &&\n bytes[0] === 0x47 &&\n bytes[1] === 0x49 &&\n bytes[2] === 0x46 &&\n bytes[3] === 0x38\n )\n return 'image/gif'\n if (bytes.length >= 2 && bytes[0] === 0x42 && bytes[1] === 0x4d) return 'image/bmp'\n if (\n bytes.length >= 12 &&\n bytes[0] === 0x52 &&\n bytes[1] === 0x49 &&\n bytes[2] === 0x46 &&\n bytes[3] === 0x46 &&\n bytes[8] === 0x57 &&\n bytes[9] === 0x45 &&\n bytes[10] === 0x42 &&\n bytes[11] === 0x50\n )\n return 'image/webp'\n return declared.startsWith('image/') ? declared : 'application/octet-stream'\n}\n\n/**\n * Discover embedded package relationships (`w:object` / `o:OLEObject`) in document order.\n * `docx-preview` currently drops these nodes, so the caller can expose the original bytes separately.\n */\nexport async function readDocxEmbeddedFiles(buffer: ArrayBuffer): Promise<DocxEmbeddedFile[]> {\n const entries = readZip(buffer)\n const byPath = new Map(\n entries.filter((entry) => !entry.isDir).map((entry) => [entry.path, entry]),\n )\n const documentEntry = byPath.get(DOCUMENT_PART)\n const relationshipsEntry = byPath.get(DOCUMENT_RELS_PART)\n if (!documentEntry || !relationshipsEntry) return []\n\n const [documentXml, relationshipsXml, contentTypesXml] = await Promise.all([\n extractZipEntry(buffer, documentEntry).then(decodeEntry),\n extractZipEntry(buffer, relationshipsEntry).then(decodeEntry),\n byPath.has(CONTENT_TYPES_PART)\n ? extractZipEntry(buffer, byPath.get(CONTENT_TYPES_PART) as ZipEntry).then(decodeEntry)\n : Promise.resolve(null),\n ])\n const document = parseXml(documentXml, 'DOCX document')\n const relationships = readRelationships(relationshipsXml)\n const contentTypes = readContentTypes(contentTypesXml)\n const result: DocxEmbeddedFile[] = []\n const seenPaths = new Set<string>()\n let previousText = ''\n\n for (const paragraph of elements(document, 'p')) {\n const currentText = paragraphText(paragraph)\n for (const object of elements(paragraph, 'OLEObject')) {\n const relationshipId = attrByLocalName(object, 'id')\n const relationship = relationships.get(relationshipId)\n if (!relationshipId || !relationship || relationship.external) continue\n const relationshipType = relationship.type.toLowerCase()\n const directPackage = relationshipType.endsWith('/package')\n const oleObject = relationshipType.endsWith('/oleobject')\n if (!directPackage && !oleObject) continue\n const programId = object.getAttribute('ProgID') ?? attrByLocalName(object, 'ProgID')\n const oleKind = oleObject ? olePackageKind(programId) : null\n if (oleObject && !oleKind) continue\n\n const path = resolvePartTarget(DOCUMENT_PART, relationship.target)\n const entry = byPath.get(path)\n if (!path || !entry || seenPaths.has(path)) continue\n seenPaths.add(path)\n\n const sourceName = basename(path)\n const originalName = oleKind ? `${oleKind.label}.${oleKind.extension}` : sourceName\n const label = cleanLabel(currentText || (oleKind ? oleKind.label : previousText))\n result.push({\n id: relationshipId,\n label,\n name: friendlyName(label, originalName),\n originalName,\n path,\n size: entry.size,\n contentType: oleKind?.contentType ?? contentTypeFor(path, contentTypes),\n storage: oleKind ? 'ole-package' : 'package',\n preview: embeddedPreview(object, relationships, byPath, contentTypes),\n entry,\n })\n }\n if (currentText) previousText = currentText\n }\n return result\n}\n\n/** Extract one discovered embedded package on demand, with the same per-file ceiling as server exports. */\nexport async function extractDocxEmbeddedFile(\n buffer: ArrayBuffer,\n file: DocxEmbeddedFile,\n): Promise<Uint8Array> {\n if (file.size > MAX_EMBEDDED_FILE_BYTES) {\n throw new Error(\n `内嵌文件超过 ${MAX_EMBEDDED_FILE_BYTES / 1024 / 1024} MiB,已拒绝在浏览器中展开`,\n )\n }\n const bytes = await extractZipEntry(buffer, file.entry)\n return file.storage === 'ole-package' ? extractOlePackage(bytes, MAX_EMBEDDED_FILE_BYTES) : bytes\n}\n\n/** Extract the inert Word/WPS snapshot paired with an embedded object. */\nexport async function extractDocxEmbeddedPreview(\n buffer: ArrayBuffer,\n file: DocxEmbeddedFile,\n): Promise<ExtractedDocxPreview | null> {\n const preview = file.preview\n if (!preview) return null\n if (preview.size > MAX_PREVIEW_IMAGE_BYTES) {\n throw new Error(`内嵌预览图超过 ${MAX_PREVIEW_IMAGE_BYTES / 1024 / 1024} MiB,已跳过正文预览`)\n }\n const bytes = await extractZipEntry(buffer, preview.entry)\n const contentType = sniffImageContentType(bytes, preview.contentType)\n if (!['image/png', 'image/jpeg', 'image/gif', 'image/bmp', 'image/webp'].includes(contentType)) {\n return null\n }\n return { bytes, contentType }\n}\n"],"mappings":"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"}
|
package/dist/lib/msi.js
CHANGED
|
@@ -1,126 +1,28 @@
|
|
|
1
|
+
import { CompoundFile as e, CompoundFileError as t } from "./cfb.js";
|
|
1
2
|
//#region src/lib/msi.ts
|
|
2
|
-
var
|
|
3
|
+
var n = class extends Error {
|
|
3
4
|
constructor(e) {
|
|
4
5
|
super(e), this.name = "NotMsiError";
|
|
5
6
|
}
|
|
6
|
-
}, t = [
|
|
7
|
-
208,
|
|
8
|
-
207,
|
|
9
|
-
17,
|
|
10
|
-
224,
|
|
11
|
-
161,
|
|
12
|
-
177,
|
|
13
|
-
26,
|
|
14
|
-
225
|
|
15
|
-
], n = 4294967294, r = 4294967295, i = class {
|
|
16
|
-
dv;
|
|
17
|
-
buf;
|
|
18
|
-
sectorSize;
|
|
19
|
-
miniSectorSize;
|
|
20
|
-
miniCutoff;
|
|
21
|
-
fat = [];
|
|
22
|
-
miniFat = [];
|
|
23
|
-
miniStream = new Uint8Array();
|
|
24
|
-
entries = [];
|
|
25
|
-
constructor(i) {
|
|
26
|
-
this.buf = i;
|
|
27
|
-
let a = new DataView(i);
|
|
28
|
-
if (this.dv = a, i.byteLength < 512) throw new e("文件过小,不是有效的复合文件");
|
|
29
|
-
for (let n = 0; n < 8; n++) if (a.getUint8(n) !== t[n]) throw new e("不是 OLE 复合文件(缺少 D0CF11E0 魔数)");
|
|
30
|
-
let o = a.getUint16(26, !0);
|
|
31
|
-
this.sectorSize = 1 << a.getUint16(30, !0), this.miniSectorSize = 1 << a.getUint16(32, !0);
|
|
32
|
-
let s = a.getUint32(48, !0);
|
|
33
|
-
this.miniCutoff = a.getUint32(56, !0);
|
|
34
|
-
let c = a.getUint32(60, !0), l = a.getUint32(68, !0), u = this.sectorSize / 4, d = [];
|
|
35
|
-
for (let e = 0; e < 109; e++) {
|
|
36
|
-
let t = a.getUint32(76 + e * 4, !0);
|
|
37
|
-
t !== r && t !== n && d.push(t);
|
|
38
|
-
}
|
|
39
|
-
let f = l, p = 0;
|
|
40
|
-
for (; f !== n && f !== r && p++ < 1e6;) {
|
|
41
|
-
let e = this.sectorOffset(f);
|
|
42
|
-
for (let t = 0; t < u - 1; t++) {
|
|
43
|
-
let n = a.getUint32(e + t * 4, !0);
|
|
44
|
-
n !== r && d.push(n);
|
|
45
|
-
}
|
|
46
|
-
f = a.getUint32(e + (u - 1) * 4, !0);
|
|
47
|
-
}
|
|
48
|
-
for (let e of d) {
|
|
49
|
-
let t = this.sectorOffset(e);
|
|
50
|
-
for (let e = 0; e < u; e++) this.fat.push(a.getUint32(t + e * 4, !0));
|
|
51
|
-
}
|
|
52
|
-
let m = this.readFatStream(s, this.chain(s).length * this.sectorSize), h = new DataView(m.buffer, m.byteOffset, m.byteLength);
|
|
53
|
-
for (let e = 0; e + 128 <= m.length; e += 128) {
|
|
54
|
-
let t = h.getUint8(e + 66);
|
|
55
|
-
if (t === 0) continue;
|
|
56
|
-
let n = h.getUint16(e + 64, !0), r = "";
|
|
57
|
-
for (let t = 0; t + 1 < n; t += 2) {
|
|
58
|
-
let n = h.getUint16(e + t, !0);
|
|
59
|
-
if (n === 0) break;
|
|
60
|
-
r += String.fromCharCode(n);
|
|
61
|
-
}
|
|
62
|
-
let i = h.getUint32(e + 116, !0), a = o <= 3 ? h.getUint32(e + 120, !0) : Number(h.getBigUint64(e + 120, !0));
|
|
63
|
-
this.entries.push({
|
|
64
|
-
name: r,
|
|
65
|
-
type: t,
|
|
66
|
-
start: i,
|
|
67
|
-
size: a
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
let g = this.entries.find((e) => e.type === 5);
|
|
71
|
-
g && (this.miniStream = this.readFatStream(g.start, g.size));
|
|
72
|
-
for (let e of this.chain(c)) {
|
|
73
|
-
let t = this.sectorOffset(e);
|
|
74
|
-
for (let e = 0; e < u; e++) this.miniFat.push(a.getUint32(t + e * 4, !0));
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
sectorOffset(e) {
|
|
78
|
-
return (e + 1) * this.sectorSize;
|
|
79
|
-
}
|
|
80
|
-
chain(e) {
|
|
81
|
-
let t = [], i = e, a = 0;
|
|
82
|
-
for (; i !== n && i !== r && i < this.fat.length && a++ < 1e7;) t.push(i), i = this.fat[i];
|
|
83
|
-
return t;
|
|
84
|
-
}
|
|
85
|
-
sectorBytes(e) {
|
|
86
|
-
let t = this.sectorOffset(e), n = Math.min(this.buf.byteLength, t + this.sectorSize), r = new Uint8Array(this.sectorSize);
|
|
87
|
-
return t < this.buf.byteLength && r.set(new Uint8Array(this.buf, t, n - t)), r;
|
|
88
|
-
}
|
|
89
|
-
readFatStream(e, t) {
|
|
90
|
-
let n = this.chain(e), r = new Uint8Array(n.length * this.sectorSize);
|
|
91
|
-
for (let e = 0; e < n.length; e++) r.set(this.sectorBytes(n[e]), e * this.sectorSize);
|
|
92
|
-
return r.subarray(0, t);
|
|
93
|
-
}
|
|
94
|
-
readMiniStream(e, t) {
|
|
95
|
-
let i = Math.ceil(t / this.miniSectorSize), a = new Uint8Array(i * this.miniSectorSize), o = e, s = 0;
|
|
96
|
-
for (; o !== n && o !== r && s < i;) {
|
|
97
|
-
let e = o * this.miniSectorSize;
|
|
98
|
-
a.set(this.miniStream.subarray(e, e + this.miniSectorSize), s * this.miniSectorSize), s++, o = this.miniFat[o];
|
|
99
|
-
}
|
|
100
|
-
return a.subarray(0, t);
|
|
101
|
-
}
|
|
102
|
-
stream(e) {
|
|
103
|
-
return e.size === 0 ? new Uint8Array() : e.type === 5 || e.size >= this.miniCutoff ? this.readFatStream(e.start, e.size) : this.readMiniStream(e.start, e.size);
|
|
104
|
-
}
|
|
105
7
|
};
|
|
106
|
-
function
|
|
8
|
+
function r(e) {
|
|
107
9
|
return e < 10 ? String.fromCharCode(e + 48) : e < 36 ? String.fromCharCode(e - 10 + 65) : e < 62 ? String.fromCharCode(e - 36 + 97) : e === 62 ? "." : "_";
|
|
108
10
|
}
|
|
109
|
-
function
|
|
11
|
+
function i(e) {
|
|
110
12
|
let t = "", n = !1;
|
|
111
|
-
for (let
|
|
112
|
-
let
|
|
113
|
-
if (
|
|
114
|
-
let e =
|
|
115
|
-
t +=
|
|
116
|
-
} else
|
|
13
|
+
for (let i = 0; i < e.length; i++) {
|
|
14
|
+
let a = e.charCodeAt(i);
|
|
15
|
+
if (a >= 14336 && a < 18432) {
|
|
16
|
+
let e = a - 14336;
|
|
17
|
+
t += r(e & 63) + r(e >> 6 & 63);
|
|
18
|
+
} else a >= 18432 && a < 18496 ? t += r(a - 18432) : a === 18496 ? n = !0 : t += String.fromCharCode(a);
|
|
117
19
|
}
|
|
118
20
|
return {
|
|
119
21
|
name: t,
|
|
120
22
|
isTable: n
|
|
121
23
|
};
|
|
122
24
|
}
|
|
123
|
-
function
|
|
25
|
+
function a(e) {
|
|
124
26
|
let t = new DataView(e.buffer, e.byteOffset, e.byteLength), n = t.getUint32(44, !0), r = t.getUint32(n + 4, !0), i = [];
|
|
125
27
|
for (let e = 0; e < r; e++) {
|
|
126
28
|
let r = t.getUint32(n + 8 + e * 8, !0), a = t.getUint32(n + 8 + e * 8 + 4, !0);
|
|
@@ -134,7 +36,7 @@ function s(e) {
|
|
|
134
36
|
let e = t.getInt16(o.abs + 4, !0);
|
|
135
37
|
e < 0 && (e += 65536), a = e;
|
|
136
38
|
}
|
|
137
|
-
let s = new TextDecoder(
|
|
39
|
+
let s = new TextDecoder(d(a)), c = /* @__PURE__ */ new Map();
|
|
138
40
|
for (let { pid: n, abs: r } of i) {
|
|
139
41
|
let i = t.getUint32(r, !0);
|
|
140
42
|
if (i === 2) c.set(n, {
|
|
@@ -153,7 +55,7 @@ function s(e) {
|
|
|
153
55
|
});
|
|
154
56
|
} else i === 64 && c.set(n, {
|
|
155
57
|
type: i,
|
|
156
|
-
value:
|
|
58
|
+
value: f(t.getUint32(r + 4, !0), t.getUint32(r + 8, !0))
|
|
157
59
|
});
|
|
158
60
|
}
|
|
159
61
|
return {
|
|
@@ -161,7 +63,7 @@ function s(e) {
|
|
|
161
63
|
props: c
|
|
162
64
|
};
|
|
163
65
|
}
|
|
164
|
-
function
|
|
66
|
+
function o(e) {
|
|
165
67
|
let t = [], n = (n, r, i) => {
|
|
166
68
|
let a = e.get(n);
|
|
167
69
|
if (a == null) return;
|
|
@@ -171,12 +73,12 @@ function c(e) {
|
|
|
171
73
|
value: o
|
|
172
74
|
});
|
|
173
75
|
};
|
|
174
|
-
return n(3, "主题", String), n(4, "作者 / 厂商", String), n(6, "备注", String), n(2, "标题", String), n(5, "关键字", String), n(7, "平台 / 语言", (e) =>
|
|
76
|
+
return n(3, "主题", String), n(4, "作者 / 厂商", String), n(6, "备注", String), n(2, "标题", String), n(5, "关键字", String), n(7, "平台 / 语言", (e) => p(String(e))), n(9, "包代码", String), n(18, "制作工具", String), n(12, "创建时间", String), n(13, "上次保存", String), n(14, "架构版本", (e) => m(Number(e))), n(15, "源类型", (e) => h(Number(e))), n(19, "安全", (e) => g(Number(e))), t;
|
|
175
77
|
}
|
|
176
|
-
function
|
|
78
|
+
function s(e, t, n) {
|
|
177
79
|
let r = new DataView(e.buffer, e.byteOffset, e.byteLength), i = r.getUint16(0, !0) | r.getUint16(2, !0) << 16, a = (i & 2147483648) != 0, o = i & 2147483647;
|
|
178
80
|
o === 0 && (o = n || 1252);
|
|
179
|
-
let s = new TextDecoder(
|
|
81
|
+
let s = new TextDecoder(d(o)), c = [""], l = 0, u = Math.floor(e.length / 4);
|
|
180
82
|
for (let e = 1; e < u; e++) {
|
|
181
83
|
let n = r.getUint16(e * 4, !0);
|
|
182
84
|
if (n === 0) {
|
|
@@ -190,7 +92,7 @@ function l(e, t, n) {
|
|
|
190
92
|
longRefs: a
|
|
191
93
|
};
|
|
192
94
|
}
|
|
193
|
-
function
|
|
95
|
+
function c(e, t) {
|
|
194
96
|
let n = t.longRefs ? 3 : 2, r = new DataView(e.buffer, e.byteOffset, e.byteLength), i = Math.floor(e.length / (n * 2)), a = (e) => {
|
|
195
97
|
let t = e * n, i = r.getUint16(t, !0);
|
|
196
98
|
return n === 3 && (i += r.getUint8(t + 2) << 16), i;
|
|
@@ -204,42 +106,48 @@ function u(e, t) {
|
|
|
204
106
|
}
|
|
205
107
|
return o;
|
|
206
108
|
}
|
|
207
|
-
function
|
|
208
|
-
let
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
})), r = [], a = n.filter((e) => e.isTable).map((e) => e.name).sort((e, t) => e.localeCompare(t)), d = [], p = 0, m = n.find((e) => e.name === "SummaryInformation");
|
|
212
|
-
if (m) try {
|
|
213
|
-
let e = s(t.stream(m.e));
|
|
214
|
-
p = e.codepage, d = c(e.props);
|
|
109
|
+
function l(r) {
|
|
110
|
+
let l;
|
|
111
|
+
try {
|
|
112
|
+
l = new e(r);
|
|
215
113
|
} catch (e) {
|
|
216
|
-
|
|
114
|
+
throw e instanceof t ? new n(e.message) : e;
|
|
217
115
|
}
|
|
218
|
-
let
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
116
|
+
let d = l.entries.map((e) => ({
|
|
117
|
+
e,
|
|
118
|
+
...i(e.name)
|
|
119
|
+
})), f = [], p = d.filter((e) => e.isTable).map((e) => e.name).sort((e, t) => e.localeCompare(t)), m = [], h = 0, g = d.find((e) => e.name === "SummaryInformation");
|
|
120
|
+
if (g) try {
|
|
121
|
+
let e = a(l.stream(g.e));
|
|
122
|
+
h = e.codepage, m = o(e.props);
|
|
222
123
|
} catch (e) {
|
|
223
|
-
|
|
124
|
+
f.push("SummaryInformation 解析失败:" + u(e));
|
|
224
125
|
}
|
|
225
|
-
let y =
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
126
|
+
let _ = [], y = d.find((e) => e.name === "_StringData"), b = d.find((e) => e.name === "_StringPool"), x = d.find((e) => e.name === "Property");
|
|
127
|
+
if (y && b && x) try {
|
|
128
|
+
let e = s(l.stream(b.e), l.stream(y.e), h);
|
|
129
|
+
_ = c(l.stream(x.e), e);
|
|
130
|
+
} catch (e) {
|
|
131
|
+
f.push("Property 表解析失败:" + u(e));
|
|
132
|
+
}
|
|
133
|
+
let S = new Map(_.map((e) => [e.name, e.value])), C = S.get("ProductLanguage"), w = {
|
|
134
|
+
productName: S.get("ProductName"),
|
|
135
|
+
productVersion: S.get("ProductVersion"),
|
|
136
|
+
manufacturer: S.get("Manufacturer"),
|
|
137
|
+
productCode: S.get("ProductCode"),
|
|
138
|
+
upgradeCode: S.get("UpgradeCode"),
|
|
139
|
+
language: C ? v(C) : void 0,
|
|
140
|
+
properties: _,
|
|
141
|
+
summary: m,
|
|
142
|
+
tables: p,
|
|
143
|
+
notes: f
|
|
236
144
|
};
|
|
237
|
-
return !
|
|
145
|
+
return !_.length && !m.length && f.push("未找到 MSI 摘要信息或属性表(可能是别的复合文件,而非 MSI 数据库)"), w;
|
|
238
146
|
}
|
|
239
|
-
function
|
|
147
|
+
function u(e) {
|
|
240
148
|
return e instanceof Error ? e.message : String(e);
|
|
241
149
|
}
|
|
242
|
-
function
|
|
150
|
+
function d(e) {
|
|
243
151
|
switch (e) {
|
|
244
152
|
case 65001: return "utf-8";
|
|
245
153
|
case 1200: return "utf-16le";
|
|
@@ -259,7 +167,7 @@ function p(e) {
|
|
|
259
167
|
default: return "windows-1252";
|
|
260
168
|
}
|
|
261
169
|
}
|
|
262
|
-
function
|
|
170
|
+
function f(e, t) {
|
|
263
171
|
let n = t * 4294967296 + e;
|
|
264
172
|
if (!n) return "";
|
|
265
173
|
let r = n / 1e4 - 0xa9730b66800, i = new Date(r);
|
|
@@ -267,18 +175,18 @@ function m(e, t) {
|
|
|
267
175
|
let a = (e) => String(e).padStart(2, "0");
|
|
268
176
|
return `${i.getUTCFullYear()}-${a(i.getUTCMonth() + 1)}-${a(i.getUTCDate())} ${a(i.getUTCHours())}:${a(i.getUTCMinutes())} UTC`;
|
|
269
177
|
}
|
|
270
|
-
function
|
|
271
|
-
let [t, n] = e.split(";"), r = (n ?? "").split(",").map((e) => e.trim()).filter(Boolean).map((e) =>
|
|
178
|
+
function p(e) {
|
|
179
|
+
let [t, n] = e.split(";"), r = (n ?? "").split(",").map((e) => e.trim()).filter(Boolean).map((e) => v(e)), i = [];
|
|
272
180
|
return t && t.trim() && i.push(t.trim()), r.length && i.push(r.join(", ")), i.join(" · ") || e;
|
|
273
181
|
}
|
|
274
|
-
function
|
|
182
|
+
function m(e) {
|
|
275
183
|
return e ? `${Math.floor(e / 100)}.${String(e % 100).padStart(2, "0")}` : "";
|
|
276
184
|
}
|
|
277
|
-
function
|
|
185
|
+
function h(e) {
|
|
278
186
|
let t = [];
|
|
279
187
|
return t.push(e & 2 ? "压缩 (cabinet)" : "未压缩"), e & 1 && t.push("短文件名"), e & 4 && t.push("管理映像"), e & 8 && t.push("从源运行"), t.join(" · ");
|
|
280
188
|
}
|
|
281
|
-
function
|
|
189
|
+
function g(e) {
|
|
282
190
|
switch (e) {
|
|
283
191
|
case 0: return "无限制";
|
|
284
192
|
case 1: return "建议只读";
|
|
@@ -287,7 +195,7 @@ function v(e) {
|
|
|
287
195
|
default: return String(e);
|
|
288
196
|
}
|
|
289
197
|
}
|
|
290
|
-
var
|
|
198
|
+
var _ = {
|
|
291
199
|
0: "语言中性",
|
|
292
200
|
1025: "阿拉伯语",
|
|
293
201
|
1028: "中文(繁体)",
|
|
@@ -324,11 +232,11 @@ var y = {
|
|
|
324
232
|
3082: "西班牙语(西班牙)",
|
|
325
233
|
4100: "中文(新加坡)"
|
|
326
234
|
};
|
|
327
|
-
function
|
|
328
|
-
let t =
|
|
235
|
+
function v(e) {
|
|
236
|
+
let t = _[Number(e)];
|
|
329
237
|
return t ? `${t} (${e})` : e;
|
|
330
238
|
}
|
|
331
239
|
//#endregion
|
|
332
|
-
export {
|
|
240
|
+
export { n as NotMsiError, l as parseMsi };
|
|
333
241
|
|
|
334
242
|
//# sourceMappingURL=msi.js.map
|