docgen-utils 1.0.9 → 1.0.10

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/cli.js CHANGED
@@ -22587,6 +22587,108 @@ var import_jszip2 = __toESM(require_lib4(), 1);
22587
22587
  var TARGET_WIDTH = 1280;
22588
22588
  var TARGET_HEIGHT = 720;
22589
22589
  var EMU_PER_PX2 = 914400 / 96;
22590
+ var FONT_FALLBACK_MAP = {
22591
+ // Microsoft Office fonts → metrically compatible open-source alternatives
22592
+ "Calibri": "'Calibri','Carlito','Helvetica Neue',Helvetica,Arial,sans-serif",
22593
+ "Calibri Light": "'Calibri Light','Carlito','Helvetica Neue Light','Helvetica Neue',Arial,sans-serif",
22594
+ "Cambria": "'Cambria','Caladea','Times New Roman',Georgia,serif",
22595
+ "Cambria Math": "'Cambria Math','Caladea','Times New Roman',serif",
22596
+ "Consolas": "'Consolas','Courier New',monospace",
22597
+ "Aptos": "'Aptos','Carlito','Helvetica Neue',Arial,sans-serif",
22598
+ "Times New Roman": "'Times New Roman','Liberation Serif',Georgia,serif",
22599
+ // Handwriting / decorative fonts
22600
+ "MV Boli": "'MV Boli','Comic Sans MS','Marker Felt',cursive",
22601
+ "Kristen ITC": "'Kristen ITC','Comic Sans MS','Marker Felt',cursive",
22602
+ "Stylus BT": "'Stylus BT','Brush Script MT','Snell Roundhand',cursive",
22603
+ // Japanese sans-serif fonts → Noto Sans CJK JP (commonly installed)
22604
+ "MS Gothic": "'MS Gothic','Noto Sans CJK JP','Hiragino Kaku Gothic ProN','Yu Gothic',sans-serif",
22605
+ "MS PGothic": "'MS PGothic','Noto Sans CJK JP','Hiragino Kaku Gothic ProN','Yu Gothic',sans-serif",
22606
+ "\uFF2D\uFF33 \uFF30\u30B4\u30B7\u30C3\u30AF": "'\uFF2D\uFF33 \uFF30\u30B4\u30B7\u30C3\u30AF','Noto Sans CJK JP','Hiragino Kaku Gothic ProN','Yu Gothic',sans-serif",
22607
+ "\uFF2D\uFF33 \u30B4\u30B7\u30C3\u30AF": "'\uFF2D\uFF33 \u30B4\u30B7\u30C3\u30AF','Noto Sans CJK JP','Hiragino Kaku Gothic ProN','Yu Gothic',sans-serif",
22608
+ "Kozuka Gothic Pro R": "'Kozuka Gothic Pro R','Noto Sans CJK JP','Hiragino Kaku Gothic ProN',sans-serif",
22609
+ "Kozuka Gothic Pro B": "'Kozuka Gothic Pro B','Noto Sans CJK JP Bold','Hiragino Kaku Gothic ProN',sans-serif",
22610
+ "Kozuka Gothic Pro M": "'Kozuka Gothic Pro M','Noto Sans CJK JP Medium','Hiragino Kaku Gothic ProN',sans-serif",
22611
+ "Kozuka Gothic Pro EL": "'Kozuka Gothic Pro EL','Noto Sans CJK JP Light','Hiragino Kaku Gothic ProN',sans-serif",
22612
+ "HGSKyokashotai": "'HGSKyokashotai','Noto Sans CJK JP','Hiragino Kaku Gothic ProN',sans-serif",
22613
+ // Japanese serif fonts → Noto Serif CJK JP
22614
+ "MS Mincho": "'MS Mincho','Noto Serif CJK JP','Hiragino Mincho ProN','Yu Mincho',serif",
22615
+ "MS PMincho": "'MS PMincho','Noto Serif CJK JP','Hiragino Mincho ProN','Yu Mincho',serif",
22616
+ // CJK fonts
22617
+ "\u5B8B\u4F53": "'\u5B8B\u4F53','Noto Serif CJK SC','Songti SC','STSong',serif",
22618
+ "\u65B0\u7D30\u660E\u9AD4": "'\u65B0\u7D30\u660E\u9AD4','Noto Serif CJK TC','Songti TC',serif",
22619
+ "\u9ED1\u4F53": "'\u9ED1\u4F53','Noto Sans CJK SC','Heiti SC','STHeiti',sans-serif",
22620
+ "\u5FAE\u8EDF\u6B63\u9ED1\u9AD4": "'\u5FAE\u8EDF\u6B63\u9ED1\u9AD4','Noto Sans CJK TC','Heiti TC',sans-serif",
22621
+ "\uB9D1\uC740 \uACE0\uB515": "'\uB9D1\uC740 \uACE0\uB515','Apple SD Gothic Neo',sans-serif",
22622
+ // UI / system fonts
22623
+ "Lucida Sans Unicode": "'Lucida Sans Unicode','Lucida Grande','Lucida Sans',sans-serif",
22624
+ "Segoe UI": "'Segoe UI','-apple-system','Helvetica Neue',sans-serif",
22625
+ "Segoe Sans Display": "'Segoe Sans Display','-apple-system','Helvetica Neue',Arial,sans-serif",
22626
+ "Segoe Sans Display Semibold": "'Segoe Sans Display Semibold','-apple-system','Helvetica Neue',Arial,sans-serif",
22627
+ // Google Fonts / web fonts commonly used in presentations
22628
+ "Inter": "'Inter','Helvetica Neue',Helvetica,Arial,sans-serif",
22629
+ "Inter Light": "'Inter Light','Inter','Helvetica Neue',Arial,sans-serif",
22630
+ "Inter ExtraBold": "'Inter ExtraBold','Inter','Helvetica Neue',Arial,sans-serif",
22631
+ "Space Grotesk": "'Space Grotesk','Helvetica Neue',Helvetica,Arial,sans-serif",
22632
+ "Montserrat": "'Montserrat','Helvetica Neue',Helvetica,Arial,sans-serif",
22633
+ "Open Sans": "'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif",
22634
+ "Lato": "'Lato','Helvetica Neue',Helvetica,Arial,sans-serif",
22635
+ "Oswald": "'Oswald','Helvetica Neue',Helvetica,Arial,sans-serif",
22636
+ "Archivo": "'Archivo','Helvetica Neue',Helvetica,Arial,sans-serif",
22637
+ "Economica": "'Economica','Helvetica Neue',Arial,sans-serif",
22638
+ "Libre Baskerville": "'Libre Baskerville','Georgia','Times New Roman',serif",
22639
+ // GitHub Copilot fonts
22640
+ "Ginto Copilot": "'Ginto Copilot','Helvetica Neue',Helvetica,Arial,sans-serif",
22641
+ "Ginto Copilot Light": "'Ginto Copilot Light','Helvetica Neue Light','Helvetica Neue',Arial,sans-serif",
22642
+ "Ginto Copilot 400": "'Ginto Copilot 400','Helvetica Neue',Helvetica,Arial,sans-serif",
22643
+ // Google Fonts / decorative (serif & display)
22644
+ "Playfair Display": "'Playfair Display','Georgia','Times New Roman',serif",
22645
+ "Playfair Display SemiBold": "'Playfair Display SemiBold','Playfair Display','Georgia',serif",
22646
+ "Caveat": "'Caveat','Comic Sans MS','Marker Felt',cursive",
22647
+ "Syncopate": "'Syncopate','Helvetica Neue',Helvetica,Arial,sans-serif",
22648
+ // Montserrat weight variants
22649
+ "Montserrat SemiBold": "'Montserrat SemiBold','Montserrat','Helvetica Neue',Arial,sans-serif",
22650
+ "Montserrat ExtraBold": "'Montserrat ExtraBold','Montserrat','Helvetica Neue',Arial,sans-serif",
22651
+ // GitHub Copilot font variants
22652
+ "Ginto Copilot Medium": "'Ginto Copilot Medium','Ginto Copilot','Helvetica Neue',Arial,sans-serif",
22653
+ "Ginto Copilot Black": "'Ginto Copilot Black','Ginto Copilot','Helvetica Neue',Arial,sans-serif",
22654
+ "Ginto Copilot Thin": "'Ginto Copilot Thin','Ginto Copilot Light','Helvetica Neue',Arial,sans-serif",
22655
+ // Microsoft UI fonts
22656
+ "Segoe Sans Small Regular": "'Segoe Sans Small Regular','Segoe UI','-apple-system','Helvetica Neue',sans-serif",
22657
+ "Segoe Sans Text Regular": "'Segoe Sans Text Regular','Segoe UI','-apple-system','Helvetica Neue',sans-serif",
22658
+ "Grandview": "'Grandview','Helvetica Neue',Arial,sans-serif",
22659
+ "Nirmala UI": "'Nirmala UI','Helvetica Neue',Arial,sans-serif",
22660
+ "Ebrima": "'Ebrima','Helvetica Neue',Arial,sans-serif",
22661
+ // Common system fonts with fallbacks
22662
+ "Arial": "Arial,'Helvetica Neue',Helvetica,sans-serif",
22663
+ "Arial Black": "'Arial Black','Helvetica Neue',Helvetica,Arial,sans-serif",
22664
+ "Georgia": "Georgia,'Times New Roman',serif",
22665
+ "Georgia Regular": "'Georgia Regular',Georgia,'Times New Roman',serif",
22666
+ "Courier New": "'Courier New',Courier,monospace",
22667
+ "Times": "Times,'Times New Roman',serif",
22668
+ // Symbol fonts (preserved as-is with system fallback)
22669
+ "Wingdings": "'Wingdings','Zapf Dingbats',sans-serif",
22670
+ "Wingdings 2": "'Wingdings 2','Zapf Dingbats',sans-serif",
22671
+ "Wingdings 3": "'Wingdings 3','Zapf Dingbats',sans-serif",
22672
+ // Common fallbacks
22673
+ "Tahoma": "'Tahoma','Verdana','Geneva',sans-serif",
22674
+ "Verdana": "'Verdana','Geneva',sans-serif",
22675
+ "Helvetica Neue Light": "'Helvetica Neue Light','Helvetica Neue',Helvetica,Arial,sans-serif"
22676
+ };
22677
+ function cssFontFamily(fontName) {
22678
+ const mapped = FONT_FALLBACK_MAP[fontName];
22679
+ if (mapped) return mapped;
22680
+ if (fontName.includes(",")) {
22681
+ return fontName.split(",").map((f) => {
22682
+ const trimmed = f.trim();
22683
+ const lower = trimmed.toLowerCase();
22684
+ if (lower === "sans-serif" || lower === "serif" || lower === "monospace" || lower === "cursive" || lower === "fantasy") {
22685
+ return lower;
22686
+ }
22687
+ return `'${trimmed}'`;
22688
+ }).join(",");
22689
+ }
22690
+ return `'${fontName}',sans-serif`;
22691
+ }
22590
22692
  function emuToPx2(emu) {
22591
22693
  return emu / EMU_PER_PX2;
22592
22694
  }
