@open-file-viewer/core 0.1.24 → 0.1.26
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/dist/index.cjs +201 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +201 -27
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -6458,6 +6458,7 @@ function decodeXml(value) {
|
|
|
6458
6458
|
// src/plugins/office.ts
|
|
6459
6459
|
var import_jszip3 = __toESM(require("jszip"), 1);
|
|
6460
6460
|
var import_dompurify2 = __toESM(require("dompurify"), 1);
|
|
6461
|
+
var docxPreview = __toESM(require("docx-preview"), 1);
|
|
6461
6462
|
|
|
6462
6463
|
// src/plugins/msdoc.ts
|
|
6463
6464
|
var CFB_SIGNATURE = [208, 207, 17, 224, 161, 177, 26, 225];
|
|
@@ -7894,6 +7895,7 @@ var presentationExtensions = /* @__PURE__ */ new Set(["pptx", "pptm", "ppt", "pp
|
|
|
7894
7895
|
var packagedOfficeCandidates = /* @__PURE__ */ new Set(["wps", "et", "dps", "numbers", "key"]);
|
|
7895
7896
|
var SHEET_WINDOW_ROWS = 200;
|
|
7896
7897
|
var SHEET_WINDOW_COLUMNS = 80;
|
|
7898
|
+
var DEFAULT_DOCX_RENDER_TIMEOUT_MS = 15e3;
|
|
7897
7899
|
var DEFAULT_PPTX_RENDER_TIMEOUT_MS = 12e3;
|
|
7898
7900
|
var PPTX_REL_NS = "http://schemas.openxmlformats.org/package/2006/relationships";
|
|
7899
7901
|
var officeMimeTypes = /* @__PURE__ */ new Set([
|
|
@@ -8202,23 +8204,28 @@ async function renderDocx(panel, arrayBuffer, fit) {
|
|
|
8202
8204
|
document.head.append(styleContainer);
|
|
8203
8205
|
let disposeFit;
|
|
8204
8206
|
try {
|
|
8205
|
-
|
|
8206
|
-
|
|
8207
|
-
|
|
8208
|
-
|
|
8209
|
-
|
|
8210
|
-
|
|
8211
|
-
|
|
8212
|
-
|
|
8213
|
-
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
|
|
8217
|
-
|
|
8218
|
-
|
|
8219
|
-
|
|
8220
|
-
|
|
8221
|
-
|
|
8207
|
+
await withTimeout(
|
|
8208
|
+
(async () => {
|
|
8209
|
+
await docxPreview.renderAsync(arrayBuffer, content, styleContainer, {
|
|
8210
|
+
className: "ofv-docx",
|
|
8211
|
+
inWrapper: true,
|
|
8212
|
+
breakPages: true,
|
|
8213
|
+
ignoreWidth: false,
|
|
8214
|
+
ignoreHeight: false,
|
|
8215
|
+
ignoreFonts: false,
|
|
8216
|
+
renderHeaders: true,
|
|
8217
|
+
renderFooters: true,
|
|
8218
|
+
renderFootnotes: true,
|
|
8219
|
+
renderEndnotes: true,
|
|
8220
|
+
renderComments: true,
|
|
8221
|
+
renderAltChunks: true,
|
|
8222
|
+
experimental: true,
|
|
8223
|
+
useBase64URL: true
|
|
8224
|
+
});
|
|
8225
|
+
})(),
|
|
8226
|
+
docxRenderTimeoutMs(),
|
|
8227
|
+
"DOCX rendering"
|
|
8228
|
+
);
|
|
8222
8229
|
await normalizeDocxLayout(content, arrayBuffer);
|
|
8223
8230
|
const shouldUseTextboxFallback = await docxPreviewLooksBlank(content, arrayBuffer) || await docxPreviewMissesRichTextboxContent(content, arrayBuffer) || await docxShouldPreferTextboxLayoutFallback(arrayBuffer);
|
|
8224
8231
|
if (shouldUseTextboxFallback) {
|
|
@@ -8241,13 +8248,18 @@ async function renderDocx(panel, arrayBuffer, fit) {
|
|
|
8241
8248
|
} catch (error) {
|
|
8242
8249
|
disposeFit?.();
|
|
8243
8250
|
styleContainer.remove();
|
|
8244
|
-
|
|
8245
|
-
|
|
8246
|
-
|
|
8251
|
+
const fallbackContent = document.createElement("div");
|
|
8252
|
+
fallbackContent.className = "ofv-docx-document";
|
|
8253
|
+
await renderDocxContentFallback(fallbackContent, arrayBuffer);
|
|
8254
|
+
panel.append(fallbackContent);
|
|
8247
8255
|
console.warn("DOCX layout preview failed, fell back to Mammoth:", error);
|
|
8248
8256
|
}
|
|
8249
8257
|
return () => void 0;
|
|
8250
8258
|
}
|
|
8259
|
+
function docxRenderTimeoutMs() {
|
|
8260
|
+
const override = globalThis.__OFV_DOCX_RENDER_TIMEOUT_MS__;
|
|
8261
|
+
return typeof override === "number" && override > 0 ? override : DEFAULT_DOCX_RENDER_TIMEOUT_MS;
|
|
8262
|
+
}
|
|
8251
8263
|
async function docxPreviewLooksBlank(container, arrayBuffer) {
|
|
8252
8264
|
if (container.querySelector("img, svg, canvas, table")) {
|
|
8253
8265
|
return false;
|
|
@@ -8340,12 +8352,13 @@ async function renderDocxContentFallback(container, arrayBuffer, options = {}) {
|
|
|
8340
8352
|
return;
|
|
8341
8353
|
}
|
|
8342
8354
|
try {
|
|
8343
|
-
|
|
8344
|
-
|
|
8355
|
+
const mammothContent = document.createElement("div");
|
|
8356
|
+
await withTimeout(renderDocxWithMammoth(mammothContent, arrayBuffer), docxRenderTimeoutMs(), "DOCX fallback rendering");
|
|
8357
|
+
const renderedText = normalizePreviewText(mammothContent.querySelector(".ofv-document")?.textContent || "");
|
|
8345
8358
|
if (renderedText.length >= 24) {
|
|
8359
|
+
container.append(...Array.from(mammothContent.childNodes));
|
|
8346
8360
|
return;
|
|
8347
8361
|
}
|
|
8348
|
-
container.querySelector(".ofv-document")?.remove();
|
|
8349
8362
|
await renderDocxTextFallback(container, arrayBuffer);
|
|
8350
8363
|
} catch (fallbackError) {
|
|
8351
8364
|
await renderDocxTextFallback(container, arrayBuffer);
|
|
@@ -10578,6 +10591,7 @@ async function renderPptx(panel, arrayBuffer) {
|
|
|
10578
10591
|
container.className = "ofv-pptx-viewer";
|
|
10579
10592
|
let insight;
|
|
10580
10593
|
let zip;
|
|
10594
|
+
let placeholderFontCorrections = [];
|
|
10581
10595
|
try {
|
|
10582
10596
|
zip = await import_jszip3.default.loadAsync(arrayBuffer);
|
|
10583
10597
|
insight = await inspectPptxPresentation(zip);
|
|
@@ -10585,11 +10599,18 @@ async function renderPptx(panel, arrayBuffer) {
|
|
|
10585
10599
|
} catch (error) {
|
|
10586
10600
|
console.warn("PPTX structure insight extraction failed:", error);
|
|
10587
10601
|
}
|
|
10602
|
+
if (zip) {
|
|
10603
|
+
try {
|
|
10604
|
+
placeholderFontCorrections = await inspectPptxPlaceholderFontCorrections(zip);
|
|
10605
|
+
} catch (error) {
|
|
10606
|
+
console.warn("PPTX placeholder font extraction failed:", error);
|
|
10607
|
+
}
|
|
10608
|
+
}
|
|
10588
10609
|
panel.append(container);
|
|
10589
10610
|
try {
|
|
10590
10611
|
const { PptxViewer } = await import("@aiden0z/pptx-renderer");
|
|
10591
10612
|
await withTimeout(PptxViewer.open(arrayBuffer, container), pptxRenderTimeoutMs());
|
|
10592
|
-
normalizePptxLayout(container);
|
|
10613
|
+
normalizePptxLayout(container, placeholderFontCorrections);
|
|
10593
10614
|
} catch (error) {
|
|
10594
10615
|
container.replaceChildren();
|
|
10595
10616
|
if (insight) {
|
|
@@ -10622,13 +10643,13 @@ function renderPptxTextFallback(container, insight) {
|
|
|
10622
10643
|
container.append(article);
|
|
10623
10644
|
}
|
|
10624
10645
|
}
|
|
10625
|
-
async function withTimeout(promise, timeoutMs) {
|
|
10646
|
+
async function withTimeout(promise, timeoutMs, label = "PPTX rendering") {
|
|
10626
10647
|
let timeoutId;
|
|
10627
10648
|
try {
|
|
10628
10649
|
return await Promise.race([
|
|
10629
10650
|
promise,
|
|
10630
10651
|
new Promise((_resolve, reject) => {
|
|
10631
|
-
timeoutId = window.setTimeout(() => reject(new Error(
|
|
10652
|
+
timeoutId = window.setTimeout(() => reject(new Error(`${label} timed out after ${timeoutMs}ms.`)), timeoutMs);
|
|
10632
10653
|
})
|
|
10633
10654
|
]);
|
|
10634
10655
|
} finally {
|
|
@@ -10641,15 +10662,168 @@ function pptxRenderTimeoutMs() {
|
|
|
10641
10662
|
const override = globalThis.__OFV_PPTX_RENDER_TIMEOUT_MS__;
|
|
10642
10663
|
return typeof override === "number" && override > 0 ? override : DEFAULT_PPTX_RENDER_TIMEOUT_MS;
|
|
10643
10664
|
}
|
|
10644
|
-
function normalizePptxLayout(container) {
|
|
10665
|
+
function normalizePptxLayout(container, placeholderFontCorrections) {
|
|
10645
10666
|
const slideCanvases = findPptxSlideCanvases(container);
|
|
10646
10667
|
for (const slide of slideCanvases) {
|
|
10647
10668
|
if (!hasInlineBackground(slide)) {
|
|
10648
10669
|
slide.style.backgroundColor = "#FFFFFF";
|
|
10649
10670
|
}
|
|
10650
10671
|
}
|
|
10672
|
+
normalizePptxPlaceholderFonts(container, placeholderFontCorrections);
|
|
10651
10673
|
normalizePptxMirroredText(container);
|
|
10652
10674
|
}
|
|
10675
|
+
async function inspectPptxPlaceholderFontCorrections(zip) {
|
|
10676
|
+
const presentationXml = await zip.file("ppt/presentation.xml")?.async("text");
|
|
10677
|
+
const presentation = presentationXml ? parseOfficeXml(presentationXml) : void 0;
|
|
10678
|
+
const slideSize = presentation ? Array.from(presentation.getElementsByTagName("*")).find((element) => element.localName === "sldSz") : void 0;
|
|
10679
|
+
const slideWidth = Number(slideSize?.getAttribute("cx"));
|
|
10680
|
+
const slideHeight = Number(slideSize?.getAttribute("cy"));
|
|
10681
|
+
if (!(slideWidth > 0) || !(slideHeight > 0)) {
|
|
10682
|
+
return [];
|
|
10683
|
+
}
|
|
10684
|
+
const slideEntries = Object.values(zip.files).filter((entry) => !entry.dir && /^ppt\/slides\/slide\d+\.xml$/i.test(entry.name)).sort((a, b) => slideNumberFromPath(a.name) - slideNumberFromPath(b.name));
|
|
10685
|
+
const corrections = [];
|
|
10686
|
+
for (const [slideIndex, entry] of slideEntries.entries()) {
|
|
10687
|
+
const slideXml = await entry.async("text");
|
|
10688
|
+
const slide = parseOfficeXml(slideXml);
|
|
10689
|
+
if (!slide) {
|
|
10690
|
+
continue;
|
|
10691
|
+
}
|
|
10692
|
+
const relationships = await readPptxRelationships(zip, entry.name);
|
|
10693
|
+
const layoutTarget = resolvePptxRelationshipTarget(
|
|
10694
|
+
entry.name,
|
|
10695
|
+
relationships.find((relationship) => /\/slideLayout$/i.test(relationship.type))?.target
|
|
10696
|
+
);
|
|
10697
|
+
const layoutXml = layoutTarget ? await zip.file(layoutTarget)?.async("text") : void 0;
|
|
10698
|
+
const layout = layoutXml ? parseOfficeXml(layoutXml) : void 0;
|
|
10699
|
+
if (!layout) {
|
|
10700
|
+
continue;
|
|
10701
|
+
}
|
|
10702
|
+
const layoutFontSizes = readPptxLayoutPlaceholderFontSizes(layout);
|
|
10703
|
+
const shapes = Array.from(slide.getElementsByTagName("*")).filter((element) => element.localName === "sp");
|
|
10704
|
+
for (const shape of shapes) {
|
|
10705
|
+
const placeholder = findPptxDescendant(shape, "ph");
|
|
10706
|
+
const placeholderIndex = placeholder?.getAttribute("idx");
|
|
10707
|
+
const fontSizePt = placeholderIndex ? layoutFontSizes.get(placeholderIndex) : void 0;
|
|
10708
|
+
const textBody = findPptxDescendant(shape, "txBody");
|
|
10709
|
+
if (!textBody?.textContent?.trim() || !fontSizePt || hasExplicitPptxTextSize(textBody)) {
|
|
10710
|
+
continue;
|
|
10711
|
+
}
|
|
10712
|
+
const transform = findPptxDescendant(shape, "xfrm");
|
|
10713
|
+
const offset = transform ? findPptxChild(transform, "off") : void 0;
|
|
10714
|
+
const extent = transform ? findPptxChild(transform, "ext") : void 0;
|
|
10715
|
+
const left = Number(offset?.getAttribute("x"));
|
|
10716
|
+
const top = Number(offset?.getAttribute("y"));
|
|
10717
|
+
const width = Number(extent?.getAttribute("cx"));
|
|
10718
|
+
const height = Number(extent?.getAttribute("cy"));
|
|
10719
|
+
if (![left, top, width, height].every((value) => Number.isFinite(value)) || width <= 0 || height <= 0) {
|
|
10720
|
+
continue;
|
|
10721
|
+
}
|
|
10722
|
+
corrections.push({
|
|
10723
|
+
slideIndex,
|
|
10724
|
+
leftRatio: left / slideWidth,
|
|
10725
|
+
topRatio: top / slideHeight,
|
|
10726
|
+
widthRatio: width / slideWidth,
|
|
10727
|
+
heightRatio: height / slideHeight,
|
|
10728
|
+
fontSizePt
|
|
10729
|
+
});
|
|
10730
|
+
}
|
|
10731
|
+
}
|
|
10732
|
+
return corrections;
|
|
10733
|
+
}
|
|
10734
|
+
function readPptxLayoutPlaceholderFontSizes(layout) {
|
|
10735
|
+
const result = /* @__PURE__ */ new Map();
|
|
10736
|
+
const shapes = Array.from(layout.getElementsByTagName("*")).filter((element) => element.localName === "sp");
|
|
10737
|
+
for (const shape of shapes) {
|
|
10738
|
+
const placeholderIndex = findPptxDescendant(shape, "ph")?.getAttribute("idx");
|
|
10739
|
+
if (!placeholderIndex) {
|
|
10740
|
+
continue;
|
|
10741
|
+
}
|
|
10742
|
+
const textBody = findPptxDescendant(shape, "txBody");
|
|
10743
|
+
const defaultRunProperties = textBody ? Array.from(textBody.getElementsByTagName("*")).find(
|
|
10744
|
+
(element) => element.localName === "defRPr" && Number(element.getAttribute("sz")) > 0
|
|
10745
|
+
) : void 0;
|
|
10746
|
+
const size = Number(defaultRunProperties?.getAttribute("sz"));
|
|
10747
|
+
if (size > 0) {
|
|
10748
|
+
result.set(placeholderIndex, size / 100);
|
|
10749
|
+
}
|
|
10750
|
+
}
|
|
10751
|
+
return result;
|
|
10752
|
+
}
|
|
10753
|
+
function hasExplicitPptxTextSize(textBody) {
|
|
10754
|
+
return Array.from(textBody.getElementsByTagName("*")).some(
|
|
10755
|
+
(element) => (element.localName === "rPr" || element.localName === "defRPr" || element.localName === "endParaRPr") && Number(element.getAttribute("sz")) > 0
|
|
10756
|
+
);
|
|
10757
|
+
}
|
|
10758
|
+
function findPptxDescendant(element, localName) {
|
|
10759
|
+
return Array.from(element.getElementsByTagName("*")).find((child) => child.localName === localName);
|
|
10760
|
+
}
|
|
10761
|
+
function findPptxChild(element, localName) {
|
|
10762
|
+
return Array.from(element.children).find((child) => child.localName === localName);
|
|
10763
|
+
}
|
|
10764
|
+
function normalizePptxPlaceholderFonts(container, corrections) {
|
|
10765
|
+
for (const correction of corrections) {
|
|
10766
|
+
const wrapper = container.querySelector(`div[data-slide-index="${correction.slideIndex}"]`);
|
|
10767
|
+
if (!wrapper) {
|
|
10768
|
+
continue;
|
|
10769
|
+
}
|
|
10770
|
+
const match = findPptxPlaceholderElement(wrapper, correction);
|
|
10771
|
+
if (!match) {
|
|
10772
|
+
continue;
|
|
10773
|
+
}
|
|
10774
|
+
const styledText = Array.from(match.querySelectorAll("[style]")).filter(
|
|
10775
|
+
(element) => parseFloat(element.style.fontSize) > 0
|
|
10776
|
+
);
|
|
10777
|
+
for (const element of styledText) {
|
|
10778
|
+
element.style.fontSize = `${correction.fontSizePt}pt`;
|
|
10779
|
+
}
|
|
10780
|
+
if (styledText.length > 0) {
|
|
10781
|
+
match.dataset.ofvPptxPlaceholderFont = String(correction.fontSizePt);
|
|
10782
|
+
}
|
|
10783
|
+
}
|
|
10784
|
+
}
|
|
10785
|
+
function findPptxPlaceholderElement(wrapper, correction) {
|
|
10786
|
+
let best;
|
|
10787
|
+
for (const canvas of findPptxSlideCanvases(wrapper)) {
|
|
10788
|
+
const canvasWidth = parseCssPixelValue(canvas.style.width);
|
|
10789
|
+
const canvasHeight = parseCssPixelValue(canvas.style.height);
|
|
10790
|
+
if (!(canvasWidth > 0) || !(canvasHeight > 0)) {
|
|
10791
|
+
continue;
|
|
10792
|
+
}
|
|
10793
|
+
const expected = {
|
|
10794
|
+
left: correction.leftRatio * canvasWidth,
|
|
10795
|
+
top: correction.topRatio * canvasHeight,
|
|
10796
|
+
width: correction.widthRatio * canvasWidth,
|
|
10797
|
+
height: correction.heightRatio * canvasHeight
|
|
10798
|
+
};
|
|
10799
|
+
const candidates = Array.from(canvas.querySelectorAll("div")).filter(
|
|
10800
|
+
(element) => element.style.position === "absolute" && Boolean(element.textContent?.trim())
|
|
10801
|
+
);
|
|
10802
|
+
for (const element of candidates) {
|
|
10803
|
+
const actual = {
|
|
10804
|
+
left: parseCssPixelValue(element.style.left),
|
|
10805
|
+
top: parseCssPixelValue(element.style.top),
|
|
10806
|
+
width: parseCssPixelValue(element.style.width),
|
|
10807
|
+
height: parseCssPixelValue(element.style.height)
|
|
10808
|
+
};
|
|
10809
|
+
const deltas = [
|
|
10810
|
+
Math.abs(actual.left - expected.left),
|
|
10811
|
+
Math.abs(actual.top - expected.top),
|
|
10812
|
+
Math.abs(actual.width - expected.width),
|
|
10813
|
+
Math.abs(actual.height - expected.height)
|
|
10814
|
+
];
|
|
10815
|
+
const tolerance = Math.max(2, Math.min(canvasWidth, canvasHeight) * 5e-3);
|
|
10816
|
+
if (deltas.some((delta) => delta > tolerance)) {
|
|
10817
|
+
continue;
|
|
10818
|
+
}
|
|
10819
|
+
const score = deltas.reduce((sum, delta) => sum + delta, 0);
|
|
10820
|
+
if (!best || score < best.score) {
|
|
10821
|
+
best = { element, score };
|
|
10822
|
+
}
|
|
10823
|
+
}
|
|
10824
|
+
}
|
|
10825
|
+
return best?.element;
|
|
10826
|
+
}
|
|
10653
10827
|
function hasInlineBackground(element) {
|
|
10654
10828
|
return Boolean(element.style.background || element.style.backgroundColor || element.style.backgroundImage);
|
|
10655
10829
|
}
|