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