fukasawa e60969
/*---------------------------------------------------------------------------
fukasawa e60969
fukasawa e60969
   wpng - simple PNG-writing program                                 wpng.c
fukasawa e60969
fukasawa e60969
   This program converts certain NetPBM binary files (grayscale and RGB,
fukasawa e60969
   maxval = 255) to PNG.  Non-interlaced PNGs are written progressively;
fukasawa e60969
   interlaced PNGs are read and written in one memory-intensive blast.
fukasawa e60969
fukasawa e60969
   Thanks to Jean-loup Gailly for providing the necessary trick to read
fukasawa e60969
   interactive text from the keyboard while stdin is redirected.  Thanks
fukasawa e60969
   to Cosmin Truta for Cygwin fixes.
fukasawa e60969
fukasawa e60969
   NOTE:  includes provisional support for PNM type "8" (portable alphamap)
fukasawa e60969
          images, presumed to be a 32-bit interleaved RGBA format; no pro-
fukasawa e60969
          vision for possible interleaved grayscale+alpha (16-bit) format.
fukasawa e60969
          THIS IS UNLIKELY TO BECOME AN OFFICIAL NETPBM ALPHA FORMAT!
fukasawa e60969
fukasawa e60969
   to do:
fukasawa e60969
    - delete output file if quit before calling any writepng routines
fukasawa e60969
    - process backspace with -text option under DOS/Win? (currently get ^H)
fukasawa e60969
fukasawa e60969
  ---------------------------------------------------------------------------
fukasawa e60969
fukasawa e60969
   Changelog:
fukasawa e60969
    - 1.01:  initial public release
fukasawa e60969
    - 1.02:  modified to allow abbreviated options
fukasawa e60969
    - 1.03:  removed extraneous character from usage screen; fixed bug in
fukasawa e60969
              command-line parsing
fukasawa e60969
    - 1.04:  fixed DOS/OS2/Win32 detection, including partial Cygwin fix
fukasawa e60969
              (see http://home.att.net/~perlspinr/diffs/GregBook_cygwin.diff)
fukasawa e60969
    - 2.00:  dual-licensed (added GNU GPL)
fukasawa e60969
fukasawa e60969
        [REPORTED BUG (win32 only):  "contrib/gregbook/wpng.c - cmd line
fukasawa e60969
         dose not work!  In order to do something useful I needed to redirect
fukasawa e60969
         both input and output, with cygwin and with bcc32 as well.  Under
fukasawa e60969
         Linux, the same wpng appears to work fine.  I don't know what is
fukasawa e60969
         the problem."]
fukasawa e60969
fukasawa e60969
  ---------------------------------------------------------------------------
fukasawa e60969
fukasawa e60969
      Copyright (c) 1998-2007 Greg Roelofs.  All rights reserved.
fukasawa e60969
fukasawa e60969
      This software is provided "as is," without warranty of any kind,
fukasawa e60969
      express or implied.  In no event shall the author or contributors
fukasawa e60969
      be held liable for any damages arising in any way from the use of
fukasawa e60969
      this software.
fukasawa e60969
fukasawa e60969
      The contents of this file are DUAL-LICENSED.  You may modify and/or
fukasawa e60969
      redistribute this software according to the terms of one of the
fukasawa e60969
      following two licenses (at your option):
fukasawa e60969
fukasawa e60969
fukasawa e60969
      LICENSE 1 ("BSD-like with advertising clause"):
fukasawa e60969
fukasawa e60969
      Permission is granted to anyone to use this software for any purpose,
fukasawa e60969
      including commercial applications, and to alter it and redistribute
fukasawa e60969
      it freely, subject to the following restrictions:
fukasawa e60969
fukasawa e60969
      1. Redistributions of source code must retain the above copyright
fukasawa e60969
         notice, disclaimer, and this list of conditions.
fukasawa e60969
      2. Redistributions in binary form must reproduce the above copyright
fukasawa e60969
         notice, disclaimer, and this list of conditions in the documenta-
fukasawa e60969
         tion and/or other materials provided with the distribution.
fukasawa e60969
      3. All advertising materials mentioning features or use of this
fukasawa e60969
         software must display the following acknowledgment:
fukasawa e60969
fukasawa e60969
            This product includes software developed by Greg Roelofs
fukasawa e60969
            and contributors for the book, "PNG: The Definitive Guide,"
fukasawa e60969
            published by O'Reilly and Associates.
fukasawa e60969
fukasawa e60969
fukasawa e60969
      LICENSE 2 (GNU GPL v2 or later):
fukasawa e60969
fukasawa e60969
      This program is free software; you can redistribute it and/or modify
fukasawa e60969
      it under the terms of the GNU General Public License as published by
fukasawa e60969
      the Free Software Foundation; either version 2 of the License, or
fukasawa e60969
      (at your option) any later version.
fukasawa e60969
fukasawa e60969
      This program is distributed in the hope that it will be useful,
fukasawa e60969
      but WITHOUT ANY WARRANTY; without even the implied warranty of
fukasawa e60969
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
fukasawa e60969
      GNU General Public License for more details.
fukasawa e60969
fukasawa e60969
      You should have received a copy of the GNU General Public License
fukasawa e60969
      along with this program; if not, write to the Free Software Foundation,
fukasawa e60969
      Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
fukasawa e60969
fukasawa e60969
  ---------------------------------------------------------------------------*/
