209 lines
8.4 KiB
JavaScript
209 lines
8.4 KiB
JavaScript
import { CombedTextLayoutError } from "../errors";
|
|
import { TextAlignment } from "./alignment";
|
|
import { cleanText, lineSplit, mergeLines, charAtIndex, charSplit, } from "../../utils";
|
|
var MIN_FONT_SIZE = 4;
|
|
var MAX_FONT_SIZE = 500;
|
|
var computeFontSize = function (lines, font, bounds, multiline) {
|
|
if (multiline === void 0) { multiline = false; }
|
|
var fontSize = MIN_FONT_SIZE;
|
|
while (fontSize < MAX_FONT_SIZE) {
|
|
var linesUsed = 0;
|
|
for (var lineIdx = 0, lineLen = lines.length; lineIdx < lineLen; lineIdx++) {
|
|
linesUsed += 1;
|
|
var line = lines[lineIdx];
|
|
var words = line.split(' ');
|
|
// Layout the words using the current `fontSize`, line wrapping
|
|
// whenever we reach the end of the current line.
|
|
var spaceInLineRemaining = bounds.width;
|
|
for (var idx = 0, len = words.length; idx < len; idx++) {
|
|
var isLastWord = idx === len - 1;
|
|
var word = isLastWord ? words[idx] : words[idx] + ' ';
|
|
var widthOfWord = font.widthOfTextAtSize(word, fontSize);
|
|
spaceInLineRemaining -= widthOfWord;
|
|
if (spaceInLineRemaining <= 0) {
|
|
linesUsed += 1;
|
|
spaceInLineRemaining = bounds.width - widthOfWord;
|
|
}
|
|
}
|
|
}
|
|
// Return if we exceeded the allowed width
|
|
if (!multiline && linesUsed > lines.length)
|
|
return fontSize - 1;
|
|
var height = font.heightAtSize(fontSize);
|
|
var lineHeight = height + height * 0.2;
|
|
var totalHeight = lineHeight * linesUsed;
|
|
// Return if we exceeded the allowed height
|
|
if (totalHeight > Math.abs(bounds.height))
|
|
return fontSize - 1;
|
|
fontSize += 1;
|
|
}
|
|
return fontSize;
|
|
};
|
|
var computeCombedFontSize = function (line, font, bounds, cellCount) {
|
|
var cellWidth = bounds.width / cellCount;
|
|
var cellHeight = bounds.height;
|
|
var fontSize = MIN_FONT_SIZE;
|
|
var chars = charSplit(line);
|
|
while (fontSize < MAX_FONT_SIZE) {
|
|
for (var idx = 0, len = chars.length; idx < len; idx++) {
|
|
var c = chars[idx];
|
|
var tooLong = font.widthOfTextAtSize(c, fontSize) > cellWidth * 0.75;
|
|
if (tooLong)
|
|
return fontSize - 1;
|
|
}
|
|
var height = font.heightAtSize(fontSize, { descender: false });
|
|
if (height > cellHeight)
|
|
return fontSize - 1;
|
|
fontSize += 1;
|
|
}
|
|
return fontSize;
|
|
};
|
|
var lastIndexOfWhitespace = function (line) {
|
|
for (var idx = line.length; idx > 0; idx--) {
|
|
if (/\s/.test(line[idx]))
|
|
return idx;
|
|
}
|
|
return undefined;
|
|
};
|
|
var splitOutLines = function (input, maxWidth, font, fontSize) {
|
|
var _a;
|
|
var lastWhitespaceIdx = input.length;
|
|
while (lastWhitespaceIdx > 0) {
|
|
var line = input.substring(0, lastWhitespaceIdx);
|
|
var encoded = font.encodeText(line);
|
|
var width = font.widthOfTextAtSize(line, fontSize);
|
|
if (width < maxWidth) {
|
|
var remainder = input.substring(lastWhitespaceIdx) || undefined;
|
|
return { line: line, encoded: encoded, width: width, remainder: remainder };
|
|
}
|
|
lastWhitespaceIdx = (_a = lastIndexOfWhitespace(line)) !== null && _a !== void 0 ? _a : 0;
|
|
}
|
|
// We were unable to split the input enough to get a chunk that would fit
|
|
// within the specified `maxWidth` so we'll just return everything
|
|
return {
|
|
line: input,
|
|
encoded: font.encodeText(input),
|
|
width: font.widthOfTextAtSize(input, fontSize),
|
|
remainder: undefined,
|
|
};
|
|
};
|
|
export var layoutMultilineText = function (text, _a) {
|
|
var alignment = _a.alignment, fontSize = _a.fontSize, font = _a.font, bounds = _a.bounds;
|
|
var lines = lineSplit(cleanText(text));
|
|
if (fontSize === undefined || fontSize === 0) {
|
|
fontSize = computeFontSize(lines, font, bounds, true);
|
|
}
|
|
var height = font.heightAtSize(fontSize);
|
|
var lineHeight = height + height * 0.2;
|
|
var textLines = [];
|
|
var minX = bounds.x;
|
|
var minY = bounds.y;
|
|
var maxX = bounds.x + bounds.width;
|
|
var maxY = bounds.y + bounds.height;
|
|
var y = bounds.y + bounds.height;
|
|
for (var idx = 0, len = lines.length; idx < len; idx++) {
|
|
var prevRemainder = lines[idx];
|
|
while (prevRemainder !== undefined) {
|
|
var _b = splitOutLines(prevRemainder, bounds.width, font, fontSize), line = _b.line, encoded = _b.encoded, width = _b.width, remainder = _b.remainder;
|
|
// prettier-ignore
|
|
var x = (alignment === TextAlignment.Left ? bounds.x
|
|
: alignment === TextAlignment.Center ? bounds.x + (bounds.width / 2) - (width / 2)
|
|
: alignment === TextAlignment.Right ? bounds.x + bounds.width - width
|
|
: bounds.x);
|
|
y -= lineHeight;
|
|
if (x < minX)
|
|
minX = x;
|
|
if (y < minY)
|
|
minY = y;
|
|
if (x + width > maxX)
|
|
maxX = x + width;
|
|
if (y + height > maxY)
|
|
maxY = y + height;
|
|
textLines.push({ text: line, encoded: encoded, width: width, height: height, x: x, y: y });
|
|
// Only trim lines that we had to split ourselves. So we won't trim lines
|
|
// that the user provided themselves with whitespace.
|
|
prevRemainder = remainder === null || remainder === void 0 ? void 0 : remainder.trim();
|
|
}
|
|
}
|
|
return {
|
|
fontSize: fontSize,
|
|
lineHeight: lineHeight,
|
|
lines: textLines,
|
|
bounds: {
|
|
x: minX,
|
|
y: minY,
|
|
width: maxX - minX,
|
|
height: maxY - minY,
|
|
},
|
|
};
|
|
};
|
|
export var layoutCombedText = function (text, _a) {
|
|
var fontSize = _a.fontSize, font = _a.font, bounds = _a.bounds, cellCount = _a.cellCount;
|
|
var line = mergeLines(cleanText(text));
|
|
if (line.length > cellCount) {
|
|
throw new CombedTextLayoutError(line.length, cellCount);
|
|
}
|
|
if (fontSize === undefined || fontSize === 0) {
|
|
fontSize = computeCombedFontSize(line, font, bounds, cellCount);
|
|
}
|
|
var cellWidth = bounds.width / cellCount;
|
|
var height = font.heightAtSize(fontSize, { descender: false });
|
|
var y = bounds.y + (bounds.height / 2 - height / 2);
|
|
var cells = [];
|
|
var minX = bounds.x;
|
|
var minY = bounds.y;
|
|
var maxX = bounds.x + bounds.width;
|
|
var maxY = bounds.y + bounds.height;
|
|
var cellOffset = 0;
|
|
var charOffset = 0;
|
|
while (cellOffset < cellCount) {
|
|
var _b = charAtIndex(line, charOffset), char = _b[0], charLength = _b[1];
|
|
var encoded = font.encodeText(char);
|
|
var width = font.widthOfTextAtSize(char, fontSize);
|
|
var cellCenter = bounds.x + (cellWidth * cellOffset + cellWidth / 2);
|
|
var x = cellCenter - width / 2;
|
|
if (x < minX)
|
|
minX = x;
|
|
if (y < minY)
|
|
minY = y;
|
|
if (x + width > maxX)
|
|
maxX = x + width;
|
|
if (y + height > maxY)
|
|
maxY = y + height;
|
|
cells.push({ text: line, encoded: encoded, width: width, height: height, x: x, y: y });
|
|
cellOffset += 1;
|
|
charOffset += charLength;
|
|
}
|
|
return {
|
|
fontSize: fontSize,
|
|
cells: cells,
|
|
bounds: {
|
|
x: minX,
|
|
y: minY,
|
|
width: maxX - minX,
|
|
height: maxY - minY,
|
|
},
|
|
};
|
|
};
|
|
export var layoutSinglelineText = function (text, _a) {
|
|
var alignment = _a.alignment, fontSize = _a.fontSize, font = _a.font, bounds = _a.bounds;
|
|
var line = mergeLines(cleanText(text));
|
|
if (fontSize === undefined || fontSize === 0) {
|
|
fontSize = computeFontSize([line], font, bounds);
|
|
}
|
|
var encoded = font.encodeText(line);
|
|
var width = font.widthOfTextAtSize(line, fontSize);
|
|
var height = font.heightAtSize(fontSize, { descender: false });
|
|
// prettier-ignore
|
|
var x = (alignment === TextAlignment.Left ? bounds.x
|
|
: alignment === TextAlignment.Center ? bounds.x + (bounds.width / 2) - (width / 2)
|
|
: alignment === TextAlignment.Right ? bounds.x + bounds.width - width
|
|
: bounds.x);
|
|
var y = bounds.y + (bounds.height / 2 - height / 2);
|
|
return {
|
|
fontSize: fontSize,
|
|
line: { text: line, encoded: encoded, width: width, height: height, x: x, y: y },
|
|
bounds: { x: x, y: y, width: width, height: height },
|
|
};
|
|
};
|
|
//# sourceMappingURL=layout.js.map
|