@@ -22660,6 +22762,20 @@ function extractGradientFill(parent, themeColors) {
22660
22762
  if (color) stops.push({ pos, color });
22661
22763
  }
22662
22764
  if (stops.length === 0) return void 0;
22765
+ const pathEl = findChild2(gradFill, "path");
22766
+ if (pathEl && pathEl.getAttribute("path") === "circle") {
22767
+ const fillToRect = findChild2(pathEl, "fillToRect");
22768
+ let centerX = 50;
22769
+ let centerY = 50;
22770
+ if (fillToRect) {
22771
+ const l = parseInt(fillToRect.getAttribute("l") ?? "50000", 10) / 1e3;
22772
+ const t = parseInt(fillToRect.getAttribute("t") ?? "50000", 10) / 1e3;
22773
+ centerX = l;
22774
+ centerY = t;
22775
+ }
22776
+ const stopStr2 = stops.map((s) => `${s.color} ${s.pos}%`).join(",");
22777
+ return `radial-gradient(ellipse at ${centerX}% ${centerY}%,${stopStr2})`;
22778
+ }
22663
22779
  const lin = findChild2(gradFill, "lin");
22664
22780
  const angAttr = lin?.getAttribute("ang");
22665
22781
  const cssDeg = angAttr ? ooxmlAngleToCss(parseInt(angAttr, 10)) : 180;
@@ -22685,11 +22801,68 @@ function extractRunProps(rPr, scale, themeColors) {
22685
22801
  const color = resolveColor2(solidFill, themeColors);
22686
22802
  if (color) result.color = color;
22687
22803
  }
22804
+ if (!result.color) {
22805
+ const gradFill = findChild2(rPr, "gradFill");
22806
+ if (gradFill) {
22807
+ const gsLst = findChild2(gradFill, "gsLst");
22808
+ if (gsLst) {
22809
+ const gsEls = findChildren2(gsLst, "gs");
22810
+ if (gsEls.length > 0) {
22811
+ const stops = [];
22812
+ for (const gs of gsEls) {
22813
+ const pos = parseInt(gs.getAttribute("pos") ?? "0", 10) / 1e3;
22814
+ const color = resolveColor2(gs, themeColors);
22815
+ if (color) stops.push({ pos, color });
22816
+ }
22817
+ if (stops.length >= 2) {
22818
+ const lin = findChild2(gradFill, "lin");
22819
+ const angAttr = lin?.getAttribute("ang");
22820
+ const cssDeg = angAttr ? ooxmlAngleToCss(parseInt(angAttr, 10)) : 135;
22821
+ const stopStr = stops.map((s) => `${s.color} ${s.pos}%`).join(",");
22822
+ result.gradientFill = `linear-gradient(${cssDeg}deg,${stopStr})`;
22823
+ }
22824
+ const firstColor = resolveColor2(gsEls[0], themeColors);
22825
+ if (firstColor) result.color = firstColor;
22826
+ }
22827
+ }
22828
+ }
22829
+ }
22688
22830
  const latin = findChild2(rPr, "latin");
