├── .gitignore ├── .rspec ├── .travis.yml ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── carrierwave-processing.gemspec ├── lib ├── carrierwave-processing.rb └── carrierwave-processing │ ├── mini_magick.rb │ ├── rmagick.rb │ └── version.rb └── spec ├── fixtures ├── little_foxes.jpg ├── little_foxes_cmyk.jpg └── weird_orientation.jpg ├── integration └── carrierwave_processing_spec.rb └── spec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | .rvmrc 7 | .ruby-version 8 | .ruby-gemset 9 | Gemfile.lock 10 | InstalledFiles 11 | _yardoc 12 | coverage 13 | doc/ 14 | lib/bundler/man 15 | pkg 16 | rdoc 17 | spec/reports 18 | test/tmp 19 | test/version_tmp 20 | tmp 21 | spec/uploads 22 | uploads 23 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format documentation 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 1.9.3 4 | - 2.0.0 5 | - 2.1.0 6 | - jruby-19mode 7 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in carrierwave-processing.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Pavel Forkert 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CarrierWave::Processing 2 | 3 | [![Travis CI](https://secure.travis-ci.org/fxposter/carrierwave-processing.png)](http://travis-ci.org/fxposter/carrierwave-processing) 4 | 5 | Additional processing support for MiniMagick and RMagick. These are processors that I've been using in multiple projects so I decided to extract those as a gem. 6 | 7 | ## Installation 8 | 9 | Add this line to your application's Gemfile: 10 | 11 | gem 'carrierwave-processing' 12 | 13 | And then execute: 14 | 15 | $ bundle 16 | 17 | Or install it yourself as: 18 | 19 | $ gem install carrierwave-processing 20 | 21 | ## Usage 22 | 23 | This gem add several useful methods to CarrierWave processing RMagick and MiniMagick modules: `quality`, `strip`, `blur` and `colorspace`. 24 | To use those, you should include specified module (RMagick or MiniMagick) into your uploader and use processors: 25 | 26 | class AvatarUploader < CarrierWave::Uploader::Base 27 | include CarrierWave::RMagick 28 | include CarrierWave::Processing::RMagick 29 | 30 | process :strip # strip image of all profiles and comments 31 | process :resize_to_fill => [200, 200] 32 | process :quality => 90 # Set JPEG/MIFF/PNG compression level (0-100) 33 | process :convert => 'png' 34 | process :colorspace => :rgb # Set colorspace to rgb or cmyk 35 | process :blur => [0, 8] #reduce image noise and reduce detail levels [radius,sigma] 36 | process :auto_orient # Rotate the image if it has orientation data 37 | 38 | def filename 39 | super.chomp(File.extname(super)) + '.png' 40 | end 41 | end 42 | 43 | ## Contributing 44 | 45 | 1. Fork it 46 | 2. Create your feature branch (`git checkout -b my-new-feature`) 47 | 3. Commit your changes (`git commit -am 'Add some feature'`) 48 | 4. Push to the branch (`git push origin my-new-feature`) 49 | 5. Create new Pull Request 50 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | require 'bundler/gem_tasks' 3 | require 'rspec/core/rake_task' 4 | 5 | RSpec::Core::RakeTask.new(:spec) do |spec| 6 | end 7 | 8 | task :default => :spec 9 | -------------------------------------------------------------------------------- /carrierwave-processing.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require File.expand_path('../lib/carrierwave-processing/version', __FILE__) 3 | 4 | Gem::Specification.new do |gem| 5 | gem.authors = ["Pavel Forkert"] 6 | gem.email = ["fxposter@gmail.com"] 7 | gem.description = %q{Additional processing support for MiniMagick and RMagick} 8 | gem.summary = %q{Additional processing support for MiniMagick and RMagick} 9 | gem.homepage = "https://github.com/fxposter/carrierwave-processing" 10 | 11 | gem.files = `git ls-files`.split($\) 12 | gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } 13 | gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) 14 | gem.name = "carrierwave-processing" 15 | gem.require_paths = ["lib"] 16 | gem.version = CarrierWave::Processing::VERSION 17 | 18 | gem.add_dependency 'carrierwave' 19 | gem.add_development_dependency 'rake' 20 | gem.add_development_dependency 'rspec', '~> 3.0.0.beta1' 21 | gem.add_development_dependency 'mini_magick' 22 | gem.add_development_dependency 'rmagick' if RUBY_PLATFORM != 'java' 23 | end 24 | -------------------------------------------------------------------------------- /lib/carrierwave-processing.rb: -------------------------------------------------------------------------------- 1 | require 'carrierwave-processing/version' 2 | 3 | module CarrierWave 4 | module Processing 5 | autoload :RMagick, 'carrierwave-processing/rmagick' 6 | autoload :MiniMagick, 'carrierwave-processing/mini_magick' 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/carrierwave-processing/mini_magick.rb: -------------------------------------------------------------------------------- 1 | module CarrierWave 2 | module Processing 3 | module MiniMagick 4 | # Strips out all embedded information from the image 5 | # 6 | # process :strip 7 | # 8 | def strip 9 | manipulate! do |img| 10 | img.strip 11 | img = yield(img) if block_given? 12 | img 13 | end 14 | end 15 | 16 | # Reduces the quality of the image to the percentage given 17 | # 18 | # process :quality => 90 19 | # 20 | def quality(percentage) 21 | manipulate! do |img| 22 | if img["%Q"].to_i != percentage 23 | img.quality(percentage.to_s) 24 | end 25 | img = yield(img) if block_given? 26 | img 27 | end 28 | end 29 | 30 | # Sets the colorspace of the image to the specified value. 31 | # 32 | # process :colorspace => :rgb # force rgb 33 | # process :colorspace => :cmyk # force cmyk 34 | # 35 | def colorspace(cs) 36 | manipulate! do |img| 37 | img.combine_options do |c| 38 | case cs.to_sym 39 | when :rgb 40 | c.colorspace "sRGB" 41 | when :cmyk 42 | c.colorspace "CMYK" 43 | end 44 | end 45 | img = yield(img) if block_given? 46 | img 47 | end 48 | end 49 | 50 | # reduce image noise and reduce detail levels 51 | # 52 | # process :blur => [0, 8] 53 | # 54 | def blur(radius, sigma) 55 | manipulate! do |img| 56 | img.blur "#{radius}x#{sigma}" 57 | img = yield(img) if block_given? 58 | img 59 | end 60 | end 61 | 62 | # Auto-orients the image 63 | # 64 | # process :auto_orient 65 | def auto_orient 66 | manipulate! do |img| 67 | img.auto_orient 68 | img 69 | end 70 | end 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /lib/carrierwave-processing/rmagick.rb: -------------------------------------------------------------------------------- 1 | module CarrierWave 2 | module Processing 3 | module RMagick 4 | # Strips out all embedded information from the image 5 | # 6 | # process :strip 7 | # 8 | def strip 9 | manipulate! do |img| 10 | img.strip! 11 | img = yield(img) if block_given? 12 | img 13 | end 14 | end 15 | 16 | # Reduces the quality of the image to the percentage given 17 | # 18 | # process :quality => 90 19 | # 20 | def quality(percentage) 21 | manipulate! do |img| 22 | if img.quality != percentage 23 | img.write(current_path){ self.quality = percentage } 24 | end 25 | img = yield(img) if block_given? 26 | img 27 | end 28 | end 29 | 30 | # Sets the colorspace of the image to the specified value. 31 | # 32 | # process :colorspace => :rgb # force rgb 33 | # process :colorspace => :cmyk # force cmyk 34 | # 35 | def colorspace(cs) 36 | manipulate! do |img| 37 | case cs.to_sym 38 | when :rgb 39 | img.colorspace = Magick::RGBColorspace 40 | when :cmyk 41 | img.colorspace = Magick::CMYKColorspace 42 | end 43 | img = yield(img) if block_given? 44 | img 45 | end 46 | end 47 | 48 | 49 | # reduce image noise and reduce detail levels 50 | # 51 | # process :blur => [0, 8] 52 | # 53 | def blur(radius, sigma) 54 | manipulate! do |img| 55 | img = img.blur_image(radius, sigma) 56 | img = yield(img) if block_given? 57 | img 58 | end 59 | end 60 | 61 | # Auto-orients the image 62 | # 63 | # process :auto_orient 64 | def auto_orient 65 | manipulate! do |img| 66 | img.auto_orient! 67 | img 68 | end 69 | end 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/carrierwave-processing/version.rb: -------------------------------------------------------------------------------- 1 | module CarrierWave 2 | module Processing 3 | VERSION = '1.1.0' 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/fixtures/little_foxes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fxposter/carrierwave-processing/fffb380c258efc61f74d3276c0d5cac9157ab479/spec/fixtures/little_foxes.jpg -------------------------------------------------------------------------------- /spec/fixtures/little_foxes_cmyk.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fxposter/carrierwave-processing/fffb380c258efc61f74d3276c0d5cac9157ab479/spec/fixtures/little_foxes_cmyk.jpg -------------------------------------------------------------------------------- /spec/fixtures/weird_orientation.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fxposter/carrierwave-processing/fffb380c258efc61f74d3276c0d5cac9157ab479/spec/fixtures/weird_orientation.jpg -------------------------------------------------------------------------------- /spec/integration/carrierwave_processing_spec.rb: -------------------------------------------------------------------------------- 1 | require 'carrierwave' 2 | require 'carrierwave-processing' 3 | 4 | require 'spec_helper' 5 | 6 | working_module_names = (RUBY_PLATFORM == 'java' ? %w[MiniMagick] : %w[RMagick MiniMagick]) 7 | 8 | working_module_names.each do |module_name| 9 | describe CarrierWave::Processing.const_get(module_name) do 10 | before do 11 | FileUtils.rm_rf(fixture_path('uploads')) 12 | end 13 | 14 | it 'strips image metadata' do 15 | uploader = uploader_for(module_name) { 16 | process :strip 17 | } 18 | 19 | expect(exif(fixture_path('little_foxes.jpg'), 'Artist')).not_to be_empty 20 | 21 | open_fixture 'little_foxes.jpg' do |file| 22 | uploader.store!(file) 23 | end 24 | 25 | expect(exif(uploader.store_path, 'Artist')).to be_empty 26 | end 27 | 28 | it 'reduces image quality' do 29 | uploader = uploader_for(module_name) { 30 | process :quality => 50 31 | } 32 | 33 | open_fixture 'little_foxes.jpg' do |file| 34 | uploader.store!(file) 35 | end 36 | 37 | expect(File.size(uploader.store_path)).to be < File.size(fixture_path('little_foxes.jpg')) 38 | end 39 | 40 | it 'changes image colorspace to CMYK' do 41 | uploader = uploader_for(module_name) { 42 | process :colorspace => :cmyk 43 | } 44 | 45 | expect(colorspace(fixture_path('little_foxes.jpg'))).to include('RGB') 46 | 47 | open_fixture 'little_foxes.jpg' do |file| 48 | uploader.store!(file) 49 | end 50 | 51 | expect(colorspace(uploader.store_path)).to eq('CMYK') 52 | end 53 | 54 | it 'changes image colorspace to sRGB' do 55 | uploader = uploader_for(module_name) { 56 | process :colorspace => :rgb 57 | } 58 | 59 | expect(colorspace(fixture_path('little_foxes_cmyk.jpg'))).to eq('CMYK') 60 | 61 | open_fixture 'little_foxes_cmyk.jpg' do |file| 62 | uploader.store!(file) 63 | end 64 | 65 | expect(colorspace(uploader.store_path)).to include('RGB') 66 | end 67 | 68 | it 'blurs image' do 69 | uploader = uploader_for(module_name) { 70 | process :blur => [5, 5] 71 | } 72 | 73 | open_fixture 'little_foxes.jpg' do |file| 74 | uploader.store!(file) 75 | end 76 | 77 | expect(File.size(uploader.store_path)).to be < File.size(fixture_path('little_foxes.jpg')) 78 | end 79 | 80 | it 'auto-orients the image' do 81 | uploader = uploader_for(module_name) { 82 | process :auto_orient 83 | } 84 | 85 | open_fixture 'weird_orientation.jpg' do |file| 86 | uploader.store!(file) 87 | end 88 | 89 | modified_orientation = exif(uploader.store_path, 'Orientation') 90 | original_orientation = exif(fixture_path('weird_orientation.jpg'), 'Orientation') 91 | 92 | expect(modified_orientation).to eq('1') 93 | expect(modified_orientation).not_to eq(original_orientation) 94 | end 95 | end 96 | end 97 | 98 | describe CarrierWave::Processing do 99 | before do 100 | FileUtils.rm_rf(fixture_path('uploads')) 101 | end 102 | 103 | it 'produces the same transformations for all modules' do 104 | uploaders = working_module_names.map { |module_name| 105 | uploader_for(module_name) { 106 | process :strip 107 | process :quality => 90 108 | process :blur => [0, 2] 109 | process :colorspace => :cmyk 110 | process :auto_orient 111 | } 112 | } 113 | 114 | ['little_foxes.jpg', 'weird_orientation.jpg'].each do |test_filename| 115 | uploaders.each do |uploader| 116 | open_fixture test_filename do |file| 117 | uploader.store!(file) 118 | end 119 | end 120 | 121 | files = uploaders.map { |uploader| File.open(uploader.store_path, 'rb', &:read) } 122 | expect(files).to be_all { |file| file == files[0] } 123 | end 124 | end 125 | end 126 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | 3 | module Helpers 4 | def fixture_path(filename) 5 | File.expand_path(File.join('..', 'fixtures', filename), __FILE__) 6 | end 7 | 8 | def open_fixture(filename, &block) 9 | File.open(fixture_path(filename), &block) 10 | end 11 | 12 | def exif(path, field) 13 | `identify -format %[exif:#{field}] #{path}`.chomp 14 | end 15 | 16 | def colorspace(path) 17 | `identify -verbose #{path} | grep 'Colorspace'`.chomp.split(':').last.strip 18 | end 19 | 20 | def uploader_for(module_name, &block) 21 | Class.new(CarrierWave::Uploader::Base) { 22 | include Helpers 23 | include CarrierWave.const_get(module_name) 24 | include CarrierWave::Processing.const_get(module_name) 25 | 26 | instance_eval(&block) 27 | 28 | define_method :store_dir do 29 | File.expand_path(File.join('..', 'uploads'), __FILE__) 30 | end 31 | 32 | define_method :filename do 33 | "#{module_name}#{File.extname(super())}" if original_filename.present? 34 | end 35 | }.new 36 | end 37 | end 38 | 39 | RSpec.configure do |config| 40 | config.include Helpers 41 | end 42 | --------------------------------------------------------------------------------