1 /**************************************************************************** 2 Copyright (c) 2008-2010 Ricardo Quesada 3 Copyright (c) 2011-2012 cocos2d-x.org 4 Copyright (c) 2013-2014 Chukong Technologies Inc. 5 6 http://www.cocos2d-x.org 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy 9 of this software and associated documentation files (the "Software"), to deal 10 in the Software without restriction, including without limitation the rights 11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 copies of the Software, and to permit persons to whom the Software is 13 furnished to do so, subject to the following conditions: 14 15 The above copyright notice and this permission notice shall be included in 16 all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 THE SOFTWARE. 25 26 Use any of these editors to generate BMFonts: 27 http://glyphdesigner.71squared.com/ (Commercial, Mac OS X) 28 http://www.n4te.com/hiero/hiero.jnlp (Free, Java) 29 http://slick.cokeandcode.com/demos/hiero.jnlp (Free, Java) 30 http://www.angelcode.com/products/bmfont/ (Free, Windows only) 31 ****************************************************************************/ 32 /** 33 * @constant 34 * @type Number 35 */ 36 cc.LABEL_AUTOMATIC_WIDTH = -1; 37 38 /** 39 * <p>cc.LabelBMFont is a subclass of cc.SpriteBatchNode.</p> 40 * 41 * <p>Features:<br/> 42 * <ul><li>- Treats each character like a cc.Sprite. This means that each individual character can be:</li> 43 * <li>- rotated</li> 44 * <li>- scaled</li> 45 * <li>- translated</li> 46 * <li>- tinted</li> 47 * <li>- change the opacity</li> 48 * <li>- It can be used as part of a menu item.</li> 49 * <li>- anchorPoint can be used to align the "label"</li> 50 * <li>- Supports AngelCode text format</li></ul></p> 51 * 52 * <p>Limitations:<br/> 53 * - All inner characters are using an anchorPoint of (0.5, 0.5) and it is not recommend to change it 54 * because it might affect the rendering</p> 55 * 56 * <p>cc.LabelBMFont implements the protocol cc.LabelProtocol, like cc.Label and cc.LabelAtlas.<br/> 57 * cc.LabelBMFont has the flexibility of cc.Label, the speed of cc.LabelAtlas and all the features of cc.Sprite.<br/> 58 * If in doubt, use cc.LabelBMFont instead of cc.LabelAtlas / cc.Label.</p> 59 * 60 * <p>Supported editors:<br/> 61 * http://glyphdesigner.71squared.com/ (Commercial, Mac OS X)<br/> 62 * http://www.n4te.com/hiero/hiero.jnlp (Free, Java)<br/> 63 * http://slick.cokeandcode.com/demos/hiero.jnlp (Free, Java)<br/> 64 * http://www.angelcode.com/products/bmfont/ (Free, Windows only)</p> 65 * @class 66 * @extends cc.SpriteBatchNode 67 * 68 * @property {String} string - Content string of label 69 * @property {Number} textAlign - Horizontal Alignment of label, cc.TEXT_ALIGNMENT_LEFT|cc.TEXT_ALIGNMENT_CENTER|cc.TEXT_ALIGNMENT_RIGHT 70 * @property {Number} boundingWidth - Width of the bounding box of label, the real content width is limited by boundingWidth 71 * 72 * @param {String} str 73 * @param {String} fntFile 74 * @param {Number} [width=-1] 75 * @param {Number} [alignment=cc.TEXT_ALIGNMENT_LEFT] 76 * @param {cc.Point} [imageOffset=cc.p(0,0)] 77 * 78 * @example 79 * // Example 01 80 * var label1 = new cc.LabelBMFont("Test case", "test.fnt"); 81 * 82 * // Example 02 83 * var label2 = new cc.LabelBMFont("test case", "test.fnt", 200, cc.TEXT_ALIGNMENT_LEFT); 84 * 85 * // Example 03 86 * var label3 = new cc.LabelBMFont("This is a \n test case", "test.fnt", 200, cc.TEXT_ALIGNMENT_LEFT, cc.p(0,0)); 87 */ 88 cc.LabelBMFont = cc.SpriteBatchNode.extend(/** @lends cc.LabelBMFont# */{ 89 //property string is Getter and Setter. 90 //property textAlign is Getter and Setter. 91 //property boundingWidth is Getter and Setter. 92 _opacityModifyRGB: false, 93 94 _string: "", 95 _config: null, 96 97 // name of fntFile 98 _fntFile: "", 99 100 // initial string without line breaks 101 _initialString: "", 102 103 // alignment of all lines 104 _alignment: cc.TEXT_ALIGNMENT_CENTER, 105 106 // max width until a line break is added 107 _width: -1, 108 _lineBreakWithoutSpaces: false, 109 _imageOffset: null, 110 111 _reusedChar: null, 112 113 _textureLoaded: false, 114 _className: "LabelBMFont", 115 116 _createRenderCmd: function(){ 117 if(cc._renderType === cc._RENDER_TYPE_WEBGL) 118 return new cc.LabelBMFont.WebGLRenderCmd(this); 119 else 120 return new cc.LabelBMFont.CanvasRenderCmd(this); 121 }, 122 123 _setString: function (newString, needUpdateLabel) { 124 if (!needUpdateLabel) { 125 this._string = newString; 126 } else { 127 this._initialString = newString; 128 } 129 var locChildren = this._children; 130 if (locChildren) { 131 for (var i = 0; i < locChildren.length; i++) { 132 var selNode = locChildren[i]; 133 if (selNode) 134 selNode.setVisible(false); 135 } 136 } 137 if (this._textureLoaded) { 138 this.createFontChars(); 139 if (needUpdateLabel) 140 this.updateLabel(); 141 } 142 }, 143 144 /** 145 * Constructor function, override it to extend the construction behavior, remember to call "this._super()" in the extended "ctor" function. <br /> 146 * creates a bitmap font atlas with an initial string and the FNT file. 147 * @param {String} str 148 * @param {String} fntFile 149 * @param {Number} [width=-1] 150 * @param {Number} [alignment=cc.TEXT_ALIGNMENT_LEFT] 151 * @param {cc.Point} [imageOffset=cc.p(0,0)] 152 */ 153 ctor: function (str, fntFile, width, alignment, imageOffset) { 154 cc.SpriteBatchNode.prototype.ctor.call(this); 155 this._imageOffset = cc.p(0, 0); 156 this._reusedChar = []; 157 this._cascadeColorEnabled = true; 158 this._cascadeOpacityEnabled = true; 159 this.initWithString(str, fntFile, width, alignment, imageOffset); 160 }, 161 162 /** 163 * return texture is loaded 164 * @returns {boolean} 165 */ 166 textureLoaded: function () { 167 return this._textureLoaded; 168 }, 169 170 /** 171 * add texture loaded event listener. <br /> 172 * Will execute the callback in the loaded. 173 * @param {Function} callback 174 * @param {Object} target 175 * @deprecated since 3.1, please use addEventListener instead 176 */ 177 addLoadedEventListener: function (callback, target) { 178 this.addEventListener("load", callback, target); 179 }, 180 181 /** 182 * Conforms to cc.RGBAProtocol protocol. 183 * @return {Boolean} 184 */ 185 isOpacityModifyRGB: function () { 186 return this._opacityModifyRGB; 187 }, 188 189 /** 190 * Set whether to support cc.RGBAProtocol protocol 191 * @param {Boolean} opacityModifyRGB 192 */ 193 setOpacityModifyRGB: function (opacityModifyRGB) { 194 this._opacityModifyRGB = opacityModifyRGB; 195 var locChildren = this._children; 196 if (locChildren) { 197 for (var i = 0; i < locChildren.length; i++) { 198 var node = locChildren[i]; 199 if (node) 200 node.opacityModifyRGB = this._opacityModifyRGB; 201 } 202 } 203 }, 204 205 _changeTextureColor: function () { 206 this._renderCmd._changeTextureColor(); 207 }, 208 209 /** 210 * Initialization of the node, please do not call this function by yourself, you should pass the parameters to constructor to initialize it
. 211 */ 212 init: function () { 213 return this.initWithString(null, null, null, null, null); 214 }, 215 216 /** 217 * init a bitmap font atlas with an initial string and the FNT file 218 * @param {String} str 219 * @param {String} fntFile 220 * @param {Number} [width=-1] 221 * @param {Number} [alignment=cc.TEXT_ALIGNMENT_LEFT] 222 * @param {cc.Point} [imageOffset=cc.p(0,0)] 223 * @return {Boolean} 224 */ 225 initWithString: function (str, fntFile, width, alignment, imageOffset) { 226 var self = this, theString = str || ""; 227 var cmd = this._renderCmd; 228 229 if (self._config) 230 cc.log("cc.LabelBMFont.initWithString(): re-init is no longer supported"); 231 232 var texture; 233 if (fntFile) { 234 var newConf = cc.loader.getRes(fntFile); 235 if (!newConf) { 236 cc.log("cc.LabelBMFont.initWithString(): Impossible to create font. Please check file"); 237 return false; 238 } 239 240 self._config = newConf; 241 self._fntFile = fntFile; 242 texture = cc.textureCache.addImage(newConf.atlasName); 243 var locIsLoaded = texture.isLoaded(); 244 self._textureLoaded = locIsLoaded; 245 if (!locIsLoaded) { 246 texture.addEventListener("load", function (sender) { 247 var self1 = this; 248 self1._textureLoaded = true; 249 //reset the LabelBMFont 250 self1.initWithTexture(sender, self1._initialString.length); 251 self1.setString(self1._initialString, true); 252 self1.dispatchEvent("load"); 253 }, self); 254 } 255 } else { 256 texture = new cc.Texture2D(); 257 var image = new Image(); 258 texture.initWithElement(image); 259 self._textureLoaded = false; 260 } 261 262 if (self.initWithTexture(texture, theString.length)) { 263 self._alignment = alignment || cc.TEXT_ALIGNMENT_LEFT; 264 self._imageOffset = imageOffset || cc.p(0, 0); 265 self._width = (width == null) ? -1 : width; 266 267 self._realOpacity = 255; 268 self._realColor = cc.color(255, 255, 255, 255); 269 270 self._contentSize.width = 0; 271 self._contentSize.height = 0; 272 273 self.setAnchorPoint(0.5, 0.5); 274 275 this._renderCmd._initBatchTexture(); 276 277 self.setString(theString, true); 278 return true; 279 } 280 return false; 281 }, 282 283 /** 284 * updates the font chars based on the string to render 285 */ 286 createFontChars: function () { 287 var self = this; 288 var cmd = this._renderCmd; 289 var locTexture = cmd._texture || self.textureAtlas.texture; 290 291 var nextFontPositionX = 0; 292 293 var tmpSize = cc.size(0, 0); 294 295 var longestLine = 0; 296 297 var quantityOfLines = 1; 298 299 var locStr = self._string; 300 var stringLen = locStr ? locStr.length : 0; 301 302 if (stringLen === 0) 303 return; 304 305 var i, locCfg = self._config, locKerningDict = locCfg.kerningDict, 306 locCommonH = locCfg.commonHeight, locFontDict = locCfg.fontDefDictionary; 307 for (i = 0; i < stringLen - 1; i++) { 308 if (locStr.charCodeAt(i) === 10) quantityOfLines++; 309 } 310 311 var totalHeight = locCommonH * quantityOfLines; 312 var nextFontPositionY = -(locCommonH - locCommonH * quantityOfLines); 313 314 var prev = -1; 315 for (i = 0; i < stringLen; i++) { 316 var key = locStr.charCodeAt(i); 317 if (key === 0) continue; 318 319 if (key === 10) { 320 //new line 321 nextFontPositionX = 0; 322 nextFontPositionY -= locCfg.commonHeight; 323 continue; 324 } 325 326 var kerningAmount = locKerningDict[(prev << 16) | (key & 0xffff)] || 0; 327 var fontDef = locFontDict[key]; 328 if (!fontDef) { 329 cc.log("cocos2d: LabelBMFont: character not found " + locStr[i]); 330 331 fontDef = { 332 rect: { 333 x: 0, 334 y: 0, 335 width: 0, 336 height: 0 337 }, 338 xOffset: 0, 339 yOffset: 0, 340 xAdvance: 0 341 }; 342 } 343 344 var rect = cc.rect(fontDef.rect.x, fontDef.rect.y, fontDef.rect.width, fontDef.rect.height); 345 rect = cc.rectPixelsToPoints(rect); 346 rect.x += self._imageOffset.x; 347 rect.y += self._imageOffset.y; 348 349 var fontChar = self.getChildByTag(i); 350 351 if(!fontChar){ 352 fontChar = new cc.Sprite(); 353 fontChar.initWithTexture(locTexture, rect, false); 354 fontChar._newTextureWhenChangeColor = true; 355 this.addChild(fontChar, 0, i); 356 }else{ 357 this._renderCmd._updateCharTexture(fontChar, rect, key); 358 } 359 360 // Apply label properties 361 fontChar.opacityModifyRGB = this._opacityModifyRGB; 362 this._renderCmd._updateCharColorAndOpacity(fontChar); 363 364 var yOffset = locCfg.commonHeight - fontDef.yOffset; 365 var fontPos = cc.p(nextFontPositionX + fontDef.xOffset + fontDef.rect.width * 0.5 + kerningAmount, 366 nextFontPositionY + yOffset - rect.height * 0.5 * cc.contentScaleFactor()); 367 fontChar.setPosition(cc.pointPixelsToPoints(fontPos)); 368 369 // update kerning 370 nextFontPositionX += fontDef.xAdvance + kerningAmount; 371 prev = key; 372 373 if (longestLine < nextFontPositionX) 374 longestLine = nextFontPositionX; 375 } 376 377 //If the last character processed has an xAdvance which is less that the width of the characters image, then we need 378 // to adjust the width of the string to take this into account, or the character will overlap the end of the bounding box 379 if(fontDef && fontDef.xAdvance < fontDef.rect.width) 380 tmpSize.width = longestLine - fontDef.xAdvance + fontDef.rect.width; 381 else 382 tmpSize.width = longestLine; 383 tmpSize.height = totalHeight; 384 self.setContentSize(cc.sizePixelsToPoints(tmpSize)); 385 }, 386 387 /** 388 * Update String. <br /> 389 * Only update this label display string. 390 * @param {Boolean} fromUpdate 391 */ 392 updateString: function (fromUpdate) { 393 var self = this; 394 var locChildren = self._children; 395 if (locChildren) { 396 for (var i = 0, li = locChildren.length; i < li; i++) { 397 var node = locChildren[i]; 398 if (node) node.visible = false; 399 } 400 } 401 if (self._config) 402 self.createFontChars(); 403 404 if (!fromUpdate) 405 self.updateLabel(); 406 }, 407 408 /** 409 * Gets the text of this label 410 * @return {String} 411 */ 412 getString: function () { 413 return this._initialString; 414 }, 415 416 /** 417 * Set the text 418 * @param {String} newString 419 * @param {Boolean|null} needUpdateLabel 420 */ 421 setString: function (newString, needUpdateLabel) { 422 newString = String(newString); 423 if (needUpdateLabel == null) 424 needUpdateLabel = true; 425 if (newString == null || !cc.isString(newString)) 426 newString = newString + ""; 427 428 this._initialString = newString; 429 this._setString(newString, needUpdateLabel); 430 }, 431 432 _setStringForSetter: function (newString) { 433 this.setString(newString, false); 434 }, 435 436 /** 437 * Set the text. <br /> 438 * Change this Label display string. 439 * @deprecated since v3.0 please use .setString 440 * @param label 441 */ 442 setCString: function (label) { 443 this.setString(label, true); 444 }, 445 446 // calc the text all with in a line 447 _getCharsWidth:function (startIndex, endIndex) { 448 if (endIndex <= 0) 449 { 450 return 0; 451 } 452 var curTextFirstSprite = this.getChildByTag(startIndex); 453 var curTextLastSprite = this.getChildByTag(startIndex + endIndex); 454 return this._getLetterPosXLeft(curTextLastSprite) - this._getLetterPosXLeft(curTextFirstSprite); 455 }, 456 457 _checkWarp:function (strArr, i, maxWidth, initStringWrapNum) { 458 var self = this; 459 var text = strArr[i]; 460 var curLength = 0; 461 for (var strArrIndex = 0; strArrIndex < i; strArrIndex++) 462 { 463 curLength += strArr[strArrIndex].length; 464 } 465 466 curLength = curLength + i - initStringWrapNum; // add the wrap line num 467 468 var allWidth = self._getCharsWidth(curLength, strArr[i].length - 1); 469 470 if (allWidth > maxWidth && text.length > 1) { 471 var fuzzyLen = text.length * ( maxWidth / allWidth ) | 0; 472 var tmpText = text.substr(fuzzyLen); 473 var width = allWidth - this._getCharsWidth(curLength + fuzzyLen, tmpText.length - 1); 474 var sLine; 475 var pushNum = 0; 476 477 //Increased while cycle maximum ceiling. default 100 time 478 var checkWhile = 0; 479 480 //Exceeded the size 481 while (width > maxWidth && checkWhile++ < 100) { 482 fuzzyLen *= maxWidth / width; 483 fuzzyLen = fuzzyLen | 0; 484 tmpText = text.substr(fuzzyLen); 485 width = allWidth - this._getCharsWidth(curLength + fuzzyLen, tmpText.length - 1); 486 } 487 488 checkWhile = 0; 489 490 //Find the truncation point 491 while (width < maxWidth && checkWhile++ < 100) { 492 if (tmpText) { 493 var exec = cc.LabelTTF._wordRex.exec(tmpText); 494 pushNum = exec ? exec[0].length : 1; 495 sLine = tmpText; 496 } 497 if (self._lineBreakWithoutSpaces) { 498 pushNum = 0; 499 } 500 fuzzyLen = fuzzyLen + pushNum; 501 tmpText = text.substr(fuzzyLen); 502 width = allWidth - this._getCharsWidth(curLength + fuzzyLen, tmpText.length - 1); 503 } 504 505 fuzzyLen -= pushNum; 506 if (fuzzyLen === 0) { 507 fuzzyLen = 1; 508 sLine = sLine.substr(1); 509 } 510 511 var sText = text.substr(0, fuzzyLen), result; 512 513 //symbol in the first 514 if (cc.LabelTTF.wrapInspection) { 515 if (cc.LabelTTF._symbolRex.test(sLine || tmpText)) { 516 result = cc.LabelTTF._lastWordRex.exec(sText); 517 pushNum = result ? result[0].length : 0; 518 if (self._lineBreakWithoutSpaces) { 519 pushNum = 0; 520 } 521 fuzzyLen -= pushNum; 522 523 sLine = text.substr(fuzzyLen); 524 sText = text.substr(0, fuzzyLen); 525 } 526 } 527 528 //To judge whether a English words are truncated 529 if (cc.LabelTTF._firsrEnglish.test(sLine)) { 530 result = cc.LabelTTF._lastEnglish.exec(sText); 531 if (result && sText !== result[0]) { 532 pushNum = result[0].length; 533 if (self._lineBreakWithoutSpaces) { 534 pushNum = 0; 535 } 536 fuzzyLen -= pushNum; 537 sLine = text.substr(fuzzyLen); 538 sText = text.substr(0, fuzzyLen); 539 } 540 } 541 strArr[i] = sLine || tmpText; 542 strArr.splice(i, 0, sText); 543 } 544 }, 545 546 /** 547 * Update Label. <br /> 548 * Update this Label display string and more... 549 */ 550 updateLabel: function () { 551 var self = this; 552 self.string = self._initialString; 553 var i, j, characterSprite; 554 // process string 555 // Step 1: Make multiline 556 if (self._width > 0) { 557 var stringArr = self.string.split('\n'); 558 var wrapString = ""; 559 var newWrapNum = 0; 560 var oldArrLength = 0; 561 for (i = 0; i < stringArr.length; i++) { 562 oldArrLength = stringArr.length; 563 this._checkWarp(stringArr, i, self._width * this._scaleX, newWrapNum); 564 if (oldArrLength < stringArr.length) { 565 newWrapNum++; 566 } 567 if (i > 0) 568 { 569 wrapString += "\n"; 570 } 571 wrapString += stringArr[i]; 572 } 573 wrapString = wrapString + String.fromCharCode(0); 574 self._setString(wrapString, false); 575 } 576 577 // Step 2: Make alignment 578 if (self._alignment !== cc.TEXT_ALIGNMENT_LEFT) { 579 i = 0; 580 581 var lineNumber = 0; 582 var strlen = self._string.length; 583 var last_line = []; 584 585 for (var ctr = 0; ctr < strlen; ctr++) { 586 if (self._string[ctr].charCodeAt(0) === 10 || self._string[ctr].charCodeAt(0) === 0) { 587 var lineWidth = 0; 588 var line_length = last_line.length; 589 // if last line is empty we must just increase lineNumber and work with next line 590 if (line_length === 0) { 591 lineNumber++; 592 continue; 593 } 594 var index = i + line_length - 1 + lineNumber; 595 if (index < 0) continue; 596 597 var lastChar = self.getChildByTag(index); 598 if (lastChar == null) 599 continue; 600 lineWidth = lastChar.getPositionX() + lastChar._getWidth() / 2; 601 602 var shift = 0; 603 switch (self._alignment) { 604 case cc.TEXT_ALIGNMENT_CENTER: 605 shift = self.width / 2 - lineWidth / 2; 606 break; 607 case cc.TEXT_ALIGNMENT_RIGHT: 608 shift = self.width - lineWidth; 609 break; 610 default: 611 break; 612 } 613 614 if (shift !== 0) { 615 for (j = 0; j < line_length; j++) { 616 index = i + j + lineNumber; 617 if (index < 0) continue; 618 characterSprite = self.getChildByTag(index); 619 if (characterSprite) 620 characterSprite.x += shift; 621 } 622 } 623 624 i += line_length; 625 lineNumber++; 626 627 last_line.length = 0; 628 continue; 629 } 630 last_line.push(self._string[i]); 631 } 632 } 633 }, 634 635 /** 636 * Set text alignment. 637 * @param {Number} alignment 638 */ 639 setAlignment: function (alignment) { 640 this._alignment = alignment; 641 this.updateLabel(); 642 }, 643 644 _getAlignment: function () { 645 return this._alignment; 646 }, 647 648 /** 649 * Set the bounding width. <br /> 650 * max with display width. The exceeding string will be wrapping. 651 * @param {Number} width 652 */ 653 setBoundingWidth: function (width) { 654 this._width = width; 655 this.updateLabel(); 656 }, 657 658 _getBoundingWidth: function () { 659 return this._width; 660 }, 661 662 /** 663 * Set the param to change English word warp according to whether the space. <br /> 664 * default is false. 665 * @param {Boolean} breakWithoutSpace 666 */ 667 setLineBreakWithoutSpace: function (breakWithoutSpace) { 668 this._lineBreakWithoutSpaces = breakWithoutSpace; 669 this.updateLabel(); 670 }, 671 672 /** 673 * Set scale. <br /> 674 * Input a number, will be decrease or increase the font size. <br /> 675 * @param {Number} scale 676 * @param {Number} [scaleY=null] default is scale 677 */ 678 setScale: function (scale, scaleY) { 679 cc.Node.prototype.setScale.call(this, scale, scaleY); 680 this.updateLabel(); 681 }, 682 683 /** 684 * Set scale of x. <br /> 685 * Input a number, will be decrease or increase the font size. <br /> 686 * Horizontal scale. 687 * @param {Number} scaleX 688 */ 689 setScaleX: function (scaleX) { 690 cc.Node.prototype.setScaleX.call(this, scaleX); 691 this.updateLabel(); 692 }, 693 694 /** 695 * Set scale of x. <br /> 696 * Input a number, will be decrease or increase the font size. <br /> 697 * Longitudinal scale. 698 * @param {Number} scaleY 699 */ 700 setScaleY: function (scaleY) { 701 cc.Node.prototype.setScaleY.call(this, scaleY); 702 this.updateLabel(); 703 }, 704 705 /** 706 * set fnt file path. <br /> 707 * Change the fnt file path. 708 * @param {String} fntFile 709 */ 710 setFntFile: function (fntFile) { 711 var self = this; 712 if (fntFile != null && fntFile !== self._fntFile) { 713 var newConf = cc.loader.getRes(fntFile); 714 715 if (!newConf) { 716 cc.log("cc.LabelBMFont.setFntFile() : Impossible to create font. Please check file"); 717 return; 718 } 719 720 self._fntFile = fntFile; 721 self._config = newConf; 722 723 var texture = cc.textureCache.addImage(newConf.atlasName); 724 var locIsLoaded = texture.isLoaded(); 725 self._textureLoaded = locIsLoaded; 726 self.texture = texture; 727 this._renderCmd._updateFntFileTexture(); 728 if (!locIsLoaded) { 729 texture.addEventListener("load", function (sender) { 730 var self1 = this; 731 self1._textureLoaded = true; 732 self1.texture = sender; 733 self1.createFontChars(); 734 self1._changeTextureColor(); 735 self1.updateLabel(); 736 737 self1.dispatchEvent("load"); 738 }, self); 739 } else { 740 self.createFontChars(); 741 } 742 } 743 }, 744 745 /** 746 * Return the fnt file path. 747 * @return {String} 748 */ 749 getFntFile: function () { 750 return this._fntFile; 751 }, 752 753 setTexture: function(texture){ 754 this._renderCmd.setTexture(texture); 755 }, 756 757 /** 758 * Set the AnchorPoint of the labelBMFont. <br /> 759 * In order to change the location of label. 760 * @override 761 * @param {cc.Point|Number} point The anchor point of labelBMFont or The anchor point.x of labelBMFont. 762 * @param {Number} [y] The anchor point.y of labelBMFont. 763 */ 764 setAnchorPoint: function (point, y) { 765 cc.Node.prototype.setAnchorPoint.call(this, point, y); 766 this.updateLabel(); 767 }, 768 769 _setAnchorX: function (x) { 770 cc.Node.prototype._setAnchorX.call(this, x); 771 this.updateLabel(); 772 }, 773 774 _setAnchorY: function (y) { 775 cc.Node.prototype._setAnchorY.call(this, y); 776 this.updateLabel(); 777 }, 778 779 _atlasNameFromFntFile: function (fntFile) {}, 780 781 _kerningAmountForFirst: function (first, second) { 782 var ret = 0; 783 var key = (first << 16) | (second & 0xffff); 784 if (this._configuration.kerningDictionary) { 785 var element = this._configuration.kerningDictionary[key.toString()]; 786 if (element) 787 ret = element.amount; 788 } 789 return ret; 790 }, 791 792 _getLetterPosXLeft: function (sp) { 793 return sp.getPositionX() * this._scaleX - (sp._getWidth() * this._scaleX * sp._getAnchorX()); 794 }, 795 796 _getLetterPosXRight: function (sp) { 797 return sp.getPositionX() * this._scaleX + (sp._getWidth() * this._scaleX * sp._getAnchorX()); 798 }, 799 800 //Checking whether the character is a whitespace 801 _isspace_unicode: function(ch){ 802 ch = ch.charCodeAt(0); 803 return ((ch >= 9 && ch <= 13) || ch === 32 || ch === 133 || ch === 160 || ch === 5760 804 || (ch >= 8192 && ch <= 8202) || ch === 8232 || ch === 8233 || ch === 8239 805 || ch === 8287 || ch === 12288) 806 }, 807 808 _utf8_trim_ws: function(str){ 809 var len = str.length; 810 811 if (len <= 0) 812 return; 813 814 var last_index = len - 1; 815 816 // Only start trimming if the last character is whitespace.. 817 if (this._isspace_unicode(str[last_index])) { 818 for (var i = last_index - 1; i >= 0; --i) { 819 if (this._isspace_unicode(str[i])) { 820 last_index = i; 821 } 822 else { 823 break; 824 } 825 } 826 this._utf8_trim_from(str, last_index); 827 } 828 }, 829 830 //Trims str st str=[0, index) after the operation. 831 //Return value: the trimmed string. 832 _utf8_trim_from: function(str, index){ 833 var len = str.length; 834 if (index >= len || index < 0) 835 return; 836 str.splice(index, len); 837 } 838 }); 839 840 (function(){ 841 var p = cc.LabelBMFont.prototype; 842 cc.EventHelper.prototype.apply(p); 843 844 /** @expose */ 845 p.string; 846 cc.defineGetterSetter(p, "string", p.getString, p._setStringForSetter); 847 /** @expose */ 848 p.boundingWidth; 849 cc.defineGetterSetter(p, "boundingWidth", p._getBoundingWidth, p.setBoundingWidth); 850 /** @expose */ 851 p.textAlign; 852 cc.defineGetterSetter(p, "textAlign", p._getAlignment, p.setAlignment); 853 })(); 854 855 /** 856 * creates a bitmap font atlas with an initial string and the FNT file 857 * @deprecated since v3.0 please use new cc.LabelBMFont 858 * @param {String} str 859 * @param {String} fntFile 860 * @param {Number} [width=-1] 861 * @param {Number} [alignment=cc.TEXT_ALIGNMENT_LEFT] 862 * @param {cc.Point} [imageOffset=cc.p(0,0)] 863 * @return {cc.LabelBMFont|Null} 864 */ 865 cc.LabelBMFont.create = function (str, fntFile, width, alignment, imageOffset) { 866 return new cc.LabelBMFont(str, fntFile, width, alignment, imageOffset); 867 }; 868 869 cc._fntLoader = { 870 INFO_EXP: /info [^\n]*(\n|$)/gi, 871 COMMON_EXP: /common [^\n]*(\n|$)/gi, 872 PAGE_EXP: /page [^\n]*(\n|$)/gi, 873 CHAR_EXP: /char [^\n]*(\n|$)/gi, 874 KERNING_EXP: /kerning [^\n]*(\n|$)/gi, 875 ITEM_EXP: /\w+=[^ \r\n]+/gi, 876 INT_EXP: /^[\-]?\d+$/, 877 878 _parseStrToObj: function (str) { 879 var arr = str.match(this.ITEM_EXP); 880 var obj = {}; 881 if (arr) { 882 for (var i = 0, li = arr.length; i < li; i++) { 883 var tempStr = arr[i]; 884 var index = tempStr.indexOf("="); 885 var key = tempStr.substring(0, index); 886 var value = tempStr.substring(index + 1); 887 if (value.match(this.INT_EXP)) value = parseInt(value); 888 else if (value[0] === '"') value = value.substring(1, value.length - 1); 889 obj[key] = value; 890 } 891 } 892 return obj; 893 }, 894 895 /** 896 * Parse Fnt string. 897 * @param fntStr 898 * @param url 899 * @returns {{}} 900 */ 901 parseFnt: function (fntStr, url) { 902 var self = this, fnt = {}; 903 //padding 904 var infoObj = self._parseStrToObj(fntStr.match(self.INFO_EXP)[0]); 905 var paddingArr = infoObj["padding"].split(","); 906 var padding = { 907 left: parseInt(paddingArr[0]), 908 top: parseInt(paddingArr[1]), 909 right: parseInt(paddingArr[2]), 910 bottom: parseInt(paddingArr[3]) 911 }; 912 913 //common 914 var commonObj = self._parseStrToObj(fntStr.match(self.COMMON_EXP)[0]); 915 fnt.commonHeight = commonObj["lineHeight"]; 916 if (cc._renderType === cc._RENDER_TYPE_WEBGL) { 917 var texSize = cc.configuration.getMaxTextureSize(); 918 if (commonObj["scaleW"] > texSize.width || commonObj["scaleH"] > texSize.height) 919 cc.log("cc.LabelBMFont._parseCommonArguments(): page can't be larger than supported"); 920 } 921 if (commonObj["pages"] !== 1) cc.log("cc.LabelBMFont._parseCommonArguments(): only supports 1 page"); 922 923 //page 924 var pageObj = self._parseStrToObj(fntStr.match(self.PAGE_EXP)[0]); 925 if (pageObj["id"] !== 0) cc.log("cc.LabelBMFont._parseImageFileName() : file could not be found"); 926 fnt.atlasName = cc.path.changeBasename(url, pageObj["file"]); 927 928 //char 929 var charLines = fntStr.match(self.CHAR_EXP); 930 var fontDefDictionary = fnt.fontDefDictionary = {}; 931 for (var i = 0, li = charLines.length; i < li; i++) { 932 var charObj = self._parseStrToObj(charLines[i]); 933 var charId = charObj["id"]; 934 fontDefDictionary[charId] = { 935 rect: {x: charObj["x"], y: charObj["y"], width: charObj["width"], height: charObj["height"]}, 936 xOffset: charObj["xoffset"], 937 yOffset: charObj["yoffset"], 938 xAdvance: charObj["xadvance"] 939 }; 940 } 941 942 //kerning 943 var kerningDict = fnt.kerningDict = {}; 944 var kerningLines = fntStr.match(self.KERNING_EXP); 945 if (kerningLines) { 946 for (var i = 0, li = kerningLines.length; i < li; i++) { 947 var kerningObj = self._parseStrToObj(kerningLines[i]); 948 kerningDict[(kerningObj["first"] << 16) | (kerningObj["second"] & 0xffff)] = kerningObj["amount"]; 949 } 950 } 951 return fnt; 952 }, 953 954 /** 955 * load the fnt 956 * @param realUrl 957 * @param url 958 * @param res 959 * @param cb 960 */ 961 load: function (realUrl, url, res, cb) { 962 var self = this; 963 cc.loader.loadTxt(realUrl, function (err, txt) { 964 if (err) return cb(err); 965 cb(null, self.parseFnt(txt, url)); 966 }); 967 } 968 }; 969 cc.loader.register(["fnt"], cc._fntLoader); 970