/*
 *   Ruby/PDFlib
 *      -- Ruby extension module to access PDFlib
 *          written by TAKAHASHI Hitoshi <thitoshi@ne.scphys.kyoto-u.ac.jp>
 */


#include <ruby.h>
#include <pdflib.h>


VALUE cPDF;
VALUE mPDFConst;


static PDF *
get_pdf(obj)
     VALUE obj;
{
  PDF *pdf;

  Check_Type(obj, T_DATA);
  if (! rb_obj_is_kind_of(obj, cPDF)) {
    rb_raise(rb_eTypeError, "not a PDF");
  }
  Data_Get_Struct(obj, PDF, pdf);
  return pdf;
}

static void
rbpdf_error_handler(pdf, type, message)
     PDF *pdf;
     int type;
     const char *message;
{
  switch (type) {
  case PDF_NonfatalError:
    return;
  case PDF_MemoryError:
    rb_fatal("PDFlib : failed to allocate memory");
  case PDF_IOError:
    rb_raise(rb_eIOError, "PDFlib : %s", message);
  case PDF_IndexError:
    rb_raise(rb_eIndexError, "PDFlib : %s", message);
  case PDF_TypeError:
    rb_raise(rb_eTypeError, "PDFlib : %s", message);
  case PDF_DivisionByZero:
  case PDF_OverflowError:
  case PDF_ValueError:
    rb_raise(rb_eArgError, "PDFlib : %s", message);
  case PDF_SyntaxError:
    rb_raise(rb_eSyntaxError, "PDFlib : %s", message);
  case PDF_RuntimeError:
  case PDF_SystemError:
  case PDF_UnknownError:
  default:
    rb_raise(rb_eRuntimeError, "PDFlib : %s", message);
  }
}

static VALUE
rbpdf_initialize(argc, argv, self)
     int argc;
     VALUE *argv, self;
{
  VALUE file;
  char *filename;

  if (rb_scan_args(argc, argv, "01", &file) == 1 && ! NIL_P(file)) {
    Check_SafeStr(file);
    filename = STR2CSTR(file);
  } else {
    filename = NULL;
  }
  if (PDF_open_file(get_pdf(self), filename) == -1)
    rb_raise(rb_eIOError, "Cannot open file %s", filename ? filename : "stdout");

  return self;
}

static VALUE
rbpdf_close(self)
     VALUE self;
{
  PDF_close(get_pdf(self));
  return Qnil;
}

static VALUE
rbpdf_s_allocate(klass)
     VALUE klass;
{
  PDF *pdf = PDF_new2(rbpdf_error_handler, NULL, NULL, NULL, NULL);
  return Data_Wrap_Struct(klass, 0, PDF_delete, pdf);
}

#ifdef HAVE_OBJECT_ALLOCATE
#define rbpdf_s_new rb_class_new_instance
#else
static VALUE
rbpdf_s_new(argc, argv, klass)
     int argc;
     VALUE *argv, klass;
{
  VALUE obj = rbpdf_s_allocate(klass);
  rb_obj_call_init(obj, argc, argv);
  return obj;
}
#endif

static VALUE
rbpdf_s_open(argc, argv, klass)
     int argc;
     VALUE *argv, klass;
{
  VALUE obj = rbpdf_s_new(argc, argv, klass);
  if (rb_block_given_p())
    return rb_ensure(rb_yield, obj, rbpdf_close, obj);
  else
    return obj;
}


static VALUE
rbpdf_begin_page(self, width, height)
     VALUE self, width, height;
{
  PDF_begin_page(get_pdf(self), NUM2DBL(width), NUM2DBL(height));
  return self;
}

static VALUE
rbpdf_end_page(self)
     VALUE self;
{
  PDF_end_page(get_pdf(self));
  return self;
}

static VALUE
rbpdf_set_parameter(self, key, value)
     VALUE self, key, value;
{
  PDF_set_parameter(get_pdf(self), STR2CSTR(key), STR2CSTR(value));
  return self;
}

static VALUE
rbpdf_get_parameter(self, key, mod)
     VALUE self, key, mod;
{
  return rb_str_new2(PDF_get_parameter(get_pdf(self), STR2CSTR(key), NUM2DBL(mod)));
}

static VALUE
rbpdf_set_value(self, key, value)
     VALUE self, key, value;
{
  PDF_set_value(get_pdf(self), STR2CSTR(key), NUM2DBL(value));
  return self;
}

static VALUE
rbpdf_get_value(self, key, mod)
     VALUE self, key, mod;
{
  return rb_float_new((double) PDF_get_value(get_pdf(self), STR2CSTR(key), NUM2DBL(mod)));
}

static VALUE
rbpdf_set_info(self, key, value)
     VALUE self, key, value;
{
  PDF_set_info(get_pdf(self), STR2CSTR(key), STR2CSTR(value));
  return self;
}

static VALUE
rbpdf_get_buffer(self)
     VALUE self;
{
  const char *buf;
  long size;

  buf = PDF_get_buffer(get_pdf(self), &size);
  return buf ? rb_str_new(buf, size) : Qnil;
}


static VALUE
rbpdf_findfont(self, fontname, encoding, embed)
     VALUE self, fontname, encoding, embed;
{
  int font = PDF_findfont(get_pdf(self),
			  STR2CSTR(fontname),
			  STR2CSTR(encoding),
			  RTEST(embed));
  if (font == -1)
    rb_raise(rb_eNameError, "Cannot find font %s", STR2CSTR(fontname));
  return INT2NUM(font);
}

static VALUE
rbpdf_setfont(self, font, size)
     VALUE self, font, size;
{
  PDF_setfont(get_pdf(self), NUM2INT(font), NUM2DBL(size));
  return self;
}

static VALUE
rbpdf_get_font(self)
     VALUE self;
{
  return INT2NUM((int) PDF_get_value(get_pdf(self), "font", 0));
}

static VALUE
rbpdf_get_fontname(self)
     VALUE self;
{
  return rb_str_new2(PDF_get_parameter(get_pdf(self), "fontname", 0));
}

static VALUE
rbpdf_get_fontsize(self)
     VALUE self;
{
  return rb_float_new(PDF_get_value(get_pdf(self), "fontsize", 0));
}


