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