pnm 0.4.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PNM
2
4
 
3
5
  # Base class for all PNM exceptions.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PNM
2
4
 
3
5
  # Abstract base class for +PBM+, +PGM+, and +PPM+ images.
@@ -36,20 +38,20 @@ module PNM
36
38
  # This method should be called as PNM.create.
37
39
  # See there for a description of pixel data formats
38
40
  # and available options.
39
- def self.create(pixels, options = {})
41
+ def self.create(pixels, type: nil, maxgray: nil, comment: nil)
40
42
  assert_valid_array(pixels)
41
- assert_valid_maxgray(options[:maxgray])
42
- assert_valid_comment(options[:comment])
43
+ assert_valid_maxgray(maxgray)
44
+ assert_valid_comment(comment)
43
45
 
44
- type = sanitize_and_assert_valid_type(options[:type])
45
- type ||= detect_type(pixels, options[:maxgray])
46
+ type = sanitize_and_assert_valid_type(type)
47
+ type ||= detect_type(pixels, maxgray)
46
48
 
47
49
  # except for type detection, the maxgray option must be ignored for PBM
48
- if type == :pbm
49
- maxgray = nil
50
- else
51
- maxgray = options[:maxgray]
52
- end
50
+ maxgray = if type == :pbm
51
+ nil
52
+ else
53
+ maxgray
54
+ end
53
55
 
54
56
  image_class = case type
55
57
  when :pbm
@@ -60,7 +62,7 @@ module PNM
60
62
  PPMImage
61
63
  end
62
64
 
63
- image_class.new(pixels, maxgray, options[:comment])
65
+ image_class.new(pixels, maxgray, comment)
64
66
  end
65
67
 
66
68
  class << self
@@ -80,22 +82,31 @@ module PNM
80
82
  assert_pixel_value_range
81
83
 
82
84
  post_initialize
85
+
86
+ @pixels.freeze
87
+ @comment.freeze
83
88
  end
84
89
 
85
- # Writes the image to +file+ (a filename or an IO object),
86
- # using the specified encoding.
87
- # Valid encodings are +:binary+ (default) and +:ascii+.
90
+ # Writes the image to +file+ (a filename or an IO object).
91
+ #
92
+ # When +add_extension+ is set to +true+ (default: +false+)
93
+ # the appropriate file extension is added to the provided filename
94
+ # (+.pbm+, +.pgm+, or +.ppm+).
95
+ #
96
+ # The encoding can be set using the +encoding+ keyword argument,
97
+ # valid options are +:binary+ (default) and +:ascii+.
88
98
  #
89
99
  # Returns the number of bytes written.
90
- def write(file, encoding = :binary)
100
+ def write(file, add_extension: false, encoding: :binary)
91
101
  content = if encoding == :ascii
92
102
  to_ascii
93
103
  elsif encoding == :binary
94
104
  to_binary
95
105
  end
96
106
 
97
- if file.kind_of?(String)
98
- File.binwrite(file, content)
107
+ if file.is_a?(String)
108
+ filename = add_extension ? "#{file}.#{type}" : file
109
+ File.binwrite(filename, content)
99
110
  else
100
111
  file.binmode
101
112
  file.write content
@@ -107,35 +118,44 @@ module PNM
107
118
  "#{type.to_s.upcase} #{width}x#{height} #{type_string}"
108
119
  end
109
120
 
110
- alias :to_s :info
121
+ alias to_s info
111
122
 
112
123
  # Returns a string representation for debugging.
113
124
  def inspect
114
125
  # implemented by subclasses
115
126
  end
116
127
 
117
- private
128
+ # Equality --- Two images are considered equal if they have
129
+ # the same pixel values, type, maxgray, and comments.
130
+ def ==(other)
131
+ return true if other.equal?(self)
132
+ return false unless other.instance_of?(self.class)
133
+
134
+ type == other.type && maxgray == other.maxgray && comment == other.comment && pixels == other.pixels
135
+ end
118
136
 
119
137
  def self.assert_valid_array(pixels) # :nodoc:
