rmagick 7.1.0 → 7.1.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c56286daf220fd8db82fce268ce2116705e192b643641cc88b4d7e53b6a46d1a
4
- data.tar.gz: 1a060762de65e6bf6a384a7f10c3036f85d6876ec89c1be54fede6ea4f20e604
3
+ metadata.gz: a64e970fa95ffd136462c610a760685197a4ed3ceb2c9e6dad90be7a0c8f3211
4
+ data.tar.gz: dc300b78b89fdb54386fbc10e14b55f6dd37497e65923ec48728abe3b3c85b1d
5
5
  SHA512:
6
- metadata.gz: c7ebbec352d94fdeb8873353c8c13a8d3432b2f89c26c05a8520ee56353c1daedde3717429ad6ec1b9c66a9d9ca71eb51c498833ea0e7419fb572f38642d4fcd
7
- data.tar.gz: c7a7941f288dd513f5ab88d009011c07575fc07e95c772fd3950db9d8828738c7ab49e6fa3484a4ead018ecbe0a002b260f4e0c670c70647aac11419465f09a5
6
+ metadata.gz: f9ae77c69984d7472435ccfceda7c74637a4b2374e6529d40d47dae464629058a778c76208f7ca9c1644ecc2e50a44ca507abcd265fb57cff22c83fc7f031cf5
7
+ data.tar.gz: 3d620912beddcea78dc6953c461281f35bf5ea1c9d68c3cfe0c8a25c60cefc79f36c4834c559d59f448e1d749956755148425bd421a8f20e17859fd24f867e8f
data/CHANGELOG.md CHANGED
@@ -3,6 +3,21 @@
3
3
  All notable changes to this project are documented in this file.
4
4
  This project adheres to [Semantic Versioning](http://semver.org/).
5
5
 
6
+ ## RMagick 7.1.2
7
+
8
+ Bug Fixes
9
+
10
+ * Fix memory leak in Draw#annotate when the geometry copy fails (#1854)
11
+ * Fix affine matrix not being restored when Draw#annotate raises (#1853)
12
+ * Fix use-after-free in Draw#annotate when a callback destroys the image (#1852)
13
+ * Fix memory leak in Draw#annotate when a geometry argument raises (#1850)
14
+
15
+ ## RMagick 7.1.1
16
+
17
+ Bug Fixes
18
+
19
+ * Fix Strings being recycled while the C pointers into them are in use (#1847)
20
+
6
21
  ## RMagick 7.1.0
7
22
 
8
23
  > [!IMPORTANT]
data/Rakefile CHANGED
@@ -195,7 +195,9 @@ if RUBY_PLATFORM.include?('linux')
195
195
  )
196
196
 
197
197
  namespace :spec do
198
- RubyMemcheck::RSpec::RakeTask.new(valgrind: :compile)
198
+ RubyMemcheck::RSpec::RakeTask.new(valgrind: :compile) do |t|
199
+ t.rspec_opts = '--tag "~slow"' unless ENV['SLOW']
200
+ end
199
201
  end
200
202
  end
201
203
 
@@ -1163,8 +1163,8 @@ extern void rm_check_ary_len(VALUE, long);
1163
1163
  extern VALUE rm_check_ary_type(VALUE ary);
1164
1164
  extern Image *rm_check_destroyed(VALUE);
1165
1165
  extern Image *rm_check_frozen(VALUE);
1166
- extern char *rm_str2cstr(VALUE, size_t *);
1167
- extern char *rm_path2cstr(VALUE, size_t *);
1166
+ extern char *rm_str2cstr(VALUE *, size_t *);
1167
+ extern char *rm_path2cstr(VALUE *, size_t *);
1168
1168
  extern int rm_check_num2dbl(VALUE);
1169
1169
  extern double rm_fuzz_to_dbl(VALUE);
1170
1170
  extern Quantum rm_app2quantum(VALUE);
@@ -511,6 +511,8 @@ Image *str_to_image(VALUE str)
511
511
  DestroyExceptionInfo(exception);
512
512
  }
513
513
 
