160 lines
5.0 KiB
JavaScript
160 lines
5.0 KiB
JavaScript
"use strict";
|
|
function getBitmapTextLayout(chars, style, font, trimEnd) {
|
|
const layoutData = {
|
|
width: 0,
|
|
height: 0,
|
|
offsetY: 0,
|
|
scale: style.fontSize / font.baseMeasurementFontSize,
|
|
lines: [{
|
|
width: 0,
|
|
charPositions: [],
|
|
spaceWidth: 0,
|
|
spacesIndex: [],
|
|
chars: []
|
|
}]
|
|
};
|
|
layoutData.offsetY = font.baseLineOffset;
|
|
let currentLine = layoutData.lines[0];
|
|
let previousChar = null;
|
|
let firstWord = true;
|
|
const currentWord = {
|
|
spaceWord: false,
|
|
width: 0,
|
|
start: 0,
|
|
index: 0,
|
|
// use index to not modify the array as we use it a lot!
|
|
positions: [],
|
|
chars: []
|
|
};
|
|
const nextWord = (word) => {
|
|
const start = currentLine.width;
|
|
for (let j = 0; j < currentWord.index; j++) {
|
|
const position = word.positions[j];
|
|
currentLine.chars.push(word.chars[j]);
|
|
currentLine.charPositions.push(position + start);
|
|
}
|
|
currentLine.width += word.width;
|
|
firstWord = false;
|
|
currentWord.width = 0;
|
|
currentWord.index = 0;
|
|
currentWord.chars.length = 0;
|
|
};
|
|
const nextLine = () => {
|
|
let index = currentLine.chars.length - 1;
|
|
if (trimEnd) {
|
|
let lastChar = currentLine.chars[index];
|
|
while (lastChar === " ") {
|
|
currentLine.width -= font.chars[lastChar].xAdvance;
|
|
lastChar = currentLine.chars[--index];
|
|
}
|
|
}
|
|
layoutData.width = Math.max(layoutData.width, currentLine.width);
|
|
currentLine = {
|
|
width: 0,
|
|
charPositions: [],
|
|
chars: [],
|
|
spaceWidth: 0,
|
|
spacesIndex: []
|
|
};
|
|
firstWord = true;
|
|
layoutData.lines.push(currentLine);
|
|
layoutData.height += font.lineHeight;
|
|
};
|
|
const scale = font.baseMeasurementFontSize / style.fontSize;
|
|
const adjustedLetterSpacing = style.letterSpacing * scale;
|
|
const adjustedWordWrapWidth = style.wordWrapWidth * scale;
|
|
for (let i = 0; i < chars.length + 1; i++) {
|
|
let char;
|
|
const isEnd = i === chars.length;
|
|
if (!isEnd) {
|
|
char = chars[i];
|
|
}
|
|
const charData = font.chars[char] || font.chars[" "];
|
|
const isSpace = /(?:\s)/.test(char);
|
|
const isWordBreak = isSpace || char === "\r" || char === "\n" || isEnd;
|
|
if (isWordBreak) {
|
|
const addWordToNextLine = !firstWord && style.wordWrap && currentLine.width + currentWord.width - adjustedLetterSpacing > adjustedWordWrapWidth;
|
|
if (addWordToNextLine) {
|
|
nextLine();
|
|
nextWord(currentWord);
|
|
if (!isEnd) {
|
|
currentLine.charPositions.push(0);
|
|
}
|
|
} else {
|
|
currentWord.start = currentLine.width;
|
|
nextWord(currentWord);
|
|
if (!isEnd) {
|
|
currentLine.charPositions.push(0);
|
|
}
|
|
}
|
|
if (char === "\r" || char === "\n") {
|
|
if (currentLine.width !== 0) {
|
|
nextLine();
|
|
}
|
|
} else if (!isEnd) {
|
|
const spaceWidth = charData.xAdvance + (charData.kerning[previousChar] || 0) + adjustedLetterSpacing;
|
|
currentLine.width += spaceWidth;
|
|
currentLine.spaceWidth = spaceWidth;
|
|
currentLine.spacesIndex.push(currentLine.charPositions.length);
|
|
currentLine.chars.push(char);
|
|
}
|
|
} else {
|
|
const kerning = charData.kerning[previousChar] || 0;
|
|
const nextCharWidth = charData.xAdvance + kerning + adjustedLetterSpacing;
|
|
currentWord.positions[currentWord.index++] = currentWord.width + kerning;
|
|
currentWord.chars.push(char);
|
|
currentWord.width += nextCharWidth;
|
|
}
|
|
previousChar = char;
|
|
}
|
|
nextLine();
|
|
if (style.align === "center") {
|
|
alignCenter(layoutData);
|
|
} else if (style.align === "right") {
|
|
alignRight(layoutData);
|
|
} else if (style.align === "justify") {
|
|
alignJustify(layoutData);
|
|
}
|
|
return layoutData;
|
|
}
|
|
function alignCenter(measurementData) {
|
|
for (let i = 0; i < measurementData.lines.length; i++) {
|
|
const line = measurementData.lines[i];
|
|
const offset = measurementData.width / 2 - line.width / 2;
|
|
for (let j = 0; j < line.charPositions.length; j++) {
|
|
line.charPositions[j] += offset;
|
|
}
|
|
}
|
|
}
|
|
function alignRight(measurementData) {
|
|
for (let i = 0; i < measurementData.lines.length; i++) {
|
|
const line = measurementData.lines[i];
|
|
const offset = measurementData.width - line.width;
|
|
for (let j = 0; j < line.charPositions.length; j++) {
|
|
line.charPositions[j] += offset;
|
|
}
|
|
}
|
|
}
|
|
function alignJustify(measurementData) {
|
|
const width = measurementData.width;
|
|
for (let i = 0; i < measurementData.lines.length; i++) {
|
|
const line = measurementData.lines[i];
|
|
let indy = 0;
|
|
let spaceIndex = line.spacesIndex[indy++];
|
|
let offset = 0;
|
|
const totalSpaces = line.spacesIndex.length;
|
|
const newSpaceWidth = (width - line.width) / totalSpaces;
|
|
const spaceWidth = newSpaceWidth;
|
|
for (let j = 0; j < line.charPositions.length; j++) {
|
|
if (j === spaceIndex) {
|
|
spaceIndex = line.spacesIndex[indy++];
|
|
offset += spaceWidth;
|
|
}
|
|
line.charPositions[j] += offset;
|
|
}
|
|
}
|
|
}
|
|
|
|
export { getBitmapTextLayout };
|
|
//# sourceMappingURL=getBitmapTextLayout.mjs.map
|