ruby-imagespec 0.1.0

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.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ # Copyright (c) 2008, Brandon Anderson (anderson.brandon@gmail.com)
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without modification,
5
+ # are permitted provided that the following conditions are met:
6
+ #
7
+ # Redistributions of source code must retain the above copyright notice,
8
+ # this list of conditions and the following disclaimer.
9
+ #
10
+ # Redistributions in binary form must reproduce the above copyright notice,
11
+ # this list of conditions and the following disclaimer in the documentation
12
+ # and/or other materials provided with the distribution.
13
+ #
14
+ # Neither the name of the original author nor the names of contributors
15
+ # may be used to endorse or promote products derived from this software
16
+ # without specific prior written permission.
17
+ #
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+ #
data/README ADDED
@@ -0,0 +1,30 @@
1
+ ImageSpec
2
+ =========
3
+
4
+ ImageSpec is a lightweight module designed to extract width/height dimensions from most standard image formats, as well as SWFs.
5
+
6
+ This is a work in progress. I intend on expanding this to include other details (EXIF, various metadata, etc.) as well.
7
+
8
+
9
+ Example
10
+ =======
11
+
12
+ # From a file in your file system
13
+ instance = ImageSpec.new('/path/to/your/file')
14
+
15
+ # From a URL
16
+ instance = ImageSpec.new('http://example.com/image.png')
17
+
18
+ # From an IO stream
19
+ file = File.new('/path/to/your/file', 'rb')
20
+ instance = ImageSpec.new(file)
21
+
22
+ instance.width
23
+ instance.height
24
+ instance.content_type
25
+
26
+ Pretty simple, huh?
27
+
28
+ Copyright (c) 2008 Brandon Anderson, released under the MIT license
29
+
30
+ Contributions by Michael Sheakoski and Mike Boone
data/Rakefile ADDED
@@ -0,0 +1,41 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the image_spec plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.pattern = 'test/**/*_test.rb'
12
+ t.verbose = true
13
+ end
14
+
15
+ desc 'Generate documentation for the image_spec plugin.'
16
+ Rake::RDocTask.new(:rdoc) do |rdoc|
17
+ rdoc.rdoc_dir = 'rdoc'
18
+ rdoc.title = 'ImageSpec'
19
+ rdoc.options << '--line-numbers' << '--inline-source'
20
+ rdoc.rdoc_files.include('README')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ end
23
+
24
+ begin
25
+ require 'jeweler'
26
+ Jeweler::Tasks.new do |gemspec|
27
+ gemspec.name = "ruby-imagespec"
28
+ gemspec.summary = "Image/Flash extract width/height dimensions extractor"
29
+ gemspec.description = "A lightweight module designed to extract the width/height dimensions of various image types. Also supports SWFs."
30
+ gemspec.email = "dimitrij@blacksquaremedia.com"
31
+ gemspec.homepage = "http://github.com/dim/ruby-imagespec"
32
+ gemspec.authors = [
33
+ "Brandon Anderson",
34
+ "Michael Sheakoski",
35
+ "Mike Boone",
36
+ "Dimitrij Denissenko"]
37
+ end
38
+ Jeweler::GemcutterTasks.new
39
+ rescue LoadError
40
+ puts "Jeweler not available. Install it with: gem install jeweler"
41
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), 'lib', 'image_spec')
data/lib/image_spec.rb ADDED
@@ -0,0 +1,30 @@
1
+ require 'open-uri'
2
+ require File.join(File.dirname(__FILE__), 'parser')
3
+
4
+ class ImageSpec
5
+
6
+ def initialize(file)
7
+ @attributes = Parser.parse(stream_for(file))
8
+
9
+ @attributes.each do |key, value|
10
+ instance_eval <<-RUBY, __FILE__, __LINE__ + 1
11
+ def #{key}
12
+ @attributes[:#{key}]
13
+ end
14
+ RUBY
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def stream_for(file)
21
+ if file.respond_to?(:read)
22
+ file
23
+ elsif file.is_a?(String)
24
+ open(file, 'rb')
25
+ else
26
+ raise "Unable to get stream for #{file.inspect}"
27
+ end
28
+ end
29
+
30
+ end
data/lib/parser.rb ADDED
@@ -0,0 +1,20 @@
1
+ Dir[File.join(File.dirname(__FILE__), 'parser/*.rb')].each { |parser| require parser }
2
+
3
+ class ImageSpec
4
+
5
+ module Parser
6
+
7
+ def self.formats
8
+ @@formats ||= constants.collect { |format| const_get(format) }
9
+ end
10
+
11
+ def self.parse(stream)
12
+ formats.each do |format|
13
+ return format.attributes(stream) if format.detected?(stream)
14
+ end
15
+ raise "#{stream.inspect} is not a supported image format. Sorry bub :("
16
+ end
17
+
18
+ end
19
+
20
+ end
data/lib/parser/gif.rb ADDED
@@ -0,0 +1,28 @@
1
+ class ImageSpec
2
+
3
+ module Parser
4
+
5
+ class GIF
6
+
7
+ CONTENT_TYPE = 'image/gif'
8
+
9
+ def self.attributes(stream)
10
+ width, height = dimensions(stream)
11
+ {:width => width, :height => height, :content_type => CONTENT_TYPE}
12
+ end
13
+
14
+ def self.detected?(stream)
15
+ stream.rewind
16
+ stream.read(4) == 'GIF8'
17
+ end
18
+
19
+ def self.dimensions(stream)
20
+ stream.seek(6)
21
+ stream.read(4).unpack('SS')
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,69 @@
1
+ class ImageSpec
2
+
3
+ module Parser
4
+
5
+ class JPEG
6
+
7
+ CONTENT_TYPE = 'image/jpeg'
8
+
9
+ def self.attributes(stream)
10
+ width, height = dimensions(stream)
11
+ {:width => width, :height => height, :content_type => CONTENT_TYPE}
12
+ end
13
+
14
+ def self.detected?(stream)
15
+ stream.rewind
16
+ case stream.read(10)
17
+ when /^\xff\xd8\xff\xe0\x00\x10JFIF/ then true
18
+ when /^\xff\xd8\xff\xe1(.*){2}Exif/ then true
19
+ else false
20
+ end
21
+ end
22
+
23
+ def self.dimensions(stream)
24
+ stream.rewind
25
+ raise 'malformed JPEG' unless stream.getc == 0xFF && stream.getc == 0xD8 # SOI
26
+
27
+ class << stream
28
+ def readint
29
+ (readchar << 8) + readchar
30
+ end
31
+
32
+ def readframe
33
+ read(readint - 2)
34
+ end
35
+
36
+ def readsof
37
+ [readint, readchar, readint, readint, readchar]
38
+ end
39
+
40
+ def next
41
+ c = readchar while c != 0xFF
42
+ c = readchar while c == 0xFF
43
+ c
44
+ end
45
+ end
46
+
47
+ while marker = stream.next
48
+ case marker
49
+ when 0xC0..0xC3, 0xC5..0xC7, 0xC9..0xCB, 0xCD..0xCF
50
+ length, bits, height, width, components = stream.readsof
51
+ raise 'malformed JPEG' unless length == 8 + components * 3
52
+ return [width, height]
53
+ when 0xD9, 0xDA
54
+ break
55
+ when 0xFE
56
+ @comment = stream.readframe
57
+ when 0xE1
58
+ stream.readframe
59
+ else
60
+ stream.readframe
61
+ end
62
+ end
63
+ end
64
+
65
+ end
66
+
67
+ end
68
+
69
+ end
data/lib/parser/png.rb ADDED
@@ -0,0 +1,28 @@
1
+ class ImageSpec
2
+
3
+ module Parser
4
+
5
+ class PNG
6
+
7
+ CONTENT_TYPE = 'image/png'
8
+
9
+ def self.attributes(stream)
10
+ width, height = dimensions(stream)
11
+ {:width => width, :height => height, :content_type => CONTENT_TYPE}
12
+ end
13
+
14
+ def self.detected?(stream)
15
+ stream.rewind
16
+ stream.read(4) == "\x89PNG"
17
+ end
18
+
19
+ def self.dimensions(stream)
20
+ stream.seek(0x10)
21
+ stream.read(8).unpack('NN')
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+
28
+ end
data/lib/parser/swf.rb ADDED
@@ -0,0 +1,77 @@
1
+ require 'zlib'
2
+
3
+ class ImageSpec
4
+
5
+ module Parser
6
+
7
+ class SWF
8
+
9
+ CONTENT_TYPE = 'application/x-shockwave-flash'
10
+
11
+ def self.attributes(stream)
12
+ width, height = dimensions(stream)
13
+ {:width => width, :height => height, :content_type => CONTENT_TYPE}
14
+ end
15
+
16
+ def self.detected?(stream)
17
+ stream.rewind
18
+ stream.read(3) =~ /(F|C)WS/ ? true : false
19
+ end
20
+
21
+ def self.dimensions(stream)
22
+ # Read the entire stream into memory because the
23
+ # dimensions aren't stored in a standard location
24
+ stream.rewind
25
+ contents = stream.read
26
+
27
+ # Our 'signature' is the first 3 bytes
28
+ # Either FWS or CWS. CWS indicates compression
29
+ signature = contents[0..2]
30
+
31
+ # Determine the length of the uncompressed stream
32
+ length = contents[4..7].unpack('V').join.to_i
33
+
34
+ # If we do, in fact, have compression
35
+ if signature == 'CWS'
36
+ # Decompress the body of the SWF
37
+ body = Zlib::Inflate.inflate( contents[8..length] )
38
+
39
+ # And reconstruct the stream contents to the first 8 bytes (header)
40
+ # Plus our decompressed body
41
+ contents = contents[0..7] + body
42
+ end
43
+
44
+ # Determine the nbits of our dimensions rectangle
45
+ nbits =contents.unpack('c'*contents.length)[8] >> 3
46
+
47
+ # Determine how many bits long this entire RECT structure is
48
+ rectbits = 5 + nbits * 4 # 5 bits for nbits, as well as nbits * number of fields (4)
49
+
50
+ # Determine how many bytes rectbits composes (ceil(rectbits/8))
51
+ rectbytes = (rectbits.to_f / 8).ceil
52
+
53
+ # Unpack the RECT structure from the stream in little-endian bit order, then join it into a string
54
+ rect = contents[8..(8 + rectbytes)].unpack("#{'B8' * rectbytes}").join()
55
+
56
+ # Read in nbits incremenets starting from 5
57
+ dimensions = Array.new
58
+ 4.times do |n|
59
+ s = 5 + (n * nbits) # Calculate our start index
60
+ e = s + (nbits - 1) # Calculate our end index
61
+ dimensions[n] = rect[s..e].to_i(2) # Read that range (binary) and convert it to an integer
62
+ end
63
+
64
+ # The values we have here are in "twips"
65
+ # 20 twips to a pixel (that's why SWFs are fuzzy sometimes!)
66
+ width = (dimensions[1] - dimensions[0]) / 20
67
+ height = (dimensions[3] - dimensions[2]) / 20
68
+
69
+ # If you can't figure this one out, you probably shouldn't have read this far
70
+ return [width, height]
71
+ end
72
+
73
+ end
74
+
75
+ end
76
+
77
+ end
@@ -0,0 +1,55 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{ruby-imagespec}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Brandon Anderson", "Michael Sheakoski", "Mike Boone", "Dimitrij Denissenko"]
12
+ s.date = %q{2010-08-16}
13
+ s.description = %q{A lightweight module designed to extract the width/height dimensions of various image types. Also supports SWFs.}
14
+ s.email = %q{dimitrij@blacksquaremedia.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README"
18
+ ]
19
+ s.files = [
20
+ ".gitignore",
21
+ "LICENSE",
22
+ "README",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "init.rb",
26
+ "lib/image_spec.rb",
27
+ "lib/parser.rb",
28
+ "lib/parser/gif.rb",
29
+ "lib/parser/jpeg.rb",
30
+ "lib/parser/png.rb",
31
+ "lib/parser/swf.rb",
32
+ "ruby-imagespec.gemspec",
33
+ "tasks/image_spec_tasks.rake",
34
+ "test/image_spec_test.rb"
35
+ ]
36
+ s.homepage = %q{http://github.com/dim/ruby-imagespec}
37
+ s.rdoc_options = ["--charset=UTF-8"]
38
+ s.require_paths = ["lib"]
39
+ s.rubygems_version = %q{1.3.7}
40
+ s.summary = %q{Image/Flash extract width/height dimensions extractor}
41
+ s.test_files = [
42
+ "test/image_spec_test.rb"
43
+ ]
44
+
45
+ if s.respond_to? :specification_version then
46
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
47
+ s.specification_version = 3
48
+
49
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
50
+ else
51
+ end
52
+ else
53
+ end
54
+ end
55
+
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :image_spec do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,8 @@
1
+ require 'test/unit'
2
+
3
+ class ImageSpecTest < Test::Unit::TestCase
4
+ # Replace this with your real tests.
5
+ def test_this_plugin
6
+ flunk
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-imagespec
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Brandon Anderson
14
+ - Michael Sheakoski
15
+ - Mike Boone
16
+ - Dimitrij Denissenko
17
+ autorequire:
18
+ bindir: bin
19
+ cert_chain: []
20
+
21
+ date: 2010-08-16 00:00:00 +01:00
22
+ default_executable:
23
+ dependencies: []
24
+
25
+ description: A lightweight module designed to extract the width/height dimensions of various image types. Also supports SWFs.
26
+ email: dimitrij@blacksquaremedia.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README
34
+ files:
35
+ - .gitignore
36
+ - LICENSE
37
+ - README
38
+ - Rakefile
39
+ - VERSION
40
+ - init.rb
41
+ - lib/image_spec.rb
42
+ - lib/parser.rb
43
+ - lib/parser/gif.rb
44
+ - lib/parser/jpeg.rb
45
+ - lib/parser/png.rb
46
+ - lib/parser/swf.rb
47
+ - ruby-imagespec.gemspec
48
+ - tasks/image_spec_tasks.rake
49
+ - test/image_spec_test.rb
50
+ has_rdoc: true
51
+ homepage: http://github.com/dim/ruby-imagespec
52
+ licenses: []
53
+
54
+ post_install_message:
55
+ rdoc_options:
56
+ - --charset=UTF-8
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ hash: 3
65
+ segments:
66
+ - 0
67
+ version: "0"
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ requirements: []
78
+
79
+ rubyforge_project:
80
+ rubygems_version: 1.3.7
81
+ signing_key:
82
+ specification_version: 3
83
+ summary: Image/Flash extract width/height dimensions extractor
84
+ test_files:
85
+ - test/image_spec_test.rb