| Server IP : 20.75.53.88 / Your IP : 216.73.217.138 [ Web Server : Apache System : Linux VMRALP-3 4.4.0-256-generic #290~14.04.1-Ubuntu SMP Thu Jun 20 09:24:50 UTC 2024 x86_64 User : www-data ( 33) PHP Version : 5.5.9-1ubuntu4.29+esm15 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority, Domains : 3 Domains MySQL : ON | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /var/www/html/siac/dev/admin/components/htmldoc-master/htmldoc/ |
Upload File : |
/*
* Image handling routines for HTMLDOC, a HTML document processing program.
*
* Copyright © 2011-2022 by Michael R Sweet.
* Copyright © 1997-2010 by Easy Software Products. All rights reserved.
*
* This program is free software. Distribution and use rights are outlined in
* the file "COPYING".
*/
/*
* Include necessary headers.
*/
#include "htmldoc.h"
#include <setjmp.h>
#ifdef HAVE_LIBJPEG
extern "C" { /* Workaround for JPEG header problems... */
# include <jpeglib.h> /* JPEG/JFIF image definitions */
}
#endif // HAVE_JPEG
#ifdef HAVE_LIBPNG
# include <png.h> /* Portable Network Graphics (PNG) definitions */
#endif // HAVE_LIBPNG
/*
* GIF definitions...
*/
#define GIF_INTERLACE 0x40
#define GIF_COLORMAP 0x80
typedef uchar gif_cmap_t[256][3];
/*
* BMP definitions...
*/
#ifndef BI_RGB
# define BI_RGB 0 /* No compression - straight BGR data */
# define BI_RLE8 1 /* 8-bit run-length compression */
# define BI_RLE4 2 /* 4-bit run-length compression */
# define BI_BITFIELDS 3 /* RGB bitmap with RGB masks */
#endif /* !BI_RGB */
/*
* Local globals...
*/
static size_t num_images = 0, /* Number of images in cache */
alloc_images = 0; /* Allocated images */
static image_t **images = NULL; /* Images in cache */
static int gif_eof = 0; /* Did we hit EOF? */
/*
* Local functions...
*/
static int gif_read_cmap(FILE *fp, int ncolors, gif_cmap_t cmap,
int *gray);
static int gif_get_block(FILE *fp, uchar *buffer);
static int gif_get_code (FILE *fp, int code_size, int first_time);
static int gif_read_image(FILE *fp, image_t *img, gif_cmap_t cmap,
int interlace, int transparent);
static int gif_read_lzw(FILE *fp, int first_time, int input_code_size);
static int image_compare(image_t **img1, image_t **img2);
static int image_load_bmp(image_t *img, FILE *fp, int gray, int load_data);
static int image_load_gif(image_t *img, FILE *fp, int gray, int load_data);
#ifdef HAVE_LIBJPEG
static int image_load_jpeg(image_t *img, FILE *fp, int gray, int load_data);
static void jpeg_error_handler(j_common_ptr);
#endif // HAVE_LIBJPEG
#ifdef HAVE_LIBPNG
static int image_load_png(image_t *img, FILE *fp, int gray, int load_data);
#endif // HAVE_LIBPNG
static void image_need_mask(image_t *img, int scaling = 1);
static void image_set_mask(image_t *img, int x, int y, uchar alpha = 0);
static int read_long(FILE *fp);
static unsigned short read_word(FILE *fp);
static unsigned int read_dword(FILE *fp);
/*
* 'gif_read_cmap()' - Read the colormap from a GIF file...
*/
static int /* O - 0 on success, -1 on error */
gif_read_cmap(FILE *fp, /* I - File to read from */
int ncolors, /* I - Number of colors */
gif_cmap_t cmap, /* IO - Colormap array */
int *gray) /* IO - 1 = grayscale */
{
int i; /* Looping var */
/*
* Read the colormap...
*/
if (fread(cmap, 3, (size_t)ncolors, fp) < (size_t)ncolors)
{
progress_error(HD_ERROR_READ_ERROR,
"Unable to read GIF colormap: %s", strerror(errno));
return (-1);
}
/*
* Check to see if the colormap is a grayscale ramp...
*/
for (i = 0; i < ncolors; i ++)
if (cmap[i][0] != cmap[i][1] || cmap[i][1] != cmap[i][2])
break;
if (i == ncolors)
{
*gray = 1;
return (0);
}
/*
* If this needs to be a grayscale image, convert the RGB values to
* luminance values...
*/
if (*gray)
for (i = 0; i < ncolors; i ++)
cmap[i][0] = (cmap[i][0] * 31 + cmap[i][1] * 61 + cmap[i][2] * 8) / 100;
return (0);
}
/*
* 'gif_get_block()' - Read a GIF data block...
*/
static int /* O - Number characters read */
gif_get_block(FILE *fp, /* I - File to read from */
uchar *buf) /* I - Input buffer */
{
int count; /* Number of character to read */
/*
* Read the count byte followed by the data from the file...
*/
if ((count = getc(fp)) == EOF)
{
gif_eof = 1;
return (-1);
}
else if (count == 0)
gif_eof = 1;
else if (fread(buf, 1, (size_t)count, fp) < (size_t)count)
{
progress_error(HD_ERROR_READ_ERROR,
"Unable to read GIF block of %d bytes: %s", count,
strerror(errno));
gif_eof = 1;
return (-1);
}
else
gif_eof = 0;
return (count);
}
/*
* 'gif_get_code()' - Get a LZW code from the file...
*/
static int /* O - LZW code */
gif_get_code(FILE *fp, /* I - File to read from */
int code_size, /* I - Size of code in bits */
int first_time) /* I - 1 = first time, 0 = not first time */
{
unsigned i, j, /* Looping vars */
ret; /* Return value */
int count; /* Number of bytes read */
static uchar buf[280]; /* Input buffer */
static unsigned curbit, /* Current bit */
lastbit, /* Last bit in buffer */
done, /* Done with this buffer? */
last_byte; /* Last byte in buffer */
static unsigned bits[8] = /* Bit masks for codes */
{
0x01, 0x02, 0x04, 0x08,
0x10, 0x20, 0x40, 0x80
};
if (first_time)
{
/*
* Just initialize the input buffer...
*/
curbit = 0;
lastbit = 0;
last_byte = 0;
done = 0;
return (0);
}
if ((curbit + (unsigned)code_size) >= lastbit)
{
/*
* Don't have enough bits to hold the code...
*/
if (done)
{
progress_error(HD_ERROR_READ_ERROR, "Not enough data left to read GIF compression code.");
return (-1); /* Sorry, no more... */
}
/*
* Move last two bytes to front of buffer...
*/
if (last_byte > 1)
{
buf[0] = buf[last_byte - 2];
buf[1] = buf[last_byte - 1];
last_byte = 2;
}
else if (last_byte == 1)
{
buf[0] = buf[last_byte - 1];
last_byte = 1;
}
/*
* Read in another buffer...
*/
if ((count = gif_get_block(fp, buf + last_byte)) <= 0)
{
/*
* Whoops, no more data!
*/
done = 1;
return (-1);
}
/*
* Update buffer state...
*/
curbit = curbit + 8 * last_byte - lastbit;
last_byte += (unsigned)count;
lastbit = last_byte * 8;
}
for (ret = 0, i = curbit + (unsigned)code_size - 1, j = (unsigned)code_size;
j > 0;
i --, j --)
ret = (ret << 1) | ((buf[i / 8] & bits[i & 7]) != 0);
curbit += (unsigned)code_size;
return (int)ret;
}
/*
* 'gif_read_image()' - Read a GIF image stream...
*/
static int /* I - 0 = success, -1 = failure */
gif_read_image(FILE *fp, /* I - Input file */
image_t *img, /* I - Image pointer */
gif_cmap_t cmap, /* I - Colormap */
int interlace, /* I - Non-zero = interlaced image */
int transparent) /* I - Transparent color */
{
uchar code_size, /* Code size */
*temp; /* Current pixel */
int xpos, /* Current X position */
ypos, /* Current Y position */
pass; /* Current pass */
int pixel; /* Current pixel */
static int xpasses[4] = { 8, 8, 4, 2 },
ypasses[5] = { 0, 4, 2, 1, 999999 };
xpos = 0;
ypos = 0;
pass = 0;
code_size = (uchar)getc(fp);
if (code_size > 12)
{
progress_error(HD_ERROR_READ_ERROR, "Bad GIF file \"%s\" - invalid code size %d.", img->filename, code_size);
return (-1);
}
if (gif_read_lzw(fp, 1, code_size) < 0)
return (-1);
temp = img->pixels;
while ((pixel = gif_read_lzw(fp, 0, code_size)) >= 0 && pixel < 256)
{
temp[0] = cmap[pixel][0];
if (img->depth > 1)
{
temp[1] = cmap[pixel][1];
temp[2] = cmap[pixel][2];
}
if (pixel == transparent)
image_set_mask(img, xpos, ypos);
xpos ++;
temp += img->depth;
if (xpos == img->width)
{
xpos = 0;
if (interlace)
{
ypos += xpasses[pass];
temp += (xpasses[pass] - 1) * img->width * img->depth;
if (ypos >= img->height)
{
pass ++;
ypos = ypasses[pass];
temp = img->pixels + ypos * img->width * img->depth;
}
}
else
ypos ++;
}
if (ypos >= img->height)
break;
}
return (0);
}
/*
* 'gif_read_lzw()' - Read a byte from the LZW stream...
*/
static int /* I - Byte from stream */
gif_read_lzw(FILE *fp, /* I - File to read from */
int first_time, /* I - 1 = first time, 0 = not first time */
int input_code_size) /* I - Code size in bits */
{
int i, /* Looping var */
code, /* Current code */
incode; /* Input code */
static short fresh = 0, /* 1 = empty buffers */
code_size = 0, /* Current code size */
set_code_size = 0, /* Initial code size set */
max_code = 0, /* Maximum code used */
max_code_size = 0, /* Maximum code size */
firstcode = 0, /* First code read */
oldcode = 0, /* Last code read */
clear_code = 0, /* Clear code for LZW input */
end_code = 0, /* End code for LZW input */
table[2][4096], /* String table */
stack[8192], /* Output stack */
*sp = stack; /* Current stack pointer */
if (first_time)
{
/*
* Setup LZW state...
*/
set_code_size = (short)input_code_size;
code_size = set_code_size + 1;
clear_code = (short)(1 << set_code_size);
end_code = clear_code + 1;
max_code_size = 2 * clear_code;
max_code = clear_code + 2;
/*
* Initialize input buffers...
*/
gif_get_code(fp, 0, 1);
/*
* Wipe the decompressor table...
*/
fresh = 1;
for (i = 0; i < clear_code; i ++)
{
table[0][i] = 0;
table[1][i] = (short)i;
}
for (; i < 4096; i ++)
table[0][i] = table[1][0] = 0;
sp = stack;
return (0);
}
else if (fresh)
{
fresh = 0;
do
firstcode = oldcode = (short)gif_get_code(fp, code_size, 0);
while (firstcode == clear_code);
return (firstcode);
}
if (sp > stack)
return (*--sp);
while ((code = gif_get_code(fp, code_size, 0)) >= 0)
{
if (code == clear_code)
{
for (i = 0; i < clear_code; i ++)
{
table[0][i] = 0;
table[1][i] = (short)i;
}
for (; i < 4096; i ++)
table[0][i] = table[1][i] = 0;
code_size = set_code_size + 1;
max_code_size = 2 * clear_code;
max_code = clear_code + 2;
sp = stack;
firstcode = oldcode = (short)gif_get_code(fp, code_size, 0);
return (firstcode);
}
else if (code == end_code)
{
uchar buf[260];
if (!gif_eof)
while (gif_get_block(fp, buf) > 0);
return (-2);
}
incode = code;
if (code >= max_code)
{
*sp++ = firstcode;
code = oldcode;
}
while (code >= clear_code)
{
*sp++ = table[1][code];
if (code == table[0][code])
return (255);
code = table[0][code];
}
*sp++ = firstcode = table[1][code];
code = max_code;
if (code < 4096)
{
table[0][code] = oldcode;
table[1][code] = firstcode;
max_code ++;
if (max_code >= max_code_size && max_code_size < 4096)
{
max_code_size *= 2;
code_size ++;
}
}
oldcode = (short)incode;
if (sp > stack)
return (*--sp);
}
return (code);
}
/*
* 'image_compare()' - Compare two image filenames...
*/
static int /* O - Result of comparison */
image_compare(image_t **img1, /* I - First image */
image_t **img2) /* I - Second image */
{
#ifdef WIN32
return (strcasecmp((*img1)->filename, (*img2)->filename));
#else
return (strcmp((*img1)->filename, (*img2)->filename));
#endif /* WIN32 */
}
/*
* 'image_copy()' - Copy image files to the destination directory...
*/
void
image_copy(const char *src, /* I - Source file */
const char *realsrc, /* I - Real source file */
const char *destpath) /* I - Destination path */
{
char dest[255]; /* Destination file */
FILE *in, *out; /* Input/output files */
uchar buffer[8192]; /* Data buffer */
int nbytes; /* Number of bytes in buffer */
if (!src || !realsrc || !destpath)
return;
/*
* Figure out the destination filename...
*/
if (!strcmp(destpath, "."))
strlcpy(dest, file_basename(src), sizeof(dest));
else
snprintf(dest, sizeof(dest), "%s/%s", destpath, file_basename(src));
if (!strcmp(dest, realsrc))
return;
/*
* Open files and copy...
*/
if ((in = fopen(realsrc, "rb")) == NULL)
{
progress_error(HD_ERROR_READ_ERROR, "Unable to open \"%s\" - %s",
realsrc, strerror(errno));
return;
}
if ((out = fopen(dest, "wb")) == NULL)
{
progress_error(HD_ERROR_READ_ERROR, "Unable to create \"%s\" - %s",
dest, strerror(errno));
fclose(in);
return;
}
while ((nbytes = fread(buffer, 1, sizeof(buffer), in)) > 0)
fwrite(buffer, 1, (size_t)nbytes, out);
progress_error(HD_ERROR_NONE, "BYTES: %ld", ftell(out));
fclose(in);
fclose(out);
}
/*
* 'image_find()' - Find an image file in memory...
*/
image_t * /* O - Pointer to image */
image_find(const char *filename,/* I - Name of image file */
int load_data)/* I - 1 = load image data */
{
image_t key, /* Search key... */
*keyptr, /* Pointer to search key... */
**match; /* Matching image */
/*
* Range check...
*/
if (filename == NULL)
return (NULL);
if (filename[0] == '\0') /* Microsoft VC++ runtime bug workaround... */
return (NULL);
/*
* See if we've already loaded it...
*/
if (num_images > 0)
{
strlcpy(key.filename, filename, sizeof(key.filename));
keyptr = &key;
match = (image_t **)bsearch(&keyptr, images, (size_t)num_images, sizeof(image_t *),
(int (*)(const void *, const void *))image_compare);
if (match != NULL)
{
if (load_data && !(*match)->pixels)
return (image_load((*match)->filename, (*match)->depth == 1, 1));
else
return (*match);
}
}
return (NULL);
}
/*
* 'image_flush_cache()' - Flush the image cache...
*/
void
image_flush_cache(void)
{
size_t i; /* Looping var */
/*
* Free the memory used by each image...
*/
for (i = 0; i < num_images; i ++)
{
if (images[i]->mask)
free(images[i]->mask);
if (images[i]->pixels)
free(images[i]->pixels);
free(images[i]);
}
if (alloc_images)
{
free(images);
alloc_images = 0;
}
num_images = 0;
}
/*
* 'image_getlist()' - Get the list of images that are loaded.
*/
int /* O - Number of images in array */
image_getlist(image_t ***ptrs) /* O - Pointer to images array */
{
*ptrs = images;
return (num_images);
}
/*
* 'image_load()' - Load an image file from disk...
*/
image_t * /* O - Pointer to image */
image_load(const char *filename,/* I - Name of image file */
int gray, /* I - 0 = color, 1 = grayscale */
int load_data)/* I - 1 = load image data, 0 = just info */
{
#ifdef DEBUG
int i; /* Looping var */
#endif // DEBUG
FILE *fp; /* File pointer */
uchar header[16]; /* First 16 bytes of file */
image_t *img, /* New image buffer */
key, /* Search key... */
*keyptr, /* Pointer to search key... */
**match, /* Matching image */
**temp; /* Temporary array pointer */
int status; /* Status of load... */
const char *realname; /* Real filename */
/*
* Range check...
*/
if (filename == NULL)
return (NULL);
if (filename[0] == '\0') /* Microsoft VC++ runtime bug workaround... */
return (NULL);
DEBUG_printf(("image_load(filename=\"%s\", gray=%d, load_data=%d)\n",
filename, gray, load_data));
DEBUG_printf(("Path = \"%s\"\n", Path));
/*
* See if we've already loaded it...
*/
if (num_images > 0)
{
strlcpy(key.filename, filename, sizeof(key.filename));
keyptr = &key;
match = (image_t **)bsearch(&keyptr, images, (size_t)num_images, sizeof(image_t *),
(int (*)(const void *, const void *))image_compare);
if (match != NULL && (!load_data || (*match)->pixels))
{
(*match)->use ++;
return (*match);
}
}
else
match = NULL;
/*
* Figure out the file type...
*/
if ((realname = file_find(Path, filename)) == NULL)
{
progress_error(HD_ERROR_FILE_NOT_FOUND,
"Unable to find image file \"%s\"!", filename);
return (NULL);
}
if ((fp = fopen(realname, "rb")) == NULL)
{
progress_error(HD_ERROR_FILE_NOT_FOUND,
"Unable to open image file \"%s\" (%s) for reading!",
filename, realname);
return (NULL);
}
if (fread(header, 1, sizeof(header), fp) == 0)
{
progress_error(HD_ERROR_READ_ERROR,
"Unable to read image file \"%s\"!", filename);
fclose(fp);
return (NULL);
}
#ifdef DEBUG
printf("Header for \"%s\" (%s): \"", filename, realname);
for (i = 0; i < (int)sizeof(header); i ++)
if (header[i] < ' ' || header[i] >= 127)
printf("\\x%02X", header[i]);
else
putchar(header[i]);
puts("\"\n");
printf("match = %p\n", (void *)match);
#endif // DEBUG
rewind(fp);
// See if the images array needs to be resized...
if (!match)
{
if (num_images >= alloc_images)
{
// Yes...
alloc_images += ALLOC_FILES;
if (num_images == 0)
temp = (image_t **)malloc(sizeof(image_t *) * alloc_images);
else
temp = (image_t **)realloc(images, sizeof(image_t *) * alloc_images);
if (temp == NULL)
{
progress_error(HD_ERROR_OUT_OF_MEMORY,
"Unable to allocate memory for %d images - %s",
(int)alloc_images, strerror(errno));
fclose(fp);
return (NULL);
}
images = temp;
}
// Allocate memory...
img = (image_t *)calloc(sizeof(image_t), 1);
if (img == NULL)
{
progress_error(HD_ERROR_READ_ERROR, "Unable to allocate memory for \"%s\"",
filename);
fclose(fp);
return (NULL);
}
images[num_images] = img;
strlcpy(img->filename, filename, sizeof(img->filename));
img->use = 1;
}
else
img = *match;
// Load the image as appropriate...
if (memcmp(header, "GIF87a", 6) == 0 ||
memcmp(header, "GIF89a", 6) == 0)
status = image_load_gif(img, fp, gray, load_data);
else if (memcmp(header, "BM", 2) == 0)
status = image_load_bmp(img, fp, gray, load_data);
#ifdef HAVE_LIBPNG
else if (memcmp(header, "\211PNG", 4) == 0)
status = image_load_png(img, fp, gray, load_data);
#endif // HAVE_LIBPNG
#ifdef HAVE_LIBJPEG
else if (memcmp(header, "\377\330\377", 3) == 0)
status = image_load_jpeg(img, fp, gray, load_data);
#endif // HAVE_LIBJPEG
else
{
progress_error(HD_ERROR_BAD_FORMAT, "Unknown image file format for \"%s\".",
file_rlookup(filename));
fclose(fp);
free(img);
return (NULL);
}
fclose(fp);
if (status)
{
progress_error(HD_ERROR_READ_ERROR, "Unable to load image file \"%s\"!",
file_rlookup(filename));
if (!match)
free(img);
return (NULL);
}
if (!match)
{
num_images ++;
if (num_images > 1)
qsort(images, num_images, sizeof(image_t *),
(int (*)(const void *, const void *))image_compare);
}
return (img);
}
/*
* 'image_load_bmp()' - Read a BMP image file.
*/
static int /* O - 0 = success, -1 = fail */
image_load_bmp(image_t *img, /* I - Image to load into */
FILE *fp, /* I - File to read from */
int gray, /* I - Grayscale image? */
int load_data)/* I - 1 = load image data, 0 = just info */
{
int info_size, /* Size of info header */
depth, /* Depth of image (bits) */
compression, /* Type of compression */
colors_used, /* Number of colors used */
x, y, /* Looping vars */
color, /* Color of RLE pixel */
count, /* Number of times to repeat */
temp, /* Temporary color */
align; /* Alignment bytes */
uchar bit, /* Bit in image */
byte; /* Byte in image */
uchar *ptr; /* Pointer into pixels */
uchar colormap[256][4];/* Colormap */
// Get the header...
getc(fp); /* Skip "BM" sync chars */
getc(fp);
read_dword(fp); /* Skip size */
read_word(fp); /* Skip reserved stuff */
read_word(fp);
read_dword(fp);
// Then the bitmap information...
info_size = (int)read_dword(fp);
img->width = read_long(fp);
img->height = read_long(fp);
read_word(fp);
depth = read_word(fp);
compression = (int)read_dword(fp);
read_dword(fp);
read_long(fp);
read_long(fp);
colors_used = (int)read_dword(fp);
read_dword(fp);
if (img->width <= 0 || img->width > 8192 || img->height <= 0 || img->height > 8192 || info_size < 0)
return (-1);
if (info_size > 40)
{
for (info_size -= 40; info_size > 0; info_size --)
getc(fp);
}
// Get colormap...
if (colors_used == 0 && depth <= 8)
colors_used = 1 << depth;
else if (colors_used < 0 || colors_used > 256)
return (-1);
fread(colormap, (size_t)colors_used, 4, fp);
// Setup image and buffers...
img->depth = gray ? 1 : 3;
// If this image is indexed and we are writing an encrypted PDF file, bump the use count so
// we create an image object (Acrobat 6 bug workaround)
if (depth <= 8 && Encryption)
img->use ++;
// Return now if we only need the dimensions...
if (!load_data)
return (0);
img->pixels = (uchar *)malloc((size_t)(img->width * img->height * img->depth));
if (img->pixels == NULL)
return (-1);
if (gray && depth <= 8)
{
// Convert colormap to grayscale...
for (color = colors_used - 1; color >= 0; color --)
colormap[color][0] = (colormap[color][2] * 31 +
colormap[color][1] * 61 +
colormap[color][0] * 8) / 100;
}
// Read the image data...
color = 0;
count = 0;
align = 0;
byte = 0;
temp = 0;
for (y = img->height - 1; y >= 0; y --)
{
ptr = img->pixels + y * img->width * img->depth;
switch (depth)
{
case 1 : /* Bitmap */
for (x = img->width, bit = 128; x > 0; x --)
{
if (bit == 128)
byte = (uchar)getc(fp);
if (byte & bit)
{
if (!gray)
{
*ptr++ = colormap[1][2];
*ptr++ = colormap[1][1];
}
*ptr++ = colormap[1][0];
}
else
{
if (!gray)
{
*ptr++ = colormap[0][2];
*ptr++ = colormap[0][1];
}
*ptr++ = colormap[0][0];
}
if (bit > 1)
bit >>= 1;
else
bit = 128;
}
/*
* Read remaining bytes to align to 32 bits...
*/
for (temp = (img->width + 7) / 8; temp & 3; temp ++)
getc(fp);
break;
case 4 : /* 16-color */
for (x = img->width, bit = 0xf0; x > 0; x --)
{
/*
* Get a new count as needed...
*/
if (compression != BI_RLE4 && count == 0)
{
count = 2;
color = -1;
}
if (count == 0)
{
while (align > 0)
{
align --;
getc(fp);
}
if ((count = getc(fp)) == 0)
{
if ((count = getc(fp)) == 0)
{
/*
* End of line...
*/
x ++;
continue;
}
else if (count == 1)
{
/*
* End of image...
*/
break;
}
else if (count == 2)
{
/*
* Delta...
*/
count = getc(fp) * getc(fp) * img->width;
color = 0;
}
else
{
/*
* Absolute...
*/
color = -1;
align = ((4 - (count & 3)) / 2) & 1;
}
}
else
color = getc(fp);
}
/*
* Get a new color as needed...
*/
count --;
if (bit == 0xf0)
{
if (color < 0)
temp = getc(fp) & 255;
else
temp = color;
/*
* Copy the color value...
*/
if (!gray)
{
*ptr++ = colormap[temp >> 4][2];
*ptr++ = colormap[temp >> 4][1];
}
*ptr++ = colormap[temp >> 4][0];
bit = 0x0f;
}
else
{
/*
* Copy the color value...
*/
if (!gray)
{
*ptr++ = colormap[temp & 15][2];
*ptr++ = colormap[temp & 15][1];
}
*ptr++ = colormap[temp & 15][0];
bit = 0xf0;
}
}
break;
case 8 : /* 256-color */
for (x = img->width; x > 0; x --)
{
/*
* Get a new count as needed...
*/
if (compression != BI_RLE8)
{
count = 1;
color = -1;
}
if (count == 0)
{
while (align > 0)
{
align --;
getc(fp);
}
if ((count = getc(fp)) == 0)
{
if ((count = getc(fp)) == 0)
{
/*
* End of line...
*/
x ++;
continue;
}
else if (count == 1)
{
/*
* End of image...
*/
break;
}
else if (count == 2)
{
/*
* Delta...
*/
count = getc(fp) * getc(fp) * img->width;
color = 0;
}
else
{
/*
* Absolute...
*/
color = -1;
align = (2 - (count & 1)) & 1;
}
}
else
color = getc(fp);
}
/*
* Get a new color as needed...
*/
if (color < 0)
temp = getc(fp) & 255;
else
temp = color;
count --;
/*
* Copy the color value...
*/
if (!gray)
{
*ptr++ = colormap[temp][2];
*ptr++ = colormap[temp][1];
}
*ptr++ = colormap[temp][0];
}
break;
case 24 : /* 24-bit RGB */
if (gray)
{
for (x = img->width; x > 0; x --)
{
temp = getc(fp) * 8;
temp += getc(fp) * 61;
temp += getc(fp) * 31;
*ptr++ = (uchar)(temp / 100);
}
}
else
{
for (x = img->width; x > 0; x --, ptr += 3)
{
ptr[2] = (uchar)getc(fp);
ptr[1] = (uchar)getc(fp);
ptr[0] = (uchar)getc(fp);
}
}
/*
* Read remaining bytes to align to 32 bits...
*/
for (temp = img->width * 3; temp & 3; temp ++)
getc(fp);
break;
}
}
return (0);
}
/*
* 'image_load_gif()' - Load a GIF image file...
*/
static int /* O - 0 = success, -1 = fail */
image_load_gif(image_t *img, /* I - Image pointer */
FILE *fp, /* I - File to load from */
int gray, /* I - 0 = color, 1 = grayscale */
int load_data)/* I - 1 = load image data, 0 = just info */
{
uchar buf[1024]; /* Input buffer */
gif_cmap_t cmap; /* Colormap */
int ncolors, /* Bits per pixel */
transparent; /* Transparent color index */
/*
* Read the header; we already know it is a GIF file...
*/
fread(buf, 13, 1, fp);
img->width = (buf[7] << 8) | buf[6];
img->height = (buf[9] << 8) | buf[8];
ncolors = 2 << (buf[10] & 0x07);
if (img->width <= 0 || img->width > 32767 || img->height <= 0 || img->height > 32767)
return (-1);
// If we are writing an encrypted PDF file, bump the use count so we create
// an image object (Acrobat 6 bug workaround)
if (Encryption)
img->use ++;
if (buf[10] & GIF_COLORMAP)
if (gif_read_cmap(fp, ncolors, cmap, &gray))
return (-1);
transparent = -1;
while (1)
{
switch (getc(fp))
{
case ';' : /* End of image */
return (-1); /* Early end of file */
case '!' : /* Extension record */
buf[0] = (uchar)getc(fp);
if (buf[0] == 0xf9) /* Graphic Control Extension */
{
gif_get_block(fp, buf);
if (buf[0] & 1) /* Get transparent color index */
transparent = buf[3];
}
while (gif_get_block(fp, buf) != 0);
break;
case ',' : /* Image data */
fread(buf, 9, 1, fp);
if (buf[8] & GIF_COLORMAP)
{
ncolors = 2 << (buf[8] & 0x07);
if (gif_read_cmap(fp, ncolors, cmap, &gray))
return (-1);
}
img->width = (buf[5] << 8) | buf[4];
img->height = (buf[7] << 8) | buf[6];
img->depth = gray ? 1 : 3;
if (img->width <= 0 || img->width > 32767 || img->height <= 0 || img->height > 32767)
return (-1);
if (transparent >= 0)
{
/*
* Map transparent color to background color...
*/
if (BodyColor[0])
{
float rgb[3]; /* RGB color */
get_color((uchar *)BodyColor, rgb);
cmap[transparent][0] = (uchar)(rgb[0] * 255.0f + 0.5f);
cmap[transparent][1] = (uchar)(rgb[1] * 255.0f + 0.5f);
cmap[transparent][2] = (uchar)(rgb[2] * 255.0f + 0.5f);
}
else
{
cmap[transparent][0] = 255;
cmap[transparent][1] = 255;
cmap[transparent][2] = 255;
}
/*
* Allocate a mask image...
*/
image_need_mask(img);
}
if (!load_data)
return (0);
img->pixels = (uchar *)malloc((size_t)(img->width * img->height * img->depth));
if (img->pixels == NULL)
return (-1);
return (gif_read_image(fp, img, cmap, buf[8] & GIF_INTERLACE, transparent));
}
}
}
#ifdef HAVE_LIBJPEG
typedef struct hd_jpeg_err_s // JPEG error manager extension
{
struct jpeg_error_mgr jerr; // JPEG error manager information
jmp_buf retbuf; // setjmp() return buffer
char message[JMSG_LENGTH_MAX];
// Last error message
} hd_jpeg_err_t;
/*
* 'image_load_jpeg()' - Load a JPEG image file.
*/
static int /* O - 0 = success, -1 = fail */
image_load_jpeg(image_t *img, /* I - Image pointer */
FILE *fp, /* I - File to load from */
int gray, /* I - 0 = color, 1 = grayscale */
int load_data)/* I - 1 = load image data, 0 = just info */
{
struct jpeg_decompress_struct cinfo; /* Decompressor info */
hd_jpeg_err_t jerr; // JPEG error handler
JSAMPROW row; /* Sample row pointer */
jpeg_std_error(&jerr.jerr);
jerr.jerr.error_exit = jpeg_error_handler;
if (setjmp(jerr.retbuf))
{
progress_error(HD_ERROR_BAD_FORMAT, "%s (%s)", jerr.message, file_rlookup(img->filename));
jpeg_destroy_decompress(&cinfo);
return (-1);
}
cinfo.err = (struct jpeg_error_mgr *)&jerr;
jpeg_create_decompress(&cinfo);
jpeg_stdio_src(&cinfo, fp);
jpeg_read_header(&cinfo, (boolean)1);
cinfo.quantize_colors = FALSE;
if (gray || cinfo.num_components == 1)
{
cinfo.out_color_space = JCS_GRAYSCALE;
cinfo.out_color_components = 1;
cinfo.output_components = 1;
}
else if (cinfo.num_components != 3)
{
jpeg_destroy_decompress(&cinfo);
progress_error(HD_ERROR_BAD_FORMAT,
"CMYK JPEG files are not supported! (%s)",
file_rlookup(img->filename));
return (-1);
}
else
{
cinfo.out_color_space = JCS_RGB;
cinfo.out_color_components = 3;
cinfo.output_components = 3;
}
jpeg_calc_output_dimensions(&cinfo);
img->width = (int)cinfo.output_width;
img->height = (int)cinfo.output_height;
img->depth = (int)cinfo.output_components;
if (!load_data)
{
jpeg_destroy_decompress(&cinfo);
return (0);
}
img->pixels = (uchar *)malloc((size_t)(img->width * img->height * img->depth));
if (img->pixels == NULL)
{
jpeg_destroy_decompress(&cinfo);
return (-1);
}
jpeg_start_decompress(&cinfo);
while (cinfo.output_scanline < cinfo.output_height)
{
row = (JSAMPROW)(img->pixels + (size_t)cinfo.output_scanline * (size_t)cinfo.output_width * (size_t)cinfo.output_components);
jpeg_read_scanlines(&cinfo, &row, (JDIMENSION)1);
}
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
return (0);
}
#endif // HAVE_LIBJPEG
#ifdef HAVE_LIBPNG
/*
* 'image_load_png()' - Load a PNG image file.
*/
static int /* O - 0 = success, -1 = fail */
image_load_png(image_t *img, /* I - Image pointer */
FILE *fp, /* I - File to read from */
int gray, /* I - 0 = color, 1 = grayscale */
int load_data)/* I - 1 = load image data, 0 = just info */
{
int i, j; /* Looping vars */
png_structp pp; /* PNG read pointer */
png_infop info; /* PNG info pointers */
int depth; /* Input image depth */
png_bytep *rows = NULL; /* PNG row pointers */
uchar *inptr, /* Input pixels */
*outptr; /* Output pixels */
int color_type, /* PNG color mode */
bit_depth; /* PNG bit depth */
/*
* Setup the PNG data structures...
*/
pp = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!pp)
{
progress_error(HD_ERROR_OUT_OF_MEMORY, "Unable to allocate memory for PNG file: %s",
strerror(errno));
return (-1);
}
info = png_create_info_struct(pp);
if (!info)
{
progress_error(HD_ERROR_OUT_OF_MEMORY, "Unable to allocate memory for PNG info: %s",
strerror(errno));
png_destroy_read_struct(&pp, NULL, NULL);
return (-1);
}
if (setjmp(png_jmpbuf(pp)))
{
progress_error(HD_ERROR_BAD_FORMAT, "PNG file contains errors!");
png_destroy_read_struct(&pp, &info, NULL);
if (img != NULL)
{
free(img->pixels);
img->pixels = NULL;
}
free(rows);
rows = NULL;
return (-1);
}
/*
* Initialize the PNG read "engine"...
*/
png_init_io(pp, fp);
# if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
// Don't throw errors with "invalid" sRGB profiles produced by Adobe apps.
png_set_option(pp, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
# endif // PNG_SKIP_sRGB_CHECK_PROFILE && PNG_SET_OPTION_SUPPORTED
/*
* Get the image dimensions and convert to grayscale or RGB...
*/
png_read_info(pp, info);
bit_depth = png_get_bit_depth(pp, info);
color_type = png_get_color_type(pp, info);
if (png_get_valid(pp, info, PNG_INFO_tRNS))
{
png_set_tRNS_to_alpha(pp);
color_type |= PNG_COLOR_MASK_ALPHA;
}
if (color_type & PNG_COLOR_MASK_PALETTE)
{
png_set_palette_to_rgb(pp);
// If we are writing an encrypted PDF file, bump the use count so we create
// an image object (Acrobat 6 bug workaround)
if (Encryption)
img->use ++;
}
else if (!(color_type & PNG_COLOR_MASK_COLOR) && bit_depth < 8)
{
png_set_expand_gray_1_2_4_to_8(pp);
}
else if (bit_depth == 16)
{
# if PNG_LIBPNG_VER >= 10504
png_set_scale_16(pp);
# else
png_set_strip_16(pp);
# endif // PNG_LIBPNG_VER >= 10504
}
if (color_type & PNG_COLOR_MASK_COLOR)
{
depth = 3;
img->depth = gray ? 1 : 3;
}
else
{
depth = 1;
img->depth = 1;
}
img->width = (int)png_get_image_width(pp, info);
img->height = (int)png_get_image_height(pp, info);
if (color_type & PNG_COLOR_MASK_ALPHA)
{
if ((PSLevel == 0 && PDFVersion >= 14) || PSLevel == 3)
image_need_mask(img, 8);
else if (PSLevel == 0 && PDFVersion == 13)
image_need_mask(img, 2);
else
image_need_mask(img);
depth ++;
}
# ifdef DEBUG
printf("bit_depth=%d, color_type=0x%04x, depth=%d, img->width=%d, img->height=%d, img->depth=%d\n", bit_depth, color_type, depth, img->width, img->height, img->depth);
if (color_type & PNG_COLOR_MASK_COLOR)
puts(" COLOR");
else
puts(" GRAYSCALE");
if (color_type & PNG_COLOR_MASK_ALPHA)
puts(" ALPHA");
if (color_type & PNG_COLOR_MASK_PALETTE)
puts(" PALETTE");
# endif // DEBUG
if (!load_data)
{
png_destroy_read_struct(&pp, &info, NULL);
return (0);
}
img->pixels = (uchar *)calloc(1, (size_t)(img->width * img->height * depth));
/*
* Allocate pointers...
*/
rows = (png_bytep *)calloc(png_get_image_height(pp, info), sizeof(png_bytep));
for (i = 0; i < (int)png_get_image_height(pp, info); i ++)
rows[i] = img->pixels + i * img->width * depth;
/*
* Read the image, handling interlacing as needed...
*/
for (i = png_set_interlace_handling(pp); i > 0; i --)
png_read_rows(pp, rows, NULL, (png_uint_32)img->height);
/*
* Generate the alpha mask as necessary...
*/
if (color_type & PNG_COLOR_MASK_ALPHA)
{
# ifdef DEBUG
for (inptr = img->pixels, i = 0; i < img->height; i ++)
{
for (j = 0; j < img->width; j ++, inptr += depth)
switch (depth)
{
case 2 :
printf(" %02X%02X", inptr[0], inptr[1]);
break;
case 4 :
printf(" %02X%02X%02X%02X", inptr[0], inptr[1], inptr[2], inptr[3]);
break;
}
putchar('\n');
}
# endif // DEBUG
for (inptr = img->pixels + depth - 1, i = 0; i < img->height; i ++)
for (j = 0; j < img->width; j ++, inptr += depth)
image_set_mask(img, j, i, *inptr);
}
/*
* Reformat the data as necessary for the reader...
*/
if (gray && (color_type & PNG_COLOR_MASK_COLOR))
{
/*
* Grayscale output needed...
*/
for (inptr = img->pixels, outptr = img->pixels, i = img->width * img->height;
i > 0;
inptr += depth, outptr ++, i --)
*outptr = (31 * inptr[0] + 61 * inptr[1] + 8 * inptr[2]) / 100;
}
else if (img->depth != depth)
{
/*
* Remove alpha from final array...
*/
if (depth == 4)
{
for (inptr = img->pixels, outptr = img->pixels, i = img->width * img->height;
i > 0;
inptr ++, i --)
{
*outptr++ = *inptr++;
*outptr++ = *inptr++;
*outptr++ = *inptr++;
}
}
else
{
for (inptr = img->pixels, outptr = img->pixels, i = img->width * img->height;
i > 0;
inptr ++, i --)
*outptr++ = *inptr++;
}
}
/*
* Free memory and return...
*/
png_read_end(pp, info);
png_destroy_read_struct(&pp, &info, NULL);
free(rows);
return (0);
}
#endif // HAVE_LIBPNG
/*
* 'image_need_mask()' - Allocate memory for the image mask...
*/
static void
image_need_mask(image_t *img, /* I - Image to add mask to */
int scaling) /* I - Scaling for mask image */
{
size_t size; /* Byte size of mask image */
if (img == NULL || img->mask != NULL)
return;
/*
* Figure out the size of the mask image, and then allocate and set all the
* bits needed...
*/
img->maskscale = scaling;
if (scaling == 8)
{
// Alpha image
img->maskwidth = img->width;
size = (size_t)(img->width * img->height);
}
else
{
// Alpha mask
img->maskwidth = (img->width * scaling + 7) / 8;
size = (size_t)(img->maskwidth * img->height * scaling + 1);
}
img->mask = (uchar *)calloc(size, 1);
}
/*
* 'image_set_mask()' - Set a bit in the image mask.
*/
static void
image_set_mask(image_t *img, /* I - Image to operate on */
int x, /* I - X coordinate */
int y, /* I - Y coordinate */
uchar alpha) /* I - Alpha value */
{
int i, j; /* Looping vars */
uchar *maskptr; /* Pointer into mask image */
static uchar masks[8] = /* Masks for each bit */
{
0x80, 0x40, 0x20, 0x10,
0x08, 0x04, 0x02, 0x01
};
static uchar dither[4][4] = // Simple 4x4 clustered-dot dither
{
{ 0, 2, 15, 6 },
{ 4, 12, 9, 11 },
{ 14, 7, 1, 3 },
{ 8, 10, 5, 13 }
};
if (img == NULL || img->mask == NULL || x < 0 || x >= img->width ||
y < 0 || y >= img->height)
return;
if (img->maskscale == 8)
{
// Store the alpha value directly...
if (PSLevel)
img->mask[y * img->maskwidth + x] = 255 - alpha;
else
img->mask[y * img->maskwidth + x] = alpha;
}
else
{
// Store an alpha mask...
x *= img->maskscale;
y *= img->maskscale;
alpha >>= 4;
for (i = 0; i < img->maskscale; i ++, y ++, x -= img->maskscale)
for (j = 0; j < img->maskscale; j ++, x ++)
{
maskptr = img->mask + y * img->maskwidth + x / 8;
if (alpha <= dither[x & 3][y & 3])
*maskptr |= masks[x & 7];
}
}
}
/*
* 'image_unload()' - Unload an image from memory.
*/
void
image_unload(image_t *img) // I - Image
{
if (!img)
return;
if (!img->use || !img->pixels)
return;
if (img->obj)
img->use = 0;
else
img->use --;
if (img->use)
return;
free(img->pixels);
img->pixels = NULL;
}
#ifdef HAVE_LIBJPEG
/*
* 'jpeg_error_handler()' - Handle JPEG errors by not exiting.
*/
static void
jpeg_error_handler(j_common_ptr p) // Common JPEG data
{
hd_jpeg_err_t *jerr = (hd_jpeg_err_t *)p->err;
// JPEG error handler
// Save the error message in the string buffer...
(jerr->jerr.format_message)(p, jerr->message);
// Return to the point we called setjmp()...
longjmp(jerr->retbuf, 1);
}
#endif // HAVE_LIBJPEG
/*
* 'read_word()' - Read a 16-bit unsigned integer.
*/
static unsigned short /* O - 16-bit unsigned integer */
read_word(FILE *fp) /* I - File to read from */
{
unsigned char b0, b1; /* Bytes from file */
b0 = (uchar)getc(fp);
b1 = (uchar)getc(fp);
return (unsigned short)((b1 << 8) | b0);
}
/*
* 'read_dword()' - Read a 32-bit unsigned integer.
*/
static unsigned int /* O - 32-bit unsigned integer */
read_dword(FILE *fp) /* I - File to read from */
{
unsigned char b0, b1, b2, b3; /* Bytes from file */
b0 = (uchar)getc(fp);
b1 = (uchar)getc(fp);
b2 = (uchar)getc(fp);
b3 = (uchar)getc(fp);
return (unsigned)((((((b3 << 8) | b2) << 8) | b1) << 8) | b0);
}
/*
* 'read_long()' - Read a 32-bit signed integer.
*/
static int /* O - 32-bit signed integer */
read_long(FILE *fp) /* I - File to read from */
{
unsigned char b0, b1, b2, b3; /* Bytes from file */
b0 = (uchar)getc(fp);
b1 = (uchar)getc(fp);
b2 = (uchar)getc(fp);
b3 = (uchar)getc(fp);
return ((int)(((((b3 << 8) | b2) << 8) | b1) << 8) | b0);
}