Index: rdjpgcom.1

--- rdjpgcom.1.orig	1997-10-11 15:46:48.000000000 -0700
+++ rdjpgcom.1	2003-04-01 14:39:47.000000000 -0800
@@ -7,6 +7,10 @@
 .B \-verbose
 ]
 [
+.B \-raw
+.I key
+]
+[
 .I filename
 ]
 .LP
@@ -29,6 +33,22 @@
 Causes
 .B rdjpgcom
 to also display the JPEG image dimensions.
+.TP
+.BI \-raw " key"
+Causes a single comment selected by
+.I key
+to be display in "raw" (byte-for-byte) format.
+Valid keys are
+.B COM
+and
+.BI APP n
+where
+.I n
+is a number from 0 to 15.
+The key is not case-sensitive.
+Many digital cameras store interesting information in the
+.B APP1
+key.
 .PP
 Switch names may be abbreviated, and are not case sensitive.
 .SH HINTS
@@ -43,8 +63,13 @@
 .B rdjpgcom
 will also attempt to print the contents of any "APP12" markers as text.
 Some digital cameras produce APP12 markers containing useful textual
-information.  If you like, you can modify the source code to print
-other APPn marker types as well.
+information.
+.PP
+If the
+.B \-verbose
+switch is doubled,
+.B rdjpegcom
+will print all "APPn" markers, not just APP12.
 .SH SEE ALSO
 .BR cjpeg (1),
 .BR djpeg (1),

Index: rdjpgcom.c

--- rdjpgcom.c.orig	1997-10-11 15:41:04.000000000 -0700
+++ rdjpgcom.c	2003-04-19 03:21:02.000000000 -0700
@@ -119,8 +119,22 @@
 #define M_SOI   0xD8		/* Start Of Image (beginning of datastream) */
 #define M_EOI   0xD9		/* End Of Image (end of datastream) */
 #define M_SOS   0xDA		/* Start Of Scan (begins compressed data) */
-#define M_APP0	0xE0		/* Application-specific marker, type N */
-#define M_APP12	0xEC		/* (we don't bother to list all 16 APPn's) */
+#define M_APP0	0xE0		/* Application-specific marker, type 0 */
+#define M_APP1	0xE1		/* Application-specific marker, type 1 */
+#define M_APP2	0xE2		/* Application-specific marker, type 2 */
+#define M_APP3	0xE3		/* Application-specific marker, type 3 */
+#define M_APP4	0xE4		/* Application-specific marker, type 4 */
+#define M_APP5	0xE5		/* Application-specific marker, type 5 */
+#define M_APP6	0xE6		/* Application-specific marker, type 6 */
+#define M_APP7	0xE7		/* Application-specific marker, type 7 */
+#define M_APP8	0xE8		/* Application-specific marker, type 8 */
+#define M_APP9	0xE9		/* Application-specific marker, type 9 */
+#define M_APP10	0xEA		/* Application-specific marker, type 10 */
+#define M_APP11	0xEB		/* Application-specific marker, type 11 */
+#define M_APP12	0xEC		/* Application-specific marker, type 12 */
+#define M_APP13	0xED		/* Application-specific marker, type 13 */
+#define M_APP14	0xEE		/* Application-specific marker, type 14 */
+#define M_APP15	0xEF		/* Application-specific marker, type 15 */
 #define M_COM   0xFE		/* COMment */
 
 
@@ -215,10 +229,13 @@
  * Process a COM marker.
  * We want to print out the marker contents as legible text;
  * we must guard against non-text junk and varying newline representations.
+ *
+ * If raw is nonzero, we don't have to do all that work, because we're
+ * just printing one header in raw mode.
  */
 
 static void
