@files-preview-app/preview-file 1.3.4 → 1.3.5

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
@@ -5330,7 +5330,7 @@ function cleanRtfText(raw) {
5330
5330
  return String.fromCharCode(num);
5331
5331
  }).replace(/\\'([0-9a-fA-F]{2})/g, (_, hex) => {
5332
5332
  return String.fromCharCode(parseInt(hex, 16));
5333
- }).replace(/\\tab\b/g, " ").replace(/\\emdash\b/g, "\u2014").replace(/\\endash\b/g, "\u2013").replace(/\\bullet\b/g, "\u2022").replace(/\\lquote\b/g, "\u2018").replace(/\\rquote\b/g, "\u2019").replace(/\\ldblquote\b/g, "\u201C").replace(/\\rdblquote\b/g, "\u201D").replace(/\\\w+(?:-?\d+)?\s?/g, " ").replace(/[{}\r\n]/g, " ").replace(/\s+/g, " ").trim();
5333
+ }).replace(/\\tab\b/g, " ").replace(/\\emdash\b/g, "\u2014").replace(/\\endash\b/g, "\u2013").replace(/\\bullet\b/g, "\u2022").replace(/\\lquote\b/g, "\u2018").replace(/\\rquote\b/g, "\u2019").replace(/\\ldblquote\b/g, "\u201C").replace(/\\rdblquote\b/g, "\u201D").replace(/\\[a-zA-Z]+-?\d*\s?/g, " ").replace(/[{}\r\n]/g, " ").replace(/\s+/g, " ").trim();
5334
5334
  }
5335
5335
  function parseRtfTables(rtfText) {
5336
5336
  const rowSegments = rtfText.split(/\\row\b/);
@@ -5370,17 +5370,12 @@ function parseRtfTables(rtfText) {
5370
5370
  }
5371
5371
  const isNewTable = !currentTable || segmentStart - currentTable.lastEnd > 2500;
5372
5372
  if (isNewTable) {
5373
- const prevTextSlice = rtfText.slice(Math.max(0, segmentStart - 4e3), segmentStart);
5374
- const cleanPrev = cleanRtfText(prevTextSlice);
5375
- const anchorBefore = cleanPrev.slice(-80).trim();
5376
5373
  currentTable = {
5377
5374
  id: `RTF_TABLE_${rawTables.length}`,
5378
5375
  startPos: segmentStart,
5379
5376
  lastEnd: segmentEnd,
5380
5377
  rows: [],
5381
- columnWidths: cellWidths,
5382
- anchorBefore,
5383
- anchorAfter: ""
5378
+ columnWidths: cellWidths
5384
5379
  };
5385
5380
  rawTables.push(currentTable);
5386
5381
  }
@@ -5394,20 +5389,13 @@ function parseRtfTables(rtfText) {
5394
5389
  }
5395
5390
  charOffset += segment.length + 4;
5396
5391
  }
5397
- rawTables.forEach((tbl) => {
5398
- const afterSlice = rtfText.slice(tbl.lastEnd, Math.min(rtfText.length, tbl.lastEnd + 2e3));
5399
- const cleanAfter = cleanRtfText(afterSlice);
5400
- tbl.anchorAfter = cleanAfter.slice(0, 80).trim();
5401
- });
5402
5392
  return rawTables.map((tbl) => {
5403
5393
  const totalW = (tbl.columnWidths || []).reduce((a, b) => a + b, 0);
5404
5394
  const colPercents = totalW > 0 ? tbl.columnWidths.map((w) => Math.round(w / totalW * 100)) : [];
5405
5395
  return {
5406
5396
  id: tbl.id,
5407
5397
  rows: tbl.rows.map((r) => r.cells),
5408
- colPercents,
5409
- anchorBefore: tbl.anchorBefore,
5410
- anchorAfter: tbl.anchorAfter
5398
+ colPercents
5411
5399
  };
5412
5400
  });
5413
5401
  }
