mirror of
https://github.com/simon987/antiword.git
synced 2025-04-10 13:06:41 +00:00
1045 lines
26 KiB
C
1045 lines
26 KiB
C
/*
|
|
* 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 */
|