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.
Files changed (159) hide show
  1. data/.rspec +1 -0
  2. data/.yardopts +24 -0
  3. data/Gemfile +30 -0
  4. data/History.md +323 -0
  5. data/LICENSE +20 -0
  6. data/README.md +88 -0
  7. data/Rakefile +50 -0
  8. data/VERSION +1 -0
  9. data/config.ru +14 -0
  10. data/docs.watchr +1 -0
  11. data/dragonfly.gemspec +297 -0
  12. data/extra_docs/Analysers.md +66 -0
  13. data/extra_docs/Caching.md +23 -0
  14. data/extra_docs/Configuration.md +124 -0
  15. data/extra_docs/Couch.md +49 -0
  16. data/extra_docs/DataStorage.md +153 -0
  17. data/extra_docs/Encoding.md +67 -0
  18. data/extra_docs/GeneralUsage.md +121 -0
  19. data/extra_docs/Generators.md +60 -0
  20. data/extra_docs/Heroku.md +50 -0
  21. data/extra_docs/ImageMagick.md +125 -0
  22. data/extra_docs/Index.md +33 -0
  23. data/extra_docs/MimeTypes.md +40 -0
  24. data/extra_docs/Models.md +272 -0
  25. data/extra_docs/Mongo.md +45 -0
  26. data/extra_docs/Processing.md +77 -0
  27. data/extra_docs/Rack.md +52 -0
  28. data/extra_docs/Rails2.md +57 -0
  29. data/extra_docs/Rails3.md +62 -0
  30. data/extra_docs/Sinatra.md +25 -0
  31. data/extra_docs/URLs.md +169 -0
  32. data/features/images.feature +47 -0
  33. data/features/no_processing.feature +14 -0
  34. data/features/rails_3.0.5.feature +8 -0
  35. data/features/steps/common_steps.rb +8 -0
  36. data/features/steps/dragonfly_steps.rb +66 -0
  37. data/features/steps/rails_steps.rb +28 -0
  38. data/features/support/env.rb +13 -0
  39. data/features/support/setup.rb +32 -0
  40. data/fixtures/rails_3.0.5/files/app/models/album.rb +7 -0
  41. data/fixtures/rails_3.0.5/files/app/views/albums/new.html.erb +7 -0
  42. data/fixtures/rails_3.0.5/files/app/views/albums/show.html.erb +6 -0
  43. data/fixtures/rails_3.0.5/files/config/initializers/dragonfly.rb +4 -0
  44. data/fixtures/rails_3.0.5/files/features/manage_album_images.feature +38 -0
  45. data/fixtures/rails_3.0.5/files/features/step_definitions/helper_steps.rb +7 -0
  46. data/fixtures/rails_3.0.5/files/features/step_definitions/image_steps.rb +25 -0
  47. data/fixtures/rails_3.0.5/files/features/support/paths.rb +17 -0
  48. data/fixtures/rails_3.0.5/files/features/text_images.feature +7 -0
  49. data/fixtures/rails_3.0.5/template.rb +20 -0
  50. data/irbrc.rb +18 -0
  51. data/lib/dragonfly.rb +55 -0
  52. data/lib/dragonfly/active_model_extensions.rb +13 -0
  53. data/lib/dragonfly/active_model_extensions/attachment.rb +250 -0
  54. data/lib/dragonfly/active_model_extensions/attachment_class_methods.rb +148 -0
  55. data/lib/dragonfly/active_model_extensions/class_methods.rb +95 -0
  56. data/lib/dragonfly/active_model_extensions/instance_methods.rb +28 -0
  57. data/lib/dragonfly/active_model_extensions/validations.rb +41 -0
  58. data/lib/dragonfly/analyser.rb +58 -0
  59. data/lib/dragonfly/analysis/file_command_analyser.rb +32 -0
  60. data/lib/dragonfly/analysis/image_magick_analyser.rb +6 -0
  61. data/lib/dragonfly/app.rb +172 -0
  62. data/lib/dragonfly/config/heroku.rb +19 -0
  63. data/lib/dragonfly/config/image_magick.rb +6 -0
  64. data/lib/dragonfly/config/rails.rb +20 -0
  65. data/lib/dragonfly/configurable.rb +207 -0
  66. data/lib/dragonfly/core_ext/array.rb +7 -0
  67. data/lib/dragonfly/core_ext/hash.rb +7 -0
  68. data/lib/dragonfly/core_ext/object.rb +12 -0
  69. data/lib/dragonfly/core_ext/string.rb +9 -0
  70. data/lib/dragonfly/core_ext/symbol.rb +9 -0
  71. data/lib/dragonfly/data_storage.rb +9 -0
  72. data/lib/dragonfly/data_storage/couch_data_store.rb +64 -0
  73. data/lib/dragonfly/data_storage/file_data_store.rb +141 -0
  74. data/lib/dragonfly/data_storage/mongo_data_store.rb +86 -0
  75. data/lib/dragonfly/data_storage/s3data_store.rb +145 -0
  76. data/lib/dragonfly/encoder.rb +13 -0
  77. data/lib/dragonfly/encoding/image_magick_encoder.rb +6 -0
  78. data/lib/dragonfly/function_manager.rb +71 -0
  79. data/lib/dragonfly/generation/image_magick_generator.rb +6 -0
  80. data/lib/dragonfly/generator.rb +9 -0
  81. data/lib/dragonfly/hash_with_css_style_keys.rb +21 -0
  82. data/lib/dragonfly/image_magick/analyser.rb +51 -0
  83. data/lib/dragonfly/image_magick/config.rb +41 -0
  84. data/lib/dragonfly/image_magick/encoder.rb +57 -0
  85. data/lib/dragonfly/image_magick/generator.rb +145 -0
  86. data/lib/dragonfly/image_magick/processor.rb +99 -0
  87. data/lib/dragonfly/image_magick/utils.rb +72 -0
  88. data/lib/dragonfly/image_magick_utils.rb +4 -0
  89. data/lib/dragonfly/job.rb +451 -0
  90. data/lib/dragonfly/job_builder.rb +39 -0
  91. data/lib/dragonfly/job_definitions.rb +26 -0
  92. data/lib/dragonfly/job_endpoint.rb +15 -0
  93. data/lib/dragonfly/loggable.rb +28 -0
  94. data/lib/dragonfly/middleware.rb +20 -0
  95. data/lib/dragonfly/processing/image_magick_processor.rb +6 -0
  96. data/lib/dragonfly/processor.rb +9 -0
  97. data/lib/dragonfly/rails/images.rb +27 -0
  98. data/lib/dragonfly/response.rb +97 -0
  99. data/lib/dragonfly/routed_endpoint.rb +40 -0
  100. data/lib/dragonfly/serializer.rb +32 -0
  101. data/lib/dragonfly/server.rb +113 -0
  102. data/lib/dragonfly/simple_cache.rb +23 -0
  103. data/lib/dragonfly/temp_object.rb +175 -0
  104. data/lib/dragonfly/url_mapper.rb +78 -0
  105. data/samples/beach.png +0 -0
  106. data/samples/egg.png +0 -0
  107. data/samples/round.gif +0 -0
  108. data/samples/sample.docx +0 -0
  109. data/samples/taj.jpg +0 -0
  110. data/spec/dragonfly/active_model_extensions/model_spec.rb +1426 -0
  111. data/spec/dragonfly/active_model_extensions/spec_helper.rb +91 -0
  112. data/spec/dragonfly/analyser_spec.rb +123 -0
  113. data/spec/dragonfly/analysis/file_command_analyser_spec.rb +48 -0
  114. data/spec/dragonfly/app_spec.rb +135 -0
  115. data/spec/dragonfly/configurable_spec.rb +461 -0
  116. data/spec/dragonfly/core_ext/array_spec.rb +19 -0
  117. data/spec/dragonfly/core_ext/hash_spec.rb +19 -0
  118. data/spec/dragonfly/core_ext/string_spec.rb +17 -0
  119. data/spec/dragonfly/core_ext/symbol_spec.rb +17 -0
  120. data/spec/dragonfly/data_storage/couch_data_store_spec.rb +76 -0
  121. data/spec/dragonfly/data_storage/file_data_store_spec.rb +296 -0
  122. data/spec/dragonfly/data_storage/mongo_data_store_spec.rb +57 -0
  123. data/spec/dragonfly/data_storage/s3_data_store_spec.rb +258 -0
  124. data/spec/dragonfly/data_storage/shared_data_store_examples.rb +77 -0
  125. data/spec/dragonfly/function_manager_spec.rb +154 -0
  126. data/spec/dragonfly/hash_with_css_style_keys_spec.rb +24 -0
  127. data/spec/dragonfly/image_magick/analyser_spec.rb +64 -0
  128. data/spec/dragonfly/image_magick/encoder_spec.rb +41 -0
  129. data/spec/dragonfly/image_magick/generator_spec.rb +172 -0
  130. data/spec/dragonfly/image_magick/processor_spec.rb +233 -0
  131. data/spec/dragonfly/image_magick/utils_spec.rb +18 -0
  132. data/spec/dragonfly/job_builder_spec.rb +37 -0
  133. data/spec/dragonfly/job_definitions_spec.rb +35 -0
  134. data/spec/dragonfly/job_endpoint_spec.rb +173 -0
  135. data/spec/dragonfly/job_spec.rb +1046 -0
  136. data/spec/dragonfly/loggable_spec.rb +80 -0
  137. data/spec/dragonfly/middleware_spec.rb +47 -0
  138. data/spec/dragonfly/routed_endpoint_spec.rb +48 -0
  139. data/spec/dragonfly/serializer_spec.rb +61 -0
  140. data/spec/dragonfly/server_spec.rb +278 -0
  141. data/spec/dragonfly/simple_cache_spec.rb +27 -0
  142. data/spec/dragonfly/temp_object_spec.rb +306 -0
  143. data/spec/dragonfly/url_mapper_spec.rb +126 -0
  144. data/spec/functional/deprecations_spec.rb +51 -0
  145. data/spec/functional/image_magick_app_spec.rb +27 -0
  146. data/spec/functional/model_urls_spec.rb +85 -0
  147. data/spec/functional/remote_on_the_fly_spec.rb +51 -0
  148. data/spec/functional/to_response_spec.rb +31 -0
  149. data/spec/spec_helper.rb +51 -0
  150. data/spec/support/argument_matchers.rb +19 -0
  151. data/spec/support/image_matchers.rb +47 -0
  152. data/spec/support/simple_matchers.rb +53 -0
  153. data/yard/handlers/configurable_attr_handler.rb +38 -0
  154. data/yard/setup.rb +15 -0
  155. data/yard/templates/default/fulldoc/html/css/common.css +107 -0
  156. data/yard/templates/default/layout/html/layout.erb +89 -0
  157. data/yard/templates/default/module/html/configuration_summary.erb +31 -0
  158. data/yard/templates/default/module/setup.rb +17 -0
  159. metadata +544 -0
