@files-preview-app/preview-file 1.3.0 → 1.3.1
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/angular.cjs +169 -13
- package/dist/angular.cjs.map +1 -1
- package/dist/angular.js +169 -13
- package/dist/angular.js.map +1 -1
- package/dist/index.cjs +169 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +169 -13
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +169 -13
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +169 -13
- package/dist/react.js.map +1 -1
- package/dist/vue.cjs +169 -13
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.js +169 -13
- package/dist/vue.js.map +1 -1
- package/package.json +1 -1
package/dist/react.js
CHANGED
|
@@ -2,6 +2,7 @@ import { memo, forwardRef, useRef, useImperativeHandle, useEffect } from 'react'
|
|
|
2
2
|
import DOMPurify6 from 'dompurify';
|
|
3
3
|
import * as pdfjsLib from 'pdfjs-dist';
|
|
4
4
|
import * as docx from 'docx-preview';
|
|
5
|
+
import * as fflate from 'fflate';
|
|
5
6
|
import { unzipSync, strFromU8, unzip } from 'fflate';
|
|
6
7
|
import * as XLSX from 'xlsx';
|
|
7
8
|
import hljs from 'highlight.js';
|
|
@@ -667,9 +668,11 @@ var ToolbarController = class {
|
|
|
667
668
|
type: "button",
|
|
668
669
|
"data-action-id": id
|
|
669
670
|
});
|
|
670
|
-
const
|
|
671
|
-
if (
|
|
672
|
-
btn.innerHTML =
|
|
671
|
+
const internalSvg = ICON_MAP[iconHtml] || ICON_MAP[id];
|
|
672
|
+
if (internalSvg) {
|
|
673
|
+
btn.innerHTML = internalSvg;
|
|
674
|
+
} else if (iconHtml && iconHtml.startsWith("<svg")) {
|
|
675
|
+
btn.innerHTML = sanitizeSVG(iconHtml);
|
|
673
676
|
} else {
|
|
674
677
|
btn.textContent = title || id;
|
|
675
678
|
}
|
|
@@ -5669,8 +5672,17 @@ var DocPlugin = class {
|
|
|
5669
5672
|
let scale = 1;
|
|
5670
5673
|
let extractedRawText = "";
|
|
5671
5674
|
let isFallback = false;
|
|
5675
|
+
let chartSvg = "";
|
|
5672
5676
|
try {
|
|
5673
5677
|
const cfbf = new CfbfReader(ctx.buffer);
|
|
5678
|
+
try {
|
|
5679
|
+
const pkg = cfbf.readStream("package_stream");
|
|
5680
|
+
if (pkg && pkg.length > 100) {
|
|
5681
|
+
chartSvg = this.parseOdfChartToSvg(pkg);
|
|
5682
|
+
}
|
|
5683
|
+
} catch (chartErr) {
|
|
5684
|
+
console.warn("[DocPlugin] Chart stream parsing info:", chartErr);
|
|
5685
|
+
}
|
|
5674
5686
|
const wordDocStream = cfbf.readStream("WordDocument");
|
|
5675
5687
|
if (!wordDocStream || wordDocStream.length < 512) {
|
|
5676
5688
|
throw new Error("WordDocument stream not found or invalid in CFBF archive");
|
|
@@ -5688,7 +5700,7 @@ var DocPlugin = class {
|
|
|
5688
5700
|
extractedRawText = fallback;
|
|
5689
5701
|
isFallback = true;
|
|
5690
5702
|
}
|
|
5691
|
-
const rawPages = this.splitIntoPages(extractedRawText);
|
|
5703
|
+
const rawPages = this.splitIntoPages(extractedRawText, chartSvg);
|
|
5692
5704
|
const totalPages = Math.max(1, rawPages.length);
|
|
5693
5705
|
let currentPage = 1;
|
|
5694
5706
|
const pageCards = [];
|
|
@@ -5930,7 +5942,7 @@ var DocPlugin = class {
|
|
|
5930
5942
|
for (const run of [...ansiRuns, ...utf16Runs]) {
|
|
5931
5943
|
const trimmed = run.trim();
|
|
5932
5944
|
if (trimmed.length >= 4 && /[a-zA-Z]/.test(trimmed) && !seen.has(trimmed)) {
|
|
5933
|
-
if (!trimmed.includes("Normal.dot") && !trimmed.includes("Microsoft Word") && !trimmed.includes("Times New Roman") && !trimmed.startsWith("\xD0\xCF\xE0\xA1\xB1\xE1") && !/^[\W_0-9]+$/.test(trimmed)) {
|
|
5945
|
+
if (!trimmed.includes("Normal.dot") && !trimmed.includes("Microsoft Word") && !trimmed.includes("Times New Roman") && !trimmed.startsWith("\xD0\xCF\xE0\xA1\xB1\xE1") && !/^EMBED\b/i.test(trimmed) && !trimmed.includes("ChartDocument") && !/^[\W_0-9]+$/.test(trimmed)) {
|
|
5934
5946
|
seen.add(trimmed);
|
|
5935
5947
|
candidateLines.push(trimmed);
|
|
5936
5948
|
}
|
|
@@ -5941,9 +5953,114 @@ var DocPlugin = class {
|
|
|
5941
5953
|
heuristicTextExtraction(buffer) {
|
|
5942
5954
|
return this.extractStringsFromBytes(new Uint8Array(buffer));
|
|
5943
5955
|
}
|
|
5944
|
-
|
|
5956
|
+
/**
|
|
5957
|
+
* Parses an embedded OpenDocument Chart package into a vector SVG bar/column chart
|
|
5958
|
+
*/
|
|
5959
|
+
parseOdfChartToSvg(zipBytes) {
|
|
5960
|
+
try {
|
|
5961
|
+
const unzipped = fflate.unzipSync(zipBytes);
|
|
5962
|
+
const contentXml = unzipped["content.xml"] ? new TextDecoder("utf-8").decode(unzipped["content.xml"]) : "";
|
|
5963
|
+
if (!contentXml) return "";
|
|
5964
|
+
const rowsMatch = contentXml.match(/<table:table-row[\s\S]*?<\/table:table-row>/g) || [];
|
|
5965
|
+
if (rowsMatch.length < 2) return "";
|
|
5966
|
+
const headers = [];
|
|
5967
|
+
const firstRow = rowsMatch[0];
|
|
5968
|
+
const headerCells = firstRow ? firstRow.match(/<text:p>([^<]+)<\/text:p>/g) || [] : [];
|
|
5969
|
+
for (const h of headerCells) {
|
|
5970
|
+
headers.push(h.replace(/<\/?text:p>/g, "").trim());
|
|
5971
|
+
}
|
|
5972
|
+
const categories = [];
|
|
5973
|
+
const seriesValues = headers.map(() => []);
|
|
5974
|
+
for (let r = 1; r < rowsMatch.length; r++) {
|
|
5975
|
+
const rowStr = rowsMatch[r];
|
|
5976
|
+
if (!rowStr) continue;
|
|
5977
|
+
const cells = rowStr.match(/<table:table-cell[\s\S]*?<\/table:table-cell>/g) || [];
|
|
5978
|
+
if (cells.length > 0 && cells[0]) {
|
|
5979
|
+
const catMatch = cells[0].match(/<text:p>([^<]+)<\/text:p>/);
|
|
5980
|
+
categories.push(catMatch ? catMatch[1] : "Row " + r);
|
|
5981
|
+
for (let c = 1; c < cells.length && c - 1 < headers.length; c++) {
|
|
5982
|
+
const cellStr = cells[c];
|
|
5983
|
+
if (!cellStr) continue;
|
|
5984
|
+
const valMatch = cellStr.match(/office:value="([0-9.]+)"/) || cellStr.match(/<text:p>([0-9.]+)<\/text:p>/);
|
|
5985
|
+
const series = seriesValues[c - 1];
|
|
5986
|
+
if (series) {
|
|
5987
|
+
series.push(valMatch ? parseFloat(valMatch[1]) : 0);
|
|
5988
|
+
}
|
|
5989
|
+
}
|
|
5990
|
+
}
|
|
5991
|
+
}
|
|
5992
|
+
const colors = ["#004586", "#ff420e", "#ffd320", "#579d1c", "#7e0021"];
|
|
5993
|
+
const colorMatches = contentXml.matchAll(/draw:fill-color="(#[0-9a-fA-F]{6})"/g);
|
|
5994
|
+
let cIdx = 0;
|
|
5995
|
+
for (const cm of colorMatches) {
|
|
5996
|
+
if (cIdx < colors.length) colors[cIdx] = cm[1];
|
|
5997
|
+
cIdx++;
|
|
5998
|
+
}
|
|
5999
|
+
let maxVal = 10;
|
|
6000
|
+
for (const s of seriesValues) {
|
|
6001
|
+
for (const v of s) {
|
|
6002
|
+
if (v > maxVal) maxVal = v;
|
|
6003
|
+
}
|
|
6004
|
+
}
|
|
6005
|
+
maxVal = Math.ceil(maxVal * 1.15);
|
|
6006
|
+
const width = 560;
|
|
6007
|
+
const height = 280;
|
|
6008
|
+
const padLeft = 45;
|
|
6009
|
+
const padRight = 100;
|
|
6010
|
+
const padTop = 20;
|
|
6011
|
+
const padBottom = 40;
|
|
6012
|
+
const chartW = width - padLeft - padRight;
|
|
6013
|
+
const chartH = height - padTop - padBottom;
|
|
6014
|
+
let svg = `<svg viewBox="0 0 ${width} ${height}" width="100%" height="auto" style="max-width: 560px; height: 280px; margin: 16px auto; display: block; font-family: Calibri, sans-serif; background: #ffffff; border: 1px solid #e2e8f0; border-radius: 6px; box-shadow: 0 1px 4px rgba(0,0,0,0.05);">`;
|
|
6015
|
+
for (let step = 0; step <= 4; step++) {
|
|
6016
|
+
const yVal = (maxVal / 4 * step).toFixed(1);
|
|
6017
|
+
const yPos = padTop + chartH - step / 4 * chartH;
|
|
6018
|
+
svg += `<line x1="${padLeft}" y1="${yPos}" x2="${padLeft + chartW}" y2="${yPos}" stroke="#e2e8f0" stroke-dasharray="2,2" />`;
|
|
6019
|
+
svg += `<text x="${padLeft - 8}" y="${yPos + 4}" font-size="11" fill="#64748b" text-anchor="end">${yVal}</text>`;
|
|
6020
|
+
}
|
|
6021
|
+
const numCats = categories.length;
|
|
6022
|
+
const numSeries = headers.length;
|
|
6023
|
+
const groupW = chartW / numCats;
|
|
6024
|
+
const barW = Math.max(8, groupW * 0.7 / numSeries);
|
|
6025
|
+
const groupPad = (groupW - barW * numSeries) / 2;
|
|
6026
|
+
for (let catIdx = 0; catIdx < numCats; catIdx++) {
|
|
6027
|
+
const groupX = padLeft + catIdx * groupW + groupPad;
|
|
6028
|
+
for (let sIdx = 0; sIdx < numSeries; sIdx++) {
|
|
6029
|
+
const val = seriesValues[sIdx][catIdx] || 0;
|
|
6030
|
+
const barH = val / maxVal * chartH;
|
|
6031
|
+
const barX = groupX + sIdx * barW;
|
|
6032
|
+
const barY = padTop + chartH - barH;
|
|
6033
|
+
const col = colors[sIdx % colors.length];
|
|
6034
|
+
svg += `<rect x="${barX}" y="${barY}" width="${barW - 2}" height="${barH}" fill="${col}" rx="2"><title>${headers[sIdx]}: ${val}</title></rect>`;
|
|
6035
|
+
}
|
|
6036
|
+
const catX = padLeft + catIdx * groupW + groupW / 2;
|
|
6037
|
+
svg += `<text x="${catX}" y="${padTop + chartH + 18}" font-size="11" fill="#475569" text-anchor="middle">${categories[catIdx]}</text>`;
|
|
6038
|
+
}
|
|
6039
|
+
let legendY = padTop + 20;
|
|
6040
|
+
for (let sIdx = 0; sIdx < numSeries; sIdx++) {
|
|
6041
|
+
const col = colors[sIdx % colors.length];
|
|
6042
|
+
svg += `<rect x="${padLeft + chartW + 15}" y="${legendY}" width="12" height="12" fill="${col}" rx="2" />`;
|
|
6043
|
+
svg += `<text x="${padLeft + chartW + 32}" y="${legendY + 10}" font-size="11" fill="#334155">${headers[sIdx]}</text>`;
|
|
6044
|
+
legendY += 20;
|
|
6045
|
+
}
|
|
6046
|
+
svg += "</svg>";
|
|
6047
|
+
return svg;
|
|
6048
|
+
} catch (e) {
|
|
6049
|
+
console.warn("[DocPlugin] Error generating chart SVG:", e);
|
|
6050
|
+
return "";
|
|
6051
|
+
}
|
|
6052
|
+
}
|
|
6053
|
+
cleanWordDocFields(text, chartSvg = "") {
|
|
5945
6054
|
if (!text) return "";
|
|
5946
6055
|
let cleaned = text.replace(
|
|
6056
|
+
/\x13\s*EMBED\b[\s\S]*?\x15/gi,
|
|
6057
|
+
() => chartSvg ? `
|
|
6058
|
+
|
|
6059
|
+
${chartSvg}
|
|
6060
|
+
|
|
6061
|
+
` : ""
|
|
6062
|
+
);
|
|
6063
|
+
cleaned = cleaned.replace(
|
|
5947
6064
|
/\x13\s*HYPERLINK\s*"?([^"\x14]+)"?\s*\x14([\s\S]*?)\x15/gi,
|
|
5948
6065
|
(_match, url, label) => {
|
|
5949
6066
|
const cleanUrl = url.trim();
|
|
@@ -5951,18 +6068,23 @@ var DocPlugin = class {
|
|
|
5951
6068
|
return `<a href="${cleanUrl}" target="_blank" rel="noopener noreferrer" style="color: #2563eb; text-decoration: underline;">${cleanLabel}</a>`;
|
|
5952
6069
|
}
|
|
5953
6070
|
);
|
|
5954
|
-
cleaned = cleaned.replace(/\x13[^\x14\x15]*\x14([^\x15]*)\x15/g,
|
|
6071
|
+
cleaned = cleaned.replace(/\x13[^\x14\x15]*\x14([^\x15]*)\x15/g, (_m, res) => {
|
|
6072
|
+
if (/[\x00-\x1F]/.test(res)) return "";
|
|
6073
|
+
return res.trim();
|
|
6074
|
+
});
|
|
5955
6075
|
cleaned = cleaned.replace(/\x13[^\x15]*\x15/g, "");
|
|
5956
6076
|
cleaned = cleaned.replace(/[\x13\x14\x15]/g, "");
|
|
6077
|
+
cleaned = cleaned.replace(/[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]/g, "");
|
|
6078
|
+
cleaned = cleaned.replace(/EMBED\s+LibreOffice\.ChartDocument\.[0-9]+/gi, chartSvg || "");
|
|
5957
6079
|
return cleaned;
|
|
5958
6080
|
}
|
|
5959
|
-
splitIntoPages(text) {
|
|
6081
|
+
splitIntoPages(text, chartSvg = "") {
|
|
5960
6082
|
if (!text) return [""];
|
|
5961
|
-
const cleanedText = this.cleanWordDocFields(text);
|
|
6083
|
+
const cleanedText = this.cleanWordDocFields(text, chartSvg);
|
|
5962
6084
|
const normalized = cleanedText.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").replace(/\x07\n/g, "\n").replace(/\x07/g, " ");
|
|
5963
6085
|
const explicitParts = normalized.split(/[\x0C\f]|\n\s*[-=_]{3,}\s*(?:PAGE|Page|page break)[\s\d\w-]*[-=_]{3,}\s*\n/i).map((p) => p.trim()).filter((p) => p.length > 0);
|
|
5964
6086
|
if (explicitParts.length === 0) explicitParts.push(normalized);
|
|
5965
|
-
const maxLinesPerPage =
|
|
6087
|
+
const maxLinesPerPage = 34;
|
|
5966
6088
|
const charsPerLine = 80;
|
|
5967
6089
|
const finalPages = [];
|
|
5968
6090
|
for (const part of explicitParts) {
|
|
@@ -5970,8 +6092,8 @@ var DocPlugin = class {
|
|
|
5970
6092
|
let currentLines = [];
|
|
5971
6093
|
let count = 0;
|
|
5972
6094
|
for (const line of lines) {
|
|
5973
|
-
const
|
|
5974
|
-
const vLines = Math.max(1, Math.ceil((
|
|
6095
|
+
const isSvg = line.includes("<svg");
|
|
6096
|
+
const vLines = isSvg ? 12 : Math.max(1, Math.ceil((line.replace(/<[^>]+>/g, "").length || 1) / charsPerLine));
|
|
5975
6097
|
if (count + vLines > maxLinesPerPage && currentLines.length > 0) {
|
|
5976
6098
|
finalPages.push(currentLines.join("\n"));
|
|
5977
6099
|
currentLines = [];
|
|
@@ -6011,9 +6133,44 @@ var DocPlugin = class {
|
|
|
6011
6133
|
tableLines = [];
|
|
6012
6134
|
}
|
|
6013
6135
|
};
|
|
6136
|
+
const sanitizeOptions = {
|
|
6137
|
+
ADD_TAGS: ["a", "svg", "g", "path", "line", "rect", "circle", "text", "title"],
|
|
6138
|
+
ADD_ATTR: [
|
|
6139
|
+
"href",
|
|
6140
|
+
"target",
|
|
6141
|
+
"rel",
|
|
6142
|
+
"style",
|
|
6143
|
+
"viewBox",
|
|
6144
|
+
"width",
|
|
6145
|
+
"height",
|
|
6146
|
+
"x",
|
|
6147
|
+
"y",
|
|
6148
|
+
"x1",
|
|
6149
|
+
"y1",
|
|
6150
|
+
"x2",
|
|
6151
|
+
"y2",
|
|
6152
|
+
"fill",
|
|
6153
|
+
"stroke",
|
|
6154
|
+
"stroke-width",
|
|
6155
|
+
"stroke-dasharray",
|
|
6156
|
+
"rx",
|
|
6157
|
+
"font-size",
|
|
6158
|
+
"text-anchor"
|
|
6159
|
+
]
|
|
6160
|
+
};
|
|
6014
6161
|
let i = 0;
|
|
6015
6162
|
while (i < lines.length) {
|
|
6016
6163
|
let line = lines[i];
|
|
6164
|
+
if (line.includes("<svg")) {
|
|
6165
|
+
if (inList) {
|
|
6166
|
+
html += "</ul>";
|
|
6167
|
+
inList = false;
|
|
6168
|
+
}
|
|
6169
|
+
flushTable();
|
|
6170
|
+
html += line;
|
|
6171
|
+
i++;
|
|
6172
|
+
continue;
|
|
6173
|
+
}
|
|
6017
6174
|
let tabCount = (line.match(/\t/g) || []).length;
|
|
6018
6175
|
if (tabCount > 0) {
|
|
6019
6176
|
let j = i;
|
|
@@ -6047,7 +6204,6 @@ var DocPlugin = class {
|
|
|
6047
6204
|
i++;
|
|
6048
6205
|
continue;
|
|
6049
6206
|
}
|
|
6050
|
-
const sanitizeOptions = { ADD_TAGS: ["a"], ADD_ATTR: ["href", "target", "rel", "style"] };
|
|
6051
6207
|
if (line.length < 60 && !line.endsWith(".") && (/^[A-Z0-9\s:_-]+$/.test(line) || line.startsWith("#"))) {
|
|
6052
6208
|
if (inList) {
|
|
6053
6209
|
html += "</ul>";
|