text.js 93 KB

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