Support completed
authorMatthew Mondor <mmondor@pulsar-zone.net>
Thu, 26 Oct 2006 00:35:00 +0000 (00:35 +0000)
committerMatthew Mondor <mmondor@pulsar-zone.net>
Thu, 26 Oct 2006 00:35:00 +0000 (00:35 +0000)
mmsoftware/js/classes/js_gd.c

index 18f8ccc..5d26dae 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: js_gd.c,v 1.4 2006/10/25 19:32:24 mmondor Exp $ */
+/* $Id: js_gd.c,v 1.5 2006/10/26 00:35:00 mmondor Exp $ */
 
 /*
  * Copyright (c) 2006, Matthew Mondor
 #include <jsapi.h>
 
 #include <gd.h>
+#include <gdfonts.h>
+#include <gdfontl.h>
+#include <gdfontmb.h>
+#include <gdfontg.h>
+#include <gdfontt.h>
 
 #include <js_gd.h>
 #include <js_file.h>
@@ -111,6 +116,8 @@ static JSBool       gd_sm_true_color(JSContext *, JSObject *, uintN, jsval *,
                    jsval *);
 static JSBool  gd_sm_true_color_alpha(JSContext *, JSObject *, uintN, jsval *,
                    jsval *);
+static JSBool  gd_sm_ft_use_fontconfig(JSContext *, JSObject *, uintN,
+                   jsval *, jsval *);
 
 /* GDImage */
 static JSObject *js_InitGDImageClass(JSContext *, JSObject *);
