-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ add support for image & PDF compression (#120)
- Loading branch information
1 parent
ae952d5
commit 1660d46
Showing
18 changed files
with
587 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative 'image/image_compressor' | ||
require_relative 'image/image_utils' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# frozen_string_literal: true | ||
|
||
module Mindee | ||
# Image processing module. | ||
module Image | ||
# Image compressor module to handle image compression. | ||
module ImageCompressor | ||
# Resize and/or compress an SKBitmap. This assumes the ratio was provided before hands. | ||
# @param image [MiniMagick::Image, StringIO] Input image. | ||
# @param quality [Integer, nil] Quality of the final file. | ||
# @param max_width [Integer, nil] 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. | ||
# @return [StringIO] | ||
def self.compress_image(image, quality: 85, max_width: nil, max_height: nil) | ||
processed_image = ImageUtils.to_image(image) | ||
processed_image.format 'jpg' | ||
final_width, final_height = ImageUtils.calculate_new_dimensions( | ||
processed_image, | ||
max_width: max_width, | ||
max_height: max_height | ||
) | ||
ImageUtils.resize_image(processed_image, final_width, final_height) if final_width || final_height | ||
ImageUtils.compress_image_quality(processed_image, quality) | ||
|
||
ImageUtils.image_to_stringio(processed_image) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
# 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 from 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
# frozen_string_literal: true | ||
|
||
require_relative 'pdf/pdf_processing' | ||
require_relative 'pdf/pdf_compressor' | ||
require_relative 'pdf/pdf_processor' | ||
require_relative 'pdf/pdf_tools' |
Oops, something went wrong.