fukasawa e60969
fukasawa e60969
#define PROGNAME  "wpng"
fukasawa e60969
#define VERSION   "2.00 of 2 June 2007"
fukasawa e60969
#define APPNAME   "Simple PGM/PPM/PAM to PNG Converter"
fukasawa e60969
fukasawa e60969
#if defined(__MSDOS__) || defined(__OS2__)
fukasawa e60969
#  define DOS_OS2_W32
fukasawa e60969
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
fukasawa e60969
#  ifndef __GNUC__   /* treat Win32 native ports of gcc as Unix environments */
fukasawa e60969
#    define DOS_OS2_W32
fukasawa e60969
#  endif
fukasawa e60969
#endif
fukasawa e60969
fukasawa e60969
#include <stdio.h></stdio.h>
fukasawa e60969
#include <stdlib.h></stdlib.h>
fukasawa e60969
#include <string.h></string.h>
fukasawa e60969
#include <setjmp.h>     /* for jmpbuf declaration in writepng.h */</setjmp.h>
fukasawa e60969
#include <time.h></time.h>
fukasawa e60969
fukasawa e60969
#ifdef DOS_OS2_W32
fukasawa e60969
#  include <io.h>       /* for isatty(), setmode() prototypes */</io.h>
fukasawa e60969
#  include <fcntl.h>    /* O_BINARY for fdopen() without text translation */</fcntl.h>
fukasawa e60969
#  ifdef __EMX__
fukasawa e60969
#    ifndef getch
fukasawa e60969
#      define getch() _read_kbd(0, 1, 0)    /* need getche() */
fukasawa e60969
#    endif
fukasawa e60969
#  else /* !__EMX__ */
fukasawa e60969
#    ifdef __GO32__
fukasawa e60969
#      include <pc.h></pc.h>
fukasawa e60969
#      define getch() getkey()  /* GRR:  need getche() */
fukasawa e60969
#    else
fukasawa e60969
#      include <conio.h>        /* for getche() console input */</conio.h>
fukasawa e60969
#    endif
fukasawa e60969
#  endif /* ?__EMX__ */
fukasawa e60969
#  define FGETS(buf,len,stream)  dos_kbd_gets(buf,len)
fukasawa e60969
#else
fukasawa e60969
#  include <unistd.h>           /* for isatty() prototype */</unistd.h>
fukasawa e60969
#  define FGETS fgets
fukasawa e60969
#endif
fukasawa e60969
fukasawa e60969
/* #define DEBUG  :  this enables the Trace() macros */
fukasawa e60969
fukasawa e60969
/* #define FORBID_LATIN1_CTRL  :  this requires the user to re-enter any
fukasawa e60969
   text that includes control characters discouraged by the PNG spec; text
fukasawa e60969
   that includes an escape character (27) must be re-entered regardless */
fukasawa e60969
fukasawa e60969
#include "writepng.h"   /* typedefs, common macros, writepng prototypes */
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
/* local prototypes */
fukasawa e60969
fukasawa e60969
static int  wpng_isvalid_latin1(uch *p, int len);
fukasawa e60969
static void wpng_cleanup(void);
fukasawa e60969
fukasawa e60969
#ifdef DOS_OS2_W32
fukasawa e60969
   static char *dos_kbd_gets(char *buf, int len);
