Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * imgexam.c
 * Copyright (C) 2000-2004 A.J. van Os; Released under GNU GPL
 *
 * Description:
 * Functions to examine image headers
 *
 *================================================================
 * Part of this software is based on:
 * jpeg2ps - convert JPEG compressed images to PostScript Level 2
 * Copyright (C) 1994-99 Thomas Merz (tm@muc.de)
 *================================================================
 * The credit should go to him, but all the bugs are mine.
 */

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "antiword.h"

/* BMP compression types */
#define BI_RGB          0
#define BI_RLE8         1
#define BI_RLE4         2

/* PNG colortype bits */
#define PNG_CB_PALETTE          0x01
#define PNG_CB_COLOR            0x02
#define PNG_CB_ALPHA            0x04

/* Instance signature */
#define MSOBI_WMF       0x0216
#define MSOBI_EMF       0x03d4
#define MSOBI_PICT      0x0542
#define MSOBI_PNG       0x06e0
#define MSOBI_JPEG      0x046a
#define MSOBI_DIB       0x07a8

/* The following enum is stolen from the IJG JPEG library */
typedef enum {          /* JPEG marker codes                    */
        M_SOF0  = 0xc0, /* baseline DCT                         */
        M_SOF1  = 0xc1, /* extended sequential DCT              */
        M_SOF2  = 0xc2, /* progressive DCT                      */
        M_SOF3  = 0xc3, /* lossless (sequential)                */

        M_SOF5  = 0xc5, /* differential sequential DCT          */
        M_SOF6  = 0xc6, /* differential progressive DCT         */
        M_SOF7  = 0xc7, /* differential lossless                */

        M_JPG   = 0xc8, /* JPEG extensions                      */
        M_SOF9  = 0xc9, /* extended sequential DCT              */
        M_SOF10 = 0xca, /* progressive DCT                      */
        M_SOF11 = 0xcb, /* lossless (sequential)                */

        M_SOF13 = 0xcd, /* differential sequential DCT          */
        M_SOF14 = 0xce, /* differential progressive DCT         */
        M_SOF15 = 0xcf, /* differential lossless                */

        M_DHT   = 0xc4, /* define Huffman tables                */

        M_DAC   = 0xcc, /* define arithmetic conditioning table */

        M_RST0  = 0xd0, /* restart                              */
        M_RST1  = 0xd1, /* restart                              */
        M_RST2  = 0xd2, /* restart                              */
        M_RST3  = 0xd3, /* restart                              */
        M_RST4  = 0xd4, /* restart                              */
        M_RST5  = 0xd5, /* restart                              */
        M_RST6  = 0xd6, /* restart                              */
        M_RST7  = 0xd7, /* restart                              */

        M_SOI   = 0xd8, /* start of image                       */
        M_EOI   = 0xd9, /* end of image                         */
        M_SOS   = 0xda, /* start of scan                        */
        M_DQT   = 0xdb, /* define quantization tables           */
        M_DNL   = 0xdc, /* define number of lines               */
        M_DRI   = 0xdd, /* define restart interval              */
        M_DHP   = 0xde, /* define hierarchical progression      */
        M_EXP   = 0xdf, /* expand reference image(s)            */

        M_APP0  = 0xe0, /* application marker, used for JFIF    */
        M_APP1  = 0xe1, /* application marker                   */
        M_APP2  = 0xe2, /* application marker                   */
        M_APP3  = 0xe3, /* application marker                   */
        M_APP4  = 0xe4, /* application marker                   */
        M_APP5  = 0xe5, /* application marker                   */
        M_APP6  = 0xe6, /* application marker                   */
        M_APP7  = 0xe7, /* application marker                   */
        M_APP8  = 0xe8, /* application marker                   */
        M_APP9  = 0xe9, /* application marker                   */
        M_APP10 = 0xea, /* application marker                   */
        M_APP11 = 0xeb, /* application marker                   */
        M_APP12 = 0xec, /* application marker                   */
        M_APP13 = 0xed, /* application marker                   */
        M_APP14 = 0xee, /* application marker, used by Adobe    */
        M_APP15 = 0xef, /* application marker                   */

        M_JPG0  = 0xf0, /* reserved for JPEG extensions         */
        M_JPG13 = 0xfd, /* reserved for JPEG extensions         */
        M_COM   = 0xfe, /* comment                              */

        M_TEM   = 0x01  /* temporary use                        */
} JPEG_MARKER;


/*
 * bFillPaletteDIB - fill the palette part of the imagesdata
 *
 * returns TRUE if the images must be a color image, otherwise FALSE;
 */