514
+ RB_GC_GUARD(str);
515
+
514
516
  return image;
515
517
  }
516
518
 
@@ -834,72 +836,88 @@ Draw_undercolor_eq(VALUE self, VALUE undercolor)
834
836
  }
835
837
 
836
838
 
839
+ struct Draw_annotate_args
840
+ {
841
+ VALUE self;
842
+ VALUE image_arg;
843
+ VALUE width_arg;
844
+ VALUE height_arg;
845
+ VALUE x_arg;
846
+ VALUE y_arg;
847
+ VALUE text;
848
+ Draw *draw;
849
+ AffineMatrix keep;
850
+ #if defined(IMAGEMAGICK_7)
851
+ ExceptionInfo *exception;
852
+ #endif
853
+ };
854
+
855
+
837
856
  /**
838
- * Annotates an image with text.
857
+ * Release what annotate_body acquired and restore the affine matrix.
839
858
  *
840
- * - Additional Draw attribute methods may be called in the optional block,
841
- * which is executed in the context of a Draw object.
859
+ * No Ruby usage (internal function)
842
860
  *
843
- * @param image_arg [Magick::Image, Magick::ImageList] Either an imagelist or an image. If an
844
- * imagelist, uses the current image.
845
- * @param width_arg [Numeric] the width
846
- * @param height_arg [Numeric] the height
847
- * @param x_arg [Numeric] x position
848
- * @param y_arg [Numeric] y position
849
- * @param text [String] the annotation text
850
- * @return [Magick::Draw] self
861
+ * @param arg the annotate arguments
862
+ * @return nil
863
+ * @see Draw_annotate
851
864
  */
