exifr 1.1.3 → 1.3.8
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 +7 -0
- data/Gemfile +2 -0
- data/Rakefile +20 -22
- data/bin/exifr +4 -1
- data/lib/exifr/jpeg.rb +28 -19
- data/lib/exifr/tiff.rb +104 -40
- data/lib/exifr.rb +6 -4
- data/tests/data/gopro_hd2.exif +0 -0
- data/tests/data/gps-altitude.jpg +0 -0
- data/tests/data/samsung-sc-02b.jpg +0 -0
- data/tests/data/sony-a7ii.exif +0 -0
- data/tests/jpeg_test.rb +58 -25
- data/tests/test_helper.rb +22 -4
- data/tests/tiff_test.rb +62 -35
- metadata +64 -35
- data/CHANGELOG +0 -108
- data/README.rdoc +0 -31
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA256:
         | 
| 3 | 
            +
              metadata.gz: 19c3f1b08e84425d9d0b8a6e6ee11bd2e25190a49ce34d42ea99800f5030fe20
         | 
| 4 | 
            +
              data.tar.gz: 9a834f93b1a02e87bf401b09a033b25fb8b05625b0a60a8795e9ee65e1ea064c
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: b4fc267041e9090a85eb5ca2a0bfc2fae3d36a72aa45ab716209e6e5e896a775957b65251587e16e7c4e3f0e3c2e491805ce226c68b9b189a8b5bdaf506ebc15
         | 
| 7 | 
            +
              data.tar.gz: f39334aa7142a3b918edf30fa3bd6424ddc50c1906beb2c9d5bf384bc939fba35b410d2ee801410769929a29895e75b223d443af01d7acf7752fbf96adad0fa6
         | 
    
        data/Gemfile
    ADDED
    
    
    
        data/Rakefile
    CHANGED
    
    | @@ -1,39 +1,37 @@ | |
| 1 | 
            -
            # Copyright (c) 2006 | 
| 1 | 
            +
            # Copyright (c) 2006-2020 - R.W. van 't Veer
         | 
| 2 2 |  | 
| 3 | 
            -
            require 'rake/rdoctask'
         | 
| 4 3 | 
             
            require 'rake/testtask'
         | 
| 5 4 |  | 
| 6 5 | 
             
            task :default => :test
         | 
| 7 6 |  | 
| 8 | 
            -
            desc 'Generate site'
         | 
| 9 | 
            -
            task :site => :rdoc do
         | 
| 10 | 
            -
              system 'rsync -av --delete doc/ remvee@rubyforge.org:/var/www/gforge-projects/exifr'
         | 
| 11 | 
            -
            end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
            Rake::RDocTask.new do |rd|
         | 
| 14 | 
            -
              rd.title = 'EXIF Reader for Ruby API Documentation'
         | 
| 15 | 
            -
              rd.main = "README.rdoc"
         | 
| 16 | 
            -
              rd.rdoc_dir = "doc/api"
         | 
| 17 | 
            -
              rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
         | 
| 18 | 
            -
            end
         | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 7 | 
             
            Rake::TestTask.new do |t|
         | 
| 22 8 | 
             
              t.libs << 'lib' << 'tests'
         | 
| 23 9 | 
             
              t.test_files = FileList['tests/*_test.rb']
         | 
| 10 | 
            +
              t.warning = true
         | 
| 24 11 | 
             
            end
         | 
| 25 12 |  | 
| 26 13 | 
             
            begin
         | 
| 27 | 
            -
               | 
| 14 | 
            +
              begin
         | 
| 15 | 
            +
                require 'rdoc/task'
         | 
| 16 | 
            +
              rescue LoadError
         | 
| 17 | 
            +
                require 'rake/rdoctask'
         | 
| 18 | 
            +
              end
         | 
| 28 19 |  | 
| 29 | 
            -
               | 
| 30 | 
            -
             | 
| 31 | 
            -
                 | 
| 20 | 
            +
              desc 'Generate site'
         | 
| 21 | 
            +
              task :site => :rdoc do
         | 
| 22 | 
            +
                system 'rsync -av --delete doc/ remvee@rubyforge.org:/var/www/gforge-projects/exifr'
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              Rake::RDocTask.new do |rd|
         | 
| 26 | 
            +
                rd.title = 'EXIF Reader for Ruby API Documentation'
         | 
| 27 | 
            +
                rd.main = "README.rdoc"
         | 
| 28 | 
            +
                rd.rdoc_dir = "doc/api"
         | 
| 29 | 
            +
                rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
         | 
| 32 30 | 
             
              end
         | 
| 33 31 |  | 
| 34 | 
            -
              desc 'Remove all artifacts left by testing and packaging'
         | 
| 35 | 
            -
              task :clean => [:clobber_rdoc, :clobber_rcov]
         | 
| 36 | 
            -
            rescue LoadError
         | 
| 37 32 | 
             
              desc 'Remove all artifacts left by testing and packaging'
         | 
| 38 33 | 
             
              task :clean => [:clobber_rdoc]
         | 
| 34 | 
            +
             | 
| 35 | 
            +
            rescue StandardError
         | 
| 36 | 
            +
              nil
         | 
| 39 37 | 
             
            end
         | 
    
        data/bin/exifr
    CHANGED
    
    
    
        data/lib/exifr/jpeg.rb
    CHANGED
    
    | @@ -1,7 +1,9 @@ | |
| 1 | 
            -
            # Copyright (c) 2006 | 
| 1 | 
            +
            # Copyright (c) 2006-2020 - R.W. van 't Veer
         | 
| 2 2 |  | 
| 3 3 | 
             
            require 'exifr'
         | 
| 4 | 
            +
            require 'exifr/tiff'
         | 
| 4 5 | 
             
            require 'stringio'
         | 
| 6 | 
            +
            require 'delegate'
         | 
| 5 7 |  | 
| 6 8 | 
             
            module EXIFR
         | 
| 7 9 | 
             
              # = JPEG decoder
         | 
| @@ -56,40 +58,47 @@ module EXIFR | |
| 56 58 | 
             
                # +method+ does exist for EXIF data +nil+ will be returned.
         | 
| 57 59 | 
             
                def method_missing(method, *args)
         | 
| 58 60 | 
             
                  super unless args.empty?
         | 