-process_COM (void)
+process_COM (int raw)
 {
   unsigned int length;
   int ch;
@@ -233,12 +250,14 @@
 
   while (length > 0) {
     ch = read_1_byte();
-    /* Emit the character in a readable form.
+    /* Emit the character in a readable form (unless raw).
      * Nonprintables are converted to \nnn form,
      * while \ is converted to \\.
      * Newlines in CR, CR/LF, or LF form will be printed as one newline.
      */
-    if (ch == '\r') {
+    if (raw) {
+      putchar(ch);
+    } else if (ch == '\r') {
       printf("\n");
     } else if (ch == '\n') {
       if (lastch != '\r')
@@ -253,7 +272,8 @@
     lastch = ch;
     length--;
   }
-  printf("\n");
+  if (!raw)
+    printf("\n");
 }
 
 
@@ -263,7 +283,7 @@
  */
 
 static void
-process_SOFn (int marker)
+process_SOFn (int marker, int reallyprint)
 {
   unsigned int length;
   unsigned int image_height, image_width;
@@ -295,9 +315,11 @@
   default:	process = "Unknown";  break;
   }
 
-  printf("JPEG image is %uw * %uh, %d color components, %d bits per sample\n",
-	 image_width, image_height, num_components, data_precision);
-  printf("JPEG process: %s\n", process);
+  if (reallyprint) {
+    printf("JPEG image is %uw * %uh, %d color components, %d bits per sample\n",
+	   image_width, image_height, num_components, data_precision);
+    printf("JPEG process: %s\n", process);
+  }
 
   if (length != (unsigned int) (8 + num_components * 3))
     ERREXIT("Bogus SOF marker length");
@@ -321,7 +343,7 @@
  */
 
 static int
-scan_JPEG_header (int verbose)
+scan_JPEG_header (int verbose, int raw)
 {
   int marker;
 
@@ -350,7 +372,7 @@
     case M_SOF14:		/* Differential progressive, arithmetic */
     case M_SOF15:		/* Differential lossless, arithmetic */
       if (verbose)
-	process_SOFn(marker);
+	process_SOFn(marker, !raw);
       else
 	skip_variable();
       break;
@@ -362,16 +384,36 @@
       return marker;
 
     case M_COM:
-      process_COM();
+      if (raw == 0 || raw == marker)
+	process_COM( raw);
+      else
+	skip_variable();
       break;
 
-    case M_APP12:
+    case M_APP0:
+    case M_APP1:
+    case M_APP2:
+    case M_APP3:
+    case M_APP4:
+    case M_APP5:
+    case M_APP6:
+    case M_APP7:
+    case M_APP8:
+    case M_APP9:
+    case M_APP10:
+    case M_APP11:
+    case M_APP13:
+    case M_APP14:
+    case M_APP15:
       /* Some digital camera makers put useful textual information into
        * APP12 markers, so we print those out too when in -verbose mode.
+       * In double-verbose mode, we print all APP markers.
        */
-      if (verbose) {
-	printf("APP12 contains:\n");
-	process_COM();
+      if (verbose > 1 || (verbose && marker == M_APP12)
+	  || (raw != 0 && raw == marker)) {
+	if (!raw)
+	  printf("APP%d contains:\n", marker & 0xF);
+	process_COM(raw);
       } else
 	skip_variable();
       break;
@@ -399,6 +441,9 @@
 
   fprintf(stderr, "Switches (names may be abbreviated):\n");
   fprintf(stderr, "  -verbose    Also display dimensions of JPEG image\n");
+  fprintf(stderr, "  -verbose -verbose to show all APP comments\n");
+  fprintf(stderr, "  -raw x      Display only raw comment of type x\n");
+  fprintf(stderr, "    Valid types are COM or APPn where n is 0 to 15\n");
 
   exit(EXIT_FAILURE);
 }
@@ -438,6 +483,7 @@
 {
   int argn;
   char * arg;
+  int raw = 0;
   int verbose = 0;
 
   /* On Mac, fetch a command line. */
@@ -457,6 +503,45 @@
     arg++;			/* advance over '-' */
     if (keymatch(arg, "verbose", 1)) {
       verbose++;
+    } else if (keymatch(arg, "raw", 1) && argn < argc + 1) {
+      argn++;
+      arg = argv[argn];
+      if (keymatch(arg, "com", 1)) {
+	raw = M_COM;
+      } else if (keymatch(arg, "app15", 5)) {
+	raw = M_APP15;
+      } else if (keymatch(arg, "app14", 5)) {
+	raw = M_APP14;
+      } else if (keymatch(arg, "app13", 5)) {
+	raw = M_APP13;
+      } else if (keymatch(arg, "app12", 5)) {
+	raw = M_APP12;
+      } else if (keymatch(arg, "app11", 5)) {
+	raw = M_APP11;
+      } else if (keymatch(arg, "app10", 5)) {
+	raw = M_APP10;
+      } else if (keymatch(arg, "app9", 4)) {
+	raw = M_APP9;
+      } else if (keymatch(arg, "app8", 4)) {
+	raw = M_APP8;
+      } else if (keymatch(arg, "app7", 4)) {
+	raw = M_APP7;
+      } else if (keymatch(arg, "app6", 4)) {
+	raw = M_APP6;
+      } else if (keymatch(arg, "app5", 4)) {
+	raw = M_APP5;
+      } else if (keymatch(arg, "app4", 4)) {
+	raw = M_APP4;
+      } else if (keymatch(arg, "app3", 4)) {
+	raw = M_APP3;
+      } else if (keymatch(arg, "app2", 4)) {
+	raw = M_APP2;
+      } else if (keymatch(arg, "app1", 4)) {
+	raw = M_APP1;
+      } else if (keymatch(arg, "app0", 4)) {
+	raw = M_APP0;
+      } else
+	usage();
     } else
       usage();
   }
@@ -488,7 +573,7 @@
   }
 
   /* Scan the JPEG headers. */
-  (void) scan_JPEG_header(verbose);
+  (void) scan_JPEG_header(verbose, raw);
 
   /* All done. */
   exit(EXIT_SUCCESS);

Index: wrjpgcom.1

--- wrjpgcom.1.orig	1995-06-15 17:14:05.000000000 -0700
+++ wrjpgcom.1	2003-04-01 14:43:57.000000000 -0800
@@ -13,6 +13,9 @@
 .BI \-cfile " name"
 ]
 [
+.BI \-app "n name"
+]
+[
 .I filename
 ]
 .LP
@@ -46,6 +49,12 @@
 .TP
 .BI \-cfile " name"
 Read text for new COM block from named file.
+.TP
+.BI \-app "n name"
+Read an application-specific comment from named file.
+The value
+.I n
+must be between 0 and 15.
 .PP
 If you have only one line of comment text to add, you can provide it on the
 command line with
@@ -53,10 +62,20 @@
 The comment text must be surrounded with quotes so that it is treated as a
 single argument.  Longer comments can be read from a text file.
 .PP
-If you give neither
-.B \-comment
-nor
+You can also replace an application-specific comment using the
+.BI \-app n
+switch.
+Since these comments usually have binary components, the contents will
+be read from the named file.
+Usually, this file will have been extracted from another image using
+.BR rdjpgcom (1).
+If the file does not have that marker, it will be inserted.
+.PP
+If you give none of
+.BR \-comment ,
 .BR \-cfile ,
+or
+.BR \-app\fIn\fP ,
 then
 .B wrjpgcom
 will read the comment text from standard input.  (In this case an input image
@@ -94,6 +113,19 @@
 .I in.jpg
 .B >
 .I out.jpg
+.PP
+Copy the Exif tags from an image to a processed version:
+.IP
+.B rdjpgcom -raw app1
+.I orig.jpg
+.B >
+.I comment.app1
+.IP
+.B wrjpgcom -app1
+.I comment.app1
+.I changed.jpg
+.B >
+.I final.jpg
 .SH SEE ALSO
 .BR cjpeg (1),
 .BR djpeg (1),

Index: wrjpgcom.c

--- wrjpgcom.c.orig	1997-10-22 21:47:03.000000000 -0700
+++ wrjpgcom.c	2003-04-01 14:26:03.000000000 -0800
@@ -170,6 +170,22 @@
 #define M_SOI   0xD8		/* Start Of Image (beginning of datastream) */
 #define M_EOI   0xD9		/* End Of Image (end of datastream) */
 #define M_SOS   0xDA		/* Start Of Scan (begins compressed data) */
+#define M_APP0	0xE0		/* Application-specific marker, type 0 */
+#define M_APP1	0xE1		/* Application-specific marker, type 1 */
+#define M_APP2	0xE2		/* Application-specific marker, type 2 */
+#define M_APP3	0xE3		/* Application-specific marker, type 3 */
+#define M_APP4	0xE4		/* Application-specific marker, type 4 */
+#define M_APP5	0xE5		/* Application-specific marker, type 5 */
+#define M_APP6	0xE6		/* Application-specific marker, type 6 */
+#define M_APP7	0xE7		/* Application-specific marker, type 7 */
+#define M_APP8	0xE8		/* Application-specific marker, type 8 */
+#define M_APP9	0xE9		/* Application-specific marker, type 9 */
+#define M_APP10	0xEA		/* Application-specific marker, type 10 */
+#define M_APP11	0xEB		/* Application-specific marker, type 11 */
+#define M_APP12	0xEC		/* Application-specific marker, type 12 */
+#define M_APP13	0xED		/* Application-specific marker, type 13 */
+#define M_APP14	0xEE		/* Application-specific marker, type 14 */
+#define M_APP15	0xEF		/* Application-specific marker, type 15 */
 #define M_COM   0xFE		/* COMment */
 
 
@@ -286,7 +302,7 @@
  */
 
 static int
-scan_JPEG_header (int keep_COM)
+scan_JPEG_header (int keep_COM, int app_marker)
 {
   int marker;
 
@@ -333,6 +349,30 @@
       }
       break;
 
+    case M_APP0:
+    case M_APP1:
+    case M_APP2:
+    case M_APP3:
+    case M_APP4:
+    case M_APP5:
+    case M_APP6:
+    case M_APP7:
+    case M_APP8:
+    case M_APP9:
+    case M_APP10:
+    case M_APP11:
+    case M_APP12:
+    case M_APP13:
+    case M_APP14:
+    case M_APP15:
+      if (marker == app_marker) {
+	skip_variable();
+      } else {
+	write_marker(marker);
+	copy_variable();
+      }
+      break;
+
     default:			/* Anything else just gets copied */
       write_marker(marker);
       copy_variable();		/* we assume it has a parameter count... */
@@ -365,9 +405,11 @@
   fprintf(stderr, "  -replace         Delete any existing comments\n");
   fprintf(stderr, "  -comment \"text\"  Insert comment with given text\n");
   fprintf(stderr, "  -cfile name      Read comment from named file\n");
+  fprintf(stderr, "  -appN name       Read appN comment from named file\n");
+  fprintf(stderr, "                   N is 0 to 15\n");
   fprintf(stderr, "Notice that you must put quotes around the comment text\n");
   fprintf(stderr, "when you use -comment.\n");
-  fprintf(stderr, "If you do not give either -comment or -cfile on the command line,\n");
+  fprintf(stderr, "If you do not give any of -comment, -cfile, or -appN on the command line,\n");
   fprintf(stderr, "then the comment text is read from standard input.\n");
   fprintf(stderr, "It can be multiple lines, up to %u characters total.\n",
 	  (unsigned int) MAX_COM_LENGTH);
@@ -415,6 +457,10 @@
   int argn;
   char * arg;
   int keep_COM = 1;
+  int app_marker = 0;
+  char * app_arg = NULL;
+  FILE * app_file = NULL;
+  unsigned int app_length = 0;
   char * comment_arg = NULL;
   FILE * comment_file = NULL;
   unsigned int comment_length = 0;
@@ -467,6 +513,13 @@
 	}
       }
       comment_length = (unsigned int) strlen(comment_arg);
