pptx-angular-viewer 1.31.2 → 1.31.3
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/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,8 @@ All notable changes to this project are documented here.
|
|
|
4
4
|
This file is generated from [Conventional Commits](https://www.conventionalcommits.org)
|
|
5
5
|
by [git-cliff](https://git-cliff.org); do not edit it by hand.
|
|
6
6
|
|
|
7
|
+
## [1.31.2](https://github.com/ChristopherVR/pptx-viewer/releases/tag/pptx-angular-viewer@1.31.2) - 2026-07-19
|
|
8
|
+
|
|
7
9
|
## [1.31.1](https://github.com/ChristopherVR/pptx-viewer/releases/tag/pptx-angular-viewer@1.31.1) - 2026-07-18
|
|
8
10
|
|
|
9
11
|
## [1.31.0](https://github.com/ChristopherVR/pptx-viewer/releases/tag/pptx-angular-viewer@1.31.0) - 2026-07-18
|
|
@@ -450,22 +450,96 @@ function getResolvedShapeClipPathFor(shapeType, width, height, adjustments) {
|
|
|
450
450
|
// 4. Final fallback: core's static preset clip-path table.
|
|
451
451
|
return getShapeClipPath(shapeType);
|
|
452
452
|
}
|
|
453
|
+
/**
|
|
454
|
+
* Build a CSS `clip-path: path('…')` value from a custom-geometry (`a:custGeom`)
|
|
455
|
+
* SVG path string, rescaling its path-space coordinates (`pathWidth` x
|
|
456
|
+
* `pathHeight`) into the element's pixel box.
|
|
457
|
+
*
|
|
458
|
+
* CSS `path()` coordinates live in the element's own border-box pixel space
|
|
459
|
+
* (there is no viewBox), so freeform coordinates authored against the OOXML path
|
|
460
|
+
* extent must be scaled by `elemW / pathWidth` and `elemH / pathHeight`. Every
|
|
461
|
+
* binding already clips its shape container's background fill with the resolved
|
|
462
|
+
* clip-path, so returning one here lets a themed freeform (e.g. the "Balloons"
|
|
463
|
+
* background) render as its true outline instead of flooding its bounding box.
|
|
464
|
+
*
|
|
465
|
+
* Supports the absolute command set produced by the core geometry engine
|
|
466
|
+
* (M/L/C/Q/Z) plus elliptical arcs (A); unknown commands are passed through
|
|
467
|
+
* unscaled rather than dropped.
|
|
468
|
+
*/
|
|
469
|
+
function buildCustomGeometryClipPath(pathData, pathWidth, pathHeight, elemWidth, elemHeight) {
|
|
470
|
+
if (!pathData ||
|
|
471
|
+
!Number.isFinite(pathWidth) ||
|
|
472
|
+
!Number.isFinite(pathHeight) ||
|
|
473
|
+
pathWidth <= 0 ||
|
|
474
|
+
pathHeight <= 0 ||
|
|
475
|
+
!Number.isFinite(elemWidth) ||
|
|
476
|
+
!Number.isFinite(elemHeight) ||
|
|
477
|
+
elemWidth <= 0 ||
|
|
478
|
+
elemHeight <= 0) {
|
|
479
|
+
return undefined;
|
|
480
|
+
}
|
|
481
|
+
const sx = elemWidth / pathWidth;
|
|
482
|
+
const sy = elemHeight / pathHeight;
|
|
483
|
+
const round = (n) => {
|
|
484
|
+
const r = Math.round(n * 100) / 100;
|
|
485
|
+
return Object.is(r, -0) ? '0' : String(r);
|
|
486
|
+
};
|
|
487
|
+
const tokens = pathData.match(/[MLCQZAHVmlcqzahv][^MLCQZAHVmlcqzahv]*/g) ?? [];
|
|
488
|
+
const out = [];
|
|
489
|
+
for (const token of tokens) {
|
|
490
|
+
const cmd = token[0];
|
|
491
|
+
const upper = cmd.toUpperCase();
|
|
492
|
+
if (upper === 'Z') {
|
|
493
|
+
out.push('Z');
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
496
|
+
const nums = (token.slice(1).match(/-?[\d.]+(?:e-?\d+)?/gi) ?? []).map(Number);
|
|
497
|
+
if (upper === 'A') {
|
|
498
|
+
// rx ry x-axis-rotation large-arc-flag sweep-flag x y (per 7-number group)
|
|
499
|
+
const parts = [];
|
|
500
|
+
for (let i = 0; i + 6 < nums.length; i += 7) {
|
|
501
|
+
parts.push(round(nums[i] * sx), round(nums[i + 1] * sy), String(nums[i + 2]), String(nums[i + 3]), String(nums[i + 4]), round(nums[i + 5] * sx), round(nums[i + 6] * sy));
|
|
502
|
+
}
|
|
503
|
+
out.push(`A ${parts.join(' ')}`);
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
// M/L/C/Q (and H/V) carry (x, y) pairs; scale x by sx and y by sy.
|
|
507
|
+
const scaled = nums.map((n, i) => round(n * (i % 2 === 0 ? sx : sy)));
|
|
508
|
+
out.push(`${upper} ${scaled.join(' ')}`);
|
|
509
|
+
}
|
|
510
|
+
if (out.length === 0) {
|
|
511
|
+
return undefined;
|
|
512
|
+
}
|
|
513
|
+
return `path('${out.join(' ')}')`;
|
|
514
|
+
}
|
|
453
515
|
/**
|
|
454
516
|
* Element-level convenience wrapper. Pulls `shapeType`, `width`, `height`, and
|
|
455
517
|
* `shapeAdjustments` off a {@link PptxElement} and delegates to
|
|
456
518
|
* {@link getResolvedShapeClipPathFor}.
|
|
457
519
|
*
|
|
520
|
+
* Custom-geometry freeforms (which carry `pathData`/`pathWidth`/`pathHeight`
|
|
521
|
+
* rather than a preset `shapeType`) take priority: their outline is rescaled
|
|
522
|
+
* into the element box via {@link buildCustomGeometryClipPath} so the fill clips
|
|
523
|
+
* to the real shape instead of its bounding rectangle.
|
|
524
|
+
*
|
|
458
525
|
* @param element The PPTX element to resolve a clip-path for.
|
|
459
526
|
* @param width Optional width override (pixels). Defaults to `element.width`.
|
|
460
527
|
* @param height Optional height override (pixels). Defaults to `element.height`.
|
|
461
528
|
*/
|
|
462
529
|
function getResolvedShapeClipPath(element, width, height) {
|
|
530
|
+
const w = typeof width === 'number' ? width : element.width;
|
|
531
|
+
const h = typeof height === 'number' ? height : element.height;
|
|
532
|
+
const custom = element;
|
|
533
|
+
if (custom.pathData && custom.pathWidth && custom.pathHeight) {
|
|
534
|
+
const customClip = buildCustomGeometryClipPath(custom.pathData, custom.pathWidth, custom.pathHeight, w, h);
|
|
535
|
+
if (customClip) {
|
|
536
|
+
return customClip;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
463
539
|
const shapeType = element.shapeType;
|
|
464
540
|
if (!shapeType) {
|
|
465
541
|
return undefined;
|
|
466
542
|
}
|
|
467
|
-
const w = typeof width === 'number' ? width : element.width;
|
|
468
|
-
const h = typeof height === 'number' ? height : element.height;
|
|
469
543
|
const adjustments = element.shapeAdjustments;
|
|
470
544
|
return getResolvedShapeClipPathFor(shapeType, w, h, adjustments);
|
|
471
545
|
}
|
|
@@ -1450,20 +1524,29 @@ function getSvgStrokeDasharray(dashType, strokeWidth, customDashSegments) {
|
|
|
1450
1524
|
/**
|
|
1451
1525
|
* Builds a CSS `transform` string combining flip and rotation transforms for an element.
|
|
1452
1526
|
* Flips are expressed as `scaleX(-1)` / `scaleY(-1)`, rotation as `rotate(Ndeg)`.
|
|
1527
|
+
*
|
|
1528
|
+
* Order matters: OOXML `a:xfrm` mirrors the shape *within* its bounding box
|
|
1529
|
+
* (`flipH`/`flipV`) and *then* rotates the box by `rot`. With CSS
|
|
1530
|
+
* `transform-origin: center`, transforms apply right-to-left, so the flips must
|
|
1531
|
+
* come AFTER the rotation in the string (`rotate(θ) scaleX(-1)`) to be applied
|
|
1532
|
+
* first. Emitting `scaleX(-1) rotate(θ)` instead reflects the rotation direction
|
|
1533
|
+
* for any shape that is both flipped and rotated (e.g. the "Balloons" freeforms
|
|
1534
|
+
* render mirrored/tilted the wrong way). This matches the Angular binding's
|
|
1535
|
+
* `getContainerStyle`, which already emits `rotate() scaleX() scaleY()`.
|
|
1453
1536
|
* @param element - The element whose transforms are read.
|
|
1454
1537
|
* @returns A CSS transform string, or `undefined` if no transforms apply.
|
|
1455
1538
|
*/
|
|
1456
1539
|
function getElementTransform(element) {
|
|
1457
1540
|
const transforms = [];
|
|
1541
|
+
if (element.rotation) {
|
|
1542
|
+
transforms.push(`rotate(${element.rotation}deg)`);
|
|
1543
|
+
}
|
|
1458
1544
|
if (element.flipHorizontal) {
|
|
1459
1545
|
transforms.push('scaleX(-1)');
|
|
1460
1546
|
}
|
|
1461
1547
|
if (element.flipVertical) {
|
|
1462
1548
|
transforms.push('scaleY(-1)');
|
|
1463
1549
|
}
|
|
1464
|
-
if (element.rotation) {
|
|
1465
|
-
transforms.push(`rotate(${element.rotation}deg)`);
|
|
1466
|
-
}
|
|
1467
1550
|
if (element.skewX) {
|
|
1468
1551
|
transforms.push(`skewX(${element.skewX}deg)`);
|
|
1469
1552
|
}
|
|
@@ -73870,7 +73953,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
73870
73953
|
}], propDecorators: { canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], slideIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "slideIndex", required: false }] }] } });
|
|
73871
73954
|
|
|
73872
73955
|
// Generated by scripts/inline-shared.mjs from package.json. Do not edit.
|
|
73873
|
-
const PPTX_ANGULAR_VIEWER_VERSION = "1.31.
|
|
73956
|
+
const PPTX_ANGULAR_VIEWER_VERSION = "1.31.2";
|
|
73874
73957
|
|
|
73875
73958
|
/**
|
|
73876
73959
|
* account-page.component.ts: File > Account content.
|