From 6a409597062c9b0a8fefa555e269e070f3de5dcb Mon Sep 17 00:00:00 2001 From: Caleb Hearon Date: Mon, 13 Jan 2025 22:45:21 -0500 Subject: [PATCH] support ctx.direction and textAlign start/end --- index.d.ts | 1 + src/Canvas.h | 1 - src/CanvasRenderingContext2d.cc | 48 +++++++++++++++++++++++++++------ src/CanvasRenderingContext2d.h | 5 +++- 4 files changed, 45 insertions(+), 10 deletions(-) diff --git a/index.d.ts b/index.d.ts index 4cce92c3e..43ff107d0 100644 --- a/index.d.ts +++ b/index.d.ts @@ -290,6 +290,7 @@ export class CanvasRenderingContext2D { textBaseline: CanvasTextBaseline; textAlign: CanvasTextAlign; canvas: Canvas; + direction: 'ltr' | 'rtl'; } export class CanvasGradient { diff --git a/src/Canvas.h b/src/Canvas.h index 5b039539a..eb19843e5 100644 --- a/src/Canvas.h +++ b/src/Canvas.h @@ -37,7 +37,6 @@ enum text_align_t : int8_t { TEXT_ALIGNMENT_LEFT = -1, TEXT_ALIGNMENT_CENTER = 0, TEXT_ALIGNMENT_RIGHT = 1, - // Currently same as LEFT and RIGHT without RTL support: TEXT_ALIGNMENT_START = -2, TEXT_ALIGNMENT_END = 2 }; diff --git a/src/CanvasRenderingContext2d.cc b/src/CanvasRenderingContext2d.cc index dfbcc17a0..691a82e74 100644 --- a/src/CanvasRenderingContext2d.cc +++ b/src/CanvasRenderingContext2d.cc @@ -161,7 +161,8 @@ Context2d::Initialize(Napi::Env& env, Napi::Object& exports) { InstanceAccessor<&Context2d::GetStrokeStyle, &Context2d::SetStrokeStyle>("strokeStyle", napi_default_jsproperty), InstanceAccessor<&Context2d::GetFont, &Context2d::SetFont>("font", napi_default_jsproperty), InstanceAccessor<&Context2d::GetTextBaseline, &Context2d::SetTextBaseline>("textBaseline", napi_default_jsproperty), - InstanceAccessor<&Context2d::GetTextAlign, &Context2d::SetTextAlign>("textAlign", napi_default_jsproperty) + InstanceAccessor<&Context2d::GetTextAlign, &Context2d::SetTextAlign>("textAlign", napi_default_jsproperty), + InstanceAccessor<&Context2d::GetDirection, &Context2d::SetDirection>("direction", napi_default_jsproperty) }); exports.Set("CanvasRenderingContext2d", ctor); @@ -762,6 +763,27 @@ Context2d::AddPage(const Napi::CallbackInfo& info) { cairo_pdf_surface_set_size(canvas()->surface(), width, height); } +/* + * Get text direction. + */ +Napi::Value +Context2d::GetDirection(const Napi::CallbackInfo& info) { + return Napi::String::New(env, state->direction); +} + +/* + * Set text direction. + */ +void +Context2d::SetDirection(const Napi::CallbackInfo& info, const Napi::Value& value) { + if (!value.IsString()) return; + + std::string dir = value.As(); + if (dir != "ltr" && dir != "rtl") return; + + state->direction = dir; +} + /* * Put image data. * @@ -2451,6 +2473,9 @@ Context2d::paintText(const Napi::CallbackInfo&info, bool stroke) { pango_layout_set_text(layout, str.c_str(), -1); pango_cairo_update_layout(context(), layout); + PangoDirection pango_dir = state->direction == "ltr" ? PANGO_DIRECTION_LTR : PANGO_DIRECTION_RTL; + pango_context_set_base_dir(pango_layout_get_context(_layout), pango_dir); + if (argsNum == 3) { scaled_by = get_text_scale(layout, args[2]); cairo_save(context()); @@ -2522,18 +2547,26 @@ inline double getBaselineAdjustment(PangoLayout* layout, short baseline) { void Context2d::setTextPath(double x, double y) { PangoRectangle logical_rect; + text_align_t alignment = state->textAlignment; - switch (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; + } + + switch (alignment) { case TEXT_ALIGNMENT_CENTER: pango_layout_get_pixel_extents(_layout, NULL, &logical_rect); x -= logical_rect.width / 2; break; - case TEXT_ALIGNMENT_END: case TEXT_ALIGNMENT_RIGHT: pango_layout_get_pixel_extents(_layout, NULL, &logical_rect); x -= logical_rect.width; break; - default: ; + default: // TEXT_ALIGNMENT_LEFT + break; } y -= getBaselineAdjustment(_layout, state->textBaseline); @@ -2687,13 +2720,12 @@ Napi::Value Context2d::GetTextAlign(const Napi::CallbackInfo& info) { const char* align; switch (state->textAlignment) { - default: - // TODO the default is supposed to be "start" case TEXT_ALIGNMENT_LEFT: align = "left"; break; - case TEXT_ALIGNMENT_START: align = "start"; break; - case TEXT_ALIGNMENT_CENTER: align = "center"; break; case TEXT_ALIGNMENT_RIGHT: align = "right"; break; + case TEXT_ALIGNMENT_CENTER: align = "center"; break; + case TEXT_ALIGNMENT_START: align = "start"; break; case TEXT_ALIGNMENT_END: align = "end"; break; + default: align = "start"; } return Napi::String::New(env, align); } diff --git a/src/CanvasRenderingContext2d.h b/src/CanvasRenderingContext2d.h index a78788451..16cd42d34 100644 --- a/src/CanvasRenderingContext2d.h +++ b/src/CanvasRenderingContext2d.h @@ -31,10 +31,11 @@ struct canvas_state_t { cairo_filter_t patternQuality = CAIRO_FILTER_GOOD; float globalAlpha = 1.f; int shadowBlur = 0; - text_align_t textAlignment = TEXT_ALIGNMENT_LEFT; // TODO default is supposed to be START + text_align_t textAlignment = TEXT_ALIGNMENT_START; text_baseline_t textBaseline = TEXT_BASELINE_ALPHABETIC; canvas_draw_mode_t textDrawingMode = TEXT_DRAW_PATHS; bool imageSmoothingEnabled = true; + std::string direction = "ltr"; canvas_state_t() { fontDescription = pango_font_description_from_string("sans"); @@ -182,6 +183,8 @@ class Context2d : public Napi::ObjectWrap { void BeginTag(const Napi::CallbackInfo& info); void EndTag(const Napi::CallbackInfo& info); #endif + Napi::Value GetDirection(const Napi::CallbackInfo& info); + void SetDirection(const Napi::CallbackInfo& info, const Napi::Value& value); inline void setContext(cairo_t *ctx) { _context = ctx; } inline cairo_t *context(){ return _context; } inline Canvas *canvas(){ return _canvas; }