120
138
  assert_array_dimensions(pixels)
121
139
  assert_pixel_types(pixels)
122
140
  end
141
+ private_class_method :assert_valid_array
123
142
 
124
143
  def self.assert_array_dimensions(pixels) # :nodoc:
125
144
  msg = "invalid pixel data: Array expected"
126
- raise PNM::ArgumentError, msg unless Array === pixels
145
+ raise PNM::ArgumentError, msg unless pixels.is_a?(Array)
127
146
 
128
147
  msg = "invalid pixel array"
129
-
130
148
  raise PNM::DataError, msg unless pixels.map(&:class).uniq == [Array]
149
+
131
150
  width = pixels.first.size
132
- raise PNM::DataError, msg if width == 0
151
+ raise PNM::DataError, msg if width.zero?
133
152
  raise PNM::DataError, msg unless pixels.map(&:size).uniq == [width]
134
153
  end
154
+ private_class_method :assert_array_dimensions
135
155
 
136
156
  def self.assert_pixel_types(pixels) # :nodoc:
137
157
  pixel_values = pixels.flatten(1)
138
- is_color = (Array === pixel_values.first)
158
+ is_color = pixel_values.first.is_a?(Array)
139
159
 
140
160
  if is_color
141
161
  pixel_values.each {|pixel| assert_valid_color_pixel(pixel) }
@@ -143,43 +163,47 @@ module PNM
143
163
  pixel_values.each {|pixel| assert_valid_pixel(pixel) }
144
164
  end
145
165
  end
166
+ private_class_method :assert_pixel_types
146
167
 
147
168
  def self.assert_valid_pixel(pixel) # :nodoc:
148
- unless Fixnum === pixel
149
- msg = "invalid pixel value: Fixnum expected - #{pixel.inspect}"
150
- raise PNM::DataError, msg
151
- end
169
+ return if pixel.is_a?(Integer)
170
+
171
+ msg = "invalid pixel value: Integer expected - #{pixel.inspect}"
172
+ raise PNM::DataError, msg
152
173
  end
174
+ private_class_method :assert_valid_pixel
153
175
 
154
176
  def self.assert_valid_color_pixel(pixel) # :nodoc:
155
- unless Array === pixel && pixel.map(&:class) == [Fixnum, Fixnum, Fixnum]
156
- msg = "invalid pixel value: "
157
- msg << "Array of 3 Fixnums expected - #{pixel.inspect}"
177
+ return if pixel.is_a?(Array) && pixel.size == 3 && pixel.all? {|p| p.is_a?(Integer) }
158
178
 
159
- raise PNM::DataError, msg
160
- end
179
+ msg = "invalid pixel value: ".dup
180
+ msg << "Array of 3 Integers expected - #{pixel.inspect}"
181
+ raise PNM::DataError, msg
161
182
  end
183
+ private_class_method :assert_valid_color_pixel
162
184
 
163
185
  def self.assert_valid_maxgray(maxgray) # :nodoc:
164
186
  return unless maxgray
187
+ return if maxgray.is_a?(Integer) && maxgray > 0 && maxgray <= 255
165
188
 
166
- unless Fixnum === maxgray && maxgray > 0 && maxgray <= 255
167
- raise PNM::ArgumentError, "invalid maxgray value - #{maxgray.inspect}"
168
- end
189
+ msg = "invalid maxgray value - #{maxgray.inspect}"
190
+ raise PNM::ArgumentError, msg
169
191
  end
192
+ private_class_method :assert_valid_maxgray
170
193
 
171
194
  def self.assert_valid_comment(comment) # :nodoc:
172
195
  return unless comment
196
+ return if comment.is_a?(String)
173
197
 
174
- unless String === comment
175
- raise PNM::ArgumentError, "invalid comment value - #{comment.inspect}"
176
- end
198
+ msg = "invalid comment value - #{comment.inspect}"
199
+ raise PNM::ArgumentError, msg
177
200
  end
201
+ private_class_method :assert_valid_comment
178
202
 
