1 /*
2  * Author:      Burkhard Neidecker-Lutz
3  *              Digital CEC Karlsruhe
4  *      neideck@nestvx.enet.dec.com
5 
6  Copyright (c) Digital Equipment Corporation, 1992
7 
8  Permission to use, copy, modify, and distribute this software and its
9  documentation for any purpose and without fee is hereby granted,
10  provided that the above copyright notice appear in all copies and that
11  both that copyright notice and this permission notice appear in
12  supporting documentation, and that the name of Digital Equipment
13  Corporation not be used in advertising or publicity pertaining to
14  distribution of the software without specific, written prior
15  permission. Digital Equipment Corporation makes no representations
16  about the suitability of this software for any purpose. It is provided
17  "as is" without express or implied warranty.
18 
19  Version: 1.0           30.07.92
20 
21 */
22 
23 #include <string.h>
24 
25 #include "mallocvar.h"
26 #include "pnm.h"
27 
28 /* The structure we use to convey all sorts of "magic" data to the DDIF */
29 /* header write procedure.                      */
30 
31 typedef struct {
32     int width;
33     int height;
34     int h_res;            /* Resolutions in dpi for bounding box */
35     int v_res;
36     int bits_per_pixel;
37     int bytes_per_line;
38     int spectral;         /* 2 == monochrome, 5 == rgb        */
39     int components;
40     int bits_per_component;
41     int polarity;         /* zeromin == 2 , zeromax == 1      */
42 } imageparams;
43 
44 
45 
46 /* ASN.1 basic encoding rules magic number */
47 #define UNIVERSAL 0
48 #define APPLICATION 1
49 #define CONTEXT 2
50 #define PRIVATE 3
51 
52 #define PRIM 0
53 #define CONS 1
54 
55 /* "tag": Emit an ASN tag of the specified class and tag number.    */
56 /* This is used in conjunction with the                  */
57 /* wr_xxx routines that follow to construct the various ASN.1 entities. */
58 /* Writing each entity is a two-step process, where first the tag is    */
59 /* written and then the length and value.               */
60 /* All of these routines take a pointer to a pointer into an output */
61 /* buffer in the first argument and update it accordingly.      */
62 
tag(unsigned char ** buffer,int cl,int constructed,unsigned int t)63 static void tag(unsigned char ** buffer, int cl, int constructed,
64                 unsigned int t)
65 {
66     int tag_first;
67     unsigned int stack[10];
68     int sp;
69     unsigned char *p = *buffer;
70 
71     tag_first = (cl << 6) | constructed << 5;
72     if (t < 31) {         /* Short tag form   */
73         *p++ = tag_first | t;
74     } else {          /* Long tag form */
75         *p++ = tag_first | 31;
76         sp = 0;
77         while (t > 0) {
78             stack[sp++] = t & 0x7f;
79             t >>= 7;
80         }
81         while (--sp > 0) {  /* Tag values with continuation bits */
82             *p++ = stack[sp] | 0x80;
83         }
84         *p++ = stack[0];    /* Last tag portion without continuation bit */
85     }
86     *buffer = p;      /* Update buffer pointer */
87 }
88 
89 
90 
91 /* Emit indefinite length encoding */
92 static void
ind(unsigned char ** buffer)93 ind(unsigned char **buffer)
94 {
95     unsigned char *p = *buffer;
96 
97     *p++ = 0x80;
98     *buffer = p;
99 }
100 
101 
102 
103 /* Emit ASN.1 NULL */
104 static void
wr_null(unsigned char ** buffer)105 wr_null(unsigned char **buffer)
106 {
107     unsigned char *p = *buffer;
108 
109     *p++ = 0;
110     *buffer = p;
111 }
112 
113 
114 
115 /* Emit ASN.1 length only into buffer, no data */
116 static void
wr_length(unsigned char ** buffer,int amount)117 wr_length(unsigned char ** buffer, int amount)
118 {
119     int length;
120     unsigned int mask;
121     unsigned char *p = *buffer;
122 
123     length = 4;
124     mask = 0xff000000;
125 
126     if (amount < 128) {
127         *p++ = amount;
128     } else {          /* > 127 */
129         while (!(amount & mask)) {  /* Search for first non-zero byte */
130             mask >>= 8;
131             --length;
132         }
133 
134         *p++ = length | 0x80;       /* Number of length bytes */
135 
136         while (--length >= 0) {     /* Put length bytes   */
137             *p++ =(amount >> (8*length)) & 0xff;
138         }
139 
140     }
141     *buffer = p;
142 }
143 
144 
145 
146 /* BER encode an integer and write it's length and value */
147 static void
wr_int(unsigned char ** buffer,int val)148 wr_int(unsigned char ** buffer, int val)
149 {
150     int length;
151     int sign;
152     unsigned int mask;
153     unsigned char *p = *buffer;
154 
155     if (val == 0) {
156         *p++ = 1;               /* length */
157         *p++ = 0;               /* value  */
158     } else {
159         sign = val < 0 ? 0xff : 0x00;   /* Sign bits */
160         length = 4;
161         mask  = 0xffu << 24;
162         while ((val & mask) == sign) {  /* Find the smallest representation */
163             length--;
164             mask >>= 8;
165         }
166         sign = (0x80 << ((length-1)*8)) & val; /* New sign bit */
167         if (((val < 0) && !sign) || ((val > 0) && sign)) { /* Sign error */
168             length++;
169         }
170         *p++ = length;          /* length */
171         while (--length >= 0) {
172             *p++ = (val >> (8*length)) & 0xff;
173         }
174     }
175     *buffer = p;
176 }
177 
178 
179 
180 /* Emit and End Of Coding sequence  */
181 static void
eoc(unsigned char ** buffer)182 eoc(unsigned char ** buffer)
183 {
184     unsigned char *p = *buffer;
185 
186     *p++ = 0;
187     *p++ = 0;
188     *buffer = p;
189 }
190 
191 
192 
193 /* Emit a simple string */
194 static
wr_string(unsigned char ** const buffer,const char * const val)195 void wr_string(unsigned char ** const buffer, const char * const val)
196 {
197     int length;
198     unsigned char *p = *buffer;
199 
200     length  = strlen(val);
201     if (length > 127) {
202         fprintf(stderr,"Can't encode length > 127 yet (%d)\n",length);
203         exit(1);
204     }
205     *p++ = length;
206     {
207         const char * valCursor;
208         for (valCursor = val; *valCursor; ++valCursor)
209             *p++ = *valCursor;
210     }
211     *buffer = p;
212 }
213 
214 
215 
216 /* Emit a ISOLATIN-1 string */
217 static void
emit_isolatin1(unsigned char ** const buffer,const char * const val)218 emit_isolatin1(unsigned char ** const buffer, const char * const val)
219 {
220     int length;
221     unsigned char *p = *buffer;
222 
223     length  = strlen(val) + 1;        /* One NULL byte and charset leader */
224     if (length > 127) {
225         fprintf(stderr,"Can't encode length > 127 yet (%d)\n",length);
226         exit(1);
227     }
228     *p++ = length;
229     *p++ = 1;             /* ISO LATIN-1 */
230     {
231         const char * valCursor;
232         for (valCursor = val; *valCursor; ++valCursor)
233             *p++ = *valCursor;
234     }
235     *buffer = p;
236 }
237 
238 
239 
240 /* Write the DDIF grammar onto "file" up to the actual starting location */
241 /* of the image data. The "ip" structure needs to be set to the right    */
242 /* values. A lot of the values here are hardcoded to be just right for   */
243 /* the bit grammars that the PBMPLUS formats want.           */
244 
245 static int
write_header(FILE * file,imageparams * ip)246 write_header(FILE *file, imageparams *ip)
247 {
248     unsigned char buffer[300];            /* Be careful with the size ! */
249     unsigned char *p = buffer;
250     int headersize;
251     int bounding_x;
252     int bounding_y;
253     int i;
254 
255     /* Calculate the bounding box from the resolutions    */
256     bounding_x = ((int) (1200 * ((double) (ip->width) / ip->h_res)));
257     bounding_y = ((int) (1200 * ((double) (ip->height) / ip->v_res)));
258 
259     /* This is gross. The entire DDIF grammar is constructed by   */
260     /* hand. The indentation is meant to indicate DDIF document structure */
261 
262     tag(&p,PRIVATE,CONS,16383); ind(&p);      /* DDIF Document */
263     tag(&p,CONTEXT,CONS, 0); ind(&p);        /* Document Descriptor */
264     tag(&p,CONTEXT,PRIM, 0); wr_int(&p,1);  /* Major Version */
265     tag(&p,CONTEXT,PRIM, 1); wr_int(&p,3);  /* Minor Version */
266     tag(&p,CONTEXT,PRIM, 2); wr_string(&p,"PBM+"); /* Product Identifier */
267     tag(&p,CONTEXT,CONS, 3); ind(&p);       /* Product Name */
268     tag(&p,PRIVATE,PRIM, 9); emit_isolatin1(&p,"PBMPLUS Writer V1.0");
269     eoc(&p);
270     eoc(&p);                 /* Document Descriptor */
271     tag(&p,CONTEXT,CONS, 1); ind(&p);        /* Document Header     */
272     tag(&p,CONTEXT,CONS, 3); ind(&p);       /* Version */
273     tag(&p,PRIVATE,PRIM, 9); emit_isolatin1(&p,"1.0");
274     eoc(&p);
275     eoc(&p);                 /* Document Header */
276     tag(&p,CONTEXT,CONS, 2); ind(&p);        /* Document Content    */
277     tag(&p,APPLICATION,CONS,2); ind(&p);    /* Segment Primitive    */
278     eoc(&p);
279     tag(&p,APPLICATION,CONS,2); ind(&p);    /* Segment  */
280     tag(&p,CONTEXT,CONS, 3); ind(&p);      /* Segment Specific Attributes */
281     tag(&p,CONTEXT,PRIM, 2); wr_string(&p,"$I");  /* Category */
282     tag(&p,CONTEXT,CONS,22); ind(&p);     /* Image Attributes */
283     tag(&p,CONTEXT,CONS, 0); ind(&p);    /* Image Presentation Attributes */
284     tag(&p,CONTEXT,PRIM, 1); wr_int(&p,0);  /* Pixel Path */
285     tag(&p,CONTEXT,PRIM, 2); wr_int(&p,270); /* Line Progression */
286     tag(&p,CONTEXT,CONS, 3); ind(&p);   /* Pixel Aspect Ratio */
287     tag(&p,CONTEXT,PRIM, 0); wr_int(&p,1); /* PP Pixel Dist */
288     tag(&p,CONTEXT,PRIM, 1); wr_int(&p,1); /* LP Pixel Dist */
289     eoc(&p);                /* Pixel Aspect Ratio */
290     tag(&p,CONTEXT,PRIM, 4); wr_int(&p,ip->polarity);
291         /* Brightness Polarity */
292     tag(&p,CONTEXT,PRIM, 5); wr_int(&p,1);  /* Grid Type    */
293     tag(&p,CONTEXT,PRIM, 7); wr_int(&p,ip->spectral);  /* Spectral Mapping */
294     tag(&p,CONTEXT,CONS,10); ind(&p);   /* Pixel Group Info */
295     tag(&p,CONTEXT,PRIM, 0); wr_int(&p,1); /* Pixel Group Size */
296     tag(&p,CONTEXT,PRIM, 1); wr_int(&p,1); /* Pixel Group Order */
297     eoc(&p);                /* Pixel Group Info */
298     eoc(&p);                     /* Image Presentation Attributes */
299     tag(&p,CONTEXT,CONS, 1); ind(&p);    /* Component Space Attributes */
300     tag(&p,CONTEXT,PRIM, 0); wr_int(&p,1);  /* Component Space Organization */
301     tag(&p,CONTEXT,PRIM, 1); wr_int(&p,1);  /* Planes per Pixel */
302     tag(&p,CONTEXT,PRIM, 2); wr_int(&p,1);  /* Plane Significance   */
303     tag(&p,CONTEXT,PRIM, 3); wr_int(&p,ip->components);
304         /* Number of Components    */
305     tag(&p,CONTEXT,CONS, 4); ind(&p);   /* Bits per Component   */
306     for (i = 0; i < ip->components; i++) {
307         tag(&p,UNIVERSAL,PRIM,2); wr_int(&p,ip->bits_per_component);
308     }
309     eoc(&p);                /* Bits per Component   */
310     tag(&p,CONTEXT,CONS, 5); ind(&p);   /* Component Quantization Levels */
311     for (i = 0; i < ip->components; i++) {
312         tag(&p,UNIVERSAL,PRIM,2); wr_int(&p,1 << ip->bits_per_component);
313     }
314     eoc(&p);                /* Component Quantization Levels */
315     eoc(&p);                 /* Component Space Attributes */
316     eoc(&p);                  /* Image Attributes */
317     tag(&p,CONTEXT,CONS,23); ind(&p);     /* Frame Parameters */
318     tag(&p,CONTEXT,CONS, 1); ind(&p);    /* Bounding Box */
319     tag(&p,CONTEXT,CONS, 0); ind(&p);   /* lower-left   */
320     tag(&p,CONTEXT,CONS, 0); ind(&p);  /* XCoordinate  */
321     tag(&p,CONTEXT,PRIM, 0); wr_int(&p,0);
322     eoc(&p);                           /* XCoordinate  */
323     tag(&p,CONTEXT,CONS, 1); ind(&p);  /* YCoordinate  */
324     tag(&p,CONTEXT,PRIM, 0); wr_int(&p,0);
325     eoc(&p);                               /* YCoordinate  */
326     eoc(&p);                /* lower left */
327     tag(&p,CONTEXT,CONS, 1); ind(&p);       /* upper right */
328     tag(&p,CONTEXT,CONS, 0); ind(&p);      /* XCoordinate  */
329     tag(&p,CONTEXT,PRIM, 0); wr_int(&p,bounding_x);
330     eoc(&p);               /* XCoordinate  */
331     tag(&p,CONTEXT,CONS, 1); ind(&p);      /* YCoordinate  */
332     tag(&p,CONTEXT,PRIM, 0); wr_int(&p,bounding_y);
333     eoc(&p);                   /* YCoordinate  */
334     eoc(&p);                            /* upper right */
335     eoc(&p);                 /* Bounding Box */
336     tag(&p,CONTEXT,CONS, 4); ind(&p);    /* Frame Position */
337     tag(&p,CONTEXT,CONS, 0); ind(&p);   /* XCoordinate  */
338     tag(&p,CONTEXT,PRIM, 0); wr_int(&p,0);
339     eoc(&p);                /* XCoordinate  */
340     tag(&p,CONTEXT,CONS, 1); ind(&p);   /* YCoordinate  */
341     tag(&p,CONTEXT,PRIM, 0); wr_int(&p,0);
342     eoc(&p);                    /* YCoordinate  */
343     eoc(&p);                 /* Frame Position */
344     eoc(&p);                  /* Frame Parameters */
345     eoc(&p);                        /* Segment Specific Attributes */
346     eoc(&p);                    /* Segment */
347     tag(&p,APPLICATION,CONS,17); ind(&p);   /* Image Data Descriptor */
348     tag(&p,UNIVERSAL,CONS,16); ind(&p);    /* Sequence */
349     tag(&p,CONTEXT,CONS, 0); ind(&p);     /* Image Coding Attributes */
350     tag(&p,CONTEXT,PRIM, 1); wr_int(&p,ip->width); /* Pixels per Line    */
351     tag(&p,CONTEXT,PRIM, 2); wr_int(&p,ip->height);  /* Number of Lines  */
352     tag(&p,CONTEXT,PRIM, 3); wr_int(&p,2);   /* Compression Type */
353     tag(&p,CONTEXT,PRIM, 5); wr_int(&p,0);   /* Data Offset  */
354     tag(&p,CONTEXT,PRIM, 6); wr_int(&p,ip->bits_per_pixel);  /* Pixel Stride */
355     tag(&p,CONTEXT,PRIM, 7); wr_int(&p,ip->bytes_per_line * 8);
356         /* Scanline Stride    */
357     tag(&p,CONTEXT,PRIM, 8); wr_int(&p,1);   /* Bit Order        */
358     tag(&p,CONTEXT,PRIM, 9); wr_int(&p,ip->bits_per_pixel);
359         /* Planebits per Pixel */
360     tag(&p,CONTEXT,CONS,10); ind(&p);    /* Byteorder Info   */
361     tag(&p,CONTEXT,PRIM, 0); wr_int(&p,1);  /* Byte Unit        */
362     tag(&p,CONTEXT,PRIM, 1); wr_int(&p,1);  /* Byte Order   */
363     eoc(&p);                 /* Byteorder Info   */
364     tag(&p,CONTEXT,PRIM,11); wr_int(&p,3);   /* Data Type        */
365     eoc(&p);                              /* Image Coding Attributes */
366     tag(&p,CONTEXT,PRIM, 1); wr_length(&p,ip->bytes_per_line*ip->height);
367         /* Component Plane Data */
368     /* End of DDIF document Indentation */
369     headersize = p - buffer;
370     if (headersize >= 300)  {
371         fprintf(stderr,"Overran buffer area %d >= 300\n",headersize);
372         exit(1);
373     }
374 
375     return (fwrite(buffer, 1, headersize, file) == headersize);
376 }
377 
378 
379 
380 /* Write all the closing brackets of the DDIF grammar that are missing */
381 /* The strange indentation reflects exactly the same indentation that  */
382 /* we left off in the write_header procedure.                  */
383 static int
write_trailer(FILE * file)384 write_trailer(FILE * file)
385 {
386     unsigned char buffer[30];
387     unsigned char *p = buffer;
388     int trailersize;
389 
390     /* Indentation below gives DDIF document structure */
391     eoc(&p);                        /* Sequence */
392     eoc(&p);                     /* Image Data Descriptor */
393     tag(&p,APPLICATION,PRIM,1); wr_null(&p);     /* End Segment */
394     tag(&p,APPLICATION,PRIM,1); wr_null(&p);     /* End Segment */
395     eoc(&p);                  /* Document Content */
396     eoc(&p);                   /* DDIF Document */
397     /* End of DDIF document Indentation */
398     trailersize = p - buffer;
399     if (trailersize >= 30)  {
400         fprintf(stderr,"Overran buffer area %d >= 30\n",trailersize);
401         exit(1);
402     }
403 
404     return(fwrite(buffer, 1, trailersize, file) == trailersize);
405 }
406 
407 
408 
409 
410 static void
convertPbmRaster(FILE * const ifP,int const format,unsigned int const cols,unsigned int const rows,FILE * const ofP,unsigned int const bytesPerLine,unsigned char * const data)411 convertPbmRaster(FILE *          const ifP,
412                  int             const format,
413                  unsigned int    const cols,
414                  unsigned int    const rows,
415                  FILE *          const ofP,
416                  unsigned int    const bytesPerLine,
417                  unsigned char * const data) {
418 
419     bit * const pixels = pbm_allocrow(cols);
420 
421     unsigned int row;
422 
423     for (row = 0; row < rows; ++row) {
424         unsigned int col;
425         unsigned int k;
426         unsigned int mask;
427         unsigned char * p;
428         size_t bytesWritten;
429 
430         pbm_readpbmrow(ifP, pixels, cols, format);
431 
432         mask = 0x00;
433         p = &data[0];
434         for (col = 0, k = 0; col < cols; ++col) {
435             if (pixels[col] == PBM_BLACK)
436                 mask |= 1 << k;
437             if (k == 7) {
438                 *p++ = mask;
439                 mask = 0x00;
440                 k = 0;
441             } else
442                 ++k;
443         }
444         if (k != 7)
445             /* Flush the rest of the column */
446             *p = mask;
447 
448         bytesWritten =  fwrite(data, 1, bytesPerLine, ofP);
449         if (bytesWritten != bytesPerLine)
450             pm_error("File write error on Row %u", row);
451     }
452 
453     pbm_freerow(pixels);
454 }
455 
456 
457 
458 static void
convertPgmRaster(FILE * const ifP,int const format,xelval const maxval,unsigned int const cols,unsigned int const rows,FILE * const ofP,unsigned int const bytesPerLine,unsigned char * const data)459 convertPgmRaster(FILE *          const ifP,
460                  int             const format,
461                  xelval          const maxval,
462                  unsigned int    const cols,
463                  unsigned int    const rows,
464                  FILE *          const ofP,
465                  unsigned int    const bytesPerLine,
466                  unsigned char * const data) {
467 
468     gray * const pixels = pgm_allocrow(cols);
469 
470     unsigned int row;
471 
472     for (row = 0; row < rows; ++row) {
473         unsigned char * p;
474         unsigned int col;
475         size_t bytesWritten;
476 
477         p = &data[0];
478 
479         pgm_readpgmrow(ifP, pixels, cols, maxval, format);
480 
481         for (col = 0; col < cols; ++col)
482             *p++ = (unsigned char) pixels[col];
483 
484         bytesWritten = fwrite(data, 1, bytesPerLine, ofP);
485         if (bytesWritten != bytesPerLine)
486             pm_error("File write error on Row %u", row);
487     }
488     pgm_freerow(pixels);
489 }
490 
491 
492 
493 
494 static void
convertPpmRaster(FILE * const ifP,int const format,xelval const maxval,unsigned int const cols,unsigned int const rows,FILE * const ofP,unsigned int const bytesPerLine,unsigned char * const data)495 convertPpmRaster(FILE *          const ifP,
496                  int             const format,
497                  xelval          const maxval,
498                  unsigned int    const cols,
499                  unsigned int    const rows,
500                  FILE *          const ofP,
501                  unsigned int    const bytesPerLine,
502                  unsigned char * const data) {
503 
504     pixel * const pixels = ppm_allocrow(cols);
505 
506     unsigned int row;
507 
508     for (row = 0; row < rows; ++row) {
509         unsigned char * p;
510         unsigned int col;
511         size_t bytesWritten;
512 
513         p = &data[0];
514 
515         ppm_readppmrow(ifP, pixels, cols, maxval, format);
516 
517         for (col = 0; col < cols; ++col) {
518             *p++ = PPM_GETR(pixels[col]);
519             *p++ = PPM_GETG(pixels[col]);
520             *p++ = PPM_GETB(pixels[col]);
521         }
522         bytesWritten =  fwrite(data, 1, bytesPerLine, ofP);
523         if (bytesWritten != bytesPerLine)
524             pm_error("File write error on Row %u", row);
525     }
526     ppm_freerow(pixels);
527 }
528 
529 
530 
531 static void
convertRaster(FILE * const ifP,int const format,xelval const maxval,unsigned int const cols,unsigned int const rows,FILE * const ofP,unsigned int const bytesPerLine)532 convertRaster(FILE *       const ifP,
533               int          const format,
534               xelval       const maxval,
535               unsigned int const cols,
536               unsigned int const rows,
537               FILE *       const ofP,
538               unsigned int const bytesPerLine) {
539 
540     unsigned char * data;
541 
542     MALLOCARRAY(data, bytesPerLine);
543 
544     if (data == NULL)
545         pm_error("Couldn't allocate %u-byte line buffer", bytesPerLine);
546 
547     switch (PNM_FORMAT_TYPE(format)) {
548     case PBM_TYPE:
549         convertPbmRaster(ifP, format, cols, rows, ofP, bytesPerLine, data);
550         break;
551     case PGM_TYPE:
552         convertPgmRaster(ifP, format, maxval, cols, rows, ofP, bytesPerLine,
553                          data);
554         break;
555     case PPM_TYPE:
556         convertPpmRaster(ifP, format, maxval, cols, rows, ofP, bytesPerLine,
557                          data);
558         break;
559     default:
560         pm_error("INTERNAL ERROR: impossible format value");
561     }
562 
563     free(data);
564 }
565 
566 
567 
568 int
main(int argc,char * argv[])569 main(int argc, char *argv[]) {
570     FILE           *ifd;
571     FILE           *ofd;
572     int             rows, cols;
573     xelval          maxval;
574     int             format;
575     const char     * const usage = "[-resolution x y] [pnmfile [ddiffile]]";
576     char           *outfile;
577     int       argn;
578     int hor_resolution = 75;
579     int ver_resolution = 75;
580     imageparams ip;
581 
582     pnm_init(&argc, argv);
583 
584     for (argn = 1;argn < argc && argv[argn][0] == '-';argn++) {
585         int arglen = strlen(argv[argn]);
586 
587         if (!strncmp (argv[argn],"-resolution", arglen)) {
588             if (argn + 2 < argc) {
589                 hor_resolution = atoi(argv[argn+1]);
590                 ver_resolution = atoi(argv[argn+2]);
591                 argn += 2;
592                 continue;
593             } else {
594                 pm_usage(usage);
595             }
596         } else {
597             pm_usage(usage);
598         }
599     }
600 
601     if (hor_resolution <= 0 || ver_resolution <= 0) {
602         fprintf(stderr,"Unreasonable resolution values: %d x %d\n",
603                 hor_resolution,ver_resolution);
604         exit(1);
605     }
606 
607     if (argn == argc - 2) {
608         ifd = pm_openr(argv[argn]);
609         outfile = argv[argn+1];
610         if (!(ofd = fopen(outfile,"wb"))) {
611             perror(outfile);
612             exit(1);
613         }
614     } else if (argn == argc - 1) {
615         ifd = pm_openr(argv[argn]);
616         ofd = stdout;
617     } else {
618         ifd = stdin;
619         ofd = stdout;
620     }
621 
622     pnm_readpnminit(ifd, &cols, &rows, &maxval, &format);
623 
624     ip.width = cols;
625     ip.height = rows;
626     ip.h_res = hor_resolution;
627     ip.v_res = ver_resolution;
628 
629     switch (PNM_FORMAT_TYPE(format)) {
630     case PBM_TYPE:
631         ip.bits_per_pixel = 1;
632         ip.bytes_per_line = (cols + 7) / 8;
633         ip.spectral = 2;
634         ip.components = 1;
635         ip.bits_per_component = 1;
636         ip.polarity = 1;
637         break;
638     case PGM_TYPE:
639         ip.bytes_per_line = cols;
640         ip.bits_per_pixel = 8;
641         ip.spectral = 2;
642         ip.components = 1;
643         ip.bits_per_component = 8;
644         ip.polarity = 2;
645         break;
646     case PPM_TYPE:
647         ip.bytes_per_line = 3 * cols;
648         ip.bits_per_pixel = 24;
649         ip.spectral = 5;
650         ip.components = 3;
651         ip.bits_per_component = 8;
652         ip.polarity = 2;
653         break;
654     default:
655         fprintf(stderr, "Unrecognized PBMPLUS format %d\n", format);
656         exit(1);
657     }
658 
659     if (!write_header(ofd,&ip)) {
660         perror("Writing header");
661         exit(1);
662     }
663 
664     convertRaster(ifd, format, maxval, cols, rows, ofd, ip.bytes_per_line);
665 
666     pm_close(ifd);
667 
668     if (!write_trailer(ofd)) {
669         perror("Writing trailer");
670         exit(1);
671     }
672 
673     if (fclose(ofd) == EOF) {
674         perror("Closing output file");
675         exit(1);
676     };
677 
678     return(0);
679 }
680