From 0994eccbb0eff0d01630f5c7d96628339e48cd5a Mon Sep 17 00:00:00 2001 From: StefanBruens <> Date: Dec 01 2020 13:24:18 +0000 Subject: Update meshlab to version 2020.09 / rev 1 via SR 844946 https://build.opensuse.org/request/show/844946 by user StefanBruens + dimstar_suse New package --- diff --git a/.files b/.files new file mode 100644 index 0000000..bd1ef25 Binary files /dev/null and b/.files differ diff --git a/.meta b/.meta new file mode 100644 index 0000000..448cd6e --- /dev/null +++ b/.meta @@ -0,0 +1,6 @@ + + System for processing and editing unstructured 3D triangular meshes. + MeshLab is designed for the processing of typical large unstructured models arising in 3D scanning, providing a set of tools for editing, cleaning, healing, inspecting, rendering and converting this kind of meshes. + + + diff --git a/.rev b/.rev new file mode 100644 index 0000000..dbea096 --- /dev/null +++ b/.rev @@ -0,0 +1,10 @@ + + + 72844d7b5482f7e496c87ff1ee30cadd + 2020.09 + + dimstar_suse + New package + 844946 + + diff --git a/Added-easyexif-minimal-lib-for-exif-loading.patch b/Added-easyexif-minimal-lib-for-exif-loading.patch new file mode 100644 index 0000000..91b7553 --- /dev/null +++ b/Added-easyexif-minimal-lib-for-exif-loading.patch @@ -0,0 +1,1389 @@ +From c9bf748355ef69b76cb36abc327a980e7d0956d3 Mon Sep 17 00:00:00 2001 +From: Paolo Cignoni +Date: Sun, 20 Sep 2020 17:16:23 +0200 +Subject: [PATCH] Added easyexif minimal lib for exif loading + +--- + src/external/easyexif/.gitignore | 29 + + src/external/easyexif/CONTRIBUTORS | 10 + + src/external/easyexif/LICENSE | 24 + + src/external/easyexif/README.md | 91 +++ + src/external/easyexif/demo.cpp | 85 +++ + src/external/easyexif/exif.cpp | 918 +++++++++++++++++++++++++++++ + src/external/easyexif/exif.h | 168 ++++++ + 7 files changed, 1325 insertions(+) + create mode 100644 src/external/easyexif/.gitignore + create mode 100644 src/external/easyexif/CONTRIBUTORS + create mode 100644 src/external/easyexif/LICENSE + create mode 100644 src/external/easyexif/README.md + create mode 100644 src/external/easyexif/demo.cpp + create mode 100644 src/external/easyexif/exif.cpp + create mode 100644 src/external/easyexif/exif.h + +diff --git a/src/external/easyexif/.gitignore b/src/external/easyexif/.gitignore +new file mode 100644 +index 000000000..256a2b878 +--- /dev/null ++++ b/src/external/easyexif/.gitignore +@@ -0,0 +1,29 @@ ++# Compiled Object files ++*.slo ++*.lo ++*.o ++*.obj ++ ++# Precompiled Headers ++*.gch ++*.pch ++ ++# Compiled Dynamic libraries ++*.so ++*.dylib ++*.dll ++ ++# Fortran module files ++*.mod ++ ++# Compiled Static libraries ++*.lai ++*.la ++*.a ++*.lib ++ ++# Executables ++demo ++*.exe ++*.out ++*.app +diff --git a/src/external/easyexif/CONTRIBUTORS b/src/external/easyexif/CONTRIBUTORS +new file mode 100644 +index 000000000..2a7a7868b +--- /dev/null ++++ b/src/external/easyexif/CONTRIBUTORS +@@ -0,0 +1,10 @@ ++The following people have contributed patches or issues to EasyEXIF: ++ ++Seth Fowler ++Val Malykh ++Carlos Apablaza Brito ++Simon Fuhrmann ++Toshiaki Ohkuma ++pet.b.hunt ++Jason Moey ++Tomas Volf +diff --git a/src/external/easyexif/LICENSE b/src/external/easyexif/LICENSE +new file mode 100644 +index 000000000..3e5df3c56 +--- /dev/null ++++ b/src/external/easyexif/LICENSE +@@ -0,0 +1,24 @@ ++Copyright (c) 2010-2015 Mayank Lahiri ++mlahiri@gmail.com ++All rights reserved. ++ ++Redistribution and use in source and binary forms, with or without ++modification, are permitted provided that the following conditions are met: ++ ++-- Redistributions of source code must retain the above copyright notice, ++ this list of conditions and the following disclaimer. ++-- 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. ++ ++ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESS ++ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN ++ NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS 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, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, ++ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ +diff --git a/src/external/easyexif/README.md b/src/external/easyexif/README.md +new file mode 100644 +index 000000000..c0cbc02e8 +--- /dev/null ++++ b/src/external/easyexif/README.md +@@ -0,0 +1,91 @@ ++easyexif ++======== ++ ++A tiny ISO-compliant C++ EXIF parsing library. ++ ++EasyEXIF is a tiny, lightweight C++ library that parses basic information out of JPEG files. It uses only the std::string library and is otherwise pure C++. You pass it the binary contents of a JPEG file, and it parses several of the most important EXIF fields for you. ++ ++Why use this library? __Include one .h file, compile one .cc file, and that's it.__ ++ ++Sometimes you just need to quickly extract basic information from a JPEG file's EXIF headers: the time the image was taken (not the file timestamp, the camera's internal time), the F-stop or exposure time, GPS information embedded in the EXIF file, what the camera make and model was, etc. Unfortunately, all the EXIF libraries out there are not very lightweight or easy to integrate into larger programs. EasyEXIF aims to solve that problem, and is released under a very liberal BSD License for use practically anywhere. ++ ++The only two files you need for your project are `exif.cpp` and `exif.h`. There are no dependencies on any build system or external libraries. ++ ++Who uses this library? ++ ++ * [Blackberry's BBM messenger](http://us.blackberry.com/legal/thirdpartysoftware/bbmthirdparty/x-platform-bbm.html). ++ * [Altova Software](http://www.altova.com/legal_3rdparty.html). ++ * [Subsurface](http://trac.hohndel.org/browser/subsurface/qt-ui/exif.h?rev=a3d82bf9b1bf7a25e20e75aceeafe80cbc4f78f3). ++ * [Loom SDK](https://github.com/LoomSDK/LoomSDK/blob/master/loom/vendor/jheadexif/easyexif.h) ++ ++### Features: ++ ++ 1. Supports common Exif fields including GPS, ISO speed, etc. ++ 2. Extensively documented in the source. ++ 3. Valgrind tested for memory leaks. ++ 4. Handles corrupt JPEGs. ++ 5. Compiles without complaints using `-Wall -Wextra -Werror -pedantic -ansi` on gcc v4.8.2 ++ 6. No uses of new/malloc. ++ ++### License ++ ++BSD. Free for personal and commercial use. ++ ++### Contributions ++ ++Reasonable pull requests are gladly accepted. Please ensure that you have run the following steps before submitting a patch: ++ ++ 1. `clang-format` the source files, using the default style (Google). ++ 2. Run `test.sh` to make sure test images are being parsed correctly. ++ 3. Compile and run `demo` using `valgrind --leak-check=full` to ensure that there are no memory leaks. ++ 4. Add yourself to the contributors list in `CONTRIBUTORS` and `README.md`. ++ 5. For misbehaving images, please try to include a test image that misbehaves in the `test-images` folder. ++ ++The following people have committed patches to EasyExif. ++ ++ * Tomas Volf ++ * Seth Fowler ++ * Val Malykh ++ * Carlos Apablaza Brito ++ * Simon Fuhrmann ++ * Toshiaki Ohkuma ++ * pet.b.hunt ++ * Jason Moey ++ ++ ++### Example: ++ ++See `demo.cpp` for other Exif fields. ++ ++```C++ ++ #include "exif.h" ++ ++ EXIFInfo result; ++ result.parseFrom(JPEGFileBuffer, BufferSize); ++ ++ printf("Camera make : %s\n", result.Make.c_str()); ++ printf("Camera model : %s\n", result.Model.c_str()); ++ printf("Software : %s\n", result.Software.c_str()); ++ printf("Bits per sample : %d\n", result.BitsPerSample); ++ printf("Image width : %d\n", result.ImageWidth); ++ printf("Image height : %d\n", result.ImageHeight); ++ printf("Image description : %s\n", result.ImageDescription.c_str()); ++ printf("Image orientation : %d\n", result.Orientation); ++ printf("Image copyright : %s\n", result.Copyright.c_str()); ++ printf("Image date/time : %s\n", result.DateTime.c_str()); ++ printf("Original date/time: %s\n", result.DateTimeOriginal.c_str()); ++ printf("Digitize date/time: %s\n", result.DateTimeDigitized.c_str()); ++ printf("Subsecond time : %s\n", result.SubSecTimeOriginal.c_str()); ++ printf("Exposure time : 1/%d s\n", (unsigned) (1.0/result.ExposureTime)); ++ printf("F-stop : f/%.1f\n", result.FNumber); ++ printf("ISO speed : %d\n", result.ISOSpeedRatings); ++ printf("Subject distance : %f m\n", result.SubjectDistance); ++ printf("Exposure bias : %f EV\n", result.ExposureBiasValue); ++ printf("Flash used? : %d\n", result.Flash); ++ printf("Metering mode : %d\n", result.MeteringMode); ++ printf("Lens focal length : %f mm\n", result.FocalLength); ++ printf("35mm focal length : %u mm\n", result.FocalLengthIn35mm); ++ printf("GPS Latitude : %f deg\n", result.GeoLocation.Latitude); ++ printf("GPS Longitude : %f deg\n", result.GeoLocation.Longitude); ++ printf("GPS Altitude : %f m\n", result.GeoLocation.Altitude); ++``` +diff --git a/src/external/easyexif/demo.cpp b/src/external/easyexif/demo.cpp +new file mode 100644 +index 000000000..cdff6df53 +--- /dev/null ++++ b/src/external/easyexif/demo.cpp +@@ -0,0 +1,85 @@ ++#include ++#include "exif.h" ++ ++int main(int argc, char *argv[]) { ++ if (argc < 2) { ++ printf("Usage: demo \n"); ++ return -1; ++ } ++ ++ // Read the JPEG file into a buffer ++ FILE *fp = fopen(argv[1], "rb"); ++ if (!fp) { ++ printf("Can't open file.\n"); ++ return -1; ++ } ++ fseek(fp, 0, SEEK_END); ++ unsigned long fsize = ftell(fp); ++ rewind(fp); ++ unsigned char *buf = new unsigned char[fsize]; ++ if (fread(buf, 1, fsize, fp) != fsize) { ++ printf("Can't read file.\n"); ++ delete[] buf; ++ return -2; ++ } ++ fclose(fp); ++ ++ // Parse EXIF ++ easyexif::EXIFInfo result; ++ int code = result.parseFrom(buf, fsize); ++ delete[] buf; ++ if (code) { ++ printf("Error parsing EXIF: code %d\n", code); ++ return -3; ++ } ++ ++ // Dump EXIF information ++ printf("Camera make : %s\n", result.Make.c_str()); ++ printf("Camera model : %s\n", result.Model.c_str()); ++ printf("Software : %s\n", result.Software.c_str()); ++ printf("Bits per sample : %d\n", result.BitsPerSample); ++ printf("Image width : %d\n", result.ImageWidth); ++ printf("Image height : %d\n", result.ImageHeight); ++ printf("Image description : %s\n", result.ImageDescription.c_str()); ++ printf("Image orientation : %d\n", result.Orientation); ++ printf("Image copyright : %s\n", result.Copyright.c_str()); ++ printf("Image date/time : %s\n", result.DateTime.c_str()); ++ printf("Original date/time : %s\n", result.DateTimeOriginal.c_str()); ++ printf("Digitize date/time : %s\n", result.DateTimeDigitized.c_str()); ++ printf("Subsecond time : %s\n", result.SubSecTimeOriginal.c_str()); ++ printf("Exposure time : 1/%d s\n", ++ (unsigned)(1.0 / result.ExposureTime)); ++ printf("F-stop : f/%.1f\n", result.FNumber); ++ printf("Exposure program : %d\n", result.ExposureProgram); ++ printf("ISO speed : %d\n", result.ISOSpeedRatings); ++ printf("Subject distance : %f m\n", result.SubjectDistance); ++ printf("Exposure bias : %f EV\n", result.ExposureBiasValue); ++ printf("Flash used? : %d\n", result.Flash); ++ printf("Flash returned light : %d\n", result.FlashReturnedLight); ++ printf("Flash mode : %d\n", result.FlashMode); ++ printf("Metering mode : %d\n", result.MeteringMode); ++ printf("Lens focal length : %f mm\n", result.FocalLength); ++ printf("35mm focal length : %u mm\n", result.FocalLengthIn35mm); ++ printf("GPS Latitude : %f deg (%f deg, %f min, %f sec %c)\n", ++ result.GeoLocation.Latitude, result.GeoLocation.LatComponents.degrees, ++ result.GeoLocation.LatComponents.minutes, ++ result.GeoLocation.LatComponents.seconds, ++ result.GeoLocation.LatComponents.direction); ++ printf("GPS Longitude : %f deg (%f deg, %f min, %f sec %c)\n", ++ result.GeoLocation.Longitude, result.GeoLocation.LonComponents.degrees, ++ result.GeoLocation.LonComponents.minutes, ++ result.GeoLocation.LonComponents.seconds, ++ result.GeoLocation.LonComponents.direction); ++ printf("GPS Altitude : %f m\n", result.GeoLocation.Altitude); ++ printf("GPS Precision (DOP) : %f\n", result.GeoLocation.DOP); ++ printf("Lens min focal length: %f mm\n", result.LensInfo.FocalLengthMin); ++ printf("Lens max focal length: %f mm\n", result.LensInfo.FocalLengthMax); ++ printf("Lens f-stop min : f/%.1f\n", result.LensInfo.FStopMin); ++ printf("Lens f-stop max : f/%.1f\n", result.LensInfo.FStopMax); ++ printf("Lens make : %s\n", result.LensInfo.Make.c_str()); ++ printf("Lens model : %s\n", result.LensInfo.Model.c_str()); ++ printf("Focal plane XRes : %f\n", result.LensInfo.FocalPlaneXResolution); ++ printf("Focal plane YRes : %f\n", result.LensInfo.FocalPlaneYResolution); ++ ++ return 0; ++} +diff --git a/src/external/easyexif/exif.cpp b/src/external/easyexif/exif.cpp +new file mode 100644 +index 000000000..9b9e05a47 +--- /dev/null ++++ b/src/external/easyexif/exif.cpp +@@ -0,0 +1,918 @@ ++/************************************************************************** ++ exif.cpp -- A simple ISO C++ library to parse basic EXIF ++ information from a JPEG file. ++ ++ Copyright (c) 2010-2015 Mayank Lahiri ++ mlahiri@gmail.com ++ All rights reserved (BSD License). ++ ++ See exif.h for version history. ++ ++ Redistribution and use in source and binary forms, with or without ++ modification, are permitted provided that the following conditions are met: ++ ++ -- Redistributions of source code must retain the above copyright notice, ++ this list of conditions and the following disclaimer. ++ -- 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. ++ ++ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESS ++ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN ++ NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS 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, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, ++ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++*/ ++#include "exif.h" ++ ++#include ++#include ++#include ++#include ++ ++using std::string; ++ ++namespace { ++ ++struct Rational { ++ uint32_t numerator, denominator; ++ operator double() const { ++ if (denominator < 1e-20) { ++ return 0; ++ } ++ return static_cast(numerator) / static_cast(denominator); ++ } ++}; ++ ++// IF Entry ++class IFEntry { ++ public: ++ using byte_vector = std::vector; ++ using ascii_vector = std::string; ++ using short_vector = std::vector; ++ using long_vector = std::vector; ++ using rational_vector = std::vector; ++ ++ IFEntry() ++ : tag_(0xFF), format_(0xFF), data_(0), length_(0), val_byte_(nullptr) {} ++ IFEntry(const IFEntry &) = delete; ++ IFEntry &operator=(const IFEntry &) = delete; ++ IFEntry(IFEntry &&other) ++ : tag_(other.tag_), ++ format_(other.format_), ++ data_(other.data_), ++ length_(other.length_), ++ val_byte_(other.val_byte_) { ++ other.tag_ = 0xFF; ++ other.format_ = 0xFF; ++ other.data_ = 0; ++ other.length_ = 0; ++ other.val_byte_ = nullptr; ++ } ++ ~IFEntry() { delete_union(); } ++ unsigned short tag() const { return tag_; } ++ void tag(unsigned short tag) { tag_ = tag; } ++ unsigned short format() const { return format_; } ++ bool format(unsigned short format) { ++ switch (format) { ++ case 0x01: ++ case 0x02: ++ case 0x03: ++ case 0x04: ++ case 0x05: ++ case 0x07: ++ case 0x09: ++ case 0x0a: ++ case 0xff: ++ break; ++ default: ++ return false; ++ } ++ delete_union(); ++ format_ = format; ++ new_union(); ++ return true; ++ } ++ unsigned data() const { return data_; } ++ void data(unsigned data) { data_ = data; } ++ unsigned length() const { return length_; } ++ void length(unsigned length) { length_ = length; } ++ ++ // functions to access the data ++ // ++ // !! it's CALLER responsibility to check that format !! ++ // !! is correct before accessing it's field !! ++ // ++ // - getters are use here to allow future addition ++ // of checks if format is correct ++ byte_vector &val_byte() { return *val_byte_; } ++ ascii_vector &val_string() { return *val_string_; } ++ short_vector &val_short() { return *val_short_; } ++ long_vector &val_long() { return *val_long_; } ++ rational_vector &val_rational() { return *val_rational_; } ++ ++ private: ++ // Raw fields ++ unsigned short tag_; ++ unsigned short format_; ++ unsigned data_; ++ unsigned length_; ++ ++ // Parsed fields ++ union { ++ byte_vector *val_byte_; ++ ascii_vector *val_string_; ++ short_vector *val_short_; ++ long_vector *val_long_; ++ rational_vector *val_rational_; ++ }; ++ ++ void delete_union() { ++ switch (format_) { ++ case 0x1: ++ delete val_byte_; ++ val_byte_ = nullptr; ++ break; ++ case 0x2: ++ delete val_string_; ++ val_string_ = nullptr; ++ break; ++ case 0x3: ++ delete val_short_; ++ val_short_ = nullptr; ++ break; ++ case 0x4: ++ delete val_long_; ++ val_long_ = nullptr; ++ break; ++ case 0x5: ++ delete val_rational_; ++ val_rational_ = nullptr; ++ break; ++ case 0xff: ++ break; ++ default: ++ // should not get here ++ // should I throw an exception or ...? ++ break; ++ } ++ } ++ void new_union() { ++ switch (format_) { ++ case 0x1: ++ val_byte_ = new byte_vector(); ++ break; ++ case 0x2: ++ val_string_ = new ascii_vector(); ++ break; ++ case 0x3: ++ val_short_ = new short_vector(); ++ break; ++ case 0x4: ++ val_long_ = new long_vector(); ++ break; ++ case 0x5: ++ val_rational_ = new rational_vector(); ++ break; ++ case 0xff: ++ break; ++ default: ++ // should not get here ++ // should I throw an exception or ...? ++ break; ++ } ++ } ++}; ++ ++// Helper functions ++template ++T parse(const unsigned char *buf); ++ ++template <> ++uint8_t parse(const unsigned char *buf) { ++ return *buf; ++} ++ ++template <> ++uint8_t parse(const unsigned char *buf) { ++ return *buf; ++} ++ ++template <> ++uint16_t parse(const unsigned char *buf) { ++ return (static_cast(buf[0]) << 8) | buf[1]; ++} ++ ++template <> ++uint16_t parse(const unsigned char *buf) { ++ return (static_cast(buf[1]) << 8) | buf[0]; ++} ++ ++template <> ++uint32_t parse(const unsigned char *buf) { ++ return (static_cast(buf[0]) << 24) | ++ (static_cast(buf[1]) << 16) | ++ (static_cast(buf[2]) << 8) | buf[3]; ++} ++ ++template <> ++uint32_t parse(const unsigned char *buf) { ++ return (static_cast(buf[3]) << 24) | ++ (static_cast(buf[2]) << 16) | ++ (static_cast(buf[1]) << 8) | buf[0]; ++} ++ ++template <> ++Rational parse(const unsigned char *buf) { ++ Rational r; ++ r.numerator = parse(buf); ++ r.denominator = parse(buf + 4); ++ return r; ++} ++ ++template <> ++Rational parse(const unsigned char *buf) { ++ Rational r; ++ r.numerator = parse(buf); ++ r.denominator = parse(buf + 4); ++ return r; ++} ++ ++/** ++ * Try to read entry.length() values for this entry. ++ * ++ * Returns: ++ * true - entry.length() values were read ++ * false - something went wrong, vec's content was not touched ++ */ ++template ++bool extract_values(C &container, const unsigned char *buf, const unsigned base, ++ const unsigned len, const IFEntry &entry) { ++ const unsigned char *data; ++ uint32_t reversed_data; ++ // if data fits into 4 bytes, they are stored directly in ++ // the data field in IFEntry ++ if (sizeof(T) * entry.length() <= 4) { ++ if (alignIntel) { ++ reversed_data = entry.data(); ++ } else { ++ reversed_data = entry.data(); ++ // this reversing works, but is ugly ++ unsigned char *rdata = reinterpret_cast(&reversed_data); ++ unsigned char tmp; ++ tmp = rdata[0]; ++ rdata[0] = rdata[3]; ++ rdata[3] = tmp; ++ tmp = rdata[1]; ++ rdata[1] = rdata[2]; ++ rdata[2] = tmp; ++ } ++ data = reinterpret_cast(&(reversed_data)); ++ } else { ++ data = buf + base + entry.data(); ++ if (data + sizeof(T) * entry.length() > buf + len) { ++ return false; ++ } ++ } ++ container.resize(entry.length()); ++ for (size_t i = 0; i < entry.length(); ++i) { ++ container[i] = parse(data + sizeof(T) * i); ++ } ++ return true; ++} ++ ++template ++void parseIFEntryHeader(const unsigned char *buf, unsigned short &tag, ++ unsigned short &format, unsigned &length, ++ unsigned &data) { ++ // Each directory entry is composed of: ++ // 2 bytes: tag number (data field) ++ // 2 bytes: data format ++ // 4 bytes: number of components ++ // 4 bytes: data value or offset to data value ++ tag = parse(buf); ++ format = parse(buf + 2); ++ length = parse(buf + 4); ++ data = parse(buf + 8); ++} ++ ++template ++void parseIFEntryHeader(const unsigned char *buf, IFEntry &result) { ++ unsigned short tag; ++ unsigned short format; ++ unsigned length; ++ unsigned data; ++ ++ parseIFEntryHeader(buf, tag, format, length, data); ++ ++ result.tag(tag); ++ result.format(format); ++ result.length(length); ++ result.data(data); ++} ++ ++template ++IFEntry parseIFEntry_temp(const unsigned char *buf, const unsigned offs, ++ const unsigned base, const unsigned len) { ++ IFEntry result; ++ ++ // check if there even is enough data for IFEntry in the buffer ++ if (buf + offs + 12 > buf + len) { ++ result.tag(0xFF); ++ return result; ++ } ++ ++ parseIFEntryHeader(buf + offs, result); ++ ++ // Parse value in specified format ++ switch (result.format()) { ++ case 1: ++ if (!extract_values(result.val_byte(), buf, base, ++ len, result)) { ++ result.tag(0xFF); ++ } ++ break; ++ case 2: ++ // string is basically sequence of uint8_t (well, according to EXIF even ++ // uint7_t, but ++ // we don't have that), so just read it as bytes ++ if (!extract_values(result.val_string(), buf, base, ++ len, result)) { ++ result.tag(0xFF); ++ } ++ // and cut zero byte at the end, since we don't want that in the ++ // std::string ++ if (result.val_string()[result.val_string().length() - 1] == '\0') { ++ result.val_string().resize(result.val_string().length() - 1); ++ } ++ break; ++ case 3: ++ if (!extract_values(result.val_short(), buf, base, ++ len, result)) { ++ result.tag(0xFF); ++ } ++ break; ++ case 4: ++ if (!extract_values(result.val_long(), buf, base, ++ len, result)) { ++ result.tag(0xFF); ++ } ++ break; ++ case 5: ++ if (!extract_values(result.val_rational(), buf, ++ base, len, result)) { ++ result.tag(0xFF); ++ } ++ break; ++ case 7: ++ case 9: ++ case 10: ++ break; ++ default: ++ result.tag(0xFF); ++ } ++ return result; ++} ++ ++// helper functions for convinience ++template ++T parse_value(const unsigned char *buf, bool alignIntel) { ++ if (alignIntel) { ++ return parse(buf); ++ } else { ++ return parse(buf); ++ } ++} ++ ++void parseIFEntryHeader(const unsigned char *buf, bool alignIntel, ++ unsigned short &tag, unsigned short &format, ++ unsigned &length, unsigned &data) { ++ if (alignIntel) { ++ parseIFEntryHeader(buf, tag, format, length, data); ++ } else { ++ parseIFEntryHeader(buf, tag, format, length, data); ++ } ++} ++ ++IFEntry parseIFEntry(const unsigned char *buf, const unsigned offs, ++ const bool alignIntel, const unsigned base, ++ const unsigned len) { ++ if (alignIntel) { ++ return parseIFEntry_temp(buf, offs, base, len); ++ } else { ++ return parseIFEntry_temp(buf, offs, base, len); ++ } ++} ++} ++ ++// ++// Locates the EXIF segment and parses it using parseFromEXIFSegment ++// ++int easyexif::EXIFInfo::parseFrom(const unsigned char *buf, unsigned len) { ++ // Sanity check: all JPEG files start with 0xFFD8. ++ if (!buf || len < 4) return PARSE_EXIF_ERROR_NO_JPEG; ++ if (buf[0] != 0xFF || buf[1] != 0xD8) return PARSE_EXIF_ERROR_NO_JPEG; ++ ++ // Sanity check: some cameras pad the JPEG image with some bytes at the end. ++ // Normally, we should be able to find the JPEG end marker 0xFFD9 at the end ++ // of the image buffer, but not always. As long as there are some bytes ++ // except 0xD9 at the end of the image buffer, keep decrementing len until ++ // an 0xFFD9 is found. If JPEG end marker 0xFFD9 is not found, ++ // then we can be reasonably sure that the buffer is not a JPEG. ++ while (len > 2) { ++ if (buf[len - 1] == 0xD9 && buf[len - 2] == 0xFF) ++ break; ++ len--; ++ } ++ if (len <= 2) ++ return PARSE_EXIF_ERROR_NO_JPEG; ++ ++ clear(); ++ ++ // Scan for EXIF header (bytes 0xFF 0xE1) and do a sanity check by ++ // looking for bytes "Exif\0\0". The marker length data is in Motorola ++ // byte order, which results in the 'false' parameter to parse16(). ++ // The marker has to contain at least the TIFF header, otherwise the ++ // EXIF data is corrupt. So the minimum length specified here has to be: ++ // 2 bytes: section size ++ // 6 bytes: "Exif\0\0" string ++ // 2 bytes: TIFF header (either "II" or "MM" string) ++ // 2 bytes: TIFF magic (short 0x2a00 in Motorola byte order) ++ // 4 bytes: Offset to first IFD ++ // ========= ++ // 16 bytes ++ unsigned offs = 0; // current offset into buffer ++ for (offs = 0; offs < len - 1; offs++) ++ if (buf[offs] == 0xFF && buf[offs + 1] == 0xE1) break; ++ if (offs + 4 > len) return PARSE_EXIF_ERROR_NO_EXIF; ++ offs += 2; ++ unsigned short section_length = parse_value(buf + offs, false); ++ if (offs + section_length > len || section_length < 16) ++ return PARSE_EXIF_ERROR_CORRUPT; ++ offs += 2; ++ ++ return parseFromEXIFSegment(buf + offs, len - offs); ++} ++ ++int easyexif::EXIFInfo::parseFrom(const string &data) { ++ return parseFrom( ++ reinterpret_cast(data.data()), static_cast(data.length())); ++} ++ ++// ++// Main parsing function for an EXIF segment. ++// ++// PARAM: 'buf' start of the EXIF TIFF, which must be the bytes "Exif\0\0". ++// PARAM: 'len' length of buffer ++// ++int easyexif::EXIFInfo::parseFromEXIFSegment(const unsigned char *buf, ++ unsigned len) { ++ bool alignIntel = true; // byte alignment (defined in EXIF header) ++ unsigned offs = 0; // current offset into buffer ++ if (!buf || len < 6) return PARSE_EXIF_ERROR_NO_EXIF; ++ ++ if (!std::equal(buf, buf + 6, "Exif\0\0")) return PARSE_EXIF_ERROR_NO_EXIF; ++ offs += 6; ++ ++ // Now parsing the TIFF header. The first two bytes are either "II" or ++ // "MM" for Intel or Motorola byte alignment. Sanity check by parsing ++ // the unsigned short that follows, making sure it equals 0x2a. The ++ // last 4 bytes are an offset into the first IFD, which are added to ++ // the global offset counter. For this block, we expect the following ++ // minimum size: ++ // 2 bytes: 'II' or 'MM' ++ // 2 bytes: 0x002a ++ // 4 bytes: offset to first IDF ++ // ----------------------------- ++ // 8 bytes ++ if (offs + 8 > len) return PARSE_EXIF_ERROR_CORRUPT; ++ unsigned tiff_header_start = offs; ++ if (buf[offs] == 'I' && buf[offs + 1] == 'I') ++ alignIntel = true; ++ else { ++ if (buf[offs] == 'M' && buf[offs + 1] == 'M') ++ alignIntel = false; ++ else ++ return PARSE_EXIF_ERROR_UNKNOWN_BYTEALIGN; ++ } ++ this->ByteAlign = alignIntel; ++ offs += 2; ++ if (0x2a != parse_value(buf + offs, alignIntel)) ++ return PARSE_EXIF_ERROR_CORRUPT; ++ offs += 2; ++ unsigned first_ifd_offset = parse_value(buf + offs, alignIntel); ++ offs += first_ifd_offset - 4; ++ if (offs >= len) return PARSE_EXIF_ERROR_CORRUPT; ++ ++ // Now parsing the first Image File Directory (IFD0, for the main image). ++ // An IFD consists of a variable number of 12-byte directory entries. The ++ // first two bytes of the IFD section contain the number of directory ++ // entries in the section. The last 4 bytes of the IFD contain an offset ++ // to the next IFD, which means this IFD must contain exactly 6 + 12 * num ++ // bytes of data. ++ if (offs + 2 > len) return PARSE_EXIF_ERROR_CORRUPT; ++ int num_entries = parse_value(buf + offs, alignIntel); ++ if (offs + 6 + 12 * num_entries > len) return PARSE_EXIF_ERROR_CORRUPT; ++ offs += 2; ++ unsigned exif_sub_ifd_offset = len; ++ unsigned gps_sub_ifd_offset = len; ++ while (--num_entries >= 0) { ++ IFEntry result = ++ parseIFEntry(buf, offs, alignIntel, tiff_header_start, len); ++ offs += 12; ++ switch (result.tag()) { ++ case 0x102: ++ // Bits per sample ++ if (result.format() == 3 && result.val_short().size()) ++ this->BitsPerSample = result.val_short().front(); ++ break; ++ ++ case 0x10E: ++ // Image description ++ if (result.format() == 2) this->ImageDescription = result.val_string(); ++ break; ++ ++ case 0x10F: ++ // Digicam make ++ if (result.format() == 2) this->Make = result.val_string(); ++ break; ++ ++ case 0x110: ++ // Digicam model ++ if (result.format() == 2) this->Model = result.val_string(); ++ break; ++ ++ case 0x112: ++ // Orientation of image ++ if (result.format() == 3 && result.val_short().size()) ++ this->Orientation = result.val_short().front(); ++ break; ++ ++ case 0x131: ++ // Software used for image ++ if (result.format() == 2) this->Software = result.val_string(); ++ break; ++ ++ case 0x132: ++ // EXIF/TIFF date/time of image modification ++ if (result.format() == 2) this->DateTime = result.val_string(); ++ break; ++ ++ case 0x8298: ++ // Copyright information ++ if (result.format() == 2) this->Copyright = result.val_string(); ++ break; ++ ++ case 0x8825: ++ // GPS IFS offset ++ gps_sub_ifd_offset = tiff_header_start + result.data(); ++ break; ++ ++ case 0x8769: ++ // EXIF SubIFD offset ++ exif_sub_ifd_offset = tiff_header_start + result.data(); ++ break; ++ } ++ } ++ ++ // Jump to the EXIF SubIFD if it exists and parse all the information ++ // there. Note that it's possible that the EXIF SubIFD doesn't exist. ++ // The EXIF SubIFD contains most of the interesting information that a ++ // typical user might want. ++ if (exif_sub_ifd_offset + 4 <= len) { ++ offs = exif_sub_ifd_offset; ++ int num_sub_entries = parse_value(buf + offs, alignIntel); ++ if (offs + 6 + 12 * num_sub_entries > len) return PARSE_EXIF_ERROR_CORRUPT; ++ offs += 2; ++ while (--num_sub_entries >= 0) { ++ IFEntry result = ++ parseIFEntry(buf, offs, alignIntel, tiff_header_start, len); ++ switch (result.tag()) { ++ case 0x829a: ++ // Exposure time in seconds ++ if (result.format() == 5 && result.val_rational().size()) ++ this->ExposureTime = result.val_rational().front(); ++ break; ++ ++ case 0x829d: ++ // FNumber ++ if (result.format() == 5 && result.val_rational().size()) ++ this->FNumber = result.val_rational().front(); ++ break; ++ ++ case 0x8822: ++ // Exposure Program ++ if (result.format() == 3 && result.val_short().size()) ++ this->ExposureProgram = result.val_short().front(); ++ break; ++ ++ case 0x8827: ++ // ISO Speed Rating ++ if (result.format() == 3 && result.val_short().size()) ++ this->ISOSpeedRatings = result.val_short().front(); ++ break; ++ ++ case 0x9003: ++ // Original date and time ++ if (result.format() == 2) ++ this->DateTimeOriginal = result.val_string(); ++ break; ++ ++ case 0x9004: ++ // Digitization date and time ++ if (result.format() == 2) ++ this->DateTimeDigitized = result.val_string(); ++ break; ++ ++ case 0x9201: ++ // Shutter speed value ++ if (result.format() == 5 && result.val_rational().size()) ++ this->ShutterSpeedValue = result.val_rational().front(); ++ break; ++ ++ case 0x9204: ++ // Exposure bias value ++ if (result.format() == 5 && result.val_rational().size()) ++ this->ExposureBiasValue = result.val_rational().front(); ++ break; ++ ++ case 0x9206: ++ // Subject distance ++ if (result.format() == 5 && result.val_rational().size()) ++ this->SubjectDistance = result.val_rational().front(); ++ break; ++ ++ case 0x9209: ++ // Flash used ++ if (result.format() == 3 && result.val_short().size()) { ++ uint16_t data = result.val_short().front(); ++ ++ this->Flash = data & 1; ++ this->FlashReturnedLight = (data & 6) >> 1; ++ this->FlashMode = (data & 24) >> 3; ++ } ++ break; ++ ++ case 0x920a: ++ // Focal length ++ if (result.format() == 5 && result.val_rational().size()) ++ this->FocalLength = result.val_rational().front(); ++ break; ++ ++ case 0x9207: ++ // Metering mode ++ if (result.format() == 3 && result.val_short().size()) ++ this->MeteringMode = result.val_short().front(); ++ break; ++ ++ case 0x9291: ++ // Subsecond original time ++ if (result.format() == 2) ++ this->SubSecTimeOriginal = result.val_string(); ++ break; ++ ++ case 0xa002: ++ // EXIF Image width ++ if (result.format() == 4 && result.val_long().size()) ++ this->ImageWidth = result.val_long().front(); ++ if (result.format() == 3 && result.val_short().size()) ++ this->ImageWidth = result.val_short().front(); ++ break; ++ ++ case 0xa003: ++ // EXIF Image height ++ if (result.format() == 4 && result.val_long().size()) ++ this->ImageHeight = result.val_long().front(); ++ if (result.format() == 3 && result.val_short().size()) ++ this->ImageHeight = result.val_short().front(); ++ break; ++ ++ case 0xa20e: ++ // EXIF Focal plane X-resolution ++ if (result.format() == 5) { ++ this->LensInfo.FocalPlaneXResolution = result.val_rational()[0]; ++ } ++ break; ++ ++ case 0xa20f: ++ // EXIF Focal plane Y-resolution ++ if (result.format() == 5) { ++ this->LensInfo.FocalPlaneYResolution = result.val_rational()[0]; ++ } ++ break; ++ ++ case 0xa210: ++ // EXIF Focal plane resolution unit ++ if (result.format() == 3 && result.val_short().size()) { ++ this->LensInfo.FocalPlaneResolutionUnit = result.val_short().front(); ++ } ++ break; ++ ++ case 0xa405: ++ // Focal length in 35mm film ++ if (result.format() == 3 && result.val_short().size()) ++ this->FocalLengthIn35mm = result.val_short().front(); ++ break; ++ ++ case 0xa432: ++ // Focal length and FStop. ++ if (result.format() == 5) { ++ int sz = static_cast(result.val_rational().size()); ++ if (sz) ++ this->LensInfo.FocalLengthMin = result.val_rational()[0]; ++ if (sz > 1) ++ this->LensInfo.FocalLengthMax = result.val_rational()[1]; ++ if (sz > 2) ++ this->LensInfo.FStopMin = result.val_rational()[2]; ++ if (sz > 3) ++ this->LensInfo.FStopMax = result.val_rational()[3]; ++ } ++ break; ++ ++ case 0xa433: ++ // Lens make. ++ if (result.format() == 2) { ++ this->LensInfo.Make = result.val_string(); ++ } ++ break; ++ ++ case 0xa434: ++ // Lens model. ++ if (result.format() == 2) { ++ this->LensInfo.Model = result.val_string(); ++ } ++ break; ++ } ++ offs += 12; ++ } ++ } ++ ++ // Jump to the GPS SubIFD if it exists and parse all the information ++ // there. Note that it's possible that the GPS SubIFD doesn't exist. ++ if (gps_sub_ifd_offset + 4 <= len) { ++ offs = gps_sub_ifd_offset; ++ int num_sub_entries = parse_value(buf + offs, alignIntel); ++ if (offs + 6 + 12 * num_sub_entries > len) return PARSE_EXIF_ERROR_CORRUPT; ++ offs += 2; ++ while (--num_sub_entries >= 0) { ++ unsigned short tag, format; ++ unsigned length, data; ++ parseIFEntryHeader(buf + offs, alignIntel, tag, format, length, data); ++ switch (tag) { ++ case 1: ++ // GPS north or south ++ this->GeoLocation.LatComponents.direction = *(buf + offs + 8); ++ if (this->GeoLocation.LatComponents.direction == 0) { ++ this->GeoLocation.LatComponents.direction = '?'; ++ } ++ if ('S' == this->GeoLocation.LatComponents.direction) { ++ this->GeoLocation.Latitude = -this->GeoLocation.Latitude; ++ } ++ break; ++ ++ case 2: ++ // GPS latitude ++ if ((format == 5 || format == 10) && length == 3) { ++ this->GeoLocation.LatComponents.degrees = parse_value( ++ buf + data + tiff_header_start, alignIntel); ++ this->GeoLocation.LatComponents.minutes = parse_value( ++ buf + data + tiff_header_start + 8, alignIntel); ++ this->GeoLocation.LatComponents.seconds = parse_value( ++ buf + data + tiff_header_start + 16, alignIntel); ++ this->GeoLocation.Latitude = ++ this->GeoLocation.LatComponents.degrees + ++ this->GeoLocation.LatComponents.minutes / 60 + ++ this->GeoLocation.LatComponents.seconds / 3600; ++ if ('S' == this->GeoLocation.LatComponents.direction) { ++ this->GeoLocation.Latitude = -this->GeoLocation.Latitude; ++ } ++ } ++ break; ++ ++ case 3: ++ // GPS east or west ++ this->GeoLocation.LonComponents.direction = *(buf + offs + 8); ++ if (this->GeoLocation.LonComponents.direction == 0) { ++ this->GeoLocation.LonComponents.direction = '?'; ++ } ++ if ('W' == this->GeoLocation.LonComponents.direction) { ++ this->GeoLocation.Longitude = -this->GeoLocation.Longitude; ++ } ++ break; ++ ++ case 4: ++ // GPS longitude ++ if ((format == 5 || format == 10) && length == 3) { ++ this->GeoLocation.LonComponents.degrees = parse_value( ++ buf + data + tiff_header_start, alignIntel); ++ this->GeoLocation.LonComponents.minutes = parse_value( ++ buf + data + tiff_header_start + 8, alignIntel); ++ this->GeoLocation.LonComponents.seconds = parse_value( ++ buf + data + tiff_header_start + 16, alignIntel); ++ this->GeoLocation.Longitude = ++ this->GeoLocation.LonComponents.degrees + ++ this->GeoLocation.LonComponents.minutes / 60 + ++ this->GeoLocation.LonComponents.seconds / 3600; ++ if ('W' == this->GeoLocation.LonComponents.direction) ++ this->GeoLocation.Longitude = -this->GeoLocation.Longitude; ++ } ++ break; ++ ++ case 5: ++ // GPS altitude reference (below or above sea level) ++ this->GeoLocation.AltitudeRef = *(buf + offs + 8); ++ if (1 == this->GeoLocation.AltitudeRef) { ++ this->GeoLocation.Altitude = -this->GeoLocation.Altitude; ++ } ++ break; ++ ++ case 6: ++ // GPS altitude ++ if ((format == 5 || format == 10)) { ++ this->GeoLocation.Altitude = parse_value( ++ buf + data + tiff_header_start, alignIntel); ++ if (1 == this->GeoLocation.AltitudeRef) { ++ this->GeoLocation.Altitude = -this->GeoLocation.Altitude; ++ } ++ } ++ break; ++ ++ case 11: ++ // GPS degree of precision (DOP) ++ if ((format == 5 || format == 10)) { ++ this->GeoLocation.DOP = parse_value( ++ buf + data + tiff_header_start, alignIntel); ++ } ++ break; ++ } ++ offs += 12; ++ } ++ } ++ ++ return PARSE_EXIF_SUCCESS; ++} ++ ++void easyexif::EXIFInfo::clear() { ++ // Strings ++ ImageDescription = ""; ++ Make = ""; ++ Model = ""; ++ Software = ""; ++ DateTime = ""; ++ DateTimeOriginal = ""; ++ DateTimeDigitized = ""; ++ SubSecTimeOriginal = ""; ++ Copyright = ""; ++ ++ // Shorts / unsigned / double ++ ByteAlign = 0; ++ Orientation = 0; ++ ++ BitsPerSample = 0; ++ ExposureTime = 0; ++ FNumber = 0; ++ ExposureProgram = 0; ++ ISOSpeedRatings = 0; ++ ShutterSpeedValue = 0; ++ ExposureBiasValue = 0; ++ SubjectDistance = 0; ++ FocalLength = 0; ++ FocalLengthIn35mm = 0; ++ Flash = 0; ++ FlashReturnedLight = 0; ++ FlashMode = 0; ++ MeteringMode = 0; ++ ImageWidth = 0; ++ ImageHeight = 0; ++ ++ // Geolocation ++ GeoLocation.Latitude = 0; ++ GeoLocation.Longitude = 0; ++ GeoLocation.Altitude = 0; ++ GeoLocation.AltitudeRef = 0; ++ GeoLocation.DOP = 0; ++ GeoLocation.LatComponents.degrees = 0; ++ GeoLocation.LatComponents.minutes = 0; ++ GeoLocation.LatComponents.seconds = 0; ++ GeoLocation.LatComponents.direction = '?'; ++ GeoLocation.LonComponents.degrees = 0; ++ GeoLocation.LonComponents.minutes = 0; ++ GeoLocation.LonComponents.seconds = 0; ++ GeoLocation.LonComponents.direction = '?'; ++ ++ // LensInfo ++ LensInfo.FocalLengthMax = 0; ++ LensInfo.FocalLengthMin = 0; ++ LensInfo.FStopMax = 0; ++ LensInfo.FStopMin = 0; ++ LensInfo.FocalPlaneYResolution = 0; ++ LensInfo.FocalPlaneXResolution = 0; ++ LensInfo.FocalPlaneResolutionUnit = 0; ++ LensInfo.Make = ""; ++ LensInfo.Model = ""; ++} +diff --git a/src/external/easyexif/exif.h b/src/external/easyexif/exif.h +new file mode 100644 +index 000000000..3c340d1cc +--- /dev/null ++++ b/src/external/easyexif/exif.h +@@ -0,0 +1,168 @@ ++/************************************************************************** ++ exif.h -- A simple ISO C++ library to parse basic EXIF ++ information from a JPEG file. ++ ++ Based on the description of the EXIF file format at: ++ -- http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html ++ -- http://www.media.mit.edu/pia/Research/deepview/exif.html ++ -- http://www.exif.org/Exif2-2.PDF ++ ++ Copyright (c) 2010-2016 Mayank Lahiri ++ mlahiri@gmail.com ++ All rights reserved. ++ ++ Redistribution and use in source and binary forms, with or without ++ modification, are permitted provided that the following conditions are met: ++ ++ -- Redistributions of source code must retain the above copyright notice, ++ this list of conditions and the following disclaimer. ++ -- 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. ++ ++ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESS ++ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN ++ NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS 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, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, ++ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++*/ ++#ifndef __EXIF_H ++#define __EXIF_H ++ ++#include ++ ++namespace easyexif { ++ ++// ++// Class responsible for storing and parsing EXIF information from a JPEG blob ++// ++class EXIFInfo { ++ public: ++ // Parsing function for an entire JPEG image buffer. ++ // ++ // PARAM 'data': A pointer to a JPEG image. ++ // PARAM 'length': The length of the JPEG image. ++ // RETURN: PARSE_EXIF_SUCCESS (0) on succes with 'result' filled out ++ // error code otherwise, as defined by the PARSE_EXIF_ERROR_* macros ++ int parseFrom(const unsigned char *data, unsigned length); ++ int parseFrom(const std::string &data); ++ ++ // Parsing function for an EXIF segment. This is used internally by parseFrom() ++ // but can be called for special cases where only the EXIF section is ++ // available (i.e., a blob starting with the bytes "Exif\0\0"). ++ int parseFromEXIFSegment(const unsigned char *buf, unsigned len); ++ ++ // Set all data members to default values. ++ void clear(); ++ ++ // Data fields filled out by parseFrom() ++ char ByteAlign; // 0 = Motorola byte alignment, 1 = Intel ++ std::string ImageDescription; // Image description ++ std::string Make; // Camera manufacturer's name ++ std::string Model; // Camera model ++ unsigned short Orientation; // Image orientation, start of data corresponds to ++ // 0: unspecified in EXIF data ++ // 1: upper left of image ++ // 3: lower right of image ++ // 6: upper right of image ++ // 8: lower left of image ++ // 9: undefined ++ unsigned short BitsPerSample; // Number of bits per component ++ std::string Software; // Software used ++ std::string DateTime; // File change date and time ++ std::string DateTimeOriginal; // Original file date and time (may not exist) ++ std::string DateTimeDigitized; // Digitization date and time (may not exist) ++ std::string SubSecTimeOriginal; // Sub-second time that original picture was taken ++ std::string Copyright; // File copyright information ++ double ExposureTime; // Exposure time in seconds ++ double FNumber; // F/stop ++ unsigned short ExposureProgram; // Exposure program ++ // 0: Not defined ++ // 1: Manual ++ // 2: Normal program ++ // 3: Aperture priority ++ // 4: Shutter priority ++ // 5: Creative program ++ // 6: Action program ++ // 7: Portrait mode ++ // 8: Landscape mode ++ unsigned short ISOSpeedRatings; // ISO speed ++ double ShutterSpeedValue; // Shutter speed (reciprocal of exposure time) ++ double ExposureBiasValue; // Exposure bias value in EV ++ double SubjectDistance; // Distance to focus point in meters ++ double FocalLength; // Focal length of lens in millimeters ++ unsigned short FocalLengthIn35mm; // Focal length in 35mm film ++ char Flash; // 0 = no flash, 1 = flash used ++ unsigned short FlashReturnedLight;// Flash returned light status ++ // 0: No strobe return detection function ++ // 1: Reserved ++ // 2: Strobe return light not detected ++ // 3: Strobe return light detected ++ unsigned short FlashMode; // Flash mode ++ // 0: Unknown ++ // 1: Compulsory flash firing ++ // 2: Compulsory flash suppression ++ // 3: Automatic mode ++ unsigned short MeteringMode; // Metering mode ++ // 1: average ++ // 2: center weighted average ++ // 3: spot ++ // 4: multi-spot ++ // 5: multi-segment ++ unsigned ImageWidth; // Image width reported in EXIF data ++ unsigned ImageHeight; // Image height reported in EXIF data ++ struct Geolocation_t { // GPS information embedded in file ++ double Latitude; // Image latitude expressed as decimal ++ double Longitude; // Image longitude expressed as decimal ++ double Altitude; // Altitude in meters, relative to sea level ++ char AltitudeRef; // 0 = above sea level, -1 = below sea level ++ double DOP; // GPS degree of precision (DOP) ++ struct Coord_t { ++ double degrees; ++ double minutes; ++ double seconds; ++ char direction; ++ } LatComponents, LonComponents; // Latitude, Longitude expressed in deg/min/sec ++ } GeoLocation; ++ struct LensInfo_t { // Lens information ++ double FStopMin; // Min aperture (f-stop) ++ double FStopMax; // Max aperture (f-stop) ++ double FocalLengthMin; // Min focal length (mm) ++ double FocalLengthMax; // Max focal length (mm) ++ double FocalPlaneXResolution; // Focal plane X-resolution ++ double FocalPlaneYResolution; // Focal plane Y-resolution ++ unsigned short FocalPlaneResolutionUnit; // Focal plane resolution unit ++ // 1: No absolute unit of measurement. ++ // 2: Inch. ++ // 3: Centimeter. ++ // 4: Millimeter. ++ // 5: Micrometer. ++ std::string Make; // Lens manufacturer ++ std::string Model; // Lens model ++ } LensInfo; ++ ++ ++ EXIFInfo() { ++ clear(); ++ } ++}; ++ ++} ++ ++// Parse was successful ++#define PARSE_EXIF_SUCCESS 0 ++// No JPEG markers found in buffer, possibly invalid JPEG file ++#define PARSE_EXIF_ERROR_NO_JPEG 1982 ++// No EXIF header found in JPEG file. ++#define PARSE_EXIF_ERROR_NO_EXIF 1983 ++// Byte alignment specified in EXIF file was unknown (not Motorola or Intel). ++#define PARSE_EXIF_ERROR_UNKNOWN_BYTEALIGN 1984 ++// EXIF header was found, but data was corrupted. ++#define PARSE_EXIF_ERROR_CORRUPT 1985 ++ ++#endif diff --git a/Substituted-the-notorious-jhead-with-a-smaller-minimal-lib.patch b/Substituted-the-notorious-jhead-with-a-smaller-minimal-lib.patch new file mode 100644 index 0000000..08f44c7 --- /dev/null +++ b/Substituted-the-notorious-jhead-with-a-smaller-minimal-lib.patch @@ -0,0 +1,162 @@ +From 2a770fff3a984e596c94373b766e050afd5335fe Mon Sep 17 00:00:00 2001 +From: Paolo Cignoni +Date: Sun, 20 Sep 2020 17:18:01 +0200 +Subject: [PATCH] Substituted the notorious jhead with a smaller minimal lib + (easyexif) + +--- + src/meshlab/mainwindow_RunTime.cpp | 64 ++++++++++++++++-------------- + src/meshlab/meshlab.pro | 15 ++++--- + 2 files changed, 41 insertions(+), 38 deletions(-) + +diff --git a/src/meshlab/mainwindow_RunTime.cpp b/src/meshlab/mainwindow_RunTime.cpp +index 784bcbf6c..9b2734370 100644 +--- a/src/meshlab/mainwindow_RunTime.cpp ++++ b/src/meshlab/mainwindow_RunTime.cpp +@@ -49,12 +49,7 @@ + #include "rich_parameter_gui/richparameterlistdialog.h" + + #include +- +- +-extern "C" { +-#include "jhead.h" +-} +- ++#include "../external/easyexif/exif.h" + + using namespace std; + using namespace vcg; +@@ -2015,12 +2010,34 @@ bool MainWindow::importRaster(const QString& fileImg) + /// If no CCD Width value is provided, the intrinsics are extracted using the Equivalent 35mm focal + /// If no or invalid EXIF info is found, the Intrinsics are initialized as a "plausible" 35mm sensor, with 50mm focal + +- ::ResetJpgfile(); +- FILE * pFile = fopen(qUtf8Printable(fileName), "rb"); +- +- int ret = ::ReadJpegSections (pFile, READ_METADATA); +- fclose(pFile); +- if (!ret || (ImageInfo.CCDWidth==0.0f && ImageInfo.FocalLength35mmEquiv==0.0f)) ++ // Read the JPEG file into a buffer ++ FILE *fp = fopen(qUtf8Printable(fileName), "rb"); ++ if (fp) { ++ QString errorMsgFormat = "Exif Parsing: Unable to open file:\n\"%1\"\n\nError details: file %1 is not readable."; ++ QMessageBox::critical(this, tr("Meshlab Opening Error"), errorMsgFormat.arg(fileName)); ++ return false; ++ } ++ fseek(fp, 0, SEEK_END); ++ unsigned long fsize = ftell(fp); ++ rewind(fp); ++ unsigned char *buf = new unsigned char[fsize]; ++ if (fread(buf, 1, fsize, fp) != fsize) { ++ QString errorMsgFormat = "Exif Parsing: Unable to read the content of the opened file:\n\"%1\"\n\nError details: file %1 is not readable."; ++ QMessageBox::critical(this, tr("Meshlab Opening Error"), errorMsgFormat.arg(fileName)); ++ delete[] buf; ++ return false; ++ } ++ fclose(fp); ++ ++ // Parse EXIF ++ easyexif::EXIFInfo ImageInfo; ++ int code = ImageInfo.parseFrom(buf, fsize); ++ delete[] buf; ++ if (code) { ++ GLA()->Logf(0,"Warning unable to parse exif for file %s",qPrintable(fileName) ); ++ } ++ ++ if (code || ImageInfo.FocalLengthIn35mm==0.0f) + { + rm->shot.Intrinsics.ViewportPx = vcg::Point2i(rm->currentPlane->image.width(), rm->currentPlane->image.height()); + rm->shot.Intrinsics.CenterPx = Point2m(float(rm->currentPlane->image.width()/2.0), float(rm->currentPlane->image.width()/2.0)); +@@ -2028,26 +2045,13 @@ bool MainWindow::importRaster(const QString& fileImg) + rm->shot.Intrinsics.PixelSizeMm[1]=rm->shot.Intrinsics.PixelSizeMm[0]; + rm->shot.Intrinsics.FocalMm = 50.0f; + } +- else if (ImageInfo.CCDWidth!=0) +- { +- rm->shot.Intrinsics.ViewportPx = vcg::Point2i(ImageInfo.Width, ImageInfo.Height); +- rm->shot.Intrinsics.CenterPx = Point2m(float(ImageInfo.Width/2.0), float(ImageInfo.Height/2.0)); +- float ratio; +- if (ImageInfo.Width>ImageInfo.Height) +- ratio=(float)ImageInfo.Width/(float)ImageInfo.Height; +- else +- ratio=(float)ImageInfo.Height/(float)ImageInfo.Width; +- rm->shot.Intrinsics.PixelSizeMm[0]=ImageInfo.CCDWidth/(float)ImageInfo.Width; +- rm->shot.Intrinsics.PixelSizeMm[1]=ImageInfo.CCDWidth/((float)ImageInfo.Height*ratio); +- rm->shot.Intrinsics.FocalMm = ImageInfo.FocalLength; +- } + else + { +- rm->shot.Intrinsics.ViewportPx = vcg::Point2i(ImageInfo.Width, ImageInfo.Height); +- rm->shot.Intrinsics.CenterPx = Point2m(float(ImageInfo.Width/2.0), float(ImageInfo.Height/2.0)); +- float ratioFocal=ImageInfo.FocalLength/ImageInfo.FocalLength35mmEquiv; +- rm->shot.Intrinsics.PixelSizeMm[0]=(36.0f*ratioFocal)/(float)ImageInfo.Width; +- rm->shot.Intrinsics.PixelSizeMm[1]=(24.0f*ratioFocal)/(float)ImageInfo.Height; ++ rm->shot.Intrinsics.ViewportPx = vcg::Point2i(ImageInfo.ImageWidth, ImageInfo.ImageHeight); ++ rm->shot.Intrinsics.CenterPx = Point2m(float(ImageInfo.ImageWidth/2.0), float(ImageInfo.ImageHeight/2.0)); ++ float ratioFocal=ImageInfo.FocalLength/ImageInfo.FocalLengthIn35mm; ++ rm->shot.Intrinsics.PixelSizeMm[0]=(36.0f*ratioFocal)/(float)ImageInfo.ImageWidth; ++ rm->shot.Intrinsics.PixelSizeMm[1]=(24.0f*ratioFocal)/(float)ImageInfo.ImageHeight; + rm->shot.Intrinsics.FocalMm = ImageInfo.FocalLength; + } + +diff --git a/src/meshlab/meshlab.pro b/src/meshlab/meshlab.pro +index 4e492a184..c5e668b9d 100644 +--- a/src/meshlab/meshlab.pro ++++ b/src/meshlab/meshlab.pro +@@ -8,7 +8,6 @@ QT += network + + #CONFIG += debug_and_release + DESTDIR = $$MESHLAB_DISTRIB_DIRECTORY +-EXIF_DIR = ../external/jhead-3.04 + + DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x000000 + +@@ -17,8 +16,7 @@ INCLUDEPATH *= \ + .. \ + ../.. \ + $$VCGDIR \ +- $$EIGENDIR \ +- $$EXIF_DIR ++ $$EIGENDIR + + !CONFIG(system_glew): INCLUDEPATH *= $$GLEWDIR/include + +@@ -79,7 +77,8 @@ SOURCES += \ + glarea_setting.cpp \ + rich_parameter_gui/richparameterlistdialog.cpp \ + rich_parameter_gui/richparameterlistframe.cpp \ +- rich_parameter_gui/richparameterwidgets.cpp ++ rich_parameter_gui/richparameterwidgets.cpp \ ++ ../external/easyexif/exif.cpp + + FORMS += \ + ui/layerDialog.ui \ +@@ -114,15 +113,15 @@ ICON = images/meshlab.icns + CONFIG += stl + + win32-msvc:LIBS += \ +- -L$$MESHLAB_DISTRIB_DIRECTORY/lib/win32-msvc -ljhead \ ++ -L$$MESHLAB_DISTRIB_DIRECTORY/lib/win32-msvc \ + -L$$MESHLAB_DISTRIB_DIRECTORY/lib -lmeshlab-common -lopengl32 -lGLU32 + + win32-g++:LIBS += \ +- -L$$MESHLAB_DISTRIB_DIRECTORY/lib/win32-gcc -ljhead \ ++ -L$$MESHLAB_DISTRIB_DIRECTORY/lib/win32-gcc \ + -L$$MESHLAB_DISTRIB_DIRECTORY/lib -lmeshlab-common -lopengl32 -lGLU32 + + macx:LIBS += \ +- -L$$MESHLAB_DISTRIB_DIRECTORY/lib/macx64 -ljhead \ ++ -L$$MESHLAB_DISTRIB_DIRECTORY/lib/macx64 \ + $$MESHLAB_DISTRIB_DIRECTORY/lib/libmeshlab-common.dylib + + macx:QMAKE_POST_LINK += "\ +@@ -133,7 +132,7 @@ macx:QMAKE_POST_LINK += "\ + + linux:LIBS += \ + -lmeshlab-common -lGLU \ +- -L$$MESHLAB_DISTRIB_DIRECTORY/lib/linux -ljhead ++ -L$$MESHLAB_DISTRIB_DIRECTORY/lib/linux + + !CONFIG(system_glew) { + INCLUDEPATH *= $$GLEWDIR/include diff --git a/_constraints b/_constraints new file mode 100644 index 0000000..ea2e8cd --- /dev/null +++ b/_constraints @@ -0,0 +1,14 @@ + + + + + 5 + + + 4 + + + 800 + + + diff --git a/fixed-cmake-removed-any-other-reference-to-jhead.patch b/fixed-cmake-removed-any-other-reference-to-jhead.patch new file mode 100644 index 0000000..66b96ba --- /dev/null +++ b/fixed-cmake-removed-any-other-reference-to-jhead.patch @@ -0,0 +1,155 @@ +From 3b8245899234c9ac7343e7002e0e0835eabe69c4 Mon Sep 17 00:00:00 2001 +From: alemuntoni +Date: Wed, 23 Sep 2020 13:20:39 +0200 +Subject: [PATCH] fixed cmake, removed any other reference to jhead + +--- + src/common/CMakeLists.txt | 7 ++++--- + src/common/common.pro | 4 +--- + src/external.cmake | 20 -------------------- + src/meshlab/CMakeLists.txt | 7 ++++--- + src/templates/common.cmake | 4 +--- + src/templates/meshlab.cmake | 4 +--- + 6 files changed, 11 insertions(+), 35 deletions(-) + +diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt +index e55628e25..1a8da3eea 100644 +--- a/src/common/CMakeLists.txt ++++ b/src/common/CMakeLists.txt +@@ -28,7 +28,8 @@ set(SOURCES + ml_thread_safe_memory_info.cpp + mlapplication.cpp + pluginmanager.cpp +- searcher.cpp) ++ searcher.cpp ++ ${EXTERNAL_DIR}/easyexif/exif.cpp) + + set(HEADERS + filter_parameter/rich_parameter.h +@@ -65,6 +66,7 @@ endif() + add_library(common ${TARGET_TYPE} ${SOURCES} ${HEADERS} ${RESOURCES}) + + set_target_properties(common PROPERTIES OUTPUT_NAME meshlab-common) ++target_include_directories(common PRIVATE ${EXTERNAL_DIR}/easyexif/) + target_link_libraries( + common + PUBLIC Qt5::Core +@@ -72,8 +74,7 @@ target_link_libraries( + Qt5::Widgets + Qt5::Xml + Qt5::XmlPatterns +- external-glew +- PRIVATE external-jhead) ++ external-glew) + + set_property(TARGET common PROPERTY FOLDER Core) + +diff --git a/src/common/common.pro b/src/common/common.pro +index fc16e18a4..706b9f0c8 100644 +--- a/src/common/common.pro ++++ b/src/common/common.pro +@@ -1,5 +1,4 @@ + include (../general.pri) +-EXIF_DIR = ../external/jhead-3.04 + + QT += opengl + QT += xml +@@ -19,8 +18,7 @@ linux:CONFIG += dll + INCLUDEPATH *= \ + ../.. \ + $$VCGDIR \ +- $$EIGENDIR \ +- $$EXIF_DIR ++ $$EIGENDIR + + !CONFIG(system_glew) { + INCLUDEPATH *= $$GLEWDIR/include +diff --git a/src/external.cmake b/src/external.cmake +index ef778ce52..08be7e6c3 100644 +--- a/src/external.cmake ++++ b/src/external.cmake +@@ -1,26 +1,6 @@ + # Copyright 2019 Collabora, Ltd. + # SPDX-License-Identifier: BSL-1.0 + +-# jhead - required +-message(STATUS "- jhead - using bundled source") +-set(JHEAD_DIR ${EXTERNAL_DIR}/jhead-3.04) +-add_library( +- external-jhead STATIC +- "${JHEAD_DIR}/jpgfile.c" +- "${JHEAD_DIR}/jpgqguess.c" +- "${JHEAD_DIR}/jhead.c" +- "${JHEAD_DIR}/paths.c" +- "${JHEAD_DIR}/exif.c" +- "${JHEAD_DIR}/iptc.c" +- "${JHEAD_DIR}/gpsinfo.c" +- "${JHEAD_DIR}/makernote.c") +-if(WIN32) +- target_sources(external-jhead PRIVATE "${JHEAD_DIR}/myglob.c") +-endif() +-target_compile_definitions(external-jhead PRIVATE main=jhead_main) +-target_include_directories(external-jhead PUBLIC ${JHEAD_DIR}) +-set_property(TARGET external-jhead PROPERTY FOLDER External) +- + # GLEW - required + set(GLEW_DIR ${EXTERNAL_DIR}/glew-2.1.0) + if(ALLOW_SYSTEM_GLEW AND TARGET GLEW::GLEW) +diff --git a/src/meshlab/CMakeLists.txt b/src/meshlab/CMakeLists.txt +index 698358fb6..c44bb972d 100644 +--- a/src/meshlab/CMakeLists.txt ++++ b/src/meshlab/CMakeLists.txt +@@ -30,7 +30,8 @@ set(SOURCES + meshlab_settings/meshlabsettingsdialog.cpp + ${VCGDIR}/wrap/gui/trackball.cpp + ${VCGDIR}/wrap/gui/trackmode.cpp +- ${VCGDIR}/wrap/gui/coordinateframe.cpp) ++ ${VCGDIR}/wrap/gui/coordinateframe.cpp ++ ${EXTERNAL_DIR}/easyexif/exif.cpp) + + set(HEADERS + additionalgui.h +@@ -87,11 +88,11 @@ add_executable(meshlab WIN32 MACOSX_BUNDLE + ${UI}) + + target_compile_definitions(meshlab PUBLIC QT_DISABLE_DEPRECATED_BEFORE=0x000000) +-target_include_directories(meshlab PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) ++target_include_directories(meshlab PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${EXTERNAL_DIR}/easyexif/) + target_link_libraries( + meshlab + PUBLIC common OpenGL::GLU Qt5::Network +- PRIVATE external-jhead) ++ ) + + set_property(TARGET meshlab PROPERTY FOLDER Core) + +diff --git a/src/templates/common.cmake b/src/templates/common.cmake +index 7dfe4553a..f9f465347 100644 +--- a/src/templates/common.cmake ++++ b/src/templates/common.cmake +@@ -30,9 +30,7 @@ target_link_libraries({{name}} + Qt5::Widgets + Qt5::Xml + Qt5::XmlPatterns +- external-glew +- PRIVATE +- external-jhead) ++ external-glew) + {% endblock %} + + {% block output_dir %}{% endblock %} +diff --git a/src/templates/meshlab.cmake b/src/templates/meshlab.cmake +index 6426363e1..14116ae91 100644 +--- a/src/templates/meshlab.cmake ++++ b/src/templates/meshlab.cmake +@@ -42,9 +42,7 @@ target_link_libraries({{name}} + PUBLIC + common + OpenGL::GLU +- Qt5::Network +- PRIVATE +- external-jhead) ++ Qt5::Network) + + {% endblock %} + {% block install %} diff --git a/libjhead-removed.patch b/libjhead-removed.patch new file mode 100644 index 0000000..1a1d892 --- /dev/null +++ b/libjhead-removed.patch @@ -0,0 +1,7649 @@ +From 2027888dedbe1535cea6b73cfa8f95e0169a3d9d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Stefan=20Br=C3=BCns?= +Date: Mon, 26 Oct 2020 00:40:49 +0100 +Subject: [PATCH 3/4] t + +--- + src/external/external.pro | 4 - + src/external/jhead-3.04/changes.txt | 444 ----- + src/external/jhead-3.04/exif.c | 1604 --------------- + src/external/jhead-3.04/gpsinfo.c | 218 -- + src/external/jhead-3.04/iptc.c | 220 -- + src/external/jhead-3.04/jhead-3.04.pro | 23 - + src/external/jhead-3.04/jhead.1 | 427 ---- + src/external/jhead-3.04/jhead.c | 1771 ----------------- + src/external/jhead-3.04/jhead.h | 271 --- + src/external/jhead-3.04/jpgfile.c | 775 -------- + src/external/jhead-3.04/jpgqguess.c | 214 -- + src/external/jhead-3.04/makernote.c | 191 -- + src/external/jhead-3.04/myglob.c | 304 --- + src/external/jhead-3.04/paths.c | 140 -- + src/external/jhead-3.04/readme.txt | 60 - + src/external/jhead-3.04/usage.html | 478 ----- + src/meshlab/glarea.h | 2 +- + src/meshlab/mainwindow_RunTime.cpp | 57 +- + src/meshlab/meshlab.pro | 4 +- + .../decorate_base/decorate_base.cpp | 6 +- + .../filter_isoparametrization/tangent_space.h | 4 +- + .../filter_qhull/filter_qhull.cpp | 2 +- + .../filter_qhull/qhull_tools.cpp | 2 +- + src/meshlabplugins/io_x3d/import_x3d.h | 14 +- + src/meshlabserver/mainserver.cpp | 2 +- + 25 files changed, 47 insertions(+), 7190 deletions(-) + delete mode 100644 src/external/jhead-3.04/changes.txt + delete mode 100644 src/external/jhead-3.04/exif.c + delete mode 100644 src/external/jhead-3.04/gpsinfo.c + delete mode 100644 src/external/jhead-3.04/iptc.c + delete mode 100755 src/external/jhead-3.04/jhead-3.04.pro + delete mode 100644 src/external/jhead-3.04/jhead.1 + delete mode 100644 src/external/jhead-3.04/jhead.c + delete mode 100644 src/external/jhead-3.04/jhead.h + delete mode 100644 src/external/jhead-3.04/jpgfile.c + delete mode 100644 src/external/jhead-3.04/jpgqguess.c + delete mode 100644 src/external/jhead-3.04/makernote.c + delete mode 100644 src/external/jhead-3.04/myglob.c + delete mode 100644 src/external/jhead-3.04/paths.c + delete mode 100644 src/external/jhead-3.04/readme.txt + delete mode 100644 src/external/jhead-3.04/usage.html + +diff --git a/src/external/external.pro b/src/external/external.pro +index 50934f9..6b046ac 100644 +--- a/src/external/external.pro ++++ b/src/external/external.pro +@@ -2,10 +2,6 @@ config += debug_and_release + + TEMPLATE = subdirs + +-#just jhead needs to be compiled if the config is meshlab_mini +-SUBDIRS = \ +- jhead-3.04/jhead-3.04.pro +- + !meshlab_mini { + SUBDIRS += \ + levmar-2.3/levmar-2.3.pro \ +diff --git a/src/external/jhead-3.04/changes.txt b/src/external/jhead-3.04/changes.txt +deleted file mode 100644 +index cf268d6..0000000 +--- a/src/external/jhead-3.04/changes.txt ++++ /dev/null +@@ -1,444 +0,0 @@ +-**************************************************** +-***** Detailed change log - since version 1.2 ***** +-***** In cronological order, oldest to newest ***** +-**************************************************** +- +-Jun 3 2001: +-Added -dc option for deleting all comments +- +-Jun 5 2001: +-No longer clip comment length at 200 characters on display +- +-Jul 16 2001 +-Follow TIFF link correctly +-Save thumbnail option added +- (Thanks Michal D Hughes for his help mdh(a)logcabin.woods.bridge.com) +- +-Aug 8 & 9 2001 +-Transfer exif header option added. +-Moved relative path & discard all but jpeg stuff in separate functions +-Changed ISO heuristic to deal with ISo < 80 better +- (Thanks Harry TsaiHarryTsai(a)alum.mit.edu) +- +-Agust 12 2001 +-Testing under Linux, minor fixups. +- +--------Released version 1.3------- +- +-August 26 2001 +-Fixed problem where thumbnails in big endian files were not saved right. +- (thanks Joe Steele [joe(a)madewell.com]) +- +-Sept 1 2001 +-Added command line option to remove exif section entirely. +-Added time / time-zone adjust option. +- +-Sept 9 2001 +-Avoid renaming file with right name again with -n option +-Change name of SetFileTime variable to not conflict with windows. +- +-Oct 9 2001 +-Added option to set exif timestamp to absolute time +-Added option to save thumbnail to stdout (unix only) +- (thanks Nathan Schmidt [mailto:nathan(a)cs.stanford.edu]) +-Fixed bug in parsing truncated exif headers +- (thanks Joachim.Geyer(a)djh-freeweb.de) +-Got rid of strnprintf (not avilable on FreeBSD 4.4) +- +--------Released version 1.4------- +- +-Oct 10 2001 +-More improved handling of truncated exif headers - as may be produced +-by -dt option in older versions of this program. +- +-Oct 18 2001 +-Fixed bug in -ts option where '-' causes scanf to treat parms as negative. +- (thanks Pete Ashdon [mailto:pashdown(a)xmission.com]) +- +-Oct 25 2001 +-Added -ce option +- +--------Released version 1.5------- +- +-Dec 26 2001 +-Added -V (version) option +-Added -exonly (exif only) option +- +- +-Jan 2 2002: +-Fixed lots of typos (Thanks, David Baker [mailto:dave(a)dsb3.com]) +- +-Jan 12: 2002 +-Use EDITOR environment variable to pick which editor (Instead of notpead or VI) +- +-Jan 13: 2002 +-Improved thumbnail deletion feature - now not just shortens the header, but +-also removes pointers to the thumbnail form the exif header. +- +- +--------Released version 1.6------- +- +-Jan 29 2002 +-Use adjusted date instead of original date when -ta is used in combination +-with -ft or -n options +- +-Feb 25 2002 +-Added image orientation display to summary +- +-April 22 2002 +-Changed 35mm equivalent focal calculation to use 36mm instead of 35mm for 35mm +-negative width (35 mm negative frames are 36 mm wide) +- +-April 28 2002 +-Split jhead.c int jhead.c and jpgfile.c. Jpgfile.c contains jpeg manipulation +-code that is reusable outside of jhead, while jhead.c contains code specific +-to jhead (command line parsing, display) +- +-May 11 2002 +-Fix bug in -dt option that rears its ugly head if the directories are in the +-exif header out of order. +- +--------Released version 1.7------- +- +-June 3 2002 +-Ignore undefined bits of "flash used" tag, as cannon sets them nonzero, causing +-jhead to indicate flash used when it wasn't with some Canon models. +- +-Jul 7 2002 +--------Released version 1.8------- +- +-Sept 8 2002 +-makehtml now also lists .avi files as video clips +- +-Sept 11 2002 +-Handle first IFD offset != 8, as comes out of Pentax Optio 230 camera. +- +-Sept 13 2002 +-Show 4 digits of past decimal if exposrure time is less than 0.01 sec. +- +-Sept 25 2002 +-Integrate patch from James R Van Zandt to allow inclusion of original name +-when '%f' is part of rename format string. +- +-Dec 11 2002 +--------Released version 1.9------- +- +-Oct 8 2002 +-Minor changes where newlines are printed. +-Added check to warn about wiping out the originals with the -st option. +- +-Oct 9 2002 +-Fixed display of "flash used=no" for exif headers generated by photoshop. +- +-Oct 13 2002 +-Added -ci and -cs options. +- +-March 3 2003 +-Limit directory recursion depth to avoid crashing on circularly linked +-directories within the Exif header. +- +-April 6 2003 +-Added automatic rottion (-autorotate) to right-up images that contain +-a rotation tag from the camera. +- +-*Finally* wrote a nice MAN page for jhead. +- +--------Released version 2.0 -- April 2003 ------- +- +-Dec ?? 2003 +-Set all copies of the date to same value when setting or modifying. +-(I intentionally only set one, but too may people considered this a bug) +- +-Dec 28 2003 +-fixed unix makefile +- +-Dec 29 2003 +-added -cl (insert comment literal) option +- +-Jan 8 2004 +-Added -norot (zero out rotation tag) option +- +--------Released version 2.1 -- Jan 2004 ------- +- +-Jan 12 +-Added handling of explicit 35mm equivalent tag +-fixed inconsistency in computing 35 mm equivalent focal lengths between +-concise and regular output. +- +-Jan 17 +-Impelemented optoin to supress file date display, for regression tests. +- +-Feb 1 +-Better indentatin of verbose option, rudementary canon maker not parsing +- +-March +-Various spelling errors fixed in output strings, and +-jpeg --> JPEG, exif --> Exif +- +-April 13 +-Use '-outfile' command line option of jpegtran when launching jpegtran to +-do rotation so that syntax of launched command is same on Windows and Unix. +- +-April 19 +-Various spelling fixes in manapge. +- +-Jun 20 +-Added ability to do sequencial renaming ('%i' in format string for -n option) +- +--------Released version 2.2 -- Jun 2004 ---------- +- +-Handle some oddities - like '/' separators in date fields, or images with +-an orientation tag for the thumbnail. +- +-Increase maximum number of jpeg sections to 40 from 20 +- +-Dec 3 +-Don't try to write to readonly files. +-Use changed copy of date/time for rename and file time set operations +- +-Dec 25 +-Added -purejpg and -du options +- +-Dec 28 +-More details on flash usage. +-Show digital zoom ratio +-Don't show jpeg process if it's baseline (almost always is) +- +--------Released version 2.3 -- Jan 2005 ---------- +- +-Jan 14 2005 +-Display GPS info if included in image +- +-Feb 27 2004 +-Fix some time reference confusion bugs relating to -ta option +- +-May 29 2005 +-Added -da option for easier adjusting of date/time by large amounts. +- +--------Released version 2.4 -- May 2005 ---------- +- +-Jun 06 2005 +-Fix -da option +- +--------Released version 2.4-1 -- Jun 09 2005 -------- +- +-Jun 10 2005 +-Removed some debug printf I accidentally left in! +- +--------Released version 2.4-2 -- Jun 10 2005 -------- +- +-August 8 2005 +-Avoid duplicating exif header on some commands +- +-Sept 11 2005 +-Fix up return codes. +- +-Oct 8 2005 +-Preserve file permissions and time when files are modified. +- +-Oct 29 2005 +-Read ISO euqivalnt and white balance from canon makernote +- +-Nov 5 2005 +-Added -rt (replace thumbnail) feature, and rotate the thumbnail also +-when using the -autorot feature +- +-Dec 28 2005 +-Added -rgt (regenerate thumbnail) feature. +-Added -orp and -orl options +- +--------Released version 2.5 -- Jan 8 2006 -------- +-Jan 28 2006 +-Fix typecast issue run itno with GCC 4 +- +-Feb 17 2006 +-Fix shutter speed display in '-c' mode for very long shutter speeds +- +-Feb 26 2006 +-Fix some nitpicks from Debian folks +- +-Mar 6 2006 +-Fix a bug in autorot when rotating filenames with spaces in them. +- +-April 2 2006 +-Improved handling of corrupt exif linkages in exif header +- +-April 3 2006 +-Added -a (rename associated files) options +- +--------Released version 2.6 -- April 29 2006 -------- +- +-Sept 9 2006 +-Remove maximum jpeg sections limit +- +-Sept 10 2006 +-Added -ds option +- +-Oct 18 2006 +-On clearing rotation, clear the image and the optinoal thumbnail rotation tags. +-(some viewers use the wrong tag) +- +-Dec 29 2006 +-Add -mkexif option to make a new exif header. +- +--------Released version 2.7 -- Jan 11 2007 -------- +- +-Feb 10 2007 +-Added IPTC handling +- +-Feb 11 2007 +-Added -q option +- +-Feb 17 2007 +-Fix handling of corrupted GPS directory. +- +-Feb 18 2007 +-Extract focus distance from canon makernote. +- +-Jun 3 2007 +-Extract subject range (pentax and fuji cameras) +- +--------Released version 2.8 -- Nov 13 2007 -------- +- +-Feb 14 2008 +-Fix it so it no longer deletex XMP sections +-Improve IPTC handling a little +- +-March 3 2008 +-Change how date is encoded with -mkexif section to make it more compatible. +-Make jhead aware of XMP data and not delete it. +- +--------Released version 2.82 -- Apr 03 2008 -------- +- +-May 8 2008 +-Decode more exif tags for '-v' mode. +- +-Sep 23 2008 +-Fix a bunch of potential string overflows +- +-Oct 1 2008 +-Fix bug where IPTC sction was not deleted by -purejpg +-Fix GPS altitude decode bug +- +--------Released version 2.84 -- Oct 4 2008 -------- +- +-Jan 15 2008 +-Fix bug with -ce introduced as a result of putting all the security +-checks the debian people wanted. +- +-Feb 2 2008 +-Added the ability to move files with the -n option. (makes directories if necessary) +- +-Various minor typo and documentation fixes. +- +--------Released version 2.86 -- Feb 14 2009 -------- +- +-Fixed an #ifdef that I had defined the wrong way, causing the -ce option to fail. +- +--------Released version 2.87 -- Mar 03 2009 -------- +- +-May 2009: +-A few more tags recognized with jhead -v +-Accept strange date encoding of LG VX-9700 +-Fix metering mode display +-Fix crash bug on corrupted jpeg +-Deal better with extra padding bytes between jpeg markers +- +-Nov 3 2009: +-Now preserve resoltuion units of jfif header, or set them with info from exif header. +- +--------Released version 2.88 -- Nov 6 2009 -------- +- +-Dec 16 2009: +-Handle slightly different signature in IPTC as well. +- +-Jan 25 2010: +-Handle mixted-endian-ness files from newer Canon point ant shoot cameras. +- +-Jan 26 2010: +-More handling of IPTC variants. +- +--------Released version 2.90 -- Feb 5 2010 -------- +-May 7 2010: +-Fix a compiler warning +- +-Dec 29 2010: +-Make -n behave like -nf, and get rid of that option. +- +-Dec 2 2011: +-Handle IPTC in UTF8 (as per photoshop CS5) +- +--------Released version 2.93 -- Dec 3 2011 -------- +- +-Jan 24 2011: +-Fixed bug in jhead -cmd that caused metatdata to be deleted. +- +--------Released version 2.94 -- Jan 24 2012 -------- +- +-Mar 3 2011: +-Handle very large unsigned rational numbers in exif header +- +--------Released version 2.95 -- Mar 16 2012 -------- +- +-Jun 18 2012: +-Fix printing file info when -ft option is used +-Do not skip readonle files with -st option +- +--------Released version 2.96 -- Jun 22 2012 -------- +- +-Jul 9 2012: +-Make it compile clean with visual studio 10 +- +-Jul 28 2012: +-Various cleanups from debian folks. +- +-Oct 19 2012: +-Add feature to show quality of jpeg, (by Andy Spiegel) +- +-Dec 27 2012: +-Fix crash on some corrupt files bug, clarify time adjustment syntax in help +- +- +--------Released version 2.97 -- Jan 30 2013 -------- +-Jun 10 2013: +-Make max comment size 16000 +- +-Oct 25 2013: +-Added "-zt" option to trim 32k of trailing zeroes from Nikon 1 J2 and J3 images. +- +-Sep 28 2014: +-Add ability to reset invalid rotation tag (from Moultrie game cameras) +- +--------Released version 3.0 -- Feb 2 2015 -------- +-Ma4 5 2015: +-Add option to set exif date from date from another file. +- +-Jul 28 2015: +-Remove some unnecessary warnings with some types of GPS data +- +-Aug 4 2015: +-Remove multiple copies of the same type of section when deleting section types +- +-Aug 11 2011: +-Bug fixes relating to fuzz testing. +- +-Aug 1 2017: +-Fix bug when no orientation tag is present +- +-Aug 12 2018: +-Fix bug of not clearing exif information when processing images with an without +-exif data in one invocation. +- +--------Released version 3.02 -- Dec 11 2018 -------- +- +-Dec 21 2018: +-Fix bug where thumbnail replacement DID NOT WORK. +-(broke while fixing compiler warnings for 3.02 release) +- +--------Released version 3.03 -- Dec 31 2018 -------- +- +-Nov 20 2019: +-Apply a whole bunch of patches from Debian. +-Spell check and fuzz test stuff from Debian, nothing useful to human users. +- +--------Released version 3.04 -- Nov 22 2019 -------- +diff --git a/src/external/jhead-3.04/exif.c b/src/external/jhead-3.04/exif.c +deleted file mode 100644 +index 124db4d..0000000 +--- a/src/external/jhead-3.04/exif.c ++++ /dev/null +@@ -1,1604 +0,0 @@ +-//-------------------------------------------------------------------------- +-// Program to pull the information out of various types of EXIF digital +-// camera files and show it in a reasonably consistent way +-// +-// This module parses the very complicated exif structures. +-// +-// Matthias Wandel +-//-------------------------------------------------------------------------- +-#include "jhead.h" +- +-#include +- +-static unsigned char * DirWithThumbnailPtrs; +-static double FocalplaneXRes; +-static double FocalplaneUnits; +-static int ExifImageWidth; +-int MotorolaOrder = 0; +- +-// for fixing the rotation. +-static void * OrientationPtr[2]; +-static int OrientationNumFormat[2]; +-int NumOrientations = 0; +- +-typedef struct { +- unsigned short Tag; +- char * Desc; +-}TagTable_t; +- +- +-//-------------------------------------------------------------------------- +-// Table of Jpeg encoding process names +-static const TagTable_t ProcessTable[] = { +- { M_SOF0, "Baseline"}, +- { M_SOF1, "Extended sequential"}, +- { M_SOF2, "Progressive"}, +- { M_SOF3, "Lossless"}, +- { M_SOF5, "Differential sequential"}, +- { M_SOF6, "Differential progressive"}, +- { M_SOF7, "Differential lossless"}, +- { M_SOF9, "Extended sequential, arithmetic coding"}, +- { M_SOF10, "Progressive, arithmetic coding"}, +- { M_SOF11, "Lossless, arithmetic coding"}, +- { M_SOF13, "Differential sequential, arithmetic coding"}, +- { M_SOF14, "Differential progressive, arithmetic coding"}, +- { M_SOF15, "Differential lossless, arithmetic coding"}, +-}; +- +-#define PROCESS_TABLE_SIZE (sizeof(ProcessTable) / sizeof(TagTable_t)) +- +-// 1 - "The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side." +-// 2 - "The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side." +-// 3 - "The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side." +-// 4 - "The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side." +- +-// 5 - "The 0th row is the visual left-hand side of of the image, and the 0th column is the visual top." +-// 6 - "The 0th row is the visual right-hand side of of the image, and the 0th column is the visual top." +-// 7 - "The 0th row is the visual right-hand side of of the image, and the 0th column is the visual bottom." +-// 8 - "The 0th row is the visual left-hand side of of the image, and the 0th column is the visual bottom." +- +-// Note: The descriptions here are the same as the name of the command line +-// option to pass to jpegtran to right the image +- +-static const char * OrientTab[9] = { +- "Undefined", +- "Normal", // 1 +- "flip horizontal", // left right reversed mirror +- "rotate 180", // 3 +- "flip vertical", // upside down mirror +- "transpose", // Flipped about top-left <--> bottom-right axis. +- "rotate 90", // rotate 90 cw to right it. +- "transverse", // flipped about top-right <--> bottom-left axis +- "rotate 270", // rotate 270 to right it. +-}; +- +-const int BytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8}; +- +-//-------------------------------------------------------------------------- +-// Describes tag values +- +-#define TAG_INTEROP_INDEX 0x0001 +-#define TAG_INTEROP_VERSION 0x0002 +-#define TAG_IMAGE_WIDTH 0x0100 +-#define TAG_IMAGE_LENGTH 0x0101 +-#define TAG_BITS_PER_SAMPLE 0x0102 +-#define TAG_COMPRESSION 0x0103 +-#define TAG_PHOTOMETRIC_INTERP 0x0106 +-#define TAG_FILL_ORDER 0x010A +-#define TAG_DOCUMENT_NAME 0x010D +-#define TAG_IMAGE_DESCRIPTION 0x010E +-#define TAG_MAKE 0x010F +-#define TAG_MODEL 0x0110 +-#define TAG_SRIP_OFFSET 0x0111 +-#define TAG_ORIENTATION 0x0112 +-#define TAG_SAMPLES_PER_PIXEL 0x0115 +-#define TAG_ROWS_PER_STRIP 0x0116 +-#define TAG_STRIP_BYTE_COUNTS 0x0117 +-#define TAG_X_RESOLUTION 0x011A +-#define TAG_Y_RESOLUTION 0x011B +-#define TAG_PLANAR_CONFIGURATION 0x011C +-#define TAG_RESOLUTION_UNIT 0x0128 +-#define TAG_TRANSFER_FUNCTION 0x012D +-#define TAG_SOFTWARE 0x0131 +-#define TAG_DATETIME 0x0132 +-#define TAG_ARTIST 0x013B +-#define TAG_WHITE_POINT 0x013E +-#define TAG_PRIMARY_CHROMATICITIES 0x013F +-#define TAG_TRANSFER_RANGE 0x0156 +-#define TAG_JPEG_PROC 0x0200 +-#define TAG_THUMBNAIL_OFFSET 0x0201 +-#define TAG_THUMBNAIL_LENGTH 0x0202 +-#define TAG_Y_CB_CR_COEFFICIENTS 0x0211 +-#define TAG_Y_CB_CR_SUB_SAMPLING 0x0212 +-#define TAG_Y_CB_CR_POSITIONING 0x0213 +-#define TAG_REFERENCE_BLACK_WHITE 0x0214 +-#define TAG_RELATED_IMAGE_WIDTH 0x1001 +-#define TAG_RELATED_IMAGE_LENGTH 0x1002 +-#define TAG_CFA_REPEAT_PATTERN_DIM 0x828D +-#define TAG_CFA_PATTERN1 0x828E +-#define TAG_BATTERY_LEVEL 0x828F +-#define TAG_COPYRIGHT 0x8298 +-#define TAG_EXPOSURETIME 0x829A +-#define TAG_FNUMBER 0x829D +-#define TAG_IPTC_NAA 0x83BB +-#define TAG_EXIF_OFFSET 0x8769 +-#define TAG_INTER_COLOR_PROFILE 0x8773 +-#define TAG_EXPOSURE_PROGRAM 0x8822 +-#define TAG_SPECTRAL_SENSITIVITY 0x8824 +-#define TAG_GPSINFO 0x8825 +-#define TAG_ISO_EQUIVALENT 0x8827 +-#define TAG_OECF 0x8828 +-#define TAG_EXIF_VERSION 0x9000 +-#define TAG_DATETIME_ORIGINAL 0x9003 +-#define TAG_DATETIME_DIGITIZED 0x9004 +-#define TAG_COMPONENTS_CONFIG 0x9101 +-#define TAG_CPRS_BITS_PER_PIXEL 0x9102 +-#define TAG_SHUTTERSPEED 0x9201 +-#define TAG_APERTURE 0x9202 +-#define TAG_BRIGHTNESS_VALUE 0x9203 +-#define TAG_EXPOSURE_BIAS 0x9204 +-#define TAG_MAXAPERTURE 0x9205 +-#define TAG_SUBJECT_DISTANCE 0x9206 +-#define TAG_METERING_MODE 0x9207 +-#define TAG_LIGHT_SOURCE 0x9208 +-#define TAG_FLASH 0x9209 +-#define TAG_FOCALLENGTH 0x920A +-#define TAG_SUBJECTAREA 0x9214 +-#define TAG_MAKER_NOTE 0x927C +-#define TAG_USERCOMMENT 0x9286 +-#define TAG_SUBSEC_TIME 0x9290 +-#define TAG_SUBSEC_TIME_ORIG 0x9291 +-#define TAG_SUBSEC_TIME_DIG 0x9292 +- +-#define TAG_WINXP_TITLE 0x9c9b // Windows XP - not part of exif standard. +-#define TAG_WINXP_COMMENT 0x9c9c // Windows XP - not part of exif standard. +-#define TAG_WINXP_AUTHOR 0x9c9d // Windows XP - not part of exif standard. +-#define TAG_WINXP_KEYWORDS 0x9c9e // Windows XP - not part of exif standard. +-#define TAG_WINXP_SUBJECT 0x9c9f // Windows XP - not part of exif standard. +- +-#define TAG_FLASH_PIX_VERSION 0xA000 +-#define TAG_COLOR_SPACE 0xA001 +-#define TAG_PIXEL_X_DIMENSION 0xA002 +-#define TAG_PIXEL_Y_DIMENSION 0xA003 +-#define TAG_RELATED_AUDIO_FILE 0xA004 +-#define TAG_INTEROP_OFFSET 0xA005 +-#define TAG_FLASH_ENERGY 0xA20B +-#define TAG_SPATIAL_FREQ_RESP 0xA20C +-#define TAG_FOCAL_PLANE_XRES 0xA20E +-#define TAG_FOCAL_PLANE_YRES 0xA20F +-#define TAG_FOCAL_PLANE_UNITS 0xA210 +-#define TAG_SUBJECT_LOCATION 0xA214 +-#define TAG_EXPOSURE_INDEX 0xA215 +-#define TAG_SENSING_METHOD 0xA217 +-#define TAG_FILE_SOURCE 0xA300 +-#define TAG_SCENE_TYPE 0xA301 +-#define TAG_CFA_PATTERN 0xA302 +-#define TAG_CUSTOM_RENDERED 0xA401 +-#define TAG_EXPOSURE_MODE 0xA402 +-#define TAG_WHITEBALANCE 0xA403 +-#define TAG_DIGITALZOOMRATIO 0xA404 +-#define TAG_FOCALLENGTH_35MM 0xA405 +-#define TAG_SCENE_CAPTURE_TYPE 0xA406 +-#define TAG_GAIN_CONTROL 0xA407 +-#define TAG_CONTRAST 0xA408 +-#define TAG_SATURATION 0xA409 +-#define TAG_SHARPNESS 0xA40A +-#define TAG_DISTANCE_RANGE 0xA40C +-#define TAG_IMAGE_UNIQUE_ID 0xA420 +- +-static const TagTable_t TagTable[] = { +- { TAG_INTEROP_INDEX, "InteropIndex"}, +- { TAG_INTEROP_VERSION, "InteropVersion"}, +- { TAG_IMAGE_WIDTH, "ImageWidth"}, +- { TAG_IMAGE_LENGTH, "ImageLength"}, +- { TAG_BITS_PER_SAMPLE, "BitsPerSample"}, +- { TAG_COMPRESSION, "Compression"}, +- { TAG_PHOTOMETRIC_INTERP, "PhotometricInterpretation"}, +- { TAG_FILL_ORDER, "FillOrder"}, +- { TAG_DOCUMENT_NAME, "DocumentName"}, +- { TAG_IMAGE_DESCRIPTION, "ImageDescription"}, +- { TAG_MAKE, "Make"}, +- { TAG_MODEL, "Model"}, +- { TAG_SRIP_OFFSET, "StripOffsets"}, +- { TAG_ORIENTATION, "Orientation"}, +- { TAG_SAMPLES_PER_PIXEL, "SamplesPerPixel"}, +- { TAG_ROWS_PER_STRIP, "RowsPerStrip"}, +- { TAG_STRIP_BYTE_COUNTS, "StripByteCounts"}, +- { TAG_X_RESOLUTION, "XResolution"}, +- { TAG_Y_RESOLUTION, "YResolution"}, +- { TAG_PLANAR_CONFIGURATION, "PlanarConfiguration"}, +- { TAG_RESOLUTION_UNIT, "ResolutionUnit"}, +- { TAG_TRANSFER_FUNCTION, "TransferFunction"}, +- { TAG_SOFTWARE, "Software"}, +- { TAG_DATETIME, "DateTime"}, +- { TAG_ARTIST, "Artist"}, +- { TAG_WHITE_POINT, "WhitePoint"}, +- { TAG_PRIMARY_CHROMATICITIES, "PrimaryChromaticities"}, +- { TAG_TRANSFER_RANGE, "TransferRange"}, +- { TAG_JPEG_PROC, "JPEGProc"}, +- { TAG_THUMBNAIL_OFFSET, "ThumbnailOffset"}, +- { TAG_THUMBNAIL_LENGTH, "ThumbnailLength"}, +- { TAG_Y_CB_CR_COEFFICIENTS, "YCbCrCoefficients"}, +- { TAG_Y_CB_CR_SUB_SAMPLING, "YCbCrSubSampling"}, +- { TAG_Y_CB_CR_POSITIONING, "YCbCrPositioning"}, +- { TAG_REFERENCE_BLACK_WHITE, "ReferenceBlackWhite"}, +- { TAG_RELATED_IMAGE_WIDTH, "RelatedImageWidth"}, +- { TAG_RELATED_IMAGE_LENGTH, "RelatedImageLength"}, +- { TAG_CFA_REPEAT_PATTERN_DIM, "CFARepeatPatternDim"}, +- { TAG_CFA_PATTERN1, "CFAPattern"}, +- { TAG_BATTERY_LEVEL, "BatteryLevel"}, +- { TAG_COPYRIGHT, "Copyright"}, +- { TAG_EXPOSURETIME, "ExposureTime"}, +- { TAG_FNUMBER, "FNumber"}, +- { TAG_IPTC_NAA, "IPTC/NAA"}, +- { TAG_EXIF_OFFSET, "ExifOffset"}, +- { TAG_INTER_COLOR_PROFILE, "InterColorProfile"}, +- { TAG_EXPOSURE_PROGRAM, "ExposureProgram"}, +- { TAG_SPECTRAL_SENSITIVITY, "SpectralSensitivity"}, +- { TAG_GPSINFO, "GPS Dir offset"}, +- { TAG_ISO_EQUIVALENT, "ISOSpeedRatings"}, +- { TAG_OECF, "OECF"}, +- { TAG_EXIF_VERSION, "ExifVersion"}, +- { TAG_DATETIME_ORIGINAL, "DateTimeOriginal"}, +- { TAG_DATETIME_DIGITIZED, "DateTimeDigitized"}, +- { TAG_COMPONENTS_CONFIG, "ComponentsConfiguration"}, +- { TAG_CPRS_BITS_PER_PIXEL, "CompressedBitsPerPixel"}, +- { TAG_SHUTTERSPEED, "ShutterSpeedValue"}, +- { TAG_APERTURE, "ApertureValue"}, +- { TAG_BRIGHTNESS_VALUE, "BrightnessValue"}, +- { TAG_EXPOSURE_BIAS, "ExposureBiasValue"}, +- { TAG_MAXAPERTURE, "MaxApertureValue"}, +- { TAG_SUBJECT_DISTANCE, "SubjectDistance"}, +- { TAG_METERING_MODE, "MeteringMode"}, +- { TAG_LIGHT_SOURCE, "LightSource"}, +- { TAG_FLASH, "Flash"}, +- { TAG_FOCALLENGTH, "FocalLength"}, +- { TAG_MAKER_NOTE, "MakerNote"}, +- { TAG_USERCOMMENT, "UserComment"}, +- { TAG_SUBSEC_TIME, "SubSecTime"}, +- { TAG_SUBSEC_TIME_ORIG, "SubSecTimeOriginal"}, +- { TAG_SUBSEC_TIME_DIG, "SubSecTimeDigitized"}, +- { TAG_WINXP_TITLE, "Windows-XP Title"}, +- { TAG_WINXP_COMMENT, "Windows-XP comment"}, +- { TAG_WINXP_AUTHOR, "Windows-XP author"}, +- { TAG_WINXP_KEYWORDS, "Windows-XP keywords"}, +- { TAG_WINXP_SUBJECT, "Windows-XP subject"}, +- { TAG_FLASH_PIX_VERSION, "FlashPixVersion"}, +- { TAG_COLOR_SPACE, "ColorSpace"}, +- { TAG_PIXEL_X_DIMENSION, "ExifImageWidth"}, +- { TAG_PIXEL_Y_DIMENSION, "ExifImageLength"}, +- { TAG_RELATED_AUDIO_FILE, "RelatedAudioFile"}, +- { TAG_INTEROP_OFFSET, "InteroperabilityOffset"}, +- { TAG_FLASH_ENERGY, "FlashEnergy"}, +- { TAG_SPATIAL_FREQ_RESP, "SpatialFrequencyResponse"}, +- { TAG_FOCAL_PLANE_XRES, "FocalPlaneXResolution"}, +- { TAG_FOCAL_PLANE_YRES, "FocalPlaneYResolution"}, +- { TAG_FOCAL_PLANE_UNITS, "FocalPlaneResolutionUnit"}, +- { TAG_SUBJECT_LOCATION, "SubjectLocation"}, +- { TAG_EXPOSURE_INDEX, "ExposureIndex"}, +- { TAG_SENSING_METHOD, "SensingMethod"}, +- { TAG_FILE_SOURCE, "FileSource"}, +- { TAG_SCENE_TYPE, "SceneType"}, +- { TAG_CFA_PATTERN, "CFA Pattern"}, +- { TAG_CUSTOM_RENDERED, "CustomRendered"}, +- { TAG_EXPOSURE_MODE, "ExposureMode"}, +- { TAG_WHITEBALANCE, "WhiteBalance"}, +- { TAG_DIGITALZOOMRATIO, "DigitalZoomRatio"}, +- { TAG_FOCALLENGTH_35MM, "FocalLengthIn35mmFilm"}, +- { TAG_SUBJECTAREA, "SubjectArea"}, +- { TAG_SCENE_CAPTURE_TYPE, "SceneCaptureType"}, +- { TAG_GAIN_CONTROL, "GainControl"}, +- { TAG_CONTRAST, "Contrast"}, +- { TAG_SATURATION, "Saturation"}, +- { TAG_SHARPNESS, "Sharpness"}, +- { TAG_DISTANCE_RANGE, "SubjectDistanceRange"}, +- { TAG_IMAGE_UNIQUE_ID, "ImageUniqueId"}, +-} ; +- +-#define TAG_TABLE_SIZE (sizeof(TagTable) / sizeof(TagTable_t)) +- +- +-//-------------------------------------------------------------------------- +-// Convert a 16 bit unsigned value to file's native byte order +-//-------------------------------------------------------------------------- +-static void Put16u(void * Short, unsigned short PutValue) +-{ +- if (MotorolaOrder){ +- ((uchar *)Short)[0] = (uchar)(PutValue>>8); +- ((uchar *)Short)[1] = (uchar)PutValue; +- }else{ +- ((uchar *)Short)[0] = (uchar)PutValue; +- ((uchar *)Short)[1] = (uchar)(PutValue>>8); +- } +-} +- +-//-------------------------------------------------------------------------- +-// Convert a 16 bit unsigned value from file's native byte order +-//-------------------------------------------------------------------------- +-int Get16u(void * Short) +-{ +- if (MotorolaOrder){ +- return (((uchar *)Short)[0] << 8) | ((uchar *)Short)[1]; +- }else{ +- return (((uchar *)Short)[1] << 8) | ((uchar *)Short)[0]; +- } +-} +- +-//-------------------------------------------------------------------------- +-// Convert a 32 bit signed value from file's native byte order +-//-------------------------------------------------------------------------- +-int Get32s(void * Long) +-{ +- if (MotorolaOrder){ +- return ((( char *)Long)[0] << 24) | (((uchar *)Long)[1] << 16) +- | (((uchar *)Long)[2] << 8 ) | (((uchar *)Long)[3] << 0 ); +- }else{ +- return ((( char *)Long)[3] << 24) | (((uchar *)Long)[2] << 16) +- | (((uchar *)Long)[1] << 8 ) | (((uchar *)Long)[0] << 0 ); +- } +-} +- +-//-------------------------------------------------------------------------- +-// Convert a 32 bit unsigned value to file's native byte order +-//-------------------------------------------------------------------------- +-void Put32u(void * Value, unsigned PutValue) +-{ +- if (MotorolaOrder){ +- ((uchar *)Value)[0] = (uchar)(PutValue>>24); +- ((uchar *)Value)[1] = (uchar)(PutValue>>16); +- ((uchar *)Value)[2] = (uchar)(PutValue>>8); +- ((uchar *)Value)[3] = (uchar)PutValue; +- }else{ +- ((uchar *)Value)[0] = (uchar)PutValue; +- ((uchar *)Value)[1] = (uchar)(PutValue>>8); +- ((uchar *)Value)[2] = (uchar)(PutValue>>16); +- ((uchar *)Value)[3] = (uchar)(PutValue>>24); +- } +-} +- +-//-------------------------------------------------------------------------- +-// Convert a 32 bit unsigned value from file's native byte order +-//-------------------------------------------------------------------------- +-unsigned Get32u(void * Long) +-{ +- return (unsigned)Get32s(Long) & 0xffffffff; +-} +- +-//-------------------------------------------------------------------------- +-// Display a number as one of its many formats +-//-------------------------------------------------------------------------- +-void PrintFormatNumber(void * ValuePtr, int Format, int ByteCount) +-{ +- int s,n; +- +- for(n=0;n<16;n++){ +- switch(Format){ +- case FMT_SBYTE: +- case FMT_BYTE: printf("%02x",*(uchar *)ValuePtr); s=1; break; +- case FMT_USHORT: printf("%d",Get16u(ValuePtr)); s=2; break; +- case FMT_ULONG: +- case FMT_SLONG: printf("%d",Get32s(ValuePtr)); s=4; break; +- case FMT_SSHORT: printf("%hd",(signed short)Get16u(ValuePtr)); s=2; break; +- case FMT_URATIONAL: +- printf("%u/%u",Get32s(ValuePtr), Get32s(4+(char *)ValuePtr)); +- s = 8; +- break; +- +- case FMT_SRATIONAL: +- printf("%d/%d",Get32s(ValuePtr), Get32s(4+(char *)ValuePtr)); +- s = 8; +- break; +- +- case FMT_SINGLE: printf("%f",(double)*(float *)ValuePtr); s=8; break; +- case FMT_DOUBLE: printf("%f",*(double *)ValuePtr); s=8; break; +- default: +- printf("Unknown format %d:", Format); +- return; +- } +- ByteCount -= s; +- if (ByteCount <= 0) break; +- printf(", "); +- ValuePtr = (void *)((char *)ValuePtr + s); +- +- } +- if (n >= 16) printf("..."); +-} +- +- +-//-------------------------------------------------------------------------- +-// Evaluate number, be it int, rational, or float from directory. +-//-------------------------------------------------------------------------- +-double ConvertAnyFormat(void * ValuePtr, int Format) +-{ +- double Value; +- Value = 0; +- +- switch(Format){ +- case FMT_SBYTE: Value = *(signed char *)ValuePtr; break; +- case FMT_BYTE: Value = *(uchar *)ValuePtr; break; +- +- case FMT_USHORT: Value = Get16u(ValuePtr); break; +- case FMT_ULONG: Value = Get32u(ValuePtr); break; +- +- case FMT_URATIONAL: +- case FMT_SRATIONAL: +- { +- int Num,Den; +- Num = Get32s(ValuePtr); +- Den = Get32s(4+(char *)ValuePtr); +- if (Den == 0){ +- Value = 0; +- }else{ +- if (Format == FMT_SRATIONAL){ +- Value = (double)Num/Den; +- }else{ +- Value = (double)(unsigned)Num/(double)(unsigned)Den; +- } +- } +- break; +- } +- +- case FMT_SSHORT: Value = (signed short)Get16u(ValuePtr); break; +- case FMT_SLONG: Value = Get32s(ValuePtr); break; +- +- // Not sure if this is correct (never seen float used in Exif format) +- case FMT_SINGLE: Value = (double)*(float *)ValuePtr; break; +- case FMT_DOUBLE: Value = *(double *)ValuePtr; break; +- +- default: +- ErrNonfatal("Illegal format code %d in Exif header",Format,0); +- } +- return Value; +-} +- +-//-------------------------------------------------------------------------- +-// Process one of the nested EXIF directories. +-//-------------------------------------------------------------------------- +-static void ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, +- int ExifLength, int NestingLevel) +-{ +- int de; +- int a; +- int NumDirEntries; +- int ThumbnailOffset = 0; +- int ThumbnailSize = 0; +- char IndentString[25]; +- +- if (NestingLevel > 4){ +- ErrNonfatal("Maximum Exif directory nesting exceeded (corrupt Exif header)", 0,0); +- return; +- } +- +- memset(IndentString, ' ', 25); +- IndentString[NestingLevel * 4] = '\0'; +- +- +- NumDirEntries = Get16u(DirStart); +- #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry)) +- +- { +- unsigned char * DirEnd; +- DirEnd = DIR_ENTRY_ADDR(DirStart, NumDirEntries); +- if (DirEnd+4 > (OffsetBase+ExifLength)){ +- if (DirEnd+2 == OffsetBase+ExifLength || DirEnd == OffsetBase+ExifLength){ +- // Version 1.3 of jhead would truncate a bit too much. +- // This also caught later on as well. +- }else{ +- ErrNonfatal("Illegally sized Exif subdirectory (%d entries)",NumDirEntries,0); +- return; +- } +- } +- if (DumpExifMap){ +- printf("Map: %05u-%05u: Directory\n",(int)(DirStart-OffsetBase), (int)(DirEnd+4-OffsetBase)); +- } +- +- +- } +- +- if (ShowTags){ +- printf("(dir has %d entries)\n",NumDirEntries); +- } +- +- for (de=0;de= NUM_FORMATS) { +- // (-1) catches illegal zero case as unsigned underflows to positive large. +- ErrNonfatal("Illegal number format %d for tag %04x in Exif", Format, Tag); +- continue; +- } +- +- if ((unsigned)Components > 0x10000){ +- ErrNonfatal("Too many components %d for tag %04x in Exif", Components, Tag); +- continue; +- } +- +- ByteCount = Components * BytesPerFormat[Format]; +- +- if (ByteCount > 4){ +- int OffsetVal; +- OffsetVal = Get32u(DirEntry+8); +- // If its bigger than 4 bytes, the dir entry contains an offset. +- if (OffsetVal+ByteCount > ExifLength || OffsetVal < 0 || OffsetVal > 65536){ +- // Bogus pointer offset and / or bytecount value +- ErrNonfatal("Illegal value pointer for tag %04x in Exif", Tag,0); +- continue; +- } +- ValuePtr = OffsetBase+OffsetVal; +- +- if (OffsetVal > ImageInfo.LargestExifOffset){ +- ImageInfo.LargestExifOffset = OffsetVal; +- } +- +- if (DumpExifMap){ +- printf("Map: %05u-%05u: Data for tag %04x\n",OffsetVal, OffsetVal+ByteCount, Tag); +- } +- }else{ +- // 4 bytes or less and value is in the dir entry itself +- ValuePtr = DirEntry+8; +- } +- +- if (Tag == TAG_MAKER_NOTE){ +- if (ShowTags){ +- printf("%s Maker note: ",IndentString); +- } +- ProcessMakerNote(ValuePtr, ByteCount, OffsetBase, ExifLength); +- continue; +- } +- +- if (ShowTags){ +- // Show tag name +- for (a=0;;a++){ +- if (a >= TAG_TABLE_SIZE){ +- printf("%s Unknown Tag %04x Value = ", IndentString, Tag); +- break; +- } +- if (TagTable[a].Tag == Tag){ +- printf("%s %s = ",IndentString, TagTable[a].Desc); +- break; +- } +- } +- +- // Show tag value. +- switch(Format){ +- case FMT_BYTE: +- if(ByteCount>1){ +- printf("%.*ls\n", ByteCount/2, (wchar_t *)ValuePtr); +- }else{ +- PrintFormatNumber(ValuePtr, Format, ByteCount); +- printf("\n"); +- } +- break; +- +- case FMT_UNDEFINED: +- // Undefined is typically an ascii string. +- +- case FMT_STRING: +- // String arrays printed without function call (different from int arrays) +- { +- int NoPrint = 0; +- printf("\""); +- for (a=0;a= 32){ +- putchar(ValuePtr[a]); +- NoPrint = 0; +- }else{ +- // Avoiding indicating too many unprintable characters of proprietary +- // bits of binary information this program may not know how to parse. +- if (!NoPrint && a != ByteCount-1){ +- putchar('?'); +- NoPrint = 1; +- } +- } +- } +- printf("\"\n"); +- } +- break; +- +- default: +- // Handle arrays of numbers later (will there ever be?) +- PrintFormatNumber(ValuePtr, Format, ByteCount); +- printf("\n"); +- } +- } +- +- // Extract useful components of tag +- switch(Tag){ +- +- case TAG_MAKE: +- strncpy(ImageInfo.CameraMake, (char *)ValuePtr, ByteCount < 31 ? ByteCount : 31); +- break; +- +- case TAG_MODEL: +- strncpy(ImageInfo.CameraModel, (char *)ValuePtr, ByteCount < 39 ? ByteCount : 39); +- break; +- +- case TAG_DATETIME_ORIGINAL: +- // If we get a DATETIME_ORIGINAL, we use that one. +- strncpy(ImageInfo.DateTime, (char *)ValuePtr, 19); +- // Fallthru... +- +- case TAG_DATETIME_DIGITIZED: +- case TAG_DATETIME: +- if (!isdigit(ImageInfo.DateTime[0])){ +- // If we don't already have a DATETIME_ORIGINAL, use whatever +- // time fields we may have. +- strncpy(ImageInfo.DateTime, (char *)ValuePtr, 19); +- } +- +- if (ImageInfo.numDateTimeTags >= MAX_DATE_COPIES){ +- ErrNonfatal("More than %d date fields in Exif. This is nuts", MAX_DATE_COPIES, 0); +- break; +- } +- ImageInfo.DateTimeOffsets[ImageInfo.numDateTimeTags++] = +- (char *)ValuePtr - (char *)OffsetBase; +- break; +- +- case TAG_WINXP_COMMENT: +- if (ImageInfo.Comments[0]){ // We already have a jpeg comment. +- // Already have a comment (probably windows comment), skip this one. +- if (ShowTags) printf("Windows XP commend and other comment in header\n"); +- break; // Already have a windows comment, skip this one. +- } +- +- if (ByteCount > 1){ +- if (ByteCount > MAX_COMMENT_SIZE) ByteCount = MAX_COMMENT_SIZE; +- memcpy(ImageInfo.Comments, ValuePtr, ByteCount); +- ImageInfo.CommentWidthchars = ByteCount/2; +- } +- break; +- +- case TAG_USERCOMMENT: +- if (ImageInfo.Comments[0]){ // We already have a jpeg comment. +- // Already have a comment (probably windows comment), skip this one. +- if (ShowTags) printf("Multiple comments in exif header\n"); +- break; // Already have a windows comment, skip this one. +- } +- +- // Comment is often padded with trailing spaces. Remove these first. +- for (a=ByteCount;;){ +- a--; +- if ((ValuePtr)[a] == ' '){ +- (ValuePtr)[a] = '\0'; +- }else{ +- break; +- } +- if (a == 0) break; +- } +- +- // Copy the comment +- { +- int msiz = ExifLength - (ValuePtr-OffsetBase); +- if (msiz > ByteCount) msiz = ByteCount; +- if (msiz > MAX_COMMENT_SIZE-1) msiz = MAX_COMMENT_SIZE-1; +- if (msiz > 5 && memcmp(ValuePtr, "ASCII",5) == 0){ +- for (a=5;a<10 && a < msiz;a++){ +- int c = (ValuePtr)[a]; +- if (c != '\0' && c != ' '){ +- strncpy(ImageInfo.Comments, (char *)ValuePtr+a, msiz-a); +- break; +- } +- } +- }else{ +- strncpy(ImageInfo.Comments, (char *)ValuePtr, msiz); +- } +- } +- break; +- +- case TAG_FNUMBER: +- // Simplest way of expressing aperture, so I trust it the most. +- // (overwrite previously computd value if there is one) +- ImageInfo.ApertureFNumber = (float)ConvertAnyFormat(ValuePtr, Format); +- break; +- +- case TAG_APERTURE: +- case TAG_MAXAPERTURE: +- // More relevant info always comes earlier, so only use this field if we don't +- // have appropriate aperture information yet. +- if (ImageInfo.ApertureFNumber == 0){ +- ImageInfo.ApertureFNumber +- = (float)exp(ConvertAnyFormat(ValuePtr, Format)*log(2)*0.5); +- } +- break; +- +- case TAG_FOCALLENGTH: +- // Nice digital cameras actually save the focal length as a function +- // of how farthey are zoomed in. +- ImageInfo.FocalLength = (float)ConvertAnyFormat(ValuePtr, Format); +- break; +- +- case TAG_SUBJECT_DISTANCE: +- // Inidcates the distacne the autofocus camera is focused to. +- // Tends to be less accurate as distance increases. +- ImageInfo.Distance = (float)ConvertAnyFormat(ValuePtr, Format); +- break; +- +- case TAG_EXPOSURETIME: +- // Simplest way of expressing exposure time, so I trust it most. +- // (overwrite previously computd value if there is one) +- ImageInfo.ExposureTime = (float)ConvertAnyFormat(ValuePtr, Format); +- break; +- +- case TAG_SHUTTERSPEED: +- // More complicated way of expressing exposure time, so only use +- // this value if we don't already have it from somewhere else. +- if (ImageInfo.ExposureTime == 0){ +- ImageInfo.ExposureTime +- = (float)(1/exp(ConvertAnyFormat(ValuePtr, Format)*log(2))); +- } +- break; +- +- +- case TAG_FLASH: +- ImageInfo.FlashUsed=(int)ConvertAnyFormat(ValuePtr, Format); +- break; +- +- case TAG_ORIENTATION: +- if (NumOrientations >= 2){ +- // Can have another orientation tag for the thumbnail, but if there's +- // a third one, things are strange. +- ErrNonfatal("More than two orientation in Exif",0,0); +- break; +- } +- OrientationPtr[NumOrientations] = ValuePtr; +- OrientationNumFormat[NumOrientations] = Format; +- if (NumOrientations == 0){ +- ImageInfo.Orientation = (int)ConvertAnyFormat(ValuePtr, Format); +- } +- if (ImageInfo.Orientation < 0 || ImageInfo.Orientation > 8){ +- ErrNonfatal("Undefined rotation value %d in Exif", ImageInfo.Orientation, 0); +- } +- NumOrientations += 1; +- break; +- +- case TAG_PIXEL_Y_DIMENSION: +- case TAG_PIXEL_X_DIMENSION: +- // Use largest of height and width to deal with images that have been +- // rotated to portrait format. +- a = (int)ConvertAnyFormat(ValuePtr, Format); +- if (ExifImageWidth < a) ExifImageWidth = a; +- break; +- +- case TAG_FOCAL_PLANE_XRES: +- FocalplaneXRes = ConvertAnyFormat(ValuePtr, Format); +- break; +- +- case TAG_FOCAL_PLANE_UNITS: +- switch((int)ConvertAnyFormat(ValuePtr, Format)){ +- case 1: FocalplaneUnits = 25.4; break; // inch +- case 2: +- // According to the information I was using, 2 means meters. +- // But looking at the Cannon powershot's files, inches is the only +- // sensible value. +- FocalplaneUnits = 25.4; +- break; +- +- case 3: FocalplaneUnits = 10; break; // centimeter +- case 4: FocalplaneUnits = 1; break; // millimeter +- case 5: FocalplaneUnits = .001; break; // micrometer +- } +- break; +- +- case TAG_EXPOSURE_BIAS: +- ImageInfo.ExposureBias = (float)ConvertAnyFormat(ValuePtr, Format); +- break; +- +- case TAG_WHITEBALANCE: +- ImageInfo.Whitebalance = (int)ConvertAnyFormat(ValuePtr, Format); +- break; +- +- case TAG_LIGHT_SOURCE: +- ImageInfo.LightSource = (int)ConvertAnyFormat(ValuePtr, Format); +- break; +- +- case TAG_METERING_MODE: +- ImageInfo.MeteringMode = (int)ConvertAnyFormat(ValuePtr, Format); +- break; +- +- case TAG_EXPOSURE_PROGRAM: +- ImageInfo.ExposureProgram = (int)ConvertAnyFormat(ValuePtr, Format); +- break; +- +- case TAG_EXPOSURE_INDEX: +- if (ImageInfo.ISOequivalent == 0){ +- // Exposure index and ISO equivalent are often used interchangeably, +- // so we will do the same in jhead. +- // http://photography.about.com/library/glossary/bldef_ei.htm +- ImageInfo.ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format); +- } +- break; +- +- case TAG_EXPOSURE_MODE: +- ImageInfo.ExposureMode = (int)ConvertAnyFormat(ValuePtr, Format); +- break; +- +- case TAG_ISO_EQUIVALENT: +- ImageInfo.ISOequivalent = (int)ConvertAnyFormat(ValuePtr, Format); +- break; +- +- case TAG_DIGITALZOOMRATIO: +- ImageInfo.DigitalZoomRatio = (float)ConvertAnyFormat(ValuePtr, Format); +- break; +- +- case TAG_THUMBNAIL_OFFSET: +- ThumbnailOffset = (unsigned)ConvertAnyFormat(ValuePtr, Format); +- DirWithThumbnailPtrs = DirStart; +- break; +- +- case TAG_THUMBNAIL_LENGTH: +- ThumbnailSize = (unsigned)ConvertAnyFormat(ValuePtr, Format); +- ImageInfo.ThumbnailSizeOffset = ValuePtr-OffsetBase; +- break; +- +- case TAG_EXIF_OFFSET: +- if (ShowTags) printf("%s Exif Dir:",IndentString); +- +- case TAG_INTEROP_OFFSET: +- if (Tag == TAG_INTEROP_OFFSET && ShowTags) printf("%s Interop Dir:",IndentString); +- { +- unsigned char * SubdirStart; +- SubdirStart = OffsetBase + Get32u(ValuePtr); +- if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){ +- ErrNonfatal("Illegal Exif or interop ofset directory link",0,0); +- }else{ +- ProcessExifDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1); +- } +- continue; +- } +- break; +- +- case TAG_GPSINFO: +- if (ShowTags) printf("%s GPS info dir:",IndentString); +- { +- unsigned char * SubdirStart; +- SubdirStart = OffsetBase + Get32u(ValuePtr); +- if (SubdirStart < OffsetBase || SubdirStart > OffsetBase+ExifLength){ +- ErrNonfatal("Illegal GPS directory link in Exif",0,0); +- }else{ +- ProcessGpsInfo(SubdirStart, OffsetBase, ExifLength); +- } +- continue; +- } +- break; +- +- case TAG_FOCALLENGTH_35MM: +- // The focal length equivalent 35 mm is a 2.2 tag (defined as of April 2002) +- // if its present, use it to compute equivalent focal length instead of +- // computing it from sensor geometry and actual focal length. +- ImageInfo.FocalLength35mmEquiv = (unsigned)ConvertAnyFormat(ValuePtr, Format); +- break; +- +- case TAG_DISTANCE_RANGE: +- // Three possible standard values: +- // 1 = macro, 2 = close, 3 = distant +- ImageInfo.DistanceRange = (int)ConvertAnyFormat(ValuePtr, Format); +- break; +- +- +- +- case TAG_X_RESOLUTION: +- if (NestingLevel==0) {// Only use the values from the top level directory +- ImageInfo.xResolution = (float)ConvertAnyFormat(ValuePtr,Format); +- // if yResolution has not been set, use the value of xResolution +- if (ImageInfo.yResolution == 0.0) ImageInfo.yResolution = ImageInfo.xResolution; +- } +- break; +- +- case TAG_Y_RESOLUTION: +- if (NestingLevel==0) {// Only use the values from the top level directory +- ImageInfo.yResolution = (float)ConvertAnyFormat(ValuePtr,Format); +- // if xResolution has not been set, use the value of yResolution +- if (ImageInfo.xResolution == 0.0) ImageInfo.xResolution = ImageInfo.yResolution; +- } +- break; +- +- case TAG_RESOLUTION_UNIT: +- if (NestingLevel==0) {// Only use the values from the top level directory +- ImageInfo.ResolutionUnit = (int) ConvertAnyFormat(ValuePtr,Format); +- } +- break; +- +- } +- } +- +- +- { +- // In addition to linking to subdirectories via exif tags, +- // there's also a potential link to another directory at the end of each +- // directory. this has got to be the result of a committee! +- unsigned char * SubdirStart; +- int Offset; +- +- if (DIR_ENTRY_ADDR(DirStart, NumDirEntries) + 4 <= OffsetBase+ExifLength){ +- Offset = Get32u(DirStart+2+12*NumDirEntries); +- if (Offset){ +- SubdirStart = OffsetBase + Offset; +- if (SubdirStart > OffsetBase+ExifLength || SubdirStart < OffsetBase){ +- if (SubdirStart > OffsetBase && SubdirStart < OffsetBase+ExifLength+20){ +- // Jhead 1.3 or earlier would crop the whole directory! +- // As Jhead produces this form of format incorrectness, +- // I'll just let it pass silently +- if (ShowTags) printf("Thumbnail removed with Jhead 1.3 or earlier\n"); +- }else{ +- ErrNonfatal("Illegal subdirectory link in Exif header",0,0); +- } +- }else{ +- if (SubdirStart+2 <= OffsetBase+ExifLength){ +- if (ShowTags) printf("%s Continued directory ",IndentString); +- ProcessExifDir(SubdirStart, OffsetBase, ExifLength, NestingLevel+1); +- } +- } +- if (Offset > ImageInfo.LargestExifOffset){ +- ImageInfo.LargestExifOffset = Offset; +- } +- } +- }else{ +- // The exif header ends before the last next directory pointer. +- } +- } +- +- if (ThumbnailOffset){ +- ImageInfo.ThumbnailAtEnd = FALSE; +- +- if (DumpExifMap){ +- printf("Map: %05d-%05d: Thumbnail\n",ThumbnailOffset, ThumbnailOffset+ThumbnailSize); +- } +- +- if (ThumbnailOffset <= ExifLength){ +- if (ThumbnailSize > ExifLength-ThumbnailOffset){ +- // If thumbnail extends past exif header, only save the part that +- // actually exists. Canon's EOS viewer utility will do this - the +- // thumbnail extracts ok with this hack. +- ThumbnailSize = ExifLength-ThumbnailOffset; +- if (ShowTags) printf("Thumbnail incorrectly placed in header\n"); +- +- } +- // The thumbnail pointer appears to be valid. Store it. +- ImageInfo.ThumbnailOffset = ThumbnailOffset; +- ImageInfo.ThumbnailSize = ThumbnailSize; +- +- if (ShowTags){ +- printf("Thumbnail size: %u bytes\n",ThumbnailSize); +- } +- } +- } +-} +- +-void Clear_EXIF () +-{ +- FocalplaneXRes = 0; +- FocalplaneUnits = 0; +- ExifImageWidth = 0; +- NumOrientations = 0; +- MotorolaOrder = 0; +- OrientationPtr[0] = OrientationPtr[1] = NULL; +- OrientationNumFormat[0] = OrientationNumFormat[1] = 0; +-} +- +-//-------------------------------------------------------------------------- +-// Process a EXIF marker +-// Describes all the drivel that most digital cameras include... +-//-------------------------------------------------------------------------- +-void process_EXIF (unsigned char * ExifSection, int length) +-{ +- int FirstOffset; +- +- Clear_EXIF(); +- +- if (ShowTags){ +- printf("Exif header %u bytes long\n",length); +- } +- +- { // Check the EXIF header component +- static uchar ExifHeader[] = "Exif\0\0"; +- if (memcmp(ExifSection+2, ExifHeader,6)){ +- ErrNonfatal("Incorrect Exif header",0,0); +- return; +- } +- } +- +- if (memcmp(ExifSection+8,"II",2) == 0){ +- if (ShowTags) printf("Exif section in Intel order\n"); +- MotorolaOrder = 0; +- }else{ +- if (memcmp(ExifSection+8,"MM",2) == 0){ +- if (ShowTags) printf("Exif section in Motorola order\n"); +- MotorolaOrder = 1; +- }else{ +- ErrNonfatal("Invalid Exif alignment marker.",0,0); +- return; +- } +- } +- +- // Check the next value for correctness. +- if (Get16u(ExifSection+10) != 0x2a){ +- ErrNonfatal("Invalid Exif start (1)",0,0); +- return; +- } +- +- FirstOffset = (int)Get32u(ExifSection+12); +- if (FirstOffset < 8 || FirstOffset > 16){ +- if (FirstOffset < 16 || FirstOffset > length-16 || length < 16){ +- ErrNonfatal("invalid offset for first Exif IFD value",0,0); +- return; +- } +- // Usually set to 8, but other values valid too. +- ErrNonfatal("Suspicious offset of first Exif IFD value",0,0); +- } +- +- DirWithThumbnailPtrs = NULL; +- +- +- // First directory starts 16 bytes in. All offset are relative to 8 bytes in. +- ProcessExifDir(ExifSection+8+FirstOffset, ExifSection+8, length-8, 0); +- +- ImageInfo.ThumbnailAtEnd = ImageInfo.ThumbnailOffset >= ImageInfo.LargestExifOffset ? TRUE : FALSE; +- +- if (DumpExifMap){ +- int a,b; +- printf("Map: %05d- End of exif\n",length-8); +- for (a=0;a> 8); +- Buffer[1] = (unsigned char)DataWriteIndex; +- +- // Remove old exif section, if there was one. +- RemoveSectionType(M_EXIF); +- +- { +- // Sections need malloced buffers, so do that now, especially because +- // we now know how big it needs to be allocated. +- unsigned char * NewBuf = malloc(DataWriteIndex); +- if (NewBuf == NULL){ +- ErrFatal("Could not allocate memory"); +- } +- memcpy(NewBuf, Buffer, DataWriteIndex); +- +- CreateSection(M_EXIF, NewBuf, DataWriteIndex); +- +- // Re-parse new exif section, now that its in place +- // otherwise, we risk touching data that has already been freed. +- process_EXIF(NewBuf, DataWriteIndex); +- } +-} +- +-//-------------------------------------------------------------------------- +-// Cler the rotation tag in the exif header to 1. +-// Returns NULL if no orietnation tag exists. +-//-------------------------------------------------------------------------- +-const char * ClearOrientation(void) +-{ +- int a; +- if (NumOrientations == 0) return NULL; +- +- for (a=0;a= 1 && ImageInfo.Orientation <= 8){ +- return OrientTab[ImageInfo.Orientation]; +- }else{ +- return ""; +- } +-} +- +-//-------------------------------------------------------------------------- +-// Convert exif time to Unix time structure +-//-------------------------------------------------------------------------- +-int Exif2tm(struct tm * timeptr, char * ExifTime) +-{ +- int a; +- +- timeptr->tm_wday = -1; +- +- // Check for format: YYYY:MM:DD HH:MM:SS format. +- // Date and time normally separated by a space, but also seen a ':' there, so +- // skip the middle space with '%*c' so it can be any character. +- timeptr->tm_sec = 0; +- a = sscanf(ExifTime, "%d%*c%d%*c%d%*c%d:%d:%d", +- &timeptr->tm_year, &timeptr->tm_mon, &timeptr->tm_mday, +- &timeptr->tm_hour, &timeptr->tm_min, &timeptr->tm_sec); +- +- if (a >= 5){ +- if (timeptr->tm_year <= 12 && timeptr->tm_mday > 2000 && ExifTime[2] == '.'){ +- // LG Electronics VX-9700 seems to encode the date as 'MM.DD.YYYY HH:MM' +- // can't these people read the standard? At least they left enough room +- // in the header to put an Exif format date in there. +- int tmp; +- tmp = timeptr->tm_year; +- timeptr->tm_year = timeptr->tm_mday; +- timeptr->tm_mday = timeptr->tm_mon; +- timeptr->tm_mon = tmp; +- } +- +- // Accept five or six parameters. Some cameras do not store seconds. +- timeptr->tm_isdst = -1; +- timeptr->tm_mon -= 1; // Adjust for unix zero-based months +- timeptr->tm_year -= 1900; // Adjust for year starting at 1900 +- return TRUE; // worked. +- } +- +- return FALSE; // Wasn't in Exif date format. +-} +- +- +-//-------------------------------------------------------------------------- +-// Show the collected image info, displaying camera F-stop and shutter speed +-// in a consistent and legible fashion. +-//-------------------------------------------------------------------------- +-void ShowImageInfo(int ShowFileInfo) +-{ +- if (ShowFileInfo){ +- printf("File name : %s\n",ImageInfo.FileName); +- printf("File size : %d bytes\n",ImageInfo.FileSize); +- +- { +- char Temp[20]; +- FileTimeAsString(Temp); +- printf("File date : %s\n",Temp); +- } +- } +- +- if (ImageInfo.CameraMake[0]){ +- printf("Camera make : %s\n",ImageInfo.CameraMake); +- printf("Camera model : %s\n",ImageInfo.CameraModel); +- } +- if (ImageInfo.DateTime[0]){ +- printf("Date/Time : %s\n",ImageInfo.DateTime); +- } +- printf("Resolution : %d x %d\n",ImageInfo.Width, ImageInfo.Height); +- +- if (ImageInfo.Orientation > 1 && ImageInfo.Orientation <=8){ +- // Only print orientation if one was supplied, and if its not 1 (normal orientation) +- printf("Orientation : %s\n", OrientTab[ImageInfo.Orientation]); +- } +- +- if (ImageInfo.IsColor == 0){ +- printf("Color/bw : Black and white\n"); +- } +- +- if (ImageInfo.FlashUsed >= 0){ +- if (ImageInfo.FlashUsed & 1){ +- printf("Flash used : Yes"); +- switch (ImageInfo.FlashUsed){ +- case 0x5: printf(" (Strobe light not detected)"); break; +- case 0x7: printf(" (Strobe light detected) "); break; +- case 0x9: printf(" (manual)"); break; +- case 0xd: printf(" (manual, return light not detected)"); break; +- case 0xf: printf(" (manual, return light detected)"); break; +- case 0x19:printf(" (auto)"); break; +- case 0x1d:printf(" (auto, return light not detected)"); break; +- case 0x1f:printf(" (auto, return light detected)"); break; +- case 0x41:printf(" (red eye reduction mode)"); break; +- case 0x45:printf(" (red eye reduction mode return light not detected)"); break; +- case 0x47:printf(" (red eye reduction mode return light detected)"); break; +- case 0x49:printf(" (manual, red eye reduction mode)"); break; +- case 0x4d:printf(" (manual, red eye reduction mode, return light not detected)"); break; +- case 0x4f:printf(" (red eye reduction mode, return light detected)"); break; +- case 0x59:printf(" (auto, red eye reduction mode)"); break; +- case 0x5d:printf(" (auto, red eye reduction mode, return light not detected)"); break; +- case 0x5f:printf(" (auto, red eye reduction mode, return light detected)"); break; +- } +- }else{ +- printf("Flash used : No"); +- switch (ImageInfo.FlashUsed){ +- case 0x18:printf(" (auto)"); break; +- } +- } +- printf("\n"); +- } +- +- +- if (ImageInfo.FocalLength){ +- printf("Focal length : %4.1fmm",(double)ImageInfo.FocalLength); +- if (ImageInfo.FocalLength35mmEquiv){ +- printf(" (35mm equivalent: %dmm)", ImageInfo.FocalLength35mmEquiv); +- } +- printf("\n"); +- } +- +- if (ImageInfo.DigitalZoomRatio > 1){ +- // Digital zoom used. Shame on you! +- printf("Digital Zoom : %1.3fx\n", (double)ImageInfo.DigitalZoomRatio); +- } +- +- if (ImageInfo.CCDWidth){ +- printf("CCD width : %4.2fmm\n",(double)ImageInfo.CCDWidth); +- } +- +- if (ImageInfo.ExposureTime){ +- if (ImageInfo.ExposureTime < 0.010){ +- printf("Exposure time: %6.4f s ",(double)ImageInfo.ExposureTime); +- }else{ +- printf("Exposure time: %5.3f s ",(double)ImageInfo.ExposureTime); +- } +- if (ImageInfo.ExposureTime <= 0.5){ +- printf(" (1/%d)",(int)(0.5 + 1/ImageInfo.ExposureTime)); +- } +- printf("\n"); +- } +- if (ImageInfo.ApertureFNumber){ +- printf("Aperture : f/%3.1f\n",(double)ImageInfo.ApertureFNumber); +- } +- if (ImageInfo.Distance){ +- if (ImageInfo.Distance < 0){ +- printf("Focus dist. : Infinite\n"); +- }else{ +- printf("Focus dist. : %4.2fm\n",(double)ImageInfo.Distance); +- } +- } +- +- if (ImageInfo.ISOequivalent){ +- printf("ISO equiv. : %2d\n",(int)ImageInfo.ISOequivalent); +- } +- +- if (ImageInfo.ExposureBias){ +- // If exposure bias was specified, but set to zero, presumably its no bias at all, +- // so only show it if its nonzero. +- printf("Exposure bias: %4.2f\n",(double)ImageInfo.ExposureBias); +- } +- +- switch(ImageInfo.Whitebalance) { +- case 1: +- printf("Whitebalance : Manual\n"); +- break; +- case 0: +- printf("Whitebalance : Auto\n"); +- break; +- } +- +- //Quercus: 17-1-2004 Added LightSource, some cams return this, whitebalance or both +- switch(ImageInfo.LightSource) { +- case 1: +- printf("Light Source : Daylight\n"); +- break; +- case 2: +- printf("Light Source : Fluorescent\n"); +- break; +- case 3: +- printf("Light Source : Incandescent\n"); +- break; +- case 4: +- printf("Light Source : Flash\n"); +- break; +- case 9: +- printf("Light Source : Fine weather\n"); +- break; +- case 11: +- printf("Light Source : Shade\n"); +- break; +- default:; //Quercus: 17-1-2004 There are many more modes for this, check Exif2.2 specs +- // If it just says 'unknown' or we don't know it, then +- // don't bother showing it - it doesn't add any useful information. +- } +- +- if (ImageInfo.MeteringMode > 0){ // 05-jan-2001 vcs +- printf("Metering Mode: "); +- switch(ImageInfo.MeteringMode) { +- case 1: printf("average\n"); break; +- case 2: printf("center weight\n"); break; +- case 3: printf("spot\n"); break; +- case 4: printf("multi spot\n"); break; +- case 5: printf("pattern\n"); break; +- case 6: printf("partial\n"); break; +- case 255: printf("other\n"); break; +- default: printf("unknown (%d)\n",ImageInfo.MeteringMode); break; +- } +- } +- +- if (ImageInfo.ExposureProgram){ // 05-jan-2001 vcs +- switch(ImageInfo.ExposureProgram) { +- case 1: +- printf("Exposure : Manual\n"); +- break; +- case 2: +- printf("Exposure : program (auto)\n"); +- break; +- case 3: +- printf("Exposure : aperture priority (semi-auto)\n"); +- break; +- case 4: +- printf("Exposure : shutter priority (semi-auto)\n"); +- break; +- case 5: +- printf("Exposure : Creative Program (based towards depth of field)\n"); +- break; +- case 6: +- printf("Exposure : Action program (based towards fast shutter speed)\n"); +- break; +- case 7: +- printf("Exposure : Portrait Mode\n"); +- break; +- case 8: +- printf("Exposure : LandscapeMode \n"); +- break; +- default: +- break; +- } +- } +- switch(ImageInfo.ExposureMode){ +- case 0: // Automatic (not worth cluttering up output for) +- break; +- case 1: printf("Exposure Mode: Manual\n"); +- break; +- case 2: printf("Exposure Mode: Auto bracketing\n"); +- break; +- } +- +- if (ImageInfo.DistanceRange) { +- printf("Focus range : "); +- switch(ImageInfo.DistanceRange) { +- case 1: +- printf("macro"); +- break; +- case 2: +- printf("close"); +- break; +- case 3: +- printf("distant"); +- break; +- } +- printf("\n"); +- } +- +- +- +- if (ImageInfo.Process != M_SOF0){ +- // don't show it if its the plain old boring 'baseline' process, but do +- // show it if its something else, like 'progressive' (used on web sometimes) +- unsigned a; +- for (a=0;;a++){ +- if (a >= PROCESS_TABLE_SIZE){ +- // ran off the end of the table. +- printf("Jpeg process : Unknown\n"); +- break; +- } +- if (ProcessTable[a].Tag == ImageInfo.Process){ +- printf("Jpeg process : %s\n",ProcessTable[a].Desc); +- break; +- } +- } +- } +- +- if (ImageInfo.GpsInfoPresent){ +- printf("GPS Latitude : %s\n",ImageInfo.GpsLat); +- printf("GPS Longitude: %s\n",ImageInfo.GpsLong); +- if (ImageInfo.GpsAlt[0]) printf("GPS Altitude : %s\n",ImageInfo.GpsAlt); +- } +- +- if (ImageInfo.QualityGuess){ +- printf("JPEG Quality : %d\n", ImageInfo.QualityGuess); +- } +- +- // Print the comment. Print 'Comment:' for each new line of comment. +- if (ImageInfo.Comments[0]){ +- int a,c; +- printf("Comment : "); +- if (!ImageInfo.CommentWidthchars){ +- for (a=0;a= 0 && ImageInfo.FlashUsed & 1){ +- printf(" (flash)"); +- } +- +- if (ImageInfo.IsColor == 0){ +- printf(" (bw)"); +- } +- +- printf("\n"); +-} +diff --git a/src/external/jhead-3.04/gpsinfo.c b/src/external/jhead-3.04/gpsinfo.c +deleted file mode 100644 +index 1b8fdac..0000000 +--- a/src/external/jhead-3.04/gpsinfo.c ++++ /dev/null +@@ -1,218 +0,0 @@ +-//-------------------------------------------------------------------------- +-// Parsing of GPS info from exif header. +-// +-// Matthias Wandel, Dec 1999 - Dec 2002 +-//-------------------------------------------------------------------------- +-#include "jhead.h" +- +-#define MAX_GPS_TAG 0x1e +- +- +-#define TAG_GPS_LAT_REF 1 +-#define TAG_GPS_LAT 2 +-#define TAG_GPS_LONG_REF 3 +-#define TAG_GPS_LONG 4 +-#define TAG_GPS_ALT_REF 5 +-#define TAG_GPS_ALT 6 +- +- +-static const char * GpsTags[MAX_GPS_TAG+1]= { +- "VersionID ",//0x00 +- "LatitudeRef ",//0x01 +- "Latitude ",//0x02 +- "LongitudeRef ",//0x03 +- "Longitude ",//0x04 +- "AltitudeRef ",//0x05 +- "Altitude ",//0x06 +- "TimeStamp ",//0x07 +- "Satellites ",//0x08 +- "Status ",//0x09 +- "MeasureMode ",//0x0A +- "DOP ",//0x0B +- "SpeedRef ",//0x0C +- "Speed ",//0x0D +- "TrackRef ",//0x0E +- "Track ",//0x0F +- "ImgDirectionRef ",//0x10 +- "ImgDirection ",//0x11 +- "MapDatum ",//0x12 +- "DestLatitudeRef ",//0x13 +- "DestLatitude ",//0x14 +- "DestLongitudeRef",//0x15 +- "DestLongitude ",//0x16 +- "DestBearingRef ",//0x17 +- "DestBearing ",//0x18 +- "DestDistanceRef ",//0x19 +- "DestDistance ",//0x1A +- "ProcessingMethod",//0x1B +- "AreaInformation ",//0x1C +- "DateStamp ",//0x1D +- "Differential ",//0x1E +-}; +- +-//-------------------------------------------------------------------------- +-// Process GPS info directory +-//-------------------------------------------------------------------------- +-void ProcessGpsInfo(unsigned char * DirStart, unsigned char * OffsetBase, unsigned ExifLength) +-{ +- int de; +- unsigned a; +- int NumDirEntries; +- +- NumDirEntries = Get16u(DirStart); +- #define DIR_ENTRY_ADDR(Start, Entry) (Start+2+12*(Entry)) +- +- if (ShowTags){ +- printf("(dir has %d entries)\n",NumDirEntries); +- } +- +- ImageInfo.GpsInfoPresent = TRUE; +- strcpy(ImageInfo.GpsLat, "? ?"); +- strcpy(ImageInfo.GpsLong, "? ?"); +- ImageInfo.GpsAlt[0] = 0; +- +- for (de=0;de OffsetBase+ExifLength){ +- ErrNonfatal("GPS info directory goes past end of exif",0,0); +- return; +- } +- +- Tag = Get16u(DirEntry); +- Format = Get16u(DirEntry+2); +- Components = Get32u(DirEntry+4); +- +- if ((Format-1) >= NUM_FORMATS) { +- // (-1) catches illegal zero case as unsigned underflows to positive large. +- ErrNonfatal("Illegal number format %d for Exif gps tag %04x", Format, Tag); +- continue; +- } +- +- ComponentSize = BytesPerFormat[Format]; +- ByteCount = Components * ComponentSize; +- +- if (ByteCount > 4){ +- unsigned OffsetVal; +- OffsetVal = Get32u(DirEntry+8); +- // If its bigger than 4 bytes, the dir entry contains an offset. +- if (OffsetVal > 0x1000000 || OffsetVal+ByteCount > ExifLength){ +- // Max exif in jpeg is 64k, so any offset bigger than that is bogus. +- // Bogus pointer offset and / or bytecount value +- ErrNonfatal("Illegal value pointer for Exif gps tag %04x", Tag,0); +- continue; +- } +- ValuePtr = OffsetBase+OffsetVal; +- }else{ +- // 4 bytes or less and value is in the dir entry itself +- ValuePtr = DirEntry+8; +- } +- +- switch(Tag){ +- char FmtString[21]; +- char TempString[50]; +- double Values[3]; +- +- case TAG_GPS_LAT_REF: +- ImageInfo.GpsLat[0] = ValuePtr[0]; +- break; +- +- case TAG_GPS_LONG_REF: +- ImageInfo.GpsLong[0] = ValuePtr[0]; +- break; +- +- case TAG_GPS_LAT: +- case TAG_GPS_LONG: +- if (Format != FMT_URATIONAL){ +- ErrNonfatal("Inappropriate format (%d) for Exif GPS coordinates!", Format, 0); +- } +- strcpy(FmtString, "%0.0fd %0.0fm %0.0fs"); +- for (a=0;a<3;a++){ +- int den, digits; +- +- den = Get32s(ValuePtr+4+a*ComponentSize); +- digits = 0; +- while (den > 1 && digits <= 6){ +- den = den / 10; +- digits += 1; +- } +- if (digits > 6) digits = 6; +- FmtString[1+a*7] = (char)('2'+digits+(digits ? 1 : 0)); +- FmtString[3+a*7] = (char)('0'+digits); +- +- Values[a] = ConvertAnyFormat(ValuePtr+a*ComponentSize, Format); +- } +- +- snprintf(TempString, sizeof(TempString), FmtString, Values[0], Values[1], Values[2]); +- +- if (Tag == TAG_GPS_LAT){ +- strncpy(ImageInfo.GpsLat+2, TempString, 29); +- }else{ +- strncpy(ImageInfo.GpsLong+2, TempString, 29); +- } +- break; +- +- case TAG_GPS_ALT_REF: +- ImageInfo.GpsAlt[0] = (char)(ValuePtr[0] ? '-' : ' '); +- break; +- +- case TAG_GPS_ALT: +- snprintf(ImageInfo.GpsAlt+1, sizeof(ImageInfo.GpsAlt)-1, +- "%.2fm", ConvertAnyFormat(ValuePtr, Format)); +- break; +- } +- +- if (ShowTags){ +- // Show tag value. +- if (Tag < MAX_GPS_TAG){ +- printf(" GPS%s =", GpsTags[Tag]); +- }else{ +- // Show unknown tag +- printf(" Illegal GPS tag %04x=", Tag); +- } +- +- switch(Format){ +- case FMT_UNDEFINED: +- // Undefined is typically an ascii string. +- +- case FMT_STRING: +- // String arrays printed without function call (different from int arrays) +- { +- printf("\""); +- for (a=0;a= 32){ +- if (ZeroSkipped){ +- printf("?"); +- ZeroSkipped = 0; +- } +- putchar(ValuePtr[a]); +- }else{ +- if (ValuePtr[a] == 0){ +- ZeroSkipped = 1; +- } +- } +- } +- printf("\"\n"); +- } +- break; +- +- default: +- // Handle arrays of numbers later (will there ever be?) +- for (a=0;;){ +- PrintFormatNumber(ValuePtr+a*ComponentSize, Format, ByteCount); +- if (++a >= Components) break; +- printf(", "); +- } +- printf("\n"); +- } +- } +- } +-} +- +- +diff --git a/src/external/jhead-3.04/iptc.c b/src/external/jhead-3.04/iptc.c +deleted file mode 100644 +index 06fa4e3..0000000 +--- a/src/external/jhead-3.04/iptc.c ++++ /dev/null +@@ -1,220 +0,0 @@ +-//-------------------------------------------------------------------------- +-// Process IPTC data and XMP data. +-//-------------------------------------------------------------------------- +-#include "jhead.h" +- +-// IPTC entry types known to Jhead (there's many more defined) +-#define IPTC_RECORD_VERSION 0x00 +-#define IPTC_SUPLEMENTAL_CATEGORIES 0x14 +-#define IPTC_KEYWORDS 0x19 +-#define IPTC_CAPTION 0x78 +-#define IPTC_AUTHOR 0x7A +-#define IPTC_HEADLINE 0x69 +-#define IPTC_SPECIAL_INSTRUCTIONS 0x28 +-#define IPTC_CATEGORY 0x0F +-#define IPTC_BYLINE 0x50 +-#define IPTC_BYLINE_TITLE 0x55 +-#define IPTC_CREDIT 0x6E +-#define IPTC_SOURCE 0x73 +-#define IPTC_COPYRIGHT_NOTICE 0x74 +-#define IPTC_OBJECT_NAME 0x05 +-#define IPTC_CITY 0x5A +-#define IPTC_STATE 0x5F +-#define IPTC_COUNTRY 0x65 +-#define IPTC_TRANSMISSION_REFERENCE 0x67 +-#define IPTC_DATE 0x37 +-#define IPTC_COPYRIGHT 0x0A +-#define IPTC_COUNTRY_CODE 0x64 +-#define IPTC_REFERENCE_SERVICE 0x2D +-#define IPTC_TIME_CREATED 0x3C +-#define IPTC_SUB_LOCATION 0x5C +-#define IPTC_IMAGE_TYPE 0x82 +- +-//-------------------------------------------------------------------------- +-// Process and display IPTC marker. +-// +-// IPTC block consists of: +-// - Marker: 1 byte (0xED) +-// - Block length: 2 bytes +-// - IPTC Signature: 14 bytes ("Photoshop 3.0\0") +-// - 8BIM Signature 4 bytes ("8BIM") +-// - IPTC Block start 2 bytes (0x04, 0x04) +-// - IPTC Header length 1 byte +-// - IPTC header Header is padded to even length, counting the length byte +-// - Length 4 bytes +-// - IPTC Data which consists of a number of entries, each of which has the following format: +-// - Signature 2 bytes (0x1C02) +-// - Entry type 1 byte (for defined entry types, see #defines above) +-// - entry length 2 bytes +-// - entry data 'entry length' bytes +-// +-//-------------------------------------------------------------------------- +-void show_IPTC (unsigned char* Data, unsigned int itemlen) +-{ +- const char IptcSig1[] = "Photoshop 3.0"; +- const char IptcSig2[] = "8BIM"; +- const char IptcSig3[] = {0x04, 0x04}; +- +- unsigned char * pos = Data + sizeof(short); // position data pointer after length field +- unsigned char * maxpos = Data+itemlen; +- unsigned char headerLen = 0; +- unsigned char dataLen = 0; +- +- if (itemlen < 25) goto corrupt; +- +- // Check IPTC signatures +- if (memcmp(pos, IptcSig1, sizeof(IptcSig1)-1) != 0) goto badsig; +- pos += sizeof(IptcSig1); // move data pointer to the next field +- +- if (memcmp(pos, IptcSig2, sizeof(IptcSig2)-1) != 0) goto badsig; +- pos += sizeof(IptcSig2)-1; // move data pointer to the next field +- +- +- while (memcmp(pos, IptcSig3, sizeof(IptcSig3)) != 0) { // loop on valid Photoshop blocks +- +- pos += sizeof(IptcSig3); // move data pointer to the Header Length +- // Skip header +- headerLen = *pos; // get header length and move data pointer to the next field +- pos += (headerLen & 0xfe) + 2; // move data pointer to the next field (Header is padded to even length, counting the length byte) +- +- pos += 3; // move data pointer to length, assume only one byte, TODO: use all 4 bytes +- +- dataLen = *pos++; +- pos += dataLen; // skip data section +- +- if (memcmp(pos, IptcSig2, sizeof(IptcSig2) - 1) != 0) { +- badsig: if (ShowTags) { +- ErrNonfatal("IPTC type signature mismatch\n", 0, 0); +- } +- return; +- } +- pos += sizeof(IptcSig2) - 1; // move data pointer to the next field +- } +- +- pos += sizeof(IptcSig3); // move data pointer to the next field +- +- if (pos >= maxpos) goto corrupt; +- +- // IPTC section found +- +- // Skip header +- headerLen = *pos++; // get header length and move data pointer to the next field +- pos += headerLen + 1 - (headerLen % 2); // move data pointer to the next field (Header is padded to even length, counting the length byte) +- +- if (pos+4 >= maxpos) goto corrupt; +- +- // Get length (from motorola format) +- //length = (*pos << 24) | (*(pos+1) << 16) | (*(pos+2) << 8) | *(pos+3); +- +- pos += 4; // move data pointer to the next field +- +- printf("======= IPTC data: =======\n"); +- +- // Now read IPTC data +- while (pos < (Data + itemlen-5)) { +- short signature; +- unsigned char type = 0; +- short length = 0; +- const char * description = NULL; +- +- if (pos+5 > maxpos) goto corrupt; +- +- signature = (*pos << 8) + (*(pos+1)); +- pos += 2; +- +- if (signature != 0x1C01 && signature != 0x1c02) break; +- +- type = *pos++; +- length = (*pos << 8) + (*(pos+1)); +- if (length < 1) goto corrupt; +- pos += 2; // Skip tag length +- +- if (pos+length > maxpos) goto corrupt; +- // Process tag here +- switch (type) { +- case IPTC_RECORD_VERSION: +- printf("Record vers. : %d\n", (*pos << 8) + (*(pos+1))); +- break; +- +- case IPTC_SUPLEMENTAL_CATEGORIES: description = "SuplementalCategories"; break; +- case IPTC_KEYWORDS: description = "Keywords"; break; +- case IPTC_CAPTION: description = "Caption"; break; +- case IPTC_AUTHOR: description = "Author"; break; +- case IPTC_HEADLINE: description = "Headline"; break; +- case IPTC_SPECIAL_INSTRUCTIONS: description = "Spec. Instr."; break; +- case IPTC_CATEGORY: description = "Category"; break; +- case IPTC_BYLINE: description = "Byline"; break; +- case IPTC_BYLINE_TITLE: description = "Byline Title"; break; +- case IPTC_CREDIT: description = "Credit"; break; +- case IPTC_SOURCE: description = "Source"; break; +- case IPTC_COPYRIGHT_NOTICE: description = "(C)Notice"; break; +- case IPTC_OBJECT_NAME: description = "Object Name"; break; +- case IPTC_CITY: description = "City"; break; +- case IPTC_STATE: description = "State"; break; +- case IPTC_COUNTRY: description = "Country"; break; +- case IPTC_TRANSMISSION_REFERENCE: description = "OriginalTransmissionReference"; break; +- case IPTC_DATE: description = "DateCreated"; break; +- case IPTC_COPYRIGHT: description = "(C)Flag"; break; +- case IPTC_REFERENCE_SERVICE: description = "Country Code"; break; +- case IPTC_COUNTRY_CODE: description = "Ref. Service"; break; +- case IPTC_TIME_CREATED: description = "Time Created"; break; +- case IPTC_SUB_LOCATION: description = "Sub Location"; break; +- case IPTC_IMAGE_TYPE: description = "Image type"; break; +- +- default: +- if (ShowTags){ +- printf("Unrecognised IPTC tag: %d\n", type ); +- } +- break; +- } +- if (description != NULL) { +- char TempBuf[32]; +- memset(TempBuf, 0, sizeof(TempBuf)); +- memset(TempBuf, ' ', 14); +- memcpy(TempBuf, description, strlen(description)); +- strcat(TempBuf, ":"); +- printf("%s %*.*s\n", TempBuf, length, length, pos); +- } +- pos += length; +- } +- return; +-corrupt: +- ErrNonfatal("Pointer corruption in IPTC\n",0,0); +-} +- +- +- +-//-------------------------------------------------------------------------- +-// Dump contents of XMP section +-//-------------------------------------------------------------------------- +-void ShowXmp(Section_t XmpSection) +-{ +- unsigned char * Data; +- char OutLine[101]; +- int OutLineChars; +- int NonBlank; +- unsigned a; +- NonBlank = 0; +- Data = XmpSection.Data; +- OutLineChars = 0; +- +- +- for (a=0;a= 32 && Data[a] < 128){ +- OutLine[OutLineChars++] = Data[a]; +- if (Data[a] != ' ') NonBlank |= 1; +- }else{ +- if (Data[a] != '\n'){ +- OutLine[OutLineChars++] = '?'; +- } +- } +- if (Data[a] == '\n' || OutLineChars >= 100){ +- OutLine[OutLineChars] = 0; +- if (NonBlank){ +- puts(OutLine); +- } +- NonBlank = (NonBlank & 1) << 1; +- OutLineChars = 0; +- } +- } +-} +diff --git a/src/external/jhead-3.04/jhead-3.04.pro b/src/external/jhead-3.04/jhead-3.04.pro +deleted file mode 100755 +index b172193..0000000 +--- a/src/external/jhead-3.04/jhead-3.04.pro ++++ /dev/null +@@ -1,23 +0,0 @@ +-include(../ext_common.pri) +-TEMPLATE = lib +-TARGET = jhead +-CONFIG += staticlib +-DEPENDPATH += . +-INCLUDEPATH += . +- +-# Input +-HEADERS += jhead.h +- +-SOURCES += \ +- jpgfile.c \ +- jpgqguess.c \ +- jhead.c \ +- paths.c \ +- exif.c \ +- iptc.c \ +- gpsinfo.c \ +- makernote.c +- +-win32:SOURCES += myglob.c +- +-DEFINES += main="jhead_main" +diff --git a/src/external/jhead-3.04/jhead.1 b/src/external/jhead-3.04/jhead.1 +deleted file mode 100644 +index 1f745a9..0000000 +--- a/src/external/jhead-3.04/jhead.1 ++++ /dev/null +@@ -1,427 +0,0 @@ +-.TH JHEAD 1 "22 Nov 2019" "jhead 3.04" +-.SH NAME +-jhead \- Digicam JPEG Exif header manipulation tool +-.SH SYNOPSIS +-.B jhead +-[ +-.I options +-] +-[ +-.I file\.\.\. +-] +- +-.LP +-.SH DESCRIPTION +-.LP +-.B jhead +-is used to display and manipulate data contained in the Exif header of JPEG +-images from digital cameras. By default, jhead displays the more useful +-camera settings from the file in a user-friendly format. +-.PP +-.B jhead +-can also be used to manipulate some aspects of the image relating to JPEG and +-Exif headers, such as changing the internal timestamps, removing the thumbnail, +-or transferring Exif headers back into edited images after graphical editors +-deleted the Exif header. +-.B jhead +-can also be used to launch other programs, similar in style to the UNIX +-.B find +-command, but much simpler. +- +- +-.SH GENERAL METADATA OPTIONS +-.TP +-.BI \-\^te \ file +-Transplant Exif header from a JPEG (with Exif header) in +-.I file +-into the image that is manipulated. This option is useful if you like +-to edit the photos but still want the Exif header on your photos. As +-most photo editing programs will wipe out the Exif header, this option +-can be used to re-copy them back from original copies after editing the +-photos. +- +- +-This feature has an interesting 'relative path' option for specifying +-the thumbnail name. Whenever the contains the characters '&i', +-will substitute the original filename for this name. This allows +-creating a jhead 'relative name' when doing a whole batch of files. For +-example, the incantation: +- +-.I jhead \-te """originals/&i"" *.jpg +- +-would transfer the exif header for each .jpg file in the originals +-directory by the same name, Both Win32 and most Unix shells treat the '&' +-character in a special way, so you have to put quotes around that +-command line option for the '&' to even be passed to the program. +- +-.TP +-.B \-dc +-Delete comment field from the JPEG header. Note that the comment +-is not part of the Exif header. +-.TP +-.B \-de +-Delete the Exif header entirely. Leaves other metadata sections intact. +-.TP +-.B \-di +-Delete the IPTC section, if present. Leaves other metadata sections intact. +-.TP +-.B \-dx +-Delete the XMP section, if present. Leaves other metadata sections intact. +-.TP +-.B \-du +-Delete sections of jpeg that are not Exif, not comment, and otherwise not +-contributing to the image either - such as data that photoshop might leave in the image. +-.TP +-.B \-purejpg +-Delete all JPEG sections that aren't necessary for rendering the image. +-Strips any metadata that various applications may have left in the +-image. A combination of the \-de \-dc and \-du options. +-.TP +-.B \-mkexif +-Creates minimal exif header. Exif header contains date/time, and empty +-thumbnail fields only. Date/time set to file time by default. Use with +-\-rgt option if you want the exif header to contain a thumbnail. Note +-that exif header creation is very limited at this time, and no other +-fields can be added to the exif header this way. +-.TP +-.B \-ce +-Edit the JPEG header comment field (note, this comment field is outside +-the Exif structure and can be part of Exif and non Exif style JPEG +-images). +- +-A temporary file containing the comment is created and a text editor is +-launched to edit the file. The editor is specified in the EDITOR +-environment variable. If none is specified notepad or vi are used under +-Windows and Unix respectively. After the editor exits, the data is +-transferred back into the image, and the temporary file deleted. +-.TP +-.BI \-\^cs \ file +-Save comment section to a +-.I file +-.TP +-.BI \-\^ci \ file +-Replace comment with text from +-.I file +-.TP +-.BI \-\^cl \ string +-Replace comment with specified string from command line +- +-.SH DATE / TIME MANIPULATION OPTIONS +- +-.TP +-.B \-ft +-Sets the file's system time stamp to what is stored in the Exif header. +-.TP +-.B \-dsft +-Sets the Exif timestamp to the file's timestamp. Requires an Exif header to +-pre-exist. Use \-mkexif option to create one if needed. +-.TP +-.BI \-\^n [format_string] +-This option causes files to be renamed and/ or mmoved using the date +-information from the Exif header "DateTimeOriginal" field. If the file +-is not an Exif file, or the DateTimeOriginal does not contain a valid +-value, the file date is used. If the new name contains a '/', +-this will be interpreted as a new path, and the file will be moved +-accordingly. +- +-If the +-.I format_string +-is omitted, the file will be renamed to MMDD-HHMMSS. Note that this +-scheme doesn't include the year (I never have photos from different +-years together anyway). +- +-If a +-.I format_string +-is provided, it will be passed to the strftime function as the format +-string. In addition, if the format string contains '%f', this will +-substitute the original name of the file (minus extension). '%i' will +-substitute a sequence number. Leading zeros can be specified like with +-printf - i.e. '%04i' pads the number to 4 digits using leading zeros. +- +-If the name includes '/', this is interpreted as a new path for the file. +-If the new path does not exist, the path will be created. +- +-If the target name already exists, the name will be appended with "a", +-"b", "c", etc, unless the name ends with a letter, in which case it will +-be appended with "0", "1", "2", etc. +- +-This feature is especially useful if more than one digital camera was +-used to take pictures of an event. By renaming them to a scheme +-according to date, they will automatically appear in order of taking in +-most directory listings and image browsers. Alternatively, if your +-image browser supports listing by file time, you can use the \-ft option +-to set the file time to the time the photo was taken. +- +-Some of the more useful arguments for strftime are: +- +-.BR %H \ Hour\ in\ 24-hour\ format\ (00\ -\ 23) +-.br +-.BR %j \ Day\ of\ year\ as\ decimal\ number\ (001\ -\ 366) +-.br +-.BR %m \ Month\ as\ decimal\ number\ (01\ -\ 12) +-.br +-.BR %M \ Minute\ as\ decimal\ number\ (00\ -\ 59) +-.br +-.BR %S \ Second\ as\ decimal\ number\ (00\ -\ 59) +-.br +-.BR %w \ Weekday\ as\ decimal\ number\ (0\ -\ 6;\ Sunday\ is\ 0) +-.br +-.BR %y \ Year\ without\ century,\ as\ decimal\ number\ (00\ -\ 99) +-.br +-.BR %Y \ Year\ with\ century,\ as\ decimal\ number +- +-Example: +- +-.I jhead \-n%Y%m%d\-%H%M%S *.jpg +- +-This will rename files matched by *.jpg in the format YYYYMMDD\-HHMMSS +- +-For a full listing of strftime arguments, look up the strftime in them +-man pages. Note that some arguments to the strftime function (not +-listed here) produce strings with characters such as ':' that may not be +-valid as part of a filename on some systems. +- +-.TP +-.B \-ta<+|\-> +-Adjust time stored in the Exif header by h:mm forwards or backwards. +-Useful when having taken pictures with the wrong time set on the camera, +-such as after travelling across time zones, or when daylight savings +-time has changed. +- +-Examples: +- +-Add 1 hourand 5 minutes to the time +-.br +-jhead \-ta+1:05 +- +-Decrease time by one second: +-.br +-jhead \-ta-0:0:1 +- +- +-This option changes all Date/time fields in the exif header, including +-"DateTimeOriginal" (tag 0x9003) and "DateTimeDigitized" (tag 0x9004). +-.TP +-.B \-da\- +- +-Works like \-ta, but for specifying large date offsets, to be used when +-fixing dates from cameras where the date was set incorrectly, such as +-having date and time reset by battery removal on some cameras +- +-Because different months and years have different numbers of days in +-them, a simple offset for months, days, years would lead to unexpected +-results at times. The time offset is thus specified as a difference +-between two dates, so that jhead can figure out exactly how many days +-the timestamp needs to be adjusted by, including leap years and daylight +-savings time changes. The dates are specified as yyyy:mm:dd. For +-sub-day adjustments, a time of day can also be included, by specifying +-yyyy:nn:dd/hh:mm or yyyy:mm:dd/hh:mm:ss +- +-Examples: +- +-Year on camera was set to 2005 instead of 2004 for pictures taken in April +-.br +-jhead \-da2004:03:01\-2005:03:01 +- +-Default camera date is 2002:01:01, and date was reset on 2005:05:29 at 11:21 am +-.br +-jhead \-da2005:05:29/11:21\-2002:01:01 +-.TP +-.B \-ts +-Sets the time stored in the Exif header to what is specified on the +-command line. +-Time must be specified as: +-.I yyyy:mm:dd\-hh:mm:ss +-.TP +-.B \-ds +-Sets the date stored in the Exif header to what is specified on the +-command line. +-Can be used to set date, just year and month, or just year. +-Date is specified as: +-.I yyyy:mm:dd, yyyy:mm, or yyyy +- +-.SH THUMBNAIL MANIPULATION OPTIONS +- +-.TP +-.B \-dt +-Delete thumbnails from the Exif header, but leave the interesting parts +-intact. This option truncates the thumbnail from the Exif header, +-provided that the thumbnail is the last part of the Exif header (which +-so far as I know is always the case). Exif headers have a built-in +-thumbnail, which typically occupies around 10k of space. This thumbnail +-is used by digital cameras. Windows XP may also use this thumbnail if +-present (but it doesn't need it). The thumbnails are too small to use +-even full screen on the digicam's LCD. I have not encountered any +-adverse side effects of deleting the thumbnails, even from the software +-provided with my old Olympus digicam. Use with caution. +- +-.TP +-.BI \-\^st \ file +-Save the integral thumbnail to +-.I file +-The thumbnail lives inside the Exif header, and is a very low-res JPEG +-image. Note that making any changes to a photo, except for with some +-programs, generally wipes out the Exif header and with it the thumbnail. +- +-The thumbnail is too low res to really use for very much. +- +-This feature has an interesting 'relative path' option for specifying +-the thumbnail name. Whenever the name for +-.I file +-contains the characters '&i', +-.B jhead +-will substitute the original filename for this name. This allows +-creating a 'relative name' when doing a whole batch of files. For +-example, the incantation: +- +-.I jhead \-st """thumbnails/&i"" *.jpg +- +-would create a thumbnail for each .jpg file in the thumbnails directory +-by the same name, (provided that the thumbnails directory exists, of +-course). Both Win32 and UNIX shells treat the '&'character in a special +-way, so you have to put quotes around that command line option for the '&' +-to even be passed to the program. +- +-If a '\-' is specified for the output file, the thumbnail is sent to +-stdout. (UNIX build only) +- +-.TP +-.B \-rt +-Replace thumbnails from the Exif header. This only works if the exif +-header already contains a thumbnail, and the thumbnail is at the end of +-the header (both always the case if the photo came from a digital +-camera) +-.TP +-.BI \-\^rgt \ size +-Regenerate exif thumbnail. 'size' specifies maximum height or width of +-thumbnail. Relies on 'mogrify' program (from ImageMagick) to regenerate +-the thumbnail. This only works if the image already contains a +-thumbnail. +- +-.SH ROTATION OPTIONS +-.TP +-.B \-autorot +-Using the 'Orientation' tag of the Exif header, rotate the image so that +-it is upright. The program +-.B jpegtran +-is used to perform the rotation. This program is present in most Linux +-distributions. For windows, you need to get a copy of it. After +-rotation, the orientation tag of the Exif header is set to '1' (normal +-orientation). The thumbnail is also rotated. Other fields of the Exif +-header, including dimensions are untouched, but the JPEG height/width +-are adjusted. This feature is especially useful with newer Canon +-cameras, that set the orientation tag automatically using a gravity +-sensor. +-.TP +-.B \-norot +-Clears the rotation field in the Exif header without altering the image. +-Useful if the images were previously rotated without clearing the Exif +-rotation tag, as some image browsers will auto rotate images when the +-rotation tag is set. Sometimes, thumbnails and rotation tags can get +-very out of sync from manipulation with various tools. To reset it all +-use \-norot with \-rgt to clear this out. +- +-.SH OUTPUT VERBOSITY CONTROL +-.TP +-.B \-h +-Displays summary of command line options. +-.TP +-.B \-v +-Makes the program even more verbose than it already is. Like DOS +-programs, and unlike UNIX programs, Jhead gives feedback as to what it +-is doing, even when nothing goes wrong. Windows user that I am, when +-something doesn't give me feedback for 20 seconds, I assume its crashed. +-.TP +-.B \-q +-No output on success, more like Unix programs. +-.TP +-.B \-V +-Print version info and compilation date. +-.B \-exifmap +-Show a map of the bytes in the exif header. Useful when analyzing +-strange exif headers, not of much use to non software developers. +-.TP +-.B \-se +-Suppress error messages relating to corrupt Exif header structure. +-.TP +-.B \-c +-Concise output. This causes picture info to be summarized on one line +-instead of several. Useful for grep-ing through images, as well as +-importing into spread sheets (data is space delimited with quotes as +-text qualifier). +- +-.SH FILE MATCHING OPTIONS +-.TP +-.B \-model +-Restricts processing of files to those whose camera model, as indicated +-by the Exif image information, contains the substring specified in the +-argument after '\-model'. For example, the following command will list +-only images that are from an S100 camera: +- +-.I jhead \-model S100 *.jpg +- +-I use this option to restrict my JPEG recompensing to those images that +-came from my Canon S100 digicam, (see the \-cmd option). +-.TP +-.B \-exonly +-Skip all files that don't have an Exif header. Photos straight from a +-digital camera have an Exif header, whereas many photo manipulation +-tools discard the Exif header. +-.TP +-.B \-cmd +-Executes the specified command on each JPEG file to be processed. +- +-The Exif section of each file is read before running the command, and +-reinserted after the command finishes. +- +-The specified command invoked separately for each JPEG that is +-processed, even if multiple files are specified (explicitly or by wild +-card). +- +-Example use: +- +-Having a whole directory of photos from my S100, I run the following commands: +- +-.I jhead \-cmd """mogrify \-quality 80 &i"" \-model S100 *.jpg +-.br +-.I jhead \-cmd """jpegtran \-progressive &i > &o"" *.jpg +- +-The first command mogrifies all JPEGs in the tree that indicate that +-they are from a Canon S100 in their Exif header to 80% quality at the +-same resolution. This is a 'lossy' process, so I only run it on files +-that are from the Canon, and only run it once. The next command then +-takes a JPEGs and converts them to progressive JPEGs. The result is the +-same images, with no discernible differences, stored in half the space. +-This produces substantial savings on some cameras. +- +-.SH SEE ALSO +-.BR jpegtran (1), +-.BR mogrify (1), +-.BR rdjpgcom (1), +-.BR wrjpgcom (1) +-.SH AUTHOR +-Matthias Wandel +-.SH BUGS +-After jhead runs a program to rotate or resize an image, the image +-dimensions and thumbnail in the Exif header are not adjusted. +-.PP +-Modifying of Exif header data is very limited, as Jhead internally only +-has a read only implementation of the file system contained in the Exif +-header. For example, there is no way to replace the thumbnail or edit +-the Exif comment in the Exif header. There is also no way to create +-minimal exif headers. +-.PP +-Some Canon digital SLR cameras fail to adjust the effective sensor +-resolution when shooting at less than full resolution, causing jhead to +-incorrectly miscalculate the sensor width and 35mm equivalent focal +-length. The same can result from resizing photos with Photoshop, which +-will manipulate parts of the exif header. This is often reported as a +-bug in Jhead, but Jhead can't do much about incorrect data. +-.PP +-Send bug reports to mwandel at sentex dot net. +- +-.SH COPYING PERMISSIONS +-Jhead is 'public domain'. You may freely copy jhead, and reuse part or +-all of its code in free or proprietary programs. I do however request +-that you do not post my e-mail address in ways that spam robots can +-harvest it. +diff --git a/src/external/jhead-3.04/jhead.c b/src/external/jhead-3.04/jhead.c +deleted file mode 100644 +index f78127a..0000000 +--- a/src/external/jhead-3.04/jhead.c ++++ /dev/null +@@ -1,1771 +0,0 @@ +-//-------------------------------------------------------------------------- +-// Program to pull the information out of various types of EXIF digital +-// camera files and show it in a reasonably consistent way +-// +-// Version 3.04 +-// +-// Compiling under Windows: +-// Make sure you have Microsoft's compiler on the path, then run make.bat +-// +-// Dec 1999 - Nov 2019 +-// +-// by Matthias Wandel www.sentex.net/~mwandel +-//-------------------------------------------------------------------------- +-#ifdef _WIN32 +- #include +-#endif +- +-#include "jhead.h" +- +-#include +- +-#define JHEAD_VERSION "3.04" +- +-// This #define turns on features that are too very specific to +-// how I organize my photos. Best to ignore everything inside #ifdef MATTHIAS +-//#define MATTHIAS +- +- +-// Bitmasks for DoModify: +-#define MODIFY_ANY 1 +-#define READ_ANY 2 +-#define JPEGS_ONLY 4 +-#define MODIFY_JPEG 5 +-#define READ_JPEG 6 +-static int DoModify = FALSE; +- +- +-static int FilesMatched; +-static int FileSequence; +- +-static const char * CurrentFile; +- +-static const char * progname; // program name for error messages +- +-//-------------------------------------------------------------------------- +-// Command line options flags +-static int TrimExif = FALSE; // Cut off exif beyond interesting data. +-static int RenameToDate = 0; // 1=rename, 2=rename all. +-#ifdef _WIN32 +-static int RenameAssociatedFiles = FALSE; +-#endif +-static char * strftime_args = NULL; // Format for new file name. +-static int Exif2FileTime = FALSE; +- int ShowTags = FALSE; // Do not show raw by default. +-static int Quiet = FALSE; // Be quiet on success (like unix programs) +- int DumpExifMap = FALSE; +-static int ShowConcise = FALSE; +-static int CreateExifSection = FALSE; +-static int TrimExifTrailingZeroes = FALSE; +-static char * ApplyCommand = NULL; // Apply this command to all images. +-static char * FilterModel = NULL; +-static int FilterQuality = 0; +-static int ExifOnly = FALSE; +-static int PortraitOnly = FALSE; +-static time_t ExifTimeAdjust = 0; // Timezone adjust +-static time_t ExifTimeSet = 0; // Set exif time to a value. +-static char DateSet[11]; +-static unsigned DateSetChars = 0; +-static unsigned FileTimeToExif = FALSE; +- +-static int DeleteComments = FALSE; +-static int DeleteExif = FALSE; +-static int DeleteIptc = FALSE; +-static int DeleteXmp = FALSE; +-static int DeleteUnknown = FALSE; +-static char * ThumbSaveName = NULL; // If not NULL, use this string to make up +- // the filename to store the thumbnail to. +- +-static char * ThumbInsertName = NULL; // If not NULL, use this string to make up +- // the filename to retrieve the thumbnail from. +- +-static int RegenThumbnail = FALSE; +- +-static char * ExifXferScrFile = NULL;// Extract Exif header from this file, and +- // put it into the Jpegs processed. +- +-static int EditComment = FALSE; // Invoke an editor for editing the comment +-static int SuppressNonFatalErrors = FALSE; // Wether or not to pint warnings on recoverable errors +- +-static char * CommentSavefileName = NULL; // Save comment to this file. +-static char * CommentInsertfileName = NULL; // Insert comment from this file. +-static char * CommentInsertLiteral = NULL; // Insert this comment (from command line) +- +-static int AutoRotate = FALSE; +-static int ZeroRotateTagOnly = FALSE; +- +-static int ShowFileInfo = TRUE; // Indicates to show standard file info +- // (file name, file size, file date) +- +- +-#ifdef MATTHIAS +- // This #ifdef to take out less than elegant stuff for editing +- // the comments in a JPEG. The programs rdjpgcom and wrjpgcom +- // included with Linux distributions do a better job. +- +- static char * AddComment = NULL; // Add this tag. +- static char * RemComment = NULL; // Remove this tag +- static int AutoResize = FALSE; +-#endif // MATTHIAS +- +-//-------------------------------------------------------------------------- +-// Error exit handler +-//-------------------------------------------------------------------------- +-void ErrFatal(const char * msg) +-{ +- fprintf(stderr,"\nError : %s\n", msg); +- if (CurrentFile) fprintf(stderr,"in file '%s'\n",CurrentFile); +- exit(EXIT_FAILURE); +-} +- +-//-------------------------------------------------------------------------- +-// Report non fatal errors. Now that microsoft.net modifies exif headers, +-// there's corrupted ones, and there could be more in the future. +-//-------------------------------------------------------------------------- +-void ErrNonfatal(const char * msg, int a1, int a2) +-{ +- if (SuppressNonFatalErrors) return; +- +- fprintf(stderr,"\nNonfatal Error : "); +- if (CurrentFile) fprintf(stderr,"'%s' ",CurrentFile); +- fprintf(stderr, msg, a1, a2); +- fprintf(stderr, "\n"); +-} +- +- +-//-------------------------------------------------------------------------- +-// Invoke an editor for editing a string. +-//-------------------------------------------------------------------------- +-static int FileEditComment(char * TempFileName, char * Comment, int CommentSize) +-{ +- FILE * file; +- int a; +- char QuotedPath[PATH_MAX+10]; +- +- file = fopen(TempFileName, "w"); +- if (file == NULL){ +- fprintf(stderr, "Can't create file '%s'\n",TempFileName); +- ErrFatal("could not create temporary file"); +- } +- fwrite(Comment, CommentSize, 1, file); +- +- fclose(file); +- +- fflush(stdout); // So logs are contiguous. +- +- { +- char * Editor; +- Editor = getenv("EDITOR"); +- if (Editor == NULL){ +-#ifdef _WIN32 +- Editor = "notepad"; +-#else +- Editor = "vi"; +-#endif +- } +- if (strlen(Editor) > PATH_MAX) ErrFatal("env too long"); +- +- sprintf(QuotedPath, "%s \"%s\"",Editor, TempFileName); +- a = system(QuotedPath); +- } +- +- if (a != 0){ +- perror("Editor failed to launch"); +- exit(-1); +- } +- +- file = fopen(TempFileName, "r"); +- if (file == NULL){ +- ErrFatal("could not open temp file for read"); +- } +- +- // Read the file back in. +- CommentSize = fread(Comment, 1, MAX_COMMENT_SIZE, file); +- +- fclose(file); +- +- unlink(TempFileName); +- +- return CommentSize; +-} +- +-#ifdef MATTHIAS +-//-------------------------------------------------------------------------- +-// Modify one of the lines in the comment field. +-// This very specific to the photo album program stuff. +-//-------------------------------------------------------------------------- +-static char KnownTags[][10] = {"date", "desc","scan_date","author", +- "keyword","videograb", +- "show_raw","panorama","titlepix",""}; +- +-static int ModifyDescriptComment(char * OutComment, char * SrcComment) +-{ +- char Line[500]; +- int Len; +- int a,i; +- unsigned l; +- int HasScandate = FALSE; +- int TagExists = FALSE; +- int Modified = FALSE; +- Len = 0; +- +- OutComment[0] = 0; +- +- +- for (i=0;;i++){ +- if (SrcComment[i] == '\r' || SrcComment[i] == '\n' || SrcComment[i] == 0 || Len >= 199){ +- // Process the line. +- if (Len > 0){ +- Line[Len] = 0; +- //printf("Line: '%s'\n",Line); +- for (a=0;;a++){ +- l = strlen(KnownTags[a]); +- if (!l){ +- // Unknown tag. Discard it. +- printf("Error: Unknown tag '%s'\n", Line); // Deletes the tag. +- Modified = TRUE; +- break; +- } +- if (memcmp(Line, KnownTags[a], l) == 0){ +- if (Line[l] == ' ' || Line[l] == '=' || Line[l] == 0){ +- // Its a good tag. +- if (Line[l] == ' ') Line[l] = '='; // Use equal sign for clarity. +- if (a == 2) break; // Delete 'orig_path' tag. +- if (a == 3) HasScandate = TRUE; +- if (RemComment){ +- if (strlen(RemComment) == l){ +- if (!memcmp(Line, RemComment, l)){ +- Modified = TRUE; +- break; +- } +- } +- } +- if (AddComment){ +- // Overwrite old comment of same tag with new one. +- if (!memcmp(Line, AddComment, l+1)){ +- TagExists = TRUE; +- strncpy(Line, AddComment, sizeof(Line)); +- Line[sizeof(Line)-1]='\0'; +- Modified = TRUE; +- } +- } +- strncat(OutComment, Line, MAX_COMMENT_SIZE-5-strlen(OutComment)); +- strcat(OutComment, "\n"); +- break; +- } +- } +- } +- } +- Line[Len = 0] = 0; +- if (SrcComment[i] == 0) break; +- }else{ +- Line[Len++] = SrcComment[i]; +- } +- } +- +- if (AddComment && TagExists == FALSE){ +- strncat(OutComment, AddComment, MAX_COMMENT_SIZE-5-strlen(OutComment)); +- strcat(OutComment, "\n"); +- Modified = TRUE; +- } +- +- if (!HasScandate && !ImageInfo.DateTime[0]){ +- // Scan date is not in the file yet, and it doesn't have one built in. Add it. +- char Temp[40]; +- sprintf(Temp, "scan_date=%s", ctime(&ImageInfo.FileDateTime)); +- strncat(OutComment, Temp, MAX_COMMENT_SIZE-5-strlen(OutComment)); +- Modified = TRUE; +- } +- return Modified; +-} +-//-------------------------------------------------------------------------- +-// Automatic make smaller command stuff +-//-------------------------------------------------------------------------- +-static int AutoResizeCmdStuff(void) +-{ +- static char CommandString[PATH_MAX+1]; +- double scale; +- float TargetSize = 1800; +- +- ApplyCommand = CommandString; +- +- scale = TargetSize / ImageInfo.Width; +- if (scale > TargetSize / ImageInfo.Height) scale = TargetSize / ImageInfo.Height; +- +- if (scale > 0.8){ +- if (ImageInfo.QualityGuess >= 93){ +- // Re-compress at lower quality. +- sprintf(CommandString, "mogrify -quality 86 &i"); +- return TRUE; +- } +- printf("not resizing %dx%x '%s'\n",ImageInfo.Height, ImageInfo.Width, ImageInfo.FileName); +- return FALSE; +- } +- +- if (scale < 0.4) scale = 0.4; // Don't scale down by too much. +- +- sprintf(CommandString, "mogrify -geometry %dx%d -quality 85 &i",(int)(ImageInfo.Width*scale+0.5), +- (int)(ImageInfo.Height*scale+0.5)); +- return TRUE; +-} +- +- +-#endif // MATTHIAS +- +- +-//-------------------------------------------------------------------------- +-// Escape an argument such that it is interpreted literally by the shell +-// (returns the number of written characters) +-//-------------------------------------------------------------------------- +-static int shellescape(char* to, const char* from) +-{ +- int i, j; +- i = j = 0; +- +- // Enclosing characters in double quotes preserves the literal value of +- // all characters within the quotes, with the exception of $, `, and \. +- to[j++] = '"'; +- while(from[i]) +- { +-#ifdef _WIN32 +- // Under WIN32, there isn't really anything dangerous you can do with +- // escape characters, plus windows users aren't as sercurity paranoid. +- // Hence, no need to do fancy escaping. +- to[j++] = from[i++]; +-#else +- switch(from[i]) { +- case '"': +- case '$': +- case '`': +- case '\\': +- to[j++] = '\\'; +- // Fallthru... +- default: +- to[j++] = from[i++]; +- } +-#endif +- if (j >= PATH_MAX) ErrFatal("max path exceeded"); +- } +- to[j++] = '"'; +- return j; +-} +- +- +-//-------------------------------------------------------------------------- +-// Apply the specified command to the JPEG file. +-//-------------------------------------------------------------------------- +-static void DoCommand(const char * FileName, int ShowIt) +-{ +- int a,e; +- char ExecString[PATH_MAX*3]; +- char TempName[PATH_MAX+10]; +- int TempUsed = FALSE; +- +- e = 0; +- +- // Generate an unused temporary file name in the destination directory +- // (a is the number of characters to copy from FileName) +- a = strlen(FileName)-1; +- while(a > 0 && FileName[a-1] != SLASH) a--; +- memcpy(TempName, FileName, a); +- strcpy(TempName+a, "XXXXXX"); +- +- // Note: Compiler will warn about mkstemp. but I need a filename, not a file. +- // I could just then get the file name from what mkstemp made, and pass that +- // to the executable, but that would make for the exact same vulnerability +- // as mktemp - that is, that between getting the random name, and making the file +- // some other program could snatch that exact same name! +- // also, not all platforms support mkstemp. +- mktemp(TempName); +- +- +- if(!TempName[0]) { +- ErrFatal("Cannot find available temporary file name"); +- } +- +- +- // Build the exec string. &i and &o in the exec string get replaced by input and output files. +- for (a=0;;a++){ +- if (ApplyCommand[a] == '&'){ +- if (ApplyCommand[a+1] == 'i'){ +- // Input file. +- e += shellescape(ExecString+e, FileName); +- a += 1; +- continue; +- } +- if (ApplyCommand[a+1] == 'o'){ +- // Needs an output file distinct from the input file. +- e += shellescape(ExecString+e, TempName); +- a += 1; +- TempUsed = TRUE; +- continue; +- } +- } +- ExecString[e++] = ApplyCommand[a]; +- if (ApplyCommand[a] == 0) break; +- } +- +- if (ShowIt) printf("Cmd:%s\n",ExecString); +- +- errno = 0; +- a = system(ExecString); +- +- if (a || errno){ +- // A command can however fail without errno getting set or system returning an error. +- if (errno) perror("system"); +- ErrFatal("Problem executing specified command"); +- } +- +- if (TempUsed){ +- // Don't delete original file until we know a new one was created by the command. +- struct stat dummy; +- if (stat(TempName, &dummy) == 0){ +- struct stat buf; +- int stat_result = stat(FileName, &buf); +- +- unlink(FileName); +- rename(TempName, FileName); +- if (stat_result == 0){ +- // set Unix access rights and time to new file +- struct utimbuf mtime; +- chmod(FileName, buf.st_mode); +- +- mtime.actime = buf.st_atime; +- mtime.modtime = buf.st_mtime; +- +- utime(FileName, &mtime); +- } +- }else{ +- ErrFatal("specified command did not produce expected output file"); +- } +- } +-} +- +-//-------------------------------------------------------------------------- +-// check if this file should be skipped based on contents. +-//-------------------------------------------------------------------------- +-static int CheckFileSkip(void) +-{ +- // I sometimes add code here to only process images based on certain +- // criteria - for example, only to convert non progressive Jpegs to progressives, etc.. +- +- if (FilterModel){ +- // Filtering processing by camera model. +- // This feature is useful when pictures from multiple cameras are colated, +- // the its found that one of the cameras has the time set incorrectly. +- if (strstr(ImageInfo.CameraModel, FilterModel) == NULL){ +- // Skip. +- return TRUE; +- } +- } +- if (FilterQuality > 0){ +- //Filter by above threshold quality +- if (ImageInfo.QualityGuess < FilterQuality){ +- return TRUE; +- } +- } +- +- if (ExifOnly){ +- // Filtering by EXIF only. Skip all files that have no Exif. +- if (FindSection(M_EXIF) == NULL){ +- return TRUE; +- } +- } +- +- if (PortraitOnly == 1){ +- if (ImageInfo.Width > ImageInfo.Height) return TRUE; +- } +- +- if (PortraitOnly == -1){ +- if (ImageInfo.Width < ImageInfo.Height) return TRUE; +- } +- +- return FALSE; +-} +- +-//-------------------------------------------------------------------------- +-// Subsititute original name for '&i' if present in specified name. +-// This to allow specifying relative names when manipulating multiple files. +-//-------------------------------------------------------------------------- +-static void RelativeName(char * OutFileName, const char * NamePattern, const char * OrigName) +-{ +- // If the filename contains substring "&i", then substitute the +- // filename for that. This gives flexibility in terms of processing +- // multiple files at a time. +- char * Subst; +- Subst = strstr(NamePattern, "&i"); +- if (Subst){ +- strncpy(OutFileName, NamePattern, Subst-NamePattern); +- OutFileName[Subst-NamePattern] = 0; +- strncat(OutFileName, OrigName, PATH_MAX); +- strncat(OutFileName, Subst+2, PATH_MAX); +- }else{ +- strncpy(OutFileName, NamePattern, PATH_MAX); +- } +-} +- +- +-#ifdef _WIN32 +-//-------------------------------------------------------------------------- +-// Rename associated files +-//-------------------------------------------------------------------------- +-void RenameAssociated(const char * FileName, char * NewBaseName) +-{ +- int a; +- int PathLen; +- int ExtPos; +- char FilePattern[_MAX_PATH+1]; +- char NewName[_MAX_PATH+1]; +- struct _finddata_t finddata; +- long find_handle; +- +- for(ExtPos = strlen(FileName);FileName[ExtPos-1] != '.';){ +- if (--ExtPos == 0) return; // No extension! +- } +- +- memcpy(FilePattern, FileName, ExtPos); +- FilePattern[ExtPos] = '*'; +- FilePattern[ExtPos+1] = '\0'; +- +- for(PathLen = strlen(FileName);FileName[PathLen-1] != SLASH;){ +- if (--PathLen == 0) break; +- } +- +- find_handle = _findfirst(FilePattern, &finddata); +- +- for (;;){ +- if (find_handle == -1) break; +- +- // Eliminate the obvious patterns. +- if (!memcmp(finddata.name, ".",2)) goto next_file; +- if (!memcmp(finddata.name, "..",3)) goto next_file; +- if (finddata.attrib & _A_SUBDIR) goto next_file; +- +- strncpy(FilePattern+PathLen, finddata.name, PATH_MAX-PathLen); // full name with path +- +- strcpy(NewName, NewBaseName); +- for(a = strlen(finddata.name);finddata.name[a] != '.';){ +- if (--a == 0) goto next_file; +- } +- strncat(NewName, finddata.name+a, _MAX_PATH-strlen(NewName)); // add extension to new name +- +- if (rename(FilePattern, NewName) == 0){ +- if (!Quiet){ +- printf("%s --> %s\n",FilePattern, NewName); +- } +- } +- +- next_file: +- if (_findnext(find_handle, &finddata) != 0) break; +- } +- _findclose(find_handle); +-} +-#endif +- +-//-------------------------------------------------------------------------- +-// Handle renaming of files by date. +-//-------------------------------------------------------------------------- +-static void DoFileRenaming(const char * FileName) +-{ +- int PrefixPart = 0; // Where the actual filename starts. +- int ExtensionPart; // Where the file extension starts. +- int a; +- struct tm tm; +- char NewBaseName[PATH_MAX*2]; +- int AddLetter = 0; +- char NewName[PATH_MAX+2]; +- +- ExtensionPart = strlen(FileName); +- for (a=0;FileName[a];a++){ +- if (FileName[a] == SLASH){ +- // Don't count path component. +- PrefixPart = a+1; +- } +- +- if (FileName[a] == '.') ExtensionPart = a; // Remember where extension starts. +- } +- +- if (!Exif2tm(&tm, ImageInfo.DateTime)){ +- printf("File '%s' contains no exif date stamp. Using file date\n",FileName); +- // Use file date/time instead. +- tm = *localtime(&ImageInfo.FileDateTime); +- } +- +- +- strncpy(NewBaseName, FileName, PATH_MAX); // Get path component of name. +- +- if (strftime_args){ +- // Complicated scheme for flexibility. Just pass the args to strftime. +- time_t UnixTime; +- +- char *s; +- char pattern[PATH_MAX+20]; +- int n = ExtensionPart - PrefixPart; +- +- // Call mktime to get weekday and such filled in. +- UnixTime = mktime(&tm); +- if ((int)UnixTime == -1){ +- printf("Could not convert %s to unix time",ImageInfo.DateTime); +- return; +- } +- +- // Substitute "%f" for the original name (minus path & extension) +- pattern[PATH_MAX-1]=0; +- strncpy(pattern, strftime_args, PATH_MAX-1); +- while ((s = strstr(pattern, "%f")) && strlen(pattern) + n < PATH_MAX-1){ +- memmove(s + n, s + 2, strlen(s+2) + 1); +- memmove(s, FileName + PrefixPart, n); +- } +- +- { +- // Sequential number renaming part. +- // '%i' type pattern becomes sequence number. +- int ppos = -1; +- for (a=0;pattern[a];a++){ +- if (pattern[a] == '%'){ +- ppos = a; +- }else if (pattern[a] == 'i'){ +- if (ppos >= 0 && a= PATH_MAX) ErrFatal("str overflow"); +- memmove(pattern+ppos+nl, pattern+a+1, l+1); +- memcpy(pattern+ppos, num, nl); +- break; +- } +- }else if (!isdigit(pattern[a])){ +- ppos = -1; +- } +- } +- } +- strftime(NewName, PATH_MAX, pattern, &tm); +- }else{ +- // My favourite scheme. +- sprintf(NewName, "%02d%02d-%02d%02d%02d", +- tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); +- } +- +- NewBaseName[PrefixPart] = 0; +- CatPath(NewBaseName, NewName); +- +- AddLetter = isdigit(NewBaseName[strlen(NewBaseName)-1]); +- for (a=0;;a++){ +- char NewName[PATH_MAX+10]; +- char NameExtra[3]; +- struct stat dummy; +- +- if (a){ +- // Generate a suffix for the file name if previous choice of names is taken. +- // depending on wether the name ends in a letter or digit, pick the opposite to separate +- // it. This to avoid using a separator character - this because any good separator +- // is before the '.' in ascii, and so sorting the names would put the later name before +- // the name without suffix, causing the pictures to more likely be out of order. +- if (AddLetter){ +- NameExtra[0] = (char)('a'-1+a); // Try a,b,c,d... for suffix if it ends in a number. +- }else{ +- NameExtra[0] = (char)('0'-1+a); // Try 0,1,2,3... for suffix if it ends in a latter. +- } +- NameExtra[1] = 0; +- }else{ +- NameExtra[0] = 0; +- } +- +- snprintf(NewName, sizeof(NewName), "%s%s.jpg", NewBaseName, NameExtra); +- +- if (!strcmp(FileName, NewName)) break; // Skip if its already this name. +- +- if (!EnsurePathExists(NewBaseName)){ +- break; +- } +- +- +- if (stat(NewName, &dummy)){ +- // This name does not pre-exist. +- if (rename(FileName, NewName) == 0){ +- printf("%s --> %s\n",FileName, NewName); +-#ifdef _WIN32 +- if (RenameAssociatedFiles){ +- sprintf(NewName, "%s%s", NewBaseName, NameExtra); +- RenameAssociated(FileName, NewName); +- } +-#endif +- }else{ +- printf("Error: Couldn't rename '%s' to '%s'\n",FileName, NewName); +- } +- break; +- +- } +- +- if (a > 25 || (!AddLetter && a > 9)){ +- printf("Possible new names for for '%s' already exist\n",FileName); +- break; +- } +- } +-} +- +-//-------------------------------------------------------------------------- +-// Rotate the image and its thumbnail +-//-------------------------------------------------------------------------- +-static int DoAutoRotate(const char * FileName) +-{ +- if (ImageInfo.Orientation != 1){ +- const char * Argument; +- Argument = ClearOrientation(); +- if (Argument == NULL) return FALSE; // orientation tag in image, nothing changed. +- +- if (!ZeroRotateTagOnly){ +- char RotateCommand[PATH_MAX*2+50]; +- if (strlen(Argument) == 0){ +- // Unknown orientation, but still modified. +- return TRUE; // Image is still modified. +- } +- sprintf(RotateCommand, "jpegtran -trim -%s -outfile &o &i", Argument); +- ApplyCommand = RotateCommand; +- DoCommand(FileName, FALSE); +- ApplyCommand = NULL; +- +- // Now rotate the thumbnail, if there is one. +- if (ImageInfo.ThumbnailOffset && +- ImageInfo.ThumbnailSize && +- ImageInfo.ThumbnailAtEnd){ +- // Must have a thumbnail that exists and is modifieable. +- +- char ThumbTempName_in[PATH_MAX+5]; +- char ThumbTempName_out[PATH_MAX+5]; +- +- strcpy(ThumbTempName_in, FileName); +- strcat(ThumbTempName_in, ".thi"); +- strcpy(ThumbTempName_out, FileName); +- strcat(ThumbTempName_out, ".tho"); +- SaveThumbnail(ThumbTempName_in); +- sprintf(RotateCommand,"jpegtran -trim -%s -outfile \"%s\" \"%s\"", +- Argument, ThumbTempName_out, ThumbTempName_in); +- +- if (system(RotateCommand) == 0){ +- // Put the thumbnail back in the header +- ReplaceThumbnail(ThumbTempName_out); +- } +- +- unlink(ThumbTempName_in); +- unlink(ThumbTempName_out); +- } +- } +- return TRUE; +- } +- return FALSE; +-} +- +-//-------------------------------------------------------------------------- +-// Regenerate the thumbnail using mogrify +-//-------------------------------------------------------------------------- +-static int RegenerateThumbnail(const char * FileName) +-{ +- char ThumbnailGenCommand[PATH_MAX*2+50]; +- if (ImageInfo.ThumbnailOffset == 0 || ImageInfo.ThumbnailAtEnd == FALSE){ +- // There is no thumbnail, or the thumbnail is not at the end. +- return FALSE; +- } +- +- sprintf(ThumbnailGenCommand, "mogrify -thumbnail %dx%d -quality 80 \"%s\"", +- RegenThumbnail, RegenThumbnail, FileName); +- +- if (system(ThumbnailGenCommand) == 0){ +- // Put the thumbnail back in the header +- return ReplaceThumbnail(FileName); +- }else{ +- ErrFatal("Unable to run 'mogrify' command"); +- return FALSE; +- } +-} +- +-//-------------------------------------------------------------------------- +-// Set file time as exif time. +-//-------------------------------------------------------------------------- +-void FileTimeAsString(char * TimeStr) +-{ +- struct tm ts; +- ts = *localtime(&ImageInfo.FileDateTime); +- strftime(TimeStr, 20, "%Y:%m:%d %H:%M:%S", &ts); +-} +- +-//-------------------------------------------------------------------------- +-// Do selected operations to one file at a time. +-//-------------------------------------------------------------------------- +-static void ProcessFile(const char * FileName) +-{ +- int Modified = FALSE; +- ReadMode_t ReadMode; +- +- if (strlen(FileName) >= PATH_MAX-1){ +- // Protect against buffer overruns in strcpy / strcat's on filename +- ErrFatal("filename too long"); +- } +- +- ReadMode = READ_METADATA; +- CurrentFile = FileName; +- FilesMatched = 1; +- +- ResetJpgfile(); +- Clear_EXIF(); +- +- // Start with an empty image information structure. +- memset(&ImageInfo, 0, sizeof(ImageInfo)); +- ImageInfo.FlashUsed = -1; +- ImageInfo.MeteringMode = -1; +- ImageInfo.Whitebalance = -1; +- +- // Store file date/time. +- { +- struct stat st; +- if (stat(FileName, &st) >= 0){ +- ImageInfo.FileDateTime = st.st_mtime; +- ImageInfo.FileSize = st.st_size; +- }else{ +- ErrFatal("No such file"); +- } +- } +- +- if ((DoModify & MODIFY_ANY) || RenameToDate || Exif2FileTime){ +- if (access(FileName, 2 /*W_OK*/)){ +- printf("Skipping readonly file '%s'\n",FileName); +- return; +- } +- } +- +- strncpy(ImageInfo.FileName, FileName, PATH_MAX); +- +- +- if (ApplyCommand || AutoRotate){ +- // Applying a command is special - the headers from the file have to be +- // pre-read, then the command executed, and then the image part of the file read. +- +- if (!ReadJpegFile(FileName, READ_METADATA)) return; +- +- #ifdef MATTHIAS +- if (AutoResize){ +- // Automatic resize computation - to customize for each run... +- if (AutoResizeCmdStuff() == 0){ +- DiscardData(); +- return; +- } +- } +- #endif // MATTHIAS +- +- +- if (CheckFileSkip()){ +- DiscardData(); +- return; +- } +- +- DiscardAllButExif(); +- +- if (AutoRotate){ +- if (DoAutoRotate(FileName)){ +- Modified = TRUE; +- } +- }else{ +- struct stat dummy; +- DoCommand(FileName, Quiet ? FALSE : TRUE); +- +- if (stat(FileName, &dummy)){ +- // The file is not there anymore. Perhaps the command +- // was a delete or a move. So we are all done. +- return; +- } +- Modified = TRUE; +- } +- ReadMode = READ_IMAGE; // Don't re-read exif section again on next read. +- } +- +- if (ExifXferScrFile){ +- char RelativeExifName[PATH_MAX+1]; +- +- // Make a relative name. +- RelativeName(RelativeExifName, ExifXferScrFile, FileName); +- +- if(!ReadJpegFile(RelativeExifName, READ_METADATA)) return; +- +- DiscardAllButExif(); // Don't re-read exif section again on next read. +- +- Modified = TRUE; +- ReadMode = READ_IMAGE; +- } +- +- if (DoModify){ +- ReadMode |= READ_IMAGE; +- } +- +- if (!ReadJpegFile(FileName, ReadMode)) return; +- +- if (CheckFileSkip()){ +- DiscardData(); +- return; +- } +- +- if (TrimExifTrailingZeroes){ +- if (ImageInfo.ThumbnailAtEnd){ +- Section_t * ExifSection; +- int NumRedundant, WasRedundant; +- unsigned char * StartRedundant; +- //printf("Exif: Thumbnail %d - %d\n",ImageInfo.ThumbnailOffset, ImageInfo.ThumbnailOffset+ImageInfo.ThumbnailSize); +- ExifSection = FindSection(M_EXIF); +- +- StartRedundant = ExifSection->Data + 8 + ImageInfo.ThumbnailOffset+ImageInfo.ThumbnailSize; +- WasRedundant = NumRedundant = (ExifSection->Size) - (ImageInfo.ThumbnailOffset + ImageInfo.ThumbnailSize + 8); +- +- //printf("Exif length: %d Wasted: %d\n",ExifSection->Size, NumRedundant); +- +- for(;NumRedundant > 0 && StartRedundant[NumRedundant-1] == 0;NumRedundant--);// Only remove trailing bytes if they are zero. +- +- if (NumRedundant != WasRedundant){ +- int NewExifSize; +- printf("Trimming %d bytes from exif in %s\n", WasRedundant-NumRedundant, FileName); +- NewExifSize = ImageInfo.ThumbnailOffset + ImageInfo.ThumbnailSize + 8 + NumRedundant; +- ExifSection->Data[0] = (uchar)(NewExifSize >> 8); // Must write new length into exif data. +- ExifSection->Data[1] = (uchar)NewExifSize; +- ExifSection->Size = NewExifSize; +- Modified = TRUE; +- }else{ +- //printf("Noting to remove from %s\n", FileName); +- } +- } +- } +- +- FileSequence += 1; // Count files processed. +- +- if (ShowConcise){ +- ShowConciseImageInfo(); +- }else{ +- if (!(DoModify) || ShowTags){ +- ShowImageInfo(ShowFileInfo); +- +- { +- // if IPTC section is present, show it also. +- Section_t * IptcSection; +- IptcSection = FindSection(M_IPTC); +- +- if (IptcSection){ +- show_IPTC(IptcSection->Data, IptcSection->Size); +- } +- } +- printf("\n"); +- } +- } +- +- if (ThumbSaveName){ +- char OutFileName[PATH_MAX+1]; +- // Make a relative name. +- RelativeName(OutFileName, ThumbSaveName, FileName); +- +- if (SaveThumbnail(OutFileName)){ +- printf("Created: '%s'\n", OutFileName); +- } +- } +- +- if (CreateExifSection){ +- // Make a new minimal exif section +- create_EXIF(); +- Modified = TRUE; +- } +- +- if (RegenThumbnail){ +- if (RegenerateThumbnail(FileName)){ +- Modified = TRUE; +- } +- } +- +- if (ThumbInsertName){ +- char ThumbFileName[PATH_MAX+1]; +- // Make a relative name. +- RelativeName(ThumbFileName, ThumbInsertName, FileName); +- +- if (ReplaceThumbnail(ThumbFileName)){ +- Modified = TRUE; +- } +- }else if (TrimExif){ +- // Deleting thumbnail is just replacing it with a null thumbnail. +- if (ReplaceThumbnail(NULL)){ +- Modified = TRUE; +- } +- } +- +- if ( +-#ifdef MATTHIAS +- AddComment || RemComment || +-#endif +- EditComment || CommentInsertfileName || CommentInsertLiteral){ +- +- Section_t * CommentSec; +- char Comment[MAX_COMMENT_SIZE+1]; +- int CommentSize; +- +- CommentSec = FindSection(M_COM); +- +- if (CommentSec == NULL){ +- unsigned char * DummyData; +- +- DummyData = (uchar *) malloc(3); +- DummyData[0] = 0; +- DummyData[1] = 2; +- DummyData[2] = 0; +- CommentSec = CreateSection(M_COM, DummyData, 2); +- } +- +- CommentSize = CommentSec->Size-2; +- if (CommentSize > MAX_COMMENT_SIZE){ +- fprintf(stderr, "Truncating comment at %d chars\n",MAX_COMMENT_SIZE); +- CommentSize = MAX_COMMENT_SIZE; +- } +- +- if (CommentInsertfileName){ +- // Read a new comment section from file. +- char CommentFileName[PATH_MAX+1]; +- FILE * CommentFile; +- +- // Make a relative name. +- RelativeName(CommentFileName, CommentInsertfileName, FileName); +- +- CommentFile = fopen(CommentFileName,"r"); +- if (CommentFile == NULL){ +- printf("Could not open '%s'\n",CommentFileName); +- }else{ +- // Read it in. +- // Replace the section. +- CommentSize = fread(Comment, 1, MAX_COMMENT_SIZE, CommentFile); +- fclose(CommentFile); +- if (CommentSize < 0) CommentSize = 0; +- } +- }else if (CommentInsertLiteral){ +- strncpy(Comment, CommentInsertLiteral, MAX_COMMENT_SIZE); +- CommentSize = strlen(Comment); +- }else{ +-#ifdef MATTHIAS +- char CommentZt[MAX_COMMENT_SIZE+1]; +- memcpy(CommentZt, (char *)CommentSec->Data+2, CommentSize); +- CommentZt[CommentSize] = '\0'; +- if (ModifyDescriptComment(Comment, CommentZt)){ +- Modified = TRUE; +- CommentSize = strlen(Comment); +- } +- if (EditComment) +-#else +- memcpy(Comment, (char *)CommentSec->Data+2, CommentSize); +-#endif +- { +- char EditFileName[PATH_MAX+5]; +- strcpy(EditFileName, FileName); +- strcat(EditFileName, ".txt"); +- +- CommentSize = FileEditComment(EditFileName, Comment, CommentSize); +- } +- } +- +- if (strcmp(Comment, (char *)CommentSec->Data+2)){ +- // Discard old comment section and put a new one in. +- int size; +- size = CommentSize+2; +- free(CommentSec->Data); +- CommentSec->Size = size; +- CommentSec->Data = malloc(size); +- CommentSec->Data[0] = (uchar)(size >> 8); +- CommentSec->Data[1] = (uchar)(size); +- memcpy((CommentSec->Data)+2, Comment, size-2); +- Modified = TRUE; +- } +- if (!Modified){ +- printf("Comment not modified\n"); +- } +- } +- +- +- if (CommentSavefileName){ +- Section_t * CommentSec; +- CommentSec = FindSection(M_COM); +- +- if (CommentSec != NULL){ +- char OutFileName[PATH_MAX+1]; +- FILE * CommentFile; +- +- // Make a relative name. +- RelativeName(OutFileName, CommentSavefileName, FileName); +- +- CommentFile = fopen(OutFileName,"w"); +- if (CommentFile){ +- fwrite((char *)CommentSec->Data+2 ,CommentSec->Size-2, 1, CommentFile); +- fclose(CommentFile); +- }else{ +- ErrFatal("Could not write comment file"); +- } +- }else{ +- printf("File '%s' contains no comment section\n",FileName); +- } +- } +- +- if (ExifTimeAdjust || ExifTimeSet || DateSetChars || FileTimeToExif){ +- if (ImageInfo.numDateTimeTags){ +- struct tm tm; +- time_t UnixTime; +- char TempBuf[50]; +- int a; +- Section_t * ExifSection; +- if (ExifTimeSet){ +- // A time to set was specified. +- UnixTime = ExifTimeSet; +- }else{ +- if (FileTimeToExif){ +- FileTimeAsString(ImageInfo.DateTime); +- } +- if (DateSetChars){ +- memcpy(ImageInfo.DateTime, DateSet, DateSetChars); +- a = 1970; +- sscanf(DateSet, "%d", &a); +- if (a < 1970){ +- strcpy(TempBuf, ImageInfo.DateTime); +- goto skip_unixtime; +- } +- } +- // A time offset to adjust by was specified. +- if (!Exif2tm(&tm, ImageInfo.DateTime)) goto badtime; +- +- // Convert to unix 32 bit time value, add offset, and convert back. +- UnixTime = mktime(&tm); +- if ((int)UnixTime == -1) goto badtime; +- UnixTime += ExifTimeAdjust; +- } +- tm = *localtime(&UnixTime); +- +- // Print to temp buffer first to avoid putting null termination in destination. +- // snprintf() would do the trick, hbut not available everywhere (like FreeBSD 4.4) +- sprintf(TempBuf, "%04d:%02d:%02d %02d:%02d:%02d", +- tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, +- tm.tm_hour, tm.tm_min, tm.tm_sec); +- +-skip_unixtime: +- ExifSection = FindSection(M_EXIF); +- +- for (a = 0; a < ImageInfo.numDateTimeTags; a++) { +- uchar * Pointer; +- Pointer = ExifSection->Data+ImageInfo.DateTimeOffsets[a]+8; +- memcpy(Pointer, TempBuf, 19); +- } +- memcpy(ImageInfo.DateTime, TempBuf, 19); +- +- Modified = TRUE; +- }else{ +- printf("File '%s' contains no Exif timestamp to change\n", FileName); +- } +- } +- +- if (DeleteComments){ +- if (RemoveSectionType(M_COM)) Modified = TRUE; +- } +- if (DeleteExif){ +- if (RemoveSectionType(M_EXIF)) Modified = TRUE; +- } +- if (DeleteIptc){ +- if (RemoveSectionType(M_IPTC)) Modified = TRUE; +- } +- if (DeleteXmp){ +- if (RemoveSectionType(M_XMP)) Modified = TRUE; +- } +- if (DeleteUnknown){ +- if (RemoveUnknownSections()) Modified = TRUE; +- } +- +- +- if (Modified){ +- char BackupName[PATH_MAX+5]; +- struct stat buf; +- +- if (!Quiet) printf("Modified: %s\n",FileName); +- +- strcpy(BackupName, FileName); +- strcat(BackupName, ".t"); +- +- // Remove any .old file name that may pre-exist +- unlink(BackupName); +- +- // Rename the old file. +- rename(FileName, BackupName); +- +- // Write the new file. +- WriteJpegFile(FileName); +- +- // Copy the access rights from original file +- if (stat(BackupName, &buf) == 0){ +- // set Unix access rights and time to new file +- struct utimbuf mtime; +- chmod(FileName, buf.st_mode); +- +- mtime.actime = buf.st_mtime; +- mtime.modtime = buf.st_mtime; +- +- utime(FileName, &mtime); +- } +- +- // Now that we are done, remove original file. +- unlink(BackupName); +- } +- +- +- if (Exif2FileTime){ +- // Set the file date to the date from the exif header. +- if (ImageInfo.numDateTimeTags){ +- // Converte the file date to Unix time. +- struct tm tm; +- time_t UnixTime; +- struct utimbuf mtime; +- if (!Exif2tm(&tm, ImageInfo.DateTime)) goto badtime; +- UnixTime = mktime(&tm); +- if ((int)UnixTime == -1){ +- goto badtime; +- } +- mtime.actime = UnixTime; +- mtime.modtime = UnixTime; +- +- if (utime(FileName, &mtime) != 0){ +- printf("Error: Could not change time of file '%s'\n",FileName); +- }else{ +- if (!Quiet) printf("%s\n",FileName); +- } +- }else{ +- printf("File '%s' contains no Exif timestamp\n", FileName); +- } +- } +- +- // Feature to rename image according to date and time from camera. +- // I use this feature to put images from multiple digicams in sequence. +- +- if (RenameToDate){ +- DoFileRenaming(FileName); +- } +- DiscardData(); +- return; +-badtime: +- printf("Error: Time '%s': cannot convert to Unix time\n",ImageInfo.DateTime); +- DiscardData(); +-} +- +-//-------------------------------------------------------------------------- +-// complain about bad state of the command line. +-//-------------------------------------------------------------------------- +-static void Usage (void) +-{ +- printf("Jhead is a program for manipulating settings and thumbnails in Exif jpeg headers\n" +- "used by most Digital Cameras. v"JHEAD_VERSION" Matthias Wandel, Nov 22 2019.\n" +- "http://www.sentex.net/~mwandel/jhead\n" +- "\n"); +- +- printf("Usage: %s [options] files\n", progname); +- printf("Where:\n" +- " files path/filenames with or without wildcards\n" +- +- "[options] are:\n" +- "\nGENERAL METADATA:\n" +- " -te Transfer exif header from another image file \n" +- " Uses same name mangling as '-st' option\n" +- " -dc Delete comment field (as left by progs like Photoshop & Compupic)\n" +- " -de Strip Exif section (smaller JPEG file, but lose digicam info)\n" +- " -di Delete IPTC section (from Photoshop, or Picasa)\n" +- " -dx Deletex XMP section\n" +- " -du Delete non image sections except for Exif and comment sections\n" +- " -purejpg Strip all unnecessary data from jpeg (combines -dc -de and -du)\n" +- " -mkexif Create new minimal exif section (overwrites pre-existing exif)\n" +- " -ce Edit comment field. Uses environment variable 'editor' to\n" +- " determine which editor to use. If editor not set, uses VI\n" +- " under Unix and notepad with windows\n" +- " -cs Save comment section to a file\n" +- " -ci Insert comment section from a file. -cs and -ci use same naming\n" +- " scheme as used by the -st option\n" +- " -cl string Insert literal comment string\n" +- " -zt Trim exif header trailing zeroes (Nikon 1 wastes 30k that way)\n" +- +- "\nDATE / TIME MANIPULATION:\n" +- " -ft Set file modification time to Exif time\n" +- " -dsft Set Exif time to file modification time\n" +- " -n[format-string]\n" +- " Rename files according to date. Uses exif date if present, file\n" +- " date otherwise. If the optional format-string is not supplied,\n" +- " the format is mmdd-hhmmss. If a format-string is given, it is\n" +- " is passed to the 'strftime' function for formatting\n" +- " %%d Day of month %%H Hour (24-hour)\n" +- " %%m Month number %%M Minute %%S Second\n" +- " %%y Year (2 digit 00 - 99) %%Y Year (4 digit 1980-2036)\n" +- " For more arguments, look up the 'strftime' function.\n" +- " In addition to strftime format codes:\n" +- " '%%f' as part of the string will include the original file name\n" +- " '%%i' will include a sequence number, starting from 1. You can\n" +- " You can specify '%%03i' for example to get leading zeros.\n" +- " This feature is useful for ordering files from multiple digicams to\n" +- " sequence of taking.\n" +- " The '.jpg' is automatically added to the end of the name. If the\n" +- " destination name already exists, a letter or digit is added to \n" +- " the end of the name to make it unique.\n" +- " The new name may include a path as part of the name. If this path\n" +- " does not exist, it will be created\n" +- " -a (Windows only) Rename files with same name but different extension\n" +- " Use together with -n to rename .AVI files from exif in .THM files\n" +- " for example\n" +- " -ta<+|->h[:mm[:ss]]\n" +- " Adjust time by h:mm forwards or backwards. Useful when having\n" +- " taken pictures with the wrong time set on the camera, such as when\n" +- " traveling across time zones or DST changes. Dates can be adjusted\n" +- " by offsetting by 24 hours or more. For large date adjustments,\n" +- " use the -da option\n" +- " -da-\n" +- " Adjust date by large amounts. This is used to fix photos from\n" +- " cameras where the date got set back to the default camera date\n" +- " by accident or battery removal.\n" +- " To deal with different months and years having different numbers of\n" +- " days, a simple date-month-year offset would result in unexpected\n" +- " results. Instead, the difference is specified as desired date\n" +- " minus original date. Date is specified as yyyy:mm:dd or as date\n" +- " and time in the format yyyy:mm:dd/hh:mm:ss\n" +- " -ts