Skip to content
Draft
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## [next]

- feat: use Intl.Segmenter as default wordSplit method [#10606](https://github.com/fabricjs/fabric.js/pull/10606)
- chore(): run npm audit fix to fix a vulnerability report [#10599](https://github.com/fabricjs/fabric.js/pull/10599)
- refactor(tests): move text path tests from qunit to playwright [#10590](https://github.com/fabricjs/fabric.js/pull/10590)
- chore(): Deprecate line [#10598](https://github.com/fabricjs/fabric.js/pull/10598)
Expand Down
15 changes: 9 additions & 6 deletions src/shapes/Textbox.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ describe('Textbox', () => {

expect(wordsData[0], 'All words have the same length line 0').toEqual([
{ word: ['w', 'o', 'r', 'd'], width: largestWordWidth },
{ word: [' '], width: 10 },
{ word: ['w', 'o', 'r', 'd'], width: largestWordWidth },
]);
expect(wordsData[1], 'All words have the same length line1').toEqual([
Expand Down Expand Up @@ -409,7 +410,7 @@ describe('Textbox', () => {
expect(Math.round(wordsData[0][0].width), 'unstyle word is 82 wide').toBe(
82,
);
expect(Math.round(wordsData[0][1].width), 'unstyle word is 206 wide').toBe(
expect(Math.round(wordsData[0][2].width), 'unstyle word is 206 wide').toBe(
206,
);
expect(wordsData[2], 'All words have the same length line1').toEqual([
Expand All @@ -425,11 +426,13 @@ describe('Textbox', () => {

expect(textbox.textLines[0], '0 line match expectations').toBe('xa');
expect(textbox.textLines[1], '1 line match expectations').toBe('xb');
expect(textbox.textLines[2], '2 line match expectations').toBe('xc');
expect(textbox.textLines[3], '3 line match expectations').toBe('xd');
expect(textbox.textLines[4], '4 line match expectations').toBe('xe');
expect(textbox.textLines[5], '5 line match expectations').toBe('ya');
expect(textbox.textLines[6], '6 line match expectations').toBe('yb');
expect(textbox.textLines[2], '1 line match expectations').toBe('\t');
expect(textbox.textLines[3], '2 line match expectations').toBe('xc');
expect(textbox.textLines[4], '2 line match expectations').toBe('\r');
expect(textbox.textLines[5], '3 line match expectations').toBe('xd');
expect(textbox.textLines[6], '4 line match expectations').toBe('xe');
expect(textbox.textLines[7], '5 line match expectations').toBe('ya');
expect(textbox.textLines[8], '6 line match expectations').toBe('yb');
});

it('wrapping with splitByGrapheme', () => {
Expand Down
39 changes: 19 additions & 20 deletions src/shapes/Textbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const textboxDefaultValues: Partial<TClassProperties<Textbox>> = {
dynamicMinWidth: 2,
lockScalingFlip: true,
noScaleCache: false,
_wordJoiners: /[ \t\r]/,
_wordJoiners: /( +|[\t\r])/,
splitByGrapheme: false,
};

Expand Down Expand Up @@ -232,7 +232,7 @@ export class Textbox<
for (const p2 in obj[p1]) {
const p2Number = parseInt(p2, 10);
if (p2Number >= offset && (!shouldLimit || p2Number < nextOffset!)) {
// eslint-disable-next-line no-unused-vars

for (const p3 in obj[p1][p2]) {
return false;
}
Expand Down Expand Up @@ -341,8 +341,7 @@ export class Textbox<
*
*/
getGraphemeDataForRender(lines: string[]): GraphemeData {
const splitByGrapheme = this.splitByGrapheme,
infix = splitByGrapheme ? '' : ' ';
const splitByGrapheme = this.splitByGrapheme

let largestWordWidth = 0;

Expand All @@ -363,7 +362,7 @@ export class Textbox<
: this.graphemeSplit(word);
const width = this._measureWord(graphemeArray, lineIndex, offset);
largestWordWidth = Math.max(width, largestWordWidth);
offset += graphemeArray.length + infix.length;
offset += graphemeArray.length;
return { word: graphemeArray, width };
});
});
Expand Down Expand Up @@ -411,7 +410,7 @@ export class Textbox<
* @returns {string[]} array of words
*/
wordSplit(value: string): string[] {
return value.split(this._wordJoiners);
return value.split(this._wordJoiners).filter(Boolean);
}

/**
Expand All @@ -433,14 +432,12 @@ export class Textbox<
): string[][] {
const additionalSpace = this._getWidthOfCharSpacing(),
splitByGrapheme = this.splitByGrapheme,
graphemeLines = [],
infix = splitByGrapheme ? '' : ' ';
graphemeLines = []

let lineWidth = 0,
line: string[] = [],
// spaces in different languages?
offset = 0,
infixWidth = 0,
lineJustStarted = true;

desiredWidth -= reservedSpace;
Expand All @@ -458,25 +455,27 @@ export class Textbox<
const { word, width: wordWidth } = data[i];
offset += word.length;

lineWidth += infixWidth + wordWidth - additionalSpace;
if (lineWidth > maxWidth && !lineJustStarted) {
lineWidth += wordWidth;

if (!lineJustStarted && lineWidth - additionalSpace > maxWidth) {
// ignore only one space at the line end
if (!splitByGrapheme && line[line.length -1] === ' ')
line.pop();
graphemeLines.push(line);
line = [];
lineWidth = wordWidth;
lineJustStarted = true;
} else {
lineWidth += additionalSpace;
}

if (!lineJustStarted && !splitByGrapheme) {
line.push(infix);
if (lineJustStarted) {
// ignore only one space at the line start
if (!splitByGrapheme && word.length > 0 && word[0] === ' ') {
word.shift();
lineWidth = lineWidth - this._measureWord([' '], lineIndex, offset - word.length) - additionalSpace;
}
}
line = line.concat(word);

infixWidth = splitByGrapheme
? 0
: this._measureWord([infix], lineIndex, offset);
offset++;
line = line.concat(word);
lineJustStarted = false;
}

Expand Down