From 91c4c84d8493e39649cfa818ad7028529fba109e Mon Sep 17 00:00:00 2001 From: Eliot Jones Date: Sat, 17 Jul 2021 16:07:10 -0400 Subject: [PATCH 1/2] fixes the calculation of alignment for dynamic width shapes --- app/pencil-core/behavior/commonFunctions.js | 192 +++++++++++++------- app/stencils/Common/Definition.xml | 2 +- 2 files changed, 131 insertions(+), 63 deletions(-) diff --git a/app/pencil-core/behavior/commonFunctions.js b/app/pencil-core/behavior/commonFunctions.js index ff563297..825f1c3a 100644 --- a/app/pencil-core/behavior/commonFunctions.js +++ b/app/pencil-core/behavior/commonFunctions.js @@ -183,78 +183,146 @@ F.parseTextArray = function (text) { return a; }; -F.buildTextWrapDomContent = function (textElement, text, width, align) { - var lines = text.split("\n"); - var tspans = []; - var lastHeight = 0; +/** + * + * @param {object} textElement - an SVG element. + * @param {string} text - the text to write, newlines '\n' will split into lines. + * @param {number} width - the width of the container used for automatic wrapping and alignment. + * @param {number} align - the text alignment, 0: left, 1: center, 2: right. + * @param {boolean} [isWidthDynamic] - whether the width is dynamically sized (ignore width). + */ +F.buildTextWrapDomContent = function (textElement, text, width, align, isWidthDynamic) { + const widths = [] + const strings = [] + const yOffsets = [] + + var currentYOffset = 0 var lastLineHeight = 0; - for (var j = 0; j < lines.length; j ++) { - var line = lines[j]; - if (line.length == 0) { - lastHeight += lastLineHeight; - continue; - } - var words = line.split(' '); - var i = 0; - var s = ""; - var lastBBoxWidth = 0; - while (i < words.length) { - if (s.length > 0) s += " "; - s += words[i]; - - Dom.empty(textElement); - textElement.appendChild(textElement.ownerDocument.createTextNode(s)); - var box = textElement.getBBox(); - - i ++; - - if (box.width < width) { - lastBBoxWidth = box.width; - continue; - } + var maxWidth = 0 - //now add the tspan + function measureText(str) { + Dom.empty(textElement); + textElement.appendChild(textElement.ownerDocument.createTextNode(str)); + var box = textElement.getBBox(); + return box + } - var index = s.lastIndexOf(" "); - var line = ""; + function measureAndSplitText() { - if (index > 0) { - line = s.substring(0, index); - i --; - } else { - line = s; - lastBBoxWidth = box.width; - } - s = ""; + const lines = text.split('\n') - tspans.push({ - _name: "tspan", - _uri: "http://www.w3.org/2000/svg", - _text: line, - x: (align ? align.h : 0) * (width - lastBBoxWidth) / 2, - y: lastHeight - }); + for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { + const line = lines[lineIndex]; + if (line.length === 0) { + currentYOffset += lastLineHeight + continue + } + + let boundingBox = measureText(line) + + // Happy-path, fits in 1 go + const fits = boundingBox.width <= width + if (fits) { + if (boundingBox.width > maxWidth) { + maxWidth = boundingBox.width + } + widths.push(boundingBox.width) + strings.push(line) + yOffsets.push(currentYOffset) + currentYOffset += boundingBox.height + continue; + } - lastHeight += box.height; - lastLineHeight = box.height; + const words = line.split(' ') + let wordIndex = 0 + let stringBuilder = '' + let lastLine = '' + let lastLineWidth = 0 + while (wordIndex < words.length) { + if (stringBuilder.length > 0) { + stringBuilder += ' ' + } + + stringBuilder += words[wordIndex] + wordIndex++; + + boundingBox = measureText(stringBuilder) + if (boundingBox.width <= width) { + // The generated string is still within the max width, then just store + // it and try adding the next word. + lastLineWidth = boundingBox.width + lastLineHeight = boundingBox.height + lastLine = stringBuilder + continue; + } + + // We exceeded the width in a single-word, append it anyway and continue + if (lastLine === '') { + widths.push(boundingBox.width) + strings.push(stringBuilder) + yOffsets.push(currentYOffset) + currentYOffset += boundingBox.height + lastLineWidth = 0 + lastLineHeight = 0 + stringBuilder = '' + continue + } + + // Adding the next word exceeded the allowable width, use the previous string. + widths.push(lastLineWidth) + strings.push(lastLine) + yOffsets.push(currentYOffset) + currentYOffset += lastLineHeight + + if (lastLineWidth > maxWidth) { + maxWidth = lastLineWidth + } + + // Set the next word as the word we just tried which caused the width to be exceeded. + wordIndex -= 1 + stringBuilder = '' + lastLine = '' + lastLineWidth = 0 + lastLineHeight = 0 + } + if (stringBuilder.length > 0) { + widths.push(lastLineWidth) + strings.push(stringBuilder) + yOffsets.push(currentYOffset) + + if (lastLineWidth > maxWidth) { + maxWidth = lastLineWidth + } + currentYOffset += lastLineHeight + lastLineWidth = 0 + } } - if (s.length > 0) { - Dom.empty(textElement); - textElement.appendChild(textElement.ownerDocument.createTextNode(s)); - var box = textElement.getBBox(); - - tspans.push({ - _name: "tspan", - _uri: "http://www.w3.org/2000/svg", - _text: s, - x: (align ? align.h : 0) * (width - box.width) / 2, - y: lastHeight - }); - lastHeight += box.height; - } + } + measureAndSplitText() + console.log('text is, dynamic is, max width is', text, isWidthDynamic, maxWidth) + + const tspans = []; + for (let finalLineIndex = 0; finalLineIndex < strings.length; finalLineIndex++) { + const str = strings[finalLineIndex]; + const w = widths[finalLineIndex]; + const y = yOffsets[finalLineIndex]; + + var xMultiplier = (align ? align.h : 0) + + var widthToUse = isWidthDynamic ? maxWidth : width + + var x = xMultiplier * (widthToUse - w) / 2; + tspans.push({ + _name: "tspan", + _uri: "http://www.w3.org/2000/svg", + _text: str, + x: x, + y: y + }); } + var frag = Dom.newDOMFragment(tspans, textElement.ownerDocument); return frag; diff --git a/app/stencils/Common/Definition.xml b/app/stencils/Common/Definition.xml index ae6cf7da..21b63c8d 100644 --- a/app/stencils/Common/Definition.xml +++ b/app/stencils/Common/Definition.xml @@ -195,7 +195,7 @@ $textColor $textFont - F.buildTextWrapDomContent(F._target, $label.value, $fixedWidth.value ? $width.x : 2000, $textAlign) + F.buildTextWrapDomContent(F._target, $label.value, $fixedWidth.value ? $width.x : 2000, $textAlign, true) $fixedWidth From 4cc5c74e80a9ad8080509023ae247782acbbe0ab Mon Sep 17 00:00:00 2001 From: Eliot Jones Date: Sat, 17 Jul 2021 16:19:05 -0400 Subject: [PATCH 2/2] fix 2 typos and add tooltips to alignment buttons --- app/pencil-core/common/controller.js | 4 ++-- app/pencil-core/common/util.js | 2 +- app/views/editors/AlignEditor.xhtml | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/pencil-core/common/controller.js b/app/pencil-core/common/controller.js index a04e2935..a91f1fdf 100644 --- a/app/pencil-core/common/controller.js +++ b/app/pencil-core/common/controller.js @@ -1163,7 +1163,7 @@ Controller.prototype.rasterizeCurrentPage = function (targetPage) { var filePath = res.filePath; this.applicationPane.rasterizer.rasterizePageToFile(page, filePath, function (p, error) { if (!error) { - NotificationPopup.show("Page exprted as '" + path.basename(filePath) + "'.", "View", function () { + NotificationPopup.show("Page exported as '" + path.basename(filePath) + "'.", "View", function () { shell.openPath(filePath); }); } @@ -1252,7 +1252,7 @@ Controller.prototype.rasterizeSelection = function (options) { var filePath = res.filePath; this.applicationPane.rasterizer.rasterizeSelectionToFile(target, filePath, function (p, error) { if (!error) { - NotificationPopup.show("Selection exprted as '" + path.basename(filePath) + "'.", "View", function () { + NotificationPopup.show("Selection exported as '" + path.basename(filePath) + "'.", "View", function () { shell.openPath(filePath); }); } diff --git a/app/pencil-core/common/util.js b/app/pencil-core/common/util.js index 36032406..c2903365 100644 --- a/app/pencil-core/common/util.js +++ b/app/pencil-core/common/util.js @@ -2628,7 +2628,7 @@ function sameRelax(a, b) { } process.on('uncaughtException', function (e) { - console.error("UNCAUGHT EXCPTION", e); + console.error("UNCAUGHT EXCEPTION", e); }); Util.importSandboxFunctions(geo_buildQuickSmoothCurve, geo_buildSmoothCurve, geo_getRotatedPoint, geo_pointAngle, geo_rotate, geo_translate, geo_vectorAngle, geo_vectorLength, geo_findIntersection); diff --git a/app/views/editors/AlignEditor.xhtml b/app/views/editors/AlignEditor.xhtml index eff60f61..e2a810d7 100644 --- a/app/views/editors/AlignEditor.xhtml +++ b/app/views/editors/AlignEditor.xhtml @@ -23,13 +23,13 @@ } - - - + + + - - - + + +