🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@armniko/canvas

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@armniko/canvas - npm Package Compare versions

Comparing version
2.0.0
to
2.1.0
+42
dist/enums.d.ts
export declare enum ElementType {
Rectangle = "rectangle",
Ellipse = "ellipse",
Polygon = "polygon",
Line = "line",
Text = "text",
Image = "image"
}
export declare enum ColorType {
SolidColor = "solid-color",
LinearGradient = "linear-gradient",
RadialGradient = "radial-gradient"
}
export declare enum FontWeight {
Normal = "normal",
Bold = "bold"
}
export declare enum HorizontalAlign {
Left = "left",
Center = "center",
Right = "right"
}
export declare enum ImageFit {
Fill = "fill",
Cover = "cover"
}
export declare enum LineCap {
Round = "round",
Square = "square"
}
export declare enum PositionPoint {
LeftTop = "left-top",
RightTop = "right-top",
LeftBottom = "left-bottom",
RightBottom = "right-bottom",
Center = "center"
}
export declare enum VerticalAlign {
Top = "top",
Center = "center",
Bottom = "bottom"
}
+15
-23

@@ -0,9 +1,4 @@

/** Base */
export { Canvas } from './libraries/canvas';
export type { Color } from './libraries/colors/color';
export type { Gradient } from './libraries/colors/gradient';
export { GradientColor } from './libraries/colors/gradient-color';
export { SolidColor } from './libraries/colors/solid-color';
export { LinearGradient } from './libraries/colors/linear-gradient';
export { RadialGradient } from './libraries/colors/radial-gradient';
export type { Element } from './libraries/elements/element';
/** Elements */
export { RectangleElement } from './libraries/elements/rectangle-element';

@@ -15,18 +10,15 @@ export { EllipseElement } from './libraries/elements/ellipse-element';

export { ImageElement } from './libraries/elements/image-element';
/** Colors */
export { GradientColor } from './libraries/colors/gradient-color';
export { SolidColor } from './libraries/colors/solid-color';
export { LinearGradient } from './libraries/colors/linear-gradient';
export { RadialGradient } from './libraries/colors/radial-gradient';
/** Other */
export { Shape } from './libraries/shapes/shape';
export { ColorType } from './enums/color-type';
export { FontWeight } from './enums/font-weight';
export { HorizontalAlign } from './enums/horizontal-align';
export { ImageFit } from './enums/image-fit';
export { LineCap } from './enums/line-cap';
export { PositionPoint } from './enums/position-point';
export { VerticalAlign } from './enums/vertical-align';
export type { Position } from './interfaces/position.interface';
export type { Size } from './interfaces/size.interface';
export type { Scale } from './interfaces/scale.interface';
export type { Rotation } from './interfaces/rotation.interface';
export type { CropArea } from './interfaces/crop-area.interface';
export type { TextBox } from './interfaces/text-box.interface';
export type { TextLine } from './interfaces/text-line.interface';
export type { RelativeProps } from './interfaces/relative-props.interface';
export type { ResolvableAttribute, ImageSource } from './types';
/** Enums */
export { PositionPoint, HorizontalAlign, VerticalAlign, ColorType, FontWeight, ImageFit, LineCap } from './enums';
/** Types */
export { type Element } from './libraries/elements/element';
export { type Color } from './libraries/colors/color';
export { type Gradient } from './libraries/colors/gradient';
export type { ResolvableAttribute, ImageSource, Position, Size, Scale, Rotation, CropArea, TextBox, TextLine, RelativeProps, } from './types';

@@ -1,1 +0,1880 @@

var t=/* @__PURE__ */function(t){return t.Rectangle="rectangle",t.Ellipse="ellipse",t.Polygon="polygon",t.Line="line",t.Text="text",t.Image="image",t}({}),i=/* @__PURE__ */function(t){return t.LeftTop="left-top",t.RightTop="right-top",t.LeftBottom="left-bottom",t.RightBottom="right-bottom",t.Center="center",t}({}),e=class t{static valueInRange(t,i){return t<i.min&&(t=i.min),t>i.max&&(t=i.max),t}static valueNotGreaterThan(t,i){return t>i&&(t=i),t}static valueNotLessThan(t,i){return t<i&&(t=i),t}static relativePositionValue(i,e,s){const o=t.extractedValue(i);let h;if("%"===o.unit)h=(s-e)/100*o.value;else h=Number(i);return h}static extractedValue(t){let i;if("string"==typeof t){const e=t.match(/^(\d+)(\D+)$/)??[];i=e.length>2?{value:parseFloat(e[1]??"0"),unit:e[2]??"px"}:{value:Number(t),unit:"px"}}else i={value:t,unit:"px"};return i}static degreesToRadians(t){return t/180*Math.PI}static distance(t,i){return Math.sqrt(Math.pow(Math.abs(i.x-t.x),2)+Math.pow(Math.abs(i.y-t.y),2))}static positionBetweenPoints(i,e,s){const o=s/t.distance(i,e);return{x:i.x+o*(e.x-i.x),y:i.y+o*(e.y-i.y)}}static shapeSize(t){const i=Math.min(...t.map(t=>t.x)),e=Math.min(...t.map(t=>t.y)),s=Math.max(...t.map(t=>t.x)),o=Math.max(...t.map(t=>t.y));return{width:Math.floor(s-i),height:Math.floor(o-e)}}static positionLeftTop(t,e,s){const o={x:t.x,y:t.y};switch(e){case i.RightTop:o.x-=s.width;break;case i.RightBottom:o.x-=s.width,o.y-=s.height;break;case i.LeftBottom:o.y-=s.height;break;case i.Center:o.x-=s.width/2,o.y-=s.height/2}return o}static positionCenter(i,e,s){const o=t.positionLeftTop(i,e,s);return o.x+=s.width/2,o.y+=s.height/2,o}static resizedSize(t,i,e){const s={width:i.width,height:i.height};if(e){const e=[{width:t.width*(i.height/t.height),height:i.height},{width:i.width,height:t.height*(i.width/t.width)}].find(t=>t.width<=i.width&&t.height<=i.height);e&&(s.width=e.width,s.height=e.height)}return s}static resolvedValue(t){let i;return i=t&&"function"==typeof t?t():t,i}static isObject(t){return"object"==typeof t&&t.constructor===Object&&!Array.isArray(t)}static hasMethod(t,i){return!!t[i]&&"function"==typeof t[i]}static hash(i){return i.map(i=>{let e;return e=null==i?"undefined":t.isObject(i)?JSON.stringify(i):Array.isArray(i)?"["+t.hash(i)+"]":t.hasMethod(i,"hash")?i.hash():i.toString(),e}).join("|")}},s=class{ctx;element;relativeProps;defaultBorderColor="#000000";constructor(t,i,e){this.ctx=t,this.element=i,this.relativeProps=e??{}}relativePosition(){return this.relativeProps.position??{x:0,y:0}}relativeScale(){return this.relativeProps.scale??{x:1,y:1}}rotations(){return[...this.relativeProps.rotations??[],...this.element.rotations()]}relativeOpacity(){return this.relativeProps.opacity??1}scale(){const t=this.element.scale(),i=this.relativeScale();return{x:t.x*i.x,y:t.y*i.y}}degrees(){return this.element.degrees()??0}anchor(){return this.element.anchor()??this.defaultAnchor()}opacity(){return this.element.opacity()*this.relativeOpacity()}startDrawing(){this.ctx.save();const t=this.scale(),i=this.opacity(),e=this.degrees(),s=this.rotations(),o=this.element.shadow();1===t.x&&1===t.y||this.ctx.scale(t.x,t.y),i<1&&(this.ctx.globalAlpha=i),0!==e&&this.rotate({degrees:e,pivot:this.anchor()}),s.length>0&&s.forEach(t=>{0!==t.degrees&&this.rotate(t)}),o&&o.blur>0&&(this.ctx.shadowColor=o.color,this.ctx.shadowBlur=o.blur,this.ctx.shadowOffsetX=o.x,this.ctx.shadowOffsetY=o.y)}endDrawing(){this.ctx.restore()}rotate(t){const i=this.position(),s=t.pivot??this.defaultAnchor(),o={x:s.x+i.x,y:s.y+i.y};this.ctx.translate(o.x,o.y),this.ctx.rotate(e.degreesToRadians(t.degrees)),this.ctx.translate(-o.x,-o.y)}},o=class{_originalSize;_sizeRatio;_shapePosition;constructor(t,i){this.ctx=t,this.shape=i}draw(){this.prepare(),this.ctx.save(),this.rotateContext(),this.shape.isCloseShape()?this.makeClosedShape():this.makeOpenedShape(),this.ctx.restore()}prepare(){this._originalSize=e.shapeSize(this.shape.points()),this._sizeRatio=this.sizeRatio(),this._shapePosition=this.shapePosition()}rotateContext(){const t=this.shape.degrees();0!==t&&this.rotate({degrees:t}),this.shape.rotations().forEach(t=>{this.rotate(t)})}makeClosedShape(){const t=this.shape.points();t.length>2&&t.forEach((i,s)=>{const o=0===s,h=s===t.length-1,n=t[s+1]??t[0],r=e.distance(i,n),a=this.radius(r),l=e.positionBetweenPoints(i,n,a),d=e.positionBetweenPoints(i,n,r-a);if(o&&this.moveToPoint(l),this.lineToPoint(d),a>0){const i=t[s+2]??(h?t[1]:t[0]);this.quadraticCurveToPoint(n,i)}})}makeOpenedShape(){const t=this.shape.points();t.length>1&&t.forEach((i,s)=>{const o=0===s,h=s===t.length-1,n=s===t.length-2;if(!h){const h=t[s+1],r=h?e.distance(i,h):0,a=this.radius(r),l=o?i:e.positionBetweenPoints(i,h,a),d=n?h:e.positionBetweenPoints(i,h,r-a);o&&this.moveToPoint(l),this.lineToPoint(d),!n&&a>0&&this.quadraticCurveToPoint(h,t[s+2])}})}radius(t){const i=Math.min(this._sizeRatio.width,this._sizeRatio.height),e=Math.floor(this.shape.jointsRadius()/i),s=Math.floor(.55*t);return e>s?s:e}moveToPoint(t){this.ctx.moveTo(this._shapePosition.x+t.x*this._sizeRatio.width,this._shapePosition.y+t.y*this._sizeRatio.height)}lineToPoint(t){this.ctx.lineTo(this._shapePosition.x+t.x*this._sizeRatio.width,this._shapePosition.y+t.y*this._sizeRatio.height)}quadraticCurveToPoint(t,i){const s=e.distance(t,i),o=this.radius(s),h=e.positionBetweenPoints(t,i,o);this.ctx.quadraticCurveTo(this._shapePosition.x+t.x*this._sizeRatio.width,this._shapePosition.y+t.y*this._sizeRatio.height,this._shapePosition.x+h.x*this._sizeRatio.width,this._shapePosition.y+h.y*this._sizeRatio.height)}shapePosition(){return e.positionLeftTop(this.shape.position(),this.shape.positionPoint(),this.shapeSize())}shapeSize(){return this.shape.size()??this._originalSize}sizeRatio(){const t=this.shapeSize();return{width:t.width/e.valueNotLessThan(this._originalSize.width,1),height:t.height/e.valueNotLessThan(this._originalSize.height,1)}}rotate(t){const i=this.shapePosition(),s=t.pivot??this.defaultAnchor();s.x+=i.x,s.y+=i.y,this.ctx.translate(s.x,s.y),this.ctx.rotate(e.degreesToRadians(t.degrees)),this.ctx.translate(-s.x,-s.y)}defaultAnchor(){const t=this.shapeSize();return{x:t.width/2,y:t.height/2}}},h=/* @__PURE__ */function(t){return t.SolidColor="solid-color",t.LinearGradient="linear-gradient",t.RadialGradient="radial-gradient",t}({}),n=class{constructor(t,i,e){this.ctx=t,this.elementPosition=i,this.elementSize=e}mixed(t){let i="";if("string"==typeof t)i=t;else switch(t.type()){case h.SolidColor:i=t.color();break;case h.LinearGradient:i=this.linearGradient(t);break;case h.RadialGradient:i=this.radialGradient(t)}return i}linearGradient(t){const i=e.degreesToRadians(t.degrees()),s=this.elementPosition.x+this.elementSize.width/2,o=this.elementPosition.y+this.elementSize.height/2,h=Math.sqrt(this.elementSize.width**2+this.elementSize.height**2)/2,n=Math.cos(i)*h,r=Math.sin(i)*h,a={x:s-n,y:o-r},l={x:s+n,y:o+r},d=this.ctx.createLinearGradient(a.x,a.y,l.x,l.y);return this.appendGradientColors(d,t.colors()),d}radialGradient(t){const i=t.position()??{x:this.elementSize.width/2,y:this.elementSize.height/2},e={x:this.elementPosition.x+i.x,y:this.elementPosition.y+i.y},s=Math.sqrt(this.elementSize.width*this.elementSize.width+this.elementSize.height*this.elementSize.height)/2,o=this.ctx.createRadialGradient(e.x,e.y,0,e.x,e.y,s*t.radiusScale());return this.appendGradientColors(o,t.colors()),o}appendGradientColors(t,i){i.forEach(i=>t.addColorStop(i.offset(),i.color()))}},r=class extends s{draw(){this.startDrawing(),this.makeShape(),this.drawFill(),this.drawBorder(),this.endDrawing()}makeShape(){const t=this.element.borderWidth(),i=this.position(),e=this.element.size(),s=this.element.cutOutShapes(),h={x:i.x+t/2,y:i.y+t/2},n={width:e.width-t,height:e.height-t};this.ctx.beginPath(),s.length>0&&(this.ctx.translate(h.x,h.y),s.forEach(t=>{new o(this.ctx,t).draw()}),this.ctx.translate(-h.x,-h.y),h.x+=n.width,n.width*=-1),this.ctx.rect(h.x,h.y,n.width,n.height),this.ctx.closePath()}drawFill(){const t=this.element.fillColor();t&&(this.ctx.fillStyle=new n(this.ctx,this.position(),this.element.size()).mixed(t),this.ctx.fill())}drawBorder(){const t=this.element.borderWidth();t>0&&(this.ctx.strokeStyle=new n(this.ctx,this.position(),this.element.size()).mixed(this.element.borderColor()??this.defaultBorderColor),this.ctx.lineWidth=t,this.ctx.stroke())}position(){const t=this.element.positionLeftTop(),i=this.relativePosition();return{x:t.x+i.x,y:t.y+i.y}}defaultAnchor(){const t=this.element.size();return{x:t.width/2,y:t.height/2}}},a=class extends s{draw(){this.startDrawing(),this.drawFill(),this.drawBorder(),this.endDrawing()}drawFill(){const t=this.element.fillColor();if(t){const i=this.element.borderWidth(),s=this.position(),o=this.element.size();this.ctx.beginPath(),this.ctx.ellipse(s.x,s.y,e.valueNotLessThan(o.width/2-i,1),e.valueNotLessThan(o.height/2-i,1),0,0,2*Math.PI),this.ctx.closePath(),this.ctx.fillStyle=new n(this.ctx,this.element.positionLeftTop(),this.element.size()).mixed(t),this.ctx.fill()}}drawBorder(){const t=this.element.borderWidth();if(t>0){const i=this.position(),s=this.element.size();this.ctx.beginPath(),this.ctx.ellipse(i.x,i.y,e.valueNotLessThan(s.width/2-t/2,1),e.valueNotLessThan(s.height/2-t/2,1),0,0,2*Math.PI),this.ctx.closePath(),this.ctx.strokeStyle=new n(this.ctx,this.element.positionLeftTop(),this.element.size()).mixed(this.element.borderColor()??this.defaultBorderColor),this.ctx.lineWidth=t,this.ctx.stroke()}}position(){const t=e.positionCenter(this.element.position(),this.element.positionPoint(),this.element.boundingClientSize()),i=this.relativePosition();return{x:t.x+i.x,y:t.y+i.y}}defaultAnchor(){return{x:0,y:0}}},l=class extends s{draw(){this.prepare(),this.startDrawing(),this.drawStroke(),this.drawFill(),this.endDrawing()}prepare(){this.ctx.font=this.element.font(),this.ctx.textBaseline="alphabetic"}drawFill(){const t=this.element.fillColor();if(t){const i=this.element.textBox(),e=this.position();this.ctx.fillStyle=new n(this.ctx,this.position(),i.size).mixed(t),i.lines.forEach(t=>{this.ctx.fillText(t.text,e.x+t.position.x,Math.round(e.y+t.position.y+.75*t.size.height))})}}drawStroke(){const t=this.element.strokeWidth();if(t>0){const i=this.element.textBox(),e=this.position(),s=this.element.fillColor();this.ctx.strokeStyle=new n(this.ctx,this.position(),i.size).mixed(this.element.strokeColor()??this.defaultBorderColor),this.ctx.lineWidth=s?2*t:t,i.lines.forEach(t=>{this.ctx.strokeText(t.text,e.x+t.position.x,Math.round(e.y+t.position.y+.75*t.size.height))})}}position(){const t=this.element.positionLeftTop(),i=this.relativePosition();return{x:t.x+i.x,y:t.y+i.y}}defaultAnchor(){const t=this.element.textBox();return{x:t.size.width/2,y:t.size.height/2}}},d=/* @__PURE__ */function(t){return t.Fill="fill",t.Cover="cover",t}({}),c=class extends s{draw(){this.startDrawing(),this.drawBorder(),this.drawImage(),this.endDrawing()}drawImage(){const t=this.element.image();if(t){const i=this.sourceDimensions(),e=this.flippedDimensions(this.elementDimensions());this.ctx.drawImage(t,i.position.x,i.position.y,i.size.width,i.size.height,e.position.x,e.position.y,e.size.width,e.size.height)}}drawBorder(){const t=this.element.borderWidth(),i=new n(this.ctx,this.position(),this.element.size()).mixed(this.element.borderColor()??this.defaultBorderColor),e=this.elementDimensions();this.ctx.strokeStyle=i,this.ctx.lineWidth=2*t,this.isRadiusSet()?(this.makeShape(e),this.ctx.lineJoin="round",this.ctx.fillStyle="#ffffff",this.ctx.fill(),t>0&&(this.ctx.strokeStyle=i,this.ctx.lineWidth=2*t,this.ctx.stroke()),this.ctx.clip()):t>0&&(this.ctx.strokeStyle=i,this.ctx.lineWidth=2*t,this.ctx.strokeRect(e.position.x,e.position.y,e.size.width,e.size.height))}sourceDimensions(){const t=this.element.image(),i=this.element.cropArea(),s={position:{x:0,y:0},size:i.size??{width:t.width,height:t.height}};switch(this.element.fit()){case d.Fill:i.position&&(s.position.x=i.position.x,s.position.y=i.position.y);break;case d.Cover:{const{size:o}=this.elementDimensions();o.width/o.height<s.size.width/s.size.height?s.size.width=s.size.height/(o.height/o.width):s.size.height=s.size.width/(o.width/o.height),i.position?(s.position.x=i.position.x,s.position.y=i.position.y):(s.position.x=e.relativePositionValue("50%",s.size.width,t.width),s.position.y=e.relativePositionValue("50%",s.size.height,t.height));break}}return s}elementDimensions(){const t=this.element.borderWidth(),i=this.position(),e=this.element.size();return{position:{x:i.x+t,y:i.y+t},size:{width:e.width-2*t,height:e.height-2*t}}}flippedDimensions(t){const i=this.element.flipX(),e=this.element.flipY();if(i||e){let s=1,o=1;i&&(s=-1,t.position.x=-(+t.position.x+t.size.width)),e&&(o=-1,t.position.y=-(+t.position.y+t.size.height)),this.ctx.scale(s,o)}return t}makeShape(t){const i=this.borderRadius();this.ctx.lineJoin="round",this.ctx.beginPath(),this.ctx.moveTo(t.position.x+i.topLeft,t.position.y),this.ctx.lineTo(t.position.x+t.size.width-i.topRight,t.position.y),this.ctx.quadraticCurveTo(t.position.x+t.size.width,t.position.y,t.position.x+t.size.width,t.position.y+i.topRight),this.ctx.lineTo(t.position.x+t.size.width,t.position.y+t.size.height-i.bottomRight),this.ctx.quadraticCurveTo(t.position.x+t.size.width,t.position.y+t.size.height,t.position.x+t.size.width-i.bottomRight,t.position.y+t.size.height),this.ctx.lineTo(t.position.x+i.bottomLeft,t.position.y+t.size.height),this.ctx.quadraticCurveTo(t.position.x,t.position.y+t.size.height,t.position.x,t.position.y+t.size.height-i.bottomLeft),this.ctx.lineTo(t.position.x,t.position.y+i.topLeft),this.ctx.quadraticCurveTo(t.position.x,t.position.y,t.position.x+i.topLeft,t.position.y),this.ctx.closePath()}borderRadius(){const t=this.element.borderRadius();if(this.isRadiusSet()){const{size:i}=this.elementDimensions();Object.keys(t).forEach(e=>{t[e]>i.width/2&&(t[e]=Math.floor(i.width/2)),t[e]>i.height/2&&(t[e]=Math.floor(i.height/2))})}return t}isRadiusSet(){const t=this.element.borderRadius();return t.topLeft>0||t.topRight>0||t.bottomLeft>0||t.bottomRight>0}position(){const t=this.element.positionLeftTop(),i=this.relativePosition();return{x:t.x+i.x,y:t.y+i.y}}defaultAnchor(){const t=this.element.size();return{x:t.width/2,y:t.height/2}}},u=class{_points=[];_isCloseShape;_position={x:0,y:0};_positionPoint=i.LeftTop;_size;_jointsRadius=0;_degrees=0;_anchor;_rotations=[];constructor(t,i=!0){this._points=t,this._isCloseShape=i}hash(){return e.hash([this.points(),this.isCloseShape(),this.position(),this.positionPoint(),this.size(),this.jointsRadius(),this.rotations(),this.degrees(),this.anchor()])}points(){return this._points}isCloseShape(){return this._isCloseShape}position(){return this._position}positionPoint(){return this._positionPoint}size(){return this._size}jointsRadius(){return this._jointsRadius}degrees(){return this._degrees}anchor(){return this._anchor}rotations(){return this._rotations}setPosition(t,i){return this._position=t,i&&(this._positionPoint=i),this}setSize(t){return this._size=t,this}setJointsRadius(t){return this._jointsRadius=t,this}setDegrees(t,i){return this._degrees=t,this._anchor=i,this}setRotations(t){return this._rotations=t,this}},p=class extends s{draw(){this.startDrawing(),this.makeShape(),this.drawBorder(),this.drawFill(),this.endDrawing()}makeShape(){const t=this.element.shapePoints();if(t.length>2){const i=new u(t).setSize(this.shapeSize()).setPosition(this.shapePosition()).setJointsRadius(this.element.borderRadius());this.ctx.beginPath(),new o(this.ctx,i).draw(),this.ctx.closePath()}}drawFill(){const t=this.element.fillColor();t&&(this.ctx.fillStyle=new n(this.ctx,this.position(),this.shapeSize()).mixed(t),this.ctx.fill())}drawBorder(){const t=this.element.borderWidth();if(t>0){const i=new n(this.ctx,this.position(),this.element.size()).mixed(this.element.borderColor()??this.defaultBorderColor),e=this.element.fillColor();this.element.borderRadius()>0&&(this.ctx.lineJoin="round"),this.ctx.strokeStyle=i,this.ctx.lineWidth=e?2*t:t,this.ctx.stroke()}}shapePosition(){const t=this.position(),i=this.element.borderWidth();return{x:t.x+i,y:t.y+i}}shapeSize(){const t=this.element.size(),i=this.element.borderWidth();return{width:t.width-2*i,height:t.height-2*i}}position(){const t=this.element.positionLeftTop(),i=this.relativePosition();return{x:t.x+i.x,y:t.y+i.y}}defaultAnchor(){const t=this.element.size();return{x:t.width/2,y:t.height/2}}},x=class extends s{draw(){this.startDrawing(),this.makeShape(),this.drawStroke(),this.endDrawing()}makeShape(){this.ctx.beginPath();const t=new u(this.element.shapePoints(),!1).setSize(this.shapeSize()).setPosition(this.shapePosition()).setJointsRadius(this.element.jointsRadius());new o(this.ctx,t).draw()}drawStroke(){const t=new n(this.ctx,this.position(),this.element.boundingClientSize()).mixed(this.element.color()??this.defaultBorderColor);this.ctx.lineCap=this.element.lineCap(),this.ctx.strokeStyle=t,this.ctx.lineWidth=this.element.calculatedThickness(),this.ctx.stroke()}shapeSize(){const t=this.element.boundingClientSize(),i=this.element.calculatedThickness();return{width:t.width-i,height:t.height-i/2}}shapePosition(){const t=this.position(),i=this.element.calculatedThickness();return{x:t.x+i/2,y:t.y}}position(){const t=this.element.positionLeftTop(),i=this.relativePosition();return{x:t.x+i.x,y:t.y+i.y}}defaultAnchor(){const t=this.shapeSize();return{x:t.width/2,y:t.height/2}}},g=class{_canvas;_ctx;constructor(t){this._canvas=t??document.createElement("canvas"),this._ctx=this._canvas.getContext("2d")}nativeElement(){return this._canvas}ctx(){return this._ctx}size(){return{width:this._canvas.width,height:this._canvas.height}}setSize(t){this._canvas.width=t.width,this._canvas.height=t.height}draw(i,e){switch(i.type()){case t.Rectangle:new r(this.ctx(),i,e).draw();break;case t.Ellipse:new a(this.ctx(),i,e).draw();break;case t.Polygon:new p(this.ctx(),i,e).draw();break;case t.Line:new x(this.ctx(),i,e).draw();break;case t.Text:new l(this.ctx(),i,e).draw();break;case t.Image:new c(this.ctx(),i,e).draw()}}clear(){this.ctx().clearRect(0,0,this._canvas.width,this._canvas.height)}},m=class{},f=class extends m{_color;constructor(t,i){super(),this.setColor(t,i)}type(){return h.SolidColor}color(){return this._color}hash(){return e.hash([this.color()])}setColor(t,i){const e=9===t.length?t.slice(-2):void 0,s=void 0!==i?this.alphaToHex(i):e??"ff";return this._color=t.slice(0,7)+s,this}alphaToHex(t){let i=Math.round(255*t).toString(16);return 1===i.length&&(i="0"+i),i}},_=class extends f{_offset;constructor(t,i,e=1){super(i,e),this.setOffset(t)}offset(){return this._offset}hash(){return e.hash([super.hash(),this.offset()])}setOffset(t){return this._offset=e.valueInRange(t,{min:0,max:1}),this}},w=class extends m{_colors;constructor(t){super(),this._colors=t}colors(){return this._colors}hash(){return e.hash([this.colors()])}},y=class extends w{_degrees=0;type(){return h.LinearGradient}degrees(){return this._degrees}setDegrees(t){return this._degrees=t,this}hash(){return e.hash([super.hash(),this.degrees()])}},z=class extends w{_position;_radiusScale;type(){return h.RadialGradient}position(){return this._position}radiusScale(){return this._radiusScale??1}setPosition(t){return this._position=t,this}setRadiusScale(t){return this._radiusScale=t,this}hash(){return e.hash([super.hash(),this.position(),this.radiusScale()])}},v=class{_position={x:0,y:0};_positionPoint;_scale={x:1,y:1};_opacity=1;_rotations=[];_degrees=0;_anchor;_shadow;hash(){return e.hash([this.position(),this.positionPoint(),this.scale(),this.opacity(),this.rotations(),this.degrees(),this.anchor(),this.shadow()])}position(){return e.resolvedValue(this._position)}positionLeftTop(){return e.positionLeftTop(this.position(),this.positionPoint(),this.boundingClientSize())}positionPoint(){return e.resolvedValue(this._positionPoint)??i.LeftTop}scale(){return e.resolvedValue(this._scale)}opacity(){return e.resolvedValue(this._opacity)}rotations(){return e.resolvedValue(this._rotations)}degrees(){return e.resolvedValue(this._degrees)}anchor(){return this._anchor?e.resolvedValue(this._anchor):void 0}shadow(){return this._shadow?e.resolvedValue(this._shadow):void 0}setPosition(t,i){return this._position=t,i&&(this._positionPoint=i),this}setScale(t){return this._scale=t,this}setOpacity(t){return this._opacity=t,this}setDegrees(t,i){return this._degrees=t,this._anchor=i,this}setRotations(t){return this._rotations=t,this}setShadow(t){return this._shadow=t,this}},S=class extends v{_size={width:1,height:1};_borderColor;_borderWidth=0;hash(){return e.hash([super.hash(),this.size(),this.borderColor(),this.borderWidth()])}size(){return e.resolvedValue(this._size)}borderColor(){return this._borderColor?e.resolvedValue(this._borderColor):void 0}borderWidth(){return e.resolvedValue(this._borderWidth)}setSize(t){return this._size=t,this}setBorder(t,i){return this._borderWidth=t,this._borderColor=i,this}},b=class extends S{_fillColor;_cutOutShapes=[];type(){return t.Rectangle}hash(){return e.hash([super.hash(),this.fillColor(),this.cutOutShapes()])}fillColor(){return this._fillColor?e.resolvedValue(this._fillColor):void 0}cutOutShapes(){return e.resolvedValue(this._cutOutShapes)}boundingClientSize(){return this.size()}setFillColor(t){return this._fillColor=t,this}setCutOutShapes(t){return this._cutOutShapes=t,this}},C=class extends S{_fillColor;type(){return t.Ellipse}hash(){return e.hash([super.hash(),this.fillColor()])}fillColor(){return this._fillColor?e.resolvedValue(this._fillColor):void 0}boundingClientSize(){return this.size()}setFillColor(t){return this._fillColor=t,this}},P=class extends S{_shapePoints=[];_borderRadius=0;_fillColor;type(){return t.Polygon}hash(){return e.hash([super.hash(),this.shapePoints(),this.borderRadius(),this.fillColor()])}shapePoints(){return e.resolvedValue(this._shapePoints)}borderRadius(){return e.resolvedValue(this._borderRadius)}fillColor(){return this._fillColor?e.resolvedValue(this._fillColor):void 0}boundingClientSize(){return this.size()}setShapePoints(t){return this._shapePoints=t,this}setBorderRadius(t){return this._borderRadius=t,this}setFillColor(t){return this._fillColor=t,this}},R=/* @__PURE__ */function(t){return t.Round="round",t.Square="square",t}({}),T=class extends v{_shapePoints=[];_size;_keepRatio;_thickness=1;_jointsRadius=0;_lineCap=R.Square;_color;type(){return t.Line}hash(){return e.hash([super.hash(),this.shapePoints(),this.size(),this.keepRatio(),this.thickness(),this.jointsRadius(),this.lineCap(),this.color()])}shapePoints(){return e.resolvedValue(this._shapePoints)}size(){return this._size?e.resolvedValue(this._size):void 0}keepRatio(){return e.resolvedValue(this._keepRatio)??!1}thickness(){return e.resolvedValue(this._thickness)}calculatedThickness(){const t=this.size();let i=this.thickness();if(t){const s=e.shapeSize(this.shapePoints()),o=e.resizedSize(s,t,this.keepRatio()),h=o.width/e.valueNotLessThan(s.width,1),n=o.height/e.valueNotLessThan(s.height,1);i=e.valueNotLessThan(i*Math.min(h,n),1)}return i}jointsRadius(){return e.resolvedValue(this._jointsRadius)}lineCap(){return e.resolvedValue(this._lineCap)}color(){return this._color?e.resolvedValue(this._color):void 0}boundingClientSize(){let t=this.size();const i=this.calculatedThickness(),s=e.shapeSize(this.shapePoints());if(t){const o=e.resizedSize(s,t,this.keepRatio());t={width:o.width,height:o.height+i/2}}else t={width:s.width,height:e.valueNotLessThan(s.height+i/2,0)};return t}setShapePoints(t){return this._shapePoints=t,this}setSize(t,i){return this._size=t,i&&(this._keepRatio=i),this}setThickness(t){return this._thickness=t,this}setJointsRadius(t){return this._jointsRadius=t,this}setLineCap(t){return this._lineCap=t,this}setColor(t){return this._color=t,this}},k=/* @__PURE__ */function(t){return t.Normal="normal",t.Bold="bold",t}({}),L=/* @__PURE__ */function(t){return t.Left="left",t.Center="center",t.Right="right",t}({}),V=/* @__PURE__ */function(t){return t.Top="top",t.Center="center",t.Bottom="bottom",t}({}),B=class{element;canvas;ctx;lastTextBox;lastTextBoxHash="";constructor(t){this.element=t,this.canvas=document.createElement("canvas"),this.ctx=this.canvas.getContext("2d")}textBox(){return this.elementChanged()&&(this.lastTextBox=void 0),this.lastTextBox||(this.lastTextBox=this.createTextBox()),this.lastTextBox}createTextBox(){const t={text:this.element.text(),lines:[],size:{width:1,height:1},position:{x:0,y:0}};return t.lines=this.textLines(),t.size=this.textBoxSize(t.lines),t.position=this.textBoxPosition(t.lines),t}textLines(){const t=this.element.bounds(),i=this.element.padding(),e=t?{width:t.width-i.left-i.right,height:t.height-i.top-i.bottom}:void 0,s=this.splitText().map(t=>({text:t,size:{width:this.textWidth(t),height:this.element.lineHeight()},position:{x:0,y:0}})).map((t,i,e)=>{const s=e[i-1];return t.position={x:0,y:s?s.position.y+s.size.height:0},t});return(void 0!==e?this.alignedLines(s,e):s).map(t=>(t.position.x+=i.left,t.position.y+=i.top,t))}alignedLines(t,i){const e=this.element.horizontalAlign(),s=this.element.verticalAlign(),o=t.reduce((t,i)=>t+i.size.height,0);return t.map(t=>(e===L.Center&&(t.position.x+=Math.round((i.width-t.size.width)/2)),e===L.Right&&(t.position.x+=i.width-t.size.width),s===V.Center&&(t.position.y+=Math.round((i.height-o)/2)),s===V.Bottom&&(t.position.y+=i.height-o),t))}textBoxSize(t){const i=this.element.padding(),e=this.element.bounds()??{width:1,height:1},s=Math.max(...t.map(t=>t.size.width));return{width:Math.max(s+i.left+i.right,e.width),height:Math.max(this.element.lineHeight()*t.length+i.top+i.bottom,e.height)}}textBoxPosition(t){const i=this.element.padding(),e=Math.min(...t.map(t=>t.position.x)),s=Math.min(...t.map(t=>t.position.y));return{x:e-i.left,y:s-i.top}}splitText(){const t=this.element.text().replaceAll("<BR>","<br>").split("<br>"),i=this.element.padding(),e=(this.element.bounds()??{width:999999,height:999999}).width-(i.left+i.right);let s=[];return t.forEach(t=>{t=t.trim();const i=this.textWidth(t);if(i>e){const o=Math.floor(e/(i/t.length))-3,h=t.split(" ");let n="";h.forEach((t,i)=>{n.length+t.length>o&&(s.push(n.trim()),n=""),n+=t+" ",i===h.length-1&&s.push(n.trim())}),s=s.map(t=>t.trim())}else s.push(t)}),s}textWidth(t){return this.ctx.font=this.element.font(),Math.ceil(this.ctx.measureText(t).width+this.element.strokeWidth())}elementChanged(){const t=[this.element.text(),this.element.bounds(),this.element.horizontalAlign(),this.element.verticalAlign(),this.element.padding(),this.element.lineHeight(),this.element.font(),this.element.strokeWidth()].join("|"),i=this.lastTextBoxHash!==t;return this.lastTextBoxHash=t,i}},A=class extends v{_text="";_bounds;_fontSize=12;_fontFamily="Tahoma";_fontWeight=k.Normal;_lineHeight;_fillColor;_strokeColor;_strokeWidth=0;_horizontalAlign=L.Left;_verticalAlign=V.Top;_padding={topOrVerticalOrDefault:0};_textSplitter=new B(this);type(){return t.Text}hash(){return e.hash([super.hash(),this.text(),this.bounds(),this.font(),this.lineHeight(),this.fillColor(),this.strokeColor(),this.strokeWidth(),this.horizontalAlign(),this.verticalAlign(),this.padding()])}text(){return e.resolvedValue(this._text).toString()}bounds(){return this._bounds?e.resolvedValue(this._bounds):void 0}fontSize(){return e.resolvedValue(this._fontSize)}fontFamily(){return e.resolvedValue(this._fontFamily)}fontWeight(){return e.resolvedValue(this._fontWeight)}lineHeight(){return e.resolvedValue(this._lineHeight??Math.round(1.2*this.fontSize()))}fillColor(){return this._fillColor?e.resolvedValue(this._fillColor):void 0}strokeColor(){return this._strokeColor?e.resolvedValue(this._strokeColor):void 0}strokeWidth(){return e.resolvedValue(this._strokeWidth)}horizontalAlign(){return e.resolvedValue(this._horizontalAlign)}verticalAlign(){return e.resolvedValue(this._verticalAlign)}padding(){let t;const i=e.resolvedValue(this._padding.topOrVerticalOrDefault),s=this._padding.rightOrHorizontal?e.resolvedValue(this._padding.rightOrHorizontal):void 0,o=this._padding.bottom?e.resolvedValue(this._padding.bottom):void 0,h=this._padding.left?e.resolvedValue(this._padding.left):void 0;return t=void 0===s?{top:i,right:i,bottom:i,left:i}:void 0===o&&void 0===h?{top:i,right:s,bottom:i,left:s}:{top:i,right:s,bottom:o??0,left:h??0},t}font(){const t=[this.fontSize()+"px",this.fontFamily()],i=this.fontWeight();return i&&t.unshift(i),t.join(" ")}textBox(){return this._textSplitter.textBox()}boundingClientSize(){return this.textBox().size}setText(t){return this._text=t,this}setBounds(t){return this._bounds=t,this}setFontSize(t){return this._fontSize=t,this}setFontFamily(t){return this._fontFamily=t,this}setFontWeight(t){return this._fontWeight=t,this}setLineHeight(t){return this._lineHeight=t,this}setFillColor(t){return this._fillColor=t,this}setStroke(t,i){return this._strokeWidth=t,this._strokeColor=i,this}setHorizontalAlign(t){return this._horizontalAlign=t,this}setVerticalAlign(t){return this._verticalAlign=t,this}setPadding(t,i,e,s){return this._padding={topOrVerticalOrDefault:t,rightOrHorizontal:i,bottom:e,left:s},this}},M=class extends S{_image;_fit=d.Fill;_borderRadius={topLeftOrDefault:0};_flipX;_flipY;_cropArea;type(){return t.Image}hash(){return e.hash([super.hash(),this.image()?.id,this.fit(),this.borderRadius(),this.flipX(),this.flipY(),this.cropArea()])}image(){return this._image?e.resolvedValue(this._image):void 0}fit(){return e.resolvedValue(this._fit)}borderRadius(){let t;const i=e.resolvedValue(this._borderRadius.topLeftOrDefault),s=this._borderRadius.topRight?e.resolvedValue(this._borderRadius.topRight):void 0,o=this._borderRadius.bottomRight?e.resolvedValue(this._borderRadius.bottomRight):void 0,h=this._borderRadius.bottomLeft?e.resolvedValue(this._borderRadius.bottomLeft):void 0;return t=void 0===s&&void 0===o&&void 0===h?{topLeft:i,topRight:i,bottomRight:i,bottomLeft:i}:{topLeft:i,topRight:s??0,bottomRight:o??0,bottomLeft:h??0},t}flipX(){return e.resolvedValue(this._flipX)??!1}flipY(){return e.resolvedValue(this._flipY)??!1}cropArea(){return e.resolvedValue(this._cropArea)??{}}boundingClientSize(){return this.size()}setImage(t){return this._image=t,this}setFit(t){return this._fit=t,this}setBorderRadius(t,i,e,s){return this._borderRadius={topLeftOrDefault:t,topRight:i,bottomRight:e,bottomLeft:s},this}setFlip(t){return this._flipX=t.horizontal??!1,this._flipY=t.vertical??!1,this}setCropArea(t){return this._cropArea=t,this}};export{g as Canvas,h as ColorType,C as EllipseElement,k as FontWeight,_ as GradientColor,L as HorizontalAlign,M as ImageElement,d as ImageFit,R as LineCap,T as LineElement,y as LinearGradient,P as PolygonElement,i as PositionPoint,z as RadialGradient,b as RectangleElement,u as Shape,f as SolidColor,A as TextElement,V as VerticalAlign};
//#region src/enums.ts
var ElementType = /* @__PURE__ */ function(ElementType) {
ElementType["Rectangle"] = "rectangle";
ElementType["Ellipse"] = "ellipse";
ElementType["Polygon"] = "polygon";
ElementType["Line"] = "line";
ElementType["Text"] = "text";
ElementType["Image"] = "image";
return ElementType;
}({});
var ColorType = /* @__PURE__ */ function(ColorType) {
ColorType["SolidColor"] = "solid-color";
ColorType["LinearGradient"] = "linear-gradient";
ColorType["RadialGradient"] = "radial-gradient";
return ColorType;
}({});
var FontWeight = /* @__PURE__ */ function(FontWeight) {
FontWeight["Normal"] = "normal";
FontWeight["Bold"] = "bold";
return FontWeight;
}({});
var HorizontalAlign = /* @__PURE__ */ function(HorizontalAlign) {
HorizontalAlign["Left"] = "left";
HorizontalAlign["Center"] = "center";
HorizontalAlign["Right"] = "right";
return HorizontalAlign;
}({});
var ImageFit = /* @__PURE__ */ function(ImageFit) {
ImageFit["Fill"] = "fill";
ImageFit["Cover"] = "cover";
return ImageFit;
}({});
var LineCap = /* @__PURE__ */ function(LineCap) {
LineCap["Round"] = "round";
LineCap["Square"] = "square";
return LineCap;
}({});
var PositionPoint = /* @__PURE__ */ function(PositionPoint) {
PositionPoint["LeftTop"] = "left-top";
PositionPoint["RightTop"] = "right-top";
PositionPoint["LeftBottom"] = "left-bottom";
PositionPoint["RightBottom"] = "right-bottom";
PositionPoint["Center"] = "center";
return PositionPoint;
}({});
var VerticalAlign = /* @__PURE__ */ function(VerticalAlign) {
VerticalAlign["Top"] = "top";
VerticalAlign["Center"] = "center";
VerticalAlign["Bottom"] = "bottom";
return VerticalAlign;
}({});
//#endregion
//#region src/libraries/helper/helper.ts
var Helper = class Helper {
static valueInRange(value, range) {
if (value < range.min) value = range.min;
if (value > range.max) value = range.max;
return value;
}
static valueNotGreaterThan(value, max) {
if (value > max) value = max;
return value;
}
static valueNotLessThan(value, min) {
if (value < min) value = min;
return value;
}
static relativePositionValue(value, size, maxValue) {
const extractedValue = Helper.extractedValue(value);
let position;
switch (extractedValue.unit) {
case "%":
position = (maxValue - size) / 100 * extractedValue.value;
break;
default: position = Number(value);
}
return position;
}
static extractedValue(value) {
let result;
if (typeof value === "string") {
const match = value.match(/^(\d+)(\D+)$/) ?? [];
if (match.length > 2) result = {
value: parseFloat(match[1] ?? "0"),
unit: match[2] ?? "px"
};
else result = {
value: Number(value),
unit: "px"
};
} else result = {
value,
unit: "px"
};
return result;
}
static degreesToRadians(degrees) {
return degrees / 180 * Math.PI;
}
static distance(firstPoint, secondPoint) {
return Math.sqrt(Math.pow(Math.abs(secondPoint.x - firstPoint.x), 2) + Math.pow(Math.abs(secondPoint.y - firstPoint.y), 2));
}
static positionBetweenPoints(firstPoint, secondPoint, moveBy) {
const fraction = moveBy / Helper.distance(firstPoint, secondPoint);
return {
x: firstPoint.x + fraction * (secondPoint.x - firstPoint.x),
y: firstPoint.y + fraction * (secondPoint.y - firstPoint.y)
};
}
static shapeSize(points) {
const leftTopCorner = {
x: Math.min(...points.map((point) => point.x)),
y: Math.min(...points.map((point) => point.y))
};
const rightBottomCorner = {
x: Math.max(...points.map((point) => point.x)),
y: Math.max(...points.map((point) => point.y))
};
return {
width: Math.floor(rightBottomCorner.x - leftTopCorner.x),
height: Math.floor(rightBottomCorner.y - leftTopCorner.y)
};
}
static positionLeftTop(position, point, size) {
const positionLeftTop = {
x: position.x,
y: position.y
};
switch (point) {
case PositionPoint.RightTop:
positionLeftTop.x -= size.width;
break;
case PositionPoint.RightBottom:
positionLeftTop.x -= size.width;
positionLeftTop.y -= size.height;
break;
case PositionPoint.LeftBottom:
positionLeftTop.y -= size.height;
break;
case PositionPoint.Center:
positionLeftTop.x -= size.width / 2;
positionLeftTop.y -= size.height / 2;
break;
}
return positionLeftTop;
}
static positionCenter(position, point, size) {
const positionLeftTop = Helper.positionLeftTop(position, point, size);
positionLeftTop.x += size.width / 2;
positionLeftTop.y += size.height / 2;
return positionLeftTop;
}
static resizedSize(originalSize, desiredSize, keepRatio) {
const result = {
width: desiredSize.width,
height: desiredSize.height
};
if (keepRatio) {
const selectedVariant = [{
width: originalSize.width * (desiredSize.height / originalSize.height),
height: desiredSize.height
}, {
width: desiredSize.width,
height: originalSize.height * (desiredSize.width / originalSize.width)
}].find((variant) => {
return variant.width <= desiredSize.width && variant.height <= desiredSize.height;
});
if (selectedVariant) {
result.width = selectedVariant.width;
result.height = selectedVariant.height;
}
}
return result;
}
static resolvedValue(attribute) {
let result;
if (attribute) if (typeof attribute === "function") result = attribute();
else result = attribute;
else result = attribute;
return result;
}
static isObject(value) {
return typeof value === "object" && value.constructor === Object && !Array.isArray(value);
}
static hasMethod(object, methodName) {
return !!object[methodName] && typeof object[methodName] === "function";
}
static hash(attributes) {
return attributes.map((attribute) => {
let result;
if (attribute === void 0 || attribute === null) result = "undefined";
else if (Helper.isObject(attribute)) result = JSON.stringify(attribute);
else if (Array.isArray(attribute)) result = "[" + Helper.hash(attribute) + "]";
else if (Helper.hasMethod(attribute, "hash")) result = attribute.hash();
else result = attribute.toString();
return result;
}).join("|");
}
};
//#endregion
//#region src/libraries/artists/element-artist.ts
var ElementArtist = class {
ctx;
element;
relativeProps;
defaultBorderColor = "#000000";
constructor(ctx, element, relativeProps) {
this.ctx = ctx;
this.element = element;
this.relativeProps = relativeProps ?? {};
}
relativePosition() {
return this.relativeProps.position ?? {
x: 0,
y: 0
};
}
relativeScale() {
return this.relativeProps.scale ?? {
x: 1,
y: 1
};
}
rotations() {
return [...this.relativeProps.rotations ?? [], ...this.element.rotations()];
}
relativeOpacity() {
return this.relativeProps.opacity ?? 1;
}
scale() {
const elementScale = this.element.scale();
const relativeScale = this.relativeScale();
return {
x: elementScale.x * relativeScale.x,
y: elementScale.y * relativeScale.y
};
}
/** @deprecated Use Rotations instead */
degrees() {
return this.element.degrees() ?? 0;
}
/** @deprecated Use Rotations instead */
anchor() {
return this.element.anchor() ?? this.defaultAnchor();
}
opacity() {
return this.element.opacity() * this.relativeOpacity();
}
startDrawing() {
this.ctx.save();
const scale = this.scale();
const opacity = this.opacity();
const degrees = this.degrees();
const rotations = this.rotations();
const shadow = this.element.shadow();
if (scale.x !== 1 || scale.y !== 1) this.ctx.scale(scale.x, scale.y);
if (opacity < 1) this.ctx.globalAlpha = opacity;
if (degrees !== 0) this.rotate({
degrees,
pivot: this.anchor()
});
if (rotations.length > 0) rotations.forEach((rotation) => {
if (rotation.degrees !== 0) this.rotate(rotation);
});
if (shadow && shadow.blur > 0) {
this.ctx.shadowColor = shadow.color;
this.ctx.shadowBlur = shadow.blur;
this.ctx.shadowOffsetX = shadow.x;
this.ctx.shadowOffsetY = shadow.y;
}
}
endDrawing() {
this.ctx.restore();
}
rotate(rotation) {
const position = this.position();
const pivot = rotation.pivot ?? this.defaultAnchor();
const absolutePivot = {
x: pivot.x + position.x,
y: pivot.y + position.y
};
this.ctx.translate(absolutePivot.x, absolutePivot.y);
this.ctx.rotate(Helper.degreesToRadians(rotation.degrees));
this.ctx.translate(-absolutePivot.x, -absolutePivot.y);
}
};
//#endregion
//#region src/libraries/shapes/shaper.ts
var Shaper = class {
_originalSize;
_sizeRatio;
_shapePosition;
constructor(ctx, shape) {
this.ctx = ctx;
this.shape = shape;
}
draw() {
this.prepare();
this.ctx.save();
this.rotateContext();
if (this.shape.isCloseShape()) this.makeClosedShape();
else this.makeOpenedShape();
this.ctx.restore();
}
prepare() {
this._originalSize = Helper.shapeSize(this.shape.points());
this._sizeRatio = this.sizeRatio();
this._shapePosition = this.shapePosition();
}
rotateContext() {
const degrees = this.shape.degrees();
if (degrees !== 0) this.rotate({ degrees });
this.shape.rotations().forEach((rotation) => {
this.rotate(rotation);
});
}
makeClosedShape() {
const points = this.shape.points();
if (points.length > 2) points.forEach((point, index) => {
const isFirstPoint = index === 0;
const isLastPoint = index === points.length - 1;
const nextPoint = points[index + 1] ?? points[0];
const lineLength = Helper.distance(point, nextPoint);
const currentRadius = this.radius(lineLength);
const currentStart = Helper.positionBetweenPoints(point, nextPoint, currentRadius);
const currentEnd = Helper.positionBetweenPoints(point, nextPoint, lineLength - currentRadius);
if (isFirstPoint) this.moveToPoint(currentStart);
this.lineToPoint(currentEnd);
if (currentRadius > 0) {
const afterNextPoint = points[index + 2] ?? (isLastPoint ? points[1] : points[0]);
this.quadraticCurveToPoint(nextPoint, afterNextPoint);
}
});
}
makeOpenedShape() {
const points = this.shape.points();
if (points.length > 1) points.forEach((point, index) => {
const isFirstPoint = index === 0;
const isLastPoint = index === points.length - 1;
const isBeforeLastPoint = index === points.length - 2;
if (!isLastPoint) {
const nextPoint = points[index + 1];
const currentLineLength = nextPoint ? Helper.distance(point, nextPoint) : 0;
const currentRadius = this.radius(currentLineLength);
const currentStart = isFirstPoint ? point : Helper.positionBetweenPoints(point, nextPoint, currentRadius);
const currentEnd = isBeforeLastPoint ? nextPoint : Helper.positionBetweenPoints(point, nextPoint, currentLineLength - currentRadius);
if (isFirstPoint) this.moveToPoint(currentStart);
this.lineToPoint(currentEnd);
if (!isBeforeLastPoint && currentRadius > 0) this.quadraticCurveToPoint(nextPoint, points[index + 2]);
}
});
}
radius(lineLength) {
const averageRatio = Math.min(this._sizeRatio.width, this._sizeRatio.height);
const radius = Math.floor(this.shape.jointsRadius() / averageRatio);
const maxRadius = Math.floor(lineLength * .55);
return radius > maxRadius ? maxRadius : radius;
}
moveToPoint(point) {
this.ctx.moveTo(this._shapePosition.x + point.x * this._sizeRatio.width, this._shapePosition.y + point.y * this._sizeRatio.height);
}
lineToPoint(point) {
this.ctx.lineTo(this._shapePosition.x + point.x * this._sizeRatio.width, this._shapePosition.y + point.y * this._sizeRatio.height);
}
quadraticCurveToPoint(point, nextPoint) {
const nextLineLength = Helper.distance(point, nextPoint);
const nextRadius = this.radius(nextLineLength);
const nextStart = Helper.positionBetweenPoints(point, nextPoint, nextRadius);
this.ctx.quadraticCurveTo(this._shapePosition.x + point.x * this._sizeRatio.width, this._shapePosition.y + point.y * this._sizeRatio.height, this._shapePosition.x + nextStart.x * this._sizeRatio.width, this._shapePosition.y + nextStart.y * this._sizeRatio.height);
}
shapePosition() {
return Helper.positionLeftTop(this.shape.position(), this.shape.positionPoint(), this.shapeSize());
}
shapeSize() {
return this.shape.size() ?? this._originalSize;
}
sizeRatio() {
const size = this.shapeSize();
return {
width: size.width / Helper.valueNotLessThan(this._originalSize.width, 1),
height: size.height / Helper.valueNotLessThan(this._originalSize.height, 1)
};
}
rotate(rotation) {
const position = this.shapePosition();
const anchor = rotation.pivot ?? this.defaultAnchor();
anchor.x += position.x;
anchor.y += position.y;
this.ctx.translate(anchor.x, anchor.y);
this.ctx.rotate(Helper.degreesToRadians(rotation.degrees));
this.ctx.translate(-anchor.x, -anchor.y);
}
defaultAnchor() {
const size = this.shapeSize();
return {
x: size.width / 2,
y: size.height / 2
};
}
};
//#endregion
//#region src/libraries/colors/color-mixer.ts
var ColorMixer = class {
constructor(ctx, elementPosition, elementSize) {
this.ctx = ctx;
this.elementPosition = elementPosition;
this.elementSize = elementSize;
}
mixed(color) {
let result = "";
if (typeof color === "string") result = color;
else switch (color.type()) {
case ColorType.SolidColor:
result = color.color();
break;
case ColorType.LinearGradient:
result = this.linearGradient(color);
break;
case ColorType.RadialGradient:
result = this.radialGradient(color);
break;
}
return result;
}
linearGradient(gradient) {
const angleRadians = Helper.degreesToRadians(gradient.degrees());
const centerPosition = {
x: this.elementPosition.x + this.elementSize.width / 2,
y: this.elementPosition.y + this.elementSize.height / 2
};
const halfLength = Math.sqrt(this.elementSize.width ** 2 + this.elementSize.height ** 2) / 2;
const offset = {
x: Math.cos(angleRadians) * halfLength,
y: Math.sin(angleRadians) * halfLength
};
const startPosition = {
x: centerPosition.x - offset.x,
y: centerPosition.y - offset.y
};
const endPosition = {
x: centerPosition.x + offset.x,
y: centerPosition.y + offset.y
};
const canvasGradient = this.ctx.createLinearGradient(startPosition.x, startPosition.y, endPosition.x, endPosition.y);
this.appendGradientColors(canvasGradient, gradient.colors());
return canvasGradient;
}
radialGradient(gradient) {
const localPosition = gradient.position() ?? {
x: this.elementSize.width / 2,
y: this.elementSize.height / 2
};
const centerPosition = {
x: this.elementPosition.x + localPosition.x,
y: this.elementPosition.y + localPosition.y
};
const radius = Math.sqrt(this.elementSize.width * this.elementSize.width + this.elementSize.height * this.elementSize.height) / 2;
const canvasGradient = this.ctx.createRadialGradient(centerPosition.x, centerPosition.y, 0, centerPosition.x, centerPosition.y, radius * gradient.radiusScale());
this.appendGradientColors(canvasGradient, gradient.colors());
return canvasGradient;
}
appendGradientColors(gradient, colors) {
colors.forEach((color) => gradient.addColorStop(color.offset(), color.color()));
}
};
//#endregion
//#region src/libraries/artists/rectangle-artist.ts
var RectangleArtist = class extends ElementArtist {
draw() {
this.startDrawing();
this.makeShape();
this.drawFill();
this.drawBorder();
this.endDrawing();
}
makeShape() {
const borderWidth = this.element.borderWidth();
const position = this.position();
const size = this.element.size();
const shapes = this.element.cutOutShapes();
const drawPosition = {
x: position.x + borderWidth / 2,
y: position.y + borderWidth / 2
};
const drawSize = {
width: size.width - borderWidth,
height: size.height - borderWidth
};
this.ctx.beginPath();
if (shapes.length > 0) {
this.ctx.translate(drawPosition.x, drawPosition.y);
shapes.forEach((shape) => {
new Shaper(this.ctx, shape).draw();
});
this.ctx.translate(-drawPosition.x, -drawPosition.y);
drawPosition.x += drawSize.width;
drawSize.width *= -1;
}
this.ctx.rect(drawPosition.x, drawPosition.y, drawSize.width, drawSize.height);
this.ctx.closePath();
}
drawFill() {
const color = this.element.fillColor();
if (color) {
this.ctx.fillStyle = new ColorMixer(this.ctx, this.position(), this.element.size()).mixed(color);
this.ctx.fill();
}
}
drawBorder() {
const borderWidth = this.element.borderWidth();
if (borderWidth > 0) {
this.ctx.strokeStyle = new ColorMixer(this.ctx, this.position(), this.element.size()).mixed(this.element.borderColor() ?? this.defaultBorderColor);
this.ctx.lineWidth = borderWidth;
this.ctx.stroke();
}
}
position() {
const elementPosition = this.element.positionLeftTop();
const relativePosition = this.relativePosition();
return {
x: elementPosition.x + relativePosition.x,
y: elementPosition.y + relativePosition.y
};
}
defaultAnchor() {
const size = this.element.size();
return {
x: size.width / 2,
y: size.height / 2
};
}
};
//#endregion
//#region src/libraries/artists/ellipse-artist.ts
var EllipseArtist = class extends ElementArtist {
draw() {
this.startDrawing();
this.drawFill();
this.drawBorder();
this.endDrawing();
}
drawFill() {
const color = this.element.fillColor();
if (color) {
const borderWidth = this.element.borderWidth();
const position = this.position();
const size = this.element.size();
this.ctx.beginPath();
this.ctx.ellipse(position.x, position.y, Helper.valueNotLessThan(size.width / 2 - borderWidth, 1), Helper.valueNotLessThan(size.height / 2 - borderWidth, 1), 0, 0, Math.PI * 2);
this.ctx.closePath();
this.ctx.fillStyle = new ColorMixer(this.ctx, this.element.positionLeftTop(), this.element.size()).mixed(color);
this.ctx.fill();
}
}
drawBorder() {
const borderWidth = this.element.borderWidth();
if (borderWidth > 0) {
const position = this.position();
const size = this.element.size();
this.ctx.beginPath();
this.ctx.ellipse(position.x, position.y, Helper.valueNotLessThan(size.width / 2 - borderWidth / 2, 1), Helper.valueNotLessThan(size.height / 2 - borderWidth / 2, 1), 0, 0, Math.PI * 2);
this.ctx.closePath();
this.ctx.strokeStyle = new ColorMixer(this.ctx, this.element.positionLeftTop(), this.element.size()).mixed(this.element.borderColor() ?? this.defaultBorderColor);
this.ctx.lineWidth = borderWidth;
this.ctx.stroke();
}
}
position() {
const elementPosition = Helper.positionCenter(this.element.position(), this.element.positionPoint(), this.element.boundingClientSize());
const relativePosition = this.relativePosition();
return {
x: elementPosition.x + relativePosition.x,
y: elementPosition.y + relativePosition.y
};
}
defaultAnchor() {
return {
x: 0,
y: 0
};
}
};
//#endregion
//#region src/libraries/artists/text-artist.ts
var TextArtist = class extends ElementArtist {
draw() {
this.prepare();
this.startDrawing();
this.drawStroke();
this.drawFill();
this.endDrawing();
}
prepare() {
this.ctx.font = this.element.font();
this.ctx.textBaseline = "alphabetic";
}
drawFill() {
const color = this.element.fillColor();
if (color) {
const textBox = this.element.textBox();
const position = this.position();
this.ctx.fillStyle = new ColorMixer(this.ctx, this.position(), textBox.size).mixed(color);
textBox.lines.forEach((textLine) => {
this.ctx.fillText(textLine.text, position.x + textLine.position.x, Math.round(position.y + textLine.position.y + textLine.size.height * .75));
});
}
}
drawStroke() {
const width = this.element.strokeWidth();
if (width > 0) {
const textBox = this.element.textBox();
const position = this.position();
const fillColor = this.element.fillColor();
this.ctx.strokeStyle = new ColorMixer(this.ctx, this.position(), textBox.size).mixed(this.element.strokeColor() ?? this.defaultBorderColor);
this.ctx.lineWidth = fillColor ? width * 2 : width;
textBox.lines.forEach((textLine) => {
this.ctx.strokeText(textLine.text, position.x + textLine.position.x, Math.round(position.y + textLine.position.y + textLine.size.height * .75));
});
}
}
position() {
const elementPosition = this.element.positionLeftTop();
const relativePosition = this.relativePosition();
return {
x: elementPosition.x + relativePosition.x,
y: elementPosition.y + relativePosition.y
};
}
defaultAnchor() {
const textBox = this.element.textBox();
return {
x: textBox.size.width / 2,
y: textBox.size.height / 2
};
}
};
//#endregion
//#region src/libraries/artists/image-artist.ts
var ImageArtist = class extends ElementArtist {
draw() {
this.startDrawing();
this.drawBorder();
this.drawImage();
this.endDrawing();
}
drawImage() {
const image = this.element.image();
if (image) {
const source = this.sourceDimensions();
const destination = this.flippedDimensions(this.elementDimensions());
this.ctx.drawImage(image, source.position.x, source.position.y, source.size.width, source.size.height, destination.position.x, destination.position.y, destination.size.width, destination.size.height);
}
}
drawBorder() {
const width = this.element.borderWidth();
const color = new ColorMixer(this.ctx, this.position(), this.element.size()).mixed(this.element.borderColor() ?? this.defaultBorderColor);
const dimensions = this.elementDimensions();
this.ctx.strokeStyle = color;
this.ctx.lineWidth = width * 2;
if (this.isRadiusSet()) {
this.makeShape(dimensions);
this.ctx.lineJoin = "round";
this.ctx.fillStyle = "#ffffff";
this.ctx.fill();
if (width > 0) {
this.ctx.strokeStyle = color;
this.ctx.lineWidth = width * 2;
this.ctx.stroke();
}
this.ctx.clip();
} else if (width > 0) {
this.ctx.strokeStyle = color;
this.ctx.lineWidth = width * 2;
this.ctx.strokeRect(dimensions.position.x, dimensions.position.y, dimensions.size.width, dimensions.size.height);
}
}
sourceDimensions() {
const image = this.element.image();
const cropArea = this.element.cropArea();
const result = {
position: {
x: 0,
y: 0
},
size: cropArea.size ?? {
width: image.width,
height: image.height
}
};
switch (this.element.fit()) {
case ImageFit.Fill:
if (cropArea.position) {
result.position.x = cropArea.position.x;
result.position.y = cropArea.position.y;
}
break;
case ImageFit.Cover: {
const { size } = this.elementDimensions();
if (size.width / size.height < result.size.width / result.size.height) result.size.width = result.size.height / (size.height / size.width);
else result.size.height = result.size.width / (size.width / size.height);
if (!cropArea.position) {
result.position.x = Helper.relativePositionValue("50%", result.size.width, image.width);
result.position.y = Helper.relativePositionValue("50%", result.size.height, image.height);
} else {
result.position.x = cropArea.position.x;
result.position.y = cropArea.position.y;
}
break;
}
}
return result;
}
elementDimensions() {
const borderWidth = this.element.borderWidth();
const position = this.position();
const size = this.element.size();
return {
position: {
x: position.x + borderWidth,
y: position.y + borderWidth
},
size: {
width: size.width - borderWidth * 2,
height: size.height - borderWidth * 2
}
};
}
flippedDimensions(dimensions) {
const flipX = this.element.flipX();
const flipY = this.element.flipY();
if (flipX || flipY) {
let scaleX = 1;
let scaleY = 1;
if (flipX) {
scaleX = -1;
dimensions.position.x = -(+dimensions.position.x + dimensions.size.width);
}
if (flipY) {
scaleY = -1;
dimensions.position.y = -(+dimensions.position.y + dimensions.size.height);
}
this.ctx.scale(scaleX, scaleY);
}
return dimensions;
}
makeShape(dimensions) {
const radius = this.borderRadius();
this.ctx.lineJoin = "round";
this.ctx.beginPath();
this.ctx.moveTo(dimensions.position.x + radius.topLeft, dimensions.position.y);
this.ctx.lineTo(dimensions.position.x + dimensions.size.width - radius.topRight, dimensions.position.y);
this.ctx.quadraticCurveTo(dimensions.position.x + dimensions.size.width, dimensions.position.y, dimensions.position.x + dimensions.size.width, dimensions.position.y + radius.topRight);
this.ctx.lineTo(dimensions.position.x + dimensions.size.width, dimensions.position.y + dimensions.size.height - radius.bottomRight);
this.ctx.quadraticCurveTo(dimensions.position.x + dimensions.size.width, dimensions.position.y + dimensions.size.height, dimensions.position.x + dimensions.size.width - radius.bottomRight, dimensions.position.y + dimensions.size.height);
this.ctx.lineTo(dimensions.position.x + radius.bottomLeft, dimensions.position.y + dimensions.size.height);
this.ctx.quadraticCurveTo(dimensions.position.x, dimensions.position.y + dimensions.size.height, dimensions.position.x, dimensions.position.y + dimensions.size.height - radius.bottomLeft);
this.ctx.lineTo(dimensions.position.x, dimensions.position.y + radius.topLeft);
this.ctx.quadraticCurveTo(dimensions.position.x, dimensions.position.y, dimensions.position.x + radius.topLeft, dimensions.position.y);
this.ctx.closePath();
}
borderRadius() {
const radius = this.element.borderRadius();
if (this.isRadiusSet()) {
const { size } = this.elementDimensions();
Object.keys(radius).forEach((key) => {
if (radius[key] > size.width / 2) radius[key] = Math.floor(size.width / 2);
if (radius[key] > size.height / 2) radius[key] = Math.floor(size.height / 2);
});
}
return radius;
}
isRadiusSet() {
const radius = this.element.borderRadius();
return radius.topLeft > 0 || radius.topRight > 0 || radius.bottomLeft > 0 || radius.bottomRight > 0;
}
position() {
const elementPosition = this.element.positionLeftTop();
const relativePosition = this.relativePosition();
return {
x: elementPosition.x + relativePosition.x,
y: elementPosition.y + relativePosition.y
};
}
defaultAnchor() {
const size = this.element.size();
return {
x: size.width / 2,
y: size.height / 2
};
}
};
//#endregion
//#region src/libraries/shapes/shape.ts
var Shape = class {
_points = [];
_isCloseShape;
_position = {
x: 0,
y: 0
};
_positionPoint = PositionPoint.LeftTop;
_size;
_jointsRadius = 0;
_degrees = 0;
_anchor;
_rotations = [];
constructor(points, isCloseShape = true) {
this._points = points;
this._isCloseShape = isCloseShape;
}
hash() {
return Helper.hash([
this.points(),
this.isCloseShape(),
this.position(),
this.positionPoint(),
this.size(),
this.jointsRadius(),
this.rotations(),
this.degrees(),
this.anchor()
]);
}
points() {
return this._points;
}
isCloseShape() {
return this._isCloseShape;
}
position() {
return this._position;
}
positionPoint() {
return this._positionPoint;
}
size() {
return this._size;
}
jointsRadius() {
return this._jointsRadius;
}
/** @deprecated Use Rotations instead */
degrees() {
return this._degrees;
}
/** @deprecated Use Rotations instead */
anchor() {
return this._anchor;
}
rotations() {
return this._rotations;
}
setPosition(position, point) {
this._position = position;
if (point) this._positionPoint = point;
return this;
}
setSize(value) {
this._size = value;
return this;
}
setJointsRadius(value) {
this._jointsRadius = value;
return this;
}
/** @deprecated Use Rotations instead */
setDegrees(degrees, anchor) {
this._degrees = degrees;
this._anchor = anchor;
return this;
}
setRotations(rotations) {
this._rotations = rotations;
return this;
}
};
//#endregion
//#region src/libraries/artists/polygon-artist.ts
var PolygonArtist = class extends ElementArtist {
draw() {
this.startDrawing();
this.makeShape();
this.drawBorder();
this.drawFill();
this.endDrawing();
}
makeShape() {
const points = this.element.shapePoints();
if (points.length > 2) {
const shape = new Shape(points).setSize(this.shapeSize()).setPosition(this.shapePosition()).setJointsRadius(this.element.borderRadius());
this.ctx.beginPath();
new Shaper(this.ctx, shape).draw();
this.ctx.closePath();
}
}
drawFill() {
const color = this.element.fillColor();
if (color) {
this.ctx.fillStyle = new ColorMixer(this.ctx, this.position(), this.shapeSize()).mixed(color);
this.ctx.fill();
}
}
drawBorder() {
const width = this.element.borderWidth();
if (width > 0) {
const color = new ColorMixer(this.ctx, this.position(), this.element.size()).mixed(this.element.borderColor() ?? this.defaultBorderColor);
const fillColor = this.element.fillColor();
if (this.element.borderRadius() > 0) this.ctx.lineJoin = "round";
this.ctx.strokeStyle = color;
this.ctx.lineWidth = fillColor ? width * 2 : width;
this.ctx.stroke();
}
}
shapePosition() {
const position = this.position();
const borderWidth = this.element.borderWidth();
return {
x: position.x + borderWidth,
y: position.y + borderWidth
};
}
shapeSize() {
const size = this.element.size();
const borderWidth = this.element.borderWidth();
return {
width: size.width - borderWidth * 2,
height: size.height - borderWidth * 2
};
}
position() {
const elementPosition = this.element.positionLeftTop();
const relativePosition = this.relativePosition();
return {
x: elementPosition.x + relativePosition.x,
y: elementPosition.y + relativePosition.y
};
}
defaultAnchor() {
const size = this.element.size();
return {
x: size.width / 2,
y: size.height / 2
};
}
};
//#endregion
//#region src/libraries/artists/line-artist.ts
var LineArtist = class extends ElementArtist {
draw() {
this.startDrawing();
this.makeShape();
this.drawStroke();
this.endDrawing();
}
makeShape() {
this.ctx.beginPath();
const shape = new Shape(this.element.shapePoints(), false).setSize(this.shapeSize()).setPosition(this.shapePosition()).setJointsRadius(this.element.jointsRadius());
new Shaper(this.ctx, shape).draw();
}
drawStroke() {
const color = new ColorMixer(this.ctx, this.position(), this.element.boundingClientSize()).mixed(this.element.color() ?? this.defaultBorderColor);
this.ctx.lineCap = this.element.lineCap();
this.ctx.strokeStyle = color;
this.ctx.lineWidth = this.element.calculatedThickness();
this.ctx.stroke();
}
shapeSize() {
const size = this.element.boundingClientSize();
const thickness = this.element.calculatedThickness();
return {
width: size.width - thickness,
height: size.height - thickness / 2
};
}
shapePosition() {
const position = this.position();
const thickness = this.element.calculatedThickness();
return {
x: position.x + thickness / 2,
y: position.y
};
}
position() {
const elementPosition = this.element.positionLeftTop();
const relativePosition = this.relativePosition();
return {
x: elementPosition.x + relativePosition.x,
y: elementPosition.y + relativePosition.y
};
}
defaultAnchor() {
const size = this.shapeSize();
return {
x: size.width / 2,
y: size.height / 2
};
}
};
//#endregion
//#region src/libraries/canvas.ts
var Canvas = class {
_canvas;
_ctx;
constructor(canvas) {
this._canvas = canvas ?? document.createElement("canvas");
this._ctx = this._canvas.getContext("2d");
}
nativeElement() {
return this._canvas;
}
ctx() {
return this._ctx;
}
size() {
return {
width: this._canvas.width,
height: this._canvas.height
};
}
setSize(size) {
this._canvas.width = size.width;
this._canvas.height = size.height;
}
draw(element, relativeProps) {
switch (element.type()) {
case ElementType.Rectangle:
new RectangleArtist(this.ctx(), element, relativeProps).draw();
break;
case ElementType.Ellipse:
new EllipseArtist(this.ctx(), element, relativeProps).draw();
break;
case ElementType.Polygon:
new PolygonArtist(this.ctx(), element, relativeProps).draw();
break;
case ElementType.Line:
new LineArtist(this.ctx(), element, relativeProps).draw();
break;
case ElementType.Text:
new TextArtist(this.ctx(), element, relativeProps).draw();
break;
case ElementType.Image:
new ImageArtist(this.ctx(), element, relativeProps).draw();
break;
}
}
clear() {
this.ctx().clearRect(0, 0, this._canvas.width, this._canvas.height);
}
};
//#endregion
//#region src/libraries/elements/element.ts
var Element = class {
_position = {
x: 0,
y: 0
};
_positionPoint;
_scale = {
x: 1,
y: 1
};
_opacity = 1;
_rotations = [];
_degrees = 0;
_anchor;
_shadow;
hash() {
return Helper.hash([
this.position(),
this.positionPoint(),
this.scale(),
this.opacity(),
this.rotations(),
this.degrees(),
this.anchor(),
this.shadow()
]);
}
position() {
return Helper.resolvedValue(this._position);
}
positionLeftTop() {
return Helper.positionLeftTop(this.position(), this.positionPoint(), this.boundingClientSize());
}
positionPoint() {
return Helper.resolvedValue(this._positionPoint) ?? PositionPoint.LeftTop;
}
scale() {
return Helper.resolvedValue(this._scale);
}
opacity() {
return Helper.resolvedValue(this._opacity);
}
rotations() {
return Helper.resolvedValue(this._rotations);
}
/** @deprecated Use Rotations instead */
degrees() {
return Helper.resolvedValue(this._degrees);
}
/** @deprecated Use Rotations instead */
anchor() {
return this._anchor ? Helper.resolvedValue(this._anchor) : void 0;
}
shadow() {
return this._shadow ? Helper.resolvedValue(this._shadow) : void 0;
}
setPosition(position, point) {
this._position = position;
if (point) this._positionPoint = point;
return this;
}
setScale(value) {
this._scale = value;
return this;
}
setOpacity(value) {
this._opacity = value;
return this;
}
/** @deprecated Use Rotations instead */
setDegrees(degrees, anchor) {
this._degrees = degrees;
this._anchor = anchor;
return this;
}
setRotations(rotations) {
this._rotations = rotations;
return this;
}
setShadow(shadow) {
this._shadow = shadow;
return this;
}
};
//#endregion
//#region src/libraries/elements/geometric-element.ts
var GeometricElement = class extends Element {
_size = {
width: 1,
height: 1
};
_borderColor;
_borderWidth = 0;
hash() {
return Helper.hash([
super.hash(),
this.size(),
this.borderColor(),
this.borderWidth()
]);
}
size() {
return Helper.resolvedValue(this._size);
}
borderColor() {
return this._borderColor ? Helper.resolvedValue(this._borderColor) : void 0;
}
borderWidth() {
return Helper.resolvedValue(this._borderWidth);
}
setSize(value) {
this._size = value;
return this;
}
setBorder(width, color) {
this._borderWidth = width;
this._borderColor = color;
return this;
}
};
//#endregion
//#region src/libraries/elements/rectangle-element.ts
var RectangleElement = class extends GeometricElement {
_fillColor;
_cutOutShapes = [];
type() {
return ElementType.Rectangle;
}
hash() {
return Helper.hash([
super.hash(),
this.fillColor(),
this.cutOutShapes()
]);
}
fillColor() {
return this._fillColor ? Helper.resolvedValue(this._fillColor) : void 0;
}
cutOutShapes() {
return Helper.resolvedValue(this._cutOutShapes);
}
boundingClientSize() {
return this.size();
}
setFillColor(value) {
this._fillColor = value;
return this;
}
setCutOutShapes(value) {
this._cutOutShapes = value;
return this;
}
};
//#endregion
//#region src/libraries/elements/ellipse-element.ts
var EllipseElement = class extends GeometricElement {
_fillColor;
type() {
return ElementType.Ellipse;
}
hash() {
return Helper.hash([super.hash(), this.fillColor()]);
}
fillColor() {
return this._fillColor ? Helper.resolvedValue(this._fillColor) : void 0;
}
boundingClientSize() {
return this.size();
}
setFillColor(value) {
this._fillColor = value;
return this;
}
};
//#endregion
//#region src/libraries/elements/polygon-element.ts
var PolygonElement = class extends GeometricElement {
_shapePoints = [];
_borderRadius = 0;
_fillColor;
type() {
return ElementType.Polygon;
}
hash() {
return Helper.hash([
super.hash(),
this.shapePoints(),
this.borderRadius(),
this.fillColor()
]);
}
shapePoints() {
return Helper.resolvedValue(this._shapePoints);
}
borderRadius() {
return Helper.resolvedValue(this._borderRadius);
}
fillColor() {
return this._fillColor ? Helper.resolvedValue(this._fillColor) : void 0;
}
boundingClientSize() {
return this.size();
}
setShapePoints(value) {
this._shapePoints = value;
return this;
}
setBorderRadius(value) {
this._borderRadius = value;
return this;
}
setFillColor(value) {
this._fillColor = value;
return this;
}
};
//#endregion
//#region src/libraries/elements/line-element.ts
var LineElement = class extends Element {
_shapePoints = [];
_size;
_keepRatio;
_thickness = 1;
_jointsRadius = 0;
_lineCap = LineCap.Square;
_color;
type() {
return ElementType.Line;
}
hash() {
return Helper.hash([
super.hash(),
this.shapePoints(),
this.size(),
this.keepRatio(),
this.thickness(),
this.jointsRadius(),
this.lineCap(),
this.color()
]);
}
shapePoints() {
return Helper.resolvedValue(this._shapePoints);
}
size() {
return this._size ? Helper.resolvedValue(this._size) : void 0;
}
keepRatio() {
return Helper.resolvedValue(this._keepRatio) ?? false;
}
thickness() {
return Helper.resolvedValue(this._thickness);
}
calculatedThickness() {
const size = this.size();
let thickness = this.thickness();
if (size) {
const originalSize = Helper.shapeSize(this.shapePoints());
const resized = Helper.resizedSize(originalSize, size, this.keepRatio());
const ratioWidth = resized.width / Helper.valueNotLessThan(originalSize.width, 1);
const ratioHeight = resized.height / Helper.valueNotLessThan(originalSize.height, 1);
thickness = Helper.valueNotLessThan(thickness * Math.min(ratioWidth, ratioHeight), 1);
}
return thickness;
}
jointsRadius() {
return Helper.resolvedValue(this._jointsRadius);
}
lineCap() {
return Helper.resolvedValue(this._lineCap);
}
color() {
return this._color ? Helper.resolvedValue(this._color) : void 0;
}
boundingClientSize() {
let size = this.size();
const thickness = this.calculatedThickness();
const originalSize = Helper.shapeSize(this.shapePoints());
if (size) {
const resized = Helper.resizedSize(originalSize, size, this.keepRatio());
size = {
width: resized.width,
height: resized.height + thickness / 2
};
} else size = {
width: originalSize.width,
height: Helper.valueNotLessThan(originalSize.height + thickness / 2, 0)
};
return size;
}
setShapePoints(value) {
this._shapePoints = value;
return this;
}
setSize(value, keepRatio) {
this._size = value;
if (keepRatio) this._keepRatio = keepRatio;
return this;
}
setThickness(value) {
this._thickness = value;
return this;
}
setJointsRadius(value) {
this._jointsRadius = value;
return this;
}
setLineCap(value) {
this._lineCap = value;
return this;
}
setColor(value) {
this._color = value;
return this;
}
};
//#endregion
//#region src/libraries/texts/text-splitter.ts
var TextSplitter = class {
element;
canvas;
ctx;
lastTextBox;
lastTextBoxHash = "";
constructor(element) {
this.element = element;
this.canvas = document.createElement("canvas");
this.ctx = this.canvas.getContext("2d");
}
textBox() {
if (this.elementChanged()) this.lastTextBox = void 0;
if (!this.lastTextBox) this.lastTextBox = this.createTextBox();
return this.lastTextBox;
}
createTextBox() {
const result = {
text: this.element.text(),
lines: [],
size: {
width: 1,
height: 1
},
position: {
x: 0,
y: 0
}
};
result.lines = this.textLines();
result.size = this.textBoxSize(result.lines);
result.position = this.textBoxPosition(result.lines);
return result;
}
textLines() {
const bounds = this.element.bounds();
const padding = this.element.padding();
const boundsWithoutPadding = bounds ? {
width: bounds.width - padding.left - padding.right,
height: bounds.height - padding.top - padding.bottom
} : void 0;
const textLines = this.splitText().map((line) => {
return {
text: line,
size: {
width: this.textWidth(line),
height: this.element.lineHeight()
},
position: {
x: 0,
y: 0
}
};
}).map((line, index, transformed) => {
const previousLine = transformed[index - 1];
line.position = {
x: 0,
y: previousLine ? previousLine.position.y + previousLine.size.height : 0
};
return line;
});
return (boundsWithoutPadding !== void 0 ? this.alignedLines(textLines, boundsWithoutPadding) : textLines).map((line) => {
line.position.x += padding.left;
line.position.y += padding.top;
return line;
});
}
alignedLines(textLines, bounds) {
const horizontalAlign = this.element.horizontalAlign();
const verticalAlign = this.element.verticalAlign();
const totalLinesHeight = textLines.reduce((sum, line) => sum + line.size.height, 0);
return textLines.map((line) => {
if (horizontalAlign === HorizontalAlign.Center) line.position.x += Math.round((bounds.width - line.size.width) / 2);
if (horizontalAlign === HorizontalAlign.Right) line.position.x += bounds.width - line.size.width;
if (verticalAlign === VerticalAlign.Center) line.position.y += Math.round((bounds.height - totalLinesHeight) / 2);
if (verticalAlign === VerticalAlign.Bottom) line.position.y += bounds.height - totalLinesHeight;
return line;
});
}
textBoxSize(textLines) {
const padding = this.element.padding();
const bounds = this.element.bounds() ?? {
width: 1,
height: 1
};
const longestWidth = Math.max(...textLines.map((textLine) => textLine.size.width));
return {
width: Math.max(longestWidth + padding.left + padding.right, bounds.width),
height: Math.max(this.element.lineHeight() * textLines.length + padding.top + padding.bottom, bounds.height)
};
}
textBoxPosition(textLines) {
const padding = this.element.padding();
const smallestX = Math.min(...textLines.map((textLine) => textLine.position.x));
const smallestY = Math.min(...textLines.map((textLine) => textLine.position.y));
return {
x: smallestX - padding.left,
y: smallestY - padding.top
};
}
splitText() {
const lines = this.element.text().replaceAll("<BR>", "<br>").split("<br>");
const padding = this.element.padding();
const maxWidth = (this.element.bounds() ?? {
width: 999999,
height: 999999
}).width - (padding.left + padding.right);
let result = [];
lines.forEach((line) => {
line = line.trim();
const textWidth = this.textWidth(line);
if (textWidth > maxWidth) {
const splitEvery = Math.floor(maxWidth / (textWidth / line.length)) - 3;
const words = line.split(" ");
let tmp = "";
words.forEach((word, index) => {
if (tmp.length + word.length > splitEvery) {
result.push(tmp.trim());
tmp = "";
}
tmp += word + " ";
if (index === words.length - 1) result.push(tmp.trim());
});
result = result.map((line) => line.trim());
} else result.push(line);
});
return result;
}
textWidth(text) {
this.ctx.font = this.element.font();
return Math.ceil(this.ctx.measureText(text).width + this.element.strokeWidth());
}
elementChanged() {
const hash = [
this.element.text(),
this.element.bounds(),
this.element.horizontalAlign(),
this.element.verticalAlign(),
this.element.padding(),
this.element.lineHeight(),
this.element.font(),
this.element.strokeWidth()
].join("|");
const changed = this.lastTextBoxHash !== hash;
this.lastTextBoxHash = hash;
return changed;
}
};
//#endregion
//#region src/libraries/elements/text-element.ts
var TextElement = class extends Element {
_text = "";
_bounds;
_fontSize = 12;
_fontFamily = "Tahoma";
_fontWeight = FontWeight.Normal;
_lineHeight;
_fillColor;
_strokeColor;
_strokeWidth = 0;
_horizontalAlign = HorizontalAlign.Left;
_verticalAlign = VerticalAlign.Top;
_padding = { topOrVerticalOrDefault: 0 };
_textSplitter = new TextSplitter(this);
type() {
return ElementType.Text;
}
hash() {
return Helper.hash([
super.hash(),
this.text(),
this.bounds(),
this.font(),
this.lineHeight(),
this.fillColor(),
this.strokeColor(),
this.strokeWidth(),
this.horizontalAlign(),
this.verticalAlign(),
this.padding()
]);
}
text() {
return Helper.resolvedValue(this._text).toString();
}
bounds() {
return this._bounds ? Helper.resolvedValue(this._bounds) : void 0;
}
fontSize() {
return Helper.resolvedValue(this._fontSize);
}
fontFamily() {
return Helper.resolvedValue(this._fontFamily);
}
fontWeight() {
return Helper.resolvedValue(this._fontWeight);
}
lineHeight() {
return Helper.resolvedValue(this._lineHeight ?? Math.round(this.fontSize() * 1.2));
}
fillColor() {
return this._fillColor ? Helper.resolvedValue(this._fillColor) : void 0;
}
strokeColor() {
return this._strokeColor ? Helper.resolvedValue(this._strokeColor) : void 0;
}
strokeWidth() {
return Helper.resolvedValue(this._strokeWidth);
}
horizontalAlign() {
return Helper.resolvedValue(this._horizontalAlign);
}
verticalAlign() {
return Helper.resolvedValue(this._verticalAlign);
}
padding() {
let result;
const topOrVerticalOrDefault = Helper.resolvedValue(this._padding.topOrVerticalOrDefault);
const rightOrHorizontal = this._padding.rightOrHorizontal ? Helper.resolvedValue(this._padding.rightOrHorizontal) : void 0;
const bottom = this._padding.bottom ? Helper.resolvedValue(this._padding.bottom) : void 0;
const left = this._padding.left ? Helper.resolvedValue(this._padding.left) : void 0;
if (rightOrHorizontal === void 0) result = {
top: topOrVerticalOrDefault,
right: topOrVerticalOrDefault,
bottom: topOrVerticalOrDefault,
left: topOrVerticalOrDefault
};
else if (bottom === void 0 && left === void 0) result = {
top: topOrVerticalOrDefault,
right: rightOrHorizontal,
bottom: topOrVerticalOrDefault,
left: rightOrHorizontal
};
else result = {
top: topOrVerticalOrDefault,
right: rightOrHorizontal,
bottom: bottom ?? 0,
left: left ?? 0
};
return result;
}
font() {
const font = [this.fontSize() + "px", this.fontFamily()];
const weight = this.fontWeight();
if (weight) font.unshift(weight);
return font.join(" ");
}
textBox() {
return this._textSplitter.textBox();
}
boundingClientSize() {
return this.textBox().size;
}
setText(value) {
this._text = value;
return this;
}
setBounds(value) {
this._bounds = value;
return this;
}
setFontSize(value) {
this._fontSize = value;
return this;
}
setFontFamily(value) {
this._fontFamily = value;
return this;
}
setFontWeight(value) {
this._fontWeight = value;
return this;
}
setLineHeight(value) {
this._lineHeight = value;
return this;
}
setFillColor(value) {
this._fillColor = value;
return this;
}
setStroke(width, color) {
this._strokeWidth = width;
this._strokeColor = color;
return this;
}
setHorizontalAlign(value) {
this._horizontalAlign = value;
return this;
}
setVerticalAlign(value) {
this._verticalAlign = value;
return this;
}
setPadding(topOrVerticalOrDefault, rightOrHorizontal, bottom, left) {
this._padding = {
topOrVerticalOrDefault,
rightOrHorizontal,
bottom,
left
};
return this;
}
};
//#endregion
//#region src/libraries/elements/image-element.ts
var ImageElement = class extends GeometricElement {
_image;
_fit = ImageFit.Fill;
_borderRadius = { topLeftOrDefault: 0 };
_flipX;
_flipY;
_cropArea;
type() {
return ElementType.Image;
}
hash() {
return Helper.hash([
super.hash(),
this.image()?.id,
this.fit(),
this.borderRadius(),
this.flipX(),
this.flipY(),
this.cropArea()
]);
}
image() {
return this._image ? Helper.resolvedValue(this._image) : void 0;
}
fit() {
return Helper.resolvedValue(this._fit);
}
borderRadius() {
let result;
const topLeftOrDefault = Helper.resolvedValue(this._borderRadius.topLeftOrDefault);
const topRight = this._borderRadius.topRight ? Helper.resolvedValue(this._borderRadius.topRight) : void 0;
const bottomRight = this._borderRadius.bottomRight ? Helper.resolvedValue(this._borderRadius.bottomRight) : void 0;
const bottomLeft = this._borderRadius.bottomLeft ? Helper.resolvedValue(this._borderRadius.bottomLeft) : void 0;
if (topRight === void 0 && bottomRight === void 0 && bottomLeft === void 0) result = {
topLeft: topLeftOrDefault,
topRight: topLeftOrDefault,
bottomRight: topLeftOrDefault,
bottomLeft: topLeftOrDefault
};
else result = {
topLeft: topLeftOrDefault,
topRight: topRight ?? 0,
bottomRight: bottomRight ?? 0,
bottomLeft: bottomLeft ?? 0
};
return result;
}
flipX() {
return Helper.resolvedValue(this._flipX) ?? false;
}
flipY() {
return Helper.resolvedValue(this._flipY) ?? false;
}
cropArea() {
return Helper.resolvedValue(this._cropArea) ?? {};
}
boundingClientSize() {
return this.size();
}
setImage(value) {
this._image = value;
return this;
}
setFit(value) {
this._fit = value;
return this;
}
setBorderRadius(topLeftOrDefault, topRight, bottomRight, bottomLeft) {
this._borderRadius = {
topLeftOrDefault,
topRight,
bottomRight,
bottomLeft
};
return this;
}
setFlip(value) {
this._flipX = value.horizontal ?? false;
this._flipY = value.vertical ?? false;
return this;
}
setCropArea(value) {
this._cropArea = value;
return this;
}
};
//#endregion
//#region src/libraries/colors/color.ts
var Color = class {};
//#endregion
//#region src/libraries/colors/solid-color.ts
var SolidColor = class extends Color {
_color;
constructor(hexColor, opacity) {
super();
this.setColor(hexColor, opacity);
}
type() {
return ColorType.SolidColor;
}
color() {
return this._color;
}
hash() {
return Helper.hash([this.color()]);
}
setColor(hexColor, opacity) {
const alphaFromHex = hexColor.length === 9 ? hexColor.slice(-2) : void 0;
const alpha = opacity !== void 0 ? this.alphaToHex(opacity) : alphaFromHex ?? "ff";
this._color = hexColor.slice(0, 7) + alpha;
return this;
}
alphaToHex(alpha) {
let hex = Math.round(alpha * 255).toString(16);
if (hex.length === 1) hex = "0" + hex;
return hex;
}
};
//#endregion
//#region src/libraries/colors/gradient-color.ts
var GradientColor = class extends SolidColor {
_offset;
constructor(offset, hexColor, opacity = 1) {
super(hexColor, opacity);
this.setOffset(offset);
}
offset() {
return this._offset;
}
hash() {
return Helper.hash([super.hash(), this.offset()]);
}
setOffset(offset) {
this._offset = Helper.valueInRange(offset, {
min: 0,
max: 1
});
return this;
}
};
//#endregion
//#region src/libraries/colors/gradient.ts
var Gradient = class extends Color {
_colors;
constructor(colors) {
super();
this._colors = colors;
}
colors() {
return this._colors;
}
hash() {
return Helper.hash([this.colors()]);
}
};
//#endregion
//#region src/libraries/colors/linear-gradient.ts
var LinearGradient = class extends Gradient {
_degrees = 0;
type() {
return ColorType.LinearGradient;
}
degrees() {
return this._degrees;
}
setDegrees(value) {
this._degrees = value;
return this;
}
hash() {
return Helper.hash([super.hash(), this.degrees()]);
}
};
//#endregion
//#region src/libraries/colors/radial-gradient.ts
var RadialGradient = class extends Gradient {
_position;
_radiusScale;
type() {
return ColorType.RadialGradient;
}
position() {
return this._position;
}
radiusScale() {
return this._radiusScale ?? 1;
}
setPosition(value) {
this._position = value;
return this;
}
setRadiusScale(value) {
this._radiusScale = value;
return this;
}
hash() {
return Helper.hash([
super.hash(),
this.position(),
this.radiusScale()
]);
}
};
//#endregion
export { Canvas, ColorType, EllipseElement, FontWeight, GradientColor, HorizontalAlign, ImageElement, ImageFit, LineCap, LineElement, LinearGradient, PolygonElement, PositionPoint, RadialGradient, RectangleElement, Shape, SolidColor, TextElement, VerticalAlign };

