Initial GIF support (Issue #145)
@@ -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
|
||||
|
||||
|
||||
|
||||
391
pdfio-content.c
@@ -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.
|
||||
|
||||
50
pdfio-lzw.c
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
After Width: | Height: | Size: 245 KiB |
BIN
testfiles/gray.gif
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
testfiles/pdfio-1bit.gif
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
testfiles/pdfio-2bit.gif
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
testfiles/pdfio-4bit-t.gif
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
testfiles/pdfio-4bit.gif
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
testfiles/pdfio-8bit-a.gif
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
testfiles/pdfio-8bit-t.gif
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
testfiles/pdfio-8bit.gif
Normal file
|
After Width: | Height: | Size: 16 KiB |
337
testpdfio.c
@@ -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);
|
||||
|
||||