/*
 * 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
#if CR3_ANTIWORD_PATCH==1
                 && pImg->iComponents != 2  && pImg->iComponents != 4
#endif
                ) {
		/* 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 */
