about summary refs log tree commit diff
path: root/converter/pbm
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2015-07-14 03:14:09 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2015-07-14 03:14:09 +0000
commit7055a6924db2ca6b23a395da3d2c91dc2e858669 (patch)
tree946292d1b77bddfd0e5fa0bbc94b50eb6b602da3 /converter/pbm
parent9c2837581f378610049e64f33c58a2df8c381713 (diff)
downloadnetpbm-mirror-7055a6924db2ca6b23a395da3d2c91dc2e858669.tar.gz
netpbm-mirror-7055a6924db2ca6b23a395da3d2c91dc2e858669.tar.xz
netpbm-mirror-7055a6924db2ca6b23a395da3d2c91dc2e858669.zip
Cleanup and make -plain work
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@2606 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'converter/pbm')
-rw-r--r--converter/pbm/escp2topbm.c384
1 files changed, 290 insertions, 94 deletions
diff --git a/converter/pbm/escp2topbm.c b/converter/pbm/escp2topbm.c
index 28296da9..632e6345 100644
--- a/converter/pbm/escp2topbm.c
+++ b/converter/pbm/escp2topbm.c
@@ -14,65 +14,266 @@
 ** copyright notice and this permission notice appear in supporting
 ** documentation.  This software is provided "as is" without express or
 ** implied warranty.
+
+** Major changes were made in July 2015 by Akira Urushibata.
+** Most notably the output functions were rewritten.
+** The -plain option is honored as in other programs.
+
+*   Implementation note (July 2015)
+*
+*   The input file does not have a global header.  Image data is divided
+*   into stripes (or data blocks).   Each stripe has a header with
+*   local values for width, height, horizontal/vertical resolution
+*   and compression mode.
+*
+*   We calculate the global height by adding up the local (stripe)
+*   height values, which may vary.
+*
+*   The width value in the first stripe is used throughout; if any
+*   subsequent stripe reports a different value we abort.
+*
+*   We ignore the resolution fields.  Changes in compression mode
+*   are tolerated; they pose no problem.
+*
+*   The official manual says resolution changes within an image are
+*   not allowed.  It does not mention whether changes in stripe height or
+*   width values should be allowed.
+*
+*   A different implementation approach would be to write temporary
+*   PBM files for each stripe and concatenate them at the final stage
+*   with a system call to "pnmcat -tb".  This method has the advantage
+*   of being capable of handling variations in width.
 */
 
-#include <string.h>
 
-#include "pbm.h"
+#include <stdbool.h>
+
 #include "mallocvar.h"
+#include "pbm.h"
+
+#define ESC 033
+
+
+
+static int
+huntEsc(FILE * const ifP) {
+/*-----------------------------------------------------------------------------
+  Hunt for valid start of image stripe in input.
+
+  Return values:
+    ESC: start of image stripe (data block)
+    EOF: end of file
+    0: any other char or sequence
+-----------------------------------------------------------------------------*/
+    int const ch1 = getc(ifP);
+
+    switch (ch1) {
+    case EOF: return EOF;
+    case ESC: {
+        int const ch2 = getc(ifP);
+
+        switch (ch2) {
+        case EOF: return EOF;
+        case '.': return ESC;
+        default:  return 0;
+        }
+    } break;
+    default: return 0;
+    }
+}
+
+
+
+static unsigned char
+readChar(FILE * const ifP) {
+
+    int const ch = getc(ifP);
+
+    if (ch == EOF)
+        pm_error("EOF encountered while reading image data.");
+
+    return (unsigned char) ch;
+}
+
+
+
+static void       
+readStripeHeader(unsigned int * const widthThisStripeP,
+                 unsigned int * const rowsThisStripeP,
+                 unsigned int * const compressionP,
+                 FILE *         const ifP) {
+
+    unsigned char stripeHeader[6];
+    unsigned int widthThisStripe, rowsThisStripe;
+    unsigned int compression;
+
+    if (fread(stripeHeader, sizeof(stripeHeader), 1, ifP) != 1)
+        pm_error("Error reading image data.");
+
+    compression     = stripeHeader[0];
+    /* verticalResolution   = stripeHeader[1]; */
+    /* horizontalResolution = stripeHeader[2]; */
+    rowsThisStripe  = stripeHeader[3];  
+    widthThisStripe = stripeHeader[5] * 256 + stripeHeader[4];
+
+    if (widthThisStripe == 0 || rowsThisStripe == 0)
+        pm_error("Error: Abnormal value in data block header:  "
+                 "Says stripe has zero width or height");
+
+    if (compression != 0 && compression != 1)
+        pm_error("Error: Unknown compression mode %u", compression);
+
+    *widthThisStripeP = widthThisStripe;
+    *rowsThisStripeP  = rowsThisStripe;
+    *compressionP     = compression;
+}
+
+
 
 /* RLE decoder */
