UNPKG

@arcgis/core

Version:

ArcGIS Maps SDK for JavaScript: A complete 2D and 3D mapping and data visualization API

3 lines (2 loc) 8.28 kB
/* COPYRIGHT Esri - https://js.arcgis.com/5.0.8/LICENSE.txt */ 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 s}from"./TextHelperCanvas.js";const n=1;class r{constructor(t,e,i,s){this.text=t,this._alignment=e,this._parameters=i,this._maxSize=s,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=s(o,d,d).getContext("2d"),e=this._parameters.definition.pixelRatio,n=this._fontSize*e;this._parameters.setFontProperties(t,n);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 a=0,l=0,c=0,g=0,m=0;this._lines.forEach((i,s)=>{const n=t.measureText(i),h=n.width/e,o=h+r;this._textWidths.push(h),this._lineWidths.push(o),a=Math.max(a,o),g=Math.max(g,n.actualBoundingBoxAscent/e),m=Math.max(m,n.actualBoundingBoxDescent/e),0===s&&(l=n.actualBoundingBoxAscent/e),s===this._lines.length-1&&(c=n.actualBoundingBoxDescent/e)});const f=i(this._parameters),u=Math.max(g,f.maxAscent),p=Math.max(m,f.maxDescent),x=l,R="underline"===this._parameters.definition.font.decoration?p:c,S=a;this._metricsCached=new _(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*a}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,n)}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 0:return this._horizontalPadding;case 1:return(this.displayWidth-this._lineWidths[t])/2;case 2:return this.displayWidth-this._horizontalPadding-this._lineWidths[t]}}render(t,i,s){t.save();const n=i/=this.renderPixelRatio,r=s/=this.renderPixelRatio,h=this._haloSize,o=this._firstLineYOffset+this._metrics.firstLineAscent;i+=h,s+=o;const a=this._haloSize>0;a&&this._renderHalo(t,n,r,h,o),this._parameters.setFontProperties(t,this._renderedFontSize);for(let e=0;e<this._lines.length;++e){const n=this._lines[e],r=this._getLineXOffset(e);a&&(t.globalCompositeOperation="destination-out",t.fillStyle="rgb(0, 0, 0)",this._fillText(t,n,i+r,s),this._renderLineDecoration(t,i+r,s,this._textWidths[e])),t.globalCompositeOperation="source-over",t.fillStyle=this._parameters.textStyle,this._fillText(t,n,i+this._getLineXOffset(e),s),this._renderLineDecoration(t,i+r,s,this._textWidths[e]),s+=this._lineSpacing}if(e.TEXT_SHOW_BASELINE){t.strokeStyle=l,t.setLineDash([2,2]),t.lineWidth=1;let e=r+o;for(let i=0;i<this._lines.length;++i)this._drawLine(t,[n,e],[n+this.displayWidth,e]),e+=this._lineSpacing}if(e.TEXT_SHOW_BORDER&&(t.strokeStyle=l,t.setLineDash([]),t.lineWidth=1,this._drawBox(t,[n,r],[this.displayWidth,this.displayHeight])),this._hasBackground){const e=this._parameters.definition.background.borderRadius*this.renderPixelRatio;this._roundedRect(t,n,r,e),t.globalCompositeOperation="destination-over",t.fillStyle=this._parameters.backgroundStyle,t.fill()}t.restore()}_renderLineDecoration(t,e,i,s,n=!1){if("none"===this._parameters.definition.font.decoration||0===s)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=n?this._haloSize:0;t.strokeStyle=n?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+s+o),this._toRenderUnit(i)),t.stroke()}_roundedRect(e,i,s,n){i=this._toRenderUnit(i),s=this._toRenderUnit(s);const r=this.renderedWidth,h=this.renderedHeight;0!==n?(n=t(n,0,Math.floor(h/2)),e.beginPath(),e.moveTo(i,s+n),e.arcTo(i,s,i+n,s,n),e.lineTo(i+r-n,s),e.arcTo(i+r,s,i+r,s+n,n),e.lineTo(i+r,s+h-n),e.arcTo(i+r,s+h,i+r-n,s+h,n),e.lineTo(i+n,s+h),e.arcTo(i,s+h,i,s+h-n,n),e.closePath()):e.rect(i,s,r,h)}_renderHalo(t,e,i,n,r){const h=this.renderedWidth,a=this.renderedHeight,l=s(o,Math.max(h,d),Math.max(a,d)),_=l.getContext("2d");_.clearRect(0,0,h,a),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(_,n,r):this._renderHaloNative(_,n,r);let g=r;for(let s=0;s<this._lines.length;++s){const t=this._getLineXOffset(s);this._renderLineDecoration(_,n+t,g,this._textWidths[s],!0),g+=this._lineSpacing}t.globalAlpha=this._parameters.definition.halo.color[3],t.drawImage(l,0,0,h,a,this._toRenderUnit(e),this._toRenderUnit(i),h,a),t.globalAlpha=1}_renderHaloEmulated(t,e,i){for(let s=0;s<this._lines.length;++s){const n=this._lines[s],r=this._getLineXOffset(s);for(const[s,o]of h)this._fillText(t,n,e+r+this._haloSize*s,i+this._haloSize*o);i+=this._lineSpacing}}_renderHaloNative(t,e,i){const s=2*this._haloSize;for(let n=0;n<this._lines.length;++n){const r=this._lines[n],h=this._getLineXOffset(n),o=5,a=.1;for(let n=0;n<o;n++){const d=1-(o-1)*a+n*a;t.lineWidth=this._toRenderUnit(d*s),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,s){t.fillText(e,this._toRenderUnit(i),this._toRenderUnit(s))}_strokeText(t,e,i,s){t.strokeText(e,this._toRenderUnit(i),this._toRenderUnit(s))}_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 s=this._toRenderUnit(e[0]),n=this._toRenderUnit(e[1]),r=this._toRenderUnit(i[0]),h=this._toRenderUnit(i[1]),o=Math.floor(s)+.5,a=Math.ceil(s+r)-.5,d=Math.floor(n)+.5,l=Math.ceil(n+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)])}const o={canvas:null},a=.2,d=512,l="rgb(255, 0, 255, 0.5)";class _{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,s,n){this.firstLineAscent=t,this.lastLineDescent=e,this.maxLineAscent=i,this.maxLineDescent=s,this.displayWidth=n}}export{r as TextRenderer,n as textVerticalPaddingPx};