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.game.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 if (!locIsLoaded) { 728 texture.addEventListener("load", function (sender) { 729 var self1 = this; 730 self1._textureLoaded = true; 731 self1.texture = sender; 732 self1.createFontChars(); 733 self1._changeTextureColor(); 734 self1.updateLabel(); 735 736 self1.dispatchEvent("load"); 737 }, self); 738 } else { 739 self.createFontChars(); 740 } 741 } 742 }, 743 744 /** 745 * Return the fnt file path. 746 * @return {String} 747 */ 748 getFntFile: function () { 749 return this._fntFile; 750 }, 751 752 setTexture: function(texture){ 753 this._renderCmd.setTexture(texture); 754 }, 755 756 /** 757 * Set the AnchorPoint of the labelBMFont. <br /> 758 * In order to change the location of label. 759 * @override 760 * @param {cc.Point|Number} point The anchor point of labelBMFont or The anchor point.x of labelBMFont. 761 * @param {Number} [y] The anchor point.y of labelBMFont. 762 */ 763 setAnchorPoint: function (point, y) { 764 cc.Node.prototype.setAnchorPoint.call(this, point, y); 765 this.updateLabel(); 766 }, 767 768 _setAnchorX: function (x) { 769 cc.Node.prototype._setAnchorX.call(this, x); 770 this.updateLabel(); 771 }, 772 773 _setAnchorY: function (y) { 774 cc.Node.prototype._setAnchorY.call(this, y); 775 this.updateLabel(); 776 }, 777 778 _atlasNameFromFntFile: function (fntFile) {}, 779 780 _kerningAmountForFirst: function (first, second) { 781 var ret = 0; 782 var key = (first << 16) | (second & 0xffff); 783 if (this._configuration.kerningDictionary) { 784 var element = this._configuration.kerningDictionary[key.toString()]; 785 if (element) 786 ret = element.amount; 787 } 788 return ret; 789 }, 790 791 _getLetterPosXLeft: function (sp) { 792 return sp.getPositionX() * this._scaleX - (sp._getWidth() * this._scaleX * sp._getAnchorX()); 793 }, 794 795 _getLetterPosXRight: function (sp) { 796 return sp.getPositionX() * this._scaleX + (sp._getWidth() * this._scaleX * sp._getAnchorX()); 797 }, 798 799 //Checking whether the character is a whitespace 800 _isspace_unicode: function(ch){ 801 ch = ch.charCodeAt(0); 802 return ((ch >= 9 && ch <= 13) || ch === 32 || ch === 133 || ch === 160 || ch === 5760 803 || (ch >= 8192 && ch <= 8202) || ch === 8232 || ch === 8233 || ch === 8239 804 || ch === 8287 || ch === 12288) 805 }, 806 807 _utf8_trim_ws: function(str){ 808 var len = str.length; 809 810 if (len <= 0) 811 return; 812 813 var last_index = len - 1; 814 815 // Only start trimming if the last character is whitespace.. 816 if (this._isspace_unicode(str[last_index])) { 817 for (var i = last_index - 1; i >= 0; --i) { 818 if (this._isspace_unicode(str[i])) { 819 last_index = i; 820 } 821 else { 822 break; 823 } 824 } 825 this._utf8_trim_from(str, last_index); 826 } 827 }, 828 829 //Trims str st str=[0, index) after the operation. 830 //Return value: the trimmed string. 831 _utf8_trim_from: function(str, index){ 832 var len = str.length; 833 if (index >= len || index < 0) 834 return; 835 str.splice(index, len); 836 } 837 }); 838 839 (function(){ 840 var p = cc.LabelBMFont.prototype; 841 cc.EventHelper.prototype.apply(p); 842 843 /** @expose */ 844 p.string; 845 cc.defineGetterSetter(p, "string", p.getString, p._setStringForSetter); 846 /** @expose */ 847 p.boundingWidth; 848 cc.defineGetterSetter(p, "boundingWidth", p._getBoundingWidth, p.setBoundingWidth); 849 /** @expose */ 850 p.textAlign; 851 cc.defineGetterSetter(p, "textAlign", p._getAlignment, p.setAlignment); 852 })(); 853 854 /** 855 * creates a bitmap font atlas with an initial string and the FNT file 856 * @deprecated since v3.0 please use new cc.LabelBMFont 857 * @param {String} str 858 * @param {String} fntFile 859 * @param {Number} [width=-1] 860 * @param {Number} [alignment=cc.TEXT_ALIGNMENT_LEFT] 861 * @param {cc.Point} [imageOffset=cc.p(0,0)] 862 * @return {cc.LabelBMFont|Null} 863 */ 864 cc.LabelBMFont.create = function (str, fntFile, width, alignment, imageOffset) { 865 return new cc.LabelBMFont(str, fntFile, width, alignment, imageOffset); 866 }; 867 868 cc._fntLoader = { 869 INFO_EXP: /info [^\n]*(\n|$)/gi, 870 COMMON_EXP: /common [^\n]*(\n|$)/gi, 871 PAGE_EXP: /page [^\n]*(\n|$)/gi, 872 CHAR_EXP: /char [^\n]*(\n|$)/gi, 873 KERNING_EXP: /kerning [^\n]*(\n|$)/gi, 874 ITEM_EXP: /\w+=[^ \r\n]+/gi, 875 INT_EXP: /^[\-]?\d+$/, 876 877 _parseStrToObj: function (str) { 878 var arr = str.match(this.ITEM_EXP); 879 var obj = {}; 880 if (arr) { 881 for (var i = 0, li = arr.length; i < li; i++) { 882 var tempStr = arr[i]; 883 var index = tempStr.indexOf("="); 884 var key = tempStr.substring(0, index); 885 var value = tempStr.substring(index + 1); 886 if (value.match(this.INT_EXP)) value = parseInt(value); 887 else if (value[0] === '"') value = value.substring(1, value.length - 1); 888 obj[key] = value; 889 } 890 } 891 return obj; 892 }, 893 894 /** 895 * Parse Fnt string. 896 * @param fntStr 897 * @param url 898 * @returns {{}} 899 */ 900 parseFnt: function (fntStr, url) { 901 var self = this, fnt = {}; 902 //padding 903 var infoObj = self._parseStrToObj(fntStr.match(self.INFO_EXP)[0]); 904 var paddingArr = infoObj["padding"].split(","); 905 var padding = { 906 left: parseInt(paddingArr[0]), 907 top: parseInt(paddingArr[1]), 908 right: parseInt(paddingArr[2]), 909 bottom: parseInt(paddingArr[3]) 910 }; 911 912 //common 913 var commonObj = self._parseStrToObj(fntStr.match(self.COMMON_EXP)[0]); 914 fnt.commonHeight = commonObj["lineHeight"]; 915 if (cc._renderType === cc.game.RENDER_TYPE_WEBGL) { 916 var texSize = cc.configuration.getMaxTextureSize(); 917 if (commonObj["scaleW"] > texSize.width || commonObj["scaleH"] > texSize.height) 918 cc.log("cc.LabelBMFont._parseCommonArguments(): page can't be larger than supported"); 919 } 920 if (commonObj["pages"] !== 1) cc.log("cc.LabelBMFont._parseCommonArguments(): only supports 1 page"); 921 922 //page 923 var pageObj = self._parseStrToObj(fntStr.match(self.PAGE_EXP)[0]); 924 if (pageObj["id"] !== 0) cc.log("cc.LabelBMFont._parseImageFileName() : file could not be found"); 925 fnt.atlasName = cc.path.changeBasename(url, pageObj["file"]); 926 927 //char 928 var charLines = fntStr.match(self.CHAR_EXP); 929 var fontDefDictionary = fnt.fontDefDictionary = {}; 930 for (var i = 0, li = charLines.length; i < li; i++) { 931 var charObj = self._parseStrToObj(charLines[i]); 932 var charId = charObj["id"]; 933 fontDefDictionary[charId] = { 934 rect: {x: charObj["x"], y: charObj["y"], width: charObj["width"], height: charObj["height"]}, 935 xOffset: charObj["xoffset"], 936 yOffset: charObj["yoffset"], 937 xAdvance: charObj["xadvance"] 938 }; 939 } 940 941 //kerning 942 var kerningDict = fnt.kerningDict = {}; 943 var kerningLines = fntStr.match(self.KERNING_EXP); 944 if (kerningLines) { 945 for (var i = 0, li = kerningLines.length; i < li; i++) { 946 var kerningObj = self._parseStrToObj(kerningLines[i]); 947 kerningDict[(kerningObj["first"] << 16) | (kerningObj["second"] & 0xffff)] = kerningObj["amount"]; 948 } 949 } 950 return fnt; 951 }, 952 953 /** 954 * load the fnt 955 * @param realUrl 956 * @param url 957 * @param res 958 * @param cb 959 */ 960 load: function (realUrl, url, res, cb) { 961 var self = this; 962 cc.loader.loadTxt(realUrl, function (err, txt) { 963 if (err) return cb(err); 964 cb(null, self.parseFnt(txt, url)); 965 }); 966 } 967 }; 968 cc.loader.register(["fnt"], cc._fntLoader); 969