thingfish-processor-image 0.1.0.pre20161114102625

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 94cec81297dc83ba31fc6dfcf4b258688c4b232c
4
+ data.tar.gz: 5906271b9d85a5bc5f7da0a55f553d8e4ede3310
5
+ SHA512:
6
+ metadata.gz: 74ad63442c9b7cb228c955f172ded4091cf25a1267581d323f9ad8b9a526ce37eb546a81fb0c3d50c18ae03dfe4749dcc26ecfe85897734cdd7140da3f6ac6d5
7
+ data.tar.gz: 89fd3b8a06e6bce83d42238472dad1b503863f8676579e3d2561aa62c59b3e4937a889c5ebe79ae95180cc89598d0f249babb1b60e13e5219b2329962b7dcafc
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ README.md
3
+ ChangeLog.md
4
+
5
+ LICENSE.txt
data/.editorconfig ADDED
@@ -0,0 +1,14 @@
1
+ # http://EditorConfig.org
2
+
3
+ # top-most EditorConfig file
4
+ root = true
5
+
6
+ # Unix-style newlines with a newline ending every file
7
+ [*]
8
+ end_of_line = lf
9
+ insert_final_newline = true
10
+
11
+ # Tab indentation
12
+ [**.*]
13
+ indent_style = tab
14
+
data/.rdoc_options ADDED
@@ -0,0 +1,16 @@
1
+ --- !ruby/object:RDoc::Options
2
+ encoding: UTF-8
3
+ static_path: []
4
+ rdoc_include:
5
+ - .
6
+ charset: UTF-8
7
+ exclude:
8
+ hyperlink_all: false
9
+ line_numbers: false
10
+ main_page: README.md
11
+ markup: markdown
12
+ show_hash: false
13
+ tab_width: 8
14
+ title: Thingfish-Processor-Image Documentation
15
+ visibility: :protected
16
+ webcvs:
data/.simplecov ADDED
@@ -0,0 +1,9 @@
1
+ # Simplecov config
2
+
3
+ SimpleCov.start do
4
+ add_filter 'spec'
5
+ add_filter 'integration'
6
+ add_group "Needing tests" do |file|
7
+ file.covered_percent < 90
8
+ end
9
+ end
data/ChangeLog ADDED
@@ -0,0 +1,24 @@
1
+ 2016-11-14 Michael Granger <ged@FaerieMUD.org>
2
+
3
+ * History.md, README.md, Rakefile, lib/thingfish/processor/image.rb,
4
+ thingfish-processor-image.gemspec:
5
+ Fix up the README, Rakefile.
6
+ [e04ece44aa56] [tip]
7
+
8
+ 2016-11-13 Michael Granger <ged@FaerieMUD.org>
9
+
10
+ * thingfish-processor-image.gemspec:
11
+ Add the gemspec
12
+ [a43952fd8558] [github/master]
13
+
14
+ * .hgignore, Manifest.txt, Rakefile:
15
+ Update manifest, specify minimum version of Mongrel2
16
+ [8f7523aa9de2]
17
+
18
+ * .document, .editorconfig, .gems, .hgignore, .pryrc, .rdoc_options,
19
+ .ruby-gemset, .ruby-version, .simplecov, Gemfile, History.md,
20
+ LICENSE.txt, Manifest.txt, README.md, Rakefile, certs/ged.pem,
21
+ lib/thingfish/processor/image.rb, spec/data/skunks.jpg,
22
+ spec/spec_helper.rb, spec/thingfish/processor/image_spec.rb:
23
+ Exported from Angelfish
24
+ [3995d4e06f28]
data/History.md ADDED
@@ -0,0 +1,4 @@
1
+ ## v0.1.0 [YYYY-MM-DD] Michael Granger <ged@FaerieMUD.org>
2
+
3
+ Initial release.
4
+
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2016 Michael Granger
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt ADDED
@@ -0,0 +1,14 @@
1
+ .document
2
+ .editorconfig
3
+ .rdoc_options
4
+ .simplecov
5
+ ChangeLog
6
+ History.md
7
+ LICENSE.txt
8
+ Manifest.txt
9
+ README.md
10
+ Rakefile
11
+ lib/thingfish/processor/image.rb
12
+ spec/data/skunks.jpg
13
+ spec/spec_helper.rb
14
+ spec/thingfish/processor/image_spec.rb
data/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # Thingfish-Processor-Image
2
+
3
+ home
4
+ : http://deveiate.org/projects/Thingfish-Processor-Image
5
+
6
+ code
7
+ : http://bitbucket.org/ged/Thingfish-Processor-Image
8
+
9
+ github
10
+ : https://github.com/ged/thingfish-processor-image
11
+
12
+ docs
13
+ : http://deveiate.org/code/thingfish-processor-image
14
+
15
+
16
+ ## Description
17
+
18
+ This is a basic image-processor plugin for the Thingfish digital asset manager.
19
+ It extracts image-related metadata from uploaded media files, and generates one
20
+ or more thumbnail images as related resources.
21
+
22
+
23
+ ## Authors
24
+
25
+ * Michael Granger <ged@FaerieMUD.org>
26
+
27
+
28
+ ## Installation
29
+
30
+ This plugin relies on [ImageMagick](http://www.imagemagick.org/), so you'll
31
+ need to have that installed as well as any `-dev` dependencies appropriate for
32
+ your system.
33
+
34
+ One you've done that:
35
+
36
+ $ gem install thingfish-processor-image
37
+
38
+
39
+ ## Usage
40
+
41
+ As with Thingfish itself, this plugin uses
42
+ Configurability[https://rubygems.org/gems/configurability] to modify default
43
+ behaviors.
44
+
45
+ Here's an example configuration file that enables this plugin.
46
+
47
+ --
48
+ thingfish:
49
+ processors:
50
+ - image
51
+
52
+ images:
53
+ thumbnail_dimensions: 150x150
54
+
55
+
56
+
57
+ ## Contributing
58
+
59
+ You can check out the current development source with Mercurial via its
60
+ {project page}[http://bitbucket.org/ged/thingfish-processor-image]. Or if you prefer Git, via
61
+ {its Github mirror}[https://github.com/ged/thingfish-processor-image].
62
+
63
+ After checking out the source, run:
64
+
65
+ $ rake newb
66
+
67
+ This task will install any missing dependencies, run the tests/specs,
68
+ and generate the API documentation.
69
+
70
+
71
+ ## License
72
+
73
+ Copyright (c) 2016, Michael Granger
74
+ All rights reserved.
75
+
76
+ Redistribution and use in source and binary forms, with or without
77
+ modification, are permitted provided that the following conditions are met:
78
+
79
+ * Redistributions of source code must retain the above copyright notice,
80
+ this list of conditions and the following disclaimer.
81
+
82
+ * Redistributions in binary form must reproduce the above copyright notice,
83
+ this list of conditions and the following disclaimer in the documentation
84
+ and/or other materials provided with the distribution.
85
+
86
+ * Neither the name of the author/s, nor the names of the project's
87
+ contributors may be used to endorse or promote products derived from this
88
+ software without specific prior written permission.
89
+
90
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
91
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
92
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
93
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
94
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
95
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
96
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
97
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
98
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
99
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
100
+
101
+
data/Rakefile ADDED
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env rake
2
+
3
+ begin
4
+ require 'hoe'
5
+ rescue LoadError
6
+ abort "This Rakefile requires hoe (gem install hoe)"
7
+ end
8
+
9
+ GEMSPEC = 'thingfish-processor-image.gemspec'
10
+
11
+
12
+ Hoe.plugin :mercurial
13
+ Hoe.plugin :signing
14
+ Hoe.plugin :deveiate
15
+
16
+ Hoe.plugins.delete :rubyforge
17
+ Hoe.plugins.delete :gemcutter # Remove for public gems
18
+
19
+ hoespec = Hoe.spec 'thingfish-processor-image' do |spec|
20
+ spec.readme_file = 'README.md'
21
+ spec.history_file = 'History.md'
22
+ spec.extra_rdoc_files = FileList[ '*.rdoc', '*.md' ]
23
+ spec.urls = {
24
+ home: 'https://bitbucket.org/ged/thingfish-processor-image',
25
+ code: 'https://bitbucket.org/ged/thingfish-processor-image',
26
+ docs: 'https://deveiate.org/code/thingfish-processor-image',
27
+ github: 'https://github.com/ged/thingfish-processor-image',
28
+ }
29
+
30
+ spec.extra_rdoc_files = FileList[ '*.rdoc', '*.md' ]
31
+ spec.license 'BSD-3-Clause'
32
+
33
+ spec.developer 'Michael Granger', 'ged@FaerieMUD.org'
34
+
35
+ spec.dependency 'mongrel2', '>= 0.46'
36
+ spec.dependency 'thingfish', '~> 0.5'
37
+ spec.dependency 'loggability', '~> 0.11'
38
+ spec.dependency 'rmagick', '~> 2.16'
39
+
40
+ spec.dependency 'hoe-deveiate', '~> 0.3', :developer
41
+ spec.dependency 'simplecov', '~> 0.7', :developer
42
+ spec.dependency 'rdoc-generator-fivefish', '~> 0.1', :developer
43
+
44
+ spec.require_ruby_version( '>=2.3.1' )
45
+ spec.hg_sign_tags = true if spec.respond_to?( :hg_sign_tags= )
46
+ spec.check_history_on_release = true if spec.respond_to?( :check_history_on_release= )
47
+
48
+ self.rdoc_locations << "deveiate:/usr/local/www/public/code/#{remote_rdoc_dir}"
49
+ end
50
+
51
+
52
+ ENV['VERSION'] ||= hoespec.spec.version.to_s
53
+
54
+ # Run the tests before checking in
55
+ task 'hg:precheckin' => [ :check_history, :check_manifest, :gemspec, :spec ]
56
+
57
+ task :test => :spec
58
+
59
+ # Rebuild the ChangeLog immediately before release
60
+ task :prerelease => 'ChangeLog'
61
+ CLOBBER.include( 'ChangeLog' )
62
+
63
+ desc "Build a coverage report"
64
+ task :coverage do
65
+ ENV["COVERAGE"] = 'yes'
66
+ Rake::Task[:spec].invoke
67
+ end
68
+ CLOBBER.include( 'coverage' )
69
+
70
+
71
+ # Use the fivefish formatter for docs generated from development checkout
72
+ if File.directory?( '.hg' )
73
+ require 'rdoc/task'
74
+
75
+ Rake::Task[ 'docs' ].clear
76
+ RDoc::Task.new( 'docs' ) do |rdoc|
77
+ rdoc.main = "README.rdoc"
78
+ rdoc.markup = 'markdown'
79
+ rdoc.rdoc_files.include( "*.rdoc", "ChangeLog", "lib/**/*.rb" )
80
+ rdoc.generator = :fivefish
81
+ rdoc.title = 'Thingfish-Processor-Image'
82
+ rdoc.rdoc_dir = 'doc'
83
+ end
84
+ end
85
+
86
+ task :gemspec => GEMSPEC
87
+ file GEMSPEC => __FILE__
88
+ task GEMSPEC do |task|
89
+ spec = $hoespec.spec
90
+ spec.files.delete( '.gemtest' )
91
+ spec.signing_key = nil
92
+ spec.cert_chain = Dir.glob( 'certs/*.pem' )
93
+ spec.version = "#{spec.version.bump}.0.pre#{Time.now.strftime("%Y%m%d%H%M%S")}"
94
+ File.open( task.name, 'w' ) do |fh|
95
+ fh.write( spec.to_ruby )
96
+ end
97
+ end
98
+ CLOBBER.include( GEMSPEC.to_s )
99
+
100
+ task :default => :gemspec
101
+
@@ -0,0 +1,322 @@
1
+ # -*- ruby -*-
2
+ #encoding: utf-8
3
+
4
+ require 'rmagick'
5
+
6
+ require 'strelka'
7
+ require 'strelka/httprequest/acceptparams'
8
+
9
+ require 'thingfish'
10
+ require 'thingfish/processor'
11
+
12
+
13
+ # Image processor plugin for Thingfish
14
+ class Thingfish::Processor::Image < Thingfish::Processor
15
+ extend Loggability,
16
+ Configurability,
17
+ Strelka::MethodUtilities
18
+
19
+
20
+ # Package version
21
+ VERSION = '0.1.0'
22
+
23
+ # Version control revision
24
+ REVISION = %q$Revision: e04ece44aa56 $
25
+
26
+
27
+ # Loggability API -- log to the :angelfish logger
28
+ log_to :thingfish
29
+
30
+ # Configurability API -- use the 'image_processor' section of the config
31
+ config_key :image_processor
32
+
33
+
34
+ # The default dimensions of thumbnails
35
+ CONFIG_DEFAULTS = {
36
+ thumbnail_dimensions: '100x100',
37
+ }
38
+
39
+ # An array of mediatypes to ignore, even though ImageMagick claims it groks them
40
+ IGNORED_MIMETYPES = %w[
41
+ application/octet-stream
42
+ text/html
43
+ text/plain
44
+ text/x-server-parsed-html
45
+ ]
46
+
47
+
48
+ ##
49
+ # The (maximum) dimensions of generated thumbnails
50
+ singleton_attr_accessor :thumbnail_dimensions
51
+ @thumbnail_dimensions = [ 100, 100 ]
52
+
53
+
54
+ ### Configurability API -- configure the processor with the :images section of
55
+ ### the config.
56
+ def self::configure( config=nil )
57
+ config = self.defaults.merge( config || {} )
58
+
59
+ self.thumbnail_dimensions = config[:thumbnail_dimensions].split('x', 2).map( &:to_i )
60
+ end
61
+
62
+
63
+
64
+ #################################################################
65
+ ### I N S T A N C E M E T H O D S
66
+ #################################################################
67
+
68
+ ### Set up a new Filter object
69
+ def initialize( * ) # :notnew:
70
+ super
71
+
72
+ @supported_formats = find_supported_formats()
73
+ @handled_types = find_handled_types( @supported_formats )
74
+ @generated_types = find_generated_types( @supported_formats )
75
+ end
76
+
77
+
78
+ ######
79
+ public
80
+ ######
81
+
82
+ # The Hash of formats and the operations they support.
83
+ attr_reader :supported_formats
84
+
85
+ # The mediatypes the plugin accepts in a request, as ThingFish::AcceptParam
86
+ # objects
87
+ attr_reader :handled_types
88
+
89
+ # The mediatypes the plugin can generated in a response, as
90
+ # ThingFish::AcceptParam objects
91
+ attr_reader :generated_types
92
+
93
+
94
+ ### Returns +true+ if the given media +type+ is one the processor handles. Overridden so
95
+ ### the types can be used by the instance.
96
+ def handled_type?( type )
97
+ self.log.debug "Looking for handled type for: %p" % [ type ]
98
+ result = self.handled_types.find {|handled_type| type =~ handled_type }
99
+ self.log.debug " found: %p" % [ result ]
100
+ return result
101
+ end
102
+ alias_method :is_handled_type?, :handled_type?
103
+
104
+
105
+ ### Synchronous processor API -- extract metadata from uploaded images.
106
+ def on_request( request )
107
+ self.log.debug "Image-processing %p" % [ request ]
108
+ image = case request.body
109
+ when StringIO
110
+ self.log.debug " making a single image from a StringIO"
111
+ Magick::Image.from_blob( request.body.read )
112
+ else
113
+ self.log.debug " making a flattened image out of %p" % [ request.body.path ]
114
+ list = Magick::ImageList.new( request.body.path )
115
+ list.flatten_images
116
+ end
117
+
118
+ image = image.first if image.respond_to?( :first )
119
+ self.log.debug " image is: %p" % [ image ]
120
+ metadata = self.extract_image_metadata( image )
121
+ self.log.debug " extracted image metadata: %p" % [ metadata ]
122
+ request.add_metadata( metadata )
123
+
124
+ self.log.debug " going to generate a thumbnail..."
125
+ self.generate_thumbnail( image, metadata['title'] ) do |thumbio, thumb_metadata|
126
+ self.log.debug " generated: %p (%p)" % [ thumbio, thumb_metadata ]
127
+ request.add_related_resource( thumbio, thumb_metadata )
128
+ end
129
+
130
+ self.log.debug " destroying the image to free up memory"
131
+ image.destroy!
132
+ end
133
+
134
+
135
+ ### Return a human-readable representation of the receiving object suitable for
136
+ ### debugging.
137
+ def inspect
138
+ return "#<%p:%#x %d supported image formats, %d readable, %d writable>" % [
139
+ self.class,
140
+ self.object_id * 2,
141
+ self.supported_formats.size,
142
+ self.handled_types.size,
143
+ self.generated_types.size,
144
+ ]
145
+ end
146
+
147
+
148
+ #########
149
+ protected
150
+ #########
151
+
152
+ ### Extract metadata from the given +image+ (a Magick::Image object) and return it in
153
+ ### a Hash.
154
+ def extract_image_metadata( image )
155
+ metadata = {}
156
+
157
+ metadata.merge!( self.get_regular_metadata(image) )
158
+ metadata.merge!( self.get_exif_metadata(image) )
159
+
160
+ return metadata
161
+ end
162
+
163
+
164
+ ### Fetch regular image metadata as a Hash.
165
+ def get_regular_metadata( image )
166
+ return {
167
+ 'image:height' => image.rows,
168
+ 'image:width' => image.columns,
169
+ 'image:depth' => image.depth,
170
+ 'image:density' => image.density,
171
+ 'image:gamma' => image.gamma,
172
+ 'image:bounding_box' => image.bounding_box.to_s,
173
+ }
174
+ end
175
+
176
+
177
+ ### Fetch exif metadata and return it as a Hash.
178
+ def get_exif_metadata( image )
179
+ exif_pairs = image.get_exif_by_entry
180
+ exif_pairs.reject! {|name, val| name == 'unknown' || val.nil? }
181
+ exif_pairs.collect! do |name, val|
182
+ newname = name.gsub(/\B([A-Z])(?=[a-z])/) { '_' + $1 }.downcase
183
+ [ "exif:#{newname}", val ]
184
+ end
185
+ return Hash[ exif_pairs ]
186
+ end
187
+
188
+
189
+ ### Create a thumbnail from the given +image+ and return it in a string along with any
190
+ ### associated metadata.
191
+ def generate_thumbnail( image, title )
192
+ dimensions = self.class.thumbnail_dimensions
193
+ self.log.debug "Making thumbnail of max dimensions: [%d X %d]" % dimensions
194
+ thumb = image.resize_to_fit( *dimensions )
195
+ imgdata = StringIO.new
196
+ imgdata << thumb.to_blob {|img| img.format = 'JPG' }
197
+ imgdata.rewind
198
+
199
+ metadata = self.extract_image_metadata( thumb )
200
+ metadata.merge!({
201
+ format: thumb.mime_type,
202
+ relationship: 'thumbnail',
203
+ title: "Thumbnail of %s" % [ title || image.inspect ],
204
+ extent: imgdata.size,
205
+ })
206
+
207
+ self.log.debug " made thumbnail for %p" % [ image ]
208
+ yield( imgdata, metadata )
209
+
210
+ thumb.destroy!
211
+ end
212
+
213
+
214
+ # A struct that can represent the support in the installed ImageMagick for
215
+ # common operations. See Magick.formats for details.
216
+ class MagickOperations
217
+
218
+ ### Create a new MagickOperations for the given +ext+, reading the
219
+ ### supported features of the format from +support_string+.
220
+ def initialize( ext, support_string )
221
+ @ext = ext
222
+ @blob, @read, @write, @multi = support_string.split('')
223
+ end
224
+
225
+ ##
226
+ # Supported features
227
+ attr_reader :ext, :blob, :read, :write, :multi
228
+
229
+
230
+ ### Return a human-readable description of the operations spec
231
+ def to_s
232
+ return [
233
+ self.has_native_blob? ? "Blob" : nil,
234
+ self.can_read? ? "Readable" : nil,
235
+ self.can_write? ? "Writable" : nil,
236
+ self.supports_multi? ? "Multi" : nil,
237
+ ].compact.join(',')
238
+ end
239
+
240
+
241
+ ### Returns +true+ if the operation string indicates that ImageMagick has native blob
242
+ ### support for the associated type
243
+ def has_native_blob?
244
+ return (@blob == '*')
245
+ end
246
+
247
+
248
+ ### Returns +true+ if the operation string indicates that ImageMagick has native blob
249
+ ### support for the associated type
250
+ def can_read?
251
+ return (@read == 'r')
252
+ end
253
+
254
+
255
+ ### Returns +true+ if the operation string indicates that ImageMagick has native blob
256
+ ### support for the associated type
257
+ def can_write?
258
+ return (@write == 'w')
259
+ end
260
+
261
+
262
+ ### Returns +true+ if the operation string indicates that ImageMagick has native blob
263
+ ### support for the associated type
264
+ def supports_multi?
265
+ return (@multi == '+')
266
+ end
267
+
268
+ end # class MagickOperations
269
+
270
+
271
+ ### Transform the installed ImageMagick's list of formats into AcceptParams
272
+ ### for easy comparison later.
273
+ def find_supported_formats
274
+ formats = {}
275
+ raise "Config database doesn't have any mimetypes" if
276
+ Mongrel2::Config.mimetypes.empty?
277
+
278
+ # A hash of image formats and their properties. Each key in the returned
279
+ # hash is the name of a supported image format. Each value is a string in
280
+ # the form "BRWA". The details are in this table:
281
+ # B is "*" if the format has native blob support, and "-" otherwise.
282
+ # R is "r" if ×Magick can read the format, and "-" otherwise.
283
+ # W is "w" if ×Magick can write the format, and "-" otherwise.
284
+ # A is "+" if the format supports multi-image files, and "-" otherwise.
285
+ Magick.formats.each do |ext,support|
286
+ ext = ".#{ext.downcase}"
287
+ self.log.debug "Looking for mediatype for ext: %s (%p)" % [ ext, support ]
288
+
289
+ mimetype = Mongrel2::Config.mimetypes[ ext ] or next
290
+ self.log.debug " mimetype is: %p" % [ mimetype ]
291
+ next if IGNORED_MIMETYPES.include?( mimetype )
292
+
293
+ operations = MagickOperations.new( ext, support )
294
+ self.log.debug " registering image format %s (%s)" % [ mimetype, operations ]
295
+ formats[ mimetype ] = operations
296
+ end
297
+
298
+ self.log.debug "Registered mimetype mapping for %d of %d supported image types" %
299
+ [ formats.keys.length, Magick.formats.length ]
300
+ return formats
301
+ end
302
+
303
+
304
+ ### Return Strelka::HTTPRequest::MediaType objects for the formats that the
305
+ ### processor is capable of reading.
306
+ def find_handled_types( supported_formats )
307
+ return supported_formats.
308
+ select {|type, op| op.can_read? }.
309
+ collect {|type, op| Strelka::HTTPRequest::MediaType.parse(type) }
310
+ end
311
+
312
+
313
+ ### Return an Array of Strelka::HTTPRequest::MediaType objects for the
314
+ ### formats that the processor is capable of writing.
315
+ def find_generated_types( supported_formats )
316
+ return supported_formats.
317
+ select {|type, op| op.can_write? }.
318
+ collect {|type, op| Strelka::HTTPRequest::MediaType.parse(type) }
319
+ end
320
+
321
+ end # class ThingFish::Processor::Image
322
+
Binary file
@@ -0,0 +1,50 @@
1
+ # -*- ruby -*-
2
+ #encoding: utf-8
3
+
4
+ require 'simplecov' if ENV['COVERAGE']
5
+
6
+ require 'rspec'
7
+
8
+ require 'mongrel2'
9
+ require 'mongrel2/testing'
10
+
11
+ require 'thingfish'
12
+ require 'thingfish/spechelpers'
13
+
14
+ require 'loggability/spechelpers'
15
+
16
+ require 'thingfish/processor/image'
17
+
18
+
19
+ module Thingfish::Processor::Image::SpecHelpers
20
+
21
+ FIXTURE_DIR = Pathname( __FILE__ ).dirname + 'data'
22
+
23
+
24
+ ### Load and return the data from the fixture with the specified +filename+.
25
+ def fixture_data( filename )
26
+ fixture = FIXTURE_DIR + filename
27
+ return fixture.open( 'r', encoding: 'binary' )
28
+ end
29
+
30
+ end
31
+
32
+
33
+
34
+ ### Mock with RSpec
35
+ RSpec.configure do |config|
36
+ config.run_all_when_everything_filtered = true
37
+ config.filter_run :focus
38
+ config.order = 'random'
39
+ config.mock_with( :rspec ) do |mock|
40
+ mock.syntax = :expect
41
+ end
42
+
43
+ config.include( Mongrel2::SpecHelpers )
44
+ config.include( Thingfish::SpecHelpers )
45
+ config.include( Loggability::SpecHelpers )
46
+ config.include( Thingfish::Processor::Image::SpecHelpers )
47
+ end
48
+
49
+
50
+
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../../spec_helper'
4
+
5
+ require 'rspec'
6
+ require 'strelka'
7
+ require 'strelka/httprequest/metadata'
8
+ require 'thingfish/processor/image'
9
+
10
+
11
+
12
+ describe Thingfish::Processor::Image, :db do
13
+
14
+ before( :all ) do
15
+ Strelka::HTTPRequest.class_eval { include Strelka::HTTPRequest::Metadata }
16
+ end
17
+
18
+
19
+ let( :processor ) { Thingfish::Processor.create(:image) }
20
+ let( :factory ) do
21
+ Mongrel2::RequestFactory.new(
22
+ :route => '/',
23
+ :headers => {:accept => '*/*'})
24
+ end
25
+
26
+
27
+ it "extracts image metadata from uploaded images" do
28
+ req = factory.post( '/tf', fixture_data('skunks.jpg'), 'Content-type' => 'image/jpeg' )
29
+
30
+ processor.process_request( req )
31
+
32
+ expect( req.metadata ).to include( "image:bounding_box" => "width=640, height=480, x=0, y=0" )
33
+ end
34
+
35
+
36
+ it "extracts exif metadata from uploaded images" do
37
+ req = factory.post( '/tf', fixture_data('skunks.jpg'), 'Content-type' => 'image/jpeg' )
38
+
39
+ processor.process_request( req )
40
+
41
+ expect( req.metadata ).to include( "exif:software" => "Pixelmator 3.1" )
42
+ end
43
+
44
+
45
+ it "creates a thumbnail of uploaded images" do
46
+ req = factory.post( '/tf', fixture_data('skunks.jpg'), 'Content-type' => 'image/jpeg' )
47
+
48
+ processor.process_request( req )
49
+
50
+ expect( req.related_resources.size ).to eq( 1 )
51
+ expect( req.related_resources.keys.first ).to be_a( StringIO )
52
+ expect( req.related_resources.values.first ).
53
+ to include( 'image:height' => 75, 'image:width' => 100 )
54
+ end
55
+
56
+
57
+ it "extracts IPTC metadata from uploaded images"
58
+
59
+ end
60
+
61
+ # vim: set nosta noet ts=4 sw=4 ft=rspec:
metadata ADDED
@@ -0,0 +1,248 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: thingfish-processor-image
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.pre20161114102625
5
+ platform: ruby
6
+ authors:
7
+ - Michael Granger
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIEbDCCAtSgAwIBAgIBATANBgkqhkiG9w0BAQsFADA+MQwwCgYDVQQDDANnZWQx
14
+ GTAXBgoJkiaJk/IsZAEZFglGYWVyaWVNVUQxEzARBgoJkiaJk/IsZAEZFgNvcmcw
15
+ HhcNMTYwODIwMTgxNzQyWhcNMTcwODIwMTgxNzQyWjA+MQwwCgYDVQQDDANnZWQx
16
+ GTAXBgoJkiaJk/IsZAEZFglGYWVyaWVNVUQxEzARBgoJkiaJk/IsZAEZFgNvcmcw
17
+ ggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQC/JWGRHO+USzR97vXjkFgt
18
+ 83qeNf2KHkcvrRTSnR64i6um/ziin0I0oX23H7VYrDJC9A/uoUa5nGRJS5Zw/+wW
19
+ ENcvWVZS4iUzi4dsYJGY6yEOsXh2CcF46+QevV8iE+UmbkU75V7Dy1JCaUOyizEt
20
+ TH5UHsOtUU7k9TYARt/TgYZKuaoAMZZd5qyVqhF1vV+7/Qzmp89NGflXf2xYP26a
21
+ 4MAX2qqKX/FKXqmFO+AGsbwYTEds1mksBF3fGsFgsQWxftG8GfZQ9+Cyu2+l1eOw
22
+ cZ+lPcg834G9DrqW2zhqUoLr1MTly4pqxYGb7XoDhoR7dd1kFE2a067+DzWC/ADt
23
+ +QkcqWUm5oh1fN0eqr7NsZlVJDulFgdiiYPQiIN7UNsii4Wc9aZqBoGcYfBeQNPZ
24
+ soo/6za/bWajOKUmDhpqvaiRv9EDpVLzuj53uDoukMMwxCMfgb04+ckQ0t2G7wqc
25
+ /D+K9JW9DDs3Yjgv9k4h7YMhW5gftosd+NkNC/+Y2CkCAwEAAaN1MHMwCQYDVR0T
26
+ BAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFHKN/nkRusdqCJEuq3lgB3fJvyTg
27
+ MBwGA1UdEQQVMBOBEWdlZEBGYWVyaWVNVUQub3JnMBwGA1UdEgQVMBOBEWdlZEBG
28
+ YWVyaWVNVUQub3JnMA0GCSqGSIb3DQEBCwUAA4IBgQAPJzKiT0zBU7kpqe0aS2qb
29
+ FI0PJ4y5I8buU4IZGUD5NEt/N7pZNfOyBxkrZkXhS44Fp+xwBH5ebLbq/WY78Bqd
30
+ db0z6ZgW4LMYMpWFfbXsRbd9TU2f52L8oMAhxOvF7Of5qJMVWuFQ8FPagk2iHrdH
31
+ inYLQagqAF6goWTXgAJCdPd6SNeeSNqA6vlY7CV1Jh5kfNJJ6xu/CVij1GzCLu/5
32
+ DMOr26DBv+qLJRRC/2h34uX71q5QgeOyxvMg+7V3u/Q06DXyQ2VgeeqiwDFFpEH0
33
+ PFkdPO6ZqbTRcLfNH7mFgCBJjsfSjJrn0sPBlYyOXgCoByfZnZyrIMH/UY+lgQqS
34
+ 6Von1VDsfQm0eJh5zYZD64ZF86phSR7mUX3mXItwH04HrZwkWpvgd871DZVR3i1n
35
+ w8aNA5re5+Rt/Vvjxj5AcEnZnZiz5x959NaddQocX32Z1unHw44pzRNUur1GInfW
36
+ p4vpx2kUSFSAGjtCbDGTNV2AH8w9OU4xEmNz8c5lyoA=
37
+ -----END CERTIFICATE-----
38
+ date: 2016-11-14 00:00:00.000000000 Z
39
+ dependencies:
40
+ - !ruby/object:Gem::Dependency
41
+ name: mongrel2
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0.46'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0.46'
54
+ - !ruby/object:Gem::Dependency
55
+ name: thingfish
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.5'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.5'
68
+ - !ruby/object:Gem::Dependency
69
+ name: loggability
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '0.11'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '0.11'
82
+ - !ruby/object:Gem::Dependency
83
+ name: rmagick
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '2.16'
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '2.16'
96
+ - !ruby/object:Gem::Dependency
97
+ name: hoe-mercurial
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '1.4'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '1.4'
110
+ - !ruby/object:Gem::Dependency
111
+ name: hoe-deveiate
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '0.8'
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '0.8'
124
+ - !ruby/object:Gem::Dependency
125
+ name: hoe-highline
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '0.2'
131
+ type: :development
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '0.2'
138
+ - !ruby/object:Gem::Dependency
139
+ name: simplecov
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '0.7'
145
+ type: :development
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: '0.7'
152
+ - !ruby/object:Gem::Dependency
153
+ name: rdoc-generator-fivefish
154
+ requirement: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - "~>"
157
+ - !ruby/object:Gem::Version
158
+ version: '0.1'
159
+ type: :development
160
+ prerelease: false
161
+ version_requirements: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - "~>"
164
+ - !ruby/object:Gem::Version
165
+ version: '0.1'
166
+ - !ruby/object:Gem::Dependency
167
+ name: rdoc
168
+ requirement: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - "~>"
171
+ - !ruby/object:Gem::Version
172
+ version: '4.0'
173
+ type: :development
174
+ prerelease: false
175
+ version_requirements: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - "~>"
178
+ - !ruby/object:Gem::Version
179
+ version: '4.0'
180
+ - !ruby/object:Gem::Dependency
181
+ name: hoe
182
+ requirement: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - "~>"
185
+ - !ruby/object:Gem::Version
186
+ version: '3.15'
187
+ type: :development
188
+ prerelease: false
189
+ version_requirements: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - "~>"
192
+ - !ruby/object:Gem::Version
193
+ version: '3.15'
194
+ description: |-
195
+ This is a basic image-processor plugin for the Thingfish digital asset manager.
196
+ It extracts image-related metadata from uploaded media files, and generates one
197
+ or more thumbnail images as related resources.
198
+ email:
199
+ - ged@FaerieMUD.org
200
+ executables: []
201
+ extensions: []
202
+ extra_rdoc_files:
203
+ - History.md
204
+ - LICENSE.txt
205
+ - Manifest.txt
206
+ - README.md
207
+ files:
208
+ - ".document"
209
+ - ".editorconfig"
210
+ - ".rdoc_options"
211
+ - ".simplecov"
212
+ - ChangeLog
213
+ - History.md
214
+ - LICENSE.txt
215
+ - Manifest.txt
216
+ - README.md
217
+ - Rakefile
218
+ - lib/thingfish/processor/image.rb
219
+ - spec/data/skunks.jpg
220
+ - spec/spec_helper.rb
221
+ - spec/thingfish/processor/image_spec.rb
222
+ homepage: https://bitbucket.org/ged/thingfish-processor-image
223
+ licenses:
224
+ - BSD-3-Clause
225
+ metadata: {}
226
+ post_install_message:
227
+ rdoc_options:
228
+ - "--main"
229
+ - README.md
230
+ require_paths:
231
+ - lib
232
+ required_ruby_version: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - ">="
235
+ - !ruby/object:Gem::Version
236
+ version: 2.3.1
237
+ required_rubygems_version: !ruby/object:Gem::Requirement
238
+ requirements:
239
+ - - ">"
240
+ - !ruby/object:Gem::Version
241
+ version: 1.3.1
242
+ requirements: []
243
+ rubyforge_project:
244
+ rubygems_version: 2.5.1
245
+ signing_key:
246
+ specification_version: 4
247
+ summary: This is a basic image-processor plugin for the Thingfish digital asset manager
248
+ test_files: []