text-metrics
Version:
An efficient text measurement set for the browser.
1 lines • 31.3 kB
Source Map (JSON)
{"version":3,"file":"text-metrics.mjs","sources":["../src/utils.js","../src/index.js"],"sourcesContent":["/*\n B2\tBreak Opportunity Before and After\tEm dash\tProvide a line break opportunity before and after the character\n BA\tBreak After\tSpaces, hyphens\tGenerally provide a line break opportunity after the character\n BB\tBreak Before\tPunctuation used in dictionaries\tGenerally provide a line break opportunity before the character\n HY\tHyphen\tHYPHEN-MINUS\tProvide a line break opportunity after the character, except in numeric context\n CB\tContingent Break Opportunity\tInline objects\tProvide a line break opportunity contingent on additional information\n */\n\n// B2 Break Opportunity Before and After - http://www.unicode.org/reports/tr14/#B2\nconst B2 = new Set(['\\u2014']);\n\nconst SHY = new Set([\n // Soft hyphen\n '\\u00AD',\n]);\n\n// BA: Break After (remove on break) - http://www.unicode.org/reports/tr14/#BA\nconst BAI = new Set([\n // Spaces\n '\\u0020',\n '\\u1680',\n '\\u2000',\n '\\u2001',\n '\\u2002',\n '\\u2003',\n '\\u2004',\n '\\u2005',\n '\\u2006',\n '\\u2008',\n '\\u2009',\n '\\u200A',\n '\\u205F',\n '\\u3000',\n // Tab\n '\\u0009',\n // ZW Zero Width Space - http://www.unicode.org/reports/tr14/#ZW\n '\\u200B',\n // Mandatory breaks not interpreted by html\n '\\u2028',\n '\\u2029',\n]);\n\nconst BA = new Set([\n // Hyphen\n '\\u058A',\n '\\u2010',\n '\\u2012',\n '\\u2013',\n // Visible Word Dividers\n '\\u05BE',\n '\\u0F0B',\n '\\u1361',\n '\\u17D8',\n '\\u17DA',\n '\\u2027',\n '\\u007C',\n // Historic Word Separators\n '\\u16EB',\n '\\u16EC',\n '\\u16ED',\n '\\u2056',\n '\\u2058',\n '\\u2059',\n '\\u205A',\n '\\u205B',\n '\\u205D',\n '\\u205E',\n '\\u2E19',\n '\\u2E2A',\n '\\u2E2B',\n '\\u2E2C',\n '\\u2E2D',\n '\\u2E30',\n '\\u10100',\n '\\u10101',\n '\\u10102',\n '\\u1039F',\n '\\u103D0',\n '\\u1091F',\n '\\u12470',\n]);\n\n// BB: Break Before - http://www.unicode.org/reports/tr14/#BB\nconst BB = new Set(['\\u00B4', '\\u1FFD']);\n\n// BK: Mandatory Break (A) (Non-tailorable) - http://www.unicode.org/reports/tr14/#BK\nconst BK = new Set(['\\u000A']);\n\n/* eslint-env es6, browser */\nconst DEFAULTS = {\n 'font-size': '16px',\n 'font-weight': '400',\n 'font-family': 'Helvetica, Arial, sans-serif',\n};\n\n/**\n * We only support rem/em/pt conversion\n * @param val\n * @param options\n * @return {*}\n */\nfunction pxValue(value_, options) {\n if (!options) {\n options = {};\n }\n\n const baseFontSize = Number.parseInt(prop(options, 'base-font-size', 16), 10);\n\n const value = Number.parseFloat(value_);\n const unit = value_.replace(value, '');\n // eslint-disable-next-line default-case\n switch (unit) {\n case 'rem':\n case 'em': {\n return value * baseFontSize;\n }\n\n case 'pt': {\n return value * (96 / 72);\n }\n\n case 'px': {\n return value;\n }\n }\n\n throw new Error('The unit ' + unit + ' is not supported');\n}\n\n/**\n * Get computed word- and letter spacing for text\n * @param ws\n * @param ls\n * @return {function(*)}\n */\nexport function addWordAndLetterSpacing(ws, ls) {\n const denyList = new Set(['inherit', 'initial', 'unset', 'normal']);\n\n let wordAddon = 0;\n if (ws && !denyList.has(ws)) {\n wordAddon = pxValue(ws);\n }\n\n let letterAddon = 0;\n if (ls && !denyList.has(ls)) {\n letterAddon = pxValue(ls);\n }\n\n return (text) => {\n const words = text.trim().replace(/\\s+/gi, ' ').split(' ').length - 1;\n const chars = text.length;\n\n return words * wordAddon + chars * letterAddon;\n };\n}\n\n/**\n * Map css styles to canvas font property\n *\n * font: font-style font-variant font-weight font-size/line-height font-family;\n * http://www.w3schools.com/tags/canvas_font.asp\n *\n * @param {CSSStyleDeclaration} style\n * @param {object} options\n * @returns {string}\n */\nexport function getFont(style, options) {\n const font = [];\n\n const fontWeight = prop(options, 'font-weight', style.getPropertyValue('font-weight')) || DEFAULTS['font-weight'];\n if (\n ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '700', '800', '900'].includes(\n fontWeight.toString()\n )\n ) {\n font.push(fontWeight);\n }\n\n const fontStyle = prop(options, 'font-style', style.getPropertyValue('font-style'));\n if (['normal', 'italic', 'oblique'].includes(fontStyle)) {\n font.push(fontStyle);\n }\n\n const fontVariant = prop(options, 'font-variant', style.getPropertyValue('font-variant'));\n if (['normal', 'small-caps'].includes(fontVariant)) {\n font.push(fontVariant);\n }\n\n const fontSize = prop(options, 'font-size', style.getPropertyValue('font-size')) || DEFAULTS['font-size'];\n const fontSizeValue = pxValue(fontSize);\n font.push(fontSizeValue + 'px');\n\n const fontFamily = prop(options, 'font-family', style.getPropertyValue('font-family')) || DEFAULTS['font-family'];\n font.push(fontFamily);\n\n return font.join(' ');\n}\n\n/**\n * Check for CSSStyleDeclaration\n *\n * @param val\n * @returns {bool}\n */\nexport function isCSSStyleDeclaration(value) {\n return value && typeof value.getPropertyValue === 'function';\n}\n\n/**\n * Check wether we can get computed style\n *\n * @param el\n * @returns {bool}\n */\nexport function canGetComputedStyle(element) {\n return (\n isElement(element) &&\n element.style &&\n typeof window !== 'undefined' &&\n typeof window.getComputedStyle === 'function'\n );\n}\n\n/**\n * Check for DOM element\n *\n * @param el\n * @retutns {bool}\n */\nexport function isElement(element) {\n return typeof HTMLElement === 'object'\n ? element instanceof HTMLElement\n : Boolean(\n element &&\n typeof element === 'object' &&\n element !== null &&\n element.nodeType === 1 &&\n typeof element.nodeName === 'string'\n );\n}\n\n/**\n * Check if argument is object\n * @param obj\n * @returns {boolean}\n */\nexport function isObject(object) {\n return typeof object === 'object' && object !== null && !Array.isArray(object);\n}\n\n/**\n * Get style declaration if available\n *\n * @returns {CSSStyleDeclaration}\n */\nexport function getStyle(element, options) {\n const options_ = {...options};\n const {style} = options_;\n if (!options) {\n options = {};\n }\n\n if (isCSSStyleDeclaration(style)) {\n return style;\n }\n\n if (canGetComputedStyle(element)) {\n return window.getComputedStyle(element, prop(options, 'pseudoElt', null));\n }\n\n return {\n getPropertyValue: (key) => prop(options, key),\n };\n}\n\n/**\n * Normalize whitespace\n * https://developer.mozilla.org/de/docs/Web/CSS/white-space\n *\n * @param {string} text\n * @param {string} ws whitespace value\n * @returns {string}\n */\nexport function normalizeWhitespace(text, ws) {\n switch (ws) {\n case 'pre': {\n return text;\n }\n\n case 'pre-wrap': {\n return text;\n }\n\n case 'pre-line': {\n return (text || '').replace(/\\s+/gm, ' ').trim();\n }\n\n default: {\n return (text || '')\n .replace(/[\\r\\n]/gm, ' ')\n .replace(/\\s+/gm, ' ')\n .trim();\n }\n }\n}\n\n/**\n * Get styled text\n *\n * @param {string} text\n * @param {CSSStyleDeclaration} style\n * @returns {string}\n */\nexport function getStyledText(text, style) {\n switch (style.getPropertyValue('text-transform')) {\n case 'uppercase': {\n return text.toUpperCase();\n }\n\n case 'lowercase': {\n return text.toLowerCase();\n }\n\n default: {\n return text;\n }\n }\n}\n\n/**\n * Trim text and repace some breaking htmlentities for convenience\n * Point user to https://mths.be/he for real htmlentity decode\n * @param text\n * @returns {string}\n */\nexport function prepareText(text) {\n // Convert to unicode\n text = (text || '')\n .replace(/<wbr>/gi, '\\u200B')\n .replace(/<br\\s*\\/?>/gi, '\\u000A')\n .replace(/­/gi, '\\u00AD')\n .replace(/—/gi, '\\u2014');\n\n if (/&#(\\d+)(;?)|&#[xX]([a-fA-F\\d]+)(;?)|&([\\da-zA-Z]+);/g.test(text) && console) {\n console.error(\n 'text-metrics: Found encoded htmlenties. You may want to use https://mths.be/he to decode your text first.'\n );\n }\n\n return text;\n}\n\n/**\n * Get textcontent from element\n * Try innerText first\n * @param el\n */\nexport function getText(element) {\n if (!element) {\n return '';\n }\n\n const text = element.textContent || element.textContent || '';\n\n return text;\n}\n\n/**\n * Get property from src\n *\n * @param src\n * @param attr\n * @param defaultValue\n * @returns {*}\n */\nexport function prop(src, attr, defaultValue) {\n return (src && src[attr] !== undefined && src[attr]) || defaultValue;\n}\n\n/**\n * Normalize options\n *\n * @param options\n * @returns {*}\n */\nexport function normalizeOptions(options) {\n const options_ = {};\n\n // Normalize keys (fontSize => font-size)\n for (const key of Object.keys(options || {})) {\n const dashedKey = key.replace(/([A-Z])/g, ($1) => '-' + $1.toLowerCase());\n options_[dashedKey] = options[key];\n }\n\n return options_;\n}\n\n/**\n * Get Canvas\n * @param font\n * @throws {Error}\n * @return {Context2d}\n */\nexport function getContext2d(font) {\n try {\n const ctx = document.createElement('canvas').getContext('2d');\n const dpr = window.devicePixelRatio || 1;\n const bsr =\n ctx.webkitBackingStorePixelRatio ||\n ctx.mozBackingStorePixelRatio ||\n ctx.msBackingStorePixelRatio ||\n ctx.oBackingStorePixelRatio ||\n ctx.backingStorePixelRatio ||\n 1;\n ctx.font = font;\n ctx.setTransform(dpr / bsr, 0, 0, dpr / bsr, 0, 0);\n return ctx;\n } catch (error) {\n throw new Error('Canvas support required' + error.message);\n }\n}\n\n/**\n * Check breaking character\n * http://www.unicode.org/reports/tr14/#Table1\n *\n * @param chr\n */\nfunction checkBreak(chr) {\n return (\n (B2.has(chr) && 'B2') ||\n (BAI.has(chr) && 'BAI') ||\n (SHY.has(chr) && 'SHY') ||\n (BA.has(chr) && 'BA') ||\n (BB.has(chr) && 'BB') ||\n (BK.has(chr) && 'BK')\n );\n}\n\nexport function computeLinesDefault({ctx, text, max, wordSpacing, letterSpacing}) {\n const addSpacing = addWordAndLetterSpacing(wordSpacing, letterSpacing);\n const lines = [];\n const parts = [];\n const breakpoints = [];\n let line = '';\n let part = '';\n\n if (!text) {\n return [];\n }\n\n // Compute array of breakpoints\n for (const chr of text) {\n const type = checkBreak(chr);\n if (part === '' && type === 'BAI') {\n continue;\n }\n\n if (type) {\n breakpoints.push({chr, type});\n\n parts.push(part);\n part = '';\n } else {\n part += chr;\n }\n }\n\n if (part) {\n parts.push(part);\n }\n\n // Loop over text parts and compute the lines\n for (const [i, part] of parts.entries()) {\n if (i === 0) {\n line = part;\n continue;\n }\n\n const breakpoint = breakpoints[i - 1];\n // Special treatment as we only render the soft hyphen if we need to split\n const chr = breakpoint.type === 'SHY' ? '' : breakpoint.chr;\n if (breakpoint.type === 'BK') {\n lines.push(line);\n line = part;\n continue;\n }\n\n // Measure width\n const rawWidth = ctx.measureText(line + chr + part).width + addSpacing(line + chr + part);\n const width = Math.round(rawWidth);\n\n // Still fits in line\n if (width <= max) {\n line += chr + part;\n continue;\n }\n\n // Line is to long, we split at the breakpoint\n switch (breakpoint.type) {\n case 'SHY': {\n lines.push(line + '-');\n line = part;\n break;\n }\n\n case 'BA': {\n lines.push(line + chr);\n line = part;\n break;\n }\n\n case 'BAI': {\n lines.push(line);\n line = part;\n break;\n }\n\n case 'BB': {\n lines.push(line);\n line = chr + part;\n break;\n }\n\n case 'B2': {\n if (Number.parseInt(ctx.measureText(line + chr).width + addSpacing(line + chr), 10) <= max) {\n lines.push(line + chr);\n line = part;\n } else if (Number.parseInt(ctx.measureText(chr + part).width + addSpacing(chr + part), 10) <= max) {\n lines.push(line);\n line = chr + part;\n } else {\n lines.push(line, chr);\n line = part;\n }\n\n break;\n }\n\n default: {\n throw new Error('Undefoined break');\n }\n }\n }\n\n if ([...line].length > 0) {\n lines.push(line);\n }\n\n return lines;\n}\n\nexport function computeLinesBreakAll({ctx, text, max, wordSpacing, letterSpacing}) {\n const addSpacing = addWordAndLetterSpacing(wordSpacing, letterSpacing);\n const lines = [];\n let line = '';\n let index = 0;\n\n if (!text) {\n return [];\n }\n\n for (const chr of text) {\n const type = checkBreak(chr);\n // Mandatory break found (br's converted to \\u000A and innerText keeps br's as \\u000A\n if (type === 'BK') {\n lines.push(line);\n line = '';\n continue;\n }\n\n const lineLength = line.length;\n if (BAI.has(chr) && (lineLength === 0 || BAI.has(line[lineLength - 1]))) {\n continue;\n }\n\n // Measure width\n let rawWidth = ctx.measureText(line + chr).width + addSpacing(line + chr);\n let width = Math.ceil(rawWidth);\n\n // Check if we can put char behind the shy\n if (type === 'SHY') {\n const next = text[index + 1] || '';\n rawWidth = ctx.measureText(line + chr + next).width + addSpacing(line + chr + next);\n width = Math.ceil(rawWidth);\n }\n\n // Needs at least one character\n if (width > max && [...line].length > 0) {\n switch (type) {\n case 'SHY': {\n lines.push(line + '-');\n line = '';\n break;\n }\n\n case 'BA': {\n lines.push(line + chr);\n line = '';\n break;\n }\n\n case 'BAI': {\n lines.push(line);\n line = '';\n break;\n }\n\n default: {\n lines.push(line);\n line = chr;\n break;\n }\n }\n } else if (chr !== '\\u00AD') {\n line += chr;\n }\n\n index++;\n }\n\n if ([...line].length > 0) {\n lines.push(line);\n }\n\n return lines;\n}\n","/* eslint-env es6, browser */\nimport * as _ from './utils.js';\n\nclass TextMetrics {\n constructor(element, overwrites = {}) {\n if (!_.isElement(element) && _.isObject(element)) {\n this.el = undefined;\n this.overwrites = _.normalizeOptions(element);\n } else {\n this.el = element;\n this.overwrites = _.normalizeOptions(overwrites);\n }\n\n this.style = _.getStyle(this.el, this.overwrites);\n this.font = _.prop(overwrites, 'font', null) || _.getFont(this.style, this.overwrites);\n }\n\n padding() {\n return this.el\n ? Number.parseInt(this.style.paddingLeft || 0, 10) + Number.parseInt(this.style.paddingRight || 0, 10)\n : 0;\n }\n\n parseArgs(text, options = {}, overwrites = {}) {\n if (typeof text === 'object' && text) {\n overwrites = options;\n options = text || {};\n text = undefined;\n }\n\n const styles = {...this.overwrites, ..._.normalizeOptions(overwrites)};\n const ws = _.prop(styles, 'white-space') || this.style.getPropertyValue('white-space');\n\n if (!options) {\n options = {};\n }\n\n if (!overwrites) {\n options = {};\n }\n\n text =\n !text && this.el ? _.normalizeWhitespace(_.getText(this.el), ws) : _.prepareText(_.normalizeWhitespace(text, ws));\n\n return {text, options, overwrites, styles};\n }\n\n /**\n * Compute Text Metrics based for given text\n *\n * @param {string} text\n * @param {object} options\n * @param {object} overwrites\n * @returns {function}\n */\n width(...args) {\n const {text, options, overwrites, styles} = this.parseArgs(...args);\n\n if (!text) {\n return 0;\n }\n\n const font = _.getFont(this.style, styles);\n const letterSpacing = _.prop(styles, 'letter-spacing') || this.style.getPropertyValue('letter-spacing');\n const wordSpacing = _.prop(styles, 'word-spacing') || this.style.getPropertyValue('word-spacing');\n const addSpacing = _.addWordAndLetterSpacing(wordSpacing, letterSpacing);\n const ctx = _.getContext2d(font);\n const styledText = _.getStyledText(text, this.style);\n\n if (options.multiline) {\n // eslint-disable-next-line unicorn/no-array-reduce\n return this.lines(styledText, options, overwrites).reduce((result, text) => {\n const w = ctx.measureText(text).width + addSpacing(text);\n\n return Math.max(result, w);\n }, 0);\n }\n\n return ctx.measureText(styledText).width + addSpacing(styledText);\n }\n\n /**\n * Compute height from textbox\n *\n * @param {string} text\n * @param {object} options\n * @param {object} overwrites\n * @returns {number}\n */\n height(...args) {\n const {text, options, styles} = this.parseArgs(...args);\n\n const lineHeight = Number.parseFloat(_.prop(styles, 'line-height') || this.style.getPropertyValue('line-height'));\n\n return Math.ceil(this.lines(text, options, styles).length * lineHeight || 0);\n }\n\n /**\n * Compute lines of text with automatic word wraparound\n * element styles\n *\n * @param {string} text\n * @param {object} options\n * @param {object} overwrites\n * @returns {*}\n */\n lines(...args) {\n const {text, options, overwrites, styles} = this.parseArgs(...args);\n\n const font = _.getFont(this.style, styles);\n\n // Get max width\n let max =\n Number.parseInt(_.prop(options, 'width') || _.prop(overwrites, 'width'), 10) ||\n _.prop(this.el, 'offsetWidth', 0) ||\n Number.parseInt(_.prop(styles, 'width', 0), 10) ||\n Number.parseInt(this.style.width, 10);\n\n max -= this.padding();\n\n const wordBreak = _.prop(styles, 'word-break') || this.style.getPropertyValue('word-break');\n const letterSpacing = _.prop(styles, 'letter-spacing') || this.style.getPropertyValue('letter-spacing');\n const wordSpacing = _.prop(styles, 'word-spacing') || this.style.getPropertyValue('word-spacing');\n const ctx = _.getContext2d(font);\n const styledText = _.getStyledText(text, this.style);\n\n // Different scenario when break-word is allowed\n if (wordBreak === 'break-all') {\n return _.computeLinesBreakAll({\n ctx,\n text: styledText,\n max,\n wordSpacing,\n letterSpacing,\n });\n }\n\n return _.computeLinesDefault({\n ctx,\n text: styledText,\n max,\n wordSpacing,\n letterSpacing,\n });\n }\n\n /**\n * Compute Text Metrics based for given text\n *\n * @param {string} text\n * @param {object} options\n * @param {object} overwrites\n * @returns {string} Pixelvalue e.g. 14px\n */\n maxFontSize(...args) {\n const {text, options, overwrites, styles} = this.parseArgs(...args);\n\n // Simple compute function which adds the size and computes the with\n const compute = (size) => {\n return Math.ceil(\n this.width(text, options, {\n ...styles,\n 'font-size': size + 'px',\n })\n );\n };\n\n // Get max width\n let max =\n Number.parseInt(_.prop(options, 'width') || _.prop(overwrites, 'width'), 10) ||\n _.prop(this.el, 'offsetWidth', 0) ||\n Number.parseInt(_.prop(styles, 'width', 0), 10) ||\n Number.parseInt(this.style.width, 10);\n\n max -= this.padding();\n\n // Start with half the max size\n let size = Math.floor(max / 2);\n let cur = compute(size);\n\n // Compute next result based on first result\n size = Math.floor((size / cur) * max);\n cur = compute(size);\n\n // Happy cause we got it already\n if (Math.ceil(cur) === max) {\n return size ? size + 'px' : undefined;\n }\n\n // Go on by increase/decrease pixels\n const greater = cur > max && size > 0;\n while (cur > max && size > 0) {\n size -= 1;\n cur = compute(size);\n }\n\n if (!greater) {\n while (cur < max) {\n cur = compute(size + 1);\n if (cur > max) {\n return size ? size + 'px' : undefined;\n }\n\n size += 1;\n }\n }\n\n return size ? size + 'px' : undefined;\n }\n}\n\nexport const init = (element, overwrites) => new TextMetrics(element, overwrites);\n\nexport const utils = {..._};\n"],"names":["B2","Set","SHY","BAI","BA","BB","BK","pxValue","value_","options","baseFontSize","Number","parseInt","prop","value","parseFloat","unit","replace","Error","addWordAndLetterSpacing","ws","ls","denyList","wordAddon","has","letterAddon","text","trim","split","length","getFont","style","font","fontWeight","getPropertyValue","includes","toString","push","fontStyle","fontVariant","fontSizeValue","fontFamily","join","isCSSStyleDeclaration","canGetComputedStyle","element","isElement","window","getComputedStyle","HTMLElement","Boolean","nodeType","nodeName","isObject","object","Array","isArray","getStyle","options_","_objectSpread","key","normalizeWhitespace","getStyledText","toUpperCase","toLowerCase","prepareText","test","console","error","getText","textContent","src","attr","defaultValue","undefined","normalizeOptions","Object","keys","$1","getContext2d","ctx","document","createElement","getContext","dpr","devicePixelRatio","bsr","webkitBackingStorePixelRatio","mozBackingStorePixelRatio","msBackingStorePixelRatio","oBackingStorePixelRatio","backingStorePixelRatio","setTransform","message","checkBreak","chr","computeLinesDefault","max","wordSpacing","letterSpacing","addSpacing","lines","parts","breakpoints","line","part","type","i","entries","breakpoint","rawWidth","measureText","width","Math","round","computeLinesBreakAll","index","lineLength","ceil","next","TextMetrics","constructor","overwrites","_","this","el","padding","paddingLeft","paddingRight","parseArgs","styles","slice","call","arguments","styledText","multiline","reduce","result","w","height","lineHeight","wordBreak","maxFontSize","compute","size","floor","cur","greater","init","utils"],"mappings":"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"}