From 0f4a0f366087198cb17d920e319c3642b914c775 Mon Sep 17 00:00:00 2001 From: Joe Da Silva Date: Wed, 29 Jul 2026 11:50:27 -0700 Subject: [PATCH 1/2] Merge Dave Coffin's hbpl1.patch from 2014-04-05 to current foo2zjs. Merge part of Dave Coffin's hbpl1.patch (located here): https://web.archive.org/web/20160515134331/http://cybercom.net/~dcoffin/hbpl/hbpl1.patch to current foo2zjs. Note that it is a merge since much has changed. Only foo2hbpl1, foo2hbpl1-wrapper, and manuals added here since the other files like PPDs and xml files should be added to foomatic-db. Note edits and upgrades required to sync-up with current code base. --- ChangeLog | 30 ++ Makefile | 46 ++- README | 12 + README.in | 12 + foo2hbpl1-wrapper.1in | 240 +++++++++++++++ foo2hbpl1-wrapper.in | 577 +++++++++++++++++++++++++++++++++++ foo2hbpl1.1in | 68 +++++ foo2hbpl1.c | 692 ++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 1673 insertions(+), 4 deletions(-) create mode 100644 foo2hbpl1-wrapper.1in create mode 100644 foo2hbpl1-wrapper.in create mode 100644 foo2hbpl1.1in create mode 100644 foo2hbpl1.c 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..001f524 --- /dev/null +++ b/foo2hbpl1-wrapper.1in @@ -0,0 +1,240 @@ +'\" 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 ZJS 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 Zenographics ZjStream printer format +for driving the +Dell 1250c, Dell C1660w, Dell C1760w, +Epson AcuLaser C1700, +and Fuji-Xerox CP105b 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 \-d\0 duplex +Duplex code to send to printer [1]. +.TS +| n l | n l | n l . +1 off 2 long edge 3 short edge +.TE +.TP +.BI \-m\0 media +Media code to send to printer [1]. +.TS +l r. +_ +Media HBPL +_ +plain 1 +bond 2 +lwcard 3 +lwgcard 4 +labels 5 +envelope 6 +recycled 7 +plain2 8 +bond2 9 +lwcard2 10 +lwgcard2 11 +recycled2 12 +.TE +.TP +.BI \-p\0 paper +Paper size code to send to printer [1]. +.TS +l r. +_ +Paper HBPL +_ +A4 1 +B5jis 2 +letter 4 +executive 5 +fanfold german legal 6 +folio 6 +legal 7 +env#10 9 +envMonarch 10 +envC5 11 +envDL 12 +.TE +.TP +.BI \-n\0 copies +Number of copies [1]. +.TP +.BI \-r\0 xres x yres +Set device resolution in pixels/inch [1200x600]. +.TP +.BI \-s\0 source +Source (Input Slot) code to send to printer [7]. +.TS +| n l | n l. +1 upper 4 manual +2 lower 7 auto +.TE +.TP +.BI \-t +Draft mode. Every other pixel is white. +.TP +.BI \-T\0 density +Print density (1-5). The default is 3 (medium). +.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 [varies with paper size]. +The defaults should work on the 2200DL and 2300DL, and have not +been tested on any other printers. +.TP +.BI \-l\0 xoff x yoff +Set the offset of the end of the printable region from the +lower right corner, in pixels [varies with paper size]. +The defaults should work on the 2200DL and 2300DL, and have not +been tested on any other printers. +.TP +.BI \-L\0 mask +Send the logical clipping values from -u/-l in the ZjStream. +\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 \-P +Do not send START_PLANE codes on monochrome output. May be +needed by some monochrome-only printers, such as the +HP LaserJet 1000. +.TP +.BI \-X\0 padlen +Add extra zero padding to the end of BID segments. The default is +16 bytes. Padding 16 bytes of zeroes +is needed for older ZjStream printers, such as the Minolta 2200DL +and HP LaserJet 1000, and seems harmless to newer ones, such as +the Minolta 2300DL. So the default should be good for all cases. +.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 ZjStream from a Postscript document, +examine it, and then print it using a RAW print queue: + +.RS +.nf +foo2hbpl1-wrapper testpage.ps > testpage.prn +hbpl1decode < testpage.prn +lpr -P raw testpage.prn +.fi +.RE +.P +Create a color ZjStream 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 +${URLHBPL}/ +'\" +'\" +'\" +.em pdf_outline diff --git a/foo2hbpl1-wrapper.in b/foo2hbpl1-wrapper.in new file mode 100644 index 0000000..567e891 --- /dev/null +++ b/foo2hbpl1-wrapper.in @@ -0,0 +1,577 @@ +#!/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, write to the Free Software +#* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +#* +#* Authors: Rick Richardson + +VERSION='$Id: foo2hbpl1-wrapper.in,v 1.3 2014/03/24 23:14:24 rick Exp $' + +# +# Printer Notes: +# +# Dell 1250c +# Dell C1660w +# Dell C1760w +# Epson AcuLaser C1700 +# Fuji-Xerox DocuPrint CP105b +# + +PROGNAME="$0" +BASENAME=`basename $PROGNAME` +PREFIX=/usr +SHARE=$PREFIX/share/foo2hbpl +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 $@" &2 + exit 1 +} + +dbgcmd() { + if [ $DEBUG -ge 1 ]; then + echo "$@" >&2 + fi + "$@" +} + +# +# N-up-ify the job. Requires psnup from psutils package +# +nup() { + 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=1 # plain +COPIES=1 +PAPER=letter +RES=600x600 # do not change this +SOURCE=1 +NUP= +CLIP_ALL= +BC= +AIB= +NOPLANES= +COLOR2MONO= +GAMMAFILE=default +INTENT=0 +GSOPTS= +EXTRAPAD= +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:d:g:l:u:L:m:n:p:q:s:tz:ABS:D:G:I:PX:Vh?" opt +do + case $opt in + b) GSBIN="$OPTARG";; + c) COLOR=-c;; + d) DUPLEX="$OPTARG";; + g) GSOPTS="$GSOPTS $OPTARG";; + m) MEDIA="$OPTARG";; + n) COPIES="$OPTARG";; + p) PAPER="$OPTARG";; + q) QUALITY="$OPTARG";; + s) SOURCE="$OPTARG";; + t) SAVETONER="-t";; + z) MODEL="$OPTARG";; + u) CLIP_ALL="-u $OPTARG";; + A) AIB=-A;; + B) BC=-B;; + C) COLORMODE="$OPTARG";; + S) COLOR2MONO="-S$OPTARG";; + D) DEBUG="$OPTARG";; + G) GAMMAFILE="$OPTARG";; + I) INTENT="$OPTARG";; + P) NOPLANES=-P;; + X) EXTRAPAD="-X $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"; foo2hbpl -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 + +# +# Validate media code +# +case "$MEDIA" in +1|plain) MEDIA=1;; +2|bond) MEDIA=2;; +3|lwcard) MEDIA=3;; +4|lwgcard) MEDIA=4;; +5|labels) MEDIA=5;; +6|envelope) MEDIA=6;; +7|recycled) MEDIA=7;; +8|plain2) MEDIA=8;; +9|bond2) MEDIA=9;; +10|lwcard2) MEDIA=10;; +11|lwgcard2) MEDIA=11;; +12|recycled2) MEDIA=12;; +[0-9]*) ;; +*) error "Unknown media code $MEDIA";; +esac + +# +# Validate source (InputSlot) code +# +case "$SOURCE" in +1|auto) SOURCE=1;; +2|manual) SOURCE=2;; +3|multi) SOURCE=3;; +4|tray1) SOURCE=4;; +[0-9]*) ;; +*) error "Unknown source code $SOURCE";; +esac + +# +# 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. +# +set_clipping() { + + # Set clipping region if it isn't already set + if [ "$CLIP_ALL" = "" ]; then + CLIP_ALL="-u $1,$2,$3,$4" + 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=21; paper=letter; + DIM="${XDIM}x${YDIM}" + ;; +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=21; paper=letter; + DIM="${XDIM}x${YDIM}" + ;; + +# Use names that Ghostscript recognizes, referring to +# /usr/share/ghostscript/9.10/Resource/Init/gs_statd.ps +# foo2hbpl1 will provide the appropriate numeric value + +letter) DIM=5100x6600 ;; +legal) DIM=5100x8400 ;; +a4) DIM=4961x7016 ;; +executive) DIM=4350x6300 ;; +env10) DIM=2475x5700 ;; +monarch) DIM=2325x4500 ;; +c5) DIM=3827x5409 ;; +envDL) DIM=2599x5197 ;; +isob5|b5) DIM=4158x5906 ;; +jisb5) DIM=4299x6071 ;; +a5) DIM=3496x4961 ;; +folio) DIM=5100x7800 ;; +pa4) DIM=4961x6600 ;; +archA) DIM=5400x7200 ;; +*) error "Unimplemented paper code $PAPER";; +esac + +#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 + +PAPERSIZE="-sPAPERSIZE=$PAPER"; + +# +# 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/foo2hbpl/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 $CLIP_ALL -J "$LPJOB" -U "$USER" + +# +# 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 $CLIP_ALL" +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..a6549b4 --- /dev/null +++ b/foo2hbpl1.1in @@ -0,0 +1,68 @@ +'\" 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 the +Dell 1250c, Dell C1660w, Dell C1760w, +Epson AcuLaser C1700, +and Fuji-Xerox CP105b printers. + +.SH COMMAND LINE OPTIONS +.TP +.BI \-J\0 filename +Filename string to send to printer. +.TP +.BI \-U\0 username +Username string to send to printer. +.SH EXAMPLES +Create a black and white ZJS 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 ZJS 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 +${URLHBPL}/ +'\" +'\" +'\" +.em pdf_outline diff --git a/foo2hbpl1.c b/foo2hbpl1.c new file mode 100644 index 0000000..fb4f448 --- /dev/null +++ b/foo2hbpl1.c @@ -0,0 +1,692 @@ +/* + +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: + - 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 + +AUTHORS +This program was originally written by Dave Coffin in March 2014. + +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, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +If you want to use this program under different license conditions, +then contact the author for an arrangement. + +*/ + +static char Version[] = "$Id: foo2hbpl1.c,v 1.3 2014/03/30 05:08:32 rick Exp $"; + +#include +#include +#include +#include +#include +#include +#include +#ifdef linux + #include +#endif + +/* + * Command line options + */ +int MediaCode = 0; +char *Username = NULL; +char *Filename = NULL; +int Clip[] = { 8,8,8,8 }; + +void +usage(void) +{ + 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 >testpage.zc\n" +"\n" +"Options:\n" +"-m media Media code to send to printer [1 or 6]\n" +" 1=plain, 2=bond, 3=lwcard, 4=lwgcard, 5=labels,\n" +" 6=envelope, 7=recycled, 8=plain2, 9=bond2,\n" +" 10=lwcard2, 11=lwgcard2, 12=recycled2\n" +"-u left,top,right,bottom\n" +" Erase margins of specified width [%d,%d,%d,%d]\n" +"-J filename Filename string to send to printer\n" +"-U username Username string to send to printer\n" +"-V Version %s\n" + , Clip[0], Clip[1], Clip[2], Clip[3] + , Version); +} + +void +error(int fatal, char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (fatal) exit(fatal); +} + +struct stream +{ + unsigned char *buf; + int size, off, bits; +}; + +void +putbits(struct stream *s, unsigned val, int nbits) +{ + if (s->off + 16 > s->size && + !(s->buf = realloc(s->buf, s->size += 0x100000))) + error (1, "Out of memory\n"); + if (s->bits) + { + s->off--; + val |= s->buf[s->off] >> (8-s->bits) << nbits; + nbits += s->bits; + } + s->bits = nbits & 7; + while ((nbits -= 8) > 0) + s->buf[s->off++] = val >> nbits; + s->buf[s->off++] = val << -nbits; +} + +/* + 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(struct stream *s, unsigned val) +{ + unsigned code[] = + { + 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(s, 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(struct stream *s, signed char val) +{ + static unsigned short code[] = + { + 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(s, 0, 3); return; + case 1: putbits(s, 1, 3); return; + case -1: putbits(s, 2, 3); return; + } + abs = ((sign = val < 0)) ? -val:val; + while (abs >= code[c+4]) c += 4; + putbits(s, code[c+1], code[c+2]); + putbits(s, sign, 1); + putbits(s, abs-code[c], code[c+3]); +} + +void +setle(unsigned char *c, int s, int i) +{ + while (s--) + { + *c++ = i; + i >>= 8; + } +} + +void +start_doc(int color) +{ + char reca[] = { 0x41,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"; + char *mname[] = + { "", + "NORMAL", + "THICK", + "HIGHQUALITY", + "COAT2", + "LABEL", + "ENVELOPE", + "RECYCLED", + "NORMALREV", + "THICKSIDE2", + "HIGHQUALITYREV", + "COATEDPAPER2REV", + "RECYCLEREV", + }; + + 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@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=1\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" + , datestr, timestr + , Filename ? Filename : "" + , Username ? Username : "" + , mname[MediaCode] + , color ? "COLOR" : "GRAYSCALE" + , Username ? Username : "" + , cname); + fwrite (reca, 1, sizeof reca, stdout); +} + +#define IP (((int *)image) + off) +#define CP (((char *)image) + off) +#define DP (((char *)image) + off*deep) +#define BP(x) ((blank[(off+x) >> 3] << ((off+x) & 7)) & 128) +#define put_token(s,x) putbits(s, huff[hsel][x] >> 4, huff[hsel][x] & 15) + +void +encode_page(int color, int width, int height, char *image) +{ + unsigned char head[90] = + { + 0x43,0x91,0xa1,0x00,0x92,0xa1,0x01,0x93,0xa1,0x01,0x94,0xa1, + 0x00,0x95,0xc2,0x00,0x00,0x00,0x00,0x96,0xa1,0x00,0x97,0xc3, + 0x00,0x00,0x00,0x00,0x98,0xa1,0x00,0x99,0xa4,0x01,0x00,0x00, + 0x00,0x9a,0xc4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x9b, + 0xa1,0x00,0x9c,0xa1,0x01,0x9d,0xa1,0x00,0x9e,0xa1,0x02,0x9f, + 0xa1,0x05,0xa0,0xa1,0x08,0xa1,0xa1,0x00,0xa2,0xc4,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x52,0xa3,0xa1,0x00,0xa4, + 0xb1,0xa4 + }; + unsigned char body[52] = + { + 0x20,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x10,0x32,0x04,0x00, + 0xa1,0x42,0x00,0x00,0x00,0x00,0xff + }; + static short papers[] = + { // Official sizes to nearest 1/600 inch + // will accept +-1.5mm (35/600 inch) tolerance + 0, 5100, 6600, // Letter + 2, 5100, 8400, // Legal + 4, 4961, 7016, // A4 + 6, 4350, 6300, // Executive + 13, 2475, 5700, // #10 envelope + 15, 2325, 4500, // Monarch envelope + 17, 3827, 5409, // C5 envelope + 19, 2599, 5197, // DL envelope +// ??, 4158, 5906, // B5 ISO + 22, 4299, 6071, // B5 JIS + 30, 3496, 4961, // A5 + 410, 5100, 7800, // Folio + }; + 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; + static int pagenum = 0; + struct stream stream[5] = { { 0 } }; + 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; + + for (i = 0; i < sizeof papers / sizeof *papers; i++) + if (abs(width-papers[i+1]) < 36 && abs(height-papers[i+2]) < 36) + paper = papers[i]; + if (!MediaCode) + MediaCode = paper & 1 ? 6 : 1; + if (!pagenum) + start_doc(color); + head[12] = paper >> 1; + if (paper == 510) + { + setle (head+15, 2, (width*254+300)/600); // units of 0.1mm + setle (head+17, 2, (height*254+300)/600); + head[21] = 2; + } + width = -(-width & -8); + setle (head+33, 4, ++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; + + deep = 1 + color*3; + for (i=1; i < 5; i++) + dirs[i] -= width; + if (!color) dirs[4] = -8; + + blank = calloc(height+2, width/8); + memset (blank++, -color, width/8+1); + for (row = 1; row <= height; row++) + { + for (col = deep; col < deep*2; col++) + image[row*width*deep + col] = -1; + for (col = 8; col < width*deep; col += 4) + if (*(int *)(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 (image, -color, (width+1)*deep); + 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(stream, 5); + for (i = 0; i < deep; i++) + put_diff(stream+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(stream, 6); + } + stat = 0; + } + stat += bdir == bit; + put_token(stream, bdir - bit); + put_len(stream, brun); + bit = brun < 17057; + off += brun; + } + + putbits(stream, 0xff, 8); + for (total = 48, i = 0; i <= deep; i++) + { + putbits(stream+i, 0xff, 8); + stream[i].off--; + setle (body+32 + i*4, 4, stream[i].off); + total += 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); + for (i = 0; i <= deep; i++) + { + fwrite(stream[i].buf, 1, stream[i].off, stdout); + free(stream[i].buf); + } + free(blank-width/8-1); + printf("SD"); +} +#undef IP +#undef CP +#undef DP +#undef BP +#undef put_token + +int +getint(FILE *fp) +{ + int c, ret; + + for (;;) + { + while (isspace(c = fgetc(fp))); + if (c == '#') + while (fgetc(fp) != '\n'); + else break; + } + if (!isdigit(c)) return -1; + for (ret = c-'0'; isdigit(c = fgetc(fp)); ) + ret = ret*10 + c-'0'; + return ret; +} + +void +do_file(FILE *fp) +{ + int type, iwide, ihigh, ideep, imax, ibyte; + int wide, deep, byte, row, col, i, k; + char tupl[128], line[128]; + unsigned char *image, *sp, *dp; + + 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(fp); + ihigh = getint(fp); + imax = type == '4' ? 255 : getint(fp); + break; + case '7': + do + { + if (!fgets(line, 128, 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)) + strcpy (tupl, line + 9); + } while (strcmp(line, "ENDHDR\n")); + if (ideep != 4 || strcmp(tupl, "CMYK\n")) goto fail; + break; + default: + goto fail; + } + if (iwide <= 0 || ihigh <= 0 || imax != 255) goto fail; + wide = -(-iwide & -8); + if (ideep) + ibyte = iwide * ideep; + else + ibyte = wide >> 3; + byte = wide * deep; + image = calloc (ihigh+2, byte); + for (row = 1; row <= ihigh; row++) + { + i = fread (image, ibyte, 1, fp); + sp = image; + dp = image + row*byte; + for (col = 0; col < iwide; col++) + { + dp += deep; + switch (ideep) + { + case 0: + *dp = ((image[col >> 3] >> (~col & 7)) & 1) * 255; + break; + case 1: + *dp = ~*sp; + break; + case 3: + 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 : 255; + break; + case 4: + for (i=0; i < 4; i++) + dp[i] = sp[((i-1) & 3)]; + break; + } + sp += ideep; + } + for (i = 0; i < deep*Clip[0]; i++) + image[row*byte + deep+i] = 0; + for (i = deep*(iwide-Clip[2]); i < byte; i++) + image[row*byte + deep+i] = 0; + } + memset(image+deep, 0, byte*(Clip[1]+1)); + memset(image+deep + byte*(ihigh-Clip[3]+1), 0, byte*Clip[3]); + encode_page(deep > 1, iwide, ihigh, (char *) image); + free(image); + } + return; +fail: + fprintf (stderr, "Not an acceptable PBM, PPM or PAM file!!!\n"); +} + +int +main(int argc, char *argv[]) +{ + int c, i; + + while ( (c = getopt(argc, argv, "m:u:J:U:V")) != EOF) + switch (c) + { + case 'm': MediaCode = atoi(optarg); break; + case 'u': if (sscanf(optarg, "%d,%d,%d,%d", + Clip, Clip+1, Clip+2, Clip+3) != 4) + error(1, "Must specify four clipping margins!\n"); + break; + case 'J': if (optarg[0]) Filename = optarg; break; + case 'U': if (optarg[0]) Username = optarg; break; + case 'V': printf("%s\n", Version); return 0; + default: usage(); return 1; + } + + argc -= optind; + argv += optind; + + if (argc == 0) + { + do_file(stdin); + } + else + { + for (i = 0; i < argc; ++i) + { + FILE *ifp; + + if (!(ifp = fopen(argv[i], "r"))) + error(1, "Can't open '%s' for reading\n", argv[i]); + do_file(ifp); + fclose(ifp); + } + } + puts("B\033%-12345X@PJL EOJ"); + return 0; +} From bf6350d5c6edaad8deb58de0910d9333488d9d2a Mon Sep 17 00:00:00 2001 From: Joe Da Silva Date: Tue, 18 Aug 2026 21:14:56 -0700 Subject: [PATCH 2/2] Add edits and upgrades seen in PR #24 plus more. foo2hbpl1.c points of interest: - Added -z1 option to work with Xerox 6000 and 6010 which seems to have larger media list compared to the initial Dell C1250c, plus discovered COPIES=n works too (kept backwards compatibility for older printers). - Added printer reset so we have printer in a known state before loading job, and another reset as housekeeping cleanup when finishing job. This only done for -z1 printers (don't know if reset works on -z0 printers). - Break large print jobs into smaller jobs to keep within printer buffer. Printed large 82 page pdf without having to break it into small parts. - Moved all Global variables into Job structure, this should make program compatible with multithreading, plus we can clean-up memory in error(). - Moved Streams into Job structure so we can clean-up memory in error(), this meant having to edit putbits() and friends to pass int n instead. - Static const arrays put in global space to be shared amongst functions. - Added static const everywhere to avoid loading unnecessary mem arrays. - Added more options like in foo2zjs, eg: debug, save_toner. See main(). - Replaced clip option -u with -u, -l and -L to work similar to foo2zjs. - Check for absurdly large clipping and avoid/compensate when necessary. - Replaced several for loops with faster memset(). - Used strncpy and MAXSTREAM to avoid Stream buffer overflows. - More info added to reca[], head[], body[] (from info in hbpldecode.c). - Debug >= 9 prints time as "02/26/2026 12:34:56" for md5sum 'make check' - getint() has more input checks. script foo2hbpl1-wrapper, and man files foo2hbpl1.1, foo2hbpl1-wrapper.1 - Updated and upgraded options to work with this updated foo2hbpl1 --- foo2hbpl1-wrapper.1in | 172 ++++---- foo2hbpl1-wrapper.in | 308 ++++++++------ foo2hbpl1.1in | 142 ++++++- foo2hbpl1.c | 910 ++++++++++++++++++++++++++++++++---------- 4 files changed, 1095 insertions(+), 437 deletions(-) diff --git a/foo2hbpl1-wrapper.1in b/foo2hbpl1-wrapper.1in index 001f524..fe0f5d0 100644 --- a/foo2hbpl1-wrapper.1in +++ b/foo2hbpl1-wrapper.1in @@ -7,19 +7,18 @@ '\" MANUAL PAGE SOURCE '\"========================================================================== .SH NAME -foo2hbpl1-wrapper \- Convert Postscript into a ZJS printer stream +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 Zenographics ZjStream printer format -for driving the +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, -and Fuji-Xerox CP105b printers. +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 @@ -37,76 +36,62 @@ Print in color (else monochrome). Color correction mode [0]. .TS n l . -10 ICM color profile (using -G *.icm file) -.TE -.TP -.BI \-d\0 duplex -Duplex code to send to printer [1]. -.TS -| n l | n l | n l . -1 off 2 long edge 3 short edge +10 ICM color profile (using \-G *.icm file) .TE .TP .BI \-m\0 media -Media code to send to printer [1]. +Media code to send to printer [1] or [6]. .TS -l r. +l r r r r. _ -Media HBPL +Media \-m \-m \-m \-m +\^ \-z0 \-z0 \-z1 \-z1 +\^ side1 side2 side1 side2 _ -plain 1 -bond 2 -lwcard 3 -lwgcard 4 -labels 5 -envelope 6 -recycled 7 -plain2 8 -bond2 9 -lwcard2 10 -lwgcard2 11 -recycled2 12 +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 size code to send to printer [1]. +Paper code sizes autodetected by printer [0]. .TS -l r. -_ -Paper HBPL -_ -A4 1 -B5jis 2 -letter 4 -executive 5 -fanfold german legal 6 -folio 6 -legal 7 -env#10 9 -envMonarch 10 -envC5 11 -envDL 12 +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 [1]. -.TP -.BI \-r\0 xres x yres -Set device resolution in pixels/inch [1200x600]. -.TP -.BI \-s\0 source -Source (Input Slot) code to send to printer [7]. +Number of copies of each page [1]. Only available for \-z1 printers. .TS -| n l | n l. -1 upper 4 manual -2 lower 7 auto +l l l. +_ +Model Range Example printer(s) +_ +z0 \- Dell 1250c / C1660 / C1760 +z1 1-999 Xerox 6000 / 6010 .TE .TP -.BI \-t -Draft mode. Every other pixel is white. -.TP -.BI \-T\0 density -Print density (1-5). The default is 3 (medium). +.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. @@ -115,9 +100,9 @@ Print in N-up. Requires the \fBpsutils\fP package. Orientation used for N-up. .TS l l l. -Portrait -op (normal) -Landscape -ol (rotated 90 degrees anticlockwise) -Seascape -os (rotated 90 degrees clockwise) +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 @@ -125,24 +110,20 @@ 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 [varies with paper size]. -The defaults should work on the 2200DL and 2300DL, and have not -been tested on any other printers. +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 [varies with paper size]. -The defaults should work on the 2200DL and 2300DL, and have not -been tested on any other printers. +lower right corner, in pixels [20x20]. .TP .BI \-L\0 mask -Send the logical clipping values from -u/-l in the ZjStream. -\fBfoo2hbpl1-wrapper\fP always runs Ghostscript with the ideal page dimensions, -so that the scale of the image is correct, +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 +the amount which was clipped by \-u and \-l, and should be good in most cases. .TS l l. @@ -152,23 +133,28 @@ l l. 3 send both X and Y clipping amounts .TE .TP -.BI \-P -Do not send START_PLANE codes on monochrome output. May be -needed by some monochrome-only printers, such as the -HP LaserJet 1000. -.TP -.BI \-X\0 padlen -Add extra zero padding to the end of BID segments. The default is -16 bytes. Padding 16 bytes of zeroes -is needed for older ZjStream printers, such as the Minolta 2200DL -and HP LaserJet 1000, and seems harmless to newer ones, such as -the Minolta 2300DL. So the default should be good for all cases. +.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, +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 @@ -193,7 +179,7 @@ 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. +on the black plane. The default is to output all color planes. .TS l l. 1 Cyan @@ -206,22 +192,22 @@ l l. Set Debug level [0]. .SH EXAMPLES -Create a monochrome ZjStream from a Postscript document, +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 -hbpl1decode < testpage.prn -lpr -P raw testpage.prn +hbpldecode < testpage.prn +lpr \-P raw testpage.prn .fi .RE .P -Create a color ZjStream stream from a Postscript document: +Create a color HBPL1v1 stream from a Postscript document: .RS .nf -foo2hbpl1-wrapper -c testpage.ps > testpage.prn +foo2hbpl1-wrapper \-c testpage.ps > testpage.prn .fi .RE @@ -233,7 +219,7 @@ foo2hbpl1-wrapper -c testpage.ps > testpage.prn .SH "AUTHOR" Rick Richardson .br -${URLHBPL}/ +${URLHBPL1}/ '\" '\" '\" diff --git a/foo2hbpl1-wrapper.in b/foo2hbpl1-wrapper.in index 567e891..19a4625 100644 --- a/foo2hbpl1-wrapper.in +++ b/foo2hbpl1-wrapper.in @@ -13,12 +13,12 @@ #* 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, write to the Free Software -#* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +#* along with this program; if not, see +#* . #* #* Authors: Rick Richardson -VERSION='$Id: foo2hbpl1-wrapper.in,v 1.3 2014/03/24 23:14:24 rick Exp $' +VERSION='$Id: foo2hbpl1-wrapper.in,v 1.4 2026/07/14 12:00:00 joe Exp $' # # Printer Notes: @@ -28,12 +28,14 @@ VERSION='$Id: foo2hbpl1-wrapper.in,v 1.3 2014/03/24 23:14:24 rick Exp $' # 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/foo2hbpl +SHARE=$PREFIX/share/foo2hbpl1 PATH=$PATH:/sw/bin:/opt/local/bin # @@ -43,6 +45,7 @@ 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] +-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. +-G gamma-file.ps Prepend gamma-file to the Postscript input to perform + color correction using the setcolortransfer PS operator. Debugging Options: --D lvl Set Debug level [$DEBUG] --V $VERSION +-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 } -# -# Report an error and exit -# error() { + # Report an error and exit echo "$BASENAME: $1" >&2 exit 1 } @@ -108,20 +128,47 @@ dbgcmd() { "$@" } +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() { - 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()." - ;; + # 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 } @@ -145,21 +192,21 @@ DEBUG=0 COLOR= COLORMODE=default MODEL=0 -MEDIA=1 # plain +MEDIA= COPIES=1 -PAPER=letter +PAPER= RES=600x600 # do not change this -SOURCE=1 NUP= -CLIP_ALL= -BC= -AIB= -NOPLANES= +CLIP_UL= +CLIP_LR= +CLIP_LOG= +# default as 1. Should save Toner in CYMK +BC="-B" +AIB="-A" COLOR2MONO= GAMMAFILE=default INTENT=0 GSOPTS= -EXTRAPAD= SAVETONER= NUP_ORIENT= GSDEV=-sDEVICE=pgmraw @@ -175,21 +222,21 @@ case `$GSBIN --version` in *) DEFAULTCOLORMODE=1 ;; esac -while getopts "1:23456789o:b:cC:d:g:l:u:L:m:n:p:q:s:tz:ABS:D:G:I:PX:Vh?" opt +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;; - d) DUPLEX="$OPTARG";; g) GSOPTS="$GSOPTS $OPTARG";; m) MEDIA="$OPTARG";; n) COPIES="$OPTARG";; p) PAPER="$OPTARG";; q) QUALITY="$OPTARG";; - s) SOURCE="$OPTARG";; t) SAVETONER="-t";; z) MODEL="$OPTARG";; - u) CLIP_ALL="-u $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";; @@ -197,8 +244,6 @@ do D) DEBUG="$OPTARG";; G) GAMMAFILE="$OPTARG";; I) INTENT="$OPTARG";; - P) NOPLANES=-P;; - X) EXTRAPAD="-X $OPTARG";; [234689]) NUP="$opt";; [57]) error "Can't find acceptable layout for $opt-up";; 1) case "$OPTARG" in @@ -211,7 +256,7 @@ do s*) NUP_ORIENT=-r;; p*|*) NUP_ORIENT=;; esac;; - V) echo "$VERSION"; foo2hbpl -V; foo2zjs-pstops -V; exit 0;; + V) echo "$VERSION"; foo2hbpl1 -V; foo2zjs-pstops -V; exit 0;; h|\?) if [ "$CMDLINE" != "-?" -a "$CMDLINE" != -h ]; then echo "Illegal command:" @@ -234,38 +279,6 @@ if [ $# -ge 1 ]; then exec < $1 fi -# -# Validate media code -# -case "$MEDIA" in -1|plain) MEDIA=1;; -2|bond) MEDIA=2;; -3|lwcard) MEDIA=3;; -4|lwgcard) MEDIA=4;; -5|labels) MEDIA=5;; -6|envelope) MEDIA=6;; -7|recycled) MEDIA=7;; -8|plain2) MEDIA=8;; -9|bond2) MEDIA=9;; -10|lwcard2) MEDIA=10;; -11|lwgcard2) MEDIA=11;; -12|recycled2) MEDIA=12;; -[0-9]*) ;; -*) error "Unknown media code $MEDIA";; -esac - -# -# Validate source (InputSlot) code -# -case "$SOURCE" in -1|auto) SOURCE=1;; -2|manual) SOURCE=2;; -3|multi) SOURCE=3;; -4|tray1) SOURCE=4;; -[0-9]*) ;; -*) error "Unknown source code $SOURCE";; -esac - # # Figure out the paper dimensions in pixels/inch, and set the # default clipping region. Unfortunately, this is a trouble @@ -273,14 +286,8 @@ esac # engines react differently when asked to print into their # unprintable regions. # -set_clipping() { - - # Set clipping region if it isn't already set - if [ "$CLIP_ALL" = "" ]; then - CLIP_ALL="-u $1,$2,$3,$4" - fi -} +if [ "$PAPER" = "" ]; then PAPER=letter; fi case "$PAPER" in Custom*x*) # Command line only @@ -294,8 +301,7 @@ Custom*x*) fi XDIM=`dc -e "$XDIM 600* 72/p"` YDIM=`dc -e "$YDIM 600* 72/p"` - PAPER=21; paper=letter; - DIM="${XDIM}x${YDIM}" + PAPER=255; paper=custom; set_clipping 50 50 50 50 ;; Custom*) #%%BeginFeature: *CustomPageSize True @@ -333,35 +339,95 @@ Custom*) esac XDIM=`dc -e "$XDIM 600* 72/p"` YDIM=`dc -e "$YDIM 600* 72/p"` - PAPER=21; paper=letter; - DIM="${XDIM}x${YDIM}" + 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 -letter) DIM=5100x6600 ;; -legal) DIM=5100x8400 ;; -a4) DIM=4961x7016 ;; -executive) DIM=4350x6300 ;; -env10) DIM=2475x5700 ;; -monarch) DIM=2325x4500 ;; -c5) DIM=3827x5409 ;; -envDL) DIM=2599x5197 ;; -isob5|b5) DIM=4158x5906 ;; -jisb5) DIM=4299x6071 ;; -a5) DIM=3496x4961 ;; -folio) DIM=5100x7800 ;; -pa4) DIM=4961x6600 ;; -archA) DIM=5400x7200 ;; +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 -PAPERSIZE="-sPAPERSIZE=$PAPER"; +# +# 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 @@ -456,7 +522,7 @@ else esac fi -CRDBASE="$PREFIX/share/foo2hbpl/crd" +CRDBASE="$PREFIX/share/foo2hbpl1/crd" case "$MODEL" in 0) model=CLP-300;; 1) model=CLP-600;; @@ -549,7 +615,8 @@ 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 $CLIP_ALL -J "$LPJOB" -U "$USER" +| 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 @@ -558,7 +625,8 @@ 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 $CLIP_ALL" + "foo2hbpl1 -m$MEDIA $COPIES $CLIP_UL $CLIP_LR $CLIP_LOG \ + $SAVETONER $COLOR2MONO -D$DEBUG $BC $AIB" fi # diff --git a/foo2hbpl1.1in b/foo2hbpl1.1in index a6549b4..8df0a0d 100644 --- a/foo2hbpl1.1in +++ b/foo2hbpl1.1in @@ -14,37 +14,153 @@ foo2hbpl1 \- Convert Ghostscript output to an HBPLv1 printer stream .SH DESCRIPTION .B foo2hbpl1 converts Ghostscript pbmraw, pgmraw, ppmraw, or pamcmyk32 output -formats to monochrome or color HBPL version 1 streams, to drive the +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, -and Fuji-Xerox CP105b printers. +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 ZJS stream: +Create a black and white HBPL1v1 stream: .RS .nf -gs -q -dBATCH -dSAFER -dQUIET -dNOPAUSE \ - -sPAPERSIZE=letter -r600x600 -sDEVICE=pgmraw \ - -sOutputFile=- - < testpage.ps \ +gs \-q \-dBATCH \-dSAFER \-dQUIET \-dNOPAUSE \ + \-sPAPERSIZE=letter \-r600x600 \-sDEVICE=pgmraw \ + \-sOutputFile=- \- < testpage.ps \ | foo2hbpl1 >testpage.zm .fi .RE .P -Create a color ZJS stream: +Create a color HBPL1v1 stream: .RS .nf -gs -q -dBATCH -dSAFER -dQUIET -dNOPAUSE \ - -sPAPERSIZE=letter -g5100x6600 -r600x600 -sDEVICE=pamcmyk32 \ - -sOutputFile=- - < testpage.ps \ +gs \-q \-dBATCH \-dSAFER \-dQUIET \-dNOPAUSE \ + \-sPAPERSIZE=letter \-g5100x6600 \-r600x600 \-sDEVICE=pamcmyk32 \ + \-sOutputFile=- \- < testpage.ps \ | foo2hbpl1 >testpage.zc .fi .RE @@ -61,7 +177,9 @@ Peter Korf .br Dave Coffin .br -${URLHBPL}/ +Joe Da Silva +.br +${URLHBPL1}/ '\" '\" '\" diff --git a/foo2hbpl1.c b/foo2hbpl1.c index fb4f448..4e7245b 100644 --- a/foo2hbpl1.c +++ b/foo2hbpl1.c @@ -7,14 +7,25 @@ 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 was originally written by Dave Coffin in March 2014. +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 @@ -28,15 +39,12 @@ 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, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -If you want to use this program under different license conditions, -then contact the author for an arrangement. +along with this program; if not, see +. */ -static char Version[] = "$Id: foo2hbpl1.c,v 1.3 2014/03/30 05:08:32 rick Exp $"; +static const char Version[] = "$Id: foo2hbpl1.c,v 1.4 2026/07/14 12:00:00 joe Exp $"; #include #include @@ -49,16 +57,122 @@ static char Version[] = "$Id: foo2hbpl1.c,v 1.3 2014/03/30 05:08:32 rick Exp $"; #include #endif -/* - * Command line options - */ -int MediaCode = 0; -char *Username = NULL; -char *Filename = NULL; -int Clip[] = { 8,8,8,8 }; +#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(void) +usage(Job *job) { fprintf(stderr, "Usage:\n" @@ -70,57 +184,234 @@ usage(void) " gs -q -dBATCH -dSAFER -dQUIET -dNOPAUSE \\ \n" " -sPAPERSIZE=letter -r600x600 -sDEVICE=pamcmyk32 \\ \n" " -sOutputFile=- - < testpage.ps \\ \n" -" | foo2hbpl1 >testpage.zc\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" -"Options:\n" -"-m media Media code to send to printer [1 or 6]\n" -" 1=plain, 2=bond, 3=lwcard, 4=lwgcard, 5=labels,\n" -" 6=envelope, 7=recycled, 8=plain2, 9=bond2,\n" -" 10=lwcard2, 11=lwgcard2, 12=recycled2\n" -"-u left,top,right,bottom\n" -" Erase margins of specified width [%d,%d,%d,%d]\n" -"-J filename Filename string to send to printer\n" -"-U username Username string to send to printer\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" - , Clip[0], Clip[1], Clip[2], Clip[3] + , 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 -error(int fatal, char *fmt, ...) +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; - if (fatal) exit(fatal); + 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; + } } -struct stream +void +end_doc(Job *job, int done) { - unsigned char *buf; - int size, off, bits; -}; + printf("\033%%-12345X@PJL EOJ\n%s", + (job->Model > 0 && done > 0 ? "@PJL RESET\n" : "")); + debug(job, 1, "End printer JOB.\n"); +} void -putbits(struct stream *s, unsigned val, int nbits) +error(Job *job, int fatal, char *fmt, ...) { - if (s->off + 16 > s->size && - !(s->buf = realloc(s->buf, s->size += 0x100000))) - error (1, "Out of memory\n"); - if (s->bits) + 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) { - s->off--; - val |= s->buf[s->off] >> (8-s->bits) << nbits; - nbits += s->bits; + 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; + } } - s->bits = nbits & 7; + 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) - s->buf[s->off++] = val >> nbits; - s->buf[s->off++] = val << -nbits; + 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: @@ -157,9 +448,9 @@ putbits(struct stream *s, unsigned val, int nbits) 17057 111 111 11111111111111 */ void -put_len(struct stream *s, unsigned val) +put_len(Job *job, int n, unsigned val) { - unsigned code[] = + static const unsigned code[34] = { 1, 0, 2, 2, 2, 3, @@ -178,7 +469,7 @@ put_len(struct stream *s, unsigned val) if (val < 1 || val > 17057) return; while (val >= code[c+3]) c += 3; - putbits(s, val-code[c] + code[c+1], code[c+2]); + putbits(job, n, val-code[c] + code[c+1], code[c+2]); } /* @@ -217,9 +508,9 @@ put_len(struct stream *s, unsigned val) 128 11111110000 */ void -put_diff(struct stream *s, signed char val) +put_diff(Job *job, int n, signed char val) { - static unsigned short code[] = + static const unsigned short code[25] = { 2, 3, 3, 1, 4, 4, 3, 2, @@ -233,15 +524,15 @@ put_diff(struct stream *s, signed char val) switch (val) { - case 0: putbits(s, 0, 3); return; - case 1: putbits(s, 1, 3); return; - case -1: putbits(s, 2, 3); return; + 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(s, code[c+1], code[c+2]); - putbits(s, sign, 1); - putbits(s, abs-code[c], code[c+3]); + putbits(job, n, code[c+1], code[c+2]); + putbits(job, n, sign, 1); + putbits(job, n, abs-code[c], code[c+3]); } void @@ -255,28 +546,19 @@ setle(unsigned char *c, int s, int i) } void -start_doc(int color) +start_doc(Job *job, int done, int color) { - char reca[] = { 0x41,0x81,0xa1,0x00,0x82,0xa2,0x07,0x00,0x83,0xa2,0x01,0x00 }; + 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"; - char *mname[] = - { "", - "NORMAL", - "THICK", - "HIGHQUALITY", - "COAT2", - "LABEL", - "ENVELOPE", - "RECYCLED", - "NORMALREV", - "THICKSIDE2", - "HIGHQUALITYREV", - "COATEDPAPER2REV", - "RECYCLEREV", - }; t = time(NULL); tmp = localtime(&t); @@ -293,10 +575,10 @@ start_doc(int color) } #endif -/* Lines end with \n, not \r\n */ + /* Lines end with \n, not \r\n */ printf( - "\033%%-12345X@PJL SET STRINGCODESET=UTF8\n" + "\033%%-12345X%s@PJL SET STRINGCODESET=UTF8\n" "@PJL COMMENT DATE=%s\n" "@PJL COMMENT TIME=%s\n" "@PJL COMMENT DNAME=%s\n" @@ -314,7 +596,7 @@ start_doc(int color) "@PJL SET JOBATTR=\"@TRAP=ON\"\n" "@PJL SET JOBATTR=\"@JOAU=%s\"\n" "@PJL SET JOBATTR=\"@CNAM=%s\"\n" - "@PJL SET COPIES=1\n" + "@PJL SET COPIES=%d\n" "@PJL SET QTY=1\n" "@PJL SET PAPERDIRECTION=SEF\n" "@PJL SET RESOLUTION=600\n" @@ -337,107 +619,145 @@ start_doc(int color) "@PJL SET JOBATTR=\"@GDFT=0\"\n" "@PJL SET JOBATTR=\"@IDFT=0\"\n" "@PJL ENTER LANGUAGE=HBPL\n" - , datestr, timestr - , Filename ? Filename : "" - , Username ? Username : "" - , mname[MediaCode] + , (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" - , Username ? Username : "" - , cname); - fwrite (reca, 1, sizeof reca, stdout); + , 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 *)image) + off) -#define CP (((char *)image) + off) -#define DP (((char *)image) + off*deep) +#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(s,x) putbits(s, huff[hsel][x] >> 4, huff[hsel][x] & 15) +#define put_token(j,n,x) putbits(job,n,huff[hsel][x] >> 4, huff[hsel][x] & 15) void -encode_page(int color, int width, int height, char *image) +encode_page(Job *job, int color, int width, int height) { unsigned char head[90] = { - 0x43,0x91,0xa1,0x00,0x92,0xa1,0x01,0x93,0xa1,0x01,0x94,0xa1, - 0x00,0x95,0xc2,0x00,0x00,0x00,0x00,0x96,0xa1,0x00,0x97,0xc3, - 0x00,0x00,0x00,0x00,0x98,0xa1,0x00,0x99,0xa4,0x01,0x00,0x00, - 0x00,0x9a,0xc4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x9b, - 0xa1,0x00,0x9c,0xa1,0x01,0x9d,0xa1,0x00,0x9e,0xa1,0x02,0x9f, - 0xa1,0x05,0xa0,0xa1,0x08,0xa1,0xa1,0x00,0xa2,0xc4,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x52,0xa3,0xa1,0x00,0xa4, - 0xb1,0xa4 + 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,0x01,0x00,0x00,0x10,0x32,0x04,0x00, - 0xa1,0x42,0x00,0x00,0x00,0x00,0xff - }; - static short papers[] = - { // Official sizes to nearest 1/600 inch - // will accept +-1.5mm (35/600 inch) tolerance - 0, 5100, 6600, // Letter - 2, 5100, 8400, // Legal - 4, 4961, 7016, // A4 - 6, 4350, 6300, // Executive - 13, 2475, 5700, // #10 envelope - 15, 2325, 4500, // Monarch envelope - 17, 3827, 5409, // C5 envelope - 19, 2599, 5197, // DL envelope -// ??, 4158, 5906, // B5 ISO - 22, 4299, 6071, // B5 JIS - 30, 3496, 4961, // A5 - 410, 5100, 7800, // Folio + 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; - static int pagenum = 0; - struct stream stream[5] = { { 0 } }; + 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; - for (i = 0; i < sizeof papers / sizeof *papers; i++) + 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 = papers[i]; - if (!MediaCode) - MediaCode = paper & 1 ? 6 : 1; - if (!pagenum) - start_doc(color); - head[12] = paper >> 1; - if (paper == 510) + { + 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) { - setle (head+15, 2, (width*254+300)/600); // units of 0.1mm - setle (head+17, 2, (height*254+300)/600); - head[21] = 2; + 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, ++pagenum); - setle (head+39, 4, width); - setle (head+43, 4, height); - setle (head+70, 4, width); - setle (head+74, 4, height); + 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; - deep = 1 + color*3; for (i=1; i < 5; i++) dirs[i] -= width; if (!color) dirs[4] = -8; - blank = calloc(height+2, width/8); - memset (blank++, -color, width/8+1); + blank = blank0 = calloc(height+2, width/8); + memset(blank++, -color, width/8+1); for (row = 1; row <= height; row++) { - for (col = deep; col < deep*2; col++) - image[row*width*deep + col] = -1; + memset(job->image + row*width * deep + deep, -1, deep); for (col = 8; col < width*deep; col += 4) - if (*(int *)(image + row*width*deep + col)) + if (*(int *)(job->image + row*width*deep + col)) { for (col = 12; col < margin/8; col++) blank[row*(width/8)+col] = -1; @@ -445,8 +765,8 @@ encode_page(int color, int width, int height, char *image) break; } } - memset (image, -color, (width+1)*deep); - image += (width+1)*deep; + memset(job->image, -color, (width+1)*deep); + job->image += (width+1)*deep; blank += width/8; while (off < width * height) @@ -473,9 +793,9 @@ encode_page(int color, int width, int height, char *image) } if (brun == 0) { - put_token(stream, 5); + put_token(job, 0, 5); for (i = 0; i < deep; i++) - put_diff(stream+1+i, DP[i] - DP[i-deep]); + put_diff(job, 1+i, DP[i] - DP[i-deep]); bit = 0; off++; stat--; @@ -495,36 +815,41 @@ encode_page(int color, int width, int height, char *image) if (abs(stat) > 8 && ((stat >> 31) & 1) != hsel) { hsel ^= 1; - put_token(stream, 6); + put_token(job, 0, 6); } stat = 0; } stat += bdir == bit; - put_token(stream, bdir - bit); - put_len(stream, brun); + put_token(job, 0, bdir - bit); + put_len(job, 0, brun); bit = brun < 17057; off += brun; } - putbits(stream, 0xff, 8); + putbits(job, 0, 0xff, 8); for (total = 48, i = 0; i <= deep; i++) { - putbits(stream+i, 0xff, 8); - stream[i].off--; - setle (body+32 + i*4, 4, stream[i].off); - total += stream[i].off; + 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); + 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(stream[i].buf, 1, stream[i].off, stdout); - free(stream[i].buf); + fwrite(job->stream[i].buf, 1, job->stream[i].off, stdout); + job->PrintSize += job->stream[i].off; } - free(blank-width/8-1); + 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 @@ -533,7 +858,7 @@ encode_page(int color, int width, int height, char *image) #undef put_token int -getint(FILE *fp) +getint(Job *job, FILE *fp) { int c, ret; @@ -541,23 +866,38 @@ getint(FILE *fp) { while (isspace(c = fgetc(fp))); if (c == '#') - while (fgetc(fp) != '\n'); - else break; + { + 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(FILE *fp) +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); @@ -572,14 +912,14 @@ do_file(FILE *fp) goto six; case '6': deep = 1 + (ideep = 3); -six: iwide = getint(fp); - ihigh = getint(fp); - imax = type == '4' ? 255 : getint(fp); +six: iwide = getint(job, fp); + ihigh = getint(job, fp); + imax = type == '4' ? 255 : getint(job, fp); break; case '7': do { - if (!fgets(line, 128, fp)) goto fail; + if (!fgets(line, sizeof(line), fp)) goto fail; if (!strncmp(line, "WIDTH ",6)) iwide = atoi(line + 6); if (!strncmp(line, "HEIGHT ",7)) @@ -589,13 +929,15 @@ six: iwide = getint(fp); if (!strncmp(line, "MAXVAL ",7)) imax = atoi(line + 7); if (!strncmp(line, "TUPLTYPE ",9)) - strcpy (tupl, line + 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) @@ -603,78 +945,221 @@ six: iwide = getint(fp); else ibyte = wide >> 3; byte = wide * deep; - image = calloc (ihigh+2, byte); + + 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 (image, ibyte, 1, fp); - sp = image; - dp = image + row*byte; - for (col = 0; col < iwide; col++) + i = fread(job->image, ibyte, 1, fp); + if (row > lct && row <= lcb) { - dp += deep; - switch (ideep) + 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++) { - case 0: - *dp = ((image[col >> 3] >> (~col & 7)) & 1) * 255; - break; - case 1: - *dp = ~*sp; - break; - case 3: - 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 : 255; - break; - case 4: - for (i=0; i < 4; i++) - dp[i] = sp[((i-1) & 3)]; - break; + 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; } - sp += ideep; } - for (i = 0; i < deep*Clip[0]; i++) - image[row*byte + deep+i] = 0; - for (i = deep*(iwide-Clip[2]); i < byte; i++) - image[row*byte + deep+i] = 0; } - memset(image+deep, 0, byte*(Clip[1]+1)); - memset(image+deep + byte*(ihigh-Clip[3]+1), 0, byte*Clip[3]); - encode_page(deep > 1, iwide, ihigh, (char *) image); + 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"); + 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[]) { - int c, i; + 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:u:J:U:V")) != EOF) + while ((c = getopt(argc, argv, "m:n:p:u:l:z:L:J:U:S:D:tABV?h")) != EOF) switch (c) { - case 'm': MediaCode = atoi(optarg); break; - case 'u': if (sscanf(optarg, "%d,%d,%d,%d", - Clip, Clip+1, Clip+2, Clip+3) != 4) - error(1, "Must specify four clipping margins!\n"); - break; - case 'J': if (optarg[0]) Filename = optarg; break; - case 'U': if (optarg[0]) Username = optarg; break; - case 'V': printf("%s\n", Version); return 0; - default: usage(); return 1; + 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(stdin); - } + do_file(&job, stdin); else { for (i = 0; i < argc; ++i) @@ -682,11 +1167,12 @@ main(int argc, char *argv[]) FILE *ifp; if (!(ifp = fopen(argv[i], "r"))) - error(1, "Can't open '%s' for reading\n", argv[i]); - do_file(ifp); + error(&job, 1, "Can't open '%s' for reading\n", argv[i]); + do_file(&job, ifp); fclose(ifp); } } - puts("B\033%-12345X@PJL EOJ"); + if (job.pagenum) + end_doc(&job, 1); return 0; }