WritePNG.pl 15 KB

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