Blame converter/pbm/pbmtoepson.c

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