Skip to content

Commit

Permalink
'deltaY', superscript, subscript etc.
Browse files Browse the repository at this point in the history
Honors 'deltaY' during rendering; provides functionality for 'superscript' & 'subscript'; some bug-fixing
  • Loading branch information
denim2x committed Aug 10, 2017
1 parent e4432c8 commit 045fe04
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 53 deletions.
6 changes: 6 additions & 0 deletions src/mixins/object.svg_export.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@

/**
* Returns styles-string for svg-export
* @param {Object} style the object from which to retrieve style properties
* @return {String}
*/
getSvgSpanStyles: function(style) {
Expand All @@ -86,6 +87,11 @@
].join('');
},

/**
* Returns text-decoration property for svg-export
* @param {Object} style the object from which to retrieve style properties
* @return {String}
*/
getSvgTextDecoration: function(style) {
if ('overline' in style || 'underline' in style || 'linethrough' in style) {
return 'text-decoration: ' + (style.overline ? 'overline ' : '') +
Expand Down
179 changes: 126 additions & 53 deletions src/shapes/text.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
'charSpacing',
'styles'
);

/**
* Text class
* @class fabric.Text
Expand All @@ -57,7 +58,7 @@

/**
* Properties which when set cause object to change dimensions
* @type Object
* @type Array
* @private
*/
_dimensionAffectingProps: [
Expand Down Expand Up @@ -168,6 +169,26 @@
*/
lineHeight: 1.16,

/**
* Superscript schema object (based on https://tr.im/subscript_superscript)
* @type Object
* @default
*/
superscript: {
size: 0.60, // fontSize factor
baseline: 0.67 // baseline-shift factor (upwards)
},

/**
* Subscript schema object (based on https://tr.im/subscript_superscript)
* @type Object
* @default
*/
subscript: {
size: 0.62, // fontSize factor
baseline: -0.25 // baseline-shift factor (downwards)
},

/**
* Background color of text lines
* @type String
Expand Down Expand Up @@ -235,8 +256,8 @@
charSpacing: 0,

/**
* Object containing character styles
* (where top-level properties corresponds to line number and 2nd-level properties -- to char number in a line)
* Object containing character styles - top-level properties -> line numbers,
* 2nd-level properties - charater numbers
* @type Object
* @default
*/
Expand All @@ -253,7 +274,7 @@
_measuringContext: null,

/**
* Array of properties that define a style unit.
* Array of properties that define a style unit (of 'styles').
* @type {Array}
* @default
*/
Expand Down Expand Up @@ -443,16 +464,9 @@
*/
_extendStyles: function(index, styles) {
var loc = this.get2DCursorLocation(index);

if (!this._getLineStyle(loc.lineIndex)) {
this._setLineStyle(loc.lineIndex, {});
}

if (!this._getStyleDeclaration(loc.lineIndex, loc.charIndex)) {
this._setStyleDeclaration(loc.lineIndex, loc.charIndex, {});
}

fabric.util.object.extend(this._getStyleDeclaration(loc.lineIndex, loc.charIndex), styles);
var decl = this._getStyleDeclaration(loc.lineIndex, loc.charIndex) || {};
fabric.util.object.extend(decl, styles);
this._setStyleDeclaration(loc.lineIndex, loc.charIndex, decl);
},

/**
Expand Down Expand Up @@ -738,7 +752,9 @@
* @private
*/
_setStyleDeclaration: function(lineIndex, charIndex, style) {
this.styles[lineIndex][charIndex] = style;
var decl = this._getLineStyle(lineIndex) || {};
decl[charIndex] = style;
this._setLineStyle(lineIndex, decl);
},

/**
Expand Down Expand Up @@ -833,13 +849,13 @@
},

/**
* return height of char in fontSize for a character at lineIndex, charIndex
* @param {Number} l line Index
* @param {Number} c char index
* @return {Number} fontSize of that character
* Computes height of character at given position
* @param {Number} line the line number
* @param {Number} char the character number
* @return {Number} fontSize of the character
*/
getHeightOfChar: function(l, c) {
return this.getValueOfPropertyAt(l, c, 'fontSize');
getHeightOfChar: function(line, char) {
return this.getValueOfPropertyAt(line, char, 'fontSize');
},

/**
Expand Down Expand Up @@ -894,23 +910,26 @@
* @param {String} grapheme to be measured
* @param {Number} lineIndex index of the line where the char is
* @param {Number} charIndex position in the line
* @param {String} [previousChar] character preceding the one to be measured
* @param {String} [prevGrapheme] character preceding the one to be measured
*/
_getGraphemeBox: function(grapheme, lineIndex, charIndex, previousGrapheme, skipLeft) {
var charStyle = this.getCompleteStyleDeclaration(lineIndex, charIndex),
prevCharStyle = previousGrapheme ? this.getCompleteStyleDeclaration(lineIndex, charIndex - 1) : { },
info = this._measureChar(grapheme, charStyle, previousGrapheme, prevCharStyle),
kernedWidth = info.kernedWidth, width = info.width;
_getGraphemeBox: function(grapheme, lineIndex, charIndex, prevGrapheme, skipLeft) {
var style = this.getCompleteStyleDeclaration(lineIndex, charIndex),
prevStyle = prevGrapheme ? this.getCompleteStyleDeclaration(lineIndex, charIndex - 1) : { },
info = this._measureChar(grapheme, style, prevGrapheme, prevStyle),
kernedWidth = info.kernedWidth,
width = info.width;

if (this.charSpacing !== 0) {
width += this._getWidthOfCharSpacing();
kernedWidth += this._getWidthOfCharSpacing();
}

var box = {
width: width,
left: 0,
height: charStyle.fontSize,
height: style.fontSize,
kernedWidth: kernedWidth,
deltaY: style.deltaY || 0,
};
if (charIndex > 0 && !skipLeft) {
var previousBox = this.__charBounds[lineIndex][charIndex - 1];
Expand All @@ -920,32 +939,26 @@
},

/**
* Calculate height of chosen line
* height of line is based mainly on fontSize
* @private
* @param {Number} lineIndex index of the line to calculate
* Calculate height of line at 'lineIndex'
* @param {Number} lineIndex index of line to calculate
* @return {Number}
*/
getHeightOfLine: function(lineIndex) {
if (this.__lineHeights[lineIndex]) {
return this.__lineHeights[lineIndex];
}

var line = this._textLines[lineIndex],
maxHeight = this.getHeightOfChar(lineIndex, 0);

for (var i = 1, len = line.length; i < len; i++) {
var currentCharHeight = this.getHeightOfChar(lineIndex, i);
if (currentCharHeight > maxHeight) {
maxHeight = currentCharHeight;
}
var line = this._textLines[lineIndex], maxHeight = 0;
for (var i = 0, len = line.length; i < len; i++) {
maxHeight = Math.max(this.getHeightOfChar(lineIndex, i), maxHeight);
// TODO: Take 'deltaY' into account to avoid overlaps (may require 'allowOverlaps' property)
}
this.__lineHeights[lineIndex] = maxHeight * this.lineHeight * this._fontSizeMult;
return this.__lineHeights[lineIndex];

return this.__lineHeights[lineIndex] = maxHeight * this.lineHeight * this._fontSizeMult;
},

/**
* calculate text box height
* @private
* Calculate text box height
*/
calcTextHeight: function() {
var lineHeight, height = 0;
Expand Down Expand Up @@ -1109,11 +1122,35 @@
if (decl && decl.textBackgroundColor) {
this._removeShadow(ctx);
}
if (decl && decl.deltaY) {
top += decl.deltaY;
}

shouldFill && ctx.fillText(_char, left, top);
shouldStroke && ctx.strokeText(_char, left, top);
decl && ctx.restore();
},

/**
* Turns the character into a 'superior figure' (aka. 'superscript')
* @param {Number} line the line number
* @param {Number} char the character number
* @returns {Object} this
*/
setSuperscript: function(line, char) {
return this.superscript.apply(this, line, char);
},

/**
* Turns the character into an 'inferior figure' (aka. 'subscript')
* @param {Number} line the line number
* @param {Number} char the character number
* @returns {Object} this
*/
setSubscript: function(line, char) {
return this.subscript.apply(this, line, char);
},

/**
* @private
* @param {Object} prevStyle
Expand All @@ -1126,7 +1163,8 @@
prevStyle.fontSize !== thisStyle.fontSize ||
prevStyle.fontFamily !== thisStyle.fontFamily ||
prevStyle.fontWeight !== thisStyle.fontWeight ||
prevStyle.fontStyle !== thisStyle.fontStyle
prevStyle.fontStyle !== thisStyle.fontStyle ||
prevStyle.deltaY != thisStyle.deltaY
);
},

Expand Down Expand Up @@ -1204,16 +1242,35 @@
},

/**
* @private
* @param {Number} LineIndex
* @param {Number} charIndex
* @param {String} property
* Retrieves the value of property at given character position
* @param {Number} lineIndex the line number
* @param {Number} charIndex the charater number
* @param {String} property the property name
* @returns the value of 'property'
*/
getValueOfPropertyAt: function(lineIndex, charIndex, property) {
var charStyle = this._getStyleDeclaration(lineIndex, charIndex),
styleDecoration = charStyle && typeof charStyle[property] !== 'undefined';
return styleDecoration ? charStyle[property] : this[property];
var charStyle = this._getStyleDeclaration(lineIndex, charIndex);

if (charStyle && typeof charStyle[property] !== 'undefined') {
return charStyle[property];
}

return this[property];
},

/**
* Assigns 'value' to the property 'key' at given character position
* @param {Number} line the line number
* @param {Number} char the character number
* @param {String} key the property name
* @param {Any} value the value
* @returns {Object} this
*/
setPropertyAt: function(line, char, key, value) {
var decl = this._getStyleDeclaration(line, char) || {};
decl[key] = value;
this._setStyleDeclaration(line, char, decl);
return this;
},

/**
Expand Down Expand Up @@ -1379,6 +1436,22 @@
complexity: function() {
return 1;
}
}, function(prototype, Text) {
prototype.superscript.apply = prototype.subscript.apply =
/**
* Mutates the style at given position in 'self' based on values from 'this'
* @param {Object} self the object to be mutated
* @param {Number} line the line number
* @param {Number} char the character number
* @returns {Object} self
*/
function(self, line, char) {
var size = self.getValueOfPropertyAt(line, char, 'fontSize');
var dy = self.getValueOfPropertyAt(line, char, 'deltaY') || 0;
self.setPropertyAt(line, char, 'fontSize', size * this.size);
self.setPropertyAt(line, char, 'deltaY', dy + size * -this.baseline);
return self;
};
});

/* _FROM_SVG_START_ */
Expand Down
4 changes: 4 additions & 0 deletions src/util/lang_class.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@

/** @ignore */
addMethods = function(klass, source, parent) {
if (typeof source === 'function') {
return source(klass.prototype, klass, parent);
}

for (var property in source) {

if (property in klass.prototype &&
Expand Down
30 changes: 30 additions & 0 deletions test/unit/text.js
Original file line number Diff line number Diff line change
Expand Up @@ -382,4 +382,34 @@
equal(cache, cache2, 'you get the same cache');
});

test('text superscript', function() {
var text = new fabric.Text('xxx');
var size = text.fontSize;
var schema = text.superscript;

ok(typeof text.setSuperscript === 'function');
text.setSuperscript(0, 1);

equal(text.styles[0][0].fontSize, size, 'character 0: fontSize remained the same');
equal(text.styles[0][0].deltaY, undefined, 'character 0: deltaY is not set');

equal(text.styles[0][1].fontSize, size * schema.size, 'character 1: fontSize was reduced');
equal(text.styles[0][1].deltaY, size * -schema.baseline, 'character 1: deltaY has been set');
});

test('text subscript', function() {
var text = new fabric.Text('xxx');
var size = text.fontSize;
var schema = text.subscript;

ok(typeof text.setSubscript === 'function');
text.setSubscript(0, 1);

equal(text.styles[0][0].fontSize, size, 'character 0: fontSize remained the same');
equal(text.styles[0][0].deltaY, undefined, 'character 0: deltaY is not set');

equal(text.styles[0][1].fontSize, size * schema.size, 'character 1: fontSize was reduced');
equal(text.styles[0][1].deltaY, size * -schema.baseline, 'character 1: deltaY has been set');
});

})();

0 comments on commit 045fe04

Please sign in to comment.