RSRC.pm 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. #------------------------------------------------------------------------------
  2. # File: RSRC.pm
  3. #
  4. # Description: Read Mac OS Resource information
  5. #
  6. # Revisions: 2010/03/17 - P. Harvey Created
  7. #
  8. # References: 1) http://developer.apple.com/legacy/mac/library/documentation/mac/MoreToolbox/MoreToolbox-99.html
  9. #------------------------------------------------------------------------------
  10. package Image::ExifTool::RSRC;
  11. use strict;
  12. use vars qw($VERSION);
  13. use Image::ExifTool qw(:DataAccess :Utils);
  14. $VERSION = '1.08';
  15. # Information decoded from Mac OS resources
  16. %Image::ExifTool::RSRC::Main = (
  17. GROUPS => { 2 => 'Document' },
  18. NOTES => q{
  19. Tags extracted from Mac OS resource files and DFONT files. These tags may
  20. also be extracted from the resource fork of any file in OS X, either by
  21. adding "/..namedfork/rsrc" to the filename to process the resource fork
  22. alone, or by using the ExtractEmbedded (-ee) option to process the resource
  23. fork as a sub-document of the main file. When writing, ExifTool preserves
  24. the Mac OS resource fork by default, but it may deleted with C<-rsrc:all=>
  25. on the command line.
  26. },
  27. '8BIM' => {
  28. Name => 'PhotoshopInfo',
  29. SubDirectory => { TagTable => 'Image::ExifTool::Photoshop::Main' },
  30. },
  31. 'sfnt' => {
  32. Name => 'Font',
  33. SubDirectory => { TagTable => 'Image::ExifTool::Font::Name' },
  34. },
  35. # my samples of postscript-type DFONT files have a POST resource
  36. # with ID 0x1f5 and the same format as a PostScript file
  37. 'POST_0x01f5' => {
  38. Name => 'PostscriptFont',
  39. SubDirectory => { TagTable => 'Image::ExifTool::PostScript::Main' },
  40. },
  41. 'usro_0x0000' => 'OpenWithApplication',
  42. 'vers_0x0001' => 'ApplicationVersion',
  43. 'STR _0xbff3' => 'ApplicationMissingMsg',
  44. 'STR _0xbff4' => 'CreatorApplication',
  45. # the following written by Photoshop
  46. # (ref http://www.adobe.ca/devnet/photoshop/psir/ps_image_resources.pdf)
  47. 'STR#_0x0080' => 'Keywords',
  48. 'TEXT_0x0080' => 'Description',
  49. # don't extract PICT's because the clip region isn't set properly
  50. # in the PICT resource for some reason. Also, a dummy 512-byte
  51. # header would have to be added to create a valid PICT file.
  52. # 'PICT' => { Name => 'PreviewPICT', Binary => 1 },
  53. );
  54. #------------------------------------------------------------------------------
  55. # Read information from a Mac resource file (ref 1)
  56. # Inputs: 0) ExifTool ref, 1) dirInfo ref
  57. # Returns: 1 on success, 0 if this wasn't a valid resource file
  58. sub ProcessRSRC($$)
  59. {
  60. my ($et, $dirInfo) = @_;
  61. my $raf = $$dirInfo{RAF};
  62. my ($hdr, $map, $buff, $i, $j);
  63. # attempt to validate the format as thoroughly as practical
  64. return 0 unless $raf->Read($hdr, 30) == 30;
  65. my ($datOff, $mapOff, $datLen, $mapLen) = unpack('N*', $hdr);
  66. return 0 unless $raf->Seek(0, 2);
  67. my $fLen = $raf->Tell();
  68. return 0 if $datOff < 0x10 or $datOff + $datLen > $fLen;
  69. return 0 if $mapOff < 0x10 or $mapOff + $mapLen > $fLen or $mapLen < 30;
  70. return 0 if $datOff < $mapOff and $datOff + $datLen > $mapOff;
  71. return 0 if $mapOff < $datOff and $mapOff + $mapLen > $datOff;
  72. # read the resource map
  73. $raf->Seek($mapOff, 0) and $raf->Read($map, $mapLen) == $mapLen or return 0;
  74. SetByteOrder('MM');
  75. my $typeOff = Get16u(\$map, 24);
  76. my $nameOff = Get16u(\$map, 26);
  77. my $numTypes = Get16u(\$map, 28);
  78. # validate offsets in the resource map
  79. return 0 if $typeOff < 28 or $nameOff < 30;
  80. $et->SetFileType('RSRC') unless $$et{IN_RESOURCE};
  81. my $verbose = $et->Options('Verbose');
  82. my $tagTablePtr = GetTagTable('Image::ExifTool::RSRC::Main');
  83. $et->VerboseDir('RSRC', $numTypes+1);
  84. # parse resource type list
  85. for ($i=0; $i<=$numTypes; ++$i) {
  86. my $off = $typeOff + 2 + 8 * $i; # offset of entry in type list
  87. last if $off + 8 > $mapLen;
  88. my $resType = substr($map,$off,4); # resource type
  89. my $resNum = Get16u(\$map,$off+4); # number of resources - 1
  90. my $refOff = Get16u(\$map,$off+6) + $typeOff; # offset to first resource reference
  91. # loop through all resources
  92. for ($j=0; $j<=$resNum; ++$j) {
  93. my $roff = $refOff + 12 * $j;
  94. last if $roff + 12 > $mapLen;
  95. # read only the 24-bit resource data offset
  96. my $id = Get16u(\$map,$roff);
  97. my $resOff = (Get32u(\$map,$roff+4) & 0x00ffffff) + $datOff;
  98. my $resNameOff = Get16u(\$map,$roff+2) + $nameOff + $mapOff;
  99. my ($tag, $val, $valLen);
  100. my $tagInfo = $$tagTablePtr{$resType};
  101. if ($tagInfo) {
  102. $tag = $resType;
  103. } else {
  104. $tag = sprintf('%s_0x%.4x', $resType, $id);
  105. $tagInfo = $$tagTablePtr{$tag};
  106. }
  107. # read the resource data if necessary
  108. if ($tagInfo or $verbose) {
  109. unless ($raf->Seek($resOff, 0) and $raf->Read($buff, 4) == 4 and
  110. ($valLen = unpack('N', $buff)) < 100000000 and # arbitrary size limit (100MB)
  111. $raf->Read($val, $valLen) == $valLen)
  112. {
  113. $et->Warn("Error reading $resType resource");
  114. next;
  115. }
  116. }
  117. if ($verbose) {
  118. my ($resName, $nameLen);
  119. $resName = '' unless $raf->Seek($resNameOff, 0) and $raf->Read($buff, 1) and
  120. ($nameLen = ord $buff) != 0 and $raf->Read($resName, $nameLen) == $nameLen;
  121. $et->VPrint(0,sprintf("%s resource ID 0x%.4x (offset 0x%.4x, $valLen bytes, name='%s'):\n",
  122. $resType, $id, $resOff, $resName));
  123. $et->VerboseDump(\$val);
  124. }
  125. next unless $tagInfo;
  126. if ($resType eq 'vers') {
  127. # parse the 'vers' resource to get the long version string
  128. next unless $valLen > 8;
  129. # long version string is after short version
  130. my $p = 7 + Get8u(\$val, 6);
  131. next if $p >= $valLen;
  132. my $vlen = Get8u(\$val, $p++);
  133. next if $p + $vlen > $valLen;
  134. my $tagTablePtr = GetTagTable('Image::ExifTool::RSRC::Main');
  135. $val = $et->Decode(substr($val, $p, $vlen), 'MacRoman');
  136. } elsif ($resType eq 'sfnt') {
  137. # parse the OTF font block
  138. $raf->Seek($resOff + 4, 0) or next;
  139. $$dirInfo{Base} = $resOff + 4;
  140. require Image::ExifTool::Font;
  141. unless (Image::ExifTool::Font::ProcessOTF($et, $dirInfo)) {
  142. $et->Warn('Unrecognized sfnt resource format');
  143. }
  144. # assume this is a DFONT file unless processing the rsrc fork
  145. $et->OverrideFileType('DFONT') unless $$et{DOC_NUM};
  146. next;
  147. } elsif ($resType eq '8BIM') {
  148. my $ttPtr = GetTagTable('Image::ExifTool::Photoshop::Main');
  149. $et->HandleTag($ttPtr, $id, $val,
  150. DataPt => \$val,
  151. DataPos => $resOff + 4,
  152. Size => $valLen,
  153. Start => 0,
  154. Parent => 'RSRC',
  155. );
  156. next;
  157. } elsif ($resType eq 'STR ' and $valLen > 1) {
  158. # extract Pascal string
  159. my $len = ord $val;
  160. next unless $valLen >= $len + 1;
  161. $val = substr($val, 1, $len);
  162. } elsif ($resType eq 'usro' and $valLen > 4) {
  163. my $len = unpack('N', $val);
  164. next unless $valLen >= $len + 4;
  165. ($val = substr($val, 4, $len)) =~ s/\0.*//g; # truncate at null
  166. } elsif ($resType eq 'STR#' and $valLen > 2) {
  167. # extract list of strings (ref http://simtech.sourceforge.net/tech/strings.html)
  168. my $num = unpack('n', $val);
  169. next if $num & 0xf000; # (ignore special-format STR# resources)
  170. my ($i, @vals);
  171. my $pos = 2;
  172. for ($i=0; $i<$num; ++$i) {
  173. last if $pos >= $valLen;
  174. my $len = ord substr($val, $pos++, 1);
  175. last if $pos + $len > $valLen;
  176. push @vals, substr($val, $pos, $len);
  177. $pos += $len;
  178. }
  179. $val = \@vals;
  180. } elsif ($resType eq 'POST') {
  181. # assume this is a DFONT file unless processing the rsrc fork
  182. $et->OverrideFileType('DFONT') unless $$et{DOC_NUM};
  183. $val = substr $val, 2;
  184. } elsif ($resType ne 'TEXT') {
  185. next;
  186. }
  187. $et->HandleTag($tagTablePtr, $tag, $val);
  188. }
  189. }
  190. return 1;
  191. }
  192. 1; # end
  193. __END__
  194. =head1 NAME
  195. Image::ExifTool::RSRC - Read Mac OS Resource information
  196. =head1 SYNOPSIS
  197. This module is used by Image::ExifTool
  198. =head1 DESCRIPTION
  199. This module contains routines required by Image::ExifTool to read Mac OS
  200. resource files.
  201. =head1 AUTHOR
  202. Copyright 2003-2016, Phil Harvey (phil at owl.phy.queensu.ca)
  203. This library is free software; you can redistribute it and/or modify it
  204. under the same terms as Perl itself.
  205. =head1 REFERENCES
  206. =over 4
  207. =item L<http://developer.apple.com/legacy/mac/library/documentation/mac/MoreToolbox/MoreToolbox-99.html>
  208. =back
  209. =head1 SEE ALSO
  210. L<Image::ExifTool::TagNames/RSRC Tags>,
  211. L<Image::ExifTool(3pm)|Image::ExifTool>
  212. =cut