text.js 76 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919
  1. /*!
  2. * @pixi/text - v6.5.10
  3. * Compiled Thu, 06 Jul 2023 15:25:11 UTC
  4. *
  5. * @pixi/text is licensed under the MIT License.
  6. * http://www.opensource.org/licenses/mit-license
  7. */
  8. 'use strict';
  9. Object.defineProperty(exports, '__esModule', { value: true });
  10. var sprite = require('@pixi/sprite');
  11. var core = require('@pixi/core');
  12. var settings = require('@pixi/settings');
  13. var math = require('@pixi/math');
  14. var utils = require('@pixi/utils');
  15. /*! *****************************************************************************
  16. Copyright (c) Microsoft Corporation.
  17. Permission to use, copy, modify, and/or distribute this software for any
  18. purpose with or without fee is hereby granted.
  19. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  20. REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  21. AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  22. INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  23. LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  24. OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  25. PERFORMANCE OF THIS SOFTWARE.
  26. ***************************************************************************** */
  27. /* global Reflect, Promise */
  28. var extendStatics = function(d, b) {
  29. extendStatics = Object.setPrototypeOf ||
  30. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  31. function (d, b) { for (var p in b) { if (b.hasOwnProperty(p)) { d[p] = b[p]; } } };
  32. return extendStatics(d, b);
  33. };
  34. function __extends(d, b) {
  35. extendStatics(d, b);
  36. function __() { this.constructor = d; }
  37. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  38. }
  39. /**
  40. * Constants that define the type of gradient on text.
  41. * @static
  42. * @constant
  43. * @name TEXT_GRADIENT
  44. * @memberof PIXI
  45. * @type {object}
  46. * @property {number} LINEAR_VERTICAL Vertical gradient
  47. * @property {number} LINEAR_HORIZONTAL Linear gradient
  48. */
  49. exports.TEXT_GRADIENT = void 0;
  50. (function (TEXT_GRADIENT) {
  51. TEXT_GRADIENT[TEXT_GRADIENT["LINEAR_VERTICAL"] = 0] = "LINEAR_VERTICAL";
  52. TEXT_GRADIENT[TEXT_GRADIENT["LINEAR_HORIZONTAL"] = 1] = "LINEAR_HORIZONTAL";
  53. })(exports.TEXT_GRADIENT || (exports.TEXT_GRADIENT = {}));
  54. // disabling eslint for now, going to rewrite this in v5
  55. var defaultStyle = {
  56. align: 'left',
  57. breakWords: false,
  58. dropShadow: false,
  59. dropShadowAlpha: 1,
  60. dropShadowAngle: Math.PI / 6,
  61. dropShadowBlur: 0,
  62. dropShadowColor: 'black',
  63. dropShadowDistance: 5,
  64. fill: 'black',
  65. fillGradientType: exports.TEXT_GRADIENT.LINEAR_VERTICAL,
  66. fillGradientStops: [],
  67. fontFamily: 'Arial',
  68. fontSize: 26,
  69. fontStyle: 'normal',
  70. fontVariant: 'normal',
  71. fontWeight: 'normal',
  72. letterSpacing: 0,
  73. lineHeight: 0,
  74. lineJoin: 'miter',
  75. miterLimit: 10,
  76. padding: 0,
  77. stroke: 'black',
  78. strokeThickness: 0,
  79. textBaseline: 'alphabetic',
  80. trim: false,
  81. whiteSpace: 'pre',
  82. wordWrap: false,
  83. wordWrapWidth: 100,
  84. leading: 0,
  85. };
  86. var genericFontFamilies = [
  87. 'serif',
  88. 'sans-serif',
  89. 'monospace',
  90. 'cursive',
  91. 'fantasy',
  92. 'system-ui' ];
  93. /**
  94. * A TextStyle Object contains information to decorate a Text objects.
  95. *
  96. * An instance can be shared between multiple Text objects; then changing the style will update all text objects using it.
  97. *
  98. * A tool can be used to generate a text style [here](https://pixijs.io/pixi-text-style).
  99. *
  100. * @memberof PIXI
  101. */
  102. var TextStyle = /** @class */ (function () {
  103. /**
  104. * @param {object} [style] - The style parameters
  105. * @param {string} [style.align='left'] - Alignment for multiline text ('left', 'center' or 'right'),
  106. * does not affect single line text
  107. * @param {boolean} [style.breakWords=false] - Indicates if lines can be wrapped within words, it
  108. * needs wordWrap to be set to true
  109. * @param {boolean} [style.dropShadow=false] - Set a drop shadow for the text
  110. * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow
  111. * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow
  112. * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius
  113. * @param {string|number} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00'
  114. * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow
  115. * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas
  116. * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient
  117. * eg ['#000000','#FFFFFF']
  118. * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN}
  119. * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fill is an array of colours
  120. * to create a gradient, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT}
  121. * @param {number[]} [style.fillGradientStops] - If fill is an array of colours to create a gradient, this array can set
  122. * the stop points (numbers between 0 and 1) for the color, overriding the default behaviour of evenly spacing them.
  123. * @param {string|string[]} [style.fontFamily='Arial'] - The font family
  124. * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string,
  125. * equivalents are '26px','20pt','160%' or '1.6em')
  126. * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique')
  127. * @param {string} [style.fontVariant='normal'] - The font variant ('normal' or 'small-caps')
  128. * @param {string} [style.fontWeight='normal'] - The font weight ('normal', 'bold', 'bolder', 'lighter' and '100',
  129. * '200', '300', '400', '500', '600', '700', '800' or '900')
  130. * @param {number} [style.leading=0] - The space between lines
  131. * @param {number} [style.letterSpacing=0] - The amount of spacing between letters, default is 0
  132. * @param {number} [style.lineHeight] - The line height, a number that represents the vertical space that a letter uses
  133. * @param {string} [style.lineJoin='miter'] - The lineJoin property sets the type of corner created, it can resolve
  134. * spiked text issues. Possible values "miter" (creates a sharp corner), "round" (creates a round corner) or "bevel"
  135. * (creates a squared corner).
  136. * @param {number} [style.miterLimit=10] - The miter limit to use when using the 'miter' lineJoin mode. This can reduce
  137. * or increase the spikiness of rendered text.
  138. * @param {number} [style.padding=0] - Occasionally some fonts are cropped. Adding some padding will prevent this from
  139. * happening by adding padding to all sides of the text.
  140. * @param {string|number} [style.stroke='black'] - A canvas fillstyle that will be used on the text stroke
  141. * e.g 'blue', '#FCFF00'
  142. * @param {number} [style.strokeThickness=0] - A number that represents the thickness of the stroke.
  143. * Default is 0 (no stroke)
  144. * @param {boolean} [style.trim=false] - Trim transparent borders
  145. * @param {string} [style.textBaseline='alphabetic'] - The baseline of the text that is rendered.
  146. * @param {string} [style.whiteSpace='pre'] - Determines whether newlines & spaces are collapsed or preserved "normal"
  147. * (collapse, collapse), "pre" (preserve, preserve) | "pre-line" (preserve, collapse). It needs wordWrap to be set to true
  148. * @param {boolean} [style.wordWrap=false] - Indicates if word wrap should be used
  149. * @param {number} [style.wordWrapWidth=100] - The width at which text will wrap, it needs wordWrap to be set to true
  150. */
  151. function TextStyle(style) {
  152. this.styleID = 0;
  153. this.reset();
  154. deepCopyProperties(this, style, style);
  155. }
  156. /**
  157. * Creates a new TextStyle object with the same values as this one.
  158. * Note that the only the properties of the object are cloned.
  159. *
  160. * @return New cloned TextStyle object
  161. */
  162. TextStyle.prototype.clone = function () {
  163. var clonedProperties = {};
  164. deepCopyProperties(clonedProperties, this, defaultStyle);
  165. return new TextStyle(clonedProperties);
  166. };
  167. /** Resets all properties to the defaults specified in TextStyle.prototype._default */
  168. TextStyle.prototype.reset = function () {
  169. deepCopyProperties(this, defaultStyle, defaultStyle);
  170. };
  171. Object.defineProperty(TextStyle.prototype, "align", {
  172. /**
  173. * Alignment for multiline text ('left', 'center' or 'right'), does not affect single line text
  174. *
  175. * @member {string}
  176. */
  177. get: function () {
  178. return this._align;
  179. },
  180. set: function (align) {
  181. if (this._align !== align) {
  182. this._align = align;
  183. this.styleID++;
  184. }
  185. },
  186. enumerable: false,
  187. configurable: true
  188. });
  189. Object.defineProperty(TextStyle.prototype, "breakWords", {
  190. /** Indicates if lines can be wrapped within words, it needs wordWrap to be set to true. */
  191. get: function () {
  192. return this._breakWords;
  193. },
  194. set: function (breakWords) {
  195. if (this._breakWords !== breakWords) {
  196. this._breakWords = breakWords;
  197. this.styleID++;
  198. }
  199. },
  200. enumerable: false,
  201. configurable: true
  202. });
  203. Object.defineProperty(TextStyle.prototype, "dropShadow", {
  204. /** Set a drop shadow for the text. */
  205. get: function () {
  206. return this._dropShadow;
  207. },
  208. set: function (dropShadow) {
  209. if (this._dropShadow !== dropShadow) {
  210. this._dropShadow = dropShadow;
  211. this.styleID++;
  212. }
  213. },
  214. enumerable: false,
  215. configurable: true
  216. });
  217. Object.defineProperty(TextStyle.prototype, "dropShadowAlpha", {
  218. /** Set alpha for the drop shadow. */
  219. get: function () {
  220. return this._dropShadowAlpha;
  221. },
  222. set: function (dropShadowAlpha) {
  223. if (this._dropShadowAlpha !== dropShadowAlpha) {
  224. this._dropShadowAlpha = dropShadowAlpha;
  225. this.styleID++;
  226. }
  227. },
  228. enumerable: false,
  229. configurable: true
  230. });
  231. Object.defineProperty(TextStyle.prototype, "dropShadowAngle", {
  232. /** Set a angle of the drop shadow. */
  233. get: function () {
  234. return this._dropShadowAngle;
  235. },
  236. set: function (dropShadowAngle) {
  237. if (this._dropShadowAngle !== dropShadowAngle) {
  238. this._dropShadowAngle = dropShadowAngle;
  239. this.styleID++;
  240. }
  241. },
  242. enumerable: false,
  243. configurable: true
  244. });
  245. Object.defineProperty(TextStyle.prototype, "dropShadowBlur", {
  246. /** Set a shadow blur radius. */
  247. get: function () {
  248. return this._dropShadowBlur;
  249. },
  250. set: function (dropShadowBlur) {
  251. if (this._dropShadowBlur !== dropShadowBlur) {
  252. this._dropShadowBlur = dropShadowBlur;
  253. this.styleID++;
  254. }
  255. },
  256. enumerable: false,
  257. configurable: true
  258. });
  259. Object.defineProperty(TextStyle.prototype, "dropShadowColor", {
  260. /** A fill style to be used on the dropshadow e.g 'red', '#00FF00'. */
  261. get: function () {
  262. return this._dropShadowColor;
  263. },
  264. set: function (dropShadowColor) {
  265. var outputColor = getColor(dropShadowColor);
  266. if (this._dropShadowColor !== outputColor) {
  267. this._dropShadowColor = outputColor;
  268. this.styleID++;
  269. }
  270. },
  271. enumerable: false,
  272. configurable: true
  273. });
  274. Object.defineProperty(TextStyle.prototype, "dropShadowDistance", {
  275. /** Set a distance of the drop shadow. */
  276. get: function () {
  277. return this._dropShadowDistance;
  278. },
  279. set: function (dropShadowDistance) {
  280. if (this._dropShadowDistance !== dropShadowDistance) {
  281. this._dropShadowDistance = dropShadowDistance;
  282. this.styleID++;
  283. }
  284. },
  285. enumerable: false,
  286. configurable: true
  287. });
  288. Object.defineProperty(TextStyle.prototype, "fill", {
  289. /**
  290. * A canvas fillstyle that will be used on the text e.g 'red', '#00FF00'.
  291. *
  292. * Can be an array to create a gradient eg ['#000000','#FFFFFF']
  293. * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN}
  294. *
  295. * @member {string|string[]|number|number[]|CanvasGradient|CanvasPattern}
  296. */
  297. get: function () {
  298. return this._fill;
  299. },
  300. set: function (fill) {
  301. // TODO: Can't have different types for getter and setter. The getter shouldn't have the number type as
  302. // the setter converts to string. See this thread for more details:
  303. // https://github.com/microsoft/TypeScript/issues/2521
  304. // TODO: Not sure if getColor works properly with CanvasGradient and/or CanvasPattern, can't pass in
  305. // without casting here.
  306. var outputColor = getColor(fill);
  307. if (this._fill !== outputColor) {
  308. this._fill = outputColor;
  309. this.styleID++;
  310. }
  311. },
  312. enumerable: false,
  313. configurable: true
  314. });
  315. Object.defineProperty(TextStyle.prototype, "fillGradientType", {
  316. /**
  317. * If fill is an array of colours to create a gradient, this can change the type/direction of the gradient.
  318. *
  319. * @see PIXI.TEXT_GRADIENT
  320. */
  321. get: function () {
  322. return this._fillGradientType;
  323. },
  324. set: function (fillGradientType) {
  325. if (this._fillGradientType !== fillGradientType) {
  326. this._fillGradientType = fillGradientType;
  327. this.styleID++;
  328. }
  329. },
  330. enumerable: false,
  331. configurable: true
  332. });
  333. Object.defineProperty(TextStyle.prototype, "fillGradientStops", {
  334. /**
  335. * If fill is an array of colours to create a gradient, this array can set the stop points
  336. * (numbers between 0 and 1) for the color, overriding the default behaviour of evenly spacing them.
  337. */
  338. get: function () {
  339. return this._fillGradientStops;
  340. },
  341. set: function (fillGradientStops) {
  342. if (!areArraysEqual(this._fillGradientStops, fillGradientStops)) {
  343. this._fillGradientStops = fillGradientStops;
  344. this.styleID++;
  345. }
  346. },
  347. enumerable: false,
  348. configurable: true
  349. });
  350. Object.defineProperty(TextStyle.prototype, "fontFamily", {
  351. /** The font family. */
  352. get: function () {
  353. return this._fontFamily;
  354. },
  355. set: function (fontFamily) {
  356. if (this.fontFamily !== fontFamily) {
  357. this._fontFamily = fontFamily;
  358. this.styleID++;
  359. }
  360. },
  361. enumerable: false,
  362. configurable: true
  363. });
  364. Object.defineProperty(TextStyle.prototype, "fontSize", {
  365. /**
  366. * The font size
  367. * (as a number it converts to px, but as a string, equivalents are '26px','20pt','160%' or '1.6em')
  368. */
  369. get: function () {
  370. return this._fontSize;
  371. },
  372. set: function (fontSize) {
  373. if (this._fontSize !== fontSize) {
  374. this._fontSize = fontSize;
  375. this.styleID++;
  376. }
  377. },
  378. enumerable: false,
  379. configurable: true
  380. });
  381. Object.defineProperty(TextStyle.prototype, "fontStyle", {
  382. /**
  383. * The font style
  384. * ('normal', 'italic' or 'oblique')
  385. *
  386. * @member {string}
  387. */
  388. get: function () {
  389. return this._fontStyle;
  390. },
  391. set: function (fontStyle) {
  392. if (this._fontStyle !== fontStyle) {
  393. this._fontStyle = fontStyle;
  394. this.styleID++;
  395. }
  396. },
  397. enumerable: false,
  398. configurable: true
  399. });
  400. Object.defineProperty(TextStyle.prototype, "fontVariant", {
  401. /**
  402. * The font variant
  403. * ('normal' or 'small-caps')
  404. *
  405. * @member {string}
  406. */
  407. get: function () {
  408. return this._fontVariant;
  409. },
  410. set: function (fontVariant) {
  411. if (this._fontVariant !== fontVariant) {
  412. this._fontVariant = fontVariant;
  413. this.styleID++;
  414. }
  415. },
  416. enumerable: false,
  417. configurable: true
  418. });
  419. Object.defineProperty(TextStyle.prototype, "fontWeight", {
  420. /**
  421. * The font weight
  422. * ('normal', 'bold', 'bolder', 'lighter' and '100', '200', '300', '400', '500', '600', '700', 800' or '900')
  423. *
  424. * @member {string}
  425. */
  426. get: function () {
  427. return this._fontWeight;
  428. },
  429. set: function (fontWeight) {
  430. if (this._fontWeight !== fontWeight) {
  431. this._fontWeight = fontWeight;
  432. this.styleID++;
  433. }
  434. },
  435. enumerable: false,
  436. configurable: true
  437. });
  438. Object.defineProperty(TextStyle.prototype, "letterSpacing", {
  439. /** The amount of spacing between letters, default is 0. */
  440. get: function () {
  441. return this._letterSpacing;
  442. },
  443. set: function (letterSpacing) {
  444. if (this._letterSpacing !== letterSpacing) {
  445. this._letterSpacing = letterSpacing;
  446. this.styleID++;
  447. }
  448. },
  449. enumerable: false,
  450. configurable: true
  451. });
  452. Object.defineProperty(TextStyle.prototype, "lineHeight", {
  453. /** The line height, a number that represents the vertical space that a letter uses. */
  454. get: function () {
  455. return this._lineHeight;
  456. },
  457. set: function (lineHeight) {
  458. if (this._lineHeight !== lineHeight) {
  459. this._lineHeight = lineHeight;
  460. this.styleID++;
  461. }
  462. },
  463. enumerable: false,
  464. configurable: true
  465. });
  466. Object.defineProperty(TextStyle.prototype, "leading", {
  467. /** The space between lines. */
  468. get: function () {
  469. return this._leading;
  470. },
  471. set: function (leading) {
  472. if (this._leading !== leading) {
  473. this._leading = leading;
  474. this.styleID++;
  475. }
  476. },
  477. enumerable: false,
  478. configurable: true
  479. });
  480. Object.defineProperty(TextStyle.prototype, "lineJoin", {
  481. /**
  482. * The lineJoin property sets the type of corner created, it can resolve spiked text issues.
  483. * Default is 'miter' (creates a sharp corner).
  484. *
  485. * @member {string}
  486. */
  487. get: function () {
  488. return this._lineJoin;
  489. },
  490. set: function (lineJoin) {
  491. if (this._lineJoin !== lineJoin) {
  492. this._lineJoin = lineJoin;
  493. this.styleID++;
  494. }
  495. },
  496. enumerable: false,
  497. configurable: true
  498. });
  499. Object.defineProperty(TextStyle.prototype, "miterLimit", {
  500. /**
  501. * The miter limit to use when using the 'miter' lineJoin mode.
  502. *
  503. * This can reduce or increase the spikiness of rendered text.
  504. */
  505. get: function () {
  506. return this._miterLimit;
  507. },
  508. set: function (miterLimit) {
  509. if (this._miterLimit !== miterLimit) {
  510. this._miterLimit = miterLimit;
  511. this.styleID++;
  512. }
  513. },
  514. enumerable: false,
  515. configurable: true
  516. });
  517. Object.defineProperty(TextStyle.prototype, "padding", {
  518. /**
  519. * Occasionally some fonts are cropped. Adding some padding will prevent this from happening
  520. * by adding padding to all sides of the text.
  521. */
  522. get: function () {
  523. return this._padding;
  524. },
  525. set: function (padding) {
  526. if (this._padding !== padding) {
  527. this._padding = padding;
  528. this.styleID++;
  529. }
  530. },
  531. enumerable: false,
  532. configurable: true
  533. });
  534. Object.defineProperty(TextStyle.prototype, "stroke", {
  535. /**
  536. * A canvas fillstyle that will be used on the text stroke
  537. * e.g 'blue', '#FCFF00'
  538. */
  539. get: function () {
  540. return this._stroke;
  541. },
  542. set: function (stroke) {
  543. // TODO: Can't have different types for getter and setter. The getter shouldn't have the number type as
  544. // the setter converts to string. See this thread for more details:
  545. // https://github.com/microsoft/TypeScript/issues/2521
  546. var outputColor = getColor(stroke);
  547. if (this._stroke !== outputColor) {
  548. this._stroke = outputColor;
  549. this.styleID++;
  550. }
  551. },
  552. enumerable: false,
  553. configurable: true
  554. });
  555. Object.defineProperty(TextStyle.prototype, "strokeThickness", {
  556. /**
  557. * A number that represents the thickness of the stroke.
  558. *
  559. * @default 0
  560. */
  561. get: function () {
  562. return this._strokeThickness;
  563. },
  564. set: function (strokeThickness) {
  565. if (this._strokeThickness !== strokeThickness) {
  566. this._strokeThickness = strokeThickness;
  567. this.styleID++;
  568. }
  569. },
  570. enumerable: false,
  571. configurable: true
  572. });
  573. Object.defineProperty(TextStyle.prototype, "textBaseline", {
  574. /**
  575. * The baseline of the text that is rendered.
  576. *
  577. * @member {string}
  578. */
  579. get: function () {
  580. return this._textBaseline;
  581. },
  582. set: function (textBaseline) {
  583. if (this._textBaseline !== textBaseline) {
  584. this._textBaseline = textBaseline;
  585. this.styleID++;
  586. }
  587. },
  588. enumerable: false,
  589. configurable: true
  590. });
  591. Object.defineProperty(TextStyle.prototype, "trim", {
  592. /** Trim transparent borders. */
  593. get: function () {
  594. return this._trim;
  595. },
  596. set: function (trim) {
  597. if (this._trim !== trim) {
  598. this._trim = trim;
  599. this.styleID++;
  600. }
  601. },
  602. enumerable: false,
  603. configurable: true
  604. });
  605. Object.defineProperty(TextStyle.prototype, "whiteSpace", {
  606. /**
  607. * How newlines and spaces should be handled.
  608. * Default is 'pre' (preserve, preserve).
  609. *
  610. * value | New lines | Spaces
  611. * --- | --- | ---
  612. * 'normal' | Collapse | Collapse
  613. * 'pre' | Preserve | Preserve
  614. * 'pre-line' | Preserve | Collapse
  615. *
  616. * @member {string}
  617. */
  618. get: function () {
  619. return this._whiteSpace;
  620. },
  621. set: function (whiteSpace) {
  622. if (this._whiteSpace !== whiteSpace) {
  623. this._whiteSpace = whiteSpace;
  624. this.styleID++;
  625. }
  626. },
  627. enumerable: false,
  628. configurable: true
  629. });
  630. Object.defineProperty(TextStyle.prototype, "wordWrap", {
  631. /** Indicates if word wrap should be used. */
  632. get: function () {
  633. return this._wordWrap;
  634. },
  635. set: function (wordWrap) {
  636. if (this._wordWrap !== wordWrap) {
  637. this._wordWrap = wordWrap;
  638. this.styleID++;
  639. }
  640. },
  641. enumerable: false,
  642. configurable: true
  643. });
  644. Object.defineProperty(TextStyle.prototype, "wordWrapWidth", {
  645. /** The width at which text will wrap, it needs wordWrap to be set to true. */
  646. get: function () {
  647. return this._wordWrapWidth;
  648. },
  649. set: function (wordWrapWidth) {
  650. if (this._wordWrapWidth !== wordWrapWidth) {
  651. this._wordWrapWidth = wordWrapWidth;
  652. this.styleID++;
  653. }
  654. },
  655. enumerable: false,
  656. configurable: true
  657. });
  658. /**
  659. * Generates a font style string to use for `TextMetrics.measureFont()`.
  660. *
  661. * @return Font style string, for passing to `TextMetrics.measureFont()`
  662. */
  663. TextStyle.prototype.toFontString = function () {
  664. // build canvas api font setting from individual components. Convert a numeric this.fontSize to px
  665. var fontSizeString = (typeof this.fontSize === 'number') ? this.fontSize + "px" : this.fontSize;
  666. // Clean-up fontFamily property by quoting each font name
  667. // this will support font names with spaces
  668. var fontFamilies = this.fontFamily;
  669. if (!Array.isArray(this.fontFamily)) {
  670. fontFamilies = this.fontFamily.split(',');
  671. }
  672. for (var i = fontFamilies.length - 1; i >= 0; i--) {
  673. // Trim any extra white-space
  674. var fontFamily = fontFamilies[i].trim();
  675. // Check if font already contains strings
  676. if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily) && genericFontFamilies.indexOf(fontFamily) < 0) {
  677. fontFamily = "\"" + fontFamily + "\"";
  678. }
  679. fontFamilies[i] = fontFamily;
  680. }
  681. return this.fontStyle + " " + this.fontVariant + " " + this.fontWeight + " " + fontSizeString + " " + fontFamilies.join(',');
  682. };
  683. return TextStyle;
  684. }());
  685. /**
  686. * Utility function to convert hexadecimal colors to strings, and simply return the color if it's a string.
  687. * @private
  688. * @param color
  689. * @return The color as a string.
  690. */
  691. function getSingleColor(color) {
  692. if (typeof color === 'number') {
  693. return utils.hex2string(color);
  694. }
  695. else if (typeof color === 'string') {
  696. if (color.indexOf('0x') === 0) {
  697. color = color.replace('0x', '#');
  698. }
  699. }
  700. return color;
  701. }
  702. function getColor(color) {
  703. if (!Array.isArray(color)) {
  704. return getSingleColor(color);
  705. }
  706. else {
  707. for (var i = 0; i < color.length; ++i) {
  708. color[i] = getSingleColor(color[i]);
  709. }
  710. return color;
  711. }
  712. }
  713. /**
  714. * Utility function to convert hexadecimal colors to strings, and simply return the color if it's a string.
  715. * This version can also convert array of colors
  716. * @private
  717. * @param array1 - First array to compare
  718. * @param array2 - Second array to compare
  719. * @return Do the arrays contain the same values in the same order
  720. */
  721. function areArraysEqual(array1, array2) {
  722. if (!Array.isArray(array1) || !Array.isArray(array2)) {
  723. return false;
  724. }
  725. if (array1.length !== array2.length) {
  726. return false;
  727. }
  728. for (var i = 0; i < array1.length; ++i) {
  729. if (array1[i] !== array2[i]) {
  730. return false;
  731. }
  732. }
  733. return true;
  734. }
  735. /**
  736. * Utility function to ensure that object properties are copied by value, and not by reference
  737. * @private
  738. * @param target - Target object to copy properties into
  739. * @param source - Source object for the properties to copy
  740. * @param propertyObj - Object containing properties names we want to loop over
  741. */
  742. function deepCopyProperties(target, source, propertyObj) {
  743. for (var prop in propertyObj) {
  744. if (Array.isArray(source[prop])) {
  745. target[prop] = source[prop].slice();
  746. }
  747. else {
  748. target[prop] = source[prop];
  749. }
  750. }
  751. }
  752. // Default settings used for all getContext calls
  753. var contextSettings = {
  754. // TextMetrics requires getImageData readback for measuring fonts.
  755. willReadFrequently: true,
  756. };
  757. /**
  758. * The TextMetrics object represents the measurement of a block of text with a specified style.
  759. *
  760. * ```js
  761. * let style = new PIXI.TextStyle({fontFamily : 'Arial', fontSize: 24, fill : 0xff1010, align : 'center'})
  762. * let textMetrics = PIXI.TextMetrics.measureText('Your text', style)
  763. * ```
  764. * @memberof PIXI
  765. */
  766. var TextMetrics = /** @class */ (function () {
  767. /**
  768. * @param text - the text that was measured
  769. * @param style - the style that was measured
  770. * @param width - the measured width of the text
  771. * @param height - the measured height of the text
  772. * @param lines - an array of the lines of text broken by new lines and wrapping if specified in style
  773. * @param lineWidths - an array of the line widths for each line matched to `lines`
  774. * @param lineHeight - the measured line height for this style
  775. * @param maxLineWidth - the maximum line width for all measured lines
  776. * @param {PIXI.IFontMetrics} fontProperties - the font properties object from TextMetrics.measureFont
  777. */
  778. function TextMetrics(text, style, width, height, lines, lineWidths, lineHeight, maxLineWidth, fontProperties) {
  779. this.text = text;
  780. this.style = style;
  781. this.width = width;
  782. this.height = height;
  783. this.lines = lines;
  784. this.lineWidths = lineWidths;
  785. this.lineHeight = lineHeight;
  786. this.maxLineWidth = maxLineWidth;
  787. this.fontProperties = fontProperties;
  788. }
  789. /**
  790. * Measures the supplied string of text and returns a Rectangle.
  791. * @param text - The text to measure.
  792. * @param style - The text style to use for measuring
  793. * @param wordWrap - Override for if word-wrap should be applied to the text.
  794. * @param canvas - optional specification of the canvas to use for measuring.
  795. * @returns Measured width and height of the text.
  796. */
  797. TextMetrics.measureText = function (text, style, wordWrap, canvas) {
  798. if (canvas === void 0) { canvas = TextMetrics._canvas; }
  799. wordWrap = (wordWrap === undefined || wordWrap === null) ? style.wordWrap : wordWrap;
  800. var font = style.toFontString();
  801. var fontProperties = TextMetrics.measureFont(font);
  802. // fallback in case UA disallow canvas data extraction
  803. // (toDataURI, getImageData functions)
  804. if (fontProperties.fontSize === 0) {
  805. fontProperties.fontSize = style.fontSize;
  806. fontProperties.ascent = style.fontSize;
  807. }
  808. var context = canvas.getContext('2d', contextSettings);
  809. context.font = font;
  810. var outputText = wordWrap ? TextMetrics.wordWrap(text, style, canvas) : text;
  811. var lines = outputText.split(/(?:\r\n|\r|\n)/);
  812. var lineWidths = new Array(lines.length);
  813. var maxLineWidth = 0;
  814. for (var i = 0; i < lines.length; i++) {
  815. var lineWidth = context.measureText(lines[i]).width + ((lines[i].length - 1) * style.letterSpacing);
  816. lineWidths[i] = lineWidth;
  817. maxLineWidth = Math.max(maxLineWidth, lineWidth);
  818. }
  819. var width = maxLineWidth + style.strokeThickness;
  820. if (style.dropShadow) {
  821. width += style.dropShadowDistance;
  822. }
  823. var lineHeight = style.lineHeight || fontProperties.fontSize + style.strokeThickness;
  824. var height = Math.max(lineHeight, fontProperties.fontSize + style.strokeThickness)
  825. + ((lines.length - 1) * (lineHeight + style.leading));
  826. if (style.dropShadow) {
  827. height += style.dropShadowDistance;
  828. }
  829. return new TextMetrics(text, style, width, height, lines, lineWidths, lineHeight + style.leading, maxLineWidth, fontProperties);
  830. };
  831. /**
  832. * Applies newlines to a string to have it optimally fit into the horizontal
  833. * bounds set by the Text object's wordWrapWidth property.
  834. * @param text - String to apply word wrapping to
  835. * @param style - the style to use when wrapping
  836. * @param canvas - optional specification of the canvas to use for measuring.
  837. * @returns New string with new lines applied where required
  838. */
  839. TextMetrics.wordWrap = function (text, style, canvas) {
  840. if (canvas === void 0) { canvas = TextMetrics._canvas; }
  841. var context = canvas.getContext('2d', contextSettings);
  842. var width = 0;
  843. var line = '';
  844. var lines = '';
  845. var cache = Object.create(null);
  846. var letterSpacing = style.letterSpacing, whiteSpace = style.whiteSpace;
  847. // How to handle whitespaces
  848. var collapseSpaces = TextMetrics.collapseSpaces(whiteSpace);
  849. var collapseNewlines = TextMetrics.collapseNewlines(whiteSpace);
  850. // whether or not spaces may be added to the beginning of lines
  851. var canPrependSpaces = !collapseSpaces;
  852. // There is letterSpacing after every char except the last one
  853. // t_h_i_s_' '_i_s_' '_a_n_' '_e_x_a_m_p_l_e_' '_!
  854. // so for convenience the above needs to be compared to width + 1 extra letterSpace
  855. // t_h_i_s_' '_i_s_' '_a_n_' '_e_x_a_m_p_l_e_' '_!_
  856. // ________________________________________________
  857. // And then the final space is simply no appended to each line
  858. var wordWrapWidth = style.wordWrapWidth + letterSpacing;
  859. // break text into words, spaces and newline chars
  860. var tokens = TextMetrics.tokenize(text);
  861. for (var i = 0; i < tokens.length; i++) {
  862. // get the word, space or newlineChar
  863. var token = tokens[i];
  864. // if word is a new line
  865. if (TextMetrics.isNewline(token)) {
  866. // keep the new line
  867. if (!collapseNewlines) {
  868. lines += TextMetrics.addLine(line);
  869. canPrependSpaces = !collapseSpaces;
  870. line = '';
  871. width = 0;
  872. continue;
  873. }
  874. // if we should collapse new lines
  875. // we simply convert it into a space
  876. token = ' ';
  877. }
  878. // if we should collapse repeated whitespaces
  879. if (collapseSpaces) {
  880. // check both this and the last tokens for spaces
  881. var currIsBreakingSpace = TextMetrics.isBreakingSpace(token);
  882. var lastIsBreakingSpace = TextMetrics.isBreakingSpace(line[line.length - 1]);
  883. if (currIsBreakingSpace && lastIsBreakingSpace) {
  884. continue;
  885. }
  886. }
  887. // get word width from cache if possible
  888. var tokenWidth = TextMetrics.getFromCache(token, letterSpacing, cache, context);
  889. // word is longer than desired bounds
  890. if (tokenWidth > wordWrapWidth) {
  891. // if we are not already at the beginning of a line
  892. if (line !== '') {
  893. // start newlines for overflow words
  894. lines += TextMetrics.addLine(line);
  895. line = '';
  896. width = 0;
  897. }
  898. // break large word over multiple lines
  899. if (TextMetrics.canBreakWords(token, style.breakWords)) {
  900. // break word into characters
  901. var characters = TextMetrics.wordWrapSplit(token);
  902. // loop the characters
  903. for (var j = 0; j < characters.length; j++) {
  904. var char = characters[j];
  905. var k = 1;
  906. // we are not at the end of the token
  907. while (characters[j + k]) {
  908. var nextChar = characters[j + k];
  909. var lastChar = char[char.length - 1];
  910. // should not split chars
  911. if (!TextMetrics.canBreakChars(lastChar, nextChar, token, j, style.breakWords)) {
  912. // combine chars & move forward one
  913. char += nextChar;
  914. }
  915. else {
  916. break;
  917. }
  918. k++;
  919. }
  920. j += char.length - 1;
  921. var characterWidth = TextMetrics.getFromCache(char, letterSpacing, cache, context);
  922. if (characterWidth + width > wordWrapWidth) {
  923. lines += TextMetrics.addLine(line);
  924. canPrependSpaces = false;
  925. line = '';
  926. width = 0;
  927. }
  928. line += char;
  929. width += characterWidth;
  930. }
  931. }
  932. // run word out of the bounds
  933. else {
  934. // if there are words in this line already
  935. // finish that line and start a new one
  936. if (line.length > 0) {
  937. lines += TextMetrics.addLine(line);
  938. line = '';
  939. width = 0;
  940. }
  941. var isLastToken = i === tokens.length - 1;
  942. // give it its own line if it's not the end
  943. lines += TextMetrics.addLine(token, !isLastToken);
  944. canPrependSpaces = false;
  945. line = '';
  946. width = 0;
  947. }
  948. }
  949. // word could fit
  950. else {
  951. // word won't fit because of existing words
  952. // start a new line
  953. if (tokenWidth + width > wordWrapWidth) {
  954. // if its a space we don't want it
  955. canPrependSpaces = false;
  956. // add a new line
  957. lines += TextMetrics.addLine(line);
  958. // start a new line
  959. line = '';
  960. width = 0;
  961. }
  962. // don't add spaces to the beginning of lines
  963. if (line.length > 0 || !TextMetrics.isBreakingSpace(token) || canPrependSpaces) {
  964. // add the word to the current line
  965. line += token;
  966. // update width counter
  967. width += tokenWidth;
  968. }
  969. }
  970. }
  971. lines += TextMetrics.addLine(line, false);
  972. return lines;
  973. };
  974. /**
  975. * Convienience function for logging each line added during the wordWrap method.
  976. * @param line - The line of text to add
  977. * @param newLine - Add new line character to end
  978. * @returns A formatted line
  979. */
  980. TextMetrics.addLine = function (line, newLine) {
  981. if (newLine === void 0) { newLine = true; }
  982. line = TextMetrics.trimRight(line);
  983. line = (newLine) ? line + "\n" : line;
  984. return line;
  985. };
  986. /**
  987. * Gets & sets the widths of calculated characters in a cache object
  988. * @param key - The key
  989. * @param letterSpacing - The letter spacing
  990. * @param cache - The cache
  991. * @param context - The canvas context
  992. * @returns The from cache.
  993. */
  994. TextMetrics.getFromCache = function (key, letterSpacing, cache, context) {
  995. var width = cache[key];
  996. if (typeof width !== 'number') {
  997. var spacing = ((key.length) * letterSpacing);
  998. width = context.measureText(key).width + spacing;
  999. cache[key] = width;
  1000. }
  1001. return width;
  1002. };
  1003. /**
  1004. * Determines whether we should collapse breaking spaces.
  1005. * @param whiteSpace - The TextStyle property whiteSpace
  1006. * @returns Should collapse
  1007. */
  1008. TextMetrics.collapseSpaces = function (whiteSpace) {
  1009. return (whiteSpace === 'normal' || whiteSpace === 'pre-line');
  1010. };
  1011. /**
  1012. * Determines whether we should collapse newLine chars.
  1013. * @param whiteSpace - The white space
  1014. * @returns should collapse
  1015. */
  1016. TextMetrics.collapseNewlines = function (whiteSpace) {
  1017. return (whiteSpace === 'normal');
  1018. };
  1019. /**
  1020. * Trims breaking whitespaces from string.
  1021. * @param text - The text
  1022. * @returns Trimmed string
  1023. */
  1024. TextMetrics.trimRight = function (text) {
  1025. if (typeof text !== 'string') {
  1026. return '';
  1027. }
  1028. for (var i = text.length - 1; i >= 0; i--) {
  1029. var char = text[i];
  1030. if (!TextMetrics.isBreakingSpace(char)) {
  1031. break;
  1032. }
  1033. text = text.slice(0, -1);
  1034. }
  1035. return text;
  1036. };
  1037. /**
  1038. * Determines if char is a newline.
  1039. * @param char - The character
  1040. * @returns True if newline, False otherwise.
  1041. */
  1042. TextMetrics.isNewline = function (char) {
  1043. if (typeof char !== 'string') {
  1044. return false;
  1045. }
  1046. return (TextMetrics._newlines.indexOf(char.charCodeAt(0)) >= 0);
  1047. };
  1048. /**
  1049. * Determines if char is a breaking whitespace.
  1050. *
  1051. * It allows one to determine whether char should be a breaking whitespace
  1052. * For example certain characters in CJK langs or numbers.
  1053. * It must return a boolean.
  1054. * @param char - The character
  1055. * @param [_nextChar] - The next character
  1056. * @returns True if whitespace, False otherwise.
  1057. */
  1058. TextMetrics.isBreakingSpace = function (char, _nextChar) {
  1059. if (typeof char !== 'string') {
  1060. return false;
  1061. }
  1062. return (TextMetrics._breakingSpaces.indexOf(char.charCodeAt(0)) >= 0);
  1063. };
  1064. /**
  1065. * Splits a string into words, breaking-spaces and newLine characters
  1066. * @param text - The text
  1067. * @returns A tokenized array
  1068. */
  1069. TextMetrics.tokenize = function (text) {
  1070. var tokens = [];
  1071. var token = '';
  1072. if (typeof text !== 'string') {
  1073. return tokens;
  1074. }
  1075. for (var i = 0; i < text.length; i++) {
  1076. var char = text[i];
  1077. var nextChar = text[i + 1];
  1078. if (TextMetrics.isBreakingSpace(char, nextChar) || TextMetrics.isNewline(char)) {
  1079. if (token !== '') {
  1080. tokens.push(token);
  1081. token = '';
  1082. }
  1083. tokens.push(char);
  1084. continue;
  1085. }
  1086. token += char;
  1087. }
  1088. if (token !== '') {
  1089. tokens.push(token);
  1090. }
  1091. return tokens;
  1092. };
  1093. /**
  1094. * Overridable helper method used internally by TextMetrics, exposed to allow customizing the class's behavior.
  1095. *
  1096. * It allows one to customise which words should break
  1097. * Examples are if the token is CJK or numbers.
  1098. * It must return a boolean.
  1099. * @param _token - The token
  1100. * @param breakWords - The style attr break words
  1101. * @returns Whether to break word or not
  1102. */
  1103. TextMetrics.canBreakWords = function (_token, breakWords) {
  1104. return breakWords;
  1105. };
  1106. /**
  1107. * Overridable helper method used internally by TextMetrics, exposed to allow customizing the class's behavior.
  1108. *
  1109. * It allows one to determine whether a pair of characters
  1110. * should be broken by newlines
  1111. * For example certain characters in CJK langs or numbers.
  1112. * It must return a boolean.
  1113. * @param _char - The character
  1114. * @param _nextChar - The next character
  1115. * @param _token - The token/word the characters are from
  1116. * @param _index - The index in the token of the char
  1117. * @param _breakWords - The style attr break words
  1118. * @returns whether to break word or not
  1119. */
  1120. TextMetrics.canBreakChars = function (_char, _nextChar, _token, _index, _breakWords) {
  1121. return true;
  1122. };
  1123. /**
  1124. * Overridable helper method used internally by TextMetrics, exposed to allow customizing the class's behavior.
  1125. *
  1126. * It is called when a token (usually a word) has to be split into separate pieces
  1127. * in order to determine the point to break a word.
  1128. * It must return an array of characters.
  1129. * @example
  1130. * // Correctly splits emojis, eg "🤪🤪" will result in two element array, each with one emoji.
  1131. * TextMetrics.wordWrapSplit = (token) => [...token];
  1132. * @param token - The token to split
  1133. * @returns The characters of the token
  1134. */
  1135. TextMetrics.wordWrapSplit = function (token) {
  1136. return token.split('');
  1137. };
  1138. /**
  1139. * Calculates the ascent, descent and fontSize of a given font-style
  1140. * @param font - String representing the style of the font
  1141. * @returns Font properties object
  1142. */
  1143. TextMetrics.measureFont = function (font) {
  1144. // as this method is used for preparing assets, don't recalculate things if we don't need to
  1145. if (TextMetrics._fonts[font]) {
  1146. return TextMetrics._fonts[font];
  1147. }
  1148. var properties = {
  1149. ascent: 0,
  1150. descent: 0,
  1151. fontSize: 0,
  1152. };
  1153. var canvas = TextMetrics._canvas;
  1154. var context = TextMetrics._context;
  1155. context.font = font;
  1156. var metricsString = TextMetrics.METRICS_STRING + TextMetrics.BASELINE_SYMBOL;
  1157. var width = Math.ceil(context.measureText(metricsString).width);
  1158. var baseline = Math.ceil(context.measureText(TextMetrics.BASELINE_SYMBOL).width);
  1159. var height = Math.ceil(TextMetrics.HEIGHT_MULTIPLIER * baseline);
  1160. baseline = baseline * TextMetrics.BASELINE_MULTIPLIER | 0;
  1161. canvas.width = width;
  1162. canvas.height = height;
  1163. context.fillStyle = '#f00';
  1164. context.fillRect(0, 0, width, height);
  1165. context.font = font;
  1166. context.textBaseline = 'alphabetic';
  1167. context.fillStyle = '#000';
  1168. context.fillText(metricsString, 0, baseline);
  1169. var imagedata = context.getImageData(0, 0, width, height).data;
  1170. var pixels = imagedata.length;
  1171. var line = width * 4;
  1172. var i = 0;
  1173. var idx = 0;
  1174. var stop = false;
  1175. // ascent. scan from top to bottom until we find a non red pixel
  1176. for (i = 0; i < baseline; ++i) {
  1177. for (var j = 0; j < line; j += 4) {
  1178. if (imagedata[idx + j] !== 255) {
  1179. stop = true;
  1180. break;
  1181. }
  1182. }
  1183. if (!stop) {
  1184. idx += line;
  1185. }
  1186. else {
  1187. break;
  1188. }
  1189. }
  1190. properties.ascent = baseline - i;
  1191. idx = pixels - line;
  1192. stop = false;
  1193. // descent. scan from bottom to top until we find a non red pixel
  1194. for (i = height; i > baseline; --i) {
  1195. for (var j = 0; j < line; j += 4) {
  1196. if (imagedata[idx + j] !== 255) {
  1197. stop = true;
  1198. break;
  1199. }
  1200. }
  1201. if (!stop) {
  1202. idx -= line;
  1203. }
  1204. else {
  1205. break;
  1206. }
  1207. }
  1208. properties.descent = i - baseline;
  1209. properties.fontSize = properties.ascent + properties.descent;
  1210. TextMetrics._fonts[font] = properties;
  1211. return properties;
  1212. };
  1213. /**
  1214. * Clear font metrics in metrics cache.
  1215. * @param {string} [font] - font name. If font name not set then clear cache for all fonts.
  1216. */
  1217. TextMetrics.clearMetrics = function (font) {
  1218. if (font === void 0) { font = ''; }
  1219. if (font) {
  1220. delete TextMetrics._fonts[font];
  1221. }
  1222. else {
  1223. TextMetrics._fonts = {};
  1224. }
  1225. };
  1226. Object.defineProperty(TextMetrics, "_canvas", {
  1227. /**
  1228. * Cached canvas element for measuring text
  1229. * TODO: this should be private, but isn't because of backward compat, will fix later.
  1230. * @ignore
  1231. */
  1232. get: function () {
  1233. if (!TextMetrics.__canvas) {
  1234. var canvas = void 0;
  1235. try {
  1236. // OffscreenCanvas2D measureText can be up to 40% faster.
  1237. var c = new OffscreenCanvas(0, 0);
  1238. var context = c.getContext('2d', contextSettings);
  1239. if (context && context.measureText) {
  1240. TextMetrics.__canvas = c;
  1241. return c;
  1242. }
  1243. canvas = settings.settings.ADAPTER.createCanvas();
  1244. }
  1245. catch (ex) {
  1246. canvas = settings.settings.ADAPTER.createCanvas();
  1247. }
  1248. canvas.width = canvas.height = 10;
  1249. TextMetrics.__canvas = canvas;
  1250. }
  1251. return TextMetrics.__canvas;
  1252. },
  1253. enumerable: false,
  1254. configurable: true
  1255. });
  1256. Object.defineProperty(TextMetrics, "_context", {
  1257. /**
  1258. * TODO: this should be private, but isn't because of backward compat, will fix later.
  1259. * @ignore
  1260. */
  1261. get: function () {
  1262. if (!TextMetrics.__context) {
  1263. TextMetrics.__context = TextMetrics._canvas.getContext('2d', contextSettings);
  1264. }
  1265. return TextMetrics.__context;
  1266. },
  1267. enumerable: false,
  1268. configurable: true
  1269. });
  1270. return TextMetrics;
  1271. }());
  1272. /**
  1273. * Internal return object for {@link PIXI.TextMetrics.measureFont `TextMetrics.measureFont`}.
  1274. * @typedef {object} FontMetrics
  1275. * @property {number} ascent - The ascent distance
  1276. * @property {number} descent - The descent distance
  1277. * @property {number} fontSize - Font size from ascent to descent
  1278. * @memberof PIXI.TextMetrics
  1279. * @private
  1280. */
  1281. /**
  1282. * Cache of {@see PIXI.TextMetrics.FontMetrics} objects.
  1283. * @memberof PIXI.TextMetrics
  1284. * @type {object}
  1285. * @private
  1286. */
  1287. TextMetrics._fonts = {};
  1288. /**
  1289. * String used for calculate font metrics.
  1290. * These characters are all tall to help calculate the height required for text.
  1291. * @static
  1292. * @memberof PIXI.TextMetrics
  1293. * @name METRICS_STRING
  1294. * @type {string}
  1295. * @default |ÉqÅ
  1296. */
  1297. TextMetrics.METRICS_STRING = '|ÉqÅ';
  1298. /**
  1299. * Baseline symbol for calculate font metrics.
  1300. * @static
  1301. * @memberof PIXI.TextMetrics
  1302. * @name BASELINE_SYMBOL
  1303. * @type {string}
  1304. * @default M
  1305. */
  1306. TextMetrics.BASELINE_SYMBOL = 'M';
  1307. /**
  1308. * Baseline multiplier for calculate font metrics.
  1309. * @static
  1310. * @memberof PIXI.TextMetrics
  1311. * @name BASELINE_MULTIPLIER
  1312. * @type {number}
  1313. * @default 1.4
  1314. */
  1315. TextMetrics.BASELINE_MULTIPLIER = 1.4;
  1316. /**
  1317. * Height multiplier for setting height of canvas to calculate font metrics.
  1318. * @static
  1319. * @memberof PIXI.TextMetrics
  1320. * @name HEIGHT_MULTIPLIER
  1321. * @type {number}
  1322. * @default 2.00
  1323. */
  1324. TextMetrics.HEIGHT_MULTIPLIER = 2.0;
  1325. /**
  1326. * Cache of new line chars.
  1327. * @memberof PIXI.TextMetrics
  1328. * @type {number[]}
  1329. * @private
  1330. */
  1331. TextMetrics._newlines = [
  1332. 0x000A,
  1333. 0x000D ];
  1334. /**
  1335. * Cache of breaking spaces.
  1336. * @memberof PIXI.TextMetrics
  1337. * @type {number[]}
  1338. * @private
  1339. */
  1340. TextMetrics._breakingSpaces = [
  1341. 0x0009,
  1342. 0x0020,
  1343. 0x2000,
  1344. 0x2001,
  1345. 0x2002,
  1346. 0x2003,
  1347. 0x2004,
  1348. 0x2005,
  1349. 0x2006,
  1350. 0x2008,
  1351. 0x2009,
  1352. 0x200A,
  1353. 0x205F,
  1354. 0x3000 ];
  1355. /**
  1356. * A number, or a string containing a number.
  1357. * @memberof PIXI
  1358. * @typedef {object} IFontMetrics
  1359. * @property {number} ascent - Font ascent
  1360. * @property {number} descent - Font descent
  1361. * @property {number} fontSize - Font size
  1362. */
  1363. var defaultDestroyOptions = {
  1364. texture: true,
  1365. children: false,
  1366. baseTexture: true,
  1367. };
  1368. /**
  1369. * A Text Object will create a line or multiple lines of text.
  1370. *
  1371. * The text is created using the [Canvas API](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API).
  1372. *
  1373. * The primary advantage of this class over BitmapText is that you have great control over the style of the text,
  1374. * which you can change at runtime.
  1375. *
  1376. * The primary disadvantages is that each piece of text has it's own texture, which can use more memory.
  1377. * When text changes, this texture has to be re-generated and re-uploaded to the GPU, taking up time.
  1378. *
  1379. * To split a line you can use '\n' in your text string, or, on the `style` object,
  1380. * change its `wordWrap` property to true and and give the `wordWrapWidth` property a value.
  1381. *
  1382. * A Text can be created directly from a string and a style object,
  1383. * which can be generated [here](https://pixijs.io/pixi-text-style).
  1384. *
  1385. * ```js
  1386. * let text = new PIXI.Text('This is a PixiJS text',{fontFamily : 'Arial', fontSize: 24, fill : 0xff1010, align : 'center'});
  1387. * ```
  1388. * @memberof PIXI
  1389. */
  1390. var Text = /** @class */ (function (_super) {
  1391. __extends(Text, _super);
  1392. /**
  1393. * @param text - The string that you would like the text to display
  1394. * @param {object|PIXI.TextStyle} [style] - The style parameters
  1395. * @param canvas - The canvas element for drawing text
  1396. */
  1397. function Text(text, style, canvas) {
  1398. var _this = this;
  1399. var ownCanvas = false;
  1400. if (!canvas) {
  1401. canvas = settings.settings.ADAPTER.createCanvas();
  1402. ownCanvas = true;
  1403. }
  1404. canvas.width = 3;
  1405. canvas.height = 3;
  1406. var texture = core.Texture.from(canvas);
  1407. texture.orig = new math.Rectangle();
  1408. texture.trim = new math.Rectangle();
  1409. _this = _super.call(this, texture) || this;
  1410. _this._ownCanvas = ownCanvas;
  1411. _this.canvas = canvas;
  1412. _this.context = canvas.getContext('2d', {
  1413. // required for trimming to work without warnings
  1414. willReadFrequently: true,
  1415. });
  1416. _this._resolution = settings.settings.RESOLUTION;
  1417. _this._autoResolution = true;
  1418. _this._text = null;
  1419. _this._style = null;
  1420. _this._styleListener = null;
  1421. _this._font = '';
  1422. _this.text = text;
  1423. _this.style = style;
  1424. _this.localStyleID = -1;
  1425. return _this;
  1426. }
  1427. /**
  1428. * Renders text to its canvas, and updates its texture.
  1429. *
  1430. * By default this is used internally to ensure the texture is correct before rendering,
  1431. * but it can be used called externally, for example from this class to 'pre-generate' the texture from a piece of text,
  1432. * and then shared across multiple Sprites.
  1433. * @param respectDirty - Whether to abort updating the text if the Text isn't dirty and the function is called.
  1434. */
  1435. Text.prototype.updateText = function (respectDirty) {
  1436. var style = this._style;
  1437. // check if style has changed..
  1438. if (this.localStyleID !== style.styleID) {
  1439. this.dirty = true;
  1440. this.localStyleID = style.styleID;
  1441. }
  1442. if (!this.dirty && respectDirty) {
  1443. return;
  1444. }
  1445. this._font = this._style.toFontString();
  1446. var context = this.context;
  1447. var measured = TextMetrics.measureText(this._text || ' ', this._style, this._style.wordWrap, this.canvas);
  1448. var width = measured.width;
  1449. var height = measured.height;
  1450. var lines = measured.lines;
  1451. var lineHeight = measured.lineHeight;
  1452. var lineWidths = measured.lineWidths;
  1453. var maxLineWidth = measured.maxLineWidth;
  1454. var fontProperties = measured.fontProperties;
  1455. this.canvas.width = Math.ceil(Math.ceil((Math.max(1, width) + (style.padding * 2))) * this._resolution);
  1456. this.canvas.height = Math.ceil(Math.ceil((Math.max(1, height) + (style.padding * 2))) * this._resolution);
  1457. context.scale(this._resolution, this._resolution);
  1458. context.clearRect(0, 0, this.canvas.width, this.canvas.height);
  1459. context.font = this._font;
  1460. context.lineWidth = style.strokeThickness;
  1461. context.textBaseline = style.textBaseline;
  1462. context.lineJoin = style.lineJoin;
  1463. context.miterLimit = style.miterLimit;
  1464. var linePositionX;
  1465. var linePositionY;
  1466. // require 2 passes if a shadow; the first to draw the drop shadow, the second to draw the text
  1467. var passesCount = style.dropShadow ? 2 : 1;
  1468. // For v4, we drew text at the colours of the drop shadow underneath the normal text. This gave the correct zIndex,
  1469. // but features such as alpha and shadowblur did not look right at all, since we were using actual text as a shadow.
  1470. //
  1471. // For v5.0.0, we moved over to just use the canvas API for drop shadows, which made them look much nicer and more
  1472. // visually please, but now because the stroke is drawn and then the fill, drop shadows would appear on both the fill
  1473. // and the stroke; and fill drop shadows would appear over the top of the stroke.
  1474. //
  1475. // For v5.1.1, the new route is to revert to v4 style of drawing text first to get the drop shadows underneath normal
  1476. // text, but instead drawing text in the correct location, we'll draw it off screen (-paddingY), and then adjust the
  1477. // drop shadow so only that appears on screen (+paddingY). Now we'll have the correct draw order of the shadow
  1478. // beneath the text, whilst also having the proper text shadow styling.
  1479. for (var i = 0; i < passesCount; ++i) {
  1480. var isShadowPass = style.dropShadow && i === 0;
  1481. // we only want the drop shadow, so put text way off-screen
  1482. var dsOffsetText = isShadowPass ? Math.ceil(Math.max(1, height) + (style.padding * 2)) : 0;
  1483. var dsOffsetShadow = dsOffsetText * this._resolution;
  1484. if (isShadowPass) {
  1485. // On Safari, text with gradient and drop shadows together do not position correctly
  1486. // if the scale of the canvas is not 1: https://bugs.webkit.org/show_bug.cgi?id=197689
  1487. // Therefore we'll set the styles to be a plain black whilst generating this drop shadow
  1488. context.fillStyle = 'black';
  1489. context.strokeStyle = 'black';
  1490. var dropShadowColor = style.dropShadowColor;
  1491. var rgb = utils.hex2rgb(typeof dropShadowColor === 'number' ? dropShadowColor : utils.string2hex(dropShadowColor));
  1492. var dropShadowBlur = style.dropShadowBlur * this._resolution;
  1493. var dropShadowDistance = style.dropShadowDistance * this._resolution;
  1494. context.shadowColor = "rgba(" + rgb[0] * 255 + "," + rgb[1] * 255 + "," + rgb[2] * 255 + "," + style.dropShadowAlpha + ")";
  1495. context.shadowBlur = dropShadowBlur;
  1496. context.shadowOffsetX = Math.cos(style.dropShadowAngle) * dropShadowDistance;
  1497. context.shadowOffsetY = (Math.sin(style.dropShadowAngle) * dropShadowDistance) + dsOffsetShadow;
  1498. }
  1499. else {
  1500. // set canvas text styles
  1501. context.fillStyle = this._generateFillStyle(style, lines, measured);
  1502. // TODO: Can't have different types for getter and setter. The getter shouldn't have the number type as
  1503. // the setter converts to string. See this thread for more details:
  1504. // https://github.com/microsoft/TypeScript/issues/2521
  1505. context.strokeStyle = style.stroke;
  1506. context.shadowColor = 'black';
  1507. context.shadowBlur = 0;
  1508. context.shadowOffsetX = 0;
  1509. context.shadowOffsetY = 0;
  1510. }
  1511. var linePositionYShift = (lineHeight - fontProperties.fontSize) / 2;
  1512. if (!Text.nextLineHeightBehavior || lineHeight - fontProperties.fontSize < 0) {
  1513. linePositionYShift = 0;
  1514. }
  1515. // draw lines line by line
  1516. for (var i_1 = 0; i_1 < lines.length; i_1++) {
  1517. linePositionX = style.strokeThickness / 2;
  1518. linePositionY = ((style.strokeThickness / 2) + (i_1 * lineHeight)) + fontProperties.ascent
  1519. + linePositionYShift;
  1520. if (style.align === 'right') {
  1521. linePositionX += maxLineWidth - lineWidths[i_1];
  1522. }
  1523. else if (style.align === 'center') {
  1524. linePositionX += (maxLineWidth - lineWidths[i_1]) / 2;
  1525. }
  1526. if (style.stroke && style.strokeThickness) {
  1527. this.drawLetterSpacing(lines[i_1], linePositionX + style.padding, linePositionY + style.padding - dsOffsetText, true);
  1528. }
  1529. if (style.fill) {
  1530. this.drawLetterSpacing(lines[i_1], linePositionX + style.padding, linePositionY + style.padding - dsOffsetText);
  1531. }
  1532. }
  1533. }
  1534. this.updateTexture();
  1535. };
  1536. /**
  1537. * Render the text with letter-spacing.
  1538. * @param text - The text to draw
  1539. * @param x - Horizontal position to draw the text
  1540. * @param y - Vertical position to draw the text
  1541. * @param isStroke - Is this drawing for the outside stroke of the
  1542. * text? If not, it's for the inside fill
  1543. */
  1544. Text.prototype.drawLetterSpacing = function (text, x, y, isStroke) {
  1545. if (isStroke === void 0) { isStroke = false; }
  1546. var style = this._style;
  1547. // letterSpacing of 0 means normal
  1548. var letterSpacing = style.letterSpacing;
  1549. // Checking that we can use moddern canvas2D api
  1550. // https://developer.chrome.com/origintrials/#/view_trial/3585991203293757441
  1551. // note: this is unstable API, Chrome less 94 use a `textLetterSpacing`, newest use a letterSpacing
  1552. // eslint-disable-next-line max-len
  1553. var supportLetterSpacing = Text.experimentalLetterSpacing
  1554. && ('letterSpacing' in CanvasRenderingContext2D.prototype
  1555. || 'textLetterSpacing' in CanvasRenderingContext2D.prototype);
  1556. if (letterSpacing === 0 || supportLetterSpacing) {
  1557. if (supportLetterSpacing) {
  1558. this.context.letterSpacing = letterSpacing;
  1559. this.context.textLetterSpacing = letterSpacing;
  1560. }
  1561. if (isStroke) {
  1562. this.context.strokeText(text, x, y);
  1563. }
  1564. else {
  1565. this.context.fillText(text, x, y);
  1566. }
  1567. return;
  1568. }
  1569. var currentPosition = x;
  1570. // Using Array.from correctly splits characters whilst keeping emoji together.
  1571. // This is not supported on IE as it requires ES6, so regular text splitting occurs.
  1572. // This also doesn't account for emoji that are multiple emoji put together to make something else.
  1573. // Handling all of this would require a big library itself.
  1574. // https://medium.com/@giltayar/iterating-over-emoji-characters-the-es6-way-f06e4589516
  1575. // https://github.com/orling/grapheme-splitter
  1576. var stringArray = Array.from ? Array.from(text) : text.split('');
  1577. var previousWidth = this.context.measureText(text).width;
  1578. var currentWidth = 0;
  1579. for (var i = 0; i < stringArray.length; ++i) {
  1580. var currentChar = stringArray[i];
  1581. if (isStroke) {
  1582. this.context.strokeText(currentChar, currentPosition, y);
  1583. }
  1584. else {
  1585. this.context.fillText(currentChar, currentPosition, y);
  1586. }
  1587. var textStr = '';
  1588. for (var j = i + 1; j < stringArray.length; ++j) {
  1589. textStr += stringArray[j];
  1590. }
  1591. currentWidth = this.context.measureText(textStr).width;
  1592. currentPosition += previousWidth - currentWidth + letterSpacing;
  1593. previousWidth = currentWidth;
  1594. }
  1595. };
  1596. /** Updates texture size based on canvas size. */
  1597. Text.prototype.updateTexture = function () {
  1598. var canvas = this.canvas;
  1599. if (this._style.trim) {
  1600. var trimmed = utils.trimCanvas(canvas);
  1601. if (trimmed.data) {
  1602. canvas.width = trimmed.width;
  1603. canvas.height = trimmed.height;
  1604. this.context.putImageData(trimmed.data, 0, 0);
  1605. }
  1606. }
  1607. var texture = this._texture;
  1608. var style = this._style;
  1609. var padding = style.trim ? 0 : style.padding;
  1610. var baseTexture = texture.baseTexture;
  1611. texture.trim.width = texture._frame.width = canvas.width / this._resolution;
  1612. texture.trim.height = texture._frame.height = canvas.height / this._resolution;
  1613. texture.trim.x = -padding;
  1614. texture.trim.y = -padding;
  1615. texture.orig.width = texture._frame.width - (padding * 2);
  1616. texture.orig.height = texture._frame.height - (padding * 2);
  1617. // call sprite onTextureUpdate to update scale if _width or _height were set
  1618. this._onTextureUpdate();
  1619. baseTexture.setRealSize(canvas.width, canvas.height, this._resolution);
  1620. texture.updateUvs();
  1621. this.dirty = false;
  1622. };
  1623. /**
  1624. * Renders the object using the WebGL renderer
  1625. * @param renderer - The renderer
  1626. */
  1627. Text.prototype._render = function (renderer) {
  1628. if (this._autoResolution && this._resolution !== renderer.resolution) {
  1629. this._resolution = renderer.resolution;
  1630. this.dirty = true;
  1631. }
  1632. this.updateText(true);
  1633. _super.prototype._render.call(this, renderer);
  1634. };
  1635. /** Updates the transform on all children of this container for rendering. */
  1636. Text.prototype.updateTransform = function () {
  1637. this.updateText(true);
  1638. _super.prototype.updateTransform.call(this);
  1639. };
  1640. Text.prototype.getBounds = function (skipUpdate, rect) {
  1641. this.updateText(true);
  1642. if (this._textureID === -1) {
  1643. // texture was updated: recalculate transforms
  1644. skipUpdate = false;
  1645. }
  1646. return _super.prototype.getBounds.call(this, skipUpdate, rect);
  1647. };
  1648. /**
  1649. * Gets the local bounds of the text object.
  1650. * @param rect - The output rectangle.
  1651. * @returns The bounds.
  1652. */
  1653. Text.prototype.getLocalBounds = function (rect) {
  1654. this.updateText(true);
  1655. return _super.prototype.getLocalBounds.call(this, rect);
  1656. };
  1657. /** Calculates the bounds of the Text as a rectangle. The bounds calculation takes the worldTransform into account. */
  1658. Text.prototype._calculateBounds = function () {
  1659. this.calculateVertices();
  1660. // if we have already done this on THIS frame.
  1661. this._bounds.addQuad(this.vertexData);
  1662. };
  1663. /**
  1664. * Generates the fill style. Can automatically generate a gradient based on the fill style being an array
  1665. * @param style - The style.
  1666. * @param lines - The lines of text.
  1667. * @param metrics
  1668. * @returns The fill style
  1669. */
  1670. Text.prototype._generateFillStyle = function (style, lines, metrics) {
  1671. // TODO: Can't have different types for getter and setter. The getter shouldn't have the number type as
  1672. // the setter converts to string. See this thread for more details:
  1673. // https://github.com/microsoft/TypeScript/issues/2521
  1674. var fillStyle = style.fill;
  1675. if (!Array.isArray(fillStyle)) {
  1676. return fillStyle;
  1677. }
  1678. else if (fillStyle.length === 1) {
  1679. return fillStyle[0];
  1680. }
  1681. // the gradient will be evenly spaced out according to how large the array is.
  1682. // ['#FF0000', '#00FF00', '#0000FF'] would created stops at 0.25, 0.5 and 0.75
  1683. var gradient;
  1684. // a dropshadow will enlarge the canvas and result in the gradient being
  1685. // generated with the incorrect dimensions
  1686. var dropShadowCorrection = (style.dropShadow) ? style.dropShadowDistance : 0;
  1687. // should also take padding into account, padding can offset the gradient
  1688. var padding = style.padding || 0;
  1689. var width = (this.canvas.width / this._resolution) - dropShadowCorrection - (padding * 2);
  1690. var height = (this.canvas.height / this._resolution) - dropShadowCorrection - (padding * 2);
  1691. // make a copy of the style settings, so we can manipulate them later
  1692. var fill = fillStyle.slice();
  1693. var fillGradientStops = style.fillGradientStops.slice();
  1694. // wanting to evenly distribute the fills. So an array of 4 colours should give fills of 0.25, 0.5 and 0.75
  1695. if (!fillGradientStops.length) {
  1696. var lengthPlus1 = fill.length + 1;
  1697. for (var i = 1; i < lengthPlus1; ++i) {
  1698. fillGradientStops.push(i / lengthPlus1);
  1699. }
  1700. }
  1701. // stop the bleeding of the last gradient on the line above to the top gradient of the this line
  1702. // by hard defining the first gradient colour at point 0, and last gradient colour at point 1
  1703. fill.unshift(fillStyle[0]);
  1704. fillGradientStops.unshift(0);
  1705. fill.push(fillStyle[fillStyle.length - 1]);
  1706. fillGradientStops.push(1);
  1707. if (style.fillGradientType === exports.TEXT_GRADIENT.LINEAR_VERTICAL) {
  1708. // start the gradient at the top center of the canvas, and end at the bottom middle of the canvas
  1709. gradient = this.context.createLinearGradient(width / 2, padding, width / 2, height + padding);
  1710. // we need to repeat the gradient so that each individual line of text has the same vertical gradient effect
  1711. // ['#FF0000', '#00FF00', '#0000FF'] over 2 lines would create stops at 0.125, 0.25, 0.375, 0.625, 0.75, 0.875
  1712. // Actual height of the text itself, not counting spacing for lineHeight/leading/dropShadow etc
  1713. var textHeight = metrics.fontProperties.fontSize + style.strokeThickness;
  1714. for (var i = 0; i < lines.length; i++) {
  1715. var lastLineBottom = (metrics.lineHeight * (i - 1)) + textHeight;
  1716. var thisLineTop = metrics.lineHeight * i;
  1717. var thisLineGradientStart = thisLineTop;
  1718. // Handle case where last & this line overlap
  1719. if (i > 0 && lastLineBottom > thisLineTop) {
  1720. thisLineGradientStart = (thisLineTop + lastLineBottom) / 2;
  1721. }
  1722. var thisLineBottom = thisLineTop + textHeight;
  1723. var nextLineTop = metrics.lineHeight * (i + 1);
  1724. var thisLineGradientEnd = thisLineBottom;
  1725. // Handle case where this & next line overlap
  1726. if (i + 1 < lines.length && nextLineTop < thisLineBottom) {
  1727. thisLineGradientEnd = (thisLineBottom + nextLineTop) / 2;
  1728. }
  1729. // textHeight, but as a 0-1 size in global gradient stop space
  1730. var gradStopLineHeight = (thisLineGradientEnd - thisLineGradientStart) / height;
  1731. for (var j = 0; j < fill.length; j++) {
  1732. // 0-1 stop point for the current line, multiplied to global space afterwards
  1733. var lineStop = 0;
  1734. if (typeof fillGradientStops[j] === 'number') {
  1735. lineStop = fillGradientStops[j];
  1736. }
  1737. else {
  1738. lineStop = j / fill.length;
  1739. }
  1740. var globalStop = Math.min(1, Math.max(0, (thisLineGradientStart / height) + (lineStop * gradStopLineHeight)));
  1741. // There's potential for floating point precision issues at the seams between gradient repeats.
  1742. globalStop = Number(globalStop.toFixed(5));
  1743. gradient.addColorStop(globalStop, fill[j]);
  1744. }
  1745. }
  1746. }
  1747. else {
  1748. // start the gradient at the center left of the canvas, and end at the center right of the canvas
  1749. gradient = this.context.createLinearGradient(padding, height / 2, width + padding, height / 2);
  1750. // can just evenly space out the gradients in this case, as multiple lines makes no difference
  1751. // to an even left to right gradient
  1752. var totalIterations = fill.length + 1;
  1753. var currentIteration = 1;
  1754. for (var i = 0; i < fill.length; i++) {
  1755. var stop = void 0;
  1756. if (typeof fillGradientStops[i] === 'number') {
  1757. stop = fillGradientStops[i];
  1758. }
  1759. else {
  1760. stop = currentIteration / totalIterations;
  1761. }
  1762. gradient.addColorStop(stop, fill[i]);
  1763. currentIteration++;
  1764. }
  1765. }
  1766. return gradient;
  1767. };
  1768. /**
  1769. * Destroys this text object.
  1770. *
  1771. * Note* Unlike a Sprite, a Text object will automatically destroy its baseTexture and texture as
  1772. * the majority of the time the texture will not be shared with any other Sprites.
  1773. * @param options - Options parameter. A boolean will act as if all options
  1774. * have been set to that value
  1775. * @param {boolean} [options.children=false] - if set to true, all the children will have their
  1776. * destroy method called as well. 'options' will be passed on to those calls.
  1777. * @param {boolean} [options.texture=true] - Should it destroy the current texture of the sprite as well
  1778. * @param {boolean} [options.baseTexture=true] - Should it destroy the base texture of the sprite as well
  1779. */
  1780. Text.prototype.destroy = function (options) {
  1781. if (typeof options === 'boolean') {
  1782. options = { children: options };
  1783. }
  1784. options = Object.assign({}, defaultDestroyOptions, options);
  1785. _super.prototype.destroy.call(this, options);
  1786. // set canvas width and height to 0 to workaround memory leak in Safari < 13
  1787. // https://stackoverflow.com/questions/52532614/total-canvas-memory-use-exceeds-the-maximum-limit-safari-12
  1788. if (this._ownCanvas) {
  1789. this.canvas.height = this.canvas.width = 0;
  1790. }
  1791. // make sure to reset the context and canvas.. dont want this hanging around in memory!
  1792. this.context = null;
  1793. this.canvas = null;
  1794. this._style = null;
  1795. };
  1796. Object.defineProperty(Text.prototype, "width", {
  1797. /** The width of the Text, setting this will actually modify the scale to achieve the value set. */
  1798. get: function () {
  1799. this.updateText(true);
  1800. return Math.abs(this.scale.x) * this._texture.orig.width;
  1801. },
  1802. set: function (value) {
  1803. this.updateText(true);
  1804. var s = utils.sign(this.scale.x) || 1;
  1805. this.scale.x = s * value / this._texture.orig.width;
  1806. this._width = value;
  1807. },
  1808. enumerable: false,
  1809. configurable: true
  1810. });
  1811. Object.defineProperty(Text.prototype, "height", {
  1812. /** The height of the Text, setting this will actually modify the scale to achieve the value set. */
  1813. get: function () {
  1814. this.updateText(true);
  1815. return Math.abs(this.scale.y) * this._texture.orig.height;
  1816. },
  1817. set: function (value) {
  1818. this.updateText(true);
  1819. var s = utils.sign(this.scale.y) || 1;
  1820. this.scale.y = s * value / this._texture.orig.height;
  1821. this._height = value;
  1822. },
  1823. enumerable: false,
  1824. configurable: true
  1825. });
  1826. Object.defineProperty(Text.prototype, "style", {
  1827. /**
  1828. * Set the style of the text.
  1829. *
  1830. * Set up an event listener to listen for changes on the style object and mark the text as dirty.
  1831. */
  1832. get: function () {
  1833. // TODO: Can't have different types for getter and setter. The getter shouldn't have the ITextStyle
  1834. // since the setter creates the TextStyle. See this thread for more details:
  1835. // https://github.com/microsoft/TypeScript/issues/2521
  1836. return this._style;
  1837. },
  1838. set: function (style) {
  1839. style = style || {};
  1840. if (style instanceof TextStyle) {
  1841. this._style = style;
  1842. }
  1843. else {
  1844. this._style = new TextStyle(style);
  1845. }
  1846. this.localStyleID = -1;
  1847. this.dirty = true;
  1848. },
  1849. enumerable: false,
  1850. configurable: true
  1851. });
  1852. Object.defineProperty(Text.prototype, "text", {
  1853. /** Set the copy for the text object. To split a line you can use '\n'. */
  1854. get: function () {
  1855. return this._text;
  1856. },
  1857. set: function (text) {
  1858. text = String(text === null || text === undefined ? '' : text);
  1859. if (this._text === text) {
  1860. return;
  1861. }
  1862. this._text = text;
  1863. this.dirty = true;
  1864. },
  1865. enumerable: false,
  1866. configurable: true
  1867. });
  1868. Object.defineProperty(Text.prototype, "resolution", {
  1869. /**
  1870. * The resolution / device pixel ratio of the canvas.
  1871. *
  1872. * This is set to automatically match the renderer resolution by default, but can be overridden by setting manually.
  1873. * @default 1
  1874. */
  1875. get: function () {
  1876. return this._resolution;
  1877. },
  1878. set: function (value) {
  1879. this._autoResolution = false;
  1880. if (this._resolution === value) {
  1881. return;
  1882. }
  1883. this._resolution = value;
  1884. this.dirty = true;
  1885. },
  1886. enumerable: false,
  1887. configurable: true
  1888. });
  1889. /**
  1890. * New behavior for `lineHeight` that's meant to mimic HTML text. A value of `true` will
  1891. * make sure the first baseline is offset by the `lineHeight` value if it is greater than `fontSize`.
  1892. * A value of `false` will use the legacy behavior and not change the baseline of the first line.
  1893. * In the next major release, we'll enable this by default.
  1894. */
  1895. Text.nextLineHeightBehavior = false;
  1896. /**
  1897. * New rendering behavior for letter-spacing which uses Chrome's new native API. This will
  1898. * lead to more accurate letter-spacing results because it does not try to manually draw
  1899. * each character. However, this Chrome API is experimental and may not serve all cases yet.
  1900. */
  1901. Text.experimentalLetterSpacing = false;
  1902. return Text;
  1903. }(sprite.Sprite));
  1904. exports.Text = Text;
  1905. exports.TextMetrics = TextMetrics;
  1906. exports.TextStyle = TextStyle;
  1907. //# sourceMappingURL=text.js.map