2025-04-27 07:49:33 -04:00

709 lines
17 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
faxdump.c
Abstract:
Extract a single page out of the fax driver output file
Environment:
Fax driver, utility
Revision History:
01/11/96 -davidx-
Created it.
mm/dd/yy -author-
description
--*/
#include "faxlib.h"
typedef PVOID PDEVDATA;
#include "faxtiff.h"
#include "faxtable.h"
static const CODETABLE MMRCodes[] = {
{ 7, 0x02 }, // VL3
{ 6, 0x02 }, // VL2
{ 3, 0x02 }, // VL1
{ 1, 0x01 }, // V0
{ 3, 0x03 }, // VR1
{ 6, 0x03 }, // VR2
{ 7, 0x03 }, // VR3
{ 4, 0x01 }, // PASSCODE
{ 3, 0x01 }, // HORZCODE
};
typedef const CODETABLE *PCODETABLE;
#define MMR_TABLE_SIZE (sizeof(MMRCodes) / sizeof(CODETABLE))
#define RUN_TABLE_SIZE (sizeof(BlackRunCodes) / sizeof(CODETABLE))
#define BYTEBITS 8
#define DWORDBITS 32
#define ErrorExit(arg) { printf arg; DebugBreak(); }
INT
FindCode(
PCODETABLE pCodeTable,
INT tableSize,
DWORD code,
DWORD bitcnt
)
{
INT index;
for (index=0; index < tableSize; index++) {
if (bitcnt >= pCodeTable[index].length &&
pCodeTable[index].code == (code >> (32 - pCodeTable[index].length)))
{
return index;
}
}
return -1;
}
INT
FindWhiteRun(
PBYTE pbuf,
INT startBit,
INT stopBit
)
/*++
Routine Description:
Find the next span of white pixels on the specified line
Arguments:
pbuf - Points to uncompressed pixel data for the current line
startBit - Starting bit index
stopBit - Last bit index
Return Value:
Length of the next run of white pixels
--*/
{
static const BYTE WhiteRuns[256] = {
8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
INT run, bits, n;
pbuf += (startBit >> 3);
if ((bits = stopBit-startBit) <= 0)
return 0;
//
// Take care of the case where starting bit index is not a multiple of 8
//
if (n = (startBit & 7)) {
run = WhiteRuns[(*pbuf << n) & 0xff];
if (run > BYTEBITS-n)
run = BYTEBITS-n;
if (n+run < BYTEBITS)
return run;
bits -= run;
pbuf++;
} else
run = 0;
//
// Look for consecutive DWORD value = 0
//
if (bits >= DWORDBITS * 2) {
PDWORD pdw;
//
// Align to a DWORD boundary first
//
while ((DWORD) pbuf & 3) {
if (*pbuf != 0)
return run + WhiteRuns[*pbuf];
run += BYTEBITS;
bits -= BYTEBITS;
pbuf++;
}
pdw = (PDWORD) pbuf;
while (bits >= DWORDBITS && *pdw == 0) {
pdw++;
run += DWORDBITS;
bits -= DWORDBITS;
}
pbuf = (PBYTE) pdw;
}
//
// Look for consecutive BYTE value = 0
//
while (bits >= BYTEBITS) {
if (*pbuf != 0)
return run + WhiteRuns[*pbuf];
pbuf++;
run += BYTEBITS;
bits -= BYTEBITS;
}
//
// Count the number of white pixels in the last byte
//
if (bits > 0)
run += WhiteRuns[*pbuf];
return run;
}
INT
FindBlackRun(
PBYTE pbuf,
INT startBit,
INT stopBit
)
/*++
Routine Description:
Find the next span of black pixels on the specified line
Arguments:
pbuf - Points to uncompressed pixel data for the current line
startBit - Starting bit index
stopBit - Last bit index
Return Value:
Length of the next run of black pixels
--*/
{
static const BYTE BlackRuns[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8
};
INT run, bits, n;
pbuf += (startBit >> 3);
if ((bits = stopBit-startBit) <= 0)
return 0;
//
// Take care of the case where starting bit index is not a multiple of 8
//
if (n = (startBit & 7)) {
run = BlackRuns[(*pbuf << n) & 0xff];
if (run > BYTEBITS-n)
run = BYTEBITS-n;
if (n+run < BYTEBITS)
return run;
bits -= run;
pbuf++;
} else
run = 0;
//
// Look for consecutive DWORD value = 0xffffffff
//
if (bits >= DWORDBITS * 2) {
PDWORD pdw;
//
// Align to a DWORD boundary first
//
while ((DWORD) pbuf & 3) {
if (*pbuf != 0xff)
return run + BlackRuns[*pbuf];
run += BYTEBITS;
bits -= BYTEBITS;
pbuf++;
}
pdw = (PDWORD) pbuf;
while (bits >= DWORDBITS && *pdw == 0xffffffff) {
pdw++;
run += DWORDBITS;
bits -= DWORDBITS;
}
pbuf = (PBYTE) pdw;
}
//
// Look for consecutive BYTE value = 0xff
//
while (bits >= BYTEBITS) {
if (*pbuf != 0xff)
return run + BlackRuns[*pbuf];
pbuf++;
run += BYTEBITS;
bits -= BYTEBITS;
}
//
// Count the number of white pixels in the last byte
//
if (bits > 0)
run += BlackRuns[*pbuf];
return run;
}
VOID
OutputRun(
PBYTE plinebuf,
INT startbit,
INT run,
INT color
)
{
//
// Since the line buffer is always zero initalized, we don't
// need to do anything when output a white run.
//
if (color == 0 || run == 0)
return;
plinebuf += (startbit >> 3);
if ((startbit &= 7) != 0) {
if (run >= 8 - startbit) {
*plinebuf++ |= 0xff >> startbit;
run -= 8 - startbit;
} else {
*plinebuf |= 0xff >> startbit;
startbit += run;
*plinebuf &= ~(0xff >> startbit);
return;
}
}
while (run >= 8) {
*plinebuf++ = 0xff;
run -= 8;
}
if (run > 0)
*plinebuf = 0xff << (8 - run);
}
VOID
DecodeMMR(
PBYTE pBuffer,
LONG bytecnt,
LONG width,
LONG height
)
#define ReadCompressedBits() \
while (bitCount <= 24 && bytecnt > 0) { \
codeValue |= *pBuffer++ << (24 - bitCount); \
bitCount += 8; \
bytecnt--; \
}
{
PBYTE prefline, plinebuf;
LONG lineByteCnt, lineIndex;
DWORD codeValue, bitCount;
INT codeTableIndex, color, run, horiz;
INT a0, a1, b1, b2;
PCODETABLE pCodeTable;
lineByteCnt = (width + 7) / 8;
prefline = (PBYTE) LocalAlloc(LPTR, lineByteCnt);
plinebuf = (PBYTE) LocalAlloc(LPTR, lineByteCnt);
if (!prefline || !plinebuf)
ErrorExit(("Memory allocation failed\n"));
bitCount = codeValue = 0;
for (lineIndex=0; lineIndex < height; lineIndex++) {
color = 0;
a0 = 0;
b1 = GetBit(prefline, 0) ? 0 : NextChangingElement(prefline, 0, width, 0);
while (TRUE) {
b2 = (b1 >= width) ? width :
NextChangingElement(prefline, b1, width, GetBit(prefline, b1));
ReadCompressedBits();
codeTableIndex = FindCode(MMRCodes, MMR_TABLE_SIZE, codeValue, bitCount);
if (codeTableIndex == -1)
ErrorExit(("Unrecognized code on line %d\n", lineIndex));
bitCount -= MMRCodes[codeTableIndex].length;
codeValue <<= MMRCodes[codeTableIndex].length;
switch (codeTableIndex) {
case 0: // VL3
case 1: // VL2
case 2: // VL1
case 3: // V0
case 4: // VR1
case 5: // VR2
case 6: // VR3
a1 = b1 + codeTableIndex - 3;
OutputRun(plinebuf, a0, a1 - a0, color);
color ^= 1;
a0 = a1;
break;
case 7: // PASSCODE
OutputRun(plinebuf, a0, b2 - a0, color);
a0 = b2;
break;
case 8: // HORZCODE
for (horiz=0; horiz < 2; horiz++) {
pCodeTable = color ? BlackRunCodes : WhiteRunCodes;
do {
ReadCompressedBits();
codeTableIndex = FindCode(pCodeTable, RUN_TABLE_SIZE, codeValue, bitCount);
if (codeTableIndex == -1)
ErrorExit(("Unrecognized code on line %d\n", lineIndex));
bitCount -= pCodeTable[codeTableIndex].length;
codeValue <<= pCodeTable[codeTableIndex].length;
run = (codeTableIndex < 64) ? codeTableIndex : (codeTableIndex - 63) * 64;
OutputRun(plinebuf, a0, run, color);
a0 += run;
} while (run >= 64);
color ^= 1;
}
break;
}
if (a0 == width)
break;
if (a0 > width)
ErrorExit(("Too many pixels on line %d: %d\n", lineIndex, a0));
b1 = NextChangingElement(prefline, a0, width, color ^ 1);
b1 = NextChangingElement(prefline, b1, width, color);
}
CopyMemory(prefline, plinebuf, lineByteCnt);
ZeroMemory(plinebuf, lineByteCnt);
}
ReadCompressedBits();
if (codeValue != 0x00100100)
ErrorExit(("Missing EOB after the last scanline: 0x%x\n", codeValue));
while (bytecnt-- > 0) {
if (*pBuffer++)
ErrorExit(("Unused bits at the end\n"));
}
LocalFree((HLOCAL) prefline);
LocalFree((HLOCAL) plinebuf);
}
VOID
ReverseBitOrder(
PBYTE pBuffer,
LONG count
)
/*++
Routine Description:
Reverse the bit order of compressed bitmap data
Arguments:
pBuffer - Points to the compressed bitmap data buffer
count - Specifies the size of the buffer
Return Value:
NONE
--*/
{
static const BYTE BitReverseTable[256] = {
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
};
while (count-- > 0) {
*pBuffer = BitReverseTable[*pBuffer];
pBuffer++;
}
}
INT _cdecl
main(
INT argc,
CHAR **argv
)
{
CHAR *pInputFilename;
INT pageIndex;
HANDLE hFile;
DWORD nextIFDOffset, cb;
TIFFFILEHEADER tiffFileHeader;
if (argc != 2) {
printf("usage: %s filename\n", *argv);
exit(-1);
}
pInputFilename = argv[1];
//
// Open the input file
//
hFile = CreateFile(pInputFilename,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
ErrorExit(("Failed to open input file: %s\n", pInputFilename));
//
// Read the TIFF file header information
//
if (!ReadFile(hFile, &tiffFileHeader, sizeof(tiffFileHeader), &cb, NULL) ||
tiffFileHeader.magic1 != TIFF_MAGIC1 ||
tiffFileHeader.magic2 != TIFF_MAGIC2 ||
tiffFileHeader.signature != DRIVER_SIGNATURE)
{
ErrorExit(("Not an NT fax driver output file: %s\n", pInputFilename));
}
pageIndex = 1;
nextIFDOffset = tiffFileHeader.firstIFD;
do {
FAXIFD faxIFD;
LONG compressedBytes, compressedDataOffset;
LONG imageWidth, imageHeight;
PBYTE pBuffer;
//
// Read the IFD information
//
if (SetFilePointer(hFile,
nextIFDOffset - offsetof(FAXIFD, wIFDEntries),
NULL,
FILE_BEGIN) == 0xffffffff)
{
ErrorExit(("Couldn't locate the next IFD\n"));
}
if (! ReadFile(hFile, &faxIFD, sizeof(faxIFD), &cb, NULL))
ErrorExit(("Couldn't read IFD entries\n"));
if (faxIFD.wIFDEntries != NUM_IFD_ENTRIES ||
faxIFD.filler != DRIVER_SIGNATURE && faxIFD.filler != 0)
{
ErrorExit(("Not an NT fax driver output file\n"));
}
nextIFDOffset = faxIFD.nextIFDOffset;
compressedBytes = faxIFD.ifd[IFD_STRIPBYTECOUNTS].value;
compressedDataOffset = faxIFD.ifd[IFD_STRIPOFFSETS].value;
imageWidth = faxIFD.ifd[IFD_IMAGEWIDTH].value;
imageHeight = faxIFD.ifd[IFD_IMAGEHEIGHT].value;
printf("Page #%d:\n", pageIndex++);
printf(" width: %d\n", imageWidth);
printf(" height: %d\n", imageHeight);
printf(" compressed bytes: 0x%x\n", compressedBytes);
if ((pBuffer = (PBYTE) LocalAlloc(LPTR, compressedBytes)) == NULL ||
SetFilePointer(hFile, compressedDataOffset, NULL, FILE_BEGIN) == 0xffffffff ||
!ReadFile(hFile, pBuffer, compressedBytes, &cb, NULL))
{
ErrorExit(("Couldn't read compressed bitmap data\n"));
}
//
// If the fill order is LSB, reverse it to MSB
//
if (faxIFD.ifd[IFD_FILLORDER].value == FILLORDER_LSB)
ReverseBitOrder(pBuffer, compressedBytes);
//
// Decode compressed MMR data
//
DecodeMMR(pBuffer, compressedBytes, imageWidth, imageHeight);
LocalFree((HLOCAL) pBuffer);
} while (nextIFDOffset);
CloseHandle(hFile);
return 0;
}