[libpng16] Implement eXIf chunk support
diff --git a/ANNOUNCE b/ANNOUNCE
index 4896a96..0448062 100644
--- a/ANNOUNCE
+++ b/ANNOUNCE
@@ -59,6 +59,7 @@
     overflow' check that is on by default with -Wall -Wextra.
 
 Version 1.6.31beta05 [July 11, 2017]
+  Added eXIf chunk support.
 
 Send comments/corrections/commendations to png-mng-implement at lists.sf.net
 (subscription required; visit
diff --git a/CHANGES b/CHANGES
index 3e07b17..18e90d6 100644
--- a/CHANGES
+++ b/CHANGES
@@ -5889,6 +5889,7 @@
     overflow' check that is on by default with -Wall -Wextra.
 
 Version 1.6.31beta05 [July 11, 2017]
+  Added eXIf chunk support.
 
 Send comments/corrections/commendations to png-mng-implement at lists.sf.net
 (subscription required; visit
diff --git a/libpng-manual.txt b/libpng-manual.txt
index 2903f92..a6251f9 100644
--- a/libpng-manual.txt
+++ b/libpng-manual.txt
@@ -1453,6 +1453,11 @@
                      the single transparent color for
                      non-paletted images (PNG_INFO_tRNS)
 
+    png_get_eXIf(png_ptr, info_ptr, &exif);
+                     (PNG_INFO_eXIf)
+
+    exif           - Exif profile (array of png_byte)
+
     png_get_hIST(png_ptr, info_ptr, &hist);
                      (PNG_INFO_hIST)
 
@@ -2498,6 +2503,7 @@
              PNG_INFO_gAMA, PNG_INFO_sBIT,
              PNG_INFO_cHRM, PNG_INFO_PLTE,
              PNG_INFO_tRNS, PNG_INFO_bKGD,
+             PNG_INFO_eXIf,
              PNG_INFO_hIST, PNG_INFO_pHYs,
              PNG_INFO_oFFs, PNG_INFO_tIME,
              PNG_INFO_pCAL, PNG_INFO_sRGB,
@@ -3097,6 +3103,11 @@
                      single transparent color for
                      non-paletted images (PNG_INFO_tRNS)
 
+    png_set_eXIf(png_ptr, info_ptr, exif);
+
+    hist           - Exif profile (array of
+                     png_byte) (PNG_INFO_eXIf)
+
     png_set_hIST(png_ptr, info_ptr, hist);
 
     hist           - histogram of palette (array of
@@ -5205,6 +5216,11 @@
 enforced, and the palette was always limited to 256 entries. An over-length
 PLTE chunk found in an input PNG is silently truncated.
 
+Starting with libpng-1.6.31, the eXIf chunk is supported. Libpng does not
+attempt to decode the Exif profile; it simply returns a byte array
+containing the profile to the calling application which must do its own
+decoding.
+
 XIII.  Detecting libpng
 
 The png_get_io_ptr() function has been present since libpng-0.88, has never
diff --git a/libpng.3 b/libpng.3
index 9dec52b..62298f8 100644
--- a/libpng.3
+++ b/libpng.3
@@ -97,6 +97,8 @@
 
 \fBpng_byte png_get_header_version (png_const_structp \fIpng_ptr\fP\fB);\fP
 
+\fBpng_uint_32 png_get_eXIf (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, png_bytep \fI*exif\fP\fB);\fP
+
 \fBpng_uint_32 png_get_hIST (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, png_uint_16p \fI*hist\fP\fB);\fP
 
 \fBpng_uint_32 png_get_iCCP (png_const_structp \fP\fIpng_ptr\fP\fB, png_const_infop \fP\fIinfo_ptr\fP\fB, png_charpp \fP\fIname\fP\fB, int \fP\fI*compression_type\fP\fB, png_bytepp \fP\fIprofile\fP\fB, png_uint_32 \fI*proflen\fP\fB);\fP
@@ -347,6 +349,8 @@
 
 \fBvoid png_set_gray_to_rgb (png_structp \fIpng_ptr\fP\fB);\fP
 
+\fBvoid png_set_eXIf (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_bytep \fIexif\fP\fB);\fP
+
 \fBvoid png_set_hIST (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_16p \fIhist\fP\fB);\fP
 
 \fBvoid png_set_iCCP (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_const_charp \fP\fIname\fP\fB, int \fP\fIcompression_type\fP\fB, png_const_bytep \fP\fIprofile\fP\fB, png_uint_32 \fIproflen\fP\fB);\fP
@@ -1963,6 +1967,11 @@
                      the single transparent color for
                      non-paletted images (PNG_INFO_tRNS)
 
+    png_get_eXIf(png_ptr, info_ptr, &exif);
+                     (PNG_INFO_eXIf)
+
+    exif           - Exif profile (array of png_byte)
+
     png_get_hIST(png_ptr, info_ptr, &hist);
                      (PNG_INFO_hIST)
 
@@ -3008,6 +3017,7 @@
              PNG_INFO_gAMA, PNG_INFO_sBIT,
              PNG_INFO_cHRM, PNG_INFO_PLTE,
              PNG_INFO_tRNS, PNG_INFO_bKGD,
+             PNG_INFO_eXIf,
              PNG_INFO_hIST, PNG_INFO_pHYs,
              PNG_INFO_oFFs, PNG_INFO_tIME,
              PNG_INFO_pCAL, PNG_INFO_sRGB,
@@ -3607,6 +3617,11 @@
                      single transparent color for
                      non-paletted images (PNG_INFO_tRNS)
 
+    png_set_eXIf(png_ptr, info_ptr, exif);
+
+    hist           - Exif profile (array of
+                     png_byte) (PNG_INFO_eXIf)
+
     png_set_hIST(png_ptr, info_ptr, hist);
 
     hist           - histogram of palette (array of
@@ -5715,6 +5730,11 @@
 enforced, and the palette was always limited to 256 entries. An over-length
 PLTE chunk found in an input PNG is silently truncated.
 
+Starting with libpng-1.6.31, the eXIf chunk is supported. Libpng does not
+attempt to decode the Exif profile; it simply returns a byte array
+containing the profile to the calling application which must do its own
+decoding.
+
 .SH XIII.  Detecting libpng
 
 The png_get_io_ptr() function has been present since libpng-0.88, has never
diff --git a/png.c b/png.c
index 3a7d6f0..2081d73 100644
--- a/png.c
+++ b/png.c
@@ -615,6 +615,16 @@
    }
 #endif
 
+#ifdef PNG_eXIf_SUPPORTED
+   /* Free any eXIf entry */
+   if (((mask & PNG_FREE_EXIF) & info_ptr->free_me) != 0)
+   {
+      png_free(png_ptr, info_ptr->exif);
+      info_ptr->exif = NULL;
+      info_ptr->valid &= ~PNG_INFO_eXIf;
+   }
+#endif
+
 #ifdef PNG_hIST_SUPPORTED
    /* Free any hIST entry */
    if (((mask & PNG_FREE_HIST) & info_ptr->free_me) != 0)
diff --git a/png.h b/png.h
index 62d6ac6..92f9bd1 100644
--- a/png.h
+++ b/png.h
@@ -776,6 +776,7 @@
 #define PNG_INFO_sPLT 0x2000U  /* ESR, 1.0.6 */
 #define PNG_INFO_sCAL 0x4000U  /* ESR, 1.0.6 */
 #define PNG_INFO_IDAT 0x8000U  /* ESR, 1.0.6 */
+#define PNG_INFO_eXIf 0x10000U /* GR-P, 1.6.31 */
 
 /* This is used for the transformation routines, as some of them
  * change these values for the row.  It also should enable using
@@ -1788,7 +1789,8 @@
 #define PNG_FREE_PLTE 0x1000U
 #define PNG_FREE_TRNS 0x2000U
 #define PNG_FREE_TEXT 0x4000U
-#define PNG_FREE_ALL  0x7fffU
+#define PNG_FREE_EXIF 0x8000U /* Added at libpng-1.6.31 */
+#define PNG_FREE_ALL  0xffffU
 #define PNG_FREE_MUL  0x4220U /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */
 
 #ifdef PNG_USER_MEM_SUPPORTED
@@ -2007,6 +2009,13 @@
     png_fixed_point int_blue_Z))
 #endif
 
+#ifdef PNG_eXIf_SUPPORTED
+PNG_EXPORT(246, png_uint_32, png_get_eXIf, (png_const_structrp png_ptr,
+    png_inforp info_ptr, png_bytep *exif));
+PNG_EXPORT(247, void, png_set_eXIf, (png_const_structrp png_ptr,
+    png_inforp info_ptr, const png_bytep exif));
+#endif
+
 #ifdef PNG_gAMA_SUPPORTED
 PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA, (png_const_structrp png_ptr,
     png_const_inforp info_ptr, double *file_gamma))
