oahu-dragonfly 0.8.2
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/.rspec +1 -0
- data/.yardopts +24 -0
- data/Gemfile +30 -0
- data/History.md +323 -0
- data/LICENSE +20 -0
- data/README.md +88 -0
- data/Rakefile +50 -0
- data/VERSION +1 -0
- data/config.ru +14 -0
- data/docs.watchr +1 -0
- data/dragonfly.gemspec +297 -0
- data/extra_docs/Analysers.md +66 -0
- data/extra_docs/Caching.md +23 -0
- data/extra_docs/Configuration.md +124 -0
- data/extra_docs/Couch.md +49 -0
- data/extra_docs/DataStorage.md +153 -0
- data/extra_docs/Encoding.md +67 -0
- data/extra_docs/GeneralUsage.md +121 -0
- data/extra_docs/Generators.md +60 -0
- data/extra_docs/Heroku.md +50 -0
- data/extra_docs/ImageMagick.md +125 -0
- data/extra_docs/Index.md +33 -0
- data/extra_docs/MimeTypes.md +40 -0
- data/extra_docs/Models.md +272 -0
- data/extra_docs/Mongo.md +45 -0
- data/extra_docs/Processing.md +77 -0
- data/extra_docs/Rack.md +52 -0
- data/extra_docs/Rails2.md +57 -0
- data/extra_docs/Rails3.md +62 -0
- data/extra_docs/Sinatra.md +25 -0
- data/extra_docs/URLs.md +169 -0
- data/features/images.feature +47 -0
- data/features/no_processing.feature +14 -0
- data/features/rails_3.0.5.feature +8 -0
- data/features/steps/common_steps.rb +8 -0
- data/features/steps/dragonfly_steps.rb +66 -0
- data/features/steps/rails_steps.rb +28 -0
- data/features/support/env.rb +13 -0
- data/features/support/setup.rb +32 -0
- data/fixtures/rails_3.0.5/files/app/models/album.rb +7 -0
- data/fixtures/rails_3.0.5/files/app/views/albums/new.html.erb +7 -0
- data/fixtures/rails_3.0.5/files/app/views/albums/show.html.erb +6 -0
- data/fixtures/rails_3.0.5/files/config/initializers/dragonfly.rb +4 -0
- data/fixtures/rails_3.0.5/files/features/manage_album_images.feature +38 -0
- data/fixtures/rails_3.0.5/files/features/step_definitions/helper_steps.rb +7 -0
- data/fixtures/rails_3.0.5/files/features/step_definitions/image_steps.rb +25 -0
- data/fixtures/rails_3.0.5/files/features/support/paths.rb +17 -0
- data/fixtures/rails_3.0.5/files/features/text_images.feature +7 -0
- data/fixtures/rails_3.0.5/template.rb +20 -0
- data/irbrc.rb +18 -0
- data/lib/dragonfly.rb +55 -0
- data/lib/dragonfly/active_model_extensions.rb +13 -0
- data/lib/dragonfly/active_model_extensions/attachment.rb +250 -0
- data/lib/dragonfly/active_model_extensions/attachment_class_methods.rb +148 -0
- data/lib/dragonfly/active_model_extensions/class_methods.rb +95 -0
- data/lib/dragonfly/active_model_extensions/instance_methods.rb +28 -0
- data/lib/dragonfly/active_model_extensions/validations.rb +41 -0
- data/lib/dragonfly/analyser.rb +58 -0
- data/lib/dragonfly/analysis/file_command_analyser.rb +32 -0
- data/lib/dragonfly/analysis/image_magick_analyser.rb +6 -0
- data/lib/dragonfly/app.rb +172 -0
- data/lib/dragonfly/config/heroku.rb +19 -0
- data/lib/dragonfly/config/image_magick.rb +6 -0
- data/lib/dragonfly/config/rails.rb +20 -0
- data/lib/dragonfly/configurable.rb +207 -0
- data/lib/dragonfly/core_ext/array.rb +7 -0
- data/lib/dragonfly/core_ext/hash.rb +7 -0
- data/lib/dragonfly/core_ext/object.rb +12 -0
- data/lib/dragonfly/core_ext/string.rb +9 -0
- data/lib/dragonfly/core_ext/symbol.rb +9 -0
- data/lib/dragonfly/data_storage.rb +9 -0
- data/lib/dragonfly/data_storage/couch_data_store.rb +64 -0
- data/lib/dragonfly/data_storage/file_data_store.rb +141 -0
- data/lib/dragonfly/data_storage/mongo_data_store.rb +86 -0
- data/lib/dragonfly/data_storage/s3data_store.rb +145 -0
- data/lib/dragonfly/encoder.rb +13 -0
- data/lib/dragonfly/encoding/image_magick_encoder.rb +6 -0
- data/lib/dragonfly/function_manager.rb +71 -0
- data/lib/dragonfly/generation/image_magick_generator.rb +6 -0
- data/lib/dragonfly/generator.rb +9 -0
- data/lib/dragonfly/hash_with_css_style_keys.rb +21 -0
- data/lib/dragonfly/image_magick/analyser.rb +51 -0
- data/lib/dragonfly/image_magick/config.rb +41 -0
- data/lib/dragonfly/image_magick/encoder.rb +57 -0
- data/lib/dragonfly/image_magick/generator.rb +145 -0
- data/lib/dragonfly/image_magick/processor.rb +99 -0
- data/lib/dragonfly/image_magick/utils.rb +72 -0
- data/lib/dragonfly/image_magick_utils.rb +4 -0
- data/lib/dragonfly/job.rb +451 -0
- data/lib/dragonfly/job_builder.rb +39 -0
- data/lib/dragonfly/job_definitions.rb +26 -0
- data/lib/dragonfly/job_endpoint.rb +15 -0
- data/lib/dragonfly/loggable.rb +28 -0
- data/lib/dragonfly/middleware.rb +20 -0
- data/lib/dragonfly/processing/image_magick_processor.rb +6 -0
- data/lib/dragonfly/processor.rb +9 -0
- data/lib/dragonfly/rails/images.rb +27 -0
- data/lib/dragonfly/response.rb +97 -0
- data/lib/dragonfly/routed_endpoint.rb +40 -0
- data/lib/dragonfly/serializer.rb +32 -0
- data/lib/dragonfly/server.rb +113 -0
- data/lib/dragonfly/simple_cache.rb +23 -0
- data/lib/dragonfly/temp_object.rb +175 -0
- data/lib/dragonfly/url_mapper.rb +78 -0
- data/samples/beach.png +0 -0
- data/samples/egg.png +0 -0
- data/samples/round.gif +0 -0
- data/samples/sample.docx +0 -0
- data/samples/taj.jpg +0 -0
- data/spec/dragonfly/active_model_extensions/model_spec.rb +1426 -0
- data/spec/dragonfly/active_model_extensions/spec_helper.rb +91 -0
- data/spec/dragonfly/analyser_spec.rb +123 -0
- data/spec/dragonfly/analysis/file_command_analyser_spec.rb +48 -0
- data/spec/dragonfly/app_spec.rb +135 -0
- data/spec/dragonfly/configurable_spec.rb +461 -0
- data/spec/dragonfly/core_ext/array_spec.rb +19 -0
- data/spec/dragonfly/core_ext/hash_spec.rb +19 -0
- data/spec/dragonfly/core_ext/string_spec.rb +17 -0
- data/spec/dragonfly/core_ext/symbol_spec.rb +17 -0
- data/spec/dragonfly/data_storage/couch_data_store_spec.rb +76 -0
- data/spec/dragonfly/data_storage/file_data_store_spec.rb +296 -0
- data/spec/dragonfly/data_storage/mongo_data_store_spec.rb +57 -0
- data/spec/dragonfly/data_storage/s3_data_store_spec.rb +258 -0
- data/spec/dragonfly/data_storage/shared_data_store_examples.rb +77 -0
- data/spec/dragonfly/function_manager_spec.rb +154 -0
- data/spec/dragonfly/hash_with_css_style_keys_spec.rb +24 -0
- data/spec/dragonfly/image_magick/analyser_spec.rb +64 -0
- data/spec/dragonfly/image_magick/encoder_spec.rb +41 -0
- data/spec/dragonfly/image_magick/generator_spec.rb +172 -0
- data/spec/dragonfly/image_magick/processor_spec.rb +233 -0
- data/spec/dragonfly/image_magick/utils_spec.rb +18 -0
- data/spec/dragonfly/job_builder_spec.rb +37 -0
- data/spec/dragonfly/job_definitions_spec.rb +35 -0
- data/spec/dragonfly/job_endpoint_spec.rb +173 -0
- data/spec/dragonfly/job_spec.rb +1046 -0
- data/spec/dragonfly/loggable_spec.rb +80 -0
- data/spec/dragonfly/middleware_spec.rb +47 -0
- data/spec/dragonfly/routed_endpoint_spec.rb +48 -0
- data/spec/dragonfly/serializer_spec.rb +61 -0
- data/spec/dragonfly/server_spec.rb +278 -0
- data/spec/dragonfly/simple_cache_spec.rb +27 -0
- data/spec/dragonfly/temp_object_spec.rb +306 -0
- data/spec/dragonfly/url_mapper_spec.rb +126 -0
- data/spec/functional/deprecations_spec.rb +51 -0
- data/spec/functional/image_magick_app_spec.rb +27 -0
- data/spec/functional/model_urls_spec.rb +85 -0
- data/spec/functional/remote_on_the_fly_spec.rb +51 -0
- data/spec/functional/to_response_spec.rb +31 -0
- data/spec/spec_helper.rb +51 -0
- data/spec/support/argument_matchers.rb +19 -0
- data/spec/support/image_matchers.rb +47 -0
- data/spec/support/simple_matchers.rb +53 -0
- data/yard/handlers/configurable_attr_handler.rb +38 -0
- data/yard/setup.rb +15 -0
- data/yard/templates/default/fulldoc/html/css/common.css +107 -0
- data/yard/templates/default/layout/html/layout.erb +89 -0
- data/yard/templates/default/module/html/configuration_summary.erb +31 -0
- data/yard/templates/default/module/setup.rb +17 -0
- metadata +544 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module Dragonfly
|
|
2
|
+
class FunctionManager
|
|
3
|
+
|
|
4
|
+
# Exceptions
|
|
5
|
+
class NotDefined < NoMethodError; end
|
|
6
|
+
class UnableToHandle < NotImplementedError; end
|
|
7
|
+
|
|
8
|
+
include Loggable
|
|
9
|
+
include Configurable
|
|
10
|
+
|
|
11
|
+
def initialize
|
|
12
|
+
@functions = {}
|
|
13
|
+
@objects = []
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def add(name, callable_obj=nil, &block)
|
|
17
|
+
functions[name] ||= []
|
|
18
|
+
functions[name] << (callable_obj || block)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
attr_reader :functions, :objects
|
|
22
|
+
|
|
23
|
+
def register(klass, *args, &block)
|
|
24
|
+
obj = klass.new(*args)
|
|
25
|
+
obj.configure(&block) if block
|
|
26
|
+
obj.use_same_log_as(self) if obj.is_a?(Loggable)
|
|
27
|
+
obj.use_as_fallback_config(self) if obj.is_a?(Configurable)
|
|
28
|
+
methods_to_add(obj).each do |meth|
|
|
29
|
+
add meth.to_sym, obj.method(meth)
|
|
30
|
+
end
|
|
31
|
+
objects << obj
|
|
32
|
+
obj
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def call_last(meth, *args)
|
|
36
|
+
if functions[meth.to_sym]
|
|
37
|
+
functions[meth.to_sym].reverse.each do |function|
|
|
38
|
+
catch :unable_to_handle do
|
|
39
|
+
return function.call(*args)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
# If the code gets here, then none of the registered functions were able to handle the method call
|
|
43
|
+
raise UnableToHandle, "None of the functions registered with #{self} were able to deal with the method call " +
|
|
44
|
+
"#{meth}(#{args.map{|a| a.inspect[0..100]}.join(',')}). You may need to register one that can."
|
|
45
|
+
else
|
|
46
|
+
raise NotDefined, "function #{meth} not registered with #{self}"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def get_registered(klass)
|
|
51
|
+
objects.reverse.detect{|o| o.instance_of?(klass) }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def inspect
|
|
55
|
+
to_s.sub(/>$/, " with functions: #{functions.keys.map{|k| k.to_s }.sort.join(', ')} >")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def methods_to_add(obj)
|
|
61
|
+
if obj.is_a?(Configurable)
|
|
62
|
+
obj.public_methods(false) -
|
|
63
|
+
obj.config_methods.map{|meth| meth.to_method_name} -
|
|
64
|
+
[:configured_class.to_method_name] # Hacky - there must be a better way...
|
|
65
|
+
else
|
|
66
|
+
obj.public_methods(false)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Dragonfly
|
|
2
|
+
|
|
3
|
+
# HashWithCssStyleKeys is solely for being able to access a hash
|
|
4
|
+
# which has css-style keys (e.g. 'font-size') with the underscore
|
|
5
|
+
# symbol version
|
|
6
|
+
# @example
|
|
7
|
+
# opts = {'font-size' => '23px', :color => 'white'}
|
|
8
|
+
# opts = HashWithCssStyleKeys[opts]
|
|
9
|
+
# opts[:font_size] # ===> '23px'
|
|
10
|
+
# opts[:color] # ===> 'white'
|
|
11
|
+
class HashWithCssStyleKeys < Hash
|
|
12
|
+
def [](key)
|
|
13
|
+
super || (
|
|
14
|
+
str_key = key.to_s
|
|
15
|
+
css_key = str_key.gsub('_','-')
|
|
16
|
+
super(str_key) || super(css_key) || super(css_key.to_sym)
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module Dragonfly
|
|
2
|
+
module ImageMagick
|
|
3
|
+
class Analyser
|
|
4
|
+
|
|
5
|
+
include Utils
|
|
6
|
+
include Configurable
|
|
7
|
+
|
|
8
|
+
def width(temp_object)
|
|
9
|
+
identify(temp_object)[:width]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def height(temp_object)
|
|
13
|
+
identify(temp_object)[:height]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def aspect_ratio(temp_object)
|
|
17
|
+
attrs = identify(temp_object)
|
|
18
|
+
attrs[:width].to_f / attrs[:height]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def portrait?(temp_object)
|
|
22
|
+
attrs = identify(temp_object)
|
|
23
|
+
attrs[:width] <= attrs[:height]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def landscape?(temp_object)
|
|
27
|
+
attrs = identify(temp_object)
|
|
28
|
+
attrs[:width] >= attrs[:height]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def depth(temp_object)
|
|
32
|
+
identify(temp_object)[:depth]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def number_of_colours(temp_object)
|
|
36
|
+
details = raw_identify(temp_object, '-verbose -unique')
|
|
37
|
+
details[/Colors: (\d+)/, 1].to_i
|
|
38
|
+
end
|
|
39
|
+
alias number_of_colors number_of_colours
|
|
40
|
+
|
|
41
|
+
def format(temp_object)
|
|
42
|
+
identify(temp_object)[:format]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def image?(temp_object)
|
|
46
|
+
!!catch(:unable_to_handle){ identify(temp_object) }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Dragonfly
|
|
2
|
+
module ImageMagick
|
|
3
|
+
|
|
4
|
+
# ImageMagick Config is a saved configuration for Dragonfly apps, which does the following:
|
|
5
|
+
# - registers an imagemagick analyser
|
|
6
|
+
# - registers an imagemagick processor
|
|
7
|
+
# - registers an imagemagick encoder
|
|
8
|
+
# - adds thumb shortcuts like '280x140!', etc.
|
|
9
|
+
# Look at the source code for apply_configuration to see exactly how it configures the app.
|
|
10
|
+
module Config
|
|
11
|
+
|
|
12
|
+
def self.apply_configuration(app, opts={})
|
|
13
|
+
app.configure do |c|
|
|
14
|
+
c.analyser.register(ImageMagick::Analyser)
|
|
15
|
+
c.processor.register(ImageMagick::Processor)
|
|
16
|
+
c.encoder.register(ImageMagick::Encoder)
|
|
17
|
+
c.generator.register(ImageMagick::Generator)
|
|
18
|
+
|
|
19
|
+
c.job :thumb do |geometry, format|
|
|
20
|
+
process :thumb, geometry
|
|
21
|
+
encode format if format
|
|
22
|
+
end
|
|
23
|
+
c.job :gif do
|
|
24
|
+
encode :gif
|
|
25
|
+
end
|
|
26
|
+
c.job :jpg do
|
|
27
|
+
encode :jpg
|
|
28
|
+
end
|
|
29
|
+
c.job :png do
|
|
30
|
+
encode :png
|
|
31
|
+
end
|
|
32
|
+
c.job :convert do |args, format|
|
|
33
|
+
process :convert, args, format
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Dragonfly
|
|
2
|
+
module ImageMagick
|
|
3
|
+
class Encoder
|
|
4
|
+
|
|
5
|
+
include Configurable
|
|
6
|
+
include Utils
|
|
7
|
+
|
|
8
|
+
configurable_attr :supported_formats, [
|
|
9
|
+
:ai,
|
|
10
|
+
:bmp,
|
|
11
|
+
:eps,
|
|
12
|
+
:gif,
|
|
13
|
+
:gif87,
|
|
14
|
+
:ico,
|
|
15
|
+
:j2c,
|
|
16
|
+
:jp2,
|
|
17
|
+
:jpeg,
|
|
18
|
+
:jpg,
|
|
19
|
+
:pbm,
|
|
20
|
+
:pcd,
|
|
21
|
+
:pct,
|
|
22
|
+
:pcx,
|
|
23
|
+
:pdf,
|
|
24
|
+
:pict,
|
|
25
|
+
:pjpeg,
|
|
26
|
+
:png,
|
|
27
|
+
:png24,
|
|
28
|
+
:png32,
|
|
29
|
+
:png8,
|
|
30
|
+
:pnm,
|
|
31
|
+
:ppm,
|
|
32
|
+
:ps,
|
|
33
|
+
:psd,
|
|
34
|
+
:ras,
|
|
35
|
+
:tga,
|
|
36
|
+
:tiff,
|
|
37
|
+
:wbmp,
|
|
38
|
+
:xbm,
|
|
39
|
+
:xpm,
|
|
40
|
+
:xwd
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
def encode(temp_object, format, args='')
|
|
44
|
+
format = format.to_s.downcase
|
|
45
|
+
throw :unable_to_handle unless supported_formats.include?(format.to_sym)
|
|
46
|
+
details = identify(temp_object)
|
|
47
|
+
|
|
48
|
+
if details[:format] == format.to_sym && args.empty?
|
|
49
|
+
temp_object
|
|
50
|
+
else
|
|
51
|
+
convert(temp_object, args, format)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
module Dragonfly
|
|
2
|
+
module ImageMagick
|
|
3
|
+
class Generator
|
|
4
|
+
|
|
5
|
+
FONT_STYLES = {
|
|
6
|
+
'normal' => 'normal',
|
|
7
|
+
'italic' => 'italic',
|
|
8
|
+
'oblique' => 'oblique'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
FONT_STRETCHES = {
|
|
12
|
+
'normal' => 'normal',
|
|
13
|
+
'semi-condensed' => 'semi-condensed',
|
|
14
|
+
'condensed' => 'condensed',
|
|
15
|
+
'extra-condensed' => 'extra-condensed',
|
|
16
|
+
'ultra-condensed' => 'ultra-condensed',
|
|
17
|
+
'semi-expanded' => 'semi-expanded',
|
|
18
|
+
'expanded' => 'expanded',
|
|
19
|
+
'extra-expanded' => 'extra-expanded',
|
|
20
|
+
'ultra-expanded' => 'ultra-expanded'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
FONT_WEIGHTS = {
|
|
24
|
+
'normal' => 'normal',
|
|
25
|
+
'bold' => 'bold',
|
|
26
|
+
'bolder' => 'bolder',
|
|
27
|
+
'lighter' => 'lighter',
|
|
28
|
+
'100' => 100,
|
|
29
|
+
'200' => 200,
|
|
30
|
+
'300' => 300,
|
|
31
|
+
'400' => 400,
|
|
32
|
+
'500' => 500,
|
|
33
|
+
'600' => 600,
|
|
34
|
+
'700' => 700,
|
|
35
|
+
'800' => 800,
|
|
36
|
+
'900' => 900
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
include Utils
|
|
40
|
+
include Configurable
|
|
41
|
+
|
|
42
|
+
def plain(width, height, colour, opts={})
|
|
43
|
+
format = opts[:format] || 'png'
|
|
44
|
+
[
|
|
45
|
+
convert(nil, "-size #{width}x#{height} 'xc:#{colour}'", format),
|
|
46
|
+
{:format => format.to_sym, :name => "plain.#{format}"}
|
|
47
|
+
]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def plasma(width, height, format='png')
|
|
51
|
+
[
|
|
52
|
+
convert(nil, "-size #{width}x#{height} plasma:fractal", format),
|
|
53
|
+
{:format => format.to_sym, :name => "plasma.#{format}"}
|
|
54
|
+
]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def text(string, opts={})
|
|
58
|
+
opts = HashWithCssStyleKeys[opts]
|
|
59
|
+
args = []
|
|
60
|
+
format = (opts[:format] || :png)
|
|
61
|
+
background = opts[:background_color] || 'none'
|
|
62
|
+
font_size = (opts[:font_size] || 12).to_i
|
|
63
|
+
escaped_string = "\"#{string.gsub(/"/, '\"')}\""
|
|
64
|
+
|
|
65
|
+
# Settings
|
|
66
|
+
args.push("-gravity NorthWest")
|
|
67
|
+
args.push("-antialias")
|
|
68
|
+
args.push("-pointsize #{font_size}")
|
|
69
|
+
args.push("-font '#{opts[:font]}'") if opts[:font]
|
|
70
|
+
args.push("-family '#{opts[:font_family]}'") if opts[:font_family]
|
|
71
|
+
args.push("-fill #{opts[:color]}") if opts[:color]
|
|
72
|
+
args.push("-stroke #{opts[:stroke_color]}") if opts[:stroke_color]
|
|
73
|
+
args.push("-style #{FONT_STYLES[opts[:font_style]]}") if opts[:font_style]
|
|
74
|
+
args.push("-stretch #{FONT_STRETCHES[opts[:font_stretch]]}") if opts[:font_stretch]
|
|
75
|
+
args.push("-weight #{FONT_WEIGHTS[opts[:font_weight]]}") if opts[:font_weight]
|
|
76
|
+
args.push("-background #{background}")
|
|
77
|
+
args.push("label:#{escaped_string}")
|
|
78
|
+
|
|
79
|
+
# Padding
|
|
80
|
+
pt, pr, pb, pl = parse_padding_string(opts[:padding]) if opts[:padding]
|
|
81
|
+
padding_top = (opts[:padding_top] || pt || 0)
|
|
82
|
+
padding_right = (opts[:padding_right] || pr || 0)
|
|
83
|
+
padding_bottom = (opts[:padding_bottom] || pb || 0)
|
|
84
|
+
padding_left = (opts[:padding_left] || pl || 0)
|
|
85
|
+
|
|
86
|
+
tempfile = convert(nil, args.join(' '), format)
|
|
87
|
+
|
|
88
|
+
if (padding_top || padding_right || padding_bottom || padding_left)
|
|
89
|
+
attrs = identify(tempfile)
|
|
90
|
+
text_width = attrs[:width].to_i
|
|
91
|
+
text_height = attrs[:height].to_i
|
|
92
|
+
width = padding_left + text_width + padding_right
|
|
93
|
+
height = padding_top + text_height + padding_bottom
|
|
94
|
+
|
|
95
|
+
args = args.slice(0, args.length - 2)
|
|
96
|
+
args.push("-size #{width}x#{height}")
|
|
97
|
+
args.push("xc:#{background}")
|
|
98
|
+
args.push("-annotate 0x0+#{padding_left}+#{padding_top} #{escaped_string}")
|
|
99
|
+
run "#{convert_command} #{args.join(' ')} #{tempfile.path}"
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
[
|
|
103
|
+
tempfile,
|
|
104
|
+
{:format => format, :name => "text.#{format}"}
|
|
105
|
+
]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
private
|
|
109
|
+
|
|
110
|
+
# Use css-style padding declaration, i.e.
|
|
111
|
+
# 10 (all sides)
|
|
112
|
+
# 10 5 (top/bottom, left/right)
|
|
113
|
+
# 10 5 10 (top, left/right, bottom)
|
|
114
|
+
# 10 5 10 5 (top, right, bottom, left)
|
|
115
|
+
def parse_padding_string(str)
|
|
116
|
+
padding_parts = str.gsub('px','').split(/\s+/).map{|px| px.to_i}
|
|
117
|
+
case padding_parts.size
|
|
118
|
+
when 1
|
|
119
|
+
p = padding_parts.first
|
|
120
|
+
[p,p,p,p]
|
|
121
|
+
when 2
|
|
122
|
+
p,q = padding_parts
|
|
123
|
+
[p,q,p,q]
|
|
124
|
+
when 3
|
|
125
|
+
p,q,r = padding_parts
|
|
126
|
+
[p,q,r,q]
|
|
127
|
+
when 4
|
|
128
|
+
padding_parts
|
|
129
|
+
else raise ArgumentError, "Couldn't parse padding string '#{str}' - should be a css-style string"
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def scale_factor_for(font_size)
|
|
134
|
+
# Scale approximately to 64 if below
|
|
135
|
+
min_size = 64
|
|
136
|
+
if font_size < min_size
|
|
137
|
+
(min_size.to_f / font_size).ceil
|
|
138
|
+
else
|
|
139
|
+
1
|
|
140
|
+
end.to_f
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
module Dragonfly
|
|
2
|
+
module ImageMagick
|
|
3
|
+
class Processor
|
|
4
|
+
|
|
5
|
+
GRAVITIES = {
|
|
6
|
+
'nw' => 'NorthWest',
|
|
7
|
+
'n' => 'North',
|
|
8
|
+
'ne' => 'NorthEast',
|
|
9
|
+
'w' => 'West',
|
|
10
|
+
'c' => 'Center',
|
|
11
|
+
'e' => 'East',
|
|
12
|
+
'sw' => 'SouthWest',
|
|
13
|
+
's' => 'South',
|
|
14
|
+
'se' => 'SouthEast'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
# Geometry string patterns
|
|
18
|
+
RESIZE_GEOMETRY = /^\d*x\d*[><%^!]?$|^\d+@$/ # e.g. '300x200!'
|
|
19
|
+
CROPPED_RESIZE_GEOMETRY = /^(\d+)x(\d+)#(\w{1,2})?$/ # e.g. '20x50#ne'
|
|
20
|
+
CROP_GEOMETRY = /^(\d+)x(\d+)([+-]\d+)?([+-]\d+)?(\w{1,2})?$/ # e.g. '30x30+10+10'
|
|
21
|
+
THUMB_GEOMETRY = Regexp.union RESIZE_GEOMETRY, CROPPED_RESIZE_GEOMETRY, CROP_GEOMETRY
|
|
22
|
+
|
|
23
|
+
include Utils
|
|
24
|
+
|
|
25
|
+
def resize(temp_object, geometry)
|
|
26
|
+
convert(temp_object, "-resize '#{geometry}'")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def crop(temp_object, opts={})
|
|
30
|
+
width = opts[:width]
|
|
31
|
+
height = opts[:height]
|
|
32
|
+
gravity = GRAVITIES[opts[:gravity]]
|
|
33
|
+
x = "#{opts[:x] || 0}"
|
|
34
|
+
x = '+' + x unless x[/^[+-]/]
|
|
35
|
+
y = "#{opts[:y] || 0}"
|
|
36
|
+
y = '+' + y unless y[/^[+-]/]
|
|
37
|
+
|
|
38
|
+
convert(temp_object, "-crop #{width}x#{height}#{x}#{y}#{" -gravity #{gravity}" if gravity}")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def flip(temp_object)
|
|
42
|
+
convert(temp_object, "-flip")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def flop(temp_object)
|
|
46
|
+
convert(temp_object, "-flop")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def greyscale(temp_object)
|
|
50
|
+
convert(temp_object, "-colorspace Gray")
|
|
51
|
+
end
|
|
52
|
+
alias grayscale greyscale
|
|
53
|
+
|
|
54
|
+
def resize_and_crop(temp_object, opts={})
|
|
55
|
+
attrs = identify(temp_object)
|
|
56
|
+
current_width = attrs[:width].to_i
|
|
57
|
+
current_height = attrs[:height].to_i
|
|
58
|
+
|
|
59
|
+
width = opts[:width] ? opts[:width].to_i : current_width
|
|
60
|
+
height = opts[:height] ? opts[:height].to_i : current_height
|
|
61
|
+
gravity = opts[:gravity] || 'c'
|
|
62
|
+
|
|
63
|
+
if width != current_width || height != current_height
|
|
64
|
+
scale = [width.to_f / current_width, height.to_f / current_height].max
|
|
65
|
+
temp_object = TempObject.new(resize(temp_object, "#{(scale * current_width).ceil}x#{(scale * current_height).ceil}"))
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
crop(temp_object, :width => width, :height => height, :gravity => gravity)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def rotate(temp_object, amount, opts={})
|
|
72
|
+
convert(temp_object, "-rotate '#{amount}#{opts[:qualifier]}'")
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def thumb(temp_object, geometry)
|
|
76
|
+
case geometry
|
|
77
|
+
when RESIZE_GEOMETRY
|
|
78
|
+
resize(temp_object, geometry)
|
|
79
|
+
when CROPPED_RESIZE_GEOMETRY
|
|
80
|
+
resize_and_crop(temp_object, :width => $1, :height => $2, :gravity => $3)
|
|
81
|
+
when CROP_GEOMETRY
|
|
82
|
+
crop(temp_object,
|
|
83
|
+
:width => $1,
|
|
84
|
+
:height => $2,
|
|
85
|
+
:x => $3,
|
|
86
|
+
:y => $4,
|
|
87
|
+
:gravity => $5
|
|
88
|
+
)
|
|
89
|
+
else raise ArgumentError, "Didn't recognise the geometry string #{geometry}"
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def convert(temp_object, args='', format=nil)
|
|
94
|
+
format ? [super, {:format => format.to_sym}] : super
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|