From efadaead733585d650557a7742479fa9e84afcab Mon Sep 17 00:00:00 2001 From: c-spencer Date: Fri, 10 Aug 2012 11:48:39 +0100 Subject: [PATCH] Add measureText implementation for pango --- src/CanvasRenderingContext2d.cc | 109 ++++++++++++++++++++++++++------ 1 file changed, 88 insertions(+), 21 deletions(-) diff --git a/src/CanvasRenderingContext2d.cc b/src/CanvasRenderingContext2d.cc index 93de1ac..a3ceaa6 100644 --- a/src/CanvasRenderingContext2d.cc +++ b/src/CanvasRenderingContext2d.cc @@ -56,6 +56,16 @@ void state_assign_fontFamily(canvas_state_t *state, const char *str) { state->fontFamily = strndup(str, 100); } + +/* + * Simple helper macro for a rather verbose function call. + */ + +#define PANGO_LAYOUT_GET_METRICS(LAYOUT) pango_context_get_metrics( \ + pango_layout_get_context(LAYOUT), \ + pango_layout_get_font_description(LAYOUT), \ + pango_context_get_language(pango_layout_get_context(LAYOUT))) + #endif /* @@ -1552,13 +1562,6 @@ Context2d::StrokeText(const Arguments &args) { * Set text path for the given string at (x, y). */ -// Define simple macro substitution so we can use these lazily within setTextPath -#define GET_EXTENTS() pango_layout_get_pixel_extents(_layout, &ink_rect, &logical_rect) -#define GET_METRICS() metrics = pango_context_get_metrics( \ - pango_layout_get_context(_layout), \ - pango_layout_get_font_description(_layout), \ - pango_context_get_language(pango_layout_get_context(_layout))) - void Context2d::setTextPath(const char *str, double x, double y) { #if HAVE_PANGO @@ -1572,27 +1575,27 @@ Context2d::setTextPath(const char *str, double x, double y) { switch (state->textAlignment) { // center case 0: - GET_EXTENTS(); + pango_layout_get_pixel_extents(_layout, &ink_rect, &logical_rect); x -= logical_rect.width / 2; break; // right case 1: - GET_EXTENTS(); + pango_layout_get_pixel_extents(_layout, &ink_rect, &logical_rect); x -= logical_rect.width; break; } switch (state->textBaseline) { case TEXT_BASELINE_ALPHABETIC: - GET_METRICS(); + metrics = PANGO_LAYOUT_GET_METRICS(_layout); y -= pango_font_metrics_get_ascent(metrics) / PANGO_SCALE; break; case TEXT_BASELINE_MIDDLE: - GET_METRICS(); + metrics = PANGO_LAYOUT_GET_METRICS(_layout); y -= (pango_font_metrics_get_ascent(metrics) + pango_font_metrics_get_descent(metrics))/(2.0 * PANGO_SCALE); break; case TEXT_BASELINE_BOTTOM: - GET_METRICS(); + metrics = PANGO_LAYOUT_GET_METRICS(_layout); y -= (pango_font_metrics_get_ascent(metrics) + pango_font_metrics_get_descent(metrics)) / PANGO_SCALE; break; } @@ -1834,14 +1837,72 @@ Context2d::MeasureText(const Arguments &args) { String::Utf8Value str(args[0]->ToString()); Local obj = Object::New(); +#if HAVE_PANGO + + PangoRectangle ink_rect, logical_rect; + PangoFontMetrics *metrics; + PangoLayout *layout = context->layout(); + + pango_layout_set_text(layout, *str, -1); + pango_cairo_update_layout(ctx, layout); + + pango_layout_get_pixel_extents(layout, &ink_rect, &logical_rect); + metrics = PANGO_LAYOUT_GET_METRICS(layout); + + double x_offset; + switch (context->state->textAlignment) { + case 0: // center + x_offset = logical_rect.width / 2; + break; + case 1: // right + x_offset = logical_rect.width; + break; + default: // left + x_offset = 0.0; + } + + double y_offset; + switch (context->state->textBaseline) { + case TEXT_BASELINE_ALPHABETIC: + y_offset = -pango_font_metrics_get_ascent(metrics) / PANGO_SCALE; + break; + case TEXT_BASELINE_MIDDLE: + y_offset = -(pango_font_metrics_get_ascent(metrics) + pango_font_metrics_get_descent(metrics))/(2.0 * PANGO_SCALE); + break; + case TEXT_BASELINE_BOTTOM: + y_offset = -(pango_font_metrics_get_ascent(metrics) + pango_font_metrics_get_descent(metrics)) / PANGO_SCALE; + break; + default: + y_offset = 0.0; + } + + obj->Set(String::New("width"), Number::New(logical_rect.width)); + obj->Set(String::New("actualBoundingBoxLeft"), + Number::New(x_offset - PANGO_LBEARING(logical_rect))); + obj->Set(String::New("actualBoundingBoxRight"), + Number::New(x_offset + PANGO_RBEARING(logical_rect))); + obj->Set(String::New("actualBoundingBoxAscent"), + Number::New(-(y_offset+ink_rect.y))); + obj->Set(String::New("actualBoundingBoxDescent"), + Number::New((PANGO_DESCENT(ink_rect) + y_offset))); + obj->Set(String::New("emHeightAscent"), + Number::New(PANGO_ASCENT(logical_rect) - y_offset)); + obj->Set(String::New("emHeightDescent"), + Number::New(PANGO_DESCENT(logical_rect) + y_offset)); + obj->Set(String::New("alphabeticBaseline"), + Number::New((pango_font_metrics_get_ascent(metrics) / PANGO_SCALE) + + y_offset)); + + pango_font_metrics_unref(metrics); + +#else + cairo_text_extents_t te; cairo_font_extents_t fe; cairo_text_extents(ctx, *str, &te); cairo_font_extents(ctx, &fe); - obj->Set(String::New("width"), Number::New(te.x_advance)); - double x_offset; switch (context->state->textAlignment) { case 0: // center @@ -1854,9 +1915,6 @@ Context2d::MeasureText(const Arguments &args) { x_offset = 0.0; } - obj->Set(String::New("actualBoundingBoxLeft"), Number::New(x_offset - te.x_bearing)); - obj->Set(String::New("actualBoundingBoxRight"), Number::New((te.x_bearing + te.width) - x_offset)); - double y_offset; switch (context->state->textBaseline) { case TEXT_BASELINE_TOP: @@ -1872,12 +1930,21 @@ Context2d::MeasureText(const Arguments &args) { default: y_offset = 0.0; } - obj->Set(String::New("actualBoundingBoxAscent"), Number::New(-(te.y_bearing + y_offset))); - obj->Set(String::New("actualBoundingBoxDescent"), Number::New(te.height + te.y_bearing + y_offset)); - + + obj->Set(String::New("width"), Number::New(te.x_advance)); + obj->Set(String::New("actualBoundingBoxLeft"), + Number::New(x_offset - te.x_bearing)); + obj->Set(String::New("actualBoundingBoxRight"), + Number::New((te.x_bearing + te.width) - x_offset)); + obj->Set(String::New("actualBoundingBoxAscent"), + Number::New(-(te.y_bearing + y_offset))); + obj->Set(String::New("actualBoundingBoxDescent"), + Number::New(te.height + te.y_bearing + y_offset)); obj->Set(String::New("emHeightAscent"), Number::New(fe.ascent - y_offset)); obj->Set(String::New("emHeightDescent"), Number::New(fe.descent + y_offset)); - obj->Set(String::New("alphabeticBaseline"), Number::New(-y_offset)); + obj->Set(String::New("alphabeticBaseline"), Number::New(y_offset)); + +#endif return scope.Close(obj); }