dragonfly 0.9.15 → 1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of dragonfly might be problematic. Click here for more details.

Files changed (203) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -8
  3. data/.travis.yml +11 -0
  4. data/Gemfile +1 -0
  5. data/History.md +52 -2
  6. data/LICENSE +1 -1
  7. data/README.md +38 -95
  8. data/dev/grid.jpg +0 -0
  9. data/dev/irbrc.rb +27 -0
  10. data/dev/rails_template.rb +38 -0
  11. data/dev/test.ru +56 -0
  12. data/dev/test_rails +19 -0
  13. data/dragonfly.gemspec +3 -21
  14. data/lib/dragonfly.rb +45 -44
  15. data/lib/dragonfly/app.rb +175 -96
  16. data/lib/dragonfly/configurable.rb +71 -170
  17. data/lib/dragonfly/content.rb +211 -0
  18. data/lib/dragonfly/core_ext/object.rb +1 -6
  19. data/lib/dragonfly/file_data_store.rb +197 -0
  20. data/lib/dragonfly/image_magick/analysers/image_properties.rb +23 -0
  21. data/lib/dragonfly/image_magick/generators/convert.rb +19 -0
  22. data/lib/dragonfly/image_magick/generators/plain.rb +26 -0
  23. data/lib/dragonfly/image_magick/generators/plasma.rb +25 -0
  24. data/lib/dragonfly/image_magick/generators/text.rb +127 -0
  25. data/lib/dragonfly/image_magick/plugin.rb +83 -0
  26. data/lib/dragonfly/image_magick/processors/convert.rb +29 -0
  27. data/lib/dragonfly/image_magick/processors/encode.rb +18 -0
  28. data/lib/dragonfly/image_magick/processors/thumb.rb +76 -0
  29. data/lib/dragonfly/job.rb +118 -134
  30. data/lib/dragonfly/job_endpoint.rb +2 -0
  31. data/lib/dragonfly/memory_data_store.rb +34 -0
  32. data/lib/dragonfly/middleware.rb +5 -3
  33. data/lib/dragonfly/{active_model_extensions.rb → model.rb} +5 -3
  34. data/lib/dragonfly/{active_model_extensions → model}/attachment.rb +40 -35
  35. data/lib/dragonfly/{active_model_extensions → model}/attachment_class_methods.rb +36 -40
  36. data/lib/dragonfly/model/class_methods.rb +109 -0
  37. data/lib/dragonfly/{active_model_extensions → model}/instance_methods.rb +2 -2
  38. data/lib/dragonfly/{active_model_extensions → model}/validations.rb +17 -12
  39. data/lib/dragonfly/railtie.rb +8 -6
  40. data/lib/dragonfly/register.rb +27 -0
  41. data/lib/dragonfly/response.rb +47 -52
  42. data/lib/dragonfly/routed_endpoint.rb +4 -0
  43. data/lib/dragonfly/serializer.rb +15 -5
  44. data/lib/dragonfly/server.rb +56 -29
  45. data/lib/dragonfly/shell.rb +12 -23
  46. data/lib/dragonfly/spec/data_store_examples.rb +64 -0
  47. data/lib/dragonfly/temp_object.rb +32 -47
  48. data/lib/dragonfly/url_attributes.rb +19 -22
  49. data/lib/dragonfly/url_mapper.rb +3 -0
  50. data/lib/dragonfly/utils.rb +12 -0
  51. data/lib/dragonfly/version.rb +1 -1
  52. data/lib/dragonfly/whitelist.rb +19 -0
  53. data/lib/rails/generators/dragonfly/USAGE +8 -0
  54. data/lib/rails/generators/dragonfly/dragonfly_generator.rb +24 -0
  55. data/lib/rails/generators/dragonfly/templates/initializer.rb.erb +27 -0
  56. data/samples/gif.gif +0 -0
  57. data/spec/dragonfly/app_spec.rb +270 -64
  58. data/spec/dragonfly/configurable_spec.rb +142 -418
  59. data/spec/dragonfly/content_spec.rb +353 -0
  60. data/spec/dragonfly/cookie_monster_spec.rb +2 -1
  61. data/spec/dragonfly/file_data_store_spec.rb +301 -0
  62. data/spec/dragonfly/image_magick/analysers/image_properties_spec.rb +20 -0
  63. data/spec/dragonfly/image_magick/generators/convert_spec.rb +19 -0
  64. data/spec/dragonfly/image_magick/generators/plain_spec.rb +50 -0
  65. data/spec/dragonfly/image_magick/generators/plasma_spec.rb +32 -0
  66. data/spec/dragonfly/image_magick/generators/text_spec.rb +77 -0
  67. data/spec/dragonfly/image_magick/plugin_spec.rb +131 -0
  68. data/spec/dragonfly/image_magick/processors/convert_spec.rb +56 -0
  69. data/spec/dragonfly/image_magick/processors/thumb_spec.rb +173 -0
  70. data/spec/dragonfly/job_endpoint_spec.rb +30 -73
  71. data/spec/dragonfly/job_spec.rb +280 -608
  72. data/spec/dragonfly/memory_data_store_spec.rb +20 -0
  73. data/spec/dragonfly/middleware_spec.rb +47 -27
  74. data/spec/dragonfly/{active_model_extensions → model}/model_spec.rb +331 -555
  75. data/spec/dragonfly/model/validations_spec.rb +196 -0
  76. data/spec/dragonfly/register_spec.rb +35 -0
  77. data/spec/dragonfly/routed_endpoint_spec.rb +6 -6
  78. data/spec/dragonfly/serializer_spec.rb +13 -15
  79. data/spec/dragonfly/server_spec.rb +122 -46
  80. data/spec/dragonfly/shell_spec.rb +43 -24
  81. data/spec/dragonfly/temp_object_spec.rb +69 -94
  82. data/spec/dragonfly/url_attributes_spec.rb +49 -0
  83. data/spec/dragonfly/utils_spec.rb +32 -0
  84. data/spec/dragonfly/whitelist_spec.rb +30 -0
  85. data/spec/dragonfly_spec.rb +43 -0
  86. data/spec/fixtures/deprecated_stored_content/eggs.bonus +1 -0
  87. data/spec/fixtures/deprecated_stored_content/eggs.bonus.meta +1 -0
  88. data/spec/functional/configuration_spec.rb +19 -0
  89. data/spec/functional/model_urls_spec.rb +43 -41
  90. data/spec/functional/remote_on_the_fly_spec.rb +14 -16
  91. data/spec/functional/shell_commands_spec.rb +24 -14
  92. data/spec/functional/to_response_spec.rb +10 -10
  93. data/spec/functional/urls_spec.rb +5 -3
  94. data/spec/spec_helper.rb +23 -28
  95. data/spec/support/argument_matchers.rb +7 -8
  96. data/spec/support/image_matchers.rb +14 -36
  97. data/spec/support/model_helpers.rb +97 -0
  98. data/spec/support/simple_matchers.rb +12 -0
  99. metadata +92 -393
  100. data/.yardopts +0 -29
  101. data/Rakefile +0 -36
  102. data/config.ru +0 -14
  103. data/docs.watchr +0 -1
  104. data/extra_docs/Analysers.md +0 -68
  105. data/extra_docs/Caching.md +0 -23
  106. data/extra_docs/Configuration.md +0 -149
  107. data/extra_docs/Couch.md +0 -49
  108. data/extra_docs/DataStorage.md +0 -226
  109. data/extra_docs/Encoding.md +0 -67
  110. data/extra_docs/ExampleUseCases.md +0 -116
  111. data/extra_docs/GeneralUsage.md +0 -105
  112. data/extra_docs/Generators.md +0 -68
  113. data/extra_docs/Heroku.md +0 -50
  114. data/extra_docs/ImageMagick.md +0 -136
  115. data/extra_docs/Index.md +0 -33
  116. data/extra_docs/MimeTypes.md +0 -40
  117. data/extra_docs/Models.md +0 -441
  118. data/extra_docs/Mongo.md +0 -42
  119. data/extra_docs/Processing.md +0 -77
  120. data/extra_docs/Rack.md +0 -52
  121. data/extra_docs/Rails2.md +0 -57
  122. data/extra_docs/Rails3.md +0 -56
  123. data/extra_docs/ServingRemotely.md +0 -104
  124. data/extra_docs/Sinatra.md +0 -25
  125. data/extra_docs/URLs.md +0 -203
  126. data/features/images.feature +0 -47
  127. data/features/no_processing.feature +0 -14
  128. data/features/rails.feature +0 -8
  129. data/features/steps/common_steps.rb +0 -8
  130. data/features/steps/dragonfly_steps.rb +0 -66
  131. data/features/steps/rails_steps.rb +0 -40
  132. data/features/support/env.rb +0 -13
  133. data/features/support/setup.rb +0 -41
  134. data/fixtures/rails/files/app/models/album.rb +0 -6
  135. data/fixtures/rails/files/app/views/albums/new.html.erb +0 -7
  136. data/fixtures/rails/files/app/views/albums/show.html.erb +0 -6
  137. data/fixtures/rails/files/config/initializers/dragonfly.rb +0 -4
  138. data/fixtures/rails/files/features/manage_album_images.feature +0 -38
  139. data/fixtures/rails/files/features/step_definitions/helper_steps.rb +0 -7
  140. data/fixtures/rails/files/features/step_definitions/image_steps.rb +0 -25
  141. data/fixtures/rails/files/features/step_definitions/web_steps.rb +0 -189
  142. data/fixtures/rails/files/features/support/paths.rb +0 -17
  143. data/fixtures/rails/files/features/text_images.feature +0 -7
  144. data/fixtures/rails/template.rb +0 -20
  145. data/irbrc.rb +0 -19
  146. data/lib/dragonfly/active_model_extensions/class_methods.rb +0 -98
  147. data/lib/dragonfly/analyser.rb +0 -58
  148. data/lib/dragonfly/analysis/file_command_analyser.rb +0 -33
  149. data/lib/dragonfly/analysis/image_magick_analyser.rb +0 -6
  150. data/lib/dragonfly/config/heroku.rb +0 -26
  151. data/lib/dragonfly/config/image_magick.rb +0 -6
  152. data/lib/dragonfly/config/rails.rb +0 -20
  153. data/lib/dragonfly/data_storage.rb +0 -11
  154. data/lib/dragonfly/data_storage/couch_data_store.rb +0 -83
  155. data/lib/dragonfly/data_storage/file_data_store.rb +0 -144
  156. data/lib/dragonfly/data_storage/mongo_data_store.rb +0 -96
  157. data/lib/dragonfly/data_storage/s3data_store.rb +0 -168
  158. data/lib/dragonfly/encoder.rb +0 -13
  159. data/lib/dragonfly/encoding/image_magick_encoder.rb +0 -6
  160. data/lib/dragonfly/function_manager.rb +0 -67
  161. data/lib/dragonfly/generation/image_magick_generator.rb +0 -6
  162. data/lib/dragonfly/generator.rb +0 -9
  163. data/lib/dragonfly/image_magick/analyser.rb +0 -53
  164. data/lib/dragonfly/image_magick/config.rb +0 -44
  165. data/lib/dragonfly/image_magick/encoder.rb +0 -57
  166. data/lib/dragonfly/image_magick/generator.rb +0 -147
  167. data/lib/dragonfly/image_magick/processor.rb +0 -114
  168. data/lib/dragonfly/image_magick/utils.rb +0 -46
  169. data/lib/dragonfly/image_magick_utils.rb +0 -4
  170. data/lib/dragonfly/job_builder.rb +0 -39
  171. data/lib/dragonfly/job_definitions.rb +0 -30
  172. data/lib/dragonfly/loggable.rb +0 -28
  173. data/lib/dragonfly/processing/image_magick_processor.rb +0 -6
  174. data/lib/dragonfly/processor.rb +0 -9
  175. data/lib/dragonfly/rails/images.rb +0 -32
  176. data/lib/dragonfly/simple_cache.rb +0 -23
  177. data/spec/dragonfly/active_model_extensions/spec_helper.rb +0 -95
  178. data/spec/dragonfly/analyser_spec.rb +0 -123
  179. data/spec/dragonfly/analysis/file_command_analyser_spec.rb +0 -49
  180. data/spec/dragonfly/data_storage/couch_data_store_spec.rb +0 -84
  181. data/spec/dragonfly/data_storage/file_data_store_spec.rb +0 -308
  182. data/spec/dragonfly/data_storage/mongo_data_store_spec.rb +0 -81
  183. data/spec/dragonfly/data_storage/s3_data_store_spec.rb +0 -277
  184. data/spec/dragonfly/data_storage/shared_data_store_examples.rb +0 -77
  185. data/spec/dragonfly/function_manager_spec.rb +0 -154
  186. data/spec/dragonfly/image_magick/analyser_spec.rb +0 -73
  187. data/spec/dragonfly/image_magick/encoder_spec.rb +0 -46
  188. data/spec/dragonfly/image_magick/generator_spec.rb +0 -178
  189. data/spec/dragonfly/image_magick/processor_spec.rb +0 -293
  190. data/spec/dragonfly/job_builder_spec.rb +0 -37
  191. data/spec/dragonfly/job_definitions_spec.rb +0 -57
  192. data/spec/dragonfly/loggable_spec.rb +0 -80
  193. data/spec/dragonfly/simple_cache_spec.rb +0 -27
  194. data/spec/dragonfly/url_attributes.rb +0 -47
  195. data/spec/functional/deprecations_spec.rb +0 -51
  196. data/spec/functional/image_magick_app_spec.rb +0 -27
  197. data/spec/test_imagemagick.ru +0 -49
  198. data/yard/handlers/configurable_attr_handler.rb +0 -38
  199. data/yard/setup.rb +0 -15
  200. data/yard/templates/default/fulldoc/html/css/common.css +0 -109
  201. data/yard/templates/default/layout/html/layout.erb +0 -93
  202. data/yard/templates/default/module/html/configuration_summary.erb +0 -31
  203. data/yard/templates/default/module/setup.rb +0 -17
