Skip to content

Instantly share code, notes, and snippets.

@defremov
Last active July 8, 2026 17:23
Show Gist options
  • Select an option

  • Save defremov/eda5e00596f2eded8b85 to your computer and use it in GitHub Desktop.

Select an option

Save defremov/eda5e00596f2eded8b85 to your computer and use it in GitHub Desktop.
Add print quality settings to foo2hbpl1 driver for HBPL1 printers, http://www.dechifro.org/hbpl/
diff --git a/foo2hbpl1.c b/foo2hbpl1.c
index fb4f448..d0ad6cd 100644
--- a/foo2hbpl1.c
+++ b/foo2hbpl1.c
@@ -52,11 +52,38 @@ static char Version[] = "$Id: foo2hbpl1.c,v 1.3 2014/03/30 05:08:32 rick Exp $";
/*
* Command line options
*/
-int MediaCode = 0;
+char *ClutFilePath = NULL;
+int DocType = 0;
+int OutputColor = 0;
+int DraftMode = 0;
+int NumCopies = 1;
+int Trapping = 0;
+int Screen = 0;
+int MediaCode = 1;
char *Username = NULL;
char *Filename = NULL;
int Clip[] = { 8,8,8,8 };
+char *cb_cmyk = "CMYK";
+char *cb_lmh = "LMH";
+char ColorBalance[] =
+/* +--- L = Low
+ |+-- M = Medium
+ ||+- H = High
+ ||| */
+ "333" /* C = Cyan */
+ "333" /* M = Magenta */
+ "333" /* Y = Yellow */
+ "333"; /* K = Black */
+/*
+ 0 = Lighter (-3)
+ 1 = Lighter (-2)
+ 2 = Lighter (-1)
+ * 3 = Normal
+ 4 = Darker (+1)
+ 5 = Darker (+2)
+ 6 = Darker (+3) */
+
void
usage(void)
{
@@ -73,15 +100,73 @@ usage(void)
" | 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"
+"-a values Color balance adjustment [default: %s]\n"
+" Argument is a string of 12 adjustment values\n"
+" in range of 0..6 each:\n"
+" 0 = Lighter (-3)\n"
+" 1 = Lighter (-2)\n"
+" 2 = Lighter (-1)\n"
+" 3 = Normal\n"
+" 4 = Darker (+1)\n"
+" 5 = Darker (+2)\n"
+" 6 = Darker (+3)\n"
+" in the following order:\n"
+" |C|C|C|M|M|M|Y|Y|Y|K|K|K|\n"
+" |l|m|h|l|m|h|l|m|h|l|m|h|\n"
+" where C=Cyan, M=Magenta, Y=Yellow, K=Black,\n"
+" l=Low, m=Medium, h=High.\n"
+"-c Print ppmraw in color mode.\n"
+" If not set print in black and white.\n"
+" Has no effect if document type (-i option) is 0\n"
+"-i type Document type code for print quality\n"
+" adjustment of ppmraw input [default: %d]\n"
+" 0=No adjustment, 1=Standard, 2=Photos,\n"
+" 3=Presentation, 4=Web Pages, 5=POP, 6=None\n"
+"-l path Pathname of the file containing color\n"
+" look-up tables. Must be specified for\n"
+" document types 1..5 in color mode.\n"
+"-m media Media code to send to printer [default: %d]\n"
+" 1=Plain\n"
+" 2=Plain, side 2\n"
+" 3=Bond\n"
+" 4=Bond, side 2\n"
+" 5=Recycled\n"
+" 6=Recycled, side 2\n"
+" 7=Labels\n"
+" 8=Lightweight Cardstock\n"
+" 9=Lightweight Cardstock, side 2\n"
+" 10=Lightweight Glossy Cardstock\n"
+" 11=Lightweight Glossy Cardstock, side 2\n"
+" 12=Letterhead\n"
+" 13=Letterhead, side 2\n"
+" 14=Pre-Printed\n"
+" 15=Pre-Printed, side 2\n"
+" 16=Hole Punched\n"
+" 17=Hole Punched, side 2\n"
+" 18=Colored\n"
+" 19=Colored, side 2\n"
+" 20=Custom Type\n"
+" 21=Custom Type, side 2\n"
+" 22=Special\n"
+" 23=Special, side 2\n"
+" 24=Envelope\n"
+"-n copies Number of copies, 1..100 [default: %d]\n"
+"-s type Screen type code [default: %d]\n"
+" 0=Auto, 1=Fineness, 2=Standard, 3=Gradation\n"
+"-t Print ppmraw in draft mode.\n"
+" Has no effect if document type (-i option) is 0\n"
"-u left,top,right,bottom\n"
-" Erase margins of specified width [%d,%d,%d,%d]\n"
+" Erase margins of specified width\n"
+" [default: %d,%d,%d,%d]\n"
"-J filename Filename string to send to printer\n"
+"-T Turn trapping on.\n"
"-U username Username string to send to printer\n"
"-V Version %s\n"
+ , ColorBalance
+ , DocType
+ , MediaCode
+ , NumCopies
+ , Screen
, Clip[0], Clip[1], Clip[2], Clip[3]
, Version);
}
@@ -98,6 +183,37 @@ error(int fatal, char *fmt, ...)
if (fatal) exit(fatal);
}
+enum doc_type_id {
+ DOC_NORMAL,
+ DOC_PHOTO,
+ DOC_PRESENTATION,
+ DOC_WEB,
+ DOC_POP,
+ DOC_NONE
+};
+
+struct doc_type_jobattr
+{
+ int color, mono;
+} screen_jobattr_index[] = {
+ {61, 64}, // Fineness
+ {62, 65}, // Standard
+ {63, 66} // Gradation
+ };
+
+static const struct doc_type
+{
+ int clut_id;
+ struct doc_type_jobattr jobattr;
+} doc_type_index[] = {
+ { 0, {11, 33}}, // Normal
+ { 2, {14, 33}}, // Photo
+ { 3, {15, 33}}, // Presentation
+ { 5, {17, 33}}, // Web
+ { 7, {19, 33}}, // POP
+ {18, {31, 33}} // Off
+ };
+
struct stream
{
unsigned char *buf;
@@ -262,20 +378,38 @@ start_doc(int color)
struct tm *tmp;
char datestr[16], timestr[16];
char cname[128] = "My Computer";
+ int i, doc_type_id;
+ static const char *doc_type_jobattr_cprucr[] = {"CPR", "UCR"};
+ static const char *doc_type_jobattr_trcscr[] = {"TRC", "SCR"};
+ static const char *doc_type_jobattr_prefix = "TGI";
+ int doc_type_jobattr = 0;
+ const struct doc_type_jobattr *s_doc_type_jobattr = NULL;
char *mname[] =
- { "",
- "NORMAL",
- "THICK",
- "HIGHQUALITY",
- "COAT2",
- "LABEL",
- "ENVELOPE",
- "RECYCLED",
- "NORMALREV",
- "THICKSIDE2",
- "HIGHQUALITYREV",
- "COATEDPAPER2REV",
- "RECYCLEREV",
+ {
+ "NORMAL",
+ "NORMALREV",
+ "HIGHQUALITY",
+ "HIGHQUALITYREV",
+ "RECYCLE",
+ "RECYCLEREV",
+ "LABEL",
+ "THICK",
+ "THICKSIDE2",
+ "COATEDPAPER2",
+ "COATEDPAPER2REV",
+ "LETTERHEAD",
+ "LETTERHEADREV",
+ "PREPRINTED",
+ "PREPRINTEDREV",
+ "PREPUNCHED",
+ "PREPUNCHEDREV",
+ "COLOR",
+ "COLORREV",
+ "USER1",
+ "USER1REV",
+ "SPECIAL",
+ "SPECIALREV",
+ "ENVELOPE"
};
t = time(NULL);
@@ -296,54 +430,98 @@ start_doc(int color)
/* 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);
+ "\033%%-12345X@PJL JOB MODE=PRINTER\n"
+ "@PJL SET STRINGCODESET=UTF8\n"
+ );
+
+ printf("@PJL COMMENT DATE=%s\n", datestr);
+ printf("@PJL COMMENT TIME=%s\n", timestr);
+ printf("@PJL COMMENT DNAME=%s\n", Filename ? Filename : "");
+ printf("@PJL SET JOBATTR=\"@LUNA=%s\"\n", Username ? Username : "");
+ printf("@PJL SET COPIES=%d\n", NumCopies);
+
+ printf(
+ "@PJL SET JOBATTR=\"@TRCH=OFF\"\n"
+ "@PJL SET DUPLEX=OFF\n"
+ );
+
+ for (i = 0; i < 12; i++) {
+ printf("@PJL SET JOBATTR=\"@AJ%c%c=%c\"\n",
+ cb_cmyk[i / 3], cb_lmh[i % 3], ColorBalance[i]);
+ }
+
+ printf("@PJL SET JOBATTR=\"@APSP=OFF\"\n");
+
+ printf("@PJL SET JOBATTR=\"@MSIP=%s\"\n", mname[MediaCode - 1]);
+ printf("@PJL SET RENDERMODE=%s\n", color ? "COLOR" : "GRAYSCALE");
+ printf("@PJL SET ECONOMODE=%s\n", DraftMode ? "ON" : "OFF");
+
+ printf("@PJL SET JOBATTR=\"@IREC=OFF\"\n");
+
+ printf("@PJL SET JOBATTR=\"@TRAP=%s\"\n",
+ color && Trapping ? "ON" : "OFF");
+
+ // "ON" only if 'Image Enhancement' option is enabled in original Xerox's driver.
+ printf("@PJL SET RET=%s\n", "OFF");
+
+ printf(
+ "@PJL SET JOBATTR=\"@TDFT=0\"\n"
+ "@PJL SET JOBATTR=\"@GDFT=0\"\n"
+ "@PJL SET JOBATTR=\"@IDFT=0\"\n"
+ "@PJL SET JOBATTR=\"@NLPP=1\"\n"
+ "@PJL SET JOBATTR=\"@HOAD=IC0A809\"\n"
+ );
+
+ printf("@PJL SET JOBATTR=\"@JOAU=%s\"\n", Username ? Username : "");
+ printf("@PJL SET JOBATTR=\"@CNAM=%s\"\n", cname);
+
+ printf(
+ "@PJL SET OUTBIN=FACEDOWN\n"
+ "@PJL SET JOBATTR=\"@PODR=NORMAL\"\n"
+ "@PJL SET SLIPSHEET=OFF\n"
+ "@PJL SET PAPERDIRECTION=SEF\n"
+ "@PJL SET RESOLUTION=600\n"
+ "@PJL SET BITSPERPIXEL=8\n"
+ "@PJL SET JOBATTR=\"@DRDM=RASTER\"\n"
+ );
+
+ doc_type_id = DocType == 0 ? 0 : DocType - 1;
+
+ if (color) {
+ for (i = 0; i < 6; i++) {
+ printf("@PJL SET JOBATTR=\"@%c%s=%d\"\n",
+ doc_type_jobattr_prefix[i % 3],
+ doc_type_jobattr_cprucr[i / 3],
+ doc_type_index[doc_type_id].jobattr.color);
+ }
+ }
+
+ if (Screen == 0) {
+ s_doc_type_jobattr = &doc_type_index[doc_type_id].jobattr;
+ } else {
+ s_doc_type_jobattr = &screen_jobattr_index[Screen - 1];
+ }
+ doc_type_jobattr = color ?
+ s_doc_type_jobattr->color : s_doc_type_jobattr->mono;
+ for (i = 0; i < 6; i++) {
+ printf("@PJL SET JOBATTR=\"@%c%s=%d\"\n",
+ doc_type_jobattr_prefix[i % 3],
+ doc_type_jobattr_trcscr[i / 3],
+ doc_type_jobattr);
+ }
+
+ if (!color) {
+ printf("@PJL SET JOBATTR=\"@PBLK=OFF\"\n");
+ }
+
+ printf("@PJL SET JOBATTR=\"@BANR=DEVICE\"\n");
+
+ if (doc_type_id == DOC_NONE) {
+ printf("@PJL SET JOBATTR=\"@FCMS=ON\"\n");
+ }
+
+ printf("@PJL ENTER LANGUAGE=HBPL\n");
+
fwrite (reca, 1, sizeof reca, stdout);
}
@@ -401,11 +579,14 @@ encode_page(int color, int width, int height, char *image)
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)
+ for (i = 0; i < sizeof papers / sizeof *papers; i+=3) {
+ if (abs(width-papers[i+1]) < 36 && abs(height-papers[i+2]) < 36) {
paper = papers[i];
+ break;
+ }
+ }
if (!MediaCode)
- MediaCode = paper & 1 ? 6 : 1;
+ MediaCode = paper & 1 ? 24 : 1;
if (!pagenum)
start_doc(color);
head[12] = paper >> 1;
@@ -532,6 +713,421 @@ encode_page(int color, int width, int height, char *image)
#undef BP
#undef put_token
+#define CLUT_BLK0_OFFSET 0x374
+#define CLUT_OFFSET 0x308
+#define CLUT_SIZE (17 * 17 * 17 * 4)
+#define CLUT_BLK_SIZE ((CLUT_OFFSET) + (CLUT_SIZE))
+#define FILE_CLUT_OFFSET(i) ((CLUT_BLK0_OFFSET) + (CLUT_BLK_SIZE) * (i) + (CLUT_OFFSET))
+
+#define CLUT_X_FACT (1 * 4)
+#define CLUT_Y_FACT (17 * 4)
+#define CLUT_Z_FACT (17 * 17 * 4)
+
+#define RGB_TO_GRAY(r, g, b) ((1229 * (r) + 2417 * (g) + 451 * (b)) >> 12)
+
+unsigned char*
+load_clut(const int clut_id, const char *clut_fname)
+{
+ unsigned char *clut;
+ FILE *clut_fp;
+
+ if ((clut = malloc(CLUT_SIZE * sizeof(char))) == NULL) {
+ error(1, "Can't allocate memory for CLUT\n");
+ }
+
+ if (!(clut_fp = fopen(clut_fname, "r"))) {
+ error(1, "Can't open '%s' for reading\n", clut_fname);
+ }
+
+ if (fseek(clut_fp, FILE_CLUT_OFFSET(clut_id), SEEK_SET) != 0
+ || fread(clut, sizeof(char), CLUT_SIZE, clut_fp) < CLUT_SIZE) {
+ fclose(clut_fp);
+ error(1, "Can't read CLUT #%d from '%s'\n", clut_id, clut_fname);
+ }
+
+ fclose(clut_fp);
+
+ return clut;
+}
+
+// Borrowed from Little Color Management System, http://www.littlecms.com
+// Copyright (c) 1998-2012 Marti Maria Saguer
+// Licensed under the MIT license
+// http://opensource.org/licenses/mit-license.php
+
+// Tetrahedral interpolation, using Sakamoto algorithm.
+void
+rgb_to_kcmy(const unsigned char clut[], unsigned char kcmy[], const unsigned char rgb[])
+{
+ int i;
+ int rx, ry, rz;
+ int x0, y0, z0;
+ int x1, y1, z1;
+ const unsigned char *plane;
+ int c0, c1, c2, c3;
+
+ rx = rgb[0] & 0xf; ry = rgb[1] & 0xf; rz = rgb[2] & 0xf;
+
+ if (rgb[0] == 255) {
+ x0 = CLUT_X_FACT << 4; x1 = 0;
+ } else {
+ x0 = (rgb[0] >> 4) * (x1 = CLUT_X_FACT);
+ }
+
+ if (rgb[1] == 255) {
+ y0 = CLUT_Y_FACT << 4; y1 = 0;
+ } else {
+ y0 = (rgb[1] >> 4) * (y1 = CLUT_Y_FACT);
+ }
+
+ if (rgb[2] == 255) {
+ z0 = CLUT_Z_FACT << 4; z1 = 0;
+ } else {
+ z0 = (rgb[2] >> 4) * (z1 = CLUT_Z_FACT);
+ }
+
+ clut = &clut[x0 + y0 + z0];
+
+ if (rx >= ry) {
+ if (ry >= rz) {
+ y1 += x1;
+ z1 += y1;
+ for (i = 0; i < 4; i++) {
+ plane = &clut[(i - 1) & 3]; // in KCMY order
+ c1 = plane[x1];
+ c2 = plane[y1];
+ c3 = plane[z1];
+ c0 = plane[0];
+ c3 -= c2;
+ c2 -= c1;
+ c1 -= c0;
+ kcmy[i] = c0 + ((c1 * rx + c2 * ry + c3 * rz) >> 4);
+ }
+ } else if (rz >= rx) {
+ x1 += z1;
+ y1 += x1;
+ for (i = 0; i < 4; i++) {
+ plane = &clut[(i - 1) & 3];
+ c1 = plane[x1];
+ c2 = plane[y1];
+ c3 = plane[z1];
+ c0 = plane[0];
+ c2 -= c1;
+ c1 -= c3;
+ c3 -= c0;
+ kcmy[i] = c0 + ((c1 * rx + c2 * ry + c3 * rz) >> 4);
+ }
+ } else {
+ z1 += x1;
+ y1 += z1;
+ for (i = 0; i < 4; i++) {
+ plane = &clut[(i - 1) & 3];
+ c1 = plane[x1];
+ c2 = plane[y1];
+ c3 = plane[z1];
+ c0 = plane[0];
+ c2 -= c3;
+ c3 -= c1;
+ c1 -= c0;
+ kcmy[i] = c0 + ((c1 * rx + c2 * ry + c3 * rz) >> 4);
+ }
+ }
+ } else {
+ if (rx >= rz) {
+ x1 += y1;
+ z1 += x1;
+ for (i = 0; i < 4; i++) {
+ plane = &clut[(i - 1) & 3];
+ c1 = plane[x1];
+ c2 = plane[y1];
+ c3 = plane[z1];
+ c0 = plane[0];
+ c3 -= c1;
+ c1 -= c2;
+ c2 -= c0;
+ kcmy[i] = c0 + ((c1 * rx + c2 * ry + c3 * rz) >> 4);
+ }
+ } else if (ry >= rz) {
+ z1 += y1;
+ x1 += z1;
+ for (i = 0; i < 4; i++) {
+ plane = &clut[(i - 1) & 3];
+ c1 = plane[x1];
+ c2 = plane[y1];
+ c3 = plane[z1];
+ c0 = plane[0];
+ c1 -= c3;
+ c3 -= c2;
+ c2 -= c0;
+ kcmy[i] = c0 + ((c1 * rx + c2 * ry + c3 * rz) >> 4);
+ }
+ } else {
+ y1 += z1;
+ x1 += y1;
+ for (i = 0; i < 4; i++) {
+ plane = &clut[(i - 1) & 3];
+ c1 = plane[x1];
+ c2 = plane[y1];
+ c3 = plane[z1];
+ c0 = plane[0];
+ c1 -= c2;
+ c2 -= c3;
+ c3 -= c0;
+ kcmy[i] = c0 + ((c1 * rx + c2 * ry + c3 * rz) >> 4);
+ }
+ }
+ }
+
+ return;
+}
+
+enum proc_mode {
+ PROC_BITMAP,
+ PROC_GRAY,
+ PROC_RGB_NOADJ,
+ PROC_RGB_ADJ_COLOR,
+ PROC_RGB_ADJ_COLOR_NONE,
+ PROC_RGB_ADJ_COLOR_DRAFT,
+ PROC_RGB_ADJ_COLOR_NONE_DRAFT,
+ PROC_RGB_ADJ_MONO,
+ PROC_RGB_ADJ_MONO_DRAFT,
+ PROC_CMYK
+};
+
+enum margin_pos {
+ LEFT,
+ TOP,
+ RIGHT,
+ BOTTOM,
+};
+
+static unsigned char trc_rgb_color_std[] = {
+ 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 14,
+ 15, 16, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 29, 31, 32, 33, 34, 35,
+ 36, 37, 38, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 74, 75,
+ 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 90, 91, 92, 93, 94,
+ 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+ 110, 111, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
+ 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152,
+ 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166,
+ 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180,
+ 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
+ 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208,
+ 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222,
+ 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236,
+ 237, 238, 239, 240, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249,
+ 250, 251, 252, 253, 254, 255
+};
+
+// Identity transformation
+static unsigned char trc_rgb_color_none[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
+ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
+ 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
+ 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
+ 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
+ 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135,
+ 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+ 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163,
+ 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177,
+ 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
+ 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205,
+ 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
+ 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233,
+ 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247,
+ 248, 249, 250, 251, 252, 253, 254, 255
+};
+
+static unsigned char trc_rgb_gray_std[] = {
+ 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 4, 6, 7, 10, 12, 13, 15, 16, 19, 20,
+ 23, 23, 26, 27, 28, 31, 32, 34, 35, 36, 38, 40, 41, 43, 44, 45, 46, 48,
+ 50, 51, 52, 54, 55, 56, 57, 59, 61, 62, 63, 64, 66, 67, 68, 69, 70, 71,
+ 74, 75, 76, 77, 78, 79, 80, 81, 83, 84, 85, 86, 87, 88, 90, 91, 92, 93,
+ 94, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+ 110, 111, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
+ 139, 140, 141, 142, 143, 144, 145, 145, 146, 147, 148, 149, 150, 151,
+ 152, 153, 154, 155, 156, 157, 158, 159, 159, 160, 161, 162, 163, 164,
+ 165, 166, 167, 168, 169, 169, 170, 171, 172, 173, 174, 175, 176, 177,
+ 178, 178, 179, 180, 181, 182, 183, 184, 185, 185, 186, 187, 188, 189,
+ 190, 191, 192, 192, 193, 194, 195, 196, 197, 198, 198, 199, 200, 201,
+ 202, 203, 204, 204, 205, 206, 207, 208, 209, 209, 210, 211, 212, 213,
+ 214, 215, 215, 216, 217, 218, 219, 220, 220, 221, 222, 223, 224, 225,
+ 225, 226, 227, 228, 229, 229, 230, 231, 232, 233, 234, 234, 235, 236,
+ 237, 238, 238, 239, 240, 240, 241, 241, 242, 243, 244, 245, 245, 246,
+ 247, 248, 249, 249, 250, 251, 252, 253, 253, 254, 255
+};
+
+static unsigned char trc_rgb_gray_none[] = {
+ 0, 1, 1, 2, 2, 3, 4, 6, 7, 8, 10, 11, 13, 15, 16, 18, 20, 21, 23, 24,
+ 26, 27, 29, 30, 32, 33, 34, 36, 37, 39, 40, 41, 43, 44, 45, 47, 48, 49,
+ 50, 52, 53, 54, 55, 57, 58, 59, 60, 62, 63, 64, 65, 66, 68, 69, 70, 71,
+ 72, 73, 75, 76, 77, 78, 79, 80, 81, 82, 84, 85, 86, 87, 88, 89, 90, 91,
+ 92, 93, 94, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108,
+ 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122,
+ 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136,
+ 137, 138, 139, 140, 141, 142, 143, 144, 144, 145, 146, 147, 148, 149,
+ 150, 151, 152, 153, 154, 155, 156, 157, 158, 158, 159, 160, 161, 162,
+ 163, 164, 165, 166, 167, 168, 168, 169, 170, 171, 172, 173, 174, 175,
+ 176, 177, 177, 178, 179, 180, 181, 182, 183, 184, 184, 185, 186, 187,
+ 188, 189, 190, 191, 191, 192, 193, 194, 195, 196, 197, 197, 198, 199,
+ 200, 201, 202, 203, 203, 204, 205, 206, 207, 208, 208, 209, 210, 211,
+ 212, 213, 214, 214, 215, 216, 217, 218, 219, 219, 220, 221, 222, 223,
+ 224, 224, 225, 226, 227, 228, 228, 229, 230, 231, 232, 233, 233, 234,
+ 235, 236, 237, 237, 238, 239, 240, 241, 241, 242, 243, 244, 245, 245,
+ 246, 247, 248, 249, 249, 250, 251, 252, 253, 253, 254, 255
+};
+
+static unsigned char trc_draft[] = {
+ 0, 0, 1, 2, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 9, 10, 11, 11, 12, 13, 14, 14,
+ 15, 16, 16, 17, 18, 18, 19, 20, 21, 21, 22, 23, 23, 24, 25, 25, 26, 27,
+ 28, 28, 29, 30, 30, 31, 32, 32, 33, 34, 35, 35, 36, 37, 37, 38, 39, 39,
+ 40, 41, 42, 42, 43, 44, 44, 45, 46, 46, 47, 48, 49, 49, 50, 51, 51, 52,
+ 53, 53, 54, 55, 56, 56, 57, 58, 58, 59, 60, 60, 61, 62, 62, 63, 64, 65,
+ 65, 66, 67, 67, 68, 69, 70, 70, 71, 72, 72, 73, 74, 74, 75, 76, 77, 77,
+ 78, 79, 79, 80, 81, 81, 82, 83, 84, 84, 85, 86, 86, 87, 88, 88, 89, 90,
+ 91, 91, 92, 93, 93, 94, 95, 95, 96, 97, 98, 98, 99, 100, 100, 101, 102,
+ 102, 103, 104, 105, 105, 106, 107, 107, 108, 109, 109, 110, 111, 112,
+ 112, 113, 114, 114, 115, 116, 116, 117, 118, 118, 119, 120, 121, 121,
+ 122, 123, 123, 124, 125, 125, 126, 127, 128, 128, 129, 130, 130, 131,
+ 132, 133, 133, 134, 135, 135, 136, 137, 137, 138, 139, 140, 140, 141,
+ 142, 142, 143, 144, 144, 145, 146, 147, 147, 148, 149, 149, 150, 151,
+ 151, 152, 153, 154, 154, 155, 156, 156, 157, 158, 158, 159, 160, 161,
+ 161, 162, 163, 163, 164, 165, 165, 166, 167, 168, 168, 169, 170, 170,
+ 171, 172, 172, 173, 174, 175, 175, 176, 177, 177, 178
+};
+
+void
+detect_rgb_content(unsigned char *const image,
+ const int wide, const int high, const int margins[4],
+ int *is_color, int *is_blank)
+{
+ const int deep = 3;
+ const int byte = wide * deep;
+ int i, m[4];
+ int start_row, end_row;
+ int start_col, end_col;
+ int start_col_off, end_col_off;
+ unsigned char *rowp, *end_rowp, *startp, *endp, *p;
+ int color_detected = 0;
+ int gray_detected = 0;
+
+ for (i = 0; i < 4; i++) {
+ m[i] = margins[i] < 0 ? 0 : margins[i];
+ }
+
+ start_row = m[TOP];
+ end_row = high - m[BOTTOM];
+ start_col = m[LEFT];
+ end_col = wide - m[RIGHT];
+
+ if (start_row < end_row && start_col < end_col) {
+ end_rowp = image + (end_row - 1) * byte;
+ start_col_off = start_col * deep;
+ end_col_off = (end_col - 1) * deep;
+
+ for (rowp = image + start_row * byte;; rowp += byte) {
+ startp = rowp + start_col_off;
+ endp = rowp + end_col_off;
+
+ for (p = startp;; p += deep) {
+ if (p[0] != p[1] || p[1] != p[2]) {
+ color_detected = 1;
+ goto end_search;
+ } else if (p[0] != 255) {
+ gray_detected = 1;
+ }
+ if (p == endp) {
+ break;
+ }
+ }
+ if (rowp == end_rowp) {
+ break;
+ }
+ }
+ }
+
+end_search: *is_color = color_detected;
+ *is_blank = !(color_detected || gray_detected);
+
+ return;
+}
+
+void
+apply_trapping(unsigned char *image, int wide, int high, const int margins[4])
+{
+ int byte;
+ int i, j, k, m[4], n[24];
+ int skip_mlr, eff_byte;
+ unsigned char *p, *row_endp, *img_endp, *np;
+
+ for (i = 0; i < 4; i++) {
+ m[i] = margins[i] > 1 ? margins[i] : 2;
+ }
+
+ if (m[TOP] < high - m[BOTTOM] && m[LEFT] < wide - m[RIGHT]) {
+
+ byte = wide * 4;
+
+ // Array `n` contains relative offsets
+ // of the neighbor pixels in a 5x5 window.
+ //
+ // +----+----+----+----+----+
+ // | 0 | 1 | 2 | 3 | 4 |
+ // +----+----+----+----+----+
+ // | 5 | 6 | 7 | 8 | 9 |
+ // +----+----+---------+----+
+ // | 10 | 11 | p | 12 | 13 |
+ // +----+----+---------+----+
+ // | 14 | 15 | 16 | 17 | 18 |
+ // +----+----+----+----+----+
+ // | 19 | 20 | 21 | 22 | 23 |
+ // +----+----+----+----+----+
+
+ for (k = 0, i = -2; i < 3; i++) {
+ for (j = -2; j < 3; j++) {
+ if (i || j) {
+ n[k++] = byte * i + 4 * j;
+ }
+ }
+ }
+
+ p = image + m[TOP] * byte + m[LEFT] * 4;
+ img_endp = image + (high - m[BOTTOM]) * byte - m[RIGHT] * 4;
+ skip_mlr = (m[LEFT] + m[RIGHT]) * 4;
+ eff_byte = byte - skip_mlr;
+ row_endp = p + eff_byte;
+
+ for (; p != img_endp; p += 4) {
+ if (p == row_endp) {
+ p += skip_mlr;
+ row_endp = p + eff_byte;
+ }
+
+ if (p[0] < 204) {
+ continue;
+ }
+
+ for (i = 0; i < 24; i++) {
+ np = p + n[i];
+ if (np[0] < 204) {
+ for (j = 1; j < 4; j++) {
+ if (p[j] < np[j]) {
+ p[j] = np[j];
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return;
+}
+
int
getint(FILE *fp)
{
@@ -557,6 +1153,15 @@ do_file(FILE *fp)
int wide, deep, byte, row, col, i, k;
char tupl[128], line[128];
unsigned char *image, *sp, *dp;
+ int buf_size, ibuf_size;
+ unsigned char *sstart, *dstart;
+ int doc_type_id = 0;
+ const struct doc_type *s_doc_type;
+ unsigned char *clut = NULL;
+ enum proc_mode proc_mode;
+ unsigned char *trc = trc_rgb_color_none;
+ int is_color = 0;
+ int is_blank = 0;
while ((type = fgetc(fp)) != EOF)
{
@@ -566,12 +1171,37 @@ do_file(FILE *fp)
{
case '4':
deep = 1 + (ideep = 0);
+ proc_mode = PROC_BITMAP;
goto six;
case '5':
deep = ideep = 1;
+ proc_mode = PROC_GRAY;
goto six;
case '6':
- deep = 1 + (ideep = 3);
+ if (DocType != 0) {
+ doc_type_id = DocType - 1;
+ if (OutputColor) {
+ deep = 1 + (ideep = 3);
+ if (doc_type_id == DOC_NONE) {
+ proc_mode = DraftMode ?
+ PROC_RGB_ADJ_COLOR_NONE_DRAFT : PROC_RGB_ADJ_COLOR_NONE;
+ } else {
+ if (!ClutFilePath) {
+ error(1, "CLUT file path (option -l) is not set.\n");
+ }
+ proc_mode = DraftMode ?
+ PROC_RGB_ADJ_COLOR_DRAFT : PROC_RGB_ADJ_COLOR;
+ }
+ } else {
+ ideep = 3;
+ deep = 1;
+ proc_mode = DraftMode ?
+ PROC_RGB_ADJ_MONO_DRAFT : PROC_RGB_ADJ_MONO;
+ }
+ } else {
+ deep = 1 + (ideep = 3);
+ proc_mode = PROC_RGB_NOADJ;
+ }
six: iwide = getint(fp);
ihigh = getint(fp);
imax = type == '4' ? 255 : getint(fp);
@@ -592,6 +1222,7 @@ six: iwide = getint(fp);
strcpy (tupl, line + 9);
} while (strcmp(line, "ENDHDR\n"));
if (ideep != 4 || strcmp(tupl, "CMYK\n")) goto fail;
+ proc_mode = PROC_CMYK;
break;
default:
goto fail;
@@ -603,37 +1234,118 @@ six: iwide = getint(fp);
else
ibyte = wide >> 3;
byte = wide * deep;
- image = calloc (ihigh+2, byte);
+
+ buf_size = (ibyte > byte ? ibyte : byte) * ihigh + byte * 2;
+
+ if ((image = malloc(buf_size)) == NULL) {
+ error(1, "Can't allocate memory for page buffer\n");
+ }
+
+ dstart = image + byte;
+ ibuf_size = ibyte * ihigh;
+ sstart = image + (buf_size - ibuf_size);
+
+ if (fread (sstart, ibuf_size, 1, fp) != 1) {
+ error(1, "Error reading image data\n");
+ }
+
+ if (ideep == 3 && DocType != 0) {
+ detect_rgb_content(sstart, iwide, ihigh, Clip,
+ &is_color, &is_blank);
+
+ if (doc_type_id == DOC_NONE) {
+ trc = is_color ? trc_rgb_color_none : trc_rgb_gray_none;
+ } else {
+ s_doc_type = &doc_type_index[doc_type_id];
+ clut = load_clut(s_doc_type->clut_id, ClutFilePath);
+ trc = is_color ? trc_rgb_color_std : trc_rgb_gray_std;
+ }
+ }
+
for (row = 1; row <= ihigh; row++)
{
- i = fread (image, ibyte, 1, fp);
- sp = image;
- dp = image + row*byte;
+ sp = sstart;
+ dp = dstart;
for (col = 0; col < iwide; col++)
{
dp += deep;
- switch (ideep)
+ switch (proc_mode)
{
- case 0:
- *dp = ((image[col >> 3] >> (~col & 7)) & 1) * 255;
+ case PROC_BITMAP:
+ *dp = ((sp[col >> 3] >> (~col & 7)) & 1) * 255;
break;
- case 1:
+ case PROC_GRAY:
*dp = ~*sp;
break;
- case 3:
+ case PROC_RGB_NOADJ:
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:
+ case PROC_RGB_ADJ_COLOR:
+ for (i = 0; i < 3; i++) {
+ sp[i] = trc[sp[i]];
+ }
+ rgb_to_kcmy(clut, dp, sp);
+ break;
+ case PROC_RGB_ADJ_COLOR_NONE:
+ for (i = 0; i < 3; i++) {
+ sp[i] = trc[sp[i]];
+ }
+ 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 - sp[i];
+ break;
+ case PROC_RGB_ADJ_COLOR_DRAFT:
+ for (i = 0; i < 3; i++) {
+ sp[i] = trc[sp[i]];
+ }
+ rgb_to_kcmy(clut, dp, sp);
+ for (i = 0; i < 4; i++) {
+ dp[i] = trc_draft[dp[i]];
+ }
+ break;
+ case PROC_RGB_ADJ_COLOR_NONE_DRAFT:
+ for (i = 0; i < 3; i++) {
+ sp[i] = trc[sp[i]];
+ }
+ 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 - sp[i];
+ for (i = 0; i < 4; i++) {
+ dp[i] = trc_draft[dp[i]];
+ }
+ break;
+ case PROC_RGB_ADJ_MONO:
+ for (i = 0; i < 3; i++) {
+ sp[i] = trc[sp[i]];
+ }
+ *dp = ~RGB_TO_GRAY(sp[0], sp[1], sp[2]);
+ break;
+ case PROC_RGB_ADJ_MONO_DRAFT:
+ for (i = 0; i < 3; i++) {
+ sp[i] = trc[sp[i]];
+ }
+ *dp = ~RGB_TO_GRAY(sp[0], sp[1], sp[2]);
+ for (i = 0; i < 4; i++) {
+ dp[i] = trc_draft[dp[i]];
+ }
+ break;
+ case PROC_CMYK:
for (i=0; i < 4; i++)
dp[i] = sp[((i-1) & 3)];
break;
}
sp += ideep;
}
+ sstart += ibyte;
+ dstart += byte;
for (i = 0; i < deep*Clip[0]; i++)
image[row*byte + deep+i] = 0;
for (i = deep*(iwide-Clip[2]); i < byte; i++)
@@ -641,8 +1353,14 @@ six: iwide = getint(fp);
}
memset(image+deep, 0, byte*(Clip[1]+1));
memset(image+deep + byte*(ihigh-Clip[3]+1), 0, byte*Clip[3]);
+ if (deep == 4 && Trapping) {
+ apply_trapping(image + byte + deep, iwide, ihigh, Clip);
+ }
encode_page(deep > 1, iwide, ihigh, (char *) image);
free(image);
+ if (ideep == 3 && DocType != 0 && doc_type_id != DOC_NONE) {
+ free(clut);
+ }
}
return;
fail:
@@ -654,15 +1372,61 @@ main(int argc, char *argv[])
{
int c, i;
- while ( (c = getopt(argc, argv, "m:u:J:U:V")) != EOF)
+ while ( (c = getopt(argc, argv, "a:ci:l:m:s:tu:J:TU:V")) != EOF)
switch (c)
{
- case 'm': MediaCode = atoi(optarg); break;
+ case 'a':
+ for (i = 0; i < 12 && optarg[i]; i++) {
+ if (optarg[i] >= '0' && optarg[i] <= '6') {
+ ColorBalance[i] = optarg[i];
+ } else {
+ error(1, "Color balance values must be in range 0..6\n");
+ }
+ }
+ break;
+ case 'c':
+ OutputColor = 1;
+ break;
+ case 'i':
+ if (sscanf(optarg, "%d", &DocType) != 1
+ || DocType < 0 || DocType > 6
+ ) {
+ error(1, "Document type code must be in range 1..6.\n");
+ }
+ break;
+ case 'l': if (optarg[0]) ClutFilePath = optarg; break;
+ case 'm':
+ if (sscanf(optarg, "%d", &MediaCode) != 1
+ || MediaCode < 1 || MediaCode > 24
+ ) {
+ error(1, "Media code must be in range 1..24.\n");
+ }
+ break;
+ case 'n':
+ if (sscanf(optarg, "%d", &NumCopies) != 1
+ || NumCopies < 1 || NumCopies > 24
+ ) {
+ error(1, "Number of copies must be in range 1..100.\n");
+ }
+ break;
+ case 's':
+ if (sscanf(optarg, "%d", &Screen) != 1
+ || Screen < 0 || Screen > 3
+ ) {
+ error(1, "Screen type code must be in range 0..3.\n");
+ }
+ break;
+ case 't':
+ DraftMode = 1;
+ 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 'T':
+ Trapping = 1;
+ break;
case 'U': if (optarg[0]) Username = optarg; break;
case 'V': printf("%s\n", Version); return 0;
default: usage(); return 1;
@defremov

Copy link
Copy Markdown
Author

Unfortunately the xerox driver are only 32-bit which the new Ubuntu version doesen't support.

I have googled up a bit the situation with Ubuntu, they seem to promise that they will support 32-bit printer drivers.

@Norbert01

Copy link
Copy Markdown

yeah, got the xerox driver working.

In the xerox ppd are some good examples how modify the ppd.

@JoesCat

JoesCat commented Jul 7, 2026

Copy link
Copy Markdown

Hi @defremov - I was looking at the original HBPLv1 code by Dave Coffin, and it follows a certain sequence of 6 codes, which I'm assuming is probably based on his printer of that time, maybe a Dell 1250c, or one of the other 4 listed.
Looking at the 24 MediaCodes you have listed, do these all work? which printer is it?

@defremov

defremov commented Jul 8, 2026

Copy link
Copy Markdown
Author

@JoesCat, mine was Xerox Phaser 6010N. As to whether those codes worked I can't even remember now but I am pretty sure I didn't test them all at the time by actually printing something using each one of them.

@JoesCat

JoesCat commented Jul 8, 2026

Copy link
Copy Markdown

Reading the conversation above, I sort of guessed that, but thanks for the confirmation, plus the additional info. I have the same one. It's a pretty good printer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment