Skip to content

Commit 52330b8

Browse files
committed
support ctx.direction and textAlign start/end
1 parent 0b2edc1 commit 52330b8

6 files changed

+47
-10
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ project adheres to [Semantic Versioning](http://semver.org/).
1313

1414
### Added
1515
* Support for accessibility and links in PDFs
16+
* `ctx.direction` is implemented: `'rtl'` or `'ltr'` set the base direction of text
17+
* `ctx.textAlign` `'start'` and `'end'` are now `'right'` and `'left'` when `ctx.direction === 'rtl'`
1618

1719
### Fixed
1820
* Fix a crash in `getImageData` when the rectangle is entirely outside the canvas. ([#2024](https://github.com/Automattic/node-canvas/issues/2024))

index.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ export class CanvasRenderingContext2D {
290290
textBaseline: CanvasTextBaseline;
291291
textAlign: CanvasTextAlign;
292292
canvas: Canvas;
293+
direction: 'ltr' | 'rtl';
293294
}
294295

295296
export class CanvasGradient {

src/Canvas.h

-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ enum text_align_t : int8_t {
3737
TEXT_ALIGNMENT_LEFT = -1,
3838
TEXT_ALIGNMENT_CENTER = 0,
3939
TEXT_ALIGNMENT_RIGHT = 1,
40-
// Currently same as LEFT and RIGHT without RTL support:
4140
TEXT_ALIGNMENT_START = -2,
4241
TEXT_ALIGNMENT_END = 2
4342
};

src/CanvasRenderingContext2d.cc

+40-6
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,8 @@ Context2d::Initialize(Napi::Env& env, Napi::Object& exports) {
161161
InstanceAccessor<&Context2d::GetStrokeStyle, &Context2d::SetStrokeStyle>("strokeStyle", napi_default_jsproperty),
162162
InstanceAccessor<&Context2d::GetFont, &Context2d::SetFont>("font", napi_default_jsproperty),
163163
InstanceAccessor<&Context2d::GetTextBaseline, &Context2d::SetTextBaseline>("textBaseline", napi_default_jsproperty),
164-
InstanceAccessor<&Context2d::GetTextAlign, &Context2d::SetTextAlign>("textAlign", napi_default_jsproperty)
164+
InstanceAccessor<&Context2d::GetTextAlign, &Context2d::SetTextAlign>("textAlign", napi_default_jsproperty),
165+
InstanceAccessor<&Context2d::GetDirection, &Context2d::SetDirection>("direction", napi_default_jsproperty)
165166
});
166167

167168
exports.Set("CanvasRenderingContext2d", ctor);
@@ -230,6 +231,8 @@ Context2d::Context2d(const Napi::CallbackInfo& info) : Napi::ObjectWrap<Context2
230231
pango_context_set_round_glyph_positions(pango_layout_get_context(_layout), FALSE);
231232
#endif
232233

234+
pango_layout_set_auto_dir(_layout, FALSE);
235+
233236
states.emplace();
234237
state = &states.top();
235238
pango_layout_set_font_description(_layout, state->fontDescription);
@@ -762,6 +765,27 @@ Context2d::AddPage(const Napi::CallbackInfo& info) {
762765
cairo_pdf_surface_set_size(canvas()->surface(), width, height);
763766
}
764767

768+
/*
769+
* Get text direction.
770+
*/
771+
Napi::Value
772+
Context2d::GetDirection(const Napi::CallbackInfo& info) {
773+
return Napi::String::New(env, state->direction);
774+
}
775+
776+
/*
777+
* Set text direction.
778+
*/
779+
void
780+
Context2d::SetDirection(const Napi::CallbackInfo& info, const Napi::Value& value) {
781+
if (!value.IsString()) return;
782+
783+
std::string dir = value.As<Napi::String>();
784+
if (dir != "ltr" && dir != "rtl") return;
785+
786+
state->direction = dir;
787+
}
788+
765789
/*
766790
* Put image data.
767791
*
@@ -2451,6 +2475,9 @@ Context2d::paintText(const Napi::CallbackInfo&info, bool stroke) {
24512475
pango_layout_set_text(layout, str.c_str(), -1);
24522476
pango_cairo_update_layout(context(), layout);
24532477

2478+
PangoDirection pango_dir = state->direction == "ltr" ? PANGO_DIRECTION_LTR : PANGO_DIRECTION_RTL;
2479+
pango_context_set_base_dir(pango_layout_get_context(_layout), pango_dir);
2480+
24542481
if (argsNum == 3) {
24552482
scaled_by = get_text_scale(layout, args[2]);
24562483
cairo_save(context());
@@ -2522,18 +2549,26 @@ inline double getBaselineAdjustment(PangoLayout* layout, short baseline) {
25222549
void
25232550
Context2d::setTextPath(double x, double y) {
25242551
PangoRectangle logical_rect;
2552+
text_align_t alignment = state->textAlignment;
25252553

2526-
switch (state->textAlignment) {
2554+
// Convert start/end to left/right based on direction
2555+
if (alignment == TEXT_ALIGNMENT_START) {
2556+
alignment = (state->direction == "rtl") ? TEXT_ALIGNMENT_RIGHT : TEXT_ALIGNMENT_LEFT;
2557+
} else if (alignment == TEXT_ALIGNMENT_END) {
2558+
alignment = (state->direction == "rtl") ? TEXT_ALIGNMENT_LEFT : TEXT_ALIGNMENT_RIGHT;
2559+
}
2560+
2561+
switch (alignment) {
25272562
case TEXT_ALIGNMENT_CENTER:
25282563
pango_layout_get_pixel_extents(_layout, NULL, &logical_rect);
25292564
x -= logical_rect.width / 2;
25302565
break;
2531-
case TEXT_ALIGNMENT_END:
25322566
case TEXT_ALIGNMENT_RIGHT:
25332567
pango_layout_get_pixel_extents(_layout, NULL, &logical_rect);
25342568
x -= logical_rect.width;
25352569
break;
2536-
default: ;
2570+
default: // TEXT_ALIGNMENT_LEFT
2571+
break;
25372572
}
25382573

25392574
y -= getBaselineAdjustment(_layout, state->textBaseline);
@@ -2687,13 +2722,12 @@ Napi::Value
26872722
Context2d::GetTextAlign(const Napi::CallbackInfo& info) {
26882723
const char* align;
26892724
switch (state->textAlignment) {
2690-
default:
2691-
// TODO the default is supposed to be "start"
26922725
case TEXT_ALIGNMENT_LEFT: align = "left"; break;
26932726
case TEXT_ALIGNMENT_START: align = "start"; break;
26942727
case TEXT_ALIGNMENT_CENTER: align = "center"; break;
26952728
case TEXT_ALIGNMENT_RIGHT: align = "right"; break;
26962729
case TEXT_ALIGNMENT_END: align = "end"; break;
2730+
default: align = "start";
26972731
}
26982732
return Napi::String::New(env, align);
26992733
}

src/CanvasRenderingContext2d.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,11 @@ struct canvas_state_t {
3131
cairo_filter_t patternQuality = CAIRO_FILTER_GOOD;
3232
float globalAlpha = 1.f;
3333
int shadowBlur = 0;
34-
text_align_t textAlignment = TEXT_ALIGNMENT_LEFT; // TODO default is supposed to be START
34+
text_align_t textAlignment = TEXT_ALIGNMENT_START;
3535
text_baseline_t textBaseline = TEXT_BASELINE_ALPHABETIC;
3636
canvas_draw_mode_t textDrawingMode = TEXT_DRAW_PATHS;
3737
bool imageSmoothingEnabled = true;
38+
std::string direction = "ltr";
3839

3940
canvas_state_t() {
4041
fontDescription = pango_font_description_from_string("sans");
@@ -182,6 +183,8 @@ class Context2d : public Napi::ObjectWrap<Context2d> {
182183
void BeginTag(const Napi::CallbackInfo& info);
183184
void EndTag(const Napi::CallbackInfo& info);
184185
#endif
186+
Napi::Value GetDirection(const Napi::CallbackInfo& info);
187+
void SetDirection(const Napi::CallbackInfo& info, const Napi::Value& value);
185188
inline void setContext(cairo_t *ctx) { _context = ctx; }
186189
inline cairo_t *context(){ return _context; }
187190
inline Canvas *canvas(){ return _canvas; }

test/canvas.test.js

-2
Original file line numberDiff line numberDiff line change
@@ -569,8 +569,6 @@ describe('Canvas', function () {
569569
const canvas = createCanvas(200, 200)
570570
const ctx = canvas.getContext('2d')
571571

572-
assert.equal('left', ctx.textAlign) // default TODO wrong default
573-
ctx.textAlign = 'start'
574572
assert.equal('start', ctx.textAlign)
575573
ctx.textAlign = 'center'
576574
assert.equal('center', ctx.textAlign)

0 commit comments

Comments
 (0)