- read/write i_xres, i_yres, i_aspect only tage with JPEG files,
authorTony Cook <tony@develop=help.com>
Wed, 7 Dec 2005 13:06:21 +0000 (13:06 +0000)
committerTony Cook <tony@develop=help.com>
Wed, 7 Dec 2005 13:06:21 +0000 (13:06 +0000)
  and read/write jpeg_density_unit (+_name) tag
- save the jpeg_comment tag when writing

Changes
imexif.c
jpeg.c
lib/Imager/Files.pod
t/t101jpeg.t

diff --git a/Changes b/Changes
index b4916b4..b23f702 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1208,6 +1208,9 @@ Revision history for Perl extension Imager.
 - add smoke test for nearest_color filter
 - added integer overflow checks to many memory allocation calls
 - added experimental EXIF decoding when reading JPEG files.
+- read/write i_xres, i_yres, i_aspect only tage with JPEG files,
+  and read/write jpeg_density_unit (+_name) tag
+- save the jpeg_comment tag when writing
 
 =================================================================
 
index 8d94986..814de14 100644 (file)
--- a/imexif.c
+++ b/imexif.c
@@ -1054,7 +1054,7 @@ copy_string_tags(i_img *im, imtiff *tiff, tag_map *map, int map_count) {
       if (map[i].tag == entry->tag) {
        int len = entry->type == ift_ascii ? entry->size - 1 : entry->size;
        i_tags_add(&im->tags, map[i].name, 0,
-                  tiff->base + entry->offset, len, 0);
+                  (char const *)(tiff->base + entry->offset), len, 0);
        break;
       }
     }
diff --git a/jpeg.c b/jpeg.c
index d6c7f03..1ba75ac 100644 (file)
--- a/jpeg.c
+++ b/jpeg.c
@@ -415,6 +415,31 @@ i_readjpeg_wiol(io_glue *data, int length, char** iptc_itext, int *itlength) {
     markerp = markerp->next;
   }
 
+  if (cinfo.saw_JFIF_marker) {
+    double xres = cinfo.X_density;
+    double yres = cinfo.Y_density;
+    
+    i_tags_addn(&im->tags, "jpeg_density_unit", 0, cinfo.density_unit);
+    switch (cinfo.density_unit) {
+    case 0: /* values are just the aspect ratio */
+      i_tags_addn(&im->tags, "i_aspect_only", 0, 1);
+      i_tags_add(&im->tags, "jpeg_density_unit_name", 0, "none", -1, 0);
+      break;
+
+    case 1: /* per inch */
+      i_tags_add(&im->tags, "jpeg_density_unit_name", 0, "inch", -1, 0);
+      break;
+
+    case 2: /* per cm */
+      i_tags_add(&im->tags, "jpeg_density_unit_name", 0, "centimeter", -1, 0);
+      xres *= 2.54;
+      yres *= 2.54;
+      break;
+    }
+    i_tags_set_float2(&im->tags, "i_xres", 0, xres, 6);
+    i_tags_set_float2(&im->tags, "i_yres", 0, yres, 6);
+  }
+
   (void) jpeg_finish_decompress(&cinfo);
   jpeg_destroy_decompress(&cinfo);
   *itlength=tlength;