static BOOL
bFillPaletteDIB(FILE *pFile, imagedata_type *pImg, BOOL bNewFormat)
{
        int     iIndex;
        BOOL    bIsColorPalette;

        fail(pFile == NULL);
        fail(pImg == NULL);

        if (pImg->uiBitsPerComponent > 8) {
                /* No palette, image uses more than 256 colors */
                return TRUE;
        }

        if (pImg->iColorsUsed <= 0) {
                /* Not specified, so compute the number of colors used */
                pImg->iColorsUsed = 1 << pImg->uiBitsPerComponent;
        }

        fail(pImg->iColorsUsed > 256);
        if (pImg->iColorsUsed > 256) {
                pImg->iColorsUsed = 256;
        }

        bIsColorPalette = FALSE;
        for (iIndex = 0; iIndex < pImg->iColorsUsed; iIndex++) {
                /* From BGR order to RGB order */
                pImg->aucPalette[iIndex][2] = (UCHAR)iNextByte(pFile);
                pImg->aucPalette[iIndex][1] = (UCHAR)iNextByte(pFile);
                pImg->aucPalette[iIndex][0] = (UCHAR)iNextByte(pFile);
                if (bNewFormat) {
                        (void)iNextByte(pFile);
                }
                NO_DBG_PRINT_BLOCK(pImg->aucPalette[iIndex], 3);
                if (pImg->aucPalette[iIndex][0] !=
                     pImg->aucPalette[iIndex][1] ||
                    pImg->aucPalette[iIndex][1] !=
                     pImg->aucPalette[iIndex][2]) {
                        bIsColorPalette = TRUE;
                }
        }

        return bIsColorPalette;
} /* end of bFillPaletteDIB */

/*
 * bExamineDIB - Examine a DIB header
 *
 * return TRUE if successful, otherwise FALSE
 */
static BOOL
bExamineDIB(FILE *pFile, imagedata_type *pImg)
{
        size_t  tHeaderSize;
        int     iPlanes, iCompression;

        tHeaderSize = (size_t)ulNextLong(pFile);
        switch (tHeaderSize) {
        case 12:
                pImg->iWidth = (int)usNextWord(pFile);
                pImg->iHeight = (int)usNextWord(pFile);
                iPlanes = (int)usNextWord(pFile);
                pImg->uiBitsPerComponent = (UINT)usNextWord(pFile);
                iCompression = BI_RGB;
                pImg->iColorsUsed = 0;
                break;
        case 40:
        case 64:
                pImg->iWidth = (int)ulNextLong(pFile);
                pImg->iHeight = (int)ulNextLong(pFile);
                iPlanes = (int)usNextWord(pFile);
                pImg->uiBitsPerComponent = (UINT)usNextWord(pFile);
                iCompression = (int)ulNextLong(pFile);
                (void)tSkipBytes(pFile, 12);
                pImg->iColorsUsed = (int)ulNextLong(pFile);
                (void)tSkipBytes(pFile, tHeaderSize - 36);
                break;
        default:
                DBG_DEC(tHeaderSize);
                return FALSE;
        }
        DBG_DEC(pImg->iWidth);
        DBG_DEC(pImg->iHeight);
        DBG_DEC(pImg->uiBitsPerComponent);
        DBG_DEC(iCompression);
        DBG_DEC(pImg->iColorsUsed);

        /* Do some sanity checks with the parameters */
        if (iPlanes != 1) {
                DBG_DEC(iPlanes);
                return FALSE;
        }
        if (pImg->iWidth <= 0 || pImg->iHeight <= 0) {
                DBG_DEC(pImg->iWidth);
                DBG_DEC(pImg->iHeight);
                return FALSE;
        }
        if (pImg->uiBitsPerComponent != 1 && pImg->uiBitsPerComponent != 4 &&
            pImg->uiBitsPerComponent != 8 && pImg->uiBitsPerComponent != 24) {
                DBG_DEC(pImg->uiBitsPerComponent);
                return FALSE;
        }
        if (iCompression != BI_RGB &&
            (pImg->uiBitsPerComponent == 1 || pImg->uiBitsPerComponent == 24)) {
                return FALSE;
        }
        if (iCompression == BI_RLE8 && pImg->uiBitsPerComponent == 4) {
                return FALSE;
        }
        if (iCompression == BI_RLE4 && pImg->uiBitsPerComponent == 8) {
                return FALSE;
        }

        switch (iCompression) {
        case BI_RGB:
                pImg->eCompression = compression_none;
                break;
        case BI_RLE4:
                pImg->eCompression = compression_rle4;
                break;
        case BI_RLE8:
                pImg->eCompression = compression_rle8;
                break;
        default:
                DBG_DEC(iCompression);
                return FALSE;
        }

        pImg->bColorImage = bFillPaletteDIB(pFile, pImg, tHeaderSize > 12);

        if (pImg->uiBitsPerComponent <= 8) {
                pImg->iComponents = 1;
        } else {
                pImg->iComponents = (int)(pImg->uiBitsPerComponent / 8);
        }

        return TRUE;
} /* end of bExamineDIB */

