@files-preview-app/preview-file 1.2.6 → 1.2.7

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/react.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { memo, forwardRef, useRef, useImperativeHandle, useEffect } from 'react';
2
- import DOMPurify2 from 'dompurify';
2
+ import DOMPurify6 from 'dompurify';
3
3
  import * as pdfjsLib from 'pdfjs-dist';
4
4
  import * as docx from 'docx-preview';
5
5
  import { unzipSync, strFromU8, unzip } from 'fflate';
@@ -358,7 +358,7 @@ async function sourceToArrayBuffer(source, signal) {
358
358
  if (magicMime === "application/zip") {
359
359
  const ooxmlMime = detectOoxmlType(buffer);
360
360
  if (ooxmlMime && ooxmlMime !== "application/zip") {
361
- metadata.mimeType = ooxmlMime;
361
+ metadata.mimeType = metadata.mimeType ?? ooxmlMime;
362
362
  if (ooxmlMime.includes("wordprocessing")) metadata.extension = metadata.extension ?? ".docx";
363
363
  else if (ooxmlMime.includes("spreadsheet")) metadata.extension = metadata.extension ?? ".xlsx";
364
364
  else if (ooxmlMime.includes("presentation")) metadata.extension = metadata.extension ?? ".pptx";
@@ -387,7 +387,7 @@ async function sourceToArrayBuffer(source, signal) {
387
387
  return { buffer, metadata };
388
388
  }
389
389
  function sanitizeSVG(svg) {
390
- return DOMPurify2.sanitize(svg, {
390
+ return DOMPurify6.sanitize(svg, {
391
391
  USE_PROFILES: { svg: true, svgFilters: true }
392
392
  });
393
393
  }
@@ -1449,7 +1449,19 @@ var PdfPlugin = class {
1449
1449
  }
1450
1450
  };
1451
1451
  await renderPage(1);
1452
+ let resizeTimer = null;
1453
+ const resizeObserver = new ResizeObserver(() => {
1454
+ if (resizeTimer) clearTimeout(resizeTimer);
1455
+ resizeTimer = setTimeout(() => {
1456
+ if (Math.abs(zoomScale - 1) < 0.05) {
1457
+ renderPage(currentPage);
1458
+ }
1459
+ }, 150);
1460
+ });
1461
+ resizeObserver.observe(container);
1452
1462
  const cleanup = () => {
1463
+ resizeObserver.disconnect();
1464
+ if (resizeTimer) clearTimeout(resizeTimer);
1453
1465
  if (currentRenderTask) {
1454
1466
  try {
1455
1467
  currentRenderTask.cancel();
@@ -2197,90 +2209,149 @@ var DocxPlugin = class {
2197
2209
  console.warn("[DocxPlugin] Error parsing relationships:", relsErr);
2198
2210
  }
2199
2211
  }
2200
- const card = document.createElement("div");
2201
- card.className = "fp-docx-page-card";
2202
- card.style.backgroundColor = "#ffffff";
2203
- card.style.borderRadius = "6px";
2204
- card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
2205
- card.style.padding = "48px 56px";
2206
- card.style.fontFamily = 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
2207
- card.style.color = "#1e293b";
2208
- card.style.lineHeight = "1.6";
2212
+ const sectPrs = Array.from(doc.getElementsByTagNameNS("*", "sectPr"));
2213
+ const lastSectPr = sectPrs[sectPrs.length - 1];
2214
+ let defaultW = 12240;
2215
+ let defaultH = 15840;
2216
+ let margins = { top: 1440, right: 1440, bottom: 1440, left: 1440 };
2217
+ if (lastSectPr) {
2218
+ const pgSz = lastSectPr.getElementsByTagNameNS("*", "pgSz")[0];
2219
+ if (pgSz) {
2220
+ defaultW = parseInt(pgSz.getAttribute("w:w") || pgSz.getAttribute("w") || "12240", 10);
2221
+ defaultH = parseInt(pgSz.getAttribute("w:h") || pgSz.getAttribute("h") || "15840", 10);
2222
+ }
2223
+ const pgMar = lastSectPr.getElementsByTagNameNS("*", "pgMar")[0];
2224
+ if (pgMar) {
2225
+ margins.top = parseInt(pgMar.getAttribute("w:top") || pgMar.getAttribute("top") || "1440", 10);
2226
+ margins.bottom = parseInt(pgMar.getAttribute("w:bottom") || pgMar.getAttribute("bottom") || "1440", 10);
2227
+ margins.left = parseInt(pgMar.getAttribute("w:left") || pgMar.getAttribute("left") || "1440", 10);
2228
+ margins.right = parseInt(pgMar.getAttribute("w:right") || pgMar.getAttribute("right") || "1440", 10);
2229
+ }
2230
+ }
2231
+ const createPageCard = () => {
2232
+ const card = document.createElement("div");
2233
+ card.className = "fp-docx-page-card";
2234
+ card.style.backgroundColor = "#ffffff";
2235
+ card.style.borderRadius = "4px";
2236
+ card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
2237
+ card.style.fontFamily = 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
2238
+ card.style.color = "#1e293b";
2239
+ card.style.lineHeight = "1.6";
2240
+ card.style.boxSizing = "border-box";
2241
+ card.style.overflow = "hidden";
2242
+ card.style.position = "relative";
2243
+ card.style.marginBottom = "24px";
2244
+ card.style.width = `${defaultW / 15}px`;
2245
+ card.style.height = `${defaultH / 15}px`;
2246
+ card.style.padding = `${margins.top / 15}px ${margins.right / 15}px ${margins.bottom / 15}px ${margins.left / 15}px`;
2247
+ return card;
2248
+ };
2249
+ let currentCard = createPageCard();
2250
+ let currentHtml = "";
2251
+ const pages = [{ card: currentCard, html: "" }];
2252
+ const flushHtml = () => {
2253
+ pages[pages.length - 1].html += currentHtml;
2254
+ currentHtml = "";
2255
+ };
2256
+ const newPage = () => {
2257
+ flushHtml();
2258
+ currentCard = createPageCard();
2259
+ pages.push({ card: currentCard, html: "" });
2260
+ };
2209
2261
  const body = doc.getElementsByTagNameNS("*", "body")[0] || doc.documentElement;
2210
- let html = "";
2211
2262
  for (const child of Array.from(body.children)) {
2212
2263
  const tag = child.localName || child.nodeName.split(":").pop();
2213
2264
  if (tag === "p") {
2214
2265
  const pStyle = child.getElementsByTagNameNS("*", "pStyle")[0];
2215
2266
  const styleVal = pStyle?.getAttribute("w:val") || pStyle?.getAttribute("val") || "";
2216
2267
  const numPr = child.getElementsByTagNameNS("*", "numPr")[0];
2217
- const textContent = this.extractParagraphHtml(child, imageMap);
2218
- if (!textContent.trim()) {
2219
- html += '<div style="height: 10px;"></div>';
2220
- continue;
2221
- }
2222
- const lowerStyle = styleVal.toLowerCase();
2223
- if (lowerStyle.includes("title")) {
2224
- html += `<h1 style="font-size: 28px; font-weight: 700; color: #1e3a8a; margin: 24px 0 12px; border-bottom: 2px solid #e2e8f0; padding-bottom: 8px;">${textContent}</h1>`;
2225
- } else if (lowerStyle.includes("heading1") || styleVal === "1") {
2226
- html += `<h2 style="font-size: 22px; font-weight: 700; color: #1e40af; margin: 20px 0 10px;">${textContent}</h2>`;
2227
- } else if (lowerStyle.includes("heading2") || styleVal === "2") {
2228
- html += `<h3 style="font-size: 18px; font-weight: 600; color: #2563eb; margin: 16px 0 8px;">${textContent}</h3>`;
2229
- } else if (lowerStyle.includes("heading3") || styleVal === "3") {
2230
- html += `<h4 style="font-size: 15px; font-weight: 600; color: #334155; margin: 12px 0 6px;">${textContent}</h4>`;
2231
- } else if (numPr) {
2232
- html += `<div style="display: flex; gap: 8px; margin: 4px 0 4px 20px;"><span style="color: #2563eb; font-weight: bold;">\u2022</span><span>${textContent}</span></div>`;
2233
- } else {
2234
- html += `<p style="margin: 8px 0; font-size: 14px;">${textContent}</p>`;
2268
+ const chunks = this.extractParagraphChunks(child, imageMap);
2269
+ for (let i = 0; i < chunks.length; i++) {
2270
+ if (i > 0) {
2271
+ newPage();
2272
+ }
2273
+ const textContent = chunks[i];
2274
+ if (!textContent.trim() && !textContent.includes("<img")) {
2275
+ currentHtml += '<div style="height: 10px;"></div>';
2276
+ continue;
2277
+ }
2278
+ const lowerStyle = styleVal.toLowerCase();
2279
+ if (lowerStyle.includes("title")) {
2280
+ currentHtml += `<h1 style="font-size: 28px; font-weight: 700; color: #1e3a8a; margin: 24px 0 12px; border-bottom: 2px solid #e2e8f0; padding-bottom: 8px;">${textContent}</h1>`;
2281
+ } else if (lowerStyle.includes("heading1") || styleVal === "1") {
2282
+ currentHtml += `<h2 style="font-size: 22px; font-weight: 700; color: #1e40af; margin: 20px 0 10px;">${textContent}</h2>`;
2283
+ } else if (lowerStyle.includes("heading2") || styleVal === "2") {
2284
+ currentHtml += `<h3 style="font-size: 18px; font-weight: 600; color: #2563eb; margin: 16px 0 8px;">${textContent}</h3>`;
2285
+ } else if (lowerStyle.includes("heading3") || styleVal === "3") {
2286
+ currentHtml += `<h4 style="font-size: 15px; font-weight: 600; color: #334155; margin: 12px 0 6px;">${textContent}</h4>`;
2287
+ } else if (numPr) {
2288
+ currentHtml += `<div style="display: flex; gap: 8px; margin: 4px 0 4px 20px;"><span style="color: #2563eb; font-weight: bold;">\u2022</span><span>${textContent}</span></div>`;
2289
+ } else {
2290
+ currentHtml += `<p style="margin: 8px 0; font-size: 14px;">${textContent}</p>`;
2291
+ }
2235
2292
  }
2236
2293
  } else if (tag === "tbl") {
2237
- html += '<table style="width: 100%; border-collapse: collapse; margin: 20px 0; border: 1px solid #cbd5e1;">';
2294
+ currentHtml += '<table style="width: 100%; border-collapse: collapse; margin: 20px 0; border: 1px solid #cbd5e1;">';
2238
2295
  const rows = Array.from(child.getElementsByTagNameNS("*", "tr"));
2239
2296
  rows.forEach((tr, rIdx) => {
2240
- html += `<tr style="${rIdx === 0 ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
2297
+ currentHtml += `<tr style="${rIdx === 0 ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
2241
2298
  const cells = Array.from(tr.getElementsByTagNameNS("*", "tc"));
2242
2299
  cells.forEach((tc) => {
2243
2300
  const cellText = this.extractParagraphHtml(tc, imageMap);
2244
- html += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px; font-size: 13px;">${cellText || "&nbsp;"}</td>`;
2301
+ currentHtml += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px; font-size: 13px;">${cellText || "&nbsp;"}</td>`;
2245
2302
  });
2246
- html += "</tr>";
2303
+ currentHtml += "</tr>";
2247
2304
  });
2248
- html += "</table>";
2305
+ currentHtml += "</table>";
2249
2306
  }
2250
2307
  }
2251
- card.innerHTML = DOMPurify2.sanitize(html, {
2252
- ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "s", "strike", "img", "div", "br"],
2253
- ADD_ATTR: ["style", "src", "alt", "colspan", "rowspan"]
2254
- });
2255
- wrapper.appendChild(card);
2308
+ flushHtml();
2309
+ for (const page of pages) {
2310
+ page.card.innerHTML = DOMPurify6.sanitize(page.html, {
2311
+ ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "s", "strike", "img", "div", "br"],
2312
+ ADD_ATTR: ["style", "src", "alt", "colspan", "rowspan"]
2313
+ });
2314
+ wrapper.appendChild(page.card);
2315
+ }
2256
2316
  }
2257
2317
  extractParagraphHtml(pElement, imageMap = {}) {
2258
- let result = "";
2318
+ return this.extractParagraphChunks(pElement, imageMap).join("<br/>");
2319
+ }
2320
+ extractParagraphChunks(pElement, imageMap = {}) {
2321
+ const chunks = [""];
2322
+ let currentChunkIndex = 0;
2259
2323
  const drawings = Array.from(pElement.getElementsByTagNameNS("*", "drawing"));
2260
2324
  for (const drawing of drawings) {
2261
2325
  const blip = drawing.getElementsByTagNameNS("*", "blip")[0];
2262
2326
  const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
2263
2327
  if (rId && imageMap[rId]) {
2264
- result += `<div style="text-align:center; margin: 12px 0;"><img src="${imageMap[rId]}" style="max-width: 100%; height: auto; border-radius: 4px;" /></div>`;
2328
+ chunks[currentChunkIndex] += `<div style="text-align:center; margin: 12px 0;"><img src="${imageMap[rId]}" style="max-width: 100%; height: auto; border-radius: 4px;" /></div>`;
2265
2329
  }
2266
2330
  }
2267
2331
  const runs = Array.from(pElement.getElementsByTagNameNS("*", "r"));
2268
- if (runs.length === 0 && !result) {
2269
- return DOMPurify2.sanitize(pElement.textContent || "");
2332
+ if (runs.length === 0 && !chunks[currentChunkIndex]) {
2333
+ chunks[currentChunkIndex] = DOMPurify6.sanitize(pElement.textContent || "");
2334
+ return chunks;
2270
2335
  }
2271
2336
  for (const r of runs) {
2272
2337
  const blip = r.getElementsByTagNameNS("*", "blip")[0] || r.getElementsByTagNameNS("*", "imagedata")[0];
2273
2338
  const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
2274
2339
  if (rId && imageMap[rId]) {
2275
- result += `<img src="${imageMap[rId]}" style="max-width: 100%; height: auto; display: inline-block; margin: 4px;" />`;
2340
+ chunks[currentChunkIndex] += `<img src="${imageMap[rId]}" style="max-width: 100%; height: auto; display: inline-block; margin: 4px;" />`;
2276
2341
  }
2277
- const brs = r.getElementsByTagNameNS("*", "br");
2278
- for (let i = 0; i < brs.length; i++) {
2279
- result += "<br/>";
2342
+ const brs = Array.from(r.getElementsByTagNameNS("*", "br"));
2343
+ for (const br of brs) {
2344
+ const type = br.getAttribute("w:type") || br.getAttribute("type");
2345
+ if (type === "page") {
2346
+ chunks.push("");
2347
+ currentChunkIndex++;
2348
+ } else {
2349
+ chunks[currentChunkIndex] += "<br/>";
2350
+ }
2280
2351
  }
2281
2352
  const tabs = r.getElementsByTagNameNS("*", "tab");
2282
2353
  if (tabs.length > 0) {
2283
- result += "&emsp;";
2354
+ chunks[currentChunkIndex] += "&emsp;";
2284
2355
  }
2285
2356
  const rPr = r.getElementsByTagNameNS("*", "rPr")[0];
2286
2357
  const isBold = !!rPr?.getElementsByTagNameNS("*", "b")[0];
@@ -2294,7 +2365,7 @@ var DocxPlugin = class {
2294
2365
  const texts = Array.from(r.getElementsByTagNameNS("*", "t"));
2295
2366
  let text = texts.map((t) => t.textContent || "").join("");
2296
2367
  if (!text) continue;
2297
- text = DOMPurify2.sanitize(text);
2368
+ text = DOMPurify6.sanitize(text);
2298
2369
  let styles = "";
2299
2370
  if (isBold) styles += "font-weight: bold; ";
2300
2371
  if (isItalic) styles += "font-style: italic; ";
@@ -2306,12 +2377,12 @@ var DocxPlugin = class {
2306
2377
  if (!isNaN(pt) && pt > 0) styles += `font-size: ${pt}pt; `;
2307
2378
  }
2308
2379
  if (styles) {
2309
- result += `<span style="${styles}">${text}</span>`;
2380
+ chunks[currentChunkIndex] += `<span style="${styles}">${text}</span>`;
2310
2381
  } else {
2311
- result += text;
2382
+ chunks[currentChunkIndex] += text;
2312
2383
  }
2313
2384
  }
2314
- return result;
2385
+ return chunks;
2315
2386
  }
2316
2387
  renderBinaryDocFallback(ctx, wrapper) {
2317
2388
  const card = document.createElement("div");
@@ -2328,14 +2399,14 @@ var DocxPlugin = class {
2328
2399
  const wordDocStream = cfbf.readStream("WordDocument");
2329
2400
  if (wordDocStream && wordDocStream.length >= 512) {
2330
2401
  const text2 = this.extractReadableStrings(wordDocStream);
2331
- card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2.sanitize(text2)}</div>`;
2402
+ card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify6.sanitize(text2)}</div>`;
2332
2403
  wrapper.appendChild(card);
2333
2404
  return;
2334
2405
  }
2335
2406
  } catch {
2336
2407
  }
2337
2408
  const text = this.extractReadableStrings(new Uint8Array(ctx.buffer));
2338
- card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2.sanitize(text)}</div>`;
2409
+ card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify6.sanitize(text)}</div>`;
2339
2410
  wrapper.appendChild(card);
2340
2411
  }
2341
2412
  extractReadableStrings(bytes) {
@@ -2934,41 +3005,94 @@ var CodePlugin = class {
2934
3005
  async render(ctx) {
2935
3006
  const decoder = new TextDecoder("utf-8");
2936
3007
  const fullText = decoder.decode(ctx.buffer);
2937
- const pageSplitRegex = /(?:\f|\x0C|(?:\r?\n|^)\s*[-=_]{3,}\s*(?:PAGE|Page|page break|Page Break)[\s\d\w-]*[-=_]{3,}\s*(?:\r?\n|$))/i;
2938
- const rawPages = fullText.split(pageSplitRegex).map((p) => p.trim()).filter((p) => p.length > 0);
3008
+ const extRaw = ctx.metadata.extension?.toLowerCase();
3009
+ const mimeRaw = ctx.metadata.mimeType?.toLowerCase();
3010
+ const isTxt = extRaw === ".txt" || mimeRaw === "text/plain" && (!extRaw || !this.extensions.filter((e) => e !== ".txt").includes(extRaw));
3011
+ let rawPages = [];
3012
+ if (isTxt) {
3013
+ const explicitPages = fullText.split(/(?:\f|\x0C)/);
3014
+ for (const ep of explicitPages) {
3015
+ const lines = ep.split(/\r?\n/);
3016
+ let currentChunk = [];
3017
+ for (let i = 0; i < lines.length; i++) {
3018
+ currentChunk.push(lines[i]);
3019
+ if (currentChunk.length >= 46) {
3020
+ rawPages.push(currentChunk.join("\n"));
3021
+ currentChunk = [];
3022
+ }
3023
+ }
3024
+ if (currentChunk.length > 0 || lines.length === 0) {
3025
+ rawPages.push(currentChunk.join("\n"));
3026
+ }
3027
+ }
3028
+ if (rawPages.length === 0) rawPages = [""];
3029
+ } else {
3030
+ const pageSplitRegex = /(?:\f|\x0C|(?:\r?\n|^)\s*[-=_]{3,}\s*(?:PAGE|Page|page break|Page Break)[\s\d\w-]*[-=_]{3,}\s*(?:\r?\n|$))/i;
3031
+ rawPages = fullText.split(pageSplitRegex).map((p) => p.trim()).filter((p) => p.length > 0);
3032
+ }
2939
3033
  const totalPages = Math.max(1, rawPages.length);
2940
3034
  let currentPage = 1;
2941
3035
  const container = document.createElement("div");
2942
3036
  container.style.width = "100%";
2943
3037
  container.style.height = "100%";
2944
3038
  container.style.overflow = "auto";
2945
- container.style.backgroundColor = "#1e1e1e";
2946
- container.style.color = "#d4d4d4";
2947
- container.style.padding = "16px";
2948
- container.style.boxSizing = "border-box";
2949
3039
  let fontSize = 13;
2950
3040
  let rotation = 0;
3041
+ let zoomLevel = 1;
2951
3042
  const pre = document.createElement("pre");
2952
- pre.style.margin = "0";
2953
- pre.style.fontFamily = "Consolas, Menlo, Monaco, monospace";
2954
- pre.style.fontSize = `${fontSize}px`;
2955
- pre.style.lineHeight = "1.5";
2956
- pre.style.whiteSpace = "pre-wrap";
2957
- pre.style.wordBreak = "break-all";
2958
3043
  const code = document.createElement("code");
3044
+ if (isTxt) {
3045
+ container.style.backgroundColor = "#f1f5f9";
3046
+ container.style.padding = "32px";
3047
+ container.style.boxSizing = "border-box";
3048
+ container.style.display = "flex";
3049
+ container.style.flexDirection = "column";
3050
+ container.style.alignItems = "center";
3051
+ pre.style.margin = "0";
3052
+ pre.style.fontFamily = "Consolas, 'Courier New', monospace";
3053
+ pre.style.fontSize = "13px";
3054
+ pre.style.color = "#1e293b";
3055
+ pre.style.lineHeight = "1.5";
3056
+ pre.style.whiteSpace = "pre-wrap";
3057
+ pre.style.wordBreak = "break-word";
3058
+ pre.style.backgroundColor = "#ffffff";
3059
+ pre.style.width = "816px";
3060
+ pre.style.minHeight = "1056px";
3061
+ pre.style.padding = "72px 56px";
3062
+ pre.style.boxSizing = "border-box";
3063
+ pre.style.boxShadow = "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)";
3064
+ pre.style.borderRadius = "4px";
3065
+ pre.style.transformOrigin = "top center";
3066
+ } else {
3067
+ container.style.backgroundColor = "#1e1e1e";
3068
+ container.style.color = "#d4d4d4";
3069
+ container.style.padding = "16px";
3070
+ container.style.boxSizing = "border-box";
3071
+ pre.style.margin = "0";
3072
+ pre.style.fontFamily = "Consolas, Menlo, Monaco, monospace";
3073
+ pre.style.fontSize = `${fontSize}px`;
3074
+ pre.style.lineHeight = "1.5";
3075
+ pre.style.whiteSpace = "pre-wrap";
3076
+ pre.style.wordBreak = "break-all";
3077
+ pre.style.transformOrigin = "top left";
3078
+ }
2959
3079
  const ext = (ctx.metadata.extension || "").replace(".", "");
2960
3080
  const renderCodePage = (text) => {
2961
- try {
2962
- if (ext && hljs.getLanguage(ext)) {
2963
- code.innerHTML = hljs.highlight(text, { language: ext }).value;
2964
- } else {
2965
- code.innerHTML = hljs.highlightAuto(text).value;
2966
- }
2967
- } catch {
3081
+ if (isTxt) {
2968
3082
  code.textContent = text;
3083
+ } else {
3084
+ try {
3085
+ if (ext && hljs.getLanguage(ext)) {
3086
+ code.innerHTML = hljs.highlight(text, { language: ext }).value;
3087
+ } else {
3088
+ code.innerHTML = hljs.highlightAuto(text).value;
3089
+ }
3090
+ } catch {
3091
+ code.textContent = text;
3092
+ }
2969
3093
  }
2970
3094
  };
2971
- renderCodePage(rawPages[0] || fullText);
3095
+ renderCodePage(rawPages[0] || (isTxt ? "" : fullText));
2972
3096
  pre.appendChild(code);
2973
3097
  container.appendChild(pre);
2974
3098
  let indicator = null;
@@ -2977,15 +3101,22 @@ var CodePlugin = class {
2977
3101
  indicator.className = "fp-code-page-indicator";
2978
3102
  indicator.style.position = "sticky";
2979
3103
  indicator.style.bottom = "16px";
2980
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
2981
- indicator.style.backdropFilter = "blur(8px)";
2982
- indicator.style.color = "#f8fafc";
3104
+ if (isTxt) {
3105
+ indicator.style.backgroundColor = "rgba(255, 255, 255, 0.9)";
3106
+ indicator.style.color = "#334155";
3107
+ indicator.style.border = "1px solid #e2e8f0";
3108
+ indicator.style.boxShadow = "0 1px 3px rgba(0,0,0,0.1)";
3109
+ } else {
3110
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
3111
+ indicator.style.color = "#f8fafc";
3112
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
3113
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
3114
+ indicator.style.backdropFilter = "blur(8px)";
3115
+ }
2983
3116
  indicator.style.fontSize = "12px";
2984
3117
  indicator.style.fontWeight = "600";
2985
3118
  indicator.style.padding = "5px 14px";
2986
3119
  indicator.style.borderRadius = "20px";
2987
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
2988
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
2989
3120
  indicator.style.zIndex = "10";
2990
3121
  indicator.style.userSelect = "none";
2991
3122
  indicator.style.pointerEvents = "none";
@@ -2996,7 +3127,7 @@ var CodePlugin = class {
2996
3127
  }
2997
3128
  const showPage = (pageNum) => {
2998
3129
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
2999
- renderCodePage(rawPages[currentPage - 1] || fullText);
3130
+ renderCodePage(rawPages[currentPage - 1] || (isTxt ? "" : fullText));
3000
3131
  if (indicator) {
3001
3132
  indicator.textContent = `Page ${currentPage} of ${totalPages}`;
3002
3133
  }
@@ -3012,39 +3143,57 @@ var CodePlugin = class {
3012
3143
  ctx.container.innerHTML = "";
3013
3144
  };
3014
3145
  ctx.signal.addEventListener("abort", cleanup);
3146
+ const updateTransform = () => {
3147
+ if (isTxt) {
3148
+ pre.style.transform = `scale(${zoomLevel}) rotate(${rotation}deg)`;
3149
+ } else {
3150
+ pre.style.transform = `rotate(${rotation}deg)`;
3151
+ pre.style.fontSize = `${fontSize}px`;
3152
+ }
3153
+ };
3015
3154
  return {
3016
3155
  destroy: cleanup,
3017
3156
  getPageCount: () => totalPages,
3018
3157
  getCurrentPage: () => currentPage,
3019
3158
  goToPage: (page) => showPage(page),
3020
3159
  zoomIn: () => {
3021
- fontSize = Math.min(32, fontSize + 2);
3022
- pre.style.fontSize = `${fontSize}px`;
3160
+ if (isTxt) {
3161
+ zoomLevel = Math.min(3, zoomLevel + 0.1);
3162
+ } else {
3163
+ fontSize = Math.min(32, fontSize + 2);
3164
+ }
3165
+ updateTransform();
3023
3166
  },
3024
3167
  zoomOut: () => {
3025
- fontSize = Math.max(8, fontSize - 2);
3026
- pre.style.fontSize = `${fontSize}px`;
3168
+ if (isTxt) {
3169
+ zoomLevel = Math.max(0.1, zoomLevel - 0.1);
3170
+ } else {
3171
+ fontSize = Math.max(8, fontSize - 2);
3172
+ }
3173
+ updateTransform();
3027
3174
  },
3028
- getZoom: () => fontSize / 13,
3175
+ getZoom: () => isTxt ? zoomLevel : fontSize / 13,
3029
3176
  setZoom: (level) => {
3030
- fontSize = Math.round(13 * level);
3031
- pre.style.fontSize = `${fontSize}px`;
3177
+ if (isTxt) {
3178
+ zoomLevel = level;
3179
+ } else {
3180
+ fontSize = Math.round(13 * level);
3181
+ }
3182
+ updateTransform();
3032
3183
  },
3033
3184
  fitToPage: () => {
3034
3185
  fontSize = 13;
3035
3186
  rotation = 0;
3036
- pre.style.fontSize = "13px";
3037
- pre.style.transform = "none";
3187
+ zoomLevel = 1;
3188
+ updateTransform();
3038
3189
  },
3039
3190
  rotateCW: () => {
3040
3191
  rotation = (rotation + 90) % 360;
3041
- pre.style.transform = `rotate(${rotation}deg)`;
3042
- pre.style.transformOrigin = "top left";
3192
+ updateTransform();
3043
3193
  },
3044
3194
  rotateCCW: () => {
3045
3195
  rotation = (rotation - 90 + 360) % 360;
3046
- pre.style.transform = `rotate(${rotation}deg)`;
3047
- pre.style.transformOrigin = "top left";
3196
+ updateTransform();
3048
3197
  },
3049
3198
  download: () => {
3050
3199
  const mimeType = ctx.metadata.mimeType || "text/plain";
@@ -3343,7 +3492,7 @@ var MarkdownPlugin = class {
3343
3492
  gfm: true,
3344
3493
  breaks: true
3345
3494
  });
3346
- const cleanHtml = DOMPurify2.sanitize(rawHtml, {
3495
+ const cleanHtml = DOMPurify6.sanitize(rawHtml, {
3347
3496
  USE_PROFILES: { html: true }
3348
3497
  });
3349
3498
  const wrapper = document.createElement("div");
@@ -3979,6 +4128,14 @@ var RtfPlugin = class {
3979
4128
  group: "actions",
3980
4129
  execute: () => instance.download?.()
3981
4130
  },
4131
+ {
4132
+ id: "copy",
4133
+ icon: "copy",
4134
+ label: "Copy Text",
4135
+ type: "button",
4136
+ group: "actions",
4137
+ execute: () => instance.copy?.()
4138
+ },
3982
4139
  {
3983
4140
  id: "print",
3984
4141
  icon: "print",
@@ -4040,14 +4197,31 @@ var RtfPlugin = class {
4040
4197
  } catch (err) {
4041
4198
  console.warn("[RtfPlugin] RTF render error, fallback text:", err);
4042
4199
  const text = new TextDecoder("latin1").decode(ctx.buffer);
4043
- const clean = text.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "");
4044
- const pre = document.createElement("pre");
4045
- pre.style.whiteSpace = "pre-wrap";
4046
- pre.style.fontFamily = "serif";
4047
- pre.style.color = "#333";
4048
- pre.textContent = clean;
4049
- wrapper.appendChild(pre);
4050
- pageElements = [pre];
4200
+ const rawPages = text.split(/\\page\b/).map((segment) => {
4201
+ return segment.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim();
4202
+ }).filter((p) => p.length > 0);
4203
+ const pages = rawPages.length > 0 ? rawPages : [text.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim()];
4204
+ for (let i = 0; i < pages.length; i++) {
4205
+ const pageCard = document.createElement("div");
4206
+ pageCard.className = "fp-rtf-page-card";
4207
+ pageCard.style.backgroundColor = "#ffffff";
4208
+ pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4209
+ pageCard.style.borderRadius = "4px";
4210
+ pageCard.style.padding = "72px 56px";
4211
+ pageCard.style.width = "816px";
4212
+ pageCard.style.minHeight = "1056px";
4213
+ pageCard.style.maxWidth = "100%";
4214
+ pageCard.style.boxSizing = "border-box";
4215
+ pageCard.style.fontFamily = "serif";
4216
+ pageCard.style.fontSize = "12pt";
4217
+ pageCard.style.lineHeight = "1.6";
4218
+ pageCard.style.color = "#1e293b";
4219
+ pageCard.style.whiteSpace = "pre-wrap";
4220
+ pageCard.style.display = i === 0 ? "block" : "none";
4221
+ pageCard.textContent = pages[i];
4222
+ wrapper.appendChild(pageCard);
4223
+ pageElements.push(pageCard);
4224
+ }
4051
4225
  }
4052
4226
  const totalPages = Math.max(1, pageElements.length);
4053
4227
  let currentPage = 1;
@@ -4135,6 +4309,10 @@ var RtfPlugin = class {
4135
4309
  a.click();
4136
4310
  URL.revokeObjectURL(url);
4137
4311
  },
4312
+ copy: () => {
4313
+ const text = wrapper.textContent || "";
4314
+ navigator.clipboard?.writeText(text);
4315
+ },
4138
4316
  print: () => {
4139
4317
  window.print();
4140
4318
  }
@@ -4209,7 +4387,7 @@ var HtmlPreviewPlugin = class {
4209
4387
  }
4210
4388
  async render(ctx) {
4211
4389
  const rawHtml = new TextDecoder("utf-8").decode(ctx.buffer);
4212
- const sanitized = DOMPurify2.sanitize(rawHtml, {
4390
+ const sanitized = DOMPurify6.sanitize(rawHtml, {
4213
4391
  WHOLE_DOCUMENT: true,
4214
4392
  ADD_TAGS: ["style", "link"],
4215
4393
  ADD_ATTR: ["target", "rel"]
@@ -4475,14 +4653,97 @@ var OpenDocumentPlugin = class {
4475
4653
  wrapper.textContent = contentDoc.documentElement.textContent || "Formula content";
4476
4654
  }
4477
4655
  } else {
4478
- wrapper.style.maxWidth = "850px";
4479
- wrapper.style.padding = "48px";
4656
+ wrapper.style.maxWidth = "none";
4657
+ wrapper.style.padding = "0";
4480
4658
  wrapper.style.minHeight = "100%";
4659
+ wrapper.style.backgroundColor = "transparent";
4660
+ wrapper.style.boxShadow = "none";
4661
+ wrapper.style.position = "relative";
4662
+ const dims = this.getPageDimensions(stylesDoc, contentDoc);
4481
4663
  const bodyHtml = this.renderOdfBody(contentDoc, styleMap, imageUrls);
4482
- wrapper.innerHTML = DOMPurify2.sanitize(bodyHtml, {
4483
- ADD_TAGS: ["math", "semantics", "mrow", "mi", "mo", "mn", "msup", "msub"],
4664
+ const tempDiv = document.createElement("div");
4665
+ tempDiv.style.width = `${dims.width - dims.marginLeft - dims.marginRight}px`;
4666
+ tempDiv.style.position = "absolute";
4667
+ tempDiv.style.visibility = "hidden";
4668
+ tempDiv.innerHTML = DOMPurify6.sanitize(bodyHtml, {
4669
+ ADD_TAGS: ["math", "semantics", "mrow", "mi", "mo", "mn", "msup", "msub", "hr"],
4484
4670
  ADD_ATTR: ["style", "colspan", "rowspan"]
4485
4671
  });
4672
+ document.body.appendChild(tempDiv);
4673
+ const contentHeight = dims.height - dims.marginTop - dims.marginBottom;
4674
+ const pageElements = [[]];
4675
+ let currentHeight = 0;
4676
+ let currentPageIdx = 0;
4677
+ Array.from(tempDiv.children).forEach((child) => {
4678
+ const el = child;
4679
+ const style = el.getAttribute("style") || "";
4680
+ const isBreakBefore = style.includes("page-break-before: always");
4681
+ const isBreakAfter = style.includes("page-break-after: always");
4682
+ const isSoftBreak = el.classList.contains("odf-page-break");
4683
+ if (isBreakBefore) {
4684
+ if (pageElements[currentPageIdx].length > 0) {
4685
+ currentPageIdx++;
4686
+ pageElements.push([]);
4687
+ currentHeight = 0;
4688
+ }
4689
+ }
4690
+ const h = el.offsetHeight || 0;
4691
+ if (currentHeight + h > contentHeight && pageElements[currentPageIdx].length > 0 && !isSoftBreak) {
4692
+ currentPageIdx++;
4693
+ pageElements.push([]);
4694
+ currentHeight = 0;
4695
+ }
4696
+ if (!isSoftBreak) {
4697
+ pageElements[currentPageIdx].push(el.cloneNode(true));
4698
+ currentHeight += h;
4699
+ }
4700
+ if (isBreakAfter || isSoftBreak) {
4701
+ currentPageIdx++;
4702
+ pageElements.push([]);
4703
+ currentHeight = 0;
4704
+ }
4705
+ });
4706
+ document.body.removeChild(tempDiv);
4707
+ if (pageElements.length > 1 && pageElements[pageElements.length - 1].length === 0) {
4708
+ pageElements.pop();
4709
+ }
4710
+ totalPages = Math.max(1, pageElements.length);
4711
+ pageElements.forEach((elements, idx) => {
4712
+ const page = document.createElement("div");
4713
+ page.className = `fp-odt-page fp-odt-page-${idx + 1}`;
4714
+ page.style.width = `${dims.width}px`;
4715
+ page.style.minHeight = `${dims.height}px`;
4716
+ page.style.padding = `${dims.marginTop}px ${dims.marginRight}px ${dims.marginBottom}px ${dims.marginLeft}px`;
4717
+ page.style.margin = "0 auto";
4718
+ page.style.backgroundColor = "#ffffff";
4719
+ page.style.boxShadow = "0 2px 10px rgba(0,0,0,0.08)";
4720
+ page.style.borderRadius = "4px";
4721
+ page.style.boxSizing = "border-box";
4722
+ page.style.display = idx === 0 ? "block" : "none";
4723
+ page.style.position = "absolute";
4724
+ page.style.top = "0";
4725
+ page.style.left = "50%";
4726
+ page.style.transform = "translateX(-50%)";
4727
+ elements.forEach((el) => page.appendChild(el));
4728
+ wrapper.appendChild(page);
4729
+ slides.push(page);
4730
+ });
4731
+ const pageIndicator = document.createElement("div");
4732
+ pageIndicator.className = "fp-odt-page-indicator";
4733
+ pageIndicator.style.position = "sticky";
4734
+ pageIndicator.style.bottom = "16px";
4735
+ pageIndicator.style.left = "50%";
4736
+ pageIndicator.style.transform = "translateX(-50%)";
4737
+ pageIndicator.style.backgroundColor = "rgba(0, 0, 0, 0.6)";
4738
+ pageIndicator.style.color = "#fff";
4739
+ pageIndicator.style.padding = "6px 12px";
4740
+ pageIndicator.style.borderRadius = "16px";
4741
+ pageIndicator.style.fontSize = "12px";
4742
+ pageIndicator.style.zIndex = "100";
4743
+ pageIndicator.style.display = "inline-block";
4744
+ pageIndicator.style.width = "fit-content";
4745
+ pageIndicator.textContent = `Page 1 of ${totalPages}`;
4746
+ container.appendChild(pageIndicator);
4486
4747
  }
4487
4748
  const cleanup = () => {
4488
4749
  imageUrls.forEach((url) => URL.revokeObjectURL(url));
@@ -4491,11 +4752,15 @@ var OpenDocumentPlugin = class {
4491
4752
  };
4492
4753
  ctx.signal.addEventListener("abort", cleanup);
4493
4754
  const goToPage = (page) => {
4494
- if (!isPresentation || page < 1 || page > totalPages) return;
4755
+ if (page < 1 || page > totalPages) return;
4495
4756
  currentPage = page;
4496
4757
  slides.forEach((s, idx) => {
4497
4758
  s.style.display = idx === page - 1 ? "block" : "none";
4498
4759
  });
4760
+ const indicator = container.querySelector(".fp-odt-page-indicator");
4761
+ if (indicator) {
4762
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4763
+ }
4499
4764
  ctx.emit("page-change", { page: currentPage, total: totalPages });
4500
4765
  };
4501
4766
  return {
@@ -4564,6 +4829,43 @@ var OpenDocumentPlugin = class {
4564
4829
  }
4565
4830
  };
4566
4831
  }
4832
+ getPageDimensions(stylesDoc, contentDoc) {
4833
+ let width = 850;
4834
+ let height = 1123;
4835
+ let marginTop = 48;
4836
+ let marginBottom = 48;
4837
+ let marginLeft = 48;
4838
+ let marginRight = 48;
4839
+ const parseUnit = (val) => {
4840
+ if (!val) return null;
4841
+ if (val.endsWith("cm")) return parseFloat(val) * 37.8;
4842
+ if (val.endsWith("mm")) return parseFloat(val) * 3.78;
4843
+ if (val.endsWith("in")) return parseFloat(val) * 96;
4844
+ if (val.endsWith("pt")) return parseFloat(val) * 1.33;
4845
+ if (val.endsWith("px")) return parseFloat(val);
4846
+ return parseFloat(val);
4847
+ };
4848
+ const docs = [stylesDoc, contentDoc].filter(Boolean);
4849
+ for (const doc of docs) {
4850
+ const pageLayout = doc.querySelector("page-layout-properties, [page-width]");
4851
+ if (pageLayout) {
4852
+ const w = parseUnit(pageLayout.getAttribute("fo:page-width") || pageLayout.getAttribute("page-width"));
4853
+ const h = parseUnit(pageLayout.getAttribute("fo:page-height") || pageLayout.getAttribute("page-height"));
4854
+ const mt = parseUnit(pageLayout.getAttribute("fo:margin-top") || pageLayout.getAttribute("margin-top"));
4855
+ const mb = parseUnit(pageLayout.getAttribute("fo:margin-bottom") || pageLayout.getAttribute("margin-bottom"));
4856
+ const ml = parseUnit(pageLayout.getAttribute("fo:margin-left") || pageLayout.getAttribute("margin-left"));
4857
+ const mr = parseUnit(pageLayout.getAttribute("fo:margin-right") || pageLayout.getAttribute("margin-right"));
4858
+ if (w !== null) width = w;
4859
+ if (h !== null) height = h;
4860
+ if (mt !== null) marginTop = mt;
4861
+ if (mb !== null) marginBottom = mb;
4862
+ if (ml !== null) marginLeft = ml;
4863
+ if (mr !== null) marginRight = mr;
4864
+ break;
4865
+ }
4866
+ }
4867
+ return { width, height, marginTop, marginBottom, marginLeft, marginRight };
4868
+ }
4567
4869
  extractStyles(stylesDoc, contentDoc) {
4568
4870
  const map = /* @__PURE__ */ new Map();
4569
4871
  const styleNodes = [];
@@ -4586,14 +4888,21 @@ var OpenDocumentPlugin = class {
4586
4888
  if (color) css += `color: ${color}; `;
4587
4889
  if (size) css += `font-size: ${size}; `;
4588
4890
  }
4589
- const paraProp = node.querySelector("paragraph-properties, [text-align]");
4891
+ let paraProp = node.querySelector("paragraph-properties, [text-align]");
4892
+ if (!paraProp) {
4893
+ paraProp = Array.from(node.children).find((c) => c.tagName.includes("paragraph-properties")) || null;
4894
+ }
4590
4895
  if (paraProp) {
4591
4896
  const align = paraProp.getAttribute("fo:text-align") || paraProp.getAttribute("text-align");
4592
4897
  const mt = paraProp.getAttribute("fo:margin-top") || paraProp.getAttribute("margin-top");
4593
4898
  const mb = paraProp.getAttribute("fo:margin-bottom") || paraProp.getAttribute("margin-bottom");
4899
+ const breakBefore = paraProp.getAttribute("fo:break-before") || paraProp.getAttribute("break-before");
4900
+ const breakAfter = paraProp.getAttribute("fo:break-after") || paraProp.getAttribute("break-after");
4594
4901
  if (align) css += `text-align: ${align}; `;
4595
4902
  if (mt) css += `margin-top: ${mt}; `;
4596
4903
  if (mb) css += `margin-bottom: ${mb}; `;
4904
+ if (breakBefore === "page") css += "page-break-before: always; ";
4905
+ if (breakAfter === "page") css += "page-break-after: always; ";
4597
4906
  }
4598
4907
  if (css) map.set(name, css);
4599
4908
  }
@@ -4678,6 +4987,8 @@ var OpenDocumentPlugin = class {
4678
4987
  result += "&emsp;";
4679
4988
  } else if (tag === "line-break") {
4680
4989
  result += "<br/>";
4990
+ } else if (tag === "soft-page-break") {
4991
+ result += '<hr class="odf-page-break" style="page-break-after: always; border: none; margin: 0; padding: 0; height: 0;" />';
4681
4992
  } else {
4682
4993
  result += el.textContent || "";
4683
4994
  }
@@ -4838,20 +5149,9 @@ var DocPlugin = class {
4838
5149
  container.style.overflow = "auto";
4839
5150
  container.style.padding = "32px 16px";
4840
5151
  container.style.backgroundColor = "#f1f5f9";
4841
- const wrapper = document.createElement("div");
4842
- wrapper.className = "fp-doc-wrapper";
4843
- wrapper.style.maxWidth = "850px";
4844
- wrapper.style.margin = "0 auto";
4845
- wrapper.style.backgroundColor = "#ffffff";
4846
- wrapper.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4847
- wrapper.style.borderRadius = "4px";
4848
- wrapper.style.padding = "56px 48px";
4849
- wrapper.style.minHeight = "100%";
4850
- wrapper.style.transformOrigin = "top center";
4851
- wrapper.style.transition = "transform 0.2s ease";
4852
- wrapper.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
4853
- wrapper.style.color = "#1e293b";
4854
- container.appendChild(wrapper);
5152
+ container.style.display = "flex";
5153
+ container.style.justifyContent = "center";
5154
+ container.style.alignItems = "flex-start";
4855
5155
  ctx.container.appendChild(container);
4856
5156
  let scale = 1;
4857
5157
  let extractedRawText = "";
@@ -4878,21 +5178,27 @@ var DocPlugin = class {
4878
5178
  const rawPages = this.splitIntoPages(extractedRawText);
4879
5179
  const totalPages = Math.max(1, rawPages.length);
4880
5180
  let currentPage = 1;
4881
- wrapper.innerHTML = "";
4882
5181
  const pageCards = [];
4883
5182
  for (let i = 0; i < totalPages; i++) {
4884
5183
  const pageCard = document.createElement("div");
4885
5184
  pageCard.className = "fp-doc-page-card";
5185
+ pageCard.style.width = "816px";
5186
+ pageCard.style.height = "1056px";
4886
5187
  pageCard.style.backgroundColor = "#ffffff";
4887
5188
  pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4888
5189
  pageCard.style.borderRadius = "4px";
4889
- pageCard.style.padding = "56px 48px";
4890
- pageCard.style.minHeight = "100%";
5190
+ pageCard.style.padding = "96px 72px";
5191
+ pageCard.style.boxSizing = "border-box";
5192
+ pageCard.style.overflow = "hidden";
4891
5193
  pageCard.style.display = i === 0 ? "block" : "none";
5194
+ pageCard.style.transformOrigin = "top center";
5195
+ pageCard.style.transition = "transform 0.2s ease";
5196
+ pageCard.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
5197
+ pageCard.style.color = "#1e293b";
4892
5198
  if (isFallback && i === 0) {
4893
5199
  pageCard.innerHTML = `
4894
5200
  <div style="border-bottom: 1px solid #e2e8f0; padding-bottom: 12px; margin-bottom: 24px;">
4895
- <h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${DOMPurify2.sanitize(ctx.metadata.name || "Word Document (.doc)")}</h2>
5201
+ <h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${DOMPurify6.sanitize(ctx.metadata.name || "Word Document (.doc)")}</h2>
4896
5202
  <span style="font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px;">Legacy Word 97-2003 Binary Preview</span>
4897
5203
  </div>
4898
5204
  ${this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document")}
@@ -4900,15 +5206,17 @@ var DocPlugin = class {
4900
5206
  } else {
4901
5207
  pageCard.innerHTML = this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document");
4902
5208
  }
4903
- wrapper.appendChild(pageCard);
5209
+ container.appendChild(pageCard);
4904
5210
  pageCards.push(pageCard);
4905
5211
  }
4906
5212
  let indicator = null;
4907
5213
  if (totalPages > 1) {
4908
5214
  indicator = document.createElement("div");
4909
5215
  indicator.className = "fp-doc-page-indicator";
4910
- indicator.style.position = "sticky";
5216
+ indicator.style.position = "fixed";
4911
5217
  indicator.style.bottom = "16px";
5218
+ indicator.style.left = "50%";
5219
+ indicator.style.transform = "translateX(-50%)";
4912
5220
  indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
4913
5221
  indicator.style.backdropFilter = "blur(8px)";
4914
5222
  indicator.style.color = "#f8fafc";
@@ -4922,17 +5230,25 @@ var DocPlugin = class {
4922
5230
  indicator.style.userSelect = "none";
4923
5231
  indicator.style.pointerEvents = "none";
4924
5232
  indicator.style.textAlign = "center";
4925
- indicator.style.width = "fit-content";
4926
- indicator.style.margin = "16px auto 0";
4927
5233
  container.appendChild(indicator);
4928
5234
  }
5235
+ let rotation = 0;
5236
+ const applyTransform = () => {
5237
+ const activeCard = pageCards[currentPage - 1];
5238
+ if (activeCard) {
5239
+ activeCard.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5240
+ }
5241
+ };
4929
5242
  const showPage = (pageNum) => {
4930
5243
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
4931
- if (totalPages > 1) {
4932
- pageCards.forEach((card, idx) => {
4933
- card.style.display = idx + 1 === currentPage ? "block" : "none";
4934
- });
4935
- }
5244
+ pageCards.forEach((card, idx) => {
5245
+ if (idx + 1 === currentPage) {
5246
+ card.style.display = "block";
5247
+ card.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5248
+ } else {
5249
+ card.style.display = "none";
5250
+ }
5251
+ });
4936
5252
  if (indicator) {
4937
5253
  indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4938
5254
  }
@@ -4941,19 +5257,13 @@ var DocPlugin = class {
4941
5257
  if (totalPages > 1) {
4942
5258
  showPage(1);
4943
5259
  }
4944
- let rotation = 0;
4945
5260
  const calculateFitScale = () => {
4946
- const activeCard = pageCards[currentPage - 1] || wrapper;
4947
- const elW = activeCard.offsetWidth || 850;
4948
- const elH = activeCard.offsetHeight || 1e3;
5261
+ const elW = 816;
5262
+ const elH = 1056;
4949
5263
  const availW = Math.max(200, ctx.container.clientWidth - 48);
4950
5264
  const availH = Math.max(200, ctx.container.clientHeight - 80);
4951
5265
  return Math.min(1.1, Math.min(availW / elW, availH / elH));
4952
5266
  };
4953
- const applyTransform = () => {
4954
- wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
4955
- wrapper.style.transformOrigin = "top center";
4956
- };
4957
5267
  setTimeout(() => {
4958
5268
  scale = calculateFitScale();
4959
5269
  applyTransform();
@@ -5111,8 +5421,28 @@ var DocPlugin = class {
5111
5421
  }
5112
5422
  splitIntoPages(text) {
5113
5423
  if (!text) return [""];
5114
- const parts = text.split(/[\x0C\f]|\r?\n\s*[-=_]{3,}\s*(?:PAGE|Page|page break)[\s\d\w-]*[-=_]{3,}\s*\r?\n/i).map((p) => p.trim()).filter((p) => p.length > 0);
5115
- return parts.length > 0 ? parts : [text];
5424
+ const explicitParts = text.split(/[\x0C\f]|\r?\n\s*[-=_]{3,}\s*(?:PAGE|Page|page break)[\s\d\w-]*[-=_]{3,}\s*\r?\n/i).map((p) => p.trim()).filter((p) => p.length > 0);
5425
+ if (explicitParts.length === 0) explicitParts.push(text);
5426
+ const maxLinesPerPage = 48;
5427
+ const finalPages = [];
5428
+ for (const part of explicitParts) {
5429
+ const lines = part.split(/\r?\n/);
5430
+ let currentLines = [];
5431
+ let count = 0;
5432
+ for (const line of lines) {
5433
+ currentLines.push(line);
5434
+ count++;
5435
+ if (count >= maxLinesPerPage) {
5436
+ finalPages.push(currentLines.join("\n"));
5437
+ currentLines = [];
5438
+ count = 0;
5439
+ }
5440
+ }
5441
+ if (currentLines.length > 0) {
5442
+ finalPages.push(currentLines.join("\n"));
5443
+ }
5444
+ }
5445
+ return finalPages.length > 0 ? finalPages : [text];
5116
5446
  }
5117
5447
  /**
5118
5448
  * Format cleaned extracted text into attractive HTML paragraphs, headings, and lists
@@ -5121,17 +5451,65 @@ var DocPlugin = class {
5121
5451
  const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").split("\n");
5122
5452
  let html = "";
5123
5453
  let inList = false;
5124
- for (const rawLine of lines) {
5125
- const line = rawLine.trim();
5454
+ let tableLines = [];
5455
+ const flushTable = () => {
5456
+ if (tableLines.length > 0) {
5457
+ html += '<table style="width: 100%; border-collapse: collapse; margin: 16px 0; font-size: 12px;">';
5458
+ for (const tLine of tableLines) {
5459
+ html += "<tr>";
5460
+ const cols = tLine.split(" ");
5461
+ for (const col of cols) {
5462
+ html += `<td style="border: 1px solid #cbd5e1; padding: 6px 8px;">${DOMPurify6.sanitize(col.trim())}</td>`;
5463
+ }
5464
+ html += "</tr>";
5465
+ }
5466
+ html += "</table>";
5467
+ tableLines = [];
5468
+ }
5469
+ };
5470
+ let i = 0;
5471
+ while (i < lines.length) {
5472
+ let line = lines[i];
5473
+ let tabCount = (line.match(/\t/g) || []).length;
5474
+ if (tabCount > 0) {
5475
+ let consecutiveTableLines = 1;
5476
+ let j = i + 1;
5477
+ while (j < lines.length) {
5478
+ const nextTabCount = (lines[j].match(/\t/g) || []).length;
5479
+ if (nextTabCount === tabCount) {
5480
+ consecutiveTableLines++;
5481
+ j++;
5482
+ } else {
5483
+ break;
5484
+ }
5485
+ }
5486
+ if (consecutiveTableLines >= 3) {
5487
+ if (inList) {
5488
+ html += "</ul>";
5489
+ inList = false;
5490
+ }
5491
+ tableLines = lines.slice(i, j);
5492
+ flushTable();
5493
+ i = j;
5494
+ continue;
5495
+ }
5496
+ }
5497
+ line = line.trim();
5126
5498
  if (!line) {
5127
5499
  if (inList) {
5128
5500
  html += "</ul>";
5129
5501
  inList = false;
5130
5502
  }
5503
+ html += '<div style="height: 1.15em;"></div>';
5504
+ i++;
5505
+ continue;
5506
+ }
5507
+ if (/^[\x00-\x1F\x7F-\x9F]+$/.test(line)) {
5508
+ i++;
5131
5509
  continue;
5132
5510
  }
5133
- if (/^[\x00-\x1F\x7F-\x9F]+$/.test(line)) continue;
5134
- if (line.includes("Normal.dot") || line.includes("Microsoft Word") || line.includes("Times New Roman") && line.length < 30) {
5511
+ if ((line.includes("Normal.dot") || line.includes("Microsoft Word") || line.includes("Times New Roman")) && line.length < 30) {
5512
+ i++;
5135
5513
  continue;
5136
5514
  }
5137
5515
  if (line.length < 60 && !line.endsWith(".") && (/^[A-Z0-9\s:_-]+$/.test(line) || line.startsWith("#"))) {
@@ -5139,24 +5517,26 @@ var DocPlugin = class {
5139
5517
  html += "</ul>";
5140
5518
  inList = false;
5141
5519
  }
5142
- html += `<h2 style="font-size: 18px; font-weight: 700; color: #1e3a8a; margin: 20px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify2.sanitize(line)}</h2>`;
5520
+ html += `<h2 style="font-size: 16px; font-weight: 700; color: #1e3a8a; margin: 16px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify6.sanitize(line)}</h2>`;
5143
5521
  } else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
5144
5522
  if (!inList) {
5145
5523
  html += '<ul style="margin: 8px 0; padding-left: 24px;">';
5146
5524
  inList = true;
5147
5525
  }
5148
5526
  const bulletText = line.replace(/^[•\-\*]\s*/, "");
5149
- html += `<li style="margin: 4px 0; line-height: 1.6;">${DOMPurify2.sanitize(bulletText)}</li>`;
5527
+ html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6.sanitize(bulletText)}</li>`;
5150
5528
  } else {
5151
5529
  if (inList) {
5152
5530
  html += "</ul>";
5153
5531
  inList = false;
5154
5532
  }
5155
- html += `<p style="line-height: 1.7; margin: 10px 0; font-size: 14px; text-align: justify;">${DOMPurify2.sanitize(line)}</p>`;
5533
+ html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6.sanitize(line)}</p>`;
5156
5534
  }
5535
+ i++;
5157
5536
  }
5158
5537
  if (inList) html += "</ul>";
5159
- return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify2.sanitize(filename)})</p>`;
5538
+ flushTable();
5539
+ return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify6.sanitize(filename)})</p>`;
5160
5540
  }
5161
5541
  };
5162
5542
  function docPlugin() {
@@ -5334,7 +5714,7 @@ var PptPlugin = class {
5334
5714
  if (s.pictureUrl) {
5335
5715
  contentHtml = `
5336
5716
  <div style="flex: 1; display: flex; justify-content: center; align-items: center; padding: 12px; overflow: hidden;">
5337
- <img src="${s.pictureUrl}" alt="${DOMPurify2.sanitize(s.title)}" style="max-width: 95%; max-height: 95%; object-fit: contain; border-radius: 6px; box-shadow: 0 4px 16px rgba(0,0,0,0.1); background: #ffffff;" />
5717
+ <img src="${s.pictureUrl}" alt="${DOMPurify6.sanitize(s.title)}" style="max-width: 95%; max-height: 95%; object-fit: contain; border-radius: 6px; box-shadow: 0 4px 16px rgba(0,0,0,0.1); background: #ffffff;" />
5338
5718
  </div>
5339
5719
  `;
5340
5720
  } else if (s.tableColumns.length > 0) {
@@ -5344,7 +5724,7 @@ var PptPlugin = class {
5344
5724
  <table style="width: 100%; border-collapse: collapse; border: 1px solid #cbd5e1; border-radius: 6px; overflow: hidden; background: #ffffff;">
5345
5725
  <thead>
5346
5726
  <tr style="background: #e2e8f0; color: #1e293b; font-weight: 600; font-size: 14px;">
5347
- ${cols.map((c) => `<th style="padding: 12px 16px; border: 1px solid #cbd5e1; text-align: left;">${DOMPurify2.sanitize(c)}</th>`).join("")}
5727
+ ${cols.map((c) => `<th style="padding: 12px 16px; border: 1px solid #cbd5e1; text-align: left;">${DOMPurify6.sanitize(c)}</th>`).join("")}
5348
5728
  </tr>
5349
5729
  </thead>
5350
5730
  <tbody>
@@ -5360,7 +5740,7 @@ var PptPlugin = class {
5360
5740
  } else {
5361
5741
  const pTags = s.paragraphs.map((p) => {
5362
5742
  const lines = p.split(/[\r\n]+/).map((l) => l.trim()).filter(Boolean);
5363
- return lines.map((l) => `<p style="margin: 0 0 14px; font-size: 14px; line-height: 1.65; color: #334155; text-align: justify;">${DOMPurify2.sanitize(l)}</p>`).join("");
5743
+ return lines.map((l) => `<p style="margin: 0 0 14px; font-size: 14px; line-height: 1.65; color: #334155; text-align: justify;">${DOMPurify6.sanitize(l)}</p>`).join("");
5364
5744
  }).join("");
5365
5745
  contentHtml = `
5366
5746
  <div style="flex: 1; overflow: auto; padding: 4px 8px; display: flex; flex-direction: column; justify-content: flex-start;">
@@ -5373,11 +5753,11 @@ var PptPlugin = class {
5373
5753
  <!-- Header Banner matching PowerPoint design -->
5374
5754
  <div style="background: linear-gradient(90deg, #a3e635 0%, #84cc16 100%); padding: 12px 24px; border-radius: 4px; display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); flex-shrink: 0;">
5375
5755
  <h1 style="margin: 0; font-size: 26px; font-weight: 700; color: #1e293b; letter-spacing: -0.3px;">
5376
- ${DOMPurify2.sanitize(s.title)}
5756
+ ${DOMPurify6.sanitize(s.title)}
5377
5757
  </h1>
5378
5758
  ${s.subtitle ? `
5379
5759
  <span style="background: #38bdf8; color: #ffffff; padding: 4px 14px; border-radius: 4px; font-weight: 700; font-size: 13px; letter-spacing: 0.5px; box-shadow: 0 1px 4px rgba(0,0,0,0.15);">
5380
- ${DOMPurify2.sanitize(s.subtitle)}
5760
+ ${DOMPurify6.sanitize(s.subtitle)}
5381
5761
  </span>
5382
5762
  ` : `
5383
5763
  <span style="font-size: 12px; color: #365314; font-weight: 600;">