Permalink
Browse files

Add pango drawing support, and silence some warnings.

  • Loading branch information...
1 parent d3f3a94 commit 35d491a2841b3f51414e82b1c6ecba361ea08630 @c-spencer c-spencer committed Aug 9, 2012
Showing with 194 additions and 8 deletions.
  1. 0 binding.gyp
  2. +1 −1 examples/text.js
  3. +5 −0 src/Canvas.h
  4. +160 −3 src/CanvasRenderingContext2d.cc
  5. +20 −0 src/CanvasRenderingContext2d.h
  6. +8 −4 src/Image.cc
View
0 binding.gyp 100755 → 100644
No changes.
View
@@ -38,4 +38,4 @@ var out = fs.createWriteStream(__dirname + '/text.png')
stream.on('data', function(chunk){
out.write(chunk);
-});
+});
View
@@ -12,7 +12,12 @@
#include <node.h>
#include <node_object_wrap.h>
#include <node_version.h>
+
+#if HAVE_PANGO
+#include <pango/pangocairo.h>
+#else
#include <cairo/cairo.h>
+#endif
using namespace v8;
using namespace node;
@@ -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<Object> 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,14 +146,22 @@ 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;
state->stroke = transparent;
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<String> 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<Context2d>(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:
@@ -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
View
@@ -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
}

0 comments on commit 35d491a

Please sign in to comment.