1 //= require "object.class" 2 3 (function(global) { 4 5 "use strict"; 6 7 var fabric = global.fabric || (global.fabric = { }), 8 extend = fabric.util.object.extend, 9 clone = fabric.util.object.clone; 10 11 if (fabric.Text) { 12 fabric.warn('fabric.Text is already defined'); 13 return; 14 } 15 if (!fabric.Object) { 16 fabric.warn('fabric.Text requires fabric.Object'); 17 return; 18 } 19 20 /** 21 * @class Text 22 * @extends fabric.Object 23 */ 24 fabric.Text = fabric.util.createClass(fabric.Object, /** @scope fabric.Text.prototype */ { 25 26 fontsize: 20, 27 fontweight: 100, 28 fontfamily: 'Modernist_One_400', 29 textDecoration: '', 30 textShadow: null, 31 fontStyle: '', 32 lineHeight: 1.6, 33 strokeStyle: '', 34 strokeWidth: 1, 35 backgroundColor: '', 36 path: null, 37 38 /** 39 * @property 40 * @type String 41 */ 42 type: 'text', 43 44 /** 45 * Constructor 46 * @method initialize 47 * @param {String} text 48 * @param {Object} [options] 49 * @return {fabric.Text} thisArg 50 */ 51 initialize: function(text, options) { 52 this._initStateProperties(); 53 this.text = text; 54 this.setOptions(options); 55 this.theta = this.angle * Math.PI / 180; 56 this.width = this.getWidth(); 57 this.setCoords(); 58 }, 59 60 /** 61 * Creates `stateProperties` list on an instance, and adds `fabric.Text` -specific ones to it 62 * (such as "fontfamily", "fontweight", etc.) 63 * @private 64 * @method _initStateProperties 65 */ 66 _initStateProperties: function() { 67 this.stateProperties = this.stateProperties.concat(); 68 this.stateProperties.push( 69 'fontfamily', 70 'fontweight', 71 'path', 72 'text', 73 'textDecoration', 74 'textShadow', 75 'fontStyle', 76 'lineHeight', 77 'strokeStyle', 78 'strokeWidth', 79 'backgroundColor' 80 ); 81 fabric.util.removeFromArray(this.stateProperties, 'width'); 82 }, 83 84 /** 85 * Returns string representation of an instance 86 * @method toString 87 * @return {String} String representation of text object 88 */ 89 toString: function() { 90 return '#<fabric.Text ('+ this.complexity() +'): ' + 91 JSON.stringify({ text: this.text, fontfamily: this.fontfamily }) + '>'; 92 }, 93 94 /** 95 * @private 96 * @method _render 97 * @param {CanvasRenderingContext2D} ctx Context to render on 98 */ 99 _render: function(context) { 100 var o = Cufon.textOptions || (Cufon.textOptions = { }); 101 102 // export options to be used by cufon.js 103 o.left = this.left; 104 o.top = this.top; 105 o.context = context; 106 o.color = this.fill; 107 108 var el = this._initDummyElement(); 109 110 // set "cursor" to top/left corner 111 this.transform(context); 112 113 // draw text 114 Cufon.replaceElement(el, { 115 separate: 'none', 116 fontFamily: this.fontfamily, 117 enableTextDecoration: true, 118 textDecoration: this.textDecoration, 119 textShadow: this.textShadow, 120 fontStyle: this.fontStyle, 121 lineHeight: this.lineHeight, 122 strokeStyle: this.strokeStyle, 123 strokeWidth: this.strokeWidth, 124 backgroundColor: this.backgroundColor 125 }); 126 127 // update width, height 128 this.width = o.width; 129 this.height = o.height; 130 131 // need to set coords _after_ the width/height was retreived from Cufon 132 this.setCoords(); 133 }, 134 135 /** 136 * @private 137 * @method _initDummyElement 138 */ 139 _initDummyElement: function() { 140 var el = document.createElement('div'), 141 container = document.createElement('div'); 142 143 // Cufon doesn't play nice with textDecoration=underline if element doesn't have a parent 144 container.appendChild(el); 145 el.innerHTML = this.text; 146 147 // need to specify these manually, since Jaxer doesn't support retrieving computed style 148 el.style.fontSize = '40px'; 149 el.style.fontWeight = '400'; 150 el.style.letterSpacing = 'normal'; 151 el.style.color = '#000000'; 152 el.style.fontWeight = '600'; 153 el.style.fontFamily = 'Verdana'; 154 155 return el; 156 }, 157 158 /** 159 * Renders text instance on a specified context 160 * @method render 161 * @param ctx {CanvasRenderingContext2D} context to render on 162 */ 163 render: function(context) { 164 context.save(); 165 this._render(context); 166 if (this.active) { 167 this.drawBorders(context); 168 this.drawCorners(context); 169 } 170 context.restore(); 171 }, 172 173 /** 174 * Returns object representation of an instance 175 * @method toObject 176 * @return {Object} Object representation of text object 177 */ 178 toObject: function() { 179 return extend(this.callSuper('toObject'), { 180 text: this.text, 181 fontsize: this.fontsize, 182 fontweight: this.fontweight, 183 fontfamily: this.fontfamily, 184 fontStyle: this.fontStyle, 185 lineHeight: this.lineHeight, 186 textDecoration: this.textDecoration, 187 textShadow: this.textShadow, 188 path: this.path, 189 strokeStyle: this.strokeStyle, 190 strokeWidth: this.strokeWidth, 191 backgroundColor: this.backgroundColor 192 }); 193 }, 194 195 /** 196 * Sets "color" of an instance (alias of `set('fill', …)`) 197 * @method setColor 198 * @param {String} value 199 * @return {fabric.Text} thisArg 200 * @chainable 201 */ 202 setColor: function(value) { 203 this.set('fill', value); 204 return this; 205 }, 206 207 /** 208 * Sets fontsize of an instance and updates its coordinates 209 * @method setFontsize 210 * @param {Number} value 211 * @return {fabric.Text} thisArg 212 * @chainable 213 */ 214 setFontsize: function(value) { 215 this.set('fontsize', value); 216 this.setCoords(); 217 return this; 218 }, 219 220 /** 221 * Returns actual text value of an instance 222 * @method getText 223 * @return {String} 224 */ 225 getText: function() { 226 return this.text; 227 }, 228 229 /** 230 * Sets text of an instance, and updates its coordinates 231 * @method setText 232 * @param {String} value 233 * @return {fabric.Text} thisArg 234 * @chainable 235 */ 236 setText: function(value) { 237 this.set('text', value); 238 this.setCoords(); 239 return this; 240 }, 241 242 /** 243 * Sets specified property to a specified value 244 * @method set 245 * @param {String} name 246 * @param {Any} value 247 * @return {fabric.Text} thisArg 248 * @chainable 249 */ 250 set: function(name, value) { 251 this[name] = value; 252 if (name === 'fontfamily') { 253 this.path = this.path.replace(/(.*?)([^\/]*)(\.font\.js)/, '$1' + value + '$3'); 254 } 255 return this; 256 } 257 }); 258 259 /** 260 * Returns fabric.Text instance from an object representation 261 * @static 262 * @method fromObject 263 * @param {Object} object to create an instance from 264 * @return {fabric.Text} an instance 265 */ 266 fabric.Text.fromObject = function(object) { 267 return new fabric.Text(object.text, clone(object)); 268 }; 269 270 /** 271 * Returns fabric.Text instance from an SVG element (<b>not yet implemented</b>) 272 * @static 273 * @method fabric.Text.fromElement 274 * @return {fabric.Text} an instance 275 */ 276 fabric.Text.fromElement = function(element) { 277 // TODO (kangax): implement this 278 }; 279 })(this);