852
- VALUE Draw_annotate(
853
- VALUE self,
854
- VALUE image_arg,
855
- VALUE width_arg,
856
- VALUE height_arg,
857
- VALUE x_arg,
858
- VALUE y_arg,
859
- VALUE text)
865
+ static VALUE
866
+ annotate_ensure(VALUE arg)
860
867
  {
861
- Draw *draw;
868
+ struct Draw_annotate_args *annotate = (struct Draw_annotate_args *)arg;
869
+ Draw *draw = annotate->draw;
870
+
871
+ magick_free(draw->info->text);
872
+ draw->info->text = NULL;
873
+ draw->info->affine = annotate->keep;
874
+
875
+ #if defined(IMAGEMAGICK_7)
876
+ if (annotate->exception)
877
+ {
878
+ DestroyExceptionInfo(annotate->exception);
879
+ annotate->exception = NULL;
880
+ }
881
+ #endif
882
+
883
+ return Qnil;
884
+ }
885
+
886
+
887
+ /**
888
+ * Annotate the image.
889
+ *
890
+ * No Ruby usage (internal function)
891
+ *
892
+ * @param arg the annotate arguments
893
+ * @return self
894
+ * @see Draw_annotate
895
+ */
896
+ static VALUE
897
+ annotate_body(VALUE arg)
898
+ {
899
+ struct Draw_annotate_args *annotate = (struct Draw_annotate_args *)arg;
900
+ Draw *draw = annotate->draw;
862
901
  Image *image;
863
902
  unsigned long width, height;
864
903
  long x, y;
865
- AffineMatrix keep;
866
904
  char geometry_str[100];
867
905
  char *embed_text;
868
906
  #if defined(IMAGEMAGICK_7)
869
907
  ExceptionInfo *exception;
870
908
  #endif
871
909
 
872
- // Save the affine matrix in case it is modified by
873
- // Draw#rotation=
874
- draw = get_draw(self);
875
- keep = draw->info->affine;
876
-
877
- image_arg = rm_cur_image(image_arg);
878
- image = rm_check_frozen(image_arg);
879
-
880
910
  // If we have an optional parm block, run it in self's context,
881
911
  // allowing the app a chance to modify the object's attributes
882
912
  if (rb_block_given_p())
883
913
  {
884
- rb_yield(self);
914
+ rb_yield(annotate->self);
885
915
  }
886
916
 
887
- // Store in Draw structure. The text is drawn as given: it is not run
888
- // through InterpretImageProperties(), so a `%[...]` or `%x` escape in it is
889
- // not expanded. Everything those escapes provide is available directly from
890
- // Ruby -- Image#columns, Image#filename, Image#artifact and so on.
891
- embed_text = StringValueCStr(text);
892
- draw->info->text = ConstantString(embed_text);
893
- #if defined(IMAGEMAGICK_7)
894
- exception = AcquireExceptionInfo();
895
- #endif
896
-
897
- // Create geometry string, copy to Draw structure, overriding
898
- // any previously existing value.
899
- width = NUM2ULONG(width_arg);
900
- height = NUM2ULONG(height_arg);
901
- x = NUM2LONG(x_arg);
902
- y = NUM2LONG(y_arg);
917
+ width = NUM2ULONG(annotate->width_arg);
918
+ height = NUM2ULONG(annotate->height_arg);
919
+ x = NUM2LONG(annotate->x_arg);
920
+ y = NUM2LONG(annotate->y_arg);
903
921
 
904
922
  if (width == 0 && height == 0)
905
923
  {
@@ -914,25 +932,81 @@ VALUE Draw_annotate(
914
932
 
915
933
  magick_clone_string(&draw->info->geometry, geometry_str);
916
934
 
935
+ // Store in Draw structure. The text is drawn as given: it is not run
936
+ // through InterpretImageProperties(), so a `%[...]` or `%x` escape in it is
937
+ // not expanded. Everything those escapes provide is available directly from
938
+ // Ruby -- Image#columns, Image#filename, Image#artifact and so on.
939
+ embed_text = StringValueCStr(annotate->text);
940
+ image = rm_check_frozen(annotate->image_arg);
941
+ draw->info->text = ConstantString(embed_text);
942
+
917
943
  #if defined(IMAGEMAGICK_7)
918
- GVL_STRUCT_TYPE(AnnotateImage) args = { image, draw->info, exception };
944
+ annotate->exception = AcquireExceptionInfo();
945
+ GVL_STRUCT_TYPE(AnnotateImage) args = { image, draw->info, annotate->exception };
919
946
  #else
920
947
  GVL_STRUCT_TYPE(AnnotateImage) args = { image, draw->info };
921
948
  #endif
922
949
  CALL_FUNC_WITHOUT_GVL(GVL_FUNC(AnnotateImage), &args);
923
950
 
924
- magick_free(draw->info->text);
925
- draw->info->text = NULL;
926
- draw->info->affine = keep;
927
-
928
951
  #if defined(IMAGEMAGICK_7)
952
+ exception = annotate->exception;
953
+ annotate->exception = NULL;
954
+
929
955
  CHECK_EXCEPTION();
930
956
  DestroyExceptionInfo(exception);
931
957
  #else
932
958
  rm_check_image_exception(image, RetainOnError);
933
959
  #endif
934
960
 
935
- return self;
961
+ return annotate->self;
962
+ }
963
+
964
+
965
+ /**
966
+ * Annotates an image with text.
967
+ *
968
+ * - Additional Draw attribute methods may be called in the optional block,
969
+ * which is executed in the context of a Draw object.
970
+ *
971
+ * @param image_arg [Magick::Image, Magick::ImageList] Either an imagelist or an image. If an
972
+ * imagelist, uses the current image.
973
+ * @param width_arg [Numeric] the width
974
+ * @param height_arg [Numeric] the height
975
+ * @param x_arg [Numeric] x position
976
+ * @param y_arg [Numeric] y position
977
+ * @param text [String] the annotation text
978
+ * @return [Magick::Draw] self
979
+ */
980
+ VALUE Draw_annotate(
981
+ VALUE self,
982
+ VALUE image_arg,
983
+ VALUE width_arg,
984
+ VALUE height_arg,
985
+ VALUE x_arg,
986
+ VALUE y_arg,
987
+ VALUE text)
988
+ {
989
+ struct Draw_annotate_args annotate;
990
+
991
+ // Save the affine matrix in case it is modified by
992
+ // Draw#rotation=
993
+ annotate.draw = get_draw(self);
994
+ annotate.keep = annotate.draw->info->affine;
995
+
996
+ annotate.self = self;
997
+ annotate.image_arg = rm_cur_image(image_arg);
998
+ annotate.width_arg = width_arg;
999
+ annotate.height_arg = height_arg;
1000
+ annotate.x_arg = x_arg;
1001
+ annotate.y_arg = y_arg;
1002
+ annotate.text = text;
1003
+ #if defined(IMAGEMAGICK_7)
1004
+ annotate.exception = NULL;
1005
+ #endif
1006
+
1007
+ rm_check_frozen(annotate.image_arg);
1008
+
1009
+ return rb_ensure(annotate_body, (VALUE)&annotate, annotate_ensure, (VALUE)&annotate);
936
1010
  }
937
1011
 
938
1012
 
@@ -1617,6 +1691,7 @@ get_type_metrics(int argc, VALUE *argv, VALUE self, gvl_function_t fp)
1617
1691
  Image *image;
1618
1692
  Draw *draw;
1619
1693
  VALUE t;
1694
+ VALUE text_arg = Qnil;
1620
1695
  TypeMetric metrics;
1621
1696
  char *text = NULL;
1622
1697
  size_t text_l;
@@ -1628,13 +1703,15 @@ get_type_metrics(int argc, VALUE *argv, VALUE self, gvl_function_t fp)
1628
1703
  switch (argc)
1629
1704
  {
1630
1705
  case 1: // use default image
1631
- text = rm_str2cstr(argv[0], &text_l);
1706
+ text_arg = argv[0];
1707
+ text = rm_str2cstr(&text_arg, &text_l);
1632
1708
  TypedData_Get_Struct(get_dummy_tm_img(CLASS_OF(self)), Image, &rm_image_data_type, image);
1633
1709
  break;
1634
1710
  case 2:
1635
1711
  t = rm_cur_image(argv[0]);
1636
1712
  image = rm_check_destroyed(t);
1637
- text = rm_str2cstr(argv[1], &text_l);
1713
+ text_arg = argv[1];
1714
+ text = rm_str2cstr(&text_arg, &text_l);
1638
1715
  break; // okay
1639
1716
  default:
1640
1717
  rb_raise(rb_eArgError, "wrong number of arguments (%d for 1 or 2)", argc);
@@ -1680,6 +1757,7 @@ get_type_metrics(int argc, VALUE *argv, VALUE self, gvl_function_t fp)
1680
1757
  #endif
1681
1758
 
1682
1759
  RB_GC_GUARD(t);
1760
+ RB_GC_GUARD(text_arg);
1683
1761
 
1684
1762
  return Import_TypeMetric(&metrics);
1685
1763
  }
@@ -2925,7 +2925,7 @@ set_profile(VALUE self, const char *name, VALUE profile)
2925
2925
 
2926
2926
  image = rm_check_frozen(self);
2927
2927
 
2928
- profile_blob = rm_str2cstr(profile, &profile_length);
2928
+ profile_blob = rm_str2cstr(&profile, &profile_length);
2929
2929
 
2930
2930
  exception = AcquireExceptionInfo();
2931
2931
  m = GetMagickInfo(name, exception);
@@ -3002,6 +3002,8 @@ set_profile(VALUE self, const char *name, VALUE profile)
3002
3002
  rm_check_image_exception(image, RetainOnError);
3003
3003
  #endif
3004
3004
 
3005
+ RB_GC_GUARD(profile);
3006
+
3005
3007
  return self;
3006
3008
  }
3007
3009
 
@@ -4372,7 +4374,7 @@ Image_constitute(VALUE klass ATTRIBUTE_UNUSED, VALUE width_arg, VALUE height_arg
4372
4374
 
4373
4375
  width = NUM2LONG(width_arg);
4374
4376
  height = NUM2LONG(height_arg);
4375
- map = rm_str2cstr(map_arg, &map_l);
4377
+ map = rm_str2cstr(&map_arg, &map_l);
4376
4378
 
4377
4379
  npixels = width * height * map_l;
4378
4380
  if (RARRAY_LEN(pixels_arg) != npixels)
@@ -4495,6 +4497,7 @@ Image_constitute(VALUE klass ATTRIBUTE_UNUSED, VALUE width_arg, VALUE height_arg
4495
4497
  RB_GC_GUARD(pixel);
4496
4498
  RB_GC_GUARD(pixel0);
4497
4499
  RB_GC_GUARD(pixel_class);
4500
+ RB_GC_GUARD(map_arg);
4498
4501
 
4499
4502
  return rm_image_new(new_image);
4500
4503
  }
@@ -5252,6 +5255,8 @@ Image_decipher(VALUE self, VALUE passphrase)
5252
5255
 
5253
5256
  DestroyExceptionInfo(exception);
5254
5257
 
5258
+ RB_GC_GUARD(passphrase);
5259
+
5255
5260
  return rm_image_new(new_image);
5256
5261
  }
5257
5262
 
@@ -5753,7 +5758,7 @@ Image_dispatch(int argc, VALUE *argv, VALUE self)
5753
5758
  y = NUM2LONG(argv[1]);
5754
5759
  columns = NUM2ULONG(argv[2]);
5755
5760
  rows = NUM2ULONG(argv[3]);
5756
- map = rm_str2cstr(argv[4], &mapL);
5761
+ map = rm_str2cstr(&argv[4], &mapL);
5757
5762
  if (argc == 6)
5758
5763
  {
5759
5764
  stg_type = RTEST(argv[5]) ? DoublePixel : QuantumPixel;
@@ -5803,6 +5808,7 @@ Image_dispatch(int argc, VALUE *argv, VALUE self)
5803
5808
  xfree((void *)pixels.v);
5804
5809
 
5805
5810
  RB_GC_GUARD(pixels_ary);
5811
+ RB_GC_GUARD(argv[4]);
5806
5812
 
5807
5813
  return pixels_ary;
5808
5814
  }
@@ -6381,6 +6387,8 @@ Image_encipher(VALUE self, VALUE passphrase)
6381
6387
 
6382
6388
  DestroyExceptionInfo(exception);
6383
6389
 
6390
+ RB_GC_GUARD(passphrase);
6391
+
6384
6392
  return rm_image_new(new_image);
6385
6393
  }
