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