Browse Source

Merge branch 'text'

v1.x
Tj Holowaychuk 14 years ago
parent
commit
37c603592b
  1. 60
      lib/canvas.js
  2. 108
      src/CanvasRenderingContext2d.cc
  3. 4
      src/CanvasRenderingContext2d.h
  4. 96
      test/canvas.test.js

60
lib/canvas.js

@ -93,7 +93,15 @@ function normalizedColor(prop) {
}
}
exports.parseFont = function(str){
/**
* Parse font `str`.
*
* @param {String} str
* @return {Object}
* @api public
*/
var parseFont = exports.parseFont = function(str){
if (cache[str]) return cache[str];
var obj = {}
, captures = font.exec(str);
@ -330,6 +338,22 @@ Context2d.prototype.setTransform = function(){
this.transform.apply(this, arguments);
};
/**
* Set text path then `stroke()`.
*
* @param {String} str
* @param {Number} x
* @param {Number} y
* @api public
*/
var strokeText = Context2d.prototype.strokeText;
Context2d.prototype.strokeText = function(str, x, y){
this.beginPath();
strokeText.call(this, str, x, y);
return this.stroke();
};
/**
* Set the fill style with the given css color string.
*
@ -391,6 +415,7 @@ Context2d.prototype.__defineSetter__('strokeStyle', function(val){
*/
Context2d.prototype.__defineGetter__('strokeStyle', normalizedColor('lastStrokeStyle'));
/**
* Set the shadow color with the given css color string.
*
@ -419,3 +444,36 @@ Context2d.prototype.__defineSetter__('shadowColor', function(val){
*/
Context2d.prototype.__defineGetter__('shadowColor', normalizedColor('lastShadowColor'));
/**
* Set font.
*
* @see exports.parseFont()
* @api public
*/
Context2d.prototype.__defineSetter__('font', function(val){
if ('string' == typeof val) {
var font;
if (font = parseFont(val)) {
this.lastFontString = val;
this.setFont(
font.weight
, font.style
, font.size
, font.unit
, font.family);
}
}
});
/**
* Get the current font.
*
* @see exports.parseFont()
* @api public
*/
Context2d.prototype.__defineGetter__('font', function(){
return this.lastFontString || '10px sans-serif';
});

108
src/CanvasRenderingContext2d.cc

@ -88,6 +88,8 @@ Context2d::Initialize(Handle<Object> target) {
NODE_SET_PROTOTYPE_METHOD(t, "strokeRect", StrokeRect);
NODE_SET_PROTOTYPE_METHOD(t, "clearRect", ClearRect);
NODE_SET_PROTOTYPE_METHOD(t, "rect", Rect);
NODE_SET_PROTOTYPE_METHOD(t, "strokeText", StrokeText);
NODE_SET_PROTOTYPE_METHOD(t, "fillText", FillText);
NODE_SET_PROTOTYPE_METHOD(t, "moveTo", MoveTo);
NODE_SET_PROTOTYPE_METHOD(t, "lineTo", LineTo);
NODE_SET_PROTOTYPE_METHOD(t, "bezierCurveTo", BezierCurveTo);
@ -95,6 +97,7 @@ Context2d::Initialize(Handle<Object> target) {
NODE_SET_PROTOTYPE_METHOD(t, "beginPath", BeginPath);
NODE_SET_PROTOTYPE_METHOD(t, "closePath", ClosePath);
NODE_SET_PROTOTYPE_METHOD(t, "arc", Arc);
NODE_SET_PROTOTYPE_METHOD(t, "setFont", SetFont);
NODE_SET_PROTOTYPE_METHOD(t, "setShadowRGBA", SetShadowRGBA);
NODE_SET_PROTOTYPE_METHOD(t, "setFillRGBA", SetFillRGBA);
NODE_SET_PROTOTYPE_METHOD(t, "setStrokeRGBA", SetStrokeRGBA);
@ -790,6 +793,111 @@ Context2d::MoveTo(const Arguments &args) {
return Undefined();
}
/*
* Set font:
* - weight
* - style
* - size
* - unit
* - family
*/
Handle<Value>
Context2d::SetFont(const Arguments &args) {
HandleScope scope;
// Ignore invalid args
if (!args[0]->IsString()
|| !args[1]->IsString()
|| !args[2]->IsNumber()
|| !args[3]->IsString()
|| !args[4]->IsString()) return Undefined();
String::AsciiValue weight(args[0]);
String::AsciiValue style(args[1]);
double size = args[2]->NumberValue();
String::AsciiValue unit(args[3]);
String::AsciiValue family(args[4]);
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
cairo_t *ctx = context->getContext();
// Size
cairo_set_font_size(ctx, size);
// Style
cairo_font_slant_t s = CAIRO_FONT_SLANT_NORMAL;
if (0 == strcmp("italic", *style)) {
s = CAIRO_FONT_SLANT_ITALIC;
} else if (0 == strcmp("oblique", *style)) {
s = CAIRO_FONT_SLANT_OBLIQUE;
}
// Weight
cairo_font_weight_t w = CAIRO_FONT_WEIGHT_NORMAL;
if (0 == strcmp("bold", *weight)) {
w = CAIRO_FONT_WEIGHT_BOLD;
}
cairo_select_font_face(ctx, *family, s, w);
return Undefined();
}
/*
* Stroke text at x, y.
*/
Handle<Value>
Context2d::StrokeText(const Arguments &args) {
HandleScope scope;
// Ignore when args are not present
if (!args[0]->IsString()
|| !args[1]->IsNumber()
|| !args[2]->IsNumber()) return Undefined();
String::Utf8Value str(args[0]);
double x = args[1]->NumberValue()
, y = args[2]->NumberValue();
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
cairo_t *ctx = context->getContext();
cairo_text_extents_t te;
cairo_move_to(ctx, x, y);
cairo_text_path(ctx, *str);
return Undefined();
}
/*
* Fill text at x, y.
*/
Handle<Value>
Context2d::FillText(const Arguments &args) {
HandleScope scope;
// Ignore when args are not present
if (!args[0]->IsString()
|| !args[1]->IsNumber()
|| !args[2]->IsNumber()) return Undefined();
String::Utf8Value str(args[0]);
double x = args[1]->NumberValue()
, y = args[2]->NumberValue();
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
cairo_t *ctx = context->getContext();
cairo_text_extents_t te;
cairo_move_to(ctx, x, y);
cairo_show_text(ctx, *str);
return Undefined();
}
/*
* Fill the rectangle defined by x, y, width and height.
*/

4
src/CanvasRenderingContext2d.h

@ -58,12 +58,14 @@ class Context2d: public node::ObjectWrap {
static Handle<Value> Clip(const Arguments &args);
static Handle<Value> Fill(const Arguments &args);
static Handle<Value> Stroke(const Arguments &args);
static Handle<Value> SetSource(const Arguments &args);
static Handle<Value> SetFont(const Arguments &args);
static Handle<Value> SetFillRGBA(const Arguments &args);
static Handle<Value> SetStrokeRGBA(const Arguments &args);
static Handle<Value> SetShadowRGBA(const Arguments &args);
static Handle<Value> SetFillPattern(const Arguments &args);
static Handle<Value> SetStrokePattern(const Arguments &args);
static Handle<Value> StrokeText(const Arguments &args);
static Handle<Value> FillText(const Arguments &args);
static Handle<Value> BezierCurveTo(const Arguments &args);
static Handle<Value> QuadraticCurveTo(const Arguments &args);
static Handle<Value> LineTo(const Arguments &args);

96
test/canvas.test.js

@ -20,7 +20,7 @@ function assertChecksum(canvas, path, checksum, msg) {
function assertChecksumOf(canvas, path, checksum, msg) {
fs.readFile(path, function(err, buf){
assert.equal(hash(buf), checksum, msg);
assert.equal(hash(buf), checksum, msg + ' \n path: ' + path);
});
}
@ -78,6 +78,8 @@ module.exports = {
, { size: 20, unit: 'px', family: 'monospace' }
, '50px Arial, sans-serif'
, { size: 50, unit: 'px', family: 'Arial, sans-serif' }
, 'bold italic 50px Arial, sans-serif'
, { style: 'italic', weight: 'bold', size: 50, unit: 'px', family: 'Arial, sans-serif' }
, '50px Helvetica , Arial, sans-serif'
, { size: 50, unit: 'px', family: 'Helvetica , Arial, sans-serif' }
, '50px "Helvetica Nueue", sans-serif'
@ -90,6 +92,8 @@ module.exports = {
, { size: 20, unit: 'px', style: 'oblique', family: 'Arial' }
, 'normal 20px Arial'
, { size: 20, unit: 'px', style: 'normal', family: 'Arial' }
, '300 20px Arial'
, { size: 20, unit: 'px', weight: '300', family: 'Arial' }
, '800 20px Arial'
, { size: 20, unit: 'px', weight: '800', family: 'Arial' }
, 'bolder 20px Arial'
@ -352,6 +356,15 @@ module.exports = {
, 'Context2d#rect() failed');
},
'test Context2d#font=': function(assert){
var canvas = new Canvas(200, 200)
, ctx = canvas.getContext('2d');
assert.equal('10px sans-serif', ctx.font);
ctx.font = '15px Arial, sans-serif';
assert.equal('15px Arial, sans-serif', ctx.font);
},
'test Context2d#fillStyle=': function(assert){
var canvas = new Canvas(200, 200)
, ctx = canvas.getContext('2d')
@ -653,6 +666,87 @@ module.exports = {
assert.ok(!ctx.isPointInPath(50,120));
},
'test Context2d#fillText()': function(assert){
var canvas = new Canvas(200, 200)
, ctx = canvas.getContext('2d')
, path = __dirname + '/images/fillText.png';
ctx.font = 'italic 12px Helvetica';
ctx.strokeRect(0,0,200,200);
ctx.lineTo(0,100);
ctx.lineTo(200,100);
ctx.stroke();
ctx.beginPath();
ctx.lineTo(100,0);
ctx.lineTo(100,200);
ctx.stroke();
ctx.fillText("Testing :)", 100, 100);
assertChecksum(
canvas
, path
, '8a1f08023332a424e079b3d6ddfccc56'
, 'Context2d#fillText() failed');
},
'test Context2d#fillText() transformations': function(assert){
var canvas = new Canvas(200, 200)
, ctx = canvas.getContext('2d')
, path = __dirname + '/images/fillText-transformations.png';
ctx.font = 'bold 12px Helvetica';
ctx.strokeRect(0,0,200,200);
ctx.lineTo(0,100);
ctx.lineTo(200,100);
ctx.stroke();
ctx.beginPath();
ctx.lineTo(100,0);
ctx.lineTo(100,200);
ctx.stroke();
ctx.rotate(0.2);
ctx.fillText("foo", 150, 100);
ctx.font = 'normal 30px Impact';
ctx.fillText("bar", 50, 100);
assertChecksum(
canvas
, path
, 'f3879c6cc72916153dc309e2bf49dd0a'
, 'Context2d#fillText() transformations failed');
},
'test Context2d#strokeText()': function(assert){
var canvas = new Canvas(200, 200)
, ctx = canvas.getContext('2d')
, path = __dirname + '/images/strokeText.png';
ctx.strokeRect(0,0,200,200);
ctx.lineTo(0,100);
ctx.lineTo(200,100);
ctx.stroke();
ctx.beginPath();
ctx.lineTo(100,0);
ctx.lineTo(100,200);
ctx.stroke();
ctx.strokeStyle = 'red';
ctx.font = 'normal 50px Impact';
ctx.strokeText("bar", 100, 100);
assertChecksum(
canvas
, path
, 'e5f6d8a3c57e1454c4c79358a32f1c2c'
, 'Context2d#strokeText()');
},
'test Canvas#toBuffer()': function(assert){
assert.ok(Buffer.isBuffer(new Canvas(200, 200).toBuffer()), 'Canvas#toBuffer() failed');
},

Loading…
Cancel
Save