+    } else if (strncmp(arg, "app", 3) == 0 || strncmp(arg, "APP", 3) == 0) {
+      app_marker = M_APP0 + atoi(arg + 3);
+      if (++argn >= argc) usage();
+      if ((app_file = fopen(argv[argn], "r")) == NULL) {
+	fprintf(stderr, "%s: can't open %s\n", progname, argv[argn]);
+	exit(EXIT_FAILURE);
+      }
     } else
       usage();
   }
@@ -477,7 +530,8 @@
   /* If there is neither -comment nor -cfile, we will read the comment text
    * from stdin; in this case there MUST be an input JPEG file name.
    */
-  if (comment_arg == NULL && comment_file == NULL && argn >= argc)
+  if (comment_arg == NULL && comment_file == NULL && app_file == NULL
+	&& argn >= argc)
     usage();
 
   /* Open the input file. */
@@ -534,7 +588,7 @@
 #endif /* TWO_FILE_COMMANDLINE */
 
   /* Collect comment text from comment_file or stdin, if necessary */
-  if (comment_arg == NULL) {
+  if (comment_arg == NULL && app_file == NULL) {
     FILE * src_file;
     int c;
 
@@ -555,13 +609,44 @@
       fclose(comment_file);
   }
 
+  /* Collect application text from app_file, if necessary */
+  if (app_file != NULL) {
+    int ch;
+
+    app_arg = (char *) malloc((size_t) MAX_COM_LENGTH);
+    if (app_arg == NULL)
+      ERREXIT("Insufficient memory");
+    app_length = 0;
+    while ((ch = getc(app_file)) != EOF) {
+      if (app_length >= (unsigned int) MAX_COM_LENGTH) {
+	fprintf(stderr,
+		"Application-specif comment text may not exceed %u bytes\n",
+		(unsigned int) MAX_COM_LENGTH);
+	exit(EXIT_FAILURE);
+      }
+      app_arg[app_length++] = (char) ch;
+    }
+    fclose(app_file);
+  }
+
   /* Copy JPEG headers until SOFn marker;
    * we will insert the new comment marker just before SOFn.
    * This (a) causes the new comment to appear after, rather than before,
    * existing comments; and (b) ensures that comments come after any JFIF
    * or JFXX markers, as required by the JFIF specification.
    */
-  marker = scan_JPEG_header(keep_COM);
+  marker = scan_JPEG_header(keep_COM, app_marker);
+
+  /* Insert the new application marker, if any */
+  if (app_length != 0) {
+    write_marker(app_marker);
+    write_2_bytes(app_length + 2);
+    while (app_length > 0) {
+      write_1_byte(*app_arg++);
+      app_length--;
+    }
+  }
+
   /* Insert the new COM marker, but only if nonempty text has been supplied */
   if (comment_length > 0) {
     write_marker(M_COM);
@@ -571,6 +656,7 @@
       comment_length--;
     }
   }
+
   /* Duplicate the remainder of the source file.
    * Note that any COM markers occuring after SOF will not be touched.
    */
