+#define forward(v,id,args...) rb_respond_to((v), (id))?rb_funcall((v), (id), args):0
+
+VALUE convert_line(gfxline_t*line)
+{
+ int len = 0;
+ gfxline_t*l = line;
+ while(l) {l=l->next;len++;}
+
+ VALUE*a = malloc(sizeof(VALUE)*len);
+ int pos = 0;
+ l = line;
+ while(l) {
+ if(l->type == gfx_moveTo) {
+ a[pos] = rb_ary_new3(3, ID2SYM(id_move), rb_float_new(l->x), rb_float_new(l->y));
+ } else if(l->type == gfx_lineTo) {
+ a[pos] = rb_ary_new3(3, ID2SYM(id_line), rb_float_new(l->x), rb_float_new(l->y));
+ } else {
+ a[pos] = rb_ary_new3(5, ID2SYM(id_spline), rb_float_new(l->x), rb_float_new(l->y), rb_float_new(l->sx), rb_float_new(l->sy));
+ }
+ pos++;
+ l=l->next;
+ }
+ return rb_ary_new4(len, a);
+}
+VALUE convert_color(gfxcolor_t*color)
+{
+ return rb_ary_new3(4, INT2FIX(color->a), INT2FIX(color->r), INT2FIX(color->g), INT2FIX(color->b));
+}
+VALUE convert_matrix(gfxmatrix_t*matrix)
+{
+ return rb_ary_new3(3,
+ rb_ary_new3(2, rb_float_new(matrix->m00), rb_float_new(matrix->m01)),
+ rb_ary_new3(2, rb_float_new(matrix->m10), rb_float_new(matrix->m11)),
+ rb_ary_new3(2, rb_float_new(matrix->tx), rb_float_new(matrix->ty)));
+}
+int rb_setparameter(gfxdevice_t*dev, const char*key, const char*value)
+{
+ VALUE v = (VALUE)(ptroff_t)dev->internal;
+ VALUE ret = forward(v,id_setparameter,2,rb_tainted_str_new2(key),rb_tainted_str_new2(value));
+ return 0;
+}
+void rb_startpage(gfxdevice_t*dev, int width, int height)
+{
+ VALUE v = (VALUE)(ptroff_t)dev->internal;
+ VALUE ret = forward(v,id_startpage,2,INT2FIX(width),INT2FIX(height));
+}
+void rb_startclip(gfxdevice_t*dev, gfxline_t*line)
+{
+ VALUE v = (VALUE)(ptroff_t)dev->internal;
+ VALUE ret = forward(v,id_startclip,1,convert_line(line));
+}
+void rb_endclip(gfxdevice_t*dev)
+{
+ VALUE v = (VALUE)(ptroff_t)dev->internal;
+ VALUE ret = forward(v,id_endclip,0);
+}
+void rb_stroke(gfxdevice_t*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit)
+{
+ VALUE v = (VALUE)(ptroff_t)dev->internal;
+
+ ID cap = 0;
+ if(cap_style == gfx_capButt) cap = id_butt;
+ else if(cap_style == gfx_capRound) cap = id_round;
+ else if(cap_style == gfx_capSquare) cap = id_square;
+
+ ID joint = 0;
+ if(joint_style == gfx_joinRound) joint = id_round;
+ else if(joint_style == gfx_joinMiter) joint = id_miter;
+ else if(joint_style == gfx_joinBevel) joint = id_bevel;
+
+ forward(v, id_stroke, 6, convert_line(line), rb_float_new(width), convert_color(color), ID2SYM(cap), ID2SYM(joint), rb_float_new(miterLimit));
+}
+void rb_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
+{
+ VALUE v = (VALUE)(ptroff_t)dev->internal;
+ forward(v, id_fill, 2, convert_line(line), convert_color(color));
+}
+void rb_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
+{
+ VALUE v = (VALUE)(ptroff_t)dev->internal;
+ VALUE image = convert_image(img);
+ forward(v, id_fillbitmap, 4, convert_line(line), image, convert_matrix(matrix), Qnil);
+ invalidate_image(image);
+}
+void rb_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
+{
+ VALUE v = (VALUE)(ptroff_t)dev->internal;
+ ID typeid = (type == gfxgradient_linear)? id_linear : id_radial;
+ forward(v, id_fillgradient, 4, convert_line(line), convert_gradient(gradient), ID2SYM(typeid), convert_matrix(matrix));
+}
+void rb_addfont(gfxdevice_t*dev, gfxfont_t*font)
+{
+ VALUE v = (VALUE)(ptroff_t)dev->internal;
+ //forward(v, id_addfont, 1, convert_font(font));
+}
+void rb_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix)
+{
+ VALUE v = (VALUE)(ptroff_t)dev->internal;
+ //forward(v, id_drawchar, 1, convert_font(font));
+}
+void rb_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action)
+{
+ VALUE v = (VALUE)(ptroff_t)dev->internal;
+ forward(v, id_drawlink, convert_line(line), rb_tainted_str_new2(action));
+}
+void rb_endpage(gfxdevice_t*dev)
+{
+ VALUE v = (VALUE)(ptroff_t)dev->internal;
+ forward(v, id_endpage, 0);
+}
+gfxresult_t* rb_finish(gfxdevice_t*dev)
+{
+ VALUE v = (VALUE)(ptroff_t)dev->internal;
+ VALUE ret = forward(v, id_endpage, 0);
+ gfxresult_t*r = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
+ r->internal = (void*)(ptroff_t)ret;
+ return r;
+}
+
+static VALUE page_render(VALUE cls, VALUE device)
+{
+ Check_Type(device, T_OBJECT);
+ Get_Page(page,cls)
+
+ gfxdevice_t dev;
+ dev.internal = (void*)(ptroff_t)device;
+ dev.setparameter = rb_setparameter;
+ dev.startpage = rb_startpage;
+ dev.startclip = rb_startclip;
+ dev.endclip = rb_endclip;
+ dev.stroke = rb_stroke;
+ dev.fill = rb_fill;
+ dev.fillbitmap = rb_fillbitmap;
+ dev.fillgradient = rb_fillgradient;
+ dev.addfont = rb_addfont;
+ dev.drawchar = rb_drawchar;
+ dev.drawlink = rb_drawlink;
+ dev.endpage = rb_endpage;
+ dev.finish = rb_finish;
+
+ page->page->render(page->page, &dev);
+
+ return cls;
+}
+
+