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

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
  }
@@ -434,7 +434,8 @@ function createElement(tag, attrs, ...children) {
434
434
  }
435
435
  var ICON_ZOOM_IN = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line><line x1="11" y1="8" x2="11" y2="14"></line><line x1="8" y1="11" x2="14" y2="11"></line></svg>`;
436
436
  var ICON_ZOOM_OUT = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line><line x1="8" y1="11" x2="14" y2="11"></line></svg>`;
437
- var ICON_FIT_PAGE = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" y1="3" x2="14" y2="10"></line><line x1="3" y1="21" x2="10" y2="14"></line></svg>`;
437
+ var ICON_FIT_PAGE = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"></rect><line x1="8" y1="12" x2="16" y2="12"></line><polyline points="11 9 8 12 11 15"></polyline><polyline points="13 9 16 12 13 15"></polyline></svg>`;
438
+ var ICON_FIT_WIDTH = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="2" y1="12" x2="22" y2="12"></line><polyline points="5 9 2 12 5 15"></polyline><polyline points="19 9 22 12 19 15"></polyline><line x1="2" y1="5" x2="2" y2="19"></line><line x1="22" y1="5" x2="22" y2="19"></line></svg>`;
438
439
  var ICON_ROTATE_CW = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"></polyline><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path></svg>`;
439
440
  var ICON_ROTATE_CCW = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"></polyline><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"></path></svg>`;
440
441
  var ICON_DOWNLOAD = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>`;
