kt-paperclip 6.2.0
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.
- checksums.yaml +7 -0
- data/.codeclimate.yml +17 -0
- data/.github/issue_template.md +3 -0
- data/.gitignore +19 -0
- data/.hound.yml +1050 -0
- data/.rubocop.yml +1 -0
- data/.travis.yml +47 -0
- data/Appraisals +24 -0
- data/CONTRIBUTING.md +86 -0
- data/Gemfile +18 -0
- data/LICENSE +24 -0
- data/NEWS +515 -0
- data/README.md +1053 -0
- data/RELEASING.md +17 -0
- data/Rakefile +52 -0
- data/UPGRADING +17 -0
- data/features/basic_integration.feature +85 -0
- data/features/migration.feature +29 -0
- data/features/rake_tasks.feature +62 -0
- data/features/step_definitions/attachment_steps.rb +110 -0
- data/features/step_definitions/html_steps.rb +15 -0
- data/features/step_definitions/rails_steps.rb +257 -0
- data/features/step_definitions/s3_steps.rb +14 -0
- data/features/step_definitions/web_steps.rb +106 -0
- data/features/support/env.rb +12 -0
- data/features/support/fakeweb.rb +11 -0
- data/features/support/file_helpers.rb +34 -0
- data/features/support/fixtures/boot_config.txt +15 -0
- data/features/support/fixtures/gemfile.txt +5 -0
- data/features/support/fixtures/preinitializer.txt +20 -0
- data/features/support/paths.rb +28 -0
- data/features/support/rails.rb +39 -0
- data/features/support/selectors.rb +19 -0
- data/gemfiles/4.2.gemfile +20 -0
- data/gemfiles/5.0.gemfile +20 -0
- data/gemfiles/5.1.gemfile +20 -0
- data/gemfiles/5.2.gemfile +20 -0
- data/gemfiles/6.0.gemfile +20 -0
- data/lib/generators/paperclip/USAGE +8 -0
- data/lib/generators/paperclip/paperclip_generator.rb +36 -0
- data/lib/generators/paperclip/templates/paperclip_migration.rb.erb +15 -0
- data/lib/paperclip.rb +215 -0
- data/lib/paperclip/attachment.rb +617 -0
- data/lib/paperclip/attachment_registry.rb +60 -0
- data/lib/paperclip/callbacks.rb +42 -0
- data/lib/paperclip/content_type_detector.rb +80 -0
- data/lib/paperclip/errors.rb +34 -0
- data/lib/paperclip/file_command_content_type_detector.rb +28 -0
- data/lib/paperclip/filename_cleaner.rb +15 -0
- data/lib/paperclip/geometry.rb +157 -0
- data/lib/paperclip/geometry_detector_factory.rb +45 -0
- data/lib/paperclip/geometry_parser_factory.rb +31 -0
- data/lib/paperclip/glue.rb +17 -0
- data/lib/paperclip/has_attached_file.rb +116 -0
- data/lib/paperclip/helpers.rb +60 -0
- data/lib/paperclip/interpolations.rb +201 -0
- data/lib/paperclip/interpolations/plural_cache.rb +18 -0
- data/lib/paperclip/io_adapters/abstract_adapter.rb +75 -0
- data/lib/paperclip/io_adapters/attachment_adapter.rb +47 -0
- data/lib/paperclip/io_adapters/data_uri_adapter.rb +22 -0
- data/lib/paperclip/io_adapters/empty_string_adapter.rb +19 -0
- data/lib/paperclip/io_adapters/file_adapter.rb +26 -0
- data/lib/paperclip/io_adapters/http_url_proxy_adapter.rb +16 -0
- data/lib/paperclip/io_adapters/identity_adapter.rb +17 -0
- data/lib/paperclip/io_adapters/nil_adapter.rb +37 -0
- data/lib/paperclip/io_adapters/registry.rb +36 -0
- data/lib/paperclip/io_adapters/stringio_adapter.rb +36 -0
- data/lib/paperclip/io_adapters/uploaded_file_adapter.rb +44 -0
- data/lib/paperclip/io_adapters/uri_adapter.rb +68 -0
- data/lib/paperclip/locales/en.yml +18 -0
- data/lib/paperclip/logger.rb +21 -0
- data/lib/paperclip/matchers.rb +64 -0
- data/lib/paperclip/matchers/have_attached_file_matcher.rb +54 -0
- data/lib/paperclip/matchers/validate_attachment_content_type_matcher.rb +101 -0
- data/lib/paperclip/matchers/validate_attachment_presence_matcher.rb +59 -0
- data/lib/paperclip/matchers/validate_attachment_size_matcher.rb +97 -0
- data/lib/paperclip/media_type_spoof_detector.rb +90 -0
- data/lib/paperclip/missing_attachment_styles.rb +84 -0
- data/lib/paperclip/processor.rb +56 -0
- data/lib/paperclip/processor_helpers.rb +52 -0
- data/lib/paperclip/rails_environment.rb +21 -0
- data/lib/paperclip/railtie.rb +31 -0
- data/lib/paperclip/schema.rb +81 -0
- data/lib/paperclip/storage.rb +3 -0
- data/lib/paperclip/storage/filesystem.rb +99 -0
- data/lib/paperclip/storage/fog.rb +252 -0
- data/lib/paperclip/storage/s3.rb +461 -0
- data/lib/paperclip/style.rb +106 -0
- data/lib/paperclip/tempfile.rb +42 -0
- data/lib/paperclip/tempfile_factory.rb +22 -0
- data/lib/paperclip/thumbnail.rb +131 -0
- data/lib/paperclip/url_generator.rb +76 -0
- data/lib/paperclip/validators.rb +73 -0
- data/lib/paperclip/validators/attachment_content_type_validator.rb +88 -0
- data/lib/paperclip/validators/attachment_file_name_validator.rb +75 -0
- data/lib/paperclip/validators/attachment_file_type_ignorance_validator.rb +28 -0
- data/lib/paperclip/validators/attachment_presence_validator.rb +28 -0
- data/lib/paperclip/validators/attachment_size_validator.rb +109 -0
- data/lib/paperclip/validators/media_type_spoof_detection_validator.rb +29 -0
- data/lib/paperclip/version.rb +3 -0
- data/lib/tasks/paperclip.rake +140 -0
- data/paperclip.gemspec +50 -0
- data/shoulda_macros/paperclip.rb +134 -0
- data/spec/database.yml +4 -0
- data/spec/paperclip/attachment_definitions_spec.rb +13 -0
- data/spec/paperclip/attachment_processing_spec.rb +79 -0
- data/spec/paperclip/attachment_registry_spec.rb +158 -0
- data/spec/paperclip/attachment_spec.rb +1590 -0
- data/spec/paperclip/content_type_detector_spec.rb +47 -0
- data/spec/paperclip/file_command_content_type_detector_spec.rb +40 -0
- data/spec/paperclip/filename_cleaner_spec.rb +13 -0
- data/spec/paperclip/geometry_detector_spec.rb +38 -0
- data/spec/paperclip/geometry_parser_spec.rb +73 -0
- data/spec/paperclip/geometry_spec.rb +255 -0
- data/spec/paperclip/glue_spec.rb +42 -0
- data/spec/paperclip/has_attached_file_spec.rb +78 -0
- data/spec/paperclip/integration_spec.rb +702 -0
- data/spec/paperclip/interpolations_spec.rb +270 -0
- data/spec/paperclip/io_adapters/abstract_adapter_spec.rb +160 -0
- data/spec/paperclip/io_adapters/attachment_adapter_spec.rb +140 -0
- data/spec/paperclip/io_adapters/data_uri_adapter_spec.rb +88 -0
- data/spec/paperclip/io_adapters/empty_string_adapter_spec.rb +17 -0
- data/spec/paperclip/io_adapters/file_adapter_spec.rb +131 -0
- data/spec/paperclip/io_adapters/http_url_proxy_adapter_spec.rb +137 -0
- data/spec/paperclip/io_adapters/identity_adapter_spec.rb +8 -0
- data/spec/paperclip/io_adapters/nil_adapter_spec.rb +25 -0
- data/spec/paperclip/io_adapters/registry_spec.rb +35 -0
- data/spec/paperclip/io_adapters/stringio_adapter_spec.rb +64 -0
- data/spec/paperclip/io_adapters/uploaded_file_adapter_spec.rb +146 -0
- data/spec/paperclip/io_adapters/uri_adapter_spec.rb +221 -0
- data/spec/paperclip/matchers/have_attached_file_matcher_spec.rb +19 -0
- data/spec/paperclip/matchers/validate_attachment_content_type_matcher_spec.rb +108 -0
- data/spec/paperclip/matchers/validate_attachment_presence_matcher_spec.rb +69 -0
- data/spec/paperclip/matchers/validate_attachment_size_matcher_spec.rb +88 -0
- data/spec/paperclip/media_type_spoof_detector_spec.rb +120 -0
- data/spec/paperclip/meta_class_spec.rb +30 -0
- data/spec/paperclip/paperclip_missing_attachment_styles_spec.rb +88 -0
- data/spec/paperclip/paperclip_spec.rb +196 -0
- data/spec/paperclip/plural_cache_spec.rb +37 -0
- data/spec/paperclip/processor_helpers_spec.rb +57 -0
- data/spec/paperclip/processor_spec.rb +26 -0
- data/spec/paperclip/rails_environment_spec.rb +30 -0
- data/spec/paperclip/rake_spec.rb +103 -0
- data/spec/paperclip/schema_spec.rb +252 -0
- data/spec/paperclip/storage/filesystem_spec.rb +79 -0
- data/spec/paperclip/storage/fog_spec.rb +560 -0
- data/spec/paperclip/storage/s3_live_spec.rb +188 -0
- data/spec/paperclip/storage/s3_spec.rb +1695 -0
- data/spec/paperclip/style_spec.rb +251 -0
- data/spec/paperclip/tempfile_factory_spec.rb +33 -0
- data/spec/paperclip/tempfile_spec.rb +35 -0
- data/spec/paperclip/thumbnail_spec.rb +504 -0
- data/spec/paperclip/url_generator_spec.rb +221 -0
- data/spec/paperclip/validators/attachment_content_type_validator_spec.rb +322 -0
- data/spec/paperclip/validators/attachment_file_name_validator_spec.rb +159 -0
- data/spec/paperclip/validators/attachment_presence_validator_spec.rb +85 -0
- data/spec/paperclip/validators/attachment_size_validator_spec.rb +235 -0
- data/spec/paperclip/validators/media_type_spoof_detection_validator_spec.rb +48 -0
- data/spec/paperclip/validators_spec.rb +164 -0
- data/spec/spec_helper.rb +45 -0
- data/spec/support/assertions.rb +84 -0
- data/spec/support/fake_model.rb +24 -0
- data/spec/support/fake_rails.rb +12 -0
- data/spec/support/fixtures/12k.png +0 -0
- data/spec/support/fixtures/50x50.png +0 -0
- data/spec/support/fixtures/5k.png +0 -0
- data/spec/support/fixtures/animated +0 -0
- data/spec/support/fixtures/animated.gif +0 -0
- data/spec/support/fixtures/animated.unknown +0 -0
- data/spec/support/fixtures/bad.png +1 -0
- data/spec/support/fixtures/empty.html +1 -0
- data/spec/support/fixtures/empty.xlsx +0 -0
- data/spec/support/fixtures/fog.yml +8 -0
- data/spec/support/fixtures/rotated.jpg +0 -0
- data/spec/support/fixtures/s3.yml +8 -0
- data/spec/support/fixtures/spaced file.jpg +0 -0
- data/spec/support/fixtures/spaced file.png +0 -0
- data/spec/support/fixtures/text.txt +1 -0
- data/spec/support/fixtures/twopage.pdf +0 -0
- data/spec/support/fixtures/uppercase.PNG +0 -0
- data/spec/support/matchers/accept.rb +5 -0
- data/spec/support/matchers/exist.rb +5 -0
- data/spec/support/matchers/have_column.rb +23 -0
- data/spec/support/mock_attachment.rb +24 -0
- data/spec/support/mock_interpolator.rb +24 -0
- data/spec/support/mock_url_generator_builder.rb +26 -0
- data/spec/support/model_reconstruction.rb +72 -0
- data/spec/support/reporting.rb +11 -0
- data/spec/support/test_data.rb +13 -0
- data/spec/support/version_helper.rb +9 -0
- metadata +586 -0
@@ -0,0 +1,60 @@
|
|
1
|
+
require "singleton"
|
2
|
+
|
3
|
+
module Paperclip
|
4
|
+
class AttachmentRegistry
|
5
|
+
include Singleton
|
6
|
+
|
7
|
+
def self.register(klass, attachment_name, attachment_options)
|
8
|
+
instance.register(klass, attachment_name, attachment_options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.clear
|
12
|
+
instance.clear
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.names_for(klass)
|
16
|
+
instance.names_for(klass)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.each_definition(&block)
|
20
|
+
instance.each_definition(&block)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.definitions_for(klass)
|
24
|
+
instance.definitions_for(klass)
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
clear
|
29
|
+
end
|
30
|
+
|
31
|
+
def register(klass, attachment_name, attachment_options)
|
32
|
+
@attachments ||= {}
|
33
|
+
@attachments[klass] ||= {}
|
34
|
+
@attachments[klass][attachment_name] = attachment_options
|
35
|
+
end
|
36
|
+
|
37
|
+
def clear
|
38
|
+
@attachments = Hash.new { |h, k| h[k] = {} }
|
39
|
+
end
|
40
|
+
|
41
|
+
def names_for(klass)
|
42
|
+
@attachments[klass].keys
|
43
|
+
end
|
44
|
+
|
45
|
+
def each_definition
|
46
|
+
@attachments.each do |klass, attachments|
|
47
|
+
attachments.each do |name, options|
|
48
|
+
yield klass, name, options
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def definitions_for(klass)
|
54
|
+
parent_classes = klass.ancestors.reverse
|
55
|
+
parent_classes.each_with_object({}) do |ancestor, inherited_definitions|
|
56
|
+
inherited_definitions.deep_merge! @attachments[ancestor]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Paperclip
|
2
|
+
module Callbacks
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(Defining)
|
5
|
+
base.send(:include, Running)
|
6
|
+
end
|
7
|
+
|
8
|
+
module Defining
|
9
|
+
def define_paperclip_callbacks(*callbacks)
|
10
|
+
define_callbacks(*[callbacks, { terminator: hasta_la_vista_baby }].flatten)
|
11
|
+
callbacks.each do |callback|
|
12
|
+
eval <<-end_callbacks
|
13
|
+
def before_#{callback}(*args, &blk)
|
14
|
+
set_callback(:#{callback}, :before, *args, &blk)
|
15
|
+
end
|
16
|
+
def after_#{callback}(*args, &blk)
|
17
|
+
set_callback(:#{callback}, :after, *args, &blk)
|
18
|
+
end
|
19
|
+
end_callbacks
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def hasta_la_vista_baby
|
26
|
+
lambda do |_, result|
|
27
|
+
if result.respond_to?(:call)
|
28
|
+
result.call == false
|
29
|
+
else
|
30
|
+
result == false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module Running
|
37
|
+
def run_paperclip_callbacks(callback, &block)
|
38
|
+
run_callbacks(callback, &block)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Paperclip
|
2
|
+
class ContentTypeDetector
|
3
|
+
# The content-type detection strategy is as follows:
|
4
|
+
#
|
5
|
+
# 1. Blank/Empty files: If there's no filepath or the file is empty,
|
6
|
+
# provide a sensible default (application/octet-stream or inode/x-empty)
|
7
|
+
#
|
8
|
+
# 2. Calculated match: Return the first result that is found by both the
|
9
|
+
# `file` command and MIME::Types.
|
10
|
+
#
|
11
|
+
# 3. Standard types: Return the first standard (without an x- prefix) entry
|
12
|
+
# in MIME::Types
|
13
|
+
#
|
14
|
+
# 4. Experimental types: If there were no standard types in MIME::Types
|
15
|
+
# list, try to return the first experimental one
|
16
|
+
#
|
17
|
+
# 5. Raw `file` command: Just use the output of the `file` command raw, or
|
18
|
+
# a sensible default. This is cached from Step 2.
|
19
|
+
|
20
|
+
EMPTY_TYPE = "inode/x-empty"
|
21
|
+
SENSIBLE_DEFAULT = "application/octet-stream"
|
22
|
+
|
23
|
+
def initialize(filepath)
|
24
|
+
@filepath = filepath
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns a String describing the file's content type
|
28
|
+
def detect
|
29
|
+
if blank_name?
|
30
|
+
SENSIBLE_DEFAULT
|
31
|
+
elsif empty_file?
|
32
|
+
EMPTY_TYPE
|
33
|
+
elsif calculated_type_matches.any?
|
34
|
+
calculated_type_matches.first
|
35
|
+
else
|
36
|
+
type_from_file_contents || SENSIBLE_DEFAULT
|
37
|
+
end.to_s
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def blank_name?
|
43
|
+
@filepath.nil? || @filepath.empty?
|
44
|
+
end
|
45
|
+
|
46
|
+
def empty_file?
|
47
|
+
File.exist?(@filepath) && File.size(@filepath) == 0
|
48
|
+
end
|
49
|
+
|
50
|
+
alias :empty? :empty_file?
|
51
|
+
|
52
|
+
def calculated_type_matches
|
53
|
+
possible_types.select do |content_type|
|
54
|
+
content_type == type_from_file_contents
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def possible_types
|
59
|
+
MIME::Types.type_for(@filepath).collect(&:content_type)
|
60
|
+
end
|
61
|
+
|
62
|
+
def type_from_file_contents
|
63
|
+
type_from_mime_magic || type_from_file_command
|
64
|
+
rescue Errno::ENOENT => e
|
65
|
+
Paperclip.log("Error while determining content type: #{e}")
|
66
|
+
SENSIBLE_DEFAULT
|
67
|
+
end
|
68
|
+
|
69
|
+
def type_from_mime_magic
|
70
|
+
@type_from_mime_magic ||= File.open(@filepath) do |file|
|
71
|
+
MimeMagic.by_magic(file).try(:type)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def type_from_file_command
|
76
|
+
@type_from_file_command ||=
|
77
|
+
FileCommandContentTypeDetector.new(@filepath).detect
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Paperclip
|
2
|
+
# A base error class for Paperclip. Most of the error that will be thrown
|
3
|
+
# from Paperclip will inherits from this class.
|
4
|
+
class Error < StandardError
|
5
|
+
end
|
6
|
+
|
7
|
+
module Errors
|
8
|
+
# Will be thrown when a storage method is not found.
|
9
|
+
class StorageMethodNotFound < Paperclip::Error
|
10
|
+
end
|
11
|
+
|
12
|
+
# Will be thrown when a command or executable is not found.
|
13
|
+
class CommandNotFoundError < Paperclip::Error
|
14
|
+
end
|
15
|
+
|
16
|
+
# Attachments require a content_type or file_name validator,
|
17
|
+
# or to have explicitly opted out of them.
|
18
|
+
class MissingRequiredValidatorError < Paperclip::Error
|
19
|
+
end
|
20
|
+
|
21
|
+
# Will be thrown when ImageMagic cannot determine the uploaded file's
|
22
|
+
# metadata, usually this would mean the file is not an image. If you are
|
23
|
+
# consistently receiving this error on PDFs make sure that you have
|
24
|
+
# installed Ghostscript.
|
25
|
+
class NotIdentifiedByImageMagickError < Paperclip::Error
|
26
|
+
end
|
27
|
+
|
28
|
+
# Will be thrown if the interpolation is creating an infinite loop. If you
|
29
|
+
# are creating an interpolator which might cause an infinite loop, you
|
30
|
+
# should be throwing this error upon the infinite loop as well.
|
31
|
+
class InfiniteInterpolationError < Paperclip::Error
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Paperclip
|
2
|
+
class FileCommandContentTypeDetector
|
3
|
+
SENSIBLE_DEFAULT = "application/octet-stream"
|
4
|
+
|
5
|
+
def initialize(filename)
|
6
|
+
@filename = filename
|
7
|
+
end
|
8
|
+
|
9
|
+
def detect
|
10
|
+
type_from_file_command
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def type_from_file_command
|
16
|
+
# On BSDs, `file` doesn't give a result code of 1 if the file doesn't exist.
|
17
|
+
type = begin
|
18
|
+
Paperclip.run("file", "-b --mime :file", file: @filename)
|
19
|
+
rescue Terrapin::CommandLineError => e
|
20
|
+
Paperclip.log("Error while determining content type: #{e}")
|
21
|
+
SENSIBLE_DEFAULT
|
22
|
+
end
|
23
|
+
|
24
|
+
type = SENSIBLE_DEFAULT if type.nil? || type.match(/\(.*?\)/)
|
25
|
+
type.split(/[:;\s]+/)[0]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Paperclip
|
2
|
+
class FilenameCleaner
|
3
|
+
def initialize(invalid_character_regex)
|
4
|
+
@invalid_character_regex = invalid_character_regex
|
5
|
+
end
|
6
|
+
|
7
|
+
def call(filename)
|
8
|
+
if @invalid_character_regex
|
9
|
+
filename.gsub(@invalid_character_regex, "_")
|
10
|
+
else
|
11
|
+
filename
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
module Paperclip
|
2
|
+
# Defines the geometry of an image.
|
3
|
+
class Geometry
|
4
|
+
attr_accessor :height, :width, :modifier
|
5
|
+
|
6
|
+
EXIF_ROTATED_ORIENTATION_VALUES = [5, 6, 7, 8].freeze
|
7
|
+
|
8
|
+
# Gives a Geometry representing the given height and width
|
9
|
+
def initialize(width = nil, height = nil, modifier = nil)
|
10
|
+
if width.is_a?(Hash)
|
11
|
+
options = width
|
12
|
+
@height = options[:height].to_f
|
13
|
+
@width = options[:width].to_f
|
14
|
+
@modifier = options[:modifier]
|
15
|
+
@orientation = options[:orientation].to_i
|
16
|
+
else
|
17
|
+
@height = height.to_f
|
18
|
+
@width = width.to_f
|
19
|
+
@modifier = modifier
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Extracts the Geometry from a file (or path to a file)
|
24
|
+
def self.from_file(file)
|
25
|
+
GeometryDetector.new(file).make
|
26
|
+
end
|
27
|
+
|
28
|
+
# Extracts the Geometry from a "WxH,O" string
|
29
|
+
# Where W is the width, H is the height,
|
30
|
+
# and O is the EXIF orientation
|
31
|
+
def self.parse(string)
|
32
|
+
GeometryParser.new(string).make
|
33
|
+
end
|
34
|
+
|
35
|
+
# Swaps the height and width if necessary
|
36
|
+
def auto_orient
|
37
|
+
if EXIF_ROTATED_ORIENTATION_VALUES.include?(@orientation)
|
38
|
+
@height, @width = @width, @height
|
39
|
+
@orientation -= 4
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# True if the dimensions represent a square
|
44
|
+
def square?
|
45
|
+
height == width
|
46
|
+
end
|
47
|
+
|
48
|
+
# True if the dimensions represent a horizontal rectangle
|
49
|
+
def horizontal?
|
50
|
+
height < width
|
51
|
+
end
|
52
|
+
|
53
|
+
# True if the dimensions represent a vertical rectangle
|
54
|
+
def vertical?
|
55
|
+
height > width
|
56
|
+
end
|
57
|
+
|
58
|
+
# The aspect ratio of the dimensions.
|
59
|
+
def aspect
|
60
|
+
width / height
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns the larger of the two dimensions
|
64
|
+
def larger
|
65
|
+
[height, width].max
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns the smaller of the two dimensions
|
69
|
+
def smaller
|
70
|
+
[height, width].min
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns the width and height in a format suitable to be passed to Geometry.parse
|
74
|
+
def to_s
|
75
|
+
s = ""
|
76
|
+
s << width.to_i.to_s if width > 0
|
77
|
+
s << "x#{height.to_i}" if height > 0
|
78
|
+
s << modifier.to_s
|
79
|
+
s
|
80
|
+
end
|
81
|
+
|
82
|
+
# Same as to_s
|
83
|
+
def inspect
|
84
|
+
to_s
|
85
|
+
end
|
86
|
+
|
87
|
+
# Returns the scaling and cropping geometries (in string-based ImageMagick format)
|
88
|
+
# neccessary to transform this Geometry into the Geometry given. If crop is true,
|
89
|
+
# then it is assumed the destination Geometry will be the exact final resolution.
|
90
|
+
# In this case, the source Geometry is scaled so that an image containing the
|
91
|
+
# destination Geometry would be completely filled by the source image, and any
|
92
|
+
# overhanging image would be cropped. Useful for square thumbnail images. The cropping
|
93
|
+
# is weighted at the center of the Geometry.
|
94
|
+
def transformation_to(dst, crop = false)
|
95
|
+
if crop
|
96
|
+
ratio = Geometry.new(dst.width / width, dst.height / height)
|
97
|
+
scale_geometry, scale = scaling(dst, ratio)
|
98
|
+
crop_geometry = cropping(dst, ratio, scale)
|
99
|
+
else
|
100
|
+
scale_geometry = dst.to_s
|
101
|
+
end
|
102
|
+
|
103
|
+
[scale_geometry, crop_geometry]
|
104
|
+
end
|
105
|
+
|
106
|
+
# resize to a new geometry
|
107
|
+
# @param geometry [String] the Paperclip geometry definition to resize to
|
108
|
+
# @example
|
109
|
+
# Paperclip::Geometry.new(150, 150).resize_to('50x50!')
|
110
|
+
# #=> Paperclip::Geometry(50, 50)
|
111
|
+
def resize_to(geometry)
|
112
|
+
new_geometry = Paperclip::Geometry.parse geometry
|
113
|
+
case new_geometry.modifier
|
114
|
+
when "!", "#"
|
115
|
+
new_geometry
|
116
|
+
when ">"
|
117
|
+
if new_geometry.width >= width && new_geometry.height >= height
|
118
|
+
self
|
119
|
+
else
|
120
|
+
scale_to new_geometry
|
121
|
+
end
|
122
|
+
when "<"
|
123
|
+
if new_geometry.width <= width || new_geometry.height <= height
|
124
|
+
self
|
125
|
+
else
|
126
|
+
scale_to new_geometry
|
127
|
+
end
|
128
|
+
else
|
129
|
+
scale_to new_geometry
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def scaling(dst, ratio)
|
136
|
+
if ratio.horizontal? || ratio.square?
|
137
|
+
["%dx" % dst.width, ratio.width]
|
138
|
+
else
|
139
|
+
["x%d" % dst.height, ratio.height]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def cropping(dst, ratio, scale)
|
144
|
+
if ratio.horizontal? || ratio.square?
|
145
|
+
"%dx%d+%d+%d" % [dst.width, dst.height, 0, (height * scale - dst.height) / 2]
|
146
|
+
else
|
147
|
+
"%dx%d+%d+%d" % [dst.width, dst.height, (width * scale - dst.width) / 2, 0]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# scale to the requested geometry and preserve the aspect ratio
|
152
|
+
def scale_to(new_geometry)
|
153
|
+
scale = [new_geometry.width.to_f / width.to_f, new_geometry.height.to_f / height.to_f].min
|
154
|
+
Paperclip::Geometry.new((width * scale).round, (height * scale).round)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Paperclip
|
2
|
+
class GeometryDetector
|
3
|
+
def initialize(file)
|
4
|
+
@file = file
|
5
|
+
raise_if_blank_file
|
6
|
+
end
|
7
|
+
|
8
|
+
def make
|
9
|
+
geometry = GeometryParser.new(geometry_string.strip).make
|
10
|
+
geometry || raise(Errors::NotIdentifiedByImageMagickError.new)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def geometry_string
|
16
|
+
orientation = Paperclip.options[:use_exif_orientation] ?
|
17
|
+
"%[exif:orientation]" : "1"
|
18
|
+
Paperclip.run(
|
19
|
+
Paperclip.options[:is_windows] ? "magick identify" : "identify",
|
20
|
+
"-format '%wx%h,#{orientation}' :file", {
|
21
|
+
file: "#{path}[0]"
|
22
|
+
},
|
23
|
+
swallow_stderr: true
|
24
|
+
)
|
25
|
+
rescue Terrapin::ExitStatusError
|
26
|
+
""
|
27
|
+
rescue Terrapin::CommandNotFoundError => e
|
28
|
+
raise_because_imagemagick_missing
|
29
|
+
end
|
30
|
+
|
31
|
+
def path
|
32
|
+
@file.respond_to?(:path) ? @file.path : @file
|
33
|
+
end
|
34
|
+
|
35
|
+
def raise_if_blank_file
|
36
|
+
if path.blank?
|
37
|
+
raise Errors::NotIdentifiedByImageMagickError.new("Cannot find the geometry of a file with a blank name")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def raise_because_imagemagick_missing
|
42
|
+
raise Errors::CommandNotFoundError.new("Could not run the `identify` command. Please install ImageMagick.")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|