22689
22831
  if (latin) {
22690
22832
  const typeface = latin.getAttribute("typeface");
22691
22833
  if (typeface) result.fontFamily = typeface;
22692
22834
  }
22835
+ const effectLst = findChild2(rPr, "effectLst");
22836
+ if (effectLst) {
22837
+ const glow = findChild2(effectLst, "glow");
22838
+ if (glow) {
22839
+ const radAttr = glow.getAttribute("rad");
22840
+ const radiusEmu = radAttr ? parseInt(radAttr, 10) : 0;
22841
+ const radiusPx = Math.round(emuToPx2(radiusEmu) * scale);
22842
+ const glowColor = resolveColor2(glow, themeColors);
22843
+ if (glowColor && radiusPx > 0) {
22844
+ const INVERSE_GLOW_SIZE_SCALE = 0.4;
22845
+ const INVERSE_GLOW_OPACITY_SCALE = 3.33;
22846
+ const cssRadius = Math.round(radiusPx * INVERSE_GLOW_SIZE_SCALE);
22847
+ const rgbaMatch = glowColor.match(/rgba?\((\d+),(\d+),(\d+)(?:,([0-9.]+))?\)/);
22848
+ if (rgbaMatch) {
22849
+ const r = rgbaMatch[1];
22850
+ const g = rgbaMatch[2];
22851
+ const b2 = rgbaMatch[3];
22852
+ const originalAlpha = rgbaMatch[4] ? parseFloat(rgbaMatch[4]) : 1;
22853
+ const scaledAlpha = Math.min(originalAlpha * INVERSE_GLOW_OPACITY_SCALE, 1);
22854
+ result.textShadow = `0 0 ${cssRadius}px rgba(${r},${g},${b2},${scaledAlpha.toFixed(2)})`;
22855
+ } else if (glowColor.startsWith("#")) {
22856
+ const r = parseInt(glowColor.slice(1, 3), 16);
22857
+ const g = parseInt(glowColor.slice(3, 5), 16);
22858
+ const b2 = parseInt(glowColor.slice(5, 7), 16);
22859
+ result.textShadow = `0 0 ${cssRadius}px rgba(${r},${g},${b2},1)`;
22860
+ } else {
22861
+ result.textShadow = `0 0 ${cssRadius}px ${glowColor}`;
22862
+ }
22863
+ }
22864
+ }
22865
+ }
22693
22866
  return result;
22694
22867
  }
