source: gs2-extensions/parallel-building/trunk/src/perllib/cpan/Image/ExifTool/WritePNG.pl@ 24626

Last change on this file since 24626 was 24626, checked in by jmt12, 13 years ago

An (almost) complete copy of the perllib directory from a (circa SEP2011) head checkout from Greenstone 2 trunk - in order to try and make merging in this extension a little easier later on (as there have been some major changes to buildcol.pl commited in the main trunk but not in the x64 branch)

File size: 12.5 KB
Line 
1#------------------------------------------------------------------------------
2# File: WritePNG.pl
3#
4# Description: Write PNG meta information
5#
6# Revisions: 09/16/2005 - P. Harvey Created
7#
8# References: 1) http://www.libpng.org/pub/png/spec/1.2/
9#------------------------------------------------------------------------------
10package Image::ExifTool::PNG;
11
12use strict;
13
14#------------------------------------------------------------------------------
15# Calculate CRC or update running CRC (ref 1)
16# Inputs: 0) data reference, 1) running crc to update (undef intially)
17# 2) data position (undef for 0), 3) data length (undef for all data),
18# Returns: updated CRC
19my @crcTable;
20sub CalculateCRC($;$$$)
21{
22 my ($dataPt, $crc, $pos, $len) = @_;
23 $crc = 0 unless defined $crc;
24 $pos = 0 unless defined $pos;
25 $len = length($$dataPt) - $pos unless defined $len;
26 $crc ^= 0xffffffff; # undo 1's complement
27 # build lookup table unless done already
28 unless (@crcTable) {
29 my ($c, $n, $k);
30 for ($n=0; $n<256; ++$n) {
31 for ($k=0, $c=$n; $k<8; ++$k) {
32 $c = ($c & 1) ? 0xedb88320 ^ ($c >> 1) : $c >> 1;
33 }
34 $crcTable[$n] = $c;
35 }
36 }
37 # calculate the CRC
38 foreach (unpack("x${pos}C$len", $$dataPt)) {
39 $crc = $crcTable[($crc^$_) & 0xff] ^ ($crc >> 8);
40 }
41 return $crc ^ 0xffffffff; # return 1's complement
42}
43
44#------------------------------------------------------------------------------
45# Encode data in ASCII Hex
46# Inputs: 0) input data reference
47# Returns: Hex-encoded data (max 72 chars per line)
48sub HexEncode($)
49{
50 my $dataPt = shift;
51 my $len = length($$dataPt);
52 my $hex = '';
53 my $pos;
54 for ($pos = 0; $pos < $len; $pos += 36) {
55 my $n = $len - $pos;
56 $n > 36 and $n = 36;
57 $hex .= unpack('H*',substr($$dataPt,$pos,$n)) . "\n";
58 }
59 return $hex;
60}
61
62#------------------------------------------------------------------------------
63# Write profile to tEXt or zTXt chunk (zTXt if Zlib is available)
64# Inputs: 0) outfile, 1) Raw profile type, 2) data ref
65# 3) profile header type (undef if not a text profile)
66# Returns: 1 on success
67sub WriteProfile($$$;$)
68{
69 my ($outfile, $rawType, $dataPt, $profile) = @_;
70 my ($buff, $prefix, $chunk, $deflate);
71 if (eval 'require Compress::Zlib') {
72 $deflate = Compress::Zlib::deflateInit();
73 }
74 if (not defined $profile) {
75 # write ICC profile as compressed iCCP chunk if possible
76 return 0 unless $deflate;
77 $buff = $deflate->deflate($$dataPt);
78 return 0 unless defined $buff;
79 $buff .= $deflate->flush();
80 my %rawTypeChunk = ( icm => 'iCCP' );
81 $chunk = $rawTypeChunk{$rawType} or return 0;
82 $prefix = "$rawType\0\0";
83 $dataPt = \$buff;
84 } else {
85 # write as ASCII-hex encoded profile in tEXt or zTXt chunk
86 my $txtHdr = sprintf("\n$profile profile\n%8d\n", length($$dataPt));
87 $buff = $txtHdr . HexEncode($dataPt);
88 $chunk = 'tEXt'; # write as tEXt if deflate not available
89 $prefix = "Raw profile type $rawType\0";
90 $dataPt = \$buff;
91 # write profile as zTXt chunk if possible
92 if ($deflate) {
93 my $buf2 = $deflate->deflate($buff);
94 if (defined $buf2) {
95 $dataPt = \$buf2;
96 $buf2 .= $deflate->flush();
97 $chunk = 'zTXt';
98 $prefix .= "\0"; # compression type byte (0=deflate)
99 }
100 }
101 }
102 my $hdr = pack('Na4', length($prefix) + length($$dataPt), $chunk) . $prefix;
103 my $crc = CalculateCRC(\$hdr, undef, 4);
104 $crc = CalculateCRC($dataPt, $crc);
105 return Write($outfile, $hdr, $$dataPt, pack('N',$crc));
106}
107
108#------------------------------------------------------------------------------
109# Add iCCP to the PNG image if necessary (must come before PLTE and IDAT)
110# Inputs: 0) ExifTool object ref, 1) output file or scalar ref
111# Returns: true on success
112sub Add_iCCP($$)
113{
114 my ($exifTool, $outfile) = @_;
115 if ($exifTool->{ADD_DIRS}->{ICC_Profile}) {
116 # write new ICC data
117 my $tagTablePtr = Image::ExifTool::GetTagTable('Image::ExifTool::ICC_Profile::Main');
118 my %dirInfo = ( Parent => 'PNG', DirName => 'ICC_Profile' );
119 my $buff = $exifTool->WriteDirectory(\%dirInfo, $tagTablePtr);
120 if (defined $buff and length $buff and WriteProfile($outfile, 'icm', \$buff)) {
121 $exifTool->VPrint(0, "Created ICC profile\n");
122 delete $exifTool->{ADD_DIRS}->{ICC_Profile}; # don't add it again
123 }
124 }
125 return 1;
126}
127
128#------------------------------------------------------------------------------
129# Generate tEXt, zTXt or iTXt data for writing
130# Inputs: 0) ExifTool ref, 1) tagID, 2) tagInfo ref, 3) value string, 4) language code
131# Returns: chunk data (not including 8-byte chunk header)
132# Notes: Sets ExifTool TextChunkType member to the type of chunk written
133sub BuildTextChunk($$$$$)
134{
135 my ($exifTool, $tag, $tagInfo, $val, $lang) = @_;
136 my ($xtra, $compVal, $iTXt, $comp);
137 if ($$tagInfo{SubDirectory}) {
138 if ($$tagInfo{Name} eq 'XMP') {
139 $iTXt = 2; # write as iTXt but flag to avoid encoding
140 # (never compress XMP)
141 } else {
142 $comp = 2; # compress raw profile if possible
143 }
144 } else {
145 # compress if specified
146 $comp = 1 if $exifTool->Options('Compress');
147 if ($lang) {
148 $iTXt = 1; # write as iTXt if it has a language code
149 $tag =~ s/-$lang$//; # remove language code from tagID
150 } elsif ($$exifTool{OPTIONS}{Charset} ne 'Latin' and $val =~ /[\x80-\xff]/) {
151 $iTXt = 1; # write as iTXt if it contains non-Latin special characters
152 }
153 }
154 if ($comp) {
155 my $warn;
156 if (eval 'require Compress::Zlib') {
157 my $deflate = Compress::Zlib::deflateInit();
158 $compVal = $deflate->deflate($val) if $deflate;
159 if (defined $compVal) {
160 $compVal .= $deflate->flush();
161 # only compress if it actually saves space
162 unless (length($compVal) < length($val)) {
163 undef $compVal;
164 $warn = 'uncompressed data is smaller';
165 }
166 } else {
167 $warn = 'deflate error';
168 }
169 } else {
170 $warn = 'Compress::Zlib not available';
171 }
172 # warn if any user-specified compression fails
173 if ($warn and $comp == 1) {
174 $exifTool->Warn("PNG:$$tagInfo{Name} not compressed ($warn)", 1);
175 }
176 }
177 # decide whether to write as iTXt, zTXt or tEXt
178 if ($iTXt) {
179 $$exifTool{TextChunkType} = 'iTXt';
180 $xtra = (defined $compVal ? "\x01\0" : "\0\0") . ($lang || '') . "\0\0";
181 # iTXt is encoded as UTF-8 (but note that XMP is already UTF-8)
182 $val = $exifTool->Encode($val, 'UTF8') if $iTXt == 1;
183 } elsif (defined $compVal) {
184 $$exifTool{TextChunkType} = 'zTXt';
185 $xtra = "\0";
186 } else {
187 $$exifTool{TextChunkType} = 'tEXt';
188 $xtra = '';
189 }
190 return $tag . "\0" . $xtra . (defined $compVal ? $compVal : $val);
191}
192
193#------------------------------------------------------------------------------
194# Add any outstanding new chunks to the PNG image
195# Inputs: 0) ExifTool object ref, 1) output file or scalar ref
196# Returns: true on success
197sub AddChunks($$)
198{
199 my ($exifTool, $outfile) = @_;
200 # write any outstanding PNG tags
201 my $addTags = $exifTool->{ADD_PNG};
202 delete $exifTool->{ADD_PNG};
203 my ($tag, $dir, $err, $tagTablePtr);
204
205 foreach $tag (sort keys %$addTags) {
206 my $tagInfo = $$addTags{$tag};
207 my $nvHash = $exifTool->GetNewValueHash($tagInfo);
208 # (always create native PNG information, so don't check IsCreating())
209 next unless Image::ExifTool::IsOverwriting($nvHash) > 0;
210 my $val = Image::ExifTool::GetNewValues($nvHash);
211 if (defined $val) {
212 my $data;
213 if ($$tagInfo{Table} eq \%Image::ExifTool::PNG::TextualData) {
214 $data = BuildTextChunk($exifTool, $tag, $tagInfo, $val, $$tagInfo{LangCode});
215 $data = $$exifTool{TextChunkType} . $data;
216 delete $$exifTool{TextChunkType};
217 } else {
218 $data = "$tag$val";
219 }
220 my $hdr = pack('N', length($data) - 4);
221 my $cbuf = pack('N', CalculateCRC(\$data, undef));
222 Write($outfile, $hdr, $data, $cbuf) or $err = 1;
223 $exifTool->VerboseValue("+ PNG:$$tagInfo{Name}", $val);
224 ++$exifTool->{CHANGED};
225 }
226 }
227 $addTags = { }; # prevent from adding tags again
228 # create any necessary directories
229 foreach $dir (sort keys %{$exifTool->{ADD_DIRS}}) {
230 my $buff;
231 my %dirInfo = (
232 Parent => 'PNG',
233 DirName => $dir,
234 );
235 if ($dir eq 'IFD0') {
236 $exifTool->VPrint(0, "Creating EXIF profile:\n");
237 $exifTool->{TIFF_TYPE} = 'APP1';
238 $tagTablePtr = Image::ExifTool::GetTagTable('Image::ExifTool::Exif::Main');
239 $buff = $exifTool->WriteDirectory(\%dirInfo, $tagTablePtr, \&Image::ExifTool::WriteTIFF);
240 if (defined $buff and length $buff) {
241 $buff = $Image::ExifTool::exifAPP1hdr . $buff;
242 WriteProfile($outfile, 'APP1', \$buff, 'generic') or $err = 1;
243 }
244 } elsif ($dir eq 'XMP') {
245 $exifTool->VPrint(0, "Creating XMP iTXt chunk:\n");
246 $tagTablePtr = Image::ExifTool::GetTagTable('Image::ExifTool::XMP::Main');
247 $dirInfo{ReadOnly} = 1;
248 $buff = $exifTool->WriteDirectory(\%dirInfo, $tagTablePtr);
249 if (defined $buff and length $buff and
250 # the packet is read-only (because of CRC)
251 Image::ExifTool::XMP::ValidateXMP(\$buff, 'r'))
252 {
253 # (previously, XMP was created as a non-standard XMP profile chunk)
254 # $buff = $Image::ExifTool::xmpAPP1hdr . $buff;
255 # WriteProfile($outfile, 'APP1', \$buff, 'generic') or $err = 1;
256 # (but now write XMP iTXt chunk according to XMP specification)
257 $buff = "iTXtXML:com.adobe.xmp\0\0\0\0\0" . $buff;
258 my $hdr = pack('N', length($buff) - 4);
259 my $cbuf = pack('N', CalculateCRC(\$buff, undef));
260 Write($outfile, $hdr, $buff, $cbuf) or $err = 1;
261 }
262 } elsif ($dir eq 'IPTC') {
263 $exifTool->VPrint(0, "Creating IPTC profile:\n");
264 # write new IPTC data
265 $tagTablePtr = Image::ExifTool::GetTagTable('Image::ExifTool::Photoshop::Main');
266 $buff = $exifTool->WriteDirectory(\%dirInfo, $tagTablePtr);
267 if (defined $buff and length $buff) {
268 WriteProfile($outfile, 'iptc', \$buff, 'IPTC') or $err = 1;
269 }
270 } elsif ($dir eq 'ICC_Profile') {
271 $exifTool->VPrint(0, "Creating ICC profile:\n");
272 # write new ICC data (only done if we couldn't create iCCP chunk)
273 $tagTablePtr = Image::ExifTool::GetTagTable('Image::ExifTool::ICC_Profile::Main');
274 $buff = $exifTool->WriteDirectory(\%dirInfo, $tagTablePtr);
275 if (defined $buff and length $buff) {
276 WriteProfile($outfile, 'icm', \$buff, 'ICC') or $err = 1;
277 $exifTool->Warn('Wrote ICC as a raw profile (no Compress::Zlib)');
278 }
279 }
280 }
281 $exifTool->{ADD_DIRS} = { }; # prevent from adding dirs again
282 return not $err;
283}
284
285
2861; # end
287
288__END__
289
290=head1 NAME
291
292Image::ExifTool::WritePNG.pl - Write PNG meta information
293
294=head1 SYNOPSIS
295
296These routines are autoloaded by Image::ExifTool::PNG.
297
298=head1 DESCRIPTION
299
300This file contains routines to write PNG metadata.
301
302=head1 NOTES
303
304Compress::Zlib is required to write compressed text.
305
306Existing text tags are always rewritten in their original form (compressed
307zTXt, uncompressed tEXt or internation iTXt), so pre-existing compressed
308information can only be modified if Compress::Zlib is available.
309
310Newly created textual information is written in uncompressed tEXt form by
311default, or as compressed zTXt if the Compress option is used and
312Compress::Zlib is available (but only if the resulting compressed data is
313smaller than the original text, which isn't always the case for short text
314strings).
315
316=head1 AUTHOR
317
318Copyright 2003-2011, Phil Harvey (phil at owl.phy.queensu.ca)
319
320This library is free software; you can redistribute it and/or modify it
321under the same terms as Perl itself.
322
323=head1 SEE ALSO
324
325L<Image::ExifTool::PNG(3pm)|Image::ExifTool::PNG>,
326L<Image::ExifTool(3pm)|Image::ExifTool>
327
328=cut
Note: See TracBrowser for help on using the repository browser.