Skip to content

fix(TextMetrics): rtl direction + start/end textAlign #2510

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

Open
wants to merge 6 commits into
base: master
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ project adheres to [Semantic Versioning](http://semver.org/).
* Fix fetching prebuilds during installation on certain newer versions of Node (#2497)
* Fixed issue with fillText that was breaking subsequent fillText calls (#2171)
* Fix svg rendering when the image is resized (#2498)
* Fix measureText with direction rtl textAlign start/end

3.1.0
==================
Expand Down
29 changes: 18 additions & 11 deletions src/CanvasRenderingContext2d.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2557,6 +2557,20 @@ inline double getBaselineAdjustment(PangoLayout* layout, short baseline) {
}
}

text_align_t
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I expose a strict text align enum?

enum text_align_strict_t : int8_t {
  STRICT_TEXT_ALIGNMENT_LEFT = TEXT_ALIGNMENT_LEFT,
  STRICT_TEXT_ALIGNMENT_CENTER = TEXT_ALIGNMENT_CENTER,
  STRICT_TEXT_ALIGNMENT_RIGHT = TEXT_ALIGNMENT_RIGHT
};

Context2d::resolveTextAlignment() {
text_align_t alignment = state->textAlignment;

// Convert start/end to left/right based on direction
if (alignment == TEXT_ALIGNMENT_START) {
return (state->direction == "rtl") ? TEXT_ALIGNMENT_RIGHT : TEXT_ALIGNMENT_LEFT;
} else if (alignment == TEXT_ALIGNMENT_END) {
return (state->direction == "rtl") ? TEXT_ALIGNMENT_LEFT : TEXT_ALIGNMENT_RIGHT;
}

return alignment;
}

/*
* Set text path for the string in the layout at (x, y).
* This function is called by paintText and won't behave correctly
Expand All @@ -2567,14 +2581,7 @@ inline double getBaselineAdjustment(PangoLayout* layout, short baseline) {
void
Context2d::setTextPath(double x, double y) {
PangoRectangle logical_rect;
text_align_t alignment = state->textAlignment;

// Convert start/end to left/right based on direction
if (alignment == TEXT_ALIGNMENT_START) {
alignment = (state->direction == "rtl") ? TEXT_ALIGNMENT_RIGHT : TEXT_ALIGNMENT_LEFT;
} else if (alignment == TEXT_ALIGNMENT_END) {
alignment = (state->direction == "rtl") ? TEXT_ALIGNMENT_LEFT : TEXT_ALIGNMENT_RIGHT;
}
text_align_t alignment = resolveTextAlignment();

switch (alignment) {
case TEXT_ALIGNMENT_CENTER:
Expand Down Expand Up @@ -2816,16 +2823,16 @@ Context2d::MeasureText(const Napi::CallbackInfo& info) {

metrics = PANGO_LAYOUT_GET_METRICS(layout);

text_align_t alignment = resolveTextAlignment();

double x_offset;
switch (state->textAlignment) {
switch (alignment) {
case TEXT_ALIGNMENT_CENTER:
x_offset = logical_rect.width / 2.;
break;
case TEXT_ALIGNMENT_END:
case TEXT_ALIGNMENT_RIGHT:
x_offset = logical_rect.width;
break;
case TEXT_ALIGNMENT_START:
case TEXT_ALIGNMENT_LEFT:
default:
x_offset = 0.0;
Expand Down
1 change: 1 addition & 0 deletions src/CanvasRenderingContext2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ class Context2d : public Napi::ObjectWrap<Context2d> {
void _setStrokePattern(Napi::Value arg);
void checkFonts();
void paintText(const Napi::CallbackInfo&, bool);
text_align_t resolveTextAlignment();
Napi::Reference<Napi::Value> _fillStyle;
Napi::Reference<Napi::Value> _strokeStyle;
Canvas *_canvas;
Expand Down
33 changes: 33 additions & 0 deletions test/canvas.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,39 @@ describe('Canvas', function () {
assertApprox(rm.actualBoundingBoxLeft, 19, 6)
assertApprox(rm.actualBoundingBoxRight, 1, 6)
})

it('resolves text alignment wrt Context2d#direction #2508', function () {
const canvas = createCanvas(0, 0)
const ctx = canvas.getContext('2d')

ctx.textAlign = "left";
const leftMetrics = ctx.measureText('hello');
assert(leftMetrics.actualBoundingBoxLeft < leftMetrics.actualBoundingBoxRight, "leftMetrics.actualBoundingBoxLeft < leftMetrics.actualBoundingBoxRight");

ctx.textAlign = "right";
const rightMetrics = ctx.measureText('hello');
assert(rightMetrics.actualBoundingBoxLeft > rightMetrics.actualBoundingBoxRight, "metrics.actualBoundingBoxLeft > metrics.actualBoundingBoxRight");

ctx.textAlign = "start";

ctx.direction = "ltr";
const ltrStartMetrics = ctx.measureText('hello');
assert.deepStrictEqual(ltrStartMetrics, leftMetrics, "ltr start metrics should equal left metrics");

ctx.direction = "rtl";
const rtlStartMetrics = ctx.measureText('hello');
assert.deepStrictEqual(rtlStartMetrics, rightMetrics, "rtl start metrics should equal right metrics");

ctx.textAlign = "end";

ctx.direction = "ltr";
const ltrEndMetrics = ctx.measureText('hello');
assert.deepStrictEqual(ltrEndMetrics, rightMetrics, "ltr end metrics should equal right metrics");

ctx.direction = "rtl";
const rtlEndMetrics = ctx.measureText('hello');
assert.deepStrictEqual(rtlEndMetrics, leftMetrics, "rtl end metrics should equal left metrics");
})
})

it('Context2d#fillText()', function () {
Expand Down
Loading