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 +1 -0
- data/LICENSE +28 -0
- data/README +30 -0
- data/Rakefile +41 -0
- data/VERSION +1 -0
- data/init.rb +1 -0
- data/lib/image_spec.rb +30 -0
- data/lib/parser.rb +20 -0
- data/lib/parser/gif.rb +28 -0
- data/lib/parser/jpeg.rb +69 -0
- data/lib/parser/png.rb +28 -0
- data/lib/parser/swf.rb +77 -0
- data/ruby-imagespec.gemspec +55 -0
- data/tasks/image_spec_tasks.rake +4 -0
- data/test/image_spec_test.rb +8 -0
- metadata +85 -0
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
|
data/lib/parser/jpeg.rb
ADDED
@@ -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
|
+
|
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
|