/*
 * iNextMarker - read the next JPEG marker
 */
static int
iNextMarker(FILE *pFile)
{
        int     iMarker;

        do {
                do {
                        iMarker = iNextByte(pFile);
                } while (iMarker != 0xff && iMarker != EOF);
                if (iMarker == EOF) {
                        return EOF;
                }
                do {
                        iMarker = iNextByte(pFile);
                } while (iMarker == 0xff);
        } while (iMarker == 0x00);                      /* repeat if ff/00 */

        return iMarker;
} /* end of iNextMarker */

/*
 * bExamineJPEG - Examine a JPEG header
 *
 * return TRUE if successful, otherwise FALSE
 */
static BOOL
bExamineJPEG(FILE *pFile, imagedata_type *pImg)
{
        size_t  tLength;
        int     iMarker, iIndex;
        char    appstring[10];
        BOOL    bSOFDone;

        tLength = 0;
        bSOFDone = FALSE;

        /* process JPEG markers */
        while (!bSOFDone && (iMarker = iNextMarker(pFile)) != (int)M_EOI) {
                switch (iMarker) {
                case EOF:
                        DBG_MSG("Error: unexpected end of JPEG file");
                        return FALSE;
        /* The following are not officially supported in PostScript level 2 */
                case M_SOF2:
                case M_SOF3:
                case M_SOF5:
                case M_SOF6:
                case M_SOF7:
                case M_SOF9:
                case M_SOF10:
                case M_SOF11:
                case M_SOF13:
                case M_SOF14:
                case M_SOF15:
                        DBG_HEX(iMarker);
                        return FALSE;
                case M_SOF0:
                case M_SOF1:
                        tLength = (size_t)usNextWordBE(pFile);
                        pImg->uiBitsPerComponent = (UINT)iNextByte(pFile);
                        pImg->iHeight = (int)usNextWordBE(pFile);
                        pImg->iWidth = (int)usNextWordBE(pFile);
                        pImg->iComponents = iNextByte(pFile);
                        bSOFDone = TRUE;
                        break;
                case M_APP14:
                /*
                 * Check for Adobe application marker. It is known (per Adobe's
                 * TN5116) to contain the string "Adobe" at the start of the
                 * APP14 marker.
                 */
                        tLength = (size_t)usNextWordBE(pFile);
                        if (tLength < 12) {
                                (void)tSkipBytes(pFile, tLength - 2);
                        } else {
                                for (iIndex = 0; iIndex < 5; iIndex++) {
                                        appstring[iIndex] =
                                                        (char)iNextByte(pFile);
                                }
                                appstring[5] = '\0';
                                if (STREQ(appstring, "Adobe")) {
                                        pImg->bAdobe = TRUE;
                                }
                                (void)tSkipBytes(pFile, tLength - 7);
                        }
                        break;
                case M_SOI:             /* ignore markers without parameters */
                case M_EOI:
                case M_TEM:
                case M_RST0:
                case M_RST1:
                case M_RST2:
                case M_RST3:
                case M_RST4:
                case M_RST5:
                case M_RST6:
                case M_RST7:
                        break;
                default:                /* skip variable length markers */
                        tLength = (size_t)usNextWordBE(pFile);
                        (void)tSkipBytes(pFile, tLength - 2);
                        break;
                }
        }

        DBG_DEC(pImg->iWidth);
        DBG_DEC(pImg->iHeight);
        DBG_DEC(pImg->uiBitsPerComponent);
        DBG_DEC(pImg->iComponents);

        /* Do some sanity checks with the parameters */
        if (pImg->iHeight <= 0 ||
            pImg->iWidth <= 0 ||
            pImg->iComponents <= 0) {
                DBG_DEC(pImg->iHeight);
                DBG_DEC(pImg->iWidth);
                DBG_DEC(pImg->iComponents);
                return FALSE;
        }

        /* Some broken JPEG files have this but they print anyway... */
        if (pImg->iComponents * 3 + 8 != (int)tLength) {
                DBG_MSG("Warning: SOF marker has incorrect length - ignored");
        }

        if (pImg->uiBitsPerComponent != 8) {
                DBG_DEC(pImg->uiBitsPerComponent);
                DBG_MSG("Not supported in PostScript level 2");
                return FALSE;
        }

        if (pImg->iComponents != 1 &&
            pImg->iComponents != 3 &&
            pImg->iComponents != 4) {
                DBG_DEC(pImg->iComponents);
                return FALSE;
        }

        pImg->bColorImage = pImg->iComponents >= 3;
        pImg->iColorsUsed = 0;
        pImg->eCompression = compression_jpeg;

        return TRUE;
} /* end of bExamineJPEG */