fukasawa e60969
#endif
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
static mainprog_info wpng_info;   /* lone global */
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
int main(int argc, char **argv)
fukasawa e60969
{
fukasawa e60969
#ifndef DOS_OS2_W32
fukasawa e60969
    FILE *keybd;
fukasawa e60969
#endif
fukasawa e60969
#ifdef sgi
fukasawa e60969
    FILE *tmpfile;      /* or we could just use keybd, since no overlap */
fukasawa e60969
    char tmpline[80];
fukasawa e60969
#endif
fukasawa e60969
    char *inname = NULL, outname[256];
fukasawa e60969
    char *p, pnmchar, pnmline[256];
fukasawa e60969
    char *bgstr, *textbuf = NULL;
fukasawa e60969
    ulg rowbytes;
fukasawa e60969
    int rc, len = 0;
fukasawa e60969
    int error = 0;
fukasawa e60969
    int text = FALSE;
fukasawa e60969
    int maxval;
fukasawa e60969
    double LUT_exponent;                /* just the lookup table */
fukasawa e60969
    double CRT_exponent = 2.2;          /* just the monitor */
fukasawa e60969
    double default_display_exponent;    /* whole display system */
fukasawa e60969
    double default_gamma = 0.0;
fukasawa e60969
fukasawa e60969
fukasawa e60969
    wpng_info.infile = NULL;
fukasawa e60969
    wpng_info.outfile = NULL;
fukasawa e60969
    wpng_info.image_data = NULL;
fukasawa e60969
    wpng_info.row_pointers = NULL;
fukasawa e60969
    wpng_info.filter = FALSE;
fukasawa e60969
    wpng_info.interlaced = FALSE;
fukasawa e60969
    wpng_info.have_bg = FALSE;
fukasawa e60969
    wpng_info.have_time = FALSE;
fukasawa e60969
    wpng_info.have_text = 0;
fukasawa e60969
    wpng_info.gamma = 0.0;
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* First get the default value for our display-system exponent, i.e.,
fukasawa e60969
     * the product of the CRT exponent and the exponent corresponding to
fukasawa e60969
     * the frame-buffer's lookup table (LUT), if any.  If the PNM image
fukasawa e60969
     * looks correct on the user's display system, its file gamma is the
fukasawa e60969
     * inverse of this value.  (Note that this is not an exhaustive list
fukasawa e60969
     * of LUT values--e.g., OpenStep has a lot of weird ones--but it should
fukasawa e60969
     * cover 99% of the current possibilities.  This section must ensure
fukasawa e60969
     * that default_display_exponent is positive.) */
fukasawa e60969
fukasawa e60969
#if defined(NeXT)
fukasawa e60969
    /* third-party utilities can modify the default LUT exponent */
fukasawa e60969
    LUT_exponent = 1.0 / 2.2;
fukasawa e60969
    /*
fukasawa e60969
    if (some_next_function_that_returns_gamma(&next_gamma))
fukasawa e60969
        LUT_exponent = 1.0 / next_gamma;
fukasawa e60969
     */
fukasawa e60969
#elif defined(sgi)
fukasawa e60969
    LUT_exponent = 1.0 / 1.7;
fukasawa e60969
    /* there doesn't seem to be any documented function to
fukasawa e60969
     * get the "gamma" value, so we do it the hard way */
fukasawa e60969
    tmpfile = fopen("/etc/config/system.glGammaVal", "r");
fukasawa e60969
    if (tmpfile) {
fukasawa e60969
        double sgi_gamma;
fukasawa e60969
fukasawa e60969
        fgets(tmpline, 80, tmpfile);
fukasawa e60969
        fclose(tmpfile);
fukasawa e60969
        sgi_gamma = atof(tmpline);
fukasawa e60969
        if (sgi_gamma > 0.0)
fukasawa e60969
            LUT_exponent = 1.0 / sgi_gamma;
fukasawa e60969
    }
fukasawa e60969
#elif defined(Macintosh)
fukasawa e60969
    LUT_exponent = 1.8 / 2.61;
fukasawa e60969
    /*
fukasawa e60969
    if (some_mac_function_that_returns_gamma(&mac_gamma))
fukasawa e60969
        LUT_exponent = mac_gamma / 2.61;
fukasawa e60969
     */
fukasawa e60969
#else
fukasawa e60969
    LUT_exponent = 1.0;   /* assume no LUT:  most PCs */
fukasawa e60969
#endif
fukasawa e60969
fukasawa e60969
    /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
fukasawa e60969
    default_display_exponent = LUT_exponent * CRT_exponent;
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* If the user has set the SCREEN_GAMMA environment variable as suggested
fukasawa e60969
     * (somewhat imprecisely) in the libpng documentation, use that; otherwise
fukasawa e60969
     * use the default value we just calculated.  Either way, the user may
fukasawa e60969
     * override this via a command-line option. */
fukasawa e60969
fukasawa e60969
    if ((p = getenv("SCREEN_GAMMA")) != NULL) {
fukasawa e60969
        double exponent = atof(p);
fukasawa e60969
fukasawa e60969
        if (exponent > 0.0)
fukasawa e60969
            default_gamma = 1.0 / exponent;
fukasawa e60969
    }
fukasawa e60969
fukasawa e60969
    if (default_gamma == 0.0)
fukasawa e60969
        default_gamma = 1.0 / default_display_exponent;
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* Now parse the command line for options and the PNM filename. */
fukasawa e60969
fukasawa e60969
    while (*++argv && !error) {
fukasawa e60969
        if (!strncmp(*argv, "-i", 2)) {
fukasawa e60969
            wpng_info.interlaced = TRUE;
fukasawa e60969
        } else if (!strncmp(*argv, "-time", 3)) {
fukasawa e60969
            wpng_info.modtime = time(NULL);
fukasawa e60969
            wpng_info.have_time = TRUE;
fukasawa e60969
        } else if (!strncmp(*argv, "-text", 3)) {
fukasawa e60969
            text = TRUE;
fukasawa e60969
        } else if (!strncmp(*argv, "-gamma", 2)) {
fukasawa e60969
            if (!*++argv)
fukasawa e60969
                ++error;
fukasawa e60969
            else {
fukasawa e60969
                wpng_info.gamma = atof(*argv);
fukasawa e60969
                if (wpng_info.gamma <= 0.0)
fukasawa e60969
                    ++error;
fukasawa e60969
                else if (wpng_info.gamma > 1.01)
fukasawa e60969
                    fprintf(stderr, PROGNAME
fukasawa e60969
                      " warning:  file gammas are usually less than 1.0\n");
fukasawa e60969
            }
fukasawa e60969
        } else if (!strncmp(*argv, "-bgcolor", 4)) {
fukasawa e60969
            if (!*++argv)
fukasawa e60969
                ++error;
fukasawa e60969
            else {
fukasawa e60969
                bgstr = *argv;
fukasawa e60969
                if (strlen(bgstr) != 7 || bgstr[0] != '#')
fukasawa e60969
                    ++error;
fukasawa e60969
                else {
fukasawa e60969
                    unsigned r, g, b;  /* this way quiets compiler warnings */
fukasawa e60969
fukasawa e60969
                    sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
fukasawa e60969
                    wpng_info.bg_red   = (uch)r;
fukasawa e60969
                    wpng_info.bg_green = (uch)g;
fukasawa e60969
                    wpng_info.bg_blue  = (uch)b;
fukasawa e60969
                    wpng_info.have_bg = TRUE;
fukasawa e60969
                }
fukasawa e60969
            }
fukasawa e60969
        } else {
fukasawa e60969
            if (**argv != '-') {
fukasawa e60969
                inname = *argv;
fukasawa e60969
                if (argv[1])   /* shouldn't be any more args after filename */
fukasawa e60969
                    ++error;
fukasawa e60969
            } else
fukasawa e60969
                ++error;   /* not expecting any other options */
fukasawa e60969
        }
fukasawa e60969
    }
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* open the input and output files, or register an error and abort */
fukasawa e60969
fukasawa e60969
    if (!inname) {
fukasawa e60969
        if (isatty(0)) {
fukasawa e60969
            fprintf(stderr, PROGNAME
fukasawa e60969
              ":  must give input filename or provide image data via stdin\n");
fukasawa e60969
            ++error;
fukasawa e60969
        } else {
fukasawa e60969
#ifdef DOS_OS2_W32
fukasawa e60969
            /* some buggy C libraries require BOTH setmode() and fdopen(bin) */
fukasawa e60969
            setmode(fileno(stdin), O_BINARY);
fukasawa e60969
            setmode(fileno(stdout), O_BINARY);
fukasawa e60969
#endif
fukasawa e60969
            if ((wpng_info.infile = fdopen(fileno(stdin), "rb")) == NULL) {
fukasawa e60969
                fprintf(stderr, PROGNAME
fukasawa e60969
                  ":  unable to reopen stdin in binary mode\n");
fukasawa e60969
                ++error;
fukasawa e60969
            } else
fukasawa e60969
            if ((wpng_info.outfile = fdopen(fileno(stdout), "wb")) == NULL) {
fukasawa e60969
                fprintf(stderr, PROGNAME
fukasawa e60969
                  ":  unable to reopen stdout in binary mode\n");
fukasawa e60969
                fclose(wpng_info.infile);
fukasawa e60969
                ++error;
fukasawa e60969
            } else
fukasawa e60969
                wpng_info.filter = TRUE;
fukasawa e60969
        }
fukasawa e60969
    } else if ((len = strlen(inname)) > 250) {
fukasawa e60969
        fprintf(stderr, PROGNAME ":  input filename is too long [%d chars]\n",
fukasawa e60969
          len);
fukasawa e60969
        ++error;
fukasawa e60969
    } else if (!(wpng_info.infile = fopen(inname, "rb"))) {
fukasawa e60969
        fprintf(stderr, PROGNAME ":  can't open input file [%s]\n", inname);
fukasawa e60969
        ++error;
fukasawa e60969
    }
fukasawa e60969
fukasawa e60969
    if (!error) {
fukasawa e60969
        fgets(pnmline, 256, wpng_info.infile);
fukasawa e60969
        if (pnmline[0] != 'P' || ((pnmchar = pnmline[1]) != '5' &&
fukasawa e60969
            pnmchar != '6' && pnmchar != '8'))
fukasawa e60969
        {
fukasawa e60969
            fprintf(stderr, PROGNAME
fukasawa e60969
              ":  input file [%s] is not a binary PGM, PPM or PAM file\n",
fukasawa e60969
              inname);
fukasawa e60969
            ++error;
fukasawa e60969
        } else {
fukasawa e60969
            wpng_info.pnmtype = (int)(pnmchar - '0');
fukasawa e60969
            if (wpng_info.pnmtype != 8)
fukasawa e60969
                wpng_info.have_bg = FALSE;  /* no need for bg if opaque */
fukasawa e60969
            do {
fukasawa e60969
                fgets(pnmline, 256, wpng_info.infile);  /* lose any comments */
fukasawa e60969
            } while (pnmline[0] == '#');
fukasawa e60969
            sscanf(pnmline, "%ld %ld", &wpng_info.width, &wpng_info.height);
fukasawa e60969
            do {
fukasawa e60969
                fgets(pnmline, 256, wpng_info.infile);  /* more comment lines */
fukasawa e60969
            } while (pnmline[0] == '#');
fukasawa e60969
            sscanf(pnmline, "%d", &maxval);
fukasawa e60969
            if (wpng_info.width <= 0L || wpng_info.height <= 0L ||
fukasawa e60969
                maxval != 255)
fukasawa e60969
            {
fukasawa e60969
                fprintf(stderr, PROGNAME
fukasawa e60969
                  ":  only positive width/height, maxval == 255 allowed \n");
fukasawa e60969
                ++error;
fukasawa e60969
            }
fukasawa e60969
            wpng_info.sample_depth = 8;  /* <==> maxval 255 */
fukasawa e60969
fukasawa e60969
            if (!wpng_info.filter) {
fukasawa e60969
                /* make outname from inname */
fukasawa e60969
                if ((p = strrchr(inname, '.')) == NULL ||
fukasawa e60969
                    (p - inname) != (len - 4))
fukasawa e60969
                {
fukasawa e60969
                    strcpy(outname, inname);
fukasawa e60969
                    strcpy(outname+len, ".png");
fukasawa e60969
                } else {
fukasawa e60969
                    len -= 4;
fukasawa e60969
                    strncpy(outname, inname, len);
fukasawa e60969
                    strcpy(outname+len, ".png");
fukasawa e60969
                }
fukasawa e60969
                /* check if outname already exists; if not, open */
fukasawa e60969
                if ((wpng_info.outfile = fopen(outname, "rb")) != NULL) {
fukasawa e60969
                    fprintf(stderr, PROGNAME ":  output file exists [%s]\n",
fukasawa e60969
                      outname);
fukasawa e60969
                    fclose(wpng_info.outfile);
fukasawa e60969
                    ++error;
fukasawa e60969
                } else if (!(wpng_info.outfile = fopen(outname, "wb"))) {
fukasawa e60969
                    fprintf(stderr, PROGNAME ":  can't open output file [%s]\n",
fukasawa e60969
                      outname);
fukasawa e60969
                    ++error;
fukasawa e60969
                }
fukasawa e60969
            }
fukasawa e60969
        }
fukasawa e60969
        if (error) {
fukasawa e60969
            fclose(wpng_info.infile);
fukasawa e60969
            wpng_info.infile = NULL;
fukasawa e60969
            if (wpng_info.filter) {
fukasawa e60969
                fclose(wpng_info.outfile);
fukasawa e60969
                wpng_info.outfile = NULL;
fukasawa e60969
            }
fukasawa e60969
        }
fukasawa e60969
    }
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* if we had any errors, print usage and die horrible death...arrr! */
fukasawa e60969
fukasawa e60969
    if (error) {
fukasawa e60969
        fprintf(stderr, "\n%s %s:  %s\n", PROGNAME, VERSION, APPNAME);
fukasawa e60969
        writepng_version_info();
fukasawa e60969
        fprintf(stderr, "\n"
fukasawa e60969
"Usage:  %s [-gamma exp] [-bgcolor bg] [-text] [-time] [-interlace] pnmfile\n"
fukasawa e60969
"or: ... | %s [-gamma exp] [-bgcolor bg] [-text] [-time] [-interlace] | ...\n"
fukasawa e60969
         "    exp \ttransfer-function exponent (``gamma'') of the image in\n"
fukasawa e60969
         "\t\t  floating-point format (e.g., ``%.5f''); if image looks\n"
fukasawa e60969
         "\t\t  correct on given display system, image gamma is equal to\n"
fukasawa e60969
         "\t\t  inverse of display-system exponent, i.e., 1 / (LUT * CRT)\n"
fukasawa e60969
         "\t\t  (where LUT = lookup-table exponent and CRT = CRT exponent;\n"
fukasawa e60969
         "\t\t  first varies, second is usually 2.2, all are positive)\n"
fukasawa e60969
         "    bg  \tdesired background color for alpha-channel images, in\n"
fukasawa e60969
         "\t\t  7-character hex RGB format (e.g., ``#ff7700'' for orange:\n"
fukasawa e60969
         "\t\t  same as HTML colors)\n"
fukasawa e60969
         "    -text\tprompt interactively for text info (tEXt chunks)\n"
fukasawa e60969
         "    -time\tinclude a tIME chunk (last modification time)\n"
fukasawa e60969
         "    -interlace\twrite interlaced PNG image\n"
fukasawa e60969
         "\n"
fukasawa e60969
"pnmfile or stdin must be a binary PGM (`P5'), PPM (`P6') or (extremely\n"
fukasawa e60969
"unofficial and unsupported!) PAM (`P8') file.  Currently it is required\n"
fukasawa e60969
"to have maxval == 255 (i.e., no scaling).  If pnmfile is specified, it\n"
fukasawa e60969
"is converted to the corresponding PNG file with the same base name but a\n"
fukasawa e60969
"``.png'' extension; files read from stdin are converted and sent to stdout.\n"
fukasawa e60969
"The conversion is progressive (low memory usage) unless interlacing is\n"
fukasawa e60969
"requested; in that case the whole image will be buffered in memory and\n"
fukasawa e60969
"written in one call.\n"
fukasawa e60969
         "\n", PROGNAME, PROGNAME, default_gamma);
fukasawa e60969
        exit(1);
fukasawa e60969
    }
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* prepare the text buffers for libpng's use; note that even though
fukasawa e60969
     * PNG's png_text struct includes a length field, we don't have to fill
fukasawa e60969
     * it out */
fukasawa e60969
fukasawa e60969
    if (text &&
fukasawa e60969
#ifndef DOS_OS2_W32
fukasawa e60969
        (keybd = fdopen(fileno(stderr), "r")) != NULL &&
fukasawa e60969
#endif
fukasawa e60969
        (textbuf = (char *)malloc((5 + 9)*75)) != NULL)
fukasawa e60969
    {
fukasawa e60969
        int i, valid, result;
fukasawa e60969
fukasawa e60969
        fprintf(stderr,
fukasawa e60969
          "Enter text info (no more than 72 characters per line);\n");
fukasawa e60969
        fprintf(stderr, "to skip a field, hit the <enter> key.\n");</enter>
fukasawa e60969
        /* note:  just <enter> leaves len == 1 */</enter>
fukasawa e60969
fukasawa e60969
        do {
fukasawa e60969
            valid = TRUE;
fukasawa e60969
            p = textbuf + TEXT_TITLE_OFFSET;
fukasawa e60969
            fprintf(stderr, "  Title: ");
fukasawa e60969
            fflush(stderr);
fukasawa e60969
            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
fukasawa e60969
                if (p[len-1] == '\n')
fukasawa e60969
                    p[--len] = '\0';
fukasawa e60969
                wpng_info.title = p;
fukasawa e60969
                wpng_info.have_text |= TEXT_TITLE;
fukasawa e60969
                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
fukasawa e60969
                    fprintf(stderr, "    " PROGNAME " warning:  character code"
fukasawa e60969
                      " %u is %sdiscouraged by the PNG\n    specification "
fukasawa e60969
                      "[first occurrence was at character position #%d]\n",
fukasawa e60969
                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
fukasawa e60969
                      result+1);
fukasawa e60969
                    fflush(stderr);
fukasawa e60969
#ifdef FORBID_LATIN1_CTRL
fukasawa e60969
                    wpng_info.have_text &= ~TEXT_TITLE;
fukasawa e60969
                    valid = FALSE;
fukasawa e60969
#else
fukasawa e60969
                    if (p[result] == 27) {    /* escape character */
fukasawa e60969
                        wpng_info.have_text &= ~TEXT_TITLE;
fukasawa e60969
                        valid = FALSE;
fukasawa e60969
                    }
fukasawa e60969
#endif
fukasawa e60969
                }
fukasawa e60969
            }
fukasawa e60969
        } while (!valid);
fukasawa e60969
fukasawa e60969
        do {
fukasawa e60969
            valid = TRUE;
fukasawa e60969
            p = textbuf + TEXT_AUTHOR_OFFSET;
fukasawa e60969
            fprintf(stderr, "  Author: ");
fukasawa e60969
            fflush(stderr);
fukasawa e60969
            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
fukasawa e60969
                if (p[len-1] == '\n')
fukasawa e60969
                    p[--len] = '\0';
fukasawa e60969
                wpng_info.author = p;
fukasawa e60969
                wpng_info.have_text |= TEXT_AUTHOR;
fukasawa e60969
                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
fukasawa e60969
                    fprintf(stderr, "    " PROGNAME " warning:  character code"
fukasawa e60969
                      " %u is %sdiscouraged by the PNG\n    specification "
fukasawa e60969
                      "[first occurrence was at character position #%d]\n",
fukasawa e60969
                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
fukasawa e60969
                      result+1);
fukasawa e60969
                    fflush(stderr);
fukasawa e60969
#ifdef FORBID_LATIN1_CTRL
fukasawa e60969
                    wpng_info.have_text &= ~TEXT_AUTHOR;
fukasawa e60969
                    valid = FALSE;
fukasawa e60969
#else
fukasawa e60969
                    if (p[result] == 27) {    /* escape character */
fukasawa e60969
                        wpng_info.have_text &= ~TEXT_AUTHOR;
fukasawa e60969
                        valid = FALSE;
fukasawa e60969
                    }
fukasawa e60969
#endif
fukasawa e60969
                }
fukasawa e60969
            }
fukasawa e60969
        } while (!valid);
fukasawa e60969
fukasawa e60969
        do {
fukasawa e60969
            valid = TRUE;
fukasawa e60969
            p = textbuf + TEXT_DESC_OFFSET;
fukasawa e60969
            fprintf(stderr, "  Description (up to 9 lines):\n");
fukasawa e60969
            for (i = 1;  i < 10;  ++i) {
fukasawa e60969
                fprintf(stderr, "    [%d] ", i);
fukasawa e60969
                fflush(stderr);
fukasawa e60969
                if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1)
fukasawa e60969
                    p += len;   /* now points at NULL; char before is newline */
fukasawa e60969
                else
fukasawa e60969
                    break;
fukasawa e60969
            }
fukasawa e60969
            if ((len = p - (textbuf + TEXT_DESC_OFFSET)) > 1) {
fukasawa e60969
                if (p[-1] == '\n') {
fukasawa e60969
                    p[-1] = '\0';
fukasawa e60969
                    --len;
fukasawa e60969
                }
fukasawa e60969
                wpng_info.desc = textbuf + TEXT_DESC_OFFSET;
fukasawa e60969
                wpng_info.have_text |= TEXT_DESC;
fukasawa e60969
                p = textbuf + TEXT_DESC_OFFSET;
fukasawa e60969
                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
fukasawa e60969
                    fprintf(stderr, "    " PROGNAME " warning:  character code"
fukasawa e60969
                      " %u is %sdiscouraged by the PNG\n    specification "
fukasawa e60969
                      "[first occurrence was at character position #%d]\n",
fukasawa e60969
                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
fukasawa e60969
                      result+1);
fukasawa e60969
                    fflush(stderr);
fukasawa e60969
#ifdef FORBID_LATIN1_CTRL
fukasawa e60969
                    wpng_info.have_text &= ~TEXT_DESC;
fukasawa e60969
                    valid = FALSE;
fukasawa e60969
#else
fukasawa e60969
                    if (p[result] == 27) {    /* escape character */
fukasawa e60969
                        wpng_info.have_text &= ~TEXT_DESC;
fukasawa e60969
                        valid = FALSE;
fukasawa e60969
                    }
fukasawa e60969
#endif
fukasawa e60969
                }
