mirror of
https://git.code.sf.net/p/libpng/code.git
synced 2025-07-10 18:04:09 +02:00
tIME and text position handling
The handling of tIME and text chunks on read now records the location of the chunks relative to PLTE and IDAT. Behavior on write is unchanged except that if the position was recorded on read it will be re-used. This involves an ABI change to the png_text_struct; a one byte location field is added (with the same meaning as the one used to record unknown chunk location.) Because this field is only used on read there is no API change unless a png_info from a libpng read is passed to a subsequent libpng write (this did not work very well before 1.7; the tIME chunk could get duplicated.) png_set_text ignores the new field, resetting it to the current position in the read or write stream. On write the position is set to the next location to be written unless the write has not started (the position is before the signature) in which case the location is set to PNG_HAVE_PLTE|PNG_AFTER_IDAT. When the chunk is written the position is set to the actual write location (effectively the position is frozen.) Signed-off-by: John Bowler <jbowler@acm.org>
This commit is contained in:
parent
c90572ee77
commit
4253c4d759
19
png.h
19
png.h
@ -692,6 +692,16 @@ typedef png_sPLT_t * * png_sPLT_tpp;
|
||||
* PNG_TEXT_COMPRESSION_zTXt. Note that the "compression value" is not the
|
||||
* same as what appears in the PNG tEXt/zTXt/iTXt chunk's "compression flag"
|
||||
* which is always 0 or 1, or its "compression method" which is always 0.
|
||||
*
|
||||
* The location field (added in libpng 1.7.0) records where the text chunk was
|
||||
* found when png_get_text is used. When png_set_text is used the field in the
|
||||
* structure passed in is ignored and, instead, the field is set to the current
|
||||
* write position.
|
||||
*
|
||||
* Prior to 1.7.0 the write behavior was the same; the text fields were written
|
||||
* (once) at the next write_info call, however the read mechanism did not record
|
||||
* the chunk location so if an info_struct from read was passed to the write
|
||||
* APIs the text chunks would all be written at the start (before PLTE).
|
||||
*/
|
||||
typedef struct png_text_struct
|
||||
{
|
||||
@ -700,6 +710,7 @@ typedef struct png_text_struct
|
||||
0: zTXt, deflate
|
||||
1: iTXt, none
|
||||
2: iTXt, deflate */
|
||||
png_byte location; /* before/after PLTE or after IDAT */
|
||||
png_charp key; /* keyword, 1-79 character description of "text" */
|
||||
png_charp text; /* comment, may be an empty string (ie "")
|
||||
or a NULL pointer */
|
||||
@ -717,8 +728,14 @@ typedef png_text * * png_textpp;
|
||||
|
||||
/* Supported compression types for text in PNG files (tEXt, and zTXt).
|
||||
* The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */
|
||||
#ifdef PNG_OLD_COMPRESSION_CODES_SUPPORTED
|
||||
/* These values were used to prevent double write of text chunks in versions
|
||||
* prior to 1.7.0. They are never set now; if you need them #define the
|
||||
* _SUPPORTED macro.
|
||||
*/
|
||||
#define PNG_TEXT_COMPRESSION_NONE_WR -3
|
||||
#define PNG_TEXT_COMPRESSION_zTXt_WR -2
|
||||
#endif /* OLD_COMPRESSION_CODES */
|
||||
#define PNG_TEXT_COMPRESSION_NONE -1
|
||||
#define PNG_TEXT_COMPRESSION_zTXt 0
|
||||
#define PNG_ITXT_COMPRESSION_NONE 1
|
||||
@ -776,7 +793,7 @@ typedef const png_unknown_chunk * png_const_unknown_chunkp;
|
||||
typedef png_unknown_chunk * * png_unknown_chunkpp;
|
||||
#endif
|
||||
|
||||
/* Flag values for the unknown chunk location byte. */
|
||||
/* Flag values for the chunk location byte. */
|
||||
#define PNG_HAVE_IHDR 0x01U
|
||||
#define PNG_HAVE_PLTE 0x02U
|
||||
#define PNG_AFTER_IDAT 0x08U
|
||||
|
@ -118,6 +118,7 @@ struct png_info_def
|
||||
* modified. See the png_time struct for the contents of this struct.
|
||||
*/
|
||||
png_time mod_time;
|
||||
png_byte time_location;
|
||||
#endif
|
||||
|
||||
#ifdef PNG_sBIT_SUPPORTED
|
||||
|
@ -1150,12 +1150,12 @@ PNG_INTERNAL_FUNCTION(void,png_write_hIST,(png_structrp png_ptr,
|
||||
/* Chunks that have keywords */
|
||||
#ifdef PNG_WRITE_tEXt_SUPPORTED
|
||||
PNG_INTERNAL_FUNCTION(void,png_write_tEXt,(png_structrp png_ptr,
|
||||
png_const_charp key, png_const_charp text, png_size_t text_len),PNG_EMPTY);
|
||||
png_const_charp key, png_const_charp text, png_size_t text_len),PNG_EMPTY);
|
||||
#endif
|
||||
|
||||
#ifdef PNG_WRITE_zTXt_SUPPORTED
|
||||
PNG_INTERNAL_FUNCTION(void,png_write_zTXt,(png_structrp png_ptr, png_const_charp
|
||||
key, png_const_charp text, int compression),PNG_EMPTY);
|
||||
PNG_INTERNAL_FUNCTION(void,png_write_zTXt,(png_structrp png_ptr,
|
||||
png_const_charp key, png_const_charp text, int compression),PNG_EMPTY);
|
||||
#endif
|
||||
|
||||
#ifdef PNG_WRITE_iTXt_SUPPORTED
|
||||
|
80
pngset.c
80
pngset.c
@ -673,6 +673,77 @@ png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr,
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(PNG_TEXT_SUPPORTED) || defined(PNG_tIME_SUPPORTED)
|
||||
static png_byte
|
||||
get_location(png_const_structrp png_ptr)
|
||||
/* Return the correct location flag for a chunk. For a png_set_<chunk>
|
||||
* called during read this is the current read location, for a
|
||||
* png_set_<chunk> called during write it is the following write location
|
||||
* (because the currents at the current location have already been written.)
|
||||
* For a png_set_<chunk> called before read starts (none of the 'position'
|
||||
* mode bits are set) the position is set to the start (PNG_HAVE_IHDR). For
|
||||
* a png_set_chunk> called before write starts PNG_HAVE_PLTE|PNG_AFTER_IDAT
|
||||
* are set because we don't know whether this is the main png_info or the one
|
||||
* for use after the IDAT from png_write_end.
|
||||
*
|
||||
* The latter behavior gives compatibility with the old behavior of
|
||||
* png_set_text; it only wrote text chunks after the PLTE or IDAT and it just
|
||||
* wrote them once.
|
||||
*/
|
||||
{
|
||||
if ((png_ptr->mode & PNG_AFTER_IDAT) != 0U)
|
||||
return PNG_AFTER_IDAT;
|
||||
|
||||
else if ((png_ptr->mode & PNG_HAVE_PLTE) != 0U)
|
||||
{
|
||||
/* In a write operation PNG_HAVE_PLTE is set when the chunks before the
|
||||
* IDAT are written (in png_write_info), so the location needs to be after
|
||||
* IDAT. In a read the chunk was read after the PLTE but before the IDAT.
|
||||
*/
|
||||
if (png_ptr->read_struct)
|
||||
return PNG_HAVE_PLTE;
|
||||
|
||||
else /* write struct */
|
||||
return PNG_AFTER_IDAT;
|
||||
}
|
||||
|
||||
else if ((png_ptr->mode & PNG_HAVE_IHDR) != 0U)
|
||||
{
|
||||
/* For read this means the chunk is between the IHDR and any PLTE; there
|
||||
* may be none but then there is no order to preserve.
|
||||
*
|
||||
* For write png_write_IHDR has been called and that means that the info
|
||||
* before PLTE has been written, so the chunk goes after.
|
||||
*/
|
||||
if (png_ptr->read_struct)
|
||||
return PNG_HAVE_IHDR;
|
||||
|
||||
else /* write struct */
|
||||
return PNG_HAVE_PLTE;
|
||||
}
|
||||
|
||||
else if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) != 0U)
|
||||
{
|
||||
/* This should not happen on read, on write it means that the app has
|
||||
* started writing so assume the text is meant to go before PLTE.
|
||||
*/
|
||||
return PNG_HAVE_IHDR;
|
||||
}
|
||||
|
||||
/* Either a png_set_<chunk> from the application during read before reading
|
||||
* starts (so mode is 0) or the same for write.
|
||||
*/
|
||||
else
|
||||
{
|
||||
if (png_ptr->read_struct)
|
||||
return PNG_HAVE_IHDR;
|
||||
|
||||
else
|
||||
return PNG_HAVE_PLTE|PNG_AFTER_IDAT;
|
||||
}
|
||||
}
|
||||
#endif /* TEXT || tIME */
|
||||
|
||||
#ifdef PNG_TEXT_SUPPORTED
|
||||
void PNGAPI
|
||||
png_set_text(png_structrp png_ptr, png_inforp info_ptr,
|
||||
@ -802,6 +873,12 @@ png_set_text_2(png_structrp png_ptr, png_inforp info_ptr,
|
||||
}
|
||||
# endif
|
||||
|
||||
/* Record the location for posterity. On the read side this just says
|
||||
* where the chunk was (approximately). On the write side it says how far
|
||||
* through the write process libpng was before this API was called.
|
||||
*/
|
||||
textp->location = get_location(png_ptr);
|
||||
|
||||
if (text_ptr[i].text == NULL || text_ptr[i].text[0] == '\0')
|
||||
{
|
||||
text_length = 0;
|
||||
@ -919,9 +996,10 @@ png_set_tIME(png_const_structrp png_ptr, png_inforp info_ptr,
|
||||
}
|
||||
|
||||
info_ptr->mod_time = *mod_time;
|
||||
info_ptr->time_location = get_location(png_ptr);
|
||||
info_ptr->valid |= PNG_INFO_tIME;
|
||||
}
|
||||
#endif
|
||||
#endif /* tIME */
|
||||
|
||||
#ifdef PNG_tRNS_SUPPORTED
|
||||
void PNGAPI
|
||||
|
@ -400,9 +400,6 @@ struct png_struct_def
|
||||
unsigned int palette_index_check_disabled :1; /* defaults to 0, 'enabled' */
|
||||
unsigned int palette_index_check_issued :1; /* error message output */
|
||||
#endif /* CHECK_FOR_INVALID_INDEX */
|
||||
#ifdef PNG_WRITE_tIME_SUPPORTED
|
||||
unsigned int wrote_tIME :1; /* Stop writing of duplicate tIME chunks */
|
||||
#endif /* WRITE_tIME */
|
||||
#ifdef PNG_READ_tRNS_SUPPORTED
|
||||
png_color_16 trans_color; /* transparent color for non-paletted files */
|
||||
#endif /* READ_tRNS */
|
||||
|
160
pngwrite.c
160
pngwrite.c
@ -72,6 +72,75 @@ write_unknown_chunks(png_structrp png_ptr, png_const_inforp info_ptr,
|
||||
}
|
||||
#endif /* WRITE_UNKNOWN_CHUNKS */
|
||||
|
||||
#ifdef PNG_WRITE_TEXT_SUPPORTED
|
||||
static void
|
||||
png_write_text(png_structrp png_ptr, png_const_inforp info_ptr, png_byte where)
|
||||
/* Text chunk helper */
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Check to see if we need to write text chunks */
|
||||
for (i = 0; i < info_ptr->num_text; i++)
|
||||
{
|
||||
png_debug2(2, "Writing text chunk %d, type %d", i,
|
||||
info_ptr->text[i].compression);
|
||||
|
||||
/* Text chunks are written at info_ptr->text[i].location, skip the chunk
|
||||
* if we are not writing at that location:
|
||||
*/
|
||||
if ((info_ptr->text[i].location & where) == 0U)
|
||||
continue;
|
||||
|
||||
switch (info_ptr->text[i].compression)
|
||||
{
|
||||
case PNG_ITXT_COMPRESSION_NONE:
|
||||
case PNG_ITXT_COMPRESSION_zTXt:
|
||||
# ifdef PNG_WRITE_iTXt_SUPPORTED
|
||||
/* Write international chunk */
|
||||
png_write_iTXt(png_ptr, info_ptr->text[i].compression,
|
||||
info_ptr->text[i].key, info_ptr->text[i].lang,
|
||||
info_ptr->text[i].lang_key, info_ptr->text[i].text);
|
||||
# else /* !WRITE_iTXT */
|
||||
png_app_error(png_ptr, "Unable to write international text");
|
||||
# endif /* !WRITE_iTXT */
|
||||
break;
|
||||
|
||||
case PNG_TEXT_COMPRESSION_zTXt:
|
||||
# ifdef PNG_WRITE_zTXt_SUPPORTED
|
||||
/* Write compressed chunk */
|
||||
png_write_zTXt(png_ptr, info_ptr->text[i].key,
|
||||
info_ptr->text[i].text, info_ptr->text[i].compression);
|
||||
# else /* !WRITE_zTXT */
|
||||
png_app_error(png_ptr, "Unable to write compressed text");
|
||||
# endif /* !WRITE_zTXT */
|
||||
break;
|
||||
|
||||
case PNG_TEXT_COMPRESSION_NONE:
|
||||
# ifdef PNG_WRITE_tEXt_SUPPORTED
|
||||
/* Write uncompressed chunk */
|
||||
png_write_tEXt(png_ptr, info_ptr->text[i].key,
|
||||
info_ptr->text[i].text, 0);
|
||||
# else /* !WRITE_tEXt */
|
||||
/* Can't get here TODO: why not? */
|
||||
png_app_error(png_ptr, "Unable to write uncompressed text");
|
||||
# endif /* !WRITE_tEXt */
|
||||
break;
|
||||
|
||||
default:
|
||||
/* This is an internal error because the libpng checking should
|
||||
* never manage to set any 'compression' except the above values.
|
||||
*/
|
||||
impossible("invalid text compression");
|
||||
}
|
||||
|
||||
/* The chunk was written, record where. This allows the location to have
|
||||
* multiple bits set, the first successful write freezes the location.
|
||||
*/
|
||||
info_ptr->text[i].location = where;
|
||||
}
|
||||
}
|
||||
#endif /* WRITE_TEXT */
|
||||
|
||||
/* Writes all the PNG information. This is the suggested way to use the
|
||||
* library. If you have a new chunk to add, make a function to write it,
|
||||
* and put it in the correct location here. If you want the chunk written
|
||||
@ -138,6 +207,12 @@ png_write_info_before_PLTE(png_structrp png_ptr, png_const_inforp info_ptr)
|
||||
* the application continues writing the PNG. So check the 'invalid'
|
||||
* flag here too.
|
||||
*/
|
||||
# ifdef PNG_WRITE_tIME_SUPPORTED
|
||||
if ((info_ptr->valid & PNG_INFO_tIME) != 0 &&
|
||||
(info_ptr->time_location & PNG_HAVE_IHDR) != 0)
|
||||
png_write_tIME(png_ptr, &(info_ptr->mod_time));
|
||||
# endif /* WRITE_tIME */
|
||||
|
||||
# ifdef PNG_WRITE_gAMA_SUPPORTED /* enables GAMMA */
|
||||
if ((info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) == 0 &&
|
||||
(info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_gAMA) != 0 &&
|
||||
@ -194,6 +269,11 @@ png_write_info_before_PLTE(png_structrp png_ptr, png_const_inforp info_ptr)
|
||||
png_write_cHRM_fixed(png_ptr, &info_ptr->colorspace.end_points_xy);
|
||||
# endif /* WRITE_cHRM */
|
||||
|
||||
# ifdef PNG_WRITE_TEXT_SUPPORTED
|
||||
if (info_ptr->num_text > 0)
|
||||
png_write_text(png_ptr, info_ptr, PNG_HAVE_IHDR);
|
||||
# endif /* WRITE_TEXT */
|
||||
|
||||
# ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
|
||||
/* The third arugment must encode only one bit, otherwise chunks will
|
||||
* be written twice because the test in write_unknown_chunks is
|
||||
@ -208,71 +288,6 @@ png_write_info_before_PLTE(png_structrp png_ptr, png_const_inforp info_ptr)
|
||||
"png_write_info_before_PLTE called more than once");
|
||||
}
|
||||
|
||||
#ifdef PNG_WRITE_TEXT_SUPPORTED
|
||||
static void
|
||||
png_write_text(png_structrp png_ptr, png_const_inforp info_ptr)
|
||||
/* Text chunk helper */
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Check to see if we need to write text chunks */
|
||||
for (i = 0; i < info_ptr->num_text; i++)
|
||||
{
|
||||
png_debug2(2, "Writing text chunk %d, type %d", i,
|
||||
info_ptr->text[i].compression);
|
||||
|
||||
/* An internationalized chunk? */
|
||||
if (info_ptr->text[i].compression > 0)
|
||||
{
|
||||
# ifdef PNG_WRITE_iTXt_SUPPORTED
|
||||
/* Write international chunk */
|
||||
png_write_iTXt(png_ptr, info_ptr->text[i].compression,
|
||||
info_ptr->text[i].key, info_ptr->text[i].lang,
|
||||
info_ptr->text[i].lang_key, info_ptr->text[i].text);
|
||||
# else /* !WRITE_iTXT */
|
||||
png_app_error(png_ptr, "Unable to write international text");
|
||||
# endif /* !WRITE_iTXT */
|
||||
|
||||
/* Mark this chunk as written */
|
||||
if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
|
||||
info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
|
||||
else
|
||||
info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
|
||||
}
|
||||
|
||||
/* If we want a compressed text chunk */
|
||||
else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt)
|
||||
{
|
||||
# ifdef PNG_WRITE_zTXt_SUPPORTED
|
||||
/* Write compressed chunk */
|
||||
png_write_zTXt(png_ptr, info_ptr->text[i].key,
|
||||
info_ptr->text[i].text, info_ptr->text[i].compression);
|
||||
# else /* !WRITE_zTXT */
|
||||
png_app_error(png_ptr, "Unable to write compressed text");
|
||||
# endif /* !WRITE_zTXT */
|
||||
|
||||
/* Mark this chunk as written */
|
||||
info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
|
||||
}
|
||||
|
||||
else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
|
||||
{
|
||||
# ifdef PNG_WRITE_tEXt_SUPPORTED
|
||||
/* Write uncompressed chunk */
|
||||
png_write_tEXt(png_ptr, info_ptr->text[i].key,
|
||||
info_ptr->text[i].text, 0);
|
||||
# else /* !WRITE_tEXt */
|
||||
/* Can't get here TODO: why not? */
|
||||
png_app_error(png_ptr, "Unable to write uncompressed text");
|
||||
# endif /* !WRITE_tEXt */
|
||||
|
||||
/* Mark this chunk as written */
|
||||
info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* WRITE_TEXT */
|
||||
|
||||
void PNGAPI
|
||||
png_write_info(png_structrp png_ptr, png_const_inforp info_ptr)
|
||||
{
|
||||
@ -303,6 +318,11 @@ png_write_info(png_structrp png_ptr, png_const_inforp info_ptr)
|
||||
png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
|
||||
png_error(png_ptr, "Valid palette required for paletted images");
|
||||
|
||||
/* But always set the mode flag because without this we don't know when to
|
||||
* write the post-palette text or unknown chunks.
|
||||
*/
|
||||
png_ptr->mode |= PNG_HAVE_PLTE;
|
||||
|
||||
# ifdef PNG_WRITE_tRNS_SUPPORTED
|
||||
if ((info_ptr->valid & PNG_INFO_tRNS) !=0)
|
||||
{
|
||||
@ -349,7 +369,8 @@ png_write_info(png_structrp png_ptr, png_const_inforp info_ptr)
|
||||
# endif /* WRITE_pHYs */
|
||||
|
||||
# ifdef PNG_WRITE_tIME_SUPPORTED
|
||||
if ((info_ptr->valid & PNG_INFO_tIME) != 0)
|
||||
if ((info_ptr->valid & PNG_INFO_tIME) != 0 &&
|
||||
(info_ptr->time_location & PNG_HAVE_PLTE) != 0)
|
||||
png_write_tIME(png_ptr, &(info_ptr->mod_time));
|
||||
# endif /* WRITE_tIME */
|
||||
|
||||
@ -365,7 +386,7 @@ png_write_info(png_structrp png_ptr, png_const_inforp info_ptr)
|
||||
|
||||
# ifdef PNG_WRITE_TEXT_SUPPORTED
|
||||
if (info_ptr->num_text > 0)
|
||||
png_write_text(png_ptr, info_ptr);
|
||||
png_write_text(png_ptr, info_ptr, PNG_HAVE_PLTE);
|
||||
# endif /* WRITE_TEXT */
|
||||
|
||||
# ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
|
||||
@ -438,13 +459,14 @@ png_write_end(png_structrp png_ptr, png_inforp info_ptr)
|
||||
{
|
||||
# ifdef PNG_WRITE_tIME_SUPPORTED
|
||||
/* Check to see if user has supplied a time chunk */
|
||||
if ((info_ptr->valid & PNG_INFO_tIME) != 0)
|
||||
if ((info_ptr->valid & PNG_INFO_tIME) != 0 &&
|
||||
(info_ptr->time_location & PNG_AFTER_IDAT) != 0)
|
||||
png_write_tIME(png_ptr, &(info_ptr->mod_time));
|
||||
# endif
|
||||
|
||||
# ifdef PNG_WRITE_TEXT_SUPPORTED
|
||||
if (info_ptr->num_text > 0)
|
||||
png_write_text(png_ptr, info_ptr);
|
||||
png_write_text(png_ptr, info_ptr, PNG_AFTER_IDAT);
|
||||
# endif /* WRITE_TEXT */
|
||||
|
||||
# ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
|
||||
|
@ -2218,12 +2218,6 @@ png_write_tIME(png_structrp png_ptr, png_const_timep mod_time)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Duplicate tIME chunks are invalid; this works round a bug in png_write_png
|
||||
* where it would write the tIME chunk once before and once after the IDAT.
|
||||
*/
|
||||
if (png_ptr->wrote_tIME)
|
||||
return;
|
||||
|
||||
png_save_uint_16(buf, mod_time->year);
|
||||
buf[2] = mod_time->month;
|
||||
buf[3] = mod_time->day;
|
||||
@ -2232,7 +2226,6 @@ png_write_tIME(png_structrp png_ptr, png_const_timep mod_time)
|
||||
buf[6] = mod_time->second;
|
||||
|
||||
png_write_complete_chunk(png_ptr, png_tIME, buf, (png_size_t)7);
|
||||
png_ptr->wrote_tIME = 1U;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user