static VALUE
rbpdf_show(argc, argv, self)
     int argc;
     VALUE *argv, self;
{
  VALUE text, x, y;

  if (rb_scan_args(argc, argv, "12", &text, &x, &y) == 3) {
    PDF_show_xy(get_pdf(self), STR2CSTR(text), NUM2DBL(x), NUM2DBL(y));
  } else {
    PDF_show(get_pdf(self), STR2CSTR(text));
  }
  return self;
}

static VALUE
rbpdf_continue_text(self, text)
     VALUE self, text;
{
  PDF_continue_text(get_pdf(self), STR2CSTR(text));
  return self;
}

static VALUE
rbpdf_stringwidth(self, text, font, size)
     VALUE self, text, font, size;
{
  float width = PDF_stringwidth(get_pdf(self),
				STR2CSTR(text),
				NUM2INT(font),
				NUM2DBL(size));
  return rb_float_new(width);
}

static VALUE
rbpdf_set_leading(self, leading)
     VALUE self, leading;
{
  PDF_set_value(get_pdf(self), "leading", NUM2DBL(leading));
  return self;
}

static VALUE
rbpdf_set_text_rise(self, rise)
     VALUE self, rise;
{
  PDF_set_value(get_pdf(self), "textrise", NUM2DBL(rise));
  return self;
}

static VALUE
rbpdf_set_horiz_scaling(self, scale)
     VALUE self, scale;
{
  PDF_set_value(get_pdf(self), "horizscaling", NUM2DBL(scale));
  return self;
}

static VALUE
rbpdf_set_text_rendering(self, mode, clipping)
     VALUE self, mode, clipping;
{
  int cmode = NUM2INT(mode);

  if (RTEST(clipping))
    cmode += 4;
  PDF_set_value(get_pdf(self), "textrendering", cmode);
  return self;
}

static VALUE
rbpdf_set_text_matrix(self, a, b, c, d, e, f)
     VALUE self, a, b, c, d, e, f;
{
  PDF_set_text_matrix(get_pdf(self), NUM2DBL(a), NUM2DBL(b), NUM2DBL(c),
		      NUM2DBL(d), NUM2DBL(e), NUM2DBL(f));
  return self;
}

static VALUE
rbpdf_set_text_pos(self, x, y)
     VALUE self, x, y;
{
  PDF_set_text_pos(get_pdf(self), NUM2DBL(x), NUM2DBL(y));
  return self;
}

static VALUE
rbpdf_set_char_spacing(self, space)
     VALUE self, space;
{
  PDF_set_value(get_pdf(self), "charspacing", NUM2DBL(space));
  return self;
}

static VALUE
rbpdf_set_word_spacing(self, space)
     VALUE self, space;
{
  PDF_set_value(get_pdf(self), "wordspacing", NUM2DBL(space));
  return self;
}

static VALUE
rbpdf_show_boxed(self, text, x, y, width, height, mode, reserved)
     VALUE self, text, x, y, width, height, mode, reserved;
{
  int num = PDF_show_boxed(get_pdf(self), STR2CSTR(text),
			   NUM2DBL(x), NUM2DBL(y),
			   NUM2DBL(width), NUM2DBL(height),
			   STR2CSTR(mode), STR2CSTR(reserved));
  return INT2NUM(num);
}


static VALUE
rbpdf_setdash(self, black, white)
     VALUE self, black, white;
{
  PDF_setdash(get_pdf(self), NUM2DBL(black), NUM2DBL(white));
  return self;
}

static VALUE
rbpdf_setpolydash(self, array)
     VALUE self, array;
{
  int i;
  float *darray;
  int len = RARRAY(array)->len;

  if (len == 0) {
    darray = NULL;
  } else {
    darray = ALLOCA_N(float, len);
    for (i = 0; i < len; i++)
      darray[i] = NUM2DBL(RARRAY(array)->ptr[i]);
  }
  PDF_setpolydash(get_pdf(self), darray, len);
  return self;
}

static VALUE
rbpdf_setflat(self, flat)
     VALUE self, flat;
{
  PDF_setflat(get_pdf(self), NUM2DBL(flat));
  return self;
}

static VALUE
rbpdf_setlinejoin(self, join)
     VALUE self, join;
{
  PDF_setlinejoin(get_pdf(self), NUM2INT(join));
  return self;
}

static VALUE
rbpdf_setlinecap(self, cap)
     VALUE self, cap;
{
  PDF_setlinecap(get_pdf(self), NUM2INT(cap));
  return self;
}

static VALUE
rbpdf_setmiterlimit(self, limit)
     VALUE self, limit;
{
  PDF_setmiterlimit(get_pdf(self), NUM2DBL(limit));
  return self;
}

static VALUE
rbpdf_setlinewidth(self, width)
     VALUE self, width;
{
  PDF_setlinewidth(get_pdf(self), NUM2DBL(width));
  return self;
}

static VALUE
rbpdf_set_fillrule(self, rule)
     VALUE self, rule;
{
  PDF_set_parameter(get_pdf(self), "fillrule", STR2CSTR(rule));
  return self;
}


static VALUE
rbpdf_save(self)
     VALUE self;
{
  PDF_save(get_pdf(self));
  return self;
}

static VALUE
rbpdf_restore(self)
     VALUE self;
{
  PDF_restore(get_pdf(self));
  return self;
}

static VALUE
rbpdf_translate(self, x, y)
     VALUE self, x, y;
{
  PDF_translate(get_pdf(self), NUM2DBL(x), NUM2DBL(y));
  return self;
}

static VALUE
rbpdf_scale(self, x, y)
     VALUE self, x, y;
{
  PDF_scale(get_pdf(self), NUM2DBL(x), NUM2DBL(y));
  return self;
}

static VALUE
rbpdf_rotate(self, phi)
     VALUE self, phi;
{
  PDF_rotate(get_pdf(self), NUM2DBL(phi));
  return self;
}

static VALUE
rbpdf_skew(self, alpha, beta)
     VALUE self, alpha, beta;
{
  PDF_skew(get_pdf(self), NUM2DBL(alpha), NUM2DBL(beta));
  return self;
}