6386
6394
 
@@ -7350,7 +7358,7 @@ Image_from_blob(VALUE klass ATTRIBUTE_UNUSED, VALUE blob_arg)
7350
7358
  void *blob;
7351
7359
  size_t length;
7352
7360
 
7353
- blob = (void *) rm_str2cstr(blob_arg, &length);
7361
+ blob = (void *) rm_str2cstr(&blob_arg, &length);
7354
7362
 
7355
7363
  // Get a new Info object - run the parm block if supplied
7356
7364
  info_obj = rm_info_new();
@@ -7368,6 +7376,7 @@ Image_from_blob(VALUE klass ATTRIBUTE_UNUSED, VALUE blob_arg)
7368
7376
  rm_sync_image_options(images, info);
7369
7377
 
7370
7378
  RB_GC_GUARD(info_obj);
7379
+ RB_GC_GUARD(blob_arg);
7371
7380
 
7372
7381
  return array_from_images(images);
7373
7382
  }
@@ -8155,7 +8164,7 @@ Image_import_pixels(int argc, VALUE *argv, VALUE self)
8155
8164
  // binary pixel data.
8156
8165
  if (rb_respond_to(pixel_arg, rb_intern("to_str")))
8157
8166
  {
8158
- buffer = (void *)rm_str2cstr(pixel_arg, &buffer_l);
8167
+ buffer = (void *)rm_str2cstr(&pixel_arg, &buffer_l);
8159
8168
  switch (stg_type)
8160
8169
  {
8161
8170
  case CharPixel:
@@ -8956,7 +8965,7 @@ Image__load(VALUE klass ATTRIBUTE_UNUSED, VALUE str)
8956
8965
  char *blob;
8957
8966
  size_t length;
8958
8967
 
8959
- blob = rm_str2cstr(str, &length);
8968
+ blob = rm_str2cstr(&str, &length);
8960
8969
 
8961
8970
  // Must be at least as big as the 1st 4 fields in DumpedImage
8962
8971
  if (length <= (long)(sizeof(DumpedImage)-MaxTextExtent))
@@ -9006,6 +9015,8 @@ Image__load(VALUE klass ATTRIBUTE_UNUSED, VALUE str)
9006
9015
  rm_check_exception(exception, image, DestroyOnError);
9007
9016
  DestroyExceptionInfo(exception);
9008
9017
 
9018
+ RB_GC_GUARD(str);
9019
+
9009
9020
  return rm_image_new(image);
9010
9021
  }
9011
9022
 
@@ -9169,6 +9180,9 @@ Image_marshal_load(VALUE self, VALUE ary)
9169
9180
  UPDATE_DATA_PTR(self, new_image);
9170
9181
  rm_image_destroy(image);
9171
9182
 
9183
+ RB_GC_GUARD(filename);
9184
+ RB_GC_GUARD(blob);
9185
+
9172
9186
  return self;
9173
9187
  }