@@ -5420,8 +5408,6 @@ function renderRtfTableElement(table) {
5420
5408
  tableEl.style.fontSize = "10pt";
5421
5409
  tableEl.style.lineHeight = "1.5";
5422
5410
  tableEl.style.border = "1px solid #cbd5e1";
5423
- tableEl.style.borderRadius = "4px";
5424
- tableEl.style.boxSizing = "border-box";
5425
5411
  tableEl.style.backgroundColor = "#ffffff";
5426
5412
  const thead = document.createElement("thead");
5427
5413
  const tbody = document.createElement("tbody");
@@ -5430,27 +5416,17 @@ function renderRtfTableElement(table) {
5430
5416
  const isHeader = rIdx === 0;
5431
5417
  if (isHeader) {
5432
5418
  tr.style.backgroundColor = "#f8fafc";
5433
- tr.style.borderBottom = "2px solid #cbd5e1";
5434
- } else {
5435
- tr.style.borderBottom = "1px solid #e2e8f0";
5436
- if (rIdx % 2 === 0) {
5437
- tr.style.backgroundColor = "#fcfdfe";
5438
- }
5439
5419
  }
5440
5420
  rowCells.forEach((cellText, cIdx) => {
5441
5421
  const cellEl = document.createElement(isHeader ? "th" : "td");
5442
5422
  cellEl.style.padding = "8px 12px";
5443
5423
  cellEl.style.border = "1px solid #cbd5e1";
5444
5424
  cellEl.style.verticalAlign = "top";
5445
- cellEl.style.color = isHeader ? "#0f172a" : "#1e293b";
5446
- cellEl.style.fontWeight = isHeader ? "600" : "normal";
5447
5425
  if (table.colPercents && table.colPercents[cIdx]) {
5448
5426
  cellEl.style.width = `${table.colPercents[cIdx]}%`;
5449
5427
  }
5450
5428
  if (/^\d+$/.test(cellText.trim()) || cellText.trim() === "#") {
5451
5429
  cellEl.style.textAlign = "center";
5452
- } else {
5453
- cellEl.style.textAlign = "left";
5454
5430
  }
5455
5431
  cellEl.textContent = cellText;
5456
5432
  tr.appendChild(cellEl);
@@ -5465,23 +5441,19 @@ function renderRtfTableElement(table) {
5465
5441
  tableEl.appendChild(tbody);
5466
5442
  return tableEl;
5467
5443
  }
5468
- function extractMediaFingerprint(el) {
5469
- const img = el.tagName.toLowerCase() === "img" ? el : el.querySelector("img");
5470
- if (img && img.src) {
5471
- const s = img.src;
5472
- return s.length > 300 ? `img_${s.slice(0, 80)}_${s.length}_${s.slice(-80)}` : `img_${s}`;
5473
- }
5474
- const innerImg = el.querySelector("image");
5475
- if (innerImg) {
5476
- const h = innerImg.getAttribute("xlink:href") || innerImg.getAttribute("href") || "";
5477
- if (h) return h.length > 300 ? `svgimg_${h.slice(0, 80)}_${h.length}_${h.slice(-80)}` : `svgimg_${h}`;
5478
- }
5479
- const svg = el.tagName.toLowerCase() === "svg" ? el : el.querySelector("svg");
5480
- if (svg) {
5481
- const vb = svg.getAttribute("viewBox") || "";
5482
- return `svg_${vb}_${svg.innerHTML.length}`;
5483
- }
5484
- return null;
5444
+ function isHeadingElement(el) {
5445
+ if (["H1", "H2", "H3", "H4"].includes(el.tagName)) return true;
5446
+ const spans = el.querySelectorAll ? Array.from(el.querySelectorAll("span, b, strong, div")) : [];
5447
+ for (const s of spans) {
5448
+ const hEl = s;
5449
+ const fs = parseFloat(hEl.style.fontSize) || 0;
5450
+ const isBold = hEl.style.fontWeight === "bold" || parseInt(hEl.style.fontWeight, 10) >= 600 || ["B", "STRONG"].includes(hEl.tagName);
5451
+ if (isBold && fs >= 14) return true;
5452
+ }
5453
+ const elFs = parseFloat(el.style.fontSize) || 0;
5454
+ const elBold = el.style.fontWeight === "bold" || parseInt(el.style.fontWeight, 10) >= 600;
5455
+ if (elBold && elFs >= 14) return true;
5456
+ return false;
5485
5457
  }
5486
5458
  var RtfPlugin = class {
5487
5459
  id = "rtf";
@@ -5594,10 +5566,66 @@ var RtfPlugin = class {
5594
5566
  return actions;
5595
5567
  }
5596
5568
  async render(ctx) {
5569
+ const rawRtf = new TextDecoder("latin1").decode(ctx.buffer);
5570
+ const paperwMatch = rawRtf.match(/\\paperw(\d+)/);
5571
+ const paperhMatch = rawRtf.match(/\\paperh(\d+)/);
5572
+ const marglMatch = rawRtf.match(/\\margl(\d+)/);
5573
+ const margrMatch = rawRtf.match(/\\margr(\d+)/);
5574
+ const margtMatch = rawRtf.match(/\\margt(\d+)/);
5575
+ const margbMatch = rawRtf.match(/\\margb(\d+)/);
5576
+ const paperwTwips = paperwMatch ? parseInt(paperwMatch[1], 10) : 11906;
5577
+ const paperhTwips = paperhMatch ? parseInt(paperhMatch[1], 10) : 16838;
5578
+ const marglTwips = marglMatch ? parseInt(marglMatch[1], 10) : 1134;
5579
+ const margrTwips = margrMatch ? parseInt(margrMatch[1], 10) : 1134;
5580
+ const margtTwips = margtMatch ? parseInt(margtMatch[1], 10) : 1134;
5581
+ const margbTwips = margbMatch ? parseInt(margbMatch[1], 10) : 1134;
5582
+ const pageWidth = Math.round(paperwTwips / 15);
5583
+ const pageHeight = Math.round(paperhTwips / 15);
5584
+ const padLeft = Math.round(marglTwips / 15);
5585
+ const padRight = Math.round(margrTwips / 15);
5586
+ const padTop = Math.round(margtTwips / 15);
5587
+ const padBottom = Math.round(margbTwips / 15);
5588
+ const printableHeight = pageHeight - padTop - padBottom;
5589
+ const extractedImages = [];
5590
+ let p = 0;
5591
+ while ((p = rawRtf.indexOf("\\pict", p)) !== -1) {
5592
+ const startP = p;
5593
+ p += 5;
5594
+ const header = rawRtf.substring(startP, startP + 600);
5595
+ let mime = "";
5596
+ let magic = "";
5597
+ if (header.includes("\\pngblip")) {
5598
+ mime = "image/png";
5599
+ magic = "89504e47";
5600
+ } else if (header.includes("\\jpegblip")) {
5601
+ mime = "image/jpeg";
5602
+ magic = "ffd8ff";
5603
+ }
5604
+ if (!mime || !magic) continue;
5605
+ const magicPos = rawRtf.indexOf(magic, startP);
5606
+ if (magicPos === -1 || magicPos - startP > 3e3) continue;
5607
+ let endP = magicPos;
5608
+ while (endP < rawRtf.length && /[0-9a-fA-F\r\n\s]/.test(rawRtf[endP])) {
5609
+ endP++;
5610
+ }
5611
+ const cleanHex = rawRtf.substring(magicPos, endP).replace(/[\r\n\s]/g, "");
5612
+ if (cleanHex.length < 100) continue;
5613
+ const hexLen = cleanHex.length / 2;
5614
+ const u8 = new Uint8Array(hexLen);
5615
+ for (let k = 0; k < hexLen; k++) {
5616
+ u8[k] = parseInt(cleanHex.substr(k * 2, 2), 16);
5617
+ }
5618
+ let bStr = "";
5619
+ for (let k = 0; k < u8.length; k += 8192) {
5620
+ bStr += String.fromCharCode.apply(null, Array.from(u8.subarray(k, k + 8192)));
5621
+ }
5622
+ extractedImages.push({ mime, dataUrl: `data:${mime};base64,${btoa(bStr)}`, offset: startP });
5623
+ }
5624
+ const tables = parseRtfTables(rawRtf);
5597
5625
  const wrapper = document.createElement("div");
5598
5626
  wrapper.className = "fp-rtf-wrapper";
5599
5627
  wrapper.style.padding = "0";
5600
- wrapper.style.width = "816px";
5628
+ wrapper.style.width = `${pageWidth}px`;
5601
5629
  wrapper.style.margin = "0 auto";
5602
5630
  wrapper.style.boxSizing = "border-box";
5603
5631
  wrapper.style.display = "flex";
@@ -5617,9 +5645,8 @@ var RtfPlugin = class {
5617
5645
  let isUserZoomed = false;
5618
5646
  let fitMode = ctx?.options?.fitMode || "width";
5619
5647
  const calculateFitScale = (mode = fitMode) => {
5620
- const activeEl = pageElements[currentPage - 1] || wrapper;
5621
- const elW = 816;
5622
- const singlePageH = activeEl?.offsetHeight || 1056;
5648
+ const elW = pageWidth;
5649
+ const singlePageH = pageHeight;
5623
5650
  const availW = Math.max(280, ctx.container.clientWidth - 32);
5624
5651
  const availH = Math.max(280, ctx.container.clientHeight - 32);
5625
5652
  const sW = availW / elW;
@@ -5632,8 +5659,7 @@ var RtfPlugin = class {
5632
5659
  const applyTransform = () => {
5633
5660
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5634
5661
  wrapper.style.transformOrigin = "top center";
5635
- const activeEl = pageElements[currentPage - 1];
5636
- const baseH = activeEl?.offsetHeight || 1056;
5662
+ const baseH = pageHeight;
5637
5663
  const scaledH = baseH * scale;
5638
5664
  const extraH = Math.max(0, scaledH - baseH);
5639
5665
  wrapper.style.marginBottom = `${extraH + 32}px`;
@@ -5653,193 +5679,220 @@ var RtfPlugin = class {
5653
5679
  if (typeof RTFJS.loggingEnabled === "function") {
5654
5680
  RTFJS.loggingEnabled(false);
5655
5681
  }
5656
- const rawText = new TextDecoder("latin1").decode(ctx.buffer);
5657
- const tables = parseRtfTables(rawText);
5658
- const seenMediaSignatures = /* @__PURE__ */ new Set();
5659
5682
  const doc = new RTFJS.Document(ctx.buffer, {
5660
5683
  onPicture: (isLegacy, createPic) => {
5661
- if (isLegacy === true) {
5662
- return null;
5663
- }
5664
- const pic = createPic();
5665
- if (!pic) return null;
5666
- const fp = extractMediaFingerprint(pic);
5667
- if (fp) {
5668
- if (seenMediaSignatures.has(fp)) {
5669
- return null;
5670
- }
5671
- seenMediaSignatures.add(fp);
5672
- }
5673
- return pic;
5684
+ if (isLegacy === true) return null;
5685
+ return createPic();
5674
5686
  }
5675
5687
  });
5676
- const htmlElements = await doc.render();
5677
- const contentNodes = [];
5678
- const seenInContentBlocks = /* @__PURE__ */ new Set();
5679
- const extractBlocks = (nodes) => {
5680
- for (const item of nodes) {
5681
- if (!item || !(item instanceof HTMLElement)) continue;
5682
- const tag = item.tagName.toLowerCase();
5683
- const hasChildBlocks = Array.from(item.children).some((c) => {
5684
- const ct = c.tagName.toLowerCase();
5685
- return ["p", "div", "table", "h1", "h2", "h3", "h4", "h5", "h6", "section", "article", "ul", "ol"].includes(ct);
5686
- });
5687
- if (hasChildBlocks && !["table", "tr", "td", "th", "ul", "ol", "li"].includes(tag)) {
5688
- extractBlocks(Array.from(item.children));
5689
- } else {
5690
- const fp = extractMediaFingerprint(item);
5691
- if (fp) {
5692
- if (seenInContentBlocks.has(fp)) {
5693
- continue;
5694
- }
5695
- seenInContentBlocks.add(fp);
5696
- }
5697
- const hasMedia = item.querySelector("img, svg, canvas, table") !== null || ["img", "svg", "table"].includes(tag);
5698
- const textContent = (item.textContent || "").trim();
5699
- if (!hasMedia && textContent.length === 0) {
5700
- continue;
5701
- }
5702
- const svgs = item.querySelectorAll("svg, img");
5703
- svgs.forEach((s) => {
5704
- const el = s;
5705
- el.style.maxWidth = "100%";
5706
- el.style.maxHeight = "360px";
5707
- el.style.display = "block";
5708
- el.style.margin = "12px auto";
5709
- if (s.tagName.toLowerCase() === "svg") {
5710
- s.removeAttribute("width");
5711
- s.removeAttribute("height");
5712
- el.style.width = "100%";
5713
- el.style.maxHeight = "360px";
5714
- }
5715
- });
5716
- if (tag === "p" || tag === "div") {
5717
- item.style.display = "block";
5718
- item.style.marginBottom = "12px";
5719
- item.style.lineHeight = "1.6";
5720
- }
5721
- contentNodes.push(item);
5688
+ const elements = await doc.render();
5689
+ let unsupportedIndex = 1;
5690
+ const usedTables = /* @__PURE__ */ new Set();
5691
+ for (let i = 0; i < elements.length; i++) {
5692
+ const el = elements[i];
5693
+ const text = el.textContent || "";
5694
+ if (text.includes("[Unsupported image format]")) {
5695
+ if (extractedImages[unsupportedIndex]) {
5696
+ const img = document.createElement("img");
5697
+ img.src = extractedImages[unsupportedIndex].dataUrl;
5698
+ img.style.width = "100%";
5699
+ img.style.height = "auto";
5700
+ img.style.maxHeight = "430px";
5701
+ img.style.objectFit = "contain";
5702
+ img.style.display = "block";
5703
+ img.style.margin = "12px auto";
5704
+ el.innerHTML = "";
5705
+ el.appendChild(img);
5706
+ unsupportedIndex++;
5722
5707
  }
5723
5708
  }
5724
- };
5725
- extractBlocks(htmlElements);
5726
- tables.forEach((table) => {
5727
- const tableEl = renderRtfTableElement(table);
5728
- let inserted = false;
5729
- if (table.anchorBefore && table.anchorBefore.length > 10) {
5730
- const keyword = table.anchorBefore.slice(-35).trim();
5731
- for (let i = 0; i < contentNodes.length; i++) {
5732
- if (contentNodes[i].textContent?.includes(keyword)) {
5733
- contentNodes.splice(i + 1, 0, tableEl);
5734
- inserted = true;
5735
- break;
5709
+ if (tables.length > 0) {
5710
+ for (let t = 0; t < tables.length; t++) {
5711
+ if (usedTables.has(t)) continue;
5712
+ const tbl = tables[t];
5713
+ if (tbl.rows.length >= 2) {
5714
+ const sampleCell1 = tbl.rows[1]?.[1] || tbl.rows[0]?.[1] || "";
5715
+ const sampleCell2 = tbl.rows[2]?.[1] || tbl.rows[1]?.[0] || "";
5716
+ const match1 = sampleCell1 && text.includes(sampleCell1.slice(0, 15).trim());
5717
+ const match2 = sampleCell2 && text.includes(sampleCell2.slice(0, 15).trim());
5718
+ if (match1 && match2) {
5719
+ el.innerHTML = "";
5720
+ el.appendChild(renderRtfTableElement(tbl));
5721
+ usedTables.add(t);
5722
+ break;
5723
+ }
5736
5724
  }
5737
5725
  }
5738
5726
  }
5739
- if (!inserted && table.anchorAfter && table.anchorAfter.length > 10) {
5740
- const keyword = table.anchorAfter.slice(0, 35).trim();
5741
- for (let i = 0; i < contentNodes.length; i++) {
5742
- if (contentNodes[i].textContent?.includes(keyword)) {
5743
- contentNodes.splice(i, 0, tableEl);
5744
- inserted = true;
5745
- break;
5746
- }
5747
- }
5727
+ }
5728
+ elements.forEach((el) => {
5729
+ const svgs = el.querySelectorAll ? Array.from(el.querySelectorAll("svg")) : [];
5730
+ svgs.forEach((svg) => {
5731
+ svg.removeAttribute("width");
5732
+ svg.removeAttribute("height");
5733
+ svg.style.maxWidth = "100%";
5734
+ svg.style.maxHeight = "430px";
5735
+ svg.style.height = "auto";
5736
+ svg.style.display = "block";
5737
+ svg.style.margin = "12px auto";
5738
+ });
5739
+ const imgs = el.querySelectorAll ? Array.from(el.querySelectorAll("img")) : [];
5740
+ imgs.forEach((img) => {
5741
+ img.style.maxWidth = "100%";
5742
+ img.style.maxHeight = "430px";
5743
+ img.style.height = "auto";
5744
+ img.style.objectFit = "contain";
5745
+ img.style.display = "block";
5746
+ img.style.margin = "12px auto";
5747
+ });
5748
+ });
5749
+ const allImgs = Array.from(document.querySelectorAll("img")).concat(
5750
+ elements.flatMap((el) => Array.from(el.querySelectorAll ? el.querySelectorAll("img") : []))
5751
+ );
5752
+ await Promise.all(allImgs.map((img) => {
5753
+ if (img.complete && img.naturalHeight > 0) return Promise.resolve();
5754
+ if (img.decode) return img.decode().catch(() => {
5755
+ });
5756
+ return new Promise((r) => {
5757
+ img.onload = r;
5758
+ img.onerror = r;
5759
+ });
5760
+ }));
5761
+ elements.forEach((el) => {
5762
+ const text = el.textContent || "";
5763
+ if (text.includes("\uF0B7")) {
5764
+ const cleanText = text.replace(/^[\s\uf0b7\t]+/, "");
5765
+ el.style.display = "flex";
5766
+ el.style.alignItems = "flex-start";
5767
+ el.style.margin = "6px 0 6px 28px";
5768
+ el.style.fontSize = "11pt";
5769
+ el.style.lineHeight = "1.5";
5770
+ el.innerHTML = `<span style="margin-right:10px; user-select:none;">&#9633;</span><span>${cleanText}</span>`;
5748
5771
  }
5749
- if (!inserted) {
5750
- if (contentNodes.length > 2) {
5751
- contentNodes.splice(2, 0, tableEl);
5752
- } else {
5753
- contentNodes.push(tableEl);
5754
- }
5772
+ });
5773
+ elements.forEach((el) => {
5774
+ const text = (el.textContent || "").trim();
5775
+ const hasMedia = el.querySelector("img, svg, canvas, table") !== null || ["IMG", "SVG", "TABLE"].includes(el.tagName);
5776
+ if (isHeadingElement(el)) {
5777
+ el.style.marginTop = "20px";
5778
+ el.style.marginBottom = "10px";
5779
+ el.style.lineHeight = "1.25";
5780
+ } else if (text.length > 0 && !hasMedia) {
5781
+ el.style.marginBottom = "12px";
5782
+ el.style.lineHeight = "1.45";
5755
5783
  }
5756
5784
  });
5757
- const createRtfCard = (pageNum) => {
5785
+ const measureCard = document.createElement("div");
5786
+ measureCard.style.width = `${pageWidth}px`;
5787
+ measureCard.style.padding = `${padTop}px ${padRight}px ${padBottom}px ${padLeft}px`;
5788
+ measureCard.style.boxSizing = "border-box";
5789
+ measureCard.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
5790
+ measureCard.style.lineHeight = "1.45";
5791
+ measureCard.style.visibility = "hidden";
5792
+ measureCard.style.position = "absolute";
5793
+ measureCard.style.top = "-99999px";
5794
+ document.body.appendChild(measureCard);
5795
+ const createPageCard = (num) => {
5758
5796
  const card = document.createElement("div");
5759
5797
  card.className = "fp-rtf-page-card";
5760
- card.setAttribute("data-page-number", String(pageNum));
5798
+ card.setAttribute("data-page-number", String(num));
5761
5799
  card.style.backgroundColor = "#ffffff";
5762
5800
  card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08), 0 1px 3px rgba(0,0,0,0.04)";
5763
5801
  card.style.borderRadius = "4px";
5764
- card.style.padding = "64px 56px";
5765
- card.style.width = "816px";
5766
- card.style.minHeight = "1056px";
5767
- card.style.maxHeight = "1056px";
5768
- card.style.overflow = "hidden";
5802
+ card.style.width = `${pageWidth}px`;
5803
+ card.style.height = `${pageHeight}px`;
5804
+ card.style.padding = `${padTop}px ${padRight}px ${padBottom}px ${padLeft}px`;
5769
5805
  card.style.boxSizing = "border-box";
5806
+ card.style.overflow = "hidden";
5770
5807
  card.style.marginBottom = "24px";
5771
- card.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
5772
- card.style.lineHeight = "1.6";
5773
5808
  card.style.color = "#1e293b";
5809
+ card.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
5810
+ card.style.lineHeight = "1.45";
5774
5811
  card.style.position = "relative";
5812
+ card.style.textAlign = "left";
5775
5813
  return card;
5776
5814
  };
5777
- wrapper.innerHTML = "";
5778
- const measureCard = createRtfCard(0);
5779
- measureCard.style.maxHeight = "none";
5780
- measureCard.style.height = "auto";
5781
- wrapper.appendChild(measureCard);
5782
- contentNodes.forEach((node) => measureCard.appendChild(node));
5783
- const childHeights = contentNodes.map((c) => {
5784
- const rect = c.getBoundingClientRect ? c.getBoundingClientRect() : null;
5785
- const rectH = rect ? rect.height : 0;
5786
- const offH = c.offsetHeight || 0;
5787
- let realH = Math.max(rectH, offH);
5788
- try {
5789
- const cs = window.getComputedStyle(c);
5790
- realH += (parseFloat(cs.marginTop) || 0) + (parseFloat(cs.marginBottom) || 0);
5791
- } catch {
5792
- }
5793
- const hasMedia = c.querySelector("img, svg") !== null || ["img", "svg"].includes(c.tagName.toLowerCase());
5794
- const hasTable = c.querySelector("table") !== null || c.tagName.toLowerCase() === "table";
5795
- if (hasMedia) {
5796
- realH = Math.max(realH, 300);
5797
- } else if (hasTable) {
5798
- realH = Math.max(realH, 240);
5799
- }
5800
- if (realH > 0) return realH;
5801
- const textLen = c.textContent?.trim().length || 0;
5802
- return Math.max(28, Math.ceil(textLen / 75) * 26 + 16);
5803
- });
5804
- wrapper.innerHTML = "";
5805
- let curCard = createRtfCard(1);
5806
- wrapper.appendChild(curCard);
5807
- pageElements = [curCard];
5808
- let curH = 0;
5809
- const maxH = 860;
5810
- for (let i = 0; i < contentNodes.length; i++) {
5811
- const child = contentNodes[i];
5812
- const chH = childHeights[i];
5813
- const isChildEmpty = (child.textContent || "").trim().length === 0 && !child.querySelector("table, img, svg, canvas");
5814
- if (curH === 0 && isChildEmpty) {
5815
+ const pages = [];
5816
+ let curCard = createPageCard(1);
5817
+ pages.push(curCard);
5818
+ let curHeight = 0;
5819
+ for (let i = 0; i < elements.length; i++) {
5820
+ const el = elements[i];
5821
+ if (!el || !(el instanceof HTMLElement)) continue;
5822
+ const text = (el.textContent || "").trim();
5823
+ const hasMedia = el.querySelector("img, svg, canvas, table") !== null || ["IMG", "SVG", "TABLE"].includes(el.tagName);
5824
+ const isEmptyPar = !hasMedia && text.length === 0;
5825
+ if (isEmptyPar) {
5826
+ const emptyH = 35;
5827
+ if (curHeight + emptyH > printableHeight) {
5828
+ if (curCard.childNodes.length > 0) {
5829
+ curCard = createPageCard(pages.length + 1);
5830
+ pages.push(curCard);
5831
+ curHeight = 0;
5832
+ }
5833
+ }
5834
+ const spacer = document.createElement("div");
5835
+ spacer.style.height = `${emptyH}px`;
5836
+ curCard.appendChild(spacer);
5837
+ curHeight += emptyH;
5815
5838
  continue;
5816
5839
  }
5817
- const isTable = child.querySelector("table") !== null || child.tagName.toLowerCase() === "table";
5818
- const shouldBreakBeforeTable = isTable && curH > 400 && curCard.childNodes.length > 0;
5819
- if ((curH + chH > maxH || shouldBreakBeforeTable) && curCard.childNodes.length > 0) {
5820
- curCard = createRtfCard(pageElements.length + 1);
5821
- wrapper.appendChild(curCard);
5822
- pageElements.push(curCard);
5823
- curH = 0;
5840
+ measureCard.innerHTML = "";
5841
+ const clone = el.cloneNode(true);
5842
+ measureCard.appendChild(clone);
5843
+ const compStyle = window.getComputedStyle(clone);
5844
+ const mt = parseFloat(compStyle.marginTop) || 0;
5845
+ const mb = parseFloat(compStyle.marginBottom) || 0;
5846
+ const elH = clone.offsetHeight + mt + mb;
5847
+ const isHeading = isHeadingElement(el);
5848
+ let nextContentH = 0;
5849
+ if (isHeading || hasMedia) {
5850
+ for (let j = i + 1; j < elements.length; j++) {
5851
+ const nextEl = elements[j];
5852
+ if (nextEl && (nextEl.textContent || "").trim().length > 0) {
5853
+ measureCard.innerHTML = "";
5854
+ const nClone = nextEl.cloneNode(true);
5855
+ measureCard.appendChild(nClone);
5856
+ const nStyle = window.getComputedStyle(nClone);
5857
+ nextContentH = nClone.offsetHeight + (parseFloat(nStyle.marginTop) || 0) + (parseFloat(nStyle.marginBottom) || 0);
5858
+ break;
5859
+ }
5860
+ }
5824
5861
  }
5825
- curCard.appendChild(child);
5826
- curH += chH;
5827
- }
5828
- pageElements = pageElements.filter((card) => {
5862
+ const remainingSpace = printableHeight - curHeight;
5863
+ const isLargeMedia = hasMedia && elH > 300;
5864
+ const keepWithNextOverflow = isHeading && nextContentH > 0 && elH + nextContentH + 20 > remainingSpace;
5865
+ const largeMediaOverflow = isLargeMedia && curHeight > 0 && elH + 80 > remainingSpace;
5866
+ const shouldBreak = (curHeight + elH > printableHeight || keepWithNextOverflow || largeMediaOverflow) && curCard.childNodes.length > 0;
5867
+ if (shouldBreak) {
5868
+ curCard = createPageCard(pages.length + 1);
5869
+ pages.push(curCard);
5870
+ curHeight = 0;
5871
+ }
5872
+ curCard.appendChild(el);
5873
+ curHeight += elH;
5874
+ }
5875
+ measureCard.remove();
5876
+ pageElements = pages.filter((card) => {
5829
5877
  const hasText = (card.textContent || "").trim().length > 0;
5830
- const hasMedia = card.querySelector("table, img, svg, canvas") !== null;
5831
- return hasText || hasMedia;
5878
+ const hasCardMedia = card.querySelector("table, img, svg, canvas") !== null;
5879
+ return hasText || hasCardMedia;
5832
5880
  });
5833
5881
  if (pageElements.length === 0) {
5834
5882
  pageElements = [curCard];
5835
5883
  }
5884
+ wrapper.innerHTML = "";
5885
+ pageElements.forEach((card, idx) => {
5886
+ card.setAttribute("data-page-number", String(idx + 1));
5887
+ card.style.display = idx === 0 ? "block" : "none";
5888
+ wrapper.appendChild(card);
5889
+ });
5836
5890
  } catch (err) {
5837
5891
  console.warn("[RtfPlugin] RTF render error, fallback text:", err);
5838
- const text = new TextDecoder("latin1").decode(ctx.buffer);
5839
- const tables = parseRtfTables(text);
5892
+ const text = rawRtf;
5840
5893
  const rawPages = text.split(/\\page\b/).map((segment) => {
5841
5894
  return segment.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim();
5842
- }).filter((p) => p.length > 0);
5895
+ }).filter((p2) => p2.length > 0);
5843
5896
  const pages = rawPages.length > 0 ? rawPages : [text.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim()];
5844
5897
  for (let i = 0; i < pages.length; i++) {
5845
5898
  const pageCard = document.createElement("div");
@@ -5847,9 +5900,9 @@ var RtfPlugin = class {
5847
5900
  pageCard.style.backgroundColor = "#ffffff";
5848
5901
  pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
5849
5902
  pageCard.style.borderRadius = "4px";
5850
- pageCard.style.padding = "64px 56px";
5851
- pageCard.style.width = "816px";
5852
- pageCard.style.minHeight = "1056px";
5903
+ pageCard.style.padding = `${padTop}px ${padRight}px ${padBottom}px ${padLeft}px`;
5904
+ pageCard.style.width = `${pageWidth}px`;
5905
+ pageCard.style.minHeight = `${pageHeight}px`;
5853
5906
  pageCard.style.boxSizing = "border-box";
5854
5907
  pageCard.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
5855
5908
  pageCard.style.fontSize = "12pt";
@@ -5869,11 +5922,9 @@ var RtfPlugin = class {
5869
5922
  let currentPage = 1;
5870
5923
  const showPage = (pageNum) => {
5871
5924
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
5872
- if (totalPages > 1) {
5873
- pageElements.forEach((el, idx) => {
5874
- el.style.display = idx + 1 === currentPage ? "block" : "none";
5875
- });
5876
- }
5925
+ pageElements.forEach((el, idx) => {
5926
+ el.style.display = idx + 1 === currentPage ? "block" : "none";
5927
+ });
5877
5928
  ctx.container.scrollTop = 0;
5878
5929
  ctx.emit("page-change", { page: currentPage, total: totalPages });
5879
5930
  };