179
203
  def self.sanitize_and_assert_valid_type(type) # :nodoc:
180
204
  return unless type
181
205
 
182
- type = type.to_sym if type.kind_of?(String)
206
+ type = type.to_sym if type.is_a?(String)
183
207
 
184
208
  unless [:pbm, :pgm, :ppm].include?(type)
185
209
  msg = "invalid image type - #{type.inspect}"
@@ -188,9 +212,10 @@ module PNM
188
212
 
189
213
  type
190
214
  end
215
+ private_class_method :sanitize_and_assert_valid_type
191
216
 
192
217
  def self.detect_type(pixels, maxgray) # :nodoc:
193
- if pixels.first.first.kind_of?(Array)
218
+ if pixels.first.first.is_a?(Array)
194
219
  :ppm
195
220
  elsif (maxgray && maxgray > 1) || pixels.flatten.max > 1
196
221
  :pgm
@@ -198,25 +223,27 @@ module PNM
198
223
  :pbm
199
224
  end
200
225
  end
226
+ private_class_method :detect_type
227
+
228
+ private
201
229
 
202
230
  def assert_grayscale_data # :nodoc:
203
- if color_pixels?
204
- msg = "specified type does not match RGB data - #{type.inspect}"
205
- raise PNM::DataError, msg
206
- end
231
+ return unless color_pixels?
232
+
233
+ msg = "specified type does not match RGB data - #{type.inspect}"
234
+ raise PNM::DataError, msg
207
235
  end
208
236
 
209
237
  def assert_pixel_value_range # :nodoc:
210
- unless pixels.flatten.max <= maxgray
211
- raise PNM::DataError, "invalid data: value(s) greater than maxgray"
212
- end
213
- unless pixels.flatten.min >= 0
214
- raise PNM::DataError, "invalid data: value(s) less than zero"
215
- end
238
+ msg = "invalid data: value(s) greater than maxgray"
239
+ raise PNM::DataError, msg unless pixels.flatten.max <= maxgray
240
+
241
+ msg = "invalid data: value(s) less than zero"
242
+ raise PNM::DataError, msg unless pixels.flatten.min >= 0
216
243
  end
217
244
 
218
245
  def header_without_maxgray(encoding) # :nodoc:
219
- header = "#{PNM.magic_number[type][encoding]}\n"
246
+ header = "#{PNM.magic_number[type][encoding]}\n".dup
220
247
  comment_lines.each do |line|
221
248
  header << (line.empty? ? "#\n" : "# #{line}\n")
222
249
  end
@@ -231,7 +258,7 @@ module PNM
231
258
 
232
259
  def comment_lines # :nodoc:
233
260
  return [] unless comment
234
- return [''] if comment.empty?
261
+ return [""] if comment.empty?
235
262
 
236
263
  keep_trailing_null_fields = -1 # magic value for split limit
237
264
  comment.split(/\n/, keep_trailing_null_fields)
@@ -250,11 +277,11 @@ module PNM
250
277
  end
251
278
 
252
279
  def color_pixels? # :nodoc:
253
- (pixels.first.first).kind_of?(Array)
280
+ pixels.first.first.is_a?(Array)
254
281
  end
255
282
 
256
283
  def inspect_string_with_maxgray # :nodoc:
257
- "#<%s:0x%x %s, maxgray=%d>" % [self.class.name, object_id, info, maxgray]
284
+ "#<%s:0x%x %s, maxgray=%d>" % [self.class.name, object_id, info, maxgray]
258
285
  end
259
286
 
260
287
  def inspect_string_without_maxgray # :nodoc:
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PNM
2
4
 
3
5
  # @private
@@ -20,14 +22,14 @@ module PNM
20
22
  content = content.dup
21
23
 
22
24
  magic_number = nil
23
- tokens = []
25
+ tokens = []
24
26
  comments = []
25
27
 
26
28
  until magic_number
27
29
  token = next_token!(content)
28
30
 
