snapimage 0.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/.autotest +3 -0
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/bin/snapimage_generate_config +63 -0
- data/bin/snapimage_server +55 -0
- data/lib/snapimage.rb +24 -0
- data/lib/snapimage/config.rb +51 -0
- data/lib/snapimage/exceptions.rb +25 -0
- data/lib/snapimage/image/image.rb +96 -0
- data/lib/snapimage/image/image_name_utils.rb +131 -0
- data/lib/snapimage/middleware.rb +27 -0
- data/lib/snapimage/rack/request.rb +19 -0
- data/lib/snapimage/rack/request_file.rb +26 -0
- data/lib/snapimage/rack/response.rb +51 -0
- data/lib/snapimage/server.rb +50 -0
- data/lib/snapimage/server_actions/server_actions.authorize.rb +69 -0
- data/lib/snapimage/server_actions/server_actions.delete_resource_images.rb +23 -0
- data/lib/snapimage/server_actions/server_actions.generate_image.rb +167 -0
- data/lib/snapimage/server_actions/server_actions.list_resource_images.rb +23 -0
- data/lib/snapimage/server_actions/server_actions.sync_resource.rb +78 -0
- data/lib/snapimage/storage/storage.rb +120 -0
- data/lib/snapimage/storage/storage_server.local.rb +120 -0
- data/lib/snapimage/storage/storage_server.rb +110 -0
- data/lib/snapimage/version.rb +3 -0
- data/snapimage.gemspec +27 -0
- data/spec/acceptance/delete_resource_images_spec.rb +166 -0
- data/spec/acceptance/list_resource_images_spec.rb +158 -0
- data/spec/acceptance/modify_spec.rb +165 -0
- data/spec/acceptance/sync_spec.rb +260 -0
- data/spec/acceptance/upload_spec.rb +235 -0
- data/spec/snapimage/config_spec.rb +56 -0
- data/spec/snapimage/image/image_name_utils_spec.rb +127 -0
- data/spec/snapimage/image/image_spec.rb +71 -0
- data/spec/snapimage/middleware_spec.rb +27 -0
- data/spec/snapimage/rack/request_file_spec.rb +15 -0
- data/spec/snapimage/rack/request_spec.rb +52 -0
- data/spec/snapimage/rack/response_spec.rb +33 -0
- data/spec/snapimage/server_actions/server_actions.authorize_spec.rb +67 -0
- data/spec/snapimage/server_actions/server_actions.generate_image_spec.rb +146 -0
- data/spec/snapimage/server_actions/server_actions.sync_resource_spec.rb +91 -0
- data/spec/snapimage/server_spec.rb +55 -0
- data/spec/snapimage/storage/assets/local/resource_1/12345678-1x1-0x0x1x1-1x1-1.gif +0 -0
- data/spec/snapimage/storage/assets/local/resource_1/12345678-1x1-0x0x1x1-300x200-0.jpg +0 -0
- data/spec/snapimage/storage/assets/local/resource_1/12345678-1x1.png +0 -0
- data/spec/snapimage/storage/assets/local/resource_2/12345678-1x1-0x0x1x1-1x1-1.gif +0 -0
- data/spec/snapimage/storage/assets/local/resource_2/12345678-1x1-0x0x1x1-300x200-0.jpg +0 -0
- data/spec/snapimage/storage/assets/local/resource_2/12345678-1x1.png +0 -0
- data/spec/snapimage/storage/storage_server.local_spec.rb +150 -0
- data/spec/snapimage/storage/storage_server_spec.rb +97 -0
- data/spec/snapimage/storage/storage_spec.rb +49 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/support/assets/config.json +8 -0
- data/spec/support/assets/config.yml +9 -0
- data/spec/support/assets/stub-1x1.png +0 -0
- data/spec/support/assets/stub-2048x100.png +0 -0
- data/spec/support/assets/stub-300x200.png +0 -0
- metadata +272 -0
data/.autotest
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Wesley Wong
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Snapimage
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'snapimage'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install snapimage
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "optparse"
|
4
|
+
|
5
|
+
options = {
|
6
|
+
file: "snapimage_config.yml",
|
7
|
+
force: false
|
8
|
+
}
|
9
|
+
|
10
|
+
optparse = OptionParser.new do |opts|
|
11
|
+
opts.banner = "Usage: snapimage_generate_config local_root public_url [options]"
|
12
|
+
|
13
|
+
opts.separator ""
|
14
|
+
opts.separator "local_root\tPath to the directory where the images will be stored"
|
15
|
+
opts.separator "public_url\tURL at which the images will accessible from"
|
16
|
+
|
17
|
+
opts.separator ""
|
18
|
+
opts.separator "Options:"
|
19
|
+
|
20
|
+
opts.on("-f", "--file FILE", "File to generate (Default: snapimage_config.yml)") do |file|
|
21
|
+
options[:file] = file
|
22
|
+
end
|
23
|
+
|
24
|
+
opts.on("--force", "Overwrites the file if it exists") do
|
25
|
+
options[:force] = true
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on("-h", "--help", "Display the help screen") do
|
29
|
+
puts opts
|
30
|
+
exit
|
31
|
+
end
|
32
|
+
end
|
33
|
+
optparse.parse!
|
34
|
+
|
35
|
+
unless ARGV.length == 2
|
36
|
+
puts optparse.help
|
37
|
+
exit
|
38
|
+
end
|
39
|
+
|
40
|
+
local_root = ARGV[0]
|
41
|
+
public_url = ARGV[1]
|
42
|
+
|
43
|
+
if !options[:force] && File.exists?(options[:file])
|
44
|
+
puts "File '#{options[:file]}' already exists. Use --force if you want to overwrite."
|
45
|
+
puts "Config file not generated."
|
46
|
+
puts
|
47
|
+
puts optparse.help
|
48
|
+
exit
|
49
|
+
end
|
50
|
+
|
51
|
+
File.open(options[:file], "w") do |f|
|
52
|
+
f.write(<<-EOF
|
53
|
+
primary_storage_server: "local"
|
54
|
+
storage_servers:
|
55
|
+
-
|
56
|
+
name: "local"
|
57
|
+
type: "LOCAL"
|
58
|
+
local_root: "#{local_root}"
|
59
|
+
public_url: "#{public_url}"
|
60
|
+
EOF
|
61
|
+
)
|
62
|
+
end
|
63
|
+
puts "Config file generated at '#{options[:file]}'."
|
@@ -0,0 +1,55 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "optparse"
|
4
|
+
|
5
|
+
options = {
|
6
|
+
port: 54321
|
7
|
+
}
|
8
|
+
|
9
|
+
optparse = OptionParser.new do |opts|
|
10
|
+
opts.banner = "Usage: snapimage_server config [options]"
|
11
|
+
|
12
|
+
opts.on("-p", "--port", "Set the port (Default: 54321") do |port|
|
13
|
+
options[:port] = port
|
14
|
+
end
|
15
|
+
|
16
|
+
opts.on("--path PATH", "Set the URL path of the SnapImage API Server (Default: /snapimage_api)") do |path|
|
17
|
+
options[:path] = path
|
18
|
+
end
|
19
|
+
|
20
|
+
opts.on("-h", "--help", "Display the help screen") do
|
21
|
+
puts opts
|
22
|
+
exit
|
23
|
+
end
|
24
|
+
end
|
25
|
+
optparse.parse!
|
26
|
+
|
27
|
+
unless ARGV.length == 1
|
28
|
+
puts optparse.help
|
29
|
+
exit
|
30
|
+
end
|
31
|
+
options[:config] = ARGV[0]
|
32
|
+
|
33
|
+
require "sinatra"
|
34
|
+
require "snapimage"
|
35
|
+
|
36
|
+
# Make sure the built-in web server runs.
|
37
|
+
set :run, true
|
38
|
+
|
39
|
+
# Set up Sinatra.
|
40
|
+
set :environment, :production
|
41
|
+
set :port, options[:port]
|
42
|
+
|
43
|
+
use SnapImage::Middleware, options
|
44
|
+
|
45
|
+
get "/crossdomain.xml" do
|
46
|
+
<<-XML
|
47
|
+
<?xml version="1.0"?>
|
48
|
+
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
|
49
|
+
<cross-domain-policy>
|
50
|
+
|
51
|
+
<allow-access-from domain="*" to-ports="#{options[:port]}" />
|
52
|
+
|
53
|
+
</cross-domain-policy>
|
54
|
+
XML
|
55
|
+
end
|
data/lib/snapimage.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require "rack"
|
2
|
+
require "json"
|
3
|
+
require "fileutils"
|
4
|
+
require "digest/sha1"
|
5
|
+
require "open-uri"
|
6
|
+
require "RMagick"
|
7
|
+
require "snapimage/version"
|
8
|
+
require "snapimage/exceptions"
|
9
|
+
require "snapimage/rack/request_file"
|
10
|
+
require "snapimage/rack/request"
|
11
|
+
require "snapimage/rack/response"
|
12
|
+
require "snapimage/image/image"
|
13
|
+
require "snapimage/image/image_name_utils"
|
14
|
+
require "snapimage/storage/storage_server"
|
15
|
+
require "snapimage/storage/storage_server.local"
|
16
|
+
require "snapimage/storage/storage"
|
17
|
+
require "snapimage/config"
|
18
|
+
require "snapimage/server_actions/server_actions.authorize"
|
19
|
+
require "snapimage/server_actions/server_actions.generate_image"
|
20
|
+
require "snapimage/server_actions/server_actions.sync_resource"
|
21
|
+
require "snapimage/server_actions/server_actions.delete_resource_images"
|
22
|
+
require "snapimage/server_actions/server_actions.list_resource_images"
|
23
|
+
require "snapimage/server"
|
24
|
+
require "snapimage/middleware"
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module SnapImage
|
2
|
+
class Config
|
3
|
+
# Arguments:
|
4
|
+
# * config:: Filename of the YAML or JSOn file to load or a config Hash.
|
5
|
+
#
|
6
|
+
# NOTE: All keys are strings, not symbols.
|
7
|
+
def initialize(config)
|
8
|
+
@raw_config = config
|
9
|
+
end
|
10
|
+
|
11
|
+
def validate_config
|
12
|
+
raise SnapImage::InvalidConfig, 'Missing "primary_storage_server"' unless @config["primary_storage_server"]
|
13
|
+
raise SnapImage::InvalidConfig, 'Missing "storage_servers"' unless @config["storage_servers"]
|
14
|
+
raise SnapImage::InvalidConfig, '"storage_servers" must be an array' unless @config["storage_servers"].is_a? Array
|
15
|
+
end
|
16
|
+
|
17
|
+
def set_config_defaults
|
18
|
+
@config["max_width"] ||= 1024
|
19
|
+
@config["max_height"] ||= 2048
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_config
|
23
|
+
return @config if @config
|
24
|
+
@config = @raw_config
|
25
|
+
if @raw_config.is_a? String
|
26
|
+
ext = File.extname(@raw_config)
|
27
|
+
case ext
|
28
|
+
when ".yml", ".yaml"
|
29
|
+
@config = YAML::load(File.open(@raw_config))
|
30
|
+
when ".json"
|
31
|
+
@config = JSON::parse(File.read(@raw_config))
|
32
|
+
else
|
33
|
+
raise SnapImage::UnknownFileType, "Unknown filetype. Expecting .yaml, .yml, or .json: #{@raw_config}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
raise SnapImage::UnknownConfigType, "Unknown config type. Expecting a filename or hash: #{@config}" unless @config.is_a? Hash
|
38
|
+
validate_config
|
39
|
+
set_config_defaults
|
40
|
+
@config
|
41
|
+
end
|
42
|
+
|
43
|
+
def [](key)
|
44
|
+
get_config[key]
|
45
|
+
end
|
46
|
+
|
47
|
+
def storage
|
48
|
+
@storage ||= SnapImage::Storage.new(get_config["storage_servers"], get_config["primary_storage_server"], get_config["max_width"], get_config["max_height"])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module SnapImage
|
2
|
+
# Configuration.
|
3
|
+
class InvalidConfig < StandardError; end
|
4
|
+
class InvalidStorageConfig < StandardError; end
|
5
|
+
class UnknownConfigType < StandardError; end
|
6
|
+
class MissingConfig < StandardError; end
|
7
|
+
|
8
|
+
# Authorization.
|
9
|
+
class AuthorizationRequired < StandardError; end
|
10
|
+
class AuthorizationFailed < StandardError; end
|
11
|
+
|
12
|
+
# Request.
|
13
|
+
class BadRequest < StandardError; end
|
14
|
+
class ActionNotImplemented < StandardError; end
|
15
|
+
|
16
|
+
# Files.
|
17
|
+
class UnknownFileType < StandardError; end
|
18
|
+
class FileDoesNotExist < StandardError; end
|
19
|
+
|
20
|
+
# Images.
|
21
|
+
class InvalidImageIdentifier < StandardError; end
|
22
|
+
|
23
|
+
# Resources.
|
24
|
+
class InvalidResourceIdentifier < StandardError; end
|
25
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module SnapImage
|
2
|
+
class Image
|
3
|
+
def self.from_path(path, public_url)
|
4
|
+
image = SnapImage::Image.new(public_url)
|
5
|
+
image.set_image_from_path(path)
|
6
|
+
image
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.from_blob(blob)
|
10
|
+
image = SnapImage::Image.new
|
11
|
+
image.set_image_from_blob(blob)
|
12
|
+
image
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.from_image(img)
|
16
|
+
image = SnapImage::Image.new
|
17
|
+
image.set_image_from_image(img)
|
18
|
+
image
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_accessor :public_url
|
22
|
+
|
23
|
+
# Arguments:
|
24
|
+
# * public_url:: Public URL associated with the image
|
25
|
+
def initialize(public_url = nil)
|
26
|
+
@public_url = public_url
|
27
|
+
end
|
28
|
+
|
29
|
+
# Arguments:
|
30
|
+
# * path:: Local path or URL
|
31
|
+
def set_image_from_path(path)
|
32
|
+
@image = Magick::ImageList.new(path)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Arguments:
|
36
|
+
# * blob:: Image blob
|
37
|
+
def set_image_from_blob(blob)
|
38
|
+
@image = Magick::ImageList.new
|
39
|
+
@image.from_blob(blob)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Arguments:
|
43
|
+
# * image:: RMagick Image
|
44
|
+
def set_image_from_image(image)
|
45
|
+
@image = image
|
46
|
+
end
|
47
|
+
|
48
|
+
def width
|
49
|
+
@image.columns
|
50
|
+
end
|
51
|
+
|
52
|
+
def height
|
53
|
+
@image.rows
|
54
|
+
end
|
55
|
+
|
56
|
+
def blob
|
57
|
+
@image.to_blob
|
58
|
+
end
|
59
|
+
|
60
|
+
# Crops the image with the given parameters and returns a SnapImage::Image
|
61
|
+
# object.
|
62
|
+
#
|
63
|
+
# Arguments:
|
64
|
+
# * x:: x coordinate of the top left corner
|
65
|
+
# * y:: y coordinate of the top left corner
|
66
|
+
# * width:: width of the crop rectangle
|
67
|
+
# * height:: height of the crop rectangle
|
68
|
+
def crop(x, y, width, height)
|
69
|
+
SnapImage::Image.from_image(@image.crop(x, y, width, height))
|
70
|
+
end
|
71
|
+
|
72
|
+
# Generates a new resized image and returns it as a SnapImage::Image object.
|
73
|
+
#
|
74
|
+
# Arguments:
|
75
|
+
# * width:: Width to resize to
|
76
|
+
# * height:: Height to resize to (optional)
|
77
|
+
# * maintain_aspect_ratio:: If true, the image will be resized to fit within the width/height specified while maintaining the aspect ratio. If false, the image is allowed to be stretched.
|
78
|
+
def resize(width, height = nil, maintain_aspect_ratio = true)
|
79
|
+
raise "Height must be specified when not maintaining aspect ratio." if !maintain_aspect_ratio && !height
|
80
|
+
# If no height is given, make sure it does not interfere with the
|
81
|
+
# resizing.
|
82
|
+
height ||= [width, @image.rows].max
|
83
|
+
if maintain_aspect_ratio
|
84
|
+
SnapImage::Image.from_image(@image.resize_to_fit(width, height))
|
85
|
+
else
|
86
|
+
SnapImage::Image.from_image(@image.resize(width, height))
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Generates a new sharpened image and returns it as a SnapImage::Image
|
91
|
+
# object.
|
92
|
+
def sharpen
|
93
|
+
SnapImage::Image.from_image(@image.sharpen)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module SnapImage
|
2
|
+
class ImageNameUtils
|
3
|
+
# Base Image Format:
|
4
|
+
# {basename}-{original dimensions}.{extname}
|
5
|
+
#
|
6
|
+
# Modified Image Format:
|
7
|
+
# {basename}-{original dimensions}-{crop}-{dimensions}-{sharpen}.{extname}
|
8
|
+
#
|
9
|
+
# Notes
|
10
|
+
# * crop:: {x}x{y}x{width}x{height}
|
11
|
+
# * dimensions:: {width}x{height}
|
12
|
+
# * sharpen:: 0 or 1
|
13
|
+
IMAGE_PATH_REGEXP = /^(.*\/|)(([a-z0-9]{8})-(\d+)x(\d+)(-(\d+)x(\d+)x(\d+)x(\d+)-(\d+)x(\d+)-([01])|).(png|jpg|gif))$/
|
14
|
+
|
15
|
+
class << self
|
16
|
+
# Returns true if the path is for a base image. False otherwise.
|
17
|
+
#
|
18
|
+
# The path points to a base image if the dimensions are the same as the
|
19
|
+
# original dimensions, there is no cropping, and there is no sharpening.
|
20
|
+
def base_image?(path)
|
21
|
+
get_image_name_parts(path)[:is_base]
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns the extension name of the path. Normalizes jpeg to jpg.
|
25
|
+
def get_image_type(path)
|
26
|
+
type = File.extname(path).sub(/^\./, "")
|
27
|
+
type = "jpg" if type == "jpeg"
|
28
|
+
type
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns true if the name is valid. False otherwise.
|
32
|
+
def valid?(path)
|
33
|
+
get_image_name_parts(path)
|
34
|
+
return true
|
35
|
+
rescue
|
36
|
+
return false
|
37
|
+
end
|
38
|
+
|
39
|
+
# Parses the following format:
|
40
|
+
# {basename}-{original dimensions}-{crop}-{dimensions}-{sharpen}.{extname}
|
41
|
+
# Notes about the format:
|
42
|
+
# * crop:: {x}x{y}x{width}x{height}
|
43
|
+
# * dimensions:: {width}x{height}
|
44
|
+
# * sharpen:: 0 or 1
|
45
|
+
#
|
46
|
+
# Returns the information in a hash.
|
47
|
+
def get_image_name_parts(path)
|
48
|
+
matches = path.match(SnapImage::ImageNameUtils::IMAGE_PATH_REGEXP)
|
49
|
+
raise SnapImage::InvalidImageIdentifier, "The image identifier is invalid: #{path}" unless matches
|
50
|
+
parts = {
|
51
|
+
is_base: matches[6].size == 0,
|
52
|
+
full: matches[0],
|
53
|
+
path: matches[1].sub(/\/$/, ""),
|
54
|
+
filename: matches[2],
|
55
|
+
basename: matches[3],
|
56
|
+
original_dimensions: [matches[4].to_i, matches[5].to_i],
|
57
|
+
extname: matches[14]
|
58
|
+
}
|
59
|
+
unless parts[:is_base]
|
60
|
+
parts.merge!({
|
61
|
+
crop: {
|
62
|
+
x: matches[7].to_i,
|
63
|
+
y: matches[8].to_i,
|
64
|
+
width: matches[9].to_i,
|
65
|
+
height: matches[10].to_i
|
66
|
+
},
|
67
|
+
dimensions: [matches[11].to_i, matches[12].to_i],
|
68
|
+
sharpen: !!matches[13]
|
69
|
+
})
|
70
|
+
end
|
71
|
+
parts
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns the base image name from the path.
|
75
|
+
def get_base_image_path(path)
|
76
|
+
parts = get_image_name_parts(path)
|
77
|
+
"#{parts[:path]}/#{parts[:basename]}-#{parts[:original_dimensions][0]}x#{parts[:original_dimensions][1]}.#{parts[:extname]}"
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns the name with the new width and height.
|
81
|
+
def get_resized_image_name(name, width, height)
|
82
|
+
parts = SnapImage::ImageNameUtils.get_image_name_parts(name)
|
83
|
+
if parts[:is_base]
|
84
|
+
resized_name = SnapImage::ImageNameUtils.generate_image_name(width, height, parts[:extname], basename: parts[:basename])
|
85
|
+
else
|
86
|
+
options = {
|
87
|
+
basename: parts[:basename],
|
88
|
+
crop: parts[:crop],
|
89
|
+
width: width,
|
90
|
+
height: height,
|
91
|
+
sharpen: parts[:sharpend]
|
92
|
+
}
|
93
|
+
resized_name = SnapImage::ImageNameUtils.generate_image_name(parts[:original_dimensions][0], parts[:original_dimensions][1], parts[:extname], options)
|
94
|
+
end
|
95
|
+
resized_name
|
96
|
+
end
|
97
|
+
|
98
|
+
# Generates a random alphanumeric string of 8 characters.
|
99
|
+
def generate_basename
|
100
|
+
(0...8).map { rand(36).to_s(36) }.join
|
101
|
+
end
|
102
|
+
|
103
|
+
# When no options besides :basename are given, generates a base image
|
104
|
+
# name in the format:
|
105
|
+
# {basename}-{original dimensions}.{extname}
|
106
|
+
#
|
107
|
+
# Otherwise, generates a modified image name in the format:
|
108
|
+
# {basename}-{original dimensions}-{crop}-{dimensions}-{sharpen}.{extname}
|
109
|
+
#
|
110
|
+
# Notes about the format:
|
111
|
+
# * crop:: {x}x{y}x{width}x{height}
|
112
|
+
# * dimensions:: {width}x{height}
|
113
|
+
# * sharpen:: 0 or 1
|
114
|
+
#
|
115
|
+
# Options:
|
116
|
+
# * basename:: Defaults to a randomly generated basename of 8 characters
|
117
|
+
# * crop:: Crop values {x: <int>, y: <int>, width: <int>, height: <int>}
|
118
|
+
# * width:: New width
|
119
|
+
# * height:: New height
|
120
|
+
# * sharpen:: True or false
|
121
|
+
def generate_image_name(original_width, original_height, extname, options = {})
|
122
|
+
basename = options.delete(:basename) || self.generate_basename
|
123
|
+
if options.empty?
|
124
|
+
"#{basename}-#{original_width}x#{original_height}.#{extname}"
|
125
|
+
else
|
126
|
+
"#{basename}-#{original_width}x#{original_height}-#{options[:crop][:x]}x#{options[:crop][:y]}x#{options[:crop][:width]}x#{options[:crop][:height]}-#{options[:width]}x#{options[:height]}-#{options[:sharpen] ? 1 : 0}.#{extname}"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|