mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3283 lines
80 KiB
3283 lines
80 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
tifflib.c
|
|
|
|
Abstract:
|
|
|
|
This file contains all of the public TIFF library functions.
|
|
The following functions are available from this library:
|
|
|
|
o TiffCreate Creates a new TIFF file
|
|
o TiffOpen Opens an existing TIFF file
|
|
o TiffClose Closes a previously open or created TIFF file
|
|
o TiffStartPage Starts a new page for writing
|
|
o TiffEndPage Ends a page for writing
|
|
o TiffWrite Writes a line of TIFF data
|
|
o TiffWriteRaw Writes a line of TIFF data with no-encoding
|
|
o TiffRead Reads a page of TIFF data
|
|
o TiffSeekToPage Positions to a page for reading
|
|
|
|
This library can be used anywhere in user mode and is thread
|
|
safe for multithreaded apps.
|
|
|
|
The encoding methods implemented in this library are coded
|
|
to the ITU specification labeled T.4 03/93.
|
|
|
|
|
|
Environment:
|
|
|
|
WIN32 User Mode
|
|
|
|
|
|
Author:
|
|
|
|
Wesley Witt (wesw) 17-Feb-1996
|
|
|
|
--*/
|
|
|
|
|
|
#include "tifflibp.h"
|
|
#pragma hdrstop
|
|
|
|
#include "fasttiff.h"
|
|
|
|
DWORD TiffDataWidth[] = {
|
|
0, // nothing
|
|
1, // TIFF_BYTE
|
|
1, // TIFF_ASCII
|
|
2, // TIFF_SHORT
|
|
4, // TIFF_LONG
|
|
8, // TIFF_RATIONAL
|
|
1, // TIFF_SBYTE
|
|
1, // TIFF_UNDEFINED
|
|
2, // TIFF_SSHORT
|
|
4, // TIFF_SLONG
|
|
8, // TIFF_SRATIONAL
|
|
4, // TIFF_FLOAT
|
|
8 // TIFF_DOUBLE
|
|
};
|
|
|
|
//
|
|
// IFD template for creating a new TIFF data page
|
|
//
|
|
|
|
FAXIFD FaxIFDTemplate = {
|
|
|
|
NUM_IFD_ENTRIES,
|
|
|
|
{
|
|
{ TIFFTAG_SUBFILETYPE, TIFF_LONG, 1, FILETYPE_PAGE }, // 254
|
|
{ TIFFTAG_IMAGEWIDTH, TIFF_LONG, 1, 0 }, // 256
|
|
{ TIFFTAG_IMAGELENGTH, TIFF_LONG, 1, 0 }, // 257
|
|
{ TIFFTAG_BITSPERSAMPLE, TIFF_SHORT, 1, 1 }, // 258
|
|
{ TIFFTAG_COMPRESSION, TIFF_SHORT, 1, 0 }, // 259
|
|
{ TIFFTAG_PHOTOMETRIC, TIFF_SHORT, 1, PHOTOMETRIC_MINISWHITE }, // 262
|
|
{ TIFFTAG_FILLORDER, TIFF_SHORT, 1, FILLORDER_LSB2MSB }, // 266
|
|
{ TIFFTAG_STRIPOFFSETS, TIFF_LONG, 1, 0 }, // 273
|
|
{ TIFFTAG_SAMPLESPERPIXEL, TIFF_SHORT, 1, 1 }, // 277
|
|
{ TIFFTAG_ROWSPERSTRIP, TIFF_LONG, 1, 0 }, // 278
|
|
{ TIFFTAG_STRIPBYTECOUNTS, TIFF_LONG, 1, 0 }, // 279
|
|
{ TIFFTAG_XRESOLUTION, TIFF_RATIONAL, 1, 0 }, // 281
|
|
{ TIFFTAG_YRESOLUTION, TIFF_RATIONAL, 1, 0 }, // 282
|
|
{ TIFFTAG_GROUP3OPTIONS, TIFF_LONG, 1, 0 }, // 292
|
|
{ TIFFTAG_RESOLUTIONUNIT, TIFF_SHORT, 1, RESUNIT_INCH }, // 296
|
|
{ TIFFTAG_PAGENUMBER, TIFF_SHORT, 2, 0 }, // 297
|
|
{ TIFFTAG_SOFTWARE, TIFF_ASCII, SOFTWARE_STR_LEN, 0 }, // 305
|
|
{ TIFFTAG_CLEANFAXDATA, TIFF_SHORT, 1, 0 }, // 327
|
|
{ TIFFTAG_CONSECUTIVEBADFAXLINES, TIFF_SHORT, 1, 0 } // 328
|
|
},
|
|
|
|
0,
|
|
SERVICE_SIGNATURE,
|
|
TIFFF_RES_X,
|
|
1,
|
|
TIFFF_RES_Y,
|
|
1,
|
|
SOFTWARE_STR
|
|
};
|
|
|
|
|
|
|
|
DWORD
|
|
FaxTiffDllInit(
|
|
HINSTANCE hInstance,
|
|
DWORD Reason,
|
|
LPVOID Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
DLL initialization function.
|
|
|
|
Arguments:
|
|
|
|
hInstance - Instance handle
|
|
Reason - Reason for the entrypoint being called
|
|
Context - Context record
|
|
|
|
Return Value:
|
|
|
|
TRUE - Initialization succeeded
|
|
FALSE - Initialization failed
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
extern DWORD GlobFaxTiffUsage;
|
|
extern BOOL GlobFaxTiffInitialized;
|
|
|
|
if (Reason == DLL_PROCESS_ATTACH) {
|
|
BuildLookupTables(15);
|
|
GlobFaxTiffInitialized = TRUE;
|
|
InterlockedIncrement(&GlobFaxTiffUsage);
|
|
|
|
}
|
|
|
|
if (Reason == DLL_PROCESS_DETACH) {
|
|
|
|
InterlockedDecrement(&GlobFaxTiffUsage);
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
FaxTiffInitialize()
|
|
{
|
|
HeapInitialize(NULL,NULL,NULL,0);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
HANDLE
|
|
TiffCreate(
|
|
LPTSTR FileName,
|
|
DWORD CompressionType,
|
|
DWORD ImageWidth,
|
|
DWORD FillOrder,
|
|
DWORD HiRes
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a new TIFF file. The act of creating a new
|
|
file requires more than just opening the file. The
|
|
TIFF header is written and instance data is initialized
|
|
for further operations on the new file.
|
|
|
|
If FileName is NULL, no file is created. This is used to
|
|
to in memory decoding/encoding.
|
|
|
|
Arguments:
|
|
|
|
FileName - Full or partial path/file name
|
|
CompressionType - Requested compression type, see tifflib.h
|
|
ImageWidth - Width of the image in pixels
|
|
|
|
Return Value:
|
|
|
|
Handle to the new TIFF file or NULL on error.
|
|
|
|
--*/
|
|
|
|
{
|
|
PTIFF_INSTANCE_DATA TiffInstance;
|
|
DWORD Bytes;
|
|
|
|
|
|
|
|
TiffInstance = MemAlloc( sizeof(TIFF_INSTANCE_DATA) );
|
|
if (!TiffInstance) {
|
|
return NULL;
|
|
}
|
|
|
|
if (FileName != NULL) {
|
|
|
|
TiffInstance->hFile = CreateFile(
|
|
FileName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
0,
|
|
NULL
|
|
);
|
|
if (TiffInstance->hFile == INVALID_HANDLE_VALUE) {
|
|
return NULL;
|
|
}
|
|
|
|
} else {
|
|
|
|
TiffInstance->hFile = INVALID_HANDLE_VALUE;
|
|
|
|
}
|
|
|
|
if (FileName) {
|
|
_tcscpy( TiffInstance->FileName, FileName );
|
|
}
|
|
|
|
TiffInstance->TiffHdr.Identifier = 0x4949;
|
|
TiffInstance->TiffHdr.Version = 0x2a;
|
|
TiffInstance->TiffHdr.IFDOffset = 0;
|
|
TiffInstance->PageCount = 0;
|
|
TiffInstance->DataOffset = 0;
|
|
TiffInstance->IfdOffset = FIELD_OFFSET( TIFF_HEADER, IFDOffset );
|
|
TiffInstance->CompressionType = CompressionType;
|
|
TiffInstance->bitdata = 0;
|
|
TiffInstance->bitcnt = DWORDBITS;
|
|
TiffInstance->ImageWidth = ImageWidth;
|
|
TiffInstance->FillOrder = FillOrder;
|
|
|
|
if (HiRes) {
|
|
TiffInstance->YResolution = 196;
|
|
}
|
|
else {
|
|
TiffInstance->YResolution = 98;
|
|
}
|
|
|
|
|
|
FillMemory( TiffInstance->Buffer, sizeof(TiffInstance->Buffer), WHITE );
|
|
|
|
TiffInstance->RefLine = &TiffInstance->Buffer[0];
|
|
TiffInstance->CurrLine = &TiffInstance->Buffer[FAXBYTES];
|
|
TiffInstance->bitbuf = &TiffInstance->Buffer[FAXBYTES];
|
|
|
|
CopyMemory( &TiffInstance->TiffIfd, &FaxIFDTemplate, sizeof(FaxIFDTemplate) );
|
|
|
|
if (TiffInstance->hFile != INVALID_HANDLE_VALUE) {
|
|
if (!WriteFile(
|
|
TiffInstance->hFile,
|
|
&TiffInstance->TiffHdr,
|
|
sizeof(TIFF_HEADER),
|
|
&Bytes,
|
|
NULL
|
|
)) {
|
|
CloseHandle( TiffInstance->hFile );
|
|
DeleteFile( FileName );
|
|
MemFree( TiffInstance );
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return TiffInstance;
|
|
}
|
|
|
|
|
|
HANDLE
|
|
TiffOpen(
|
|
LPTSTR FileName,
|
|
PTIFF_INFO TiffInfo,
|
|
BOOL ReadOnly,
|
|
DWORD RequestedFillOrder
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Opens an existing TIFF file for reading.
|
|
|
|
Arguments:
|
|
|
|
FileName - Full or partial path/file name
|
|
ImageWidth - Optionaly receives the image width in pixels
|
|
ImageLength - Optionaly receives the image height in lines
|
|
PageCount - Optionaly receives the page count
|
|
|
|
Return Value:
|
|
|
|
Handle to the open TIFF file or NULL on error.
|
|
|
|
--*/
|
|
|
|
{
|
|
PTIFF_INSTANCE_DATA TiffInstance = NULL;
|
|
WORD NumDirEntries;
|
|
DWORD IFDOffset;
|
|
|
|
|
|
TiffInstance = MemAlloc( sizeof(TIFF_INSTANCE_DATA) );
|
|
if (!TiffInstance) {
|
|
goto error_exit;
|
|
}
|
|
|
|
TiffInstance->hFile = CreateFile(
|
|
FileName,
|
|
ReadOnly ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE),
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if (TiffInstance->hFile == INVALID_HANDLE_VALUE) {
|
|
goto error_exit;
|
|
}
|
|
|
|
TiffInstance->FileSize = GetFileSize(TiffInstance->hFile,NULL);
|
|
if (TiffInstance->FileSize == 0xFFFFFFFF ||
|
|
TiffInstance->FileSize < sizeof(TIFF_HEADER)) {
|
|
goto error_exit;
|
|
}
|
|
|
|
TiffInstance->hMap = CreateFileMapping(
|
|
TiffInstance->hFile,
|
|
NULL,
|
|
ReadOnly ? (PAGE_READONLY | SEC_COMMIT) : (PAGE_READWRITE | SEC_COMMIT),
|
|
0,
|
|
0,
|
|
NULL
|
|
);
|
|
if (!TiffInstance->hMap) {
|
|
goto error_exit;
|
|
}
|
|
|
|
TiffInstance->fPtr = MapViewOfFile(
|
|
TiffInstance->hMap,
|
|
ReadOnly ? FILE_MAP_READ : (FILE_MAP_READ | FILE_MAP_WRITE),
|
|
0,
|
|
0,
|
|
0
|
|
);
|
|
if (!TiffInstance->fPtr) {
|
|
goto error_exit;
|
|
}
|
|
|
|
_tcscpy( TiffInstance->FileName, FileName );
|
|
|
|
//
|
|
// read in the TIFF header
|
|
//
|
|
CopyMemory(
|
|
&TiffInstance->TiffHdr,
|
|
TiffInstance->fPtr,
|
|
sizeof(TIFF_HEADER)
|
|
);
|
|
|
|
//
|
|
// validate that the file is really a TIFF file
|
|
//
|
|
if ((TiffInstance->TiffHdr.Identifier != TIFF_LITTLEENDIAN) ||
|
|
(TiffInstance->TiffHdr.Version != TIFF_VERSION)) {
|
|
goto error_exit;
|
|
}
|
|
|
|
IFDOffset = TiffInstance->TiffHdr.IFDOffset;
|
|
if (IFDOffset > TiffInstance->FileSize) {
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// walk the IFD list to count the number of pages
|
|
//
|
|
|
|
while ( IFDOffset ) {
|
|
|
|
//
|
|
// get the count of tags in this IFD
|
|
//
|
|
NumDirEntries = *(LPWORD)(TiffInstance->fPtr + IFDOffset);
|
|
|
|
//
|
|
// get the next IFD offset
|
|
//
|
|
IFDOffset = *(UNALIGNED DWORD *)(TiffInstance->fPtr + (NumDirEntries * sizeof(TIFF_TAG)) + IFDOffset + sizeof(WORD));
|
|
if (IFDOffset > TiffInstance->FileSize) {
|
|
goto error_exit;
|
|
}
|
|
//
|
|
// increment the page counter
|
|
//
|
|
TiffInstance->PageCount += 1;
|
|
|
|
}
|
|
|
|
TiffInstance->IfdOffset = TiffInstance->TiffHdr.IFDOffset;
|
|
TiffInstance->FillOrder = RequestedFillOrder;
|
|
|
|
|
|
if (!TiffSeekToPage( TiffInstance, 1, RequestedFillOrder )) {
|
|
goto error_exit;
|
|
}
|
|
|
|
TiffInfo->PageCount = TiffInstance->PageCount;
|
|
TiffInfo->ImageWidth = TiffInstance->ImageWidth;
|
|
TiffInfo->ImageHeight = TiffInstance->ImageHeight;
|
|
TiffInfo->PhotometricInterpretation = TiffInstance->PhotometricInterpretation;
|
|
TiffInfo->FillOrder = TiffInstance->FillOrder;
|
|
TiffInfo->YResolution = TiffInstance->YResolution;
|
|
TiffInfo->CompressionType = TiffInstance->CompressionType;
|
|
TiffInstance->RefLine = &TiffInstance->Buffer[0];
|
|
TiffInstance->CurrLine = &TiffInstance->Buffer[FAXBYTES];
|
|
TiffInstance->CurrPage = 1;
|
|
|
|
FillMemory( TiffInstance->Buffer, sizeof(TiffInstance->Buffer), WHITE );
|
|
|
|
return TiffInstance;
|
|
|
|
error_exit:
|
|
if (TiffInstance && TiffInstance->hFile && TiffInstance->hFile != INVALID_HANDLE_VALUE) {
|
|
UnmapViewOfFile( TiffInstance->fPtr );
|
|
CloseHandle( TiffInstance->hMap );
|
|
CloseHandle( TiffInstance->hFile );
|
|
}
|
|
if (TiffInstance) {
|
|
MemFree( TiffInstance );
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TiffClose(
|
|
HANDLE hTiff
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Closes a TIFF file and frees all allocated resources.
|
|
|
|
Arguments:
|
|
|
|
hTiff - TIFF handle returned by TiffCreate or TiffOpen
|
|
|
|
Return Value:
|
|
|
|
TRUE for success, FALSE on error
|
|
|
|
--*/
|
|
|
|
{
|
|
PTIFF_INSTANCE_DATA TiffInstance = (PTIFF_INSTANCE_DATA) hTiff;
|
|
|
|
|
|
Assert(TiffInstance);
|
|
|
|
if (TiffInstance->StripData) {
|
|
|
|
VirtualFree(
|
|
TiffInstance->StripData,
|
|
0,
|
|
MEM_RELEASE
|
|
);
|
|
|
|
}
|
|
|
|
if (TiffInstance->hMap) {
|
|
|
|
UnmapViewOfFile( TiffInstance->fPtr );
|
|
CloseHandle( TiffInstance->hMap );
|
|
CloseHandle( TiffInstance->hFile );
|
|
|
|
} else {
|
|
|
|
if (TiffInstance->hFile != INVALID_HANDLE_VALUE)
|
|
CloseHandle( TiffInstance->hFile );
|
|
|
|
}
|
|
|
|
MemFree( TiffInstance );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
RemoveGarbage(
|
|
PTIFF_INSTANCE_DATA TiffInstance
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Removes the garbage from a page of tiff data.
|
|
|
|
Arguments:
|
|
|
|
TiffInstance - Pointer to the TIFF instance data
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
if (TiffInstance->StartGood == 0 || TiffInstance->EndGood == 0) {
|
|
return;
|
|
}
|
|
|
|
CopyMemory(
|
|
TiffInstance->fPtr + TiffInstance->StripOffset,
|
|
(LPVOID) ((LPBYTE)TiffInstance->StripData + TiffInstance->StartGood),
|
|
TiffInstance->EndGood - TiffInstance->StartGood
|
|
);
|
|
|
|
|
|
PutTagData( TiffInstance->fPtr, 0, TiffInstance->TagStripByteCounts, TiffInstance->EndGood-TiffInstance->StartGood );
|
|
PutTagData( TiffInstance->fPtr, 0, TiffInstance->TagFillOrder, FILLORDER_MSB2LSB );
|
|
if (TiffInstance->BadFaxLines) {
|
|
PutTagData( TiffInstance->fPtr, 0, TiffInstance->TagBadFaxLines, TiffInstance->BadFaxLines );
|
|
PutTagData( TiffInstance->fPtr, 0, TiffInstance->TagCleanFaxData, CLEANFAXDATA_UNCLEAN );
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
TiffPostProcess(
|
|
LPTSTR FileName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Opens an existing TIFF file for reading.
|
|
|
|
Arguments:
|
|
|
|
FileName - Full or partial path/file name
|
|
|
|
Return Value:
|
|
|
|
TRUE for success, FALSE for failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
PTIFF_INSTANCE_DATA TiffInstance;
|
|
TIFF_INFO TiffInfo;
|
|
DWORD PageCnt;
|
|
BYTE TiffData[FAXBYTES*10];
|
|
|
|
|
|
|
|
TiffInstance = (PTIFF_INSTANCE_DATA) TiffOpen(
|
|
FileName,
|
|
&TiffInfo,
|
|
FALSE,
|
|
FILLORDER_MSB2LSB
|
|
);
|
|
|
|
if (!TiffInstance) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (TiffInstance->ImageHeight) {
|
|
TiffClose( (HANDLE) TiffInstance );
|
|
return TRUE;
|
|
}
|
|
|
|
ZeroMemory( TiffData, sizeof(TiffData) );
|
|
|
|
for (PageCnt=0; PageCnt<TiffInfo.PageCount; PageCnt++) {
|
|
|
|
if (!TiffSeekToPage( (HANDLE) TiffInstance, PageCnt+1, FILLORDER_MSB2LSB )) {
|
|
TiffClose( (HANDLE) TiffInstance );
|
|
return TRUE;
|
|
}
|
|
|
|
switch( TiffInstance->CompressionType ) {
|
|
|
|
case TIFF_COMPRESSION_NONE:
|
|
|
|
if (!DecodeUnCompressedFaxData( TiffInstance, TiffData, TRUE, 0 )) {
|
|
TiffClose( (HANDLE) TiffInstance );
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
case TIFF_COMPRESSION_MH:
|
|
|
|
if (!DecodeMHFaxData( TiffInstance, TiffData, TRUE, 0 )) {
|
|
TiffClose( (HANDLE) TiffInstance );
|
|
return FALSE;
|
|
}
|
|
RemoveGarbage( TiffInstance );
|
|
break;
|
|
|
|
case TIFF_COMPRESSION_MR:
|
|
|
|
if (!DecodeMRFaxData( TiffInstance, TiffData, TRUE, 0 )) {
|
|
TiffClose( (HANDLE) TiffInstance );
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
case TIFF_COMPRESSION_MMR:
|
|
|
|
if (!DecodeMMRFaxData( TiffInstance, TiffData, TRUE, 0 )) {
|
|
TiffClose( (HANDLE) TiffInstance );
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
PutTagData( TiffInstance->fPtr, 0, TiffInstance->TagRowsPerStrip, TiffInstance->Lines );
|
|
PutTagData( TiffInstance->fPtr, 0, TiffInstance->TagImageLength, TiffInstance->Lines );
|
|
|
|
}
|
|
|
|
TiffClose( (HANDLE) TiffInstance );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TiffStartPage(
|
|
HANDLE hTiff
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the file to be ready to write TIFF data to a new page.
|
|
|
|
Arguments:
|
|
|
|
hTiff - TIFF handle returned by TiffCreate or TiffOpen
|
|
|
|
Return Value:
|
|
|
|
TRUE for success, FALSE on error
|
|
|
|
--*/
|
|
|
|
{
|
|
PTIFF_INSTANCE_DATA TiffInstance = (PTIFF_INSTANCE_DATA) hTiff;
|
|
|
|
|
|
Assert(TiffInstance);
|
|
|
|
TiffInstance->DataOffset = SetFilePointer(
|
|
TiffInstance->hFile,
|
|
0,
|
|
NULL,
|
|
FILE_CURRENT
|
|
);
|
|
if (TiffInstance->DataOffset == 0xffffffff) {
|
|
TiffInstance->DataOffset = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TiffEndPage(
|
|
HANDLE hTiff
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Ends a TIFF page in progress. This causes the IFDs to be written.
|
|
|
|
Arguments:
|
|
|
|
hTiff - TIFF handle returned by TiffCreate or TiffOpen
|
|
|
|
Return Value:
|
|
|
|
TRUE for success, FALSE on error
|
|
|
|
--*/
|
|
|
|
{
|
|
PTIFF_INSTANCE_DATA TiffInstance = (PTIFF_INSTANCE_DATA) hTiff;
|
|
PFAXIFD TiffIfd;
|
|
DWORD Bytes;
|
|
DWORD CurrOffset;
|
|
|
|
|
|
Assert(TiffInstance);
|
|
TiffIfd = &TiffInstance->TiffIfd;
|
|
|
|
CurrOffset = SetFilePointer(
|
|
TiffInstance->hFile,
|
|
0,
|
|
NULL,
|
|
FILE_CURRENT
|
|
);
|
|
|
|
CurrOffset = Align( 8, CurrOffset );
|
|
|
|
SetFilePointer(
|
|
TiffInstance->hFile,
|
|
TiffInstance->IfdOffset,
|
|
NULL,
|
|
FILE_BEGIN
|
|
);
|
|
|
|
WriteFile(
|
|
TiffInstance->hFile,
|
|
&CurrOffset,
|
|
sizeof(CurrOffset),
|
|
&Bytes,
|
|
NULL
|
|
);
|
|
|
|
SetFilePointer(
|
|
TiffInstance->hFile,
|
|
CurrOffset,
|
|
NULL,
|
|
FILE_BEGIN
|
|
);
|
|
|
|
TiffInstance->PageCount += 1;
|
|
|
|
TiffIfd->yresNum = TiffInstance->YResolution;
|
|
|
|
TiffIfd->ifd[IFD_PAGENUMBER].value = MAKELONG( TiffInstance->PageCount-1, 0);
|
|
TiffIfd->ifd[IFD_IMAGEWIDTH].value = TiffInstance->ImageWidth;
|
|
TiffIfd->ifd[IFD_IMAGEHEIGHT].value = TiffInstance->Lines;
|
|
TiffIfd->ifd[IFD_ROWSPERSTRIP].value = TiffInstance->Lines;
|
|
TiffIfd->ifd[IFD_STRIPBYTECOUNTS].value = TiffInstance->Bytes;
|
|
TiffIfd->ifd[IFD_STRIPOFFSETS].value = TiffInstance->DataOffset;
|
|
TiffIfd->ifd[IFD_XRESOLUTION].value = CurrOffset + FIELD_OFFSET( FAXIFD, xresNum );
|
|
TiffIfd->ifd[IFD_YRESOLUTION].value = CurrOffset + FIELD_OFFSET( FAXIFD, yresNum );
|
|
TiffIfd->ifd[IFD_SOFTWARE].value = CurrOffset + FIELD_OFFSET( FAXIFD, software );
|
|
TiffIfd->ifd[IFD_FILLORDER].value = TiffInstance->FillOrder;
|
|
|
|
if (TiffInstance->CompressionType == TIFF_COMPRESSION_NONE) {
|
|
TiffIfd->ifd[IFD_COMPRESSION].value = COMPRESSION_NONE;
|
|
TiffIfd->ifd[IFD_G3OPTIONS].value = GROUP3OPT_FILLBITS;
|
|
}
|
|
else if (TiffInstance->CompressionType == TIFF_COMPRESSION_MMR) {
|
|
TiffIfd->ifd[IFD_COMPRESSION].value = TIFF_COMPRESSION_MMR;
|
|
TiffIfd->ifd[IFD_G3OPTIONS].value = GROUP3OPT_FILLBITS |
|
|
(TiffInstance->CompressionType == TIFF_COMPRESSION_MH ? 0 : GROUP3OPT_2DENCODING);
|
|
}
|
|
else {
|
|
TiffIfd->ifd[IFD_COMPRESSION].value = COMPRESSION_CCITTFAX3;
|
|
TiffIfd->ifd[IFD_G3OPTIONS].value = GROUP3OPT_FILLBITS |
|
|
(TiffInstance->CompressionType == TIFF_COMPRESSION_MH ? 0 : GROUP3OPT_2DENCODING);
|
|
}
|
|
|
|
if (!WriteFile(
|
|
TiffInstance->hFile,
|
|
TiffIfd,
|
|
sizeof(FAXIFD),
|
|
&Bytes,
|
|
NULL
|
|
)) {
|
|
return FALSE;
|
|
}
|
|
|
|
TiffInstance->IfdOffset = CurrOffset + FIELD_OFFSET( FAXIFD, nextIFDOffset );
|
|
TiffInstance->Bytes = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TiffWrite(
|
|
HANDLE hTiff,
|
|
LPBYTE TiffData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Writes a new line of data to a TIFF file. The data
|
|
is encoded according to the compression type specified
|
|
when TiffCreate was called.
|
|
|
|
Arguments:
|
|
|
|
hTiff - TIFF handle returned by TiffCreate or TiffOpen
|
|
|
|
Return Value:
|
|
|
|
TRUE for success, FALSE on error
|
|
|
|
--*/
|
|
|
|
{
|
|
PTIFF_INSTANCE_DATA TiffInstance = (PTIFF_INSTANCE_DATA) hTiff;
|
|
DWORD Bytes;
|
|
|
|
Assert(TiffInstance);
|
|
|
|
EncodeFaxData(
|
|
TiffInstance,
|
|
TiffData,
|
|
TiffInstance->ImageWidth,
|
|
TiffInstance->CompressionType
|
|
);
|
|
|
|
FlushBits( TiffInstance );
|
|
|
|
Bytes = (DWORD)(TiffInstance->bitbuf - &TiffInstance->Buffer[FAXBYTES]);
|
|
TiffInstance->bitbuf = &TiffInstance->Buffer[FAXBYTES];
|
|
|
|
WriteFile(
|
|
TiffInstance->hFile,
|
|
TiffInstance->bitbuf,
|
|
Bytes,
|
|
&Bytes,
|
|
NULL
|
|
);
|
|
|
|
TiffInstance->Bytes += Bytes;
|
|
|
|
ZeroMemory( TiffInstance->bitbuf, FAXBYTES );
|
|
TiffInstance->Lines += 1;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TiffWriteRaw(
|
|
HANDLE hTiff,
|
|
LPBYTE TiffData,
|
|
DWORD Size
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Writes a new line of data to a TIFF file. The data
|
|
is encoded according to the compression type specified
|
|
when TiffCreate was called.
|
|
|
|
Arguments:
|
|
|
|
hTiff - TIFF handle returned by TiffCreate or TiffOpen
|
|
|
|
Return Value:
|
|
|
|
TRUE for success, FALSE on error
|
|
|
|
--*/
|
|
|
|
{
|
|
PTIFF_INSTANCE_DATA TiffInstance = (PTIFF_INSTANCE_DATA) hTiff;
|
|
DWORD Bytes;
|
|
|
|
|
|
Assert(TiffInstance);
|
|
|
|
WriteFile(
|
|
TiffInstance->hFile,
|
|
TiffData,
|
|
Size,
|
|
&Bytes,
|
|
NULL
|
|
);
|
|
|
|
TiffInstance->Bytes += Bytes;
|
|
|
|
if (Size == FAXBYTES) {
|
|
TiffInstance->Lines += 1;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
GetTiffBits(
|
|
HANDLE hTiff,
|
|
LPBYTE Buffer,
|
|
LPDWORD BufferSize,
|
|
DWORD FillOrder
|
|
)
|
|
{
|
|
PTIFF_INSTANCE_DATA TiffInstance = (PTIFF_INSTANCE_DATA) hTiff;
|
|
DWORD i;
|
|
LPBYTE TmpBuffer;
|
|
|
|
|
|
if (TiffInstance->StripDataSize > *BufferSize) {
|
|
*BufferSize = TiffInstance->StripDataSize;
|
|
return FALSE;
|
|
}
|
|
|
|
CopyMemory( Buffer, TiffInstance->StripData, TiffInstance->StripDataSize );
|
|
|
|
if (FillOrder != TiffInstance->FillOrder) {
|
|
for (i = 0, TmpBuffer = Buffer; i < TiffInstance->StripDataSize; i++) {
|
|
TmpBuffer[i] = BitReverseTable[TmpBuffer[i]];
|
|
}
|
|
}
|
|
|
|
*BufferSize = TiffInstance->StripDataSize;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TiffReadRaw(
|
|
HANDLE hTiff,
|
|
IN OUT LPBYTE Buffer,
|
|
IN OUT LPDWORD BufferSize,
|
|
IN DWORD RequestedCompressionType,
|
|
IN DWORD FillOrder,
|
|
IN BOOL HiRes
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads in a page of TIFF data starting at the current
|
|
page. The current page is set by calling TiffSeekToPage.
|
|
Returns the data with the RequestedCompressionType and FillOrder
|
|
doing conversions if necessary.
|
|
|
|
Arguments:
|
|
|
|
hTiff - TIFF handle returned by or TiffOpen
|
|
Buffer - pointer to buffer
|
|
BufferSize - pointer to size of buffer
|
|
RequestedCompressionType - type of compression desired
|
|
FillOrder - desired FillOrder
|
|
|
|
Return Value:
|
|
|
|
TRUE for success, FALSE on error
|
|
If the buffer passed in is not
|
|
big enough, return FALSE and set BufferSize to the required size.
|
|
If another error occurs, set BufferSize to 0 and return FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
PTIFF_INSTANCE_DATA TiffInstance = (PTIFF_INSTANCE_DATA) hTiff;
|
|
PTIFF_INSTANCE_DATA TmpTiffData; // temporary used to convert bits into
|
|
LPBYTE TmpBuffer; // temporary buffer for above
|
|
LPBYTE CurLine; // pointer to current scan line in Buffer
|
|
DWORD CompLineBytes; // number of bytes in compressed scan line
|
|
DWORD TotalBytes=0; // total number of bytes in compress page
|
|
LPBYTE OutBufPtr; // pointer to output buffer
|
|
DWORD BytesInLine;
|
|
LPBYTE EndOfBuffer;
|
|
DWORD LineWidth;
|
|
DWORD PageHeight;
|
|
DWORD i;
|
|
DWORD K = HiRes ? 4 : 2;
|
|
|
|
|
|
LineWidth = TiffInstance->ImageWidth;
|
|
PageHeight = TiffInstance->ImageHeight;
|
|
|
|
//
|
|
// create an in-memory tiff header (TIFF_INSTANCE_DATA) to hold the data that is converted
|
|
// this call does not create a file because the filename is NULL
|
|
//
|
|
if (((HANDLE) TmpTiffData =
|
|
TiffCreate(NULL, RequestedCompressionType, LineWidth, FillOrder, 1)) == NULL )
|
|
{
|
|
*BufferSize = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
TmpBuffer = VirtualAlloc(
|
|
NULL,
|
|
*BufferSize,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE
|
|
);
|
|
if (TmpBuffer == NULL) {
|
|
TiffClose(TmpTiffData);
|
|
*BufferSize = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
if (!TiffRead(hTiff, TmpBuffer, 0)) {
|
|
VirtualFree( TmpBuffer, 0, MEM_RELEASE );
|
|
TiffClose(TmpTiffData);
|
|
*BufferSize = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
CurLine = TmpBuffer;
|
|
OutBufPtr = Buffer;
|
|
BytesInLine = TiffInstance->BytesPerLine;
|
|
EndOfBuffer = TmpBuffer + (PageHeight * BytesInLine);
|
|
|
|
i = 0;
|
|
while (CurLine < EndOfBuffer)
|
|
{
|
|
switch( RequestedCompressionType ) {
|
|
case TIFF_COMPRESSION_MH:
|
|
EncodeFaxDataMhCompression( TmpTiffData, CurLine, LineWidth );
|
|
break;
|
|
|
|
case TIFF_COMPRESSION_MR:
|
|
if (i++ % K == 0) {
|
|
EncodeFaxDataMhCompression( TmpTiffData, CurLine, LineWidth );
|
|
} else {
|
|
EncodeFaxDataMmrCompression( TmpTiffData, CurLine, LineWidth );
|
|
}
|
|
break;
|
|
|
|
case TIFF_COMPRESSION_MMR:
|
|
EncodeFaxDataMmrCompression( TmpTiffData, CurLine, LineWidth );
|
|
break;
|
|
}
|
|
|
|
FlushBits( TmpTiffData );
|
|
|
|
CompLineBytes = (DWORD)(TmpTiffData->bitbuf - &TmpTiffData->Buffer[FAXBYTES]);
|
|
TmpTiffData->bitbuf = &TmpTiffData->Buffer[FAXBYTES];
|
|
|
|
CopyMemory( OutBufPtr, TmpTiffData->bitbuf, CompLineBytes );
|
|
ZeroMemory(TmpTiffData->bitbuf, FAXBYTES);
|
|
TotalBytes += CompLineBytes;
|
|
|
|
CurLine += BytesInLine;
|
|
OutBufPtr += CompLineBytes;
|
|
}
|
|
|
|
*BufferSize = TotalBytes;
|
|
VirtualFree( TmpBuffer, 0, MEM_RELEASE);
|
|
|
|
TiffClose(TmpTiffData);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TiffRead(
|
|
HANDLE hTiff,
|
|
LPBYTE TiffData,
|
|
DWORD PadLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads in a page of TIFF data starting at the current
|
|
page. The current page is set by calling TiffSeekToPage.
|
|
This always returns the data with FillOrder FILLORDER_LSB2MSB
|
|
|
|
Arguments:
|
|
|
|
hTiff - TIFF handle returned by TiffCreate or TiffOpen
|
|
|
|
Return Value:
|
|
|
|
TRUE for success, FALSE on error
|
|
|
|
--*/
|
|
|
|
{
|
|
switch( ((PTIFF_INSTANCE_DATA) hTiff)->CompressionType ) {
|
|
case TIFF_COMPRESSION_NONE:
|
|
return DecodeUnCompressedFaxData( (PTIFF_INSTANCE_DATA) hTiff, TiffData, FALSE, PadLength );
|
|
|
|
case TIFF_COMPRESSION_MH:
|
|
return DecodeMHFaxData( (PTIFF_INSTANCE_DATA) hTiff, TiffData, FALSE, PadLength );
|
|
|
|
case TIFF_COMPRESSION_MR:
|
|
return DecodeMRFaxData( (PTIFF_INSTANCE_DATA) hTiff, TiffData, FALSE, PadLength );
|
|
|
|
case TIFF_COMPRESSION_MMR:
|
|
return DecodeMMRFaxData( (PTIFF_INSTANCE_DATA) hTiff, TiffData, FALSE, PadLength );
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TiffSeekToPage(
|
|
HANDLE hTiff,
|
|
DWORD PageNumber,
|
|
DWORD FillOrder
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Positions the TIFF file to the requested page. The next
|
|
TiffRead call gets this page's data.
|
|
|
|
Arguments:
|
|
|
|
hTiff - TIFF handle returned by TiffCreate or TiffOpen
|
|
PageNumber - Requested page number
|
|
|
|
Return Value:
|
|
|
|
TRUE for success, FALSE on error
|
|
|
|
--*/
|
|
|
|
{
|
|
PTIFF_INSTANCE_DATA TiffInstance = (PTIFF_INSTANCE_DATA) hTiff;
|
|
WORD NumDirEntries;
|
|
DWORD IfdOffset;
|
|
DWORD PageCount;
|
|
DWORD i;
|
|
DWORD j;
|
|
LPBYTE dataPtr;
|
|
WORD PrevTagId;
|
|
PSTRIP_INFO StripInfo = NULL;
|
|
DWORD StripCount;
|
|
PTIFF_TAG TiffTags;
|
|
DWORD CompressionType;
|
|
|
|
|
|
Assert(TiffInstance);
|
|
|
|
if (PageNumber > TiffInstance->PageCount) {
|
|
return FALSE;
|
|
}
|
|
|
|
PageCount = 0;
|
|
|
|
if (PageNumber == TiffInstance->CurrPage + 1) {
|
|
|
|
//
|
|
// get the count of tags in this IFD
|
|
//
|
|
|
|
IfdOffset = TiffInstance->IfdOffset;
|
|
|
|
NumDirEntries = *(LPWORD)(TiffInstance->fPtr + IfdOffset);
|
|
|
|
} else {
|
|
|
|
IfdOffset = TiffInstance->TiffHdr.IFDOffset;
|
|
|
|
while ( IfdOffset ) {
|
|
|
|
//
|
|
// get the count of tags in this IFD
|
|
//
|
|
NumDirEntries = *(LPWORD)(TiffInstance->fPtr + IfdOffset);
|
|
|
|
//
|
|
// increment the page counter and bail if ready
|
|
//
|
|
PageCount += 1;
|
|
if (PageCount == PageNumber) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// get the next IFD offset
|
|
//
|
|
IfdOffset = *(UNALIGNED DWORD *)(TiffInstance->fPtr + (NumDirEntries * sizeof(TIFF_TAG)) + IfdOffset + sizeof(WORD));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!IfdOffset) {
|
|
goto error_exit;
|
|
}
|
|
|
|
//
|
|
// set the tag pointer
|
|
//
|
|
TiffTags = (PTIFF_TAG)(TiffInstance->fPtr + IfdOffset + sizeof(WORD));
|
|
|
|
//
|
|
// get the next IFD offset
|
|
//
|
|
TiffInstance->IfdOffset = *(UNALIGNED DWORD *)(TiffInstance->fPtr + (NumDirEntries * sizeof(TIFF_TAG)) + IfdOffset + sizeof(WORD));
|
|
|
|
//
|
|
// walk the tags and pick out the info we need
|
|
//
|
|
for (i=0,PrevTagId=0; i<NumDirEntries; i++) {
|
|
|
|
//
|
|
// verify that the tags are in ascending order
|
|
//
|
|
if (TiffTags[i].TagId < PrevTagId) {
|
|
goto error_exit;
|
|
}
|
|
|
|
PrevTagId = TiffTags[i].TagId;
|
|
|
|
switch( TiffTags[i].TagId ) {
|
|
|
|
case TIFFTAG_STRIPOFFSETS:
|
|
|
|
StripInfo = (PSTRIP_INFO) MemAlloc(
|
|
TiffTags[i].DataCount * sizeof(STRIP_INFO)
|
|
);
|
|
|
|
if (!StripInfo) {
|
|
goto error_exit;
|
|
}
|
|
|
|
StripCount = TiffTags[i].DataCount;
|
|
|
|
for (j=0; j<TiffTags[i].DataCount; j++) {
|
|
|
|
StripInfo[j].Offset = GetTagData( TiffInstance->fPtr, j, &TiffTags[i] );
|
|
StripInfo[j].Data = TiffInstance->fPtr + StripInfo[j].Offset;
|
|
|
|
TiffInstance->StripOffset = StripInfo[j].Offset;
|
|
|
|
}
|
|
break;
|
|
|
|
case TIFFTAG_ROWSPERSTRIP:
|
|
|
|
TiffInstance->TagRowsPerStrip = &TiffTags[i];
|
|
TiffInstance->RowsPerStrip = GetTagData( TiffInstance->fPtr, 0, &TiffTags[i] );
|
|
break;
|
|
|
|
case TIFFTAG_STRIPBYTECOUNTS:
|
|
|
|
TiffInstance->TagStripByteCounts = &TiffTags[i];
|
|
for (j=0; j<TiffTags[i].DataCount; j++) {
|
|
|
|
StripInfo[j].Bytes = GetTagData( TiffInstance->fPtr, j, &TiffTags[i] );
|
|
|
|
if (StripInfo[j].Offset+StripInfo[j].Bytes > TiffInstance->FileSize) {
|
|
|
|
//
|
|
// the creator of this tiff file is a liar, trim the bytes
|
|
//
|
|
|
|
DWORD Delta;
|
|
|
|
Delta = (StripInfo[j].Offset + StripInfo[j].Bytes) - TiffInstance->FileSize;
|
|
if (Delta >= StripInfo[j].Bytes) {
|
|
//
|
|
// the offset lies beyond the end of the file
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
StripInfo[j].Bytes -= Delta;
|
|
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case TIFFTAG_COMPRESSION:
|
|
|
|
CompressionType = GetTagData( TiffInstance->fPtr, 0, &TiffTags[i] );
|
|
|
|
switch ( CompressionType ) {
|
|
|
|
case COMPRESSION_NONE:
|
|
TiffInstance->CompressionType = TIFF_COMPRESSION_NONE;
|
|
break;
|
|
|
|
case COMPRESSION_CCITTRLE:
|
|
TiffInstance->CompressionType = TIFF_COMPRESSION_MH;
|
|
break;
|
|
|
|
case COMPRESSION_CCITTFAX3:
|
|
TiffInstance->CompressionType = TIFF_COMPRESSION_MH;
|
|
break;
|
|
|
|
case COMPRESSION_CCITTFAX4:
|
|
TiffInstance->CompressionType = TIFF_COMPRESSION_MMR;
|
|
break;
|
|
|
|
case COMPRESSION_LZW:
|
|
case COMPRESSION_OJPEG:
|
|
case COMPRESSION_JPEG:
|
|
case COMPRESSION_NEXT:
|
|
case COMPRESSION_CCITTRLEW:
|
|
case COMPRESSION_PACKBITS:
|
|
case COMPRESSION_THUNDERSCAN:
|
|
//
|
|
// unsupported compression type
|
|
//
|
|
goto error_exit;
|
|
|
|
default:
|
|
//
|
|
// unknown compression type
|
|
//
|
|
goto error_exit;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TIFFTAG_GROUP3OPTIONS:
|
|
|
|
CompressionType = GetTagData( TiffInstance->fPtr, 0, &TiffTags[i] );
|
|
|
|
if (CompressionType & GROUP3OPT_2DENCODING) {
|
|
if (TiffInstance->CompressionType != TIFF_COMPRESSION_MMR) {
|
|
TiffInstance->CompressionType = TIFF_COMPRESSION_MR;
|
|
}
|
|
|
|
} else if (CompressionType & GROUP3OPT_UNCOMPRESSED) {
|
|
|
|
TiffInstance->CompressionType = TIFF_COMPRESSION_NONE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TIFFTAG_IMAGEWIDTH:
|
|
|
|
TiffInstance->ImageWidth = GetTagData( TiffInstance->fPtr, 0, &TiffTags[i] );
|
|
TiffInstance->BytesPerLine = (TiffInstance->ImageWidth/8)+(TiffInstance->ImageWidth%8?1:0);
|
|
break;
|
|
|
|
case TIFFTAG_IMAGELENGTH:
|
|
|
|
TiffInstance->TagImageLength = &TiffTags[i];
|
|
TiffInstance->ImageHeight = GetTagData( TiffInstance->fPtr, 0, &TiffTags[i] );
|
|
break;
|
|
|
|
case TIFFTAG_XRESOLUTION:
|
|
|
|
TiffInstance->XResolution = GetTagData( TiffInstance->fPtr, 0, &TiffTags[i] );
|
|
break;
|
|
|
|
case TIFFTAG_YRESOLUTION:
|
|
|
|
TiffInstance->YResolution = GetTagData( TiffInstance->fPtr, 0, &TiffTags[i] );
|
|
break;
|
|
|
|
case TIFFTAG_PHOTOMETRIC:
|
|
|
|
TiffInstance->PhotometricInterpretation = GetTagData( TiffInstance->fPtr, 0, &TiffTags[i] );
|
|
break;
|
|
|
|
case TIFFTAG_FILLORDER:
|
|
|
|
TiffInstance->TagFillOrder = &TiffTags[i];
|
|
TiffInstance->FillOrder = GetTagData( TiffInstance->fPtr, 0, &TiffTags[i] );
|
|
break;
|
|
|
|
case TIFFTAG_CLEANFAXDATA:
|
|
|
|
TiffInstance->TagCleanFaxData = &TiffTags[i];
|
|
TiffInstance->CleanFaxData = GetTagData( TiffInstance->fPtr, 0, &TiffTags[i] );
|
|
break;
|
|
|
|
case TIFFTAG_CONSECUTIVEBADFAXLINES:
|
|
|
|
TiffInstance->TagBadFaxLines = &TiffTags[i];
|
|
TiffInstance->BadFaxLines = GetTagData( TiffInstance->fPtr, 0, &TiffTags[i] );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// now go read the strip data
|
|
//
|
|
|
|
for (i=0,j=0; i<StripCount; i++) {
|
|
|
|
j += StripInfo[i].Bytes;
|
|
|
|
}
|
|
|
|
if (j >= TiffInstance->StripDataSize) {
|
|
|
|
if (TiffInstance->StripData) {
|
|
|
|
VirtualFree(
|
|
TiffInstance->StripData,
|
|
0,
|
|
MEM_RELEASE
|
|
);
|
|
|
|
}
|
|
|
|
TiffInstance->StripDataSize = j;
|
|
|
|
TiffInstance->StripData = VirtualAlloc(
|
|
NULL,
|
|
TiffInstance->StripDataSize,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE
|
|
);
|
|
|
|
if (!TiffInstance->StripData) {
|
|
goto error_exit;
|
|
}
|
|
|
|
} else {
|
|
|
|
if (TiffInstance->StripData) {
|
|
ZeroMemory(
|
|
TiffInstance->StripData,
|
|
TiffInstance->StripDataSize
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
for (i=0,dataPtr=TiffInstance->StripData; i<StripCount; i++) {
|
|
|
|
__try {
|
|
|
|
CopyMemory(
|
|
dataPtr,
|
|
StripInfo[i].Data,
|
|
StripInfo[i].Bytes
|
|
);
|
|
|
|
dataPtr += StripInfo[i].Bytes;
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (TiffInstance->FillOrder != FillOrder) {
|
|
for (i=0,dataPtr=TiffInstance->StripData; i<TiffInstance->StripDataSize; i++) {
|
|
dataPtr[i] = BitReverseTable[dataPtr[i]];
|
|
}
|
|
}
|
|
|
|
TiffInstance->CurrPtr = TiffInstance->StripData;
|
|
TiffInstance->CurrPage = PageNumber;
|
|
|
|
MemFree( StripInfo );
|
|
|
|
return TRUE;
|
|
|
|
error_exit:
|
|
|
|
if (StripInfo) {
|
|
MemFree( StripInfo );
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
ConvMmrHiResToLowRes(
|
|
LPTSTR SrcFileName,
|
|
LPTSTR DestFileName
|
|
)
|
|
|
|
{
|
|
LPBYTE bmiBuf[sizeof(BITMAPINFOHEADER)+(sizeof(RGBQUAD)*2)];
|
|
PBITMAPINFO bmi = (PBITMAPINFO) bmiBuf;
|
|
HBITMAP hBmp;
|
|
|
|
|
|
TIFF_INFO TiffInfoSrc;
|
|
HANDLE hTiffSrc;
|
|
DWORD CurrPage;
|
|
LPBYTE pSrcBits;
|
|
|
|
HANDLE hTiffDest;
|
|
LPBYTE TiffDataDestMmr;
|
|
DWORD DestSize;
|
|
LPBYTE pDestBits;
|
|
|
|
DWORD DestHeight;
|
|
|
|
HDC hdcMem;
|
|
INT ScanLines;
|
|
INT DestScanLines;
|
|
int StretchMode;
|
|
DWORD PageCnt;
|
|
DWORD DestHiRes;
|
|
|
|
BOOL bRet = FALSE;
|
|
|
|
|
|
|
|
CurrPage = 1;
|
|
|
|
hTiffSrc = TiffOpen(
|
|
SrcFileName,
|
|
&TiffInfoSrc,
|
|
TRUE,
|
|
FILLORDER_MSB2LSB
|
|
);
|
|
if (! hTiffSrc) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (TiffInfoSrc.YResolution == 196) {
|
|
DestHiRes = 1;
|
|
}
|
|
else {
|
|
DestHiRes = 0;
|
|
}
|
|
|
|
|
|
hTiffDest = TiffCreate(
|
|
DestFileName,
|
|
TIFF_COMPRESSION_MMR,
|
|
1728,
|
|
FILLORDER_MSB2LSB,
|
|
DestHiRes);
|
|
|
|
if (! hTiffDest) {
|
|
TiffClose(hTiffSrc);
|
|
return FALSE;
|
|
}
|
|
|
|
pSrcBits = (LPBYTE) VirtualAlloc(
|
|
NULL,
|
|
TiffInfoSrc.ImageHeight * (TiffInfoSrc.ImageWidth / 8),
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE
|
|
);
|
|
if (!pSrcBits) {
|
|
TiffClose(hTiffSrc);
|
|
TiffClose(hTiffDest);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
pDestBits = (LPBYTE) VirtualAlloc(
|
|
NULL,
|
|
TiffInfoSrc.ImageHeight * (TiffInfoSrc.ImageWidth / 8),
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE
|
|
);
|
|
if (!pDestBits) {
|
|
TiffClose(hTiffSrc);
|
|
TiffClose(hTiffDest);
|
|
VirtualFree ( pSrcBits, 0 , MEM_RELEASE );
|
|
return FALSE;
|
|
}
|
|
|
|
bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
|
bmi->bmiHeader.biWidth = TiffInfoSrc.ImageWidth;
|
|
bmi->bmiHeader.biHeight = (INT) TiffInfoSrc.ImageHeight;
|
|
bmi->bmiHeader.biPlanes = 1;
|
|
bmi->bmiHeader.biBitCount = 1;
|
|
bmi->bmiHeader.biCompression = 0;
|
|
bmi->bmiHeader.biSizeImage = 0;
|
|
bmi->bmiHeader.biXPelsPerMeter = 0;
|
|
bmi->bmiHeader.biYPelsPerMeter = 0;
|
|
bmi->bmiHeader.biClrUsed = 0;
|
|
bmi->bmiHeader.biClrImportant = 0;
|
|
|
|
if ( ! TiffInfoSrc.PhotometricInterpretation) {
|
|
bmi->bmiColors[0].rgbBlue = 0;
|
|
bmi->bmiColors[0].rgbGreen = 0;
|
|
bmi->bmiColors[0].rgbRed = 0;
|
|
bmi->bmiColors[0].rgbReserved = 0;
|
|
bmi->bmiColors[1].rgbBlue = 0xff;
|
|
bmi->bmiColors[1].rgbGreen = 0xff;
|
|
bmi->bmiColors[1].rgbRed = 0xff;
|
|
bmi->bmiColors[1].rgbReserved = 0;
|
|
} else {
|
|
bmi->bmiColors[0].rgbBlue = 0xff;
|
|
bmi->bmiColors[0].rgbGreen = 0xff;
|
|
bmi->bmiColors[0].rgbRed = 0xff;
|
|
bmi->bmiColors[0].rgbReserved = 0;
|
|
bmi->bmiColors[1].rgbBlue = 0;
|
|
bmi->bmiColors[1].rgbGreen = 0;
|
|
bmi->bmiColors[1].rgbRed = 0;
|
|
bmi->bmiColors[1].rgbReserved = 0;
|
|
}
|
|
|
|
|
|
DestHeight = TiffInfoSrc.ImageHeight / 2;
|
|
|
|
hdcMem = CreateCompatibleDC( NULL );
|
|
hBmp = CreateCompatibleBitmap( hdcMem, TiffInfoSrc.ImageWidth, DestHeight );
|
|
SelectObject( hdcMem, hBmp );
|
|
|
|
StretchMode = STRETCH_ORSCANS;
|
|
SetStretchBltMode(hdcMem, StretchMode);
|
|
|
|
for (PageCnt=0; PageCnt<TiffInfoSrc.PageCount; PageCnt++) {
|
|
|
|
if ( ! TiffSeekToPage( hTiffSrc, PageCnt+1, FILLORDER_MSB2LSB) ) {
|
|
goto l_exit;
|
|
}
|
|
|
|
if (!TiffRead( hTiffSrc, (LPBYTE) pSrcBits, 0 )) {
|
|
goto l_exit;
|
|
}
|
|
bmi->bmiHeader.biHeight = (INT) TiffInfoSrc.ImageHeight;
|
|
|
|
ScanLines = StretchDIBits(
|
|
hdcMem,
|
|
0,
|
|
0,
|
|
TiffInfoSrc.ImageWidth,
|
|
DestHeight,
|
|
0,
|
|
0,
|
|
TiffInfoSrc.ImageWidth,
|
|
TiffInfoSrc.ImageHeight,
|
|
pSrcBits,
|
|
bmi,
|
|
DIB_RGB_COLORS,
|
|
SRCCOPY
|
|
);
|
|
|
|
bmi->bmiHeader.biHeight = (INT) DestHeight;
|
|
|
|
DestScanLines = GetDIBits(
|
|
hdcMem,
|
|
hBmp,
|
|
0,
|
|
DestHeight,
|
|
pDestBits,
|
|
bmi,
|
|
DIB_RGB_COLORS
|
|
);
|
|
|
|
// reuse pSrcBits buffer
|
|
TiffDataDestMmr = pSrcBits;
|
|
|
|
((PTIFF_INSTANCE_DATA) hTiffDest)->bitbuf = TiffDataDestMmr;
|
|
((PTIFF_INSTANCE_DATA) hTiffDest)->bitcnt = DWORDBITS;
|
|
((PTIFF_INSTANCE_DATA) hTiffDest)->bitdata = 0;
|
|
|
|
if (! TiffStartPage(hTiffDest) ) {
|
|
goto l_exit;
|
|
}
|
|
|
|
|
|
if ( ! EncodeFaxPageMmrCompression(
|
|
(PTIFF_INSTANCE_DATA) hTiffDest,
|
|
(PBYTE) pDestBits,
|
|
1728,
|
|
DestScanLines,
|
|
&DestSize) ) {
|
|
|
|
goto l_exit;
|
|
}
|
|
|
|
|
|
if (! TiffWriteRaw( hTiffDest, TiffDataDestMmr, DestSize) ) {
|
|
goto l_exit;
|
|
}
|
|
|
|
if (! TiffEndPage(hTiffDest) ) {
|
|
goto l_exit;
|
|
}
|
|
}
|
|
|
|
bRet = TRUE;
|
|
|
|
l_exit:
|
|
|
|
DeleteObject(hBmp);
|
|
DeleteDC(hdcMem);
|
|
|
|
VirtualFree ( pSrcBits, 0 , MEM_RELEASE );
|
|
VirtualFree ( pDestBits, 0 , MEM_RELEASE );
|
|
|
|
TiffClose(hTiffSrc);
|
|
TiffClose(hTiffDest);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DrawBannerBitmap(
|
|
LPTSTR pBannerString,
|
|
INT width,
|
|
INT height,
|
|
HBITMAP *phBitmap,
|
|
PVOID *ppBits
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Draw the specified banner string into a memory bitmap
|
|
|
|
Arguments:
|
|
|
|
pBannerString - Specifies the banner string to be drawn
|
|
width, height - Specifies the width and height of the banner bitmap (in pixels)
|
|
phBitmap - Returns a handle to the banner bitmap
|
|
ppBits - Returns a pointer to the banner bitmap data
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if there is an error
|
|
|
|
Note:
|
|
|
|
When this function returns successful, you must call DeleteObject
|
|
on the returned bitmap handle after you're done with the bitmap.
|
|
|
|
Scanlines of the bitmap data always start on DWORD boundary.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// Information about the bitmap which is passed to CreateDIBSection
|
|
//
|
|
|
|
struct {
|
|
|
|
BITMAPINFOHEADER bmiHeader;
|
|
RGBQUAD bmiColors[2];
|
|
|
|
} bitmapInfo = {
|
|
|
|
{
|
|
sizeof(BITMAPINFOHEADER),
|
|
width,
|
|
-height,
|
|
1,
|
|
1,
|
|
BI_RGB,
|
|
0,
|
|
7874,
|
|
7874,
|
|
0,
|
|
0,
|
|
},
|
|
|
|
//
|
|
// Colors used in the bitmap: 0 = white, 1 = black
|
|
//
|
|
|
|
{
|
|
{ 255, 255, 255 },
|
|
{ 0, 0, 0 },
|
|
}
|
|
};
|
|
|
|
HDC hdcMem = NULL;
|
|
HBITMAP hBitmap = NULL, hOldBitmap = NULL;
|
|
PVOID pBits = NULL;
|
|
HFONT hFont = NULL, hOldFont = NULL;
|
|
RECT rect = { 0, 0, width, height };
|
|
LOGFONT logFont;
|
|
|
|
//
|
|
// Create a memory DC and a DIBSection and
|
|
// select the bitmap into the memory DC and
|
|
// select an appropriate sized monospace font
|
|
//
|
|
|
|
ZeroMemory(&logFont, sizeof(logFont));
|
|
logFont.lfHeight = -height;
|
|
logFont.lfWeight = FW_NORMAL;
|
|
logFont.lfCharSet = DEFAULT_CHARSET;
|
|
logFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
|
|
logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
|
|
logFont.lfQuality = DEFAULT_QUALITY;
|
|
logFont.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
|
|
|
|
if ((pBannerString != NULL && width > 0 && height > 0) &&
|
|
(hdcMem = CreateCompatibleDC(NULL)) &&
|
|
(hBitmap = CreateDIBSection(NULL,
|
|
(LPBITMAPINFO) &bitmapInfo,
|
|
DIB_RGB_COLORS,
|
|
&pBits,
|
|
NULL,
|
|
0)) &&
|
|
(hOldBitmap = SelectObject(hdcMem, hBitmap)) &&
|
|
(hFont = CreateFontIndirect(&logFont)) &&
|
|
(hOldFont = SelectObject(hdcMem, hFont)))
|
|
{
|
|
//
|
|
// Use monospace system font to draw the banner string
|
|
//
|
|
|
|
DrawText(hdcMem,
|
|
pBannerString,
|
|
-1,
|
|
&rect,
|
|
DT_CENTER | DT_VCENTER | DT_SINGLELINE);
|
|
|
|
//
|
|
// Return a handle to the bitmap and a pointer to the bitmap data
|
|
//
|
|
|
|
*phBitmap = hBitmap;
|
|
*ppBits = pBits;
|
|
}
|
|
else
|
|
{
|
|
*phBitmap = NULL;
|
|
*ppBits = NULL;
|
|
}
|
|
|
|
//
|
|
// Perform any necessary clean up before returning
|
|
//
|
|
|
|
if (hOldFont != NULL)
|
|
SelectObject(hdcMem, hOldFont);
|
|
|
|
if (hFont != NULL)
|
|
DeleteObject(hFont);
|
|
|
|
if (hOldBitmap != NULL)
|
|
SelectObject(hdcMem, hOldBitmap);
|
|
|
|
if (pBits == NULL && hBitmap != NULL)
|
|
DeleteObject(hBitmap);
|
|
|
|
if (hdcMem != NULL)
|
|
DeleteDC(hdcMem);
|
|
|
|
return (*ppBits != NULL);
|
|
}
|
|
|
|
|
|
BOOL
|
|
MmrAddBranding(
|
|
LPTSTR SrcFileName,
|
|
LPTSTR Branding,
|
|
LPTSTR BrandingEnd,
|
|
INT BrandingHeight
|
|
)
|
|
|
|
{
|
|
|
|
INT BrandingWidth = 1728;
|
|
LPTSTR DestFileName;
|
|
TIFF_INFO TiffInfoSrc;
|
|
HANDLE hTiffSrc;
|
|
DWORD CurrPage;
|
|
BYTE *pBrandBits;
|
|
BYTE *pMmrBrandBitsAlloc;
|
|
DWORD *lpdwMmrBrandBits;
|
|
|
|
BYTE pCleanBeforeBrandBits[4] = {0xff, 0xff, 0xff, 0xff}; // 32 blank lines at the beginning
|
|
|
|
HANDLE hTiffDest;
|
|
LPDWORD lpdwSrcBits;
|
|
LPDWORD lpdwSrc;
|
|
LPDWORD lpdwSrcEnd;
|
|
|
|
DWORD PageCnt;
|
|
DWORD DestHiRes;
|
|
DWORD BrandingLen = _tcslen(Branding); // without Page#
|
|
BOOL bRet = FALSE;
|
|
DWORD DwordsOut;
|
|
DWORD BytesOut;
|
|
DWORD BitsOut;
|
|
DWORD BufferSize;
|
|
DWORD BufferUsedSize;
|
|
DWORD StripDataSize;
|
|
HBITMAP hBitmap;
|
|
PVOID pBannerBits;
|
|
DWORD TotalSrcBytes;
|
|
DWORD NumSrcDwords;
|
|
|
|
DWORD ImageHeight;
|
|
|
|
//
|
|
// Build Dest. file name from Src. file name
|
|
//
|
|
|
|
if ( (DestFileName = MemAlloc( (_tcslen(SrcFileName)+1) * sizeof (TCHAR) ) ) == NULL ) {
|
|
return FALSE;
|
|
}
|
|
|
|
_tcscpy(DestFileName, SrcFileName);
|
|
DestFileName[_tcslen(DestFileName) - 1] = TEXT('$');
|
|
|
|
|
|
|
|
pBrandBits = MemAlloc((BrandingHeight+1) * (BrandingWidth / 8));
|
|
if (!pBrandBits) {
|
|
MemFree(DestFileName);
|
|
return FALSE;
|
|
}
|
|
|
|
pMmrBrandBitsAlloc = MemAlloc( sizeof(DWORD) * (BrandingHeight+1) * (BrandingWidth / 8));
|
|
if (!pMmrBrandBitsAlloc) {
|
|
MemFree(DestFileName);
|
|
MemFree(pBrandBits);
|
|
return FALSE;
|
|
}
|
|
|
|
// align
|
|
lpdwMmrBrandBits = (LPDWORD) ( ((ULONG_PTR) pMmrBrandBitsAlloc) & ~(3) );
|
|
|
|
|
|
|
|
hTiffSrc = TiffOpen(
|
|
SrcFileName,
|
|
&TiffInfoSrc,
|
|
TRUE,
|
|
FILLORDER_LSB2MSB
|
|
);
|
|
|
|
if (! hTiffSrc) {
|
|
MemFree(DestFileName);
|
|
MemFree(pBrandBits);
|
|
MemFree(pMmrBrandBitsAlloc);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BufferSize = TiffInfoSrc.ImageHeight * (TiffInfoSrc.ImageWidth / 8);
|
|
|
|
lpdwSrcBits = (LPDWORD) VirtualAlloc(
|
|
NULL,
|
|
BufferSize,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE
|
|
);
|
|
|
|
if (!lpdwSrcBits) {
|
|
MemFree(DestFileName);
|
|
MemFree(pBrandBits);
|
|
MemFree(pMmrBrandBitsAlloc);
|
|
TiffClose(hTiffSrc);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
if (TiffInfoSrc.YResolution == 196) {
|
|
DestHiRes = 1;
|
|
}
|
|
else {
|
|
DestHiRes = 0;
|
|
}
|
|
|
|
|
|
hTiffDest = TiffCreate(
|
|
DestFileName,
|
|
TIFF_COMPRESSION_MMR,
|
|
1728,
|
|
FILLORDER_LSB2MSB,
|
|
DestHiRes);
|
|
|
|
if (! hTiffDest) {
|
|
MemFree(DestFileName);
|
|
MemFree(pBrandBits);
|
|
MemFree(pMmrBrandBitsAlloc);
|
|
VirtualFree ( lpdwSrcBits, 0 , MEM_RELEASE );
|
|
TiffClose(hTiffSrc);
|
|
return FALSE;
|
|
}
|
|
|
|
CurrPage = 1;
|
|
|
|
for (PageCnt=0; PageCnt<TiffInfoSrc.PageCount; PageCnt++) {
|
|
|
|
if ( ! TiffSeekToPage( hTiffSrc, PageCnt+1, FILLORDER_LSB2MSB) ) {
|
|
goto l_exit;
|
|
}
|
|
|
|
if (! TiffStartPage(hTiffDest) ) {
|
|
goto l_exit;
|
|
}
|
|
|
|
//
|
|
// Create branding for every page.
|
|
//
|
|
// Last scan line - all white:
|
|
// 1. to isolate branding from the real image.
|
|
// 2. to avoid an MMR-merge with the real image.
|
|
//
|
|
|
|
ZeroMemory(pBrandBits, (BrandingHeight+1) * (BrandingWidth / 8) );
|
|
|
|
|
|
_stprintf( &Branding[BrandingLen], TEXT("%03d %s %03d"),
|
|
PageCnt+1,
|
|
BrandingEnd,
|
|
TiffInfoSrc.PageCount);
|
|
|
|
if ( ! DrawBannerBitmap(Branding, // banner string
|
|
BrandingWidth, // width in pixels
|
|
BrandingHeight, // height in pixels,
|
|
&hBitmap,
|
|
&pBannerBits)) {
|
|
|
|
// Handle error case here
|
|
goto l_exit;
|
|
|
|
}
|
|
|
|
CopyMemory(pBrandBits, pBannerBits, BrandingHeight * (BrandingWidth / 8) );
|
|
|
|
//
|
|
// Convert uncompressed branding to an MMR
|
|
//
|
|
|
|
ZeroMemory(pMmrBrandBitsAlloc, sizeof(DWORD) * (BrandingHeight+1) * (BrandingWidth / 8) );
|
|
|
|
EncodeMmrBranding(pBrandBits, lpdwMmrBrandBits, BrandingHeight+1, BrandingWidth, &DwordsOut, &BitsOut);
|
|
|
|
BytesOut = (DwordsOut << 2);
|
|
|
|
DeleteObject(hBitmap);
|
|
|
|
//
|
|
// write Spaces 4 bytes = 32 bits = 32 blank lines.
|
|
//
|
|
|
|
if (! TiffWriteRaw( hTiffDest, pCleanBeforeBrandBits, 4) ) {
|
|
goto l_exit;
|
|
}
|
|
|
|
//
|
|
// write branding without the last DWORD
|
|
//
|
|
|
|
|
|
if (! TiffWriteRaw( hTiffDest, (LPBYTE) lpdwMmrBrandBits, BytesOut) ) {
|
|
goto l_exit;
|
|
}
|
|
|
|
//
|
|
// check the current page dimensions. Add memory if needed.
|
|
//
|
|
|
|
TiffGetCurrentPageData( hTiffSrc,
|
|
NULL,
|
|
&StripDataSize,
|
|
NULL,
|
|
&ImageHeight
|
|
);
|
|
|
|
|
|
if (StripDataSize > BufferSize) {
|
|
VirtualFree ( lpdwSrcBits, 0 , MEM_RELEASE );
|
|
|
|
BufferSize = StripDataSize;
|
|
|
|
lpdwSrcBits = (LPDWORD) VirtualAlloc(
|
|
NULL,
|
|
BufferSize,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE
|
|
);
|
|
|
|
if (!lpdwSrcBits) {
|
|
MemFree(pBrandBits);
|
|
MemFree(pMmrBrandBitsAlloc);
|
|
|
|
TiffClose(hTiffSrc);
|
|
TiffClose(hTiffDest);
|
|
|
|
DeleteFile(DestFileName);
|
|
|
|
MemFree(DestFileName);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
BufferUsedSize = BufferSize;
|
|
|
|
|
|
if (BitsOut == 0) {
|
|
//
|
|
// Simple merge
|
|
//
|
|
if (!GetTiffBits( hTiffSrc, (LPBYTE) lpdwSrcBits, &BufferUsedSize, FILLORDER_LSB2MSB)) {
|
|
goto l_exit;
|
|
}
|
|
|
|
// add EOLs at the end of each page
|
|
|
|
NumSrcDwords = ( ((PTIFF_INSTANCE_DATA) hTiffSrc)->StripDataSize) >> 2;
|
|
lpdwSrc = lpdwSrcBits + NumSrcDwords;
|
|
|
|
*(++lpdwSrc) = 0x80000000;
|
|
*(++lpdwSrc) = 0x80000000;
|
|
*(++lpdwSrc) = 0x80000000;
|
|
|
|
TotalSrcBytes = (DWORD)((lpdwSrc - lpdwSrcBits) << 2);
|
|
|
|
}
|
|
else {
|
|
|
|
|
|
//
|
|
// Read current page of the Src MMR Image.
|
|
// Save the 1st slot for the bit-shifting merge with the Branding.
|
|
//
|
|
|
|
|
|
|
|
if (!GetTiffBits( hTiffSrc, (LPBYTE) (lpdwSrcBits+1), &BufferUsedSize, FILLORDER_LSB2MSB )) {
|
|
goto l_exit;
|
|
}
|
|
|
|
|
|
NumSrcDwords = ( ( ((PTIFF_INSTANCE_DATA) hTiffSrc)->StripDataSize) >> 2) + 1;
|
|
lpdwSrc = lpdwSrcBits;
|
|
lpdwSrcEnd = lpdwSrcBits + NumSrcDwords;
|
|
|
|
//
|
|
// Get the last DWORD of lpdwBrandBits
|
|
//
|
|
|
|
*lpdwSrcBits = *(lpdwMmrBrandBits + DwordsOut);
|
|
|
|
|
|
// copy entire DWORDs in a middle
|
|
|
|
|
|
while (lpdwSrc < lpdwSrcEnd) {
|
|
*lpdwSrc += ( *(lpdwSrc+1) << BitsOut );
|
|
lpdwSrc++;
|
|
*lpdwSrc = *lpdwSrc >> (32 - BitsOut);
|
|
}
|
|
|
|
// add EOLs at the end of each page
|
|
|
|
*(++lpdwSrc) = 0x80000000;
|
|
*(++lpdwSrc) = 0x80000000;
|
|
*(++lpdwSrc) = 0x80000000;
|
|
|
|
TotalSrcBytes = (DWORD)((lpdwSrc - lpdwSrcBits) << 2);
|
|
}
|
|
|
|
|
|
//
|
|
// write src image
|
|
//
|
|
|
|
if (! TiffWriteRaw( hTiffDest, (LPBYTE) lpdwSrcBits, TotalSrcBytes )) {
|
|
goto l_exit;
|
|
}
|
|
|
|
|
|
//
|
|
// prepare Lines TAG. Same for all pages; min avail. lines
|
|
//
|
|
|
|
((PTIFF_INSTANCE_DATA) hTiffDest)->Lines = 32 + ImageHeight + BrandingHeight + 1 ;
|
|
|
|
|
|
if (! TiffEndPage(hTiffDest) ) {
|
|
goto l_exit;
|
|
}
|
|
}
|
|
|
|
bRet = TRUE;
|
|
|
|
l_exit:
|
|
|
|
MemFree(pBrandBits);
|
|
MemFree(pMmrBrandBitsAlloc);
|
|
|
|
VirtualFree ( lpdwSrcBits, 0 , MEM_RELEASE );
|
|
|
|
TiffClose(hTiffSrc);
|
|
TiffClose(hTiffDest);
|
|
|
|
if (bRet == TRUE) {
|
|
// replace the original MH file by the new clean MMR file
|
|
DeleteFile(SrcFileName);
|
|
MoveFile(DestFileName, SrcFileName);
|
|
}
|
|
|
|
MemFree(DestFileName);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TiffGetCurrentPageData(
|
|
HANDLE hTiff,
|
|
LPDWORD Lines,
|
|
LPDWORD StripDataSize,
|
|
LPDWORD ImageWidth,
|
|
LPDWORD ImageHeight
|
|
)
|
|
{
|
|
|
|
PTIFF_INSTANCE_DATA TiffInstance = (PTIFF_INSTANCE_DATA) hTiff;
|
|
|
|
|
|
Assert(TiffInstance);
|
|
|
|
if (Lines) {
|
|
*Lines = TiffInstance->Lines;
|
|
}
|
|
|
|
if (StripDataSize) {
|
|
*StripDataSize = TiffInstance->StripDataSize;
|
|
}
|
|
|
|
if (ImageWidth) {
|
|
*ImageWidth = TiffInstance->ImageWidth;
|
|
}
|
|
|
|
if (ImageHeight) {
|
|
*ImageHeight = TiffInstance->ImageHeight;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AddStringTag(
|
|
HANDLE hFile,
|
|
LPWSTR String,
|
|
WORD TagId,
|
|
PTIFF_TAG MsTags
|
|
)
|
|
{
|
|
BOOL Rval = FALSE;
|
|
LPSTR s;
|
|
DWORD BytesRead;
|
|
|
|
|
|
s = UnicodeStringToAnsiString( String );
|
|
if (!s) {
|
|
return FALSE;
|
|
}
|
|
MsTags->TagId = TagId;
|
|
MsTags->DataType = TIFF_ASCII;
|
|
MsTags->DataCount = strlen(s) + 1;
|
|
if (strlen(s) < 4) {
|
|
strcpy( (LPSTR) &MsTags->DataOffset, s );
|
|
Rval = TRUE;
|
|
} else {
|
|
MsTags->DataOffset = SetFilePointer( hFile, 0, NULL, FILE_CURRENT );
|
|
if (MsTags->DataOffset != 0xffffffff) {
|
|
if (WriteFile( hFile, (LPVOID) s, strlen(s)+1, &BytesRead, NULL )) {
|
|
Rval = TRUE;
|
|
}
|
|
}
|
|
}
|
|
MemFree( s );
|
|
return Rval;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TiffExtractFirstPage(
|
|
LPWSTR FileName,
|
|
LPBYTE *Buffer,
|
|
LPDWORD BufferSize,
|
|
LPDWORD ImageWidth,
|
|
LPDWORD ImageHeight
|
|
)
|
|
{
|
|
PTIFF_INSTANCE_DATA TiffInstance;
|
|
TIFF_INFO TiffInfo;
|
|
|
|
|
|
TiffInstance = TiffOpen( FileName, &TiffInfo, TRUE, FILLORDER_MSB2LSB );
|
|
if (!TiffInstance) {
|
|
return FALSE;
|
|
}
|
|
|
|
*Buffer = VirtualAlloc(
|
|
NULL,
|
|
TiffInstance->StripDataSize,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE
|
|
);
|
|
if (!*Buffer) {
|
|
TiffClose( TiffInstance );
|
|
return FALSE;
|
|
}
|
|
|
|
CopyMemory( *Buffer, TiffInstance->StripData, TiffInstance->StripDataSize );
|
|
*BufferSize = TiffInstance->StripDataSize;
|
|
*ImageWidth = TiffInstance->ImageWidth;
|
|
*ImageHeight = TiffInstance->ImageHeight;
|
|
|
|
TiffClose( TiffInstance );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
TiffAddMsTags(
|
|
LPWSTR FileName,
|
|
PMS_TAG_INFO MsTagInfo
|
|
)
|
|
{
|
|
HANDLE hFile;
|
|
TIFF_HEADER TiffHeader;
|
|
WORD NumDirEntries;
|
|
DWORD BytesRead;
|
|
BOOL rVal = FALSE;
|
|
PTIFF_TAG TiffTags = NULL;
|
|
DWORD IfdSize;
|
|
DWORD NextIFDOffset;
|
|
DWORD NewIFDOffset;
|
|
DWORD i;
|
|
|
|
// Initialize MsTagCnt to 1 because MsTagInfo->FaxTime always gets written
|
|
DWORD MsTagCnt = 1;
|
|
TIFF_TAG MsTags[8] = {0};
|
|
|
|
|
|
if (MsTagInfo->RecipName) {
|
|
MsTagCnt += 1;
|
|
}
|
|
|
|
if (MsTagInfo->RecipNumber) {
|
|
MsTagCnt += 1;
|
|
}
|
|
|
|
if (MsTagInfo->SenderName) {
|
|
MsTagCnt += 1;
|
|
}
|
|
|
|
if (MsTagInfo->Routing) {
|
|
MsTagCnt += 1;
|
|
}
|
|
|
|
if (MsTagInfo->CallerId) {
|
|
MsTagCnt += 1;
|
|
}
|
|
|
|
if (MsTagInfo->Csid) {
|
|
MsTagCnt += 1;
|
|
}
|
|
|
|
if (MsTagInfo->Tsid) {
|
|
MsTagCnt += 1;
|
|
}
|
|
|
|
hFile = CreateFile(
|
|
FileName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL
|
|
);
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// read the tiff header
|
|
//
|
|
|
|
if (!ReadFile( hFile, (LPVOID) &TiffHeader, sizeof(TIFF_HEADER), &BytesRead, NULL )) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// validate that the file is really a tiff file
|
|
//
|
|
|
|
if ((TiffHeader.Identifier != TIFF_LITTLEENDIAN) || (TiffHeader.Version != TIFF_VERSION)) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// position the file to read the ifd's tag count
|
|
//
|
|
|
|
if (SetFilePointer( hFile, TiffHeader.IFDOffset, NULL, FILE_BEGIN ) == 0xffffffff) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// read the tag count for the first ifd
|
|
//
|
|
|
|
if (!ReadFile( hFile, (LPVOID) &NumDirEntries, sizeof(WORD), &BytesRead, NULL )) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// allocate memory for the first ifd's tags
|
|
//
|
|
|
|
IfdSize = NumDirEntries * sizeof(TIFF_TAG);
|
|
TiffTags = (PTIFF_TAG) MemAlloc( IfdSize );
|
|
if (!TiffTags) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// read the the first ifd's tags
|
|
//
|
|
|
|
if (!ReadFile( hFile, (LPVOID) TiffTags, IfdSize, &BytesRead, NULL )) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// read the next pointer
|
|
//
|
|
|
|
if (!ReadFile( hFile, (LPVOID) &NextIFDOffset, sizeof(DWORD), &BytesRead, NULL )) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// position the file to the end
|
|
//
|
|
|
|
if (SetFilePointer( hFile, 0, NULL, FILE_END ) == 0xffffffff) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// write out the strings
|
|
//
|
|
|
|
i = 0;
|
|
|
|
if (MsTagInfo->RecipName) {
|
|
if (AddStringTag( hFile, MsTagInfo->RecipName, TIFFTAG_RECIP_NAME, &MsTags[i] )) {
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
if (MsTagInfo->RecipNumber) {
|
|
if (AddStringTag( hFile, MsTagInfo->RecipNumber, TIFFTAG_RECIP_NUMBER, &MsTags[i] )) {
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
if (MsTagInfo->SenderName) {
|
|
if (AddStringTag( hFile, MsTagInfo->SenderName, TIFFTAG_SENDER_NAME, &MsTags[i] )) {
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
if (MsTagInfo->Routing) {
|
|
if (AddStringTag( hFile, MsTagInfo->Routing, TIFFTAG_ROUTING, &MsTags[i] )) {
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
if (MsTagInfo->CallerId) {
|
|
if (AddStringTag( hFile, MsTagInfo->CallerId, TIFFTAG_CALLERID, &MsTags[i] )) {
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
if (MsTagInfo->Tsid) {
|
|
if (AddStringTag( hFile, MsTagInfo->Tsid, TIFFTAG_TSID, &MsTags[i] )) {
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
if (MsTagInfo->Csid) {
|
|
if (AddStringTag( hFile, MsTagInfo->Csid, TIFFTAG_CSID, &MsTags[i] )) {
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
MsTags[i].DataOffset = SetFilePointer( hFile, 0, NULL, FILE_CURRENT );
|
|
if (!WriteFile( hFile, (LPVOID) &MsTagInfo->FaxTime, sizeof(MsTagInfo->FaxTime), &BytesRead, NULL )) {
|
|
goto exit;
|
|
}
|
|
MsTags[i].TagId = TIFFTAG_FAX_TIME;
|
|
MsTags[i].DataType = TIFF_SRATIONAL;
|
|
MsTags[i].DataCount = sizeof(MsTagInfo->FaxTime);
|
|
|
|
//
|
|
// get the current file position - this is used to set the linked list pointer
|
|
//
|
|
|
|
NewIFDOffset = SetFilePointer( hFile, 0, NULL, FILE_CURRENT );
|
|
if (NewIFDOffset == 0xffffffff) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// write the tag count for the first ifd
|
|
//
|
|
|
|
NumDirEntries += (WORD) MsTagCnt;
|
|
|
|
if (!WriteFile( hFile, (LPVOID) &NumDirEntries, sizeof(WORD), &BytesRead, NULL )) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// write out the original tags
|
|
//
|
|
|
|
if (!WriteFile( hFile, (LPVOID) TiffTags, IfdSize, &BytesRead, NULL )) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// write out the microsoft specific tags
|
|
//
|
|
|
|
if (!WriteFile( hFile, (LPVOID) &MsTags, sizeof(TIFF_TAG)*MsTagCnt, &BytesRead, NULL )) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// write the next pointer
|
|
//
|
|
|
|
if (!WriteFile( hFile, (LPVOID) &NextIFDOffset, sizeof(DWORD), &BytesRead, NULL )) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// re-write the tiff header
|
|
//
|
|
|
|
//
|
|
// position the file to the beginning
|
|
//
|
|
|
|
if (SetFilePointer( hFile, 0, NULL, FILE_BEGIN ) == 0xffffffff) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// write the tiff header
|
|
//
|
|
|
|
TiffHeader.IFDOffset = NewIFDOffset;
|
|
|
|
if (!WriteFile( hFile, (LPVOID) &TiffHeader, sizeof(TIFF_HEADER), &BytesRead, NULL )) {
|
|
goto exit;
|
|
}
|
|
|
|
rVal = TRUE;
|
|
|
|
exit:
|
|
MemFree( TiffTags );
|
|
CloseHandle( hFile );
|
|
return rVal;
|
|
}
|
|
|
|
|
|
BOOL
|
|
PrintTiffFile(
|
|
HDC PrinterDC,
|
|
LPWSTR FileName
|
|
)
|
|
{
|
|
BOOL Rval = TRUE;
|
|
TIFF_INFO TiffInfo;
|
|
HANDLE hTiff = NULL;
|
|
PTIFF_INSTANCE_DATA TiffInstance = NULL;
|
|
DWORD i,j;
|
|
INT HorzRes;
|
|
INT VertRes;
|
|
BOOL Result = FALSE;
|
|
DWORD VertResFactor = 1;
|
|
PTIFF_TAG TiffTags = NULL;
|
|
DWORD XRes = 0;
|
|
DWORD YRes = 0;
|
|
LPBYTE Bitmap;
|
|
LPBYTE dPtr;
|
|
LPBYTE sPtr;
|
|
INT DestWidth;
|
|
INT DestHeight;
|
|
FLOAT ScaleX;
|
|
FLOAT ScaleY;
|
|
FLOAT Scale;
|
|
DWORD LineSize;
|
|
struct {
|
|
|
|
BITMAPINFOHEADER bmiHeader;
|
|
RGBQUAD bmiColors[2];
|
|
|
|
} SrcBitmapInfo = {
|
|
|
|
{
|
|
sizeof(BITMAPINFOHEADER), // biSize
|
|
0, // biWidth
|
|
0, // biHeight
|
|
1, // biPlanes
|
|
1, // biBitCount
|
|
BI_RGB, // biCompression
|
|
0, // biSizeImage
|
|
7874, // biXPelsPerMeter - 200dpi
|
|
7874, // biYPelsPerMeter
|
|
0, // biClrUsed
|
|
0, // biClrImportant
|
|
},
|
|
{
|
|
{
|
|
0, // rgbBlue
|
|
0, // rgbGreen
|
|
0, // rgbRed
|
|
0 // rgbReserved
|
|
},
|
|
{
|
|
255, // rgbBlue
|
|
255, // rgbGreen
|
|
255, // rgbRed
|
|
0 // rgbReserved
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
//
|
|
// open the tiff file
|
|
//
|
|
|
|
hTiff = TiffOpen( FileName, &TiffInfo, TRUE, FILLORDER_MSB2LSB );
|
|
if (hTiff == NULL) {
|
|
goto exit;
|
|
}
|
|
|
|
TiffInstance = (PTIFF_INSTANCE_DATA) hTiff;
|
|
|
|
if (!TiffInfo.PhotometricInterpretation) {
|
|
//
|
|
// white is zero
|
|
//
|
|
SrcBitmapInfo.bmiColors[1].rgbBlue = 0;
|
|
SrcBitmapInfo.bmiColors[1].rgbGreen = 0;
|
|
SrcBitmapInfo.bmiColors[1].rgbRed = 0;
|
|
SrcBitmapInfo.bmiColors[1].rgbReserved = 0;
|
|
SrcBitmapInfo.bmiColors[0].rgbBlue = 0xFF;
|
|
SrcBitmapInfo.bmiColors[0].rgbGreen = 0xFF;
|
|
SrcBitmapInfo.bmiColors[0].rgbRed = 0xFF;
|
|
SrcBitmapInfo.bmiColors[0].rgbReserved = 0;
|
|
}
|
|
|
|
HorzRes = GetDeviceCaps( PrinterDC, HORZRES );
|
|
VertRes = GetDeviceCaps( PrinterDC, VERTRES );
|
|
|
|
for (i=0; i<TiffInfo.PageCount; i++) {
|
|
|
|
if (!TiffSeekToPage( hTiff, i+1, FILLORDER_MSB2LSB )) {
|
|
goto exit;
|
|
}
|
|
|
|
if (TiffInstance->YResolution < 100) {
|
|
SrcBitmapInfo.bmiHeader.biYPelsPerMeter /= 2;
|
|
VertResFactor = 2;
|
|
}
|
|
LineSize = TiffInstance->ImageWidth / 8;
|
|
LineSize += (TiffInstance->ImageWidth % 8) ? 1 : 0;
|
|
Bitmap = (LPBYTE) VirtualAlloc( NULL, TiffInstance->StripDataSize+(TiffInstance->ImageHeight*sizeof(DWORD)), MEM_COMMIT, PAGE_READWRITE );
|
|
if (Bitmap) {
|
|
sPtr = TiffInstance->StripData;
|
|
dPtr = Bitmap;
|
|
for (j=0; j<TiffInstance->ImageHeight; j++) {
|
|
CopyMemory( dPtr, sPtr, LineSize );
|
|
sPtr += LineSize;
|
|
dPtr = (LPBYTE) Align( 4, (ULONG_PTR)dPtr+LineSize );
|
|
}
|
|
StartPage( PrinterDC );
|
|
ScaleX = (FLOAT) TiffInstance->ImageWidth / (FLOAT) HorzRes;
|
|
ScaleY = ((FLOAT) TiffInstance->ImageHeight * VertResFactor) / (FLOAT) VertRes;
|
|
Scale = ScaleX > ScaleY ? ScaleX : ScaleY;
|
|
DestWidth = (int) ((FLOAT) TiffInstance->ImageWidth / Scale);
|
|
DestHeight = (int) (((FLOAT) TiffInstance->ImageHeight * VertResFactor) / Scale);
|
|
SrcBitmapInfo.bmiHeader.biWidth = TiffInstance->ImageWidth;
|
|
SrcBitmapInfo.bmiHeader.biHeight = - (INT) TiffInstance->ImageHeight;
|
|
StretchDIBits(
|
|
PrinterDC,
|
|
0,
|
|
0,
|
|
DestWidth,
|
|
DestHeight,
|
|
0,
|
|
0,
|
|
TiffInstance->ImageWidth,
|
|
TiffInstance->ImageHeight,
|
|
Bitmap,
|
|
(BITMAPINFO *) &SrcBitmapInfo,
|
|
DIB_RGB_COLORS,
|
|
SRCCOPY
|
|
);
|
|
EndPage( PrinterDC );
|
|
VirtualFree( Bitmap, 0, MEM_RELEASE );
|
|
} else {
|
|
Rval = FALSE;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (hTiff) {
|
|
TiffClose( hTiff );
|
|
}
|
|
|
|
return Rval;
|
|
|
|
}
|
|
|
|
|
|
BOOL
|
|
ConvertTiffFileToValidFaxFormat(
|
|
LPWSTR TiffFileName,
|
|
LPWSTR NewFileName,
|
|
LPDWORD Flags
|
|
)
|
|
{
|
|
BOOL Rval = FALSE;
|
|
DWORD i;
|
|
TIFF_INFO TiffInfo;
|
|
HANDLE hTiff = NULL;
|
|
PTIFF_INSTANCE_DATA TiffInstance = NULL;
|
|
PTIFF_INSTANCE_DATA TiffInstanceMmr = NULL;
|
|
LPBYTE Buffer = NULL;
|
|
DWORD BufferSize;
|
|
DWORD ResultSize;
|
|
LPBYTE CompBuffer;
|
|
FILE_MAPPING fmTemp = {0};
|
|
PTIFF_HEADER TiffHdr;
|
|
LPBYTE p;
|
|
DWORD CurrOffset;
|
|
LPDWORD LastIfdOffset;
|
|
PFAXIFD TiffIfd;
|
|
DWORD CompressionType;
|
|
DWORD G3Options;
|
|
DWORD XResolution;
|
|
DWORD YResolution;
|
|
DWORD PageWidth;
|
|
DWORD PageBytes;
|
|
BOOL ValidFaxTiff;
|
|
PTIFF_TAG TiffTags;
|
|
DWORD IfdOffset;
|
|
WORD NumDirEntries;
|
|
BOOL ProduceUncompressedBits = FALSE;
|
|
DWORD NewFileSize;
|
|
|
|
|
|
*Flags = 0;
|
|
|
|
//
|
|
// open the tiff file
|
|
//
|
|
|
|
hTiff = TiffOpen( TiffFileName, &TiffInfo, TRUE, FILLORDER_MSB2LSB );
|
|
if (hTiff == NULL) {
|
|
*Flags |= TIFFCF_NOT_TIFF_FILE;
|
|
goto exit;
|
|
}
|
|
|
|
TiffInstance = (PTIFF_INSTANCE_DATA) hTiff;
|
|
|
|
//
|
|
// check to see if the if good
|
|
//
|
|
|
|
IfdOffset = TiffInstance->TiffHdr.IFDOffset;
|
|
ValidFaxTiff = TRUE;
|
|
|
|
while ( IfdOffset ) {
|
|
|
|
//
|
|
// get the count of tags in this IFD
|
|
//
|
|
|
|
NumDirEntries = *(LPWORD)(TiffInstance->fPtr + IfdOffset);
|
|
|
|
//
|
|
// set the tag pointer
|
|
//
|
|
|
|
TiffTags = (PTIFF_TAG)(TiffInstance->fPtr + IfdOffset + sizeof(WORD));
|
|
|
|
//
|
|
// get the tiff information
|
|
//
|
|
|
|
CompressionType = 0;
|
|
G3Options = 0;
|
|
PageWidth = 0;
|
|
XResolution = 0;
|
|
YResolution = 0;
|
|
|
|
for (i=0; i<NumDirEntries; i++) {
|
|
switch( TiffTags[i].TagId ) {
|
|
case TIFFTAG_COMPRESSION:
|
|
CompressionType = GetTagData( TiffInstance->fPtr, 0, &TiffTags[i] );
|
|
break;
|
|
|
|
case TIFFTAG_GROUP3OPTIONS:
|
|
G3Options = GetTagData( TiffInstance->fPtr, 0, &TiffTags[i] );
|
|
break;
|
|
|
|
case TIFFTAG_XRESOLUTION:
|
|
XResolution = GetTagData( TiffInstance->fPtr, 0, &TiffTags[i] );
|
|
break;
|
|
|
|
case TIFFTAG_YRESOLUTION:
|
|
YResolution = GetTagData( TiffInstance->fPtr, 0, &TiffTags[i] );
|
|
break;
|
|
|
|
case TIFFTAG_IMAGEWIDTH:
|
|
PageWidth = GetTagData( TiffInstance->fPtr, 0, &TiffTags[i] );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (CompressionType == COMPRESSION_NONE) {
|
|
*Flags |= TIFFCF_UNCOMPRESSED_BITS;
|
|
} else if ((CompressionType == COMPRESSION_CCITTFAX3 && (G3Options & GROUP3OPT_2DENCODING)) ||
|
|
(CompressionType == COMPRESSION_CCITTFAX4 && PageWidth == FAXBITS))
|
|
{
|
|
ValidFaxTiff = TRUE;
|
|
} else {
|
|
//
|
|
// unsupported compression type
|
|
// try to use imaging program to print the tiff file,it might understand the compression scheme
|
|
//
|
|
ValidFaxTiff = FALSE;
|
|
*Flags = TIFFCF_NOT_TIFF_FILE;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// the resolution check must account for offical Class F tiff
|
|
// documents and pseudo fax documents created by scanners and
|
|
// imaging applications.
|
|
//
|
|
// |-------------|----------|----------|---------|
|
|
// | scan width | pels | xres | yres |
|
|
// |-------------|----------|----------|---------|
|
|
// | | | | |
|
|
// | 8.46/215 | 1728 | 204 | 98/196 |
|
|
// | | | | |
|
|
// | 8.50/216 | 1700 | 200 | 200 |
|
|
// | | | | |
|
|
// |-------------|----------|----------|---------|
|
|
//
|
|
|
|
if (XResolution > 204 || YResolution > 200 || PageWidth > FAXBITS) {
|
|
//
|
|
// the file cannot be converted to valid fax bits
|
|
// so we produce a tiff file that has uncompressed bits
|
|
// the caller can then render the uncompressed bits
|
|
// using the fax print driver to get good fax bits
|
|
//
|
|
ProduceUncompressedBits = TRUE;
|
|
*Flags |= TIFFCF_UNCOMPRESSED_BITS;
|
|
ValidFaxTiff = FALSE;
|
|
}
|
|
|
|
//
|
|
// get the next IFD offset
|
|
//
|
|
|
|
IfdOffset = *(UNALIGNED DWORD *)(TiffInstance->fPtr + (NumDirEntries * sizeof(TIFF_TAG)) + IfdOffset + sizeof(WORD));
|
|
}
|
|
|
|
if (ValidFaxTiff) {
|
|
*Flags |= TIFFCF_ORIGINAL_FILE_GOOD;
|
|
Rval = TRUE;
|
|
goto exit;
|
|
}
|
|
|
|
PageWidth = max( TiffInstance->ImageWidth, FAXBITS );
|
|
PageBytes = (PageWidth/8)+(PageWidth%8?1:0);
|
|
|
|
//
|
|
// open the temporary file to hold the new mmr tiff data
|
|
//
|
|
|
|
if (ProduceUncompressedBits) {
|
|
NewFileSize = sizeof(TIFF_HEADER) + (TiffInstance->PageCount * (sizeof(FAXIFD) + (TiffInfo.ImageHeight * PageWidth)));
|
|
} else {
|
|
NewFileSize = GetFileSize( TiffInstance->hFile, NULL );
|
|
}
|
|
|
|
if (!MapFileOpen( NewFileName, FALSE, NewFileSize, &fmTemp )) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// allocate a temporary buffer big enough to hold an uncompressed image
|
|
//
|
|
|
|
BufferSize = TiffInfo.ImageHeight * PageWidth;
|
|
|
|
Buffer = VirtualAlloc(
|
|
NULL,
|
|
BufferSize,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE
|
|
);
|
|
if (!Buffer) {
|
|
goto exit;
|
|
}
|
|
|
|
CompBuffer = VirtualAlloc(
|
|
NULL,
|
|
GetFileSize(TiffInstance->hFile,NULL),
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE
|
|
);
|
|
if (!CompBuffer) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// convert the tiff data to mmr
|
|
//
|
|
|
|
TiffHdr = (PTIFF_HEADER) fmTemp.fPtr;
|
|
|
|
TiffHdr->Identifier = TIFF_LITTLEENDIAN;
|
|
TiffHdr->Version = TIFF_VERSION;
|
|
TiffHdr->IFDOffset = 0;
|
|
|
|
p = fmTemp.fPtr + sizeof(TIFF_HEADER);
|
|
CurrOffset = sizeof(TIFF_HEADER);
|
|
LastIfdOffset = (LPDWORD) (p - sizeof(DWORD));
|
|
|
|
TiffInstanceMmr = TiffCreate( NULL, TIFF_COMPRESSION_MMR, PageWidth, FILLORDER_MSB2LSB, 1 );
|
|
if (TiffInstanceMmr == NULL) {
|
|
goto exit;
|
|
}
|
|
|
|
for (i=0; i<TiffInfo.PageCount; i++) {
|
|
|
|
//
|
|
// position the file pointers and read the raw data
|
|
//
|
|
|
|
if (!TiffSeekToPage( hTiff, i+1, FILLORDER_MSB2LSB )) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// get the uncompressed bits
|
|
//
|
|
|
|
if (!TiffRead( hTiff, Buffer, ProduceUncompressedBits ? 0 : FAXBITS )) {
|
|
goto exit;
|
|
}
|
|
|
|
if (ProduceUncompressedBits) {
|
|
|
|
ResultSize = PageBytes * TiffInstance->ImageHeight;
|
|
CopyMemory( p, Buffer, ResultSize );
|
|
|
|
} else {
|
|
|
|
//
|
|
// compress the bits
|
|
//
|
|
|
|
TiffInstanceMmr->bitbuf = CompBuffer;
|
|
TiffInstanceMmr->bitcnt = DWORDBITS;
|
|
TiffInstanceMmr->bitdata = 0;
|
|
TiffInstanceMmr->BytesPerLine = PageBytes;
|
|
|
|
EncodeFaxPageMmrCompression(
|
|
TiffInstanceMmr,
|
|
Buffer,
|
|
PageWidth,
|
|
TiffInstance->ImageHeight,
|
|
&ResultSize
|
|
);
|
|
|
|
CopyMemory( p, CompBuffer, ResultSize );
|
|
}
|
|
|
|
CurrOffset += ResultSize;
|
|
p += ResultSize;
|
|
|
|
*LastIfdOffset = (DWORD)(p - fmTemp.fPtr);
|
|
|
|
//
|
|
// write the ifd
|
|
//
|
|
|
|
TiffIfd = (PFAXIFD) p;
|
|
|
|
CopyMemory( TiffIfd, &FaxIFDTemplate, sizeof(FaxIFDTemplate) );
|
|
|
|
TiffIfd->yresNum = TiffInstance->XResolution;
|
|
TiffIfd->xresNum = TiffInstance->YResolution;
|
|
TiffIfd->ifd[IFD_PAGENUMBER].value = MAKELONG( i+1, 0);
|
|
TiffIfd->ifd[IFD_IMAGEWIDTH].value = PageWidth;
|
|
TiffIfd->ifd[IFD_IMAGEHEIGHT].value = TiffInstance->ImageHeight;
|
|
TiffIfd->ifd[IFD_ROWSPERSTRIP].value = TiffInstance->ImageHeight;
|
|
TiffIfd->ifd[IFD_STRIPBYTECOUNTS].value = ResultSize;
|
|
TiffIfd->ifd[IFD_STRIPOFFSETS].value = CurrOffset - ResultSize;
|
|
TiffIfd->ifd[IFD_XRESOLUTION].value = CurrOffset + FIELD_OFFSET( FAXIFD, xresNum );
|
|
TiffIfd->ifd[IFD_YRESOLUTION].value = CurrOffset + FIELD_OFFSET( FAXIFD, yresNum );
|
|
TiffIfd->ifd[IFD_SOFTWARE].value = CurrOffset + FIELD_OFFSET( FAXIFD, software );
|
|
TiffIfd->ifd[IFD_FILLORDER].value = FILLORDER_MSB2LSB;
|
|
TiffIfd->ifd[IFD_COMPRESSION].value = ProduceUncompressedBits ? COMPRESSION_NONE : TIFF_COMPRESSION_MMR;
|
|
TiffIfd->ifd[IFD_G3OPTIONS].value = ProduceUncompressedBits ? 0 : GROUP3OPT_FILLBITS | GROUP3OPT_2DENCODING;
|
|
|
|
//
|
|
// update the page pointers
|
|
//
|
|
|
|
LastIfdOffset = (LPDWORD) (p + FIELD_OFFSET(FAXIFD,nextIFDOffset));
|
|
CurrOffset += sizeof(FAXIFD);
|
|
p += sizeof(FAXIFD);
|
|
}
|
|
|
|
Rval = TRUE;
|
|
|
|
exit:
|
|
if (fmTemp.hFile) {
|
|
MapFileClose( &fmTemp, CurrOffset );
|
|
}
|
|
if (hTiff) {
|
|
TiffClose( hTiff );
|
|
}
|
|
if (TiffInstanceMmr) {
|
|
TiffClose( TiffInstanceMmr );
|
|
}
|
|
if (Buffer) {
|
|
VirtualFree( Buffer, 0, MEM_RELEASE);
|
|
}
|
|
if (CompBuffer) {
|
|
VirtualFree( CompBuffer, 0, MEM_RELEASE);
|
|
}
|
|
|
|
return Rval;
|
|
}
|
|
|
|
|
|
BOOL
|
|
MergeTiffFiles(
|
|
LPWSTR BaseTiffFile,
|
|
LPWSTR NewTiffFile
|
|
)
|
|
{
|
|
BOOL Rval = TRUE;
|
|
FILE_MAPPING fmBase;
|
|
FILE_MAPPING fmNew;
|
|
LPBYTE p;
|
|
DWORD NextIfdOffset;
|
|
WORD TagCount;
|
|
PTIFF_TAG TiffTag;
|
|
DWORD i;
|
|
DWORD j;
|
|
LPBYTE StripOffsets;
|
|
DWORD DataSize;
|
|
DWORD Delta;
|
|
DWORD Space;
|
|
|
|
|
|
//
|
|
// open the files
|
|
//
|
|
|
|
if (!MapFileOpen( NewTiffFile, TRUE, 0, &fmNew )) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!MapFileOpen( BaseTiffFile, FALSE, fmNew.fSize, &fmBase )) {
|
|
MapFileClose( &fmNew, 0 );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// append the new file to the end of the base file
|
|
//
|
|
|
|
p = fmNew.fPtr + sizeof(TIFF_HEADER);
|
|
CopyMemory( fmBase.fPtr+fmBase.fSize, p, fmNew.fSize-sizeof(TIFF_HEADER) );
|
|
|
|
//
|
|
// fix up the ifd pointers in the appended tiff data
|
|
//
|
|
|
|
Delta = fmBase.fSize - sizeof(TIFF_HEADER);
|
|
|
|
NextIfdOffset = ((PTIFF_HEADER)fmNew.fPtr)->IFDOffset;
|
|
while (NextIfdOffset) {
|
|
p = fmBase.fPtr + NextIfdOffset + Delta;
|
|
TagCount = *((LPWORD)p);
|
|
//
|
|
// fixup the data offsets in the tiff tags
|
|
//
|
|
TiffTag = (PTIFF_TAG) (p + sizeof(WORD));
|
|
for (i=0; i<TagCount; i++) {
|
|
DataSize = TiffDataWidth[TiffTag[i].DataType];
|
|
Space = TiffTag[i].DataCount * DataSize;
|
|
if (Space > 4) {
|
|
TiffTag[i].DataOffset += Delta;
|
|
}
|
|
if (TiffTag[i].TagId == TIFFTAG_STRIPOFFSETS) {
|
|
if (Space > 4) {
|
|
StripOffsets = (LPBYTE) (fmBase.fPtr + TiffTag[i].DataOffset);
|
|
for (j=0; j<TiffTag[i].DataCount; j++) {
|
|
if (TiffTag[i].DataType == TIFF_SHORT) {
|
|
*((LPWORD)StripOffsets) += (WORD)Delta;
|
|
} else {
|
|
*((LPDWORD)StripOffsets) += Delta;
|
|
}
|
|
StripOffsets += DataSize;
|
|
}
|
|
} else {
|
|
if (TiffTag[i].DataCount > 1) {
|
|
Rval = FALSE;
|
|
goto exit;
|
|
}
|
|
TiffTag[i].DataOffset += Delta;
|
|
}
|
|
}
|
|
}
|
|
p = p + sizeof(WORD) + (TagCount * sizeof(TIFF_TAG));
|
|
NextIfdOffset = *((LPDWORD)p);
|
|
if (NextIfdOffset) {
|
|
*((LPDWORD)p) = NextIfdOffset + Delta;
|
|
}
|
|
}
|
|
|
|
//
|
|
// find the last ifd offset in the chain for the base
|
|
// file and change it to point to the first ifd in the
|
|
// data that was appended
|
|
//
|
|
|
|
NextIfdOffset = ((PTIFF_HEADER)fmBase.fPtr)->IFDOffset;
|
|
while (NextIfdOffset) {
|
|
p = fmBase.fPtr + NextIfdOffset;
|
|
TagCount = *((LPWORD)p);
|
|
p = p + sizeof(WORD) + (TagCount * sizeof(TIFF_TAG));
|
|
NextIfdOffset = *((LPDWORD)p);
|
|
}
|
|
|
|
*((LPDWORD)p) = (DWORD)(Delta + ((PTIFF_HEADER)fmNew.fPtr)->IFDOffset);
|
|
|
|
exit:
|
|
//
|
|
// close the files
|
|
//
|
|
|
|
MapFileClose( &fmBase, fmBase.fSize+fmNew.fSize-sizeof(TIFF_HEADER) );
|
|
MapFileClose( &fmNew, 0 );
|
|
|
|
return Rval;
|
|
}
|
|
|
|
BOOL
|
|
TiffRecoverGoodPages(
|
|
LPTSTR SrcFileName,
|
|
LPDWORD RecoveredPages,
|
|
LPDWORD TotalPages
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Try to recover the good data out of the source and put it into the destination file
|
|
|
|
Arguments:
|
|
|
|
SrcFileName - source file name
|
|
RecoveredPages - number of pages we were able to recover
|
|
TotalPages - total pages in the tiff file
|
|
|
|
Return Value:
|
|
|
|
TRUE for success, FALSE for failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
TIFF_INFO TiffInfo;
|
|
PTIFF_INSTANCE_DATA TiffInstance = NULL;
|
|
BOOL bSuccess = FALSE;
|
|
|
|
|
|
if (!SrcFileName || !RecoveredPages || !TotalPages) {
|
|
return FALSE;
|
|
}
|
|
|
|
*RecoveredPages = 0;
|
|
*TotalPages = 0;
|
|
|
|
TiffInstance = (PTIFF_INSTANCE_DATA) TiffOpen(SrcFileName,&TiffInfo,FALSE,FILLORDER_LSB2MSB);
|
|
|
|
if (!TiffInstance) {
|
|
*TotalPages = 0;
|
|
goto exit;
|
|
}
|
|
|
|
*TotalPages = TiffInfo.PageCount;
|
|
|
|
|
|
if (TiffInstance->ImageHeight) {
|
|
//
|
|
// should be view-able
|
|
//
|
|
TiffClose( (HANDLE) TiffInstance );
|
|
return TRUE;
|
|
}
|
|
|
|
if (*TotalPages <=1) {
|
|
//
|
|
// no data to recover
|
|
//
|
|
goto exit;
|
|
}
|
|
|
|
switch (TiffInstance->CompressionType) {
|
|
|
|
case TIFF_COMPRESSION_MH:
|
|
|
|
if (!PostProcessMhToMmr( (HANDLE) TiffInstance, TiffInfo, NULL )) {
|
|
*RecoveredPages = TiffInfo.PageCount;
|
|
goto exit;
|
|
}
|
|
break;
|
|
|
|
case TIFF_COMPRESSION_MR:
|
|
|
|
if (!PostProcessMrToMmr( (HANDLE) TiffInstance, TiffInfo, NULL )) {
|
|
goto exit;
|
|
}
|
|
break;
|
|
|
|
case TIFF_COMPRESSION_MMR:
|
|
bSuccess = TRUE;
|
|
goto exit;
|
|
break;
|
|
default:
|
|
//
|
|
// unexpected compression type
|
|
//
|
|
DebugPrint((TEXT("TiffRecoverGoodPages: %s: Unexpected Compression type %d\n"),
|
|
TiffInstance->FileName,
|
|
TiffInstance->CompressionType));
|
|
goto exit;
|
|
}
|
|
|
|
*RecoveredPages = TiffInfo.PageCount;
|
|
*TotalPages += 1;
|
|
|
|
bSuccess = TRUE;
|
|
|
|
exit:
|
|
|
|
return bSuccess;
|
|
|
|
}
|