giftrim 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YzFkZDExNDVjYTA0YTFlYzYxZGNmZGJiODIwYWRjYjBkYTJhMjcwMg==
5
+ data.tar.gz: !binary |-
6
+ YzNmYTA0NGU5ZTVkNTY1N2ZjYmVjY2IwZWI5NjQ4NThhYTU1MWRjMQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ NWYwNzAyYjc1ZWEyZTcyYTNmMDZkM2Q0ZTlkYzcwYTNkNGM3NGYyMGMwNDQx
10
+ ZDk1ODE2NjZkZDY4MzQxM2JmYmE1MmQ4MWU2ZjY5N2NlZTEwNTk4NWZjYjc5
11
+ ZjVmMTk4MTY4MjRkOTg2MjY4NzY5Mjk4YTQ4NGMzYjIwNDA4M2I=
12
+ data.tar.gz: !binary |-
13
+ N2EyMTRhZmU4YTQxOTU4M2QyZWI4MDk2YzgwZTIzZWU0MDkyZGM1ZWNkY2Qw
14
+ MDJkNjA1ZjllYzljMTkyYTgwZmZjZTVjNjFkMzk1MWI1MjI1Y2I1OWUzNmQ0
15
+ NTYwMjBkZDQ1YzMyMDQ4YjBkMmYzNTMwYmJiYjJjMDFmM2U3M2I=
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ test/gifs/*.gif
19
+ test/gifs/output/*.gif
20
+ test/gifs/error/*.gif
21
+ coverage
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in giftrim.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Leo Lou
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.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2005-2013 Corey Johnson probablycorey@gmail.com
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/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # Giftrim
2
+
3
+ Provide convenient methods for GIF optimizations using gifsicle
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem "giftrim"
10
+
11
+ ## Usage
12
+
13
+ ### Resize to 300x300, and trim to 10 frames
14
+ ```
15
+ image = Giftrim::Image.open "input.gif"
16
+ image.trim
17
+ image.write "output.gif"
18
+ ```
19
+
20
+ ## Contributing
21
+
22
+ 1. Fork it
23
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
24
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
25
+ 4. Push to the branch (`git push origin my-new-feature`)
26
+ 5. Create new Pull Request
27
+
28
+ ## License
29
+
30
+ See LICENSE.txt and LICENSE_MiniMagick.txt
31
+
32
+ The Testing GIF was retrieved from
33
+ http://commons.wikimedia.org/wiki/File:Lightnings_sequence_2_animation.gif
34
+ licensed under [Creative Commons Attribution-Share Alike 2.5 Generic](http://creativecommons.org/licenses/by-sa/2.5/deed.en) license
35
+
36
+ ## Alternatives
37
+
38
+ The first version was at the branch *minimagick*. It adds frames trimming
39
+ methods to MiniMagick::Image. However, from my tests mogrify is not accepting
40
+ `-delete ranges`, such as `-delete 1-2,4,6`.
41
+
42
+ This version uses gifsicle instead of ImageMagick which allows selecting frames
43
+ to be included.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/giftrim.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'giftrim/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "giftrim"
8
+ spec.version = Giftrim::VERSION
9
+ spec.authors = ["Leo Lou"]
10
+ spec.email = ["louyuhong@gmail.com"]
11
+ spec.description = "GIF optimizations using gifsicle"
12
+ spec.summary = "GIF optimizations using gifsicle "
13
+ spec.homepage = "https://github.com/l4u/giftrim"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency("simplecov", "~> 0.7")
24
+ spec.add_development_dependency("simplecov-gem-adapter", "~> 1.0.1")
25
+ spec.add_runtime_dependency("subexec", ["~> 0.2.2"])
26
+
27
+
28
+ end
@@ -0,0 +1,7 @@
1
+ # From http://stackoverflow.com/questions/11903839/algorithm-to-spread-selection-over-a-fixed-size-array
2
+ class Array
3
+ def spread(n)
4
+ step = self.length.to_f / n
5
+ array = (0..(n - 1)).to_a.collect{|i| self[(i * step)]}.uniq
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module Giftrim
2
+ VERSION = "0.0.1"
3
+ end
data/lib/giftrim.rb ADDED
@@ -0,0 +1,190 @@
1
+ require 'tempfile'
2
+ require 'subexec'
3
+ require 'stringio'
4
+ require 'pathname'
5
+ require_relative "giftrim/version"
6
+ require_relative "core_ext/array"
7
+ module Giftrim
8
+ class << self
9
+ attr_accessor :timeout
10
+
11
+ def frame_number_wanted total_number, target_number
12
+ (0..(total_number-2)).to_a.spread(target_number)
13
+ end
14
+ end
15
+
16
+ class Image
17
+ # @return [String] The location of the current working file
18
+ attr_accessor :path
19
+
20
+ class << self
21
+
22
+ # Modified from MiniMagick, see License_MiniMagick
23
+ # This is the primary loading method used by all of the other class methods.
24
+ #
25
+ # Use this to pass in a stream object. Must respond to Object#read(size) or be a binary string object (BLOBBBB)
26
+ #
27
+ # As a change from the old API, please try and use IOStream objects. They are much, much better and more efficient!
28
+ #
29
+ # Probably easier to use the #open method if you want to open a file or a URL.
30
+ #
31
+ # @param stream [IOStream, String] Some kind of stream object that needs to be read or is a binary String blob!
32
+ # @param ext [String] A manual extension to use for reading the file. Not required, but if you are having issues, give this a try.
33
+ # @return [Image]
34
+ def read(stream, ext = nil)
35
+ if stream.is_a?(String)
36
+ stream = StringIO.new(stream)
37
+ elsif stream.is_a?(StringIO)
38
+ # Do nothing, we want a StringIO-object
39
+ elsif stream.respond_to? :path
40
+ if File.respond_to?(:binread)
41
+ stream = StringIO.new File.binread(stream.path.to_s)
42
+ else
43
+ stream = StringIO.new File.open(stream.path.to_s,"rb") { |f| f.read }
44
+ end
45
+ end
46
+
47
+ create(ext) do |f|
48
+ while chunk = stream.read(8192)
49
+ f.write(chunk)
50
+ end
51
+ end
52
+ end
53
+
54
+ # Modified from MiniMagick, see License_MiniMagick
55
+ # Opens a specific image file either on the local file system or at a URI.
56
+ #
57
+ # Use this if you don't want to overwrite the image file.
58
+ #
59
+ # Extension is either guessed from the path or you can specify it as a second parameter.
60
+ #
61
+ # If you pass in what looks like a URL, we require 'open-uri' before opening it.
62
+ #
63
+ # @param file_or_url [String] Either a local file path or a URL that open-uri can read
64
+ # @param ext [String] Specify the extension you want to read it as
65
+ # @return [Image] The loaded image
66
+ def open(file_or_url, ext = nil)
67
+ file_or_url = file_or_url.to_s # Force it to be a String... hell or highwater
68
+ if file_or_url.include?("://")
69
+ require 'open-uri'
70
+ ext ||= File.extname(URI.parse(file_or_url).path)
71
+ self.read(Kernel::open(file_or_url), ext)
72
+ else
73
+ ext ||= File.extname(file_or_url)
74
+ File.open(file_or_url, "rb") do |f|
75
+ self.read(f, ext)
76
+ end
77
+ end
78
+ end
79
+
80
+ # Modified from MiniMagick, see License_MiniMagick
81
+ # Used to create a new Image object data-copy. Not used to "paint" or that kind of thing.
82
+ #
83
+ # Takes an extension in a block and can be used to build a new Image object. Used
84
+ # by both #open and #read to create a new object! Ensures we have a good tempfile!
85
+ #
86
+ # @param ext [String] Specify the extension you want to read it as
87
+ # @param validate [Boolean] If false, skips validation of the created image. Defaults to true.
88
+ # @yield [IOStream] You can #write bits to this object to create the new Image
89
+ # @return [Image] The created image
90
+ def create(ext = nil, validate = true, &block)
91
+ begin
92
+ tempfile = Tempfile.new(['giftrim_', ext.to_s.downcase])
93
+ tempfile.binmode
94
+ block.call(tempfile)
95
+ tempfile.close
96
+
97
+ image = self.new(tempfile.path, tempfile)
98
+
99
+ if validate and !image.valid?
100
+ raise Giftrim::Invalid
101
+ end
102
+ return image
103
+ ensure
104
+ tempfile.close if tempfile
105
+ end
106
+ end
107
+
108
+ end
109
+
110
+ # Modified from MiniMagick, see License_MiniMagick
111
+ # Create a new MiniMagick::Image object
112
+ #
113
+ # _DANGER_: The file location passed in here is the *working copy*. That is, it gets *modified*.
114
+ # you can either copy it yourself or use the MiniMagick::Image.open(path) method which creates a
115
+ # temporary file for you and protects your original!
116
+ #
117
+ # @param input_path [String] The location of an image file
118
+ # @todo Allow this to accept a block that can pass off to Image#combine_options
119
+ def initialize(input_path, tempfile = nil)
120
+ @path = input_path
121
+ @tempfile = tempfile # ensures that the tempfile will stick around until this image is garbage collected.
122
+ end
123
+
124
+ # @return [Boolean]
125
+ def valid?
126
+ # TODO
127
+ true
128
+ end
129
+
130
+ def run_command command
131
+ sub = Subexec.run(command, :timeout => Giftrim.timeout)
132
+ if sub.exitstatus == 0
133
+ sub.output
134
+ else
135
+ raise Error, "Command (#{command.inspect.gsub("\\", "")}) failed: #{{:status_code => sub.exitstatus, :output => sub.output}.inspect}"
136
+ end
137
+ end
138
+
139
+ # Modified from MiniMagick, see License_MiniMagick
140
+ # Writes the temporary file out to either a file location (by passing in a String) or by
141
+ # passing in a Stream that you can #write(chunk) to repeatedly
142
+ #
143
+ # @param output_to [IOStream, String] Some kind of stream object that needs to be read or a file path as a String
144
+ # @return [IOStream, Boolean] If you pass in a file location [String] then you get a success boolean. If its a stream, you get it back.
145
+ # Writes the temporary image that we are using for processing to the output path
146
+ def write(output_to)
147
+ if output_to.kind_of?(String) || !output_to.respond_to?(:write)
148
+ FileUtils.copy_file @path, output_to
149
+ run_command "identify #{output_to}" # Verify that we have a good image
150
+ else # stream
151
+ File.open(@path, "rb") do |f|
152
+ f.binmode
153
+ while chunk = f.read(8192)
154
+ output_to.write(chunk)
155
+ end
156
+ end
157
+ output_to
158
+ end
159
+ end
160
+
161
+ def number_of_frames
162
+ command = "gifsicle --info #{@path}"
163
+ output = run_command command
164
+ if output
165
+ first_line = output.lines.first
166
+ number_of_frames = first_line.scan(/\d+[ \t]*images/).first.to_i
167
+ end
168
+ number_of_frames
169
+ end
170
+
171
+ def trim
172
+ # --batch to modify the GIF file in place (not working for frames)
173
+ frames = Giftrim::frame_number_wanted self.number_of_frames, 10
174
+ frames_formatted = frames.map{|frame| "\"##{frame}\""}.join " "
175
+
176
+ @outfile = Tempfile.new('giftrim_')
177
+ @outfile.binmode
178
+ @outfile.close
179
+
180
+ command = "gifsicle --resize-fit '300x300' #{@path} #{frames_formatted} > #{@outfile.path}"
181
+ output = run_command command
182
+ if output
183
+ @tempfile = @outfile
184
+ @path = @tempfile.path
185
+ @outfile = nil
186
+ end
187
+ output
188
+ end
189
+ end
190
+ end
data/test/gifs/0.gif ADDED
Binary file
@@ -0,0 +1,40 @@
1
+ require_relative "test_helper"
2
+ require File.expand_path('../../lib/giftrim.rb', __FILE__)
3
+
4
+ describe Giftrim do
5
+ it "must return frames wanted" do
6
+ frames = Giftrim::frame_number_wanted 30, 20
7
+ frames.length.must_equal 20
8
+ frames.must_equal [0, 1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 15, 17, 18, 20, 21, 23, 24, 26, 27]
9
+
10
+ target_number = 20
11
+ (20..40).to_a.each do |i|
12
+ frames = Giftrim::frame_number_wanted i, target_number
13
+ frames.length.must_be :<=, target_number
14
+ end
15
+ end
16
+
17
+ describe "when number of frames is smaller than the target number" do
18
+ it "must return frames to be removed" do
19
+ frames = Giftrim::frame_number_wanted 10, 20
20
+ frames.must_equal [0, 1, 2, 3, 4, 5, 6, 7, 8]
21
+ end
22
+ end
23
+
24
+ it "must get the number of frames" do
25
+ image = Giftrim::Image.open "test/gifs/0.gif"
26
+ image.number_of_frames.must_equal 10
27
+ end
28
+
29
+ it "should trim a gif" do
30
+ Giftrim.timeout = 10
31
+
32
+ Dir.glob("test/gifs/*.gif").each do |file|
33
+ image = Giftrim::Image.open file
34
+ image.trim
35
+ image.number_of_frames.must_be :<=, 10
36
+ image.number_of_frames.must_be :>=, 1
37
+ image.write "test/gifs/output/#{File.basename(file)}"
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,5 @@
1
+ require 'simplecov'
2
+ require 'simplecov-gem-adapter'
3
+ SimpleCov.start 'gem'
4
+ require 'minitest/autorun'
5
+ require 'minitest/pride'
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: giftrim
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Leo Lou
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-04-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: simplecov
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '0.7'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '0.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: simplecov-gem-adapter
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.0.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 1.0.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: subexec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 0.2.2
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 0.2.2
83
+ description: GIF optimizations using gifsicle
84
+ email:
85
+ - louyuhong@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - .gitignore
91
+ - Gemfile
92
+ - LICENSE.txt
93
+ - LICENSE_MiniMagick.txt
94
+ - README.md
95
+ - Rakefile
96
+ - giftrim.gemspec
97
+ - lib/core_ext/array.rb
98
+ - lib/giftrim.rb
99
+ - lib/giftrim/version.rb
100
+ - test/gifs/0.gif
101
+ - test/giftrim_test.rb
102
+ - test/test_helper.rb
103
+ homepage: https://github.com/l4u/giftrim
104
+ licenses:
105
+ - MIT
106
+ metadata: {}
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ! '>='
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ! '>='
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubyforge_project:
123
+ rubygems_version: 2.0.3
124
+ signing_key:
125
+ specification_version: 4
126
+ summary: GIF optimizations using gifsicle
127
+ test_files:
128
+ - test/gifs/0.gif
129
+ - test/giftrim_test.rb
130
+ - test/test_helper.rb