(function() { QUnit.module('fabric.Text'); function createTextObject(text) { return new fabric.Text(text || 'x'); } var CHAR_WIDTH = 20; var REFERENCE_TEXT_OBJECT = { 'version': fabric.version, 'type': 'text', 'originX': 'left', 'originY': 'top', 'left': 0, 'top': 0, 'width': CHAR_WIDTH, 'height': 45.2, 'fill': 'rgb(0,0,0)', 'stroke': null, 'strokeWidth': 1, 'strokeDashArray': null, 'strokeLineCap': 'butt', 'strokeDashOffset': 0, 'strokeLineJoin': 'miter', 'strokeMiterLimit': 4, 'scaleX': 1, 'scaleY': 1, 'angle': 0, 'flipX': false, 'flipY': false, 'opacity': 1, 'shadow': null, 'visible': true, 'clipTo': null, 'backgroundColor': '', 'text': 'x', 'fontSize': 40, 'fontWeight': 'normal', 'fontFamily': 'Times New Roman', 'fontStyle': 'normal', 'lineHeight': 1.16, 'underline': false, 'overline': false, 'linethrough': false, 'textAlign': 'left', 'textBackgroundColor': '', 'fillRule': 'nonzero', 'paintFirst': 'fill', 'globalCompositeOperation': 'source-over', 'skewX': 0, 'skewY': 0, 'transformMatrix': null, 'charSpacing': 0, 'styles': {} }; QUnit.test('constructor', function(assert) { assert.ok(fabric.Text); var text = createTextObject(); assert.ok(text); assert.ok(text instanceof fabric.Text); assert.ok(text instanceof fabric.Object); assert.equal(text.get('type'), 'text'); assert.equal(text.get('text'), 'x'); }); QUnit.test('toString', function(assert) { var text = createTextObject(); assert.ok(typeof text.toString === 'function'); assert.equal(text.toString(), '#'); }); QUnit.test('_getFontDeclaration', function(assert) { var text = createTextObject(); assert.ok(typeof text._getFontDeclaration === 'function', 'has a private method _getFontDeclaration'); var fontDecl = text._getFontDeclaration(); assert.ok(typeof fontDecl === 'string', 'it returns a string'); assert.equal(fontDecl, 'normal normal 40px "Times New Roman"'); text.fontFamily = '"Times New Roman"'; fontDecl = text._getFontDeclaration(); assert.equal(fontDecl, 'normal normal 40px "Times New Roman"'); text.fontFamily = '\'Times New Roman\''; fontDecl = text._getFontDeclaration(); assert.equal(fontDecl, 'normal normal 40px \'Times New Roman\''); }); fabric.Text.genericFonts.forEach(function(fontName) { QUnit.test('_getFontDeclaration with genericFonts', function(assert) { var text = createTextObject(); text.fontFamily = fontName; var fontDecl = text._getFontDeclaration(); assert.equal(fontDecl, 'normal normal 40px ' + fontName, 'it does not quote ' + fontName); text.fontFamily = fontName.toUpperCase(); var fontDecl = text._getFontDeclaration(); assert.equal(fontDecl, 'normal normal 40px ' + fontName.toUpperCase(), 'it uses a non case sensitive logic'); }); }); QUnit.test('toObject', function(assert) { var text = createTextObject(); assert.ok(typeof text.toObject === 'function'); assert.deepEqual(text.toObject(), REFERENCE_TEXT_OBJECT); }); QUnit.test('complexity', function(assert) { var text = createTextObject(); assert.ok(typeof text.complexity === 'function'); assert.equal(text.complexity(), 1); }); QUnit.test('set', function(assert) { var text = createTextObject(); assert.ok(typeof text.set === 'function'); assert.equal(text.set('text', 'bar'), text, 'should be chainable'); text.set({ left: 1234, top: 2345, angle: 55 }); assert.equal(text.get('left'), 1234); assert.equal(text.get('top'), 2345); assert.equal(text.get('angle'), 55); }); QUnit.test('lineHeight with single line', function(assert) { var text = createTextObject(); text.text = 'text with one line'; text.lineHeight = 2; text.initDimensions(); var height = text.height; text.lineHeight = 0.5; text.initDimensions(); var heightNew = text.height; assert.equal(height, heightNew, 'text height does not change with one single line'); }); QUnit.test('lineHeight with multi line', function(assert) { var text = createTextObject(); text.text = 'text with\ntwo lines'; text.lineHeight = 0.1; text.initDimensions(); var height = text.height, minimumHeight = text.fontSize * text._fontSizeMult; assert.equal(height > minimumHeight, true, 'text height is always bigger than minimum Height'); }); QUnit.test('set with "hash"', function(assert) { var text = createTextObject(); text.set({ opacity: 0.123, fill: 'red', fontFamily: 'blah' }); assert.equal(text.opacity, 0.123); assert.equal(text.fill, 'red'); assert.equal(text.fontFamily, 'blah'); }); QUnit.test('get bounding rect after init', function(assert) { var string = 'Some long text, the quick brown fox jumps over the lazy dog etc... blah blah blah'; var text = new fabric.Text(string, { left: 30, top: 30, fill: '#ffffff', fontSize: 24, fontWeight: 'normal', fontFamily: 'Arial', originY: 'bottom' }); var br = text.getBoundingRect(); text.setCoords(); var br2 = text.getBoundingRect(); assert.deepEqual(br, br2, 'text bounding box is the same before and after calling setCoords'); }); QUnit.test('setShadow', function(assert) { var text = createTextObject(); assert.ok(typeof text.setShadow === 'function'); assert.equal(text.setShadow('10px 8px 2px red'), text, 'should be chainable'); assert.ok(text.shadow instanceof fabric.Shadow, 'should inherit from fabric.Shadow'); assert.equal(text.shadow.color, 'red'); assert.equal(text.shadow.offsetX, 10); assert.equal(text.shadow.offsetY, 8); assert.equal(text.shadow.blur, 2); }); QUnit.test('fabric.Text.fromObject', function(assert) { var done = assert.async(); assert.ok(typeof fabric.Text.fromObject === 'function'); fabric.Text.fromObject(REFERENCE_TEXT_OBJECT, function(text) { assert.deepEqual(text.toObject(), REFERENCE_TEXT_OBJECT); done(); }); }); QUnit.test('fabric.Text.fromElement', function(assert) { assert.ok(typeof fabric.Text.fromElement === 'function'); var elText = fabric.document.createElement('text'); elText.textContent = 'x'; fabric.Text.fromElement(elText, function(text) { assert.ok(text instanceof fabric.Text); var expectedObject = fabric.util.object.extend(fabric.util.object.clone(REFERENCE_TEXT_OBJECT), { left: 0, top: -14.05, width: 8, height: 18.08, fontSize: 16, originX: 'left' }); assert.deepEqual(text.toObject(), expectedObject, 'parsed object is what expected'); }); }); QUnit.test('fabric.Text.fromElement with custom attributes', function(assert) { var elTextWithAttrs = fabric.document.createElement('text'); elTextWithAttrs.textContent = 'x'; elTextWithAttrs.setAttribute('x', 10); elTextWithAttrs.setAttribute('y', 20); elTextWithAttrs.setAttribute('fill', 'rgb(255,255,255)'); elTextWithAttrs.setAttribute('opacity', 0.45); elTextWithAttrs.setAttribute('stroke', 'blue'); elTextWithAttrs.setAttribute('stroke-width', 3); elTextWithAttrs.setAttribute('stroke-dasharray', '5, 2'); elTextWithAttrs.setAttribute('stroke-linecap', 'round'); elTextWithAttrs.setAttribute('stroke-linejoin', 'bevil'); elTextWithAttrs.setAttribute('stroke-miterlimit', 5); elTextWithAttrs.setAttribute('font-family', 'Monaco'); elTextWithAttrs.setAttribute('font-style', 'italic'); elTextWithAttrs.setAttribute('font-weight', 'bold'); elTextWithAttrs.setAttribute('font-size', '123'); elTextWithAttrs.setAttribute('letter-spacing', '1em'); elTextWithAttrs.setAttribute('text-decoration', 'underline'); elTextWithAttrs.setAttribute('text-anchor', 'middle'); fabric.Text.fromElement(elTextWithAttrs, function(textWithAttrs) { // temp workaround for text objects not obtaining width under node textWithAttrs.width = CHAR_WIDTH; assert.ok(textWithAttrs instanceof fabric.Text); var expectedObject = fabric.util.object.extend(fabric.util.object.clone(REFERENCE_TEXT_OBJECT), { /* left varies slightly due to node-canvas rendering */ left: fabric.util.toFixed(textWithAttrs.left + '', 2), top: -88.03, width: CHAR_WIDTH, height: 138.99, fill: 'rgb(255,255,255)', opacity: 0.45, stroke: 'blue', strokeWidth: 3, strokeDashArray: [5, 2], strokeLineCap: 'round', strokeLineJoin: 'bevil', strokeMiterLimit: 5, fontFamily: 'Monaco', paintFirst: 'fill', fontStyle: 'italic', charSpacing: 1000, fontWeight: 'bold', fontSize: 123, underline: true, }); assert.deepEqual(textWithAttrs.toObject(), expectedObject); }); }); QUnit.test('empty fromElement', function(assert) { fabric.Text.fromElement(null, function(text) { assert.equal(text, null); }); }); QUnit.test('dimensions after text change', function(assert) { var text = new fabric.Text('x'); assert.equal(text.width, CHAR_WIDTH); text.set('text', 'xx'); assert.equal(text.width, CHAR_WIDTH * 2); }); QUnit.test('dimensions without text', function(assert) { var text = new fabric.Text(''); assert.equal(text.width, 2); }); QUnit.test('setting fontFamily', function(assert) { var text = new fabric.Text('x'); text.path = 'foobar.js'; text.set('fontFamily', 'foobar'); assert.equal(text.get('fontFamily'), 'foobar'); text.set('fontFamily', '"Arial Black", Arial'); assert.equal(text.get('fontFamily'), '"Arial Black", Arial'); }); QUnit.test('text styleHas', function(assert) { var text = new fabric.Text('xxxxxx\nx y'); text.styles = { }; assert.ok(typeof text.styleHas === 'function'); assert.equal(text.styleHas('stroke'), false, 'the text style has no stroke'); text.styles = { 1: { 0: { stroke: 'red' }}}; assert.equal(text.styleHas('stroke'), true, 'the text style has stroke'); }); QUnit.test('text cleanStyle', function(assert) { var text = new fabric.Text('xxxxxx\nx y'); text.styles = { 1: { 0: { stroke: 'red' }}}; text.stroke = 'red'; assert.ok(typeof text.cleanStyle === 'function'); text.cleanStyle('stroke'); assert.equal(text.styles[1], undefined, 'the style has been cleaned since stroke was equal to text property'); text.styles = { 1: { 0: { stroke: 'blue' }}}; text.stroke = 'red'; text.cleanStyle('stroke'); assert.equal(text.styles[1][0].stroke, 'blue', 'nothing to clean, style untouched'); }); QUnit.test('text cleanStyle with different sub styles styles', function(assert) { var text = new fabric.Text('xxxxxx\nx y'); text.styles = { 1: { 0: { fill: 'red' }, 1: { stroke: 'red' }, 2: { stroke: 'blue' }}}; text.stroke = 'red'; text.cleanStyle('stroke'); assert.equal(text.stroke, 'red', 'the stroke stays red'); assert.equal(text.styles[1][0].fill, 'red', 'the style has not been changed since it\'s a different property'); assert.equal(text.styles[1][0].stroke, undefined, 'the style has been cleaned since stroke was equal to text property'); assert.equal(text.styles[1][1], undefined, 'the style remains undefined'); assert.equal(text.styles[1][2].stroke, 'blue', 'the style remains unchanged'); }); QUnit.test('text cleanStyle with undefined and set styles', function(assert) { var text = new fabric.Text('xxxxxx\nx y'); text.styles = { 1: { 1: { stroke: 'red' }, 3: { stroke: 'red' }}}; text.stroke = 'red'; text.cleanStyle('stroke'); assert.equal(text.stroke, 'red', 'the stroke stays red'); assert.equal(text.styles[1], undefined, 'the style has been cleaned since stroke was equal to text property'); }); QUnit.test('text cleanStyle with empty styles', function(assert) { var text = new fabric.Text('xxxxxx\nx y'); text.styles = { 1: { 0: { }, 1: { }}, 2: { }, 3: { 4: { }}}; text.cleanStyle('any'); assert.equal(text.styles[1], undefined, 'the style has been cleaned since there were no usefull informations'); assert.equal(text.styles[2], undefined, 'the style has been cleaned since there were no usefull informations'); assert.equal(text.styles[3], undefined, 'the style has been cleaned since there were no usefull informations'); }); QUnit.test('text cleanStyle with full style', function(assert) { var text = new fabric.Text('xxx'); text.styles = { 0: { 0: { fill: 'blue' }, 1: { fill: 'blue' }, 2: { fill: 'blue' }}}; text.fill = 'black'; text.cleanStyle('fill'); assert.equal(text.fill, 'blue', 'the fill has been changed to blue'); assert.equal(text.styles[0], undefined, 'all the style has been removed'); }); QUnit.test('text cleanStyle with no relevant style', function(assert) { var text = new fabric.Text('xxx'); text.styles = { 0: { 0: { other: 'value1' }, 1: { other: 'value2' }, 2: { other: 'value3' }}}; text.fill = 'black'; text.cleanStyle('fill'); assert.equal(text.fill, 'black', 'the fill remains black'); assert.equal(text.styles[0][0].other, 'value1', 'style remains the same'); assert.equal(text.styles[0][0].full, undefined, 'style remains undefined'); assert.equal(text.styles[0][1].other, 'value2', 'style remains the same'); assert.equal(text.styles[0][1].full, undefined, 'style remains undefined'); assert.equal(text.styles[0][2].other, 'value3', 'style remains the same'); assert.equal(text.styles[0][2].full, undefined, 'style remains undefined'); }); QUnit.test('text removeStyle with some style', function(assert) { var text = new fabric.Text('xxx'); text.styles = { 0: { 0: { stroke: 'black', fill: 'blue' }, 1: { fill: 'blue' }, 2: { fill: 'blue' }}}; assert.ok(typeof text.removeStyle === 'function'); text.fill = 'red'; text.removeStyle('fill'); assert.equal(text.fill, 'red', 'the fill has not been changed'); assert.equal(text.styles[0][0].stroke, 'black', 'the non fill part of the style is still there'); assert.equal(text.styles[0][0].fill, undefined, 'the fill part of the style has been removed'); text.styles = { 0: { 0: { fill: 'blue' }, 1: { fill: 'blue' }, 2: { fill: 'blue' }}}; text.removeStyle('fill'); assert.equal(text.styles[0], undefined, 'the styles got empty and has been removed'); }); QUnit.test('getFontCache works with fontWeight numbers', function(assert) { var text = new fabric.Text('xxx', { fontWeight: 400 }); text.initDimensions(); var cache = fabric.charWidthsCache[text.fontFamily.toLowerCase()]; var cacheProp = text.fontStyle + '_400'; assert.equal(cacheProp in cache, true, '400 is converted to string'); }); QUnit.test('getFontCache is case insensitive', function(assert) { var text = new fabric.Text('xxx', { fontWeight: 'BOld', fontStyle: 'NormaL' }); text.initDimensions(); var text2 = new fabric.Text('xxx', { fontWeight: 'bOLd', fontStyle: 'nORMAl' }); text2.initDimensions(); var cache = text.getFontCache(text); var cache2 = text2.getFontCache(text2); assert.equal(cache, cache2, 'you get the same cache'); }); // moved QUnit.test('getSelectionStyles with no arguments', function(assert) { var iText = new fabric.Text('test foo bar-baz\nqux', { styles: { 0: { 0: { textDecoration: 'underline' }, 2: { textDecoration: 'overline' }, 4: { textBackgroundColor: '#ffc' } }, 1: { 0: { fill: 'red' }, 1: { fill: 'green' }, 2: { fill: 'blue' } } } }); assert.equal(typeof iText.getSelectionStyles, 'function'); assert.deepEqual(iText.getSelectionStyles(), []); }); QUnit.test('getSelectionStyles with 2 args', function(assert) { var iText = new fabric.Text('test foo bar-baz\nqux', { styles: { 0: { 0: { textDecoration: 'underline' }, 2: { textDecoration: 'overline' }, 4: { textBackgroundColor: '#ffc' } }, 1: { 0: { fill: 'red' }, 1: { fill: 'green' }, 2: { fill: 'blue' } } } }); assert.deepEqual(iText.getSelectionStyles(0, 5), [ { textDecoration: 'underline' }, {}, { textDecoration: 'overline' }, {}, { textBackgroundColor: '#ffc' }, ]); assert.deepEqual(iText.getSelectionStyles(2, 2), [ ]); }); QUnit.test('setSelectionStyles', function(assert) { var iText = new fabric.Text('test foo bar-baz\nqux', { styles: { 0: { 0: { fill: '#112233' }, 2: { stroke: '#223344' } } } }); assert.equal(typeof iText.setSelectionStyles, 'function'); iText.setSelectionStyles({ fill: 'red', stroke: 'yellow' }); assert.deepEqual(iText.styles[0][0], { fill: '#112233' }); iText.setSelectionStyles({ fill: 'red', stroke: 'yellow' }, 0, 1); assert.deepEqual(iText.styles[0][0], { fill: 'red', stroke: 'yellow' }); iText.setSelectionStyles({ fill: '#998877', stroke: 'yellow' }, 2, 3); assert.deepEqual(iText.styles[0][2], { fill: '#998877', stroke: 'yellow' }); }); QUnit.test('getStyleAtPosition', function(assert) { var iText = new fabric.Text('test foo bar-baz\nqux', { styles: { 0: { 0: { textDecoration: 'underline' }, 2: { textDecoration: 'overline' }, 4: { textBackgroundColor: '#ffc' } }, 1: { 0: { fill: 'red' }, 1: { fill: 'green' }, 2: { fill: 'blue' } } } }); assert.equal(typeof iText.getStyleAtPosition, 'function'); assert.deepEqual(iText.getStyleAtPosition(2), { textDecoration: 'overline' }); assert.deepEqual(iText.getStyleAtPosition(1), { }); assert.deepEqual(iText.getStyleAtPosition(18), { fill: 'green' }); }); QUnit.test('_splitText', function(assert) { var text = new fabric.Text('test foo bar-baz\nqux', {}); var test = text._splitText(); assert.equal(test.lines[0], 'test foo bar-baz', 'first line is correct'); assert.equal(test.lines[1], 'qux', 'second line is correct'); assert.deepEqual(test.graphemeLines[0], ['t','e','s','t',' ','f','o','o',' ','b','a','r','-','b','a','z'], 'first line is correct'); assert.deepEqual(test.graphemeLines[1], ['q','u','x'], 'second line is correct'); }); QUnit.test('getStyleAtPosition complete', function(assert) { var iText = new fabric.Text('test foo bar-baz\nqux', { styles: { 0: { 0: { underline: true }, 2: { overline: true }, 4: { textBackgroundColor: '#ffc' } }, 1: { 0: { fill: 'red' }, 1: { fill: 'green' }, 2: { fill: 'blue' } } } }); var expectedStyle0 = { stroke: null, strokeWidth: 1, fill: 'rgb(0,0,0)', fontFamily: 'Times New Roman', fontSize: 40, fontWeight: 'normal', fontStyle: 'normal', underline: true, overline: false, linethrough: false, textBackgroundColor: '', deltaY: 0, }; var expectedStyle2 = { stroke: null, strokeWidth: 1, fill: 'rgb(0,0,0)', fontFamily: 'Times New Roman', fontSize: 40, fontWeight: 'normal', fontStyle: 'normal', underline: false, overline: true, linethrough: false, textBackgroundColor: '', deltaY: 0, }; assert.equal(typeof iText.getStyleAtPosition, 'function'); assert.deepEqual(iText.getStyleAtPosition(0, true), expectedStyle0, 'styles do match at 0'); assert.deepEqual(iText.getStyleAtPosition(2, true), expectedStyle2, 'styles do match at 2'); }); QUnit.test('toSVG with NUM_FRACTION_DIGITS', function(assert) { var iText = new fabric.IText('test foo bar-baz', { // makes weird numbers styles: { 0: { 0: { fill: 'red', }, 1: { fill: 'blue', }, 2: { fill: 'green', }, 3: { fill: 'yellow', }, 4: { fill: 'pink', }, } } }); fabric.Object.NUM_FRACTION_DIGITS = 1; var SVG_1 = iText.toSVG(); // var SVG_1_EXPECTED = '\t\n\t\ttest foo bar-baz\n\t\n'; //assert.equal(SVG_1, SVG_1_EXPECTED, 'numbers have max 1 decimal'); fabric.Object.NUM_FRACTION_DIGITS = 3; var SVG_2 = iText.toSVG(); // var SVG_2_EXPECTED = '\t\n\t\ttest foo bar-baz\n\t\n'; //assert.equal(SVG_2, SVG_2_EXPECTED, 'numbers have max 3 decimal'); assert.ok(SVG_2.length > SVG_1.length, 'SVG 2 has more decimal'); // put back to 2 or break all tests fabric.Object.NUM_FRACTION_DIGITS = 2; }); QUnit.test('getSvgSpanStyles produces correct output', function(assert) { var iText = new fabric.IText('test foo bar-baz'); var styleObject = { fill: 'red', strokeWidth: 30, fontFamily: 'Verdana', fontSize: 25, }; var styleString = iText.getSvgSpanStyles(styleObject); var expected = 'stroke-width: 30; font-family: \'Verdana\'; font-size: 25px; fill: rgb(255,0,0); '; assert.equal(styleString, expected, 'style is as expected'); }); QUnit.test('getSvgSpanStyles produces correct output with useWhiteSpace', function(assert) { var iText = new fabric.IText('test foo bar-baz'); var styleObject = { fill: 'red', strokeWidth: 30, fontFamily: 'Verdana', fontSize: 25, }; var styleString = iText.getSvgSpanStyles(styleObject, true); var expected = 'stroke-width: 30; font-family: \'Verdana\'; font-size: 25px; fill: rgb(255,0,0); white-space: pre; '; assert.equal(styleString, expected, 'style is as expected'); }); QUnit.test('getSvgTextDecoration with overline true produces correct output', function(assert){ var iText = new fabric.IText('test foo bar-baz'); var styleObject = { overline: true, }; var styleString = iText.getSvgTextDecoration(styleObject); var expected = 'overline '; assert.equal(styleString, expected, 'style is as expected'); }); QUnit.test('getSvgTextDecoration with overline underline true produces correct output', function(assert){ var iText = new fabric.IText('test foo bar-baz'); var styleObject = { overline: true, underline: true, }; var styleString = iText.getSvgTextDecoration(styleObject); var expected = 'overline underline '; assert.equal(styleString, expected, 'style is as expected with overline underline'); }); QUnit.test('getSvgTextDecoration with overline underline true produces correct output', function(assert){ var iText = new fabric.IText('test foo bar-baz'); var styleObject = { overline: true, underline: true, linethrough: true, }; var styleString = iText.getSvgTextDecoration(styleObject); var expected = 'overline underline line-through '; assert.equal(styleString, expected, 'style is as expected with overline underline'); }); QUnit.test('getSvgTextDecoration with overline underline true produces correct output', function(assert){ var iText = new fabric.IText('test foo bar-baz'); var styleObject = { overline: true, underline: true, linethrough: true, }; var styleString = iText.getSvgTextDecoration(styleObject); var expected = 'overline underline line-through '; assert.equal(styleString, expected, 'style is as expected with overline underline'); }); QUnit.test('text superscript', function(assert) { var text = new fabric.Text('xxx', { styles: { 0: { 0: { stroke: 'black', fill: 'blue' }, 1: { fill: 'blue' }, 2: { fontSize: 4, deltaY: 20 }} } }); assert.ok(typeof text.setSuperscript === 'function'); var size = text.fontSize; var schema = text.superscript; var styleFontSize = text.styles[0][2].fontSize; var styleDeltaY = text.styles[0][2].deltaY; text.setSuperscript(1, 2).setSuperscript(2, 3); assert.equal(text.styles[0][0].fontSize, undefined, 'character 0: fontSize is not set'); assert.equal(text.styles[0][0].deltaY, undefined, 'character 0: deltaY is not set'); assert.equal(text.styles[0][1].fontSize, size * schema.size, 'character 1: fontSize has been set'); assert.equal(text.styles[0][1].deltaY, size * schema.baseline, 'character 1: deltaY has been set'); assert.equal(text.styles[0][2].fontSize, styleFontSize * schema.size, 'character 2: fontSize has been decreased'); assert.equal(text.styles[0][2].deltaY, styleDeltaY + styleFontSize * schema.baseline, 'character 2: deltaY has been decreased'); }); QUnit.test('text subscript', function(assert) { var text = new fabric.Text('xxx', { styles: { 0: { 0: { stroke: 'black', fill: 'blue' }, 1: { fill: 'blue' }, 2: { fontSize: 4, deltaY: 20 }} } }); assert.ok(typeof text.setSubscript === 'function'); var size = text.fontSize; var schema = text.subscript; var styleFontSize = text.styles[0][2].fontSize; var styleDeltaY = text.styles[0][2].deltaY; text.setSubscript(1,2).setSubscript(2,3); assert.equal(text.styles[0][0].fontSize, undefined, 'character 0: fontSize is not set'); assert.equal(text.styles[0][0].deltaY, undefined, 'character 0: deltaY is not set'); assert.equal(text.styles[0][1].fontSize, size * schema.size, 'character 1: fontSize has been set'); assert.equal(text.styles[0][1].deltaY, size * schema.baseline, 'character 1: deltaY has been set'); assert.equal(text.styles[0][2].fontSize, styleFontSize * schema.size, 'character 2: fontSize has been decreased'); assert.equal(text.styles[0][2].deltaY, styleDeltaY + styleFontSize * schema.baseline, 'character 2: deltaY has been increased'); }); QUnit.test('getHeightOfLine measures height of aline', function(assert) { var text = new fabric.Text('xxx\n'); var height1 = text.getHeightOfLine(0); var height2 = text.getHeightOfLine(1); assert.equal(Math.round(height1), 52, 'height of line with text is ok'); assert.equal(Math.round(height2), 52, 'height of empty line is ok'); assert.equal(height1, height2, 'should have same height'); }); QUnit.test('_measureChar handles 0 width chars', function(assert) { fabric.charWidthsCache = {}; var zwc = '\u200b'; var text = new fabric.Text(''); var style = text.getCompleteStyleDeclaration(0, 0); var box = text._measureChar('a', style, zwc, style); var box2 = text._measureChar('a', style, zwc, style); assert.equal(fabric.charWidthsCache[text.fontFamily.toLowerCase()].normal_normal[zwc], 0, 'zwc is a 0 width char'); assert.equal(box.kernedWidth, box2.kernedWidth, '2 measurements of the same string return the same number'); }); })();