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