"Complete PC FAX" to PBM conversion program, part 1 of 1

Steve Hayman sahayman at iuvax.cs.indiana.edu
Sat Dec 15 09:16:03 AEST 1990


This program converts incoming fax files produced by a PC hardware/software
product known as "The Complete FAX" to portable bitmap format.
See also "alt.fax".

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README File-Format Makefile cpctopbm.c cpctopbm.man
# Wrapped by sahayman at iuvax on Fri Dec 14 17:13:57 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(1799 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XThis is cpctopbm, a tool that will convert fax files as produced by the
XPC hardware/software product 'The Complete FAX' and convert them to the
XPBM Portable BitMap format.  You can use this  and the appropriate PBM
XToolkit program to convert Complete Fax files to PostScript, or display
Xthem in an X window, or whatever you like. 
X
XNote that the Complete PC people have three different file formats
X(see the "File-Formats" file) and this program only works with
Xformat 1, FAX files.  That's all I have to test.  It probably wouldn't
Xbe hard to make it work with theother formats.
X
X
XHere at IU we have a PC with a complete fax card and an ethernet board.
XWe FTP the various fax files (which are stored one page per file)
Xonto another machine, and then run them through cpctopbm and
Xother various filters.  Some possibilities are:
X
X    # convert to postscript and print
X    cpctopbm fax-file.001 | pgmtops -scale 0.33333 | lpr
X
X    # Using X Windows, view several pages of the same fax in succession, using
X    # the PD 'xloadimage' program, which can display a variety of
X    # formats, including PBM.
X
X
X    cpctopbm fax-file.001 >page1.pbm
X    cpctopbm fax-file.002 >page2.pbm 
X      .. etc etc ..
X    xloadimage -slideshow page1.pbm page2.pbm ...
X
X    'xview' is another program that can be used to display PBM files.
X
XUnfortunately none of this is particularly fast.  You may want to
Xplay around with scaling the PBM images to some other size so
Xthat they display faster. These bitmaps are 1728 pixels wide, which
Xis probably wider than your screen can display (although tools like
Xxloadimage and xview let you scroll around.)
XWe're still experimenting too.
X
X
XFeel free to pass bugs or suggestions along to me.
X
XSteve Hayman
XIndiana University
Xsahayman at cs.indiana.edu
XFri Dec 14 16:15:16 EST 1990
END_OF_FILE
if test 1799 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'File-Format' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'File-Format'\"
else
echo shar: Extracting \"'File-Format'\" \(3441 characters\)
sed "s/^X//" >'File-Format' <<'END_OF_FILE'
XSomeone in technical support at The Complete PC sent (faxed, naturally) me
Xthis description of the format.
X
X3 types of files.  
XType 1:	Fax file (Only kind I have ...sahayman)
XType 2:	Complete Hand Scanner File (pre-Release 2.0)
XType 3: 	Complete PC Scanner File (Release 2.1 and later)
X
XAll files consist of a header followed by one or more rows
Xof tokens, each  of which represents a scan line or line of pixels.
X
Xbyte	description
X0-3		Header.  Type 1:      80 01 40 02 (hex)
X		     Type 2 or 3: 80 01 40 00
X		    
X4-9		Hour, Minute, Second when fax was received. 2 bytes each.
X10-17	Month, Day, WeekDay, year
X
X18-77	Reserved - ignore when reading, make zeros when writing.
X
X78		Resolution.  00 if standard resolution (100x200),
X	    02 if fine resolution (200x200).  In a Type 2 or 3
X	    file this is set to 02.
X
X79		Picture Start.  The starting pixel column for the image
X	    for Type 1 and Type 2 files.  This is 00 for Type 3.
X
X80-99	Reserved.  Should be 0, except for Type 3 file where byte
X	    90 represents the image resolution and can have the
X	    values 1 (100 dpi), 2 (200), 3(300), 4(400),
X	    15 (150) or 75 (75).  Bytes 91-92 represent the number of
X	    bytes per scanline.  in a type 1 or 2 file each line is
X	    set at 1728 pixels; in a type 3 file, the value can vary 
X	    with the image so it is specified here.
X
X
XBody of File:
X
XAfter the header, one EOL (0x69) is followed by a repeating series
Xof {scaline, EOL} and the file ends with 4 EOLs.
XThe exception is when a scanline is 7F (a white line), this is
Xnot followed by an EOL.  In type 1 and 2 files, a 7F is
Xinterpreted identically to a 91, 1, EOL (decimal).  for Type 3
Xfiles, a 7F is interpreted as a full image width of white.
X
XEach scanline represents the data for one row of the image.  The
Xencoding is described below.  In a Type 1 or Type 2 file, each
Xscanline is fixed at 1728 pixels.  After conversion to a bitmap,
Xthat makes 1728 bits or 216 bytes per scanline.  In a type 3 file
Xthe (post-conversion-to-bitmap) scanline length in bytes is set in
Xthe header bytes 91-92.
X
XA token represents a run length of consecutive black or white dots.
XIt can also be used to store a 7-bit image.  A run length is
Xrepresented by 1 (if the run length is under 64) or 2 (if the
Xrun length is 64 or over) tokens.  If 2 tokens are used, the first 
Xis the highest multiple of 64 that is less than or equal
Xto the run length, and the second token is (run length mod 64).
XTokens represent alternating white and black run lengths;
Xeach scan line starts with a white run length.  Remember, in a 
XType 1 or Type 2 file, the run lengths in a scanline must add
Xto 1728.
X
XIn a Type 3 file, if the scanline is too long to represent with
X2 tokens, then the sequence {token, token, 01, token, token, EOL}
Xis used to represent an exceptionally long run of one colour. 
XThe 01 in the middle is used as a "placeholder" and represents
Xa 0-length run of the other colour.
X
XThe following tokens are defined (values listed are in decimal)
X
XValue	Run Length
X1	0
X2	1
X3	2
X4	3
X ...
X64	63
X65	64
X66	128
X67	192
X68	256
X69	320
X ...
X103	2496
X104	2560
X105	EOL
X125	Null Token
X127	Blank Scan Line (white line)
X
X
XA token value of 128 to 255 (high bit set) means that the remaining
Xseven bits represent a simple pixel pattern with no encoding.
XA bit set to 1 indicates a white pixel.
X
XA pixel token (high bit set) is always followed by a single byte token
Xof matching colour (matches the last bit.)
END_OF_FILE
if test 3441 -ne `wc -c <'File-Format'`; then
    echo shar: \"'File-Format'\" unpacked with wrong size!
fi
# end of 'File-Format'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(906 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
XCFLAGS        = -O
X
XDEST	      = /usr/local/bin
X
XEXTHDRS	      = /usr/include/stdio.h
X
XHDRS	      =
X
XLDFLAGS	      =
X
XLIBS	      =
X
XLINKER	      = cc
X
XMAKEFILE      = Makefile
X
XOBJS	      = cpctopbm.o
X
XPRINT	      = pr
X
XPROGRAM	      = cpctopbm
X
XSRCS	      = cpctopbm.c
X
Xall:		$(PROGRAM)
X
X$(PROGRAM):     $(OBJS) $(LIBS)
X		$(LINKER) $(LDFLAGS) $(OBJS) $(LIBS) -o $(PROGRAM)
X
Xclean:;		rm -f $(OBJS)
X
Xdepend:;	mkmf -f $(MAKEFILE) PROGRAM=$(PROGRAM) DEST=$(DEST)
X
Xindex:;		ctags -wx $(HDRS) $(SRCS)
X
Xinstall:	$(PROGRAM)
X		install -s $(PROGRAM) $(DEST)
X
Xprint:;		$(PRINT) $(HDRS) $(SRCS)
X
Xprogram:        $(PROGRAM)
X
Xtags:           $(HDRS) $(SRCS); ctags $(HDRS) $(SRCS)
X
Xupdate:		$(DEST)/$(PROGRAM)
X
X$(DEST)/$(PROGRAM): $(SRCS) $(LIBS) $(HDRS) $(EXTHDRS)
X		@make -f $(MAKEFILE) DEST=$(DEST) install
Xshar:
X	shar README File-Format Makefile cpctopbm.c cpctopbm.man >.shar
X
X###
Xcpctopbm.o: /usr/include/stdio.h
END_OF_FILE
if test 906 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'cpctopbm.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cpctopbm.c'\"
else
echo shar: Extracting \"'cpctopbm.c'\" \(7943 characters\)
sed "s/^X//" >'cpctopbm.c' <<'END_OF_FILE'
X#include <stdio.h>
X
X/*
X * cpctopbm
X * convert "Complete PC" messages to PBM format.
X * This has only been tested on Complete PC Fax files, though.
X * with some work it could also work with other Complete PC files
X * (e.g. formats 2 and 3, "Complete Hand Scanner" and "Complete PC Scanner"
X *  formats) but I don't have any of those to test out.
X *
X * Steve Hayman
X * sahayman at cs.indiana.edu
X * 90/10/10
X */
X
X/*
X * see the README file for a description of the header and
X * file format
X */
X
Xstruct cpc_header {
X    unsigned char magic[4];
X    short hour, min, sec;
X    short month, day, wday, year;
X
X    char reserved_1[ 77 - 18 + 1 ];
X    char resolution;
X    char picture_start;
X
X    char reserved_2[ 99 - 80 + 1];
X};
X    
Xstruct cpc_header header;
Xint debug = 0;
Xint verbose = 0;
Xchar *Pname;
Xint linecount = 0;
X
X#define FAXBLACK 0
X#define FAXWHITE 1
X
X#define FAXWIDTH 1728
X
Xint pbm_format = 4;
Xint fix_aspect = 1;
X
X
X
X#define EOL 0x69
X#define WHITELINE	0x7f
X
Xmain (argc, argv)
Xint argc;
Xchar **argv;
X{
X    int c;
X    extern int optind, opterr;
X    extern char *optarg;
X    int errflg = 0;
X
X    char *filename;
X    FILE *fax;
X
X    char tmpname[32];
X    FILE *tmp;
X
X
X    Pname = argv[0];
X
X    while ((c = getopt(argc, argv, "dvap:")) != EOF) {
X	switch (c) {
X	case 'a':
X	    fix_aspect = 0;
X	    break;
X	case 'd':
X	    debug++;
X	     break;
X	case 'p':
X	    pbm_format = atoi(optarg);
X	    break;
X	case 'v':
X	    verbose++;
X	    break;
X	default:
X	     errflg++;
X	}
X    }
X
X
X    if ( optind == argc ) {
X	fax = stdin;
X	filename = "stdin";
X    } else if ( optind == argc - 1 )  {
X
X	filename = argv[optind];
X	if ( (fax = fopen(filename, "r")) == NULL ) {
X	    fprintf(stderr, "%s: Can't open ");
X	    perror(filename);
X	    exit(1);
X	}
X    } else {
X	errflg++;
X    }
X
X    if (errflg) {
X	fprintf (stderr, "%s: Usage: %s [-p pbm-format] [-a] [-v] [-d] [cfax-file]\n", Pname, Pname);
X	exit(2);
X    }
X
X    init_runlength_table();
X
X    /*
X     * Read the header.
X     */
X
X    if ( fread((char *)&header, sizeof(header), 1, fax) != 1 ) {
X	fprintf(stderr, "%s: could not read header\n", Pname);
X	exit(1);
X    }
X
X    /*
X     * Magic number must be 80 01 40 02.
X     */
X    
X
X    if ( header.magic[0] != 0x80
X     ||  header.magic[1] != 0x01
X     ||  header.magic[2] != 0x40
X     ||  header.magic[3] != 0x02 ) {
X	
X	fprintf(stderr, "%s: file '%s' format not recognized\n", 
X		Pname, filename);
X	exit(1);
X    }
X
X    if ( verbose ) {
X	fprintf(stderr, "File: %s\nReceived: %d/%d/%d %d:%02d:%02d\n",
X		filename,
X		header.year, header.month, header.day,
X		header.hour, header.min, header.sec );
X	fprintf(stderr, "Resolution: %s\n",
X		header.resolution == 0 ? "100x200" : "200x200" );
X    }
X    /*
X     * Open scratch file.
X     * We'll rewind this later and schlep it to stdout.
X     */
X
X    strcpy(tmpname, "/tmp/pbm.XXXXXX");
X    mktemp(tmpname);
X
X    if ( (tmp = fopen(tmpname, "w+")) == NULL ) {
X	fprintf(stderr, "%s: can't create temp file ", Pname);
X	perror(tmpname);
X	exit(1);
X    }
X
X    /*
X     * Slimy manouevre to make sure tmp file goes away when
X     * program exits
X     */
X    unlink(tmpname);
X
X    /*
X     * Gobble one more EOL
X     */
X    if ( getc(fax) != EOL ) {
X	fprintf(stderr,"%s: file format error, 'EOL' after header missing\n");
X	exit(1);
X    }
X    /*
X     * Now read and write the rest.
X     */
X
X    cfaxtopbm(fax, tmp);
X
X    /*
X     * OK, rewind, fill in correct values of header,
X     * schlep it to stdout
X     */
X
X    fseek(tmp, 0L, 0);
X
X    /*
X     * Now write the header to stdout
X     * Compensate for the four bogus EOL's at the end of the file.
X     */
X    
X    /*
X     * Add a few useful comments to the pbm header, since they're
X     * in the fax header anyway.
X     */
X    
X    printf( "P%d\n", pbm_format);
X    printf( "# Converted from CPC-format fax file '%s'\n", filename);
X    printf( "# received: %d %d %d  %d:%02d:%02d\n",
X		header.year, header.month, header.day,
X		header.hour, header.min, header.sec );
X
X    linecount -= 4;
X    if ( header.resolution == 0 && fix_aspect ) {
X	linecount -= 4; /* we've been counting them twice */
X    }
X    printf("%d %d\n", FAXWIDTH, linecount);
X    /*
X     * Now copy the tmp file.
X     */
X
X    while ( (c = getc(tmp)) != EOF ) {
X	putchar(c);
X    }
X
X
X    if ( verbose ) {
X	fprintf(stderr, "%d lines\n", linecount);
X    }
X    exit(0);
X}
X
X/*
X * RunLength[i] is the length of a run of pixels encoded by the
X * byte value 'i'.
X */
X
Xint RunLength[128];
X
Xinit_runlength_table()
X{
X    int i;
X
X    for ( i = 1; i <= 64; i++ )
X	RunLength[i] = i - 1;
X    
X    for ( i = 65; i <= 104; i++ ) {
X	RunLength[i] = (i - 64) * 64;
X    }
X}
X	
X
Xint pixel = FAXWHITE;
X
Xcfaxtopbm(fax, out)
XFILE *fax;
XFILE *out;
X{
X
X    int c;
X    int rl;
X
X    int eol_count = 0;
X
X    /*
X     * pbm header
X     */
X
X    while ( (c = getc(fax)) != EOF ) {
X
X	/*
X	 * Five EOL's mark the end of the data even though
X	 * this might not be the end of the file.
X	 */
X
X	if ( c == EOL ) {
X	    if ( ++eol_count == 5 ) {
X		/*
X		 * Done.
X		 */
X		return;
X	    }
X	} else eol_count = 0;
X
X	switch ( c ) {
X	case EOL:
X	    EndOfLine(out);
X	    if ( debug ) fprintf(stderr, "End of line\n");
X	    
X	    break;
X	case WHITELINE:
X	    RunLenOut( FAXWIDTH, FAXWHITE );
X	    if ( debug ) fprintf(stderr, "White line\n");
X	    EndOfLine(out);
X	    break;
X	default:
X
X	    if ( c >= 128 && c <= 255 ) {
X		/*
X		 * byte encoded value.
X		 * 1-bits indicate 'white'
X		 */
X
X		if ( debug ) fprintf(stderr, "Byte encoded: 0x%x\n", c);
X		ByteEncoded(c);
X
X		/*
X		 * "A pixel token is always followed by a single-byte token
X		 *  of matching color (matches the last bit)"
X		 */
X		
X		pixel = c & 1;
X	    } else if ( c >= 1 && c <= 104 ) {
X		
X		rl = RunLength[c];
X		if ( c > 64 ) {
X		    rl += RunLength[ getc(fax) ];
X		}
X
X		if ( debug ) fprintf(stderr,"RunLen(%d, %d)\n", rl, pixel);
X		RunLenOut( rl, pixel );
X		pixel = 1 - pixel;
X	    }
X
X	    break;
X		
X	}
X    }
X	
X
X}
X
X/*
X * The idea here is that we gradually accumulate an entire scanline of bits,
X * and then actually write it out via EndOfLine()
X */
X
Xint byte = 0;
Xint bitnum = 7;
Xchar line[FAXWIDTH / 8];	/* array of bits */
X
XEndOfLine(out)
XFILE *out;
X{
X    int i;
X    pixel = FAXWHITE;
X    WriteLine(out);
X    ++linecount;
X
X    /*
X     * If we're fixing the aspect ratio and this image was
X     * originally 100x200 resolution, write the line twice
X     * so that the resulting PBM file is squarish.
X     */
X    if ( header.resolution == 0 && fix_aspect ) {
X	++linecount;
X	WriteLine(out);
X    }
X    if ( debug ) {
X	if ( linecount % 10 == 0 ) 
X	    fprintf(stderr, "%d\n", linecount);
X    }
X    byte = 0;
X    bitnum = 0;
X
X    for ( i = 0; i < sizeof(line); i++ )
X	line[i] = 0;
X
X}
X
X/*
X * Decode all the bits in a byte encoded value, and set some pixels
X */
XByteEncoded(c)
Xregister int c;
X{
X    register int bitpos;
X
X    for ( bitpos = 6; bitpos >= 0; bitpos-- ) {
X	Pixout( (c & (1 << bitpos)) >> bitpos );
X    }
X}
X
X
X
X/*
X * PBM type 1 files aren't supposed to have long lines so we
X * truncate them periodically.
X */
X#define MAXPBMWIDTH 60
X
X/*
X * write out the current scanline line[] in the appropriate format.
X */
XWriteLine(out)
XFILE *out;
X{
X    int byte;
X    int bit;
X    int linewidth = 0;
X    if ( pbm_format == 1 ) {
X
X	for ( byte = 0; byte < sizeof(line); byte++ ) {
X	    for ( bit = 7; bit >= 0; bit-- ) {
X		if ( line[byte] & ( 1<<bit) ) {
X		    putc( '1',out);
X		} else {
X		    putc('0',out);
X		}
X		putc(' ',out );
X		linewidth += 2;
X		if ( linewidth > MAXPBMWIDTH ) {
X		    putc('\n',out);
X		    linewidth = 0;
X		}
X	    }
X	}
X    } else {
X	
X	/*
X	 * just output the raw bits.
X	 */
X	fwrite( line, sizeof(line), 1, out);
X    }
X}
X
X/*
X * output the next pixel (actually just set the appropriate bit in the
X * line array
X */
XPixout( c ) 
Xregister int c;
X{
X
X    if ( c == FAXBLACK ) {
X	line[byte] |= ( 1<<bitnum );
X    }
X    if ( --bitnum < 0 ) {
X	bitnum = 7;
X	byte++;
X    }
X}
X
X
X
X/*
X * output a run length worth of pixels
X */
XRunLenOut( rl, p )
Xregister int rl;
Xregister int p;
X{
X    while ( rl-- ) 
X	Pixout(p);
X}
END_OF_FILE
if test 7943 -ne `wc -c <'cpctopbm.c'`; then
    echo shar: \"'cpctopbm.c'\" unpacked with wrong size!
fi
# end of 'cpctopbm.c'
fi
if test -f 'cpctopbm.man' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cpctopbm.man'\"
else
echo shar: Extracting \"'cpctopbm.man'\" \(1177 characters\)
sed "s/^X//" >'cpctopbm.man' <<'END_OF_FILE'
X.TH CPCTOPBM 1 IU
X.SH NAME
Xcpctopbm \- convert \fIThe Complete PC\fP files to portable bitmap
X.SH SYNOPSIS
Xcpctopbm [-p1] [-p4] [-d] [-v] [fax-file]
X.SH DESCRIPTION
XReads a file in
X.I "The Complete PC Image File Format"
X(such as is produced by 
X.IR The Complete Fax ,)
Xand writes out a portable bitmap as output.
XThe input format is the file storage format used by
Xthe
X.I "Complete Fax"
Xsoftware (not to be confused with true G3-fax format.)
X.SH OPTIONS
X.TP
X.B -p1
XProduce
X.I P1
Xstyle PBM output.
XUncompressed.  Large.
X.TP
X.B -p4
XProduce
X.I P4
Xstyle PBM output - compressed and compact.  The default.
X.TP
X.B -a
XDon't fix the aspect ratio.  Some CPC fax files are stored at
Xa resolution of 100x200; by default, cpctopbm tries to
Xcorrect for this and prints every bitmap output line twice.
XIf you don't want that correction and are willing to live
Xwith a squished looking PBM file, use \-a.
X.TP
X.B -v
XVerbose, give some chatty output.
X.TP
X.B -d
XDebug, give some different chatty output.
X.SH REFERENCES
XThere's a document called
X.I "The Complete PC Image File Format"
Xthat the software vendor faxed me once.
X.SH AUTHOR
XSteve Hayman,
XIndiana University
X.br
Xsahayman at cs.indiana.edu
END_OF_FILE
if test 1177 -ne `wc -c <'cpctopbm.man'`; then
    echo shar: \"'cpctopbm.man'\" unpacked with wrong size!
fi
# end of 'cpctopbm.man'
fi
echo shar: End of shell archive.
exit 0



More information about the Alt.sources mailing list