@@ -2025,9 +2034,6 @@
 #ifdef PNG_hIST_SUPPORTED
 PNG_EXPORT(141, png_uint_32, png_get_hIST, (png_const_structrp png_ptr,
     png_inforp info_ptr, png_uint_16p *hist));
-#endif
-
-#ifdef PNG_hIST_SUPPORTED
 PNG_EXPORT(142, void, png_set_hIST, (png_const_structrp png_ptr,
     png_inforp info_ptr, png_const_uint_16p hist));
 #endif
@@ -3253,7 +3259,7 @@
  * one to use is one more than this.)
  */
 #ifdef PNG_EXPORT_LAST_ORDINAL
-  PNG_EXPORT_LAST_ORDINAL(245);
+  PNG_EXPORT_LAST_ORDINAL(247);
 #endif
 
 #ifdef __cplusplus
diff --git a/pngget.c b/pngget.c
index 141c393..ace9e63 100644
--- a/pngget.c
+++ b/pngget.c
@@ -773,6 +773,24 @@
 }
 #endif
 
+#ifdef PNG_eXIf_SUPPORTED
+png_uint_32 PNGAPI
+png_get_eXIf(png_const_structrp png_ptr, png_inforp info_ptr,
+    png_bytep *exif)
+{
+   png_debug1(1, "in %s retrieval function", "eXIf");
+
+   if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_eXIf) != 0 && exif != NULL)
+   {
+      *exif = info_ptr->exif;
+      return (PNG_INFO_eXIf);
+   }
+
+   return (0);
+}
+#endif
+
 #ifdef PNG_hIST_SUPPORTED
 png_uint_32 PNGAPI
 png_get_hIST(png_const_structrp png_ptr, png_inforp info_ptr,
diff --git a/pnginfo.h b/pnginfo.h
index 361ed8b..6e6d46a 100644
--- a/pnginfo.h
+++ b/pnginfo.h
@@ -185,6 +185,11 @@
    png_byte phys_unit_type; /* resolution type (see PNG_RESOLUTION_ below) */
 #endif
 
+#ifdef PNG_eXIf_SUPPORTED
+   int num_exif;
+   png_bytep exif;
+#endif
+
 #ifdef PNG_hIST_SUPPORTED
    /* The hIST chunk contains the relative frequency or importance of the
     * various palette entries, so that a viewer can intelligently select a
diff --git a/pngpriv.h b/pngpriv.h
index 663608b..7c273bd 100644
--- a/pngpriv.h
+++ b/pngpriv.h
@@ -842,6 +842,7 @@
 #define png_PLTE PNG_U32( 80,  76,  84,  69)
 #define png_bKGD PNG_U32( 98,  75,  71,  68)
 #define png_cHRM PNG_U32( 99,  72,  82,  77)
+#define png_eXIf PNG_U32(101,  88,  73, 102) /* registered July 2017 */
 #define png_fRAc PNG_U32(102,  82,  65,  99) /* registered, not defined */
 #define png_gAMA PNG_U32(103,  65,  77,  65)
 #define png_gIFg PNG_U32(103,  73,  70, 103)
@@ -1441,6 +1442,11 @@
     png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
 #endif
 
+#ifdef PNG_READ_eXIf_SUPPORTED
+PNG_INTERNAL_FUNCTION(void,png_handle_eXIf,(png_structrp png_ptr,
+    png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
+#endif
+
 #ifdef PNG_READ_gAMA_SUPPORTED
 PNG_INTERNAL_FUNCTION(void,png_handle_gAMA,(png_structrp png_ptr,
     png_inforp info_ptr, png_uint_32 length),PNG_EMPTY);
diff --git a/pngrutil.c b/pngrutil.c
index bf3ef62..9cde648 100644
--- a/pngrutil.c
+++ b/pngrutil.c
@@ -2009,6 +2009,44 @@
 }
 #endif
 
+#ifdef PNG_READ_eXIf_SUPPORTED
+void /* PRIVATE */
+png_handle_eXIf(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
+{
+   unsigned int i;
+   png_bytep eXIf_buf;
+
+   png_debug(1, "in png_handle_eXIf");
+
+   if ((png_ptr->mode & PNG_HAVE_IHDR) == 0)
+      png_chunk_error(png_ptr, "missing IHDR");
+
+   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_eXIf) != 0)
+   {
+      png_crc_finish(png_ptr, length);
+      png_chunk_benign_error(png_ptr, "duplicate");
+      return;
+   }
+
+   eXIf_buf = png_voidcast(png_bytep,
+             png_malloc_warn(png_ptr, length));
+
+   for (i = 0; i < length; i++)
+   {
+      png_byte buf[1];
+      png_crc_read(png_ptr, buf, 1);
+      eXIf_buf[i] = buf[0];
+   }
+
+   if (png_crc_finish(png_ptr, 0) != 0)
+      return;
+
+   info_ptr->num_exif = length;
+
+   png_set_eXIf(png_ptr, info_ptr, eXIf_buf);
+}
+#endif
+
 #ifdef PNG_READ_hIST_SUPPORTED
 void /* PRIVATE */
 png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length)
