exempi 0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f8b542870176af4c52ddf7a13d1f4bae891db89b
4
+ data.tar.gz: 3cea7bcc51f8f8921f45277bd43980b90dfcd810
5
+ SHA512:
6
+ metadata.gz: 8002302d3272d602a1c50ec9e2932bd2c25e00298439d09754b5500c5a817aaacd1614499ae229ccac144fd9c8e826ebcb9d37da247a1d1e7cbd74888b1dda67
7
+ data.tar.gz: b95e971af05e2f0b82810031449c511414fa2eb5dcd1e7ceee52677039be54f10680c4fb9c00c76d1529ad00db8ae5f783e4ec37030510496191613a352aa64d
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in exempi.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,34 @@
1
+ Copyright (C) 2015 Misty De Meo
2
+ Copyright (C) 2012 Canadian Museum for Human Rights
3
+ Copyright (C) 2007-2008,2012 Hubert Figuiere
4
+ Copyright 2002-2007 Adobe Systems Incorporated
5
+ All rights reserved.
6
+
7
+ Redistribution and use in source and binary forms, with or without
8
+ modification, are permitted provided that the following conditions
9
+ are met:
10
+
11
+ 1 Redistributions of source code must retain the above copyright
12
+ notice, this list of conditions and the following disclaimer.
13
+
14
+ 2 Redistributions in binary form must reproduce the above copyright
15
+ notice, this list of conditions and the following disclaimer in the
16
+ documentation and/or other materials provided with the
17
+ distribution.
18
+
19
+ 3 Neither the name of the Authors, nor the names of its
20
+ contributors may be used to endorse or promote products derived
21
+ from this software wit hout specific prior written permission.
22
+
23
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26
+ FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27
+ COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
34
+ OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # Exempi
2
+
3
+ Exempi is an ultra-thin [FFI](https://github.com/ffi/ffi)-based wrapper for the [Exempi](http://libopenraw.freedesktop.org/wiki/Exempi) C library. It wraps Exempi's functions in the Exempi module.
4
+
5
+ Don't like dealing with C functions directly? Check out [Fasttrack](https://github.com/mistydemeo/fasttrack), an object-oriented wrapper around Exempi.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'exempi'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install exempi
20
+
21
+ ## Usage samples
22
+
23
+ Opening a file:
24
+
25
+ ```ruby
26
+ # create an empty file pointer
27
+ file_ptr = Exempi.xmp_files_new
28
+ Exempi.xmp_files_open file_ptr, "myfile.jpg", :XMP_OPEN_READ
29
+ ```
30
+
31
+ Extracting XMP from a file:
32
+
33
+ ```ruby
34
+ xmp_ptr = Exempi.files_get_new_xmp file_ptr
35
+ ```
36
+
37
+ Fetching an XMP property from a file:
38
+
39
+ ```ruby
40
+ # Exempi provides a set of standard namespace URIs for you
41
+ exif_ns = Exempi::Namespaces::XMP_NS_EXIF
42
+ xmp_string = Exempi.xmp_string_new
43
+
44
+ Exempi.xmp_get_property xmp_ptr, exif_ns, 'DateTimeOriginal', xmp_string
45
+ datetime = Exempi.xmp_string_cstr xmp_string
46
+ Exempi.xmp_string_free xmp_string
47
+ ```
48
+
49
+ For more information see Exempi's function documentation, included in lib/exempi/exempi.rb.
50
+
51
+ ## Contributing
52
+
53
+ 1. Fork it
54
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
55
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
56
+ 4. Push to the branch (`git push origin my-new-feature`)
57
+ 5. Create new Pull Request
58
+
59
+ ## License
60
+
61
+ 3-clause BSD, identical to the license used by Exempi and Adobe XMP Toolkit. For the license text, see LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ require 'rake/testtask'
5
+ Rake::TestTask.new do |t|
6
+ t.libs << "test"
7
+ t.test_files = FileList['test/*_test.rb']
8
+ t.verbose = true
9
+ end
data/exempi.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/exempi/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Misty De Meo"]
6
+ gem.email = ["mistydemeo@gmail.com"]
7
+ gem.description = <<-EOS
8
+ Exempi is a thin FFI-based wrapper around the
9
+ Exempi library. It provides a variety of
10
+ functions for reading, writing, and manipulating
11
+ XMP metadata.
12
+ EOS
13
+ gem.summary = %q{Ruby wrapper for Exempi}
14
+ gem.homepage = "https://github.com/mistydemeo/exempi"
15
+
16
+ gem.files = `git ls-files`.split($\)
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.name = "exempi"
20
+ gem.require_paths = ["lib"]
21
+ gem.version = Exempi::VERSION
22
+
23
+ gem.add_dependency 'ffi', '>= 1.1.5'
24
+
25
+ gem.add_development_dependency 'rake', '>= 0.9.2.2'
26
+ end
data/lib/exempi.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'exempi/consts'
2
+ require 'exempi/exempi'
3
+ require 'exempi/errors'
4
+ require 'exempi/exceptions'
@@ -0,0 +1,338 @@
1
+ # These constants and comments are taken from Exempi's xmp.h
2
+ #
3
+ # Copyright (C) 2015 Misty De Meo
4
+ # Copyright (C) 2012 Canadian Museum for Human Rights
5
+ # Copyright (C) 2007-2008,2012 Hubert Figuiere
6
+ # Copyright 2002-2007 Adobe Systems Incorporated
7
+ # All rights reserved.
8
+ #
9
+ # Redistribution and use in source and binary forms, with or without
10
+ # modification, are permitted provided that the following conditions
11
+ # are met:
12
+ #
13
+ # 1 Redistributions of source code must retain the above copyright
14
+ # notice, this list of conditions and the following disclaimer.
15
+ #
16
+ # 2 Redistributions in binary form must reproduce the above copyright
17
+ # notice, this list of conditions and the following disclaimer in the
18
+ # documentation and/or other materials provided with the
19
+ # distribution.
20
+ #
21
+ # 3 Neither the name of the Authors, nor the names of its
22
+ # contributors may be used to endorse or promote products derived
23
+ # from this software wit hout specific prior written permission.
24
+ #
25
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28
+ # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29
+ # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33
+ # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
36
+ # OF THE POSSIBILITY OF SUCH DAMAGE.
37
+
38
+ require 'exempi/exceptions'
39
+
40
+ require 'date'
41
+ require 'ffi'
42
+
43
+ module Exempi
44
+ extend FFI::Library
45
+
46
+ # Option bits for xmp_files_open()
47
+ XMP_OPEN_FILE_OPTIONS = enum [
48
+ :XMP_OPEN_NOOPTION, 0x00000000, # No open option
49
+ :XMP_OPEN_READ, 0x00000001, # Open for read-only access.
50
+ :XMP_OPEN_FORUPDATE, 0x00000002, # Open for reading and writing.
51
+ :XMP_OPEN_ONLYXMP, 0x00000004, # Only the XMP is wanted, allows space/time optimizations.
52
+ :XMP_OPEN_CACHETNAIL, 0x00000008, # Cache thumbnail if possible, GetThumbnail will be called.
53
+ :XMP_OPEN_STRICTLY, 0x00000010, # Be strict about locating XMP and reconciling with other forms.
54
+ :XMP_OPEN_USESMARTHANDLER, 0x00000020, # Require the use of a smart handler.
55
+ :XMP_OPEN_USEPACKETSCANNING, 0x00000040, # Force packet scanning, don't use a smart handler.
56
+ :XMP_OPEN_LIMITSCANNING, 0x00000080, # Only packet scan files "known" to need scanning.
57
+ :XMP_OPEN_REPAIR_FILE, 0x00000100, # Attempt to repair a file opened for update, default is to not open (throw an exception).
58
+ :XMP_OPEN_INBACKGROUND, 0x10000000 # Set if calling from background thread.
59
+ ]
60
+
61
+ # Option bits for xmp_files_close()
62
+ XMP_CLOSE_FILE_OPTIONS = enum [
63
+ :XMP_CLOSE_NOOPTION, 0x0000,
64
+ :XMP_CLOSE_SAFEUPDATE, 0x0001
65
+ ]
66
+
67
+ # File formats
68
+ XMP_FILE_TYPE = enum [
69
+ :XMP_FT_PDF, 0x50444620, # 'PDF '
70
+ :XMP_FT_PS, 0x50532020, # 'PS ', general PostScript following DSC conventions.
71
+ :XMP_FT_EPS, 0x45505320, # 'EPS ', encapsulated PostScript.
72
+
73
+ :XMP_FT_JPEG, 0x4A504547, # 'JPEG'
74
+ :XMP_FT_JPEG2K, 0x4A505820, # 'JPX ', ISO 15444-1
75
+ :XMP_FT_TIFF, 0x54494646, # 'TIFF'
76
+ :XMP_FT_GIF, 0x47494620, # 'GIF '
77
+ :XMP_FT_PNG, 0x504E4720, # 'PNG '
78
+
79
+ :XMP_FT_SWF, 0x53574620, # 'SWF '
80
+ :XMP_FT_FLA, 0x464C4120, # 'FLA '
81
+ :XMP_FT_FLV, 0x464C5620, # 'FLV '
82
+
83
+ :XMP_FT_MOV, 0x4D4F5620, # 'MOV ', Quicktime
84
+ :XMP_FT_AVI, 0x41564920, # 'AVI '
85
+ :XMP_FT_CIN, 0x43494E20, # 'CIN ', Cineon
86
+ :XMP_FT_WAV, 0x57415620, # 'WAV '
87
+ :XMP_FT_MP3, 0x4D503320, # 'MP3 '
88
+ :XMP_FT_SES, 0x53455320, # 'SES ', Audition session
89
+ :XMP_FT_CEL, 0x43454C20, # 'CEL ', Audition loop
90
+ :XMP_FT_MPEG, 0x4D504547, # 'MPEG'
91
+ :XMP_FT_MPEG2, 0x4D503220, # 'MP2 '
92
+ :XMP_FT_MPEG4, 0x4D503420, # 'MP4 ', ISO 14494-12 and -14
93
+ :XMP_FT_WMAV, 0x574D4156, # 'WMAV', Windows Media Audio and Video
94
+ :XMP_FT_AIFF, 0x41494646, # 'AIFF'
95
+
96
+ :XMP_FT_HTML, 0x48544D4C, # 'HTML'
97
+ :XMP_FT_XML, 0x584D4C20, # 'XML '
98
+ :XMP_FT_TEXT, 0x74657874, # 'text'
99
+
100
+ # Adobe application file formats.
101
+ :XMP_FT_PHOTOSHOP, 0x50534420, # 'PSD '
102
+ :XMP_FT_ILLUSTRATOR, 0x41492020, # 'AI '
103
+ :XMP_FT_INDESIGN, 0x494E4444, # 'INDD'
104
+ :XMP_FT_AEPROJECT, 0x41455020, # 'AEP '
105
+ :XMP_FT_AEPROJTEMPLATE, 0x41455420, # 'AET ', After Effects Project Template
106
+ :XMP_FT_AEFILTERPRESET, 0x46465820, # 'FFX '
107
+ :XMP_FT_ENCOREPROJECT, 0x4E434F52, # 'NCOR'
108
+ :XMP_FT_PREMIEREPROJECT, 0x5052504A, # 'PRPJ'
109
+ :XMP_FT_PREMIERETITLE, 0x5052544C, # 'PRTL'
110
+
111
+ # Catch all.
112
+ :XMP_FT_UNKNOWN, 0x20202020
113
+ ]
114
+
115
+ XMP_FILE_FORMAT_OPTIONS = enum [
116
+ :XMP_FMT_CAN_INJECT_XMP, 0x00000001,
117
+ :XMP_FMT_CAN_EXPAND, 0x00000002,
118
+ :XMP_FMT_CAN_REWRITE, 0x00000004,
119
+ :XMP_FMT_PREFERS_IN_PLACE, 0x00000008,
120
+ :XMP_FMT_CAN_RECONCILE, 0x00000010,
121
+ :XMP_FMT_ALLOWS_ONLY_XMP, 0x00000020,
122
+ :XMP_FMT_RETURNS_RAW_PACKET, 0x00000040,
123
+ :XMP_FMT_HANDLER_OWNS_FILE, 0x00000100,
124
+ :XMP_FMT_ALLOW_SAFE_UPDATE, 0x00000200,
125
+ :XMP_FMT_NEEDS_READONLY_PACKET, 0x00000400,
126
+ :XMP_FMT_USE_SIDECAR_XMP, 0x00000800,
127
+ :XMP_FMT_FOLDER_BASED_FORMAT, 0x00001000,
128
+ :_XMP_FMT_LAST
129
+ ]
130
+
131
+ XMP_ITER_OPTIONS = enum [
132
+ :XMP_ITER_CLASSMASK, 0x00FF, # The low 8 bits are an enum of what data structure to iterate.
133
+ :XMP_ITER_PROPERTIES, 0x0000, # Iterate the property tree of a TXMPMeta object.
134
+ :XMP_ITER_ALIASES, 0x0001, # Iterate the global alias table.
135
+ :XMP_ITER_NAMESPACES, 0x0002, # Iterate the global namespace table.
136
+ :XMP_ITER_JUSTCHILDREN, 0x0100, # Just do the immediate children of the root, default is subtree.
137
+ :XMP_ITER_JUSTLEAFNODES, 0x0200, # Just do the leaf nodes, default is all nodes in the subtree.
138
+ :XMP_ITER_JUSTLEAFNAME, 0x0400, # Return just the leaf part of the path, default is the full path.
139
+ :XMP_ITER_INCLUDEALIASES, 0x0800, # Include aliases, default is just actual properties.
140
+ :XMP_ITER_OMITQUALIFIERS, 0x1000 # Omit all qualifiers.
141
+ ]
142
+
143
+ XMP_ITER_SKIP_OPTIONS = enum [
144
+ :XMP_ITER_SKIPSUBTREE, 0x0001, # Skip the subtree below the current node.
145
+ :XMP_ITER_SKIPSIBLINGS, 0x0002 # Skip the subtree below and remaining siblings of the current node.
146
+ ]
147
+
148
+ XMP_PROPS_BITS = enum [
149
+ # Options relating to the XML string form of the property value.
150
+ :XMP_PROP_VALUE_IS_URI, 0x00000002, # The value is a URI, use rdf:resource attribute.
151
+ # DISCOURAGED
152
+
153
+ # Options relating to qualifiers attached to a property.
154
+ :XMP_PROP_HAS_QUALIFIERS, 0x00000010, # The property has qualifiers, includes rdf:type and xml:lang.
155
+ :XMP_PROP_IS_QUALIFIER, 0x00000020, # This is a qualifier, includes rdf:type and xml:lang.
156
+ :XMP_PROP_HAS_LANG, 0x00000040, # Implies XMP_PropHasQualifiers, property has xml:lang.
157
+ :XMP_PROP_HAS_TYPE, 0x00000080, # Implies XMP_PropHasQualifiers, property has rdf:type.
158
+
159
+ # Options relating to the data structure form.
160
+ :XMP_PROP_VALUE_IS_STRUCT, 0x00000100, # The value is a structure with nested fields.
161
+ :XMP_PROP_VALUE_IS_ARRAY, 0x00000200, # The value is an array (RDF alt/bag/seq).
162
+ :XMP_PROP_ARRAY_IS_UNORDERED, 0x00000200, # The item order does not matter. (e.g. same as XMP_PROP_VALUE_IS_ARRAY)
163
+ :XMP_PROP_ARRAY_IS_ORDERED, 0x00000400, # Implies XMP_PropValueIsArray, item order matters.
164
+ :XMP_PROP_ARRAY_IS_ALT, 0x00000800, # Implies XMP_PropArrayIsOrdered, items are alternates.
165
+
166
+ # Additional struct and array options.
167
+ :XMP_PROP_ARRAY_IS_ALTTEXT, 0x00001000, # Implies kXMP_PropArrayIsAlternate, items are localized text.
168
+ :XMP_PROP_ARRAY_INSERT_BEFORE, 0x00004000, # Used by array functions.
169
+ :XMP_PROP_ARRAY_INSERT_AFTER, 0x00008000, # Used by array functions.
170
+
171
+ # Other miscellaneous options.
172
+ :XMP_PROP_IS_ALIAS, 0x00010000, # This property is an alias name for another property.
173
+ :XMP_PROP_HAS_ALIASES, 0x00020000, # This property is the base value for a set of aliases.
174
+ :XMP_PROP_IS_INTERNAL, 0x00040000, # This property is an "internal" property, owned by applications.
175
+ :XMP_PROP_IS_STABLE, 0x00100000, # This property is not derived from the document content.
176
+ :XMP_PROP_IS_DERIVED, 0x00200000, # This property is derived from the document content.
177
+
178
+ # Note that these are commented out in Exempi
179
+ # kXMPUtil_AllowCommas, 0x10000000UL, ! Used by TXMPUtils::CatenateArrayItems and ::SeparateArrayItems.
180
+ # kXMP_DeleteExisting, 0x20000000UL, ! Used by TXMPMeta::SetXyz functions to delete any pre-existing property.
181
+ # kXMP_SchemaNode, 0x80000000UL, ! Returned by iterators - #define to avoid warnings
182
+
183
+ # Original values of these bitmasks noted; we can't access them by name
184
+ # XMP_PROP_ARRAY_FORM_MASK = XMP_PROP_VALUE_IS_ARRAY | XMP_PROP_ARRAY_IS_ORDERED | XMP_PROP_ARRAY_IS_ALT | XMP_PROP_ARRAY_IS_ALTTEXT,
185
+ :XMP_PROP_ARRAY_FORM_MASK, 0x00000200 | 0x00000400 | 0x00000800 | 0x00001000,
186
+
187
+ # XMP_PROP_COMPOSITE_MASK = XMP_PROP_VALUE_IS_STRUCT | XMP_PROP_ARRAY_FORM_MASK, /* Is it simple or composite (array or struct)? */
188
+ :XMP_PROP_COMPOSITE_MASK, 0x00000100 | 0x00001E00,
189
+ :XMP_IMPL_RESERVED_MASK, 0x70000000 # Reserved for transient use by the implementation.
190
+ ]
191
+
192
+ # These values are used in the enum below
193
+ XMP_LITTLEENDIAN_BIT = 0x0001 # ! Don't use directly, see the combined values below!
194
+ XMP_UTF16_BIT = 0x0002
195
+ XMP_UTF32_BIT = 0x0004
196
+
197
+ # Options for xmp_serialize
198
+ # Unnamed in exempi, named :XmpSerialOptions here
199
+ XMP_SERIAL_OPTIONS = enum [
200
+ :XMP_SERIAL_OMITPACKETWRAPPER, 0x0010, # Omit the XML packet wrapper. */
201
+ :XMP_SERIAL_READONLYPACKET, 0x0020, # Defat is a writeable packet. */
202
+ :XMP_SERIAL_USECOMPACTFORMAT, 0x0040, # Use a compact form of RDF. */
203
+
204
+ :XMP_SERIAL_INCLUDETHUMBNAILPAD, 0x0100, # Include a padding allowance for a thumbnail image. */
205
+ :XMP_SERIAL_EXACTPACKETLENGTH, 0x0200, # The padding parameter is the overall packet length. */
206
+ :XMP_SERIAL_WRITEALIASCOMMENTS, 0x0400, # Show aliases as XML comments. */
207
+ :XMP_SERIAL_OMITALLFORMATTING, 0x0800, # Omit all formatting whitespace. */
208
+
209
+ :XMP_SERIAL_ENCODINGMASK, 0x0007,
210
+ :XMP_SERIAL_ENCODEUTF8, 0,
211
+ :XMP_SERIAL_ENCODEUTF16BIG, XMP_UTF16_BIT,
212
+ :XMP_SERIAL_ENCODEUTF16LITTLE, XMP_UTF16_BIT | XMP_LITTLEENDIAN_BIT,
213
+ :XMP_SERIAL_ENCODEUTF32BIG, XMP_UTF32_BIT,
214
+ :XMP_SERIAL_ENCODEUTF32LITTLE, XMP_UTF32_BIT | XMP_LITTLEENDIAN_BIT
215
+ ]
216
+
217
+ # Values used for tzSign field.
218
+ # Another unnamed enum, used in XmpDateTime below
219
+ TzSignValues = enum [
220
+ :XMP_TZ_WEST, -1, # West of UTC
221
+ :XMP_TZ_UTC, 0, # UTC
222
+ :XMP_TZ_EAST, +1 # East of UTC
223
+ ]
224
+
225
+ class XmpDateTime < FFI::Struct
226
+ layout(
227
+ :year, :int32,
228
+ :month, :int32, # 1..12
229
+ :day, :int32, # 1..31
230
+ :hour, :int32, # 0..23
231
+ :minute, :int32, # 0..59
232
+ :second, :int32, # 0..59
233
+ :tzSign, Exempi::TzSignValues, # -1..+1, 0 means UTC, -1 is west, +1 is east.
234
+ :tzHour, :int32, # 0..23
235
+ :tzMinute, :int32, # 0..59
236
+ :nanoSecond, :int32
237
+ )
238
+
239
+ # Converts a XmpDateTime struct into a Ruby object
240
+ # @return [DateTime] A new Ruby DateTime object
241
+ def to_datetime
242
+ sign = self[:tzSign] == :XMP_TZ_WEST ? '-' : '+'
243
+ zone = "%s%02d:%02d" % [sign, self[:tzHour], self[:tzMinute]]
244
+
245
+ second = self[:second] + Rational(self[:nanoSecond],1000000000)
246
+
247
+ DateTime.new self[:year], self[:month], self[:day], self[:hour],
248
+ self[:minute], second, zone
249
+ end
250
+
251
+ # Creates an XmpDateTime struct using a Ruby DateTime object,
252
+ # or a date string which is parseable by DateTime
253
+ # @param [DateTime, String] source the date to convert
254
+ # @return [Exempi::XmpDateTime] an XmpDateTime struct
255
+ def self.from_datetime source
256
+ if source.is_a? String
257
+ source = DateTime.parse source
258
+ end
259
+
260
+ struct = self.new
261
+ [:year, :month, :day, :hour, :minute, :second].each do |field|
262
+ struct[field] = source.send field
263
+ end
264
+ struct[:nanoSecond] = source.to_time.nsec
265
+
266
+ match = source.zone.match /(?<sign>[-+]){1}(?<hour>\d\d){1}:(?<minute>\d\d){1}/
267
+ if match
268
+ if match[:sign] == '-' then struct[:tzSign] = -1
269
+ elsif match[:hour] == '00' && match[:minute] == '00' then struct[:tzSign] = 0
270
+ else struct[:tzSign] = 1
271
+ end
272
+
273
+ struct[:tzHour] = match[:hour].to_i
274
+ struct[:tzMinute] = match[:minute].to_i
275
+ end
276
+
277
+ struct
278
+ end
279
+ end
280
+
281
+ # Creates DataConverter classes from each of these enums
282
+ [ "XMP_OPEN_FILE_OPTIONS", "XMP_CLOSE_FILE_OPTIONS", "XMP_FILE_TYPE",
283
+ "XMP_FILE_FORMAT_OPTIONS", "XMP_ITER_OPTIONS", "XMP_ITER_SKIP_OPTIONS",
284
+ "XMP_PROPS_BITS", "XMP_SERIAL_OPTIONS" ].each do |enum|
285
+ klass = Class.new do
286
+ extend FFI::DataConverter
287
+ native_type FFI::Type::INT
288
+ @enum = enum
289
+
290
+ def self.to_native(values, ctx)
291
+ case values
292
+ when Symbol
293
+ val = Exempi.const_get(@enum)[values]
294
+ if not val
295
+ raise Exempi::InvalidOptionError,
296
+ "#{values} is not a valid option for #{@enum}"
297
+ else
298
+ val
299
+ end
300
+ when Fixnum then values
301
+ when NilClass then 0
302
+ else
303
+ invalid_opt = values.find {|v| Exempi.const_get(@enum)[v].nil?}
304
+ if invalid_opt
305
+ raise Exempi::InvalidOptionError,
306
+ "#{invalid_opt} is not a valid option for #{@enum}"
307
+ end
308
+
309
+ values.inject(0) {|mask, value| mask |= Exempi.const_get(@enum)[value]}
310
+ end
311
+ end
312
+ end
313
+
314
+ klass_name = enum.split("_").map {|str| str.capitalize}.join
315
+ const_set klass_name, klass
316
+ end
317
+
318
+ # Converts a bitfield into a hash of named options via bitwise AND.
319
+ # @param [Integer] int the bitfield integer
320
+ # @param [FFI::Enum] enum the enum with which to compare
321
+ # @param [Boolean] short_name true to return shortened option names
322
+ # @return [Hash] a hash which includes symbol representations of the included options
323
+ def self.parse_bitmask int, enum, short_name=false
324
+ enum_hash = enum.to_hash
325
+ opt_hash = {}
326
+ enum_hash.each do |k,v|
327
+ if short_name
328
+ option = k.to_s.split("_")[2..-1].join("_").downcase.to_sym
329
+ else
330
+ option = k
331
+ end
332
+
333
+ opt_hash[option] = ((int & v) == v)
334
+ end
335
+
336
+ opt_hash
337
+ end
338
+ end
@@ -0,0 +1,97 @@
1
+ # Error values and comments are taken from Exempi's xmperrors.h
2
+ #
3
+ # Copyright (C) 2015 Misty De Meo
4
+ # Copyright (C) 2012 Canadian Museum for Human Rights
5
+ # Copyright (C) 2007 Hubert Figuiere
6
+ # Copyright 2002-2007 Adobe Systems Incorporated
7
+ # All rights reserved.
8
+ #
9
+ # Redistribution and use in source and binary forms, with or without
10
+ # modification, are permitted provided that the following conditions
11
+ # are met:
12
+ #
13
+ # 1 Redistributions of source code must retain the above copyright
14
+ # notice, this list of conditions and the following disclaimer.
15
+ #
16
+ # 2 Redistributions in binary form must reproduce the above copyright
17
+ # notice, this list of conditions and the following disclaimer in the
18
+ # documentation and/or other materials provided with the
19
+ # distribution.
20
+ #
21
+ # 3 Neither the name of the Authors, nor the names of its
22
+ # contributors may be used to endorse or promote products derived
23
+ # from this software wit hout specific prior written permission.
24
+ #
25
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28
+ # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29
+ # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33
+ # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
36
+ # OF THE POSSIBILITY OF SUCH DAMAGE.
37
+
38
+ module Exempi
39
+ # This maps the numeric exceptions to their names.
40
+ # The numeric values are output by xmp_get_error()
41
+ ErrorCodes = {
42
+ # More or less generic error codes.
43
+ 0 => :XMPErr_Unknown,
44
+ -1 => :XMPErr_TBD,
45
+ -2 => :XMPErr_Unavailable,
46
+ -3 => :XMPErr_BadObject,
47
+ -4 => :XMPErr_BadParam,
48
+ -5 => :XMPErr_BadValue,
49
+ -6 => :XMPErr_AssertFailure,
50
+ -7 => :XMPErr_EnforceFailure,
51
+ -8 => :XMPErr_Unimplemented,
52
+ -9 => :XMPErr_InternalFailure,
53
+ -10 => :XMPErr_Deprecated,
54
+ -11 => :XMPErr_ExternalFailure,
55
+ -12 => :XMPErr_UserAbort,
56
+ -13 => :XMPErr_StdException,
57
+ -14 => :XMPErr_UnknownException,
58
+ -15 => :XMPErr_NoMemory,
59
+
60
+ # More specific parameter error codes.
61
+ -101 => :XMPErr_BadSchema,
62
+ -102 => :XMPErr_BadXPath,
63
+ -103 => :XMPErr_BadOptions,
64
+ -104 => :XMPErr_BadIndex,
65
+ -105 => :XMPErr_BadIterPosition,
66
+ -106 => :XMPErr_BadParse,
67
+ -107 => :XMPErr_BadSerialize,
68
+ -108 => :XMPErr_BadFileFormat,
69
+ -109 => :XMPErr_NoFileHandler,
70
+ -110 => :XMPErr_TooLargeForJPEG,
71
+
72
+ # File format and internal structure error codes.
73
+ -201 => :XMPErr_BadXML,
74
+ -202 => :XMPErr_BadRDF,
75
+ -203 => :XMPErr_BadXMP,
76
+ -204 => :XMPErr_EmptyIterator,
77
+ -205 => :XMPErr_BadUnicode,
78
+ -206 => :XMPErr_BadTIFF,
79
+ -207 => :XMPErr_BadJPEG,
80
+ -208 => :XMPErr_BadPSD,
81
+ -209 => :XMPErr_BadPSIR,
82
+ -210 => :XMPErr_BadIPTC,
83
+ -211 => :XMPErr_BadMPEG
84
+ }
85
+
86
+ # Returns a symbol representing the XMP error for an int code.
87
+ # Used with the output of Exempi.xmp_get_error
88
+ # This is just a bit of sugar over Exempi::ErrorCodes[code]
89
+ # @param [Integer] code integer error code; should be obtained from
90
+ # Exempi.xmp_get_error
91
+ def self.exception_for code
92
+ if !ErrorCodes[code]
93
+ raise ArgumentError, "#{error_code} is not an Exempi error code"
94
+ end
95
+ ErrorCodes[code]
96
+ end
97
+ end
@@ -0,0 +1,24 @@
1
+ require 'exempi/errors'
2
+
3
+ module Exempi
4
+ # This class provides a convenient interface for throwing exceptions
5
+ # when using Exempi functions. It allows the caller to specify an
6
+ # error code (retrievable from xmp_get_error()), and provides a
7
+ # method to retrieve the error name based on the code.
8
+ class ExempiError < StandardError
9
+ attr :error_code
10
+ def initialize error_code=0
11
+ if !Exempi::ErrorCodes[error_code]
12
+ raise ArgumentError, "#{error_code} is not an Exempi error code"
13
+ end
14
+ @error_code = error_code
15
+ end
16
+
17
+ # @return [Symbol] Exempi's error string for this exception
18
+ def xmp_error
19
+ Exempi::ErrorCodes[@error_code]
20
+ end
21
+ end
22
+
23
+ class InvalidOptionError < StandardError; end
24
+ end
@@ -0,0 +1,325 @@
1
+ # These comments are taken from Exempi's xmp.h
2
+ #
3
+ # Copyright (C) 2015 Misty De Meo
4
+ # Copyright (C) 2012 Canadian Museum for Human Rights
5
+ # Copyright (C) 2007-2008,2012 Hubert Figuiere
6
+ # Copyright 2002-2007 Adobe Systems Incorporated
7
+ # All rights reserved.
8
+ #
9
+ # Redistribution and use in source and binary forms, with or without
10
+ # modification, are permitted provided that the following conditions
11
+ # are met:
12
+ #
13
+ # 1 Redistributions of source code must retain the above copyright
14
+ # notice, this list of conditions and the following disclaimer.
15
+ #
16
+ # 2 Redistributions in binary form must reproduce the above copyright
17
+ # notice, this list of conditions and the following disclaimer in the
18
+ # documentation and/or other materials provided with the
19
+ # distribution.
20
+ #
21
+ # 3 Neither the name of the Authors, nor the names of its
22
+ # contributors may be used to endorse or promote products derived
23
+ # from this software wit hout specific prior written permission.
24
+ #
25
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28
+ # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29
+ # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33
+ # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
36
+ # OF THE POSSIBILITY OF SUCH DAMAGE.
37
+
38
+ require 'exempi/consts'
39
+
40
+ require 'ffi'
41
+
42
+ module Exempi
43
+ extend FFI::Library
44
+ ffi_lib 'exempi'
45
+
46
+ # we redefine attach_function so we can wrap all of the C functions
47
+ class << self
48
+ def verbose?; @verbose; end
49
+ attr_writer :verbose
50
+
51
+ def attach_function name, func, args, returns=nil, options={}
52
+ super
53
+ old_method = method(name)
54
+ define_singleton_method(name) do |*args|
55
+ shutup! { old_method.call(*args) }
56
+ end
57
+ end
58
+
59
+ protected
60
+
61
+ # Exempi spews stderr all over the place without giving you any way
62
+ # to quiet it! Boo!
63
+ def shutup!
64
+ if not verbose?
65
+ io = IO.new 2
66
+ stderr = io.dup
67
+ io.reopen IO::NULL
68
+ end
69
+ yield
70
+ ensure
71
+ io.reopen stderr unless verbose?
72
+ end
73
+ end
74
+
75
+ # Init the library. Must be called before anything else
76
+ attach_function :xmp_init, [ ], :bool
77
+ attach_function :xmp_terminate, [ ], :void
78
+
79
+ # get the error code that last occurred.
80
+ # @todo make this thread-safe. Getting the error code
81
+ # from another thread than the on it occurred in is undefined.
82
+ attach_function :xmp_get_error, [ ], :int
83
+
84
+ attach_function :xmp_files_new, [ ], :pointer
85
+ attach_function :xmp_files_open_new, [ :string, XmpOpenFileOptions ], :pointer
86
+ attach_function :xmp_files_open, [ :pointer, :string, XmpOpenFileOptions ], :bool
87
+
88
+ # Close an XMP file. Will flush the changes.
89
+ # @param [FFI::Pointer, FFI::MemoryPointer] xf the file object
90
+ # @param [Exempi::XmpCloseFileOptions] options the options to close.
91
+ # @return [Boolean] true on succes, false on error
92
+ # xmp_get_error() will give the error code.
93
+ attach_function :xmp_files_close, [ :pointer, XmpCloseFileOptions ], :bool
94
+
95
+ attach_function :xmp_files_get_new_xmp, [ :pointer ], :pointer
96
+ attach_function :xmp_files_get_xmp, [ :pointer, :pointer ], :bool
97
+
98
+ attach_function :xmp_files_can_put_xmp, [ :pointer, :pointer ], :bool
99
+ attach_function :xmp_files_put_xmp, [ :pointer, :pointer ], :bool
100
+
101
+ # Get the file info from the open file
102
+ # @param [FFI::Pointer, FFI::MemoryPointer] xf the file object
103
+ # @param [FFI::Pointer, FFI::MemoryPointer] filePath the file path object to store the path in. Pass NULL if not needed.
104
+ # @param [FFI::Pointer, FFI::MemoryPointer] options the options for open. Pass NULL if not needed.
105
+ # @param [FFI::Pointer, FFI::MemoryPointer] file_format the detected file format. Pass NULL if not needed.
106
+ # @param [FFI::Pointer, FFI::MemoryPointer] handler_flags the format options like from %xmp_files_get_format_info.
107
+ # @return [Boolean] false in case of error.
108
+ attach_function :xmp_files_get_file_info, [ :pointer, :pointer, :pointer, :pointer, :pointer ], :bool
109
+
110
+ # Free a XmpFilePtr.
111
+ # @param [FFI::Pointer, FFI::MemoryPointer] xf the file ptr. Cannot be NULL
112
+ # @return [Boolean] false on error.
113
+ # xmp_get_error() will give the error code.
114
+ attach_function :xmp_files_free, [ :pointer ], :bool
115
+
116
+ # Get the format info
117
+ # @param [Integer] format type identifier
118
+ # @param [FFI::Pointer, FFI::MemoryPointer] option the options for the file format handler
119
+ # @return [Boolean] false on error
120
+ attach_function :xmp_files_get_format_info, [ :int, :pointer ], :bool
121
+
122
+ # Check the file format of a file. Use the same logic as in xmp_files_open()
123
+ # @param [String] filePath the path to the file
124
+ # @return [Integer] XMP_FT_UNKNOWN on error or if the file type is unknown
125
+ attach_function :xmp_files_check_file_format, [ :string ], :int
126
+
127
+ # Register a new namespace to add properties to.
128
+ # This is done automatically when reading the metadata block
129
+ # @param [String] namespaceURI the namespace URI to register
130
+ # @param [String] suggestedPrefix the suggested prefix
131
+ # @param [FFI::Pointer, FFI::MemoryPointer] registeredPrefix the really registered prefix. Not necessarily
132
+ # %suggestedPrefix.
133
+ # @return [Boolean] true if success, false otherwise.
134
+ attach_function :xmp_register_namespace, [ :string, :string, :pointer ], :bool
135
+
136
+ # Check if a namespace is registered.
137
+ # @param [String] ns the namespace to check.
138
+ # @param [FFI::Pointer, FFI::MemoryPointer] prefix The prefix associated if registered. Pass NULL
139
+ # if not interested.
140
+ # @return [Boolean] true if registered.
141
+ # NEW in 2.1
142
+ attach_function :xmp_namespace_prefix, [ :string, :pointer ], :bool
143
+
144
+ # Check if a ns prefix is registered.
145
+ # @param [String] prefix the prefix to check.
146
+ # @param [FFI::Pointer, FFI::MemoryPointer] ns the namespace associated if registered. Pass NULL
147
+ # if not interested.
148
+ # @return [Boolean] true if registered.
149
+ # NEW in 2.1
150
+ attach_function :xmp_prefix_namespace_uri, [ :string, :pointer ], :bool
151
+
152
+ attach_function :xmp_new_empty, [ ], :pointer
153
+
154
+ # Create a new XMP packet
155
+ # @param [String] buffer the buffer to load data from. UTF-8 encoded.
156
+ # @param [Integer] len the buffer length in byte
157
+ # @return [FFI::Pointer] the packet pointer. Must be free with xmp_free()
158
+ attach_function :xmp_new, [ :string, :uint ], :pointer
159
+
160
+ # Create a new XMP packet from the one passed.
161
+ # @param [FFI::Pointer, FFI::MemoryPointer] xmp the instance to copy. Can be NULL.
162
+ # @return [FFI::Pointer] the packet pointer. NULL is failer (or NULL is passed).
163
+ attach_function :xmp_copy, [ :pointer ], :pointer
164
+
165
+ # Free the xmp packet
166
+ # @param [FFI::Pointer, FFI::MemoryPointer] xmp the xmp packet to free
167
+ attach_function :xmp_free, [ :pointer ], :bool
168
+
169
+ # Parse the XML passed through the buffer and load
170
+ # it.
171
+ # @param [FFI::Pointer, FFI::MemoryPointer] xmp the XMP packet.
172
+ # @param [String] buffer the buffer.
173
+ # @param [Integer] len the length of the buffer.
174
+ attach_function :xmp_parse, [ :pointer, :string, :uint ], :bool
175
+
176
+ # Serialize the XMP Packet to the given buffer
177
+ # @param [FFI::Pointer, FFI::MemoryPointer] xmp the XMP Packet
178
+ # @param [FFI::Pointer, FFI::MemoryPointer] buffer the buffer to write the XMP to
179
+ # @param [Exempi::XmpSerialOptions] options options on how to write the XMP. See XMP_SERIAL_*
180
+ # @param [Integer] padding number of bytes of padding, useful for modifying
181
+ # embedded XMP in place.
182
+ # @return [Boolean] TRUE if success.
183
+ attach_function :xmp_serialize, [ :pointer, :pointer, XmpSerialOptions, :uint32 ], :bool
184
+
185
+ # Serialize the XMP Packet to the given buffer with formatting
186
+ # @param [FFI::Pointer, FFI::MemoryPointer] xmp the XMP Packet
187
+ # @param [FFI::Pointer, FFI::MemoryPointer] buffer the buffer to write the XMP to
188
+ # @param [Exempi::XmpSerialOptions] options options on how to write the XMP. See XMP_SERIAL_*
189
+ # @param [Integer] padding number of bytes of padding, useful for modifying
190
+ # embedded XMP in place.
191
+ # @param [String] newline the new line character to use
192
+ # @param [String] tab the indentation character to use
193
+ # @param [Integer] indent the initial indentation level
194
+ # @return [Boolean] TRUE if success.
195
+ attach_function :xmp_serialize_and_format, [ :pointer, :pointer, XmpSerialOptions, :uint32, :string, :string, :int32 ], :bool
196
+
197
+ # Get an XMP property and it option bits from the XMP packet
198
+ # @param [FFI::Pointer, FFI::MemoryPointer] xmp the XMP packet
199
+ # @param [String] schema
200
+ # @param [String] name
201
+ # @param [FFI::Pointer, FFI::MemoryPointer] property the allocated XmpStringPtr
202
+ # @param [FFI::Pointer, FFI::MemoryPointer] propsBits pointer to the option bits. Pass NULL if not needed
203
+ # @return [Boolean] true if found
204
+ attach_function :xmp_get_property, [ :pointer, :string, :string, :pointer, :pointer ], :bool
205
+ attach_function :xmp_get_property_date, [ :pointer, :string, :string, XmpDateTime, :pointer ], :bool
206
+ attach_function :xmp_get_property_float, [ :pointer, :string, :string, :pointer, :pointer ], :bool
207
+ attach_function :xmp_get_property_bool, [ :pointer, :string, :string, :pointer, :pointer ], :bool
208
+ attach_function :xmp_get_property_int32, [ :pointer, :string, :string, :pointer, :pointer ], :bool
209
+ attach_function :xmp_get_property_int64, [ :pointer, :string, :string, :pointer, :pointer ], :bool
210
+
211
+ # Get an item frpm an array property
212
+ # @param [FFI::Pointer, FFI::MemoryPointer] xmp the xmp meta
213
+ # @param [String] schema the schema
214
+ # @param [String] name the property name
215
+ # @param [Integer] index the index in the array
216
+ # @param [FFI::Pointer, FFI::MemoryPointer] property the property value
217
+ # @param [FFI::Pointer, FFI::MemoryPointer] propsBits the property bits. Pass NULL is unwanted.
218
+ # @return [Boolean] TRUE if success.
219
+ attach_function :xmp_get_array_item, [ :pointer, :string, :string, :int32, :pointer, :pointer ], :bool
220
+
221
+ # Set an XMP property in the XMP packet
222
+ # @param [FFI::Pointer, FFI::MemoryPointer] xmp the XMP packet
223
+ # @param [String] schema
224
+ # @param [String] name
225
+ # @param [String] value 0 terminated string
226
+ # @param [Exempi::XmpPropsBits] optionBits
227
+ # @return [Boolean] false if failure
228
+ attach_function :xmp_set_property, [ :pointer, :string, :string, :string, XmpPropsBits ], :bool
229
+
230
+ # Set a date XMP property in the XMP packet
231
+ # @param [FFI::Pointer, FFI::MemoryPointer] xmp the XMP packet
232
+ # @param [String] schema
233
+ # @param [String] name
234
+ # @param [FFI::Pointer, FFI::MemoryPointer] value the date-time struct
235
+ # @param [Exempi::XmpPropsBits] optionBits
236
+ # @return [Boolean] false if failure
237
+ attach_function :xmp_set_property_date, [ :pointer, :string, :string, :pointer, XmpPropsBits ], :bool
238
+
239
+ # Set a float XMP property in the XMP packet
240
+ # @param [FFI::Pointer, FFI::MemoryPointer] xmp the XMP packet
241
+ # @param [String] schema
242
+ # @param [String] name
243
+ # @param [Float] value the float value
244
+ # @param [Exempi::XmpPropsBits] optionBits
245
+ # @return [Boolean] false if failure
246
+ attach_function :xmp_set_property_float, [ :pointer, :string, :string, :double, XmpPropsBits ], :bool
247
+ attach_function :xmp_set_property_bool, [ :pointer, :string, :string, :bool, XmpPropsBits ], :bool
248
+ attach_function :xmp_set_property_int32, [ :pointer, :string, :string, :int32, XmpPropsBits ], :bool
249
+ attach_function :xmp_set_property_int64, [ :pointer, :string, :string, :int64, XmpPropsBits ], :bool
250
+ attach_function :xmp_set_array_item, [ :pointer, :string, :string, :int32, :string, :uint32 ], :bool
251
+
252
+ attach_function :xmp_append_array_item, [ :pointer, :string, :string, :uint32, :string, :uint32 ], :bool
253
+
254
+ # Delete a property from the XMP Packet provided
255
+ # @param [FFI::Pointer, FFI::MemoryPointer] xmp the XMP packet
256
+ # @param [String] schema the schema of the property
257
+ # @param [String] name the name of the property
258
+ attach_function :xmp_delete_property, [ :pointer, :string, :string ], :bool
259
+
260
+ # Determines if a property exists in the XMP Packet provided
261
+ # @param [FFI::Pointer, FFI::MemoryPointer] xmp the XMP packet
262
+ # @param [String] schema the schema of the property. Can't be NULL or empty.
263
+ # @param [String] name the name of the property. Can't be NULL or empty.
264
+ # @return [Boolean] true is the property exists
265
+ attach_function :xmp_has_property, [ :pointer, :string, :string ], :bool
266
+
267
+ # Get a localised text from a localisable property.
268
+ # @param [FFI::Pointer, FFI::MemoryPointer] xmp the XMP packet
269
+ # @param [String] schema the schema
270
+ # @param [String] name the property name.
271
+ # @param [String] genericLang the generic language you may want as a fall back.
272
+ # Can be NULL or empty.
273
+ # @param [String] specificLang the specific language you want. Can't be NULL or empty.
274
+ # @param [FFI::Pointer, FFI::MemoryPointer] actualLang the actual language of the value. Can be NULL if
275
+ # not wanted.
276
+ # @param [FFI::Pointer, FFI::MemoryPointer] itemValue the localized value. Can be NULL if not wanted.
277
+ # @param [FFI::Pointer, FFI::MemoryPointer] propBits the options flags describing the property. Can be NULL.
278
+ # @return [Boolean] true if found, false otherwise.
279
+ attach_function :xmp_get_localized_text, [ :pointer, :string, :string, :string, :string, :pointer, :pointer, :pointer ], :bool
280
+
281
+ # Set a localised text in a localisable property.
282
+ # @param [FFI::Pointer, FFI::MemoryPointer] xmp the XMP packet
283
+ # @param [String] schema the schema
284
+ # @param [String] name the property name.
285
+ # @param [String] genericLang the generic language you may want to set too.
286
+ # Can be NULL or empty.
287
+ # @param [String] specificLang the specific language you want. Can't be NULL or empty.
288
+ # @param [String] value the localized value. Cannot be NULL.
289
+ # @param [Exempi::XmpPropsBits] optionBits the options flags describing the property.
290
+ # @return [Boolean] true if set, false otherwise.
291
+ attach_function :xmp_set_localized_text, [ :pointer, :string, :string, :string, :string, :string, XmpPropsBits ], :bool
292
+
293
+ attach_function :xmp_delete_localized_text, [ :pointer, :string, :string, :string, :string ], :bool
294
+
295
+ # Instanciate a new string
296
+ # @return [FFI::Pointer] the new instance. Must be freed with
297
+ # xmp_string_free()
298
+ attach_function :xmp_string_new, [ ], :pointer
299
+
300
+ # Free a XmpStringPtr
301
+ # @param [FFI::Pointer, FFI::MemoryPointer] s the string to free
302
+ attach_function :xmp_string_free, [ :pointer ], :void
303
+
304
+ # Get the C string from the XmpStringPtr
305
+ # @param [FFI::Pointer, FFI::MemoryPointer] s the string object
306
+ # @return [String] the const char * for the XmpStringPtr. It
307
+ # belong to the object.
308
+ attach_function :xmp_string_cstr, [ :pointer ], :string
309
+
310
+ attach_function :xmp_iterator_new, [ :pointer, :string, :string, XmpIterOptions ], :pointer
311
+ attach_function :xmp_iterator_free, [ :pointer ], :bool
312
+
313
+ # Iterate to the next value
314
+ # @param [FFI::Pointer, FFI::MemoryPointer] iter the iterator
315
+ # @param [FFI::Pointer, FFI::MemoryPointer] schema the schema name. Pass NULL if not wanted
316
+ # @param [FFI::Pointer, FFI::MemoryPointer] propName the property path. Pass NULL if not wanted
317
+ # @param [FFI::Pointer, FFI::MemoryPointer] propValue the value of the property. Pass NULL if not wanted.
318
+ # @param [FFI::Pointer, FFI::MemoryPointer] options the options for the property. Pass NULL if not wanted.
319
+ # @return [Boolean] true if still something, false if none
320
+ attach_function :xmp_iterator_next, [ :pointer, :pointer, :pointer, :pointer, :pointer ], :bool
321
+ attach_function :xmp_iterator_skip, [ :pointer, XmpIterSkipOptions ], :bool
322
+
323
+ # We do this first, before anything else!!
324
+ xmp_init
325
+ end
@@ -0,0 +1,161 @@
1
+ # Copyright 2002 Adobe Systems Incorporated
2
+ # All Rights Reserved.
3
+ #
4
+ # NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
5
+ # of the Adobe license agreement accompanying it.
6
+ #
7
+ # Copyright (c) 1999 - 2010, Adobe Systems IncorporatedAll rights reserved.
8
+ #
9
+ # Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
10
+ #
11
+ # * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
12
+ #
13
+ # * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
14
+ #
15
+ # * Neither the name of Adobe Systems Incorporated, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOTLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FORA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER ORCONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, ORPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OFLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDINGNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THISSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
18
+
19
+ module Exempi
20
+ module Namespaces
21
+ # Standard namespace URI constants
22
+ #
23
+ # \name XML namespace constants for standard XMP schema.
24
+ # @{
25
+ #
26
+ # \def XMP_NS_XMP
27
+ # \brief The XML namespace for the XMP "basic" schema.
28
+ #
29
+ # \def XMP_NS_XMP_Rights
30
+ # \brief The XML namespace for the XMP copyright schema.
31
+ #
32
+ # \def XMP_NS_XMP_MM
33
+ # \brief The XML namespace for the XMP digital asset management schema.
34
+ #
35
+ # \def XMP_NS_XMP_BJ
36
+ # \brief The XML namespace for the job management schema.
37
+ #
38
+ # \def XMP_NS_XMP_T
39
+ # \brief The XML namespace for the XMP text document schema.
40
+ #
41
+ # \def XMP_NS_XMP_T_PG
42
+ # \brief The XML namespace for the XMP paged document schema.
43
+ #
44
+ # \def XMP_NS_PDF
45
+ # \brief The XML namespace for the PDF schema.
46
+ #
47
+ # \def XMP_NS_Photoshop
48
+ # \brief The XML namespace for the Photoshop custom schema.
49
+ #
50
+ # \def XMP_NS_EXIF
51
+ # \brief The XML namespace for Adobe's EXIF schema.
52
+ #
53
+ # \def XMP_NS_TIFF
54
+ # \brief The XML namespace for Adobe's TIFF schema.
55
+
56
+ XMP_NS_XMP = "http://ns.adobe.com/xap/1.0/"
57
+
58
+ XMP_NS_XMP_Rights = "http://ns.adobe.com/xap/1.0/rights/"
59
+ XMP_NS_XMP_MM = "http://ns.adobe.com/xap/1.0/mm/"
60
+ XMP_NS_XMP_BJ = "http://ns.adobe.com/xap/1.0/bj/"
61
+
62
+ XMP_NS_PDF = "http://ns.adobe.com/pdf/1.3/"
63
+ XMP_NS_Photoshop = "http://ns.adobe.com/photoshop/1.0/"
64
+ XMP_NS_PSAlbum = "http://ns.adobe.com/album/1.0/"
65
+ XMP_NS_EXIF = "http://ns.adobe.com/exif/1.0/"
66
+ XMP_NS_EXIF_Aux = "http://ns.adobe.com/exif/1.0/aux/"
67
+ XMP_NS_TIFF = "http://ns.adobe.com/tiff/1.0/"
68
+ XMP_NS_PNG = "http://ns.adobe.com/png/1.0/"
69
+ XMP_NS_SWF = "http://ns.adobe.com/swf/1.0/"
70
+ XMP_NS_JPEG = "http://ns.adobe.com/jpeg/1.0/"
71
+ XMP_NS_JP2K = "http://ns.adobe.com/jp2k/1.0/"
72
+ XMP_NS_CameraRaw = "http://ns.adobe.com/camera-raw-settings/1.0/"
73
+ XMP_NS_DM = "http://ns.adobe.com/xmp/1.0/DynamicMedia/"
74
+ XMP_NS_Script = "http://ns.adobe.com/xmp/1.0/Script/"
75
+ XMP_NS_ASF = "http://ns.adobe.com/asf/1.0/"
76
+ XMP_NS_WAV = "http://ns.adobe.com/xmp/wav/1.0/"
77
+ XMP_NS_BWF = "http://ns.adobe.com/bwf/bext/1.0/"
78
+
79
+ XMP_NS_XMP_Note = "http://ns.adobe.com/xmp/note/"
80
+
81
+ XMP_NS_AdobeStockPhoto = "http://ns.adobe.com/StockPhoto/1.0/"
82
+ XMP_NS_CreatorAtom = "http://ns.adobe.com/creatorAtom/1.0/"
83
+
84
+ # \name XML namespace constants for qualifiers and structured property fields.
85
+ # @{
86
+ #
87
+ # \def XMP_NS_XMP_IdentifierQual
88
+ # \brief The XML namespace for qualifiers of the xmp:Identifier property.
89
+ #
90
+ # \def XMP_NS_XMP_Dimensions
91
+ # \brief The XML namespace for fields of the Dimensions type.
92
+ #
93
+ # \def XMP_NS_XMP_Image
94
+ # \brief The XML namespace for fields of a graphical image. Used for the Thumbnail type.
95
+ #
96
+ # \def XMP_NS_XMP_ResourceEvent
97
+ # \brief The XML namespace for fields of the ResourceEvent type.
98
+ #
99
+ # \def XMP_NS_XMP_ResourceRef
100
+ # \brief The XML namespace for fields of the ResourceRef type.
101
+ #
102
+ # \def XMP_NS_XMP_ST_Version
103
+ # \brief The XML namespace for fields of the Version type.
104
+ #
105
+ # \def XMP_NS_XMP_ST_Job
106
+ # \brief The XML namespace for fields of the JobRef type.
107
+
108
+ XMP_NS_XMP_IdentifierQual = "http://ns.adobe.com/xmp/Identifier/qual/1.0/"
109
+ XMP_NS_XMP_Dimensions = "http://ns.adobe.com/xap/1.0/sType/Dimensions#"
110
+ XMP_NS_XMP_Text = "http://ns.adobe.com/xap/1.0/t/"
111
+ XMP_NS_XMP_PagedFile = "http://ns.adobe.com/xap/1.0/t/pg/"
112
+ XMP_NS_XMP_Graphics = "http://ns.adobe.com/xap/1.0/g/"
113
+ XMP_NS_XMP_Image = "http://ns.adobe.com/xap/1.0/g/img/"
114
+ XMP_NS_XMP_Font = "http://ns.adobe.com/xap/1.0/sType/Font#"
115
+ XMP_NS_XMP_ResourceEvent = "http://ns.adobe.com/xap/1.0/sType/ResourceEvent#"
116
+ XMP_NS_XMP_ResourceRef = "http://ns.adobe.com/xap/1.0/sType/ResourceRef#"
117
+ XMP_NS_XMP_ST_Version = "http://ns.adobe.com/xap/1.0/sType/Version#"
118
+ XMP_NS_XMP_ST_Job = "http://ns.adobe.com/xap/1.0/sType/Job#"
119
+ XMP_NS_XMP_ManifestItem = "http://ns.adobe.com/xap/1.0/sType/ManifestItem#"
120
+
121
+ # Deprecated XML namespace constants
122
+ XMP_NS_XMP_T = "http://ns.adobe.com/xap/1.0/t/"
123
+ XMP_NS_XMP_T_PG = "http://ns.adobe.com/xap/1.0/t/pg/"
124
+ XMP_NS_XMP_G_IMG = "http://ns.adobe.com/xap/1.0/g/img/"
125
+
126
+ # XML namespace constants from outside Adobe.
127
+ # @{
128
+ #
129
+ # \def XMP_NS_DC
130
+ # \brief The XML namespace for the Dublin Core schema.
131
+ #
132
+ # \def XMP_NS_IPTCCore
133
+ # \brief The XML namespace for the IPTC Core schema.
134
+ #
135
+ # \def XMP_NS_RDF
136
+ # \brief The XML namespace for RDF.
137
+ #
138
+ # \def XMP_NS_XML
139
+ # \brief The XML namespace for XML.
140
+
141
+ XMP_NS_DC = "http://purl.org/dc/elements/1.1/"
142
+
143
+ XMP_NS_IPTCCore = "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/"
144
+
145
+ XMP_NS_DICOM = "http://ns.adobe.com/DICOM/"
146
+
147
+ XMP_NS_PDFA_Schema = "http://www.aiim.org/pdfa/ns/schema#"
148
+ XMP_NS_PDFA_Property = "http://www.aiim.org/pdfa/ns/property#"
149
+ XMP_NS_PDFA_Type = "http://www.aiim.org/pdfa/ns/type#"
150
+ XMP_NS_PDFA_Field = "http://www.aiim.org/pdfa/ns/field#"
151
+ XMP_NS_PDFA_ID = "http://www.aiim.org/pdfa/ns/id/"
152
+ XMP_NS_PDFA_Extension = "http://www.aiim.org/pdfa/ns/extension/"
153
+
154
+ XMP_NS_PDFX = "http://ns.adobe.com/pdfx/1.3/"
155
+ XMP_NS_PDFX_ID = "http://www.npes.org/pdfx/ns/id/"
156
+
157
+ XMP_NS_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
158
+ XMP_NS_XML = "http://www.w3.org/XML/1998/namespace"
159
+
160
+ end
161
+ end
@@ -0,0 +1,3 @@
1
+ module Exempi
2
+ VERSION = "0.1"
3
+ end
@@ -0,0 +1,48 @@
1
+ require 'exempi/consts'
2
+
3
+ require 'minitest/autorun'
4
+
5
+ describe "dataconverter helper" do
6
+ it "should be able to look up single symbol values" do
7
+ Exempi::XmpOpenFileOptions.to_native(:XMP_OPEN_READ, nil).must_equal 0x00000001
8
+ end
9
+
10
+ it "should be able to look up an array of symbol values" do
11
+ Exempi::XmpOpenFileOptions.to_native([:XMP_OPEN_USESMARTHANDLER,
12
+ :XMP_OPEN_USEPACKETSCANNING], nil).must_equal 0x00000060
13
+ end
14
+
15
+ it "should be able to handle pure integer values" do
16
+ Exempi::XmpOpenFileOptions.to_native(0x00000001, nil).must_equal 0x00000001
17
+ end
18
+
19
+ it "should return 0 when nil is passed" do
20
+ Exempi::XmpOpenFileOptions.to_native(nil, nil).must_equal 0
21
+ end
22
+
23
+ it "should raise when invalid options are passed" do
24
+ lambda do
25
+ Exempi::XmpOpenFileOptions.to_native(:notanoption, nil)
26
+ end.must_raise Exempi::InvalidOptionError
27
+
28
+ lambda do
29
+ Exempi::XmpOpenFileOptions.to_native([:notanoption, :notanoption], nil)
30
+ end.must_raise Exempi::InvalidOptionError
31
+ end
32
+
33
+ it "should be able to translate integers into hashes of options" do
34
+ hash = Exempi.parse_bitmask 0x00000060, Exempi::XMP_OPEN_FILE_OPTIONS
35
+
36
+ assert hash[:XMP_OPEN_USESMARTHANDLER]
37
+ assert hash[:XMP_OPEN_USEPACKETSCANNING]
38
+ refute hash[:XMP_OPEN_FORUPDATE]
39
+ end
40
+
41
+ it "should optionally be able to return short option names" do
42
+ hash = Exempi.parse_bitmask 0x00000060, Exempi::XMP_OPEN_FILE_OPTIONS, true
43
+
44
+ assert hash[:usesmarthandler]
45
+ assert hash[:usepacketscanning]
46
+ refute hash[:forupdate]
47
+ end
48
+ end
@@ -0,0 +1,35 @@
1
+ require 'exempi/consts'
2
+
3
+ require 'minitest/autorun'
4
+
5
+ describe "DateTime struct wrapper" do
6
+ it "should be able to construct an XmpDateTime struct manually" do
7
+ date = Exempi::XmpDateTime.new
8
+ date[:year] = 2012
9
+ date[:month] = 9
10
+ date[:day] = 7
11
+ end
12
+
13
+ it "should be able to create an XmpDateTime struct from a DateTime object" do
14
+ today = DateTime.now
15
+ date = Exempi::XmpDateTime.from_datetime today
16
+
17
+ date[:year].must_equal today.year
18
+ end
19
+
20
+ it "should be able to create an XmpDateTime struct from a string" do
21
+ today = '2012-09-07'
22
+ date = Exempi::XmpDateTime.from_datetime today
23
+
24
+ date[:year].must_equal 2012
25
+ date[:month].must_equal 9
26
+ date[:day].must_equal 7
27
+ end
28
+
29
+ it "should be able to translate XmpDateTime structs into DateTime objects" do
30
+ today = DateTime.now
31
+ date = Exempi::XmpDateTime.from_datetime today
32
+
33
+ date.to_datetime.must_equal today
34
+ end
35
+ end
@@ -0,0 +1,22 @@
1
+ require 'exempi'
2
+
3
+ require 'minitest/autorun'
4
+
5
+ describe "Exempi helpers" do
6
+ it "should be able to silence stderr for noisy blocks" do
7
+ Exempi.verbose = false
8
+ lambda do
9
+ Exempi.send(:shutup!) { STDERR.puts "You should not see this" }
10
+ end.must_be_silent
11
+ end
12
+
13
+ it "should wrap Exempi functions correctly" do
14
+ Exempi.verbose = false
15
+ lambda do
16
+ # Exempi spews errors to stderr, so we've wrapped attach_function
17
+ # in order to ensure that every function call is wrapped with
18
+ # the silencing shutup! method
19
+ Exempi.xmp_files_open_new('no_file_here', 0)
20
+ end.must_be_silent
21
+ end
22
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: exempi
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Misty De Meo
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-12-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ffi
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.1.5
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 1.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: 0.9.2.2
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 0.9.2.2
41
+ description: |2
42
+ Exempi is a thin FFI-based wrapper around the
43
+ Exempi library. It provides a variety of
44
+ functions for reading, writing, and manipulating
45
+ XMP metadata.
46
+ email:
47
+ - mistydemeo@gmail.com
48
+ executables: []
49
+ extensions: []
50
+ extra_rdoc_files: []
51
+ files:
52
+ - ".gitignore"
53
+ - Gemfile
54
+ - LICENSE
55
+ - README.md
56
+ - Rakefile
57
+ - exempi.gemspec
58
+ - lib/exempi.rb
59
+ - lib/exempi/consts.rb
60
+ - lib/exempi/errors.rb
61
+ - lib/exempi/exceptions.rb
62
+ - lib/exempi/exempi.rb
63
+ - lib/exempi/namespaces.rb
64
+ - lib/exempi/version.rb
65
+ - test/dataconverter_test.rb
66
+ - test/datetime_test.rb
67
+ - test/helpers_test.rb
68
+ homepage: https://github.com/mistydemeo/exempi
69
+ licenses: []
70
+ metadata: {}
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ requirements: []
86
+ rubyforge_project:
87
+ rubygems_version: 2.4.5.1
88
+ signing_key:
89
+ specification_version: 4
90
+ summary: Ruby wrapper for Exempi
91
+ test_files:
92
+ - test/dataconverter_test.rb
93
+ - test/datetime_test.rb
94
+ - test/helpers_test.rb
95
+ has_rdoc: