Skip to content

Commit

Permalink
fixes in emoji rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
lavrton committed Dec 23, 2024
1 parent f2ed14f commit adba007
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 18 deletions.
50 changes: 32 additions & 18 deletions src/shapes/Text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,15 @@ export function stringToArray(string: string): string[] {
return [...string].reduce((acc, char, index, array) => {
// Handle emoji with skin tone modifiers and ZWJ sequences
if (/\p{Emoji}/u.test(char)) {
if (acc.length > 0 && /\p{Emoji}/u.test(acc[acc.length - 1])) {
// Combine with previous emoji if it's part of a sequence
acc[acc.length - 1] += char;
// Check if next character is a modifier or ZWJ sequence
const nextChar = array[index + 1];
if (nextChar && /\p{Emoji_Modifier}|\u200D/u.test(nextChar)) {
// If we have a modifier, combine with current emoji
acc.push(char + nextChar);
// Skip the next character since we've used it
array[index + 1] = '';
} else {
// No modifier - treat as separate emoji
acc.push(char);
}
}
Expand All @@ -36,7 +41,8 @@ export function stringToArray(string: string): string[] {
acc[acc.length - 1] += char;
}
// Handle other characters
else {
else if (char) {
// Only push if not an empty string (skipped modifier)
acc.push(char);
}
return acc;
Expand Down Expand Up @@ -520,13 +526,16 @@ export class Text extends Shape<TextConfig> {
* that would fit in the specified width
*/
let low = 0,
high = line.length,
high = stringToArray(line).length, // Convert to array for proper emoji handling
match = '',
matchWidth = 0;
while (low < high) {
const mid = (low + high) >>> 1,
substr = line.slice(0, mid + 1),
// Convert array indices to string
lineArray = stringToArray(line),
substr = lineArray.slice(0, mid + 1).join(''),
substrWidth = this._getTextWidth(substr) + additionalWidth;

if (substrWidth <= maxWidth) {
low = mid + 1;
match = substr;
Expand All @@ -544,20 +553,24 @@ export class Text extends Shape<TextConfig> {
// a fitting substring was found
if (wrapAtWord) {
// try to find a space or dash where wrapping could be done
var wrapIndex;
const nextChar = line[match.length];
const lineArray = stringToArray(line);
const matchArray = stringToArray(match);
const nextChar = lineArray[matchArray.length];
const nextIsSpaceOrDash = nextChar === SPACE || nextChar === DASH;

let wrapIndex;
if (nextIsSpaceOrDash && matchWidth <= maxWidth) {
wrapIndex = match.length;
wrapIndex = matchArray.length;
} else {
wrapIndex =
Math.max(match.lastIndexOf(SPACE), match.lastIndexOf(DASH)) +
1;
// Find last space or dash in the array
const lastSpaceIndex = matchArray.lastIndexOf(SPACE);
const lastDashIndex = matchArray.lastIndexOf(DASH);
wrapIndex = Math.max(lastSpaceIndex, lastDashIndex) + 1;
}

if (wrapIndex > 0) {
// re-cut the substring found at the space/dash position
low = wrapIndex;
match = match.slice(0, low);
match = lineArray.slice(0, low).join('');
matchWidth = this._getTextWidth(match);
}
}
Expand All @@ -578,13 +591,14 @@ export class Text extends Shape<TextConfig> {
*/
break;
}
line = line.slice(low);
line = line.trimLeft();

// Convert remaining text using array operations
const lineArray = stringToArray(line);
line = lineArray.slice(low).join('').trimLeft();

if (line.length > 0) {
// Check if the remaining text would fit on one line
lineWidth = this._getTextWidth(line);
if (lineWidth <= maxWidth) {
// if it does, add the line and break out of the loop
this._addTextLine(line);
currentHeightPx += lineHeightPx;
textWidth = Math.max(textWidth, lineWidth);
Expand Down
29 changes: 29 additions & 0 deletions test/unit/Text-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,35 @@ describe('Text', function () {
context.fillStyle = 'darkgrey';
context.fillText('😬👧🏿', 10, 10 + 25);

compareLayerAndCanvas(layer, canvas, 254, 100);
});

it('check emoji rendering', function () {
var stage = addStage();
var layer = new Konva.Layer();

var text = new Konva.Text({
text: '😁😁😁',
x: 10,
y: 10,
fontSize: 20,
draggable: true,
width: 65,
fill: 'black',
scaleY: 0.9999999999999973,
});

layer.add(text);
stage.add(layer);

var canvas = createCanvas();
var context = canvas.getContext('2d');
context.textBaseline = 'middle';
context.font = 'normal normal 20px Arial';
context.fillStyle = 'black';
context.fillText('😁😁', 10, 10 + 10);
context.fillText('😁', 10, 10 + 30);

compareLayerAndCanvas(layer, canvas, 254);
});

Expand Down

0 comments on commit adba007

Please sign in to comment.