/*---------------------------------------------------------------------------*
 |              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_bmp.c,v 1.38.2.5 2007/11/08 17:50:15 kurt Exp $
 *
 * BMP processing for PDFlib
 *
 */

#include "p_intern.h"
#include "p_color.h"
#include "p_image.h"

#ifndef PDF_BMP_SUPPORTED

pdc_bool
pdf_is_BMP_file(PDF *p, pdc_file *fp)
{
    (void) p;
    (void) fp;

    return pdc_false;
}

int
pdf_process_BMP_data(
    PDF *p,
    int imageslot)
{
    (void) imageslot;

    pdc_set_errmsg(p->pdc, PDF_E_UNSUPP_IMAGE, "BMP", 0, 0, 0);

    return -1;
}

#else  /* !PDF_BMP_SUPPORTED */

/* for documentation only */
#if 0

/* BMP file header structure */
typedef struct
{
    pdc_ushort  bfType;           /* Magic number for file */
    pdc_uint32  bfSize;           /* Size of file */
    pdc_ushort  bfReserved1;      /* Reserved */
    pdc_ushort  bfReserved2;      /* ... */
    pdc_uint32  bfOffBits;        /* Offset to bitmap data */
}
BITMAPFILEHEADER;

/* BMP file info structure */
typedef struct
{
    pdc_uint32  biSize;           /* Size of info header */
    pdc_sint32  biWidth;          /* Width of image */
    pdc_sint32  biHeight;         /* Height of image */
    pdc_ushort  biPlanes;         /* Number of color planes */
    pdc_ushort  biBitCount;       /* Number of bits per pixel */
    pdc_uint32  biCompression;    /* Type of compression to use */
    pdc_uint32  biSizeImage;      /* Size of image data */
    pdc_sint32  biXPelsPerMeter;  /* X pixels per meter */
    pdc_sint32  biYPelsPerMeter;  /* Y pixels per meter */
    pdc_uint32  biClrUsed;        /* Number of colors used */
    pdc_uint32  biClrImportant;   /* Number of important colors */
}
BITMAPINFOHEADER;

#endif

#define PDF_GET_BYTE(pos)   *pos,                   pos += sizeof(pdc_byte)
#define PDF_GET_SHORT(pos)  pdc_get_le_short(pos),  pos += sizeof(pdc_short)
#define PDF_GET_USHORT(pos) pdc_get_le_ushort(pos), pos += sizeof(pdc_ushort)
#define PDF_GET_LONG(pos)   pdc_get_le_long(pos),   pos += sizeof(pdc_sint32)
#define PDF_GET_ULONG(pos)  pdc_get_le_ulong(pos),  pos += sizeof(pdc_uint32)

#define PDF_BMP_STRING     "\102\115"   /* "BM" */

#define PDF_BMP_RGB                 0   /* No compression - straight BGR data */
#define PDF_BMP_RLE8                1   /* 8-bit run-length compression */
#define PDF_BMP_RLE4                2   /* 4-bit run-length compression */
#define PDF_BMP_BITFIELDS           3   /* RGB bitmap with RGB masks */

#define PDF_BMP_FILE_HEADSIZE      14   /* File header size */
#define PDF_BMP_INFO_HEAD2SIZE     12   /* Info header size BMP Version 2 */
#define PDF_BMP_INFO_HEAD3SIZE     40   /* Info header size BMP Version 3 */
#define PDF_BMP_INFO_HEAD4SIZE    108   /* Info header size BMP Version 4 */

static void
pdf_data_source_BMP_init(PDF *p, PDF_data_source *src)
{
    static const char *fn = "pdf_data_source_BMP_init";
    pdf_image *image = (pdf_image *) src->private_data;

    src->buffer_length = image->info.bmp.rowbytes_buf;
    src->buffer_start = (pdc_byte *)
        pdc_calloc(p->pdc, image->info.bmp.rowbytes_pad, fn);
    src->bytes_available = image->info.bmp.rowbytes_pdf;
    src->next_byte = src->buffer_start;
}

