diff --git a/lib/dynamic_image/errors.rb b/lib/dynamic_image/errors.rb index c143481..b8bae16 100644 --- a/lib/dynamic_image/errors.rb +++ b/lib/dynamic_image/errors.rb @@ -33,6 +33,7 @@ class InvalidSignature < DynamicImage::Errors::Error; end # the size works out to less than a pixel, as "0x0" does, or "1x1" against a very wide image. # # @see DynamicImage::ImageSizing#fit + # @see DynamicImage::Model::Transformations#resize class InvalidSizeOptions < DynamicImage::Errors::Error; end # Raised when a transformation can't be applied, either because a rotation isn't a multiple of 90 degrees or diff --git a/lib/dynamic_image/model/transformations.rb b/lib/dynamic_image/model/transformations.rb index 792eeb2..4f8dafe 100644 --- a/lib/dynamic_image/model/transformations.rb +++ b/lib/dynamic_image/model/transformations.rb @@ -17,9 +17,11 @@ module Transformations # @param max_size [Vector2d, String] the size to fit within, either a vector or a "{width}x{height}" # string. Either dimension may be omitted to scale by the other alone. # @return [self] + # @raise [DynamicImage::Errors::InvalidSizeOptions] if the result is less than a pixel in either dimension + # @raise [DynamicImage::Errors::InvalidImage] if the stored data can't be processed def resize(max_size) transform_image do |image| - resized = image.resize(real_size.fit(max_size)) + resized = image.resize(fit_size(max_size)) scale_crop(resized.size) resized end @@ -30,6 +32,7 @@ def resize(max_size) # @param degrees [Integer] the angle, which must be a multiple of 90. Rotating by 0 is a no-op. # @return [self] # @raise [DynamicImage::Errors::InvalidTransformation] if the angle isn't a multiple of 90 + # @raise [DynamicImage::Errors::InvalidImage] if the stored data can't be processed # # @example # image.rotate(90) @@ -52,6 +55,16 @@ def rotate(degrees = 90) private + # Scales +max_size+ against the image, rejecting a result smaller than a pixel in either dimension, as + # {DynamicImage::ImageSizing#fit} does for the sizes rendered on request. + def fit_size(max_size) + new_size = real_size.fit(max_size) + return new_size if new_size.x >= 1 && new_size.y >= 1 + + raise DynamicImage::Errors::InvalidSizeOptions, + "#{new_size} has a dimension smaller than one pixel" + end + def scale_crop(new_size) scale = new_size.to_f_vector / real_size @@ -121,6 +134,8 @@ def transform_image(&block) end read_image_metadata self + rescue Vips::Error => e + raise DynamicImage::Errors::InvalidImage, e.message end end end diff --git a/spec/dynamic_image/model/transformations_spec.rb b/spec/dynamic_image/model/transformations_spec.rb index b7c3c00..23ecc22 100644 --- a/spec/dynamic_image/model/transformations_spec.rb +++ b/spec/dynamic_image/model/transformations_spec.rb @@ -77,6 +77,27 @@ expect(resized.crop_gravity).to eq(Vector2d(1, 1)) end end + + context "when the size is smaller than a pixel" do + subject(:resized) { image.resize("0x0") } + + it "raises an error" do + expect { resized }.to( + raise_error(DynamicImage::Errors::InvalidSizeOptions) + ) + end + end + + context "when vips fails to process the data" do + before do + allow(DynamicImage::ImageProcessor) + .to receive(:new).and_raise(Vips::Error, "unable to read") + end + + it "raises an error" do + expect { resized }.to raise_error(DynamicImage::Errors::InvalidImage) + end + end end describe "#rotate" do @@ -96,6 +117,17 @@ end end + context "when vips fails to process the data" do + before do + allow(DynamicImage::ImageProcessor) + .to receive(:new).and_raise(Vips::Error, "unable to read") + end + + it "raises an error" do + expect { rotated }.to raise_error(DynamicImage::Errors::InvalidImage) + end + end + context "with a 90 degree rotation" do it "rotates the image" do expect(rotated.real_size).to eq(Vector2d.new(200, 320))