-static unsigned int 
-dec_epson_rle(unsigned        const int k, 
-              unsigned        const char * in, 
-              unsigned char * const out) {
+static void
+decEpsonRLE(unsigned int    const blockSize, 
+            unsigned char * const outBuffer,
+            FILE *          const ifP) {
 
-    unsigned int i;
-    unsigned int pos;
     unsigned int dpos;
 
-    pos = 0;  /* initial value */
-    dpos = 0; /* initial value */
+    for (dpos = 0; dpos < blockSize; ) {
+        unsigned char const flag = readChar(ifP);
 
-    while (dpos < k) {
-        if (in[pos] < 128) {
-            for (i = 0; i < in[pos] + 1; ++i)
-                out[dpos+i] = in[pos + i + 1];     
+        if (flag < 128) {
             /* copy through */
-            pos += i + 1;
+
+            unsigned int const nonrunLength = flag + 1;
+
+            unsigned int i;
+
+            for (i = 0; i < nonrunLength; ++i)
+                outBuffer[dpos+i] = readChar(ifP);
+
+            dpos += nonrunLength;
+        } else if (flag == 128) {
+            pm_message("Code 128 detected in compressed input data: ignored");
         } else {
-            for (i = 0; i < 257 - in[pos]; ++i)
-                out[dpos + i] = in[pos + 1];  
             /* inflate this run */
-            pos += 2;
+
+            unsigned int const runLength = 257 - flag;
+            unsigned char const repeatChar = readChar( ifP );
+
+            unsigned int i;
+
+            for (i = 0; i < runLength; ++i)
+                outBuffer[dpos + i] = repeatChar;  
+            dpos += runLength;
         }
-        dpos += i;
     }
-    if(dpos > k)
-      pm_error("Corrupt compressed block"); 
-    return pos;        /* return number of treated input bytes */
+    if (dpos != blockSize)
+      pm_error("Corruption detected in compressed input data");
 }
 
 
 
-int
-main(int    argc,
-     char * argv[]) {
+static void
+processStripeRaster(unsigned char ** const bitarray,
+                    unsigned int     const rowsThisStripe,
+                    unsigned int     const width,
+                    unsigned int     const compression,
+                    FILE *           const ifP,
+                    unsigned int *   const rowIdxP) {
+         
+    unsigned int const initialRowIdx = *rowIdxP;
+    unsigned int const widthInBytes = pbm_packed_bytes(width);
+    unsigned int const blockSize = rowsThisStripe * widthInBytes;
+    unsigned int const margin = compression==1 ? 256 : 0;
+
+    unsigned char * imageBuffer;
+    unsigned int i;
+    unsigned int rowIdx;
 
-    unsigned int const size = 4096; /* arbitrary value */
+    /* We allocate a new buffer each time this function is called.  Add some
+       margin to the buffer for compressed mode to cope with overruns caused
+       by corrupt input data.
+    */
+
+    MALLOCARRAY(imageBuffer, blockSize + margin);
+
+    if (imageBuffer == NULL)
+        pm_error("Failed to allocate buffer for a block of size %u",
+                 blockSize);
+
+    if (compression == 0) {
+        if (fread(imageBuffer, blockSize, 1, ifP) != 1)
+            pm_error("Error reading image data");
+    } else /* compression == 1 */
+        decEpsonRLE(blockSize, imageBuffer, ifP);
+
+    /* Hand over image data to output by pointer assignment */
+    for (i = 0, rowIdx = initialRowIdx; i < rowsThisStripe; ++i)
+        bitarray[rowIdx++] = &imageBuffer[i * widthInBytes];
+
+    *rowIdxP = rowIdx;
+}
 
-    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");
+static void
+expandBitarray(unsigned char *** const bitarrayP,
+               unsigned int   *  const bitarraySizeP) {
+
+    unsigned int const heightIncrement = 5120;
+    unsigned int const heightMax = 5120 * 200;
+        /* 5120 rows is sufficient for US legal at 360 DPI */
+
+    *bitarraySizeP += heightIncrement;
+    if (*bitarraySizeP > heightMax)
+        pm_error("Image too tall");
+    else
+        REALLOCARRAY_NOFAIL(*bitarrayP, *bitarraySizeP); 
+}
+
+
+
+static void
+writePbmImage(unsigned char ** const bitarray,
+              unsigned int     const width,
+              unsigned int     const height) {
+
+    unsigned int row;
+
+    if (height == 0)
+        pm_error("No image");
+
+    pbm_writepbminit(stdout, width, height, 0);
+ 
+    for (row = 0; row < height; ++row) {
+        pbm_cleanrowend_packed(bitarray[row], width);
+        pbm_writepbmrow_packed(stdout, bitarray[row], width, 0);
+    }
+}
+
+
+
+int
+main(int          argc,
+     const char * argv[]) {
+
+    FILE * ifP;
+    unsigned int width;
+        /* Width of the image, or zero to mean width is not yet known.
+           (We get the width from the first stripe in the input; until
+           we've seen that stripe, we don't know the width)
+        */
+    unsigned int height;
+        /* Height of the image as seen so far.  (We process a stripe at a
+           time, increasing this value as we go).
+        */
+    unsigned int rowIdx;
+    unsigned char ** bitarray;
+    unsigned int bitarraySize;
+    const char * fileName;
+    bool eof;
+
+    pm_proginit(&argc, argv);
 
     if (argc-1 > 1)
         pm_error("Too many arguments (%u).  Only argument is filename.",
@@ -85,69 +286,64 @@ main(int    argc,
 
     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;
-    }
+    /* Initialize bitarray */
+    bitarray = NULL;  bitarraySize = 0;
+    expandBitarray(&bitarray, &bitarraySize);
 
-    /* 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;
+    for (eof = false, width = 0, height = 0, rowIdx = 0; !eof; ) {
+        int const r = huntEsc(ifP);
+
+        if (r == EOF)
+            eof = true;
+        else {
+            if (r == ESC) {
+                unsigned int compression;
+                unsigned int rowsThisStripe;
+                unsigned int widthThisStripe;
+            
+                readStripeHeader(&widthThisStripe, &rowsThisStripe,
+                                 &compression, ifP);
+
+                if (rowsThisStripe == 0)
+                    pm_error("Abnormal data block height value: 0");
+                else if (rowsThisStripe != 24 && rowsThisStripe != 8 &&
+                         rowsThisStripe != 1) {
+                    /* The official Epson manual says valid values are 1, 8,
+                       24 but we just print a warning message and continue if
+                       other values are detected.
+                    */ 
+                    pm_message("Abnormal data block height value: %u "
+                               "(ignoring)",
+                               rowsThisStripe);
+                }
+                if (width == 0) {
+                    /* Get width from 1st stripe header */
+                    width = widthThisStripe;
+                } else if (width != widthThisStripe) {
+                    /* width change not allowed */
+                    pm_error("Error: Width changed in middle of image "
+                             "from %u to %u",
+                             width, widthThisStripe);
+                }
+                height += rowsThisStripe;
+                if (height > bitarraySize)
+                    expandBitarray(&bitarray, &bitarraySize);
+
+                processStripeRaster(bitarray, rowsThisStripe, width,
+                                    compression, ifP, &rowIdx);
+            } else {
+                /* r != ESC; do nothing */
             }
         }
-        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);
+    writePbmImage(bitarray, width, height);
+
+    fclose(stdout);
+    fclose(ifP);
 
     return 0;
 }
+
+
+