rmagick 7.1.4 → 7.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -47,6 +47,25 @@ get_option(VALUE self, const char *key)
47
47
  return Qnil;
48
48
  }
49
49
 
50
+ /**
51
+ * Raise ArgumentError if the option is one that ReadImage runs through
52
+ * InterpretImageProperties() and the value would make it read the file it names.
53
+ *
54
+ * No Ruby usage (internal function)
55
+ *
56
+ * @param key the option key
57
+ * @param value the value
58
+ */
59
+ static void
60
+ check_interpreted_option(const char *key, const char *value)
61
+ {
62
+ if ((rm_strcasecmp(key, "caption") == 0 || rm_strcasecmp(key, "comment") == 0 || rm_strcasecmp(key, "label") == 0)
63
+ && rm_has_file_reference(value))
64
+ {
65
+ rb_raise(rb_eArgError, "the %s option must not name a file with '@'", key);
66
+ }
67
+ }
68
+
50
69
  /**
51
70
  * Set the specified option to this value. If the value is nil just unset any
52
71
  * current value.
@@ -74,6 +93,7 @@ set_option(VALUE self, const char *key, VALUE string)
74
93
  char *value;
75
94
 
76
95
  value = StringValueCStr(string);
96
+ check_interpreted_option(key, value);
77
97
  SetImageOption(info, key, value);
78
98
  }
79
99
  return string;
@@ -306,6 +326,9 @@ Info_aref(int argc, VALUE *argv, VALUE self)
306
326
  *
307
327
  * - Essentially the same function as {Info#define} but paired with {Info#[]}
308
328
  * - If the value is nil it is equivalent to {Info#undefine}.
329
+ * - A value whose first non-blank character is '@', or that contains a %[fx:@...], %[hex:@...] or
330
+ * %[pixel:@...] escape, is rejected for the "caption", "comment" and "label" keys because
331
+ * ImageMagick reads the file it names.
309
332
  *
310
333
  * @overload []=(format, key)
311
334
  * @param format [String] An image format name such as "ps" or "tiff".
@@ -315,6 +338,7 @@ Info_aref(int argc, VALUE *argv, VALUE self)
315
338
  * @param key [String] A string that identifies the option.
316
339
  *
317
340
  * @return [Magick::Image::Info] self
341
+ * @raise [ArgumentError] if the "caption", "comment" or "label" value names a file with '@'
318
342
  * @see #[]
319
343
  * @see #define
320
344
  * @see #undefine
@@ -368,6 +392,7 @@ Info_aset(int argc, VALUE *argv, VALUE self)
368
392
  /* Allow any argument that supports to_s */
369
393
  value = rb_String(value);
370
394
  value_p = StringValueCStr(value);
395
+ check_interpreted_option(ckey, value_p);
371
396
 
372
397
  okay = SetImageOption(info, ckey, value_p);
373
398
  if (!okay)
@@ -552,10 +577,13 @@ Info_caption(VALUE self)
552
577
 
553
578
 
554
579
  /**
555
- * Assigns a caption to an image.
580
+ * Assigns a caption to an image. A caption whose first non-blank character is '@', or that
581
+ * contains a %[fx:@...], %[hex:@...] or %[pixel:@...] escape, is rejected because ImageMagick
582
+ * reads the file it names.
556
583
  *
557
584
  * @param caption [String] the caption
558
585
  * @return [String] the given value
586
+ * @raise [ArgumentError] if the caption names a file with '@'
559
587
  */
560
588
  VALUE
561
589
  Info_caption_eq(VALUE self, VALUE caption)
@@ -637,10 +665,13 @@ VALUE Info_comment(VALUE self)
637
665
  }
638
666
 
639
667
  /**
640
- * Set the comment
668
+ * Set the comment. A comment whose first non-blank character is '@', or that contains a
669
+ * %[fx:@...], %[hex:@...] or %[pixel:@...] escape, is rejected because ImageMagick reads the
670
+ * file it names.
641
671
  *
642
672
  * @param string [String] the comment
643
673
  * @return [String] the given comment
674
+ * @raise [ArgumentError] if the comment names a file with '@'
644
675
  */
