Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixes the calculation of alignment for dynamic width shapes #692

Open
wants to merge 3 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 130 additions & 62 deletions app/pencil-core/behavior/commonFunctions.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,78 +184,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;
Expand Down
2 changes: 1 addition & 1 deletion app/pencil-core/common/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -2629,7 +2629,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);
2 changes: 1 addition & 1 deletion app/stencils/Common/Definition.xml
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@
<For ref="text">
<Fill>$textColor</Fill>
<Font>$textFont</Font>
<DomContent>F.buildTextWrapDomContent(F._target, $label.value, $fixedWidth.value ? $width.x : 2000, $textAlign)</DomContent>
<DomContent>F.buildTextWrapDomContent(F._target, $label.value, $fixedWidth.value ? $width.x : 2000, $textAlign, true)</DomContent>
</For>
<For ref="bgRect">
<Visibility>$fixedWidth</Visibility>
Expand Down
12 changes: 6 additions & 6 deletions app/views/editors/AlignEditor.xhtml
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@
}
</style>
<hbox class="Group Horz" anon-id="horizontalGroup" flex="1">
<button data="0" flex="1"><i>format_align_left</i></button>
<button data="1" flex="1"><i>format_align_center</i></button>
<button data="2" flex="1"><i>format_align_right</i></button>
<button data="0" title="Align text left"><i>format_align_left</i></button>
<button data="1" title="Align text center"><i>format_align_center</i></button>
<button data="2" title="Align text right"><i>format_align_right</i></button>
</hbox>
<hbox class="Group Vert" anon-id="verticalGroup" flex="1">
<button data="0" flex="1"><i>vertical_align_top</i></button>
<button data="1" flex="1"><i>vertical_align_center</i></button>
<button data="2" flex="1"><i>vertical_align_bottom</i></button>
<button data="0" title="Align text top"><i>vertical_align_top</i></button>
<button data="1" title="Align text middle"><i>vertical_align_center</i></button>
<button data="2" title="Align text bottom"><i>vertical_align_bottom</i></button>
</hbox>
</hbox>