UNPKG

fabric

Version:

Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.

3 lines (2 loc) 16.3 kB
import{defineProperty as t}from"../../../_virtual/_rollupPluginBabelHelpers.min.mjs";import{cache as e}from"../../cache.min.mjs";import{STROKE as i,BOTTOM as s,TOP as n,CENTER as h,RTL as r,RIGHT as o,LTR as l,FILL as a,LEFT as f,DEFAULT_SVG_FONT_SIZE as d}from"../../constants.min.mjs";import{StyledText as c}from"./StyledText.min.mjs";import{SHARED_ATTRIBUTES as g}from"../../parser/attributes.min.mjs";import{parseAttributes as p}from"../../parser/parseAttributes.min.mjs";import{classRegistry as m}from"../../ClassRegistry.min.mjs";import{graphemeSplit as _}from"../../util/lang_string.min.mjs";import{createCanvasElementFor as u}from"../../util/misc/dom.min.mjs";import{hasStyleChanged as x,stylesToArray as S,stylesFromArray as y}from"../../util/misc/textStyles.min.mjs";import{getPathSegmentsInfo as T,getPointOnPath as L}from"../../util/path/index.min.mjs";import"../Object/FabricObject.min.mjs";import{TextSVGExportMixin as O}from"./TextSVGExportMixin.min.mjs";import{applyMixins as w}from"../../util/applyMixins.min.mjs";import{textLayoutProperties as C,additionalProps as k,textDefaultValues as W,JUSTIFY as v,JUSTIFY_CENTER as A,JUSTIFY_RIGHT as P,JUSTIFY_LEFT as D,TEXT_DECORATION_THICKNESS as H}from"./constants.min.mjs";import{isFiller as I}from"../../util/typeAssertions.min.mjs";import{normalizeWs as j}from"../../util/internals/normalizeWhiteSpace.min.mjs";import{cacheProperties as b}from"../Object/defaultValues.min.mjs";let z;class F extends c{static getDefaults(){return{...super.getDefaults(),...F.ownDefaults}}constructor(e,i){super(),t(this,"__charBounds",[]),Object.assign(this,F.ownDefaults),this.setOptions(i),this.styles||(this.styles={}),this.text=e,this.initialized=!0,this.path&&this.setPathInfo(),this.initDimensions(),this.setCoords()}setPathInfo(){const t=this.path;t&&(t.segmentsInfo=T(t.path))}_splitText(){const t=this._splitTextIntoLines(this.text);return this.textLines=t.lines,this._textLines=t.graphemeLines,this._unwrappedTextLines=t._unwrappedLines,this._text=t.graphemeText,t}initDimensions(){this._splitText(),this._clearCache(),this.dirty=!0,this.path?(this.width=this.path.width,this.height=this.path.height):(this.width=this.calcTextWidth()||this.cursorWidth||this.MIN_TEXT_WIDTH,this.height=this.calcTextHeight()),this.textAlign.includes(v)&&this.enlargeSpaces()}enlargeSpaces(){let t,e,i,s,n,h,r;for(let o=0,l=this._textLines.length;o<l;o++)if((this.textAlign===v||o!==l-1&&!this.isEndOfWrapping(o))&&(s=0,n=this._textLines[o],e=this.getLineWidth(o),e<this.width&&(r=this.textLines[o].match(this._reSpacesAndTabs)))){i=r.length,t=(this.width-e)/i;for(let e=0;e<=n.length;e++)h=this.__charBounds[o][e],this._reSpaceAndTab.test(n[e])?(h.width+=t,h.kernedWidth+=t,h.left+=s,s+=t):h.left+=s}}isEndOfWrapping(t){return t===this._textLines.length-1}missingNewlineOffset(t){return 1}get2DCursorLocation(t,e){const i=e?this._unwrappedTextLines:this._textLines;let s;for(s=0;s<i.length;s++){if(t<=i[s].length)return{lineIndex:s,charIndex:t};t-=i[s].length+this.missingNewlineOffset(s,e)}return{lineIndex:s-1,charIndex:i[s-1].length<t?i[s-1].length:t}}toString(){return`#<Text (${this.complexity()}): { "text": "${this.text}", "fontFamily": "${this.fontFamily}" }>`}_getCacheCanvasDimensions(){const t=super._getCacheCanvasDimensions(),e=this.fontSize;return t.width+=e*t.zoomX,t.height+=e*t.zoomY,t}_render(t){const e=this.path;e&&!e.isNotVisible()&&e._render(t),this._setTextStyles(t),this._renderTextLinesBackground(t),this._renderTextDecoration(t,"underline"),this._renderText(t),this._renderTextDecoration(t,"overline"),this._renderTextDecoration(t,"linethrough")}_renderText(t){this.paintFirst===i?(this._renderTextStroke(t),this._renderTextFill(t)):(this._renderTextFill(t),this._renderTextStroke(t))}_setTextStyles(t,e,i){if(t.textBaseline="alphabetic",this.path)switch(this.pathAlign){case h:t.textBaseline="middle";break;case"ascender":t.textBaseline=n;break;case"descender":t.textBaseline=s}t.font=this._getFontDeclaration(e,i)}calcTextWidth(){let t=this.getLineWidth(0);for(let e=1,i=this._textLines.length;e<i;e++){const i=this.getLineWidth(e);i>t&&(t=i)}return t}_renderTextLine(t,e,i,s,n,h){this._renderChars(t,e,i,s,n,h)}_renderTextLinesBackground(t){if(!this.textBackgroundColor&&!this.styleHas("textBackgroundColor"))return;const e=t.fillStyle,i=this._getLeftOffset();let s=this._getTopOffset();for(let e=0,n=this._textLines.length;e<n;e++){const n=this.getHeightOfLine(e);if(!this.textBackgroundColor&&!this.styleHas("textBackgroundColor",e)){s+=n;continue}const h=this._textLines[e].length,o=this._getLineLeftOffset(e);let l,a,f=0,d=0,c=this.getValueOfPropertyAt(e,0,"textBackgroundColor");const g=this.getHeightOfLineImpl(e);for(let n=0;n<h;n++){const h=this.__charBounds[e][n];a=this.getValueOfPropertyAt(e,n,"textBackgroundColor"),this.path?(t.save(),t.translate(h.renderLeft,h.renderTop),t.rotate(h.angle),t.fillStyle=a,a&&t.fillRect(-h.width/2,-g*(1-this._fontSizeFraction),h.width,g),t.restore()):a!==c?(l=i+o+d,this.direction===r&&(l=this.width-l-f),t.fillStyle=c,c&&t.fillRect(l,s,f,g),d=h.left,f=h.width,c=a):f+=h.kernedWidth}a&&!this.path&&(l=i+o+d,this.direction===r&&(l=this.width-l-f),t.fillStyle=a,t.fillRect(l,s,f,g)),s+=n}t.fillStyle=e,this._removeShadow(t)}_measureChar(t,i,s,n){const h=e.getFontCache(i),r=this._getFontDeclaration(i),o=s?s+t:t,l=s&&r===this._getFontDeclaration(n),a=i.fontSize/this.CACHE_FONT_SIZE;let f,d,c,g;if(s&&h.has(s)&&(c=h.get(s)),h.has(t)&&(g=f=h.get(t)),l&&h.has(o)&&(d=h.get(o),g=d-c),void 0===f||void 0===c||void 0===d){const e=function(){if(!z){const t=u({width:0,height:0});z=t.getContext("2d")}return z}();this._setTextStyles(e,i,!0),void 0===f&&(g=f=e.measureText(t).width,h.set(t,f)),void 0===c&&l&&s&&(c=e.measureText(s).width,h.set(s,c)),l&&void 0===d&&(d=e.measureText(o).width,h.set(o,d),g=d-c)}return{width:f*a,kernedWidth:g*a}}getHeightOfChar(t,e){return this.getValueOfPropertyAt(t,e,"fontSize")}measureLine(t){const e=this._measureLine(t);return 0!==this.charSpacing&&(e.width-=this._getWidthOfCharSpacing()),e.width<0&&(e.width=0),e}_measureLine(t){let e,i,s=0;const n=this.pathSide===o,r=this.path,l=this._textLines[t],a=l.length,d=new Array(a);this.__charBounds[t]=d;for(let n=0;n<a;n++){const h=l[n];i=this._getGraphemeBox(h,t,n,e),d[n]=i,s+=i.kernedWidth,e=h}if(d[a]={left:i?i.left+i.width:0,width:0,kernedWidth:0,height:this.fontSize,deltaY:0},r&&r.segmentsInfo){let t=0;const e=r.segmentsInfo[r.segmentsInfo.length-1].length;switch(this.textAlign){case f:t=n?e-s:0;break;case h:t=(e-s)/2;break;case o:t=n?0:e-s}t+=this.pathStartOffset*(n?-1:1);for(let s=n?a-1:0;n?s>=0:s<a;n?s--:s++)i=d[s],t>e?t%=e:t<0&&(t+=e),this._setGraphemeOnPath(t,i),t+=i.kernedWidth}return{width:s,numOfSpaces:0}}_setGraphemeOnPath(t,e){const i=t+e.kernedWidth/2,s=this.path,n=L(s.path,i,s.segmentsInfo);e.renderLeft=n.x-s.pathOffset.x,e.renderTop=n.y-s.pathOffset.y,e.angle=n.angle+(this.pathSide===o?Math.PI:0)}_getGraphemeBox(t,e,i,s,n){const h=this.getCompleteStyleDeclaration(e,i),r=s?this.getCompleteStyleDeclaration(e,i-1):{},o=this._measureChar(t,h,s,r);let l,a=o.kernedWidth,f=o.width;0!==this.charSpacing&&(l=this._getWidthOfCharSpacing(),f+=l,a+=l);const d={width:f,left:0,height:h.fontSize,kernedWidth:a,deltaY:h.deltaY};if(i>0&&!n){const t=this.__charBounds[e][i-1];d.left=t.left+t.width+o.kernedWidth-o.width}return d}getHeightOfLineImpl(t){const e=this.__lineHeights;if(e[t])return e[t];let i=this.getHeightOfChar(t,0);for(let e=1,s=this._textLines[t].length;e<s;e++)i=Math.max(this.getHeightOfChar(t,e),i);return e[t]=i*this._fontSizeMult}getHeightOfLine(t){return this.getHeightOfLineImpl(t)*this.lineHeight}calcTextHeight(){let t=0;for(let e=0,i=this._textLines.length;e<i;e++)t+=e===i-1?this.getHeightOfLineImpl(e):this.getHeightOfLine(e);return t}_getLeftOffset(){return this.direction===l?-this.width/2:this.width/2}_getTopOffset(){return-this.height/2}_renderTextCommon(t,e){t.save();let i=0;const s=this._getLeftOffset(),n=this._getTopOffset();for(let h=0,r=this._textLines.length;h<r;h++)this._renderTextLine(e,t,this._textLines[h],s+this._getLineLeftOffset(h),n+i+this.getHeightOfLineImpl(h),h),i+=this.getHeightOfLine(h);t.restore()}_renderTextFill(t){(this.fill||this.styleHas(a))&&this._renderTextCommon(t,"fillText")}_renderTextStroke(t){(this.stroke&&0!==this.strokeWidth||!this.isEmptyStyles())&&(this.shadow&&!this.shadow.affectStroke&&this._removeShadow(t),t.save(),this._setLineDash(t,this.strokeDashArray),t.beginPath(),this._renderTextCommon(t,"strokeText"),t.closePath(),t.restore())}_renderChars(t,e,i,s,n,h){const a=this.textAlign.includes(v),d=this.path,c=!a&&0===this.charSpacing&&this.isEmptyStyles(h)&&!d,g=this.direction===l,p=this.direction===l?1:-1,m=e.direction;let _,u,S,y,T,L="",O=0;if(e.save(),m!==this.direction&&(e.canvas.setAttribute("dir",g?l:r),e.direction=g?l:r,e.textAlign=g?f:o),n-=this.getHeightOfLineImpl(h)*this._fontSizeFraction,c)return this._renderChar(t,e,h,0,i.join(""),s,n),void e.restore();for(let r=0,o=i.length-1;r<=o;r++)y=r===o||this.charSpacing||d,L+=i[r],S=this.__charBounds[h][r],0===O?(s+=p*(S.kernedWidth-S.width),O+=S.width):O+=S.kernedWidth,a&&!y&&this._reSpaceAndTab.test(i[r])&&(y=!0),y||(_=_||this.getCompleteStyleDeclaration(h,r),u=this.getCompleteStyleDeclaration(h,r+1),y=x(_,u,!1)),y&&(d?(e.save(),e.translate(S.renderLeft,S.renderTop),e.rotate(S.angle),this._renderChar(t,e,h,r,L,-O/2,0),e.restore()):(T=s,this._renderChar(t,e,h,r,L,T,n)),L="",_=u,s+=p*O,O=0);e.restore()}_applyPatternGradientTransformText(t){const e=this.width+this.strokeWidth,i=this.height+this.strokeWidth,s=u({width:e,height:i}),n=s.getContext("2d");return s.width=e,s.height=i,n.beginPath(),n.moveTo(0,0),n.lineTo(e,0),n.lineTo(e,i),n.lineTo(0,i),n.closePath(),n.translate(e/2,i/2),n.fillStyle=t.toLive(n),this._applyPatternGradientTransform(n,t),n.fill(),n.createPattern(s,"no-repeat")}handleFiller(t,e,i){let s,n;return I(i)?"percentage"===i.gradientUnits||i.gradientTransform||i.patternTransform?(s=-this.width/2,n=-this.height/2,t.translate(s,n),t[e]=this._applyPatternGradientTransformText(i),{offsetX:s,offsetY:n}):(t[e]=i.toLive(t),this._applyPatternGradientTransform(t,i)):(t[e]=i,{offsetX:0,offsetY:0})}_setStrokeStyles(t,e){let{stroke:i,strokeWidth:s}=e;return t.lineWidth=s,t.lineCap=this.strokeLineCap,t.lineDashOffset=this.strokeDashOffset,t.lineJoin=this.strokeLineJoin,t.miterLimit=this.strokeMiterLimit,this.handleFiller(t,"strokeStyle",i)}_setFillStyles(t,e){let{fill:i}=e;return this.handleFiller(t,"fillStyle",i)}_renderChar(t,e,i,s,n,h,r){const o=this._getStyleDeclaration(i,s),l=this.getCompleteStyleDeclaration(i,s),a="fillText"===t&&l.fill,f="strokeText"===t&&l.stroke&&l.strokeWidth;if(f||a){if(e.save(),e.font=this._getFontDeclaration(l),o.textBackgroundColor&&this._removeShadow(e),o.deltaY&&(r+=o.deltaY),a){const t=this._setFillStyles(e,l);e.fillText(n,h-t.offsetX,r-t.offsetY)}if(f){const t=this._setStrokeStyles(e,l);e.strokeText(n,h-t.offsetX,r-t.offsetY)}e.restore()}}setSuperscript(t,e){this._setScript(t,e,this.superscript)}setSubscript(t,e){this._setScript(t,e,this.subscript)}_setScript(t,e,i){const s=this.get2DCursorLocation(t,!0),n=this.getValueOfPropertyAt(s.lineIndex,s.charIndex,"fontSize"),h=this.getValueOfPropertyAt(s.lineIndex,s.charIndex,"deltaY"),r={fontSize:n*i.size,deltaY:h+n*i.baseline};this.setSelectionStyles(r,t,e)}_getLineLeftOffset(t){const e=this.getLineWidth(t),i=this.width-e,s=this.textAlign,n=this.direction,l=this.isEndOfWrapping(t);let a=0;return s===v||s===A&&!l||s===P&&!l||s===D&&!l?0:(s===h&&(a=i/2),s===o&&(a=i),s===A&&(a=i/2),s===P&&(a=i),n===r&&(s===o||s===P?a=0:s===f||s===D?a=-i:s!==h&&s!==A||(a=-i/2)),a)}_clearCache(){this._forceClearCache=!1,this.__lineWidths=[],this.__lineHeights=[],this.__charBounds=[]}getLineWidth(t){if(void 0!==this.__lineWidths[t])return this.__lineWidths[t];const{width:e}=this.measureLine(t);return this.__lineWidths[t]=e,e}_getWidthOfCharSpacing(){return 0!==this.charSpacing?this.fontSize*this.charSpacing/1e3:0}getValueOfPropertyAt(t,e,i){var s;return null!==(s=this._getStyleDeclaration(t,e)[i])&&void 0!==s?s:this[i]}_renderTextDecoration(t,e){if(!this[e]&&!this.styleHas(e))return;let i=this._getTopOffset();const s=this._getLeftOffset(),n=this.path,h=this._getWidthOfCharSpacing(),o="linethrough"===e?.5:"overline"===e?1:0,l=this.offsets[e];for(let f=0,d=this._textLines.length;f<d;f++){const d=this.getHeightOfLine(f);if(!this[e]&&!this.styleHas(e,f)){i+=d;continue}const c=this._textLines[f],g=d/this.lineHeight,p=this._getLineLeftOffset(f);let m=0,_=0,u=this.getValueOfPropertyAt(f,0,e),x=this.getValueOfPropertyAt(f,0,a),S=this.getValueOfPropertyAt(f,0,H),y=u,T=x,L=S;const O=i+g*(1-this._fontSizeFraction);let w=this.getHeightOfChar(f,0),C=this.getValueOfPropertyAt(f,0,"deltaY");for(let i=0,h=c.length;i<h;i++){const h=this.__charBounds[f][i];y=this.getValueOfPropertyAt(f,i,e),T=this.getValueOfPropertyAt(f,i,a),L=this.getValueOfPropertyAt(f,i,H);const d=this.getHeightOfChar(f,i),c=this.getValueOfPropertyAt(f,i,"deltaY");if(n&&y&&T){const e=this.fontSize*L/1e3;t.save(),t.fillStyle=x,t.translate(h.renderLeft,h.renderTop),t.rotate(h.angle),t.fillRect(-h.kernedWidth/2,l*d+c-o*e,h.kernedWidth,e),t.restore()}else if((y!==u||T!==x||d!==w||L!==S||c!==C)&&_>0){const e=this.fontSize*S/1e3;let i=s+p+m;this.direction===r&&(i=this.width-i-_),u&&x&&S&&(t.fillStyle=x,t.fillRect(i,O+l*w+C-o*e,_,e)),m=h.left,_=h.width,u=y,S=L,x=T,w=d,C=c}else _+=h.kernedWidth}let k=s+p+m;this.direction===r&&(k=this.width-k-_),t.fillStyle=T;const W=this.fontSize*L/1e3;y&&T&&L&&t.fillRect(k,O+l*w+C-o*W,_-h,W),i+=d}this._removeShadow(t)}_getFontDeclaration(){let{fontFamily:t=this.fontFamily,fontStyle:e=this.fontStyle,fontWeight:i=this.fontWeight,fontSize:s=this.fontSize}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=arguments.length>1?arguments[1]:void 0;const h=t.includes("'")||t.includes('"')||t.includes(",")||F.genericFonts.includes(t.toLowerCase())?t:`"${t}"`;return[e,i,`${n?this.CACHE_FONT_SIZE:s}px`,h].join(" ")}render(t){this.visible&&(this.canvas&&this.canvas.skipOffscreen&&!this.group&&!this.isOnScreen()||(this._forceClearCache&&this.initDimensions(),super.render(t)))}graphemeSplit(t){return _(t)}_splitTextIntoLines(t){const e=t.split(this._reNewline),i=new Array(e.length),s=["\n"];let n=[];for(let t=0;t<e.length;t++)i[t]=this.graphemeSplit(e[t]),n=n.concat(i[t],s);return n.pop(),{_unwrappedLines:i,lines:e,graphemeText:n,graphemeLines:i}}toObject(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];return{...super.toObject([...k,...t]),styles:S(this.styles,this.text),...this.path?{path:this.path.toObject()}:{}}}set(t,e){const{textLayoutProperties:i}=this.constructor;super.set(t,e);let s=!1,n=!1;if("object"==typeof t)for(const e in t)"path"===e&&this.setPathInfo(),s=s||i.includes(e),n=n||"path"===e;else s=i.includes(t),n="path"===t;return n&&this.setPathInfo(),s&&this.initialized&&(this.initDimensions(),this.setCoords()),this}complexity(){return 1}static async fromElement(t,e,i){const s=p(t,F.ATTRIBUTE_NAMES,i),{textAnchor:n=f,textDecoration:r="",dx:l=0,dy:a=0,top:c=0,left:g=0,fontSize:m=d,strokeWidth:_=1,...u}={...e,...s},x=new this(j(t.textContent||"").trim(),{left:g+l,top:c+a,underline:r.includes("underline"),overline:r.includes("overline"),linethrough:r.includes("line-through"),strokeWidth:0,fontSize:m,...u}),S=x.getScaledHeight()/x.height,y=((x.height+x.strokeWidth)*x.lineHeight-x.height)*S,T=x.getScaledHeight()+y;let L=0;return n===h&&(L=x.getScaledWidth()/2),n===o&&(L=x.getScaledWidth()),x.set({left:x.left-L,top:x.top-(T-x.fontSize*(.07+x._fontSizeFraction))/x.lineHeight,strokeWidth:_}),x}static fromObject(t){return this._fromObject({...t,styles:y(t.styles||{},t.text)},{extraParam:"text"})}}t(F,"textLayoutProperties",C),t(F,"cacheProperties",[...b,...k]),t(F,"ownDefaults",W),t(F,"type","Text"),t(F,"genericFonts",["serif","sans-serif","monospace","cursive","fantasy","system-ui","ui-serif","ui-sans-serif","ui-monospace","ui-rounded","math","emoji","fangsong"]),t(F,"ATTRIBUTE_NAMES",g.concat("x","y","dx","dy","font-family","font-style","font-weight","font-size","letter-spacing","text-decoration","text-anchor")),w(F,[O]),m.setClass(F),m.setSVGClass(F);export{F as FabricText}; //# sourceMappingURL=Text.min.mjs.map