1 // ***************************************************************** -*- C++ -*-
2 /*
3 * Copyright (C) 2009 Brad Schick <schickb@gmail.com>
4 *
5 * This file is part of the organize tool.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, 5th Floor, Boston, MA 02110-1301 USA.
20 */
21 // *****************************************************************************
22
23 #include <boost/algorithm/string.hpp>
24 #include <boost/regex.hpp>
25 #include <boost/format.hpp>
26 #include <boost/lexical_cast.hpp>
27 #include <exiv2/image.hpp>
28 #include <exiv2/easyaccess.hpp>
29 #include <exiv2/exif.hpp>
30 #include <exiv2/iptc.hpp>
31 #include <exiv2/tags.hpp>
32 //#include <exiv2/xmp.hpp>
33 #include <cassert>
34 #include <sstream>
35 #include <ctime>
36 #include "helpers.hpp"
37
38 #define BOOST_FILESYSTEM_NO_DEPRECATED
39
40 namespace fs = boost::filesystem;
41 typedef Exiv2::ExifData::const_iterator (*EasyAccessFct)(const Exiv2::ExifData& ed);
42
43
scrub(const std::string & dirty,bool strip_space=false)44 std::string scrub(const std::string &dirty, bool strip_space = false)
45 {
46 std::string scrub = boost::trim_copy(dirty);
47 if(strip_space) {
48 boost::regex space("\\s");
49 scrub = boost::regex_replace(scrub, space, "");
50 }
51 boost::regex dash("[:/\\\\|<>]");
52 boost::regex under("[\"'\\[\\]\\{\\}#=%\\$\\?,\\+\\*]");
53 scrub = boost::regex_replace(scrub, dash, "-");
54
55 return boost::regex_replace(scrub, under, "_");
56 }
57
exif_data(const Exiv2::Image * image,const char * key,Exiv2::ExifData::const_iterator & md)58 bool exif_data(const Exiv2::Image *image, const char *key, Exiv2::ExifData::const_iterator &md)
59 {
60 assert(image && key);
61 bool ok = false;
62 try {
63 const Exiv2::ExifData &exifData = image->exifData();
64 Exiv2::ExifKey exifKey(key);
65 md = exifData.findKey(exifKey);
66 if(md != exifData.end() && md->typeId() != Exiv2::undefined)
67 ok = true;
68 }
69 catch(const Exiv2::AnyError&) {
70 }
71 return ok;
72 }
73
exif_data_easy(const Exiv2::Image * image,EasyAccessFct easy,Exiv2::ExifData::const_iterator & md)74 bool exif_data_easy(const Exiv2::Image *image, EasyAccessFct easy, Exiv2::ExifData::const_iterator &md)
75 {
76 assert(image && easy);
77 bool ok = false;
78 try {
79 const Exiv2::ExifData &exifData = image->exifData();
80 md = easy(exifData);
81 if(md != exifData.end() && md->typeId() != Exiv2::undefined)
82 ok = true;
83 }
84 catch(const Exiv2::AnyError&) {
85 }
86 return ok;
87 }
88
89
iptc_data(const Exiv2::Image * image,const char * key,Exiv2::IptcData::const_iterator & md)90 bool iptc_data(const Exiv2::Image *image, const char *key, Exiv2::IptcData::const_iterator &md)
91 {
92 bool ok = false;
93 assert(image && key);
94 try {
95 const Exiv2::IptcData &iptcData = image->iptcData();
96 Exiv2::IptcKey iptcKey(key);
97 md = iptcData.findKey(iptcKey);
98 if(md != iptcData.end() && md->typeId() != Exiv2::undefined)
99 ok = true;
100 }
101 catch(const Exiv2::AnyError&) {
102 }
103 return ok;
104 }
105
exif_date(const Exiv2::Image * image,const fs::path &)106 std::string exif_date(const Exiv2::Image *image, const fs::path &)
107 {
108 Exiv2::ExifData::const_iterator md;
109 bool done = exif_data(image, "Exif.Photo.DateTimeDigitized", md);
110 if(!done)
111 done = exif_data(image, "Exif.Photo.DateTimeOriginal", md);
112 if(!done)
113 return "";
114
115 std::string date = scrub(md->print().substr(0,10));
116 // Some files have zeros for dates, just fail in that case
117 if(boost::lexical_cast<int>(date.substr(0,4))==0)
118 return "";
119
120 return date;
121 }
122
exif_year(const Exiv2::Image * image,const fs::path & path)123 std::string exif_year(const Exiv2::Image *image, const fs::path &path)
124 {
125 std::string date = exif_date(image, path);
126 if(date.length())
127 return date.substr(0,4);
128 else
129 return date;
130 }
131
exif_month(const Exiv2::Image * image,const fs::path & path)132 std::string exif_month(const Exiv2::Image *image, const fs::path &path)
133 {
134 std::string date = exif_date(image, path);
135 if(date.length())
136 return date.substr(5,2);
137 else
138 return date;
139 }
140
exif_day(const Exiv2::Image * image,const fs::path & path)141 std::string exif_day(const Exiv2::Image *image, const fs::path &path)
142 {
143 std::string date = exif_date(image, path);
144 if(date.length())
145 return date.substr(8,2);
146 else
147 return date;
148 }
149
iptc_get_date(const Exiv2::Image * image,Exiv2::DateValue::Date & date)150 bool iptc_get_date(const Exiv2::Image *image, Exiv2::DateValue::Date &date)
151 {
152 Exiv2::IptcData::const_iterator md;
153 bool done = iptc_data(image, "Iptc.Application2.DigitizationDate", md);
154 if(!done)
155 done = iptc_data(image, "Iptc.Application2.DateCreated", md);
156 if(!done)
157 return false;
158 date = ((Exiv2::DateValue*)md->getValue().get())->getDate();
159 return date.year > 0;
160 }
161
iptc_date(const Exiv2::Image * image,const fs::path &)162 std::string iptc_date(const Exiv2::Image *image, const fs::path &)
163 {
164 Exiv2::DateValue::Date date;
165 if(iptc_get_date(image, date))
166 return str(boost::format("%4d-%02d-%02d") % date.year % date.month % date.day);
167 else
168 return "";
169 }
170
iptc_year(const Exiv2::Image * image,const fs::path &)171 std::string iptc_year(const Exiv2::Image *image, const fs::path &)
172 {
173 Exiv2::DateValue::Date date;
174 if(iptc_get_date(image, date))
175 return str(boost::format("%4d") % date.year);
176 else
177 return "";
178 }
179
iptc_month(const Exiv2::Image * image,const fs::path &)180 std::string iptc_month(const Exiv2::Image *image, const fs::path &)
181 {
182 Exiv2::DateValue::Date date;
183 if(iptc_get_date(image, date))
184 return str(boost::format("%02d") % date.month);
185 else
186 return "";
187 }
188
iptc_day(const Exiv2::Image * image,const fs::path &)189 std::string iptc_day(const Exiv2::Image *image, const fs::path &)
190 {
191 Exiv2::DateValue::Date date;
192 if(iptc_get_date(image, date))
193 return str(boost::format("%02d") % date.day);
194 else
195 return "";
196 }
197
file_get_tm(const fs::path & path,std::tm & tm)198 bool file_get_tm(const fs::path &path, std::tm &tm)
199 {
200 std::time_t timer = fs::last_write_time(path);
201 if(time > 0) {
202 tm = *localtime(&timer);
203 return true;
204 }
205 else {
206 return false;
207 }
208 }
209
file_date(const Exiv2::Image *,const fs::path & path)210 std::string file_date(const Exiv2::Image *, const fs::path &path)
211 {
212 std::tm tm;
213 if(file_get_tm(path, tm))
214 return str(boost::format("%4d-%02d-%02d") % (tm.tm_year + 1900) % (tm.tm_mon + 1) % tm.tm_mday);
215 else
216 return "";
217 }
218
file_year(const Exiv2::Image *,const fs::path & path)219 std::string file_year(const Exiv2::Image *, const fs::path &path)
220 {
221 std::tm tm;
222 if(file_get_tm(path, tm))
223 return str(boost::format("%4d") % (tm.tm_year + 1900));
224 else
225 return "";
226 }
227
file_month(const Exiv2::Image *,const fs::path & path)228 std::string file_month(const Exiv2::Image *, const fs::path &path)
229 {
230 std::tm tm;
231 if(file_get_tm(path, tm))
232 return str(boost::format("%02d") % (tm.tm_mon + 1));
233 else
234 return "";
235 }
236
file_day(const Exiv2::Image *,const fs::path & path)237 std::string file_day(const Exiv2::Image *, const fs::path &path)
238 {
239 std::tm tm;
240 if(file_get_tm(path, tm))
241 return str(boost::format("%02d") % tm.tm_mday);
242 else
243 return "";
244 }
245
246 /*
247 std::string xmp_date(const Exiv2::Image *image, const fs::path &)
248 {
249 return "";
250 }
251
252 std::string xmp_year(const Exiv2::Image *image, const fs::path &)
253 {
254 return "";
255 }
256
257 std::string xmp_month(const Exiv2::Image *image, const fs::path &)
258 {
259 return "";
260 }
261
262 std::string xmp_day(const Exiv2::Image *image, const fs::path &)
263 {
264 return "";
265 }*/
266
exif_time(const Exiv2::Image * image,const fs::path &)267 std::string exif_time(const Exiv2::Image *image, const fs::path &)
268 {
269 Exiv2::ExifData::const_iterator md;
270 bool done = exif_data(image, "Exif.Photo.DateTimeDigitized", md);
271 if(!done)
272 done = exif_data(image, "Exif.Photo.DateTimeOriginal", md);
273 if(!done)
274 return "";
275
276 std::string datetime = md->print();
277 // Some files have zeros for dates, just fail in that case
278 if(boost::lexical_cast<int>(datetime.substr(0,4)) == 0)
279 return "";
280
281 return scrub(datetime.substr(11));
282 }
283
exif_hour(const Exiv2::Image * image,const fs::path & path)284 std::string exif_hour(const Exiv2::Image *image, const fs::path &path)
285 {
286 std::string time = exif_time(image, path);
287 if(time.length())
288 return time.substr(0,2);
289 else
290 return time;
291 }
292
exif_minute(const Exiv2::Image * image,const fs::path & path)293 std::string exif_minute(const Exiv2::Image *image, const fs::path &path)
294 {
295 std::string time = exif_time(image, path);
296 if(time.length())
297 return time.substr(3,2);
298 else
299 return time;
300 }
301
exif_second(const Exiv2::Image * image,const fs::path & path)302 std::string exif_second(const Exiv2::Image *image, const fs::path &path)
303 {
304 std::string time = exif_time(image, path);
305 if(time.length())
306 return time.substr(6,2);
307 else
308 return time;
309 }
310
iptc_get_time(const Exiv2::Image * image,Exiv2::TimeValue::Time & time)311 bool iptc_get_time(const Exiv2::Image *image, Exiv2::TimeValue::Time &time)
312 {
313 Exiv2::IptcData::const_iterator md;
314 bool done = iptc_data(image, "Iptc.Application2.DigitizationTime", md);
315 if(!done)
316 done = iptc_data(image, "Iptc.Application2.TimeCreated", md);
317 if(!done)
318 return false;
319 time = ((Exiv2::TimeValue*)md->getValue().get())->getTime();
320 // Zero is a valid time, so this one is hard to check.
321 return true;
322 }
323
iptc_time(const Exiv2::Image * image,const fs::path &)324 std::string iptc_time(const Exiv2::Image *image, const fs::path &)
325 {
326 Exiv2::TimeValue::Time time;
327 if(iptc_get_time(image, time))
328 return str(boost::format("%02d-%02d-%02d") % time.hour % time.minute % time.second);
329 else
330 return "";
331 }
332
iptc_hour(const Exiv2::Image * image,const fs::path &)333 std::string iptc_hour(const Exiv2::Image *image, const fs::path &)
334 {
335 Exiv2::TimeValue::Time time;
336 if(iptc_get_time(image, time))
337 return str(boost::format("%02d") % time.hour);
338 else
339 return "";
340 }
341
iptc_minute(const Exiv2::Image * image,const fs::path &)342 std::string iptc_minute(const Exiv2::Image *image, const fs::path &)
343 {
344 Exiv2::TimeValue::Time time;
345 if(iptc_get_time(image, time))
346 return str(boost::format("%02d") % time.minute);
347 else
348 return "";
349 }
350
iptc_second(const Exiv2::Image * image,const fs::path &)351 std::string iptc_second(const Exiv2::Image *image, const fs::path &)
352 {
353 Exiv2::TimeValue::Time time;
354 if(iptc_get_time(image, time))
355 return str(boost::format("%02d") % time.second);
356 else
357 return "";
358 }
359
file_time(const Exiv2::Image *,const fs::path & path)360 std::string file_time(const Exiv2::Image *, const fs::path &path)
361 {
362 std::tm tm;
363 if(file_get_tm(path, tm))
364 return str(boost::format("%02d-%02d-%02d") % tm.tm_hour % tm.tm_min % tm.tm_sec);
365 else
366 return "";
367 }
368
file_hour(const Exiv2::Image *,const fs::path & path)369 std::string file_hour(const Exiv2::Image *, const fs::path &path)
370 {
371 std::tm tm;
372 if(file_get_tm(path, tm))
373 return str(boost::format("%02d") % tm.tm_hour);
374 else
375 return "";
376 }
377
file_minute(const Exiv2::Image *,const fs::path & path)378 std::string file_minute(const Exiv2::Image *, const fs::path &path)
379 {
380 std::tm tm;
381 if(file_get_tm(path, tm))
382 return str(boost::format("%02d") % tm.tm_min);
383 else
384 return "";
385 }
386
file_second(const Exiv2::Image *,const fs::path & path)387 std::string file_second(const Exiv2::Image *, const fs::path &path)
388 {
389 std::tm tm;
390 if(file_get_tm(path, tm))
391 return str(boost::format("%02d") % tm.tm_sec);
392 else
393 return "";
394 }
395
396 /*std::string xmp_time(const Exiv2::Image *image, const fs::path &)
397 {
398 return "";
399 }
400
401 std::string xmp_hour(const Exiv2::Image *image, const fs::path &)
402 {
403 return "";
404 }
405
406 std::string xmp_minute(const Exiv2::Image *image, const fs::path &)
407 {
408 return "";
409 }
410
411 std::string xmp_second(const Exiv2::Image *image, const fs::path &)
412 {
413 return "";
414 }*/
415
exif_dimension(const Exiv2::Image * image,const fs::path & path)416 std::string exif_dimension(const Exiv2::Image *image, const fs::path &path)
417 {
418 return exif_width(image, path) + "-" + exif_height(image, path);
419 }
420
exif_width(const Exiv2::Image * image,const fs::path &)421 std::string exif_width(const Exiv2::Image *image, const fs::path &)
422 {
423 Exiv2::ExifData::const_iterator md;
424 bool done = exif_data(image, "Exif.Photo.PixelXDimension", md);
425 if(!done)
426 return "";
427 return scrub(md->print());
428 }
429
exif_height(const Exiv2::Image * image,const fs::path &)430 std::string exif_height(const Exiv2::Image *image, const fs::path &)
431 {
432 Exiv2::ExifData::const_iterator md;
433 bool done = exif_data(image, "Exif.Photo.PixelYDimension", md);
434 if(!done)
435 return "";
436 return scrub(md->print());
437 }
438
file_dimension(const Exiv2::Image * image,const fs::path & path)439 std::string file_dimension(const Exiv2::Image *image, const fs::path &path)
440 {
441 if(image)
442 return file_width(image, path) + "-" + file_height(image, path);
443 else
444 return "";
445 }
446
file_width(const Exiv2::Image * image,const fs::path &)447 std::string file_width(const Exiv2::Image *image, const fs::path &)
448 {
449 if(image)
450 return str(boost::format("%02d") % image->pixelWidth());
451 else
452 return "";
453 }
454
file_height(const Exiv2::Image * image,const fs::path &)455 std::string file_height(const Exiv2::Image *image, const fs::path &)
456 {
457 if(image)
458 return str(boost::format("%02d") % image->pixelHeight());
459 else
460 return "";
461 }
462
463 /*
464 std::string xmp_dimension(const Exiv2::Image *image, const fs::path &)
465 {
466 return ""
467 }
468
469 std::string xmp_width(const Exiv2::Image *image, const fs::path &)
470 {
471 return "";
472 }
473
474 std::string xmp_height(const Exiv2::Image *image, const fs::path &)
475 {
476 return "";
477 }*/
478
exif_model(const Exiv2::Image * image,const fs::path &)479 std::string exif_model(const Exiv2::Image *image, const fs::path &)
480 {
481 Exiv2::ExifData::const_iterator md;
482 bool done = exif_data(image, "Exif.Image.Model", md);
483 if(!done)
484 return "";
485 return scrub(md->print());
486 }
487
exif_make(const Exiv2::Image * image,const fs::path &)488 std::string exif_make(const Exiv2::Image *image, const fs::path &)
489 {
490 Exiv2::ExifData::const_iterator md;
491 bool done = exif_data(image, "Exif.Image.Make", md);
492 if(!done)
493 return "";
494 return scrub(md->print());
495 }
496
497 /*std::string xmp_model(const Exiv2::Image *image, const fs::path &)
498 {
499 return "";
500 }*/
501
exif_speed(const Exiv2::Image * image,const fs::path &)502 std::string exif_speed(const Exiv2::Image *image, const fs::path &)
503 {
504 Exiv2::ExifData::const_iterator md;
505 bool done = exif_data(image, "Exif.Photo.ShutterSpeedValue", md);
506 if(!done)
507 done = exif_data(image, "Exif.Photo.ExposureTime", md);
508 if(!done)
509 return "";
510 return scrub(md->print());
511 }
512
513 /*std::string xmp_speed(const Exiv2::Image *image, const fs::path &)
514 {
515 return "";
516 }*/
517
exif_aperture(const Exiv2::Image * image,const fs::path &)518 std::string exif_aperture(const Exiv2::Image *image, const fs::path &)
519 {
520 Exiv2::ExifData::const_iterator md;
521 bool done = exif_data(image, "Exif.Photo.ApertureValue", md);
522 if(!done)
523 done = exif_data(image, "Exif.Photo.FNumber", md);
524 if(!done)
525 return "";
526 return scrub(md->print());
527 }
528
529 /*std::string xmp_aperture(const Exiv2::Image *image, const fs::path &)
530 {
531 return "";
532 }*/
533
exif_focal(const Exiv2::Image * image,const fs::path &)534 std::string exif_focal(const Exiv2::Image *image, const fs::path &)
535 {
536 Exiv2::ExifData::const_iterator md;
537 bool done = exif_data(image, "Exif.Photo.FocalLength", md);
538 if(!done)
539 return "";
540 return scrub(md->print());
541 }
542
543 /*std::string xmp_focal(const Exiv2::Image *image, const fs::path &)
544 {
545 return "";
546 }*/
547
exif_distance(const Exiv2::Image * image,const fs::path &)548 std::string exif_distance(const Exiv2::Image *image, const fs::path &)
549 {
550 Exiv2::ExifData::const_iterator md;
551 bool done = exif_data(image, "Exif.Photo.SubjectDistance", md);
552 if(!done)
553 return "";
554 return scrub(md->print());
555 }
556
557 /*std::string xmp_distance(const Exiv2::Image *image, const fs::path &)
558 {
559 return "";
560 }*/
561
exif_meter(const Exiv2::Image * image,const fs::path &)562 std::string exif_meter(const Exiv2::Image *image, const fs::path &)
563 {
564 Exiv2::ExifData::const_iterator md;
565 bool done = exif_data(image, "Exif.Photo.MeteringMode", md);
566 if(!done)
567 return "";
568 return scrub(md->print());
569 }
570
exif_macro(const Exiv2::Image * image,const fs::path &)571 std::string exif_macro(const Exiv2::Image *image, const fs::path &)
572 {
573 Exiv2::ExifData::const_iterator md;
574 bool done = exif_data_easy(image, Exiv2::macroMode, md);
575 if(!done)
576 return "";
577 return scrub(md->print());
578 }
579
exif_orientation(const Exiv2::Image * image,const fs::path &)580 std::string exif_orientation(const Exiv2::Image *image, const fs::path &)
581 {
582 Exiv2::ExifData::const_iterator md;
583 bool done = exif_data_easy(image, Exiv2::orientation, md);
584 if(!done)
585 return "";
586 return scrub(md->print(), true);
587 }
588
exif_lens(const Exiv2::Image * image,const fs::path &)589 std::string exif_lens(const Exiv2::Image *image, const fs::path &)
590 {
591 Exiv2::ExifData::const_iterator md;
592 bool done = exif_data_easy(image, Exiv2::lensName, md);
593 if(!done)
594 return "";
595 return scrub(md->print());
596 }
597
598
exif_iso(const Exiv2::Image * image,const fs::path &)599 std::string exif_iso(const Exiv2::Image *image, const fs::path &)
600 {
601 Exiv2::ExifData::const_iterator md;
602 bool done = exif_data_easy(image, Exiv2::isoSpeed, md);
603 if(!done)
604 return "";
605 return scrub(md->print());
606 }
607
608 /*std::string xmp_meter(const Exiv2::Image *image, const fs::path &)
609 {
610 return "";
611 }*/
612
exif_keyword(const Exiv2::Image * image,const fs::path &)613 std::string exif_keyword(const Exiv2::Image *image, const fs::path &)
614 {
615 Exiv2::ExifData::const_iterator md;
616 bool done = exif_data(image, "Exif.Photo.UserComment", md);
617 if(!done)
618 return "";
619 return scrub(md->print());
620 }
621
iptc_keyword(const Exiv2::Image * image,const fs::path &)622 std::string iptc_keyword(const Exiv2::Image *image, const fs::path &)
623 {
624 Exiv2::IptcData::const_iterator md;
625 bool done = iptc_data(image, "Iptc.Application2.Keywords", md);
626 if(!done)
627 return "";
628 return scrub(md->print());
629 }
630
631 /*std::string xmp_keyword(const Exiv2::Image *image, const fs::path &)
632 {
633 return "";
634 }*/
635
636