fix: support breaking words containing hyphen - (#6014)

* fix: support breaking words containing hyphen -

* fix

* add spec

* fix

* fix

* fix

* fix and add spec

* improve code and add more tests
This commit is contained in:
Aakansha Doshi 2023-04-20 11:10:46 +05:30 committed by GitHub
parent 404a79e241
commit 5ddb28d378
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 89 additions and 9 deletions

View File

@ -9,6 +9,7 @@ import {
detectLineHeight, detectLineHeight,
getLineHeightInPx, getLineHeightInPx,
getDefaultLineHeight, getDefaultLineHeight,
parseTokens,
} from "./textElement"; } from "./textElement";
import { FontString } from "./types"; import { FontString } from "./types";
@ -183,6 +184,56 @@ now`,
expect(wrapText(text, font, -1)).toEqual(text); expect(wrapText(text, font, -1)).toEqual(text);
expect(wrapText(text, font, Infinity)).toEqual(text); expect(wrapText(text, font, Infinity)).toEqual(text);
}); });
it("should wrap the text correctly when text contains hyphen", () => {
let text =
"Wikipedia is hosted by Wikimedia- Foundation, a non-profit organization that also hosts a range-of other projects";
const res = wrapText(text, font, 110);
expect(res).toBe(
`Wikipedia \nis hosted \nby \nWikimedia-\nFoundation,\na non-\nprofit \norganizati\non that \nalso hosts\na range-of\nother \nprojects`,
);
text = "Hello thereusing-now";
expect(wrapText(text, font, 100)).toEqual("Hello \nthereusin\ng-now");
});
});
describe("Test parseTokens", () => {
it("should split into tokens correctly", () => {
let text = "Excalidraw is a virtual collaborative whiteboard";
expect(parseTokens(text)).toEqual([
"Excalidraw",
"is",
"a",
"virtual",
"collaborative",
"whiteboard",
]);
text =
"Wikipedia is hosted by Wikimedia- Foundation, a non-profit organization that also hosts a range-of other projects";
expect(parseTokens(text)).toEqual([
"Wikipedia",
"is",
"hosted",
"by",
"Wikimedia-",
"",
"Foundation,",
"a",
"non-",
"profit",
"organization",
"that",
"also",
"hosts",
"a",
"range-",
"of",
"other",
"projects",
]);
});
}); });
describe("Test measureText", () => { describe("Test measureText", () => {

View File

@ -419,6 +419,24 @@ export const getTextHeight = (
return getLineHeightInPx(fontSize, lineHeight) * lineCount; return getLineHeightInPx(fontSize, lineHeight) * lineCount;
}; };
export const parseTokens = (text: string) => {
// Splitting words containing "-" as those are treated as separate words
// by css wrapping algorithm eg non-profit => non-, profit
const words = text.split("-");
if (words.length > 1) {
// non-proft org => ['non-', 'profit org']
words.forEach((word, index) => {
if (index !== words.length - 1) {
words[index] = word += "-";
}
});
}
// Joining the words with space and splitting them again with space to get the
// final list of tokens
// ['non-', 'profit org'] =>,'non- proft org' => ['non-','profit','org']
return words.join(" ").split(" ");
};
export const wrapText = (text: string, font: FontString, maxWidth: number) => { export const wrapText = (text: string, font: FontString, maxWidth: number) => {
// if maxWidth is not finite or NaN which can happen in case of bugs in // if maxWidth is not finite or NaN which can happen in case of bugs in
// computation, we need to make sure we don't continue as we'll end up // computation, we need to make sure we don't continue as we'll end up
@ -444,17 +462,16 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => {
currentLine = ""; currentLine = "";
currentLineWidthTillNow = 0; currentLineWidthTillNow = 0;
}; };
originalLines.forEach((originalLine) => { originalLines.forEach((originalLine) => {
const currentLineWidth = getTextWidth(originalLine, font); const currentLineWidth = getTextWidth(originalLine, font);
//Push the line if its <= maxWidth // Push the line if its <= maxWidth
if (currentLineWidth <= maxWidth) { if (currentLineWidth <= maxWidth) {
lines.push(originalLine); lines.push(originalLine);
return; // continue return; // continue
} }
const words = originalLine.split(" ");
const words = parseTokens(originalLine);
resetParams(); resetParams();
let index = 0; let index = 0;
@ -472,6 +489,7 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => {
else if (currentWordWidth > maxWidth) { else if (currentWordWidth > maxWidth) {
// push current line since the current word exceeds the max width // push current line since the current word exceeds the max width
// so will be appended in next line // so will be appended in next line
push(currentLine); push(currentLine);
resetParams(); resetParams();
@ -492,15 +510,15 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => {
currentLine += currentChar; currentLine += currentChar;
} }
} }
// push current line if appending space exceeds max width // push current line if appending space exceeds max width
if (currentLineWidthTillNow + spaceWidth >= maxWidth) { if (currentLineWidthTillNow + spaceWidth >= maxWidth) {
push(currentLine); push(currentLine);
resetParams(); resetParams();
} else {
// space needs to be appended before next word // space needs to be appended before next word
// as currentLine contains chars which couldn't be appended // as currentLine contains chars which couldn't be appended
// to previous line // to previous line unless the line ends with hyphen to sync
// with css word-wrap
} else if (!currentLine.endsWith("-")) {
currentLine += " "; currentLine += " ";
currentLineWidthTillNow += spaceWidth; currentLineWidthTillNow += spaceWidth;
} }
@ -518,12 +536,23 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => {
break; break;
} }
index++; index++;
currentLine += `${word} `;
// if word ends with "-" then we don't need to add space
// to sync with css word-wrap
const shouldAppendSpace = !word.endsWith("-");
currentLine += word;
if (shouldAppendSpace) {
currentLine += " ";
}
// Push the word if appending space exceeds max width // Push the word if appending space exceeds max width
if (currentLineWidthTillNow + spaceWidth >= maxWidth) { if (currentLineWidthTillNow + spaceWidth >= maxWidth) {
const word = currentLine.slice(0, -1); if (shouldAppendSpace) {
push(word); lines.push(currentLine.slice(0, -1));
} else {
lines.push(currentLine);
}
resetParams(); resetParams();
break; break;
} }