diff --git a/pngset.c b/pngset.c
index 2bb575d..9984735 100644
--- a/pngset.c
+++ b/pngset.c
@@ -134,6 +134,39 @@
 
 #endif /* cHRM */
 
+#ifdef PNG_eXIf_SUPPORTED
+void PNGAPI
+png_set_eXIf(png_const_structrp png_ptr, png_inforp info_ptr,
+    const png_bytep eXIf_buf)
+{
+   int i;
+
+   png_debug1(1, "in %s storage function", "eXIf");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   png_free_data(png_ptr, info_ptr, PNG_FREE_EXIF, 0);
+
+   info_ptr->exif = png_voidcast(png_bytep, png_malloc_warn(png_ptr,
+       info_ptr->num_exif));
+
+   if (info_ptr->exif == NULL)
+   {
+      png_warning(png_ptr, "Insufficient memory for eXIf chunk data");
+
+      return;
+   }
+
+   info_ptr->free_me |= PNG_FREE_EXIF;
+
+   for (i = 0; i < info_ptr->num_exif; i++)
+      info_ptr->exif[i] = eXIf_buf[i];
+
+   info_ptr->valid |= PNG_INFO_eXIf;
+}
+#endif /* eXIf */
+
 #ifdef PNG_gAMA_SUPPORTED
 void PNGFAPI
 png_set_gAMA_fixed(png_const_structrp png_ptr, png_inforp info_ptr,
diff --git a/pngstruct.h b/pngstruct.h
index 749d7e3..1e1c5f8 100644
--- a/pngstruct.h
+++ b/pngstruct.h
@@ -479,5 +479,8 @@
    png_colorspace   colorspace;
 #endif
 #endif
+
+/* New member added in libpng-1.6.30 */
+   int num_exif;
 };
 #endif /* PNGSTRUCT_H */
