ruby-imagespec 0.3.1 → 0.4.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.
@@ -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