diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 0ed445e55e2..151fd712cb3 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -1763,6 +1763,7 @@ en: png_to_jpg_quality: "Quality of the converted JPG file (1 is lowest quality, 99 is best quality, 100 to disable)." allow_staff_to_upload_any_file_in_pm: "Allow staff members to upload any files in PM." + convert_heif_to_jpeg: "Convert '.heic' or '.heif' image uploads to jpeg." strip_image_metadata: "Strip image metadata." diff --git a/config/site_settings.yml b/config/site_settings.yml index c8dcaa6fd74..c1123380af6 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -1302,6 +1302,8 @@ files: decompressed_backup_max_file_size_mb: default: 100000 hidden: true + convert_heif_to_jpeg: + default: false trust: default_trust_level: diff --git a/lib/upload_creator.rb b/lib/upload_creator.rb index ea05115b20b..acab69e9c07 100644 --- a/lib/upload_creator.rb +++ b/lib/upload_creator.rb @@ -40,6 +40,12 @@ class UploadCreator is_image = false if @opts[:for_theme] DistributedMutex.synchronize("upload_#{user_id}_#{@filename}") do + # We need to convert HEIFs early because FastImage does not consider them as images + if convert_heif_to_jpeg? + convert_heif! + is_image = FileHelper.is_supported_image?("test.#{@image_info.type}") + end + if is_image extract_image_info! return @upload if @upload.errors.present? @@ -211,6 +217,28 @@ class UploadCreator end end + def convert_heif_to_jpeg? + SiteSetting.convert_heif_to_jpeg && File.extname(@filename).downcase.match?(/\.hei(f|c)$/) + end + + def convert_heif! + jpeg_tempfile = Tempfile.new(["image", ".jpg"]) + from = @file.path + to = jpeg_tempfile.path + OptimizedImage.ensure_safe_paths!(from, to) + + begin + execute_convert(from, to) + rescue + # retry with debugging enabled + execute_convert(from, to, true) + end + + @file.respond_to?(:close!) ? @file.close! : @file.close + @file = jpeg_tempfile + extract_image_info! + end + def execute_convert(from, to, debug = false) command = [ "convert", diff --git a/spec/fixtures/images/should_be_jpeg.heic b/spec/fixtures/images/should_be_jpeg.heic new file mode 100644 index 00000000000..0eb9f4f4ea0 Binary files /dev/null and b/spec/fixtures/images/should_be_jpeg.heic differ diff --git a/spec/lib/upload_creator_spec.rb b/spec/lib/upload_creator_spec.rb index 9b0098e264b..91a431565cb 100644 --- a/spec/lib/upload_creator_spec.rb +++ b/spec/lib/upload_creator_spec.rb @@ -170,6 +170,28 @@ RSpec.describe UploadCreator do end end + describe 'converting HEIF to jpeg' do + let(:filename) { "should_be_jpeg.heic" } + let(:file) { file_from_fixtures(filename, "images") } + + before do + SiteSetting.convert_heif_to_jpeg = true + SiteSetting.authorized_extensions = 'jpg|heic' + end + + it 'should store the upload with the right extension' do + expect do + UploadCreator.new(file, filename).create_for(user.id) + end.to change { Upload.count }.by(1) + + upload = Upload.last + + expect(upload.extension).to eq('jpeg') + expect(File.extname(upload.url)).to eq('.jpeg') + expect(upload.original_filename).to eq('should_be_jpeg.jpg') + end + end + describe 'secure attachments' do let(:filename) { "small.pdf" } let(:file) { file_from_fixtures(filename, "pdf") }