@@ -454,6 +455,7 @@ var ICON_MAP = {
454
455
  "zoom-in": ICON_ZOOM_IN,
455
456
  "zoom-out": ICON_ZOOM_OUT,
456
457
  "fit-page": ICON_FIT_PAGE,
458
+ "fit-width": ICON_FIT_WIDTH,
457
459
  "fit-slide": ICON_FIT_PAGE,
458
460
  "rotate-cw": ICON_ROTATE_CW,
459
461
  "rotate-ccw": ICON_ROTATE_CCW,
@@ -1400,6 +1402,7 @@ var PdfPlugin = class {
1400
1402
  let currentPage = 1;
1401
1403
  let zoomScale = 1;
1402
1404
  let rotation = 0;
1405
+ let fitMode = "width";
1403
1406
  let currentRenderTask = null;
1404
1407
  const renderPage = async (pageNum) => {
1405
1408
  if (currentRenderTask) {
@@ -1419,11 +1422,16 @@ var PdfPlugin = class {
1419
1422
  const containerWidth = container.clientWidth || 900;
1420
1423
  const containerHeight = container.clientHeight || 700;
1421
1424
  const unscaledVp = page.getViewport({ scale: 1, rotation });
1422
- const availWidth = Math.max(100, containerWidth - 48);
1423
- const availHeight = Math.max(100, containerHeight - 88);
1425
+ const availWidth = Math.max(280, containerWidth - 48);
1426
+ const availHeight = Math.max(280, containerHeight - 88);
1424
1427
  const scaleW = availWidth / unscaledVp.width;
1425
1428
  const scaleH = availHeight / unscaledVp.height;
1426
- const fitScale = Math.min(scaleW, scaleH);
1429
+ let fitScale;
1430
+ if (fitMode === "page") {
1431
+ fitScale = Math.max(0.5, Math.min(scaleW, scaleH));
1432
+ } else {
1433
+ fitScale = Math.max(0.65, Math.min(1.15, scaleW));
1434
+ }
1427
1435
  const effectiveScale = (fitScale > 0 ? fitScale : 1) * zoomScale;
1428
1436
  const pixelRatio = window.devicePixelRatio || 1;
1429
1437
  const viewport = page.getViewport({ scale: effectiveScale, rotation });
@@ -1449,7 +1457,19 @@ var PdfPlugin = class {
1449
1457
  }
1450
1458
  };
1451
1459
  await renderPage(1);
1460
+ let resizeTimer = null;
1461
+ const resizeObserver = new ResizeObserver(() => {
1462
+ if (resizeTimer) clearTimeout(resizeTimer);
1463
+ resizeTimer = setTimeout(() => {
1464
+ if (Math.abs(zoomScale - 1) < 0.05) {
1465
+ renderPage(currentPage);
1466
+ }
1467
+ }, 150);
1468
+ });
1469
+ resizeObserver.observe(container);
1452
1470
  const cleanup = () => {
1471
+ resizeObserver.disconnect();
1472
+ if (resizeTimer) clearTimeout(resizeTimer);
1453
1473
  if (currentRenderTask) {
1454
1474
  try {
1455
1475
  currentRenderTask.cancel();
@@ -1480,6 +1500,7 @@ var PdfPlugin = class {
1480
1500
  renderPage(currentPage);
1481
1501
  },
1482
1502
  fitToPage: () => {
1503
+ fitMode = fitMode === "width" ? "page" : "width";
1483
1504
  zoomScale = 1;
1484
1505
  rotation = 0;
1485
1506
  renderPage(currentPage);
@@ -2033,8 +2054,53 @@ var DocxPlugin = class {
2033
2054
  }
2034
2055
  }
2035
2056
  }
2036
- const sections = wrapper.querySelectorAll("section.docx");
2037
- const cards = wrapper.querySelectorAll(".fp-docx-page-card");
2057
+ let sections = Array.from(wrapper.querySelectorAll("section.docx"));
2058
+ const cards = Array.from(wrapper.querySelectorAll(".fp-docx-page-card"));
2059
+ if (sections.length === 1 && cards.length === 0) {
2060
+ const singleSec = sections[0];
2061
+ const pageH = 1056;
2062
+ const secH = singleSec.offsetHeight || singleSec.scrollHeight;
2063
+ if (secH > pageH * 1.25) {
2064
+ const children = Array.from(singleSec.children);
2065
+ if (children.length > 1) {
2066
+ const parent = singleSec.parentElement || wrapper;
2067
+ const newSections = [singleSec];
2068
+ singleSec.innerHTML = "";
2069
+ singleSec.style.minHeight = `${pageH}px`;
2070
+ singleSec.style.maxHeight = `${pageH}px`;
2071
+ singleSec.style.overflow = "hidden";
2072
+ singleSec.style.boxSizing = "border-box";
2073
+ let curSec = singleSec;
2074
+ let curH = 0;
2075
+ const maxH = pageH - 96;
2076
+ for (let i = 0; i < children.length; i++) {
2077
+ const child = children[i];
2078
+ curSec.appendChild(child);
2079
+ const chH = child.offsetHeight || 28;
2080
+ curH += chH;
2081
+ if (curH >= maxH && i < children.length - 1) {
2082
+ const nextSec = document.createElement("section");
2083
+ nextSec.className = singleSec.className;
2084
+ nextSec.style.cssText = singleSec.style.cssText;
2085
+ nextSec.style.width = singleSec.style.width || "816px";
2086
+ nextSec.style.minHeight = `${pageH}px`;
2087
+ nextSec.style.maxHeight = `${pageH}px`;
2088
+ nextSec.style.overflow = "hidden";
2089
+ nextSec.style.boxSizing = "border-box";
2090
+ nextSec.style.backgroundColor = "#ffffff";
2091
+ nextSec.style.boxShadow = "0 4px 24px rgba(0, 0, 0, 0.08)";
2092
+ nextSec.style.borderRadius = "4px";
2093
+ nextSec.style.marginBottom = "24px";
2094
+ parent.appendChild(nextSec);
2095
+ newSections.push(nextSec);
2096
+ curSec = nextSec;
2097
+ curH = 0;
2098
+ }
2099
+ }
2100
+ sections = newSections;
2101
+ }
2102
+ }
2103
+ }
2038
2104
  const pageElements = sections.length > 0 ? sections : cards;
2039
2105
  const totalPages = Math.max(1, pageElements.length);
2040
2106
  let currentPage = 1;
@@ -2078,22 +2144,27 @@ var DocxPlugin = class {
2078
2144
  }
2079
2145
  scale = 1;
2080
2146
  let rotation = 0;
2081
- const calculateFitScale = () => {
2147
+ let fitMode = "width";
2148
+ const calculateFitScale = (mode = fitMode) => {
2082
2149
  const activeEl = pageElements[currentPage - 1] || wrapper.firstElementChild || wrapper;
2083
2150
  const elW = activeEl.offsetWidth || 816;
2084
- const elH = activeEl.offsetHeight || 1056;
2085
- const availW = Math.max(200, ctx.container.clientWidth - 48);
2086
- const availH = Math.max(200, ctx.container.clientHeight - 80);
2151
+ const singlePageH = Math.min(activeEl.offsetHeight || 1056, Math.round(elW * 1.32));
2152
+ const availW = Math.max(280, ctx.container.clientWidth - 48);
2153
+ const availH = Math.max(280, ctx.container.clientHeight - 80);
2087
2154
  const sW = availW / elW;
2088
- const sH = availH / elH;
2089
- return Math.min(1.1, Math.min(sW, sH));
2155
+ const sH = availH / singlePageH;
2156
+ if (mode === "page") {
2157
+ return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
2158
+ }
2159
+ return Math.max(0.65, Math.min(1.05, sW));
2090
2160
  };
2091
2161
  const applyTransform = () => {
2092
2162
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
2093
2163
  wrapper.style.transformOrigin = "top center";
2094
2164
  };
2095
2165
  setTimeout(() => {
2096
- scale = calculateFitScale();
2166
+ fitMode = "width";
2167
+ scale = calculateFitScale("width");
2097
2168
  applyTransform();
2098
2169
  }, 60);
2099
2170
  const cleanup = () => {
@@ -2127,7 +2198,8 @@ var DocxPlugin = class {
2127
2198
  applyTransform();
2128
2199
  },
2129
2200
  fitToPage: () => {
2130
- scale = calculateFitScale();
2201
+ fitMode = fitMode === "width" ? "page" : "width";
2202
+ scale = calculateFitScale(fitMode);
2131
2203
  rotation = 0;
2132
2204
  applyTransform();
2133
2205
  },
@@ -2197,90 +2269,149 @@ var DocxPlugin = class {
2197
2269
  console.warn("[DocxPlugin] Error parsing relationships:", relsErr);
2198
2270
  }
2199
2271
  }
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";
2272
+ const sectPrs = Array.from(doc.getElementsByTagNameNS("*", "sectPr"));
2273
+ const lastSectPr = sectPrs[sectPrs.length - 1];
2274
+ let defaultW = 12240;
2275
+ let defaultH = 15840;
2276
+ let margins = { top: 1440, right: 1440, bottom: 1440, left: 1440 };
2277
+ if (lastSectPr) {
2278
+ const pgSz = lastSectPr.getElementsByTagNameNS("*", "pgSz")[0];
2279
+ if (pgSz) {
2280
+ defaultW = parseInt(pgSz.getAttribute("w:w") || pgSz.getAttribute("w") || "12240", 10);
2281
+ defaultH = parseInt(pgSz.getAttribute("w:h") || pgSz.getAttribute("h") || "15840", 10);
2282
+ }
2283
+ const pgMar = lastSectPr.getElementsByTagNameNS("*", "pgMar")[0];
2284
+ if (pgMar) {
2285
+ margins.top = parseInt(pgMar.getAttribute("w:top") || pgMar.getAttribute("top") || "1440", 10);
2286
+ margins.bottom = parseInt(pgMar.getAttribute("w:bottom") || pgMar.getAttribute("bottom") || "1440", 10);
2287
+ margins.left = parseInt(pgMar.getAttribute("w:left") || pgMar.getAttribute("left") || "1440", 10);
2288
+ margins.right = parseInt(pgMar.getAttribute("w:right") || pgMar.getAttribute("right") || "1440", 10);
2289
+ }
2290
+ }
2291
+ const createPageCard = () => {
2292
+ const card = document.createElement("div");
2293
+ card.className = "fp-docx-page-card";
2294
+ card.style.backgroundColor = "#ffffff";
2295
+ card.style.borderRadius = "4px";
2296
+ card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
2297
+ card.style.fontFamily = 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
2298
+ card.style.color = "#1e293b";
2299
+ card.style.lineHeight = "1.6";
2300
+ card.style.boxSizing = "border-box";
2301
+ card.style.overflow = "hidden";
2302
+ card.style.position = "relative";
2303
+ card.style.marginBottom = "24px";
2304
+ card.style.width = `${defaultW / 15}px`;
2305
+ card.style.height = `${defaultH / 15}px`;
2306
+ card.style.padding = `${margins.top / 15}px ${margins.right / 15}px ${margins.bottom / 15}px ${margins.left / 15}px`;
2307
+ return card;
2308
+ };
2309
+ let currentCard = createPageCard();
2310
+ let currentHtml = "";
2311
+ const pages = [{ card: currentCard, html: "" }];
2312
+ const flushHtml = () => {
2313
+ pages[pages.length - 1].html += currentHtml;
2314
+ currentHtml = "";
2315
+ };
2316
+ const newPage = () => {
2317
+ flushHtml();
2318
+ currentCard = createPageCard();
2319
+ pages.push({ card: currentCard, html: "" });
2320
+ };
2209
2321
  const body = doc.getElementsByTagNameNS("*", "body")[0] || doc.documentElement;
2210
- let html = "";
2211
2322
  for (const child of Array.from(body.children)) {
2212
2323
  const tag = child.localName || child.nodeName.split(":").pop();
2213
2324
  if (tag === "p") {
2214
2325
  const pStyle = child.getElementsByTagNameNS("*", "pStyle")[0];
2215
2326
  const styleVal = pStyle?.getAttribute("w:val") || pStyle?.getAttribute("val") || "";
2216
2327
  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>`;
2328
+ const chunks = this.extractParagraphChunks(child, imageMap);
2329
+ for (let i = 0; i < chunks.length; i++) {
2330
+ if (i > 0) {
2331
+ newPage();
2332
+ }
2333
+ const textContent = chunks[i];
2334
+ if (!textContent.trim() && !textContent.includes("<img")) {
2335
+ currentHtml += '<div style="height: 10px;"></div>';
2336
+ continue;
2337
+ }
2338
+ const lowerStyle = styleVal.toLowerCase();
2339
+ if (lowerStyle.includes("title")) {
2340
+ 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>`;
2341
+ } else if (lowerStyle.includes("heading1") || styleVal === "1") {
2342
+ currentHtml += `<h2 style="font-size: 22px; font-weight: 700; color: #1e40af; margin: 20px 0 10px;">${textContent}</h2>`;
2343
+ } else if (lowerStyle.includes("heading2") || styleVal === "2") {
2344
+ currentHtml += `<h3 style="font-size: 18px; font-weight: 600; color: #2563eb; margin: 16px 0 8px;">${textContent}</h3>`;
2345
+ } else if (lowerStyle.includes("heading3") || styleVal === "3") {
2346
+ currentHtml += `<h4 style="font-size: 15px; font-weight: 600; color: #334155; margin: 12px 0 6px;">${textContent}</h4>`;
2347
+ } else if (numPr) {
2348
+ 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>`;
2349
+ } else {
2350
+ currentHtml += `<p style="margin: 8px 0; font-size: 14px;">${textContent}</p>`;
2351
+ }
2235
2352
  }
2236
2353
  } else if (tag === "tbl") {
2237
- html += '<table style="width: 100%; border-collapse: collapse; margin: 20px 0; border: 1px solid #cbd5e1;">';
2354
+ currentHtml += '<table style="width: 100%; border-collapse: collapse; margin: 20px 0; border: 1px solid #cbd5e1;">';
2238
2355
  const rows = Array.from(child.getElementsByTagNameNS("*", "tr"));
2239
2356
  rows.forEach((tr, rIdx) => {
2240
- html += `<tr style="${rIdx === 0 ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
2357
+ currentHtml += `<tr style="${rIdx === 0 ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
2241
2358
  const cells = Array.from(tr.getElementsByTagNameNS("*", "tc"));
2242
2359
  cells.forEach((tc) => {
2243
2360
  const cellText = this.extractParagraphHtml(tc, imageMap);
2244
- html += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px; font-size: 13px;">${cellText || "&nbsp;"}</td>`;
2361
+ currentHtml += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px; font-size: 13px;">${cellText || "&nbsp;"}</td>`;
2245
2362
  });
2246
- html += "</tr>";
2363
+ currentHtml += "</tr>";
2247
2364
  });
2248
- html += "</table>";
2365
+ currentHtml += "</table>";
2249
2366
  }
2250
2367
  }
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);
2368
+ flushHtml();
2369
+ for (const page of pages) {
2370
+ page.card.innerHTML = DOMPurify6.sanitize(page.html, {
2371
+ ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "s", "strike", "img", "div", "br"],
2372
+ ADD_ATTR: ["style", "src", "alt", "colspan", "rowspan"]
2373
+ });
2374
+ wrapper.appendChild(page.card);
2375
+ }
2256
2376
  }
