@aurigma/design-atoms
Version:
Design Atoms is a part of Customer's Canvas SDK which allows for manipulating individual design elements through your code.
885 lines • 55 kB
JavaScript
import { RotatedRectangleF, PointF, RectangleF, Path, Matrix, Transform, Environment, SurfaceContainer, MockupContainer, EqualsOfFloatNumbers, SizeF } from "@aurigma/design-atoms-model";
import { toTextWhizzPath, MobileTextEditor } from "@aurigma/design-atoms-text";
import { Graphics } from "./Graphics";
import { RenderState, PlaceholderItemHandler, NewBaseTextItemHandler, GroupItemHandler } from "./ItemHandlers";
import { PlaceholderEditingViewMode, InteractiveZoneLabelPosition } from "./Viewer/Interfaces";
import { CanvasRendererStyle } from "./Canvas/CanvasRendererStyle";
import { CoordinatesConvertUtils } from "./Utils/CoordinatesConvertUtils";
import { parseMargin } from "./Utils";
import { Margin } from "@aurigma/design-atoms-model/Math/Margin";
import { StringUtils } from "./Utils/StringUtils";
import { ViolationState } from "./Services";
import { MaxArtworkAreaViolation } from "./Services/ViolationService/Violations/MaxArtworkAreaViolation";
const INNER_BORDER = 1;
const OUTER_BORDER = 2;
const TRANSPARENT = "rgba(0,0,0,0)";
export class CanvasRenderer {
get violationSrc() {
return this._violationSrc;
}
set violationSrc(service) {
this._violationSrc = service;
}
constructor(_selection, _dndHandler, handlerConfiguration, _rubberbandHandler, _viewportHandler, _spotlightHandler, _hoverHandler, _snapLinesHandler, _canvasElementHandler, _interactiveZonesHandler, _isPreviewRenderer = false) {
this._selection = _selection;
this._dndHandler = _dndHandler;
this._rubberbandHandler = _rubberbandHandler;
this._viewportHandler = _viewportHandler;
this._spotlightHandler = _spotlightHandler;
this._hoverHandler = _hoverHandler;
this._snapLinesHandler = _snapLinesHandler;
this._canvasElementHandler = _canvasElementHandler;
this._interactiveZonesHandler = _interactiveZonesHandler;
this._isPreviewRenderer = _isPreviewRenderer;
this._loadingImage = new Image();
this._rotationIconImage = null;
this._snapLineMarkSize = 2;
this.style = new CanvasRendererStyle();
this.previewMode = false;
this._loadRotationIconImage = () => {
const brandPrimaryColor = getComputedStyle(document.documentElement).getPropertyValue("--de-brand-primary");
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("width", "16");
svg.setAttribute("height", "16");
svg.setAttribute("viewBox", "0 0 16 16");
svg.setAttribute("fill", "none");
svg.innerHTML = `<path d="M11.151 2.54293C14.1643 4.28262 15.1967 8.13564 13.457 11.1489C13.175 11.6374 12.8374 12.0738 12.4569 12.4548L10.5467 14.3629M4.85101 13.4548C1.83777 11.7151 0.805356 7.86213 2.54505 4.84888C2.82707 4.36041 3.16463 3.924 3.54514 3.54301L5.4555 1.63498M10.5456 11.182L10.5467 14.3629M10.5467 14.3629L13.7275 14.364M5.45439 4.81807L5.4555 1.63498M5.4555 1.63498L2.27241 1.63609" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>`;
svg.style.color = brandPrimaryColor;
const xml = new XMLSerializer().serializeToString(svg);
const svg64 = btoa(xml);
const b64Start = 'data:image/svg+xml;base64,';
const image64 = b64Start + svg64;
const image = document.createElement("img");
image.onload = () => this._rotationIconImage = image;
image.src = image64;
};
this.conf = handlerConfiguration;
this._loadRotationIconImage();
}
set textRenderer(value) {
this._textRenderer = value;
}
set conf(conf) {
this._handlerConfiguration = Object.assign({ placeholderEditingViewMode: PlaceholderEditingViewMode.Overlay }, (conf || {}));
}
setTextWhizzModule(textWhizz) {
this._textWhizz = textWhizz;
}
redrawActiveTextCanvas(context, layers) {
if (this._textRenderer == null)
return;
let excludeEditedText = context.textEditor instanceof MobileTextEditor;
let allItemHandlers = this._getItemHandlersByRenderState(layers, RenderState.Active, excludeEditedText);
let textItemHandlers = [];
allItemHandlers.forEach(handler => {
if (handler instanceof NewBaseTextItemHandler)
textItemHandlers.push(handler);
if (handler instanceof GroupItemHandler) {
const nestedTextHandlers = handler.getNestedItemHandlers().filter((childHandler) => childHandler instanceof NewBaseTextItemHandler);
textItemHandlers.push(...nestedTextHandlers);
}
});
this._textRenderer.clearText();
textItemHandlers.forEach(handler => {
var _a, _b;
if (((_a = handler.textEditorController) === null || _a === void 0 ? void 0 : _a.isActive) || (((_b = handler.textEditorController) === null || _b === void 0 ? void 0 : _b.isInEdit) && context.textEditor.redrawActiveTextInEditMode))
handler.redrawActiveText();
});
}
redrawActiveCanvas(layers, activeCanvas, workspace, productHandler, offset) {
const activeCtx = this._getScaledContext(activeCanvas, workspace, offset);
if (activeCtx == null)
return;
this._getItemHandlersByRenderState(layers, RenderState.Active, false)
.forEach(i => this._drawHandler(i, activeCtx, productHandler, workspace, offset));
const selectedItemHandlers = this._selection.selectedItemHandlers;
const multiselection = selectedItemHandlers.length > 1;
if (!multiselection) {
const current = this._selection.currentItemHandler;
if (current != null && this._isEditing(current)) {
if (this._handlerConfiguration.placeholderEditingViewMode === PlaceholderEditingViewMode.Overlay && current instanceof PlaceholderItemHandler) {
this._drawHandler(current, activeCtx, productHandler, workspace, offset);
}
}
}
this._dndHandler.draw();
activeCtx.restore();
}
redrawSelectionCanvas(designCanvas, workspace, style, textContext, viewer, contentAngle, offset, layers, messages, drawShadow, drawSnapLines) {
const designCtx = this._getScaledContext(designCanvas, workspace, offset, false);
if (designCtx == null)
return;
if (drawShadow) {
this._drawShadow(designCtx, workspace, offset);
}
this._drawInteractiveZones(designCtx);
const violationInfo = this._getMaxArtworkSizeViolation(layers);
if (this._needDrawMaxArtwork(violationInfo)) {
this._drawMaxArtwork(designCtx, violationInfo, style, messages);
}
this._drawSpotlights(designCtx, style);
this._drawHighlight(designCtx, textContext);
this._drawHover(designCtx);
this._drawSelection(designCtx, viewer, offset, contentAngle);
if (drawSnapLines) {
this._drawSnapLines(designCtx, contentAngle);
}
this._rubberbandHandler.draw(designCtx, this, offset);
this._drawLimitsMessage(designCtx, textContext);
designCtx.restore();
}
redrawInactiveCanvas(layers, surfaceCanvas, inactiveCanvas, workspace, margins, mul, productHandler, offset, mockupMarginWorkaroundEnabled, canvasBackground) {
const surfaceCtx = this._getScaledContext(surfaceCanvas, workspace, offset);
const inactiveCtx = this._getScaledContext(inactiveCanvas, workspace, offset);
if (surfaceCtx == null || inactiveCtx == null)
return;
this._drawMargins(surfaceCtx, margins, workspace, mul, offset);
let drawnMockupWorkaround = false;
const drawMockupWorkaroundMargin = (container, context) => {
if (mockupMarginWorkaroundEnabled === false)
return;
if (drawnMockupWorkaround || container instanceof MockupContainer === false)
return;
context.restore();
this.applyContextScale(context, workspace, offset, false);
this._drawMockupWorkaround(context, workspace.width, workspace.height, offset, canvasBackground);
context.restore();
this.applyContextScale(context, workspace, offset);
drawnMockupWorkaround = true;
};
this._getItemHandlersByRenderState(layers, RenderState.Bottom, false).forEach(i => {
drawMockupWorkaroundMargin(i.layer.container, surfaceCtx);
this._drawHandler(i, surfaceCtx, productHandler, workspace, offset);
});
this._getItemHandlersByRenderState(layers, RenderState.Top, false).forEach(i => {
drawMockupWorkaroundMargin(i.layer.container, inactiveCtx);
this._drawHandler(i, inactiveCtx, productHandler, workspace, offset);
});
inactiveCtx.restore();
surfaceCtx.restore();
}
drawRubberband(context, rubberband, offset) {
if (offset != null && !offset.isOrigin()) {
rubberband = rubberband.translate(new PointF(-offset.x, -offset.y));
}
const transform = context.getTransform();
const matrix = new Matrix(transform.a, transform.b, transform.c, transform.d, transform.e, transform.f);
context.setTransform(1, 0, 0, 1, 0, 0);
const rectCenterX = rubberband.left + (rubberband.right - rubberband.left) / 2;
const rectCenterY = rubberband.top + (rubberband.bottom - rubberband.top) / 2;
const rectWidth = (rubberband.right - rubberband.left);
const rectHeight = (rubberband.bottom - rubberband.top);
let rect = new RotatedRectangleF(rectCenterX, rectCenterY, rectWidth, rectHeight, 0);
rect = this._applyMatrixToRectangle(rect, matrix);
const rb = this.style.rubberband;
Graphics.rectangle(context, rect, rb.width * Environment.devicePixelRatio, rb.color, rb.fillColor, 1, rb.dashPattern);
context.setTransform(transform);
}
drawWaitClock(context, center, mul) {
const rect = new RotatedRectangleF(center.x || 0, center.y || 0, this._loadingImage.width / mul, this._loadingImage.height / mul, 0);
if (this._loadingImage.complete) {
Graphics.drawImage(context, this._loadingImage, rect, mul, mul);
}
}
setLoadingImageUrl(url) {
if (url != null) {
this._loadingImage.crossOrigin = "anonymous";
this._loadingImage.src = url;
}
}
drawTextCursor(canvas, itemHandler, callback, workspace, mul, offset) {
const renderingContext = this._getScaledContext(canvas, workspace, offset);
if (renderingContext == null)
return;
const matrix = itemHandler.item.transform.toMatrix();
const center = itemHandler.getControlCenter();
renderingContext.translate(center.x, center.y);
renderingContext.transform(matrix.m00, matrix.m10, matrix.m01, matrix.m11, matrix.m02, matrix.m12);
renderingContext.translate(-center.x, -center.y);
callback(renderingContext);
renderingContext.strokeStyle = "white";
renderingContext.lineWidth = 1 / mul;
renderingContext.stroke();
renderingContext.restore();
}
clearTextCursor(canvas) {
const context = this._getContext(canvas);
if (context == null)
return;
context.restore();
}
applyContextScale(renderingContext, workspace, offset, clipByWorkspace = true) {
renderingContext.save();
const canvas = renderingContext.canvas;
this._viewportHandler.applyViewportTransformTo2dContext(renderingContext, canvas);
const scale = Environment.screenDpi * workspace.zoom / 72;
renderingContext.scale(scale, scale);
if (offset != null && !offset.isOrigin()) {
renderingContext.translate(offset.x, offset.y);
}
if (clipByWorkspace) {
this._contextSetClipping(renderingContext, workspace.width, workspace.height, offset);
}
return renderingContext;
}
_getMaxArtworkSizeViolation(layers) {
if (this.violationSrc == null)
return null;
return layers.map(l => l.itemHandlers.toArray())
.flat()
.map((handler) => this._violationSrc.getViolationInfoByType(MaxArtworkAreaViolation, handler.item))
.filter((info) => info != null)
.find((info) => info.state === ViolationState.Bad);
}
_drawMaxArtwork(context, violationInfo, style, messages) {
const data = violationInfo.data;
const overallBounds = data.overallBounds;
const { width, height, shapeType } = data.constraints;
if (shapeType === 'ellipse') {
if (width === height) {
this._drawCircleMaxArtwork(context, overallBounds, data.constraints, style.artworkSize, messages);
}
else {
this._drawEllipseMaxArtwork(context, overallBounds, data.constraints, style.artworkSize, messages);
}
}
else {
this._drawRectangleMaxArwork(context, overallBounds, data.constraints, style.artworkSize, messages);
}
}
_drawEllipseMaxArtwork(context, overallBounds, constraints, style, messages) {
const { width, height } = constraints;
const { artworkFrameColor, artworkFrameTextFont, artworkFrameTextFontSize, artworkFrameShowLabel, oversizeFrameColor, artworkInnerBorderFrameDash: oversizeFrameDash } = style;
const font = `normal ${artworkFrameTextFontSize}px ${artworkFrameTextFont}`;
const label = messages.maxArtworkSizeCapture;
if (artworkFrameShowLabel) {
const textPoint = new PointF(overallBounds.center.x, (overallBounds.center.y - height / 2) - 6);
Graphics.text(context, label, textPoint, font, artworkFrameColor, null, 0, null, "center");
}
const overallRect = new RectangleF(overallBounds.left - 1, overallBounds.top - 1, overallBounds.width + 2, overallBounds.height + 2);
Graphics.drawRectangle(context, RotatedRectangleF.fromRectangleF(overallRect, 0), 1, oversizeFrameColor, 1);
const innerEllipseSize = new SizeF(width, height);
Graphics.drawEllipse(context, overallBounds.center, innerEllipseSize, TRANSPARENT, INNER_BORDER, artworkFrameColor, 1, oversizeFrameDash);
const outEllipseSize = new SizeF(width + 4, height + 4);
Graphics.drawEllipse(context, overallBounds.center, outEllipseSize, TRANSPARENT, OUTER_BORDER, artworkFrameColor);
}
_drawCircleMaxArtwork(context, overallBounds, constraints, style, messages) {
const { width, height } = constraints;
const { artworkFrameColor, artworkFrameTextFont, artworkFrameTextFontSize, artworkFrameShowLabel, oversizeFrameColor, artworkInnerBorderFrameDash: oversizeFrameDash } = style;
const font = `normal ${artworkFrameTextFontSize}px ${artworkFrameTextFont}`;
const label = messages.maxArtworkSizeCapture;
if (artworkFrameShowLabel) {
const textPoint = new PointF(overallBounds.center.x, (overallBounds.center.y - constraints.height / 2) - 6);
Graphics.text(context, label, textPoint, font, artworkFrameColor, null, 0, null, "center");
}
const diametr = Math.sqrt(overallBounds.width ** 2 + overallBounds.height ** 2);
const overallEllipseSize = new SizeF(diametr + 1, diametr + 1);
Graphics.drawEllipse(context, overallBounds.center, overallEllipseSize, TRANSPARENT, 1, oversizeFrameColor, 1);
const innerEllipseSize = new SizeF(width, height);
Graphics.drawEllipse(context, overallBounds.center, innerEllipseSize, TRANSPARENT, INNER_BORDER, artworkFrameColor, 1, oversizeFrameDash);
const outEllipseSize = new SizeF(width + 4, height + 4);
Graphics.drawEllipse(context, overallBounds.center, outEllipseSize, TRANSPARENT, OUTER_BORDER, artworkFrameColor);
}
_drawRectangleMaxArwork(context, overallBounds, constraints, style, messages) {
const { width, height } = constraints;
const { artworkFrameColor, artworkFrameTextFont, artworkFrameTextFontSize, artworkFrameShowLabel, oversizeFrameColor, artworkInnerBorderFrameDash: oversizeFrameDash } = style;
const font = `normal ${artworkFrameTextFontSize}px ${artworkFrameTextFont}`;
const label = messages.maxArtworkSizeCapture;
const transform = new Transform(1, 1, -width / 2, -height / 2);
if (artworkFrameShowLabel) {
const pointOfLabel = new PointF(overallBounds.center.x - 6, overallBounds.center.y - 8).transform(transform, overallBounds.center);
Graphics.text(context, label, pointOfLabel, font, artworkFrameColor);
}
const overallRect = new RectangleF(overallBounds.left - 1, overallBounds.top - 1, overallBounds.width + 2, overallBounds.height + 2);
Graphics.drawRectangle(context, RotatedRectangleF.fromRectangleF(overallRect, 0), 1, oversizeFrameColor, 1);
const outerRect = new RectangleF(overallBounds.center.x - 4, overallBounds.center.y - 4, width + 8, height + 8).updateByMatrix(transform.toMatrix());
Graphics.drawRectangle(context, RotatedRectangleF.fromRectangleF(outerRect, 0), OUTER_BORDER, artworkFrameColor, 1);
const innerRect = new RectangleF(overallBounds.center.x - 1, overallBounds.center.y - 1, width + 2, height + 2).updateByMatrix(transform.toMatrix());
Graphics.rectangle(context, RotatedRectangleF.fromRectangleF(innerRect, 0), INNER_BORDER, artworkFrameColor, TRANSPARENT, 1, oversizeFrameDash);
}
_needDrawMaxArtwork(violationInfo) {
var _a, _b, _c;
if (!violationInfo)
return false;
if (violationInfo.state === ViolationState.Bad)
return true;
const isIntaracting = ((_a = this._selection) === null || _a === void 0 ? void 0 : _a.isRotating) || ((_b = this._selection) === null || _b === void 0 ? void 0 : _b.isDragging) || ((_c = this._selection) === null || _c === void 0 ? void 0 : _c.isResizing);
if (isIntaracting)
return true;
}
_getScaleFactor(ctx) {
const transform = ctx.getTransform();
const matrix = new Matrix(transform.a, transform.b, transform.c, transform.d, transform.e, transform.f);
const rect = new RectangleF(0, 0, 1, 1);
const scaledRect = rect.updateByMatrix(matrix);
return Math.abs(scaledRect.width);
}
_drawInteractiveZones(ctx) {
if (this._interactiveZonesHandler == null)
return;
const zones = this._interactiveZonesHandler.interactiveZones;
if (zones == null || zones.length == 0)
return;
const scaleFactor = this._getScaleFactor(ctx);
const activeZone = this._interactiveZonesHandler.activeZone;
const highlightedZone = this._interactiveZonesHandler.highlightZone;
for (let zone of zones) {
if (zone == activeZone)
continue;
if (zone == highlightedZone)
continue;
this._drawInteractiveZone(ctx, zone, false, false, scaleFactor);
}
if (activeZone != null && activeZone != highlightedZone)
this._drawInteractiveZone(ctx, activeZone, true, false, scaleFactor);
if (highlightedZone != null && highlightedZone != activeZone)
this._drawInteractiveZone(ctx, highlightedZone, false, true, scaleFactor);
if (activeZone != null && activeZone == highlightedZone)
this._drawInteractiveZone(ctx, activeZone, true, true, scaleFactor);
}
_drawInteractiveZone(ctx, zone, isActive, isHover, scaleFactor) {
var _a, _b, _c, _d, _e, _f;
if (this._interactiveZonesHandler.isReady() == false)
return;
const style = this._interactiveZonesHandler.getZoneStyle(zone, isActive, isHover);
let dash = [];
if (((_a = style.borderDash) === null || _a === void 0 ? void 0 : _a.length) > 0) {
for (let i = 0; i < style.borderDash.length; i++) {
dash[i] = style.borderDash[i] / scaleFactor;
}
}
const width = style.borderWidth / scaleFactor;
if (style.fillColor) {
Graphics.fillPath(ctx, zone.path, new PointF(), new Transform(), style.fillColor);
}
Graphics.drawPath(ctx, zone.path, new PointF(), new Transform(), width, style.borderColor, 1, dash, style.borderAltColor);
const title = (_b = style.labelTitle) !== null && _b !== void 0 ? _b : zone.name;
if (style.showLabel && title) {
const transform = ctx.getTransform();
const matrix = new Matrix(transform.a, transform.b, transform.c, transform.d, transform.e, transform.f);
ctx.setTransform(1, 0, 0, 1, 0, 0);
const { point, align, baseline } = this._getLabelPosition(zone, style, matrix);
const margin = parseMargin((_c = style.labelMargin) !== null && _c !== void 0 ? _c : 3);
const labelColor = (_d = style.labelColor) !== null && _d !== void 0 ? _d : "black";
const font = (_e = style.labelFont) !== null && _e !== void 0 ? _e : "Roboto";
const pxRatioRounded = Math.round(Environment.devicePixelRatio);
const fontSize = ((_f = style.labelFontSize) !== null && _f !== void 0 ? _f : 24) * pxRatioRounded;
Graphics.text(ctx, StringUtils.toSingleLine(title), point, `${fontSize}pt ${font}`, labelColor, null, 0, null, align, baseline, style.labelBackgroundColor, null, null, margin);
ctx.setTransform(transform);
}
}
_getLabelPosition(zone, style, matrix) {
var _a;
const bounds = this._interactiveZonesHandler.getZoneBounds(zone);
const boundsRect = this._applyMatrixToRectangle(RotatedRectangleF.fromRectangleF(bounds), matrix);
if (style.labelPosition == InteractiveZoneLabelPosition.outside) {
const margin = parseMargin((_a = style.labelMargin) !== null && _a !== void 0 ? _a : 3);
const pt = boundsRect.getUpperLeftCorner().translate(margin.left - 1, -margin.bottom + 1);
return { point: pt, align: "left", baseline: "bottom" };
}
else {
return { point: boundsRect.center, align: "center", baseline: "middle" };
}
}
_drawShadow(ctx, workspace, offset) {
const transform = ctx.getTransform();
const matrix = new Matrix(transform.a, transform.b, transform.c, transform.d, transform.e, transform.f);
ctx.setTransform(1, 0, 0, 1, 0, 0);
const left = offset != null ? -offset.x : 0;
const top = offset != null ? -offset.y : 0;
const rotRect = RotatedRectangleF.fromRectangleF(new RectangleF(left, top, workspace.width, workspace.height));
const rect = this._applyMatrixToRectangle(rotRect, matrix);
Graphics.rectangle(ctx, rect, 1, "gray", null, 0.5);
rect.width += 2;
rect.height += 2;
Graphics.rectangle(ctx, rect, 1, "gray", null, 0.3);
rect.width += 2;
rect.height += 2;
Graphics.rectangle(ctx, rect, 1, "gray", null, 0.2);
rect.width += 2;
rect.height += 2;
Graphics.rectangle(ctx, rect, 1, "gray", null, 0.1);
rect.width += 2;
rect.height += 2;
Graphics.rectangle(ctx, rect, 1, "gray", null, 0.1);
ctx.setTransform(transform);
}
_applyMatrixToPoint(point, matrix) {
point = matrix.transformPoint(point, true);
this._alignPoint(point);
return point;
}
_applyMatrixToRectangle(rect, matrix, align = false) {
const rect1 = rect.toRectangleF();
rect1.updateByMatrix(matrix);
const rect2 = RectangleF.FromLTRB(Math.floor(rect1.left), Math.floor(rect1.top), Math.ceil(rect1.right), Math.ceil(rect1.bottom));
const rect3 = RotatedRectangleF.fromRectangleF(rect2, rect.angle);
if (align) {
this._alignRectangle(rect3);
}
return rect3;
}
_drawSnapLines(ctx, contentAngle) {
const transform = ctx.getTransform();
const matrix = new Matrix(transform.a, transform.b, transform.c, transform.d, transform.e, transform.f);
ctx.setTransform(1, 0, 0, 1, 0, 0);
const drawVertical = this._selection.manipulationPermissions.allowMoveVertical;
const drawHorizontal = this._selection.manipulationPermissions.allowMoveHorizontal;
const getLimitPoints = (position, limitValues, matrix, isVertical) => {
if (limitValues == null || limitValues.length == 0)
return null;
if (this._snapLinesHandler.currentItemRectangle == null)
return null;
const getPosValue = (pt) => isVertical ? pt.x : pt.y;
const getOtherValue = (pt) => isVertical ? pt.y : pt.x;
const curRect = this._snapLinesHandler.currentItemRectangle;
let possiblePointsAll = [curRect.getUpperLeftCorner(), curRect.getUpperRightCorner(), curRect.center, curRect.getBottomLeftCorner(), curRect.getBottomRightCorner()];
let possiblePoints = possiblePointsAll
.filter(pt => EqualsOfFloatNumbers(getPosValue(pt), position));
const values = [...limitValues, ...possiblePoints.map(pt => getOtherValue(pt))];
const min = Math.min(...values);
const max = Math.max(...values);
const middle = values.filter(v => v > min && v < max);
const resultNumbers = [min, max, ...middle];
if (isVertical) {
return resultNumbers.map(v => this._applyMatrixToPoint(new PointF(position, v), matrix));
}
else {
return resultNumbers.map(v => this._applyMatrixToPoint(new PointF(v, position), matrix));
}
};
if (drawVertical) {
const verticalLineData = this._snapLinesHandler.getVerticalLineData();
if (verticalLineData != null) {
let pt = new PointF(verticalLineData.anchor.position, 0);
pt = this._applyMatrixToPoint(pt, matrix);
const limitPoints = getLimitPoints(verticalLineData.anchor.position, verticalLineData.anchor.limitPoints, matrix, true);
if (contentAngle == 90 || contentAngle == 270) {
this._drawHorizontalLine(ctx, pt.y, verticalLineData.config.color, limitPoints);
}
else {
this._drawVerticalLine(ctx, pt.x, verticalLineData.config.color, limitPoints);
}
}
}
if (drawHorizontal) {
const horizontalLineData = this._snapLinesHandler.getHorizontalLineData();
if (horizontalLineData != null) {
const limitPoints = getLimitPoints(horizontalLineData.anchor.position, horizontalLineData.anchor.limitPoints, matrix, false);
let pt = new PointF(0, horizontalLineData.anchor.position);
pt = this._applyMatrixToPoint(pt, matrix);
if (contentAngle == 90 || contentAngle == 270) {
this._drawVerticalLine(ctx, pt.x, horizontalLineData.config.color, limitPoints);
}
else {
this._drawHorizontalLine(ctx, pt.y, horizontalLineData.config.color, limitPoints);
}
}
}
ctx.setTransform(transform);
}
_getResizeIndex(contentAngle, rectSigns) {
let index = this._selection.resizeIndex;
if (rectSigns.x == -1) {
const xremap = [0, 2, 1, 4, 3, 7, 6, 5, 8];
index = xremap[index];
}
if (rectSigns.y == -1) {
const yremap = [0, 4, 3, 2, 1, 5, 8, 7, 6];
index = yremap[index];
}
let remap = null;
switch (contentAngle) {
case 90:
remap = [0, 2, 3, 4, 1, 6, 7, 8, 5];
break;
case 180:
remap = [0, 3, 4, 1, 2, 7, 8, 5, 6];
break;
case 270:
remap = [0, 4, 1, 2, 3, 8, 5, 6, 7];
break;
default:
remap = [0, 1, 2, 3, 4, 5, 6, 7, 8];
break;
}
return remap[index];
}
_drawSelection(renderingContext, context, offset, contentAngle) {
this._selection.isDragging;
const selectedItemHandlers = this._selection.selectedItemHandlers;
const multiselection = selectedItemHandlers.length > 1;
if (!multiselection) {
const current = this._selection.currentItemHandler;
if (current != null && this._isEditing(current)) {
if (this._handlerConfiguration.placeholderEditingViewMode === PlaceholderEditingViewMode.Overlay && current instanceof PlaceholderItemHandler) {
this._drawFade(renderingContext, current, context, offset);
}
}
}
const transform = renderingContext.getTransform();
const matrix = new Matrix(transform.a, transform.b, transform.c, transform.d, transform.e, transform.f);
renderingContext.setTransform(1, 0, 0, 1, 0, 0);
if (this._selection.parentRectangle) {
const rect = this._applyMatrixToRectangle(this._selection.parentRectangle, matrix);
Graphics.rectangle(renderingContext, rect, this.style.selection.width * Environment.devicePixelRatio, this.style.selection.color, null, 1, [3, 3]);
}
const handler = this._selection.currentItemHandler;
const isInEdit = (handler instanceof NewBaseTextItemHandler && handler.isInEdit);
if (this._selection.visibleRectangle && !isInEdit) {
const rect = this._applyMatrixToRectangle(this._selection.visibleRectangle, matrix);
Graphics.rectangle(renderingContext, rect, this.style.selection.width * Environment.devicePixelRatio, this.style.selection.color, this.style.selection.fillColor, 1, this.style.selection.dashPattern);
const angle = rect.angle;
const selectionOptions = this._selection.getSelectionDrawingParams();
const drawGrip = (index) => {
if (!selectionOptions.resize)
return false;
if (!selectionOptions.arbitraryHeightResize && (index == 6 || index == 8))
return false;
if (!selectionOptions.arbitraryWidthResize && (index == 5 || index == 7))
return false;
if (this._selection.isIdle)
return true;
if (index === this._getResizeIndex(contentAngle, this._selection.rectangleSigns))
return true;
return false;
};
if (drawGrip(1))
this._drawGripRectangle(renderingContext, rect.getUpperLeftCorner(), angle);
if (drawGrip(2))
this._drawGripRectangle(renderingContext, rect.getUpperRightCorner(), angle);
if (drawGrip(4))
this._drawGripRectangle(renderingContext, rect.getBottomLeftCorner(), angle);
if (drawGrip(3))
this._drawGripRectangle(renderingContext, rect.getBottomRightCorner(), angle);
if (drawGrip(6))
this._drawGripRectangle(renderingContext, rect.getUpperCenterPoint(), angle);
if (drawGrip(8))
this._drawGripRectangle(renderingContext, rect.getBottomCenterPoint(), angle);
if (drawGrip(5))
this._drawGripRectangle(renderingContext, rect.getLeftCenterPoint(), angle);
if (drawGrip(7))
this._drawGripRectangle(renderingContext, rect.getRightCenterPoint(), angle);
const drawRotateGrip = () => {
if (!selectionOptions.rotate)
return false;
if (this._selection.isIdle)
return true;
if (this._selection.isRotating)
return true;
return false;
};
if (drawRotateGrip())
this._drawRotateGrip(renderingContext, rect, angle);
}
renderingContext.setTransform(transform);
}
_drawSpotlights(ctx, style) {
const spotlightedItems = this._spotlightHandler.getSpotlightItems()
.filter(x => !this._selection.selectedItemHandlers.contains(x.handler));
const transform = ctx.getTransform();
const matrix = new Matrix(transform.a, transform.b, transform.c, transform.d, transform.e, transform.f);
ctx.setTransform(1, 0, 0, 1, 0, 0);
spotlightedItems.forEach(spotlight => {
var _a, _b, _c, _d, _e, _f, _g;
spotlight.options.color = (_a = spotlight.options.color) !== null && _a !== void 0 ? _a : style.spotlight.color;
spotlight.options.width = (_b = spotlight.options.width) !== null && _b !== void 0 ? _b : style.spotlight.width;
spotlight.options.showLabel = (_c = spotlight.options.showLabel) !== null && _c !== void 0 ? _c : style.spotlight.showLabel;
spotlight.options.fillColor = (_d = spotlight.options.fillColor) !== null && _d !== void 0 ? _d : style.spotlight.fillColor;
spotlight.options.textStyle = (_e = spotlight.options.textStyle) !== null && _e !== void 0 ? _e : style.spotlight.textStyle;
spotlight.options.textColor = (_f = spotlight.options.textColor) !== null && _f !== void 0 ? _f : style.spotlight.textColor;
spotlight.options.dashPattern = (_g = spotlight.options.dashPattern) !== null && _g !== void 0 ? _g : style.spotlight.dashPattern;
this._drawSpotlight(ctx, spotlight, matrix);
});
ctx.setTransform(transform);
}
_drawHover(ctx) {
if (!this.style.canvasItemHoverEnabled)
return;
if (this._selection.selectedItemHandlers.contains(this._hoverHandler.currentHandler))
return;
const transform = ctx.getTransform();
const matrix = new Matrix(transform.a, transform.b, transform.c, transform.d, transform.e, transform.f);
ctx.setTransform(1, 0, 0, 1, 0, 0);
const rect = (this._hoverHandler.currentHandler) ?
this._hoverHandler.currentHandler.getHighlightRectangles({ includeChildren: false, includeParent: true })[0] : null;
if (rect)
this._drawHoverRect(ctx, rect, this._hoverHandler.label, matrix);
const rectPermanent = this._hoverHandler.rectanglePermanent;
if (rectPermanent)
this._drawHoverRect(ctx, rectPermanent, this._hoverHandler.labelPermanent, matrix);
ctx.setTransform(transform);
}
_drawHoverRect(ctx, rect, label, matrix) {
const hoverRect = this._applyMatrixToRectangle(rect, matrix);
Graphics.rectangle(ctx, hoverRect, this.style.hover.width * Environment.devicePixelRatio, this.style.hover.color, this.style.hover.fillColor, 1, this.style.hover.dashPattern);
if (label && this.style.hover.showLabel) {
const { point, angle } = this._calculateLabelPlacement(hoverRect);
Graphics.text(ctx, StringUtils.toSingleLine(label), point, this.style.hover.textStyle, this.style.hover.textColor, null, angle, undefined);
}
}
_calculateLabelPlacement(hoverRect) {
let pt0 = hoverRect.getUpperLeftCorner();
let pt1 = hoverRect.getUpperRightCorner();
let pt2 = hoverRect.getBottomRightCorner();
let pt3 = hoverRect.getBottomLeftCorner();
let angle = hoverRect.angle;
let points = [
{ index: 0, point: pt0 },
{ index: 1, point: pt1 },
{ index: 2, point: pt2 },
{ index: 3, point: pt3 }
];
points.sort((a, b) => a.point.y - b.point.y);
let topPointIndex = points[0].index;
let labelPoint = null;
let labelAngle = angle;
if (topPointIndex == 0) {
if (angle <= 45)
labelPoint = pt0;
else {
labelPoint = pt3;
labelAngle = angle - 90;
}
}
else if (topPointIndex == 1) {
if (angle <= 315) {
labelPoint = pt1;
labelAngle = angle + 90;
}
else
labelPoint = pt0;
}
else if (topPointIndex == 2) {
if (angle <= 230) {
labelPoint = pt2;
labelAngle = angle + 180;
}
else {
labelPoint = pt1;
labelAngle = angle + 90;
}
}
else if (topPointIndex == 3) {
if (angle <= 135) {
labelPoint = pt3;
labelAngle = angle - 90;
}
else {
labelPoint = pt2;
labelAngle = angle + 180;
}
}
let pt = labelPoint;
pt.translate(0, -5);
return { point: pt, angle: labelAngle };
}
_drawHighlight(ctx, textLimitsContext) {
const transform = ctx.getTransform();
const matrix = new Matrix(transform.a, transform.b, transform.c, transform.d, transform.e, transform.f);
ctx.setTransform(1, 0, 0, 1, 0, 0);
const rects = this._selection.getHighlightRectangles();
this._drawHighlightRects(ctx, rects, matrix, textLimitsContext);
ctx.setTransform(transform);
}
_drawHighlightRects(ctx, rectangles, matrix, textLimitsContext) {
if (rectangles == null)
return;
rectangles.forEach(r => {
const rect = this._applyMatrixToRectangle(r, matrix);
Graphics.rectangle(ctx, rect, this.style.selection.width * Environment.devicePixelRatio, (textLimitsContext.errorInput) ? "rgba(255,0,0,1)" : this.style.selection.color, this.style.selection.fillColor, 1, this.style.selection.dashPattern);
});
}
_drawSpotlight(ctx, spotlight, matrix) {
const rect = this._applyMatrixToRectangle(spotlight.handler.rectangle, matrix);
Graphics.rectangle(ctx, rect, spotlight.options.width * Environment.devicePixelRatio, spotlight.options.color, spotlight.options.fillColor, 1, spotlight.options.dashPattern);
if (spotlight.options.name && spotlight.options.showLabel) {
let pt = rect.getUpperLeftCorner();
pt.translate(0, -5);
Graphics.text(ctx, StringUtils.toSingleLine(spotlight.options.name), pt, spotlight.options.textStyle, spotlight.options.textColor, null, 0, rect.width);
}
}
_alignPoint(point) {
point.x = Math.ceil(point.x) - 0.5;
point.y = Math.ceil(point.y) - 0.5;
}
_alignRectangle(rect) {
if (rect.centerX % 1 > 0.1) { // 0.5
if (rect.width % 2 == 1)
rect.width += 1;
}
else {
if (rect.width % 2 == 0)
rect.width += 1;
}
if (rect.centerY % 1 > 0.1) { // 0.5
if (rect.height % 2 == 1)
rect.height += 1;
}
}
_drawRotateGrip(ctx, rectangle, angle) {
if (!this._rotationIconImage)
return;
ctx.save();
const gripSize = (Environment.IsTouchDevice()) ? this.style.selection.rotationGripSizeOnTouchDevice : this.style.selection.rotationGripSize;
const size = gripSize * Environment.devicePixelRatio;
const shadowSize = (gripSize + 2) * Environment.devicePixelRatio;
const svgSize = size / 1.35;
const gripPoint = new PointF(rectangle.centerX, rectangle.centerY + rectangle.height / 2 + size * 1.5);
gripPoint.rotateAt(angle, rectangle.center);
const fullRect = new RotatedRectangleF(gripPoint.x, gripPoint.y, size, size, 0);
const shadowRect = new RotatedRectangleF(gripPoint.x, gripPoint.y, shadowSize, shadowSize, 0);
const circle = new RotatedRectangleF(gripPoint.x, gripPoint.y, svgSize, svgSize, 0);
ctx.fillStyle = "transparent";
ctx.strokeStyle = "rgba(0, 0, 0, 20%)";
ctx.lineWidth = 1 * Environment.devicePixelRatio;
Graphics.drawRoundedRectangle(ctx, shadowRect.toRectangleF(), shadowSize / 2);
ctx.fillStyle = this.style.rotationGripColor;
ctx.strokeStyle = this.style.rotationGripLineColor;
ctx.lineWidth = 1;
Graphics.drawRoundedRectangle(ctx, fullRect.toRectangleF(), size / 2);
Graphics.drawImage(ctx, this._rotationIconImage, circle, size / 24, size / 24);
ctx.restore();
}
_drawGripRectangle(ctx, point, angle) {
const gripRectSize = this.style.selection.resizeGripSize * Environment.devicePixelRatio;
const gripRect = new RotatedRectangleF(Math.ceil(point.x) + 0.5, Math.ceil(point.y) + 0.5, gripRectSize, gripRectSize, angle);
Graphics.fillRectangle(ctx, gripRect, this.style.selection.resizeGripColor);
Graphics.rectangle(ctx, gripRect, this.style.selection.resizeGripLineWidth * Environment.devicePixelRatio, this.style.selection.resizeGripLineColor, this.style.selection.resizeGripColor, 1);
}
_getMemoryCanvas(width, height) {
const memoryCanvas = this._canvasElementHandler.viewportMemoryCanvas;
memoryCanvas.width = width;
memoryCanvas.height = height;
return memoryCanvas;
}
_getTextureCanvas(width, height) {
const textureCanvas = this._canvasElementHandler.viewportTextureCanvas;
textureCanvas.width = width;
textureCanvas.height = height;
return textureCanvas;
}
_drawHandler(handler, renderingContext, productHandler, workspace, offset) {
var _a, _b;
let memoryCanvas = null;
let memoryCanvasCtx = null;
let textureCanvas = null;
let textureCanvasCtx = null;
if (handler.isNormalRenderingType == false) {
const width = renderingContext.canvas.width;
const height = renderingContext.canvas.height;
memoryCanvas = this._getMemoryCanvas(width, height);
memoryCanvasCtx = this._getScaledContext(memoryCanvas, workspace, offset);
textureCanvas = this._getTextureCanvas(width, height);
textureCanvasCtx = this._getScaledContext(textureCanvas, workspace, offset);
}
const isMasked = productHandler.isMasked(handler.layer.container);
let isDrawn = false;
if (!this.previewMode)
return handler.draw(renderingContext, null, textureCanvas, textureCanvasCtx, memoryCanvas, memoryCanvasCtx, this, isMasked);
const container = (_a = handler === null || handler === void 0 ? void 0 : handler.item) === null || _a === void 0 ? void 0 : _a.parentContainer;
if (container instanceof SurfaceContainer) {
(_b = container === null || container === void 0 ? void 0 : container.parentComponent) === null || _b === void 0 ? void 0 : _b.printAreas.forEach(p => {
const printAreaPath = Path.rectangle(p.bounds.left, p.bounds.top, p.bounds.width, p.bounds.height);
if (printAreaPath != null) {
isDrawn = true;
handler.draw(renderingContext, printAreaPath, textureCanvas, textureCanvasCtx, memoryCanvas, memoryCanvasCtx, this, isMasked);
}
});
}
if (!isDrawn)
handler.draw(renderingContext, null, textureCanvas, textureCanvasCtx, memoryCanvas, memoryCanvasCtx, this, isMasked);
}
_drawMockupWorkaround(ctx, workspaceWidth, workspaceHeight, offset, canvasBackground) {
const transform = ctx.getTransform();
const matrix = new Matrix(transform.a, transform.b, transform.c, transform.d, transform.e, transform.f);
ctx.setTransform(1, 0, 0, 1, 0, 0);
const clipRect = this._getClippingRectangle(workspaceWidth, workspaceHeight, offset);
let clipRectRot = RotatedRectangleF.fromRectangleF(clipRect);
clipRectRot = this._applyMatrixToRectangle(clipRectRot, matrix, false);
clipRectRot.width -= 1;
clipRectRot.height -= 1;
const clipRectRot1 = clipRectRot.clone();
clipRectRot1.width -= 2;
clipRectRot1.height -= 2;
const clipRectRot2 = clipRectRot.clone();
clipRectRot2.width += 2;
clipRectRot2.height += 2;
const color = canvasBackground !== null && canvasBackground !== void 0 ? canvasBackground : "white";
Graphics.rectangle(ctx, clipRectRot, 1, color, null, 1);
Graphics.rectangle(ctx, clipRectRot1, 1, color, null, 1);
Graphics.rectangle(ctx, clipRectRot2, 1, color, null, 1);
ctx.setTransform(transform);
}
_needToShowLimits(context) {
const { characterLimit, maxLineCount, maxLineLength, isSingleLineText } = context.textEditor.textLimits;
if (characterLimit || maxLineLength)
return true;
if (maxLineCount && !isSingleLineText)
return true;
return false;
}
_drawLimitsMessage(renderingContext, context) {
var _a, _b;
if (!((_a = context === null || context === void 0 ? void 0 : context.textEditor) === null || _a === void 0 ? void 0 : _a.isActive) || !context.textEditor.textLimits)
return;
if (this._needToShowLimits(context) == false)
return;
const { characterLimit, maxLineCount, maxLineLength } = context.textEditor.textLimits;
const { textLength, currentLineLength, linesCount } = context.textEditor.textMetrics;
const backgroundColor = context.errorInput ? "rgba(224,0,0,0.8)" : "rgba(0,0,0,0.8)";
let message = "";
if (((_b = context.lastLimitsEvent) === null || _b === void 0 ? void 0 : _b.isPastePrevented) && context.errorInput) {
message += context.translations.pasteLimitsExceededMsg;
}
else {
if (maxLineLength)
message += context.translations.charactersInParagraphMsg.replace("{value}", currentLineLength === null || currentLineLength === void 0 ? void 0 : currentLineLength.toString())
.replace("{maxValue}", maxLineLength.toString());
if (maxLineCount) {
message += maxLineLength ? "\n" : "";
message += context.translations.paragraphsMsg.substring(0).replace("{value}", linesCount === null || linesCount === void 0 ? void 0 : linesCount.toString())
.replace("{maxValue}", maxLineCount.toString());
;
}
if (characterLimit) {
message += maxLineCount || maxLineLength ? "\n" : "";
message += `${maxLineLength ? context.translations.totalCharactersMsg : context.translations.charactersMsg}`.substring(0).replace("{value}", textLength === null || textLength === void 0 ? void 0 : textLength.toString())
.replace("{maxValue}", characterLimit.toString());
;
}
}
const pxRatioRounded = Math.round(Environment.devicePixelRatio);
const width = renderingContext.canvas.width;
const height = renderingContext.canvas.height;
renderingContext.setTransform(1, 0, 0, 1, 0, 0);
const pt = new PointF(width / 2, height - 24 * pxRatioRounded);
const fontsize = 14 * pxRatioRounded;
Graphics.text(renderingContext, message, pt, `normal ${fontsize}px Roboto`, "rgba(255,255,255,0.8)", "rgba(255,255,255,0.8)", 0, width - 48 * pxRatioRounded, "center", "bottom", backgroundColor, null, 0, new Margin({ horizontal: 16, vertical: 8 }), 8 * pxRatioRounded);
}
_drawVerticalLine(context, xAnchor, color, points) {
const lineWidth = 1;
const h = context.canvas.clientHeight * Environment.devicePixelRatio;
if ((points === null || points === void 0 ? void 0 : points.length) >= 2) {
Graphics.drawLine(context, points[0].x, points[0].y, points[1].x, points[1].y, lineWidth, color);
for (let pt of points) {
this._drawSnapLineMark(context, pt, lineWidth, color);
}
}
else {
Graphics.drawLine(context, xAnchor, 0, xAnchor, h, lineWidth, color);
}
}
_drawSnapLineMark(context, pt, lineWidth, color) {
Graphics.drawCross(context, pt.x, pt.y, this._snapLineMarkSize * 2, lineWidth, color);
}
_drawHorizontalLine(context, yAnchor, color, points) {
const lineWidth = 1;
const h = context.canvas.clientWidth * Environment.devicePixelRatio;
if ((points === null || points === void 0 ? void 0 : points.length) >= 2) {
Graphics.drawLine(context, points[0].x, points[0].y, points[1].x, points[1].y, lineWidth, color);
for (let pt of points) {
this._drawSnapLineMark(context, pt, lineWidth, color);
}
}
e