pnm 0.2.1 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 309f7c8934e624de40581059644cbda2d9ae575e
4
- data.tar.gz: 36cb4969d02048d04064962091b2df86f8a24491
3
+ metadata.gz: ef19db0d86c0c55bca6a6b6c3f9a5d4eaaac62f6
4
+ data.tar.gz: ffa665ee3688f90f201045230818b97dd5c11b22
5
5
  SHA512:
6
- metadata.gz: 1d30ee950e419b1d83165f3e000ccb0769b6f9a50a223f94f6d50af048c8c8fcce9f3cb7799c0c7b74bcafddd1accbeae2fd78e6acef3b844ae1ec03b8208935
7
- data.tar.gz: 561aaf230c4b41044bcaeb3ee62204963b032e9cbad3ea96c39e17510cd2608ef43e3334e4e6d9aba7b425f2eb1e65f80a73eb97e4030d2771428e50c672d812
6
+ metadata.gz: b8ff711e119c0950b4d249c9171735b0baf8e5c9401e10e8160e047def08df4d53f1db203be41a7220bd5e6a09945e56e308141fa23877b059b4242097916be7
7
+ data.tar.gz: 18d891b4969653b32f4d7d5c253a673479c14fcc3b53ea918b282528737a108714463538150552886d39ee61ae777f99c9950a23333869564262c26ca3e43212
data/README.md CHANGED
@@ -80,10 +80,11 @@ Requirements
80
80
 
81
81
  - PNM has been tested with
82
82
 
83
+ - Ruby 2.1.0,
83
84
  - Ruby 2.0.0 on Linux and on Windows,
84
85
  - Ruby 1.9.3,
85
- - JRuby 1.7.9,
86
- - Rubinius 2.2.1.
86
+ - JRuby 1.7.10,
87
+ - Rubinius 2.2.4.
87
88
 
88
89
  Documentation
89
90
  -------------
@@ -98,7 +99,7 @@ Report bugs on the PNM home page: <https://github.com/stomar/pnm/>
98
99
  License
99
100
  -------
100
101
 
101
- Copyright &copy; 2013 Marcus Stollsteimer
102
+ Copyright &copy; 2013-2014 Marcus Stollsteimer
102
103
 
103
104
  `PNM` is free software: you can redistribute it and/or modify
104
105
  it under the terms of the GNU General Public License version 3 or later (GPLv3+),
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
1
  # rakefile for the PNM library.
2
2
  #
3
- # Copyright (C) 2013 Marcus Stollsteimer
3
+ # Copyright (C) 2013-2014 Marcus Stollsteimer
4
4
 
5
5
  require 'rake/testtask'
6
6
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  # bm_converter.rb: Benchmarks for the PNM library.
3
3
  #
4
- # Copyright (C) 2013 Marcus Stollsteimer
4
+ # Copyright (C) 2013-2014 Marcus Stollsteimer
5
5
 
6
6
  require 'benchmark'
7
7
  require_relative '../lib/pnm'
data/lib/pnm.rb CHANGED
@@ -6,6 +6,7 @@ require_relative 'pnm/version'
6
6
  require_relative 'pnm/image'
7
7
  require_relative 'pnm/parser'
8
8
  require_relative 'pnm/converter'
9
+ require_relative 'pnm/exceptions'
9
10
 
10
11
 
11
12
  # PNM is a pure Ruby library for creating, reading,
@@ -68,7 +69,7 @@ require_relative 'pnm/converter'
68
69
  #
69
70
  # == Author
70
71
  #
71
- # Copyright (C) 2013 Marcus Stollsteimer
72
+ # Copyright (C) 2013-2014 Marcus Stollsteimer
72
73
  #
73
74
  # License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
74
75
  #
@@ -94,7 +95,7 @@ module PNM
94
95
  TAGLINE = 'create/read/write PNM image files (PBM, PGM, PPM)' # :nodoc:
95
96
 
96
97
  COPYRIGHT = <<-copyright.gsub(/^ +/, '') # :nodoc:
97
- Copyright (C) 2012-2013 Marcus Stollsteimer.
98
+ Copyright (C) 2013-2014 Marcus Stollsteimer.
98
99
  License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
99
100
  This is free software: you are free to change and redistribute it.
100
101
  There is NO WARRANTY, to the extent permitted by law.
@@ -104,12 +105,13 @@ module PNM
104
105
  #
105
106
  # Returns a PNM::Image object.
106
107
  def self.read(file)
107
- raw_data = nil
108
108
  if file.kind_of?(String)
109
109
  raw_data = File.binread(file)
110
- else
110
+ elsif file.respond_to?(:binmode)
111
111
  file.binmode
112
112
  raw_data = file.read
