comparison src/name/blackcap/exifwasher/exiv2/native.cpp @ 13:a59d84674fb0

Make it seamlessly work on IPTC and XMP metadata, too.
author David Barts <davidb@stashtea.com>
date Sat, 11 Apr 2020 09:14:31 -0700
parents efd9fe2d70d7
children cd2ca4727b7f
comparison
equal deleted inserted replaced
12:9ac6136c710c 13:a59d84674fb0
1 #include <jni.h> 1 #include <jni.h>
2 #include <string.h>
2 #include <exiv2/exiv2.hpp> 3 #include <exiv2/exiv2.hpp>
3 #include <exception> 4 #include <exception>
4 #include <iostream> 5 #include <iostream>
5 #include <iomanip> 6 #include <iomanip>
6 #include <cassert> 7 #include <cassert>
14 #endif 15 #endif
15 16
16 /* 17 /*
17 * Utility function to get pointer field. 18 * Utility function to get pointer field.
18 */ 19 */
19 jlong getPointer(JNIEnv *jEnv, jobject jThis) { 20 Exiv2::Image *getPointer(JNIEnv *jEnv, jobject jThis) {
20 return jEnv->GetLongField(jThis, jEnv->GetFieldID(jEnv->GetObjectClass(jThis), "pointer", "J")); 21 return reinterpret_cast<Exiv2::Image *>
22 (jEnv->GetLongField(jThis,
23 jEnv->GetFieldID(jEnv->GetObjectClass(jThis), "pointer", "J")));
21 } 24 }
22 25
23 /* 26 /*
24 * Class: name_blackcap_exifwasher_exiv2_Image 27 * Class: name_blackcap_exifwasher_exiv2_Image
25 * Method: _ctor 28 * Method: _ctor
45 * Method: _writeMetadata 48 * Method: _writeMetadata
46 * Signature: ()V 49 * Signature: ()V
47 */ 50 */
48 JNIEXPORT void JNICALL Java_name_blackcap_exifwasher_exiv2_Image__1writeMetadata 51 JNIEXPORT void JNICALL Java_name_blackcap_exifwasher_exiv2_Image__1writeMetadata
49 (JNIEnv *jEnv, jobject jThis) { 52 (JNIEnv *jEnv, jobject jThis) {
50 Exiv2::Image *image = reinterpret_cast<Exiv2::Image *> (getPointer(jEnv, jThis)); 53 Exiv2::Image *image = getPointer(jEnv, jThis);
51 if (jEnv->ExceptionCheck()) return; 54 if (jEnv->ExceptionCheck()) return;
52 try { 55 try {
53 image->writeMetadata(); 56 image->writeMetadata();
54 } catch (std::exception& e) { 57 } catch (std::exception& e) {
55 jEnv->ExceptionClear(); 58 jEnv->ExceptionClear();
63 * Method: _getMetadata 66 * Method: _getMetadata
64 * Signature: ()J 67 * Signature: ()J
65 */ 68 */
66 JNIEXPORT jlong JNICALL Java_name_blackcap_exifwasher_exiv2_Image__1getMetadata 69 JNIEXPORT jlong JNICALL Java_name_blackcap_exifwasher_exiv2_Image__1getMetadata
67 (JNIEnv *jEnv, jobject jThis) { 70 (JNIEnv *jEnv, jobject jThis) {
68 Exiv2::Image *image = reinterpret_cast<Exiv2::Image *> (getPointer(jEnv, jThis)); 71 Exiv2::Image *image = getPointer(jEnv, jThis);
69 if (jEnv->ExceptionCheck()) return 0; 72 if (jEnv->ExceptionCheck()) return 0;
70 try { 73 try {
71 image->readMetadata(); 74 image->readMetadata();
72 } catch (std::exception& e) { 75 } catch (std::exception& e) {
73 jEnv->ExceptionClear(); 76 jEnv->ExceptionClear();
74 jclass ex = jEnv->FindClass("name/blackcap/exifwasher/exiv2/Exiv2Exception"); 77 jclass ex = jEnv->FindClass("name/blackcap/exifwasher/exiv2/Exiv2Exception");
75 jEnv->ThrowNew(ex, e.what()); 78 jEnv->ThrowNew(ex, e.what());
76 return 0; 79 return 0;
77 } 80 }
78 return reinterpret_cast<jlong> (&(image->exifData())); 81 return reinterpret_cast<jlong>(image);
79 } 82 }
80 83
81 /* 84 /*
82 * Class: name_blackcap_exifwasher_exiv2_Image 85 * Class: name_blackcap_exifwasher_exiv2_Image
83 * Method: _dtor 86 * Method: _dtor
84 * Signature: ()V 87 * Signature: ()V
85 */ 88 */
86 JNIEXPORT void JNICALL Java_name_blackcap_exifwasher_exiv2_Image__1dtor 89 JNIEXPORT void JNICALL Java_name_blackcap_exifwasher_exiv2_Image__1dtor
87 (JNIEnv *jEnv, jobject jThis) { 90 (JNIEnv *jEnv, jobject jThis) {
88 Exiv2::Image *image = reinterpret_cast<Exiv2::Image *> (getPointer(jEnv, jThis)); 91 Exiv2::Image *image = getPointer(jEnv, jThis);
89 if (jEnv->ExceptionCheck()) return; 92 if (jEnv->ExceptionCheck()) return;
90 delete image; 93 delete image;
91 } 94 }
92 95
93 #ifdef __cplusplus 96 #ifdef __cplusplus
94 } 97 }
95 #endif 98 #endif
96 #endif 99 #endif
97 /* Header for class name_blackcap_exifwasher_exiv2_ExifData */ 100 /* Header for class name_blackcap_exifwasher_exiv2_ExifData */
98 101
102 /*
103 * Utility function to delete a (key, value) pair.
104 */
105 template<class D, class K>
106 void deleteValue(D &data, const char *key) {
107 typename D::iterator found = data.findKey(K(std::string(key)));
108 data.erase(found);
109 }
110
111 /*
112 * Utility function to retrieve a value, given a key.
113 */
114 template<class D, class K>
115 jobject getValue(JNIEnv *jEnv, D &data, const char *key) {
116 typename D::iterator found = data.findKey(K(std::string(key)));
117 if (found == data.end()) {
118 return NULL;
119 }
120 jclass klass = jEnv->FindClass("name/blackcap/exifwasher/exiv2/ExifData$Value");
121 if (jEnv->ExceptionCheck()) return NULL;
122 jstring type = jEnv->NewStringUTF(found->typeName());
123 jstring value = jEnv->NewStringUTF(found->toString().c_str());
124 jmethodID method = jEnv->GetMethodID(klass, "<init>", "(Ljava/lang/String;Ljava/lang/String;)V");
125 if (jEnv->ExceptionCheck()) return NULL;
126 return jEnv->NewObject(klass, method, type, value);
127 }
128
129 /*
130 * Utility function to read keys, given a target to put them and a collection
131 * of data.
132 */
133 template<class D>
134 jsize getKeys(JNIEnv *jEnv, jobjectArray target, jsize start, D &data) {
135 typename D::const_iterator end = data.end();
136 jsize j = start;
137 for (typename D::const_iterator i = data.begin(); i != end; ++i) {
138 jEnv->SetObjectArrayElement(target, j++, jEnv->NewStringUTF(i->key().c_str()));
139 if (jEnv->ExceptionCheck()) break;
140 }
141 return j;
142 }
143
99 #ifndef _Included_name_blackcap_exifwasher_exiv2_ExifData 144 #ifndef _Included_name_blackcap_exifwasher_exiv2_ExifData
100 #define _Included_name_blackcap_exifwasher_exiv2_ExifData 145 #define _Included_name_blackcap_exifwasher_exiv2_ExifData
101 #ifdef __cplusplus 146 #ifdef __cplusplus
102 extern "C" { 147 extern "C" {
103 #endif 148 #endif
149
150 /*
151 * Utility function to parse out a type prefix from a key name.
152 */
153 char *getType(const char *key) {
154 char *dot = strchr(key, '.');
155 if (dot == NULL)
156 throw std::invalid_argument(std::string("invalid key: ") + std::string(key));
157 int length = dot - key;
158 char *ret = (char *) malloc(length + 1);
159 memcpy(ret, key, length);
160 ret[length] = '\0';
161 return ret;
162 }
163
104 /* 164 /*
105 * Class: name_blackcap_exifwasher_exiv2_ExifData 165 * Class: name_blackcap_exifwasher_exiv2_ExifData
106 * Method: _erase 166 * Method: _erase
107 * Signature: (Ljava/lang/String;)V 167 * Signature: (Ljava/lang/String;)V
108 */ 168 */
109 JNIEXPORT void JNICALL Java_name_blackcap_exifwasher_exiv2_ExifData__1erase 169 JNIEXPORT void JNICALL Java_name_blackcap_exifwasher_exiv2_ExifData__1erase
110 (JNIEnv *jEnv, jobject jThis, jstring key) { 170 (JNIEnv *jEnv, jobject jThis, jstring key) {
111 Exiv2::ExifData *data = reinterpret_cast<Exiv2::ExifData *> (getPointer(jEnv, jThis)); 171 Exiv2::Image *image = getPointer(jEnv, jThis);
112 if (jEnv->ExceptionCheck()) return; 172 if (jEnv->ExceptionCheck()) return;
113 const char *cKey = jEnv->GetStringUTFChars(key, NULL); 173 const char *cKey = jEnv->GetStringUTFChars(key, NULL);
114 Exiv2::ExifData::iterator found = data->findKey(Exiv2::ExifKey(std::string(cKey))); 174 char *type = NULL;
115 try { 175 try {
116 data->erase(found); 176 type = getType(cKey);
117 } catch (std::exception& e) { 177 if (!strcmp(type, "Exif")) {
118 jEnv->ExceptionClear(); 178 deleteValue<Exiv2::ExifData, Exiv2::ExifKey>(image->exifData(), cKey);
119 jclass ex = jEnv->FindClass("name/blackcap/exifwasher/exiv2/Exiv2Exception"); 179 } else if (!strcmp(type, "Xmp")) {
120 jEnv->ThrowNew(ex, e.what()); 180 deleteValue<Exiv2::XmpData, Exiv2::XmpKey>(image->xmpData(), cKey);
121 } 181 } else if (!strcmp(type, "Iptc")) {
182 deleteValue<Exiv2::IptcData, Exiv2::IptcKey>(image->iptcData(), cKey);
183 } else {
184 throw std::invalid_argument(std::string("invalid key: ") + std::string(cKey));
185 }
186 } catch (std::exception& e) {
187 jEnv->ExceptionClear();
188 jclass ex = jEnv->FindClass("name/blackcap/exifwasher/exiv2/Exiv2Exception");
189 jEnv->ThrowNew(ex, e.what());
190 }
191 if (type != NULL) free(type);
122 jEnv->ReleaseStringUTFChars(key, cKey); 192 jEnv->ReleaseStringUTFChars(key, cKey);
123 } 193 }
124 194
125 /* 195 /*
126 * Class: name_blackcap_exifwasher_exiv2_ExifData 196 * Class: name_blackcap_exifwasher_exiv2_ExifData
127 * Method: _value 197 * Method: _value
128 * Signature: (Ljava/lang/String;)Lname/blackcap/exifwasher/exiv2/ExifData/Value; 198 * Signature: (Ljava/lang/String;)Lname/blackcap/exifwasher/exiv2/ExifData/Value;
129 */ 199 */
130 JNIEXPORT jobject JNICALL Java_name_blackcap_exifwasher_exiv2_ExifData__1value 200 JNIEXPORT jobject JNICALL Java_name_blackcap_exifwasher_exiv2_ExifData__1value
131 (JNIEnv *jEnv, jobject jThis, jstring key) { 201 (JNIEnv *jEnv, jobject jThis, jstring key) {
132 Exiv2::ExifData *data = reinterpret_cast<Exiv2::ExifData *> (getPointer(jEnv, jThis)); 202 Exiv2::Image *image = getPointer(jEnv, jThis);
133 if (jEnv->ExceptionCheck()) return NULL; 203 if (jEnv->ExceptionCheck()) return NULL;
134 const char *cKey = jEnv->GetStringUTFChars(key, NULL); 204 const char *cKey = jEnv->GetStringUTFChars(key, NULL);
135 Exiv2::ExifData::const_iterator found = data->findKey(Exiv2::ExifKey(std::string(cKey))); 205 char *type = NULL;
206 jobject ret = NULL;
207 try {
208 type = getType(cKey);
209 if (!strcmp(type, "Exif")) {
210 ret = getValue<Exiv2::ExifData, Exiv2::ExifKey>(jEnv, image->exifData(), cKey);
211 } else if (!strcmp(type, "Xmp")) {
212 ret = getValue<Exiv2::XmpData, Exiv2::XmpKey>(jEnv, image->xmpData(), cKey);
213 } else if (!strcmp(type, "Iptc")) {
214 ret = getValue<Exiv2::IptcData, Exiv2::IptcKey>(jEnv, image->iptcData(), cKey);
215 } else {
216 throw std::invalid_argument(std::string("invalid key: ") + std::string(cKey));
217 }
218 } catch (std::exception& e) {
219 jEnv->ExceptionClear();
220 jclass ex = jEnv->FindClass("name/blackcap/exifwasher/exiv2/Exiv2Exception");
221 jEnv->ThrowNew(ex, e.what());
222 }
223 if (type != NULL) free(type);
136 jEnv->ReleaseStringUTFChars(key, cKey); 224 jEnv->ReleaseStringUTFChars(key, cKey);
137 if (found == data->end()) { 225 return ret;
138 return NULL;
139 }
140 jclass klass = jEnv->FindClass("name/blackcap/exifwasher/exiv2/ExifData$Value");
141 if (jEnv->ExceptionCheck()) return NULL;
142 jstring type = jEnv->NewStringUTF(found->typeName());
143 jstring value = jEnv->NewStringUTF(found->toString().c_str());
144 jmethodID method = jEnv->GetMethodID(klass, "<init>", "(Ljava/lang/String;Ljava/lang/String;)V");
145 if (jEnv->ExceptionCheck()) return NULL;
146 return jEnv->NewObject(klass, method, type, value);
147 } 226 }
148 227
149 /* 228 /*
150 * Class: name_blackcap_exifwasher_exiv2_ExifData 229 * Class: name_blackcap_exifwasher_exiv2_ExifData
151 * Method: _keys 230 * Method: _keys
152 * Signature: ()[Ljava/lang/String; 231 * Signature: ()[Ljava/lang/String;
153 */ 232 */
154 JNIEXPORT jobjectArray JNICALL Java_name_blackcap_exifwasher_exiv2_ExifData__1keys 233 JNIEXPORT jobjectArray JNICALL Java_name_blackcap_exifwasher_exiv2_ExifData__1keys
155 (JNIEnv *jEnv, jobject jThis) { 234 (JNIEnv *jEnv, jobject jThis) {
156 Exiv2::ExifData *data = reinterpret_cast<Exiv2::ExifData *> (getPointer(jEnv, jThis)); 235 Exiv2::Image *image = getPointer(jEnv, jThis);
236 Exiv2::ExifData eData = image->exifData();
237 Exiv2::XmpData xData = image->xmpData();
238 Exiv2::IptcData iData = image->iptcData();
157 if (jEnv->ExceptionCheck()) return NULL; 239 if (jEnv->ExceptionCheck()) return NULL;
158 jclass klass = jEnv->FindClass("java/lang/String"); 240 jclass klass = jEnv->FindClass("java/lang/String");
159 if (jEnv->ExceptionCheck()) return NULL; 241 if (jEnv->ExceptionCheck()) return NULL;
160 jobjectArray ret = jEnv->NewObjectArray(data->count(), klass, NULL); 242 jobjectArray ret = jEnv->NewObjectArray(eData.count() + iData.count() + xData.count(), klass, NULL);
161 if (jEnv->ExceptionCheck()) return NULL; 243 if (jEnv->ExceptionCheck()) return NULL;
162 Exiv2::ExifData::const_iterator end = data->end(); 244 jsize start = getKeys(jEnv, ret, 0, eData);
163 jsize j = 0; 245 if (jEnv->ExceptionCheck()) return NULL;
164 for (Exiv2::ExifData::const_iterator i = data->begin(); i != end; ++i) { 246 start = getKeys(jEnv, ret, start, xData);
165 jEnv->SetObjectArrayElement(ret, j++, jEnv->NewStringUTF(i->key().c_str())); 247 if (jEnv->ExceptionCheck()) return NULL;
166 if (jEnv->ExceptionCheck()) break; 248 start = getKeys(jEnv, ret, start, iData);
167 } 249 if (jEnv->ExceptionCheck()) return NULL;
168 return ret; 250 return ret;
169 } 251 }
170 252
171 #ifdef __cplusplus 253 #ifdef __cplusplus
172 } 254 }