/**************************************************************************//** * Info class method definitions for RMagick. * * Copyright © 2002 - 2009 by Timothy P. Hunter * * Changes since Nov. 2009 copyright © by Benjamin Thomas and Omer Bar-or * * @file rminfo.c * @version $Id: rminfo.c,v 1.79 2009/12/20 02:33:33 baror Exp $ * @author Tim Hunter ******************************************************************************/ #include "rmagick.h" /** * Return the value of the specified option. * * Ruby usage: * - @verbatim Info#get_option(key) @endverbatim * * @param self this object * @param key the option key * @return the value of key */ static VALUE get_option(VALUE self, const char *key) { Info *info; const char *value; Data_Get_Struct(self, Info, info); value = GetImageOption(info, key); if (value) { return rb_str_new2(value); } return Qnil; } /** * Set the specified option to this value. If the value is nil just unset any * current value. * * Ruby usage: * - @verbatim Info#set_option(key,string) @endverbatim * * @param self this object * @param key the option key * @param string the value * @return self */ static VALUE set_option(VALUE self, const char *key, VALUE string) { Info *info; char *value; Data_Get_Struct(self, Info, info); if (NIL_P(string)) { (void) RemoveImageOption(info, key); } else { value = StringValuePtr(string); (void) SetImageOption(info, key, value); } return self; } /** * Set a color name as the value of the specified option * * No Ruby usage (internal function) * * Notes: * - Call QueryColorDatabase to validate color name. * * @param self this object * @param option the option * @param color the color name * @return self */ static VALUE set_color_option(VALUE self, const char *option, VALUE color) { Info *info; char *name; PixelPacket pp; ExceptionInfo *exception; MagickBooleanType okay; Data_Get_Struct(self, Info, info); if (NIL_P(color)) { (void) RemoveImageOption(info, option); } else { exception = AcquireExceptionInfo(); name = StringValuePtr(color); okay = QueryColorDatabase(name, &pp, exception); (void) DestroyExceptionInfo(exception); if (!okay) { rb_raise(rb_eArgError, "invalid color name `%s'", name); } (void) RemoveImageOption(info, option); (void) SetImageOption(info, option, name); } return self; } /** * Get an Image::Info option floating-point value. * * No Ruby usage (internal function) * * Notes: * - Convert the string value to a float * * @param self this object * @param option the option name * @return the Image::Info option */ static VALUE get_dbl_option(VALUE self, const char *option) { Info *info; const char *value; double d; long n; Data_Get_Struct(self, Info, info); value = GetImageOption(info, option); if (!value) { return Qnil; } d = atof(value); n = (long) floor(d); return d == (double)n ? LONG2NUM(n) : rb_float_new(d); } /** * Set an Image::Info option to a floating-point value. * * No Ruby usage (internal function) * * Notes: * - SetImageOption expects the value to be a string. * * @param self this object * @param option the option name * @param value the value * @return self */ static VALUE set_dbl_option(VALUE self, const char *option, VALUE value) { Info *info; char buff[50]; double d; long n; int len; Data_Get_Struct(self, Info, info); if (NIL_P(value)) { (void) RemoveImageOption(info, option); } else { d = NUM2DBL(value); n = floor(d); if (d == n) { len = sprintf(buff, "%-10ld", n); } else { len = sprintf(buff, "%-10.2f", d); } memset(buff+len, '\0', sizeof(buff)-len); (void) RemoveImageOption(info, option); (void) SetImageOption(info, option, buff); } return self; } #if 0 /** * Convert a PixelPacket to a hex-format color name. * * No Ruby usage (internal function) * * @param pp the pixel packet * @param name pointer to the name * @return the name */ static char *pixel_packet_to_hexname(PixelPacket *pp, char *name) { MagickPixelPacket mpp; GetMagickPixelPacket(NULL, &mpp); rm_set_magick_pixel_packet(pp, &mpp); (void) GetColorTuple(&mpp, MagickTrue, name); return name; } #endif DEF_ATTR_ACCESSOR(Info, antialias, bool) /** Maximum length of a format (@see Info_aref) */ #define MAX_FORMAT_LEN 60 /** * Get the value of an option set by Info[]= * * Ruby usage: * - @verbatim Info[format, key] @endverbatim * - @verbatim Info[key] @endverbatim * * Notes: * - The 2 argument form is the original form. Added support for a single * argument after ImageMagick started using Set/GetImageOption for options * that aren't represented by fields in the ImageInfo structure. * * @param argc number of input arguments * @param argv array of input arguments * @param self this object * @return the option value * @see Info_aset */ VALUE Info_aref(int argc, VALUE *argv, VALUE self) { Info *info; char *format_p, *key_p; long format_l, key_l; const char *value; char fkey[MaxTextExtent]; switch (argc) { case 2: format_p = rm_str2cstr(argv[0], &format_l); key_p = rm_str2cstr(argv[1], &key_l); if (format_l > MAX_FORMAT_LEN || format_l + key_l > MaxTextExtent-1) { rb_raise(rb_eArgError, "can't reference %.60s:%.1024s - too long", format_p, key_p); } sprintf(fkey, "%.60s:%.*s", format_p, (int)(MaxTextExtent-61), key_p); break; case 1: strncpy(fkey, StringValuePtr(argv[0]), sizeof(fkey)-1); fkey[sizeof(fkey)-1] = '\0'; break; default: rb_raise(rb_eArgError, "wrong number of arguments (%d for 1 or 2)", argc); break; } Data_Get_Struct(self, Info, info); value = GetImageOption(info, fkey); if (!value) { return Qnil; } return rb_str_new2(value); } /** * Call SetImageOption * * Ruby usage: * - @verbatim Info[format, key]= @endverbatim * - @verbatim Info[key]= @endverbatim * * Notes: * - Essentially the same function as Info_define but paired with Info_aref * - If the value is nil it is equivalent to Info_undefine. * - The 2 argument form is the original form. Added support for a single * argument after ImageMagick started using Set/GetImageOption for options * that aren't represented by fields in the ImageInfo structure. * * @param argc number of input arguments * @param argv array of input arguments * @param self this object * @return self * @see Info_aref * @see Info_define * @see Info_undefine */ VALUE Info_aset(int argc, VALUE *argv, VALUE self) { Info *info; VALUE value; char *format_p, *key_p, *value_p = NULL; long format_l, key_l; char ckey[MaxTextExtent]; unsigned int okay; Data_Get_Struct(self, Info, info); switch (argc) { case 3: format_p = rm_str2cstr(argv[0], &format_l); key_p = rm_str2cstr(argv[1], &key_l); if (format_l > MAX_FORMAT_LEN || format_l+key_l > MaxTextExtent-1) { rb_raise(rb_eArgError, "%.60s:%.1024s not defined - too long", format_p, key_p); } (void) sprintf(ckey, "%.60s:%.*s", format_p, (int)(sizeof(ckey)-MAX_FORMAT_LEN), key_p); value = argv[2]; break; case 2: strncpy(ckey, StringValuePtr(argv[0]), sizeof(ckey)-1); ckey[sizeof(ckey)-1] = '\0'; value = argv[1]; break; default: rb_raise(rb_eArgError, "wrong number of arguments (%d for 2 or 3)", argc); break; } if (NIL_P(value)) { (void) RemoveImageOption(info, ckey); } else { /* Allow any argument that supports to_s */ value = rm_to_s(value); value_p = StringValuePtr(value); (void) RemoveImageOption(info, ckey); okay = SetImageOption(info, ckey, value_p); if (!okay) { rb_warn("`%s' not defined - SetImageOption failed.", ckey); return Qnil; } } RB_GC_GUARD(value); return self; } /** * Get the attenuate attribute. * * Ruby usage: * - @verbatim Info#attenuate @endverbatim * * @param self this object * @return the attenuate */ VALUE Info_attenuate(VALUE self) { return get_dbl_option(self, "attenuate"); } /** * Set the attenuate attribute. * * Ruby usage: * - @verbatim Info#attenuate= @endverbatim * * @param self this object * @param value the attenuate * @return self */ VALUE Info_attenuate_eq(VALUE self, VALUE value) { return set_dbl_option(self, "attenuate", value); } /** * Get the authenticate attribute. * * Ruby usage: * - @verbatim Info#authenticate @endverbatim * * @param self this object * @return the authenticate */ VALUE Info_authenticate(VALUE self) { Info *info; Data_Get_Struct(self, Info, info); if (info->authenticate) { return rb_str_new2(info->authenticate); } else { return Qnil; } } /** * Set the authenticate attribute. * * Ruby usage: * - @verbatim Info#authenticate= @endverbatim * * @param self this object * @param passwd the authenticating password * @return self */ VALUE Info_authenticate_eq(VALUE self, VALUE passwd) { Info *info; char *passwd_p = NULL; long passwd_l = 0; Data_Get_Struct(self, Info, info); if (!NIL_P(passwd)) { passwd_p = rm_str2cstr(passwd, &passwd_l); } if (info->authenticate) { magick_free(info->authenticate); info->authenticate = NULL; } if (passwd_l > 0) { magick_clone_string(&info->authenticate, passwd_p); } return self; } /** * Return the name of the background color as a String * * Ruby usage: * - @verbatim Info#background_color @endverbatim * * @param self this object * @return the name of the background color * @see Image_background_color */ VALUE Info_background_color(VALUE self) { Info *info; Data_Get_Struct(self, Info, info); return rm_pixelpacket_to_color_name_info(info, &info->background_color); } /** * Set the background color. * * Ruby usage: * - @verbatim Info#background_color= @endverbatim * * Notes: * - Color should be a string * * @param self this object * @param bc_arg the background color * @return self * @throw ArgumentError */ VALUE Info_background_color_eq(VALUE self, VALUE bc_arg) { Info *info; //char colorname[MaxTextExtent]; Data_Get_Struct(self, Info, info); Color_to_PixelPacket(&info->background_color, bc_arg); //SetImageOption(info, "background", pixel_packet_to_hexname(&info->background_color, colorname)); return self; } /** * Return the name of the border color as a String. * * Ruby usage: * - @verbatim Info#border_color @endverbatim * * @param self this object * @return the border color * @see Image_border_color */ VALUE Info_border_color(VALUE self) { Info *info; Data_Get_Struct(self, Info, info); return rm_pixelpacket_to_color_name_info(info, &info->border_color); } /** * set the border color * * Ruby usage: * - @verbatim Info#border_color= @endverbatim * * Notes: * - Color should be a string * * @param self this object * @param bc_arg the border color * @return self * @throw ArgumentError */ VALUE Info_border_color_eq(VALUE self, VALUE bc_arg) { Info *info; //char colorname[MaxTextExtent]; Data_Get_Struct(self, Info, info); Color_to_PixelPacket(&info->border_color, bc_arg); //SetImageOption(info, "bordercolor", pixel_packet_to_hexname(&info->border_color, colorname)); return self; } /** * Emulate the -caption option. * * Ruby usage: * - @verbatim Info#caption @endverbatim * * @param self this object * @return the caption */ VALUE Info_caption(VALUE self) { return get_option(self, "caption"); } /** * Emulate the -caption option. * * Ruby usage: * - @verbatim Info#caption= @endverbatim * * @param self this object * @param caption the caption * @return self */ VALUE Info_caption_eq(VALUE self, VALUE caption) { return set_option(self, "caption", caption); } /** * Set the channels * * Ruby usage: * - @verbatim Info#channel @endverbatim * - @verbatim Info#channel(channel) @endverbatim * - @verbatim Info#channel(channel, ...) @endverbatim * * Notes: * - Default channel is AllChannels * - Thanks to Douglas Sellers. * * @param argc number of input arguments * @param argv array of input arguments * @param self this object * @return self */ VALUE Info_channel(int argc, VALUE *argv, VALUE self) { Info *info; ChannelType channels; channels = extract_channels(&argc, argv); // Ensure all arguments consumed. if (argc > 0) { raise_ChannelType_error(argv[argc-1]); } Data_Get_Struct(self, Info, info); info->channel = channels; return self; } /** * Get the colorspace type. * * Ruby usage: * - @verbatim Info#colorspace @endverbatim * * @param self this object * @return the colorspace type */ VALUE Info_colorspace(VALUE self) { Info *info; Data_Get_Struct(self, Info, info); return ColorspaceType_new(info->colorspace); } /** * Set the colorspace type * * Ruby usage: * - @verbatim Info#colorspace= @endverbatim * * @param self this object * @param colorspace the colorspace type * @return self * @throw ArgumentError */ VALUE Info_colorspace_eq(VALUE self, VALUE colorspace) { Info *info; Data_Get_Struct(self, Info, info); VALUE_TO_ENUM(colorspace, info->colorspace, ColorspaceType); return self; } OPTION_ATTR_ACCESSOR(comment, Comment) /** * Get the compression type. * * Ruby usage: * - @verbatim Info#compression @endverbatim * * @param self this object * @return the compression type */ VALUE Info_compression(VALUE self) { Info *info; Data_Get_Struct(self, Info, info); return CompressionType_new(info->compression); } /** * Set the compression type * * Ruby usage: * - @verbatim Info#compression= @endverbatim * * @param self this object * @param type the compression type * @return self * @throw ArgumentError */ VALUE Info_compression_eq(VALUE self, VALUE type) { Info *info; Data_Get_Struct(self, Info, info); VALUE_TO_ENUM(type, info->compression, CompressionType); return self; } /** * Call SetImageOption * * Ruby usage: * - @verbatim Info#define(format, key) @endverbatim * - @verbatim Info#define(format, key, value) @endverbatim * * Notes: * - Default value is the empty string * - This is the only method in Info that is not an attribute accessor. * * @param argc number of input arguments * @param argv array of input arguments * @param self this object * @return self */ VALUE Info_define(int argc, VALUE *argv, VALUE self) { Info *info; char *format, *key; const char *value = ""; long format_l, key_l; char ckey[100]; unsigned int okay; VALUE fmt_arg; Data_Get_Struct(self, Info, info); switch (argc) { case 3: /* Allow any argument that supports to_s */ fmt_arg = rb_String(argv[2]); value = (const char *)StringValuePtr(fmt_arg); case 2: key = rm_str2cstr(argv[1], &key_l); format = rm_str2cstr(argv[0], &format_l); break; default: rb_raise(rb_eArgError, "wrong number of arguments (%d for 2 or 3)", argc); } if (2 + format_l + key_l > (long)sizeof(ckey)) { rb_raise(rb_eArgError, "%.20s:%.20s not defined - format or key too long", format, key); } (void) sprintf(ckey, "%s:%s", format, key); (void) RemoveImageOption(info, ckey); okay = SetImageOption(info, ckey, value); if (!okay) { rb_warn("%.20s=\"%.78s\" not defined - SetImageOption failed.", ckey, value); return Qnil; } RB_GC_GUARD(fmt_arg); return self; } /** * Get the delay attribute. * * Ruby usage: * - @verbatim Info#delay @endverbatim * * Notes: * - Convert from string to numeric * * @param self this object * @return the delay */ VALUE Info_delay(VALUE self) { Info *info; const char *delay; char *p; long d; Data_Get_Struct(self, Info, info); delay = GetImageOption(info, "delay"); if (delay) { d = strtol(delay, &p, 10); if (*p != '\0') { rb_raise(rb_eRangeError, "failed to convert %s to Numeric", delay); } return LONG2NUM(d); } return Qnil; } /** * Will raise an exception if `arg' can't be converted to an int. * * No Ruby usage (internal function) * * @param arg the argument * @return arg */ static VALUE arg_is_integer(VALUE arg) { int d = NUM2INT(arg); d = d; // satisfy icc return arg; } /** * Set the delay attribute. * * Ruby usage: * - @verbatim Info#delay= @endverbatim * * Notes: * - Convert from numeric value to string. * * @param self this object * @param string the delay * @return self */ VALUE Info_delay_eq(VALUE self, VALUE string) { Info *info; int delay; int not_num; char dstr[20]; Data_Get_Struct(self, Info, info); if (NIL_P(string)) { (void) RemoveImageOption(info, "delay"); } else { not_num = 0; (void) rb_protect(arg_is_integer, string, ¬_num); if (not_num) { rb_raise(rb_eTypeError, "failed to convert %s into Integer", rb_class2name(CLASS_OF(string))); } delay = NUM2INT(string); sprintf(dstr, "%d", delay); (void) RemoveImageOption(info, "delay"); (void) SetImageOption(info, "delay", dstr); } return self; } /** * Get the density attribute * * Ruby usage: * - @verbatim Info#density @endverbatim * * @param self this object * @return the density */ DEF_ATTR_READER(Info, density, str) /** * Set the text rendering density * * Ruby usage: * - @verbatim Info#density= @endverbatim * * Notes: * - density should be a string, e.g., "72x72" * * @param self this object * @param density_arg the density * @return self * @throw ArgumentError */ VALUE Info_density_eq(VALUE self, VALUE density_arg) { Info *info; VALUE density; char *dens; Data_Get_Struct(self, Info, info); if (NIL_P(density_arg)) { magick_free(info->density); info->density = NULL; return self; } density = rm_to_s(density_arg); dens = StringValuePtr(density); if (!IsGeometry(dens)) { rb_raise(rb_eArgError, "invalid density geometry: %s", dens); } magick_clone_string(&info->density, dens); RB_GC_GUARD(density); return self; } /** * Get the depth attribute * * Ruby usage: * - @verbatim Info#depth @endverbatim * * @param self this object * @return the depth */ DEF_ATTR_READER(Info, depth, int) /** * Set the depth (8, 16, 32). * * Ruby usage: * - @verbatim Info#depth= @endverbatim * * @param self this object * @param depth the depth * @return self * @throw ArgumentError */ VALUE Info_depth_eq(VALUE self, VALUE depth) { Info *info; unsigned long d; Data_Get_Struct(self, Info, info); d = NUM2ULONG(depth); switch (d) { case 8: // always okay #if MAGICKCORE_QUANTUM_DEPTH == 16 || MAGICKCORE_QUANTUM_DEPTH == 32 || MAGICKCORE_QUANTUM_DEPTH == 64 case 16: #if MAGICKCORE_QUANTUM_DEPTH == 32 || MAGICKCORE_QUANTUM_DEPTH == 64 case 32: #if MAGICKCORE_QUANTUM_DEPTH == 64 case 64: #endif #endif #endif break; default: rb_raise(rb_eArgError, "invalid depth (%lu)", d); break; } info->depth = d; return self; } /** A dispose option */ static struct { const char *string; /**< the argument given by the user */ const char *enum_name; /**< the enumerator name */ DisposeType enumerator; /**< the enumerator itself */ } Dispose_Option[] = { { "Background", "BackgroundDispose", BackgroundDispose}, { "None", "NoneDispose", NoneDispose}, { "Previous", "PreviousDispose", PreviousDispose}, { "Undefined", "UndefinedDispose", UndefinedDispose}, { "0", "UndefinedDispose", UndefinedDispose}, { "1", "NoneDispose", NoneDispose}, { "2", "BackgroundDispose", BackgroundDispose}, { "3", "PreviousDispose", PreviousDispose}, }; /** Number of dispose options */ #define N_DISPOSE_OPTIONS (int)(sizeof(Dispose_Option)/sizeof(Dispose_Option[0])) /** * Retrieve a dispose option string and convert it to a DisposeType enumerator. * * No Ruby usage (internal function) * * @param name the dispose string * @return the DisposeType enumerator */ DisposeType rm_dispose_to_enum(const char *name) { DisposeType dispose = UndefinedDispose; int x; for (x = 0; x < N_DISPOSE_OPTIONS; x++) { if (strcmp(Dispose_Option[x].string, name) == 0) { dispose = Dispose_Option[x].enumerator; break; } } return dispose; } /** * Retrieve the dispose option string and convert it to a DisposeType * enumerator. * * Ruby usage: * - @verbatim Info#dispose @endverbatim * * @param self this object * @return a DisposeType enumerator */ VALUE Info_dispose(VALUE self) { Info *info; int x; ID dispose_id; const char *dispose; Data_Get_Struct(self, Info, info); dispose_id = rb_intern("UndefinedDispose"); // Map the dispose option string to a DisposeType enumerator. dispose=GetImageOption(info, "dispose"); if (dispose) { for (x = 0; x < N_DISPOSE_OPTIONS; x++) { if (strcmp(dispose, Dispose_Option[x].string) == 0) { dispose_id = rb_intern(Dispose_Option[x].enum_name); break; } } } return rb_const_get(Module_Magick, dispose_id); } /** * Convert a DisposeType enumerator into the equivalent dispose option string. * * Ruby usage: * - @verbatim Info#dispose= @endverbatim * * @param self this object * @param disp the DisposeType enumerator * @return self */ VALUE Info_dispose_eq(VALUE self, VALUE disp) { Info *info; DisposeType dispose; const char *option; int x; Data_Get_Struct(self, Info, info); if (NIL_P(disp)) { (void) RemoveImageOption(info, "dispose"); return self; } VALUE_TO_ENUM(disp, dispose, DisposeType); option = "Undefined"; for (x = 0; x < N_DISPOSE_OPTIONS; x++) { if (dispose == Dispose_Option[x].enumerator) { option = Dispose_Option[x].string; break; } } (void) SetImageOption(info, "dispose", option); return self; } DEF_ATTR_ACCESSOR(Info, dither, bool) /** * Get the endian attribute. * * Ruby usage: * - @verbatim Info#endian @endverbatim * * @param self this object * @return the endian (Magick::MSBEndian or Magick::LSBEndian) */ VALUE Info_endian(VALUE self) { Info *info; Data_Get_Struct(self, Info, info); return EndianType_new(info->endian); } /** * Set the endian attribute. * * Ruby usage: * - @verbatim Info#endian= @endverbatim * * @param self this object * @param endian the endian (Magick::MSBEndian or Magick::LSBEndian) * @return self */ VALUE Info_endian_eq(VALUE self, VALUE endian) { Info *info; EndianType type = UndefinedEndian; if (endian != Qnil) { VALUE_TO_ENUM(endian, type, EndianType); } Data_Get_Struct(self, Info, info); info->endian = type; return self; } /** * Get the extract string, e.g. "200x200+100+100" * * Ruby usage: * - @verbatim Info#extract @endverbatim * * Notes: * - Defined for ImageMagick 5.5.6 and later * * @param self this object * @return the extract string */ DEF_ATTR_READER(Info, extract, str) /** * Set the extract string, e.g. "200x200+100+100" * * Ruby usage: * - @verbatim Info#extract= @endverbatim * * Notes: * - Defined for ImageMagick 5.5.6 and later * * @param self this object * @param extract_arg the extract string * @return self * @throw ArgumentError */ VALUE Info_extract_eq(VALUE self, VALUE extract_arg) { Info *info; char *extr; VALUE extract; Data_Get_Struct(self, Info, info); if (NIL_P(extract_arg)) { magick_free(info->extract); info->extract = NULL; return self; } extract = rm_to_s(extract_arg); extr = StringValuePtr(extract); if (!IsGeometry(extr)) { rb_raise(rb_eArgError, "invalid extract geometry: %s", extr); } magick_clone_string(&info->extract, extr); RB_GC_GUARD(extract); return self; } /** * Get the "filename". * * Ruby usage: * - @verbatim Info#filename @endverbatim * * Notes: * - Only used for Image_capture * * @param self this object * @return the filename ("" if filename not set) * @see Image_capture */ VALUE Info_filename(VALUE self) { Info *info; Data_Get_Struct(self, Info, info); return rb_str_new2(info->filename); } /** * Set the "filename". * * Ruby usage: * - @verbatim Info#filename= @endverbatim * * Notes: * - Only used for Image_capture * * @param self this object * @param filename the filename * @return self * @see Image_capture */ VALUE Info_filename_eq(VALUE self, VALUE filename) { Info *info; char *fname; Data_Get_Struct(self, Info, info); // Allow "nil" - remove current filename if (NIL_P(filename) || StringValuePtr(filename) == NULL) { info->filename[0] = '\0'; } else { // Otherwise copy in filename fname = StringValuePtr(filename); strncpy(info->filename, fname, MaxTextExtent); } return self; } /** * Return the fill color as a String. * * Ruby usage: * - @verbatim Info#fill @endverbatim * * @param self this object * @return the fill color */ VALUE Info_fill(VALUE self) { return get_option(self, "fill"); } /** * Set the fill color * * Ruby usage: * - @verbatim Info#fill= @endverbatim * * @param self this object * @param color the fill color (as a String) * @return self * @throw ArgumentError */ VALUE Info_fill_eq(VALUE self, VALUE color) { return set_color_option(self, "fill", color); } /** * Get the text font. * * Ruby usage: * - @verbatim Info#font @endverbatim * * @param self this object * @return the font */ DEF_ATTR_READER(Info, font, str) /** * Set the text font. * * Ruby usage: * - @verbatim Info#font= @endverbatim * * @param self this object * @param font_arg the font (as a String) * @return self */ VALUE Info_font_eq(VALUE self, VALUE font_arg) { Info *info; char *font; Data_Get_Struct(self, Info, info); if (NIL_P(font_arg) || StringValuePtr(font_arg) == NULL) { magick_free(info->font); info->font = NULL; } else { font = StringValuePtr(font_arg); magick_clone_string(&info->font, font); } return self; } /** * Return the image encoding format. * * Ruby usage: * - @verbatim Info#format @endverbatim * * @param self this object * @return the encoding format */ VALUE Info_format(VALUE self) { Info *info; const MagickInfo *magick_info ; ExceptionInfo *exception; Data_Get_Struct(self, Info, info); if (*info->magick) { exception = AcquireExceptionInfo(); magick_info = GetMagickInfo(info->magick, exception); (void) DestroyExceptionInfo(exception); return magick_info ? rb_str_new2(magick_info->name) : Qnil; } return Qnil; } /** * Set the image encoding format. * * Ruby usage: * - @verbatim Info#format= @endverbatim * * @param self this object * @param magick the encoding format * @return self */ VALUE Info_format_eq(VALUE self, VALUE magick) { Info *info; const MagickInfo *m; char *mgk; ExceptionInfo *exception; Data_Get_Struct(self, Info, info); exception = AcquireExceptionInfo(); mgk = StringValuePtr(magick); m = GetMagickInfo(mgk, exception); CHECK_EXCEPTION() (void) DestroyExceptionInfo(exception); if (!m) { rb_raise(rb_eArgError, "unknown format: %s", mgk); } strncpy(info->magick, m->name, MaxTextExtent-1); return self; } /** * Get the fuzz. * * Ruby usage: * - @verbatim Info#fuzz @endverbatim * * @param self this object * @return the fuzz * @see Image_fuzz */ DEF_ATTR_READER(Info, fuzz, dbl) /** * Set the fuzz. * * Ruby usage: * - @verbatim Info#fuzz=number @endverbatim * - @verbatim Info#fuzz=NN% @endverbatim * * @param self this object * @param fuzz the fuzz * @return self * @see Image_fuzz_eq */ VALUE Info_fuzz_eq(VALUE self, VALUE fuzz) { Info *info; Data_Get_Struct(self, Info, info); info->fuzz = rm_fuzz_to_dbl(fuzz); return self; } /** A gravity option */ static struct { const char *string; /**< the argument given by the user */ const char *enum_name; /**< the enumerator name */ GravityType enumerator; /**< the enumerator itself */ } Gravity_Option[] = { { "Undefined", "UndefinedGravity", UndefinedGravity}, { "None", "UndefinedGravity", UndefinedGravity}, { "Center", "CenterGravity", CenterGravity}, { "East", "EastGravity", EastGravity}, { "Forget", "ForgetGravity", ForgetGravity}, { "NorthEast", "NorthEastGravity", NorthEastGravity}, { "North", "NorthGravity", NorthGravity}, { "NorthWest", "NorthWestGravity", NorthWestGravity}, { "SouthEast", "SouthEastGravity", SouthEastGravity}, { "South", "SouthGravity", SouthGravity}, { "SouthWest", "SouthWestGravity", SouthWestGravity}, { "West", "WestGravity", WestGravity}, { "Static", "StaticGravity", StaticGravity} }; /** Number of gravity options */ #define N_GRAVITY_OPTIONS (int)(sizeof(Gravity_Option)/sizeof(Gravity_Option[0])) /** * Return the value of the gravity option as a GravityType enumerator. * * No Ruby usage (internal function) * * @param name the name of the gravity option * @return the enumerator for name */ GravityType rm_gravity_to_enum(const char *name) { GravityType gravity = UndefinedGravity; int x; for (x = 0; x < N_GRAVITY_OPTIONS; x++) { if (strcmp(name, Gravity_Option[x].string) == 0) { gravity = Gravity_Option[x].enumerator; break; } } return gravity; } /** * Return the value of the gravity option as a GravityType enumerator. * * Ruby usage: * - @verbatim Info#gravity @endverbatim * * @param self this object * @return the gravity enumerator */ VALUE Info_gravity(VALUE self) { Info *info; const char *gravity; int x; ID gravity_id; Data_Get_Struct(self, Info, info); gravity_id = rb_intern("UndefinedGravity"); // Map the gravity option string to a GravityType enumerator. gravity=GetImageOption(info, "gravity"); if (gravity) { for (x = 0; x < N_GRAVITY_OPTIONS; x++) { if (strcmp(gravity, Gravity_Option[x].string) == 0) { gravity_id = rb_intern(Gravity_Option[x].enum_name); break; } } } return rb_const_get(Module_Magick, gravity_id); } /** * Convert a GravityType enum to a gravity option name and store in the Info * structure. * * Ruby usage: * - @verbatim Info#gravity= @endverbatim * * @param self this object * @param grav the gravity enumerator * @return self */ VALUE Info_gravity_eq(VALUE self, VALUE grav) { Info *info; GravityType gravity; const char *option; int x; Data_Get_Struct(self, Info, info); if (NIL_P(grav)) { (void) RemoveImageOption(info, "gravity"); return self; } VALUE_TO_ENUM(grav, gravity, GravityType); option = "Undefined"; for (x = 0; x < N_GRAVITY_OPTIONS; x++) { if (gravity == Gravity_Option[x].enumerator) { option = Gravity_Option[x].string; break; } } (void) SetImageOption(info, "gravity", option); return self; } DEF_ATTR_ACCESSOR(Info, group, long) /** * Get the classification type. * * Ruby usage: * - @verbatim Info#image_type @endverbatim * * @param self this object * @return the classification type */ VALUE Info_image_type(VALUE self) { Info *info; Data_Get_Struct(self, Info, info); return ImageType_new(info->type); } /** * Set the classification type. * * Ruby usage: * - @verbatim Info#image_type= @endverbatim * * @param self this object * @param type the classification type * @return self * @throw ArgumentError */ VALUE Info_image_type_eq(VALUE self, VALUE type) { Info *info; Data_Get_Struct(self, Info, info); VALUE_TO_ENUM(type, info->type, ImageType); return self; } /** * Get the interlace type. * * Ruby usage: * - @verbatim Info#interlace @endverbatim * * @param self this object * @return the interlace type */ VALUE Info_interlace(VALUE self) { Info *info; Data_Get_Struct(self, Info, info); return InterlaceType_new(info->interlace); } /** * Set the interlace type * * Ruby usage: * - @verbatim Info#interlace= @endverbatim * * @param self this object * @param inter the interlace type * @return self * @throw ArgumentError */ VALUE Info_interlace_eq(VALUE self, VALUE inter) { Info *info; Data_Get_Struct(self, Info, info); VALUE_TO_ENUM(inter, info->interlace, InterlaceType); return self; } OPTION_ATTR_ACCESSOR(label, Label) /** * Return the name of the matte color as a String. * * Ruby usage: * - @verbatim Info#matte_color @endverbatim * * @param self this object * @return the name of the matte color * @see Image_matte_color */ VALUE Info_matte_color(VALUE self) { Info *info; Data_Get_Struct(self, Info, info); return rm_pixelpacket_to_color_name_info(info, &info->matte_color); } /** * Set the matte color. * * Ruby usage: * - @verbatim Info#matte_color= @endverbatim * * @param self this object * @param matte_arg the name of the matte as a String * @return self * @throw ArgumentError */ VALUE Info_matte_color_eq(VALUE self, VALUE matte_arg) { Info *info; //char colorname[MaxTextExtent]; Data_Get_Struct(self, Info, info); Color_to_PixelPacket(&info->matte_color, matte_arg); //SetImageOption(info, "mattecolor", pixel_packet_to_hexname(&info->matte_color, colorname)); return self; } /** * Establish a progress monitor. * * Ruby usage: * - @verbatim Info#monitor= @endverbatim * * @param self this object * @param monitor the monitor * @return self * @see Image_monitor_eq */ VALUE Info_monitor_eq(VALUE self, VALUE monitor) { Info *info; Data_Get_Struct(self, Info, info); if (NIL_P(monitor)) { info->progress_monitor = NULL; } else { (void) SetImageInfoProgressMonitor(info, rm_progress_monitor, (void *)monitor); } return self; } DEF_ATTR_ACCESSOR(Info, monochrome, bool) DEF_ATTR_ACCESSOR(Info, number_scenes, ulong) /** * Return the orientation attribute as an OrientationType enum value. * * Ruby usage: * - @verbatim Info#orientation @endverbatim * * @param self this object * @return the orientation */ VALUE Info_orientation(VALUE self) { Info *info; Data_Get_Struct(self, Info, info); return OrientationType_new(info->orientation); } /** * Set the Orientation type. * * Ruby usage: * - @verbatim Info#Orientation= @endverbatim * * @param self this object * @param inter the orientation type as an OrientationType enum value * @return self * @throw ArgumentError */ VALUE Info_orientation_eq(VALUE self, VALUE inter) { Info *info; Data_Get_Struct(self, Info, info); VALUE_TO_ENUM(inter, info->orientation, OrientationType); return self; } /** * Return origin geometry. * * Ruby usage: * - @verbatim Info#origin @endverbatim * * @param self this object * @return the origin geometry */ VALUE Info_origin(VALUE self) { Info *info; const char *origin; Data_Get_Struct(self, Info, info); origin = GetImageOption(info, "origin"); return origin ? rb_str_new2(origin) : Qnil; } /** * Set origin geometry. Argument may be a Geometry object as well as a geometry * string. * * Ruby usage: * - @verbatim Info#origin=+-x+-y @endverbatim * * @param self this object * @param origin_arg the origin geometry * @return self */ VALUE Info_origin_eq(VALUE self, VALUE origin_arg) { Info *info; VALUE origin_str; char *origin; Data_Get_Struct(self, Info, info); if (NIL_P(origin_arg)) { (void) RemoveImageOption(info, "origin"); return self; } origin_str = rm_to_s(origin_arg); origin = GetPageGeometry(StringValuePtr(origin_str)); if (IsGeometry(origin) == MagickFalse) { rb_raise(rb_eArgError, "invalid origin geometry: %s", origin); } (void) SetImageOption(info, "origin", origin); RB_GC_GUARD(origin_str); return self; } /** * Get the Postscript page geometry. * * Ruby usage: * - @verbatim Info_page @endverbatim * * @param self this object * @return the page geometry */ VALUE Info_page(VALUE self) { Info *info; Data_Get_Struct(self, Info, info); return info->page ? rb_str_new2(info->page) : Qnil; } /** * Store the Postscript page geometry. Argument may be a Geometry object as well * as a geometry string. * * Ruby usage: * - @verbatim Info#page= @endverbatim * * @param self this object * @param page_arg the geometry * @return self */ VALUE Info_page_eq(VALUE self, VALUE page_arg) { Info *info; VALUE geom_str; char *geometry; Data_Get_Struct(self, Info, info); if (NIL_P(page_arg)) { magick_free(info->page); info->page = NULL; return self; } geom_str = rm_to_s(page_arg); geometry=GetPageGeometry(StringValuePtr(geom_str)); if (*geometry == '\0') { magick_free(info->page); info->page = NULL; return self; } magick_clone_string(&info->page, geometry); RB_GC_GUARD(geom_str); return self; } DEF_ATTR_ACCESSOR(Info, pointsize, dbl) DEF_ATTR_ACCESSOR(Info, quality, ulong) /** * Get sampling factors used by JPEG or MPEG-2 encoder and YUV decoder/encoder. * * Ruby usage: * - @verbatim Info#sampling_factor @endverbatim * * @param self this object * @return the sampling factors */ VALUE Info_sampling_factor(VALUE self) { Info *info; Data_Get_Struct(self, Info, info); if (info->sampling_factor) { return rb_str_new2(info->sampling_factor); } else { return Qnil; } } /** * Set sampling factors used by JPEG or MPEG-2 encoder and YUV decoder/encoder. * * Ruby usage: * - @verbatim Info#sampling_factor= @endverbatim * * @param self this object * @param sampling_factor the sampling factors * @return self */ VALUE Info_sampling_factor_eq(VALUE self, VALUE sampling_factor) { Info *info; char *sampling_factor_p = NULL; long sampling_factor_len = 0; Data_Get_Struct(self, Info, info); if (!NIL_P(sampling_factor)) { sampling_factor_p = rm_str2cstr(sampling_factor, &sampling_factor_len); } if (info->sampling_factor) { magick_free(info->sampling_factor); info->sampling_factor = NULL; } if (sampling_factor_len > 0) { magick_clone_string(&info->sampling_factor, sampling_factor_p); } return self; } /** * Get the scene number. * * Ruby usage: * - @verbatim Info#scene @endverbatim * * @param self this object * @return the scene number */ VALUE Info_scene(VALUE self) { Info *info; Data_Get_Struct(self, Info, info); return ULONG2NUM(info->scene); } /** * Set the scene number. * * Ruby usage: * - @verbatim Info#scene= @endverbatim * * @param self this object * @param scene the scene number * @return self */ VALUE Info_scene_eq(VALUE self, VALUE scene) { Info *info; char buf[25]; Data_Get_Struct(self, Info, info); info->scene = NUM2ULONG(scene); #if defined(HAVE_SNPRINTF) (void) snprintf(buf, sizeof(buf), "%-ld", info->scene); #else (void) sprintf(buf, "%-l", info->scene); #endif (void) SetImageOption(info, "scene", buf); return self; } /** * Get the server name. * * Ruby usage: * - @verbatim Info#server_name @endverbatim * * @param self this object * @return the server name */ DEF_ATTR_READER(Info, server_name, str) /** * Set the server name. * * Ruby usage: * - @verbatim Info#server_name= @endverbatim * * @param self this object * @param server_arg the server name as a String * @return self */ VALUE Info_server_name_eq(VALUE self, VALUE server_arg) { Info *info; char *server; Data_Get_Struct(self, Info, info); if (NIL_P(server_arg) || StringValuePtr(server_arg) == NULL) { magick_free(info->server_name); info->server_name = NULL; } else { server = StringValuePtr(server_arg); magick_clone_string(&info->server_name, server); } return self; } /** * Get ths size * * Ruby usage: * - @verbatim Info#size @endverbatim * * @param self this object * @return the size as a Geometry object */ DEF_ATTR_READER(Info, size, str) /** * Set the size (either as a Geometry object or a Geometry string, i.e. * WxH{+-}x{+-}y) * * Ruby usage: * - @verbatim Info#size= @endverbatim * * @param self this object * @param size_arg the size * @return self * @throw ArgumentError */ VALUE Info_size_eq(VALUE self, VALUE size_arg) { Info *info; VALUE size; char *sz; Data_Get_Struct(self, Info, info); if (NIL_P(size_arg)) { magick_free(info->size); info->size = NULL; return self; } size = rm_to_s(size_arg); sz = StringValuePtr(size); if (!IsGeometry(sz)) { rb_raise(rb_eArgError, "invalid size geometry: %s", sz); } magick_clone_string(&info->size, sz); RB_GC_GUARD(size); return self; } /** * Return the stroke color as a String. * * Ruby usage: * - @verbatim Info#stroke @endverbatim * * @param self this object * @return the stroke color */ VALUE Info_stroke(VALUE self) { return get_option(self, "stroke"); } /** * Set the stroke color * * Ruby usage: * - @verbatim Info#stroke= @endverbatim * * @param self this object * @param color the stroke color as a String * @return self * @throw ArgumentError */ VALUE Info_stroke_eq(VALUE self, VALUE color) { return set_color_option(self, "stroke", color); } /** * Support for caption: format. * * Ruby usage: * - @verbatim Info#stroke_width @endverbatim * * Notes: * - Supported in ImageMagick >= 6.3.2-6 * * @param self this object * @return the stroke width */ VALUE Info_stroke_width(VALUE self) { return get_dbl_option(self, "strokewidth"); } /** * Support for caption: format. * * Ruby usage: * - @verbatim Info#stroke_width= @endverbatim * * Notes: * - Supported in ImageMagick >= 6.3.2-6 * * @param self this object * @param stroke_width the stroke width * @return self */ VALUE Info_stroke_width_eq(VALUE self, VALUE stroke_width) { return set_dbl_option(self, "strokewidth", stroke_width); } /** * Set name of texture to tile onto the image background. * * Ruby usage: * - @verbatim Image::Info#texture= @endverbatim * * @param self this object * @param texture the name of the texture image * @return self */ VALUE Info_texture_eq(VALUE self, VALUE texture) { Info *info; Image *image; char name[MaxTextExtent]; Data_Get_Struct(self, Info, info); // Delete any existing texture file if (info->texture) { rm_delete_temp_image(info->texture); magick_free(info->texture); info->texture = NULL; } // If argument is nil we're done if (texture == Qnil) { return self; } // Create a temp copy of the texture and store its name in the texture field image = rm_check_destroyed(texture); rm_write_temp_image(image, name); magick_clone_string(&info->texture, name); return self; } /** * info.tile_offset = [+/-]x[+/-]y. * * Ruby usage: * - @verbatim Image::Info#tile_offset= @endverbatim * * @param self this object * @param offset the offset * @return self */ VALUE Info_tile_offset_eq(VALUE self, VALUE offset) { Info *info; VALUE offset_str; char *tile_offset; offset_str = rm_to_s(offset); tile_offset = StringValuePtr(offset_str); if (!IsGeometry(tile_offset)) { rb_raise(rb_eArgError, "invalid tile offset geometry: %s", tile_offset); } Data_Get_Struct(self, Info, info); (void) DeleteImageOption(info, "tile-offset"); (void) SetImageOption(info, "tile-offset", tile_offset); RB_GC_GUARD(offset_str); return self; } /** * Return the name of the transparent color as a String. * * Ruby usage: * - @verbatim Info#transparent_color @endverbatim * * @param self this object * @return the name of the transparent color * @see Image_transparent_color */ VALUE Info_transparent_color(VALUE self) { Info *info; Data_Get_Struct(self, Info, info); return rm_pixelpacket_to_color_name_info(info, &info->transparent_color); } /** * Set the transparent color. * * Ruby usage: * - @verbatim Info#transparent_color= @endverbatim * * @param self this object * @param tc_arg the transparent color as a String * @return self * @throw ArgumentError */ VALUE Info_transparent_color_eq(VALUE self, VALUE tc_arg) { Info *info; //char colorname[MaxTextExtent]; Data_Get_Struct(self, Info, info); Color_to_PixelPacket(&info->transparent_color, tc_arg); //SetImageOption(info, "transparent", pixel_packet_to_hexname(&info->transparent_color, colorname)); return self; } /** * Return tile_offset attribute values. * * Ruby usage: * - @verbatim Image::Info#tile_offset @endverbatim * * @param self this object * @return the tile offset */ VALUE Info_tile_offset(VALUE self) { Info *info; const char *tile_offset; Data_Get_Struct(self, Info, info); tile_offset = GetImageOption(info, "tile-offset"); if (!tile_offset) { return Qnil; } return rb_str_new2(tile_offset); } /** * Undefine image option. * * Ruby usage: * - @verbatim Info#undefine(format,key) @endverbatim * * @param self this object * @param format the format * @param key the key * @return self */ VALUE Info_undefine(VALUE self, VALUE format, VALUE key) { Info *info; char *format_p, *key_p; long format_l, key_l; char fkey[MaxTextExtent]; format_p = rm_str2cstr(format, &format_l); key_p = rm_str2cstr(key, &key_l); if (format_l > MAX_FORMAT_LEN || format_l + key_l > MaxTextExtent) { rb_raise(rb_eArgError, "can't undefine %.60s:%.1024s - too long", format_p, key_p); } sprintf(fkey, "%.60s:%.*s", format_p, (int)(MaxTextExtent-61), key_p); Data_Get_Struct(self, Info, info); /* Depending on the IM version, RemoveImageOption returns either */ /* char * or MagickBooleanType. Ignore the return value. */ (void) RemoveImageOption(info, fkey); return self; } /** * Return the undercolor color as a String. * * Ruby usage: * - @verbatim Info#undercolor @endverbatim * * @param self this object * @return the undercolor */ VALUE Info_undercolor(VALUE self) { return get_option(self, "undercolor"); } /** * Set the undercolor color. * * Ruby usage: * - @verbatim Info#undercolor= @endverbatim * * @param self this object * @param color the undercolor color as a String * @return self * @throw ArgumentError */ VALUE Info_undercolor_eq(VALUE self, VALUE color) { return set_color_option(self, "undercolor", color); } /** * Get the resolution type. * * Ruby usage: * - @verbatim Info#units @endverbatim * * @param self this object * @return the resolution type */ VALUE Info_units(VALUE self) { Info *info; Data_Get_Struct(self, Info, info); return ResolutionType_new(info->units); } /** * Set the resolution type * * Ruby usage: * - @verbatim Info#units= @endverbatim * * @param self this object * @param units the resolution type * @return self * @throw ArgumentError */ VALUE Info_units_eq(VALUE self, VALUE units) { Info *info; Data_Get_Struct(self, Info, info); VALUE_TO_ENUM(units, info->units, ResolutionType); return self; } /** * Get FlashPix viewing parameters. * * Ruby usage: * - @verbatim Info#view @endverbatim * * @param self this object. * @return the viewing parameters */ DEF_ATTR_READER(Info, view, str) /** * Set FlashPix viewing parameters. * * Ruby usage: * - @verbatim Info#view= @endverbatim * * @param self this object * @param view_arg the viewing parameters * @return self */ VALUE Info_view_eq(VALUE self, VALUE view_arg) { Info *info; char *view; Data_Get_Struct(self, Info, info); if (NIL_P(view_arg) || StringValuePtr(view_arg) == NULL) { magick_free(info->view); info->view = NULL; } else { view = StringValuePtr(view_arg); magick_clone_string(&info->view, view); } return self; } /** * If there is a texture image, delete it before destroying the ImageInfo * structure. * * No Ruby usage (internal function) * * @param infoptr pointer to the Info object */ static void destroy_Info(void *infoptr) { Info *info = (Info *)infoptr; if (info->texture) { rm_delete_temp_image(info->texture); magick_free(info->texture); info->texture = NULL; } (void) DestroyImageInfo(info); } /** * Create an ImageInfo object. * * No Ruby usage (internal function) * * @param class the Ruby class to use * @return a new ImageInfo object */ VALUE Info_alloc(VALUE class) { Info *info; VALUE info_obj; info = CloneImageInfo(NULL); if (!info) { rb_raise(rb_eNoMemError, "not enough memory to initialize Info object"); } info_obj = Data_Wrap_Struct(class, NULL, destroy_Info, info); RB_GC_GUARD(info_obj); return info_obj; } /** * Provide a Info.new method for internal use. * * No Ruby usage (internal function) * * Notes: * - Takes no parameters, but runs the parm block if present * * @return a new ImageInfo object */ VALUE rm_info_new(void) { VALUE info_obj; info_obj = Info_alloc(Class_Info); RB_GC_GUARD(info_obj); return Info_initialize(info_obj); } /** * If an initializer block is present, run it. * * Ruby usage: * - @verbatim Info#initialize @endverbatim * * @param self this object * @return self */ VALUE Info_initialize(VALUE self) { if (rb_block_given_p()) { // Run the block in self's context (void) rb_obj_instance_eval(0, NULL, self); } return self; }