modern-text 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,16 +30,16 @@ const text = new Text({
30
30
  fontSize: 22,
31
31
  textDecoration: 'underline',
32
32
  },
33
- data: [
33
+ content: [
34
34
  {
35
35
  letterSpacing: 3,
36
36
  fragments: [
37
- { data: 'He', color: 'red', fontSize: 12 },
38
- { data: 'llo', color: 'black' },
37
+ { content: 'He', color: 'red', fontSize: 12 },
38
+ { content: 'llo', color: 'black' },
39
39
  ],
40
40
  },
41
- { data: ', ', color: 'grey' },
42
- { data: 'World!', color: 'black' },
41
+ { content: ', ', color: 'grey' },
42
+ { content: 'World!', color: 'black' },
43
43
  ],
44
44
  })
45
45
 
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class p{static get defaultStyle(){return{width:"auto",height:"auto",color:"#000000",fontSize:14,fontWeight:"normal",fontFamily:"sans-serif",fontStyle:"normal",fontKerning:"normal",textWrap:"wrap",textAlign:"start",textBaseline:"middle",textDecoration:null,textStrokeWidth:0,textStrokeColor:"#000000",direction:"inherit",lineHeight:1,letterSpacing:0,shadowColor:"#000000",shadowOffsetX:0,shadowOffsetY:0,shadowBlur:0}}constructor(a={}){const{view:t=document.createElement("canvas"),pixelRatio:i=window.devicePixelRatio||1,data:o="",style:n}=a;this.view=t,this.context=t.getContext("2d"),this.pixelRatio=i,this.data=o,this.style={...p.defaultStyle,...n},this.update()}measure(a=0,t=0){let i=this._createParagraphs(this.data);i=this._createWrapedParagraphs(i,a);const o=this.context;let n=0;for(let h=i.length,r=0;r<h;r++){const e=i[r];e.relativeX=0,e.relativeY=n,e.width=0,e.height=0;let s=0,u;for(const c of e.fragments){const f=c.style;this._setContextStyle(f);const d=o.measureText(c.data),g=d.width,x=d.actualBoundingBoxRight+d.actualBoundingBoxLeft;c.relativeX=s,c.relativeY=e.relativeY,c.width=g,c.actualBoundingBoxWidth=x,c.height=f.fontSize*f.lineHeight,s+=g+f.letterSpacing,u=c,e.height=Math.max(e.height,c.height)}e.width=u?u.relativeX+Math.max(u.width,u.actualBoundingBoxWidth):0,n+=e.height}const l=i.reduce((h,r)=>(h.x=Math.min(h.x,r.relativeX),h.y=Math.min(h.y,r.relativeY),h.width=Math.max(h.width,r.width),h.height+=r.height,h),{x:0,y:0,width:0,height:0});a=a||l.width,t=Math.max(t,l.height);for(let h=i.length,r=0;r<h;r++){const e=i[r];switch(this.style.textAlign){case"center":e.absoluteX=(a-l.width)/2,e.fragments.forEach(s=>{s.absoluteX=e.absoluteX+s.relativeX,s.fillX=s.absoluteX+s.width/2});break;case"end":case"right":e.absoluteX=a-l.width,e.fragments.forEach(s=>{s.absoluteX=e.absoluteX+s.relativeX,s.fillX=s.absoluteX+s.width});break;case"start":case"left":default:e.absoluteX=0,e.fragments.forEach(s=>{s.absoluteX=e.absoluteX+s.relativeX,s.fillX=s.absoluteX});break}switch(this.style.textBaseline){case"top":case"hanging":e.absoluteY=e.relativeY,e.fragments.forEach(s=>{s.absoluteY=e.absoluteY,s.fillY=s.absoluteY});break;case"middle":case"alphabetic":case"ideographic":e.absoluteY=e.relativeY+(t-l.height)/2,e.fragments.forEach(s=>{s.absoluteY=e.absoluteY,s.fillY=s.absoluteY+e.height/2});break;case"bottom":e.absoluteY=e.relativeY+t-l.height,e.fragments.forEach(s=>{s.absoluteY=e.absoluteY,s.fillY=s.absoluteY+e.height});break}}return{...l,paragraphs:i}}_createParagraphs(a){const t={width:0,height:0,actualBoundingBoxWidth:0,relativeX:0,relativeY:0,absoluteX:0,absoluteY:0},i=(l={})=>({...t,fragments:[],...l}),o=(l={})=>{const h=[],{width:r,height:e,...s}=this.style;return h.push({fillX:0,fillY:0,...t,...l,style:{...s,...l.style},data:l.data??""}),h},n=[];if(typeof a=="string")n.push(i({fragments:o({data:a})}));else{a=Array.isArray(a)?a:[a];for(const l of a){const h=i();if("fragments"in l){const{fragments:r,...e}=l;for(const s of r){const{data:u,...c}=s;h.fragments.push(...o({data:u,style:{...e,...c}}))}}else if("data"in l){const{data:r,...e}=l;h.fragments.push(...o({data:r,style:e}))}n.push(h)}}return n}_createWrapedParagraphs(a,t){const i=[],o=a.slice();let n,l;for(;n=o.shift();){const h=n.fragments.slice();let r=0;const e=[];let s=!0;for(;l=h.shift();){const u=l.style;this._setContextStyle(u);let c="",f=!1;for(const d of l.data){const g=this.context.measureText(d).width+(s?0:u.letterSpacing),x=/^[\r\n]$/.test(d);if(x||u.textWrap==="wrap"&&t&&r+g>t){let w=x?c.length+1:c.length;!r&&!w&&(c+=d,w++),c.length&&e.push({...l,text:c}),e.length&&(i.push({...n,fragments:e.slice()}),e.length=0);const b=l.data.substring(w);(b.length||h.length)&&o.unshift({...n,fragments:(b.length?[{...l,data:b}]:[]).concat(h.slice())}),h.length=0,f=!0;break}else r+=g;c+=d}f||e.push({...l}),s=!1}e.length&&i.push({...n,fragments:e})}return i}_draw(a){const t=this.context;a.forEach(i=>{i.fragments.forEach(o=>{const n=o.style;switch(this._setContextStyle(n),n.textStrokeWidth&&t.strokeText(o.data,o.fillX,o.fillY),t.fillText(o.data,o.fillX,o.fillY),n.textDecoration){case"underline":t.beginPath(),t.moveTo(o.absoluteX,i.absoluteY+i.height-2),t.lineTo(o.absoluteX+o.width,i.absoluteY+i.height-2),t.stroke();break;case"line-through":t.beginPath(),t.moveTo(o.absoluteX,i.absoluteY+i.height/2),t.lineTo(o.absoluteX+o.width,i.absoluteY+i.height/2),t.stroke();break}})})}_resizeView(a,t){const i=this.view;i.style.width=`${a}px`,i.style.height=`${t}px`,i.dataset.width=String(a),i.dataset.height=String(t),i.width=Math.max(1,Math.floor(a*this.pixelRatio)),i.height=Math.max(1,Math.floor(t*this.pixelRatio))}_setContextStyle(a){const t=this.context;t.shadowColor=a.shadowColor,t.shadowOffsetX=a.shadowOffsetX,t.shadowOffsetY=a.shadowOffsetY,t.shadowBlur=a.shadowBlur,t.strokeStyle=a.textStrokeColor,t.lineWidth=a.textStrokeWidth,t.fillStyle=a.color,t.direction=a.direction,t.textAlign=a.textAlign,t.textBaseline=a.textBaseline,t.font=[a.fontStyle,a.fontWeight,`${a.fontSize}px`,a.fontFamily].join(" "),t.fontKerning=a.fontKerning,t.letterSpacing=`${a.letterSpacing}px`}update(){const a=this.context;let{width:t,height:i}=this.style;t==="auto"&&(t=0),i==="auto"&&(i=0);const o=this.measure(t,i);t||(t=o.width),i=Math.max(i,o.height),this._resizeView(t,i);const n=this.pixelRatio;a.scale(n,n),a.clearRect(0,0,a.canvas.width,a.canvas.height),this._draw(o.paragraphs)}}exports.Text=p;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class u{static get defaultStyle(){return{width:"auto",height:"auto",color:"#000000",fontSize:14,fontWeight:"normal",fontFamily:"sans-serif",fontStyle:"normal",fontKerning:"normal",textWrap:"wrap",textAlign:"start",verticalAlign:"baseline",textDecoration:null,textStrokeWidth:0,textStrokeColor:"#000000",direction:"inherit",lineHeight:1,letterSpacing:0,shadowColor:"#000000",shadowOffsetX:0,shadowOffsetY:0,shadowBlur:0}}constructor(i={}){const{view:t=document.createElement("canvas"),pixelRatio:l=window.devicePixelRatio||1,content:o="",style:h}=i;this.view=t,this.context=t.getContext("2d"),this.pixelRatio=l,this.content=o,this.style={...u.defaultStyle,...h},this.update()}measure(i=0,t=0){let l=this._createParagraphs(this.content);l=this._createWrapedParagraphs(l,i);const o=this.context;let h=0;for(let c=l.length,x=0;x<c;x++){const e=l[x];e.contentBox.top=h;let s=0,d=null;for(const n of e.fragments){this._setContextStyle({...n.style,textAlign:"left",verticalAlign:"baseline"});const r=o.measureText(n.content);n.inlineBox.left=s,n.inlineBox.top=h,n.inlineBox.width=r.width,n.inlineBox.height=n.style.fontSize*n.style.lineHeight,n.contentBox.left=n.inlineBox.left,n.contentBox.width=n.inlineBox.width,n.contentBox.height=n.style.fontSize,n.contentBox.top=n.inlineBox.top+(n.inlineBox.height-n.contentBox.height)/2,n.textBox.width=r.actualBoundingBoxLeft+r.actualBoundingBoxRight,n.textBox.height=r.actualBoundingBoxAscent+r.actualBoundingBoxDescent,n.textBox.left=n.contentBox.left+(n.contentBox.width-n.textBox.width)/2,n.textBox.top=n.contentBox.top+(n.contentBox.height-n.textBox.height)/2,n.baseline=n.textBox.top+r.actualBoundingBoxAscent,s+=n.contentBox.width+n.style.letterSpacing,e.contentBox.height<n.contentBox.height&&(d=n),e.contentBox.left=Math.min(e.contentBox.left,n.contentBox.left),e.contentBox.top=Math.min(e.contentBox.top,n.contentBox.top),e.contentBox.height=Math.max(e.contentBox.height,n.contentBox.height),e.lineBox.height=Math.max(e.lineBox.height,n.inlineBox.height)}const B=e.fragments[e.fragments.length-1];e.contentBox.width=Math.max(e.contentBox.width,B?B.contentBox.left+Math.max(B.contentBox.width,B.textBox.width):0),e.lineBox.left=0,e.lineBox.top=h,e.lineBox.width=Math.max(i,e.contentBox.width),this._setContextStyle({...(d??e).style,textAlign:"left",verticalAlign:"baseline"});const f=o.measureText("X");e.baseline=e.lineBox.top+(e.lineBox.height-f.actualBoundingBoxAscent+f.actualBoundingBoxDescent)/2+f.actualBoundingBoxAscent,h+=e.lineBox.height}const a=l.reduce((c,x)=>(c.left=Math.min(c.left,x.lineBox.left),c.top=Math.min(c.top,x.lineBox.top),c.width=Math.max(c.width,x.lineBox.width),c.height+=x.lineBox.height,c),{left:0,top:0,width:0,height:0});for(let c=l.length,x=0;x<c;x++){const e=l[x];e.fragments.forEach(s=>{const d=s.inlineBox.left,B=s.inlineBox.top,f=e.lineBox.height-s.inlineBox.height;switch(s.style.textAlign){case"end":case"right":s.inlineBox.left+=e.lineBox.width-e.contentBox.width;break;case"center":s.inlineBox.left+=(e.lineBox.width-e.contentBox.width)/2;break;case"start":case"left":default:s.inlineBox.left+=e.lineBox.left;break}switch(s.style.verticalAlign){case"top":s.inlineBox.top=e.lineBox.top;break;case"middle":s.inlineBox.top=e.lineBox.top+f/2;break;case"bottom":s.inlineBox.top=e.lineBox.top+f;break;case"sub":case"text-top":case"text-bottom":break;case"baseline":default:s.inlineBox.height<e.lineBox.height&&(s.inlineBox.top+=e.baseline-s.baseline);break}const n=s.inlineBox.left-d,r=s.inlineBox.top-B;s.contentBox.left+=n,s.contentBox.top+=r,s.textBox.left+=n,s.textBox.top+=r})}return{...a,paragraphs:l}}_createParagraphs(i){const t=(h={})=>{const{width:a,height:c,...x}=this.style;return{contentBox:{left:0,top:0,width:0,height:0},lineBox:{left:0,top:0,width:0,height:0},baseline:0,fragments:[],...h,style:{...x,...h.style}}},l=(h={})=>{const a=[],{width:c,height:x,...e}=this.style;return a.push({contentBox:{left:0,top:0,width:0,height:0},inlineBox:{left:0,top:0,width:0,height:0},textBox:{left:0,top:0,width:0,height:0},baseline:0,...h,style:{...e,...h.style},content:h.content??""}),a},o=[];if(typeof i=="string")o.push(t({fragments:l({content:i})}));else{i=Array.isArray(i)?i:[i];for(const h of i)if("fragments"in h){const{fragments:a,...c}=h,x=t({style:c});for(const e of a){const{content:s,...d}=e;x.fragments.push(...l({content:s,style:{...c,...d}}))}o.push(x)}else if("content"in h){const{content:a,...c}=h,x=t({style:c});x.fragments.push(...l({content:a,style:c})),o.push(x)}}return o}_createWrapedParagraphs(i,t){const l=[],o=i.slice();let h,a;for(;h=o.shift();){const c=h.fragments.slice();let x=0;const e=[];let s=!0;for(;a=c.shift();){const d=a.style;this._setContextStyle(d);let B="",f=!1;for(const n of a.content){const r=this.context.measureText(n).width+(s?0:d.letterSpacing),w=/^[\r\n]$/.test(n);if(w||d.textWrap==="wrap"&&t&&x+r>t){let g=w?B.length+1:B.length;!x&&!g&&(B+=n,g++),B.length&&e.push({...a,text:B}),e.length&&(l.push({baseline:h.baseline,style:{...h.style},contentBox:{...h.contentBox},lineBox:{...h.lineBox},fragments:e.slice()}),e.length=0);const p=a.content.substring(g);(p.length||c.length)&&o.unshift({baseline:h.baseline,style:{...h.style},contentBox:{...h.contentBox},lineBox:{...h.lineBox},fragments:(p.length?[{...a,content:p}]:[]).concat(c.slice())}),c.length=0,f=!0;break}else x+=r;B+=n}f||e.push({...a}),s=!1}e.length&&l.push({...h,fragments:e})}return l}_draw(i){const t=this.context;this.style.backgroundColor&&(t.fillStyle=this.style.backgroundColor,t.fillRect(0,0,t.canvas.width,t.canvas.height)),i.forEach(l=>{l.style.backgroundColor&&(t.fillStyle=l.style.backgroundColor,t.fillRect(l.lineBox.left,l.lineBox.top,l.lineBox.width,l.lineBox.height))}),i.forEach(l=>{l.fragments.forEach(o=>{switch(o.style.backgroundColor&&(t.fillStyle=o.style.backgroundColor,t.fillRect(o.inlineBox.left,o.inlineBox.top,o.inlineBox.width,o.inlineBox.height)),this._setContextStyle({...o.style,textAlign:"left",verticalAlign:"top"}),o.style.textStrokeWidth&&t.strokeText(o.content,o.contentBox.left,o.contentBox.top),t.fillText(o.content,o.contentBox.left,o.contentBox.top),o.style.textDecoration){case"underline":t.beginPath(),t.moveTo(o.contentBox.left,o.contentBox.top+o.contentBox.height-2),t.lineTo(o.contentBox.left+o.contentBox.width,o.contentBox.top+o.contentBox.height-2),t.stroke();break;case"line-through":t.beginPath(),t.moveTo(o.contentBox.left,o.contentBox.top+o.contentBox.height/2),t.lineTo(o.contentBox.left+o.contentBox.width,o.contentBox.top+o.contentBox.height/2),t.stroke();break}})})}_resizeView(i,t){const l=this.view;l.style.width=`${i}px`,l.style.height=`${t}px`,l.dataset.width=String(i),l.dataset.height=String(t),l.width=Math.max(1,Math.floor(i*this.pixelRatio)),l.height=Math.max(1,Math.floor(t*this.pixelRatio))}_setContextStyle(i){const t=this.context;switch(t.shadowColor=i.shadowColor,t.shadowOffsetX=i.shadowOffsetX,t.shadowOffsetY=i.shadowOffsetY,t.shadowBlur=i.shadowBlur,t.strokeStyle=i.textStrokeColor,t.lineWidth=i.textStrokeWidth,t.fillStyle=i.color,t.direction=i.direction,t.textAlign=i.textAlign,i.verticalAlign){case"baseline":t.textBaseline="alphabetic";break;case"top":case"middle":case"bottom":t.textBaseline=i.verticalAlign;break}t.font=[i.fontStyle,i.fontWeight,`${i.fontSize}px`,i.fontFamily].join(" "),t.fontKerning=i.fontKerning,t.letterSpacing=`${i.letterSpacing}px`}update(){const i=this.context;let{width:t,height:l}=this.style;t==="auto"&&(t=0),l==="auto"&&(l=0);const o=this.measure(t,l);t||(t=o.width),l=Math.max(l,o.height),this._resizeView(t,l);const h=this.pixelRatio;i.scale(h,h),i.clearRect(0,0,i.canvas.width,i.canvas.height),this._draw(o.paragraphs)}}exports.Text=u;
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- (function(x,f){typeof exports=="object"&&typeof module<"u"?f(exports):typeof define=="function"&&define.amd?define(["exports"],f):(x=typeof globalThis<"u"?globalThis:x||self,f(x.modernText={}))})(this,function(x){"use strict";class f{static get defaultStyle(){return{width:"auto",height:"auto",color:"#000000",fontSize:14,fontWeight:"normal",fontFamily:"sans-serif",fontStyle:"normal",fontKerning:"normal",textWrap:"wrap",textAlign:"start",textBaseline:"middle",textDecoration:null,textStrokeWidth:0,textStrokeColor:"#000000",direction:"inherit",lineHeight:1,letterSpacing:0,shadowColor:"#000000",shadowOffsetX:0,shadowOffsetY:0,shadowBlur:0}}constructor(i={}){const{view:t=document.createElement("canvas"),pixelRatio:s=window.devicePixelRatio||1,data:o="",style:n}=i;this.view=t,this.context=t.getContext("2d"),this.pixelRatio=s,this.data=o,this.style={...f.defaultStyle,...n},this.update()}measure(i=0,t=0){let s=this._createParagraphs(this.data);s=this._createWrapedParagraphs(s,i);const o=this.context;let n=0;for(let h=s.length,r=0;r<h;r++){const e=s[r];e.relativeX=0,e.relativeY=n,e.width=0,e.height=0;let a=0,d;for(const c of e.fragments){const g=c.style;this._setContextStyle(g);const u=o.measureText(c.data),b=u.width,w=u.actualBoundingBoxRight+u.actualBoundingBoxLeft;c.relativeX=a,c.relativeY=e.relativeY,c.width=b,c.actualBoundingBoxWidth=w,c.height=g.fontSize*g.lineHeight,a+=b+g.letterSpacing,d=c,e.height=Math.max(e.height,c.height)}e.width=d?d.relativeX+Math.max(d.width,d.actualBoundingBoxWidth):0,n+=e.height}const l=s.reduce((h,r)=>(h.x=Math.min(h.x,r.relativeX),h.y=Math.min(h.y,r.relativeY),h.width=Math.max(h.width,r.width),h.height+=r.height,h),{x:0,y:0,width:0,height:0});i=i||l.width,t=Math.max(t,l.height);for(let h=s.length,r=0;r<h;r++){const e=s[r];switch(this.style.textAlign){case"center":e.absoluteX=(i-l.width)/2,e.fragments.forEach(a=>{a.absoluteX=e.absoluteX+a.relativeX,a.fillX=a.absoluteX+a.width/2});break;case"end":case"right":e.absoluteX=i-l.width,e.fragments.forEach(a=>{a.absoluteX=e.absoluteX+a.relativeX,a.fillX=a.absoluteX+a.width});break;case"start":case"left":default:e.absoluteX=0,e.fragments.forEach(a=>{a.absoluteX=e.absoluteX+a.relativeX,a.fillX=a.absoluteX});break}switch(this.style.textBaseline){case"top":case"hanging":e.absoluteY=e.relativeY,e.fragments.forEach(a=>{a.absoluteY=e.absoluteY,a.fillY=a.absoluteY});break;case"middle":case"alphabetic":case"ideographic":e.absoluteY=e.relativeY+(t-l.height)/2,e.fragments.forEach(a=>{a.absoluteY=e.absoluteY,a.fillY=a.absoluteY+e.height/2});break;case"bottom":e.absoluteY=e.relativeY+t-l.height,e.fragments.forEach(a=>{a.absoluteY=e.absoluteY,a.fillY=a.absoluteY+e.height});break}}return{...l,paragraphs:s}}_createParagraphs(i){const t={width:0,height:0,actualBoundingBoxWidth:0,relativeX:0,relativeY:0,absoluteX:0,absoluteY:0},s=(l={})=>({...t,fragments:[],...l}),o=(l={})=>{const h=[],{width:r,height:e,...a}=this.style;return h.push({fillX:0,fillY:0,...t,...l,style:{...a,...l.style},data:l.data??""}),h},n=[];if(typeof i=="string")n.push(s({fragments:o({data:i})}));else{i=Array.isArray(i)?i:[i];for(const l of i){const h=s();if("fragments"in l){const{fragments:r,...e}=l;for(const a of r){const{data:d,...c}=a;h.fragments.push(...o({data:d,style:{...e,...c}}))}}else if("data"in l){const{data:r,...e}=l;h.fragments.push(...o({data:r,style:e}))}n.push(h)}}return n}_createWrapedParagraphs(i,t){const s=[],o=i.slice();let n,l;for(;n=o.shift();){const h=n.fragments.slice();let r=0;const e=[];let a=!0;for(;l=h.shift();){const d=l.style;this._setContextStyle(d);let c="",g=!1;for(const u of l.data){const b=this.context.measureText(u).width+(a?0:d.letterSpacing),w=/^[\r\n]$/.test(u);if(w||d.textWrap==="wrap"&&t&&r+b>t){let p=w?c.length+1:c.length;!r&&!p&&(c+=u,p++),c.length&&e.push({...l,text:c}),e.length&&(s.push({...n,fragments:e.slice()}),e.length=0);const X=l.data.substring(p);(X.length||h.length)&&o.unshift({...n,fragments:(X.length?[{...l,data:X}]:[]).concat(h.slice())}),h.length=0,g=!0;break}else r+=b;c+=u}g||e.push({...l}),a=!1}e.length&&s.push({...n,fragments:e})}return s}_draw(i){const t=this.context;i.forEach(s=>{s.fragments.forEach(o=>{const n=o.style;switch(this._setContextStyle(n),n.textStrokeWidth&&t.strokeText(o.data,o.fillX,o.fillY),t.fillText(o.data,o.fillX,o.fillY),n.textDecoration){case"underline":t.beginPath(),t.moveTo(o.absoluteX,s.absoluteY+s.height-2),t.lineTo(o.absoluteX+o.width,s.absoluteY+s.height-2),t.stroke();break;case"line-through":t.beginPath(),t.moveTo(o.absoluteX,s.absoluteY+s.height/2),t.lineTo(o.absoluteX+o.width,s.absoluteY+s.height/2),t.stroke();break}})})}_resizeView(i,t){const s=this.view;s.style.width=`${i}px`,s.style.height=`${t}px`,s.dataset.width=String(i),s.dataset.height=String(t),s.width=Math.max(1,Math.floor(i*this.pixelRatio)),s.height=Math.max(1,Math.floor(t*this.pixelRatio))}_setContextStyle(i){const t=this.context;t.shadowColor=i.shadowColor,t.shadowOffsetX=i.shadowOffsetX,t.shadowOffsetY=i.shadowOffsetY,t.shadowBlur=i.shadowBlur,t.strokeStyle=i.textStrokeColor,t.lineWidth=i.textStrokeWidth,t.fillStyle=i.color,t.direction=i.direction,t.textAlign=i.textAlign,t.textBaseline=i.textBaseline,t.font=[i.fontStyle,i.fontWeight,`${i.fontSize}px`,i.fontFamily].join(" "),t.fontKerning=i.fontKerning,t.letterSpacing=`${i.letterSpacing}px`}update(){const i=this.context;let{width:t,height:s}=this.style;t==="auto"&&(t=0),s==="auto"&&(s=0);const o=this.measure(t,s);t||(t=o.width),s=Math.max(s,o.height),this._resizeView(t,s);const n=this.pixelRatio;i.scale(n,n),i.clearRect(0,0,i.canvas.width,i.canvas.height),this._draw(o.paragraphs)}}x.Text=f,Object.defineProperty(x,Symbol.toStringTag,{value:"Module"})});
1
+ (function(p,g){typeof exports=="object"&&typeof module<"u"?g(exports):typeof define=="function"&&define.amd?define(["exports"],g):(p=typeof globalThis<"u"?globalThis:p||self,g(p.modernText={}))})(this,function(p){"use strict";class g{static get defaultStyle(){return{width:"auto",height:"auto",color:"#000000",fontSize:14,fontWeight:"normal",fontFamily:"sans-serif",fontStyle:"normal",fontKerning:"normal",textWrap:"wrap",textAlign:"start",verticalAlign:"baseline",textDecoration:null,textStrokeWidth:0,textStrokeColor:"#000000",direction:"inherit",lineHeight:1,letterSpacing:0,shadowColor:"#000000",shadowOffsetX:0,shadowOffsetY:0,shadowBlur:0}}constructor(i={}){const{view:t=document.createElement("canvas"),pixelRatio:l=window.devicePixelRatio||1,content:o="",style:s}=i;this.view=t,this.context=t.getContext("2d"),this.pixelRatio=l,this.content=o,this.style={...g.defaultStyle,...s},this.update()}measure(i=0,t=0){let l=this._createParagraphs(this.content);l=this._createWrapedParagraphs(l,i);const o=this.context;let s=0;for(let c=l.length,x=0;x<c;x++){const e=l[x];e.contentBox.top=s;let h=0,d=null;for(const n of e.fragments){this._setContextStyle({...n.style,textAlign:"left",verticalAlign:"baseline"});const r=o.measureText(n.content);n.inlineBox.left=h,n.inlineBox.top=s,n.inlineBox.width=r.width,n.inlineBox.height=n.style.fontSize*n.style.lineHeight,n.contentBox.left=n.inlineBox.left,n.contentBox.width=n.inlineBox.width,n.contentBox.height=n.style.fontSize,n.contentBox.top=n.inlineBox.top+(n.inlineBox.height-n.contentBox.height)/2,n.textBox.width=r.actualBoundingBoxLeft+r.actualBoundingBoxRight,n.textBox.height=r.actualBoundingBoxAscent+r.actualBoundingBoxDescent,n.textBox.left=n.contentBox.left+(n.contentBox.width-n.textBox.width)/2,n.textBox.top=n.contentBox.top+(n.contentBox.height-n.textBox.height)/2,n.baseline=n.textBox.top+r.actualBoundingBoxAscent,h+=n.contentBox.width+n.style.letterSpacing,e.contentBox.height<n.contentBox.height&&(d=n),e.contentBox.left=Math.min(e.contentBox.left,n.contentBox.left),e.contentBox.top=Math.min(e.contentBox.top,n.contentBox.top),e.contentBox.height=Math.max(e.contentBox.height,n.contentBox.height),e.lineBox.height=Math.max(e.lineBox.height,n.inlineBox.height)}const B=e.fragments[e.fragments.length-1];e.contentBox.width=Math.max(e.contentBox.width,B?B.contentBox.left+Math.max(B.contentBox.width,B.textBox.width):0),e.lineBox.left=0,e.lineBox.top=s,e.lineBox.width=Math.max(i,e.contentBox.width),this._setContextStyle({...(d??e).style,textAlign:"left",verticalAlign:"baseline"});const f=o.measureText("X");e.baseline=e.lineBox.top+(e.lineBox.height-f.actualBoundingBoxAscent+f.actualBoundingBoxDescent)/2+f.actualBoundingBoxAscent,s+=e.lineBox.height}const a=l.reduce((c,x)=>(c.left=Math.min(c.left,x.lineBox.left),c.top=Math.min(c.top,x.lineBox.top),c.width=Math.max(c.width,x.lineBox.width),c.height+=x.lineBox.height,c),{left:0,top:0,width:0,height:0});for(let c=l.length,x=0;x<c;x++){const e=l[x];e.fragments.forEach(h=>{const d=h.inlineBox.left,B=h.inlineBox.top,f=e.lineBox.height-h.inlineBox.height;switch(h.style.textAlign){case"end":case"right":h.inlineBox.left+=e.lineBox.width-e.contentBox.width;break;case"center":h.inlineBox.left+=(e.lineBox.width-e.contentBox.width)/2;break;case"start":case"left":default:h.inlineBox.left+=e.lineBox.left;break}switch(h.style.verticalAlign){case"top":h.inlineBox.top=e.lineBox.top;break;case"middle":h.inlineBox.top=e.lineBox.top+f/2;break;case"bottom":h.inlineBox.top=e.lineBox.top+f;break;case"sub":case"text-top":case"text-bottom":break;case"baseline":default:h.inlineBox.height<e.lineBox.height&&(h.inlineBox.top+=e.baseline-h.baseline);break}const n=h.inlineBox.left-d,r=h.inlineBox.top-B;h.contentBox.left+=n,h.contentBox.top+=r,h.textBox.left+=n,h.textBox.top+=r})}return{...a,paragraphs:l}}_createParagraphs(i){const t=(s={})=>{const{width:a,height:c,...x}=this.style;return{contentBox:{left:0,top:0,width:0,height:0},lineBox:{left:0,top:0,width:0,height:0},baseline:0,fragments:[],...s,style:{...x,...s.style}}},l=(s={})=>{const a=[],{width:c,height:x,...e}=this.style;return a.push({contentBox:{left:0,top:0,width:0,height:0},inlineBox:{left:0,top:0,width:0,height:0},textBox:{left:0,top:0,width:0,height:0},baseline:0,...s,style:{...e,...s.style},content:s.content??""}),a},o=[];if(typeof i=="string")o.push(t({fragments:l({content:i})}));else{i=Array.isArray(i)?i:[i];for(const s of i)if("fragments"in s){const{fragments:a,...c}=s,x=t({style:c});for(const e of a){const{content:h,...d}=e;x.fragments.push(...l({content:h,style:{...c,...d}}))}o.push(x)}else if("content"in s){const{content:a,...c}=s,x=t({style:c});x.fragments.push(...l({content:a,style:c})),o.push(x)}}return o}_createWrapedParagraphs(i,t){const l=[],o=i.slice();let s,a;for(;s=o.shift();){const c=s.fragments.slice();let x=0;const e=[];let h=!0;for(;a=c.shift();){const d=a.style;this._setContextStyle(d);let B="",f=!1;for(const n of a.content){const r=this.context.measureText(n).width+(h?0:d.letterSpacing),y=/^[\r\n]$/.test(n);if(y||d.textWrap==="wrap"&&t&&x+r>t){let u=y?B.length+1:B.length;!x&&!u&&(B+=n,u++),B.length&&e.push({...a,text:B}),e.length&&(l.push({baseline:s.baseline,style:{...s.style},contentBox:{...s.contentBox},lineBox:{...s.lineBox},fragments:e.slice()}),e.length=0);const w=a.content.substring(u);(w.length||c.length)&&o.unshift({baseline:s.baseline,style:{...s.style},contentBox:{...s.contentBox},lineBox:{...s.lineBox},fragments:(w.length?[{...a,content:w}]:[]).concat(c.slice())}),c.length=0,f=!0;break}else x+=r;B+=n}f||e.push({...a}),h=!1}e.length&&l.push({...s,fragments:e})}return l}_draw(i){const t=this.context;this.style.backgroundColor&&(t.fillStyle=this.style.backgroundColor,t.fillRect(0,0,t.canvas.width,t.canvas.height)),i.forEach(l=>{l.style.backgroundColor&&(t.fillStyle=l.style.backgroundColor,t.fillRect(l.lineBox.left,l.lineBox.top,l.lineBox.width,l.lineBox.height))}),i.forEach(l=>{l.fragments.forEach(o=>{switch(o.style.backgroundColor&&(t.fillStyle=o.style.backgroundColor,t.fillRect(o.inlineBox.left,o.inlineBox.top,o.inlineBox.width,o.inlineBox.height)),this._setContextStyle({...o.style,textAlign:"left",verticalAlign:"top"}),o.style.textStrokeWidth&&t.strokeText(o.content,o.contentBox.left,o.contentBox.top),t.fillText(o.content,o.contentBox.left,o.contentBox.top),o.style.textDecoration){case"underline":t.beginPath(),t.moveTo(o.contentBox.left,o.contentBox.top+o.contentBox.height-2),t.lineTo(o.contentBox.left+o.contentBox.width,o.contentBox.top+o.contentBox.height-2),t.stroke();break;case"line-through":t.beginPath(),t.moveTo(o.contentBox.left,o.contentBox.top+o.contentBox.height/2),t.lineTo(o.contentBox.left+o.contentBox.width,o.contentBox.top+o.contentBox.height/2),t.stroke();break}})})}_resizeView(i,t){const l=this.view;l.style.width=`${i}px`,l.style.height=`${t}px`,l.dataset.width=String(i),l.dataset.height=String(t),l.width=Math.max(1,Math.floor(i*this.pixelRatio)),l.height=Math.max(1,Math.floor(t*this.pixelRatio))}_setContextStyle(i){const t=this.context;switch(t.shadowColor=i.shadowColor,t.shadowOffsetX=i.shadowOffsetX,t.shadowOffsetY=i.shadowOffsetY,t.shadowBlur=i.shadowBlur,t.strokeStyle=i.textStrokeColor,t.lineWidth=i.textStrokeWidth,t.fillStyle=i.color,t.direction=i.direction,t.textAlign=i.textAlign,i.verticalAlign){case"baseline":t.textBaseline="alphabetic";break;case"top":case"middle":case"bottom":t.textBaseline=i.verticalAlign;break}t.font=[i.fontStyle,i.fontWeight,`${i.fontSize}px`,i.fontFamily].join(" "),t.fontKerning=i.fontKerning,t.letterSpacing=`${i.letterSpacing}px`}update(){const i=this.context;let{width:t,height:l}=this.style;t==="auto"&&(t=0),l==="auto"&&(l=0);const o=this.measure(t,l);t||(t=o.width),l=Math.max(l,o.height),this._resizeView(t,l);const s=this.pixelRatio;i.scale(s,s),i.clearRect(0,0,i.canvas.width,i.canvas.height),this._draw(o.paragraphs)}}p.Text=g,Object.defineProperty(p,Symbol.toStringTag,{value:"Module"})});
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- class p {
1
+ class w {
2
2
  static get defaultStyle() {
3
3
  return {
4
4
  width: "auto",
@@ -11,7 +11,7 @@ class p {
11
11
  fontKerning: "normal",
12
12
  textWrap: "wrap",
13
13
  textAlign: "start",
14
- textBaseline: "middle",
14
+ verticalAlign: "baseline",
15
15
  textDecoration: null,
16
16
  textStrokeWidth: 0,
17
17
  textStrokeColor: "#000000",
@@ -24,198 +24,236 @@ class p {
24
24
  shadowBlur: 0
25
25
  };
26
26
  }
27
- constructor(a = {}) {
27
+ constructor(i = {}) {
28
28
  const {
29
29
  view: t = document.createElement("canvas"),
30
- pixelRatio: i = window.devicePixelRatio || 1,
31
- data: o = "",
32
- style: n
33
- } = a;
34
- this.view = t, this.context = t.getContext("2d"), this.pixelRatio = i, this.data = o, this.style = {
35
- ...p.defaultStyle,
36
- ...n
30
+ pixelRatio: l = window.devicePixelRatio || 1,
31
+ content: o = "",
32
+ style: h
33
+ } = i;
34
+ this.view = t, this.context = t.getContext("2d"), this.pixelRatio = l, this.content = o, this.style = {
35
+ ...w.defaultStyle,
36
+ ...h
37
37
  }, this.update();
38
38
  }
39
- measure(a = 0, t = 0) {
40
- let i = this._createParagraphs(this.data);
41
- i = this._createWrapedParagraphs(i, a);
39
+ measure(i = 0, t = 0) {
40
+ let l = this._createParagraphs(this.content);
41
+ l = this._createWrapedParagraphs(l, i);
42
42
  const o = this.context;
43
- let n = 0;
44
- for (let h = i.length, r = 0; r < h; r++) {
45
- const e = i[r];
46
- e.relativeX = 0, e.relativeY = n, e.width = 0, e.height = 0;
47
- let s = 0, u;
48
- for (const c of e.fragments) {
49
- const f = c.style;
50
- this._setContextStyle(f);
51
- const d = o.measureText(c.data), g = d.width, x = d.actualBoundingBoxRight + d.actualBoundingBoxLeft;
52
- c.relativeX = s, c.relativeY = e.relativeY, c.width = g, c.actualBoundingBoxWidth = x, c.height = f.fontSize * f.lineHeight, s += g + f.letterSpacing, u = c, e.height = Math.max(e.height, c.height);
43
+ let h = 0;
44
+ for (let c = l.length, x = 0; x < c; x++) {
45
+ const e = l[x];
46
+ e.contentBox.top = h;
47
+ let s = 0, d = null;
48
+ for (const n of e.fragments) {
49
+ this._setContextStyle({
50
+ ...n.style,
51
+ textAlign: "left",
52
+ verticalAlign: "baseline"
53
+ });
54
+ const r = o.measureText(n.content);
55
+ n.inlineBox.left = s, n.inlineBox.top = h, n.inlineBox.width = r.width, n.inlineBox.height = n.style.fontSize * n.style.lineHeight, n.contentBox.left = n.inlineBox.left, n.contentBox.width = n.inlineBox.width, n.contentBox.height = n.style.fontSize, n.contentBox.top = n.inlineBox.top + (n.inlineBox.height - n.contentBox.height) / 2, n.textBox.width = r.actualBoundingBoxLeft + r.actualBoundingBoxRight, n.textBox.height = r.actualBoundingBoxAscent + r.actualBoundingBoxDescent, n.textBox.left = n.contentBox.left + (n.contentBox.width - n.textBox.width) / 2, n.textBox.top = n.contentBox.top + (n.contentBox.height - n.textBox.height) / 2, n.baseline = n.textBox.top + r.actualBoundingBoxAscent, s += n.contentBox.width + n.style.letterSpacing, e.contentBox.height < n.contentBox.height && (d = n), e.contentBox.left = Math.min(e.contentBox.left, n.contentBox.left), e.contentBox.top = Math.min(e.contentBox.top, n.contentBox.top), e.contentBox.height = Math.max(e.contentBox.height, n.contentBox.height), e.lineBox.height = Math.max(e.lineBox.height, n.inlineBox.height);
53
56
  }
54
- e.width = u ? u.relativeX + Math.max(u.width, u.actualBoundingBoxWidth) : 0, n += e.height;
57
+ const B = e.fragments[e.fragments.length - 1];
58
+ e.contentBox.width = Math.max(
59
+ e.contentBox.width,
60
+ B ? B.contentBox.left + Math.max(B.contentBox.width, B.textBox.width) : 0
61
+ ), e.lineBox.left = 0, e.lineBox.top = h, e.lineBox.width = Math.max(i, e.contentBox.width), this._setContextStyle({
62
+ ...(d ?? e).style,
63
+ textAlign: "left",
64
+ verticalAlign: "baseline"
65
+ });
66
+ const f = o.measureText("X");
67
+ e.baseline = e.lineBox.top + (e.lineBox.height - f.actualBoundingBoxAscent + f.actualBoundingBoxDescent) / 2 + f.actualBoundingBoxAscent, h += e.lineBox.height;
55
68
  }
56
- const l = i.reduce((h, r) => (h.x = Math.min(h.x, r.relativeX), h.y = Math.min(h.y, r.relativeY), h.width = Math.max(h.width, r.width), h.height += r.height, h), { x: 0, y: 0, width: 0, height: 0 });
57
- a = a || l.width, t = Math.max(t, l.height);
58
- for (let h = i.length, r = 0; r < h; r++) {
59
- const e = i[r];
60
- switch (this.style.textAlign) {
61
- case "center":
62
- e.absoluteX = (a - l.width) / 2, e.fragments.forEach((s) => {
63
- s.absoluteX = e.absoluteX + s.relativeX, s.fillX = s.absoluteX + s.width / 2;
64
- });
65
- break;
66
- case "end":
67
- case "right":
68
- e.absoluteX = a - l.width, e.fragments.forEach((s) => {
69
- s.absoluteX = e.absoluteX + s.relativeX, s.fillX = s.absoluteX + s.width;
70
- });
71
- break;
72
- case "start":
73
- case "left":
74
- default:
75
- e.absoluteX = 0, e.fragments.forEach((s) => {
76
- s.absoluteX = e.absoluteX + s.relativeX, s.fillX = s.absoluteX;
77
- });
78
- break;
79
- }
80
- switch (this.style.textBaseline) {
81
- case "top":
82
- case "hanging":
83
- e.absoluteY = e.relativeY, e.fragments.forEach((s) => {
84
- s.absoluteY = e.absoluteY, s.fillY = s.absoluteY;
85
- });
86
- break;
87
- case "middle":
88
- case "alphabetic":
89
- case "ideographic":
90
- e.absoluteY = e.relativeY + (t - l.height) / 2, e.fragments.forEach((s) => {
91
- s.absoluteY = e.absoluteY, s.fillY = s.absoluteY + e.height / 2;
92
- });
93
- break;
94
- case "bottom":
95
- e.absoluteY = e.relativeY + t - l.height, e.fragments.forEach((s) => {
96
- s.absoluteY = e.absoluteY, s.fillY = s.absoluteY + e.height;
97
- });
98
- break;
99
- }
69
+ const a = l.reduce((c, x) => (c.left = Math.min(c.left, x.lineBox.left), c.top = Math.min(c.top, x.lineBox.top), c.width = Math.max(c.width, x.lineBox.width), c.height += x.lineBox.height, c), { left: 0, top: 0, width: 0, height: 0 });
70
+ for (let c = l.length, x = 0; x < c; x++) {
71
+ const e = l[x];
72
+ e.fragments.forEach((s) => {
73
+ const d = s.inlineBox.left, B = s.inlineBox.top, f = e.lineBox.height - s.inlineBox.height;
74
+ switch (s.style.textAlign) {
75
+ case "end":
76
+ case "right":
77
+ s.inlineBox.left += e.lineBox.width - e.contentBox.width;
78
+ break;
79
+ case "center":
80
+ s.inlineBox.left += (e.lineBox.width - e.contentBox.width) / 2;
81
+ break;
82
+ case "start":
83
+ case "left":
84
+ default:
85
+ s.inlineBox.left += e.lineBox.left;
86
+ break;
87
+ }
88
+ switch (s.style.verticalAlign) {
89
+ case "top":
90
+ s.inlineBox.top = e.lineBox.top;
91
+ break;
92
+ case "middle":
93
+ s.inlineBox.top = e.lineBox.top + f / 2;
94
+ break;
95
+ case "bottom":
96
+ s.inlineBox.top = e.lineBox.top + f;
97
+ break;
98
+ case "sub":
99
+ case "text-top":
100
+ case "text-bottom":
101
+ break;
102
+ case "baseline":
103
+ default:
104
+ s.inlineBox.height < e.lineBox.height && (s.inlineBox.top += e.baseline - s.baseline);
105
+ break;
106
+ }
107
+ const n = s.inlineBox.left - d, r = s.inlineBox.top - B;
108
+ s.contentBox.left += n, s.contentBox.top += r, s.textBox.left += n, s.textBox.top += r;
109
+ });
100
110
  }
101
- return { ...l, paragraphs: i };
111
+ return { ...a, paragraphs: l };
102
112
  }
103
- _createParagraphs(a) {
104
- const t = {
105
- width: 0,
106
- height: 0,
107
- actualBoundingBoxWidth: 0,
108
- relativeX: 0,
109
- relativeY: 0,
110
- absoluteX: 0,
111
- absoluteY: 0
112
- }, i = (l = {}) => ({ ...t, fragments: [], ...l }), o = (l = {}) => {
113
- const h = [], { width: r, height: e, ...s } = this.style;
114
- return h.push({
115
- fillX: 0,
116
- fillY: 0,
117
- ...t,
118
- ...l,
113
+ _createParagraphs(i) {
114
+ const t = (h = {}) => {
115
+ const { width: a, height: c, ...x } = this.style;
116
+ return {
117
+ contentBox: { left: 0, top: 0, width: 0, height: 0 },
118
+ lineBox: { left: 0, top: 0, width: 0, height: 0 },
119
+ baseline: 0,
120
+ fragments: [],
121
+ ...h,
119
122
  style: {
120
- ...s,
121
- ...l.style
123
+ ...x,
124
+ ...h.style
125
+ }
126
+ };
127
+ }, l = (h = {}) => {
128
+ const a = [], { width: c, height: x, ...e } = this.style;
129
+ return a.push({
130
+ contentBox: { left: 0, top: 0, width: 0, height: 0 },
131
+ inlineBox: { left: 0, top: 0, width: 0, height: 0 },
132
+ textBox: { left: 0, top: 0, width: 0, height: 0 },
133
+ baseline: 0,
134
+ ...h,
135
+ style: {
136
+ ...e,
137
+ ...h.style
122
138
  },
123
- data: l.data ?? ""
124
- }), h;
125
- }, n = [];
126
- if (typeof a == "string")
127
- n.push(i({ fragments: o({ data: a }) }));
139
+ content: h.content ?? ""
140
+ }), a;
141
+ }, o = [];
142
+ if (typeof i == "string")
143
+ o.push(t({ fragments: l({ content: i }) }));
128
144
  else {
129
- a = Array.isArray(a) ? a : [a];
130
- for (const l of a) {
131
- const h = i();
132
- if ("fragments" in l) {
133
- const { fragments: r, ...e } = l;
134
- for (const s of r) {
135
- const { data: u, ...c } = s;
136
- h.fragments.push(...o({ data: u, style: { ...e, ...c } }));
145
+ i = Array.isArray(i) ? i : [i];
146
+ for (const h of i)
147
+ if ("fragments" in h) {
148
+ const { fragments: a, ...c } = h, x = t({ style: c });
149
+ for (const e of a) {
150
+ const { content: s, ...d } = e;
151
+ x.fragments.push(...l({ content: s, style: { ...c, ...d } }));
137
152
  }
138
- } else if ("data" in l) {
139
- const { data: r, ...e } = l;
140
- h.fragments.push(...o({ data: r, style: e }));
153
+ o.push(x);
154
+ } else if ("content" in h) {
155
+ const { content: a, ...c } = h, x = t({ style: c });
156
+ x.fragments.push(...l({ content: a, style: c })), o.push(x);
141
157
  }
142
- n.push(h);
143
- }
144
158
  }
145
- return n;
159
+ return o;
146
160
  }
147
- _createWrapedParagraphs(a, t) {
148
- const i = [], o = a.slice();
149
- let n, l;
150
- for (; n = o.shift(); ) {
151
- const h = n.fragments.slice();
152
- let r = 0;
161
+ _createWrapedParagraphs(i, t) {
162
+ const l = [], o = i.slice();
163
+ let h, a;
164
+ for (; h = o.shift(); ) {
165
+ const c = h.fragments.slice();
166
+ let x = 0;
153
167
  const e = [];
154
168
  let s = !0;
155
- for (; l = h.shift(); ) {
156
- const u = l.style;
157
- this._setContextStyle(u);
158
- let c = "", f = !1;
159
- for (const d of l.data) {
160
- const g = this.context.measureText(d).width + (s ? 0 : u.letterSpacing), x = /^[\r\n]$/.test(d);
161
- if (x || u.textWrap === "wrap" && t && r + g > t) {
162
- let w = x ? c.length + 1 : c.length;
163
- !r && !w && (c += d, w++), c.length && e.push({ ...l, text: c }), e.length && (i.push({ ...n, fragments: e.slice() }), e.length = 0);
164
- const b = l.data.substring(w);
165
- (b.length || h.length) && o.unshift({
166
- ...n,
167
- fragments: (b.length ? [{ ...l, data: b }] : []).concat(h.slice())
168
- }), h.length = 0, f = !0;
169
+ for (; a = c.shift(); ) {
170
+ const d = a.style;
171
+ this._setContextStyle(d);
172
+ let B = "", f = !1;
173
+ for (const n of a.content) {
174
+ const r = this.context.measureText(n).width + (s ? 0 : d.letterSpacing), u = /^[\r\n]$/.test(n);
175
+ if (u || d.textWrap === "wrap" && t && x + r > t) {
176
+ let g = u ? B.length + 1 : B.length;
177
+ !x && !g && (B += n, g++), B.length && e.push({ ...a, text: B }), e.length && (l.push({
178
+ baseline: h.baseline,
179
+ style: { ...h.style },
180
+ contentBox: { ...h.contentBox },
181
+ lineBox: { ...h.lineBox },
182
+ fragments: e.slice()
183
+ }), e.length = 0);
184
+ const p = a.content.substring(g);
185
+ (p.length || c.length) && o.unshift({
186
+ baseline: h.baseline,
187
+ style: { ...h.style },
188
+ contentBox: { ...h.contentBox },
189
+ lineBox: { ...h.lineBox },
190
+ fragments: (p.length ? [{ ...a, content: p }] : []).concat(c.slice())
191
+ }), c.length = 0, f = !0;
169
192
  break;
170
193
  } else
171
- r += g;
172
- c += d;
194
+ x += r;
195
+ B += n;
173
196
  }
174
- f || e.push({ ...l }), s = !1;
197
+ f || e.push({ ...a }), s = !1;
175
198
  }
176
- e.length && i.push({ ...n, fragments: e });
199
+ e.length && l.push({ ...h, fragments: e });
177
200
  }
178
- return i;
201
+ return l;
179
202
  }
180
- _draw(a) {
203
+ _draw(i) {
181
204
  const t = this.context;
182
- a.forEach((i) => {
183
- i.fragments.forEach((o) => {
184
- const n = o.style;
185
- switch (this._setContextStyle(n), n.textStrokeWidth && t.strokeText(o.data, o.fillX, o.fillY), t.fillText(o.data, o.fillX, o.fillY), n.textDecoration) {
205
+ this.style.backgroundColor && (t.fillStyle = this.style.backgroundColor, t.fillRect(0, 0, t.canvas.width, t.canvas.height)), i.forEach((l) => {
206
+ l.style.backgroundColor && (t.fillStyle = l.style.backgroundColor, t.fillRect(l.lineBox.left, l.lineBox.top, l.lineBox.width, l.lineBox.height));
207
+ }), i.forEach((l) => {
208
+ l.fragments.forEach((o) => {
209
+ switch (o.style.backgroundColor && (t.fillStyle = o.style.backgroundColor, t.fillRect(o.inlineBox.left, o.inlineBox.top, o.inlineBox.width, o.inlineBox.height)), this._setContextStyle({
210
+ ...o.style,
211
+ textAlign: "left",
212
+ verticalAlign: "top"
213
+ }), o.style.textStrokeWidth && t.strokeText(o.content, o.contentBox.left, o.contentBox.top), t.fillText(o.content, o.contentBox.left, o.contentBox.top), o.style.textDecoration) {
186
214
  case "underline":
187
- t.beginPath(), t.moveTo(o.absoluteX, i.absoluteY + i.height - 2), t.lineTo(o.absoluteX + o.width, i.absoluteY + i.height - 2), t.stroke();
215
+ t.beginPath(), t.moveTo(o.contentBox.left, o.contentBox.top + o.contentBox.height - 2), t.lineTo(o.contentBox.left + o.contentBox.width, o.contentBox.top + o.contentBox.height - 2), t.stroke();
188
216
  break;
189
217
  case "line-through":
190
- t.beginPath(), t.moveTo(o.absoluteX, i.absoluteY + i.height / 2), t.lineTo(o.absoluteX + o.width, i.absoluteY + i.height / 2), t.stroke();
218
+ t.beginPath(), t.moveTo(o.contentBox.left, o.contentBox.top + o.contentBox.height / 2), t.lineTo(o.contentBox.left + o.contentBox.width, o.contentBox.top + o.contentBox.height / 2), t.stroke();
191
219
  break;
192
220
  }
193
221
  });
194
222
  });
195
223
  }
196
- _resizeView(a, t) {
197
- const i = this.view;
198
- i.style.width = `${a}px`, i.style.height = `${t}px`, i.dataset.width = String(a), i.dataset.height = String(t), i.width = Math.max(1, Math.floor(a * this.pixelRatio)), i.height = Math.max(1, Math.floor(t * this.pixelRatio));
224
+ _resizeView(i, t) {
225
+ const l = this.view;
226
+ l.style.width = `${i}px`, l.style.height = `${t}px`, l.dataset.width = String(i), l.dataset.height = String(t), l.width = Math.max(1, Math.floor(i * this.pixelRatio)), l.height = Math.max(1, Math.floor(t * this.pixelRatio));
199
227
  }
200
- _setContextStyle(a) {
228
+ _setContextStyle(i) {
201
229
  const t = this.context;
202
- t.shadowColor = a.shadowColor, t.shadowOffsetX = a.shadowOffsetX, t.shadowOffsetY = a.shadowOffsetY, t.shadowBlur = a.shadowBlur, t.strokeStyle = a.textStrokeColor, t.lineWidth = a.textStrokeWidth, t.fillStyle = a.color, t.direction = a.direction, t.textAlign = a.textAlign, t.textBaseline = a.textBaseline, t.font = [
203
- a.fontStyle,
204
- a.fontWeight,
205
- `${a.fontSize}px`,
206
- a.fontFamily
207
- ].join(" "), t.fontKerning = a.fontKerning, t.letterSpacing = `${a.letterSpacing}px`;
230
+ switch (t.shadowColor = i.shadowColor, t.shadowOffsetX = i.shadowOffsetX, t.shadowOffsetY = i.shadowOffsetY, t.shadowBlur = i.shadowBlur, t.strokeStyle = i.textStrokeColor, t.lineWidth = i.textStrokeWidth, t.fillStyle = i.color, t.direction = i.direction, t.textAlign = i.textAlign, i.verticalAlign) {
231
+ case "baseline":
232
+ t.textBaseline = "alphabetic";
233
+ break;
234
+ case "top":
235
+ case "middle":
236
+ case "bottom":
237
+ t.textBaseline = i.verticalAlign;
238
+ break;
239
+ }
240
+ t.font = [
241
+ i.fontStyle,
242
+ i.fontWeight,
243
+ `${i.fontSize}px`,
244
+ i.fontFamily
245
+ ].join(" "), t.fontKerning = i.fontKerning, t.letterSpacing = `${i.letterSpacing}px`;
208
246
  }
209
247
  update() {
210
- const a = this.context;
211
- let { width: t, height: i } = this.style;
212
- t === "auto" && (t = 0), i === "auto" && (i = 0);
213
- const o = this.measure(t, i);
214
- t || (t = o.width), i = Math.max(i, o.height), this._resizeView(t, i);
215
- const n = this.pixelRatio;
216
- a.scale(n, n), a.clearRect(0, 0, a.canvas.width, a.canvas.height), this._draw(o.paragraphs);
248
+ const i = this.context;
249
+ let { width: t, height: l } = this.style;
250
+ t === "auto" && (t = 0), l === "auto" && (l = 0);
251
+ const o = this.measure(t, l);
252
+ t || (t = o.width), l = Math.max(l, o.height), this._resizeView(t, l);
253
+ const h = this.pixelRatio;
254
+ i.scale(h, h), i.clearRect(0, 0, i.canvas.width, i.canvas.height), this._draw(o.paragraphs);
217
255
  }
218
256
  }
219
257
  export {
220
- p as Text
258
+ w as Text
221
259
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "modern-text",
3
3
  "type": "module",
4
- "version": "0.0.4",
4
+ "version": "0.0.6",
5
5
  "packageManager": "pnpm@8.14.1",
6
6
  "description": "Text measurement and rendering of canvas 2d",
7
7
  "author": "wxm",
package/types/Text.d.ts CHANGED
@@ -3,32 +3,32 @@ export type FontStyle = 'normal' | 'italic' | 'oblique' | `oblique ${string}`;
3
3
  export type FontKerning = 'auto' | 'none' | 'normal';
4
4
  export type TextWrap = 'wrap' | 'nowrap';
5
5
  export type TextAlign = 'center' | 'end' | 'left' | 'right' | 'start';
6
- export type TextBaseline = 'alphabetic' | 'bottom' | 'hanging' | 'ideographic' | 'middle' | 'top';
6
+ export type VerticalAlign = 'baseline' | 'top' | 'middle' | 'bottom' | 'sub' | 'text-top' | 'text-bottom';
7
7
  export type TextDecoration = 'underline' | 'line-through';
8
- export interface TextParagraph {
8
+ export interface BoundingBox {
9
+ left: number;
10
+ top: number;
9
11
  width: number;
10
12
  height: number;
11
- relativeX: number;
12
- relativeY: number;
13
- absoluteX: number;
14
- absoluteY: number;
13
+ }
14
+ export interface TextParagraph {
15
+ contentBox: BoundingBox;
16
+ lineBox: BoundingBox;
17
+ baseline: number;
15
18
  fragments: Array<TextFragment>;
19
+ style: TextFragmentStyle;
16
20
  }
17
21
  export interface TextFragment {
18
- width: number;
19
- height: number;
20
- actualBoundingBoxWidth: number;
21
- relativeX: number;
22
- relativeY: number;
23
- absoluteX: number;
24
- absoluteY: number;
25
- fillX: number;
26
- fillY: number;
27
- data: string;
22
+ contentBox: BoundingBox;
23
+ inlineBox: BoundingBox;
24
+ textBox: BoundingBox;
25
+ baseline: number;
26
+ content: string;
28
27
  style: TextFragmentStyle;
29
28
  }
30
29
  export interface TextFragmentStyle {
31
30
  color: string;
31
+ backgroundColor?: string;
32
32
  fontSize: number;
33
33
  fontWeight: FontWeight;
34
34
  fontFamily: string;
@@ -36,7 +36,7 @@ export interface TextFragmentStyle {
36
36
  fontKerning: FontKerning;
37
37
  textWrap: TextWrap;
38
38
  textAlign: TextAlign;
39
- textBaseline: TextBaseline;
39
+ verticalAlign: VerticalAlign;
40
40
  textDecoration: TextDecoration | null;
41
41
  textStrokeWidth: number;
42
42
  textStrokeColor: string;
@@ -52,27 +52,23 @@ export interface TextStyle extends TextFragmentStyle {
52
52
  width: number | 'auto';
53
53
  height: number | 'auto';
54
54
  }
55
- export interface TextParagraphWithDataAndStyle extends Partial<TextFragmentStyle> {
56
- data: string;
55
+ export interface TextParagraphWithContentAndStyle extends Partial<TextFragmentStyle> {
56
+ content: string;
57
57
  }
58
58
  export interface TextParagraphWithFragmentsAndStyle extends Partial<TextFragmentStyle> {
59
59
  fragments: Array<TextFragmentWithStyle>;
60
60
  }
61
61
  export interface TextFragmentWithStyle extends Partial<TextFragmentStyle> {
62
- data: string;
62
+ content: string;
63
63
  }
64
- export type TextData = string | TextParagraphWithDataAndStyle | TextParagraphWithFragmentsAndStyle | Array<TextParagraphWithDataAndStyle | TextParagraphWithFragmentsAndStyle>;
64
+ export type TextData = string | TextParagraphWithContentAndStyle | TextParagraphWithFragmentsAndStyle | Array<TextParagraphWithContentAndStyle | TextParagraphWithFragmentsAndStyle>;
65
65
  export interface TextOptions {
66
66
  view?: HTMLCanvasElement;
67
67
  pixelRatio?: number;
68
- data?: TextData;
68
+ content?: TextData;
69
69
  style?: Partial<TextStyle>;
70
70
  }
71
- export interface MeasureResult {
72
- x: number;
73
- y: number;
74
- width: number;
75
- height: number;
71
+ export interface MeasureResult extends BoundingBox {
76
72
  paragraphs: Array<TextParagraph>;
77
73
  }
78
74
  export declare class Text {
@@ -81,10 +77,10 @@ export declare class Text {
81
77
  readonly context: CanvasRenderingContext2D;
82
78
  pixelRatio: number;
83
79
  style: TextStyle;
84
- data: TextData;
80
+ content: TextData;
85
81
  constructor(options?: TextOptions);
86
- measure(width?: number, height?: number): MeasureResult;
87
- protected _createParagraphs(data: TextData): Array<TextParagraph>;
82
+ measure(width?: number, _height?: number): MeasureResult;
83
+ protected _createParagraphs(content: TextData): Array<TextParagraph>;
88
84
  protected _createWrapedParagraphs(paragraphs: Array<TextParagraph>, width: number): Array<TextParagraph>;
89
85
  protected _draw(paragraphs: Array<TextParagraph>): void;
90
86
  protected _resizeView(width: number, height: number): void;