guy 3bf951f42c Patch level : 10.0
Files correlati     : pdflib
Ricompilazione Demo : [ ]
Commento            :
Aggiornata pdflib.dll alla versione 7.0.4


git-svn-id: svn://10.65.10.50/trunk@18580 c028cbd2-c16b-5b4b-a496-9718f37d4682
2009-03-23 08:55:58 +00:00

862 lines
22 KiB
C
Executable File

/*---------------------------------------------------------------------------*
| PDFlib - A library for generating PDF on the fly |
+---------------------------------------------------------------------------+
| Copyright (c) 1997-2006 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. |
| |
*---------------------------------------------------------------------------*/
/* $Id: p_png.c,v 1.4 2009-03-23 08:55:35 guy Exp $
*
* 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 */
} /* for */
/* return SPNG_ERR_OK; never reached! */
} /* spng_init */
#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";
#ifdef HAVE_LIBPNG
pdf_image *image = (pdf_image *) src->private_data;
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",
pdf_get_image_filename(p, image), 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;
PDC_TRY(p->pdc)
{
#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)
{
PDC_EXIT_TRY(p->pdc);
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;
PDC_EXIT_TRY(p->pdc);
return pdc_true;
}
}
}
while (buf_avail);
#ifdef HAVE_LIBPNG
}
else
{
if (image->info.png.cur_line == image->height)
{
PDC_EXIT_TRY(p->pdc);
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 */
}
PDC_CATCH(p->pdc)
{
image->corrupt = pdc_true;
}
return !image->corrupt;
}
static void
pdf_data_source_PNG_terminate(PDF *p, PDF_data_source *src)
{
#ifdef HAVE_LIBPNG
pdf_image *image = (pdf_image *) src->private_data;
if (image->use_raw)
{
#endif
pdc_free(p->pdc, (void *) src->buffer_start);
#ifdef HAVE_LIBPNG
}
#endif
}
#ifdef HAVE_LIBPNG
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);
}
}
/*
* 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_logg_cond(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 = NULL, 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,
(png_error_ptr) pdf_libpng_error_handler,
(png_error_ptr) 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 memory */
png_set_read_fn(image->info.png.png_ptr, image->fp,
(png_rw_ptr) 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 = 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 = pdc_fsearch_fopen(p->pdc, image->filename, NULL, 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);
if (row_pointers != NULL)
pdc_free(p->pdc, row_pointers);
png_destroy_read_struct(&image->info.png.png_ptr,
&image->info.png.info_ptr, NULL);
if (!image->corrupt)
return imageslot;
PDF_PNG_ERROR:
{
const char *stemp = NULL;
if (errcode)
{
png_destroy_read_struct(&image->info.png.png_ptr,
&image->info.png.info_ptr, NULL);
stemp = pdf_get_image_filename(p, image);
}
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;
}
}
return -1;
}
#else /* !HAVE_LIBPNG */
pdc_bool
pdf_is_PNG_file(PDF *p, pdc_file *fp)
{
(void) p;
(void) fp;
return pdc_false;
}
/* simple built-in PNG reader without libpng */
int
pdf_process_PNG_data(
PDF *p,
int imageslot)
{
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 = pdf_comp_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);
return -1;
}
image->in_use = pdc_true; /* mark slot as used */
pdf_put_image(p, imageslot, pdc_true, pdc_true);
return image->corrupt ? -1 : imageslot;
}
#endif /* !HAVE_LIBPNG */