| 59 | 
            -
                  super unless methods.include?(method | 
| 61 | 
            +
                  super unless methods.include?(method)
         | 
| 60 62 | 
             
                  @exif.send method if defined?(@exif) && @exif
         | 
| 61 63 | 
             
                end
         | 
| 62 64 |  | 
| 63 | 
            -
                def respond_to?(method) # :nodoc:
         | 
| 64 | 
            -
                  super || methods.include?(method. | 
| 65 | 
            +
                def respond_to?(method, include_all = false) # :nodoc:
         | 
| 66 | 
            +
                  super || methods.include?(method) || (include_all && private_methods.include?(method))
         | 
| 65 67 | 
             
                end
         | 
| 66 68 |  | 
| 67 | 
            -
                def methods # :nodoc:
         | 
| 68 | 
            -
                   | 
| 69 | 
            +
                def methods(regular=true) # :nodoc:
         | 
| 70 | 
            +
                  if regular
         | 
| 71 | 
            +
                    super + TIFF::TAGS << :gps
         | 
| 72 | 
            +
                  else
         | 
| 73 | 
            +
                    super
         | 
| 74 | 
            +
                  end
         | 
| 69 75 | 
             
                end
         | 
| 70 76 |  | 
| 71 77 | 
             
                class << self
         | 
| 72 78 | 
             
                  alias instance_methods_without_jpeg_extras instance_methods
         | 
| 73 79 | 
             
                  def instance_methods(include_super = true) # :nodoc:
         | 
| 74 | 
            -
                    instance_methods_without_jpeg_extras(include_super) + TIFF::TAGS <<  | 
| 80 | 
            +
                    instance_methods_without_jpeg_extras(include_super) + TIFF::TAGS << :gps
         | 
| 75 81 | 
             
                  end
         | 
| 76 82 | 
             
                end
         | 
| 77 83 |  | 
| 78 84 | 
             
              private
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                class Reader < SimpleDelegator
         | 
| 87 | 
            +
                  def readbyte; readchar; end unless File.method_defined?(:readbyte)
         | 
| 88 | 
            +
                  def readint; (readbyte << 8) + readbyte; end
         | 
| 89 | 
            +
                  def readframe; read(readint - 2); end
         | 
| 90 | 
            +
                  def readsof; [readint, readbyte, readint, readint, readbyte]; end
         | 
| 91 | 
            +
                  def next
         | 
| 92 | 
            +
                    c = readbyte while c != 0xFF
         | 
| 93 | 
            +
                    c = readbyte while c == 0xFF
         | 
| 94 | 
            +
                    c
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
                end
         | 
| 97 | 
            +
             | 
| 79 98 | 
             
                def examine(io)
         | 
| 80 | 
            -
                   | 
| 81 | 
            -
                    def readbyte; readchar; end unless method_defined?(:readbyte)
         | 
| 82 | 
            -
                    def readint; (readbyte << 8) + readbyte; end
         | 
| 83 | 
            -
                    def readframe; read(readint - 2); end
         | 
| 84 | 
            -
                    def readsof; [readint, readbyte, readint, readint, readbyte]; end
         | 
| 85 | 
            -
                    def next
         | 
| 86 | 
            -
                      c = readbyte while c != 0xFF
         | 
| 87 | 
            -
                      c = readbyte while c == 0xFF
         | 
| 88 | 
            -
                      c
         | 
| 89 | 
            -
                    end
         | 
| 90 | 
            -
                  end unless io.respond_to? :readsof
         | 
| 99 | 
            +
                  io = Reader.new(io)
         | 
| 91 100 |  | 
| 92 | 
            -
                  unless io. | 
| 101 | 
            +
                  unless io.getbyte == 0xFF && io.getbyte == 0xD8 # SOI
         | 
| 93 102 | 
             
                    raise MalformedJPEG, "no start of image marker found"
         | 
| 94 103 | 
             
                  end
         | 
| 95 104 |  | 
    
        data/lib/exifr/tiff.rb
    CHANGED
    
    | @@ -1,4 +1,4 @@ | |
| 1 | 
            -
            # Copyright (c) 2007 | 
| 1 | 
            +
            # Copyright (c) 2007-2017 - R.W. van 't Veer
         | 
| 2 2 |  | 
| 3 3 | 
             
            require 'exifr'
         | 
| 4 4 | 
             
            require 'rational'
         | 
| @@ -169,7 +169,7 @@ module EXIFR | |
| 169 169 | 
             
                    0x927c => :maker_note,
         | 
| 170 170 | 
             
                    0x9286 => :user_comment,
         | 
| 171 171 | 
             
                    0x9290 => :subsec_time,
         | 
| 172 | 
            -
                    0x9291 => : | 
| 172 | 
            +
                    0x9291 => :subsec_time_original,
         | 
| 173 173 | 
             
                    0x9292 => :subsec_time_digitized,
         | 
| 174 174 | 
             
                    0xa000 => :flashpix_version,
         | 
| 175 175 | 
             
                    0xa001 => :color_space,
         | 
| @@ -199,7 +199,10 @@ module EXIFR | |
| 199 199 | 
             
                    0xa40a => :sharpness,
         | 
| 200 200 | 
             
                    0xa40b => :device_setting_description,
         | 
| 201 201 | 
             
                    0xa40c => :subject_distance_range,
         | 
| 202 | 
            -
                    0xa420 => :image_unique_id
         | 
| 202 | 
            +
                    0xa420 => :image_unique_id,
         | 
| 203 | 
            +
                    0xa433 => :lens_make,
         | 
| 204 | 
            +
                    0xa434 => :lens_model,
         | 
| 205 | 
            +
                    0xa435 => :lens_serial_number
         | 
| 203 206 | 
             
                  },
         | 
| 204 207 |  | 
| 205 208 | 
             
                  :gps => {
         | 
| @@ -234,16 +237,28 @@ module EXIFR | |
| 234 237 | 
             
                    0x001c => :gps_area_information,
         | 
| 235 238 | 
             
                    0x001d => :gps_date_stamp,
         | 
| 236 239 | 
             
                    0x001e => :gps_differential,
         | 
| 240 | 
            +
                    0x001f => :gps_h_positioning_error
         | 
| 237 241 | 
             
                  },
         | 
| 238 242 | 
             
                })
         | 
| 239 243 | 
             
                IFD_TAGS = [:image, :exif, :gps] # :nodoc:
         | 
| 240 244 |  | 
| 245 | 
            +
                class << self
         | 
| 246 | 
            +
                  # Callable to create a +Time+ object.  Defaults to <tt>proc{|*a|Time.local(*a)}</tt>.
         | 
| 247 | 
            +
                  attr_accessor :mktime_proc
         | 
| 248 | 
            +
                end
         | 
| 249 | 
            +
                self.mktime_proc = proc { |*args| Time.local(*args) }
         | 
| 250 | 
            +
             | 
| 241 251 | 
             
                time_proc = proc do |value|
         | 
| 242 | 
            -
                  value.map do | | 
| 243 | 
            -
                    if  | 
| 244 | 
            -
                       | 
| 252 | 
            +
                  value.map do |v|
         | 
| 253 | 
            +
                    if v =~ /^(\d{4}):(\d\d):(\d\d) (\d\d):(\d\d):(\d\d)(?:\.(\d{3}))?$/
         | 
| 254 | 
            +
                      begin
         | 
| 255 | 
            +
                        mktime_proc.call($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, $7.to_i * 1000)
         | 
| 256 | 
            +
                      rescue => ex
         | 
| 257 | 
            +
                        EXIFR.logger.warn("Bad date/time value #{v.inspect}: #{ex}")
         | 
| 258 | 
            +
                        nil
         | 
| 259 | 
            +
                      end
         | 
| 245 260 | 
             
                    else
         | 
| 246 | 
            -
                       | 
| 261 | 
            +
                      v
         | 
| 247 262 | 
             
                    end
         | 
| 248 263 | 
             
                  end
         | 
| 249 264 | 
             
                end
         | 
| @@ -259,6 +274,11 @@ module EXIFR | |
| 259 274 | 
             
                    @value
         | 
| 260 275 | 
             
                  end
         | 
| 261 276 |  | 
| 277 | 
            +
                  # Symbolic value.
         | 
| 278 | 
            +
                  def to_sym
         | 
| 279 | 
            +
                    @type
         | 
| 280 | 
            +
                  end
         | 
| 281 | 
            +
             | 
| 262 282 | 
             
                  # Debugging output.
         | 
| 263 283 | 
             
                  def inspect
         | 
| 264 284 | 
             
                    "\#<EXIFR::TIFF::Orientation:#{@type}(#{@value})>"
         | 
| @@ -300,9 +320,18 @@ module EXIFR | |
| 300 320 | 
             
                  const_set("#{type}Orientation", ORIENTATIONS[index] = Orientation.new(index, type))
         | 
| 301 321 | 
             
                end
         | 
| 302 322 |  | 
| 323 | 
            +
                degrees_proc = proc do |v|
         | 
| 324 | 
            +
                  begin
         | 
| 325 | 
            +
                    Degrees.new(v)
         | 
| 326 | 
            +
                  rescue => ex
         | 
| 327 | 
            +
                    EXIFR.logger.warn("malformed GPS degrees: #{ex}")
         | 
| 328 | 
            +
                    nil
         | 
| 329 | 
            +
                  end
         | 
| 330 | 
            +
                end
         | 
| 331 | 
            +
             | 
| 303 332 | 
             
                class Degrees < Array
         | 
| 304 333 | 
             
                  def initialize(arr)
         | 
| 305 | 
            -
                    raise  | 
| 334 | 
            +
                    raise "expected [degrees, minutes, seconds]" unless arr.length == 3
         | 
| 306 335 | 
             
                    super
         | 
| 307 336 | 
             
                  end
         | 
| 308 337 |  | 
| @@ -312,7 +341,13 @@ module EXIFR | |
| 312 341 | 
             
                end
         | 
| 313 342 |  | 
| 314 343 | 
             
                def self.rational(n, d)
         | 
| 315 | 
            -
                   | 
| 344 | 
            +
                  if d == 0
         | 
| 345 | 
            +
                    n.to_f / d.to_f
         | 
| 346 | 
            +
                  elsif Rational.respond_to?(:reduce)
         | 
| 347 | 
            +
                    Rational.reduce(n, d)
         | 
| 348 | 
            +
                  else
         | 
| 349 | 
            +
                    n.quo(d)
         | 
| 350 | 
            +
                  end
         | 
| 316 351 | 
             
                end
         | 
| 317 352 |  | 
| 318 353 | 
             
                def self.round(f, n)
         | 
| @@ -325,19 +360,19 @@ module EXIFR | |
| 325 360 | 
             
                                  :date_time_original => time_proc,
         | 
| 326 361 | 
             
                                  :date_time_digitized => time_proc,
         | 
| 327 362 | 
             
                                  :date_time => time_proc,
         | 