645
676
  VALUE Info_comment_eq(VALUE self, VALUE string)
646
677
  {
@@ -1529,10 +1560,13 @@ VALUE Info_label(VALUE self)
1529
1560
  }
1530
1561
 
1531
1562
  /**
1532
- * Set the label.
1563
+ * Set the label. A label whose first non-blank character is '@', or that contains a
1564
+ * %[fx:@...], %[hex:@...] or %[pixel:@...] escape, is rejected because ImageMagick reads the
1565
+ * file it names.
1533
1566
  *
1534
1567
  * @param string [String] the label
1535
1568
  * @return [String] the given label
1569
+ * @raise [ArgumentError] if the label names a file with '@'
1536
1570
  */
1537
1571
  VALUE Info_label_eq(VALUE self, VALUE string)
1538
1572
  {
@@ -82,14 +82,28 @@ KernelInfo_alloc(VALUE klass)
82
82
  /**
83
83
  * KernelInfo object constructor
84
84
  *
85
+ * - Calling it again on an initialized object replaces its kernel and destroys the old one.
86
+ * - A kernel string whose first non-blank character is '@' is rejected: ImageMagick would read the
87
+ * kernel from the file it names. A string that contains a %[fx:@...], %[hex:@...] or
88
+ * %[pixel:@...] escape is rejected as well, by the same check as the other options that name files.
89
+ *
85
90
  * @param kernel_string [String] kernel info string representation to be parsed
86
91
  * @return [Magick::KernelInfo] self
92
+ * @raise [FrozenError] if the object is frozen
93
+ * @raise [ArgumentError] if the kernel string names a file with '@'
87
94
  */
88
95
  VALUE
89
96
  KernelInfo_initialize(VALUE self, VALUE kernel_string)
90
97
  {
91
- KernelInfo *kernel;
92
- char *string = StringValueCStr(kernel_string);
98
+ KernelInfo *kernel, *old_kernel;
99
+ char *string;
100
+
101
+ rb_check_frozen(self);
102
+ string = StringValueCStr(kernel_string);
103
+ if (rm_has_file_reference(string))
104
+ {
105
+ rb_raise(rb_eArgError, "the kernel must not name a file with '@'");
106
+ }
93
107
 
94
108
  #if defined(IMAGEMAGICK_7)
95
109
  ExceptionInfo *exception;
@@ -113,7 +127,12 @@ KernelInfo_initialize(VALUE self, VALUE kernel_string)
113
127
  rb_raise(rb_eRuntimeError, "failed to parse kernel string");
114
128
  }
115
129
 
130
+ old_kernel = (KernelInfo *)DATA_PTR(self);
116
131
  DATA_PTR(self) = kernel;
132
+ if (old_kernel)
133
+ {
134
+ DestroyKernelInfo(old_kernel);
135
+ }
117
136
 
118
137
  return self;
119
138
  }
@@ -145,6 +164,24 @@ get_kernel_info(VALUE self)
145
164
  }
146
165
 
147
166
 
167
+ /**
168
+ * Return the KernelInfo struct of an object that is about to be changed,
169
+ * raising FrozenError if the object is frozen.
170
+ *
171
+ * No Ruby usage (internal function)
172
+ *
173
+ * @param self the KernelInfo object
174
+ * @return the KernelInfo struct
175
+ * @throw FrozenError if the object is frozen
176
+ */
177
+ static KernelInfo *
178
+ get_unfrozen_kernel_info(VALUE self)
179
+ {
180
+ rb_check_frozen(self);
181
+ return get_kernel_info(self);
182
+ }
183
+
184
+
148
185
  /**
149
186
  * Adds a given amount of the 'Unity' Convolution Kernel to the given pre-scaled and normalized Kernel.
150
187
  *
@@ -153,7 +190,7 @@ get_kernel_info(VALUE self)
153
190
  VALUE
154
191
  KernelInfo_unity_add(VALUE self, VALUE scale)
155
192
  {
156
- GVL_STRUCT_TYPE(UnityAddKernelInfo) args = { get_kernel_info(self), NUM2DBL(scale) };
193
+ GVL_STRUCT_TYPE(UnityAddKernelInfo) args = { get_unfrozen_kernel_info(self), NUM2DBL(scale) };
157
194
  CALL_FUNC_WITHOUT_GVL(GVL_FUNC(UnityAddKernelInfo), &args);
158
195
  return Qnil;
159
196
  }
@@ -164,8 +201,10 @@ KernelInfo_unity_add(VALUE self, VALUE scale)
164
201
  * of the sum of the kernel values (as per given flags).
165
202
  *
166
203
  * @param scale [Numeric] scale to use
167
- * @param flags [Magick::GeometryFlags] one of Magick::NormalizeValue, Magick::CorrelateNormalizeValue,
168
- * and/or Magick::PercentValue
204
+ * @param flags [Magick::GeometryFlags] Magick::NoValue, Magick::NormalizeValue or
205
+ * Magick::CorrelateNormalizeValue. ScaleKernelInfo ignores the other values, including
206
+ * Magick::PercentValue; use {KernelInfo#scale_geometry} for a percentage.
207
+ * @return [nil]
169
208
  */