fukasawa e60969
            }
fukasawa e60969
        } while (!valid);
fukasawa e60969
fukasawa e60969
        do {
fukasawa e60969
            valid = TRUE;
fukasawa e60969
            p = textbuf + TEXT_COPY_OFFSET;
fukasawa e60969
            fprintf(stderr, "  Copyright: ");
fukasawa e60969
            fflush(stderr);
fukasawa e60969
            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
fukasawa e60969
                if (p[len-1] == '\n')
fukasawa e60969
                    p[--len] = '\0';
fukasawa e60969
                wpng_info.copyright = p;
fukasawa e60969
                wpng_info.have_text |= TEXT_COPY;
fukasawa e60969
                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
fukasawa e60969
                    fprintf(stderr, "    " PROGNAME " warning:  character code"
fukasawa e60969
                      " %u is %sdiscouraged by the PNG\n    specification "
fukasawa e60969
                      "[first occurrence was at character position #%d]\n",
fukasawa e60969
                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
fukasawa e60969
                      result+1);
fukasawa e60969
                    fflush(stderr);
fukasawa e60969
#ifdef FORBID_LATIN1_CTRL
fukasawa e60969
                    wpng_info.have_text &= ~TEXT_COPY;
fukasawa e60969
                    valid = FALSE;
fukasawa e60969
#else
fukasawa e60969
                    if (p[result] == 27) {    /* escape character */
fukasawa e60969
                        wpng_info.have_text &= ~TEXT_COPY;
fukasawa e60969
                        valid = FALSE;
fukasawa e60969
                    }
fukasawa e60969
#endif
fukasawa e60969
                }
fukasawa e60969
            }
fukasawa e60969
        } while (!valid);
fukasawa e60969
fukasawa e60969
        do {
fukasawa e60969
            valid = TRUE;
fukasawa e60969
            p = textbuf + TEXT_EMAIL_OFFSET;
fukasawa e60969
            fprintf(stderr, "  E-mail: ");
fukasawa e60969
            fflush(stderr);
fukasawa e60969
            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
fukasawa e60969
                if (p[len-1] == '\n')
fukasawa e60969
                    p[--len] = '\0';
fukasawa e60969
                wpng_info.email = p;
fukasawa e60969
                wpng_info.have_text |= TEXT_EMAIL;
fukasawa e60969
                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
fukasawa e60969
                    fprintf(stderr, "    " PROGNAME " warning:  character code"
fukasawa e60969
                      " %u is %sdiscouraged by the PNG\n    specification "
fukasawa e60969
                      "[first occurrence was at character position #%d]\n",
fukasawa e60969
                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
fukasawa e60969
                      result+1);
fukasawa e60969
                    fflush(stderr);
fukasawa e60969
#ifdef FORBID_LATIN1_CTRL
fukasawa e60969
                    wpng_info.have_text &= ~TEXT_EMAIL;
fukasawa e60969
                    valid = FALSE;
fukasawa e60969
#else
fukasawa e60969
                    if (p[result] == 27) {    /* escape character */
fukasawa e60969
                        wpng_info.have_text &= ~TEXT_EMAIL;
fukasawa e60969
                        valid = FALSE;
fukasawa e60969
                    }
fukasawa e60969
#endif
fukasawa e60969
                }
fukasawa e60969
            }
fukasawa e60969
        } while (!valid);
