fog-dragonfly 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. data/.specopts +2 -0
  2. data/.yardopts +23 -0
  3. data/Gemfile +23 -0
  4. data/Gemfile.rails.2.3.5 +14 -0
  5. data/History.md +266 -0
  6. data/LICENSE +20 -0
  7. data/README.md +88 -0
  8. data/Rakefile +92 -0
  9. data/VERSION +1 -0
  10. data/config.ru +13 -0
  11. data/docs.watchr +1 -0
  12. data/dragonfly.gemspec +293 -0
  13. data/extra_docs/Analysers.md +108 -0
  14. data/extra_docs/Caching.md +23 -0
  15. data/extra_docs/Configuration.md +138 -0
  16. data/extra_docs/DataStorage.md +136 -0
  17. data/extra_docs/Encoding.md +96 -0
  18. data/extra_docs/GeneralUsage.md +121 -0
  19. data/extra_docs/Generators.md +102 -0
  20. data/extra_docs/Heroku.md +50 -0
  21. data/extra_docs/Index.md +36 -0
  22. data/extra_docs/MimeTypes.md +40 -0
  23. data/extra_docs/Models.md +266 -0
  24. data/extra_docs/Mongo.md +45 -0
  25. data/extra_docs/Processing.md +130 -0
  26. data/extra_docs/Rack.md +52 -0
  27. data/extra_docs/Rails2.md +55 -0
  28. data/extra_docs/Rails3.md +62 -0
  29. data/extra_docs/Sinatra.md +25 -0
  30. data/extra_docs/URLs.md +169 -0
  31. data/features/3.0.3.feature +8 -0
  32. data/features/images.feature +47 -0
  33. data/features/no_processing.feature +14 -0
  34. data/features/rails_2.3.5.feature +7 -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 +39 -0
  38. data/features/support/env.rb +40 -0
  39. data/fixtures/files/app/models/album.rb +3 -0
  40. data/fixtures/files/app/views/albums/new.html.erb +4 -0
  41. data/fixtures/files/app/views/albums/show.html.erb +4 -0
  42. data/fixtures/files/config/initializers/dragonfly.rb +4 -0
  43. data/fixtures/files/features/manage_album_images.feature +12 -0
  44. data/fixtures/files/features/step_definitions/image_steps.rb +15 -0
  45. data/fixtures/files/features/support/paths.rb +15 -0
  46. data/fixtures/files/features/text_images.feature +7 -0
  47. data/fixtures/rails_2.3.5/template.rb +10 -0
  48. data/fixtures/rails_3.0.3/template.rb +20 -0
  49. data/irbrc.rb +17 -0
  50. data/lib/dragonfly.rb +45 -0
  51. data/lib/dragonfly/active_model_extensions.rb +13 -0
  52. data/lib/dragonfly/active_model_extensions/attachment.rb +169 -0
  53. data/lib/dragonfly/active_model_extensions/class_methods.rb +45 -0
  54. data/lib/dragonfly/active_model_extensions/instance_methods.rb +28 -0
  55. data/lib/dragonfly/active_model_extensions/validations.rb +37 -0
  56. data/lib/dragonfly/analyser.rb +59 -0
  57. data/lib/dragonfly/analysis/file_command_analyser.rb +32 -0
  58. data/lib/dragonfly/analysis/image_magick_analyser.rb +47 -0
  59. data/lib/dragonfly/analysis/r_magick_analyser.rb +63 -0
  60. data/lib/dragonfly/app.rb +182 -0
  61. data/lib/dragonfly/config/heroku.rb +19 -0
  62. data/lib/dragonfly/config/image_magick.rb +41 -0
  63. data/lib/dragonfly/config/r_magick.rb +46 -0
  64. data/lib/dragonfly/config/rails.rb +17 -0
  65. data/lib/dragonfly/configurable.rb +119 -0
  66. data/lib/dragonfly/core_ext/object.rb +8 -0
  67. data/lib/dragonfly/core_ext/string.rb +9 -0
  68. data/lib/dragonfly/core_ext/symbol.rb +9 -0
  69. data/lib/dragonfly/data_storage.rb +9 -0
  70. data/lib/dragonfly/data_storage/file_data_store.rb +114 -0
  71. data/lib/dragonfly/data_storage/mongo_data_store.rb +82 -0
  72. data/lib/dragonfly/data_storage/s3data_store.rb +115 -0
  73. data/lib/dragonfly/encoder.rb +13 -0
  74. data/lib/dragonfly/encoding/image_magick_encoder.rb +57 -0
  75. data/lib/dragonfly/encoding/r_magick_encoder.rb +61 -0
  76. data/lib/dragonfly/function_manager.rb +69 -0
  77. data/lib/dragonfly/generation/hash_with_css_style_keys.rb +23 -0
  78. data/lib/dragonfly/generation/image_magick_generator.rb +140 -0
  79. data/lib/dragonfly/generation/r_magick_generator.rb +155 -0
  80. data/lib/dragonfly/generator.rb +9 -0
  81. data/lib/dragonfly/image_magick_utils.rb +81 -0
  82. data/lib/dragonfly/job.rb +371 -0
  83. data/lib/dragonfly/job_builder.rb +39 -0
  84. data/lib/dragonfly/job_definitions.rb +26 -0
  85. data/lib/dragonfly/job_endpoint.rb +15 -0
  86. data/lib/dragonfly/loggable.rb +28 -0
  87. data/lib/dragonfly/middleware.rb +34 -0
  88. data/lib/dragonfly/processing/image_magick_processor.rb +99 -0
  89. data/lib/dragonfly/processing/r_magick_processor.rb +126 -0
  90. data/lib/dragonfly/processor.rb +9 -0
  91. data/lib/dragonfly/r_magick_utils.rb +48 -0
  92. data/lib/dragonfly/rails/images.rb +22 -0
  93. data/lib/dragonfly/response.rb +82 -0
  94. data/lib/dragonfly/routed_endpoint.rb +40 -0
  95. data/lib/dragonfly/serializer.rb +32 -0
  96. data/lib/dragonfly/simple_cache.rb +23 -0
  97. data/lib/dragonfly/simple_endpoint.rb +63 -0
  98. data/lib/dragonfly/temp_object.rb +220 -0
  99. data/samples/beach.png +0 -0
  100. data/samples/egg.png +0 -0
  101. data/samples/round.gif +0 -0
  102. data/samples/sample.docx +0 -0
  103. data/samples/taj.jpg +0 -0
  104. data/spec/argument_matchers.rb +19 -0
  105. data/spec/dragonfly/active_model_extensions/active_model_setup.rb +97 -0
  106. data/spec/dragonfly/active_model_extensions/active_record_setup.rb +85 -0
  107. data/spec/dragonfly/active_model_extensions/model_spec.rb +723 -0
  108. data/spec/dragonfly/active_model_extensions/spec_helper.rb +11 -0
  109. data/spec/dragonfly/analyser_spec.rb +123 -0
  110. data/spec/dragonfly/analysis/file_command_analyser_spec.rb +57 -0
  111. data/spec/dragonfly/analysis/image_magick_analyser_spec.rb +15 -0
  112. data/spec/dragonfly/analysis/r_magick_analyser_spec.rb +27 -0
  113. data/spec/dragonfly/analysis/shared_analyser_spec.rb +51 -0
  114. data/spec/dragonfly/app_spec.rb +280 -0
  115. data/spec/dragonfly/config/r_magick_spec.rb +25 -0
  116. data/spec/dragonfly/configurable_spec.rb +220 -0
  117. data/spec/dragonfly/core_ext/string_spec.rb +17 -0
  118. data/spec/dragonfly/core_ext/symbol_spec.rb +17 -0
  119. data/spec/dragonfly/data_storage/data_store_spec.rb +76 -0
  120. data/spec/dragonfly/data_storage/file_data_store_spec.rb +169 -0
  121. data/spec/dragonfly/data_storage/mongo_data_store_spec.rb +38 -0
  122. data/spec/dragonfly/data_storage/s3_data_store_spec.rb +94 -0
  123. data/spec/dragonfly/deprecation_spec.rb +20 -0
  124. data/spec/dragonfly/encoding/image_magick_encoder_spec.rb +41 -0
  125. data/spec/dragonfly/encoding/r_magick_encoder_spec.rb +37 -0
  126. data/spec/dragonfly/function_manager_spec.rb +154 -0
  127. data/spec/dragonfly/generation/hash_with_css_style_keys_spec.rb +24 -0
  128. data/spec/dragonfly/generation/image_magick_generator_spec.rb +12 -0
  129. data/spec/dragonfly/generation/r_magick_generator_spec.rb +24 -0
  130. data/spec/dragonfly/generation/shared_generator_spec.rb +91 -0
  131. data/spec/dragonfly/image_magick_utils_spec.rb +16 -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 +120 -0
  135. data/spec/dragonfly/job_spec.rb +773 -0
  136. data/spec/dragonfly/loggable_spec.rb +80 -0
  137. data/spec/dragonfly/middleware_spec.rb +68 -0
  138. data/spec/dragonfly/processing/image_magick_processor_spec.rb +29 -0
  139. data/spec/dragonfly/processing/r_magick_processor_spec.rb +26 -0
  140. data/spec/dragonfly/processing/shared_processing_spec.rb +215 -0
  141. data/spec/dragonfly/routed_endpoint_spec.rb +48 -0
  142. data/spec/dragonfly/serializer_spec.rb +61 -0
  143. data/spec/dragonfly/simple_cache_spec.rb +27 -0
  144. data/spec/dragonfly/simple_endpoint_spec.rb +89 -0
  145. data/spec/dragonfly/temp_object_spec.rb +352 -0
  146. data/spec/image_matchers.rb +47 -0
  147. data/spec/simple_matchers.rb +44 -0
  148. data/spec/spec_helper.rb +58 -0
  149. data/yard/handlers/configurable_attr_handler.rb +38 -0
  150. data/yard/setup.rb +15 -0
  151. data/yard/templates/default/fulldoc/html/css/common.css +107 -0
  152. data/yard/templates/default/layout/html/layout.erb +87 -0
  153. data/yard/templates/default/module/html/configuration_summary.erb +31 -0
  154. data/yard/templates/default/module/setup.rb +17 -0
  155. metadata +550 -0
