seed random from ruby interface
[swftools.git] / lib / ruby / gfx.c
1 #include <ruby.h>
2 #include "../../config.h"
3 #include "../gfxdevice.h"
4 #include "../gfxsource.h"
5 #include "../gfxtools.h"
6 #include "../gfximage.h"
7 #include "../gfxfont.h"
8 #include "../gfxfilter.h"
9 #include "../devices/pdf.h"
10 #include "../readers/swf.h"
11 #include "../readers/image.h"
12 #include "../pdf/pdf.h"
13 #include "../mem.h"
14 #include "../types.h"
15 #include "../log.h"
16 #include "../args.h"
17
18 #define RUBY_GFX_VERSION  "0.9.0"
19
20 static VALUE GFX;
21 static VALUE Font, Glyph, Bitmap, Document, DocumentPage, PDFClass, SWFClass, ImageClass, Device;
22 static ID id_doc;
23
24 typedef struct doc_internal {
25     VALUE self;
26     gfxsource_t*driver; // filled by alloc
27     gfxdocument_t*doc;
28     gfxfontlist_t*fontlist;
29 } doc_internal_t;
30
31 typedef struct page_internal {
32     doc_internal_t*doc;
33     gfxpage_t*page;
34 } page_internal_t;
35
36 typedef struct image_internal {
37     doc_internal_t*doc;
38     gfximage_t*image;
39 } image_internal_t;
40
41 typedef struct font_internal {
42     VALUE self;
43     VALUE glyph_array;
44     gfxfont_t*font;
45 } font_internal_t;
46
47 typedef struct glyph_internal {
48     font_internal_t*font;
49     int nr;
50 } glyph_internal_t;
51
52 static gfxsource_t* pdfdriver = 0;
53 static gfxsource_t* imagedriver = 0;
54 static gfxsource_t* swfdriver = 0;
55
56 #define Get_Doc(doc,cls) doc_internal_t*doc=0;Data_Get_Struct(cls, doc_internal_t, doc);
57 #define Get_Page(page,cls) page_internal_t*page=0;Data_Get_Struct(cls, page_internal_t, page);
58
59 static VALUE doc_allocate(VALUE cls, gfxsource_t*driver);
60 static VALUE page_allocate(VALUE cls);
61
62 // ------------------------ documents ---------------------------------------
63
64 static VALUE doc_initialize(VALUE cls, VALUE _filename)
65 {
66     Check_Type(_filename, T_STRING);
67     Get_Doc(doc,cls);
68     const char*filename = StringValuePtr(_filename);
69     doc->fontlist = gfxfontlist_create();
70     doc->doc = pdfdriver->open(pdfdriver, filename);
71     if(!doc->doc) {
72         rb_raise(rb_eIOError, "couldn't open %s", filename);
73     }
74     return cls;
75 }
76
77 static VALUE doc_num_pages(VALUE cls)
78 {
79     Get_Doc(doc,cls)
80     return INT2FIX(doc->doc->num_pages);
81 }
82
83 static VALUE doc_get_page(VALUE cls, VALUE _nr)
84 {
85     Check_Type(_nr, T_FIXNUM);
86     int nr = FIX2INT(_nr);
87     Get_Doc(doc,cls);
88
89     VALUE v = page_allocate(DocumentPage);
90     Get_Page(page,v)
91     page->page = doc->doc->getpage(doc->doc, nr);
92     page->doc = doc;
93     if(!page->page) {
94         rb_raise(rb_eArgError, "No page %d in document", nr);
95         return Qnil;
96     }
97     return v;
98 }
99
100 static VALUE doc_each_page(VALUE cls)
101 {
102     Get_Doc(doc,cls);
103     int t;
104     for(t=1;t<=doc->doc->num_pages;t++) {
105         VALUE v = page_allocate(DocumentPage);
106         Get_Page(page,v)
107         page->page = doc->doc->getpage(doc->doc, t);
108         page->doc = doc;
109         rb_yield(v);
110     }
111     return cls;
112 }
113
114 static void doc_mark(doc_internal_t*doc)
115 {
116     gfxfontlist_t*l = doc->fontlist;
117     while(l) {
118         if(l->user) 
119             rb_gc_mark((VALUE)l->user);
120         l = l->next;
121     }
122 }
123
124 static void doc_free(doc_internal_t*doc)
125 {
126     gfxfontlist_free(doc->fontlist, 0);
127     if(doc->doc) {
128         doc->doc->destroy(doc->doc);
129     }
130     doc->doc = 0;
131     free(doc);
132 }
133
134 static VALUE doc_allocate(VALUE cls, gfxsource_t*driver)
135 {
136     doc_internal_t*doc = 0;
137     VALUE v = Data_Make_Struct(cls, doc_internal_t, doc_mark, doc_free, doc);
138     doc->self = v;
139     memset(doc, 0, sizeof(doc_internal_t));
140     doc->driver = driver;
141     return v;
142 }
143
144 static VALUE pdf_allocate(VALUE cls) {return doc_allocate(cls, pdfdriver);}
145 static VALUE swf_allocate(VALUE cls) {return doc_allocate(cls, swfdriver);}
146 static VALUE imgdrv_allocate(VALUE cls) {return doc_allocate(cls, imagedriver);}
147
148 // ------------------------ doc pages ---------------------------------------
149
150 static void page_free(page_internal_t*page)
151 {
152     if(!page) return;
153     if(page->page) {
154         page->page->destroy(page->page);
155         page->page = 0;
156     }
157     free(page);
158 }
159 static void page_mark(page_internal_t*page)
160 {
161     rb_gc_mark(page->doc->self);
162 }
163 static VALUE page_allocate(VALUE cls)
164 {
165     page_internal_t*page = 0;
166     VALUE v = Data_Make_Struct(cls, page_internal_t, page_mark, page_free, page);
167     memset(page, 0, sizeof(page_internal_t));
168     return v;
169 }
170 static VALUE page_nr(VALUE cls)
171 {
172     Get_Page(page,cls)
173     return INT2FIX(page->page->nr);
174 }
175 static VALUE page_width(VALUE cls)
176 {
177     Get_Page(page,cls)
178     return INT2FIX(page->page->width);
179 }
180 static VALUE page_height(VALUE cls)
181 {
182     Get_Page(page,cls)
183     return INT2FIX(page->page->height);
184 }
185
186 // ------------------------ image -------------------------------------------
187
188 #define Get_Image(image,cls) image_internal_t*image=0;Data_Get_Struct(cls, image_internal_t, image);
189
190 static void image_free(image_internal_t*image)
191 {
192     free(image);
193 }
194 static void image_mark(image_internal_t*image)
195 {
196     rb_gc_mark(image->doc->self);
197 }
198 static VALUE image_allocate(VALUE cls)
199 {
200     image_internal_t*image = 0;
201     VALUE v = Data_Make_Struct(cls, image_internal_t, image_mark, image_free, image);
202     memset(image, 0, sizeof(image_internal_t));
203     return v;
204 }
205 static VALUE image_width(VALUE cls)
206 {
207     Get_Image(image,cls)
208     return INT2FIX(image->image->width);
209 }
210 static VALUE image_height(VALUE cls)
211 {
212     Get_Image(image,cls)
213     return INT2FIX(image->image->height);
214 }
215 static VALUE image_rescale(VALUE cls, VALUE _width, VALUE _height)
216 {
217     Get_Image(image,cls)
218     Check_Type(_width, T_FIXNUM);
219     Check_Type(_height, T_FIXNUM);
220     int width = FIX2INT(_width);
221     int height = FIX2INT(_height);
222     volatile VALUE v_image2 = image_allocate(Bitmap);
223     Get_Image(image2,v_image2)
224     image2->doc = image->doc;
225     image2->image = gfximage_rescale(image->image, width, height);
226     if(!image2->image) {
227         rb_raise(rb_eArgError, "Can't rescale to size %dx%d", width, height);
228     }
229     return v_image2;
230 }
231 static VALUE image_has_alpha(VALUE cls)
232 {
233     Get_Image(image,cls)
234     int size = image->image->width * image->image->height;
235     gfxcolor_t*data = image->image->data;
236     int t;
237     for(t=0;t<size;t++) {
238         if(data[t].a!=255) 
239             return Qtrue;
240     }
241     return Qfalse;
242 }
243 static VALUE image_save_jpeg(VALUE cls, VALUE _filename, VALUE quality)
244 {
245     Get_Image(image,cls)
246     Check_Type(_filename, T_STRING);
247     Check_Type(quality, T_FIXNUM);
248     const char*filename = StringValuePtr(_filename);
249     gfximage_save_jpeg(image->image, filename, FIX2INT(quality));
250     return cls;
251 }
252 static VALUE image_save_png(VALUE cls, VALUE _filename)
253 {
254     Get_Image(image,cls)
255     Check_Type(_filename, T_STRING);
256     const char*filename = StringValuePtr(_filename);
257     gfximage_save_png(image->image, filename);
258     return cls;
259 }
260 VALUE convert_image(doc_internal_t*doc,gfximage_t*_image)
261 {
262     VALUE v = image_allocate(Bitmap);
263     Get_Image(image,v)
264     image->image = _image;
265     image->doc = doc;
266     return v;
267 }
268 void invalidate_image(VALUE v)
269 {
270     Get_Image(image,v)
271     image->image = 0;
272 }
273
274 // ------------------------ glyphs ------------------------------------------
275
276 static VALUE convert_line(gfxline_t*line);
277
278 #define Get_Glyph(glyph,cls) glyph_internal_t*glyph=0;Data_Get_Struct(cls, glyph_internal_t, glyph);
279
280 static void glyph_free(glyph_internal_t*glyph)
281 {
282     free(glyph);
283 }
284
285 static void glyph_mark(glyph_internal_t*glyph)
286 {
287     rb_gc_mark(glyph->font->self);
288 }
289
290 static VALUE glyph_allocate(VALUE cls)
291 {
292     glyph_internal_t*glyph = 0;
293     VALUE v = Data_Make_Struct(cls, glyph_internal_t, glyph_mark, glyph_free, glyph);
294     memset(glyph, 0, sizeof(glyph_internal_t));
295     return v;
296 }
297
298 static VALUE glyph_polygon(VALUE cls)
299 {
300     Get_Glyph(glyph,cls);
301     return convert_line(glyph->font->font->glyphs[glyph->nr].line);
302 }
303
304 static VALUE glyph_advance(VALUE cls)
305 {
306     Get_Glyph(glyph,cls);
307     return rb_float_new(glyph->font->font->glyphs[glyph->nr].advance);
308 }
309
310 static VALUE glyph_bbox(VALUE cls)
311 {
312     Get_Glyph(glyph,cls);
313     gfxbbox_t bbox = gfxline_getbbox(glyph->font->font->glyphs[glyph->nr].line);
314     return rb_ary_new3(4, rb_float_new(bbox.xmin), 
315                           rb_float_new(bbox.ymin), 
316                           rb_float_new(bbox.xmax), 
317                           rb_float_new(bbox.ymax));
318 }
319
320 static VALUE glyph_unicode(VALUE cls)
321 {
322     Get_Glyph(glyph,cls);
323     return INT2FIX(glyph->font->font->glyphs[glyph->nr].unicode);
324 }
325
326 // ------------------------ font --------------------------------------------
327
328 #define Get_Font(font,cls) font_internal_t*font=0;Data_Get_Struct(cls, font_internal_t, font);
329
330 static void font_mark(font_internal_t*font)
331 {
332     rb_gc_mark(font->glyph_array);
333 }
334
335 static void font_free(font_internal_t*font)
336 {
337     free(font);
338 }
339
340 static VALUE font_allocate(VALUE cls)
341 {
342     font_internal_t*font = 0;
343     VALUE v = Data_Make_Struct(cls, font_internal_t, font_mark, font_free, font);
344     memset(font, 0, sizeof(font_internal_t));
345     font->self = v;
346     return v;
347 }
348
349 static VALUE font_ascent(VALUE cls)
350 {
351     Get_Font(font,cls);
352     return rb_float_new(font->font->ascent);
353 }
354
355 static VALUE font_descent(VALUE cls)
356 {
357     Get_Font(font,cls);
358     return rb_float_new(font->font->descent);
359 }
360
361 static VALUE font_name(VALUE cls)
362 {
363     Get_Font(font,cls);
364     return rb_tainted_str_new2(font->font->id);
365 }
366
367 static VALUE font_glyphs(VALUE cls)
368 {
369     Get_Font(font,cls);
370     return font->glyph_array;
371 }
372
373 static VALUE font_save_ttf(VALUE cls, VALUE _filename)
374 {
375     Get_Font(font,cls);
376     Check_Type(_filename, T_STRING);
377     const char*filename = StringValuePtr(_filename);
378     gfxfont_save(font->font, filename);
379     return Qnil;
380 }
381
382 static VALUE font_save_eot(VALUE cls, VALUE _filename)
383 {
384     Get_Font(font,cls);
385     Check_Type(_filename, T_STRING);
386     const char*filename = StringValuePtr(_filename);
387     gfxfont_save_eot(font->font, filename);
388     return Qnil;
389 }
390
391 static VALUE font_kerning(VALUE cls)
392 {
393     Get_Font(font,cls);
394     gfxkerning_t*kerning = font->font->kerning;
395     int kerning_size = font->font->kerning_size;
396     volatile VALUE a = rb_ary_new2(kerning_size);
397     int t;
398     for(t=0;t<kerning_size;t++) {
399         volatile VALUE tuple = rb_ary_new2(3);
400         rb_ary_store(tuple, 0, INT2FIX(kerning[t].c1));
401         rb_ary_store(tuple, 1, INT2FIX(kerning[t].c2));
402         rb_ary_store(tuple, 2, INT2FIX(kerning[t].advance));
403         rb_ary_store(a, t, tuple);
404     }
405     return a;
406 }
407
408 // ------------------------ gfx device --------------------------------------
409
410 typedef struct device_internal {
411     doc_internal_t*doc;
412     VALUE v;
413 } device_internal_t;
414
415 static ID id_setparameter = 0;
416 static ID id_startpage = 0;
417 static ID id_startclip = 0;
418 static ID id_endclip = 0;
419 static ID id_stroke = 0;
420 static ID id_fill = 0;
421 static ID id_fillbitmap = 0;
422 static ID id_fillgradient = 0;
423 static ID id_addfont = 0;
424 static ID id_drawchar = 0;
425 static ID id_drawlink = 0;
426 static ID id_endpage = 0;
427 static ID id_geterror = 0;
428 static ID id_finish = 0;
429 static ID id_butt = 0;
430 static ID id_round = 0;
431 static ID id_square = 0;
432 static ID id_bevel = 0;
433 static ID id_miter = 0;
434 static ID id_move = 0;
435 static ID id_line = 0;
436 static ID id_spline = 0;
437 static ID id_radial = 0;
438 static ID id_linear = 0;
439 static ID id_remove_font_transforms = 0;
440 static ID id_maketransparent = 0;
441 static ID id_vectors_to_glyphs = 0;
442
443 static VALUE noop(int argc, VALUE *argv, VALUE obj) {return obj;}
444
445 #define forward(v,id,args...) rb_respond_to((v), (id))?rb_funcall((v), (id), args):0
446
447 VALUE convert_line(gfxline_t*line)
448 {
449     int len = 0;
450     gfxline_t*l = line;
451     while(l) {l=l->next;len++;}
452
453     volatile VALUE array = rb_ary_new2(len);
454
455     int pos = 0;
456     l = line;
457     while(l) {
458         volatile VALUE e;
459         if(l->type == gfx_moveTo) {
460             e = rb_ary_new3(3, ID2SYM(id_move), Qfalse, Qfalse);
461             rb_ary_store(array, pos, e);
462             rb_ary_store(e, 1, rb_float_new(l->x));
463             rb_ary_store(e, 2, rb_float_new(l->y));
464         } else if(l->type == gfx_lineTo) {
465             e = rb_ary_new3(3, ID2SYM(id_line), Qfalse, Qfalse);
466             rb_ary_store(array, pos, e);
467             rb_ary_store(e, 1, rb_float_new(l->x));
468             rb_ary_store(e, 2, rb_float_new(l->y));
469         } else {
470             e = rb_ary_new3(5, ID2SYM(id_spline), Qfalse, Qfalse, Qfalse, Qfalse);
471             rb_ary_store(array, pos, e);
472             rb_ary_store(e, 1, rb_float_new(l->x));
473             rb_ary_store(e, 2, rb_float_new(l->y));
474             rb_ary_store(e, 3, rb_float_new(l->sx));
475             rb_ary_store(e, 4, rb_float_new(l->sy));
476         }
477         pos++;
478         l=l->next;
479     }
480     return array;
481 }
482 VALUE convert_color(gfxcolor_t*color)
483 {
484     return rb_ary_new3(4, INT2FIX(color->a), INT2FIX(color->r), INT2FIX(color->g), INT2FIX(color->b));
485 }
486 VALUE convert_matrix(gfxmatrix_t*matrix)
487 {
488     volatile VALUE array = rb_ary_new2(3);
489     volatile VALUE a = rb_ary_new2(2);
490     rb_ary_store(array, 0, a);
491     rb_ary_store(a, 0, rb_float_new(matrix->m00));
492     rb_ary_store(a, 1, rb_float_new(matrix->m01));
493     a = rb_ary_new2(2);
494     rb_ary_store(array, 1, a);
495     rb_ary_store(a, 0, rb_float_new(matrix->m10));
496     rb_ary_store(a, 1, rb_float_new(matrix->m11));
497     a = rb_ary_new2(2);
498     rb_ary_store(array, 2, a);
499     rb_ary_store(a, 0, rb_float_new(matrix->tx));
500     rb_ary_store(a, 1, rb_float_new(matrix->ty));
501     return array;
502 }
503 static VALUE font_is_cached(device_internal_t*i, gfxfont_t*font)
504 {
505     return (VALUE)gfxfontlist_getuserdata(i->doc->fontlist, font->id);
506 }
507 static void cache_font(device_internal_t*i, gfxfont_t*font, VALUE v)
508 {
509     i->doc->fontlist = gfxfontlist_addfont2(i->doc->fontlist, font, (void*)v);
510 }
511 static VALUE convert_font(gfxfont_t*font)
512 {
513     volatile VALUE v2 = font_allocate(Font);
514     Get_Font(f, v2);
515     f->font = font;
516     f->glyph_array = rb_ary_new2(font->num_glyphs);
517
518     int t;
519     for(t=0;t<font->num_glyphs;t++) {
520         volatile VALUE a = glyph_allocate(Glyph);
521         rb_ary_store(f->glyph_array, t, a);
522         Get_Glyph(g, a);
523         g->font = f;
524         g->nr = t;
525     }
526     return v2;
527 }
528 static VALUE convert_gradient(gfxgradient_t*gradient)
529 {
530     return Qnil; //TODO
531 }
532 #define HEAD \
533     device_internal_t*i = (device_internal_t*)dev->internal; \
534     VALUE v = i->v;
535 int rb_setparameter(gfxdevice_t*dev, const char*key, const char*value)
536 {
537     HEAD
538     volatile VALUE v_key = rb_tainted_str_new2(key);
539     volatile VALUE v_value = rb_tainted_str_new2(value);
540     VALUE ret = forward(v,id_setparameter,2,v_key,v_value);
541     return 0;
542 }
543 void rb_startpage(gfxdevice_t*dev, int width, int height)
544 {
545     HEAD
546     VALUE ret = forward(v,id_startpage,2,INT2FIX(width),INT2FIX(height));
547 }
548 void rb_startclip(gfxdevice_t*dev, gfxline_t*line)
549 {
550     HEAD
551     volatile VALUE v_line = convert_line(line);
552     VALUE ret = forward(v,id_startclip,1,v_line);
553 }
554 void rb_endclip(gfxdevice_t*dev)
555 {
556     HEAD
557     VALUE ret = forward(v,id_endclip,0);
558 }
559 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)
560 {
561     HEAD
562     
563     ID cap = 0;
564     if(cap_style == gfx_capButt) cap = id_butt;
565     else if(cap_style == gfx_capRound) cap = id_round;
566     else if(cap_style == gfx_capSquare) cap = id_square;
567     
568     ID joint = 0;
569     if(joint_style == gfx_joinRound) joint = id_round;
570     else if(joint_style == gfx_joinMiter) joint = id_miter;
571     else if(joint_style == gfx_joinBevel) joint = id_bevel;
572
573     volatile VALUE v_line = convert_line(line);
574     volatile VALUE v_width = rb_float_new(width);
575     volatile VALUE v_color = convert_color(color);
576     volatile VALUE v_miter = rb_float_new(miterLimit);
577     forward(v, id_stroke, 6, v_line, v_width, v_color, ID2SYM(cap), ID2SYM(joint), v_miter);
578 }
579 void rb_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
580 {
581     HEAD
582     
583     volatile VALUE v_line = convert_line(line);
584     volatile VALUE v_color = convert_color(color);
585     forward(v, id_fill, 2, v_line, v_color);
586 }
587 void rb_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
588 {
589     HEAD
590     volatile VALUE v_image = convert_image(i->doc, img);
591     volatile VALUE v_line = convert_line(line);
592     volatile VALUE v_matrix = convert_matrix(matrix);
593     forward(v, id_fillbitmap, 4, v_line, v_image, v_matrix, Qnil);
594     invalidate_image(v_image);
595 }
596 void rb_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
597 {
598     HEAD
599     ID typeid = (type == gfxgradient_linear)? id_linear : id_radial;
600     
601     volatile VALUE v_line = convert_line(line);
602     volatile VALUE v_matrix = convert_matrix(matrix);
603     volatile VALUE v_gradient = convert_gradient(gradient);
604     forward(v, id_fillgradient, 4, v_line, v_gradient, ID2SYM(typeid), v_matrix);
605 }
606 void rb_addfont(gfxdevice_t*dev, gfxfont_t*font)
607 {
608     HEAD
609
610     volatile VALUE f = font_is_cached(i, font);
611     if(!f) {f=convert_font(font);cache_font(i,font,f);}
612
613     forward(v, id_addfont, 1, f);
614 }
615 void rb_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix)
616 {
617     HEAD
618     volatile VALUE f = font_is_cached(i, font);
619     if(!f) {f=convert_font(font);cache_font(i,font,f);}
620
621     volatile VALUE v_color = convert_color(color);
622     volatile VALUE v_matrix = convert_matrix(matrix);
623     forward(v, id_drawchar, 4, f, INT2FIX(glyphnr), v_color, v_matrix);
624 }
625 void rb_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action)
626 {
627     HEAD
628     volatile VALUE v_line = convert_line(line);
629     volatile VALUE v_action = rb_tainted_str_new2(action);
630
631     forward(v, id_drawlink, 2, v_line, v_action);
632 }
633 void rb_endpage(gfxdevice_t*dev)
634 {
635     HEAD
636     forward(v, id_endpage, 0);
637 }
638 void gfxresult_rb_destroy(gfxresult_t*r)
639 {
640     free(r);
641 }
642 gfxresult_t* rb_finish(gfxdevice_t*dev)
643 {
644     HEAD
645     VALUE ret = forward(v, id_finish, 0);
646     gfxresult_t*r = (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
647     r->destroy = gfxresult_rb_destroy;
648     r->internal = (void*)(ptroff_t)ret;
649     return r;
650 }
651
652 #define make_device(dev, idoc, device) \
653     gfxdevice_t dev; \
654     device_internal_t i; \
655     i.v = device; \
656     i.doc = idoc; \
657     dev.internal = &i; \
658     dev.setparameter = rb_setparameter; \
659     dev.startpage = rb_startpage; \
660     dev.startclip = rb_startclip; \
661     dev.endclip = rb_endclip; \
662     dev.stroke = rb_stroke; \
663     dev.fill = rb_fill; \
664     dev.fillbitmap = rb_fillbitmap; \
665     dev.fillgradient = rb_fillgradient; \
666     dev.addfont = rb_addfont; \
667     dev.drawchar = rb_drawchar; \
668     dev.drawlink = rb_drawlink; \
669     dev.endpage = rb_endpage; \
670     dev.finish = rb_finish;
671
672 static VALUE page_render(VALUE cls, VALUE device)
673 {
674     Check_Type(device, T_OBJECT);
675     Get_Page(page,cls)
676
677     make_device(dev, page->doc, device);
678
679     dev.startpage(&dev, page->page->width, page->page->height);
680     page->page->render(page->page, &dev);
681     dev.endpage(&dev);
682
683     return cls;
684 }
685
686 static VALUE doc_render(VALUE cls, VALUE device, VALUE _range, VALUE filters)
687 {
688     const char*range = 0;
689     if(!NIL_P(_range)) {
690         Check_Type(_range, T_STRING);
691         range = StringValuePtr(_range);
692     }
693     Get_Doc(doc,cls);
694
695     make_device(_dev, doc, device);
696     gfxdevice_t*dev = &_dev;
697
698     if(!NIL_P(filters)) {
699         if(TYPE(filters) != T_ARRAY)
700             rb_raise(rb_eArgError, "third argument of doc->render must be an array of symbols");
701
702         int len = RARRAY(filters)->len;
703         int t=0;
704         while(t<len) {
705             VALUE filter = RARRAY(filters)->ptr[t++];
706             Check_Type(filter, T_SYMBOL);
707             ID id = SYM2ID(filter);
708 #           define PARAM(x) VALUE x;if(t==len) rb_raise(rb_eArgError, "End of array while parsing arguments for filter %s", rb_id2name(id)); \
709                             else x = RARRAY(filters)->ptr[t++];
710             if(id == id_remove_font_transforms) {
711                 wrap_filter2(dev, remove_font_transforms);
712             } else if(id == id_vectors_to_glyphs) {
713                 wrap_filter2(dev, vectors_to_glyphs);
714             } else if(id == id_maketransparent) {
715                 PARAM(alpha);
716                 wrap_filter(dev, maketransparent, FIX2INT(alpha));
717             } else {
718                 rb_raise(rb_eArgError, "unknown filter %s", rb_id2name(id));
719             }
720         }
721     }
722
723     int pagenr;
724     for(pagenr=1;pagenr<=doc->doc->num_pages;pagenr++) {
725         if(is_in_range(pagenr, (char*)range)) {
726             gfxpage_t*page = doc->doc->getpage(doc->doc, pagenr);
727             dev->startpage(dev, page->width, page->height);
728             page->render(page, dev);
729             dev->endpage(dev);
730             page->destroy(page);
731         }
732     }
733    
734
735     gfxresult_t*r = dev->finish(dev);
736     r->destroy(r);
737
738     return Qnil;
739 }
740
741 static VALUE doc_prepare(VALUE cls, VALUE device)
742 {
743     Get_Doc(doc,cls);
744     make_device(dev, doc, device);
745     doc->doc->prepare(doc->doc, &dev);
746     return cls;
747 }
748
749
750 // ---------------------- global functions ----------------------------------
751
752 VALUE gfx_setparameter(VALUE module, VALUE _key, VALUE _value)
753 {
754     Check_Type(_key, T_STRING);
755     Check_Type(_value, T_STRING);
756     const char*key = StringValuePtr(_key);
757     const char*value = StringValuePtr(_value);
758     pdfdriver->setparameter(pdfdriver, key, value);
759     swfdriver->setparameter(swfdriver, key, value);
760     imagedriver->setparameter(imagedriver, key, value);
761     return GFX;
762 }
763
764 // --------------------------------------------------------------------------
765
766 void Init_gfx()
767 {
768     initLog(0,0,0,0,0,2);
769 #ifdef HAVE_SRAND48
770     srand48(time(0));
771 #else
772 #ifdef HAVE_SRAND
773     srand(time(0));
774 #endif
775 #endif
776
777     pdfdriver = gfxsource_pdf_create();
778     swfdriver = gfxsource_swf_create();
779     imagedriver = gfxsource_image_create();
780
781     GFX = rb_define_module("GFX");
782     rb_define_const(GFX, "VERSION", INT2FIX(20100309));
783     
784     rb_define_module_function(GFX, "setparameter", gfx_setparameter, 2);
785     
786     DocumentPage = rb_define_class_under(GFX, "DocumentPage", rb_cObject);
787     rb_define_method(DocumentPage, "width", page_width, 0);
788     rb_define_method(DocumentPage, "height", page_height, 0);
789     rb_define_method(DocumentPage, "nr", page_nr, 0);
790     rb_define_method(DocumentPage, "render", page_render, 1);
791
792     Document = rb_define_class_under(GFX, "Document", rb_cObject);
793     rb_define_method(Document, "initialize", doc_initialize, 1);
794     rb_define_method(Document, "page", doc_get_page, 1);
795     rb_define_method(Document, "each_page", doc_each_page, 0);
796     rb_define_method(Document, "prepare", doc_prepare, 1);
797     rb_define_method(Document, "render", doc_render, 3);
798     
799     Bitmap = rb_define_class_under(GFX, "Bitmap", rb_cObject);
800     rb_define_method(Bitmap, "save_jpeg", image_save_jpeg, 2);
801     rb_define_method(Bitmap, "save_png", image_save_png, 1);
802     rb_define_method(Bitmap, "width", image_width, 0);
803     rb_define_method(Bitmap, "height", image_height, 0);
804     rb_define_method(Bitmap, "rescale", image_rescale, 2);
805     rb_define_method(Bitmap, "has_alpha", image_has_alpha, 0);
806     
807     Glyph = rb_define_class_under(GFX, "Glyph", rb_cObject);
808     rb_define_method(Glyph, "polygon", glyph_polygon, 0);
809     rb_define_method(Glyph, "unicode", glyph_unicode, 0);
810     rb_define_method(Glyph, "advance", glyph_advance, 0);
811     rb_define_method(Glyph, "bbox", glyph_bbox, 0);
812     
813     Font = rb_define_class_under(GFX, "Font", rb_cObject);
814     rb_define_method(Font, "name", font_name, 0);
815     rb_define_method(Font, "ascent", font_ascent, 0);
816     rb_define_method(Font, "descent", font_descent, 0);
817     rb_define_method(Font, "glyphs", font_glyphs, 0);
818     rb_define_method(Font, "kerning", font_kerning, 0);
819     rb_define_method(Font, "get_kerning_table", font_kerning, 0);
820     rb_define_method(Font, "save_ttf", font_save_ttf, 1);
821     rb_define_method(Font, "save_eot", font_save_eot, 1);
822     
823     Device = rb_define_class_under(GFX, "Device", rb_cObject);
824     rb_define_method(Device, "startpage", noop, -1);
825     rb_define_method(Device, "endpage", noop, -1);
826     rb_define_method(Device, "startclip", noop, -1);
827     rb_define_method(Device, "endclip", noop, -1);
828     rb_define_method(Device, "stroke", noop, -1);
829     rb_define_method(Device, "fill", noop, -1);
830     rb_define_method(Device, "fillbitmap", noop, -1);
831     rb_define_method(Device, "fillgradient", noop, -1);
832     rb_define_method(Device, "addfont", noop, -1);
833     rb_define_method(Device, "drawchar", noop, -1);
834     rb_define_method(Device, "drawlink", noop, -1);
835
836     PDFClass = rb_define_class_under(GFX, "PDF", Document);
837     rb_define_alloc_func(PDFClass, pdf_allocate);
838     
839     SWFClass = rb_define_class_under(GFX, "SWF", Document);
840     rb_define_alloc_func(SWFClass, swf_allocate);
841     
842     ImageClass = rb_define_class_under(GFX, "ImageRead", Document);
843     rb_define_alloc_func(ImageClass, imgdrv_allocate);
844     
845     id_setparameter = rb_intern("setparameter");
846     id_startpage = rb_intern("startpage") ;
847     id_startclip = rb_intern("startclip") ;
848     id_endclip = rb_intern("endclip") ;
849     id_stroke = rb_intern("stroke") ;
850     id_fill = rb_intern("fill") ;
851     id_fillbitmap = rb_intern("fillbitmap") ;
852     id_fillgradient = rb_intern("fillgradient") ;
853     id_addfont = rb_intern("addfont") ;
854     id_drawchar = rb_intern("drawchar") ;
855     id_drawlink = rb_intern("drawlink") ;
856     id_endpage = rb_intern("endpage") ;
857     id_geterror = rb_intern("geterror") ;
858     id_finish = rb_intern("finish") ;
859     id_butt = rb_intern("butt");
860     id_round = rb_intern("round");
861     id_square = rb_intern("square");
862     id_miter = rb_intern("miter");
863     id_bevel = rb_intern("bevel");
864     id_move = rb_intern("move");
865     id_line = rb_intern("line");
866     id_spline = rb_intern("spline");
867     id_radial = rb_intern("radial");
868     id_linear = rb_intern("linear");
869     id_remove_font_transforms = rb_intern("remove_font_transforms");
870     id_maketransparent = rb_intern("maketransparent");
871     id_vectors_to_glyphs = rb_intern("vectors_to_glyphs");
872 }
873