2257
2377
  extractParagraphHtml(pElement, imageMap = {}) {
2258
- let result = "";
2378
+ return this.extractParagraphChunks(pElement, imageMap).join("<br/>");
2379
+ }
2380
+ extractParagraphChunks(pElement, imageMap = {}) {
2381
+ const chunks = [""];
2382
+ let currentChunkIndex = 0;
2259
2383
  const drawings = Array.from(pElement.getElementsByTagNameNS("*", "drawing"));
2260
2384
  for (const drawing of drawings) {
2261
2385
  const blip = drawing.getElementsByTagNameNS("*", "blip")[0];
2262
2386
  const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
2263
2387
  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>`;
2388
+ 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
2389
  }
2266
2390
  }
2267
2391
  const runs = Array.from(pElement.getElementsByTagNameNS("*", "r"));
2268
- if (runs.length === 0 && !result) {
2269
- return DOMPurify2.sanitize(pElement.textContent || "");
2392
+ if (runs.length === 0 && !chunks[currentChunkIndex]) {
2393
+ chunks[currentChunkIndex] = DOMPurify6.sanitize(pElement.textContent || "");
2394
+ return chunks;
2270
2395
  }
2271
2396
  for (const r of runs) {
2272
2397
  const blip = r.getElementsByTagNameNS("*", "blip")[0] || r.getElementsByTagNameNS("*", "imagedata")[0];
2273
2398
  const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
2274
2399
  if (rId && imageMap[rId]) {
2275
- result += `<img src="${imageMap[rId]}" style="max-width: 100%; height: auto; display: inline-block; margin: 4px;" />`;
2400
+ chunks[currentChunkIndex] += `<img src="${imageMap[rId]}" style="max-width: 100%; height: auto; display: inline-block; margin: 4px;" />`;
2276
2401
  }
2277
- const brs = r.getElementsByTagNameNS("*", "br");
2278
- for (let i = 0; i < brs.length; i++) {
2279
- result += "<br/>";
2402
+ const brs = Array.from(r.getElementsByTagNameNS("*", "br"));
2403
+ for (const br of brs) {
2404
+ const type = br.getAttribute("w:type") || br.getAttribute("type");
2405
+ if (type === "page") {
2406
+ chunks.push("");
2407
+ currentChunkIndex++;
2408
+ } else {
2409
+ chunks[currentChunkIndex] += "<br/>";
2410
+ }
2280
2411
  }
2281
2412
  const tabs = r.getElementsByTagNameNS("*", "tab");
2282
2413
  if (tabs.length > 0) {
2283
- result += "&emsp;";
2414
+ chunks[currentChunkIndex] += "&emsp;";
2284
2415
  }
2285
2416
  const rPr = r.getElementsByTagNameNS("*", "rPr")[0];
2286
2417
  const isBold = !!rPr?.getElementsByTagNameNS("*", "b")[0];
@@ -2294,7 +2425,7 @@ var DocxPlugin = class {
2294
2425
  const texts = Array.from(r.getElementsByTagNameNS("*", "t"));
2295
2426
  let text = texts.map((t) => t.textContent || "").join("");
2296
2427
  if (!text) continue;
2297
- text = DOMPurify2.sanitize(text);
2428
+ text = DOMPurify6.sanitize(text);
2298
2429
  let styles = "";
2299
2430
  if (isBold) styles += "font-weight: bold; ";
2300
2431
  if (isItalic) styles += "font-style: italic; ";
@@ -2306,12 +2437,12 @@ var DocxPlugin = class {
2306
2437
  if (!isNaN(pt) && pt > 0) styles += `font-size: ${pt}pt; `;
2307
2438
  }
2308
2439
  if (styles) {
2309
- result += `<span style="${styles}">${text}</span>`;
2440
+ chunks[currentChunkIndex] += `<span style="${styles}">${text}</span>`;
2310
2441
  } else {
2311
- result += text;
2442
+ chunks[currentChunkIndex] += text;
2312
2443
  }
2313
2444
  }
2314
- return result;
2445
+ return chunks;
2315
2446
  }
2316
2447
  renderBinaryDocFallback(ctx, wrapper) {
2317
2448
  const card = document.createElement("div");
@@ -2328,14 +2459,14 @@ var DocxPlugin = class {
2328
2459
  const wordDocStream = cfbf.readStream("WordDocument");
2329
2460
  if (wordDocStream && wordDocStream.length >= 512) {
2330
2461
  const text2 = this.extractReadableStrings(wordDocStream);
2331
- card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2.sanitize(text2)}</div>`;
2462
+ card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify6.sanitize(text2)}</div>`;
2332
2463
  wrapper.appendChild(card);
2333
2464
  return;
2334
2465
  }
2335
2466
  } catch {
2336
2467
  }
2337
2468
  const text = this.extractReadableStrings(new Uint8Array(ctx.buffer));
2338
- card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2.sanitize(text)}</div>`;
2469
+ card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify6.sanitize(text)}</div>`;
2339
2470
  wrapper.appendChild(card);
2340
2471
  }
2341
2472
  extractReadableStrings(bytes) {
@@ -2512,11 +2643,9 @@ var ExcelPlugin = class {
2512
2643
  const calculateFitScale = () => {
2513
2644
  const table = contentArea.querySelector("table");
2514
2645
  if (!table) return 1;
2515
- const availW = Math.max(200, container.clientWidth - 48);
2516
- const availH = Math.max(200, container.clientHeight - 80);
2646
+ const availW = Math.max(280, container.clientWidth - 48);
2517
2647
  const tW = table.offsetWidth || 800;
2518
- const tH = table.offsetHeight || 600;
2519
- return Math.min(1, Math.min(availW / tW, availH / tH));
2648
+ return Math.max(0.65, Math.min(1, availW / tW));
2520
2649
  };
2521
2650
  try {
2522
2651
  wb = XLSX.read(new Uint8Array(ctx.buffer), { type: "array", cellDates: true });
@@ -2934,41 +3063,94 @@ var CodePlugin = class {
2934
3063
  async render(ctx) {
2935
3064
  const decoder = new TextDecoder("utf-8");
2936
3065
  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);
3066
+ const extRaw = ctx.metadata.extension?.toLowerCase();
3067
+ const mimeRaw = ctx.metadata.mimeType?.toLowerCase();
3068
+ const isTxt = extRaw === ".txt" || mimeRaw === "text/plain" && (!extRaw || !this.extensions.filter((e) => e !== ".txt").includes(extRaw));
3069
+ let rawPages = [];
3070
+ if (isTxt) {
3071
+ const explicitPages = fullText.split(/(?:\f|\x0C)/);
3072
+ for (const ep of explicitPages) {
3073
+ const lines = ep.split(/\r?\n/);
3074
+ let currentChunk = [];
3075
+ for (let i = 0; i < lines.length; i++) {
3076
+ currentChunk.push(lines[i]);
3077
+ if (currentChunk.length >= 46) {
3078
+ rawPages.push(currentChunk.join("\n"));
3079
+ currentChunk = [];
3080
+ }
3081
+ }
3082
+ if (currentChunk.length > 0 || lines.length === 0) {
3083
+ rawPages.push(currentChunk.join("\n"));
3084
+ }
3085
+ }
3086
+ if (rawPages.length === 0) rawPages = [""];
3087
+ } else {
3088
+ const pageSplitRegex = /(?:\f|\x0C|(?:\r?\n|^)\s*[-=_]{3,}\s*(?:PAGE|Page|page break|Page Break)[\s\d\w-]*[-=_]{3,}\s*(?:\r?\n|$))/i;
3089
+ rawPages = fullText.split(pageSplitRegex).map((p) => p.trim()).filter((p) => p.length > 0);
3090
+ }
2939
3091
  const totalPages = Math.max(1, rawPages.length);
2940
3092
  let currentPage = 1;
2941
3093
  const container = document.createElement("div");
2942
3094
  container.style.width = "100%";
2943
3095
  container.style.height = "100%";
2944
3096
  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
3097
  let fontSize = 13;
2950
3098
  let rotation = 0;
3099
+ let zoomLevel = 1;
2951
3100
  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
3101
  const code = document.createElement("code");
3102
+ if (isTxt) {
3103
+ container.style.backgroundColor = "#f1f5f9";
3104
+ container.style.padding = "32px";
3105
+ container.style.boxSizing = "border-box";
3106
+ container.style.display = "flex";
3107
+ container.style.flexDirection = "column";
3108
+ container.style.alignItems = "center";
3109
+ pre.style.margin = "0";
3110
+ pre.style.fontFamily = "Consolas, 'Courier New', monospace";
3111
+ pre.style.fontSize = "13px";
3112
+ pre.style.color = "#1e293b";
3113
+ pre.style.lineHeight = "1.5";
3114
+ pre.style.whiteSpace = "pre-wrap";
3115
+ pre.style.wordBreak = "break-word";
3116
+ pre.style.backgroundColor = "#ffffff";
3117
+ pre.style.width = "816px";
3118
+ pre.style.minHeight = "1056px";
3119
+ pre.style.padding = "72px 56px";
3120
+ pre.style.boxSizing = "border-box";
3121
+ pre.style.boxShadow = "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)";
3122
+ pre.style.borderRadius = "4px";
3123
+ pre.style.transformOrigin = "top center";
3124
+ } else {
3125
+ container.style.backgroundColor = "#1e1e1e";
3126
+ container.style.color = "#d4d4d4";
3127
+ container.style.padding = "16px";
3128
+ container.style.boxSizing = "border-box";
3129
+ pre.style.margin = "0";
3130
+ pre.style.fontFamily = "Consolas, Menlo, Monaco, monospace";
3131
+ pre.style.fontSize = `${fontSize}px`;
3132
+ pre.style.lineHeight = "1.5";
3133
+ pre.style.whiteSpace = "pre-wrap";
3134
+ pre.style.wordBreak = "break-all";
3135
+ pre.style.transformOrigin = "top left";
3136
+ }
2959
3137
  const ext = (ctx.metadata.extension || "").replace(".", "");
2960
3138
  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 {
3139
+ if (isTxt) {
2968
3140
  code.textContent = text;
3141
+ } else {
3142
+ try {
3143
+ if (ext && hljs.getLanguage(ext)) {
3144
+ code.innerHTML = hljs.highlight(text, { language: ext }).value;
3145
+ } else {
3146
+ code.innerHTML = hljs.highlightAuto(text).value;
3147
+ }
3148
+ } catch {
3149
+ code.textContent = text;
3150
+ }
2969
3151
  }
2970
3152
  };
2971
- renderCodePage(rawPages[0] || fullText);
3153
+ renderCodePage(rawPages[0] || (isTxt ? "" : fullText));
2972
3154
  pre.appendChild(code);
2973
3155
  container.appendChild(pre);
2974
3156
  let indicator = null;
@@ -2977,15 +3159,22 @@ var CodePlugin = class {
2977
3159
  indicator.className = "fp-code-page-indicator";
2978
3160
  indicator.style.position = "sticky";
2979
3161
  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";
3162
+ if (isTxt) {
3163
+ indicator.style.backgroundColor = "rgba(255, 255, 255, 0.9)";
3164
+ indicator.style.color = "#334155";
3165
+ indicator.style.border = "1px solid #e2e8f0";
3166
+ indicator.style.boxShadow = "0 1px 3px rgba(0,0,0,0.1)";
3167
+ } else {
3168
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
3169
+ indicator.style.color = "#f8fafc";
3170
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
3171
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
3172
+ indicator.style.backdropFilter = "blur(8px)";
3173
+ }
2983
3174
  indicator.style.fontSize = "12px";
2984
3175
  indicator.style.fontWeight = "600";
2985
3176
  indicator.style.padding = "5px 14px";
2986
3177
  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
3178
  indicator.style.zIndex = "10";
2990
3179
  indicator.style.userSelect = "none";
2991
3180
  indicator.style.pointerEvents = "none";
@@ -2996,7 +3185,7 @@ var CodePlugin = class {
2996
3185
  }
2997
3186
  const showPage = (pageNum) => {
2998
3187
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
2999
- renderCodePage(rawPages[currentPage - 1] || fullText);
3188
+ renderCodePage(rawPages[currentPage - 1] || (isTxt ? "" : fullText));
3000
3189
  if (indicator) {
3001
3190
  indicator.textContent = `Page ${currentPage} of ${totalPages}`;
3002
3191
  }
@@ -3012,39 +3201,57 @@ var CodePlugin = class {
3012
3201
  ctx.container.innerHTML = "";
3013
3202
  };
3014
3203
  ctx.signal.addEventListener("abort", cleanup);
3204
+ const updateTransform = () => {
3205
+ if (isTxt) {
3206
+ pre.style.transform = `scale(${zoomLevel}) rotate(${rotation}deg)`;
3207
+ } else {
3208
+ pre.style.transform = `rotate(${rotation}deg)`;
3209
+ pre.style.fontSize = `${fontSize}px`;
3210
+ }
3211
+ };
3015
3212
  return {
3016
3213
  destroy: cleanup,
3017
3214
  getPageCount: () => totalPages,
3018
3215
  getCurrentPage: () => currentPage,
3019
3216
  goToPage: (page) => showPage(page),
3020
3217
  zoomIn: () => {
3021
- fontSize = Math.min(32, fontSize + 2);
3022
- pre.style.fontSize = `${fontSize}px`;
3218
+ if (isTxt) {
3219
+ zoomLevel = Math.min(3, zoomLevel + 0.1);
3220
+ } else {
3221
+ fontSize = Math.min(32, fontSize + 2);
3222
+ }
3223
+ updateTransform();
3023
3224
  },
3024
3225
  zoomOut: () => {
3025
- fontSize = Math.max(8, fontSize - 2);
3026
- pre.style.fontSize = `${fontSize}px`;
3226
+ if (isTxt) {
3227
+ zoomLevel = Math.max(0.1, zoomLevel - 0.1);
3228
+ } else {
3229
+ fontSize = Math.max(8, fontSize - 2);
3230
+ }
3231
+ updateTransform();
3027
3232
  },
3028
- getZoom: () => fontSize / 13,
3233
+ getZoom: () => isTxt ? zoomLevel : fontSize / 13,
3029
3234
  setZoom: (level) => {
3030
- fontSize = Math.round(13 * level);
3031
- pre.style.fontSize = `${fontSize}px`;
3235
+ if (isTxt) {
3236
+ zoomLevel = level;
3237
+ } else {
3238
+ fontSize = Math.round(13 * level);
3239
+ }
3240
+ updateTransform();
3032
3241
  },
3033
3242
  fitToPage: () => {
3034
3243
  fontSize = 13;
3035
3244
  rotation = 0;
3036
- pre.style.fontSize = "13px";
3037
- pre.style.transform = "none";
3245
+ zoomLevel = isTxt ? Math.max(0.65, Math.min(1.05, (container.clientWidth - 48) / 816)) : 1;
3246
+ updateTransform();
3038
3247
  },
3039
3248
  rotateCW: () => {
3040
3249
  rotation = (rotation + 90) % 360;
3041
- pre.style.transform = `rotate(${rotation}deg)`;
3042
- pre.style.transformOrigin = "top left";
3250
+ updateTransform();
3043
3251
  },
3044
3252
  rotateCCW: () => {
3045
3253
  rotation = (rotation - 90 + 360) % 360;
3046
- pre.style.transform = `rotate(${rotation}deg)`;
3047
- pre.style.transformOrigin = "top left";
3254
+ updateTransform();
3048
3255
  },
3049
3256
  download: () => {
3050
3257
  const mimeType = ctx.metadata.mimeType || "text/plain";
@@ -3343,7 +3550,7 @@ var MarkdownPlugin = class {
3343
3550
  gfm: true,
3344
3551
  breaks: true
3345
3552
  });
3346
- const cleanHtml = DOMPurify2.sanitize(rawHtml, {
3553
+ const cleanHtml = DOMPurify6.sanitize(rawHtml, {
3347
3554
  USE_PROFILES: { html: true }
3348
3555
  });
3349
3556
  const wrapper = document.createElement("div");
@@ -3979,6 +4186,14 @@ var RtfPlugin = class {
3979
4186
  group: "actions",
3980
4187
  execute: () => instance.download?.()
3981
4188
  },
4189
+ {
4190
+ id: "copy",
4191
+ icon: "copy",
4192
+ label: "Copy Text",
4193
+ type: "button",
4194
+ group: "actions",
4195
+ execute: () => instance.copy?.()
4196
+ },
3982
4197
  {
3983
4198
  id: "print",
3984
4199
  icon: "print",
@@ -4009,20 +4224,27 @@ var RtfPlugin = class {
4009
4224
  let scale = 1;
4010
4225
  let rotation = 0;
4011
4226
  let pageElements = [];
4012
- const calculateFitScale = () => {
4227
+ let fitMode = "width";
4228
+ const calculateFitScale = (mode = fitMode) => {
4013
4229
  const activeEl = pageElements[currentPage - 1] || wrapper;
4014
4230
  const elW = activeEl.offsetWidth || 850;
4015
- const elH = activeEl.offsetHeight || 1e3;
4016
- const availW = Math.max(200, ctx.container.clientWidth - 48);
4017
- const availH = Math.max(200, ctx.container.clientHeight - 80);
4018
- return Math.min(1.1, Math.min(availW / elW, availH / elH));
4231
+ const singlePageH = Math.min(activeEl.offsetHeight || 1056, Math.round(elW * 1.32));
4232
+ const availW = Math.max(280, ctx.container.clientWidth - 48);
4233
+ const availH = Math.max(280, ctx.container.clientHeight - 80);
4234
+ const sW = availW / elW;
4235
+ const sH = availH / singlePageH;
4236
+ if (mode === "page") {
4237
+ return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
4238
+ }
4239
+ return Math.max(0.65, Math.min(1.05, sW));
4019
4240
  };
4020
4241
  const applyTransform = () => {
4021
4242
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
4022
4243
  wrapper.style.transformOrigin = "top center";
4023
4244
  };
4024
4245
  setTimeout(() => {
4025
- scale = calculateFitScale();
4246
+ fitMode = "width";
4247
+ scale = calculateFitScale("width");
4026
4248
  applyTransform();
4027
4249
  }, 60);
4028
4250
  try {
@@ -4040,14 +4262,31 @@ var RtfPlugin = class {
4040
4262
  } catch (err) {
4041
4263
  console.warn("[RtfPlugin] RTF render error, fallback text:", err);
4042
4264
  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];
4265
+ const rawPages = text.split(/\\page\b/).map((segment) => {
4266
+ return segment.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim();
4267
+ }).filter((p) => p.length > 0);
4268
+ const pages = rawPages.length > 0 ? rawPages : [text.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim()];
4269
+ for (let i = 0; i < pages.length; i++) {
4270
+ const pageCard = document.createElement("div");
4271
+ pageCard.className = "fp-rtf-page-card";
4272
+ pageCard.style.backgroundColor = "#ffffff";
4273
+ pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4274
+ pageCard.style.borderRadius = "4px";
4275
+ pageCard.style.padding = "72px 56px";
4276
+ pageCard.style.width = "816px";
4277
+ pageCard.style.minHeight = "1056px";
4278
+ pageCard.style.maxWidth = "100%";
4279
+ pageCard.style.boxSizing = "border-box";
4280
+ pageCard.style.fontFamily = "serif";
4281
+ pageCard.style.fontSize = "12pt";
4282
+ pageCard.style.lineHeight = "1.6";
4283
+ pageCard.style.color = "#1e293b";
4284
+ pageCard.style.whiteSpace = "pre-wrap";
4285
+ pageCard.style.display = i === 0 ? "block" : "none";
4286
+ pageCard.textContent = pages[i];
4287
+ wrapper.appendChild(pageCard);
4288
+ pageElements.push(pageCard);
4289
+ }
4051
4290
  }
4052
4291
  const totalPages = Math.max(1, pageElements.length);
4053
4292
  let currentPage = 1;
@@ -4114,7 +4353,8 @@ var RtfPlugin = class {
4114
4353
  applyTransform();
4115
4354
  },
4116
4355
  fitToPage: () => {
4117
- scale = calculateFitScale();
4356
+ fitMode = fitMode === "width" ? "page" : "width";
4357
+ scale = calculateFitScale(fitMode);
4118
4358
  rotation = 0;
4119
4359
  applyTransform();
4120
4360
  },
@@ -4135,6 +4375,10 @@ var RtfPlugin = class {
4135
4375
  a.click();
4136
4376
  URL.revokeObjectURL(url);
4137
4377
  },
4378
+ copy: () => {
4379
+ const text = wrapper.textContent || "";
4380
+ navigator.clipboard?.writeText(text);
4381
+ },
4138
4382
  print: () => {
4139
4383
  window.print();
4140
4384
  }
@@ -4209,7 +4453,7 @@ var HtmlPreviewPlugin = class {
4209
4453
  }
4210
4454
  async render(ctx) {
4211
4455
  const rawHtml = new TextDecoder("utf-8").decode(ctx.buffer);
4212
- const sanitized = DOMPurify2.sanitize(rawHtml, {
4456
+ const sanitized = DOMPurify6.sanitize(rawHtml, {
4213
4457
  WHOLE_DOCUMENT: true,
4214
4458
  ADD_TAGS: ["style", "link"],
4215
4459
  ADD_ATTR: ["target", "rel"]
@@ -4429,19 +4673,29 @@ var OpenDocumentPlugin = class {
4429
4673
  ctx.container.appendChild(container);
4430
4674
  let scale = 1;
4431
4675
  let rotation = 0;
4432
- const calculateFitScale = () => {
4676
+ let fitMode = "width";
4677
+ const calculateFitScale = (mode = fitMode) => {
4433
4678
  const elW = wrapper.offsetWidth || 850;
4434
- const elH = wrapper.offsetHeight || (isPresentation ? 540 : 1e3);
4435
- const availW = Math.max(200, ctx.container.clientWidth - 48);
4436
- const availH = Math.max(200, ctx.container.clientHeight - 80);
4437
- return Math.min(1, Math.min(availW / elW, availH / elH));
4679
+ const singlePageH = Math.min(wrapper.offsetHeight || 1056, Math.round(elW * 1.32));
4680
+ const availW = Math.max(280, ctx.container.clientWidth - 48);
4681
+ const availH = Math.max(280, ctx.container.clientHeight - 80);
4682
+ const sW = availW / elW;
4683
+ const sH = availH / (isPresentation ? 540 : singlePageH);
4684
+ if (isPresentation) {
4685
+ return Math.min(1, Math.min(sW, sH));
4686
+ }
4687
+ if (mode === "page") {
4688
+ return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
4689
+ }
4690
+ return Math.max(0.65, Math.min(1.05, sW));
4438
4691
  };
4439
4692
  const applyTransform = () => {
4440
4693
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
4441
4694
  wrapper.style.transformOrigin = "top center";
4442
4695
  };
4443
4696
  setTimeout(() => {
4444
- scale = calculateFitScale();
4697
+ fitMode = "width";
4698
+ scale = calculateFitScale("width");
4445
4699
  applyTransform();
4446
4700
  }, 60);
4447
4701
  let currentPage = 1;
@@ -4475,14 +4729,97 @@ var OpenDocumentPlugin = class {
4475
4729
  wrapper.textContent = contentDoc.documentElement.textContent || "Formula content";
4476
4730
  }
4477
4731
  } else {
4478
- wrapper.style.maxWidth = "850px";
4479
- wrapper.style.padding = "48px";
4732
+ wrapper.style.maxWidth = "none";
4733
+ wrapper.style.padding = "0";
4480
4734
  wrapper.style.minHeight = "100%";
4735
+ wrapper.style.backgroundColor = "transparent";
4736
+ wrapper.style.boxShadow = "none";
4737
+ wrapper.style.position = "relative";
4738
+ const dims = this.getPageDimensions(stylesDoc, contentDoc);
4481
4739
  const bodyHtml = this.renderOdfBody(contentDoc, styleMap, imageUrls);
4482
- wrapper.innerHTML = DOMPurify2.sanitize(bodyHtml, {
4483
- ADD_TAGS: ["math", "semantics", "mrow", "mi", "mo", "mn", "msup", "msub"],
4740
+ const tempDiv = document.createElement("div");
4741
+ tempDiv.style.width = `${dims.width - dims.marginLeft - dims.marginRight}px`;
4742
+ tempDiv.style.position = "absolute";
4743
+ tempDiv.style.visibility = "hidden";
4744
+ tempDiv.innerHTML = DOMPurify6.sanitize(bodyHtml, {
4745
+ ADD_TAGS: ["math", "semantics", "mrow", "mi", "mo", "mn", "msup", "msub", "hr"],
4484
4746
  ADD_ATTR: ["style", "colspan", "rowspan"]
4485
4747
  });
4748
+ document.body.appendChild(tempDiv);
4749
+ const contentHeight = dims.height - dims.marginTop - dims.marginBottom;
4750
+ const pageElements = [[]];
4751
+ let currentHeight = 0;
4752
+ let currentPageIdx = 0;
4753
+ Array.from(tempDiv.children).forEach((child) => {
4754
+ const el = child;
4755
+ const style = el.getAttribute("style") || "";
4756
+ const isBreakBefore = style.includes("page-break-before: always");
4757
+ const isBreakAfter = style.includes("page-break-after: always");
4758
+ const isSoftBreak = el.classList.contains("odf-page-break");
4759
+ if (isBreakBefore) {
4760
+ if (pageElements[currentPageIdx].length > 0) {
4761
+ currentPageIdx++;
4762
+ pageElements.push([]);
4763
+ currentHeight = 0;
4764
+ }
4765
+ }
4766
+ const h = el.offsetHeight || 0;
4767
+ if (currentHeight + h > contentHeight && pageElements[currentPageIdx].length > 0 && !isSoftBreak) {
4768
+ currentPageIdx++;
4769
+ pageElements.push([]);
4770
+ currentHeight = 0;
4771
+ }
4772
+ if (!isSoftBreak) {
4773
+ pageElements[currentPageIdx].push(el.cloneNode(true));
4774
+ currentHeight += h;
4775
+ }
4776
+ if (isBreakAfter || isSoftBreak) {
4777
+ currentPageIdx++;
4778
+ pageElements.push([]);
4779
+ currentHeight = 0;
4780
+ }
4781
+ });
4782
+ document.body.removeChild(tempDiv);
4783
+ if (pageElements.length > 1 && pageElements[pageElements.length - 1].length === 0) {
4784
+ pageElements.pop();
4785
+ }
4786
+ totalPages = Math.max(1, pageElements.length);
4787
+ pageElements.forEach((elements, idx) => {
4788
+ const page = document.createElement("div");
4789
+ page.className = `fp-odt-page fp-odt-page-${idx + 1}`;
4790
+ page.style.width = `${dims.width}px`;
4791
+ page.style.minHeight = `${dims.height}px`;
4792
+ page.style.padding = `${dims.marginTop}px ${dims.marginRight}px ${dims.marginBottom}px ${dims.marginLeft}px`;
4793
+ page.style.margin = "0 auto";
4794
+ page.style.backgroundColor = "#ffffff";
4795
+ page.style.boxShadow = "0 2px 10px rgba(0,0,0,0.08)";
4796
+ page.style.borderRadius = "4px";
4797
+ page.style.boxSizing = "border-box";
4798
+ page.style.display = idx === 0 ? "block" : "none";
4799
+ page.style.position = "absolute";
4800
+ page.style.top = "0";
4801
+ page.style.left = "50%";
4802
+ page.style.transform = "translateX(-50%)";
4803
+ elements.forEach((el) => page.appendChild(el));
4804
+ wrapper.appendChild(page);
4805
+ slides.push(page);
4806
+ });
4807
+ const pageIndicator = document.createElement("div");
4808
+ pageIndicator.className = "fp-odt-page-indicator";
4809
+ pageIndicator.style.position = "sticky";
4810
+ pageIndicator.style.bottom = "16px";
4811
+ pageIndicator.style.left = "50%";
4812
+ pageIndicator.style.transform = "translateX(-50%)";
4813
+ pageIndicator.style.backgroundColor = "rgba(0, 0, 0, 0.6)";
4814
+ pageIndicator.style.color = "#fff";
4815
+ pageIndicator.style.padding = "6px 12px";
4816
+ pageIndicator.style.borderRadius = "16px";
4817
+ pageIndicator.style.fontSize = "12px";
4818
+ pageIndicator.style.zIndex = "100";
4819
+ pageIndicator.style.display = "inline-block";
4820
+ pageIndicator.style.width = "fit-content";
4821
+ pageIndicator.textContent = `Page 1 of ${totalPages}`;
4822
+ container.appendChild(pageIndicator);
4486
4823
  }
4487
4824
  const cleanup = () => {
4488
4825
  imageUrls.forEach((url) => URL.revokeObjectURL(url));
@@ -4491,11 +4828,15 @@ var OpenDocumentPlugin = class {
4491
4828
  };
4492
4829
  ctx.signal.addEventListener("abort", cleanup);
4493
4830
  const goToPage = (page) => {
4494
- if (!isPresentation || page < 1 || page > totalPages) return;
4831
+ if (page < 1 || page > totalPages) return;
4495
4832
  currentPage = page;
4496
4833
  slides.forEach((s, idx) => {
4497
4834
  s.style.display = idx === page - 1 ? "block" : "none";
4498
4835
  });
4836
+ const indicator = container.querySelector(".fp-odt-page-indicator");
4837
+ if (indicator) {
4838
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4839
+ }
4499
4840
  ctx.emit("page-change", { page: currentPage, total: totalPages });
4500
4841
  };
4501
4842
  return {
@@ -4515,7 +4856,8 @@ var OpenDocumentPlugin = class {
4515
4856
  applyTransform();
4516
4857
  },
4517
4858
  fitToPage: () => {
4518
- scale = calculateFitScale();
4859
+ fitMode = fitMode === "width" ? "page" : "width";
4860
+ scale = calculateFitScale(fitMode);
4519
4861
  rotation = 0;
4520
4862
  applyTransform();
4521
4863
  },
@@ -4564,6 +4906,43 @@ var OpenDocumentPlugin = class {
4564
4906
  }
4565
4907
  };
4566
4908
  }
4909
+ getPageDimensions(stylesDoc, contentDoc) {
4910
+ let width = 850;
4911
+ let height = 1123;
4912
+ let marginTop = 48;
4913
+ let marginBottom = 48;
4914
+ let marginLeft = 48;
4915
+ let marginRight = 48;
4916
+ const parseUnit = (val) => {
4917
+ if (!val) return null;
4918
+ if (val.endsWith("cm")) return parseFloat(val) * 37.8;
4919
+ if (val.endsWith("mm")) return parseFloat(val) * 3.78;
4920
+ if (val.endsWith("in")) return parseFloat(val) * 96;
4921
+ if (val.endsWith("pt")) return parseFloat(val) * 1.33;
4922
+ if (val.endsWith("px")) return parseFloat(val);
4923
+ return parseFloat(val);
4924
+ };
4925
+ const docs = [stylesDoc, contentDoc].filter(Boolean);
4926
+ for (const doc of docs) {
4927
+ const pageLayout = doc.querySelector("page-layout-properties, [page-width]");
4928
+ if (pageLayout) {
4929
+ const w = parseUnit(pageLayout.getAttribute("fo:page-width") || pageLayout.getAttribute("page-width"));
4930
+ const h = parseUnit(pageLayout.getAttribute("fo:page-height") || pageLayout.getAttribute("page-height"));
4931
+ const mt = parseUnit(pageLayout.getAttribute("fo:margin-top") || pageLayout.getAttribute("margin-top"));
4932
+ const mb = parseUnit(pageLayout.getAttribute("fo:margin-bottom") || pageLayout.getAttribute("margin-bottom"));
4933
+ const ml = parseUnit(pageLayout.getAttribute("fo:margin-left") || pageLayout.getAttribute("margin-left"));
4934
+ const mr = parseUnit(pageLayout.getAttribute("fo:margin-right") || pageLayout.getAttribute("margin-right"));
4935
+ if (w !== null) width = w;
4936
+ if (h !== null) height = h;
4937
+ if (mt !== null) marginTop = mt;
4938
+ if (mb !== null) marginBottom = mb;
4939
+ if (ml !== null) marginLeft = ml;
4940
+ if (mr !== null) marginRight = mr;
4941
+ break;
4942
+ }
4943
+ }
4944
+ return { width, height, marginTop, marginBottom, marginLeft, marginRight };
4945
+ }
4567
4946
  extractStyles(stylesDoc, contentDoc) {
4568
4947
  const map = /* @__PURE__ */ new Map();
4569
4948
  const styleNodes = [];
@@ -4586,14 +4965,21 @@ var OpenDocumentPlugin = class {
4586
4965
  if (color) css += `color: ${color}; `;
4587
4966
  if (size) css += `font-size: ${size}; `;
4588
4967
  }
4589
- const paraProp = node.querySelector("paragraph-properties, [text-align]");
4968
+ let paraProp = node.querySelector("paragraph-properties, [text-align]");
4969
+ if (!paraProp) {
4970
+ paraProp = Array.from(node.children).find((c) => c.tagName.includes("paragraph-properties")) || null;
4971
+ }
4590
4972
  if (paraProp) {
4591
4973
  const align = paraProp.getAttribute("fo:text-align") || paraProp.getAttribute("text-align");
4592
4974
  const mt = paraProp.getAttribute("fo:margin-top") || paraProp.getAttribute("margin-top");
4593
4975
  const mb = paraProp.getAttribute("fo:margin-bottom") || paraProp.getAttribute("margin-bottom");
4976
+ const breakBefore = paraProp.getAttribute("fo:break-before") || paraProp.getAttribute("break-before");
4977
+ const breakAfter = paraProp.getAttribute("fo:break-after") || paraProp.getAttribute("break-after");
4594
4978
  if (align) css += `text-align: ${align}; `;
4595
4979
  if (mt) css += `margin-top: ${mt}; `;
4596
4980
  if (mb) css += `margin-bottom: ${mb}; `;
4981
+ if (breakBefore === "page") css += "page-break-before: always; ";
4982
+ if (breakAfter === "page") css += "page-break-after: always; ";
4597
4983
  }
4598
4984
  if (css) map.set(name, css);
4599
4985
  }
@@ -4678,6 +5064,8 @@ var OpenDocumentPlugin = class {
4678
5064
  result += "&emsp;";
4679
5065
  } else if (tag === "line-break") {
4680
5066
  result += "<br/>";
5067
+ } else if (tag === "soft-page-break") {
5068
+ result += '<hr class="odf-page-break" style="page-break-after: always; border: none; margin: 0; padding: 0; height: 0;" />';
4681
5069
  } else {
4682
5070
  result += el.textContent || "";
4683
5071
  }
@@ -4838,20 +5226,9 @@ var DocPlugin = class {
4838
5226
  container.style.overflow = "auto";
4839
5227
  container.style.padding = "32px 16px";
4840
5228
  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);
5229
+ container.style.display = "flex";
5230
+ container.style.justifyContent = "center";
5231
+ container.style.alignItems = "flex-start";
4855
5232
  ctx.container.appendChild(container);
4856
5233
  let scale = 1;
4857
5234
  let extractedRawText = "";
@@ -4878,21 +5255,27 @@ var DocPlugin = class {
4878
5255
  const rawPages = this.splitIntoPages(extractedRawText);
4879
5256
  const totalPages = Math.max(1, rawPages.length);
4880
5257
  let currentPage = 1;
4881
- wrapper.innerHTML = "";
4882
5258
  const pageCards = [];
4883
5259
  for (let i = 0; i < totalPages; i++) {
4884
5260
  const pageCard = document.createElement("div");
4885
5261
  pageCard.className = "fp-doc-page-card";
5262
+ pageCard.style.width = "816px";
5263
+ pageCard.style.height = "1056px";
4886
5264
  pageCard.style.backgroundColor = "#ffffff";
4887
5265
  pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4888
5266
  pageCard.style.borderRadius = "4px";
4889
- pageCard.style.padding = "56px 48px";
4890
- pageCard.style.minHeight = "100%";
5267
+ pageCard.style.padding = "96px 72px";
5268
+ pageCard.style.boxSizing = "border-box";
5269
+ pageCard.style.overflow = "hidden";
4891
5270
  pageCard.style.display = i === 0 ? "block" : "none";
5271
+ pageCard.style.transformOrigin = "top center";
5272
+ pageCard.style.transition = "transform 0.2s ease";
5273
+ pageCard.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
5274
+ pageCard.style.color = "#1e293b";
4892
5275
  if (isFallback && i === 0) {
4893
5276
  pageCard.innerHTML = `
