Browse Source

Allow to choose the splashscreen.
The author is taken from the Artist exif metadata of the jpeg
using 'KLab exif' lib for that (exiv2 has an incompatible licence and libexif need autoconf for compiling)

remi durand 3 years ago
parent
commit
b6a98377ae

BIN
resources/icons/splashscreen.jpg → resources/splashscreen/benchy-splashscreen.jpg


BIN
resources/icons/splashscreen-gcodepreview.jpg → resources/splashscreen/prusa-gcodepreview.jpg


+ 2 - 0
src/CMakeLists.txt

@@ -25,6 +25,8 @@ if (SLIC3R_GUI)
     add_subdirectory(imgui)
     add_subdirectory(hidapi)
     include_directories(hidapi/include)
+    add_subdirectory(exif)
+    include_directories(exif/include)
 
     if(WIN32)
         message(STATUS "WXWIN environment set to: $ENV{WXWIN}")

+ 6 - 0
src/exif/CMakeLists.txt

@@ -0,0 +1,6 @@
+
+cmake_minimum_required(VERSION 2.5)
+
+include_directories(include)
+
+add_library(exif STATIC exif.c)

+ 201 - 0
src/exif/COPYING

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 94 - 0
src/exif/README

@@ -0,0 +1,94 @@
+"exif.c" is a simple implementation to access the Exif segment in a JPEG file.
+It easily enables you to get the value of the IFD tag field with such code:
+
+  TagNodeInfo *tag = getTagInfo(ifdArray, IFD_EXIF, TAG_DateTimeOriginal);
+  printf("DateTimeOriginal = [%s]\n", tag->byteData);
+
+  -> DateTimeOriginal = [2013:09:01 09:49:00]
+
+See "sample_main.c" and "exif.h" for details.
+
+exif.c only uses standard C library functions. So, it will be usable in many environments. 
+It has been tested in the following environments.
+
+ - Windows XP 32bit + 32bit Visual C++
+ - Windows 7 64bit + 64bit Visual C++
+ - Redhat Linux 32bit + 32bit gcc
+ - Mac OS X 64bit + 64bit gcc
+
+building with gcc:
+gcc -o exif sample_main.c exif.c
+
+building with Microsoft Visual C++:
+cl.exe /o exif sample_main.c exif.c
+
+The following output is the result of the sample program.
+---------------------------------------------------------------------------
+$ exif test.jpg
+
+[test.jpg] createIfdTableArray: result=4
+
+{0TH IFD}
+ - Make: [Apple]
+ - Model: [iPod touch]
+ - Orientation: 1
+ - XResolution: 72/1
+ - YResolution: 72/1
+ - ResolutionUnit: 2
+ - Software: [6.1.4]
+ - DateTime: [2013:09:01 09:49:00]
+ - YCbCrPositioning: 1
+ - ExifIFDPointer: 206
+ - GPSInfoIFDPointer: 576
+
+{EXIF IFD}
+ - ExposureTime: 1/30
+ - FNumber: 12/5
+ - ExposureProgram: 2
+ - PhotographicSensitivity: 400
+ - ExifVersion: 0 2 2 1
+ - DateTimeOriginal: [2013:09:01 09:49:00]
+ - DateTimeDigitized: [2013:09:01 09:49:00]
+ - ComponentsConfiguration: 0x01 0x02 0x03 0x00
+ - ShutterSpeedValue: 4035/821
+ - ApertureValue: 4845/1918
+ - BrightnessValue: 2234/1113
+ - MeteringMode: 5
+ - Flash: 32
+ - FocalLength: 77/20
+ - FlashPixVersion: 0 1 0 0
+ - ColorSpace: 1
+ - PixelXDimension: 960
+ - PixelYDimension: 720
+ - SensingMethod: 2
+ - ExposureMode: 0
+ - WhiteBalance: 0
+ - FocalLengthIn35mmFormat: 32
+ - SceneCaptureType: 0
+
+{GPS IFD}
+ - GPSLatitudeRef: [S]
+ - GPSLatitude: 69/1 17/100 0/1
+ - GPSLongitudeRef: [E]
+ - GPSLongitude: 39/1 35/100 0/1
+ - GPSAltitudeRef: 0
+ - GPSAltitude: 6151/470
+ - GPSTimeStamp: 0/1 48/1 3921/100
+
+{1ST IFD}
+ - Compression: 6
+ - XResolution: 72/1
+ - YResolution: 72/1
+ - ResolutionUnit: 2
+ - JPEGInterchangeFormat: 840
+ - JPEGInterchangeFormatLength: 8648
+
+0th IFD : Model = [iPod touch]
+Exif IFD : DateTimeOriginal = [2013:09:01 09:49:00]
+GPS IFD : GPSLatitude = 69/1 17/100 0/1
+removeExifSegmentFromJPEGFile: result=1
+---------------------------------------------------------------------------
+http://dsas.blog.klab.org/archives/52123322.html (Japanese only)
+
+Copyright (C) 2013 KLab Inc.
+

+ 2889 - 0
src/exif/exif.c

