/* escp2topbm.c - read an Epson ESC/P2 printer file and
**                 create a pbm file from the raster data,
**                 ignoring all other data.
**                 Can be regarded as a simple raster printer emulator
**                 with a RLE run length decoder.
**                 This program was made primarily for the test of pbmtoescp2
**
** Copyright (C) 2003 by Ulrich Walcher (u.walcher@gmx.de)
**                       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.
*/

#include <string.h>

#include "pbm.h"
#include "mallocvar.h"

/* RLE decoder */
static unsigned int 
dec_epson_rle(unsigned        const int k, 
              unsigned        const char * in, 
              unsigned char * const out) {

    unsigned int i;
    unsigned int pos;
    unsigned int dpos;

    pos = 0;  /* initial value */
    dpos = 0; /* initial value */

    while (dpos < k) {
        if (in[pos] < 128) {
            for (i = 0; i < in[pos] + 1; ++i)
                out[dpos+i] = in[pos + i + 1];     
            /* copy through */
            pos += i + 1;
        } else {
            for (i = 0; i < 257 - in[pos]; ++i)
                out[dpos + i] = in[pos + 1];  
            /* inflate this run */
            pos += 2;
        }
        dpos += i;
    }
    if(dpos > k)
      pm_error("Corrupt compressed block"); 
    return pos;        /* return number of treated input bytes */
}



int
main(int    argc,
     char * argv[]) {

    unsigned int const size = 4096; /* arbitrary value */

    FILE *ifP;
    unsigned int i, len, pos, opos, width, height;
    unsigned char *input, *output;
    const char * fileName;

    pbm_init(&argc, argv);

    MALLOCARRAY(input, size);
    MALLOCARRAY(output, size);
    
    if (input == NULL || output == NULL)
        pm_error("Cannot allocate memory");

    if (argc-1 > 1)
        pm_error("Too many arguments (%u).  Only argument is filename.",
                 argc-1);

    if (argc == 2)
        fileName = argv[1];
    else
        fileName = "-";

    ifP = pm_openr(fileName);

    /* read the whole file */
    len = 0;  /* initial value */
    for (i = 0; !feof(ifP); ++i) {
        size_t bytesRead;
        REALLOCARRAY(input, (i+1) * size);
        if (input == NULL)
            pm_error("Cannot allocate memory");
        bytesRead = fread(input + i * size, 1, size, ifP);
        len += bytesRead;
    }

    /* filter out raster data */
    height = 0;  /* initial value */
    width  = 0;  /* initial value */
    pos = 0;     /* initial value */
    opos = 0;    /* initial value */

    while (pos < len) {
        /* only ESC sequences are regarded  */
        if (input[pos] == '\x1b' && input[pos+1] == '.') {
            unsigned int const k =
                input[pos+5] * ((input[pos+7] * 256 + input[pos+6] + 7) / 8);
            unsigned int const margin = 256;
            if(input[pos+5] == 0)
                pm_error("Abnormal height value in escape sequence");
            height += input[pos+5];
            if(width == 0) /* initialize */
                width = input[pos+7] * 256 + input[pos+6];
            else if(width != input[pos+7] * 256 + input[pos+6])
                pm_error("Abnormal width value in escape sequence");

            REALLOCARRAY(output, opos + k + margin);
            if (output == NULL)
                pm_error("Cannot allocate memory");

            switch (input[pos+2]) {
            case 0:
                /* copy the data block */
                memcpy(output + opos, input + pos + 8, k);        
                pos += k + 8;
                opos += k;
                break;
            case 1: {
                /* inflate the data block */
                unsigned int l;
                l = dec_epson_rle(k,input+pos+8,output+opos);  
                pos += l + 8;
                opos += k;
            }
                break;
            default:
                pm_error("unknown compression mode");
                break;
            }
        }
        else
            ++pos;      /* skip bytes outside the ESCX sequence */
    }

    pbm_writepbminit(stdout, width, height, 0);
    fwrite(output, opos, 1, stdout);
    free(input); free(output);
    fclose(stdout); fclose(ifP);

    return 0;
}