113
+ else
114
+ raise PNM::ArgumentError, "wrong argument type"
113
115
  end
114
116
 
115
117
  content = Parser.parse(raw_data)
@@ -135,9 +137,9 @@ module PNM
135
137
  encoding = :binary
136
138
  end
137
139
 
138
- width = content[:width].to_i
139
- height = content[:height].to_i
140
- maxgray = content[:maxgray].to_i
140
+ width = content[:width]
141
+ height = content[:height]
142
+ maxgray = content[:maxgray]
141
143
  pixels = if encoding == :ascii
142
144
  Converter.ascii2array(type, width, height, content[:data])
143
145
  else
@@ -5,7 +5,7 @@ module PNM
5
5
 
6
6
  # Returns the number of bytes needed for one row of pixels
7
7
  # (in binary encoding).
8
- def self.byte_width(type, width)
8
+ def self.byte_width(type, width) # :nodoc:
9
9
  case type
10
10
  when :pbm
11
11
  (width - 1) / 8 + 1
@@ -24,7 +24,10 @@ module PNM
24
24
  #
25
25
  # Returns a two-dimensional array of bilevel, gray, or RGB values.
26
26
  def self.ascii2array(type, width, height, data)
27
- values = data.gsub(/\A[ \t\r\n]+/, '').split(/[ \t\r\n]+/).map {|value| value.to_i }
27
+ values_per_row = type == :ppm ? 3 * width : width
28
+
29
+ values = convert_to_integers(data)
30
+ assert_data_size(values.size, values_per_row * height)
28
31
 
29
32
  case type
30
33
  when :pbm, :pgm
@@ -50,9 +53,7 @@ module PNM
50
53
  data.slice!(-1)
51
54
  end
52
55
 
53
- if data.size != bytes_per_row * height
54
- raise 'data size does not match expected size'
55
- end
56
+ assert_data_size(data.size, bytes_per_row * height)
56
57
 
57
58
  case type
58
59
  when :pbm
@@ -101,5 +102,23 @@ module PNM
101
102
 
102
103
  data_string
103
104
  end
105
+
106
+ def self.convert_to_integers(data) # :nodoc:
107
+ data.gsub(/\A[ \t\r\n]+/, '').split(/[ \t\r\n]+/).map do |value|
108
+ Integer(value)
109
+ end
110
+ rescue ::ArgumentError => e
111
+ if e.message.start_with?('invalid value for Integer')
112
+ raise PNM::DataError, "invalid pixel value: Integer expected"
113
+ else
114
+ raise
115
+ end
116
+ end
117
+
118
+ def self.assert_data_size(actual, expected) # :nodoc:
119
+ unless actual == expected
120
+ raise PNM::DataSizeError, 'data size does not match expected size'
121
+ end
122
+ end
104
123
  end
105
124
  end
@@ -0,0 +1,10 @@
1
+ module PNM
2
+
3
+ # Base class for all PNM exceptions.
4
+ class Error < StandardError; end
5
+
6
+ class ArgumentError < Error; end
7
+ class ParserError < Error; end
8
+ class DataSizeError < Error; end
9
+ class DataError < Error; end
10
+ end
@@ -1,6 +1,11 @@
1
1
  module PNM
2
2
 
3
- # Class for +PBM+, +PGM+, and +PPM+ images. See PNM module for examples.
3
+ # Class for +PBM+, +PGM+, and +PPM+ images.
4
+ #
5
+ # Images can be created from pixel values, see ::new,
6
+ # or read from a file or I/O stream, see PNM.read.
7
+ #
8
+ # See PNM module for examples.
4
9
  class Image
5
10
 
6
11
  # The type of the image. See ::new for details.
@@ -20,11 +25,13 @@ module PNM
20
25
  # See ::new for details.
21
26
  attr_reader :pixels
22
27
 
23
- # An optional multiline comment string.
28
+ # An optional multiline comment string (or +nil+).
24
29
  attr_reader :comment
25
30
 
26
31
  # Creates an image from a two-dimensional array of bilevel,
27
32
  # gray, or RGB values.
33
+ # The image type is guessed from the provided pixel data,
34
+ # unless it is explicitly set with the +type+ option.
28
35
  #
29
36
  # +pixels+:: The pixel data, given as a two-dimensional array of
30
37
  #
@@ -33,38 +40,47 @@ module PNM
33
40
  # * for PPM: an array of 3 values between 0 and +maxgray+,
34
41
  # corresponding to red, green, and blue (RGB).
35
42
  #
36
- # PPM also accepts an array of bilevel or gray values.
37
- #
38
43
  # A value of 0 means that the color is turned off.
39
44
  #
40
45
  # Optional settings that can be specified in the +options+ hash:
41
46
  #
