From dac26808bde172e4daeba012fa86d15199f5dc37 Mon Sep 17 00:00:00 2001 From: alemuntoni Date: Wed, 23 Sep 2020 13:07:30 +0200 Subject: [PATCH] libjhead removed --- src/common/common.pro | 146 +- 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 | 148 +- 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 +- vcglib | 2 +- 27 files changed, 168 insertions(+), 7308 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/common/common.pro b/src/common/common.pro index ee57d0e48..fc16e18a4 100644 --- a/src/common/common.pro +++ b/src/common/common.pro @@ -17,93 +17,95 @@ win32-g++:DLLDESTDIR = $$MESHLAB_DISTRIB_DIRECTORY/lib linux:CONFIG += dll INCLUDEPATH *= \ - ../.. \ - $$VCGDIR \ - $$EIGENDIR \ - $$EXIF_DIR + ../.. \ + $$$$MESHLAB_EXTERNAL_DIRECTORY/easyexif \ + $$VCGDIR \ + $$EIGENDIR \ + $$EXIF_DIR !CONFIG(system_glew) { - INCLUDEPATH *= $$GLEWDIR/include - GLEWCODE = $$GLEWDIR/src/glew.c + INCLUDEPATH *= $$GLEWDIR/include + GLEWCODE = $$GLEWDIR/src/glew.c } !CONFIG(system_glew) { - SOURCES += $$GLEWCODE - DEFINES += GLEW_STATIC + SOURCES += $$GLEWCODE + DEFINES += GLEW_STATIC } # defining meshlab version exists(../../ML_VERSION){ - MESHLAB_VERSION = $$cat(../../ML_VERSION) - message(MeshLab Version: $$MESHLAB_VERSION) - DEFINES += "MESHLAB_VERSION=$$MESHLAB_VERSION" + MESHLAB_VERSION = $$cat(../../ML_VERSION) + message(MeshLab Version: $$MESHLAB_VERSION) + DEFINES += "MESHLAB_VERSION=$$MESHLAB_VERSION" } # Input HEADERS += \ - GLExtensionsManager.h \ - filter_parameter/rich_parameter_list.h \ - filter_parameter/value.h \ - filter_parameter/rich_parameter.h \ - filterscript.h \ - GLLogStream.h \ - interfaces/decorate_plugin_interface.h \ - interfaces/edit_plugin_interface.h \ - interfaces/filter_plugin_interface.h \ - interfaces/io_plugin_interface.h \ - interfaces/mainwindow_interface.h \ - interfaces/plugin_interface.h \ - interfaces/render_plugin_interface.h \ - ml_mesh_type.h \ - meshmodel.h \ - pluginmanager.h \ - mlexception.h \ - mlapplication.h \ - meshlabdocumentxml.h \ - ml_shared_data_context.h \ - ml_selection_buffers.h \ - meshlabdocumentxml.h + GLExtensionsManager.h \ + filter_parameter/rich_parameter_list.h \ + filter_parameter/value.h \ + filter_parameter/rich_parameter.h \ + filterscript.h \ + GLLogStream.h \ + interfaces/decorate_plugin_interface.h \ + interfaces/edit_plugin_interface.h \ + interfaces/filter_plugin_interface.h \ + interfaces/io_plugin_interface.h \ + interfaces/mainwindow_interface.h \ + interfaces/plugin_interface.h \ + interfaces/render_plugin_interface.h \ + ml_mesh_type.h \ + meshmodel.h \ + pluginmanager.h \ + mlexception.h \ + mlapplication.h \ + meshlabdocumentxml.h \ + ml_shared_data_context.h \ + ml_selection_buffers.h \ + meshlabdocumentxml.h SOURCES += \ - GLExtensionsManager.cpp \ - filter_parameter/rich_parameter.cpp \ - filter_parameter/rich_parameter_list.cpp \ - filter_parameter/value.cpp \ - filterscript.cpp \ - GLLogStream.cpp \ - interfaces/decorate_plugin_interface.cpp \ - interfaces/filter_plugin_interface.cpp \ - interfaces/plugin_interface.cpp \ - meshmodel.cpp \ - pluginmanager.cpp \ - mlapplication.cpp \ - searcher.cpp \ - meshlabdocumentxml.cpp \ - meshlabdocumentbundler.cpp \ - ml_shared_data_context.cpp \ - ml_selection_buffers.cpp + GLExtensionsManager.cpp \ + filter_parameter/rich_parameter.cpp \ + filter_parameter/rich_parameter_list.cpp \ + filter_parameter/value.cpp \ + filterscript.cpp \ + GLLogStream.cpp \ + interfaces/decorate_plugin_interface.cpp \ + interfaces/filter_plugin_interface.cpp \ + interfaces/plugin_interface.cpp \ + meshmodel.cpp \ + pluginmanager.cpp \ + mlapplication.cpp \ + searcher.cpp \ + meshlabdocumentxml.cpp \ + meshlabdocumentbundler.cpp \ + ml_shared_data_context.cpp \ + ml_selection_buffers.cpp \ + $$MESHLAB_EXTERNAL_DIRECTORY/easyexif/exif.cpp macx:QMAKE_POST_LINK = "\ - if [ -d $$MESHLAB_DISTRIB_DIRECTORY/meshlab.app/Contents/Frameworks/ ]; \ - then \ - echo "Copying"; \ - else \ - mkdir -p $$MESHLAB_DISTRIB_DIRECTORY/meshlab.app/Contents/Frameworks; \ - fi; \ - cp $$MESHLAB_DISTRIB_DIRECTORY/lib/libmeshlab-common.* $$MESHLAB_DISTRIB_DIRECTORY/meshlab.app/Contents/Frameworks/ ;\ -# if [ -d ../external/ ];\ -# then \ -# echo "ok external dir exists"; \ -# else \ -# ln -s ../../meshlab/src/external ../external;\ -# echo "linked external dir"; \ -# fi;\ -# if [ -d $$MESHLAB_DISTRIB_DIRECTORY/shaders/ ];\ -# then \ -# echo "ok shader dir exists"; \ -# else \ -# ln -s ../../../meshlab/src/distrib/shaders ../distrib/shaders ;\ -# echo "linked shader dir"; \ -# fi;\ - " + if [ -d $$MESHLAB_DISTRIB_DIRECTORY/meshlab.app/Contents/Frameworks/ ]; \ + then \ + echo "Copying"; \ + else \ + mkdir -p $$MESHLAB_DISTRIB_DIRECTORY/meshlab.app/Contents/Frameworks; \ + fi; \ + cp $$MESHLAB_DISTRIB_DIRECTORY/lib/libmeshlab-common.* $$MESHLAB_DISTRIB_DIRECTORY/meshlab.app/Contents/Frameworks/ ;\ +# if [ -d ../external/ ];\ +# then \ +# echo "ok external dir exists"; \ +# else \ +# ln -s ../../meshlab/src/external ../external;\ +# echo "linked external dir"; \ +# fi;\ +# if [ -d $$MESHLAB_DISTRIB_DIRECTORY/shaders/ ];\ +# then \ +# echo "ok shader dir exists"; \ +# else \ +# ln -s ../../../meshlab/src/distrib/shaders ../distrib/shaders ;\ +# echo "linked shader dir"; \ +# fi;\ + " diff --git a/src/external/external.pro b/src/external/external.pro index 50934f902..6b046aca5 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 cf268d6a4..000000000 --- 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 124db4d0b..000000000 --- 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 1b8fdac11..000000000 --- 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 06fa4e356..000000000 --- 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 b17219332..000000000 --- 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 1f745a949..000000000 --- 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 f78127aa8..000000000 --- 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