@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/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import DOMPurify6 from 'dompurify';
|
|
2
2
|
import * as pdfjsLib from 'pdfjs-dist';
|
|
3
3
|
import * as docx from 'docx-preview';
|
|
4
|
+
import * as fflate from 'fflate';
|
|
4
5
|
import { unzipSync, strFromU8, unzip } from 'fflate';
|
|
5
6
|
import * as XLSX from 'xlsx';
|
|
6
7
|
import hljs from 'highlight.js';
|
|
@@ -750,9 +751,11 @@ var ToolbarController = class {
|
|
|
750
751
|
type: "button",
|
|
751
752
|
"data-action-id": id
|
|
752
753
|
});
|
|
753
|
-
const
|
|
754
|
-
if (
|
|
755
|
-
btn.innerHTML =
|
|
754
|
+
const internalSvg = ICON_MAP[iconHtml] || ICON_MAP[id];
|
|
755
|
+
if (internalSvg) {
|
|
756
|
+
btn.innerHTML = internalSvg;
|
|
757
|
+
} else if (iconHtml && iconHtml.startsWith("<svg")) {
|
|
758
|
+
btn.innerHTML = sanitizeSVG(iconHtml);
|
|
756
759
|
} else {
|
|
757
760
|
btn.textContent = title || id;
|
|
758
761
|
}
|
|
@@ -5752,8 +5755,17 @@ var DocPlugin = class {
|
|
|
5752
5755
|
let scale = 1;
|
|
5753
5756
|
let extractedRawText = "";
|
|
5754
5757
|
let isFallback = false;
|
|
5758
|
+
let chartSvg = "";
|
|
5755
5759
|
try {
|
|
5756
5760
|
const cfbf = new CfbfReader(ctx.buffer);
|
|
5761
|
+
try {
|
|
5762
|
+
const pkg = cfbf.readStream("package_stream");
|
|
5763
|
+
if (pkg && pkg.length > 100) {
|
|
5764
|
+
chartSvg = this.parseOdfChartToSvg(pkg);
|
|
5765
|
+
}
|
|
5766
|
+
} catch (chartErr) {
|
|
5767
|
+
console.warn("[DocPlugin] Chart stream parsing info:", chartErr);
|
|
5768
|
+
}
|
|
5757
5769
|
const wordDocStream = cfbf.readStream("WordDocument");
|
|
5758
5770
|
if (!wordDocStream || wordDocStream.length < 512) {
|
|
5759
5771
|
throw new Error("WordDocument stream not found or invalid in CFBF archive");
|
|
@@ -5771,7 +5783,7 @@ var DocPlugin = class {
|
|
|
5771
5783
|
extractedRawText = fallback;
|
|
5772
5784
|
isFallback = true;
|
|
5773
5785
|
}
|
|
5774
|
-
const rawPages = this.splitIntoPages(extractedRawText);
|
|
5786
|
+
const rawPages = this.splitIntoPages(extractedRawText, chartSvg);
|
|
5775
5787
|
const totalPages = Math.max(1, rawPages.length);
|
|
5776
5788
|
let currentPage = 1;
|
|
5777
5789
|
const pageCards = [];
|
|
@@ -6013,7 +6025,7 @@ var DocPlugin = class {
|
|
|
6013
6025
|
for (const run of [...ansiRuns, ...utf16Runs]) {
|
|
6014
6026
|
const trimmed = run.trim();
|
|
6015
6027
|
if (trimmed.length >= 4 && /[a-zA-Z]/.test(trimmed) && !seen.has(trimmed)) {
|
|
6016
|
-
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)) {
|
|
6028
|
+
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)) {
|
|
6017
6029
|
seen.add(trimmed);
|
|
6018
6030
|
candidateLines.push(trimmed);
|
|
6019
6031
|
}
|
|
@@ -6024,9 +6036,114 @@ var DocPlugin = class {
|
|
|
6024
6036
|
heuristicTextExtraction(buffer) {
|
|
6025
6037
|
return this.extractStringsFromBytes(new Uint8Array(buffer));
|
|
6026
6038
|
}
|
|
6027
|
-
|
|
6039
|
+
/**
|
|
6040
|
+
* Parses an embedded OpenDocument Chart package into a vector SVG bar/column chart
|
|
6041
|
+
*/
|
|
6042
|
+
parseOdfChartToSvg(zipBytes) {
|
|
6043
|
+
try {
|
|
6044
|
+
const unzipped = fflate.unzipSync(zipBytes);
|
|
6045
|
+
const contentXml = unzipped["content.xml"] ? new TextDecoder("utf-8").decode(unzipped["content.xml"]) : "";
|
|
6046
|
+
if (!contentXml) return "";
|
|
6047
|
+
const rowsMatch = contentXml.match(/<table:table-row[\s\S]*?<\/table:table-row>/g) || [];
|
|
6048
|
+
if (rowsMatch.length < 2) return "";
|
|
6049
|
+
const headers = [];
|
|
6050
|
+
const firstRow = rowsMatch[0];
|
|
6051
|
+
const headerCells = firstRow ? firstRow.match(/<text:p>([^<]+)<\/text:p>/g) || [] : [];
|
|
6052
|
+
for (const h of headerCells) {
|
|
6053
|
+
headers.push(h.replace(/<\/?text:p>/g, "").trim());
|
|
6054
|
+
}
|
|
6055
|
+
const categories = [];
|
|
6056
|
+
const seriesValues = headers.map(() => []);
|
|
6057
|
+
for (let r = 1; r < rowsMatch.length; r++) {
|
|
6058
|
+
const rowStr = rowsMatch[r];
|
|
6059
|
+
if (!rowStr) continue;
|
|
6060
|
+
const cells = rowStr.match(/<table:table-cell[\s\S]*?<\/table:table-cell>/g) || [];
|
|
6061
|
+
if (cells.length > 0 && cells[0]) {
|
|
6062
|
+
const catMatch = cells[0].match(/<text:p>([^<]+)<\/text:p>/);
|
|
6063
|
+
categories.push(catMatch ? catMatch[1] : "Row " + r);
|
|
6064
|
+
for (let c = 1; c < cells.length && c - 1 < headers.length; c++) {
|
|
6065
|
+
const cellStr = cells[c];
|
|
6066
|
+
if (!cellStr) continue;
|
|
6067
|
+
const valMatch = cellStr.match(/office:value="([0-9.]+)"/) || cellStr.match(/<text:p>([0-9.]+)<\/text:p>/);
|
|
6068
|
+
const series = seriesValues[c - 1];
|
|
6069
|
+
if (series) {
|
|
6070
|
+
series.push(valMatch ? parseFloat(valMatch[1]) : 0);
|
|
6071
|
+
}
|
|
6072
|
+
}
|
|
6073
|
+
}
|
|
6074
|
+
}
|
|
6075
|
+
const colors = ["#004586", "#ff420e", "#ffd320", "#579d1c", "#7e0021"];
|
|
6076
|
+
const colorMatches = contentXml.matchAll(/draw:fill-color="(#[0-9a-fA-F]{6})"/g);
|
|
6077
|
+
let cIdx = 0;
|
|
6078
|
+
for (const cm of colorMatches) {
|
|
6079
|
+
if (cIdx < colors.length) colors[cIdx] = cm[1];
|
|
6080
|
+
cIdx++;
|
|
6081
|
+
}
|
|
6082
|
+
let maxVal = 10;
|
|
6083
|
+
for (const s of seriesValues) {
|
|
6084
|
+
for (const v of s) {
|
|
6085
|
+
if (v > maxVal) maxVal = v;
|
|
6086
|
+
}
|
|
6087
|
+
}
|
|
6088
|
+
maxVal = Math.ceil(maxVal * 1.15);
|
|
6089
|
+
const width = 560;
|
|
6090
|
+
const height = 280;
|
|
6091
|
+
const padLeft = 45;
|
|
6092
|
+
const padRight = 100;
|
|
6093
|
+
const padTop = 20;
|
|
6094
|
+
const padBottom = 40;
|
|
6095
|
+
const chartW = width - padLeft - padRight;
|
|
6096
|
+
const chartH = height - padTop - padBottom;
|
|
6097
|
+
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);">`;
|
|
6098
|
+
for (let step = 0; step <= 4; step++) {
|
|
6099
|
+
const yVal = (maxVal / 4 * step).toFixed(1);
|
|
6100
|
+
const yPos = padTop + chartH - step / 4 * chartH;
|
|
6101
|
+
svg += `<line x1="${padLeft}" y1="${yPos}" x2="${padLeft + chartW}" y2="${yPos}" stroke="#e2e8f0" stroke-dasharray="2,2" />`;
|
|
6102
|
+
svg += `<text x="${padLeft - 8}" y="${yPos + 4}" font-size="11" fill="#64748b" text-anchor="end">${yVal}</text>`;
|
|
6103
|
+
}
|
|
6104
|
+
const numCats = categories.length;
|
|
6105
|
+
const numSeries = headers.length;
|
|
6106
|
+
const groupW = chartW / numCats;
|
|
6107
|
+
const barW = Math.max(8, groupW * 0.7 / numSeries);
|
|
6108
|
+
const groupPad = (groupW - barW * numSeries) / 2;
|
|
6109
|
+
for (let catIdx = 0; catIdx < numCats; catIdx++) {
|
|
6110
|
+
const groupX = padLeft + catIdx * groupW + groupPad;
|
|
6111
|
+
for (let sIdx = 0; sIdx < numSeries; sIdx++) {
|
|
6112
|
+
const val = seriesValues[sIdx][catIdx] || 0;
|
|
6113
|
+
const barH = val / maxVal * chartH;
|
|
6114
|
+
const barX = groupX + sIdx * barW;
|
|
6115
|
+
const barY = padTop + chartH - barH;
|
|
6116
|
+
const col = colors[sIdx % colors.length];
|
|
6117
|
+
svg += `<rect x="${barX}" y="${barY}" width="${barW - 2}" height="${barH}" fill="${col}" rx="2"><title>${headers[sIdx]}: ${val}</title></rect>`;
|
|
6118
|
+
}
|
|
6119
|
+
const catX = padLeft + catIdx * groupW + groupW / 2;
|
|
6120
|
+
svg += `<text x="${catX}" y="${padTop + chartH + 18}" font-size="11" fill="#475569" text-anchor="middle">${categories[catIdx]}</text>`;
|
|
6121
|
+
}
|
|
6122
|
+
let legendY = padTop + 20;
|
|
6123
|
+
for (let sIdx = 0; sIdx < numSeries; sIdx++) {
|
|
6124
|
+
const col = colors[sIdx % colors.length];
|
|
6125
|
+
svg += `<rect x="${padLeft + chartW + 15}" y="${legendY}" width="12" height="12" fill="${col}" rx="2" />`;
|
|
6126
|
+
svg += `<text x="${padLeft + chartW + 32}" y="${legendY + 10}" font-size="11" fill="#334155">${headers[sIdx]}</text>`;
|
|
6127
|
+
legendY += 20;
|
|
6128
|
+
}
|
|
6129
|
+
svg += "</svg>";
|
|
6130
|
+
return svg;
|
|
6131
|
+
} catch (e) {
|
|
6132
|
+
console.warn("[DocPlugin] Error generating chart SVG:", e);
|
|
6133
|
+
return "";
|
|
6134
|
+
}
|
|
6135
|
+
}
|
|
6136
|
+
cleanWordDocFields(text, chartSvg = "") {
|
|
6028
6137
|
if (!text) return "";
|
|
6029
6138
|
let cleaned = text.replace(
|
|
6139
|
+
/\x13\s*EMBED\b[\s\S]*?\x15/gi,
|
|
6140
|
+
() => chartSvg ? `
|
|
6141
|
+
|
|
6142
|
+
${chartSvg}
|
|
6143
|
+
|
|
6144
|
+
` : ""
|
|
6145
|
+
);
|
|
6146
|
+
cleaned = cleaned.replace(
|
|
6030
6147
|
/\x13\s*HYPERLINK\s*"?([^"\x14]+)"?\s*\x14([\s\S]*?)\x15/gi,
|
|
6031
6148
|
(_match, url, label) => {
|
|
6032
6149
|
const cleanUrl = url.trim();
|
|
@@ -6034,18 +6151,23 @@ var DocPlugin = class {
|
|
|
6034
6151
|
return `<a href="${cleanUrl}" target="_blank" rel="noopener noreferrer" style="color: #2563eb; text-decoration: underline;">${cleanLabel}</a>`;
|
|
6035
6152
|
}
|
|
6036
6153
|
);
|
|
6037
|
-
cleaned = cleaned.replace(/\x13[^\x14\x15]*\x14([^\x15]*)\x15/g,
|
|
6154
|
+
cleaned = cleaned.replace(/\x13[^\x14\x15]*\x14([^\x15]*)\x15/g, (_m, res) => {
|
|
6155
|
+
if (/[\x00-\x1F]/.test(res)) return "";
|
|
6156
|
+
return res.trim();
|
|
6157
|
+
});
|
|
6038
6158
|
cleaned = cleaned.replace(/\x13[^\x15]*\x15/g, "");
|
|
6039
6159
|
cleaned = cleaned.replace(/[\x13\x14\x15]/g, "");
|
|
6160
|
+
cleaned = cleaned.replace(/[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]/g, "");
|
|
6161
|
+
cleaned = cleaned.replace(/EMBED\s+LibreOffice\.ChartDocument\.[0-9]+/gi, chartSvg || "");
|
|
6040
6162
|
return cleaned;
|
|
6041
6163
|
}
|
|
6042
|
-
splitIntoPages(text) {
|
|
6164
|
+
splitIntoPages(text, chartSvg = "") {
|
|
6043
6165
|
if (!text) return [""];
|
|
6044
|
-
const cleanedText = this.cleanWordDocFields(text);
|
|
6166
|
+
const cleanedText = this.cleanWordDocFields(text, chartSvg);
|
|
6045
6167
|
const normalized = cleanedText.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").replace(/\x07\n/g, "\n").replace(/\x07/g, " ");
|
|
6046
6168
|
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);
|
|
6047
6169
|
if (explicitParts.length === 0) explicitParts.push(normalized);
|
|
6048
|
-
const maxLinesPerPage =
|
|
6170
|
+
const maxLinesPerPage = 34;
|
|
6049
6171
|
const charsPerLine = 80;
|
|
6050
6172
|
const finalPages = [];
|
|
6051
6173
|
for (const part of explicitParts) {
|
|
@@ -6053,8 +6175,8 @@ var DocPlugin = class {
|
|
|
6053
6175
|
let currentLines = [];
|
|
6054
6176
|
let count = 0;
|
|
6055
6177
|
for (const line of lines) {
|
|
6056
|
-
const
|
|
6057
|
-
const vLines = Math.max(1, Math.ceil((
|
|
6178
|
+
const isSvg = line.includes("<svg");
|
|
6179
|
+
const vLines = isSvg ? 12 : Math.max(1, Math.ceil((line.replace(/<[^>]+>/g, "").length || 1) / charsPerLine));
|
|
6058
6180
|
if (count + vLines > maxLinesPerPage && currentLines.length > 0) {
|
|
6059
6181
|
finalPages.push(currentLines.join("\n"));
|
|
6060
6182
|
currentLines = [];
|
|
@@ -6094,9 +6216,44 @@ var DocPlugin = class {
|
|
|
6094
6216
|
tableLines = [];
|
|
6095
6217
|
}
|
|
6096
6218
|
};
|
|
6219
|
+
const sanitizeOptions = {
|
|
6220
|
+
ADD_TAGS: ["a", "svg", "g", "path", "line", "rect", "circle", "text", "title"],
|
|
6221
|
+
ADD_ATTR: [
|
|
6222
|
+
"href",
|
|
6223
|
+
"target",
|
|
6224
|
+
"rel",
|
|
6225
|
+
"style",
|
|
6226
|
+
"viewBox",
|
|
6227
|
+
"width",
|
|
6228
|
+
"height",
|
|
6229
|
+
"x",
|
|
6230
|
+
"y",
|
|
6231
|
+
"x1",
|
|
6232
|
+
"y1",
|
|
6233
|
+
"x2",
|
|
6234
|
+
"y2",
|
|
6235
|
+
"fill",
|
|
6236
|
+
"stroke",
|
|
6237
|
+
"stroke-width",
|
|
6238
|
+
"stroke-dasharray",
|
|
6239
|
+
"rx",
|
|
6240
|
+
"font-size",
|
|
6241
|
+
"text-anchor"
|
|
6242
|
+
]
|
|
6243
|
+
};
|
|
6097
6244
|
let i = 0;
|
|
6098
6245
|
while (i < lines.length) {
|
|
6099
6246
|
let line = lines[i];
|
|
6247
|
+
if (line.includes("<svg")) {
|
|
6248
|
+
if (inList) {
|
|
6249
|
+
html += "</ul>";
|
|
6250
|
+
inList = false;
|
|
6251
|
+
}
|
|
6252
|
+
flushTable();
|
|
6253
|
+
html += line;
|
|
6254
|
+
i++;
|
|
6255
|
+
continue;
|
|
6256
|
+
}
|
|
6100
6257
|
let tabCount = (line.match(/\t/g) || []).length;
|
|
6101
6258
|
if (tabCount > 0) {
|
|
6102
6259
|
let j = i;
|
|
@@ -6130,7 +6287,6 @@ var DocPlugin = class {
|
|
|
6130
6287
|
i++;
|
|
6131
6288
|
continue;
|
|
6132
6289
|
}
|
|
6133
|
-
const sanitizeOptions = { ADD_TAGS: ["a"], ADD_ATTR: ["href", "target", "rel", "style"] };
|
|
6134
6290
|
if (line.length < 60 && !line.endsWith(".") && (/^[A-Z0-9\s:_-]+$/.test(line) || line.startsWith("#"))) {
|
|
6135
6291
|
if (inList) {
|
|
6136
6292
|
html += "</ul>";
|