From f0449dbe4c0485e9f0a2bf65af823733608e2597 Mon Sep 17 00:00:00 2001 From: shaehn Date: Tue, 5 Nov 2019 10:37:18 -0500 Subject: [PATCH] 'Wrap' value addition for issue #52 Allow user to specify 'wrap' option as true or false values, with true representing 'auto' matic wrapping and false representing 'ellipsis' line truncation marking. --- docs/text.js.html | 55 +++++++++++++++++++++++++++++++++++++++++------ lib/text.js | 15 ++++++++++--- 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/docs/text.js.html b/docs/text.js.html index 9e4c714..33bcc3e 100644 --- a/docs/text.js.html +++ b/docs/text.js.html @@ -87,7 +87,9 @@

text.js

* @param {number} [options.textBox.minHeight=0] - Text Box minimum height * @param {number|number[]} [options.textBox.padding=0] - Text Box padding, [top, right, bottom, left] * @param {number} [options.textBox.lineHeight=0] - Text Box line height - * @param {string} [options.textBox.wrap='auto'] - Text wrapping mechanism, 'auto', 'clip', 'trim'. + * @param {string|Boolean} [options.textBox.wrap='auto'] - Text wrapping mechanism, may be true, false, + * 'auto', 'clip', 'trim', 'ellipsis'. All the option values that are not equivalent to 'auto' dictate + * how the text which does not fit on a line is to be truncated. True is equivalent to 'auto'. False is equivalent to 'ellipsis'. * @param {string} [options.textBox.textAlign='left top'] - Alignment inside text box, specified as 'horizontal vertical', * where horizontal is one of: 'left', 'center', 'right', 'justify' and veritical is one of: 'top', 'center', 'bottom'. * @param {Object} [options.textBox.style] - Text Box styles @@ -140,6 +142,13 @@

text.js

wrap: (options.textBox.wrap !== undefined) ? options.textBox.wrap : 'auto' }; + // allow user to treat wrap as boolean + if (textBox.wrap === true) { + textBox.wrap = 'auto'; + } else if (textBox.wrap === false) { + textBox.wrap = 'ellipsis' + } + // Allows user to enter a single number which will be used for all text box sides, // or an array of values [top, right, bottom, left] with any combination of missing sides. // Default value for a missing side is the value of the text box's opposite side (see below). @@ -480,6 +489,39 @@

text.js

return new Word(nextWord, pathOptions); } +function elideNonFittingText(textBox, line, word, pathOptions) { + if (textBox.wrap === 'clip') { + line.addWord(word); + } else if (textBox.wrap === 'ellipsis') { + // This is more complicated than the other no-wrap options. + // It makes an initial attempt to take the word that was + // too big and make it shrink in size until it and the + // ellipsis character fit. If that doesn't work, one more + // stab is taken by trying to shrink the previous word + // that fit on the line. + const ellipsis = '…'; + let usingPreviousWord = false; + let tooBig = new Word(word.value.slice(0,-2)+ellipsis, pathOptions); + + while ( !line.canFit(tooBig)) { + + if (tooBig.value.length > 1) { + tooBig = new Word(tooBig.value.slice(0,-2)+ellipsis, pathOptions); + + // Try last word that fit in box? + } else if (!usingPreviousWord) { + tooBig = new Word(line.words.pop().value.slice(0,-2) + ellipsis, pathOptions); + usingPreviousWord = true; + + } else { + break; // give up, only get 2 shots at this. + } + } + + line.addWord(tooBig); + } +} + function getToWriteTextObjects(textObject = {}, pathOptions, textBox = {}) { const toWriteTextObjects = []; let text = ((textObject.prependValue) ? textObject.prependValue : '') + @@ -529,9 +571,8 @@

text.js

// now deal with text line wrap (what happens to text that doesn't fit in line) if (textBox.wrap !== 'auto') { flushLine = true; - if (textBox.wrap === 'clip') { - newLine.addWord(word); - } + elideNonFittingText(textBox, newLine, word, pathOptions); + } else { // this is the auto wrap section newLine = new Line(lineMaxWidth, textBox.lineHeight, size, pathOptions); @@ -548,13 +589,13 @@

text.js

if (flushLine) { while (bk) { - bk = breaker.nextBreak(); - if (bk && bk.required) { + if (bk.required) { flushLine = false; newLine = new Line(lineMaxWidth, textBox.lineHeight, size, pathOptions); word = null; break; } + bk = breaker.nextBreak(); } } else { /** @@ -629,7 +670,7 @@

text.js


diff --git a/lib/text.js b/lib/text.js index e7816ef..6498cef 100644 --- a/lib/text.js +++ b/lib/text.js @@ -48,7 +48,9 @@ const xObjectForm = require('./xObjectForm'); * @param {number} [options.textBox.minHeight=0] - Text Box minimum height * @param {number|number[]} [options.textBox.padding=0] - Text Box padding, [top, right, bottom, left] * @param {number} [options.textBox.lineHeight=0] - Text Box line height - * @param {string} [options.textBox.wrap='auto'] - Text wrapping mechanism, 'auto', 'clip', 'trim', 'ellipsis'. + * @param {string|Boolean} [options.textBox.wrap='auto'] - Text wrapping mechanism, may be true, false, + * 'auto', 'clip', 'trim', 'ellipsis'. All the option values that are not equivalent to 'auto' dictate + * how the text which does not fit on a line is to be truncated. True is equivalent to 'auto'. False is equivalent to 'ellipsis'. * @param {string} [options.textBox.textAlign='left top'] - Alignment inside text box, specified as 'horizontal vertical', * where horizontal is one of: 'left', 'center', 'right', 'justify' and veritical is one of: 'top', 'center', 'bottom'. * @param {Object} [options.textBox.style] - Text Box styles @@ -101,6 +103,13 @@ exports.text = function text(text = '', x, y, options = {}) { wrap: (options.textBox.wrap !== undefined) ? options.textBox.wrap : 'auto' }; + // allow user to treat wrap as boolean + if (textBox.wrap === true) { + textBox.wrap = 'auto'; + } else if (textBox.wrap === false) { + textBox.wrap = 'ellipsis' + } + // Allows user to enter a single number which will be used for all text box sides, // or an array of values [top, right, bottom, left] with any combination of missing sides. // Default value for a missing side is the value of the text box's opposite side (see below). @@ -449,7 +458,7 @@ function elideNonFittingText(textBox, line, word, pathOptions) { // It makes an initial attempt to take the word that was // too big and make it shrink in size until it and the // ellipsis character fit. If that doesn't work, one more - // stab is taken by trying to shrink the previous word + // attempt is taken by trying to shrink the previous word // that fit on the line. const ellipsis = '…'; let usingPreviousWord = false; @@ -462,7 +471,7 @@ function elideNonFittingText(textBox, line, word, pathOptions) { // Try last word that fit in box? } else if (!usingPreviousWord) { - tooBig = new Word(line.words.pop().value.slice(0,-2) + ellipsis, pathOptions); + tooBig = new Word(line.words.pop().value.slice(0,-1) + ellipsis, pathOptions); usingPreviousWord = true; } else {