@@ -1,1 +0,1903 @@

!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).Canvas={})}(this,function(t){Object.defineProperty(t,Symbol.toStringTag,{value:"Module"});var e=function(t){return t.Rectangle="rectangle",t.Ellipse="ellipse",t.Polygon="polygon",t.Line="line",t.Text="text",t.Image="image",t}({}),i=function(t){return t.LeftTop="left-top",t.RightTop="right-top",t.LeftBottom="left-bottom",t.RightBottom="right-bottom",t.Center="center",t}({}),s=class t{static valueInRange(t,e){return t<e.min&&(t=e.min),t>e.max&&(t=e.max),t}static valueNotGreaterThan(t,e){return t>e&&(t=e),t}static valueNotLessThan(t,e){return t<e&&(t=e),t}static relativePositionValue(e,i,s){const o=t.extractedValue(e);let h;if("%"===o.unit)h=(s-i)/100*o.value;else h=Number(e);return h}static extractedValue(t){let e;if("string"==typeof t){const i=t.match(/^(\d+)(\D+)$/)??[];e=i.length>2?{value:parseFloat(i[1]??"0"),unit:i[2]??"px"}:{value:Number(t),unit:"px"}}else e={value:t,unit:"px"};return e}static degreesToRadians(t){return t/180*Math.PI}static distance(t,e){return Math.sqrt(Math.pow(Math.abs(e.x-t.x),2)+Math.pow(Math.abs(e.y-t.y),2))}static positionBetweenPoints(e,i,s){const o=s/t.distance(e,i);return{x:e.x+o*(i.x-e.x),y:e.y+o*(i.y-e.y)}}static shapeSize(t){const e=Math.min(...t.map(t=>t.x)),i=Math.min(...t.map(t=>t.y)),s=Math.max(...t.map(t=>t.x)),o=Math.max(...t.map(t=>t.y));return{width:Math.floor(s-e),height:Math.floor(o-i)}}static positionLeftTop(t,e,s){const o={x:t.x,y:t.y};switch(e){case i.RightTop:o.x-=s.width;break;case i.RightBottom:o.x-=s.width,o.y-=s.height;break;case i.LeftBottom:o.y-=s.height;break;case i.Center:o.x-=s.width/2,o.y-=s.height/2}return o}static positionCenter(e,i,s){const o=t.positionLeftTop(e,i,s);return o.x+=s.width/2,o.y+=s.height/2,o}static resizedSize(t,e,i){const s={width:e.width,height:e.height};if(i){const i=[{width:t.width*(e.height/t.height),height:e.height},{width:e.width,height:t.height*(e.width/t.width)}].find(t=>t.width<=e.width&&t.height<=e.height);i&&(s.width=i.width,s.height=i.height)}return s}static resolvedValue(t){let e;return e=t&&"function"==typeof t?t():t,e}static isObject(t){return"object"==typeof t&&t.constructor===Object&&!Array.isArray(t)}static hasMethod(t,e){return!!t[e]&&"function"==typeof t[e]}static hash(e){return e.map(e=>{let i;return i=null==e?"undefined":t.isObject(e)?JSON.stringify(e):Array.isArray(e)?"["+t.hash(e)+"]":t.hasMethod(e,"hash")?e.hash():e.toString(),i}).join("|")}},o=class{ctx;element;relativeProps;defaultBorderColor="#000000";constructor(t,e,i){this.ctx=t,this.element=e,this.relativeProps=i??{}}relativePosition(){return this.relativeProps.position??{x:0,y:0}}relativeScale(){return this.relativeProps.scale??{x:1,y:1}}rotations(){return[...this.relativeProps.rotations??[],...this.element.rotations()]}relativeOpacity(){return this.relativeProps.opacity??1}scale(){const t=this.element.scale(),e=this.relativeScale();return{x:t.x*e.x,y:t.y*e.y}}degrees(){return this.element.degrees()??0}anchor(){return this.element.anchor()??this.defaultAnchor()}opacity(){return this.element.opacity()*this.relativeOpacity()}startDrawing(){this.ctx.save();const t=this.scale(),e=this.opacity(),i=this.degrees(),s=this.rotations(),o=this.element.shadow();1===t.x&&1===t.y||this.ctx.scale(t.x,t.y),e<1&&(this.ctx.globalAlpha=e),0!==i&&this.rotate({degrees:i,pivot:this.anchor()}),s.length>0&&s.forEach(t=>{0!==t.degrees&&this.rotate(t)}),o&&o.blur>0&&(this.ctx.shadowColor=o.color,this.ctx.shadowBlur=o.blur,this.ctx.shadowOffsetX=o.x,this.ctx.shadowOffsetY=o.y)}endDrawing(){this.ctx.restore()}rotate(t){const e=this.position(),i=t.pivot??this.defaultAnchor(),o={x:i.x+e.x,y:i.y+e.y};this.ctx.translate(o.x,o.y),this.ctx.rotate(s.degreesToRadians(t.degrees)),this.ctx.translate(-o.x,-o.y)}},h=class{_originalSize;_sizeRatio;_shapePosition;constructor(t,e){this.ctx=t,this.shape=e}draw(){this.prepare(),this.ctx.save(),this.rotateContext(),this.shape.isCloseShape()?this.makeClosedShape():this.makeOpenedShape(),this.ctx.restore()}prepare(){this._originalSize=s.shapeSize(this.shape.points()),this._sizeRatio=this.sizeRatio(),this._shapePosition=this.shapePosition()}rotateContext(){const t=this.shape.degrees();0!==t&&this.rotate({degrees:t}),this.shape.rotations().forEach(t=>{this.rotate(t)})}makeClosedShape(){const t=this.shape.points();t.length>2&&t.forEach((e,i)=>{const o=0===i,h=i===t.length-1,n=t[i+1]??t[0],r=s.distance(e,n),a=this.radius(r),l=s.positionBetweenPoints(e,n,a),d=s.positionBetweenPoints(e,n,r-a);if(o&&this.moveToPoint(l),this.lineToPoint(d),a>0){const e=t[i+2]??(h?t[1]:t[0]);this.quadraticCurveToPoint(n,e)}})}makeOpenedShape(){const t=this.shape.points();t.length>1&&t.forEach((e,i)=>{const o=0===i,h=i===t.length-1,n=i===t.length-2;if(!h){const h=t[i+1],r=h?s.distance(e,h):0,a=this.radius(r),l=o?e:s.positionBetweenPoints(e,h,a),d=n?h:s.positionBetweenPoints(e,h,r-a);o&&this.moveToPoint(l),this.lineToPoint(d),!n&&a>0&&this.quadraticCurveToPoint(h,t[i+2])}})}radius(t){const e=Math.min(this._sizeRatio.width,this._sizeRatio.height),i=Math.floor(this.shape.jointsRadius()/e),s=Math.floor(.55*t);return i>s?s:i}moveToPoint(t){this.ctx.moveTo(this._shapePosition.x+t.x*this._sizeRatio.width,this._shapePosition.y+t.y*this._sizeRatio.height)}lineToPoint(t){this.ctx.lineTo(this._shapePosition.x+t.x*this._sizeRatio.width,this._shapePosition.y+t.y*this._sizeRatio.height)}quadraticCurveToPoint(t,e){const i=s.distance(t,e),o=this.radius(i),h=s.positionBetweenPoints(t,e,o);this.ctx.quadraticCurveTo(this._shapePosition.x+t.x*this._sizeRatio.width,this._shapePosition.y+t.y*this._sizeRatio.height,this._shapePosition.x+h.x*this._sizeRatio.width,this._shapePosition.y+h.y*this._sizeRatio.height)}shapePosition(){return s.positionLeftTop(this.shape.position(),this.shape.positionPoint(),this.shapeSize())}shapeSize(){return this.shape.size()??this._originalSize}sizeRatio(){const t=this.shapeSize();return{width:t.width/s.valueNotLessThan(this._originalSize.width,1),height:t.height/s.valueNotLessThan(this._originalSize.height,1)}}rotate(t){const e=this.shapePosition(),i=t.pivot??this.defaultAnchor();i.x+=e.x,i.y+=e.y,this.ctx.translate(i.x,i.y),this.ctx.rotate(s.degreesToRadians(t.degrees)),this.ctx.translate(-i.x,-i.y)}defaultAnchor(){const t=this.shapeSize();return{x:t.width/2,y:t.height/2}}},n=function(t){return t.SolidColor="solid-color",t.LinearGradient="linear-gradient",t.RadialGradient="radial-gradient",t}({}),r=class{constructor(t,e,i){this.ctx=t,this.elementPosition=e,this.elementSize=i}mixed(t){let e="";if("string"==typeof t)e=t;else switch(t.type()){case n.SolidColor:e=t.color();break;case n.LinearGradient:e=this.linearGradient(t);break;case n.RadialGradient:e=this.radialGradient(t)}return e}linearGradient(t){const e=s.degreesToRadians(t.degrees()),i=this.elementPosition.x+this.elementSize.width/2,o=this.elementPosition.y+this.elementSize.height/2,h=Math.sqrt(this.elementSize.width**2+this.elementSize.height**2)/2,n=Math.cos(e)*h,r=Math.sin(e)*h,a={x:i-n,y:o-r},l={x:i+n,y:o+r},d=this.ctx.createLinearGradient(a.x,a.y,l.x,l.y);return this.appendGradientColors(d,t.colors()),d}radialGradient(t){const e=t.position()??{x:this.elementSize.width/2,y:this.elementSize.height/2},i={x:this.elementPosition.x+e.x,y:this.elementPosition.y+e.y},s=Math.sqrt(this.elementSize.width*this.elementSize.width+this.elementSize.height*this.elementSize.height)/2,o=this.ctx.createRadialGradient(i.x,i.y,0,i.x,i.y,s*t.radiusScale());return this.appendGradientColors(o,t.colors()),o}appendGradientColors(t,e){e.forEach(e=>t.addColorStop(e.offset(),e.color()))}},a=class extends o{draw(){this.startDrawing(),this.makeShape(),this.drawFill(),this.drawBorder(),this.endDrawing()}makeShape(){const t=this.element.borderWidth(),e=this.position(),i=this.element.size(),s=this.element.cutOutShapes(),o={x:e.x+t/2,y:e.y+t/2},n={width:i.width-t,height:i.height-t};this.ctx.beginPath(),s.length>0&&(this.ctx.translate(o.x,o.y),s.forEach(t=>{new h(this.ctx,t).draw()}),this.ctx.translate(-o.x,-o.y),o.x+=n.width,n.width*=-1),this.ctx.rect(o.x,o.y,n.width,n.height),this.ctx.closePath()}drawFill(){const t=this.element.fillColor();t&&(this.ctx.fillStyle=new r(this.ctx,this.position(),this.element.size()).mixed(t),this.ctx.fill())}drawBorder(){const t=this.element.borderWidth();t>0&&(this.ctx.strokeStyle=new r(this.ctx,this.position(),this.element.size()).mixed(this.element.borderColor()??this.defaultBorderColor),this.ctx.lineWidth=t,this.ctx.stroke())}position(){const t=this.element.positionLeftTop(),e=this.relativePosition();return{x:t.x+e.x,y:t.y+e.y}}defaultAnchor(){const t=this.element.size();return{x:t.width/2,y:t.height/2}}},l=class extends o{draw(){this.startDrawing(),this.drawFill(),this.drawBorder(),this.endDrawing()}drawFill(){const t=this.element.fillColor();if(t){const e=this.element.borderWidth(),i=this.position(),o=this.element.size();this.ctx.beginPath(),this.ctx.ellipse(i.x,i.y,s.valueNotLessThan(o.width/2-e,1),s.valueNotLessThan(o.height/2-e,1),0,0,2*Math.PI),this.ctx.closePath(),this.ctx.fillStyle=new r(this.ctx,this.element.positionLeftTop(),this.element.size()).mixed(t),this.ctx.fill()}}drawBorder(){const t=this.element.borderWidth();if(t>0){const e=this.position(),i=this.element.size();this.ctx.beginPath(),this.ctx.ellipse(e.x,e.y,s.valueNotLessThan(i.width/2-t/2,1),s.valueNotLessThan(i.height/2-t/2,1),0,0,2*Math.PI),this.ctx.closePath(),this.ctx.strokeStyle=new r(this.ctx,this.element.positionLeftTop(),this.element.size()).mixed(this.element.borderColor()??this.defaultBorderColor),this.ctx.lineWidth=t,this.ctx.stroke()}}position(){const t=s.positionCenter(this.element.position(),this.element.positionPoint(),this.element.boundingClientSize()),e=this.relativePosition();return{x:t.x+e.x,y:t.y+e.y}}defaultAnchor(){return{x:0,y:0}}},d=class extends o{draw(){this.prepare(),this.startDrawing(),this.drawStroke(),this.drawFill(),this.endDrawing()}prepare(){this.ctx.font=this.element.font(),this.ctx.textBaseline="alphabetic"}drawFill(){const t=this.element.fillColor();if(t){const e=this.element.textBox(),i=this.position();this.ctx.fillStyle=new r(this.ctx,this.position(),e.size).mixed(t),e.lines.forEach(t=>{this.ctx.fillText(t.text,i.x+t.position.x,Math.round(i.y+t.position.y+.75*t.size.height))})}}drawStroke(){const t=this.element.strokeWidth();if(t>0){const e=this.element.textBox(),i=this.position(),s=this.element.fillColor();this.ctx.strokeStyle=new r(this.ctx,this.position(),e.size).mixed(this.element.strokeColor()??this.defaultBorderColor),this.ctx.lineWidth=s?2*t:t,e.lines.forEach(t=>{this.ctx.strokeText(t.text,i.x+t.position.x,Math.round(i.y+t.position.y+.75*t.size.height))})}}position(){const t=this.element.positionLeftTop(),e=this.relativePosition();return{x:t.x+e.x,y:t.y+e.y}}defaultAnchor(){const t=this.element.textBox();return{x:t.size.width/2,y:t.size.height/2}}},c=function(t){return t.Fill="fill",t.Cover="cover",t}({}),u=class extends o{draw(){this.startDrawing(),this.drawBorder(),this.drawImage(),this.endDrawing()}drawImage(){const t=this.element.image();if(t){const e=this.sourceDimensions(),i=this.flippedDimensions(this.elementDimensions());this.ctx.drawImage(t,e.position.x,e.position.y,e.size.width,e.size.height,i.position.x,i.position.y,i.size.width,i.size.height)}}drawBorder(){const t=this.element.borderWidth(),e=new r(this.ctx,this.position(),this.element.size()).mixed(this.element.borderColor()??this.defaultBorderColor),i=this.elementDimensions();this.ctx.strokeStyle=e,this.ctx.lineWidth=2*t,this.isRadiusSet()?(this.makeShape(i),this.ctx.lineJoin="round",this.ctx.fillStyle="#ffffff",this.ctx.fill(),t>0&&(this.ctx.strokeStyle=e,this.ctx.lineWidth=2*t,this.ctx.stroke()),this.ctx.clip()):t>0&&(this.ctx.strokeStyle=e,this.ctx.lineWidth=2*t,this.ctx.strokeRect(i.position.x,i.position.y,i.size.width,i.size.height))}sourceDimensions(){const t=this.element.image(),e=this.element.cropArea(),i={position:{x:0,y:0},size:e.size??{width:t.width,height:t.height}};switch(this.element.fit()){case c.Fill:e.position&&(i.position.x=e.position.x,i.position.y=e.position.y);break;case c.Cover:{const{size:o}=this.elementDimensions();o.width/o.height<i.size.width/i.size.height?i.size.width=i.size.height/(o.height/o.width):i.size.height=i.size.width/(o.width/o.height),e.position?(i.position.x=e.position.x,i.position.y=e.position.y):(i.position.x=s.relativePositionValue("50%",i.size.width,t.width),i.position.y=s.relativePositionValue("50%",i.size.height,t.height));break}}return i}elementDimensions(){const t=this.element.borderWidth(),e=this.position(),i=this.element.size();return{position:{x:e.x+t,y:e.y+t},size:{width:i.width-2*t,height:i.height-2*t}}}flippedDimensions(t){const e=this.element.flipX(),i=this.element.flipY();if(e||i){let s=1,o=1;e&&(s=-1,t.position.x=-(+t.position.x+t.size.width)),i&&(o=-1,t.position.y=-(+t.position.y+t.size.height)),this.ctx.scale(s,o)}return t}makeShape(t){const e=this.borderRadius();this.ctx.lineJoin="round",this.ctx.beginPath(),this.ctx.moveTo(t.position.x+e.topLeft,t.position.y),this.ctx.lineTo(t.position.x+t.size.width-e.topRight,t.position.y),this.ctx.quadraticCurveTo(t.position.x+t.size.width,t.position.y,t.position.x+t.size.width,t.position.y+e.topRight),this.ctx.lineTo(t.position.x+t.size.width,t.position.y+t.size.height-e.bottomRight),this.ctx.quadraticCurveTo(t.position.x+t.size.width,t.position.y+t.size.height,t.position.x+t.size.width-e.bottomRight,t.position.y+t.size.height),this.ctx.lineTo(t.position.x+e.bottomLeft,t.position.y+t.size.height),this.ctx.quadraticCurveTo(t.position.x,t.position.y+t.size.height,t.position.x,t.position.y+t.size.height-e.bottomLeft),this.ctx.lineTo(t.position.x,t.position.y+e.topLeft),this.ctx.quadraticCurveTo(t.position.x,t.position.y,t.position.x+e.topLeft,t.position.y),this.ctx.closePath()}borderRadius(){const t=this.element.borderRadius();if(this.isRadiusSet()){const{size:e}=this.elementDimensions();Object.keys(t).forEach(i=>{t[i]>e.width/2&&(t[i]=Math.floor(e.width/2)),t[i]>e.height/2&&(t[i]=Math.floor(e.height/2))})}return t}isRadiusSet(){const t=this.element.borderRadius();return t.topLeft>0||t.topRight>0||t.bottomLeft>0||t.bottomRight>0}position(){const t=this.element.positionLeftTop(),e=this.relativePosition();return{x:t.x+e.x,y:t.y+e.y}}defaultAnchor(){const t=this.element.size();return{x:t.width/2,y:t.height/2}}},p=class{_points=[];_isCloseShape;_position={x:0,y:0};_positionPoint=i.LeftTop;_size;_jointsRadius=0;_degrees=0;_anchor;_rotations=[];constructor(t,e=!0){this._points=t,this._isCloseShape=e}hash(){return s.hash([this.points(),this.isCloseShape(),this.position(),this.positionPoint(),this.size(),this.jointsRadius(),this.rotations(),this.degrees(),this.anchor()])}points(){return this._points}isCloseShape(){return this._isCloseShape}position(){return this._position}positionPoint(){return this._positionPoint}size(){return this._size}jointsRadius(){return this._jointsRadius}degrees(){return this._degrees}anchor(){return this._anchor}rotations(){return this._rotations}setPosition(t,e){return this._position=t,e&&(this._positionPoint=e),this}setSize(t){return this._size=t,this}setJointsRadius(t){return this._jointsRadius=t,this}setDegrees(t,e){return this._degrees=t,this._anchor=e,this}setRotations(t){return this._rotations=t,this}},x=class extends o{draw(){this.startDrawing(),this.makeShape(),this.drawBorder(),this.drawFill(),this.endDrawing()}makeShape(){const t=this.element.shapePoints();if(t.length>2){const e=new p(t).setSize(this.shapeSize()).setPosition(this.shapePosition()).setJointsRadius(this.element.borderRadius());this.ctx.beginPath(),new h(this.ctx,e).draw(),this.ctx.closePath()}}drawFill(){const t=this.element.fillColor();t&&(this.ctx.fillStyle=new r(this.ctx,this.position(),this.shapeSize()).mixed(t),this.ctx.fill())}drawBorder(){const t=this.element.borderWidth();if(t>0){const e=new r(this.ctx,this.position(),this.element.size()).mixed(this.element.borderColor()??this.defaultBorderColor),i=this.element.fillColor();this.element.borderRadius()>0&&(this.ctx.lineJoin="round"),this.ctx.strokeStyle=e,this.ctx.lineWidth=i?2*t:t,this.ctx.stroke()}}shapePosition(){const t=this.position(),e=this.element.borderWidth();return{x:t.x+e,y:t.y+e}}shapeSize(){const t=this.element.size(),e=this.element.borderWidth();return{width:t.width-2*e,height:t.height-2*e}}position(){const t=this.element.positionLeftTop(),e=this.relativePosition();return{x:t.x+e.x,y:t.y+e.y}}defaultAnchor(){const t=this.element.size();return{x:t.width/2,y:t.height/2}}},g=class extends o{draw(){this.startDrawing(),this.makeShape(),this.drawStroke(),this.endDrawing()}makeShape(){this.ctx.beginPath();const t=new p(this.element.shapePoints(),!1).setSize(this.shapeSize()).setPosition(this.shapePosition()).setJointsRadius(this.element.jointsRadius());new h(this.ctx,t).draw()}drawStroke(){const t=new r(this.ctx,this.position(),this.element.boundingClientSize()).mixed(this.element.color()??this.defaultBorderColor);this.ctx.lineCap=this.element.lineCap(),this.ctx.strokeStyle=t,this.ctx.lineWidth=this.element.calculatedThickness(),this.ctx.stroke()}shapeSize(){const t=this.element.boundingClientSize(),e=this.element.calculatedThickness();return{width:t.width-e,height:t.height-e/2}}shapePosition(){const t=this.position(),e=this.element.calculatedThickness();return{x:t.x+e/2,y:t.y}}position(){const t=this.element.positionLeftTop(),e=this.relativePosition();return{x:t.x+e.x,y:t.y+e.y}}defaultAnchor(){const t=this.shapeSize();return{x:t.width/2,y:t.height/2}}},m=class{},f=class extends m{_color;constructor(t,e){super(),this.setColor(t,e)}type(){return n.SolidColor}color(){return this._color}hash(){return s.hash([this.color()])}setColor(t,e){const i=9===t.length?t.slice(-2):void 0,s=void 0!==e?this.alphaToHex(e):i??"ff";return this._color=t.slice(0,7)+s,this}alphaToHex(t){let e=Math.round(255*t).toString(16);return 1===e.length&&(e="0"+e),e}},_=class extends m{_colors;constructor(t){super(),this._colors=t}colors(){return this._colors}hash(){return s.hash([this.colors()])}},w=class{_position={x:0,y:0};_positionPoint;_scale={x:1,y:1};_opacity=1;_rotations=[];_degrees=0;_anchor;_shadow;hash(){return s.hash([this.position(),this.positionPoint(),this.scale(),this.opacity(),this.rotations(),this.degrees(),this.anchor(),this.shadow()])}position(){return s.resolvedValue(this._position)}positionLeftTop(){return s.positionLeftTop(this.position(),this.positionPoint(),this.boundingClientSize())}positionPoint(){return s.resolvedValue(this._positionPoint)??i.LeftTop}scale(){return s.resolvedValue(this._scale)}opacity(){return s.resolvedValue(this._opacity)}rotations(){return s.resolvedValue(this._rotations)}degrees(){return s.resolvedValue(this._degrees)}anchor(){return this._anchor?s.resolvedValue(this._anchor):void 0}shadow(){return this._shadow?s.resolvedValue(this._shadow):void 0}setPosition(t,e){return this._position=t,e&&(this._positionPoint=e),this}setScale(t){return this._scale=t,this}setOpacity(t){return this._opacity=t,this}setDegrees(t,e){return this._degrees=t,this._anchor=e,this}setRotations(t){return this._rotations=t,this}setShadow(t){return this._shadow=t,this}},y=class extends w{_size={width:1,height:1};_borderColor;_borderWidth=0;hash(){return s.hash([super.hash(),this.size(),this.borderColor(),this.borderWidth()])}size(){return s.resolvedValue(this._size)}borderColor(){return this._borderColor?s.resolvedValue(this._borderColor):void 0}borderWidth(){return s.resolvedValue(this._borderWidth)}setSize(t){return this._size=t,this}setBorder(t,e){return this._borderWidth=t,this._borderColor=e,this}},z=function(t){return t.Round="round",t.Square="square",t}({}),v=function(t){return t.Normal="normal",t.Bold="bold",t}({}),S=function(t){return t.Left="left",t.Center="center",t.Right="right",t}({}),b=function(t){return t.Top="top",t.Center="center",t.Bottom="bottom",t}({}),C=class{element;canvas;ctx;lastTextBox;lastTextBoxHash="";constructor(t){this.element=t,this.canvas=document.createElement("canvas"),this.ctx=this.canvas.getContext("2d")}textBox(){return this.elementChanged()&&(this.lastTextBox=void 0),this.lastTextBox||(this.lastTextBox=this.createTextBox()),this.lastTextBox}createTextBox(){const t={text:this.element.text(),lines:[],size:{width:1,height:1},position:{x:0,y:0}};return t.lines=this.textLines(),t.size=this.textBoxSize(t.lines),t.position=this.textBoxPosition(t.lines),t}textLines(){const t=this.element.bounds(),e=this.element.padding(),i=t?{width:t.width-e.left-e.right,height:t.height-e.top-e.bottom}:void 0,s=this.splitText().map(t=>({text:t,size:{width:this.textWidth(t),height:this.element.lineHeight()},position:{x:0,y:0}})).map((t,e,i)=>{const s=i[e-1];return t.position={x:0,y:s?s.position.y+s.size.height:0},t});return(void 0!==i?this.alignedLines(s,i):s).map(t=>(t.position.x+=e.left,t.position.y+=e.top,t))}alignedLines(t,e){const i=this.element.horizontalAlign(),s=this.element.verticalAlign(),o=t.reduce((t,e)=>t+e.size.height,0);return t.map(t=>(i===S.Center&&(t.position.x+=Math.round((e.width-t.size.width)/2)),i===S.Right&&(t.position.x+=e.width-t.size.width),s===b.Center&&(t.position.y+=Math.round((e.height-o)/2)),s===b.Bottom&&(t.position.y+=e.height-o),t))}textBoxSize(t){const e=this.element.padding(),i=this.element.bounds()??{width:1,height:1},s=Math.max(...t.map(t=>t.size.width));return{width:Math.max(s+e.left+e.right,i.width),height:Math.max(this.element.lineHeight()*t.length+e.top+e.bottom,i.height)}}textBoxPosition(t){const e=this.element.padding(),i=Math.min(...t.map(t=>t.position.x)),s=Math.min(...t.map(t=>t.position.y));return{x:i-e.left,y:s-e.top}}splitText(){const t=this.element.text().replaceAll("<BR>","<br>").split("<br>"),e=this.element.padding(),i=(this.element.bounds()??{width:999999,height:999999}).width-(e.left+e.right);let s=[];return t.forEach(t=>{t=t.trim();const e=this.textWidth(t);if(e>i){const o=Math.floor(i/(e/t.length))-3,h=t.split(" ");let n="";h.forEach((t,e)=>{n.length+t.length>o&&(s.push(n.trim()),n=""),n+=t+" ",e===h.length-1&&s.push(n.trim())}),s=s.map(t=>t.trim())}else s.push(t)}),s}textWidth(t){return this.ctx.font=this.element.font(),Math.ceil(this.ctx.measureText(t).width+this.element.strokeWidth())}elementChanged(){const t=[this.element.text(),this.element.bounds(),this.element.horizontalAlign(),this.element.verticalAlign(),this.element.padding(),this.element.lineHeight(),this.element.font(),this.element.strokeWidth()].join("|"),e=this.lastTextBoxHash!==t;return this.lastTextBoxHash=t,e}};t.Canvas=class{_canvas;_ctx;constructor(t){this._canvas=t??document.createElement("canvas"),this._ctx=this._canvas.getContext("2d")}nativeElement(){return this._canvas}ctx(){return this._ctx}size(){return{width:this._canvas.width,height:this._canvas.height}}setSize(t){this._canvas.width=t.width,this._canvas.height=t.height}draw(t,i){switch(t.type()){case e.Rectangle:new a(this.ctx(),t,i).draw();break;case e.Ellipse:new l(this.ctx(),t,i).draw();break;case e.Polygon:new x(this.ctx(),t,i).draw();break;case e.Line:new g(this.ctx(),t,i).draw();break;case e.Text:new d(this.ctx(),t,i).draw();break;case e.Image:new u(this.ctx(),t,i).draw()}}clear(){this.ctx().clearRect(0,0,this._canvas.width,this._canvas.height)}},t.ColorType=n,t.EllipseElement=class extends y{_fillColor;type(){return e.Ellipse}hash(){return s.hash([super.hash(),this.fillColor()])}fillColor(){return this._fillColor?s.resolvedValue(this._fillColor):void 0}boundingClientSize(){return this.size()}setFillColor(t){return this._fillColor=t,this}},t.FontWeight=v,t.GradientColor=class extends f{_offset;constructor(t,e,i=1){super(e,i),this.setOffset(t)}offset(){return this._offset}hash(){return s.hash([super.hash(),this.offset()])}setOffset(t){return this._offset=s.valueInRange(t,{min:0,max:1}),this}},t.HorizontalAlign=S,t.ImageElement=class extends y{_image;_fit=c.Fill;_borderRadius={topLeftOrDefault:0};_flipX;_flipY;_cropArea;type(){return e.Image}hash(){return s.hash([super.hash(),this.image()?.id,this.fit(),this.borderRadius(),this.flipX(),this.flipY(),this.cropArea()])}image(){return this._image?s.resolvedValue(this._image):void 0}fit(){return s.resolvedValue(this._fit)}borderRadius(){let t;const e=s.resolvedValue(this._borderRadius.topLeftOrDefault),i=this._borderRadius.topRight?s.resolvedValue(this._borderRadius.topRight):void 0,o=this._borderRadius.bottomRight?s.resolvedValue(this._borderRadius.bottomRight):void 0,h=this._borderRadius.bottomLeft?s.resolvedValue(this._borderRadius.bottomLeft):void 0;return t=void 0===i&&void 0===o&&void 0===h?{topLeft:e,topRight:e,bottomRight:e,bottomLeft:e}:{topLeft:e,topRight:i??0,bottomRight:o??0,bottomLeft:h??0},t}flipX(){return s.resolvedValue(this._flipX)??!1}flipY(){return s.resolvedValue(this._flipY)??!1}cropArea(){return s.resolvedValue(this._cropArea)??{}}boundingClientSize(){return this.size()}setImage(t){return this._image=t,this}setFit(t){return this._fit=t,this}setBorderRadius(t,e,i,s){return this._borderRadius={topLeftOrDefault:t,topRight:e,bottomRight:i,bottomLeft:s},this}setFlip(t){return this._flipX=t.horizontal??!1,this._flipY=t.vertical??!1,this}setCropArea(t){return this._cropArea=t,this}},t.ImageFit=c,t.LineCap=z,t.LineElement=class extends w{_shapePoints=[];_size;_keepRatio;_thickness=1;_jointsRadius=0;_lineCap=z.Square;_color;type(){return e.Line}hash(){return s.hash([super.hash(),this.shapePoints(),this.size(),this.keepRatio(),this.thickness(),this.jointsRadius(),this.lineCap(),this.color()])}shapePoints(){return s.resolvedValue(this._shapePoints)}size(){return this._size?s.resolvedValue(this._size):void 0}keepRatio(){return s.resolvedValue(this._keepRatio)??!1}thickness(){return s.resolvedValue(this._thickness)}calculatedThickness(){const t=this.size();let e=this.thickness();if(t){const i=s.shapeSize(this.shapePoints()),o=s.resizedSize(i,t,this.keepRatio()),h=o.width/s.valueNotLessThan(i.width,1),n=o.height/s.valueNotLessThan(i.height,1);e=s.valueNotLessThan(e*Math.min(h,n),1)}return e}jointsRadius(){return s.resolvedValue(this._jointsRadius)}lineCap(){return s.resolvedValue(this._lineCap)}color(){return this._color?s.resolvedValue(this._color):void 0}boundingClientSize(){let t=this.size();const e=this.calculatedThickness(),i=s.shapeSize(this.shapePoints());if(t){const o=s.resizedSize(i,t,this.keepRatio());t={width:o.width,height:o.height+e/2}}else t={width:i.width,height:s.valueNotLessThan(i.height+e/2,0)};return t}setShapePoints(t){return this._shapePoints=t,this}setSize(t,e){return this._size=t,e&&(this._keepRatio=e),this}setThickness(t){return this._thickness=t,this}setJointsRadius(t){return this._jointsRadius=t,this}setLineCap(t){return this._lineCap=t,this}setColor(t){return this._color=t,this}},t.LinearGradient=class extends _{_degrees=0;type(){return n.LinearGradient}degrees(){return this._degrees}setDegrees(t){return this._degrees=t,this}hash(){return s.hash([super.hash(),this.degrees()])}},t.PolygonElement=class extends y{_shapePoints=[];_borderRadius=0;_fillColor;type(){return e.Polygon}hash(){return s.hash([super.hash(),this.shapePoints(),this.borderRadius(),this.fillColor()])}shapePoints(){return s.resolvedValue(this._shapePoints)}borderRadius(){return s.resolvedValue(this._borderRadius)}fillColor(){return this._fillColor?s.resolvedValue(this._fillColor):void 0}boundingClientSize(){return this.size()}setShapePoints(t){return this._shapePoints=t,this}setBorderRadius(t){return this._borderRadius=t,this}setFillColor(t){return this._fillColor=t,this}},t.PositionPoint=i,t.RadialGradient=class extends _{_position;_radiusScale;type(){return n.RadialGradient}position(){return this._position}radiusScale(){return this._radiusScale??1}setPosition(t){return this._position=t,this}setRadiusScale(t){return this._radiusScale=t,this}hash(){return s.hash([super.hash(),this.position(),this.radiusScale()])}},t.RectangleElement=class extends y{_fillColor;_cutOutShapes=[];type(){return e.Rectangle}hash(){return s.hash([super.hash(),this.fillColor(),this.cutOutShapes()])}fillColor(){return this._fillColor?s.resolvedValue(this._fillColor):void 0}cutOutShapes(){return s.resolvedValue(this._cutOutShapes)}boundingClientSize(){return this.size()}setFillColor(t){return this._fillColor=t,this}setCutOutShapes(t){return this._cutOutShapes=t,this}},t.Shape=p,t.SolidColor=f,t.TextElement=class extends w{_text="";_bounds;_fontSize=12;_fontFamily="Tahoma";_fontWeight=v.Normal;_lineHeight;_fillColor;_strokeColor;_strokeWidth=0;_horizontalAlign=S.Left;_verticalAlign=b.Top;_padding={topOrVerticalOrDefault:0};_textSplitter=new C(this);type(){return e.Text}hash(){return s.hash([super.hash(),this.text(),this.bounds(),this.font(),this.lineHeight(),this.fillColor(),this.strokeColor(),this.strokeWidth(),this.horizontalAlign(),this.verticalAlign(),this.padding()])}text(){return s.resolvedValue(this._text).toString()}bounds(){return this._bounds?s.resolvedValue(this._bounds):void 0}fontSize(){return s.resolvedValue(this._fontSize)}fontFamily(){return s.resolvedValue(this._fontFamily)}fontWeight(){return s.resolvedValue(this._fontWeight)}lineHeight(){return s.resolvedValue(this._lineHeight??Math.round(1.2*this.fontSize()))}fillColor(){return this._fillColor?s.resolvedValue(this._fillColor):void 0}strokeColor(){return this._strokeColor?s.resolvedValue(this._strokeColor):void 0}strokeWidth(){return s.resolvedValue(this._strokeWidth)}horizontalAlign(){return s.resolvedValue(this._horizontalAlign)}verticalAlign(){return s.resolvedValue(this._verticalAlign)}padding(){let t;const e=s.resolvedValue(this._padding.topOrVerticalOrDefault),i=this._padding.rightOrHorizontal?s.resolvedValue(this._padding.rightOrHorizontal):void 0,o=this._padding.bottom?s.resolvedValue(this._padding.bottom):void 0,h=this._padding.left?s.resolvedValue(this._padding.left):void 0;return t=void 0===i?{top:e,right:e,bottom:e,left:e}:void 0===o&&void 0===h?{top:e,right:i,bottom:e,left:i}:{top:e,right:i,bottom:o??0,left:h??0},t}font(){const t=[this.fontSize()+"px",this.fontFamily()],e=this.fontWeight();return e&&t.unshift(e),t.join(" ")}textBox(){return this._textSplitter.textBox()}boundingClientSize(){return this.textBox().size}setText(t){return this._text=t,this}setBounds(t){return this._bounds=t,this}setFontSize(t){return this._fontSize=t,this}setFontFamily(t){return this._fontFamily=t,this}setFontWeight(t){return this._fontWeight=t,this}setLineHeight(t){return this._lineHeight=t,this}setFillColor(t){return this._fillColor=t,this}setStroke(t,e){return this._strokeWidth=t,this._strokeColor=e,this}setHorizontalAlign(t){return this._horizontalAlign=t,this}setVerticalAlign(t){return this._verticalAlign=t,this}setPadding(t,e,i,s){return this._padding={topOrVerticalOrDefault:t,rightOrHorizontal:e,bottom:i,left:s},this}},t.VerticalAlign=b});
(function(global, factory) {
typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.Canvas = {}));
})(this, function(exports) {
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
//#region src/enums.ts
var ElementType = /* @__PURE__ */ function(ElementType) {
ElementType["Rectangle"] = "rectangle";
ElementType["Ellipse"] = "ellipse";
ElementType["Polygon"] = "polygon";
ElementType["Line"] = "line";
ElementType["Text"] = "text";
ElementType["Image"] = "image";
return ElementType;
}({});
var ColorType = /* @__PURE__ */ function(ColorType) {
ColorType["SolidColor"] = "solid-color";
ColorType["LinearGradient"] = "linear-gradient";
ColorType["RadialGradient"] = "radial-gradient";
return ColorType;
}({});
var FontWeight = /* @__PURE__ */ function(FontWeight) {
FontWeight["Normal"] = "normal";
FontWeight["Bold"] = "bold";
return FontWeight;
}({});
var HorizontalAlign = /* @__PURE__ */ function(HorizontalAlign) {
HorizontalAlign["Left"] = "left";
HorizontalAlign["Center"] = "center";
HorizontalAlign["Right"] = "right";
return HorizontalAlign;
}({});
var ImageFit = /* @__PURE__ */ function(ImageFit) {
ImageFit["Fill"] = "fill";
ImageFit["Cover"] = "cover";
return ImageFit;
}({});
var LineCap = /* @__PURE__ */ function(LineCap) {
LineCap["Round"] = "round";
LineCap["Square"] = "square";
return LineCap;
}({});
var PositionPoint = /* @__PURE__ */ function(PositionPoint) {
PositionPoint["LeftTop"] = "left-top";
PositionPoint["RightTop"] = "right-top";
PositionPoint["LeftBottom"] = "left-bottom";
PositionPoint["RightBottom"] = "right-bottom";
PositionPoint["Center"] = "center";
return PositionPoint;
}({});
var VerticalAlign = /* @__PURE__ */ function(VerticalAlign) {
VerticalAlign["Top"] = "top";
VerticalAlign["Center"] = "center";
VerticalAlign["Bottom"] = "bottom";
return VerticalAlign;
}({});
//#endregion
//#region src/libraries/helper/helper.ts
var Helper = class Helper {
static valueInRange(value, range) {
if (value < range.min) value = range.min;
if (value > range.max) value = range.max;
return value;
}
static valueNotGreaterThan(value, max) {
if (value > max) value = max;
return value;
}
static valueNotLessThan(value, min) {
if (value < min) value = min;
return value;
}
static relativePositionValue(value, size, maxValue) {
const extractedValue = Helper.extractedValue(value);
let position;
switch (extractedValue.unit) {
case "%":
position = (maxValue - size) / 100 * extractedValue.value;
break;
default: position = Number(value);
}
return position;
}
static extractedValue(value) {
let result;
if (typeof value === "string") {
const match = value.match(/^(\d+)(\D+)$/) ?? [];
if (match.length > 2) result = {
value: parseFloat(match[1] ?? "0"),
unit: match[2] ?? "px"
};
else result = {
value: Number(value),
unit: "px"
};
} else result = {
value,
unit: "px"
};
return result;
}
static degreesToRadians(degrees) {
return degrees / 180 * Math.PI;
}
static distance(firstPoint, secondPoint) {
return Math.sqrt(Math.pow(Math.abs(secondPoint.x - firstPoint.x), 2) + Math.pow(Math.abs(secondPoint.y - firstPoint.y), 2));
}
static positionBetweenPoints(firstPoint, secondPoint, moveBy) {
const fraction = moveBy / Helper.distance(firstPoint, secondPoint);
return {
x: firstPoint.x + fraction * (secondPoint.x - firstPoint.x),
y: firstPoint.y + fraction * (secondPoint.y - firstPoint.y)
};
}
static shapeSize(points) {
const leftTopCorner = {
x: Math.min(...points.map((point) => point.x)),
y: Math.min(...points.map((point) => point.y))
};
const rightBottomCorner = {
x: Math.max(...points.map((point) => point.x)),
y: Math.max(...points.map((point) => point.y))
};
return {
width: Math.floor(rightBottomCorner.x - leftTopCorner.x),
height: Math.floor(rightBottomCorner.y - leftTopCorner.y)
};
}
static positionLeftTop(position, point, size) {
const positionLeftTop = {
x: position.x,
y: position.y
};
switch (point) {
case PositionPoint.RightTop:
positionLeftTop.x -= size.width;
break;
case PositionPoint.RightBottom:
positionLeftTop.x -= size.width;
positionLeftTop.y -= size.height;
break;
case PositionPoint.LeftBottom:
positionLeftTop.y -= size.height;
break;
case PositionPoint.Center:
positionLeftTop.x -= size.width / 2;
positionLeftTop.y -= size.height / 2;
break;
}
return positionLeftTop;
}
static positionCenter(position, point, size) {
const positionLeftTop = Helper.positionLeftTop(position, point, size);
positionLeftTop.x += size.width / 2;
positionLeftTop.y += size.height / 2;
return positionLeftTop;
}
static resizedSize(originalSize, desiredSize, keepRatio) {
const result = {
width: desiredSize.width,
height: desiredSize.height
};
if (keepRatio) {
const selectedVariant = [{
width: originalSize.width * (desiredSize.height / originalSize.height),
height: desiredSize.height
}, {
width: desiredSize.width,
height: originalSize.height * (desiredSize.width / originalSize.width)
}].find((variant) => {
return variant.width <= desiredSize.width && variant.height <= desiredSize.height;
});
if (selectedVariant) {
result.width = selectedVariant.width;
result.height = selectedVariant.height;
}
}
return result;
}
static resolvedValue(attribute) {
let result;
if (attribute) if (typeof attribute === "function") result = attribute();
else result = attribute;
else result = attribute;
return result;
}
static isObject(value) {
return typeof value === "object" && value.constructor === Object && !Array.isArray(value);
}
static hasMethod(object, methodName) {
return !!object[methodName] && typeof object[methodName] === "function";
}
static hash(attributes) {
return attributes.map((attribute) => {
let result;
if (attribute === void 0 || attribute === null) result = "undefined";
else if (Helper.isObject(attribute)) result = JSON.stringify(attribute);
else if (Array.isArray(attribute)) result = "[" + Helper.hash(attribute) + "]";
else if (Helper.hasMethod(attribute, "hash")) result = attribute.hash();
else result = attribute.toString();
return result;
}).join("|");
}
};
//#endregion
//#region src/libraries/artists/element-artist.ts
var ElementArtist = class {
ctx;
element;
relativeProps;
defaultBorderColor = "#000000";
constructor(ctx, element, relativeProps) {
this.ctx = ctx;
this.element = element;
this.relativeProps = relativeProps ?? {};
}
relativePosition() {
return this.relativeProps.position ?? {
x: 0,
y: 0
};
}
relativeScale() {
return this.relativeProps.scale ?? {
x: 1,
y: 1
};
}
rotations() {
return [...this.relativeProps.rotations ?? [], ...this.element.rotations()];
}
relativeOpacity() {
return this.relativeProps.opacity ?? 1;
}
scale() {
const elementScale = this.element.scale();
const relativeScale = this.relativeScale();
return {
x: elementScale.x * relativeScale.x,
y: elementScale.y * relativeScale.y
};
}
/** @deprecated Use Rotations instead */
degrees() {
return this.element.degrees() ?? 0;
}
/** @deprecated Use Rotations instead */
anchor() {
return this.element.anchor() ?? this.defaultAnchor();
}
opacity() {
return this.element.opacity() * this.relativeOpacity();
}
startDrawing() {
this.ctx.save();
const scale = this.scale();
const opacity = this.opacity();
const degrees = this.degrees();
const rotations = this.rotations();
const shadow = this.element.shadow();
if (scale.x !== 1 || scale.y !== 1) this.ctx.scale(scale.x, scale.y);
if (opacity < 1) this.ctx.globalAlpha = opacity;
if (degrees !== 0) this.rotate({
degrees,
pivot: this.anchor()
});
if (rotations.length > 0) rotations.forEach((rotation) => {
if (rotation.degrees !== 0) this.rotate(rotation);
});
if (shadow && shadow.blur > 0) {
this.ctx.shadowColor = shadow.color;
this.ctx.shadowBlur = shadow.blur;
this.ctx.shadowOffsetX = shadow.x;
this.ctx.shadowOffsetY = shadow.y;
}
}
endDrawing() {
this.ctx.restore();
}
rotate(rotation) {
const position = this.position();
const pivot = rotation.pivot ?? this.defaultAnchor();
const absolutePivot = {
x: pivot.x + position.x,
y: pivot.y + position.y
};
this.ctx.translate(absolutePivot.x, absolutePivot.y);
this.ctx.rotate(Helper.degreesToRadians(rotation.degrees));
this.ctx.translate(-absolutePivot.x, -absolutePivot.y);
}
};
//#endregion
//#region src/libraries/shapes/shaper.ts
var Shaper = class {
_originalSize;
_sizeRatio;
_shapePosition;
constructor(ctx, shape) {
this.ctx = ctx;
this.shape = shape;
}
draw() {
this.prepare();
this.ctx.save();
this.rotateContext();
if (this.shape.isCloseShape()) this.makeClosedShape();
else this.makeOpenedShape();
this.ctx.restore();
}
prepare() {
this._originalSize = Helper.shapeSize(this.shape.points());
this._sizeRatio = this.sizeRatio();
this._shapePosition = this.shapePosition();
}
rotateContext() {
const degrees = this.shape.degrees();
if (degrees !== 0) this.rotate({ degrees });
this.shape.rotations().forEach((rotation) => {
this.rotate(rotation);
});
}
makeClosedShape() {
const points = this.shape.points();
if (points.length > 2) points.forEach((point, index) => {
const isFirstPoint = index === 0;
const isLastPoint = index === points.length - 1;
const nextPoint = points[index + 1] ?? points[0];
const lineLength = Helper.distance(point, nextPoint);
const currentRadius = this.radius(lineLength);
const currentStart = Helper.positionBetweenPoints(point, nextPoint, currentRadius);
const currentEnd = Helper.positionBetweenPoints(point, nextPoint, lineLength - currentRadius);
if (isFirstPoint) this.moveToPoint(currentStart);
this.lineToPoint(currentEnd);
if (currentRadius > 0) {
const afterNextPoint = points[index + 2] ?? (isLastPoint ? points[1] : points[0]);
this.quadraticCurveToPoint(nextPoint, afterNextPoint);
}
});
}
makeOpenedShape() {
const points = this.shape.points();
if (points.length > 1) points.forEach((point, index) => {
const isFirstPoint = index === 0;
const isLastPoint = index === points.length - 1;
const isBeforeLastPoint = index === points.length - 2;
if (!isLastPoint) {
const nextPoint = points[index + 1];
const currentLineLength = nextPoint ? Helper.distance(point, nextPoint) : 0;
const currentRadius = this.radius(currentLineLength);
const currentStart = isFirstPoint ? point : Helper.positionBetweenPoints(point, nextPoint, currentRadius);
const currentEnd = isBeforeLastPoint ? nextPoint : Helper.positionBetweenPoints(point, nextPoint, currentLineLength - currentRadius);
if (isFirstPoint) this.moveToPoint(currentStart);
this.lineToPoint(currentEnd);
if (!isBeforeLastPoint && currentRadius > 0) this.quadraticCurveToPoint(nextPoint, points[index + 2]);
}
});
}
radius(lineLength) {
const averageRatio = Math.min(this._sizeRatio.width, this._sizeRatio.height);
const radius = Math.floor(this.shape.jointsRadius() / averageRatio);
const maxRadius = Math.floor(lineLength * .55);
return radius > maxRadius ? maxRadius : radius;
}
moveToPoint(point) {
this.ctx.moveTo(this._shapePosition.x + point.x * this._sizeRatio.width, this._shapePosition.y + point.y * this._sizeRatio.height);
}
lineToPoint(point) {
this.ctx.lineTo(this._shapePosition.x + point.x * this._sizeRatio.width, this._shapePosition.y + point.y * this._sizeRatio.height);
}
quadraticCurveToPoint(point, nextPoint) {
const nextLineLength = Helper.distance(point, nextPoint);
const nextRadius = this.radius(nextLineLength);
const nextStart = Helper.positionBetweenPoints(point, nextPoint, nextRadius);
this.ctx.quadraticCurveTo(this._shapePosition.x + point.x * this._sizeRatio.width, this._shapePosition.y + point.y * this._sizeRatio.height, this._shapePosition.x + nextStart.x * this._sizeRatio.width, this._shapePosition.y + nextStart.y * this._sizeRatio.height);
}
shapePosition() {
return Helper.positionLeftTop(this.shape.position(), this.shape.positionPoint(), this.shapeSize());
}
shapeSize() {
return this.shape.size() ?? this._originalSize;
}
sizeRatio() {
const size = this.shapeSize();
return {
width: size.width / Helper.valueNotLessThan(this._originalSize.width, 1),
height: size.height / Helper.valueNotLessThan(this._originalSize.height, 1)
};
}
rotate(rotation) {
const position = this.shapePosition();
const anchor = rotation.pivot ?? this.defaultAnchor();
anchor.x += position.x;
anchor.y += position.y;
this.ctx.translate(anchor.x, anchor.y);
this.ctx.rotate(Helper.degreesToRadians(rotation.degrees));
this.ctx.translate(-anchor.x, -anchor.y);
}
defaultAnchor() {
const size = this.shapeSize();
return {
x: size.width / 2,
y: size.height / 2
};
}
};
//#endregion
//#region src/libraries/colors/color-mixer.ts
var ColorMixer = class {
constructor(ctx, elementPosition, elementSize) {
this.ctx = ctx;
this.elementPosition = elementPosition;
this.elementSize = elementSize;
}
mixed(color) {
let result = "";
if (typeof color === "string") result = color;
else switch (color.type()) {
case ColorType.SolidColor:
result = color.color();
break;
case ColorType.LinearGradient:
result = this.linearGradient(color);
break;
case ColorType.RadialGradient:
result = this.radialGradient(color);
break;
}
return result;
}
linearGradient(gradient) {
const angleRadians = Helper.degreesToRadians(gradient.degrees());
const centerPosition = {
x: this.elementPosition.x + this.elementSize.width / 2,
y: this.elementPosition.y + this.elementSize.height / 2
};
const halfLength = Math.sqrt(this.elementSize.width ** 2 + this.elementSize.height ** 2) / 2;
const offset = {
x: Math.cos(angleRadians) * halfLength,
y: Math.sin(angleRadians) * halfLength
};
const startPosition = {
x: centerPosition.x - offset.x,
y: centerPosition.y - offset.y
};
const endPosition = {
x: centerPosition.x + offset.x,
y: centerPosition.y + offset.y
};
const canvasGradient = this.ctx.createLinearGradient(startPosition.x, startPosition.y, endPosition.x, endPosition.y);
this.appendGradientColors(canvasGradient, gradient.colors());
return canvasGradient;
}
radialGradient(gradient) {
const localPosition = gradient.position() ?? {
x: this.elementSize.width / 2,
y: this.elementSize.height / 2
};
const centerPosition = {
x: this.elementPosition.x + localPosition.x,
y: this.elementPosition.y + localPosition.y
};
const radius = Math.sqrt(this.elementSize.width * this.elementSize.width + this.elementSize.height * this.elementSize.height) / 2;
const canvasGradient = this.ctx.createRadialGradient(centerPosition.x, centerPosition.y, 0, centerPosition.x, centerPosition.y, radius * gradient.radiusScale());
this.appendGradientColors(canvasGradient, gradient.colors());
return canvasGradient;
}
appendGradientColors(gradient, colors) {
colors.forEach((color) => gradient.addColorStop(color.offset(), color.color()));
}
};
//#endregion
//#region src/libraries/artists/rectangle-artist.ts
var RectangleArtist = class extends ElementArtist {
draw() {
this.startDrawing();
this.makeShape();
this.drawFill();
this.drawBorder();
this.endDrawing();
}
makeShape() {
const borderWidth = this.element.borderWidth();
const position = this.position();
const size = this.element.size();
const shapes = this.element.cutOutShapes();
const drawPosition = {
x: position.x + borderWidth / 2,
y: position.y + borderWidth / 2
};
const drawSize = {
width: size.width - borderWidth,
height: size.height - borderWidth
};
this.ctx.beginPath();
if (shapes.length > 0) {
this.ctx.translate(drawPosition.x, drawPosition.y);
shapes.forEach((shape) => {
new Shaper(this.ctx, shape).draw();
});
this.ctx.translate(-drawPosition.x, -drawPosition.y);
drawPosition.x += drawSize.width;
drawSize.width *= -1;
}
this.ctx.rect(drawPosition.x, drawPosition.y, drawSize.width, drawSize.height);
this.ctx.closePath();
}
drawFill() {
const color = this.element.fillColor();
if (color) {
this.ctx.fillStyle = new ColorMixer(this.ctx, this.position(), this.element.size()).mixed(color);
this.ctx.fill();
}
}
drawBorder() {
const borderWidth = this.element.borderWidth();
if (borderWidth > 0) {
this.ctx.strokeStyle = new ColorMixer(this.ctx, this.position(), this.element.size()).mixed(this.element.borderColor() ?? this.defaultBorderColor);
this.ctx.lineWidth = borderWidth;
this.ctx.stroke();
}
}
position() {
const elementPosition = this.element.positionLeftTop();
const relativePosition = this.relativePosition();
return {
x: elementPosition.x + relativePosition.x,
y: elementPosition.y + relativePosition.y
};
}
defaultAnchor() {
const size = this.element.size();
return {
x: size.width / 2,
y: size.height / 2
};
}
};
//#endregion
//#region src/libraries/artists/ellipse-artist.ts
var EllipseArtist = class extends ElementArtist {
draw() {
this.startDrawing();
this.drawFill();
this.drawBorder();
this.endDrawing();
}
drawFill() {
const color = this.element.fillColor();
if (color) {
const borderWidth = this.element.borderWidth();
const position = this.position();
const size = this.element.size();
this.ctx.beginPath();
this.ctx.ellipse(position.x, position.y, Helper.valueNotLessThan(size.width / 2 - borderWidth, 1), Helper.valueNotLessThan(size.height / 2 - borderWidth, 1), 0, 0, Math.PI * 2);
this.ctx.closePath();
this.ctx.fillStyle = new ColorMixer(this.ctx, this.element.positionLeftTop(), this.element.size()).mixed(color);
this.ctx.fill();
}
}
drawBorder() {
const borderWidth = this.element.borderWidth();
if (borderWidth > 0) {
const position = this.position();
const size = this.element.size();
this.ctx.beginPath();
this.ctx.ellipse(position.x, position.y, Helper.valueNotLessThan(size.width / 2 - borderWidth / 2, 1), Helper.valueNotLessThan(size.height / 2 - borderWidth / 2, 1), 0, 0, Math.PI * 2);
this.ctx.closePath();
this.ctx.strokeStyle = new ColorMixer(this.ctx, this.element.positionLeftTop(), this.element.size()).mixed(this.element.borderColor() ?? this.defaultBorderColor);
this.ctx.lineWidth = borderWidth;
this.ctx.stroke();
}
}
position() {
const elementPosition = Helper.positionCenter(this.element.position(), this.element.positionPoint(), this.element.boundingClientSize());
const relativePosition = this.relativePosition();
return {
x: elementPosition.x + relativePosition.x,
y: elementPosition.y + relativePosition.y
};
}
defaultAnchor() {
return {
x: 0,
y: 0
};
}
};
//#endregion
//#region src/libraries/artists/text-artist.ts
var TextArtist = class extends ElementArtist {
draw() {
this.prepare();
this.startDrawing();
this.drawStroke();
this.drawFill();
this.endDrawing();
}
prepare() {
this.ctx.font = this.element.font();
this.ctx.textBaseline = "alphabetic";
}
drawFill() {
const color = this.element.fillColor();
if (color) {
const textBox = this.element.textBox();
const position = this.position();
this.ctx.fillStyle = new ColorMixer(this.ctx, this.position(), textBox.size).mixed(color);
textBox.lines.forEach((textLine) => {
this.ctx.fillText(textLine.text, position.x + textLine.position.x, Math.round(position.y + textLine.position.y + textLine.size.height * .75));
});
}
}
drawStroke() {
const width = this.element.strokeWidth();
if (width > 0) {
const textBox = this.element.textBox();
const position = this.position();
const fillColor = this.element.fillColor();
this.ctx.strokeStyle = new ColorMixer(this.ctx, this.position(), textBox.size).mixed(this.element.strokeColor() ?? this.defaultBorderColor);
this.ctx.lineWidth = fillColor ? width * 2 : width;
textBox.lines.forEach((textLine) => {
this.ctx.strokeText(textLine.text, position.x + textLine.position.x, Math.round(position.y + textLine.position.y + textLine.size.height * .75));
});
}
}
position() {
const elementPosition = this.element.positionLeftTop();
const relativePosition = this.relativePosition();
return {
x: elementPosition.x + relativePosition.x,
y: elementPosition.y + relativePosition.y
};
}
defaultAnchor() {
const textBox = this.element.textBox();
return {
x: textBox.size.width / 2,
y: textBox.size.height / 2
};
}
};
//#endregion
//#region src/libraries/artists/image-artist.ts
var ImageArtist = class extends ElementArtist {
draw() {
this.startDrawing();
this.drawBorder();
this.drawImage();
this.endDrawing();
}
drawImage() {
const image = this.element.image();
if (image) {
const source = this.sourceDimensions();
const destination = this.flippedDimensions(this.elementDimensions());
this.ctx.drawImage(image, source.position.x, source.position.y, source.size.width, source.size.height, destination.position.x, destination.position.y, destination.size.width, destination.size.height);
}
}
drawBorder() {
const width = this.element.borderWidth();
const color = new ColorMixer(this.ctx, this.position(), this.element.size()).mixed(this.element.borderColor() ?? this.defaultBorderColor);
const dimensions = this.elementDimensions();
this.ctx.strokeStyle = color;
this.ctx.lineWidth = width * 2;
if (this.isRadiusSet()) {
this.makeShape(dimensions);
this.ctx.lineJoin = "round";
this.ctx.fillStyle = "#ffffff";
this.ctx.fill();
if (width > 0) {
this.ctx.strokeStyle = color;
this.ctx.lineWidth = width * 2;
this.ctx.stroke();
}
this.ctx.clip();
} else if (width > 0) {
this.ctx.strokeStyle = color;
this.ctx.lineWidth = width * 2;
this.ctx.strokeRect(dimensions.position.x, dimensions.position.y, dimensions.size.width, dimensions.size.height);
}
}
sourceDimensions() {
const image = this.element.image();
const cropArea = this.element.cropArea();
const result = {
position: {
x: 0,
y: 0
},
size: cropArea.size ?? {
width: image.width,
height: image.height
}
};
switch (this.element.fit()) {
case ImageFit.Fill:
if (cropArea.position) {
result.position.x = cropArea.position.x;
result.position.y = cropArea.position.y;
}
break;
case ImageFit.Cover: {
const { size } = this.elementDimensions();
if (size.width / size.height < result.size.width / result.size.height) result.size.width = result.size.height / (size.height / size.width);
else result.size.height = result.size.width / (size.width / size.height);
if (!cropArea.position) {
result.position.x = Helper.relativePositionValue("50%", result.size.width, image.width);
result.position.y = Helper.relativePositionValue("50%", result.size.height, image.height);
} else {
result.position.x = cropArea.position.x;
result.position.y = cropArea.position.y;
}
break;
}
}
return result;
}
elementDimensions() {
const borderWidth = this.element.borderWidth();
const position = this.position();
const size = this.element.size();
return {
position: {
x: position.x + borderWidth,
y: position.y + borderWidth
},
size: {
width: size.width - borderWidth * 2,
height: size.height - borderWidth * 2
}
};
}
flippedDimensions(dimensions) {
const flipX = this.element.flipX();
const flipY = this.element.flipY();
if (flipX || flipY) {
let scaleX = 1;
let scaleY = 1;
if (flipX) {
scaleX = -1;
dimensions.position.x = -(+dimensions.position.x + dimensions.size.width);
}
if (flipY) {
scaleY = -1;
dimensions.position.y = -(+dimensions.position.y + dimensions.size.height);
}
this.ctx.scale(scaleX, scaleY);
}
return dimensions;
}
makeShape(dimensions) {
const radius = this.borderRadius();
this.ctx.lineJoin = "round";
this.ctx.beginPath();
this.ctx.moveTo(dimensions.position.x + radius.topLeft, dimensions.position.y);
this.ctx.lineTo(dimensions.position.x + dimensions.size.width - radius.topRight, dimensions.position.y);
this.ctx.quadraticCurveTo(dimensions.position.x + dimensions.size.width, dimensions.position.y, dimensions.position.x + dimensions.size.width, dimensions.position.y + radius.topRight);
this.ctx.lineTo(dimensions.position.x + dimensions.size.width, dimensions.position.y + dimensions.size.height - radius.bottomRight);
this.ctx.quadraticCurveTo(dimensions.position.x + dimensions.size.width, dimensions.position.y + dimensions.size.height, dimensions.position.x + dimensions.size.width - radius.bottomRight, dimensions.position.y + dimensions.size.height);
this.ctx.lineTo(dimensions.position.x + radius.bottomLeft, dimensions.position.y + dimensions.size.height);
this.ctx.quadraticCurveTo(dimensions.position.x, dimensions.position.y + dimensions.size.height, dimensions.position.x, dimensions.position.y + dimensions.size.height - radius.bottomLeft);
this.ctx.lineTo(dimensions.position.x, dimensions.position.y + radius.topLeft);
this.ctx.quadraticCurveTo(dimensions.position.x, dimensions.position.y, dimensions.position.x + radius.topLeft, dimensions.position.y);
this.ctx.closePath();
}
borderRadius() {
const radius = this.element.borderRadius();
if (this.isRadiusSet()) {
const { size } = this.elementDimensions();
Object.keys(radius).forEach((key) => {
if (radius[key] > size.width / 2) radius[key] = Math.floor(size.width / 2);
if (radius[key] > size.height / 2) radius[key] = Math.floor(size.height / 2);
});
}
return radius;
}
isRadiusSet() {
const radius = this.element.borderRadius();
return radius.topLeft > 0 || radius.topRight > 0 || radius.bottomLeft > 0 || radius.bottomRight > 0;
}
position() {
const elementPosition = this.element.positionLeftTop();
const relativePosition = this.relativePosition();
return {
x: elementPosition.x + relativePosition.x,
y: elementPosition.y + relativePosition.y
};
}
defaultAnchor() {
const size = this.element.size();
return {
x: size.width / 2,
y: size.height / 2
};
}
};
//#endregion
//#region src/libraries/shapes/shape.ts
var Shape = class {
_points = [];
_isCloseShape;
_position = {
x: 0,
y: 0
};
_positionPoint = PositionPoint.LeftTop;
_size;
_jointsRadius = 0;
_degrees = 0;
_anchor;
_rotations = [];
constructor(points, isCloseShape = true) {
this._points = points;
this._isCloseShape = isCloseShape;
}
hash() {
return Helper.hash([
this.points(),
this.isCloseShape(),
this.position(),
this.positionPoint(),
this.size(),
this.jointsRadius(),
this.rotations(),
this.degrees(),
this.anchor()
]);
}
points() {
return this._points;
}
isCloseShape() {
return this._isCloseShape;
}
position() {
return this._position;
}
positionPoint() {
return this._positionPoint;
}
size() {
return this._size;
}
jointsRadius() {
return this._jointsRadius;
}
/** @deprecated Use Rotations instead */
degrees() {
return this._degrees;
}
/** @deprecated Use Rotations instead */
anchor() {
return this._anchor;
}
rotations() {
return this._rotations;
}
setPosition(position, point) {
this._position = position;
if (point) this._positionPoint = point;
return this;
}
setSize(value) {
this._size = value;
return this;
}
setJointsRadius(value) {
this._jointsRadius = value;
return this;
}
/** @deprecated Use Rotations instead */
setDegrees(degrees, anchor) {
this._degrees = degrees;
this._anchor = anchor;
return this;
}
setRotations(rotations) {
this._rotations = rotations;
return this;
}
};
//#endregion
//#region src/libraries/artists/polygon-artist.ts
var PolygonArtist = class extends ElementArtist {
draw() {
this.startDrawing();
this.makeShape();
this.drawBorder();
this.drawFill();
this.endDrawing();
}
makeShape() {
const points = this.element.shapePoints();
if (points.length > 2) {
const shape = new Shape(points).setSize(this.shapeSize()).setPosition(this.shapePosition()).setJointsRadius(this.element.borderRadius());
this.ctx.beginPath();
new Shaper(this.ctx, shape).draw();
this.ctx.closePath();
}
}
drawFill() {
const color = this.element.fillColor();
if (color) {
this.ctx.fillStyle = new ColorMixer(this.ctx, this.position(), this.shapeSize()).mixed(color);
this.ctx.fill();
}
}
drawBorder() {
const width = this.element.borderWidth();
if (width > 0) {
const color = new ColorMixer(this.ctx, this.position(), this.element.size()).mixed(this.element.borderColor() ?? this.defaultBorderColor);
const fillColor = this.element.fillColor();
if (this.element.borderRadius() > 0) this.ctx.lineJoin = "round";
this.ctx.strokeStyle = color;
this.ctx.lineWidth = fillColor ? width * 2 : width;
this.ctx.stroke();
}
}
shapePosition() {
const position = this.position();
const borderWidth = this.element.borderWidth();
return {
x: position.x + borderWidth,
y: position.y + borderWidth
};
}
shapeSize() {
const size = this.element.size();
const borderWidth = this.element.borderWidth();
return {
width: size.width - borderWidth * 2,
height: size.height - borderWidth * 2
};
}
position() {
const elementPosition = this.element.positionLeftTop();
const relativePosition = this.relativePosition();
return {
x: elementPosition.x + relativePosition.x,
y: elementPosition.y + relativePosition.y
};
}
defaultAnchor() {
const size = this.element.size();
return {
x: size.width / 2,
y: size.height / 2
};
}
};
//#endregion
//#region src/libraries/artists/line-artist.ts
var LineArtist = class extends ElementArtist {
draw() {
this.startDrawing();
this.makeShape();
this.drawStroke();
this.endDrawing();
}
makeShape() {
this.ctx.beginPath();
const shape = new Shape(this.element.shapePoints(), false).setSize(this.shapeSize()).setPosition(this.shapePosition()).setJointsRadius(this.element.jointsRadius());
new Shaper(this.ctx, shape).draw();
}
drawStroke() {
const color = new ColorMixer(this.ctx, this.position(), this.element.boundingClientSize()).mixed(this.element.color() ?? this.defaultBorderColor);
this.ctx.lineCap = this.element.lineCap();
this.ctx.strokeStyle = color;
this.ctx.lineWidth = this.element.calculatedThickness();
this.ctx.stroke();
}
shapeSize() {
const size = this.element.boundingClientSize();
const thickness = this.element.calculatedThickness();
return {
width: size.width - thickness,
height: size.height - thickness / 2
};
}
shapePosition() {
const position = this.position();
const thickness = this.element.calculatedThickness();
return {
x: position.x + thickness / 2,
y: position.y
};
}
position() {
const elementPosition = this.element.positionLeftTop();
const relativePosition = this.relativePosition();
return {
x: elementPosition.x + relativePosition.x,
y: elementPosition.y + relativePosition.y
};
}
defaultAnchor() {
const size = this.shapeSize();
return {
x: size.width / 2,
y: size.height / 2
};
}
};
//#endregion
//#region src/libraries/canvas.ts
var Canvas = class {
_canvas;
_ctx;
constructor(canvas) {
this._canvas = canvas ?? document.createElement("canvas");
this._ctx = this._canvas.getContext("2d");
}
nativeElement() {
return this._canvas;
}
ctx() {
return this._ctx;
}
size() {
return {
width: this._canvas.width,
height: this._canvas.height
};
}
setSize(size) {
this._canvas.width = size.width;
this._canvas.height = size.height;
}
draw(element, relativeProps) {
switch (element.type()) {
case ElementType.Rectangle:
new RectangleArtist(this.ctx(), element, relativeProps).draw();
break;
case ElementType.Ellipse:
new EllipseArtist(this.ctx(), element, relativeProps).draw();
break;
case ElementType.Polygon:
new PolygonArtist(this.ctx(), element, relativeProps).draw();
break;
case ElementType.Line:
new LineArtist(this.ctx(), element, relativeProps).draw();
break;
case ElementType.Text:
new TextArtist(this.ctx(), element, relativeProps).draw();
break;
case ElementType.Image:
new ImageArtist(this.ctx(), element, relativeProps).draw();
break;
}
}
clear() {
this.ctx().clearRect(0, 0, this._canvas.width, this._canvas.height);
}
};
//#endregion
//#region src/libraries/elements/element.ts
var Element = class {
_position = {
x: 0,
y: 0
};
_positionPoint;
_scale = {
x: 1,
y: 1
};
_opacity = 1;
_rotations = [];
_degrees = 0;
_anchor;
_shadow;
hash() {
return Helper.hash([
this.position(),
this.positionPoint(),
this.scale(),
this.opacity(),
this.rotations(),
this.degrees(),
this.anchor(),
this.shadow()
]);
}
position() {
return Helper.resolvedValue(this._position);
}
positionLeftTop() {
return Helper.positionLeftTop(this.position(), this.positionPoint(), this.boundingClientSize());
}
positionPoint() {
return Helper.resolvedValue(this._positionPoint) ?? PositionPoint.LeftTop;
}
scale() {
return Helper.resolvedValue(this._scale);
}
opacity() {
return Helper.resolvedValue(this._opacity);
}
rotations() {
return Helper.resolvedValue(this._rotations);
}
/** @deprecated Use Rotations instead */
degrees() {
return Helper.resolvedValue(this._degrees);
}
/** @deprecated Use Rotations instead */
anchor() {
return this._anchor ? Helper.resolvedValue(this._anchor) : void 0;
}
shadow() {
return this._shadow ? Helper.resolvedValue(this._shadow) : void 0;
}
setPosition(position, point) {
this._position = position;
if (point) this._positionPoint = point;
return this;
}
setScale(value) {
this._scale = value;
return this;
}
setOpacity(value) {
this._opacity = value;
return this;
}
/** @deprecated Use Rotations instead */
setDegrees(degrees, anchor) {
this._degrees = degrees;
this._anchor = anchor;
return this;
}
setRotations(rotations) {
this._rotations = rotations;
return this;
}
setShadow(shadow) {
this._shadow = shadow;
return this;
}
};
//#endregion
//#region src/libraries/elements/geometric-element.ts
var GeometricElement = class extends Element {
_size = {
width: 1,
height: 1
};
_borderColor;
_borderWidth = 0;
hash() {
return Helper.hash([
super.hash(),
this.size(),
this.borderColor(),
this.borderWidth()
]);
}
size() {
return Helper.resolvedValue(this._size);
}
borderColor() {
return this._borderColor ? Helper.resolvedValue(this._borderColor) : void 0;
}
borderWidth() {
return Helper.resolvedValue(this._borderWidth);
}
setSize(value) {
this._size = value;
return this;
}
setBorder(width, color) {
this._borderWidth = width;
this._borderColor = color;
return this;
}
};
//#endregion
//#region src/libraries/elements/rectangle-element.ts
var RectangleElement = class extends GeometricElement {
_fillColor;
_cutOutShapes = [];
type() {
return ElementType.Rectangle;
}
hash() {
return Helper.hash([
super.hash(),
this.fillColor(),
this.cutOutShapes()
]);
}
fillColor() {
return this._fillColor ? Helper.resolvedValue(this._fillColor) : void 0;
}
cutOutShapes() {
return Helper.resolvedValue(this._cutOutShapes);
}
boundingClientSize() {
return this.size();
}
setFillColor(value) {
this._fillColor = value;
return this;
}
setCutOutShapes(value) {
this._cutOutShapes = value;
return this;
}
};
//#endregion
//#region src/libraries/elements/ellipse-element.ts
var EllipseElement = class extends GeometricElement {
_fillColor;
type() {
return ElementType.Ellipse;
}
hash() {
return Helper.hash([super.hash(), this.fillColor()]);
}
fillColor() {
return this._fillColor ? Helper.resolvedValue(this._fillColor) : void 0;
}
boundingClientSize() {
return this.size();
}
setFillColor(value) {
this._fillColor = value;
return this;
}
};
//#endregion
//#region src/libraries/elements/polygon-element.ts
var PolygonElement = class extends GeometricElement {
_shapePoints = [];
_borderRadius = 0;
_fillColor;
type() {
return ElementType.Polygon;
}
hash() {
return Helper.hash([
super.hash(),
this.shapePoints(),
this.borderRadius(),
this.fillColor()
]);
}
shapePoints() {
return Helper.resolvedValue(this._shapePoints);
}
borderRadius() {
return Helper.resolvedValue(this._borderRadius);
}
fillColor() {
return this._fillColor ? Helper.resolvedValue(this._fillColor) : void 0;
}
boundingClientSize() {
return this.size();
}
setShapePoints(value) {
this._shapePoints = value;
return this;
}
setBorderRadius(value) {
this._borderRadius = value;
return this;
}
setFillColor(value) {
this._fillColor = value;
return this;
}
};
//#endregion
//#region src/libraries/elements/line-element.ts
var LineElement = class extends Element {
_shapePoints = [];
_size;
_keepRatio;
_thickness = 1;
_jointsRadius = 0;
_lineCap = LineCap.Square;
_color;
type() {
return ElementType.Line;
}
hash() {
return Helper.hash([
super.hash(),
this.shapePoints(),
this.size(),
this.keepRatio(),
this.thickness(),
this.jointsRadius(),
this.lineCap(),
this.color()
]);
}
shapePoints() {
return Helper.resolvedValue(this._shapePoints);
}
size() {
return this._size ? Helper.resolvedValue(this._size) : void 0;
}
keepRatio() {
return Helper.resolvedValue(this._keepRatio) ?? false;
}
thickness() {
return Helper.resolvedValue(this._thickness);
}
calculatedThickness() {
const size = this.size();
let thickness = this.thickness();
if (size) {
const originalSize = Helper.shapeSize(this.shapePoints());
const resized = Helper.resizedSize(originalSize, size, this.keepRatio());
const ratioWidth = resized.width / Helper.valueNotLessThan(originalSize.width, 1);
const ratioHeight = resized.height / Helper.valueNotLessThan(originalSize.height, 1);
thickness = Helper.valueNotLessThan(thickness * Math.min(ratioWidth, ratioHeight), 1);
}
return thickness;
}
jointsRadius() {
return Helper.resolvedValue(this._jointsRadius);
}
lineCap() {
return Helper.resolvedValue(this._lineCap);
}
color() {
return this._color ? Helper.resolvedValue(this._color) : void 0;
}
boundingClientSize() {
let size = this.size();
const thickness = this.calculatedThickness();
const originalSize = Helper.shapeSize(this.shapePoints());
if (size) {
const resized = Helper.resizedSize(originalSize, size, this.keepRatio());
size = {
width: resized.width,
height: resized.height + thickness / 2
};
} else size = {
width: originalSize.width,
height: Helper.valueNotLessThan(originalSize.height + thickness / 2, 0)
};
return size;
}
setShapePoints(value) {
this._shapePoints = value;
return this;
}
setSize(value, keepRatio) {
this._size = value;
if (keepRatio) this._keepRatio = keepRatio;
return this;
}
setThickness(value) {
this._thickness = value;
return this;
}
setJointsRadius(value) {
this._jointsRadius = value;
return this;
}
setLineCap(value) {
this._lineCap = value;
return this;
}
setColor(value) {
this._color = value;
return this;
}
};
//#endregion
//#region src/libraries/texts/text-splitter.ts
var TextSplitter = class {
element;
canvas;
ctx;
lastTextBox;
lastTextBoxHash = "";
constructor(element) {
this.element = element;
this.canvas = document.createElement("canvas");
this.ctx = this.canvas.getContext("2d");
}
textBox() {
if (this.elementChanged()) this.lastTextBox = void 0;
if (!this.lastTextBox) this.lastTextBox = this.createTextBox();
return this.lastTextBox;
}
createTextBox() {
const result = {
text: this.element.text(),
lines: [],
size: {
width: 1,
height: 1
},
position: {
x: 0,
y: 0
}
};
result.lines = this.textLines();
result.size = this.textBoxSize(result.lines);
result.position = this.textBoxPosition(result.lines);
return result;
}
textLines() {
const bounds = this.element.bounds();
const padding = this.element.padding();
const boundsWithoutPadding = bounds ? {
width: bounds.width - padding.left - padding.right,
height: bounds.height - padding.top - padding.bottom
} : void 0;
const textLines = this.splitText().map((line) => {
return {
text: line,
size: {
width: this.textWidth(line),
height: this.element.lineHeight()
},
position: {
x: 0,
y: 0
}
};
}).map((line, index, transformed) => {
const previousLine = transformed[index - 1];
line.position = {
x: 0,
y: previousLine ? previousLine.position.y + previousLine.size.height : 0
};
return line;
});
return (boundsWithoutPadding !== void 0 ? this.alignedLines(textLines, boundsWithoutPadding) : textLines).map((line) => {
line.position.x += padding.left;
line.position.y += padding.top;
return line;
});
}
alignedLines(textLines, bounds) {
const horizontalAlign = this.element.horizontalAlign();
const verticalAlign = this.element.verticalAlign();
const totalLinesHeight = textLines.reduce((sum, line) => sum + line.size.height, 0);
return textLines.map((line) => {
if (horizontalAlign === HorizontalAlign.Center) line.position.x += Math.round((bounds.width - line.size.width) / 2);
if (horizontalAlign === HorizontalAlign.Right) line.position.x += bounds.width - line.size.width;
if (verticalAlign === VerticalAlign.Center) line.position.y += Math.round((bounds.height - totalLinesHeight) / 2);
if (verticalAlign === VerticalAlign.Bottom) line.position.y += bounds.height - totalLinesHeight;
return line;
});
}
textBoxSize(textLines) {
const padding = this.element.padding();
const bounds = this.element.bounds() ?? {
width: 1,
height: 1
};
const longestWidth = Math.max(...textLines.map((textLine) => textLine.size.width));
return {
width: Math.max(longestWidth + padding.left + padding.right, bounds.width),
height: Math.max(this.element.lineHeight() * textLines.length + padding.top + padding.bottom, bounds.height)
};
}
textBoxPosition(textLines) {
const padding = this.element.padding();
const smallestX = Math.min(...textLines.map((textLine) => textLine.position.x));
const smallestY = Math.min(...textLines.map((textLine) => textLine.position.y));
return {
x: smallestX - padding.left,
y: smallestY - padding.top
};
}
splitText() {
const lines = this.element.text().replaceAll("<BR>", "<br>").split("<br>");
const padding = this.element.padding();
const maxWidth = (this.element.bounds() ?? {
width: 999999,
height: 999999
}).width - (padding.left + padding.right);
let result = [];
lines.forEach((line) => {
line = line.trim();
const textWidth = this.textWidth(line);
if (textWidth > maxWidth) {
const splitEvery = Math.floor(maxWidth / (textWidth / line.length)) - 3;
const words = line.split(" ");
let tmp = "";
words.forEach((word, index) => {
if (tmp.length + word.length > splitEvery) {
result.push(tmp.trim());
tmp = "";
}
tmp += word + " ";
if (index === words.length - 1) result.push(tmp.trim());
});
result = result.map((line) => line.trim());
} else result.push(line);
});
return result;
}
textWidth(text) {
this.ctx.font = this.element.font();
return Math.ceil(this.ctx.measureText(text).width + this.element.strokeWidth());
}
elementChanged() {
const hash = [
this.element.text(),
this.element.bounds(),
this.element.horizontalAlign(),
this.element.verticalAlign(),
this.element.padding(),
this.element.lineHeight(),
this.element.font(),
this.element.strokeWidth()
].join("|");
const changed = this.lastTextBoxHash !== hash;
this.lastTextBoxHash = hash;
return changed;
}
};
//#endregion
//#region src/libraries/elements/text-element.ts
var TextElement = class extends Element {
_text = "";
_bounds;
_fontSize = 12;
_fontFamily = "Tahoma";
_fontWeight = FontWeight.Normal;
_lineHeight;
_fillColor;
_strokeColor;
_strokeWidth = 0;
_horizontalAlign = HorizontalAlign.Left;
_verticalAlign = VerticalAlign.Top;
_padding = { topOrVerticalOrDefault: 0 };
_textSplitter = new TextSplitter(this);
type() {
return ElementType.Text;
}
hash() {
return Helper.hash([
super.hash(),
this.text(),
this.bounds(),
this.font(),
this.lineHeight(),
this.fillColor(),
this.strokeColor(),
this.strokeWidth(),
this.horizontalAlign(),
this.verticalAlign(),
this.padding()
]);
}
text() {
return Helper.resolvedValue(this._text).toString();
}
bounds() {
return this._bounds ? Helper.resolvedValue(this._bounds) : void 0;
}
fontSize() {
return Helper.resolvedValue(this._fontSize);
}
fontFamily() {
return Helper.resolvedValue(this._fontFamily);
}
fontWeight() {
return Helper.resolvedValue(this._fontWeight);
}
lineHeight() {
return Helper.resolvedValue(this._lineHeight ?? Math.round(this.fontSize() * 1.2));
}
fillColor() {
return this._fillColor ? Helper.resolvedValue(this._fillColor) : void 0;
}
strokeColor() {
return this._strokeColor ? Helper.resolvedValue(this._strokeColor) : void 0;
}
strokeWidth() {
return Helper.resolvedValue(this._strokeWidth);
}
horizontalAlign() {
return Helper.resolvedValue(this._horizontalAlign);
}
verticalAlign() {
return Helper.resolvedValue(this._verticalAlign);
}
padding() {
let result;
const topOrVerticalOrDefault = Helper.resolvedValue(this._padding.topOrVerticalOrDefault);
const rightOrHorizontal = this._padding.rightOrHorizontal ? Helper.resolvedValue(this._padding.rightOrHorizontal) : void 0;
const bottom = this._padding.bottom ? Helper.resolvedValue(this._padding.bottom) : void 0;
const left = this._padding.left ? Helper.resolvedValue(this._padding.left) : void 0;
if (rightOrHorizontal === void 0) result = {
top: topOrVerticalOrDefault,
right: topOrVerticalOrDefault,
bottom: topOrVerticalOrDefault,
left: topOrVerticalOrDefault
};
else if (bottom === void 0 && left === void 0) result = {
top: topOrVerticalOrDefault,
right: rightOrHorizontal,
bottom: topOrVerticalOrDefault,
left: rightOrHorizontal
};
else result = {
top: topOrVerticalOrDefault,
right: rightOrHorizontal,
bottom: bottom ?? 0,
left: left ?? 0
};
return result;
}
font() {
const font = [this.fontSize() + "px", this.fontFamily()];
const weight = this.fontWeight();
if (weight) font.unshift(weight);
return font.join(" ");
}
textBox() {
return this._textSplitter.textBox();
}
boundingClientSize() {
return this.textBox().size;
}
setText(value) {
this._text = value;
return this;
}
setBounds(value) {
this._bounds = value;
return this;
}
setFontSize(value) {
this._fontSize = value;
return this;
}
setFontFamily(value) {
this._fontFamily = value;
return this;
}
setFontWeight(value) {
this._fontWeight = value;
return this;
}
setLineHeight(value) {
this._lineHeight = value;
return this;
}
setFillColor(value) {
this._fillColor = value;
return this;
}
setStroke(width, color) {
this._strokeWidth = width;
this._strokeColor = color;
return this;
}
setHorizontalAlign(value) {
this._horizontalAlign = value;
return this;
}
setVerticalAlign(value) {
this._verticalAlign = value;
return this;
}
setPadding(topOrVerticalOrDefault, rightOrHorizontal, bottom, left) {
this._padding = {
topOrVerticalOrDefault,
rightOrHorizontal,
bottom,
left
};
return this;
}
};
//#endregion
//#region src/libraries/elements/image-element.ts
var ImageElement = class extends GeometricElement {
_image;
_fit = ImageFit.Fill;
_borderRadius = { topLeftOrDefault: 0 };
_flipX;
_flipY;
_cropArea;
type() {
return ElementType.Image;
}
hash() {
return Helper.hash([
super.hash(),
this.image()?.id,
this.fit(),
this.borderRadius(),
this.flipX(),
this.flipY(),
this.cropArea()
]);
}
image() {
return this._image ? Helper.resolvedValue(this._image) : void 0;
}
fit() {
return Helper.resolvedValue(this._fit);
}
borderRadius() {
let result;
const topLeftOrDefault = Helper.resolvedValue(this._borderRadius.topLeftOrDefault);
const topRight = this._borderRadius.topRight ? Helper.resolvedValue(this._borderRadius.topRight) : void 0;
const bottomRight = this._borderRadius.bottomRight ? Helper.resolvedValue(this._borderRadius.bottomRight) : void 0;
const bottomLeft = this._borderRadius.bottomLeft ? Helper.resolvedValue(this._borderRadius.bottomLeft) : void 0;
if (topRight === void 0 && bottomRight === void 0 && bottomLeft === void 0) result = {
topLeft: topLeftOrDefault,
topRight: topLeftOrDefault,
bottomRight: topLeftOrDefault,
bottomLeft: topLeftOrDefault
};
else result = {
topLeft: topLeftOrDefault,
topRight: topRight ?? 0,
bottomRight: bottomRight ?? 0,
bottomLeft: bottomLeft ?? 0
};
return result;
}
flipX() {
return Helper.resolvedValue(this._flipX) ?? false;
}
flipY() {
return Helper.resolvedValue(this._flipY) ?? false;
}
cropArea() {
return Helper.resolvedValue(this._cropArea) ?? {};
}
boundingClientSize() {
return this.size();
}
setImage(value) {
this._image = value;
return this;
}
setFit(value) {
this._fit = value;
return this;
}
setBorderRadius(topLeftOrDefault, topRight, bottomRight, bottomLeft) {
this._borderRadius = {
topLeftOrDefault,
topRight,
bottomRight,
bottomLeft
};
return this;
}
setFlip(value) {
this._flipX = value.horizontal ?? false;
this._flipY = value.vertical ?? false;
return this;
}
setCropArea(value) {
this._cropArea = value;
return this;
}
};
//#endregion
//#region src/libraries/colors/color.ts
var Color = class {};
//#endregion
//#region src/libraries/colors/solid-color.ts
var SolidColor = class extends Color {
_color;
constructor(hexColor, opacity) {
super();
this.setColor(hexColor, opacity);
}
type() {
return ColorType.SolidColor;
}
color() {
return this._color;
}
hash() {
return Helper.hash([this.color()]);
}
setColor(hexColor, opacity) {
const alphaFromHex = hexColor.length === 9 ? hexColor.slice(-2) : void 0;
const alpha = opacity !== void 0 ? this.alphaToHex(opacity) : alphaFromHex ?? "ff";
this._color = hexColor.slice(0, 7) + alpha;
return this;
}
alphaToHex(alpha) {
let hex = Math.round(alpha * 255).toString(16);
if (hex.length === 1) hex = "0" + hex;
return hex;
}
};
//#endregion
//#region src/libraries/colors/gradient-color.ts
var GradientColor = class extends SolidColor {
_offset;
constructor(offset, hexColor, opacity = 1) {
super(hexColor, opacity);
this.setOffset(offset);
}
offset() {
return this._offset;
}
hash() {
return Helper.hash([super.hash(), this.offset()]);
}
setOffset(offset) {
this._offset = Helper.valueInRange(offset, {
min: 0,
max: 1
});
return this;
}
};
//#endregion
//#region src/libraries/colors/gradient.ts
var Gradient = class extends Color {
_colors;
constructor(colors) {
super();
this._colors = colors;
}
colors() {
return this._colors;
}
hash() {
return Helper.hash([this.colors()]);
}
};
//#endregion
//#region src/libraries/colors/linear-gradient.ts
var LinearGradient = class extends Gradient {
_degrees = 0;
type() {
return ColorType.LinearGradient;
}
degrees() {
return this._degrees;
}
setDegrees(value) {
this._degrees = value;
return this;
}
hash() {
return Helper.hash([super.hash(), this.degrees()]);
}
};
//#endregion
//#region src/libraries/colors/radial-gradient.ts
var RadialGradient = class extends Gradient {
_position;
_radiusScale;
type() {
return ColorType.RadialGradient;
}
position() {
return this._position;
}
radiusScale() {
return this._radiusScale ?? 1;
}
setPosition(value) {
this._position = value;
return this;
}
setRadiusScale(value) {
this._radiusScale = value;
return this;
}
hash() {
return Helper.hash([
super.hash(),
this.position(),
this.radiusScale()
]);
}
};
//#endregion
exports.Canvas = Canvas;
exports.ColorType = ColorType;
exports.EllipseElement = EllipseElement;
exports.FontWeight = FontWeight;
exports.GradientColor = GradientColor;
exports.HorizontalAlign = HorizontalAlign;
exports.ImageElement = ImageElement;
exports.ImageFit = ImageFit;
exports.LineCap = LineCap;
exports.LineElement = LineElement;
exports.LinearGradient = LinearGradient;
exports.PolygonElement = PolygonElement;
exports.PositionPoint = PositionPoint;
exports.RadialGradient = RadialGradient;
exports.RectangleElement = RectangleElement;
exports.Shape = Shape;
exports.SolidColor = SolidColor;
exports.TextElement = TextElement;
exports.VerticalAlign = VerticalAlign;
});
import { Element } from '../elements/element';
import { Position } from '../../interfaces/position.interface';
import { RelativeProps } from '../../interfaces/relative-props.interface';
import { Scale } from '../../interfaces/scale.interface';
import { Rotation } from '../../interfaces/rotation.interface';
import { Position, RelativeProps, Scale, Rotation } from '../../types';
export declare abstract class ElementArtist<AnyCanvasElement extends Element> {

@@ -7,0 +4,0 @@ protected readonly ctx: CanvasRenderingContext2D;

import { ElementArtist } from './element-artist';
import { Position } from '../../interfaces/position.interface';
import { Position } from '../../types';
import { EllipseElement } from '../elements/ellipse-element';

@@ -4,0 +4,0 @@ export declare class EllipseArtist extends ElementArtist<EllipseElement> {

import { ElementArtist } from './element-artist';
import { Position } from '../../interfaces/position.interface';
import { ImageElement } from '../elements/image-element';
import { Position } from '../../types';
export declare class ImageArtist extends ElementArtist<ImageElement> {

@@ -5,0 +5,0 @@ draw(): void;

import { ElementArtist } from './element-artist';
import { Position } from '../../interfaces/position.interface';
import { Position } from '../../types';
import { LineElement } from '../elements/line-element';

@@ -4,0 +4,0 @@ export declare class LineArtist extends ElementArtist<LineElement> {

import { ElementArtist } from './element-artist';
import { Position } from '../../interfaces/position.interface';
import { Position } from '../../types';
import { PolygonElement } from '../elements/polygon-element';

@@ -4,0 +4,0 @@ export declare class PolygonArtist extends ElementArtist<PolygonElement> {

import { ElementArtist } from './element-artist';
import { Position } from '../../interfaces/position.interface';
import { Position } from '../../types';
import { RectangleElement } from '../elements/rectangle-element';

@@ -4,0 +4,0 @@ export declare class RectangleArtist extends ElementArtist<RectangleElement> {

import { ElementArtist } from './element-artist';
import { TextElement } from '../elements/text-element';
import { Position } from '../../interfaces/position.interface';
import { Position } from '../../types';
export declare class TextArtist extends ElementArtist<TextElement> {

@@ -5,0 +5,0 @@ draw(): void;

@@ -1,4 +0,3 @@

import { Size } from '../interfaces/size.interface';
import { Element } from './elements/element';
import { RelativeProps } from '../interfaces/relative-props.interface';
import { RelativeProps, Size } from '../types';
export declare class Canvas {

@@ -5,0 +4,0 @@ private readonly _canvas;

import { Color } from './color';
import { Size } from '../../interfaces/size.interface';
import { Position } from '../../interfaces/position.interface';
import { Position, Size } from '../../types';
export declare class ColorMixer {

@@ -5,0 +4,0 @@ private ctx;

@@ -1,2 +0,2 @@

import { ColorType } from '../../enums/color-type';
import { ColorType } from '../../enums';
export declare abstract class Color {

@@ -3,0 +3,0 @@ abstract type(): ColorType;

@@ -1,2 +0,2 @@

import { ColorType } from '../../enums/color-type';
import { ColorType } from '../../enums';
import { Gradient } from './gradient';

@@ -3,0 +3,0 @@ export declare class LinearGradient extends Gradient {

@@ -1,4 +0,4 @@

import { ColorType } from '../../enums/color-type';
import { ColorType } from '../../enums';
import { Gradient } from './gradient';
import { Position } from '../../interfaces/position.interface';
import { Position } from '../../types';
export declare class RadialGradient extends Gradient {

@@ -5,0 +5,0 @@ private _position;

@@ -1,2 +0,2 @@

import { ColorType } from '../../enums/color-type';
import { ColorType } from '../../enums';
import { Color } from './color';

@@ -3,0 +3,0 @@ export declare class SolidColor extends Color {

@@ -1,9 +0,3 @@

import { Position } from '../../interfaces/position.interface';
import { ElementType } from '../../enums/element-type';
import { Shadow } from '../../interfaces/shadow.interface';
import { PositionPoint } from '../../enums/position-point';
import { Size } from '../../interfaces/size.interface';
import { ResolvableAttribute } from '../../types';
import { Scale } from '../../interfaces/scale.interface';
import { Rotation } from '../../interfaces/rotation.interface';
import { ElementType, PositionPoint } from '../../enums';
import { Shadow, Position, Size, Scale, Rotation, ResolvableAttribute } from '../../types';
export declare abstract class Element {

@@ -10,0 +4,0 @@ private _position;

@@ -1,6 +0,5 @@

import { ElementType } from '../../enums/element-type';
import { ElementType } from '../../enums';
import { GeometricElement } from './geometric-element';
import { Color } from '../colors/color';
import { Size } from '../../interfaces/size.interface';
import { ResolvableAttribute } from '../../types';
import { Size, ResolvableAttribute } from '../../types';
export declare class EllipseElement extends GeometricElement {

@@ -7,0 +6,0 @@ private _fillColor?;

import { Element } from './element';
import { Size } from '../../interfaces/size.interface';
import { Color } from '../colors/color';
import { ResolvableAttribute } from '../../types';
import { Size, ResolvableAttribute } from '../../types';
export declare abstract class GeometricElement extends Element {

@@ -6,0 +5,0 @@ private _size;

@@ -1,8 +0,4 @@

import { ElementType } from '../../enums/element-type';
import { ImageFit } from '../../enums/image-fit';
import { BorderRadius } from '../../interfaces/border-radius.interface';
import { ElementType, ImageFit } from '../../enums';
import { GeometricElement } from './geometric-element';
import { ImageSource, ResolvableAttribute } from '../../types';
import { CropArea } from '../../interfaces/crop-area.interface';
import { Size } from '../../interfaces/size.interface';
import { Size, BorderRadius, CropArea, ImageSource, ResolvableAttribute } from '../../types';
export declare class ImageElement extends GeometricElement {

@@ -9,0 +5,0 @@ private _image?;

@@ -1,8 +0,5 @@

import { ElementType } from '../../enums/element-type';
import { Position } from '../../interfaces/position.interface';
import { ElementType, LineCap } from '../../enums';
import { Element } from './element';
import { Size } from '../../interfaces/size.interface';
import { LineCap } from '../../enums/line-cap';
import { Color } from '../colors/color';
import { ResolvableAttribute } from '../../types';
import { Size, Position, ResolvableAttribute } from '../../types';
export declare class LineElement extends Element {

@@ -9,0 +6,0 @@ private _shapePoints;

@@ -1,7 +0,5 @@

import { ElementType } from '../../enums/element-type';
import { Position } from '../../interfaces/position.interface';
import { ElementType } from '../../enums';
import { GeometricElement } from './geometric-element';
import { Color } from '../colors/color';
import { Size } from '../../interfaces/size.interface';
import { ResolvableAttribute } from '../../types';
import { Size, Position, ResolvableAttribute } from '../../types';
export declare class PolygonElement extends GeometricElement {

@@ -8,0 +6,0 @@ private _shapePoints;

@@ -1,7 +0,6 @@

import { ElementType } from '../../enums/element-type';
import { ElementType } from '../../enums';
import { GeometricElement } from './geometric-element';
import { Shape } from '../shapes/shape';
import { GeometricElement } from './geometric-element';
import { Color } from '../colors/color';
import { Size } from '../../interfaces/size.interface';
import { ResolvableAttribute } from '../../types';
import { Size, ResolvableAttribute } from '../../types';
export declare class RectangleElement extends GeometricElement {

@@ -8,0 +7,0 @@ private _fillColor?;

import { Element } from './element';
import { Size } from '../../interfaces/size.interface';
import { ElementType } from '../../enums/element-type';
import { FontWeight } from '../../enums/font-weight';
import { HorizontalAlign } from '../../enums/horizontal-align';
import { Padding } from '../../interfaces/padding.interface';
import { VerticalAlign } from '../../enums/vertical-align';
import { ElementType, FontWeight, HorizontalAlign, VerticalAlign } from '../../enums';
import { Color } from '../colors/color';
import { TextBox } from '../../interfaces/text-box.interface';
import { ResolvableAttribute } from '../../types';
import { Size, Padding, TextBox, ResolvableAttribute } from '../../types';
export declare class TextElement extends Element {

@@ -12,0 +6,0 @@ private _text;

@@ -1,6 +0,3 @@

import { Position } from '../../interfaces/position.interface';
import { Size } from '../../interfaces/size.interface';
import { PositionPoint } from '../../enums/position-point';
import { ExtractedValue } from '../../interfaces/extracted-value.interface';
import { ResolvableAttribute } from '../../types';
import { PositionPoint } from '../../enums';
import { Position, Size, ExtractedValue, ResolvableAttribute } from '../../types';
export declare class Helper {

@@ -7,0 +4,0 @@ static valueInRange(value: number, range: {

@@ -1,5 +0,3 @@

import { Position } from '../../interfaces/position.interface';
import { Size } from '../../interfaces/size.interface';
import { PositionPoint } from '../../enums/position-point';
import { Rotation } from '../../interfaces/rotation.interface';
import { PositionPoint } from '../../enums';
import { Position, Size, Rotation } from '../../types';
export declare class Shape {

@@ -6,0 +4,0 @@ private readonly _points;

import { TextElement } from '../elements/text-element';
import { TextBox } from '../../interfaces/text-box.interface';
import { TextBox } from '../../types';
export declare class TextSplitter {

@@ -4,0 +4,0 @@ private element;

@@ -6,1 +6,64 @@ export type ImageSource = HTMLCanvasElement | HTMLImageElement | HTMLVideoElement;

};
export interface BorderRadius {
topRight: number;
topLeft: number;
bottomLeft: number;
bottomRight: number;
}
export interface CropArea {
size?: Size;
position?: Position;
}
export interface Dimensions {
position: Position;
size: Size;
}
export interface ExtractedValue {
value: number;
unit: 'px' | '%';
}
export interface Padding {
top: number;
right: number;
bottom: number;
left: number;
}
export interface Position {
x: number;
y: number;
}
export interface Size {
width: number;
height: number;
}
export interface Scale {
x: number;
y: number;
}
export interface RelativeProps {
position?: Position;
scale?: Scale;
opacity?: number;
rotations?: Rotation[];
}
export interface Rotation {
degrees: number;
pivot?: Position;
}
export interface Shadow {
color: string;
blur: number;
x: number;
y: number;
}
export interface TextBox {
text: string;
lines: TextLine[];
size: Size;
position: Position;
}
export interface TextLine {
text: string;
size: Size;
position: Position;
}
{
"name": "@armniko/canvas",
"version": "2.0.0",
"description": "Simplified and object-based drawing on HTML canvas!",
"version": "2.1.0",
"description": "A lightweight, object-based abstraction over the HTML Canvas 2D API. Compose drawings declaratively with chainable element builders.",
"author": "Armīns Nikolajevs <armins.nikolajevs@gmail.com>",

@@ -12,10 +12,31 @@ "license": "MIT",

"canvas",
"html5-canvas",
"canvas-2d",
"2d-graphics",
"2d-drawing",
"drawing",
"html canvas",
"object-based"
"graphics",
"rendering",
"typescript",
"object-oriented",
"gradient",
"shapes",
"text-rendering",
"reactive",
"change-detection"
],
"type": "module",
"sideEffects": false,
"main": "./dist/index.umd.js",
"module": "./dist/index.esm.js",
"typings": "./dist/index.d.ts",
"type": "module",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.esm.js",
"require": "./dist/index.umd.js",
"default": "./dist/index.umd.js"
},
"./package.json": "./package.json"
},
"files": [

@@ -39,5 +60,5 @@ "dist"

"@types/node": "^24.12.2",
"@typescript-eslint/eslint-plugin": "^8.58.2",
"@typescript-eslint/parser": "^8.58.2",
"@vitest/coverage-v8": "^4.1.4",
"@typescript-eslint/eslint-plugin": "^8.59.0",
"@typescript-eslint/parser": "^8.59.0",
"@vitest/coverage-v8": "^4.1.5",
"eslint": "^10.2.1",

@@ -49,10 +70,9 @@ "eslint-config-prettier": "^10.1.8",

"prettier": "^3.8.3",
"puppeteer": "^24.41.0",
"terser": "^5.46.1",
"puppeteer": "^24.42.0",
"typescript": "^6.0.3",
"typescript-eslint": "^8.58.2",
"vite": "^8.0.8",
"typescript-eslint": "^8.59.0",
"vite": "^8.0.10",
"vite-plugin-dts": "^4.5.4",
"vitest": "^4.1.4"
"vitest": "^4.1.5"
}
}

@@ -8,8 +8,18 @@ # Canvas

Simplified and object-based drawing on HTML canvas!
A lightweight, object-based abstraction over the HTML Canvas 2D API. Compose drawings declaratively with chainable
element builders.
<hr>
### Why Canvas?
- 🧩 **Object-based** — describe what to draw, not how to draw it
- 🧮 **Reactive-friendly** — pass functions (or signals) into any setter; attributes update with your app state, no
manual rewiring
- 🔍 **Built-in change detection** — every element exposes `getHash()`, making memoization and dirty-checking trivial
- 🌐 **Framework-agnostic** — drop into Vue, Angular, React, or vanilla TS
- 📦 **Zero dependencies** and fully typed
### Installation
```
```bash
npm install @armniko/canvas

@@ -59,2 +69,11 @@ ```

<tr>
<td>v2.1.0</td>
<td>
<b>Technical:</b><br>
- removed minification of build<br>
- added <code>exports</code> field for proper module resolution and types<br>
- marked package as side-effect free
</td>
</tr>
<tr>
<td>v2.0.0</td>

@@ -61,0 +80,0 @@ <td>

export declare enum ColorType {
SolidColor = "solid-color",
LinearGradient = "linear-gradient",
RadialGradient = "radial-gradient"
}
export declare enum ElementType {
Rectangle = "rectangle",
Ellipse = "ellipse",
Polygon = "polygon",
Line = "line",
Text = "text",
Image = "image"
}
export declare enum FontWeight {
Normal = "normal",
Bold = "bold"
}
export declare enum HorizontalAlign {
Left = "left",
Center = "center",
Right = "right"
}
export declare enum ImageFit {
Fill = "fill",
Cover = "cover"
}
export declare enum LineCap {
Round = "round",
Square = "square"
}
export declare enum PositionPoint {
LeftTop = "left-top",
RightTop = "right-top",
LeftBottom = "left-bottom",
RightBottom = "right-bottom",
Center = "center"
}
export declare enum VerticalAlign {
Top = "top",
Center = "center",
Bottom = "bottom"
}
export interface BorderRadius {
topRight: number;
topLeft: number;
bottomLeft: number;
bottomRight: number;
}
import { Size } from './size.interface';
import { Position } from './position.interface';
export interface CropArea {
size?: Size;
position?: Position;
}
import { Size } from './size.interface';
import { Position } from './position.interface';
export interface Dimensions {
position: Position;
size: Size;
}
export interface ExtractedValue {
value: number;
unit: 'px' | '%';
}
export interface Padding {
top: number;
right: number;
bottom: number;
left: number;
}
export interface Position {
x: number;
y: number;
}
/**
* @deprecated This interface is deprecated. Use `Position` instead.
*/
export type PositionInterface = Position;
import { Position } from './position.interface';
import { Scale } from './scale.interface';
import { Rotation } from './rotation.interface';
export interface RelativeProps {
position?: Position;
scale?: Scale;
opacity?: number;
rotations?: Rotation[];
}
import { Position } from './position.interface';
export interface Rotation {
degrees: number;
pivot?: Position;
}
export interface Scale {
x: number;
y: number;
}
export interface Shadow {
color: string;
blur: number;
x: number;
y: number;
}
export interface Size {
width: number;
height: number;
}
/**
* @deprecated This interface is deprecated. Use `Size` instead.
*/
export type SizeInterface = Size;
import { Size } from './size.interface';
import { Position } from './position.interface';
import { TextLine } from './text-line.interface';
export interface TextBox {
text: string;
lines: TextLine[];
size: Size;
position: Position;
}
import { Size } from './size.interface';
import { Position } from './position.interface';
export interface TextLine {
text: string;
size: Size;
position: Position;
}