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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/angular.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Component, ChangeDetectionStrategy, Input, Output, ViewChild, EventEmitter as EventEmitter$1 } from '@angular/core';
2
2
  import { CommonModule } from '@angular/common';
3
- import DOMPurify2 from 'dompurify';
3
+ import DOMPurify6 from 'dompurify';
4
4
  import * as pdfjsLib from 'pdfjs-dist';
5
5
  import * as docx from 'docx-preview';
6
6
  import { unzipSync, strFromU8, unzip } from 'fflate';
@@ -405,7 +405,7 @@ async function sourceToArrayBuffer(source, signal) {
405
405
  if (magicMime === "application/zip") {
406
406
  const ooxmlMime = detectOoxmlType(buffer);
407
407
  if (ooxmlMime && ooxmlMime !== "application/zip") {
408
- metadata.mimeType = ooxmlMime;
408
+ metadata.mimeType = metadata.mimeType ?? ooxmlMime;
409
409
  if (ooxmlMime.includes("wordprocessing")) metadata.extension = metadata.extension ?? ".docx";
410
410
  else if (ooxmlMime.includes("spreadsheet")) metadata.extension = metadata.extension ?? ".xlsx";
411
411
  else if (ooxmlMime.includes("presentation")) metadata.extension = metadata.extension ?? ".pptx";
@@ -434,7 +434,7 @@ async function sourceToArrayBuffer(source, signal) {
434
434
  return { buffer, metadata };
435
435
  }
436
436
  function sanitizeSVG(svg) {
437
- return DOMPurify2.sanitize(svg, {
437
+ return DOMPurify6.sanitize(svg, {
438
438
  USE_PROFILES: { svg: true, svgFilters: true }
439
439
  });
440
440
  }
@@ -1496,7 +1496,19 @@ var PdfPlugin = class {
1496
1496
  }
1497
1497
  };
1498
1498
  await renderPage(1);
1499
+ let resizeTimer = null;
1500
+ const resizeObserver = new ResizeObserver(() => {
1501
+ if (resizeTimer) clearTimeout(resizeTimer);
1502
+ resizeTimer = setTimeout(() => {
1503
+ if (Math.abs(zoomScale - 1) < 0.05) {
1504
+ renderPage(currentPage);
1505
+ }
1506
+ }, 150);
1507
+ });
1508
+ resizeObserver.observe(container);
1499
1509
  const cleanup = () => {
1510
+ resizeObserver.disconnect();
1511
+ if (resizeTimer) clearTimeout(resizeTimer);
1500
1512
  if (currentRenderTask) {
1501
1513
  try {
1502
1514
  currentRenderTask.cancel();
@@ -2244,90 +2256,149 @@ var DocxPlugin = class {
2244
2256
  console.warn("[DocxPlugin] Error parsing relationships:", relsErr);
2245
2257
  }
2246
2258
  }
2247
- const card = document.createElement("div");
2248
- card.className = "fp-docx-page-card";
2249
- card.style.backgroundColor = "#ffffff";
2250
- card.style.borderRadius = "6px";
2251
- card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
2252
- card.style.padding = "48px 56px";
2253
- card.style.fontFamily = 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
2254
- card.style.color = "#1e293b";
2255
- card.style.lineHeight = "1.6";
2259
+ const sectPrs = Array.from(doc.getElementsByTagNameNS("*", "sectPr"));
2260
+ const lastSectPr = sectPrs[sectPrs.length - 1];
2261
+ let defaultW = 12240;
2262
+ let defaultH = 15840;
2263
+ let margins = { top: 1440, right: 1440, bottom: 1440, left: 1440 };
2264
+ if (lastSectPr) {
2265
+ const pgSz = lastSectPr.getElementsByTagNameNS("*", "pgSz")[0];
2266
+ if (pgSz) {
2267
+ defaultW = parseInt(pgSz.getAttribute("w:w") || pgSz.getAttribute("w") || "12240", 10);
2268
+ defaultH = parseInt(pgSz.getAttribute("w:h") || pgSz.getAttribute("h") || "15840", 10);
2269
+ }
2270
+ const pgMar = lastSectPr.getElementsByTagNameNS("*", "pgMar")[0];
2271
+ if (pgMar) {
2272
+ margins.top = parseInt(pgMar.getAttribute("w:top") || pgMar.getAttribute("top") || "1440", 10);
2273
+ margins.bottom = parseInt(pgMar.getAttribute("w:bottom") || pgMar.getAttribute("bottom") || "1440", 10);
2274
+ margins.left = parseInt(pgMar.getAttribute("w:left") || pgMar.getAttribute("left") || "1440", 10);
2275
+ margins.right = parseInt(pgMar.getAttribute("w:right") || pgMar.getAttribute("right") || "1440", 10);
2276
+ }
2277
+ }
2278
+ const createPageCard = () => {
2279
+ const card = document.createElement("div");
2280
+ card.className = "fp-docx-page-card";
2281
+ card.style.backgroundColor = "#ffffff";
2282
+ card.style.borderRadius = "4px";
2283
+ card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
2284
+ card.style.fontFamily = 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
2285
+ card.style.color = "#1e293b";
2286
+ card.style.lineHeight = "1.6";
2287
+ card.style.boxSizing = "border-box";
2288
+ card.style.overflow = "hidden";
2289
+ card.style.position = "relative";
2290
+ card.style.marginBottom = "24px";
2291
+ card.style.width = `${defaultW / 15}px`;
2292
+ card.style.height = `${defaultH / 15}px`;
2293
+ card.style.padding = `${margins.top / 15}px ${margins.right / 15}px ${margins.bottom / 15}px ${margins.left / 15}px`;
2294
+ return card;
2295
+ };
2296
+ let currentCard = createPageCard();
2297
+ let currentHtml = "";
2298
+ const pages = [{ card: currentCard, html: "" }];
2299
+ const flushHtml = () => {
2300
+ pages[pages.length - 1].html += currentHtml;
2301
+ currentHtml = "";
2302
+ };
2303
+ const newPage = () => {
2304
+ flushHtml();
2305
+ currentCard = createPageCard();
2306
+ pages.push({ card: currentCard, html: "" });
2307
+ };
2256
2308
  const body = doc.getElementsByTagNameNS("*", "body")[0] || doc.documentElement;
2257
- let html = "";
2258
2309
  for (const child of Array.from(body.children)) {
2259
2310
  const tag = child.localName || child.nodeName.split(":").pop();
2260
2311
  if (tag === "p") {
2261
2312
  const pStyle = child.getElementsByTagNameNS("*", "pStyle")[0];
2262
2313
  const styleVal = pStyle?.getAttribute("w:val") || pStyle?.getAttribute("val") || "";
2263
2314
  const numPr = child.getElementsByTagNameNS("*", "numPr")[0];
2264
- const textContent = this.extractParagraphHtml(child, imageMap);
2265
- if (!textContent.trim()) {
2266
- html += '<div style="height: 10px;"></div>';
2267
- continue;
2268
- }
2269
- const lowerStyle = styleVal.toLowerCase();
2270
- if (lowerStyle.includes("title")) {
2271
- 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>`;
2272
- } else if (lowerStyle.includes("heading1") || styleVal === "1") {
2273
- html += `<h2 style="font-size: 22px; font-weight: 700; color: #1e40af; margin: 20px 0 10px;">${textContent}</h2>`;
2274
- } else if (lowerStyle.includes("heading2") || styleVal === "2") {
2275
- html += `<h3 style="font-size: 18px; font-weight: 600; color: #2563eb; margin: 16px 0 8px;">${textContent}</h3>`;
2276
- } else if (lowerStyle.includes("heading3") || styleVal === "3") {
2277
- html += `<h4 style="font-size: 15px; font-weight: 600; color: #334155; margin: 12px 0 6px;">${textContent}</h4>`;
2278
- } else if (numPr) {
2279
- 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>`;
2280
- } else {
2281
- html += `<p style="margin: 8px 0; font-size: 14px;">${textContent}</p>`;
2315
+ const chunks = this.extractParagraphChunks(child, imageMap);
2316
+ for (let i = 0; i < chunks.length; i++) {
2317
+ if (i > 0) {
2318
+ newPage();
2319
+ }
2320
+ const textContent = chunks[i];
2321
+ if (!textContent.trim() && !textContent.includes("<img")) {
2322
+ currentHtml += '<div style="height: 10px;"></div>';
2323
+ continue;
2324
+ }
2325
+ const lowerStyle = styleVal.toLowerCase();
2326
+ if (lowerStyle.includes("title")) {
2327
+ 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>`;
2328
+ } else if (lowerStyle.includes("heading1") || styleVal === "1") {
2329
+ currentHtml += `<h2 style="font-size: 22px; font-weight: 700; color: #1e40af; margin: 20px 0 10px;">${textContent}</h2>`;
2330
+ } else if (lowerStyle.includes("heading2") || styleVal === "2") {
2331
+ currentHtml += `<h3 style="font-size: 18px; font-weight: 600; color: #2563eb; margin: 16px 0 8px;">${textContent}</h3>`;
2332
+ } else if (lowerStyle.includes("heading3") || styleVal === "3") {
2333
+ currentHtml += `<h4 style="font-size: 15px; font-weight: 600; color: #334155; margin: 12px 0 6px;">${textContent}</h4>`;
2334
+ } else if (numPr) {
2335
+ 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>`;
2336
+ } else {
2337
+ currentHtml += `<p style="margin: 8px 0; font-size: 14px;">${textContent}</p>`;
2338
+ }
2282
2339
  }
2283
2340
  } else if (tag === "tbl") {
2284
- html += '<table style="width: 100%; border-collapse: collapse; margin: 20px 0; border: 1px solid #cbd5e1;">';
2341
+ currentHtml += '<table style="width: 100%; border-collapse: collapse; margin: 20px 0; border: 1px solid #cbd5e1;">';
2285
2342
  const rows = Array.from(child.getElementsByTagNameNS("*", "tr"));
2286
2343
  rows.forEach((tr, rIdx) => {
2287
- html += `<tr style="${rIdx === 0 ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
2344
+ currentHtml += `<tr style="${rIdx === 0 ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
2288
2345
  const cells = Array.from(tr.getElementsByTagNameNS("*", "tc"));
2289
2346
  cells.forEach((tc) => {
2290
2347
  const cellText = this.extractParagraphHtml(tc, imageMap);
2291
- html += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px; font-size: 13px;">${cellText || "&nbsp;"}</td>`;
2348
+ currentHtml += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px; font-size: 13px;">${cellText || "&nbsp;"}</td>`;
2292
2349
  });
2293
- html += "</tr>";
2350
+ currentHtml += "</tr>";
2294
2351
  });
2295
- html += "</table>";
2352
+ currentHtml += "</table>";
2296
2353
  }
2297
2354
  }
2298
- card.innerHTML = DOMPurify2.sanitize(html, {
2299
- ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "s", "strike", "img", "div", "br"],
2300
- ADD_ATTR: ["style", "src", "alt", "colspan", "rowspan"]
2301
- });
2302
- wrapper.appendChild(card);
2355
+ flushHtml();
2356
+ for (const page of pages) {
2357
+ page.card.innerHTML = DOMPurify6.sanitize(page.html, {
2358
+ ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "s", "strike", "img", "div", "br"],
2359
+ ADD_ATTR: ["style", "src", "alt", "colspan", "rowspan"]
2360
+ });
2361
+ wrapper.appendChild(page.card);
2362
+ }
2303
2363
  }
2304
2364
  extractParagraphHtml(pElement, imageMap = {}) {
2305
- let result = "";
2365
+ return this.extractParagraphChunks(pElement, imageMap).join("<br/>");
2366
+ }
2367
+ extractParagraphChunks(pElement, imageMap = {}) {
2368
+ const chunks = [""];
2369
+ let currentChunkIndex = 0;
2306
2370
  const drawings = Array.from(pElement.getElementsByTagNameNS("*", "drawing"));
2307
2371
  for (const drawing of drawings) {
2308
2372
  const blip = drawing.getElementsByTagNameNS("*", "blip")[0];
2309
2373
  const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
2310
2374
  if (rId && imageMap[rId]) {
2311
- result += `<div style="text-align:center; margin: 12px 0;"><img src="${imageMap[rId]}" style="max-width: 100%; height: auto; border-radius: 4px;" /></div>`;
2375
+ chunks[currentChunkIndex] += `<div style="text-align:center; margin: 12px 0;"><img src="${imageMap[rId]}" style="max-width: 100%; height: auto; border-radius: 4px;" /></div>`;
2312
2376
  }
2313
2377
  }
2314
2378
  const runs = Array.from(pElement.getElementsByTagNameNS("*", "r"));
2315
- if (runs.length === 0 && !result) {
2316
- return DOMPurify2.sanitize(pElement.textContent || "");
2379
+ if (runs.length === 0 && !chunks[currentChunkIndex]) {
2380
+ chunks[currentChunkIndex] = DOMPurify6.sanitize(pElement.textContent || "");
2381
+ return chunks;
2317
2382
  }
2318
2383
  for (const r of runs) {
2319
2384
  const blip = r.getElementsByTagNameNS("*", "blip")[0] || r.getElementsByTagNameNS("*", "imagedata")[0];
2320
2385
  const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
2321
2386
  if (rId && imageMap[rId]) {
2322
- result += `<img src="${imageMap[rId]}" style="max-width: 100%; height: auto; display: inline-block; margin: 4px;" />`;
2387
+ chunks[currentChunkIndex] += `<img src="${imageMap[rId]}" style="max-width: 100%; height: auto; display: inline-block; margin: 4px;" />`;
2323
2388
  }
2324
- const brs = r.getElementsByTagNameNS("*", "br");
2325
- for (let i = 0; i < brs.length; i++) {
2326
- result += "<br/>";
2389
+ const brs = Array.from(r.getElementsByTagNameNS("*", "br"));
2390
+ for (const br of brs) {
2391
+ const type = br.getAttribute("w:type") || br.getAttribute("type");
2392
+ if (type === "page") {
2393
+ chunks.push("");
2394
+ currentChunkIndex++;
2395
+ } else {
2396
+ chunks[currentChunkIndex] += "<br/>";
2397
+ }
2327
2398
  }
2328
2399
  const tabs = r.getElementsByTagNameNS("*", "tab");
2329
2400
  if (tabs.length > 0) {
2330
- result += "&emsp;";
2401
+ chunks[currentChunkIndex] += "&emsp;";
2331
2402
  }
2332
2403
  const rPr = r.getElementsByTagNameNS("*", "rPr")[0];
2333
2404
  const isBold = !!rPr?.getElementsByTagNameNS("*", "b")[0];
@@ -2341,7 +2412,7 @@ var DocxPlugin = class {
2341
2412
  const texts = Array.from(r.getElementsByTagNameNS("*", "t"));
2342
2413
  let text = texts.map((t) => t.textContent || "").join("");
2343
2414
  if (!text) continue;
2344
- text = DOMPurify2.sanitize(text);
2415
+ text = DOMPurify6.sanitize(text);
2345
2416
  let styles = "";
2346
2417
  if (isBold) styles += "font-weight: bold; ";
2347
2418
  if (isItalic) styles += "font-style: italic; ";
@@ -2353,12 +2424,12 @@ var DocxPlugin = class {
2353
2424
  if (!isNaN(pt) && pt > 0) styles += `font-size: ${pt}pt; `;
2354
2425
  }
2355
2426
  if (styles) {
2356
- result += `<span style="${styles}">${text}</span>`;
2427
+ chunks[currentChunkIndex] += `<span style="${styles}">${text}</span>`;
2357
2428
  } else {
2358
- result += text;
2429
+ chunks[currentChunkIndex] += text;
2359
2430
  }
2360
2431
  }
2361
- return result;
2432
+ return chunks;
2362
2433
  }
2363
2434
  renderBinaryDocFallback(ctx, wrapper) {
2364
2435
  const card = document.createElement("div");
@@ -2375,14 +2446,14 @@ var DocxPlugin = class {
2375
2446
  const wordDocStream = cfbf.readStream("WordDocument");
2376
2447
  if (wordDocStream && wordDocStream.length >= 512) {
2377
2448
  const text2 = this.extractReadableStrings(wordDocStream);
2378
- card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2.sanitize(text2)}</div>`;
2449
+ card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify6.sanitize(text2)}</div>`;
2379
2450
  wrapper.appendChild(card);
2380
2451
  return;
2381
2452
  }
2382
2453
  } catch {
2383
2454
  }
2384
2455
  const text = this.extractReadableStrings(new Uint8Array(ctx.buffer));
2385
- card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2.sanitize(text)}</div>`;
2456
+ card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify6.sanitize(text)}</div>`;
2386
2457
  wrapper.appendChild(card);
2387
2458
  }
2388
2459
  extractReadableStrings(bytes) {
@@ -2981,41 +3052,94 @@ var CodePlugin = class {
2981
3052
  async render(ctx) {
2982
3053
  const decoder = new TextDecoder("utf-8");
2983
3054
  const fullText = decoder.decode(ctx.buffer);
2984
- const pageSplitRegex = /(?:\f|\x0C|(?:\r?\n|^)\s*[-=_]{3,}\s*(?:PAGE|Page|page break|Page Break)[\s\d\w-]*[-=_]{3,}\s*(?:\r?\n|$))/i;
2985
- const rawPages = fullText.split(pageSplitRegex).map((p) => p.trim()).filter((p) => p.length > 0);
3055
+ const extRaw = ctx.metadata.extension?.toLowerCase();
3056
+ const mimeRaw = ctx.metadata.mimeType?.toLowerCase();
3057
+ const isTxt = extRaw === ".txt" || mimeRaw === "text/plain" && (!extRaw || !this.extensions.filter((e) => e !== ".txt").includes(extRaw));
3058
+ let rawPages = [];
3059
+ if (isTxt) {
3060
+ const explicitPages = fullText.split(/(?:\f|\x0C)/);
3061
+ for (const ep of explicitPages) {
3062
+ const lines = ep.split(/\r?\n/);
3063
+ let currentChunk = [];
3064
+ for (let i = 0; i < lines.length; i++) {
3065
+ currentChunk.push(lines[i]);
3066
+ if (currentChunk.length >= 46) {
3067
+ rawPages.push(currentChunk.join("\n"));
3068
+ currentChunk = [];
3069
+ }
3070
+ }
3071
+ if (currentChunk.length > 0 || lines.length === 0) {
3072
+ rawPages.push(currentChunk.join("\n"));
3073
+ }
3074
+ }
3075
+ if (rawPages.length === 0) rawPages = [""];
3076
+ } else {
3077
+ const pageSplitRegex = /(?:\f|\x0C|(?:\r?\n|^)\s*[-=_]{3,}\s*(?:PAGE|Page|page break|Page Break)[\s\d\w-]*[-=_]{3,}\s*(?:\r?\n|$))/i;
3078
+ rawPages = fullText.split(pageSplitRegex).map((p) => p.trim()).filter((p) => p.length > 0);
3079
+ }
2986
3080
  const totalPages = Math.max(1, rawPages.length);
2987
3081
  let currentPage = 1;
2988
3082
  const container = document.createElement("div");
2989
3083
  container.style.width = "100%";
2990
3084
  container.style.height = "100%";
2991
3085
  container.style.overflow = "auto";
2992
- container.style.backgroundColor = "#1e1e1e";
2993
- container.style.color = "#d4d4d4";
2994
- container.style.padding = "16px";
2995
- container.style.boxSizing = "border-box";
2996
3086
  let fontSize = 13;
2997
3087
  let rotation = 0;
3088
+ let zoomLevel = 1;
2998
3089
  const pre = document.createElement("pre");
2999
- pre.style.margin = "0";
3000
- pre.style.fontFamily = "Consolas, Menlo, Monaco, monospace";
3001
- pre.style.fontSize = `${fontSize}px`;
3002
- pre.style.lineHeight = "1.5";
3003
- pre.style.whiteSpace = "pre-wrap";
3004
- pre.style.wordBreak = "break-all";
3005
3090
  const code = document.createElement("code");
3091
+ if (isTxt) {
3092
+ container.style.backgroundColor = "#f1f5f9";
3093
+ container.style.padding = "32px";
3094
+ container.style.boxSizing = "border-box";
3095
+ container.style.display = "flex";
3096
+ container.style.flexDirection = "column";
3097
+ container.style.alignItems = "center";
3098
+ pre.style.margin = "0";
3099
+ pre.style.fontFamily = "Consolas, 'Courier New', monospace";
3100
+ pre.style.fontSize = "13px";
3101
+ pre.style.color = "#1e293b";
3102
+ pre.style.lineHeight = "1.5";
3103
+ pre.style.whiteSpace = "pre-wrap";
3104
+ pre.style.wordBreak = "break-word";
3105
+ pre.style.backgroundColor = "#ffffff";
3106
+ pre.style.width = "816px";
3107
+ pre.style.minHeight = "1056px";
3108
+ pre.style.padding = "72px 56px";
3109
+ pre.style.boxSizing = "border-box";
3110
+ pre.style.boxShadow = "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)";
3111
+ pre.style.borderRadius = "4px";
3112
+ pre.style.transformOrigin = "top center";
3113
+ } else {
3114
+ container.style.backgroundColor = "#1e1e1e";
3115
+ container.style.color = "#d4d4d4";
3116
+ container.style.padding = "16px";
3117
+ container.style.boxSizing = "border-box";
3118
+ pre.style.margin = "0";
3119
+ pre.style.fontFamily = "Consolas, Menlo, Monaco, monospace";
3120
+ pre.style.fontSize = `${fontSize}px`;
3121
+ pre.style.lineHeight = "1.5";
3122
+ pre.style.whiteSpace = "pre-wrap";
3123
+ pre.style.wordBreak = "break-all";
3124
+ pre.style.transformOrigin = "top left";
3125
+ }
3006
3126
  const ext = (ctx.metadata.extension || "").replace(".", "");
3007
3127
  const renderCodePage = (text) => {
3008
- try {
3009
- if (ext && hljs.getLanguage(ext)) {
3010
- code.innerHTML = hljs.highlight(text, { language: ext }).value;
3011
- } else {
3012
- code.innerHTML = hljs.highlightAuto(text).value;
3013
- }
3014
- } catch {
3128
+ if (isTxt) {
3015
3129
  code.textContent = text;
3130
+ } else {
3131
+ try {
3132
+ if (ext && hljs.getLanguage(ext)) {
3133
+ code.innerHTML = hljs.highlight(text, { language: ext }).value;
3134
+ } else {
3135
+ code.innerHTML = hljs.highlightAuto(text).value;
3136
+ }
3137
+ } catch {
3138
+ code.textContent = text;
3139
+ }
3016
3140
  }
3017
3141
  };
3018
- renderCodePage(rawPages[0] || fullText);
3142
+ renderCodePage(rawPages[0] || (isTxt ? "" : fullText));
3019
3143
  pre.appendChild(code);
3020
3144
  container.appendChild(pre);
3021
3145
  let indicator = null;
@@ -3024,15 +3148,22 @@ var CodePlugin = class {
3024
3148
  indicator.className = "fp-code-page-indicator";
3025
3149
  indicator.style.position = "sticky";
3026
3150
  indicator.style.bottom = "16px";
3027
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
3028
- indicator.style.backdropFilter = "blur(8px)";
3029
- indicator.style.color = "#f8fafc";
3151
+ if (isTxt) {
3152
+ indicator.style.backgroundColor = "rgba(255, 255, 255, 0.9)";
3153
+ indicator.style.color = "#334155";
3154
+ indicator.style.border = "1px solid #e2e8f0";
3155
+ indicator.style.boxShadow = "0 1px 3px rgba(0,0,0,0.1)";
3156
+ } else {
3157
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
3158
+ indicator.style.color = "#f8fafc";
3159
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
3160
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
3161
+ indicator.style.backdropFilter = "blur(8px)";
3162
+ }
3030
3163
  indicator.style.fontSize = "12px";
3031
3164
  indicator.style.fontWeight = "600";
3032
3165
  indicator.style.padding = "5px 14px";
3033
3166
  indicator.style.borderRadius = "20px";
3034
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
3035
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
3036
3167
  indicator.style.zIndex = "10";
3037
3168
  indicator.style.userSelect = "none";
3038
3169
  indicator.style.pointerEvents = "none";
@@ -3043,7 +3174,7 @@ var CodePlugin = class {
3043
3174
  }
3044
3175
  const showPage = (pageNum) => {
3045
3176
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
3046
- renderCodePage(rawPages[currentPage - 1] || fullText);
3177
+ renderCodePage(rawPages[currentPage - 1] || (isTxt ? "" : fullText));
3047
3178
  if (indicator) {
3048
3179
  indicator.textContent = `Page ${currentPage} of ${totalPages}`;
3049
3180
  }
@@ -3059,39 +3190,57 @@ var CodePlugin = class {
3059
3190
  ctx.container.innerHTML = "";
3060
3191
  };
3061
3192
  ctx.signal.addEventListener("abort", cleanup);
3193
+ const updateTransform = () => {
3194
+ if (isTxt) {
3195
+ pre.style.transform = `scale(${zoomLevel}) rotate(${rotation}deg)`;
3196
+ } else {
3197
+ pre.style.transform = `rotate(${rotation}deg)`;
3198
+ pre.style.fontSize = `${fontSize}px`;
3199
+ }
3200
+ };
3062
3201
  return {
3063
3202
  destroy: cleanup,
3064
3203
  getPageCount: () => totalPages,
3065
3204
  getCurrentPage: () => currentPage,
3066
3205
  goToPage: (page) => showPage(page),
3067
3206
  zoomIn: () => {
3068
- fontSize = Math.min(32, fontSize + 2);
3069
- pre.style.fontSize = `${fontSize}px`;
3207
+ if (isTxt) {
3208
+ zoomLevel = Math.min(3, zoomLevel + 0.1);
3209
+ } else {
3210
+ fontSize = Math.min(32, fontSize + 2);
3211
+ }
3212
+ updateTransform();
3070
3213
  },
3071
3214
  zoomOut: () => {
3072
- fontSize = Math.max(8, fontSize - 2);
3073
- pre.style.fontSize = `${fontSize}px`;
3215
+ if (isTxt) {
3216
+ zoomLevel = Math.max(0.1, zoomLevel - 0.1);
3217
+ } else {
3218
+ fontSize = Math.max(8, fontSize - 2);
3219
+ }
3220
+ updateTransform();
3074
3221
  },
3075
- getZoom: () => fontSize / 13,
3222
+ getZoom: () => isTxt ? zoomLevel : fontSize / 13,
3076
3223
  setZoom: (level) => {
3077
- fontSize = Math.round(13 * level);
3078
- pre.style.fontSize = `${fontSize}px`;
3224
+ if (isTxt) {
3225
+ zoomLevel = level;
3226
+ } else {
3227
+ fontSize = Math.round(13 * level);
3228
+ }
3229
+ updateTransform();
3079
3230
  },
3080
3231
  fitToPage: () => {
3081
3232
  fontSize = 13;
3082
3233
  rotation = 0;
3083
- pre.style.fontSize = "13px";
3084
- pre.style.transform = "none";
3234
+ zoomLevel = 1;
3235
+ updateTransform();
3085
3236
  },
3086
3237
  rotateCW: () => {
3087
3238
  rotation = (rotation + 90) % 360;
3088
- pre.style.transform = `rotate(${rotation}deg)`;
3089
- pre.style.transformOrigin = "top left";
3239
+ updateTransform();
3090
3240
  },
3091
3241
  rotateCCW: () => {
3092
3242
  rotation = (rotation - 90 + 360) % 360;
3093
- pre.style.transform = `rotate(${rotation}deg)`;
3094
- pre.style.transformOrigin = "top left";
3243
+ updateTransform();
3095
3244
  },
3096
3245
  download: () => {
3097
3246
  const mimeType = ctx.metadata.mimeType || "text/plain";
@@ -3390,7 +3539,7 @@ var MarkdownPlugin = class {
3390
3539
  gfm: true,
3391
3540
  breaks: true
3392
3541
  });
3393
- const cleanHtml = DOMPurify2.sanitize(rawHtml, {
3542
+ const cleanHtml = DOMPurify6.sanitize(rawHtml, {
3394
3543
  USE_PROFILES: { html: true }
3395
3544
  });
3396
3545
  const wrapper = document.createElement("div");
@@ -4026,6 +4175,14 @@ var RtfPlugin = class {
4026
4175
  group: "actions",
4027
4176
  execute: () => instance.download?.()
4028
4177
  },
4178
+ {
4179
+ id: "copy",
4180
+ icon: "copy",
4181
+ label: "Copy Text",
4182
+ type: "button",
4183
+ group: "actions",
4184
+ execute: () => instance.copy?.()
4185
+ },
4029
4186
  {
4030
4187
  id: "print",
4031
4188
  icon: "print",
@@ -4087,14 +4244,31 @@ var RtfPlugin = class {
4087
4244
  } catch (err) {
4088
4245
  console.warn("[RtfPlugin] RTF render error, fallback text:", err);
4089
4246
  const text = new TextDecoder("latin1").decode(ctx.buffer);
4090
- const clean = text.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "");
4091
- const pre = document.createElement("pre");
4092
- pre.style.whiteSpace = "pre-wrap";
4093
- pre.style.fontFamily = "serif";
4094
- pre.style.color = "#333";
4095
- pre.textContent = clean;
4096
- wrapper.appendChild(pre);
4097
- pageElements = [pre];
4247
+ const rawPages = text.split(/\\page\b/).map((segment) => {
4248
+ return segment.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim();
4249
+ }).filter((p) => p.length > 0);
4250
+ const pages = rawPages.length > 0 ? rawPages : [text.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim()];
4251
+ for (let i = 0; i < pages.length; i++) {
4252
+ const pageCard = document.createElement("div");
4253
+ pageCard.className = "fp-rtf-page-card";
4254
+ pageCard.style.backgroundColor = "#ffffff";
4255
+ pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4256
+ pageCard.style.borderRadius = "4px";
4257
+ pageCard.style.padding = "72px 56px";
4258
+ pageCard.style.width = "816px";
4259
+ pageCard.style.minHeight = "1056px";
4260
+ pageCard.style.maxWidth = "100%";
4261
+ pageCard.style.boxSizing = "border-box";
4262
+ pageCard.style.fontFamily = "serif";
4263
+ pageCard.style.fontSize = "12pt";
4264
+ pageCard.style.lineHeight = "1.6";
4265
+ pageCard.style.color = "#1e293b";
4266
+ pageCard.style.whiteSpace = "pre-wrap";
4267
+ pageCard.style.display = i === 0 ? "block" : "none";
4268
+ pageCard.textContent = pages[i];
4269
+ wrapper.appendChild(pageCard);
4270
+ pageElements.push(pageCard);
4271
+ }
4098
4272
  }
4099
4273
  const totalPages = Math.max(1, pageElements.length);
4100
4274
  let currentPage = 1;
@@ -4182,6 +4356,10 @@ var RtfPlugin = class {
4182
4356
  a.click();
4183
4357
  URL.revokeObjectURL(url);
4184
4358
  },
4359
+ copy: () => {
4360
+ const text = wrapper.textContent || "";
4361
+ navigator.clipboard?.writeText(text);
4362
+ },
4185
4363
  print: () => {
4186
4364
  window.print();
4187
4365
  }
@@ -4256,7 +4434,7 @@ var HtmlPreviewPlugin = class {
4256
4434
  }
4257
4435
  async render(ctx) {
4258
4436
  const rawHtml = new TextDecoder("utf-8").decode(ctx.buffer);
4259
- const sanitized = DOMPurify2.sanitize(rawHtml, {
4437
+ const sanitized = DOMPurify6.sanitize(rawHtml, {
4260
4438
  WHOLE_DOCUMENT: true,
4261
4439
  ADD_TAGS: ["style", "link"],
4262
4440
  ADD_ATTR: ["target", "rel"]
@@ -4522,14 +4700,97 @@ var OpenDocumentPlugin = class {
4522
4700
  wrapper.textContent = contentDoc.documentElement.textContent || "Formula content";
4523
4701
  }
4524
4702
  } else {
4525
- wrapper.style.maxWidth = "850px";
4526
- wrapper.style.padding = "48px";
4703
+ wrapper.style.maxWidth = "none";
4704
+ wrapper.style.padding = "0";
4527
4705
  wrapper.style.minHeight = "100%";
4706
+ wrapper.style.backgroundColor = "transparent";
4707
+ wrapper.style.boxShadow = "none";
4708
+ wrapper.style.position = "relative";
4709
+ const dims = this.getPageDimensions(stylesDoc, contentDoc);
4528
4710
  const bodyHtml = this.renderOdfBody(contentDoc, styleMap, imageUrls);
4529
- wrapper.innerHTML = DOMPurify2.sanitize(bodyHtml, {
4530
- ADD_TAGS: ["math", "semantics", "mrow", "mi", "mo", "mn", "msup", "msub"],
4711
+ const tempDiv = document.createElement("div");
4712
+ tempDiv.style.width = `${dims.width - dims.marginLeft - dims.marginRight}px`;
4713
+ tempDiv.style.position = "absolute";
4714
+ tempDiv.style.visibility = "hidden";
4715
+ tempDiv.innerHTML = DOMPurify6.sanitize(bodyHtml, {
4716
+ ADD_TAGS: ["math", "semantics", "mrow", "mi", "mo", "mn", "msup", "msub", "hr"],
4531
4717
  ADD_ATTR: ["style", "colspan", "rowspan"]
4532
4718
  });
4719
+ document.body.appendChild(tempDiv);
4720
+ const contentHeight = dims.height - dims.marginTop - dims.marginBottom;
4721
+ const pageElements = [[]];
4722
+ let currentHeight = 0;
4723
+ let currentPageIdx = 0;
4724
+ Array.from(tempDiv.children).forEach((child) => {
4725
+ const el = child;
4726
+ const style = el.getAttribute("style") || "";
4727
+ const isBreakBefore = style.includes("page-break-before: always");
4728
+ const isBreakAfter = style.includes("page-break-after: always");
4729
+ const isSoftBreak = el.classList.contains("odf-page-break");
4730
+ if (isBreakBefore) {
4731
+ if (pageElements[currentPageIdx].length > 0) {
4732
+ currentPageIdx++;
4733
+ pageElements.push([]);
4734
+ currentHeight = 0;
4735
+ }
4736
+ }
4737
+ const h = el.offsetHeight || 0;
4738
+ if (currentHeight + h > contentHeight && pageElements[currentPageIdx].length > 0 && !isSoftBreak) {
4739
+ currentPageIdx++;
4740
+ pageElements.push([]);
4741
+ currentHeight = 0;
4742
+ }
4743
+ if (!isSoftBreak) {
4744
+ pageElements[currentPageIdx].push(el.cloneNode(true));
4745
+ currentHeight += h;
4746
+ }
4747
+ if (isBreakAfter || isSoftBreak) {
4748
+ currentPageIdx++;
4749
+ pageElements.push([]);
4750
+ currentHeight = 0;
4751
+ }
4752
+ });
4753
+ document.body.removeChild(tempDiv);
4754
+ if (pageElements.length > 1 && pageElements[pageElements.length - 1].length === 0) {
4755
+ pageElements.pop();
4756
+ }
4757
+ totalPages = Math.max(1, pageElements.length);
4758
+ pageElements.forEach((elements, idx) => {
4759
+ const page = document.createElement("div");
4760
+ page.className = `fp-odt-page fp-odt-page-${idx + 1}`;
4761
+ page.style.width = `${dims.width}px`;
4762
+ page.style.minHeight = `${dims.height}px`;
4763
+ page.style.padding = `${dims.marginTop}px ${dims.marginRight}px ${dims.marginBottom}px ${dims.marginLeft}px`;
4764
+ page.style.margin = "0 auto";
4765
+ page.style.backgroundColor = "#ffffff";
4766
+ page.style.boxShadow = "0 2px 10px rgba(0,0,0,0.08)";
4767
+ page.style.borderRadius = "4px";
4768
+ page.style.boxSizing = "border-box";
4769
+ page.style.display = idx === 0 ? "block" : "none";
4770
+ page.style.position = "absolute";
4771
+ page.style.top = "0";
4772
+ page.style.left = "50%";
4773
+ page.style.transform = "translateX(-50%)";
4774
+ elements.forEach((el) => page.appendChild(el));
4775
+ wrapper.appendChild(page);
4776
+ slides.push(page);
4777
+ });
4778
+ const pageIndicator = document.createElement("div");
4779
+ pageIndicator.className = "fp-odt-page-indicator";
4780
+ pageIndicator.style.position = "sticky";
4781
+ pageIndicator.style.bottom = "16px";
4782
+ pageIndicator.style.left = "50%";
4783
+ pageIndicator.style.transform = "translateX(-50%)";
4784
+ pageIndicator.style.backgroundColor = "rgba(0, 0, 0, 0.6)";
4785
+ pageIndicator.style.color = "#fff";
4786
+ pageIndicator.style.padding = "6px 12px";
4787
+ pageIndicator.style.borderRadius = "16px";
4788
+ pageIndicator.style.fontSize = "12px";
4789
+ pageIndicator.style.zIndex = "100";
4790
+ pageIndicator.style.display = "inline-block";
4791
+ pageIndicator.style.width = "fit-content";
4792
+ pageIndicator.textContent = `Page 1 of ${totalPages}`;
4793
+ container.appendChild(pageIndicator);
4533
4794
  }
4534
4795
  const cleanup = () => {
4535
4796
  imageUrls.forEach((url) => URL.revokeObjectURL(url));
@@ -4538,11 +4799,15 @@ var OpenDocumentPlugin = class {
4538
4799
  };
4539
4800
  ctx.signal.addEventListener("abort", cleanup);
4540
4801
  const goToPage = (page) => {
4541
- if (!isPresentation || page < 1 || page > totalPages) return;
4802
+ if (page < 1 || page > totalPages) return;
4542
4803
  currentPage = page;
4543
4804
  slides.forEach((s, idx) => {
4544
4805
  s.style.display = idx === page - 1 ? "block" : "none";
4545
4806
  });
4807
+ const indicator = container.querySelector(".fp-odt-page-indicator");
4808
+ if (indicator) {
4809
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4810
+ }
4546
4811
  ctx.emit("page-change", { page: currentPage, total: totalPages });
4547
4812
  };
4548
4813
  return {
@@ -4611,6 +4876,43 @@ var OpenDocumentPlugin = class {
4611
4876
  }
4612
4877
  };
4613
4878
  }
4879
+ getPageDimensions(stylesDoc, contentDoc) {
4880
+ let width = 850;
4881
+ let height = 1123;
4882
+ let marginTop = 48;
4883
+ let marginBottom = 48;
4884
+ let marginLeft = 48;
4885
+ let marginRight = 48;
4886
+ const parseUnit = (val) => {
4887
+ if (!val) return null;
4888
+ if (val.endsWith("cm")) return parseFloat(val) * 37.8;
4889
+ if (val.endsWith("mm")) return parseFloat(val) * 3.78;
4890
+ if (val.endsWith("in")) return parseFloat(val) * 96;
4891
+ if (val.endsWith("pt")) return parseFloat(val) * 1.33;
4892
+ if (val.endsWith("px")) return parseFloat(val);
4893
+ return parseFloat(val);
4894
+ };
4895
+ const docs = [stylesDoc, contentDoc].filter(Boolean);
4896
+ for (const doc of docs) {
4897
+ const pageLayout = doc.querySelector("page-layout-properties, [page-width]");
4898
+ if (pageLayout) {
4899
+ const w = parseUnit(pageLayout.getAttribute("fo:page-width") || pageLayout.getAttribute("page-width"));
4900
+ const h = parseUnit(pageLayout.getAttribute("fo:page-height") || pageLayout.getAttribute("page-height"));
4901
+ const mt = parseUnit(pageLayout.getAttribute("fo:margin-top") || pageLayout.getAttribute("margin-top"));
4902
+ const mb = parseUnit(pageLayout.getAttribute("fo:margin-bottom") || pageLayout.getAttribute("margin-bottom"));
4903
+ const ml = parseUnit(pageLayout.getAttribute("fo:margin-left") || pageLayout.getAttribute("margin-left"));
4904
+ const mr = parseUnit(pageLayout.getAttribute("fo:margin-right") || pageLayout.getAttribute("margin-right"));
4905
+ if (w !== null) width = w;
4906
+ if (h !== null) height = h;
4907
+ if (mt !== null) marginTop = mt;
4908
+ if (mb !== null) marginBottom = mb;
4909
+ if (ml !== null) marginLeft = ml;
4910
+ if (mr !== null) marginRight = mr;
4911
+ break;
4912
+ }
4913
+ }
4914
+ return { width, height, marginTop, marginBottom, marginLeft, marginRight };
4915
+ }
4614
4916
  extractStyles(stylesDoc, contentDoc) {
4615
4917
  const map = /* @__PURE__ */ new Map();
4616
4918
  const styleNodes = [];
@@ -4633,14 +4935,21 @@ var OpenDocumentPlugin = class {
4633
4935
  if (color) css += `color: ${color}; `;
4634
4936
  if (size) css += `font-size: ${size}; `;
4635
4937
  }
4636
- const paraProp = node.querySelector("paragraph-properties, [text-align]");
4938
+ let paraProp = node.querySelector("paragraph-properties, [text-align]");
4939
+ if (!paraProp) {
4940
+ paraProp = Array.from(node.children).find((c) => c.tagName.includes("paragraph-properties")) || null;
4941
+ }
4637
4942
  if (paraProp) {
4638
4943
  const align = paraProp.getAttribute("fo:text-align") || paraProp.getAttribute("text-align");
4639
4944
  const mt = paraProp.getAttribute("fo:margin-top") || paraProp.getAttribute("margin-top");
4640
4945
  const mb = paraProp.getAttribute("fo:margin-bottom") || paraProp.getAttribute("margin-bottom");
4946
+ const breakBefore = paraProp.getAttribute("fo:break-before") || paraProp.getAttribute("break-before");
4947
+ const breakAfter = paraProp.getAttribute("fo:break-after") || paraProp.getAttribute("break-after");
4641
4948
  if (align) css += `text-align: ${align}; `;
4642
4949
  if (mt) css += `margin-top: ${mt}; `;
4643
4950
  if (mb) css += `margin-bottom: ${mb}; `;
4951
+ if (breakBefore === "page") css += "page-break-before: always; ";
4952
+ if (breakAfter === "page") css += "page-break-after: always; ";
4644
4953
  }
4645
4954
  if (css) map.set(name, css);
4646
4955
  }
@@ -4725,6 +5034,8 @@ var OpenDocumentPlugin = class {
4725
5034
  result += "&emsp;";
4726
5035
  } else if (tag === "line-break") {
4727
5036
  result += "<br/>";
5037
+ } else if (tag === "soft-page-break") {
5038
+ result += '<hr class="odf-page-break" style="page-break-after: always; border: none; margin: 0; padding: 0; height: 0;" />';
4728
5039
  } else {
4729
5040
  result += el.textContent || "";
4730
5041
  }
@@ -4885,20 +5196,9 @@ var DocPlugin = class {
4885
5196
  container.style.overflow = "auto";
4886
5197
  container.style.padding = "32px 16px";
4887
5198
  container.style.backgroundColor = "#f1f5f9";
4888
- const wrapper = document.createElement("div");
4889
- wrapper.className = "fp-doc-wrapper";
4890
- wrapper.style.maxWidth = "850px";
4891
- wrapper.style.margin = "0 auto";
4892
- wrapper.style.backgroundColor = "#ffffff";
4893
- wrapper.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4894
- wrapper.style.borderRadius = "4px";
4895
- wrapper.style.padding = "56px 48px";
4896
- wrapper.style.minHeight = "100%";
4897
- wrapper.style.transformOrigin = "top center";
4898
- wrapper.style.transition = "transform 0.2s ease";
4899
- wrapper.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
4900
- wrapper.style.color = "#1e293b";
4901
- container.appendChild(wrapper);
5199
+ container.style.display = "flex";
5200
+ container.style.justifyContent = "center";
5201
+ container.style.alignItems = "flex-start";
4902
5202
  ctx.container.appendChild(container);
4903
5203
  let scale = 1;
4904
5204
  let extractedRawText = "";
@@ -4925,21 +5225,27 @@ var DocPlugin = class {
4925
5225
  const rawPages = this.splitIntoPages(extractedRawText);
4926
5226
  const totalPages = Math.max(1, rawPages.length);
4927
5227
  let currentPage = 1;
4928
- wrapper.innerHTML = "";
4929
5228
  const pageCards = [];
4930
5229
  for (let i = 0; i < totalPages; i++) {
4931
5230
  const pageCard = document.createElement("div");
4932
5231
  pageCard.className = "fp-doc-page-card";
5232
+ pageCard.style.width = "816px";
5233
+ pageCard.style.height = "1056px";
4933
5234
  pageCard.style.backgroundColor = "#ffffff";
4934
5235
  pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4935
5236
  pageCard.style.borderRadius = "4px";
4936
- pageCard.style.padding = "56px 48px";
4937
- pageCard.style.minHeight = "100%";
5237
+ pageCard.style.padding = "96px 72px";
5238
+ pageCard.style.boxSizing = "border-box";
5239
+ pageCard.style.overflow = "hidden";
4938
5240
  pageCard.style.display = i === 0 ? "block" : "none";
5241
+ pageCard.style.transformOrigin = "top center";
5242
+ pageCard.style.transition = "transform 0.2s ease";
5243
+ pageCard.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
5244
+ pageCard.style.color = "#1e293b";
4939
5245
  if (isFallback && i === 0) {
4940
5246
  pageCard.innerHTML = `
4941
5247
  <div style="border-bottom: 1px solid #e2e8f0; padding-bottom: 12px; margin-bottom: 24px;">
4942
- <h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${DOMPurify2.sanitize(ctx.metadata.name || "Word Document (.doc)")}</h2>
5248
+ <h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${DOMPurify6.sanitize(ctx.metadata.name || "Word Document (.doc)")}</h2>
4943
5249
  <span style="font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px;">Legacy Word 97-2003 Binary Preview</span>
4944
5250
  </div>
4945
5251
  ${this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document")}
@@ -4947,15 +5253,17 @@ var DocPlugin = class {
4947
5253
  } else {
4948
5254
  pageCard.innerHTML = this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document");
4949
5255
  }
4950
- wrapper.appendChild(pageCard);
5256
+ container.appendChild(pageCard);
4951
5257
  pageCards.push(pageCard);
4952
5258
  }
4953
5259
  let indicator = null;
4954
5260
  if (totalPages > 1) {
4955
5261
  indicator = document.createElement("div");
4956
5262
  indicator.className = "fp-doc-page-indicator";
4957
- indicator.style.position = "sticky";
5263
+ indicator.style.position = "fixed";
4958
5264
  indicator.style.bottom = "16px";
5265
+ indicator.style.left = "50%";
5266
+ indicator.style.transform = "translateX(-50%)";
4959
5267
  indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
4960
5268
  indicator.style.backdropFilter = "blur(8px)";
4961
5269
  indicator.style.color = "#f8fafc";
@@ -4969,17 +5277,25 @@ var DocPlugin = class {
4969
5277
  indicator.style.userSelect = "none";
4970
5278
  indicator.style.pointerEvents = "none";
4971
5279
  indicator.style.textAlign = "center";
4972
- indicator.style.width = "fit-content";
4973
- indicator.style.margin = "16px auto 0";
4974
5280
  container.appendChild(indicator);
4975
5281
  }
5282
+ let rotation = 0;
5283
+ const applyTransform = () => {
5284
+ const activeCard = pageCards[currentPage - 1];
5285
+ if (activeCard) {
5286
+ activeCard.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5287
+ }
5288
+ };
4976
5289
  const showPage = (pageNum) => {
4977
5290
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
4978
- if (totalPages > 1) {
4979
- pageCards.forEach((card, idx) => {
4980
- card.style.display = idx + 1 === currentPage ? "block" : "none";
4981
- });
4982
- }
5291
+ pageCards.forEach((card, idx) => {
5292
+ if (idx + 1 === currentPage) {
5293
+ card.style.display = "block";
5294
+ card.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5295
+ } else {
5296
+ card.style.display = "none";
5297
+ }
5298
+ });
4983
5299
  if (indicator) {
4984
5300
  indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4985
5301
  }
@@ -4988,19 +5304,13 @@ var DocPlugin = class {
4988
5304
  if (totalPages > 1) {
4989
5305
  showPage(1);
4990
5306
  }
4991
- let rotation = 0;
4992
5307
  const calculateFitScale = () => {
4993
- const activeCard = pageCards[currentPage - 1] || wrapper;
4994
- const elW = activeCard.offsetWidth || 850;
4995
- const elH = activeCard.offsetHeight || 1e3;
5308
+ const elW = 816;
5309
+ const elH = 1056;
4996
5310
  const availW = Math.max(200, ctx.container.clientWidth - 48);
4997
5311
  const availH = Math.max(200, ctx.container.clientHeight - 80);
4998
5312
  return Math.min(1.1, Math.min(availW / elW, availH / elH));
4999
5313
  };
5000
- const applyTransform = () => {
5001
- wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5002
- wrapper.style.transformOrigin = "top center";
5003
- };
5004
5314
  setTimeout(() => {
5005
5315
  scale = calculateFitScale();
5006
5316
  applyTransform();
@@ -5158,8 +5468,28 @@ var DocPlugin = class {
5158
5468
  }
5159
5469
  splitIntoPages(text) {
5160
5470
  if (!text) return [""];
5161
- 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);
5162
- return parts.length > 0 ? parts : [text];
5471
+ 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);
5472
+ if (explicitParts.length === 0) explicitParts.push(text);
5473
+ const maxLinesPerPage = 48;
5474
+ const finalPages = [];
5475
+ for (const part of explicitParts) {
5476
+ const lines = part.split(/\r?\n/);
5477
+ let currentLines = [];
5478
+ let count = 0;
5479
+ for (const line of lines) {
5480
+ currentLines.push(line);
5481
+ count++;
5482
+ if (count >= maxLinesPerPage) {
5483
+ finalPages.push(currentLines.join("\n"));
5484
+ currentLines = [];
5485
+ count = 0;
5486
+ }
5487
+ }
5488
+ if (currentLines.length > 0) {
5489
+ finalPages.push(currentLines.join("\n"));
5490
+ }
5491
+ }
5492
+ return finalPages.length > 0 ? finalPages : [text];
5163
5493
  }
5164
5494
  /**
5165
5495
  * Format cleaned extracted text into attractive HTML paragraphs, headings, and lists
@@ -5168,17 +5498,65 @@ var DocPlugin = class {
5168
5498
  const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").split("\n");
5169
5499
  let html = "";
5170
5500
  let inList = false;
5171
- for (const rawLine of lines) {
5172
- const line = rawLine.trim();
5501
+ let tableLines = [];
5502
+ const flushTable = () => {
5503
+ if (tableLines.length > 0) {
5504
+ html += '<table style="width: 100%; border-collapse: collapse; margin: 16px 0; font-size: 12px;">';
5505
+ for (const tLine of tableLines) {
5506
+ html += "<tr>";
5507
+ const cols = tLine.split(" ");
5508
+ for (const col of cols) {
5509
+ html += `<td style="border: 1px solid #cbd5e1; padding: 6px 8px;">${DOMPurify6.sanitize(col.trim())}</td>`;
5510
+ }
5511
+ html += "</tr>";
5512
+ }
5513
+ html += "</table>";
5514
+ tableLines = [];
5515
+ }
5516
+ };
5517
+ let i = 0;
5518
+ while (i < lines.length) {
5519
+ let line = lines[i];
5520
+ let tabCount = (line.match(/\t/g) || []).length;
5521
+ if (tabCount > 0) {
5522
+ let consecutiveTableLines = 1;
5523
+ let j = i + 1;
5524
+ while (j < lines.length) {
5525
+ const nextTabCount = (lines[j].match(/\t/g) || []).length;
5526
+ if (nextTabCount === tabCount) {
5527
+ consecutiveTableLines++;
5528
+ j++;
5529
+ } else {
5530
+ break;
5531
+ }
5532
+ }
5533
+ if (consecutiveTableLines >= 3) {
5534
+ if (inList) {
5535
+ html += "</ul>";
5536
+ inList = false;
5537
+ }
5538
+ tableLines = lines.slice(i, j);
5539
+ flushTable();
5540
+ i = j;
5541
+ continue;
5542
+ }
5543
+ }
5544
+ line = line.trim();
5173
5545
  if (!line) {
5174
5546
  if (inList) {
5175
5547
  html += "</ul>";
5176
5548
  inList = false;
5177
5549
  }
5550
+ html += '<div style="height: 1.15em;"></div>';
5551
+ i++;
5552
+ continue;
5553
+ }
5554
+ if (/^[\x00-\x1F\x7F-\x9F]+$/.test(line)) {
5555
+ i++;
5178
5556
  continue;
5179
5557
  }
5180
- if (/^[\x00-\x1F\x7F-\x9F]+$/.test(line)) continue;
5181
- if (line.includes("Normal.dot") || line.includes("Microsoft Word") || line.includes("Times New Roman") && line.length < 30) {
5558
+ if ((line.includes("Normal.dot") || line.includes("Microsoft Word") || line.includes("Times New Roman")) && line.length < 30) {
5559
+ i++;
5182
5560
  continue;
5183
5561
  }
5184
5562
  if (line.length < 60 && !line.endsWith(".") && (/^[A-Z0-9\s:_-]+$/.test(line) || line.startsWith("#"))) {
@@ -5186,24 +5564,26 @@ var DocPlugin = class {
5186
5564
  html += "</ul>";
5187
5565
  inList = false;
5188
5566
  }
5189
- 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>`;
5567
+ 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>`;
5190
5568
  } else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
5191
5569
  if (!inList) {
5192
5570
  html += '<ul style="margin: 8px 0; padding-left: 24px;">';
5193
5571
  inList = true;
5194
5572
  }
5195
5573
  const bulletText = line.replace(/^[•\-\*]\s*/, "");
5196
- html += `<li style="margin: 4px 0; line-height: 1.6;">${DOMPurify2.sanitize(bulletText)}</li>`;
5574
+ html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6.sanitize(bulletText)}</li>`;
5197
5575
  } else {
5198
5576
  if (inList) {
5199
5577
  html += "</ul>";
5200
5578
  inList = false;
5201
5579
  }
5202
- html += `<p style="line-height: 1.7; margin: 10px 0; font-size: 14px; text-align: justify;">${DOMPurify2.sanitize(line)}</p>`;
5580
+ html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6.sanitize(line)}</p>`;
5203
5581
  }
5582
+ i++;
5204
5583
  }
5205
5584
  if (inList) html += "</ul>";
5206
- return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify2.sanitize(filename)})</p>`;
5585
+ flushTable();
5586
+ return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify6.sanitize(filename)})</p>`;
5207
5587
  }
5208
5588
  };
5209
5589
  function docPlugin() {
@@ -5381,7 +5761,7 @@ var PptPlugin = class {
5381
5761
  if (s.pictureUrl) {
5382
5762
  contentHtml = `
5383
5763
  <div style="flex: 1; display: flex; justify-content: center; align-items: center; padding: 12px; overflow: hidden;">
5384
- <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;" />
5764
+ <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;" />
5385
5765
  </div>
5386
5766
  `;
5387
5767
  } else if (s.tableColumns.length > 0) {
@@ -5391,7 +5771,7 @@ var PptPlugin = class {
5391
5771
  <table style="width: 100%; border-collapse: collapse; border: 1px solid #cbd5e1; border-radius: 6px; overflow: hidden; background: #ffffff;">
5392
5772
  <thead>
5393
5773
  <tr style="background: #e2e8f0; color: #1e293b; font-weight: 600; font-size: 14px;">
5394
- ${cols.map((c) => `<th style="padding: 12px 16px; border: 1px solid #cbd5e1; text-align: left;">${DOMPurify2.sanitize(c)}</th>`).join("")}
5774
+ ${cols.map((c) => `<th style="padding: 12px 16px; border: 1px solid #cbd5e1; text-align: left;">${DOMPurify6.sanitize(c)}</th>`).join("")}
5395
5775
  </tr>
5396
5776
  </thead>
5397
5777
  <tbody>
@@ -5407,7 +5787,7 @@ var PptPlugin = class {
5407
5787
  } else {
5408
5788
  const pTags = s.paragraphs.map((p) => {
5409
5789
  const lines = p.split(/[\r\n]+/).map((l) => l.trim()).filter(Boolean);
5410
- 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("");
5790
+ 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("");
5411
5791
  }).join("");
5412
5792
  contentHtml = `
5413
5793
  <div style="flex: 1; overflow: auto; padding: 4px 8px; display: flex; flex-direction: column; justify-content: flex-start;">
@@ -5420,11 +5800,11 @@ var PptPlugin = class {
5420
5800
  <!-- Header Banner matching PowerPoint design -->
5421
5801
  <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;">
5422
5802
  <h1 style="margin: 0; font-size: 26px; font-weight: 700; color: #1e293b; letter-spacing: -0.3px;">
5423
- ${DOMPurify2.sanitize(s.title)}
5803
+ ${DOMPurify6.sanitize(s.title)}
5424
5804
  </h1>
5425
5805
  ${s.subtitle ? `
5426
5806
  <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);">
5427
- ${DOMPurify2.sanitize(s.subtitle)}
5807
+ ${DOMPurify6.sanitize(s.subtitle)}
5428
5808
  </span>
5429
5809
  ` : `
5430
5810
  <span style="font-size: 12px; color: #365314; font-weight: 600;">