86 lines
2.4 KiB
Ruby
86 lines
2.4 KiB
Ruby
class TitreIdentiteWatermarkJob < ApplicationJob
|
|
class FileNotScannedYetError < StandardError
|
|
end
|
|
|
|
# If by the time the job runs the blob has been deleted, ignore the error
|
|
discard_on ActiveRecord::RecordNotFound
|
|
# If the file is deleted during the scan, ignore the error
|
|
discard_on ActiveStorage::FileNotFoundError
|
|
# If the file is not analyzed or scanned for viruses yet, retry later
|
|
# (to avoid modifying the file while it is being scanned).
|
|
retry_on FileNotScannedYetError, wait: :exponentially_longer, attempts: 10
|
|
|
|
MAX_IMAGE_SIZE = 1500
|
|
SCALE = 0.9
|
|
WATERMARK = URI.parse(WATERMARK_FILE).is_a?(URI::HTTP) ? WATERMARK_FILE : Rails.root.join("app/assets/images/#{WATERMARK_FILE}")
|
|
|
|
def perform(blob)
|
|
return if blob.watermark_done?
|
|
raise FileNotScannedYetError if blob.virus_scanner.pending?
|
|
|
|
blob.open do |file|
|
|
watermark = resize_watermark(file)
|
|
|
|
if watermark.present?
|
|
processed = watermark_image(file, watermark)
|
|
|
|
blob.upload(processed)
|
|
blob.touch(:watermarked_at)
|
|
end
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def watermark_image(file, watermark)
|
|
ImageProcessing::MiniMagick
|
|
.source(file)
|
|
.convert("png")
|
|
.resize_to_limit(MAX_IMAGE_SIZE, MAX_IMAGE_SIZE)
|
|
.composite(watermark, mode: "over", gravity: "center")
|
|
.call
|
|
end
|
|
|
|
def resize_watermark(file)
|
|
metadata = image_metadata(file)
|
|
|
|
if metadata[:width].present? && metadata[:height].present?
|
|
width = [metadata[:width], MAX_IMAGE_SIZE].min * SCALE
|
|
height = [metadata[:height], MAX_IMAGE_SIZE].min * SCALE
|
|
diagonal = Math.sqrt(height**2 + width**2)
|
|
angle = Math.asin(height / diagonal) * 180 / Math::PI
|
|
|
|
ImageProcessing::MiniMagick
|
|
.source(WATERMARK)
|
|
.resize_to_limit(diagonal, diagonal / 2)
|
|
.rotate(-angle, background: :transparent)
|
|
.call
|
|
end
|
|
end
|
|
|
|
def image_metadata(file)
|
|
read_image(file) do |image|
|
|
if rotated_image?(image)
|
|
{ width: image.height, height: image.width }
|
|
else
|
|
{ width: image.width, height: image.height }
|
|
end
|
|
end
|
|
end
|
|
|
|
def read_image(file)
|
|
require "mini_magick"
|
|
image = MiniMagick::Image.new(file.path)
|
|
|
|
if image.valid?
|
|
yield image
|
|
else
|
|
logger.info "Skipping image analysis because ImageMagick doesn't support the file"
|
|
{}
|
|
end
|
|
end
|
|
|
|
def rotated_image?(image)
|
|
['RightTop', 'LeftBottom'].include?(image["%[orientation]"])
|
|
end
|
|
end
|