home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 3 / 3253 < prev    next >
Internet Message Format  |  1991-04-29  |  64KB

  1. From: tony@sdd.hp.com (Tony Parkhurst)
  2. Newsgroups: alt.sources
  3. Subject: HP-PCL graphics compression filter for printers.
  4. Message-ID: <1991Apr29.165853.19335@sdd.hp.com>
  5. Date: 29 Apr 91 16:58:53 GMT
  6.  
  7.  
  8.  
  9.      Ok, here is pclcomp -- An HP-PCL compression filter.  It reads in
  10. PCL graphics files, and outputs compressed PCL files which may be sent
  11. directly to printers that support the compressions.  A partial list of
  12. printer support is included.
  13.  
  14.      Why use pclcomp?
  15.  
  16.     1)  PCL files are much smaller (I routinely see compressions up
  17.         to 90%.
  18.  
  19.     2)  Graphics printing on a LaserJet (IIP or III) is faster.
  20.  
  21.  
  22.      If you have a LaserJet II that does not support compression, you can
  23. still compress the files for storage, and decompress them while printing.
  24.  
  25.      I wrote this program for testing.  This is NOT an HP product.  It will
  26. NOT be supported by HP, but rather myself, in my spare time, if need be.
  27.  
  28.      If you need real support for driver development, then call Hewlett-
  29. Packard directly, preferably the ISV support group at the Boise Division
  30. (I think).
  31.  
  32.      You may use parts of this code within your drivers to support compression
  33. if you wish.
  34.  
  35.      I did what I think is a reasonable job to make the program work for
  36. most possible PCL files.  Please feel free to send comments, complaints 
  37. or suggestions to me at tony@sdd.hp.com.  If you have a file that does
  38. not survive the filter intact, please e-mail me the file and describe the
  39. problem.
  40.  
  41.      You will need to supply getopt().  If this is a problem, I can email
  42. a PD version of getopt().
  43.  
  44.      This filter runs under UNIX and MS-DOS and hopefully anything else that
  45. supports ANSI-C.
  46.  
  47.      Please direct all compliments and praise to:  tony@sdd.hp.com
  48.  
  49.      -- Tony Parkhurst
  50.  
  51.  
  52.  
  53. # This is a shell archive.  Remove anything before this line,
  54. # then unpack it by saving it in a file and typing "sh file".
  55. #
  56. # Wrapped by Tony Parkhurst <tony@hpsdl108> on Mon Apr 29 08:53:34 1991
  57. #
  58. # This archive contains:
  59. #    pclcomp.man    printer.note    pclcomp.1    pclcomp.c    
  60. #
  61. # Error checking via wc(1) will be performed.
  62. # Error checking via sum(1) will be performed.
  63.  
  64. LANG=""; export LANG
  65. PATH=/bin:/usr/bin:$PATH; export PATH
  66.  
  67. if sum -r </dev/null >/dev/null 2>&1
  68. then
  69.     sumopt='-r'
  70. else
  71.     sumopt=''
  72. fi
  73.  
  74. echo x - pclcomp.man
  75. cat >pclcomp.man <<'@EOF'
  76.  
  77.  
  78.  
  79.      PCLCOMP(1)                         PCLCOMP(1)
  80.  
  81.  
  82.  
  83.      NAME
  84.       pclcomp - Compress PCL graphics files.
  85.  
  86.      SYNOPSIS
  87.       pclcomp [ -0123drsvz ] [ -n num ] [ inputfile [ outputfile ]]
  88.  
  89.  
  90.      DESCRIPTION
  91.       Pclcomp compresses (or decompresses) HP-PCL (Printer Control
  92.       Language) graphics data.  The supported compression modes
  93.       are 0 (uncompressed), 1, 2 and 3.  Pclcomp will read files
  94.       using any of the modes 0 through 3, and will output using
  95.       the modes which will give the best compression.  This
  96.       compressed version of the file may be sent directly to a PCL
  97.       compatible printer, thus reducing I/O bandwidth.  Pictures
  98.       may also be saved in compressed form, reducing disk usage.
  99.       In addition, PCL "imaging" files for the PaintJet XL are
  100.       also supported.
  101.  
  102.       The options to pclcomp control the compression modes.  By
  103.       default, pclcomp will use all modes it knows about, but the
  104.       user may restrict which output modes it uses by specifying
  105.       them on the command line with the -0, -1, -2 and -3 options.
  106.       To decompress a file, simply specify -0 as the only mode to
  107.       use for output.  Mode 0 ( -0 ) should always be allowed
  108.       since modes 1, 2 and 3 cannot be guaranteed to be better
  109.       than mode 0 for all types of pictures.
  110.  
  111.       The -z option disables the zero "strip" feature.  Since most
  112.       printers do zero "filling", pclcomp, by default, "strips"
  113.       the trailing zeros of each row (or plane) of data.  Some
  114.       printers or programs may require that zero "stripping" be
  115.       disabled.
  116.  
  117.       By default, pclcomp expects the input raster width to be
  118.       2400 pixels (8" at 300 dpi), and if it is different (e.g.
  119.       PaintJet), then the raster width should be specified by the
  120.       Source Raster Width escape sequence <esc*r#S>. However, many
  121.       applications do not set the width and assume a default,
  122.       therefore, the user may use the -n option to pclcomp to
  123.       specify a new default raster width.  For PaintJet (8" at 180
  124.       dpi), the number should be 1440.  If the PCL file contains
  125.       the Source Raster Width escape sequence, it will override
  126.       this default.  If pclcomp thinks that more data is coming in
  127.       than the specified width, it will generate a warning, and
  128.       continue processing (and perhaps truncating) data.
  129.  
  130.       The -r option causes pclcomp to append an <esc>E reset
  131.       sequence to the end of the job.
  132.  
  133.       The DeskJet printer behaves slightly differently then a
  134.       LaserJet.  To account for this difference, specify the -d
  135.  
  136.  
  137.  
  138.                    - 1 -    Formatted:    April 22, 1991
  139.  
  140.  
  141.  
  142.  
  143.  
  144.  
  145.      PCLCOMP(1)                         PCLCOMP(1)
  146.  
  147.  
  148.  
  149.       option to pclcomp.
  150.  
  151.       Some applications send <esc>*rB and <esc>*rA sequences
  152.       between every row of graphics data.  The -s option to
  153.       pclcomp will "strip" all <esc>*rB sequences, and all
  154.       <esc>*rA sequences after the first occurrence of this
  155.       sequence. In addition, text and control characters residing
  156.       between <esc>*rA and <esc>*rB sequences will be discarded.
  157.       While this will work well for many jobs, it may have
  158.       problems on multi-page or complex jobs.
  159.  
  160.       The input and output files may be specified on the command
  161.       line.  This is useful for MS-DOS users because stdin and
  162.       stdout are not opened in binary mode.
  163.  
  164.       The -v option simply gives statistics to stderr about which
  165.       compression modes were used.
  166.  
  167.      EXAMPLES
  168.       To compress a PCL file for the PaintJet (A size page at 180 dpi), use:
  169.            pclcomp -01n 1440  < infile > outfile
  170.  
  171.       To compress a PCL file for LaserJet III, use:
  172.            pclcomp infile outfile
  173.  
  174.       To compress a PCL file for DeskJet, use:
  175.            pclcomp -d012 infile outfile
  176.  
  177.       To fully decompress a PCL file, use:
  178.            pclcomp -0z < infile > outfile
  179.  
  180.      WARNINGS
  181.       If the -z option is specified, and pclcomp thinks the source
  182.       raster width is larger than the actual data, the output may
  183.       grow.
  184.  
  185.       Be cautious when using the -s option.
  186.  
  187.       Do not use stdin or stdout on MS-DOS type systems.
  188.  
  189.      AUTHOR
  190.       Tony Parkhurst, Hewlett-Packard, San Diego Division
  191.       (tony@sdd.hp.com)
  192.  
  193.  
  194.  
  195.  
  196.  
  197.  
  198.  
  199.  
  200.  
  201.  
  202.  
  203.  
  204.                    - 2 -    Formatted:    April 22, 1991
  205.  
  206.  
  207.  
  208. @EOF
  209. set `sum $sumopt <pclcomp.man`; if test $1 -ne 21926
  210. then
  211.     echo ERROR: pclcomp.man checksum is $1 should be 21926
  212. fi
  213. set `wc -lwc <pclcomp.man`
  214. if test $1$2$3 != 1326264045
  215. then
  216.     echo ERROR: wc results of pclcomp.man are $* should be 132 626 4045
  217. fi
  218.  
  219. chmod 664 pclcomp.man
  220.  
  221. echo x - printer.note
  222. cat >printer.note <<'@EOF'
  223.  
  224.  
  225. Here is a list of printers and the compression modes they support:
  226.  
  227.  
  228.  
  229. Printer            Modes
  230. -------            -----
  231.  
  232. LaserJet        0
  233. LaserJet+        0
  234. LaserJet 500        0
  235. LaserJet 2000        0
  236. LaserJet II        0
  237. LaserJet IIP        0 1 2
  238. LaserJet III        0 1 2 3
  239.  
  240. DeskJet            0 1 2
  241. DeskJet+        0 1 2
  242. DeskJet 500        0 1 2 3
  243.  
  244. PaintJet        0 1
  245. PaintJet XL        0 1 2 3
  246.  
  247.  
  248. Mode 0 is uncompressed graphics data.
  249. @EOF
  250. set `sum $sumopt <printer.note`; if test $1 -ne 62183
  251. then
  252.     echo ERROR: printer.note checksum is $1 should be 62183
  253. fi
  254. set `wc -lwc <printer.note`
  255. if test $1$2$3 != 2669350
  256. then
  257.     echo ERROR: wc results of printer.note are $* should be 26 69 350
  258. fi
  259.  
  260. chmod 664 printer.note
  261.  
  262. echo x - pclcomp.1
  263. cat >pclcomp.1 <<'@EOF'
  264. .TH PCLCOMP 1
  265. .SH NAME
  266. pclcomp \- Compress PCL graphics files.
  267. .SH SYNOPSIS
  268. .B pclcomp
  269. [
  270. .B "-0123drsvz"
  271. ]
  272. [
  273. .B "-n"
  274. .I num
  275. ]
  276. [
  277. .I inputfile
  278. [
  279. .I outputfile
  280. ]]
  281. .br
  282. .SH DESCRIPTION
  283. .PP
  284. .B Pclcomp
  285. compresses (or decompresses) HP-PCL (Printer Control Language) graphics data.
  286. The supported compression modes are 0 (uncompressed), 1, 2 and 3.
  287. .B Pclcomp
  288. will read files using any of the modes 0 through 3, and will output using the
  289. modes which will give the best compression.  This compressed version of
  290. the file may be sent directly to a PCL compatible printer, thus reducing
  291. I/O bandwidth.  Pictures may also be saved in compressed form, reducing
  292. disk usage.
  293. In addition, PCL "imaging" files for the PaintJet XL are also supported.
  294. .PP
  295. The options to
  296. .B pclcomp
  297. control the compression modes.  By default,
  298. .B pclcomp
  299. will use all modes it knows about, but the user may restrict which output
  300. modes it uses by specifying them on the command line with the
  301. .B -0,
  302. .B -1,
  303. .B -2
  304. and
  305. .B -3
  306. options.  To decompress a file, simply specify
  307. .B -0
  308. as the only mode to use for output.  Mode 0 (
  309. .B -0
  310. ) should always be allowed since modes 1, 2 and 3 cannot be guaranteed to
  311. be better than mode 0 for all types of pictures.
  312. .PP
  313. The
  314. .B -z
  315. option disables the zero "strip" feature.  Since most printers do 
  316. zero "filling",
  317. .B pclcomp,
  318. by default, "strips" the trailing zeros of each row (or plane) of data.
  319. Some printers or programs may require that zero "stripping" be disabled.
  320. .PP
  321. By default,
  322. .B pclcomp
  323. expects the input raster width to be 2400 pixels (8" at 300 dpi), and if it is
  324. different (e.g. PaintJet), then the raster width should be specified by
  325. the Source Raster Width escape sequence
  326. .I <esc*r#S>.
  327. However, many applications do not set the width and assume a default, therefore,
  328. the user may use the 
  329. .B -n
  330. option to
  331. .B pclcomp
  332. to specify a new default raster width.  For PaintJet (8" at 180 dpi), the
  333. number should be 1440.  If the PCL file contains the Source Raster Width
  334. escape sequence, it will override this default.  If
  335. .B pclcomp
  336. thinks that more data is coming in than the specified width, it will
  337. generate a warning, and continue processing (and perhaps truncating) data.
  338. .PP
  339. The
  340. .B -r
  341. option causes
  342. .B pclcomp
  343. to append an
  344. .I "<esc>E"
  345. reset sequence to the end of the job.
  346. .PP
  347. The DeskJet printer behaves slightly differently then a LaserJet.  To account
  348. for this difference, specify the
  349. .B "-d"
  350. option to
  351. .B pclcomp.
  352. .PP
  353. Some applications send
  354. .I "<esc>*rB"
  355. and
  356. .I "<esc>*rA"
  357. sequences between every row of graphics data.  The 
  358. .B -s
  359. option to
  360. .B pclcomp
  361. will "strip" all 
  362. .I "<esc>*rB"
  363. sequences, and all
  364. .I "<esc>*rA"
  365. sequences after the first occurrence of this sequence.  
  366. In addition, text and control characters residing between
  367. .I "<esc>*rA"
  368. and
  369. .I "<esc>*rB"
  370. sequences will be discarded.
  371. While this will work
  372. well
  373. for many jobs, it may have problems on multi-page or complex jobs.
  374. .PP
  375. The input and output files may be specified on the command line.
  376. This is useful for MS-DOS users because
  377. .I stdin
  378. and
  379. .I stdout
  380. are not opened in binary mode.
  381. .PP
  382. The
  383. .B -v
  384. option simply gives statistics to
  385. .I stderr
  386. about which compression modes were used.
  387. .SH EXAMPLES
  388. .nf
  389. To compress a PCL file for the PaintJet (A size page at 180 dpi), use:
  390.     pclcomp -01n 1440  < infile > outfile
  391.  
  392. To compress a PCL file for LaserJet III, use:
  393.     pclcomp infile outfile
  394.  
  395. To compress a PCL file for DeskJet, use:
  396.     pclcomp -d012 infile outfile
  397.  
  398. To fully decompress a PCL file, use:
  399.     pclcomp -0z < infile > outfile
  400. .fi
  401. .SH WARNINGS
  402. .PP
  403. If the 
  404. .B -z
  405. option is specified, and
  406. .B pclcomp
  407. thinks the source raster width is
  408. larger than the actual data, the output may grow.
  409. .PP
  410. Be cautious when using the 
  411. .B -s
  412. option.
  413. .PP
  414. Do not use
  415. .I stdin
  416. or
  417. .I stdout
  418. on MS-DOS type systems.
  419. .SH AUTHOR
  420. Tony Parkhurst, Hewlett-Packard, San Diego Division  (tony@sdd.hp.com)
  421. @EOF
  422. set `sum $sumopt <pclcomp.1`; if test $1 -ne 31538
  423. then
  424.     echo ERROR: pclcomp.1 checksum is $1 should be 31538
  425. fi
  426. set `wc -lwc <pclcomp.1`
  427. if test $1$2$3 != 1576773844
  428. then
  429.     echo ERROR: wc results of pclcomp.1 are $* should be 157 677 3844
  430. fi
  431.  
  432. chmod 444 pclcomp.1
  433.  
  434. echo x - pclcomp.c
  435. cat >pclcomp.c <<'@EOF'
  436. /*
  437. **  Pclcomp -- PCL compression filter.
  438. **
  439. **  If you have any problems or errors to report, please send them to me:
  440. **
  441. **  Tony Parkhurst  
  442. ** 
  443. **  Email address:  tony@sdd.hp.com    -or-   hp-sdd!tony
  444. **
  445. **  Please send a copy of the graphic file that is a problem, and the version
  446. **  of pclcomp you are using.
  447. **
  448. **  All suggestions and requests are welcome.
  449. */
  450.  
  451. /*
  452.  ***************************************************************************
  453.  *
  454.  * $Source: /disc/44/cgtriton/tony/filters/pclcomp/RCS/pclcomp.c,v $ 
  455.  * $Date: 91/04/23 15:48:05 $ 
  456.  * $Revision: 1.27 $
  457.  *
  458.  * Description:    Compresses pcl graphics files.
  459.  *
  460.  * Author:       Tony Parkhurst
  461.  * Created:      890427
  462.  * Language:     C
  463.  *
  464.  * (c) Copyright 1989, Hewlett-Packard Company, all rights reserved.
  465.  *
  466.  ***************************************************************************
  467.  */
  468.  
  469.  
  470. /*
  471.  ***************************************************************************
  472.  *
  473.  * $Log:    pclcomp.c,v $
  474.  * Revision 1.27  91/04/23  15:48:05  15:48:05  tony (Tony Parkhurst)
  475.  * Added handling of plus_sign in value fields.
  476.  * 
  477.  * Revision 1.26  91/04/23  09:47:11  09:47:11  tony (Tony Parkhurst)
  478.  * Pass thru unknown modes.
  479.  * 
  480.  * Revision 1.25  91/04/18  11:09:27  11:09:27  tony (Tony Parkhurst)
  481.  * Added parse for fractions in values (i.e. <esc>(s16.67H)
  482.  * 
  483.  * Revision 1.24  91/04/10  14:16:30  14:16:30  tony (Tony Parkhurst)
  484.  * strips text and control codes between <esc>*rA and <esc>*rB w/ -s option
  485.  * 
  486.  * Revision 1.23  91/04/05  14:53:25  14:53:25  tony (Tony Parkhurst)
  487.  * Added fixed for deskjet
  488.  * Also added a stripping feature.
  489.  * 
  490.  * Revision 1.22  91/04/05  08:48:53  08:48:53  tony (Tony Parkhurst)
  491.  * Added some error checkin on output for MS-DOS users.
  492.  * 
  493.  * Revision 1.21  91/04/04  12:53:32  12:53:32  tony (Tony Parkhurst)
  494.  * Replaced parser.
  495.  *    Now handles combined escape sequences.
  496.  *    Now handles downloads.
  497.  *    Now combines mode changes with data.
  498.  * 
  499.  * Revision 1.20  91/04/04  08:02:12  08:02:12  tony (Tony Parkhurst)
  500.  * Removed some test code.
  501.  * 
  502.  * Revision 1.19  91/03/25  14:38:48  14:38:48  tony (Tony Parkhurst)
  503.  * Changed defaults.
  504.  * 
  505.  * Revision 1.18  91/03/25  14:31:22  14:31:22  tony (Tony Parkhurst)
  506.  * Re-worked memory allocation stuff for funky input files.
  507.  * 
  508.  * Revision 1.17  91/03/25  13:50:19  13:50:19  tony (Tony Parkhurst)
  509.  * Use command line args for file w/o -i or -o.
  510.  * 
  511.  * Revision 1.16  91/03/04  14:23:15  14:23:15  tony (Tony Parkhurst)
  512.  * Fixed to allow ONLY mode 3 if the user really wants it.
  513.  * 
  514.  * Revision 1.15  91/03/04  14:08:23  14:08:23  tony (Tony Parkhurst)
  515.  * Added an exit(0) at the end of main.
  516.  * fixed up some zerostrip stuff.
  517.  * made mode 3 the highest priority mode.
  518.  * 
  519.  * Revision 1.14  91/02/20  13:57:27  13:57:27  tony (Tony Parkhurst)
  520.  * Changed priority a bit.
  521.  * Added some zerostripping for mode 2.
  522.  * 
  523.  * Revision 1.13  91/02/06  15:31:00  15:31:00  tony (Tony Parkhurst)
  524.  * oops.
  525.  * 
  526.  * Revision 1.12  91/02/06  14:41:28  14:41:28  tony (Tony Parkhurst)
  527.  * fixed usage message
  528.  * 
  529.  * Revision 1.11  91/02/06  14:38:10  14:38:10  tony (Tony Parkhurst)
  530.  * Added file input and output for MS-DOS.
  531.  * 
  532.  * Revision 1.10  91/02/05  17:49:23  17:49:23  tony (Tony Parkhurst)
  533.  * Fixed problem with zero stripped input.
  534.  * 
  535.  * Revision 1.9  91/02/05  16:11:39  16:11:39  tony (Tony Parkhurst)
  536.  * Removed delay code and bitfield stuff.
  537.  * 
  538.  * Revision 1.8  91/02/05  11:04:53  11:04:53  tony (Tony Parkhurst)
  539.  * Added io delay stuff for triton.
  540.  * 
  541.  * Revision 1.7  91/02/05  10:28:32  10:28:32  tony (Tony Parkhurst)
  542.  * Fix for someone specifing ONLY mode 3.
  543.  * 
  544.  * Revision 1.6  91/01/29  14:13:09  14:13:09  tony (Tony Parkhurst)
  545.  * Updated some comments.
  546.  * 
  547.  * Revision 1.5  91/01/29  13:26:24  13:26:24  tony (Tony Parkhurst)
  548.  * Cleaned up, revamped a bit.
  549.  * 
  550.  * Revision 1.4  89/11/09  15:59:16  15:59:16  tony (Tony Parkhurst)
  551.  * Fix for esc * r U coming after esc * r A.
  552.  * 
  553.  * Revision 1.3  89/10/24  11:31:12  11:31:12  tony (Tony Parkhurst)
  554.  * Added parsing of <esc>*rC
  555.  * 
  556.  * Revision 1.2  89/10/13  09:56:46  09:56:46  tony (Tony Parkhurst)
  557.  * Completely revamped by Greg G.
  558.  * 
  559.  * Revision 1.1  89/06/15  13:57:46  13:57:46  tony (Tony Parkhurst)
  560.  * Initial revision
  561.  * 
  562.  *
  563.  ***************************************************************************
  564.  */
  565.  
  566. static char *rcs_id="$Header: pclcomp.c,v 1.27 91/04/23 15:48:05 tony Exp $";
  567.  
  568. static char *rev_id="$Revision: 1.27 $";
  569.  
  570.  
  571. /* This program takes a PCL graphics file and will try and
  572.  * optimize the compression.
  573.  */
  574.  
  575. /*
  576.  *   This program was first a filter by Dean to compress pcl graphics.
  577.  *
  578.  *   This program now will do optimal compression using modes 0,1,2 and 3
  579.  *
  580.  *   Also, this program will take compressed input.
  581.  *
  582.  *   Input and output formats are standard pcl.
  583.  *
  584.  *   Imaging files will be compressed too.
  585.  *
  586.  *   pclcomp does not take advantage of Y-Offset for blank areas.  
  587.  *   This is because Y-Offset creates white areas, but we don't do enough
  588.  *   parsing to determine what value "white" has.  An application that
  589.  *   can assume white values could make use of this sequence.
  590.  *
  591.  *   pclcomp does not do any of the block compression modes (4-8).
  592.  *
  593.  *   There are a few obvious inefficiencies that I will fix later.
  594.  *
  595.  *   Speaking of mode 3, there is a possible problem because each of the
  596.  *   output row storage areas are 2* size of what mode 0 would be.  This
  597.  *   is clearly sufficient for modes 1 and 2, but it may not be for mode
  598.  *   3.  But I cannot think of a case in Mode 3 where this would be a problem.
  599.  *
  600.  *   An additional enhancement would be to compare all the planes in a
  601.  *   multi-plane file (color) and if nothing changed, using mode 3, just
  602.  *   output a single <esc>*b0W.
  603.  */
  604.  
  605. /*
  606.  *   Usage:  pclcomp [-v] [-0] [-1] [-2] [-3] [-z] [-n###] < infile > outfile
  607.  *
  608.  *   Pclcomp will do graphics compression based on compression modes 0, 1, 2
  609.  *   and 3.  (Mode 0 is uncompressed).  Pclcomp will accept all modes, and
  610.  *   will attempt to optimize by selecting the best output mode for each
  611.  *   row (or plane) of data.  By default, pclcomp will use all 4 modes, but
  612.  *   the user may restrict which output modes to use with the -0123 options.
  613.  *   For example, to use pclcomp for output to a PaintJet which only knows
  614.  *   modes 0 and 1 (the XL also understands modes 2 and 3), one would use:
  615.  *
  616.  *      pclcomp -01 < infile > outfile
  617.  *
  618.  *   Note:  Mode 0 should always be allowed.  None of the other modes is
  619.  *   guaranteed to be better than mode 0 in all cases.
  620.  *
  621.  *   The 'v' option tells the number of rows (planes) of data input and output
  622.  *   in the different modes (to stderr).
  623.  *
  624.  *   The 'z' option is useful for PaintJet files using only modes 0 and 1, it
  625.  *   does zero "stripping" as the PaintJet will do zero "filling".
  626.  *   Note: 'z' now means do NOT zerostrip.
  627.  *
  628.  *   The 'n' option is to change the default number of pixels in a picture.
  629.  *   The proper way to set the pixel width is with the source raster width
  630.  *   sequence <esc*r#S>, but soo many applications just assume the default,
  631.  *   which is different on different printers, so I am providing this
  632.  *   command line option to set a new default.  One could also change the
  633.  *   DEFAULT constant below (make sure it is a multiple of 8).  Currently
  634.  *   it is set to 8" at 180 dpi (1440), but for 300 dpi, set it to 2400.
  635.  *
  636.  *   default is now 2400 (for 300dpi ala LaserJet).
  637.  */
  638.  
  639. #include <stdio.h>
  640. #include <string.h>
  641.  
  642.  
  643. /* This flag is for code that uses bitfields for 68000 systems */
  644. #define BITFIELDS 0
  645.  
  646. #define Get_Character() getchar()
  647.  
  648. #define MIN(x,y)    ( ((x) < (y)) ? (x) : (y) )
  649.  
  650. #define TRUE 1
  651. #define FALSE 0
  652.  
  653. #define ESC    27
  654.  
  655. #define DEFAULT 2400        /* default width in pixels (multiple of 8) */
  656.  
  657. #define MAXMODES  4
  658. #define MAXPLANES 8
  659. #define MAXBYTES 60000        /* now mostly meaningless, just a big number */
  660.  
  661. unsigned char    *seed_row[MAXPLANES];
  662. unsigned char    *new_row;
  663. unsigned char    *out_row[MAXMODES];
  664. unsigned int    out_size[MAXMODES];
  665.  
  666. char    memflag = FALSE;    /* set when memory has been allocated */
  667.  
  668.  
  669. char    mode0=FALSE,
  670.     mode1=FALSE,
  671.     mode2=FALSE,
  672.     mode3=FALSE;
  673.  
  674. unsigned char    num_planes=1;
  675. unsigned char    curr_plane=0;
  676.  
  677. char    imaging = FALSE;        /* not imaging, so no lockout */
  678.  
  679. char    verbose = FALSE;
  680.  
  681. unsigned char    inmode = 0;        /* input compression mode */
  682. unsigned char    outmode = 0;        /* output compression mode */
  683.  
  684. unsigned int    rasterwidth=DEFAULT/8;    /* width of picture, in bytes */
  685. unsigned int    rpix = DEFAULT;        /* width of picture, in pixels */
  686.  
  687. unsigned char    invert=FALSE;        /* invert the data (obsolete) */
  688.  
  689. unsigned char    zerostrip= TRUE;    /* strip trailing zeros */
  690.  
  691. unsigned int    inuse[4]={0,0,0,0}, outuse[4] = {0,0,0,0};
  692.  
  693. char    widthwarning = FALSE;    /* for trucation warning */
  694. char    firstrow = TRUE;    /* to prevent mode 3 from being first */
  695.  
  696.  
  697. struct {            /* this will hold the data for the  */
  698.      unsigned char model;    /* configuring of image processing   */
  699.      unsigned char pix_mode;
  700.      unsigned char inx_bits;
  701.      unsigned char red;
  702.      unsigned char green;
  703.      unsigned char blue;
  704.      short wr;
  705.      short wg;
  706.      short wb;
  707.      short br;
  708.      short bg;
  709.      short bb; 
  710. } imdata;
  711.  
  712. extern    unsigned char *malloc();
  713.  
  714. char    *filein = NULL, *fileout = NULL;
  715.  
  716. /*
  717. **  These variables are for the new parser.
  718. **  The new parser handles more sequences, and also deals with combined
  719. **  escape sequences better.
  720. */
  721.  
  722. int    parameter;
  723. int    group_char;
  724. int    terminator;
  725. int    old_terminator;
  726. int    value;
  727. int    frac;
  728. int    scanf_count;
  729. char    in_sequence = FALSE;
  730. char    pass_seq;
  731. char    plus_sign;        /* for relative values */
  732.  
  733.  
  734. /* dummy buffer */
  735. char buf[BUFSIZ];
  736.  
  737. /*
  738. **  If the printer is a DeskJet, then we must handle <esc>*rB differently
  739. **  Option '-d' will turn on this mode.
  740. */
  741.  
  742. char    deskjet = FALSE;
  743.  
  744. /*
  745. **  Many drivers it seems put <esc>*rB<esc>*rA between each and every row
  746. **  of data.  This defeats compression mode 3 on a DeskJet, and also
  747. **  makes the PaintJet (not XL) quite slow.  This next flag "-s" on the
  748. **  command line, will attempt to do a reasonable job of stripping
  749. **  out the excess commands.
  750. **
  751. **  The in_graphics flag will be used to strip unwanted control chars from
  752. **  the file.
  753. */
  754.  
  755. char    strip_seq = FALSE;
  756. char    in_graphics = FALSE;
  757.  
  758.  
  759. /*
  760. **  Just for certain special cases, it would be nice to append an <esc>E reset
  761. **  to the end of the job.  Specify with "-r".
  762. */
  763.  
  764. char    reset_seq = FALSE;
  765.  
  766.  
  767. char    *progname;
  768.  
  769.  
  770. /*
  771. **  Main program.
  772. */
  773.  
  774. main(argc, argv)
  775. int argc;
  776. char *argv[];
  777. {
  778.   int    c,j;
  779.   extern char *optarg;
  780.   extern int   optind;
  781.  
  782.     progname = argv[0];
  783.  
  784.     /* parse up the args here */
  785.  
  786.   while ((c = getopt(argc, argv, "0123drsvzn:i:o:s")) != EOF )
  787.     switch(c){
  788.     case '0':
  789.             mode0++;
  790.             break;
  791.     case '1':
  792.             mode1++;
  793.             break;
  794.     case '2':
  795.             mode2++;
  796.             break;
  797.     case '3':
  798.             mode3++;
  799.             break;
  800.     case 'd':
  801.             deskjet++;
  802.             break;
  803.     case 'r':
  804.             reset_seq++;
  805.             break;
  806.     case 's':
  807.             strip_seq++;
  808.             break;
  809.     case 'v':
  810.             verbose++;
  811.             break;
  812.     case 'z':
  813.             zerostrip = FALSE;
  814.             break;
  815.     case 'n':
  816.             rpix = atoi(optarg);    /* new default */
  817.             rasterwidth = (rpix + 7) / 8;    /* round up */
  818.             break;
  819.  
  820.     case 'i':
  821.             filein = optarg;
  822.             break;
  823.     case 'o':
  824.             fileout = optarg;
  825.             break;
  826.  
  827.     case '?':
  828.     default:
  829.             fprintf(stderr, "Usage: %s [-0] [-1] [-2] [-3] [-n###] [-z] [-v] [infile [outfile]]\n",
  830.                 argv[0]);
  831.             exit(-1);
  832.     };
  833.  
  834.     if ( verbose )
  835.     {
  836.         fprintf(stderr, "%s: %s\n", argv[0], rev_id);
  837.     }
  838.  
  839.  
  840.   if ( ! ( mode0 || mode1 || mode2 || mode3) )    /* any modes on? */
  841.     mode0 = mode1 = mode2 = mode3 = TRUE;    /* all  modes by default */
  842.  
  843.     /*
  844.     **  Check to see if any file args were given on the command line.
  845.     **  Ones that were not preceded by a "-i" or "-o".
  846.     **  Just making it easier for the MS-DOS folks that already have
  847.     **  enough to put up with :-)
  848.     */
  849.  
  850.     if ( filein == NULL && optind < argc && argv[optind] != NULL )
  851.         filein = argv[optind++];
  852.  
  853.     if ( fileout == NULL && optind < argc && argv[optind] != NULL )
  854.         fileout = argv[optind++];
  855.  
  856.     /*
  857.     /*
  858.     **  Now open files for stdin and stdout if provided.  This
  859.     **  functionality is for MS-DOS which needs files to be opened
  860.     **  in "binary" mode.  That is, input and output files MUST be
  861.     **  given on the command line.  Unless there is a way to turn on
  862.     **  binary mode for an already open file, but I don't know.
  863.     */
  864.  
  865.     if ( filein != NULL )        /* new input file */
  866.  
  867.         if ( freopen( filein, "rb", stdin ) == NULL )
  868.         {
  869.             fprintf(stderr,"Unable to open %s for input.\n",filein);
  870.             exit(-42);
  871.         }
  872.  
  873.     if ( fileout != NULL )        /* new output file */
  874.  
  875.         if ( freopen( fileout, "wb", stdout ) == NULL )
  876.         {
  877.             fprintf(stderr, "Unable to open %s for output.\n",
  878.                 fileout);
  879.             exit(-43);
  880.         }
  881.  
  882.  
  883.     /*
  884.     **  This is the pcl input parsing loop.
  885.     */
  886.  
  887.     while( ( c = getchar() ) != EOF )
  888.     {
  889.  
  890.         /*  Ignore all chars until an escape char  */
  891.  
  892.         /*
  893.         **  If we are in graphics, toss it if strip_seq is set.
  894.         */
  895.  
  896.         if ( c != ESC )
  897.         {
  898.             if ( !strip_seq || !in_graphics )
  899.                 putchar(c);    /* pass it thru */
  900.  
  901.             continue;    /* pop to the top of the loop */
  902.         }
  903.  
  904.         /*
  905.         **  Now we have an escape sequence, get the parameter char.
  906.         */
  907.  
  908.         parameter = getchar();
  909.  
  910.         if ( parameter == EOF )        /* oops */
  911.         {
  912.             putchar ( ESC );
  913.             fprintf(stderr, "Warning:  File ended with <esc>.\n");
  914.             break;            /* unexpected end of input */
  915.         }
  916.  
  917.         /*
  918.         **  Now check to see if it is a two character sequence.
  919.         */
  920.  
  921.         if ( parameter >= '0' && parameter <= '~' )
  922.         {
  923.             putchar ( ESC );
  924.             putchar ( parameter );        /* pass it thru */
  925.  
  926.             /*
  927.             **  If the second character is an E, then we
  928.             **  and the printer do a reset.
  929.             */
  930.  
  931.             if ( parameter == 'E' )
  932.             {
  933.                 free_mem();
  934.                 curr_plane = 0;
  935.                 num_planes = 1;
  936.                 imaging = FALSE;
  937.                 inmode = 0;
  938.                 outmode = 0;
  939.                 in_graphics = FALSE;
  940.  
  941.                 /* can't do this if user gave value with -n.
  942.                 rasterwidth = DEFAULT/8;
  943.                 rpix = DEFAULT;
  944.                 */
  945.             }
  946.  
  947.             continue;        /* return to the top */
  948.         }
  949.  
  950.         /*
  951.         **  Now check to make sure that the parameter character is
  952.         **  within range.
  953.         */
  954.  
  955.         if ( parameter < '!' || parameter > '/' )
  956.         {
  957.             putchar ( ESC );
  958.             putchar ( parameter );
  959.  
  960.             fprintf(stderr, "Warning:  Invalid escape sequence.\n");
  961.  
  962.             continue;
  963.         }
  964.  
  965.         /*
  966.         **  We are only interested in certain parameters, so pass
  967.         **  the rest of the sequences.
  968.         */
  969.  
  970.         /*
  971.         **  For the moment, we are only interested in '*' (graphics)
  972.         **  '(' and ')' (downloads).  Although we do not do anything
  973.         **  with downloads, we need to pass the binary data thru
  974.         **  untouched.
  975.         **  Also, '&' is handled too.
  976.         */
  977.  
  978.         if ( parameter != '*' && parameter != '(' 
  979.             && parameter != ')' && parameter != '&' )
  980.         {
  981.             putchar ( ESC );
  982.             putchar ( parameter );
  983.             Flush_To_Term();        /* flush rest of seq. */
  984.             continue;
  985.         }
  986.  
  987.  
  988.         /*
  989.         **  Parameter character is in range, look for a valid group char
  990.         */
  991.  
  992.         group_char = getchar();
  993.  
  994.         if ( group_char == EOF )    /* oops, ran out of input */
  995.         {
  996.             putchar ( ESC );
  997.             putchar ( parameter );
  998.  
  999.             fprintf(stderr, "Warning:  Incomplete escape sequence.\n");
  1000.             break;
  1001.         }
  1002.  
  1003.         /*
  1004.         **  See if in proper range.  If it isn't, it is not an error
  1005.         **  because the group character is optional for some sequences.
  1006.         **  For the moment, we are not interested in those sequences,
  1007.         **  so pass them thru.
  1008.         */
  1009.  
  1010.         if ( group_char < '`' || group_char > '~' )
  1011.         {
  1012.             putchar ( ESC );
  1013.             putchar ( parameter );
  1014.             putchar ( group_char );
  1015.             if ( group_char < '@' || group_char > '^' )
  1016.                 Flush_To_Term();    /* pass rest of seq. */
  1017.             continue;
  1018.         }
  1019.  
  1020.         /*
  1021.         **  Now we have a valid group character, decide if we want
  1022.         **  to deal with this escape sequence.
  1023.         **
  1024.         **  Sequences we want do deal with include:
  1025.         **
  1026.         **    <esc>*r    ** graphics
  1027.         **    <esc>*b    ** graphics
  1028.         **    <esc>*v    ** graphics
  1029.         **
  1030.         **  Sequences we must pass thru binary data:
  1031.         **
  1032.         **    <esc>*c    ** pattern
  1033.         **    <esc>*t    ** obsolete
  1034.         **    <esc>(f    ** download char set
  1035.         **    <esc>(s    ** download char
  1036.         **    <esc>)s    ** download font
  1037.         **    <esc>&a    ** logical page
  1038.         **    <esc>&l    ** obsolete
  1039.         **
  1040.         */
  1041.  
  1042.         if (  ( parameter == '*'
  1043.             && group_char != 'r' && group_char != 'b' 
  1044.             && group_char != 'v' && group_char != 'c' )
  1045.            || ( parameter == '&'
  1046.             && group_char != 'a' && group_char != 'l' )
  1047.            || ( parameter == '(' 
  1048.             && group_char != 'f' && group_char != 's' )
  1049.            || ( parameter == ')' && group_char != 's' ) )
  1050.         {
  1051.             /*
  1052.             **  Definately not interested in the sequence.
  1053.             */
  1054.  
  1055.             putchar ( ESC );
  1056.             putchar ( parameter );
  1057.             putchar ( group_char );
  1058.             Flush_To_Term();
  1059.             continue;
  1060.         }
  1061.  
  1062.         /*
  1063.         **  Now set up a pass thru flag so we can ignore the entire
  1064.         **  sequences of some of these.
  1065.         */
  1066.  
  1067.         if ( parameter != '*' )
  1068.             pass_seq = TRUE;
  1069.         else if ( group_char == 'c' || group_char == 't' )
  1070.             pass_seq = TRUE;
  1071.         else
  1072.             pass_seq = FALSE;
  1073.  
  1074.  
  1075.         /*
  1076.         **  Now we have a sequence that we are definately interested in.
  1077.         **
  1078.         **  Get the value field and terminator, and loop until final
  1079.         **  terminator is found.
  1080.         */
  1081.  
  1082.         do
  1083.         {
  1084.             /* first see if the value has a plus sign */
  1085.  
  1086.             scanf_count = scanf(" + %d", &value );
  1087.  
  1088.             if ( scanf_count == 1 )
  1089.  
  1090.                 plus_sign = TRUE;
  1091.             else
  1092.             {
  1093.                 plus_sign = FALSE;
  1094.  
  1095.                 scanf_count = scanf(" %d", &value );
  1096.  
  1097.                 if ( scanf_count == 0 )
  1098.                     value = 0;        /* by default */
  1099.             }
  1100.  
  1101.             terminator = getchar();
  1102.  
  1103.             /*
  1104.             ** check to see if a fractional parameter was passed.
  1105.             */
  1106.  
  1107.             frac = 0;    /* in case no fraction */
  1108.  
  1109.             if ( terminator == '.' )
  1110.             {
  1111.                 /*
  1112.                 **  Need to get fractional part.
  1113.                 */
  1114.  
  1115.                 if ( scanf("%d", &frac) != 1 )
  1116.                 {
  1117.                     frac = 0;    /* no frac? */
  1118.                 }
  1119.  
  1120.                 /*
  1121.                 **  Now get the real terminator.
  1122.                 */
  1123.  
  1124.                 terminator = getchar();
  1125.             }
  1126.  
  1127.  
  1128.             if ( terminator == EOF )    /* barf */
  1129.             {
  1130.                 fprintf(stderr, "Warning:  Incomplete sequence at end of file.\n");
  1131.                 break;
  1132.             }
  1133.  
  1134.             /*
  1135.             **  If the pass_seq flag is set, then just pass
  1136.             **  it thru to stdout until a 'W' is found.
  1137.             */
  1138.  
  1139.             if ( pass_seq )
  1140.             {
  1141.                 /*
  1142.                 **  If not in sequence, then we output esc
  1143.                 **  otherwise, output the saved terminator.
  1144.                 */
  1145.  
  1146.                 if ( !in_sequence )
  1147.                 {
  1148.                     in_sequence = TRUE;
  1149.                     putchar ( ESC );
  1150.                     putchar ( parameter );
  1151.                     putchar ( group_char );
  1152.                 } else
  1153.                 {
  1154.                     putchar ( old_terminator );
  1155.                 }
  1156.  
  1157.                 /* now pass the value */
  1158.  
  1159.                 if ( plus_sign )
  1160.                     putchar('+');
  1161.  
  1162.                 if ( scanf_count )    /* there was a value */
  1163.                     printf("%0d", value);
  1164.                 
  1165.                 /* need to output the fractional part */
  1166.  
  1167.                 if ( frac )
  1168.                     printf(".%0d", frac);
  1169.  
  1170.                 /*
  1171.                 **  We save the terminator, because we may
  1172.                 **  need to change it to upper case.
  1173.                 */
  1174.  
  1175.                 old_terminator = terminator;
  1176.  
  1177.                 /* if binary data, pass it thru */
  1178.  
  1179.                 if ( terminator == 'W' )    /* aha */
  1180.                 {
  1181.                     putchar ( terminator );
  1182.                     in_sequence = FALSE;    /* terminates */
  1183.                     Flush_Bytes ( value );    /* pass data */
  1184.                 }
  1185.  
  1186.                 continue;
  1187.             }
  1188.  
  1189.             /*
  1190.             **  Ok, this is a sequence we want to pay attention to.
  1191.             **
  1192.             **  Do_Graphics returns TRUE if we need to pass seq.
  1193.             **
  1194.             **  Note:  Do_Graphics modifies the parser vars such
  1195.             **         as in_sequence.  This is because it may
  1196.             **         have to output stuff directly.
  1197.             */
  1198.  
  1199.             if ( Do_Graphics ( group_char, value, terminator ) )
  1200.             {
  1201.                 /*
  1202.                 **  If not in sequence, then we output esc
  1203.                 **  otherwise, output the saved terminator.
  1204.                 */
  1205.  
  1206.                 if ( !in_sequence )
  1207.                 {
  1208.                     in_sequence = TRUE;
  1209.                     putchar ( ESC );
  1210.                     putchar ( parameter );
  1211.                     putchar ( group_char );
  1212.                 } else
  1213.                 {
  1214.                     putchar ( old_terminator );
  1215.                 }
  1216.  
  1217.                 /* now pass the value */
  1218.  
  1219.                 if ( plus_sign )
  1220.                     putchar('+');
  1221.  
  1222.                 if ( scanf_count )    /* there was a value */
  1223.                     printf("%0d", value);
  1224.  
  1225.                 /* need to output the fractional part */
  1226.  
  1227.                 if ( frac )
  1228.                     printf(".%0d", frac);
  1229.  
  1230.                 /*
  1231.                 **  We save the terminator, because we may
  1232.                 **  need to change it to upper case.
  1233.                 */
  1234.  
  1235.                 old_terminator = terminator;
  1236.             }
  1237.  
  1238.         } while ( terminator >= '`' && terminator <= '~' );
  1239.  
  1240.         /*
  1241.         ** The oppsite test (above) may be more appropriate.  That is, 
  1242.         ** !(terminator >= '@' && terminator <= '^').
  1243.         */
  1244.         
  1245.         /*
  1246.         **  If we were in a sequence, then we must terminate it.
  1247.         **  If it was lower case, then it must be uppered.
  1248.         */
  1249.  
  1250.         if ( in_sequence )
  1251.         {
  1252.             putchar ( terminator & 0xdf );        /* a ==> A */
  1253.             in_sequence = FALSE;
  1254.         }
  1255.     }
  1256.     
  1257.  
  1258.     /*
  1259.     **  If the user wants a reset, give him one.
  1260.     */
  1261.  
  1262.     if ( reset_seq )
  1263.     {
  1264.         putchar ( ESC );
  1265.         putchar ( 'E' );
  1266.     }
  1267.  
  1268.  
  1269.     /*
  1270.     **  Finished up, so print stats and close output file.
  1271.     */
  1272.  
  1273.     fclose(stdout);
  1274.  
  1275.  
  1276.     if ( verbose )
  1277.     {
  1278.         for(j = 0; j < 4; j++)
  1279.             fprintf(stderr,"Rows in mode %1d: %d\n", j, inuse[j]);
  1280.         for(j = 0; j < 4; j++)
  1281.             fprintf(stderr,"Rows out mode %1d: %d\n", j, outuse[j]);
  1282.     }
  1283.  
  1284.     exit(0);
  1285. }
  1286.  
  1287.  
  1288. /*
  1289. **  Do_Graphics() takes the graphics escape sequence and performs the
  1290. **  necessary functions.
  1291. **  TRUE is returned if the escape sequence needs to be passed to the output.
  1292. */
  1293.  
  1294. int    Do_Graphics( group, num, terminator )
  1295. int    group, num, terminator;
  1296. {
  1297.  
  1298.     /*  first look at vW  */
  1299.  
  1300.     if ( group == 'v' )
  1301.  
  1302.         if ( terminator != 'W' )
  1303.             
  1304.             return ( TRUE );    /* pass it thru */
  1305.         else
  1306.         {
  1307.             if ( !in_sequence )
  1308.             {
  1309.                 putchar ( ESC );
  1310.                 putchar ( parameter );
  1311.                 putchar ( group );
  1312.             } else
  1313.                 putchar ( old_terminator );
  1314.  
  1315.             in_sequence = FALSE;        /* terminating */
  1316.  
  1317.             printf("%0d", num);
  1318.             putchar ( terminator );
  1319.  
  1320.             free_mem();    /* reset memory */
  1321.  
  1322.             imaging++;
  1323.  
  1324.             fread(&imdata, MIN(num, 18), 1, stdin);
  1325.             fwrite(&imdata, MIN(num, 18), 1, stdout);
  1326.  
  1327.             num -= MIN(num, 18);
  1328.  
  1329.             /* copy rest of unknown data */
  1330.  
  1331.             if ( num > 0 )
  1332.                 Flush_Bytes(num);
  1333.  
  1334.  
  1335.             switch(imdata.pix_mode){
  1336.                 case 0x00:
  1337.                     rasterwidth = (rpix + 7)/8;
  1338.                     num_planes = imdata.inx_bits;
  1339.                     break;
  1340.                 case 0x01:
  1341.                     rasterwidth = rpix*imdata.inx_bits/8;
  1342.                     break;
  1343.                 case 0x02:
  1344.                     rasterwidth = (rpix + 7)/8;
  1345.                     num_planes =imdata.red + imdata.green +
  1346.                                 imdata.blue;
  1347.                     break;
  1348.                 case 0x03:
  1349.                     rasterwidth = (imdata.red +
  1350.                                    imdata.green +
  1351.                                    imdata.blue)*rpix/8;
  1352.                     break;
  1353.             }
  1354.  
  1355.             return ( FALSE );
  1356.         }
  1357.  
  1358.     /*
  1359.     **  Now deal with <esc>*r stuff
  1360.     */
  1361.  
  1362.     if ( group == 'r' )
  1363.     {
  1364.         switch ( terminator )
  1365.         {
  1366.             case 'A':
  1367.             case 'a':
  1368.  
  1369.                 /* in graphics mode, may do stripping */
  1370.                 in_graphics = TRUE;
  1371.  
  1372.                 /* if user wants to strip redundant seq */
  1373.                 if ( strip_seq && memflag )
  1374.                     return( FALSE );
  1375.  
  1376.                 curr_plane=0;
  1377.                 zero_seeds();    /* may allocate mem */
  1378.                 break;
  1379.  
  1380.             case 'C':
  1381.             case 'c':
  1382.  
  1383.                 /* not in graphics disable code strip */
  1384.  
  1385.                 in_graphics = FALSE;
  1386.  
  1387.                 if ( strip_seq )
  1388.                     return( FALSE );
  1389.  
  1390.                 inmode = 0;
  1391.                 outmode = 0;
  1392.  
  1393.                 free_mem();
  1394.                 curr_plane=0;
  1395.                 break;
  1396.  
  1397.             case 'B':
  1398.             case 'b':
  1399.  
  1400.                 /* not in graphics disable code strip */
  1401.  
  1402.                 in_graphics = FALSE;
  1403.  
  1404.                 if ( strip_seq )
  1405.                     return( FALSE );
  1406.  
  1407.                 if ( deskjet )    /* B resets modes on DJ */
  1408.                 {
  1409.                     inmode = 0;
  1410.                     outmode = 0;
  1411.                 }
  1412.                 free_mem();
  1413.                 curr_plane=0;
  1414.                 break;
  1415.  
  1416.             case 'S':
  1417.             case 's':
  1418.  
  1419.                 /* free mem in case widths changed */
  1420.                 free_mem();
  1421.  
  1422.                 rpix = num;
  1423.  
  1424.                 if (imaging){
  1425.                     switch(imdata.pix_mode)
  1426.                     {
  1427.                         case 0x00:
  1428.                             rasterwidth=(rpix+7)/8;
  1429.                             break;
  1430.                         case 0x01:
  1431.                             rasterwidth = 
  1432.                              rpix*imdata.inx_bits/8;
  1433.                             break;
  1434.                         case 0x02:
  1435.                             rasterwidth=(rpix+7)/8;
  1436.                             break;
  1437.                         case 0x03:
  1438.                             rasterwidth = 
  1439.                               (imdata.red 
  1440.                               + imdata.green
  1441.                               + imdata.blue)*rpix/8;
  1442.                             break;
  1443.                     }
  1444.                 } else
  1445.                     rasterwidth = (num + 7) / 8;
  1446.                 break;
  1447.  
  1448.             case 'T':
  1449.             case 't':
  1450.                 break;
  1451.  
  1452.             case 'U':
  1453.             case 'u':
  1454.                 curr_plane=0;
  1455.                 free_mem();    /* if ESC*rA came first */
  1456.                 num_planes = num;
  1457.                 imaging = FALSE;    /* goes off */
  1458.                 break;
  1459.  
  1460.             default:
  1461.                 break;
  1462.         }
  1463.  
  1464.         return ( TRUE );        /* pass sequence on */
  1465.  
  1466.     }    /* group r */
  1467.  
  1468.     /*
  1469.     **  Last and final group 'b'.  All the graphics data comes thru here.
  1470.     */
  1471.  
  1472.  
  1473.     switch ( terminator )
  1474.     {
  1475.            case 'm':
  1476.            case 'M':
  1477.             inmode = num;
  1478.             return ( FALSE );    /* we do NOT pass this */
  1479.             break;
  1480.  
  1481.            /*
  1482.            **  <esc>*b#X is obsolete, don't bother with it. 
  1483.            **  If I did do something, then I would zero part of the
  1484.            **  seed rows.
  1485.            */
  1486.  
  1487.            case 'x':
  1488.            case 'X':
  1489.             break;
  1490.  
  1491.            case 'y':
  1492.            case 'Y':
  1493.             /* zero only if allocated */
  1494.             if ( memflag )
  1495.                 zero_seeds();
  1496.             break;
  1497.  
  1498.            case 'W':
  1499.             if(!memflag)
  1500.                 zero_seeds();        /* get memory */
  1501.  
  1502.             /* fire up sequence */
  1503.  
  1504.             if ( !in_sequence )
  1505.             {
  1506.                 putchar ( ESC );
  1507.                 putchar ( parameter );
  1508.                 putchar ( group );
  1509.             } else
  1510.                 putchar ( old_terminator );
  1511.  
  1512.             in_sequence = FALSE;        /* terminating */
  1513.  
  1514.             if(curr_plane < num_planes) 
  1515.             {
  1516.  
  1517.                 Process(num, 'W');
  1518.  
  1519.                 if ( curr_plane + 1 < num_planes )
  1520.                 {
  1521.                     /* now we have a problem */
  1522.                     zero_upper(curr_plane + 1);
  1523.                 }
  1524.             } else
  1525.                 Process_extra(num,'W');   
  1526.  
  1527.             curr_plane=0;
  1528.  
  1529.             return ( FALSE );
  1530.  
  1531.             break;
  1532.  
  1533.            case 'V':
  1534.             if(!memflag)
  1535.                 zero_seeds();        /* get memory */
  1536.             
  1537.             if( curr_plane < num_planes ) 
  1538.             {
  1539.                 /* fire up sequence */
  1540.  
  1541.                 if ( !in_sequence )
  1542.                 {
  1543.                     putchar ( ESC );
  1544.                     putchar ( parameter );
  1545.                     putchar ( group );
  1546.                 } else
  1547.                     putchar ( old_terminator );
  1548.  
  1549.                 in_sequence = FALSE;    /* terminating */
  1550.             
  1551.  
  1552.                 Process(num, 'V');
  1553.                 curr_plane++;
  1554.             } else
  1555.                 Process_extra(num,'V');
  1556.  
  1557.             return ( FALSE );
  1558.  
  1559.             break;
  1560.  
  1561.         default:
  1562.             break;
  1563.     }
  1564.  
  1565.     return ( TRUE );        /* pass sequence */
  1566. }
  1567.  
  1568.  
  1569.  
  1570. /*
  1571. **  Flush_To_Term() simply passes thru input until a valid terminator
  1572. **  character is found.  This is for unwanted escape sequences.
  1573. */
  1574.  
  1575. Flush_To_Term()
  1576. {
  1577.     int    c;
  1578.  
  1579.     do
  1580.     {
  1581.         c = getchar();
  1582.  
  1583.         if ( c == EOF )            /* this is a problem */
  1584.             return;
  1585.         
  1586.         putchar ( c );
  1587.  
  1588.     } while ( c < '@' || c > '^' );
  1589. }
  1590.  
  1591.  
  1592. /*
  1593. **  Flush_Bytes() simply transfers so many bytes directly from input to output.
  1594. **  This is used to pass thru binary data that we are not interested in so that
  1595. **  it will not confuse the parser.  I.e. downloads.
  1596. */
  1597.  
  1598. Flush_Bytes( num )
  1599. unsigned int    num;
  1600. {
  1601.     int    bnum;
  1602.  
  1603.     while ( num > 0 )
  1604.     {
  1605.         bnum = MIN ( BUFSIZ, num );
  1606.  
  1607.         fread( buf, 1, bnum, stdin );
  1608.  
  1609.         if ( fwrite( buf, 1, bnum, stdout ) < bnum )
  1610.  
  1611.             /* check for error and exit */
  1612.  
  1613.             if ( ferror(stdout) )
  1614.             {
  1615.                 perror("Output error");
  1616.                 exit(-2);
  1617.             }
  1618.  
  1619.         num -= bnum;
  1620.     }
  1621. }
  1622.  
  1623.  
  1624.  
  1625.  
  1626. /*----------------------------------------*/
  1627.  
  1628. /*
  1629. **    Zero_seeds() will allocate and initialize memory.
  1630. **    If memory has already been allocated, then it will just initialize it.
  1631. */
  1632.  
  1633.  
  1634. zero_seeds()
  1635. {
  1636.     int r;
  1637.  
  1638.     /* first allocate and init seed_rows for number of planes. */
  1639.  
  1640.     for ( r = 0; r < num_planes ; r++)
  1641.     {
  1642.         if(!memflag)
  1643.         {
  1644.             seed_row[r] = (unsigned char *) malloc(rasterwidth);
  1645.  
  1646.             if ( seed_row[r] == NULL )
  1647.             {
  1648.                 fprintf(stderr, "Out of memory.\n");
  1649.                 exit(-3);
  1650.             }
  1651.         }
  1652.  
  1653.         /* zero seeds for mode 3 */
  1654.  
  1655.         memset(seed_row[r], 0, rasterwidth);
  1656.     }
  1657.  
  1658.  
  1659.     if(!memflag)
  1660.     {
  1661.         new_row = (unsigned char *) malloc(rasterwidth);
  1662.  
  1663.         if ( new_row == NULL )
  1664.         {
  1665.             fprintf(stderr, "Out of memory.\n");
  1666.             exit(-3);
  1667.         }
  1668.  
  1669.         for(r=0; r<MAXMODES; r++)
  1670.         {
  1671.             /* 2 * width is needed for modes 1, 2 and 3 */
  1672.  
  1673.             out_row[r] = (unsigned char *) malloc(2 * rasterwidth);
  1674.  
  1675.             if ( out_row[r] == NULL )
  1676.             {
  1677.                 fprintf(stderr, "Out of memory.\n");
  1678.                 exit(-3);
  1679.             }
  1680.         }
  1681.  
  1682.     }
  1683.  
  1684.     memset(new_row, 0, rasterwidth);
  1685.  
  1686.     memflag = TRUE;            /* memory is in place */
  1687. }
  1688.  
  1689.  
  1690. /* this routine if for incomplete transfers of data */
  1691.  
  1692. zero_upper(plane)
  1693. int    plane;
  1694. {
  1695.     int i;
  1696.  
  1697.     /* assume memory already present */
  1698.  
  1699.     for ( i = plane; i < num_planes; i++)
  1700.         memset(seed_row[i], 0, rasterwidth);
  1701. }
  1702.  
  1703.  
  1704. Process(inbytes, terminator)
  1705. int inbytes, terminator;
  1706. {
  1707.  
  1708.     int insize;
  1709.     int minmode = 0;
  1710.  
  1711.     inuse[inmode]++;
  1712.  
  1713.     switch ( inmode ) {
  1714.  
  1715.     case 0:
  1716.         if ( !widthwarning && inbytes > rasterwidth )
  1717.         {
  1718.             /* This is likely to result in data truncation. */
  1719.             widthwarning = TRUE;
  1720.             fprintf(stderr,"Warning: Input pixel width exceeds expected width.\n");
  1721.         }
  1722.  
  1723.         insize = Mode_0_Graphics(inbytes,rasterwidth,new_row,invert);
  1724.         break;
  1725.     case 1:
  1726.         insize = Mode_1_Graphics(inbytes,rasterwidth,new_row,invert);
  1727.         break;
  1728.     case 2:
  1729.         insize = Mode_2_Graphics(inbytes,rasterwidth,new_row,invert);
  1730.         break;
  1731.     case 3:
  1732.         memcpy(new_row, seed_row[curr_plane], rasterwidth);
  1733.         insize = Mode_3_Graphics(inbytes,rasterwidth,new_row,invert);
  1734.         break;
  1735.  
  1736.     default:        /* unknown mode? */
  1737.  
  1738.         /*  Don't know what to do about seed rows, pass stuff thru */
  1739.  
  1740.         fprintf(stderr, "%s: Unsupported compression mode %d.\n",
  1741.             progname, inmode );
  1742.  
  1743.         ChangeMode(inmode);    /* go to that mode */
  1744.  
  1745.         /* <esc>*b has already been output */
  1746.  
  1747.         printf("%1d%c", inbytes, terminator);
  1748.  
  1749.         Flush_Bytes( inbytes );
  1750.  
  1751.         firstrow = TRUE;        /* pop it out of mode 3 */
  1752.  
  1753.         return;
  1754.  
  1755.     }
  1756.  
  1757.  
  1758.     /*
  1759.     **
  1760.     */
  1761.  
  1762.     if ( mode0 )
  1763.         out_size[0] = Output_0( new_row, out_row[0], rasterwidth );
  1764.     else
  1765.         out_size[0] = MAXBYTES+1;
  1766.  
  1767.     if ( mode1 )
  1768.         out_size[1] = Output_1( new_row, out_row[1], rasterwidth );
  1769.     else
  1770.         out_size[1] = MAXBYTES+1;
  1771.  
  1772.     if ( mode2 )
  1773.         out_size[2] = Output_2( new_row, out_row[2], rasterwidth );
  1774.     else
  1775.         out_size[2] = MAXBYTES+1;
  1776.  
  1777.     if ( mode3 )
  1778.         out_size[3] = Output_3( seed_row[curr_plane], new_row, out_row[3], rasterwidth );
  1779.     else
  1780.         out_size[3] = MAXBYTES+1;
  1781.     
  1782.  
  1783.     /*
  1784.     **  Now determine which mode will give the best output.  Note that it
  1785.     **  takes 5 bytes to change modes, so we penalize all modes that are
  1786.     **  not the current output by 5 bytes.  This is to discourage changing
  1787.     **  unless the benifit is worth it.  The exception to this rule is
  1788.     **  mode 3.  We want to encourage going to mode 3 because of the seed
  1789.     **  row behaviour.  That is, if we have a simple picture that does
  1790.     **  not change much, and say each of the sizes for modes 1 and 2 always
  1791.     **  comes out to 4 bytes of data, then if we add 5 to mode 3 each time,
  1792.     **  it would never get selected.  But, we remove the penalty, and if
  1793.     **  mode 3 is selected (0 bytes of data needed for mode 3), then each
  1794.     **  succesive row only needs 0 bytes of data.  For a 300 dpi A size
  1795.     **  picture with 3 data planes, this could be a savings of 37k bytes.
  1796.     */
  1797.  
  1798.     /*
  1799.     **  With the new parser, the output to change modes is now only
  1800.     **  2 bytes, since it gets combined with the *b#W sequence.
  1801.     **  So, I decided to ignore the switching penalty.
  1802.     */
  1803.  
  1804.     /*
  1805.     **  Due to a possible bug in PaintJet XL, don't allow mode 3 to be
  1806.     **  selected for the first row of output.  But do allow it if the
  1807.     **  user has no other mode selected.
  1808.     */
  1809.  
  1810.     if ( firstrow && (mode0 || mode1 || mode2) )
  1811.     {
  1812.         out_size[3] = MAXBYTES+1;    /* disable mode 3 for now */
  1813.  
  1814.         if ( terminator == 'W' )    /* last plane? */
  1815.             firstrow = FALSE;    /* no longer first row */
  1816.     }
  1817.  
  1818.  
  1819.     minmode = 3;
  1820.  
  1821.     if ( out_size[2] < out_size[minmode] )
  1822.         minmode = 2;
  1823.  
  1824.     if ( out_size[1] < out_size[minmode] )
  1825.         minmode = 1;
  1826.  
  1827.     if ( out_size[0] < out_size[minmode] )
  1828.         minmode = 0;
  1829.  
  1830.  
  1831.                     /* I may remove this sometime */
  1832.     if ( minmode != outmode )
  1833.         if ( out_size[minmode] == out_size[outmode] )
  1834.             minmode = outmode;
  1835.  
  1836.  
  1837.     outuse[minmode]++;
  1838.  
  1839.     if ( outmode != minmode )
  1840.         ChangeMode( minmode );
  1841.     
  1842.     /* <esc>*b has already been output */
  1843.  
  1844.     printf("%1d%c", out_size[minmode], terminator);
  1845.  
  1846.     if ( fwrite( out_row[minmode], 1, out_size[minmode], stdout) < 
  1847.                             out_size[minmode] )
  1848.  
  1849.         /* check for error and exit */
  1850.  
  1851.         if ( ferror(stdout) )
  1852.         {
  1853.             perror("Output error");
  1854.             exit(-2);
  1855.         }
  1856.  
  1857.  
  1858.     memcpy(seed_row[curr_plane], new_row, rasterwidth);
  1859.  
  1860. }
  1861.  
  1862. Process_extra(bytes, terminator)
  1863. int bytes;
  1864. char terminator;
  1865. {
  1866.     int  i;
  1867.  
  1868.     /* toss any excess data */
  1869.  
  1870.     for(i = 0; i < bytes; i++)
  1871.        getchar();
  1872.  
  1873.     /* last plane? force move down to next row */
  1874.  
  1875.     if(terminator == 'W')
  1876.     {
  1877.         /* <esc>*b has already been output */
  1878.         printf("0W");
  1879.  
  1880.         firstrow = FALSE;        /* not on first row anymore */
  1881.  
  1882.     }
  1883.  
  1884. }
  1885.  
  1886. ChangeMode(newmode)
  1887. int newmode;
  1888. {
  1889.     /*
  1890.     **  <esc>*b have already been output.
  1891.     **  terminator is 'm' instead of 'M' since will be followed by 'W'
  1892.     */
  1893.     printf("%1dm", newmode);
  1894.     outmode = newmode;
  1895. }
  1896.  
  1897.  
  1898. /* these decoders came from graphics.c in the gp parser */
  1899.  
  1900.  
  1901. /* $PAGE$ */
  1902. /*-----------------------------------------------------------------------*\
  1903.  |                                                                       |
  1904.  |  Function Name: Mode_0_Graphics                                       |
  1905.  |                                                                       |
  1906.  |  Description:                                                         |
  1907.  |                                                                       |
  1908.  |  This is the routine that handles a Mode 0 graphics block transfer    |
  1909.  |  to the Formatter Module.                                             |
  1910.  |                                                                       |
  1911. \*-----------------------------------------------------------------------*/
  1912.  
  1913. /* FUNCTION */
  1914.  
  1915. Mode_0_Graphics(input_bytes, output_bytes, address, invert)
  1916.  
  1917. unsigned int
  1918.    input_bytes,                 /* Count of bytes to be read. */
  1919.    output_bytes;                /* Count of bytes to be stored. */
  1920.  
  1921. unsigned char
  1922.    *address;                    /* Pointer to where to store bytes. */
  1923.  
  1924. unsigned char            /* Boolean to request data inversion */
  1925.     invert;
  1926.  
  1927. {
  1928.    /* LOCAL VARIABLES */
  1929.  
  1930.    unsigned char
  1931.       *store_ptr;               /* Pointer to where to store the byte. */
  1932.  
  1933.    unsigned int
  1934.       read_bytes,               /* Local copy of input_bytes. */
  1935.       write_bytes;              /* Local copy of output_bytes. */
  1936.  
  1937.    /* CODE */
  1938.  
  1939.    /* Initialize the local variables. */
  1940.  
  1941.    read_bytes = input_bytes;
  1942.    write_bytes = output_bytes;
  1943.    store_ptr = address;
  1944.    
  1945.  
  1946.    /* transfer the lesser of available bytes or available room */
  1947.  
  1948.    if (invert)
  1949.      Inv_Transfer_Block( MIN(write_bytes,read_bytes), store_ptr);
  1950.    else
  1951.      Transfer_Block( MIN(write_bytes,read_bytes), store_ptr);
  1952.  
  1953.    /* now zero fill or throw excess data away */
  1954.  
  1955.    if ( read_bytes > write_bytes )
  1956.       Discard_Block(read_bytes - write_bytes);        /* throw excess */
  1957.    else {
  1958.       store_ptr += read_bytes;                /* adjust pointer */
  1959.       write_bytes -= read_bytes;            /* zero fill count */
  1960.  
  1961.       memset(store_ptr, 0, write_bytes);
  1962.    }
  1963.  
  1964.    return ( input_bytes );
  1965. }
  1966.  
  1967. /* $PAGE$ */
  1968. /*-----------------------------------------------------------------------*\
  1969.  |                                                                       |
  1970.  |  Function Name: Mode_1_Graphics                                       |
  1971.  |                                                                       |
  1972.  |  Description:                                                         |
  1973.  |                                                                       |
  1974.  |  This is the routine that handles a Mode 1 graphics block transfer    |
  1975.  |  to the Formatter Module.  Mode 1 graphics is a compacted mode.       |
  1976.  |  The data in Mode 1 is in pairs.  The first byte is a replicate       |
  1977.  |  count and the second byte is the data.  The data byte is stored      |
  1978.  |  then replicated the replicate count.  Therefore a replicate count    |
  1979.  |  of 0 means the data byte is stored once.  The input byte count       |
  1980.  |  must be an even amount for the data to be in byte pairs.             |
  1981.  |                                                                       |
  1982. \*-----------------------------------------------------------------------*/
  1983.  
  1984. /* FUNCTION */
  1985.  
  1986. Mode_1_Graphics(input_bytes, output_bytes, address, invert)
  1987.  
  1988. unsigned int
  1989.    input_bytes,                 /* Count of bytes to be read. */
  1990.    output_bytes;                /* Count of bytes to be stored. */
  1991.  
  1992. unsigned char
  1993.    *address;                    /* Pointer to where to store bytes. */
  1994.  
  1995. unsigned char            /* Boolean to request data inversion */
  1996.     invert;
  1997.  
  1998. {
  1999.    /* LOCAL VARIABLES */
  2000.  
  2001.    unsigned char
  2002.       *store_ptr,               /* Pointer to where to store the byte. */
  2003.       input_char;               /* Byte to be replicated. */
  2004.  
  2005.    unsigned int
  2006.       read_bytes,               /* Local copy of input_bytes. */
  2007.       write_bytes;              /* Local copy of output_bytes. */
  2008.  
  2009.    int
  2010.       replicate_count;          /* Number of times to replicate data. */
  2011.  
  2012.    /* CODE */
  2013.  
  2014.    /* Initialize the local variables. */
  2015.  
  2016.    read_bytes = input_bytes;
  2017.    write_bytes = output_bytes;
  2018.    store_ptr = address;
  2019.    
  2020.    /* Check for an even input count. */
  2021.  
  2022.    if ((read_bytes % 2) == 0)
  2023.    {
  2024.       /* Even so input data is in pairs as required. So store the data. */
  2025.    
  2026.       while ((read_bytes != 0) && (write_bytes != 0))
  2027.       {
  2028.          /* First get the replicate count and the byte to store. */
  2029.  
  2030.          replicate_count = (unsigned char) Get_Character();
  2031.          input_char = (invert ? ~Get_Character() : Get_Character());
  2032.          read_bytes -= 2;
  2033.       
  2034.          /* Since write_bytes was 0 there is room to store the byte. */
  2035.  
  2036.          *store_ptr++ = input_char;
  2037.          write_bytes--;
  2038.          
  2039.          /* Now make sure there is room for the replicated data. */
  2040.  
  2041.          if (replicate_count > write_bytes)
  2042.          {
  2043.             /* Too much so limit to the room available. */
  2044.  
  2045.             replicate_count = write_bytes;
  2046.          }
  2047.             
  2048.          /* Update the amount to be written. */
  2049.  
  2050.          write_bytes -= replicate_count;
  2051.  
  2052.          /* Then replicate it. */
  2053.  
  2054.          while (replicate_count != 0)
  2055.          {
  2056.             /* Store the byte the decrement the count. */
  2057.  
  2058.             *store_ptr++ = input_char;
  2059.          
  2060.             replicate_count--;
  2061.          }
  2062.       }
  2063.  
  2064.    }
  2065.    /* Discard any left over input. */
  2066.     /* OR */
  2067.    /* Discard all of the input data as odd byte count. */
  2068.  
  2069.    Discard_Block(read_bytes);
  2070.  
  2071.    read_bytes = store_ptr - address;  /* how much was done? */
  2072.  
  2073.    /* zero fill if needed */
  2074.    memset(store_ptr, 0, write_bytes);
  2075.          
  2076.    return(read_bytes);
  2077. }
  2078.  
  2079. /* $PAGE$ */
  2080. /*-----------------------------------------------------------------------*\
  2081.  |                                                                       |
  2082.  |  Function Name: Mode_2_Graphics                                       |
  2083.  |                                                                       |
  2084.  |  Description:                                                         |
  2085.  |                                                                       |
  2086.  |  This is the routine that handles a Mode 2 graphics block transfer    |
  2087.  |  to the Formatter Module.  Mode 2 graphics is a compacted mode.       |
  2088.  |  The data in Mode 2 is of one of two types.  The first type is a      |
  2089.  |  class type and the second type is a data type.  The class type is    |
  2090.  |  a single byte which is a combination of replicate count and a sub    |
  2091.  |  mode.  There are two sub modes within mode 2, sub mode 0 and sub     |
  2092.  |  mode 1.  These sub modes are flagged by the MSB of the class type    |
  2093.  |  byte.  If the MSB = 0 then the replicate count is the value of the   |
  2094.  |  class type byte.  In sub mode 0 the replicate count ranges from 1    |
  2095.  |  to 127.  In sub mode 0 the next byte and then the replicate count    |
  2096.  |  of bytes are of the data type and stored.  If the MSB = 1 then the   |
  2097.  |  sub mode is 1 and the replicate count is the negative value of the   |
  2098.  |  class type.  In sub mode 1 the replicate count is stored in 2s       |
  2099.  |  compliment form and ranges from -1 to -127.  In sub mode 1 the       |
  2100.  |  next byte is of the data type and is stored.  That data byte is      |
  2101.  |  then replicated and stored the replicate count.  If the class type   |
  2102.  |  byte is 128 then there is no data type byte.                         |
  2103.  |                                                                       |
  2104. \*-----------------------------------------------------------------------*/
  2105.  
  2106. /* FUNCTION */
  2107.  
  2108. Mode_2_Graphics(input_bytes, output_bytes, address, invert)
  2109.  
  2110. unsigned int
  2111.    input_bytes,                 /* Count of bytes to be read. */
  2112.    output_bytes;                /* Count of bytes to be stored. */
  2113.  
  2114. unsigned char
  2115.    *address;                    /* Pointer to where to store bytes. */
  2116.  
  2117. unsigned char            /* Boolean to request data inversion */
  2118.     invert;
  2119.  
  2120. {
  2121.    /* LOCAL VARIABLES */
  2122.  
  2123.    unsigned char
  2124.       *store_ptr,               /* Pointer to where to store the byte. */
  2125.       input_char,               /* Byte to be replicated. */
  2126.       sub_mode;                 /* Flag if sub mode is 0 or 1. */
  2127.  
  2128.    unsigned int
  2129.       read_bytes,               /* Local copy of input_bytes. */
  2130.       write_bytes;              /* Local copy of output_bytes. */
  2131.  
  2132.    int
  2133.       replicate_count;          /* Number of times to replicate data. */
  2134.  
  2135.    /* CODE */
  2136.  
  2137.    /* Initialize the local variables. */
  2138.  
  2139.    read_bytes = input_bytes;
  2140.    write_bytes = output_bytes;
  2141.    store_ptr = address;
  2142.    
  2143.    while ((read_bytes > 1) && (write_bytes != 0))
  2144.    {
  2145.       /* First get the class type byte and the first data type byte. */
  2146.  
  2147.       replicate_count = Get_Character();
  2148.  
  2149.       /* First check that this not an ignore class type. */
  2150.  
  2151.       if (replicate_count != 128)
  2152.       {
  2153.          /* Not ignore so get the data class byte. */
  2154.  
  2155.          input_char = (invert ? ~Get_Character() : Get_Character());
  2156.          read_bytes -= 2;
  2157.          
  2158.         /* Since write_bytes wasn't 0 there is room to store the byte. */
  2159.  
  2160.          *store_ptr++ = input_char;
  2161.          write_bytes--;
  2162.  
  2163.          /* Determine the sub mode. */
  2164.    
  2165.          if (replicate_count > 128)
  2166.          {
  2167.             /* Sub mode 1. */
  2168.    
  2169.             sub_mode = 1;
  2170.             /* replicate count was unsigned char */
  2171.             replicate_count = 256 - replicate_count;
  2172.          }
  2173.          else
  2174.          {
  2175.             /* Sub mode 0. */
  2176.    
  2177.             sub_mode = 0;
  2178.  
  2179.             /* See if there is enoungh input left for the data byte count. */
  2180.  
  2181.             if (replicate_count > read_bytes)
  2182.             {
  2183.                /* Too many data bytes so limit to the input left. */
  2184.  
  2185.                replicate_count = read_bytes;
  2186.             }
  2187.          }
  2188.             
  2189.          /* Now make sure there is room for the replicated data. */
  2190.    
  2191.          if (replicate_count > write_bytes)
  2192.          {
  2193.             /* Too much so limit to the room available. */
  2194.    
  2195.             replicate_count = write_bytes;
  2196.          }
  2197.                
  2198.          /* Update the amount to be written. */
  2199.    
  2200.          write_bytes -= replicate_count;
  2201.    
  2202.          /* Then replicate it. */
  2203.    
  2204.          if (sub_mode == 0)
  2205.          {
  2206.             /* Sub mode 0 so get the replicate count of data bytes. */
  2207.    
  2208.             if (invert)
  2209.               Inv_Transfer_Block(replicate_count, store_ptr);
  2210.             else
  2211.               Transfer_Block(replicate_count, store_ptr);
  2212.  
  2213.             read_bytes -= replicate_count;
  2214.             
  2215.             /* Find the last byte stored. */
  2216.    
  2217.             store_ptr += replicate_count;
  2218.          }
  2219.          else
  2220.          {
  2221.             /* Sub mode 1 so just duplicate the original byte. */
  2222.    
  2223.             while (replicate_count != 0)
  2224.             {
  2225.                /* Store the byte the decrement the count. */
  2226.    
  2227.                *store_ptr++ = input_char;
  2228.             
  2229.                replicate_count--;
  2230.             }
  2231.          }
  2232.       }
  2233.       else
  2234.       {
  2235.          /* Ignore class so don't get the data class byte. */
  2236.  
  2237.          read_bytes--;
  2238.       }
  2239.    }
  2240.  
  2241.    /* Now discard any left over input. */
  2242.  
  2243.    Discard_Block(read_bytes);
  2244.  
  2245.    read_bytes = store_ptr - address;
  2246.  
  2247.    /* zero fill if needed */
  2248.    memset(store_ptr, 0, write_bytes);
  2249.          
  2250.    
  2251.    return(read_bytes);
  2252. }
  2253.  
  2254. /* $PAGE$ */
  2255. /*-----------------------------------------------------------------------*\
  2256.  |                                                                       |
  2257.  |  Function Name: Mode_3_Graphics                                       |
  2258.  |                                                                       |
  2259.  |  Description:                                                         |
  2260.  |                                                                       |
  2261.  |  This is the routine that handles a Mode 3 graphics block transfer    |
  2262.  |  to the Formatter Module.  Mode 3 graphics is a compacted mode.       |
  2263.  |  Mode 3 data is a difference from one row to the next.  In order to   |
  2264.  |  work, each row must be saved to be a seed for the next.  This        |
  2265.  |  mode is used in conjuction with other compaction modes when the      |
  2266.  |  data remains fairly constant between pairs of rows.                  |
  2267.  |  The data is in the form:                                             |
  2268.  |  <command byte>[<optional offset bytes>]<1 to 8 replacement bytes>    |
  2269.  |  The command byte is in the form:                                     |
  2270.  |    Bits 5-7: Number of bytes to replace (1 - 8)                       |
  2271.  |    Bits 0-4: Relative offset from last byte.                          |
  2272.  |       (If the offset is 31, then add the following bytes for offset   |
  2273.  |       until an offset byte of less then 255 (but inclusive)           |
  2274.  |                                                                       |
  2275. \*-----------------------------------------------------------------------*/
  2276.  
  2277. /* FUNCTION */
  2278.  
  2279. Mode_3_Graphics(input_bytes, output_bytes, address, invert)
  2280.  
  2281. unsigned int
  2282.    input_bytes,                 /* Count of bytes to be read. */
  2283.    output_bytes;                /* Count of bytes to be stored. */
  2284.  
  2285. unsigned char
  2286.    *address;                    /* Pointer to where to store bytes. */
  2287.  
  2288. unsigned char            /* Boolean to request data inversion */
  2289.     invert;
  2290.  
  2291. {
  2292.    /* LOCAL VARIABLES */
  2293.  
  2294.    unsigned char
  2295.       *store_ptr,               /* Pointer to where to store the byte. */
  2296.       input_char;               /* Byte to be changed. */
  2297.  
  2298.    unsigned int
  2299.       read_bytes,               /* Local copy of input_bytes. */
  2300.       write_bytes;              /* Local copy of output_bytes. */
  2301.  
  2302.    unsigned int
  2303.       replace,            /* number of bytes to replace, 1-8 */
  2304.       offset;            /* relative offset */
  2305.  
  2306. #if BITFIELDS
  2307.    union comtype {
  2308.       unsigned char comchar;    /* command byte as char */
  2309.       struct btype {
  2310.      unsigned repcount:3;    /* replace count 1-8 */
  2311.      unsigned roff:5;    /* relative offset 0-30 */
  2312.       } bitf;
  2313.    } command;
  2314. #else
  2315.     unsigned char    command;
  2316. #endif
  2317.  
  2318.    /* CODE */
  2319.  
  2320.    /* Initialize the local variables. */
  2321.  
  2322.    read_bytes = input_bytes;
  2323.    write_bytes = output_bytes;
  2324.    store_ptr = address;
  2325.  
  2326. /* read_bytes has to be at least 2 to be valid */
  2327.  
  2328.    while ( read_bytes > 1 && write_bytes > 0 ){
  2329.  
  2330.       /* start by getting the command byte */
  2331.  
  2332.       read_bytes--;
  2333.  
  2334. #if BITFIELDS
  2335.       command.comchar = Get_Character();
  2336.  
  2337.       replace = command.bitf.repcount + 1;    /* replace count 1-8 */
  2338.  
  2339.       offset = command.bitf.roff;        /* offset 0-30, 31= extend */
  2340. #else
  2341.     command = Get_Character();
  2342.     replace = (command >> 5) + 1;
  2343.     offset = command & 0x1f;
  2344. #endif
  2345.  
  2346.       store_ptr += offset;
  2347.       write_bytes -= offset;
  2348.  
  2349.       if ( offset == 31 )        /* get more offsets */
  2350.      do{
  2351.  
  2352.         offset = Get_Character();
  2353.  
  2354.         read_bytes--;
  2355.             if ( read_bytes == 0 )        /* premature finish? */
  2356.            return;                /* no zero fill wih 3 */
  2357.  
  2358.         store_ptr += offset;
  2359.             write_bytes -= offset;
  2360.  
  2361.      } while (offset == 255);    /* 255 = keep going */
  2362.       
  2363.       /* now do the byte replacement */
  2364.  
  2365.       while ( replace-- && write_bytes > 0 && read_bytes > 0 ){
  2366.      
  2367.      *store_ptr++ = (invert ? ~Get_Character() : Get_Character() );
  2368.  
  2369.      read_bytes--;
  2370.      write_bytes--;
  2371.       }
  2372.    }
  2373.    
  2374.    /* don't do any zero fill with mode 3 */
  2375.  
  2376.    /* discard any leftover input */
  2377.  
  2378.    Discard_Block(read_bytes);
  2379.  
  2380.    return( store_ptr - address );
  2381. }
  2382.  
  2383.  
  2384. Discard_Block(count)
  2385. unsigned int count;
  2386. {
  2387.     while ( count-- )
  2388.         getchar();
  2389. }
  2390.  
  2391. Transfer_Block( count, dest )
  2392. unsigned int count;
  2393. unsigned char *dest;
  2394. {
  2395.     fread(dest, 1, count, stdin);
  2396. }
  2397.  
  2398. /* this doesn't invert at the moment */
  2399.  
  2400. Inv_Transfer_Block( count, dest )
  2401. unsigned int count;
  2402. unsigned char *dest;
  2403. {
  2404.     fread(dest, 1, count, stdin);
  2405. }
  2406.  
  2407.  
  2408. Output_0(src, dest, count)
  2409. unsigned char *src, *dest;
  2410. int count;
  2411. {
  2412.     memcpy(dest, src, count);
  2413.  
  2414.     if ( zerostrip )
  2415.         while ( count && dest[count-1] == 0 )
  2416.             count--;
  2417.  
  2418.     return(count);
  2419.  
  2420. }
  2421.  
  2422. Output_1(src, dest, count)
  2423. unsigned char *src, *dest;
  2424. register int count;
  2425. {
  2426.     unsigned char *optr = dest, *iptr;
  2427.     int k,c;
  2428.  
  2429.     if ( zerostrip )            /* strip zeros */
  2430.     {
  2431.         iptr = src + count - 1;        /* point to end of data */
  2432.  
  2433.         while ( count > 0 && *iptr-- == 0 )    /* hunt thru 0's */
  2434.             count--;
  2435.     }
  2436.  
  2437.     iptr = src;
  2438.  
  2439.     while ( count ){
  2440.         
  2441.         c = *iptr++;        /* get value to work with */
  2442.         count--;
  2443.  
  2444.         k = 0;
  2445.  
  2446.         while ( *iptr == c && k < 255 && count ){
  2447.             k++;
  2448.             iptr++;
  2449.             count--;
  2450.         }
  2451.  
  2452.         *optr++ = k;        /* output repeat count */
  2453.         *optr++ = c;        /* output value */
  2454.     }
  2455.  
  2456.     count = optr - dest;        /* for return value */
  2457.  
  2458.     return ( count );
  2459. }
  2460.  
  2461.  
  2462. Output_2(src, dest, count)
  2463. unsigned char *src, *dest;
  2464. register int count;
  2465. {
  2466.     unsigned char *optr = dest, *iptr;
  2467.     int k,c;
  2468.     unsigned char *tptr, *tptr1, *tptr2;
  2469.     int tk,tc;
  2470.  
  2471.  
  2472.     if ( zerostrip )            /* strip zeros */
  2473.     {
  2474.         iptr = src + count - 1;        /* point to end of data */
  2475.  
  2476.         while ( count > 0 && *iptr-- == 0 )    /* hunt thru 0's */
  2477.             count--;
  2478.     }
  2479.  
  2480.     iptr = src;
  2481.  
  2482.     while ( count ){
  2483.         
  2484.         c = *iptr++;        /* get value to work with */
  2485.         count--;
  2486.  
  2487.         k = 0;
  2488.  
  2489.         while ( *iptr == c && k < 127 && count ){
  2490.             k++;
  2491.             iptr++;
  2492.             count--;
  2493.         }
  2494.  
  2495.         if ( k >= 1 ){
  2496.             *optr++ = 256 - k;    /* output repeat count */
  2497.             *optr++ = c;        /* output value */
  2498.         } else {
  2499.                     /* a two byte replicate run will
  2500.                      * be sent as a repeated byte 
  2501.                      * unless it is preceeded and
  2502.                      * and followed by a literal run,
  2503.                      * in which case it is merged
  2504.                      * into the run.
  2505.                      */
  2506.             tk = 0;
  2507.             tc = c;
  2508.             tptr = iptr;
  2509.             tptr1 = tptr;
  2510.             tptr1++;
  2511.             tptr2 = tptr1;
  2512.             tptr2++;
  2513.  
  2514.             while ( tk < 128 && (count - tk) > 0    && 
  2515.                 ((*tptr != tc) || (*tptr == tc &&
  2516.                                    (count - tk - 1) > 0 &&
  2517.                                    *tptr1 != *tptr &&
  2518.                                    *tptr2 != *tptr1))){
  2519.  
  2520.                 tc = *tptr++;
  2521.                 tk++;
  2522.                 tptr1++;
  2523.                 tptr2++;
  2524.             }
  2525.  
  2526.             if ( count && tk )
  2527.                 tk--;
  2528.  
  2529.             *optr++ = tk;        /* output count */
  2530.             *optr++ = c;        /* output firstvalue */
  2531.  
  2532.             while ( tk-- > 0){
  2533.                 *optr++ = *iptr++;
  2534.                 count--;
  2535.             }
  2536.  
  2537.         }
  2538.     }
  2539.  
  2540.     count = optr - dest;        /* for return value */
  2541.  
  2542.     return ( count );
  2543. }
  2544.  
  2545. Output_3(seed, new, dest, count)
  2546. unsigned char *seed, *new, *dest;
  2547. int count;
  2548. {
  2549.     unsigned char *sptr=seed, *nptr=new, *dptr=dest;
  2550.     int i,j;
  2551.  
  2552.  
  2553. #if BITFIELDS
  2554.    union comtype {
  2555.       unsigned char comchar;    /* command byte as char */
  2556.       struct btype {
  2557.      unsigned repcount:3;    /* replace count 1-8 */
  2558.      unsigned roff:5;    /* relative offset 0-30 */
  2559.       } bitf;
  2560.    } command;
  2561. #else
  2562.     unsigned char    command;
  2563. #endif
  2564.  
  2565.     while ( count > 0 ){
  2566.         i = 0;
  2567.  
  2568.                     /* find first diff */
  2569.         while ( *sptr == *nptr && i < count ){
  2570.             i++;
  2571.             sptr++;
  2572.             nptr++;
  2573.         }
  2574.  
  2575.         if ( i >= count )    /* too far to find diff */
  2576.             return(dptr - dest);    /* bail */
  2577.  
  2578.         count -= i;
  2579.         
  2580.         /* now count how many bytes to change */
  2581.  
  2582.         for ( j = 1; j < 8; j++)    /* j == 0 is already known */
  2583.             if ( j > count || sptr[j] == nptr[j] )
  2584.                 break;
  2585.         
  2586.         j--;    /* adjust */
  2587.  
  2588. #if BITFIELDS
  2589.         command.bitf.repcount = j;    /* 0-7 ==> 1-8 */
  2590.  
  2591.         command.bitf.roff = MIN ( i, 31 );
  2592.  
  2593.         *dptr++ = command.comchar;    /* output command */
  2594. #else
  2595.         command = (j << 5);
  2596.         command += MIN( i, 31 );
  2597.         *dptr++ = command;
  2598. #endif
  2599.  
  2600.         if ( i == 31 )
  2601.             *dptr++ = 0;
  2602.         
  2603.         i -= MIN (i, 31);
  2604.  
  2605.         while( i ){
  2606.             *dptr++ = MIN ( i, 255 );
  2607.  
  2608.             if ( i == 255 )
  2609.                 *dptr++ = 0;
  2610.             
  2611.             i -= MIN ( i, 255 );
  2612.         }
  2613.  
  2614.         while (j-- >= 0){
  2615.             *dptr++ = *nptr++;
  2616.             sptr++;
  2617.             count--;
  2618.         }
  2619.     }
  2620.  
  2621.     return ( dptr - dest );
  2622. }
  2623.  
  2624.  
  2625. /*----------------------------------------------------------------------*\
  2626.  * This is here in case <ESC>*rU is sent after <ESC>*r#A, in which case *
  2627.  * we must deallocate the memory to provide for a different amount of   *
  2628.  * planes when graphics are sent.                                       *
  2629. \*----------------------------------------------------------------------*/
  2630.  
  2631. free_mem()    
  2632. {
  2633.     int r;
  2634.  
  2635.  
  2636.     if ( !memflag )
  2637.         return;        /* no memory to free */
  2638.  
  2639.     free(new_row);
  2640.  
  2641.     for(r = MAXMODES -1; r >= 0; r--)
  2642.         free(out_row[r]);
  2643.  
  2644.     for(r = num_planes - 1; r >= 0; r--)
  2645.         free(seed_row[r]);
  2646.  
  2647.     memflag = FALSE;
  2648. }
  2649. @EOF
  2650. set `sum $sumopt <pclcomp.c`; if test $1 -ne 39113
  2651. then
  2652.     echo ERROR: pclcomp.c checksum is $1 should be 39113
  2653. fi
  2654. set `wc -lwc <pclcomp.c`
  2655. if test $1$2$3 != 2213781551659
  2656. then
  2657.     echo ERROR: wc results of pclcomp.c are $* should be 2213 7815 51659
  2658. fi
  2659.  
  2660. chmod 444 pclcomp.c
  2661.  
  2662. exit 0
  2663. -- 
  2664.         Tony Parkhurst    ( tony@sdd.HP.COM )
  2665.  
  2666. "free people need no drug laws" -- James A. Parker
  2667.