42
47
  # +type+:: The type of the image (+:pbm+, +:pgm+, or +:ppm+).
43
- # By default, the type is guessed from the provided
44
- # pixel data, unless this option is used.
48
+ # By explicitly setting +type+, PGM images can be
49
+ # created from bilevel pixel data, and PPM images can be
50
+ # created from bilevel or gray pixel data.
45
51
  # +maxgray+:: The maximum gray or color value.
46
52
  # For PGM and PPM, +maxgray+ must be less or equal 255
47
53
  # (the default value).
48
54
  # For PBM pixel data, setting +maxgray+ implies a conversion
49
55
  # to +:pgm+. If +type+ is explicitly set to +:pbm+,
50
56
  # the +maxgray+ setting is disregarded.
51
- # +comment+:: A multiline comment string (default: empty string).
57
+ # +comment+:: A multiline comment string.
52
58
  def initialize(pixels, options = {})
53
59
  @type = options[:type]
54
- @width = pixels.first.size
55
- @height = pixels.size
56
60
  @maxgray = options[:maxgray]
57
- @comment = (options[:comment] || '').chomp
61
+ @comment = options[:comment]
58
62
  @pixels = pixels.dup
59
63
 
64
+ assert_valid_type if @type
65
+ assert_valid_maxgray if @maxgray
66
+ assert_valid_comment if @comment
67
+ assert_valid_array
68
+
69
+ @width = pixels.first.size
70
+ @height = pixels.size
71
+
60
72
  @type ||= detect_type(@pixels, @maxgray)
61
73
 
74
+ assert_matching_type_and_data
75
+
62
76
  if @type == :pbm
63
77
  @maxgray = 1
64
78
  else
65
79
  @maxgray ||= 255
66
80
  end
67
81
 
82
+ assert_valid_pixel_values
83
+
68
84
  if type == :ppm && !pixels.first.first.kind_of?(Array)
69
85
  @pixels.map! {|row| row.map {|pixel| gray_to_rgb(pixel) } }
70
86
  end
@@ -113,17 +129,89 @@ module PNM
113
129
  def detect_type(pixels, maxgray) # :nodoc:
114
130
  if pixels.first.first.kind_of?(Array)
115
131
  :ppm
116
- elsif pixels.flatten.max <= 1
117
- maxgray ? :pgm : :pbm
118
- else
132
+ elsif maxgray || pixels.flatten.max > 1
119
133
  :pgm
134
+ else
135
+ :pbm
136
+ end
137
+ end
138
+
139
+ def assert_valid_array # :nodoc:
140
+ msg = "invalid pixel data: Array expected"
141
+ raise PNM::ArgumentError, msg unless Array === pixels
142
+
143
+ msg = "invalid pixel array"
144
+ raise PNM::DataError, msg unless Array === pixels.first
145
+
146
+ width = pixels.first.size
147
+ is_color = (Array === pixels.first.first)
148
+
149
+ pixels.each do |row|
150
+ raise PNM::DataError, msg unless Array === row && row.size == width
151
+
152
+ if is_color
153
+ row.each {|pixel| assert_valid_color_pixel(pixel) }
154
+ else
155
+ row.each {|pixel| assert_valid_pixel(pixel) }
156
+ end
157
+ end
158
+ end
159
+
160
+ def assert_valid_pixel(pixel) # :nodoc:
161
+ unless Fixnum === pixel
162
+ msg = "invalid pixel value: Fixnum expected - #{pixel.inspect}"
163
+ raise PNM::DataError, msg
164
+ end
165
+ end
166
+
167
+ def assert_valid_color_pixel(pixel) # :nodoc:
168
+ unless Array === pixel && pixel.map(&:class) == [Fixnum, Fixnum, Fixnum]
169
+ msg = "invalid pixel value: "
170
+ msg << "Array of 3 Fixnums expected - #{pixel.inspect}"
171
+
172
+ raise PNM::DataError, msg
173
+ end
174
+ end
175
+
176
+ def assert_valid_type # :nodoc:
177
+ unless [:pbm, :pgm, :ppm].include?(type)
178
+ msg = "invalid image type - #{type.inspect}"
179
+ raise PNM::ArgumentError, msg
180
+ end
181
+ end
182
+
183
+ def assert_matching_type_and_data # :nodoc:
184
+ if Array === pixels.first.first && [:pbm, :pgm].include?(type)
185
+ msg = "specified type does not match data - #{type.inspect}"
186
+ raise PNM::DataError, msg
187
+ end
188
+ end
189
+
190
+ def assert_valid_maxgray # :nodoc:
191
+ unless Fixnum === maxgray && maxgray > 0 && maxgray <= 255
192
+ raise PNM::ArgumentError, "invalid maxgray value - #{maxgray.inspect}"
193
+ end
194
+ end
195
+
196
+ def assert_valid_comment # :nodoc:
197
+ unless String === comment
198
+ raise PNM::ArgumentError, "invalid comment value - #{comment.inspect}"
199
+ end
200
+ end
201
+
202
+ def assert_valid_pixel_values # :nodoc:
203
+ unless pixels.flatten.max <= maxgray
204
+ raise PNM::DataError, "invalid data: value(s) greater than maxgray"
205
+ end
206
+ unless pixels.flatten.min >= 0
207
+ raise PNM::DataError, "invalid data: value(s) less than zero"
120
208
  end
