lib/mindee/image/image_utils.rb



# frozen_string_literal: true

module Mindee
  # Image processing module.
  module Image
    # Miscellaneous image operations.
    module ImageUtils
      # Resizes a provided MiniMagick Image with the given width & height, if present.
      # @param image [MiniMagick::Image] MiniMagick image handle.
      # @param width [Integer] Width to comply with.
      # @param height [Integer] Height to comply with.
      def self.resize_image(image, width, height)
        if width && height
          image.resize "#{width}x#{height}"
        elsif width
          image.resize width.to_s
        elsif height
          image.resize "x#{height}"
        end
      end

      # Compresses the quality of the provided MiniMagick image.
      # @param image [MiniMagick::Image] MiniMagick image handle.
      # @param quality [Integer] Quality to apply to the image. This is independent of a JPG's base quality.
      def self.compress_image_quality(image, quality)
        image.quality quality.to_s
      end

      # Mostly here so that IDEs don't get confused on the type (@type annotation fails sometimes.)
      # @param [MiniMagick::Image, StringIO, File, Tempfile] image The input image
      # @return [MiniMagick::Image]
      def self.to_image(image)
        if image.respond_to?(:read) && image.respond_to?(:rewind)
          image.rewind
          MiniMagick::Image.read(image)
        elsif image.is_a?(MiniMagick::Image)
          image
        else
          raise "Expected an I/O object or a MiniMagick::Image. '#{image.class}' given instead."
        end
      end

      # Converts a StringIO containing an image into a MiniMagick image.
      # @param image [MiniMagick::Image] the input image.
      # @param format [String] Format parameter, left open for the future, but should be JPEG for current use-cases.
      # @return [StringIO]
      def self.image_to_stringio(image, format = 'JPEG')
        image.format format
        blob = image.to_blob
        stringio = StringIO.new(blob)
        stringio.rewind

        stringio
      end

      # Computes the new dimensions for a given SKBitmap, and returns a scaled down version of it relative to the
      # provided bounds.
      # @param [MiniMagick::Image] original Input MiniMagick image.
      # @param max_width [Integer] Maximum width. If not specified, the horizontal ratio will remain the same.
      # @param max_height [Integer] Maximum height. If not specified, the vertical ratio will remain the same.
      def self.calculate_new_dimensions(original, max_width: nil, max_height: nil)
        raise 'Provided image could not be processed for resizing.' if original.nil?

        return [original.width, original.height] if max_width.nil? && max_height.nil?

        width_ratio = max_width ? max_width.to_f / original.width : Float::INFINITY
        height_ratio = max_height ? max_height.to_f / original.height : Float::INFINITY

        scale_factor = [width_ratio, height_ratio].min

        new_width = (original.width * scale_factor).to_i
        new_height = (original.height * scale_factor).to_i

        [new_width, new_height]
      end

      # Computes the Height & Width from a page's media box. Falls back to the size of the initial image.
      # @param image [MiniMagick::Image] The initial image that will fit into the page.
      # @param media_box [Array<Integer>, nil]
      # @return [Array<Integer>]
      def self.calculate_dimensions_from_media_box(image, media_box)
        if !media_box.nil? && media_box.any?
          [
            media_box[2]&.to_i || image[:width].to_i,
            media_box[3]&.to_i || image[:height].to_i,
          ]
        else
          [image[:width].to_i, image[:height].to_i]
        end
      end

      # Transforms a PDF into a MagickImage. This is currently used for single-page PDFs.
      # @param pdf_stream [StringIO] Input stream.
      # @param image_quality [Integer] Quality to apply to the image.
      # @return [MiniMagick::Image]
      def self.pdf_to_magick_image(pdf_stream, image_quality)
        compressed_image = MiniMagick::Image.read(pdf_stream.read)
        compressed_image.format('jpg')
        compressed_image.quality image_quality.to_s
        compressed_image
      end
    end
  end
end