wpng.c 31 KB

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