Skip to content

Commit

Permalink
Add Canvas strokeText()
Browse files Browse the repository at this point in the history
  • Loading branch information
TooTallNate committed Dec 9, 2023
1 parent 4946766 commit f3c52b6
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/wise-dolphins-smell.md
@@ -0,0 +1,5 @@
---
'nxjs-runtime': patch
---

Add Canvas `strokeText()`
12 changes: 11 additions & 1 deletion packages/runtime/src/canvas/canvas-rendering-context-2d.ts
Expand Up @@ -741,13 +741,23 @@ export class CanvasRenderingContext2D {
this.moveTo(x, y);
}

/**
* Draws the outlines of the characters of the text string at the specified coordinates,
* stroking the string's characters with the current {@link CanvasRenderingContext2D.strokeStyle | `strokeStyle`}.
*
* @param text A string specifying the text string to render into the context.
* @param x The x-axis coordinate of the point at which to begin drawing the text, in pixels.
* @param y The y-axis coordinate of the baseline on which to begin drawing the text, in pixels.
* @param maxWidth The maximum number of pixels wide the text may be once rendered. If not specified, there is no limit to the width of the text.
* @see https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/strokeText
*/
strokeText(
text: string,
x: number,
y: number,
maxWidth?: number | undefined
): void {
throw new Error('Method not implemented.');
stub();
}

createConicGradient(
Expand Down
Expand Up @@ -737,13 +737,23 @@ export class OffscreenCanvasRenderingContext2D {
this.moveTo(x, y);
}

/**
* Draws the outlines of the characters of the text string at the specified coordinates,
* stroking the string's characters with the current {@link CanvasRenderingContext2D.strokeStyle | `strokeStyle`}.
*
* @param text A string specifying the text string to render into the context.
* @param x The x-axis coordinate of the point at which to begin drawing the text, in pixels.
* @param y The y-axis coordinate of the baseline on which to begin drawing the text, in pixels.
* @param maxWidth The maximum number of pixels wide the text may be once rendered. If not specified, there is no limit to the width of the text.
* @see https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/strokeText
*/
strokeText(
text: string,
x: number,
y: number,
maxWidth?: number | undefined
): void {
throw new Error('Method not implemented.');
stub();
}

createConicGradient(
Expand Down
63 changes: 63 additions & 0 deletions source/canvas.c
Expand Up @@ -582,6 +582,68 @@ static JSValue nx_canvas_context_2d_fill_text(JSContext *ctx, JSValueConst this_
return JS_UNDEFINED;
}

static JSValue nx_canvas_context_2d_stroke_text(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
CANVAS_CONTEXT_THIS;
double args[2];
if (js_validate_doubles_args(ctx, argv, args, 2, 1))
return JS_EXCEPTION;

const char *text = JS_ToCString(ctx, argv[0]);
if (!text)
return JS_EXCEPTION;

save_path(context);

// Create HarfBuzz buffer
hb_buffer_t *buf = hb_buffer_create();

// Set buffer to LTR direction, common script and default language
hb_buffer_set_direction(buf, HB_DIRECTION_LTR);
hb_buffer_set_script(buf, HB_SCRIPT_COMMON);
hb_buffer_set_language(buf, hb_language_get_default());

// Add text and layout it
hb_buffer_add_utf8(buf, text, -1, 0, -1);
hb_shape(context->hb_font, buf, NULL, 0);

// Get buffer data
unsigned int glyph_count = hb_buffer_get_length (buf);
hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buf, NULL);
hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buf, NULL);

// Shape glyph for Cairo
cairo_glyph_t *cairo_glyphs = cairo_glyph_allocate(glyph_count);
int x = 0;
int y = 0;
for (int i = 0 ; i < glyph_count ; ++i) {
cairo_glyphs[i].index = glyph_info[i].codepoint;
cairo_glyphs[i].x = x + (glyph_pos[i].x_offset / (64.0));
cairo_glyphs[i].y = -(y + glyph_pos[i].y_offset / (64.0));
x += glyph_pos[i].x_advance / (64.0);
y += glyph_pos[i].y_advance / (64.0);
}

// Move glyphs to the correct positions
for (int i = 0 ; i < glyph_count ; ++i) {
cairo_glyphs[i].x += args[0];
cairo_glyphs[i].y += args[1];
}

// Draw the text onto the Cairo surface
cairo_glyph_path(context->ctx, cairo_glyphs, glyph_count);

stroke(context, false);

restore_path(context);

cairo_glyph_free(cairo_glyphs);
hb_buffer_destroy(buf);
JS_FreeCString(ctx, text);

return JS_UNDEFINED;
}

static JSValue nx_canvas_context_2d_measure_text(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
CANVAS_CONTEXT_THIS;
Expand Down Expand Up @@ -1905,6 +1967,7 @@ static JSValue nx_canvas_context_2d_init_class(JSContext *ctx, JSValueConst this
NX_DEF_FUNC(proto, "setLineDash", nx_canvas_context_2d_set_line_dash, 1);
NX_DEF_FUNC(proto, "stroke", nx_canvas_context_2d_stroke, 0);
NX_DEF_FUNC(proto, "strokeRect", nx_canvas_context_2d_stroke_rect, 4);
NX_DEF_FUNC(proto, "strokeText", nx_canvas_context_2d_stroke_text, 3);
NX_DEF_FUNC(proto, "transform", nx_canvas_context_2d_transform, 6);
NX_DEF_FUNC(proto, "translate", nx_canvas_context_2d_translate, 2);
JS_FreeValue(ctx, proto);
Expand Down

0 comments on commit f3c52b6

Please sign in to comment.