diff --git a/app/models/concerns/attachment_auto_rotate_concern.rb b/app/models/concerns/attachment_auto_rotate_concern.rb new file mode 100644 index 000000000..9fba8a9ab --- /dev/null +++ b/app/models/concerns/attachment_auto_rotate_concern.rb @@ -0,0 +1,22 @@ +module AttachmentAutoRotateConcern + extend ActiveSupport::Concern + + included do + after_create_commit :auto_rotate + end + + private + + def auto_rotate + return if blob.nil? + return if ["image/jpeg", "image/jpg"].exclude?(blob.content_type) + + blob.open do |file| + Tempfile.create(["rotated", File.extname(file)]) do |output| + processed = AutoRotateService.new.process(file, output) + blob.upload(processed) # also update checksum & byte_size accordingly + blob.save! + end + end + end +end diff --git a/app/services/auto_rotate_service.rb b/app/services/auto_rotate_service.rb new file mode 100644 index 000000000..39813c8c5 --- /dev/null +++ b/app/services/auto_rotate_service.rb @@ -0,0 +1,30 @@ +class AutoRotateService + def process(file, output) + auto_rotate_image(file, output) + output + end + + private + + def auto_rotate_image(file, output) + image = MiniMagick::Image.new(file.to_path) + + case image["%[orientation]"] + when 'LeftBottom' + rotate_image(file, output, 90) + when 'BottomRight' + rotate_image(file, output, 180) + when 'RightTop' + rotate_image(file, output, 270) + end + end + + def rotate_image(file, output, degree) + MiniMagick::Tool::Convert.new do |convert| + convert << file.to_path + convert.rotate(degree) + convert.auto_orient + convert << output.to_path + end + end +end diff --git a/config/initializers/active_storage.rb b/config/initializers/active_storage.rb index 042333ae3..c20df224f 100644 --- a/config/initializers/active_storage.rb +++ b/config/initializers/active_storage.rb @@ -17,6 +17,7 @@ end ActiveSupport.on_load(:active_storage_attachment) do include AttachmentTitreIdentiteWatermarkConcern include AttachmentVirusScannerConcern + include AttachmentAutoRotateConcern end Rails.application.reloader.to_prepare do diff --git a/spec/fixtures/files/image-rotated.jpg b/spec/fixtures/files/image-rotated.jpg new file mode 100644 index 000000000..2f58be2c8 Binary files /dev/null and b/spec/fixtures/files/image-rotated.jpg differ diff --git a/spec/services/auto_rotate_service_spec.rb b/spec/services/auto_rotate_service_spec.rb new file mode 100644 index 000000000..9895be7d6 --- /dev/null +++ b/spec/services/auto_rotate_service_spec.rb @@ -0,0 +1,15 @@ +RSpec.describe AutoRotateService do + let(:image) { file_fixture("image-rotated.jpg") } + let(:auto_rotate_service) { AutoRotateService.new } + + describe '#process' do + it 'returns a tempfile if auto_rotate succeeds' do + Tempfile.create do |output| + auto_rotate_service.process(image, output) + expect(MiniMagick::Image.new(image.to_path)["%[orientation]"]).to eq('LeftBottom') + expect(MiniMagick::Image.new(output.to_path)["%[orientation]"]).to eq('TopLeft') + expect(output.size).to be_between(image.size / 1.2, image.size) + end + end + end +end