@@ -1,12 +1,7 @@
1
1
  class Object
2
-
3
- # Don't want to depend on activesupport for this
4
- def blank?
5
- respond_to?(:empty?) ? empty? : !self
6
- end
7
2
 
8
3
  def to_dragonfly_unique_s
9
4
  to_s
10
5
  end
11
-
6
+
12
7
  end
@@ -0,0 +1,197 @@
1
+ require 'pathname'
2
+ require 'yaml'
3
+ require 'fileutils'
4
+ require 'dragonfly/utils'
5
+
6
+ module Dragonfly
7
+ class FileDataStore
8
+
9
+ # Exceptions
10
+ class BadUID < RuntimeError; end
11
+ class UnableToFormUrl < RuntimeError; end
12
+
13
+ class MetaStore
14
+ def write(data_path, meta)
15
+ File.open(meta_path(data_path), 'wb') do |f|
16
+ f.write dump(meta)
17
+ end
18
+ end
19
+
20
+ def read(data_path)
21
+ path = meta_path(data_path)
22
+ File.open(path,'rb'){|f| load(f.read) } if File.exist?(path)
23
+ end
24
+
25
+ def destroy(path)
26
+ FileUtils.rm_f meta_path(path)
27
+ end
28
+
29
+ private
30
+
31
+ def meta_path(data_path)
32
+ raise NotImplementedError
33
+ end
34
+
35
+ def dump(meta)
36
+ raise NotImplementedError
37
+ end
38
+
39
+ def load(string)
40
+ raise NotImplementedError
41
+ end
42
+ end
43
+
44
+ class YAMLMetaStore < MetaStore
45
+ def meta_path(data_path)
46
+ "#{data_path}.meta.yml"
47
+ end
48
+
49
+ def dump(meta)
50
+ YAML.dump(meta)
51
+ end
52
+
53
+ def load(string)
54
+ YAML.load(string)
55
+ end
56
+ end
57
+
58
+ class MarshalMetaStore < MetaStore
59
+ def meta_path(data_path)
60
+ "#{data_path}.meta"
61
+ end
62
+
63
+ def dump(meta)
64
+ Marshal.dump(meta)
65
+ end
66
+
67
+ def load(string)
68
+ Utils.stringify_keys Marshal.load(string)
69
+ end
70
+ end
71
+
72
+ def initialize(opts={})
73
+ self.root_path = opts[:root_path] || 'dragonfly'
74
+ self.server_root = opts[:server_root]
75
+ self.store_meta = opts[:store_meta]
76
+ @meta_store = YAMLMetaStore.new
77
+ @deprecated_meta_store = MarshalMetaStore.new
78
+ end
79
+
80
+ attr_writer :store_meta
81
+ attr_reader :root_path, :server_root
82
+
83
+ def root_path=(path)
84
+ @root_path = path ? path.to_s : nil
85
+ end
86
+
87
+ def server_root=(path)
88
+ @server_root = path ? path.to_s : nil
89
+ end
90
+
91
+ def store_meta?
92
+ @store_meta != false # Default to true if not set
93
+ end
94
+
95
+ def write(content, opts={})
96
+ relative_path = if opts[:path]
97
+ opts[:path]
98
+ else
99
+ filename = content.name || 'file'
100
+ relative_path = relative_path_for(filename)
101
+ end
102
+
103
+ path = absolute(relative_path)
104
+ until !File.exist?(path)
105
+ path = disambiguate(path)
106
+ end
107
+ content.to_file(path).close
108
+ meta_store.write(path, content.meta) if store_meta?
109
+
110
+ relative(path)
111
+ end
112
+
113
+ def read(relative_path)
114
+ validate_path!(relative_path)
115
+ path = absolute(relative_path)
116
+ pathname = Pathname.new(path)
117
+ return nil unless pathname.exist?
118
+ [
119
+ pathname,
120
+ (
121
+ if store_meta?
122
+ meta_store.read(path) || deprecated_meta_store.read(path) || {}
123
+ end
124
+ )
125
+ ]
126
+ end
127
+
128
+ def destroy(relative_path)
129
+ validate_path!(relative_path)
130
+ path = absolute(relative_path)
131
+ FileUtils.rm path
132
+ meta_store.destroy(path)
133
+ purge_empty_directories(relative_path)
134
+ rescue Errno::ENOENT => e
135
+ Dragonfly.warn("#{self.class.name} destroy error: #{e}")
136
+ end
137
+
138
+ def url_for(relative_path, opts={})
139
+ if server_root.nil?
140
+ raise UnableToFormUrl, "you need to configure server_root for #{self.class.name} in order to form urls"
141
+ else
142
+ _, __, path = absolute(relative_path).partition(server_root)
143
+ if path.empty?
144
+ raise UnableToFormUrl, "couldn't form url for uid #{relative_path.inspect} with root_path #{root_path.inspect} and server_root #{server_root.inspect}"
145
+ else
146
+ path
147
+ end
148
+ end
149
+ end
150
+
151
+ def disambiguate(path)
152
+ dirname = File.dirname(path)
153
+ basename = File.basename(path, '.*')
154
+ extname = File.extname(path)
155
+ "#{dirname}/#{basename}_#{(Time.now.usec*10 + rand(100)).to_s(32)}#{extname}"
156
+ end
157
+
158
+ private
159
+
160
+ attr_reader :meta_store, :deprecated_meta_store
161
+
162
+ def absolute(relative_path)
163
+ relative_path.to_s == '.' ? root_path : File.join(root_path, relative_path)
164
+ end
165
+
166
+ def relative(absolute_path)
167
+ absolute_path[/^#{Regexp.escape root_path}\/?(.*)$/, 1]
168
+ end
169
+
170
+ def directory_empty?(path)
171
+ Dir.entries(path) == ['.','..']
172
+ end
173
+
174
+ def root_path?(dir)
175
+ root_path == dir
176
+ end
177
+
178
+ def relative_path_for(filename)
179
+ time = Time.now
180
+ "#{time.strftime '%Y/%m/%d/%H_%M_%S'}_#{time.usec.to_s(36)}_#{filename.gsub(/[^\w.]+/,'_')}"
181
+ end
182
+
183
+ def purge_empty_directories(path)
184
+ containing_directory = Pathname.new(path).dirname
185
+ containing_directory.ascend do |relative_dir|
186
+ dir = absolute(relative_dir)
187
+ FileUtils.rmdir dir if directory_empty?(dir) && !root_path?(dir)
188
+ end
189
+ end
190
+
191
+ def validate_path!(uid)
192
+ raise BadUID, uid if Utils.blank?(uid) || uid['../']
193
+ end
194
+
195
+ end
196
+
197
+ end
@@ -0,0 +1,23 @@
1
+ module Dragonfly
2
+ module ImageMagick
3
+ module Analysers
4
+ class ImageProperties
5
+
6
+ def call(content)
7
+ identify_command = content.env[:identify_command] || 'identify'
8
+ details = content.shell_eval do |path|
9
+ "#{identify_command} -ping -format '%m %w %h' #{path}"
10
+ end
11
+ format, width, height = details.split
12
+ {
13
+ 'format' => format.downcase,
14
+ 'width' => width.to_i,
15
+ 'height' => height.to_i
16
+ }
17
+ end
18
+
19
+ end
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,19 @@
1
+ module Dragonfly
2
+ module ImageMagick
3
+ module Generators
4
+ class Convert
5
+
6
+ def call(content, args, format)
7
+ format = format.to_s
8
+ convert_command = content.env[:convert_command] || 'convert'
9
+ content.shell_generate :ext => format do |path|
10
+ "#{convert_command} #{args} #{path}"
11
+ end
12
+ content.add_meta('format' => format)
13
+ end
14
+
15
+ end
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,26 @@
1
+ module Dragonfly
2
+ module ImageMagick
3
+ module Generators
4
+ class Plain
5
+
6
+ def call(content, width, height, opts={})
7
+ format = extract_format(opts)
8
+ colour = opts['colour'] || opts['color'] || 'white'
9
+ content.generate!(:convert, "-size #{width}x#{height} xc:#{colour}", format)
10
+ content.add_meta('format' => format, 'name' => "plain.#{format}")
11
+ end
12
+
13
+ def update_url(url_attributes, width, height, opts={})
14
+ url_attributes.name = "plain.#{extract_format(opts)}"
15
+ end
16
+
17
+ private
18
+
19
+ def extract_format(opts)
20
+ opts['format'] || 'png'
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,25 @@
1
+ module Dragonfly
2
+ module ImageMagick
3
+ module Generators
4
+ class Plasma
5
+
6
+ def call(content, width, height, opts={})
7
+ format = extract_format(opts)
8
+ content.generate!(:convert, "-size #{width}x#{height} plasma:fractal", format)
9
+ content.add_meta('format' => format, 'name' => "plasma.#{format}")
10
+ end
11
+
12
+ def update_url(url_attributes, width, height, opts={})
13
+ url_attributes.name = "plasma.#{extract_format(opts)}"
14
+ end
15
+
16
+ private
17
+
18
+ def extract_format(opts)
19
+ opts['format'] || 'png'
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,127 @@
1
+ require 'dragonfly/hash_with_css_style_keys'
2
+
3
+ module Dragonfly
4
+ module ImageMagick
5
+ module Generators
6
+ class Text
7
+
8
+ FONT_STYLES = {
9
+ 'normal' => 'normal',
10
+ 'italic' => 'italic',
11
+ 'oblique' => 'oblique'
12
+ }
13
+
14
+ FONT_STRETCHES = {
15
+ 'normal' => 'normal',
16
+ 'semi-condensed' => 'semi-condensed',
17
+ 'condensed' => 'condensed',
18
+ 'extra-condensed' => 'extra-condensed',
19
+ 'ultra-condensed' => 'ultra-condensed',
20
+ 'semi-expanded' => 'semi-expanded',
21
+ 'expanded' => 'expanded',
22
+ 'extra-expanded' => 'extra-expanded',
23
+ 'ultra-expanded' => 'ultra-expanded'
24
+ }
25
+
26
+ FONT_WEIGHTS = {
27
+ 'normal' => 'normal',
28
+ 'bold' => 'bold',
29
+ 'bolder' => 'bolder',
30
+ 'lighter' => 'lighter',
31
+ '100' => 100,
32
+ '200' => 200,
33
+ '300' => 300,
34
+ '400' => 400,
35
+ '500' => 500,
36
+ '600' => 600,
37
+ '700' => 700,
38
+ '800' => 800,
39
+ '900' => 900
40
+ }
41
+
42
+ def update_url(url_attributes, string, opts={})
43
+ url_attributes.name = "text.#{extract_format(opts)}"
44
+ end
45
+
46
+ def call(content, string, opts={})
47
+ opts = HashWithCssStyleKeys[opts]
48
+ args = []
49
+ format = extract_format(opts)
50
+ background = opts['background_color'] || 'none'
51
+ font_size = (opts['font_size'] || 12).to_i
52
+ escaped_string = "\"#{string.gsub(/"/, '\"')}\""
53
+
54
+ # Settings
55
+ args.push("-gravity NorthWest")
56
+ args.push("-antialias")
57
+ args.push("-pointsize #{font_size}")
58
+ args.push("-font \"#{opts['font']}\"") if opts['font']
59
+ args.push("-family '#{opts['font_family']}'") if opts['font_family']
60
+ args.push("-fill #{opts['color']}") if opts['color']
61
+ args.push("-stroke #{opts['stroke_color']}") if opts['stroke_color']
62
+ args.push("-style #{FONT_STYLES[opts['font_style']]}") if opts['font_style']
63
+ args.push("-stretch #{FONT_STRETCHES[opts['font_stretch']]}") if opts['font_stretch']
64
+ args.push("-weight #{FONT_WEIGHTS[opts['font_weight']]}") if opts['font_weight']
65
+ args.push("-background #{background}")
66
+ args.push("label:#{escaped_string}")
67
+
68
+ # Padding
69
+ pt, pr, pb, pl = parse_padding_string(opts['padding']) if opts['padding']
70
+ padding_top = (opts['padding_top'] || pt || 0)
71
+ padding_right = (opts['padding_right'] || pr || 0)
72
+ padding_bottom = (opts['padding_bottom'] || pb || 0)
73
+ padding_left = (opts['padding_left'] || pl || 0)
74
+
75
+ content.generate!(:convert, args.join(' '), format)
76
+
77
+ if (padding_top || padding_right || padding_bottom || padding_left)
78
+ dimensions = content.analyse(:image_properties)
79
+ text_width = dimensions['width']
80
+ text_height = dimensions['height']
81
+ width = padding_left + text_width + padding_right
82
+ height = padding_top + text_height + padding_bottom
83
+
84
+ args = args.slice(0, args.length - 2)
85
+ args.push("-size #{width}x#{height}")
86
+ args.push("xc:#{background}")
87
+ args.push("-annotate 0x0+#{padding_left}+#{padding_top} #{escaped_string}")
88
+ content.generate!(:convert, args.join(' '), format)
89
+ end
90
+
91
+ content.add_meta('format' => format, 'name' => "text.#{format}")
92
+ end
93
+
94
+ private
95
+
96
+ def extract_format(opts)
97
+ opts['format'] || 'png'
98
+ end
99
+
100
+ # Use css-style padding declaration, i.e.
101
+ # 10 (all sides)
102
+ # 10 5 (top/bottom, left/right)
103
+ # 10 5 10 (top, left/right, bottom)
104
+ # 10 5 10 5 (top, right, bottom, left)
105
+ def parse_padding_string(str)
106
+ padding_parts = str.gsub('px','').split(/\s+/).map{|px| px.to_i}
107
+ case padding_parts.size
108
+ when 1
109
+ p = padding_parts.first
110
+ [p,p,p,p]
111
+ when 2
112
+ p,q = padding_parts
113
+ [p,q,p,q]
114
+ when 3
115
+ p,q,r = padding_parts
116
+ [p,q,r,q]
117
+ when 4
118
+ padding_parts
119
+ else raise ArgumentError, "Couldn't parse padding string '#{str}' - should be a css-style string"
120
+ end
121
+ end
122
+ end
123
+
124
+ end
125
+ end
126
+ end
127
+
@@ -0,0 +1,83 @@
1
+ require 'dragonfly/image_magick/analysers/image_properties'
2
+ require 'dragonfly/image_magick/generators/convert'
3
+ require 'dragonfly/image_magick/generators/plain'
4
+ require 'dragonfly/image_magick/generators/plasma'
5
+ require 'dragonfly/image_magick/generators/text'
6
+ require 'dragonfly/image_magick/processors/convert'
7
+ require 'dragonfly/image_magick/processors/encode'
8
+ require 'dragonfly/image_magick/processors/thumb'
9
+
10
+ module Dragonfly
11
+ module ImageMagick
12
+
13
+ # The ImageMagick Plugin registers an app with generators, analysers and processors.
14
+ # Look at the source code for #call to see exactly how it configures the app.
15
+ class Plugin
16
+
17
+ def call(app, opts={})
18
+ # ENV
19
+ app.env[:convert_command] = opts[:convert_command] || 'convert'
20
+ app.env[:identify_command] = opts[:identify_command] || 'identify'
21
+
22
+ # Analysers
23
+ app.add_analyser :image_properties, ImageMagick::Analysers::ImageProperties.new
24
+ app.add_analyser :width do |content|
25
+ content.analyse(:image_properties)['width']
26
+ end
27
+ app.add_analyser :height do |content|
28
+ content.analyse(:image_properties)['height']
29
+ end
30
+ app.add_analyser :format do |content|
31
+ content.analyse(:image_properties)['format']
32
+ end
33
+ app.add_analyser :aspect_ratio do |content|
34
+ attrs = content.analyse(:image_properties)
35
+ attrs['width'].to_f / attrs['height']
36
+ end
37
+ app.add_analyser :portrait do |content|
38
+ attrs = content.analyse(:image_properties)
39
+ attrs['width'] <= attrs['height']
40
+ end
41
+ app.add_analyser :landscape do |content|
42
+ !content.analyse(:portrait)
43
+ end
44
+ app.add_analyser :image do |content|
45
+ begin
46
+ content.analyse(:image_properties)['format'] != 'pdf'
47
+ rescue Shell::CommandFailed
48
+ false
49
+ end
50
+ end
51
+
52
+ # Aliases
53
+ app.define(:portrait?) { portrait }
54
+ app.define(:landscape?) { landscape }
55
+ app.define(:image?) { image }
56
+
57
+ # Generators
58
+ app.add_generator :convert, ImageMagick::Generators::Convert.new
59
+ app.add_generator :plain, ImageMagick::Generators::Plain.new
60
+ app.add_generator :plasma, ImageMagick::Generators::Plasma.new
61
+ app.add_generator :text, ImageMagick::Generators::Text.new
62
+
63
+ # Processors
64
+ app.add_processor :convert, Processors::Convert.new
65
+ app.add_processor :encode, Processors::Encode.new
66
+ app.add_processor :thumb, Processors::Thumb.new
67
+ app.add_processor :rotate do |content, amount|
68
+ content.process!(:convert, "-rotate #{amount}")
69
+ end
70
+
71
+ # Extra methods
72
+ app.define :identify do |args=""|
73
+ shell_eval do |path|
74
+ "#{app.env[:identify_command]} #{args} #{path}"
75
+ end
76
+ end
77
+
78
+ end
79
+
80
+ end
81
+ end
82
+ end
83
+