const charSpacing = function charSpacing(text, charSpace) {
let txt = text.trim();
return (txt.length) ? (txt.length - 1) * charSpace : 0;
};
// Have to set up word as a constant, then export it below
// so that Line can see it. Otherwise, an error is thrown.
const Word = class Word {
constructor(word, pathOptions) {
this._value = word;
this._pathOptions = pathOptions;
this._last = false;
this._text = (word === ' ') ? 'o' : word; // allows space to get an actual dimension
}
get value() {
return this._value;
}
get dimensions() {
if (this._dimensions) {
return this._dimensions;
}
this._dimensions = this._pathOptions.font.calculateTextDimensions(
this._text, this._pathOptions.size
);
this._dimensions.xMax += this.charSpacing;
return this._dimensions;
}
get last() {
return this._last;
}
get charSpacing() {
return charSpacing(this._text, this._pathOptions.charSpace);
}
lastWord(value=true) { // indicate last word in line (for justification)
this._last = value;
if (this._last) {
this._value = this._value.trim(); // wack any trailing space
this._text = this._value;
this._dimensions = this._pathOptions.font.calculateTextDimensions(
this._text, this._pathOptions.size
);
this._dimensions.xMax += this.charSpacing;
}
}
};
exports.Word = Word; // ... now export Word to the rest of the library.
exports.Line = class Line {
constructor(width, height, size, pathOptions) {
this._width = width || 999999999;
this._height = height;
this._pathOptions = pathOptions;
this.size = size || pathOptions.size;
this._lineID = Date.now() * Math.random();
this.wordObjects = [];
}
set lineID(id) {
if (id) {
this._lineID = id;
}
}
get lineID() {
return this._lineID;
}
addWord(wordObject) {
this.wordObjects.push(wordObject);
}
indent(amount) {
for (let i = 0; i < amount; i++) {
this.addWord(new Word(' ', this._pathOptions));
}
}
markLastWord() {
if (this.wordObjects.length > 0) {
this.wordObjects[this.wordObjects.length-1].lastWord();
}
}
get lastWord() {
return this.wordObjects[this.wordObjects.length-1];
}
charSpacing(text) {
return charSpacing(text, this._pathOptions.charSpace);
}
canFit(wordObject) {
const tempValue = this.value + wordObject.value;
const toWidth = this._pathOptions.font.calculateTextDimensions(
tempValue, this.size
).xMax + this.charSpacing(tempValue);
return (toWidth <= this.width);
}
replaceLastWord(wordObject) {
if (typeof wordObject === 'string') {
wordObject = new Word(wordObject, this._pathOptions);
}
this.wordObjects.pop();
this.addWord(wordObject);
wordObject.lastWord();
}
get words() {
return this.wordObjects;
}
get spaceWidth() {
return this._pathOptions.font.calculateTextDimensions(
'o', this.size
).width;
}
get value() {
const value = this.wordObjects.reduce((string, word) => {
string += word.value;
return string;
}, '');
return value;
}
get currentWidth() {
return this._pathOptions.font.calculateTextDimensions(
this.value, this.size
).xMax + this.charSpacing(this.value);
}
get textWidth() {
return this.wordObjects.reduce((width, word) => {
width += word.dimensions.xMax;
return width;
}, 0);
}
get width() {
return this._width;
}
// dynamic adjust height based on word height?
get height() {
if (this._height) {
return this._height;
}
const toHeight = this._pathOptions.font.calculateTextDimensions(
this.value, this.size
).height; // ymax
return toHeight + 20;
}
};
/**
* @todo handle page margin and padding
*/
exports._getTextBoxOffset = function _getTextBoxOffset(textBox, options = {}) {
let offsetX = 0;
let offsetY = -textBox.firstLineHeight;
let { width, height, textHeight } = textBox;
if (options.align) {
const alignments = options.align.split(' ');
if (alignments[0]) {
switch (alignments[0]) {
case 'center':
offsetX = -1 * width / 2;
break;
case 'right':
offsetX = -width;
break;
default:
}
}
if (alignments[1]) {
height = height || textHeight;
switch (alignments[1]) {
case 'center':
offsetY = (textBox.isSimpleText) ?
-textBox.firstLineHeight / 2 :
height / 2 + offsetY;
break;
case 'bottom':
offsetY = height + offsetY;
break;
default:
}
}
}
return {
offsetX,
offsetY
};
};
/**
* Get text dimensions
* @name textDimensions
* @function
* @memberof Recipe
* @param {string} text - text to be measured
* @param {Object} [options] - The options
* @param {string} [options.font='helvetica'] - name of font from which measurements are to be taken
* @param {number} [options.size=14] - size of font to be used in taking measurements
* @param {number} [options.charSpace=0] - character spacing being applied to the given text.
* @returns {Object} measurement components of given text: width, height, xMin, xMax, yMin, yMax
*/
exports.textDimensions = function textDimensions(text, options = {}) {
const font = this._getFont(options);
let dimensions = {};
let charSpaces = 0;
if (font) {
if (options.charSpace) {
charSpaces = charSpacing(text, options.charSpace);
}
const fontSize = options.size || this.current.defaultFontSize;
dimensions = font.calculateTextDimensions(text, fontSize);
dimensions.xMax += charSpaces;
}
return dimensions;
};
exports.Column = class Column {
constructor(x, y, width, height, text='', field='', options={}) {
this._x = x;
this._y = y;
this._width = width;
this._height = height || 99999;
this._field = field; // associated data field
this._text = text || field; // for column title
this._gap = 0;
this._options = JSON.parse(JSON.stringify(options)); // cloning
this._options.textBox = this._options.cell;
if (this._options.cell) {
delete this._options['cell'];
}
if (!this._options.textBox || this._options.textBox.padding === undefined) {
this._options.textBox = this._options.textBox || {};
this._options.textBox.padding = 2;
}
if (!this._options.header || typeof this._options.header==='boolean') {
this._options.header = {bold:true, textBox:{padding:2, textAlign:'center center'}};
}
if (options.renderer) {
this._options.renderer = options.renderer;
}
}
get width() {
return this._width;
}
get height() {
return this._height;
}
get x() {
return this._x;
}
set x(x) {
this._x = x;
}
get y() {
return this._y;
}
get position() {
return [this._x, this._y];
}
set position(pos) {
[this._x, this._y] = pos;
}
get gap() {
return this._gap;
}
set gap(gap) {
this._gap = gap;
}
get field() {
return this._field;
}
get text() {
return this._text;
}
get options() {
return this._options;
}
};