@@ -435,6 +460,9 @@ undef_int
 i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor) {
   JSAMPLE *image_buffer;
   int quality;
+  int got_xres, got_yres, aspect_only, resunit;
+  double xres, yres;
+  int comment_entry;
 
   struct jpeg_compress_struct cinfo;
   struct my_error_mgr jerr;
@@ -485,8 +513,38 @@ i_writejpeg_wiol(i_img *im, io_glue *ig, int qfactor) {
   jpeg_set_defaults(&cinfo);
   jpeg_set_quality(&cinfo, quality, TRUE);  /* limit to baseline-JPEG values */
 
+  got_xres = i_tags_get_float(&im->tags, "i_xres", 0, &xres);
+  got_yres = i_tags_get_float(&im->tags, "i_yres", 0, &yres);
+  if (!i_tags_get_int(&im->tags, "i_aspect_only", 0,&aspect_only))
+    aspect_only = 0;
+  if (!i_tags_get_int(&im->tags, "jpeg_density_unit", 0, &resunit))
+    resunit = 1; /* per inch */
+  if (resunit < 0 || resunit > 2) /* default to inch if invalid */
+    resunit = 1;
+  if (got_xres || got_yres) {
+    if (!got_xres)
+      xres = yres;
+    else if (!got_yres)
+      yres = xres;
+    if (aspect_only)
+      resunit = 0; /* standard tags override format tags */
+    if (resunit == 2) {
+      /* convert to per cm */
+      xres /= 2.54;
+      yres /= 2.54;
+    }
+    cinfo.density_unit = resunit;
+    cinfo.X_density = (int)(xres + 0.5);
+    cinfo.Y_density = (int)(yres + 0.5);
+  }
+
   jpeg_start_compress(&cinfo, TRUE);
 
+  if (i_tags_find(&im->tags, "jpeg_comment", 0, &comment_entry)) {
+    jpeg_write_marker(&cinfo, JPEG_COM, im->tags.tags[comment_entry].data,
+                     im->tags.tags[comment_entry].size);
+  }
+
   row_stride = im->xsize * im->channels;       /* JSAMPLEs per row in image_buffer */
 
   if (!im->virtual && im->type == i_direct_type && im->bits == i_8_bits) {
index 3b370ac..33706d0 100644 (file)
@@ -290,7 +290,36 @@ JPEG as a 3 channel image.
 
   $img->read(file=>'foo.jpg') or die $img->errstr;
 
-JPEG does not support the spatial resolution tags.
+The following tags are set in a JPEG image when read, and can be set
+to control output:
+
+=over
+
+=item jpeg_density_unit
+
+The value of the density unit field in the JFIF header.  This is
+ignored on writing if the i_aspect_only tag is non-zero.
+
+The C<i_xres> and C<i_yres> tags are expressed in pixels per inch no
+matter the value of this tag, they will be converted to/from the value
+stored in the JPEG file.
+
+=item jpeg_density_unit_name
+
+This is set when reading a JPEG file to the name of the unit given by
+C<jpeg_density_unit>.  Possible results include C<inch>,
+C<centimeter>, C<none> (the C<i_aspect_only> tag is also set reading
+these files).  If the value of jpeg_density_unit is unknown then this
+tag isn't set.
+
+=item jpeg_comment
+
+Text comment.
+
+=back
+
+JPEG supports the spatial resolution tags C<i_xres>, C<i_yres> and
+C<i_aspect_only>.
 
 If an APP1 block containing EXIF information is found, then any of the
 following tags can be set:
@@ -567,7 +596,7 @@ The value of the ResolutionUnit tag.  This is ignored on writing if
 the i_aspect_only tag is non-zero.
 
 The C<i_xres> and C<i_yres> tags are expressed in pixels per inch no
-matter tha value of this tag, they will be converted to/from the value
+matter the value of this tag, they will be converted to/from the value
 stored in the TIFF file.
 
 =item tiff_resolutionunit_name
index f0e428d..ebf80f0 100644 (file)
@@ -2,7 +2,7 @@
 use strict;
 use lib 't';
 use Imager qw(:all);
-use Test::More tests => 34;
+use Test::More tests => 49;
 
 init_log("testout/t101jpeg.log",1);
 
@@ -55,6 +55,7 @@ if (!i_has_format("jpeg")) {
        Imager::log_entry("Starting 4\n", 1);
   my $imoo = Imager->new;
   ok($imoo->read(file=>'testout/t101.jpg'), "read jpeg OO");
+
   ok($imoo->write(file=>'testout/t101_oo.jpg'), "write jpeg OO");
        Imager::log_entry("Starting 5\n", 1);
   my $oocmp = Imager->new;
@@ -141,5 +142,96 @@ if (!i_has_format("jpeg")) {
         "test value of exif tag $key");
     }
   }
+
+  {
+    # tests that the density values are set and read correctly
+    # tests jpeg_comment too
+    my @density_tests =
+      (
+       [ 't101cm100.jpg', 
+        { 
+         jpeg_density_unit => 2, 
+         i_xres => 254, 
+         i_yres => 254
+        },
+        { 
+         jpeg_density_unit => 2, 
+         i_xres => 254, 
+         i_yres => 254,
+         i_aspect_only => undef,
+        },
+       ],
+       [
+       't101xonly.jpg',
+       {
+        i_xres => 100,
+       },
+       {
+        i_xres => 100,
+        i_yres => 100,
+        jpeg_density_unit => 1,
+        i_aspect_only => undef,
+       },
+       ],
+       [
+       't101yonly.jpg',
+       {
+        i_yres => 100,
+       },
+       {
+        i_xres => 100,
+        i_yres => 100,
+        jpeg_density_unit => 1,
+        i_aspect_only => undef,
+       },
+       ],
+       [
+       't101asponly.jpg',
+       {
+        i_xres => 50,
+        i_yres => 100,
+        i_aspect_only => 1,
+       },
+       {
+        i_xres => 50,
+        i_yres => 100,
+        i_aspect_only => 1,
+        jpeg_density_unit => 0,
+       },
+       ],
+       [
+       't101com.jpg',
+       {
+        jpeg_comment => 'test comment'
+       },
+       ],
+      );
+
+    print "# test density tags\n";
+    # I don't care about the content
+    my $base_im = Imager->new(xsize => 10, ysize => 10);
+    for my $test (@density_tests) {
+      my ($filename, $out_tags, $expect_tags) = @$test;
+      $expect_tags ||= $out_tags;
+
+      my $work = $base_im->copy;
+      for my $key (keys %$out_tags) {
+       $work->addtag(name => $key, value => $out_tags->{$key});
+      }
+
+      ok($work->write(file=>"testout/$filename", type=>'jpeg'),
+        "save $filename");
+      
+      my $check = Imager->new;
+      ok($check->read(file=> "testout/$filename"),
+        "read $filename");
+
+      my %tags;
+      for my $key (keys %$expect_tags) {
+       $tags{$key} = $check->tags(name=>$key);
+      }
+      is_deeply($expect_tags, \%tags, "check tags for $filename");
+    }
+  }
 }