thumbkit 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ .DS_Store
2
+ *.gem
3
+ *.rbc
4
+ .bundle
5
+ .config
6
+ Gemfile.lock
7
+ coverage
8
+ doc/
9
+ pkg
10
+ rdoc
11
+ spec/reports
12
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in thumbkit.gemspec
4
+ gemspec
5
+
6
+
7
+ # Development tools
8
+ gem 'rspec'
9
+
10
+ gem 'guard'
11
+ gem 'guard-rspec'
12
+ gem 'guard-bundler'
13
+
14
+ gem 'mini_magick'
15
+ gem 'waveform', git: 'https://github.com/amiel/waveform', branch: 'thumbkit'
16
+ gem 'oily_png'
data/Guardfile ADDED
@@ -0,0 +1,16 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ notification :off
5
+
6
+ guard 'bundler' do
7
+ watch('Gemfile')
8
+ watch(/^.+\.gemspec/)
9
+ end
10
+
11
+ guard 'rspec', version: 2, cli: '--color -f doc', keep_failed: false do
12
+ watch(%r{^spec/.+_spec\.rb$})
13
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{ m[1] }_spec.rb" }
14
+ watch('spec/spec_helper.rb') { "spec" }
15
+ end
16
+
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Amiel Martin
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,246 @@
1
+ # Thumbkit
2
+
3
+ > it's like quicklook for carrierwave :)
4
+ >
5
+ > -- <cite>[Emmanuel Gomez][1]</cite>
6
+
7
+ [1]:https://github.com/emmanuel
8
+
9
+ Thumbkit makes thumbnails from a variety of media types.
10
+ Thumbkit is designed to work with carrierwave but does not require it.
11
+
12
+ ## Synopsis
13
+
14
+ ```ruby
15
+ Thumbkit.new('path/to/audio.mp3').write_thumbnail # => 'path/to/audio.png'
16
+ Thumbkit.new('path/to/text.txt').write_thumbnail # => 'path/to/text.png'
17
+ Thumbkit.new('path/to/image.jpg').write_thumbnail # => 'path/to/image.jpg'
18
+ ```
19
+
20
+ See [Usage](#usage) below for more examples
21
+
22
+ ## Installation
23
+
24
+ Add this line to your application's Gemfile:
25
+
26
+ gem 'thumbkit'
27
+ gem 'mini_magick' # For text or image thumbnails
28
+ gem 'waveform' # For audio thumbnails
29
+ gem 'oily_png' # Optional, for presumably faster audio thumbnails
30
+
31
+ And then execute:
32
+
33
+ $ bundle
34
+
35
+ Please see [Requirements](#requirements) for more information about each
36
+ thumbnail type.
37
+
38
+
39
+ ## Requirements
40
+
41
+ ### Image thumbnails
42
+
43
+ Thumbkit uses [MiniMagick](https://github.com/probablycorey/mini_magick) to
44
+ resize and crop images.
45
+
46
+ On OS X:
47
+
48
+ $ brew install imagemagick
49
+ $ gem install mini_magick
50
+
51
+ ### Text thumbnails
52
+
53
+ Thumbkit uses [MiniMagick](https://github.com/probablycorey/mini_magick) to
54
+ render text files.
55
+
56
+ On OS X:
57
+
58
+ $ brew install imagemagick
59
+ $ gem install mini_magick
60
+
61
+ ### HTML thumbnails
62
+
63
+ HTML thumbnails are not yet supported, but the plan is to use phantomjs to
64
+ render html files.
65
+
66
+ ### Audio thumbnails
67
+
68
+ Thumbkit uses the [waveform](https://github.com/benalavi/waveform) gem to render
69
+ audio files. [waveform](https://github.com/benalavi/waveform) depends on
70
+ libsndfile. **ffmpeg** is required in order to generate thumbnails from anything
71
+ other than .wav files.
72
+
73
+ See https://github.com/benalavi/waveform for more on requirements.
74
+
75
+ NOTE: As of 0.0.3 waveform fails on mono files (benalavi/waveform#4,
76
+ benalavi/waveform#5. I've forked and fixed (see benalavi/waveform#6). Until my
77
+ fix gets merged in you can use https://github.com/amiel/waveform/tree/thumbkit.
78
+ Like so:
79
+
80
+ ```ruby
81
+ gem 'thumbkit'
82
+ gem 'waveform', git: 'https://github.com/amiel/waveform', branch: 'thumbkit'
83
+ gem 'oily_png' # Optional, for presumably faster audio thumbnails
84
+ ```
85
+
86
+ ## Usage
87
+
88
+ Thumbkit takes a path to a file, and saves a thumbnail for that file regardless
89
+ of type. Certain types require different gems, but none are dependencies so
90
+ you'll have to install them yourself.
91
+
92
+ All settings can be set globally. These are the defaults:
93
+
94
+ ### Configuration
95
+
96
+ ```ruby
97
+ Thumbkit.defaults = {
98
+ width: 200, height: 200,
99
+ gravity: 'Center',
100
+ colors: { foreground: '#888888', background: '#eeeeee' },
101
+ font: {
102
+ family: 'Arial-Regular',
103
+ pointsize: '18',
104
+ direction: :auto,
105
+ },
106
+ }
107
+ ```
108
+
109
+ Setting `Thumbkit.defaults=` will deep merge. So setting one option is possible
110
+ with:
111
+
112
+ ```ruby
113
+ Thumbkit.defaults = { colors: { foreground: '#FF69B4' } } # HOT PINK
114
+ ```
115
+
116
+ #### Font options
117
+
118
+ The list of fonts available to imagemagick can be found with
119
+ `identify -list Font`
120
+
121
+ #### Gravity Options
122
+
123
+ A list of gravity options can be found with `identify -list Gravity`
124
+
125
+ See http://www.imagemagick.org/script/command-line-options.php#gravity for more
126
+ information.
127
+
128
+ ### Image thumbnails
129
+
130
+ ```ruby
131
+ Thumbkit.new('path/to/image.jpg').write_thumbnail # => 'path/to/image.jpg'
132
+ ```
133
+
134
+ Will write a 60x60 cropped image to `path/to/image.jpg`.
135
+
136
+ The format of the output file will depend on the extension of the output path
137
+ and defaults to the same as the input file.
138
+
139
+ ### Text thumbnails
140
+
141
+ ```ruby
142
+ text = Thumbkit.new('path/to/text_file.txt')
143
+
144
+ text.write_thumbnail(nil, {
145
+ width: 200, height: 200,
146
+ colors: { foreground: '#663854' },
147
+ font: { pointsize: '18' },
148
+ }) # => 'path/to/text_file.png'
149
+ ```
150
+
151
+ Will write a 200x200 cropped image to `path/to/text_file.png`.
152
+
153
+ The format of output will depend on the extension of the output path provided
154
+ but defaults to .png.
155
+
156
+ #### RTL support
157
+
158
+ ```ruby
159
+ text = Thumbkit.new('path/to/text_file.txt')
160
+ text.write_thumbnail(nil, font: { direction: 'right-to-left' }) # Force RTL
161
+ ```
162
+
163
+ `direction` options:
164
+
165
+ * `nil`: don't specify the option to imagemagick (OS default)
166
+ * `:auto`: try to detect. Currently, this switches to `'right-to-left'` if there
167
+ are *any* RTL characters in the input. This is the default.
168
+ * `'right-to-left'`, `'left-to-right'`: force LTR or RTL
169
+
170
+ ### Audio thumbnails
171
+
172
+ ```ruby
173
+ audio = Thumbkit.new('path/to/audio.mp3')
174
+ audio.write_thumbnail('path/to/ouput.png', {
175
+ colors: { foreground: '#ffffff', background: '#000000' },
176
+ }) # => 'path/to/output.png'
177
+ ```
178
+
179
+ Will write a 60x60 cropped image to `path/to/output.png`.
180
+
181
+ Note that while imagemagick supports most color specification formats, waveform
182
+ only takes 6 digit hex values. However, there is one special case for the symbol
183
+ :transparent.
184
+
185
+ Audio thumbnails only support PNG output. A png file will be created regardless
186
+ of the extension of the output file provided.
187
+
188
+ ### Composite thumbnails
189
+
190
+ NOT YET IMPLEMENTED
191
+
192
+ ```ruby
193
+ composite = Thumbkit.new(['path/to/audio.mp3', 'path/to/text_file.txt'])
194
+ composite.write_thumbnail('path/to/collection.png')
195
+ ```
196
+
197
+ ### CarrierWave usage
198
+
199
+ NOT YET IMPLEMENTED
200
+
201
+ ```ruby
202
+ class MyUploader < CarrierWave::Uploader::Base
203
+ include CarrierWave::Thumbkit
204
+
205
+ version :thumbnail do
206
+ process :thumbkit => [200, 200, { colors: { foreground: '#cccccc' } }]
207
+ end
208
+ end
209
+ ```
210
+
211
+ ## Other plans
212
+
213
+ * Accept a StringIO instead of a pathname
214
+ * Maybe use filemagic if available
215
+ * Processors:
216
+ * Composite
217
+ * HTML
218
+ * PDF
219
+ * Video
220
+
221
+ ## Contributing
222
+
223
+ 1. Fork it
224
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
225
+ 3. Run the test suite to make sure all tests pass before you start (`guard`)
226
+ 4. Make your changes
227
+ 5. Run the test suite again to make sure you didn't break anything existing (`guard`)
228
+ 6. Commit your changes (`git commit -am 'Added some feature'`)
229
+ 7. Push to the branch (`git push origin my-new-feature`)
230
+ 8. Create new Pull Request
231
+
232
+ ## Testing
233
+
234
+ Tests run in guard. If you don't like guard, a pull request on `Rakefile` would
235
+ be welcome.
236
+
237
+ Output files are placed in `spec/tmp` which is created automatically before each
238
+ test run and deleted automatically afterward unless `spec/tmp/.keep` exists. If
239
+ you would like to inspect the generated output files, create a file at
240
+ `spec/tmp/.keep`:
241
+
242
+ $ mkdir spec/tmp; touch spec/tmp/.keep
243
+
244
+ Many of the tests just verify that an image was created of the right type and
245
+ size, but do not actually verify that they have the correct content so it is
246
+ good to inspect the generated files.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,15 @@
1
+ require 'delegate'
2
+
3
+ class Thumbkit::Options < DelegateClass(Hash)
4
+ @@merge_proc = proc { |key, left, right|
5
+ Hash === left && Hash === right ? left.merge(right, &@@merge_proc) : right
6
+ }
7
+
8
+ def merge(other)
9
+ Thumbkit::Options::new(__getobj__.merge(other, &@@merge_proc))
10
+ end
11
+
12
+ def +(other)
13
+ merge(other)
14
+ end
15
+ end
@@ -0,0 +1,28 @@
1
+ require 'waveform'
2
+
3
+ class Thumbkit::Processor::Audio < Thumbkit::Processor
4
+
5
+ def determine_outfile
6
+ self.class.force_extension(path, 'png')
7
+ end
8
+
9
+ def write
10
+ Waveform.new(path).generate(outfile, build_options)
11
+
12
+ outfile
13
+ end
14
+
15
+ private
16
+
17
+ def build_options
18
+ {
19
+ force: true,
20
+ method: :rms, # Hard-coded for now.
21
+ width: options[:width],
22
+ height: options[:height],
23
+ color: options[:colors][:foreground],
24
+ background_color: options[:colors][:background],
25
+ }
26
+ end
27
+
28
+ end
@@ -0,0 +1,47 @@
1
+ require 'mini_magick'
2
+
3
+ class Thumbkit::Processor::Image < Thumbkit::Processor
4
+
5
+ def determine_outfile
6
+ @path
7
+ end
8
+
9
+ def write
10
+ resize_to_fill
11
+
12
+ outfile
13
+ end
14
+
15
+
16
+
17
+ private
18
+
19
+ def type
20
+ File.extname(outfile)[1..-1]
21
+ end
22
+
23
+
24
+ # Copied and adjusted from CarrierWave
25
+ def resize_to_fill
26
+ image = ::MiniMagick::Image.open(path)
27
+
28
+ cols, rows = image[:dimensions]
29
+ image.format type
30
+ image.combine_options do |cmd|
31
+ if options[:width] != cols || options[:height] != rows
32
+ scale = [options[:width]/cols.to_f, options[:height]/rows.to_f].max
33
+ cols = (scale * (cols + 0.5)).round
34
+ rows = (scale * (rows + 0.5)).round
35
+ cmd.resize "#{ cols }x#{ rows }"
36
+ end
37
+ cmd.gravity options[:gravity].to_s
38
+ cmd.background options[:colors][:background].to_s
39
+
40
+ if cols != options[:width] || rows != options[:height]
41
+ cmd.extent "#{ options[:width] }x#{ options[:height] }"
42
+ end
43
+ end
44
+
45
+ image.write(outfile)
46
+ end
47
+ end
@@ -0,0 +1,99 @@
1
+ require 'mini_magick'
2
+
3
+ # NOTES: For now, use reverse markdown for html
4
+ # https://github.com/xijo/reverse_markdown
5
+ # https://github.com/cousine/downmark_it
6
+ class Thumbkit::Processor::Text < Thumbkit::Processor
7
+
8
+ def determine_outfile
9
+ self.class.force_extension(path, 'png')
10
+ end
11
+
12
+ def write
13
+ command = build_command
14
+ run(command)
15
+
16
+ outfile
17
+ end
18
+
19
+ private
20
+
21
+
22
+ def command_builder
23
+ MiniMagick::CommandBuilder.new('mogrify')
24
+ end
25
+
26
+ # Regexes copies from www.frequency-decoder.com/demo/detect-text-direction/
27
+ # and https://github.com/geeksoflondon/grid4rails/issues/120
28
+ #
29
+ # Currently, this detects the direction by checking if *any* character in the
30
+ # input is an RTL character.
31
+ # TODO: Maybe check for a percentage of RTL characters?
32
+ def direction
33
+ # For future reference
34
+ # Hebrew - U+05D0 to U+05EA, U+05F0 to U+05F2, U+05BE, U+05C0, U+05C3, U+05F3, U+05F4, U+05B0 to U+05C4, U+0591 to U+05AF.
35
+ # Arabic - U+0600 to U+06FF, U+0750 to U+077F, U+FB50 to U+FDFF, U+FE70 to U+FEFF, U+10E60 to U+10E7F.
36
+ # ltrChars = 'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF'+'\u2C00-\uFB1C\uFDFE-\uFE6F\uFEFD-\uFFFF',
37
+ # rtlChars = '\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC',
38
+
39
+ dir = options[:font][:direction]
40
+ if dir == :auto
41
+ if /[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]/.match(IO.read(path))
42
+ 'right-to-left'
43
+ else
44
+ 'left-to-right'
45
+ end
46
+ else
47
+ dir
48
+ end
49
+ end
50
+
51
+ # Example generated command
52
+ # mogrify -density "288" -background "#ccc" -fill "#333" -pointsize "18" -antialias -font "Helvetica" -format png -trim -resize "%25" +repage -crop "200x200+10+10" +repage -write test.png test.txt
53
+ def build_command
54
+ command_builder.tap do |mogrify|
55
+ mogrify.density((72 * 4).to_s)
56
+ mogrify.background options[:colors][:background].to_s
57
+ mogrify.fill options[:colors][:foreground].to_s
58
+ mogrify.pointsize options[:font][:size].to_s
59
+ mogrify.antialias
60
+ mogrify.font options[:font][:family]
61
+ mogrify.direction direction if direction
62
+ mogrify.gravity options[:gravity].to_s
63
+ # While we convert to png, imagemagick will still output the format given
64
+ # by the extension. This allows users to costumize the output with the
65
+ # outfile extension.
66
+ mogrify << '-format png'
67
+ mogrify.trim
68
+ mogrify.resize '25%'
69
+ mogrify << '+repage'
70
+ mogrify.crop "#{ options[:width] }x#{ options[:height] }+0+0"
71
+ mogrify.extent "#{ options[:width] }x#{ options[:height] }"
72
+ mogrify << '+repage'
73
+ mogrify.write outfile
74
+ mogrify << "label:@#{path}"
75
+ end
76
+ end
77
+
78
+ # Mostly copied from MiniMagick
79
+ def run(command_builder)
80
+ command = command_builder.command
81
+
82
+ sub = Subexec.run(command, timeout: MiniMagick.timeout)
83
+
84
+ if sub.exitstatus != 0
85
+
86
+ # Raise the appropriate error
87
+ if sub.output =~ /no decode delegate/i || sub.output =~ /did not return an image/i
88
+ raise MiniMagick::Invalid, sub.output
89
+ else
90
+ # TODO: should we do something different if the command times out ...?
91
+ # its definitely better for logging.. otherwise we dont really know
92
+ raise MiniMagick::Error, "Command (#{command.inspect.gsub("\\", "")}) failed: #{{:status_code => sub.exitstatus, :output => sub.output}.inspect}"
93
+ end
94
+ else
95
+ sub.output
96
+ end
97
+ end
98
+
99
+ end
@@ -0,0 +1,45 @@
1
+ class Thumbkit::Processor
2
+ autoload :Text, 'thumbkit/processor/text'
3
+ autoload :Audio, 'thumbkit/processor/audio'
4
+ autoload :Image, 'thumbkit/processor/image'
5
+
6
+ def self.processors
7
+ @processors ||= {
8
+ 'png' => 'Image',
9
+ 'jpg' => 'Image',
10
+ 'txt' => 'Text',
11
+ 'md' => 'Text',
12
+ 'mp3' => 'Audio',
13
+ 'wav' => 'Audio',
14
+ 'm4a' => 'Audio',
15
+ }
16
+ end
17
+
18
+ def self.processor_for(extension)
19
+ if (class_name = self.processors[extension])
20
+ self.const_get(class_name)
21
+ end
22
+ end
23
+
24
+
25
+ def self.force_extension(filename, extension)
26
+ dir, fname = File.split(filename)
27
+ File.join(dir, "#{ File.basename(fname, '.*') }.#{ extension }")
28
+ end
29
+
30
+ attr_accessor :path, :outfile, :options
31
+ def initialize(path, outfile, options)
32
+ @path = path
33
+ @outfile = outfile || determine_outfile
34
+ @options = Thumbkit.defaults + options
35
+ end
36
+
37
+ def determine_outfile
38
+ raise NotImplementedError, 'Cannot determine an output file'
39
+ end
40
+
41
+ def write
42
+ raise NotImplementedError
43
+ end
44
+
45
+ end
@@ -0,0 +1,3 @@
1
+ class Thumbkit
2
+ VERSION = "0.0.2"
3
+ end
data/lib/thumbkit.rb ADDED
@@ -0,0 +1,42 @@
1
+ require "thumbkit/version"
2
+
3
+ class Thumbkit
4
+ autoload :Processor, 'thumbkit/processor'
5
+ autoload :Options, 'thumbkit/options'
6
+
7
+ attr_accessor :path, :filename, :type
8
+
9
+ def self.defaults
10
+ @defaults ||= Thumbkit::Options.new({
11
+ width: 200,
12
+ height: 200,
13
+ # Try `identify -list Gravity` for a list of available options
14
+ gravity: 'Center',
15
+ colors: {
16
+ # Colors must be 6-digit hex
17
+ background: '#eeeeee',
18
+ foreground: '#888888',
19
+ },
20
+ font: {
21
+ # Try `identify -list Font` for available font options
22
+ family: 'Arial-Regular',
23
+ size: '18', # In points
24
+ direction: :auto, # nil, :auto, 'right-to-left', or 'left-to-right'
25
+ },
26
+ })
27
+ end
28
+
29
+ def initialize(path)
30
+ @path = File.expand_path(path)
31
+ @filename = File.basename(@path)
32
+ @type = File.extname(filename)[1..-1]
33
+ end
34
+
35
+ def processor
36
+ @processor ||= Thumbkit::Processor.processor_for(type)
37
+ end
38
+
39
+ def write_thumbnail(outfile = nil, options = {})
40
+ processor.new(path, outfile, options).write
41
+ end
42
+ end
@@ -0,0 +1,7 @@
1
+ أَبْجَدِيَّة عَرَبِيَّة‎
2
+
3
+ ا ب ت ث ج ح
4
+ خ د ذ ر ز س
5
+ ش ص ض ط ظ ع
6
+ غ ف ق ك ل
7
+ م ن ه و ي
@@ -0,0 +1,14 @@
1
+ Capital letters
2
+ Α Β Γ Δ Ε Ζ Η Θ Ι Κ Λ Μ Ν Ξ Ο Π Ρ Σ Τ Υ Φ Χ Ψ Ω
3
+ Capital letters with tonos
4
+ Ά Έ Ή Ί Ό Ύ Ώ
5
+ Capital letters with dialytika
6
+ Ϊ Ϋ
7
+ Small letters
8
+ α β γ δ ε ζ η θ ι κ λ μ ν ξ ο π ρ σς τ υ φ χ ψ ω
9
+ Small letters with tonos
10
+ ά έ ή ί ό ύ ώ
11
+ Small letters with dialytika
12
+ ϊ ϋ
13
+ Small letters with dialytika and tonos
14
+ ΐ ΰ
@@ -0,0 +1,17 @@
1
+ Hebrew
2
+
3
+ Hebrew alphabet
4
+ ’ b g d h w z H T y kk l mm nn s ‘ pp cc q r š ś t
5
+ א ב ג ד ה ו ז ח ט י כך ל מם נן ס ע פף צץ ק ר שׁ שׂ ת
6
+ Letters with dagesh or mapiq
7
+ ’ b g d h w z T y kk l m n s pp c q r š ś t
8
+ אּ בּ גּ דּ הּ וּ זּ טּ יּ כּךּ לּ מּ נּ סּ פּףּ צּ קּ רּ שּׁ שּׂ תּ
9
+ Yiddish digraphs
10
+ ww wy yy
11
+ װ ױ ײ
12
+ Letters with rafeh
13
+ v x f
14
+ בֿ כֿ פֿ
15
+ Vowels with points
16
+ a å o u i ai
17
+ אַ אָ וֹ וּ יִ ײַ
Binary file
Binary file
@@ -0,0 +1,15 @@
1
+ An Example Text File
2
+
3
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
4
+ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
5
+ quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
6
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
7
+ cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
8
+ proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
9
+
10
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
11
+ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
12
+ quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
13
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
14
+ cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
15
+ proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
@@ -0,0 +1,51 @@
1
+ require 'thumbkit'
2
+ require 'fileutils'
3
+
4
+ module Helpers
5
+
6
+ def path_to_fixture(fixture_name)
7
+ Pathname.new("spec/fixtures").join(fixture_name).expand_path
8
+ end
9
+
10
+ def tmp_path
11
+ Pathname.new("spec/tmp")
12
+ end
13
+
14
+ def path_for_output(outfile = '')
15
+ tmp_path.join(outfile).expand_path
16
+ end
17
+ end
18
+
19
+ module ImageMacros
20
+ def its_size_should_be(size)
21
+ it "its size should be #{ size }" do
22
+ r = `identify -ping -quiet -format "%wx%h" "#{subject}"`.chomp
23
+ r.should == size
24
+ end
25
+ end
26
+
27
+ def its_mimetype_should_be(mime)
28
+ it "its mime type should be #{ mime }" do
29
+ m = `file --mime-type -n -N "#{ subject }"|cut -d' ' -f2`.chomp
30
+ m.should == mime
31
+ end
32
+ end
33
+ end
34
+
35
+ RSpec.configure do |config|
36
+ config.extend ImageMacros
37
+ config.include Helpers
38
+
39
+ def mkdir_safe(dir)
40
+ Dir.mkdir(dir)
41
+ rescue Errno::EEXIST
42
+ # Great, it's already there.
43
+ end
44
+
45
+ config.around(:each) do |example|
46
+ mkdir_safe tmp_path.to_s
47
+ example.run
48
+ FileUtils.rm_rf(tmp_path.to_s) unless File.exist?(tmp_path.join('.keep').to_s)
49
+ end
50
+ end
51
+
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe Thumbkit::Options do
4
+ describe '#merge' do
5
+ it "does a deep merge" do
6
+ h1 = { x: { y: [4, 5, 6], z: [7, 8, 9], a: 'abar' } }
7
+ h2 = { x: { y: [1, 2, 3], z: 'xyz', b: { c: 'd' } } }
8
+ defaults = Thumbkit::Options.new(h1)
9
+ defaults.merge(h2).should == {
10
+ x: {
11
+ y: [1, 2, 3],
12
+ z: 'xyz',
13
+ a: 'abar',
14
+ b: { c: 'd' },
15
+ }
16
+ }
17
+ end
18
+
19
+ it "does an even deeper merge" do
20
+ # NOTE: These options resemble, but don't actually reflect the available
21
+ # options for Thumbkit.
22
+ h1 = { colors: { background: 'blue' }, font: { family: { name: 'Helvetica', weight: 'bold' } } }
23
+ h2 = { colors: { foreground: 'black' }, font: { family: { weight: 'light', transform: 'small-caps' } } }
24
+ defaults = Thumbkit::Options.new(h1)
25
+ defaults.merge(h2).should == {
26
+ colors: {
27
+ background: 'blue',
28
+ foreground: 'black',
29
+ },
30
+ font: {
31
+ family: {
32
+ name: 'Helvetica',
33
+ weight: 'light',
34
+ transform: 'small-caps',
35
+ }
36
+ }
37
+ }
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe Thumbkit::Processor::Audio do
4
+
5
+ let(:fixture) { '16Hz-20kHz-Exp-1f-5sec.mp3' }
6
+ let(:path) { File.expand_path(path_to_fixture(fixture)) }
7
+ let(:outfile) { nil }
8
+ let(:options) { {} }
9
+ let(:processor) { Thumbkit::Processor::Audio.new(path, outfile, options) }
10
+ subject { processor }
11
+
12
+ its(:path) { should == path }
13
+
14
+ describe '#auto_outflie' do
15
+ subject { processor.outfile }
16
+ context 'when nothing specified' do
17
+ it { should == File.expand_path(path_to_fixture('16Hz-20kHz-Exp-1f-5sec.png')) }
18
+ end
19
+
20
+ # context 'with an extensionless outfile' do
21
+ # let(:outfile) { 'foo.' }
22
+ # it { should == 'foo.png' }
23
+ # end
24
+
25
+ # Not sure what to do in this case. Waveform just outputs a png file no
26
+ # matter what...
27
+ #
28
+ # context 'with a jpg specified' do
29
+ # let(:outfile) { path_for_output('audio-test.jpg').to_s }
30
+ # before { processor.write }
31
+ # it { should == path_for_output('audio_test.jpg').to_s }
32
+ # end
33
+ end
34
+
35
+ describe '#write' do
36
+ let(:outfile) { path_for_output('audio-test.png').to_s }
37
+ subject { processor.write }
38
+
39
+ it 'returns the path of the outfile' do
40
+ subject.should == outfile
41
+ end
42
+
43
+ it 'writes a file' do
44
+ File.should exist(subject)
45
+ end
46
+
47
+ its_size_should_be('200x200')
48
+
49
+ context 'with size settings' do
50
+ let(:outfile) { path_for_output('audio-test-300x250.png').to_s }
51
+ # Let's change a few settings for manual inspection
52
+ let(:options) { { width: 300, height: 250, colors: { background: :transparent, foreground: '#ffeecc' } } }
53
+
54
+ its_size_should_be('300x250')
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ describe Thumbkit::Processor::Image do
4
+
5
+ let(:fixture) { 'png_file.png' }
6
+ let(:path) { File.expand_path(path_to_fixture(fixture)) }
7
+ let(:outfile) { nil }
8
+ let(:options) { {} }
9
+ let(:processor) { Thumbkit::Processor::Image.new(path, outfile, options) }
10
+ subject { processor }
11
+
12
+ its(:path) { should == path }
13
+
14
+ describe '#auto_outflie' do
15
+ subject { processor.outfile }
16
+ context 'when nothing specified' do
17
+ it { should == File.expand_path(path_to_fixture('png_file.png')) }
18
+
19
+ context 'when the fixture is a jpeg' do
20
+ let(:fixture) { 'jpg_file.jpg' }
21
+ it { should == File.expand_path(path_to_fixture('jpg_file.jpg')) }
22
+ end
23
+ end
24
+
25
+ # context 'with an extensionless outfile' do
26
+ # let(:outfile) { 'foo.' }
27
+ # it { should == 'foo.png' }
28
+ # end
29
+ end
30
+
31
+ describe '#write' do
32
+ let(:outfile) { path_for_output('png_file.png').to_s }
33
+ subject { processor.write }
34
+
35
+ it 'returns the path of the outfile' do
36
+ subject.should == outfile
37
+ end
38
+
39
+ it 'writes a file' do
40
+ File.should exist(subject)
41
+ end
42
+
43
+ its_size_should_be('200x200')
44
+ its_mimetype_should_be('image/png')
45
+
46
+ context 'with size settings' do
47
+ let(:outfile) { path_for_output('resize_test_300x250.png').to_s }
48
+ # Let's change a few settings for manual inspection
49
+ let(:options) { { width: 300, height: 250 } }
50
+
51
+ its_size_should_be('300x250')
52
+ its_mimetype_should_be('image/png')
53
+ end
54
+
55
+ context 'with size and gravity settings' do
56
+ let(:outfile) { path_for_output('resize_test_southwest_300x100.png').to_s }
57
+ # Let's change a few settings for manual inspection
58
+ let(:options) { { width: 300, height: 100, gravity: 'SouthWest' } }
59
+
60
+ its_size_should_be('300x100')
61
+ its_mimetype_should_be('image/png')
62
+ end
63
+
64
+
65
+ context 'with size settings larger than the image' do
66
+ let(:outfile) { path_for_output('resize_test_600x600.png').to_s }
67
+ # Let's change a few settings for manual inspection
68
+ let(:options) { { width: 600, height: 600 } }
69
+
70
+ its_size_should_be('600x600')
71
+ its_mimetype_should_be('image/png')
72
+ end
73
+
74
+ context 'with a jpg file' do
75
+ let(:outfile) { path_for_output('jpg_file.jpg').to_s }
76
+ it { should == path_for_output('jpg_file.jpg').to_s }
77
+ its_size_should_be('200x200')
78
+ its_mimetype_should_be('image/jpeg')
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,114 @@
1
+ require 'spec_helper'
2
+
3
+ describe Thumbkit::Processor::Text do
4
+
5
+ let(:fixture) { 'text_file.txt' }
6
+ let(:path) { File.expand_path(path_to_fixture(fixture)) }
7
+ let(:outfile) { nil }
8
+ let(:options) { {} }
9
+ let(:processor) { Thumbkit::Processor::Text.new(path, outfile, options) }
10
+ subject { processor }
11
+
12
+ its(:path) { should == path }
13
+
14
+
15
+ describe '#auto_outflie' do
16
+ subject { processor.outfile }
17
+ context 'when nothing specified' do
18
+ it { should == File.expand_path(path_to_fixture('text_file.png')) }
19
+ end
20
+
21
+ # context 'with an extensionless outfile' do
22
+ # let(:outfile) { 'foo.' }
23
+ # it { should == 'foo.png' }
24
+ # end
25
+ end
26
+
27
+ describe '#write' do
28
+ let(:outfile) { path_for_output('text-test.png').to_s }
29
+ subject { processor.write }
30
+
31
+ it 'returns the path of the outfile' do
32
+ subject.should == outfile
33
+ end
34
+
35
+ it 'returns the name of the outfile' do
36
+ File.basename(subject).should == 'text-test.png'
37
+ end
38
+
39
+ it 'writes a file' do
40
+ File.should exist(subject)
41
+ end
42
+
43
+ its_size_should_be('200x200')
44
+ its_mimetype_should_be('image/png')
45
+
46
+ context 'with size settings' do
47
+ let(:outfile) { path_for_output('text-test-300x250.png').to_s }
48
+ # Let's change a few settings for manual inspection
49
+ let(:options) { { width: 300, height: 250, colors: { background: :transparent, foreground: '#334455' } } }
50
+
51
+ its_size_should_be('300x250')
52
+ its_mimetype_should_be('image/png')
53
+ # Manually check the file to verify colors
54
+ end
55
+
56
+ context 'with gravity settings' do
57
+ let(:outfile) { path_for_output('text-test-northeast-150x250.png').to_s }
58
+ # Let's change a few settings for manual inspection
59
+ let(:options) { {
60
+ width: 150, height: 250, gravity: 'NorthEast',
61
+ colors: { background: :transparent, foreground: '#334455' }
62
+ } }
63
+
64
+ its_size_should_be('150x250')
65
+ its_mimetype_should_be('image/png')
66
+ # Manually check the file to verify colors and gravity
67
+ end
68
+
69
+ context 'with some greek letters' do
70
+ let(:fixture) { 'greek.txt' }
71
+ let(:outfile) { path_for_output('greek.png').to_s }
72
+ it('writes a file') { File.should exist(subject) }
73
+ it('autodetects left-to-right') { processor.__send__(:direction).should == 'left-to-right' }
74
+ its_size_should_be('200x200')
75
+ its_mimetype_should_be('image/png')
76
+ # Manually check the file to verify unicode stuff worked
77
+ end
78
+
79
+ context 'with an arabic file' do
80
+ let(:options) { { font: { direction: :auto } } }
81
+ let(:fixture) { 'arabic.txt' }
82
+ let(:outfile) { path_for_output('arabic.png').to_s }
83
+ it('writes a file') { File.should exist(subject) }
84
+ it('autodetects right-to-left') { processor.__send__(:direction).should == 'right-to-left' }
85
+ its_size_should_be('200x200')
86
+ its_mimetype_should_be('image/png')
87
+ # Manually check the file to verify unicode stuff and right-to-left worked
88
+ end
89
+
90
+ context 'with an hebrew file' do
91
+ let(:options) { { font: { direction: 'right-to-left', size: '12' }, width: 400 } }
92
+ let(:fixture) { 'hebrew.txt' }
93
+ let(:outfile) { path_for_output('hebrew.png').to_s }
94
+ it('writes a file') { File.should exist(subject) }
95
+ it('is right-to-left') { processor.__send__(:direction).should == 'right-to-left' }
96
+ its_size_should_be('400x200')
97
+ its_mimetype_should_be('image/png')
98
+ # Manually check the file to verify unicode stuff and right-to-left worked
99
+ end
100
+
101
+
102
+ context 'with an arabic file to output to jpg' do
103
+ let(:options) { { width: 600, height: 400 } }
104
+ let(:fixture) { 'arabic.txt' }
105
+ let(:outfile) { path_for_output('arabic.jpg').to_s }
106
+ it('writes a file') { File.should exist(subject) }
107
+ it('autodetects right-to-left') { processor.__send__(:direction).should == 'right-to-left' }
108
+ its_size_should_be('600x400')
109
+ its_mimetype_should_be('image/jpeg')
110
+ # Manually check the file to verify unicode stuff and right-to-left worked
111
+ end
112
+
113
+ end
114
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe Thumbkit::Processor do
4
+ describe '.processor_for' do
5
+ subject { Thumbkit::Processor.processor_for(extension) }
6
+ context 'for a .txt' do
7
+ let(:extension) { 'txt' }
8
+ it { should == Thumbkit::Processor::Text }
9
+ end
10
+
11
+ context 'for a .mp3' do
12
+ let(:extension) { 'mp3' }
13
+ it { should == Thumbkit::Processor::Audio }
14
+ end
15
+ end
16
+
17
+ describe '.force_extension' do
18
+ let(:extension) { 'png' }
19
+ subject { Thumbkit::Processor.force_extension path, extension }
20
+ context 'given an mp3 file' do
21
+ let(:path) { 'foo/bar/blah.mp3' }
22
+ it { should == 'foo/bar/blah.png' }
23
+ end
24
+ end
25
+
26
+ describe '#initialize' do
27
+ let(:options) { {} }
28
+ subject { Thumbkit::Processor.new('infile.png', 'outfile.png', options) }
29
+
30
+ context 'with no options' do
31
+ its(:options) { should == Thumbkit.defaults }
32
+ end
33
+
34
+ context 'with some overrides' do
35
+ let(:options) { { width: 250, height: 250 } }
36
+
37
+ it 'overrides the options' do
38
+ subject.options[:width].should == 250
39
+ subject.options[:height].should == 250
40
+ end
41
+
42
+ it 'but leaves others' do
43
+ subject.options[:colors].should == Thumbkit.defaults[:colors]
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe Thumbkit do
4
+ context 'a .txt file' do
5
+ subject { Thumbkit.new path_to_fixture('text_file.txt') }
6
+
7
+ its(:filename) { should == 'text_file.txt' }
8
+ its(:path) { should == File.expand_path(path_to_fixture('text_file.txt')) }
9
+ its(:processor) { should == Thumbkit::Processor::Text }
10
+ its(:type) { should == 'txt' }
11
+ end
12
+
13
+ context 'with a .mp3 file' do
14
+ subject { Thumbkit.new path_to_fixture('16Hz-20kHz-Exp-1f-5sec.mp3') }
15
+
16
+ its(:filename) { should == '16Hz-20kHz-Exp-1f-5sec.mp3' }
17
+ its(:path) { should == File.expand_path(path_to_fixture('16Hz-20kHz-Exp-1f-5sec.mp3')) }
18
+ its(:processor) { should == Thumbkit::Processor::Audio }
19
+ its(:type) { should == 'mp3' }
20
+ end
21
+ end
22
+
23
+
24
+ describe Thumbkit, '.defaults' do
25
+ subject { Thumbkit.defaults }
26
+
27
+ it 'returns defaults for colors' do
28
+ subject.should have_key(:colors)
29
+ end
30
+
31
+ it 'returns defaults for font' do
32
+ subject.should have_key(:font)
33
+ end
34
+ end
data/thumbkit.gemspec ADDED
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/thumbkit/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Amiel Martin"]
6
+ gem.email = ["amiel@carnesmedia.com"]
7
+ gem.description = %q{Thumbkit makes thumbnails!}
8
+ gem.summary = %q{Thumbkit makes thumbnails from a variety of media types. Planned: Audio, Text, Image, and Video.}
9
+ gem.homepage = "http://github.com/carnesmedia/thumbkit"
10
+
11
+ # TODO: Remove dependency on git
12
+ gem.files = `git ls-files`.split($\)
13
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
14
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
+ gem.name = "thumbkit"
16
+ gem.require_paths = ["lib"]
17
+ gem.version = Thumbkit::VERSION
18
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: thumbkit
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Amiel Martin
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-01 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Thumbkit makes thumbnails!
15
+ email:
16
+ - amiel@carnesmedia.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - Gemfile
23
+ - Guardfile
24
+ - LICENSE
25
+ - README.md
26
+ - Rakefile
27
+ - lib/thumbkit.rb
28
+ - lib/thumbkit/options.rb
29
+ - lib/thumbkit/processor.rb
30
+ - lib/thumbkit/processor/audio.rb
31
+ - lib/thumbkit/processor/image.rb
32
+ - lib/thumbkit/processor/text.rb
33
+ - lib/thumbkit/version.rb
34
+ - spec/fixtures/16Hz-20kHz-Exp-1f-5sec.mp3
35
+ - spec/fixtures/arabic.txt
36
+ - spec/fixtures/greek.txt
37
+ - spec/fixtures/hebrew.txt
38
+ - spec/fixtures/jpg_file.jpg
39
+ - spec/fixtures/png_file.png
40
+ - spec/fixtures/text_file.txt
41
+ - spec/spec_helper.rb
42
+ - spec/thumbkit/options_spec.rb
43
+ - spec/thumbkit/processor/audio_spec.rb
44
+ - spec/thumbkit/processor/image_spec.rb
45
+ - spec/thumbkit/processor/text_spec.rb
46
+ - spec/thumbkit/processor_spec.rb
47
+ - spec/thumbkit_spec.rb
48
+ - thumbkit.gemspec
49
+ homepage: http://github.com/carnesmedia/thumbkit
50
+ licenses: []
51
+ post_install_message:
52
+ rdoc_options: []
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ requirements: []
68
+ rubyforge_project:
69
+ rubygems_version: 1.8.15
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: ! 'Thumbkit makes thumbnails from a variety of media types. Planned: Audio,
73
+ Text, Image, and Video.'
74
+ test_files:
75
+ - spec/fixtures/16Hz-20kHz-Exp-1f-5sec.mp3
76
+ - spec/fixtures/arabic.txt
77
+ - spec/fixtures/greek.txt
78
+ - spec/fixtures/hebrew.txt
79
+ - spec/fixtures/jpg_file.jpg
80
+ - spec/fixtures/png_file.png
81
+ - spec/fixtures/text_file.txt
82
+ - spec/spec_helper.rb
83
+ - spec/thumbkit/options_spec.rb
84
+ - spec/thumbkit/processor/audio_spec.rb
85
+ - spec/thumbkit/processor/image_spec.rb
86
+ - spec/thumbkit/processor/text_spec.rb
87
+ - spec/thumbkit/processor_spec.rb
88
+ - spec/thumbkit_spec.rb
89
+ has_rdoc: