Skip to content

Commit

Permalink
'deltaY', superscript, subscript etc.
Browse files Browse the repository at this point in the history
Honor 'deltaY' during rendering; provide functionality for 'superscript' and 'subscript'; fixed some bugs
  • Loading branch information
denim2x committed Aug 9, 2017
1 parent e4432c8 commit 7b84dd4
Showing 1 changed file with 152 additions and 54 deletions.
206 changes: 152 additions & 54 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 characteristics (based on https://tr.im/subscript_superscript)
* @type Object
* @default
*/
superscript: {
size: 0.60, // fontSize factor
baseline: 0.67 // baseline-shift factor (upwards)
},

/**
* Subscript charateristics (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) || {};
this._setStyleDeclaration(loc.lineIndex, loc.charIndex, decl);
fabric.util.object.extend(decl, styles);
},

/**
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,22 +910,25 @@
* @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();
}

// TODO: Take 'deltaY' into account
var box = {
width: width,
left: 0,
height: charStyle.fontSize,
height: style.fontSize,
kernedWidth: kernedWidth,
};
if (charIndex > 0 && !skipLeft) {
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 @@ -1093,7 +1106,7 @@
* @param {Number} top Top coordinate
* @param {Number} lineHeight Height of the line
*/
_renderChar: function(method, ctx, lineIndex, charIndex, _char, left, top) {
_renderChar: function(method, ctx, lineIndex, charIndex, _char, left, top, lineHeight) {
var decl = this._getStyleDeclaration(lineIndex, charIndex),
fullDecl = this.getCompleteStyleDeclaration(lineIndex, charIndex),
shouldFill = method === 'fillText' && fullDecl.fill,
Expand All @@ -1109,11 +1122,77 @@
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();
},

/**
* Scales various attributes (given as keys in 'schema') based on
* the character's 'fontSize'
* @param {Number} line the line number
* @param {Number} char the character number
* @param {Object} schema object specifying what attributes to scale - and how
* @returns this
*/
scaleChar: function(line, char, schema) {
switch (typeof schema) {
case 'number':
schema = { size: schema };
break;
case 'object':
break;
default:
throw "[scale()] Invalid 'schema': " + schema;
}

var size = this.getValueOfPropertyAt(line, char, 'fontSize');
for (var key in schema) { // accounts for inherited properties
var value = schema[key];
if (typeof value != 'number')
return;

switch (key) {
case 'fontSize':
case 'size':
this.setPropertyAt(line, char, 'fontSize', size * value);
break;

case 'baseline':
this.setPropertyAt(line, char, 'deltaY', size * -value);
break;
}
}

return this;
},

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

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

/**
* @private
* @param {Object} prevStyle
Expand All @@ -1126,7 +1205,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 +1284,34 @@
},

/**
* @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 'value'
*/
setPropertyAt: function(line, char, key, value) {
var decl = this._getStyleDeclaration(line, char) || {};
decl[key] = value;
this._setStyleDeclaration(line, char, decl);
return value;
},

/**
Expand Down

0 comments on commit 7b84dd4

Please sign in to comment.