blob: 6d3359c465d9d385c59032226326ec200c41f365 [file] [log] [blame]
/*
* pngxread.c - libpng external I/O: read utility functions.
* Copyright (C) 2003-2011 Cosmin Truta.
*/
#include "pngxtern.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PNGX_INTERNAL
#include "pngxpriv.h"
static int
pngx_sig_is_png(png_structp png_ptr,
png_bytep sig, size_t sig_size,
png_const_charpp fmt_name_ptr,
png_const_charpp fmt_long_name_ptr)
{
/* The signature of this function differs from the other pngx_sig_is_X()
* functions, to allow extra functionality (e.g. customized error messages)
* without requiring a full pngx_read_png().
*/
static const char pngx_png_standalone_fmt_name[] =
"PNG";
static const char pngx_png_datastream_fmt_name[] =
"PNG datastream";
static const char pngx_png_standalone_fmt_long_name[] =
"Portable Network Graphics";
static const char pngx_png_datastream_fmt_long_name[] =
"Portable Network Graphics embedded datastream";
static const png_byte png_file_sig[8] = {137, 80, 78, 71, 13, 10, 26, 10};
static const png_byte mng_file_sig[8] = {138, 77, 78, 71, 13, 10, 26, 10};
static const png_byte png_ihdr_sig[8] = {0, 0, 0, 13, 73, 72, 68, 82};
int has_png_sig;
/* Since png_read_png() fails rather abruptly with png_error(),
* spend a little more effort to ensure that the format is indeed PNG.
* Among other things, look for the presence of IHDR.
*/
if (sig_size <= 25 + 18) /* size of (IHDR + IDAT) > (12+13) + (12+6) */
return -1;
has_png_sig = (memcmp(sig, png_file_sig, 8) == 0);
if (memcmp(sig + (has_png_sig ? 8 : 0), png_ihdr_sig, 8) != 0)
{
/* This is not valid PNG: get as much information as possible. */
if (memcmp(sig, png_file_sig, 4) == 0 && (sig[4] == 10 || sig[4] == 13))
png_error(png_ptr,
"PNG file appears to be corrupted by text file conversions");
else if (memcmp(sig, mng_file_sig, 8) == 0)
png_error(png_ptr, "MNG decoding is not supported");
/* JNG is handled by the pngxrjpg module. */
return 0; /* not PNG */
}
/* Store the format name. */
if (fmt_name_ptr != NULL)
{
*fmt_name_ptr = has_png_sig ?
pngx_png_standalone_fmt_name :
pngx_png_datastream_fmt_name;
}
if (fmt_long_name_ptr != NULL)
{
*fmt_long_name_ptr = has_png_sig ?
pngx_png_standalone_fmt_long_name :
pngx_png_datastream_fmt_long_name;
}
return 1; /* PNG, really! */
}
int PNGAPI
pngx_read_image(png_structp png_ptr, png_infop info_ptr,
png_const_charpp fmt_name_ptr,
png_const_charpp fmt_long_name_ptr)
{
png_byte sig[128];
size_t num;
int (*read_fn)(png_structp, png_infop, FILE *);
FILE *stream;
fpos_t fpos;
int result;
/* Precondition. */
#ifdef PNG_FLAG_MALLOC_NULL_MEM_OK
if (png_ptr->flags & PNG_FLAG_MALLOC_NULL_MEM_OK)
png_error(png_ptr, "pngxtern requires a safe allocator");
#endif
/* Read the signature bytes. */
stream = (FILE *)png_get_io_ptr(png_ptr);
if (fgetpos(stream, &fpos) != 0)
png_error(png_ptr, "Can't ftell in input file stream");
num = fread(sig, 1, sizeof(sig), stream);
if (fsetpos(stream, &fpos) != 0)
png_error(png_ptr, "Can't fseek in input file stream");
/* Try the PNG format first. */
if (pngx_sig_is_png(png_ptr, sig, num, fmt_name_ptr, fmt_long_name_ptr) > 0)
{
png_read_png(png_ptr, info_ptr, 0, NULL);
if (getc(stream) != EOF)
{
png_warning(png_ptr, "Extraneous data found after IEND");
fseek(stream, 0, SEEK_END);
}
return 1;
}
/* Check the signature bytes against other known image formats. */
if (pngx_sig_is_bmp(sig, num, fmt_name_ptr, fmt_long_name_ptr) > 0)
read_fn = pngx_read_bmp;
else if (pngx_sig_is_gif(sig, num, fmt_name_ptr, fmt_long_name_ptr) > 0)
read_fn = pngx_read_gif;
else if (pngx_sig_is_jpeg(sig, num, fmt_name_ptr, fmt_long_name_ptr) > 0)
read_fn = pngx_read_jpeg;
else if (pngx_sig_is_pnm(sig, num, fmt_name_ptr, fmt_long_name_ptr) > 0)
read_fn = pngx_read_pnm;
else if (pngx_sig_is_tiff(sig, num, fmt_name_ptr, fmt_long_name_ptr) > 0)
read_fn = pngx_read_tiff;
else
return 0; /* not a known image format */
/* Read the image. */
result = read_fn(png_ptr, info_ptr, stream);
/* Signature checking may give false positives; reading can still fail. */
if (result <= 0) /* this isn't the format we thought it was */
if (fsetpos(stream, &fpos) != 0)
png_error(png_ptr, "Can't fseek in input file stream");
return result;
}