@contrail/documents 1.20.0-alpha.25ac790 → 1.20.0-alpha.d5bd4fa

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
@@ -10,8 +10,9 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
10
10
  ### Added
11
11
 
12
12
  - `MaskDefinition` and `MaskShapeKey` on `DocumentElement` — `maskDefinition` records that an image is masked into one of nine preset shapes, with an optional clockwise `rotation`. Its presence changes how `cropDefinition`'s percents are read: element-space (the mask window within the element) instead of the source-image crop they otherwise mean. Anything deriving image geometry must check `hasShapeMask` before treating a crop as a source crop — reading one as the other samples the wrong region of the bitmap.
13
- - `@contrail/documents/util/mask` — the shared mask model, exported from the package root: the nine shapes as renderer-agnostic cubic path segments (`getMaskShapeSegments`), `rotatedRectAABB` for the bounds a tilted shape actually occupies, `rotatePoint`, the mask-rect/percent conversions, and `MASK_SHAPE_PRESET_GEOMETRY` mapping each shape to its OOXML preset so exporters and the canvas cannot drift apart. Segments are cubic-only because cubics survive affine maps exactly, letting each consumer render them however it draws — `Path2D` in a browser, preset geometry in pptx.
13
+ - `@contrail/documents/util/mask` — the shared mask model, exported from the package root: the nine shapes as renderer-agnostic cubic path segments (`getMaskShapeSegments`), `rotatedRectAABB` for the bounds a tilted shape actually occupies, `rotatePoint`, and the mask-rect/percent conversions, so exporters and the canvas cannot drift apart. Segments are cubic-only because cubics survive affine maps exactly, letting each consumer render them however it draws — `Path2D` in a browser, an SVG composite in a service.
14
14
  - `getMaskBakeGeometry`, `getMaskBakeLayout` and `maskShapeSegmentsToSvgPath` — the bake inputs a consumer needs to flatten a mask into a bitmap, and an SVG serializer for renderers that go through SVG rather than a canvas path. The layout scales to the source bitmap's own resolution rather than the element's on-board size, so a shrunk image does not bake at reduced detail, and re-bases a rotated mask onto its own bounding box. Only the rasterizer stays per-consumer: `Path2D` in a browser, an SVG composite in a service.
15
+ - `hasShapeMask` is a type guard and the complete discriminator: it requires a shape key the renderer knows and all four crop percents, so an origin of `0` passes while a missing origin (previously multiplied into `NaN` by `cropDefinitionToMaskRect`) and an unknown shape (previously thrown on by `getMaskShapeSegments`) are rejected up front. The true branch narrows the percents to numbers.
15
16
 
16
17
  ## [1.18.0] - 2026-09-10
17
18
 
@@ -1,5 +1,4 @@
1
1
  export * from './mask-shape-geometry';
2
2
  export * from './rotation-math';
3
3
  export * from './shape-mask-element';
4
- export * from './mask-preset-geometry';
5
4
  export * from './mask-bake-layout';
@@ -17,5 +17,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./mask-shape-geometry"), exports);
18
18
  __exportStar(require("./rotation-math"), exports);
19
19
  __exportStar(require("./shape-mask-element"), exports);
20
- __exportStar(require("./mask-preset-geometry"), exports);
21
20
  __exportStar(require("./mask-bake-layout"), exports);
@@ -1,6 +1,13 @@
1
- import { CropDefinition, DocumentElement, SizeDefinition } from '../../types';
1
+ import { CropDefinition, DocumentElement, MaskDefinition, SizeDefinition } from '../../types';
2
2
  import { MaskRect, MaskShapeKey } from './mask-shape-geometry';
3
- export declare function hasShapeMask(element: DocumentElement): boolean;
3
+ export type MaskCropDefinition = CropDefinition & Required<Pick<CropDefinition, 'x1Percent' | 'y1Percent' | 'widthPercent' | 'heightPercent'>>;
4
+ export type ShapeMaskedElement = DocumentElement & {
5
+ maskDefinition: MaskDefinition & {
6
+ shape: MaskShapeKey;
7
+ };
8
+ cropDefinition: MaskCropDefinition;
9
+ };
10
+ export declare function hasShapeMask(element: DocumentElement): element is ShapeMaskedElement;
4
11
  export declare function getShapeMaskKey(element: DocumentElement): MaskShapeKey | null;
5
12
  export declare function getShapeMaskRotation(element: DocumentElement): number;
6
13
  export declare function normalizeMaskRotation(rotation: number): number;
@@ -9,10 +9,19 @@ exports.isShapeMaskable = isShapeMaskable;
9
9
  exports.getDefaultShapeMaskCrop = getDefaultShapeMaskCrop;
10
10
  exports.cropDefinitionToMaskRect = cropDefinitionToMaskRect;
11
11
  exports.maskRectToCropDefinition = maskRectToCropDefinition;
12
+ const mask_shape_geometry_1 = require("./mask-shape-geometry");
12
13
  const DEFAULT_MASK_FRACTION = 0.72;
13
14
  function hasShapeMask(element) {
14
- var _a, _b;
15
- return (!!getShapeMaskKey(element) && !!((_a = element === null || element === void 0 ? void 0 : element.cropDefinition) === null || _a === void 0 ? void 0 : _a.widthPercent) && !!((_b = element === null || element === void 0 ? void 0 : element.cropDefinition) === null || _b === void 0 ? void 0 : _b.heightPercent));
15
+ var _a;
16
+ const shape = (_a = element === null || element === void 0 ? void 0 : element.maskDefinition) === null || _a === void 0 ? void 0 : _a.shape;
17
+ if (!shape || !mask_shape_geometry_1.MASK_SHAPE_KEYS.includes(shape)) {
18
+ return false;
19
+ }
20
+ const crop = element === null || element === void 0 ? void 0 : element.cropDefinition;
21
+ return (Number.isFinite(crop === null || crop === void 0 ? void 0 : crop.x1Percent) &&
22
+ Number.isFinite(crop === null || crop === void 0 ? void 0 : crop.y1Percent) &&
23
+ crop.widthPercent > 0 &&
24
+ crop.heightPercent > 0);
16
25
  }
17
26
  function getShapeMaskKey(element) {
18
27
  var _a, _b;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrail/documents",
3
- "version": "1.20.0-alpha.25ac790",
3
+ "version": "1.20.0-alpha.d5bd4fa",
4
4
  "description": "Documents library for contrail platform",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -1,3 +0,0 @@
1
- import { MaskShapeKey } from './mask-shape-geometry';
2
- export declare const MASK_SHAPE_PRESET_GEOMETRY: Record<MaskShapeKey, string>;
3
- export declare function getMaskPresetGeometry(shape: MaskShapeKey | null): string | null;
@@ -1,19 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MASK_SHAPE_PRESET_GEOMETRY = void 0;
4
- exports.getMaskPresetGeometry = getMaskPresetGeometry;
5
- exports.MASK_SHAPE_PRESET_GEOMETRY = {
6
- circle: 'ellipse',
7
- square: 'rect',
8
- rounded: 'roundRect',
9
- triangle: 'triangle',
10
- diamond: 'diamond',
11
- parallelogram: 'parallelogram',
12
- star: 'star5',
13
- heart: 'heart',
14
- cloud: 'cloud',
15
- };
16
- function getMaskPresetGeometry(shape) {
17
- var _a;
18
- return shape ? (_a = exports.MASK_SHAPE_PRESET_GEOMETRY[shape]) !== null && _a !== void 0 ? _a : null : null;
19
- }