9174
9188
 
@@ -11624,7 +11638,7 @@ rd_image(VALUE klass ATTRIBUTE_UNUSED, VALUE file, gvl_function_t fp)
11624
11638
  // Convert arg to string. If an exception occurs raise an error condition.
11625
11639
  file = rb_rescue(RESCUE_FUNC(rb_String), file, RESCUE_EXCEPTION_HANDLER_FUNC(file_arg_rescue), file);
11626
11640
 
11627
- filename = rm_path2cstr(file, &filename_l);
11641
+ filename = rm_path2cstr(&file, &filename_l);
11628
11642
  filename_l = min(filename_l, MaxTextExtent-1);
11629
11643
  if (filename_l == 0)
11630
11644
  {
@@ -11771,7 +11785,7 @@ Image_read_inline(VALUE self ATTRIBUTE_UNUSED, VALUE content)
11771
11785
  size_t blob_l;
11772
11786
  ExceptionInfo *exception;
11773
11787
 
11774
- image_data = rm_str2cstr(content, &image_data_l);
11788
+ image_data = rm_str2cstr(&content, &image_data_l);
11775
11789
 
11776
11790
  // Search for a comma. If found, we'll set the start of the
11777
11791
  // image data just following the comma. Otherwise we'll assume
@@ -11813,6 +11827,7 @@ Image_read_inline(VALUE self ATTRIBUTE_UNUSED, VALUE content)
11813
11827
  rm_sync_image_options(images, info);
11814
11828
 
11815
11829
  RB_GC_GUARD(info_obj);
11830
+ RB_GC_GUARD(content);
11816
11831
 
11817
11832
  return array_from_images(images);
11818
11833
  }
