diff options
author | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2015-07-14 03:14:09 +0000 |
---|---|---|
committer | giraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8> | 2015-07-14 03:14:09 +0000 |
commit | 7055a6924db2ca6b23a395da3d2c91dc2e858669 (patch) | |
tree | 946292d1b77bddfd0e5fa0bbc94b50eb6b602da3 | |
parent | 9c2837581f378610049e64f33c58a2df8c381713 (diff) | |
download | netpbm-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
-rw-r--r-- | converter/pbm/escp2topbm.c | 384 | ||||
-rw-r--r-- | doc/HISTORY | 3 |
2 files changed, 293 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; } + + + diff --git a/doc/HISTORY b/doc/HISTORY index dcd0ed16..6982ce59 100644 --- a/doc/HISTORY +++ b/doc/HISTORY @@ -19,6 +19,9 @@ not yet BJH Release 10.72.00 pbmtoescp2: Pad output horizontally to a multiple of 8 columns and vertically to a whole stripe to prevent image loss. + escp2topbm: Fix -plain. Always broken (escp2topbm was new in + Netpbm 10.18 (September 2003)). + pnmpad: Add -mheight, -mwidth. ppmtoilbm: Fix failure with -hamforce and -nocompression. |