/*
 * bFillPalettePNG - fill the palette part of the imagesdata
 *
 * returns TRUE if sucessful, otherwise FALSE;
 */
static BOOL
bFillPalettePNG(FILE *pFile, imagedata_type *pImg, size_t tLength)
{
        int     iIndex, iEntries;

        fail(pFile == NULL);
        fail(pImg == NULL);

        if (pImg->uiBitsPerComponent > 8) {
                /* No palette, image uses more than 256 colors */
                return TRUE;
        }

        if (!pImg->bColorImage) {
                /* Only color images can have a palette */
                return FALSE;
        }

        if (tLength % 3 != 0) {
                /* Each palette entry takes three bytes */
                DBG_DEC(tLength);
                return FALSE;
        }

        iEntries = (int)(tLength / 3);
        DBG_DEC(iEntries);
        pImg->iColorsUsed = 1 << pImg->uiBitsPerComponent;
        DBG_DEC(pImg->iColorsUsed);

        if (iEntries > 256) {
                DBG_DEC(iEntries);
                return FALSE;
        }

        for (iIndex = 0; iIndex < iEntries; iIndex++) {
                pImg->aucPalette[iIndex][0] = (UCHAR)iNextByte(pFile);
                pImg->aucPalette[iIndex][1] = (UCHAR)iNextByte(pFile);
                pImg->aucPalette[iIndex][2] = (UCHAR)iNextByte(pFile);
                NO_DBG_PRINT_BLOCK(pImg->aucPalette[iIndex], 3);
        }
        for (;iIndex < pImg->iColorsUsed; iIndex++) {
                pImg->aucPalette[iIndex][0] = 0;
                pImg->aucPalette[iIndex][1] = 0;
                pImg->aucPalette[iIndex][2] = 0;
        }

        return TRUE;
} /* end of bFillPalettePNG */

/*
 * bExaminePNG - Examine a PNG header
 *
 * return TRUE if successful, otherwise FALSE
 */
