text.mjs 76 KB

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