@@ -0,0 +1,2889 @@
+/*
+ * Copyright (C) 2013 KLab Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Links related to EXIF, TIFF 6.0 and MPO (Multi Picture Object) format
+//
+// https://www.exif.org/Exif2-2.PDF
+// https://www.itu.int/itudoc/itu-t/com16/tiff-fx/docs/tiff6.pdf
+//
+// https://en.wikipedia.org/wiki/JPEG#JPEG_Multi-Picture_Format
+// http://www.cmsoft.com.br/downloads/cmsoft-stereoscopic-picture-editor-converter/3d-picture-gallery/
+// https://dmitrybrant.com/2011/02/08/the-fujifilm-mpo-3d-photo-format
+//
+#ifdef _MSC_VER
+#include <windows.h>
+//#define vsnprintf _vsnprintf
+#endif
+
+#include "exif.h"
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <memory.h>
+#include <ctype.h>
+
+#ifdef _MSC_VER
+#ifdef __cplusplus
+extern "C" {
+#endif
+#endif
+
+
+#pragma pack(2)
+
+#define VERSION  "1.0.1"
+
+#define APP0_MARKER		0xFFE0
+#define APP1_MARKER		0xFFE1
+#define APP2_MARKER		0xFFE2
+
+// TIFF Header
+typedef struct _tiff_Header {
+    uint16_t byteOrder;
+    uint16_t reserved;
+    unsigned int Ifd0thOffset;
+} TIFF_HEADER;
+
+// APP1 Exif Segment Header
+typedef struct _App_Header {
+    uint16_t marker;
+    uint16_t length;
+    char id[6]; // "Exif\0\0"
+    TIFF_HEADER tiff;
+} APP_HEADER;
+
+// MPF Segment Header
+typedef struct _MPF_Header {
+	uint16_t marker;
+	uint16_t length;
+	char id[4]; // "MPF\0"
+	TIFF_HEADER tiff;
+} MPF_HEADER;
+
+// tag field in IFD
+typedef struct {
+    uint16_t tag;
+    uint16_t type;
+    unsigned int count;
+    unsigned int offset;
+} IFD_TAG;
+
+// tag node - internal use
+typedef struct _tagNode TagNode;
+struct _tagNode {
+    uint16_t tagId;
+    uint16_t type;
+    unsigned int count;
+    unsigned int *numData;
+    uint8_t *byteData;
+    uint16_t error;
+    TagNode *prev;
+    TagNode *next;
+};
+
+// IFD table - internal use
+typedef struct _ifdTable IfdTable;
+struct _ifdTable {
+    EXIF_IFD_TYPE ifdType;
+    uint16_t tagCount;
+    TagNode *tags;
+    unsigned int nextIfdOffset;
+    uint16_t offset;
+    uint16_t length;
+    uint8_t *p;
+};
+
+static int init(FILE*);
+static int systemIsLittleEndian();
+static int dataIsLittleEndian();
+static void freeIfdTable(void*);
+static void *parseIFD(FILE*, unsigned int, unsigned int, EXIF_IFD_TYPE);
+static TagNode *getTagNodePtrFromIfd(IfdTable*, uint16_t);
+static TagNode *duplicateTagNode(TagNode*);
+static void freeTagNode(void*);
+static const char *getTagName(int, uint16_t);
+static int countIfdTableOnIfdTableArray(void **ifdTableArray);
+static IfdTable *getIfdTableFromIfdTableArray(void **ifdTableArray, EXIF_IFD_TYPE ifdType);
+static void *createIfdTable(EXIF_IFD_TYPE IfdType, uint16_t tagCount, unsigned int nextOfs);
+static void *addTagNodeToIfd(void *pIfd, uint16_t tagId, uint16_t type,
+                      unsigned int count, unsigned int *numData,uint8_t *byteData);
+static int writeExifSegment(FILE *fp, void **ifdTableArray);
+static int removeTagOnIfd(void *pIfd, uint16_t tagId);
+static int fixLengthAndOffsetInIfdTables(void **ifdTableArray);
+static int setSingleNumDataToTag(TagNode *tag, unsigned int value);
+static int getAppNStartOffset(FILE *fp, uint16_t appMarkerN, const char *App1IDString,
+                              size_t App1IDStringLength, int *pDQTOffset);
+static uint16_t swab16(uint16_t us);
+static void PRINTF(char **ms, const char *fmt, ...);
+static void _dumpIfdTable(void *pIfd, char **p);
+
+static int Verbose = 0;
+static int App1StartOffset = -1;
+static int App2StartOffset = -1;
+static int MPFStartOffset = -1;
+static int JpegDQTOffset = -1;
+static APP_HEADER App1Header;
+static APP_HEADER App2Header;
+static MPF_HEADER MPFHeader;
+
+
+// private functions
+
+static int dataIsLittleEndian()
+{
+	return (App1Header.tiff.byteOrder == 0x4949) ? 1 : 0;
+}
+
+static int systemIsLittleEndian()
+{
+	static int i = 1;
+	return (int)(*(char*)&i);
+}
+
+static uint16_t swab16(uint16_t us)
+{
+	return (us << 8) | ((us >> 8) & 0x00FF);
+}
+
+static unsigned int swab32(unsigned int ui)
+{
+	return
+		((ui << 24) & 0xFF000000) | ((ui << 8) & 0x00FF0000) |
+		((ui >> 8) & 0x0000FF00) | ((ui >> 24) & 0x000000FF);
+}
+
+static uint16_t fix_short(uint16_t us)
+{
+	return (dataIsLittleEndian() !=
+		systemIsLittleEndian()) ? swab16(us) : us;
+}
+
+static unsigned int fix_int(unsigned int ui)
+{
+	return (dataIsLittleEndian() !=
+		systemIsLittleEndian()) ? swab32(ui) : ui;
+}
+
+// public funtions
+
+/**
+ * setVerbose()
+ *
+ * Verbose output on/off
+ *
+ * parameters
+ *  [in] v : 1=on  0=off
+ */
+EXIF_API_EXPORT void exif_setVerbose(int v)
+{
+    Verbose = v;
+}
+
+/**
+ * removeExifSegmentFromJPEGFile()
+ *
+ * Remove the Exif segment from a JPEG file
+ *
+ * parameters
+ *  [in] inJPEGFileName : original JPEG file
+ *  [in] outJPGEFileName : output JPEG file
+ *
+ * return
+ *   1: OK
+ *   0: the Exif segment is not found
+ *  -n: error
+ *      EXIF_ERR_READ_FILE
+ *      EXIF_ERR_WRITE_FILE
+ *      EXIF_ERR_INVALID_JPEG
+ *      EXIF_ERR_INVALID_APP1HEADER
+ */
+int exif_removeExifSegmentFromJPEGFile(const char *inJPEGFileName,
+                                  const char *outJPGEFileName)
+{
+    int ofs;
+    int i, sts = 1;
+    size_t readLen, writeLen;
+    uint8_t buf[8192], *p;
+    FILE *fpr = NULL, *fpw = NULL;
+
+    fpr = fopen(inJPEGFileName, "rb");
+    if (!fpr) {
+        sts = EXIF_ERR_READ_FILE;
+        goto DONE;
+    }
+    sts = init(fpr);
+    if (sts <= 0) {
+        goto DONE;
+    }
+    fpw = fopen(outJPGEFileName, "wb");
+    if (!fpw) {
+        sts = EXIF_ERR_WRITE_FILE;
+        goto DONE;
+    }
+    // copy the data in front of the Exif segment
+    rewind(fpr);
+    p = buf;
+    if (App1StartOffset > sizeof(buf)) {
+        // allocate new buffer if needed
+        p = (uint8_t*)malloc(App1StartOffset);
+    }
+    if (!p) {
+        for (i = 0; i < App1StartOffset; i++) {
+            fread(buf, 1, sizeof(char), fpr);
+            fwrite(buf, 1, sizeof(char), fpw);
+        }
+    } else {
+        if (fread(p, 1, App1StartOffset, fpr) < (size_t)App1StartOffset) {
+            sts = EXIF_ERR_READ_FILE;
+            goto DONE;
+        }
+        if (fwrite(p, 1, App1StartOffset, fpw) < (size_t)App1StartOffset) {
+            sts = EXIF_ERR_WRITE_FILE;
+            goto DONE;
+        }
+        if (p != &buf[0]) {
+            free(p);
+        }
+    }
+    // seek to the end of the Exif segment
+    ofs = App1StartOffset + sizeof(App1Header.marker) + App1Header.length;
+    if (fseek(fpr, ofs, SEEK_SET) != 0) {
+        sts = EXIF_ERR_READ_FILE;
+        goto DONE;
+    }
+    // read & write
+    for (;;) {
+        readLen = fread(buf, 1, sizeof(buf), fpr);
+        if (readLen <= 0) {
+            break;
+        }
+        writeLen = fwrite(buf, 1, readLen, fpw);
+        if (writeLen != readLen) {
+            sts = EXIF_ERR_WRITE_FILE;
+            goto DONE;
+        }
+    }
+DONE:
+    if (fpw) {
+        fclose(fpw);
+    }
+    if (fpr) {
+        fclose(fpr);
+    }
+    return sts;
+}
+
+/**
+ * fillIfdTableArray()
+ *
+ * Parse the JPEG header and fill in the IFD table
+ *
+ * parameters
+ *  [in] JPEGFileName : target JPEG file
+ *  [out] ifdArray[32] : array of IfdTable pointers
+ *
+ * return
+ *   n: number of IFD tables
+ *   0: the Exif segment is not found
+ *  -n: error
+ *      EXIF_ERR_READ_FILE
+ *      EXIF_ERR_INVALID_JPEG
+ *      EXIF_ERR_INVALID_APP1HEADER
+ *      EXIF_ERR_INVALID_IFD
+ */
+int EXIF_API_EXPORT exif_fillIfdTableArray(const char *JPEGFileName, void* ifdArray[32])
+{
+    #define FMT_ERR "critical error in %s IFD\n"
+
+    int sts = 1, ifdCount = 0;
+    unsigned int ifdOffset;
+    FILE *fp = NULL;
+    TagNode *tag;
+	IfdTable *IFD_0th, *IFD_exif, *IFD_gps, *IFD_io, *IFD_1st, *mpf_ifd;
+
+    IFD_0th = IFD_exif = IFD_gps = IFD_io = IFD_1st = NULL;
+    memset(ifdArray, 0, sizeof(void*) * 32);
+
+    fp = fopen(JPEGFileName, "rb");
+    if (!fp) {
+        sts = EXIF_ERR_READ_FILE;
+        goto DONE;
+    }
+    sts = init(fp);
+    if (sts <= 0) {
+        goto DONE;
+    }
+    if (Verbose) {
+        printf("system: %s-endian\n  data: %s-endian\n", 
+            systemIsLittleEndian() ? "little" : "big",
+            dataIsLittleEndian() ? "little" : "big");
+    }
+
+    // for 0th IFD
+	IFD_0th = parseIFD(fp, App1StartOffset + offsetof(APP_HEADER, tiff), App1Header.tiff.Ifd0thOffset, IFD_0TH);
+    if (!IFD_0th) {
+        if (Verbose) {
+            printf(FMT_ERR, "0th");
+        }
+        sts = EXIF_ERR_INVALID_IFD;
+        goto DONE; // non-continuable
+    }
+    ifdArray[ifdCount++] = IFD_0th;
+
+	if (MPFStartOffset > 0) {
+		mpf_ifd = parseIFD(fp, MPFStartOffset + offsetof(MPF_HEADER, tiff), MPFHeader.tiff.Ifd0thOffset, IFD_MPF);
+		ifdArray[ifdCount++] = mpf_ifd;
+	}
+
+    // for Exif IFD 
+    tag = getTagNodePtrFromIfd(IFD_0th, TAG_ExifIFDPointer);
+    if (tag && !tag->error) {
+        ifdOffset = tag->numData[0];
+        if (ifdOffset != 0) {
+			IFD_exif = parseIFD(fp, App1StartOffset + offsetof(APP_HEADER, tiff), ifdOffset, IFD_EXIF);
+            if (IFD_exif) {
+                ifdArray[ifdCount++] = IFD_exif;
+                // for InteroperabilityIFDPointer IFD
+                tag = getTagNodePtrFromIfd(IFD_exif, TAG_InteroperabilityIFDPointer);
+                if (tag && !tag->error) {
+                    ifdOffset = tag->numData[0];
+                    if (ifdOffset != 0) {
+						IFD_io = parseIFD(fp, App1StartOffset + offsetof(APP_HEADER, tiff), ifdOffset, IFD_IO);
+                        if (IFD_io) {
+                            ifdArray[ifdCount++] = IFD_io;
+                        } else {
+                            if (Verbose) {
+                                printf(FMT_ERR, "Interoperability");
+                            }
+                            sts = EXIF_ERR_INVALID_IFD;
+                        }
+                    }
+                }
+            } else {
+                if (Verbose) {
+                    printf(FMT_ERR, "Exif");
+                }
+                sts = EXIF_ERR_INVALID_IFD;
+            }
+        }
+    }
+
+    // for GPS IFD
+    tag = getTagNodePtrFromIfd(IFD_0th, TAG_GPSInfoIFDPointer);
+    if (tag && !tag->error) {
+        ifdOffset = tag->numData[0];
+        if (ifdOffset != 0) {
+			IFD_gps = parseIFD(fp, App1StartOffset + offsetof(APP_HEADER, tiff), ifdOffset, IFD_GPS);
+            if (IFD_gps) {
+                ifdArray[ifdCount++] = IFD_gps;
+            } else {
+                if (Verbose) {
+                    printf(FMT_ERR, "GPS");
+                }
+                sts = EXIF_ERR_INVALID_IFD;
+            }
+        }
+    }
+
+    // for 1st IFD
+    ifdOffset = IFD_0th->nextIfdOffset;
+    if (Verbose) {
+        printf("1st IFD ifdOffset=%u\n", ifdOffset);
+    }
+    if (ifdOffset != 0) {
+		IFD_1st = parseIFD(fp, App1StartOffset + offsetof(APP_HEADER, tiff), ifdOffset, IFD_1ST);
+        if (IFD_1st) {
+            ifdArray[ifdCount++] = IFD_1st;
+        } else {
+            if (Verbose) {
+                printf(FMT_ERR, "1st");
+            }
+            sts = EXIF_ERR_INVALID_IFD;
+        }
+    }
+
+DONE:
+    if (fp) {
+        fclose(fp);
+    }
+    return (sts <= 0) ? sts : ifdCount;
+}
+
+/**
+ * createIfdTableArray()
+ *
+ * Parse the JPEG header and create the pointer array of the IFD tables
+ *
+ * parameters
+ *  [in] JPEGFileName : target JPEG file
+ *  [out] result : result status value 
+ *   n: number of IFD tables
+ *   0: the Exif segment is not found
+ *  -n: error
+ *      EXIF_ERR_READ_FILE
+ *      EXIF_ERR_INVALID_JPEG
+ *      EXIF_ERR_INVALID_APP1HEADER
+ *      EXIF_ERR_INVALID_IFD
+ *
+ * return
+ *   NULL: error or no Exif segment
+ *  !NULL: pointer array of the IFD tables
+ */
+void EXIF_API_EXPORT ** EXIF_API_CALL exif_createIfdTableArray(const char *JPEGFileName, int *result)
+{
+    void* ifdTable[32];
+    void** ppIfdArray = NULL;
+    int i, count = exif_fillIfdTableArray(JPEGFileName, ifdTable);
+    *result = count;
+    if (count > 0) {
+        // +1 extra NULL element to the array 
+        ppIfdArray = (void**)malloc(sizeof(void*)*(count+1));
+        memset(ppIfdArray, 0, sizeof(void*)*(count+1));
+        for (i = 0; ifdTable[i] != NULL; i++) {
+            ppIfdArray[i] = ifdTable[i];
+        }
+    }
+    return ppIfdArray;
+}
+
+/**
+ * freeIfdTables()
+ *
+ * Free the pointer array of the IFD tables
+ *
+ * parameters
+ *  [in] ifdArray : address of the IFD array
+ */
+void EXIF_API_EXPORT exif_freeIfdTables(void* ifdArray[32])
+{
+    int i;
+    for (i = 0; ifdArray[i] != NULL; i++) {
+        freeIfdTable(ifdArray[i]);
+    }
+}
+
+/**
+ * freeIfdTableArray()
+ *
+ * Free the pointer array of the IFD tables
+ *
+ * parameters
+ *  [in] ifdArray : address of the IFD array
+ */
+void EXIF_API_EXPORT exif_freeIfdTableArray(void **ifdArray)
+{
+    exif_freeIfdTables(ifdArray);
+    free(ifdArray);
+}
+
+/**
+ * getIfdType()
+ *
+ * Returns the type of the IFD
+ *
+ * parameters
+ *  [in] ifd: target IFD
+ *
+ * return
+ *  IFD TYPE value
+ */
+EXIF_IFD_TYPE EXIF_API_EXPORT exif_getIfdType(void *pIfd)
+{
+    IfdTable *ifd = (IfdTable*)pIfd;
+    if (!ifd) {
+        return IFD_UNKNOWN;
+    }
+    return ifd->ifdType;
+}
+
+/**
+ * dumpIfdTable()
+ *
+ * Dump the IFD table
+ *
+ * parameters
+ *  [in] ifd: target IFD
+ */
+
+void EXIF_API_EXPORT exif_dumpIfdTable(void *pIfd)
+{
+    _dumpIfdTable(pIfd, NULL);
+}
+
+void EXIF_API_EXPORT exif_getIfdTableDump(void *pIfd, char **pp)
+{
+    if (pp) {
+        *pp = NULL;
+    }
+    _dumpIfdTable(pIfd, pp);
+}
+
+static void _dumpIfdTable(void *pIfd, char **p)
+{
+    int i;
+    IfdTable *ifd;
+    TagNode *tag;
+    char tagName[512];
+    int cnt = 0;
+    unsigned int count;
+
+    if (!pIfd) {
+        return;
+    }
+    ifd = (IfdTable*)pIfd;
+
+    PRINTF(p, "\n{%s IFD}",
+        (ifd->ifdType == IFD_0TH)  ? "0TH" :
+        (ifd->ifdType == IFD_1ST)  ? "1ST" :
+        (ifd->ifdType == IFD_EXIF) ? "EXIF" :
+        (ifd->ifdType == IFD_GPS)  ? "GPS" :
+        (ifd->ifdType == IFD_IO)   ? "Interoperability" :
+		(ifd->ifdType == IFD_MPF)  ? "MPF" : "");
+
+    if (Verbose) {
+        PRINTF(p, " tags=%u\n", ifd->tagCount);
+    } else {
+        PRINTF(p, "\n");
+    }
+
+    tag = ifd->tags;
+    while (tag) {
+        if (Verbose) {
+            PRINTF(p, "tag[%02d] 0x%04X %s\n",
+                cnt++, tag->tagId, getTagName(ifd->ifdType, tag->tagId));
+            PRINTF(p, "\ttype=%u count=%u ", tag->type, tag->count);
+            PRINTF(p, "val=");
+        } else {
+            strcpy(tagName, getTagName(ifd->ifdType, tag->tagId));
+            PRINTF(p, " - %s: ", (strlen(tagName) > 0) ? tagName : "(unknown)");
+        }
+        if (tag->error) {
+            PRINTF(p, "(error)");
+        } else {
+            switch (tag->type) {
+            case TYPE_BYTE:
+                for (i = 0; i < (int)tag->count; i++) {
+                    PRINTF(p, "%u ", (uint8_t)tag->numData[i]);
+                }
+                break;
+
+            case TYPE_ASCII:
+                PRINTF(p, "[%s]", (char*)tag->byteData);
+                break;
+
+            case TYPE_SHORT:
+                for (i = 0; i < (int)tag->count; i++) {
+                    PRINTF(p, "%hu ", (uint16_t)tag->numData[i]);
+                }
+                break;
+
+            case TYPE_LONG:
+                for (i = 0; i < (int)tag->count; i++) {
+                    PRINTF(p, "%u ", tag->numData[i]);
+                }
+                break;
+
+            case TYPE_RATIONAL:
+                for (i = 0; i < (int)tag->count; i++) {
+                    PRINTF(p, "%u/%u ", tag->numData[i*2], tag->numData[i*2+1]);
+                }
+                break;
+
+            case TYPE_SBYTE:
+                for (i = 0; i < (int)tag->count; i++) {
+                    PRINTF(p, "%d ", (char)tag->numData[i]);
+                }
+                break;
+
+            case TYPE_UNDEFINED:
+                count = tag->count;
+                // omit too long data if !Verbose
+                if (count > 16 && !Verbose) {
+                    count = 16;
+                }
+                for (i = 0; i < (int)count; i++) {
+                    // if character is printable
+                    if (isgraph(tag->byteData[i])) {
+                        PRINTF(p, "%c ", tag->byteData[i]);
+                    } else {
+                        PRINTF(p, "0x%02x ", tag->byteData[i]);
+                    }
+                }
+                if (count < tag->count) {
+                    PRINTF(p, "(omitted)");
+                }
+                break;
+
+            case TYPE_SSHORT:
+                for (i = 0; i < (int)tag->count; i++) {
+                    PRINTF(p, "%hd ", (short)tag->numData[i]);
+                }
+                break;
+
+            case TYPE_SLONG:
+                for (i = 0; i < (int)tag->count; i++) {
+                    PRINTF(p, "%d ", (int)tag->numData[i]);
+                }
+                break;
+
+            case TYPE_SRATIONAL:
+                for (i = 0; i < (int)tag->count; i++) {
+                    PRINTF(p, "%d/%d ", (int)tag->numData[i*2], (int)tag->numData[i*2+1]);
+                }
+                break;
+
+            default:
+                break;
+            }
+        }
+        PRINTF(p, "\n");
+
+        tag = tag->next;
+    }
+    return;
+}
+
+/**
+ * dumpIfdTableArray()
+ *
+ * Dump the array of the IFD tables
+ *
+ * parameters
+ *  [in] ifdArray : address of the IFD array
+ */
+void EXIF_API_EXPORT exif_dumpIfdTableArray(void **ifdArray)
+{
+    int i;
+    if (ifdArray) {
+        for (i = 0; ifdArray[i] != NULL; i++) {
+            exif_dumpIfdTable(ifdArray[i]);
+        }
+    }
+}
+
+/**
+ * getTagInfo()
+ *
+ * Get the ExifTagNodeInfo that matches the EXIF_IFD_TYPE & TagId
+ *
+ * parameters
+ *  [in] ifdArray : address of the IFD array
+ *  [in] ifdType : target IFD TYPE
+ *  [in] tagId : target tag ID
+ *
+ * return
+ *   NULL: tag is not found
+ *  !NULL: address of the ExifTagNodeInfo structure
+ */
+ExifTagNodeInfo EXIF_API_EXPORT * exif_getTagInfo(void **ifdArray,
+                       EXIF_IFD_TYPE ifdType,
+                       uint16_t tagId)
+{
+    int i;
+    if (!ifdArray) {
+        return NULL;
+    }
+    for (i = 0; ifdArray[i] != NULL; i++) {
+        if (exif_getIfdType(ifdArray[i]) == ifdType) {
+            void *targetTag = getTagNodePtrFromIfd(ifdArray[i], tagId);
+            if (!targetTag) {
+                return NULL;
+            }
+            return (ExifTagNodeInfo*)duplicateTagNode(targetTag);
+        }
+    }
+    return NULL;
+}
+
+/**
+ * getTagInfoFromIfd()
+ *
+ * Get the ExifTagNodeInfo that matches the TagId
+ *
+ * parameters
+ *  [in] ifd : target IFD table
+ *  [in] tagId : target tag ID
+ *
+ * return
+ *  NULL: tag is not found
+ *  !NULL: address of the ExifTagNodeInfo structure
+ */
+ExifTagNodeInfo EXIF_API_EXPORT * exif_getTagInfoFromIfd(void *ifd,
+                               uint16_t tagId)
+{
+    if (!ifd) {
+        return NULL;
+    }
+    return (ExifTagNodeInfo*)getTagNodePtrFromIfd(ifd, tagId);
+}
+
+/**
+ * freeTagInfo()
+ *
+ * Free the ExifTagNodeInfo allocated by getTagInfo() or getTagInfoFromIfd()
+ *
+ * parameters
+ *  [in] tag : target ExifTagNodeInfo
+ */
+void EXIF_API_EXPORT exif_freeTagInfo(void *tag)
+{
+    freeTagNode(tag);
+}
+
+/**
+ * queryTagNodeIsExist()
+ *
+ * Query if the specified tag node is exist in the IFD tables
+ *
+ * parameters
+ *  [in] ifdTableArray: address of the IFD tables array
+ *  [in] ifdType : target IFD type
+ *  [in] tagId : target tag ID
+ *
+ * return
+ *  0: not exist
+ *  1: exist
+ */
+int EXIF_API_EXPORT exif_queryTagNodeIsExist(void **ifdTableArray,
+                        EXIF_IFD_TYPE ifdType,
+                        uint16_t tagId)
+{
+    IfdTable *ifd;
+    TagNode *tag;
+    if (!ifdTableArray) {
+        return 0;
+    }
+    ifd = getIfdTableFromIfdTableArray(ifdTableArray, ifdType);
+    if (!ifd) {
+        return 0;
+    }
+    tag = getTagNodePtrFromIfd(ifd, tagId);
+    if (!tag) {
+        return 0;
+    }
+    return 1;
+}
+
+/**
+ * createTagInfo()
+ *
+ * Create new ExifTagNodeInfo block
+ *
+ * parameters
+ *  [in] tagId: id of the tag
+ *  [in] type: type of the tag
+ *  [in] count: data count of the tag
+ *  [out] pResult : error status
+ *   0: OK
+ *  -n: error
+ *      EXIF_ERR_INVALID_TYPE
+ *      EXIF_ERR_INVALID_COUNT
+ *      EXIF_ERR_MEMALLOC
+ *
+ * return
+ *  NULL: error
+ * !NULL: address of the newly created ExifTagNodeInfo
+ */
+ExifTagNodeInfo EXIF_API_EXPORT * exif_createTagInfo(uint16_t tagId,
+                           uint16_t type,
+                           unsigned int count,
+                           int *pResult)
+{
+    TagNode *tag;
+    if (type < TYPE_BYTE || type > TYPE_SRATIONAL) {
+        if (pResult) {
+            *pResult = EXIF_ERR_INVALID_TYPE;
+        }
+        return NULL;
+    }
+    if (count <= 0) {
+        if (pResult) {
+            *pResult = EXIF_ERR_INVALID_COUNT;
+        }
+        return NULL;
+    }
+    tag = (TagNode*)malloc(sizeof(TagNode));
+    if (!tag) {
+        if (pResult) {
+            *pResult = EXIF_ERR_MEMALLOC;
+        }
+        return NULL;
+    }
+    memset(tag, 0, sizeof(TagNode));
+    tag->tagId = tagId;
+    tag->type = type;
+    tag->count = count;
+
+    if (type == TYPE_ASCII || type == TYPE_UNDEFINED) {
+        tag->byteData = (uint8_t*)malloc(count*sizeof(char));
+    }
+    else if (type == TYPE_BYTE   ||
+             type == TYPE_SBYTE  ||
+             type == TYPE_SHORT  ||
+             type == TYPE_LONG   ||
+             type == TYPE_SSHORT ||
+             type == TYPE_SLONG) {
+        tag->numData = (unsigned int*)malloc(count*sizeof(int));
+    }
+    else if (type == TYPE_RATIONAL ||
+             type == TYPE_SRATIONAL) {
+        tag->numData = (unsigned int*)malloc(count*sizeof(int)*2);
+    }
+    if (pResult) {
+        *pResult = 0;
+    }
+    return (ExifTagNodeInfo*)tag;
+}
+
+/**
+ * removeIfdTableFromIfdTableArray()
+ *
+ * Remove the IFD table from the ifdTableArray
+ *
+ * parameters
+ *  [in] ifdTableArray: address of the IFD tables array
+ *  [in] ifdType : target IFD type
+ *
+ * return
+ *  n: number of the removed IFD tables
+ */
+int EXIF_API_EXPORT exif_removeIfdTableFromIfdTableArray(void **ifdTableArray, EXIF_IFD_TYPE ifdType)
+{
+    int i, num = 0, ret = 0;
+    if (!ifdTableArray) {
+        return 0;
+    }
+    // count IFD tables
+    num = countIfdTableOnIfdTableArray(ifdTableArray);
+    for (;;) { // possibility of multiple entries
+        for (i = 0; i < num; i++) {
+            IfdTable *ifd = ifdTableArray[i];
+            if (ifd->ifdType == ifdType) {
+                freeIfdTable(ifd);
+                ifdTableArray[i] = NULL;
+                ret++;
+                break;
+            }
+        }
+        if (i == num) {
+            break; // no more found
+        }
+        // left justify the array
+        memcpy(&ifdTableArray[i], &ifdTableArray[i+1], (num-i) * sizeof(void*));
+        num--;
+    }
+    return ret;
+}
+
+/**
+ * insertIfdTableToIfdTableArray()
+ *
+ * Insert new IFD table to the ifdTableArray
+ *
+ * parameters
+ *  [in] ifdTableArray: address of the IFD tables array
+ *  [in] ifdType : target IFD type
+ *  [out] pResult : error status
+ *   0: OK
+ *  -n: error
+ *      EXIF_ERR_ALREADY_EXIST
+ *      EXIF_ERR_MEMALLOC
+ *
+ * return
+ *  NULL: error
+ * !NULL: address of the newly created ifdTableArray
+ *
+ * note
+ * This function frees old ifdTableArray if is not NULL.
+ */
+void EXIF_API_EXPORT ** exif_insertIfdTableToIfdTableArray(void **ifdTableArray,
+                                     EXIF_IFD_TYPE ifdType,
+                                     int *pResult)
+{
+    void *newIfd;
+    void **newIfdTableArray;
+    int num = 0;
+    if (!ifdTableArray) {
+        num = 0;
+    } else {
+        num = countIfdTableOnIfdTableArray(ifdTableArray);
+    }
+    if (num > 0 && getIfdTableFromIfdTableArray(ifdTableArray, ifdType) != NULL) {
+        if (pResult) {
+            *pResult = EXIF_ERR_ALREADY_EXIST;
+        }
+        return NULL;
+    }
+    // create the new IFD table
+    newIfd = createIfdTable(ifdType, 0, 0);
+    if (!newIfd) {
+        if (pResult) {
+            *pResult = EXIF_ERR_MEMALLOC;
+        }
+        return NULL;
+    }
+    // copy existing IFD tables to the new array
+    newIfdTableArray = (void**)malloc(sizeof(void*)*(num+2));
+    if (!newIfdTableArray) {
+        if (pResult) {
+            *pResult = EXIF_ERR_MEMALLOC;
+        }
+        free(newIfd);
+        return NULL;
+    }
+    memset(newIfdTableArray, 0, sizeof(void*)*(num+2));
+    if (num > 0) {
+        memcpy(newIfdTableArray, ifdTableArray, num * sizeof(void*));
+    }
+    // add the new IFD table
+    newIfdTableArray[num] = newIfd;
+    if (ifdTableArray) {
+        free(ifdTableArray); // free the old array
+    }
+    if (pResult) {
+        *pResult = 0;
+    }
+    return newIfdTableArray;
+}
+
+/**
+ * removeTagNodeFromIfdTableArray()
+ *
+ * Remove the specified tag node from the IFD table
+ *
+ * parameters
+ *  [in] ifdTableArray: address of the IFD tables array
+ *  [in] ifdType : target IFD type
+ *  [in] tagId : target tag ID
+ *
+ * return
+ *  n: number of the removed tags
+ */
+int EXIF_API_EXPORT exif_removeTagNodeFromIfdTableArray(void **ifdTableArray,
+                             EXIF_IFD_TYPE ifdType,
+                             uint16_t tagId)
+{
+    IfdTable *ifd = getIfdTableFromIfdTableArray(ifdTableArray, ifdType);
+    if (!ifd) {
+        return 0;
+    }
+    return removeTagOnIfd(ifd, tagId);
+}
+
+/**
+ * insertTagNodeToIfdTableArray()
+ *
+ * Insert the specified tag node to the IFD table
+ *
+ * parameters
+ *  [in] ifdTableArray: address of the IFD tables array
+ *  [in] ifdType : target IFD type
+ *  [in] ExifTagNodeInfo: address of the ExifTagNodeInfo
+ *
+ * note
+ * This function uses the copy of the specified tag data.
+ * The caller must free it after this function returns.
+ *
+ * return
+ *  0: OK
+ *  EXIF_ERR_INVALID_POINTER:
+ *  EXIF_ERR_NOT_EXIST:
+ *  EXIF_ERR_ALREADY_EXIST:
+ *  EXIF_ERR_UNKNOWN:
+ */
+int EXIF_API_EXPORT exif_insertTagNodeToIfdTableArray(void **ifdTableArray,
+                             EXIF_IFD_TYPE ifdType,
+                             ExifTagNodeInfo *ExifTagNodeInfo)
+{
+    IfdTable *ifd;
+    if (!ifdTableArray) {
+        return EXIF_ERR_INVALID_POINTER;
+    }
+    if (!ExifTagNodeInfo) {
+        return EXIF_ERR_INVALID_POINTER;
+    }
+    ifd = getIfdTableFromIfdTableArray(ifdTableArray, ifdType);
+    if (!ifd) {
+        return EXIF_ERR_NOT_EXIST;
+    }
+    // already exists the same type entry
+    if (getTagNodePtrFromIfd(ifd, ExifTagNodeInfo->tagId) != NULL) {
+        return EXIF_ERR_ALREADY_EXIST;
+    }
+    // add to the IFD table
+    if (!addTagNodeToIfd(ifd, 
+                    ExifTagNodeInfo->tagId,
+                    ExifTagNodeInfo->type,
+                    ExifTagNodeInfo->count,
+                    ExifTagNodeInfo->numData,
+                    ExifTagNodeInfo->byteData)) {
+        return EXIF_ERR_UNKNOWN;
+    }
+    ifd->tagCount++;
+    return 0;
+}
+
+/**
+ * getThumbnailDataOnIfdTableArray()
+ *
+ * Get a copy of the thumbnail data from the 1st IFD table
+ *
+ * parameters
+ *  [in] ifdTableArray : address of the IFD tables array
+ *  [out] pLength : returns the length of the thumbnail data
+ *  [out] pResult : error status
+ *   0: OK
+ *  -n: error
+ *      EXIF_ERR_INVALID_POINTER
+ *      EXIF_ERR_MEMALLOC
+ *      EXIF_ERR_NOT_EXIST
+ *
+ * return
+ *  NULL: error
+ * !NULL: the thumbnail data
+ *
+ * note
+ * This function returns the copy of the thumbnail data.
+ * The caller must free it.
+ */
+uint8_t EXIF_API_EXPORT * exif_getThumbnailDataOnIfdTableArray(void **ifdTableArray,
+                                               unsigned int *pLength,
+                                               int *pResult)
+{
+    IfdTable *ifd;
+    TagNode *tag;
+    unsigned int len;
+    uint8_t *retp;
+    if (!ifdTableArray || !pLength) {
+        if (pResult) {
+            *pResult = EXIF_ERR_INVALID_POINTER;
+        }
+        return NULL;
+    }
+    ifd = getIfdTableFromIfdTableArray(ifdTableArray, IFD_1ST);
+    if (!ifd || !ifd->p) {
+        if (pResult) {
+            *pResult = EXIF_ERR_NOT_EXIST;
+        }
+        return NULL;
+    }
+    tag = getTagNodePtrFromIfd(ifd, TAG_JPEGInterchangeFormatLength);
+    if (!tag || tag->error) {
+        if (pResult) {
+            *pResult = EXIF_ERR_NOT_EXIST;
+        }
+        return NULL;
+    }
+    len = tag->numData[0];
+    if (len <= 0) {
+        if (pResult) {
+            *pResult = EXIF_ERR_NOT_EXIST;
+        }
+        return NULL;
+    }
+    retp= (uint8_t*)malloc(len);
+    if (!retp) {
+        if (pResult) {
+            *pResult = EXIF_ERR_MEMALLOC;
+        }
+        return NULL;
+    }
+    memcpy(retp, ifd->p, len);
+    *pLength = len;
+    if (pResult) {
+        *pResult = 0;
+    }
+    return retp;
+}
+
+/**
+ * setThumbnailDataOnIfdTableArray()
+ *
+ * Set or update the thumbnail data to the 1st IFD table
+ *
+ * parameters
+ *  [in] ifdTableArray : address of the IFD tables array
+ *  [in] pData : thumbnail data
+ *  [in] length : thumbnail data length
+ *
+ * note
+ * This function creates the copy of the specified data.
+ * The caller must free it after this function returns.
+ *
+ * return
+ *   0: OK
+ *  -n: error
+ *      EXIF_ERR_INVALID_POINTER
+ *      EXIF_ERR_MEMALLOC
+ *      EXIF_ERR_UNKNOWN
+ */
+int EXIF_API_EXPORT exif_setThumbnailDataOnIfdTableArray(void **ifdTableArray,
+                                    uint8_t *pData,
+                                    unsigned int length)
+{
+    IfdTable *ifd;
+    TagNode *tag;
+    unsigned int zero = 0;
+    if (!ifdTableArray || !pData || length <= 0) {
+        return EXIF_ERR_INVALID_POINTER;
+    }
+    ifd = getIfdTableFromIfdTableArray(ifdTableArray, IFD_1ST);
+    if (!ifd) {
+        int count = countIfdTableOnIfdTableArray(ifdTableArray);
+        void* ifd1st = createIfdTable(IFD_1ST, 0, 0);
+        printf("count=%d ifd1st=%p\n", count, ifd1st);
+        ifdTableArray[count] = ifd1st;
+        ifd = getIfdTableFromIfdTableArray(ifdTableArray, IFD_1ST);
+        if (!ifd) {
+            return EXIF_ERR_NOT_EXIST;
+        }
+    }
+    if (ifd->p) {
+        free(ifd->p);
+    }
+    // set thumbnail length;
+    tag = getTagNodePtrFromIfd(ifd, TAG_JPEGInterchangeFormatLength);
+    if (tag) {
+        setSingleNumDataToTag(tag, length);
+    } else {
+        if (!addTagNodeToIfd(ifd, TAG_JPEGInterchangeFormatLength,
+                            TYPE_LONG, 1, &length, NULL)) {
+            return EXIF_ERR_UNKNOWN;
+        }
+    }
+    tag = getTagNodePtrFromIfd(ifd, TAG_JPEGInterchangeFormat);
+    if (tag) {
+        setSingleNumDataToTag(tag, zero);
+    } else {
+        // add thumbnail offset tag if not exist
+        addTagNodeToIfd(ifd, TAG_JPEGInterchangeFormat,
+                            TYPE_LONG, 1, &zero, NULL);
+    }
+    ifd->p = (uint8_t*)malloc(length);
+    if (!ifd->p) {
+        return EXIF_ERR_MEMALLOC;
+    }
+    memcpy(ifd->p, pData, length);
+    return 0;
+}
+
+/**
+ * updateExifSegmentInJPEGFile()
+ *
+ * Update the Exif segment in a JPEG file
+ *
+ * parameters
+ *  [in] inJPEGFileName : original JPEG file
+ *  [in] outJPGEFileName : output JPEG file
+ *  [in] ifdTableArray : address of the IFD tables array
+ *
+ * return
+ *   1: OK
+ *  -n: error
+ *      EXIF_ERR_READ_FILE
+ *      EXIF_ERR_WRITE_FILE
+ *      EXIF_ERR_INVALID_JPEG
+ *      EXIF_ERR_INVALID_APP1HEADER
+ *      EXIF_ERR_INVALID_POINTER
+ *      ERROR_UNKNOWN:
+ */
+int EXIF_API_EXPORT exif_updateExifSegmentInJPEGFile(const char *inJPEGFileName,
+                                const char *outJPGEFileName,
+                                void **ifdTableArray)
+{
+    int ofs;
+    int i, sts = 1, hasExifSegment;
+    size_t readLen, writeLen;
+    uint8_t buf[8192], *p;
+    FILE *fpr = NULL, *fpw = NULL;
+
+    // refresh the length and offset variables in the IFD table
+    sts = fixLengthAndOffsetInIfdTables(ifdTableArray);
+    if (sts != 0) {
+        goto DONE;
+    }
+    fpr = fopen(inJPEGFileName, "rb");
+    if (!fpr) {
+        sts = EXIF_ERR_READ_FILE;
+        goto DONE;
+    }
+    sts = init(fpr);
+    if (sts < 0) {
+        goto DONE;
+    }
+    if (sts == 0) {
+        hasExifSegment = 0;
+        ofs = JpegDQTOffset;
+    } else {
+        hasExifSegment = 1;
+        ofs = App1StartOffset;
+    }
+    fpw = fopen(outJPGEFileName, "wb");
+    if (!fpw) {
+        sts = EXIF_ERR_WRITE_FILE;
+        goto DONE;
+    }
+    // copy the data in front of the Exif segment
+    rewind(fpr);
+    p = buf;
+    if (ofs > sizeof(buf)) {
+        // allocate new buffer if needed
+        p = (uint8_t*)malloc(ofs);
+    }
+    if (!p) {
+        for (i = 0; i < ofs; i++) {
+            fread(buf, 1, sizeof(char), fpr);
+            fwrite(buf, 1, sizeof(char), fpw);
+        }
+    } else {
+        if (fread(p, 1, ofs, fpr) < (size_t)ofs) {
+            sts = EXIF_ERR_READ_FILE;
+            goto DONE;
+        }
+        if (fwrite(p, 1, ofs, fpw) < (size_t)ofs) {
+            sts = EXIF_ERR_WRITE_FILE;
+            goto DONE;
+        }
+        if (p != &buf[0]) {
+            free(p);
+        }
+    }
+    // write new Exif segment
+    sts = writeExifSegment(fpw, ifdTableArray);
+    if (sts != 0) {
+        goto DONE;
+    }
+    sts = 1;
+    if (hasExifSegment) {
+        // seek to the end of the Exif segment
+        ofs = App1StartOffset + sizeof(App1Header.marker) + App1Header.length;
+        if (fseek(fpr, ofs, SEEK_SET) != 0) {
+            sts = EXIF_ERR_READ_FILE;
+            goto DONE;
+        }
+    }
+    // read & write
+    for (;;) {
+        readLen = fread(buf, 1, sizeof(buf), fpr);
+        if (readLen <= 0) {
+            break;
+        }
+        writeLen = fwrite(buf, 1, readLen, fpw);
+        if (writeLen != readLen) {
+            sts = EXIF_ERR_WRITE_FILE;
+            goto DONE;
+        }
+    }
+DONE:
+    if (fpw) {
+        fclose(fpw);
+    }
+    if (fpr) {
+        fclose(fpr);
+    }
+    return sts;
+}
+
+/**
+ * removeAdobeMetadataSegmentFromJPEGFile()
+ *
+ * Remove Adobe's XMP metadata segment from a JPEG file
+ *
+ * parameters
+ *  [in] inJPEGFileName : original JPEG file
+ *  [in] outJPGEFileName : output JPEG file
+ *
+ * return
+ *   1: OK
+ *   0: Adobe's metadata segment is not found
+ *  -n: error
+ *      EXIF_ERR_READ_FILE
+ *      EXIF_ERR_WRITE_FILE
+ *      EXIF_ERR_INVALID_JPEG
+ *      EXIF_ERR_INVALID_APP1HEADER
+ */
+int EXIF_API_EXPORT exif_removeAdobeMetadataSegmentFromJPEGFile(const char *inJPEGFileName,
+                                           const char *outJPGEFileName)
+{
+#define ADOBE_METADATA_ID     "http://ns.adobe.com/xap/"
+#define ADOBE_METADATA_ID_LEN 24
+
+    typedef struct _SegmentHeader {
+        uint16_t marker;
+        uint16_t length;
+    } SEGMENT_HEADER;
+
+    SEGMENT_HEADER hdr;
+    int i, sts = 1;
+    size_t readLen, writeLen;
+    unsigned int ofs;
+    uint8_t buf[8192], *p;
+    FILE *fpr = NULL, *fpw = NULL;
+
+    fpr = fopen(inJPEGFileName, "rb");
+    if (!fpr) {
+        sts = EXIF_ERR_READ_FILE;
+        goto DONE;
+    }
+	sts = getAppNStartOffset(fpr, APP1_MARKER, ADOBE_METADATA_ID, ADOBE_METADATA_ID_LEN, NULL);
+    if (sts <= 0) { // target segment is not exist or something error
+        goto DONE;
+    }
+    ofs = sts;
+    sts = 1;
+    fpw = fopen(outJPGEFileName, "wb");
+    if (!fpw) {
+        sts = EXIF_ERR_WRITE_FILE;
+        goto DONE;
+    }
+    // copy the data in front of the App1 segment
+    rewind(fpr);
+    p = buf;
+    if (ofs > sizeof(buf)) {
+        // allocate new buffer if needed
+        p = (uint8_t*)malloc(ofs);
+    }
+    if (!p) {
+        for (i = 0; i < (int)ofs; i++) {
+            fread(buf, 1, sizeof(char), fpr);
+            fwrite(buf, 1, sizeof(char), fpw);
+        }
+    } else {
+        if (fread(p, 1, ofs, fpr) < (size_t)ofs) {
+            sts = EXIF_ERR_READ_FILE;
+            goto DONE;
+        }
+        if (fwrite(p, 1, ofs, fpw) < (size_t)ofs) {
+            sts = EXIF_ERR_WRITE_FILE;
+            goto DONE;
+        }
+        if (p != &buf[0]) {
+            free(p);
+        }
+    }
+    if (fread(&hdr, 1, sizeof(SEGMENT_HEADER), fpr) != sizeof(SEGMENT_HEADER)) {
+        sts = EXIF_ERR_READ_FILE;
+        goto DONE;
+    }
+    if (systemIsLittleEndian()) {
+        // the segment length value is always in big-endian order
+        hdr.length = swab16(hdr.length);
+    }
+    // seek to the end of the App1 segment
+    if (fseek(fpr, hdr.length - sizeof(hdr.length), SEEK_CUR) != 0) {
+        sts = EXIF_ERR_READ_FILE;
+        goto DONE;
+    }
+    // read & write
+    for (;;) {
+        readLen = fread(buf, 1, sizeof(buf), fpr);
+        if (readLen <= 0) {
+            break;
+        }
+        writeLen = fwrite(buf, 1, readLen, fpw);
+        if (writeLen != readLen) {
+            sts = EXIF_ERR_WRITE_FILE;
+            goto DONE;
+        }
+    }
+DONE:
+    if (fpw) {
+        fclose(fpw);
+    }
+    if (fpr) {
+        fclose(fpr);
+    }
+    return sts;
+}
+static int seekToRelativeOffset(FILE *fp, unsigned int baseOff, unsigned int ofs)
+{
+    return fseek(fp, baseOff + ofs, SEEK_SET);
+}
+
+static const char *getTagName(int ifdType, uint16_t tagId)
+{
+    if (ifdType == IFD_0TH || ifdType == IFD_1ST || ifdType == IFD_EXIF) {
+        switch (tagId) {
+            case TAG_ImageWidth: return "ImageWidth";
+            case TAG_ImageLength: return "ImageLength";
+            case TAG_BitsPerSample: return "BitsPerSample";
+            case TAG_Compression: return "Compression";
+            case TAG_PhotometricInterpretation: return "PhotometricInterpretation";
+            case TAG_Orientation: return "Orientation";
+            case TAG_SamplesPerPixel: return "SamplesPerPixel";
+            case TAG_PlanarConfiguration: return "PlanarConfiguration";
+            case TAG_YCbCrSubSampling: return "YCbCrSubSampling";
+            case TAG_YCbCrPositioning: return "YCbCrPositioning";
+            case TAG_XResolution: return "XResolution";
+            case TAG_YResolution: return "YResolution";
+            case TAG_ResolutionUnit: return "ResolutionUnit";
+
+            case TAG_StripOffsets: return "StripOffsets";
+            case TAG_RowsPerStrip: return "RowsPerStrip";
+            case TAG_StripByteCounts: return "StripByteCounts";
+            case TAG_JPEGInterchangeFormat: return "JPEGInterchangeFormat";
+            case TAG_JPEGInterchangeFormatLength: return "JPEGInterchangeFormatLength";
+
+            case TAG_TransferFunction: return "TransferFunction";
+            case TAG_WhitePoint: return "WhitePoint";
+            case TAG_PrimaryChromaticities: return "PrimaryChromaticities";
+            case TAG_YCbCrCoefficients: return "YCbCrCoefficients";
+            case TAG_ReferenceBlackWhite: return "ReferenceBlackWhite";
+
+            case TAG_DateTime: return "DateTime";
+            case TAG_ImageDescription: return "ImageDescription";
+            case TAG_Make: return "Make";
+            case TAG_Model: return "Model";
+            case TAG_Software: return "Software";
+            case TAG_Artist: return "Artist";
+            case TAG_Copyright: return "Copyright";
+            case TAG_ExifIFDPointer: return "ExifIFDPointer";
+			case TAG_GPSInfoIFDPointer: return "GPSInfoIFDPointer";
+            case TAG_InteroperabilityIFDPointer: return "InteroperabilityIFDPointer";
+
+            case TAG_Rating: return "Rating";
+
+            case TAG_ExifVersion: return "ExifVersion";
+            case TAG_FlashPixVersion: return "FlashPixVersion";
+
+            case TAG_ColorSpace: return "ColorSpace";
+
+            case TAG_ComponentsConfiguration: return "ComponentsConfiguration";
+            case TAG_CompressedBitsPerPixel: return "CompressedBitsPerPixel";
+            case TAG_PixelXDimension: return "PixelXDimension";
+            case TAG_PixelYDimension: return "PixelYDimension";
+
+            case TAG_MakerNote: return "MakerNote";
+            case TAG_UserComment: return "UserComment";
+
+            case TAG_RelatedSoundFile: return "RelatedSoundFile";
+
+            case TAG_DateTimeOriginal: return "DateTimeOriginal";
+            case TAG_DateTimeDigitized: return "DateTimeDigitized";
+            case TAG_SubSecTime: return "SubSecTime";
+            case TAG_SubSecTimeOriginal: return "SubSecTimeOriginal";
+            case TAG_SubSecTimeDigitized: return "SubSecTimeDigitized";
+
+            case TAG_ExposureTime: return "ExposureTime";
+            case TAG_FNumber: return "FNumber";
+            case TAG_ExposureProgram: return "ExposureProgram";
+            case TAG_SpectralSensitivity: return "SpectralSensitivity";
+            case TAG_PhotographicSensitivity: return "PhotographicSensitivity";
+            case TAG_OECF: return "OECF";
+            case TAG_SensitivityType: return "SensitivityType";
+            case TAG_StandardOutputSensitivity: return "StandardOutputSensitivity";
+            case TAG_RecommendedExposureIndex: return "RecommendedExposureIndex";
+            case TAG_ISOSpeed: return "ISOSpeed";
+            case TAG_ISOSpeedLatitudeyyy: return "ISOSpeedLatitudeyyy";
+            case TAG_ISOSpeedLatitudezzz: return "ISOSpeedLatitudezzz";
+
+            case TAG_ShutterSpeedValue: return "ShutterSpeedValue";
+            case TAG_ApertureValue: return "ApertureValue";
+            case TAG_BrightnessValue: return "BrightnessValue";
+            case TAG_ExposureBiasValue: return "ExposureBiasValue";
+            case TAG_MaxApertureValue: return "MaxApertureValue";
+            case TAG_SubjectDistance: return "SubjectDistance";
+            case TAG_MeteringMode: return "MeteringMode";
+            case TAG_LightSource: return "LightSource";
+            case TAG_Flash: return "Flash";
+            case TAG_FocalLength: return "FocalLength";
+            case TAG_SubjectArea: return "SubjectArea";
+            case TAG_FlashEnergy: return "FlashEnergy";
+            case TAG_SpatialFrequencyResponse: return "SpatialFrequencyResponse";
+            case TAG_FocalPlaneXResolution: return "FocalPlaneXResolution";
+            case TAG_FocalPlaneYResolution: return "FocalPlaneYResolution";
+            case TAG_FocalPlaneResolutionUnit: return "FocalPlaneResolutionUnit";
+            case TAG_SubjectLocation: return "SubjectLocation";
+            case TAG_ExposureIndex: return "ExposureIndex";
+            case TAG_SensingMethod: return "SensingMethod";
+            case TAG_FileSource: return "FileSource";
+            case TAG_SceneType: return "SceneType";
+            case TAG_CFAPattern: return "CFAPattern";
+
+            case TAG_CustomRendered: return "CustomRendered";
+            case TAG_ExposureMode: return "ExposureMode";
+            case TAG_WhiteBalance: return "WhiteBalance";
+            case TAG_DigitalZoomRatio: return "DigitalZoomRatio";
+            case TAG_FocalLengthIn35mmFormat: return "FocalLengthIn35mmFormat";
+            case TAG_SceneCaptureType: return "SceneCaptureType";
+            case TAG_GainControl: return "GainControl";
+            case TAG_Contrast: return "Contrast";
+            case TAG_Saturation: return "Saturation";
+            case TAG_Sharpness: return "Sharpness";
+            case TAG_DeviceSettingDescription: return "DeviceSettingDescription";
+            case TAG_SubjectDistanceRange: return "SubjectDistanceRange";
+
+            case TAG_ImageUniqueID: return "ImageUniqueID";
+            case TAG_CameraOwnerName: return "CameraOwnerName";
+            case TAG_BodySerialNumber: return "BodySerialNumber";
+            case TAG_LensSpecification: return "LensSpecification";
+            case TAG_LensMake: return "LensMake";
+            case TAG_LensModel: return "LensModel";
+            case TAG_LensSerialNumber: return "LensSerialNumber";
+            case TAG_Gamma: return "Gamma";
+            case TAG_PrintIM: return "PrintIM";
+            case TAG_Padding: return "Padding";
+            default: ;
+        }
+    } else if (ifdType == IFD_GPS) {
+        switch (tagId) {
+            case TAG_GPSVersionID: return "GPSVersionID";
+            case TAG_GPSLatitudeRef: return "GPSLatitudeRef";
+            case TAG_GPSLatitude: return "GPSLatitude";
+            case TAG_GPSLongitudeRef: return "GPSLongitudeRef";
+            case TAG_GPSLongitude: return "GPSLongitude";
+            case TAG_GPSAltitudeRef: return "GPSAltitudeRef";
+            case TAG_GPSAltitude: return "GPSAltitude";
+            case TAG_GPSTimeStamp: return "GPSTimeStamp";
+            case TAG_GPSSatellites: return "GPSSatellites";
+            case TAG_GPSStatus: return "GPSStatus";
+            case TAG_GPSMeasureMode: return "GPSMeasureMode";
+            case TAG_GPSDOP: return "GPSDOP";
+            case TAG_GPSSpeedRef: return "GPSSpeedRef";
+            case TAG_GPSSpeed: return "GPSSpeed";
+            case TAG_GPSTrackRef: return "GPSTrackRef";
+            case TAG_GPSTrack: return "GPSTrack";
+            case TAG_GPSImgDirectionRef: return "GPSImgDirectionRef";
+            case TAG_GPSImgDirection: return "GPSImgDirection";
+            case TAG_GPSMapDatum: return "GPSMapDatum";
+            case TAG_GPSDestLatitudeRef: return "GPSDestLatitudeRef";
+            case TAG_GPSDestLatitude: return "GPSDestLatitude";
+            case TAG_GPSDestLongitudeRef: return "GPSDestLongitudeRef";
+            case TAG_GPSDestLongitude: return "GPSDestLongitude";
+            case TAG_GPSBearingRef: return "GPSBearingRef";
+            case TAG_GPSBearing: return "GPSBearing";
+            case TAG_GPSDestDistanceRef: return "GPSDestDistanceRef";
+            case TAG_GPSDestDistance: return "GPSDestDistance";
+            case TAG_GPSProcessingMethod: return "GPSProcessingMethod";
+            case TAG_GPSAreaInformation: return "GPSAreaInformation";
+            case TAG_GPSDateStamp: return "GPSDateStamp";
+            case TAG_GPSDifferential: return "GPSDifferential";
+            case TAG_GPSHPositioningError: return "GPSHPositioningError";
+            default: ;
+        }
+    } else if (ifdType == IFD_IO) {
+        switch (tagId) {
+            case TAG_InteroperabilityIndex: return "InteroperabilityIndex";
+            case TAG_InteroperabilityVersion: return "InteroperabilityVersion";
+            case TAG_RelatedImageFileFormat: return "RelatedImageFileFormat";
+            case TAG_RelatedImageWidth: return "RelatedImageWidth";
+            case TAG_RelatedImageHeight: return "RelatedImageHeight";
+            default: ;
+        }
+	} else if (ifdType == IFD_MPF) {
+		switch (tagId) {
+			case TAG_MPFVersion: return "MPFVersion";
+			case TAG_NumberOfImage: return "NumberOfImage";
+			case TAG_MPImageList: return "MPImageList";
+			case TAG_ImageUIDList: return "ImageUIDList";
+			case TAG_TotalFrames: return "TotalFrames";
+
+			case TAG_MPIndividualNum: return "MPIndividualNum";
+			case TAG_PanOrientation: return "PanOrientation";
+			case TAG_PanOverlapH: return "PanOverlapH";
+			case TAG_PanOverlapV: return "PanOverlapV";
+			case TAG_BaseViewpointNum: return "BaseViewpointNum";
+			case TAG_ConvergenceAngle: return "ConvergenceAngle";
+			case TAG_BaselineLength: return "BaseLineLength";
+			case TAG_VerticalDivergence: return "VerticalDivergence";
+			case TAG_AxisDistanceX: return "AxisDistanceX";
+			case TAG_AxisDistanceY: return "AxisDistanceY";
+			case TAG_AxisDistanceZ: return "AxisDistanceZ";
+			case TAG_YawAngle: return "YawAngle";
+			case TAG_PitchAngle: return "PitchAngle";
+			case TAG_RollAngle: return "RollAngle";
+			default:;
+		}
+		return "(Unknown)";
+	}
+    return "(Unknown)";
+}
+
+// create the IFD table
+static void *createIfdTable(EXIF_IFD_TYPE IfdType, uint16_t tagCount, unsigned int nextOfs)
+{
+    IfdTable *ifd = (IfdTable*)malloc(sizeof(IfdTable));
+    if (!ifd) {
+        return NULL;
+    }
+    memset(ifd, 0, sizeof(IfdTable));
+    ifd->ifdType = IfdType;
+    ifd->tagCount = tagCount;
+    ifd->nextIfdOffset = nextOfs;
+    return ifd;
+}
+
+// add the TagNode enrtry to the IFD table
+static void *addTagNodeToIfd(void *pIfd,
+                      uint16_t tagId,
+                      uint16_t type,
+                      unsigned int count,
+                      unsigned int *numData,
+                      uint8_t *byteData)
+{
+    int i;
+    IfdTable *ifd = (IfdTable*)pIfd;
+    TagNode *tag;
+    if (!ifd) {
+        return NULL;
+    }
+    tag = (TagNode*)malloc(sizeof(TagNode));
+    memset(tag, 0, sizeof(TagNode));
+    tag->tagId = tagId;
+    tag->type = type;
+    tag->count = count;
+
+    if (count > 0) {
+        if (numData != NULL) {
+            int num = count;
+            if (type == TYPE_RATIONAL ||
+                type == TYPE_SRATIONAL) {
+                num *= 2;
+            }
+            tag->numData = (unsigned int*)malloc(sizeof(int)*num);
+            for (i = 0; i < num; i++) {
+                tag->numData[i] = numData[i];
+            }
+        } else if (byteData != NULL) {
+            tag->byteData = (uint8_t*)malloc(count);
+            memcpy(tag->byteData, byteData, count);
+        } else {
+            tag->error = 1;
+        }
+    } else {
+        tag->error = 1;
+    }
+    
+    // first tag
+    if (!ifd->tags) {
+        ifd->tags = tag;
+    } else {
+        TagNode *tagWk = ifd->tags;
+        while (tagWk->next) {
+            tagWk = tagWk->next;
+        }
+        tagWk->next = tag;
+        tag->prev = tagWk;
+    }
+
+    return tag;
+}
+
+// create a copy of TagNode
+static TagNode *duplicateTagNode(TagNode *src)
+{
+    TagNode *dup;
+    size_t len;
+    if (!src || src->count <= 0) {
+        return NULL;
+    }
+    dup = (TagNode*)malloc(sizeof(TagNode));
+    memset(dup, 0, sizeof(TagNode));
+    dup->tagId = src->tagId;
+    dup->type = src->type;
+    dup->count = src->count;
+    dup->error = src->error;
+    if (src->numData) {
+        len = sizeof(int) * src->count;
+        if (src->type == TYPE_RATIONAL ||
+            src->type == TYPE_SRATIONAL) {
+            len *= 2;
+        }
+        dup->numData = (unsigned int*)malloc(len);
+        memcpy(dup->numData, src->numData, len);
+    } else if (src->byteData) {
+        len = sizeof(char) * src->count;
+        dup->byteData = (uint8_t*)malloc(len);
+        memcpy(dup->byteData, src->byteData, len);
+    }
+    return dup;
+}
+
+// free TagNode
+static void freeTagNode(void *pTag)
+{
+    TagNode *tag = (TagNode*)pTag;
+    if (!tag) {
+        return;
+    }
+    if (tag->numData) {
+        free(tag->numData);
+    }
+    if (tag->byteData) {
+        free(tag->byteData);
+    }
+    free(tag);
+}
+
+// free entire IFD table
+static void freeIfdTable(void *pIfd)
+{
+    IfdTable *ifd = (IfdTable*)pIfd;
+    TagNode *tag;
+    if (!ifd) {
+        return;
+    }
+    tag = ifd->tags;
+    if (ifd->p) {
+        free(ifd->p);
+    }
+    free(ifd);
+
+    if (tag) {
+        while (tag->next) {
+            tag = tag->next;
+        }
+        while (tag) {
+            TagNode *tagWk = tag->prev;
+            freeTagNode(tag);
+            tag = tagWk;
+        }
+    }
+    return;
+}
+
+// search the specified tag's node from the IFD table
+static TagNode *getTagNodePtrFromIfd(IfdTable *ifd, uint16_t tagId)
+{
+    TagNode *tag;
+    if (!ifd) {
+        return NULL;
+    }
+    tag = ifd->tags;
+    while (tag) {
+        if (tag->tagId == tagId) {
+            return tag;
+        }
+        tag = tag->next;
+    }
+    return NULL;
+}
+
+// remove the TagNode entry from the IFD table
+static int removeTagOnIfd(void *pIfd, uint16_t tagId)
+{
+    int num = 0;
+    IfdTable *ifd = (IfdTable*)pIfd;
+    TagNode *tag;
+    if (!ifd) {
+        return 0;
+    }
+    for (;;) { // possibility of multiple entries
+        tag = getTagNodePtrFromIfd(ifd, tagId);
+        if (!tag) {
+            break; // no more found
+        }
+        num++;
+        if (tag->prev) {
+            tag->prev->next = tag->next;
+        } else {
+            ifd->tags = tag->next;
+        }
+        if (tag->next) {
+            tag->next->prev = tag->prev;
+        }
+        freeTagNode(tag);
+        ifd->tagCount--;
+    }
+    return num;
+}
+
+// get the IFD table address of the specified type
+static IfdTable *getIfdTableFromIfdTableArray(void **ifdTableArray, EXIF_IFD_TYPE ifdType)
+{
+    int i;
+    if (!ifdTableArray) {
+        return NULL;
+    }
+    for (i = 0; ifdTableArray[i] != NULL; i++) {
+        IfdTable *ifd = ifdTableArray[i];
+        if (ifd->ifdType == ifdType) {
+            return ifd;
+        }
+    }
+    return NULL;
+}
+
+// count IFD tables
+static int countIfdTableOnIfdTableArray(void **ifdTableArray)
+{
+    int i, num = 0;
+    for (i = 0; ifdTableArray[i] != NULL; i++) {
+        num++;
+    }
+    return num;
+}
+
+// set single numeric value to the existing TagNode entry
+static int setSingleNumDataToTag(TagNode *tag, unsigned int value)
+{
+    if (!tag) {
+        return 0;
+    }
+    if (tag->type != TYPE_BYTE   &&
+        tag->type != TYPE_SHORT  &&
+        tag->type != TYPE_LONG   &&
+        tag->type != TYPE_SBYTE  &&
+        tag->type != TYPE_SSHORT &&
+        tag->type != TYPE_SLONG) {
+        return 0;
+    }
+    if (!tag->numData) {
+        tag->numData = (unsigned int*)malloc(sizeof(int));
+    }
+    tag->count = 1;
+    tag->numData[0] = value;
+    tag->error = 0;
+    return 1;
+}
+
+/**
+ * write the Exif segment to the file
+ *
+ * parameters
+ *  [in] fp: the output file pointer
+ *  [in] ifdTableArray: address of the IFD tables array
+ *
+ * return
+ *  0: OK
+ *  EXIF_ERR_WRITE_FILE
+ */
+static int writeExifSegment(FILE *fp, void **ifdTableArray)
+{
+#define IFDMAX 5
+
+    union _packed {
+        unsigned int ui;
+        uint16_t us[2];
+        uint8_t uc[4];
+    };
+
+    IfdTable *ifds[IFDMAX], *ifd0th;
+    TagNode *tag;
+    IFD_TAG tagField;
+    uint16_t num, us;
+    unsigned int ui;
+    int zero = 0;
+    int i, x;
+    unsigned int ofs;
+    union _packed packed;
+    APP_HEADER dupApp1Header = App1Header;
+
+    ifds[0] = getIfdTableFromIfdTableArray(ifdTableArray, IFD_0TH);
+    ifds[1] = getIfdTableFromIfdTableArray(ifdTableArray, IFD_EXIF);
+    ifds[2] = getIfdTableFromIfdTableArray(ifdTableArray, IFD_IO);
+    ifds[3] = getIfdTableFromIfdTableArray(ifdTableArray, IFD_GPS);
+    ifds[4] = getIfdTableFromIfdTableArray(ifdTableArray, IFD_1ST);
+    ifd0th = ifds[0];
+
+    // return if 0th IFD is not exist
+    if (!ifd0th) {
+        return 0;
+    }
+    // get total length of the segment
+    us = sizeof(APP_HEADER) - sizeof(short);
+    for (x = 0; x < IFDMAX; x++) {
+        if (ifds[x]) {
+            us = us + ifds[x]->length;
+        }
+    }
+    // segment length must be written in big-endian
+    if (systemIsLittleEndian()) {
+        us = swab16(us);
+    }
+    dupApp1Header.length = us;
+    dupApp1Header.tiff.reserved = fix_short(dupApp1Header.tiff.reserved);
+    dupApp1Header.tiff.Ifd0thOffset = fix_int(dupApp1Header.tiff.Ifd0thOffset);
+    // write Exif segment Header
+    if (fwrite(&dupApp1Header, 1, sizeof(APP_HEADER), fp) != sizeof(APP_HEADER)) {
+        return EXIF_ERR_WRITE_FILE;
+    }
+
+    // base offset of the Exif segment
+    ofs = sizeof(TIFF_HEADER);
+    for (x = 0; x < IFDMAX; x++) {
+        IfdTable *ifd = ifds[x];
+        if (ifd == NULL) {
+            continue;
+        }
+        // calculate the start offset to write the tag's data of this IFD
+        ofs += sizeof(short) + // sizeof the tag number area
+               sizeof(IFD_TAG) * ifd->tagCount + // sizeof the tag fields
+               sizeof(int);    // sizeof the NextOffset area
+
+        // write actual tag number of the current IFD
+        num = 0;
+        tag = ifd->tags;
+        while (tag) {
+            if (!tag->error) {
+                num++;
+            }
+            tag = tag->next;
+        }
+        us = fix_short(num);
+        if (fwrite(&us, 1, sizeof(short), fp) != sizeof(short)) {
+            return EXIF_ERR_WRITE_FILE;
+        }
+
+        // write the each tag fields
+        tag = ifd->tags;
+        while (tag) {
+            if (tag->error) {
+                tag = tag->next; // ignore
+                continue;
+            }
+            tagField.tag = fix_short(tag->tagId);
+            tagField.type = fix_short(tag->type);
+            tagField.count = fix_int(tag->count);
+            packed.ui = 0;
+
+            switch (tag->type) {
+            case TYPE_ASCII:
+            case TYPE_UNDEFINED:
+                if (tag->count <= 4) {
+                    for (i = 0; i < (int)tag->count; i++) {
+                        packed.uc[i] = tag->byteData[i];
+                    }
+                } else {
+                    packed.ui = fix_int(ofs);
+                    ofs += tag->count;
+                    if (tag->count % 2 != 0) {
+                        ofs++;
+                    }
+                }
+                break;
+            case TYPE_BYTE:
+            case TYPE_SBYTE:
+                if (tag->count <= 4) {
+                    for (i = 0; i < (int)tag->count; i++) {
+                        packed.uc[i] = (uint8_t)tag->numData[i];
+                    }
+                } else {
+                    packed.ui = fix_int(ofs);
+                    ofs += tag->count;
+                    if (tag->count % 2 != 0) {
+                        ofs++;
+                    }
+                }
+                break;
+            case TYPE_SHORT:
+            case TYPE_SSHORT:
+                if (tag->count <= 2) {
+                    for (i = 0; i < (int)tag->count; i++) {
+                        packed.us[i] = fix_short((uint16_t)tag->numData[i]);
+                    }
+                } else {
+                    packed.ui = fix_int(ofs);
+                    ofs += tag->count * sizeof(short);
+                }
+                break;
+            case TYPE_LONG:
+            case TYPE_SLONG:
+                if (tag->count <= 1) {
+                    packed.ui = fix_int((unsigned int)tag->numData[0]);
+                } else {
+                    packed.ui = fix_int(ofs);
+                    ofs += tag->count * sizeof(short);
+                }
+                break;
+            case TYPE_RATIONAL:
+            case TYPE_SRATIONAL:
+                packed.ui = fix_int(ofs);
+                ofs += tag->count * sizeof(int) * 2;
+                break;
+            }
+            tagField.offset = packed.ui;
+            if (fwrite(&tagField, 1, sizeof(tagField), fp) != sizeof(tagField)) {
+                return EXIF_ERR_WRITE_FILE;
+            }
+            tag = tag->next;
+        }
+        ui = fix_int(ifd->nextIfdOffset);
+        if (fwrite(&ui, 1, sizeof(int), fp) != sizeof(int)) {
+            return EXIF_ERR_WRITE_FILE;
+        }
+
+        // write the tag values over 4 bytes 
+        tag = ifd->tags;
+        while (tag) {
+            if (tag->error) {
+                tag = tag->next;
+                continue;
+            }
+            switch (tag->type) {
+            case TYPE_ASCII:
+            case TYPE_UNDEFINED:
+                if (tag->count > 4) {
+                    if (fwrite(tag->byteData, 1, tag->count, fp) != tag->count) {
+                        return EXIF_ERR_WRITE_FILE;
+                    }
+                    if (tag->count % 2 != 0) { // for even boundary
+                        if (fwrite(&zero, 1, sizeof(char), fp) != sizeof(char)) {
+                            return EXIF_ERR_WRITE_FILE;
+                        }
+                    }
+                }
+                break;
+            case TYPE_BYTE:
+            case TYPE_SBYTE:
+                if (tag->count > 4) {
+                    for (i = 0; i < (int)tag->count; i++) {
+                        uint8_t n = (uint8_t)tag->numData[i];
+                        if (fwrite(&n, 1, sizeof(char), fp) != sizeof(char)) {
+                            return EXIF_ERR_WRITE_FILE;
+                        }
+
+                    }
+                    if (tag->count % 2 != 0) {
+                        if (fwrite(&zero, 1, sizeof(char), fp) != sizeof(char)) {
+                            return EXIF_ERR_WRITE_FILE;
+                        }
+                    }
+                }
+                break;
+            case TYPE_SHORT:
+            case TYPE_SSHORT:
+                if (tag->count > 2) {
+                    for (i = 0; i < (int)tag->count; i++) {
+                        uint16_t n = fix_short((uint16_t)tag->numData[i]);
+                        if (fwrite(&n, 1, sizeof(short), fp) != sizeof(short)) {
+                            return EXIF_ERR_WRITE_FILE;
+                        }
+                    }
+                }
+                break;
+            case TYPE_LONG:
+            case TYPE_SLONG:
+                if (tag->count > 1) {
+                    for (i = 0; i < (int)tag->count; i++) {
+                        unsigned int n = fix_int((unsigned int)tag->numData[i]);
+                        if (fwrite(&n, 1, sizeof(int), fp) != sizeof(int)) {
+                            return EXIF_ERR_WRITE_FILE;
+                        }
+                    }
+                }
+                break;
+            case TYPE_RATIONAL:
+            case TYPE_SRATIONAL:
+                for (i = 0; i < (int)tag->count*2; i++) {
+                    unsigned int n = fix_int((unsigned int)tag->numData[i]);
+                    if (fwrite(&n, 1, sizeof(int), fp) != sizeof(int)) {
+                        return EXIF_ERR_WRITE_FILE;
+                    }
+                }
+                break;
+            }
+            tag = tag->next;
+        }
+        // write the thumbnail data in the 1st IFD
+        if (ifd->ifdType == IFD_1ST && ifd->p != NULL) {
+            tag = getTagNodePtrFromIfd(ifd, TAG_JPEGInterchangeFormatLength);
+            if (tag) {
+                if (tag->numData[0] > 0) {
+                    if (fwrite(ifd->p, 1, tag->numData[0], fp) != tag->numData[0]) {
+                        return EXIF_ERR_WRITE_FILE;
+                    }
+                }
+            }
+        }
+    }
+    return 0;
+}
+
+// calculate the actual length of the IFD
+static uint16_t calcIfdSize(void *pIfd)
+{
+    unsigned int size, num = 0;
+    TagNode *tag;
+    IfdTable *ifd = (IfdTable*)pIfd;
+    if (!ifd) {
+        return 0;
+    }
+    // count the actual tag number
+    tag = ifd->tags;
+    while (tag) {
+        if (!tag->error) {
+            num++;
+        }
+        tag = tag->next;
+    }
+
+    size = sizeof(short) + // sizeof the tag number area
+           sizeof(IFD_TAG) * num + // sizeof tag fields
+           sizeof(int); // sizeof the NextOffset area
+
+    // add thumbnail data length
+    if (ifd->ifdType == IFD_1ST) {
+        if (ifd->p) {
+            tag = getTagNodePtrFromIfd(ifd, TAG_JPEGInterchangeFormatLength);
+            if (tag) {
+                size += tag->numData[0];
+            }
+        }
+    }
+    tag = ifd->tags;
+    while (tag) {
+        if (tag->error) {
+            // ignore
+            tag = tag->next;
+            continue;
+        }
+        switch (tag->type) {
+        case TYPE_ASCII:
+        case TYPE_UNDEFINED:
+        case TYPE_BYTE:
+        case TYPE_SBYTE:
+            if (tag->count > 4) {
+                size += tag->count;
+                if (tag->count % 2 != 0) {
+                    // padding for even byte boundary
+                    size += 1;
+                }
+            }
+            break;
+        case TYPE_SHORT:
+        case TYPE_SSHORT:
+            if (tag->count > 2) {
+                size += tag->count * sizeof(short);
+            }
+            break;
+        case TYPE_LONG:
+        case TYPE_SLONG:
+            if (tag->count > 1) {
+                size += tag->count * sizeof(int);
+            }
+            break;
+        case TYPE_RATIONAL:
+        case TYPE_SRATIONAL:
+            if (tag->count > 0) {
+                size += tag->count * sizeof(int) * 2;
+            }
+            break;
+        }
+        tag = tag->next;
+    }
+    return (uint16_t)size;
+}
+
+/**
+ * refresh the length and offset variables in the IFD tables
+ *
+ * parameters
+ *  [in/out] ifdTableArray: address of the IFD tables array
+ *
+ * return
+ *  0: OK
+ *  EXIF_ERR_INVALID_POINTER
+ *  ERROR_UNKNOWN
+ */
+static int fixLengthAndOffsetInIfdTables(void **ifdTableArray)
+{
+    int i;
+    TagNode *tag, *tagwk;
+    uint16_t num;
+    uint16_t ofsBase = sizeof(TIFF_HEADER);
+    unsigned int len, dummy = 0, again = 0;
+    IfdTable *ifd0th, *ifdExif, *ifdIo, *ifdGps, *ifd1st; 
+    if (!ifdTableArray) {
+        return EXIF_ERR_INVALID_POINTER;
+    }
+
+AGAIN:
+    // calculate the length of the each IFD tables.
+    for (i = 0; ifdTableArray[i] != NULL; i++) {
+        IfdTable *ifd = ifdTableArray[i];
+        // count the actual tag number
+        tag = ifd->tags;
+        num = 0;
+        while (tag) {
+            // ignore and dispose the error tag
+            if (tag->error) {
+                tagwk = tag->next;
+                if (tag->prev) {
+                    tag->prev->next = tag->next;
+                } else {
+                    ifd->tags = tag->next;
+                }
+                if (tag->next) {
+                    tag->next->prev = tag->prev;
+                }
+                freeTagNode(tag);
+                tag = tagwk;
+                continue;
+            }
+            num++;
+            tag = tag->next;
+        }
+        ifd->tagCount = num;
+        ifd->length = calcIfdSize(ifd);
+        ifd->nextIfdOffset = 0;
+    }
+    ifd0th  = getIfdTableFromIfdTableArray(ifdTableArray, IFD_0TH);
+    ifdExif = getIfdTableFromIfdTableArray(ifdTableArray, IFD_EXIF);
+    ifdIo   = getIfdTableFromIfdTableArray(ifdTableArray, IFD_IO);
+    ifdGps  = getIfdTableFromIfdTableArray(ifdTableArray, IFD_GPS);
+    ifd1st  = getIfdTableFromIfdTableArray(ifdTableArray, IFD_1ST);
+
+    if (!ifd0th) {
+        return 0; // not error
+    }
+    ifd0th->offset = ofsBase;
+
+    // set "NextOffset" variable in the 0th IFD if the 1st IFD is exist
+    if (ifd1st) {
+        ifd0th->nextIfdOffset =
+                ofsBase +
+                ifd0th->length + 
+                ((ifdExif)? ifdExif->length : 0) +
+                ((ifdIo)? ifdIo->length : 0) +
+                ((ifdGps)? ifdGps->length : 0);
+        ifd1st->offset = (uint16_t)ifd0th->nextIfdOffset;
+
+        // set the offset value of the thumbnail data
+        if (ifd1st->p) {
+            tag = getTagNodePtrFromIfd(ifd1st, TAG_JPEGInterchangeFormatLength);
+            if (tag) {
+                len = tag->numData[0]; // thumbnail length;
+                tag = getTagNodePtrFromIfd(ifd1st, TAG_JPEGInterchangeFormat);
+                if (tag) {
+                    // set the offset value
+                    setSingleNumDataToTag(tag, ifd1st->offset + ifd1st->length - len);
+                } else {
+                    // create the JPEGInterchangeFormat tag if not exist
+                    if (!addTagNodeToIfd(ifd1st, TAG_JPEGInterchangeFormat, 
+                        TYPE_LONG, 1, &dummy, NULL)) {
+                        return EXIF_ERR_UNKNOWN;
+                    }
+                    again = 1;
+                }
+            } else {
+                tag = getTagNodePtrFromIfd(ifd1st, TAG_JPEGInterchangeFormat);
+                if (tag) {
+                    setSingleNumDataToTag(tag, 0);
+                }
+            }
+        }
+    } else {
+        ifd0th->nextIfdOffset = 0; // 1st IFD is not exist
+    }
+
+    // set "ExifIFDPointer" tag value in the 0th IFD if the Exif IFD is exist
+    if (ifdExif) {
+        tag = getTagNodePtrFromIfd(ifd0th, TAG_ExifIFDPointer);
+        if (tag) {
+            setSingleNumDataToTag(tag, ofsBase + ifd0th->length);
+            ifdExif->offset = (uint16_t)tag->numData[0];
+        } else {
+            // create the tag if not exist
+            if (!addTagNodeToIfd(ifd0th, TAG_ExifIFDPointer, TYPE_LONG, 1,
+                                    &dummy, NULL)) {
+                return EXIF_ERR_UNKNOWN;
+            }
+            again = 1;
+        }
+        // set "InteroperabilityIFDPointer" tag value in the Exif IFD
+        // if the Interoperability IFD is exist
+        if (ifdIo) {
+            tag = getTagNodePtrFromIfd(ifdExif, TAG_InteroperabilityIFDPointer);
+            if (tag) {
+                setSingleNumDataToTag(tag, ofsBase + ifd0th->length + ifdExif->length);
+                ifdIo->offset = (uint16_t)tag->numData[0];
+            } else {
+                // create the tag if not exist
+                if (!addTagNodeToIfd(ifdExif, TAG_InteroperabilityIFDPointer,
+                                        TYPE_LONG, 1, &dummy, NULL)) {
+                    return EXIF_ERR_UNKNOWN;
+                }
+                again = 1;
+            }
+        } else {
+            tag = getTagNodePtrFromIfd(ifdExif, TAG_InteroperabilityIFDPointer);
+            if (tag) {
+                setSingleNumDataToTag(tag, 0);
+            }
+        }
+    } else { // Exif 
+        tag = getTagNodePtrFromIfd(ifd0th, TAG_ExifIFDPointer);
+        if (tag) {
+            setSingleNumDataToTag(tag, 0);
+        }
+    }
+
+    // set "GPSInfoIFDPointer" tag value in the 0th IFD if the GPS IFD is exist
+    if (ifdGps) {
+        tag = getTagNodePtrFromIfd(ifd0th, TAG_GPSInfoIFDPointer);
+        if (tag) {
+            setSingleNumDataToTag(tag, ofsBase +
+                                        ifd0th->length + 
+                                        ((ifdExif)? ifdExif->length : 0) +
+                                        ((ifdIo)? ifdIo->length : 0));
+            ifdGps->offset = (uint16_t)tag->numData[0];
+        } else {
+            // create the tag if not exist
+            if (!addTagNodeToIfd(ifd0th, TAG_GPSInfoIFDPointer, TYPE_LONG,
+                                1, &dummy, NULL)) {
+                return EXIF_ERR_UNKNOWN;
+            }
+            again = 1;
+        }
+    } else { // GPS IFD is not exist
+        tag = getTagNodePtrFromIfd(ifd0th, TAG_GPSInfoIFDPointer);
+        if (tag) {
+            setSingleNumDataToTag(tag, 0);
+        }
+    }
+    // repeat again if needed
+    if (again) {
+        again = 0;
+        goto AGAIN;
+    }
+    return 0;
+}
+
+/**
+ * Set the data of the IFD to the internal table
+ *
+ * parameters
+ *  [in] fp: file pointer of opened file
+ *  [in] startOffset : offset of target IFD
+ *  [in] ifdType : type of the IFD
+ *
+ * return
+ *   NULL: critical error occurred
+ *  !NULL: the address of the IFD table
+ */
+static void *parseIFD(FILE *fp,
+					  unsigned int baseOffset,
+                      unsigned int startOffset,
+                      EXIF_IFD_TYPE ifdType)
+{
+    void *ifd;
+    uint8_t buf[8192];
+    uint16_t tagCount, us;
+    unsigned int nextOffset = 0;
+    unsigned int *array, val, allocSize;
+    int size, cnt, i;
+    size_t len;
+    int pos;
+    
+    // get the count of the tags
+    if (seekToRelativeOffset(fp, baseOffset, startOffset) != 0 ||
+        fread(&tagCount, 1, sizeof(short), fp) < sizeof(short)) {
+        return NULL;
+    }
+    tagCount = fix_short(tagCount);
+    pos = ftell(fp);
+
+    // in case of the 0th IFD, check the offset of the 1st IFD
+    if (ifdType == IFD_0TH || ifdType == IFD_MPF) {
+        // next IFD's offset is at the tail of the segment
+        if (seekToRelativeOffset(fp, baseOffset, 
+                sizeof(TIFF_HEADER) + sizeof(short) + sizeof(IFD_TAG) * tagCount) != 0 ||
+            fread(&nextOffset, 1, sizeof(int), fp) < sizeof(int)) {
+            return NULL;
+        }
+        nextOffset = fix_int(nextOffset);
+        fseek(fp, pos, SEEK_SET);
+    }
+    // create new IFD table
+    ifd = createIfdTable(ifdType, tagCount, nextOffset);
+
+    // parse all tags
+    for (cnt = 0; cnt < tagCount; cnt++) {
+        IFD_TAG tag;
+        uint8_t data[4];
+        if (fseek(fp, pos, SEEK_SET) != 0 ||
+            fread(&tag, 1, sizeof(tag), fp) < sizeof(tag)) {
+            goto ERR;
+        }
+        memcpy(data, &tag.offset, 4); // keep raw data temporary
+        tag.tag = fix_short(tag.tag);
+        tag.type = fix_short(tag.type);
+        tag.count = fix_int(tag.count);
+        tag.offset = fix_int(tag.offset);
+        pos = ftell(fp);
+
+        //printf("tag=0x%04X type=%u count=%u offset=%u name=[%s]\n",
+        //  tag.tag, tag.type, tag.count, tag.offset, getTagName(ifdType, tag.tag));
+
+        if (tag.type == TYPE_ASCII ||     // ascii = the null-terminated string
+            tag.type == TYPE_UNDEFINED) { // undefined = the chunk data bytes
+            if (tag.count <= 4)  {
+                // 4 bytes or less data is placed in the 'offset' area directly
+                addTagNodeToIfd(ifd, tag.tag, tag.type, tag.count, NULL, data);
+            } else {
+                // 5 bytes or more data is placed in the value area of the IFD
+                uint8_t *p = buf;
+                if (tag.count > sizeof(buf)) {
+                    // allocate new buffer if needed
+                    if (tag.count >= App1Header.length) { // illegal
+                        p = NULL;
+                    } else {
+                        p = (uint8_t*)malloc(tag.count);
+                    }
+                    if (!p) {
+                        // treat as an error
+                        addTagNodeToIfd(ifd, tag.tag, tag.type, tag.count, NULL, NULL);
+                        continue;
+                    }
+                    memset(p, 0, tag.count);
+                }
+                if (seekToRelativeOffset(fp, baseOffset, tag.offset) != 0 ||
+                    fread(p, 1, tag.count, fp) < tag.count) {
+                    if (p != &buf[0]) {
+                        free(p);
+                    }
+                    addTagNodeToIfd(ifd, tag.tag, tag.type, tag.count, NULL, NULL);
+                    continue;
+                }
+                addTagNodeToIfd(ifd, tag.tag, tag.type, tag.count, NULL, p);
+                if (p != &buf[0]) {
+                    free(p);
+                }
+            }
+        }
+        else if (tag.type == TYPE_RATIONAL || tag.type == TYPE_SRATIONAL) {
+            unsigned int realCount = tag.count * 2; // need double the space
+            size_t len = realCount * sizeof(int);
+            if (len >= App1Header.length) { // illegal
+                array = NULL;
+            } else {
+                array = (unsigned int*)malloc(len);
+                if (array) {
+                    if (seekToRelativeOffset(fp, baseOffset, tag.offset) != 0 ||
+                        fread(array, 1, len , fp) < len) {
+                        free(array);
+                        array = NULL;
+                    } else {
+                        for (i = 0; i < (int)realCount; i++) {
+                            array[i] = fix_int(array[i]);
+                        }
+                    }
+                }
+            }
+            addTagNodeToIfd(ifd, tag.tag, tag.type, tag.count, array, NULL);
+            if (array) {
+                free(array);
+            }
+        }
+        else if (tag.type == TYPE_BYTE   ||
+                 tag.type == TYPE_SHORT  ||
+                 tag.type == TYPE_LONG   ||
+                 tag.type == TYPE_SBYTE  ||
+                 tag.type == TYPE_SSHORT ||
+                 tag.type == TYPE_SLONG ) {
+
+            // the single value is always stored in tag.offset area directly
+            // # the data is Left-justified if less than 4 bytes
+            if (tag.count <= 1) {
+                val = tag.offset;
+                if (tag.type == TYPE_BYTE || tag.type == TYPE_SBYTE) {
+                    uint8_t uc = data[0];
+                    val = uc;
+                } else if (tag.type == TYPE_SHORT || tag.type == TYPE_SSHORT) {
+                    memcpy(&us, data, sizeof(short));
+                    us = fix_short(us);
+                    val = us;
+                }
+                addTagNodeToIfd(ifd, tag.tag, tag.type, tag.count, &val, NULL);
+             }
+             // multiple value
+             else {
+                size = sizeof(int);
+                if (tag.type == TYPE_BYTE || tag.type == TYPE_SBYTE) {
+                    size = sizeof(char);
+                } else if (tag.type == TYPE_SHORT || tag.type == TYPE_SSHORT) {
+                    size = sizeof(short);
+                }
+                // for the sake of simplicity, using the 4bytes area for
+                // each numeric data type 
+                allocSize = sizeof(int) * tag.count;
+                if (allocSize >= App1Header.length) { // illegal
+                    array = NULL;
+                } else {
+                    array = (unsigned int*)malloc(allocSize);
+                }
+                if (!array) {
+                    addTagNodeToIfd(ifd, tag.tag, tag.type, tag.count, NULL, NULL);
+                    continue;
+                }
+                len = size * tag.count;
+                // if the total length of the value is less than or equal to 4bytes, 
+                // they have been stored in the tag.offset area
+                if (len <= 4) {
+                    if (size == 1) { // byte
+                        for (i = 0; i < (int)tag.count; i++) {
+                            array[i] = (unsigned int)data[i];
+                        }
+                    } else if (size == 2) { // short
+                        for (i = 0; i < 2; i++) {
+                            memcpy(&us, &data[i*2], sizeof(short));
+                            us = fix_short(us);
+                            array[i] = (unsigned int)us;
+                        }
+                    }
+                } else {
+                    if (seekToRelativeOffset(fp, baseOffset, tag.offset) != 0 ||
+                        fread(buf, 1, len , fp) < len) {
+                        addTagNodeToIfd(ifd, tag.tag, tag.type, tag.count, NULL, NULL);
+                        continue;
+                    }
+                    for (i = 0; i < (int)tag.count; i++) {
+                        memcpy(&val, &buf[i*size], size);
+                        if (size == sizeof(int)) {
+                            val = fix_int(val);
+                        } else if (size == sizeof(short)) {
+                            val = fix_short((uint16_t)val);
+                        }
+                        array[i] = (unsigned int)val;
+                    }
+                }
+                addTagNodeToIfd(ifd, tag.tag, tag.type, tag.count, array, NULL);
+                free(array);
+             }
+         }
+    }
+    if (ifdType == IFD_1ST) {
+        // get thumbnail data
+        unsigned int thumbnail_ofs = 0, thumbnail_len;
+        IfdTable *ifdTable = (IfdTable*)ifd;
+        TagNode *tag  = getTagNodePtrFromIfd(ifd, TAG_JPEGInterchangeFormat);
+        if (tag) {
+            thumbnail_ofs = tag->numData[0];
+        }
+        if (thumbnail_ofs > 0) {
+            tag = getTagNodePtrFromIfd(ifd, TAG_JPEGInterchangeFormatLength);
+            if (tag) {
+                thumbnail_len = tag->numData[0];
+                if (thumbnail_len > 0) {
+                    ifdTable->p = (uint8_t*)malloc(thumbnail_len);
+                    if (ifdTable->p) {
+                        if (seekToRelativeOffset(fp, baseOffset, thumbnail_ofs) == 0) {
+                            if (fread(ifdTable->p, 1, thumbnail_len, fp)
+                                                        != thumbnail_len) {
+                                free(ifdTable->p);
+                                ifdTable->p = NULL;
+                            } else {
+                                // for test
+                                //FILE *fpw = fopen("thumbnail.jpg", "wb");
+                                //fwrite(ifdTable->p, 1, thumbnail_len, fpw);
+                                //fclose(fpw);
+                            }
+                        } else {
+                            free(ifdTable->p);
+                            ifdTable->p = NULL;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return ifd;
+ERR:
+    if (ifd) {
+        freeIfdTable(ifd);
+    }
+    return NULL;
+}
+
+
+void setDefaultAppNSegmentHeader(APP_HEADER* appHeader, const char* strId, uint16_t marker)
+{
+    memset(&App1Header, 0, sizeof(APP_HEADER));
+	appHeader->marker = systemIsLittleEndian() ? swab16(marker) : marker;
+    appHeader->length = 0;
+    strncpy(appHeader->id, strId, sizeof(appHeader->id));
+    appHeader->tiff.byteOrder = 0x4949; // means little-endian
+    appHeader->tiff.reserved = 0x002A;
+    appHeader->tiff.Ifd0thOffset = 0x00000008;
+}
+
+void setDefaultMPFSegmentHeader(MPF_HEADER* appHeader, const char* strId, uint16_t marker)
+{
+	memset(&App1Header, 0, sizeof(APP_HEADER));
+	appHeader->marker = systemIsLittleEndian() ? swab16(marker) : marker;
+	appHeader->length = 0;
+	strncpy(appHeader->id, strId, sizeof(appHeader->id));
+	appHeader->tiff.byteOrder = 0x4949; // means little-endian
+	appHeader->tiff.reserved = 0x002A;
+	appHeader->tiff.Ifd0thOffset = 0x00000008;
+}
+
+/**
+ * Load the APP1 segment header
+ *
+ * return
+ *  1: success
+ *  0: error
+ */
+static int readAppNSegmentHeader(FILE *fp, APP_HEADER* appHeader, size_t startOffset)
+{
+    // read the APP1 header
+    if (fseek(fp, startOffset, SEEK_SET) != 0 ||
+        fread(appHeader, 1, sizeof(APP_HEADER), fp) <
+                                            sizeof(APP_HEADER)) {
+        return 0;
+    }
+    if (systemIsLittleEndian()) {
+        // the segment length value is always in big-endian order
+		appHeader->length = swab16(appHeader->length);
+    }
+    // byte-order identifier
+    if (appHeader->tiff.byteOrder != 0x4D4D && // big-endian
+        appHeader->tiff.byteOrder != 0x4949) { // little-endian
+        return 0;
+    }
+    // TIFF version number (always 0x002A)
+    appHeader->tiff.reserved = fix_short(appHeader->tiff.reserved);
+    if (appHeader->tiff.reserved != 0x002A) {
+        return 0;
+    }
+    // offset of the 0TH IFD
+    appHeader->tiff.Ifd0thOffset = fix_int(appHeader->tiff.Ifd0thOffset);
+    return 1;
+}
+
+/**
+* Load the MPF segment header
+*
+* return
+*  1: success
+*  0: error
+*/
+static int readMPFSegmentHeader(FILE *fp, MPF_HEADER* appHeader, size_t startOffset)
+{
+	// read the MPF header
+	if (fseek(fp, startOffset, SEEK_SET) != 0 ||
+		fread(appHeader, 1, sizeof(MPF_HEADER), fp) <
+		sizeof(MPF_HEADER)) {
+		return 0;
+	}
+	if (systemIsLittleEndian()) {
+		// the segment length value is always in big-endian order
+		appHeader->length = swab16(appHeader->length);
+	}
+	// byte-order identifier
+	if (appHeader->tiff.byteOrder != 0x4D4D && // big-endian
+		appHeader->tiff.byteOrder != 0x4949) { // little-endian
+		return 0;
+	}
+	// TIFF version number (always 0x002A)
+	appHeader->tiff.reserved = fix_short(appHeader->tiff.reserved);
+	if (appHeader->tiff.reserved != 0x002A) {
+		return 0;
+	}
+	// offset of the 0TH IFD
+	appHeader->tiff.Ifd0thOffset = fix_int(appHeader->tiff.Ifd0thOffset);
+	return 1;
+}
+/**
+ * Get the offset of the Exif segment in the current opened JPEG file
+ *
+ * return
+ *   n: the offset from the beginning of the file
+ *   0: the Exif segment is not found
+ *  -n: error
+ */
+#define EXIF_ID_STR     "Exif\0"
+#define EXIF_ID_STR_LEN 5
+#define FPXR_ID_STR     "FPXR\0"
+#define FPXR_ID_STR_LEN 5
+#define MPF_ID_STR		"MPF\0"
+#define MPF_ID_STR_LEN	4
+
+static int getAppNStartOffset(FILE *fp,
+							  uint16_t appMarkerN,
+                              const char *App1IDString,
+                              size_t App1IDStringLength,
+                              int *pDQTOffset)
+{
+    int pos;
+    uint8_t buf[64];
+    uint16_t len, marker;
+	uint32_t appn_pos = 0;
+    if (!fp) {
+        return EXIF_ERR_READ_FILE;
+    }
+    rewind(fp);
+
+    // check JPEG SOI Marker (0xFFD8)
+    if (fread(&marker, 1, sizeof(short), fp) < sizeof(short)) {
+        return EXIF_ERR_READ_FILE;
+    }
+    if (systemIsLittleEndian()) {
+        marker = swab16(marker);
+    }
+    if (marker != 0xFFD8) {
+        return EXIF_ERR_INVALID_JPEG;
+    }
+    // check for next 2 bytes
+    if (fread(&marker, 1, sizeof(short), fp) < sizeof(short)) {
+        return EXIF_ERR_READ_FILE;
+    }
+    if (systemIsLittleEndian()) {
+        marker = swab16(marker);
+    }
+    // if DQT marker (0xFFDB) is appeared, the application segment
+    // doesn't exist
+    if (marker == 0xFFDB) {
+        if (pDQTOffset != NULL) {
+            *pDQTOffset = ftell(fp) - sizeof(short);
+        }
+        return 0; // not found the Exif segment
+    }
+
+    pos = ftell(fp);
+    for (;;) {
+        // unexpected value. is not a APP[0-14] marker
+        if (!(marker >= 0xFFE0 && marker <= 0xFFEF)) {
+            // found DQT
+            if (marker == 0xFFDB && pDQTOffset != NULL) {
+                *pDQTOffset = pos - sizeof(short);
+				break;
+            }
+        }
+        // read the length of the segment
+        if (fread(&len, 1, sizeof(short), fp) < sizeof(short)) {
+            return EXIF_ERR_READ_FILE;
+        }
+        if (systemIsLittleEndian()) {
+            len = swab16(len);
+        }
+        // if is not a APPn segment, move to next segment
+		if (marker != appMarkerN) {
+			if (appn_pos != 0) {
+				break;
+			}
+            if (fseek(fp, len - sizeof(short), SEEK_CUR) != 0) {
+                return EXIF_ERR_INVALID_JPEG;
+            }
+        } else {
+            // check if it is the Exif segment
+			size_t bytesread = fread(buf, 1, App1IDStringLength + 4, fp);
+            if (bytesread < App1IDStringLength) {
+                return EXIF_ERR_READ_FILE;
+            }
+            if (memcmp(buf, App1IDString, App1IDStringLength) == 0) {
+                // return the start offset of the Exif segment
+				if (appn_pos == 0) {
+					appn_pos = pos - sizeof(short);
+				}
+			}
+			if (Verbose) {
+				unsigned char c1 = buf[0];
+				unsigned char c2 = buf[1];
+				unsigned char c3 = buf[2];
+				unsigned char c4 = buf[3];
+				if (c4 < ' ') {
+					c4 = '?';
+				}
+				printf("APP%u %c%c%c%c len=%u\n", appMarkerN - APP0_MARKER, c1, c2, c3, c4, len - 2);
+			}
+            // if is not a Exif segment, move to next segment
+            if (fseek(fp, pos, SEEK_SET) != 0 ||
+                fseek(fp, len, SEEK_CUR) != 0) {
+                return EXIF_ERR_INVALID_JPEG;
+            }
+        }
+        // read next marker
+        if (fread(&marker, 1, sizeof(short), fp) < sizeof(short)) {
+            return EXIF_ERR_READ_FILE;
+        }
+        if (systemIsLittleEndian()) {
+            marker = swab16(marker);
+        }
+        pos = ftell(fp);
+    }
+    return appn_pos; // return Exif segment if found
+}
+
+/**
+ * Initialize
+ *
+ * return
+ *   1: OK
+ *   0: the Exif segment is not found
+ *  -n: error
+ */
+static int init(FILE *fp)
+{
+    int sts, dqtOffset = -1;;
+    setDefaultAppNSegmentHeader(&App1Header, "Exif", 0xFFE1);
+	setDefaultAppNSegmentHeader(&App2Header, "FPXR", 0xFFE2);
+	setDefaultMPFSegmentHeader(&MPFHeader, "MPF", 0xFFE2);
+	// get the offset of the Exif segment
+	sts = getAppNStartOffset(fp, APP1_MARKER, EXIF_ID_STR, EXIF_ID_STR_LEN, &dqtOffset);
+    if (sts < 0) { // error
+        return sts;
+    }
+	JpegDQTOffset = dqtOffset;
+	App1StartOffset = sts;
+	if (sts == 0) {
+		return sts;
+	}
+
+	App2StartOffset = getAppNStartOffset(fp, APP2_MARKER, FPXR_ID_STR, FPXR_ID_STR_LEN, NULL);
+
+	MPFStartOffset = getAppNStartOffset(fp, APP2_MARKER, MPF_ID_STR, MPF_ID_STR_LEN, NULL);
+
+	// Load the App1 segment header
+    if (!readAppNSegmentHeader(fp, &App1Header, App1StartOffset)) {
+        return EXIF_ERR_INVALID_APP1HEADER;
+    }
+
+	if (MPFStartOffset > 0) {
+		if (!readMPFSegmentHeader(fp, &MPFHeader, MPFStartOffset)) {
+			return EXIF_ERR_INVALID_APP1HEADER;
+		}
+	}
+    return 1;
+}
+
+static void PRINTF(char **ms, const char *fmt, ...) {
+    char buf[4096];
+    char *p = NULL;
+    int len, cnt;
+    va_list args;
+    va_start(args, fmt);
+    cnt = vsnprintf(buf, sizeof(buf)-1, fmt, args);
+    if (!ms) {
+        printf("%s", buf);
+        return;
+    }
+    else if (*ms) {
+        len = (int)(strlen(*ms) + cnt + 1);
+        p = (char*)malloc(len);
+        strcpy(p, *ms);
+        strcat(p, buf);
+        free(*ms);
+    } else {
+        len = cnt + 1;
+        p = (char*)malloc(len);
+        strcpy(p, buf);
+    }
+    *ms = p;
+    va_end(args);
+}
+
+#ifdef _MSC_VER
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+#endif

+ 751 - 0
src/exif/include/exif.h

@@ -0,0 +1,751 @@
+/*
+ * Copyright (C) 2013 KLab Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef EXIF_H_
+#define EXIF_H_
+
+#ifdef _WIN32
+#define EXIF_API_EXPORT __declspec(dllexport)
+#define EXIF_API_CALL
+#else
+#define EXIF_API_EXPORT /**< API export macro */
+#define EXIF_API_CALL /**< API call macro */
+#endif
+
+#define EXIF_API_EXPORT_CALL EXIF_API_EXPORT EXIF_API_CALL /**< API export and call macro*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /**
+  * setVerbose()
+  *
+  * Verbose output on/off
+  *
+  * parameters
+  *  [in] v : 1=on  0=off
+  */
+void EXIF_API_EXPORT_CALL exif_setVerbose(int);
+
+#ifdef _DEBUG
+#ifdef _MSC_VER
+#define _CRTDBG_MAP_ALLOC
+#include <crtdbg.h>
+//#define new ::new(_NORMAL_BLOCK, __FILE__, __LINE__)
+#endif
+#endif
+#include <stdint.h>
+
+    /**
+     *   Typical Usage:
+     *
+     *   The following code describes how to get "Model" Tag's data
+     *   from 0th IFD in "test.jpg".
+     *
+     *   // Parse the JPEG header and create the pointer array of the IFD tables
+     *   int result;
+     *   void **ifdArray = createIfdTableArray("test.jpg", *result);
+     *   switch (result) {
+     *     case 0:
+     *        :
+     *   }
+     *   if (!ifdArray) {
+     *      return;
+     *   }
+     *
+     *   // Get the TagNodeInfo that matches the IFD_TYPE & TagId
+     *   TagNodeInfo *tag = getTagInfo(ifdArray, IFD_0TH, TAG_Model);
+     *
+     *   if (tag) {
+     *      if (!tag->error) {
+     *          printf("Model=[%s]\n", tag->byteData); // TYPE_ASCII
+     *      }
+     *      freeTagInfo(tag);
+     *   }
+     *
+     *   freeIfdTableArray(ifdArray);
+     *   return;
+     *
+     */
+
+     // IFD Type
+    typedef enum {
+        IFD_UNKNOWN = 0,
+        IFD_0TH,
+        IFD_1ST,
+        IFD_EXIF,
+        IFD_GPS,
+        IFD_IO,
+        IFD_MPF
+    } EXIF_IFD_TYPE;
+
+    // Tag Type
+    typedef enum {
+        TYPE_BYTE = 1,
+        TYPE_ASCII,
+        TYPE_SHORT,
+        TYPE_LONG,
+        TYPE_RATIONAL,
+        TYPE_SBYTE,
+        TYPE_UNDEFINED,
+        TYPE_SSHORT,
+        TYPE_SLONG,
+        TYPE_SRATIONAL
+    } EXIF_IFD_TAG_TYPE;
+
+    // Tag info structure
+    typedef struct _exif_tagNodeInfo ExifTagNodeInfo;
+    struct _exif_tagNodeInfo {
+        uint16_t tagId;    // tag ID (e.g. TAG_Model = 0x0110)
+        uint16_t type;     // data Type (e.g. TYPE_ASCII = 2)
+        unsigned int count;      // count of the data
+        unsigned int* numData;   // numeric data array
+        uint8_t* byteData; // byte data array
+        uint16_t error;    // 0: no error 1: parse error
+    };
+
+    typedef struct _exif_image_dir_ent
+    {
+        uint32_t ImageFlags;
+        uint32_t ImageLength;
+        uint32_t ImageStart;
+        uint16_t Image1EntryNum;
+        uint16_t Image2EntryNum;
+    } EXIF_IMAGE_DIR_ENT;
+    /**
+     * Note:
+     *
+     * [Type = TYPE_BYTE, TYPE_SHORT, TYPE_LONG,
+     *         TYPE_SBYTE, TYPE_SSHORT, TYPE_SLONG]
+     *  'numData' holds numeric values.
+     *  numData[0] to numData[count-1] is accessible.
+     *
+     * [Type = TYPE_RATIONAL, TYPE_SRATIONAL]
+     *  'numData' holds numeric values.
+     *  numData[0] to numData[count*2-1] is accessible.
+     *
+     * [Type = TYPE_ASCII]
+     *  'byteData' holds ascii string data.
+     *  'count' means the whole length of string with '\0' terminator.
+     *
+     * [Type = TYPE_UNDEFINED]
+     *  'byteData' holds byte data.
+     *  byteData[0] to byteData[count-1] is accessible.
+     *
+     * If the original tag field holds the wrong value, the 'error' flag
+     * will be set to 1. In such cases, both 'numData' and 'byteData'
+     * might have set to NULL. So, the flag should be checked first.
+     */
+
+     // error status
+#define EXIF_ERR_READ_FILE            -1
+#define EXIF_ERR_WRITE_FILE           -2
+#define EXIF_ERR_INVALID_JPEG         -3
+#define EXIF_ERR_INVALID_APP1HEADER   -4
+#define EXIF_ERR_INVALID_IFD          -5
+#define EXIF_ERR_INVALID_ID           -6
+#define EXIF_ERR_INVALID_TYPE         -7
+#define EXIF_ERR_INVALID_COUNT        -8
+#define EXIF_ERR_INVALID_POINTER      -9
+#define EXIF_ERR_NOT_EXIST           -10
+#define EXIF_ERR_ALREADY_EXIST       -11
+#define EXIF_ERR_UNKNOWN             -12
+#define EXIF_ERR_MEMALLOC            -13
+
+// public funtions
+
+    /**
+     * removeExifSegmentFromJPEGFile()
+     *
+     * Remove the Exif segment from a JPEG file
+     *
+     * parameters
+     *  [in] inJPEGFileName : original JPEG file
+     *  [in] outJPGEFileName : output JPEG file
+     *
+     * return
+     *   1: OK
+     *   0: the Exif segment is not found
+     *  -n: error
+     *      ERR_READ_FILE
+     *      ERR_WRITE_FILE
+     *      ERR_INVALID_JPEG
+     *      ERR_INVALID_APP1HEADER
+     */
+    int EXIF_API_EXPORT_CALL exif_removeExifSegmentFromJPEGFile(const char* inJPEGFileName,
+        const char* outJPGEFileName);
+
+    /**
+     * fillIfdTableArray()
+     *
+     * Parse the JPEG header and fill in the IFD table
+     *
+     * parameters
+     *  [in] JPEGFileName : target JPEG file
+     *  [out] ifdArray[32] : array of IfdTable pointers
+     *
+     * return
+     *   n: number of IFD tables
+     *   0: the Exif segment is not found
+     *  -n: error
+     *      ERR_READ_FILE
+     *      ERR_INVALID_JPEG
+     *      ERR_INVALID_APP1HEADER
+     *      ERR_INVALID_IFD
+     */
+    int EXIF_API_EXPORT_CALL exif_fillIfdTableArray(const char* JPEGFileName, void* ifdArray[32]);
+
+    /**
+     * createIfdTableArray()
+     *
+     * Parse the JPEG header and create the pointer array of the IFD tables
+     *
+     * parameters
+     *  [in] JPEGFileName : target JPEG file
+     *  [out] result : result status value
+     *   n: number of IFD tables
+     *   0: the Exif segment is not found
+     *  -n: error
+     *      ERR_READ_FILE
+     *      ERR_INVALID_JPEG
+     *      ERR_INVALID_APP1HEADER
+     *      ERR_INVALID_IFD
+     *
+     * return
+     *   NULL: error or no Exif segment
+     *  !NULL: pointer array of the IFD tables
+     */
+    void EXIF_API_EXPORT ** EXIF_API_CALL exif_createIfdTableArray(const char* JPEGFileName, int* result);
+
+    /**
+     * freeIfdTables()
+     *
+     * Free the pointer array of the IFD tables
+     *
+     * parameters
+     *  [in] ifdArray : address of the IFD array
+     */
+    void EXIF_API_EXPORT_CALL exif_freeIfdTables(void* ifdArray[32]);
+
+    /**
+     * freeIfdTableArray()
+     *
+     * Free the pointer array of the IFD tables
+     *
+     * parameters
+     *  [in] ifdArray : address of the IFD array
+     */
+    void EXIF_API_EXPORT_CALL exif_freeIfdTableArray(void** ifdArray);
+
+    /**
+     * getIfdType()
+     *
+     * Returns the type of the IFD
+     *
+     * parameters
+     *  [in] ifd: target IFD
+     *
+     * return
+     *  IFD TYPE value
+     */
+    EXIF_IFD_TYPE EXIF_API_EXPORT_CALL exif_getIfdType(void* ifd);
+
+    /**
+     * dumpIfdTable()
+     *
+     * Dump the IFD table
+     *
+     * parameters
+     *  [in] ifd: target IFD
+     */
+    void EXIF_API_EXPORT_CALL exif_dumpIfdTable(void* ifd);
+
+    /**
+     * dumpIfdTableArray()
+     *
+     * Dump the array of the IFD tables
+     *
+     * parameters
+     *  [in] ifdArray : address of the IFD array
+     */
+    void EXIF_API_EXPORT_CALL exif_dumpIfdTableArray(void** ifdArray);
+
+    /**
+     * getTagInfo()
+     *
+     * Get the TagNodeInfo that matches the IFD_TYPE & TagId
+     *
+     * parameters
+     *  [in] ifdArray : address of the IFD array
+     *  [in] ifdType : target IFD TYPE
+     *  [in] tagId : target tag ID
+     *
+     * return
+     *   NULL: tag is not found
+     *  !NULL: address of the TagNodeInfo structure
+     */
+    ExifTagNodeInfo EXIF_API_EXPORT * EXIF_API_CALL exif_getTagInfo(void** ifdArray,
+        EXIF_IFD_TYPE ifdType,
+        uint16_t tagId);
+
+    /**
+     * getTagInfoFromIfd()
+     *
+     * Get the TagNodeInfo that matches the TagId
+     *
+     * parameters
+     *  [in] ifd : target IFD
+     *  [in] tagId : target tag ID
+     *
+     * return
+     *  NULL: tag is not found
+     *  !NULL: address of the TagNodeInfo structure
+     */
+    ExifTagNodeInfo EXIF_API_EXPORT * EXIF_API_CALL exif_getTagInfoFromIfd(void* ifd, uint16_t tagId);
+
+    /**
+     * freeTagInfo()
+     *
+     * Free the TagNodeInfo allocated by getTagInfo() or getTagInfoFromIfd()
+     *
+     * parameters
+     *  [in] tag : target TagNodeInfo
+     */
+    void EXIF_API_EXPORT_CALL exif_freeTagInfo(void* tag);
+
+    /**
+     * queryTagNodeIsExist()
+     *
+     * Query if the specified tag node is exist in the IFD tables
+     *
+     * parameters
+     *  [in] ifdTableArray: address of the IFD tables array
+     *  [in] ifdType : target IFD type
+     *  [in] tagId : target tag ID
+     *
+     * return
+     *  0: not exist
+     *  1: exist
+     */
+    int EXIF_API_EXPORT_CALL exif_queryTagNodeIsExist(void** ifdTableArray,
+        EXIF_IFD_TYPE ifdType,
+        uint16_t tagId);
+
+    /**
+     * createTagInfo()
+     *
+     * Create new TagNodeInfo block
+     *
+     * parameters
+     *  [in] tagId: id of the tag
+     *  [in] type: type of the tag
+     *  [in] count: data count of the tag
+     *  [out] pResult : error status
+     *   0: OK
+     *  -n: error
+     *      ERR_INVALID_TYPE
+     *      ERR_INVALID_COUNT
+     *      ERR_MEMALLOC
+     *
+     * return
+     *  NULL: error
+     * !NULL: address of the newly created TagNodeInfo
+     */
+    ExifTagNodeInfo EXIF_API_EXPORT * EXIF_API_CALL exif_createTagInfo(uint16_t tagId,
+        uint16_t type,
+        unsigned int count,
+        int* pResult);
+
+    /**
+     * removeIfdTableFromIfdTableArray()
+     *
+     * Remove the IFD table from the ifdTableArray
+     *
+     * parameters
+     *  [in] ifdTableArray: address of the IFD tables array
+     *  [in] ifdType : target IFD type
+     *
+     * return
+     *  n: number of the removed IFD tables
+     */
+    int EXIF_API_EXPORT_CALL exif_removeIfdTableFromIfdTableArray(void** ifdTableArray, EXIF_IFD_TYPE ifdType);
+
+    /**
+     * insertIfdTableToIfdTableArray()
+     *
+     * Insert new IFD table to the ifdTableArray
+     *
+     * parameters
+     *  [in] ifdTableArray: address of the IFD tables array
+     *  [in] ifdType : target IFD type
+     *  [out] pResult : result status
+     *   0: OK
+     *  -n: error
+     *      ERR_INVALID_POINTER
+     *      ERR_ALREADY_EXIST
+     *      ERR_MEMALLOC
+     *
+     * return
+     *  NULL: error
+     * !NULL: address of the newly created ifdTableArray
+     *
+     * note
+     * This function frees old ifdTableArray if is not NULL.
+     */
+    void EXIF_API_EXPORT ** EXIF_API_CALL exif_insertIfdTableToIfdTableArray(void** ifdTableArray,
+        EXIF_IFD_TYPE ifdType,
+        int* pResult);
+
+    /**
+     * removeTagNodeFromIfdTableArray()
+     *
+     * Remove the specified tag node from the IFD table
+     *
+     * parameters
+     *  [in] ifdTableArray: address of the IFD tables array
+     *  [in] ifdType : target IFD type
+     *  [in] tagId : target tag ID
+     *
+     * return
+     *  n: number of the removed tags
+     */
+    int EXIF_API_EXPORT_CALL exif_removeTagNodeFromIfdTableArray(void** ifdTableArray,
+        EXIF_IFD_TYPE ifdType,
+        uint16_t tagId);
+
+    /**
+     * insertTagNodeToIfdTableArray()
+     *
+     * Insert the specified tag node to the IFD table
+     *
+     * parameters
+     *  [in] ifdTableArray: address of the IFD tables array
+     *  [in] ifdType : target IFD type
+     *  [in] tagNodeInfo: address of the TagNodeInfo
+     *
+     * note
+     * This function uses the copy of the specified tag data.
+     * The caller must free it after this function returns.
+     *
+     * return
+     *  0: OK
+     *  ERR_INVALID_POINTER:
+     *  ERR_NOT_EXIST:
+     *  ERR_ALREADY_EXIST:
+     *  ERR_UNKNOWN:
+     */
+    int EXIF_API_EXPORT_CALL exif_insertTagNodeToIfdTableArray(void** ifdTableArray,
+        EXIF_IFD_TYPE ifdType,
+        ExifTagNodeInfo* tagNodeInfo);
+
+    /**
+     * getThumbnailDataOnIfdTableArray()
+     *
+     * Get a copy of the thumbnail data from the 1st IFD table
+     *
+     * parameters
+     *  [in] ifdTableArray : address of the IFD tables array
+     *  [out] pLength : returns the length of the thumbnail data
+     *  [out] pResult : result status
+     *   0: OK
+     *  -n: error
+     *      ERR_INVALID_POINTER
+     *      ERR_MEMALLOC
+     *      ERR_NOT_EXIST
+     *
+     * return
+     *  NULL: error
+     * !NULL: the thumbnail data
+     *
+     * note
+     * This function returns the copy of the thumbnail data.
+     * The caller must free it.
+     */
+    uint8_t EXIF_API_EXPORT * EXIF_API_CALL exif_getThumbnailDataOnIfdTableArray(void** ifdTableArray,
+        unsigned int* pLength,
+        int* pResult);
+
+    /**
+     * setThumbnailDataOnIfdTableArray()
+     *
+     * Set or update the thumbnail data to the 1st IFD table
+     *
+     * parameters
+     *  [in] ifdTableArray : address of the IFD tables array
+     *  [in] pData : thumbnail data
+     *  [in] length : thumbnail data length
+     *
+     * note
+     * This function creates the copy of the specified data.
+     * The caller must free it after this function returns.
+     *
+     * return
+     *   0: OK
+     *  -n: error
+     *      ERR_INVALID_POINTER
+     *      ERR_MEMALLOC
+     *      ERR_UNKNOWN
+     */
+    int EXIF_API_EXPORT_CALL exif_setThumbnailDataOnIfdTableArray(void** ifdTableArray,
+        uint8_t* pData,
+        unsigned int length);
+
+    void EXIF_API_EXPORT_CALL exif_getIfdTableDump(void* pIfd, char** pp);
+
+
+    /**
+     * updateExifSegmentInJPEGFile()
+     *
+     * Update the Exif segment in a JPEG file
+     *
+     * parameters
+     *  [in] inJPEGFileName : original JPEG file
+     *  [in] outJPGEFileName : output JPEG file
+     *  [in] ifdTableArray : address of the IFD tables array
+     *
+     * return
+     *   1: OK
+     *  -n: error
+     *      ERR_READ_FILE
+     *      ERR_WRITE_FILE
+     *      ERR_INVALID_JPEG
+     *      ERR_INVALID_APP1HEADER
+     *      ERR_INVALID_POINTER
+     *      ERROR_UNKNOWN:
+     */
+    int EXIF_API_EXPORT_CALL exif_updateExifSegmentInJPEGFile(const char* inJPEGFileName,
+        const char* outJPGEFileName,
+        void** ifdTableArray);
+
+    void EXIF_API_EXPORT_CALL exif_getIfdTableDump(void* pIfd, char** pp);
+
+    /**
+     * removeAdobeMetadataSegmentFromJPEGFile()
+     *
+     * Remove Adobe's XMP metadata segment from a JPEG file
+     *
+     * parameters
+     *  [in] inJPEGFileName : original JPEG file
+     *  [in] outJPGEFileName : output JPEG file
+     *
+     * return
+     *   1: OK
+     *   0: Adobe's metadata segment is not found
+     *  -n: error
+     *      ERR_READ_FILE
+     *      ERR_WRITE_FILE
+     *      ERR_INVALID_JPEG
+     *      ERR_INVALID_APP1HEADER
+     */
+    int EXIF_API_EXPORT_CALL exif_removeAdobeMetadataSegmentFromJPEGFile(const char* inJPEGFileName,
+        const char* outJPGEFileName);
+
+    // Tag IDs
+    // 0th IFD, 1st IFD, Exif IFD
+#define TAG_ImageWidth                   0x0100
+#define TAG_ImageLength                  0x0101
+#define TAG_BitsPerSample                0x0102
+#define TAG_Compression                  0x0103
+#define TAG_PhotometricInterpretation    0x0106
+#define TAG_Orientation                  0x0112
+#define TAG_SamplesPerPixel              0x0115
+#define TAG_PlanarConfiguration          0x011C
+#define TAG_YCbCrSubSampling             0x0212
+#define TAG_YCbCrPositioning             0x0213
+#define TAG_XResolution                  0x011A
+#define TAG_YResolution                  0x011B
+#define TAG_ResolutionUnit               0x0128
+
+#define TAG_StripOffsets                 0x0111
+#define TAG_RowsPerStrip                 0x0116
+#define TAG_StripByteCounts              0x0117
+#define TAG_JPEGInterchangeFormat        0x0201
+#define TAG_JPEGInterchangeFormatLength  0x0202
+
+#define TAG_TransferFunction             0x012D
+#define TAG_WhitePoint                   0x013E
+#define TAG_PrimaryChromaticities        0x013F
+#define TAG_YCbCrCoefficients            0x0211
+#define TAG_ReferenceBlackWhite          0x0214
+
+#define TAG_DateTime                     0x0132
+#define TAG_ImageDescription             0x010E
+#define TAG_Make                         0x010F
+#define TAG_Model                        0x0110
+#define TAG_Software                     0x0131
+#define TAG_Artist                       0x013B
+#define TAG_Copyright                    0x8298
+#define TAG_ExifIFDPointer               0x8769
+#define TAG_GPSInfoIFDPointer            0x8825
+#define TAG_InteroperabilityIFDPointer   0xA005
+
+#define TAG_Rating                       0x4746
+
+#define TAG_ExifVersion                  0x9000
+#define TAG_FlashPixVersion              0xA000
+
+#define TAG_ColorSpace                   0xA001
+
+#define TAG_ComponentsConfiguration      0x9101
+#define TAG_CompressedBitsPerPixel       0x9102
+#define TAG_PixelXDimension              0xA002
+#define TAG_PixelYDimension              0xA003
+
+#define TAG_MakerNote                    0x927C
+#define TAG_UserComment                  0x9286
+
+#define TAG_RelatedSoundFile             0xA004
+
+#define TAG_DateTimeOriginal             0x9003
+#define TAG_DateTimeDigitized            0x9004
+#define TAG_SubSecTime                   0x9290
+#define TAG_SubSecTimeOriginal           0x9291
+#define TAG_SubSecTimeDigitized          0x9292
+
+#define TAG_ExposureTime                 0x829A
+#define TAG_FNumber                      0x829D
+#define TAG_ExposureProgram              0x8822
+#define TAG_SpectralSensitivity          0x8824
+#define TAG_PhotographicSensitivity      0x8827
+#define TAG_OECF                         0x8828
+#define TAG_SensitivityType              0x8830
+#define TAG_StandardOutputSensitivity    0x8831
+#define TAG_RecommendedExposureIndex     0x8832
+#define TAG_ISOSpeed                     0x8833
+#define TAG_ISOSpeedLatitudeyyy          0x8834
+#define TAG_ISOSpeedLatitudezzz          0x8835
+
+#define TAG_ShutterSpeedValue            0x9201
+#define TAG_ApertureValue                0x9202
+#define TAG_BrightnessValue              0x9203
+#define TAG_ExposureBiasValue            0x9204
+#define TAG_MaxApertureValue             0x9205
+#define TAG_SubjectDistance              0x9206
+#define TAG_MeteringMode                 0x9207
+#define TAG_LightSource                  0x9208
+#define TAG_Flash                        0x9209
+#define TAG_FocalLength                  0x920A
+#define TAG_SubjectArea                  0x9214
+#define TAG_FlashEnergy                  0xA20B
+#define TAG_SpatialFrequencyResponse     0xA20C
+#define TAG_FocalPlaneXResolution        0xA20E
+#define TAG_FocalPlaneYResolution        0xA20F
+#define TAG_FocalPlaneResolutionUnit     0xA210
+#define TAG_SubjectLocation              0xA214
+#define TAG_ExposureIndex                0xA215
+#define TAG_SensingMethod                0xA217
+#define TAG_FileSource                   0xA300
+#define TAG_SceneType                    0xA301
+#define TAG_CFAPattern                   0xA302
+
+#define TAG_CustomRendered               0xA401
+#define TAG_ExposureMode                 0xA402
+#define TAG_WhiteBalance                 0xA403
+#define TAG_DigitalZoomRatio             0xA404
+#define TAG_FocalLengthIn35mmFormat      0xA405
+#define TAG_SceneCaptureType             0xA406
+#define TAG_GainControl                  0xA407
+#define TAG_Contrast                     0xA408
+#define TAG_Saturation                   0xA409
+#define TAG_Sharpness                    0xA40A
+#define TAG_DeviceSettingDescription     0xA40B
+#define TAG_SubjectDistanceRange         0xA40C
+
+#define TAG_ImageUniqueID                0xA420
+#define TAG_CameraOwnerName              0xA430
+#define TAG_BodySerialNumber             0xA431
+#define TAG_LensSpecification            0xA432
+#define TAG_LensMake                     0xA433
+#define TAG_LensModel                    0xA434
+#define TAG_LensSerialNumber             0xA435
+#define TAG_Gamma                        0xA500
+
+#define TAG_PrintIM                      0xC4A5
+#define TAG_Padding                      0xEA1C
+
+// GPS IFD
+#define TAG_GPSVersionID                 0x0000
+#define TAG_GPSLatitudeRef               0x0001
+#define TAG_GPSLatitude                  0x0002
+#define TAG_GPSLongitudeRef              0x0003
+#define TAG_GPSLongitude                 0x0004
+#define TAG_GPSAltitudeRef               0x0005
+#define TAG_GPSAltitude                  0x0006
+#define TAG_GPSTimeStamp                 0x0007
+#define TAG_GPSSatellites                0x0008
+#define TAG_GPSStatus                    0x0009
+#define TAG_GPSMeasureMode               0x000A
+#define TAG_GPSDOP                       0x000B
+#define TAG_GPSSpeedRef                  0x000C
+#define TAG_GPSSpeed                     0x000D
+#define TAG_GPSTrackRef                  0x000E
+#define TAG_GPSTrack                     0x000F
+#define TAG_GPSImgDirectionRef           0x0010
+#define TAG_GPSImgDirection              0x0011
+#define TAG_GPSMapDatum                  0x0012
+#define TAG_GPSDestLatitudeRef           0x0013
+#define TAG_GPSDestLatitude              0x0014
+#define TAG_GPSDestLongitudeRef          0x0015
+#define TAG_GPSDestLongitude             0x0016
+#define TAG_GPSBearingRef                0x0017
+#define TAG_GPSBearing                   0x0018
+#define TAG_GPSDestDistanceRef           0x0019
+#define TAG_GPSDestDistance              0x001A
+#define TAG_GPSProcessingMethod          0x001B
+#define TAG_GPSAreaInformation           0x001C
+#define TAG_GPSDateStamp                 0x001D
+#define TAG_GPSDifferential              0x001E
+#define TAG_GPSHPositioningError         0x001F
+
+// Interoperability IFD
+#define TAG_InteroperabilityIndex        0x0001
+#define TAG_InteroperabilityVersion      0x0002
+
+#define TAG_RelatedImageFileFormat       0x1000
+#define TAG_RelatedImageWidth            0x1001
+#define TAG_RelatedImageHeight           0x1002
+
+// MPF tags
+#define TAG_MPFVersion                   0xB000
+#define TAG_NumberOfImage                0xB001
+#define TAG_MPImageList                  0xB002
+#define TAG_ImageUIDList                 0xB003
+#define TAG_TotalFrames                  0xB004
+
+#define TAG_MPIndividualNum              0xB101
+
+#define TAG_PanOrientation               0xB201
+#define TAG_PanOverlapH                  0xB202
+#define TAG_PanOverlapV                  0xB203
+#define TAG_BaseViewpointNum             0xB204
+#define TAG_ConvergenceAngle             0xB205
+#define TAG_BaselineLength               0xB206
+#define TAG_VerticalDivergence           0xB207
+#define TAG_AxisDistanceX                0xB208
+#define TAG_AxisDistanceY                0xB209
+#define TAG_AxisDistanceZ                0xB20A
+#define TAG_YawAngle                     0xB20B
+#define TAG_PitchAngle                   0xB20C
+#define TAG_RollAngle                    0xB20D
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif // _EXIF_H_

+ 6 - 0
src/libslic3r/AppConfig.cpp

@@ -214,6 +214,12 @@ void AppConfig::set_defaults()
     if (get("show_splash_screen").empty())
         set("show_splash_screen", "1");
 
+    if (get("splash_screen_editor").empty())
+        set("splash_screen_editor", "benchy-splashscreen.jpg");
+
+    if (get("splash_screen_gcodeviewer").empty())
+        set("splash_screen_gcodeviewer", "prusa-gcodepreview.jpg");
+
 #if ENABLE_CTRL_M_ON_WINDOWS
 #ifdef _WIN32
     if (get("use_legacy_3DConnexion").empty())

+ 1 - 1
src/slic3r/CMakeLists.txt

@@ -257,7 +257,7 @@ target_compile_definitions(libslic3r_gui PRIVATE $<$<BOOL:${SLIC3R_ALPHA}>:SLIC3
 
 encoding_check(libslic3r_gui)
 
-target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi libcurl ${wxWidgets_LIBRARIES})
+target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui GLEW::GLEW OpenGL::GL OpenGL::GLU hidapi exif libcurl ${wxWidgets_LIBRARIES})
 
 if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
     target_link_libraries(libslic3r_gui ${DBUS_LIBRARIES}) 

Some files were not shown because too many files changed in this diff