static pdc_bool
pdf_data_source_BMP_fill(PDF *p, PDF_data_source *src)
{
    pdf_image *image = (pdf_image *) src->private_data;
    int i, j;

    (void) p;

    if (image->info.bmp.bpp == 16)
    {
        if (image->info.bmp.pos < image->info.bmp.end)
        {
            pdc_ushort pixel, ppixel;
            int ilast = image->info.bmp.rowbytes_buf - 3;

            i = 0;
            for (j = 0; j < (int) image->info.bmp.rowbytes; j += 2)
            {
                pixel = PDF_GET_USHORT(image->info.bmp.pos);

                if (i <= ilast)
                {
                    ppixel = (pixel & image->info.bmp.redmask);
                    ppixel = (ppixel >> image->info.bmp.redmove);
                    src->buffer_start[i] = (pdc_byte) ((ppixel * 255) /
                                               image->info.bmp.redmax);
                    i++;

                    ppixel = (pixel & image->info.bmp.greenmask);
                    ppixel = (ppixel >> image->info.bmp.greenmove);
                    src->buffer_start[i] = (pdc_byte) ((ppixel * 255) /
                                               image->info.bmp.greenmax);
                    i++;

                    ppixel = (pixel & image->info.bmp.bluemask);
                    ppixel = (ppixel >> image->info.bmp.bluemove);
                    src->buffer_start[i] = (pdc_byte) ((ppixel * 255) /
                                               image->info.bmp.bluemax);
                    i++;
                }
            }
        }
        else
        {
            src->bytes_available = 0;
        }
    }

    else if (image->info.bmp.bpp == 32 ||
             image->info.bmp.compression == PDF_BMP_RGB)
    {
        size_t avail;

        /* Read 1 padded row from file */
        avail = pdc_fread(src->buffer_start, 1, image->info.bmp.rowbytes_pad,
                          image->fp);
        if (avail > 0)
        {
            /* Fill up remaining bytes */
            if (avail < image->info.bmp.rowbytes_pad)
            {
                for (i = (int) avail; i < (int) src->buffer_length; i++)
                    src->buffer_start[i] = 0;
            }

            if (image->colorspace == DeviceRGB)
            {
                /* Swap red and blue */
                if (image->info.bmp.bpp == 32)
                {
                    pdc_uint32 pixel, ppixel;
                    pdc_byte *pos = src->buffer_start;

                    i = 0;
                    for (j = 0; j < (int) image->info.bmp.rowbytes_buf; j += 4)
                    {
                        pixel = PDF_GET_ULONG(pos);

                        ppixel = (pixel & image->info.bmp.redmask);
                        ppixel = (ppixel >> image->info.bmp.redmove);
                        src->buffer_start[i] = (pdc_byte) ((ppixel * 255) /
                                                   image->info.bmp.redmax);
                        i++;

                        ppixel = (pixel & image->info.bmp.greenmask);
                        ppixel = (ppixel >> image->info.bmp.greenmove);
                        src->buffer_start[i] = (pdc_byte) ((ppixel * 255) /
                                                   image->info.bmp.greenmax);
                        i++;

                        ppixel = (pixel & image->info.bmp.bluemask);
                        ppixel = (ppixel >> image->info.bmp.bluemove);
                        src->buffer_start[i] = (pdc_byte) ((ppixel * 255) /
                                                   image->info.bmp.bluemax);
                        i++;
                    }
                }
                else
                {
                    for (j = 0; j < (int) image->info.bmp.rowbytes_buf; j += 3)
                    {
                        pdc_byte c = src->buffer_start[j];
                        src->buffer_start[j] = src->buffer_start[j + 2];
                        src->buffer_start[j + 2] = c;
                    }
                }
            }
        }
        else
        {
            src->bytes_available = 0;
        }
    }

    /* Compression methods RLE8 and RLE4 */
    else
    {
        int col = 0;
        int fnibble = 1;
        pdc_byte c, cc, ccc, cn[2], ccn;

        if (image->info.bmp.pos < image->info.bmp.end)
        {
            if (image->info.bmp.skiprows)
            {
                for (; col < (int) image->info.bmp.rowbytes; col++)
                    src->buffer_start[col] = 0;
                image->info.bmp.skiprows--;
            }
            else
            {
                while (1)
                {
                    c = *image->info.bmp.pos;
                    image->info.bmp.pos++;
                    if (image->info.bmp.pos >= image->info.bmp.end)
                        goto PDF_BMP_CORRUPT;
                    cc = *image->info.bmp.pos;

                    if (c != 0)
                    {
                        /* Repeat c time pixel value */
                        if (image->info.bmp.compression == PDF_BMP_RLE8)
                        {
                            for (i = 0; i < (int) c; i++)
                            {
                                if (col >= (int) image->info.bmp.rowbytes)
                                    goto PDF_BMP_CORRUPT;
                                src->buffer_start[col] = cc;
                                col++;
                            }
                        }
                        else
                        {
                            cn[0] = (pdc_byte) ((cc & 0xF0) >> 4);
                            cn[1] = (pdc_byte) (cc & 0x0F);
                            for (i = 0; i < (int) c; i++)
                            {
                                if (col >= (int) image->info.bmp.rowbytes)
                                    goto PDF_BMP_CORRUPT;
                                ccn = cn[i%2];
                                if (fnibble)
                                {
                                    fnibble = 0;
                                    src->buffer_start[col] =
                                        (pdc_byte) (ccn << 4);
                                }
                                else
                                {
                                    fnibble = 1;
                                    src->buffer_start[col] |= ccn;
                                    col++;
                                }
                            }
                        }
                    }
                    else if (cc > 2)
                    {
                        /* cc different pixel values */
                        if (image->info.bmp.compression == PDF_BMP_RLE8)
                        {
                            for (i = 0; i < (int) cc; i++)
                            {
                                image->info.bmp.pos++;
                                if (image->info.bmp.pos >= image->info.bmp.end)
                                    goto PDF_BMP_CORRUPT;
                                if (col >= (int) image->info.bmp.rowbytes)
                                    goto PDF_BMP_CORRUPT;
                                src->buffer_start[col] = *image->info.bmp.pos;
                                col++;
                            }
                        }
                        else
                        {
                            for (i = 0; i < (int) cc; i++)
                            {
                                if (!(i%2))
                                {
                                    image->info.bmp.pos++;
                                    if (image->info.bmp.pos >=
                                        image->info.bmp.end)
                                        goto PDF_BMP_CORRUPT;
                                    ccc = *image->info.bmp.pos;
                                    cn[0] = (pdc_byte) ((ccc & 0xF0) >> 4);
                                    cn[1] = (pdc_byte) (ccc & 0x0F);
                                }
                                if (col >= (int) image->info.bmp.rowbytes)
                                    goto PDF_BMP_CORRUPT;
                                ccn = cn[i%2];
                                if (fnibble)
                                {
                                    fnibble = 0;
                                    src->buffer_start[col] =
                                        (pdc_byte) (ccn << 4);
                                }
                                else
                                {
                                    fnibble = 1;
                                    src->buffer_start[col] |= ccn;
                                    col++;
                                }
                            }
                            if (cc % 2) cc++;
                            cc /= 2;
                        }

                        /* Odd number of bytes */
                        if (cc % 2)
                            image->info.bmp.pos++;
                    }
                    else if (cc < 2)
                    {
                        /* End of scan line or end of bitmap data*/
                        for (; col < (int) image->info.bmp.rowbytes; col++)
                            src->buffer_start[col] = 0;
                    }
                    else if (cc == 2)
                    {
                        int cola;

                        /* Run offset marker */
                        if (image->info.bmp.pos >= image->info.bmp.end - 1)
                            goto PDF_BMP_CORRUPT;
                        image->info.bmp.pos++;
                        c = *image->info.bmp.pos;
                        image->info.bmp.pos++;
                        cc = *image->info.bmp.pos;

                        /* Fill current row */
                        cola = col;
                        for (; col < (int) image->info.bmp.rowbytes; col++)
                            src->buffer_start[col] = 0;
                        if (col - cola != (int) c)
                            goto PDF_BMP_CORRUPT;

                        /* Number of rows to be skipped */
                        image->info.bmp.skiprows = (size_t) cc;
                    }

                    image->info.bmp.pos++;
                    if (col >= (int) image->info.bmp.rowbytes)
                    {
                        /* Skip end of scan line marker */
                        if (image->info.bmp.pos < image->info.bmp.end - 1)
                        {
                            c = *image->info.bmp.pos;
                            cc = *(image->info.bmp.pos + 1);
                            if(cc == 0 && cc <= 1)
                                image->info.bmp.pos += 2;
                        }
                        break;
                    }
                }
            }
        }
        else
        {
            src->bytes_available = 0;
        }
    }

    return (src->bytes_available ? pdc_true : pdc_false);

    PDF_BMP_CORRUPT:

    image->corrupt = pdc_true;
    src->bytes_available = 0;
    return pdc_false;
}

