Initial GIF support (Issue #145)

This commit is contained in:
Michael R Sweet
2026-01-18 10:31:50 -05:00
parent 4e9ec397f1
commit b3aaf2e70f
15 changed files with 763 additions and 22 deletions

View File

@@ -201,7 +201,7 @@ install: $(TARGETS)
# Test everything
test: testpdfio
./testpdfio 2>>test.log
./testpdfio 2>test.log
LANG=fr_FR.UTF-8 ./testpdfio 2>>test.log

View File

@@ -1,7 +1,7 @@
//
// Content helper functions for PDFio.
//
// Copyright © 2021-2025 by Michael R Sweet.
// Copyright © 2021-2026 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
@@ -25,6 +25,10 @@
// Local constants...
//
#define _PDFIO_GIF_COLORBITS 0x07 // Color bits
#define _PDFIO_GIF_INTERLACE 0x40 // Interlace flag
#define _PDFIO_GIF_COLORMAP 0x80 // Colormap present flag
#define _PDFIO_JPEG_SOF0 0xc0 // Start of frame (0)
#define _PDFIO_JPEG_SOF1 0xc1 // Start of frame (1)
#define _PDFIO_JPEG_SOF2 0xc2 // Start of frame (2)
@@ -75,6 +79,8 @@
// Local types...
//
typedef uint8_t _pdfio_gif_cmap_t[256][3];
typedef pdfio_obj_t *(*_pdfio_image_func_t)(pdfio_dict_t *dict, int fd);
@@ -82,19 +88,27 @@ typedef pdfio_obj_t *(*_pdfio_image_func_t)(pdfio_dict_t *dict, int fd);
// Local functions...
//
static pdfio_obj_t *copy_gif(pdfio_dict_t *dict, int fd);
static pdfio_obj_t *copy_jpeg(pdfio_dict_t *dict, int fd);
static pdfio_obj_t *copy_png(pdfio_dict_t *dict, int fd);
static bool create_cp1252(pdfio_file_t *pdf);
static pdfio_obj_t *create_font(pdfio_obj_t *file_obj, ttf_t *font, bool unicode);
static pdfio_obj_t *create_image(pdfio_dict_t *dict, const unsigned char *data, size_t width, size_t height, size_t depth, size_t num_colors, bool alpha);
static ssize_t gif_get_block(pdfio_file_t *pdf, int fd, uint8_t *buffer, size_t bufsize);
static bool gif_read_image(pdfio_file_t *pdf, int fd, uint8_t *data, size_t width, size_t height, int *ncolors, _pdfio_gif_cmap_t cmap);
#ifdef HAVE_LIBPNG
static void png_error_func(png_structp pp, png_const_charp message);
static void png_read_func(png_structp png_ptr, png_bytep data, size_t length);
#endif // HAVE_LIBPNG
static void ttf_error_cb(pdfio_file_t *pdf, const char *message);
#ifndef HAVE_LIBPNG
static unsigned update_png_crc(unsigned crc, const unsigned char *buffer, size_t length);
#endif // !HAVE_LIBPNG
static bool write_array(pdfio_stream_t *st, pdfio_array_t *a);
static bool write_dict(pdfio_stream_t *st, pdfio_dict_t *dict);
static bool write_string(pdfio_stream_t *st, bool unicode, const char *s, bool *newline);
@@ -2174,7 +2188,12 @@ pdfioFileCreateImageObjFromFile(
PDFIO_DEBUG("pdfioFileCreateImageObjFromFile: buffer=<%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X>\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], buffer[8], buffer[9], buffer[10], buffer[11], buffer[12], buffer[13], buffer[14], buffer[15], buffer[16], buffer[17], buffer[18], buffer[19], buffer[20], buffer[21], buffer[22], buffer[23], buffer[24], buffer[25], buffer[26], buffer[27], buffer[28], buffer[29], buffer[30], buffer[31]);
if (!memcmp(buffer, "\211PNG\015\012\032\012\000\000\000\015IHDR", 16))
if (!memcmp(buffer, "GIF87a", 6) || !memcmp(buffer, "GIF89a", 6))
{
// GIF image...
copy_func = copy_gif;
}
else if (!memcmp(buffer, "\211PNG\015\012\032\012\000\000\000\015IHDR", 16))
{
// PNG image...
copy_func = copy_png;
@@ -2456,6 +2475,155 @@ pdfioPageDictAddImage(
}
/*
* 'copy_gif()' - Copy a GIF image file.
*/
static pdfio_obj_t * // O - Image object or `NULL` on error
copy_gif(pdfio_dict_t *dict, // I - Image dictionary
int fd) // I - File to read from
{
pdfio_obj_t *image_obj = NULL;
// Image object
uint8_t header[13]; // Image header
_pdfio_gif_cmap_t cmap; // Colormap
int ncolors, // Number of colors/bits per pixel
transparent; // Transparent color index
size_t width, // Width of image
height; // Height of image
uint8_t *data = NULL; // Image data
size_t datasize; // Size of image data
uint8_t desc, // Descriptor header
extdesc; // Extension descriptor
uint8_t block[256]; // Extension block
ssize_t blocklen; // Length of block
// Read the header; we already know it is a GIF file...
if (read(fd, header, sizeof(header)) < sizeof(header))
{
_pdfioFileError(dict->pdf, "Unable to read GIF header: %s", strerror(errno));
return (NULL);
}
PDFIO_DEBUG("copy_gif: header=<%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X>\n", header[0], header[1], header[2], header[3], header[4], header[5], header[6], header[7], header[8], header[9], header[10], header[11], header[12]);
width = (header[7] << 8) | header[6];
height = (header[9] << 8) | header[8];
ncolors = 2 << (header[10] & _PDFIO_GIF_COLORBITS);
PDFIO_DEBUG("copy_gif: width=%u, height=%u, ncolors=%d\n", (unsigned)width, (unsigned)height, ncolors);
if (width < 1 || width > 16384 || height < 1 || height > 16384)
{
_pdfioFileError(dict->pdf, "Unsupported image dimensions %ux%ux%d.", (unsigned)width, (unsigned)height, ncolors);
return (NULL);
}
if (header[10] & _PDFIO_GIF_COLORMAP)
{
// Read colormap after the file header...
PDFIO_DEBUG("copy_gif: Have colormap.\n");
if (read(fd, cmap, ncolors * 3) < (ncolors * 3))
{
_pdfioFileError(dict->pdf, "Unable to read GIF colormap: %s", strerror(errno));
return (NULL);
}
}
else
{
// Make a grayscale colormap for the number of colors...
PDFIO_DEBUG("copy_gif: Using default grayscale colormap.\n");
for (int i = 0; i < ncolors; i ++)
cmap[i][0] = cmap[i][1] = cmap[i][2] = 255 * i / (ncolors - 1);
}
#if DEBUG > 1
for (int i = 0; i < ncolors; i ++)
PDFIO_DEBUG("copy_gif: cmap[%d]={%4d,%4d,%4d}\n", i, cmap[i][0], cmap[i][1], cmap[i][2]);
#endif // DEBUG > 1
transparent = -1;
// Allocate memory for the image and clear it to the background color...
datasize = width * height;
if ((data = malloc(datasize)) == NULL)
{
_pdfioFileError(dict->pdf, "Unable to allocate memory for GIF image: %s", strerror(errno));
return (NULL);
}
memset(data, header[11], datasize);
// Read the image...
while (read(fd, &desc, 1) > 0)
{
PDFIO_DEBUG("copy_gif: desc=%02X('%c')\n", desc, desc);
switch (desc)
{
case ';' : // End of image
pdfioDictSetNumber(dict, "Width", width);
pdfioDictSetNumber(dict, "Height", height);
pdfioDictSetNumber(dict, "BitsPerComponent", 8);
pdfioDictSetArray(dict, "ColorSpace", pdfioArrayCreateColorFromPalette(dict->pdf, ncolors, cmap[0]));
if (transparent >= 0)
{
pdfio_array_t *mask = pdfioArrayCreate(dict->pdf);
pdfioArrayAppendNumber(mask, transparent);
pdfioArrayAppendNumber(mask, transparent);
pdfioDictSetArray(dict, "Mask", mask);
}
image_obj = create_image(dict, data, width, height, 8, 1, /*alpha*/false);
goto done;
case '!' : // Extension record
if (read(fd, &extdesc, 1) < 1)
{
_pdfioFileError(dict->pdf, "Unable to read extension descriptor.");
goto done;
}
if (extdesc == 0xf9) // Graphic Control Extension
{
if ((blocklen = gif_get_block(dict->pdf, fd, block, sizeof(block))) < 4)
goto done;
if (block[0] & 1) // Get transparent color index
{
transparent = block[3];
PDFIO_DEBUG("copy_gif: transparent=%d\n", transparent);
}
}
do
{
}
while ((blocklen = gif_get_block(dict->pdf, fd, block, sizeof(block))) > 0);
if (blocklen < 0)
goto done;
break;
case ',' : // Image data
if (!gif_read_image(dict->pdf, fd, data, width, height, &ncolors, cmap))
goto done;
break;
}
}
done:
free(data);
return (image_obj);
}
//
// 'copy_jpeg()' - Copy a JPEG image.
//
@@ -3819,6 +3987,225 @@ create_image(
}
//
// 'gif_get_block()' - Read a GIF data block...
//
static ssize_t // O - Number characters read
gif_get_block(pdfio_file_t *pdf, // I - PDF file
int fd, // I - File to read from
uint8_t *buffer, // I - Input buffer (at least 256 bytes)
size_t bufsize) // I - Size of buffer
{
uint8_t count; // Number of bytes to read
// Make sure the buffer is sufficiently large...
if (bufsize < 256)
{
_pdfioFileError(pdf, "GIF block buffer too small.");
return (-1);
}
// Read the count byte followed by the data from the file...
if (read(fd, &count, 1) < 1)
{
_pdfioFileError(pdf, "Unable to read GIF block length.");
return (-1);
}
PDFIO_DEBUG("gif_get_block: count=%u\n", count);
if (count > 0 && read(fd, buffer, count) < count)
{
_pdfioFileError(pdf, "Unable to read GIF block data.");
return (-1);
}
return (count);
}
//
// 'gif_read_image()' - Read a GIF image stream...
//
static bool // I - `true` on success, `false` on failure
gif_read_image(
pdfio_file_t *pdf, // I - PDF file
int fd, // I - Input file
uint8_t *data, // I - Indexed image data
size_t width, // I - Width of image
size_t height, // I - Height of image
int *ncolors, // IO - Number of colors
_pdfio_gif_cmap_t cmap) // I - Colormap
{
bool ret = false; // Return value
uint8_t code_size; // Code size
_pdfio_lzw_t *lzw; // LZW decompressor state
uint8_t block[256]; // Data block from GIF file
ssize_t blocklen; // Length of data block
uint8_t pixels[1024], // Temporary pixel buffer
*pixptr = pixels, // Pointer into pixel buffer
*pixend = pixels; // End of pixel buffer
uint8_t *dataptr; // Current pixel in image
uint8_t iheader[9]; // Image header
size_t ileft, // Left position
itop, // Top position
iwidth, // Width
iheight; // Height
bool interlace; // Interlaced image?
size_t xpos = 0, // Current X position
ypos = 0, // Current Y position
pass = 0, // Current pass
remaining = width * height;
// Remaining pixels
bool saw_endblock = false; // Have we seen the 0-length block?
static size_t xpasses[4] = { 8, 8, 4, 2 },
ypasses[5] = { 0, 4, 2, 1, 0 };
if (read(fd, iheader, sizeof(iheader)) < sizeof(iheader))
{
_pdfioFileError(pdf, "Unable to read GIF image header.");
return (false);
}
PDFIO_DEBUG("gif_read_image: iheader=<%02X%02X%02X%02X%02X%02X%02X%02X%02X>\n", iheader[0], iheader[1], iheader[2], iheader[3], iheader[4], iheader[5], iheader[6], iheader[7], iheader[8]);
ileft = iheader[0] | (iheader[1] << 8);
itop = iheader[2] | (iheader[3] << 8);
iwidth = iheader[4] | (iheader[5] << 8);
iheight = iheader[6] | (iheader[7] << 8);
interlace = (iheader[8] & _PDFIO_GIF_INTERLACE) != 0;
PDFIO_DEBUG("gif_read_image: ileft=%u, itop=%u, iwidth=%u, iheight=%u, interlace=%s\n", (unsigned)ileft, (unsigned)itop, (unsigned)iwidth, (unsigned)iheight, interlace ? "true" : "false");
if ((ileft + iwidth) > width || (itop + iheight) > height)
{
_pdfioFileError(pdf, "Invalid GIF image %ux%u offset %ux%u.", (unsigned)iwidth, (unsigned)iheight, (unsigned)ileft, (unsigned)itop);
return (false);
}
iwidth += ileft;
iheight += itop;
if (iheader[8] & _PDFIO_GIF_COLORMAP)
{
// Local image colormap...
int ncolors2 = 2 << (iheader[8] & _PDFIO_GIF_COLORBITS);
PDFIO_DEBUG("gif_read_image: Colormap with %d colors.\n", ncolors2);
if (read(fd, cmap, 3 * ncolors2) < (3 * ncolors2))
{
_pdfioFileError(pdf, "Unable to read GIF image colormap.");
return (false);
}
if (ncolors2 > *ncolors)
*ncolors = ncolors2;
}
xpos = ileft;
ypos = itop;
pass = 0;
code_size = 0;
if (read(fd, &code_size, 1) < 1 || code_size < 2 || code_size > 12)
{
_pdfioFileError(pdf, "Invalid code size %u in GIF file.", code_size);
return (false);
}
PDFIO_DEBUG("gif_read_image: code_size=%u\n", code_size);
if ((lzw = _pdfioLZWCreate(code_size, /*early*/0, /*reversed*/true)) == NULL)
{
_pdfioFileError(pdf, "Unable to create GIF decompressor for code size %u.", code_size);
return (false);
}
// TODO: Support 1-bit and 4-bit per output data; current code is 8-bit only
dataptr = data + ypos * width + xpos;
while (remaining > 0)
{
// Fill the pixel buffer as needed...
while (pixptr >= pixend)
{
// Fill input buffer as needed...
if (lzw->avail_in == 0 && !saw_endblock)
{
blocklen = gif_get_block(pdf, fd, block, sizeof(block));
saw_endblock = blocklen <= 0;
if (blocklen < 0)
goto done;
lzw->avail_in = (size_t)blocklen;
lzw->next_in = block;
}
lzw->avail_out = sizeof(pixels);
lzw->next_out = pixels;
if (!_pdfioLZWInflate(lzw))
{
_pdfioFileError(pdf, "Unable to decompress GIF image: %s", lzw->error);
goto done;
}
pixptr = pixels;
pixend = pixels + sizeof(pixels) - lzw->avail_out;
}
// Add this pixel and advance to the next one...
*dataptr++ = *pixptr++;
remaining --;
xpos ++;
if (xpos >= width)
{
// New line...
xpos = ileft;
if (interlace)
{
// Skip lines when the image is interlaced...
ypos += xpasses[pass];
if (ypos >= height)
{
pass ++;
ypos = ypasses[pass] + itop;
}
}
else
{
// No interlacing, move to next line...
ypos ++;
}
dataptr = data + ypos * width + xpos;
}
}
ret = true;
done:
// Read any remaining blocks...
while (!saw_endblock)
saw_endblock = gif_get_block(pdf, fd, block, sizeof(block)) <= 0;
// Free the LZW decoder state and return...
_pdfioLZWDelete(lzw);
return (ret);
}
#ifdef HAVE_LIBPNG
//
// 'png_error_func()' - PNG error message function.

View File

@@ -28,8 +28,9 @@ static int lzw_get_code(_pdfio_lzw_t *lzw);
//
_pdfio_lzw_t * // O - LZW state
_pdfioLZWCreate(int code_size, // I - Data code size in bits (typically 8 for PDF, 2-8 for GIF)
int early) // I - Number of early codes
_pdfioLZWCreate(int code_size, // I - Data code size in bits (typically 8 for PDF, 2-8 for GIF)
int early, // I - Number of early codes
bool reversed) // I - Reversed (GIF) LZW bit encoding?
{
_pdfio_lzw_t *lzw; // LZW state
@@ -40,6 +41,7 @@ _pdfioLZWCreate(int code_size, // I - Data code size in bits (typically 8 for P
lzw->clear_code = (short)(1 << code_size);
lzw->eod_code = lzw->clear_code + 1;
lzw->early = early;
lzw->reversed = reversed;
lzw_clear(lzw);
}
@@ -244,6 +246,10 @@ lzw_get_code(_pdfio_lzw_t *lzw) // I - LZW state
{
0xff, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f
};
static uint8_t rbits[8] = // Right-to-left bit masks
{
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
};
// Fill input bytes as needed...
@@ -290,24 +296,36 @@ lzw_get_code(_pdfio_lzw_t *lzw) // I - LZW state
PDFIO_DEBUG("lzw_get_code: in_bit=%u, in_bits=%u, in_bytes=<...%02X%02X%02X...>, cur_code_size=%u\n", lzw->in_bit, lzw->in_bits, lzw->in_bytes[lzw->in_bit / 8], lzw->in_bytes[lzw->in_bit / 8 + 1], lzw->in_bytes[lzw->in_bit / 8 + 2], lzw->cur_code_size);
// Now extract the code from the buffer...
for (code = 0, in_bit = lzw->in_bit, remaining = lzw->cur_code_size; remaining > 0; in_bit += bits, remaining -= bits)
if (lzw->reversed)
{
// See how many bits we can extract from the current byte...
boff = (in_bit & 7);
byte = lzw->in_bytes[in_bit / 8];
bits = 8 - boff;
if (bits > remaining)
bits = remaining;
// Insane GIF-style right-to-left bit encoding...
for (code = 0, in_bit = lzw->in_bit + lzw->cur_code_size - 1, remaining = lzw->cur_code_size; remaining > 0; in_bit --, remaining --)
{
code = (code << 1) | ((lzw->in_bytes[in_bit / 8] & rbits[in_bit & 7]) != 0);
}
}
else
{
// TIFF/PDF-style left-to-right bit encoding...
for (code = 0, in_bit = lzw->in_bit, remaining = lzw->cur_code_size; remaining > 0; in_bit += bits, remaining -= bits)
{
// See how many bits we can extract from the current byte...
boff = (in_bit & 7);
byte = lzw->in_bytes[in_bit / 8];
bits = 8 - boff;
if (bits > remaining)
bits = remaining;
// Get those bits
if (bits == 8) // Full byte from buffer
code = (code << 8) | byte;
else // Partial byte from buffer
code = (code << bits) | ((byte >> (8 - bits - boff)) & mask[bits]);
// Get those bits
if (bits == 8) // Full byte from buffer
code = (code << 8) | byte;
else // Partial byte from buffer
code = (code << bits) | ((byte >> (8 - bits - boff)) & mask[bits]);
}
}
// Save the updated position in the input buffer and return the code...
lzw->in_bit = in_bit;
// Update the position in the input buffer and return the code...
lzw->in_bit += lzw->cur_code_size;
#ifdef DEBUG
if (code >= 0x20 && code < 0x7f)

View File

@@ -229,6 +229,7 @@ typedef struct _pdfio_lzw_s // LZW state
uint8_t cur_code_size, // Current code size
def_code_size, // Initial/default code size
early; // Early code change offset
bool reversed; // Reversed bit encoding?
uint16_t clear_code, // Clear code
eod_code, // End code
next_code, // Next code to be used
@@ -452,7 +453,7 @@ extern off_t _pdfioFileSeek(pdfio_file_t *pdf, off_t offset, int whence) _PDFIO
extern off_t _pdfioFileTell(pdfio_file_t *pdf) _PDFIO_INTERNAL;
extern bool _pdfioFileWrite(pdfio_file_t *pdf, const void *buffer, size_t bytes) _PDFIO_INTERNAL;
extern _pdfio_lzw_t *_pdfioLZWCreate(int def_code_size, int early) _PDFIO_INTERNAL;
extern _pdfio_lzw_t *_pdfioLZWCreate(int def_code_size, int early, bool reversed) _PDFIO_INTERNAL;
extern void _pdfioLZWDelete(_pdfio_lzw_t *lzw) _PDFIO_INTERNAL;
extern bool _pdfioLZWInflate(_pdfio_lzw_t *lzw) _PDFIO_INTERNAL;

View File

@@ -652,7 +652,7 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
}
}
if ((st->lzw = _pdfioLZWCreate(/*code_size*/8, early)) == NULL)
if ((st->lzw = _pdfioLZWCreate(/*code_size*/8, early, /*reversed*/false)) == NULL)
{
_pdfioFileError(st->pdf, "Unable to initialize LZWDecode filter: %s", strerror(errno));
goto error;

BIN
testfiles/color.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

BIN
testfiles/gray.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
testfiles/pdfio-1bit.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

BIN
testfiles/pdfio-2bit.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
testfiles/pdfio-4bit-t.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
testfiles/pdfio-4bit.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
testfiles/pdfio-8bit-a.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
testfiles/pdfio-8bit-t.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
testfiles/pdfio-8bit.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -51,6 +51,7 @@ static int write_alpha_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font);
static int write_color_patch(pdfio_stream_t *st, bool device);
static int write_color_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font);
static int write_font_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font, const char *textfontfile, bool unicode);
static int write_gif_tests(pdfio_file_t *pdf, int number, pdfio_obj_t *font);
static int write_header_footer(pdfio_stream_t *st, const char *title, int number);
static pdfio_obj_t *write_image_object(pdfio_file_t *pdf, _pdfio_predictor_t predictor);
static int write_images_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font);
@@ -411,7 +412,7 @@ do_lzw_tests(void)
testBegin("_pdfioLZWCreate(8)");
testEnd((lzw = _pdfioLZWCreate(/*code_size*/8, /*early*/1)) != NULL);
testEnd((lzw = _pdfioLZWCreate(/*code_size*/8, /*early*/1, /*reversed*/false)) != NULL);
if (!lzw)
return (1);
@@ -1292,6 +1293,7 @@ do_unit_tests(void)
if (read_unit_file("testpdfio-out.pdf", num_pages, first_image, false))
goto fail;
#if 0
// Stream a new PDF file...
if ((outfd = open("testpdfio-out2.pdf", O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0666)) < 0)
{
@@ -1449,6 +1451,7 @@ do_unit_tests(void)
// Do PDF/A tests...
if (do_pdfa_tests())
return (1);
#endif // 0
return (0);
@@ -3159,6 +3162,332 @@ write_font_test(
}
//
// 'write_gif_tests()' - Write pages of GIF test images.
//
static int // O - 0 on success, 1 on failure
write_gif_tests(pdfio_file_t *pdf, // I - PDF file
int number, // I - Page number
pdfio_obj_t *font) // I - Page number font
{
pdfio_dict_t *dict; // Page dictionary
pdfio_stream_t *st; // Page contents stream
size_t i; // Looping var
pdfio_obj_t *color, // color.gif
*gray, // gray.gif
*pdfio[7]; // pdfio-*.gif
char imgname[32]; // Image name
double x, xt, // X positions
y; // Y position
static const char * const pdfio_files[7] =
{ // PDFIO GIF test filenames
"testfiles/pdfio-1bit.gif",
"testfiles/pdfio-2bit.gif",
"testfiles/pdfio-4bit.gif",
"testfiles/pdfio-4bit-t.gif",
"testfiles/pdfio-8bit.gif",
"testfiles/pdfio-8bit-a.gif",
"testfiles/pdfio-8bit-t.gif"
};
static const char * const pdfio_labels[7] =
{ // PDFIO GIF test labels
"pdfio-1bit",
"pdfio-2bit",
"pdfio-4bit",
"pdfio-4bit-t",
"pdfio-8bit",
"pdfio-8bit-a",
"pdfio-8bit-t"
};
// Import the GIF test images
testBegin("pdfioFileCreateImageObjFromFile(\"testfiles/color.gif\")");
if ((color = pdfioFileCreateImageObjFromFile(pdf, "testfiles/color.gif", false)) != NULL)
{
testEnd(true);
}
else
{
testEnd(false);
return (1);
}
testBegin("pdfioFileCreateImageObjFromFile(\"testfiles/gray.gif\")");
if ((gray = pdfioFileCreateImageObjFromFile(pdf, "testfiles/gray.gif", false)) != NULL)
{
testEnd(true);
}
else
{
testEnd(false);
return (1);
}
for (i = 0; i < (sizeof(pdfio_files) / sizeof(pdfio_files[0])); i ++)
{
testBegin("pdfioFileCreateImageObjFromFile(\"%s\")", pdfio_files[i]);
if ((pdfio[i] = pdfioFileCreateImageObjFromFile(pdf, pdfio_files[i], false)) != NULL)
{
testEnd(true);
}
else
{
testEnd(false);
return (1);
}
}
// Create the page dictionary, object, and stream...
testBegin("pdfioDictCreate");
if ((dict = pdfioDictCreate(pdf)) != NULL)
{
testEnd(true);
}
else
{
testEnd(false);
return (1);
}
testBegin("pdfioPageDictAddImage(color)");
if (pdfioPageDictAddImage(dict, "IM1", color))
{
testEnd(true);
}
else
{
testEnd(false);
return (1);
}
testBegin("pdfioPageDictAddImage(gray)");
if (pdfioPageDictAddImage(dict, "IM2", gray))
{
testEnd(true);
}
else
{
testEnd(false);
return (1);
}
for (i = 0; i < (sizeof(pdfio_files) / sizeof(pdfio_files[0])); i ++)
{
testBegin("pdfioPageDictAddImage(\"%s\")", pdfio_labels[i]);
snprintf(imgname, sizeof(imgname), "IM%u", (unsigned)(i + 11));
if (pdfioPageDictAddImage(dict, pdfioStringCreate(pdf, imgname), pdfio[i]))
{
testEnd(true);
}
else
{
testEnd(false);
return (1);
}
}
testBegin("pdfioPageDictAddFont(F1)");
if (pdfioPageDictAddFont(dict, "F1", font))
{
testEnd(true);
}
else
{
testEnd(false);
return (1);
}
testBegin("pdfioFileCreatePage(%d)", number);
if ((st = pdfioFileCreatePage(pdf, dict)) != NULL)
{
testEnd(true);
}
else
{
testEnd(false);
return (1);
}
if (write_header_footer(st, "GIF Image Test Page", number))
goto error;
// Show content...
testBegin("pdfioContentSetTextFont(\"F1\", 18.0)");
if (pdfioContentSetTextFont(st, "F1", 18.0))
testEnd(true);
else
goto error;
testBegin("pdfioContentTextBegin()");
if (pdfioContentTextBegin(st))
testEnd(true);
else
goto error;
x = 36.0;
xt = x + 0.5 * (216.0 - pdfioContentTextMeasure(font, "gray.gif", 18.0));
testBegin("pdfioContentTextMoveTo(%g, 396)", xt);
if (pdfioContentTextMoveTo(st, xt, 396.0))
testEnd(true);
else
goto error;
testBegin("pdfioContentTextShow(\"gray.gif\")");
if (pdfioContentTextShow(st, false, "gray.gif"))
testEnd(true);
else
goto error;
testBegin("pdfioContentTextEnd()");
if (pdfioContentTextEnd(st))
testEnd(true);
else
goto error;
testBegin("pdfioContentDrawImage(\"IM1\")");
if (pdfioContentDrawImage(st, "IM1", x, 432, 216, 324))
testEnd(true);
else
goto error;
testBegin("pdfioContentTextBegin()");
if (pdfioContentTextBegin(st))
testEnd(true);
else
goto error;
x = 360.0;
xt = x + 0.5 * (216.0 - pdfioContentTextMeasure(font, "color.gif", 18.0));
testBegin("pdfioContentTextMoveTo(%g, 396)", xt);
if (pdfioContentTextMoveTo(st, xt, 396.0))
testEnd(true);
else
goto error;
testBegin("pdfioContentTextShow(\"color.gif\")");
if (pdfioContentTextShow(st, false, "color.gif"))
testEnd(true);
else
goto error;
testBegin("pdfioContentTextEnd()");
if (pdfioContentTextEnd(st))
testEnd(true);
else
goto error;
testBegin("pdfioContentDrawImage(\"IM2\")");
if (pdfioContentDrawImage(st, "IM2", x, 432, 216, 324))
testEnd(true);
else
goto error;
for (i = 0; i < (sizeof(pdfio_labels) / sizeof(pdfio_labels[0])); i ++)
{
x = 36.0 + (i & 3) * (108.0 + 36.0);
y = 360.0 - (1 + i / 4) * (108.0 + 36.0);
testBegin("pdfioContentSetFillColorDeviceRGB(0, 1, 1)");
if (pdfioContentSetFillColorDeviceRGB(st, 0.0, 1.0, 1.0))
testEnd(true);
else
goto error;
testBegin("pdfioContentPathRect(%g, %g, 108, 108)", x, y + 36.0);
if (pdfioContentPathRect(st, x, y + 36.0, 108, 108))
testEnd(true);
else
goto error;
testBegin("pdfioContentFill(false)");
if (pdfioContentFill(st, false))
testEnd(true);
else
goto error;
testBegin("pdfioContentSetFillColorDeviceGray(0.0)");
if (pdfioContentSetFillColorDeviceGray(st, 0.0))
testEnd(true);
else
goto error;
testBegin("pdfioContentTextBegin()");
if (pdfioContentTextBegin(st))
{
testEnd(true);
}
else
{
testEnd(false);
goto error;
}
xt = x + 0.5 * (108.0 - pdfioContentTextMeasure(font, pdfio_labels[i], 18.0));
testBegin("pdfioContentTextMoveTo(%g, %g)", xt, y + 12.0);
if (pdfioContentTextMoveTo(st, xt, y + 12.0))
testEnd(true);
else
goto error;
testBegin("pdfioContentTextShow(\"%s\")", pdfio_labels[i]);
if (pdfioContentTextShow(st, false, pdfio_labels[i]))
{
testEnd(true);
}
else
{
testEnd(false);
goto error;
}
testBegin("pdfioContentTextEnd()");
if (pdfioContentTextEnd(st))
{
testEnd(true);
}
else
{
testEnd(false);
goto error;
}
snprintf(imgname, sizeof(imgname), "IM%u", (unsigned)(i + 11));
testBegin("pdfioContentDrawImage(\"%s\")", imgname);
if (pdfioContentDrawImage(st, imgname, x, y + 36, 108, 108))
{
testEnd(true);
}
else
{
testEnd(false);
goto error;
}
}
// Close the object and stream...
testBegin("pdfioStreamClose");
if (pdfioStreamClose(st))
{
testEnd(true);
}
else
{
testEnd(false);
return (1);
}
return (0);
error:
pdfioStreamClose(st);
return (1);
}
//
// 'write_header_footer()' - Write common header and footer text.
//
@@ -4674,6 +5003,12 @@ write_unit_file(
return (1);
pagenum ++;
// Write a page with GIF images...
if (write_gif_tests(outpdf, pagenum, helvetica))
return (1);
pagenum ++;
// Write a page with PNG images...
if (write_png_tests(outpdf, pagenum, helvetica))
return (1);