22695
22868
  function parseShape(sp, scale, themeColors) {
@@ -22711,13 +22884,16 @@ function parseShape(sp, scale, themeColors) {
22711
22884
  paragraphs: []
22712
22885
  };
22713
22886
  const prstGeom = findChild2(spPr, "prstGeom");
22714
- if (prstGeom?.getAttribute("prst") === "roundRect") {
22887
+ const prstType = prstGeom?.getAttribute("prst");
22888
+ if (prstType === "roundRect" && prstGeom) {
22715
22889
  const avLst = findChild2(prstGeom, "avLst");
22716
22890
  const gd = avLst ? findChild2(avLst, "gd") : null;
22717
22891
  const adjVal = gd ? parseInt(gd.getAttribute("fmla")?.replace("val ", "") ?? "16667", 10) : 16667;
22718
22892
  const minDim = Math.min(wEmu, hEmu);
22719
22893
  const radiusEmu = minDim * Math.min(adjVal, 5e4) / 1e5;
22720
22894
  shape.borderRadius = Math.round(emuToPx2(radiusEmu) * scale);
22895
+ } else if (prstType === "ellipse") {
22896
+ shape.isEllipse = true;
22721
22897
  }
22722
22898
  const ln = findChild2(spPr, "ln");
22723
22899
  if (ln) {
@@ -22731,6 +22907,13 @@ function parseShape(sp, scale, themeColors) {
22731
22907
  if (lnFill) {
22732
22908
  shape.borderColor = resolveColor2(lnFill, themeColors);
22733
22909
  }
22910
+ const prstDash = findChild2(ln, "prstDash");
22911
+ if (prstDash) {
22912
+ const dashVal = prstDash.getAttribute("val");
22913
+ if (dashVal && dashVal !== "solid") {
22914
+ shape.borderDashType = dashVal;
22915
+ }
22916
+ }
22734
22917
  }
22735
22918
  }
22736
22919
  const txBody = findChild2(sp, "txBody");
@@ -22799,21 +22982,33 @@ function parseShape(sp, scale, themeColors) {
22799
22982
  para.bulletChar = buChar.getAttribute("char") ?? void 0;
22800
22983
  }
22801
22984
  }
22802
- const runs = findChildren2(p, "r");
22803
- for (const r of runs) {
22804
- const rPr = findChild2(r, "rPr");
22805
- const props = extractRunProps(rPr, scale, themeColors);
22806
- const tEls = findChildren2(r, "t");
22807
- const text = tEls.map((t) => t.textContent ?? "").join("");
22808
- if (text) {
22809
- para.runs.push({
22810
- text,
22811
- bold: props.bold ?? defaults?.bold,
22812
- italic: props.italic ?? defaults?.italic,
22813
- fontSize: props.fontSize ?? defaults?.fontSize,
22814
- color: props.color ?? defaults?.color,
22815
- fontFamily: props.fontFamily ?? defaults?.fontFamily
22816
- });
22985
+ for (const child of Array.from(p.childNodes)) {
22986
+ if (child.nodeType !== 1) continue;
22987
+ const el = child;
22988
+ const localName = el.localName || el.nodeName.split(":").pop();
22989
+ if (localName === "r") {
22990
+ const rPr = findChild2(el, "rPr");
22991
+ const props = extractRunProps(rPr, scale, themeColors);
22992
+ const tEls = findChildren2(el, "t");
22993
+ const text = tEls.map((t) => t.textContent ?? "").join("");
22994
+ if (text) {
22995
+ para.runs.push({
22996
+ text,
22997
+ bold: props.bold ?? defaults?.bold,
22998
+ italic: props.italic ?? defaults?.italic,
22999
+ fontSize: props.fontSize ?? defaults?.fontSize,
23000
+ color: props.color ?? defaults?.color,
23001
+ fontFamily: props.fontFamily ?? defaults?.fontFamily,
23002
+ textShadow: props.textShadow ?? defaults?.textShadow,
23003
+ gradientFill: props.gradientFill ?? defaults?.gradientFill
23004
+ });
23005
+ }
23006
+ } else if (localName === "br") {
23007
+ if (para.runs.length > 0) {
23008
+ para.runs[para.runs.length - 1].text += "\n";
23009
+ } else {
23010
+ para.runs.push({ text: "\n" });
23011
+ }
22817
23012
  }
22818
23013
  }
22819
23014
  if (para.runs.length > 0) {
@@ -22835,23 +23030,68 @@ function parsePicture(pic, scale, imageMap) {
22835
23030
  if (!blipFill) return null;
22836
23031
  const blip = findChild2(blipFill, "blip");
22837
23032
  if (!blip) return null;
22838
- const rEmbed = blip.getAttribute("r:embed") ?? blip.getAttributeNS(
22839
- "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
22840
- "embed"
22841
- );
23033
+ let rEmbed = null;
23034
+ const extLst = findChild2(blip, "extLst");
23035
+ if (extLst) {
23036
+ for (let i = 0; i < extLst.children.length; i++) {
23037
+ const ext2 = extLst.children[i];
23038
+ if (ext2.localName === "ext") {
23039
+ for (let j = 0; j < ext2.children.length; j++) {
23040
+ const child = ext2.children[j];
23041
+ if (child.localName === "svgBlip") {
23042
+ rEmbed = child.getAttribute("r:embed") ?? child.getAttributeNS(
23043
+ "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
23044
+ "embed"
23045
+ );
23046
+ break;
23047
+ }
23048
+ }
23049
+ if (rEmbed) break;
23050
+ }
23051
+ }
23052
+ }
23053
+ if (!rEmbed) {
23054
+ rEmbed = blip.getAttribute("r:embed") ?? blip.getAttributeNS(
23055
+ "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
23056
+ "embed"
23057
+ );
23058
+ }
22842
23059
  if (!rEmbed) return null;
22843
23060
  const dataUri = imageMap.get(rEmbed);
22844
23061
  if (!dataUri) return null;
22845
23062
  const nvPicPr = findChild2(pic, "nvPicPr");
22846
23063
  const cNvPr = nvPicPr ? findChild2(nvPicPr, "cNvPr") : null;
22847
23064
  const alt = cNvPr?.getAttribute("descr") ?? void 0;
23065
+ const srcRect = findChild2(blipFill, "srcRect");
23066
+ let hasCrop = false;
23067
+ if (srcRect) {
23068
+ const l = parseInt(srcRect.getAttribute("l") ?? "0", 10);
23069
+ const r = parseInt(srcRect.getAttribute("r") ?? "0", 10);
23070
+ const t = parseInt(srcRect.getAttribute("t") ?? "0", 10);
23071
+ const b = parseInt(srcRect.getAttribute("b") ?? "0", 10);
23072
+ hasCrop = l > 0 || r > 0 || t > 0 || b > 0;
23073
+ }
23074
+ let borderRadius;
23075
+ const prstGeom = findChild2(spPr, "prstGeom");
23076
+ if (prstGeom?.getAttribute("prst") === "roundRect") {
23077
+ const avLst = findChild2(prstGeom, "avLst");
23078
+ const gd = avLst ? findChild2(avLst, "gd") : null;
23079
+ const adjVal = gd ? parseInt(gd.getAttribute("fmla")?.replace("val ", "") ?? "16667", 10) : 16667;
23080
+ const wEmu = parseInt(ext.getAttribute("cx") ?? "0", 10);
23081
+ const hEmu = parseInt(ext.getAttribute("cy") ?? "0", 10);
23082
+ const minDim = Math.min(wEmu, hEmu);
23083
+ const radiusEmu = minDim * Math.min(adjVal, 5e4) / 1e5;
23084
+ borderRadius = Math.round(emuToPx2(radiusEmu) * scale);
23085
+ }
22848
23086
  return {
22849
23087
  x: Math.round(emuToPx2(parseInt(off.getAttribute("x") ?? "0", 10)) * scale),
22850
23088
  y: Math.round(emuToPx2(parseInt(off.getAttribute("y") ?? "0", 10)) * scale),
22851
23089
  w: Math.round(emuToPx2(parseInt(ext.getAttribute("cx") ?? "0", 10)) * scale),
22852
23090
  h: Math.round(emuToPx2(parseInt(ext.getAttribute("cy") ?? "0", 10)) * scale),
22853
23091
  dataUri,
22854
- alt
23092
+ alt,
23093
+ borderRadius,
23094
+ hasCrop
22855
23095
  };
22856
23096
  }
22857
23097
  function renderSlideHtml(elements, bgColor) {
@@ -22861,14 +23101,20 @@ function renderSlideHtml(elements, bgColor) {
22861
23101
  const elId = `el-${elementIndex++}`;
22862
23102
  if (el.kind === "image") {
22863
23103
  const img = el.data;
22864
- const styles2 = [
23104
+ const isFullbleed = img.x <= 10 && img.y <= 10 && img.w >= TARGET_WIDTH - 20 && img.h >= TARGET_HEIGHT - 20;
23105
+ const objectFit = isFullbleed || img.hasCrop ? "cover" : "contain";
23106
+ const stylesList = [
22865
23107
  "position:absolute",
22866
23108
  `left:${img.x}px`,
22867
23109
  `top:${img.y}px`,
22868
23110
  `width:${img.w}px`,
22869
23111
  `height:${img.h}px`,
22870
- "object-fit:contain"
22871
- ].join(";");
23112
+ `object-fit:${objectFit}`
23113
+ ];
23114
+ if (img.borderRadius && img.borderRadius > 0) {
23115
+ stylesList.push(`border-radius:${img.borderRadius}px`);
23116
+ }
23117
+ const styles2 = stylesList.join(";");
22872
23118
  const altAttr = img.alt ? ` alt="${img.alt.replace(/"/g, "&quot;")}"` : "";
22873
23119
  inner += `<img id="${elId}" data-elementType="image" src="${img.dataUri}"${altAttr} style="${styles2}" />
22874
23120
  `;
@@ -22888,9 +23134,25 @@ function renderSlideHtml(elements, bgColor) {
22888
23134
  }
22889
23135
  if (shape.borderRadius) {
22890
23136
  styles.push(`border-radius:${shape.borderRadius}px`);
23137
+ } else if (shape.isEllipse) {
23138
+ styles.push(`border-radius:50%`);
22891
23139
  }
22892
23140
  if (shape.borderWidth && shape.borderColor) {
22893
- styles.push(`border:${shape.borderWidth}px solid ${shape.borderColor}`);
23141
+ let borderStyle = "solid";
23142
+ if (shape.borderDashType) {
23143
+ switch (shape.borderDashType) {
23144
+ case "dash":
23145
+ case "lgDash":
23146
+ case "sysDash":
23147
+ borderStyle = "dashed";
23148
+ break;
23149
+ case "sysDot":
23150
+ case "dot":
23151
+ borderStyle = "dotted";
23152
+ break;
23153
+ }
23154
+ }
23155
+ styles.push(`border:${shape.borderWidth}px ${borderStyle} ${shape.borderColor}`);
22894
23156
  styles.push("box-sizing:border-box");
22895
23157
  }
22896
23158
  if (shape.padding) {
@@ -22930,11 +23192,19 @@ function renderSlideHtml(elements, bgColor) {
22930
23192
  for (const run of para.runs) {
22931
23193
  const rStyles = [];
22932
23194
  if (run.fontSize) rStyles.push(`font-size:${run.fontSize}px`);
22933
- if (run.color) rStyles.push(`color:${run.color}`);
23195
+ if (run.gradientFill) {
23196
+ rStyles.push(`background:${run.gradientFill}`);
23197
+ rStyles.push("-webkit-background-clip:text");
23198
+ rStyles.push("-webkit-text-fill-color:transparent");
23199
+ rStyles.push("background-clip:text");
23200
+ } else if (run.color) {
23201
+ rStyles.push(`color:${run.color}`);
23202
+ }
22934
23203
  if (run.bold) rStyles.push("font-weight:bold");
22935
23204
  if (run.italic) rStyles.push("font-style:italic");
22936
23205
  if (run.fontFamily)
22937
- rStyles.push(`font-family:'${run.fontFamily}',sans-serif`);
23206
+ rStyles.push(`font-family:${cssFontFamily(run.fontFamily)}`);
23207
+ if (run.textShadow) rStyles.push(`text-shadow:${run.textShadow}`);
22938
23208
  const escapedText = run.text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
22939
23209
  if (rStyles.length > 0) {
22940
23210
  runHtml += `<span style="${rStyles.join(";")}">${escapedText}</span>`;
@@ -23050,6 +23320,20 @@ async function importPptx(arrayBuffer) {
23050
23320
  }
23051
23321
  }
23052
23322
  }
23323
+ const embeddedFontNames = /* @__PURE__ */ new Set();
23324
+ const embeddedFontEls = presDoc.getElementsByTagName("p:embeddedFont");
23325
+ for (let i = 0; i < embeddedFontEls.length; i++) {
23326
+ const fontEl = embeddedFontEls[i].getElementsByTagName("p:font")[0];
23327
+ if (fontEl) {
23328
+ const typeface = fontEl.getAttribute("typeface");
23329
+ if (typeface) embeddedFontNames.add(typeface);
23330
+ }
23331
+ }
23332
+ let fontStyleBlock = "";
23333
+ if (embeddedFontNames.size > 0) {
23334
+ const fontFamilies = Array.from(embeddedFontNames).map((name) => name.replace(/ /g, "+") + ":ital,wght@0,400;0,700;1,400;1,700").join("&family=");
23335
+ fontStyleBlock = `<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=${fontFamilies}&display=swap">`;
23336
+ }
23053
23337
  const slides = [];
23054
23338
  for (const slideFile of slideFiles) {
23055
23339
  const slideXml = await zip.file(slideFile)?.async("text");
@@ -23107,6 +23391,9 @@ async function importPptx(arrayBuffer) {
23107
23391
  }
23108
23392
  slides.push(renderSlideHtml(elements, slideBg));
23109
23393
  }
23394
+ if (fontStyleBlock && slides.length > 0) {
23395
+ slides[0] = fontStyleBlock + slides[0];
23396
+ }
23110
23397
  return slides;
23111
23398
  }
23112
23399
 
@@ -46278,7 +46565,7 @@ function createTableRow(cells, isHeaderRow, columnCount, cellPadding, headerBack
46278
46565
  return new TableRow({
46279
46566
  tableHeader: isHeaderRow,
46280
46567
  children: cells.map(
46281
- (cell) => {
46568
+ (cell, cellIndex) => {
46282
46569
  const textRuns = typeof cell === "string" ? [new TextRun({
46283
46570
  text: cell,
46284
46571
  bold: isHeaderRow,
@@ -46318,6 +46605,10 @@ function createTableRow(cells, isHeaderRow, columnCount, cellPadding, headerBack
46318
46605
  fill: "F9FAFB"
46319
46606
  };
46320
46607
  }
46608
+ const isLastCell = cellIndex === cells.length - 1;
46609
+ const remainingColumns = columnCount - cells.length;
46610
+ const needsSpan = isLastCell && remainingColumns > 0;
46611
+ const cellColumnSpan = needsSpan ? remainingColumns + 1 : void 0;
46321
46612
  return new TableCell({
46322
46613
  children: [
46323
46614
  new Paragraph({
@@ -46328,6 +46619,7 @@ function createTableRow(cells, isHeaderRow, columnCount, cellPadding, headerBack
46328
46619
  size: 100 / columnCount,
46329
46620
  type: WidthType.PERCENTAGE
46330
46621
  },
46622
+ columnSpan: cellColumnSpan,
46331
46623
  shading,
46332
46624
  // Apply cell padding from CSS
46333
46625
  margins: cellPadding ? {
@@ -46471,11 +46763,12 @@ function convertElementToDocx(element) {
46471
46763
  }
46472
46764
  case "table": {
46473
46765
  const useHeaderStyling = element.noBorders ? false : element.hasHeader !== false;
46766
+ const maxColumnCount = Math.max(...element.rows.map((row) => row.length));
46474
46767
  const tableRows = element.rows.map(
46475
46768
  (row, rowIndex) => createTableRow(
46476
46769
  row,
46477
46770
  useHeaderStyling && rowIndex === 0,
46478
- row.length,
46771
+ maxColumnCount,
46479
46772
  element.cellPadding,
46480
46773
  element.headerBackgroundColor,
46481
46774
  element.headerTextColor,
@@ -1 +1 @@
1
- {"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../../../packages/docs/convert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsD,SAAS,EAAe,KAAK,EAA8G,MAAM,MAAM,CAAC;AACrN,OAAO,EAAwD,aAAa,EAA8D,MAAM,UAAU,CAAC;AAoqB3J;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,aAAa,GAAG,CAAC,SAAS,GAAG,KAAK,CAAC,EAAE,CA43BlF"}
1
+ {"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../../../packages/docs/convert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsD,SAAS,EAAe,KAAK,EAA8G,MAAM,MAAM,CAAC;AACrN,OAAO,EAAwD,aAAa,EAA8D,MAAM,UAAU,CAAC;AA+qB3J;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,aAAa,GAAG,CAAC,SAAS,GAAG,KAAK,CAAC,EAAE,CAk4BlF"}
@@ -471,7 +471,7 @@ function createTableRow(cells, isHeaderRow, columnCount, cellPadding, headerBack
471
471
  };
472
472
  return new TableRow({
473
473
  tableHeader: isHeaderRow,
474
- children: cells.map((cell) => {
474
+ children: cells.map((cell, cellIndex) => {
475
475
  // Build text runs with formatting preserved
476
476
  // Apply header text color if specified, otherwise use run's color or default
477
477
  const textRuns = typeof cell === "string"
@@ -519,6 +519,15 @@ function createTableRow(cells, isHeaderRow, columnCount, cellPadding, headerBack
519
519
  fill: "F9FAFB",
520
520
  };
521
521
  }
522
+ // When this row has fewer cells than the table's column count,
523
+ // the last cell must span the remaining grid columns via columnSpan.
524
+ // Without this, the OOXML is structurally invalid (cells don't cover
525
+ // all gridCol entries) and strict parsers like Azure Document
526
+ // Intelligence will reject the document.
527
+ const isLastCell = cellIndex === cells.length - 1;
528
+ const remainingColumns = columnCount - cells.length;
529
+ const needsSpan = isLastCell && remainingColumns > 0;
530
+ const cellColumnSpan = needsSpan ? remainingColumns + 1 : undefined;
522
531
  return new DocxTableCell({
523
532
  children: [
524
533
  new Paragraph({
@@ -529,6 +538,7 @@ function createTableRow(cells, isHeaderRow, columnCount, cellPadding, headerBack
529
538
  size: 100 / columnCount,
530
539
  type: WidthType.PERCENTAGE,
531
540
  },
541
+ columnSpan: cellColumnSpan,
532
542
  shading,
533
543
  // Apply cell padding from CSS
534
544
  margins: cellPadding ? {
@@ -710,7 +720,13 @@ export function convertElementToDocx(element) {
710
720
  // For noBorders tables (like flex column layouts), don't apply header styling
711
721
  // For regular tables (with borders), default to treating first row as header unless explicitly disabled
712
722
  const useHeaderStyling = element.noBorders ? false : (element.hasHeader !== false);
713
- const tableRows = element.rows.map((row, rowIndex) => createTableRow(row, useHeaderStyling && rowIndex === 0, row.length, element.cellPadding, element.headerBackgroundColor, element.headerTextColor,
723
+ // Compute the maximum column count across all rows so that rows with fewer
724
+ // cells can set columnSpan to fill the remaining grid columns. Without this,
725
+ // the docx library creates gridCol entries for the max column count but rows
726
+ // with fewer cells lack a gridSpan attribute, producing invalid OOXML that
727
+ // strict parsers like Azure Document Intelligence reject.
728
+ const maxColumnCount = Math.max(...element.rows.map(row => row.length));
729
+ const tableRows = element.rows.map((row, rowIndex) => createTableRow(row, useHeaderStyling && rowIndex === 0, maxColumnCount, element.cellPadding, element.headerBackgroundColor, element.headerTextColor,
714
730
  // Apply even row background color for alternating row styling (rowIndex > 0 and odd = even rows in 0-indexed)
715
731
  rowIndex > 0 && rowIndex % 2 === 0 ? element.evenRowBackgroundColor : undefined));
716
732
  // Default table border color (light gray), or no borders