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 
 27 /**
 28  * cc.LabelTTF is a subclass of cc.TextureNode that knows how to render text labels<br/>
 29  * All features from cc.TextureNode are valid in cc.LabelTTF<br/>
 30  * cc.LabelTTF objects are slow for js-binding on mobile devices.<br/>
 31  * Consider using cc.LabelAtlas or cc.LabelBMFont instead.<br/>
 32  * @class
 33  * @extends cc.Sprite
 34  */
 35 cc.LabelTTF = cc.Sprite.extend(/** @lends cc.LabelTTF# */{
 36     _dimensions:cc.SizeZero(),
 37     _hAlignment:cc.TEXT_ALIGNMENT_CENTER,
 38     _vAlignment:cc.VERTICAL_TEXT_ALIGNMENT_TOP,
 39     _fontName:"Arial",
 40     _fontSize:0.0,
 41     _string:"",
 42     _fontStyleStr:null,
 43     _colorStyleStr:null,
 44     /**
 45      * Constructor
 46      */
 47     ctor:function () {
 48         this._super();
 49 
 50         this._opacityModifyRGB = false;
 51         this._fontStyleStr = "";
 52         this._colorStyleStr = "";
 53         this._opacity = 255;
 54         this._color = cc.white();
 55         this._setColorStyleStr();
 56     },
 57 
 58     init:function (callsuper) {
 59         if (callsuper) {
 60             return this._super();
 61         }
 62         return this.initWithString([" ", this._fontName, this._fontSize]);
 63     },
 64     /**
 65      * Prints out a description of this class
 66      * @return {String}
 67      */
 68     description:function () {
 69         return "<cc.LabelTTF | FontName =" + this._fontName + " FontSize = " + this._fontSize.toFixed(1) + ">";
 70     },
 71 
 72     setColor:function (color3) {
 73         if ((this._color.r == color3.r) && (this._color.g == color3.g) && (this._color.b == color3.b)) {
 74             return;
 75         }
 76 
 77         this._color = this._colorUnmodified = new cc.Color3B(color3.r, color3.g, color3.b);
 78         this._setColorStyleStr();
 79         this.setNodeDirty();
 80     },
 81 
 82     setOpacity:function (opacity) {
 83         if (this._opacity === opacity) {
 84             return;
 85         }
 86 
 87         this._opacity = opacity;
 88         this._setColorStyleStr();
 89         this.setNodeDirty();
 90     },
 91 
 92     _setColorStyleStr:function () {
 93         this._colorStyleStr = "rgba(" + this._color.r + "," + this._color.g + "," + this._color.b + ", " + this._opacity / 255 + ")";
 94     },
 95 
 96     /**
 97      * changes the string to render
 98      * @warning Changing the string is as expensive as creating a new cc.LabelTTF. To obtain better performance use cc.LabelAtlas
 99      * @param {String} string text for the label
100      */
101     setString:function (string) {
102         if (this._string != string) {
103             this._string = string + "";
104 
105             // Force update
106             if (this._string.length > 0) {
107                 this._updateTTF();
108             }
109         }
110     },
111 
112     /**
113      * returns the text of the label
114      * @return {String}
115      */
116     getString:function () {
117         return this._string;
118     },
119 
120     /**
121      * return Horizontal Alignment of cc.LabelTTF
122      * @return {cc.TEXT_ALIGNMENT_LEFT|cc.TEXT_ALIGNMENT_CENTER|cc.TEXT_ALIGNMENT_RIGHT}
123      */
124     getHorizontalAlignment:function () {
125         return this._hAlignment;
126     },
127 
128     /**
129      * set Horizontal Alignment of cc.LabelTTF
130      * @param {cc.TEXT_ALIGNMENT_LEFT|cc.TEXT_ALIGNMENT_CENTER|cc.TEXT_ALIGNMENT_RIGHT} Horizontal Alignment
131      */
132     setHorizontalAlignment:function (alignment) {
133         if (alignment != this._hAlignment) {
134             this._hAlignment = alignment;
135 
136             // Force update
137             if (this._string.length > 0) {
138                 this._updateTTF();
139             }
140         }
141     },
142 
143     /**
144      * return Vertical Alignment of cc.LabelTTF
145      * @return {cc.VERTICAL_TEXT_ALIGNMENT_TOP|cc.VERTICAL_TEXT_ALIGNMENT_CENTER|cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM}
146      */
147     getVerticalAlignment:function () {
148         return this._vAlignment;
149     },
150 
151     /**
152      * set Vertical Alignment of cc.LabelTTF
153      * @param {cc.VERTICAL_TEXT_ALIGNMENT_TOP|cc.VERTICAL_TEXT_ALIGNMENT_CENTER|cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM} verticalAlignment
154      */
155     setVerticalAlignment:function (verticalAlignment) {
156         if (verticalAlignment != this._vAlignment) {
157             this._vAlignment = verticalAlignment;
158 
159             // Force update
160             if (this._string.length > 0) {
161                 this._updateTTF();
162             }
163         }
164     },
165 
166     /**
167      * return Dimensions of cc.LabelTTF
168      * @return {cc.Size}
169      */
170     getDimensions:function () {
171         return this._dimensions;
172     },
173 
174     /**
175      * set Dimensions of cc.LabelTTF
176      * @param {cc.Size} dim
177      */
178     setDimensions:function (dim) {
179         if (dim.width != this._dimensions.width || dim.height != this._dimensions.height) {
180             this._dimensions = dim;
181 
182             // Force udpate
183             if (this._string.length > 0) {
184                 this._updateTTF();
185             }
186         }
187     },
188 
189     /**
190      * return font size of cc.LabelTTF
191      * @return {Number}
192      */
193     getFontSize:function () {
194         return this._fontSize;
195     },
196 
197     /**
198      * set font size of cc.LabelTTF
199      * @param {Number} fontSize
200      */
201     setFontSize:function (fontSize) {
202         if (this._fontSize != fontSize) {
203             this._fontSize = fontSize;
204 
205             // Force update
206             if (this._string.length > 0) {
207                 this._updateTTF();
208             }
209         }
210     },
211 
212     /**
213      * return font name of cc.LabelTTF
214      * @return {String}
215      */
216     getFontName:function () {
217         return this._fontName;
218     },
219 
220     /**
221      * set font name of cc.LabelTTF
222      * @param {String} fontName
223      */
224     setFontName:function (fontName) {
225         if (this._fontName != fontName) {
226             this._fontName = new String(fontName);
227             // Force update
228             if (this._string.length > 0) {
229                 this._updateTTF();
230             }
231         }
232     },
233 
234     /**
235      * initializes the cc.LabelTTF with a font name, alignment, dimension and font size
236      * @param {String} initialize string
237      * @param {String} fontName
238      * @param {Number} fontSize
239      * @param {cc.Size} dimensions
240      * @param {cc.TEXT_ALIGNMENT_LEFT|cc.TEXT_ALIGNMENT_CENTER|cc.TEXT_ALIGNMENT_RIGHT} alignment
241      * @return {Boolean} return false on error
242      */
243     initWithString:function (arg) {
244         var strInfo = new String(arg[0]), fontName, fontSize, dimensions, hAlignment, vAlignment;
245         cc.Assert(strInfo != null, "cc.LabelTTF.initWithString() label is null");
246         if (arg.length == 6) {
247             fontName = arg[1];
248             fontSize = arg[2];
249             dimensions = arg[3];
250             hAlignment = arg[4];
251             vAlignment = arg[5];
252         } else if (arg.length == 5) {
253             fontName = arg[1];
254             fontSize = arg[2];
255             dimensions = arg[3];
256             hAlignment = arg[4];
257             vAlignment = cc.VERTICAL_TEXT_ALIGNMENT_TOP;
258         } else {
259             fontName = arg[1];
260             fontSize = arg[2];
261             dimensions = cc.size(0, arg[2]);
262             hAlignment = cc.TEXT_ALIGNMENT_LEFT;
263             vAlignment = cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM;
264         }
265 
266         if (this.init(true)) {
267             this._dimensions = cc.size(dimensions.width, dimensions.height);
268             this._fontName = fontName;
269             this._hAlignment = hAlignment;
270             this._vAlignment = vAlignment;
271             this._fontSize = fontSize * cc.CONTENT_SCALE_FACTOR();
272             this.setString(strInfo);
273             this._fontStyleStr = this._fontSize + "px '" + this._fontName + "'";
274             this._updateTTF();
275             return true;
276         }
277         return false;
278     },
279     /**
280      * renders the label
281      * @param {CanvasContext|Null} ctx
282      */
283     draw:function (ctx) {
284         if (cc.renderContextType == cc.CANVAS) {
285             var context = ctx || cc.renderContext;
286             if (this._flipX)
287                 context.scale(-1, 1);
288 
289             if (this._flipY)
290                 context.scale(1, -1);
291 
292             //this is fillText for canvas
293             context.fillStyle = this._colorStyleStr;
294 
295             if (context.font != this._fontStyleStr)
296                 context.font = this._fontStyleStr;
297 
298             if (((this._contentSize.width > this._dimensions.width) || this._string.indexOf("\n") > -1) && this._dimensions.width !== 0) {
299                 context.textBaseline = cc.LabelTTF._textBaseline[this._vAlignment];
300                 context.textAlign = cc.LabelTTF._textAlign[this._hAlignment];
301                 this._wrapText(context, this._string,
302                     -this._dimensions.width * this._anchorPoint.x,
303                     this._dimensions.height * this._anchorPoint.y,
304                     this._dimensions.width,
305                     this._dimensions.height,
306                     this._fontSize * 1.2);
307             } else if (this._dimensions.width == 0) {
308                 context.textBaseline = "bottom";
309                 context.textAlign = "left";
310                 if(!this._string.indexOf){
311                     var z = 0;
312                 }
313 
314                 if (this._string.indexOf("\n") > -1)
315                     this._multiLineText(context);
316                  else
317                    context.fillText(this._string, -this._contentSize.width * this._anchorPoint.x, this._contentSize.height * this._anchorPoint.y);
318             } else {
319                 context.textBaseline = cc.LabelTTF._textBaseline[this._vAlignment];
320                 context.textAlign = cc.LabelTTF._textAlign[this._hAlignment];
321                 var xOffset = 0, yOffset = 0;
322                 if (this._hAlignment == cc.TEXT_ALIGNMENT_RIGHT)
323                     xOffset = this._dimensions.width;
324                 if (this._hAlignment == cc.TEXT_ALIGNMENT_CENTER)
325                     xOffset = this._dimensions.width / 2;
326 
327                 if (this._vAlignment == cc.VERTICAL_TEXT_ALIGNMENT_TOP)
328                     yOffset = -this._dimensions.height;
329                 if (this._vAlignment == cc.VERTICAL_TEXT_ALIGNMENT_CENTER)
330                     yOffset = -this._dimensions.height / 2;
331 
332                 context.fillText(this._string, -this._dimensions.width * this._anchorPoint.x + xOffset,
333                     this._dimensions.height * this._anchorPoint.y + yOffset);
334             }
335 
336             cc.INCREMENT_GL_DRAWS(1);
337         }
338     },
339 
340     _multiLineText:function(context){
341         var rowHeight = this._fontSize * 1.2;
342         var tmpWords = this._string.split("\n");
343         var lineHeight = tmpWords.length;
344         var splitStrWidthArr = [];
345         var maxLineWidth = 0;
346         for(var i = 0; i< lineHeight; i++){
347             splitStrWidthArr[i] = context.measureText(tmpWords[i]).width;
348             if(splitStrWidthArr[i] > maxLineWidth)
349                 maxLineWidth = splitStrWidthArr[i];
350         }
351 
352         var centerPoint = cc.p(maxLineWidth / 2,(lineHeight * rowHeight) / 2);
353         for (i = 0; i < lineHeight; i++) {
354             var xOffset = -splitStrWidthArr[i]/2;
355             if (this._hAlignment == cc.TEXT_ALIGNMENT_RIGHT)
356                 xOffset = centerPoint.x - maxLineWidth;
357             if (this._hAlignment == cc.TEXT_ALIGNMENT_CENTER)
358                 xOffset = maxLineWidth - splitStrWidthArr[i];
359             context.fillText(tmpWords[i], xOffset, i * rowHeight - centerPoint.y + rowHeight/2);
360         }
361     },
362 
363     _wrapText:function (context, text, x, y, maxWidth, maxHeight, lineHeight) {
364         var num = this._lineCount() - 1;
365         var xOffset = 0, yOffset = 0;
366         if (this._hAlignment === cc.TEXT_ALIGNMENT_RIGHT)
367             xOffset = maxWidth;
368         if (this._hAlignment === cc.TEXT_ALIGNMENT_CENTER)
369             xOffset = maxWidth / 2;
370 
371         if (this._vAlignment === cc.VERTICAL_TEXT_ALIGNMENT_TOP)
372             yOffset = -maxHeight;
373         if (this._vAlignment === cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM)
374             yOffset = -lineHeight * num;
375         if (this._vAlignment === cc.VERTICAL_TEXT_ALIGNMENT_CENTER)
376             yOffset = -maxHeight / 2 - (lineHeight * num / 2);
377 
378         var tmpWords = text.split("\n");
379         for (var j = 0; j < tmpWords.length; j++) {
380             var jOffset = j * lineHeight;
381             var words = tmpWords[j].split(" ");
382             var line = "";
383 
384             for (var n = 0; n < words.length; n++) {
385                 var testLine = line + words[n] + " ";
386                 var testWidth = context.measureText(testLine).width - context.measureText(" ").width;
387                 if (testWidth >= maxWidth) {
388                     context.fillText(line, x + xOffset, y + yOffset + jOffset);
389                     y += lineHeight;
390                     line = words[n] + " ";
391                 } else {
392                     line = testLine;
393                 }
394                 if (n == words.length - 1) {
395                     context.fillText(line, x + xOffset, y + yOffset + jOffset);
396                 }
397             }
398         }
399     },
400 
401     _lineCount:function () {
402         if (this._dimensions.width == 0) {
403             return 1;
404         }
405         var context = cc.renderContext;
406         var words = this._string.split(" ");
407         var line = "", num = 0;
408         cc.renderContext.save();
409         for (var n = 0; n < words.length; n++) {
410             var tmpLine = line + words[n] + " ";
411             var tmpWidth = context.measureText(tmpLine).width - context.measureText(" ").width;
412             if (tmpWidth >= this._dimensions.width) {
413                 num++;
414                 line = words[n] + " ";
415             }
416             else {
417                 line = tmpLine;
418             }
419             if (n == words.length - 1) {
420                 num++;
421             }
422         }
423         cc.renderContext.restore();
424         return num;
425     },
426 
427     _updateTTF:function () {
428         var oldFontStr = cc.renderContext.font;
429         this._fontStyleStr = this._fontSize + "px '" + this._fontName + "'";
430         cc.renderContext.font = this._fontStyleStr;
431         var dim = cc.renderContext.measureText(this._string);
432         this.setContentSize(cc.size(dim.width, this._fontSize));
433         cc.renderContext.font = oldFontStr;
434         this.setNodeDirty();
435     }
436 });
437 
438 cc.LabelTTF._textAlign = ["left", "center", "right"];
439 
440 cc.LabelTTF._textBaseline = ["top", "middle", "bottom"];
441 
442 /**
443  * creates a cc.LabelTTF from a fontname, alignment, dimension and font size
444  * @param {String} label
445  * @param {String} fontName
446  * @param {Number} fontSize
447  * @param {cc.Size} dimensions
448  * @param {cc.TEXT_ALIGNMENT_LEFT|cc.TEXT_ALIGNMENT_CENTER|cc.TEXT_ALIGNMENT_RIGHT} alignment
449  * @return {cc.LabelTTF|Null}
450  * @example
451  * // Example
452  * var myLabel = cc.LabelTTF.create('label text',  'Times New Roman', 32, cc.size(32,16), cc.TEXT_ALIGNMENT_LEFT);
453  */
454 cc.LabelTTF.create = function (/* Multi arguments */) {
455     var ret = new cc.LabelTTF();
456     if (ret.initWithString(arguments)) {
457         return ret;
458     }
459     return null;
460 };
461 
462 cc.LabelTTF.node = function () {
463     return cc.LabelTTF.create();
464 };
465