/*
* 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 ([email protected])
*================================================================
* 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 */
|