/*====================================================================* - Copyright (C) 2001 Leptonica. All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials - provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *====================================================================*/ /*! * \file pnmio.c *
* * Stream interface * PIX *pixReadStreamPnm() * l_int32 readHeaderPnm() * l_int32 freadHeaderPnm() * l_int32 pixWriteStreamPnm() * l_int32 pixWriteStreamAsciiPnm() * l_int32 pixWriteStreamPam() * * Read/write to memory * PIX *pixReadMemPnm() * l_int32 readHeaderMemPnm() * l_int32 pixWriteMemPnm() * l_int32 pixWriteMemPam() * * Local helpers * static l_int32 pnmReadNextAsciiValue(); * static l_int32 pnmReadNextNumber(); * static l_int32 pnmReadNextString(); * static l_int32 pnmSkipCommentLines(); * * These are here by popular demand, with the help of Mattias * Kregert (mattias@kregert.se), who provided the first implementation. * * The pnm formats are exceedingly simple, because they have * no compression and no colormaps. They support images that * are 1 bpp; 2, 4, 8 and 16 bpp grayscale; and rgb. * * The original pnm formats ("ASCII") are included for completeness, * but their use is deprecated for all but tiny iconic images. * They are extremely wasteful of memory; for example, the P1 binary * ASCII format is 16 times as big as the packed uncompressed * format, because 2 characters are used to represent every bit * (pixel) in the image. Reading is slow because we check for extra * white space and EOL at every sample value. * * The packed pnm formats ("raw") give file sizes similar to * bmp files, which are uncompressed packed. However, bmp * are more flexible, because they can support colormaps. * * We don't differentiate between the different types ("pbm", * "pgm", "ppm") at the interface level, because this is really a * "distinction without a difference." You read a file, you get * the appropriate Pix. You write a file from a Pix, you get the * appropriate type of file. If there is a colormap on the Pix, * and the Pix is more than 1 bpp, you get either an 8 bpp pgm * or a 24 bpp RGB pnm, depending on whether the colormap colors * are gray or rgb, respectively. * * This follows the general policy that the I/O routines don't * make decisions about the content of the image -- you do that * with image processing before you write it out to file. * The I/O routines just try to make the closest connection * possible between the file and the Pix in memory. * * On systems like windows without fmemopen() and open_memstream(), * we write data to a temp file and read it back for operations * between pix and compressed-data, such as pixReadMemPnm() and * pixWriteMemPnm(). * * The P7 format is new. It introduced a header with multiple * lines containing distinct tags for the various fields. * See: http://netpbm.sourceforge.net/doc/pam.html * * WIDTH*/ #ifdef HAVE_CONFIG_H #include "config_auto.h" #endif /* HAVE_CONFIG_H */ #include; mandatory, exactly once * HEIGHT ; mandatory, exactly once * DEPTH ; mandatory, exactly once, * ; its meaning is equivalent to spp * MAXVAL ; mandatory, one of 1, 3, 15, 255 or 65535 * TUPLTYPE ; optional; BLACKANDWHITE, GRAYSCALE, RGB * ; and optional suffix _ALPHA, e.g. RGB_ALPHA * ENDHDR ; mandatory, last header line * * Reading BLACKANDWHITE_ALPHA and GRAYSCALE_ALPHA, which have a DEPTH * value of 2, is supported. The original image is converted to a Pix * with 32-bpp and alpha channel (spp == 4). * * Writing P7 format is currently selected for 32-bpp with alpha * channel, i.e. for Pix which have spp == 4, using pixWriteStreamPam(). * Jürgen Buchmüller provided the implementation for the P7 (pam) format. *
* Notes: * (1) This writes "raw" packed format only: * 1 bpp --> pbm (P4) * 2, 4, 8, 16 bpp, no colormap or grayscale colormap --> pgm (P5) * 2, 4, 8 bpp with color-valued colormap, or rgb --> rgb ppm (P6) * (2) 24 bpp rgb are not supported in leptonica, but this will * write them out as a packed array of bytes (3 to a pixel). **/ l_ok pixWriteStreamPnm(FILE *fp, PIX *pix) { l_uint8 val8; l_uint8 pel[4]; l_uint16 val16; l_int32 h, w, d, ds, i, j, wpls, bpl, filebpl, writeerror, maxval; l_uint32 *pword, *datas, *lines; PIX *pixs; PROCNAME("pixWriteStreamPnm"); if (!fp) return ERROR_INT("fp not defined", procName, 1); if (!pix) return ERROR_INT("pix not defined", procName, 1); pixGetDimensions(pix, &w, &h, &d); if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 24 && d != 32) return ERROR_INT("d not in {1,2,4,8,16,24,32}", procName, 1); if (d == 32 && pixGetSpp(pix) == 4) return pixWriteStreamPam(fp, pix); /* If a colormap exists, remove and convert to grayscale or rgb */ if (pixGetColormap(pix) != NULL) pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); else pixs = pixClone(pix); ds = pixGetDepth(pixs); datas = pixGetData(pixs); wpls = pixGetWpl(pixs); writeerror = 0; if (ds == 1) { /* binary */ fprintf(fp, "P4\n# Raw PBM file written by leptonica " "(www.leptonica.com)\n%d %d\n", w, h); bpl = (w + 7) / 8; for (i = 0; i < h; i++) { lines = datas + i * wpls; for (j = 0; j < bpl; j++) { val8 = GET_DATA_BYTE(lines, j); fwrite(&val8, 1, 1, fp); } } } else if (ds == 2 || ds == 4 || ds == 8 || ds == 16) { /* grayscale */ maxval = (1 << ds) - 1; fprintf(fp, "P5\n# Raw PGM file written by leptonica " "(www.leptonica.com)\n%d %d\n%d\n", w, h, maxval); if (ds != 16) { for (i = 0; i < h; i++) { lines = datas + i * wpls; for (j = 0; j < w; j++) { if (ds == 2) val8 = GET_DATA_DIBIT(lines, j); else if (ds == 4) val8 = GET_DATA_QBIT(lines, j); else /* ds == 8 */ val8 = GET_DATA_BYTE(lines, j); fwrite(&val8, 1, 1, fp); } } } else { /* ds == 16 */ for (i = 0; i < h; i++) { lines = datas + i * wpls; for (j = 0; j < w; j++) { val16 = GET_DATA_TWO_BYTES(lines, j); fwrite(&val16, 2, 1, fp); } } } } else { /* rgb color */ fprintf(fp, "P6\n# Raw PPM file written by leptonica " "(www.leptonica.com)\n%d %d\n255\n", w, h); if (d == 24) { /* packed, 3 bytes to a pixel */ filebpl = 3 * w; for (i = 0; i < h; i++) { /* write out each raster line */ lines = datas + i * wpls; if (fwrite(lines, 1, filebpl, fp) != filebpl) writeerror = 1; } } else { /* 32 bpp rgb */ for (i = 0; i < h; i++) { lines = datas + i * wpls; for (j = 0; j < wpls; j++) { pword = lines + j; pel[0] = GET_DATA_BYTE(pword, COLOR_RED); pel[1] = GET_DATA_BYTE(pword, COLOR_GREEN); pel[2] = GET_DATA_BYTE(pword, COLOR_BLUE); if (fwrite(pel, 1, 3, fp) != 3) writeerror = 1; } } } } pixDestroy(&pixs); if (writeerror) return ERROR_INT("image write fail", procName, 1); return 0; } /*! * \brief pixWriteStreamAsciiPnm() * * \param[in] fp file stream opened for write * \param[in] pix * \return 0 if OK; 1 on error * * Writes "ASCII" format only: * 1 bpp --> pbm P1 * 2, 4, 8, 16 bpp, no colormap or grayscale colormap --> pgm P2 * 2, 4, 8 bpp with color-valued colormap, or rgb --> rgb ppm P3 */ l_ok pixWriteStreamAsciiPnm(FILE *fp, PIX *pix) { char buffer[256]; l_uint8 cval[3]; l_int32 h, w, d, ds, i, j, k, maxval, count; l_uint32 val; PIX *pixs; PROCNAME("pixWriteStreamAsciiPnm"); if (!fp) return ERROR_INT("fp not defined", procName, 1); if (!pix) return ERROR_INT("pix not defined", procName, 1); pixGetDimensions(pix, &w, &h, &d); if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) return ERROR_INT("d not in {1,2,4,8,16,32}", procName, 1); /* If a colormap exists, remove and convert to grayscale or rgb */ if (pixGetColormap(pix) != NULL) pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); else pixs = pixClone(pix); ds = pixGetDepth(pixs); if (ds == 1) { /* binary */ fprintf(fp, "P1\n# Ascii PBM file written by leptonica " "(www.leptonica.com)\n%d %d\n", w, h); count = 0; for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { pixGetPixel(pixs, j, i, &val); if (val == 0) fputc('0', fp); else /* val == 1 */ fputc('1', fp); fputc(' ', fp); count += 2; if (count >= 70) { fputc('\n', fp); count = 0; } } } } else if (ds == 2 || ds == 4 || ds == 8 || ds == 16) { /* grayscale */ maxval = (1 << ds) - 1; fprintf(fp, "P2\n# Ascii PGM file written by leptonica " "(www.leptonica.com)\n%d %d\n%d\n", w, h, maxval); count = 0; for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { pixGetPixel(pixs, j, i, &val); if (ds == 2) { snprintf(buffer, sizeof(buffer), "%1d ", val); fwrite(buffer, 1, 2, fp); count += 2; } else if (ds == 4) { snprintf(buffer, sizeof(buffer), "%2d ", val); fwrite(buffer, 1, 3, fp); count += 3; } else if (ds == 8) { snprintf(buffer, sizeof(buffer), "%3d ", val); fwrite(buffer, 1, 4, fp); count += 4; } else { /* ds == 16 */ snprintf(buffer, sizeof(buffer), "%5d ", val); fwrite(buffer, 1, 6, fp); count += 6; } if (count >= 60) { fputc('\n', fp); count = 0; } } } } else { /* rgb color */ fprintf(fp, "P3\n# Ascii PPM file written by leptonica " "(www.leptonica.com)\n%d %d\n255\n", w, h); count = 0; for (i = 0; i < h; i++) { for (j = 0; j < w; j++) { pixGetPixel(pixs, j, i, &val); cval[0] = GET_DATA_BYTE(&val, COLOR_RED); cval[1] = GET_DATA_BYTE(&val, COLOR_GREEN); cval[2] = GET_DATA_BYTE(&val, COLOR_BLUE); for (k = 0; k < 3; k++) { snprintf(buffer, sizeof(buffer), "%3d ", cval[k]); fwrite(buffer, 1, 4, fp); count += 4; if (count >= 60) { fputc('\n', fp); count = 0; } } } } } pixDestroy(&pixs); return 0; } /*! * \brief pixWriteStreamPam() * * \param[in] fp file stream opened for write * \param[in] pix * \return 0 if OK; 1 on error * *
* Notes: * (1) This writes arbitrary PAM (P7) packed format. * (2) 24 bpp rgb are not supported in leptonica, but this will * write them out as a packed array of bytes (3 to a pixel). **/ l_ok pixWriteStreamPam(FILE *fp, PIX *pix) { l_uint8 val8; l_uint8 pel[8]; l_uint16 val16; l_int32 h, w, d, ds, i, j; l_int32 wpls, spps, filebpl, writeerror, maxval; l_uint32 *pword, *datas, *lines; PIX *pixs; PROCNAME("pixWriteStreamPam"); if (!fp) return ERROR_INT("fp not defined", procName, 1); if (!pix) return ERROR_INT("pix not defined", procName, 1); pixGetDimensions(pix, &w, &h, &d); if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 24 && d != 32) return ERROR_INT("d not in {1,2,4,8,16,24,32}", procName, 1); /* If a colormap exists, remove and convert to grayscale or rgb */ if (pixGetColormap(pix) != NULL) pixs = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); else pixs = pixClone(pix); ds = pixGetDepth(pixs); datas = pixGetData(pixs); wpls = pixGetWpl(pixs); spps = pixGetSpp(pixs); if (ds < 24) maxval = (1 << ds) - 1; else maxval = 255; writeerror = 0; fprintf(fp, "P7\n# Arbitrary PAM file written by leptonica " "(www.leptonica.com)\n"); fprintf(fp, "WIDTH %d\n", w); fprintf(fp, "HEIGHT %d\n", h); fprintf(fp, "DEPTH %d\n", spps); fprintf(fp, "MAXVAL %d\n", maxval); if (spps == 1 && ds == 1) fprintf(fp, "TUPLTYPE BLACKANDWHITE\n"); else if (spps == 1) fprintf(fp, "TUPLTYPE GRAYSCALE\n"); else if (spps == 3) fprintf(fp, "TUPLTYPE RGB\n"); else if (spps == 4) fprintf(fp, "TUPLTYPE RGB_ALPHA\n"); fprintf(fp, "ENDHDR\n"); switch (d) { case 1: for (i = 0; i < h; i++) { lines = datas + i * wpls; for (j = 0; j < w; j++) { val8 = GET_DATA_BIT(lines, j); val8 ^= 1; /* pam apparently uses white-is-1 photometry */ if (fwrite(&val8, 1, 1, fp) != 1) writeerror = 1; } } break; case 2: for (i = 0; i < h; i++) { lines = datas + i * wpls; for (j = 0; j < w; j++) { val8 = GET_DATA_DIBIT(lines, j); if (fwrite(&val8, 1, 1, fp) != 1) writeerror = 1; } } break; case 4: for (i = 0; i < h; i++) { lines = datas + i * wpls; for (j = 0; j < w; j++) { val8 = GET_DATA_QBIT(lines, j); if (fwrite(&val8, 1, 1, fp) != 1) writeerror = 1; } } break; case 8: for (i = 0; i < h; i++) { lines = datas + i * wpls; for (j = 0; j < w; j++) { val8 = GET_DATA_BYTE(lines, j); if (fwrite(&val8, 1, 1, fp) != 1) writeerror = 1; } } break; case 16: for (i = 0; i < h; i++) { lines = datas + i * wpls; for (j = 0; j < w; j++) { val16 = GET_DATA_TWO_BYTES(lines, j); if (fwrite(&val16, 2, 1, fp) != 1) writeerror = 1; } } break; case 24: filebpl = 3 * w; for (i = 0; i < h; i++) { lines = datas + i * wpls; if (fwrite(lines, 1, filebpl, fp) != filebpl) writeerror = 1; } break; case 32: switch (spps) { case 3: for (i = 0; i < h; i++) { lines = datas + i * wpls; for (j = 0; j < wpls; j++) { pword = lines + j; pel[0] = GET_DATA_BYTE(pword, COLOR_RED); pel[1] = GET_DATA_BYTE(pword, COLOR_GREEN); pel[2] = GET_DATA_BYTE(pword, COLOR_BLUE); if (fwrite(pel, 1, 3, fp) != 3) writeerror = 1; } } break; case 4: for (i = 0; i < h; i++) { lines = datas + i * wpls; for (j = 0; j < wpls; j++) { pword = lines + j; pel[0] = GET_DATA_BYTE(pword, COLOR_RED); pel[1] = GET_DATA_BYTE(pword, COLOR_GREEN); pel[2] = GET_DATA_BYTE(pword, COLOR_BLUE); pel[3] = GET_DATA_BYTE(pword, L_ALPHA_CHANNEL); if (fwrite(pel, 1, 4, fp) != 4) writeerror = 1; } } break; } break; } pixDestroy(&pixs); if (writeerror) return ERROR_INT("image write fail", procName, 1); return 0; } /*---------------------------------------------------------------------* * Read/write to memory * *---------------------------------------------------------------------*/ /*! * \brief pixReadMemPnm() * * \param[in] data const; pnm-encoded * \param[in] size of data * \return pix, or NULL on error * *
* Notes: * (1) The %size byte of %data must be a null character. **/ PIX * pixReadMemPnm(const l_uint8 *data, size_t size) { FILE *fp; PIX *pix; PROCNAME("pixReadMemPnm"); if (!data) return (PIX *)ERROR_PTR("data not defined", procName, NULL); if ((fp = fopenReadFromMemory(data, size)) == NULL) return (PIX *)ERROR_PTR("stream not opened", procName, NULL); pix = pixReadStreamPnm(fp); fclose(fp); if (!pix) L_ERROR("pix not read\n", procName); return pix; } /*! * \brief readHeaderMemPnm() * * \param[in] data const; pnm-encoded * \param[in] size of data * \param[out] pw [optional] * \param[out] ph [optional] * \param[out] pd [optional] * \param[out] ptype [optional] pnm type * \param[out] pbps [optional] bits/sample * \param[out] pspp [optional] samples/pixel * \return 0 if OK, 1 on error */ l_ok readHeaderMemPnm(const l_uint8 *data, size_t size, l_int32 *pw, l_int32 *ph, l_int32 *pd, l_int32 *ptype, l_int32 *pbps, l_int32 *pspp) { l_int32 ret; FILE *fp; PROCNAME("readHeaderMemPnm"); if (!data) return ERROR_INT("data not defined", procName, 1); if ((fp = fopenReadFromMemory(data, size)) == NULL) return ERROR_INT("stream not opened", procName, 1); ret = freadHeaderPnm(fp, pw, ph, pd, ptype, pbps, pspp); fclose(fp); if (ret) return ERROR_INT("header data read failed", procName, 1); return 0; } /*! * \brief pixWriteMemPnm() * * \param[out] pdata data of PNM image * \param[out] psize size of returned data * \param[in] pix * \return 0 if OK, 1 on error * *
* Notes: * (1) See pixWriteStreamPnm() for usage. This version writes to * memory instead of to a file stream. **/ l_ok pixWriteMemPnm(l_uint8 **pdata, size_t *psize, PIX *pix) { l_int32 ret; FILE *fp; PROCNAME("pixWriteMemPnm"); if (pdata) *pdata = NULL; if (psize) *psize = 0; if (!pdata) return ERROR_INT("&data not defined", procName, 1 ); if (!psize) return ERROR_INT("&size not defined", procName, 1 ); if (!pix) return ERROR_INT("&pix not defined", procName, 1 ); #if HAVE_FMEMOPEN if ((fp = open_memstream((char **)pdata, psize)) == NULL) return ERROR_INT("stream not opened", procName, 1); ret = pixWriteStreamPnm(fp, pix); #else L_INFO("work-around: writing to a temp file\n", procName); #ifdef _WIN32 if ((fp = fopenWriteWinTempfile()) == NULL) return ERROR_INT("tmpfile stream not opened", procName, 1); #else if ((fp = tmpfile()) == NULL) return ERROR_INT("tmpfile stream not opened", procName, 1); #endif /* _WIN32 */ ret = pixWriteStreamPnm(fp, pix); rewind(fp); *pdata = l_binaryReadStream(fp, psize); #endif /* HAVE_FMEMOPEN */ fclose(fp); return ret; } /*! * \brief pixWriteMemPam() * * \param[out] pdata data of PAM image * \param[out] psize size of returned data * \param[in] pix * \return 0 if OK, 1 on error * *
* Notes: * (1) See pixWriteStreamPnm() for usage. This version writes to * memory instead of to a file stream. **/ l_ok pixWriteMemPam(l_uint8 **pdata, size_t *psize, PIX *pix) { l_int32 ret; FILE *fp; PROCNAME("pixWriteMemPam"); if (pdata) *pdata = NULL; if (psize) *psize = 0; if (!pdata) return ERROR_INT("&data not defined", procName, 1 ); if (!psize) return ERROR_INT("&size not defined", procName, 1 ); if (!pix) return ERROR_INT("&pix not defined", procName, 1 ); #if HAVE_FMEMOPEN if ((fp = open_memstream((char **)pdata, psize)) == NULL) return ERROR_INT("stream not opened", procName, 1); ret = pixWriteStreamPam(fp, pix); #else L_INFO("work-around: writing to a temp file\n", procName); #ifdef _WIN32 if ((fp = fopenWriteWinTempfile()) == NULL) return ERROR_INT("tmpfile stream not opened", procName, 1); #else if ((fp = tmpfile()) == NULL) return ERROR_INT("tmpfile stream not opened", procName, 1); #endif /* _WIN32 */ ret = pixWriteStreamPam(fp, pix); rewind(fp); *pdata = l_binaryReadStream(fp, psize); #endif /* HAVE_FMEMOPEN */ fclose(fp); return ret; } /*--------------------------------------------------------------------* * Static helpers * *--------------------------------------------------------------------*/ /*! * \brief pnmReadNextAsciiValue() * * Return: 0 if OK, 1 on error or EOF. * * Notes: * (1) This reads the next sample value in ASCII from the file. */ static l_int32 pnmReadNextAsciiValue(FILE *fp, l_int32 *pval) { l_int32 c, ignore; PROCNAME("pnmReadNextAsciiValue"); if (!pval) return ERROR_INT("&val not defined", procName, 1); *pval = 0; if (!fp) return ERROR_INT("stream not open", procName, 1); do { /* skip whitespace */ if ((c = fgetc(fp)) == EOF) return 1; } while (c == ' ' || c == '\t' || c == '\n' || c == '\r'); fseek(fp, -1L, SEEK_CUR); /* back up one byte */ ignore = fscanf(fp, "%d", pval); return 0; } /*! * \brief pnmReadNextNumber() * * \param[in] fp file stream * \param[out] pval value as an integer * \return 0 if OK, 1 on error or EOF. * *
* Notes: * (1) This reads the next set of numeric chars, returning * the value and swallowing the trailing whitespace character. * This is needed to read the maxval in the header, which * precedes the binary data. **/ static l_int32 pnmReadNextNumber(FILE *fp, l_int32 *pval) { char buf[8]; l_int32 i, c, foundws; PROCNAME("pnmReadNextNumber"); if (!pval) return ERROR_INT("&val not defined", procName, 1); *pval = 0; if (!fp) return ERROR_INT("stream not open", procName, 1); /* The ASCII characters for the number are followed by exactly * one whitespace character. */ foundws = FALSE; for (i = 0; i < 8; i++) buf[i] = '\0'; for (i = 0; i < 8; i++) { if ((c = fgetc(fp)) == EOF) return ERROR_INT("end of file reached", procName, 1); if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { foundws = TRUE; buf[i] = '\n'; break; } if (!isdigit(c)) return ERROR_INT("char read is not a digit", procName, 1); buf[i] = c; } if (!foundws) return ERROR_INT("no whitespace found", procName, 1); if (sscanf(buf, "%d", pval) != 1) return ERROR_INT("invalid read", procName, 1); return 0; } /*! * \brief pnmReadNextString() * * \param[in] fp file stream * \param[out] buff pointer to the string buffer * \param[in] size max. number of charactes in buffer * \return 0 if OK, 1 on error or EOF. * *
* Notes: * (1) This reads the next set of alphanumeric chars, * returning the string and swallowing the trailing * whitespace characters. * This is needed to read header lines, which precede * the P7 format binary data. **/ static l_int32 pnmReadNextString(FILE *fp, char *buff, l_int32 size) { l_int32 i, c; PROCNAME("pnmReadNextString"); if (!buff) return ERROR_INT("buff not defined", procName, 1); *buff = '\0'; if (!fp) return ERROR_INT("stream not open", procName, 1); if (size <= 0) return ERROR_INT("size is too small", procName, 1); do { /* skip whitespace */ if ((c = fgetc(fp)) == EOF) return ERROR_INT("end of file reached", procName, 1); } while (c == ' ' || c == '\t' || c == '\n' || c == '\r'); /* Comment lines are allowed to appear * anywhere in the header lines */ if (c == '#') { do { /* each line starting with '#' */ do { /* this entire line */ if ((c = fgetc(fp)) == EOF) return ERROR_INT("end of file reached", procName, 1); } while (c != '\n'); if ((c = fgetc(fp)) == EOF) return ERROR_INT("end of file reached", procName, 1); } while (c == '#'); } /* The next string ends when there is * a whitespace character following. */ for (i = 0; i < size - 1; i++) { if (c == ' ' || c == '\t' || c == '\n' || c == '\r') break; buff[i] = c; if ((c = fgetc(fp)) == EOF) return ERROR_INT("end of file reached", procName, 1); } buff[i] = '\0'; /* Back up one byte */ fseek(fp, -1L, SEEK_CUR); if (i >= size - 1) return ERROR_INT("buff size too small", procName, 1); /* Skip over trailing spaces and tabs */ for (;;) { if ((c = fgetc(fp)) == EOF) return ERROR_INT("end of file reached", procName, 1); if (c != ' ' && c != '\t') break; } /* Back up one byte */ fseek(fp, -1L, SEEK_CUR); return 0; } /*! * \brief pnmSkipCommentLines() * * Return: 0 if OK, 1 on error or EOF * * Notes: * (1) Comment lines begin with '#' * (2) Usage: caller should check return value for EOF */ static l_int32 pnmSkipCommentLines(FILE *fp) { l_int32 c; PROCNAME("pnmSkipCommentLines"); if (!fp) return ERROR_INT("stream not open", procName, 1); if ((c = fgetc(fp)) == EOF) return 1; if (c == '#') { do { /* each line starting with '#' */ do { /* this entire line */ if ((c = fgetc(fp)) == EOF) return 1; } while (c != '\n'); if ((c = fgetc(fp)) == EOF) return 1; } while (c == '#'); } /* Back up one byte */ fseek(fp, -1L, SEEK_CUR); return 0; } /* --------------------------------------------*/ #endif /* USE_PNMIO */ /* --------------------------------------------*/