/*---------------------------------------------------------------------------* | 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_gif.c,v 1.2 2009/10/20 18:14:16 scuri Exp $ * * GIF processing for PDFlib * */ /* * This module contains modified parts of the giftopnm.c progam in the * netpbm package. It contained the following copyright notice: */ /* +-------------------------------------------------------------------+ */ /* | Copyright 1990 - 1994, David Koblas. (koblas@netcom.com) | */ /* | Permission to use, copy, modify, and distribute this software | */ /* | and its documentation for any purpose and without fee is hereby | */ /* | granted, provided that the above copyright notice appear in all | */ /* | copies and that both that copyright notice and this permission | */ /* | notice appear in supporting documentation. This software is | */ /* | provided "as is" without express or implied warranty. | */ /* +-------------------------------------------------------------------+ */ #include "p_intern.h" #include "p_color.h" #include "p_image.h" #ifndef PDF_GIF_SUPPORTED pdc_bool pdf_is_GIF_file(PDF *p, pdc_file *fp) { (void) p; (void) fp; return pdc_false; } int pdf_process_GIF_data( PDF *p, int imageslot) { (void) imageslot; pdc_set_errmsg(p->pdc, PDF_E_UNSUPP_IMAGE, "GIF", 0, 0, 0); return -1; } void pdf_cleanup_gif(PDF *p, pdf_image *image) { (void) p; (void) image; } #else #define LOCALCOLORMAP 0x80 #define BitSet(byteval, bitval) (((byteval) & (bitval)) == (bitval)) static int ReadColorMap(pdc_core *pdc, pdc_file *fp, int number, pdf_colormap *buffer); static int DoExtension(PDF *p, pdf_image *image, int label); static int GetDataBlock(PDF *p, pdf_image *image, unsigned char *buf); static void ReadImage(PDF *p, pdf_image *image, PDF_data_source *src); static void pdf_data_source_GIF_init(PDF *p, PDF_data_source *src) { pdf_image *image; image = (pdf_image *) src->private_data; src->buffer_length = (size_t) (image->width * image->height * 1); src->buffer_start = (pdc_byte *) pdc_malloc(p->pdc, src->buffer_length, "pdf_data_source_GIF_init"); src->bytes_available= 0; src->next_byte = NULL; } static pdc_bool pdf_data_source_GIF_fill(PDF *p, PDF_data_source *src) { pdf_image *image; if (src->next_byte != NULL) /* all finished in one turn */ return pdc_false; image = (pdf_image *) src->private_data; src->next_byte = src->buffer_start; src->bytes_available = src->buffer_length; PDC_TRY(p->pdc) { ReadImage(p, image, src); } PDC_CATCH(p->pdc) { image->corrupt = pdc_true; } pdf_cleanup_gif(p, image); return !image->corrupt; } static void pdf_data_source_GIF_terminate(PDF *p, PDF_data_source *src) { pdc_free(p->pdc, (void *) src->buffer_start); } #define PDF_STRING_GIF "\107\111\106" #define PDF_STRING_87a "\070\067\141" #define PDF_STRING_89a "\070\071\141" pdc_bool pdf_is_GIF_file(PDF *p, pdc_file *fp) { unsigned char buf[3]; pdc_logg_cond(p->pdc, 1, trc_image, "\tChecking image type GIF...\n"); if (!PDC_OK_FREAD(fp, buf, 3) || strncmp((const char *) buf, PDF_STRING_GIF, 3) != 0) { pdc_fseek(fp, 0L, SEEK_SET); return pdc_false; } return pdc_true; } int pdf_process_GIF_data( PDF *p, int imageslot) { unsigned char buf[16]; char c; int imageCount = 0; char version[4]; int errcode = 0; pdf_image *image; pdf_colorspace cs; pdf_colormap colormap; int slot; image = &p->images[imageslot]; image->info.gif.stack = NULL; image->info.gif.table = NULL; /* we invert this flag later */ if (image->ignoremask) image->transparent = pdc_true; if (image->page == pdc_undef) image->page = 1; /* Error reading magic number or not a GIF file */ if (pdf_is_GIF_file(p, image->fp) == pdc_false) { errcode = PDC_E_IO_BADFORMAT; goto PDF_GIF_ERROR; } /* Version number */ if (! PDC_OK_FREAD(image->fp, buf, 3)) { errcode = PDC_E_IO_BADFORMAT; goto PDF_GIF_ERROR; } strncpy(version, (const char *) buf, 3); version[3] = '\0'; if ((strcmp(version, PDF_STRING_87a) != 0) && (strcmp(version, PDF_STRING_89a) != 0)) { errcode = PDC_E_IO_BADFORMAT; goto PDF_GIF_ERROR; } /* Failed to read screen descriptor */ if (! PDC_OK_FREAD(image->fp, buf, 7)) { errcode = PDC_E_IO_BADFORMAT; goto PDF_GIF_ERROR; } cs.type = Indexed; /* size of the global color table */ cs.val.indexed.palette_size = 2 << (buf[4] & 0x07); cs.val.indexed.base = DeviceRGB; cs.val.indexed.colormap = &colormap; cs.val.indexed.colormap_id = PDC_BAD_ID; if (BitSet(buf[4], LOCALCOLORMAP)) { /* Global Colormap */ if (ReadColorMap(p->pdc, image->fp, cs.val.indexed.palette_size, &colormap)) { errcode = PDF_E_IMAGE_COLORMAP; goto PDF_GIF_ERROR; } } /* translate the aspect ratio to PDFlib notation */ if (buf[6] != 0) { image->dpi_x = (pdc_scalar) (-(buf[6] + 15.0) / 64.0); image->dpi_y = -1.0; } for (/* */ ; /* */ ; /* */) { /* EOF / read error in image data */ if (!PDC_OK_FREAD(image->fp, &c, 1)) { errcode = PDC_E_IO_READ; goto PDF_GIF_ERROR; } #define PDF_SEMICOLON ((char) 0x3b) /* ASCII ';' */ if (c == PDF_SEMICOLON) { /* GIF terminator */ /* Not enough images found in file */ if (imageCount < image->page) { if (!imageCount) errcode = PDF_E_IMAGE_CORRUPT; else errcode = PDF_E_IMAGE_NOPAGE; goto PDF_GIF_ERROR; } break; } #define PDF_EXCLAM ((char) 0x21) /* ASCII '!' */ if (c == PDF_EXCLAM) { /* Extension */ if (!PDC_OK_FREAD(image->fp, &c, 1)) { /* EOF / read error on extension function code */ errcode = PDC_E_IO_READ; goto PDF_GIF_ERROR; } DoExtension(p, image, (int) c); continue; } #define PDF_COMMA ((char) 0x2c) /* ASCII ',' */ if (c != PDF_COMMA) { /* Not a valid start character */ /* Bogus character, ignoring */ continue; } ++imageCount; if (! PDC_OK_FREAD(image->fp, buf, 9)) { /* Couldn't read left/top/width/height */ errcode = PDC_E_IO_READ; goto PDF_GIF_ERROR; } image->components = 1; image->bpc = 8; image->width = (pdc_scalar) pdc_get_le_ushort(&buf[4]); image->height = (pdc_scalar) pdc_get_le_ushort(&buf[6]); #define INTERLACE 0x40 image->info.gif.interlace= BitSet(buf[8], INTERLACE); if (image->imagemask) { if (p->compatibility <= PDC_1_3) { errcode = PDF_E_IMAGE_MASK1BIT13; goto PDF_GIF_ERROR; } else { /* 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; } if (BitSet(buf[8], LOCALCOLORMAP)) { /* The local color map may have a different size */ cs.val.indexed.palette_size = 2 << (buf[8] & 0x07); if (ReadColorMap(p->pdc, image->fp, cs.val.indexed.palette_size, &colormap)) { errcode = PDF_E_IMAGE_COLORMAP; goto PDF_GIF_ERROR; } } if (imageCount == image->page) break; } image->src.init = pdf_data_source_GIF_init; image->src.fill = pdf_data_source_GIF_fill; image->src.terminate = pdf_data_source_GIF_terminate; image->src.private_data = (void *) image; image->compression = pdf_comp_none; image->use_raw = pdc_false; image->in_use = pdc_true; /* mark slot as used */ slot = pdf_add_colorspace(p, &cs, pdc_false); image->colorspace = slot; pdf_put_image(p, imageslot, pdc_true, pdc_true); if (!image->corrupt) return imageslot; PDF_GIF_ERROR: { const char *stemp = NULL; if (errcode) stemp = pdf_get_image_filename(p, image); switch (errcode) { case PDC_E_IO_READ: case PDF_E_IMAGE_COLORMAP: case PDF_E_IMAGE_MASK1BIT13: pdc_set_errmsg(p->pdc, errcode, stemp, 0, 0, 0); break; case PDC_E_IO_BADFORMAT: pdc_set_errmsg(p->pdc, errcode, stemp, "GIF", 0, 0); break; case PDF_E_IMAGE_CORRUPT: pdc_set_errmsg(p->pdc, errcode, "GIF", stemp, 0, 0); break; case PDF_E_IMAGE_NOPAGE: pdc_set_errmsg(p->pdc, errcode, pdc_errprintf(p->pdc, "%d", image->page), "GIF", stemp, 0); break; case 0: /* error code and message already set */ break; } } return -1; } /* pdf_open_GIF_data */ static int ReadColorMap(pdc_core *pdc, pdc_file *fp, int number, pdf_colormap *buffer) { int i; unsigned char rgb[3]; (void) pdc; for (i = 0; i < number; ++i) { if (! PDC_OK_FREAD(fp, rgb, sizeof(rgb))) { return pdc_true; /* yk: true == error */ } (*buffer)[i][0] = rgb[0] ; (*buffer)[i][1] = rgb[1] ; (*buffer)[i][2] = rgb[2] ; } return pdc_false; /* yk: false == ok. */ } /* ReadColorMap */ static int DoExtension(PDF *p, pdf_image *image, int label) { pdc_byte buf[256]; switch ((unsigned char) label) { case 0x01: /* Plain Text Extension */ break; case 0xff: /* Application Extension */ break; case 0xfe: /* Comment Extension */ while (GetDataBlock(p, image, (unsigned char*) buf) != 0) { /* */ } return pdc_false; case 0xf9: /* Graphic Control Extension */ (void) GetDataBlock(p, image, (unsigned char*) buf); if ((buf[0] & 0x1) != 0) { image->transparent = !image->transparent; image->transval[0] = buf[3]; } while (GetDataBlock(p, image, (unsigned char*) buf) != 0) { /* */ ; } return pdc_false; default: break; } while (GetDataBlock(p, image, (unsigned char*) buf) != 0) { /* */ ; } return pdc_false; } /* DoExtension */ /* * A bunch of formely static variables which are now kept in the * image structure in order to keep the GIF reader thread-safe. */ /* for GetDataBlock() */ #define ZeroDataBlock (image->info.gif.ZeroDataBlock) /* for initLWZ() */ #define curbit (image->info.gif.curbit) #define lastbit (image->info.gif.lastbit) #define get_done (image->info.gif.get_done) #define last_byte (image->info.gif.last_byte) #define return_clear (image->info.gif.return_clear) #define sp (image->info.gif.sp) #define code_size (image->info.gif.code_size) #define set_code_size (image->info.gif.set_code_size) #define max_code (image->info.gif.max_code) #define max_code_size (image->info.gif.max_code_size) #define clear_code (image->info.gif.clear_code) #define end_code (image->info.gif.end_code) /* for nextCode() */ #define buf (image->info.gif.buf) /* for nextLWZ() */ #define stack (image->info.gif.stack) #define table (image->info.gif.table) #define firstcode (image->info.gif.firstcode) #define oldcode (image->info.gif.oldcode) static int GetDataBlock(PDF *p, pdf_image *image, unsigned char *lbuf) { unsigned char count; pdc_file *fp = image->fp; if (!PDC_OK_FREAD(fp, &count, 1)) return -1; /* Error in getting DataBlock size */ ZeroDataBlock = (count == (unsigned char) 0); if ((count != (unsigned char) 0) && (!PDC_OK_FREAD(fp, lbuf, count))) { /* Error in reading DataBlock */ pdc_error(p->pdc, PDF_E_IMAGE_CORRUPT, "GIF", pdf_get_image_filename(p, image), 0, 0); } return count; } /* GetDataBlock */ static void initLWZ(PDF *p, pdf_image *image, int input_code_size) { #define GIF_TABLE_SIZE (sizeof(int [2][GIF_TABLE_ELEMENTS])) #define GIF_STACK_SIZE (sizeof(int [GIF_TABLE_ELEMENTS*2])) table = (int(*)[GIF_TABLE_ELEMENTS]) pdc_malloc(p->pdc, GIF_TABLE_SIZE, "initLWZ"); stack = (int *) pdc_malloc(p->pdc, GIF_STACK_SIZE, "initLWZ"); set_code_size = input_code_size; code_size = set_code_size + 1; clear_code = 1 << set_code_size ; end_code = clear_code + 1; max_code_size = 2 * clear_code; max_code = clear_code + 2; curbit = lastbit = 0; last_byte = 2; get_done = pdc_false; return_clear = pdc_true; sp = stack; } /* * We clean up after decompressing the image; in rare cases (exception * caused by damaged compressed data) this may also be called when * cleaning up the full image struct. */ void pdf_cleanup_gif(PDF *p, pdf_image *image) { if (table) { pdc_free(p->pdc, table); table = NULL; } if (stack) { pdc_free(p->pdc, stack); stack = NULL; } } static int nextCode(PDF *p, pdf_image *image, int codesize) { static const int maskTbl[16] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, }; int i, j, ret, end; if (return_clear) { return_clear = pdc_false; return clear_code; } end = curbit + codesize; if (end >= lastbit) { int count; if (get_done) { if (curbit >= lastbit) { /* ERROR("ran off the end of my bits" ); */ pdc_error(p->pdc, PDF_E_IMAGE_CORRUPT, "GIF", pdf_get_image_filename(p, image), 0, 0); } return -1; } if (last_byte >= 2) { buf[0] = buf[last_byte-2]; buf[1] = buf[last_byte-1]; } if ((count = GetDataBlock(p, image, &buf[2])) == 0) get_done = pdc_true; last_byte = 2 + count; curbit = (curbit - lastbit) + 16; lastbit = (2+count)*8 ; end = curbit + codesize; } j = end / 8; i = curbit / 8; if (i == j) ret = buf[i]; else if (i + 1 == j) ret = buf[i] | (buf[i+1] << 8); else ret = buf[i] | (buf[i+1] << 8) | (buf[i+2] << 16); ret = (ret >> (curbit % 8)) & maskTbl[codesize]; curbit += codesize; return ret; } #define readLWZ(p, image) ((sp > stack) ? *--sp : nextLWZ(p, image)) static int nextLWZ(PDF *p, pdf_image *image) { int code, incode; int i; while ((code = nextCode(p, image, code_size)) >= 0) { if (code == clear_code) { for (i = 0; i < clear_code; ++i) { table[0][i] = 0; table[1][i] = i; } for (; i < (1< 0) ; /* count != 0: INFO_MSG(("missing EOD in data stream")); */ 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]) { /* ERROR("circular table entry BIG ERROR"); */ pdc_error(p->pdc, PDF_E_IMAGE_CORRUPT, "GIF", pdf_get_image_filename(p, image), 0, 0); } code = table[0][code]; } *sp++ = firstcode = table[1][code]; if ((code = max_code) <(1<= max_code_size) && (max_code_size < (1< stack) return *--sp; } return code; } static void ReadImage(PDF *p, pdf_image *image, PDF_data_source *src) { unsigned char c; int v; unsigned int xpos = 0, ypos = 0; pdc_byte *dp; unsigned int h = (unsigned int) image->height; unsigned int w = (unsigned int) image->width; /* * Initialize the Compression routines */ ZeroDataBlock = pdc_false; if (!PDC_OK_FREAD(image->fp, &c, 1)) { pdc_error(p->pdc, PDF_E_IMAGE_CORRUPT, "GIF", pdf_get_image_filename(p, image), 0, 0); } initLWZ(p, image, c); if (image->info.gif.interlace) { int i; int pass = 0, step = 8; for (i = 0; i < (int) h; i++) { dp = &src->buffer_start[w * ypos]; for (xpos = 0; xpos < w; xpos++) { if ((v = readLWZ(p, image)) < 0) goto fini; *dp++ = v; } if ((ypos += step) >= h) { do { if (pass++ > 0) step /= 2; ypos = step / 2; } while (ypos > h); } } } else { dp = src->buffer_start; for (ypos = 0; ypos < h; ypos++) { for (xpos = 0; xpos < w; xpos++) { if ((v = readLWZ(p, image)) < 0) goto fini; *dp++ = v; } } } fini: if (readLWZ(p, image) >= 0) /* Too much input data in GIF file '%s', ignoring extra. */ ; } #undef fresh #undef code_size #undef set_code_size #undef max_code #undef max_code_size #undef firstcode #undef oldcode #undef clear_code #undef end_code #undef sp #undef table #undef stack #endif /* PDF_GIF_SUPPORTED */