/*
**---------------------------------------------------------------------
** tifftopbm.c
**---------------------------------------------------------------------
**
**      Copyright 1988 by Paul J. Emerson 
**                                                                     
**      Permission to use, copy, modify, and distribute this software 
**      and its documentation for any purpose and without fee is        
**      hereby granted, provided that the above copyright notice        
**      appear in all copies and that both that copyright notice and    
**      this permission notice appear in supporting documentation, and  
**      that the name of Paul J. Emerson not be used in advertising or 
**      publicity pertaining to distribution of the software without 
**      specific, written prior permission.  Paul J. Emerson makes no 
**      representations about the suitability of this software for 
**      any purpose.  It is provided "as is" without express or implied 
**      warranty.          
**
**      Version 1.4
**        Jef Poskanzer  (jef@helios.ee.lbl.gov)
**        April 1989
**
**        Comments
**          - Now uses new PBM error handling routine.
**
**	Version 1.3
**	  Lindsay F. Marshall (Lindsay.Marshall@newcastle.ac.uk)
**	  January 1989
**
**	  Comments
**	    - Removed include of malloc.h and added extern for malloc.
**
**      Version 1.2
**        Jef Poskanzer  (jef@rtsg.ee.lbl.gov)
**        January 1989
**
**        Comments
**          - Patch from Mike K. Peterson (mkp@hac2arpa.hac.com) to handle
**            MicroTek VersaScan variant.
**
**      Version 1.1
**        Jef Poskanzer  (jef@rtsg.ee.lbl.gov)
**        December 1988
**
**        Comments
**          - Changed name from tiff2pbm to tifftopbm.
**          - Changed to use libpbm routines.
**
**      Version 1.0
**        Paul J. Emerson  (ucf-cs!sdgsun!paul) 
**        December 1988
**
**        Comments
**          - TIFF Support:
**            - Single Bit Plane
**            - Black & White (No grey scale or color)
**            - Uncompressed TIFF 
**          - Supports PBM magic number
**	        - Usage: tifftopbm [-h] tifffile
**                   -h  Dump TIFF file header info to stderr
**                       This may be helpful in detecting unsuported aspects
**                       of the TIFF file format.
**
**      Notes:
**        Compile: cc -O tifftopbm.c -o tifftopbm
**
**---------------------------------------------------------------------
*/

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include "tiff.h"
#include "pbm.h"

#define TRUE  1
#define FALSE 0

main(argc,argv)
int argc;
char *argv[];
{
int fd,
    Dump=FALSE;
struct Tiffstruct Tiff;
struct TiffHeader Header;
char fname[255];
char *usage = "[-h] tifffile";

           pm_progname = argv[0];

	   if (argc >= 2) 
	      {
	      if (!strcmp("-h",argv[1]))
	         {
	         Dump = TRUE;
	         if (argc == 3)
	            strcpy(fname,argv[2]); 
	         else
	            {
	            printf(usage,argv[0]);
                exit(1); 
	            }
	         }
	      else
	         strcpy(fname,argv[1]); 

	      if ((fd = open(fname,O_RDONLY)) < 0 )
	         {
	         perror(fname);
		 exit(1); 
	         }
	      }
	   else
	      {
	      printf(usage,argv[0]);
	      exit(1); 
	      }

	if (readTiff(&Tiff,&Header,fd))
	   {
	   if (Dump)
	      dumpHeader(&Tiff,&Header);
	   PbmOut(fd,&Tiff);
	   }
	else
	   pm_error( "readTiff failure", 0,0,0,0,0 );
	close(fd);

}

/*
**------------------------------------------------------------------------
**  Send image to stdout in PBM format
**------------------------------------------------------------------------
*/
PbmOut(fd,t)
int fd;
struct Tiffstruct *t;
{
register int i, row;
char *bptr;
LONG *cntptr;
LONG *dataptr;
register bit **bits;

extern char *malloc();

	bits = pbm_allocarray(t->ImageWidth,t->ImageLength);
	
	dataptr = t->StripOffset;
	cntptr  = t->SBytesCntOffset;

	for (i=0, row=0; i < t->StOffsetCnt; i++, row += t->RowsStrip)
	    {

	    lseek(fd,(long) *dataptr,0);
	    bptr = malloc(*cntptr);
	    if ( bptr == 0 )
		pm_error( "out of memory", 0,0,0,0,0 );
	    if  (read(fd,bptr,*cntptr) != *cntptr)
	        {
	        perror("StripOffset read ");
		return;
	        }

	    PbmDoRow(bptr,*cntptr,bits,row,t->ImageWidth);

	    free(bptr);
	    cntptr++;
	    dataptr++;
	    }

	pbm_writepbm(stdout,bits,t->ImageWidth,t->ImageLength);
}
/*
**------------------------------------------------------------------------
** Output a row in PBM format 
**------------------------------------------------------------------------
*/
PbmDoRow(buffer,count,bits,row,cols)
char *buffer;
int count;
bit **bits;
int row, cols;
{
register int i, col;

	col = 0;
	while (count--)
	      {
	      for (i = 0 ; i < 8; i++)
	          {
	         bits[row][col++] =
		     ( ( *buffer << i ) & 0x80 ) ? PBM_BLACK : PBM_WHITE;
		 if ( col >= cols )
		     {
		     col = 0;
		     row++;
		     }
	         }
	      buffer++;
	      }
}

/*
**------------------------------------------------------------------------
** Read and decode a Tiff header. 
** Not all TIFF capabilities are supported. Only single plane bit maps. 
**------------------------------------------------------------------------
*/
int
readTiff(T,H,fd)
struct Tiffstruct *T;
struct TiffHeader *H;
int fd;
{
struct IDF idf;
struct IDF_Entry  *ptr; 
LONG TempRational[2];

#ifdef SYSV
	memset((char *)T,0,sizeof(struct Tiffstruct));
#else SYSV
	bzero((char *)T,sizeof(struct Tiffstruct));
#endif SYSV

	if (read(fd,H,sizeof(struct TiffHeader)) != sizeof(struct TiffHeader))
	   {
	   perror("Header "); 
       return(FALSE);
	   }

	lseek(fd,(long)H->IdfOffset,0);

	if (read(fd,&idf.NumEntries,sizeof(SHORT)) != sizeof(SHORT))
	   {
	   perror("Idf count "); 
       return(FALSE);
	   }

	if ((idf.idfptr = (struct IDF_Entry *)
                      calloc(idf.NumEntries,sizeof(struct IDF_Entry))) == NULL)
       {
	   perror("calloc ");
       return(FALSE);
	   }

    if (read(fd,idf.idfptr,idf.NumEntries*sizeof(struct IDF_Entry))
               != idf.NumEntries*sizeof(struct IDF_Entry))
       {
       perror("Idf count "); 
       return(FALSE);
       }

	ptr = idf.idfptr;

	while (idf.NumEntries--)
          {
	      switch (ptr->Tag)
                 {
	       case  0x00FF:
	             T->SubFileType = (ptr->ValueOffset >> 16);
	             break;
	                
	       case  0x0100:
	             T->ImageWidth = (ptr->ValueOffset >> 16);
	             break;

	       case  0x0101:
	             T->ImageLength = (ptr->ValueOffset >> 16);
	             break;

	       case  0x0102:
	             T->BitsPerSample = (ptr->ValueOffset >> 16);
	             break;

	       case  0x0103:
	             T->Compression = (ptr->ValueOffset >> 16);
	             break;

	       case  0x0106:
	             T->PhotoInterp = (ptr->ValueOffset >> 16);
	             break;

	       case  0x0107:
	             T->Threshold = (ptr->ValueOffset >> 16);
	             break;

	       case  0x0108:
	             T->CellWidth = (ptr->ValueOffset >> 16);
	             break;

	       case  0x0109:
	             T->CellLength = (ptr->ValueOffset >> 16);
	             break;

	       case  0x010A:
	             T->FillOrder = (ptr->ValueOffset >> 16);
	             break;

	       case  0x010D:
	             fprintf(stderr,"Unsupported feature\n");
	             /*
	             lseek(fd,(long)ptr->ValueOffset,0);
	             */
	             break;

	       case  0x010E:
	             fprintf(stderr,"Unsupported feature\n");
	             /*
	             lseek(fd,(long)ptr->ValueOffset,0);
	             */
	             break;

	       case  0x010F:
	             /*
	             lseek(fd,(long)ptr->ValueOffset,0);
	             */
	             fprintf(stderr,"Make: %s\n", T->Make); 
	             break;

	       case  0x0110:
	             fprintf(stderr,"Unsupported feature\n");
	             /*
	             lseek(fd,(long)ptr->ValueOffset,0);
	             */
	             break;

	       case  0x0111:
	             T->StOffsetCnt = ptr->Length;
	             if ((T->StripOffset = (LONG *) calloc(T->StOffsetCnt,
                                                 sizeof (LONG)))== NULL)
	                {
	                perror("calloc Soffset ");
	                return(FALSE); 
	                }
		     if(T->StOffsetCnt == 1)
		        *(T->StripOffset) = ptr->ValueOffset;
		     else
		        {
			lseek(fd, (long) ptr->ValueOffset, 0);
			read(fd, T->StripOffset, T->StOffsetCnt * sizeof(LONG));
			}
	             break;

	       case  0x0112:
	             T->Orientation= (ptr->ValueOffset >> 16);
	             break;

	       case  0x0115:
	             T->SamplesPixel= (ptr->ValueOffset >> 16);
	             break;

	       case  0x0116:
	             T->RowsStrip= (ptr->ValueOffset);
	             break;

	       case  0x0117:
	             T->StripByteCnt = (ptr->Length);
	             if ((T->SBytesCntOffset = (LONG *) calloc(T->StripByteCnt,
                                                 sizeof (LONG)))== NULL)
	                {
	                perror("calloc StripByteCnt ");
	                return(FALSE); 
	                }
		    if(T->StripByteCnt == 1)
			*(T->SBytesCntOffset) = ptr->ValueOffset;
		     else
			{
			lseek(fd,(long)ptr->ValueOffset,0);
			read(fd,T->SBytesCntOffset,T->StripByteCnt*sizeof(LONG));
			}
	             break;

	       case  0x0118:
	             T->MinSampleValue= (ptr->ValueOffset >> 16);
	             break;

	       case  0x0119:
	             T->MaxSampleValue= (ptr->ValueOffset >> 16);
	             break;

	       case  0x011A:
	             lseek(fd,(long)ptr->ValueOffset,0);
	             read(fd,TempRational,sizeof(TempRational));
	             T->Xres = TempRational[0]/TempRational[1];
	             break;

	       case  0x011B:
	             lseek(fd,(long)ptr->ValueOffset,0);
	             read(fd,TempRational,sizeof(TempRational));
	             T->Yres = TempRational[0]/TempRational[1];
	             break;

	       case  0x011C:
	             T->PlanarConfig= (ptr->ValueOffset >> 16);
	             break;

	       case  0x011D:
	             fprintf(stderr,"Unsupported feature: PageName\n");
	             /* T->PageName */
	             break;

	       case  0x011E:
	             fprintf(stderr,"Unsupported feature: Xpos\n");
	             /* T->XPos */ 
	             break;

	       case  0x011F:
	             fprintf(stderr,"Unsupported feature: Ypos\n");
	             /* T->YPos */
	             break;

	       case  0x0120:
	             fprintf(stderr,"Unsupported feature: FreeOffsets\n");
	             /* T->FreeOffsets */ 
	             break;

	       case  0x0121:
	             fprintf(stderr,"Unsupported feature: FreeByteCount \n");
	             /* T->FreeByteCount */ 
	             break;

	       case  0x0122:
	             fprintf(stderr,"Unsupported feature: GrayResUnit\n");
	             /* T->GrayResUnit */ 
	             break;

	       case  0x0123:
	             fprintf(stderr,"Unsupported feature: GrayResCurve\n");
	             /* T->GrayResCurve */ 
	             break;

	       case  0x0124:
	             fprintf(stderr,"Unsupported feature: Group3Option\n");
	             /* T->Group3Option */ 
	             break;

	       case  0x0125:
	             fprintf(stderr,"Unsupported feature: Group4Option\n");
	             /* T->Group4Option */ 
	             break;

	       case  0x0128:
	             fprintf(stderr,"Unsupported feature: ResolutionUnit\n");
 	             /* T->ResolutionUnit */
	             break;

	       case  0x0129:
	             fprintf(stderr,"Unsupported feature: PageNumber\n");
	             /* T->PageNumber */ 
	             break;

	       case  0x012C:
	             fprintf(stderr,"Unsupported feature: ColorResUnit\n");
	             /* T->ColorResUnit */ 
	             break;

	       case  0x012D:
	             fprintf(stderr,"Unsupported feature: ColorResCurv\n");
	             /* T->ColorResCurv */
	             break;

	       default:
	             fprintf(stderr,"Unsupported feature: Unknown Tag\n");
	             fprintf(stderr,"Default\n");
	             break;
	       }
	ptr++;
    }
	return(TRUE);
}
/* 
** Dump header information
*/
dumpHeader(T,H)
struct Tiffstruct *T;
struct TiffHeader *H;
{
int i;
LONG *offptr;

	   fprintf(stderr,"Version:        %d\n",H->Version);
	   fprintf(stderr,"ByteOrder:      %c%c\n",
	                   H->ByteOrder[0],
	                   H->ByteOrder[1]);
	   fprintf(stderr,"Subfile Type:   %d\n", T->SubFileType); 
	   fprintf(stderr,"ImageWidth:     %d\n", T->ImageWidth); 
	   fprintf(stderr,"ImageLength:    %d\n", T->ImageLength); 
	   fprintf(stderr,"BitsPerSample:  %d\n", T->BitsPerSample); 
	   fprintf(stderr,"Compression:    %d\n", T->Compression); 
	   fprintf(stderr,"PhotoInterp:    %d\n", T->PhotoInterp); 
	   fprintf(stderr,"Threshold:      %d\n", T->Threshold); 
	   fprintf(stderr,"CellWidth:      %d\n", T->CellWidth); 
	   fprintf(stderr,"CellLength:     %d\n", T->CellLength); 
	   fprintf(stderr,"FillOrder:      %d\n", T->FillOrder); 
	   fprintf(stderr,"DocName:        %s\n", T->DocName); 
	   fprintf(stderr,"ImageDescript:  %s\n", T->ImageDescript); 
	   fprintf(stderr,"Model:          %s\n", T->Model); 
	   fprintf(stderr,"StripOffsetCnt: %d\n", T->StOffsetCnt); 
	   offptr = T->StripOffset;
	   for(i=0; i < T->StOffsetCnt; i++)
	      {
	      fprintf(stderr,"Strip [%02d] starts at: %d \n", i,*offptr); 
	      offptr++; 
	      }
	   fprintf(stderr,"Orientation:    %d\n", T->Orientation); 
	   fprintf(stderr,"SamplesPixel:   %d\n", T->SamplesPixel); 
	   fprintf(stderr,"RowsStrip:      %d\n", T->RowsStrip); 
	   fprintf(stderr,"StripByteCnt:   %d\n", T->StripByteCnt); 
	   offptr = T->SBytesCntOffset;
	   for(i=0; i < T->StripByteCnt; i++)
	      {
	      fprintf(stderr,"Strip [%02d]: %d bytes\n", i,*offptr); 
	      offptr++; 
	      }
	   fprintf(stderr,"MinSampleValue: %d\n", T->MinSampleValue); 
	   fprintf(stderr,"MaxSampleValue: %d\n", T->MaxSampleValue); 
	   fprintf(stderr,"Xres:           %d\n", T->Xres); 
	   fprintf(stderr,"Yres:           %d\n", T->Yres); 
	   fprintf(stderr,"PlanarConfig:   %d\n", T->PlanarConfig); 
}
