From 35d491a2841b3f51414e82b1c6ecba361ea08630 Mon Sep 17 00:00:00 2001 From: c-spencer Date: Thu, 9 Aug 2012 16:55:36 +0100 Subject: [PATCH] Add pango drawing support, and silence some warnings. --- binding.gyp | 0 examples/text.js | 2 +- src/Canvas.h | 5 + src/CanvasRenderingContext2d.cc | 163 +++++++++++++++++++++++++++++++- src/CanvasRenderingContext2d.h | 20 ++++ src/Image.cc | 12 ++- 6 files changed, 194 insertions(+), 8 deletions(-) mode change 100755 => 100644 binding.gyp diff --git a/binding.gyp b/binding.gyp old mode 100755 new mode 100644 diff --git a/examples/text.js b/examples/text.js index 3d00a08..a8a68c5 100644 --- a/examples/text.js +++ b/examples/text.js @@ -38,4 +38,4 @@ var out = fs.createWriteStream(__dirname + '/text.png') stream.on('data', function(chunk){ out.write(chunk); -}); \ No newline at end of file +}); diff --git a/src/Canvas.h b/src/Canvas.h index f5b3740..66895fb 100644 --- a/src/Canvas.h +++ b/src/Canvas.h @@ -12,7 +12,12 @@ #include #include #include + +#if HAVE_PANGO +#include +#else #include +#endif using namespace v8; using namespace node; diff --git a/src/CanvasRenderingContext2d.cc b/src/CanvasRenderingContext2d.cc index 3952563..dabb479 100644 --- a/src/CanvasRenderingContext2d.cc +++ b/src/CanvasRenderingContext2d.cc @@ -45,6 +45,20 @@ enum { , TEXT_BASELINE_HANGING }; +#if HAVE_PANGO + +/* + * State helper function + */ + +void state_assign_fontFamily(canvas_state_t *state, const char *str) { + free(state->fontFamily); + state->fontFamily = (char *) malloc(strlen(str) + 1); + strcpy(state->fontFamily, str); +} + +#endif + /* * Initialize Context2d. */ @@ -121,6 +135,9 @@ Context2d::Initialize(Handle target) { Context2d::Context2d(Canvas *canvas) { _canvas = canvas; _context = cairo_create(canvas->surface()); +#if HAVE_PANGO + _layout = pango_cairo_create_layout(_context); +#endif cairo_set_line_width(_context, 1); state = states[stateno = 0] = (canvas_state_t *) malloc(sizeof(canvas_state_t)); state->shadowBlur = 0; @@ -129,7 +146,7 @@ Context2d::Context2d(Canvas *canvas) { state->textAlignment = -1; state->fillPattern = state->strokePattern = NULL; state->fillGradient = state->strokeGradient = NULL; - state->textBaseline = NULL; + state->textBaseline = TEXT_BASELINE_ALPHABETIC; rgba_t transparent = { 0,0,0,1 }; rgba_t transparent_black = { 0,0,0,0 }; state->fill = transparent; @@ -137,6 +154,14 @@ Context2d::Context2d(Canvas *canvas) { state->shadow = transparent_black; state->patternQuality = CAIRO_FILTER_GOOD; state->textDrawingMode = TEXT_DRAW_PATHS; +#if HAVE_PANGO + state->fontWeight = PANGO_WEIGHT_NORMAL; + state->fontStyle = PANGO_STYLE_NORMAL; + state->fontSize = 10; + state->fontFamily = NULL; + state_assign_fontFamily(state, "sans serif"); + setFontFromState(); +#endif } /* @@ -177,6 +202,10 @@ Context2d::saveState() { if (stateno == CANVAS_MAX_STATES) return; states[++stateno] = (canvas_state_t *) malloc(sizeof(canvas_state_t)); memcpy(states[stateno], state, sizeof(canvas_state_t)); +#if HAVE_PANGO + states[stateno]->fontFamily = (char *) malloc(strlen(state->fontFamily) + 1); + strcpy(states[stateno]->fontFamily, state->fontFamily); +#endif state = states[stateno]; } @@ -188,9 +217,15 @@ void Context2d::restoreState() { if (0 == stateno) return; // Olaf (2011-02-21): Free old state data +#if HAVE_PANGO + free(states[stateno]->fontFamily); +#endif free(states[stateno]); states[stateno] = NULL; state = states[--stateno]; +#if HAVE_PANGO + setFontFromState(); +#endif } /* @@ -936,6 +971,8 @@ Context2d::GetTextDrawingMode(Local prop, const AccessorInfo &info) { mode = "path"; } else if (context->state->textDrawingMode == TEXT_DRAW_GLYPHS) { mode = "glyph"; + } else { + mode = "unknown"; } return scope.Close(String::NewSymbol(mode)); } @@ -1517,8 +1554,62 @@ 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 + + PangoRectangle ink_rect, logical_rect; + PangoFontMetrics *metrics = NULL; + + pango_layout_set_text(_layout, str, -1); + pango_cairo_update_layout(_context, _layout); + + switch (state->textAlignment) { + // center + case 0: + GET_EXTENTS(); + x -= logical_rect.width / 2; + break; + // right + case 1: + GET_EXTENTS(); + x -= logical_rect.width; + break; + } + + switch (state->textBaseline) { + case TEXT_BASELINE_ALPHABETIC: + GET_METRICS(); + y -= pango_font_metrics_get_ascent(metrics) / PANGO_SCALE; + break; + case TEXT_BASELINE_MIDDLE: + GET_METRICS(); + y -= (pango_font_metrics_get_ascent(metrics) + pango_font_metrics_get_descent(metrics))/(2.0 * PANGO_SCALE); + break; + case TEXT_BASELINE_BOTTOM: + GET_METRICS(); + y -= (pango_font_metrics_get_ascent(metrics) + pango_font_metrics_get_descent(metrics)) / PANGO_SCALE; + break; + } + + if (metrics) pango_font_metrics_unref(metrics); + + cairo_move_to(_context, x, y); + if (state->textDrawingMode == TEXT_DRAW_PATHS) { + pango_cairo_layout_path(_context, _layout); + } else if (state->textDrawingMode == TEXT_DRAW_GLYPHS) { + pango_cairo_show_layout(_context, _layout); + } + +#else + cairo_text_extents_t te; cairo_font_extents_t fe; @@ -1567,6 +1658,8 @@ Context2d::setTextPath(const char *str, double x, double y) { } else if (state->textDrawingMode == TEXT_DRAW_GLYPHS) { cairo_show_text(_context, str); } + +#endif } /* @@ -1636,8 +1729,49 @@ Context2d::SetFont(const Arguments &args) { double size = args[2]->NumberValue(); String::AsciiValue unit(args[3]); String::AsciiValue family(args[4]); - + Context2d *context = ObjectWrap::Unwrap(args.This()); + +#if HAVE_PANGO + + if (strlen(*family) > 0) state_assign_fontFamily(context->state, *family); + + if (size > 0) context->state->fontSize = size; + + if (strlen(*style) > 0) { + if (0 == strcmp("italic", *style)) { + context->state->fontStyle = PANGO_STYLE_ITALIC; + } else if (0 == strcmp("oblique", *style)) { + context->state->fontStyle = PANGO_STYLE_OBLIQUE; + } + } + + if (strlen(*weight) > 0) { + if (0 == strcmp("bold", *weight)) { + context->state->fontWeight = PANGO_WEIGHT_BOLD; + } else if (0 == strcmp("200", *weight)) { + context->state->fontWeight = PANGO_WEIGHT_ULTRALIGHT; + } else if (0 == strcmp("300", *weight)) { + context->state->fontWeight = PANGO_WEIGHT_LIGHT; + } else if (0 == strcmp("400", *weight)) { + context->state->fontWeight = PANGO_WEIGHT_NORMAL; + } else if (0 == strcmp("500", *weight)) { + context->state->fontWeight = PANGO_WEIGHT_MEDIUM; + } else if (0 == strcmp("600", *weight)) { + context->state->fontWeight = PANGO_WEIGHT_SEMIBOLD; + } else if (0 == strcmp("700", *weight)) { + context->state->fontWeight = PANGO_WEIGHT_BOLD; + } else if (0 == strcmp("800", *weight)) { + context->state->fontWeight = PANGO_WEIGHT_ULTRABOLD; + } else if (0 == strcmp("900", *weight)) { + context->state->fontWeight = PANGO_WEIGHT_HEAVY; + } + } + + context->setFontFromState(); + +#else + cairo_t *ctx = context->context(); // Size @@ -1658,10 +1792,33 @@ Context2d::SetFont(const Arguments &args) { } cairo_select_font_face(ctx, *family, s, w); - + +#endif + return Undefined(); } +#if HAVE_PANGO + +/* + * Sets PangoLayout options from the current font state + */ + +void +Context2d::setFontFromState() { + PangoFontDescription *fd = pango_font_description_new(); + + pango_font_description_set_family(fd, state->fontFamily); + pango_font_description_set_absolute_size(fd, state->fontSize * PANGO_SCALE); + pango_font_description_set_style(fd, state->fontStyle); + pango_font_description_set_weight(fd, state->fontWeight); + + pango_layout_set_font_description(_layout, fd); + pango_font_description_free(fd); +} + +#endif + /* * Return the given text extents. * TODO: Support for: diff --git a/src/CanvasRenderingContext2d.h b/src/CanvasRenderingContext2d.h index 1fd7a30..23bb503 100644 --- a/src/CanvasRenderingContext2d.h +++ b/src/CanvasRenderingContext2d.h @@ -40,8 +40,20 @@ typedef struct { double shadowOffsetX; double shadowOffsetY; canvas_draw_mode_t textDrawingMode; + +#if HAVE_PANGO + PangoWeight fontWeight; + PangoStyle fontStyle; + double fontSize; + char *fontFamily; +#endif + } canvas_state_t; +#if HAVE_PANGO +void state_assign_fontFamily(canvas_state_t *state, const char *str); +#endif + class Context2d: public node::ObjectWrap { public: short stateno; @@ -134,11 +146,19 @@ class Context2d: public node::ObjectWrap { void save(); void restore(); +#if HAVE_PANGO + void setFontFromState(); + inline PangoLayout *layout(){ return _layout; } +#endif + private: ~Context2d(); Canvas *_canvas; cairo_t *_context; cairo_path_t *_path; +#if HAVE_PANGO + PangoLayout *_layout; +#endif }; #endif diff --git a/src/Image.cc b/src/Image.cc index bb2ddbf..00d39bb 100644 --- a/src/Image.cc +++ b/src/Image.cc @@ -881,17 +881,21 @@ Image::loadJPEG(FILE *stream) { buf = (uint8_t *) malloc(len); if (!buf) return CAIRO_STATUS_NO_MEMORY; - fread(buf, len, 1, stream); - fclose(stream); - - if ((DATA_IMAGE | DATA_MIME) == data_mode) { + if (fread(buf, len, 1, stream) != len) { + status = CAIRO_STATUS_READ_ERROR; + } else if ((DATA_IMAGE | DATA_MIME) == data_mode) { status = loadJPEGFromBuffer(buf, len); if (!status) status = assignDataAsMime(buf, len, CAIRO_MIME_TYPE_JPEG); } else if (DATA_MIME == data_mode) { status = decodeJPEGBufferIntoMimeSurface(buf, len); + } else { + status = CAIRO_STATUS_READ_ERROR; } + fclose(stream); free(buf); +#else + status = CAIRO_STATUS_READ_ERROR; #endif }