@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/angular.js
CHANGED
|
@@ -3,6 +3,7 @@ import { CommonModule } from '@angular/common';
|
|
|
3
3
|
import DOMPurify6 from 'dompurify';
|
|
4
4
|
import * as pdfjsLib from 'pdfjs-dist';
|
|
5
5
|
import * as docx from 'docx-preview';
|
|
6
|
+
import * as fflate from 'fflate';
|
|
6
7
|
import { unzipSync, strFromU8, unzip } from 'fflate';
|
|
7
8
|
import * as XLSX from 'xlsx';
|
|
8
9
|
import hljs from 'highlight.js';
|
|
@@ -714,9 +715,11 @@ var ToolbarController = class {
|
|
|
714
715
|
type: "button",
|
|
715
716
|
"data-action-id": id
|
|
716
717
|
});
|
|
717
|
-
const
|
|
718
|
-
if (
|
|
719
|
-
btn.innerHTML =
|
|
718
|
+
const internalSvg = ICON_MAP[iconHtml] || ICON_MAP[id];
|
|
719
|
+
if (internalSvg) {
|
|
720
|
+
btn.innerHTML = internalSvg;
|
|
721
|
+
} else if (iconHtml && iconHtml.startsWith("<svg")) {
|
|
722
|
+
btn.innerHTML = sanitizeSVG(iconHtml);
|
|
720
723
|
} else {
|
|
721
724
|
btn.textContent = title || id;
|
|
722
725
|
}
|
|
@@ -5716,8 +5719,17 @@ var DocPlugin = class {
|
|
|
5716
5719
|
let scale = 1;
|
|
5717
5720
|
let extractedRawText = "";
|
|
5718
5721
|
let isFallback = false;
|
|
5722
|
+
let chartSvg = "";
|
|
5719
5723
|
try {
|
|
5720
5724
|
const cfbf = new CfbfReader(ctx.buffer);
|
|
5725
|
+
try {
|
|
5726
|
+
const pkg = cfbf.readStream("package_stream");
|
|
5727
|
+
if (pkg && pkg.length > 100) {
|
|
5728
|
+
chartSvg = this.parseOdfChartToSvg(pkg);
|
|
5729
|
+
}
|
|
5730
|
+
} catch (chartErr) {
|
|
5731
|
+
console.warn("[DocPlugin] Chart stream parsing info:", chartErr);
|
|
5732
|
+
}
|
|
5721
5733
|
const wordDocStream = cfbf.readStream("WordDocument");
|
|
5722
5734
|
if (!wordDocStream || wordDocStream.length < 512) {
|
|
5723
5735
|
throw new Error("WordDocument stream not found or invalid in CFBF archive");
|
|
@@ -5735,7 +5747,7 @@ var DocPlugin = class {
|
|
|
5735
5747
|
extractedRawText = fallback;
|
|
5736
5748
|
isFallback = true;
|
|
5737
5749
|
}
|
|
5738
|
-
const rawPages = this.splitIntoPages(extractedRawText);
|
|
5750
|
+
const rawPages = this.splitIntoPages(extractedRawText, chartSvg);
|
|
5739
5751
|
const totalPages = Math.max(1, rawPages.length);
|
|
5740
5752
|
let currentPage = 1;
|
|
5741
5753
|
const pageCards = [];
|
|
@@ -5977,7 +5989,7 @@ var DocPlugin = class {
|
|
|
5977
5989
|
for (const run of [...ansiRuns, ...utf16Runs]) {
|
|
5978
5990
|
const trimmed = run.trim();
|
|
5979
5991
|
if (trimmed.length >= 4 && /[a-zA-Z]/.test(trimmed) && !seen.has(trimmed)) {
|
|
5980
|
-
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)) {
|
|
5992
|
+
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)) {
|
|
5981
5993
|
seen.add(trimmed);
|
|
5982
5994
|
candidateLines.push(trimmed);
|
|
5983
5995
|
}
|
|
@@ -5988,9 +6000,114 @@ var DocPlugin = class {
|
|
|
5988
6000
|
heuristicTextExtraction(buffer) {
|
|
5989
6001
|
return this.extractStringsFromBytes(new Uint8Array(buffer));
|
|
5990
6002
|
}
|
|
5991
|
-
|
|
6003
|
+
/**
|
|
6004
|
+
* Parses an embedded OpenDocument Chart package into a vector SVG bar/column chart
|
|
6005
|
+
*/
|
|
6006
|
+
parseOdfChartToSvg(zipBytes) {
|
|
6007
|
+
try {
|
|
6008
|
+
const unzipped = fflate.unzipSync(zipBytes);
|
|
6009
|
+
const contentXml = unzipped["content.xml"] ? new TextDecoder("utf-8").decode(unzipped["content.xml"]) : "";
|
|
6010
|
+
if (!contentXml) return "";
|
|
6011
|
+
const rowsMatch = contentXml.match(/<table:table-row[\s\S]*?<\/table:table-row>/g) || [];
|
|
6012
|
+
if (rowsMatch.length < 2) return "";
|
|
6013
|
+
const headers = [];
|
|
6014
|
+
const firstRow = rowsMatch[0];
|
|
6015
|
+
const headerCells = firstRow ? firstRow.match(/<text:p>([^<]+)<\/text:p>/g) || [] : [];
|
|
6016
|
+
for (const h of headerCells) {
|
|
6017
|
+
headers.push(h.replace(/<\/?text:p>/g, "").trim());
|
|
6018
|
+
}
|
|
6019
|
+
const categories = [];
|
|
6020
|
+
const seriesValues = headers.map(() => []);
|
|
6021
|
+
for (let r = 1; r < rowsMatch.length; r++) {
|
|
6022
|
+
const rowStr = rowsMatch[r];
|
|
6023
|
+
if (!rowStr) continue;
|
|
6024
|
+
const cells = rowStr.match(/<table:table-cell[\s\S]*?<\/table:table-cell>/g) || [];
|
|
6025
|
+
if (cells.length > 0 && cells[0]) {
|
|
6026
|
+
const catMatch = cells[0].match(/<text:p>([^<]+)<\/text:p>/);
|
|
6027
|
+
categories.push(catMatch ? catMatch[1] : "Row " + r);
|
|
6028
|
+
for (let c = 1; c < cells.length && c - 1 < headers.length; c++) {
|
|
6029
|
+
const cellStr = cells[c];
|
|
6030
|
+
if (!cellStr) continue;
|
|
6031
|
+
const valMatch = cellStr.match(/office:value="([0-9.]+)"/) || cellStr.match(/<text:p>([0-9.]+)<\/text:p>/);
|
|
6032
|
+
const series = seriesValues[c - 1];
|
|
6033
|
+
if (series) {
|
|
6034
|
+
series.push(valMatch ? parseFloat(valMatch[1]) : 0);
|
|
6035
|
+
}
|
|
6036
|
+
}
|
|
6037
|
+
}
|
|
6038
|
+
}
|
|
6039
|
+
const colors = ["#004586", "#ff420e", "#ffd320", "#579d1c", "#7e0021"];
|
|
6040
|
+
const colorMatches = contentXml.matchAll(/draw:fill-color="(#[0-9a-fA-F]{6})"/g);
|
|
6041
|
+
let cIdx = 0;
|
|
6042
|
+
for (const cm of colorMatches) {
|
|
6043
|
+
if (cIdx < colors.length) colors[cIdx] = cm[1];
|
|
6044
|
+
cIdx++;
|
|
6045
|
+
}
|
|
6046
|
+
let maxVal = 10;
|
|
6047
|
+
for (const s of seriesValues) {
|
|
6048
|
+
for (const v of s) {
|
|
6049
|
+
if (v > maxVal) maxVal = v;
|
|
6050
|
+
}
|
|
6051
|
+
}
|
|
6052
|
+
maxVal = Math.ceil(maxVal * 1.15);
|
|
6053
|
+
const width = 560;
|
|
6054
|
+
const height = 280;
|
|
6055
|
+
const padLeft = 45;
|
|
6056
|
+
const padRight = 100;
|
|
6057
|
+
const padTop = 20;
|
|
6058
|
+
const padBottom = 40;
|
|
6059
|
+
const chartW = width - padLeft - padRight;
|
|
6060
|
+
const chartH = height - padTop - padBottom;
|
|
6061
|
+
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);">`;
|
|
6062
|
+
for (let step = 0; step <= 4; step++) {
|
|
6063
|
+
const yVal = (maxVal / 4 * step).toFixed(1);
|
|
6064
|
+
const yPos = padTop + chartH - step / 4 * chartH;
|
|
6065
|
+
svg += `<line x1="${padLeft}" y1="${yPos}" x2="${padLeft + chartW}" y2="${yPos}" stroke="#e2e8f0" stroke-dasharray="2,2" />`;
|
|
6066
|
+
svg += `<text x="${padLeft - 8}" y="${yPos + 4}" font-size="11" fill="#64748b" text-anchor="end">${yVal}</text>`;
|
|
6067
|
+
}
|
|
6068
|
+
const numCats = categories.length;
|
|
6069
|
+
const numSeries = headers.length;
|
|
6070
|
+
const groupW = chartW / numCats;
|
|
6071
|
+
const barW = Math.max(8, groupW * 0.7 / numSeries);
|
|
6072
|
+
const groupPad = (groupW - barW * numSeries) / 2;
|
|
6073
|
+
for (let catIdx = 0; catIdx < numCats; catIdx++) {
|
|
6074
|
+
const groupX = padLeft + catIdx * groupW + groupPad;
|
|
6075
|
+
for (let sIdx = 0; sIdx < numSeries; sIdx++) {
|
|
6076
|
+
const val = seriesValues[sIdx][catIdx] || 0;
|
|
6077
|
+
const barH = val / maxVal * chartH;
|
|
6078
|
+
const barX = groupX + sIdx * barW;
|
|
6079
|
+
const barY = padTop + chartH - barH;
|
|
6080
|
+
const col = colors[sIdx % colors.length];
|
|
6081
|
+
svg += `<rect x="${barX}" y="${barY}" width="${barW - 2}" height="${barH}" fill="${col}" rx="2"><title>${headers[sIdx]}: ${val}</title></rect>`;
|
|
6082
|
+
}
|
|
6083
|
+
const catX = padLeft + catIdx * groupW + groupW / 2;
|
|
6084
|
+
svg += `<text x="${catX}" y="${padTop + chartH + 18}" font-size="11" fill="#475569" text-anchor="middle">${categories[catIdx]}</text>`;
|
|
6085
|
+
}
|
|
6086
|
+
let legendY = padTop + 20;
|
|
6087
|
+
for (let sIdx = 0; sIdx < numSeries; sIdx++) {
|
|
6088
|
+
const col = colors[sIdx % colors.length];
|
|
6089
|
+
svg += `<rect x="${padLeft + chartW + 15}" y="${legendY}" width="12" height="12" fill="${col}" rx="2" />`;
|
|
6090
|
+
svg += `<text x="${padLeft + chartW + 32}" y="${legendY + 10}" font-size="11" fill="#334155">${headers[sIdx]}</text>`;
|
|
6091
|
+
legendY += 20;
|
|
6092
|
+
}
|
|
6093
|
+
svg += "</svg>";
|
|
6094
|
+
return svg;
|
|
6095
|
+
} catch (e) {
|
|
6096
|
+
console.warn("[DocPlugin] Error generating chart SVG:", e);
|
|
6097
|
+
return "";
|
|
6098
|
+
}
|
|
6099
|
+
}
|
|
6100
|
+
cleanWordDocFields(text, chartSvg = "") {
|
|
5992
6101
|
if (!text) return "";
|
|
5993
6102
|
let cleaned = text.replace(
|
|
6103
|
+
/\x13\s*EMBED\b[\s\S]*?\x15/gi,
|
|
6104
|
+
() => chartSvg ? `
|
|
6105
|
+
|
|
6106
|
+
${chartSvg}
|
|
6107
|
+
|
|
6108
|
+
` : ""
|
|
6109
|
+
);
|
|
6110
|
+
cleaned = cleaned.replace(
|
|
5994
6111
|
/\x13\s*HYPERLINK\s*"?([^"\x14]+)"?\s*\x14([\s\S]*?)\x15/gi,
|
|
5995
6112
|
(_match, url, label) => {
|
|
5996
6113
|
const cleanUrl = url.trim();
|
|
@@ -5998,18 +6115,23 @@ var DocPlugin = class {
|
|
|
5998
6115
|
return `<a href="${cleanUrl}" target="_blank" rel="noopener noreferrer" style="color: #2563eb; text-decoration: underline;">${cleanLabel}</a>`;
|
|
5999
6116
|
}
|
|
6000
6117
|
);
|
|
6001
|
-
cleaned = cleaned.replace(/\x13[^\x14\x15]*\x14([^\x15]*)\x15/g,
|
|
6118
|
+
cleaned = cleaned.replace(/\x13[^\x14\x15]*\x14([^\x15]*)\x15/g, (_m, res) => {
|
|
6119
|
+
if (/[\x00-\x1F]/.test(res)) return "";
|
|
6120
|
+
return res.trim();
|
|
6121
|
+
});
|
|
6002
6122
|
cleaned = cleaned.replace(/\x13[^\x15]*\x15/g, "");
|
|
6003
6123
|
cleaned = cleaned.replace(/[\x13\x14\x15]/g, "");
|
|
6124
|
+
cleaned = cleaned.replace(/[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]/g, "");
|
|
6125
|
+
cleaned = cleaned.replace(/EMBED\s+LibreOffice\.ChartDocument\.[0-9]+/gi, chartSvg || "");
|
|
6004
6126
|
return cleaned;
|
|
6005
6127
|
}
|
|
6006
|
-
splitIntoPages(text) {
|
|
6128
|
+
splitIntoPages(text, chartSvg = "") {
|
|
6007
6129
|
if (!text) return [""];
|
|
6008
|
-
const cleanedText = this.cleanWordDocFields(text);
|
|
6130
|
+
const cleanedText = this.cleanWordDocFields(text, chartSvg);
|
|
6009
6131
|
const normalized = cleanedText.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").replace(/\x07\n/g, "\n").replace(/\x07/g, " ");
|
|
6010
6132
|
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);
|
|
6011
6133
|
if (explicitParts.length === 0) explicitParts.push(normalized);
|
|
6012
|
-
const maxLinesPerPage =
|
|
6134
|
+
const maxLinesPerPage = 34;
|
|
6013
6135
|
const charsPerLine = 80;
|
|
6014
6136
|
const finalPages = [];
|
|
6015
6137
|
for (const part of explicitParts) {
|
|
@@ -6017,8 +6139,8 @@ var DocPlugin = class {
|
|
|
6017
6139
|
let currentLines = [];
|
|
6018
6140
|
let count = 0;
|
|
6019
6141
|
for (const line of lines) {
|
|
6020
|
-
const
|
|
6021
|
-
const vLines = Math.max(1, Math.ceil((
|
|
6142
|
+
const isSvg = line.includes("<svg");
|
|
6143
|
+
const vLines = isSvg ? 12 : Math.max(1, Math.ceil((line.replace(/<[^>]+>/g, "").length || 1) / charsPerLine));
|
|
6022
6144
|
if (count + vLines > maxLinesPerPage && currentLines.length > 0) {
|
|
6023
6145
|
finalPages.push(currentLines.join("\n"));
|
|
6024
6146
|
currentLines = [];
|
|
@@ -6058,9 +6180,44 @@ var DocPlugin = class {
|
|
|
6058
6180
|
tableLines = [];
|
|
6059
6181
|
}
|
|
6060
6182
|
};
|
|
6183
|
+
const sanitizeOptions = {
|
|
6184
|
+
ADD_TAGS: ["a", "svg", "g", "path", "line", "rect", "circle", "text", "title"],
|
|
6185
|
+
ADD_ATTR: [
|
|
6186
|
+
"href",
|
|
6187
|
+
"target",
|
|
6188
|
+
"rel",
|
|
6189
|
+
"style",
|
|
6190
|
+
"viewBox",
|
|
6191
|
+
"width",
|
|
6192
|
+
"height",
|
|
6193
|
+
"x",
|
|
6194
|
+
"y",
|
|
6195
|
+
"x1",
|
|
6196
|
+
"y1",
|
|
6197
|
+
"x2",
|
|
6198
|
+
"y2",
|
|
6199
|
+
"fill",
|
|
6200
|
+
"stroke",
|
|
6201
|
+
"stroke-width",
|
|
6202
|
+
"stroke-dasharray",
|
|
6203
|
+
"rx",
|
|
6204
|
+
"font-size",
|
|
6205
|
+
"text-anchor"
|
|
6206
|
+
]
|
|
6207
|
+
};
|
|
6061
6208
|
let i = 0;
|
|
6062
6209
|
while (i < lines.length) {
|
|
6063
6210
|
let line = lines[i];
|
|
6211
|
+
if (line.includes("<svg")) {
|
|
6212
|
+
if (inList) {
|
|
6213
|
+
html += "</ul>";
|
|
6214
|
+
inList = false;
|
|
6215
|
+
}
|
|
6216
|
+
flushTable();
|
|
6217
|
+
html += line;
|
|
6218
|
+
i++;
|
|
6219
|
+
continue;
|
|
6220
|
+
}
|
|
6064
6221
|
let tabCount = (line.match(/\t/g) || []).length;
|
|
6065
6222
|
if (tabCount > 0) {
|
|
6066
6223
|
let j = i;
|
|
@@ -6094,7 +6251,6 @@ var DocPlugin = class {
|
|
|
6094
6251
|
i++;
|
|
6095
6252
|
continue;
|
|
6096
6253
|
}
|
|
6097
|
-
const sanitizeOptions = { ADD_TAGS: ["a"], ADD_ATTR: ["href", "target", "rel", "style"] };
|
|
6098
6254
|
if (line.length < 60 && !line.endsWith(".") && (/^[A-Z0-9\s:_-]+$/.test(line) || line.startsWith("#"))) {
|
|
6099
6255
|
if (inList) {
|
|
6100
6256
|
html += "</ul>";
|