exifr 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 5b456bd64343b465f723f0778d5098ccd7c7f605
4
- data.tar.gz: '09e496e36851230684fac146dbcebe94a5247313'
2
+ SHA256:
3
+ metadata.gz: 19c3f1b08e84425d9d0b8a6e6ee11bd2e25190a49ce34d42ea99800f5030fe20
4
+ data.tar.gz: 9a834f93b1a02e87bf401b09a033b25fb8b05625b0a60a8795e9ee65e1ea064c
5
5
  SHA512:
6
- metadata.gz: c2223f73d2129e066f279dc4cc1fb49cb6f95fd8433745962cf47b8db6b04562ae8cc466378e378f7fdccfdad77b07ca62649818ce7b2840adf3d9f7465c4cb0
7
- data.tar.gz: 2476dd828a37c4434a07c8ba5e401de94be7937c8823001da9af7f7c6818aa8b39e43c41b79ad6fc7df89e7df6112a3ac9f7e825e38ffbc3b5e77e74416d8d65
6
+ metadata.gz: b4fc267041e9090a85eb5ca2a0bfc2fae3d36a72aa45ab716209e6e5e896a775957b65251587e16e7c4e3f0e3c2e491805ce226c68b9b189a8b5bdaf506ebc15
7
+ data.tar.gz: f39334aa7142a3b918edf30fa3bd6424ddc50c1906beb2c9d5bf384bc939fba35b410d2ee801410769929a29895e75b223d443af01d7acf7752fbf96adad0fa6
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "https://rubygems.org"
2
+ gemspec
data/Rakefile CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2006-2017 - R.W. van 't Veer
1
+ # Copyright (c) 2006-2020 - R.W. van 't Veer
2
2
 
3
3
  require 'rake/testtask'
4
4
 
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2006-2017 - R.W. van 't Veer
1
+ # Copyright (c) 2006-2020 - R.W. van 't Veer
2
2
 
3
3
  require 'logger'
4
4
 
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2006-2017 - R.W. van 't Veer
1
+ # Copyright (c) 2006-2020 - R.W. van 't Veer
2
2
 
3
3
  require 'exifr'
4
4
  require 'exifr/tiff'
@@ -66,8 +66,12 @@ module EXIFR
66
66
  super || methods.include?(method) || (include_all && private_methods.include?(method))
67
67
  end
68
68
 
69
- def methods # :nodoc:
70
- super + TIFF::TAGS << :gps
69
+ def methods(regular=true) # :nodoc:
70
+ if regular
71
+ super + TIFF::TAGS << :gps
72
+ else
73
+ super
74
+ end
71
75
  end
72
76
 
73
77
  class << self
@@ -237,6 +237,7 @@ module EXIFR
237
237
  0x001c => :gps_area_information,
238
238
  0x001d => :gps_date_stamp,
239
239
  0x001e => :gps_differential,
240
+ 0x001f => :gps_h_positioning_error
240
241
  },
241
242
  })
242
243
  IFD_TAGS = [:image, :exif, :gps] # :nodoc:
@@ -249,9 +250,9 @@ module EXIFR
249
250
 
250
251
  time_proc = proc do |value|
251
252
  value.map do |v|
252
- if v =~ /^(\d{4}):(\d\d):(\d\d) (\d\d):(\d\d):(\d\d)$/
253
+ if v =~ /^(\d{4}):(\d\d):(\d\d) (\d\d):(\d\d):(\d\d)(?:\.(\d{3}))?$/
253
254
  begin
254
- mktime_proc.call($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i)
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)
255
256
  rescue => ex
256
257
  EXIFR.logger.warn("Bad date/time value #{v.inspect}: #{ex}")
257
258
  nil
@@ -383,7 +384,12 @@ module EXIFR
383
384
  @jpeg_thumbnails = @ifds.map do |v|
384
385
  if v.jpeg_interchange_format && v.jpeg_interchange_format_length
385
386
  start, length = v.jpeg_interchange_format, v.jpeg_interchange_format_length
386
- data[start..(start + 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
387
393
  end
388
394
  end.compact
389
395
  end
@@ -423,8 +429,12 @@ module EXIFR
423
429
  TAGS.include?(method)
424
430
  end
425
431
 
426
- def methods # :nodoc:
427
- (super + TAGS + IFD.instance_methods(false)).uniq
432
+ def methods(regular=true) # :nodoc:
433
+ if regular
434
+ (super + TAGS + IFD.instance_methods(false)).uniq
435
+ else
436
+ super
437
+ end
428
438
  end
429
439
 
430
440
  def encode_with(coder)
@@ -477,17 +487,19 @@ module EXIFR
477
487
 
478
488
  pos = offset || @data.readlong(4)
479
489
  num = @data.readshort(pos)
480
- pos += 2
481
490
 
482
- num.times do
483
- add_field(Field.new(@data, pos))
484
- pos += 12
485
- end
491
+ if pos && num
492
+ pos += 2
486
493
 
487
- @offset_next = @data.readlong(pos)
494
+ num.times do
495
+ add_field(Field.new(@data, pos))
496
+ pos += 12
497
+ end
498
+
499
+ @offset_next = @data.readlong(pos)
500
+ end
488
501
  rescue => ex
489
502
  EXIFR.logger.warn("Badly formed IFD: #{ex}")
490
- @offset_next = 0
491
503
  end
492
504
 
493
505
  def method_missing(method, *args)
@@ -515,7 +527,7 @@ module EXIFR
515
527
  end
516
528
 
517
529
  def next?
518
- @offset_next != 0 && @offset_next < @data.size
530
+ @offset_next && @offset_next > 0 && @offset_next < @data.size
519
531
  end
520
532
 
521
533
  def next
@@ -590,6 +602,8 @@ module EXIFR
590
602
  end
591
603
  rationals
592
604
  end
605
+ else
606
+ return
593
607
  end
594
608
 
595
609
  if len && pack && @type != 7
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  #
3
- # Copyright (c) 2006-2017 - R.W. van 't Veer
3
+ # Copyright (c) 2006-2020 - R.W. van 't Veer
4
4
 
5
5
  require 'test_helper'
6
6
 
@@ -103,6 +103,13 @@ class JPEGTest < TestCase
103
103
  end
104
104
  end
105
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)
111
+ end
112
+
106
113
  def test_multiple_app1
107
114
  assert JPEG.new(f('multiple-app1.jpg')).exif?
108
115
  end
@@ -126,4 +133,22 @@ class JPEGTest < TestCase
126
133
  assert_equal([Rational(230, 1), Rational(0, 1), Rational(0, 1)], t.gps_altitude)
127
134
  assert_equal(230, t.gps.altitude)
128
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
+
129
154
  end
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  #
3
- # Copyright (c) 2006-2017 - R.W. van 't Veer
3
+ # Copyright (c) 2006-2020 - R.W. van 't Veer
4
4
 
5
5
  require 'stringio'
6
6
  require 'pp'
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  #
3
- # Copyright (c) 2006-2017 - R.W. van 't Veer
3
+ # Copyright (c) 2006-2020 - R.W. van 't Veer
4
4
 
5
5
  require 'test_helper'
6
6
 
@@ -55,7 +55,7 @@ class TIFFTest < TestCase
55
55
  end
56
56
 
57
57
  def test_dates
58
- (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|
59
59
  assert_kind_of Time, TIFF.new(fname).date_time
60
60
  end
61
61
  assert_nil TIFF.new(f('weird_date.exif')).date_time
@@ -184,6 +184,11 @@ class TIFFTest < TestCase
184
184
  assert true
185
185
  end
186
186
 
187
+ def test_should_read_truncated
188
+ TIFF.new(f('truncated.exif'))
189
+ assert true
190
+ end
191
+
187
192
  def test_user_comment
188
193
  assert_equal("Manassas Battlefield", TIFF.new(f('user-comment.exif')).user_comment)
189
194
  end
@@ -199,4 +204,11 @@ class TIFFTest < TestCase
199
204
  def test_nul_terminated_strings
200
205
  assert_equal 'GoPro', TIFF.new(f('gopro_hd2.exif')).make
201
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
202
214
  end
metadata CHANGED
@@ -1,26 +1,51 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: exifr
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.3
4
+ version: 1.3.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - R.W. van 't Veer
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-01 00:00:00.000000000 Z
12
- dependencies: []
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'
13
41
  description: EXIF Reader is a module to read EXIF from JPEG and TIFF images.
14
42
  email: exifr@remworks.net
15
43
  executables:
16
44
  - exifr
17
45
  extensions: []
18
- extra_rdoc_files:
19
- - README.rdoc
20
- - CHANGELOG
46
+ extra_rdoc_files: []
21
47
  files:
22
- - CHANGELOG
23
- - README.rdoc
48
+ - Gemfile
24
49
  - Rakefile
25
50
  - bin/exifr
26
51
  - lib/exifr.rb
@@ -56,13 +81,14 @@ files:
56
81
  homepage: http://github.com/remvee/exifr/
57
82
  licenses:
58
83
  - MIT
59
- metadata: {}
60
- post_install_message:
61
- rdoc_options:
62
- - "--title"
63
- - EXIF Reader for Ruby API Documentation
64
- - "--main"
65
- - README.rdoc
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: []
66
92
  require_paths:
67
93
  - lib
68
94
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -76,9 +102,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
102
  - !ruby/object:Gem::Version
77
103
  version: '0'
78
104
  requirements: []
79
- rubyforge_project:
80
- rubygems_version: 2.6.12
81
- signing_key:
105
+ rubyforge_project:
106
+ rubygems_version: 2.7.6
107
+ signing_key:
82
108
  specification_version: 4
83
109
  summary: Read EXIF from JPEG and TIFF images
84
110
  test_files: []
data/CHANGELOG DELETED
@@ -1,148 +0,0 @@
1
- EXIF Reader 1.3.3
2
- * bug fix; "Using first gps altitude value in case 'gps_altitude' is an Array"; thanks Antonio Marques
3
-
4
- EXIF Reader 1.3.2
5
- * enhancement; "Add EXIF 2.3 lens information tags"; thanks Clay Gregory
6
-
7
- EXIF Reader 1.3.1
8
- * fix executable bin/exifr
9
-
10
- EXIF Reader 1.3.0
11
- * workaround warnings when using the ruby "-w" option
12
- * NOTE: requiring only 'exifr' will nolonger provide EXIFR::JPEG or EXIFR::TIFF, require 'exifr/jpeg' or 'exifr/tiff' instead
13
-
14
- EXIF Reader 1.2.6
15
- * bug fix; "[GH#48] handle empty file as malformed jpeg"; thanks Hoàng Xuân Phú
16
-
17
- EXIF Reader 1.2.5
18
- * bug fix; "[GH#46] Having stringified methods is not a good idea"; thanks Filipe Costa
19
- * bug fix; "Fix typo in subsec_time_original key"; thanks Filipe Costa
20
-
21
- EXIF Reader 1.2.4
22
- * bug fix; "[GH#43] Fix `respond_to?` signature"; thanks Pete Johns
23
-
24
- EXIF Reader 1.2.3.1
25
- * bug fix; "[GH#41] gopro_hd2.exif is not included in the gem"; thanks Peter Wilmott
26
-
27
- EXIF Reader 1.2.3
28
- * bug fix; "[GH#40] Handle NUL-terminated ASCII strings"; thanks Malte Rohde
29
-
30
- EXIF Reader 1.2.2
31
- * bug fix; "use Reader delegator instead of adding method to File instance"; thanks Ivan Kuchin
32
-
33
- EXIF Reader 1.2.1
34
- * bug fix: Value of infinity yields division by zero error
35
-
36
- EXIF Reader 1.2.0
37
- * enhancement; "log warnings when EXIF data is malformed"
38
- * enhancement; "allow alternative Time adapter"
39
- * enhancement; "[GH#33] add reader for orientation type"; thanks Damir Svrtan
40
-
41
- EXIF Reader 1.1.3
42
- * bug fix; "[GH#27] some error when :shutter_speed_value is too big"; thanks to zamia
43
-
44
- EXIF Reader 1.1.2
45
- * bug fix; "[GH#25] Incorrect values being extracted for a number of fields"; thanks to Matthew McEachen
46
-
47
- EXIF Reader 1.1.1
48
- * feature; "added gps convenience method to make accessing location data easier (degrees to float conversion)"
49
- * bug fix; "[GH#22] Fix warnings about uninitialized @comment"; thanks to Ryan Greenberg"
50
-
51
- EXIF Reader 1.0.6
52
- * bug fix: "[GH#20] `readlong': undefined method `unpack' for nil:NilClass (NoMethodError)"
53
-
54
- EXIF Reader 1.0.5
55
- * bug fix: "[GH#19] files opened by TIFF initialize were not being closed"; thanks to Joe Van Overberghe
56
-
57
- EXIF Reader 1.0.4
58
- * bug fix: "[GH#17] avoid library search path pollution"
59
- * enhancement: "[GH#18] add EXIFR::JPEG#app1s method"
60
-
61
- EXIF Reader 1.0.3
62
- * enhancement; "raise specific exception to allow better error handling"; thanks to James Miller
63
-
64
- EXIF Reader 1.0.2
65
- * bug fix; "[GH#9/12] no block given"; thanks to Ian Leitch
66
-
67
- EXIF Reader 1.0.1
68
- * bug fix; "[GH#7] Unable to properly display exposure_bias_value"; thanks to John Krueger
69
-
70
- EXIF Reader 1.0.0
71
- * bug fix; "ArgumentError: invalid byte sequence in UTF-8" when reading messy field using Ruby 1.9+
72
- * enhancement; "[GH#4] more informative inspect for EXIFR::TIFF::Orientation instance"
73
- * bug fix; "[GH#6] ERROR RuntimeError: can't add a new key into hash during iteration"; thanks to KISHIMOTO, Makoto
74
-
75
- EXIF Reader 0.10.9
76
- * bug fix; "[GH#1] user_comment returns nil for jpeg with UserComment"; thanks to Mark Lundquist
77
- * bug fix; "[GH#2] Nil pointer in tiff.rb"
78
- * enhancement; "don't read entire files into memory"; thanks to Victor Bogado
79
-
80
- EXIF Reader 0.10.8
81
- * feature request; "[#23694] The object interface of JPEG is different from the TIFF one."
82
-
83
- EXIF Reader 0.10.7
84
- * bug fix; "[#22403] Wrong file size reported"
85
-
86
- EXIF Reader 0.10.6.1
87
- * moved to GitHub
88
-
89
- EXIF Reader 0.10.6
90
- * bug fix (thanks to Forian Munz for reporting it); endless loop when reading a malformed EXIF/TIFF
91
-
92
- EXIF Reader 0.10.5
93
- * bug fix; "[#15421] duplicate orientation field behavior", first field (of any type) is leading now
94
- * Ruby 1.9 compatible
95
-
96
- EXIF Reader 0.10.4
97
- * Thumbnail extraction; [#15317] Please add thumbnail extraction
98
-
99
- EXIF Reader 0.10.3
100
- * YAML friendly; can now safely (de)serialize
101
-
102
- EXIF Reader 0.10.2
103
- * bug fix (thanks to Alexander Staubo for providing me with sample data);
104
- don't fail on out-of-range IFD offsets for Apple Aperture generated JPGs
105
-
106
- EXIF Reader 0.10.1
107
- * old style exif access
108
-
109
- EXIF Reader 0.10
110
- * TIFF support
111
-
112
- EXIF Reader 0.9.6
113
- * bug fix; "[#8458] Conversion from string to Time fails", weird dates will now reflect nil
114
-
115
- EXIF Reader 0.9.5.1
116
- * make tinderbox happy by hiding rcov task
117
-
118
- EXIF Reader 0.9.5
119
- * patch calls to jpeg through to exif, i.e. jpeg., i.e. jpeg.model == jpeg.exif.model
120
- * fix exifr commandline utility, needs require 'exifr' now
121
- * improve test helper
122
- * reduce size of test images
123
- * include tests for tinderbox
124
-
125
- EXIF Reader 0.9.4
126
- * bug fix (thanks to Benjamin Storrier for providing me with sample date);
127
- multiple app1 frames will potentially overwrite EXIF tag
128
-
129
- EXIF Reader 0.9.3
130
- * bug fix; "[#4876] Unable to extract gpsinfo"
131
- * one-off bug in TiffHeader found and fixed
132
- * make "InteroperabilityIndex" available
133
-
134
- EXIF Reader 0.9.2
135
- * bug fix; "[#4595] EXIFR::JPEG doesn't support multiple comments", the
136
- comment property of a JPEG object now contains an array instead of a string
137
- when multiple COM frames are found
138
- * EXIF orientation modules including RMagick code to rotate to viewable state
139
- * access to thumbnail included in EXIF
140
- * simple commandline utility, "exifr", to view image properties
141
- * overall code improvements including documentation and tests
142
-
143
- EXIF Reader 0.9.1
144
- * bug fix; "4321 Can't create object", division by zero when
145
- denominator of rational value is zero
146
-
147
- EXIF Reader 0.9
148
- * 1st release
@@ -1,57 +0,0 @@
1
- = EXIF Reader
2
- {<img src="https://badge.fury.io/rb/exifr.svg" alt="Gem Version" />}[https://badge.fury.io/rb/exifr]
3
- {<img src="https://circleci.com/gh/remvee/exifr/tree/master.svg?style=shield&circle-token=7d957be2d7195f5d07a7c1962926ee626b050c2e" alt="Circle CI" />}[https://circleci.com/gh/remvee/exifr]
4
-
5
- EXIF Reader is a module to read metadata from JPEG and TIFF images.
6
-
7
- == Examples
8
- require 'exifr/jpeg'
9
- EXIFR::JPEG.new('IMG_6841.JPG').width # => 2272
10
- EXIFR::JPEG.new('IMG_6841.JPG').height # => 1704
11
- EXIFR::JPEG.new('IMG_6841.JPG').exif? # => true
12
- EXIFR::JPEG.new('IMG_6841.JPG').model # => "Canon PowerShot G3"
13
- EXIFR::JPEG.new('IMG_6841.JPG').date_time # => Fri Feb 09 16:48:54 +0100 2007
14
- EXIFR::JPEG.new('IMG_6841.JPG').exposure_time.to_s # => "1/15"
15
- EXIFR::JPEG.new('IMG_6841.JPG').f_number.to_f # => 2.0
16
- EXIFR::JPEG.new('enkhuizen.jpg').gps.latitude # => 52.7197888888889
17
- EXIFR::JPEG.new('enkhuizen.jpg').gps.longitude # => 5.28397777777778
18
-
19
- require 'exifr/tiff'
20
- EXIFR::TIFF.new('DSC_0218.TIF').width # => 3008
21
- EXIFR::TIFF.new('DSC_0218.TIF')[1].width # => 160
22
- EXIFR::TIFF.new('DSC_0218.TIF').model # => "NIKON D1X"
23
- EXIFR::TIFF.new('DSC_0218.TIF').date_time # => Tue May 23 19:15:32 +0200 2006
24
- EXIFR::TIFF.new('DSC_0218.TIF').exposure_time.to_s # => "1/100"
25
- EXIFR::TIFF.new('DSC_0218.TIF').f_number.to_f # => 5.0
26
-
27
- == Logging warnings
28
- When EXIF information is malformed, a warning is logged to STDERR with
29
- the standard Ruby logger. Log to some other location by supplying an
30
- alternative implementation:
31
-
32
- EXIFR.logger = SyslogLogger.new
33
-
34
- == Time zone support
35
- EXIF does not support time zones so this code does not support time
36
- zones. All time stamps are created in the local time zone with:
37
-
38
- Time.local(..)
39
-
40
- It is possible to change this behavior by supplying an alternative
41
- implementation. For those who prefer UTC:
42
-
43
- EXIFR::TIFF.mktime_proc = proc{|*args| Time.utc(*args)}
44
-
45
- Or when the application depends on ActiveSupport for time zone handling:
46
-
47
- EXIFR::TIFF.mktime_proc = proc{|*args| Time.zone.local(*args)}
48
-
49
- == XMP data access
50
- If you need to access XMP data you can use the xmp gem. More info and
51
- examples at https://github.com/amberbit/xmp
52
-
53
- == Author
54
- R.W. van 't Veer
55
-
56
- == Copyright
57
- Copyright (c) 2006-2017 - R.W. van 't Veer