static BOOL
bExaminePNG(FILE *pFile, imagedata_type *pImg)
{
        size_t          tLength;
        ULONG           ulLong1, ulLong2, ulName;
        int             iIndex, iTmp;
        int             iCompressionMethod, iFilterMethod, iInterlaceMethod;
        int             iColor, iIncrement;
        BOOL            bHasPalette, bHasAlpha;
        UCHAR   aucBuf[4];

        /* Check signature */
        ulLong1 = ulNextLongBE(pFile);
        ulLong2 = ulNextLongBE(pFile);
        if (ulLong1 != 0x89504e47UL || ulLong2 != 0x0d0a1a0aUL) {
                DBG_HEX(ulLong1);
                DBG_HEX(ulLong2);
                return FALSE;
        }

        ulName = 0x00;
        bHasPalette = FALSE;

        /* Examine chunks */
        while (ulName != PNG_CN_IEND) {
                tLength = (size_t)ulNextLongBE(pFile);
                ulName = 0x00;
                for (iIndex = 0; iIndex < (int)elementsof(aucBuf); iIndex++) {
                        aucBuf[iIndex] = (UCHAR)iNextByte(pFile);
                        if (!isalpha(aucBuf[iIndex])) {
                                DBG_HEX(aucBuf[iIndex]);
                                return FALSE;
                        }
                        ulName <<= 8;
                        ulName |= aucBuf[iIndex];
                }

                switch (ulName) {
                case PNG_CN_IHDR:
                        /* Header chunck */
                        if (tLength < 13) {
                                DBG_DEC(tLength);
                                return FALSE;
                        }
                        pImg->iWidth = (int)ulNextLongBE(pFile);
                        pImg->iHeight = (int)ulNextLongBE(pFile);
                        pImg->uiBitsPerComponent = (UINT)iNextByte(pFile);
                        iTmp = iNextByte(pFile);
                        NO_DBG_HEX(iTmp);
                        pImg->bColorImage = (iTmp & PNG_CB_COLOR) != 0;
                        bHasPalette = (iTmp & PNG_CB_PALETTE) != 0;
                        bHasAlpha = (iTmp & PNG_CB_ALPHA) != 0;
                        if (bHasPalette && pImg->uiBitsPerComponent > 8) {
                                /* This should not happen */
                                return FALSE;
                        }
                        pImg->iComponents =
                                (bHasPalette || !pImg->bColorImage) ? 1 : 3;
                        if (bHasAlpha) {
                                pImg->iComponents++;
                        }
                        iCompressionMethod = iNextByte(pFile);
                        if (iCompressionMethod != 0) {
                                DBG_DEC(iCompressionMethod);
                                return FALSE;
                        }
                        iFilterMethod = iNextByte(pFile);
                        if (iFilterMethod != 0) {
                                DBG_DEC(iFilterMethod);
                                return FALSE;
                        }
                        iInterlaceMethod = iNextByte(pFile);
                        if (iInterlaceMethod != 0) {
                                DBG_DEC(iInterlaceMethod);
                                return FALSE;
                        }
                        pImg->iColorsUsed = 0;
                        (void)tSkipBytes(pFile, tLength - 13 + 4);
                        break;
                case PNG_CN_PLTE:
                        if (!bHasPalette) {
                                return FALSE;
                        }
                        if (!bFillPalettePNG(pFile, pImg, tLength)) {
                                return FALSE;
                        }
                        (void)tSkipBytes(pFile, 4);
                        break;
                default:
                        (void)tSkipBytes(pFile, tLength + 4);
                        break;
                }
        }

        DBG_DEC(pImg->iWidth);
        DBG_DEC(pImg->iHeight);
        DBG_DEC(pImg->uiBitsPerComponent);
        DBG_DEC(pImg->iColorsUsed);
        DBG_DEC(pImg->iComponents);

        /* Do some sanity checks with the parameters */
        if (pImg->iWidth <= 0 || pImg->iHeight <= 0) {
                return FALSE;
        }

        if (pImg->uiBitsPerComponent != 1 && pImg->uiBitsPerComponent != 2 &&
            pImg->uiBitsPerComponent != 4 && pImg->uiBitsPerComponent != 8 &&
            pImg->uiBitsPerComponent != 16) {
                DBG_DEC(pImg->uiBitsPerComponent);
                return  FALSE;
        }

        if (pImg->iComponents != 1 && pImg->iComponents != 3) {
                /* Not supported */
                DBG_DEC(pImg->iComponents);
                return FALSE;
        }

        if (pImg->uiBitsPerComponent > 8) {
                /* Not supported */
                DBG_DEC(pImg->uiBitsPerComponent);
                return FALSE;
        }

        if (pImg->iColorsUsed == 0 &&
            pImg->iComponents == 1 &&
            pImg->uiBitsPerComponent <= 4) {
                /*
                 * No palette is supplied, but PostScript needs one in these
                 * cases, so we add a default palette here
                 */
                pImg->iColorsUsed = 1 << pImg->uiBitsPerComponent;
                iIncrement = 0xff / (pImg->iColorsUsed - 1);
                for (iIndex = 0, iColor = 0x00;
                     iIndex < pImg->iColorsUsed;
                     iIndex++, iColor += iIncrement) {
                        pImg->aucPalette[iIndex][0] = (UCHAR)iColor;
                        pImg->aucPalette[iIndex][1] = (UCHAR)iColor;
                        pImg->aucPalette[iIndex][2] = (UCHAR)iColor;
                }
                /* Just to be sure */
                pImg->bColorImage = FALSE;
        }

        pImg->eCompression = compression_zlib;

        return TRUE;
} /* end of bExaminePNG */

/*
 * bExamineWMF - Examine a WMF header
 *
 * return TRUE if successful, otherwise FALSE
 */
static BOOL
bExamineWMF(FILE *pFile, imagedata_type *pImg)
{
        ULONG   ulFileSize, ulMaxRecord, ulMagic;
        USHORT  usType, usHeaderSize, usVersion, usNoObjects;

        usType = usNextWord(pFile);
        usHeaderSize = usNextWord(pFile);
        ulMagic = ((ULONG)usHeaderSize << 16) | (ULONG)usType;
        usVersion = usNextWord(pFile);
        ulFileSize = ulNextLong(pFile);
        usNoObjects = usNextWord(pFile);
        ulMaxRecord = ulNextLong(pFile);

        DBG_HEX(ulMagic);
        DBG_DEC(usType);
        DBG_DEC(usHeaderSize);
        DBG_HEX(usVersion);
        DBG_DEC(ulFileSize);
        DBG_DEC(usNoObjects);
        DBG_DEC(ulMaxRecord);

        return FALSE;
} /* end of bExamineWMF */

#if !defined(__riscos)
/*
 * vImage2Papersize - make sure the image fits on the paper
 *
 * This function should not be needed if Word would do a proper job
 */
