UNPKG

@aurigma/design-atoms

Version:

Design Atoms is a part of Customer's Canvas SDK which allows for manipulating individual design elements through your code.

853 lines 47.2 kB
import { RotatedRectangleF, PointF, RectangleF, Path, Matrix, Transform, Environment, SurfaceContainer, MockupContainer, flatten, EqualsOfFloatNumbers } 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"; export class CanvasRenderer { constructor(_selection, _dndHandler, handlerConfiguration, _rubberbandHandler, _viewportHandler, _spotlightHandler, _hoverHandler, _snapLinesHandler, _canvasElementHandler, _interactiveZonesHandler) { 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._loadingImage = new Image(); this._rotationIconImage = null; this._snapLineMarkSize = 2; this.style = new CanvasRendererStyle(); this.previewMode = false; this._loadRotationIconImage = () => { let brandPrimaryColor = getComputedStyle(document.documentElement).getPropertyValue("--de-brand-primary"); var 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; var xml = new XMLSerializer().serializeToString(svg); var svg64 = btoa(xml); var b64Start = 'data:image/svg+xml;base64,'; var image64 = b64Start + svg64; let 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, drawShadow, drawSnapLines) { const designCtx = this._getScaledContext(designCanvas, workspace, offset, false); if (designCtx == null) return; if (drawShadow) this._drawShadow(designCtx, workspace, offset); this._drawInteractiveZones(designCtx); 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) { 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); 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; } _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 { // inside 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; var rotRect = RotatedRectangleF.fromRectangleF(new RectangleF(left, top, workspace.width, workspace.height)); let 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) { let rect1 = rect.toRectangleF(); rect1.updateByMatrix(matrix); let rect2 = RectangleF.FromLTRB(Math.floor(rect1.left), Math.floor(rect1.top), Math.ceil(rect1.right), Math.ceil(rect1.bottom)); let 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; 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); let fullRect = new RotatedRectangleF(gripPoint.x, gripPoint.y, size, size, 0); let shadowRect = new RotatedRectangleF(gripPoint.x, gripPoint.y, shadowSize, shadowSize, 0); let 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); } _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) { 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; Graphics.rectangle(ctx, clipRectRot, 1, "white", null, 1); Graphics.rectangle(ctx, clipRectRot1, 1, "white", null, 1); Graphics.rectangle(ctx, clipRectRot2, 1, "white", 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); } else Graphics.drawLine(context, 0, yAnchor, h, yAnchor, lineWidth, color); } _getContext(canvas) { if (canvas == null) return null; if (canvas.width == 0 || canvas.height == 0) return null; const context = canvas.getContext("2d"); if (context == null) return null; Graphics.clearCanvas(context); return context; } _getScaledContext(canvas, workspace, offset, clipByWorkspace = true) { const context = this._getContext(canvas); if (context == null) return; this.applyContextScale(context, workspace, offset, clipByWorkspace); return context; } _getClippingRectangle(workspaceWidth, workspaceHeight, offset) { const left = offset != null ? -offset.x : 0; const top = offset != null ? -offset.y : 0; return new RectangleF(left, top, workspaceWidth, workspaceHeight); } _contextSetClipping(context, workspaceWidth, workspaceHeight, offset) { const rect = this._getClippingRectangle(workspaceWidth, workspaceHeight, offset); const path = new Path2D(); path.rect(rect.left, rect.top, rect.width, rect.height); context.clip(path); } _isEditing(itemHandler) { return itemHandler instanceof PlaceholderItemHandler && itemHandler.editing || itemHandler instanceof NewBaseTextItemHandler && itemHandler.editing; } _getItemHandlersByRenderState(layers, state, excludeEditedText = true) { const isNormalPlaceholderViewMode = this._handlerConfiguration.placeholderEditingViewMode === PlaceholderEditingViewMode.Normal; return flatten(layers.map(l => l.itemHandlers.toArray())) .filter(i => i.renderState == state && (isNormalPlaceholderViewMode || !(i instanceof PlaceholderItemHandler && i.editing)) && !((i instanceof NewBaseTextItemHandler && i.editing) && excludeEditedText)); } _drawFade(renderingContext, handler, context, offset) { const rw = context.actualRulerWidth; const w = renderingContext.canvas.clientWidth; const h = renderingContext.canvas.clientHeight; const ctrlPt0 = new PointF(rw, rw); const ctrlPt1 = new PointF(rw + w, rw + h); const wsPt0 = CoordinatesConvertUtils.controlToWorkspacePointCorrect(ctrlPt0, context); if (offset != null) wsPt0.translate(-offset.x, -offset.y); const wsPt1 = CoordinatesConvertUtils.controlToWorkspacePointCorrect(ctrlPt1, context); const viewportPathTw = new this._textWhizz.Path(); viewportPathTw.rect(wsPt0.x, wsPt0.y, wsPt1.x - wsPt0.x, wsPt1.y - wsPt0.y); const handlerRect = handler.rectangle; const handlerRectPath = Path.rotatedRectangle(handlerRect); const handlerRectPathTw = toTextWhizzPath(this._textWhizz, handlerRectPath); const coverPathTw = this._textWhizz.Path.subtract(viewportPathTw, handlerRectPathTw); const center = new PointF(0, 0); const transform = new Transform(); Graphics.fillPath(renderingContext, coverPathTw, center, transform, "rgb(0, 0, 0", 0.75); } _drawMargins(renderingContext, margins, workspace, mul, offset) { var _a, _b; // Draw margins. const offsetX = -((_a = offset === null || offset === void 0 ? void 0 : offset.x) !== null && _a !== void 0 ? _a : 0); const offsetY = -((_b = offset === null || offset === void 0 ? void 0 : offset.y) !== null && _b !== void 0 ? _b : 0); const lm = margins.leftMargin + offsetX; const rm = margins.rightMargin; const tm = margins.topMargin + offsetY; const bm = margins.bottomMargin; const ww = workspace.width; const wh = workspace.height; // for drawing into pixel units. const mw = margins.marginWidth / mul; const mc = margins.marginColor; if (lm !== 0 && rm !== 0 && tm !== 0 && bm !== 0 && ww >= (lm + rm) && wh >= (tm + bm) && mw > 0) { Graphics.drawLine(renderingContext, lm, tm, lm, wh - bm, mw, mc); Graphics.drawLine(renderingContext, lm, tm, ww - rm, tm, mw, mc); Graphics.drawLine(renderingContext, ww - rm, wh - bm, lm, wh - bm, mw, mc); Graphics.drawLine(renderingContext, ww - rm, wh - bm, ww - rm, tm, mw, mc); } } } //# sourceMappingURL=CanvasRenderer.js.map