/**************************************************************************** Copyright (c) 2008-2010 Ricardo Quesada Copyright (c) 2011-2012 cocos2d-x.org Copyright (c) 2013-2014 Chukong Technologies Inc. http://www.cocos2d-x.org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ****************************************************************************/ /** *

cc.LabelTTF is a subclass of cc.TextureNode that knows how to render text labels with system font or a ttf font file
* All features from cc.Sprite are valid in cc.LabelTTF
* cc.LabelTTF objects are slow for js-binding on mobile devices.
* Consider using cc.LabelAtlas or cc.LabelBMFont instead.
* You can create a cc.LabelTTF from a font name, alignment, dimension and font size or a cc.FontDefinition object.

* @class * @extends cc.Sprite * * @param {String} text * @param {String|cc.FontDefinition} [fontName="Arial"] * @param {Number} [fontSize=16] * @param {cc.Size} [dimensions=cc.size(0,0)] * @param {Number} [hAlignment=cc.TEXT_ALIGNMENT_LEFT] * @param {Number} [vAlignment=cc.VERTICAL_TEXT_ALIGNMENT_TOP] * @example * var myLabel = new cc.LabelTTF('label text', 'Times New Roman', 32, cc.size(320,32), cc.TEXT_ALIGNMENT_LEFT); * * var fontDef = new cc.FontDefinition(); * fontDef.fontName = "Arial"; * fontDef.fontSize = "32"; * var myLabel = new cc.LabelTTF('label text', fontDef); * * @property {String} string - Content string of label * @property {Number} textAlign - Horizontal Alignment of label: cc.TEXT_ALIGNMENT_LEFT|cc.TEXT_ALIGNMENT_CENTER|cc.TEXT_ALIGNMENT_RIGHT * @property {Number} verticalAlign - Vertical Alignment of label: cc.VERTICAL_TEXT_ALIGNMENT_TOP|cc.VERTICAL_TEXT_ALIGNMENT_CENTER|cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM * @property {Number} fontSize - Font size of label * @property {String} fontName - Font name of label * @property {String} font - The label font with a style string: e.g. "18px Verdana" * @property {Number} boundingWidth - Width of the bounding box of label, the real content width is limited by boundingWidth * @property {Number} boundingHeight - Height of the bounding box of label, the real content height is limited by boundingHeight * @property {cc.Color} fillStyle - The fill color * @property {cc.Color} strokeStyle - The stroke color * @property {Number} lineWidth - The line width for stroke * @property {Number} shadowOffsetX - The x axis offset of shadow * @property {Number} shadowOffsetY - The y axis offset of shadow * @property {Number} shadowOpacity - The opacity of shadow * @property {Number} shadowBlur - The blur size of shadow */ cc.LabelTTF = cc.Sprite.extend(/** @lends cc.LabelTTF# */{ _dimensions: null, _hAlignment: cc.TEXT_ALIGNMENT_CENTER, _vAlignment: cc.VERTICAL_TEXT_ALIGNMENT_TOP, _fontName: null, _fontSize: 0.0, _string: "", _originalText: null, _isMultiLine: false, _fontStyleStr: null, // font shadow _shadowEnabled: false, _shadowOffset: null, _shadowOpacity: 0, _shadowBlur: 0, _shadowColorStr: null, // font stroke _strokeEnabled: false, _strokeColor: null, _strokeSize: 0, _strokeColorStr: null, // font tint _textFillColor: null, _fillColorStr: null, _strokeShadowOffsetX: 0, _strokeShadowOffsetY: 0, _needUpdateTexture: false, _labelCanvas: null, _labelContext: null, _lineWidths: null, _className: "LabelTTF", /** * Initializes the cc.LabelTTF with a font name, alignment, dimension and font size, do not call it by yourself, you should pass the correct arguments in constructor to initialize the label. * @param {String} label string * @param {String} fontName * @param {Number} fontSize * @param {cc.Size} [dimensions=] * @param {Number} [hAlignment=] * @param {Number} [vAlignment=] * @return {Boolean} return false on error */ initWithString: function (label, fontName, fontSize, dimensions, hAlignment, vAlignment) { var strInfo; if (label) strInfo = label + ""; else strInfo = ""; fontSize = fontSize || 16; dimensions = dimensions || cc.size(0, 0/*fontSize*/); hAlignment = hAlignment || cc.TEXT_ALIGNMENT_LEFT; vAlignment = vAlignment || cc.VERTICAL_TEXT_ALIGNMENT_TOP; this._opacityModifyRGB = false; this._dimensions = cc.size(dimensions.width, dimensions.height); this._fontName = fontName || "Arial"; this._hAlignment = hAlignment; this._vAlignment = vAlignment; //this._fontSize = (cc._renderType === cc._RENDER_TYPE_CANVAS) ? fontSize : fontSize * cc.contentScaleFactor(); this._fontSize = fontSize; this._fontStyleStr = this._fontSize + "px '" + fontName + "'"; this._fontClientHeight = cc.LabelTTF.__getFontHeightByDiv(fontName, this._fontSize); this.string = strInfo; this._setColorsString(); this._updateTexture(); this._needUpdateTexture = false; return true; }, ctor: function (text, fontName, fontSize, dimensions, hAlignment, vAlignment) { cc.Sprite.prototype.ctor.call(this); this._dimensions = cc.size(0, 0); this._hAlignment = cc.TEXT_ALIGNMENT_LEFT; this._vAlignment = cc.VERTICAL_TEXT_ALIGNMENT_TOP; this._opacityModifyRGB = false; this._fontStyleStr = ""; this._fontName = "Arial"; this._isMultiLine = false; this._shadowEnabled = false; this._shadowOffset = cc.p(0, 0); this._shadowOpacity = 0; this._shadowBlur = 0; this._shadowColorStr = "rgba(128, 128, 128, 0.5)"; this._strokeEnabled = false; this._strokeColor = cc.color(255, 255, 255, 255); this._strokeSize = 0; this._strokeColorStr = ""; this._textFillColor = cc.color(255, 255, 255, 255); this._fillColorStr = "rgba(255,255,255,1)"; this._strokeShadowOffsetX = 0; this._strokeShadowOffsetY = 0; this._needUpdateTexture = false; this._lineWidths = []; this._setColorsString(); if (fontName && fontName instanceof cc.FontDefinition) { this.initWithStringAndTextDefinition(text, fontName); } else { cc.LabelTTF.prototype.initWithString.call(this, text, fontName, fontSize, dimensions, hAlignment, vAlignment); } }, init: function () { return this.initWithString(" ", this._fontName, this._fontSize); }, _measureConfig: function () { this._getLabelContext().font = this._fontStyleStr; }, _measure: function (text) { return this._getLabelContext().measureText(text).width; }, description: function () { return ""; }, setColor: null, _setColorsString: null, updateDisplayedColor: null, setOpacity: null, updateDisplayedOpacity: null, updateDisplayedOpacityForCanvas: function (parentOpacity) { cc.Node.prototype.updateDisplayedOpacity.call(this, parentOpacity); this._setColorsString(); }, /** * Returns the text of the label * @return {String} */ getString: function () { return this._string; }, /** * Returns Horizontal Alignment of cc.LabelTTF * @return {cc.TEXT_ALIGNMENT_LEFT|cc.TEXT_ALIGNMENT_CENTER|cc.TEXT_ALIGNMENT_RIGHT} */ getHorizontalAlignment: function () { return this._hAlignment; }, /** * Returns Vertical Alignment of cc.LabelTTF * @return {cc.VERTICAL_TEXT_ALIGNMENT_TOP|cc.VERTICAL_TEXT_ALIGNMENT_CENTER|cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM} */ getVerticalAlignment: function () { return this._vAlignment; }, /** * Returns the dimensions of cc.LabelTTF, the dimension is the maximum size of the label, set it so that label will automatically change lines when necessary. * @see cc.LabelTTF#setDimensions, cc.LabelTTF#boundingWidth and cc.LabelTTF#boundingHeight * @return {cc.Size} */ getDimensions: function () { return cc.size(this._dimensions); }, /** * Returns font size of cc.LabelTTF * @return {Number} */ getFontSize: function () { return this._fontSize; }, /** * Returns font name of cc.LabelTTF * @return {String} */ getFontName: function () { return this._fontName; }, /** * Initializes the CCLabelTTF with a font name, alignment, dimension and font size, do not call it by yourself, you should pass the correct arguments in constructor to initialize the label. * @param {String} text * @param {cc.FontDefinition} textDefinition * @return {Boolean} */ initWithStringAndTextDefinition: null, /** * Sets the text definition used by this label * @param {cc.FontDefinition} theDefinition */ setTextDefinition: function (theDefinition) { if (theDefinition) this._updateWithTextDefinition(theDefinition, true); }, /** * Extract the text definition used by this label * @return {cc.FontDefinition} */ getTextDefinition: function () { return this._prepareTextDefinition(false); }, /** * Enable or disable shadow for the label * @param {Number} shadowOffsetX The x axis offset of the shadow * @param {Number} shadowOffsetY The y axis offset of the shadow * @param {Number} shadowOpacity The opacity of the shadow (0 to 1) * @param {Number} shadowBlur The blur size of the shadow */ enableShadow: function (shadowOffsetX, shadowOffsetY, shadowOpacity, shadowBlur) { shadowOpacity = shadowOpacity || 0.5; if (false === this._shadowEnabled) this._shadowEnabled = true; var locShadowOffset = this._shadowOffset; if (locShadowOffset && (locShadowOffset.x != shadowOffsetX) || (locShadowOffset._y != shadowOffsetY)) { locShadowOffset.x = shadowOffsetX; locShadowOffset.y = shadowOffsetY; } if (this._shadowOpacity != shadowOpacity) { this._shadowOpacity = shadowOpacity; } this._setColorsString(); if (this._shadowBlur != shadowBlur) this._shadowBlur = shadowBlur; this._needUpdateTexture = true; }, _getShadowOffsetX: function () { return this._shadowOffset.x; }, _setShadowOffsetX: function (x) { if (false === this._shadowEnabled) this._shadowEnabled = true; if (this._shadowOffset.x != x) { this._shadowOffset.x = x; this._needUpdateTexture = true; } }, _getShadowOffsetY: function () { return this._shadowOffset._y; }, _setShadowOffsetY: function (y) { if (false === this._shadowEnabled) this._shadowEnabled = true; if (this._shadowOffset._y != y) { this._shadowOffset._y = y; this._needUpdateTexture = true; } }, _getShadowOffset: function () { return cc.p(this._shadowOffset.x, this._shadowOffset.y); }, _setShadowOffset: function (offset) { if (false === this._shadowEnabled) this._shadowEnabled = true; if (this._shadowOffset.x != offset.x || this._shadowOffset.y != offset.y) { this._shadowOffset.x = offset.x; this._shadowOffset.y = offset.y; this._needUpdateTexture = true; } }, _getShadowOpacity: function () { return this._shadowOpacity; }, _setShadowOpacity: function (shadowOpacity) { if (false === this._shadowEnabled) this._shadowEnabled = true; if (this._shadowOpacity != shadowOpacity) { this._shadowOpacity = shadowOpacity; this._setColorsString(); this._needUpdateTexture = true; } }, _getShadowBlur: function () { return this._shadowBlur; }, _setShadowBlur: function (shadowBlur) { if (false === this._shadowEnabled) this._shadowEnabled = true; if (this._shadowBlur != shadowBlur) { this._shadowBlur = shadowBlur; this._needUpdateTexture = true; } }, /** * Disable shadow rendering */ disableShadow: function () { if (this._shadowEnabled) { this._shadowEnabled = false; this._needUpdateTexture = true; } }, /** * Enable label stroke with stroke parameters * @param {cc.Color} strokeColor The color of stroke * @param {Number} strokeSize The size of stroke */ enableStroke: function (strokeColor, strokeSize) { if (this._strokeEnabled === false) this._strokeEnabled = true; var locStrokeColor = this._strokeColor; if ((locStrokeColor.r !== strokeColor.r) || (locStrokeColor.g !== strokeColor.g) || (locStrokeColor.b !== strokeColor.b)) { locStrokeColor.r = strokeColor.r; locStrokeColor.g = strokeColor.g; locStrokeColor.b = strokeColor.b; this._setColorsString(); } if (this._strokeSize !== strokeSize) this._strokeSize = strokeSize || 0; this._needUpdateTexture = true; }, _getStrokeStyle: function () { return this._strokeColor; }, _setStrokeStyle: function (strokeStyle) { if (this._strokeEnabled === false) this._strokeEnabled = true; var locStrokeColor = this._strokeColor; if ((locStrokeColor.r !== strokeStyle.r) || (locStrokeColor.g !== strokeStyle.g) || (locStrokeColor.b !== strokeStyle.b)) { locStrokeColor.r = strokeStyle.r; locStrokeColor.g = strokeStyle.g; locStrokeColor.b = strokeStyle.b; this._setColorsString(); this._needUpdateTexture = true; } }, _getLineWidth: function () { return this._strokeSize; }, _setLineWidth: function (lineWidth) { if (this._strokeEnabled === false) this._strokeEnabled = true; if (this._strokeSize !== lineWidth) { this._strokeSize = lineWidth || 0; this._needUpdateTexture = true; } }, /** * Disable label stroke */ disableStroke: function () { if (this._strokeEnabled) { this._strokeEnabled = false; this._needUpdateTexture = true; } }, /** * Sets the text fill color * @function * @param {cc.Color} fillColor The fill color of the label */ setFontFillColor: null, _getFillStyle: function () { return this._textFillColor; }, //set the text definition for this label _updateWithTextDefinition: function (textDefinition, mustUpdateTexture) { if (textDefinition.fontDimensions) { this._dimensions.width = textDefinition.boundingWidth; this._dimensions.height = textDefinition.boundingHeight; } else { this._dimensions.width = 0; this._dimensions.height = 0; } this._hAlignment = textDefinition.textAlign; this._vAlignment = textDefinition.verticalAlign; this._fontName = textDefinition.fontName; this._fontSize = textDefinition.fontSize || 12; this._fontStyleStr = this._fontSize + "px '" + this._fontName + "'"; this._fontClientHeight = cc.LabelTTF.__getFontHeightByDiv(this._fontName, this._fontSize); // shadow if (textDefinition.shadowEnabled) this.enableShadow(textDefinition.shadowOffsetX, textDefinition.shadowOffsetY, textDefinition.shadowOpacity, textDefinition.shadowBlur); // stroke if (textDefinition.strokeEnabled) this.enableStroke(textDefinition.strokeStyle, textDefinition.lineWidth); // fill color this.setFontFillColor(textDefinition.fillStyle); if (mustUpdateTexture) this._updateTexture(); }, _prepareTextDefinition: function (adjustForResolution) { var texDef = new cc.FontDefinition(); if (adjustForResolution) { //texDef.fontSize = (cc._renderType === cc._RENDER_TYPE_CANVAS) ? this._fontSize : this._fontSize * cc.contentScaleFactor(); texDef.fontSize = this._fontSize; texDef.boundingWidth = cc.contentScaleFactor() * this._dimensions.width; texDef.boundingHeight = cc.contentScaleFactor() * this._dimensions.height; } else { texDef.fontSize = this._fontSize; texDef.boundingWidth = this._dimensions.width; texDef.boundingHeight = this._dimensions.height; } texDef.fontName = this._fontName; texDef.textAlign = this._hAlignment; texDef.verticalAlign = this._vAlignment; // stroke if (this._strokeEnabled) { texDef.strokeEnabled = true; var locStrokeColor = this._strokeColor; texDef.strokeStyle = cc.color(locStrokeColor.r, locStrokeColor.g, locStrokeColor.b); texDef.lineWidth = this._strokeSize; } else texDef.strokeEnabled = false; // shadow if (this._shadowEnabled) { texDef.shadowEnabled = true; texDef.shadowBlur = this._shadowBlur; texDef.shadowOpacity = this._shadowOpacity; texDef.shadowOffsetX = (adjustForResolution ? cc.contentScaleFactor() : 1) * this._shadowOffset.x; texDef.shadowOffsetY = (adjustForResolution ? cc.contentScaleFactor() : 1) * this._shadowOffset.y; } else texDef._shadowEnabled = false; // text tint var locTextFillColor = this._textFillColor; texDef.fillStyle = cc.color(locTextFillColor.r, locTextFillColor.g, locTextFillColor.b); return texDef; }, _fontClientHeight: 18, /** * Changes the text content of the label * @warning Changing the string is as expensive as creating a new cc.LabelTTF. To obtain better performance use cc.LabelAtlas * @param {String} text Text content for the label */ setString: function (text) { text = String(text); if (this._originalText != text) { this._originalText = text + ""; this._updateString(); // Force update this._needUpdateTexture = true; } }, _updateString: function () { this._string = this._originalText; }, /** * Sets Horizontal Alignment of cc.LabelTTF * @param {cc.TEXT_ALIGNMENT_LEFT|cc.TEXT_ALIGNMENT_CENTER|cc.TEXT_ALIGNMENT_RIGHT} alignment Horizontal Alignment */ setHorizontalAlignment: function (alignment) { if (alignment !== this._hAlignment) { this._hAlignment = alignment; // Force update this._needUpdateTexture = true; } }, /** * Sets Vertical Alignment of cc.LabelTTF * @param {cc.VERTICAL_TEXT_ALIGNMENT_TOP|cc.VERTICAL_TEXT_ALIGNMENT_CENTER|cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM} verticalAlignment */ setVerticalAlignment: function (verticalAlignment) { if (verticalAlignment != this._vAlignment) { this._vAlignment = verticalAlignment; // Force update this._needUpdateTexture = true; } }, /** * Set Dimensions of cc.LabelTTF, the dimension is the maximum size of the label, set it so that label will automatically change lines when necessary. * @param {cc.Size|Number} dim dimensions or width of dimensions * @param {Number} [height] height of dimensions */ setDimensions: function (dim, height) { var width; if(height === undefined){ width = dim.width; height = dim.height; }else width = dim; if (width != this._dimensions.width || height != this._dimensions.height) { this._dimensions.width = width; this._dimensions.height = height; this._updateString(); // Force udpate this._needUpdateTexture = true; } }, _getBoundingWidth: function () { return this._dimensions.width; }, _setBoundingWidth: function (width) { if (width != this._dimensions.width) { this._dimensions.width = width; this._updateString(); // Force udpate this._needUpdateTexture = true; } }, _getBoundingHeight: function () { return this._dimensions.height; }, _setBoundingHeight: function (height) { if (height != this._dimensions.height) { this._dimensions.height = height; this._updateString(); // Force udpate this._needUpdateTexture = true; } }, /** * Sets font size of cc.LabelTTF * @param {Number} fontSize */ setFontSize: function (fontSize) { if (this._fontSize !== fontSize) { this._fontSize = fontSize; this._fontStyleStr = fontSize + "px '" + this._fontName + "'"; this._fontClientHeight = cc.LabelTTF.__getFontHeightByDiv(this._fontName, fontSize); // Force update this._needUpdateTexture = true; } }, /** * Sets font name of cc.LabelTTF * @param {String} fontName */ setFontName: function (fontName) { if (this._fontName && this._fontName != fontName) { this._fontName = fontName; this._fontStyleStr = this._fontSize + "px '" + fontName + "'"; this._fontClientHeight = cc.LabelTTF.__getFontHeightByDiv(fontName, this._fontSize); // Force update this._needUpdateTexture = true; } }, _getFont: function () { return this._fontStyleStr; }, _setFont: function (fontStyle) { var res = cc.LabelTTF._fontStyleRE.exec(fontStyle); if (res) { this._fontSize = parseInt(res[1]); this._fontName = res[2]; this._fontStyleStr = fontStyle; this._fontClientHeight = cc.LabelTTF.__getFontHeightByDiv(this._fontName, this._fontSize); // Force update this._needUpdateTexture = true; } }, _drawTTFInCanvas: function (context) { if (!context) return; var locStrokeShadowOffsetX = this._strokeShadowOffsetX, locStrokeShadowOffsetY = this._strokeShadowOffsetY; var locContentSizeHeight = this._contentSize.height - locStrokeShadowOffsetY, locVAlignment = this._vAlignment, locHAlignment = this._hAlignment, locFontHeight = this._fontClientHeight, locStrokeSize = this._strokeSize; context.setTransform(1, 0, 0, 1, 0 + locStrokeShadowOffsetX * 0.5, locContentSizeHeight + locStrokeShadowOffsetY * 0.5); //this is fillText for canvas if (context.font != this._fontStyleStr) context.font = this._fontStyleStr; context.fillStyle = this._fillColorStr; var xOffset = 0, yOffset = 0; //stroke style setup var locStrokeEnabled = this._strokeEnabled; if (locStrokeEnabled) { context.lineWidth = locStrokeSize * 2; context.strokeStyle = this._strokeColorStr; } //shadow style setup if (this._shadowEnabled) { var locShadowOffset = this._shadowOffset; context.shadowColor = this._shadowColorStr; context.shadowOffsetX = locShadowOffset.x; context.shadowOffsetY = -locShadowOffset.y; context.shadowBlur = this._shadowBlur; } context.textBaseline = cc.LabelTTF._textBaseline[locVAlignment]; context.textAlign = cc.LabelTTF._textAlign[locHAlignment]; var locContentWidth = this._contentSize.width - locStrokeShadowOffsetX; if (locHAlignment === cc.TEXT_ALIGNMENT_RIGHT) xOffset += locContentWidth; else if (locHAlignment === cc.TEXT_ALIGNMENT_CENTER) xOffset += locContentWidth / 2; else xOffset += 0; if (this._isMultiLine) { var locStrLen = this._strings.length; if (locVAlignment === cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM) yOffset = locFontHeight + locContentSizeHeight - locFontHeight * locStrLen; else if (locVAlignment === cc.VERTICAL_TEXT_ALIGNMENT_CENTER) yOffset = locFontHeight / 2 + (locContentSizeHeight - locFontHeight * locStrLen) / 2; for (var i = 0; i < locStrLen; i++) { var line = this._strings[i]; var tmpOffsetY = -locContentSizeHeight + (locFontHeight * i) + yOffset; if (locStrokeEnabled) context.strokeText(line, xOffset, tmpOffsetY); context.fillText(line, xOffset, tmpOffsetY); } } else { if (locVAlignment === cc.VERTICAL_TEXT_ALIGNMENT_BOTTOM) { if (locStrokeEnabled) context.strokeText(this._string, xOffset, yOffset); context.fillText(this._string, xOffset, yOffset); } else if (locVAlignment === cc.VERTICAL_TEXT_ALIGNMENT_TOP) { yOffset -= locContentSizeHeight; if (locStrokeEnabled) context.strokeText(this._string, xOffset, yOffset); context.fillText(this._string, xOffset, yOffset); } else { yOffset -= locContentSizeHeight * 0.5; if (locStrokeEnabled) context.strokeText(this._string, xOffset, yOffset); context.fillText(this._string, xOffset, yOffset); } } }, _getLabelContext: function () { if (this._labelContext) return this._labelContext; if (!this._labelCanvas) { var locCanvas = cc.newElement("canvas"); var labelTexture = new cc.Texture2D(); labelTexture.initWithElement(locCanvas); this.texture = labelTexture; this._labelCanvas = locCanvas; } this._labelContext = this._labelCanvas.getContext("2d"); return this._labelContext; }, _checkWarp: function(strArr, i, maxWidth){ var text = strArr[i]; var allWidth = this._measure(text); if(allWidth > maxWidth && text.length > 1){ var fuzzyLen = text.length * ( maxWidth / allWidth ) | 0; var tmpText = text.substr(fuzzyLen); var width = allWidth - this._measure(tmpText); var sLine; var pushNum = 0; //Increased while cycle maximum ceiling. default 100 time var checkWhile = 0; //Exceeded the size while(width > maxWidth && checkWhile++ < 100){ fuzzyLen *= maxWidth / width; fuzzyLen = fuzzyLen | 0; tmpText = text.substr(fuzzyLen); width = allWidth - this._measure(tmpText); } checkWhile = 0; //Find the truncation point while(width < maxWidth && checkWhile++ < 100){ if(tmpText){ var exec = cc.LabelTTF._wordRex.exec(tmpText); pushNum = exec ? exec[0].length : 1; sLine = tmpText; } fuzzyLen = fuzzyLen + pushNum; tmpText = text.substr(fuzzyLen); width = allWidth - this._measure(tmpText); } fuzzyLen -= pushNum; var sText = text.substr(0, fuzzyLen); //symbol in the first if(cc.LabelTTF.wrapInspection){ if(cc.LabelTTF._symbolRex.test(sLine || tmpText)){ var result = cc.LabelTTF._lastWordRex.exec(sText); fuzzyLen -= result ? result[0].length : 0; sLine = text.substr(fuzzyLen); sText = text.substr(0, fuzzyLen); } } //To judge whether a English words are truncated if(cc.LabelTTF._firsrEnglish.test(sLine)){ var result = cc.LabelTTF._lastEnglish.exec(sText); if(result && sText !== result[0]){ fuzzyLen -= result[0].length; sLine = text.substr(fuzzyLen); sText = text.substr(0, fuzzyLen); } } strArr[i] = sLine || tmpText; strArr.splice(i, 0, sText); } }, _updateTTF: function () { var locDimensionsWidth = this._dimensions.width, i, strLength; var locLineWidth = this._lineWidths; locLineWidth.length = 0; this._isMultiLine = false; this._measureConfig(); if (locDimensionsWidth !== 0) { // Content processing this._strings = this._string.split('\n'); for(i = 0; i < this._strings.length; i++){ this._checkWarp(this._strings, i, locDimensionsWidth); } } else { this._strings = this._string.split('\n'); for (i = 0, strLength = this._strings.length; i < strLength; i++) { locLineWidth.push(this._measure(this._strings[i])); } } if (this._strings.length > 0) this._isMultiLine = true; var locSize, locStrokeShadowOffsetX = 0, locStrokeShadowOffsetY = 0; if (this._strokeEnabled) locStrokeShadowOffsetX = locStrokeShadowOffsetY = this._strokeSize * 2; if (this._shadowEnabled) { var locOffsetSize = this._shadowOffset; locStrokeShadowOffsetX += Math.abs(locOffsetSize.x) * 2; locStrokeShadowOffsetY += Math.abs(locOffsetSize.y) * 2; } //get offset for stroke and shadow if (locDimensionsWidth === 0) { if (this._isMultiLine) locSize = cc.size(0 | (Math.max.apply(Math, locLineWidth) + locStrokeShadowOffsetX), 0 | ((this._fontClientHeight * this._strings.length) + locStrokeShadowOffsetY)); else locSize = cc.size(0 | (this._measure(this._string) + locStrokeShadowOffsetX), 0 | (this._fontClientHeight + locStrokeShadowOffsetY)); } else { if (this._dimensions.height === 0) { if (this._isMultiLine) locSize = cc.size(0 | (locDimensionsWidth + locStrokeShadowOffsetX), 0 | ((this._fontClientHeight * this._strings.length) + locStrokeShadowOffsetY)); else locSize = cc.size(0 | (locDimensionsWidth + locStrokeShadowOffsetX), 0 | (this._fontClientHeight + locStrokeShadowOffsetY)); } else { //dimension is already set, contentSize must be same as dimension locSize = cc.size(0 | (locDimensionsWidth + locStrokeShadowOffsetX), 0 | (this._dimensions.height + locStrokeShadowOffsetY)); } } this.setContentSize(locSize); this._strokeShadowOffsetX = locStrokeShadowOffsetX; this._strokeShadowOffsetY = locStrokeShadowOffsetY; // need computing _anchorPointInPoints var locAP = this._anchorPoint; this._anchorPointInPoints.x = (locStrokeShadowOffsetX * 0.5) + ((locSize.width - locStrokeShadowOffsetX) * locAP.x); this._anchorPointInPoints.y = (locStrokeShadowOffsetY * 0.5) + ((locSize.height - locStrokeShadowOffsetY) * locAP.y); }, /** * Returns the actual content size of the label, the content size is the real size that the label occupied while dimension is the outer bounding box of the label. * @returns {cc.Size} The content size */ getContentSize: function () { if (this._needUpdateTexture) this._updateTTF(); return cc.Sprite.prototype.getContentSize.call(this); }, _getWidth: function () { if (this._needUpdateTexture) this._updateTTF(); return cc.Sprite.prototype._getWidth.call(this); }, _getHeight: function () { if (this._needUpdateTexture) this._updateTTF(); return cc.Sprite.prototype._getHeight.call(this); }, _updateTexture: function () { var locContext = this._getLabelContext(), locLabelCanvas = this._labelCanvas; var locContentSize = this._contentSize; if (this._string.length === 0) { locLabelCanvas.width = 1; locLabelCanvas.height = locContentSize.height || 1; this._texture && this._texture.handleLoadedTexture(); this.setTextureRect(cc.rect(0, 0, 1, locContentSize.height)); return true; } //set size for labelCanvas locContext.font = this._fontStyleStr; this._updateTTF(); var width = locContentSize.width, height = locContentSize.height; var flag = locLabelCanvas.width == width && locLabelCanvas.height == height; locLabelCanvas.width = width; locLabelCanvas.height = height; if (flag) locContext.clearRect(0, 0, width, height); //draw text to labelCanvas this._drawTTFInCanvas(locContext); this._texture && this._texture.handleLoadedTexture(); this.setTextureRect(cc.rect(0, 0, width, height)); return true; }, visit: function (ctx) { if (!this._string || this._string == "") return; if (this._needUpdateTexture) { this._needUpdateTexture = false; this._updateTexture(); } var context = ctx || cc._renderContext; cc.Sprite.prototype.visit.call(this, context); }, draw: null, _setTextureCoords: function (rect) { var tex = this._batchNode ? this.textureAtlas.texture : this._texture; if (!tex) return; var atlasWidth = tex.pixelsWidth; var atlasHeight = tex.pixelsHeight; var left, right, top, bottom, tempSwap, locQuad = this._quad; if (this._rectRotated) { if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) { left = (2 * rect.x + 1) / (2 * atlasWidth); right = left + (rect.height * 2 - 2) / (2 * atlasWidth); top = (2 * rect.y + 1) / (2 * atlasHeight); bottom = top + (rect.width * 2 - 2) / (2 * atlasHeight); } else { left = rect.x / atlasWidth; right = (rect.x + rect.height) / atlasWidth; top = rect.y / atlasHeight; bottom = (rect.y + rect.width) / atlasHeight; }// CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL if (this._flippedX) { tempSwap = top; top = bottom; bottom = tempSwap; } if (this._flippedY) { tempSwap = left; left = right; right = tempSwap; } locQuad.bl.texCoords.u = left; locQuad.bl.texCoords.v = top; locQuad.br.texCoords.u = left; locQuad.br.texCoords.v = bottom; locQuad.tl.texCoords.u = right; locQuad.tl.texCoords.v = top; locQuad.tr.texCoords.u = right; locQuad.tr.texCoords.v = bottom; } else { if (cc.FIX_ARTIFACTS_BY_STRECHING_TEXEL) { left = (2 * rect.x + 1) / (2 * atlasWidth); right = left + (rect.width * 2 - 2) / (2 * atlasWidth); top = (2 * rect.y + 1) / (2 * atlasHeight); bottom = top + (rect.height * 2 - 2) / (2 * atlasHeight); } else { left = rect.x / atlasWidth; right = (rect.x + rect.width) / atlasWidth; top = rect.y / atlasHeight; bottom = (rect.y + rect.height) / atlasHeight; } // ! CC_FIX_ARTIFACTS_BY_STRECHING_TEXEL if (this._flippedX) { tempSwap = left; left = right; right = tempSwap; } if (this._flippedY) { tempSwap = top; top = bottom; bottom = tempSwap; } locQuad.bl.texCoords.u = left; locQuad.bl.texCoords.v = bottom; locQuad.br.texCoords.u = right; locQuad.br.texCoords.v = bottom; locQuad.tl.texCoords.u = left; locQuad.tl.texCoords.v = top; locQuad.tr.texCoords.u = right; locQuad.tr.texCoords.v = top; } this._quadDirty = true; } }); if (cc._renderType === cc._RENDER_TYPE_CANVAS) { var _p = cc.LabelTTF.prototype; _p.setColor = function (color3) { cc.Node.prototype.setColor.call(this, color3); this._setColorsString(); }; _p._setColorsString = function () { this._needUpdateTexture = true; var locDisplayColor = this._displayedColor, locDisplayedOpacity = this._displayedOpacity; var locStrokeColor = this._strokeColor, locFontFillColor = this._textFillColor; this._shadowColorStr = "rgba(" + (0 | (locDisplayColor.r * 0.5)) + "," + (0 | (locDisplayColor.g * 0.5)) + "," + (0 | (locDisplayColor.b * 0.5)) + "," + this._shadowOpacity + ")"; this._fillColorStr = "rgba(" + (0 | (locDisplayColor.r / 255 * locFontFillColor.r)) + "," + (0 | (locDisplayColor.g / 255 * locFontFillColor.g)) + "," + (0 | (locDisplayColor.b / 255 * locFontFillColor.b)) + ", " + locDisplayedOpacity / 255 + ")"; this._strokeColorStr = "rgba(" + (0 | (locDisplayColor.r / 255 * locStrokeColor.r)) + "," + (0 | (locDisplayColor.g / 255 * locStrokeColor.g)) + "," + (0 | (locDisplayColor.b / 255 * locStrokeColor.b)) + ", " + locDisplayedOpacity / 255 + ")"; }; _p.updateDisplayedColor = function (parentColor) { cc.Node.prototype.updateDisplayedColor.call(this, parentColor); this._setColorsString(); }; _p.setOpacity = function (opacity) { if (this._opacity === opacity) return; cc.Sprite.prototype.setOpacity.call(this, opacity); this._setColorsString(); this._needUpdateTexture = true; }; //TODO: _p._updateDisplayedOpacityForCanvas _p.updateDisplayedOpacity = cc.Sprite.prototype.updateDisplayedOpacity; _p.initWithStringAndTextDefinition = function (text, textDefinition) { // prepare everything needed to render the label this._updateWithTextDefinition(textDefinition, false); // set the string this.string = text; return true; }; _p.setFontFillColor = function (tintColor) { var locTextFillColor = this._textFillColor; if (locTextFillColor.r != tintColor.r || locTextFillColor.g != tintColor.g || locTextFillColor.b != tintColor.b) { locTextFillColor.r = tintColor.r; locTextFillColor.g = tintColor.g; locTextFillColor.b = tintColor.b; this._setColorsString(); this._needUpdateTexture = true; } }; _p.draw = cc.Sprite.prototype.draw; _p.setTextureRect = function (rect, rotated, untrimmedSize) { this._rectRotated = rotated || false; untrimmedSize = untrimmedSize || rect; this.setContentSize(untrimmedSize); this.setVertexRect(rect); var locTextureCoordRect = this._textureRect_Canvas; locTextureCoordRect.x = rect.x; locTextureCoordRect.y = rect.y; locTextureCoordRect.width = rect.width; locTextureCoordRect.height = rect.height; locTextureCoordRect.validRect = !(locTextureCoordRect.width === 0 || locTextureCoordRect.height === 0 || locTextureCoordRect.x < 0 || locTextureCoordRect.y < 0); var relativeOffset = this._unflippedOffsetPositionFromCenter; if (this._flippedX) relativeOffset.x = -relativeOffset.x; if (this._flippedY) relativeOffset.y = -relativeOffset.y; this._offsetPosition.x = relativeOffset.x + (this._contentSize.width - this._rect.width) / 2; this._offsetPosition.y = relativeOffset.y + (this._contentSize.height - this._rect.height) / 2; // rendering using batch node if (this._batchNode) { this.dirty = true; } }; _p = null; } else { cc.assert(typeof cc._tmp.WebGLLabelTTF === "function", cc._LogInfos.MissingFile, "LabelTTFWebGL.js"); cc._tmp.WebGLLabelTTF(); delete cc._tmp.WebGLLabelTTF; } cc.assert(typeof cc._tmp.PrototypeLabelTTF === "function", cc._LogInfos.MissingFile, "LabelTTFPropertyDefine.js"); cc._tmp.PrototypeLabelTTF(); delete cc._tmp.PrototypeLabelTTF; cc.LabelTTF._textAlign = ["left", "center", "right"]; cc.LabelTTF._textBaseline = ["top", "middle", "bottom"]; //check the first character cc.LabelTTF.wrapInspection = true; //Support: English French German //Other as Oriental Language cc.LabelTTF._wordRex = /([a-zA-Z0-9"A"O"U"a"oüsséècàùê^a^i^o^u]+|\S)/; cc.LabelTTF._symbolRex = /^[!,.:;}\]%\?>、‘“》?。,!]/; cc.LabelTTF._lastWordRex = /([a-zA-Z0-9"A"O"U"a"oüsséècàùê^a^i^o^u]+|\S)$/; cc.LabelTTF._lastEnglish = /[a-zA-Z0-9"A"O"U"a"oüsséècàùê^a^i^o^u]+$/; cc.LabelTTF._firsrEnglish = /^[a-zA-Z0-9"A"O"U"a"oüsséècàùê^a^i^o^u]/; // Only support style in this format: "18px Verdana" or "18px 'Helvetica Neue'" cc.LabelTTF._fontStyleRE = /^(\d+)px\s+['"]?([\w\s\d]+)['"]?$/; /** * creates a cc.LabelTTF from a font name, alignment, dimension and font size * @deprecated since v3.0, please use the new construction instead * @see cc.LabelTTF * @static * @param {String} text * @param {String|cc.FontDefinition} [fontName="Arial"] * @param {Number} [fontSize=16] * @param {cc.Size} [dimensions=cc.size(0,0)] * @param {Number} [hAlignment=cc.TEXT_ALIGNMENT_LEFT] * @param {Number} [vAlignment=cc.VERTICAL_TEXT_ALIGNMENT_TOP] * @return {cc.LabelTTF|Null} */ cc.LabelTTF.create = function (text, fontName, fontSize, dimensions, hAlignment, vAlignment) { return new cc.LabelTTF(text, fontName, fontSize, dimensions, hAlignment, vAlignment); }; /** * @deprecated since v3.0, please use the new construction instead * @function * @static */ cc.LabelTTF.createWithFontDefinition = cc.LabelTTF.create; if (cc.USE_LA88_LABELS) cc.LabelTTF._SHADER_PROGRAM = cc.SHADER_POSITION_TEXTURECOLOR; else cc.LabelTTF._SHADER_PROGRAM = cc.SHADER_POSITION_TEXTUREA8COLOR; cc.LabelTTF.__labelHeightDiv = cc.newElement("div"); cc.LabelTTF.__labelHeightDiv.style.fontFamily = "Arial"; cc.LabelTTF.__labelHeightDiv.style.position = "absolute"; cc.LabelTTF.__labelHeightDiv.style.left = "-100px"; cc.LabelTTF.__labelHeightDiv.style.top = "-100px"; cc.LabelTTF.__labelHeightDiv.style.lineHeight = "normal"; document.body ? document.body.appendChild(cc.LabelTTF.__labelHeightDiv) : cc._addEventListener(window, 'load', function () { this.removeEventListener('load', arguments.callee, false); document.body.appendChild(cc.LabelTTF.__labelHeightDiv); }, false); cc.LabelTTF.__getFontHeightByDiv = function (fontName, fontSize) { var clientHeight = cc.LabelTTF.__fontHeightCache[fontName + "." + fontSize]; if (clientHeight > 0) return clientHeight; var labelDiv = cc.LabelTTF.__labelHeightDiv; labelDiv.innerHTML = "ajghl~!"; labelDiv.style.fontFamily = fontName; labelDiv.style.fontSize = fontSize + "px"; clientHeight = labelDiv.clientHeight; cc.LabelTTF.__fontHeightCache[fontName + "." + fontSize] = clientHeight; labelDiv.innerHTML = ""; return clientHeight; }; cc.LabelTTF.__fontHeightCache = {};