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.
- 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
|
+
|