Files
Foundry-VTT-Docker/resources/app/node_modules/@pixi/text/lib/TextMetrics.mjs.map
2025-01-04 00:34:03 +01:00

1 line
38 KiB
Plaintext

{"version":3,"file":"TextMetrics.mjs","sources":["../src/TextMetrics.ts"],"sourcesContent":["import { settings } from '@pixi/core';\n\nimport type { ICanvas, ICanvasRenderingContext2D, ICanvasRenderingContext2DSettings } from '@pixi/core';\nimport type { TextStyle, TextStyleWhiteSpace } from './TextStyle';\n\n// The type for Intl.Segmenter is only available since TypeScript 4.7.2, so let's make a polyfill for it.\ninterface ISegmentData\n{\n segment: string;\n}\ninterface ISegments\n{\n [Symbol.iterator](): IterableIterator<ISegmentData>;\n}\ninterface ISegmenter\n{\n segment(input: string): ISegments;\n}\ninterface IIntl\n{\n Segmenter?: {\n prototype: ISegmenter;\n new(): ISegmenter;\n };\n}\n\n/**\n * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.\n * @typedef {object} FontMetrics\n * @property {number} ascent - The ascent distance\n * @property {number} descent - The descent distance\n * @property {number} fontSize - Font size from ascent to descent\n * @memberof PIXI.TextMetrics\n * @private\n */\n\n/**\n * A number, or a string containing a number.\n * @memberof PIXI\n * @typedef {object} IFontMetrics\n * @property {number} ascent - Font ascent\n * @property {number} descent - Font descent\n * @property {number} fontSize - Font size\n */\ninterface IFontMetrics\n{\n ascent: number;\n descent: number;\n fontSize: number;\n}\n\ntype CharacterWidthCache = Record<string, number>;\n\n// Default settings used for all getContext calls\nconst contextSettings: ICanvasRenderingContext2DSettings = {\n // TextMetrics requires getImageData readback for measuring fonts.\n willReadFrequently: true,\n};\n\n/**\n * The TextMetrics object represents the measurement of a block of text with a specified style.\n * @example\n * import { TextMetrics, TextStyle } from 'pixi.js';\n *\n * const style = new TextStyle({\n * fontFamily: 'Arial',\n * fontSize: 24,\n * fill: 0xff1010,\n * align: 'center',\n * });\n * const textMetrics = TextMetrics.measureText('Your text', style);\n * @memberof PIXI\n */\nexport class TextMetrics\n{\n /** The text that was measured. */\n public text: string;\n\n /** The style that was measured. */\n public style: TextStyle;\n\n /** The measured width of the text. */\n public width: number;\n\n /** The measured height of the text. */\n public height: number;\n\n /** An array of lines of the text broken by new lines and wrapping is specified in style. */\n public lines: string[];\n\n /** An array of the line widths for each line matched to `lines`. */\n public lineWidths: number[];\n\n /** The measured line height for this style. */\n public lineHeight: number;\n\n /** The maximum line width for all measured lines. */\n public maxLineWidth: number;\n\n /** The font properties object from TextMetrics.measureFont. */\n public fontProperties: IFontMetrics;\n\n /**\n * String used for calculate font metrics.\n * These characters are all tall to help calculate the height required for text.\n */\n public static METRICS_STRING = '|ÉqÅ';\n\n /** Baseline symbol for calculate font metrics. */\n public static BASELINE_SYMBOL = 'M';\n\n /** Baseline multiplier for calculate font metrics. */\n public static BASELINE_MULTIPLIER = 1.4;\n\n /** Height multiplier for setting height of canvas to calculate font metrics. */\n public static HEIGHT_MULTIPLIER = 2.0;\n\n /**\n * A Unicode \"character\", or \"grapheme cluster\", can be composed of multiple Unicode code points,\n * such as letters with diacritical marks (e.g. `'\\u0065\\u0301'`, letter e with acute)\n * or emojis with modifiers (e.g. `'\\uD83E\\uDDD1\\u200D\\uD83D\\uDCBB'`, technologist).\n * The new `Intl.Segmenter` API in ES2022 can split the string into grapheme clusters correctly. If it is not available,\n * PixiJS will fallback to use the iterator of String, which can only spilt the string into code points.\n * If you want to get full functionality in environments that don't support `Intl.Segmenter` (such as Firefox),\n * you can use other libraries such as [grapheme-splitter]{@link https://www.npmjs.com/package/grapheme-splitter}\n * or [graphemer]{@link https://www.npmjs.com/package/graphemer} to create a polyfill. Since these libraries can be\n * relatively large in size to handle various Unicode grapheme clusters properly, PixiJS won't use them directly.\n */\n public static graphemeSegmenter: (s: string) => string[] = (() =>\n {\n if (typeof (Intl as IIntl)?.Segmenter === 'function')\n {\n const segmenter = new (Intl as IIntl).Segmenter();\n\n return (s: string) => [...segmenter.segment(s)].map((x) => x.segment);\n }\n\n return (s: string) => [...s];\n })();\n\n public static _experimentalLetterSpacingSupported?: boolean;\n\n /**\n * Checking that we can use modern canvas 2D API.\n *\n * Note: This is an unstable API, Chrome < 94 use `textLetterSpacing`, later versions use `letterSpacing`.\n * @see PIXI.TextMetrics.experimentalLetterSpacing\n * @see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/letterSpacing\n * @see https://developer.chrome.com/origintrials/#/view_trial/3585991203293757441\n */\n public static get experimentalLetterSpacingSupported(): boolean\n {\n let result = TextMetrics._experimentalLetterSpacingSupported;\n\n if (result !== undefined)\n {\n const proto = settings.ADAPTER.getCanvasRenderingContext2D().prototype;\n\n result\n = TextMetrics._experimentalLetterSpacingSupported\n = 'letterSpacing' in proto || 'textLetterSpacing' in proto;\n }\n\n return result;\n }\n\n /**\n * New rendering behavior for letter-spacing which uses Chrome's new native API. This will\n * lead to more accurate letter-spacing results because it does not try to manually draw\n * each character. However, this Chrome API is experimental and may not serve all cases yet.\n * @see PIXI.TextMetrics.experimentalLetterSpacingSupported\n */\n public static experimentalLetterSpacing = false;\n\n /** Cache of {@see PIXI.TextMetrics.FontMetrics} objects. */\n private static _fonts: Record<string, IFontMetrics> = {};\n\n /** Cache of new line chars. */\n private static _newlines: number[] = [\n 0x000A, // line feed\n 0x000D, // carriage return\n ];\n\n /** Cache of breaking spaces. */\n private static _breakingSpaces: number[] = [\n 0x0009, // character tabulation\n 0x0020, // space\n 0x2000, // en quad\n 0x2001, // em quad\n 0x2002, // en space\n 0x2003, // em space\n 0x2004, // three-per-em space\n 0x2005, // four-per-em space\n 0x2006, // six-per-em space\n 0x2008, // punctuation space\n 0x2009, // thin space\n 0x200A, // hair space\n 0x205F, // medium mathematical space\n 0x3000, // ideographic space\n ];\n\n private static __canvas: ICanvas;\n private static __context: ICanvasRenderingContext2D;\n\n /**\n * @param text - the text that was measured\n * @param style - the style that was measured\n * @param width - the measured width of the text\n * @param height - the measured height of the text\n * @param lines - an array of the lines of text broken by new lines and wrapping if specified in style\n * @param lineWidths - an array of the line widths for each line matched to `lines`\n * @param lineHeight - the measured line height for this style\n * @param maxLineWidth - the maximum line width for all measured lines\n * @param {PIXI.IFontMetrics} fontProperties - the font properties object from TextMetrics.measureFont\n */\n constructor(text: string, style: TextStyle, width: number, height: number, lines: string[], lineWidths: number[],\n lineHeight: number, maxLineWidth: number, fontProperties: IFontMetrics)\n {\n this.text = text;\n this.style = style;\n this.width = width;\n this.height = height;\n this.lines = lines;\n this.lineWidths = lineWidths;\n this.lineHeight = lineHeight;\n this.maxLineWidth = maxLineWidth;\n this.fontProperties = fontProperties;\n }\n\n /**\n * Measures the supplied string of text and returns a Rectangle.\n * @param text - The text to measure.\n * @param style - The text style to use for measuring\n * @param wordWrap - Override for if word-wrap should be applied to the text.\n * @param canvas - optional specification of the canvas to use for measuring.\n * @returns Measured width and height of the text.\n */\n public static measureText(\n text: string,\n style: TextStyle,\n wordWrap?: boolean,\n canvas: ICanvas = TextMetrics._canvas\n ): TextMetrics\n {\n wordWrap = (wordWrap === undefined || wordWrap === null) ? style.wordWrap : wordWrap;\n const font = style.toFontString();\n const fontProperties = TextMetrics.measureFont(font);\n\n // fallback in case UA disallow canvas data extraction\n // (toDataURI, getImageData functions)\n if (fontProperties.fontSize === 0)\n {\n fontProperties.fontSize = style.fontSize as number;\n fontProperties.ascent = style.fontSize as number;\n }\n\n const context = canvas.getContext('2d', contextSettings);\n\n context.font = font;\n\n const outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;\n const lines = outputText.split(/(?:\\r\\n|\\r|\\n)/);\n const lineWidths = new Array<number>(lines.length);\n let maxLineWidth = 0;\n\n for (let i = 0; i < lines.length; i++)\n {\n const lineWidth = TextMetrics._measureText(lines[i], style.letterSpacing, context);\n\n lineWidths[i] = lineWidth;\n maxLineWidth = Math.max(maxLineWidth, lineWidth);\n }\n let width = maxLineWidth + style.strokeThickness;\n\n if (style.dropShadow)\n {\n width += style.dropShadowDistance;\n }\n\n const lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;\n let height\n = Math.max(lineHeight, fontProperties.fontSize + (style.strokeThickness * 2)) + style.leading\n + ((lines.length - 1) * (lineHeight + style.leading));\n\n if (style.dropShadow)\n {\n height += style.dropShadowDistance;\n }\n\n return new TextMetrics(\n text,\n style,\n width,\n height,\n lines,\n lineWidths,\n lineHeight + style.leading,\n maxLineWidth,\n fontProperties\n );\n }\n\n private static _measureText(\n text: string,\n letterSpacing: number,\n context: ICanvasRenderingContext2D\n )\n {\n let useExperimentalLetterSpacing = false;\n\n if (TextMetrics.experimentalLetterSpacingSupported)\n {\n if (TextMetrics.experimentalLetterSpacing)\n {\n context.letterSpacing = `${letterSpacing}px`;\n context.textLetterSpacing = `${letterSpacing}px`;\n useExperimentalLetterSpacing = true;\n }\n else\n {\n context.letterSpacing = '0px';\n context.textLetterSpacing = '0px';\n }\n }\n\n let width = context.measureText(text).width;\n\n if (width > 0)\n {\n if (useExperimentalLetterSpacing)\n {\n width -= letterSpacing;\n }\n else\n {\n width += (TextMetrics.graphemeSegmenter(text).length - 1) * letterSpacing;\n }\n }\n\n return width;\n }\n\n /**\n * Applies newlines to a string to have it optimally fit into the horizontal\n * bounds set by the Text object's wordWrapWidth property.\n * @param text - String to apply word wrapping to\n * @param style - the style to use when wrapping\n * @param canvas - optional specification of the canvas to use for measuring.\n * @returns New string with new lines applied where required\n */\n private static wordWrap(\n text: string,\n style: TextStyle,\n canvas: ICanvas = TextMetrics._canvas\n ): string\n {\n const context = canvas.getContext('2d', contextSettings);\n\n let width = 0;\n let line = '';\n let lines = '';\n\n const cache: CharacterWidthCache = Object.create(null);\n const { letterSpacing, whiteSpace } = style;\n\n // How to handle whitespaces\n const collapseSpaces = TextMetrics.collapseSpaces(whiteSpace);\n const collapseNewlines = TextMetrics.collapseNewlines(whiteSpace);\n\n // whether or not spaces may be added to the beginning of lines\n let canPrependSpaces = !collapseSpaces;\n\n // There is letterSpacing after every char except the last one\n // t_h_i_s_' '_i_s_' '_a_n_' '_e_x_a_m_p_l_e_' '_!\n // so for convenience the above needs to be compared to width + 1 extra letterSpace\n // t_h_i_s_' '_i_s_' '_a_n_' '_e_x_a_m_p_l_e_' '_!_\n // ________________________________________________\n // And then the final space is simply no appended to each line\n const wordWrapWidth = style.wordWrapWidth + letterSpacing;\n\n // break text into words, spaces and newline chars\n const tokens = TextMetrics.tokenize(text);\n\n for (let i = 0; i < tokens.length; i++)\n {\n // get the word, space or newlineChar\n let token = tokens[i];\n\n // if word is a new line\n if (TextMetrics.isNewline(token))\n {\n // keep the new line\n if (!collapseNewlines)\n {\n lines += TextMetrics.addLine(line);\n canPrependSpaces = !collapseSpaces;\n line = '';\n width = 0;\n continue;\n }\n\n // if we should collapse new lines\n // we simply convert it into a space\n token = ' ';\n }\n\n // if we should collapse repeated whitespaces\n if (collapseSpaces)\n {\n // check both this and the last tokens for spaces\n const currIsBreakingSpace = TextMetrics.isBreakingSpace(token);\n const lastIsBreakingSpace = TextMetrics.isBreakingSpace(line[line.length - 1]);\n\n if (currIsBreakingSpace && lastIsBreakingSpace)\n {\n continue;\n }\n }\n\n // get word width from cache if possible\n const tokenWidth = TextMetrics.getFromCache(token, letterSpacing, cache, context);\n\n // word is longer than desired bounds\n if (tokenWidth > wordWrapWidth)\n {\n // if we are not already at the beginning of a line\n if (line !== '')\n {\n // start newlines for overflow words\n lines += TextMetrics.addLine(line);\n line = '';\n width = 0;\n }\n\n // break large word over multiple lines\n if (TextMetrics.canBreakWords(token, style.breakWords))\n {\n // break word into characters\n const characters = TextMetrics.wordWrapSplit(token);\n\n // loop the characters\n for (let j = 0; j < characters.length; j++)\n {\n let char = characters[j];\n let lastChar = char;\n\n let k = 1;\n\n // we are not at the end of the token\n while (characters[j + k])\n {\n const nextChar = characters[j + k];\n\n // should not split chars\n if (!TextMetrics.canBreakChars(lastChar, nextChar, token, j, style.breakWords))\n {\n // combine chars & move forward one\n char += nextChar;\n }\n else\n {\n break;\n }\n\n lastChar = nextChar;\n k++;\n }\n\n j += k - 1;\n\n const characterWidth = TextMetrics.getFromCache(char, letterSpacing, cache, context);\n\n if (characterWidth + width > wordWrapWidth)\n {\n lines += TextMetrics.addLine(line);\n canPrependSpaces = false;\n line = '';\n width = 0;\n }\n\n line += char;\n width += characterWidth;\n }\n }\n\n // run word out of the bounds\n else\n {\n // if there are words in this line already\n // finish that line and start a new one\n if (line.length > 0)\n {\n lines += TextMetrics.addLine(line);\n line = '';\n width = 0;\n }\n\n const isLastToken = i === tokens.length - 1;\n\n // give it its own line if it's not the end\n lines += TextMetrics.addLine(token, !isLastToken);\n canPrependSpaces = false;\n line = '';\n width = 0;\n }\n }\n\n // word could fit\n else\n {\n // word won't fit because of existing words\n // start a new line\n if (tokenWidth + width > wordWrapWidth)\n {\n // if its a space we don't want it\n canPrependSpaces = false;\n\n // add a new line\n lines += TextMetrics.addLine(line);\n\n // start a new line\n line = '';\n width = 0;\n }\n\n // don't add spaces to the beginning of lines\n if (line.length > 0 || !TextMetrics.isBreakingSpace(token) || canPrependSpaces)\n {\n // add the word to the current line\n line += token;\n\n // update width counter\n width += tokenWidth;\n }\n }\n }\n\n lines += TextMetrics.addLine(line, false);\n\n return lines;\n }\n\n /**\n * Convienience function for logging each line added during the wordWrap method.\n * @param line - The line of text to add\n * @param newLine - Add new line character to end\n * @returns A formatted line\n */\n private static addLine(line: string, newLine = true): string\n {\n line = TextMetrics.trimRight(line);\n\n line = (newLine) ? `${line}\\n` : line;\n\n return line;\n }\n\n /**\n * Gets & sets the widths of calculated characters in a cache object\n * @param key - The key\n * @param letterSpacing - The letter spacing\n * @param cache - The cache\n * @param context - The canvas context\n * @returns The from cache.\n */\n private static getFromCache(key: string, letterSpacing: number, cache: CharacterWidthCache,\n context: ICanvasRenderingContext2D): number\n {\n let width = cache[key];\n\n if (typeof width !== 'number')\n {\n width = TextMetrics._measureText(key, letterSpacing, context) + letterSpacing;\n cache[key] = width;\n }\n\n return width;\n }\n\n /**\n * Determines whether we should collapse breaking spaces.\n * @param whiteSpace - The TextStyle property whiteSpace\n * @returns Should collapse\n */\n private static collapseSpaces(whiteSpace: TextStyleWhiteSpace): boolean\n {\n return (whiteSpace === 'normal' || whiteSpace === 'pre-line');\n }\n\n /**\n * Determines whether we should collapse newLine chars.\n * @param whiteSpace - The white space\n * @returns should collapse\n */\n private static collapseNewlines(whiteSpace: TextStyleWhiteSpace): boolean\n {\n return (whiteSpace === 'normal');\n }\n\n /**\n * Trims breaking whitespaces from string.\n * @param text - The text\n * @returns Trimmed string\n */\n private static trimRight(text: string): string\n {\n if (typeof text !== 'string')\n {\n return '';\n }\n\n for (let i = text.length - 1; i >= 0; i--)\n {\n const char = text[i];\n\n if (!TextMetrics.isBreakingSpace(char))\n {\n break;\n }\n\n text = text.slice(0, -1);\n }\n\n return text;\n }\n\n /**\n * Determines if char is a newline.\n * @param char - The character\n * @returns True if newline, False otherwise.\n */\n private static isNewline(char: string): boolean\n {\n if (typeof char !== 'string')\n {\n return false;\n }\n\n return TextMetrics._newlines.includes(char.charCodeAt(0));\n }\n\n /**\n * Determines if char is a breaking whitespace.\n *\n * It allows one to determine whether char should be a breaking whitespace\n * For example certain characters in CJK langs or numbers.\n * It must return a boolean.\n * @param char - The character\n * @param [_nextChar] - The next character\n * @returns True if whitespace, False otherwise.\n */\n static isBreakingSpace(char: string, _nextChar?: string): boolean\n {\n if (typeof char !== 'string')\n {\n return false;\n }\n\n return TextMetrics._breakingSpaces.includes(char.charCodeAt(0));\n }\n\n /**\n * Splits a string into words, breaking-spaces and newLine characters\n * @param text - The text\n * @returns A tokenized array\n */\n private static tokenize(text: string): string[]\n {\n const tokens: string[] = [];\n let token = '';\n\n if (typeof text !== 'string')\n {\n return tokens;\n }\n\n for (let i = 0; i < text.length; i++)\n {\n const char = text[i];\n const nextChar = text[i + 1];\n\n if (TextMetrics.isBreakingSpace(char, nextChar) || TextMetrics.isNewline(char))\n {\n if (token !== '')\n {\n tokens.push(token);\n token = '';\n }\n\n tokens.push(char);\n\n continue;\n }\n\n token += char;\n }\n\n if (token !== '')\n {\n tokens.push(token);\n }\n\n return tokens;\n }\n\n /**\n * Overridable helper method used internally by TextMetrics, exposed to allow customizing the class's behavior.\n *\n * It allows one to customise which words should break\n * Examples are if the token is CJK or numbers.\n * It must return a boolean.\n * @param _token - The token\n * @param breakWords - The style attr break words\n * @returns Whether to break word or not\n */\n static canBreakWords(_token: string, breakWords: boolean): boolean\n {\n return breakWords;\n }\n\n /**\n * Overridable helper method used internally by TextMetrics, exposed to allow customizing the class's behavior.\n *\n * It allows one to determine whether a pair of characters\n * should be broken by newlines\n * For example certain characters in CJK langs or numbers.\n * It must return a boolean.\n * @param _char - The character\n * @param _nextChar - The next character\n * @param _token - The token/word the characters are from\n * @param _index - The index in the token of the char\n * @param _breakWords - The style attr break words\n * @returns whether to break word or not\n */\n static canBreakChars(_char: string, _nextChar: string, _token: string, _index: number,\n _breakWords: boolean): boolean\n {\n return true;\n }\n\n /**\n * Overridable helper method used internally by TextMetrics, exposed to allow customizing the class's behavior.\n *\n * It is called when a token (usually a word) has to be split into separate pieces\n * in order to determine the point to break a word.\n * It must return an array of characters.\n * @param token - The token to split\n * @returns The characters of the token\n * @see TextMetrics.graphemeSegmenter\n */\n static wordWrapSplit(token: string): string[]\n {\n return TextMetrics.graphemeSegmenter(token);\n }\n\n /**\n * Calculates the ascent, descent and fontSize of a given font-style\n * @param font - String representing the style of the font\n * @returns Font properties object\n */\n public static measureFont(font: string): IFontMetrics\n {\n // as this method is used for preparing assets, don't recalculate things if we don't need to\n if (TextMetrics._fonts[font])\n {\n return TextMetrics._fonts[font];\n }\n\n const properties: IFontMetrics = {\n ascent: 0,\n descent: 0,\n fontSize: 0,\n };\n\n const canvas = TextMetrics._canvas;\n const context = TextMetrics._context;\n\n context.font = font;\n\n const metricsString = TextMetrics.METRICS_STRING + TextMetrics.BASELINE_SYMBOL;\n const width = Math.ceil(context.measureText(metricsString).width);\n let baseline = Math.ceil(context.measureText(TextMetrics.BASELINE_SYMBOL).width);\n const height = Math.ceil(TextMetrics.HEIGHT_MULTIPLIER * baseline);\n\n baseline = baseline * TextMetrics.BASELINE_MULTIPLIER | 0;\n\n if (width === 0 || height === 0)\n {\n TextMetrics._fonts[font] = properties;\n\n return properties;\n }\n\n canvas.width = width;\n canvas.height = height;\n\n context.fillStyle = '#f00';\n context.fillRect(0, 0, width, height);\n\n context.font = font;\n\n context.textBaseline = 'alphabetic';\n context.fillStyle = '#000';\n context.fillText(metricsString, 0, baseline);\n\n const imagedata = context.getImageData(0, 0, width, height).data;\n const pixels = imagedata.length;\n const line = width * 4;\n\n let i = 0;\n let idx = 0;\n let stop = false;\n\n // ascent. scan from top to bottom until we find a non red pixel\n for (i = 0; i < baseline; ++i)\n {\n for (let j = 0; j < line; j += 4)\n {\n if (imagedata[idx + j] !== 255)\n {\n stop = true;\n break;\n }\n }\n if (!stop)\n {\n idx += line;\n }\n else\n {\n break;\n }\n }\n\n properties.ascent = baseline - i;\n\n idx = pixels - line;\n stop = false;\n\n // descent. scan from bottom to top until we find a non red pixel\n for (i = height; i > baseline; --i)\n {\n for (let j = 0; j < line; j += 4)\n {\n if (imagedata[idx + j] !== 255)\n {\n stop = true;\n break;\n }\n }\n\n if (!stop)\n {\n idx -= line;\n }\n else\n {\n break;\n }\n }\n\n properties.descent = i - baseline;\n properties.fontSize = properties.ascent + properties.descent;\n\n TextMetrics._fonts[font] = properties;\n\n return properties;\n }\n\n /**\n * Clear font metrics in metrics cache.\n * @param {string} [font] - font name. If font name not set then clear cache for all fonts.\n */\n public static clearMetrics(font = ''): void\n {\n if (font)\n {\n delete TextMetrics._fonts[font];\n }\n else\n {\n TextMetrics._fonts = {};\n }\n }\n\n /**\n * Cached canvas element for measuring text\n * TODO: this should be private, but isn't because of backward compat, will fix later.\n * @ignore\n */\n public static get _canvas(): ICanvas\n {\n if (!TextMetrics.__canvas)\n {\n let canvas: ICanvas;\n\n try\n {\n // OffscreenCanvas2D measureText can be up to 40% faster.\n const c = new OffscreenCanvas(0, 0);\n const context = c.getContext('2d', contextSettings);\n\n if (context?.measureText)\n {\n TextMetrics.__canvas = c;\n\n return c;\n }\n\n canvas = settings.ADAPTER.createCanvas();\n }\n catch (ex)\n {\n canvas = settings.ADAPTER.createCanvas();\n }\n canvas.width = canvas.height = 10;\n TextMetrics.__canvas = canvas;\n }\n\n return TextMetrics.__canvas;\n }\n\n /**\n * TODO: this should be private, but isn't because of backward compat, will fix later.\n * @ignore\n */\n public static get _context(): ICanvasRenderingContext2D\n {\n if (!TextMetrics.__context)\n {\n TextMetrics.__context = TextMetrics._canvas.getContext('2d', contextSettings);\n }\n\n return TextMetrics.__context;\n }\n}\n"],"names":["_TextMetrics"],"mappings":"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"}