2006-05-04 16:36:51 +00:00
|
|
|
/*---------------------------------------------------------------------------*
|
|
|
|
| PDFlib - A library for generating PDF on the fly |
|
|
|
|
+---------------------------------------------------------------------------+
|
|
|
|
| Copyright (c) 1997-2005 Thomas Merz and PDFlib GmbH. All rights reserved. |
|
|
|
|
+---------------------------------------------------------------------------+
|
|
|
|
| |
|
|
|
|
| This software is subject to the PDFlib license. It is NOT in the |
|
|
|
|
| public domain. Extended versions and commercial licenses are |
|
|
|
|
| available, please check http://www.pdflib.com. |
|
|
|
|
| |
|
|
|
|
*---------------------------------------------------------------------------*/
|
|
|
|
|
2006-07-11 13:10:51 +00:00
|
|
|
/* $Id: p_png.c,v 1.2 2006-07-11 13:10:33 alex Exp $
|
2006-05-04 16:36:51 +00:00
|
|
|
*
|
|
|
|
* PNG processing for PDFlib
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "p_intern.h"
|
|
|
|
#include "p_color.h"
|
|
|
|
#include "p_image.h"
|
|
|
|
|
|
|
|
#if defined(__ia64__) && defined (__linux__)
|
|
|
|
#define PDF_ALIGN16
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* SPNG - Simple PNG
|
|
|
|
**
|
|
|
|
** The items below, prefixed with spng_, or SPNG_, establish a replacement
|
|
|
|
** for LIBPNG that works very fast, but processes simple PNG images only:
|
|
|
|
** - bit_depth <= 8 (no 16-bit)
|
|
|
|
** - interlace_type 0 (no interlacing)
|
|
|
|
** - color_type 0, 2, or 3 (no alpha-channel); color type 3 requires
|
|
|
|
** libpng for palette processing
|
|
|
|
*/
|
|
|
|
#define SPNG_SIGNATURE "\211\120\116\107\015\012\032\012"
|
|
|
|
|
|
|
|
#define SPNG_CHUNK_IHDR 0x49484452
|
|
|
|
#define SPNG_CHUNK_PLTE 0x504C5445
|
|
|
|
#define SPNG_CHUNK_tRNS 0x74524E53
|
|
|
|
#define SPNG_CHUNK_IDAT 0x49444154
|
|
|
|
#define SPNG_CHUNK_IEND 0x49454E44
|
|
|
|
|
|
|
|
/* spng_init() return codes
|
|
|
|
*/
|
|
|
|
#define SPNG_ERR_OK 0 /* no error */
|
|
|
|
#define SPNG_ERR_NOPNG 1 /* bad PNG signature */
|
|
|
|
#define SPNG_ERR_FMT 2 /* bad PNG file format */
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
/* from IHDR:
|
|
|
|
*/
|
|
|
|
int width;
|
|
|
|
int height;
|
|
|
|
pdc_byte bit_depth;
|
|
|
|
pdc_byte color_type;
|
|
|
|
pdc_byte compr_type;
|
|
|
|
pdc_byte filter_type;
|
|
|
|
pdc_byte interlace_type;
|
|
|
|
} spng_info;
|
|
|
|
|
|
|
|
static int
|
|
|
|
spng_getint(pdc_file *fp)
|
|
|
|
{
|
|
|
|
unsigned char buf[4];
|
|
|
|
|
|
|
|
if (!PDC_OK_FREAD(fp, buf, 4))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return (int) pdc_get_be_long(buf);
|
|
|
|
} /* spng_getint */
|
|
|
|
|
|
|
|
static int
|
|
|
|
spng_init(PDF *p, pdf_image *image, spng_info *spi)
|
|
|
|
{
|
|
|
|
pdc_file *fp = image->fp;
|
|
|
|
char buf[8];
|
|
|
|
|
|
|
|
(void) p;
|
|
|
|
|
|
|
|
/* check signature
|
|
|
|
*/
|
|
|
|
if (!PDC_OK_FREAD(fp, buf, 8) ||
|
|
|
|
strncmp(buf, SPNG_SIGNATURE, 8) != 0)
|
|
|
|
return SPNG_ERR_NOPNG;
|
|
|
|
|
|
|
|
/* read IHDR
|
|
|
|
*/
|
|
|
|
if (spng_getint(fp) != 13 ||
|
|
|
|
spng_getint(fp) != SPNG_CHUNK_IHDR)
|
|
|
|
return SPNG_ERR_FMT;
|
|
|
|
|
|
|
|
spi->width = spng_getint(fp);
|
|
|
|
spi->height = spng_getint(fp);
|
|
|
|
|
|
|
|
if (!PDC_OK_FREAD(fp, buf, 5))
|
|
|
|
return SPNG_ERR_FMT;
|
|
|
|
|
|
|
|
spi->bit_depth = (pdc_byte) buf[0];
|
|
|
|
spi->color_type = (pdc_byte) buf[1];
|
|
|
|
spi->compr_type = (pdc_byte) buf[2];
|
|
|
|
spi->filter_type = (pdc_byte) buf[3];
|
|
|
|
spi->interlace_type = (pdc_byte) buf[4];
|
|
|
|
|
|
|
|
(void) spng_getint(fp); /* CRC */
|
|
|
|
|
|
|
|
/* decide whether this image is "simple".
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_LIBPNG
|
|
|
|
if (spi->bit_depth > 8 || spi->color_type > 3 || spi->interlace_type != 0)
|
|
|
|
#else
|
|
|
|
if (spi->bit_depth > 8 || spi->color_type > 2 || spi->interlace_type != 0)
|
|
|
|
#endif /* !HAVE_LIBPNG */
|
|
|
|
{
|
|
|
|
image->use_raw = pdc_false;
|
|
|
|
return SPNG_ERR_OK;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
image->use_raw = pdc_true;
|
|
|
|
|
|
|
|
/* read (or skip) all chunks up to the first IDAT.
|
|
|
|
*/
|
|
|
|
for (/* */ ; /* */ ; /* */)
|
|
|
|
{
|
|
|
|
int len = spng_getint(fp);
|
|
|
|
int type = spng_getint(fp);
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case SPNG_CHUNK_IDAT: /* prepare data xfer */
|
|
|
|
image->info.png.nbytes = (size_t) len;
|
|
|
|
return SPNG_ERR_OK;
|
|
|
|
|
|
|
|
case -1:
|
|
|
|
return SPNG_ERR_FMT;
|
|
|
|
|
|
|
|
/* if we decide to live without LIBPNG,
|
|
|
|
** we should handle these cases, too.
|
|
|
|
*/
|
|
|
|
case SPNG_CHUNK_tRNS: /* transparency chunk */
|
|
|
|
case SPNG_CHUNK_PLTE: /* read in palette */
|
|
|
|
|
|
|
|
default:
|
|
|
|
pdc_fseek(fp, len + 4, SEEK_CUR);
|
|
|
|
/* skip data & CRC */
|
|
|
|
break;
|
|
|
|
} /* switch */
|
|
|
|
}
|
|
|
|
|
|
|
|
return SPNG_ERR_OK;
|
|
|
|
} /* spng_init */
|
|
|
|
|
|
|
|
static void
|
|
|
|
pdf_png_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
|
|
|
|
{
|
|
|
|
pdc_file *fp = (pdc_file *) png_ptr->io_ptr;
|
|
|
|
char *filename = (char *) pdc_file_name(fp);
|
|
|
|
|
|
|
|
if (!PDC_OK_FREAD(fp, data, length))
|
|
|
|
{
|
|
|
|
pdc_core *pdc = pdc_file_getpdc(fp);
|
|
|
|
|
|
|
|
pdc_error(pdc, PDF_E_IMAGE_CORRUPT, "PNG", filename, 0, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define PDF_PNG_BUFFERSIZE 8192
|
|
|
|
|
|
|
|
static void
|
|
|
|
pdf_data_source_PNG_init(PDF *p, PDF_data_source *src)
|
|
|
|
{
|
|
|
|
static const char fn[] = "pdf_data_source_PNG_init";
|
|
|
|
pdf_image *image = (pdf_image *) src->private_data;
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBPNG
|
|
|
|
if (image->use_raw)
|
|
|
|
{
|
|
|
|
#endif
|
|
|
|
src->buffer_length = PDF_PNG_BUFFERSIZE;
|
|
|
|
src->buffer_start = (pdc_byte *)
|
|
|
|
pdc_malloc(p->pdc, src->buffer_length, fn);
|
|
|
|
src->bytes_available = 0;
|
|
|
|
src->next_byte = src->buffer_start;
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBPNG
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
image->info.png.cur_line = 0;
|
|
|
|
src->buffer_length = image->info.png.rowbytes;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef min
|
|
|
|
#define min(a, b) (((a) < (b)) ? (a) : (b))
|
|
|
|
|
|
|
|
static void
|
|
|
|
spng_error(PDF *p, PDF_data_source *src)
|
|
|
|
{
|
|
|
|
pdf_image *image = (pdf_image *) src->private_data;
|
|
|
|
|
|
|
|
pdc_error(p->pdc, PDF_E_IMAGE_CORRUPT, "PNG", image->filename, 0, 0);
|
|
|
|
} /* spng_error */
|
|
|
|
|
|
|
|
static pdc_bool
|
|
|
|
pdf_data_source_PNG_fill(PDF *p, PDF_data_source *src)
|
|
|
|
{
|
|
|
|
pdf_image *image = (pdf_image *) src->private_data;
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBPNG
|
|
|
|
if (image->use_raw)
|
|
|
|
{
|
|
|
|
#endif
|
|
|
|
pdc_file * fp = image->fp;
|
|
|
|
size_t buf_avail = src->buffer_length;
|
|
|
|
pdc_byte *scan = src->buffer_start;
|
|
|
|
|
|
|
|
src->bytes_available = 0;
|
|
|
|
|
|
|
|
if (image->info.png.nbytes == 0)
|
|
|
|
return pdc_false;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
size_t nbytes = min(image->info.png.nbytes, buf_avail);
|
|
|
|
|
|
|
|
if (!PDC_OK_FREAD(fp, scan, nbytes))
|
|
|
|
spng_error(p, src);
|
|
|
|
|
|
|
|
src->bytes_available += nbytes;
|
|
|
|
scan += nbytes;
|
|
|
|
buf_avail -= nbytes;
|
|
|
|
|
|
|
|
if ((image->info.png.nbytes -= nbytes) == 0)
|
|
|
|
{
|
|
|
|
/* proceed to next IDAT chunk
|
|
|
|
*/
|
|
|
|
(void) spng_getint(fp); /* CRC */
|
|
|
|
image->info.png.nbytes =
|
|
|
|
(size_t) spng_getint(fp); /* length */
|
|
|
|
|
|
|
|
if (spng_getint(fp) != SPNG_CHUNK_IDAT)
|
|
|
|
{
|
|
|
|
image->info.png.nbytes = 0;
|
|
|
|
return pdc_true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (buf_avail);
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBPNG
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (image->info.png.cur_line == image->height)
|
|
|
|
return pdc_false;
|
|
|
|
|
|
|
|
src->next_byte = image->info.png.raster +
|
|
|
|
image->info.png.cur_line * image->info.png.rowbytes;
|
|
|
|
|
|
|
|
src->bytes_available = src->buffer_length;
|
|
|
|
|
|
|
|
image->info.png.cur_line++;
|
|
|
|
}
|
|
|
|
#endif /* HAVE_LIBPNG */
|
|
|
|
|
|
|
|
return pdc_true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pdf_data_source_PNG_terminate(PDF *p, PDF_data_source *src)
|
|
|
|
{
|
|
|
|
pdf_image *image = (pdf_image *) src->private_data;
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBPNG
|
|
|
|
if (image->use_raw)
|
|
|
|
{
|
|
|
|
#endif
|
|
|
|
pdc_free(p->pdc, (void *) src->buffer_start);
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBPNG
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBPNG
|
|
|
|
/*
|
|
|
|
* We suppress libpng's warning message by supplying
|
|
|
|
* our own error and warning handlers
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
pdf_libpng_warning_handler(png_structp png_ptr, png_const_charp message)
|
|
|
|
{
|
|
|
|
(void) png_ptr; /* avoid compiler warning "unreferenced parameter" */
|
|
|
|
(void) message; /* avoid compiler warning "unreferenced parameter" */
|
|
|
|
|
|
|
|
/* do nothing */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* use the libpng portability aid in an attempt to overcome version differences
|
|
|
|
*/
|
|
|
|
#ifndef png_jmpbuf
|
|
|
|
#define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void
|
|
|
|
pdf_libpng_error_handler(png_structp png_ptr, png_const_charp message)
|
|
|
|
{
|
|
|
|
#ifdef PDF_ALIGN16
|
|
|
|
jmp_buf jbuf;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
(void) message; /* avoid compiler warning "unreferenced parameter" */
|
|
|
|
|
|
|
|
#ifdef PDF_ALIGN16
|
|
|
|
memcpy(jbuf, png_jmpbuf(png_ptr), sizeof (jmp_buf));
|
|
|
|
longjmp(jbuf, 1);
|
|
|
|
#else
|
|
|
|
longjmp(png_jmpbuf(png_ptr), 1);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *
|
|
|
|
pdf_libpng_malloc(png_structp png_ptr, size_t size)
|
|
|
|
{
|
|
|
|
PDF *p = (PDF *)png_ptr->mem_ptr;
|
|
|
|
|
|
|
|
return pdc_malloc(p->pdc, size, "libpng");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pdf_libpng_free(png_structp png_ptr, void *mem)
|
|
|
|
{
|
|
|
|
PDF *p = (PDF *)png_ptr->mem_ptr;
|
|
|
|
|
|
|
|
pdc_free(p->pdc, mem);
|
|
|
|
}
|
|
|
|
|
|
|
|
pdc_bool
|
|
|
|
pdf_is_PNG_file(PDF *p, pdc_file *fp)
|
|
|
|
{
|
|
|
|
pdc_byte sig[8];
|
|
|
|
|
|
|
|
pdc_trace_protocol(p->pdc, 1, trc_image, "\tChecking image type PNG...\n");
|
|
|
|
|
|
|
|
if (!PDC_OK_FREAD(fp, sig, 8) || !png_check_sig(sig, 8)) {
|
|
|
|
pdc_fseek(fp, 0L, SEEK_SET);
|
|
|
|
return pdc_false;
|
|
|
|
}
|
|
|
|
return pdc_true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
pdf_process_PNG_data(
|
|
|
|
PDF *p,
|
|
|
|
int imageslot)
|
|
|
|
{
|
|
|
|
static const char *fn = "pdf_process_PNG_data";
|
|
|
|
pdc_file *save_fp;
|
|
|
|
spng_info s_info;
|
|
|
|
#ifdef PDF_ALIGN16
|
|
|
|
jmp_buf jbuf;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
png_uint_32 width, height, ui;
|
|
|
|
png_bytep *row_pointers, trans;
|
|
|
|
png_color_8p sig_bit;
|
|
|
|
png_color_16p trans_values;
|
|
|
|
int bit_depth, color_type, i, num_trans;
|
|
|
|
pdc_scalar dpi_x, dpi_y;
|
|
|
|
pdf_image *image;
|
|
|
|
volatile int errcode = 0;
|
|
|
|
pdf_colorspace cs;
|
|
|
|
pdf_colormap * volatile colormap = NULL;
|
|
|
|
volatile int slot;
|
|
|
|
|
|
|
|
image = &p->images[imageslot];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We can install our own memory handlers in libpng since
|
|
|
|
* our PNG library is specially extended to support this.
|
|
|
|
* A custom version of libpng without support for
|
|
|
|
* png_create_read_struct_2() is no longer supported.
|
|
|
|
*/
|
|
|
|
|
|
|
|
image->info.png.png_ptr =
|
|
|
|
png_create_read_struct_2(PNG_LIBPNG_VER_STRING, (png_voidp) NULL,
|
|
|
|
pdf_libpng_error_handler, pdf_libpng_warning_handler,
|
|
|
|
p, (png_malloc_ptr) pdf_libpng_malloc,
|
|
|
|
(png_free_ptr) pdf_libpng_free);
|
|
|
|
|
|
|
|
if (!image->info.png.png_ptr) {
|
|
|
|
pdc_error(p->pdc, PDC_E_MEM_OUT, fn, 0, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
image->info.png.info_ptr = png_create_info_struct(image->info.png.png_ptr);
|
|
|
|
|
|
|
|
if (image->info.png.info_ptr == NULL) {
|
|
|
|
png_destroy_read_struct(&image->info.png.png_ptr,
|
|
|
|
(png_infopp) NULL, (png_infopp) NULL);
|
|
|
|
pdc_error(p->pdc, PDC_E_MEM_OUT, fn, 0, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* due to alignment bug on itanium machines:
|
|
|
|
** use well aligned local jbuf instead of sometimes
|
|
|
|
** bad aligned (allocated) jmp_buf.
|
|
|
|
*/
|
|
|
|
#ifdef PDF_ALIGN16
|
|
|
|
if (setjmp(jbuf))
|
|
|
|
#else
|
|
|
|
if (setjmp(png_jmpbuf(image->info.png.png_ptr)))
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
errcode = PDF_E_IMAGE_CORRUPT;
|
|
|
|
goto PDF_PNG_ERROR;
|
|
|
|
}
|
|
|
|
#ifdef PDF_ALIGN16
|
|
|
|
memcpy(png_jmpbuf(image->info.png.png_ptr), jbuf, sizeof (jmp_buf));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (pdf_is_PNG_file(p, image->fp) == pdc_false) {
|
|
|
|
errcode = PDC_E_IO_BADFORMAT;
|
|
|
|
goto PDF_PNG_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* from file or from file or memory */
|
|
|
|
png_set_read_fn(image->info.png.png_ptr, image->fp, pdf_png_read_data);
|
|
|
|
|
|
|
|
png_set_sig_bytes(image->info.png.png_ptr, 8);
|
|
|
|
png_read_info(image->info.png.png_ptr, image->info.png.info_ptr);
|
|
|
|
png_get_IHDR(image->info.png.png_ptr, image->info.png.info_ptr,
|
|
|
|
&width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
|
|
|
|
|
|
|
|
image->width = (pdc_scalar) width;
|
|
|
|
image->height = (pdc_scalar) height;
|
|
|
|
|
|
|
|
/* reduce 16-bit images to 8 bit since PDF < 1.5 stops at 8 bit */
|
|
|
|
if (p->compatibility < PDC_1_5 && bit_depth == 16) {
|
|
|
|
png_set_strip_16(image->info.png.png_ptr);
|
|
|
|
bit_depth = 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
image->bpc = bit_depth;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We currently don't support a real alpha channel but only binary
|
|
|
|
* tranparency ("poor man's alpha"). For palette images we do our best and
|
|
|
|
* treat alpha values of up to 50% as transparent, and values above 50%
|
|
|
|
* as opaque. Gray and RGB images with an associated alpha channel will
|
|
|
|
* be pre-multiplied by libpng (against white background).
|
|
|
|
*/
|
|
|
|
#define ALPHA_THRESHOLD 128
|
|
|
|
|
|
|
|
switch (color_type) {
|
|
|
|
case PNG_COLOR_TYPE_GRAY_ALPHA:
|
|
|
|
/* LATER: construct mask from alpha channel */
|
|
|
|
/*
|
|
|
|
png_set_IHDR(image->info.png.png_ptr, image->info.png.info_ptr,
|
|
|
|
width, height, bit_depth, PNG_COLOR_MASK_ALPHA,
|
|
|
|
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
|
|
|
|
PNG_FILTER_TYPE_DEFAULT);
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* We strip the alpha channel, and let libpng pre-multiply
|
|
|
|
* the opacity values to the image data.
|
|
|
|
*/
|
|
|
|
png_set_strip_alpha(image->info.png.png_ptr);
|
|
|
|
/* fall through */
|
|
|
|
|
|
|
|
case PNG_COLOR_TYPE_GRAY:
|
|
|
|
if (png_get_sBIT(image->info.png.png_ptr,
|
|
|
|
image->info.png.info_ptr, &sig_bit)) {
|
|
|
|
png_set_shift(image->info.png.png_ptr, sig_bit);
|
|
|
|
}
|
|
|
|
|
|
|
|
image->colorspace = DeviceGray;
|
|
|
|
|
|
|
|
image->components = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PNG_COLOR_TYPE_RGB_ALPHA:
|
|
|
|
/* LATER: construct mask from alpha channel */
|
|
|
|
/*
|
|
|
|
* We strip the alpha channel, and let libpng pre-multiply
|
|
|
|
* the opacity values to the image data.
|
|
|
|
*/
|
|
|
|
png_set_strip_alpha(image->info.png.png_ptr);
|
|
|
|
/* fall through */
|
|
|
|
|
|
|
|
case PNG_COLOR_TYPE_RGB:
|
|
|
|
if (image->colorspace == pdc_undef)
|
|
|
|
image->colorspace = DeviceRGB;
|
|
|
|
image->components = 3;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PNG_COLOR_TYPE_PALETTE:
|
|
|
|
{
|
|
|
|
png_colorp pcm;
|
|
|
|
|
|
|
|
png_get_PLTE(image->info.png.png_ptr, image->info.png.info_ptr,
|
|
|
|
&pcm, &cs.val.indexed.palette_size);
|
|
|
|
|
|
|
|
colormap =
|
|
|
|
(pdf_colormap *) pdc_malloc(p->pdc, sizeof(pdf_colormap), fn);
|
|
|
|
|
|
|
|
/* This seems redundant, but the png_colorp structure may not
|
|
|
|
* be packed on some platforms.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < cs.val.indexed.palette_size; i++)
|
|
|
|
{
|
|
|
|
(*colormap)[i][0] = (pdc_byte) pcm[i].red;
|
|
|
|
(*colormap)[i][1] = (pdc_byte) pcm[i].green;
|
|
|
|
(*colormap)[i][2] = (pdc_byte) pcm[i].blue;
|
|
|
|
}
|
|
|
|
|
|
|
|
image->components = 1;
|
|
|
|
|
|
|
|
/* This case should arguably be prohibited. However, we allow
|
|
|
|
* it and take the palette indices 0 and 1 as the mask,
|
|
|
|
* disregarding any color values in the palette.
|
|
|
|
*/
|
|
|
|
if (image->imagemask) {
|
|
|
|
image->colorspace = DeviceGray;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
cs.type = Indexed;
|
|
|
|
cs.val.indexed.base = DeviceRGB;
|
|
|
|
cs.val.indexed.colormap = colormap;
|
|
|
|
cs.val.indexed.colormap_id = PDC_BAD_ID;
|
|
|
|
slot = pdf_add_colorspace(p, &cs, pdc_false);
|
|
|
|
|
|
|
|
image->colorspace = (pdf_colorspacetype) slot;
|
|
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (colormap)
|
|
|
|
pdc_free(p->pdc, colormap);
|
|
|
|
|
|
|
|
if (image->imagemask)
|
|
|
|
{
|
|
|
|
if (image->components != 1) {
|
|
|
|
errcode = PDF_E_IMAGE_BADMASK;
|
|
|
|
goto PDF_PNG_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p->compatibility <= PDC_1_3) {
|
|
|
|
if (image->components != 1 || image->bpc != 1) {
|
|
|
|
errcode = PDF_E_IMAGE_MASK1BIT13;
|
|
|
|
goto PDF_PNG_ERROR;
|
|
|
|
}
|
|
|
|
} else if (image->bpc > 1) {
|
|
|
|
/* images with more than one bit will be written as /SMask,
|
|
|
|
* and don't require an /ImageMask entry.
|
|
|
|
*/
|
|
|
|
image->imagemask = pdc_false;
|
|
|
|
}
|
|
|
|
image->colorspace = DeviceGray;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we invert this flag later */
|
|
|
|
if (image->ignoremask)
|
|
|
|
image->transparent = pdc_true;
|
|
|
|
|
|
|
|
/* let libpng expand interlaced images */
|
|
|
|
(void) png_set_interlace_handling(image->info.png.png_ptr);
|
|
|
|
|
|
|
|
/* read the physical dimensions chunk to find the resolution values */
|
|
|
|
dpi_x = (pdc_scalar) png_get_x_pixels_per_meter(image->info.png.png_ptr,
|
|
|
|
image->info.png.info_ptr);
|
|
|
|
dpi_y = (pdc_scalar) png_get_y_pixels_per_meter(image->info.png.png_ptr,
|
|
|
|
image->info.png.info_ptr);
|
|
|
|
|
|
|
|
if (dpi_x != 0 && dpi_y != 0) { /* absolute values */
|
|
|
|
image->dpi_x = dpi_x * PDC_INCH2METER;
|
|
|
|
image->dpi_y = dpi_y * PDC_INCH2METER;
|
|
|
|
|
|
|
|
} else { /* aspect ratio */
|
|
|
|
image->dpi_y = -png_get_pixel_aspect_ratio(image->info.png.png_ptr,
|
|
|
|
image->info.png.info_ptr);
|
|
|
|
|
|
|
|
if (image->dpi_y == 0) /* unknown */
|
|
|
|
image->dpi_x = 0;
|
|
|
|
else
|
|
|
|
image->dpi_x = -1.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* read the transparency chunk */
|
|
|
|
if (png_get_valid(image->info.png.png_ptr, image->info.png.info_ptr,
|
|
|
|
PNG_INFO_tRNS)) {
|
|
|
|
png_get_tRNS(image->info.png.png_ptr, image->info.png.info_ptr,
|
|
|
|
&trans, &num_trans, &trans_values);
|
|
|
|
if (num_trans > 0) {
|
|
|
|
if (color_type == PNG_COLOR_TYPE_GRAY) {
|
|
|
|
image->transparent = !image->transparent;
|
|
|
|
/* LATER: scale down 16-bit transparency values ? */
|
|
|
|
image->transval[0] = (pdc_byte) trans_values[0].gray;
|
|
|
|
|
|
|
|
} else if (color_type == PNG_COLOR_TYPE_RGB) {
|
|
|
|
image->transparent = !image->transparent;
|
|
|
|
/* LATER: scale down 16-bit transparency values ? */
|
|
|
|
image->transval[0] = (pdc_byte) trans_values[0].red;
|
|
|
|
image->transval[1] = (pdc_byte) trans_values[0].green;
|
|
|
|
image->transval[2] = (pdc_byte) trans_values[0].blue;
|
|
|
|
|
|
|
|
} else if (color_type == PNG_COLOR_TYPE_PALETTE) {
|
|
|
|
/* we use the first transparent entry in the tRNS palette */
|
|
|
|
for (i = 0; i < num_trans; i++) {
|
|
|
|
if ((pdc_byte) trans[i] < ALPHA_THRESHOLD) {
|
|
|
|
image->transparent = !image->transparent;
|
|
|
|
image->transval[0] = (pdc_byte) i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
png_read_update_info(image->info.png.png_ptr, image->info.png.info_ptr);
|
|
|
|
|
|
|
|
image->info.png.rowbytes =
|
|
|
|
png_get_rowbytes(image->info.png.png_ptr, image->info.png.info_ptr);
|
|
|
|
|
|
|
|
image->info.png.raster = (pdc_byte *)
|
|
|
|
pdc_calloc(p->pdc,image->info.png.rowbytes * height, fn);
|
|
|
|
|
|
|
|
row_pointers = (png_bytep *)
|
|
|
|
pdc_malloc(p->pdc, height * sizeof(png_bytep), fn);
|
|
|
|
|
|
|
|
for (ui = 0; ui < height; ui++) {
|
|
|
|
row_pointers[ui] =
|
|
|
|
image->info.png.raster + ui * image->info.png.rowbytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* try the simple way:
|
|
|
|
*/
|
|
|
|
save_fp = image->fp;
|
|
|
|
|
|
|
|
image->src.init = pdf_data_source_PNG_init;
|
|
|
|
image->src.fill = pdf_data_source_PNG_fill;
|
|
|
|
image->src.terminate = pdf_data_source_PNG_terminate;
|
|
|
|
image->src.private_data = (void *) image;
|
|
|
|
|
|
|
|
|
|
|
|
image->fp = pdf_fopen(p, image->filename, NULL, PDC_FILE_BINARY);
|
|
|
|
if (image->fp != NULL &&
|
|
|
|
spng_init(p, image, &s_info) == SPNG_ERR_OK && image->use_raw)
|
|
|
|
{
|
|
|
|
pdc_fclose(save_fp);
|
|
|
|
image->predictor = pred_png;
|
|
|
|
image->compression = pdf_comp_flate;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (image->fp != (pdc_file *) 0)
|
|
|
|
pdc_fclose(image->fp);
|
|
|
|
|
|
|
|
image->fp = save_fp;
|
|
|
|
|
|
|
|
/* Provide a suitable background for images with alpha channel */
|
|
|
|
if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
|
|
|
|
color_type == PNG_COLOR_TYPE_RGB_ALPHA)
|
|
|
|
{
|
|
|
|
png_color_16p image_background;
|
|
|
|
|
|
|
|
if (png_get_bKGD(image->info.png.png_ptr, image->info.png.info_ptr,
|
|
|
|
&image_background))
|
|
|
|
{
|
|
|
|
png_set_background(image->info.png.png_ptr, image_background,
|
|
|
|
PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
png_color_16 my_white;
|
|
|
|
|
|
|
|
if (bit_depth == 8)
|
|
|
|
{
|
|
|
|
my_white.red = 0xFF;
|
|
|
|
my_white.green = 0xFF;
|
|
|
|
my_white.blue = 0xFF;
|
|
|
|
my_white.gray = 0xFF;
|
|
|
|
} else
|
|
|
|
{
|
|
|
|
my_white.red = 0xFFFF;
|
|
|
|
my_white.green = 0xFFFF;
|
|
|
|
my_white.blue = 0xFFFF;
|
|
|
|
my_white.gray = 0xFFFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
png_set_background(image->info.png.png_ptr, &my_white,
|
|
|
|
PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fetch the actual image data */
|
|
|
|
png_read_image(image->info.png.png_ptr, row_pointers);
|
|
|
|
}
|
|
|
|
|
|
|
|
image->in_use = pdc_true; /* mark slot as used */
|
|
|
|
|
|
|
|
pdf_put_image(p, imageslot, pdc_true, pdc_true);
|
|
|
|
|
|
|
|
pdc_free(p->pdc, image->info.png.raster);
|
|
|
|
pdc_free(p->pdc, row_pointers);
|
|
|
|
|
|
|
|
png_destroy_read_struct(&image->info.png.png_ptr,
|
|
|
|
&image->info.png.info_ptr, NULL);
|
|
|
|
|
|
|
|
return imageslot;
|
|
|
|
|
|
|
|
PDF_PNG_ERROR:
|
|
|
|
png_destroy_read_struct(&image->info.png.png_ptr,
|
|
|
|
&image->info.png.info_ptr, NULL);
|
|
|
|
{
|
|
|
|
const char *stemp =
|
|
|
|
pdc_errprintf(p->pdc, "%.*s", PDC_ET_MAXSTRLEN, image->filename);
|
|
|
|
switch (errcode)
|
|
|
|
{
|
|
|
|
case PDF_E_IMAGE_ICC:
|
|
|
|
case PDF_E_IMAGE_ICC2:
|
|
|
|
case PDF_E_IMAGE_MASK1BIT13:
|
|
|
|
case PDF_E_IMAGE_BADMASK:
|
|
|
|
pdc_set_errmsg(p->pdc, errcode, stemp, 0, 0, 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PDC_E_IO_BADFORMAT:
|
|
|
|
pdc_set_errmsg(p->pdc, errcode, stemp, "PNG", 0, 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PDF_E_IMAGE_CORRUPT:
|
|
|
|
pdc_set_errmsg(p->pdc, errcode, "PNG", stemp, 0, 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0: /* error code and message already set */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (image->verbose)
|
|
|
|
pdc_error(p->pdc, -1, 0, 0, 0, 0);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* !HAVE_LIBPNG */
|
|
|
|
|
|
|
|
pdc_bool
|
|
|
|
pdf_is_PNG_file(PDF *p, pdc_file *fp)
|
|
|
|
{
|
|
|
|
return pdc_false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* simple built-in PNG reader without libpng */
|
|
|
|
|
|
|
|
int
|
|
|
|
pdf_process_PNG_data(
|
|
|
|
PDF *p,
|
|
|
|
int imageslot)
|
|
|
|
{
|
|
|
|
static const char fn[] = "pdf_process_PNG_data";
|
|
|
|
spng_info s_info;
|
|
|
|
pdf_image *image;
|
|
|
|
|
|
|
|
image = &p->images[imageslot];
|
|
|
|
|
|
|
|
image->src.init = pdf_data_source_PNG_init;
|
|
|
|
image->src.fill = pdf_data_source_PNG_fill;
|
|
|
|
image->src.terminate = pdf_data_source_PNG_terminate;
|
|
|
|
image->src.private_data = (void *) image;
|
|
|
|
|
|
|
|
if (spng_init(p, image, &s_info) == SPNG_ERR_OK && image->use_raw)
|
|
|
|
{
|
|
|
|
image->predictor = pred_png;
|
|
|
|
image->compression = flate;
|
|
|
|
|
|
|
|
image->width = (pdc_scalar) s_info.width;
|
|
|
|
image->height = (pdc_scalar) s_info.height;
|
|
|
|
image->bpc = s_info.bit_depth;
|
|
|
|
|
|
|
|
image->components = 3;
|
|
|
|
|
|
|
|
/* other types are rejected in spng_init() */
|
|
|
|
image->colorspace =
|
|
|
|
(s_info.color_type == 0 ? DeviceGray : DeviceRGB);
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pdc_set_errmsg(p->pdc, PDF_E_UNSUPP_IMAGE, "PNG", 0, 0, 0);
|
|
|
|
|
|
|
|
if (image->verbose)
|
|
|
|
pdc_warning(p->pdc, -1, 0, 0, 0, 0);
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
image->in_use = pdc_true; /* mark slot as used */
|
|
|
|
|
|
|
|
pdf_put_image(p, imageslot, pdc_true, pdc_true);
|
|
|
|
|
|
|
|
return imageslot;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* !HAVE_LIBPNG */
|