| 328 | 
            -
                                  :orientation => proc { | | 
| 329 | 
            -
                                  :gps_latitude =>  | 
| 330 | 
            -
                                  :gps_longitude =>  | 
| 331 | 
            -
                                  :gps_dest_latitude =>  | 
| 332 | 
            -
                                  :gps_dest_longitude =>  | 
| 333 | 
            -
                                  :shutter_speed_value => proc { | | 
| 334 | 
            -
                                  :aperture_value => proc { | | 
| 363 | 
            +
                                  :orientation => proc { |x| x.map{|y| ORIENTATIONS[y]} },
         | 
| 364 | 
            +
                                  :gps_latitude => degrees_proc,
         | 
| 365 | 
            +
                                  :gps_longitude => degrees_proc,
         | 
| 366 | 
            +
                                  :gps_dest_latitude => degrees_proc,
         | 
| 367 | 
            +
                                  :gps_dest_longitude => degrees_proc,
         | 
| 368 | 
            +
                                  :shutter_speed_value => proc { |x| x.map { |y| y.abs < 100 ? rational(1, (2 ** y).to_i) : nil } },
         | 
| 369 | 
            +
                                  :aperture_value => proc { |x| x.map { |y| round(1.4142 ** y, 1) } }
         | 
| 335 370 | 
             
                                })
         | 
| 336 371 |  | 
| 337 372 | 
             
                # Names for all recognized TIFF fields.
         | 
| 338 | 
            -
                TAGS =  | 
| 373 | 
            +
                TAGS = [TAG_MAPPING.keys, TAG_MAPPING.values.map{|v|v.values}].flatten.uniq - IFD_TAGS
         | 
| 339 374 |  | 
| 340 | 
            -
                # +file+ is a filename or an IO object.  Hint: use StringIO when working with slurped data like blobs.
         | 
| 375 | 
            +
                # +file+ is a filename or an +IO+ object.  Hint: use +StringIO+ when working with slurped data like blobs.
         | 
| 341 376 | 
             
                def initialize(file)
         | 
| 342 377 | 
             
                  Data.open(file) do |data|
         | 
| 343 378 | 
             
                    @ifds = [IFD.new(data)]
         | 
| @@ -346,10 +381,15 @@ module EXIFR | |
| 346 381 | 
             
                      @ifds << ifd
         | 
| 347 382 | 
             
                    end
         | 
| 348 383 |  | 
| 349 | 
            -
                    @jpeg_thumbnails = @ifds.map do | | 
| 350 | 
            -
                      if  | 
| 351 | 
            -
                        start, length =  | 
| 352 | 
            -
                         | 
| 384 | 
            +
                    @jpeg_thumbnails = @ifds.map do |v|
         | 
| 385 | 
            +
                      if v.jpeg_interchange_format && v.jpeg_interchange_format_length
         | 
| 386 | 
            +
                        start, length = v.jpeg_interchange_format, v.jpeg_interchange_format_length
         | 
| 387 | 
            +
                        if Integer === start && Integer === length
         | 
| 388 | 
            +
                          data[start..(start + length)]
         | 
| 389 | 
            +
                        else
         | 
| 390 | 
            +
                          EXIFR.logger.warn("Non numeric JpegInterchangeFormat data")
         | 
| 391 | 
            +
                          nil
         | 
| 392 | 
            +
                        end
         | 
| 353 393 | 
             
                      end
         | 
| 354 394 | 
             
                    end.compact
         | 
| 355 395 | 
             
                  end
         | 
| @@ -376,21 +416,33 @@ module EXIFR | |
| 376 416 |  | 
| 377 417 | 
             
                  if @ifds.first.respond_to?(method)
         | 
| 378 418 | 
             
                    @ifds.first.send(method)
         | 
| 379 | 
            -
                  elsif TAGS.include?(method | 
| 419 | 
            +
                  elsif TAGS.include?(method)
         | 
| 380 420 | 
             
                    @ifds.first.to_hash[method]
         | 
| 381 421 | 
             
                  else
         | 
| 382 422 | 
             
                    super
         | 
| 383 423 | 
             
                  end
         | 
| 384 424 | 
             
                end
         | 
| 385 425 |  | 
| 386 | 
            -
                def respond_to?(method) # :nodoc:
         | 
| 426 | 
            +
                def respond_to?(method, include_all = false) # :nodoc:
         | 
| 387 427 | 
             
                  super ||
         | 
| 388 | 
            -
                    (defined?(@ifds) && @ifds && @ifds.first && @ifds.first.respond_to?(method)) ||
         | 
| 389 | 
            -
                    TAGS.include?(method | 
| 428 | 
            +
                    (defined?(@ifds) && @ifds && @ifds.first && @ifds.first.respond_to?(method, include_all)) ||
         | 
| 429 | 
            +
                    TAGS.include?(method)
         | 
| 430 | 
            +
                end
         | 
| 431 | 
            +
             | 
| 432 | 
            +
                def methods(regular=true) # :nodoc:
         | 
| 433 | 
            +
                  if regular
         | 
| 434 | 
            +
                    (super + TAGS + IFD.instance_methods(false)).uniq
         | 
| 435 | 
            +
                  else
         | 
| 436 | 
            +
                    super
         | 
| 437 | 
            +
                  end
         | 
| 390 438 | 
             
                end
         | 
| 391 439 |  | 
| 392 | 
            -
                def  | 
| 393 | 
            -
                   | 
| 440 | 
            +
                def encode_with(coder)
         | 
| 441 | 
            +
                  coder["ifds"] = @ifds
         | 
| 442 | 
            +
                end
         | 
| 443 | 
            +
             | 
| 444 | 
            +
                def to_yaml_properties
         | 
| 445 | 
            +
                  ['@ifds']
         | 
| 394 446 | 
             
                end
         | 
| 395 447 |  | 
| 396 448 | 
             
                class << self
         | 
| @@ -414,9 +466,12 @@ module EXIFR | |
| 414 466 | 
             
                # Get GPS location, altitude and image direction return nil when not available.
         | 
| 415 467 | 
             
                def gps
         | 
| 416 468 | 
             
                  return nil unless gps_latitude && gps_longitude
         | 
| 469 | 
            +
             | 
| 470 | 
            +
                  altitude = gps_altitude.is_a?(Array) ? gps_altitude.first : gps_altitude
         | 
| 471 | 
            +
             | 
| 417 472 | 
             
                  GPS.new(gps_latitude.to_f * (gps_latitude_ref == 'S' ? -1 : 1),
         | 
| 418 473 | 
             
                          gps_longitude.to_f * (gps_longitude_ref == 'W' ? -1 : 1),
         | 
| 419 | 
            -
                           | 
| 474 | 
            +
                          altitude && (altitude.to_f * (gps_altitude_ref == "\1" ? -1 : 1)),
         | 
| 420 475 | 
             
                          gps_img_direction && gps_img_direction.to_f)
         | 
| 421 476 | 
             
                end
         | 
| 422 477 |  | 
| @@ -432,20 +487,23 @@ module EXIFR | |
| 432 487 |  | 
| 433 488 | 
             
                    pos = offset || @data.readlong(4)
         | 
| 434 489 | 
             
                    num = @data.readshort(pos)
         | 
| 435 | 
            -
                    pos += 2
         | 
| 436 490 |  | 
| 437 | 
            -
                    num | 
| 438 | 
            -
                       | 
| 439 | 
            -
             | 
| 440 | 
            -
             | 
| 491 | 
            +
                    if pos && num
         | 
| 492 | 
            +
                      pos += 2
         | 
| 493 | 
            +
             | 
| 494 | 
            +
                      num.times do
         | 
| 495 | 
            +
                        add_field(Field.new(@data, pos))
         | 
| 496 | 
            +
                        pos += 12
         | 
| 497 | 
            +
                      end
         | 
| 441 498 |  | 
| 442 | 
            -
             | 
| 443 | 
            -
             | 
| 444 | 
            -
             | 
| 499 | 
            +
                      @offset_next = @data.readlong(pos)
         | 
| 500 | 
            +
                    end
         | 
| 501 | 
            +
                  rescue => ex
         | 
| 502 | 
            +
                    EXIFR.logger.warn("Badly formed IFD: #{ex}")
         | 
| 445 503 | 
             
                  end
         | 
| 446 504 |  | 
| 447 505 | 
             
                  def method_missing(method, *args)
         | 
| 448 | 
            -
                    super unless args.empty? && TAGS.include?(method | 
| 506 | 
            +
                    super unless args.empty? && TAGS.include?(method)
         | 
| 449 507 | 
             
                    to_hash[method]
         | 
| 450 508 | 
             
                  end
         | 
| 451 509 |  | 
| @@ -469,13 +527,17 @@ module EXIFR | |
| 469 527 | 
             
                  end
         | 
| 470 528 |  | 
| 471 529 | 
             
                  def next?
         | 
| 472 | 
            -
                    @offset_next  | 
| 530 | 
            +
                    @offset_next && @offset_next > 0 && @offset_next < @data.size
         | 
| 473 531 | 
             
                  end
         | 
| 474 532 |  | 
| 475 533 | 
             
                  def next
         | 
| 476 534 | 
             
                    IFD.new(@data, @offset_next) if next?
         | 
| 477 535 | 
             
                  end
         | 
| 478 536 |  | 
| 537 | 
            +
                  def encode_with(coder)
         | 
| 538 | 
            +
                    coder["fields"] = @fields
         | 
| 539 | 
            +
                  end
         | 
| 540 | 
            +
             | 
| 479 541 | 
             
                  def to_yaml_properties
         | 
| 480 542 | 
             
                    ['@fields']
         | 
| 481 543 | 
             
                  end
         | 
| @@ -507,7 +569,7 @@ module EXIFR | |
| 507 569 | 
             
                    when 6 # signed byte
         | 
| 508 570 | 
             
                      len, pack = count, proc { |d| sign_byte(d) }
         | 
| 509 571 | 
             
                    when 2 # ascii
         | 
| 510 | 
            -
                      len, pack = count, proc { |d| d.unpack( | 
| 572 | 
            +
                      len, pack = count, proc { |d| d.unpack('Z*') }
         | 
| 511 573 | 
             
                    when 3 # short
         | 
| 512 574 | 
             
                      len, pack = count * 2, proc { |d| d.unpack(data.short + '*') }
         | 
| 513 575 | 
             
                    when 8 # signed short
         | 
| @@ -540,6 +602,8 @@ module EXIFR | |
| 540 602 | 
             
                        end
         | 
| 541 603 | 
             
                        rationals
         | 
| 542 604 | 
             
                      end
         | 
| 605 | 
            +
                    else
         | 
| 606 | 
            +
                      return
         | 
| 543 607 | 
             
                    end
         | 
| 544 608 |  | 
| 545 609 | 
             
                    if len && pack && @type != 7
         | 
    
        data/lib/exifr.rb
    CHANGED
    
    | @@ -1,10 +1,12 @@ | |
| 1 | 
            -
            # Copyright (c) 2006 | 
| 1 | 
            +
            # Copyright (c) 2006-2020 - R.W. van 't Veer
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'logger'
         | 
| 2 4 |  | 
| 3 5 | 
             
            module EXIFR
         | 
| 4 6 | 
             
              class MalformedImage < StandardError; end
         | 
| 5 7 | 
             
              class MalformedJPEG < MalformedImage; end
         | 
| 6 8 | 
             
              class MalformedTIFF < MalformedImage; end
         | 
| 7 | 
            -
            end
         | 
| 8 9 |  | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 10 | 
            +
              class << self; attr_accessor :logger; end
         | 
| 11 | 
            +
              self.logger = Logger.new(STDERR)
         | 
| 12 | 
            +
            end
         | 
| Binary file | 
| Binary file | 
| Binary file | 
| Binary file | 
    
        data/tests/jpeg_test.rb
    CHANGED
    
    | @@ -1,27 +1,28 @@ | |
| 1 1 | 
             
            #!/usr/bin/env ruby
         | 
| 2 2 | 
             
            #
         | 
| 3 | 
            -
            # Copyright (c) 2006 | 
| 3 | 
            +
            # Copyright (c) 2006-2020 - R.W. van 't Veer
         | 
| 4 4 |  | 
| 5 5 | 
             
            require 'test_helper'
         | 
| 6 6 |  | 
| 7 | 
            -
            class JPEGTest <  | 
| 7 | 
            +
            class JPEGTest < TestCase
         | 
| 8 8 | 
             
              def test_initialize
         | 
| 9 9 | 
             
                all_test_jpegs.each do |fname|
         | 
| 10 | 
            -
                   | 
| 11 | 
            -
             | 
| 12 | 
            -
                   | 
| 13 | 
            -
                  assert_nothing_raised do
         | 
| 14 | 
            -
                    open(fname) { |rd| JPEG.new(rd) }
         | 
| 15 | 
            -
                  end
         | 
| 16 | 
            -
                  assert_nothing_raised do
         | 
| 17 | 
            -
                    JPEG.new(StringIO.new(File.read(fname)))
         | 
| 18 | 
            -
                  end
         | 
| 10 | 
            +
                  assert JPEG.new(fname)
         | 
| 11 | 
            +
                  open(fname) { |rd| assert JPEG.new(rd) }
         | 
| 12 | 
            +
                  assert JPEG.new(StringIO.new(File.read(fname)))
         | 
| 19 13 | 
             
                end
         | 
| 20 14 | 
             
              end
         | 
| 21 15 |  | 
| 22 16 | 
             
              def test_raises_malformed_jpeg
         | 
| 23 | 
            -
                 | 
| 17 | 
            +
                begin
         | 
| 18 | 
            +
                  JPEG.new(StringIO.new(""))
         | 
| 19 | 
            +
                rescue MalformedJPEG => ex
         | 
| 20 | 
            +
                  assert ex
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
                begin
         | 
| 24 23 | 
             
                  JPEG.new(StringIO.new("djibberish"))
         | 
| 24 | 
            +
                rescue MalformedJPEG => ex
         | 
| 25 | 
            +
                  assert ex
         | 
| 25 26 | 
             
                end
         | 
| 26 27 | 
             
              end
         | 
| 27 28 |  | 
| @@ -64,8 +65,8 @@ class JPEGTest < Test::Unit::TestCase | |
| 64 65 | 
             
              def test_exif
         | 
| 65 66 | 
             
                assert ! JPEG.new(f('image.jpg')).exif?
         | 
| 66 67 | 
             
                assert JPEG.new(f('exif.jpg')).exif?
         | 
| 67 | 
            -
                 | 
| 68 | 
            -
                 | 
| 68 | 
            +
                assert JPEG.new(f('exif.jpg')).exif.date_time
         | 
| 69 | 
            +
                assert JPEG.new(f('exif.jpg')).exif.f_number
         | 
| 69 70 | 
             
              end
         | 
| 70 71 |  | 
| 71 72 | 
             
              def test_to_hash
         | 
| @@ -82,21 +83,31 @@ class JPEGTest < Test::Unit::TestCase | |
| 82 83 |  | 
| 83 84 | 
             
              def test_exif_dispatch
         | 
| 84 85 | 
             
                j = JPEG.new(f('exif.jpg'))
         | 
| 85 | 
            -
             | 
| 86 | 
            -
                assert  | 
| 87 | 
            -
                assert j.methods.include?('date_time')
         | 
| 86 | 
            +
                assert JPEG.instance_methods.include?(:date_time)
         | 
| 87 | 
            +
                assert j.methods.include?(:date_time)
         | 
| 88 88 | 
             
                assert j.respond_to?(:date_time)
         | 
| 89 | 
            -
                assert j. | 
| 90 | 
            -
                assert_not_nil j.date_time
         | 
| 89 | 
            +
                assert j.date_time
         | 
| 91 90 | 
             
                assert_kind_of Time, j.date_time
         | 
| 92 91 |  | 
| 93 | 
            -
                 | 
| 92 | 
            +
                assert j.f_number
         | 
| 94 93 | 
             
                assert_kind_of Rational, j.f_number
         | 
| 95 94 | 
             
              end
         | 
| 96 95 |  | 
| 97 96 | 
             
              def test_no_method_error
         | 
| 98 | 
            -
                 | 
| 99 | 
            -
             | 
| 97 | 
            +
                JPEG.new(f('image.jpg')).f_number
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                begin
         | 
| 100 | 
            +
                  JPEG.new(f('image.jpg')).foo
         | 
| 101 | 
            +
                rescue NoMethodError => ex
         | 
| 102 | 
            +
                  assert ex
         | 
| 103 | 
            +
                end
         | 
| 104 | 
            +
              end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
              def test_methods_method
         | 
| 107 | 
            +
                j = JPEG.new(f('exif.jpg'))
         | 
| 108 | 
            +
                assert j.methods.include?(:date_time)
         | 
| 109 | 
            +
                assert j.methods(true).include?(:date_time)
         | 
| 110 | 
            +
                assert ! j.methods(false).include?(:date_time)
         | 
| 100 111 | 
             
              end
         | 
| 101 112 |  | 
| 102 113 | 
             
              def test_multiple_app1
         | 
| @@ -108,9 +119,7 @@ class JPEGTest < Test::Unit::TestCase | |
| 108 119 | 
             
                all_test_jpegs.each do |fname|
         | 
| 109 120 | 
             
                  jpeg = JPEG.new(fname)
         | 
| 110 121 | 
             
                  unless jpeg.thumbnail.nil?
         | 
| 111 | 
            -
                     | 
| 112 | 
            -
                      JPEG.new(StringIO.new(jpeg.thumbnail))
         | 
| 113 | 
            -
                    end
         | 
| 122 | 
            +
                    assert JPEG.new(StringIO.new(jpeg.thumbnail))
         | 
| 114 123 | 
             
                    count += 1
         | 
| 115 124 | 
             
                  end
         | 
| 116 125 | 
             
                end
         | 
| @@ -118,4 +127,28 @@ class JPEGTest < Test::Unit::TestCase | |
| 118 127 | 
             
                assert count > 0, 'no thumbnails found'
         | 
| 119 128 | 
             
              end
         | 
| 120 129 |  | 
| 130 | 
            +
              def test_gps_with_altitude
         | 
| 131 | 
            +
                t = JPEG.new(f('gps-altitude.jpg'))
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                assert_equal([Rational(230, 1), Rational(0, 1), Rational(0, 1)], t.gps_altitude)
         | 
| 134 | 
            +
                assert_equal(230, t.gps.altitude)
         | 
| 135 | 
            +
              end
         | 
| 136 | 
            +
             | 
| 137 | 
            +
              def test_exif_datetime_milliseconds
         | 
| 138 | 
            +
                if Time.now.strftime('%L') == '%L'
         | 
| 139 | 
            +
                  STDERR.puts("skipping milliseconds test; not supported on this platform")
         | 
| 140 | 
            +
                  return
         | 
| 141 | 
            +
                end
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                j = JPEG.new(f('exif.jpg'))
         | 
| 144 | 
            +
                assert_equal('000', j.date_time.strftime('%L'))
         | 
| 145 | 
            +
                assert_equal('000', j.date_time_original.strftime('%L'))
         | 
| 146 | 
            +
                assert_equal('000', j.date_time_digitized.strftime('%L'))
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                j = JPEG.new(f('ios-mspix-milliseconds.jpg'))
         | 
| 149 | 
            +
                assert_equal('978', j.date_time.strftime('%L'))
         | 
| 150 | 
            +
                assert_equal('978', j.date_time_original.strftime('%L'))
         | 
| 151 | 
            +
                assert_equal('978', j.date_time_digitized.strftime('%L'))
         | 
| 152 | 
            +
              end
         | 
| 153 | 
            +
             | 
| 121 154 | 
             
            end
         | 
    
        data/tests/test_helper.rb
    CHANGED
    
    | @@ -1,21 +1,39 @@ | |
| 1 1 | 
             
            #!/usr/bin/env ruby
         | 
| 2 2 | 
             
            #
         | 
| 3 | 
            -
            # Copyright (c) 2006 | 
| 3 | 
            +
            # Copyright (c) 2006-2020 - R.W. van 't Veer
         | 
| 4 4 |  | 
| 5 | 
            -
            require 'test/unit'
         | 
| 6 5 | 
             
            require 'stringio'
         | 
| 7 6 | 
             
            require 'pp'
         | 
| 8 7 |  | 
| 8 | 
            +
            TestCase = begin
         | 
| 9 | 
            +
                         tc = begin
         | 
| 10 | 
            +
                                gem 'minitest' rescue nil
         | 
| 11 | 
            +
                                require 'minitest/autorun'
         | 
| 12 | 
            +
                                case
         | 
| 13 | 
            +
                                when defined?(Minitest::Test) ; Minitest::Test
         | 
| 14 | 
            +
                                when defined?(Minitest::Unit::TestCase) ; Minitest::Unit::TestCase
         | 
| 15 | 
            +
                                end
         | 
| 16 | 
            +
                              rescue LoadError
         | 
| 17 | 
            +
                                # nop
         | 
| 18 | 
            +
                              end
         | 
| 19 | 
            +
                         unless tc
         | 
| 20 | 
            +
                           require "test/unit"
         | 
| 21 | 
            +
                           tc = Test::Unit::TestCase
         | 
| 22 | 
            +
                         end
         | 
| 23 | 
            +
                         tc
         | 
| 24 | 
            +
                       end
         | 
| 25 | 
            +
             | 
| 9 26 | 
             
            $:.unshift("#{File.dirname(__FILE__)}/../lib")
         | 
| 10 | 
            -
            require 'exifr'
         | 
| 27 | 
            +
            require 'exifr/jpeg'
         | 
| 28 | 
            +
            require 'exifr/tiff'
         | 
| 11 29 | 
             
            include EXIFR
         | 
| 12 30 |  | 
| 31 | 
            +
            EXIFR.logger = Logger.new(StringIO.new)
         | 
| 13 32 |  | 
| 14 33 | 
             
            def all_test_jpegs
         | 
| 15 34 | 
             
              Dir[f('*.jpg')]
         | 
| 16 35 | 
             
            end
         | 
| 17 36 |  | 
| 18 | 
            -
             | 
| 19 37 | 
             
            def all_test_exifs
         | 
| 20 38 | 
             
              Dir[f('*.exif')]
         | 
| 21 39 | 
             
            end
         | 
    
        data/tests/tiff_test.rb
    CHANGED
    
    | @@ -1,31 +1,27 @@ | |
| 1 1 | 
             
            #!/usr/bin/env ruby
         | 
| 2 2 | 
             
            #
         | 
| 3 | 
            -
            # Copyright (c) 2006 | 
| 3 | 
            +
            # Copyright (c) 2006-2020 - R.W. van 't Veer
         | 
| 4 4 |  | 
| 5 5 | 
             
            require 'test_helper'
         | 
| 6 6 |  | 
| 7 | 
            -
            class TIFFTest <  | 
| 7 | 
            +
            class TIFFTest < TestCase
         | 
| 8 8 | 
             
              def setup
         | 
| 9 9 | 
             
                @t = TIFF.new(f('nikon_d1x.tif'))
         | 
| 10 10 | 
             
              end
         | 
| 11 11 |  | 
| 12 12 | 
             
              def test_initialize
         | 
| 13 13 | 
             
                all_test_tiffs.each do |fname|
         | 
| 14 | 
            -
                   | 
| 15 | 
            -
             | 
| 16 | 
            -
                   | 
| 17 | 
            -
                  assert_nothing_raised do
         | 
| 18 | 
            -
                    open(fname) { |rd| TIFF.new(rd) }
         | 
| 19 | 
            -
                  end
         | 
| 20 | 
            -
                  assert_nothing_raised do
         | 
| 21 | 
            -
                    TIFF.new(StringIO.new(File.read(fname)))
         | 
| 22 | 
            -
                  end
         | 
| 14 | 
            +
                  assert TIFF.new(fname)
         | 
| 15 | 
            +
                  open(fname) { |rd| assert TIFF.new(rd) }
         | 
| 16 | 
            +
                  assert TIFF.new(StringIO.new(File.read(fname)))
         | 
| 23 17 | 
             
                end
         | 
| 24 18 | 
             
              end
         | 
| 25 | 
            -
             | 
| 19 | 
            +
             | 
| 26 20 | 
             
              def test_raises_malformed_tiff
         | 
| 27 | 
            -
                 | 
| 21 | 
            +
                begin
         | 
| 28 22 | 
             
                  TIFF.new(StringIO.new("djibberish"))
         | 
| 23 | 
            +
                rescue MalformedTIFF => ex
         | 
| 24 | 
            +
                  assert ex
         | 
| 29 25 | 
             
                end
         | 
| 30 26 | 
             
              end
         | 
| 31 27 |  | 
| @@ -59,12 +55,20 @@ class TIFFTest < Test::Unit::TestCase | |
| 59 55 | 
             
              end
         | 
| 60 56 |  | 
| 61 57 | 
             
              def test_dates
         | 
| 62 | 
            -
                (all_test_tiffs - [f('weird_date.exif'), f('plain.tif'), f('endless-loop.exif')]).each do |fname|
         | 
| 58 | 
            +
                (all_test_tiffs - [f('weird_date.exif'), f('plain.tif'), f('endless-loop.exif'), f('truncated.exif')]).each do |fname|
         | 
| 63 59 | 
             
                  assert_kind_of Time, TIFF.new(fname).date_time
         | 
| 64 60 | 
             
                end
         | 
| 65 61 | 
             
                assert_nil TIFF.new(f('weird_date.exif')).date_time
         | 
| 66 62 | 
             
              end
         | 
| 67 63 |  | 
| 64 | 
            +
              def test_time_with_zone
         | 
| 65 | 
            +
                old_proc = TIFF.mktime_proc
         | 
| 66 | 
            +
                TIFF.mktime_proc = proc { |*args| "TIME-WITH-ZONE" }
         | 
| 67 | 
            +
                assert_equal "TIME-WITH-ZONE", TIFF.new(f('nikon_d1x.tif')).date_time
         | 
| 68 | 
            +
              ensure
         | 
| 69 | 
            +
                TIFF.mktime_proc = old_proc
         | 
| 70 | 
            +
              end
         | 
| 71 | 
            +
             | 
| 68 72 | 
             
              def test_orientation
         | 
| 69 73 | 
             
                tested = 0 # count tests because not all exif samples have an orientation field
         | 
| 70 74 | 
             
                all_test_exifs.each do |fname|
         | 
| @@ -80,6 +84,16 @@ class TIFFTest < Test::Unit::TestCase | |
| 80 84 | 
             
                      TIFF::RightBottomOrientation,
         | 
| 81 85 | 
             
                      TIFF::LeftBottomOrientation
         | 
| 82 86 | 
             
                    ].any? { |c| orientation == c }, 'not an orientation'
         | 
| 87 | 
            +
                    assert [
         | 
| 88 | 
            +
                      :TopLeft,
         | 
| 89 | 
            +
                      :TopRight,
         | 
| 90 | 
            +
                      :BottomRight,
         | 
| 91 | 
            +
                      :BottomLeft,
         | 
| 92 | 
            +
                      :LeftTop,
         | 
| 93 | 
            +
                      :RightTop,
         | 
| 94 | 
            +
                      :RightBottom,
         | 
| 95 | 
            +
                      :LeftBottom
         | 
| 96 | 
            +
                    ].any? { |c| orientation.to_sym == c }, 'not an orientation symbol'
         | 
| 83 97 | 
             
                    assert orientation.respond_to?(:to_i)
         | 
| 84 98 | 
             
                    assert orientation.respond_to?(:transform_rmagick)
         | 
| 85 99 | 
             
                    tested += 1
         | 
| @@ -98,30 +112,33 @@ class TIFFTest < Test::Unit::TestCase | |
| 98 112 | 
             
                assert_equal('WGS84', t.gps_map_datum)
         | 
| 99 113 | 
             
                assert_equal(54, t.gps.latitude.round)
         | 
| 100 114 | 
             
                assert_equal(-7, t.gps.longitude.round)
         | 
| 115 | 
            +
                assert_nil(t.gps.altitude)
         | 
| 101 116 |  | 
| 102 117 | 
             
                (all_test_exifs - %w(gps user-comment out-of-range negative-exposure-bias-value).map{|v| f("#{v}.exif")}).each do |fname|
         | 
| 103 118 | 
             
                  assert_nil TIFF.new(fname).gps_version_id
         | 
| 104 119 | 
             
                end
         | 
| 105 120 | 
             
              end
         | 
| 106 121 |  | 
| 122 | 
            +
              def test_lens_model
         | 
| 123 | 
            +
                t = TIFF.new(f('sony-a7ii.exif'))
         | 
| 124 | 
            +
                assert_equal('FE 16-35mm F4 ZA OSS', t.lens_model)
         | 
| 125 | 
            +
              end
         | 
| 126 | 
            +
             | 
| 107 127 | 
             
              def test_ifd_dispatch
         | 
| 108 128 | 
             
                assert @t.respond_to?(:f_number)
         | 
| 109 | 
            -
                assert @t. | 
| 110 | 
            -
                assert  | 
| 111 | 
            -
                assert TIFF.instance_methods.include?('f_number')
         | 
| 129 | 
            +
                assert @t.methods.include?(:f_number)
         | 
| 130 | 
            +
                assert TIFF.instance_methods.include?(:f_number)
         | 
| 112 131 |  | 
| 113 | 
            -
                 | 
| 132 | 
            +
                assert @t.f_number
         | 
| 114 133 | 
             
                assert_kind_of Rational, @t.f_number
         | 
| 115 | 
            -
                 | 
| 134 | 
            +
                assert @t[0].f_number
         | 
| 116 135 | 
             
                assert_kind_of Rational, @t[0].f_number
         | 
| 117 136 | 
             
              end
         | 
| 118 137 |  | 
| 119 138 | 
             
              def test_avoid_dispatch_to_nonexistent_ifds
         | 
| 120 | 
            -
                 | 
| 121 | 
            -
                   | 
| 122 | 
            -
             | 
| 123 | 
            -
                    TIFF::TAGS.each { |tag| t.send(tag) }
         | 
| 124 | 
            -
                  end
         | 
| 139 | 
            +
                all_test_tiffs.each do |fname|
         | 
| 140 | 
            +
                  assert t = TIFF.new(fname)
         | 
| 141 | 
            +
                  assert TIFF::TAGS.map { |tag| t.send(tag) }
         | 
| 125 142 | 
             
                end
         | 
| 126 143 | 
             
              end
         | 
| 127 144 |  | 
| @@ -135,9 +152,7 @@ class TIFFTest < Test::Unit::TestCase | |
| 135 152 | 
             
              end
         | 
| 136 153 |  | 
| 137 154 | 
             
              def test_old_style
         | 
| 138 | 
            -
                 | 
| 139 | 
            -
                  assert_not_nil @t[:f_number]
         | 
| 140 | 
            -
                end
         | 
| 155 | 
            +
                assert @t[:f_number]
         | 
| 141 156 | 
             
              end
         | 
| 142 157 |  | 
| 143 158 | 
             
              def test_yaml_dump_and_load
         | 
| @@ -155,10 +170,8 @@ class TIFFTest < Test::Unit::TestCase | |
| 155 170 | 
             
                all_test_tiffs.each do |fname|
         | 
| 156 171 | 
             
                  t = TIFF.new(fname)
         | 
| 157 172 | 
             
                  unless t.jpeg_thumbnails.empty?
         | 
| 158 | 
            -
                     | 
| 159 | 
            -
                       | 
| 160 | 
            -
                        JPEG.new(StringIO.new(n))
         | 
| 161 | 
            -
                      end
         | 
| 173 | 
            +
                    t.jpeg_thumbnails.each do |n|
         | 
| 174 | 
            +
                      assert JPEG.new(StringIO.new(n))
         | 
| 162 175 | 
             
                    end
         | 
| 163 176 | 
             
                    count += 1
         | 
| 164 177 | 
             
                  end
         | 
| @@ -171,17 +184,31 @@ class TIFFTest < Test::Unit::TestCase | |
| 171 184 | 
             
                assert true
         | 
| 172 185 | 
             
              end
         | 
| 173 186 |  | 
| 187 | 
            +
              def test_should_read_truncated
         | 
| 188 | 
            +
                TIFF.new(f('truncated.exif'))
         | 
| 189 | 
            +
                assert true
         | 
| 190 | 
            +
              end
         | 
| 191 | 
            +
             | 
| 174 192 | 
             
              def test_user_comment
         | 
| 175 193 | 
             
                assert_equal("Manassas Battlefield", TIFF.new(f('user-comment.exif')).user_comment)
         | 
| 176 194 | 
             
              end
         | 
| 177 195 |  | 
| 178 196 | 
             
              def test_handle_out_of_range_offset
         | 
| 179 | 
            -
                 | 
| 180 | 
            -
                  assert 'NIKON', TIFF.new(f('out-of-range.exif')).make
         | 
| 181 | 
            -
                end
         | 
| 197 | 
            +
                assert_equal 'NIKON', TIFF.new(f('out-of-range.exif')).make
         | 
| 182 198 | 
             
              end
         | 
| 183 | 
            -
             | 
| 199 | 
            +
             | 
| 184 200 | 
             
              def test_negative_exposure_bias_value
         | 
| 185 201 | 
             
                assert_equal(-1.quo(3), TIFF.new(f('negative-exposure-bias-value.exif')).exposure_bias_value)
         | 
| 186 202 | 
             
              end
         | 
| 203 | 
            +
             | 
| 204 | 
            +
              def test_nul_terminated_strings
         | 
| 205 | 
            +
                assert_equal 'GoPro', TIFF.new(f('gopro_hd2.exif')).make
         | 
| 206 | 
            +
              end
         | 
| 207 | 
            +
             | 
| 208 | 
            +
              def test_methods_method
         | 
| 209 | 
            +
                t = TIFF.new(f('gopro_hd2.exif'))
         | 
| 210 | 
            +
                assert t.methods.include?(:make)
         | 
| 211 | 
            +
                assert t.methods(true).include?(:make)
         | 
| 212 | 
            +
                assert ! t.methods(false).include?(:make)
         | 
| 213 | 
            +
              end
         | 
| 187 214 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,81 +1,110 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: exifr
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 1. | 
| 5 | 
            -
              prerelease: 
         | 
| 4 | 
            +
              version: 1.3.8
         | 
| 6 5 | 
             
            platform: ruby
         | 
| 7 6 | 
             
            authors:
         | 
| 8 7 | 
             
            - R.W. van 't Veer
         | 
| 9 | 
            -
            autorequire: | 
| 8 | 
            +
            autorequire:
         | 
| 10 9 | 
             
            bindir: bin
         | 
| 11 10 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date:  | 
| 13 | 
            -
            dependencies: | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 11 | 
            +
            date: 2020-09-24 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: test-unit
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - '='
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: 3.1.5
         | 
| 20 | 
            +
              type: :development
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - '='
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: 3.1.5
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: rake
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - "~>"
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: '12'
         | 
| 34 | 
            +
              type: :development
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - "~>"
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: '12'
         | 
| 41 | 
            +
            description: EXIF Reader is a module to read EXIF from JPEG and TIFF images.
         | 
| 42 | 
            +
            email: exifr@remworks.net
         | 
| 16 43 | 
             
            executables:
         | 
| 17 44 | 
             
            - exifr
         | 
| 18 45 | 
             
            extensions: []
         | 
| 19 | 
            -
            extra_rdoc_files:
         | 
| 20 | 
            -
            - README.rdoc
         | 
| 21 | 
            -
            - CHANGELOG
         | 
| 46 | 
            +
            extra_rdoc_files: []
         | 
| 22 47 | 
             
            files:
         | 
| 48 | 
            +
            - Gemfile
         | 
| 23 49 | 
             
            - Rakefile
         | 
| 24 50 | 
             
            - bin/exifr
         | 
| 25 51 | 
             
            - lib/exifr.rb
         | 
| 26 52 | 
             
            - lib/exifr/jpeg.rb
         | 
| 27 53 | 
             
            - lib/exifr/tiff.rb
         | 
| 28 54 | 
             
            - tests/data/1x1.jpg
         | 
| 29 | 
            -
            - tests/data/apple-aperture-1.5.exif
         | 
| 30 | 
            -
            - tests/data/canon-g3.exif
         | 
| 31 55 | 
             
            - tests/data/Canon_PowerShot_A85.exif
         | 
| 32 56 | 
             
            - tests/data/Casio-EX-S20.exif
         | 
| 57 | 
            +
            - tests/data/FUJIFILM-FinePix_S3000.exif
         | 
| 58 | 
            +
            - tests/data/Panasonic-DMC-LC33.exif
         | 
| 59 | 
            +
            - tests/data/Trust-DC3500_MINI.exif
         | 
| 60 | 
            +
            - tests/data/apple-aperture-1.5.exif
         | 
| 61 | 
            +
            - tests/data/bad-shutter_speed_value.exif
         | 
| 62 | 
            +
            - tests/data/canon-g3.exif
         | 
| 33 63 | 
             
            - tests/data/endless-loop.exif
         | 
| 34 64 | 
             
            - tests/data/exif.jpg
         | 
| 35 | 
            -
            - tests/data/ | 
| 65 | 
            +
            - tests/data/gopro_hd2.exif
         | 
| 66 | 
            +
            - tests/data/gps-altitude.jpg
         | 
| 36 67 | 
             
            - tests/data/gps.exif
         | 
| 37 68 | 
             
            - tests/data/image.jpg
         | 
| 38 69 | 
             
            - tests/data/multiple-app1.jpg
         | 
| 39 70 | 
             
            - tests/data/negative-exposure-bias-value.exif
         | 
| 40 71 | 
             
            - tests/data/nikon_d1x.tif
         | 
| 41 72 | 
             
            - tests/data/out-of-range.exif
         | 
| 42 | 
            -
            - tests/data/Panasonic-DMC-LC33.exif
         | 
| 43 73 | 
             
            - tests/data/plain.tif
         | 
| 44 | 
            -
            - tests/data/ | 
| 74 | 
            +
            - tests/data/samsung-sc-02b.jpg
         | 
| 75 | 
            +
            - tests/data/sony-a7ii.exif
         | 
| 45 76 | 
             
            - tests/data/user-comment.exif
         | 
| 46 77 | 
             
            - tests/data/weird_date.exif
         | 
| 47 | 
            -
            - tests/data/bad-shutter_speed_value.exif
         | 
| 48 78 | 
             
            - tests/jpeg_test.rb
         | 
| 49 79 | 
             
            - tests/test_helper.rb
         | 
| 50 80 | 
             
            - tests/tiff_test.rb
         | 
| 51 | 
            -
            - README.rdoc
         | 
| 52 | 
            -
            - CHANGELOG
         | 
| 53 81 | 
             
            homepage: http://github.com/remvee/exifr/
         | 
| 54 | 
            -
            licenses: | 
| 55 | 
            -
             | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 82 | 
            +
            licenses:
         | 
| 83 | 
            +
            - MIT
         | 
| 84 | 
            +
            metadata:
         | 
| 85 | 
            +
              bug_tracker_uri: https://github.com/remvee/exifr/issues
         | 
| 86 | 
            +
              changelog_uri: https://github.com/remvee/exifr/blob/master/CHANGELOG
         | 
| 87 | 
            +
              documentation_uri: https://remvee.github.io/exifr/api/
         | 
| 88 | 
            +
              homepage_uri: https://remvee.github.io/exifr/
         | 
| 89 | 
            +
              source_code_uri: https://github.com/remvee/exifr
         | 
| 90 | 
            +
            post_install_message:
         | 
| 91 | 
            +
            rdoc_options: []
         | 
| 61 92 | 
             
            require_paths:
         | 
| 62 93 | 
             
            - lib
         | 
| 63 94 | 
             
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 64 | 
            -
              none: false
         | 
| 65 95 | 
             
              requirements:
         | 
| 66 | 
            -
              - -  | 
| 96 | 
            +
              - - ">="
         | 
| 67 97 | 
             
                - !ruby/object:Gem::Version
         | 
| 68 | 
            -
                  version:  | 
| 98 | 
            +
                  version: 1.8.7
         | 
| 69 99 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 70 | 
            -
              none: false
         | 
| 71 100 | 
             
              requirements:
         | 
| 72 | 
            -
              - -  | 
| 101 | 
            +
              - - ">="
         | 
| 73 102 | 
             
                - !ruby/object:Gem::Version
         | 
| 74 103 | 
             
                  version: '0'
         | 
| 75 104 | 
             
            requirements: []
         | 
| 76 | 
            -
            rubyforge_project: | 
| 77 | 
            -
            rubygems_version:  | 
| 78 | 
            -
            signing_key: | 
| 79 | 
            -
            specification_version:  | 
| 80 | 
            -
            summary:  | 
| 105 | 
            +
            rubyforge_project:
         | 
| 106 | 
            +
            rubygems_version: 2.7.6
         | 
| 107 | 
            +
            signing_key:
         | 
| 108 | 
            +
            specification_version: 4
         | 
| 109 | 
            +
            summary: Read EXIF from JPEG and TIFF images
         | 
| 81 110 | 
             
            test_files: []
         | 
    
        data/CHANGELOG
    DELETED
    
    | @@ -1,108 +0,0 @@ | |
| 1 | 
            -
            EXIF Reader 1.1.3
         | 
| 2 | 
            -
            * bug fix; "[GH#27] some error when :shutter_speed_value is too big"; thanks to zamia
         | 
| 3 | 
            -
             | 
| 4 | 
            -
            EXIF Reader 1.1.2
         | 
| 5 | 
            -
            * bug fix; "[GH#25] Incorrect values being extracted for a number of fields"; thanks to Matthew McEachen
         | 
| 6 | 
            -
             | 
| 7 | 
            -
            EXIF Reader 1.1.1
         | 
| 8 | 
            -
            * feature; "added gps convenience method to make accessing location data easier (degrees to float conversion)"
         | 
| 9 | 
            -
            * bug fix; "[GH#22] Fix warnings about uninitialized @comment"; thanks to Ryan Greenberg"
         | 
| 10 | 
            -
             | 
| 11 | 
            -
            EXIF Reader 1.0.6
         | 
| 12 | 
            -
            * bug fix: "[GH#20] `readlong': undefined method `unpack' for nil:NilClass (NoMethodError)"
         | 
| 13 | 
            -
             | 
| 14 | 
            -
            EXIF Reader 1.0.5
         | 
| 15 | 
            -
            * bug fix: "[GH#19] files opened by TIFF initialize were not being closed"; thanks to Joe Van Overberghe
         | 
| 16 | 
            -
             | 
| 17 | 
            -
            EXIF Reader 1.0.4
         | 
| 18 | 
            -
            * bug fix: "[GH#17] avoid library search path pollution"
         | 
| 19 | 
            -
            * enhancement: "[GH#18] add EXIFR::JPEG#app1s method"
         | 
| 20 | 
            -
             | 
| 21 | 
            -
            EXIF Reader 1.0.3
         | 
| 22 | 
            -
            * enhancement; "raise specific exception to allow better error handling"; thanks to James Miller
         | 
| 23 | 
            -
             | 
| 24 | 
            -
            EXIF Reader 1.0.2
         | 
| 25 | 
            -
            * bug fix; "[GH#9/12] no block given"; thanks to Ian Leitch
         | 
| 26 | 
            -
             | 
| 27 | 
            -
            EXIF Reader 1.0.1
         | 
| 28 | 
            -
            * bug fix; "[GH#7] Unable to properly display exposure_bias_value"; thanks to John Krueger
         | 
| 29 | 
            -
             | 
| 30 | 
            -
            EXIF Reader 1.0.0
         | 
| 31 | 
            -
            * bug fix; "ArgumentError: invalid byte sequence in UTF-8" when reading messy field using Ruby 1.9+
         | 
| 32 | 
            -
            * enhancement; "[GH#4] more informative inspect for EXIFR::TIFF::Orientation instance"
         | 
| 33 | 
            -
            * bug fix; "[GH#6] ERROR RuntimeError: can't add a new key into hash during iteration"; thanks to KISHIMOTO, Makoto
         | 
| 34 | 
            -
             | 
| 35 | 
            -
            EXIF Reader 0.10.9
         | 
| 36 | 
            -
            * bug fix; "[GH#1] user_comment returns nil for jpeg with UserComment"; thanks to Mark Lundquist
         | 
| 37 | 
            -
            * bug fix; "[GH#2] Nil pointer in tiff.rb"
         | 
| 38 | 
            -
            * enhancement; "don't read entire files into memory"; thanks to Victor Bogado
         | 
| 39 | 
            -
             | 
| 40 | 
            -
            EXIF Reader 0.10.8
         | 
| 41 | 
            -
            * feature request; "[#23694] The object interface of JPEG is different from the TIFF one."
         | 
| 42 | 
            -
             | 
| 43 | 
            -
            EXIF Reader 0.10.7
         | 
| 44 | 
            -
            * bug fix; "[#22403] Wrong file size reported"
         | 
| 45 | 
            -
             | 
| 46 | 
            -
            EXIF Reader 0.10.6.1
         | 
| 47 | 
            -
            * moved to GitHub
         | 
| 48 | 
            -
             | 
| 49 | 
            -
            EXIF Reader 0.10.6
         | 
| 50 | 
            -
            * bug fix (thanks to Forian Munz for reporting it); endless loop when reading a malformed EXIF/TIFF
         | 
| 51 | 
            -
             | 
| 52 | 
            -
            EXIF Reader 0.10.5
         | 
| 53 | 
            -
            * bug fix; "[#15421] duplicate orientation field behavior", first field (of any type) is leading now
         | 
| 54 | 
            -
            * Ruby 1.9 compatible
         | 
| 55 | 
            -
             | 
| 56 | 
            -
            EXIF Reader 0.10.4
         | 
| 57 | 
            -
            * Thumbnail extraction; [#15317] Please add thumbnail extraction
         | 
| 58 | 
            -
             | 
| 59 | 
            -
            EXIF Reader 0.10.3
         | 
| 60 | 
            -
            * YAML friendly; can now safely (de)serialize
         | 
| 61 | 
            -
             | 
| 62 | 
            -
            EXIF Reader 0.10.2
         | 
| 63 | 
            -
            * bug fix (thanks to Alexander Staubo for providing me with sample data);
         | 
| 64 | 
            -
              don't fail on out-of-range IFD offsets for Apple Aperture generated JPGs
         | 
| 65 | 
            -
             | 
| 66 | 
            -
            EXIF Reader 0.10.1
         | 
| 67 | 
            -
            * old style exif access
         | 
| 68 | 
            -
             | 
| 69 | 
            -
            EXIF Reader 0.10
         | 
| 70 | 
            -
            * TIFF support
         | 
| 71 | 
            -
             | 
| 72 | 
            -
            EXIF Reader 0.9.6
         | 
| 73 | 
            -
            * bug fix; "[#8458] Conversion from string to Time fails", weird dates will now reflect nil
         | 
| 74 | 
            -
             | 
| 75 | 
            -
            EXIF Reader 0.9.5.1
         | 
| 76 | 
            -
            * make tinderbox happy by hiding rcov task
         | 
| 77 | 
            -
             | 
| 78 | 
            -
            EXIF Reader 0.9.5
         | 
| 79 | 
            -
            * patch calls to jpeg through to exif, i.e. jpeg., i.e. jpeg.model == jpeg.exif.model
         | 
| 80 | 
            -
            * fix exifr commandline utility, needs require 'exifr' now
         | 
| 81 | 
            -
            * improve test helper
         | 
| 82 | 
            -
            * reduce size of test images
         | 
| 83 | 
            -
            * include tests for tinderbox
         | 
| 84 | 
            -
             | 
| 85 | 
            -
            EXIF Reader 0.9.4
         | 
| 86 | 
            -
            * bug fix (thanks to Benjamin Storrier for providing me with sample date);
         | 
| 87 | 
            -
              multiple app1 frames will potentially overwrite EXIF tag
         | 
| 88 | 
            -
             | 
| 89 | 
            -
            EXIF Reader 0.9.3
         | 
| 90 | 
            -
            * bug fix; "[#4876] Unable to extract gpsinfo"
         | 
| 91 | 
            -
            * one-off bug in TiffHeader found and fixed
         | 
| 92 | 
            -
            * make "InteroperabilityIndex" available
         | 
| 93 | 
            -
             | 
| 94 | 
            -
            EXIF Reader 0.9.2
         | 
| 95 | 
            -
            * bug fix; "[#4595] EXIFR::JPEG doesn't support multiple comments", the
         | 
| 96 | 
            -
              comment property of a JPEG object now contains an array instead of a string
         | 
| 97 | 
            -
              when multiple COM frames are found
         | 
| 98 | 
            -
            * EXIF orientation modules including RMagick code to rotate to viewable state
         | 
| 99 | 
            -
            * access to thumbnail included in EXIF
         | 
| 100 | 
            -
            * simple commandline utility, "exifr", to view image properties
         | 
| 101 | 
            -
            * overall code improvements including documentation and tests
         | 
| 102 | 
            -
             | 
| 103 | 
            -
            EXIF Reader 0.9.1
         | 
| 104 | 
            -
            * bug fix; "4321 Can't create object", division by zero when
         | 
| 105 | 
            -
              denominator of rational value is zero
         | 
| 106 | 
            -
             | 
| 107 | 
            -
            EXIF Reader 0.9
         | 
| 108 | 
            -
            * 1st release
         | 
    
        data/README.rdoc
    DELETED
    
    | @@ -1,31 +0,0 @@ | |
| 1 | 
            -
            = EXIF Reader
         | 
| 2 | 
            -
            EXIF Reader is a module to read metadata from JPEG and TIFF images.
         | 
| 3 | 
            -
             | 
| 4 | 
            -
            == Examples
         | 
| 5 | 
            -
              EXIFR::JPEG.new('IMG_6841.JPG').width               # => 2272
         | 
| 6 | 
            -
              EXIFR::JPEG.new('IMG_6841.JPG').height              # => 1704
         | 
| 7 | 
            -
              EXIFR::JPEG.new('IMG_6841.JPG').exif?               # => true
         | 
| 8 | 
            -
              EXIFR::JPEG.new('IMG_6841.JPG').model               # => "Canon PowerShot G3"
         | 
| 9 | 
            -
              EXIFR::JPEG.new('IMG_6841.JPG').date_time           # => Fri Feb 09 16:48:54 +0100 2007
         | 
| 10 | 
            -
              EXIFR::JPEG.new('IMG_6841.JPG').exposure_time.to_s  # => "1/15"
         | 
| 11 | 
            -
              EXIFR::JPEG.new('IMG_6841.JPG').f_number.to_f       # => 2.0
         | 
| 12 | 
            -
             | 
| 13 | 
            -
              EXIFR::TIFF.new('DSC_0218.TIF').width               # => 3008
         | 
| 14 | 
            -
              EXIFR::TIFF.new('DSC_0218.TIF')[1].width            # => 160
         | 
| 15 | 
            -
              EXIFR::TIFF.new('DSC_0218.TIF').model               # => "NIKON D1X"
         | 
| 16 | 
            -
              EXIFR::TIFF.new('DSC_0218.TIF').date_time           # => Tue May 23 19:15:32 +0200 2006
         | 
| 17 | 
            -
              EXIFR::TIFF.new('DSC_0218.TIF').exposure_time.to_s  # => "1/100"
         | 
| 18 | 
            -
              EXIFR::TIFF.new('DSC_0218.TIF').f_number.to_f       # => 5.0
         | 
| 19 | 
            -
             | 
| 20 | 
            -
              EXIFR::JPEG.new('enkhuizen.jpg').gps.latitude       # => 52.7197888888889
         | 
| 21 | 
            -
              EXIFR::JPEG.new('enkhuizen.jpg').gps.longitude      # => 5.28397777777778
         | 
| 22 | 
            -
             | 
| 23 | 
            -
            == XMP data access
         | 
| 24 | 
            -
            If you need to access XMP data you can use the xmp gem.  More info and
         | 
| 25 | 
            -
            examples at https://github.com/amberbit/xmp
         | 
| 26 | 
            -
             | 
| 27 | 
            -
            == Author
         | 
| 28 | 
            -
            R.W. van 't Veer
         | 
| 29 | 
            -
             | 
| 30 | 
            -
            == Copyright
         | 
| 31 | 
            -
            Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011 - R.W. van 't Veer
         |