170
209
  VALUE
171
210
  KernelInfo_scale(VALUE self, VALUE scale, VALUE flags)
@@ -174,7 +213,7 @@ KernelInfo_scale(VALUE self, VALUE scale, VALUE flags)
174
213
 
175
214
  VALUE_TO_ENUM(flags, geoflags, GeometryFlags);
176
215
 
177
- GVL_STRUCT_TYPE(ScaleKernelInfo) args = { get_kernel_info(self), NUM2DBL(scale), geoflags };
216
+ GVL_STRUCT_TYPE(ScaleKernelInfo) args = { get_unfrozen_kernel_info(self), NUM2DBL(scale), geoflags };
178
217
  CALL_FUNC_WITHOUT_GVL(GVL_FUNC(ScaleKernelInfo), &args);
179
218
  return Qnil;
180
219
  }
@@ -190,7 +229,7 @@ KernelInfo_scale_geometry(VALUE self, VALUE geometry)
190
229
  {
191
230
  char *geom = StringValueCStr(geometry);
192
231
 
193
- GVL_STRUCT_TYPE(ScaleGeometryKernelInfo) args = { get_kernel_info(self), geom };
232
+ GVL_STRUCT_TYPE(ScaleGeometryKernelInfo) args = { get_unfrozen_kernel_info(self), geom };
194
233
  CALL_FUNC_WITHOUT_GVL(GVL_FUNC(ScaleGeometryKernelInfo), &args);
195
234
 
196
235
  RB_GC_GUARD(geometry);
@@ -199,19 +238,32 @@ KernelInfo_scale_geometry(VALUE self, VALUE geometry)
199
238
  }
200
239
 
201
240
  /**
202
- * Creates a new clone of the object so that it can be modified without affecting the original.
241
+ * Initialize a copy made by clone or dup with a copy of the kernel of the original.
203
242
  *
204
- * @return [Magick::KernelInfo] new KernelInfo object
243
+ * @param orig the original object
244
+ * @return [Magick::KernelInfo] self
245
+ * @raise [FrozenError] if the object is frozen
205
246
  */
206
247
  VALUE
207
- KernelInfo_clone(VALUE self)
248
+ KernelInfo_init_copy(VALUE self, VALUE orig)
208
249
  {
209
- KernelInfo *kernel = CloneKernelInfo(get_kernel_info(self));
250
+ KernelInfo *kernel, *old_kernel;
251
+
252
+ rb_check_frozen(self);
253
+ kernel = CloneKernelInfo(get_kernel_info(orig));
210
254
  if (!kernel)
211
255
  {
212
256
  rb_raise(rb_eNoMemError, "not enough memory to continue");
213
257
  }
214
- return TypedData_Wrap_Struct(Class_KernelInfo, &rm_kernel_info_data_type, kernel);
258
+
259
+ old_kernel = (KernelInfo *)DATA_PTR(self);
260
+ DATA_PTR(self) = kernel;
261
+ if (old_kernel)
262
+ {
263
+ DestroyKernelInfo(old_kernel);
264
+ }
265
+
266
+ return self;
215
267
  }
216
268
 
217
269
  /**
@@ -219,6 +271,9 @@ KernelInfo_clone(VALUE self)
219
271
  * kernels used for special purposes such as gaussian blurring, skeleton
220
272
  * pruning, and edge distance determination.
221
273
  *
274
+ * - The kernel is parsed from "name:geometry" as KernelInfo.new does, so the
275
+ * arguments that are left out get ImageMagick's defaults.
276
+ *
222
277
  * @param what [Magick::KernelInfoType] kernel one of Magick::KernelInfoType enums
223
278
  * @param geometry [String] geometry to pass to default kernel
224
279
  * @return [Magick::KernelInfo] a new KernelInfo object
@@ -229,21 +284,32 @@ KernelInfo_builtin(VALUE self, VALUE what, VALUE geometry)
229
284
  KernelInfo *kernel;
230
285
  KernelInfoType kernel_type;
231
286
  GeometryInfo info;
232
- const char *geom_str;
287
+ const char *geom_str, *name;
288
+ char kernel_string[MaxTextExtent];
289
+ int length;
233
290
  #if defined(IMAGEMAGICK_7)
234
291
  ExceptionInfo *exception;
235
292
  #endif
236
293
 
237
294
  VALUE_TO_ENUM(what, kernel_type, KernelInfoType);
238
295
  geom_str = StringValueCStr(geometry);
239
- if (ParseGeometry(geom_str, &info) == NoValue && *geom_str)
296
+ // AcquireKernelInfo() reads a list of kernels separated by ';'. ParseGeometry() rejects such a
297
+ // string today, but builtin makes one built-in kernel, so reject ';' explicitly as well.
298
+ if ((ParseGeometry(geom_str, &info) == NoValue && *geom_str) || strchr(geom_str, ';'))
240
299
  {
241
300
  rb_raise(rb_eArgError, "invalid geometry string");
242
301
  }
243
302
 
303
+ name = CommandOptionToMnemonic(MagickKernelOptions, kernel_type);
304
+ length = snprintf(kernel_string, sizeof(kernel_string), "%s:%s", name, geom_str);
305
+ if (length < 0 || (size_t)length >= sizeof(kernel_string))
306
+ {
307
+ rb_raise(rb_eArgError, "geometry string too long");
308
+ }
309
+
244
310
  #if defined(IMAGEMAGICK_7)
245
311
  exception = AcquireExceptionInfo();
246
- kernel = AcquireKernelBuiltIn(kernel_type, &info, exception);
312
+ kernel = AcquireKernelInfo(kernel_string, exception);
247
313
  if (rm_should_raise_exception(exception, DestroyExceptionRetention))
248
314
  {
249
315
  if (kernel != (KernelInfo *) NULL)
@@ -253,9 +319,11 @@ KernelInfo_builtin(VALUE self, VALUE what, VALUE geometry)
253
319
  rm_raise_exception(exception);
254
320
  }
255
321
  #else
256
- kernel = AcquireKernelBuiltIn(kernel_type, &info);
322
+ kernel = AcquireKernelInfo(kernel_string);
257
323
  #endif
258
324
 
325
+ RB_GC_GUARD(geometry);
326
+
259
327
  if (!kernel)
260
328
  {
261
329
  rb_raise(rb_eRuntimeError, "failed to acquire builtin kernel");
@@ -267,6 +267,7 @@ Init_RMagick2(void)
267
267
  rb_define_module_function(Module_Magick, "fonts", RUBY_METHOD_FUNC(Magick_fonts), 0);
268
268
  rb_define_module_function(Module_Magick, "init_formats", RUBY_METHOD_FUNC(Magick_init_formats), 0);
269
269
  rb_define_module_function(Module_Magick, "limit_resource", RUBY_METHOD_FUNC(Magick_limit_resource), -1);
270
+ rb_define_module_function(Module_Magick, "resource_usage", RUBY_METHOD_FUNC(Magick_resource_usage), 1);
270
271
  rb_define_module_function(Module_Magick, "set_cache_threshold", RUBY_METHOD_FUNC(Magick_set_cache_threshold), 1);
271
272
  rb_define_module_function(Module_Magick, "set_log_event_mask", RUBY_METHOD_FUNC(Magick_set_log_event_mask), -1);
272
273
  rb_define_module_function(Module_Magick, "set_log_format", RUBY_METHOD_FUNC(Magick_set_log_format), 1);
@@ -436,7 +437,6 @@ Init_RMagick2(void)
436
437
  rb_define_method(Class_Image, "charcoal", RUBY_METHOD_FUNC(Image_charcoal), -1);
437
438
  rb_define_method(Class_Image, "chop", RUBY_METHOD_FUNC(Image_chop), 4);
438
439
  rb_define_method(Class_Image, "clut_channel", RUBY_METHOD_FUNC(Image_clut_channel), -1);
439
- rb_define_method(Class_Image, "clone", RUBY_METHOD_FUNC(Image_clone), 0);
440
440
  rb_define_method(Class_Image, "color_flood_fill", RUBY_METHOD_FUNC(Image_color_flood_fill), 5);
441
441
  rb_define_method(Class_Image, "color_histogram", RUBY_METHOD_FUNC(Image_color_histogram), 0);
442
442
  rb_define_method(Class_Image, "colorize", RUBY_METHOD_FUNC(Image_colorize), -1);
@@ -476,7 +476,6 @@ Init_RMagick2(void)
476
476
  rb_define_method(Class_Image, "distort", RUBY_METHOD_FUNC(Image_distort), -1);
477
477
  rb_define_method(Class_Image, "distortion_channel", RUBY_METHOD_FUNC(Image_distortion_channel), -1);
478
478
  rb_define_method(Class_Image, "_dump", RUBY_METHOD_FUNC(Image__dump), 1);
479
- rb_define_method(Class_Image, "dup", RUBY_METHOD_FUNC(Image_dup), 0);
480
479
  rb_define_method(Class_Image, "each_profile", RUBY_METHOD_FUNC(Image_each_profile), 0);
481
480
  rb_define_method(Class_Image, "edge", RUBY_METHOD_FUNC(Image_edge), -1);
482
481
  rb_define_method(Class_Image, "emboss", RUBY_METHOD_FUNC(Image_emboss), -1);
@@ -678,10 +677,8 @@ Init_RMagick2(void)
678
677
  rb_include_module(Class_Draw, Module_DrawAttribute);
679
678
 
680
679
  rb_define_method(Class_Draw, "annotate", RUBY_METHOD_FUNC(Draw_annotate), 6);
681
- rb_define_method(Class_Draw, "clone", RUBY_METHOD_FUNC(Draw_clone), 0);
682
680
  rb_define_method(Class_Draw, "composite", RUBY_METHOD_FUNC(Draw_composite), -1);
683
681
  rb_define_method(Class_Draw, "draw", RUBY_METHOD_FUNC(Draw_draw), 1);
684
- rb_define_method(Class_Draw, "dup", RUBY_METHOD_FUNC(Draw_dup), 0);
685
682
  rb_define_method(Class_Draw, "get_type_metrics", RUBY_METHOD_FUNC(Draw_get_type_metrics), -1);
686
683
  rb_define_method(Class_Draw, "get_multiline_type_metrics", RUBY_METHOD_FUNC(Draw_get_multiline_type_metrics), -1);
687
684
  rb_define_method(Class_Draw, "initialize", RUBY_METHOD_FUNC(Draw_initialize), 0);
@@ -747,8 +744,6 @@ Init_RMagick2(void)
747
744
  rb_define_method(Class_Pixel, "eql?", RUBY_METHOD_FUNC(Pixel_eql_q), 1);
748
745
  rb_define_method(Class_Pixel, "initialize", RUBY_METHOD_FUNC(Pixel_initialize), -1);
749
746
  rb_define_method(Class_Pixel, "initialize_copy", RUBY_METHOD_FUNC(Pixel_init_copy), 1);
750
- rb_define_method(Class_Pixel, "clone", RUBY_METHOD_FUNC(Pixel_clone), 0);
751
- rb_define_method(Class_Pixel, "dup", RUBY_METHOD_FUNC(Pixel_dup), 0);
752
747
  rb_define_method(Class_Pixel, "fcmp", RUBY_METHOD_FUNC(Pixel_fcmp), -1);
753
748
  rb_define_method(Class_Pixel, "hash", RUBY_METHOD_FUNC(Pixel_hash), 0);
754
749
  rb_define_method(Class_Pixel, "intensity", RUBY_METHOD_FUNC(Pixel_intensity), 0);
@@ -907,8 +902,7 @@ Init_RMagick2(void)
907
902
  rb_define_method(Class_KernelInfo, "unity_add", RUBY_METHOD_FUNC(KernelInfo_unity_add), 1);
908
903
  rb_define_method(Class_KernelInfo, "scale", RUBY_METHOD_FUNC(KernelInfo_scale), 2);
909
904
  rb_define_method(Class_KernelInfo, "scale_geometry", RUBY_METHOD_FUNC(KernelInfo_scale_geometry), 1);
910
- rb_define_method(Class_KernelInfo, "clone", RUBY_METHOD_FUNC(KernelInfo_clone), 0);
911
- rb_define_method(Class_KernelInfo, "dup", RUBY_METHOD_FUNC(KernelInfo_clone), 0);
905
+ rb_define_method(Class_KernelInfo, "initialize_copy", RUBY_METHOD_FUNC(KernelInfo_init_copy), 1);
912
906
 
913
907
  rb_define_singleton_method(Class_KernelInfo, "builtin", RUBY_METHOD_FUNC(KernelInfo_builtin), 2);
914
908
 
@@ -442,18 +442,29 @@ Montage_tile_eq(VALUE self, VALUE tile_arg)
442
442
 
443
443
 
444
444
  /**
445
- * Set title value.
445
+ * Set title value. A title whose first non-blank character is '@', or that contains a
446
+ * %[fx:@...], %[hex:@...] or %[pixel:@...] escape, is rejected because ImageMagick reads the
447
+ * file it names.
446
448
  *
447
449
  * @param title [String] the title
448
450
  * @return [String] the given title
451
+ * @raise [ArgumentError] if the title names a file with '@'
449
452
  */
450
453
  VALUE
451
454
  Montage_title_eq(VALUE self, VALUE title)
452
455
  {
453
456
  Montage *montage;
457
+ const char *title_cstr;
454
458
 
455
459
  TypedData_Get_Struct(self, Montage, &rm_montage_data_type, montage);
456
- magick_clone_string(&montage->info->title, StringValueCStr(title));
460
+ title_cstr = StringValueCStr(title);
461
+
462
+ if (rm_has_file_reference(title_cstr))
463
+ {
464
+ rb_raise(rb_eArgError, "the title must not name a file with '@'");
465
+ }
466
+
467
+ magick_clone_string(&montage->info->title, title_cstr);
457
468
  return title;
458
469
  }
459
470
 
@@ -531,50 +531,8 @@ Pixel_case_eq(VALUE self, VALUE other)
531
531
  }
532
532
 
533
533
 
534
- /**
535
- * Clone a Pixel.
536
- *
537
- * @return [Magick::Pixel] a clone object
538
- * @see #dup
539
- * @see #initialize_copy
540
- */
541
- VALUE
542
- Pixel_clone(VALUE self)
543
- {
544
- VALUE clone;
545
-
546
- clone = Pixel_dup(self);
547
- if (OBJ_FROZEN(self))
548
- {
549
- OBJ_FREEZE(clone);
550
- }
551
-
552
- RB_GC_GUARD(clone);
553
-
554
- return clone;
555
- }
556
534
 
