/* pbmtoeps.c - read a PBM image and produce Epson graphics ** ** Copyright (C) 1990 by John Tiller (tiller@galois.msfc.nasa.gov) ** and Jef Poskanzer. ** ** Permission to use, copy, modify, and distribute this software and its ** documentation for any purpose and without fee is hereby granted, provided ** that the above copyright notice appear in all copies and that both that ** copyright notice and this permission notice appear in supporting ** documentation. This software is provided "as is" without express or ** implied warranty. */ #define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */ #define _BSD_SOURCE /* Make sure strcaseeq() is in nstring.h */ #include #include "pm_c_util.h" #include "mallocvar.h" #include "nstring.h" #include "shhopt.h" #include "pbm.h" static char const esc = 033; enum epsonProtocol {ESCP9, ESCP}; enum adjacence {ADJACENT_ANY, ADJACENT_YES, ADJACENT_NO}; struct CmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ const char * inputFileName; /* '-' if stdin */ unsigned int dpi; /* zero means "any" */ enum adjacence adjacence; enum epsonProtocol protocol; }; static void parseCommandLine(int argc, const char ** argv, struct CmdlineInfo * cmdlineP ) { /*---------------------------------------------------------------------------- Parse program command line described in Unix standard form by argc and argv. Return the information in the options as *cmdlineP. If command line is internally inconsistent (invalid options, etc.), issue error message to stderr and abort program. Note that the strings we return are stored in the storage that was passed to us as the argv array. We also trash *argv. -----------------------------------------------------------------------------*/ optEntry * option_def; optStruct3 opt; unsigned int option_def_index; char * protocol; unsigned int adjacentSpec, nonadjacentSpec; unsigned int dpiSpec, protocolSpec; MALLOCARRAY_NOFAIL(option_def, 100); option_def_index = 0; /* incremented by OPTENT3 */ OPTENT3(0, "protocol", OPT_STRING, &protocol, &protocolSpec, 0); OPTENT3(0, "dpi", OPT_UINT, &cmdlineP->dpi, &dpiSpec, 0); OPTENT3(0, "adjacent", OPT_FLAG, NULL, &adjacentSpec, 0); OPTENT3(0, "nonadjacent", OPT_FLAG, NULL, &nonadjacentSpec, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ opt.allowNegNum = FALSE; /* We have no parms that are negative numbers */ pm_optParseOptions4( &argc, argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ if (!dpiSpec) cmdlineP->dpi = 0; else { if (cmdlineP->dpi == 0) pm_error("-dpi must be positive"); } if (!protocolSpec) cmdlineP->protocol = ESCP9; else { if (strcaseeq(protocol, "escp9")) cmdlineP->protocol = ESCP9; else if (strcaseeq(protocol, "escp")) cmdlineP->protocol = ESCP; else if (strcaseeq(protocol, "escp2")) pm_error("This program cannot do ESC/P2. Try Pbmtoescp2."); else pm_error("Unrecognized value '%s' for -protocol. " "Only recognized values are 'escp9' and 'escp'", protocol); } if (adjacentSpec && nonadjacentSpec) pm_error("You can't specify both -adjacent and -nonadjacent"); else if (adjacentSpec) cmdlineP->adjacence = ADJACENT_YES; else if (nonadjacentSpec) cmdlineP->adjacence = ADJACENT_NO; else cmdlineP->adjacence = ADJACENT_ANY; if (argc-1 < 1) cmdlineP->inputFileName = "-"; else { cmdlineP->inputFileName = argv[1]; if (argc-1 > 1) pm_error("Too many arguments (%d). The only non-option argument " "is the file name", argc-1); } free(option_def); } static unsigned int lineWidth(const bit ** const stripeBits, unsigned int const cols, unsigned int const stripeRows) { /*---------------------------------------------------------------------------- Return the column number just past the rightmost column of the stripe stripeBits[] that contains at least some black. The stripe is 'cols' wide by 'stripeRows' high. -----------------------------------------------------------------------------*/ unsigned int col; unsigned int endSoFar; endSoFar = 0; for (col = 0; col < cols; ++ col) { unsigned int stripeRow; /* row number within stripe */ for (stripeRow = 0; stripeRow < stripeRows; ++stripeRow) { if (stripeBits[stripeRow][col] == PBM_BLACK) endSoFar = col+1; } } return endSoFar; } static void printStripe(const bit ** const stripeBits, unsigned int const cols, unsigned int const stripeRows, char const m) { /*---------------------------------------------------------------------------- Print one stripe (a group of rows printed with one pass of the print head. The stripe is cols columns wide by stripeRows high. stripeBits[row][col] is the pixel value for Row row, Column col within the stripe. 'm' is the "m" parameter for the Select Bit Image command. It controls such things as the horizontal density. -----------------------------------------------------------------------------*/ unsigned int col; /* Print header of Select Bit Image command */ printf("%c%c%c%c%c", esc, '*', m, cols % 256, cols / 256); /* Print the data part of the Select Bit Image command */ for (col = 0; col < cols; ++col) { unsigned int stripeRow; int val; val = 0; for (stripeRow = 0; stripeRow < stripeRows; ++stripeRow) if (stripeBits[stripeRow][col] == PBM_BLACK) val |= (1 << (8-1-stripeRow)); putchar(val); } } static void computeM(enum epsonProtocol const protocol, unsigned int const dpi, enum adjacence const adjacence, char * const mP) { /*---------------------------------------------------------------------------- Compute the "m" parameter for the Select Bit Image command. -----------------------------------------------------------------------------*/ switch (dpi) { case 0: /* Special value meaning "any dpi you feel is appropriate" */ if (adjacence == ADJACENT_NO) *mP = 2; else { switch (protocol) { case ESCP9: *mP = 5; break; case ESCP: *mP = 6; break; } } break; case 60: if (adjacence == ADJACENT_NO) pm_error("You can't print at %u dpi " "with adjacent dot printing", dpi); *mP = 0; break; case 120: *mP = adjacence == ADJACENT_NO ? 2 : 1; break; case 240: if (adjacence == ADJACENT_YES) pm_error("You can't print at %u dpi " "without adjacent dot printing", dpi); *mP = 3; break; case 80: if (adjacence == ADJACENT_NO) pm_error("You can't print at %u dpi " "with adjacent dot printing", dpi); *mP = 4; break; case 72: if (protocol != ESCP9) pm_error("%u dpi is possible only with the ESC/P 9-pin protocol", dpi); if (adjacence == ADJACENT_NO) pm_error("You can't print at %u dpi " "with adjacent dot printing", dpi); *mP = 5; break; case 90: if (adjacence == ADJACENT_NO) pm_error("You can't print at %u dpi " "with adjacent dot printing", dpi); *mP = 6; break; case 144: if (protocol != ESCP9) pm_error("%u dpi is possible only with the ESC/P 9-pin protocol", dpi); if (adjacence == ADJACENT_NO) pm_error("You can't print at %u dpi " "with adjacent dot printing", dpi); *mP = 7; break; default: pm_error("Invalid DPI value: %u. This program knows only " "60, 72, 80, 90, 120, 144, and 240.", dpi); } } static void convertToEpson(const bit ** const bits, int const cols, int const rows, enum epsonProtocol const protocol, unsigned int const dpi, enum adjacence const adjacence) { unsigned int const rowsPerStripe = 8; unsigned int const stripeCt = (rows + rowsPerStripe-1) / rowsPerStripe; unsigned int stripe; char m; computeM(protocol, dpi, adjacence, &m); /* Change line spacing to 8/72 inches. */ printf("%c%c%c", esc, 'A', 8); /* Write out the rows, one stripe at a time. A stripe is 8 rows -- the amount written in one pass of the print head. The bottommost stripe can be fewer than 8 rows. */ for (stripe = 0; stripe < stripeCt; ++stripe) { const bit ** const stripeBits = &bits[stripe*rowsPerStripe]; unsigned int const stripeRows = MIN(rowsPerStripe, rows - stripe * rowsPerStripe); /* Number of rows in this stripe (8 for all but bottom stripe) */ unsigned int const endcol = lineWidth(stripeBits, cols, stripeRows); /* Column where right margin (contiguous white area at right end of stripe) begins. Zero if entire stripe is white. */ if (endcol > 0) printStripe(stripeBits, endcol, stripeRows, m); putchar('\n'); } putchar('\f'); /* Restore normal line spacing. */ printf("%c%c", esc, '@'); } int main(int argc, const char ** argv) { struct CmdlineInfo cmdline; FILE * ifP; const bit** bits; int rows, cols; pm_proginit(&argc, argv); parseCommandLine(argc, argv, &cmdline); ifP = pm_openr(cmdline.inputFileName); bits = (const bit **)pbm_readpbm(ifP, &cols, &rows); pm_close(ifP); convertToEpson(bits, cols, rows, cmdline.protocol, cmdline.dpi, cmdline.adjacence); pbm_freearray(bits, rows); return 0; }