rscale 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,11 @@
1
+ module RScale
2
+ class Configuration
3
+ attr_accessor :root
4
+ attr_accessor :public
5
+
6
+ def initialize(root=nil, public=nil)
7
+ @root = root || File.dirname(__FILE__)
8
+ @public = public || @root + '/public'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ module RScale
2
+ class ConfigError < Exception ; end
3
+ class FormatError < Exception ; end
4
+ class ProcessingError < Exception ; end
5
+ end
@@ -0,0 +1,19 @@
1
+ module RScale
2
+ class Format
3
+ attr_reader :name
4
+ attr_accessor :styles, :url
5
+
6
+ def initialize(name)
7
+ @name = name.to_s
8
+ @styles = {}
9
+ @url = '/:format/:style/:filename.:extension'
10
+ end
11
+
12
+ def style(name, opts={})
13
+ raise FormatError, 'Options required!' if opts.empty?
14
+ raise FormatError, 'Options must be a Hash!' unless opts.kind_of?(Hash)
15
+ opts[:style] = name
16
+ @styles[name] = opts
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,24 @@
1
+ module RScale::Processor
2
+ class Geometry
3
+ attr_reader :width, :height
4
+
5
+ def initialize(w, h) ; @width = w ; @height = h ; end
6
+ def square? ; width == height ; end
7
+ def horizontal? ; width > height ; end
8
+ def vertical? ; width < height ; end
9
+ def ratio ; width.to_f / height.to_f ; end
10
+ def to_s ; "#{width}x#{height}" ; end
11
+ def to_crop_resize ; horizontal? ? "#{width}x" : "x#{height}" ; end
12
+
13
+ # parse dimensions from WxH string (W - width, H - height)
14
+ def self.parse(str)
15
+ sz = str.split('x').collect { |v| v.to_i }
16
+ Geometry.new(sz[0], sz[1])
17
+ end
18
+
19
+ # parse geomerty from file using imagemagick identify command
20
+ def self.from_file(file)
21
+ Geometry.parse(`identify -format %wx%h #{file}`.strip)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,43 @@
1
+ class String
2
+ def shellescape
3
+ self.gsub(/\s/, '\ ')
4
+ end
5
+
6
+ def placeholders(values={})
7
+ self.gsub(/:([a-z0-9_-]{1,})/i) { values[$1.to_sym] }
8
+ end
9
+ end
10
+
11
+ class Hash
12
+ def extract(*keys)
13
+ keys.inject({}) { |h,k| h[k] = self[k] if key?(k); h }
14
+ end
15
+ end
16
+
17
+ module Digest
18
+ BLOCK_SIZE = 8192
19
+
20
+ class MD5
21
+ def self.filedigest(path)
22
+ d = Digest::MD5.new
23
+ File.open(path, 'r') { |f| d.update(f.read(BLOCK_SIZE)) until f.eof? }
24
+ d.hexdigest
25
+ end
26
+ end
27
+
28
+ class SHA1
29
+ def self.filedigest(path)
30
+ d = Digest::SHA1.new
31
+ File.open(path, 'r') { |f| d.update(f.read(BLOCK_SIZE)) until f.eof? }
32
+ d.hexdigest
33
+ end
34
+ end
35
+ end
36
+
37
+ class File
38
+ def self.get_info(path)
39
+ ext = File.extname(path) ; ext = ext[1..(ext.length-1)]
40
+ name = File.basename(path, File.extname(path))
41
+ return {:filename => name, :extension => ext, :filesize => File.size(path)}
42
+ end
43
+ end
@@ -0,0 +1,48 @@
1
+ require 'ftools'
2
+
3
+ module RScale
4
+ module Processor
5
+ @@sharp_level = [0.5, 0.5]
6
+ @@silent = true
7
+
8
+ class Convert
9
+ attr_reader :file_from, :file_to
10
+ attr_reader :options
11
+
12
+ def initialize(file_from, file_to)
13
+ @file_from = File.expand_path(file_from)
14
+ @file_to = File.expand_path(file_to)
15
+ @options = []
16
+ yield self if block_given?
17
+ end
18
+
19
+ def add(param, value)
20
+ @options << "-#{param.to_s} '#{value}'"
21
+ end
22
+
23
+ def execute
24
+ unless File.exists?(File.dirname(@file_to))
25
+ File.makedirs(File.dirname(@file_to))
26
+ end
27
+ `convert #{@file_from.shellescape} #{@options.join(' ')} #{@file_to.shellescape} 2>&1`
28
+ end
29
+ end
30
+
31
+ def self.process(file_in, file_out, opts={})
32
+ src = Geometry.from_file(file_in)
33
+ dst = Geometry.parse(opts[:size])
34
+ sz = dst.ratio > src.ratio ? "#{dst.width}x" : "x#{dst.height}"
35
+ opts[:crop] = true unless opts.key?(:crop)
36
+
37
+ convert = Convert.new(file_in, file_out) do |c|
38
+ c.add(:resize, sz)
39
+ c.add(:gravity, 'Center')
40
+ c.add(:crop, "#{dst}+0+0") if opts[:crop]
41
+ c.add(:sharpen, "#{@@sharp_level.first}{#{@@sharp_level.last}}'") if opts.key?(:sharp)
42
+ c.add(:quality, "#{opts[:q]}") if opts.key?(:q)
43
+ c.execute
44
+ end
45
+ return File.exists?(file_out)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,52 @@
1
+ module RScale
2
+ @@config = nil
3
+ @@formats = {}
4
+
5
+ def self.configure
6
+ @@config = Configuration.new if @@config.nil?
7
+ yield @@config
8
+ end
9
+
10
+ def self.format(name)
11
+ unless @@formats.key?(name)
12
+ @@formats[name] = Format.new(name)
13
+ yield @@formats[name] if block_given?
14
+ else
15
+ raise FormatError, "Format with name [#{name.to_s}] is already defined"
16
+ end
17
+ end
18
+
19
+ def self.formats
20
+ @@formats
21
+ end
22
+
23
+ def self.image_for(format, file)
24
+ if @@formats.key?(format.to_sym)
25
+ fmt = @@formats[format.to_sym]
26
+ file_info = File.get_info(file)
27
+ url = fmt.url
28
+ image = {}
29
+
30
+ options = file_info.merge(:format => fmt.name)
31
+
32
+ options[:time] = Time.now.to_i unless url[':time'].nil?
33
+ options[:md5] = Digest::MD5.filedigest(file) unless url[':md5'].nil?
34
+ unless url[':uuid'].nil?
35
+ options[:uuid] = `uuidgen`.strip.gsub(/-/,'')
36
+ options[:uuid_dir] = "#{options[:uuid][0,2]}/#{options[:uuid][2,2]}"
37
+ end
38
+
39
+ fmt.styles.each_pair do |k,v|
40
+ url = fmt.url.placeholders(options.merge(v))
41
+ file_out = @@config.public + "/#{url}"
42
+ if Processor.process(file, file_out, v)
43
+ image[k] = url
44
+ end
45
+ end
46
+
47
+ return image.size == fmt.styles.size ? image : nil
48
+ else
49
+ raise ArgumentError, "Format #{format.to_s} cannot be found!"
50
+ end
51
+ end
52
+ end
data/lib/rscale.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'rscale/rscale'
2
+ require 'rscale/errors'
3
+ require 'rscale/configuration'
4
+ require 'rscale/format'
5
+ require 'rscale/helpers'
6
+ require 'rscale/processor'
7
+ require 'rscale/geometry'
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rscale
3
+ version: !ruby/object:Gem::Version
4
+ hash: 9
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ version: "0.1"
10
+ platform: ruby
11
+ authors:
12
+ - Dan Sosedoff
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-07-02 00:00:00 -05:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: Image scaling wrapper based on ImageMagick console utils
22
+ email: dan.sosedoff@gmail.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - lib/rscale.rb
31
+ - lib/rscale/rscale.rb
32
+ - lib/rscale/errors.rb
33
+ - lib/rscale/configuration.rb
34
+ - lib/rscale/format.rb
35
+ - lib/rscale/helpers.rb
36
+ - lib/rscale/processor.rb
37
+ - lib/rscale/geometry.rb
38
+ has_rdoc: true
39
+ homepage: http://github.com/sosedoff/rscale
40
+ licenses: []
41
+
42
+ post_install_message:
43
+ rdoc_options:
44
+ - --charset=UTF-8
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ hash: 3
53
+ segments:
54
+ - 0
55
+ version: "0"
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 3
62
+ segments:
63
+ - 0
64
+ version: "0"
65
+ requirements: []
66
+
67
+ rubyforge_project:
68
+ rubygems_version: 1.3.7
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: Image scaling wrapper based on ImageMagick console utils
72
+ test_files: []
73
+