home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-05-23 | 55.1 KB | 2,367 lines |
- Newsgroups: comp.sources.misc
- From: tony@sdd.hp.com (Tony Parkhurst)
- Subject: v30i017: pclcomp - HP-PCL Compression Filter, Part01/02
- Message-ID: <csm-v30i017=pclcomp.131509@sparky.IMD.Sterling.COM>
- X-Md4-Signature: 691e3c8e4b6ae41b4add9c437cf0b0c1
- Date: Sat, 23 May 1992 18:15:58 GMT
- Approved: kent@sparky.imd.sterling.com
-
- Submitted-by: tony@sdd.hp.com (Tony Parkhurst)
- Posting-number: Volume 30, Issue 17
- Archive-name: pclcomp/part01
- Environment: LaserJet
- Supersedes: pclcomp: Volume 25, Issue 10-11
-
- Here is the newest version of pclcomp -- The HP PCL graphics compression
- filter. Many bugs are fixed.
-
- Tony Parkhurst
- -------
- #!/bin/sh
- # This is a shell archive (produced by shar 3.49)
- # To extract the files from this archive, save it to a file, remove
- # everything above the "!/bin/sh" line above, and type "sh file_name".
- #
- # made 05/23/1992 18:11 UTC by kent@sparky.IMD.Sterling.COM
- # Source directory /home/kent/mod/csm/queue/test
- #
- # existing files will NOT be overwritten unless -c is specified
- #
- # This is part 1 of a multipart archive
- # do not concatenate these parts, unpack them in order with /bin/sh
- #
- # This shar contains:
- # length mode name
- # ------ ---------- ------------------------------------------
- # 674 -rw-rw-r-- Makefile
- # 1461 -rw-rw-r-- README
- # 1431 -r--r--r-- getopt.c
- # 3847 -r--r--r-- pclcomp.1
- # 70635 -r--r--r-- pclcomp.c
- # 4053 -rw-rw-r-- pclcomp.man
- # 546 -rw-rw-r-- printer.note
- #
- if test -r _shar_seq_.tmp; then
- echo 'Must unpack archives in sequence!'
- echo Please unpack part `cat _shar_seq_.tmp` next
- exit 1
- fi
- # ============= Makefile ==============
- if test -f 'Makefile' -a X"$1" != X"-c"; then
- echo 'x - skipping Makefile (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting Makefile (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
- SHELL = /bin/sh
- BINDIR = /usr/local/bin
- S = 1
- M = /usr/local
- P = pclcomp
- CFLAGS =-O -Aa
- LDFLAGS =
- X
- AUX_SOURCE = README $P.man printer.note
- X
- SOURCE = $P.c getopt.c
- X
- OBJECTS = $P.o
- X
- MAKES = Makefile
- X
- $P : ${OBJECTS}
- X cc -o $P $(CFLAGS) $(OBJECTS) $(LDFLAGS)
- X
- ${BINDIR}/$P : $P
- X strip $P
- X -mv ${BINDIR}/$P ${BINDIR}/$P.old
- X cp $P ${BINDIR}
- X
- $M/man/man$S/$P.$S : $P.$S
- X /bin/rm -f $M/man/man$S/$P.$S
- X cp $P.$S $M/man/man$S/$P.$S
- X /bin/rm -f $M/man/cat*$S*/$P.$S
- X
- install : ${BINDIR}/$P $M/man/man$S/$P.$S
- X
- shar : $P.shar
- X
- $P.shar: $P.$S ${MAKES} ${HEADERS} ${SOURCE}
- X shar -ces ${AUX_SOURCE} $P.$S ${MAKES} ${HEADERS} ${SOURCE} > $P.shar
- X
- clean :
- X /bin/rm -f ${OBJECTS} $P core
- SHAR_EOF
- chmod 0664 Makefile ||
- echo 'restore of Makefile failed'
- Wc_c="`wc -c < 'Makefile'`"
- test 674 -eq "$Wc_c" ||
- echo 'Makefile: original size 674, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= README ==============
- if test -f 'README' -a X"$1" != X"-c"; then
- echo 'x - skipping README (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting README (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'README' &&
- X
- X
- X This is pclcomp -- An HP-PCL compression filter. It reads in
- PCL graphics files, and outputs compressed PCL files which may be sent
- directly to printers that support the compressions. A partial list of
- printer support is included.
- X
- X Why use pclcomp?
- X
- X 1) PCL files are much smaller ( up to 90% ).
- X
- X 2) Graphics printing on a LaserJet (IIP or III) is faster.
- X
- X
- X If you have a LaserJet II that does not support compression, you can
- still compress the files for storage, and decompress them while printing.
- X
- X I wrote this program for testing. This is NOT an HP product. It will
- NOT be supported by HP, but rather myself, in my spare time, if need be.
- X
- X If you need real support for driver development, then call Hewlett-
- Packard directly, preferably the ISV support group at the Boise Division.
- X
- X You may use parts of this code within your drivers to support compression
- if you wish.
- X
- X I did what I think is a reasonable job to make the program work for
- most possible PCL files. Please feel free to send comments, complaints
- or suggestions to me at tony@sdd.hp.com. If you have a file that does
- not survive the filter intact, please e-mail me the file and describe the
- problem.
- X
- X This filter runs under UNIX and MS-DOS and hopefully anything else that
- supports ANSI-C. To compile under HP-UX:
- X
- cc -Aa -O pclcomp.c -o pclcomp
- X
- X
- X Please direct all compliments and praise to: tony@sdd.hp.com
- X
- X
- X -- Tony Parkhurst
- SHAR_EOF
- chmod 0664 README ||
- echo 'restore of README failed'
- Wc_c="`wc -c < 'README'`"
- test 1461 -eq "$Wc_c" ||
- echo 'README: original size 1461, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= getopt.c ==============
- if test -f 'getopt.c' -a X"$1" != X"-c"; then
- echo 'x - skipping getopt.c (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting getopt.c (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'getopt.c' &&
- #include <stdio.h>
- X
- #define index strchr
- X
- /*
- X * get option letter from argument vector
- X */
- int opterr = 1, /* useless, never set or used */
- X optind = 1, /* index into parent argv vector */
- X optopt; /* character checked for validity */
- char *optarg; /* argument associated with option */
- X
- #define BADCH (int)'?'
- #define EMSG ""
- #define tell(s) fputs(*nargv,stderr);fputs(s,stderr); \
- X fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);
- X
- getopt(nargc,nargv,ostr)
- int nargc;
- char **nargv,
- X *ostr;
- {
- X static char *place = EMSG; /* option letter processing */
- X register char *oli; /* option letter list index */
- X char *index();
- X
- X if(!*place) { /* update scanning pointer */
- X if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) return(EOF);
- X if (*place == '-') { /* found "--" */
- X ++optind;
- X return(EOF);
- X }
- X } /* option letter okay? */
- X if ((optopt = (int)*place++) == (int)':' || !(oli = index(ostr,optopt))) {
- X if(!*place) ++optind;
- X tell(": illegal option -- ");
- X }
- X if (*++oli != ':') { /* don't need argument */
- X optarg = NULL;
- X if (!*place) ++optind;
- X }
- X else { /* need an argument */
- X if (*place) optarg = place; /* no white space */
- X else if (nargc <= ++optind) { /* no arg */
- X place = EMSG;
- X tell(": option requires an argument -- ");
- X }
- X else optarg = nargv[optind]; /* white space */
- X place = EMSG;
- X ++optind;
- X }
- X return(optopt); /* dump back option letter */
- }
- X
- X
- X
- SHAR_EOF
- chmod 0444 getopt.c ||
- echo 'restore of getopt.c failed'
- Wc_c="`wc -c < 'getopt.c'`"
- test 1431 -eq "$Wc_c" ||
- echo 'getopt.c: original size 1431, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= pclcomp.1 ==============
- if test -f 'pclcomp.1' -a X"$1" != X"-c"; then
- echo 'x - skipping pclcomp.1 (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting pclcomp.1 (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'pclcomp.1' &&
- .TH PCLCOMP 1
- .SH NAME
- pclcomp \- Compress PCL graphics files.
- .SH SYNOPSIS
- .B pclcomp
- [
- .B "-0123drsvxz"
- ]
- [
- .B "-n"
- .I num
- ]
- [
- .I infile
- [
- .I outfile
- ]]
- .br
- .SH DESCRIPTION
- .PP
- .B Pclcomp
- compresses (or decompresses) HP-PCL (Printer Control Language) graphics data.
- The supported compression modes are 0 (uncompressed), 1, 2 and 3.
- .B Pclcomp
- will read files using any of the modes 0 through 3, and will output using the
- modes which will give the best compression. This compressed version of
- the file may be sent directly to a PCL compatible printer, thus reducing
- I/O bandwidth. Pictures may also be saved in compressed form, reducing
- disk usage.
- In addition, PCL "imaging" files for the PaintJet XL are also supported.
- .PP
- The options to
- .B pclcomp
- control the compression modes. By default,
- .B pclcomp
- will use modes 0, 2 and 3, but the user may restrict which output
- modes it uses by specifying them on the command line with the
- .B -0,
- .B -1,
- .B -2
- and
- .B -3
- options. To decompress a file, simply specify
- .B -0
- as the only mode to use for output. Mode 0 (
- .B -0
- ) should always be allowed since modes 1, 2 and 3 cannot be guaranteed to
- be better than mode 0 for all types of pictures.
- .PP
- The
- .B -z
- option disables the zero "strip" feature. Since most printers do
- zero "filling",
- .B pclcomp,
- by default, "strips" the trailing zeros of each row (or plane) of data.
- Some printers or programs may require that zero "stripping" be disabled.
- .PP
- By default,
- .B pclcomp
- expects the input raster width to be 2400 pixels (8" at 300 dpi), and if it is
- different (e.g. PaintJet), then the raster width should be specified by
- the Source Raster Width escape sequence
- .I (<esc>*r#S).
- However, many applications do not set the width and assume a default, therefore,
- the user may use the
- .B -n
- option to
- .B pclcomp
- to specify a new default raster width. For PaintJet (8" at 180 dpi), the
- number should be 1440. If the PCL file contains the Source Raster Width
- escape sequence, it will override this default. If
- .B pclcomp
- thinks that more data is coming in than the specified width, it will
- generate a warning, and continue processing (and perhaps truncating) data.
- .PP
- The
- .B -x
- option will cause
- .B pclcomp
- to remove any horizontal offset sequences from the data. Only use this
- option if white is defined to be zero (as with LaserJets). This will
- shrink the data more if modes 2 or 3 are used.
- .PP
- The
- .B -r
- option causes
- .B pclcomp
- to append a
- reset sequence
- .I "(<esc>E)"
- to the end of the job.
- .PP
- Use the
- .B "-d"
- option to
- .B pclcomp
- if the output is to be sent to a DeskJet printer.
- .PP
- Some applications erroneously send
- .I "<esc>*rB"
- and
- .I "<esc>*rA"
- sequences between every row of graphics data. The
- .B -s
- option to
- .B pclcomp
- will "strip" all
- .I "<esc>*rB"
- sequences, and all
- .I "<esc>*rA"
- sequences after the first occurrence of this sequence.
- In addition, text and control characters residing between
- .I "<esc>*rA"
- and
- .I "<esc>*rB"
- sequences will be discarded.
- While this will work
- well
- for many jobs, it may have problems on multi-page or complex jobs.
- .PP
- The
- .B -v
- option simply gives statistics to
- .I stderr
- about which compression modes were used.
- .SH EXAMPLES
- .nf
- To compress a PCL file for LaserJet III, use:
- X pclcomp infile outfile
- X
- To compress a PCL file for the PaintJet (A size page at 180 dpi), use:
- X pclcomp -01 -n 1440 infile outfile
- X
- To compress a PCL file for DeskJet, use:
- X pclcomp -d012 infile outfile
- X
- To fully decompress a PCL file, use:
- X pclcomp -0z < infile > outfile
- .fi
- .SH WARNINGS
- .PP
- The
- .B -z
- option can cause the output to be larger than the input.
- .PP
- The
- .B -s
- option is useful, but it can cause erroneous output.
- .PP
- The
- .B -x
- option can cause black areas on the left side of the picture on color
- printers.
- .SH AUTHOR
- Tony Parkhurst, Hewlett-Packard, San Diego Division (tony@sdd.hp.com)
- SHAR_EOF
- chmod 0444 pclcomp.1 ||
- echo 'restore of pclcomp.1 failed'
- Wc_c="`wc -c < 'pclcomp.1'`"
- test 3847 -eq "$Wc_c" ||
- echo 'pclcomp.1: original size 3847, current size' "$Wc_c"
- rm -f _shar_wnt_.tmp
- fi
- # ============= pclcomp.c ==============
- if test -f 'pclcomp.c' -a X"$1" != X"-c"; then
- echo 'x - skipping pclcomp.c (File already exists)'
- rm -f _shar_wnt_.tmp
- else
- > _shar_wnt_.tmp
- echo 'x - extracting pclcomp.c (Text)'
- sed 's/^X//' << 'SHAR_EOF' > 'pclcomp.c' &&
- /*
- ** Pclcomp -- PCL compression filter.
- **
- ** If you have any problems or errors to report, please send them to me:
- **
- ** Tony Parkhurst
- **
- ** Email address: tony@sdd.hp.com -or- hp-sdd!tony
- **
- ** Please send a copy of the file that is causing the problem, and the
- ** version of pclcomp you are using.
- **
- ** All suggestions and requests are welcome.
- */
- X
- /*
- X ***************************************************************************
- X *
- X * $Source: /disc/44/cgtriton/tony/filters/pclcomp/RCS/pclcomp.c,v $
- X * $Date: 92/04/13 11:55:36 $
- X * $Revision: 1.48 $
- X *
- X * Description: Compresses pcl graphics files.
- X *
- X * Author: Tony Parkhurst
- X * Created: 890427
- X * Language: C
- X *
- X * (c) Copyright 1989, Hewlett-Packard Company, all rights reserved.
- X *
- X ***************************************************************************
- X */
- X
- X
- /*
- X ***************************************************************************
- X *
- X * $Log: pclcomp.c,v $
- X * Revision 1.48 92/04/13 11:55:36 11:55:36 tony (Tony Parkhurst)
- X * Fixed problem with reseting rasterwidth when <esc>*r#U follows a <esc>*v6W.
- X *
- X * Revision 1.47 92/04/06 16:07:54 16:07:54 tony (Tony Parkhurst)
- X * Removed special case to force first row to be other than mode 3.
- X *
- X * Revision 1.46 92/03/11 15:50:06 15:50:06 tony (Tony Parkhurst)
- X * Fixed a bug with Uncompress_3() when input goes past expect width.
- X * (This was mainly a problem if the offsets went over the end.)
- X *
- X * Also, fixed some semantic errors (null statements) that lint caught.
- X *
- X * Revision 1.45 92/02/13 14:15:54 14:15:54 tony (Tony Parkhurst)
- X * Replaced mode 1 compress routine.
- X *
- X * Revision 1.44 92/02/13 11:14:08 11:14:08 tony (Tony Parkhurst)
- X * Replaced mode 3 compression routine (cleaner version)
- X * Changed some function names for clarity.
- X * Added comments for worst case conditions.
- X *
- X * Revision 1.43 92/02/10 13:47:30 13:47:30 tony (Tony Parkhurst)
- X * Added AppleTalk sequence to pass thru. (plus fixed dither sequence).
- X *
- X * Revision 1.42 92/02/10 13:38:16 13:38:16 tony (Tony Parkhurst)
- X * Changed download dither matrix sequence to <esc>*m#W.
- X *
- X * Revision 1.41 92/01/14 13:54:43 13:54:43 tony (Tony Parkhurst)
- X * Changed some comments. Added compression ratio to verbose mode.
- X *
- X * Revision 1.40 92/01/14 13:20:32 13:20:32 tony (Tony Parkhurst)
- X * Added code to deal with implicit end of graphics
- X * (which zeros the seed rows).
- X *
- X * Revision 1.39 91/09/13 13:56:28 13:56:28 tony (Tony Parkhurst)
- X * Added code to disable zerostrip in mode 2.
- X *
- X * Revision 1.38 91/09/10 15:47:28 15:47:28 tony (Tony Parkhurst)
- X * Added include file for isdigit()
- X *
- X * Revision 1.37 91/09/10 15:08:23 15:08:23 tony (Tony Parkhurst)
- X * Clamped horizontal offsets to raster widths.
- X *
- X * Revision 1.36 91/09/10 15:04:15 15:04:15 tony (Tony Parkhurst)
- X * Re-vamped fraction parsing.
- X *
- X * Revision 1.35 91/09/10 14:03:40 14:03:40 tony (Tony Parkhurst)
- X * Fixed potential problem with fractions.
- X * Removed obsolete invert flag.
- X * Cleaned up some comments.
- X *
- X * Revision 1.34 91/09/10 13:21:48 13:21:48 tony (Tony Parkhurst)
- X * Fixed problems with data gaps (0W instead of 0V0V0W)
- X * Fixed problems with horizontal offsets and mode 3 compression.
- X * Added option to strip horizontal offsets (zero value must be white).
- X *
- X * Revision 1.33 91/07/18 15:18:43 15:18:43 tony (Tony Parkhurst)
- X * Replaced mode 2 compression routine. Works better now.
- X *
- X * Revision 1.32 91/07/08 11:27:24 11:27:24 tony (Tony Parkhurst)
- X * Enhanced the strip algorithm for merged graphics.
- X * (Also cleaned up some comments, couple of statements.)
- X *
- X * Revision 1.31 91/05/30 15:18:51 15:18:51 tony (Tony Parkhurst)
- X * Oops, fixed it right this time.
- X *
- X * Revision 1.30 91/05/30 15:06:20 15:06:20 tony (Tony Parkhurst)
- X * Added fix for negative value for <esc>*r#U.
- X *
- X * Revision 1.29 91/05/03 10:12:30 10:12:30 tony (Tony Parkhurst)
- X * Small changes.
- X *
- X * Revision 1.28 91/04/30 09:41:24 09:41:24 tony (Tony Parkhurst)
- X * Now puts stdin and stdout in binary mode for MSDOS.
- X * Changes courtesy of Mike Slomin.
- X * Changed usage message a bit.
- X *
- X * Revision 1.27 91/04/23 15:48:05 15:48:05 tony (Tony Parkhurst)
- X * Added handling of plus_sign in value fields.
- X *
- X * Revision 1.26 91/04/23 09:47:11 09:47:11 tony (Tony Parkhurst)
- X * Pass thru unknown modes.
- X *
- X * Revision 1.25 91/04/18 11:09:27 11:09:27 tony (Tony Parkhurst)
- X * Added parse for fractions in values (i.e. <esc>(s16.67H)
- X *
- X * Revision 1.24 91/04/10 14:16:30 14:16:30 tony (Tony Parkhurst)
- X * strips text and control codes between <esc>*rA and <esc>*rB w/ -s option
- X *
- X * Revision 1.23 91/04/05 14:53:25 14:53:25 tony (Tony Parkhurst)
- X * Added fixed for deskjet
- X * Also added a stripping feature.
- X *
- X * Revision 1.22 91/04/05 08:48:53 08:48:53 tony (Tony Parkhurst)
- X * Added some error checkin on output for MS-DOS users.
- X *
- X * Revision 1.21 91/04/04 12:53:32 12:53:32 tony (Tony Parkhurst)
- X * Replaced parser.
- X * Now handles combined escape sequences.
- X * Now handles downloads.
- X * Now combines mode changes with data.
- X *
- X * Revision 1.20 91/04/04 08:02:12 08:02:12 tony (Tony Parkhurst)
- X * Removed some test code.
- X *
- X * Revision 1.19 91/03/25 14:38:48 14:38:48 tony (Tony Parkhurst)
- X * Changed defaults.
- X *
- X * Revision 1.18 91/03/25 14:31:22 14:31:22 tony (Tony Parkhurst)
- X * Re-worked memory allocation stuff for funky input files.
- X *
- X * Revision 1.17 91/03/25 13:50:19 13:50:19 tony (Tony Parkhurst)
- X * Use command line args for file w/o -i or -o.
- X *
- X * Revision 1.16 91/03/04 14:23:15 14:23:15 tony (Tony Parkhurst)
- X * Fixed to allow ONLY mode 3 if the user really wants it.
- X *
- X * Revision 1.15 91/03/04 14:08:23 14:08:23 tony (Tony Parkhurst)
- X * Added an exit(0) at the end of main.
- X * fixed up some zerostrip stuff.
- X * made mode 3 the highest priority mode.
- X *
- X * Revision 1.14 91/02/20 13:57:27 13:57:27 tony (Tony Parkhurst)
- X * Changed priority a bit.
- X * Added some zerostripping for mode 2.
- X *
- X * Revision 1.13 91/02/06 15:31:00 15:31:00 tony (Tony Parkhurst)
- X * oops.
- X *
- X * Revision 1.12 91/02/06 14:41:28 14:41:28 tony (Tony Parkhurst)
- X * fixed usage message
- X *
- X * Revision 1.11 91/02/06 14:38:10 14:38:10 tony (Tony Parkhurst)
- X * Added file input and output for MS-DOS.
- X *
- X * Revision 1.10 91/02/05 17:49:23 17:49:23 tony (Tony Parkhurst)
- X * Fixed problem with zero stripped input.
- X *
- X * Revision 1.9 91/02/05 16:11:39 16:11:39 tony (Tony Parkhurst)
- X * Removed delay code and bitfield stuff.
- X *
- X * Revision 1.8 91/02/05 11:04:53 11:04:53 tony (Tony Parkhurst)
- X * Added io delay stuff for testing.
- X *
- X * Revision 1.7 91/02/05 10:28:32 10:28:32 tony (Tony Parkhurst)
- X * Fix for someone specifing ONLY mode 3.
- X *
- X * Revision 1.6 91/01/29 14:13:09 14:13:09 tony (Tony Parkhurst)
- X * Updated some comments.
- X *
- X * Revision 1.5 91/01/29 13:26:24 13:26:24 tony (Tony Parkhurst)
- X * Cleaned up, revamped a bit.
- X *
- X * Revision 1.4 89/11/09 15:59:16 15:59:16 tony (Tony Parkhurst)
- X * Fix for esc * r U coming after esc * r A.
- X *
- X * Revision 1.3 89/10/24 11:31:12 11:31:12 tony (Tony Parkhurst)
- X * Added parsing of <esc>*rC
- X *
- X * Revision 1.2 89/10/13 09:56:46 09:56:46 tony (Tony Parkhurst)
- X * Completely revamped by Greg G.
- X *
- X * Revision 1.1 89/06/15 13:57:46 13:57:46 tony (Tony Parkhurst)
- X * Initial revision
- X *
- X *
- X ***************************************************************************
- X */
- X
- static const char copyr[]=
- X "Copyright (c) 1991, Hewlett-Packard Company, all rights reserved.";
- X
- static const char author[]="Tony Parkhurst";
- X
- static const char rcs_id[]="$Header: pclcomp.c,v 1.48 92/04/13 11:55:36 tony Exp $";
- X
- static const char rev_id[]="$Revision: 1.48 $";
- X
- X
- /*
- X * Input and output formats are HP pcl.
- X *
- X * Imaging files ("Configure Image Data") are supported.
- X *
- X * Pclcomp does not take advantage of Y-Offset for blank areas.
- X * This is because Y-Offset creates white areas, but we don't do enough
- X * parsing to determine what value "white" has. An application that
- X * can assume white values could make use of this sequence.
- X *
- X * Pclcomp does not do any of the block compression modes (4-8).
- X *
- X * An additional enhancement would be to compare all the planes in a
- X * multi-plane file (color) and if nothing changed, using mode 3, just
- X * output a single <esc>*b0W.
- X *
- X *
- X * Usage: pclcomp [-v] [-0] [-1] [-2] [-3] [-z] [-n###] < infile > outfile
- X *
- X * Pclcomp will do graphics compression based on compression modes 0, 1, 2
- X * and 3. (Mode 0 is uncompressed). Pclcomp will accept all modes, and
- X * will attempt to optimize by selecting the best output mode for each
- X * row (or plane) of data. By default, pclcomp will use all 4 modes, but
- X * the user may restrict which output modes to use with the -0123 options.
- X * For example, to use pclcomp for output to a PaintJet which only knows
- X * modes 0 and 1 (the XL also understands modes 2 and 3), one would use:
- X *
- X * pclcomp -01 < infile > outfile
- X *
- X * Note: Mode 0 should always be allowed. None of the other modes is
- X * guaranteed to be better than mode 0 in all cases.
- X *
- X * The 'v' option tells the number of rows (planes) of data input and output
- X * in the different modes (to stderr).
- X *
- X * By default, pclcomp does zero "stripping" which is useful for PaintJet
- X * files using only modes 0 and 1, the PaintJet (and other PCL printers)
- X * will do zero "filling".
- X * NOTE: Use the 'z' option to disable zero stripping.
- X *
- X * The 'n' option is to change the default number of pixels in a picture.
- X * The proper way to set the pixel width is with the source raster width
- X * sequence <esc*r#S>, but soo many applications just assume the default,
- X * which is different on different printers, so I am providing this
- X * command line option to set a new default. One could also change the
- X * DEFAULT constant below (make sure it is a multiple of 8).
- X * Currently it is set to 8" at 300 dpi (2400).
- X */
- X
- #include <stdio.h>
- #include <string.h>
- #include <ctype.h>
- X
- #ifdef MSDOS
- #include <fcntl.h>
- #endif
- X
- X
- #define Get_Character() getchar()
- X
- #define MIN(x,y) ( ((x) < (y)) ? (x) : (y) )
- X
- #define TRUE 1
- #define FALSE 0
- X
- #define ESC 27
- X
- #define DEFAULT 2400 /* default width in pixels (multiple of 8) */
- X
- #define MAXMODES 4
- #define MAXPLANES 8
- #define MAXBYTES 60000 /* now mostly meaningless, just a big number */
- X
- unsigned char *seed_row[MAXPLANES];
- unsigned char *new_row;
- unsigned char *out_row[MAXMODES];
- unsigned int out_size[MAXMODES];
- X
- char memflag = FALSE; /* set when memory has been allocated */
- X
- X
- char mode0=FALSE,
- X mode1=FALSE,
- X mode2=FALSE,
- X mode3=FALSE;
- X
- unsigned char num_planes=1;
- unsigned char curr_plane=0;
- X
- char imaging = FALSE; /* not imaging, so no lockout */
- X
- char verbose = FALSE;
- X
- unsigned char inmode = 0; /* input compression mode */
- unsigned char outmode = 0; /* output compression mode */
- X
- unsigned int rasterwidth=DEFAULT/8; /* width of picture, in bytes */
- unsigned int rpix = DEFAULT; /* width of picture, in pixels */
- X
- unsigned char zerostrip= TRUE; /* strip trailing zeros */
- X
- unsigned int inuse[4]={0,0,0,0}, outuse[4] = {0,0,0,0};
- X
- char widthwarning = FALSE; /* for trucation warning */
- char firstrow = TRUE; /* to prevent mode 3 from being first */
- X
- X
- struct { /* this will hold the data for the */
- X unsigned char model; /* configuring of image processing */
- X unsigned char pix_mode;
- X unsigned char inx_bits;
- X unsigned char red;
- X unsigned char green;
- X unsigned char blue;
- X short wr;
- X short wg;
- X short wb;
- X short br;
- X short bg;
- X short bb;
- } imdata;
- X
- extern unsigned char *malloc();
- X
- char *filein = NULL, *fileout = NULL;
- X
- /*
- ** These variables are for the new parser.
- ** The new parser handles more sequences, and also deals with combined
- ** escape sequences better.
- */
- X
- int parameter;
- int group_char;
- int terminator;
- int old_terminator;
- int value;
- float fvalue; /* fractional value */
- int scanf_count;
- char in_sequence = FALSE;
- char pass_seq;
- char plus_sign; /* for relative values */
- X
- X
- /* dummy buffer */
- char buf[BUFSIZ];
- X
- /*
- ** If the printer is a DeskJet, then we must handle <esc>*rB differently
- ** Option '-d' will turn on this mode.
- */
- X
- char deskjet = FALSE;
- X
- X
- /*
- ** Many drivers it seems put <esc>*rB<esc>*rA between each and every row
- ** of data. This defeats compression mode 3 on a DeskJet, and also
- ** makes the PaintJet (not XL) quite slow. This next flag "-s" on the
- ** command line, will attempt to do a reasonable job of stripping
- ** out the excess commands.
- **
- ** The in_graphics flag will be used to strip unwanted control chars from
- ** the file. It will also be used to spot implicit end of graphics.
- */
- X
- char strip_seq = FALSE;
- char in_graphics = FALSE;
- X
- X
- /*
- ** Just for certain special cases, it would be nice to append an <esc>E reset
- ** to the end of the job. Specify with "-r".
- */
- X
- char reset_seq = FALSE;
- X
- X
- char *progname; /* to hold the program name for verbose */
- X
- /*
- ** Even though the horizontal offset command <esc>*b#X is obsolete, many
- ** drivers still use it, and it causes some interesting problems with
- ** mode 3 compression, so pclcomp needs to deal with it in some hopefully
- ** intelligent fashion, and they will get stripped if -x is used.
- */
- X
- int horiz_offset = 0;
- X
- char strip_offsets = FALSE;
- X
- X
- static float Get_Frac(); /* instead of scanf */
- X
- X
- X
- /*
- ******************************************************************************
- **
- ** Main program.
- **
- ******************************************************************************
- */
- X
- main(argc, argv)
- int argc;
- char *argv[];
- {
- X int c,j;
- X extern char *optarg;
- X extern int optind;
- X
- X progname = argv[0];
- X
- #ifdef MSDOS
- X setmode(fileno(stdin), O_BINARY); /* Place stdin and stdout in */
- X setmode(fileno(stdout), O_BINARY); /* binary mode. (Mike Slomin)*/
- #endif
- X
- X /* parse up the args here */
- X
- X while ((c = getopt(argc, argv, "0123drsvzn:i:o:sx")) != EOF )
- X switch(c){
- X case '0':
- X mode0 = TRUE;
- X break;
- X case '1':
- X mode1 = TRUE;
- X break;
- X case '2':
- X mode2 = TRUE;
- X break;
- X case '3':
- X mode3 = TRUE;
- X break;
- X case 'd':
- X deskjet = TRUE;
- X break;
- X case 'r':
- X reset_seq = TRUE;
- X break;
- X case 's':
- X strip_seq = TRUE;
- X break;
- X case 'v':
- X verbose = TRUE;
- X break;
- X case 'x':
- X strip_offsets = TRUE;
- X break;
- X case 'z':
- X zerostrip = FALSE;
- X break;
- X case 'n':
- X rpix = atoi(optarg); /* new default */
- X rasterwidth = (rpix + 7) / 8; /* round up */
- X break;
- X
- X case 'i':
- X filein = optarg;
- X break;
- X case 'o':
- X fileout = optarg;
- X break;
- X
- X case '?':
- X default:
- X fprintf(stderr,
- X "Usage: %s [-0123drsvxz] [-n###] [infile [outfile]]\n",
- X argv[0]);
- X exit(-1);
- X };
- X
- X if ( verbose )
- X {
- X fprintf(stderr, "%s: %s\n", argv[0], rev_id);
- X }
- X
- X
- X if ( ! ( mode0 || mode1 || mode2 || mode3) ) /* any modes on? */
- X mode0 = /* mode1 = */ mode2 = mode3 = TRUE; /* 3 modes by default */
- X
- X /*
- X ** Check to see if any file args were given on the command line.
- X ** Ones that were not preceded by a "-i" or "-o".
- X */
- X
- X if ( filein == NULL && optind < argc && argv[optind] != NULL )
- X filein = argv[optind++];
- X
- X if ( fileout == NULL && optind < argc && argv[optind] != NULL )
- X fileout = argv[optind++];
- X
- X /*
- X ** Now open files for stdin and stdout if provided by the user.
- X */
- X
- X if ( filein != NULL ) /* new input file */
- X
- X if ( freopen( filein, "rb", stdin ) == NULL )
- X {
- X fprintf(stderr,"Unable to open %s for input.\n",filein);
- X exit(-42);
- X }
- X
- X if ( fileout != NULL ) /* new output file */
- X
- X if ( freopen( fileout, "wb", stdout ) == NULL )
- X {
- X fprintf(stderr, "Unable to open %s for output.\n",
- X fileout);
- X exit(-43);
- X }
- X
- X
- X /*
- X **
- X ** This is the pcl input parsing loop.
- X **
- X */
- X
- X while( ( c = getchar() ) != EOF )
- X {
- X
- X /* Ignore all chars until an escape char */
- X
- X /*
- X ** If we are in graphics, toss it if strip_seq is set.
- X */
- X
- X if ( c != ESC )
- X {
- X /*
- X ** Simply pass thru the character if not stripping
- X ** or not in graphics.
- X */
- X
- X if ( !strip_seq || !in_graphics )
- X putchar(c); /* pass it thru */
- X
- X /*
- X ** If we are in graphics and we are not stripping,
- X ** then this character implies an end raster graphics,
- X ** and the seed rows need to be zeroed.
- X */
- X
- X if ( in_graphics && !strip_seq )
- X {
- X zero_seeds();
- X in_graphics = FALSE; /* fell out */
- X }
- X
- X continue; /* pop to the top of the loop */
- X }
- X
- X /*
- X ** Now we have an escape sequence, get the parameter char.
- X */
- X
- X parameter = getchar();
- X
- X if ( parameter == EOF ) /* oops */
- X {
- X putchar ( ESC );
- X fprintf(stderr, "Warning: File ended with <esc>.\n");
- X break; /* unexpected end of input */
- X }
- X
- X /*
- X ** Now check to see if it is a two character sequence.
- X */
- X
- X if ( parameter >= '0' && parameter <= '~' )
- X {
- X putchar ( ESC );
- X putchar ( parameter ); /* pass it thru */
- X
- X /*
- X ** If the second character is an E, then we
- X ** and the printer do a reset.
- X */
- X
- X if ( parameter == 'E' )
- X {
- X free_mem();
- X curr_plane = 0;
- X num_planes = 1;
- X imaging = FALSE;
- X inmode = 0;
- X outmode = 0;
- X in_graphics = FALSE;
- X
- X /* can't do this if user gave value with -n.
- X rasterwidth = DEFAULT/8;
- X rpix = DEFAULT;
- X */
- X }
- X
- X continue; /* return to the top */
- X }
- X
- X /*
- X ** Now check to make sure that the parameter character is
- X ** within range.
- X */
- X
- X if ( parameter < '!' || parameter > '/' )
- X {
- X putchar ( ESC );
- X putchar ( parameter );
- X
- X fprintf(stderr, "Warning: Invalid escape sequence.\n");
- X
- X continue;
- X }
- X
- X /*
- X ** We are only interested in certain parameters, so pass
- X ** the rest of the sequences.
- X */
- X
- X /*
- X ** For the moment, we are only interested in '*' (graphics)
- X ** '(' and ')' (downloads). Although we do not do anything
- X ** with downloads, we need to pass the binary data thru
- X ** untouched.
- X ** Oops, '&' is handled too.
- X */
- X
- X if ( parameter != '*' && parameter != '('
- X && parameter != ')' && parameter != '&' )
- X {
- X
- X /*
- X ** If the "stripper" is active, we need to suspend
- X ** it till graphics are re-started.
- X */
- X
- X if ( strip_seq && !in_graphics )
- X {
- X curr_plane = 0;
- X free_mem(); /* force re-start */
- X }
- X
- X /*
- X ** Pass thru the sequence intact.
- X */
- X
- X putchar ( ESC );
- X putchar ( parameter );
- X Flush_To_Term(); /* flush rest of seq. */
- X continue;
- X }
- X
- X
- X /*
- X ** Parameter character is in range, look for a valid group char
- X */
- X
- X group_char = getchar();
- X
- X if ( group_char == EOF ) /* oops, ran out of input */
- X {
- X putchar ( ESC );
- X putchar ( parameter );
- X
- X fprintf(stderr, "Warning: Incomplete escape sequence.\n");
- X break;
- X }
- X
- X /*
- X ** See if in proper range. If it isn't, it is not an error
- X ** because the group character is optional for some sequences.
- X ** For the moment, we are not interested in those sequences,
- X ** so pass them thru.
- X */
- X
- X if ( group_char < '`' || group_char > '~' )
- X {
- X
- X /*
- X ** If the "stripper" is active, we need to suspend
- X ** it till graphics are re-started.
- X */
- X
- X if ( strip_seq && !in_graphics )
- X {
- X curr_plane = 0;
- X free_mem(); /* force re-start */
- X }
- X
- X /*
- X ** Pass thru the sequence intact.
- X */
- X
- X putchar ( ESC );
- X putchar ( parameter );
- X putchar ( group_char );
- X if ( group_char < '@' || group_char > '^' )
- X Flush_To_Term(); /* pass rest of seq. */
- X continue;
- X }
- X
- X /*
- X ** Now we have a valid group character, decide if we want
- X ** to deal with this escape sequence.
- X **
- X ** Sequences we want do deal with include:
- X **
- X ** <esc>*r ** graphics
- X ** <esc>*b ** graphics
- X ** <esc>*v ** graphics
- X **
- X ** Sequences we must pass thru binary data:
- X **
- X ** <esc>*c ** pattern
- X ** <esc>*m ** download dither
- X ** <esc>*t ** obsolete
- X ** <esc>(f ** download char set
- X ** <esc>(s ** download char
- X ** <esc>)s ** download font
- X ** <esc>&a ** logical page
- X ** <esc>&b ** AppleTalk stuff
- X ** <esc>&l ** obsolete
- X **
- X */
- X
- X if ( ( parameter == '*'
- X && group_char != 'r' && group_char != 'b'
- X && group_char != 'v' && group_char != 'c'
- X && group_char != 't' && group_char != 'm' )
- X || ( parameter == '&'
- X && group_char != 'a' && group_char != 'l'
- X && group_char != 'b' )
- X || ( parameter == '('
- X && group_char != 'f' && group_char != 's' )
- X || ( parameter == ')' && group_char != 's' ) )
- X {
- X /*
- X ** Definately not interested in the sequence.
- X */
- X
- X /*
- X ** If the "stripper" is active, we need to suspend
- X ** it till graphics are re-started.
- X */
- X
- X if ( strip_seq && !in_graphics )
- X {
- X curr_plane = 0;
- X free_mem(); /* force re-start */
- X }
- X
- X /*
- X ** Pass thru the sequence intact.
- X */
- X
- X putchar ( ESC );
- X putchar ( parameter );
- X putchar ( group_char );
- X Flush_To_Term();
- X continue;
- X }
- X
- X
- X /*
- X ** If the sequence is <esc>&a#H, it will have gotten past
- X ** the above, but we need to suspend the "stripper" if
- X ** it is active, because the CAP is getting moved.
- X **
- X ** The <esc>*p#X/Y sequences will have been filtered
- X ** thru just above (<esc>*p is not a needed group).
- X */
- X
- X if ( strip_seq && parameter != '*' && !in_graphics )
- X {
- X curr_plane = 0;
- X free_mem(); /* force re-start */
- X }
- X
- X
- X /*
- X ** Now set up a pass thru flag so we can ignore the entire
- X ** sequences of some of these.
- X */
- X
- X if ( parameter != '*' )
- X pass_seq = TRUE;
- X
- X else if ( group_char == 'c' || group_char == 't'
- X || group_char == 'm' )
- X
- X pass_seq = TRUE;
- X else
- X pass_seq = FALSE;
- X
- X
- X /*
- X ** Now we have a sequence that we are definately interested in.
- X **
- X ** Get the value field and terminator, and loop until final
- X ** terminator is found.
- X */
- X
- X do
- X {
- X /* first see if the value has a plus sign */
- X
- X scanf_count = scanf(" + %d", &value );
- X
- X if ( scanf_count == 1 )
- X
- X plus_sign = TRUE;
- X else
- X {
- X plus_sign = FALSE;
- X
- X scanf_count = scanf(" %d", &value );
- X
- X if ( scanf_count == 0 )
- X value = 0; /* by default */
- X }
- X
- X /*
- X ** I wonder if I will get bitten by a trailing
- X ** space character right here?
- X */
- X
- X terminator = getchar();
- X
- X /*
- X ** Check for a fractional component.
- X */
- X
- X fvalue = 0.0;
- X
- X if ( terminator == '.' )
- X {
- X fvalue = Get_Frac();
- X
- X /*
- X ** Now get real terminator.
- X */
- X
- X terminator = getchar();
- X }
- X
- X
- X if ( terminator == EOF ) /* barf */
- X {
- X fprintf(stderr,
- X "Warning: Incomplete sequence at EOF.\n");
- X break;
- X }
- X
- X /*
- X ** If the pass_seq flag is set, then just pass
- X ** it thru to stdout until a 'W' is found.
- X */
- X
- X if ( pass_seq )
- X {
- X /*
- X ** If not in sequence, then we output esc
- X ** otherwise, output the saved terminator.
- X */
- X
- X if ( !in_sequence )
- X {
- X in_sequence = TRUE;
- X putchar ( ESC );
- X putchar ( parameter );
- X putchar ( group_char );
- X } else
- X {
- X putchar ( old_terminator );
- X }
- X
- X /* now pass the value */
- X
- X if ( plus_sign )
- X putchar('+');
- X
- X /*
- X ** See if there was a non-zero fraction.
- X */
- X
- X if ( fvalue != 0.0 )
- X {
- X if ( value < 0 )
- X {
- X putchar('-');
- X value = -value;
- X }
- X
- X fvalue += value;
- X
- X printf("%g", fvalue);
- X
- X } else if ( scanf_count )
- X printf("%0d", value);
- X
- X /*
- X ** We save the terminator, because we may
- X ** need to change it to upper case.
- X */
- X
- X old_terminator = terminator;
- X
- X /* if binary data, pass it thru */
- X
- X if ( terminator == 'W' ) /* aha */
- X {
- X putchar ( terminator );
- X in_sequence = FALSE; /* terminates */
- X Flush_Bytes ( value ); /* pass data */
- X }
- X
- X continue;
- X }
- X
- X /*
- X ** Ok, this is a sequence we want to pay attention to.
- X **
- X ** Do_Graphics returns TRUE if we need to pass seq.
- X **
- X ** Note: Do_Graphics modifies the parser vars such
- X ** as in_sequence. This is because it may
- X ** have to output stuff directly.
- X */
- X
- X if ( Do_Graphics ( group_char, value, terminator ) )
- X {
- X /*
- X ** If not in sequence, then we output esc
- X ** otherwise, output the saved terminator.
- X */
- X
- X if ( !in_sequence )
- X {
- X in_sequence = TRUE;
- X putchar ( ESC );
- X putchar ( parameter );
- X putchar ( group_char );
- X } else
- X {
- X putchar ( old_terminator );
- X }
- X
- X /* now pass the value */
- X
- X if ( plus_sign )
- X putchar('+');
- X
- X /*
- X ** See if there was a non-zero fraction.
- X */
- X
- X if ( fvalue != 0.0 )
- X {
- X if ( value < 0 )
- X {
- X putchar('-');
- X value = -value;
- X }
- X
- X fvalue += value;
- X
- X printf("%g", fvalue);
- X
- X } else if ( scanf_count )
- X printf("%0d", value);
- X
- X /*
- X ** We save the terminator, because we may
- X ** need to change it to upper case.
- X */
- X
- X old_terminator = terminator;
- X }
- X
- X } while ( terminator >= '`' && terminator <= '~' );
- X
- X /*
- X ** The oppsite test (above) may be more appropriate. That is,
- X ** !(terminator >= '@' && terminator <= '^').
- X */
- X
- X /*
- X ** If we were in a sequence, then we must terminate it.
- X ** If it was lower case, then it must be uppered.
- X */
- X
- X if ( in_sequence )
- X {
- X putchar ( terminator & 0xdf ); /* a ==> A */
- X in_sequence = FALSE;
- X }
- X }
- X
- X
- X /*
- X ** If the user wants a reset, give him one.
- X */
- X
- X if ( reset_seq )
- X {
- X putchar ( ESC );
- X putchar ( 'E' );
- X }
- X
- X
- X /*
- X ** Finished up, so print stats and close output file.
- X */
- X
- X
- X
- X if ( verbose )
- X {
- X long inpos, outpos;
- X
- X for(j = 0; j < 4; j++)
- X fprintf(stderr,"Rows in mode %1d: %d\n", j, inuse[j]);
- X for(j = 0; j < 4; j++)
- X fprintf(stderr,"Rows out mode %1d: %d\n", j, outuse[j]);
- X
- X inpos = ftell(stdin);
- X outpos = ftell(stdout);
- X
- X /*
- X ** If the input or output is a pipe, then ftell returns a
- X ** -1. Don't bother telling the user about it.
- X */
- X
- X if ( inpos > 0 && outpos > 0 )
- X {
- X
- X fprintf(stderr, "Input size: %ld bytes\n", inpos );
- X fprintf(stderr, "Output size: %ld bytes\n", outpos );
- X
- X if ( inpos > outpos )
- X fprintf(stderr, "Compression: %ld%%\n",
- X (long)(99L - 100L*outpos/inpos));
- X else if ( outpos > inpos )
- X fprintf(stderr, "Expansion: %ld%%\n",
- X (long)(100L*outpos/inpos - 100L));
- X else
- X fprintf(stderr, "No compression.\n");
- X }
- X }
- X
- X fclose(stdout);
- X
- X exit(0);
- }
- X
- X
- /*
- ** Do_Graphics() takes the graphics escape sequence and performs the
- ** necessary functions.
- ** TRUE is returned if the escape sequence needs to be passed to the output.
- */
- X
- int Do_Graphics( group, num, terminator )
- int group, num, terminator;
- {
- X /* first look at vW */
- X
- X if ( group == 'v' )
- X
- X if ( terminator != 'W' )
- X
- X return ( TRUE ); /* pass it thru */
- X else
- X {
- X if ( !in_sequence )
- X {
- X putchar ( ESC );
- X putchar ( parameter );
- X putchar ( group );
- X } else
- X putchar ( old_terminator );
- X
- X in_sequence = FALSE; /* terminating */
- X
- X printf("%0d", num);
- X putchar ( terminator );
- X
- X free_mem(); /* reset memory */
- X
- X imaging++;
- X
- X fread(&imdata, MIN(num, 18), 1, stdin);
- X fwrite(&imdata, MIN(num, 18), 1, stdout);
- X
- X num -= MIN(num, 18);
- X
- X /* copy rest of unknown data */
- X
- X if ( num > 0 )
- X Flush_Bytes(num);
- X
- X
- X switch(imdata.pix_mode){
- X case 0x00:
- X rasterwidth = (rpix + 7)/8;
- X num_planes = imdata.inx_bits;
- X break;
- X case 0x01:
- X rasterwidth = rpix*imdata.inx_bits/8;
- X break;
- X case 0x02:
- X rasterwidth = (rpix + 7)/8;
- X num_planes =imdata.red + imdata.green +
- X imdata.blue;
- X break;
- X case 0x03:
- X rasterwidth = (imdata.red +
- X imdata.green +
- X imdata.blue)*rpix/8;
- X break;
- X }
- X
- X return ( FALSE );
- X }
- X
- X /*
- X ** Now deal with <esc>*r stuff
- X */
- X
- X if ( group == 'r' )
- X {
- X switch ( terminator )
- X {
- X case 'A':
- X case 'a':
- X
- X /* Enter graphics mode, enable stripping */
- X
- X in_graphics = TRUE;
- X
- X /* if user wants to strip redundant seq */
- X if ( strip_seq && memflag )
- X return( FALSE );
- X
- X curr_plane=0;
- X zero_seeds(); /* may allocate mem */
- X break;
- X
- X case 'C':
- X case 'c':
- X
- X /* Exit graphics, disable stripping */
- X
- X in_graphics = FALSE;
- X
- X if ( strip_seq )
- X return( FALSE );
- X
- X inmode = 0;
- X outmode = 0;
- X
- X free_mem();
- X curr_plane=0;
- X break;
- X
- X case 'B':
- X case 'b':
- X
- X /* Exit graphics, disable stripping */
- X
- X in_graphics = FALSE;
- X
- X if ( strip_seq )
- X return( FALSE );
- X
- X if ( deskjet ) /* B resets modes on DJ */
- X {
- X inmode = 0;
- X outmode = 0;
- X }
- X free_mem();
- X curr_plane=0;
- X break;
- X
- X case 'S':
- X case 's':
- X
- X /* free mem in case widths changed */
- X free_mem();
- X
- X rpix = num;
- X
- X if (imaging){
- X switch(imdata.pix_mode)
- X {
- X case 0x00:
- X rasterwidth=(rpix+7)/8;
- X break;
- X case 0x01:
- X rasterwidth =
- X rpix*imdata.inx_bits/8;
- X break;
- X case 0x02:
- X rasterwidth=(rpix+7)/8;
- X break;
- X case 0x03:
- X rasterwidth =
- X (imdata.red
- X + imdata.green
- X + imdata.blue)*rpix/8;
- X break;
- X }
- X } else
- X rasterwidth = (num + 7) / 8;
- X break;
- X
- X case 'T':
- X case 't':
- X break;
- X
- X case 'U':
- X case 'u':
- X curr_plane=0;
- X free_mem(); /* if ESC*rA came first */
- X
- X /* num can be negative */
- X
- X if ( num < 0 )
- X num_planes= -num;
- X else
- X num_planes = num;
- X
- X /*
- X ** This turns off imaging mode,
- X ** so we must recalculate rasterwidth,
- X ** (which is number of bytes needed),
- X ** based on normal raster transfer.
- X */
- X
- X imaging = FALSE; /* goes off */
- X rasterwidth = (rpix + 7) / 8;
- X
- X break;
- X
- X default:
- X break;
- X }
- X
- X return ( TRUE ); /* pass sequence on */
- X
- X } /* group r */
- X
- X /*
- X ** Last and final group 'b'. All the graphics data comes thru here.
- X */
- X
- X
- X switch ( terminator )
- X {
- X case 'm':
- X case 'M':
- X inmode = num;
- X return ( FALSE ); /* we do NOT pass this */
- X break;
- X
- X /*
- X ** <esc>*b#X is obsolete, but I need to use it.
- X ** In addition, they will not get passed thru.
- X */
- X
- X case 'x':
- X case 'X':
- X /*
- X ** Compute in bytes, rounding down.
- X */
- X
- X horiz_offset = num / 8;
- X
- X if ( horiz_offset < 0 ) /* just in case */
- X horiz_offset = 0;
- X
- X if ( strip_offsets || horiz_offset == 0 )
- X return ( FALSE ); /* do not pass seq */
- X
- X break;
- X
- X case 'y':
- X case 'Y':
- X /* zero only if allocated */
- X if ( memflag )
- X zero_seeds();
- X break;
- X
- X case 'W':
- X if(!memflag)
- X zero_seeds(); /* get memory */
- X
- X /* fire up sequence */
- X
- X if ( !in_sequence )
- X {
- X putchar ( ESC );
- X putchar ( parameter );
- X putchar ( group );
- X } else
- X putchar ( old_terminator );
- X
- X in_sequence = FALSE; /* terminating */
- X
- X /*
- X ** Check to see if we are expecting another plane.
- X */
- X
- X if(curr_plane < num_planes)
- X {
- X /*
- X ** If the input file does not have all the
- X ** expected planes (i.e., <esc>*b0W instead
- X ** of <esc>*b0V<esc>*b0V<esc>*b0W), then
- X ** special handling is needed.
- X */
- X
- X if( curr_plane + 1 < num_planes )
- X {
- X Process_Gap ( num );
- X
- X } else /* don't worry, be happy */
- X
- X Process(num, 'W');
- X
- X } else /* oops, too many planes of data */
- X
- X Process_extra(num,'W');
- X
- X curr_plane=0;
- X
- X /*
- X ** If we were not already in graphics, then we are
- X ** now (implied start of raster graphics).
- X */
- X
- X in_graphics = TRUE;
- X
- X return ( FALSE );
- X
- X break;
- X
- X case 'V':
- X if(!memflag)
- X zero_seeds(); /* get memory */
- X
- X /*
- X ** If curr_plane is the last plane, this should
- X ** be a 'W', not a 'V'. I could change it,
- X ** then I would fix Process_extra() to not output
- X ** anything as the 'W' was already sent.
- X */
- X
- X if( curr_plane < num_planes )
- X {
- X /* fire up sequence */
- X
- X if ( !in_sequence )
- X {
- X putchar ( ESC );
- X putchar ( parameter );
- X putchar ( group );
- X } else
- X putchar ( old_terminator );
- X
- X in_sequence = FALSE; /* terminating */
- X
- X
- X Process(num, 'V');
- X curr_plane++;
- X } else
- X Process_extra(num,'V');
- X
- X /*
- X ** If we were not already in graphics, then we are
- X ** now (implied start of raster graphics).
- X */
- X
- X in_graphics = TRUE;
- X
- X return ( FALSE );
- X
- X break;
- X
- X default:
- X break;
- X }
- X
- X return ( TRUE ); /* pass sequence */
- }
- X
- X
- /*
- ** Flush_To_Term() simply passes thru input until a valid terminator
- ** character is found. This is for unwanted escape sequences.
- */
- X
- Flush_To_Term()
- {
- X int c;
- X
- X do
- X {
- X c = getchar();
- X
- X if ( c == EOF ) /* this is a problem */
- X return;
- X
- X putchar ( c );
- X
- X } while ( c < '@' || c > '^' );
- }
- X
- X
- /*
- ** Flush_Bytes() simply transfers so many bytes directly from input to output.
- ** This is used to pass thru binary data that we are not interested in so that
- ** it will not confuse the parser. I.e. downloads.
- */
- X
- Flush_Bytes( num )
- unsigned int num;
- {
- X int bnum;
- X
- X while ( num > 0 )
- X {
- X bnum = MIN ( BUFSIZ, num );
- X
- X fread( buf, 1, bnum, stdin );
- X
- X if ( fwrite( buf, 1, bnum, stdout ) < bnum )
- X
- X /* check for error and exit */
- X
- X if ( ferror(stdout) )
- X {
- X perror("Output error");
- X exit(-2);
- X }
- X
- X num -= bnum;
- X }
- }
- X
- X
- X
- X
- /*----------------------------------------*/
- X
- /*
- ** Zero_seeds() will allocate and initialize memory.
- ** If memory has already been allocated, then it will just initialize it.
- */
- X
- X
- zero_seeds()
- {
- X int r;
- X
- X /* first allocate and init seed_rows for number of planes. */
- X
- X for ( r = 0; r < num_planes ; r++)
- X {
- X if(!memflag)
- X {
- X seed_row[r] = (unsigned char *) malloc(rasterwidth);
- X
- X if ( seed_row[r] == NULL )
- X {
- X fprintf(stderr, "Out of memory.\n");
- X exit(-3);
- X }
- X }
- X
- X /* zero seeds for mode 3 */
- X
- X memset(seed_row[r], 0, rasterwidth);
- X }
- X
- X
- X if(!memflag)
- X {
- X new_row = (unsigned char *) malloc(rasterwidth);
- X
- X if ( new_row == NULL )
- X {
- X fprintf(stderr, "Out of memory.\n");
- X exit(-3);
- X }
- X
- X for(r=0; r<MAXMODES; r++)
- X {
- X
- X /*
- X ** Given input size (uncompressed) of n bytes,
- X ** the worst case output size for each mode is:
- X **
- X ** Mode 0: n
- X **
- X ** Mode 1: n * 2
- X **
- X ** Mode 2: n + (n + 127)/128
- X **
- X ** Mode 3: n + (n + 7)/8
- X **
- X ** So, the worst would be mode 1 at 2*n, so I
- X ** simply make all the output sizes be 2*n.
- X */
- X
- X
- X out_row[r] = (unsigned char *) malloc(2 * rasterwidth);
- X
- X if ( out_row[r] == NULL )
- X {
- X fprintf(stderr, "Out of memory.\n");
- X exit(-3);
- X }
- X }
- X
- X }
- X
- X memset(new_row, 0, rasterwidth);
- X
- X memflag = TRUE; /* memory is in place */
- }
- X
- X
- /* this routine if for incomplete transfers of data */
- X
- zero_upper(plane)
- int plane;
- {
- X int i;
- X
- X /* assume memory already present */
- X
- X for ( i = plane; i < num_planes; i++)
- X memset(seed_row[i], 0, rasterwidth);
- }
- X
- X
- /*
- ** Process() manages the decompression and re-compression of data.
- */
- X
- Process(inbytes, terminator)
- int inbytes, terminator;
- {
- X
- X int minmode = 0;
- X
- X inuse[inmode]++;
- X
- X /*
- X ** Clamp horizontal offset to the rasterwidth for safety.
- X */
- X
- X if ( horiz_offset > rasterwidth )
- X
- X horiz_offset = rasterwidth;
- X
- X /*
- X ** Zero out horiz_offset bytes in new_row.
- X */
- X
- X if ( horiz_offset )
- X
- X memset ( new_row, 0, horiz_offset );
- X
- X
- X switch ( inmode ) {
- X
- X case 0:
- X if ( !widthwarning && inbytes > rasterwidth )
- X {
- X /* This is likely to result in data truncation. */
- X widthwarning = TRUE;
- X fprintf(stderr,"Warning: Input pixel width exceeds expected width.\n");
- X }
- X
- X Uncompress_0( inbytes, rasterwidth - horiz_offset,
- X new_row + horiz_offset);
- X break;
- X case 1:
- X Uncompress_1( inbytes, rasterwidth - horiz_offset,
- X new_row + horiz_offset);
- X break;
- X case 2:
- X Uncompress_2( inbytes, rasterwidth - horiz_offset,
- X new_row + horiz_offset);
- X break;
- X case 3:
- X memcpy(new_row, seed_row[curr_plane], rasterwidth);
- X
- X if ( horiz_offset )
- X memset ( new_row, 0, MIN( horiz_offset, rasterwidth ) );
- X
- X Uncompress_3(inbytes, rasterwidth - horiz_offset,
- X new_row + horiz_offset);
- X break;
- X
- X default: /* unknown mode? */
- X
- X /* Don't know what to do about seed rows, pass stuff thru */
- X
- X fprintf(stderr, "%s: Unsupported compression mode %d.\n",
- X progname, inmode );
- X
- X ChangeMode(inmode); /* go to that mode */
- X
- X /* <esc>*b has already been output */
- X
- X printf("%1d%c", inbytes, terminator);
- X
- X Flush_Bytes( inbytes );
- X
- X firstrow = TRUE; /* pop it out of mode 3 */
- X
- X /* Go ahead and clear the seed rows if present */
- X if ( memflag )
- X zero_seeds();
- X
- X return;
- X
- X }
- X
- X /*
- X ** We need to account for the horizontal offset, but if strip_offsets
- X ** is on, then assume that zero is white.
- X */
- X
- X if ( strip_offsets )
- X horiz_offset = 0;
- X
- X
- X if ( mode0 )
- X /* actually, this is redundant since new_row is mode 0 */
- X out_size[0] = Compress_0( new_row + horiz_offset, out_row[0],
- X rasterwidth - horiz_offset );
- X else
- X out_size[0] = MAXBYTES+1;
- X
- X if ( mode1 )
- X out_size[1] = Compress_1( new_row + horiz_offset, out_row[1],
- X rasterwidth - horiz_offset );
- X else
- X out_size[1] = MAXBYTES+1;
- X
- X if ( mode2 )
- X out_size[2] = Compress_2( new_row + horiz_offset, out_row[2],
- X rasterwidth - horiz_offset );
- X else
- X out_size[2] = MAXBYTES+1;
- X
- X if ( mode3 )
- X out_size[3] = Compress_3( seed_row[curr_plane] + horiz_offset,
- X new_row + horiz_offset, out_row[3],
- X rasterwidth - horiz_offset );
- X else
- X out_size[3] = MAXBYTES+1;
- X
- X
- X /*
- X ** Obsolete comment:
- X **
- X ** Now determine which mode will give the best output. Note that it
- X ** takes 5 bytes to change modes, so we penalize all modes that are
- X ** not the current output by 5 bytes. This is to discourage changing
- X ** unless the benifit is worth it. The exception to this rule is
- X ** mode 3. We want to encourage going to mode 3 because of the seed
- X ** row behaviour. That is, if we have a simple picture that does
- X ** not change much, and say each of the sizes for modes 1 and 2 always
- X ** comes out to 4 bytes of data, then if we add 5 to mode 3 each time,
- X ** it would never get selected. But, we remove the penalty, and if
- X ** mode 3 is selected (0 bytes of data needed for mode 3), then each
- X ** succesive row only needs 0 bytes of data. For a 300 dpi A size
- X ** picture with 3 data planes, this could be a savings of 37k bytes.
- X */
- X
- X /*
- X ** With the new parser, the output to change modes is now only
- X ** 2 bytes, since it gets combined with the *b#W sequence.
- X ** So, I decided to ignore the switching penalty.
- X */
- X
- #if 0
- X /*
- X ** Due to a possible bug in PaintJet XL, don't allow mode 3 to be
- X ** selected for the first row of output. But do allow it if the
- X ** user has no other mode selected.
- X */
- X
- X /*
- X ** Turns out that the PaintJet XL bug only happens after a Y-offset,
- X ** which was not being taken care of here anyway, and since
- X ** the one known driver that took advantage of this bug broke with
- X ** this code, I am now removing it.
- X */
- X
- X if ( firstrow && (mode0 || mode1 || mode2) )
- X {
- X out_size[3] = MAXBYTES+1; /* disable mode 3 for now */
- X
- X if ( terminator == 'W' ) /* last plane? */
- X firstrow = FALSE; /* no longer first row */
- X }
- #endif
- X
- X minmode = 3;
- X
- X if ( out_size[2] < out_size[minmode] )
- X minmode = 2;
- X
- X if ( out_size[1] < out_size[minmode] )
- X minmode = 1;
- X
- X if ( out_size[0] < out_size[minmode] )
- X minmode = 0;
- X
- X
- X /* I may remove this sometime */
- X if ( minmode != outmode )
- X if ( out_size[minmode] == out_size[outmode] )
- X minmode = outmode;
- X
- X
- X outuse[minmode]++;
- X
- X if ( outmode != minmode )
- X ChangeMode( minmode );
- X
- X /* <esc>*b has already been output */
- X
- X printf("%1d%c", out_size[minmode], terminator);
- X
- X if ( fwrite( out_row[minmode], 1, out_size[minmode], stdout) <
- X out_size[minmode] )
- X
- X /* check for error and exit */
- X
- X if ( ferror(stdout) )
- X {
- X perror("Output error");
- X exit(-2);
- X }
- X
- X
- X memcpy(seed_row[curr_plane], new_row, rasterwidth);
- X
- X /*
- X ** Now clear horizontal offset for next plane.
- X */
- X
- X horiz_offset = 0;
- X
- }
- X
- X
- /*
- ** Process_Gap() is to handle the case where less planes are sent for a
- ** row than we are expecting. For example, if we are expecting 3 planes
- ** per row, and the driver decides to take a short cut for blank areas and
- ** send only the final 'W' ( <esc>*b0W instead of the complete <esc>*b0V
- ** <esc>*b0V <esc>*b0W), then we have to do some special handling for mode
- ** 3 seed rows.
- **
- ** The terminator is not needed as a parameter since we know that it must
- ** be 'W' to get into this routine.
- */
- X
- Process_Gap(bytes)
- int bytes;
- {
- X char save0, save1, save2, save3;
- X
- X /*
- X ** If the input file does not have all the expected planes
- X ** (i.e., <esc>*b0W instead ** of <esc>*b0V<esc>*b0V<esc>*b0W),
- X ** then special handling is needed.
- X **
- X ** 4 cases are handled:
- X **
- X ** input mode output mode extra action
- X ** ---------- ----------- ------------
- X **
- X ** non-3 non-3 zero seeds
- X **
- X ** 3 3 do nothing
- X **
- X ** non-3 3 zero seeds & extra output
- X **
- X ** 3 non-3 extra output
- X **
- X ** Note: We don't know what the output
- X ** mode will be before we call Process(),
- X ** so we must force the modes.
- X */
- X
- X /*
- X ** Save output modes in case we need to manipulate them.
- X */
- X
- X save0 = mode0;
- X save1 = mode1;
- X save2 = mode2;
- X save3 = mode3;
- X
- X
- X if ( inmode != 3 )
- X {
- X /*
- X ** Force output to non-3
- X ** to do as little as possible.
- X */
- X
- X if ( mode0 || mode1 || mode2 )
- X {
- X mode3 = FALSE;
- X
- X Process(bytes, 'W');
- X
- X mode3 = save3; /* restore mode 3 */
- X
- X zero_upper( curr_plane + 1);
- X
- X } else /* mode 3 is only one allowed for output */
- X {
- X /*
- X ** We must output more info.
- X */
- X
- X Process( bytes, 'V' ); /* convert to plane */
- X
- X curr_plane++;
- X
- X while ( curr_plane < num_planes )
- X {
- X /*
- X ** Restart graphics data sequence.
- X */
- X
- X putchar ( ESC );
- X putchar ( '*' );
- X putchar ( 'b' );
- X
- X /*
- X ** Call Process() with 0 bytes instead
- X ** of just doing output because we
- X ** need Process() to zero the appropriate
- X ** seed rows, and to use mode 3 to clear
- X ** the seed rows in the output (printer).
- X */
- X
- X if ( curr_plane + 1 == num_planes )
- X
- X Process(0, 'W'); /* last plane */
- X else
- X Process(0, 'V');
- X
- X curr_plane++;
- X }
- X }
- X } else /* inmode == 3 */
- X {
- X /*
- X ** Inmode is 3, so make outmode be 3 so we can do nothing.
- X */
- X
- X if ( mode3 ) /* is mode 3 allowed? */
- X {
- X mode0 =
- X mode1 =
- X mode2 = FALSE;
- X
- X Process(bytes, 'W');
- X
- X mode0 = save0; /* restore modes */
- X mode1 = save1;
- X mode2 = save2;
- X
- X } else /* ooops, no mode 3 */
- X {
- X /*
- X ** We must output more info.
- X */
- X
- X Process( bytes, 'V' ); /* convert to plane */
- X
- X curr_plane++;
- X
- X while ( curr_plane < num_planes )
- X {
- X /*
- X ** Restart graphics data sequence.
- X */
- X
- X putchar ( ESC );
- X putchar ( '*' );
- X putchar ( 'b' );
- X
- X /*
- X ** Call Process() with 0 bytes instead
- X ** of just doing output because we
- X ** need Process() to use the seed rows
- X ** to create non-mode3 data.
- X */
- X
- X if ( curr_plane + 1 == num_planes )
- X
- X Process(0, 'W'); /* last plane */
- X else
- X Process(0, 'V');
- X
- X curr_plane++;
- X }
- X }
- X }
- }
- X
- X
- /*
- ** Process_extra() is to handle the extra planes. That is, for PaintJets,
- ** when sending 3 planes of data using <esc>*b#V, many drivers send a
- SHAR_EOF
- true || echo 'restore of pclcomp.c failed'
- fi
- echo 'End of part 1'
- echo 'File pclcomp.c is continued in part 2'
- echo 2 > _shar_seq_.tmp
- exit 0
- exit 0 # Just in case...
-