121
209
  end
122
210
 
123
211
  def header(encoding) # :nodoc:
124
212
  header = "#{PNM.magic_number[type][encoding]}\n"
125
- if comment
126
- comment.split("\n").each {|line| header << "# #{line}\n" }
213
+ comment_lines.each do |line|
214
+ header << (line.empty? ? "#\n" : "# #{line}\n")
127
215
  end
128
216
  header << "#{width} #{height}\n"
129
217
  header << "#{maxgray}\n" unless type == :pbm
@@ -131,6 +219,14 @@ module PNM
131
219
  header
132
220
  end
133
221
 
222
+ def comment_lines # :nodoc:
223
+ return [] unless comment
224
+ return [''] if comment.empty?
225
+
226
+ keep_trailing_null_fields = -1 # magic value for split limit
227
+ comment.split(/\n/, keep_trailing_null_fields)
228
+ end
229
+
134
230
  def to_ascii # :nodoc:
135
231
  data_string = Converter.array2ascii(pixels)
136
232
 
@@ -7,13 +7,13 @@ module PNM
7
7
  #
8
8
  # +content+:: A string containing the image file content.
9
9
  #
10
- # Returns a hash containing the parsed data as strings:
10
+ # Returns a hash containing the parsed data:
11
11
  #
12
- # * +:magic_number+,
12
+ # * +:magic_number+ (<tt>"P1"</tt>, ..., <tt>"P6"</tt>),
13
13
  # * +:maxgray+ (only for PGM and PPM),
14
14
  # * +:width+,
15
15
  # * +:height+,
16
- # * +:data+,
16
+ # * +:data+ (as string),
17
17
  # * +:comments+ (only if present): an array of comment strings.
18
18
  def self.parse(content)
19
19
  content = content.dup
@@ -32,7 +32,7 @@ module PNM
32
32
  end
33
33
  end
34
34
 
35
- raise "Unknown magic number" unless token_number[magic_number]
35
+ assert_valid_magic_number(magic_number)
36
36
 
37
37
  while tokens.size < token_number[magic_number]
38
38
  content.gsub!(/\A[ \t\r\n]+/, '')
@@ -47,6 +47,18 @@ module PNM
47
47
 
48
48
  width, height, maxgray = tokens
49
49
 
