From 4cdd2eaff65b4d3a9dd41fbfb974dd525b9453b6 Mon Sep 17 00:00:00 2001 From: Sven Slootweg Date: Sun, 5 Mar 2017 18:46:04 +0100 Subject: [PATCH] Reorganization and cleanup --- src/index.js | 82 ++++-------------------- src/layout.js | 49 -------------- src/layout/adjust-line-heights.js | 12 ++++ src/layout/index.js | 102 ++++++++++++++++++++++++++++++ src/parser/index.js | 9 +++ src/parser/to-lines.js | 2 +- src/render/measure-text.js | 8 --- src/render/set-text-styles.js | 2 +- src/util/max.js | 5 ++ src/util/min.js | 5 ++ src/util/sum.js | 7 ++ 11 files changed, 156 insertions(+), 127 deletions(-) delete mode 100644 src/layout.js create mode 100644 src/layout/adjust-line-heights.js create mode 100644 src/layout/index.js create mode 100644 src/parser/index.js create mode 100644 src/util/max.js create mode 100644 src/util/min.js create mode 100644 src/util/sum.js diff --git a/src/index.js b/src/index.js index 37aa087..22075fb 100644 --- a/src/index.js +++ b/src/index.js @@ -3,9 +3,7 @@ const canvassed = require("canvassed"); const objectPick = require("object.pick"); -const tagParser = require("./tag-parser"); const layout = require("./layout"); -const fromTree = require("./text-components/from-tree"); const setTextStyles = require("./render/set-text-styles"); const measureText = require("./render/measure-text"); @@ -28,74 +26,22 @@ module.exports = function createTextShape(options) { fontWeight: "normal", lineHeight: 1.16, onRender: function onRender(context) { - if (this.tags === true) { - let currentLineOffset = 0; - let currentComponentOffset = 0; - - this._layout.forEach((line) => { - line.items.forEach((item) => { - setTextStyles(context, item.style); - context.fillText(item.text, currentComponentOffset, currentLineOffset - line.lineMinAscender); - currentComponentOffset += item.measurements.width; - }); - - currentLineOffset += line.lineHeight * this.lineHeight; - currentComponentOffset = 0; - }); - } else { - let currentLineOffset = 0; - - setTextStyles(context, getTextProperties(this)); - console.log(this._lines, this.renderWidth, this.renderHeight); - this._lines.forEach((line) => { - context.fillText(line.text, 0, currentLineOffset - line.lineMinAscender); - currentLineOffset += line.measurements.height * this.lineHeight; - }); - } + this._layout.items.forEach((item) => { + setTextStyles(context, item.style); + context.fillText(item.text, item.x, item.y); + }); }, onRecalculateSize: function onRecalculateSize() { - if (this.tags === true) { - this._layout = layout(this.text, { - classes: options.classes, - defaultStyle: getTextProperties(this) - }); - - let combinedLineHeight = this._layout.reduce((total, line, i) => { - if (i !== this._layout.length - 1) { - return total + (line.lineHeight * this.lineHeight); - } else { - /* The last line doesn't get a lineHeight multiplier... */ - return total + line.lineHeight; - } - }, 0); - - return { - height: Math.ceil(combinedLineHeight), - width: Math.ceil(Math.max.apply(null, this._layout.map(line => line.lineWidth))) - } - } else { - this._lines = this.text.split("\n").map((line) => { - return { - text: line, - measurements: measureText(line, getTextProperties(this)) - } - }); - - console.log("line", this._lines) - - function sum(values) { - return values.reduce((total, value) => { - return total + value; - }, 0); - } - - let multipliedLineHeights = this._lines.slice(0, -1).map(line => line.measurements.height * this.lineHeight); - let combinedLineHeight = sum(multipliedLineHeights) + this._lines[this._lines.length - 1].measurements.height; - - return { - height: Math.ceil(combinedLineHeight), - width: Math.ceil(Math.max.apply(null, this._lines.map(line => line.measurements.width))) - } + this._layout = layout(this.text, { + classes: options.classes, + defaultStyle: getTextProperties(this), + lineHeight: this.lineHeight, + tags: this.tags + }); + + return { + width: this._layout.width, + height: this._layout.height } } }, options)); diff --git a/src/layout.js b/src/layout.js deleted file mode 100644 index cb021e7..0000000 --- a/src/layout.js +++ /dev/null @@ -1,49 +0,0 @@ -'use strict'; - -const componentsToLines = require("./parser/to-lines"); -const findAlignment = require("./parser/find-alignment"); -const generateStyle = require("./parser/generate-style"); -const fromTree = require("./parser/from-tree"); -const tagParser = require("./parser/tag-parser"); -const measureText = require("./render/measure-text"); - -module.exports = function layoutFormattedText(text, options) { - if (options.tags === true) { - let tree = tagParser.parse(text); - - let lines = componentsToLines(fromTree(tree)).map((lineItems) => { - let measuredItems = lineItems.map((item) => { - let generatedStyle = generateStyle(item, { - classes: options.classes, - defaultStyle: options.defaultStyle - }); - - return Object.assign({ - measurements: measureText(item.text, generatedStyle), - style: generatedStyle - }, item); - }); - - let combinedWidth = measuredItems.reduce((total, item) => { - return total + item.measurements.width; - }, 0); - - let lineHeight = Math.max.apply(null, measuredItems.map(item => item.measurements.height)); - let lineMinAscender = Math.min.apply(null, measuredItems.map(item => item.measurements.ascender)); - let lineMaxDescender = Math.max.apply(null, measuredItems.map(item => item.measurements.descender)); - - return { - alignment: (lineItems.length > 0) ? findAlignment(lineItems[0]) : null, - items: measuredItems, - height: lineHeight, - width: combinedWidth, - minAscender: lineMinAscender, - maxDescender: lineMaxDescender - } - }); - - - } else { - - } -}; diff --git a/src/layout/adjust-line-heights.js b/src/layout/adjust-line-heights.js new file mode 100644 index 0000000..2466782 --- /dev/null +++ b/src/layout/adjust-line-heights.js @@ -0,0 +1,12 @@ +'use strict'; + +module.exports = function adjustLineHeights(lines, lineHeight) { + return lines.map((line, i) => { + if (i !== lines.length - 1) { + return line.height * lineHeight * 1.13; + } else { + /* The last line doesn't get a lineHeight multiplier... */ + return line.height * 1.13; + } + }); +}; diff --git a/src/layout/index.js b/src/layout/index.js new file mode 100644 index 0000000..d2f20b3 --- /dev/null +++ b/src/layout/index.js @@ -0,0 +1,102 @@ +'use strict'; + +const parser = require("../parser"); +const findAlignment = require("../parser/find-alignment"); +const generateStyle = require("../parser/generate-style"); +const measureText = require("../render/measure-text"); +const adjustLineHeights = require("./adjust-line-heights"); + +const sum = require("../util/sum"); +const min = require("../util/min"); +const max = require("../util/max"); + +module.exports = function layoutFormattedText(text, options) { + if (options.tags === true) { + let lines = parser(text).map((lineItems) => { + let measuredItems = lineItems.map((item) => { + let generatedStyle = generateStyle(item, { + classes: options.classes, + defaultStyle: options.defaultStyle + }); + + return Object.assign({ + measurements: measureText(item.text, generatedStyle), + style: generatedStyle + }, item); + }); + + 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)) + } + }); + + let adjustedLineHeights = adjustLineHeights(lines, options.lineHeight); + + let currentLineOffset = 0; + let currentComponentOffset = 0; + + let positionedItems = lines.reduce((items, line, i) => { + let newItems = items.concat(line.items.map((item) => { + let positionedItem = Object.assign({ + x: currentComponentOffset, + y: currentLineOffset - line.minAscender + }, item); + + currentComponentOffset += item.measurements.width; + return positionedItem; + })); + + currentLineOffset += adjustedLineHeights[i]; + currentComponentOffset = 0; + + return newItems; + }, []); + + return { + width: Math.ceil(max(lines.map(line => line.width))), + height: Math.ceil(sum(adjustedLineHeights)), + items: positionedItems + } + } else { + let lines = text.split("\n").map((line) => { + return { + text: line, + measurements: measureText(line, options.defaultStyle) + }; + }); + + let adjustedLineHeights = adjustLineHeights(lines.map((line) => { + return { + text: line.text, + height: line.measurements.height + } + }), options.lineHeight); + + let currentLineOffset = 0; + + let positionedItems = lines.map((line, i) => { + let newItem = { + text: line.text, + measurements: line.measurements, + style: options.defaultStyle, + x: 0, + y: currentLineOffset - line.measurements.ascender + } + + currentLineOffset += adjustedLineHeights[i]; + + return newItem; + }); + + return { + width: Math.ceil(max(lines.map(line => line.measurements.width))), + height: Math.ceil(sum(adjustedLineHeights)), + items: positionedItems + } + } +}; diff --git a/src/parser/index.js b/src/parser/index.js new file mode 100644 index 0000000..33a889a --- /dev/null +++ b/src/parser/index.js @@ -0,0 +1,9 @@ +'use strict'; + +const componentsToLines = require("./to-lines"); +const fromTree = require("./from-tree"); +const tagParser = require("./tag-parser"); + +module.exports = function parseTextToLines(text) { + return componentsToLines(fromTree(tagParser.parse(text))); +}; diff --git a/src/parser/to-lines.js b/src/parser/to-lines.js index ecdbc93..6af9a1c 100644 --- a/src/parser/to-lines.js +++ b/src/parser/to-lines.js @@ -1,6 +1,6 @@ 'use strict'; -const findAlignment = require("../tags/find-alignment"); +const findAlignment = require("./find-alignment"); module.exports = function componentsToLines(items) { let currentLineItems = []; diff --git a/src/render/measure-text.js b/src/render/measure-text.js index a524898..353364f 100644 --- a/src/render/measure-text.js +++ b/src/render/measure-text.js @@ -15,14 +15,6 @@ module.exports = function measureText(text, options) { let fontMeasurements = measureFont(options.fontFamily); return Object.assign(context.measureText(text), { - /* FIXME: The following is a dirty hack until the Canvas v5 API is - * widely supported in major browsers. The 1.13 multiplier comes - * from the fabric.js source code - don't ask me why it's 1.13. - * See also: - * https://kangax.github.io/jstests/canvas-v5/ - * https://lists.w3.org/Archives/Public/public-whatwg-archive/2012Mar/0269.htm - */ - //height: options.fontSize * 1.13, height: options.fontSize * (fontMeasurements.descender - fontMeasurements.topBounding), ascender: options.fontSize * fontMeasurements.ascender, descender: options.fontSize * fontMeasurements.descender, diff --git a/src/render/set-text-styles.js b/src/render/set-text-styles.js index 59e45e2..52ba590 100644 --- a/src/render/set-text-styles.js +++ b/src/render/set-text-styles.js @@ -14,7 +14,7 @@ module.exports = function setTextStyles(context, options) { options.fontVariant, options.fontWeight, `${options.fontSize}px`, // FIXME: Other units? - options.fontFamily + `'${options.fontFamily}'` // FIXME: Escaping of font family names containing a ' ]; context.font = fontSegments.filter(segment => (segment != null)).join(" "); diff --git a/src/util/max.js b/src/util/max.js new file mode 100644 index 0000000..b0e3790 --- /dev/null +++ b/src/util/max.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = function max(values) { + return Math.max(...values); +} diff --git a/src/util/min.js b/src/util/min.js new file mode 100644 index 0000000..005c428 --- /dev/null +++ b/src/util/min.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = function min(values) { + return Math.min(...values); +} diff --git a/src/util/sum.js b/src/util/sum.js new file mode 100644 index 0000000..e844264 --- /dev/null +++ b/src/util/sum.js @@ -0,0 +1,7 @@ +'use strict'; + +module.exports = function sum(values) { + return values.reduce((total, value) => { + return total + value; + }, 0); +};