docgen-utils 1.0.19 → 1.0.21
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/README.md +65 -18
- package/dist/bundle.js +27905 -26887
- package/dist/bundle.min.js +253 -263
- package/dist/cli.js +3696 -2584
- package/dist/packages/cli/commands/export-docs.d.ts.map +1 -1
- package/dist/packages/cli/commands/export-docs.js +161 -12
- package/dist/packages/cli/commands/export-docs.js.map +1 -1
- package/dist/packages/cli/commands/export-slides.d.ts.map +1 -1
- package/dist/packages/cli/commands/export-slides.js +11 -7
- package/dist/packages/cli/commands/export-slides.js.map +1 -1
- package/dist/packages/cli/index.js.map +1 -1
- package/dist/packages/docs/common.d.ts +2 -0
- package/dist/packages/docs/common.d.ts.map +1 -1
- package/dist/packages/docs/convert.d.ts.map +1 -1
- package/dist/packages/docs/convert.js +6 -15
- package/dist/packages/docs/convert.js.map +1 -1
- package/dist/packages/docs/create-document.d.ts.map +1 -1
- package/dist/packages/docs/create-document.js +8 -2
- package/dist/packages/docs/create-document.js.map +1 -1
- package/dist/packages/docs/import-docx.d.ts.map +1 -1
- package/dist/packages/docs/import-docx.js +170 -83
- package/dist/packages/docs/import-docx.js.map +1 -1
- package/dist/packages/docs/parse-colors.d.ts +0 -5
- package/dist/packages/docs/parse-colors.d.ts.map +1 -1
- package/dist/packages/docs/parse-colors.js +2 -2
- package/dist/packages/docs/parse-colors.js.map +1 -1
- package/dist/packages/docs/parse-css.d.ts +0 -9
- package/dist/packages/docs/parse-css.d.ts.map +1 -1
- package/dist/packages/docs/parse-css.js +4 -6
- package/dist/packages/docs/parse-css.js.map +1 -1
- package/dist/packages/docs/parse-helpers.d.ts +0 -1
- package/dist/packages/docs/parse-helpers.d.ts.map +1 -1
- package/dist/packages/docs/parse-helpers.js +1 -1
- package/dist/packages/docs/parse-helpers.js.map +1 -1
- package/dist/packages/docs/parse-inline.d.ts +0 -13
- package/dist/packages/docs/parse-inline.d.ts.map +1 -1
- package/dist/packages/docs/parse-inline.js +7 -7
- package/dist/packages/docs/parse-inline.js.map +1 -1
- package/dist/packages/docs/parse-layout.d.ts.map +1 -1
- package/dist/packages/docs/parse-layout.js +1 -14
- package/dist/packages/docs/parse-layout.js.map +1 -1
- package/dist/packages/docs/parse-special.js +1 -1
- package/dist/packages/docs/parse-special.js.map +1 -1
- package/dist/packages/docs/parse.d.ts.map +1 -1
- package/dist/packages/docs/parse.js +120 -130
- package/dist/packages/docs/parse.js.map +1 -1
- package/dist/packages/shared/zip-guard.d.ts +37 -0
- package/dist/packages/shared/zip-guard.d.ts.map +1 -0
- package/dist/packages/shared/zip-guard.js +101 -0
- package/dist/packages/shared/zip-guard.js.map +1 -0
- package/dist/packages/slides/convert.d.ts +1 -3
- package/dist/packages/slides/convert.d.ts.map +1 -1
- package/dist/packages/slides/convert.js +8 -74
- package/dist/packages/slides/convert.js.map +1 -1
- package/dist/packages/slides/createPresentation.d.ts +1 -1
- package/dist/packages/slides/createPresentation.d.ts.map +1 -1
- package/dist/packages/slides/createPresentation.js +1 -10
- package/dist/packages/slides/createPresentation.js.map +1 -1
- package/dist/packages/slides/import-pptx.d.ts.map +1 -1
- package/dist/packages/slides/import-pptx.js +78 -9
- package/dist/packages/slides/import-pptx.js.map +1 -1
- package/dist/packages/slides/parse.d.ts +0 -22
- package/dist/packages/slides/parse.d.ts.map +1 -1
- package/dist/packages/slides/parse.js +106 -44
- package/dist/packages/slides/parse.js.map +1 -1
- package/dist/packages/slides/transform.d.ts.map +1 -1
- package/dist/packages/slides/transform.js +1 -5
- package/dist/packages/slides/transform.js.map +1 -1
- package/dist/packages/slides/vendor/VENDORING.md +2 -2
- package/package.json +15 -8
- package/dist/packages/cli/commands/common.d.ts +0 -2
- package/dist/packages/cli/commands/common.d.ts.map +0 -1
- package/dist/packages/cli/commands/common.js +0 -22
- package/dist/packages/cli/commands/common.js.map +0 -1
|
@@ -19,21 +19,21 @@ const PX_PER_IN = 96;
|
|
|
19
19
|
// ---------------------------------------------------------------------------
|
|
20
20
|
const SINGLE_WEIGHT_FONTS = ['impact'];
|
|
21
21
|
// ---------------------------------------------------------------------------
|
|
22
|
-
//
|
|
22
|
+
// Internal helper functions
|
|
23
23
|
// ---------------------------------------------------------------------------
|
|
24
24
|
/** Convert pixel value to inches. */
|
|
25
|
-
|
|
25
|
+
function pxToInch(px) {
|
|
26
26
|
return px / PX_PER_IN;
|
|
27
27
|
}
|
|
28
28
|
/** Convert a CSS pixel string (e.g. "16px") to points. */
|
|
29
|
-
|
|
29
|
+
function pxToPoints(pxStr) {
|
|
30
30
|
return parseFloat(pxStr) * PT_PER_PX;
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
33
|
* Convert an `rgb()` / `rgba()` color string to a 6-char hex string.
|
|
34
34
|
* Returns `'FFFFFF'` for transparent or unparseable values.
|
|
35
35
|
*/
|
|
36
|
-
|
|
36
|
+
function rgbToHex(rgbStr) {
|
|
37
37
|
// Remove !important suffix if present
|
|
38
38
|
rgbStr = rgbStr.replace(/\s*!important\s*$/i, '').trim();
|
|
39
39
|
if (rgbStr === 'rgba(0, 0, 0, 0)' || rgbStr === 'transparent')
|
|
@@ -64,7 +64,7 @@ function isFullyTransparent(colorStr) {
|
|
|
64
64
|
* Extract transparency percentage from an `rgba()` string.
|
|
65
65
|
* Returns `null` for opaque or non-rgba values, otherwise 0-100.
|
|
66
66
|
*/
|
|
67
|
-
|
|
67
|
+
function extractAlpha(rgbStr) {
|
|
68
68
|
const match = rgbStr.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)/);
|
|
69
69
|
if (!match || !match[4])
|
|
70
70
|
return null;
|
|
@@ -354,7 +354,7 @@ function renderRepeatingGradientPatternAsImage(bgImage, bgSize, bgPosition, widt
|
|
|
354
354
|
}
|
|
355
355
|
}
|
|
356
356
|
// ---------------------------------------------------------------------------
|
|
357
|
-
// parseCssGradient (
|
|
357
|
+
// parseCssGradient (internal, used within this module)
|
|
358
358
|
// ---------------------------------------------------------------------------
|
|
359
359
|
/**
|
|
360
360
|
* Parse a CSS gradient string (`linear-gradient(...)` or `radial-gradient(...)`)
|
|
@@ -362,7 +362,7 @@ function renderRepeatingGradientPatternAsImage(bgImage, bgSize, bgPosition, widt
|
|
|
362
362
|
*
|
|
363
363
|
* Returns `null` if the string does not contain a recognized gradient.
|
|
364
364
|
*/
|
|
365
|
-
|
|
365
|
+
function parseCssGradient(gradientStr) {
|
|
366
366
|
const colorToHex = (colorStr) => {
|
|
367
367
|
colorStr = colorStr.trim().toLowerCase();
|
|
368
368
|
// Remove !important suffix if present
|
|
@@ -444,10 +444,30 @@ export function parseCssGradient(gradientStr) {
|
|
|
444
444
|
}
|
|
445
445
|
return null;
|
|
446
446
|
};
|
|
447
|
+
// Shared helper: parse color stops from a gradient parts array.
|
|
448
|
+
// Prevents divide-by-zero when there is only one color stop.
|
|
449
|
+
const parseColorStops = (colorStops) => {
|
|
450
|
+
return colorStops.map((stop, idx) => {
|
|
451
|
+
const posMatch = stop.match(/([\d.]+)%\s*$/);
|
|
452
|
+
const position = posMatch
|
|
453
|
+
? parseFloat(posMatch[1])
|
|
454
|
+
: (idx / Math.max(colorStops.length - 1, 1)) * 100;
|
|
455
|
+
const colorPart = posMatch
|
|
456
|
+
? stop.replace(/([\d.]+)%\s*$/, '').trim()
|
|
457
|
+
: stop.trim();
|
|
458
|
+
const stopData = { color: colorToHex(colorPart), position };
|
|
459
|
+
const transparency = extractTransparency(colorPart);
|
|
460
|
+
if (transparency !== null)
|
|
461
|
+
stopData.transparency = transparency;
|
|
462
|
+
return stopData;
|
|
463
|
+
});
|
|
464
|
+
};
|
|
447
465
|
// Try linear-gradient
|
|
448
466
|
const linearContent = extractGradientContent(gradientStr, 'linear-gradient(');
|
|
449
467
|
if (linearContent) {
|
|
450
468
|
const parts = splitGradientParts(linearContent);
|
|
469
|
+
if (parts.length === 0)
|
|
470
|
+
return null;
|
|
451
471
|
let cssAngle = 180;
|
|
452
472
|
let colorStops = parts;
|
|
453
473
|
const angleMatch = parts[0].match(/^([\d.]+)deg$/);
|
|
@@ -474,26 +494,15 @@ export function parseCssGradient(gradientStr) {
|
|
|
474
494
|
cssAngle = dirMap[dir] ?? 180;
|
|
475
495
|
colorStops = parts.slice(1);
|
|
476
496
|
}
|
|
477
|
-
const stops = colorStops
|
|
478
|
-
const posMatch = stop.match(/([\d.]+)%\s*$/);
|
|
479
|
-
const position = posMatch
|
|
480
|
-
? parseFloat(posMatch[1])
|
|
481
|
-
: (idx / (colorStops.length - 1)) * 100;
|
|
482
|
-
const colorPart = posMatch
|
|
483
|
-
? stop.replace(/([\d.]+)%\s*$/, '').trim()
|
|
484
|
-
: stop.trim();
|
|
485
|
-
const stopData = { color: colorToHex(colorPart), position };
|
|
486
|
-
const transparency = extractTransparency(colorPart);
|
|
487
|
-
if (transparency !== null)
|
|
488
|
-
stopData.transparency = transparency;
|
|
489
|
-
return stopData;
|
|
490
|
-
});
|
|
497
|
+
const stops = parseColorStops(colorStops);
|
|
491
498
|
return { type: 'linear', angle: cssAngle, stops };
|
|
492
499
|
}
|
|
493
500
|
// Try radial-gradient
|
|
494
501
|
const radialContent = extractGradientContent(gradientStr, 'radial-gradient(');
|
|
495
502
|
if (radialContent) {
|
|
496
503
|
const parts = splitGradientParts(radialContent);
|
|
504
|
+
if (parts.length === 0)
|
|
505
|
+
return null;
|
|
497
506
|
let centerX = 50;
|
|
498
507
|
let centerY = 50;
|
|
499
508
|
let colorStops = parts;
|
|
@@ -506,20 +515,7 @@ export function parseCssGradient(gradientStr) {
|
|
|
506
515
|
else if (parts[0].includes('circle') || parts[0].includes('ellipse')) {
|
|
507
516
|
colorStops = parts.slice(1);
|
|
508
517
|
}
|
|
509
|
-
const stops = colorStops
|
|
510
|
-
const posMatch2 = stop.match(/([\d.]+)%\s*$/);
|
|
511
|
-
const position = posMatch2
|
|
512
|
-
? parseFloat(posMatch2[1])
|
|
513
|
-
: (idx / (colorStops.length - 1)) * 100;
|
|
514
|
-
const colorPart = posMatch2
|
|
515
|
-
? stop.replace(/([\d.]+)%\s*$/, '').trim()
|
|
516
|
-
: stop.trim();
|
|
517
|
-
const stopData = { color: colorToHex(colorPart), position };
|
|
518
|
-
const transparency = extractTransparency(colorPart);
|
|
519
|
-
if (transparency !== null)
|
|
520
|
-
stopData.transparency = transparency;
|
|
521
|
-
return stopData;
|
|
522
|
-
});
|
|
518
|
+
const stops = parseColorStops(colorStops);
|
|
523
519
|
return { type: 'radial', centerX, centerY, stops };
|
|
524
520
|
}
|
|
525
521
|
return null;
|
|
@@ -1599,8 +1595,18 @@ function extractPseudoElements(el, win) {
|
|
|
1599
1595
|
// the element's content area.
|
|
1600
1596
|
}
|
|
1601
1597
|
// Convert to absolute page coordinates
|
|
1602
|
-
|
|
1603
|
-
|
|
1598
|
+
// For absolutely positioned pseudo-elements, CSS left/top values are relative
|
|
1599
|
+
// to the padding box (inside the border), but getBoundingClientRect() returns
|
|
1600
|
+
// the border box. We need to add the parent's border widths to get correct positioning.
|
|
1601
|
+
const parentComputedForBorder = win.getComputedStyle(el);
|
|
1602
|
+
let borderOffsetLeft = 0;
|
|
1603
|
+
let borderOffsetTop = 0;
|
|
1604
|
+
if (pseudoPos === 'absolute' || pseudoPos === 'fixed') {
|
|
1605
|
+
borderOffsetLeft = parseFloat(parentComputedForBorder.borderLeftWidth) || 0;
|
|
1606
|
+
borderOffsetTop = parseFloat(parentComputedForBorder.borderTopWidth) || 0;
|
|
1607
|
+
}
|
|
1608
|
+
const absLeft = parentRect.left + pLeft + borderOffsetLeft;
|
|
1609
|
+
const absTop = parentRect.top + pTop + borderOffsetTop;
|
|
1604
1610
|
// Check for visual content: background color, gradient, or border
|
|
1605
1611
|
const hasBg = pComputed.backgroundColor && pComputed.backgroundColor !== 'rgba(0, 0, 0, 0)';
|
|
1606
1612
|
const bgImage = pComputed.backgroundImage;
|
|
@@ -1611,7 +1617,7 @@ function extractPseudoElements(el, win) {
|
|
|
1611
1617
|
continue;
|
|
1612
1618
|
// Parse gradient(s) — a single background-image property may contain multiple gradients
|
|
1613
1619
|
let gradient = null;
|
|
1614
|
-
|
|
1620
|
+
const extraPseudoGradients = [];
|
|
1615
1621
|
if (hasGradient) {
|
|
1616
1622
|
const gradParts = splitCssBackgroundGradients(bgImage);
|
|
1617
1623
|
if (gradParts.length > 0)
|
|
@@ -2597,7 +2603,7 @@ export function parseSlideHtml(doc) {
|
|
|
2597
2603
|
}
|
|
2598
2604
|
const hasBg = computed.backgroundColor && computed.backgroundColor !== 'rgba(0, 0, 0, 0)';
|
|
2599
2605
|
// Check for clip-path: polygon() — store for custom geometry
|
|
2600
|
-
const clipPathValue = computed.clipPath || computed.webkitClipPath;
|
|
2606
|
+
const clipPathValue = computed.clipPath || computed.webkitClipPath || '';
|
|
2601
2607
|
const clipPathPolygon = parseClipPathPolygon(clipPathValue);
|
|
2602
2608
|
// Check for background images or gradients
|
|
2603
2609
|
const elBgImage = computed.backgroundImage;
|
|
@@ -2605,7 +2611,7 @@ export function parseSlideHtml(doc) {
|
|
|
2605
2611
|
let bgImageSize = null;
|
|
2606
2612
|
let bgImagePosition = null;
|
|
2607
2613
|
let bgGradient = null;
|
|
2608
|
-
|
|
2614
|
+
const extraDivGradients = [];
|
|
2609
2615
|
if (elBgImage && elBgImage !== 'none') {
|
|
2610
2616
|
if (elBgImage.includes('linear-gradient') ||
|
|
2611
2617
|
elBgImage.includes('radial-gradient')) {
|
|
@@ -2848,7 +2854,7 @@ export function parseSlideHtml(doc) {
|
|
|
2848
2854
|
const alignItems = computed.alignItems;
|
|
2849
2855
|
const justifyContent = computed.justifyContent;
|
|
2850
2856
|
let valign = 'top';
|
|
2851
|
-
let align
|
|
2857
|
+
let align;
|
|
2852
2858
|
if (isFlexContainer) {
|
|
2853
2859
|
const flexDirection = computed.flexDirection || 'row';
|
|
2854
2860
|
if (flexDirection === 'row' || flexDirection === 'row-reverse') {
|
|
@@ -3583,11 +3589,67 @@ export function parseSlideHtml(doc) {
|
|
|
3583
3589
|
// and process each child element (SVGs, spans, text nodes) individually
|
|
3584
3590
|
return;
|
|
3585
3591
|
}
|
|
3592
|
+
// For lists with custom bullets via ::before pseudo-elements (list-style: none
|
|
3593
|
+
// with padding-left on li), process each li as its own text element.
|
|
3594
|
+
// This ensures each li's ::before renders at the correct position relative
|
|
3595
|
+
// to its own li bounding box, avoiding text/bullet overlap.
|
|
3596
|
+
const hasCustomPseudoBullets = !hasNativeBullets && liElements.some((li) => {
|
|
3597
|
+
const liComputed = win.getComputedStyle(li);
|
|
3598
|
+
const liPadding = parseFloat(liComputed.paddingLeft) || 0;
|
|
3599
|
+
// Check if li has position:relative (needed for absolute ::before positioning)
|
|
3600
|
+
// and has padding-left (space for the bullet)
|
|
3601
|
+
return liComputed.position === 'relative' && liPadding > 0;
|
|
3602
|
+
});
|
|
3603
|
+
if (hasCustomPseudoBullets) {
|
|
3604
|
+
// Process each li as a separate text element
|
|
3605
|
+
// The ::before pseudo-elements will be extracted in the second pass
|
|
3606
|
+
liElements.forEach((li) => {
|
|
3607
|
+
const liEl = li;
|
|
3608
|
+
const liRect = liEl.getBoundingClientRect();
|
|
3609
|
+
if (liRect.width === 0 || liRect.height === 0)
|
|
3610
|
+
return;
|
|
3611
|
+
const liComputed = win.getComputedStyle(liEl);
|
|
3612
|
+
const liPaddingLeft = parseFloat(liComputed.paddingLeft) || 0;
|
|
3613
|
+
// Get the text content, offset by padding-left
|
|
3614
|
+
const textLeft = liRect.left + liPaddingLeft;
|
|
3615
|
+
const textWidth = liRect.width - liPaddingLeft;
|
|
3616
|
+
const runs = parseInlineFormatting(liEl, { breakLine: false }, [], (x) => x, win);
|
|
3617
|
+
if (runs.length === 0)
|
|
3618
|
+
return;
|
|
3619
|
+
// Strip any leading bullet characters that might have been left over
|
|
3620
|
+
runs[0].text = runs[0].text.replace(/^[•\-*\u25AA\u25B8]\s*/, '');
|
|
3621
|
+
const textElement = {
|
|
3622
|
+
type: 'p',
|
|
3623
|
+
text: runs,
|
|
3624
|
+
position: {
|
|
3625
|
+
x: pxToInch(textLeft),
|
|
3626
|
+
y: pxToInch(liRect.top),
|
|
3627
|
+
w: pxToInch(textWidth),
|
|
3628
|
+
h: pxToInch(liRect.height),
|
|
3629
|
+
},
|
|
3630
|
+
style: {
|
|
3631
|
+
fontSize: pxToPoints(liComputed.fontSize),
|
|
3632
|
+
fontFace: extractFontFace(liComputed.fontFamily),
|
|
3633
|
+
color: rgbToHex(liComputed.color),
|
|
3634
|
+
transparency: extractAlpha(liComputed.color),
|
|
3635
|
+
align: liComputed.textAlign === 'start'
|
|
3636
|
+
? 'left'
|
|
3637
|
+
: liComputed.textAlign,
|
|
3638
|
+
valign: 'top',
|
|
3639
|
+
lineSpacing: pxToPoints(liComputed.lineHeight),
|
|
3640
|
+
},
|
|
3641
|
+
};
|
|
3642
|
+
elements.push(textElement);
|
|
3643
|
+
processed.add(liEl);
|
|
3644
|
+
});
|
|
3645
|
+
processed.add(el);
|
|
3646
|
+
return;
|
|
3647
|
+
}
|
|
3586
3648
|
liElements.forEach((li, idx) => {
|
|
3587
3649
|
const isLast = idx === liElements.length - 1;
|
|
3588
3650
|
const runs = parseInlineFormatting(li, { breakLine: false }, [], (x) => x, win);
|
|
3589
3651
|
if (runs.length > 0) {
|
|
3590
|
-
runs[0].text = runs[0].text.replace(/^[
|
|
3652
|
+
runs[0].text = runs[0].text.replace(/^[•\-*\u25AA\u25B8]\s*/, '');
|
|
3591
3653
|
if (hasNativeBullets) {
|
|
3592
3654
|
runs[0].options.bullet = { indent: textIndent };
|
|
3593
3655
|
}
|
|
@@ -3720,7 +3782,7 @@ export function parseSlideHtml(doc) {
|
|
|
3720
3782
|
}
|
|
3721
3783
|
}
|
|
3722
3784
|
if (el.tagName !== 'LI' &&
|
|
3723
|
-
/^[
|
|
3785
|
+
/^[•\-*\u25AA\u25B8\u25CB\u25CF\u25C6\u25C7\u25A0\u25A1]\s/.test(text.trimStart())) {
|
|
3724
3786
|
errors.push(`Text element <${el.tagName.toLowerCase()}> starts with bullet symbol "${text.substring(0, 20)}...". ` +
|
|
3725
3787
|
'Use <ul> or <ol> lists instead of manual bullet symbols.');
|
|
3726
3788
|
return;
|
|
@@ -3730,7 +3792,7 @@ export function parseSlideHtml(doc) {
|
|
|
3730
3792
|
const fontSizePx = parseFloat(computed.fontSize);
|
|
3731
3793
|
const lineHeightPx = parseFloat(computed.lineHeight);
|
|
3732
3794
|
const lineHeightMultiplier = fontSizePx > 0 && !isNaN(lineHeightPx) ? lineHeightPx / fontSizePx : 1.0;
|
|
3733
|
-
|
|
3795
|
+
const textAlign = computed.textAlign === 'start' ? 'left' : computed.textAlign;
|
|
3734
3796
|
let valign = null;
|
|
3735
3797
|
const checkFlexParent = (parent) => {
|
|
3736
3798
|
if (!parent)
|