rscale 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+