@@ -12350,7 +12365,7 @@ rotate(int bang, int argc, VALUE *argv, VALUE self)
12350
12365
  switch (argc)
12351
12366
  {
12352
12367
  case 2:
12353
- arrow = rm_str2cstr(argv[1], &arrow_l);
12368
+ arrow = rm_str2cstr(&argv[1], &arrow_l);
12354
12369
  if (arrow_l != 1 || (*arrow != '<' && *arrow != '>'))
12355
12370
  {
12356
12371
  rb_raise(rb_eArgError, "second argument must be '<' or '>', '%s' given", arrow);
@@ -15913,7 +15928,7 @@ void add_format_prefix(Info *info, VALUE file)
15913
15928
  }
15914
15929
  file = rb_rescue(RESCUE_FUNC(rb_String), file, RESCUE_EXCEPTION_HANDLER_FUNC(file_arg_rescue), file);
15915
15930
 
15916
- filename = rm_path2cstr(file, &filename_l);
15931
+ filename = rm_path2cstr(&file, &filename_l);
15917
15932
 
15918
15933
  if (*info->magick == '\0')
15919
15934
  {
@@ -269,8 +269,8 @@ Info_aref(int argc, VALUE *argv, VALUE self)
269
269
  switch (argc)
270
270
  {
271
271
  case 2:
272
- format_p = rm_str2cstr(argv[0], &format_l);
273
- key_p = rm_str2cstr(argv[1], &key_l);
272
+ format_p = rm_str2cstr(&argv[0], &format_l);
273
+ key_p = rm_str2cstr(&argv[1], &key_l);
274
274
  if (format_l > MAX_FORMAT_LEN || format_l + key_l > MaxTextExtent-1)
275
275
  {
276
276
  rb_raise(rb_eArgError, "can't reference %.60s:%.1024s - too long", format_p, key_p);
@@ -333,8 +333,8 @@ Info_aset(int argc, VALUE *argv, VALUE self)
333
333
  switch (argc)
334
334
  {
335
335
  case 3:
336
- format_p = rm_str2cstr(argv[0], &format_l);
337
- key_p = rm_str2cstr(argv[1], &key_l);
336
+ format_p = rm_str2cstr(&argv[0], &format_l);
337
+ key_p = rm_str2cstr(&argv[1], &key_l);
338
338
 
339
339
  if (format_l > MAX_FORMAT_LEN || format_l+key_l > MaxTextExtent-1)
340
340
  {
@@ -707,8 +707,8 @@ Info_define(int argc, VALUE *argv, VALUE self)
707
707
  fmt_arg = rb_String(argv[2]);
708
708
  value = (const char *)StringValueCStr(fmt_arg);
709
709
  case 2:
710
- key = rm_str2cstr(argv[1], &key_l);
711
- format = rm_str2cstr(argv[0], &format_l);
710
+ key = rm_str2cstr(&argv[1], &key_l);
711
+ format = rm_str2cstr(&argv[0], &format_l);
712
712
  break;
713
713
  default:
714
714
  rb_raise(rb_eArgError, "wrong number of arguments (%d for 2 or 3)", argc);
@@ -1850,7 +1850,7 @@ Info_sampling_factor_eq(VALUE self, VALUE sampling_factor)
1850
1850
 
1851
1851
  if (!NIL_P(sampling_factor))
1852
1852
  {
1853
- sampling_factor_p = rm_str2cstr(sampling_factor, &sampling_factor_len);
1853
+ sampling_factor_p = rm_str2cstr(&sampling_factor, &sampling_factor_len);
1854
1854
  }
1855
1855
 
1856
1856
  if (info->sampling_factor)
@@ -2187,8 +2187,8 @@ Info_undefine(VALUE self, VALUE format, VALUE key)
2187
2187
  size_t format_l, key_l;
2188
2188
  char fkey[MaxTextExtent];
2189
2189
 
2190
- format_p = rm_str2cstr(format, &format_l);
2191
- key_p = rm_str2cstr(key, &key_l);
2190
+ format_p = rm_str2cstr(&format, &format_l);
2191
+ key_p = rm_str2cstr(&key, &key_l);
2192
2192
 
2193
2193
  if (format_l > MAX_FORMAT_LEN || format_l + key_l > MaxTextExtent)
2194
2194
  {
@@ -188,8 +188,13 @@ KernelInfo_scale(VALUE self, VALUE scale, VALUE flags)
188
188
  VALUE
189
189
  KernelInfo_scale_geometry(VALUE self, VALUE geometry)
190
190
  {
191
- GVL_STRUCT_TYPE(ScaleGeometryKernelInfo) args = { get_kernel_info(self), StringValueCStr(geometry) };
191
+ char *geom = StringValueCStr(geometry);
192
+
193
+ GVL_STRUCT_TYPE(ScaleGeometryKernelInfo) args = { get_kernel_info(self), geom };
192
194
  CALL_FUNC_WITHOUT_GVL(GVL_FUNC(ScaleGeometryKernelInfo), &args);
195
+
196
+ RB_GC_GUARD(geometry);
197
+
193
198
  return Qnil;
194
199
  }
195
200
 
@@ -320,21 +320,34 @@ rm_no_freeze(VALUE obj)
320
320
  /**
321
321
  * Supply our own version of the "obsolete" rb_str2cstr.
322
322
  *
323
+ * The argument is taken by reference because StringValue may replace it. When
324
+ * the argument is not a String but responds to #to_str, StringValue converts
325
+ * it and the String it produces is referenced by nothing but the variable it
326
+ * is stored in. Writing that String back into the caller's variable is what
327
+ * keeps it - and therefore the buffer the returned pointer addresses - alive
328
+ * for as long as the caller can see it. Taking the argument by value would
329
+ * leave the String unreferenced the moment this function returns, and the GC
330
+ * would be free to recycle it out from under the pointer.
331
+ *
332
+ * Callers must still RB_GC_GUARD the variable after their last use of the
333
+ * pointer whenever the GC can run in between - that is, whenever they release
334
+ * the GVL or call into Ruby.
335
+ *
323
336
  * No Ruby usage (internal function)
324
337
  *
325
- * @param str the Ruby string
338
+ * @param str pointer to the Ruby string, updated in place if it is converted
326
339
  * @param len pointer to a size_t in which to store the number of characters
327
340
  * @return a C string version of str
328
341
  */
329
342
  char *
330
- rm_str2cstr(VALUE str, size_t *len)
343
+ rm_str2cstr(VALUE *str, size_t *len)
331
344
  {
332
- StringValue(str);
345
+ StringValue(*str);
333
346
  if (len)
334
347
  {
335
- *len = (size_t)RSTRING_LEN(str);
348
+ *len = (size_t)RSTRING_LEN(*str);
336
349
  }
337
- return RSTRING_PTR(str);
350
+ return RSTRING_PTR(*str);
338
351
  }
339
352
 
340
353
 
@@ -350,19 +363,20 @@ rm_str2cstr(VALUE str, size_t *len)
350
363
  *
351
364
  * No Ruby usage (internal function)
352
365
  *
353
- * @param str the Ruby string
366
+ * @param str pointer to the Ruby string, updated in place if it is converted
354
367
  * @param len pointer to a size_t in which to store the number of characters
355
368
  * @return a C string version of str
356
369
  * @throw ArgumentError if str contains a NUL byte
370
+ * @see rm_str2cstr for why the argument is taken by reference
357
371
  */
358
372
  char *
359
- rm_path2cstr(VALUE str, size_t *len)
373
+ rm_path2cstr(VALUE *str, size_t *len)
360
374
  {
361
- char *path = StringValueCStr(str);
375
+ char *path = StringValueCStr(*str);
362
376
 
363
377
  if (len)
364
378
  {
365
- *len = (size_t)RSTRING_LEN(str);
379
+ *len = (size_t)RSTRING_LEN(*str);
366
380
  }
367
381
  return path;
368
382
  }
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Magick
4
- VERSION = '7.1.0'
4
+ VERSION = '7.1.2'
5
5
  MIN_RUBY_VERSION = '3.2.0'
6
6
  MIN_IM6_VERSION = '6.9.0'
7
7
  MIN_IM7_VERSION = '7.1.0'
@@ -17,7 +17,7 @@ module Magick
17
17
  end
18
18
  end
19
19
 
20
- [Shape, TextBase, Image, Group, Content, Use, ClipPath, Pattern, self].each do |c|
20
+ [Shape, TextBase, Image, Group, Content, Use, ClipPath, Pattern, RVG].each do |c|
21
21
  c.class_eval do
22
22
  def deep_equal(other)
23
23
  ivs = instance_variables
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rmagick
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.1.0
4
+ version: 7.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Hunter
@@ -143,7 +143,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
143
143
  version: '0'
144
144
  requirements:
145
145
  - ImageMagick 6.9.0+ (for ImageMagick 6) or 7.1.0+ (for ImageMagick 7)
146
- rubygems_version: 4.0.16
146
+ rubygems_version: 4.0.18
147
147
  specification_version: 4
148
148
  summary: Ruby binding to ImageMagick
149
149
  test_files: []