source: gs2-extensions/parallel-building/trunk/src/perllib/cpan/Image/ExifTool/InDesign.pm@ 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)

  • Property svn:executable set to *
File size: 9.7 KB
Line 
1#------------------------------------------------------------------------------
2# File: InDesign.pm
3#
4# Description: Read/write meta information in Adobe InDesign files
5#
6# Revisions: 2009-06-17 - P. Harvey Created
7#
8# References: 1) http://www.adobe.com/devnet/xmp/pdfs/XMPSpecificationPart3.pdf
9#------------------------------------------------------------------------------
10
11package Image::ExifTool::InDesign;
12
13use strict;
14use vars qw($VERSION);
15use Image::ExifTool qw(:DataAccess :Utils);
16
17$VERSION = '1.02';
18
19# map for writing metadata to InDesign files (currently only write XMP)
20my %indMap = (
21 XMP => 'IND',
22);
23
24# GUID's used in InDesign files
25my $masterPageGUID = "\x06\x06\xed\xf5\xd8\x1d\x46\xe5\xbd\x31\xef\xe7\xfe\x74\xb7\x1d";
26my $objectHeaderGUID = "\xde\x39\x39\x79\x51\x88\x4b\x6c\x8E\x63\xee\xf8\xae\xe0\xdd\x38";
27my $objectTrailerGUID = "\xfd\xce\xdb\x70\xf7\x86\x4b\x4f\xa4\xd3\xc7\x28\xb3\x41\x71\x06";
28
29#------------------------------------------------------------------------------
30# Read or write meta information in an InDesign file
31# Inputs: 0) ExifTool object reference, 1) dirInfo reference
32# Returns: 1 on success, 0 if this wasn't a valid InDesign file, or -1 on write error
33sub ProcessIND($$)
34{
35 my ($exifTool, $dirInfo) = @_;
36 my $raf = $$dirInfo{RAF};
37 my $outfile = $$dirInfo{OutFile};
38 my ($hdr, $buff, $buf2, $err, $writeLen, $foundXMP);
39
40 # validate the InDesign file
41 return 0 unless $raf->Read($hdr, 16) == 16;
42 return 0 unless $hdr eq $masterPageGUID;
43 return 0 unless $raf->Read($buff, 8) == 8;
44 $exifTool->SetFileType($buff eq 'DOCUMENT' ? 'INDD' : 'IND'); # set the FileType tag
45
46 # read the master pages
47 $raf->Seek(0, 0) or $err = 'Seek error', goto DONE;
48 unless ($raf->Read($buff, 4096) == 4096 and
49 $raf->Read($buf2, 4096) == 4096)
50 {
51 $err = 'Unexpected end of file';
52 goto DONE; # (goto's can be our friend)
53 }
54 SetByteOrder('II');
55 unless ($buf2 =~ /^\Q$masterPageGUID/) {
56 $err = 'Second master page is invalid';
57 goto DONE;
58 }
59 my $seq1 = Get64u(\$buff, 264);
60 my $seq2 = Get64u(\$buf2, 264);
61 # take the most current master page
62 my $curPage = $seq2 > $seq1 ? \$buf2 : \$buff;
63 # byte order of stream data may be different than headers
64 my $streamInt32u = Get8u($curPage, 24);
65 if ($streamInt32u == 1) {
66 $streamInt32u = 'V'; # little-endian int32u
67 } elsif ($streamInt32u == 2) {
68 $streamInt32u = 'N'; # big-endian int32u
69 } else {
70 $err = 'Invalid stream byte order';
71 goto DONE;
72 }
73 my $pages = Get32u($curPage, 280);
74 $pages < 2 and $err = 'Invalid page count', goto DONE;
75 my $pos = $pages * 4096;
76 if ($pos > 0x7fffffff and not $exifTool->Options('LargeFileSupport')) {
77 $err = 'InDesign files larger than 2 GB not supported (LargeFileSupport not set)';
78 goto DONE;
79 }
80 if ($outfile) {
81 # make XMP the preferred group for writing
82 $exifTool->InitWriteDirs(\%indMap, 'XMP');
83
84 Write($outfile, $buff, $buf2) or $err = 1, goto DONE;
85 my $result = Image::ExifTool::CopyBlock($raf, $outfile, $pos - 8192);
86 unless ($result) {
87 $err = defined $result ? 'Error reading InDesign database' : 1;
88 goto DONE;
89 }
90 $writeLen = 0;
91 } else {
92 $raf->Seek($pos, 0) or $err = 'Seek error', goto DONE;
93 }
94 # scan through the contiguous objects for XMP
95 my $verbose = $exifTool->Options('Verbose');
96 my $out = $exifTool->Options('TextOut');
97 for (;;) {
98 $raf->Read($hdr, 32) or last;
99 unless (length($hdr) == 32 and $hdr =~ /^\Q$objectHeaderGUID/) {
100 # this must be null padding or we have an error
101 $hdr =~ /^\0+$/ or $err = 'Corrupt file or unsupported InDesign version';
102 last;
103 }
104 my $len = Get32u(\$hdr, 24);
105 if ($verbose) {
106 printf $out "Contiguous object at offset 0x%x (%d bytes):\n", $raf->Tell(), $len;
107 if ($verbose > 2) {
108 my %parms = (Addr => $raf->Tell());
109 $parms{MaxLen} = $verbose > 3 ? 1024 : 96 if $verbose < 5;
110 $raf->Seek(-$raf->Read($buff, $len), 1) or $err = 1;
111 Image::ExifTool::HexDump(\$buff, undef, %parms);
112 }
113 }
114 # check for XMP if stream data is long enough
115 # (56 bytes is just enough for XMP header)
116 if ($len > 56) {
117 $raf->Read($buff, 56) == 56 or $err = 'Unexpected end of file', last;
118 if ($buff =~ /^(....)<\?xpacket begin=(['"])\xef\xbb\xbf\2 id=(['"])W5M0MpCehiHzreSzNTczkc9d\3/s) {
119 my $lenWord = $1; # save length word for writing later
120 $len -= 4; # get length of XMP only
121 # load and parse the XMP data
122 unless ($raf->Seek(-52, 1) and $raf->Read($buff, $len) == $len) {
123 $err = 'Error reading XMP stream';
124 last;
125 }
126 $foundXMP = 1;
127 my %dirInfo = (
128 DataPt => \$buff,
129 Parent => 'IND',
130 NoDelete => 1, # do not allow this to be deleted when writing
131 );
132 my $tagTablePtr = GetTagTable('Image::ExifTool::XMP::Main');
133 if ($outfile) {
134 # validate xmp data length (should be same as length in header - 4)
135 my $xmpLen = unpack($streamInt32u, $lenWord);
136 unless ($xmpLen == $len) {
137 $err = "Incorrect XMP stream length ($xmpLen should be $len)";
138 last;
139 }
140 # make sure that XMP is writable
141 my $classID = Get32u(\$hdr, 20);
142 $classID & 0x40000000 or $err = 'XMP stream is not writable', last;
143 my $xmp = $exifTool->WriteDirectory(\%dirInfo, $tagTablePtr);
144 if ($xmp and length $xmp) {
145 # write new xmp with leading length word
146 $buff = pack($streamInt32u, length $xmp) . $xmp;
147 # update header with new length and invalid checksum
148 Set32u(length($buff), \$hdr, 24);
149 Set32u(0xffffffff, \$hdr, 28);
150 } else {
151 $$exifTool{CHANGED} = 0; # didn't change anything
152 $exifTool->Warn("Can't delete XMP as a block from InDesign file") if defined $xmp;
153 # put length word back at start of stream
154 $buff = $lenWord . $buff;
155 }
156 } else {
157 $exifTool->ProcessDirectory(\%dirInfo, $tagTablePtr);
158 }
159 $len = 0; # we got the full stream (nothing left to read)
160 } else {
161 $len -= 56; # we got 56 bytes of the stream
162 }
163 } else {
164 $buff = ''; # must reset this for writing later
165 }
166 if ($outfile) {
167 # write object header and data
168 Write($outfile, $hdr, $buff) or $err = 1, last;
169 my $result = Image::ExifTool::CopyBlock($raf, $outfile, $len);
170 unless ($result) {
171 $err = defined $result ? 'Truncated stream data' : 1;
172 last;
173 }
174 $writeLen += 32 + length($buff) + $len;
175 } elsif ($len) {
176 # skip over remaining stream data
177 $raf->Seek($len, 1) or $err = 'Seek error', last;
178 }
179 $raf->Read($buff, 32) == 32 or $err = 'Unexpected end of file', last;
180 unless ($buff =~ /^\Q$objectTrailerGUID/) {
181 $err = 'Invalid object trailer';
182 last;
183 }
184 if ($outfile) {
185 # make sure object UID and ClassID are the same in the trailer
186 substr($hdr,16,8) eq substr($buff,16,8) or $err = 'Non-matching object trailer', last;
187 # write object trailer
188 Write($outfile, $objectTrailerGUID, substr($hdr,16)) or $err = 1, last;
189 $writeLen += 32;
190 }
191 }
192 if ($outfile) {
193 # write null padding if necessary
194 # (InDesign files must be an even number of 4096-byte blocks)
195 my $part = $writeLen % 4096;
196 Write($outfile, "\0" x (4096 - $part)) or $err = 1 if $part;
197 }
198DONE:
199 if (not $err) {
200 $exifTool->Warn('No XMP stream to edit') if $outfile and not $foundXMP;
201 return 1; # success!
202 } elsif (not $outfile) {
203 # issue warning on read error
204 $exifTool->Warn($err) unless $err eq '1';
205 } elsif ($err ne '1') {
206 # set error and return success code
207 $exifTool->Error($err);
208 } else {
209 return -1; # write error
210 }
211 return 1;
212}
213
2141; # end
215
216__END__
217
218=head1 NAME
219
220Image::ExifTool::InDesign - Read/write meta information in Adobe InDesign files
221
222=head1 SYNOPSIS
223
224This module is used by Image::ExifTool
225
226=head1 DESCRIPTION
227
228This module contains routines required by Image::ExifTool to read XMP
229meta information from Adobe InDesign (.IND, .INDD and .INDT) files.
230
231=head1 LIMITATIONS
232
2331) Only XMP meta information is processed.
234
2352) A new XMP stream may not be created, so XMP tags may only be written to
236InDesign files which previously contained XMP.
237
2383) File sizes of greater than 2 GB and are not currently supported because
239the ability to handle large files like this is system dependent.
240
241=head1 AUTHOR
242
243Copyright 2003-2011, Phil Harvey (phil at owl.phy.queensu.ca)
244
245This library is free software; you can redistribute it and/or modify it
246under the same terms as Perl itself.
247
248=head1 REFERENCES
249
250=over 4
251
252=item L<http://www.adobe.com/devnet/xmp/pdfs/XMPSpecificationPart3.pdf>
253
254=back
255
256=head1 SEE ALSO
257
258L<Image::ExifTool(3pm)|Image::ExifTool>
259
260=cut
261
Note: See TracBrowser for help on using the repository browser.