4894
5277
  <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>
5278
+ <h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${DOMPurify6.sanitize(ctx.metadata.name || "Word Document (.doc)")}</h2>
4896
5279
  <span style="font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px;">Legacy Word 97-2003 Binary Preview</span>
4897
5280
  </div>
4898
5281
  ${this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document")}
@@ -4900,15 +5283,17 @@ var DocPlugin = class {
4900
5283
  } else {
4901
5284
  pageCard.innerHTML = this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document");
4902
5285
  }
4903
- wrapper.appendChild(pageCard);
5286
+ container.appendChild(pageCard);
4904
5287
  pageCards.push(pageCard);
4905
5288
  }
4906
5289
  let indicator = null;
4907
5290
  if (totalPages > 1) {
4908
5291
  indicator = document.createElement("div");
4909
5292
  indicator.className = "fp-doc-page-indicator";
4910
- indicator.style.position = "sticky";
5293
+ indicator.style.position = "fixed";
4911
5294
  indicator.style.bottom = "16px";
5295
+ indicator.style.left = "50%";
5296
+ indicator.style.transform = "translateX(-50%)";
4912
5297
  indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
4913
5298
  indicator.style.backdropFilter = "blur(8px)";
4914
5299
  indicator.style.color = "#f8fafc";
@@ -4922,17 +5307,25 @@ var DocPlugin = class {
4922
5307
  indicator.style.userSelect = "none";
4923
5308
  indicator.style.pointerEvents = "none";
4924
5309
  indicator.style.textAlign = "center";
4925
- indicator.style.width = "fit-content";
4926
- indicator.style.margin = "16px auto 0";
4927
5310
  container.appendChild(indicator);
4928
5311
  }
5312
+ let rotation = 0;
5313
+ const applyTransform = () => {
5314
+ const activeCard = pageCards[currentPage - 1];
5315
+ if (activeCard) {
5316
+ activeCard.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5317
+ }
5318
+ };
4929
5319
  const showPage = (pageNum) => {
4930
5320
  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
- }
5321
+ pageCards.forEach((card, idx) => {
5322
+ if (idx + 1 === currentPage) {
5323
+ card.style.display = "block";
5324
+ card.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5325
+ } else {
5326
+ card.style.display = "none";
5327
+ }
5328
+ });
4936
5329
  if (indicator) {
4937
5330
  indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4938
5331
  }
@@ -4941,21 +5334,22 @@ var DocPlugin = class {
4941
5334
  if (totalPages > 1) {
4942
5335
  showPage(1);
4943
5336
  }
4944
- let rotation = 0;
4945
- const calculateFitScale = () => {
4946
- const activeCard = pageCards[currentPage - 1] || wrapper;
4947
- const elW = activeCard.offsetWidth || 850;
4948
- const elH = activeCard.offsetHeight || 1e3;
4949
- const availW = Math.max(200, ctx.container.clientWidth - 48);
4950
- const availH = Math.max(200, ctx.container.clientHeight - 80);
4951
- return Math.min(1.1, Math.min(availW / elW, availH / elH));
4952
- };
4953
- const applyTransform = () => {
4954
- wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
4955
- wrapper.style.transformOrigin = "top center";
5337
+ let fitMode = "width";
5338
+ const calculateFitScale = (mode = fitMode) => {
5339
+ const elW = 816;
5340
+ const elH = 1056;
5341
+ const availW = Math.max(280, ctx.container.clientWidth - 48);
5342
+ const availH = Math.max(280, ctx.container.clientHeight - 80);
5343
+ const sW = availW / elW;
5344
+ const sH = availH / elH;
5345
+ if (mode === "page") {
5346
+ return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
5347
+ }
5348
+ return Math.max(0.65, Math.min(1.05, sW));
4956
5349
  };
4957
5350
  setTimeout(() => {
4958
- scale = calculateFitScale();
5351
+ fitMode = "width";
5352
+ scale = calculateFitScale("width");
4959
5353
  applyTransform();
4960
5354
  }, 60);
4961
5355
  const cleanup = () => {
@@ -4983,7 +5377,8 @@ var DocPlugin = class {
4983
5377
  applyTransform();
4984
5378
  },
4985
5379
  fitToPage: () => {
4986
- scale = calculateFitScale();
5380
+ fitMode = fitMode === "width" ? "page" : "width";
5381
+ scale = calculateFitScale(fitMode);
4987
5382
  rotation = 0;
4988
5383
  applyTransform();
4989
5384
  },
@@ -5111,8 +5506,28 @@ var DocPlugin = class {
5111
5506
  }
5112
5507
  splitIntoPages(text) {
5113
5508
  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];
5509
+ 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);
5510
+ if (explicitParts.length === 0) explicitParts.push(text);
5511
+ const maxLinesPerPage = 48;
5512
+ const finalPages = [];
5513
+ for (const part of explicitParts) {
5514
+ const lines = part.split(/\r?\n/);
5515
+ let currentLines = [];
5516
+ let count = 0;
5517
+ for (const line of lines) {
5518
+ currentLines.push(line);
5519
+ count++;
5520
+ if (count >= maxLinesPerPage) {
5521
+ finalPages.push(currentLines.join("\n"));
5522
+ currentLines = [];
5523
+ count = 0;
5524
+ }
5525
+ }
5526
+ if (currentLines.length > 0) {
5527
+ finalPages.push(currentLines.join("\n"));
5528
+ }
5529
+ }
5530
+ return finalPages.length > 0 ? finalPages : [text];
5116
5531
  }
5117
5532
  /**
5118
5533
  * Format cleaned extracted text into attractive HTML paragraphs, headings, and lists
@@ -5121,17 +5536,65 @@ var DocPlugin = class {
5121
5536
  const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").split("\n");
5122
5537
  let html = "";
5123
5538
  let inList = false;
5124
- for (const rawLine of lines) {
5125
- const line = rawLine.trim();
5539
+ let tableLines = [];
5540
+ const flushTable = () => {
5541
+ if (tableLines.length > 0) {
5542
+ html += '<table style="width: 100%; border-collapse: collapse; margin: 16px 0; font-size: 12px;">';
5543
+ for (const tLine of tableLines) {
5544
+ html += "<tr>";
5545
+ const cols = tLine.split(" ");
5546
+ for (const col of cols) {
5547
+ html += `<td style="border: 1px solid #cbd5e1; padding: 6px 8px;">${DOMPurify6.sanitize(col.trim())}</td>`;
5548
+ }
5549
+ html += "</tr>";
5550
+ }
5551
+ html += "</table>";
5552
+ tableLines = [];
5553
+ }
5554
+ };
5555
+ let i = 0;
5556
+ while (i < lines.length) {
5557
+ let line = lines[i];
5558
+ let tabCount = (line.match(/\t/g) || []).length;
5559
+ if (tabCount > 0) {
5560
+ let consecutiveTableLines = 1;
5561
+ let j = i + 1;
5562
+ while (j < lines.length) {
5563
+ const nextTabCount = (lines[j].match(/\t/g) || []).length;
5564
+ if (nextTabCount === tabCount) {
5565
+ consecutiveTableLines++;
5566
+ j++;
5567
+ } else {
5568
+ break;
5569
+ }
5570
+ }
5571
+ if (consecutiveTableLines >= 3) {
5572
+ if (inList) {
5573
+ html += "</ul>";
5574
+ inList = false;
5575
+ }
5576
+ tableLines = lines.slice(i, j);
5577
+ flushTable();
5578
+ i = j;
5579
+ continue;
5580
+ }
5581
+ }
5582
+ line = line.trim();
5126
5583
  if (!line) {
5127
5584
  if (inList) {
5128
5585
  html += "</ul>";
5129
5586
  inList = false;
5130
5587
  }
5588
+ html += '<div style="height: 1.15em;"></div>';
5589
+ i++;
5590
+ continue;
5591
+ }
5592
+ if (/^[\x00-\x1F\x7F-\x9F]+$/.test(line)) {
5593
+ i++;
5131
5594
  continue;
5132
5595
  }
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) {
5596
+ if ((line.includes("Normal.dot") || line.includes("Microsoft Word") || line.includes("Times New Roman")) && line.length < 30) {
5597
+ i++;
5135
5598
  continue;
5136
5599
  }
5137
5600
  if (line.length < 60 && !line.endsWith(".") && (/^[A-Z0-9\s:_-]+$/.test(line) || line.startsWith("#"))) {
@@ -5139,24 +5602,26 @@ var DocPlugin = class {
5139
5602
  html += "</ul>";
5140
5603
  inList = false;
5141
5604
  }
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>`;
5605
+ 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
5606
  } else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
5144
5607
  if (!inList) {
5145
5608
  html += '<ul style="margin: 8px 0; padding-left: 24px;">';
5146
5609
  inList = true;
5147
5610
  }
5148
5611
  const bulletText = line.replace(/^[•\-\*]\s*/, "");
5149
- html += `<li style="margin: 4px 0; line-height: 1.6;">${DOMPurify2.sanitize(bulletText)}</li>`;
5612
+ html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6.sanitize(bulletText)}</li>`;
5150
5613
  } else {
5151
5614
  if (inList) {
5152
5615
  html += "</ul>";
5153
5616
  inList = false;
5154
5617
  }
5155
- html += `<p style="line-height: 1.7; margin: 10px 0; font-size: 14px; text-align: justify;">${DOMPurify2.sanitize(line)}</p>`;
5618
+ html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6.sanitize(line)}</p>`;
5156
5619
  }
