ruby-imagespec 0.3.1 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5b959efb6a395dbe1ba3d6ffc3716ebb40489839
4
+ data.tar.gz: 6c556dded2c7788fe947855eec6afc736c5f4008
5
+ SHA512:
6
+ metadata.gz: 21ae732a659fb3b433d84e466a3ce83cc565798251216363384f7ff61a43b17e8968fa9ccc59c39bf731c609ca0a9398e706c3c83e468dec25e6bcf787221e1d
7
+ data.tar.gz: 2add58ef7912adefbc39edbf06dc6b4b17628af5d00548e141f8f7fbf8aa4e8b8c71fc484fb17934b83542411a6b941fadec71c129803c152e924e58be7e960c
@@ -1,7 +1,7 @@
1
1
  require 'open-uri'
2
- require File.join(File.dirname(__FILE__), 'parser')
3
2
 
4
3
  class ImageSpec
4
+ Error = Class.new(StandardError)
5
5
 
6
6
  def initialize(file)
7
7
  @attributes = Parser.parse(stream_for(file))
@@ -28,3 +28,7 @@ class ImageSpec
28
28
  end
29
29
 
30
30
  end
31
+
32
+ %w|parser|.each do |name|
33
+ require "image_spec/#{name}"
34
+ end
@@ -0,0 +1,18 @@
1
+ module ImageSpec::Parser
2
+
3
+ def self.formats
4
+ @@formats ||= constants.collect { |format| const_get(format) }
5
+ end
6
+
7
+ def self.parse(stream)
8
+ formats.each do |format|
9
+ return format.attributes(stream) if format.detected?(stream)
10
+ end
11
+ raise ImageSpec::Error, "#{stream.inspect} is not a supported image format. Sorry bub :("
12
+ end
13
+
14
+ end
15
+
16
+ %w|gif jpeg png swf bmp|.each do |name|
17
+ require "image_spec/parser/#{name}"
18
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: ascii-8bit
2
+ class ImageSpec::Parser::BMP
3
+ CONTENT_TYPE = 'image/bmp'
4
+
5
+ def self.attributes(stream)
6
+ width, height = dimensions(stream)
7
+ {:width => width, :height => height, :content_type => CONTENT_TYPE, :dimensions => [width, height], :file_size => size(stream)}
8
+ end
9
+
10
+ def self.detected?(stream)
11
+ stream.rewind
12
+ stream.read(2) == 'BM'
13
+ end
14
+
15
+ def self.dimensions(stream)
16
+ stream.seek(18)
17
+ stream.read(8).unpack('LL')
18
+ end
19
+
20
+ def self.size(stream)
21
+ stream.size
22
+ end
23
+
24
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: ascii-8bit
2
+ class ImageSpec::Parser::GIF
3
+ CONTENT_TYPE = 'image/gif'
4
+
5
+ def self.attributes(stream)
6
+ width, height = dimensions(stream)
7
+ {:width => width, :height => height, :content_type => CONTENT_TYPE, :dimensions => [width, height], :file_size => size(stream)}
8
+ end
9
+
10
+ def self.detected?(stream)
11
+ stream.rewind
12
+ stream.read(4) == 'GIF8'
13
+ end
14
+
15
+ def self.dimensions(stream)
16
+ stream.seek(6)
17
+ stream.read(4).unpack('SS')
18
+ end
19
+
20
+ def self.size(stream)
21
+ stream.size
22
+ end
23
+
24
+ end
@@ -0,0 +1,65 @@
1
+ # encoding: ascii-8bit
2
+ class ImageSpec::Parser::JPEG
3
+
4
+ CONTENT_TYPE = 'image/jpeg'
5
+ TYPE_JFIF = Regexp.new '^\xff\xd8\xff\xe0\x00\x10JFIF', nil, 'n'
6
+ TYPE_EXIF = Regexp.new '^\xff\xd8\xff\xe1(.*){2}Exif', nil, 'n'
7
+
8
+ def self.attributes(stream)
9
+ width, height = dimensions(stream)
10
+ {:width => width, :height => height, :content_type => CONTENT_TYPE, :dimensions => [width, height], :file_size => size(stream)}
11
+ end
12
+
13
+ def self.detected?(stream)
14
+ stream.rewind
15
+ case stream.read(10)
16
+ when TYPE_JFIF, TYPE_EXIF then true
17
+ else false
18
+ end
19
+ end
20
+
21
+ def self.dimensions(stream)
22
+ stream.rewind
23
+ raise ImageSpec::Error, 'malformed JPEG' unless stream.readbyte.chr == "\xFF" && stream.readbyte.chr == "\xD8" # SOI
24
+
25
+ class << stream
26
+ def readint
27
+ (readbyte.ord << 8) + readbyte.ord
28
+ end
29
+
30
+ def readframe
31
+ read(readint - 2)
32
+ end
33
+
34
+ def readsof
35
+ [readint, readbyte.chr, readint, readint, readbyte.chr]
36
+ end
37
+
38
+ def next
39
+ c = readbyte.chr while c != "\xFF"
40
+ c = readbyte.chr while c == "\xFF"
41
+ c
42
+ end
43
+ end
44
+
45
+ while marker = stream.next
46
+ case marker
47
+ when "\xC0".."\xC3", "\xC5".."\xC7", "\xC9".."\xCB", "\xCD".."\xCF"
48
+ length, bits, height, width, components = stream.readsof
49
+ raise ImageSpec::Error, 'malformed JPEG' unless length == 8 + components[0].ord * 3
50
+ return [width, height]
51
+ when "\xD9", "\xDA"
52
+ break
53
+ when "\xFE"
54
+ @comment = stream.readframe
55
+ else
56
+ stream.readframe
57
+ end
58
+ end
59
+ end
60
+
61
+ def self.size(stream)
62
+ stream.size
63
+ end
64
+
65
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: ascii-8bit
2
+ class ImageSpec::Parser::PNG
3
+ CONTENT_TYPE = 'image/png'
4
+
5
+ def self.attributes(stream)
6
+ width, height = dimensions(stream)
7
+ {:width => width, :height => height, :content_type => CONTENT_TYPE, :dimensions => [width, height], :file_size => size(stream)}
8
+ end
9
+
10
+ def self.detected?(stream)
11
+ stream.rewind
12
+ stream.read(4) == "\x89PNG"
13
+ end
14
+
15
+ def self.dimensions(stream)
16
+ stream.seek(0x10)
17
+ stream.read(8).unpack('NN')
18
+ end
19
+
20
+ def self.size(stream)
21
+ stream.size
22
+ end
23
+
24
+ end
@@ -0,0 +1,76 @@
1
+ # encoding: ascii-8bit
2
+ require 'zlib'
3
+
4
+ class ImageSpec::Parser::SWF
5
+ CONTENT_TYPE = 'application/x-shockwave-flash'
6
+
7
+ def self.attributes(stream)
8
+ width, height, version = dimensions(stream)
9
+ {:width => width, :height => height, :content_type => CONTENT_TYPE, :version => version, :dimensions => [width, height], :file_size => size(stream)}
10
+ end
11
+
12
+ def self.detected?(stream)
13
+ stream.rewind
14
+ stream.read(3) =~ /(F|C)WS/ ? true : false
15
+ end
16
+
17
+ def self.dimensions(stream)
18
+ # Read the entire stream into memory because the
19
+ # dimensions aren't stored in a standard location
20
+ stream.rewind
21
+ contents = stream.read.force_encoding("ASCII-8BIT")
22
+
23
+ # Our 'signature' is the first 3 bytes
24
+ # Either FWS or CWS. CWS indicates compression
25
+ signature = contents[0..2]
26
+
27
+ # SWF version
28
+ version = contents[3].unpack('C').join.to_i
29
+
30
+ # Determine the length of the uncompressed stream
31
+ length = contents[4..7].unpack('V').join.to_i
32
+
33
+ # If we do, in fact, have compression
34
+ if signature == 'CWS'
35
+ # Decompress the body of the SWF
36
+ body = Zlib::Inflate.inflate( contents[8..length] )
37
+
38
+ # And reconstruct the stream contents to the first 8 bytes (header)
39
+ # Plus our decompressed body
40
+ contents = contents[0..7] + body
41
+ end
42
+
43
+ # Determine the nbits of our dimensions rectangle
44
+ nbits = contents.unpack('C'*contents.length)[8] >> 3
45
+
46
+ # Determine how many bits long this entire RECT structure is
47
+ rectbits = 5 + nbits * 4 # 5 bits for nbits, as well as nbits * number of fields (4)
48
+
49
+ # Determine how many bytes rectbits composes (ceil(rectbits/8))
50
+ rectbytes = (rectbits.to_f / 8).ceil
51
+
52
+ # Unpack the RECT structure from the stream in little-endian bit order, then join it into a string
53
+ rect = contents[8..(8 + rectbytes)].unpack("#{'B8' * rectbytes}").join()
54
+
55
+ # Read in nbits incremenets starting from 5
56
+ dimensions = Array.new
57
+ 4.times do |n|
58
+ s = 5 + (n * nbits) # Calculate our start index
59
+ e = s + (nbits - 1) # Calculate our end index
60
+ dimensions[n] = rect[s..e].to_i(2) # Read that range (binary) and convert it to an integer
61
+ end
62
+
63
+ # The values we have here are in "twips"
64
+ # 20 twips to a pixel (that's why SWFs are fuzzy sometimes!)
65
+ width = (dimensions[1] - dimensions[0]) / 20
66
+ height = (dimensions[3] - dimensions[2]) / 20
67
+
68
+ # If you can't figure this one out, you probably shouldn't have read this far
69
+ return [width, height, version]
70
+ end
71
+
72
+ def self.size(stream)
73
+ stream.size
74
+ end
75
+
76
+ end
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-imagespec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
5
- prerelease:
4
+ version: 0.4.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Brandon Anderson
@@ -12,22 +11,48 @@ authors:
12
11
  autorequire:
13
12
  bindir: bin
14
13
  cert_chain: []
15
- date: 2012-08-22 00:00:00.000000000 Z
14
+ date: 2015-06-23 00:00:00.000000000 Z
16
15
  dependencies:
17
16
  - !ruby/object:Gem::Dependency
18
17
  name: rake
19
18
  requirement: !ruby/object:Gem::Requirement
20
- none: false
21
19
  requirements:
22
- - - ! '>='
20
+ - - ">="
23
21
  - !ruby/object:Gem::Version
24
22
  version: '0'
25
23
  type: :development
26
24
  prerelease: false
27
25
  version_requirements: !ruby/object:Gem::Requirement
28
- none: false
29
26
  requirements:
30
- - - ! '>='
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: bundler
32
+ requirement: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ type: :development
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ - !ruby/object:Gem::Dependency
45
+ name: test-unit
46
+ requirement: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ type: :development
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
31
56
  - !ruby/object:Gem::Version
32
57
  version: '0'
33
58
  description:
@@ -38,35 +63,35 @@ extra_rdoc_files: []
38
63
  files:
39
64
  - README
40
65
  - init.rb
41
- - lib/parser/gif.rb
42
- - lib/parser/png.rb
43
- - lib/parser/swf.rb
44
- - lib/parser/jpeg.rb
45
66
  - lib/image_spec.rb