static VALUE
rbpdf_concat(self, a, b, c, d, e, f)
     VALUE self, a, b, c, d, e, f;
{
  PDF_concat(get_pdf(self), NUM2DBL(a), NUM2DBL(b), NUM2DBL(c),
	     NUM2DBL(d), NUM2DBL(e), NUM2DBL(f));
  return self;
}

#if PDFLIB_MAJORVERSION > 3
static VALUE
rbpdf_initgraphics(self)
     VALUE self;
{
  PDF_initgraphics(get_pdf(self));
  return self;
}

static VALUE
rbpdf_setmatrix(self, a, b, c, d, e, f)
     VALUE self, a, b, c, d, e, f;
{
  PDF_setmatrix(get_pdf(self), NUM2DBL(a), NUM2DBL(b), NUM2DBL(c),
		NUM2DBL(d), NUM2DBL(e), NUM2DBL(f));
  return self;
}
#endif  /* PDFLIB_MAJORVERSION > 3 */


static VALUE
rbpdf_moveto(self, x, y)
     VALUE self, x, y;
{
  PDF_moveto(get_pdf(self), NUM2DBL(x), NUM2DBL(y));
  return self;
}

static VALUE
rbpdf_lineto(self, x, y)
     VALUE self, x, y;
{
  PDF_lineto(get_pdf(self), NUM2DBL(x), NUM2DBL(y));
  return self;
}

static VALUE
rbpdf_curveto(self, x1, y1, x2, y2, x3, y3)
     VALUE self, x1, y1, x2, y2, x3, y3;
{
  PDF_curveto(get_pdf(self), NUM2DBL(x1), NUM2DBL(y1),
	      NUM2DBL(x2), NUM2DBL(y2), NUM2DBL(x3), NUM2DBL(y3));
  return self;
}

static VALUE
rbpdf_circle(self, x, y, r)
     VALUE self, x, y, r;
{
  PDF_circle(get_pdf(self), NUM2DBL(x), NUM2DBL(y), NUM2DBL(r));
  return self;
}

static VALUE
rbpdf_arc(self, x, y, r, alpha1, alpha2)
     VALUE self, x, y, r, alpha1, alpha2;
{
  PDF_arc(get_pdf(self), NUM2DBL(x), NUM2DBL(y), NUM2DBL(r),
	  NUM2DBL(alpha1), NUM2DBL(alpha2));
  return self;
}

#if PDFLIB_MAJORVERSION > 3
static VALUE
rbpdf_arcn(self, x, y, r, alpha1, alpha2)
     VALUE self, x, y, r, alpha1, alpha2;
{
  PDF_arcn(get_pdf(self), NUM2DBL(x), NUM2DBL(y), NUM2DBL(r),
	   NUM2DBL(alpha1), NUM2DBL(alpha2));
  return self;
}
#endif

static VALUE
rbpdf_rect(self, x, y, width, height)
     VALUE self, x, y, width, height;
{
  PDF_rect(get_pdf(self), NUM2DBL(x), NUM2DBL(y),
	   NUM2DBL(width), NUM2DBL(height));
  return self;
}

static VALUE
rbpdf_closepath(self)
     VALUE self;
{
  PDF_closepath(get_pdf(self));
  return self;
}

static VALUE
rbpdf_stroke(self)
     VALUE self;
{
  PDF_stroke(get_pdf(self));
  return self;
}

static VALUE
rbpdf_closepath_stroke(self)
     VALUE self;
{
  PDF_closepath_stroke(get_pdf(self));
  return self;
}

static VALUE
rbpdf_fill(self)
     VALUE self;
{
  PDF_fill(get_pdf(self));
  return self;
}

static VALUE
rbpdf_fill_stroke(self)
     VALUE self;
{
  PDF_fill_stroke(get_pdf(self));
  return self;
}

static VALUE
rbpdf_closepath_fill_stroke(self)
     VALUE self;
{
  PDF_closepath_fill_stroke(get_pdf(self));
  return self;
}

static VALUE
rbpdf_endpath(self)
     VALUE self;
{
  PDF_endpath(get_pdf(self));
  return self;
}

static VALUE
rbpdf_clip(self)
     VALUE self;
{
  PDF_clip(get_pdf(self));
  return self;
}


static VALUE
rbpdf_setgray_fill(self, gray)
     VALUE self, gray;
{
#if PDFLIB_MAJORVERSION < 4
  PDF_setgray_fill(get_pdf(self), NUM2DBL(gray));
#else
  PDF_setcolor(get_pdf(self), "fill", "gray", NUM2DBL(gray), 0, 0, 0);
#endif
  return self;
}

static VALUE
rbpdf_setgray_stroke(self, gray)
     VALUE self, gray;
{
#if PDFLIB_MAJORVERSION < 4
  PDF_setgray_stroke(get_pdf(self), NUM2DBL(gray));
#else
  PDF_setcolor(get_pdf(self), "stroke", "gray", NUM2DBL(gray), 0, 0, 0);
#endif
  return self;
}

static VALUE
rbpdf_setgray(self, gray)
     VALUE self, gray;
{
#if PDFLIB_MAJORVERSION < 4
  PDF_setgray(get_pdf(self), NUM2DBL(gray));
#else
  PDF_setcolor(get_pdf(self), "both", "gray", NUM2DBL(gray), 0, 0, 0);
#endif
  return self;
}

static VALUE
rbpdf_setrgbcolor_fill(self, red, green, blue)
     VALUE self, red, green, blue;
{
#if PDFLIB_MAJORVERSION < 4
  PDF_setrgbcolor_fill(get_pdf(self), NUM2DBL(red),
		       NUM2DBL(green), NUM2DBL(blue));
#else
  PDF_setcolor(get_pdf(self), "fill", "rgb",
	       NUM2DBL(red), NUM2DBL(green), NUM2DBL(blue), 0);
#endif
  return self;
}

static VALUE
rbpdf_setrgbcolor_stroke(self, red, green, blue)
     VALUE self, red, green, blue;
{
#if PDFLIB_MAJORVERSION < 4
  PDF_setrgbcolor_stroke(get_pdf(self), NUM2DBL(red),
			 NUM2DBL(green), NUM2DBL(blue));
#else
  PDF_setcolor(get_pdf(self), "stroke", "rgb",
	       NUM2DBL(red), NUM2DBL(green), NUM2DBL(blue), 0);
#endif
  return self;
}

static VALUE
rbpdf_setrgbcolor(self, red, green, blue)
     VALUE self, red, green, blue;
{
#if PDFLIB_MAJORVERSION < 4
  PDF_setrgbcolor(get_pdf(self), NUM2DBL(red),
		  NUM2DBL(green), NUM2DBL(blue));
#else
  PDF_setcolor(get_pdf(self), "both", "rgb",
	       NUM2DBL(red), NUM2DBL(green), NUM2DBL(blue), 0);
#endif
  return self;
}

#if PDFLIB_MAJORVERSION > 3
static VALUE
rbpdf_setcolor(argc, argv, self)
     int argc;
     VALUE *argv, self;
{
  float c1, c2, c3, c4;
  char *colorspace;

  if (argc < 3 || argc > 6)
    rb_raise(rb_eArgError, "wrong # of arguments(%d for 3 - 6)", argc);
  colorspace = STR2CSTR(argv[1]);
  c1 = NUM2DBL(argv[2]);
  c2 = c3 = c4 = 0;

  if (strncmp(colorspace, "rgb", 3) == 0) {
    if (argc < 5)
      rb_raise(rb_eArgError, "wrong # of arguments(%d for 5)", argc);
    c2 = NUM2DBL(argv[3]);
    c3 = NUM2DBL(argv[4]);
  } else if (strncmp(colorspace, "cmyk", 4) == 0) {
    if (argc < 6)
      rb_raise(rb_eArgError, "wrong # of arguments(%d for 6)", argc);
    c2 = NUM2DBL(argv[3]);
    c3 = NUM2DBL(argv[4]);
    c4 = NUM2DBL(argv[5]);
  } else if (strncmp(colorspace, "spot", 4) == 0) {
    if (argc < 4)
      rb_raise(rb_eArgError, "wrong # of arguments(%d for 4)", argc);
    c2 = NUM2DBL(argv[3]);
  }

  PDF_setcolor(get_pdf(self), STR2CSTR(argv[0]), colorspace, c1, c2, c3, c4);
  return self;
}

static VALUE
rbpdf_makespotcolor(self, spotname, len)
     VALUE self, spotname, len;
{
  return INT2NUM(PDF_makespotcolor(get_pdf(self), STR2CSTR(spotname),
				   NUM2INT(len)));
}


static VALUE
rbpdf_begin_pattern(self, width, height, xstep, ystep, type)
     VALUE self, width, height, xstep, ystep, type;
{
  return INT2NUM(PDF_begin_pattern(get_pdf(self),
				   NUM2DBL(width), NUM2DBL(height),
				   NUM2DBL(xstep), NUM2DBL(ystep),
				   NUM2INT(type)));
}

static VALUE
rbpdf_end_pattern(self)
     VALUE self;
{
  PDF_end_pattern(get_pdf(self));
  return self;
}

static VALUE
rbpdf_begin_template(self, width, height)
     VALUE self, width, height;
{
  return INT2NUM(PDF_begin_template(get_pdf(self),
				    NUM2DBL(width), NUM2DBL(height)));
}

static VALUE
rbpdf_end_template(self)
     VALUE self;
{
  PDF_end_template(get_pdf(self));
  return self;
}
#endif  /* PDFLIB_MAJORVERSION > 3 */


static VALUE
rbpdf_open_image_file(argc, argv, self)
     int argc;
     VALUE *argv, self;
{
  VALUE type, filename, str_par, int_par;
  int image;

  if (rb_scan_args(argc, argv, "22", &type, &filename, &str_par, &int_par) == 4) {
    image = PDF_open_image_file(get_pdf(self), STR2CSTR(type),
				STR2CSTR(filename),
				STR2CSTR(str_par), NUM2INT(int_par));
  } else {
    image = PDF_open_image_file(get_pdf(self), STR2CSTR(type),
				STR2CSTR(filename), NULL, 0);
  }
  if (image == -1)
    rb_raise(rb_eIOError, "Cannot open file %s", STR2CSTR(filename));

  return INT2NUM(image);
}

static VALUE
rbpdf_close_image(self, image)
     VALUE self, image;
{
  PDF_close_image(get_pdf(self), NUM2INT(image));
  return self;
}

static VALUE
rbpdf_place_image(self, image, x, y, scale)
     VALUE self, image, x, y, scale;
{
  PDF_place_image(get_pdf(self), NUM2INT(image),
		  NUM2DBL(x), NUM2DBL(y), NUM2DBL(scale));
  return self;
}

static VALUE
rbpdf_get_image_width(self, image)
     VALUE self, image;
{
  return INT2NUM((int) PDF_get_value(get_pdf(self), "imagewidth",
				     NUM2INT(image)));
}

static VALUE
rbpdf_get_image_height(self, image)
     VALUE self, image;
{
  return INT2NUM((int) PDF_get_value(get_pdf(self), "imageheight",
				     NUM2INT(image)));
}

static VALUE
rbpdf_add_thumbnail(self, image)
     VALUE self, image;
{
  PDF_add_thumbnail(get_pdf(self), NUM2INT(image));
  return self;
}

static VALUE
rbpdf_open_ccitt(self, filename, width, height, reverse, k, black)
     VALUE self, filename, width, height, reverse, k, black;
{
  int image = PDF_open_CCITT(get_pdf(self), STR2CSTR(filename),
			     NUM2INT(width), NUM2INT(height),
			     RTEST(reverse), NUM2INT(k), RTEST(black));
  if (image == -1)
    rb_raise(rb_eIOError, "Cannot open file %s", STR2CSTR(filename));

  return INT2NUM(image);
}


static VALUE
rbpdf_add_bookmark(self, text, parent, open)
     VALUE self, text, parent, open;
{
  int id = PDF_add_bookmark(get_pdf(self), STR2CSTR(text),
			    NUM2INT(parent), RTEST(open));
  return INT2NUM(id);
}

