Mercurial > geeqie
comparison src/exiv2.cc @ 1069:aeae25d5d50d
infrastructure for preprocessing of metadata
author | nadvornik |
---|---|
date | Sat, 18 Oct 2008 18:22:57 +0000 |
parents | 90dcc0060e22 |
children | abf48b53216b |
comparison
equal
deleted
inserted
replaced
1068:e67636316f4c | 1069:aeae25d5d50d |
---|---|
59 #include "ui_fileops.h" | 59 #include "ui_fileops.h" |
60 } | 60 } |
61 | 61 |
62 struct _ExifData | 62 struct _ExifData |
63 { | 63 { |
64 Exiv2::Image::AutoPtr image; | |
65 Exiv2::Image::AutoPtr sidecar; | |
66 Exiv2::ExifData::const_iterator exifIter; /* for exif_get_next_item */ | 64 Exiv2::ExifData::const_iterator exifIter; /* for exif_get_next_item */ |
67 Exiv2::IptcData::const_iterator iptcIter; /* for exif_get_next_item */ | 65 Exiv2::IptcData::const_iterator iptcIter; /* for exif_get_next_item */ |
68 #if EXIV2_TEST_VERSION(0,16,0) | 66 #if EXIV2_TEST_VERSION(0,16,0) |
69 Exiv2::XmpData::const_iterator xmpIter; /* for exif_get_next_item */ | 67 Exiv2::XmpData::const_iterator xmpIter; /* for exif_get_next_item */ |
70 #endif | 68 #endif |
71 bool have_sidecar; | 69 |
70 virtual ~_ExifData() | |
71 { | |
72 } | |
73 | |
74 virtual void writeMetadata() | |
75 { | |
76 g_critical("Unsupported method of writing metadata"); | |
77 } | |
78 | |
79 virtual ExifData *original() | |
80 { | |
81 return NULL; | |
82 } | |
83 | |
84 virtual Exiv2::Image *image() = 0; | |
85 | |
86 virtual Exiv2::ExifData &exifData() = 0; | |
87 | |
88 virtual Exiv2::IptcData &iptcData() = 0; | |
89 | |
90 #if EXIV2_TEST_VERSION(0,16,0) | |
91 virtual Exiv2::XmpData &xmpData() = 0; | |
92 #endif | |
93 | |
94 virtual void add_jpeg_color_profile(unsigned char *cp_data, guint cp_length) = 0; | |
95 | |
96 virtual guchar *get_jpeg_color_profile(guint *data_len) = 0; | |
97 }; | |
98 | |
99 // This allows read-only access to the original metadata | |
100 struct _ExifDataOriginal : public _ExifData | |
101 { | |
102 protected: | |
103 Exiv2::Image::AutoPtr image_; | |
72 | 104 |
73 /* the icc profile in jpeg is not technically exif - store it here */ | 105 /* the icc profile in jpeg is not technically exif - store it here */ |
74 unsigned char *cp_data; | 106 unsigned char *cp_data_; |
75 guint cp_length; | 107 guint cp_length_; |
76 | 108 |
77 _ExifData(gchar *path, gchar *sidecar_path) | 109 public: |
78 { | 110 |
79 have_sidecar = false; | 111 _ExifDataOriginal(gchar *path) |
80 cp_data = NULL; | 112 { |
81 cp_length = 0; | 113 cp_data_ = NULL; |
114 cp_length_ = 0; | |
82 gchar *pathl = path_from_utf8(path); | 115 gchar *pathl = path_from_utf8(path); |
83 image = Exiv2::ImageFactory::open(pathl); | 116 image_ = Exiv2::ImageFactory::open(pathl); |
84 g_free(pathl); | 117 g_free(pathl); |
85 // g_assert (image.get() != 0); | 118 // g_assert (image.get() != 0); |
86 image->readMetadata(); | 119 image_->readMetadata(); |
87 | 120 |
88 #if EXIV2_TEST_VERSION(0,16,0) | 121 #if EXIV2_TEST_VERSION(0,16,0) |
89 DEBUG_2("xmp count %li", image->xmpData().count()); | 122 if (image_->mimeType() == "application/rdf+xml") |
90 if (sidecar_path && image->xmpData().empty()) | 123 { |
91 { | 124 //Exiv2 sidecar converts xmp to exif and iptc, we don't want it. |
92 gchar *sidecar_pathl = path_from_utf8(sidecar_path); | 125 image_->clearExifData(); |
93 sidecar = Exiv2::ImageFactory::open(sidecar_pathl); | 126 image_->clearIptcData(); |
94 g_free(sidecar_pathl); | 127 } |
95 sidecar->readMetadata(); | 128 #endif |
96 have_sidecar = sidecar->good(); | 129 |
97 DEBUG_2("sidecar xmp count %li", sidecar->xmpData().count()); | |
98 } | |
99 | |
100 #endif | |
101 #if EXIV2_TEST_VERSION(0,14,0) | 130 #if EXIV2_TEST_VERSION(0,14,0) |
102 if (image->mimeType() == std::string("image/jpeg")) | 131 if (image_->mimeType() == "image/jpeg") |
103 { | 132 { |
104 /* try to get jpeg color profile */ | 133 /* try to get jpeg color profile */ |
105 Exiv2::BasicIo &io = image->io(); | 134 Exiv2::BasicIo &io = image_->io(); |
106 gint open = io.isopen(); | 135 gint open = io.isopen(); |
107 if (!open) io.open(); | 136 if (!open) io.open(); |
108 unsigned char *mapped = (unsigned char*)io.mmap(); | 137 unsigned char *mapped = (unsigned char*)io.mmap(); |
109 if (mapped) exif_jpeg_parse_color(this, mapped, io.size()); | 138 if (mapped) exif_jpeg_parse_color(this, mapped, io.size()); |
110 io.munmap(); | 139 io.munmap(); |
111 if (!open) io.close(); | 140 if (!open) io.close(); |
112 } | 141 } |
113 #endif | 142 #endif |
114 } | 143 } |
115 | 144 |
116 ~_ExifData() | 145 virtual ~_ExifDataOriginal() |
117 { | 146 { |
118 if (cp_data) g_free(cp_data); | 147 if (cp_data_) g_free(cp_data_); |
119 } | 148 } |
120 | 149 |
121 void writeMetadata() | 150 virtual Exiv2::Image *image() |
122 { | 151 { |
123 if (have_sidecar) sidecar->writeMetadata(); | 152 return image_.get(); |
124 image->writeMetadata(); | 153 } |
125 } | 154 |
126 | 155 virtual Exiv2::ExifData &exifData () |
127 Exiv2::ExifData &exifData () | 156 { |
128 { | 157 return image_->exifData(); |
129 return image->exifData(); | 158 } |
130 } | 159 |
131 | 160 virtual Exiv2::IptcData &iptcData () |
132 Exiv2::IptcData &iptcData () | 161 { |
133 { | 162 return image_->iptcData(); |
134 return image->iptcData(); | 163 } |
135 } | 164 |
136 | 165 #if EXIV2_TEST_VERSION(0,16,0) |
137 #if EXIV2_TEST_VERSION(0,16,0) | 166 virtual Exiv2::XmpData &xmpData () |
138 Exiv2::XmpData &xmpData () | 167 { |
139 { | 168 return image_->xmpData(); |
140 return have_sidecar ? sidecar->xmpData() : image->xmpData(); | 169 } |
141 } | 170 #endif |
142 #endif | 171 |
143 | 172 virtual void add_jpeg_color_profile(unsigned char *cp_data, guint cp_length) |
173 { | |
174 if (cp_data_) g_free(cp_data_); | |
175 cp_data_ = cp_data; | |
176 cp_length_ = cp_length; | |
177 } | |
178 | |
179 virtual guchar *get_jpeg_color_profile(guint *data_len) | |
180 { | |
181 if (cp_data_) | |
182 { | |
183 if (data_len) *data_len = cp_length_; | |
184 return (unsigned char *) g_memdup(cp_data_, cp_length_); | |
185 } | |
186 return NULL; | |
187 } | |
144 }; | 188 }; |
145 | 189 |
190 // This allows read-write access to the metadata | |
191 struct _ExifDataProcessed : public _ExifData | |
192 { | |
193 protected: | |
194 _ExifDataOriginal *imageData_; | |
195 _ExifDataOriginal *sidecarData_; | |
196 | |
197 Exiv2::ExifData exifData_; | |
198 Exiv2::IptcData iptcData_; | |
199 #if EXIV2_TEST_VERSION(0,16,0) | |
200 Exiv2::XmpData xmpData_; | |
201 #endif | |
202 | |
203 public: | |
204 _ExifDataProcessed(gchar *path, gchar *sidecar_path) | |
205 { | |
206 imageData_ = new _ExifDataOriginal(path); | |
207 sidecarData_ = NULL; | |
208 #if EXIV2_TEST_VERSION(0,16,0) | |
209 xmpData_ = imageData_->xmpData(); | |
210 DEBUG_2("xmp count %li", xmpData_.count()); | |
211 if (sidecar_path && xmpData_.empty()) | |
212 { | |
213 sidecarData_ = new _ExifDataOriginal(sidecar_path); | |
214 xmpData_ = sidecarData_->xmpData(); | |
215 } | |
216 #endif | |
217 exifData_ = imageData_->exifData(); | |
218 iptcData_ = imageData_->iptcData(); | |
219 } | |
220 | |
221 virtual ~_ExifDataProcessed() | |
222 { | |
223 if (imageData_) delete imageData_; | |
224 if (sidecarData_) delete sidecarData_; | |
225 } | |
226 | |
227 virtual ExifData *original() | |
228 { | |
229 return imageData_; | |
230 } | |
231 | |
232 virtual void writeMetadata() | |
233 { | |
234 | |
235 if (sidecarData_) | |
236 { | |
237 sidecarData_->image()->setXmpData(xmpData_); | |
238 //Exiv2 sidecar converts xmp to exif and iptc, we don't want it. | |
239 sidecarData_->image()->clearExifData(); | |
240 sidecarData_->image()->clearIptcData(); | |
241 sidecarData_->image()->writeMetadata(); | |
242 } | |
243 else | |
244 { | |
245 imageData_->image()->setExifData(exifData_); | |
246 imageData_->image()->setIptcData(iptcData_); | |
247 imageData_->image()->setXmpData(xmpData_); | |
248 imageData_->image()->writeMetadata(); | |
249 } | |
250 } | |
251 | |
252 virtual Exiv2::Image *image() | |
253 { | |
254 return imageData_->image(); | |
255 } | |
256 | |
257 virtual Exiv2::ExifData &exifData () | |
258 { | |
259 return exifData_; | |
260 } | |
261 | |
262 virtual Exiv2::IptcData &iptcData () | |
263 { | |
264 return iptcData_; | |
265 } | |
266 | |
267 #if EXIV2_TEST_VERSION(0,16,0) | |
268 virtual Exiv2::XmpData &xmpData () | |
269 { | |
270 return xmpData_; | |
271 } | |
272 #endif | |
273 | |
274 virtual void add_jpeg_color_profile(unsigned char *cp_data, guint cp_length) | |
275 { | |
276 imageData_->add_jpeg_color_profile(cp_data, cp_length); | |
277 } | |
278 | |
279 virtual guchar *get_jpeg_color_profile(guint *data_len) | |
280 { | |
281 return imageData_->get_jpeg_color_profile(data_len); | |
282 } | |
283 }; | |
284 | |
285 | |
286 | |
287 | |
146 extern "C" { | 288 extern "C" { |
147 | 289 |
148 ExifData *exif_read(gchar *path, gchar *sidecar_path) | 290 ExifData *exif_read(gchar *path, gchar *sidecar_path) |
149 { | 291 { |
150 DEBUG_1("exif read %s, sidecar: %s", path, sidecar_path ? sidecar_path : "-"); | 292 DEBUG_1("exif read %s, sidecar: %s", path, sidecar_path ? sidecar_path : "-"); |
151 try { | 293 try { |
152 return new ExifData(path, sidecar_path); | 294 return new _ExifDataProcessed(path, sidecar_path); |
153 } | 295 } |
154 catch (Exiv2::AnyError& e) { | 296 catch (Exiv2::AnyError& e) { |
155 std::cout << "Caught Exiv2 exception '" << e << "'\n"; | 297 std::cout << "Caught Exiv2 exception '" << e << "'\n"; |
156 return NULL; | 298 return NULL; |
157 } | 299 } |
172 } | 314 } |
173 | 315 |
174 | 316 |
175 void exif_free(ExifData *exif) | 317 void exif_free(ExifData *exif) |
176 { | 318 { |
177 | 319 g_assert(dynamic_cast<_ExifDataProcessed *>(exif)); // this should not be called on ExifDataOriginal |
178 delete exif; | 320 delete exif; |
179 } | 321 } |
322 | |
323 ExifData *exif_get_original(ExifData *exif) | |
324 { | |
325 return exif->original(); | |
326 } | |
327 | |
180 | 328 |
181 ExifItem *exif_get_item(ExifData *exif, const gchar *key) | 329 ExifItem *exif_get_item(ExifData *exif, const gchar *key) |
182 { | 330 { |
183 try { | 331 try { |
184 Exiv2::Metadatum *item = NULL; | 332 Exiv2::Metadatum *item = NULL; |
586 } | 734 } |
587 } | 735 } |
588 | 736 |
589 void exif_add_jpeg_color_profile(ExifData *exif, unsigned char *cp_data, guint cp_length) | 737 void exif_add_jpeg_color_profile(ExifData *exif, unsigned char *cp_data, guint cp_length) |
590 { | 738 { |
591 if (exif->cp_data) g_free(exif->cp_data); | 739 exif->add_jpeg_color_profile(cp_data, cp_length); |
592 exif->cp_data = cp_data; | |
593 exif->cp_length =cp_length; | |
594 } | 740 } |
595 | 741 |
596 guchar *exif_get_color_profile(ExifData *exif, guint *data_len) | 742 guchar *exif_get_color_profile(ExifData *exif, guint *data_len) |
597 { | 743 { |
598 if (exif->cp_data) | 744 guchar *ret = exif->get_jpeg_color_profile(data_len); |
599 { | 745 if (ret) return ret; |
600 if (data_len) *data_len = exif->cp_length; | 746 |
601 return (unsigned char *) g_memdup(exif->cp_data, exif->cp_length); | |
602 } | |
603 ExifItem *prof_item = exif_get_item(exif, "Exif.Image.InterColorProfile"); | 747 ExifItem *prof_item = exif_get_item(exif, "Exif.Image.InterColorProfile"); |
604 if (prof_item && exif_item_get_format_id(prof_item) == EXIF_FORMAT_UNDEFINED) | 748 if (prof_item && exif_item_get_format_id(prof_item) == EXIF_FORMAT_UNDEFINED) |
605 return (unsigned char *) exif_item_get_data(prof_item, data_len); | 749 ret = (guchar *)exif_item_get_data(prof_item, data_len); |
606 return NULL; | 750 return ret; |
607 } | 751 } |
608 | 752 |
609 #if EXIV2_TEST_VERSION(0,17,90) | 753 #if EXIV2_TEST_VERSION(0,17,90) |
610 | 754 |
611 guchar *exif_get_preview(ExifData *exif, guint *data_len, gint requested_width, gint requested_height) | 755 guchar *exif_get_preview(ExifData *exif, guint *data_len, gint requested_width, gint requested_height) |
612 { | 756 { |
613 if (!exif) return NULL; | 757 if (!exif) return NULL; |
614 | 758 |
615 const char* path = exif->image->io().path().c_str(); | 759 const char* path = exif->image()->io().path().c_str(); |
616 /* given image pathname, first do simple (and fast) file extension test */ | 760 /* given image pathname, first do simple (and fast) file extension test */ |
617 gboolean is_raw = filter_file_class(path, FORMAT_CLASS_RAWIMAGE); | 761 gboolean is_raw = filter_file_class(path, FORMAT_CLASS_RAWIMAGE); |
618 | 762 |
619 if (!is_raw && requested_width == 0) return NULL; | 763 if (!is_raw && requested_width == 0) return NULL; |
620 | 764 |
621 try { | 765 try { |
622 | 766 |
623 Exiv2::PreviewManager pm(*exif->image); | 767 Exiv2::PreviewManager pm(*exif->image()); |
624 | 768 |
625 Exiv2::PreviewPropertiesList list = pm.getPreviewProperties(); | 769 Exiv2::PreviewPropertiesList list = pm.getPreviewProperties(); |
626 | 770 |
627 if (!list.empty()) | 771 if (!list.empty()) |
628 { | 772 { |
710 extern "C" guchar *exif_get_preview(ExifData *exif, guint *data_len, gint requested_width, gint requested_height) | 854 extern "C" guchar *exif_get_preview(ExifData *exif, guint *data_len, gint requested_width, gint requested_height) |
711 { | 855 { |
712 unsigned long offset; | 856 unsigned long offset; |
713 | 857 |
714 if (!exif) return NULL; | 858 if (!exif) return NULL; |
715 const char* path = exif->image->io().path().c_str(); | 859 const char* path = exif->image()->io().path().c_str(); |
716 | 860 |
717 /* given image pathname, first do simple (and fast) file extension test */ | 861 /* given image pathname, first do simple (and fast) file extension test */ |
718 if (!filter_file_class(path, FORMAT_CLASS_RAWIMAGE)) return NULL; | 862 if (!filter_file_class(path, FORMAT_CLASS_RAWIMAGE)) return NULL; |
719 | 863 |
720 try { | 864 try { |
722 guchar *map_data; | 866 guchar *map_data; |
723 size_t map_len; | 867 size_t map_len; |
724 UnmapData *ud; | 868 UnmapData *ud; |
725 int fd; | 869 int fd; |
726 | 870 |
727 RawFile rf(exif->image->io()); | 871 RawFile rf(exif->image()->io()); |
728 offset = rf.preview_offset(); | 872 offset = rf.preview_offset(); |
729 DEBUG_1("%s: offset %lu", path, offset); | 873 DEBUG_1("%s: offset %lu", path, offset); |
730 | 874 |
731 fd = open(path, O_RDONLY); | 875 fd = open(path, O_RDONLY); |
732 if (fd == -1) | 876 if (fd == -1) |