home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume30 / pclcomp / part01 < prev    next >
Encoding:
Text File  |  1992-05-23  |  55.1 KB  |  2,367 lines

  1. Newsgroups: comp.sources.misc
  2. From: tony@sdd.hp.com (Tony Parkhurst)
  3. Subject:  v30i017:  pclcomp - HP-PCL Compression Filter, Part01/02
  4. Message-ID: <csm-v30i017=pclcomp.131509@sparky.IMD.Sterling.COM>
  5. X-Md4-Signature: 691e3c8e4b6ae41b4add9c437cf0b0c1
  6. Date: Sat, 23 May 1992 18:15:58 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: tony@sdd.hp.com (Tony Parkhurst)
  10. Posting-number: Volume 30, Issue 17
  11. Archive-name: pclcomp/part01
  12. Environment: LaserJet
  13. Supersedes: pclcomp: Volume 25, Issue 10-11
  14.  
  15. Here is the newest version of pclcomp -- The HP PCL graphics compression
  16. filter.  Many bugs are fixed.
  17.  
  18. Tony Parkhurst
  19. -------
  20. #!/bin/sh
  21. # This is a shell archive (produced by shar 3.49)
  22. # To extract the files from this archive, save it to a file, remove
  23. # everything above the "!/bin/sh" line above, and type "sh file_name".
  24. #
  25. # made 05/23/1992 18:11 UTC by kent@sparky.IMD.Sterling.COM
  26. # Source directory /home/kent/mod/csm/queue/test
  27. #
  28. # existing files will NOT be overwritten unless -c is specified
  29. #
  30. # This is part 1 of a multipart archive                                    
  31. # do not concatenate these parts, unpack them in order with /bin/sh        
  32. #
  33. # This shar contains:
  34. # length  mode       name
  35. # ------ ---------- ------------------------------------------
  36. #    674 -rw-rw-r-- Makefile
  37. #   1461 -rw-rw-r-- README
  38. #   1431 -r--r--r-- getopt.c
  39. #   3847 -r--r--r-- pclcomp.1
  40. #  70635 -r--r--r-- pclcomp.c
  41. #   4053 -rw-rw-r-- pclcomp.man
  42. #    546 -rw-rw-r-- printer.note
  43. #
  44. if test -r _shar_seq_.tmp; then
  45.     echo 'Must unpack archives in sequence!'
  46.     echo Please unpack part `cat _shar_seq_.tmp` next
  47.     exit 1
  48. fi
  49. # ============= Makefile ==============
  50. if test -f 'Makefile' -a X"$1" != X"-c"; then
  51.     echo 'x - skipping Makefile (File already exists)'
  52.     rm -f _shar_wnt_.tmp
  53. else
  54. > _shar_wnt_.tmp
  55. echo 'x - extracting Makefile (Text)'
  56. sed 's/^X//' << 'SHAR_EOF' > 'Makefile' &&
  57. SHELL = /bin/sh
  58. BINDIR = /usr/local/bin
  59. S = 1
  60. M = /usr/local
  61. P = pclcomp
  62. CFLAGS =-O -Aa
  63. LDFLAGS =
  64. X
  65. AUX_SOURCE = README $P.man printer.note
  66. X
  67. SOURCE = $P.c getopt.c
  68. X
  69. OBJECTS = $P.o
  70. X
  71. MAKES = Makefile
  72. X
  73. $P : ${OBJECTS}
  74. X    cc -o $P $(CFLAGS) $(OBJECTS) $(LDFLAGS)
  75. X
  76. ${BINDIR}/$P : $P
  77. X    strip $P
  78. X    -mv ${BINDIR}/$P ${BINDIR}/$P.old
  79. X    cp $P ${BINDIR}
  80. X
  81. $M/man/man$S/$P.$S : $P.$S
  82. X    /bin/rm -f $M/man/man$S/$P.$S
  83. X    cp $P.$S $M/man/man$S/$P.$S
  84. X    /bin/rm -f $M/man/cat*$S*/$P.$S
  85. X
  86. install : ${BINDIR}/$P $M/man/man$S/$P.$S
  87. X
  88. shar : $P.shar
  89. X
  90. $P.shar: $P.$S ${MAKES} ${HEADERS} ${SOURCE}
  91. X    shar -ces ${AUX_SOURCE} $P.$S ${MAKES} ${HEADERS} ${SOURCE} > $P.shar
  92. X
  93. clean :
  94. X    /bin/rm -f ${OBJECTS} $P core
  95. SHAR_EOF
  96. chmod 0664 Makefile ||
  97. echo 'restore of Makefile failed'
  98. Wc_c="`wc -c < 'Makefile'`"
  99. test 674 -eq "$Wc_c" ||
  100.     echo 'Makefile: original size 674, current size' "$Wc_c"
  101. rm -f _shar_wnt_.tmp
  102. fi
  103. # ============= README ==============
  104. if test -f 'README' -a X"$1" != X"-c"; then
  105.     echo 'x - skipping README (File already exists)'
  106.     rm -f _shar_wnt_.tmp
  107. else
  108. > _shar_wnt_.tmp
  109. echo 'x - extracting README (Text)'
  110. sed 's/^X//' << 'SHAR_EOF' > 'README' &&
  111. X
  112. X
  113. X     This is pclcomp -- An HP-PCL compression filter.  It reads in
  114. PCL graphics files, and outputs compressed PCL files which may be sent
  115. directly to printers that support the compressions.  A partial list of
  116. printer support is included.
  117. X
  118. X     Why use pclcomp?
  119. X
  120. X    1)  PCL files are much smaller ( up to 90% ).
  121. X
  122. X    2)  Graphics printing on a LaserJet (IIP or III) is faster.
  123. X
  124. X
  125. X     If you have a LaserJet II that does not support compression, you can
  126. still compress the files for storage, and decompress them while printing.
  127. X
  128. X     I wrote this program for testing.  This is NOT an HP product.  It will
  129. NOT be supported by HP, but rather myself, in my spare time, if need be.
  130. X
  131. X     If you need real support for driver development, then call Hewlett-
  132. Packard directly, preferably the ISV support group at the Boise Division.
  133. X
  134. X     You may use parts of this code within your drivers to support compression
  135. if you wish.
  136. X
  137. X     I did what I think is a reasonable job to make the program work for
  138. most possible PCL files.  Please feel free to send comments, complaints 
  139. or suggestions to me at tony@sdd.hp.com.  If you have a file that does
  140. not survive the filter intact, please e-mail me the file and describe the
  141. problem.
  142. X
  143. X     This filter runs under UNIX and MS-DOS and hopefully anything else that
  144. supports ANSI-C.  To compile under HP-UX:
  145. X
  146. cc -Aa -O pclcomp.c -o pclcomp
  147. X
  148. X
  149. X     Please direct all compliments and praise to:  tony@sdd.hp.com
  150. X
  151. X
  152. X     -- Tony Parkhurst
  153. SHAR_EOF
  154. chmod 0664 README ||
  155. echo 'restore of README failed'
  156. Wc_c="`wc -c < 'README'`"
  157. test 1461 -eq "$Wc_c" ||
  158.     echo 'README: original size 1461, current size' "$Wc_c"
  159. rm -f _shar_wnt_.tmp
  160. fi
  161. # ============= getopt.c ==============
  162. if test -f 'getopt.c' -a X"$1" != X"-c"; then
  163.     echo 'x - skipping getopt.c (File already exists)'
  164.     rm -f _shar_wnt_.tmp
  165. else
  166. > _shar_wnt_.tmp
  167. echo 'x - extracting getopt.c (Text)'
  168. sed 's/^X//' << 'SHAR_EOF' > 'getopt.c' &&
  169. #include <stdio.h>
  170. X
  171. #define index strchr
  172. X
  173. /*
  174. X * get option letter from argument vector
  175. X */
  176. int    opterr = 1,        /* useless, never set or used */
  177. X    optind = 1,        /* index into parent argv vector */
  178. X    optopt;            /* character checked for validity */
  179. char    *optarg;        /* argument associated with option */
  180. X
  181. #define BADCH    (int)'?'
  182. #define EMSG    ""
  183. #define tell(s)    fputs(*nargv,stderr);fputs(s,stderr); \
  184. X        fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);
  185. X
  186. getopt(nargc,nargv,ostr)
  187. int    nargc;
  188. char    **nargv,
  189. X    *ostr;
  190. {
  191. X    static char    *place = EMSG;    /* option letter processing */
  192. X    register char    *oli;        /* option letter list index */
  193. X    char    *index();
  194. X
  195. X    if(!*place) {            /* update scanning pointer */
  196. X        if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) return(EOF);
  197. X        if (*place == '-') {    /* found "--" */
  198. X            ++optind;
  199. X            return(EOF);
  200. X        }
  201. X    }                /* option letter okay? */
  202. X    if ((optopt = (int)*place++) == (int)':' || !(oli = index(ostr,optopt))) {
  203. X        if(!*place) ++optind;
  204. X        tell(": illegal option -- ");
  205. X    }
  206. X    if (*++oli != ':') {        /* don't need argument */
  207. X        optarg = NULL;
  208. X        if (!*place) ++optind;
  209. X    }
  210. X    else {                /* need an argument */
  211. X        if (*place) optarg = place;    /* no white space */
  212. X        else if (nargc <= ++optind) {    /* no arg */
  213. X            place = EMSG;
  214. X            tell(": option requires an argument -- ");
  215. X        }
  216. X         else optarg = nargv[optind];    /* white space */
  217. X        place = EMSG;
  218. X        ++optind;
  219. X    }
  220. X    return(optopt);            /* dump back option letter */
  221. }
  222. X
  223. X
  224. X
  225. SHAR_EOF
  226. chmod 0444 getopt.c ||
  227. echo 'restore of getopt.c failed'
  228. Wc_c="`wc -c < 'getopt.c'`"
  229. test 1431 -eq "$Wc_c" ||
  230.     echo 'getopt.c: original size 1431, current size' "$Wc_c"
  231. rm -f _shar_wnt_.tmp
  232. fi
  233. # ============= pclcomp.1 ==============
  234. if test -f 'pclcomp.1' -a X"$1" != X"-c"; then
  235.     echo 'x - skipping pclcomp.1 (File already exists)'
  236.     rm -f _shar_wnt_.tmp
  237. else
  238. > _shar_wnt_.tmp
  239. echo 'x - extracting pclcomp.1 (Text)'
  240. sed 's/^X//' << 'SHAR_EOF' > 'pclcomp.1' &&
  241. .TH PCLCOMP 1
  242. .SH NAME
  243. pclcomp \- Compress PCL graphics files.
  244. .SH SYNOPSIS
  245. .B pclcomp
  246. [
  247. .B "-0123drsvxz"
  248. ]
  249. [
  250. .B "-n"
  251. .I num
  252. ]
  253. [
  254. .I infile
  255. [
  256. .I outfile
  257. ]]
  258. .br
  259. .SH DESCRIPTION
  260. .PP
  261. .B Pclcomp
  262. compresses (or decompresses) HP-PCL (Printer Control Language) graphics data.
  263. The supported compression modes are 0 (uncompressed), 1, 2 and 3.
  264. .B Pclcomp
  265. will read files using any of the modes 0 through 3, and will output using the
  266. modes which will give the best compression.  This compressed version of
  267. the file may be sent directly to a PCL compatible printer, thus reducing
  268. I/O bandwidth.  Pictures may also be saved in compressed form, reducing
  269. disk usage.
  270. In addition, PCL "imaging" files for the PaintJet XL are also supported.
  271. .PP
  272. The options to
  273. .B pclcomp
  274. control the compression modes.  By default,
  275. .B pclcomp
  276. will use modes 0, 2 and 3, but the user may restrict which output
  277. modes it uses by specifying them on the command line with the
  278. .B -0,
  279. .B -1,
  280. .B -2
  281. and
  282. .B -3
  283. options.  To decompress a file, simply specify
  284. .B -0
  285. as the only mode to use for output.  Mode 0 (
  286. .B -0
  287. ) should always be allowed since modes 1, 2 and 3 cannot be guaranteed to
  288. be better than mode 0 for all types of pictures.
  289. .PP
  290. The
  291. .B -z
  292. option disables the zero "strip" feature.  Since most printers do 
  293. zero "filling",
  294. .B pclcomp,
  295. by default, "strips" the trailing zeros of each row (or plane) of data.
  296. Some printers or programs may require that zero "stripping" be disabled.
  297. .PP
  298. By default,
  299. .B pclcomp
  300. expects the input raster width to be 2400 pixels (8" at 300 dpi), and if it is
  301. different (e.g. PaintJet), then the raster width should be specified by
  302. the Source Raster Width escape sequence
  303. .I (<esc>*r#S).
  304. However, many applications do not set the width and assume a default, therefore,
  305. the user may use the 
  306. .B -n
  307. option to
  308. .B pclcomp
  309. to specify a new default raster width.  For PaintJet (8" at 180 dpi), the
  310. number should be 1440.  If the PCL file contains the Source Raster Width
  311. escape sequence, it will override this default.  If
  312. .B pclcomp
  313. thinks that more data is coming in than the specified width, it will
  314. generate a warning, and continue processing (and perhaps truncating) data.
  315. .PP
  316. The
  317. .B -x
  318. option will cause
  319. .B pclcomp
  320. to remove any horizontal offset sequences from the data.  Only use this
  321. option if white is defined to be zero (as with LaserJets).  This will
  322. shrink the data more if modes 2 or 3 are used.
  323. .PP
  324. The
  325. .B -r
  326. option causes
  327. .B pclcomp
  328. to append a
  329. reset sequence
  330. .I "(<esc>E)"
  331. to the end of the job.
  332. .PP
  333. Use the
  334. .B "-d"
  335. option to
  336. .B pclcomp
  337. if the output is to be sent to a DeskJet printer.
  338. .PP
  339. Some applications erroneously send
  340. .I "<esc>*rB"
  341. and
  342. .I "<esc>*rA"
  343. sequences between every row of graphics data.  The 
  344. .B -s
  345. option to
  346. .B pclcomp
  347. will "strip" all 
  348. .I "<esc>*rB"
  349. sequences, and all
  350. .I "<esc>*rA"
  351. sequences after the first occurrence of this sequence.  
  352. In addition, text and control characters residing between
  353. .I "<esc>*rA"
  354. and
  355. .I "<esc>*rB"
  356. sequences will be discarded.
  357. While this will work
  358. well
  359. for many jobs, it may have problems on multi-page or complex jobs.
  360. .PP
  361. The
  362. .B -v
  363. option simply gives statistics to
  364. .I stderr
  365. about which compression modes were used.
  366. .SH EXAMPLES
  367. .nf
  368. To compress a PCL file for LaserJet III, use:
  369. X    pclcomp infile outfile
  370. X
  371. To compress a PCL file for the PaintJet (A size page at 180 dpi), use:
  372. X    pclcomp -01 -n 1440 infile outfile
  373. X
  374. To compress a PCL file for DeskJet, use:
  375. X    pclcomp -d012 infile outfile
  376. X
  377. To fully decompress a PCL file, use:
  378. X    pclcomp -0z < infile > outfile
  379. .fi
  380. .SH WARNINGS
  381. .PP
  382. The 
  383. .B -z
  384. option can cause the output to be larger than the input.
  385. .PP
  386. The 
  387. .B -s
  388. option is useful, but it can cause erroneous output.
  389. .PP
  390. The
  391. .B -x
  392. option can cause black areas on the left side of the picture on color
  393. printers.
  394. .SH AUTHOR
  395. Tony Parkhurst, Hewlett-Packard, San Diego Division  (tony@sdd.hp.com)
  396. SHAR_EOF
  397. chmod 0444 pclcomp.1 ||
  398. echo 'restore of pclcomp.1 failed'
  399. Wc_c="`wc -c < 'pclcomp.1'`"
  400. test 3847 -eq "$Wc_c" ||
  401.     echo 'pclcomp.1: original size 3847, current size' "$Wc_c"
  402. rm -f _shar_wnt_.tmp
  403. fi
  404. # ============= pclcomp.c ==============
  405. if test -f 'pclcomp.c' -a X"$1" != X"-c"; then
  406.     echo 'x - skipping pclcomp.c (File already exists)'
  407.     rm -f _shar_wnt_.tmp
  408. else
  409. > _shar_wnt_.tmp
  410. echo 'x - extracting pclcomp.c (Text)'
  411. sed 's/^X//' << 'SHAR_EOF' > 'pclcomp.c' &&
  412. /*
  413. **  Pclcomp -- PCL compression filter.
  414. **
  415. **  If you have any problems or errors to report, please send them to me:
  416. **
  417. **  Tony Parkhurst  
  418. ** 
  419. **  Email address:  tony@sdd.hp.com    -or-   hp-sdd!tony
  420. **
  421. **  Please send a copy of the file that is causing the problem, and the
  422. **  version of pclcomp you are using.
  423. **
  424. **  All suggestions and requests are welcome.
  425. */
  426. X
  427. /*
  428. X ***************************************************************************
  429. X *
  430. X * $Source: /disc/44/cgtriton/tony/filters/pclcomp/RCS/pclcomp.c,v $ 
  431. X * $Date: 92/04/13 11:55:36 $ 
  432. X * $Revision: 1.48 $
  433. X *
  434. X * Description:    Compresses pcl graphics files.
  435. X *
  436. X * Author:       Tony Parkhurst
  437. X * Created:      890427
  438. X * Language:     C
  439. X *
  440. X * (c) Copyright 1989, Hewlett-Packard Company, all rights reserved.
  441. X *
  442. X ***************************************************************************
  443. X */
  444. X
  445. X
  446. /*
  447. X ***************************************************************************
  448. X *
  449. X * $Log:    pclcomp.c,v $
  450. X * Revision 1.48  92/04/13  11:55:36  11:55:36  tony (Tony Parkhurst)
  451. X * Fixed problem with reseting rasterwidth when <esc>*r#U follows a <esc>*v6W.
  452. X * 
  453. X * Revision 1.47  92/04/06  16:07:54  16:07:54  tony (Tony Parkhurst)
  454. X * Removed special case to force first row to be other than mode 3.
  455. X * 
  456. X * Revision 1.46  92/03/11  15:50:06  15:50:06  tony (Tony Parkhurst)
  457. X * Fixed a bug with Uncompress_3() when input goes past expect width.
  458. X * (This was mainly a problem if the offsets went over the end.)
  459. X * 
  460. X * Also, fixed some semantic errors (null statements) that lint caught.
  461. X * 
  462. X * Revision 1.45  92/02/13  14:15:54  14:15:54  tony (Tony Parkhurst)
  463. X * Replaced mode 1 compress routine.
  464. X * 
  465. X * Revision 1.44  92/02/13  11:14:08  11:14:08  tony (Tony Parkhurst)
  466. X * Replaced mode 3 compression routine (cleaner version)
  467. X * Changed some function names for clarity.
  468. X * Added comments for worst case conditions.
  469. X * 
  470. X * Revision 1.43  92/02/10  13:47:30  13:47:30  tony (Tony Parkhurst)
  471. X * Added AppleTalk sequence to pass thru. (plus fixed dither sequence).
  472. X * 
  473. X * Revision 1.42  92/02/10  13:38:16  13:38:16  tony (Tony Parkhurst)
  474. X * Changed download dither matrix sequence to <esc>*m#W.
  475. X * 
  476. X * Revision 1.41  92/01/14  13:54:43  13:54:43  tony (Tony Parkhurst)
  477. X * Changed some comments.  Added compression ratio to verbose mode.
  478. X * 
  479. X * Revision 1.40  92/01/14  13:20:32  13:20:32  tony (Tony Parkhurst)
  480. X * Added code to deal with implicit end of graphics
  481. X *    (which zeros the seed rows).
  482. X * 
  483. X * Revision 1.39  91/09/13  13:56:28  13:56:28  tony (Tony Parkhurst)
  484. X * Added code to disable zerostrip in mode 2.
  485. X * 
  486. X * Revision 1.38  91/09/10  15:47:28  15:47:28  tony (Tony Parkhurst)
  487. X * Added include file for isdigit()
  488. X * 
  489. X * Revision 1.37  91/09/10  15:08:23  15:08:23  tony (Tony Parkhurst)
  490. X * Clamped horizontal offsets to raster widths.
  491. X * 
  492. X * Revision 1.36  91/09/10  15:04:15  15:04:15  tony (Tony Parkhurst)
  493. X * Re-vamped fraction parsing.
  494. X * 
  495. X * Revision 1.35  91/09/10  14:03:40  14:03:40  tony (Tony Parkhurst)
  496. X * Fixed potential problem with fractions.
  497. X * Removed obsolete invert flag.
  498. X * Cleaned up some comments.
  499. X * 
  500. X * Revision 1.34  91/09/10  13:21:48  13:21:48  tony (Tony Parkhurst)
  501. X * Fixed problems with data gaps  (0W instead of 0V0V0W)
  502. X * Fixed problems with horizontal offsets and mode 3 compression.
  503. X * Added option to strip horizontal offsets (zero value must be white).
  504. X * 
  505. X * Revision 1.33  91/07/18  15:18:43  15:18:43  tony (Tony Parkhurst)
  506. X * Replaced mode 2 compression routine.  Works better now.
  507. X * 
  508. X * Revision 1.32  91/07/08  11:27:24  11:27:24  tony (Tony Parkhurst)
  509. X * Enhanced the strip algorithm for merged graphics.
  510. X * (Also cleaned up some comments, couple of statements.)
  511. X * 
  512. X * Revision 1.31  91/05/30  15:18:51  15:18:51  tony (Tony Parkhurst)
  513. X * Oops, fixed it right this time.
  514. X * 
  515. X * Revision 1.30  91/05/30  15:06:20  15:06:20  tony (Tony Parkhurst)
  516. X * Added fix for negative value for <esc>*r#U.
  517. X * 
  518. X * Revision 1.29  91/05/03  10:12:30  10:12:30  tony (Tony Parkhurst)
  519. X * Small changes.
  520. X * 
  521. X * Revision 1.28  91/04/30  09:41:24  09:41:24  tony (Tony Parkhurst)
  522. X * Now puts stdin and stdout in binary mode for MSDOS.
  523. X *     Changes courtesy of Mike Slomin.
  524. X * Changed usage message a bit.
  525. X * 
  526. X * Revision 1.27  91/04/23  15:48:05  15:48:05  tony (Tony Parkhurst)
  527. X * Added handling of plus_sign in value fields.
  528. X * 
  529. X * Revision 1.26  91/04/23  09:47:11  09:47:11  tony (Tony Parkhurst)
  530. X * Pass thru unknown modes.
  531. X * 
  532. X * Revision 1.25  91/04/18  11:09:27  11:09:27  tony (Tony Parkhurst)
  533. X * Added parse for fractions in values (i.e. <esc>(s16.67H)
  534. X * 
  535. X * Revision 1.24  91/04/10  14:16:30  14:16:30  tony (Tony Parkhurst)
  536. X * strips text and control codes between <esc>*rA and <esc>*rB w/ -s option
  537. X * 
  538. X * Revision 1.23  91/04/05  14:53:25  14:53:25  tony (Tony Parkhurst)
  539. X * Added fixed for deskjet
  540. X * Also added a stripping feature.
  541. X * 
  542. X * Revision 1.22  91/04/05  08:48:53  08:48:53  tony (Tony Parkhurst)
  543. X * Added some error checkin on output for MS-DOS users.
  544. X * 
  545. X * Revision 1.21  91/04/04  12:53:32  12:53:32  tony (Tony Parkhurst)
  546. X * Replaced parser.
  547. X *    Now handles combined escape sequences.
  548. X *    Now handles downloads.
  549. X *    Now combines mode changes with data.
  550. X * 
  551. X * Revision 1.20  91/04/04  08:02:12  08:02:12  tony (Tony Parkhurst)
  552. X * Removed some test code.
  553. X * 
  554. X * Revision 1.19  91/03/25  14:38:48  14:38:48  tony (Tony Parkhurst)
  555. X * Changed defaults.
  556. X * 
  557. X * Revision 1.18  91/03/25  14:31:22  14:31:22  tony (Tony Parkhurst)
  558. X * Re-worked memory allocation stuff for funky input files.
  559. X * 
  560. X * Revision 1.17  91/03/25  13:50:19  13:50:19  tony (Tony Parkhurst)
  561. X * Use command line args for file w/o -i or -o.
  562. X * 
  563. X * Revision 1.16  91/03/04  14:23:15  14:23:15  tony (Tony Parkhurst)
  564. X * Fixed to allow ONLY mode 3 if the user really wants it.
  565. X * 
  566. X * Revision 1.15  91/03/04  14:08:23  14:08:23  tony (Tony Parkhurst)
  567. X * Added an exit(0) at the end of main.
  568. X * fixed up some zerostrip stuff.
  569. X * made mode 3 the highest priority mode.
  570. X * 
  571. X * Revision 1.14  91/02/20  13:57:27  13:57:27  tony (Tony Parkhurst)
  572. X * Changed priority a bit.
  573. X * Added some zerostripping for mode 2.
  574. X * 
  575. X * Revision 1.13  91/02/06  15:31:00  15:31:00  tony (Tony Parkhurst)
  576. X * oops.
  577. X * 
  578. X * Revision 1.12  91/02/06  14:41:28  14:41:28  tony (Tony Parkhurst)
  579. X * fixed usage message
  580. X * 
  581. X * Revision 1.11  91/02/06  14:38:10  14:38:10  tony (Tony Parkhurst)
  582. X * Added file input and output for MS-DOS.
  583. X * 
  584. X * Revision 1.10  91/02/05  17:49:23  17:49:23  tony (Tony Parkhurst)
  585. X * Fixed problem with zero stripped input.
  586. X * 
  587. X * Revision 1.9  91/02/05  16:11:39  16:11:39  tony (Tony Parkhurst)
  588. X * Removed delay code and bitfield stuff.
  589. X * 
  590. X * Revision 1.8  91/02/05  11:04:53  11:04:53  tony (Tony Parkhurst)
  591. X * Added io delay stuff for testing.
  592. X * 
  593. X * Revision 1.7  91/02/05  10:28:32  10:28:32  tony (Tony Parkhurst)
  594. X * Fix for someone specifing ONLY mode 3.
  595. X * 
  596. X * Revision 1.6  91/01/29  14:13:09  14:13:09  tony (Tony Parkhurst)
  597. X * Updated some comments.
  598. X * 
  599. X * Revision 1.5  91/01/29  13:26:24  13:26:24  tony (Tony Parkhurst)
  600. X * Cleaned up, revamped a bit.
  601. X * 
  602. X * Revision 1.4  89/11/09  15:59:16  15:59:16  tony (Tony Parkhurst)
  603. X * Fix for esc * r U coming after esc * r A.
  604. X * 
  605. X * Revision 1.3  89/10/24  11:31:12  11:31:12  tony (Tony Parkhurst)
  606. X * Added parsing of <esc>*rC
  607. X * 
  608. X * Revision 1.2  89/10/13  09:56:46  09:56:46  tony (Tony Parkhurst)
  609. X * Completely revamped by Greg G.
  610. X * 
  611. X * Revision 1.1  89/06/15  13:57:46  13:57:46  tony (Tony Parkhurst)
  612. X * Initial revision
  613. X * 
  614. X *
  615. X ***************************************************************************
  616. X */
  617. static const char copyr[]=
  618. X    "Copyright (c) 1991, Hewlett-Packard Company, all rights reserved.";
  619. X
  620. static const char author[]="Tony Parkhurst";
  621. X
  622. static const char rcs_id[]="$Header: pclcomp.c,v 1.48 92/04/13 11:55:36 tony Exp $";
  623. X
  624. static const char rev_id[]="$Revision: 1.48 $";
  625. X
  626. /*
  627. X *   Input and output formats are HP pcl.
  628. X *
  629. X *   Imaging files ("Configure Image Data") are supported.
  630. X *
  631. X *   Pclcomp does not take advantage of Y-Offset for blank areas.  
  632. X *   This is because Y-Offset creates white areas, but we don't do enough
  633. X *   parsing to determine what value "white" has.  An application that
  634. X *   can assume white values could make use of this sequence.
  635. X *
  636. X *   Pclcomp does not do any of the block compression modes (4-8).
  637. X *
  638. X *   An additional enhancement would be to compare all the planes in a
  639. X *   multi-plane file (color) and if nothing changed, using mode 3, just
  640. X *   output a single <esc>*b0W.
  641. X *
  642. X *
  643. X *   Usage:  pclcomp [-v] [-0] [-1] [-2] [-3] [-z] [-n###] < infile > outfile
  644. X *
  645. X *   Pclcomp will do graphics compression based on compression modes 0, 1, 2
  646. X *   and 3.  (Mode 0 is uncompressed).  Pclcomp will accept all modes, and
  647. X *   will attempt to optimize by selecting the best output mode for each
  648. X *   row (or plane) of data.  By default, pclcomp will use all 4 modes, but
  649. X *   the user may restrict which output modes to use with the -0123 options.
  650. X *   For example, to use pclcomp for output to a PaintJet which only knows
  651. X *   modes 0 and 1 (the XL also understands modes 2 and 3), one would use:
  652. X *
  653. X *      pclcomp -01 < infile > outfile
  654. X *
  655. X *   Note:  Mode 0 should always be allowed.  None of the other modes is
  656. X *   guaranteed to be better than mode 0 in all cases.
  657. X *
  658. X *   The 'v' option tells the number of rows (planes) of data input and output
  659. X *   in the different modes (to stderr).
  660. X *
  661. X *   By default, pclcomp does zero "stripping" which is useful for PaintJet 
  662. X *   files using only modes 0 and 1, the PaintJet (and other PCL printers) 
  663. X *   will do zero "filling".  
  664. X *   NOTE: Use the 'z' option to disable zero stripping.
  665. X *
  666. X *   The 'n' option is to change the default number of pixels in a picture.
  667. X *   The proper way to set the pixel width is with the source raster width
  668. X *   sequence <esc*r#S>, but soo many applications just assume the default,
  669. X *   which is different on different printers, so I am providing this
  670. X *   command line option to set a new default.  One could also change the
  671. X *   DEFAULT constant below (make sure it is a multiple of 8).  
  672. X *   Currently it is set to 8" at 300 dpi (2400).
  673. X */
  674. X
  675. #include <stdio.h>
  676. #include <string.h>
  677. #include <ctype.h>
  678. X
  679. #ifdef MSDOS
  680. #include <fcntl.h>
  681. #endif
  682. X
  683. X
  684. #define Get_Character() getchar()
  685. X
  686. #define MIN(x,y)    ( ((x) < (y)) ? (x) : (y) )
  687. X
  688. #define TRUE 1
  689. #define FALSE 0
  690. X
  691. #define ESC    27
  692. X
  693. #define DEFAULT 2400        /* default width in pixels (multiple of 8) */
  694. X
  695. #define MAXMODES  4
  696. #define MAXPLANES 8
  697. #define MAXBYTES 60000        /* now mostly meaningless, just a big number */
  698. X
  699. unsigned char    *seed_row[MAXPLANES];
  700. unsigned char    *new_row;
  701. unsigned char    *out_row[MAXMODES];
  702. unsigned int    out_size[MAXMODES];
  703. X
  704. char    memflag = FALSE;    /* set when memory has been allocated */
  705. X
  706. X
  707. char    mode0=FALSE,
  708. X    mode1=FALSE,
  709. X    mode2=FALSE,
  710. X    mode3=FALSE;
  711. X
  712. unsigned char    num_planes=1;
  713. unsigned char    curr_plane=0;
  714. X
  715. char    imaging = FALSE;        /* not imaging, so no lockout */
  716. X
  717. char    verbose = FALSE;
  718. X
  719. unsigned char    inmode = 0;        /* input compression mode */
  720. unsigned char    outmode = 0;        /* output compression mode */
  721. X
  722. unsigned int    rasterwidth=DEFAULT/8;    /* width of picture, in bytes */
  723. unsigned int    rpix = DEFAULT;        /* width of picture, in pixels */
  724. X
  725. unsigned char    zerostrip= TRUE;    /* strip trailing zeros */
  726. X
  727. unsigned int    inuse[4]={0,0,0,0}, outuse[4] = {0,0,0,0};
  728. X
  729. char    widthwarning = FALSE;    /* for trucation warning */
  730. char    firstrow = TRUE;    /* to prevent mode 3 from being first */
  731. X
  732. X
  733. struct {            /* this will hold the data for the  */
  734. X     unsigned char model;    /* configuring of image processing   */
  735. X     unsigned char pix_mode;
  736. X     unsigned char inx_bits;
  737. X     unsigned char red;
  738. X     unsigned char green;
  739. X     unsigned char blue;
  740. X     short wr;
  741. X     short wg;
  742. X     short wb;
  743. X     short br;
  744. X     short bg;
  745. X     short bb; 
  746. } imdata;
  747. X
  748. extern    unsigned char *malloc();
  749. X
  750. char    *filein = NULL, *fileout = NULL;
  751. X
  752. /*
  753. **  These variables are for the new parser.
  754. **  The new parser handles more sequences, and also deals with combined
  755. **  escape sequences better.
  756. */
  757. X
  758. int    parameter;
  759. int    group_char;
  760. int    terminator;
  761. int    old_terminator;
  762. int    value;
  763. float    fvalue;            /* fractional value */
  764. int    scanf_count;
  765. char    in_sequence = FALSE;
  766. char    pass_seq;
  767. char    plus_sign;        /* for relative values */
  768. X
  769. X
  770. /* dummy buffer */
  771. char buf[BUFSIZ];
  772. X
  773. /*
  774. **  If the printer is a DeskJet, then we must handle <esc>*rB differently
  775. **  Option '-d' will turn on this mode.
  776. */
  777. X
  778. char    deskjet = FALSE;
  779. X
  780. X
  781. /*
  782. **  Many drivers it seems put <esc>*rB<esc>*rA between each and every row
  783. **  of data.  This defeats compression mode 3 on a DeskJet, and also
  784. **  makes the PaintJet (not XL) quite slow.  This next flag "-s" on the
  785. **  command line, will attempt to do a reasonable job of stripping
  786. **  out the excess commands.
  787. **
  788. **  The in_graphics flag will be used to strip unwanted control chars from
  789. **  the file.  It will also be used to spot implicit end of graphics.
  790. */
  791. X
  792. char    strip_seq = FALSE;
  793. char    in_graphics = FALSE;
  794. X
  795. X
  796. /*
  797. **  Just for certain special cases, it would be nice to append an <esc>E reset
  798. **  to the end of the job.  Specify with "-r".
  799. */
  800. X
  801. char    reset_seq = FALSE;
  802. X
  803. X
  804. char    *progname;        /* to hold the program name for verbose */
  805. X
  806. /*
  807. **  Even though the horizontal offset command <esc>*b#X is obsolete, many
  808. **  drivers still use it, and it causes some interesting problems with
  809. **  mode 3 compression, so pclcomp needs to deal with it in some hopefully
  810. **  intelligent fashion, and they will get stripped if -x is used.
  811. */
  812. X
  813. int    horiz_offset = 0;
  814. X
  815. char    strip_offsets = FALSE;
  816. X
  817. X
  818. static float    Get_Frac();    /* instead of scanf */
  819. X
  820. X
  821. X
  822. /*
  823. ******************************************************************************
  824. **
  825. **                Main program.
  826. **
  827. ******************************************************************************
  828. */
  829. X
  830. main(argc, argv)
  831. int argc;
  832. char *argv[];
  833. {
  834. X  int    c,j;
  835. X  extern char *optarg;
  836. X  extern int   optind;
  837. X
  838. X    progname = argv[0];
  839. X
  840. #ifdef MSDOS
  841. X    setmode(fileno(stdin), O_BINARY);    /* Place stdin and stdout in */
  842. X    setmode(fileno(stdout), O_BINARY);    /* binary mode. (Mike Slomin)*/
  843. #endif
  844. X
  845. X    /* parse up the args here */
  846. X
  847. X  while ((c = getopt(argc, argv, "0123drsvzn:i:o:sx")) != EOF )
  848. X    switch(c){
  849. X    case '0':
  850. X            mode0 = TRUE;
  851. X            break;
  852. X    case '1':
  853. X            mode1 = TRUE;
  854. X            break;
  855. X    case '2':
  856. X            mode2 = TRUE;
  857. X            break;
  858. X    case '3':
  859. X            mode3 = TRUE;
  860. X            break;
  861. X    case 'd':
  862. X            deskjet = TRUE;
  863. X            break;
  864. X    case 'r':
  865. X            reset_seq = TRUE;
  866. X            break;
  867. X    case 's':
  868. X            strip_seq = TRUE;
  869. X            break;
  870. X    case 'v':
  871. X            verbose = TRUE;
  872. X            break;
  873. X    case 'x':
  874. X            strip_offsets = TRUE;
  875. X            break;
  876. X    case 'z':
  877. X            zerostrip = FALSE;
  878. X            break;
  879. X    case 'n':
  880. X            rpix = atoi(optarg);    /* new default */
  881. X            rasterwidth = (rpix + 7) / 8;    /* round up */
  882. X            break;
  883. X
  884. X    case 'i':
  885. X            filein = optarg;
  886. X            break;
  887. X    case 'o':
  888. X            fileout = optarg;
  889. X            break;
  890. X
  891. X    case '?':
  892. X    default:
  893. X            fprintf(stderr, 
  894. X            "Usage: %s [-0123drsvxz] [-n###] [infile [outfile]]\n",
  895. X                argv[0]);
  896. X            exit(-1);
  897. X    };
  898. X
  899. X    if ( verbose )
  900. X    {
  901. X        fprintf(stderr, "%s: %s\n", argv[0], rev_id);
  902. X    }
  903. X
  904. X
  905. X  if ( ! ( mode0 || mode1 || mode2 || mode3) )    /* any modes on? */
  906. X    mode0 = /* mode1 = */ mode2 = mode3 = TRUE;    /* 3 modes by default */
  907. X
  908. X    /*
  909. X    **  Check to see if any file args were given on the command line.
  910. X    **  Ones that were not preceded by a "-i" or "-o".
  911. X    */
  912. X
  913. X    if ( filein == NULL && optind < argc && argv[optind] != NULL )
  914. X        filein = argv[optind++];
  915. X
  916. X    if ( fileout == NULL && optind < argc && argv[optind] != NULL )
  917. X        fileout = argv[optind++];
  918. X
  919. X    /*
  920. X    **  Now open files for stdin and stdout if provided by the user.
  921. X    */
  922. X
  923. X    if ( filein != NULL )        /* new input file */
  924. X
  925. X        if ( freopen( filein, "rb", stdin ) == NULL )
  926. X        {
  927. X            fprintf(stderr,"Unable to open %s for input.\n",filein);
  928. X            exit(-42);
  929. X        }
  930. X
  931. X    if ( fileout != NULL )        /* new output file */
  932. X
  933. X        if ( freopen( fileout, "wb", stdout ) == NULL )
  934. X        {
  935. X            fprintf(stderr, "Unable to open %s for output.\n",
  936. X                fileout);
  937. X            exit(-43);
  938. X        }
  939. X
  940. X
  941. X    /*
  942. X    **
  943. X    **        This is the pcl input parsing loop.
  944. X    **
  945. X    */
  946. X
  947. X    while( ( c = getchar() ) != EOF )
  948. X    {
  949. X
  950. X        /*  Ignore all chars until an escape char  */
  951. X
  952. X        /*
  953. X        **  If we are in graphics, toss it if strip_seq is set.
  954. X        */
  955. X
  956. X        if ( c != ESC )
  957. X        {
  958. X            /*
  959. X            **  Simply pass thru the character if not stripping
  960. X            **  or not in graphics.
  961. X            */
  962. X
  963. X            if ( !strip_seq || !in_graphics )
  964. X                putchar(c);    /* pass it thru */
  965. X            
  966. X            /*
  967. X            **  If we are in graphics and we are not stripping,
  968. X            **  then this character implies an end raster graphics,
  969. X            **  and the seed rows need to be zeroed.
  970. X            */
  971. X
  972. X            if ( in_graphics && !strip_seq )
  973. X            {
  974. X                zero_seeds();
  975. X                in_graphics = FALSE;    /* fell out */
  976. X            }
  977. X
  978. X            continue;    /* pop to the top of the loop */
  979. X        }
  980. X
  981. X        /*
  982. X        **  Now we have an escape sequence, get the parameter char.
  983. X        */
  984. X
  985. X        parameter = getchar();
  986. X
  987. X        if ( parameter == EOF )        /* oops */
  988. X        {
  989. X            putchar ( ESC );
  990. X            fprintf(stderr, "Warning:  File ended with <esc>.\n");
  991. X            break;            /* unexpected end of input */
  992. X        }
  993. X
  994. X        /*
  995. X        **  Now check to see if it is a two character sequence.
  996. X        */
  997. X
  998. X        if ( parameter >= '0' && parameter <= '~' )
  999. X        {
  1000. X            putchar ( ESC );
  1001. X            putchar ( parameter );        /* pass it thru */
  1002. X
  1003. X            /*
  1004. X            **  If the second character is an E, then we
  1005. X            **  and the printer do a reset.
  1006. X            */
  1007. X
  1008. X            if ( parameter == 'E' )
  1009. X            {
  1010. X                free_mem();
  1011. X                curr_plane = 0;
  1012. X                num_planes = 1;
  1013. X                imaging = FALSE;
  1014. X                inmode = 0;
  1015. X                outmode = 0;
  1016. X                in_graphics = FALSE;
  1017. X
  1018. X                /* can't do this if user gave value with -n.
  1019. X                rasterwidth = DEFAULT/8;
  1020. X                rpix = DEFAULT;
  1021. X                */
  1022. X            }
  1023. X
  1024. X            continue;        /* return to the top */
  1025. X        }
  1026. X
  1027. X        /*
  1028. X        **  Now check to make sure that the parameter character is
  1029. X        **  within range.
  1030. X        */
  1031. X
  1032. X        if ( parameter < '!' || parameter > '/' )
  1033. X        {
  1034. X            putchar ( ESC );
  1035. X            putchar ( parameter );
  1036. X
  1037. X            fprintf(stderr, "Warning:  Invalid escape sequence.\n");
  1038. X
  1039. X            continue;
  1040. X        }
  1041. X
  1042. X        /*
  1043. X        **  We are only interested in certain parameters, so pass
  1044. X        **  the rest of the sequences.
  1045. X        */
  1046. X
  1047. X        /*
  1048. X        **  For the moment, we are only interested in '*' (graphics)
  1049. X        **  '(' and ')' (downloads).  Although we do not do anything
  1050. X        **  with downloads, we need to pass the binary data thru
  1051. X        **  untouched.
  1052. X        **  Oops, '&' is handled too.
  1053. X        */
  1054. X
  1055. X        if ( parameter != '*' && parameter != '(' 
  1056. X            && parameter != ')' && parameter != '&' )
  1057. X        {
  1058. X
  1059. X            /*
  1060. X            **  If the "stripper" is active, we need to suspend
  1061. X            **  it till graphics are re-started.
  1062. X            */
  1063. X
  1064. X            if ( strip_seq && !in_graphics )
  1065. X            {
  1066. X                curr_plane = 0;
  1067. X                free_mem();        /* force re-start */
  1068. X            }
  1069. X
  1070. X            /*
  1071. X            **  Pass thru the sequence intact.
  1072. X            */
  1073. X
  1074. X            putchar ( ESC );
  1075. X            putchar ( parameter );
  1076. X            Flush_To_Term();        /* flush rest of seq. */
  1077. X            continue;
  1078. X        }
  1079. X
  1080. X
  1081. X        /*
  1082. X        **  Parameter character is in range, look for a valid group char
  1083. X        */
  1084. X
  1085. X        group_char = getchar();
  1086. X
  1087. X        if ( group_char == EOF )    /* oops, ran out of input */
  1088. X        {
  1089. X            putchar ( ESC );
  1090. X            putchar ( parameter );
  1091. X
  1092. X            fprintf(stderr, "Warning:  Incomplete escape sequence.\n");
  1093. X            break;
  1094. X        }
  1095. X
  1096. X        /*
  1097. X        **  See if in proper range.  If it isn't, it is not an error
  1098. X        **  because the group character is optional for some sequences.
  1099. X        **  For the moment, we are not interested in those sequences,
  1100. X        **  so pass them thru.
  1101. X        */
  1102. X
  1103. X        if ( group_char < '`' || group_char > '~' )
  1104. X        {
  1105. X
  1106. X            /*
  1107. X            **  If the "stripper" is active, we need to suspend
  1108. X            **  it till graphics are re-started.
  1109. X            */
  1110. X
  1111. X            if ( strip_seq && !in_graphics )
  1112. X            {
  1113. X                curr_plane = 0;
  1114. X                free_mem();        /* force re-start */
  1115. X            }
  1116. X
  1117. X            /*
  1118. X            **  Pass thru the sequence intact.
  1119. X            */
  1120. X
  1121. X            putchar ( ESC );
  1122. X            putchar ( parameter );
  1123. X            putchar ( group_char );
  1124. X            if ( group_char < '@' || group_char > '^' )
  1125. X                Flush_To_Term();    /* pass rest of seq. */
  1126. X            continue;
  1127. X        }
  1128. X
  1129. X        /*
  1130. X        **  Now we have a valid group character, decide if we want
  1131. X        **  to deal with this escape sequence.
  1132. X        **
  1133. X        **  Sequences we want do deal with include:
  1134. X        **
  1135. X        **    <esc>*r    ** graphics
  1136. X        **    <esc>*b    ** graphics
  1137. X        **    <esc>*v    ** graphics
  1138. X        **
  1139. X        **  Sequences we must pass thru binary data:
  1140. X        **
  1141. X        **    <esc>*c    ** pattern
  1142. X        **    <esc>*m    ** download dither
  1143. X        **    <esc>*t    ** obsolete
  1144. X        **    <esc>(f    ** download char set
  1145. X        **    <esc>(s    ** download char
  1146. X        **    <esc>)s    ** download font
  1147. X        **    <esc>&a    ** logical page
  1148. X        **    <esc>&b    ** AppleTalk stuff
  1149. X        **    <esc>&l    ** obsolete
  1150. X        **
  1151. X        */
  1152. X
  1153. X        if (  ( parameter == '*'
  1154. X            && group_char != 'r' && group_char != 'b' 
  1155. X            && group_char != 'v' && group_char != 'c' 
  1156. X            && group_char != 't' && group_char != 'm' )
  1157. X           || ( parameter == '&'
  1158. X            && group_char != 'a' && group_char != 'l' 
  1159. X            && group_char != 'b' )
  1160. X           || ( parameter == '(' 
  1161. X            && group_char != 'f' && group_char != 's' )
  1162. X           || ( parameter == ')' && group_char != 's' ) )
  1163. X        {
  1164. X            /*
  1165. X            **  Definately not interested in the sequence.
  1166. X            */
  1167. X
  1168. X            /*
  1169. X            **  If the "stripper" is active, we need to suspend
  1170. X            **  it till graphics are re-started.
  1171. X            */
  1172. X
  1173. X            if ( strip_seq && !in_graphics )
  1174. X            {
  1175. X                curr_plane = 0;
  1176. X                free_mem();        /* force re-start */
  1177. X            }
  1178. X
  1179. X            /*
  1180. X            **  Pass thru the sequence intact.
  1181. X            */
  1182. X
  1183. X            putchar ( ESC );
  1184. X            putchar ( parameter );
  1185. X            putchar ( group_char );
  1186. X            Flush_To_Term();
  1187. X            continue;
  1188. X        }
  1189. X
  1190. X
  1191. X        /*
  1192. X        **  If the sequence is <esc>&a#H, it will have gotten past
  1193. X        **  the above, but we need to suspend the "stripper" if
  1194. X        **  it is active, because the CAP is getting moved.
  1195. X        **
  1196. X        **  The <esc>*p#X/Y sequences will have been filtered
  1197. X        **  thru just above (<esc>*p is not a needed group).
  1198. X        */
  1199. X
  1200. X        if ( strip_seq && parameter != '*' && !in_graphics )
  1201. X        {
  1202. X            curr_plane = 0;
  1203. X            free_mem();        /* force re-start */
  1204. X        }
  1205. X
  1206. X
  1207. X        /*
  1208. X        **  Now set up a pass thru flag so we can ignore the entire
  1209. X        **  sequences of some of these.
  1210. X        */
  1211. X
  1212. X        if ( parameter != '*' )
  1213. X            pass_seq = TRUE;
  1214. X
  1215. X        else if ( group_char == 'c' || group_char == 't' 
  1216. X               || group_char == 'm' )
  1217. X
  1218. X            pass_seq = TRUE;
  1219. X        else
  1220. X            pass_seq = FALSE;
  1221. X
  1222. X
  1223. X        /*
  1224. X        **  Now we have a sequence that we are definately interested in.
  1225. X        **
  1226. X        **  Get the value field and terminator, and loop until final
  1227. X        **  terminator is found.
  1228. X        */
  1229. X
  1230. X        do
  1231. X        {
  1232. X            /* first see if the value has a plus sign */
  1233. X
  1234. X            scanf_count = scanf(" + %d", &value );
  1235. X
  1236. X            if ( scanf_count == 1 )
  1237. X
  1238. X                plus_sign = TRUE;
  1239. X            else
  1240. X            {
  1241. X                plus_sign = FALSE;
  1242. X
  1243. X                scanf_count = scanf(" %d", &value );
  1244. X
  1245. X                if ( scanf_count == 0 )
  1246. X                    value = 0;        /* by default */
  1247. X            }
  1248. X
  1249. X            /*
  1250. X            **  I wonder if I will get bitten by a trailing
  1251. X            **  space character right here?
  1252. X            */
  1253. X
  1254. X            terminator = getchar();
  1255. X
  1256. X            /*
  1257. X            **  Check for a fractional component.
  1258. X            */
  1259. X
  1260. X            fvalue = 0.0;
  1261. X
  1262. X            if ( terminator == '.' )
  1263. X            {
  1264. X                fvalue = Get_Frac();
  1265. X
  1266. X                /*
  1267. X                **  Now get real terminator.
  1268. X                */
  1269. X
  1270. X                terminator = getchar();
  1271. X            }
  1272. X
  1273. X
  1274. X            if ( terminator == EOF )    /* barf */
  1275. X            {
  1276. X                fprintf(stderr, 
  1277. X                "Warning:  Incomplete sequence at EOF.\n");
  1278. X                break;
  1279. X            }
  1280. X
  1281. X            /*
  1282. X            **  If the pass_seq flag is set, then just pass
  1283. X            **  it thru to stdout until a 'W' is found.
  1284. X            */
  1285. X
  1286. X            if ( pass_seq )
  1287. X            {
  1288. X                /*
  1289. X                **  If not in sequence, then we output esc
  1290. X                **  otherwise, output the saved terminator.
  1291. X                */
  1292. X
  1293. X                if ( !in_sequence )
  1294. X                {
  1295. X                    in_sequence = TRUE;
  1296. X                    putchar ( ESC );
  1297. X                    putchar ( parameter );
  1298. X                    putchar ( group_char );
  1299. X                } else
  1300. X                {
  1301. X                    putchar ( old_terminator );
  1302. X                }
  1303. X
  1304. X                /* now pass the value */
  1305. X
  1306. X                if ( plus_sign )
  1307. X                    putchar('+');
  1308. X
  1309. X                /*
  1310. X                **  See if there was a non-zero fraction.
  1311. X                */
  1312. X
  1313. X                if ( fvalue != 0.0 )
  1314. X                {
  1315. X                    if ( value < 0 )
  1316. X                    {
  1317. X                        putchar('-');
  1318. X                        value = -value;
  1319. X                    }
  1320. X
  1321. X                    fvalue += value;
  1322. X
  1323. X                    printf("%g", fvalue);
  1324. X
  1325. X                } else if ( scanf_count )
  1326. X                    printf("%0d", value);
  1327. X                
  1328. X                /*
  1329. X                **  We save the terminator, because we may
  1330. X                **  need to change it to upper case.
  1331. X                */
  1332. X
  1333. X                old_terminator = terminator;
  1334. X
  1335. X                /* if binary data, pass it thru */
  1336. X
  1337. X                if ( terminator == 'W' )    /* aha */
  1338. X                {
  1339. X                    putchar ( terminator );
  1340. X                    in_sequence = FALSE;    /* terminates */
  1341. X                    Flush_Bytes ( value );    /* pass data */
  1342. X                }
  1343. X
  1344. X                continue;
  1345. X            }
  1346. X
  1347. X            /*
  1348. X            **  Ok, this is a sequence we want to pay attention to.
  1349. X            **
  1350. X            **  Do_Graphics returns TRUE if we need to pass seq.
  1351. X            **
  1352. X            **  Note:  Do_Graphics modifies the parser vars such
  1353. X            **         as in_sequence.  This is because it may
  1354. X            **         have to output stuff directly.
  1355. X            */
  1356. X
  1357. X            if ( Do_Graphics ( group_char, value, terminator ) )
  1358. X            {
  1359. X                /*
  1360. X                **  If not in sequence, then we output esc
  1361. X                **  otherwise, output the saved terminator.
  1362. X                */
  1363. X
  1364. X                if ( !in_sequence )
  1365. X                {
  1366. X                    in_sequence = TRUE;
  1367. X                    putchar ( ESC );
  1368. X                    putchar ( parameter );
  1369. X                    putchar ( group_char );
  1370. X                } else
  1371. X                {
  1372. X                    putchar ( old_terminator );
  1373. X                }
  1374. X
  1375. X                /* now pass the value */
  1376. X
  1377. X                if ( plus_sign )
  1378. X                    putchar('+');
  1379. X
  1380. X                /*
  1381. X                **  See if there was a non-zero fraction.
  1382. X                */
  1383. X
  1384. X                if ( fvalue != 0.0 )
  1385. X                {
  1386. X                    if ( value < 0 )
  1387. X                    {
  1388. X                        putchar('-');
  1389. X                        value = -value;
  1390. X                    }
  1391. X
  1392. X                    fvalue += value;
  1393. X
  1394. X                    printf("%g", fvalue);
  1395. X
  1396. X                } else if ( scanf_count )
  1397. X                    printf("%0d", value);
  1398. X
  1399. X                /*
  1400. X                **  We save the terminator, because we may
  1401. X                **  need to change it to upper case.
  1402. X                */
  1403. X
  1404. X                old_terminator = terminator;
  1405. X            }
  1406. X
  1407. X        } while ( terminator >= '`' && terminator <= '~' );
  1408. X
  1409. X        /*
  1410. X        ** The oppsite test (above) may be more appropriate.  That is, 
  1411. X        ** !(terminator >= '@' && terminator <= '^').
  1412. X        */
  1413. X        
  1414. X        /*
  1415. X        **  If we were in a sequence, then we must terminate it.
  1416. X        **  If it was lower case, then it must be uppered.
  1417. X        */
  1418. X
  1419. X        if ( in_sequence )
  1420. X        {
  1421. X            putchar ( terminator & 0xdf );        /* a ==> A */
  1422. X            in_sequence = FALSE;
  1423. X        }
  1424. X    }
  1425. X    
  1426. X
  1427. X    /*
  1428. X    **  If the user wants a reset, give him one.
  1429. X    */
  1430. X
  1431. X    if ( reset_seq )
  1432. X    {
  1433. X        putchar ( ESC );
  1434. X        putchar ( 'E' );
  1435. X    }
  1436. X
  1437. X
  1438. X    /*
  1439. X    **  Finished up, so print stats and close output file.
  1440. X    */
  1441. X
  1442. X
  1443. X
  1444. X    if ( verbose )
  1445. X    {
  1446. X        long    inpos, outpos;
  1447. X
  1448. X        for(j = 0; j < 4; j++)
  1449. X            fprintf(stderr,"Rows in mode %1d: %d\n", j, inuse[j]);
  1450. X        for(j = 0; j < 4; j++)
  1451. X            fprintf(stderr,"Rows out mode %1d: %d\n", j, outuse[j]);
  1452. X
  1453. X        inpos = ftell(stdin);
  1454. X        outpos = ftell(stdout);
  1455. X
  1456. X        /*
  1457. X        **  If the input or output is a pipe, then ftell returns a
  1458. X        **  -1.  Don't bother telling the user about it.
  1459. X        */
  1460. X
  1461. X        if ( inpos > 0 && outpos > 0 )
  1462. X        {
  1463. X
  1464. X            fprintf(stderr, "Input size:  %ld bytes\n", inpos );
  1465. X            fprintf(stderr, "Output size: %ld bytes\n", outpos );
  1466. X
  1467. X            if ( inpos > outpos )
  1468. X                fprintf(stderr, "Compression: %ld%%\n",
  1469. X                    (long)(99L - 100L*outpos/inpos));
  1470. X            else if ( outpos > inpos )
  1471. X                fprintf(stderr, "Expansion: %ld%%\n",
  1472. X                    (long)(100L*outpos/inpos - 100L));
  1473. X            else
  1474. X                fprintf(stderr, "No compression.\n");
  1475. X        }
  1476. X    }
  1477. X
  1478. X    fclose(stdout);
  1479. X
  1480. X    exit(0);
  1481. }
  1482. X
  1483. X
  1484. /*
  1485. **  Do_Graphics() takes the graphics escape sequence and performs the
  1486. **  necessary functions.
  1487. **  TRUE is returned if the escape sequence needs to be passed to the output.
  1488. */
  1489. X
  1490. int    Do_Graphics( group, num, terminator )
  1491. int    group, num, terminator;
  1492. {
  1493. X    /*  first look at vW  */
  1494. X
  1495. X    if ( group == 'v' )
  1496. X
  1497. X        if ( terminator != 'W' )
  1498. X            
  1499. X            return ( TRUE );    /* pass it thru */
  1500. X        else
  1501. X        {
  1502. X            if ( !in_sequence )
  1503. X            {
  1504. X                putchar ( ESC );
  1505. X                putchar ( parameter );
  1506. X                putchar ( group );
  1507. X            } else
  1508. X                putchar ( old_terminator );
  1509. X
  1510. X            in_sequence = FALSE;        /* terminating */
  1511. X
  1512. X            printf("%0d", num);
  1513. X            putchar ( terminator );
  1514. X
  1515. X            free_mem();    /* reset memory */
  1516. X
  1517. X            imaging++;
  1518. X
  1519. X            fread(&imdata, MIN(num, 18), 1, stdin);
  1520. X            fwrite(&imdata, MIN(num, 18), 1, stdout);
  1521. X
  1522. X            num -= MIN(num, 18);
  1523. X
  1524. X            /* copy rest of unknown data */
  1525. X
  1526. X            if ( num > 0 )
  1527. X                Flush_Bytes(num);
  1528. X
  1529. X
  1530. X            switch(imdata.pix_mode){
  1531. X                case 0x00:
  1532. X                    rasterwidth = (rpix + 7)/8;
  1533. X                    num_planes = imdata.inx_bits;
  1534. X                    break;
  1535. X                case 0x01:
  1536. X                    rasterwidth = rpix*imdata.inx_bits/8;
  1537. X                    break;
  1538. X                case 0x02:
  1539. X                    rasterwidth = (rpix + 7)/8;
  1540. X                    num_planes =imdata.red + imdata.green +
  1541. X                                imdata.blue;
  1542. X                    break;
  1543. X                case 0x03:
  1544. X                    rasterwidth = (imdata.red +
  1545. X                                   imdata.green +
  1546. X                                   imdata.blue)*rpix/8;
  1547. X                    break;
  1548. X            }
  1549. X
  1550. X            return ( FALSE );
  1551. X        }
  1552. X
  1553. X    /*
  1554. X    **  Now deal with <esc>*r stuff
  1555. X    */
  1556. X
  1557. X    if ( group == 'r' )
  1558. X    {
  1559. X        switch ( terminator )
  1560. X        {
  1561. X            case 'A':
  1562. X            case 'a':
  1563. X
  1564. X                /* Enter graphics mode, enable stripping */
  1565. X
  1566. X                in_graphics = TRUE;
  1567. X
  1568. X                /* if user wants to strip redundant seq */
  1569. X                if ( strip_seq && memflag )
  1570. X                    return( FALSE );
  1571. X
  1572. X                curr_plane=0;
  1573. X                zero_seeds();    /* may allocate mem */
  1574. X                break;
  1575. X
  1576. X            case 'C':
  1577. X            case 'c':
  1578. X
  1579. X                /* Exit graphics, disable stripping */
  1580. X
  1581. X                in_graphics = FALSE;
  1582. X
  1583. X                if ( strip_seq )
  1584. X                    return( FALSE );
  1585. X
  1586. X                inmode = 0;
  1587. X                outmode = 0;
  1588. X
  1589. X                free_mem();
  1590. X                curr_plane=0;
  1591. X                break;
  1592. X
  1593. X            case 'B':
  1594. X            case 'b':
  1595. X
  1596. X                /* Exit graphics, disable stripping */
  1597. X
  1598. X                in_graphics = FALSE;
  1599. X
  1600. X                if ( strip_seq )
  1601. X                    return( FALSE );
  1602. X
  1603. X                if ( deskjet )    /* B resets modes on DJ */
  1604. X                {
  1605. X                    inmode = 0;
  1606. X                    outmode = 0;
  1607. X                }
  1608. X                free_mem();
  1609. X                curr_plane=0;
  1610. X                break;
  1611. X
  1612. X            case 'S':
  1613. X            case 's':
  1614. X
  1615. X                /* free mem in case widths changed */
  1616. X                free_mem();
  1617. X
  1618. X                rpix = num;
  1619. X
  1620. X                if (imaging){
  1621. X                    switch(imdata.pix_mode)
  1622. X                    {
  1623. X                        case 0x00:
  1624. X                            rasterwidth=(rpix+7)/8;
  1625. X                            break;
  1626. X                        case 0x01:
  1627. X                            rasterwidth = 
  1628. X                             rpix*imdata.inx_bits/8;
  1629. X                            break;
  1630. X                        case 0x02:
  1631. X                            rasterwidth=(rpix+7)/8;
  1632. X                            break;
  1633. X                        case 0x03:
  1634. X                            rasterwidth = 
  1635. X                              (imdata.red 
  1636. X                              + imdata.green
  1637. X                              + imdata.blue)*rpix/8;
  1638. X                            break;
  1639. X                    }
  1640. X                } else
  1641. X                    rasterwidth = (num + 7) / 8;
  1642. X                break;
  1643. X
  1644. X            case 'T':
  1645. X            case 't':
  1646. X                break;
  1647. X
  1648. X            case 'U':
  1649. X            case 'u':
  1650. X                curr_plane=0;
  1651. X                free_mem();    /* if ESC*rA came first */
  1652. X
  1653. X                /*  num can be negative */
  1654. X
  1655. X                if ( num < 0 )
  1656. X                    num_planes= -num;
  1657. X                else
  1658. X                    num_planes = num;
  1659. X
  1660. X                /*
  1661. X                **  This turns off imaging mode,
  1662. X                **  so we must recalculate rasterwidth,
  1663. X                **  (which is number of bytes needed),
  1664. X                **  based on normal raster transfer.
  1665. X                */
  1666. X
  1667. X                imaging = FALSE;    /* goes off */
  1668. X                rasterwidth = (rpix + 7) / 8;
  1669. X
  1670. X                break;
  1671. X
  1672. X            default:
  1673. X                break;
  1674. X        }
  1675. X
  1676. X        return ( TRUE );        /* pass sequence on */
  1677. X
  1678. X    }    /* group r */
  1679. X
  1680. X    /*
  1681. X    **  Last and final group 'b'.  All the graphics data comes thru here.
  1682. X    */
  1683. X
  1684. X
  1685. X    switch ( terminator )
  1686. X    {
  1687. X           case 'm':
  1688. X           case 'M':
  1689. X            inmode = num;
  1690. X            return ( FALSE );    /* we do NOT pass this */
  1691. X            break;
  1692. X
  1693. X           /*
  1694. X           **  <esc>*b#X is obsolete, but I need to use it.
  1695. X           **  In addition, they will not get passed thru.
  1696. X           */
  1697. X
  1698. X           case 'x':
  1699. X           case 'X':
  1700. X            /*
  1701. X            **  Compute in bytes, rounding down.
  1702. X            */
  1703. X
  1704. X            horiz_offset = num / 8;
  1705. X
  1706. X            if ( horiz_offset < 0 )        /* just in case */
  1707. X                horiz_offset = 0;
  1708. X
  1709. X            if ( strip_offsets || horiz_offset == 0 )
  1710. X                return ( FALSE );    /* do not pass seq */
  1711. X
  1712. X            break;
  1713. X
  1714. X           case 'y':
  1715. X           case 'Y':
  1716. X            /* zero only if allocated */
  1717. X            if ( memflag )
  1718. X                zero_seeds();
  1719. X            break;
  1720. X
  1721. X           case 'W':
  1722. X            if(!memflag)
  1723. X                zero_seeds();        /* get memory */
  1724. X
  1725. X            /* fire up sequence */
  1726. X
  1727. X            if ( !in_sequence )
  1728. X            {
  1729. X                putchar ( ESC );
  1730. X                putchar ( parameter );
  1731. X                putchar ( group );
  1732. X            } else
  1733. X                putchar ( old_terminator );
  1734. X
  1735. X            in_sequence = FALSE;        /* terminating */
  1736. X
  1737. X            /*
  1738. X            **  Check to see if we are expecting another plane.
  1739. X            */
  1740. X
  1741. X            if(curr_plane < num_planes) 
  1742. X            {
  1743. X                /*
  1744. X                **  If the input file does not have all the
  1745. X                **  expected planes  (i.e., <esc>*b0W instead
  1746. X                **  of <esc>*b0V<esc>*b0V<esc>*b0W), then
  1747. X                **  special handling is needed.
  1748. X                */
  1749. X
  1750. X                if( curr_plane + 1 < num_planes )
  1751. X                {
  1752. X                    Process_Gap ( num );
  1753. X
  1754. X                } else        /* don't worry, be happy */
  1755. X
  1756. X                    Process(num, 'W');
  1757. X
  1758. X            } else        /* oops, too many planes of data */
  1759. X
  1760. X                Process_extra(num,'W');   
  1761. X
  1762. X            curr_plane=0;
  1763. X
  1764. X            /*
  1765. X            **  If we were not already in graphics, then we are
  1766. X            **  now (implied start of raster graphics).
  1767. X            */
  1768. X
  1769. X            in_graphics = TRUE;
  1770. X
  1771. X            return ( FALSE );
  1772. X
  1773. X            break;
  1774. X
  1775. X           case 'V':
  1776. X            if(!memflag)
  1777. X                zero_seeds();        /* get memory */
  1778. X            
  1779. X            /*
  1780. X            **  If curr_plane is the last plane, this should
  1781. X            **  be a 'W', not a 'V'.  I could change it,
  1782. X            **  then I would fix Process_extra() to not output
  1783. X            **  anything as the 'W' was already sent.
  1784. X            */
  1785. X
  1786. X            if( curr_plane < num_planes ) 
  1787. X            {
  1788. X                /* fire up sequence */
  1789. X
  1790. X                if ( !in_sequence )
  1791. X                {
  1792. X                    putchar ( ESC );
  1793. X                    putchar ( parameter );
  1794. X                    putchar ( group );
  1795. X                } else
  1796. X                    putchar ( old_terminator );
  1797. X
  1798. X                in_sequence = FALSE;    /* terminating */
  1799. X            
  1800. X
  1801. X                Process(num, 'V');
  1802. X                curr_plane++;
  1803. X            } else
  1804. X                Process_extra(num,'V');
  1805. X
  1806. X            /*
  1807. X            **  If we were not already in graphics, then we are
  1808. X            **  now (implied start of raster graphics).
  1809. X            */
  1810. X
  1811. X            in_graphics = TRUE;
  1812. X
  1813. X            return ( FALSE );
  1814. X
  1815. X            break;
  1816. X
  1817. X        default:
  1818. X            break;
  1819. X    }
  1820. X
  1821. X    return ( TRUE );        /* pass sequence */
  1822. }
  1823. X
  1824. X
  1825. /*
  1826. **  Flush_To_Term() simply passes thru input until a valid terminator
  1827. **  character is found.  This is for unwanted escape sequences.
  1828. */
  1829. X
  1830. Flush_To_Term()
  1831. {
  1832. X    int    c;
  1833. X
  1834. X    do
  1835. X    {
  1836. X        c = getchar();
  1837. X
  1838. X        if ( c == EOF )            /* this is a problem */
  1839. X            return;
  1840. X        
  1841. X        putchar ( c );
  1842. X
  1843. X    } while ( c < '@' || c > '^' );
  1844. }
  1845. X
  1846. X
  1847. /*
  1848. **  Flush_Bytes() simply transfers so many bytes directly from input to output.
  1849. **  This is used to pass thru binary data that we are not interested in so that
  1850. **  it will not confuse the parser.  I.e. downloads.
  1851. */
  1852. X
  1853. Flush_Bytes( num )
  1854. unsigned int    num;
  1855. {
  1856. X    int    bnum;
  1857. X
  1858. X    while ( num > 0 )
  1859. X    {
  1860. X        bnum = MIN ( BUFSIZ, num );
  1861. X
  1862. X        fread( buf, 1, bnum, stdin );
  1863. X
  1864. X        if ( fwrite( buf, 1, bnum, stdout ) < bnum )
  1865. X
  1866. X            /* check for error and exit */
  1867. X
  1868. X            if ( ferror(stdout) )
  1869. X            {
  1870. X                perror("Output error");
  1871. X                exit(-2);
  1872. X            }
  1873. X
  1874. X        num -= bnum;
  1875. X    }
  1876. }
  1877. X
  1878. X
  1879. X
  1880. X
  1881. /*----------------------------------------*/
  1882. X
  1883. /*
  1884. **    Zero_seeds() will allocate and initialize memory.
  1885. **    If memory has already been allocated, then it will just initialize it.
  1886. */
  1887. X
  1888. X
  1889. zero_seeds()
  1890. {
  1891. X    int r;
  1892. X
  1893. X    /* first allocate and init seed_rows for number of planes. */
  1894. X
  1895. X    for ( r = 0; r < num_planes ; r++)
  1896. X    {
  1897. X        if(!memflag)
  1898. X        {
  1899. X            seed_row[r] = (unsigned char *) malloc(rasterwidth);
  1900. X
  1901. X            if ( seed_row[r] == NULL )
  1902. X            {
  1903. X                fprintf(stderr, "Out of memory.\n");
  1904. X                exit(-3);
  1905. X            }
  1906. X        }
  1907. X
  1908. X        /* zero seeds for mode 3 */
  1909. X
  1910. X        memset(seed_row[r], 0, rasterwidth);
  1911. X    }
  1912. X
  1913. X
  1914. X    if(!memflag)
  1915. X    {
  1916. X        new_row = (unsigned char *) malloc(rasterwidth);
  1917. X
  1918. X        if ( new_row == NULL )
  1919. X        {
  1920. X            fprintf(stderr, "Out of memory.\n");
  1921. X            exit(-3);
  1922. X        }
  1923. X
  1924. X        for(r=0; r<MAXMODES; r++)
  1925. X        {
  1926. X
  1927. X            /*
  1928. X            **  Given input size (uncompressed) of n bytes, 
  1929. X            **  the worst case output size for each mode is:
  1930. X            **
  1931. X            **  Mode 0:  n
  1932. X            **
  1933. X            **  Mode 1:  n * 2
  1934. X            **
  1935. X            **  Mode 2:  n + (n + 127)/128
  1936. X            **
  1937. X            **  Mode 3:  n + (n + 7)/8
  1938. X            **
  1939. X            **  So, the worst would be mode 1 at 2*n, so I
  1940. X            **  simply make all the output sizes be 2*n.
  1941. X            */
  1942. X
  1943. X
  1944. X            out_row[r] = (unsigned char *) malloc(2 * rasterwidth);
  1945. X
  1946. X            if ( out_row[r] == NULL )
  1947. X            {
  1948. X                fprintf(stderr, "Out of memory.\n");
  1949. X                exit(-3);
  1950. X            }
  1951. X        }
  1952. X
  1953. X    }
  1954. X
  1955. X    memset(new_row, 0, rasterwidth);
  1956. X
  1957. X    memflag = TRUE;            /* memory is in place */
  1958. }
  1959. X
  1960. X
  1961. /* this routine if for incomplete transfers of data */
  1962. X
  1963. zero_upper(plane)
  1964. int    plane;
  1965. {
  1966. X    int i;
  1967. X
  1968. X    /* assume memory already present */
  1969. X
  1970. X    for ( i = plane; i < num_planes; i++)
  1971. X        memset(seed_row[i], 0, rasterwidth);
  1972. }
  1973. X
  1974. X
  1975. /*
  1976. **  Process() manages the decompression and re-compression of data.
  1977. */
  1978. X
  1979. Process(inbytes, terminator)
  1980. int inbytes, terminator;
  1981. {
  1982. X
  1983. X    int minmode = 0;
  1984. X
  1985. X    inuse[inmode]++;
  1986. X
  1987. X    /*
  1988. X    **  Clamp horizontal offset to the rasterwidth for safety.
  1989. X    */
  1990. X
  1991. X    if ( horiz_offset > rasterwidth )
  1992. X
  1993. X        horiz_offset = rasterwidth;
  1994. X
  1995. X    /*
  1996. X    **  Zero out horiz_offset bytes in new_row.
  1997. X    */
  1998. X
  1999. X    if ( horiz_offset )
  2000. X
  2001. X        memset ( new_row, 0, horiz_offset );
  2002. X
  2003. X
  2004. X    switch ( inmode ) {
  2005. X
  2006. X    case 0:
  2007. X        if ( !widthwarning && inbytes > rasterwidth )
  2008. X        {
  2009. X            /* This is likely to result in data truncation. */
  2010. X            widthwarning = TRUE;
  2011. X            fprintf(stderr,"Warning: Input pixel width exceeds expected width.\n");
  2012. X        }
  2013. X
  2014. X        Uncompress_0( inbytes, rasterwidth - horiz_offset,
  2015. X                new_row + horiz_offset);
  2016. X        break;
  2017. X    case 1:
  2018. X        Uncompress_1( inbytes, rasterwidth - horiz_offset,
  2019. X                new_row + horiz_offset);
  2020. X        break;
  2021. X    case 2:
  2022. X        Uncompress_2( inbytes, rasterwidth - horiz_offset,
  2023. X                new_row + horiz_offset);
  2024. X        break;
  2025. X    case 3:
  2026. X        memcpy(new_row, seed_row[curr_plane], rasterwidth);
  2027. X
  2028. X        if ( horiz_offset )
  2029. X            memset ( new_row, 0, MIN( horiz_offset, rasterwidth ) );
  2030. X
  2031. X        Uncompress_3(inbytes, rasterwidth - horiz_offset,
  2032. X                new_row + horiz_offset);
  2033. X        break;
  2034. X
  2035. X    default:        /* unknown mode? */
  2036. X
  2037. X        /*  Don't know what to do about seed rows, pass stuff thru */
  2038. X
  2039. X        fprintf(stderr, "%s: Unsupported compression mode %d.\n",
  2040. X            progname, inmode );
  2041. X
  2042. X        ChangeMode(inmode);    /* go to that mode */
  2043. X
  2044. X        /* <esc>*b has already been output */
  2045. X
  2046. X        printf("%1d%c", inbytes, terminator);
  2047. X
  2048. X        Flush_Bytes( inbytes );
  2049. X
  2050. X        firstrow = TRUE;        /* pop it out of mode 3 */
  2051. X
  2052. X        /*  Go ahead and clear the seed rows if present  */
  2053. X        if ( memflag )
  2054. X            zero_seeds();
  2055. X
  2056. X        return;
  2057. X
  2058. X    }
  2059. X
  2060. X    /*
  2061. X    **  We need to account for the horizontal offset, but if strip_offsets
  2062. X    **  is on, then assume that zero is white.
  2063. X    */
  2064. X
  2065. X    if ( strip_offsets )
  2066. X        horiz_offset = 0;
  2067. X
  2068. X
  2069. X    if ( mode0 )
  2070. X        /* actually, this is redundant since new_row is mode 0 */
  2071. X        out_size[0] = Compress_0( new_row + horiz_offset, out_row[0], 
  2072. X                rasterwidth - horiz_offset );
  2073. X    else
  2074. X        out_size[0] = MAXBYTES+1;
  2075. X
  2076. X    if ( mode1 )
  2077. X        out_size[1] = Compress_1( new_row + horiz_offset, out_row[1], 
  2078. X                rasterwidth - horiz_offset );
  2079. X    else
  2080. X        out_size[1] = MAXBYTES+1;
  2081. X
  2082. X    if ( mode2 )
  2083. X        out_size[2] = Compress_2( new_row + horiz_offset, out_row[2], 
  2084. X                rasterwidth - horiz_offset );
  2085. X    else
  2086. X        out_size[2] = MAXBYTES+1;
  2087. X
  2088. X    if ( mode3 )
  2089. X        out_size[3] = Compress_3( seed_row[curr_plane] + horiz_offset, 
  2090. X                new_row + horiz_offset, out_row[3], 
  2091. X                rasterwidth - horiz_offset );
  2092. X    else
  2093. X        out_size[3] = MAXBYTES+1;
  2094. X    
  2095. X
  2096. X    /*
  2097. X    **  Obsolete comment:
  2098. X    **
  2099. X    **  Now determine which mode will give the best output.  Note that it
  2100. X    **  takes 5 bytes to change modes, so we penalize all modes that are
  2101. X    **  not the current output by 5 bytes.  This is to discourage changing
  2102. X    **  unless the benifit is worth it.  The exception to this rule is
  2103. X    **  mode 3.  We want to encourage going to mode 3 because of the seed
  2104. X    **  row behaviour.  That is, if we have a simple picture that does
  2105. X    **  not change much, and say each of the sizes for modes 1 and 2 always
  2106. X    **  comes out to 4 bytes of data, then if we add 5 to mode 3 each time,
  2107. X    **  it would never get selected.  But, we remove the penalty, and if
  2108. X    **  mode 3 is selected (0 bytes of data needed for mode 3), then each
  2109. X    **  succesive row only needs 0 bytes of data.  For a 300 dpi A size
  2110. X    **  picture with 3 data planes, this could be a savings of 37k bytes.
  2111. X    */
  2112. X
  2113. X    /*
  2114. X    **  With the new parser, the output to change modes is now only
  2115. X    **  2 bytes, since it gets combined with the *b#W sequence.
  2116. X    **  So, I decided to ignore the switching penalty.
  2117. X    */
  2118. X
  2119. #if 0
  2120. X    /*
  2121. X    **  Due to a possible bug in PaintJet XL, don't allow mode 3 to be
  2122. X    **  selected for the first row of output.  But do allow it if the
  2123. X    **  user has no other mode selected.
  2124. X    */
  2125. X
  2126. X    /*
  2127. X    **  Turns out that the PaintJet XL bug only happens after a Y-offset,
  2128. X    **  which was not being taken care of here anyway, and since 
  2129. X    **  the one known driver that took advantage of this bug broke with
  2130. X    **  this code, I am now removing it.
  2131. X    */
  2132. X
  2133. X    if ( firstrow && (mode0 || mode1 || mode2) )
  2134. X    {
  2135. X        out_size[3] = MAXBYTES+1;    /* disable mode 3 for now */
  2136. X
  2137. X        if ( terminator == 'W' )    /* last plane? */
  2138. X            firstrow = FALSE;    /* no longer first row */
  2139. X    }
  2140. #endif
  2141. X
  2142. X    minmode = 3;
  2143. X
  2144. X    if ( out_size[2] < out_size[minmode] )
  2145. X        minmode = 2;
  2146. X
  2147. X    if ( out_size[1] < out_size[minmode] )
  2148. X        minmode = 1;
  2149. X
  2150. X    if ( out_size[0] < out_size[minmode] )
  2151. X        minmode = 0;
  2152. X
  2153. X
  2154. X                    /* I may remove this sometime */
  2155. X    if ( minmode != outmode )
  2156. X        if ( out_size[minmode] == out_size[outmode] )
  2157. X            minmode = outmode;
  2158. X
  2159. X
  2160. X    outuse[minmode]++;
  2161. X
  2162. X    if ( outmode != minmode )
  2163. X        ChangeMode( minmode );
  2164. X    
  2165. X    /* <esc>*b has already been output */
  2166. X
  2167. X    printf("%1d%c", out_size[minmode], terminator);
  2168. X
  2169. X    if ( fwrite( out_row[minmode], 1, out_size[minmode], stdout) < 
  2170. X                            out_size[minmode] )
  2171. X
  2172. X        /* check for error and exit */
  2173. X
  2174. X        if ( ferror(stdout) )
  2175. X        {
  2176. X            perror("Output error");
  2177. X            exit(-2);
  2178. X        }
  2179. X
  2180. X
  2181. X    memcpy(seed_row[curr_plane], new_row, rasterwidth);
  2182. X
  2183. X    /*
  2184. X    **  Now clear horizontal offset for next plane.
  2185. X    */
  2186. X
  2187. X    horiz_offset = 0;
  2188. X
  2189. }
  2190. X
  2191. X
  2192. /*
  2193. **  Process_Gap() is to handle the case where less planes are sent for a
  2194. **  row than we are expecting.  For example, if we are expecting 3 planes
  2195. **  per row, and the driver decides to take a short cut for blank areas and
  2196. **  send only the final 'W'  ( <esc>*b0W instead of the complete <esc>*b0V
  2197. **  <esc>*b0V <esc>*b0W), then we have to do some special handling for mode
  2198. **  3 seed rows.
  2199. **
  2200. **  The terminator is not needed as a parameter since we know that it must
  2201. **  be 'W' to get into this routine.
  2202. */
  2203. X
  2204. Process_Gap(bytes)
  2205. int    bytes;
  2206. {
  2207. X    char    save0, save1, save2, save3;
  2208. X
  2209. X    /*
  2210. X    **  If the input file does not have all the expected planes  
  2211. X    **  (i.e., <esc>*b0W instead **  of <esc>*b0V<esc>*b0V<esc>*b0W), 
  2212. X    **  then special handling is needed.
  2213. X    **
  2214. X    **  4 cases are handled:
  2215. X    **
  2216. X    **  input mode  output mode   extra action
  2217. X    **  ----------  -----------   ------------
  2218. X    **
  2219. X    **    non-3       non-3       zero seeds
  2220. X    **
  2221. X    **      3           3         do nothing
  2222. X    **
  2223. X    **    non-3         3         zero seeds & extra output
  2224. X    **
  2225. X    **      3         non-3       extra output
  2226. X    **
  2227. X    **  Note:  We don't know what the output
  2228. X    **  mode will be before we call Process(),
  2229. X    **  so we must force the modes.
  2230. X    */
  2231. X
  2232. X    /*
  2233. X    **  Save output modes in case we need to manipulate them.
  2234. X    */
  2235. X
  2236. X    save0 = mode0;
  2237. X    save1 = mode1;
  2238. X    save2 = mode2;
  2239. X    save3 = mode3;
  2240. X
  2241. X
  2242. X    if ( inmode != 3 )
  2243. X    {
  2244. X        /*
  2245. X        **  Force output to non-3
  2246. X        **  to do as little as possible.
  2247. X        */
  2248. X
  2249. X        if ( mode0 || mode1 || mode2 )
  2250. X        {
  2251. X            mode3 = FALSE;
  2252. X
  2253. X            Process(bytes, 'W');
  2254. X
  2255. X            mode3 = save3;        /* restore mode 3 */
  2256. X
  2257. X            zero_upper( curr_plane + 1);
  2258. X
  2259. X        } else    /* mode 3 is only one allowed for output */
  2260. X        {
  2261. X            /*
  2262. X            **  We must output more info.
  2263. X            */
  2264. X
  2265. X            Process( bytes, 'V' );    /* convert to plane */
  2266. X
  2267. X            curr_plane++;
  2268. X
  2269. X            while ( curr_plane < num_planes )
  2270. X            {
  2271. X                /*
  2272. X                **  Restart graphics data sequence.
  2273. X                */
  2274. X
  2275. X                putchar ( ESC );
  2276. X                putchar ( '*' );
  2277. X                putchar ( 'b' );
  2278. X
  2279. X                /*
  2280. X                **  Call Process() with 0 bytes instead
  2281. X                **  of just doing output because we
  2282. X                **  need Process() to zero the appropriate
  2283. X                **  seed rows, and to use mode 3 to clear
  2284. X                **  the seed rows in the output (printer).
  2285. X                */
  2286. X
  2287. X                if ( curr_plane + 1 == num_planes )
  2288. X
  2289. X                    Process(0, 'W');    /* last plane */
  2290. X                else
  2291. X                    Process(0, 'V');
  2292. X
  2293. X                curr_plane++;
  2294. X            }
  2295. X        }
  2296. X    } else        /* inmode == 3 */
  2297. X    {
  2298. X        /*
  2299. X        **  Inmode is 3, so make outmode be 3 so we can do nothing.
  2300. X        */
  2301. X
  2302. X        if ( mode3 )            /* is mode 3 allowed? */
  2303. X        {
  2304. X            mode0 =
  2305. X            mode1 =
  2306. X            mode2 = FALSE;
  2307. X
  2308. X            Process(bytes, 'W');
  2309. X
  2310. X            mode0 = save0;        /* restore modes */
  2311. X            mode1 = save1;
  2312. X            mode2 = save2;
  2313. X
  2314. X        } else                /* ooops, no mode 3 */
  2315. X        {
  2316. X            /*
  2317. X            **  We must output more info.
  2318. X            */
  2319. X
  2320. X            Process( bytes, 'V' );    /* convert to plane */
  2321. X
  2322. X            curr_plane++;
  2323. X
  2324. X            while ( curr_plane < num_planes )
  2325. X            {
  2326. X                /*
  2327. X                **  Restart graphics data sequence.
  2328. X                */
  2329. X
  2330. X                putchar ( ESC );
  2331. X                putchar ( '*' );
  2332. X                putchar ( 'b' );
  2333. X
  2334. X                /*
  2335. X                **  Call Process() with 0 bytes instead
  2336. X                **  of just doing output because we
  2337. X                **  need Process() to use the seed rows
  2338. X                **  to create non-mode3 data.
  2339. X                */
  2340. X
  2341. X                if ( curr_plane + 1 == num_planes )
  2342. X
  2343. X                    Process(0, 'W');    /* last plane */
  2344. X                else
  2345. X                    Process(0, 'V');
  2346. X
  2347. X                curr_plane++;
  2348. X            }
  2349. X        }
  2350. X    }
  2351. }
  2352. X
  2353. X
  2354. /*
  2355. **  Process_extra() is to handle the extra planes.  That is, for PaintJets,
  2356. **  when sending 3 planes of data using <esc>*b#V, many drivers send a
  2357. SHAR_EOF
  2358. true || echo 'restore of pclcomp.c failed'
  2359. fi
  2360. echo 'End of  part 1'
  2361. echo 'File pclcomp.c is continued in part 2'
  2362. echo 2 > _shar_seq_.tmp
  2363. exit 0
  2364. exit 0 # Just in case...
  2365.