diff --git a/ChangeLog b/ChangeLog index 06c5f5e..368a790 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ + * Merged Dave Coffin dcoffin_hbpl1.patch + to 2026-07-29 code base. This was found following the archive + link seen in https://github.com/OpenPrinting/foo2zjs/issues/2 + and resolves the initial issue described there. as per patch: + "Fixed all known bugs in foo2hbpl1.c. It now prints multipage + color and grayscale documents without resetting printer." + NOTE: patch was merged, not applied, so it can co-exists with + existing hbpl(2) code, not replace it. Patch date=2014-04-05. + 2026-02-06: ValdikSS First release under OpenPrinting umbrella. https://github.com/OpenPrinting/foo2zjs/releases/tag/20260206 @@ -234,6 +243,27 @@ Bug with default PageSize. PLEASE delete and recreate the foo2hbpl2 printers. +2014-03-30 Rick Richardson + * foo2hbpl1.c: Cleanup the code + +2014-03-25 Rick Richardson + * foomatic and PPD stuff for foo2hbpl1 printers: Halftone and ICM + +2014-03-24 Rick Richardson + * foomatic and PPD stuff for foo2hbpl1 printers + preliminary for: Dell 1250c, Dell C1660w, Dell C1760nw, + Epson AcuLaser C1700, Fuji-Xerox DocuPrint CP105b + * foo2hbpl1-wrapper: allow for gs 7.x or gs 8.x + +2014-03-24 Dave Coffin + * foo2hbpl1, foo2hbpl1-wrapper, et al: first cut at revision 1 + stuff. Color works, mono doesn't. No multiple pages. Printer + shows: + Restart Printer + Contact Support + IfMessageReturns + 016-313 + 2014-03-22 Rick Richardson * hbpldecode: another redundancy from Dave Coffin diff --git a/Makefile b/Makefile index 42ae0d3..a249cb9 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,7 @@ SHARESLX=$(PREFIX)/share/foo2slx SHAREHC=$(PREFIX)/share/foo2hiperc SHAREHBPL=$(PREFIX)/share/foo2hbpl SHAREDDST=$(PREFIX)/share/foo2ddst +SHAREHBPL1=$(PREFIX)/share/foo2hbpl1 MANDIR=$(PREFIX)/share/man DOCDIR=$(PREFIX)/share/doc/foo2zjs/ INSTALL=install @@ -148,6 +149,8 @@ FILES = \ foo2hbpl2.1in \ foo2ddst.c \ foo2ddst.1in \ + foo2hbpl1.c \ + foo2hbpl1.1in \ cups.h \ xqx.h \ xqxdecode.c \ @@ -187,6 +190,8 @@ FILES = \ foo2hbpl2-wrapper.1in \ foo2ddst-wrapper.in \ foo2ddst-wrapper.1in \ + foo2hbpl1-wrapper.in \ + foo2hbpl1-wrapper.1in \ gamma.ps \ gamma-lookup.ps \ align.ps \ @@ -259,6 +264,7 @@ PROGS+= foo2hiperc hipercdecode PROGS+= foo2hbpl2 hbpldecode PROGS+= gipddecode PROGS+= foo2ddst ddstdecode +PROGS+= foo2hbpl1 ifneq ($(CUPS_SERVERBIN),) ifneq ($(CUPS_DEVEL),) ifneq ($(CUPS_GOODAPI),) @@ -269,7 +275,7 @@ endif SHELLS= foo2zjs-wrapper foo2oak-wrapper foo2hp2600-wrapper \ foo2xqx-wrapper foo2lava-wrapper foo2qpdl-wrapper \ foo2slx-wrapper foo2hiperc-wrapper foo2hbpl2-wrapper \ - foo2ddst-wrapper + foo2ddst-wrapper foo2hbpl1-wrapper SHELLS+= foo2zjs-pstops SHELLS+= printer-profile MANPAGES= foo2zjs-wrapper.1 foo2zjs.1 zjsdecode.1 @@ -282,6 +288,7 @@ MANPAGES+= foo2slx-wrapper.1 foo2slx.1 slxdecode.1 MANPAGES+= foo2hiperc-wrapper.1 foo2hiperc.1 hipercdecode.1 MANPAGES+= foo2hbpl2-wrapper.1 foo2hbpl2.1 hbpldecode.1 MANPAGES+= foo2ddst-wrapper.1 foo2ddst.1 ddstdecode.1 +MANPAGES+= foo2hbpl1-wrapper.1 foo2hbpl1.1 MANPAGES+= gipddecode.1 MANPAGES+= foo2zjs-pstops.1 arm2hpdl.1 usb_printerid.1 MANPAGES+= printer-profile.1 @@ -308,7 +315,8 @@ GSOPTS= -q -dBATCH -dSAFER -dQUIET -dNOPAUSE -sPAPERSIZE=letter -r$(GXR)x$(GYR) JBGOPTS=-m 16 -d 0 -p 92 # Equivalent options for pbmtojbg .SUFFIXES: .ps .pbm .pgm .pgm2 .ppm .ppm2 .zjs .cmyk .pksm .zc .zm .jbg \ - .cups .cupm .1 .1in .fig .gif .xqx .lava .qpdl .slx .hc .hbpl .ddst + .cups .cupm .1 .1in .fig .gif .xqx .lava .qpdl .slx .hc .hbpl \ + .ddst .hbpl1 .fig.gif: fig2dev -L gif $*.fig | giftrans -t "#ffffff" -o $*.gif @@ -381,6 +389,9 @@ JBGOPTS=-m 16 -d 0 -p 92 # Equivalent options for pbmtojbg .pbm.ddst: ./foo2ddst < $*.pbm > $*.ddst +.pbm.hbpl1: + ./foo2hbpl1 < $*.pbm > $*.hbpl1 + # # The usual build rules # @@ -450,6 +461,9 @@ all-done: @echo "yourself." +foo2hbpl1: foo2hbpl1.o + $(CC) $(CFLAGS) -o $@ foo2hbpl1.o $(LDFLAGS) + foo2ddst: foo2ddst.o $(CC) $(CFLAGS) -o $@ foo2ddst.o $(LIBJBG) $(LDFLAGS) @@ -481,6 +495,12 @@ foo2zjs: foo2zjs.o $(CC) $(CFLAGS) -o $@ foo2zjs.o $(LIBJBG) $(LDFLAGS) +foo2hbpl1-wrapper: foo2hbpl1-wrapper.in Makefile + [ ! -f $@ ] || chmod +w $@ + sed < $@.in > $@ \ + -e 's@^PREFIX=.*@PREFIX=$(PREFIX)@' || (rm -f $@ && exit 1) + chmod 555 $@ + foo2ddst-wrapper: foo2ddst-wrapper.in Makefile [ ! -f $@ ] || chmod +w $@ sed < $@.in > $@ \ @@ -838,9 +858,12 @@ install-extra: $(INSTALL) -c -m 644 $$i $(SHAREHC)/icm/; \ fi; \ done - # foo2hbpl ICM files (if any) + # foo2hbpl(2) ICM files (if any) $(INSTALL) $(LPuid) $(LPgid) -m 755 -d $(SHAREHBPL)/icm/ for i in hbpl*.icm; do \ + if [ $$i = hbpl1*.icm ]; then \ + continue; \ + fi; \ if [ -f $$i ]; then \ $(INSTALL) -c -m 644 $$i $(SHAREHBPL)/icm/; \ fi; \ @@ -852,6 +875,13 @@ install-extra: $(INSTALL) -c -m 644 $$i $(SHAREDDST)/icm/; \ fi; \ done + # foo2hbpl1 ICM files (if any) + $(INSTALL) $(LPuid) $(LPgid) -m 755 -d $(SHAREHBPL1)/icm/ + for i in hbpl1*.icm; do \ + if [ -f $$i ]; then \ + $(INSTALL) -c -m 644 $$i $(SHAREHBPL1)/icm/; \ + fi; \ + done MODEL=$(PREFIX)/share/cups/model LOCALMODEL=$(DESTDIR)/local/share/cups/model @@ -1173,8 +1203,9 @@ uninstall: uninstall-aa -rm -f $(MANDIR)/man1/foo2xqx*.1 $(MANDIR)/man1/xqxdecode.1 -rm -f $(MANDIR)/man1/opldecode.1 $(MANDIR)/man1/rodecode.1 -rm -f $(MANDIR)/man1/foo2hiperc*.1 $(MANDIR)/man1/hipercdecode.1 - -rm -f $(MANDIR)/man1/foo2hbpl*.1 $(MANDIR)/man1/hbpldecode.1 + -rm -f $(MANDIR)/man1/foo2hbpl2*.1 $(MANDIR)/man1/hbpldecode.1 -rm -f $(MANDIR)/man1/foo2ddst*.1 $(MANDIR)/man1/ddstdecode.1 + -rm -f $(MANDIR)/man1/foo2hbpl1*.1 -rm -f $(MANDIR)/man1/gipddecode.1 -rm -f $(MANDIR)/man1/arm2hpdl.1 $(MANDIR)/man1/usb_printerid.1 -rm -rf /usr/share/foo2zjs/ @@ -1187,6 +1218,7 @@ uninstall: uninstall-aa -rm -rf /usr/share/foo2hiperc/ -rm -rf /usr/share/foo2hbpl/ -rm -rf /usr/share/foo2ddst/ + -rm -rf /usr/share/foo2hbpl1/ -rm -f /usr/bin/arm2hpdl -rm -f /usr/bin/foo2zjs-wrapper /usr/bin/foo2zjs /usr/bin/zjsdecode -rm -f /usr/bin/foo2oak-wrapper /usr/bin/foo2oak /usr/bin/oakdecode @@ -1200,6 +1232,7 @@ uninstall: uninstall-aa -rm -f /usr/bin/foo2hbpl2-wrapper /usr/bin/foo2hbpl2 -rm -f /usr/bin/hbpldecode -rm -f /usr/bin/foo2ddst-wrapper /usr/bin/foo2ddst /usr/bin/ddstdecode + -rm -f /usr/bin/foo2hbpl1-wrapper /usr/bin/foo2hbpl1 -rm -f /usr/bin/gipddecode -rm -f /usr/bin/opldecode -rm -f /usr/bin/rodecode @@ -1233,6 +1266,7 @@ clean: -rm -f foo2hbpl2.o hbpldecode.o -rm -f opldecode.o gipddecode.o -rm -f foo2dsst.o ddstdecode.o + -rm -f foo2hbpl1.o -rm -f command2foo2lava-pjl.o -rm -f foo2oak.html foo2zjs.html foo2hp.html foo2xqx.html foo2lava.html -rm -f foo2slx.html foo2qpdl.html foo2hiperc.html foo2hbpl.html @@ -1540,6 +1574,7 @@ endif -e "s@\$${URLHC}@$(URLHC)@" \ -e "s@\$${URLHBPL}@$(URLHBPL)@" \ -e "s@\$${URLDDST}@$(URLDDST)@" \ + -e "s@\$${URLHBPL1}@$(URLHBPL1)@" \ -e "s/\$${MODpage}/$$MODpage/" \ -e "s/\$${MODver}/$$MODver/" chmod a-w $*.1 @@ -1580,6 +1615,8 @@ install-man: man $(INSTALL) -c -m 644 foo2ddst.1 $(MANDIR)/man1/ $(INSTALL) -c -m 644 foo2ddst-wrapper.1 $(MANDIR)/man1/ $(INSTALL) -c -m 644 ddstdecode.1 $(MANDIR)/man1/ + $(INSTALL) -c -m 644 foo2hbpl1.1 $(MANDIR)/man1/ + $(INSTALL) -c -m 644 foo2hbpl1-wrapper.1 $(MANDIR)/man1/ $(INSTALL) -c -m 644 gipddecode.1 $(MANDIR)/man1/ $(INSTALL) -c -m 644 foo2zjs-pstops.1 $(MANDIR)/man1/ $(INSTALL) -c -m 644 arm2hpdl.1 $(MANDIR)/man1/ @@ -1693,6 +1730,7 @@ URLSLX=http://foo2slx.rkkda.com URLHC=http://foo2hiperc.rkkda.com URLHBPL=http://foo2hbpl.rkkda.com URLDDST=http://foo2ddst.rkkda.com +URLHBPL1=https://github.com/OpenPrinting/foo2zjs FTPSITE=~/.ncftp-website FTPOPTS= FTPOPTS=-S diff --git a/README b/README index 52e065e..3a5082c 100644 --- a/README +++ b/README @@ -132,6 +132,18 @@ GENERAL - Oki C5100n/C5150n/C5200n - Oki C5500n/C5600n/C5800n + FOO2HBPL1 + --------- + foo2hbpl1 converts Ghostscript pbmraw, pgmraw, ppmraw, or pamcmyk32 + output formats to monochrome or color HBPL version 1 streams. Only + foo2hbpl1 and the wrapper are added here, and they work with: + + - Dell 1250c + - Dell C1660 + - Dell C1760 + - Epson AcuLaser C1700 + - Fuji-Xerox DocuPrint CP105 + FOO2HBPL2 --------- foo2hbpl2 converts Ghostscript pbmraw, bitcmyk, or pksmraw output for‐ diff --git a/README.in b/README.in index 631bbc5..114754f 100644 --- a/README.in +++ b/README.in @@ -132,6 +132,18 @@ GENERAL - Oki C5100n/C5150n/C5200n - Oki C5500n/C5600n/C5800n + FOO2HBPL1 + --------- + foo2hbpl1 converts Ghostscript pbmraw, pgmraw, ppmraw, or pamcmyk32 + output formats to monochrome or color HBPL version 1 streams. Only + foo2hbpl1 and the wrapper are added here, and they work with: + + - Dell 1250c + - Dell C1660 + - Dell C1760 + - Epson AcuLaser C1700 + - Fuji-Xerox DocuPrint CP105 + FOO2HBPL2 --------- foo2hbpl2 converts Ghostscript pbmraw, bitcmyk, or pksmraw output for‐ diff --git a/foo2hbpl1-wrapper.1in b/foo2hbpl1-wrapper.1in new file mode 100644 index 0000000..fe0f5d0 --- /dev/null +++ b/foo2hbpl1-wrapper.1in @@ -0,0 +1,226 @@ +'\" t +.TH foo2hbpl1-wrapper 1 "${MODpage}" "foo2hbpl1-wrapper ${MODver}" +#ifndef OLDGROFF +#include "macros.man" +#endif +'\"========================================================================== +'\" MANUAL PAGE SOURCE +'\"========================================================================== +.SH NAME +foo2hbpl1-wrapper \- Convert Postscript into a HBPLv1 printer stream +.SH SYNOPSIS +.B foo2hbpl1-wrapper +.RI [ options "] [" ps-file ] +.SH DESCRIPTION +.B foo2hbpl1-wrapper +is a Foomatic compatible printer wrapper for the \fBfoo2hbpl1\fP printer +driver. This script reads a Postscript \fIps-file\fP or standard input and +converts it to Host Based Printer Language (v1) format for driving the +Dell 1250c, Dell C1660w, Dell C1760w, +Epson AcuLaser C1700, Fuji-Xerox CP105b, +Xerox Phaser 6000B and the Xerox Phaser 6010N printers. +.P +This script can be used in a standalone fashion, but is intended to +be called from a printer spooler system which uses the Foomatic +printer database. + +.SH COMMAND LINE OPTIONS +.SS Normal Options +These are the options used to select the parameters of a +print job that are usually controlled on a per job basis. +.TP +.BI \-c +Print in color (else monochrome). +.TP +.BI \-C\0 colormode +Color correction mode [0]. +.TS +n l . +10 ICM color profile (using \-G *.icm file) +.TE +.TP +.BI \-m\0 media +Media code to send to printer [1] or [6]. +.TS +l r r r r. +_ +Media \-m \-m \-m \-m +\^ \-z0 \-z0 \-z1 \-z1 +\^ side1 side2 side1 side2 +_ +plain 1 8 1 8 +thick 2 9 2 9 +high-quality 3 10 3 10 +coated 4 11 4 11 +label 5 \- 5 \- +envelope 6 \- 6 \- +recycled 7 12 7 12 +letterhead \- \- 13 14 +preprinted \- \- 15 16 +prepunched \- \- 17 18 +color \- \- 19 20 +user1 \- \- 21 22 +special \- \- 23 24 +.TE +.TP +.BI \-p\0 paper +Paper code sizes autodetected by printer [0]. +.TS +box; +| n l | n l. +0 Letter 1 Legal +2 A4 3 Executive +\- \- \- \- +6 env #10 7 env Monarch +8 env C5 9 env DL +\- \- 11 B5jis +\- \- \- \- +\- \- 15 A5 +205 Folio 255 Custom (X x Y) +.TE +.TP +.BI \-n\0 copies +Number of copies of each page [1]. Only available for \-z1 printers. +.TS +l l l. +_ +Model Range Example printer(s) +_ +z0 \- Dell 1250c / C1660 / C1760 +z1 1-999 Xerox 6000 / 6010 +.TE +.TP +.BI \-t\0 +Draft mode. Every other pixel is white. +.TP +.BI "\-2 \-3 \-4 \-5 \-6 \-8 \-9 \-10 \-12 \-14 \-15 \-16 \-18" +Print in N-up. Requires the \fBpsutils\fP package. +.TP +.BI \-o\0 orient +Orientation used for N-up. +.TS +l l l. +Portrait \-op (normal) +Landscape \-ol (rotated 90 degrees anticlockwise) +Seascape \-os (rotated 90 degrees clockwise) +.TE +.SS Printer Tweaking Options +These are the options used to customize the operation of \fBfoo2hbpl1\fP +for a particular printer. +.TP +.BI \-u\0 xoff x yoff +Set the offset of the start of the printable region from the +upper left corner, in pixels [20x20]. +.TP +.BI \-l\0 xoff x yoff +Set the offset of the end of the printable region from the +lower right corner, in pixels [20x20]. +.TP +.BI \-L\0 mask +Send logical clipping amounts implied by \-u/-l in the HBPL1 Stream [3]. +\fBfoo2hbpl1-wrapper\fP always runs Ghostscript with the ideal page +dimensions, so that the scale of the image is correct, +regardless whether or not the printer has unprintable regions. +This option is used to move the position of the clipped image +back to where it belongs on the page. The default is to send +the amount which was clipped by \-u and \-l, and should be +good in most cases. +.TS +l l. +0 don't send any logical clipping amounts +1 only send Y clipping amount +2 only send X clipping amount +3 send both X and Y clipping amounts +.TE +.TP +.BI \-A +AllIsBlack: convert C=1,M=1,Y=1 to just K=1. Works with color input only. +.TP +.BI \-B +BlackClears: K=1 forces C,M,Y to 0. Works with color input only. +.TP +.BI \-z\0 model +Model. Default is [0]. +.TS +l l. +_ +Model Example printer(s) +_ +z0 Dell 1250c / C1660 / C1760 +z1 Xerox 6000 / 6010 +.TE +.SS Color Tweaking Options +These are the options used to control the quality of color output. +Color correction is currently a WORK IN PROGRESS. +.TP +.BI \-g\0 gsopts +Additional options to pass to Ghostscript, such as \-g\(lq-dDITHERPPI=nnn\(rq, +etc. This option may appear more than once. +.TP +.BI \-G\0 profile.icm +Convert \fIprofile.icm\fP to a Postscript color rendering +dictionary (CRD) using \fBfoo2zjs-icc2ps\fP and +adjust the printer colors by using the Postscript \fBsetcolorrendering\fP +operator. (WORK IN PROGRESS). +.TP +.BI \-G\0 gamma-file.ps +Prepend \fIgamma-file.ps\fP to the Postscript input to perform +color correction using the \fBsetcolortransfer\fP Postscript operator. +For example, the file might contain: +.br +{0.333 exp} {0.333 exp} {0.333 exp} {0.333 exp} setcolortransfer +.TP +.BI \-I\0 intent +Select profile intent from the ICM file. +0=Perceptual, 1=Colorimetric, 2=Saturation, 3=Absolute. +Default is 0 (perceptual). +.SS Debugging Options +These options are used for debugging \fBfoo2hbpl1\fP and its wrapper. +.TP +.BI \-S\0 plane +Output just a single color plane from a color print and print it +on the black plane. The default is to output all color planes. +.TS +l l. +1 Cyan +2 Magenta +3 Yellow +4 Black +.TE +.TP +.BI \-D\0 level +Set Debug level [0]. + +.SH EXAMPLES +Create a monochrome HBPL1v1 from a Postscript document, +examine it, and then print it using a RAW print queue: + +.RS +.nf +foo2hbpl1-wrapper testpage.ps > testpage.prn +hbpldecode < testpage.prn +lpr \-P raw testpage.prn +.fi +.RE +.P +Create a color HBPL1v1 stream from a Postscript document: + +.RS +.nf +foo2hbpl1-wrapper \-c testpage.ps > testpage.prn +.fi +.RE + +.SH FILES +.BR /usr/bin/foo2hbpl1-wrapper +.SH SEE ALSO +.BR foo2hbpl1 (1), +.BR hbpldecode (1) +.SH "AUTHOR" +Rick Richardson +.br +${URLHBPL1}/ +'\" +'\" +'\" +.em pdf_outline diff --git a/foo2hbpl1-wrapper.in b/foo2hbpl1-wrapper.in new file mode 100644 index 0000000..19a4625 --- /dev/null +++ b/foo2hbpl1-wrapper.in @@ -0,0 +1,645 @@ +#!/bin/sh + +#* Copyright (C) 2013 Rick Richardson +#* +#* This program is free software; you can redistribute it and/or modify +#* it under the terms of the GNU General Public License as published by +#* the Free Software Foundation; either version 2 of the License, or +#* (at your option) any later version. +#* +#* This program is distributed in the hope that it will be useful, +#* but WITHOUT ANY WARRANTY; without even the implied warranty of +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +#* GNU General Public License for more details. +#* +#* You should have received a copy of the GNU General Public License +#* along with this program; if not, see +#* . +#* +#* Authors: Rick Richardson + +VERSION='$Id: foo2hbpl1-wrapper.in,v 1.4 2026/07/14 12:00:00 joe Exp $' + +# +# Printer Notes: +# +# Dell 1250c +# Dell C1660w +# Dell C1760w +# Epson AcuLaser C1700 +# Fuji-Xerox DocuPrint CP105b +# Xerox Phaser 6000B -z1 +# and the Xerox Phaser 6010N -z1 +# + +PROGNAME="$0" +BASENAME=`basename $PROGNAME` +PREFIX=/usr +SHARE=$PREFIX/share/foo2hbpl1 +PATH=$PATH:/sw/bin:/opt/local/bin + +# +# Log the command line, for debugging and problem reports +# +if [ -x /usr/bin/logger -o -x /bin/logger ]; then + logger -t "$BASENAME" -p lpr.info -- "$BASENAME $@" x Set offset of upper left clip margin in pixels [20x20] +-l x Set offset of lower right clip margin in pixels [20x20] +-L mask Send logical clipping values from -u/-l in HBPL1 Stream [3] + 0=no, 1=Y, 2=X, 3=XY +-A AllIsBlack: convert C=1,M=1,Y=1 to just K=1 +-B BlackClears: K=1 forces C,M,Y to 0 +-z model Model: [0] + 0=(default) (Need more info for list) + 1=(example: Xerox 6000/6010) + +Color Tweaking Options: +-g gsopts Additional options to pass to Ghostscript, such as + -dDITHERPPI=nnn, etc. May appear more than once. [] +-G profile.icm Convert profile.icm to a Postscript CRD using icc2ps and + adjust colors using the setcolorrendering PS operator. + $SHARE/icm/ will be searched for profile.icm. +-I intent Select profile intent from ICM file [$INTENT] + 0=Perceptual, 1=Colorimetric, 2=Saturation, 3=Absolute +-G gamma-file.ps Prepend gamma-file to the Postscript input to perform + color correction using the setcolortransfer PS operator. + +Debugging Options: +-S plane Output just a single color plane from a color print + 1=Cyan, 2=Magenta, 3=Yellow, 4=Black +-D lvl Set Debug level [$DEBUG] +-V $VERSION +EOF + + exit 1 +} + +error() { + # Report an error and exit + echo "$BASENAME: $1" >&2 + exit 1 +} + +dbgcmd() { + if [ $DEBUG -ge 1 ]; then + echo "$@" >&2 + fi + "$@" +} + +set_clipping() { + # Set clipping region if it isn't already set + ulx=$1; uly=$2 + lrx=$3; lry=$4 + + if [ "$CLIP_UL" = "" ]; then + if [ "$ulx" -le 10 ]; then ulx=11; fi + if [ "$uly" -le 10 ]; then uly=11; fi + if [ "$MODEL" -lt 1 ]; then + if [ "$ulx" -le 32 ]; then ulx=33; fi + if [ "$uly" -le 32 ]; then uly=33; fi + fi + CLIP_UL="-u ${ulx}x${uly}" + fi + + if [ "$CLIP_LR" = "" ]; then + if [ "$lrx" -le 10 ]; then lrx=11; fi + if [ "$lry" -le 10 ]; then lry=11; fi + if [ "$MODEL" -lt 1 ]; then + if [ "$lrx" -le 32 ]; then lrx=33; fi + if [ "$lry" -le 32 ]; then lry=33; fi + fi + CLIP_LR="-l ${lrx}x${lry}" + fi +} + +# +# N-up-ify the job. Requires psnup from psutils package +# +nup() { + # N-up-ify the job. Requires psnup from psutils package + case "$NUP" in + [2368]|1[0458]) + tr '\r' '\n' | psnup $NUP_ORIENT -d2 -$NUP -m.3in -p$paper -q + ;; + [49]|1[26]) + tr '\r' '\n' | psnup $NUP_ORIENT -d2 -$NUP -m.5in -p$paper -q + ;; + *) + error "Illegal call to nup()." + ;; + esac +} + +# +# Process the options +# + +# Try to use a local copy of GhostScript 8.54, if available. Otherwise, +# fallback to whatever the Linux distro has installed (usually 7.07) +# +# N.B. := operator used here, when :- would be better, because "ash" +# doesn't have :- +if eval gs.foo -v >/dev/null 2>&1; then + GSBIN=${GSBIN:-gs.foo} +else + GSBIN=${GSBIN:-gs} +fi + +CMDLINE="$*" +DEBUG=0 +COLOR= +COLORMODE=default +MODEL=0 +MEDIA= +COPIES=1 +PAPER= +RES=600x600 # do not change this +NUP= +CLIP_UL= +CLIP_LR= +CLIP_LOG= +# default as 1. Should save Toner in CYMK +BC="-B" +AIB="-A" +COLOR2MONO= +GAMMAFILE=default +INTENT=0 +GSOPTS= +SAVETONER= +NUP_ORIENT= +GSDEV=-sDEVICE=pgmraw +# What mode to use if the user wants us to pick the "best" mode +case `$GSBIN --version` in +7*) DEFAULTCOLORMODE=10 + DEFAULTCOLORMODE=1 + ;; +8.1*) + DEFAULTCOLORMODE=1 + QUALITY=1 + ;; +*) DEFAULTCOLORMODE=1 + ;; +esac +while getopts "1:23456789o:b:cC:g:l:u:L:m:n:p:q:tz:ABS:D:G:I:Vh?" opt +do + case $opt in + b) GSBIN="$OPTARG";; + c) COLOR=-c;; + g) GSOPTS="$GSOPTS $OPTARG";; + m) MEDIA="$OPTARG";; + n) COPIES="$OPTARG";; + p) PAPER="$OPTARG";; + q) QUALITY="$OPTARG";; + t) SAVETONER="-t";; + z) MODEL="$OPTARG";; + l) CLIP_LR="-l $OPTARG";; + u) CLIP_UL="-u $OPTARG";; + L) CLIP_LOG="-L $OPTARG";; + A) AIB=-A;; + B) BC=-B;; + C) COLORMODE="$OPTARG";; + S) COLOR2MONO="-S$OPTARG";; + D) DEBUG="$OPTARG";; + G) GAMMAFILE="$OPTARG";; + I) INTENT="$OPTARG";; + [234689]) NUP="$opt";; + [57]) error "Can't find acceptable layout for $opt-up";; + 1) case "$OPTARG" in + [024568]) NUP="1$OPTARG";; + *) error "Can't find acceptable layout for 1$OPTARG-up";; + esac + ;; + o) case "$OPTARG" in + l*) NUP_ORIENT=-l;; + s*) NUP_ORIENT=-r;; + p*|*) NUP_ORIENT=;; + esac;; + V) echo "$VERSION"; foo2hbpl1 -V; foo2zjs-pstops -V; exit 0;; + h|\?) + if [ "$CMDLINE" != "-?" -a "$CMDLINE" != -h ]; then + echo "Illegal command:" + echo " $0 $CMDLINE" + echo + fi + usage;; + esac +done +shift `expr $OPTIND - 1` + +# +# If there is an argument left, take it as the file to print. +# Else, the input comes from stdin. +# +if [ $# -ge 1 ]; then + if [ "$LPJOB" = "" ]; then + : # LPJOB="$1" + fi + exec < $1 +fi + +# +# Figure out the paper dimensions in pixels/inch, and set the +# default clipping region. Unfortunately, this is a trouble +# area for ZjStream printers. Various versions of ZjS print +# engines react differently when asked to print into their +# unprintable regions. +# + +if [ "$PAPER" = "" ]; then PAPER=letter; fi +case "$PAPER" in +Custom*x*) + # Command line only + XDIM=`echo "$PAPER" | sed -e "s/Custom.//" -e "s/x.*//" ` + YDIM=`echo "$PAPER" | sed -e "s/.*x//" ` + if [ "$XDIM" = "" -o "$YDIM" = "" ]; then + error "Custom page size '$PAPER' != 1-99999" + fi + if [ "$XDIM" -eq 0 -o "$YDIM" -eq 0 ]; then + error "Custom page size '$PAPER' != 1-99999" + fi + XDIM=`dc -e "$XDIM 600* 72/p"` + YDIM=`dc -e "$YDIM 600* 72/p"` + PAPER=255; paper=custom; set_clipping 50 50 50 50 + ;; +Custom*) + #%%BeginFeature: *CustomPageSize True + #216 + #360 + #0 + #0 + #0 + #pop pop pop pop pop + + #%%BeginFeature: *CustomPageSize True + #792.000000 612.000000 1 0.000000 0.000000 + #pop pop pop pop pop + + if [ $DEBUG = 0 ]; then + TMPFILE=/tmp/cus$$ + else + TMPFILE=/tmp/custom.ps + fi + cat >$TMPFILE + exec <$TMPFILE + + tmp=`head -n 10000 $TMPFILE \ + | sed -n '/CustomPageSize/{n;p;n;p;}' \ + | tr '\n' ' '` + case "$tmp" in + [0-9]*\ [0-9]*) + XDIM=`echo "$tmp" | sed 's/ .*//'` + YDIM=`echo "$tmp" | sed -e 's/^[^ ]* //' -e 's/ .*//'` + ;; + *) + if [ $DEBUG = 0 ]; then rm -f $TMPFILE; fi + error "Custom page size [XY]DIM != 1-99999" + ;; + esac + XDIM=`dc -e "$XDIM 600* 72/p"` + YDIM=`dc -e "$YDIM 600* 72/p"` + PAPER=255; paper=custom; set_clipping 50 50 50 50 + ;; + +# Use names that Ghostscript recognizes, referring to +# /usr/share/ghostscript/9.10/Resource/Init/gs_statd.ps +# foo2hbpl1 will provide the appropriate numeric value + +0|letter|Letter) + PAPER=0; paper=letter + XDIM="5100"; YDIM="6600"; set_clipping 50 50 50 50;; +1|legal|Legal) + PAPER=1; paper=legal + XDIM="5100"; YDIM="8400"; set_clipping 50 50 50 50;; +2|A4|a4) PAPER=2; paper=a4 + XDIM="4960"; YDIM="7016"; set_clipping 50 50 50 50;; +3|executive|Executive) + PAPER=3; paper=executive + XDIM="4350"; YDIM="6300"; set_clipping 50 50 50 50;; +6|env10|Env10|"env#10") + PAPER=6; paper=env10 + XDIM="2475"; YDIM="5700"; set_clipping 50 50 50 50 + if [ "$MEDIA" = "" ]; then MEDIA=envelope; fi;; +7|monarch|Monarch|EnvMonarch|envMonarch) + PAPER=7; paper=envMonarch + XDIM="2325"; YDIM="4500"; set_clipping 50 50 50 50 + if [ "$MEDIA" = "" ]; then MEDIA=envelope; fi;; +8|C5|c5|envC5|EnvC5) + PAPER=8; paper=envC5 + XDIM="3827"; YDIM="5400"; set_clipping 50 50 50 50 + if [ "$MEDIA" = "" ]; then MEDIA=envelope; fi;; +9|DL|dl|envDL|EnvDL) + PAPER=9; paper=envD5 + XDIM="2600"; YDIM="5197"; set_clipping 50 50 50 50 + if [ "$MEDIA" = "" ]; then MEDIA=envelope; fi;; +11|jisB5|B5jis|b5jis) + PAPER=11; paper=b5 + XDIM="4299"; YDIM="6070"; set_clipping 50 50 50 50;; +15|A5|a5) PAPER=15; paper=a5 + XDIM="3496"; YDIM="4960"; set_clipping 50 50 50 50;; +205|folio|Folio) + PAPER=205; paper=folio + XDIM="5100"; YDIM="7800"; set_clipping 50 50 50 50;; +*) error "Unimplemented paper code $PAPER";; +esac +DIM="${XDIM}x${YDIM}" +PAPERSIZE="-sPAPERSIZE=$paper"; + +#set_clipping 47 48 51 46 # exact values for Dell C1660w on letter paper + set_clipping 51 51 51 51 # default is 0.085 inches all around + +# +# Validate media code +# +if [ "$MEDIA" = "" ]; then MEDIA=plain; fi +case "$MEDIA" in +1|plain) MEDIA=1;; +2|thick) MEDIA=2;; +3|highquality) MEDIA=3;; +4|coated) MEDIA=4;; +5|labels) MEDIA=5;; +6|envelope) MEDIA=6;; +7|recycled) MEDIA=7;; +8|plain2) MEDIA=8;; +9|thick2) MEDIA=9;; +10|highquality2) MEDIA=10;; +11|coated2) MEDIA=11;; +12|recycled2) MEDIA=12;; +13|letterhead) MEDIA=13;; # -z1 +14|letterhead2) MEDIA=14;; # -z1 +15|preprinted) MEDIA=15;; # -z1 +16|preprinted2) MEDIA=16;; # -z1 +17|prepunched) MEDIA=17;; # -z1 +18|prepunched2) MEDIA=18;; # -z1 +19|color) MEDIA=19;; # -z1 +20|color2) MEDIA=20;; # -z1 +21|custom) MEDIA=21;; # -z1 +22|custom2) MEDIA=22;; # -z1 +23|special) MEDIA=23;; # -z1 +24|special2) MEDIA=24;; # -z1 +[0-9]*) ;; +*) error "Unknown media code $MEDIA";; +esac + +# -z0 does not accept this option, -z1+ accepts -n[1..999] +case "$MODEL" in + 0) COPIES="";; + 1) COPIES="-n$COPIES";; +*) +esac + +# +# Filter thru psnup if N-up printing has been requested +# +case $NUP in +[234689]|1[024568]) PREFILTER="nup";; +*) PREFILTER=cat;; +esac +if [ "$DEBUG" -ge 9 ]; then + PREFILTER="tee /tmp/$BASENAME.ps" +fi + +# +# Overload -G. If the file name ends with ".icm" or ".ICM" +# then convert the ICC color profile to a Postscript CRD, +# then prepend it to the users job. Select the intent +# using the -I option. +# + +create_crd() { + # + # Create a Postscript CRD + # + ICC2PS=$PREFIX/bin/foo2zjs-icc2ps + if [ -x $ICC2PS ]; then + case "$GAMMAFILE" in + none | none.icm | */none.icm) + ;; + *) + if [ -x /usr/bin/logger ]; then + logger -t "$BASENAME" -p lpr.info -- \ + "`basename $ICC2PS` -o $GAMMAFILE -t$INTENT > $ICCTMP.crd.ps" + fi + $ICC2PS -o $GAMMAFILE -t$INTENT > $ICCTMP.crd.ps 2>$ICCTMP.log \ + || error "Problem converting .ICM file to Postscript" + ;; + esac + + PSTOPS_OPTS="$PSTOPS_OPTS -c" + cat > $ICCTMP.usecie.ps <<-EOF + %!PS-Adobe-3.0 + <>setpagedevice + EOF + if [ "$QUALITY" = wts -o "$QUALITY" = as ]; then + if [ "$QUALITY" = wts ]; then + cat >> $ICCTMP.usecie.ps <<-EOF + << /UseWTS true >> setuserparams + EOF + else + cat >> $ICCTMP.usecie.ps <<-EOF + << /UseWTS false >> setuserparams + EOF + fi + cat >> $ICCTMP.usecie.ps <<-EOF + << + /AccurateScreens true + /HalftoneType 1 + /HalftoneName (Round Dot Screen) cvn + /SpotFunction { 180 mul cos exch 180 mul cos add 2 div} + /Frequency 137 + /Angle 37 + >> sethalftone + EOF + fi + cat > $ICCTMP.selcrd.ps <<-EOF + /Current /ColorRendering findresource setcolorrendering + EOF + case "$GAMMAFILE" in + none | none.icm | */none.icm) GAMMAFILE="$ICCTMP.usecie.ps";; + *) GAMMAFILE="$ICCTMP.usecie.ps $ICCTMP.crd.ps $ICCTMP.selcrd.ps";; + esac + else + GAMMAFILE= + fi +} + +if [ $DEBUG -gt 0 ]; then + ICCTMP=/tmp/icc +else + ICCTMP=/tmp/icc$$ +fi + +if [ "" = "$COLOR" ]; then + COLORMODE= + GAMMAFILE= +else + case "$COLORMODE" in + default) COLORMODE=$DEFAULTCOLORMODE;; + esac + case "$GAMMAFILE" in + default) GAMMAFILE=samclp300-0.icm;; + esac +fi + +CRDBASE="$PREFIX/share/foo2hbpl1/crd" +case "$MODEL" in + 0) model=CLP-300;; + 1) model=CLP-600;; + 2) model=CLP-600;; + 3) model=CLP-600;; +esac + +PSTOPS_OPTS="-n" + +case "$COLORMODE" in +0|"") + # Monochrome + ;; +10|icm) + # Use old ICM method + AIB=-A + BC=-B + case "$GAMMAFILE" in + none | none.icm | */none.icm) + create_crd + ;; + *.icm|*.ICM|*.icc|*.ICC) + # + # Its really an .ICM file, not a gamma file. + # + # The file can be a full path name, or the name of a file in $SHARE/icm/ + # + if [ -r "$GAMMAFILE" ]; then + create_crd + elif [ -r "$SHARE/icm/$GAMMAFILE" ]; then + GAMMAFILE="$SHARE/icm/$GAMMAFILE" + create_crd + else + GAMMAFILE= + fi + ;; + esac + ;; +1|crd) + # CRD + GAMMAFILE="" + GAMMAFILE="$GAMMAFILE $CRDBASE/${model}cms" + GAMMAFILE="$GAMMAFILE $CRDBASE/$SCREEN" + # Black text... + TMPFILE2=/tmp/black$$ + cat $CRDBASE/black-text.ps - >$TMPFILE2 + exec <$TMPFILE2 + ;; +*.crd) + GAMMAFILE="$CRDBASE/prolog.ps" + if [ -f $COLORMODE ]; then + GAMMAFILE="$GAMMAFILE $COLORMODE" + elif [ -f $CRDBASE/$COLORMODE ]; then + GAMMAFILE="$GAMMAFILE $CRDBASE/$COLORMODE" + else + error "Can't find CRD '$COLORMODE' in . or in $CRDBASE" + fi + GAMMAFILE="$GAMMAFILE $CRDBASE/$SCREEN" + ;; +*) + error "Unknown color method '$COLORMODE'" + ;; +esac + +if [ "" != "$COLOR" ]; then + GSDEV=-sDEVICE=pamcmyk32 + $GSBIN --help | grep -q pamcmyk32 || GSDEV=-sDEVICE=ppmraw +fi + +# +# Figure out USERNAME +# +if [ "$LPUSER" != "" ]; then + USER="$LPUSER@$LPHOST" +else + USER="" +fi + +# +# Main Program, just cobble together the pipeline and run it +# +# The malarky with file descriptors 1 and 3 is to avoid a bug in +# (some versions?) of Ghostscript where Postscript's stdout gets +# intermingled with the printer drivers output, resulting in +# corrupted image data. +# +GS="$GSBIN -q -dBATCH -dSAFER -dQUIET -dNOPAUSE" + +foo2zjs-pstops $PSTOPS_OPTS | \ +$PREFILTER \ +| ($GS $PAPERSIZE -g$DIM -r$RES $GSDEV $GSOPTS \ + -sOutputFile="|cat 1>&3" $GAMMAFILE -_ >/dev/null) 3>&1 \ +| foo2hbpl1 -m$MEDIA $COPIES $CLIP_UL $CLIP_LR $CLIP_LOG \ + $SAVETONER $COLOR2MONO -D$DEBUG -J "$LPJOB" -U "$USER" $BC $AIB + +# +# Log the command line, for debugging and problem reports +# +if [ -x /usr/bin/logger ]; then + logger -t "$BASENAME" -p lpr.info -- \ + "$GSBIN $PAPERSIZE -g$DIM -r$RES $GSDEV $GSOPTS $GAMMAFILE" + logger -t "$BASENAME" -p lpr.info -- \ + "foo2hbpl1 -m$MEDIA $COPIES $CLIP_UL $CLIP_LR $CLIP_LOG \ + $SAVETONER $COLOR2MONO -D$DEBUG $BC $AIB" +fi + +# +# Remove cruft +# +if [ $DEBUG -eq 0 ]; then + for i in crd.ps log usecie.ps selcrd.ps + do + file="$ICCTMP.$i" + [ -f $file ] && rm -f $file + done + [ -f "$TMPFILE" ] && rm -f $TMPFILE + [ -f "$TMPFILE2" ] && rm -f $TMPFILE2 +fi + +exit 0 diff --git a/foo2hbpl1.1in b/foo2hbpl1.1in new file mode 100644 index 0000000..8df0a0d --- /dev/null +++ b/foo2hbpl1.1in @@ -0,0 +1,186 @@ +'\" t +.TH foo2hbpl1 1 "${MODpage}" "foo2hbpl1 ${MODver}" +#ifndef OLDGROFF +#include "macros.man" +#endif +'\"========================================================================== +'\" MANUAL PAGE SOURCE +'\"========================================================================== +.SH NAME +foo2hbpl1 \- Convert Ghostscript output to an HBPLv1 printer stream +.SH SYNOPSIS +.B foo2hbpl1 +.RI [ options "] <" pnm-or-pam-file " >" hbpl1-file +.SH DESCRIPTION +.B foo2hbpl1 +converts Ghostscript pbmraw, pgmraw, ppmraw, or pamcmyk32 output +formats to Monochrome or Color HBPL version 1 streams, to drive +HBPLv1 compatible printers like the +Dell 1250c, Dell C1660w, Dell C1760w, +Epson AcuLaser C1700, Fuji-Xerox CP105b, +Xerox Phaser 6000B and the Xerox Phaser 6010N. + +.SH COMMAND LINE OPTIONS +.SS Normal Options +These are the options used to select the parameters of a +print job that are usually controlled on a per job basis. +.TP +.BI \-m\0 media +Media code to send to printer [1] or [6]. +.TS +l r r r r. +_ +Media \-m \-m \-m \-m +\^ \-z0 \-z0 \-z1 \-z1 +\^ side1 side2 side1 side2 +_ +plain 1 8 1 8 +thick 2 9 2 9 +high-quality 3 10 3 10 +coated 4 11 4 11 +label 5 \- 5 \- +envelope 6 \- 6 \- +recycled 7 12 7 12 +letterhead \- \- 13 14 +preprinted \- \- 15 16 +prepunched \- \- 17 18 +color \- \- 19 20 +user1 \- \- 21 22 +special \- \- 23 24 +.TE +.TP +.BI \-p\0 paper +Paper code sizes autodetected by printer [0]. +.TS +box; +| n l | n l. +0 Letter 1 Legal +2 A4 3 Executive +\- \- \- \- +6 env #10 7 env Monarch +8 env C5 9 env DL +\- \- 11 B5jis +\- \- \- \- +\- \- 15 A5 +205 Folio 255 Custom (X x Y) +.TE +.TP +.BI \-n\0 copies +Number of copies of each page [1]. Only available for \-z1 printers. +.TS +l l l. +_ +Model Range Example printer(s) +_ +z0 \- Dell 1250c / C1660 / C1760 +z1 1-999 Xerox 6000 / 6010 +.TE +.TP +.BI \-t\0 +Draft mode. Every other pixel is white. +.TP +.BI \-J\0 filename +Filename string to send to printer. +.TP +.BI \-U\0 username +Username string to send to printer. +.SS Printer Tweaking Options +These are the options used to customize the operation of \fBfoo2hbpl1\fP +for a particular printer. +.TP +.BI \-u\0 xoff x yoff +Set the offset of the start of the printable region from the +upper left corner, in pixels [20x20]. +.TP +.BI \-l\0 xoff x yoff +Set the offset of the end of the printable region from the +lower right corner, in pixels [20x20]. +.TP +.BI \-L\0 mask +Send logical clipping amounts implied by \-u/-l in the HBPL1 Stream [3]. +.TS +l l. +0 don't send any logical clipping amounts +1 only send Y clipping amount +2 only send X clipping amount +3 send both X and Y clipping amounts +.TE +.TP +.BI \-A +AllIsBlack: convert C=1,M=1,Y=1 to just K=1. Works with color input only. +.TP +.BI \-B +BlackClears: K=1 forces C,M,Y to 0. Works with color input only. +.TP +.BI \-z\0 model +Model. Default is [0]. +.TS +l l. +_ +Model Example printer(s) +_ +z0 Dell 1250c / C1660 / C1760 +z1 Xerox 6000 / 6010 +.TE +.SS Debugging Options +These options are used for debugging \fBfoo2hbpl1\fP. +.TP +.BI \-S\0 plane +Output just a single color plane from a color print and print it +on the black plane. The default is to output all color planes. +.TS +l l. +1 Cyan +2 Magenta +3 Yellow +4 Black +.TE +.TP +.BI \-D\0 level +Set Debug level [0]. +.TP +.BI \-V\0 +Show \fBfoo2hbpl1\fP Version. + +.SH EXAMPLES +Create a black and white HBPL1v1 stream: + +.RS +.nf +gs \-q \-dBATCH \-dSAFER \-dQUIET \-dNOPAUSE \ + \-sPAPERSIZE=letter \-r600x600 \-sDEVICE=pgmraw \ + \-sOutputFile=- \- < testpage.ps \ +| foo2hbpl1 >testpage.zm +.fi +.RE +.P +Create a color HBPL1v1 stream: + +.RS +.nf +gs \-q \-dBATCH \-dSAFER \-dQUIET \-dNOPAUSE \ + \-sPAPERSIZE=letter \-g5100x6600 \-r600x600 \-sDEVICE=pamcmyk32 \ + \-sOutputFile=- \- < testpage.ps \ +| foo2hbpl1 >testpage.zc +.fi +.RE + +.SH FILES +.BR /usr/bin/foo2hbpl1 +.SH SEE ALSO +.BR foo2hbpl1-wrapper (1), +.BR hbpldecode (1) +.SH "AUTHOR" +Rick Richardson +.br +Peter Korf +.br +Dave Coffin +.br +Joe Da Silva +.br +${URLHBPL1}/ +'\" +'\" +'\" +.em pdf_outline diff --git a/foo2hbpl1.c b/foo2hbpl1.c new file mode 100644 index 0000000..4e7245b --- /dev/null +++ b/foo2hbpl1.c @@ -0,0 +1,1178 @@ +/* + +GENERAL +This program converts bilevel PBM, 8-bit PGM, 24-bit PPM, and 32-bit +CMYK PAM files (output by Ghostscript as "pbmraw", "pgmraw", "ppmraw", +and "pamcmyk32" respectively) to HBPL version 1 for the consumption +of various Dell, Epson, and Fuji-Xerox printers. + +With this utility, you can print to some Dell and Fuji printers, such as these: + - Generic HBPL1 Printer B/W and Color + - Dell 1250c B/W and Color + - Dell C1660 B/W and Color + - Dell C1760 B/W and Color + - Epson AcuLaser C1700 B/W and Color + - Fuji-Xerox DocuPrint CP105 B/W and Color +These -z1 printers appear to recognize an extended command set: + - Generic HBPL1 z1 Printer B/W and Color -z1 + - Xerox Phaser 6000B B/W and Color -z1 + - Xerox Phaser 6010N B/W and Color -z1 +Printers listed above are personal type with 1 autotray (maybe plus a manual feed) +xml and PPD files that use this driver are located in package foomatic-db + +AUTHORS +This program began life as Robert Szalai's 'pbmtozjs' program, +and then overhauled by Rick Richardson with several improvements. +foo2hbpl1.c originally began life as foo2hbpl2.c and was modified +for use with HBPLv1 type printers by Dave Coffin in March 2014. +Several improvements added later in August 2026 by Joe Da Silva. + +LICENSE +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see +. + +*/ + +static const char Version[] = "$Id: foo2hbpl1.c,v 1.4 2026/07/14 12:00:00 joe Exp $"; + +#include +#include +#include +#include +#include +#include +#include +#ifdef linux + #include +#endif + +#define LOGICAL_CLIP_X 2 +#define LOGICAL_CLIP_Y 1 +#define PRINTMAX 20*1024*1024 + +typedef struct +{ + unsigned char *buf; + int size, off, bits; +} Stream; + +typedef struct +{ + int AllIsBlack; + int BlackClears; + int Color2Mono; // default=0=off + int Copies; // [1..999] Page Copies (default=1) + int Debug; // Debug>=9 if md5sum testpage.ps results + int MediaCode; // -1=undefined (default to paper) + int PaperCode; // (default=letter) + int LogicalClip; // default=0=no, 1=Y, 2=X, 3=XY + int Model; // -1=undefined (default -z0) + int pagenum; // no pages, no printer codes sent + int SaveToner; // -t, every second pixel is blank + int PrintSize; // printer job byte count, <= 50M + int PageWidth; + int PageHeight; + int Width; + int Height; + int UpperLeftX; // clipping margins [left,top,right,bottom] + int UpperLeftY; + int LowerRightX; + int LowerRightY; + Stream stream[5]; + unsigned char *image; // (height+2)x(width+4)x(deep,1=GRAY,4=YCMK) + char *Username; + char *Filename; +} Job; + +static const char *mname[2+24] = { //Known media types + "COATEDPAPER2", // z1/--, 4=coated, light weight glossy card? (z1) + "RECYCLE", // z1/--, 7=recycled paper + // maintain -m[1..12] media compatibility sequence for -z0,-z1,-zX... + "NORMAL", // z1/z0, 1=plain paper + "THICK", // z1/z0, 2=thick, bond paper + "HIGHQUALITY", // z1/z0, 3=high-quality, premium, cotton? + "COAT2", // --/z0, 4=coated, light weight glossy card? (z0) + "LABEL", // z1/z0, 5=label + "ENVELOPE", // z1/z0, 6=envelope + "RECYCLED", // --/z0, 7=recycled paper + "NORMALREV", // z1/z0, 8=plain paper (side2) + "THICKSIDE2", // z1/z0, 9=thick, bond paper (side2) + "HIGHQUALITYREV", // z1/z0, 10=high-quality, premium, cotton? (side2) + "COATEDPAPER2REV", // z1/z0, 11=coated, light weight glossy card? (side2) + "RECYCLEREV", // z1/z0, 12=recycled paper (side2) + // include these -m[1..24] media codes for -z1 + "LETTERHEAD", // z1/--, 13,letterhead + "LETTERHEADREV", // z1/--, 14,letterhead (side2) + "PREPRINTED", // z1/--, 15,pre-printed + "PREPRINTEDREV", // z1/--, 16,pre-printed (side2) + "PREPUNCHED", // z1/--, 17,pre-punched? + "PREPUNCHEDREV", // z1/--, 18,pre-punched? (side2) + "COLOR", // z1/--, 19,colored + "COLORREV", // z1/--, 20,colored (side2) + "USER1", // z1/--, 21,custom (not sure if more params needed?) + "USER1REV", // z1/--, 22,custom (side2) + "SPECIAL", // z1/--, 23,special + "SPECIALREV" // z1/--, 24,special (side2) +}; + +static const char *pname[12] = { //Known paper types + "LETTER", // 0 + "LEGAL", // 1 + "A4", // 2 + "EXECUTIVE", // 3 +// unknown 4,5 + "COM10", // 6 + "MONARCH", // 7 + "C5", // 8 + "DL", // 9 +// unknown 10 + "JISB5", // 11 +// unknown 12,13,14 + "A5", // 15 + "FOLIO", // 205 +// fanfold german legal + "CUSTOM" // 255 +}; + +static const short papers[] = { // Official sizes to nearest 1/600 inch + // Official sizes to nearest 1/600 inch + // will accept +-1.5mm (35/600 inch) tolerance + // NOTE: printer appears only able to accept < 22cm max paper width + // {codeHBPL, media=[envelope=6,default=1], sizeX, sizeY} + 0, 5100, 6600, //0,l, 8.50" x 11.0" / 215.9mm x 279.4mm Letter + 2, 5100, 8400, //1,l, 8.50" x 14.0" / 215.9mm x 355.6mm Legal + 4, 4961, 7016, //2,l, 210.0mm x 297.0mm / 8.27" x 11.7" A4 + 6, 4350, 6300, //3,l, 7.25" x 10.5" / 184.2mm x 266.7mm Executive +// unknown 4 +// unknown 5 + 13, 2475, 5700, //6,e, 4.125" x 9.5" / 104.8mm x 241.3mm #10 envelope + 15, 2325, 4500, //7,e, 3.875" x 7.5" / 98.4mm x 190.5mm Monarch envelope + 17, 3827, 5409, //8,e, 162.0mm x 229.0mm / 6.38" x 9.02" C5 envelope + 19, 2599, 5197, //9,e, 110.0mm x 220.0mm / 4.33" x 8.67" DL envelope +// unknown 10 + 22, 4299, 6071, //11,l, 182.0mm x 257.0mm / 7.17" x 10.1" B5jis +// unknown 12 +// unknown 13 +// unknown 14 + 30, 3496, 4961, //15,l, 148.0mm x 210.0mm / 5.83" x 8.27" A5 + 410, 5100, 7800, //205,l, 8.50" x 13.0" / 215.9mm x 330.2mm Folio +// 205, 5100, 7800, //205,l, 8.5" x 13.0" / 215.9mm x 330.2mm fanfold german legal + 510 //255,l, Custom paper size (needs also X and Y) +}; + +void +usage(Job *job) +{ + fprintf(stderr, +"Usage:\n" +" foo2hbpl1 [options] hbpl-file\n" +"\n" +" Convert Ghostscript pbmraw, pgmraw, ppmraw, or pamcmyk32\n" +" format to HBPLv1, for the Dell C1660w and other printers.\n" +"\n" +" gs -q -dBATCH -dSAFER -dQUIET -dNOPAUSE \\ \n" +" -sPAPERSIZE=letter -r600x600 -sDEVICE=pamcmyk32 \\ \n" +" -sOutputFile=- - < testpage.ps \\ \n" +" | foo2hbpl1 -m1 -z0 >testpage.zc\n" +"\n" +"Normal Options:\n" +"-m media Media code to send to printer\n" +" -z0:\n" +" 1=plain, 2=thick, 3=high-quality, 4=coated,\n" +" 5=label, 6=envelope, 7=recycled, 8=plain (side2),\n" +" 9=thick (side2), 10=high-quality (side2),\n" +" 11=coated (side2), 12=recycled (side2)\n" +" -z1: above plus\n" +" 13=letterhead, 14=letterhead (side2),\n" +" 15=preprinted, 16=preprinted (side2),\n" +" 17=prepunched, 18=prepunched (side2),\n" +" 19=color, 20=color (side2),\n" +" 21=user, 22=user (side2),\n" +" 23=special, 24=special (side2)\n" +"-p paper Paper code autodetected by printer [%d]\n" +" 0=Letter, 1=Legal, 2=A4, 3=Executive, 6=Env10,\n" +" 7=EnvMonarch, 8=EnvC5, 9=EnvDL, 11=B5jis,\n" +" 15=A5, 205=Folio, 255=Custom (XxY)\n" +"-n copies Number of copies [%d]\n" +"-t Draft mode. Every other pixel is white.\n" +"-J filename Filename string to send to printer [%s]\n" +"-U username Username string to send to printer [%s]\n" +"\n" +"Printer Tweaking Options:\n" +"-u x Set upper-left clip margin offset [%dx%d] pixels\n" +"-l x Set lower-right clip margin offset [%dx%d] pixels\n" +"-L mask Send logical clipping values from -u/-l [%d]\n" +" 0=no, 1=Y, 2=X, 3=XY\n" +"-A AllIsBlack: convert C=1,M=1,Y=1 to just K=1\n" +"-B BlackClears: K=1 forces C,M,Y to 0\n" +"-z model Model: [%d]\n" +" 0=(default) (Need more info for list)\n" +" 1=(example: Xerox 6000/6010)\n" +"\n" +"Debugging Options:\n" +"-S plane Output a single color plane from a color print [%d]\n" +" and print it on the black plane. Default all colors\n" +" 0=off, 1=Cyan, 2=Magenta, 3=Yellow, 4=Black\n" +"-D lvl Set Debug level [%d]\n" +"-V Version %s\n" + , job->PaperCode + , job->Copies + , job->Filename ? job->Filename : "" + , job->Username ? job->Username : "" + , job->UpperLeftX, job->UpperLeftY + , job->LowerRightX, job->LowerRightY + , job->LogicalClip + , job->Model + , job->Color2Mono + , job->Debug + , Version); +} + +void +debug(Job *job, int level, char *fmt, ...) +{ + va_list ap; + + if (job->Debug < level) + return; + + setvbuf(stderr, (char *) NULL, _IOLBF, BUFSIZ); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +void +reset_streams(Job *job) +{ + int i; + + for (i = 0; i < 5; ++i) + { + if (job->stream[i].buf) + free(job->stream[i].buf); + job->stream[i].buf = NULL; + job->stream[i].size = job->stream[i].off = job->stream[i].bits = 0; + } +} + +void +end_doc(Job *job, int done) +{ + printf("\033%%-12345X@PJL EOJ\n%s", + (job->Model > 0 && done > 0 ? "@PJL RESET\n" : "")); + debug(job, 1, "End printer JOB.\n"); +} + +void +error(Job *job, int fatal, char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (fatal) { + if (job->pagenum) + end_doc(job, 1); + reset_streams(job); + if (job->image) + free(job->image); + exit(fatal); + } +} + +void +save_toner(Job *job, int color) +{ + unsigned char *dp; + unsigned int i, row, col; + + color = (color ? 4 : 1); + + // checker pattern 0xAA/0x55, 8bpp*color + for (row = 0; row < job->Height; row += 2) + { + dp = job->image + row * job->Width * color; + for (col = 0; col < job->Width; col += 2) + { + for (i = 0; i < color; ++i) + *dp++ = 0; + dp += color; + } + } + for (row = 1; row < job->Height; row += 2) + { + dp = job->image + row * job->Width * color; + for (col = 1; col < job->Width; col += 2) + { + dp += color; + for (i = 0; i < color; ++i) + *dp++ = 0; + } + } + debug(job, 2, " Done save_toner(%s)\n", (color == 4 ? "Color" : "Gray")); +} + +void +all_is_black(Job *job) +{ + unsigned char *p; + unsigned int row, col; + + for (row = 0; row < job->Height; ++row) + { + p = job->image + row * job->Width * 4; // KCMY + for (col = 0; col < job->Width; ++col) + { + if (p[1] == 255 && p[2] == 255 && p[3] == 255) + { + *p++ = 255; *p++ = 0; *p++ = 0; *p++ = 0; + //fprintf(stderr,"a"); + } + else + p += 4; + } + } + debug(job, 2, " Done all_is_black()\n"); +} + +void +black_clears(Job *job) +{ + unsigned char *p; + unsigned int row, col; + + for (row = 0; row < job->Height; ++row) + { + p = job->image + row * job->Width * 4; // KCMY + for (col = 0; col < job->Width; ++col) + { + if (*p++ == 255) + { + *p++ = 0; *p++ = 0; *p++ = 0; + //fprintf(stderr,"b"); + } + else + p += 3; + } + } + debug(job, 2, " Done black_clears()\n"); +} + +void +color_to_gray(Job *job, int *deep) +{ + unsigned char *dp, *sp; + unsigned int row, col; + + for (row = 0; row < job->Height; ++row) + { + sp = (job->image + row * job->Width * 4 + (job->Color2Mono & 3)); // KCMY + dp = (job->image + row * job->Width); + for (col = 0; col < job->Width; ++col) + { + *dp++ = *sp; + sp += 4; + } + } + *deep = 1; + debug(job, 2, " Done color_to_gray()\n"); +} + +#define MAXSTREAM (8.5*14*2400*1200)-0x100000 +void +putbits(Job *job, int n, unsigned val, int nbits) +{ + if (job->stream[n].off + 16 > job->stream[n].size && \ + (job->stream[n].size > MAXSTREAM || \ + !(job->stream[n].buf = realloc(job->stream[n].buf, job->stream[n].size += 0x100000)))) + error(job, 1, "Out of memory\n"); + if (job->stream[n].bits) + { + job->stream[n].off--; + val |= job->stream[n].buf[job->stream[n].off] >> (8-job->stream[n].bits) << nbits; + nbits += job->stream[n].bits; + } + job->stream[n].bits = nbits & 7; + while ((nbits -= 8) > 0) + job->stream[n].buf[job->stream[n].off++] = val >> nbits; + job->stream[n].buf[job->stream[n].off++] = val << -nbits; +} +#undef MAXSTREAM + +/* + Runlengths are integers between 1 and 17057 encoded as follows: + + 1 00 + 2 01 0 + 3 01 1 + 4 100 0 + 5 100 1 + 6 101 00 + 7 101 01 + 8 101 10 + 9 101 11 + 10 110 0000 + 11 110 0001 + 12 110 0010 + ... + 25 110 1111 + 26 111 000 000 + 27 111 000 001 + 28 111 000 010 + 29 111 000 011 + ... + 33 111 000 111 + 34 111 001 000 + ... + 41 111 001 111 + 42 111 010 000 + 50 111 011 0000 + 66 111 100 00000 + 98 111 101 000000 + 162 111 110 000000000 + 674 111 111 00000000000000 + 17057 111 111 11111111111111 +*/ +void +put_len(Job *job, int n, unsigned val) +{ + static const unsigned code[34] = + { + 1, 0, 2, + 2, 2, 3, + 4, 8, 4, + 6, 0x14, 5, + 10, 0x60, 7, + 26, 0x1c0, 9, + 50, 0x3b0, 10, + 66, 0x780, 11, + 98, 0xf40, 12, + 162, 0x7c00, 15, + 674, 0xfc000, 20, + 17058 + }; + int c = 0; + + if (val < 1 || val > 17057) return; + while (val >= code[c+3]) c += 3; + putbits(job, n, val-code[c] + code[c+1], code[c+2]); +} + +/* + CMYK byte differences are encoded as follows: + + 0 000 + +1 001 + -1 010 + 2 011s0 s = 0 for +, 1 for - + 3 011s1 + 4 100s00 + 5 100s01 + 6 100s10 + 7 100s11 + 8 101s000 + 9 101s001 + ... + 14 101s110 + 15 101s111 + 16 110s00000 + 17 110s00001 + 18 110s00010 + ... + 46 110s11110 + 47 110s11111 + 48 1110s00000 + 49 1110s00001 + ... + 78 1110s11110 + 79 1110s11111 + 80 1111s000000 + 81 1111s000001 + ... + 126 1111s101110 + 127 1111s101111 + 128 11111110000 +*/ +void +put_diff(Job *job, int n, signed char val) +{ + static const unsigned short code[25] = + { + 2, 3, 3, 1, + 4, 4, 3, 2, + 8, 5, 3, 3, + 16, 6, 3, 5, + 48, 14, 4, 5, + 80, 15, 4, 6, + 129 + }; + int sign, abs, c = 0; + + switch (val) + { + case 0: putbits(job, n, 0, 3); return; + case 1: putbits(job, n, 1, 3); return; + case -1: putbits(job, n, 2, 3); return; + } + abs = ((sign = val < 0)) ? -val:val; + while (abs >= code[c+4]) c += 4; + putbits(job, n, code[c+1], code[c+2]); + putbits(job, n, sign, 1); + putbits(job, n, abs-code[c], code[c+3]); +} + +void +setle(unsigned char *c, int s, int i) +{ + while (s--) + { + *c++ = i; + i >>= 8; + } +} + +void +start_doc(Job *job, int done, int color) +{ + static const char reca[12] = + { + 0x41, // 0,RECTYPE 'A' + 0x81,0xa1,0x00, + 0x82,0xa2,0x07,0x00, + 0x83,0xa2,0x01,0x00 + }; + time_t t; + struct tm *tmp; + char datestr[16], timestr[16]; + char cname[128] = "My Computer"; + + t = time(NULL); + tmp = localtime(&t); + strftime(datestr, sizeof datestr, "%m/%d/%Y", tmp); + strftime(timestr, sizeof timestr, "%H:%M:%S", tmp); + + #ifdef linux + { + struct utsname u; + + uname(&u); + strncpy(cname, u.nodename, 128); + cname[127] = 0; + } + #endif + + /* Lines end with \n, not \r\n */ + + printf( + "\033%%-12345X%s@PJL SET STRINGCODESET=UTF8\n" + "@PJL COMMENT DATE=%s\n" + "@PJL COMMENT TIME=%s\n" + "@PJL COMMENT DNAME=%s\n" + "@PJL JOB MODE=PRINTER\n" + "@PJL SET JOBATTR=\"@LUNA=%s\"\n" + "@PJL SET JOBATTR=\"@TRCH=OFF\"\n" + "@PJL SET DUPLEX=OFF\n" + "@PJL SET BINDING=LONGEDGE\n" + "@PJL SET IWAMANUALDUP=OFF\n" + "@PJL SET JOBATTR=\"@MSIP=%s\"\n" + "@PJL SET RENDERMODE=%s\n" + "@PJL SET ECONOMODE=OFF\n" + "@PJL SET RET=ON\n" + "@PJL SET JOBATTR=\"@IREC=OFF\"\n" + "@PJL SET JOBATTR=\"@TRAP=ON\"\n" + "@PJL SET JOBATTR=\"@JOAU=%s\"\n" + "@PJL SET JOBATTR=\"@CNAM=%s\"\n" + "@PJL SET COPIES=%d\n" + "@PJL SET QTY=1\n" + "@PJL SET PAPERDIRECTION=SEF\n" + "@PJL SET RESOLUTION=600\n" + "@PJL SET BITSPERPIXEL=8\n" + "@PJL SET JOBATTR=\"@DRDM=XRC\"\n" + "@PJL SET JOBATTR=\"@TSCR=11\"\n" + "@PJL SET JOBATTR=\"@GSCR=11\"\n" + "@PJL SET JOBATTR=\"@ISCR=12\"\n" + "@PJL SET JOBATTR=\"@TTRC=11\"\n" + "@PJL SET JOBATTR=\"@GTRC=11\"\n" + "@PJL SET JOBATTR=\"@ITRC=12\"\n" + "@PJL SET JOBATTR=\"@TCPR=11\"\n" + "@PJL SET JOBATTR=\"@GCPR=11\"\n" + "@PJL SET JOBATTR=\"@ICPR=12\"\n" + "@PJL SET JOBATTR=\"@TUCR=11\"\n" + "@PJL SET JOBATTR=\"@GUCR=11\"\n" + "@PJL SET JOBATTR=\"@IUCR=12\"\n" + "@PJL SET JOBATTR=\"@BSPM=OFF\"\n" + "@PJL SET JOBATTR=\"@TDFT=0\"\n" + "@PJL SET JOBATTR=\"@GDFT=0\"\n" + "@PJL SET JOBATTR=\"@IDFT=0\"\n" + "@PJL ENTER LANGUAGE=HBPL\n" + , (job->Model > 0 && done > 0 ? "@PJL RESET\n" : "") + , (job->Debug < 9 ? datestr : "02/26/2026") + , (job->Debug < 9 ? timestr : "12:34:56") + , job->Filename ? job->Filename : "" + , job->Username ? job->Username : "" + , mname[job->MediaCode] + , color ? "COLOR" : "GRAYSCALE" + , job->Username ? job->Username : "" + , cname + , job->Copies); + fwrite (reca, 1, sizeof(reca), stdout); + + debug(job, 1, " Done start_doc(%d). Init printer JOB.\n", color); + job->pagenum++; // Now begin printing as "JOB MODE=PRINTER START=1"... + job->PrintSize += 1096+12; +} + +#define IP (((int *)job->image) + off) +#define CP (((char *)job->image) + off) +#define DP (((char *)job->image) + off*deep) +#define BP(x) ((blank[(off+x) >> 3] << ((off+x) & 7)) & 128) +#define put_token(j,n,x) putbits(job,n,huff[hsel][x] >> 4, huff[hsel][x] & 15) + +void +encode_page(Job *job, int color, int width, int height) +{ + unsigned char head[90] = + { + 0x43, // 0,RECTYPE 'C' + 0x91,0xa1,0x00, + 0x92,0xa1,0x01, + 0x93,0xa1,0x01, + 0x94,0xa1, + 0x00, // 12,paper + 0x95,0xc2, + 0x00,0x00, // 15,width,custom paper size + 0x00,0x00, // 17,height,custom paper size + 0x96,0xa1, + 0x00, // 21,custom paper size (insert 2) + 0x97,0xc3,0x00,0x00,0x00,0x00,0x98,0xa1,0x00,0x99,0xa4, + 0x01,0x00,0x00,0x00, // 33,pagenum + 0x9a,0xc4, + 0x00,0x00,0x00,0x00, // 39,width + 0x00,0x00,0x00,0x00, // 43,height + 0x9b,0xa1,0x00,0x9c,0xa1,0x01,0x9d,0xa1, + 0x00, // 55,grayscale=0x00001001 or color=0x10001011 + 0x9e,0xa1,0x02,0x9f,0xa1,0x05,0xa0,0xa1,0x08,0xa1,0xa1,0x00,0xa2,0xc4, + 0x00,0x00,0x00,0x00, // 70,width + 0x00,0x00,0x00,0x00, // 74,height + 0x51,0x52,0xa3,0xa1,0x00,0xa4,0xb1, + 0xa4, // 85,(total<=0xffff)=a2,(total>0xffff)=a4 + 0x00,0x00, 0x00,0x00 // 86,total + }; + unsigned char body[52] = + { + 0x20,0x00,0x00,0x00, + 0x00, // 4,if grayscale then set this to 8 + 0x01, + 0x00, // 6,if color then set this to 1 + 0x00,0x10,0x32,0x04,0x00,0xa1,0x42,0x00,0x00,0x00,0x00,0xff, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00, + 0x00,0x00,0x00,0x00, // 32,deep0,only this if grayscale + 0x00,0x00,0x00,0x00, // 36,deep1 + 0x00,0x00,0x00,0x00, // 40,deep2 + 0x00,0x00,0x00,0x00, // 44,deep3 + 0x00,0x00,0x00,0x00 + }; + static const unsigned short huff[2][8] = + { + { 0x01,0x63,0x1c5,0x1d5,0x1e5,0x22,0x3e6 }, // for text & graphics + { 0x22,0x63,0x1c5,0x1d5,0x1e5,0x01,0x3e6 }, // for images + }; + unsigned char *blank, *blank0; + int dirs[] = { -1,0,-1,1,2 }, rotor[] = { 0,1,2,3,4 }; + int i, j, row, col, deep, dir, run, try, bdir, brun, total; + int paper = 510, hsel = 0, off = 0, bit = 0, stat = 0; + int margin = width-96; + + reset_streams(job); + + debug(job, 1, " Start encode_page(%d)\n", job->pagenum); + + deep = 1 + color*3; + + // autodetect paper size (portrait) + for (i = 0; papers[i] < 255*2; i+=3) + if (abs(width-papers[i+1]) < 36 && abs(height-papers[i+2]) < 36) + { + paper = i; + goto psize; + } + paper = i; // 255, use custom paper size if you are here + setle(head+15, 2, (width*254+300)/600); // units of 0.1mm + setle(head+17, 2, (height*254+300)/600); + head[21] = 2; +psize: + head[12] = papers[paper]>>1; + if (job->MediaCode < 0) + job->MediaCode = ((papers[paper] & 1) ? 6+1 : 1+1); + + debug(job, 2, " width=%d height=%d color=%d deep=%d=%s\n", \ + width, height, color, deep, color ? "CYMK" : "GRAY"); + debug(job, 2, " paper#%d,%d=%s media=%d=%s\n", \ + paper, papers[paper]>>1, pname[paper], \ + (job->MediaCode >= 1+1 ? job->MediaCode-1 : job->MediaCode), \ + mname[job->MediaCode]); + + if (!job->pagenum) + start_doc(job, 1, color); + else if (job->PrintSize > PRINTMAX) + { + debug(job, 1, " Break JOB into smaller segments\n"); + end_doc(job, 0); + job->pagenum = job->PrintSize = 0; + start_doc(job, 0, color); + } + + width = -(-width & -8); + setle(head+33, 4, job->pagenum); + setle(head+39, 4, width); + setle(head+43, 4, height); + setle(head+70, 4, width); + setle(head+74, 4, height); + head[55] = 9 + color*130; + if (color) body[6] = 1; + else body[4] = 8; + + for (i=1; i < 5; i++) + dirs[i] -= width; + if (!color) dirs[4] = -8; + + blank = blank0 = calloc(height+2, width/8); + memset(blank++, -color, width/8+1); + for (row = 1; row <= height; row++) + { + memset(job->image + row*width * deep + deep, -1, deep); + for (col = 8; col < width*deep; col += 4) + if (*(int *)(job->image + row*width*deep + col)) + { + for (col = 12; col < margin/8; col++) + blank[row*(width/8)+col] = -1; + blank[row*(width/8)+col] = -2 << (~margin & 7); + break; + } + } + memset(job->image, -color, (width+1)*deep); + job->image += (width+1)*deep; + blank += width/8; + + while (off < width * height) + { + for (bdir = brun = dir = 0; dir < 5; dir++) + { + try = dirs[rotor[dir]]; + for (run = 0; run < 17057; run++, try++) + { + if (color) + { + if (IP[run] != IP[try]) break; + } + else + if (CP[run] != CP[try]) break; + + if (BP(run) != BP(try)) break; + } + if (run > brun) + { + bdir = dir; + brun = run; + } + } + if (brun == 0) + { + put_token(job, 0, 5); + for (i = 0; i < deep; i++) + put_diff(job, 1+i, DP[i] - DP[i-deep]); + bit = 0; + off++; + stat--; + continue; + } + if (brun > width * height - off) + brun = width * height - off; + if (bdir) + { + j = rotor[bdir]; + for (i = bdir; i; i--) + rotor[i] = rotor[i-1]; + rotor[0] = j; + } + if ((off-1+brun)/width != (off-1)/width) + { + if (abs(stat) > 8 && ((stat >> 31) & 1) != hsel) + { + hsel ^= 1; + put_token(job, 0, 6); + } + stat = 0; + } + stat += bdir == bit; + put_token(job, 0, bdir - bit); + put_len(job, 0, brun); + bit = brun < 17057; + off += brun; + } + + putbits(job, 0, 0xff, 8); + for (total = 48, i = 0; i <= deep; i++) + { + putbits(job, i, 0xff, 8); + job->stream[i].off--; + setle(body+32 + i*4, 4, job->stream[i].off); + total += job->stream[i].off; + } + head[85] = 0xa2 + (total > 0xffff)*2; + setle(head+86, 4, total); + fwrite(head, 1, 88+(total > 0xffff)*2, stdout); + fwrite(body, 1, 48, stdout); + job->PrintSize += 88+48; + for (i = 0; i <= deep; i++) + { + fwrite(job->stream[i].buf, 1, job->stream[i].off, stdout); + job->PrintSize += job->stream[i].off; + } + reset_streams(job); + free(blank0); + printf("SD"); + debug(job, 1, " End encode_page(%d), Copies(%d). Print size ~ %d\n", \ + job->pagenum, job->Copies, job->PrintSize); + job->pagenum +=job->Copies; +} +#undef IP +#undef CP +#undef DP +#undef BP +#undef put_token + +int +getint(Job *job, FILE *fp) +{ + int c, ret; + + for (;;) + { + while (isspace(c = fgetc(fp))); + if (c == '#') + { + while ((c = fgetc(fp)) != '\n') + if (c < 0) return -1; + } + else + break; + } + if (!isdigit(c)) return -1; + for (ret = c-'0'; isdigit(c = fgetc(fp)); ) + ret = ret*10 + c-'0'; + if (c < 0) return -1; + debug(job, 3, " getint(%d)\n", ret); + return ret; +} + +void +do_file(Job *job, FILE *fp) +{ + int type, iwide, ihigh, ideep, imax, ibyte; + int wide, deep, byte, row, col, i, k; + int lcl, lct, lcr, lcb; + char tupl[128], line[128]; + unsigned char *image, *sp, *dp; + + debug(job, 1, "Start do_file()\n"); + + lcl = lct = 0; + if (job->LogicalClip & LOGICAL_CLIP_X) + lcl = job->UpperLeftX; + if (job->LogicalClip & LOGICAL_CLIP_Y) + lct = job->UpperLeftY; + + while ((type = fgetc(fp)) != EOF) + { + type = ((type - 'P') << 8) | fgetc(fp); + tupl[0] = iwide = ihigh = ideep = deep = imax = ibyte = -1; + switch (type) + { + case '4': + deep = 1 + (ideep = 0); + goto six; + case '5': + deep = ideep = 1; + goto six; + case '6': + deep = 1 + (ideep = 3); +six: iwide = getint(job, fp); + ihigh = getint(job, fp); + imax = type == '4' ? 255 : getint(job, fp); + break; + case '7': + do + { + if (!fgets(line, sizeof(line), fp)) goto fail; + if (!strncmp(line, "WIDTH ",6)) + iwide = atoi(line + 6); + if (!strncmp(line, "HEIGHT ",7)) + ihigh = atoi(line + 7); + if (!strncmp(line, "DEPTH ",6)) + deep = ideep = atoi(line + 6); + if (!strncmp(line, "MAXVAL ",7)) + imax = atoi(line + 7); + if (!strncmp(line, "TUPLTYPE ",9)) + strncpy(tupl, line + 9, sizeof(line)-9-1); + } while (strcmp(line, "ENDHDR\n")); + if (ideep != 4 || strcmp(tupl, "CMYK\n")) goto fail; + break; + default: + goto fail; + } + debug(job, 2, " iwide=%d ihigh=%d imax=%d ideep=%d\n", \ + iwide, ihigh, imax, ideep); + if (iwide <= 0 || ihigh <= 0 || imax != 255) goto fail; + wide = -(-iwide & -8); + if (ideep) + ibyte = iwide * ideep; + else + ibyte = wide >> 3; + byte = wide * deep; + + debug(job, 2, " wide=%d deep=%d ibyte=%d byte=%d\n", \ + wide, deep, ibyte, byte); + + if (job->LogicalClip & LOGICAL_CLIP_X) + lcr = iwide - job->LowerRightX; + else + lcr = iwide - job->LowerRightX - job->UpperLeftX; + if (job->LogicalClip & LOGICAL_CLIP_Y) + lcb = ihigh - job->LowerRightY; + else + lcb = ihigh - job->LowerRightY - job->UpperLeftY; + if (lcr <= 0 || lcb <= 0 || \ + job->UpperLeftY >= ihigh || job->LowerRightY >= ihigh || \ + job->UpperLeftX >= iwide || job->LowerRightX >= iwide || \ + job->UpperLeftY+job->LowerRightY >= ihigh || \ + job->UpperLeftX+job->LowerRightX >= iwide) + lct = lcb = lcl = lcr = -1; + + debug(job, 2, " Clip=[%d,%d,%d,%d] LC=%d lcl=%d lct=%d lcr=%d lcb=%d\n", \ + job->UpperLeftX, job->UpperLeftY, \ + job->LowerRightX, job->LowerRightY, \ + job->LogicalClip, lcl, lct, lcr, lcb); + + job->Height = ihigh+2; + job->Width = wide; + job->image = image = (unsigned char *)(calloc(ihigh+2, byte)); + if (image == NULL) + error(job, 1, "Out of memory\n"); + for (row = 1; row <= ihigh; row++) + { + i = fread(job->image, ibyte, 1, fp); + if (row > lct && row <= lcb) + { + sp = job->image + lcl*ideep; + dp = job->image + (row+job->UpperLeftY-lct)*byte + \ + (2+job->UpperLeftX)*deep; + //for (col = 0; col < iwide; col++) + for (col = lcl; col < lcr; col++) + { + switch (ideep) + { + case 0: // B&W 1bpp -> 8bpp + *dp = ((job->image[col >> 3] >> (~col & 7)) & 1) * 255; + break; + case 1: // GRAY 8bpp + *dp = ~*sp; + break; + case 3: // RGB -> KCMY + for (k = sp[2], i = 0; i < 2; i++) + if (k < sp[i]) k = sp[i]; + *dp = ~k; + for (i = 0; i < 3; i++) + dp[i+1] = k ? (k - sp[i]) * 255 / k : 0; + break; + case 4: // CMYK -> KCMY + for (i=0; i < 4; i++) + dp[i] = sp[((i-1) & 3)]; + break; + } + sp += ideep; + dp += deep; + } + } + } + if (lct < 0) + memset(job->image, 0, (ihigh+2)*byte); + else + { + for (row = 1+job->UpperLeftY; row <= ihigh-job->LowerRightY; row++) + { + memset(job->image + row*byte, 0, deep*(2+job->UpperLeftX)); + memset(job->image + row*byte + deep*(wide-2-job->LowerRightX), \ + 0, deep*(2+job->LowerRightX)); + } + memset(job->image, 0, byte*(1+job->UpperLeftY)); + memset(job->image + byte*(1+ihigh-job->LowerRightY), 0, \ + byte*(1+job->LowerRightY)); + } + + if (deep > 1) { + if (job->BlackClears) + black_clears(job); + if (job->AllIsBlack) + all_is_black(job); + if (job->Color2Mono) + color_to_gray(job, &deep); + } + if (job->SaveToner) + save_toner(job, deep > 1); + encode_page(job, deep > 1, iwide, ihigh); + free(image); + job->image = image = NULL; + } + debug(job, 1, "End do_file()\n"); + return; +fail: + fprintf(stderr, "Not an acceptable PBM, PPM or PAM file!!!\n"); +} + +int +parse_xy(char *str, int *xp, int *yp) +{ + char *p; + + if (!str || str[0] == 0) return -1; + + *xp = strtoul(str, &p, 10); + if (str == p) return -2; + while (*p && (*p < '0' || *p > '9')) + ++p; + str = p; + if (str[0] == 0) return -3; + *yp = strtoul(str, &p, 10); + if (str == p) return -4; + return (0); +} + +int +main(int argc, char *argv[]) +{ + Job job; + int c, i; + + job.AllIsBlack = 0; + job.BlackClears = 0; + job.Color2Mono = 0; // default=0=off + job.Copies = 1; // [1..999] Page Copies (default=1) + job.Debug = 0; // Debug>=9 if md5sum testpage.ps results + job.MediaCode = -1; // -1=undefined (default to paper) + job.PaperCode = 0; // (default=letter) + job.LogicalClip = LOGICAL_CLIP_X | LOGICAL_CLIP_Y; + job.Model = -1; // -1=undefined (default -z0) + job.pagenum = 0; // no pages, no printer codes sent + job.SaveToner = 0; + job.PrintSize = 0; // nothing sent to printer (so far) + job.PageWidth = 600 * 8.5; + job.PageHeight = 600 * 11; + job.UpperLeftX = 20; // clip margin, left + job.UpperLeftY = 20; // clip margin, top + job.LowerRightX = 20; // clip margin, right + job.LowerRightY = 20; // clip margin, bottom + job.image = NULL; + job.Username = job.Filename = NULL; + memset(job.stream, 0, sizeof(Stream)*5); + + while ((c = getopt(argc, argv, "m:n:p:u:l:z:L:J:U:S:D:tABV?h")) != EOF) + switch (c) + { + case 'm': job.MediaCode = atoi(optarg); break; + case 'n': job.Copies = atoi(optarg); break; + case 'p': job.PaperCode = atoi(optarg); break; + case 't': job.SaveToner = 1; break; + case 'u': if (strcmp(optarg, "0") == 0) + break; + if (parse_xy(optarg, &job.UpperLeftX, &job.UpperLeftY)) + error(&job, 1, "Illegal format '%s' for -u\n", optarg); + break; + case 'l': if (strcmp(optarg, "0") == 0) + break; + if (parse_xy(optarg, &job.LowerRightX, &job.LowerRightY)) + error(&job, 1, "Illegal format '%s' for -l\n", optarg); + break; + case 'A': job.AllIsBlack = -1; break; + case 'B': job.BlackClears = -1; break; + case 'J': if (optarg[0]) job.Filename = optarg; break; + case 'U': if (optarg[0]) job.Username = optarg; break; + case 'z': job.Model = atoi(optarg); + if (job.Model < 0 || job.Model > 1) + error(&job, 1, "Illegal value '%s' for -z\n", optarg); + break; + case 'L': job.LogicalClip = atoi(optarg); + if (job.LogicalClip < 0 || job.LogicalClip > 3) + error(&job, 1, "Illegal value '%s' for -L\n", optarg); + break; + case 'S': job.Color2Mono = atoi(optarg); + if (job.Color2Mono < 1 || job.Color2Mono > 4) + error(&job, 1, "Illegal value '%s' for -S\n", optarg); + case 'D': job.Debug = atoi(optarg); break; + case 'V': printf("%s\n", Version); return 0; + default: usage(&job); return 1; + } + + if (job.Model < 0) job.Model = 0; + if (job.MediaCode != -1 && (job.MediaCode <= 0 || ( \ + (job.Model == 0 && job.MediaCode > 12 ) || \ + (job.Model == 1 && job.MediaCode > 24)))) + error(&job, 1, "Illegal value for -m. For -z%d range is -m[1..%d]\n", \ + job.Model, (job.Model ? 24 : 12), optarg); + if (job.MediaCode != -1) + { + job.MediaCode++; + if (job.Model > 0) { + if (job.MediaCode == 4+1) job.MediaCode = 0; + else if (job.MediaCode == 7+1) job.MediaCode = 1; + } + } + if (job.Model <= 0 && job.Copies !=1) + error(&job, 1, "Illegal value for -n. Must be 1 for -z0 printers!\n"); + if (job.Copies < 1 || job.Copies > 999) + error(&job, 1, "Illegal value for -n%d. Must be a number [1..999]!\n", job.Copies); + if (job.UpperLeftX < 20 || job.UpperLeftX >= job.PageWidth) + error(&job, 1, "Illegal X value '%d' for -u\n", job.UpperLeftX); + if (job.UpperLeftY < 20 || job.UpperLeftY >= job.PageHeight) + error(&job, 1, "Illegal Y value '%d' for -u\n", job.UpperLeftY); + if (job.LowerRightX < 20 || job.LowerRightX >= job.PageWidth) + error(&job, 1, "Illegal X value '%d' for -l\n", job.LowerRightX); + if (job.LowerRightY < 20 || job.LowerRightY >= job.PageHeight) + error(&job, 1, "Illegal Y value '%d' for -l\n", job.LowerRightY); + + argc -= optind; + argv += optind; + + if (argc == 0) + do_file(&job, stdin); + else + { + for (i = 0; i < argc; ++i) + { + FILE *ifp; + + if (!(ifp = fopen(argv[i], "r"))) + error(&job, 1, "Can't open '%s' for reading\n", argv[i]); + do_file(&job, ifp); + fclose(ifp); + } + } + if (job.pagenum) + end_doc(&job, 1); + return 0; +}