fukasawa e60969
fukasawa e60969
        do {
fukasawa e60969
            valid = TRUE;
fukasawa e60969
            p = textbuf + TEXT_URL_OFFSET;
fukasawa e60969
            fprintf(stderr, "  URL: ");
fukasawa e60969
            fflush(stderr);
fukasawa e60969
            if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
fukasawa e60969
                if (p[len-1] == '\n')
fukasawa e60969
                    p[--len] = '\0';
fukasawa e60969
                wpng_info.url = p;
fukasawa e60969
                wpng_info.have_text |= TEXT_URL;
fukasawa e60969
                if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
fukasawa e60969
                    fprintf(stderr, "    " PROGNAME " warning:  character code"
fukasawa e60969
                      " %u is %sdiscouraged by the PNG\n    specification "
fukasawa e60969
                      "[first occurrence was at character position #%d]\n",
fukasawa e60969
                      (unsigned)p[result], (p[result] == 27)? "strongly " : "",
fukasawa e60969
                      result+1);
fukasawa e60969
                    fflush(stderr);
fukasawa e60969
#ifdef FORBID_LATIN1_CTRL
fukasawa e60969
                    wpng_info.have_text &= ~TEXT_URL;
fukasawa e60969
                    valid = FALSE;
fukasawa e60969
#else
fukasawa e60969
                    if (p[result] == 27) {    /* escape character */
fukasawa e60969
                        wpng_info.have_text &= ~TEXT_URL;
fukasawa e60969
                        valid = FALSE;
fukasawa e60969
                    }
fukasawa e60969
#endif
fukasawa e60969
                }
fukasawa e60969
            }
fukasawa e60969
        } while (!valid);
fukasawa e60969
fukasawa e60969
#ifndef DOS_OS2_W32
fukasawa e60969
        fclose(keybd);
fukasawa e60969
#endif
fukasawa e60969
fukasawa e60969
    } else if (text) {
fukasawa e60969
        fprintf(stderr, PROGNAME ":  unable to allocate memory for text\n");
fukasawa e60969
        text = FALSE;
fukasawa e60969
        wpng_info.have_text = 0;
fukasawa e60969
    }
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* allocate libpng stuff, initialize transformations, write pre-IDAT data */
fukasawa e60969
fukasawa e60969
    if ((rc = writepng_init(&wpng_info)) != 0) {
fukasawa e60969
        switch (rc) {
fukasawa e60969
            case 2:
fukasawa e60969
                fprintf(stderr, PROGNAME
fukasawa e60969
                  ":  libpng initialization problem (longjmp)\n");
fukasawa e60969
                break;
fukasawa e60969
            case 4:
fukasawa e60969
                fprintf(stderr, PROGNAME ":  insufficient memory\n");
fukasawa e60969
                break;
fukasawa e60969
            case 11:
fukasawa e60969
                fprintf(stderr, PROGNAME
fukasawa e60969
                  ":  internal logic error (unexpected PNM type)\n");
fukasawa e60969
                break;
fukasawa e60969
            default:
fukasawa e60969
                fprintf(stderr, PROGNAME
fukasawa e60969
                  ":  unknown writepng_init() error\n");
fukasawa e60969
                break;
fukasawa e60969
        }
fukasawa e60969
        exit(rc);
fukasawa e60969
    }
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* free textbuf, since it's a completely local variable and all text info
fukasawa e60969
     * has just been written to the PNG file */
fukasawa e60969
fukasawa e60969
    if (text && textbuf) {
fukasawa e60969
        free(textbuf);
fukasawa e60969
        textbuf = NULL;
fukasawa e60969
    }
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* calculate rowbytes on basis of image type; note that this becomes much
fukasawa e60969
     * more complicated if we choose to support PBM type, ASCII PNM types, or
fukasawa e60969
     * 16-bit-per-sample binary data [currently not an official NetPBM type] */
fukasawa e60969
fukasawa e60969
    if (wpng_info.pnmtype == 5)
fukasawa e60969
        rowbytes = wpng_info.width;
fukasawa e60969
    else if (wpng_info.pnmtype == 6)
fukasawa e60969
        rowbytes = wpng_info.width * 3;
fukasawa e60969
    else /* if (wpng_info.pnmtype == 8) */
fukasawa e60969
        rowbytes = wpng_info.width * 4;
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* read and write the image, either in its entirety (if writing interlaced
fukasawa e60969
     * PNG) or row by row (if non-interlaced) */
fukasawa e60969
fukasawa e60969
    fprintf(stderr, "Encoding image data...\n");
fukasawa e60969
    fflush(stderr);
fukasawa e60969
fukasawa e60969
    if (wpng_info.interlaced) {
fukasawa e60969
        long i;
fukasawa e60969
        ulg bytes;
fukasawa e60969
        ulg image_bytes = rowbytes * wpng_info.height;   /* overflow? */
fukasawa e60969
fukasawa e60969
        wpng_info.image_data = (uch *)malloc(image_bytes);
fukasawa e60969
        wpng_info.row_pointers = (uch **)malloc(wpng_info.height*sizeof(uch *));
fukasawa e60969
        if (wpng_info.image_data == NULL || wpng_info.row_pointers == NULL) {
fukasawa e60969
            fprintf(stderr, PROGNAME ":  insufficient memory for image data\n");
fukasawa e60969
            writepng_cleanup(&wpng_info);
fukasawa e60969
            wpng_cleanup();
fukasawa e60969
            exit(5);
fukasawa e60969
        }
fukasawa e60969
        for (i = 0;  i < wpng_info.height;  ++i)
fukasawa e60969
            wpng_info.row_pointers[i] = wpng_info.image_data + i*rowbytes;
fukasawa e60969
        bytes = fread(wpng_info.image_data, 1, image_bytes, wpng_info.infile);
fukasawa e60969
        if (bytes != image_bytes) {
fukasawa e60969
            fprintf(stderr, PROGNAME ":  expected %lu bytes, got %lu bytes\n",
fukasawa e60969
              image_bytes, bytes);
fukasawa e60969
            fprintf(stderr, "  (continuing anyway)\n");
fukasawa e60969
        }
fukasawa e60969
        if (writepng_encode_image(&wpng_info) != 0) {
fukasawa e60969
            fprintf(stderr, PROGNAME
fukasawa e60969
              ":  libpng problem (longjmp) while writing image data\n");
fukasawa e60969
            writepng_cleanup(&wpng_info);
fukasawa e60969
            wpng_cleanup();
fukasawa e60969
            exit(2);
fukasawa e60969
        }
fukasawa e60969
fukasawa e60969
    } else /* not interlaced:  write progressively (row by row) */ {
fukasawa e60969
        long j;
fukasawa e60969
        ulg bytes;
fukasawa e60969
fukasawa e60969
        wpng_info.image_data = (uch *)malloc(rowbytes);
fukasawa e60969
        if (wpng_info.image_data == NULL) {
fukasawa e60969
            fprintf(stderr, PROGNAME ":  insufficient memory for row data\n");
fukasawa e60969
            writepng_cleanup(&wpng_info);
fukasawa e60969
            wpng_cleanup();
fukasawa e60969
            exit(5);
fukasawa e60969
        }
fukasawa e60969
        error = 0;
fukasawa e60969
        for (j = wpng_info.height;  j > 0L;  --j) {
fukasawa e60969
            bytes = fread(wpng_info.image_data, 1, rowbytes, wpng_info.infile);
fukasawa e60969
            if (bytes != rowbytes) {
fukasawa e60969
                fprintf(stderr, PROGNAME
fukasawa e60969
                  ":  expected %lu bytes, got %lu bytes (row %ld)\n", rowbytes,
fukasawa e60969
                  bytes, wpng_info.height-j);
fukasawa e60969
                ++error;
fukasawa e60969
                break;
fukasawa e60969
            }
fukasawa e60969
            if (writepng_encode_row(&wpng_info) != 0) {
fukasawa e60969
                fprintf(stderr, PROGNAME
fukasawa e60969
                  ":  libpng problem (longjmp) while writing row %ld\n",
fukasawa e60969
                  wpng_info.height-j);
fukasawa e60969
                ++error;
fukasawa e60969
                break;
fukasawa e60969
            }
fukasawa e60969
        }
fukasawa e60969
        if (error) {
fukasawa e60969
            writepng_cleanup(&wpng_info);
fukasawa e60969
            wpng_cleanup();
fukasawa e60969
            exit(2);
fukasawa e60969
        }
fukasawa e60969
        if (writepng_encode_finish(&wpng_info) != 0) {
fukasawa e60969
            fprintf(stderr, PROGNAME ":  error on final libpng call\n");
fukasawa e60969
            writepng_cleanup(&wpng_info);
fukasawa e60969
            wpng_cleanup();
fukasawa e60969
            exit(2);
fukasawa e60969
        }
fukasawa e60969
    }
fukasawa e60969
fukasawa e60969
fukasawa e60969
    /* OK, we're done (successfully):  clean up all resources and quit */
fukasawa e60969
fukasawa e60969
    fprintf(stderr, "Done.\n");
fukasawa e60969
    fflush(stderr);
fukasawa e60969
fukasawa e60969
    writepng_cleanup(&wpng_info);
fukasawa e60969
    wpng_cleanup();
fukasawa e60969
fukasawa e60969
    return 0;
fukasawa e60969
}
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
static int wpng_isvalid_latin1(uch *p, int len)
fukasawa e60969
{
fukasawa e60969
    int i, result = -1;
fukasawa e60969
fukasawa e60969
    for (i = 0;  i < len;  ++i) {
fukasawa e60969
        if (p[i] == 10 || (p[i] > 31 && p[i] < 127) || p[i] > 160)
fukasawa e60969
            continue;           /* character is completely OK */
fukasawa e60969
        if (result < 0 || (p[result] != 27 && p[i] == 27))
fukasawa e60969
            result = i;         /* mark location of first questionable one */
fukasawa e60969
    }                           /*  or of first escape character (bad) */
fukasawa e60969
fukasawa e60969
    return result;
fukasawa e60969
}
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
static void wpng_cleanup(void)
fukasawa e60969
{
fukasawa e60969
    if (wpng_info.outfile) {
fukasawa e60969
        fclose(wpng_info.outfile);
fukasawa e60969
        wpng_info.outfile = NULL;
fukasawa e60969
    }
fukasawa e60969
fukasawa e60969
    if (wpng_info.infile) {
fukasawa e60969
        fclose(wpng_info.infile);
fukasawa e60969
        wpng_info.infile = NULL;
fukasawa e60969
    }
fukasawa e60969
fukasawa e60969
    if (wpng_info.image_data) {
fukasawa e60969
        free(wpng_info.image_data);
fukasawa e60969
        wpng_info.image_data = NULL;
fukasawa e60969
    }
fukasawa e60969
fukasawa e60969
    if (wpng_info.row_pointers) {
fukasawa e60969
        free(wpng_info.row_pointers);
fukasawa e60969
        wpng_info.row_pointers = NULL;
fukasawa e60969
    }
fukasawa e60969
}
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
fukasawa e60969
#ifdef DOS_OS2_W32
fukasawa e60969
fukasawa e60969
static char *dos_kbd_gets(char *buf, int len)
fukasawa e60969
{
fukasawa e60969
    int ch, count=0;
fukasawa e60969
fukasawa e60969
    do {
fukasawa e60969
        buf[count++] = ch = getche();
fukasawa e60969
    } while (ch != '\r' && count < len-1);
fukasawa e60969
fukasawa e60969
    buf[count--] = '\0';        /* terminate string */
fukasawa e60969
    if (buf[count] == '\r')     /* Enter key makes CR, so change to newline */
fukasawa e60969
        buf[count] = '\n';
fukasawa e60969
fukasawa e60969
    fprintf(stderr, "\n");      /* Enter key does *not* cause a newline */
fukasawa e60969
    fflush(stderr);
fukasawa e60969
fukasawa e60969
    return buf;
fukasawa e60969
}
fukasawa e60969
fukasawa e60969
#endif /* DOS_OS2_W32 */