50
+ assert_integer(width, "width")
51
+ assert_integer(height, "height")
52
+ assert_integer(maxgray, "maxgray") if maxgray
53
+
54
+ width = width.to_i
55
+ height = height.to_i
56
+ maxgray = maxgray.to_i if maxgray
57
+
58
+ assert_value(width, "width") {|x| x > 0 }
59
+ assert_value(height, "height") {|x| x > 0 }
60
+ assert_value(maxgray, "maxgray") {|x| x > 0 && x <= 255 } if maxgray
61
+
50
62
  result = {
51
63
  :magic_number => magic_number,
52
64
  :width => width,
@@ -78,9 +90,32 @@ module PNM
78
90
  end
79
91
 
80
92
  token, rest = content.split(delimiter, 2)
93
+ raise PNM::ParserError, 'not enough tokens in file' unless rest
94
+
81
95
  content.replace(rest)
82
96
 
83
97
  token
84
98
  end
99
+
100
+ def self.assert_valid_magic_number(magic_number) # :nodoc:
101
+ unless %w{P1 P2 P3 P4 P5 P6}.include?(magic_number)
102
+ msg = "unknown magic number - `#{magic_number}'"
103
+ raise PNM::ParserError, msg
104
+ end
105
+ end
106
+
107
+ def self.assert_integer(value_string, value_name) # :nodoc:
108
+ unless /\A[0-9]+\Z/ === value_string
109
+ msg = "#{value_name} must be an integer - `#{value_string}'"
110
+ raise PNM::ParserError, msg
111
+ end
112
+ end
113
+
114
+ def self.assert_value(value, name) # :nodoc:
115
+ unless yield(value)
116
+ msg = "invalid #{name} value - `#{value}'"
117
+ raise PNM::ParserError, msg
118
+ end
119
+ end
85
120
  end
86
121
  end
@@ -1,4 +1,4 @@
1
1
  module PNM
2
- VERSION = '0.2.1' # :nodoc:
3
- DATE = '2013-12-17' # :nodoc:
2
+ VERSION = '0.3.0' # :nodoc:
3
+ DATE = '2014-02-08' # :nodoc:
4
4
  end
@@ -1,6 +1,6 @@
1
1
  # test_converter.rb: Unit tests for the PNM library.
2
2
  #
3
- # Copyright (C) 2013 Marcus Stollsteimer
3
+ # Copyright (C) 2013-2014 Marcus Stollsteimer
4
4
 
5
5
  require 'minitest/spec'
6
6
  require 'minitest/autorun'
@@ -0,0 +1,230 @@
1
+ # test_exceptions.rb: Unit tests for the PNM library.
2
+ #
3
+ # Copyright (C) 2013-2014 Marcus Stollsteimer
4
+
5
+ require 'minitest/spec'
6
+ require 'minitest/autorun'
7
+ require 'stringio'
8
+ require 'pnm'
9
+
10
+
11
+ describe 'PNM::Image.new' do
12
+
13
+ it 'raises an exception for invalid data type (String)' do
14
+ data = '0'
15
+ lambda { PNM::Image.new(data) }.must_raise PNM::ArgumentError
16
+ end
17
+
18
+ it 'raises an exception for invalid type (String)' do
19
+ data = [[0, 0], [0, 0]]
20
+ lambda { PNM::Image.new(data, :type => 'pbm') }.must_raise PNM::ArgumentError
21
+ end
22
+
23
+ it 'raises an exception for invalid maxgray (String)' do
24
+ data = [[0, 0], [0, 0]]
25
+ lambda { PNM::Image.new(data, :maxgray => '255') }.must_raise PNM::ArgumentError
26
+ end
27
+
28
+ it 'raises an exception for invalid maxgray (> 255)' do
29
+ data = [[0, 0], [0, 0]]
30
+ lambda { PNM::Image.new(data, :maxgray => 256) }.must_raise PNM::ArgumentError
31
+ end
32
+
33
+ it 'raises an exception for invalid maxgray (0)' do
34
+ data = [[0, 0], [0, 0]]
35
+ lambda { PNM::Image.new(data, :maxgray => 0) }.must_raise PNM::ArgumentError
36
+ end
37
+
38
+ it 'raises an exception for invalid comment (Fixnum)' do
39
+ data = [[0, 0], [0, 0]]
40
+ lambda { PNM::Image.new(data, :comment => 1) }.must_raise PNM::ArgumentError
41
+ end
42
+
43
+ it 'raises an exception for image type and data mismatch (PBM)' do
44
+ data = [[[0,0,0], [0,0,0]], [[0,0,0], [0,0,0]]]
45
+ lambda { PNM::Image.new(data, :type => :pbm) }.must_raise PNM::DataError
46
+ end
47
+
48
+ it 'raises an exception for image type and data mismatch (PGM)' do
49
+ data = [[[0,0,0], [0,0,0]], [[0,0,0], [0,0,0]]]
50
+ lambda { PNM::Image.new(data, :type => :pgm) }.must_raise PNM::DataError
51
+ end
52
+
53
+ it 'raises an exception for non-integer pixel value (String)' do
54
+ data = [[0, 0], ['X', 0]]
55
+ lambda { PNM::Image.new(data) }.must_raise PNM::DataError
56
+ end
57
+
58
+ it 'raises an exception for non-integer pixel value (Float)' do
59
+ data = [[0, 0], [0.5, 0]]
60
+ lambda { PNM::Image.new(data) }.must_raise PNM::DataError
61
+ end
62
+
63
+ it 'raises an exception for rows of different size' do
64
+ data = [[0, 0], [0, 0, 0]]
65
+ lambda { PNM::Image.new(data) }.must_raise PNM::DataError
66
+ end
67
+
68
+ it 'raises an exception for invalid array dimensions (#1)' do
69
+ data = [0, 0, 0]
70
+ lambda { PNM::Image.new(data) }.must_raise PNM::DataError
71
+ end
72
+
73
+ it 'raises an exception for invalid array dimensions (#2)' do
74
+ data = [[0, 0], 0, 0]
75
+ lambda { PNM::Image.new(data) }.must_raise PNM::DataError
76
+ end
77
+
78
+ it 'raises an exception for invalid array dimensions (#3)' do
79
+ data = [[0, 0], [0, [0, 0]]]
80
+ lambda { PNM::Image.new(data) }.must_raise PNM::DataError
81
+ end
82
+
83
+ it 'raises an exception for invalid array dimensions (#4)' do
84
+ data = [[[0,0], [0,0]], [[0,0], [0,0]]]
85
+ lambda { PNM::Image.new(data) }.must_raise PNM::DataError
86
+ end
87
+
88
+ it 'raises an exception for invalid array dimensions (#5)' do
89
+ data = [[[0,0,0], [0,0,0]], [0 ,0]]
90
+ lambda { PNM::Image.new(data) }.must_raise PNM::DataError
91
+ end
92
+
93
+ it 'raises an exception for invalid array dimensions (#6)' do
94
+ data = [[[0,0,0], 0], [0 ,0]]
95
+ lambda { PNM::Image.new(data) }.must_raise PNM::DataError
96
+ end
97
+
98
+ it 'raises an exception for invalid PBM data (> 1)' do
99
+ data = [[0, 0], [2, 0]]
100
+ lambda { PNM::Image.new(data, :type => :pbm) }.must_raise PNM::DataError
101
+ end
102
+
103
+ it 'raises an exception for invalid PBM data (< 0)' do
104
+ data = [[0, 0], [-1, 0]]
105
+ lambda { PNM::Image.new(data, :type => :pbm) }.must_raise PNM::DataError
106
+ end
107
+
108
+ it 'raises an exception for invalid PGM data (> 255)' do
109
+ data = [[0, 0], [1, 500]]
110
+ lambda { PNM::Image.new(data, :type => :pgm) }.must_raise PNM::DataError
111
+ end
112
+
113
+ it 'raises an exception for invalid PGM data (> maxgray)' do
114
+ data = [[0, 0], [1, 200]]
115
+ lambda { PNM::Image.new(data, :maxgray => 100) }.must_raise PNM::DataError
116
+ end
117
+ end
118
+
119
+
120
+ describe 'PNM.read' do
121
+
122
+ it 'raises an exception for integer argument' do
123
+ lambda { PNM.read(123) }.must_raise PNM::ArgumentError
124
+ end
125
+
126
+ it 'raises an exception for unknown magic number' do
127
+ file = StringIO.new('P0 1 1 0')
128
+ lambda { PNM.read(file) }.must_raise PNM::ParserError
129
+ end
130
+
131
+ it 'raises an exception for an empty file' do
132
+ file = StringIO.new('')
133
+ lambda { PNM.read(file) }.must_raise PNM::ParserError
134
+ end
135
+
136
+ it 'raises an exception for missing tokens (#1)' do
137
+ file = StringIO.new('P1')
138
+ lambda { PNM.read(file) }.must_raise PNM::ParserError
139
+ end
140
+
141
+ it 'raises an exception for missing tokens (#2)' do
142
+ file = StringIO.new('P1 1')
143
+ lambda { PNM.read(file) }.must_raise PNM::ParserError
144
+ end
145
+
146
+ it 'raises an exception for missing tokens (#3)' do
147
+ file = StringIO.new('P1 1 ')
148
+ lambda { PNM.read(file) }.must_raise PNM::ParserError
149
+ end
150
+
151
+ it 'raises an exception for missing tokens (#4)' do
152
+ file = StringIO.new('P1 1 1')
153
+ lambda { PNM.read(file) }.must_raise PNM::ParserError
154
+ end
155
+
156
+ it 'raises an exception for missing tokens (#5)' do
157
+ file = StringIO.new("P1 1 # Test\n 1")
158
+ lambda { PNM.read(file) }.must_raise PNM::ParserError
159
+ end
160
+
161
+ it 'raises an exception for missing tokens (#6)' do
162
+ file = StringIO.new("P2 1 1 255")
163
+ lambda { PNM.read(file) }.must_raise PNM::ParserError
164
+ end
165
+
166
+ it 'raises an exception for token of wrong type (#1)' do
167
+ file = StringIO.new('P1 ? 1 0')
168
+ lambda { PNM.read(file) }.must_raise PNM::ParserError
169
+ end
170
+
171
+ it 'raises an exception for token of wrong type (#2)' do
172
+ file = StringIO.new('P1 1 X 0')
173
+ lambda { PNM.read(file) }.must_raise PNM::ParserError
174
+ end
175
+
176
+ it 'raises an exception for token of wrong type (#3)' do
177
+ file = StringIO.new('P2 1 1 foo 0')
178
+ lambda { PNM.read(file) }.must_raise PNM::ParserError
179
+ end
180
+
181
+ it 'raises an exception for zero width' do
182
+ file = StringIO.new('P2 0 1 255 0')
183
+ lambda { PNM.read(file) }.must_raise PNM::ParserError
184
+ end
185
+
186
+ it 'raises an exception for zero height' do
187
+ file = StringIO.new('P2 1 0 255 0')
188
+ lambda { PNM.read(file) }.must_raise PNM::ParserError
189
+ end
190
+
191
+ it 'raises an exception for invalid maxgray (> 255)' do
192
+ file = StringIO.new('P2 1 1 256 0')
193
+ lambda { PNM.read(file) }.must_raise PNM::ParserError
194
+ end
195
+
196
+ it 'raises an exception for invalid maxgray (0)' do
197
+ file = StringIO.new('P2 1 1 0 0')
198
+ lambda { PNM.read(file) }.must_raise PNM::ParserError
199
+ end
200
+
201
+ it 'raises an exception for image dimension mismatch (#1)' do
202
+ file = StringIO.new('P1 2 3 0 0 0 0 0')
203
+ lambda { PNM.read(file) }.must_raise PNM::DataSizeError
204
+ end
205
+
206
+ it 'raises an exception for image dimension mismatch (#2)' do
207
+ file = StringIO.new('P1 2 3 0 0 0 0 0 0 0')
208
+ lambda { PNM.read(file) }.must_raise PNM::DataSizeError
209
+ end
210
+
211
+ it 'raises an exception for image dimension mismatch (#3)' do
212
+ file = StringIO.new('P3 2 3 255 0 0 0 0 0 0')
213
+ lambda { PNM.read(file) }.must_raise PNM::DataSizeError
214
+ end
215
+
216
+ it 'raises an exception for image dimension mismatch (#4)' do
217
+ file = StringIO.new('P5 2 3 255 AAAAAAA')
218
+ lambda { PNM.read(file) }.must_raise PNM::DataSizeError
219
+ end
220
+
221
+ it 'raises an exception for image dimension mismatch (#5)' do
222
+ file = StringIO.new('P5 2 3 255 AAAAAAA')
223
+ lambda { PNM.read(file) }.must_raise PNM::DataSizeError
224
+ end
225
+
226
+ it 'raises an exception for non-numeric image data' do
227
+ file = StringIO.new('P1 2 3 0 X 0 0 0 0')
228
+ lambda { PNM.read(file) }.must_raise PNM::DataError
229
+ end
230
+ end
@@ -1,6 +1,6 @@
1
1
  # test_image.rb: Unit tests for the PNM library.
2
2
  #
3
- # Copyright (C) 2013 Marcus Stollsteimer
3
+ # Copyright (C) 2013-2014 Marcus Stollsteimer
4
4
 
5
5
  require 'minitest/spec'
6
6
  require 'minitest/autorun'
@@ -168,4 +168,18 @@ describe PNM::Image do
168
168
  File.binread(@temp_path).must_equal File.binread("#{@srcpath}/grayscale_binary_crlf.pgm")
169
169
  File.delete(@temp_path)
170
170
  end
171
+
172
+ it 'can write zero-length comments' do
173
+ comment = ''
174
+ PNM::Image.new([[0,0]], :comment => comment).write(@temp_path, :ascii)
175
+ File.binread(@temp_path).must_equal "P1\n#\n2 1\n0 0\n"
176
+ File.delete(@temp_path)
177
+ end
178
+
179
+ it 'can write comments with trailing zero-length line' do
180
+ comment = "An empty line:\n"
181
+ PNM::Image.new([[0,0]], :comment => comment).write(@temp_path, :ascii)
182
+ File.binread(@temp_path).must_equal "P1\n# An empty line:\n#\n2 1\n0 0\n"
183
+ File.delete(@temp_path)
184
+ end
171
185
  end
@@ -1,6 +1,6 @@
1
1
  # test_parser.rb: Unit tests for the PNM library.
2
2
  #
3
- # Copyright (C) 2013 Marcus Stollsteimer
3
+ # Copyright (C) 2013-2014 Marcus Stollsteimer
4
4
 
5
5
  require 'minitest/spec'
6
6
  require 'minitest/autorun'
@@ -21,8 +21,8 @@ describe PNM::Parser do
21
21
  EOF
22
22
  expected = {
23
23
  :magic_number => 'P1',
24
- :width => '6',
25
- :height => '2',
24
+ :width => 6,
25
+ :height => 2,
26
26
  :data => "0 1 0 0 1 1\n0 0 0 1 1 1"
27
27
  }
28
28
 
@@ -37,9 +37,9 @@ describe PNM::Parser do
37
37
  EOF
38
38
  expected = {
39
39
  :magic_number => 'P2',
40
- :width => '4',
41
- :height => '2',
42
- :maxgray => '100',
40
+ :width => 4,
41
+ :height => 2,
42
+ :maxgray => 100,
43
43
  :data => "10 20 30 40\n50 60 70 80"
44
44
  }
45
45
 
@@ -50,8 +50,8 @@ describe PNM::Parser do
50
50
  content = 'P4 8 2 ' << ['05AF'].pack('H*')
51
51
  expected = {
52
52
  :magic_number => 'P4',
53
- :width => '8',
54
- :height => '2',
53
+ :width => 8,
54
+ :height => 2,
55
55
  :data => ['05AF'].pack('H*')
56
56
  }
57
57
 
@@ -70,8 +70,8 @@ describe PNM::Parser do
70
70
  content = "P1 \n\t 3 \r \n2 0 1 0 0 1 1"
71
71
  expected = {
72
72
  :magic_number => 'P1',
73
- :width => '3',
74
- :height => '2',
73
+ :width => 3,
74
+ :height => 2,
75
75
  :data => '0 1 0 0 1 1'
76
76
  }
77
77
 
@@ -108,8 +108,8 @@ describe PNM::Parser do
108
108
  EOF
109
109
  expected = {
110
110
  :magic_number => 'P1',
111
- :width => '6',
112
- :height => '2',
111
+ :width => 6,
112
+ :height => 2,
113
113
  :comments => ['Comment 1', 'Comment 2', 'Comment 3', 'Comment 4', '', 'Comment 6'],
114
114
  :data => "0 1 0 0 1 1\n0 0 0 1 1 1"
115
115
  }
@@ -1,6 +1,6 @@
1
1
  # test_pnm.rb: Unit tests for the PNM library.
2
2
  #
3
- # Copyright (C) 2013 Marcus Stollsteimer
3
+ # Copyright (C) 2013-2014 Marcus Stollsteimer
4
4
 
5
5
  require 'minitest/spec'
6
6
  require 'minitest/autorun'
metadata CHANGED
@@ -1,27 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pnm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marcus Stollsteimer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-17 00:00:00.000000000 Z
11
+ date: 2014-02-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  description: 'PNM is a pure Ruby library for creating, reading, and writing of PNM
@@ -34,55 +34,58 @@ extra_rdoc_files: []
34
34
  files:
35
35
  - README.md
36
36
  - Rakefile
37
- - pnm.gemspec
38
- - benchmark/random_image.ppm
39
37
  - benchmark/bm_converter.rb
40
- - benchmark/random_image.pgm
41
38
  - benchmark/random_image.pbm
39
+ - benchmark/random_image.pgm
40
+ - benchmark/random_image.ppm
42
41
  - lib/pnm.rb
42
+ - lib/pnm/converter.rb
43
+ - lib/pnm/exceptions.rb
43
44
  - lib/pnm/image.rb
44
45
  - lib/pnm/parser.rb
45
46
  - lib/pnm/version.rb
46
- - lib/pnm/converter.rb
47
+ - pnm.gemspec
48
+ - test/bilevel_2_binary.pbm
47
49
  - test/bilevel_ascii.pbm
50
+ - test/bilevel_binary.pbm
51
+ - test/color_ascii.ppm
52
+ - test/color_binary.ppm
53
+ - test/grayscale_ascii.pgm
48
54
  - test/grayscale_binary.pgm
49
- - test/test_image.rb
50
55
  - test/grayscale_binary_crlf.pgm
51
- - test/color_binary.ppm
52
- - test/bilevel_binary.pbm
53
- - test/test_parser.rb
54
56
  - test/test_converter.rb
55
- - test/bilevel_2_binary.pbm
56
- - test/color_ascii.ppm
57
+ - test/test_exceptions.rb
58
+ - test/test_image.rb
59
+ - test/test_parser.rb
57
60
  - test/test_pnm.rb
58
- - test/grayscale_ascii.pgm
59
61
  homepage: https://github.com/stomar/pnm
60
62
  licenses:
61
63
  - GPL-3
62
64
  metadata: {}
63
65
  post_install_message:
64
66
  rdoc_options:
65
- - --charset=UTF-8
67
+ - "--charset=UTF-8"
66
68
  require_paths:
67
69
  - lib
68
70
  required_ruby_version: !ruby/object:Gem::Requirement
69
71
  requirements:
70
- - - '>='
72
+ - - ">="
71
73
  - !ruby/object:Gem::Version
72
74
  version: 1.9.3
73
75
  required_rubygems_version: !ruby/object:Gem::Requirement
74
76
  requirements:
75
- - - '>='
77
+ - - ">="
76
78
  - !ruby/object:Gem::Version
77
79
  version: '0'
78
80
  requirements: []
79
81
  rubyforge_project:
80
- rubygems_version: 2.1.11
82
+ rubygems_version: 2.2.0
81
83
  signing_key:
82
84
  specification_version: 4
83
85
  summary: PNM - create/read/write PNM image files (PBM, PGM, PPM)
84
86
  test_files:
85
87
  - test/test_image.rb
88
+ - test/test_exceptions.rb
86
89
  - test/test_parser.rb
87
90
  - test/test_converter.rb
88
91
  - test/test_pnm.rb