static void
pdf_data_source_BMP_terminate(PDF *p, PDF_data_source *src)
{
    pdf_image *image = (pdf_image *) src->private_data;

    pdc_free(p->pdc, (void *) src->buffer_start);
    if (image->info.bmp.bitmap != NULL)
        pdc_free(p->pdc, (void *) image->info.bmp.bitmap);
}

pdc_bool
pdf_is_BMP_file(PDF *p, pdc_file *fp)
{
    pdc_byte buf[2];

    pdc_logg_cond(p->pdc, 1, trc_image, "\tChecking image type BMP...\n");

    if (pdc_fread(buf, 1, 2, fp) < 2 ||
        strncmp((const char *) buf, PDF_BMP_STRING, 2) != 0)
    {
        pdc_fseek(fp, 0L, SEEK_SET);
        return pdc_false;
    }
    return pdc_true;
}

int
pdf_process_BMP_data(
    PDF *p,
    int imageslot)
{
    static const char *fn = "pdf_process_BMP_data";
    pdc_byte buf[256], *pos, *cmap, bdummy;
    pdf_image *image = &p->images[imageslot];
    pdc_file *fp = image->fp;
    pdc_uint32 uldummy, infosize = 0, offras = 0, planes = 0, bitmapsize = 0;
    pdc_uint32 ncolors = 0, importcolors = 0, compression = PDF_BMP_RGB;
    pdc_ushort usdummy, bpp = 0, bpp_pdf = 0;
    pdc_sint32 width = 0, height = 0, dpi_x = 0, dpi_y = 0;
    pdc_uint32 redmask = 0, greenmask = 0, bluemask = 0, ccmax;
    size_t nbytes;
    pdf_colorspace cs;
    pdf_colormap colormap;
    int i, slot, colsize = 0, errcode = 0;

    /* Error reading magic number or not a BMP file */
    if (pdf_is_BMP_file(p, image->fp) == pdc_false)
    {
        errcode = PDC_E_IO_BADFORMAT;
        goto PDF_BMP_ERROR;
    }

    /* read file header without FileType field + */
    /* Size field of info header */
    pos = &buf[2];
    nbytes = PDF_BMP_FILE_HEADSIZE - 2 + 4;
    if (!PDC_OK_FREAD(fp, pos, nbytes))
    {
        errcode = PDF_E_IMAGE_CORRUPT;
        goto PDF_BMP_ERROR;
    }
    uldummy = PDF_GET_ULONG(pos);
    usdummy = PDF_GET_USHORT(pos);
    usdummy = PDF_GET_USHORT(pos);
    offras = PDF_GET_ULONG(pos);
    infosize = PDF_GET_ULONG(pos);

    /* no support of later version than 3 */
    if (infosize != PDF_BMP_INFO_HEAD2SIZE &&
        infosize != PDF_BMP_INFO_HEAD3SIZE)
    {
        errcode = PDF_E_BMP_VERSUNSUPP;
        goto PDF_BMP_ERROR;
    }

    /* info header */
    pos = buf;
    nbytes = infosize - 4;
    if (!PDC_OK_FREAD(fp, pos, nbytes))
    {
        errcode = PDF_E_IMAGE_CORRUPT;
        goto PDF_BMP_ERROR;
    }
    if (infosize == PDF_BMP_INFO_HEAD2SIZE)
    {
        width = PDF_GET_SHORT(pos);
        height = PDF_GET_SHORT(pos);
        planes = PDF_GET_USHORT(pos);
        bpp = PDF_GET_USHORT(pos);
        colsize = 3;
    }
    else if (infosize == PDF_BMP_INFO_HEAD3SIZE)
    {
        width = PDF_GET_LONG(pos);
        height = PDF_GET_LONG(pos);
        planes = PDF_GET_USHORT(pos);
        bpp = PDF_GET_USHORT(pos);
        compression = PDF_GET_ULONG(pos);
        bitmapsize = PDF_GET_ULONG(pos);
        dpi_x = PDF_GET_LONG(pos);
        dpi_y = PDF_GET_LONG(pos);
        ncolors = PDF_GET_ULONG(pos);
        importcolors = PDF_GET_ULONG(pos);
        colsize = 4;
    }

    /* compressed BMP images by bitfields */
    if (compression == PDF_BMP_BITFIELDS)
    {
        pos = buf;
        nbytes = 3 * sizeof(pdc_uint32);
        if (!PDC_OK_FREAD(fp, pos, nbytes))
        {
            errcode = PDF_E_IMAGE_CORRUPT;
            goto PDF_BMP_ERROR;
        }
        else
        {
            redmask = PDF_GET_ULONG(pos);
            greenmask = PDF_GET_ULONG(pos);
            bluemask = PDF_GET_ULONG(pos);
        }
    }

    pdc_logg_cond(p->pdc, 5, trc_image,
                       "\t\t\tinfosize = %d\n"
                       "\t\t\twidth = %d\n"
                       "\t\t\theight = %d\n"
                       "\t\t\tplanes = %d\n"
                       "\t\t\tbpp = %d\n"
                       "\t\t\tcompression = %d\n"
                       "\t\t\tbitmapsize = %d\n"
                       "\t\t\tdpi_x = %d\n"
                       "\t\t\tdpi_y = %d\n"
                       "\t\t\tncolors = %d\n"
                       "\t\t\timportcolors = %d\n"
                       "\t\t\tcolsize = %d\n"
                       "\t\t\tredmask   = 0x%08X\n"
                       "\t\t\tgreenmask = 0x%08X\n"
                       "\t\t\tbluemask  = 0x%08X\n",
                       infosize, width, height, planes, bpp, compression,
                       bitmapsize, dpi_x, dpi_y, ncolors, importcolors,
                       colsize, redmask, greenmask, bluemask);

    image->bpc = bpp;
    image->width = width;
    image->height = -height;
    image->dpi_x = (pdc_scalar) (PDC_INCH2METER * dpi_x);
    image->dpi_y = (pdc_scalar) (PDC_INCH2METER * dpi_y);
    image->info.bmp.bpp = bpp;
    bpp_pdf = bpp;

    /* color map only for bpp = 1, 4, 8 */
    if (bpp < 16)
    {
        if (!ncolors)
            ncolors = (pdc_uint32) (1 << bpp);
        if (ncolors > (offras - PDF_BMP_FILE_HEADSIZE - infosize) / colsize)
        {
            errcode = PDF_E_IMAGE_CORRUPT;
            goto PDF_BMP_ERROR;
        }

        /* allocate and read color map */
        nbytes = colsize * ncolors;
        cmap = (pdc_byte *) pdc_malloc(p->pdc, nbytes, fn);
        if (!PDC_OK_FREAD(fp, cmap, nbytes))
        {
            errcode = PDF_E_IMAGE_CORRUPT;
            goto PDF_BMP_ERROR;
        }

        /* set color map (bgr) */
        pos = cmap;
        for (i = 0; i < (int) ncolors; i++)
        {
            colormap[i][2] = PDF_GET_BYTE(pos);
            colormap[i][1] = PDF_GET_BYTE(pos);
            colormap[i][0] = PDF_GET_BYTE(pos);
            if (infosize == PDF_BMP_INFO_HEAD3SIZE)
            {
                bdummy = PDF_GET_BYTE(pos);
            }
        }
        pdc_free(p->pdc, cmap);

        pdc_logg_cond(p->pdc, 5, trc_image,
                          "\t\t\tcolor map with %d colors generated\n",
                          ncolors);

        image->components = 1;

	    cs.type = Indexed;
	    cs.val.indexed.base = DeviceRGB;
	    cs.val.indexed.palette_size = (int) ncolors;
	    cs.val.indexed.colormap = &colormap;
	    cs.val.indexed.colormap_id = PDC_BAD_ID;
	    slot = pdf_add_colorspace(p, &cs, pdc_false);

	    image->colorspace = slot;


    }
    else
    {
        image->colorspace = DeviceRGB;
        image->components = 3;
        image->bpc = 8;
        if (bpp == 16)
        {
            bpp = 24;
            bpp_pdf = 24;
            if (compression == PDF_BMP_RGB)
            {
                redmask   = 0x00007C00;
                greenmask = 0x000003E0;
                bluemask  = 0x0000001F;
            }
        }
        if (bpp == 32)
        {
            bpp_pdf = 24;
            if (compression == PDF_BMP_RGB)
            {
                redmask   = 0x00FF0000;
                greenmask = 0x0000FF00;
                bluemask  = 0x000000FF;
            }
        }

        /* maximum and movement */
        if (image->info.bmp.bpp != 24)
        {
            for (i = 0; i < 32; i++)
            {
                ccmax = (redmask >> i);
                if (ccmax & 0x00000001)
                    break;
            }
            image->info.bmp.redmask = redmask;
            image->info.bmp.redmax = ccmax;
            image->info.bmp.redmove = i;


            for (i = 0; i < 32; i++)
            {
                ccmax = (greenmask >> i);
                if (ccmax & 0x00000001)
                    break;
            }
            image->info.bmp.greenmask = greenmask;
            image->info.bmp.greenmax = ccmax;
            image->info.bmp.greenmove = i;

            for (i = 0; i < 32; i++)
            {
                ccmax = (bluemask >> i);
                if (ccmax & 0x00000001)
                    break;
            }
            image->info.bmp.bluemask = bluemask;
            image->info.bmp.bluemax = ccmax;
            image->info.bmp.bluemove = i;
        }
    }

    if (image->imagemask)
    {
	if (image->components != 1) {
	    errcode = PDF_E_IMAGE_BADMASK;
	    goto PDF_BMP_ERROR;
	}

        if (p->compatibility <= PDC_1_3) {
            if (image->components != 1 || image->bpc != 1) {
                errcode = PDF_E_IMAGE_MASK1BIT13;
                goto PDF_BMP_ERROR;
            }
        } else if (image->bpc > 1) {
            /* images with more than one bit will be written as /SMask,
             * and don't require an /ImageMask entry.
             */
            image->imagemask = pdc_false;
        }
        image->colorspace = DeviceGray;
    }



    /* we invert this flag later */
    if (image->ignoremask)
        image->transparent = pdc_true;

    /* number of bytes per row  */
    image->info.bmp.rowbytes_pdf = (size_t) ((bpp_pdf * width + 7) / 8);
    image->info.bmp.rowbytes_buf = (size_t) ((bpp * width + 7) / 8);
    if (bpp == 4)
        image->info.bmp.rowbytes = image->info.bmp.rowbytes_buf;
    else if (image->info.bmp.bpp == 16)
        image->info.bmp.rowbytes =
                   (size_t) (4 * ((image->info.bmp.bpp * width + 31) / 32));
    else
        image->info.bmp.rowbytes = (size_t) ((bpp * width) / 8);
    image->info.bmp.rowbytes_pad = (size_t) (4 * ((bpp * width + 31) / 32));
    image->info.bmp.compression = compression;
    image->info.bmp.skiprows = 0;
    image->info.bmp.bitmap = NULL;

    pdc_logg_cond(p->pdc, 5, trc_image,
                      "\t\t\tinternal variables:\n"
                      "\t\t\t\tbpp_pdf = %d\n"
                      "\t\t\t\trowbytes = %d\n"
                      "\t\t\t\trowbytes_buf = %d\n"
                      "\t\t\t\trowbytes_pdf = %d\n"
                      "\t\t\t\trowbytes_pad = %d\n",
    bpp_pdf, image->info.bmp.rowbytes, image->info.bmp.rowbytes_buf,
    image->info.bmp.rowbytes_pdf, image->info.bmp.rowbytes_pad);

    /* read whole bitmap */
    if (image->info.bmp.bpp < 24 && (image->info.bmp.bpp == 16 ||
        image->info.bmp.compression != PDF_BMP_RGB))
    {
        if (!bitmapsize)
        {
            bitmapsize = (int) (pdc_file_size(fp) - pdc_ftell(fp));
            pdc_logg_cond(p->pdc, 5, trc_image,
                              "\t\t\tcalculated bitmapsize = %d\n", bitmapsize);
        }

        image->info.bmp.bitmap =
            (pdc_byte *) pdc_malloc(p->pdc, bitmapsize, fn);
        if (!PDC_OK_FREAD(fp, image->info.bmp.bitmap, bitmapsize))
        {
            pdc_free(p->pdc, (void *) image->info.bmp.bitmap);
            errcode = PDF_E_IMAGE_CORRUPT;
            goto PDF_BMP_ERROR;
        }
        image->info.bmp.pos = image->info.bmp.bitmap;
        image->info.bmp.end = image->info.bmp.bitmap + bitmapsize;
    }

    /* offset bitmap data */
    pdc_fseek(image->fp, (pdc_sint32) offras, SEEK_SET);

    /* put image data */
    image->src.init = pdf_data_source_BMP_init;
    image->src.fill = pdf_data_source_BMP_fill;
    image->src.terminate = pdf_data_source_BMP_terminate;
    image->src.private_data  = (void *) image;

    image->use_raw = pdc_false;
    image->in_use = pdc_true;

    pdf_put_image(p, imageslot, pdc_true, pdc_true);

    if (image->corrupt)
        errcode = PDF_E_IMAGE_CORRUPT;
    else
        return imageslot;

    PDF_BMP_ERROR:
    {
        const char *stemp = NULL;

        if (errcode)
            stemp = pdf_get_image_filename(p, image);

        switch (errcode)
        {
            case PDF_E_IMAGE_MASK1BIT13:
            case PDF_E_BMP_VERSUNSUPP:
            case PDF_E_BMP_COMPUNSUPP:
            case PDF_E_IMAGE_BADMASK:
		pdc_set_errmsg(p->pdc, errcode, stemp, 0, 0, 0);
		break;

            case PDC_E_IO_BADFORMAT:
		pdc_set_errmsg(p->pdc, errcode, stemp, "BMP", 0, 0);
		break;

            case PDF_E_IMAGE_CORRUPT:
		pdc_set_errmsg(p->pdc, errcode, "BMP", stemp, 0, 0);
		break;

	    case 0: 		/* error code and message already set */
		break;
        }
    }

    return -1;
}

#endif /* PDF_BMP_SUPPORTED */