static void
vImage2Papersize(imagedata_type *pImg)
{
        static int      iNetPageHeight = -1;
        static int      iNetPageWidth = -1;
        options_type    tOptions;
        double  dVerFactor, dHorFactor, dFactor;

        DBG_MSG("vImage2Papersize");

        fail(pImg == NULL);

        if (iNetPageHeight < 0 || iNetPageWidth < 0) {
                /* Get the page dimensions from the options */
                vGetOptions(&tOptions);
                /* Add 999 to err on the save side */
                iNetPageHeight = tOptions.iPageHeight -
                                (lDrawUnits2MilliPoints(
                                        PS_TOP_MARGIN + PS_BOTTOM_MARGIN) +
                                        999) / 1000;
                iNetPageWidth = tOptions.iPageWidth -
                                (lDrawUnits2MilliPoints(
                                        PS_LEFT_MARGIN + PS_RIGHT_MARGIN) +
                                        999) / 1000;
                DBG_DEC(iNetPageHeight);
                DBG_DEC(iNetPageWidth);
        }

        if (pImg->iVerSizeScaled < iNetPageHeight &&
            pImg->iHorSizeScaled < iNetPageWidth) {
                /* The image fits on the paper */
                return;
        }

        dVerFactor = (double)iNetPageHeight / (double)pImg->iVerSizeScaled;
        dHorFactor = (double)iNetPageWidth / (double)pImg->iHorSizeScaled;
        dFactor = min(dVerFactor, dHorFactor);
        DBG_FLT(dFactor);
        /* Round down, just to be on the save side */
        pImg->iVerSizeScaled = (int)(pImg->iVerSizeScaled * dFactor);
        pImg->iHorSizeScaled = (int)(pImg->iHorSizeScaled * dFactor);
} /* end of vImage2Papersize */
#endif /* !__riscos */

/*
 * tFind6Image - skip until the image is found
 *
 * Find the image in Word 6/7 files
 *
 * returns the new position when a image is found, otherwise -1
 */
static size_t
tFind6Image(FILE *pFile, size_t tPosition, size_t tLength,
        imagetype_enum *peImageType)
{
        ULONG   ulMarker;
        size_t  tRecordLength, tToSkip;
        USHORT  usMarker;

        fail(pFile == NULL);
        fail(peImageType == NULL);

        *peImageType = imagetype_is_unknown;
        if (tPosition + 18 >= tLength) {
                return (size_t)-1;
        }

        ulMarker = ulNextLong(pFile);
        if (ulMarker != 0x00090001) {
                DBG_HEX(ulMarker);
                return (size_t)-1;
        }
        usMarker = usNextWord(pFile);
        if (usMarker != 0x0300) {
                DBG_HEX(usMarker);
                return (size_t)-1;
        }
        (void)tSkipBytes(pFile, 10);
        usMarker = usNextWord(pFile);
        if (usMarker != 0x0000) {
                DBG_HEX(usMarker);
                return (size_t)-1;
        }
        tPosition += 18;

        while (tPosition + 6 <= tLength) {
                tRecordLength = (size_t)ulNextLong(pFile);
                usMarker = usNextWord(pFile);
                tPosition += 6;
                NO_DBG_DEC(tRecordLength);
                NO_DBG_HEX(usMarker);
                switch (usMarker) {
                case 0x0000:
                        DBG_HEX(ulGetDataOffset(pFile));
                        return (size_t)-1;
                case 0x0b41:
                        DBG_MSG("DIB");
                        *peImageType = imagetype_is_dib;
                        tPosition += tSkipBytes(pFile, 20);
                        return tPosition;
                case 0x0f43:
                        DBG_MSG("DIB");
                        *peImageType = imagetype_is_dib;
                        tPosition += tSkipBytes(pFile, 22);
                        return tPosition;
                default:
                        if (tRecordLength < 3) {
                                break;
                        }
                        if (tRecordLength > SIZE_T_MAX / 2) {
                                /*
                                 * No need to compute the number of bytes
                                 * to skip
                                 */
                                DBG_DEC(tRecordLength);
                                DBG_HEX(tRecordLength);
                                DBG_FIXME();
                                return (size_t)-1;
                        }
                        tToSkip = tRecordLength * 2 - 6;
                        if (tToSkip > tLength - tPosition) {
                                /* You can't skip this number of bytes */
                                DBG_DEC(tToSkip);
                                DBG_DEC(tLength - tPosition);
                                return (size_t)-1;
                        }
                        tPosition += tSkipBytes(pFile, tToSkip);
                        break;
                }
        }

        return (size_t)-1;
} /* end of tFind6Image */

/*
 * tFind8Image - skip until the image is found
 *
 * Find the image in Word 8/9/10 files
 *
 * returns the new position when a image is found, otherwise -1
 */
