rscale 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/rscale/configuration.rb +11 -0
- data/lib/rscale/errors.rb +5 -0
- data/lib/rscale/format.rb +19 -0
- data/lib/rscale/geometry.rb +24 -0
- data/lib/rscale/helpers.rb +43 -0
- data/lib/rscale/processor.rb +48 -0
- data/lib/rscale/rscale.rb +52 -0
- data/lib/rscale.rb +7 -0
- metadata +73 -0
@@ -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
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
|
+
|