1 /*---------------------------------------------------------------------------
2
3 wpng - simple PNG-writing program wpng.c
4
5 This program converts certain NetPBM binary files (grayscale and RGB,
6 maxval = 255) to PNG. Non-interlaced PNGs are written progressively;
7 interlaced PNGs are read and written in one memory-intensive blast.
8
9 Thanks to Jean-loup Gailly for providing the necessary trick to read
10 interactive text from the keyboard while stdin is redirected. Thanks
11 to Cosmin Truta for Cygwin fixes.
12
13 NOTE: includes provisional support for PNM type "8" (portable alphamap)
14 images, presumed to be a 32-bit interleaved RGBA format; no pro-
15 vision for possible interleaved grayscale+alpha (16-bit) format.
16 THIS IS UNLIKELY TO BECOME AN OFFICIAL NETPBM ALPHA FORMAT!
17
18 to do:
19 - delete output file if quit before calling any writepng routines
20 - process backspace with -text option under DOS/Win? (currently get ^H)
21
22 ---------------------------------------------------------------------------
23
24 Changelog:
25 - 1.01: initial public release
26 - 1.02: modified to allow abbreviated options
27 - 1.03: removed extraneous character from usage screen; fixed bug in
28 command-line parsing
29 - 1.04: fixed DOS/OS2/Win32 detection, including partial Cygwin fix
30 (see http://home.att.net/~perlspinr/diffs/GregBook_cygwin.diff)
31 - 2.00: dual-licensed (added GNU GPL)
32
33 [REPORTED BUG (win32 only): "contrib/gregbook/wpng.c - cmd line
34 dose not work! In order to do something useful I needed to redirect
35 both input and output, with cygwin and with bcc32 as well. Under
36 Linux, the same wpng appears to work fine. I don't know what is
37 the problem."]
38
39 ---------------------------------------------------------------------------
40
41 Copyright (c) 1998-2007 Greg Roelofs. All rights reserved.
42
43 This software is provided "as is," without warranty of any kind,
44 express or implied. In no event shall the author or contributors
45 be held liable for any damages arising in any way from the use of
46 this software.
47
48 The contents of this file are DUAL-LICENSED. You may modify and/or
49 redistribute this software according to the terms of one of the
50 following two licenses (at your option):
51
52
53 LICENSE 1 ("BSD-like with advertising clause"):
54
55 Permission is granted to anyone to use this software for any purpose,
56 including commercial applications, and to alter it and redistribute
57 it freely, subject to the following restrictions:
58
59 1. Redistributions of source code must retain the above copyright
60 notice, disclaimer, and this list of conditions.
61 2. Redistributions in binary form must reproduce the above copyright
62 notice, disclaimer, and this list of conditions in the documenta-
63 tion and/or other materials provided with the distribution.
64 3. All advertising materials mentioning features or use of this
65 software must display the following acknowledgment:
66
67 This product includes software developed by Greg Roelofs
68 and contributors for the book, "PNG: The Definitive Guide,"
69 published by O'Reilly and Associates.
70
71
72 LICENSE 2 (GNU GPL v2 or later):
73
74 This program is free software; you can redistribute it and/or modify
75 it under the terms of the GNU General Public License as published by
76 the Free Software Foundation; either version 2 of the License, or
77 (at your option) any later version.
78
79 This program is distributed in the hope that it will be useful,
80 but WITHOUT ANY WARRANTY; without even the implied warranty of
81 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
82 GNU General Public License for more details.
83
84 You should have received a copy of the GNU General Public License
85 along with this program; if not, write to the Free Software Foundation,
86 Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
87
88 ---------------------------------------------------------------------------*/
89
90 #define PROGNAME "wpng"
91 #define VERSION "2.00 of 2 June 2007"
92 #define APPNAME "Simple PGM/PPM/PAM to PNG Converter"
93
94 #if defined(__MSDOS__) || defined(__OS2__)
95 # define DOS_OS2_W32
96 #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
97 # ifndef __GNUC__ /* treat Win32 native ports of gcc as Unix environments */
98 # define DOS_OS2_W32
99 # endif
100 #endif
101
102 #include <stdio.h>
103 #include <stdlib.h>
104 #include <string.h>
105 #include <setjmp.h> /* for jmpbuf declaration in writepng.h */
106 #include <time.h>
107
108 #ifdef DOS_OS2_W32
109 # include <io.h> /* for isatty(), setmode() prototypes */
110 # include <fcntl.h> /* O_BINARY for fdopen() without text translation */
111 # ifdef __EMX__
112 # ifndef getch
113 # define getch() _read_kbd(0, 1, 0) /* need getche() */
114 # endif
115 # else /* !__EMX__ */
116 # ifdef __GO32__
117 # include <pc.h>
118 # define getch() getkey() /* GRR: need getche() */
119 # else
120 # include <conio.h> /* for getche() console input */
121 # endif
122 # endif /* ?__EMX__ */
123 # define FGETS(buf,len,stream) dos_kbd_gets(buf,len)
124 #else
125 # include <unistd.h> /* for isatty() prototype */
126 # define FGETS fgets
127 #endif
128
129 /* #define DEBUG : this enables the Trace() macros */
130
131 /* #define FORBID_LATIN1_CTRL : this requires the user to re-enter any
132 text that includes control characters discouraged by the PNG spec; text
133 that includes an escape character (27) must be re-entered regardless */
134
135 #include "writepng.h" /* typedefs, common macros, writepng prototypes */
136
137
138
139 /* local prototypes */
140
141 static int wpng_isvalid_latin1(uch *p, int len);
142 static void wpng_cleanup(void);
143
144 #ifdef DOS_OS2_W32
145 static char *dos_kbd_gets(char *buf, int len);
146 #endif
147
148
149
150 static mainprog_info wpng_info; /* lone global */
151
152
153
main(int argc,char ** argv)154 int main(int argc, char **argv)
155 {
156 #ifndef DOS_OS2_W32
157 FILE *keybd;
158 #endif
159 #ifdef sgi
160 FILE *tmpfile; /* or we could just use keybd, since no overlap */
161 char tmpline[80];
162 #endif
163 char *inname = NULL, outname[256];
164 char *p, pnmchar, pnmline[256];
165 char *bgstr, *textbuf = NULL;
166 ulg rowbytes;
167 int rc, len = 0;
168 int error = 0;
169 int text = FALSE;
170 int maxval;
171 double LUT_exponent; /* just the lookup table */
172 double CRT_exponent = 2.2; /* just the monitor */
173 double default_display_exponent; /* whole display system */
174 double default_gamma = 0.0;
175
176
177 wpng_info.infile = NULL;
178 wpng_info.outfile = NULL;
179 wpng_info.image_data = NULL;
180 wpng_info.row_pointers = NULL;
181 wpng_info.filter = FALSE;
182 wpng_info.interlaced = FALSE;
183 wpng_info.have_bg = FALSE;
184 wpng_info.have_time = FALSE;
185 wpng_info.have_text = 0;
186 wpng_info.gamma = 0.0;
187
188
189 /* First get the default value for our display-system exponent, i.e.,
190 * the product of the CRT exponent and the exponent corresponding to
191 * the frame-buffer's lookup table (LUT), if any. If the PNM image
192 * looks correct on the user's display system, its file gamma is the
193 * inverse of this value. (Note that this is not an exhaustive list
194 * of LUT values--e.g., OpenStep has a lot of weird ones--but it should
195 * cover 99% of the current possibilities. This section must ensure
196 * that default_display_exponent is positive.) */
197
198 #if defined(NeXT)
199 /* third-party utilities can modify the default LUT exponent */
200 LUT_exponent = 1.0 / 2.2;
201 /*
202 if (some_next_function_that_returns_gamma(&next_gamma))
203 LUT_exponent = 1.0 / next_gamma;
204 */
205 #elif defined(sgi)
206 LUT_exponent = 1.0 / 1.7;
207 /* there doesn't seem to be any documented function to
208 * get the "gamma" value, so we do it the hard way */
209 tmpfile = fopen("/etc/config/system.glGammaVal", "r");
210 if (tmpfile) {
211 double sgi_gamma;
212
213 fgets(tmpline, 80, tmpfile);
214 fclose(tmpfile);
215 sgi_gamma = atof(tmpline);
216 if (sgi_gamma > 0.0)
217 LUT_exponent = 1.0 / sgi_gamma;
218 }
219 #elif defined(Macintosh)
220 LUT_exponent = 1.8 / 2.61;
221 /*
222 if (some_mac_function_that_returns_gamma(&mac_gamma))
223 LUT_exponent = mac_gamma / 2.61;
224 */
225 #else
226 LUT_exponent = 1.0; /* assume no LUT: most PCs */
227 #endif
228
229 /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */
230 default_display_exponent = LUT_exponent * CRT_exponent;
231
232
233 /* If the user has set the SCREEN_GAMMA environment variable as suggested
234 * (somewhat imprecisely) in the libpng documentation, use that; otherwise
235 * use the default value we just calculated. Either way, the user may
236 * override this via a command-line option. */
237
238 if ((p = getenv("SCREEN_GAMMA")) != NULL) {
239 double exponent = atof(p);
240
241 if (exponent > 0.0)
242 default_gamma = 1.0 / exponent;
243 }
244
245 if (default_gamma == 0.0)
246 default_gamma = 1.0 / default_display_exponent;
247
248
249 /* Now parse the command line for options and the PNM filename. */
250
251 while (*++argv && !error) {
252 if (!strncmp(*argv, "-i", 2)) {
253 wpng_info.interlaced = TRUE;
254 } else if (!strncmp(*argv, "-time", 3)) {
255 wpng_info.modtime = time(NULL);
256 wpng_info.have_time = TRUE;
257 } else if (!strncmp(*argv, "-text", 3)) {
258 text = TRUE;
259 } else if (!strncmp(*argv, "-gamma", 2)) {
260 if (!*++argv)
261 ++error;
262 else {
263 wpng_info.gamma = atof(*argv);
264 if (wpng_info.gamma <= 0.0)
265 ++error;
266 else if (wpng_info.gamma > 1.01)
267 fprintf(stderr, PROGNAME
268 " warning: file gammas are usually less than 1.0\n");
269 }
270 } else if (!strncmp(*argv, "-bgcolor", 4)) {
271 if (!*++argv)
272 ++error;
273 else {
274 bgstr = *argv;
275 if (strlen(bgstr) != 7 || bgstr[0] != '#')
276 ++error;
277 else {
278 unsigned r, g, b; /* this way quiets compiler warnings */
279
280 sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b);
281 wpng_info.bg_red = (uch)r;
282 wpng_info.bg_green = (uch)g;
283 wpng_info.bg_blue = (uch)b;
284 wpng_info.have_bg = TRUE;
285 }
286 }
287 } else {
288 if (**argv != '-') {
289 inname = *argv;
290 if (argv[1]) /* shouldn't be any more args after filename */
291 ++error;
292 } else
293 ++error; /* not expecting any other options */
294 }
295 }
296
297
298 /* open the input and output files, or register an error and abort */
299
300 if (!inname) {
301 if (isatty(0)) {
302 fprintf(stderr, PROGNAME
303 ": must give input filename or provide image data via stdin\n");
304 ++error;
305 } else {
306 #ifdef DOS_OS2_W32
307 /* some buggy C libraries require BOTH setmode() and fdopen(bin) */
308 setmode(fileno(stdin), O_BINARY);
309 setmode(fileno(stdout), O_BINARY);
310 #endif
311 if ((wpng_info.infile = fdopen(fileno(stdin), "rb")) == NULL) {
312 fprintf(stderr, PROGNAME
313 ": unable to reopen stdin in binary mode\n");
314 ++error;
315 } else
316 if ((wpng_info.outfile = fdopen(fileno(stdout), "wb")) == NULL) {
317 fprintf(stderr, PROGNAME
318 ": unable to reopen stdout in binary mode\n");
319 fclose(wpng_info.infile);
320 ++error;
321 } else
322 wpng_info.filter = TRUE;
323 }
324 } else if ((len = strlen(inname)) > 250) {
325 fprintf(stderr, PROGNAME ": input filename is too long [%d chars]\n",
326 len);
327 ++error;
328 } else if (!(wpng_info.infile = fopen(inname, "rb"))) {
329 fprintf(stderr, PROGNAME ": can't open input file [%s]\n", inname);
330 ++error;
331 }
332
333 if (!error) {
334 fgets(pnmline, 256, wpng_info.infile);
335 if (pnmline[0] != 'P' || ((pnmchar = pnmline[1]) != '5' &&
336 pnmchar != '6' && pnmchar != '8'))
337 {
338 fprintf(stderr, PROGNAME
339 ": input file [%s] is not a binary PGM, PPM or PAM file\n",
340 inname);
341 ++error;
342 } else {
343 wpng_info.pnmtype = (int)(pnmchar - '0');
344 if (wpng_info.pnmtype != 8)
345 wpng_info.have_bg = FALSE; /* no need for bg if opaque */
346 do {
347 fgets(pnmline, 256, wpng_info.infile); /* lose any comments */
348 } while (pnmline[0] == '#');
349 sscanf(pnmline, "%ld %ld", &wpng_info.width, &wpng_info.height);
350 do {
351 fgets(pnmline, 256, wpng_info.infile); /* more comment lines */
352 } while (pnmline[0] == '#');
353 sscanf(pnmline, "%d", &maxval);
354 if (wpng_info.width <= 0L || wpng_info.height <= 0L ||
355 maxval != 255)
356 {
357 fprintf(stderr, PROGNAME
358 ": only positive width/height, maxval == 255 allowed \n");
359 ++error;
360 }
361 wpng_info.sample_depth = 8; /* <==> maxval 255 */
362
363 if (!wpng_info.filter) {
364 /* make outname from inname */
365 if ((p = strrchr(inname, '.')) == NULL ||
366 (p - inname) != (len - 4))
367 {
368 strcpy(outname, inname);
369 strcpy(outname+len, ".png");
370 } else {
371 len -= 4;
372 strncpy(outname, inname, len);
373 strcpy(outname+len, ".png");
374 }
375 /* check if outname already exists; if not, open */
376 if ((wpng_info.outfile = fopen(outname, "rb")) != NULL) {
377 fprintf(stderr, PROGNAME ": output file exists [%s]\n",
378 outname);
379 fclose(wpng_info.outfile);
380 ++error;
381 } else if (!(wpng_info.outfile = fopen(outname, "wb"))) {
382 fprintf(stderr, PROGNAME ": can't open output file [%s]\n",
383 outname);
384 ++error;
385 }
386 }
387 }
388 if (error) {
389 fclose(wpng_info.infile);
390 wpng_info.infile = NULL;
391 if (wpng_info.filter) {
392 fclose(wpng_info.outfile);
393 wpng_info.outfile = NULL;
394 }
395 }
396 }
397
398
399 /* if we had any errors, print usage and die horrible death...arrr! */
400
401 if (error) {
402 fprintf(stderr, "\n%s %s: %s\n", PROGNAME, VERSION, APPNAME);
403 writepng_version_info();
404 fprintf(stderr, "\n"
405 "Usage: %s [-gamma exp] [-bgcolor bg] [-text] [-time] [-interlace] pnmfile\n"
406 "or: ... | %s [-gamma exp] [-bgcolor bg] [-text] [-time] [-interlace] | ...\n"
407 " exp \ttransfer-function exponent (``gamma'') of the image in\n"
408 "\t\t floating-point format (e.g., ``%.5f''); if image looks\n"
409 "\t\t correct on given display system, image gamma is equal to\n"
410 "\t\t inverse of display-system exponent, i.e., 1 / (LUT * CRT)\n"
411 "\t\t (where LUT = lookup-table exponent and CRT = CRT exponent;\n"
412 "\t\t first varies, second is usually 2.2, all are positive)\n"
413 " bg \tdesired background color for alpha-channel images, in\n"
414 "\t\t 7-character hex RGB format (e.g., ``#ff7700'' for orange:\n"
415 "\t\t same as HTML colors)\n"
416 " -text\tprompt interactively for text info (tEXt chunks)\n"
417 " -time\tinclude a tIME chunk (last modification time)\n"
418 " -interlace\twrite interlaced PNG image\n"
419 "\n"
420 "pnmfile or stdin must be a binary PGM (`P5'), PPM (`P6') or (extremely\n"
421 "unofficial and unsupported!) PAM (`P8') file. Currently it is required\n"
422 "to have maxval == 255 (i.e., no scaling). If pnmfile is specified, it\n"
423 "is converted to the corresponding PNG file with the same base name but a\n"
424 "``.png'' extension; files read from stdin are converted and sent to stdout.\n"
425 "The conversion is progressive (low memory usage) unless interlacing is\n"
426 "requested; in that case the whole image will be buffered in memory and\n"
427 "written in one call.\n"
428 "\n", PROGNAME, PROGNAME, default_gamma);
429 exit(1);
430 }
431
432
433 /* prepare the text buffers for libpng's use; note that even though
434 * PNG's png_text struct includes a length field, we don't have to fill
435 * it out */
436
437 if (text &&
438 #ifndef DOS_OS2_W32
439 (keybd = fdopen(fileno(stderr), "r")) != NULL &&
440 #endif
441 (textbuf = (char *)malloc((5 + 9)*75)) != NULL)
442 {
443 int i, valid, result;
444
445 fprintf(stderr,
446 "Enter text info (no more than 72 characters per line);\n");
447 fprintf(stderr, "to skip a field, hit the <Enter> key.\n");
448 /* note: just <Enter> leaves len == 1 */
449
450 do {
451 valid = TRUE;
452 p = textbuf + TEXT_TITLE_OFFSET;
453 fprintf(stderr, " Title: ");
454 fflush(stderr);
455 if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
456 if (p[len-1] == '\n')
457 p[--len] = '\0';
458 wpng_info.title = p;
459 wpng_info.have_text |= TEXT_TITLE;
460 if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
461 fprintf(stderr, " " PROGNAME " warning: character code"
462 " %u is %sdiscouraged by the PNG\n specification "
463 "[first occurrence was at character position #%d]\n",
464 (unsigned)p[result], (p[result] == 27)? "strongly " : "",
465 result+1);
466 fflush(stderr);
467 #ifdef FORBID_LATIN1_CTRL
468 wpng_info.have_text &= ~TEXT_TITLE;
469 valid = FALSE;
470 #else
471 if (p[result] == 27) { /* escape character */
472 wpng_info.have_text &= ~TEXT_TITLE;
473 valid = FALSE;
474 }
475 #endif
476 }
477 }
478 } while (!valid);
479
480 do {
481 valid = TRUE;
482 p = textbuf + TEXT_AUTHOR_OFFSET;
483 fprintf(stderr, " Author: ");
484 fflush(stderr);
485 if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
486 if (p[len-1] == '\n')
487 p[--len] = '\0';
488 wpng_info.author = p;
489 wpng_info.have_text |= TEXT_AUTHOR;
490 if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
491 fprintf(stderr, " " PROGNAME " warning: character code"
492 " %u is %sdiscouraged by the PNG\n specification "
493 "[first occurrence was at character position #%d]\n",
494 (unsigned)p[result], (p[result] == 27)? "strongly " : "",
495 result+1);
496 fflush(stderr);
497 #ifdef FORBID_LATIN1_CTRL
498 wpng_info.have_text &= ~TEXT_AUTHOR;
499 valid = FALSE;
500 #else
501 if (p[result] == 27) { /* escape character */
502 wpng_info.have_text &= ~TEXT_AUTHOR;
503 valid = FALSE;
504 }
505 #endif
506 }
507 }
508 } while (!valid);
509
510 do {
511 valid = TRUE;
512 p = textbuf + TEXT_DESC_OFFSET;
513 fprintf(stderr, " Description (up to 9 lines):\n");
514 for (i = 1; i < 10; ++i) {
515 fprintf(stderr, " [%d] ", i);
516 fflush(stderr);
517 if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1)
518 p += len; /* now points at NULL; char before is newline */
519 else
520 break;
521 }
522 if ((len = p - (textbuf + TEXT_DESC_OFFSET)) > 1) {
523 if (p[-1] == '\n') {
524 p[-1] = '\0';
525 --len;
526 }
527 wpng_info.desc = textbuf + TEXT_DESC_OFFSET;
528 wpng_info.have_text |= TEXT_DESC;
529 p = textbuf + TEXT_DESC_OFFSET;
530 if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
531 fprintf(stderr, " " PROGNAME " warning: character code"
532 " %u is %sdiscouraged by the PNG\n specification "
533 "[first occurrence was at character position #%d]\n",
534 (unsigned)p[result], (p[result] == 27)? "strongly " : "",
535 result+1);
536 fflush(stderr);
537 #ifdef FORBID_LATIN1_CTRL
538 wpng_info.have_text &= ~TEXT_DESC;
539 valid = FALSE;
540 #else
541 if (p[result] == 27) { /* escape character */
542 wpng_info.have_text &= ~TEXT_DESC;
543 valid = FALSE;
544 }
545 #endif
546 }
547 }
548 } while (!valid);
549
550 do {
551 valid = TRUE;
552 p = textbuf + TEXT_COPY_OFFSET;
553 fprintf(stderr, " Copyright: ");
554 fflush(stderr);
555 if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
556 if (p[len-1] == '\n')
557 p[--len] = '\0';
558 wpng_info.copyright = p;
559 wpng_info.have_text |= TEXT_COPY;
560 if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
561 fprintf(stderr, " " PROGNAME " warning: character code"
562 " %u is %sdiscouraged by the PNG\n specification "
563 "[first occurrence was at character position #%d]\n",
564 (unsigned)p[result], (p[result] == 27)? "strongly " : "",
565 result+1);
566 fflush(stderr);
567 #ifdef FORBID_LATIN1_CTRL
568 wpng_info.have_text &= ~TEXT_COPY;
569 valid = FALSE;
570 #else
571 if (p[result] == 27) { /* escape character */
572 wpng_info.have_text &= ~TEXT_COPY;
573 valid = FALSE;
574 }
575 #endif
576 }
577 }
578 } while (!valid);
579
580 do {
581 valid = TRUE;
582 p = textbuf + TEXT_EMAIL_OFFSET;
583 fprintf(stderr, " E-mail: ");
584 fflush(stderr);
585 if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
586 if (p[len-1] == '\n')
587 p[--len] = '\0';
588 wpng_info.email = p;
589 wpng_info.have_text |= TEXT_EMAIL;
590 if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
591 fprintf(stderr, " " PROGNAME " warning: character code"
592 " %u is %sdiscouraged by the PNG\n specification "
593 "[first occurrence was at character position #%d]\n",
594 (unsigned)p[result], (p[result] == 27)? "strongly " : "",
595 result+1);
596 fflush(stderr);
597 #ifdef FORBID_LATIN1_CTRL
598 wpng_info.have_text &= ~TEXT_EMAIL;
599 valid = FALSE;
600 #else
601 if (p[result] == 27) { /* escape character */
602 wpng_info.have_text &= ~TEXT_EMAIL;
603 valid = FALSE;
604 }
605 #endif
606 }
607 }
608 } while (!valid);
609
610 do {
611 valid = TRUE;
612 p = textbuf + TEXT_URL_OFFSET;
613 fprintf(stderr, " URL: ");
614 fflush(stderr);
615 if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) {
616 if (p[len-1] == '\n')
617 p[--len] = '\0';
618 wpng_info.url = p;
619 wpng_info.have_text |= TEXT_URL;
620 if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) {
621 fprintf(stderr, " " PROGNAME " warning: character code"
622 " %u is %sdiscouraged by the PNG\n specification "
623 "[first occurrence was at character position #%d]\n",
624 (unsigned)p[result], (p[result] == 27)? "strongly " : "",
625 result+1);
626 fflush(stderr);
627 #ifdef FORBID_LATIN1_CTRL
628 wpng_info.have_text &= ~TEXT_URL;
629 valid = FALSE;
630 #else
631 if (p[result] == 27) { /* escape character */
632 wpng_info.have_text &= ~TEXT_URL;
633 valid = FALSE;
634 }
635 #endif
636 }
637 }
638 } while (!valid);
639
640 #ifndef DOS_OS2_W32
641 fclose(keybd);
642 #endif
643
644 } else if (text) {
645 fprintf(stderr, PROGNAME ": unable to allocate memory for text\n");
646 text = FALSE;
647 wpng_info.have_text = 0;
648 }
649
650
651 /* allocate libpng stuff, initialize transformations, write pre-IDAT data */
652
653 if ((rc = writepng_init(&wpng_info)) != 0) {
654 switch (rc) {
655 case 2:
656 fprintf(stderr, PROGNAME
657 ": libpng initialization problem (longjmp)\n");
658 break;
659 case 4:
660 fprintf(stderr, PROGNAME ": insufficient memory\n");
661 break;
662 case 11:
663 fprintf(stderr, PROGNAME
664 ": internal logic error (unexpected PNM type)\n");
665 break;
666 default:
667 fprintf(stderr, PROGNAME
668 ": unknown writepng_init() error\n");
669 break;
670 }
671 exit(rc);
672 }
673
674
675 /* free textbuf, since it's a completely local variable and all text info
676 * has just been written to the PNG file */
677
678 if (text && textbuf) {
679 free(textbuf);
680 textbuf = NULL;
681 }
682
683
684 /* calculate rowbytes on basis of image type; note that this becomes much
685 * more complicated if we choose to support PBM type, ASCII PNM types, or
686 * 16-bit-per-sample binary data [currently not an official NetPBM type] */
687
688 if (wpng_info.pnmtype == 5)
689 rowbytes = wpng_info.width;
690 else if (wpng_info.pnmtype == 6)
691 rowbytes = wpng_info.width * 3;
692 else /* if (wpng_info.pnmtype == 8) */
693 rowbytes = wpng_info.width * 4;
694
695
696 /* read and write the image, either in its entirety (if writing interlaced
697 * PNG) or row by row (if non-interlaced) */
698
699 fprintf(stderr, "Encoding image data...\n");
700 fflush(stderr);
701
702 if (wpng_info.interlaced) {
703 long i;
704 ulg bytes;
705 ulg image_bytes = rowbytes * wpng_info.height; /* overflow? */
706
707 wpng_info.image_data = (uch *)malloc(image_bytes);
708 wpng_info.row_pointers = (uch **)malloc(wpng_info.height*sizeof(uch *));
709 if (wpng_info.image_data == NULL || wpng_info.row_pointers == NULL) {
710 fprintf(stderr, PROGNAME ": insufficient memory for image data\n");
711 writepng_cleanup(&wpng_info);
712 wpng_cleanup();
713 exit(5);
714 }
715 for (i = 0; i < wpng_info.height; ++i)
716 wpng_info.row_pointers[i] = wpng_info.image_data + i*rowbytes;
717 bytes = fread(wpng_info.image_data, 1, image_bytes, wpng_info.infile);
718 if (bytes != image_bytes) {
719 fprintf(stderr, PROGNAME ": expected %lu bytes, got %lu bytes\n",
720 image_bytes, bytes);
721 fprintf(stderr, " (continuing anyway)\n");
722 }
723 if (writepng_encode_image(&wpng_info) != 0) {
724 fprintf(stderr, PROGNAME
725 ": libpng problem (longjmp) while writing image data\n");
726 writepng_cleanup(&wpng_info);
727 wpng_cleanup();
728 exit(2);
729 }
730
731 } else /* not interlaced: write progressively (row by row) */ {
732 long j;
733 ulg bytes;
734
735 wpng_info.image_data = (uch *)malloc(rowbytes);
736 if (wpng_info.image_data == NULL) {
737 fprintf(stderr, PROGNAME ": insufficient memory for row data\n");
738 writepng_cleanup(&wpng_info);
739 wpng_cleanup();
740 exit(5);
741 }
742 error = 0;
743 for (j = wpng_info.height; j > 0L; --j) {
744 bytes = fread(wpng_info.image_data, 1, rowbytes, wpng_info.infile);
745 if (bytes != rowbytes) {
746 fprintf(stderr, PROGNAME
747 ": expected %lu bytes, got %lu bytes (row %ld)\n", rowbytes,
748 bytes, wpng_info.height-j);
749 ++error;
750 break;
751 }
752 if (writepng_encode_row(&wpng_info) != 0) {
753 fprintf(stderr, PROGNAME
754 ": libpng problem (longjmp) while writing row %ld\n",
755 wpng_info.height-j);
756 ++error;
757 break;
758 }
759 }
760 if (error) {
761 writepng_cleanup(&wpng_info);
762 wpng_cleanup();
763 exit(2);
764 }
765 if (writepng_encode_finish(&wpng_info) != 0) {
766 fprintf(stderr, PROGNAME ": error on final libpng call\n");
767 writepng_cleanup(&wpng_info);
768 wpng_cleanup();
769 exit(2);
770 }
771 }
772
773
774 /* OK, we're done (successfully): clean up all resources and quit */
775
776 fprintf(stderr, "Done.\n");
777 fflush(stderr);
778
779 writepng_cleanup(&wpng_info);
780 wpng_cleanup();
781
782 return 0;
783 }
784
785
786
787
788
wpng_isvalid_latin1(uch * p,int len)789 static int wpng_isvalid_latin1(uch *p, int len)
790 {
791 int i, result = -1;
792
793 for (i = 0; i < len; ++i) {
794 if (p[i] == 10 || (p[i] > 31 && p[i] < 127) || p[i] > 160)
795 continue; /* character is completely OK */
796 if (result < 0 || (p[result] != 27 && p[i] == 27))
797 result = i; /* mark location of first questionable one */
798 } /* or of first escape character (bad) */
799
800 return result;
801 }
802
803
804
805
806
wpng_cleanup(void)807 static void wpng_cleanup(void)
808 {
809 if (wpng_info.outfile) {
810 fclose(wpng_info.outfile);
811 wpng_info.outfile = NULL;
812 }
813
814 if (wpng_info.infile) {
815 fclose(wpng_info.infile);
816 wpng_info.infile = NULL;
817 }
818
819 if (wpng_info.image_data) {
820 free(wpng_info.image_data);
821 wpng_info.image_data = NULL;
822 }
823
824 if (wpng_info.row_pointers) {
825 free(wpng_info.row_pointers);
826 wpng_info.row_pointers = NULL;
827 }
828 }
829
830
831
832
833 #ifdef DOS_OS2_W32
834
dos_kbd_gets(char * buf,int len)835 static char *dos_kbd_gets(char *buf, int len)
836 {
837 int ch, count=0;
838
839 do {
840 buf[count++] = ch = getche();
841 } while (ch != '\r' && count < len-1);
842
843 buf[count--] = '\0'; /* terminate string */
844 if (buf[count] == '\r') /* Enter key makes CR, so change to newline */
845 buf[count] = '\n';
846
847 fprintf(stderr, "\n"); /* Enter key does *not* cause a newline */
848 fflush(stderr);
849
850 return buf;
851 }
852
853 #endif /* DOS_OS2_W32 */
854