static VALUE
rbpdf_set_transition(self, type)
     VALUE self, type;
{
  PDF_set_parameter(get_pdf(self), "transition", STR2CSTR(type));
  return self;
}

static VALUE
rbpdf_set_duration(self, duration)
     VALUE self, duration;
{
  PDF_set_value(get_pdf(self), "duration", NUM2DBL(duration));
  return self;
}

static VALUE
rbpdf_attach_file(self, llx, lly, urx, ury, fname, descript, author, mimetype, icon)
     VALUE self, llx, lly, urx, ury, fname, descript, author, mimetype, icon;
{
  PDF_attach_file(get_pdf(self), NUM2DBL(llx), NUM2DBL(lly),
		  NUM2DBL(urx), NUM2DBL(ury), STR2CSTR(fname),
		  STR2CSTR(descript), STR2CSTR(author),
		  STR2CSTR(mimetype), STR2CSTR(icon));
  return self;
}

static VALUE
rbpdf_add_note(self, llx, lly, urx, ury, contents, title, icon, open)
     VALUE self, llx, lly, urx, ury, contents, title, icon, open;
{
  PDF_add_note(get_pdf(self), NUM2DBL(llx), NUM2DBL(lly),
	       NUM2DBL(urx), NUM2DBL(ury), STR2CSTR(contents),
	       STR2CSTR(title), STR2CSTR(icon), RTEST(open));
  return self;
}

static VALUE
rbpdf_add_pdflink(self, llx, lly, urx, ury, fname, page, dest)
     VALUE self, llx, lly, urx, ury, fname, page, dest;
{
  PDF_add_pdflink(get_pdf(self), NUM2DBL(llx), NUM2DBL(lly),
		  NUM2DBL(urx), NUM2DBL(ury), STR2CSTR(fname),
		  NUM2INT(page), STR2CSTR(dest));
  return self;
}

static VALUE
rbpdf_add_launchlink(self, llx, lly, urx, ury, fname)
     VALUE self, llx, lly, urx, ury, fname;
{
  PDF_add_launchlink(get_pdf(self), NUM2DBL(llx), NUM2DBL(lly),
		     NUM2DBL(urx), NUM2DBL(ury), STR2CSTR(fname));
  return self;
}

static VALUE
rbpdf_add_locallink(self, llx, lly, urx, ury, page, dest)
     VALUE self, llx, lly, urx, ury, page, dest;
{
  PDF_add_locallink(get_pdf(self), NUM2DBL(llx), NUM2DBL(lly),
		    NUM2DBL(urx), NUM2DBL(ury), NUM2INT(page), STR2CSTR(dest));
  return self;
}

static VALUE
rbpdf_add_weblink(self, llx, lly, urx, ury, url)
     VALUE self, llx, lly, urx, ury, url;
{
  PDF_add_weblink(get_pdf(self), NUM2DBL(llx), NUM2DBL(lly),
		  NUM2DBL(urx), NUM2DBL(ury), STR2CSTR(url));
  return self;
}

static VALUE
rbpdf_set_border_style(self, style, width)
     VALUE self, style, width;
{
  PDF_set_border_style(get_pdf(self), STR2CSTR(style), NUM2DBL(width));
  return self;
}

static VALUE
rbpdf_set_border_color(self, red, green, blue)
     VALUE self, red, green, blue;
{
  PDF_set_border_color(get_pdf(self), NUM2DBL(red),
		       NUM2DBL(green), NUM2DBL(blue));
  return self;
}

static VALUE
rbpdf_set_border_dash(self, white, black)
     VALUE self, white, black;
{
  PDF_set_border_dash(get_pdf(self), NUM2DBL(white), NUM2DBL(black));
  return self;
}


void
Init_pdflib()
{
  PDF_boot();

  cPDF = rb_define_class("PDF", rb_cData);


#ifdef HAVE_OBJECT_ALLOCATE
  rb_define_singleton_method(cPDF, "allocate", rbpdf_s_allocate, 0);
#else
  rb_define_singleton_method(cPDF, "new", rbpdf_s_new, -1);
#endif
  rb_define_singleton_method(cPDF, "open", rbpdf_s_open, -1);

  rb_define_method(cPDF, "initialize", rbpdf_initialize, -1);
  rb_define_method(cPDF, "close", rbpdf_close, 0);
  rb_define_method(cPDF, "begin_page", rbpdf_begin_page, 2);
  rb_define_method(cPDF, "end_page", rbpdf_end_page, 0);
  rb_define_method(cPDF, "set_parameter", rbpdf_set_parameter, 2);
  rb_define_method(cPDF, "get_parameter", rbpdf_get_parameter, 2);
  rb_define_method(cPDF, "set_value", rbpdf_set_value, 2);
  rb_define_method(cPDF, "get_value", rbpdf_get_value, 2);
  rb_define_method(cPDF, "set_info", rbpdf_set_info, 2);
  rb_define_method(cPDF, "get_buffer", rbpdf_get_buffer, 0);

  rb_define_method(cPDF, "findfont", rbpdf_findfont, 3);
  rb_define_method(cPDF, "setfont", rbpdf_setfont, 2);
  rb_define_method(cPDF, "get_font", rbpdf_get_font, 0);
  rb_define_method(cPDF, "get_fontname", rbpdf_get_fontname, 0);
  rb_define_method(cPDF, "get_fontsize", rbpdf_get_fontsize, 0);

  rb_define_method(cPDF, "show", rbpdf_show, -1);
  rb_define_method(cPDF, "continue_text", rbpdf_continue_text, 1);
  rb_define_method(cPDF, "stringwidth", rbpdf_stringwidth, 3); 
  rb_define_method(cPDF, "set_leading", rbpdf_set_leading, 1);
  rb_define_method(cPDF, "set_text_rise", rbpdf_set_text_rise, 1);
  rb_define_method(cPDF, "set_horiz_scaling", rbpdf_set_horiz_scaling, 1);
  rb_define_method(cPDF, "set_text_rendering", rbpdf_set_text_rendering, 2);
  rb_define_method(cPDF, "set_text_matrix", rbpdf_set_text_matrix, 6);
  rb_define_method(cPDF, "set_text_pos", rbpdf_set_text_pos, 2);
  rb_define_method(cPDF, "set_char_spacing", rbpdf_set_char_spacing, 1);
  rb_define_method(cPDF, "set_word_spacing", rbpdf_set_word_spacing, 1);
  rb_define_method(cPDF, "show_boxed", rbpdf_show_boxed, 7);

  rb_define_method(cPDF, "setdash", rbpdf_setdash, 2);
  rb_define_method(cPDF, "setpolydash", rbpdf_setpolydash, 1);
  rb_define_method(cPDF, "setflat", rbpdf_setflat, 1);
  rb_define_method(cPDF, "setlinejoin", rbpdf_setlinejoin, 1);
  rb_define_method(cPDF, "setlinecap", rbpdf_setlinecap, 1);
  rb_define_method(cPDF, "setmiterlimit", rbpdf_setmiterlimit, 1);
  rb_define_method(cPDF, "setlinewidth", rbpdf_setlinewidth, 1);
  rb_define_method(cPDF, "set_fillrule", rbpdf_set_fillrule, 1);

  rb_define_method(cPDF, "save", rbpdf_save, 0);
  rb_define_method(cPDF, "restore", rbpdf_restore, 0);
  rb_define_method(cPDF, "translate", rbpdf_translate, 2);
  rb_define_method(cPDF, "scale", rbpdf_scale, 2);
  rb_define_method(cPDF, "rotate", rbpdf_rotate, 1);
  rb_define_method(cPDF, "skew", rbpdf_skew, 2);
  rb_define_method(cPDF, "concat", rbpdf_concat, 6);
#if PDFLIB_MAJORVERSION > 3
  rb_define_method(cPDF, "initgraphics", rbpdf_initgraphics, 0);
  rb_define_method(cPDF, "setmatrix", rbpdf_setmatrix, 6);
#endif

  rb_define_method(cPDF, "moveto", rbpdf_moveto, 2);
  rb_define_method(cPDF, "lineto", rbpdf_lineto, 2);
  rb_define_method(cPDF, "curveto", rbpdf_curveto, 6);
  rb_define_method(cPDF, "circle", rbpdf_circle, 3);
  rb_define_method(cPDF, "arc", rbpdf_arc, 5);
#if PDFLIB_MAJORVERSION > 3
  rb_define_method(cPDF, "arcn", rbpdf_arcn, 5);
#endif
  rb_define_method(cPDF, "rect", rbpdf_rect, 4);
  rb_define_method(cPDF, "closepath", rbpdf_closepath, 0);
  rb_define_method(cPDF, "stroke", rbpdf_stroke, 0);
  rb_define_method(cPDF, "closepath_stroke", rbpdf_closepath_stroke, 0);
  rb_define_method(cPDF, "fill", rbpdf_fill, 0);
  rb_define_method(cPDF, "fill_stroke", rbpdf_fill_stroke, 0);
  rb_define_method(cPDF, "closepath_fill_stroke", rbpdf_closepath_fill_stroke, 0);
  rb_define_method(cPDF, "endpath", rbpdf_endpath, 0);
  rb_define_method(cPDF, "clip", rbpdf_clip, 0);

  rb_define_method(cPDF, "setgray_fill", rbpdf_setgray_fill, 1);
  rb_define_method(cPDF, "setgray_stroke", rbpdf_setgray_stroke, 1);
  rb_define_method(cPDF, "setgray", rbpdf_setgray, 1);
  rb_define_method(cPDF, "setrgbcolor_fill", rbpdf_setrgbcolor_fill, 3);
  rb_define_method(cPDF, "setrgbcolor_stroke", rbpdf_setrgbcolor_stroke, 3);
  rb_define_method(cPDF, "setrgbcolor", rbpdf_setrgbcolor, 3);
#if PDFLIB_MAJORVERSION > 3
  rb_define_method(cPDF, "setcolor", rbpdf_setcolor, -1);
  rb_define_method(cPDF, "makespotcolor", rbpdf_makespotcolor, 2);

  rb_define_method(cPDF, "begin_pattern", rbpdf_begin_pattern, 5);
  rb_define_method(cPDF, "end_pattern", rbpdf_end_pattern, 0);
  rb_define_method(cPDF, "begin_template", rbpdf_begin_template, 2);
  rb_define_method(cPDF, "end_template", rbpdf_end_template, 0);
#endif

  rb_define_method(cPDF, "open_image_file", rbpdf_open_image_file, -1);
  rb_define_method(cPDF, "close_image", rbpdf_close_image, 1);
  rb_define_method(cPDF, "place_image", rbpdf_place_image, 4);
  rb_define_method(cPDF, "get_image_width", rbpdf_get_image_width, 1);
  rb_define_method(cPDF, "get_image_height", rbpdf_get_image_height, 1);
  rb_define_method(cPDF, "add_thumbnail", rbpdf_add_thumbnail, 1);
  rb_define_method(cPDF, "open_CCITT", rbpdf_open_ccitt, 6);

  rb_define_method(cPDF, "add_bookmark", rbpdf_add_bookmark, 3);
  rb_define_method(cPDF, "set_transition", rbpdf_set_transition, 1);
  rb_define_method(cPDF, "set_duration", rbpdf_set_duration, 1);

  rb_define_method(cPDF, "attach_file", rbpdf_attach_file, 9);
  rb_define_method(cPDF, "add_note", rbpdf_add_note, 8);
  rb_define_method(cPDF, "add_pdflink", rbpdf_add_pdflink, 7);
  rb_define_method(cPDF, "add_launchlink", rbpdf_add_launchlink, 5);
  rb_define_method(cPDF, "add_locallink", rbpdf_add_locallink, 6);
  rb_define_method(cPDF, "add_weblink", rbpdf_add_weblink, 5);
  rb_define_method(cPDF, "set_border_style", rbpdf_set_border_style, 2);
  rb_define_method(cPDF, "set_border_color", rbpdf_set_border_color, 3);
  rb_define_method(cPDF, "set_border_dash", rbpdf_set_border_dash, 2);


  /* constants */
  mPDFConst = rb_define_module_under(cPDF, "Constants");

  /* version number */
  rb_define_const(mPDFConst, "MAJORVERSION", INT2FIX(PDFLIB_MAJORVERSION));
  rb_define_const(mPDFConst, "MINORVERSION", INT2FIX(PDFLIB_MINORVERSION));
  rb_define_const(mPDFConst, "VERSIONSTRING", rb_str_new2(PDFLIB_VERSIONSTRING));

  /* paper size */
  rb_define_const(mPDFConst, "A0_WIDTH", rb_float_new(a0_width));
  rb_define_const(mPDFConst, "A0_HEIGHT", rb_float_new(a0_height));
  rb_define_const(mPDFConst, "A1_WIDTH", rb_float_new(a1_width));
  rb_define_const(mPDFConst, "A1_HEIGHT", rb_float_new(a1_height));
  rb_define_const(mPDFConst, "A2_WIDTH", rb_float_new(a2_width));
  rb_define_const(mPDFConst, "A2_HEIGHT", rb_float_new(a2_height));
  rb_define_const(mPDFConst, "A3_WIDTH", rb_float_new(a3_width));
  rb_define_const(mPDFConst, "A3_HEIGHT", rb_float_new(a3_height));
  rb_define_const(mPDFConst, "A4_WIDTH", rb_float_new(a4_width));
  rb_define_const(mPDFConst, "A4_HEIGHT", rb_float_new(a4_height));
  rb_define_const(mPDFConst, "A5_WIDTH", rb_float_new(a5_width));
  rb_define_const(mPDFConst, "A5_HEIGHT", rb_float_new(a5_height));
  rb_define_const(mPDFConst, "A6_WIDTH", rb_float_new(a6_width));
  rb_define_const(mPDFConst, "A6_HEIGHT", rb_float_new(a6_height));
  rb_define_const(mPDFConst, "B5_WIDTH", rb_float_new(b5_width));
  rb_define_const(mPDFConst, "B5_HEIGHT", rb_float_new(b5_height));
  rb_define_const(mPDFConst, "LETTER_WIDTH", rb_float_new(letter_width));
  rb_define_const(mPDFConst, "LETTER_HEIGHT", rb_float_new(letter_height));
  rb_define_const(mPDFConst, "LEGAL_WIDTH", rb_float_new(legal_width));
  rb_define_const(mPDFConst, "LEGAL_HEIGHT", rb_float_new(legal_height));
  rb_define_const(mPDFConst, "LEDGER_WIDTH", rb_float_new(ledger_width));
  rb_define_const(mPDFConst, "LEDGER_HEIGHT", rb_float_new(ledger_height));
  rb_define_const(mPDFConst, "P11X17_WIDTH", rb_float_new(p11x17_width));
  rb_define_const(mPDFConst, "P11X17_HEIGHT", rb_float_new(p11x17_height));

  /* PDFlib parameter key */
  /* setup */
  rb_define_const(mPDFConst, "PAR_COMPRESS", rb_str_new2("compress"));
  rb_define_const(mPDFConst, "PAR_RESOURCEFILE", rb_str_new2("resourcefile"));
  rb_define_const(mPDFConst, "PAR_FONTAFM", rb_str_new2("FontAFM"));
  rb_define_const(mPDFConst, "PAR_FONTPFM", rb_str_new2("FontPFM"));
  rb_define_const(mPDFConst, "PAR_FONTOUTLINE", rb_str_new2("FontOutline"));
  rb_define_const(mPDFConst, "PAR_ENCODING", rb_str_new2("Encoding"));
  rb_define_const(mPDFConst, "PAR_DEBUG", rb_str_new2("debug"));
  rb_define_const(mPDFConst, "PAR_NODEBUG", rb_str_new2("nodebug"));
  rb_define_const(mPDFConst, "PAR_COMPATIBILITY", rb_str_new2("compatibility"));
  rb_define_const(mPDFConst, "PAR_PREFIX", rb_str_new2("prefix"));
  rb_define_const(mPDFConst, "PAR_WARNING", rb_str_new2("warning"));
  rb_define_const(mPDFConst, "PAR_BINDING", rb_str_new2("binding"));
  rb_define_const(mPDFConst, "PAR_FLUSH", rb_str_new2("flush"));
  /* page */
  rb_define_const(mPDFConst, "PAR_PAGEWIDTH", rb_str_new2("pagewidth"));
  rb_define_const(mPDFConst, "PAR_PAGEHEIGHT", rb_str_new2("pageheight"));
  /* font and text */
  rb_define_const(mPDFConst, "PAR_TEXTX", rb_str_new2("textx"));
  rb_define_const(mPDFConst, "PAR_TEXTY", rb_str_new2("texty"));
  rb_define_const(mPDFConst, "PAR_FONT", rb_str_new2("font"));
  rb_define_const(mPDFConst, "PAR_FONTSIZE", rb_str_new2("fontsize"));
  rb_define_const(mPDFConst, "PAR_LEADING", rb_str_new2("leading"));
  rb_define_const(mPDFConst, "PAR_TEXTRISE", rb_str_new2("textrise"));
  rb_define_const(mPDFConst, "PAR_HORIZSCALING", rb_str_new2("horizscaling"));
  rb_define_const(mPDFConst, "PAR_TEXTRENDERING", rb_str_new2("textrendering"));
  rb_define_const(mPDFConst, "PAR_CHARSPACING", rb_str_new2("charspacing"));
  rb_define_const(mPDFConst, "PAR_WORDSPACING", rb_str_new2("wordspacing"));
  rb_define_const(mPDFConst, "PAR_UNDERLINE", rb_str_new2("underline"));
  rb_define_const(mPDFConst, "PAR_OVERLINE", rb_str_new2("overline"));
  rb_define_const(mPDFConst, "PAR_STRIKEOUT", rb_str_new2("strikeout"));
  rb_define_const(mPDFConst, "PAR_FONTNAME", rb_str_new2("fontname"));
  rb_define_const(mPDFConst, "PAR_CAPHEIGHT", rb_str_new2("capheight"));
  rb_define_const(mPDFConst, "PAR_ASCENDER", rb_str_new2("ascender"));
  rb_define_const(mPDFConst, "PAR_DESCENDER", rb_str_new2("descender"));
  /* graphics */
  rb_define_const(mPDFConst, "PAR_CURRENTX", rb_str_new2("currentx"));
  rb_define_const(mPDFConst, "PAR_CURRENTY", rb_str_new2("currenty"));
  rb_define_const(mPDFConst, "PAR_FILLRULE", rb_str_new2("fillrule"));
  /* image */
  rb_define_const(mPDFConst, "PAR_IMAGEWIDTH", rb_str_new2("imagewidth"));
  rb_define_const(mPDFConst, "PAR_IMAGEHEIGHT", rb_str_new2("imageheight"));
  rb_define_const(mPDFConst, "PAR_RESX", rb_str_new2("resx"));
  rb_define_const(mPDFConst, "PAR_RESY", rb_str_new2("resy"));
  /* hypertext */
  rb_define_const(mPDFConst, "PAR_OPENACTION", rb_str_new2("openaction"));
  rb_define_const(mPDFConst, "PAR_TRANSITION", rb_str_new2("transition"));
  rb_define_const(mPDFConst, "PAR_DURATION", rb_str_new2("duration"));

  /* document info key */
  rb_define_const(mPDFConst, "INFO_CREATOR", rb_str_new2("Creator"));
  rb_define_const(mPDFConst, "INFO_AUTHOR", rb_str_new2("Author"));
  rb_define_const(mPDFConst, "INFO_TITLE", rb_str_new2("Title"));
  rb_define_const(mPDFConst, "INFO_SUBJECT", rb_str_new2("Subject"));

  /* encoding style */
  rb_define_const(mPDFConst, "ENCODING_HOST", rb_str_new2("host"));
  rb_define_const(mPDFConst, "ENCODING_BUILTIN", rb_str_new2("builtin"));
  rb_define_const(mPDFConst, "ENCODING_MACROMAN", rb_str_new2("macroman"));
  rb_define_const(mPDFConst, "ENCODING_WINANSI", rb_str_new2("winansi"));

  /* text drawing style */
  rb_define_const(mPDFConst, "FILL_TEXT", INT2FIX(0));
  rb_define_const(mPDFConst, "STROKE_TEXT", INT2FIX(1));
  rb_define_const(mPDFConst, "FILL_AND_STROKE_TEXT", INT2FIX(2));
  rb_define_const(mPDFConst, "INVISIBLE_TEXT", INT2FIX(3));

  /* linejoin style */
  rb_define_const(mPDFConst, "JOIN_MITER", INT2FIX(0));
  rb_define_const(mPDFConst, "JOIN_ROUND", INT2FIX(1));
  rb_define_const(mPDFConst, "JOIN_BEVEL", INT2FIX(2));

  /* linecap style */
  rb_define_const(mPDFConst, "CAP_BUTT", INT2FIX(0));
  rb_define_const(mPDFConst, "CAP_ROUND", INT2FIX(1));
  rb_define_const(mPDFConst, "CAP_SQUARE", INT2FIX(2));

  /* fill rule */
  rb_define_const(mPDFConst, "FILL_WINDING", rb_str_new2("winding"));
  rb_define_const(mPDFConst, "FILL_EVENODD", rb_str_new2("evenodd"));

  /* transition type */
  rb_define_const(mPDFConst, "TRANSIT_NONE", rb_str_new2("none"));
  rb_define_const(mPDFConst, "TRANSIT_SPLIT", rb_str_new2("split"));
  rb_define_const(mPDFConst, "TRANSIT_BLINDS", rb_str_new2("blinds"));
  rb_define_const(mPDFConst, "TRANSIT_BOX", rb_str_new2("box"));
  rb_define_const(mPDFConst, "TRANSIT_WIPE", rb_str_new2("wipe"));
  rb_define_const(mPDFConst, "TRANSIT_DISSOLVE", rb_str_new2("dissolve"));
  rb_define_const(mPDFConst, "TRANSIT_GLITTER", rb_str_new2("glitter"));
  rb_define_const(mPDFConst, "TRANSIT_REPLACE", rb_str_new2("replace"));

  /* file attachment icon */
  rb_define_const(mPDFConst, "ICON_GRAPH", rb_str_new2("graph"));
  rb_define_const(mPDFConst, "ICON_PAPERCLIP", rb_str_new2("paperclip"));
  rb_define_const(mPDFConst, "ICON_PUSHPIN", rb_str_new2("pushpin"));
  rb_define_const(mPDFConst, "ICON_TAG", rb_str_new2("tag"));

  /* note annotation icon */
  rb_define_const(mPDFConst, "ICON_COMMENT", rb_str_new2("comment"));
  rb_define_const(mPDFConst, "ICON_INSERT", rb_str_new2("insert"));
  rb_define_const(mPDFConst, "ICON_NOTE", rb_str_new2("note"));
  rb_define_const(mPDFConst, "ICON_PARAGRAPH", rb_str_new2("paragraph"));
  rb_define_const(mPDFConst, "ICON_NEWPARAGRAPH", rb_str_new2("newparagraph"));
  rb_define_const(mPDFConst, "ICON_KEY", rb_str_new2("key"));
  rb_define_const(mPDFConst, "ICON_HELP", rb_str_new2("help"));

  /* PDF link destination */
  rb_define_const(mPDFConst, "DEST_RETAIN", rb_str_new2("retain"));
  rb_define_const(mPDFConst, "DEST_FITPAGE", rb_str_new2("fitpage"));
  rb_define_const(mPDFConst, "DEST_FITWIDTH", rb_str_new2("fitwidth"));
  rb_define_const(mPDFConst, "DEST_FITHEIGHT", rb_str_new2("fitheight"));
  rb_define_const(mPDFConst, "DEST_FITBBOX", rb_str_new2("fitbbox"));

  /* annotation border style */
  rb_define_const(mPDFConst, "BORDER_SOLID", rb_str_new2("solid"));
  rb_define_const(mPDFConst, "BORDER_DASHED", rb_str_new2("dashed"));

  rb_include_module(cPDF, mPDFConst);
}
