Hi, I am the original author of Image::MetaData::JPEG.
Unfortunately APP2 segments are not really supported by this package (you can only parse two formats, and cannot modify them at high level).
In general, once you have modified a segment, added or deleted records, etc., at low level, you have to call the segment update() method, in order to dump the logical representation of the segment into a binary block that will subsequently be written to file. This is not necessary if you use high-level functions to modify well-known blocks, because it is being taken care of by high-level code.
You can still add APP2 segments if you can provide the binary block yourself though. Have a look at the following commented piece of code:
use strict; use warnings;
use Image::MetaData::JPEG;
use Image::MetaData::JPEG::Record;
use Image::MetaData::JPEG::data::Tables
qw(:JPEGgrammar :Endianness :RecordTypes);
#read the image filname from the command line arguments
my $image = new Image::MetaData::JPEG($ARGV[0]);
# This would be the correct procedure for adding records at low level
# if the APP2 update routine existed at all (but it isn't implemented)
# 1) create an empty APP2 segment
# my $segref =
# new Image::MetaData::JPEG::Segment('APP2', undef, 'NOPARSE');
# 2) store a record with a given key and value
# my $head = "MPF0";
# $segref->store_record('MP_HEADER', $ASCII, \$head);
# 3) IMPORTANT: update the binary block from the in memory
# representation (this was the step you were missing)
# $segref->update();
# But APP2 can "currently" only be parsed if it contains Flashpix
# conversion information ("FPXR") or ICC profiles data. And there is
# no update routine at all. So, either you extend the package or
# create the entire binary block by hand in a local variable:
# you skip parsing and just create the segment.
my $buffer = "MPF0\0";
my $segref =
new Image::MetaData::JPEG::Segment('APP2', \$buffer, 'NOPARSE');
# then insert it into the image
$image->insert_segments($segref);
# the segment is there, albeit without description, since unparsed
foreach my $s ($image->get_segments('APP'))
{ print $s->get_description(); }
# you can save to file nonetheless, unparsable segments aren't dropped
my $new_file_name = 'tmp1.jpg';
$image->save($new_file_name);
print "---------------------------------\n";
# now open the new file and check that APP2 is there (I avoid trying
# to parse it, since parsing would fail with "Invalid APP2 segment"
# in this case)
my $new_image = new Image::MetaData::JPEG($new_file_name, 'APP[^2]');
foreach my $s ($new_image->get_segments('APP'))
{ print $s->get_description(); }
print "---------------------------------\n";
# have a look at what's really inside your file: the APP2 segment
# should be in the very first lines, unless your APP1 is huge.
use Data::HexDump;
my $hexdumper = new Data::HexDump;
$hexdumper->file($new_file_name);
$hexdumper->block_size(1024);
print while $_ = $hexdumper->dump;
For instance, if you generate a test .jpg file with Gimp and subject it to the previous Perl processing, you will obtain something that starts like this: (commented hexadecimal dump)
FF E1 SOI ("Start of image")
FF E0 APP0 segment starts here
00 10 length of APP0 (16, i.e., 14 to follow)
......... 14 data bytes in the APP0 segment
FF E2 APP2 segment starts here
00 07 length of APP2 (7, i.e., 5 to follow)
4D 50 46 30 00 "MPF0\000"
FF FE COM segment starts here
00 13 length of COM (19, i.e. 17 to follow)
......... "Created with GIMP", and so on
Unfortunately, this is all I can offer now. It is clear that Image::MetaData::JPEG has fallen behind JPEG development, and a lot of parsing / dumping / modifying routines should be added and maintained, for which I have no time at all. The reason why the package is so conservative and restrictive is that it aims at correctness and information preservation (it never adds invalid bytes, but will also never drop unparsable segments). I would be glad to transfer to a new maintainer if anyone is available.
Best regards, Stefano Bettelli |