557
535
 
558
- /**
559
- * Duplicate a Pixel.
560
- *
561
- * @return [Magick::Pixel] a duplicated object
562
- * @see #clone
563
- * @see #initialize_copy
564
- */
565
- VALUE
566
- Pixel_dup(VALUE self)
567
- {
568
- Pixel *pixel;
569
- VALUE dup;
570
-
571
- pixel = ALLOC(Pixel);
572
- memset(pixel, '\0', sizeof(Pixel));
573
- dup = TypedData_Wrap_Struct(CLASS_OF(self), &rm_pixel_data_type, pixel);
574
- RB_GC_GUARD(dup);
575
-
576
- return rb_funcall(dup, rm_ID_initialize_copy, 1, self);
577
- }
578
536
 
579
537
 
580
538
  /**
@@ -838,7 +838,7 @@ Import_TypeMetric(TypeMetric *tm)
838
838
  max_advance = rb_float_new(tm->max_advance);
839
839
  bounds = Import_SegmentInfo(&tm->bounds);
840
840
  underline_position = rb_float_new(tm->underline_position);
841
- underline_thickness = rb_float_new(tm->underline_position);
841
+ underline_thickness = rb_float_new(tm->underline_thickness);
842
842
 
843
843
  RB_GC_GUARD(pixels_per_em);
844
844
  RB_GC_GUARD(ascent);
@@ -221,6 +221,50 @@ rm_strnlen_s(const char *str, size_t strsz)
221
221
  }
222
222
 
223
223
 
224
+ /**
225
+ * Return true if InterpretImageProperties() would read a file the string names, that is, if its
226
+ * first non-blank character is '@', or if it contains a %[fx:...], %[hex:...] or %[pixel:...]
227
+ * escape whose expression begins with '@'.
228
+ *
229
+ * ImageMagick 7 reads such an expression from the file it names, with no blank skipped before
230
+ * the '@'. It also interprets a %[...] inside an fx expression, even after "%%" since '%' is the
231
+ * modulo operator there, so every "%[" in the string is checked. The prefixes are compared with
232
+ * LocaleNCompare() as ImageMagick does. ImageMagick 6 does not read these expressions, but the
233
+ * check is the same for both.
234
+ *
235
+ * No Ruby usage (internal function)
236
+ *
237
+ * @param str the string
238
+ * @return true if the string names a file to read, otherwise false
239
+ */
240
+ bool
241
+ rm_has_file_reference(const char *str)
242
+ {
243
+ const char *p;
244
+
245
+ p = str;
246
+ while (isspace((int) ((unsigned char) *p)))
247
+ {
248
+ p++;
249
+ }
250
+ if (*p == '@')
251
+ {
252
+ return true;
253
+ }
254
+
255
+ for (p = strstr(str, "%["); p != NULL; p = strstr(p + 2, "%["))
256
+ {
257
+ if (LocaleNCompare(p + 2, "fx:@", 4) == 0
258
+ || LocaleNCompare(p + 2, "hex:@", 5) == 0
259
+ || LocaleNCompare(p + 2, "pixel:@", 7) == 0)
260
+ {
261
+ return true;
262
+ }
263
+ }
264
+ return false;
265
+ }
266
+
267
+
224
268
  /**
225
269
  * Raise exception if array too short.
226
270
  *
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Magick
4
- VERSION = '7.1.4'
4
+ VERSION = '7.1.6'
5
5
  MIN_RUBY_VERSION = '3.2.0'
6
6
  MIN_IM6_VERSION = '6.9.0'
7
7
  MIN_IM7_VERSION = '7.1.0'
@@ -1410,12 +1410,6 @@ module Magick
1410
1410
  @images.clear
1411
1411
  end
1412
1412
 
1413
- def clone
1414
- ditto = dup
1415
- ditto.freeze if frozen?
1416
- ditto
1417
- end
1418
-
1419
1413
  # override Enumerable#collect
1420
1414
  def collect(&)
1421
1415
  current = get_current
@@ -1507,13 +1501,6 @@ module Magick
1507
1501
  self
1508
1502
  end
1509
1503
 
1510
- def dup
1511
- ditto = self.class.new
1512
- @images.each { |img| ditto << img }
1513
- ditto.scene = @scene
1514
- ditto
1515
- end
1516
-
1517
1504
  def eql?(other)
1518
1505
  begin
1519
1506
  assert_image_array other
@@ -1570,6 +1557,13 @@ module Magick
1570
1557
  @scene = length - 1 if length > 0 # last image in array
1571
1558
  end
1572
1559
 
1560
+ # Called by clone and dup, after the instance variables are copied. The copy
1561
+ # has its own array of the same images.
1562
+ def initialize_copy(other)
1563
+ super
1564
+ @images = @images.dup
1565
+ end
1566
+
1573
1567
  def insert(index, *args)
1574
1568
  args.each { |image| assert_image image }
1575
1569
  current = get_current