5620
+ i++;
5157
5621
  }
5158
5622
  if (inList) html += "</ul>";
5159
- return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify2.sanitize(filename)})</p>`;
5623
+ flushTable();
5624
+ return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify6.sanitize(filename)})</p>`;
5160
5625
  }
5161
5626
  };
5162
5627
  function docPlugin() {
@@ -5334,7 +5799,7 @@ var PptPlugin = class {
5334
5799
  if (s.pictureUrl) {
5335
5800
  contentHtml = `
5336
5801
  <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;" />
5802
+ <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
5803
  </div>
5339
5804
  `;
5340
5805
  } else if (s.tableColumns.length > 0) {
@@ -5344,7 +5809,7 @@ var PptPlugin = class {
5344
5809
  <table style="width: 100%; border-collapse: collapse; border: 1px solid #cbd5e1; border-radius: 6px; overflow: hidden; background: #ffffff;">
5345
5810
  <thead>
5346
5811
  <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("")}
5812
+ ${cols.map((c) => `<th style="padding: 12px 16px; border: 1px solid #cbd5e1; text-align: left;">${DOMPurify6.sanitize(c)}</th>`).join("")}
5348
5813
  </tr>
5349
5814
  </thead>
5350
5815
  <tbody>
@@ -5360,7 +5825,7 @@ var PptPlugin = class {
5360
5825
  } else {
5361
5826
  const pTags = s.paragraphs.map((p) => {
5362
5827
  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("");
5828
+ 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
5829
  }).join("");
5365
5830
  contentHtml = `
5366
5831
  <div style="flex: 1; overflow: auto; padding: 4px 8px; display: flex; flex-direction: column; justify-content: flex-start;">
@@ -5373,11 +5838,11 @@ var PptPlugin = class {
5373
5838
  <!-- Header Banner matching PowerPoint design -->
5374
5839
  <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
5840
  <h1 style="margin: 0; font-size: 26px; font-weight: 700; color: #1e293b; letter-spacing: -0.3px;">
5376
- ${DOMPurify2.sanitize(s.title)}
5841
+ ${DOMPurify6.sanitize(s.title)}
5377
5842
  </h1>
5378
5843
  ${s.subtitle ? `
5379
5844
  <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)}
5845
+ ${DOMPurify6.sanitize(s.subtitle)}
5381
5846
  </span>
5382
5847
  ` : `
5383
5848
  <span style="font-size: 12px; color: #365314; font-weight: 600;">