@@ -0,0 +1,115 @@
1
+ require 'fog'
2
+
3
+ module Dragonfly
4
+ module DataStorage
5
+
6
+ class S3DataStore
7
+
8
+ include Configurable
9
+ include Serializer
10
+
11
+ # Available options are defined in Fog Storage[http://github.com/geemus/fog/blob/master/lib/fog/aws/storage.rb]
12
+ #
13
+ # 'eu-west-1' => 's3-eu-west-1.amazonaws.com'
14
+ # 'us-east-1' => 's3.amazonaws.com'
15
+ # 'ap-southeast-1' => 's3-ap-southeast-1.amazonaws.com'
16
+ # 'us-west-1' => 's3-us-west-1.amazonaws.com'
17
+
18
+ configurable_attr :bucket_name
19
+ configurable_attr :access_key_id
20
+ configurable_attr :secret_access_key
21
+ configurable_attr :region
22
+ configurable_attr :specific_uid
23
+ configurable_attr :use_filesystem, true
24
+ configurable_attr :autocreate_bucket, false
25
+
26
+ def initialize(opts={})
27
+ self.bucket_name = opts[:bucket_name]
28
+ self.access_key_id = opts[:access_key_id]
29
+ self.secret_access_key = opts[:secret_access_key]
30
+ end
31
+
32
+ def connection
33
+ @connection ||= Fog::AWS::Storage.new(
34
+ :aws_access_key_id => access_key_id,
35
+ :aws_secret_access_key => secret_access_key,
36
+ :region => region
37
+ )
38
+ end
39
+
40
+ def create_bucket!
41
+ connection.put_bucket(bucket_name) unless bucket_names.include?(bucket_name)
42
+ end
43
+
44
+ def store(temp_object, opts={})
45
+ uid = opts[:path] || generate_uid(temp_object.name || 'file')
46
+ ensure_initialized
47
+ extra_data = temp_object.attributes
48
+ if use_filesystem
49
+ temp_object.file do |f|
50
+ connection.put_object(bucket_name,
51
+ uid,
52
+ f.read,
53
+ s3_metadata_for(extra_data))
54
+ end
55
+ else
56
+ connection.put_object(bucket_name,
57
+ uid,
58
+ temp_object.data,
59
+ s3_metadata_for(extra_data))
60
+ end
61
+ uid
62
+ end
63
+
64
+ def retrieve(uid)
65
+ ensure_initialized
66
+ s3_object = connection.get_object(bucket_name, uid)
67
+ [
68
+ s3_object.body,
69
+ parse_s3_metadata(s3_object.headers)
70
+ ]
71
+ rescue Excon::Errors::NotFound => e
72
+ raise DataNotFound, "#{e} - #{uid}"
73
+ end
74
+
75
+ def destroy(uid)
76
+ ensure_initialized
77
+ connection.delete_object(bucket_name, uid)
78
+ rescue Excon::Errors::NotFound => e
79
+ raise DataNotFound, "#{e} - #{uid}"
80
+ end
81
+
82
+ private
83
+
84
+ def bucket_names
85
+ connection.get_service.body['Buckets'].map{|bucket| bucket['Name'] }
86
+ end
87
+
88
+ def ensure_initialized
89
+ unless @initialized
90
+ create_bucket! if autocreate_bucket
91
+ @initialized = true
92
+ end
93
+ end
94
+
95
+ def generate_uid(name)
96
+ if self.specific_uid.is_a?(Proc)
97
+ self.specific_uid.call(name)
98
+ else
99
+ "#{Time.now.strftime '%Y/%m/%d/%H/%M/%S'}/#{rand(1000)}/#{name.gsub(/[^\w.]+/, '_')}"
100
+ end
101
+ end
102
+
103
+ def s3_metadata_for(extra_data)
104
+ {'x-amz-meta-extra' => marshal_encode(extra_data)}
105
+ end
106
+
107
+ def parse_s3_metadata(metadata)
108
+ extra_data = metadata['x-amz-meta-extra']
109
+ marshal_decode(extra_data) if extra_data
110
+ end
111
+
112
+ end
113
+
114
+ end
115
+ end
@@ -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,57 @@
1
+ module Dragonfly
2
+ module Encoding
3
+ class ImageMagickEncoder
4
+
5
+ include Configurable
6
+ include ImageMagickUtils
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,61 @@
1
+ require 'RMagick'
2
+
3
+ module Dragonfly
4
+ module Encoding
5
+ class RMagickEncoder
6
+
7
+ include Configurable
8
+ include RMagickUtils
9
+
10
+ configurable_attr :supported_formats, [
11
+ :ai,
12
+ :bmp,
13
+ :eps,
14
+ :gif,
15
+ :gif87,
16
+ :ico,
17
+ :j2c,
18
+ :jp2,
19
+ :jpeg,
20
+ :jpg,
21
+ :pbm,
22
+ :pcd,
23
+ :pct,
24
+ :pcx,
25
+ :pdf,
26
+ :pict,
27
+ :pjpeg,
28
+ :png,
29
+ :png24,
30
+ :png32,
31
+ :png8,
32
+ :pnm,
33
+ :ppm,
34
+ :ps,
35
+ :psd,
36
+ :ras,
37
+ :tga,
38
+ :tiff,
39
+ :wbmp,
40
+ :xbm,
41
+ :xpm,
42
+ :xwd
43
+ ]
44
+ configurable_attr :use_filesystem, true
45
+
46
+ def encode(temp_object, format, encoding={})
47
+ format = format.to_s.downcase
48
+ throw :unable_to_handle unless supported_formats.include?(format.to_sym)
49
+ rmagick_image(temp_object) do |image|
50
+ if image.format.downcase == format
51
+ temp_object # do nothing
52
+ else
53
+ image.format = format
54
+ image
55
+ end
56
+ end
57
+ end
58
+
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,69 @@
1
+ module Dragonfly
2
+ class FunctionManager
3
+
4
+ # Exceptions
5
+ class NotDefined < NoMethodError; end
6
+ class UnableToHandle < NotImplementedError; end
7
+
8
+ include Loggable
9
+
10
+ def initialize
11
+ @functions = {}
12
+ @objects = []
13
+ end
14
+
15
+ def add(name, callable_obj=nil, &block)
16
+ functions[name] ||= []
17
+ functions[name] << (callable_obj || block)
18
+ end
19
+
20
+ attr_reader :functions, :objects
21
+
22
+ def register(klass, *args, &block)
23
+ obj = klass.new(*args)
24
+ obj.configure(&block) if block
25
+ obj.use_same_log_as(self) if obj.is_a?(Loggable)
26
+ methods_to_add(obj).each do |meth|
27
+ add meth.to_sym, obj.method(meth)
28
+ end
29
+ objects << obj
30
+ obj
31
+ end
32
+
33
+ def call_last(meth, *args)
34
+ if functions[meth.to_sym]
35
+ functions[meth.to_sym].reverse.each do |function|
36
+ catch :unable_to_handle do
37
+ return function.call(*args)
38
+ end
39
+ end
40
+ # If the code gets here, then none of the registered functions were able to handle the method call
41
+ raise UnableToHandle, "None of the functions registered with #{self} were able to deal with the method call " +
42
+ "#{meth}(#{args.map{|a| a.inspect[0..100]}.join(',')}). You may need to register one that can."
43
+ else
44
+ raise NotDefined, "function #{meth} not registered with #{self}"
45
+ end
46
+ end
47
+
48
+ def get_registered(klass)
49
+ objects.reverse.detect{|o| o.instance_of?(klass) }
50
+ end
51
+
52
+ def inspect
53
+ to_s.sub(/>$/, " with functions: #{functions.keys.map{|k| k.to_s }.sort.join(', ')} >")
54
+ end
55
+
56
+ private
57
+
58
+ def methods_to_add(obj)
59
+ if obj.is_a?(Configurable)
60
+ obj.public_methods(false) -
61
+ obj.configuration_methods.map{|meth| meth.to_method_name} -
62
+ [:configuration_methods.to_method_name]
63
+ else
64
+ obj.public_methods(false)
65
+ end
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,23 @@
1
+ module Dragonfly
2
+ module Generation
3
+
4
+ # HashWithCssStyleKeys is solely for being able to access a hash
5
+ # which has css-style keys (e.g. 'font-size') with the underscore
6
+ # symbol version
7
+ # @example
8
+ # opts = {'font-size' => '23px', :color => 'white'}
9
+ # opts = HashWithCssStyleKeys[opts]
10
+ # opts[:font_size] # ===> '23px'
11
+ # opts[:color] # ===> 'white'
12
+ class HashWithCssStyleKeys < Hash
13
+ def [](key)
14
+ super || (
15
+ str_key = key.to_s
16
+ css_key = str_key.gsub('_','-')
17
+ super(str_key) || super(css_key) || super(css_key.to_sym)
18
+ )
19
+ end
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,140 @@
1
+ module Dragonfly
2
+ module Generation
3
+ class ImageMagickGenerator
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 ImageMagickUtils
40
+ include Configurable
41
+
42
+ def plasma(width, height, format='png')
43
+ tempfile = new_tempfile(format)
44
+ run "#{convert_command} -size #{width}x#{height} plasma:fractal #{tempfile.path}"
45
+ [
46
+ tempfile,
47
+ {:format => format.to_sym, :name => "plasma.#{format}"}
48
+ ]
49
+ end
50
+
51
+ def text(string, opts={})
52
+ opts = HashWithCssStyleKeys[opts]
53
+ args = []
54
+ format = (opts[:format] || :png)
55
+ background = opts[:background_color] || 'none'
56
+ font_size = (opts[:font_size] || 12).to_i
57
+ escaped_string = "\"#{string.gsub(/"/, '\"')}\""
58
+
59
+ # Settings
60
+ args.push("-gravity NorthWest")
61
+ args.push("-antialias")
62
+ args.push("-pointsize #{font_size}")
63
+ args.push("-font '#{opts[:font]}'") if opts[:font]
64
+ args.push("-family '#{opts[:font_family]}'") if opts[:font_family]
65
+ args.push("-fill #{opts[:color]}") if opts[:color]
66
+ args.push("-stroke #{opts[:stroke_color]}") if opts[:stroke_color]
67
+ args.push("-style #{FONT_STYLES[opts[:font_style]]}") if opts[:font_style]
68
+ args.push("-stretch #{FONT_STRETCHES[opts[:font_stretch]]}") if opts[:font_stretch]
69
+ args.push("-weight #{FONT_WEIGHTS[opts[:font_weight]]}") if opts[:font_weight]
70
+ args.push("-background #{background}")
71
+ args.push("label:#{escaped_string}")
72
+
73
+ # Padding
74
+ pt, pr, pb, pl = parse_padding_string(opts[:padding]) if opts[:padding]
75
+ padding_top = (opts[:padding_top] || pt || 0)
76
+ padding_right = (opts[:padding_right] || pr || 0)
77
+ padding_bottom = (opts[:padding_bottom] || pb || 0)
78
+ padding_left = (opts[:padding_left] || pl || 0)
79
+
80
+ tempfile = new_tempfile(format)
81
+ run "#{convert_command} #{args.join(' ')} #{tempfile.path}"
82
+
83
+ if (padding_top || padding_right || padding_bottom || padding_left)
84
+ attrs = identify(tempfile)
85
+ text_width = attrs[:width].to_i
86
+ text_height = attrs[:height].to_i
87
+ width = padding_left + text_width + padding_right
88
+ height = padding_top + text_height + padding_bottom
89
+
90
+ args = args.slice(0, args.length - 2)
91
+ args.push("-size #{width}x#{height}")
92
+ args.push("xc:#{background}")
93
+ args.push("-annotate 0x0+#{padding_left}+#{padding_top} #{escaped_string}")
94
+ run "#{convert_command} #{args.join(' ')} #{tempfile.path}"
95
+ end
96
+
97
+ [
98
+ tempfile,
99
+ {:format => format, :name => "text.#{format}"}
100
+ ]
101
+ end
102
+
103
+ private
104
+
105
+ # Use css-style padding declaration, i.e.
106
+ # 10 (all sides)
107
+ # 10 5 (top/bottom, left/right)
108
+ # 10 5 10 (top, left/right, bottom)
109
+ # 10 5 10 5 (top, right, bottom, left)
110
+ def parse_padding_string(str)
111
+ padding_parts = str.gsub('px','').split(/\s+/).map{|px| px.to_i}
112
+ case padding_parts.size
113
+ when 1
114
+ p = padding_parts.first
115
+ [p,p,p,p]
116
+ when 2
117
+ p,q = padding_parts
118
+ [p,q,p,q]
119
+ when 3
120
+ p,q,r = padding_parts
121
+ [p,q,r,q]
122
+ when 4
123
+ padding_parts
124
+ else raise ArgumentError, "Couldn't parse padding string '#{str}' - should be a css-style string"
125
+ end
126
+ end
127
+
128
+ def scale_factor_for(font_size)
129
+ # Scale approximately to 64 if below
130
+ min_size = 64
131
+ if font_size < min_size
132
+ (min_size.to_f / font_size).ceil
133
+ else
134
+ 1
135
+ end.to_f
136
+ end
137
+
138
+ end
139
+ end
140
+ end