Skip to content

Commit 772c464

Browse files
authored
fix(TextMetrics): rtl direction + start/end textAlign (#2510)
* fix(TextMetrics): rtl direction + start/end textAlign * changelog * update tests * expose resolveTextAlignment * jest test
1 parent 1234a86 commit 772c464

File tree

4 files changed

+53
-11
lines changed

4 files changed

+53
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ project adheres to [Semantic Versioning](http://semver.org/).
1515
* Fix fetching prebuilds during installation on certain newer versions of Node (#2497)
1616
* Fixed issue with fillText that was breaking subsequent fillText calls (#2171)
1717
* Fix svg rendering when the image is resized (#2498)
18+
* Fix measureText with direction rtl textAlign start/end
1819

1920
3.1.0
2021
==================

src/CanvasRenderingContext2d.cc

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2557,6 +2557,20 @@ inline double getBaselineAdjustment(PangoLayout* layout, short baseline) {
25572557
}
25582558
}
25592559

2560+
text_align_t
2561+
Context2d::resolveTextAlignment() {
2562+
text_align_t alignment = state->textAlignment;
2563+
2564+
// Convert start/end to left/right based on direction
2565+
if (alignment == TEXT_ALIGNMENT_START) {
2566+
return (state->direction == "rtl") ? TEXT_ALIGNMENT_RIGHT : TEXT_ALIGNMENT_LEFT;
2567+
} else if (alignment == TEXT_ALIGNMENT_END) {
2568+
return (state->direction == "rtl") ? TEXT_ALIGNMENT_LEFT : TEXT_ALIGNMENT_RIGHT;
2569+
}
2570+
2571+
return alignment;
2572+
}
2573+
25602574
/*
25612575
* Set text path for the string in the layout at (x, y).
25622576
* This function is called by paintText and won't behave correctly
@@ -2567,14 +2581,7 @@ inline double getBaselineAdjustment(PangoLayout* layout, short baseline) {
25672581
void
25682582
Context2d::setTextPath(double x, double y) {
25692583
PangoRectangle logical_rect;
2570-
text_align_t alignment = state->textAlignment;
2571-
2572-
// Convert start/end to left/right based on direction
2573-
if (alignment == TEXT_ALIGNMENT_START) {
2574-
alignment = (state->direction == "rtl") ? TEXT_ALIGNMENT_RIGHT : TEXT_ALIGNMENT_LEFT;
2575-
} else if (alignment == TEXT_ALIGNMENT_END) {
2576-
alignment = (state->direction == "rtl") ? TEXT_ALIGNMENT_LEFT : TEXT_ALIGNMENT_RIGHT;
2577-
}
2584+
text_align_t alignment = resolveTextAlignment();
25782585

25792586
switch (alignment) {
25802587
case TEXT_ALIGNMENT_CENTER:
@@ -2816,16 +2823,16 @@ Context2d::MeasureText(const Napi::CallbackInfo& info) {
28162823

28172824
metrics = PANGO_LAYOUT_GET_METRICS(layout);
28182825

2826+
text_align_t alignment = resolveTextAlignment();
2827+
28192828
double x_offset;
2820-
switch (state->textAlignment) {
2829+
switch (alignment) {
28212830
case TEXT_ALIGNMENT_CENTER:
28222831
x_offset = logical_rect.width / 2.;
28232832
break;
2824-
case TEXT_ALIGNMENT_END:
28252833
case TEXT_ALIGNMENT_RIGHT:
28262834
x_offset = logical_rect.width;
28272835
break;
2828-
case TEXT_ALIGNMENT_START:
28292836
case TEXT_ALIGNMENT_LEFT:
28302837
default:
28312838
x_offset = 0.0;

src/CanvasRenderingContext2d.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ class Context2d : public Napi::ObjectWrap<Context2d> {
222222
void _setStrokePattern(Napi::Value arg);
223223
void checkFonts();
224224
void paintText(const Napi::CallbackInfo&, bool);
225+
text_align_t resolveTextAlignment();
225226
Napi::Reference<Napi::Value> _fillStyle;
226227
Napi::Reference<Napi::Value> _strokeStyle;
227228
Canvas *_canvas;

test/canvas.test.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,39 @@ describe('Canvas', function () {
10261026
assertApprox(rm.actualBoundingBoxLeft, 19, 6)
10271027
assertApprox(rm.actualBoundingBoxRight, 1, 6)
10281028
})
1029+
1030+
it('resolves text alignment wrt Context2d#direction #2508', function () {
1031+
const canvas = createCanvas(0, 0)
1032+
const ctx = canvas.getContext('2d')
1033+
1034+
ctx.textAlign = "left";
1035+
const leftMetrics = ctx.measureText('hello');
1036+
assert(leftMetrics.actualBoundingBoxLeft < leftMetrics.actualBoundingBoxRight, "leftMetrics.actualBoundingBoxLeft < leftMetrics.actualBoundingBoxRight");
1037+
1038+
ctx.textAlign = "right";
1039+
const rightMetrics = ctx.measureText('hello');
1040+
assert(rightMetrics.actualBoundingBoxLeft > rightMetrics.actualBoundingBoxRight, "metrics.actualBoundingBoxLeft > metrics.actualBoundingBoxRight");
1041+
1042+
ctx.textAlign = "start";
1043+
1044+
ctx.direction = "ltr";
1045+
const ltrStartMetrics = ctx.measureText('hello');
1046+
assert.deepStrictEqual(ltrStartMetrics, leftMetrics, "ltr start metrics should equal left metrics");
1047+
1048+
ctx.direction = "rtl";
1049+
const rtlStartMetrics = ctx.measureText('hello');
1050+
assert.deepStrictEqual(rtlStartMetrics, rightMetrics, "rtl start metrics should equal right metrics");
1051+
1052+
ctx.textAlign = "end";
1053+
1054+
ctx.direction = "ltr";
1055+
const ltrEndMetrics = ctx.measureText('hello');
1056+
assert.deepStrictEqual(ltrEndMetrics, rightMetrics, "ltr end metrics should equal right metrics");
1057+
1058+
ctx.direction = "rtl";
1059+
const rtlEndMetrics = ctx.measureText('hello');
1060+
assert.deepStrictEqual(rtlEndMetrics, leftMetrics, "rtl end metrics should equal left metrics");
1061+
})
10291062
})
10301063

10311064
it('Context2d#fillText()', function () {

0 commit comments

Comments
 (0)