Refactor text layouting code

master
Sven Slootweg 7 years ago
parent 17909f2ed1
commit 72845b03c8

@ -1,7 +0,0 @@
'use strict';
module.exports = function adjustLineHeights(lines, lineHeight) {
return lines.map((line, i) => {
return line.height * lineHeight * 1.13;
});
};

@ -12,8 +12,6 @@ const max = require("../util/max");
const last = require("../util/last"); const last = require("../util/last");
function drawDebugLine(context, lineNumber, y, width, color = "red") { function drawDebugLine(context, lineNumber, y, width, color = "red") {
//context.globalAlpha = 1;
context.strokeStyle = color; context.strokeStyle = color;
context.beginPath(); context.beginPath();
@ -35,164 +33,129 @@ function drawDebugLine(context, lineNumber, y, width, color = "red") {
context.font = "12px sans-serif"; context.font = "12px sans-serif";
context.fillStyle = "white"; context.fillStyle = "white";
context.fillText(lineNumber, 5 + boxOffset * 18, y + 15) context.fillText(lineNumber, 5 + boxOffset * 18, y + 15);
//context.globalAlpha = 1;
}
module.exports = function layoutFormattedText(text, options) {
if (options.tags === true) {
let lines = parser(text).map((lineItems) => {
let measuredItems = lineItems.filter(item => item.text !== "").map((item) => {
let generatedStyle = generateStyle(item, {
classes: options.classes,
defaultStyle: options.defaultStyle
});
return Object.assign({
measurements: measureText(item.text, generatedStyle),
style: generatedStyle
}, item);
});
if (measuredItems.length > 0) {
return {
alignment: (lineItems.length > 0) ? findAlignment(lineItems[0]) : null,
items: measuredItems,
height: max(measuredItems.map(item => item.measurements.height)),
width: sum(measuredItems.map(item => item.measurements.width)),
minAscender: min(measuredItems.map(item => item.measurements.ascender)),
maxDescender: max(measuredItems.map(item => item.measurements.descender))
}
} else {
/* This line does not contain any real elements, so we'll omit it. */
return null;
} }
}).filter(line => line != null);
let adjustedLineHeights = adjustLineHeights(lines, options.lineHeight); function layoutItems(lines, options = {}) {
let currentYOffset = options.initialY;
/* We start out by moving everything up half the first line's height - this let currentXOffset = 0;
* compensates for the middle-of-the-line-oriented line spacing of the first let debugYOffset = 0;
* line. Otherwise, everything would be shifted off the canvas.*/
let startHeightCorrection, endHeightCorrection;
if (options.trimVerticalWhitespace) {
startHeightCorrection = -(adjustedLineHeights[0] / 2) - (lines[0].minAscender / 2);
endHeightCorrection = -(last(adjustedLineHeights) / 2) - (last(lines).minAscender / 2);
} else {
startHeightCorrection = 0;
endHeightCorrection = 0;
}
let currentLineOffset = startHeightCorrection;
let debugLineOffset = 0;
let currentComponentOffset = 0;
let debugLines = []; let debugLines = [];
let positionedItems = lines.reduce((items, line, i) => { let positionedItems = lines.reduce((items, line, i) => {
debugLines.push({color: "red", y: debugLineOffset, lineNumber: i}); debugLines.push({color: "red", y: debugYOffset, lineNumber: i});
debugLines.push({color: "green", y: currentLineOffset, lineNumber: i}); debugLines.push({color: "green", y: currentYOffset, lineNumber: i});
debugLines.push({color: "orange", y: currentLineOffset + adjustedLineHeights[i], lineNumber: i}); debugLines.push({color: "orange", y: currentYOffset + line.adjustedHeight, lineNumber: i});
let newItems = items.concat(line.items.map((item) => { let newItems = items.concat(line.items.map((item) => {
let positionedItem = Object.assign({ let x = currentXOffset;
x: currentComponentOffset, let y = currentYOffset - (line.minAscender / 2) + (line.adjustedHeight / 2)
y: currentLineOffset - (line.minAscender / 2) + (adjustedLineHeights[i] / 2)
}, item); debugLines.push({color: "blue", y: y, x1: x, x2: x + item.measurements.width, lineNumber: i});
debugLines.push({color: "blue", y: positionedItem.y, x1: positionedItem.x, x2: positionedItem.x + item.measurements.width, lineNumber: i}); currentXOffset += item.measurements.width;
currentComponentOffset += item.measurements.width; return Object.assign({
return positionedItem; x: x,
y: y
}, item);
})); }));
currentLineOffset += adjustedLineHeights[i]; currentYOffset += line.adjustedHeight;
debugLineOffset += adjustedLineHeights[i]; debugYOffset += line.adjustedHeight;
currentComponentOffset = 0; currentXOffset = 0;
return newItems; return newItems;
}, []); }, []);
return { return {
width: Math.ceil(max(lines.map(line => line.width))), positionedItems: positionedItems,
height: Math.ceil(sum(adjustedLineHeights) + startHeightCorrection + endHeightCorrection + (last(lines).maxDescender)), debugLines: debugLines
items: positionedItems,
drawDebugLines: function drawDebugLines(context) {
debugLines.forEach((debugLine) => {
let width;
if (debugLine.x1 != null) {
width = [debugLine.x1, debugLine.x2];
} else {
width = this.width;
} }
drawDebugLine(context, debugLine.lineNumber, debugLine.y, width, debugLine.color);
});
} }
}
} else {
let debugLines = [];
let lines = text.split("\n").map((line) => { module.exports = function layoutFormattedText(text, options) {
let lines;
if (options.tags === true) {
lines = parser(text).map((lineItems) => {
return { return {
text: line, items: lineItems
measurements: measureText(line, options.defaultStyle)
}; };
}); });
} else {
lines = text.split("\n").map((line) => {
let lineMeasurements = measureText(line, options.defaultStyle);
let adjustedLineHeights = adjustLineHeights(lines.map((line) => {
return { return {
text: line.text, items: [{
height: line.measurements.height text: line
}]
};
});
} }
}), options.lineHeight);
let startHeightCorrection, endHeightCorrection; let processedLines = lines.map((line) => {
let processedItems = line.items.map((item) => {
let style;
if (options.trimVerticalWhitespace) { if (item.tags != null) {
startHeightCorrection = -(adjustedLineHeights[0] / 2) - (lines[0].measurements.ascender / 2); style = Object.assign({}, options.defaultStyle, generateStyle(item.tags, options.classes));
endHeightCorrection = -(last(adjustedLineHeights) / 2) - (last(lines).measurements.ascender / 2);
} else { } else {
startHeightCorrection = 0; style = options.defaultStyle;
endHeightCorrection = 0;
} }
let currentLineOffset = startHeightCorrection; return Object.assign(item, {
style: style,
measurements: measureText(item.text, style)
});
});
let debugLineOffset = 0; let height = max(processedItems.map(item => item.measurements.height));
let positionedItems = lines.map((line, i) => { return Object.assign(line, {
let newItem = { alignment: (line.items.length > 0) ? findAlignment(line.items[0]) : null,
text: line.text, items: processedItems,
measurements: line.measurements, height: height,
style: options.defaultStyle, adjustedHeight: height * options.lineHeight * 1.13,
x: 0, width: sum(processedItems.map(item => item.measurements.width)),
y: currentLineOffset - (line.measurements.ascender / 2) + (adjustedLineHeights[i] / 2) minAscender: min(processedItems.map(item => item.measurements.ascender)),
} maxDescender: max(processedItems.map(item => item.measurements.descender))
});
});
debugLines.push({color: "red", y: debugLineOffset, lineNumber: i}); let startHeightCorrection, endHeightCorrection;
debugLines.push({color: "green", y: currentLineOffset, lineNumber: i});
debugLines.push({color: "blue", y: newItem.y, lineNumber: i});
debugLines.push({color: "orange", y: currentLineOffset + adjustedLineHeights[i], lineNumber: i});
currentLineOffset += adjustedLineHeights[i]; if (options.trimVerticalWhitespace) {
debugLineOffset += adjustedLineHeights[i]; startHeightCorrection = -(lines[0].adjustedHeight / 2) - (lines[0].minAscender / 2);
endHeightCorrection = -(last(lines).adjustedHeight / 2) - (last(lines).minAscender / 2);
} else {
startHeightCorrection = 0;
endHeightCorrection = 0;
}
return newItem; let {positionedItems, debugLines} = layoutItems(lines, {
initialY: startHeightCorrection
}); });
let combinedLineHeights = sum(lines.map(line => line.adjustedHeight));
return { return {
width: Math.ceil(max(lines.map(line => line.measurements.width))), width: Math.ceil(max(lines.map(line => line.width))),
height: Math.ceil(sum(adjustedLineHeights) + startHeightCorrection + endHeightCorrection + (last(lines).measurements.descender)), height: Math.ceil(combinedLineHeights + startHeightCorrection + endHeightCorrection + last(lines).maxDescender),
items: positionedItems, items: positionedItems,
drawDebugLines: function drawDebugLines(context) { drawDebugLines: function drawDebugLines(context) {
debugLines.forEach((debugLine) => { debugLines.forEach((debugLine) => {
drawDebugLine(context, debugLine.lineNumber, debugLine.y, this.width, debugLine.color); let width;
});
if (debugLine.x1 != null) {
width = [debugLine.x1, debugLine.x2];
} else {
width = this.width;
} }
drawDebugLine(context, debugLine.lineNumber, debugLine.y, width, debugLine.color);
});
} }
} }
}; };

Loading…
Cancel
Save