1 /**************************************************************************** 2 Copyright (c) 2010-2012 cocos2d-x.org 3 Copyright (c) 2008-2010 Ricardo Quesada 4 Copyright (c) 2011 Zynga 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.LabelAutomaticWidth = -1; 37 38 cc._KerningHashElement = function (key, amount) { 39 this.key = key; //key for the hash. 16-bit for 1st element, 16-bit for 2nd element 40 this.amount = amount; 41 }; 42 43 cc._FontDefHashElement = function (key, fontDef) { 44 this.key = key || 0; // key. Font Unicode value 45 this.fontDef = fontDef || new cc._BMFontDef(); // font definition 46 }; 47 48 cc._BMFontDef = function (charID, rect, xOffset, yOffset, xAdvance) { 49 //! ID of the character 50 this.charID = charID || 0; 51 //! origin and size of the font 52 this.rect = rect || cc.rect(0, 0, 0.1, 0.1); 53 //! The X amount the image should be offset when drawing the image (in pixels) 54 this.xOffset = xOffset || 0; 55 //! The Y amount the image should be offset when drawing the image (in pixels) 56 this.yOffset = yOffset || 0; 57 //! The amount to move the current position after drawing the character (in pixels) 58 this.xAdvance = xAdvance || 0; 59 }; 60 61 cc._BMFontPadding = function (left, top, right, bottom) { 62 /// padding left 63 this.left = left || 0; 64 /// padding top 65 this.top = top || 0; 66 /// padding right 67 this.right = right || 0; 68 /// padding bottom 69 this.bottom = bottom || 0; 70 }; 71 72 /** 73 * cc.BMFontConfiguration has parsed _configuration of the the .fnt file 74 * @class 75 * @extends cc.Class 76 */ 77 cc.BMFontConfiguration = cc.Class.extend(/** @lends cc.BMFontConfiguration# */{ 78 // XXX: Creating a public interface so that the bitmapFontArray[] is acc.esible 79 //@public 80 /** 81 * The characters building up the font 82 * @type object 83 */ 84 bitmapFontArray:{}, 85 86 /** 87 * FNTConfig: Common Height 88 * @type Number 89 */ 90 commonHeight:0, 91 92 /** 93 * Padding 94 * @type cc._BMFontPadding 95 */ 96 padding:new cc._BMFontPadding(), 97 98 /** 99 * atlas name 100 * @type String 101 */ 102 atlasName:"", 103 104 /** 105 * values for kerning 106 * @type cc._KerningHashElement 107 */ 108 kerningDictionary:{}, 109 110 /** 111 * values for FontDef 112 * @type cc._FontDefHashElement 113 */ 114 fontDefDictionary:null, 115 /** 116 * Constructor 117 */ 118 ctor:function () { 119 this.fontDefDictionary = {}; 120 this.fontDefDictionary["0"] = new cc._FontDefHashElement(); 121 }, 122 123 /** 124 * Description of BMFontConfiguration 125 * @return {String} 126 */ 127 description:function () { 128 return "<cc.BMFontConfiguration | Kernings:" + this.kerningDictionary + " | Image = " + this.atlasName.toString() + ">"; 129 }, 130 131 /** 132 * @return {String} 133 */ 134 getAtlasName:function () { 135 return this.atlasName; 136 }, 137 138 /** 139 * @param {String} atlasName 140 */ 141 setAtlasName:function (atlasName) { 142 this.atlasName = atlasName; 143 }, 144 145 /** 146 * initializes a BitmapFontConfiguration with a FNT file 147 * @param {String} FNT file path 148 * @return {Boolean} 149 */ 150 initWithFNTfile:function (FNTfile) { 151 cc.Assert(FNTfile != null && FNTfile.length != 0, ""); 152 this._parseConfigFile(FNTfile); 153 return true; 154 }, 155 156 _parseConfigFile:function (controlFile) { 157 var data = cc.SAXParser.getInstance().getList(controlFile); 158 cc.Assert(data, "cc.BMFontConfiguration._parseConfigFile | Open file error."); 159 160 // parse spacing / padding 161 var line, re; 162 163 re = /padding+[a-z0-9\-= ",]+/gi; 164 line = re.exec(data)[0]; 165 if (line) { 166 this._parseInfoArguments(line); 167 } 168 169 re = /common lineHeight+[a-z0-9\-= ",]+/gi; 170 line = re.exec(data)[0]; 171 if (line) { 172 this._parseCommonArguments(line); 173 } 174 175 re = /page id=[a-zA-Z0-9\.\-= ",]+/gi; 176 line = re.exec(data)[0]; 177 if (line) { 178 this._parseImageFileName(line, controlFile); 179 } 180 181 re = /chars c+[a-z0-9\-= ",]+/gi; 182 line = re.exec(data)[0]; 183 if (line) { 184 // Ignore this line 185 } 186 187 re = /char id=\w[a-z0-9\-= ]+/gi; 188 line = data.match(re); 189 if (line) { 190 // Parse the current line and create a new CharDef 191 for (var i = 0; i < line.length; i++) { 192 var element = new cc._FontDefHashElement(); 193 this._parseCharacterDefinition(line[i], element.fontDef); 194 element.key = element.fontDef.charID; 195 this.fontDefDictionary[element.key] = element; 196 } 197 } 198 199 re = /kernings count+[a-z0-9\-= ",]+/gi; 200 if (re.test(data)) { 201 line = RegExp.$1[0]; 202 } 203 if (line) { 204 this._parseKerningCapacity(line); 205 } 206 207 re = /first=\w[a-z0-9\-= ]+/gi; 208 line = data.match(re); 209 if (line) { 210 for (var i = 0; i < line.length; i++) { 211 this._parseKerningEntry(line[i]); 212 } 213 } 214 }, 215 216 _parseCharacterDefinition:function (line, characterDefinition) { 217 ////////////////////////////////////////////////////////////////////////// 218 // line to parse: 219 // char id=32 x=0 y=0 width=0 height=0 xoffset=0 yoffset=44 xadvance=14 page=0 chnl=0 220 ////////////////////////////////////////////////////////////////////////// 221 // Character ID 222 var value = /id=(\d+)/gi.exec(line)[1]; 223 characterDefinition.charID = value.toString(); 224 225 // Character x 226 value = /x=([\-\d]+)/gi.exec(line)[1]; 227 characterDefinition.rect.origin.x = parseInt(value); 228 229 // Character y 230 value = /y=([\-\d]+)/gi.exec(line)[1]; 231 characterDefinition.rect.origin.y = parseInt(value); 232 233 // Character width 234 value = /width=([\-\d]+)/gi.exec(line)[1]; 235 characterDefinition.rect.size.width = parseInt(value); 236 237 // Character height 238 value = /height=([\-\d]+)/gi.exec(line)[1]; 239 characterDefinition.rect.size.height = parseInt(value); 240 241 // Character xoffset 242 value = /xoffset=([\-\d]+)/gi.exec(line)[1]; 243 characterDefinition.xOffset = parseInt(value); 244 245 // Character yoffset 246 value = /yoffset=([\-\d]+)/gi.exec(line)[1]; 247 characterDefinition.yOffset = parseInt(value); 248 249 // Character xadvance 250 value = /xadvance=([\-\d]+)/gi.exec(line)[1]; 251 characterDefinition.xAdvance = parseInt(value); 252 253 }, 254 255 _parseInfoArguments:function (line) { 256 ////////////////////////////////////////////////////////////////////////// 257 // possible lines to parse: 258 // info face="Script" size=32 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=1,4,3,2 spacing=0,0 outline=0 259 // info face="Cracked" size=36 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1 260 ////////////////////////////////////////////////////////////////////////// 261 262 // padding 263 var tmpPadding = /padding=(\d+)[,](\d+)[,](\d+)[,](\d+)/gi.exec(line); 264 this.padding.left = tmpPadding[1]; 265 this.padding.top = tmpPadding[2] 266 this.padding.right = tmpPadding[3]; 267 this.padding.bottom = tmpPadding[4]; 268 }, 269 270 _parseCommonArguments:function (line) { 271 ////////////////////////////////////////////////////////////////////////// 272 // line to parse: 273 // common lineHeight=104 base=26 scaleW=1024 scaleH=512 pages=1 packed=0 274 ////////////////////////////////////////////////////////////////////////// 275 276 var value; 277 // Height 278 this.commonHeight = parseInt(/lineHeight=(\d+)/gi.exec(line)[1]); 279 280 // pages. sanity check 281 value = /pages=(\d+)/gi.exec(line)[1]; 282 cc.Assert(parseInt(value) == 1, "cc.BitfontAtlas: only supports 1 page"); 283 284 // packed (ignore) What does this mean ?? 285 }, 286 287 _parseImageFileName:function (line, fntFile) { 288 ////////////////////////////////////////////////////////////////////////// 289 // line to parse: 290 // page id=0 file="bitmapFontTest.png" 291 ////////////////////////////////////////////////////////////////////////// 292 var value; 293 // page ID. Sanity check 294 value = /id=(\d+)/gi.exec(line)[1]; 295 cc.Assert(parseInt(value) == 0, "LabelBMFont file could not be found"); 296 297 // file 298 value = /file="([a-zA-Z0-9\-\._]+)/gi.exec(line)[1]; 299 300 this.atlasName = cc.FileUtils.getInstance().fullPathFromRelativeFile(value, fntFile); 301 }, 302 303 _parseKerningCapacity:function (line) { 304 }, 305 306 _parseKerningEntry:function (line) { 307 ////////////////////////////////////////////////////////////////////////// 308 // line to parse: 309 // kerning first=121 second=44 amount=-7 310 ////////////////////////////////////////////////////////////////////////// 311 // first 312 var value = /first=([\-\d]+)/gi.exec(line)[1]; 313 var first = parseInt(value); 314 315 // second 316 value = /second=([\-\d]+)/gi.exec(line)[1]; 317 var second = parseInt(value); 318 319 // amount 320 value = /amount=([\-\d]+)/gi.exec(line)[1]; 321 var amount = parseInt(value); 322 323 var element = new cc._KerningHashElement(); 324 element.amount = amount; 325 element.key = (first << 16) | (second & 0xffff); 326 327 this.kerningDictionary[element.key] = element; 328 329 }, 330 331 _purgeKerningDictionary:function () { 332 this.kerningDictionary = {}; 333 }, 334 335 _purgeFontDefDictionary:function () { 336 this.fontDefDictionary = { 337 "0":{ 338 "key":"0", 339 "fontDef":{ 340 "charID":"0", 341 "rect":{ 342 "origin":{ 343 "x":0, 344 "y":0 345 }, 346 "size":{ 347 "width":1, 348 "height":1 349 } 350 }, 351 "xOffset":0, 352 "yOffset":0, 353 "xAdvance":0} 354 } 355 }; 356 } 357 }); 358 359 /** 360 * Create a cc.BMFontConfiguration 361 * @param {String} FNTfile 362 * @return {cc.BMFontConfiguration|Null} returns the configuration or null if error 363 * @example 364 * // Example 365 * var conf = cc.BMFontConfiguration.create('myfont.fnt'); 366 */ 367 cc.BMFontConfiguration.create = function (FNTfile) { 368 var ret = new cc.BMFontConfiguration(); 369 if (ret.initWithFNTfile(FNTfile)) { 370 return ret; 371 } 372 return null; 373 }; 374 375 /** 376 * <p>cc.LabelBMFont is a subclass of cc.SpriteSheet.</p> 377 * 378 * <p>Features:<br/> 379 * <ul><li>- Treats each character like a cc.Sprite. This means that each individual character can be:</li> 380 * <li>- rotated</li> 381 * <li>- scaled</li> 382 * <li>- translated</li> 383 * <li>- tinted</li> 384 * <li>- chage the opacity</li> 385 * <li>- It can be used as part of a menu item.</li> 386 * <li>- anchorPoint can be used to align the "label"</li> 387 * <li>- Supports AngelCode text format</li></ul></p> 388 * 389 * <p>Limitations:<br/> 390 * - All inner characters are using an anchorPoint of (0.5, 0.5) and it is not recommend to change it 391 * because it might affect the rendering</p> 392 * 393 * <p>cc.LabelBMFont implements the protocol cc.LabelProtocol, like cc.Label and cc.LabelAtlas.<br/> 394 * cc.LabelBMFont has the flexibility of cc.Label, the speed of cc.LabelAtlas and all the features of cc.Sprite.<br/> 395 * If in doubt, use cc.LabelBMFont instead of cc.LabelAtlas / cc.Label.</p> 396 * 397 * <p>Supported editors:<br/> 398 * http://glyphdesigner.71squared.com/ (Commercial, Mac OS X)<br/> 399 * http://www.n4te.com/hiero/hiero.jnlp (Free, Java)<br/> 400 * http://slick.cokeandcode.com/demos/hiero.jnlp (Free, Java)<br/> 401 * http://www.angelcode.com/products/bmfont/ (Free, Windows only)</p> 402 * @class 403 * @extends cc. 404 */ 405 cc.LabelBMFont = cc.SpriteBatchNode.extend(/** @lends cc.LabelBMFont# */{ 406 RGBAProtocol:true, 407 _opacity:0, 408 _color:null, 409 _opacityModifyRGB:false, 410 _string:"", 411 _configuration:null, 412 // name of fntFile 413 _fntFile:"", 414 // initial string without line breaks 415 _initialString:"", 416 // alignment of all lines 417 _alignment:null, 418 // max width until a line break is added 419 _width:0, 420 _lineBreakWithoutSpaces:false, 421 _imageOffset:cc.PointZero(), 422 /** 423 * Constructor 424 */ 425 /* ctor:function () { 426 this._super(); 427 },*/ 428 /** 429 * @param {CanvasContext} ctx 430 */ 431 draw:function (ctx) { 432 this._super(); 433 var context = ctx || cc.renderContext; 434 //LabelBMFont - Debug draw 435 if (cc.LABELBMFONT_DEBUG_DRAW) { 436 var s = this.getContentSize(); 437 var pos = cc.p(0 | ( -this._anchorPointInPoints.x), 0 | ( -this._anchorPointInPoints.y)); 438 var vertices = [cc.p(pos.x, pos.y), cc.p(pos.x + s.width, pos.y), cc.p(pos.x + s.width, pos.y + s.height), cc.p(pos.x, pos.y + s.height)]; 439 context.strokeStyle = "rgba(0,255,0,1)"; 440 cc.drawingUtil.drawPoly(vertices, 4, true); 441 } 442 }, 443 444 /** 445 * conforms to cc.RGBAProtocol protocol 446 * @return {Number} 447 */ 448 getOpacity:function () { 449 return this._opacity; 450 }, 451 452 /** 453 * set the opacity of this label 454 * @param {Number} Var 455 */ 456 setOpacity:function (Var) { 457 this._opacity = Var; 458 if (this._children) { 459 for (var i = 0, len = this._children.length; i < len; i++) { 460 var node = this._children[i]; 461 if (node) { 462 node.setOpacity(this._opacity); 463 } 464 } 465 } 466 }, 467 468 /** 469 * conforms to cc.RGBAProtocol protocol 470 * @return {cc.Color3B} 471 */ 472 getColor:function () { 473 return this._color; 474 }, 475 476 /** 477 * tint this label 478 * @param {cc.Color3B} Var 479 */ 480 setColor:function (color3) { 481 if ((this._color.r == color3.r) && (this._color.g == color3.g) && (this._color.b == color3.b)) { 482 return; 483 } 484 this._color = color3; 485 if (this.getTexture()) { 486 if (cc.renderContextType == cc.CANVAS) { 487 var cacheTextureForColor = cc.TextureCache.getInstance().getTextureColors(this._originalTexture); 488 if (cacheTextureForColor) { 489 //generate color texture cache 490 var tx = this.getTexture(); 491 var textureRect = cc.rect(0, 0, tx.width, tx.height); 492 var colorTexture = cc.generateTintImage(tx, cacheTextureForColor, this._color, textureRect); 493 var img = new Image(); 494 img.src = colorTexture.toDataURL(); 495 this.setTexture(img); 496 this.updateString(false); 497 } 498 } 499 } 500 }, 501 502 /** 503 * conforms to cc.RGBAProtocol protocol 504 * @return {Boolean} 505 */ 506 isOpacityModifyRGB:function () { 507 return this._opacityModifyRGB; 508 }, 509 510 /** 511 * @param {Boolean} Var 512 */ 513 setOpacityModifyRGB:function (Var) { 514 this._opacityModifyRGB = Var; 515 if (this._children && this._children.length != 0) { 516 for (var i = 0, len = this._children.length; i < len; i++) { 517 var node = this._children[i]; 518 if (node) { 519 if (node.RGBAProtocol) { 520 node.setOpacity(255); 521 } 522 } 523 } 524 } 525 }, 526 527 /** 528 * init LabelBMFont 529 */ 530 init:function () { 531 return this.initWithString(null, null, null, null, null); 532 }, 533 534 /** 535 * init a bitmap font altas with an initial string and the FNT file 536 * @param {String} str 537 * @param {String} fntFile 538 * @param {String} width 539 * @param {Number} alignment 540 * @param {Number} imageOffset 541 * @return {Boolean} 542 */ 543 initWithString:function (str, fntFile, width, alignment, imageOffset) { 544 var theString = str; 545 546 cc.Assert(!this._configuration, "re-init is no longer supported"); 547 548 var texture; 549 if (fntFile) { 550 var newConf = cc.FNTConfigLoadFile(fntFile); 551 cc.Assert(newConf, "cc.LabelBMFont: Impossible to create font. Please check file"); 552 this._configuration = newConf; 553 this._fntFile = fntFile; 554 texture = cc.TextureCache.getInstance().addImage(this._configuration.getAtlasName()); 555 } 556 else { 557 texture = new Image(); 558 } 559 560 if (theString == null) { 561 theString = ""; 562 } 563 564 if (this.initWithTexture(texture, theString.length)) { 565 this._alignment = alignment || cc.TEXT_ALIGNMENT_LEFT; 566 this._imageOffset = imageOffset || cc.PointZero(); 567 this._width = width || cc.LabelAutomaticWidth; 568 this._opacity = 255; 569 this._color = cc.white(); 570 this._contentSize = cc.SizeZero(); 571 this.setString(theString); 572 this.setAnchorPoint(cc.p(0.5, 0.5)); 573 return true; 574 } 575 return false; 576 }, 577 578 /** 579 * updates the font chars based on the string to render 580 */ 581 createFontChars:function () { 582 var nextFontPositionX = 0; 583 var nextFontPositionY = 0; 584 var prev = -1; 585 var kerningAmount = 0; 586 587 var tmpSize = cc.SizeZero(); 588 589 var longestLine = 0; 590 var totalHeight = 0; 591 592 var quantityOfLines = 1; 593 594 var stringLen = this._string.length; 595 596 if (stringLen == 0) { 597 return; 598 } 599 var i; 600 for (i = 0; i < stringLen; i++) { 601 if (this._string.charCodeAt(i) == 10) { 602 quantityOfLines++; 603 } 604 } 605 606 totalHeight = this._configuration.commonHeight * quantityOfLines; 607 nextFontPositionY = -(this._configuration.commonHeight - this._configuration.commonHeight * quantityOfLines); 608 609 for (i = 0; i < stringLen; i++) { 610 var key = this._string.charCodeAt(i); 611 612 if (key == 10) { 613 nextFontPositionX = 0; 614 nextFontPositionY -= this._configuration.commonHeight; 615 continue; 616 } 617 618 var element = this._configuration.fontDefDictionary[key]; 619 cc.Assert(element, "FontDefinition could not be found!"); 620 621 var fontDef = element.fontDef; 622 623 var rect = cc.rect(fontDef.rect.origin.x, fontDef.rect.origin.y, fontDef.rect.size.width, fontDef.rect.size.height); 624 rect = cc.RECT_PIXELS_TO_POINTS(rect); 625 rect.origin.x += this._imageOffset.x; 626 rect.origin.y += this._imageOffset.y; 627 628 var fontChar = this.getChildByTag(i); 629 if (!fontChar) { 630 fontChar = new cc.Sprite(); 631 if (key == 32) { 632 fontChar.init(); 633 fontChar.setTextureRect(cc.RectZero(), false, cc.SizeZero()); 634 } else { 635 fontChar.initWithTexture(this._textureAtlas.getTexture(), rect, false); 636 } 637 this.addChild(fontChar, 0, i); 638 } else { 639 if (key == 32) { 640 fontChar.init(); 641 fontChar.setTextureRect(cc.RectZero(), false, cc.SizeZero()); 642 } else { 643 // reusing fonts 644 fontChar.initWithTexture(this._textureAtlas.getTexture(), rect, false); 645 // restore to default in case they were modified 646 fontChar.setVisible(true); 647 fontChar.setOpacity(255); 648 } 649 } 650 651 var yOffset = this._configuration.commonHeight - fontDef.yOffset; 652 var fontPos = cc.p(nextFontPositionX + fontDef.xOffset + fontDef.rect.size.width * 0.5 + kerningAmount, 653 nextFontPositionY + yOffset - rect.size.height * 0.5 * cc.CONTENT_SCALE_FACTOR()); 654 fontChar.setPosition(cc.POINT_PIXELS_TO_POINTS(fontPos)); 655 656 // update kerning 657 nextFontPositionX += fontDef.xAdvance + kerningAmount; 658 prev = key; 659 660 // Apply label properties 661 fontChar.setOpacityModifyRGB(this._opacityModifyRGB); 662 663 // only apply opacity if it is different than 255 ) 664 if (this._opacity != 255) { 665 fontChar.setOpacity(this._opacity); 666 } 667 668 if (longestLine < nextFontPositionX) { 669 longestLine = nextFontPositionX; 670 } 671 } 672 673 tmpSize.width = longestLine; 674 tmpSize.height = totalHeight; 675 this.setContentSize(cc.SIZE_PIXELS_TO_POINTS(tmpSize)); 676 }, 677 678 /** 679 * update String 680 * @param {Boolean} fromUpdate 681 */ 682 updateString:function (fromUpdate) { 683 if (this._children) { 684 for (var i = 0; i < this._children.length; i++) { 685 var node = this._children[i]; 686 if (node) { 687 node.setVisible(false); 688 } 689 } 690 } 691 if (this._configuration) { 692 this.createFontChars(); 693 } 694 if (!fromUpdate) { 695 this.updateLabel(); 696 } 697 }, 698 699 /** 700 * get the text of this label 701 * @return {String} 702 */ 703 getString:function () { 704 return this._initialString; 705 }, 706 707 /** 708 * set the text 709 * @param newString 710 */ 711 setString:function (newString, fromUpdate) { 712 if (this._string != newString) { 713 this._string = newString + String.fromCharCode(0); 714 // if(this._initialString == ""){ 715 this._initialString = newString + String.fromCharCode(0); 716 //} 717 this.updateString(fromUpdate); 718 } 719 }, 720 721 /** 722 * @deprecated 723 * @param label 724 */ 725 setCString:function (label) { 726 this.setString(label); 727 }, 728 729 /** 730 * update Label 731 */ 732 updateLabel:function () { 733 if (this._width > 0) { 734 this.setString(this._initialString, true); 735 736 // Step 1: Make multiline 737 var stringLength = this._string.length; 738 var multiline_string = []; 739 var last_word = []; 740 741 var line = 1, i = 0, start_line = false, start_word = false, startOfLine = -1, startOfWord = -1, skip = 0; 742 743 var characterSprite; 744 for (var j = 0; j < this._children.length; j++) { 745 while (!(characterSprite = this.getChildByTag(j + skip))) 746 skip++; 747 748 if (!characterSprite.isVisible()) continue; 749 if (i >= stringLength) 750 break; 751 752 var character = this._string[i]; 753 754 if (!start_word) { 755 startOfWord = this._getLetterPosXLeft(characterSprite); 756 start_word = true; 757 } 758 if (!start_line) { 759 startOfLine = startOfWord; 760 start_line = true; 761 } 762 763 // Newline. 764 if (character.charCodeAt(0) == 10) { 765 last_word.push('\n'); 766 multiline_string = multiline_string.concat(last_word); 767 last_word.length = 0; 768 start_word = false; 769 start_line = false; 770 startOfWord = -1; 771 startOfLine = -1; 772 i++; 773 line++; 774 775 if (i >= stringLength) 776 break; 777 778 character = this._string[i]; 779 780 if (!startOfWord) { 781 startOfWord = this._getLetterPosXLeft(characterSprite); 782 start_word = true; 783 } 784 if (!startOfLine) { 785 startOfLine = startOfWord; 786 start_line = true; 787 } 788 } 789 790 // Whitespace. 791 if (character.charCodeAt(0) == 32) { 792 last_word.push(character); 793 multiline_string = multiline_string.concat(last_word); 794 last_word.length = 0; 795 start_word = false; 796 startOfWord = -1; 797 i++; 798 continue; 799 } 800 801 // Out of bounds. 802 if (this._getLetterPosXRight(characterSprite) - startOfLine > this._width) { 803 if (!this._lineBreakWithoutSpaces) { 804 last_word.push(character); 805 806 var found = multiline_string.lastIndexOf(" "); 807 if (found != -1) 808 cc.utf8_trim_ws(multiline_string); 809 else 810 multiline_string = []; 811 812 if (multiline_string.length > 0) 813 multiline_string.push('\n'); 814 815 line++; 816 start_line = false; 817 startOfLine = -1; 818 i++; 819 } 820 else { 821 cc.utf8_trim_ws(last_word); 822 823 last_word.push('\n'); 824 multiline_string = multiline_string.concat(last_word); 825 last_word.length = 0; 826 start_word = false; 827 start_line = false; 828 startOfWord = -1; 829 startOfLine = -1; 830 line++; 831 832 if (i >= stringLength) 833 break; 834 835 if (!startOfWord) { 836 startOfWord = this._getLetterPosXLeft(characterSprite); 837 start_word = true; 838 } 839 if (!startOfLine) { 840 startOfLine = startOfWord; 841 start_line = true; 842 } 843 844 j--; 845 } 846 847 continue; 848 } 849 else { 850 // Character is normal. 851 last_word.push(character); 852 i++; 853 continue; 854 } 855 } 856 857 multiline_string = multiline_string.concat(last_word); 858 var len = multiline_string.length; 859 var str_new = ""; 860 861 for (var i = 0; i < len; ++i) { 862 str_new += multiline_string[i]; 863 } 864 865 this._string = str_new + String.fromCharCode(0); 866 this.updateString(true); 867 } 868 869 // Step 2: Make alignment 870 if (this._alignment != cc.TEXT_ALIGNMENT_LEFT) { 871 var i = 0; 872 873 var lineNumber = 0; 874 var strlen = this._string.length; 875 var last_line = []; 876 877 for (var ctr = 0; ctr < strlen; ctr++) { 878 if (this._string[ctr].charCodeAt(0) == 10 || this._string[ctr].charCodeAt(0) == 0) { 879 var lineWidth = 0; 880 var line_length = last_line.length; 881 var index = i + line_length - 1 + lineNumber; 882 if (index < 0) continue; 883 884 var lastChar = this.getChildByTag(index); 885 if (lastChar == null) 886 continue; 887 lineWidth = lastChar.getPosition().x + lastChar.getContentSize().width / 2; 888 889 var shift = 0; 890 switch (this._alignment) { 891 case cc.TEXT_ALIGNMENT_CENTER: 892 shift = this.getContentSize().width / 2 - lineWidth / 2; 893 break; 894 case cc.TEXT_ALIGNMENT_RIGHT: 895 shift = this.getContentSize().width - lineWidth; 896 break; 897 default: 898 break; 899 } 900 901 if (shift != 0) { 902 for (var j = 0; j < line_length; j++) { 903 index = i + j + lineNumber; 904 if (index < 0) continue; 905 var characterSprite = this.getChildByTag(index); 906 if(characterSprite){ 907 characterSprite.setPosition(cc.pAdd(characterSprite.getPosition(), cc.p(shift, 0))); 908 } 909 } 910 } 911 912 i += line_length; 913 lineNumber++; 914 915 last_line.length = 0; 916 continue; 917 } 918 919 last_line.push(this._string[i]); 920 } 921 } 922 }, 923 924 /** 925 * Set text vertical alignment 926 * @param {Number} alignment 927 */ 928 setAlignment:function (alignment) { 929 this._alignment = alignment; 930 this.updateLabel(); 931 }, 932 933 /** 934 * @param {Number} width 935 */ 936 setWidth:function (width) { 937 this._width = width; 938 this.updateLabel(); 939 }, 940 941 /** 942 * @param {Boolean} breakWithoutSpace 943 */ 944 setLineBreakWithoutSpace:function (breakWithoutSpace) { 945 this._lineBreakWithoutSpaces = breakWithoutSpace; 946 this.updateLabel(); 947 }, 948 949 /** 950 * @param {Number} scale 951 */ 952 setScale:function (scale, scaleY) { 953 this._super(scale, scaleY); 954 this.updateLabel(); 955 }, 956 957 /** 958 * @param {Number} scaleX 959 */ 960 setScaleX:function (scaleX) { 961 this._super(scaleX); 962 this.updateLabel(); 963 }, 964 965 /** 966 * @param {Number} scaleY 967 */ 968 setScaleY:function (scaleY) { 969 this._super(scaleY); 970 this.updateLabel(); 971 }, 972 /** 973 * set fnt file path 974 * @param {String} fntFile 975 */ 976 setFntFile:function (fntFile) { 977 if (fntFile != null && fntFile != this._fntFile) { 978 var newConf = cc.FNTConfigLoadFile(fntFile); 979 980 cc.Assert(newConf, "cc.LabelBMFont: Impossible to create font. Please check file"); 981 982 this._fntFile = fntFile; 983 this._configuration = newConf; 984 985 this.setTexture(cc.TextureCache.getInstance().addImage(this._configuration.getAtlasName())); 986 if (cc.renderContextType == cc.CANVAS) { 987 this._originalTexture = this.getTexture(); 988 } 989 this.createFontChars(); 990 } 991 }, 992 993 /** 994 * @return {String} 995 */ 996 getFntFile:function () { 997 return this._fntFile; 998 }, 999 1000 /** 1001 * set the anchorpoint of the label 1002 * @param {cc.Point} point 1003 */ 1004 setAnchorPoint:function (point) { 1005 if (!cc.Point.CCPointEqualToPoint(point, this._anchorPoint)) { 1006 this._super(point); 1007 this.updateLabel(); 1008 } 1009 }, 1010 1011 _atlasNameFromFntFile:function (fntFile) { 1012 }, 1013 _kerningAmountForFirst:function (first, second) { 1014 var ret = 0; 1015 var key = (first << 16) | (second & 0xffff); 1016 if (this._configuration.kerningDictionary) { 1017 var element = this._configuration.kerningDictionary[key.toString()]; 1018 if (element) { 1019 ret = element.amount; 1020 } 1021 } 1022 return ret; 1023 }, 1024 _getLetterPosXLeft:function (sp) { 1025 return sp.getPosition().x * this._scaleX + (sp.getContentSize().width * this._scaleX * sp.getAnchorPoint().x); 1026 }, 1027 _getLetterPosXRight:function (sp) { 1028 return sp.getPosition().x * this._scaleX - (sp.getContentSize().width * this._scaleX * sp.getAnchorPoint().x); 1029 } 1030 }); 1031 1032 /** 1033 * creates a bitmap font altas with an initial string and the FNT file 1034 * @param {String} str 1035 * @param {String} fntFile 1036 * @param {String} width 1037 * @param {Number} alignment 1038 * @param {Number} imageOffset 1039 * @return {cc.LabelBMFont|Null} 1040 * @example 1041 * // Example 01 1042 * var label1 = cc.LabelBMFont.create("Test case", "test.fnt"); 1043 * 1044 * // Example 02 1045 * var label2 = cc.LabelBMFont.create("test case", "test.fnt", 200, cc.TEXT_ALIGNMENT_LEFT); 1046 * 1047 * // Example 03 1048 * var label3 = cc.LabelBMFont.create("This is a \n test case", "test.fnt", 200, cc.TEXT_ALIGNMENT_LEFT, cc.PointZero()); 1049 */ 1050 cc.LabelBMFont.create = function (str, fntFile, width, alignment, imageOffset) { 1051 var ret = new cc.LabelBMFont(); 1052 if(arguments.length == 0){ 1053 if(ret && ret.init()){ 1054 return ret; 1055 } 1056 return null; 1057 } 1058 1059 if (ret && ret.initWithString(str, fntFile, width, alignment, imageOffset)) { 1060 return ret; 1061 } 1062 return null; 1063 }; 1064 1065 /** 1066 * shared instance of configuration 1067 * @type cc.BMFontConfiguration 1068 */ 1069 cc.configurations = null; 1070 1071 /** 1072 * Load the .fnt file 1073 * @param {String} fntFile 1074 * @return {cc.BMFontConfiguration} 1075 * Constructor 1076 */ 1077 cc.FNTConfigLoadFile = function (fntFile) { 1078 if (!cc.configurations) { 1079 cc.configurations = {}; 1080 } 1081 var ret = cc.configurations[fntFile]; 1082 if (!ret) { 1083 ret = cc.BMFontConfiguration.create(fntFile); 1084 } 1085 return ret; 1086 }; 1087 1088 /** 1089 * Purges the cached .fnt data 1090 */ 1091 cc.purgeCachedData = function () { 1092 cc.FNTConfigRemoveCache(); 1093 }; 1094 1095 /** 1096 * Purges the FNT config cache 1097 */ 1098 cc.FNTConfigRemoveCache = function () { 1099 if (cc.configurations) { 1100 cc.configurations = {}; 1101 } 1102 }; 1103 1104 /** 1105 * @param {String} ch 1106 * @return {Boolean} weather the character is a whitespace character. 1107 */ 1108 cc.isspace_unicode = function (ch) { 1109 ch = ch.charCodeAt(0); 1110 return ((ch >= 9 && ch <= 13) || ch == 32 || ch == 133 || ch == 160 || ch == 5760 1111 || (ch >= 8192 && ch <= 8202) || ch == 8232 || ch == 8233 || ch == 8239 1112 || ch == 8287 || ch == 12288) 1113 }; 1114 1115 /** 1116 * @param {String} str 1117 */ 1118 cc.utf8_trim_ws = function (str) { 1119 var len = str.length; 1120 1121 if (len <= 0) 1122 return; 1123 1124 var last_index = len - 1; 1125 1126 // Only start trimming if the last character is whitespace.. 1127 if (cc.isspace_unicode(str[last_index])) { 1128 for (var i = last_index - 1; i >= 0; --i) { 1129 if (cc.isspace_unicode(str[i])) { 1130 last_index = i; 1131 } 1132 else { 1133 break; 1134 } 1135 } 1136 cc.utf8_trim_from(str, last_index); 1137 } 1138 }; 1139 1140 /** 1141 * Trims str st str=[0, index) after the operation. 1142 * Return value: the trimmed string. 1143 * @param {String} str he string to trim 1144 * @param {Number} index the index to start trimming from. 1145 */ 1146 cc.utf8_trim_from = function (str, index) { 1147 var len = str.length; 1148 if (index >= len || index < 0) 1149 return; 1150 str.splice(index, len); 1151 }; 1152