@arcgis/core
Version:
ArcGIS Maps SDK for JavaScript: A complete 2D and 3D mapping and data visualization API
6 lines (5 loc) • 8.5 kB
JavaScript
/*
All material copyright ESRI, All Rights Reserved, unless otherwise specified.
See https://js.arcgis.com/4.33/esri/copyright.txt for details.
*/
import{clamp as t}from"../../../../core/mathUtils.js";import{debugFlags as e}from"../../support/debugFlags.js";import{getFontMetrics as i}from"./FontMetrics.js";import{getTextHelperCanvas as n}from"./TextHelperCanvas.js";const s=1;class r{constructor(t,e,i,n){this.text=t,this._alignment=e,this._parameters=i,this._maxSize=n,this._textWidths=[],this._lineWidths=[],this._renderPixelRatio=null,this._metricsCached=null,this.key=`${t}--${this._parameters.key}-${this._alignment}`,this._lines=t.replaceAll(" "," ").split(/\r?\n/)}get displayWidth(){return Math.ceil(this._displayWidth+2*this._horizontalPadding)}get displayHeight(){let t=this._metrics.firstLineAscent;for(let e=0;e<this._lines.length-1;e++)t+=this._lineSpacing;return t+=this._metrics.lastLineDescent,Math.ceil(t+2*this._haloSize+2*this._verticalPadding)}get renderedWidth(){return this._toRoundedRenderUnit(this.displayWidth)}get renderedHeight(){return this._toRoundedRenderUnit(this.displayHeight)}get firstRenderedBaselinePosition(){return this._toRenderUnit(this._firstLineYOffset+this._metrics.firstLineAscent)}get _firstLineYOffset(){return this._verticalPadding+this._haloSize}get _metrics(){if(null==this._metricsCached){const t=n(a,l,l).getContext("2d"),e=this._parameters.definition.pixelRatio,s=this._fontSize*e;this._parameters.setFontProperties(t,s);let r=2*this._haloSize;const h=this._parameters.definition.font;"italic"!==h.style&&"oblique"!==h.style&&"bold"!==h.weight&&"bolder"!==h.weight||(r+=.3*t.measureText("A").width),this._textWidths.length=0,this._lineWidths.length=0;let o=0,d=0,_=0,g=0,m=0;this._lines.forEach(((i,n)=>{const s=t.measureText(i),h=s.width/e,a=h+r;this._textWidths.push(h),this._lineWidths.push(a),o=Math.max(o,a),g=Math.max(g,s.actualBoundingBoxAscent/e),m=Math.max(m,s.actualBoundingBoxDescent/e),0===n&&(d=s.actualBoundingBoxAscent/e),n===this._lines.length-1&&(_=s.actualBoundingBoxDescent/e)}));const f=i(this._parameters),u=Math.max(g,f.maxAscent),p=Math.max(m,f.maxDescent),x=d,R="underline"===this._parameters.definition.font.decoration?p:_,S=o;this._metricsCached=new c(x,R,u,p,S)}return this._metricsCached}get _lineSpacing(){return(this._midLineHeight+this._linePadding)*this._parameters.definition.lineSpacingFactor}get _midLineHeight(){return this._metrics.midLineHeight}get _linePadding(){return this._midLineHeight*d}get _midLineAscent(){return this._metrics.maxLineAscent}get _renderedFontSize(){return this._toRenderUnit(this._fontSize)}get _fontSize(){return this._parameters.definition.size}get _renderedHaloSize(){return this._toRenderUnit(this._haloSize)}get _haloSize(){return this._parameters.haloSize}get _horizontalPadding(){return this._hasBackground?this._parameters.definition.background.padding[0]:0}get _verticalPadding(){return Math.max(this._hasBackground?this._parameters.definition.background.padding[1]:0,s)}get _hasBackground(){return!!this._parameters.backgroundStyle}get renderPixelRatio(){if(null==this._renderPixelRatio){const t=this._parameters.definition.pixelRatio;this._renderPixelRatio=Math.min(t,Math.min(this._maxSize[0]/this.displayWidth,this._maxSize[1]/this.displayHeight))}return this._renderPixelRatio}_getLineXOffset(t){switch(this._alignment){case o.Left:return this._horizontalPadding;case o.Center:return(this.displayWidth-this._lineWidths[t])/2;case o.Right:return this.displayWidth-this._horizontalPadding-this._lineWidths[t]}}render(t,i,n){t.save();const s=i/=this.renderPixelRatio,r=n/=this.renderPixelRatio,h=this._haloSize,o=this._firstLineYOffset+this._metrics.firstLineAscent;i+=h,n+=o;const a=this._haloSize>0;a&&this._renderHalo(t,s,r,h,o),this._parameters.setFontProperties(t,this._renderedFontSize);for(let e=0;e<this._lines.length;++e){const s=this._lines[e],r=this._getLineXOffset(e);a&&(t.globalCompositeOperation="destination-out",t.fillStyle="rgb(0, 0, 0)",this._fillText(t,s,i+r,n),this._renderLineDecoration(t,i+r,n,this._textWidths[e])),t.globalCompositeOperation="source-over",t.fillStyle=this._parameters.textStyle,this._fillText(t,s,i+this._getLineXOffset(e),n),this._renderLineDecoration(t,i+r,n,this._textWidths[e]),n+=this._lineSpacing}if(e.TEXT_SHOW_BASELINE){t.strokeStyle=_,t.setLineDash([2,2]),t.lineWidth=1;let e=r+o;for(let i=0;i<this._lines.length;++i)this._drawLine(t,[s,e],[s+this.displayWidth,e]),e+=this._lineSpacing}if(e.TEXT_SHOW_BORDER&&(t.strokeStyle=_,t.setLineDash([]),t.lineWidth=1,this._drawBox(t,[s,r],[this.displayWidth,this.displayHeight])),this._hasBackground){const e=this._parameters.definition.background.borderRadius*this.renderPixelRatio;this._roundedRect(t,s,r,e),t.globalCompositeOperation="destination-over",t.fillStyle=this._parameters.backgroundStyle,t.fill()}t.restore()}_renderLineDecoration(t,e,i,n,s=!1){if("none"===this._parameters.definition.font.decoration||0===n)return;const r=1,h=Math.max(this._parameters.definition.size/16,r);switch(this._parameters.definition.font.decoration){case"underline":i+=2*h;break;case"line-through":i-=.33*this._midLineAscent}const o=s?this._haloSize:0;t.strokeStyle=s?this._parameters.haloStyle:this._parameters.textStyle,t.lineWidth=this._toRenderUnit(h+2*o),t.beginPath(),t.moveTo(this._toRenderUnit(e-o),this._toRenderUnit(i)),t.lineTo(this._toRenderUnit(e+n+o),this._toRenderUnit(i)),t.stroke()}_roundedRect(e,i,n,s){i=this._toRenderUnit(i),n=this._toRenderUnit(n);const r=this.renderedWidth,h=this.renderedHeight;0!==s?(s=t(s,0,Math.floor(h/2)),e.beginPath(),e.moveTo(i,n+s),e.arcTo(i,n,i+s,n,s),e.lineTo(i+r-s,n),e.arcTo(i+r,n,i+r,n+s,s),e.lineTo(i+r,n+h-s),e.arcTo(i+r,n+h,i+r-s,n+h,s),e.lineTo(i+s,n+h),e.arcTo(i,n+h,i,n+h-s,s),e.closePath()):e.rect(i,n,r,h)}_renderHalo(t,e,i,s,r){const h=this.renderedWidth,o=this.renderedHeight,d=n(a,Math.max(h,l),Math.max(o,l)),_=d.getContext("2d");_.clearRect(0,0,h,o),this._parameters.setFontProperties(_,this._renderedFontSize),_.fillStyle=this._parameters.haloStyle,_.strokeStyle=this._parameters.haloStyle;const c=this._renderedHaloSize<3;_.lineJoin=c?"miter":"round",c?this._renderHaloEmulated(_,s,r):this._renderHaloNative(_,s,r);let g=r;for(let n=0;n<this._lines.length;++n){const t=this._getLineXOffset(n);this._renderLineDecoration(_,s+t,g,this._textWidths[n],!0),g+=this._lineSpacing}t.globalAlpha=this._parameters.definition.halo.color[3],t.drawImage(d,0,0,h,o,this._toRenderUnit(e),this._toRenderUnit(i),h,o),t.globalAlpha=1}_renderHaloEmulated(t,e,i){for(let n=0;n<this._lines.length;++n){const s=this._lines[n],r=this._getLineXOffset(n);for(const[n,o]of h)this._fillText(t,s,e+r+this._haloSize*n,i+this._haloSize*o);i+=this._lineSpacing}}_renderHaloNative(t,e,i){const n=2*this._haloSize;for(let s=0;s<this._lines.length;++s){const r=this._lines[s],h=this._getLineXOffset(s),o=5,a=.1;for(let s=0;s<o;s++){const d=1-(o-1)*a+s*a;t.lineWidth=this._toRenderUnit(d*n),this._strokeText(t,r,e+h,i)}i+=this._lineSpacing}}get _displayWidth(){return this._metrics.displayWidth}_toRenderUnit(t){return t*this.renderPixelRatio}_toRoundedRenderUnit(t){return Math.round(t*this.renderPixelRatio)}_fillText(t,e,i,n){t.fillText(e,this._toRenderUnit(i),this._toRenderUnit(n))}_strokeText(t,e,i,n){t.strokeText(e,this._toRenderUnit(i),this._toRenderUnit(n))}_drawLine(t,e,i){t.beginPath(),t.moveTo(this._toRoundedRenderUnit(e[0])+.5,this._toRoundedRenderUnit(e[1])+.5),t.lineTo(this._toRoundedRenderUnit(i[0])+.5,this._toRoundedRenderUnit(i[1])+.5),t.stroke()}_drawBox(t,e,i){const n=this._toRenderUnit(e[0]),s=this._toRenderUnit(e[1]),r=this._toRenderUnit(i[0]),h=this._toRenderUnit(i[1]),o=Math.floor(n)+.5,a=Math.ceil(n+r)-.5,d=Math.floor(s)+.5,l=Math.ceil(s+h)-.5;t.beginPath(),t.moveTo(o,d),t.lineTo(a,d),t.lineTo(a,l),t.lineTo(o,l),t.lineTo(o,d),t.stroke()}}const h=[];{const t=16;for(let e=0;e<360;e+=360/t)h.push([Math.cos(Math.PI*e/180),Math.sin(Math.PI*e/180)])}var o;!function(t){t[t.Left=0]="Left",t[t.Center=1]="Center",t[t.Right=2]="Right"}(o||(o={}));const a={canvas:null},d=.2,l=512,_="rgb(255, 0, 255, 0.5)";class c{get firstLineHeight(){return this.firstLineAscent+this.maxLineDescent}get midLineHeight(){return this.maxLineAscent+this.maxLineDescent}get lastLineHeight(){return this.maxLineAscent+this.lastLineDescent}constructor(t,e,i,n,s){this.firstLineAscent=t,this.lastLineDescent=e,this.maxLineAscent=i,this.maxLineDescent=n,this.displayWidth=s}}export{o as TextRenderAlignment,r as TextRenderer,s as textVerticalPaddingPx};