@@ -0,0 +1,13 @@
1
+ module Dragonfly
2
+ class Encoder < FunctionManager
3
+
4
+ def add(name=:encode, callable_obj=nil, &block)
5
+ super(name, callable_obj, &block)
6
+ end
7
+
8
+ def encode(temp_object, *args)
9
+ call_last(:encode, temp_object, *args)
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,6 @@
1
+ module Dragonfly
2
+ module Encoding
3
+ puts "WARNING: Dragonfly::Encoding::ImageMagickEncoder is DEPRECATED and will soon be removed. Please use Dragonfly::ImageMagick::Encoder instead."
4
+ ImageMagickEncoder = ImageMagick::Encoder
5
+ end
6
+ end
@@ -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,6 @@
1
+ module Dragonfly
2
+ module Generation
3
+ puts "WARNING: Dragonfly::Generation::ImageMagickGenerator is DEPRECATED and will soon be removed. Please use Dragonfly::ImageMagick::Generator instead."
4
+ ImageMagickGenerator = ImageMagick::Generator
5
+ end
6
+ end
@@ -0,0 +1,9 @@
1
+ module Dragonfly
2
+ class Generator < FunctionManager
3
+
4
+ def generate(method, *args)
5
+ call_last(method, *args)
6
+ end
7
+
8
+ end
9
+ 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