static size_t
tFind8Image(FILE *pFile, size_t tPosition, size_t tLength,
        imagetype_enum *peImageType)
{
        size_t  tRecordLength, tNameLen;
        USHORT  usRecordVersion, usRecordType, usRecordInstance;
        USHORT  usTmp;

        fail(pFile == NULL);
        fail(peImageType == NULL);

        *peImageType = imagetype_is_unknown;
        while (tPosition + 8 <= tLength) {
                usTmp = usNextWord(pFile);
                usRecordVersion = usTmp & 0x000f;
                usRecordInstance = usTmp >> 4;
                usRecordType = usNextWord(pFile);
                tRecordLength = (size_t)ulNextLong(pFile);
                tPosition += 8;
                NO_DBG_HEX(usRecordVersion);
                NO_DBG_HEX(usRecordInstance);
                NO_DBG_HEX(usRecordType);
                NO_DBG_DEC(tRecordLength);
                switch (usRecordType) {
                case 0xf000: case 0xf001: case 0xf002: case 0xf003:
                case 0xf004: case 0xf005:
                        break;
                case 0xf007:
                        tPosition += tSkipBytes(pFile, 33);
                        tNameLen = (size_t)iNextByte(pFile);
                        tPosition++;
                        DBG_DEC_C(tNameLen != 0, tNameLen);
                        tPosition += tSkipBytes(pFile, 2 + tNameLen * 2);
                        break;
                case 0xf008:
                        tPosition += tSkipBytes(pFile, 8);
                        break;
                case 0xf009:
                        tPosition += tSkipBytes(pFile, 16);
                        break;
                case 0xf006: case 0xf00a: case 0xf00b: case 0xf00d:
                case 0xf00e: case 0xf00f: case 0xf010: case 0xf011:
                case 0xf122:
                        tPosition += tSkipBytes(pFile, tRecordLength);
                        break;
                case 0xf01a:
                        DBG_MSG("EMF");
                        *peImageType = imagetype_is_emf;
                        tPosition += tSkipBytes(pFile, 50);
                        if ((usRecordInstance ^ MSOBI_EMF) == 1) {
                                tPosition += tSkipBytes(pFile, 16);
                        }
                        return tPosition;
                case 0xf01b:
                        DBG_MSG("WMF");
                        *peImageType = imagetype_is_wmf;
                        tPosition += tSkipBytes(pFile, 50);
                        if ((usRecordInstance ^ MSOBI_WMF) == 1) {
                                tPosition += tSkipBytes(pFile, 16);
                        }
                        return tPosition;
                case 0xf01c:
                        DBG_MSG("PICT");
                        *peImageType = imagetype_is_pict;
                        tPosition += tSkipBytes(pFile, 50);
                        if ((usRecordInstance ^ MSOBI_PICT) == 1) {
                                tPosition += tSkipBytes(pFile, 16);
                        }
                        return tPosition;
                case 0xf01d:
                        DBG_MSG("JPEG");
                        *peImageType = imagetype_is_jpeg;
                        tPosition += tSkipBytes(pFile, 17);
                        if ((usRecordInstance ^ MSOBI_JPEG) == 1) {
                                tPosition += tSkipBytes(pFile, 16);
                        }
                        return tPosition;
                case 0xf01e:
                        DBG_MSG("PNG");
                        *peImageType = imagetype_is_png;
                        tPosition += tSkipBytes(pFile, 17);
                        if ((usRecordInstance ^ MSOBI_PNG) == 1) {
                                tPosition += tSkipBytes(pFile, 16);
                        }
                        return tPosition;
                case 0xf01f:
                        DBG_MSG("DIB");
                        /* DIB is a BMP minus its 14 byte header */
                        *peImageType = imagetype_is_dib;
                        tPosition += tSkipBytes(pFile, 17);
                        if ((usRecordInstance ^ MSOBI_DIB) == 1) {
                                tPosition += tSkipBytes(pFile, 16);
                        }
                        return tPosition;
                case 0xf00c:
                default:
                        DBG_HEX(usRecordType);
                        DBG_DEC_C(tRecordLength % 4 != 0, tRecordLength);
                        DBG_FIXME();
                        return (size_t)-1;
                }
        }

        return (size_t)-1;
} /* end of tFind8Image */

/*
 * eExamineImage - Examine the image
 *
 * Returns an indication of the amount of information found
 */