diff --git a/pngtest.c b/pngtest.c
index 01e4143..77e25e1 100644
--- a/pngtest.c
+++ b/pngtest.c
@@ -1192,6 +1192,14 @@
       }
    }
 #endif
+#ifdef PNG_eXIf_SUPPORTED
+   {
+      png_bytep exif;
+
+      if (png_get_eXIf(read_ptr, read_info_ptr, &exif) != 0)
+         png_set_eXIf(write_ptr, write_info_ptr, exif);
+   }
+#endif
 #ifdef PNG_hIST_SUPPORTED
    {
       png_uint_16p hist;
@@ -1530,6 +1538,14 @@
       }
    }
 #endif
+#ifdef PNG_eXIf_SUPPORTED
+   {
+      png_bytep exif;
+
+      if (png_get_eXIf(read_ptr, end_info_ptr, &exif) != 0)
+         png_set_eXIf(write_ptr, write_end_info_ptr, exif);
+   }
+#endif
 #ifdef PNG_tIME_SUPPORTED
    {
       png_timep mod_time;
diff --git a/pngwutil.c b/pngwutil.c
index dd80058..348bb52 100644
--- a/pngwutil.c
+++ b/pngwutil.c
@@ -1473,6 +1473,37 @@
 }
 #endif
 
+#ifdef PNG_WRITE_eXIf_SUPPORTED
+/* Write the Exif data */
+void /* PRIVATE */
+png_write_eXIf(png_structrp png_ptr, png_bytep exif, int num_exif)
+{
+   int i;
+   png_byte buf[3];
+
+   png_debug(1, "in png_write_eXIf");
+
+   if (num_exif > (int)png_ptr->num_exif)
+   {
+      png_debug2(3, "num_exif = %d, png_ptr->num_exif = %d", num_exif,
+          png_ptr->num_exif);
+
+      png_warning(png_ptr, "Invalid number of exif bytes specified");
+      return;
+   }
+
+   png_write_chunk_header(png_ptr, png_eXIf, (png_uint_32)(num_exif));
+
+   for (i = 0; i < num_exif; i++)
+   {
+      buf[i] = exif[i];
+      png_write_chunk_data(png_ptr, buf, (png_size_t)1);
+   }
+
+   png_write_chunk_end(png_ptr);
+}
+#endif
+
 #ifdef PNG_WRITE_hIST_SUPPORTED
 /* Write the histogram */
 void /* PRIVATE */