46
- - lib/parser.rb
67
+ - lib/image_spec/parser.rb
68
+ - lib/image_spec/parser/bmp.rb
69
+ - lib/image_spec/parser/gif.rb
70
+ - lib/image_spec/parser/jpeg.rb
71
+ - lib/image_spec/parser/png.rb
72
+ - lib/image_spec/parser/swf.rb
47
73
  homepage: http://github.com/dim/ruby-imagespec
48
74
  licenses: []
75
+ metadata: {}
49
76
  post_install_message:
50
77
  rdoc_options: []
51
78
  require_paths:
52
79
  - lib
53
80
  required_ruby_version: !ruby/object:Gem::Requirement
54
- none: false
55
81
  requirements:
56
- - - ! '>='
82
+ - - ">="
57
83
  - !ruby/object:Gem::Version
58
84
  version: 1.9.1
59
85
  required_rubygems_version: !ruby/object:Gem::Requirement
60
- none: false
61
86
  requirements:
62
- - - ! '>='
87
+ - - ">="
63
88
  - !ruby/object:Gem::Version
64
89
  version: 1.3.6
65
90
  requirements: []
66
91
  rubyforge_project:
67
- rubygems_version: 1.8.24
92
+ rubygems_version: 2.4.7
68
93
  signing_key:
69
- specification_version: 3
94
+ specification_version: 4
70
95
  summary: Image/Flash extract width/height dimensions extractor
71
96
  test_files: []
72
97
  has_rdoc:
@@ -1,21 +0,0 @@
1
- Dir[File.join(File.dirname(__FILE__), 'parser/*.rb')].each { |parser| require parser }
2
-
3
- class ImageSpec
4
- Error = Class.new(StandardError)
5
-
6
- module Parser
7
-
8
- def self.formats
9
- @@formats ||= constants.collect { |format| const_get(format) }
10
- end
11
-
12
- def self.parse(stream)
13
- formats.each do |format|
14
- return format.attributes(stream) if format.detected?(stream)
15
- end
16
- raise Error, "#{stream.inspect} is not a supported image format. Sorry bub :("
17
- end
18
-
19
- end
20
-
21
- end
@@ -1,29 +0,0 @@
1
- # encoding: ascii-8bit
2
- class ImageSpec
3
- module Parser
4
- class GIF
5
-
6
- CONTENT_TYPE = 'image/gif'
7
-
8
- def self.attributes(stream)
9
- width, height = dimensions(stream)
10
- {:width => width, :height => height, :content_type => CONTENT_TYPE, :dimensions => [width, height], :file_size => size(stream)}
11
- end
12
-
13
- def self.detected?(stream)
14
- stream.rewind
15
- stream.read(4) == 'GIF8'
16
- end
17
-
18
- def self.dimensions(stream)
19
- stream.seek(6)
20
- stream.read(4).unpack('SS')
21
- end
22
-
23
- def self.size(stream)
24
- stream.size
25
- end
26
-
27
- end
28
- end
29
- end
@@ -1,69 +0,0 @@
1
- # encoding: ascii-8bit
2
- class ImageSpec
3
- module Parser
4
- class JPEG
5
-
6
- CONTENT_TYPE = 'image/jpeg'
7
- TYPE_JFIF = Regexp.new '^\xff\xd8\xff\xe0\x00\x10JFIF', nil, 'n'
8
- TYPE_EXIF = Regexp.new '^\xff\xd8\xff\xe1(.*){2}Exif', nil, 'n'
9
-
10
- def self.attributes(stream)
11
- width, height = dimensions(stream)
12
- {:width => width, :height => height, :content_type => CONTENT_TYPE, :dimensions => [width, height], :file_size => size(stream)}
13
- end
14
-
15
- def self.detected?(stream)
16
- stream.rewind
17
- case stream.read(10)
18
- when TYPE_JFIF, TYPE_EXIF then true
19
- else false
20
- end
21
- end
22
-
23
- def self.dimensions(stream)
24
- stream.rewind
25
- raise ImageSpec::Error, 'malformed JPEG' unless stream.readbyte.chr == "\xFF" && stream.readbyte.chr == "\xD8" # SOI
26
-
27
- class << stream
28
- def readint
29
- (readbyte.ord << 8) + readbyte.ord
30
- end
31
-
32
- def readframe
33
- read(readint - 2)
34
- end
35
-
36
- def readsof
37
- [readint, readbyte.chr, readint, readint, readbyte.chr]
38
- end
39
-
40
- def next
41
- c = readbyte.chr while c != "\xFF"
42
- c = readbyte.chr while c == "\xFF"
43
- c
44
- end
45
- end
46
-
47
- while marker = stream.next
48
- case marker
49
- when "\xC0".."\xC3", "\xC5".."\xC7", "\xC9".."\xCB", "\xCD".."\xCF"
50
- length, bits, height, width, components = stream.readsof
51
- raise ImageSpec::Error, 'malformed JPEG' unless length == 8 + components[0].ord * 3
52
- return [width, height]
53
- when "\xD9", "\xDA"
54
- break
55
- when "\xFE"
56
- @comment = stream.readframe
57
- else
58
- stream.readframe
59
- end
60
- end
61
- end
62
-
63
- def self.size(stream)
64
- stream.size
65
- end
66
-
67
- end
68
- end
69
- end
@@ -1,29 +0,0 @@
1
- # encoding: ascii-8bit
2
- class ImageSpec
3
- module Parser
4
- class PNG
5
-
6
- CONTENT_TYPE = 'image/png'
7
-
8
- def self.attributes(stream)
9
- width, height = dimensions(stream)
10
- {:width => width, :height => height, :content_type => CONTENT_TYPE, :dimensions => [width, height], :file_size => size(stream)}
11
- end
12
-
13
- def self.detected?(stream)
14
- stream.rewind
15
- stream.read(4) == "\x89PNG"
16
- end
17
-
18
- def self.dimensions(stream)
19
- stream.seek(0x10)
20
- stream.read(8).unpack('NN')
21
- end
22
-
23
- def self.size(stream)
24
- stream.size
25
- end
26
-
27
- end
28
- end
29
- end
@@ -1,78 +0,0 @@
1
- # encoding: ascii-8bit
2
- require 'zlib'
3
-
4
- class ImageSpec
5
- module Parser
6
- class SWF
7
-
8
- CONTENT_TYPE = 'application/x-shockwave-flash'
9
-
10
- def self.attributes(stream)
11
- width, height = dimensions(stream)
12
- {:width => width, :height => height, :content_type => CONTENT_TYPE, :dimensions => [width, height], :file_size => size(stream)}
13
- end
14
-
15
- def self.detected?(stream)
16
- stream.rewind
17
- stream.read(3) =~ /(F|C)WS/ ? true : false
18
- end
19
-
20
- def self.dimensions(stream)
21
- # Read the entire stream into memory because the
22
- # dimensions aren't stored in a standard location
23
- stream.rewind
24
- contents = stream.read.force_encoding("ASCII-8BIT")
25
-
26
- # Our 'signature' is the first 3 bytes
27
- # Either FWS or CWS. CWS indicates compression
28
- signature = contents[0..2]
29
-
30
- # Determine the length of the uncompressed stream
31
- length = contents[4..7].unpack('V').join.to_i
32
-
33
- # If we do, in fact, have compression
34
- if signature == 'CWS'
35
- # Decompress the body of the SWF
36
- body = Zlib::Inflate.inflate( contents[8..length] )
37
-
38
- # And reconstruct the stream contents to the first 8 bytes (header)
39
- # Plus our decompressed body
40
- contents = contents[0..7] + body
41
- end
42
-
43
- # Determine the nbits of our dimensions rectangle
44
- nbits =contents.unpack('c'*contents.length)[8] >> 3
45
-
46
- # Determine how many bits long this entire RECT structure is
47
- rectbits = 5 + nbits * 4 # 5 bits for nbits, as well as nbits * number of fields (4)
48
-
49
- # Determine how many bytes rectbits composes (ceil(rectbits/8))
50
- rectbytes = (rectbits.to_f / 8).ceil
51
-
52
- # Unpack the RECT structure from the stream in little-endian bit order, then join it into a string
53
- rect = contents[8..(8 + rectbytes)].unpack("#{'B8' * rectbytes}").join()
54
-
55
- # Read in nbits incremenets starting from 5
56
- dimensions = Array.new
57
- 4.times do |n|
58
- s = 5 + (n * nbits) # Calculate our start index
59
- e = s + (nbits - 1) # Calculate our end index
60
- dimensions[n] = rect[s..e].to_i(2) # Read that range (binary) and convert it to an integer
61
- end
62
-
63
- # The values we have here are in "twips"
64
- # 20 twips to a pixel (that's why SWFs are fuzzy sometimes!)
65
- width = (dimensions[1] - dimensions[0]) / 20
66
- height = (dimensions[3] - dimensions[2]) / 20
67
-
68
- # If you can't figure this one out, you probably shouldn't have read this far
69
- return [width, height]
70
- end
71
-
72
- def self.size(stream)
73
- stream.size
74
- end
75
-
76
- end
77
- end
78
- end