text-metrics
Version:
An efficient text measurement set for the browser.
3 lines (2 loc) • 8.95 kB
JavaScript
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e||self).textMetrics={})}(this,function(e){function t(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function r(e){for(var r=1;r<arguments.length;r++){var n=null!=arguments[r]?arguments[r]:{};r%2?t(Object(n),!0).forEach(function(t){var r,s,i;r=e,i=n[t],(s=function(e){var t=function(e,t){if("object"!=typeof e||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,"string");if("object"!=typeof n)return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e);return"symbol"==typeof t?t:String(t)}(s=t))in r?Object.defineProperty(r,s,{value:i,enumerable:!0,configurable:!0,writable:!0}):r[s]=i}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):t(Object(n)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))})}return e}const n=new Set(["—"]),s=new Set([""]),i=new Set([" "," "," "," "," "," "," "," "," "," "," "," "," "," ","\t","","\u2028","\u2029"]),o=new Set(["֊","‐","‒","–","־","་","፡","៘","៚","‧","|","᛫","᛬","᛭","⁖","⁘","⁙","⁚","⁛","⁝","⁞","⸙","⸪","⸫","⸬","⸭","⸰","တ0","တ1","တ2","္F","ွ0","႑F","ቇ0"]),a=new Set(["´","´"]),l=new Set(["\n"]);function c(e,t){t||(t={});const r=Number.parseInt(S(t,"base-font-size",16),10),n=Number.parseFloat(e),s=e.replace(n,"");switch(s){case"rem":case"em":return n*r;case"pt":return n*(96/72);case"px":return n}throw new Error("The unit "+s+" is not supported")}function u(e,t){const r=new Set(["inherit","initial","unset","normal"]);let n=0;e&&!r.has(e)&&(n=c(e));let s=0;return t&&!r.has(t)&&(s=c(t)),e=>(e.trim().replace(/\s+/gi," ").split(" ").length-1)*n+e.length*s}function p(e,t){const r=[],n=S(t,"font-weight",e.getPropertyValue("font-weight"))||"400";["normal","bold","bolder","lighter","100","200","300","400","500","600","700","800","900"].includes(n.toString())&&r.push(n);const s=S(t,"font-style",e.getPropertyValue("font-style"));["normal","italic","oblique"].includes(s)&&r.push(s);const i=S(t,"font-variant",e.getPropertyValue("font-variant"));["normal","small-caps"].includes(i)&&r.push(i);const o=c(S(t,"font-size",e.getPropertyValue("font-size"))||"16px");r.push(o+"px");const a=S(t,"font-family",e.getPropertyValue("font-family"))||"Helvetica, Arial, sans-serif";return r.push(a),r.join(" ")}function h(e){return e&&"function"==typeof e.getPropertyValue}function f(e){return d(e)&&e.style&&"undefined"!=typeof window&&"function"==typeof window.getComputedStyle}function d(e){return"object"==typeof HTMLElement?e instanceof HTMLElement:Boolean(e&&"object"==typeof e&&null!==e&&1===e.nodeType&&"string"==typeof e.nodeName)}function g(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function y(e,t){const n=r({},t),{style:s}=n;return t||(t={}),h(s)?s:f(e)?window.getComputedStyle(e,S(t,"pseudoElt",null)):{getPropertyValue:e=>S(t,e)}}function w(e,t){switch(t){case"pre":case"pre-wrap":return e;case"pre-line":return(e||"").replace(/\s+/gm," ").trim();default:return(e||"").replace(/[\r\n]/gm," ").replace(/\s+/gm," ").trim()}}function m(e,t){switch(t.getPropertyValue("text-transform")){case"uppercase":return e.toUpperCase();case"lowercase":return e.toLowerCase();default:return e}}function b(e){return e=(e||"").replace(/<wbr>/gi,"").replace(/<br\s*\/?>/gi,"\n").replace(/­/gi,"").replace(/—/gi,"—"),/&#(\d+)(;?)|&#[xX]([a-fA-F\d]+)(;?)|&([\da-zA-Z]+);/g.test(e)&&console&&console.error("text-metrics: Found encoded htmlenties. You may want to use https://mths.be/he to decode your text first."),e}function x(e){return e&&(e.textContent||e.textContent)||""}function S(e,t,r){return e&&void 0!==e[t]&&e[t]||r}function v(e){const t={};for(const r of Object.keys(e||{}))t[r.replace(/([A-Z])/g,e=>"-"+e.toLowerCase())]=e[r];return t}function P(e){try{const t=document.createElement("canvas").getContext("2d"),r=window.devicePixelRatio||1,n=t.webkitBackingStorePixelRatio||t.mozBackingStorePixelRatio||t.msBackingStorePixelRatio||t.oBackingStorePixelRatio||t.backingStorePixelRatio||1;return t.font=e,t.setTransform(r/n,0,0,r/n,0,0),t}catch(e){throw new Error("Canvas support required"+e.message)}}function j(e){return(n.has(e)?"B2":i.has(e)&&"BAI")||s.has(e)&&"SHY"||o.has(e)&&"BA"||a.has(e)&&"BB"||l.has(e)&&"BK"}function B({ctx:e,text:t,max:r,wordSpacing:n,letterSpacing:s}){const i=u(n,s),o=[],a=[],l=[];let c="",p="";if(!t)return[];for(const e of t){const t=j(e);""===p&&"BAI"===t||(t?(l.push({chr:e,type:t}),a.push(p),p=""):p+=e)}p&&a.push(p);for(const[t,n]of a.entries()){if(0===t){c=n;continue}const s=l[t-1],a="SHY"===s.type?"":s.chr;if("BK"===s.type){o.push(c),c=n;continue}const u=e.measureText(c+a+n).width+i(c+a+n);if(Math.round(u)<=r)c+=a+n;else switch(s.type){case"SHY":o.push(c+"-"),c=n;break;case"BA":o.push(c+a),c=n;break;case"BAI":o.push(c),c=n;break;case"BB":o.push(c),c=a+n;break;case"B2":Number.parseInt(e.measureText(c+a).width+i(c+a),10)<=r?(o.push(c+a),c=n):Number.parseInt(e.measureText(a+n).width+i(a+n),10)<=r?(o.push(c),c=a+n):(o.push(c,a),c=n);break;default:throw new Error("Undefoined break")}}return[...c].length>0&&o.push(c),o}function k({ctx:e,text:t,max:r,wordSpacing:n,letterSpacing:s}){const o=u(n,s),a=[];let l="",c=0;if(!t)return[];for(const n of t){const s=j(n);if("BK"===s){a.push(l),l="";continue}const u=l.length;if(i.has(n)&&(0===u||i.has(l[u-1])))continue;let p=e.measureText(l+n).width+o(l+n),h=Math.ceil(p);if("SHY"===s){const r=t[c+1]||"";p=e.measureText(l+n+r).width+o(l+n+r),h=Math.ceil(p)}if(h>r&&[...l].length>0)switch(s){case"SHY":a.push(l+"-"),l="";break;case"BA":a.push(l+n),l="";break;case"BAI":a.push(l),l="";break;default:a.push(l),l=n}else""!==n&&(l+=n);c++}return[...l].length>0&&a.push(l),a}var O={__proto__:null,addWordAndLetterSpacing:u,getFont:p,isCSSStyleDeclaration:h,canGetComputedStyle:f,isElement:d,isObject:g,getStyle:y,normalizeWhitespace:w,getStyledText:m,prepareText:b,getText:x,prop:S,normalizeOptions:v,getContext2d:P,computeLinesDefault:B,computeLinesBreakAll:k};class A{constructor(e,t={}){!d(e)&&g(e)?(this.el=void 0,this.overwrites=v(e)):(this.el=e,this.overwrites=v(t)),this.style=y(this.el,this.overwrites),this.font=S(t,"font",null)||p(this.style,this.overwrites)}padding(){return this.el?Number.parseInt(this.style.paddingLeft||0,10)+Number.parseInt(this.style.paddingRight||0,10):0}parseArgs(e,t={},n={}){"object"==typeof e&&e&&(n=t,t=e||{},e=void 0);const s=r(r({},this.overwrites),v(n)),i=S(s,"white-space")||this.style.getPropertyValue("white-space");return t||(t={}),n||(t={}),{text:e=!e&&this.el?w(x(this.el),i):b(w(e,i)),options:t,overwrites:n,styles:s}}width(){const{text:e,options:t,overwrites:r,styles:n}=this.parseArgs(...[].slice.call(arguments));if(!e)return 0;const s=p(this.style,n),i=S(n,"letter-spacing")||this.style.getPropertyValue("letter-spacing"),o=u(S(n,"word-spacing")||this.style.getPropertyValue("word-spacing"),i),a=P(s),l=m(e,this.style);return t.multiline?this.lines(l,t,r).reduce((e,t)=>{const r=a.measureText(t).width+o(t);return Math.max(e,r)},0):a.measureText(l).width+o(l)}height(){const{text:e,options:t,styles:r}=this.parseArgs(...[].slice.call(arguments)),n=Number.parseFloat(S(r,"line-height")||this.style.getPropertyValue("line-height"));return Math.ceil(this.lines(e,t,r).length*n||0)}lines(){const{text:e,options:t,overwrites:r,styles:n}=this.parseArgs(...[].slice.call(arguments)),s=p(this.style,n);let i=Number.parseInt(S(t,"width")||S(r,"width"),10)||S(this.el,"offsetWidth",0)||Number.parseInt(S(n,"width",0),10)||Number.parseInt(this.style.width,10);i-=this.padding();const o=S(n,"word-break")||this.style.getPropertyValue("word-break"),a=S(n,"letter-spacing")||this.style.getPropertyValue("letter-spacing"),l=S(n,"word-spacing")||this.style.getPropertyValue("word-spacing"),c=P(s),u=m(e,this.style);return"break-all"===o?k({ctx:c,text:u,max:i,wordSpacing:l,letterSpacing:a}):B({ctx:c,text:u,max:i,wordSpacing:l,letterSpacing:a})}maxFontSize(){const{text:e,options:t,overwrites:n,styles:s}=this.parseArgs(...[].slice.call(arguments)),i=n=>Math.ceil(this.width(e,t,r(r({},s),{},{"font-size":n+"px"})));let o=Number.parseInt(S(t,"width")||S(n,"width"),10)||S(this.el,"offsetWidth",0)||Number.parseInt(S(s,"width",0),10)||Number.parseInt(this.style.width,10);o-=this.padding();let a=Math.floor(o/2),l=i(a);if(a=Math.floor(a/l*o),l=i(a),Math.ceil(l)===o)return a?a+"px":void 0;const c=l>o&&a>0;for(;l>o&&a>0;)a-=1,l=i(a);if(!c)for(;l<o;){if(l=i(a+1),l>o)return a?a+"px":void 0;a+=1}return a?a+"px":void 0}}const T=r({},O);e.init=(e,t)=>new A(e,t),e.utils=T});
//# sourceMappingURL=text-metrics.js.map