diff --git a/scripts/pnglibconf.dfa b/scripts/pnglibconf.dfa
index 4d4e13b..cd7aaaf 100644
--- a/scripts/pnglibconf.dfa
+++ b/scripts/pnglibconf.dfa
@@ -741,6 +741,8 @@
 # Ancillary chunks
 chunk bKGD
 chunk cHRM enables COLORSPACE
+# enable eXIf only after chunk is approved
+chunk eXIf
 chunk gAMA enables GAMMA
 chunk hIST
 chunk iCCP enables COLORSPACE, GAMMA
diff --git a/scripts/pnglibconf.h.prebuilt b/scripts/pnglibconf.h.prebuilt
index a901395..eb7048f 100644
--- a/scripts/pnglibconf.h.prebuilt
+++ b/scripts/pnglibconf.h.prebuilt
@@ -84,6 +84,7 @@
 #define PNG_READ_USER_TRANSFORM_SUPPORTED
 #define PNG_READ_bKGD_SUPPORTED
 #define PNG_READ_cHRM_SUPPORTED
+#define PNG_READ_eXIf_SUPPORTED
 #define PNG_READ_gAMA_SUPPORTED
 #define PNG_READ_hIST_SUPPORTED
 #define PNG_READ_iCCP_SUPPORTED
@@ -153,6 +154,7 @@
 #define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
 #define PNG_WRITE_bKGD_SUPPORTED
 #define PNG_WRITE_cHRM_SUPPORTED
+#define PNG_WRITE_eXIf_SUPPORTED
 #define PNG_WRITE_gAMA_SUPPORTED
 #define PNG_WRITE_hIST_SUPPORTED
 #define PNG_WRITE_iCCP_SUPPORTED
@@ -170,6 +172,7 @@
 #define PNG_WRITE_zTXt_SUPPORTED
 #define PNG_bKGD_SUPPORTED
 #define PNG_cHRM_SUPPORTED
+#define PNG_eXIf_SUPPORTED
 #define PNG_gAMA_SUPPORTED
 #define PNG_hIST_SUPPORTED
 #define PNG_iCCP_SUPPORTED
diff --git a/scripts/symbols.def b/scripts/symbols.def
index 89782c9..0636509 100644
--- a/scripts/symbols.def
+++ b/scripts/symbols.def
@@ -250,3 +250,5 @@
  png_get_palette_max @243
  png_set_option @244
  png_image_write_to_memory @245
+ png_get_eXIf @246
+ png_set_eXIf @247