giftrim 0.0.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.
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