Current File : //home/tradevaly/www/node_modules/pdfmake/src/textTools.js |
'use strict';
var isString = require('./helpers').isString;
var isNumber = require('./helpers').isNumber;
var isObject = require('./helpers').isObject;
var isArray = require('./helpers').isArray;
var isUndefined = require('./helpers').isUndefined;
var LineBreaker = require('linebreak');
var LEADING = /^(\s)+/g;
var TRAILING = /(\s)+$/g;
/**
* Creates an instance of TextTools - text measurement utility
*
* @constructor
* @param {FontProvider} fontProvider
*/
function TextTools(fontProvider) {
this.fontProvider = fontProvider;
}
/**
* Converts an array of strings (or inline-definition-objects) into a collection
* of inlines and calculated minWidth/maxWidth.
* and their min/max widths
* @param {Object} textArray - an array of inline-definition-objects (or strings)
* @param {Object} styleContextStack current style stack
* @return {Object} collection of inlines, minWidth, maxWidth
*/
TextTools.prototype.buildInlines = function (textArray, styleContextStack) {
var measured = measure(this.fontProvider, textArray, styleContextStack);
var minWidth = 0,
maxWidth = 0,
currentLineWidth;
measured.forEach(function (inline) {
minWidth = Math.max(minWidth, inline.width - inline.leadingCut - inline.trailingCut);
if (!currentLineWidth) {
currentLineWidth = { width: 0, leadingCut: inline.leadingCut, trailingCut: 0 };
}
currentLineWidth.width += inline.width;
currentLineWidth.trailingCut = inline.trailingCut;
maxWidth = Math.max(maxWidth, getTrimmedWidth(currentLineWidth));
if (inline.lineEnd) {
currentLineWidth = null;
}
});
if (getStyleProperty({}, styleContextStack, 'noWrap', false)) {
minWidth = maxWidth;
}
return {
items: measured,
minWidth: minWidth,
maxWidth: maxWidth
};
function getTrimmedWidth(item) {
return Math.max(0, item.width - item.leadingCut - item.trailingCut);
}
};
/**
* Returns size of the specified string (without breaking it) using the current style
* @param {String} text text to be measured
* @param {Object} styleContextStack current style stack
* @return {Object} size of the specified string
*/
TextTools.prototype.sizeOfString = function (text, styleContextStack) {
text = text ? text.toString().replace(/\t/g, ' ') : '';
//TODO: refactor - extract from measure
var fontName = getStyleProperty({}, styleContextStack, 'font', 'Roboto');
var fontSize = getStyleProperty({}, styleContextStack, 'fontSize', 12);
var fontFeatures = getStyleProperty({}, styleContextStack, 'fontFeatures', null);
var bold = getStyleProperty({}, styleContextStack, 'bold', false);
var italics = getStyleProperty({}, styleContextStack, 'italics', false);
var lineHeight = getStyleProperty({}, styleContextStack, 'lineHeight', 1);
var characterSpacing = getStyleProperty({}, styleContextStack, 'characterSpacing', 0);
var font = this.fontProvider.provideFont(fontName, bold, italics);
return {
width: widthOfString(text, font, fontSize, characterSpacing, fontFeatures),
height: font.lineHeight(fontSize) * lineHeight,
fontSize: fontSize,
lineHeight: lineHeight,
ascender: font.ascender / 1000 * fontSize,
descender: font.descender / 1000 * fontSize
};
};
/**
* Returns size of the specified rotated string (without breaking it) using the current style
*
* @param {string} text text to be measured
* @param {number} angle
* @param {object} styleContextStack current style stack
* @returns {object} size of the specified string
*/
TextTools.prototype.sizeOfRotatedText = function (text, angle, styleContextStack) {
var angleRad = angle * Math.PI / -180;
var size = this.sizeOfString(text, styleContextStack);
return {
width: Math.abs(size.height * Math.sin(angleRad)) + Math.abs(size.width * Math.cos(angleRad)),
height: Math.abs(size.width * Math.sin(angleRad)) + Math.abs(size.height * Math.cos(angleRad))
};
}
TextTools.prototype.widthOfString = function (text, font, fontSize, characterSpacing, fontFeatures) {
return widthOfString(text, font, fontSize, characterSpacing, fontFeatures);
};
function splitWords(text, noWrap) {
var results = [];
text = text.replace(/\t/g, ' ');
if (noWrap) {
results.push({ text: text });
return results;
}
var breaker = new LineBreaker(text);
var last = 0;
var bk;
while (bk = breaker.nextBreak()) {
var word = text.slice(last, bk.position);
if (bk.required || word.match(/\r?\n$|\r$/)) { // new line
word = word.replace(/\r?\n$|\r$/, '');
results.push({ text: word, lineEnd: true });
} else {
results.push({ text: word });
}
last = bk.position;
}
return results;
}
function copyStyle(source, destination) {
destination = destination || {};
source = source || {}; //TODO: default style
for (var key in source) {
if (key != 'text' && source.hasOwnProperty(key)) {
destination[key] = source[key];
}
}
return destination;
}
function normalizeTextArray(array, styleContextStack) {
function flatten(array) {
return array.reduce(function (prev, cur) {
var current = isArray(cur.text) ? flatten(cur.text) : cur;
var more = [].concat(current).some(Array.isArray);
return prev.concat(more ? flatten(current) : current);
}, []);
}
function getOneWord(index, words, noWrap) {
if (isUndefined(words[index])) {
return null;
}
if (words[index].lineEnd) {
return null;
}
var word = words[index].text;
if (noWrap) {
var tmpWords = splitWords(normalizeString(word), false);
if (isUndefined(tmpWords[tmpWords.length - 1])) {
return null;
}
word = tmpWords[tmpWords.length - 1].text;
}
return word;
}
var results = [];
if (!isArray(array)) {
array = [array];
}
array = flatten(array);
var lastWord = null;
for (var i = 0, l = array.length; i < l; i++) {
var item = array[i];
var style = null;
var words;
var noWrap = getStyleProperty(item || {}, styleContextStack, 'noWrap', false);
if (isObject(item)) {
if (item._textRef && item._textRef._textNodeRef.text) {
item.text = item._textRef._textNodeRef.text;
}
words = splitWords(normalizeString(item.text), noWrap);
style = copyStyle(item);
} else {
words = splitWords(normalizeString(item), noWrap);
}
if (lastWord && words.length) {
var firstWord = getOneWord(0, words, noWrap);
var wrapWords = splitWords(normalizeString(lastWord + firstWord), false);
if (wrapWords.length === 1) {
results[results.length - 1].noNewLine = true;
}
}
for (var i2 = 0, l2 = words.length; i2 < l2; i2++) {
var result = {
text: words[i2].text
};
if (words[i2].lineEnd) {
result.lineEnd = true;
}
copyStyle(style, result);
results.push(result);
}
lastWord = null;
if (i + 1 < l) {
lastWord = getOneWord(words.length - 1, words, noWrap);
}
}
return results;
}
function normalizeString(value) {
if (value === undefined || value === null) {
return '';
} else if (isNumber(value)) {
return value.toString();
} else if (isString(value)) {
return value;
} else {
return value.toString();
}
}
function getStyleProperty(item, styleContextStack, property, defaultValue) {
var value;
if (item[property] !== undefined && item[property] !== null) {
// item defines this property
return item[property];
}
if (!styleContextStack) {
return defaultValue;
}
styleContextStack.auto(item, function () {
value = styleContextStack.getProperty(property);
});
if (value !== null && value !== undefined) {
return value;
} else {
return defaultValue;
}
}
function measure(fontProvider, textArray, styleContextStack) {
var normalized = normalizeTextArray(textArray, styleContextStack);
if (normalized.length) {
var leadingIndent = getStyleProperty(normalized[0], styleContextStack, 'leadingIndent', 0);
if (leadingIndent) {
normalized[0].leadingCut = -leadingIndent;
normalized[0].leadingIndent = leadingIndent;
}
}
normalized.forEach(function (item) {
var fontName = getStyleProperty(item, styleContextStack, 'font', 'Roboto');
var fontSize = getStyleProperty(item, styleContextStack, 'fontSize', 12);
var fontFeatures = getStyleProperty(item, styleContextStack, 'fontFeatures', null);
var bold = getStyleProperty(item, styleContextStack, 'bold', false);
var italics = getStyleProperty(item, styleContextStack, 'italics', false);
var color = getStyleProperty(item, styleContextStack, 'color', 'black');
var decoration = getStyleProperty(item, styleContextStack, 'decoration', null);
var decorationColor = getStyleProperty(item, styleContextStack, 'decorationColor', null);
var decorationStyle = getStyleProperty(item, styleContextStack, 'decorationStyle', null);
var background = getStyleProperty(item, styleContextStack, 'background', null);
var lineHeight = getStyleProperty(item, styleContextStack, 'lineHeight', 1);
var characterSpacing = getStyleProperty(item, styleContextStack, 'characterSpacing', 0);
var link = getStyleProperty(item, styleContextStack, 'link', null);
var linkToPage = getStyleProperty(item, styleContextStack, 'linkToPage', null);
var linkToDestination = getStyleProperty(item, styleContextStack, 'linkToDestination', null);
var noWrap = getStyleProperty(item, styleContextStack, 'noWrap', null);
var preserveLeadingSpaces = getStyleProperty(item, styleContextStack, 'preserveLeadingSpaces', false);
var preserveTrailingSpaces = getStyleProperty(item, styleContextStack, 'preserveTrailingSpaces', false);
var opacity = getStyleProperty(item, styleContextStack, 'opacity', 1);
var sup = getStyleProperty(item, styleContextStack, 'sup', false);
var sub = getStyleProperty(item, styleContextStack, 'sub', false);
if ((sup || sub) && item.fontSize === undefined) {
// font size reduction taken from here: https://en.wikipedia.org/wiki/Subscript_and_superscript#Desktop_publishing
fontSize *= 0.58
}
var font = fontProvider.provideFont(fontName, bold, italics);
item.width = widthOfString(item.text, font, fontSize, characterSpacing, fontFeatures);
item.height = font.lineHeight(fontSize) * lineHeight;
if (!item.leadingCut) {
item.leadingCut = 0;
}
var leadingSpaces;
if (!preserveLeadingSpaces && (leadingSpaces = item.text.match(LEADING))) {
item.leadingCut += widthOfString(leadingSpaces[0], font, fontSize, characterSpacing, fontFeatures);
}
var trailingSpaces;
if (!preserveTrailingSpaces && (trailingSpaces = item.text.match(TRAILING))) {
item.trailingCut = widthOfString(trailingSpaces[0], font, fontSize, characterSpacing, fontFeatures);
} else {
item.trailingCut = 0;
}
item.alignment = getStyleProperty(item, styleContextStack, 'alignment', 'left');
item.font = font;
item.fontSize = fontSize;
item.fontFeatures = fontFeatures;
item.characterSpacing = characterSpacing;
item.color = color;
item.decoration = decoration;
item.decorationColor = decorationColor;
item.decorationStyle = decorationStyle;
item.background = background;
item.link = link;
item.linkToPage = linkToPage;
item.linkToDestination = linkToDestination;
item.noWrap = noWrap;
item.opacity = opacity;
item.sup = sup;
item.sub = sub;
});
return normalized;
}
function widthOfString(text, font, fontSize, characterSpacing, fontFeatures) {
return font.widthOfString(text, fontSize, fontFeatures) + ((characterSpacing || 0) * (text.length - 1));
}
module.exports = TextTools;