29
- if token.start_with?('#')
30
- comments << token.gsub(/# */, '')
31
+ if token.start_with?("#")
32
+ comments << token.gsub(/# */, "")
31
33
  else
32
34
  magic_number = token
33
35
  end
@@ -36,11 +38,11 @@ module PNM
36
38
  assert_valid_magic_number(magic_number)
37
39
 
38
40
  while tokens.size < token_number[magic_number]
39
- content.gsub!(/\A[ \t\r\n]+/, '')
41
+ content.gsub!(/\A[ \t\r\n]+/, "")
40
42
  token = next_token!(content)
41
43
 
42
- if token.start_with?('#')
43
- comments << token.gsub(/# */, '')
44
+ if token.start_with?("#")
45
+ comments << token.gsub(/# */, "")
44
46
  else
45
47
  tokens << token
46
48
  end
@@ -48,23 +50,23 @@ module PNM
48
50
 
49
51
  width, height, maxgray = tokens
50
52
 
51
- assert_integer(width, "width")
52
- assert_integer(height, "height")
53
+ assert_integer(width, "width")
54
+ assert_integer(height, "height")
53
55
  assert_integer(maxgray, "maxgray") if maxgray
54
56
 
55
- width = width.to_i
56
- height = height.to_i
57
+ width = width.to_i
58
+ height = height.to_i
57
59
  maxgray = maxgray.to_i if maxgray
58
60
 
59
- assert_value(width, "width") {|x| x > 0 }
60
- assert_value(height, "height") {|x| x > 0 }
61
- assert_value(maxgray, "maxgray") {|x| x > 0 && x <= 255 } if maxgray
61
+ assert_positive(width, "width")
62
+ assert_positive(height, "height")
63
+ assert_in_maxgray_range(maxgray) if maxgray
62
64
 
63
65
  result = {
64
- :magic_number => magic_number,
65
- :width => width,
66
- :height => height,
67
- :data => content
66
+ magic_number: magic_number,
67
+ width: width,
68
+ height: height,
69
+ data: content
68
70
  }
69
71
  result[:maxgray] = maxgray if maxgray
70
72
  result[:comments] = comments unless comments.empty?
@@ -74,24 +76,24 @@ module PNM
74
76
 
75
77
  def self.token_number
76
78
  {
77
- 'P1' => 2,
78
- 'P2' => 3,
79
- 'P3' => 3,
80
- 'P4' => 2,
81
- 'P5' => 3,
82
- 'P6' => 3
79
+ "P1" => 2,
80
+ "P2" => 3,
81
+ "P3" => 3,
82
+ "P4" => 2,
83
+ "P5" => 3,
84
+ "P6" => 3
83
85
  }
84
86
  end
85
87
 
86
88
  def self.next_token!(content)
87
- delimiter = if content.start_with?('#')
89
+ delimiter = if content.start_with?("#")
88
90
  "\n"
89
91
  else
90
- %r{[ \t\r\n]|(?=#)}
92
+ /[ \t\r\n]|(?=#)/
91
93
  end
92
94
 
93
95
  token, rest = content.split(delimiter, 2)
94
- raise PNM::ParserError, 'not enough tokens in file' unless rest
96
+ raise PNM::ParserError, "not enough tokens in file" unless rest
95
97
 
96
98
  content.replace(rest)
97
99
 
@@ -99,24 +101,31 @@ module PNM
99
101
  end
100
102
 
101
103
  def self.assert_valid_magic_number(magic_number)
102
- unless %w{P1 P2 P3 P4 P5 P6}.include?(magic_number)
103
- msg = "unknown magic number - `#{magic_number}'"
104
- raise PNM::ParserError, msg
105
- end
104
+ return if %w[P1 P2 P3 P4 P5 P6].include?(magic_number)
105
+
106
+ msg = "unknown magic number - `#{magic_number}'"
107
+ raise PNM::ParserError, msg
106
108
  end
107
109
 
108
110
  def self.assert_integer(value_string, value_name)
109
- unless /\A[0-9]+\Z/ === value_string
110
- msg = "#{value_name} must be an integer - `#{value_string}'"
111
- raise PNM::ParserError, msg
112
- end
111
+ return if value_string =~ /\A[0-9]+\Z/
112
+
113
+ msg = "#{value_name} must be an integer - `#{value_string}'"
114
+ raise PNM::ParserError, msg
113
115
  end
114
116
 
115
- def self.assert_value(value, name)
116
- unless yield(value)
117
- msg = "invalid #{name} value - `#{value}'"
118
- raise PNM::ParserError, msg
119
- end
117
+ def self.assert_positive(value, name)
118
+ return if value > 0
119
+
120
+ msg = "#{name} must be greater than 0 - `#{value}'"
121
+ raise PNM::ParserError, msg
122
+ end
123
+
124
+ def self.assert_in_maxgray_range(value)
125
+ return if value > 0 && value <= 255
126
+
127
+ msg = "invalid maxgray value - `#{value}'"
128
+ raise PNM::ParserError, msg
120
129
  end
121
130
  end
122
131
  end
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PNM
2
- VERSION = '0.4.1'
3
- DATE = '2015-01-20'
4
+ VERSION = "0.6.0"
5
+ DATE = "2020-09-23"
4
6
  end
@@ -1,4 +1,6 @@
1
- require './lib/pnm'
1
+ # frozen_string_literal: true
2
+
3
+ require "./lib/pnm"
2
4
 
3
5
  version = PNM::VERSION
4
6
  date = PNM::DATE
@@ -6,39 +8,41 @@ homepage = PNM::HOMEPAGE
6
8
  tagline = PNM::TAGLINE
7
9
 
8
10
  Gem::Specification.new do |s|
9
- s.name = 'pnm'
11
+ s.name = "pnm"
10
12
  s.version = version
11
13
  s.date = date
12
14
 
13
- s.description = 'PNM is a pure Ruby library for creating, reading, ' +
14
- 'and writing of PNM image files (Portable Anymap): ' +
15
- 'PBM (Portable Bitmap), ' +
16
- 'PGM (Portable Graymap), and ' +
17
- 'PPM (Portable Pixmap).'
15
+ s.description = "PNM is a pure Ruby library for creating, reading, " \
16
+ "and writing of PNM image files (Portable Anymap): " \
17
+ "PBM (Portable Bitmap), " \
18
+ "PGM (Portable Graymap), and " \
19
+ "PPM (Portable Pixmap)."
18
20
  s.summary = "PNM - #{tagline}"
19
21
 
20
- s.authors = ['Marcus Stollsteimer']
21
- s.email = 'sto.mar@web.de'
22
+ s.authors = ["Marcus Stollsteimer"]
23
+ s.email = "sto.mar@web.de"
22
24
  s.homepage = homepage
23
25
 
24
- s.license = 'GPL-3'
26
+ s.license = "GPL-3.0"
25
27
 
26
- s.required_ruby_version = '>= 1.9.3'
28
+ s.required_ruby_version = ">= 2.0.0"
27
29
 
28
- s.add_development_dependency 'rake', '>= 10.0'
29
- s.add_development_dependency 'minitest', '>= 5.0'
30
+ s.add_development_dependency "minitest", ">= 5.0"
31
+ s.add_development_dependency "rake", ">= 10.0"
30
32
 
31
- s.require_path = 'lib'
33
+ s.require_paths = ["lib"]
32
34
 
33
- s.test_files = Dir.glob('test/**/test_*.rb')
35
+ s.test_files = Dir.glob("test/**/test_*.rb")
34
36
 
35
- s.files = %w{
37
+ s.files =
38
+ %w[
36
39
  README.md
37
40
  Rakefile
38
41
  pnm.gemspec
39
42
  .yardopts
40
- } +
41
- Dir.glob('{benchmark,lib,test}/**/*')
43
+ ] +
44
+ Dir.glob("{benchmark,lib,test}/**/*")
42
45
 
43
- s.rdoc_options = ['--charset=UTF-8']
46
+ s.extra_rdoc_files = ["README.md"]
47
+ s.rdoc_options = ["--charset=UTF-8", "--main=README.md"]
44
48
  end