FEATURE: Support converting HEIF images to JPEG (#10079)

FEATURE: Support converting HEIF images to JPEG (#10079)

diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index 0ed445e..151fd71 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 c8dcaa6..c112338 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 ea05115..acab69e 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 0000000..0eb9f4f
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 9b0098e..91a4315 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") }

GitHub sha: 7559758e

This commit appears in #10079 which was merged by pmusaraj.