@@ -277,6 +284,27 @@ static JSBool      gdimage_m_color_deallocate(JSContext *, JSObject *, uintN,
 static JSBool  gdimage_m_color_transparent(JSContext *, JSObject *, uintN,
                    jsval *, jsval *);
 
+static JSBool  gdimage_m_char_i(JSContext *, JSObject *, uintN, jsval *,
+                   jsval *,
+                   void (*)(gdImagePtr, gdFontPtr, int, int, int, int));
+static JSBool  gdimage_m_string_i(JSContext *, JSObject *, uintN, jsval *,
+                   jsval *, void (*)(gdImagePtr, gdFontPtr, int, int,
+                   unsigned char *, int));
+static JSBool  gdimage_m_char(JSContext *, JSObject *, uintN, jsval *,
+                   jsval *);
+static JSBool  gdimage_m_char_up(JSContext *, JSObject *, uintN, jsval *,
+                   jsval *);
+static JSBool  gdimage_m_string(JSContext *, JSObject *, uintN, jsval *,
+                   jsval *);
+static JSBool  gdimage_m_string_up(JSContext *, JSObject *, uintN, jsval *,
+                   jsval *);
+static JSBool  gdimage_m_string_ft(JSContext *, JSObject *, uintN, jsval *,
+                   jsval *);
+static JSBool  gdimage_m_string_ft_ex(JSContext *, JSObject *, uintN, jsval *,
+                   jsval *);
+static JSBool  gdimage_m_string_ft_circle(JSContext *, JSObject *, uintN,
+                   jsval *, jsval *);
+
 static JSBool  gdimage_m_copy(JSContext *, JSObject *, uintN, jsval *,
                    jsval *);
 static JSBool  gdimage_m_copy_resized(JSContext *, JSObject *, uintN, jsval *,
@@ -299,6 +327,14 @@ static JSBool      gdimage_m_compare(JSContext *, JSObject *, uintN, jsval *,
 static JSBool  gdimage_m_interlace(JSContext *, JSObject *, uintN, jsval *,
                    jsval *);
 
+/* GDFont */
+static JSObject        *js_InitGDFontClass(JSContext *, JSObject *, JSObject *);
+static JSBool  gdfont_constructor(JSContext *, JSObject *, uintN, jsval *,
+                   jsval *);
+static void    gdfont_finalize(JSContext *, JSObject *);
+static JSObject        *gdfont_new(JSContext *, gdFontPtr);
+static gdFontPtr       gdfont_get(JSContext *, jsval);
+
 
 
 /*
@@ -339,6 +375,7 @@ static JSFunctionSpec gd_smethods[] = {
 #endif
        { "trueColor", gd_sm_true_color, 3, 0, 0 },
        { "trueColorAlpha", gd_sm_true_color_alpha, 4, 0, 0 },
+       { "ftUseFontConfig", gd_sm_ft_use_fontconfig, 1, 0, 0 },
        { NULL, NULL, 0, 0, 0 }
 };
 
@@ -374,6 +411,9 @@ static struct property_spec gd_sprops[] = {
        SP(GD_CMP_BACKGROUND),
        SP(GD_CMP_INTERLACE),
        SP(GD_CMP_TRUECOLOR),
+       SP(gdFTEX_Unicode),
+       SP(gdFTEX_Shift_JIS),
+       SP(gdFTEX_Big5),
        { NULL, 0 }
 };
 
@@ -477,6 +517,22 @@ static JSFunctionSpec gdimage_methods[] = {
        { "colorDeallocate", gdimage_m_color_deallocate, 1, 0, 0 },
        { "colorTransparent", gdimage_m_color_transparent, 1, 0, 0 },
 
+       /*
+        * We don't implement charRight16, charUp16, stringRight16 and
+        * stringUp16 because GD fonts actually don't support 16-bit chars.
+        * stringFt[Ex] may be used instead which allow UTF-8 and use of
+        * external fonts.  Moreover, we don't support stringFtt which was for
+        * use with FontType v1, and is now deprecated (calling the v2
+        * function internally for compatibility).
+        */
+       { "charRight", gdimage_m_char, 5, 0, 0 },       /* char == reserved! */
+       { "charUp", gdimage_m_char_up, 5, 0, 0 },
+       { "stringRight", gdimage_m_string, 5, 0, 0 },   /* string reserved! */
+       { "stringUp", gdimage_m_string_up, 5, 0, 0 },
+       { "stringFt", gdimage_m_string_ft, 7, 0, 0 },
+       { "stringFtEx", gdimage_m_string_ft_ex, 8, 0, 0 },
+       { "stringFtCircle", gdimage_m_string_ft_circle, 10, 0, 0 },
+
        { "copy", gdimage_m_copy, 7, 0, 0 },
        { "copyResized", gdimage_m_copy_resized, 9, 0, 0 },
        { "copyResamped", gdimage_m_copy_resampled, 9, 0, 0 },
@@ -487,13 +543,26 @@ static JSFunctionSpec gdimage_methods[] = {
        { "squareToCircle", gdimage_m_square_to_circle, 1, 0, 0 },
 
        { "compare", gdimage_m_compare, 1, 0, 0 },
-       { "interlace", gdimage_m_compare, 1, 0, 0 },
+       { "interlace", gdimage_m_interlace, 1, 0, 0 },
        { NULL, NULL, 0, 0, 0 }
 };
 
 
 
 /*
+ * GDFont
+ */
+
+/* Class */
+static JSClass gdfont_class = {
+       "GDFont", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub,
+       JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub,
+       JS_ConvertStub, gdfont_finalize
+};
+
+
+
+/*
  * GD object control
  */
 
@@ -531,6 +600,19 @@ js_InitGDClass(JSContext *cx, JSObject *obj)
                (void) fprintf(stderr, "GD: InitGDImageClass()\n");
                goto err;
        }
+       /*
+        * Same for GDFont, which will use our supplied GD object pointer to
+        * set properties for existing fonts.
+        */
+       if (js_InitGDFontClass(cx, obj, ctor) == NULL) {
+               (void) fprintf(stderr, "GD: InitGDFontClass()\n");
+               goto err;
+       }
+
+       if (gdFontCacheSetup() != 0) {
+               (void) fprintf(stderr, "gdFontCacheSetup()\n");
+               goto err;
+       }
 
        return proto;
 
@@ -1126,6 +1208,32 @@ err:
        return JS_FALSE;
 }
 
+static JSBool
+gd_sm_ft_use_fontconfig(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+       int     v;
+
+       if (argc != 1) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               goto err;
+       }
+       if (!JSVAL_IS_BOOLEAN(argv[0])) {
+               QUEUE_EXCEPTION("Argument 1 not a Boolean");
+               goto err;
+       }
+
+       v = gdFTUseFontConfig((int)JSVAL_TO_BOOLEAN(argv[0]));
+       *rval = BOOLEAN_TO_JSVAL((JSBool)v);
+
+       return JS_TRUE;
+
+err:
+       *rval = OBJECT_TO_JSVAL(NULL);
+
+       return JS_FALSE;
+}
+
 
 /*
  * GDImage object control
@@ -3306,6 +3414,435 @@ gdimage_m_color_transparent(JSContext *cx, JSObject *obj, uintN argc,
 
 
 static JSBool
+gdimage_m_char_i(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval, void (*funcptr)(gdImagePtr, gdFontPtr, int, int, int, int))
+{
+       gdImagePtr      img;
+       gdFontPtr       f;
+
+       *rval = OBJECT_TO_JSVAL(NULL);
+
+       if (argc != 5) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+       if ((f = gdfont_get(cx, argv[0])) == NULL) {
+               QUEUE_EXCEPTION("Argument 1 not a GDFont object");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_INT(argv[1])) {
+               QUEUE_EXCEPTION("Argument 2 not an Integer");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_INT(argv[2])) {
+               QUEUE_EXCEPTION("Argument 3 not an Integer");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_INT(argv[3])) {
+               QUEUE_EXCEPTION("Argument 4 not an Integer");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_INT(argv[4])) {
+               QUEUE_EXCEPTION("Argument 5 not an Integer");
+               return JS_FALSE;
+       }
+
+       img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL);
+       assert(img != NULL);
+
+       funcptr(img, f, JSVAL_TO_INT(argv[1]), JSVAL_TO_INT(argv[2]),
+           JSVAL_TO_INT(argv[3]), JSVAL_TO_INT(argv[4]));
+
+       return JS_TRUE;
+}
+
+static JSBool
+gdimage_m_string_i(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval,
+    void (*funcptr)(gdImagePtr, gdFontPtr, int, int, unsigned char *, int))
+{
+       gdImagePtr      img;
+       gdFontPtr       f;
+       char            *bytes;
+
+       *rval = OBJECT_TO_JSVAL(NULL);
+
+       if (argc != 5) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+       if ((f = gdfont_get(cx, argv[0])) == NULL) {
+               QUEUE_EXCEPTION("Argument 1 not a GDFont object");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_INT(argv[1])) {
+               QUEUE_EXCEPTION("Argument 2 not an Integer");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_INT(argv[2])) {
+               QUEUE_EXCEPTION("Argument 3 not an Integer");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_STRING(argv[3]) ||
+           (bytes = JS_GetStringBytes(JSVAL_TO_STRING(argv[3]))) == NULL) {
+               QUEUE_EXCEPTION("Argument 4 not a String");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_INT(argv[4])) {
+               QUEUE_EXCEPTION("Argument 5 not an Integer");
+               return JS_FALSE;
+       }
+
+       img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL);
+       assert(img != NULL);
+
+       funcptr(img, f, JSVAL_TO_INT(argv[1]), JSVAL_TO_INT(argv[2]), bytes,
+           JSVAL_TO_INT(argv[4]));
+
+       return JS_TRUE;
+}
+
+static JSBool
+gdimage_m_char(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+
+       return gdimage_m_char_i(cx, obj, argc, argv, rval, gdImageChar);
+}
+
+static JSBool
+gdimage_m_char_up(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+
+       return gdimage_m_char_i(cx, obj, argc, argv, rval, gdImageCharUp);
+}
+
+static JSBool
+gdimage_m_string(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+
+       return gdimage_m_string_i(cx, obj, argc, argv, rval, gdImageString);
+}
+
+static JSBool
+gdimage_m_string_up(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+
+       return gdimage_m_string_i(cx, obj, argc, argv, rval, gdImageStringUp);
+}
+
+static JSBool
+gdimage_m_string_ft(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+       gdImagePtr      img;
+       char            *font, *string, *error;
+       jsdouble        ptsize, angle;
+       int             brect[8], i;
+       jsval           rect[8];
+       JSObject        *a;
+
+       if (argc != 7) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               goto err;
+       }
+       if (!JSVAL_IS_INT(argv[0])) {
+               QUEUE_EXCEPTION("Argument 1 not an Integer");
+               goto err;
+       }
+       if (!JSVAL_IS_STRING(argv[1]) ||
+           (font = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]))) == NULL) {
+               QUEUE_EXCEPTION("Argument 2 not a String");
+               goto err;
+       }
+       if (!JSVAL_IS_NUMBER(argv[2]) ||
+           !JS_ValueToNumber(cx, argv[2], &ptsize)) {
+               QUEUE_EXCEPTION("Argument 3 not a Number");
+               goto err;
+       }
+       if (!JSVAL_IS_NUMBER(argv[3]) ||
+           !JS_ValueToNumber(cx, argv[3], &angle)) {
+               QUEUE_EXCEPTION("Argument 4 not a Number");
+               goto err;
+       }
+       if (!JSVAL_IS_INT(argv[4])) {
+               QUEUE_EXCEPTION("Argument 5 not an Integer");
+               goto err;
+       }
+       if (!JSVAL_IS_INT(argv[5])) {
+               QUEUE_EXCEPTION("Argument 6 not an Integer");
+               goto err;
+       }
+       if (!JSVAL_IS_STRING(argv[6]) ||
+           (string = JS_GetStringBytes(JSVAL_TO_STRING(argv[6]))) == NULL) {
+               QUEUE_EXCEPTION("Argument 7 not a String");
+               goto err;
+       }
+
+       img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL);
+       assert(img != NULL);
+
+       if ((error = gdImageStringFT(img, brect, JSVAL_TO_INT(argv[0]), font,
+           (double)ptsize, (double)angle, JSVAL_TO_INT(argv[4]),
+           JSVAL_TO_INT(argv[5]), string)) != NULL) {
+               QUEUE_EXCEPTION(error);
+               goto err;
+       }
+
+       for (i = 0; i < 8; i++)
+               rect[i] = INT_TO_JSVAL(brect[i]);
+       if ((a = JS_NewArrayObject(cx, 8, rect)) == NULL) {
+               QUEUE_EXCEPTION("Internal error!");
+               goto err;
+       }
+       *rval = OBJECT_TO_JSVAL(a);
+
+       return JS_TRUE;
+
+err:
+       *rval = OBJECT_TO_JSVAL(NULL);
+
+       return JS_FALSE;
+}
+
+static JSBool
+gdimage_m_string_ft_ex(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+       gdImagePtr      img;
+       char            *font, *string, *error;
+       jsdouble        ptsize, angle;
+       int             brect[8], i;
+       jsval           rect[8];
+       JSObject        *o, *a;
+       JSString        *str;
+       gdFTStringExtra ex;
+
+       ex.flags = gdFTEX_XSHOW | gdFTEX_RETURNFONTPATHNAME |
+           gdFTEX_RESOLUTION;
+       ex.hdpi = ex.vdpi = 96;
+       ex.xshow = ex.fontpath = NULL;
+
+       if (argc != 8) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               goto err;
+       }
+       if (!JSVAL_IS_INT(argv[0])) {
+               QUEUE_EXCEPTION("Argument 1 not an Integer");
+               goto err;
+       }
+       if (!JSVAL_IS_STRING(argv[1]) ||
+           (font = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]))) == NULL) {
+               QUEUE_EXCEPTION("Argument 2 not a String");
+               goto err;
+       }
+       if (!JSVAL_IS_NUMBER(argv[2]) ||
+           !JS_ValueToNumber(cx, argv[2], &ptsize)) {
+               QUEUE_EXCEPTION("Argument 3 not a Number");
+               goto err;
+       }
+       if (!JSVAL_IS_NUMBER(argv[3]) ||
+           !JS_ValueToNumber(cx, argv[3], &angle)) {
+               QUEUE_EXCEPTION("Argument 4 not a Number");
+               goto err;
+       }
+       if (!JSVAL_IS_INT(argv[4])) {
+               QUEUE_EXCEPTION("Argument 5 not an Integer");
+               goto err;
+       }
+       if (!JSVAL_IS_INT(argv[5])) {
+               QUEUE_EXCEPTION("Argument 6 not an Integer");
+               goto err;
+       }
+       if (!JSVAL_IS_STRING(argv[6]) ||
+           (string = JS_GetStringBytes(JSVAL_TO_STRING(argv[6]))) == NULL) {
+               QUEUE_EXCEPTION("Argument 7 not a String");
+               goto err;
+       }
+       if (!JSVAL_IS_NULL(argv[7]) && !JSVAL_IS_OBJECT(argv[7])) {
+               QUEUE_EXCEPTION("Argument 8 not Null or Object");
+               goto err;
+       }
+
+       if (!JSVAL_IS_NULL(argv[7])) {
+               JSObject        *o;
+               jsval           v;
+
+               o = JSVAL_TO_OBJECT(argv[7]);
+
+               if (JS_GetProperty(cx, o, "linespacing", &v) &&
+                   !JSVAL_IS_VOID(v)) {
+                       jsdouble        d;
+
+                       if (!JSVAL_IS_NUMBER(v) ||
+                           !JS_ValueToNumber(cx, v, &d)) {
+                               QUEUE_EXCEPTION("linespacing not a Number");
+                               goto err;
+                       }
+                       ex.linespacing = (double)d;
+                       ex.flags |= gdFTEX_LINESPACE;
+               }
+               if (JS_GetProperty(cx, o, "charmap", &v) &&
+                   !JSVAL_IS_VOID(v)) {
+                       if (!JSVAL_IS_INT(v)) {
+                               QUEUE_EXCEPTION("charmap not an Integer");
+                               goto err;
+                       }
+                       ex.charmap = JSVAL_TO_INT(v);
+                       ex.flags |= gdFTEX_CHARMAP;
+               }
+               if (JS_GetProperty(cx, o, "hdpi", &v) && !JSVAL_IS_VOID(v)) {
+                       if (!JSVAL_IS_INT(v)) {
+                               QUEUE_EXCEPTION("hdpi not an Integer");
+                               goto err;
+                       }
+                       ex.hdpi = JSVAL_TO_INT(v);
+               }
+               if (JS_GetProperty(cx, o, "vdpi", &v) && !JSVAL_IS_VOID(v)) {
+                       if (!JSVAL_IS_INT(v)) {
+                               QUEUE_EXCEPTION("vdpi not an Integer");
+                               goto err;
+                       }
+                       ex.vdpi = JSVAL_TO_INT(v);
+               }
+       }
+
+       img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL);
+       assert(img != NULL);
+
+       if ((error = gdImageStringFTEx(img, brect, JSVAL_TO_INT(argv[0]), font,
+           (double)ptsize, (double)angle, JSVAL_TO_INT(argv[4]),
+           JSVAL_TO_INT(argv[5]), string, &ex)) != NULL) {
+               QUEUE_EXCEPTION(error);
+               goto err;
+       }
+
+       if ((o = JS_NewObject(cx, NULL, NULL, NULL)) == NULL)
+               goto err2;
+       *rval = OBJECT_TO_JSVAL(o);
+
+       if (ex.xshow != NULL) {
+               if ((str = JS_NewStringCopyZ(cx, ex.xshow)) == NULL)
+                       goto err2;
+               if (!JS_DefineProperty(cx, o, "xshow", STRING_TO_JSVAL(str),
+                   NULL, NULL, JSPROP_ENUMERATE))
+                       goto err2;
+               gdFree(ex.xshow);
+               ex.xshow = NULL;
+       }
+       if (ex.fontpath != NULL) {
+               if ((str = JS_NewStringCopyZ(cx, ex.fontpath)) == NULL)
+                       goto err2;
+               if (!JS_DefineProperty(cx, o, "fontpath", STRING_TO_JSVAL(str),
+                   NULL, NULL, JSPROP_ENUMERATE))
+                       goto err2;
+               gdFree(ex.fontpath);
+               ex.fontpath = NULL;
+       }
+
+       for (i = 0; i < 8; i++)
+               rect[i] = INT_TO_JSVAL(brect[i]);
+       if ((a = JS_NewArrayObject(cx, 8, rect)) == NULL)
+               goto err2;
+       if (!JS_DefineProperty(cx, o, "brect", OBJECT_TO_JSVAL(a), NULL, NULL,
+           JSPROP_ENUMERATE))
+               goto err2;
+
+       return JS_TRUE;
+
+err2:
+       QUEUE_EXCEPTION("Internal error!");
+err:
+       *rval = OBJECT_TO_JSVAL(NULL);
+       if (ex.xshow != NULL)
+               gdFree(ex.xshow);
+       if (ex.fontpath != NULL)
+               gdFree(ex.fontpath);
+
+       return JS_FALSE;
+}
+
+static JSBool
+gdimage_m_string_ft_circle(JSContext *cx, JSObject *obj, uintN argc,
+    jsval *argv, jsval *rval)
+{
+       gdImagePtr      img;
+       char            *font, *topstr, *botstr, *error;
+       jsdouble        radius, textradius, fillportion, ptsize;
+
+       *rval = OBJECT_TO_JSVAL(NULL);
+
+       if (argc != 10) {
+               QUEUE_EXCEPTION("Wrong number of arguments");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_INT(argv[0])) {
+               QUEUE_EXCEPTION("Argument 1 not an Integer");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_INT(argv[1])) {
+               QUEUE_EXCEPTION("Argument 2 not an Integer");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_NUMBER(argv[2]) ||
+           !JS_ValueToNumber(cx, argv[2], &radius)) {
+               QUEUE_EXCEPTION("Argument 3 not a Number");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_NUMBER(argv[3]) ||
+           !JS_ValueToNumber(cx, argv[3], &textradius)) {
+               QUEUE_EXCEPTION("Argument 4 not a Number");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_NUMBER(argv[4]) ||
+           !JS_ValueToNumber(cx, argv[4], &fillportion)) {
+               QUEUE_EXCEPTION("Argument 5 not a Number");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_STRING(argv[5]) ||
+           (font = JS_GetStringBytes(JSVAL_TO_STRING(argv[5]))) == NULL) {
+               QUEUE_EXCEPTION("Argument 6 not a String");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_NUMBER(argv[6]) ||
+           !JS_ValueToNumber(cx, argv[6], &ptsize)) {
+               QUEUE_EXCEPTION("Argument 7 not a Number");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_STRING(argv[7]) ||
+           (topstr = JS_GetStringBytes(JSVAL_TO_STRING(argv[7]))) == NULL) {
+               QUEUE_EXCEPTION("Argument 8 not a String");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_STRING(argv[8]) ||
+           (botstr = JS_GetStringBytes(JSVAL_TO_STRING(argv[8]))) == NULL) {
+               QUEUE_EXCEPTION("Argument 9 not a String");
+               return JS_FALSE;
+       }
+       if (!JSVAL_IS_INT(argv[9])) {
+               QUEUE_EXCEPTION("Argument 10 not an Integer");
+               return JS_FALSE;
+       }
+
+       img = JS_GetInstancePrivate(cx, obj, &gdimage_class, NULL);
+       assert(img != NULL);
+
+       if ((error = gdImageStringFTCircle(img, JSVAL_TO_INT(argv[0]),
+           JSVAL_TO_INT(argv[1]), (double)radius, (double)textradius,
+           (double)fillportion, font, (double)ptsize, topstr, botstr,
+           JSVAL_TO_INT(argv[9]))) != NULL) {
+               QUEUE_EXCEPTION(error);
+               return JS_FALSE;
+       }
+
+       return JS_TRUE;
+}
+
+
+static JSBool
 gdimage_m_copy(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
     jsval *rval)
 {
@@ -3724,3 +4261,97 @@ gdimage_m_interlace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
 
        return JS_TRUE;
 }
+
+
+/*
+ * GDFont object control
+ */
+
+JSObject *
+js_InitGDFontClass(JSContext *cx, JSObject *obj, JSObject *obj2)
+{
+       JSObject        *proto, *o;
+
+       if ((proto = JS_InitClass(cx, obj, NULL, &gdfont_class,
+           gdfont_constructor, 0, NULL, NULL, NULL, NULL)) == NULL)
+               return NULL;
+
+       /* Attach font objects statically to the global GD object */
+       if ((o = gdfont_new(cx, gdFontGetSmall())) != NULL)
+               JS_DefineProperty(cx, obj2, "gdFontSmall", OBJECT_TO_JSVAL(o),
+                   NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT |
+                   JSPROP_ENUMERATE);
+       if ((o = gdfont_new(cx, gdFontGetLarge())) != NULL)
+               JS_DefineProperty(cx, obj2, "gdFontLarge", OBJECT_TO_JSVAL(o),
+                   NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT |
+                   JSPROP_ENUMERATE);
+       if ((o = gdfont_new(cx, gdFontGetMediumBold())) != NULL)
+               JS_DefineProperty(cx, obj2, "gdFontMediumBold",
+                   OBJECT_TO_JSVAL(o), NULL, NULL,
+                   JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_ENUMERATE);
+       if ((o = gdfont_new(cx, gdFontGetGiant())) != NULL)
+               JS_DefineProperty(cx, obj2, "gdFontGiant", OBJECT_TO_JSVAL(o),
+                   NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT |
+                   JSPROP_ENUMERATE);
+       if ((o = gdfont_new(cx, gdFontGetTiny())) != NULL)
+               JS_DefineProperty(cx, obj2, "gdFontTiny", OBJECT_TO_JSVAL(o),
+                   NULL, NULL, JSPROP_READONLY | JSPROP_PERMANENT |
+                   JSPROP_ENUMERATE);
+
+       return proto;
+}
+
+static JSBool
+gdfont_constructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
+{
+
+       *rval = OBJECT_TO_JSVAL(NULL);
+       QUEUE_EXCEPTION("GDFont object not user-instanciable");
+
+       return JS_FALSE;
+}
+
+static void
+gdfont_finalize(JSContext *cx, JSObject *obj)
+{
+
+       if (JS_GetInstancePrivate(cx, obj, &gdfont_class, NULL) != NULL)
+               (void) JS_SetPrivate(cx, obj, NULL);
+}
+
+static JSObject *
+gdfont_new(JSContext *cx, gdFontPtr f)
+{
+       JSObject        *o;
+
+       if (f == NULL)
+               return NULL;
+
+       if ((o = JS_NewObject(cx, &gdfont_class, NULL, NULL)) == NULL)
+               return NULL;
+
+       if (!JS_SetPrivate(cx, o, f))
+               return NULL;
+
+       return o;
+}
+
+static gdFontPtr
+gdfont_get(JSContext *cx, jsval v)
+{
+       JSObject        *o;
+       gdFontPtr       f;
+
+       if (!JSVAL_IS_OBJECT(v))
+               return NULL;
+       o = JSVAL_TO_OBJECT(v);
+
+       if (!JS_InstanceOf(cx, o, &gdfont_class, NULL))
+               return NULL;
+
+       f = JS_GetInstancePrivate(cx, o, &gdfont_class, NULL);
+       assert(f != NULL);
+
+       return f;
+}