image_info_enum
eExamineImage(FILE *pFile, ULONG ulFileOffsetImage, imagedata_type *pImg)
{
        long    lTmp;
        size_t  tWordHeaderLen, tLength, tPos;
        int     iType, iHorSize, iVerSize;
        USHORT  usHorScalingFactor, usVerScalingFactor;

        if (ulFileOffsetImage == FC_INVALID) {
                return image_no_information;
        }
        DBG_HEX(ulFileOffsetImage);

        if (!bSetDataOffset(pFile, ulFileOffsetImage)) {
                return image_no_information;
        }

        tLength = (size_t)ulNextLong(pFile);
        DBG_DEC(tLength);
        if (tLength < 46) {
                /* Smaller than the smallest known header */
                DBG_FIXME();
                return image_no_information;
        }
        tWordHeaderLen = (size_t)usNextWord(pFile);
        DBG_DEC(tWordHeaderLen);
        fail(tWordHeaderLen != 46 &&
                tWordHeaderLen != 58 &&
                tWordHeaderLen != 68);

        if (tLength < tWordHeaderLen) {
                /* Smaller than the current header */
                return image_no_information;
        }
        iType = (int)usNextWord(pFile);
        DBG_DEC(iType);
        (void)tSkipBytes(pFile, 28 - 8);

        lTmp = lTwips2MilliPoints(usNextWord(pFile));
        iHorSize = (int)(lTmp / 1000);
        if (lTmp % 1000 != 0) {
                iHorSize++;
        }
        DBG_DEC(iHorSize);
        lTmp = lTwips2MilliPoints(usNextWord(pFile));
        iVerSize = (int)(lTmp / 1000);
        if (lTmp % 1000 != 0) {
                iVerSize++;
        }
        DBG_DEC(iVerSize);

        usHorScalingFactor = usNextWord(pFile);
        DBG_DEC(usHorScalingFactor);
        usVerScalingFactor = usNextWord(pFile);
        DBG_DEC(usVerScalingFactor);

        /* Sanity checks */
        lTmp = (long)iHorSize * (long)usHorScalingFactor;
        if (lTmp < 2835) {
                /* This image would be less than 1 millimeter wide */
                DBG_DEC(lTmp);
                return image_no_information;
        }
        lTmp = (long)iVerSize * (long)usVerScalingFactor;
        if (lTmp < 2835) {
                /* This image would be less than 1 millimeter high */
                DBG_DEC(lTmp);
                return image_no_information;
        }

        /* Skip the rest of the header */
        (void)tSkipBytes(pFile, tWordHeaderLen - 36);
        tPos = tWordHeaderLen;

        (void)memset(pImg, 0, sizeof(*pImg));

        switch (iType) {
        case   7:
        case   8:
                tPos = tFind6Image(pFile, tPos, tLength, &pImg->eImageType);
                if (tPos == (size_t)-1) {
                        /* No image found */
                        return image_no_information;
                }
                DBG_HEX(tPos);
                break;
        case  94:       /* Word 6/7, no image just a pathname */
                pImg->eImageType = imagetype_is_external;
                DBG_HEX(ulFileOffsetImage + tPos);
                break;
        case 100:
                tPos = tFind8Image(pFile, tPos, tLength, &pImg->eImageType);
                if (tPos == (size_t)-1) {
                        /* No image found */
                        return image_no_information;
                }
                DBG_HEX(tPos);
                break;
        case 102:       /* Word 8/9/10, no image just a pathname or URL */
                pImg->eImageType = imagetype_is_external;
                DBG_HEX(ulFileOffsetImage + tPos);
                break;
        default:
                DBG_DEC(iType);
                DBG_HEX(ulFileOffsetImage + tPos);
                DBG_FIXME();
                return image_no_information;
        }

        /* Minimal information is now available */
        pImg->tLength = tLength;
        pImg->tPosition = tPos;
        pImg->iHorSizeScaled =
                (int)(((long)iHorSize * (long)usHorScalingFactor + 500) / 1000);
        pImg->iVerSizeScaled =
                (int)(((long)iVerSize * (long)usVerScalingFactor + 500) / 1000);
#if !defined(__riscos)
        vImage2Papersize(pImg);
#endif /* !__riscos */

        /* Image type specific examinations */
        switch (pImg->eImageType) {
        case imagetype_is_dib:
                if (bExamineDIB(pFile, pImg)) {
                        return image_full_information;
                }
                return image_minimal_information;
        case imagetype_is_jpeg:
                if (bExamineJPEG(pFile, pImg)) {
                        return image_full_information;
                }
                return image_minimal_information;
        case imagetype_is_png:
                if (bExaminePNG(pFile, pImg)) {
                        return image_full_information;
                }
                return image_minimal_information;
        case imagetype_is_wmf:
                if (bExamineWMF(pFile, pImg)) {
                        return image_full_information;
                }
                return image_minimal_information;
        case imagetype_is_emf:
        case imagetype_is_pict:
        case imagetype_is_external:
                return image_minimal_information;
        case imagetype_is_unknown:
        default:
                return image_no_information;
        }
} /* end of eExamineImage */