@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/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import DOMPurify2 from 'dompurify';
1
+ import DOMPurify6 from 'dompurify';
2
2
  import * as pdfjsLib from 'pdfjs-dist';
3
3
  import * as docx from 'docx-preview';
4
4
  import { unzipSync, strFromU8, unzip } from 'fflate';
@@ -356,7 +356,7 @@ async function sourceToArrayBuffer(source, signal) {
356
356
  if (magicMime === "application/zip") {
357
357
  const ooxmlMime = detectOoxmlType(buffer);
358
358
  if (ooxmlMime && ooxmlMime !== "application/zip") {
359
- metadata.mimeType = ooxmlMime;
359
+ metadata.mimeType = metadata.mimeType ?? ooxmlMime;
360
360
  if (ooxmlMime.includes("wordprocessing")) metadata.extension = metadata.extension ?? ".docx";
361
361
  else if (ooxmlMime.includes("spreadsheet")) metadata.extension = metadata.extension ?? ".xlsx";
362
362
  else if (ooxmlMime.includes("presentation")) metadata.extension = metadata.extension ?? ".pptx";
@@ -385,12 +385,12 @@ async function sourceToArrayBuffer(source, signal) {
385
385
  return { buffer, metadata };
386
386
  }
387
387
  function sanitizeHTML(html) {
388
- return DOMPurify2.sanitize(html, {
388
+ return DOMPurify6.sanitize(html, {
389
389
  USE_PROFILES: { html: true }
390
390
  });
391
391
  }
392
392
  function sanitizeSVG(svg) {
393
- return DOMPurify2.sanitize(svg, {
393
+ return DOMPurify6.sanitize(svg, {
394
394
  USE_PROFILES: { svg: true, svgFilters: true }
395
395
  });
396
396
  }
@@ -476,7 +476,8 @@ function createElement(tag, attrs, ...children) {
476
476
  }
477
477
  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>`;
478
478
  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>`;
479
- 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>`;
479
+ 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>`;
480
+ 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>`;
480
481
  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>`;
481
482
  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>`;
482
483
  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>`;
@@ -496,6 +497,7 @@ var ICON_MAP = {
496
497
  "zoom-in": ICON_ZOOM_IN,
497
498
  "zoom-out": ICON_ZOOM_OUT,
498
499
  "fit-page": ICON_FIT_PAGE,
500
+ "fit-width": ICON_FIT_WIDTH,
499
501
  "fit-slide": ICON_FIT_PAGE,
500
502
  "rotate-cw": ICON_ROTATE_CW,
501
503
  "rotate-ccw": ICON_ROTATE_CCW,
@@ -1442,6 +1444,7 @@ var PdfPlugin = class {
1442
1444
  let currentPage = 1;
1443
1445
  let zoomScale = 1;
1444
1446
  let rotation = 0;
1447
+ let fitMode = "width";
1445
1448
  let currentRenderTask = null;
1446
1449
  const renderPage = async (pageNum) => {
1447
1450
  if (currentRenderTask) {
@@ -1461,11 +1464,16 @@ var PdfPlugin = class {
1461
1464
  const containerWidth = container.clientWidth || 900;
1462
1465
  const containerHeight = container.clientHeight || 700;
1463
1466
  const unscaledVp = page.getViewport({ scale: 1, rotation });
1464
- const availWidth = Math.max(100, containerWidth - 48);
1465
- const availHeight = Math.max(100, containerHeight - 88);
1467
+ const availWidth = Math.max(280, containerWidth - 48);
1468
+ const availHeight = Math.max(280, containerHeight - 88);
1466
1469
  const scaleW = availWidth / unscaledVp.width;
1467
1470
  const scaleH = availHeight / unscaledVp.height;
1468
- const fitScale = Math.min(scaleW, scaleH);
1471
+ let fitScale;
1472
+ if (fitMode === "page") {
1473
+ fitScale = Math.max(0.5, Math.min(scaleW, scaleH));
1474
+ } else {
1475
+ fitScale = Math.max(0.65, Math.min(1.15, scaleW));
1476
+ }
1469
1477
  const effectiveScale = (fitScale > 0 ? fitScale : 1) * zoomScale;
1470
1478
  const pixelRatio = window.devicePixelRatio || 1;
1471
1479
  const viewport = page.getViewport({ scale: effectiveScale, rotation });
@@ -1491,7 +1499,19 @@ var PdfPlugin = class {
1491
1499
  }
1492
1500
  };
1493
1501
  await renderPage(1);
1502
+ let resizeTimer = null;
1503
+ const resizeObserver = new ResizeObserver(() => {
1504
+ if (resizeTimer) clearTimeout(resizeTimer);
1505
+ resizeTimer = setTimeout(() => {
1506
+ if (Math.abs(zoomScale - 1) < 0.05) {
1507
+ renderPage(currentPage);
1508
+ }
1509
+ }, 150);
1510
+ });
1511
+ resizeObserver.observe(container);
1494
1512
  const cleanup = () => {
1513
+ resizeObserver.disconnect();
1514
+ if (resizeTimer) clearTimeout(resizeTimer);
1495
1515
  if (currentRenderTask) {
1496
1516
  try {
1497
1517
  currentRenderTask.cancel();
@@ -1522,6 +1542,7 @@ var PdfPlugin = class {
1522
1542
  renderPage(currentPage);
1523
1543
  },
1524
1544
  fitToPage: () => {
1545
+ fitMode = fitMode === "width" ? "page" : "width";
1525
1546
  zoomScale = 1;
1526
1547
  rotation = 0;
1527
1548
  renderPage(currentPage);
@@ -2075,8 +2096,53 @@ var DocxPlugin = class {
2075
2096
  }
2076
2097
  }
2077
2098
  }
2078
- const sections = wrapper.querySelectorAll("section.docx");
2079
- const cards = wrapper.querySelectorAll(".fp-docx-page-card");
2099
+ let sections = Array.from(wrapper.querySelectorAll("section.docx"));
2100
+ const cards = Array.from(wrapper.querySelectorAll(".fp-docx-page-card"));
2101
+ if (sections.length === 1 && cards.length === 0) {
2102
+ const singleSec = sections[0];
2103
+ const pageH = 1056;
2104
+ const secH = singleSec.offsetHeight || singleSec.scrollHeight;
2105
+ if (secH > pageH * 1.25) {
2106
+ const children = Array.from(singleSec.children);
2107
+ if (children.length > 1) {
2108
+ const parent = singleSec.parentElement || wrapper;
2109
+ const newSections = [singleSec];
2110
+ singleSec.innerHTML = "";
2111
+ singleSec.style.minHeight = `${pageH}px`;
2112
+ singleSec.style.maxHeight = `${pageH}px`;
2113
+ singleSec.style.overflow = "hidden";
2114
+ singleSec.style.boxSizing = "border-box";
2115
+ let curSec = singleSec;
2116
+ let curH = 0;
2117
+ const maxH = pageH - 96;
2118
+ for (let i = 0; i < children.length; i++) {
2119
+ const child = children[i];
2120
+ curSec.appendChild(child);
2121
+ const chH = child.offsetHeight || 28;
2122
+ curH += chH;
2123
+ if (curH >= maxH && i < children.length - 1) {
2124
+ const nextSec = document.createElement("section");
2125
+ nextSec.className = singleSec.className;
2126
+ nextSec.style.cssText = singleSec.style.cssText;
2127
+ nextSec.style.width = singleSec.style.width || "816px";
2128
+ nextSec.style.minHeight = `${pageH}px`;
2129
+ nextSec.style.maxHeight = `${pageH}px`;
2130
+ nextSec.style.overflow = "hidden";
2131
+ nextSec.style.boxSizing = "border-box";
2132
+ nextSec.style.backgroundColor = "#ffffff";
2133
+ nextSec.style.boxShadow = "0 4px 24px rgba(0, 0, 0, 0.08)";
2134
+ nextSec.style.borderRadius = "4px";
2135
+ nextSec.style.marginBottom = "24px";
2136
+ parent.appendChild(nextSec);
2137
+ newSections.push(nextSec);
2138
+ curSec = nextSec;
2139
+ curH = 0;
2140
+ }
2141
+ }
2142
+ sections = newSections;
2143
+ }
2144
+ }
2145
+ }
2080
2146
  const pageElements = sections.length > 0 ? sections : cards;
2081
2147
  const totalPages = Math.max(1, pageElements.length);
2082
2148
  let currentPage = 1;
@@ -2120,22 +2186,27 @@ var DocxPlugin = class {
2120
2186
  }
2121
2187
  scale = 1;
2122
2188
  let rotation = 0;
2123
- const calculateFitScale = () => {
2189
+ let fitMode = "width";
2190
+ const calculateFitScale = (mode = fitMode) => {
2124
2191
  const activeEl = pageElements[currentPage - 1] || wrapper.firstElementChild || wrapper;
2125
2192
  const elW = activeEl.offsetWidth || 816;
2126
- const elH = activeEl.offsetHeight || 1056;
2127
- const availW = Math.max(200, ctx.container.clientWidth - 48);
2128
- const availH = Math.max(200, ctx.container.clientHeight - 80);
2193
+ const singlePageH = Math.min(activeEl.offsetHeight || 1056, Math.round(elW * 1.32));
2194
+ const availW = Math.max(280, ctx.container.clientWidth - 48);
2195
+ const availH = Math.max(280, ctx.container.clientHeight - 80);
2129
2196
  const sW = availW / elW;
2130
- const sH = availH / elH;
2131
- return Math.min(1.1, Math.min(sW, sH));
2197
+ const sH = availH / singlePageH;
2198
+ if (mode === "page") {
2199
+ return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
2200
+ }
2201
+ return Math.max(0.65, Math.min(1.05, sW));
2132
2202
  };
2133
2203
  const applyTransform = () => {
2134
2204
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
2135
2205
  wrapper.style.transformOrigin = "top center";
2136
2206
  };
2137
2207
  setTimeout(() => {
2138
- scale = calculateFitScale();
2208
+ fitMode = "width";
2209
+ scale = calculateFitScale("width");
2139
2210
  applyTransform();
2140
2211
  }, 60);
2141
2212
  const cleanup = () => {
@@ -2169,7 +2240,8 @@ var DocxPlugin = class {
2169
2240
  applyTransform();
2170
2241
  },
2171
2242
  fitToPage: () => {
2172
- scale = calculateFitScale();
2243
+ fitMode = fitMode === "width" ? "page" : "width";
2244
+ scale = calculateFitScale(fitMode);
2173
2245
  rotation = 0;
2174
2246
  applyTransform();
2175
2247
  },
@@ -2239,90 +2311,149 @@ var DocxPlugin = class {
2239
2311
  console.warn("[DocxPlugin] Error parsing relationships:", relsErr);
2240
2312
  }
2241
2313
  }
2242
- const card = document.createElement("div");
2243
- card.className = "fp-docx-page-card";
2244
- card.style.backgroundColor = "#ffffff";
2245
- card.style.borderRadius = "6px";
2246
- card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
2247
- card.style.padding = "48px 56px";
2248
- card.style.fontFamily = 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
2249
- card.style.color = "#1e293b";
2250
- card.style.lineHeight = "1.6";
2314
+ const sectPrs = Array.from(doc.getElementsByTagNameNS("*", "sectPr"));
2315
+ const lastSectPr = sectPrs[sectPrs.length - 1];
2316
+ let defaultW = 12240;
2317
+ let defaultH = 15840;
2318
+ let margins = { top: 1440, right: 1440, bottom: 1440, left: 1440 };
2319
+ if (lastSectPr) {
2320
+ const pgSz = lastSectPr.getElementsByTagNameNS("*", "pgSz")[0];
2321
+ if (pgSz) {
2322
+ defaultW = parseInt(pgSz.getAttribute("w:w") || pgSz.getAttribute("w") || "12240", 10);
2323
+ defaultH = parseInt(pgSz.getAttribute("w:h") || pgSz.getAttribute("h") || "15840", 10);
2324
+ }
2325
+ const pgMar = lastSectPr.getElementsByTagNameNS("*", "pgMar")[0];
2326
+ if (pgMar) {
2327
+ margins.top = parseInt(pgMar.getAttribute("w:top") || pgMar.getAttribute("top") || "1440", 10);
2328
+ margins.bottom = parseInt(pgMar.getAttribute("w:bottom") || pgMar.getAttribute("bottom") || "1440", 10);
2329
+ margins.left = parseInt(pgMar.getAttribute("w:left") || pgMar.getAttribute("left") || "1440", 10);
2330
+ margins.right = parseInt(pgMar.getAttribute("w:right") || pgMar.getAttribute("right") || "1440", 10);
2331
+ }
2332
+ }
2333
+ const createPageCard = () => {
2334
+ const card = document.createElement("div");
2335
+ card.className = "fp-docx-page-card";
2336
+ card.style.backgroundColor = "#ffffff";
2337
+ card.style.borderRadius = "4px";
2338
+ card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
2339
+ card.style.fontFamily = 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
2340
+ card.style.color = "#1e293b";
2341
+ card.style.lineHeight = "1.6";
2342
+ card.style.boxSizing = "border-box";
2343
+ card.style.overflow = "hidden";
2344
+ card.style.position = "relative";
2345
+ card.style.marginBottom = "24px";
2346
+ card.style.width = `${defaultW / 15}px`;
2347
+ card.style.height = `${defaultH / 15}px`;
2348
+ card.style.padding = `${margins.top / 15}px ${margins.right / 15}px ${margins.bottom / 15}px ${margins.left / 15}px`;
2349
+ return card;
2350
+ };
2351
+ let currentCard = createPageCard();
2352
+ let currentHtml = "";
2353
+ const pages = [{ card: currentCard, html: "" }];
2354
+ const flushHtml = () => {
2355
+ pages[pages.length - 1].html += currentHtml;
2356
+ currentHtml = "";
2357
+ };
2358
+ const newPage = () => {
2359
+ flushHtml();
2360
+ currentCard = createPageCard();
2361
+ pages.push({ card: currentCard, html: "" });
2362
+ };
2251
2363
  const body = doc.getElementsByTagNameNS("*", "body")[0] || doc.documentElement;
2252
- let html = "";
2253
2364
  for (const child of Array.from(body.children)) {
2254
2365
  const tag = child.localName || child.nodeName.split(":").pop();
2255
2366
  if (tag === "p") {
2256
2367
  const pStyle = child.getElementsByTagNameNS("*", "pStyle")[0];
2257
2368
  const styleVal = pStyle?.getAttribute("w:val") || pStyle?.getAttribute("val") || "";
2258
2369
  const numPr = child.getElementsByTagNameNS("*", "numPr")[0];
2259
- const textContent = this.extractParagraphHtml(child, imageMap);
2260
- if (!textContent.trim()) {
2261
- html += '<div style="height: 10px;"></div>';
2262
- continue;
2263
- }
2264
- const lowerStyle = styleVal.toLowerCase();
2265
- if (lowerStyle.includes("title")) {
2266
- 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>`;
2267
- } else if (lowerStyle.includes("heading1") || styleVal === "1") {
2268
- html += `<h2 style="font-size: 22px; font-weight: 700; color: #1e40af; margin: 20px 0 10px;">${textContent}</h2>`;
2269
- } else if (lowerStyle.includes("heading2") || styleVal === "2") {
2270
- html += `<h3 style="font-size: 18px; font-weight: 600; color: #2563eb; margin: 16px 0 8px;">${textContent}</h3>`;
2271
- } else if (lowerStyle.includes("heading3") || styleVal === "3") {
2272
- html += `<h4 style="font-size: 15px; font-weight: 600; color: #334155; margin: 12px 0 6px;">${textContent}</h4>`;
2273
- } else if (numPr) {
2274
- 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>`;
2275
- } else {
2276
- html += `<p style="margin: 8px 0; font-size: 14px;">${textContent}</p>`;
2370
+ const chunks = this.extractParagraphChunks(child, imageMap);
2371
+ for (let i = 0; i < chunks.length; i++) {
2372
+ if (i > 0) {
2373
+ newPage();
2374
+ }
2375
+ const textContent = chunks[i];
2376
+ if (!textContent.trim() && !textContent.includes("<img")) {
2377
+ currentHtml += '<div style="height: 10px;"></div>';
2378
+ continue;
2379
+ }
2380
+ const lowerStyle = styleVal.toLowerCase();
2381
+ if (lowerStyle.includes("title")) {
2382
+ 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>`;
2383
+ } else if (lowerStyle.includes("heading1") || styleVal === "1") {
2384
+ currentHtml += `<h2 style="font-size: 22px; font-weight: 700; color: #1e40af; margin: 20px 0 10px;">${textContent}</h2>`;
2385
+ } else if (lowerStyle.includes("heading2") || styleVal === "2") {
2386
+ currentHtml += `<h3 style="font-size: 18px; font-weight: 600; color: #2563eb; margin: 16px 0 8px;">${textContent}</h3>`;
2387
+ } else if (lowerStyle.includes("heading3") || styleVal === "3") {
2388
+ currentHtml += `<h4 style="font-size: 15px; font-weight: 600; color: #334155; margin: 12px 0 6px;">${textContent}</h4>`;
2389
+ } else if (numPr) {
2390
+ 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>`;
2391
+ } else {
2392
+ currentHtml += `<p style="margin: 8px 0; font-size: 14px;">${textContent}</p>`;
2393
+ }
2277
2394
  }
2278
2395
  } else if (tag === "tbl") {
2279
- html += '<table style="width: 100%; border-collapse: collapse; margin: 20px 0; border: 1px solid #cbd5e1;">';
2396
+ currentHtml += '<table style="width: 100%; border-collapse: collapse; margin: 20px 0; border: 1px solid #cbd5e1;">';
2280
2397
  const rows = Array.from(child.getElementsByTagNameNS("*", "tr"));
2281
2398
  rows.forEach((tr, rIdx) => {
2282
- html += `<tr style="${rIdx === 0 ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
2399
+ currentHtml += `<tr style="${rIdx === 0 ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
2283
2400
  const cells = Array.from(tr.getElementsByTagNameNS("*", "tc"));
2284
2401
  cells.forEach((tc) => {
2285
2402
  const cellText = this.extractParagraphHtml(tc, imageMap);
2286
- html += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px; font-size: 13px;">${cellText || "&nbsp;"}</td>`;
2403
+ currentHtml += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px; font-size: 13px;">${cellText || "&nbsp;"}</td>`;
2287
2404
  });
2288
- html += "</tr>";
2405
+ currentHtml += "</tr>";
2289
2406
  });
2290
- html += "</table>";
2407
+ currentHtml += "</table>";
2291
2408
  }
2292
2409
  }
2293
- card.innerHTML = DOMPurify2.sanitize(html, {
2294
- ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "s", "strike", "img", "div", "br"],
2295
- ADD_ATTR: ["style", "src", "alt", "colspan", "rowspan"]
2296
- });
2297
- wrapper.appendChild(card);
2410
+ flushHtml();
2411
+ for (const page of pages) {
2412
+ page.card.innerHTML = DOMPurify6.sanitize(page.html, {
2413
+ ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "s", "strike", "img", "div", "br"],
2414
+ ADD_ATTR: ["style", "src", "alt", "colspan", "rowspan"]
2415
+ });
2416
+ wrapper.appendChild(page.card);
2417
+ }
2298
2418
  }
2299
2419
  extractParagraphHtml(pElement, imageMap = {}) {
2300
- let result = "";
2420
+ return this.extractParagraphChunks(pElement, imageMap).join("<br/>");
2421
+ }
2422
+ extractParagraphChunks(pElement, imageMap = {}) {
2423
+ const chunks = [""];
2424
+ let currentChunkIndex = 0;
2301
2425
  const drawings = Array.from(pElement.getElementsByTagNameNS("*", "drawing"));
2302
2426
  for (const drawing of drawings) {
2303
2427
  const blip = drawing.getElementsByTagNameNS("*", "blip")[0];
2304
2428
  const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
2305
2429
  if (rId && imageMap[rId]) {
2306
- result += `<div style="text-align:center; margin: 12px 0;"><img src="${imageMap[rId]}" style="max-width: 100%; height: auto; border-radius: 4px;" /></div>`;
2430
+ chunks[currentChunkIndex] += `<div style="text-align:center; margin: 12px 0;"><img src="${imageMap[rId]}" style="max-width: 100%; height: auto; border-radius: 4px;" /></div>`;
2307
2431
  }
2308
2432
  }
2309
2433
  const runs = Array.from(pElement.getElementsByTagNameNS("*", "r"));
2310
- if (runs.length === 0 && !result) {
2311
- return DOMPurify2.sanitize(pElement.textContent || "");
2434
+ if (runs.length === 0 && !chunks[currentChunkIndex]) {
2435
+ chunks[currentChunkIndex] = DOMPurify6.sanitize(pElement.textContent || "");
2436
+ return chunks;
2312
2437
  }
2313
2438
  for (const r of runs) {
2314
2439
  const blip = r.getElementsByTagNameNS("*", "blip")[0] || r.getElementsByTagNameNS("*", "imagedata")[0];
2315
2440
  const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
2316
2441
  if (rId && imageMap[rId]) {
2317
- result += `<img src="${imageMap[rId]}" style="max-width: 100%; height: auto; display: inline-block; margin: 4px;" />`;
2442
+ chunks[currentChunkIndex] += `<img src="${imageMap[rId]}" style="max-width: 100%; height: auto; display: inline-block; margin: 4px;" />`;
2318
2443
  }
2319
- const brs = r.getElementsByTagNameNS("*", "br");
2320
- for (let i = 0; i < brs.length; i++) {
2321
- result += "<br/>";
2444
+ const brs = Array.from(r.getElementsByTagNameNS("*", "br"));
2445
+ for (const br of brs) {
2446
+ const type = br.getAttribute("w:type") || br.getAttribute("type");
2447
+ if (type === "page") {
2448
+ chunks.push("");
2449
+ currentChunkIndex++;
2450
+ } else {
2451
+ chunks[currentChunkIndex] += "<br/>";
2452
+ }
2322
2453
  }
2323
2454
  const tabs = r.getElementsByTagNameNS("*", "tab");
2324
2455
  if (tabs.length > 0) {
2325
- result += "&emsp;";
2456
+ chunks[currentChunkIndex] += "&emsp;";
2326
2457
  }
2327
2458
  const rPr = r.getElementsByTagNameNS("*", "rPr")[0];
2328
2459
  const isBold = !!rPr?.getElementsByTagNameNS("*", "b")[0];
@@ -2336,7 +2467,7 @@ var DocxPlugin = class {
2336
2467
  const texts = Array.from(r.getElementsByTagNameNS("*", "t"));
2337
2468
  let text = texts.map((t) => t.textContent || "").join("");
2338
2469
  if (!text) continue;
2339
- text = DOMPurify2.sanitize(text);
2470
+ text = DOMPurify6.sanitize(text);
2340
2471
  let styles = "";
2341
2472
  if (isBold) styles += "font-weight: bold; ";
2342
2473
  if (isItalic) styles += "font-style: italic; ";
@@ -2348,12 +2479,12 @@ var DocxPlugin = class {
2348
2479
  if (!isNaN(pt) && pt > 0) styles += `font-size: ${pt}pt; `;
2349
2480
  }
2350
2481
  if (styles) {
2351
- result += `<span style="${styles}">${text}</span>`;
2482
+ chunks[currentChunkIndex] += `<span style="${styles}">${text}</span>`;
2352
2483
  } else {
2353
- result += text;
2484
+ chunks[currentChunkIndex] += text;
2354
2485
  }
2355
2486
  }
2356
- return result;
2487
+ return chunks;
2357
2488
  }
2358
2489
  renderBinaryDocFallback(ctx, wrapper) {
2359
2490
  const card = document.createElement("div");
@@ -2370,14 +2501,14 @@ var DocxPlugin = class {
2370
2501
  const wordDocStream = cfbf.readStream("WordDocument");
2371
2502
  if (wordDocStream && wordDocStream.length >= 512) {
2372
2503
  const text2 = this.extractReadableStrings(wordDocStream);
2373
- card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2.sanitize(text2)}</div>`;
2504
+ card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify6.sanitize(text2)}</div>`;
2374
2505
  wrapper.appendChild(card);
2375
2506
  return;
2376
2507
  }
2377
2508
  } catch {
2378
2509
  }
2379
2510
  const text = this.extractReadableStrings(new Uint8Array(ctx.buffer));
2380
- card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2.sanitize(text)}</div>`;
2511
+ card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify6.sanitize(text)}</div>`;
2381
2512
  wrapper.appendChild(card);
2382
2513
  }
2383
2514
  extractReadableStrings(bytes) {
@@ -2554,11 +2685,9 @@ var ExcelPlugin = class {
2554
2685
  const calculateFitScale = () => {
2555
2686
  const table = contentArea.querySelector("table");
2556
2687
  if (!table) return 1;
2557
- const availW = Math.max(200, container.clientWidth - 48);
2558
- const availH = Math.max(200, container.clientHeight - 80);
2688
+ const availW = Math.max(280, container.clientWidth - 48);
2559
2689
  const tW = table.offsetWidth || 800;
2560
- const tH = table.offsetHeight || 600;
2561
- return Math.min(1, Math.min(availW / tW, availH / tH));
2690
+ return Math.max(0.65, Math.min(1, availW / tW));
2562
2691
  };
2563
2692
  try {
2564
2693
  wb = XLSX.read(new Uint8Array(ctx.buffer), { type: "array", cellDates: true });
@@ -2976,41 +3105,94 @@ var CodePlugin = class {
2976
3105
  async render(ctx) {
2977
3106
  const decoder = new TextDecoder("utf-8");
2978
3107
  const fullText = decoder.decode(ctx.buffer);
2979
- const pageSplitRegex = /(?:\f|\x0C|(?:\r?\n|^)\s*[-=_]{3,}\s*(?:PAGE|Page|page break|Page Break)[\s\d\w-]*[-=_]{3,}\s*(?:\r?\n|$))/i;
2980
- const rawPages = fullText.split(pageSplitRegex).map((p) => p.trim()).filter((p) => p.length > 0);
3108
+ const extRaw = ctx.metadata.extension?.toLowerCase();
3109
+ const mimeRaw = ctx.metadata.mimeType?.toLowerCase();
3110
+ const isTxt = extRaw === ".txt" || mimeRaw === "text/plain" && (!extRaw || !this.extensions.filter((e) => e !== ".txt").includes(extRaw));
3111
+ let rawPages = [];
3112
+ if (isTxt) {
3113
+ const explicitPages = fullText.split(/(?:\f|\x0C)/);
3114
+ for (const ep of explicitPages) {
3115
+ const lines = ep.split(/\r?\n/);
3116
+ let currentChunk = [];
3117
+ for (let i = 0; i < lines.length; i++) {
3118
+ currentChunk.push(lines[i]);
3119
+ if (currentChunk.length >= 46) {
3120
+ rawPages.push(currentChunk.join("\n"));
3121
+ currentChunk = [];
3122
+ }
3123
+ }
3124
+ if (currentChunk.length > 0 || lines.length === 0) {
3125
+ rawPages.push(currentChunk.join("\n"));
3126
+ }
3127
+ }
3128
+ if (rawPages.length === 0) rawPages = [""];
3129
+ } else {
3130
+ const pageSplitRegex = /(?:\f|\x0C|(?:\r?\n|^)\s*[-=_]{3,}\s*(?:PAGE|Page|page break|Page Break)[\s\d\w-]*[-=_]{3,}\s*(?:\r?\n|$))/i;
3131
+ rawPages = fullText.split(pageSplitRegex).map((p) => p.trim()).filter((p) => p.length > 0);
3132
+ }
2981
3133
  const totalPages = Math.max(1, rawPages.length);
2982
3134
  let currentPage = 1;
2983
3135
  const container = document.createElement("div");
2984
3136
  container.style.width = "100%";
2985
3137
  container.style.height = "100%";
2986
3138
  container.style.overflow = "auto";
2987
- container.style.backgroundColor = "#1e1e1e";
2988
- container.style.color = "#d4d4d4";
2989
- container.style.padding = "16px";
2990
- container.style.boxSizing = "border-box";
2991
3139
  let fontSize = 13;
2992
3140
  let rotation = 0;
3141
+ let zoomLevel = 1;
2993
3142
  const pre = document.createElement("pre");
2994
- pre.style.margin = "0";
2995
- pre.style.fontFamily = "Consolas, Menlo, Monaco, monospace";
2996
- pre.style.fontSize = `${fontSize}px`;
2997
- pre.style.lineHeight = "1.5";
2998
- pre.style.whiteSpace = "pre-wrap";
2999
- pre.style.wordBreak = "break-all";
3000
3143
  const code = document.createElement("code");
3144
+ if (isTxt) {
3145
+ container.style.backgroundColor = "#f1f5f9";
3146
+ container.style.padding = "32px";
3147
+ container.style.boxSizing = "border-box";
3148
+ container.style.display = "flex";
3149
+ container.style.flexDirection = "column";
3150
+ container.style.alignItems = "center";
3151
+ pre.style.margin = "0";
3152
+ pre.style.fontFamily = "Consolas, 'Courier New', monospace";
3153
+ pre.style.fontSize = "13px";
3154
+ pre.style.color = "#1e293b";
3155
+ pre.style.lineHeight = "1.5";
3156
+ pre.style.whiteSpace = "pre-wrap";
3157
+ pre.style.wordBreak = "break-word";
3158
+ pre.style.backgroundColor = "#ffffff";
3159
+ pre.style.width = "816px";
3160
+ pre.style.minHeight = "1056px";
3161
+ pre.style.padding = "72px 56px";
3162
+ pre.style.boxSizing = "border-box";
3163
+ pre.style.boxShadow = "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)";
3164
+ pre.style.borderRadius = "4px";
3165
+ pre.style.transformOrigin = "top center";
3166
+ } else {
3167
+ container.style.backgroundColor = "#1e1e1e";
3168
+ container.style.color = "#d4d4d4";
3169
+ container.style.padding = "16px";
3170
+ container.style.boxSizing = "border-box";
3171
+ pre.style.margin = "0";
3172
+ pre.style.fontFamily = "Consolas, Menlo, Monaco, monospace";
3173
+ pre.style.fontSize = `${fontSize}px`;
3174
+ pre.style.lineHeight = "1.5";
3175
+ pre.style.whiteSpace = "pre-wrap";
3176
+ pre.style.wordBreak = "break-all";
3177
+ pre.style.transformOrigin = "top left";
3178
+ }
3001
3179
  const ext = (ctx.metadata.extension || "").replace(".", "");
3002
3180
  const renderCodePage = (text) => {
3003
- try {
3004
- if (ext && hljs.getLanguage(ext)) {
3005
- code.innerHTML = hljs.highlight(text, { language: ext }).value;
3006
- } else {
3007
- code.innerHTML = hljs.highlightAuto(text).value;
3008
- }
3009
- } catch {
3181
+ if (isTxt) {
3010
3182
  code.textContent = text;
3183
+ } else {
3184
+ try {
3185
+ if (ext && hljs.getLanguage(ext)) {
3186
+ code.innerHTML = hljs.highlight(text, { language: ext }).value;
3187
+ } else {
3188
+ code.innerHTML = hljs.highlightAuto(text).value;
3189
+ }
3190
+ } catch {
3191
+ code.textContent = text;
3192
+ }
3011
3193
  }
3012
3194
  };
3013
- renderCodePage(rawPages[0] || fullText);
3195
+ renderCodePage(rawPages[0] || (isTxt ? "" : fullText));
3014
3196
  pre.appendChild(code);
3015
3197
  container.appendChild(pre);
3016
3198
  let indicator = null;
@@ -3019,15 +3201,22 @@ var CodePlugin = class {
3019
3201
  indicator.className = "fp-code-page-indicator";
3020
3202
  indicator.style.position = "sticky";
3021
3203
  indicator.style.bottom = "16px";
3022
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
3023
- indicator.style.backdropFilter = "blur(8px)";
3024
- indicator.style.color = "#f8fafc";
3204
+ if (isTxt) {
3205
+ indicator.style.backgroundColor = "rgba(255, 255, 255, 0.9)";
3206
+ indicator.style.color = "#334155";
3207
+ indicator.style.border = "1px solid #e2e8f0";
3208
+ indicator.style.boxShadow = "0 1px 3px rgba(0,0,0,0.1)";
3209
+ } else {
3210
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
3211
+ indicator.style.color = "#f8fafc";
3212
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
3213
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
3214
+ indicator.style.backdropFilter = "blur(8px)";
3215
+ }
3025
3216
  indicator.style.fontSize = "12px";
3026
3217
  indicator.style.fontWeight = "600";
3027
3218
  indicator.style.padding = "5px 14px";
3028
3219
  indicator.style.borderRadius = "20px";
3029
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
3030
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
3031
3220
  indicator.style.zIndex = "10";
3032
3221
  indicator.style.userSelect = "none";
3033
3222
  indicator.style.pointerEvents = "none";
@@ -3038,7 +3227,7 @@ var CodePlugin = class {
3038
3227
  }
3039
3228
  const showPage = (pageNum) => {
3040
3229
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
3041
- renderCodePage(rawPages[currentPage - 1] || fullText);
3230
+ renderCodePage(rawPages[currentPage - 1] || (isTxt ? "" : fullText));
3042
3231
  if (indicator) {
3043
3232
  indicator.textContent = `Page ${currentPage} of ${totalPages}`;
3044
3233
  }
@@ -3054,39 +3243,57 @@ var CodePlugin = class {
3054
3243
  ctx.container.innerHTML = "";
3055
3244
  };
3056
3245
  ctx.signal.addEventListener("abort", cleanup);
3246
+ const updateTransform = () => {
3247
+ if (isTxt) {
3248
+ pre.style.transform = `scale(${zoomLevel}) rotate(${rotation}deg)`;
3249
+ } else {
3250
+ pre.style.transform = `rotate(${rotation}deg)`;
3251
+ pre.style.fontSize = `${fontSize}px`;
3252
+ }
3253
+ };
3057
3254
  return {
3058
3255
  destroy: cleanup,
3059
3256
  getPageCount: () => totalPages,
3060
3257
  getCurrentPage: () => currentPage,
3061
3258
  goToPage: (page) => showPage(page),
3062
3259
  zoomIn: () => {
3063
- fontSize = Math.min(32, fontSize + 2);
3064
- pre.style.fontSize = `${fontSize}px`;
3260
+ if (isTxt) {
3261
+ zoomLevel = Math.min(3, zoomLevel + 0.1);
3262
+ } else {
3263
+ fontSize = Math.min(32, fontSize + 2);
3264
+ }
3265
+ updateTransform();
3065
3266
  },
3066
3267
  zoomOut: () => {
3067
- fontSize = Math.max(8, fontSize - 2);
3068
- pre.style.fontSize = `${fontSize}px`;
3268
+ if (isTxt) {
3269
+ zoomLevel = Math.max(0.1, zoomLevel - 0.1);
3270
+ } else {
3271
+ fontSize = Math.max(8, fontSize - 2);
3272
+ }
3273
+ updateTransform();
3069
3274
  },
3070
- getZoom: () => fontSize / 13,
3275
+ getZoom: () => isTxt ? zoomLevel : fontSize / 13,
3071
3276
  setZoom: (level) => {
3072
- fontSize = Math.round(13 * level);
3073
- pre.style.fontSize = `${fontSize}px`;
3277
+ if (isTxt) {
3278
+ zoomLevel = level;
3279
+ } else {
3280
+ fontSize = Math.round(13 * level);
3281
+ }
3282
+ updateTransform();
3074
3283
  },
3075
3284
  fitToPage: () => {
3076
3285
  fontSize = 13;
3077
3286
  rotation = 0;
3078
- pre.style.fontSize = "13px";
3079
- pre.style.transform = "none";
3287
+ zoomLevel = isTxt ? Math.max(0.65, Math.min(1.05, (container.clientWidth - 48) / 816)) : 1;
3288
+ updateTransform();
3080
3289
  },
3081
3290
  rotateCW: () => {
3082
3291
  rotation = (rotation + 90) % 360;
3083
- pre.style.transform = `rotate(${rotation}deg)`;
3084
- pre.style.transformOrigin = "top left";
3292
+ updateTransform();
3085
3293
  },
3086
3294
  rotateCCW: () => {
3087
3295
  rotation = (rotation - 90 + 360) % 360;
3088
- pre.style.transform = `rotate(${rotation}deg)`;
3089
- pre.style.transformOrigin = "top left";
3296
+ updateTransform();
3090
3297
  },
3091
3298
  download: () => {
3092
3299
  const mimeType = ctx.metadata.mimeType || "text/plain";
@@ -3385,7 +3592,7 @@ var MarkdownPlugin = class {
3385
3592
  gfm: true,
3386
3593
  breaks: true
3387
3594
  });
3388
- const cleanHtml = DOMPurify2.sanitize(rawHtml, {
3595
+ const cleanHtml = DOMPurify6.sanitize(rawHtml, {
3389
3596
  USE_PROFILES: { html: true }
3390
3597
  });
3391
3598
  const wrapper = document.createElement("div");
@@ -4021,6 +4228,14 @@ var RtfPlugin = class {
4021
4228
  group: "actions",
4022
4229
  execute: () => instance.download?.()
4023
4230
  },
4231
+ {
4232
+ id: "copy",
4233
+ icon: "copy",
4234
+ label: "Copy Text",
4235
+ type: "button",
4236
+ group: "actions",
4237
+ execute: () => instance.copy?.()
4238
+ },
4024
4239
  {
4025
4240
  id: "print",
4026
4241
  icon: "print",
@@ -4051,20 +4266,27 @@ var RtfPlugin = class {
4051
4266
  let scale = 1;
4052
4267
  let rotation = 0;
4053
4268
  let pageElements = [];
4054
- const calculateFitScale = () => {
4269
+ let fitMode = "width";
4270
+ const calculateFitScale = (mode = fitMode) => {
4055
4271
  const activeEl = pageElements[currentPage - 1] || wrapper;
4056
4272
  const elW = activeEl.offsetWidth || 850;
4057
- const elH = activeEl.offsetHeight || 1e3;
4058
- const availW = Math.max(200, ctx.container.clientWidth - 48);
4059
- const availH = Math.max(200, ctx.container.clientHeight - 80);
4060
- return Math.min(1.1, Math.min(availW / elW, availH / elH));
4273
+ const singlePageH = Math.min(activeEl.offsetHeight || 1056, Math.round(elW * 1.32));
4274
+ const availW = Math.max(280, ctx.container.clientWidth - 48);
4275
+ const availH = Math.max(280, ctx.container.clientHeight - 80);
4276
+ const sW = availW / elW;
4277
+ const sH = availH / singlePageH;
4278
+ if (mode === "page") {
4279
+ return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
4280
+ }
4281
+ return Math.max(0.65, Math.min(1.05, sW));
4061
4282
  };
4062
4283
  const applyTransform = () => {
4063
4284
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
4064
4285
  wrapper.style.transformOrigin = "top center";
4065
4286
  };
4066
4287
  setTimeout(() => {
4067
- scale = calculateFitScale();
4288
+ fitMode = "width";
4289
+ scale = calculateFitScale("width");
4068
4290
  applyTransform();
4069
4291
  }, 60);
4070
4292
  try {
@@ -4082,14 +4304,31 @@ var RtfPlugin = class {
4082
4304
  } catch (err) {
4083
4305
  console.warn("[RtfPlugin] RTF render error, fallback text:", err);
4084
4306
  const text = new TextDecoder("latin1").decode(ctx.buffer);
4085
- const clean = text.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "");
4086
- const pre = document.createElement("pre");
4087
- pre.style.whiteSpace = "pre-wrap";
4088
- pre.style.fontFamily = "serif";
4089
- pre.style.color = "#333";
4090
- pre.textContent = clean;
4091
- wrapper.appendChild(pre);
4092
- pageElements = [pre];
4307
+ const rawPages = text.split(/\\page\b/).map((segment) => {
4308
+ return segment.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim();
4309
+ }).filter((p) => p.length > 0);
4310
+ const pages = rawPages.length > 0 ? rawPages : [text.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim()];
4311
+ for (let i = 0; i < pages.length; i++) {
4312
+ const pageCard = document.createElement("div");
4313
+ pageCard.className = "fp-rtf-page-card";
4314
+ pageCard.style.backgroundColor = "#ffffff";
4315
+ pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4316
+ pageCard.style.borderRadius = "4px";
4317
+ pageCard.style.padding = "72px 56px";
4318
+ pageCard.style.width = "816px";
4319
+ pageCard.style.minHeight = "1056px";
4320
+ pageCard.style.maxWidth = "100%";
4321
+ pageCard.style.boxSizing = "border-box";
4322
+ pageCard.style.fontFamily = "serif";
4323
+ pageCard.style.fontSize = "12pt";
4324
+ pageCard.style.lineHeight = "1.6";
4325
+ pageCard.style.color = "#1e293b";
4326
+ pageCard.style.whiteSpace = "pre-wrap";
4327
+ pageCard.style.display = i === 0 ? "block" : "none";
4328
+ pageCard.textContent = pages[i];
4329
+ wrapper.appendChild(pageCard);
4330
+ pageElements.push(pageCard);
4331
+ }
4093
4332
  }
4094
4333
  const totalPages = Math.max(1, pageElements.length);
4095
4334
  let currentPage = 1;
@@ -4156,7 +4395,8 @@ var RtfPlugin = class {
4156
4395
  applyTransform();
4157
4396
  },
4158
4397
  fitToPage: () => {
4159
- scale = calculateFitScale();
4398
+ fitMode = fitMode === "width" ? "page" : "width";
4399
+ scale = calculateFitScale(fitMode);
4160
4400
  rotation = 0;
4161
4401
  applyTransform();
4162
4402
  },
@@ -4177,6 +4417,10 @@ var RtfPlugin = class {
4177
4417
  a.click();
4178
4418
  URL.revokeObjectURL(url);
4179
4419
  },
4420
+ copy: () => {
4421
+ const text = wrapper.textContent || "";
4422
+ navigator.clipboard?.writeText(text);
4423
+ },
4180
4424
  print: () => {
4181
4425
  window.print();
4182
4426
  }
@@ -4251,7 +4495,7 @@ var HtmlPreviewPlugin = class {
4251
4495
  }
4252
4496
  async render(ctx) {
4253
4497
  const rawHtml = new TextDecoder("utf-8").decode(ctx.buffer);
4254
- const sanitized = DOMPurify2.sanitize(rawHtml, {
4498
+ const sanitized = DOMPurify6.sanitize(rawHtml, {
4255
4499
  WHOLE_DOCUMENT: true,
4256
4500
  ADD_TAGS: ["style", "link"],
4257
4501
  ADD_ATTR: ["target", "rel"]
@@ -4471,19 +4715,29 @@ var OpenDocumentPlugin = class {
4471
4715
  ctx.container.appendChild(container);
4472
4716
  let scale = 1;
4473
4717
  let rotation = 0;
4474
- const calculateFitScale = () => {
4718
+ let fitMode = "width";
4719
+ const calculateFitScale = (mode = fitMode) => {
4475
4720
  const elW = wrapper.offsetWidth || 850;
4476
- const elH = wrapper.offsetHeight || (isPresentation ? 540 : 1e3);
4477
- const availW = Math.max(200, ctx.container.clientWidth - 48);
4478
- const availH = Math.max(200, ctx.container.clientHeight - 80);
4479
- return Math.min(1, Math.min(availW / elW, availH / elH));
4721
+ const singlePageH = Math.min(wrapper.offsetHeight || 1056, Math.round(elW * 1.32));
4722
+ const availW = Math.max(280, ctx.container.clientWidth - 48);
4723
+ const availH = Math.max(280, ctx.container.clientHeight - 80);
4724
+ const sW = availW / elW;
4725
+ const sH = availH / (isPresentation ? 540 : singlePageH);
4726
+ if (isPresentation) {
4727
+ return Math.min(1, Math.min(sW, sH));
4728
+ }
4729
+ if (mode === "page") {
4730
+ return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
4731
+ }
4732
+ return Math.max(0.65, Math.min(1.05, sW));
4480
4733
  };
4481
4734
  const applyTransform = () => {
4482
4735
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
4483
4736
  wrapper.style.transformOrigin = "top center";
4484
4737
  };
4485
4738
  setTimeout(() => {
4486
- scale = calculateFitScale();
4739
+ fitMode = "width";
4740
+ scale = calculateFitScale("width");
4487
4741
  applyTransform();
4488
4742
  }, 60);
4489
4743
  let currentPage = 1;
@@ -4517,14 +4771,97 @@ var OpenDocumentPlugin = class {
4517
4771
  wrapper.textContent = contentDoc.documentElement.textContent || "Formula content";
4518
4772
  }
4519
4773
  } else {
4520
- wrapper.style.maxWidth = "850px";
4521
- wrapper.style.padding = "48px";
4774
+ wrapper.style.maxWidth = "none";
4775
+ wrapper.style.padding = "0";
4522
4776
  wrapper.style.minHeight = "100%";
4777
+ wrapper.style.backgroundColor = "transparent";
4778
+ wrapper.style.boxShadow = "none";
4779
+ wrapper.style.position = "relative";
4780
+ const dims = this.getPageDimensions(stylesDoc, contentDoc);
4523
4781
  const bodyHtml = this.renderOdfBody(contentDoc, styleMap, imageUrls);
4524
- wrapper.innerHTML = DOMPurify2.sanitize(bodyHtml, {
4525
- ADD_TAGS: ["math", "semantics", "mrow", "mi", "mo", "mn", "msup", "msub"],
4782
+ const tempDiv = document.createElement("div");
4783
+ tempDiv.style.width = `${dims.width - dims.marginLeft - dims.marginRight}px`;
4784
+ tempDiv.style.position = "absolute";
4785
+ tempDiv.style.visibility = "hidden";
4786
+ tempDiv.innerHTML = DOMPurify6.sanitize(bodyHtml, {
4787
+ ADD_TAGS: ["math", "semantics", "mrow", "mi", "mo", "mn", "msup", "msub", "hr"],
4526
4788
  ADD_ATTR: ["style", "colspan", "rowspan"]
4527
4789
  });
4790
+ document.body.appendChild(tempDiv);
4791
+ const contentHeight = dims.height - dims.marginTop - dims.marginBottom;
4792
+ const pageElements = [[]];
4793
+ let currentHeight = 0;
4794
+ let currentPageIdx = 0;
4795
+ Array.from(tempDiv.children).forEach((child) => {
4796
+ const el = child;
4797
+ const style = el.getAttribute("style") || "";
4798
+ const isBreakBefore = style.includes("page-break-before: always");
4799
+ const isBreakAfter = style.includes("page-break-after: always");
4800
+ const isSoftBreak = el.classList.contains("odf-page-break");
4801
+ if (isBreakBefore) {
4802
+ if (pageElements[currentPageIdx].length > 0) {
4803
+ currentPageIdx++;
4804
+ pageElements.push([]);
4805
+ currentHeight = 0;
4806
+ }
4807
+ }
4808
+ const h = el.offsetHeight || 0;
4809
+ if (currentHeight + h > contentHeight && pageElements[currentPageIdx].length > 0 && !isSoftBreak) {
4810
+ currentPageIdx++;
4811
+ pageElements.push([]);
4812
+ currentHeight = 0;
4813
+ }
4814
+ if (!isSoftBreak) {
4815
+ pageElements[currentPageIdx].push(el.cloneNode(true));
4816
+ currentHeight += h;
4817
+ }
4818
+ if (isBreakAfter || isSoftBreak) {
4819
+ currentPageIdx++;
4820
+ pageElements.push([]);
4821
+ currentHeight = 0;
4822
+ }
4823
+ });
4824
+ document.body.removeChild(tempDiv);
4825
+ if (pageElements.length > 1 && pageElements[pageElements.length - 1].length === 0) {
4826
+ pageElements.pop();
4827
+ }
4828
+ totalPages = Math.max(1, pageElements.length);
4829
+ pageElements.forEach((elements, idx) => {
4830
+ const page = document.createElement("div");
4831
+ page.className = `fp-odt-page fp-odt-page-${idx + 1}`;
4832
+ page.style.width = `${dims.width}px`;
4833
+ page.style.minHeight = `${dims.height}px`;
4834
+ page.style.padding = `${dims.marginTop}px ${dims.marginRight}px ${dims.marginBottom}px ${dims.marginLeft}px`;
4835
+ page.style.margin = "0 auto";
4836
+ page.style.backgroundColor = "#ffffff";
4837
+ page.style.boxShadow = "0 2px 10px rgba(0,0,0,0.08)";
4838
+ page.style.borderRadius = "4px";
4839
+ page.style.boxSizing = "border-box";
4840
+ page.style.display = idx === 0 ? "block" : "none";
4841
+ page.style.position = "absolute";
4842
+ page.style.top = "0";
4843
+ page.style.left = "50%";
4844
+ page.style.transform = "translateX(-50%)";
4845
+ elements.forEach((el) => page.appendChild(el));
4846
+ wrapper.appendChild(page);
4847
+ slides.push(page);
4848
+ });
4849
+ const pageIndicator = document.createElement("div");
4850
+ pageIndicator.className = "fp-odt-page-indicator";
4851
+ pageIndicator.style.position = "sticky";
4852
+ pageIndicator.style.bottom = "16px";
4853
+ pageIndicator.style.left = "50%";
4854
+ pageIndicator.style.transform = "translateX(-50%)";
4855
+ pageIndicator.style.backgroundColor = "rgba(0, 0, 0, 0.6)";
4856
+ pageIndicator.style.color = "#fff";
4857
+ pageIndicator.style.padding = "6px 12px";
4858
+ pageIndicator.style.borderRadius = "16px";
4859
+ pageIndicator.style.fontSize = "12px";
4860
+ pageIndicator.style.zIndex = "100";
4861
+ pageIndicator.style.display = "inline-block";
4862
+ pageIndicator.style.width = "fit-content";
4863
+ pageIndicator.textContent = `Page 1 of ${totalPages}`;
4864
+ container.appendChild(pageIndicator);
4528
4865
  }
4529
4866
  const cleanup = () => {
4530
4867
  imageUrls.forEach((url) => URL.revokeObjectURL(url));
@@ -4533,11 +4870,15 @@ var OpenDocumentPlugin = class {
4533
4870
  };
4534
4871
  ctx.signal.addEventListener("abort", cleanup);
4535
4872
  const goToPage = (page) => {
4536
- if (!isPresentation || page < 1 || page > totalPages) return;
4873
+ if (page < 1 || page > totalPages) return;
4537
4874
  currentPage = page;
4538
4875
  slides.forEach((s, idx) => {
4539
4876
  s.style.display = idx === page - 1 ? "block" : "none";
4540
4877
  });
4878
+ const indicator = container.querySelector(".fp-odt-page-indicator");
4879
+ if (indicator) {
4880
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4881
+ }
4541
4882
  ctx.emit("page-change", { page: currentPage, total: totalPages });
4542
4883
  };
4543
4884
  return {
@@ -4557,7 +4898,8 @@ var OpenDocumentPlugin = class {
4557
4898
  applyTransform();
4558
4899
  },
4559
4900
  fitToPage: () => {
4560
- scale = calculateFitScale();
4901
+ fitMode = fitMode === "width" ? "page" : "width";
4902
+ scale = calculateFitScale(fitMode);
4561
4903
  rotation = 0;
4562
4904
  applyTransform();
4563
4905
  },
@@ -4606,6 +4948,43 @@ var OpenDocumentPlugin = class {
4606
4948
  }
4607
4949
  };
4608
4950
  }
4951
+ getPageDimensions(stylesDoc, contentDoc) {
4952
+ let width = 850;
4953
+ let height = 1123;
4954
+ let marginTop = 48;
4955
+ let marginBottom = 48;
4956
+ let marginLeft = 48;
4957
+ let marginRight = 48;
4958
+ const parseUnit = (val) => {
4959
+ if (!val) return null;
4960
+ if (val.endsWith("cm")) return parseFloat(val) * 37.8;
4961
+ if (val.endsWith("mm")) return parseFloat(val) * 3.78;
4962
+ if (val.endsWith("in")) return parseFloat(val) * 96;
4963
+ if (val.endsWith("pt")) return parseFloat(val) * 1.33;
4964
+ if (val.endsWith("px")) return parseFloat(val);
4965
+ return parseFloat(val);
4966
+ };
4967
+ const docs = [stylesDoc, contentDoc].filter(Boolean);
4968
+ for (const doc of docs) {
4969
+ const pageLayout = doc.querySelector("page-layout-properties, [page-width]");
4970
+ if (pageLayout) {
4971
+ const w = parseUnit(pageLayout.getAttribute("fo:page-width") || pageLayout.getAttribute("page-width"));
4972
+ const h = parseUnit(pageLayout.getAttribute("fo:page-height") || pageLayout.getAttribute("page-height"));
4973
+ const mt = parseUnit(pageLayout.getAttribute("fo:margin-top") || pageLayout.getAttribute("margin-top"));
4974
+ const mb = parseUnit(pageLayout.getAttribute("fo:margin-bottom") || pageLayout.getAttribute("margin-bottom"));
4975
+ const ml = parseUnit(pageLayout.getAttribute("fo:margin-left") || pageLayout.getAttribute("margin-left"));
4976
+ const mr = parseUnit(pageLayout.getAttribute("fo:margin-right") || pageLayout.getAttribute("margin-right"));
4977
+ if (w !== null) width = w;
4978
+ if (h !== null) height = h;
4979
+ if (mt !== null) marginTop = mt;
4980
+ if (mb !== null) marginBottom = mb;
4981
+ if (ml !== null) marginLeft = ml;
4982
+ if (mr !== null) marginRight = mr;
4983
+ break;
4984
+ }
4985
+ }
4986
+ return { width, height, marginTop, marginBottom, marginLeft, marginRight };
4987
+ }
4609
4988
  extractStyles(stylesDoc, contentDoc) {
4610
4989
  const map = /* @__PURE__ */ new Map();
4611
4990
  const styleNodes = [];
@@ -4628,14 +5007,21 @@ var OpenDocumentPlugin = class {
4628
5007
  if (color) css += `color: ${color}; `;
4629
5008
  if (size) css += `font-size: ${size}; `;
4630
5009
  }
4631
- const paraProp = node.querySelector("paragraph-properties, [text-align]");
5010
+ let paraProp = node.querySelector("paragraph-properties, [text-align]");
5011
+ if (!paraProp) {
5012
+ paraProp = Array.from(node.children).find((c) => c.tagName.includes("paragraph-properties")) || null;
5013
+ }
4632
5014
  if (paraProp) {
4633
5015
  const align = paraProp.getAttribute("fo:text-align") || paraProp.getAttribute("text-align");
4634
5016
  const mt = paraProp.getAttribute("fo:margin-top") || paraProp.getAttribute("margin-top");
4635
5017
  const mb = paraProp.getAttribute("fo:margin-bottom") || paraProp.getAttribute("margin-bottom");
5018
+ const breakBefore = paraProp.getAttribute("fo:break-before") || paraProp.getAttribute("break-before");
5019
+ const breakAfter = paraProp.getAttribute("fo:break-after") || paraProp.getAttribute("break-after");
4636
5020
  if (align) css += `text-align: ${align}; `;
4637
5021
  if (mt) css += `margin-top: ${mt}; `;
4638
5022
  if (mb) css += `margin-bottom: ${mb}; `;
5023
+ if (breakBefore === "page") css += "page-break-before: always; ";
5024
+ if (breakAfter === "page") css += "page-break-after: always; ";
4639
5025
  }
4640
5026
  if (css) map.set(name, css);
4641
5027
  }
@@ -4720,6 +5106,8 @@ var OpenDocumentPlugin = class {
4720
5106
  result += "&emsp;";
4721
5107
  } else if (tag === "line-break") {
4722
5108
  result += "<br/>";
5109
+ } else if (tag === "soft-page-break") {
5110
+ result += '<hr class="odf-page-break" style="page-break-after: always; border: none; margin: 0; padding: 0; height: 0;" />';
4723
5111
  } else {
4724
5112
  result += el.textContent || "";
4725
5113
  }
@@ -4880,20 +5268,9 @@ var DocPlugin = class {
4880
5268
  container.style.overflow = "auto";
4881
5269
  container.style.padding = "32px 16px";
4882
5270
  container.style.backgroundColor = "#f1f5f9";
4883
- const wrapper = document.createElement("div");
4884
- wrapper.className = "fp-doc-wrapper";
4885
- wrapper.style.maxWidth = "850px";
4886
- wrapper.style.margin = "0 auto";
4887
- wrapper.style.backgroundColor = "#ffffff";
4888
- wrapper.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4889
- wrapper.style.borderRadius = "4px";
4890
- wrapper.style.padding = "56px 48px";
4891
- wrapper.style.minHeight = "100%";
4892
- wrapper.style.transformOrigin = "top center";
4893
- wrapper.style.transition = "transform 0.2s ease";
4894
- wrapper.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
4895
- wrapper.style.color = "#1e293b";
4896
- container.appendChild(wrapper);
5271
+ container.style.display = "flex";
5272
+ container.style.justifyContent = "center";
5273
+ container.style.alignItems = "flex-start";
4897
5274
  ctx.container.appendChild(container);
4898
5275
  let scale = 1;
4899
5276
  let extractedRawText = "";
@@ -4920,21 +5297,27 @@ var DocPlugin = class {
4920
5297
  const rawPages = this.splitIntoPages(extractedRawText);
4921
5298
  const totalPages = Math.max(1, rawPages.length);
4922
5299
  let currentPage = 1;
4923
- wrapper.innerHTML = "";
4924
5300
  const pageCards = [];
4925
5301
  for (let i = 0; i < totalPages; i++) {
4926
5302
  const pageCard = document.createElement("div");
4927
5303
  pageCard.className = "fp-doc-page-card";
5304
+ pageCard.style.width = "816px";
5305
+ pageCard.style.height = "1056px";
4928
5306
  pageCard.style.backgroundColor = "#ffffff";
4929
5307
  pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4930
5308
  pageCard.style.borderRadius = "4px";
4931
- pageCard.style.padding = "56px 48px";
4932
- pageCard.style.minHeight = "100%";
5309
+ pageCard.style.padding = "96px 72px";
5310
+ pageCard.style.boxSizing = "border-box";
5311
+ pageCard.style.overflow = "hidden";
4933
5312
  pageCard.style.display = i === 0 ? "block" : "none";
5313
+ pageCard.style.transformOrigin = "top center";
5314
+ pageCard.style.transition = "transform 0.2s ease";
5315
+ pageCard.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
5316
+ pageCard.style.color = "#1e293b";
4934
5317
  if (isFallback && i === 0) {
4935
5318
  pageCard.innerHTML = `
4936
5319
  <div style="border-bottom: 1px solid #e2e8f0; padding-bottom: 12px; margin-bottom: 24px;">
4937
- <h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${DOMPurify2.sanitize(ctx.metadata.name || "Word Document (.doc)")}</h2>
5320
+ <h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${DOMPurify6.sanitize(ctx.metadata.name || "Word Document (.doc)")}</h2>
4938
5321
  <span style="font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px;">Legacy Word 97-2003 Binary Preview</span>
4939
5322
  </div>
4940
5323
  ${this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document")}
@@ -4942,15 +5325,17 @@ var DocPlugin = class {
4942
5325
  } else {
4943
5326
  pageCard.innerHTML = this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document");
4944
5327
  }
4945
- wrapper.appendChild(pageCard);
5328
+ container.appendChild(pageCard);
4946
5329
  pageCards.push(pageCard);
4947
5330
  }
4948
5331
  let indicator = null;
4949
5332
  if (totalPages > 1) {
4950
5333
  indicator = document.createElement("div");
4951
5334
  indicator.className = "fp-doc-page-indicator";
4952
- indicator.style.position = "sticky";
5335
+ indicator.style.position = "fixed";
4953
5336
  indicator.style.bottom = "16px";
5337
+ indicator.style.left = "50%";
5338
+ indicator.style.transform = "translateX(-50%)";
4954
5339
  indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
4955
5340
  indicator.style.backdropFilter = "blur(8px)";
4956
5341
  indicator.style.color = "#f8fafc";
@@ -4964,17 +5349,25 @@ var DocPlugin = class {
4964
5349
  indicator.style.userSelect = "none";
4965
5350
  indicator.style.pointerEvents = "none";
4966
5351
  indicator.style.textAlign = "center";
4967
- indicator.style.width = "fit-content";
4968
- indicator.style.margin = "16px auto 0";
4969
5352
  container.appendChild(indicator);
4970
5353
  }
5354
+ let rotation = 0;
5355
+ const applyTransform = () => {
5356
+ const activeCard = pageCards[currentPage - 1];
5357
+ if (activeCard) {
5358
+ activeCard.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5359
+ }
5360
+ };
4971
5361
  const showPage = (pageNum) => {
4972
5362
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
4973
- if (totalPages > 1) {
4974
- pageCards.forEach((card, idx) => {
4975
- card.style.display = idx + 1 === currentPage ? "block" : "none";
4976
- });
4977
- }
5363
+ pageCards.forEach((card, idx) => {
5364
+ if (idx + 1 === currentPage) {
5365
+ card.style.display = "block";
5366
+ card.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5367
+ } else {
5368
+ card.style.display = "none";
5369
+ }
5370
+ });
4978
5371
  if (indicator) {
4979
5372
  indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4980
5373
  }
@@ -4983,21 +5376,22 @@ var DocPlugin = class {
4983
5376
  if (totalPages > 1) {
4984
5377
  showPage(1);
4985
5378
  }
4986
- let rotation = 0;
4987
- const calculateFitScale = () => {
4988
- const activeCard = pageCards[currentPage - 1] || wrapper;
4989
- const elW = activeCard.offsetWidth || 850;
4990
- const elH = activeCard.offsetHeight || 1e3;
4991
- const availW = Math.max(200, ctx.container.clientWidth - 48);
4992
- const availH = Math.max(200, ctx.container.clientHeight - 80);
4993
- return Math.min(1.1, Math.min(availW / elW, availH / elH));
4994
- };
4995
- const applyTransform = () => {
4996
- wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
4997
- wrapper.style.transformOrigin = "top center";
5379
+ let fitMode = "width";
5380
+ const calculateFitScale = (mode = fitMode) => {
5381
+ const elW = 816;
5382
+ const elH = 1056;
5383
+ const availW = Math.max(280, ctx.container.clientWidth - 48);
5384
+ const availH = Math.max(280, ctx.container.clientHeight - 80);
5385
+ const sW = availW / elW;
5386
+ const sH = availH / elH;
5387
+ if (mode === "page") {
5388
+ return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
5389
+ }
5390
+ return Math.max(0.65, Math.min(1.05, sW));
4998
5391
  };
4999
5392
  setTimeout(() => {
5000
- scale = calculateFitScale();
5393
+ fitMode = "width";
5394
+ scale = calculateFitScale("width");
5001
5395
  applyTransform();
5002
5396
  }, 60);
5003
5397
  const cleanup = () => {
@@ -5025,7 +5419,8 @@ var DocPlugin = class {
5025
5419
  applyTransform();
5026
5420
  },
5027
5421
  fitToPage: () => {
5028
- scale = calculateFitScale();
5422
+ fitMode = fitMode === "width" ? "page" : "width";
5423
+ scale = calculateFitScale(fitMode);
5029
5424
  rotation = 0;
5030
5425
  applyTransform();
5031
5426
  },
@@ -5153,8 +5548,28 @@ var DocPlugin = class {
5153
5548
  }
5154
5549
  splitIntoPages(text) {
5155
5550
  if (!text) return [""];
5156
- 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);
5157
- return parts.length > 0 ? parts : [text];
5551
+ 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);
5552
+ if (explicitParts.length === 0) explicitParts.push(text);
5553
+ const maxLinesPerPage = 48;
5554
+ const finalPages = [];
5555
+ for (const part of explicitParts) {
5556
+ const lines = part.split(/\r?\n/);
5557
+ let currentLines = [];
5558
+ let count = 0;
5559
+ for (const line of lines) {
5560
+ currentLines.push(line);
5561
+ count++;
5562
+ if (count >= maxLinesPerPage) {
5563
+ finalPages.push(currentLines.join("\n"));
5564
+ currentLines = [];
5565
+ count = 0;
5566
+ }
5567
+ }
5568
+ if (currentLines.length > 0) {
5569
+ finalPages.push(currentLines.join("\n"));
5570
+ }
5571
+ }
5572
+ return finalPages.length > 0 ? finalPages : [text];
5158
5573
  }
5159
5574
  /**
5160
5575
  * Format cleaned extracted text into attractive HTML paragraphs, headings, and lists
@@ -5163,17 +5578,65 @@ var DocPlugin = class {
5163
5578
  const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").split("\n");
5164
5579
  let html = "";
5165
5580
  let inList = false;
5166
- for (const rawLine of lines) {
5167
- const line = rawLine.trim();
5581
+ let tableLines = [];
5582
+ const flushTable = () => {
5583
+ if (tableLines.length > 0) {
5584
+ html += '<table style="width: 100%; border-collapse: collapse; margin: 16px 0; font-size: 12px;">';
5585
+ for (const tLine of tableLines) {
5586
+ html += "<tr>";
5587
+ const cols = tLine.split(" ");
5588
+ for (const col of cols) {
5589
+ html += `<td style="border: 1px solid #cbd5e1; padding: 6px 8px;">${DOMPurify6.sanitize(col.trim())}</td>`;
5590
+ }
5591
+ html += "</tr>";
5592
+ }
5593
+ html += "</table>";
5594
+ tableLines = [];
5595
+ }
5596
+ };
5597
+ let i = 0;
5598
+ while (i < lines.length) {
5599
+ let line = lines[i];
5600
+ let tabCount = (line.match(/\t/g) || []).length;
5601
+ if (tabCount > 0) {
5602
+ let consecutiveTableLines = 1;
5603
+ let j = i + 1;
5604
+ while (j < lines.length) {
5605
+ const nextTabCount = (lines[j].match(/\t/g) || []).length;
5606
+ if (nextTabCount === tabCount) {
5607
+ consecutiveTableLines++;
5608
+ j++;
5609
+ } else {
5610
+ break;
5611
+ }
5612
+ }
5613
+ if (consecutiveTableLines >= 3) {
5614
+ if (inList) {
5615
+ html += "</ul>";
5616
+ inList = false;
5617
+ }
5618
+ tableLines = lines.slice(i, j);
5619
+ flushTable();
5620
+ i = j;
5621
+ continue;
5622
+ }
5623
+ }
5624
+ line = line.trim();
5168
5625
  if (!line) {
5169
5626
  if (inList) {
5170
5627
  html += "</ul>";
5171
5628
  inList = false;
5172
5629
  }
5630
+ html += '<div style="height: 1.15em;"></div>';
5631
+ i++;
5632
+ continue;
5633
+ }
5634
+ if (/^[\x00-\x1F\x7F-\x9F]+$/.test(line)) {
5635
+ i++;
5173
5636
  continue;
5174
5637
  }
5175
- if (/^[\x00-\x1F\x7F-\x9F]+$/.test(line)) continue;
5176
- if (line.includes("Normal.dot") || line.includes("Microsoft Word") || line.includes("Times New Roman") && line.length < 30) {
5638
+ if ((line.includes("Normal.dot") || line.includes("Microsoft Word") || line.includes("Times New Roman")) && line.length < 30) {
5639
+ i++;
5177
5640
  continue;
5178
5641
  }
5179
5642
  if (line.length < 60 && !line.endsWith(".") && (/^[A-Z0-9\s:_-]+$/.test(line) || line.startsWith("#"))) {
@@ -5181,24 +5644,26 @@ var DocPlugin = class {
5181
5644
  html += "</ul>";
5182
5645
  inList = false;
5183
5646
  }
5184
- 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>`;
5647
+ 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>`;
5185
5648
  } else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
5186
5649
  if (!inList) {
5187
5650
  html += '<ul style="margin: 8px 0; padding-left: 24px;">';
5188
5651
  inList = true;
5189
5652
  }
5190
5653
  const bulletText = line.replace(/^[•\-\*]\s*/, "");
5191
- html += `<li style="margin: 4px 0; line-height: 1.6;">${DOMPurify2.sanitize(bulletText)}</li>`;
5654
+ html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6.sanitize(bulletText)}</li>`;
5192
5655
  } else {
5193
5656
  if (inList) {
5194
5657
  html += "</ul>";
5195
5658
  inList = false;
5196
5659
  }
5197
- html += `<p style="line-height: 1.7; margin: 10px 0; font-size: 14px; text-align: justify;">${DOMPurify2.sanitize(line)}</p>`;
5660
+ html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6.sanitize(line)}</p>`;
5198
5661
  }
5662
+ i++;
5199
5663
  }
5200
5664
  if (inList) html += "</ul>";
5201
- return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify2.sanitize(filename)})</p>`;
5665
+ flushTable();
5666
+ return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify6.sanitize(filename)})</p>`;
5202
5667
  }
5203
5668
  };
5204
5669
  function docPlugin() {
@@ -5376,7 +5841,7 @@ var PptPlugin = class {
5376
5841
  if (s.pictureUrl) {
5377
5842
  contentHtml = `
5378
5843
  <div style="flex: 1; display: flex; justify-content: center; align-items: center; padding: 12px; overflow: hidden;">
5379
- <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;" />
5844
+ <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;" />
5380
5845
  </div>
5381
5846
  `;
5382
5847
  } else if (s.tableColumns.length > 0) {
@@ -5386,7 +5851,7 @@ var PptPlugin = class {
5386
5851
  <table style="width: 100%; border-collapse: collapse; border: 1px solid #cbd5e1; border-radius: 6px; overflow: hidden; background: #ffffff;">
5387
5852
  <thead>
5388
5853
  <tr style="background: #e2e8f0; color: #1e293b; font-weight: 600; font-size: 14px;">
5389
- ${cols.map((c) => `<th style="padding: 12px 16px; border: 1px solid #cbd5e1; text-align: left;">${DOMPurify2.sanitize(c)}</th>`).join("")}
5854
+ ${cols.map((c) => `<th style="padding: 12px 16px; border: 1px solid #cbd5e1; text-align: left;">${DOMPurify6.sanitize(c)}</th>`).join("")}
5390
5855
  </tr>
5391
5856
  </thead>
5392
5857
  <tbody>
@@ -5402,7 +5867,7 @@ var PptPlugin = class {
5402
5867
  } else {
5403
5868
  const pTags = s.paragraphs.map((p) => {
5404
5869
  const lines = p.split(/[\r\n]+/).map((l) => l.trim()).filter(Boolean);
5405
- 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("");
5870
+ 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("");
5406
5871
  }).join("");
5407
5872
  contentHtml = `
5408
5873
  <div style="flex: 1; overflow: auto; padding: 4px 8px; display: flex; flex-direction: column; justify-content: flex-start;">
@@ -5415,11 +5880,11 @@ var PptPlugin = class {
5415
5880
  <!-- Header Banner matching PowerPoint design -->
5416
5881
  <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;">
5417
5882
  <h1 style="margin: 0; font-size: 26px; font-weight: 700; color: #1e293b; letter-spacing: -0.3px;">
5418
- ${DOMPurify2.sanitize(s.title)}
5883
+ ${DOMPurify6.sanitize(s.title)}
5419
5884
  </h1>
5420
5885
  ${s.subtitle ? `
5421
5886
  <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);">
5422
- ${DOMPurify2.sanitize(s.subtitle)}
5887
+ ${DOMPurify6.sanitize(s.subtitle)}
5423
5888
  </span>
5424
5889
  ` : `
5425
5890
  <span style="font-size: 12px; color: #365314; font-weight: 600;">