1 // =================================================================================================
2 // ADOBE SYSTEMS INCORPORATED
3 // Copyright 2002-2007 Adobe Systems Incorporated
4 // All Rights Reserved
5 //
6 // NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
7 // of the Adobe license agreement accompanying it.
8 // =================================================================================================
9 
10 #include <limits.h>
11 
12 #include "XMPScanner.hpp"
13 
14 #include "Scanner_Handler.hpp"
15 #include "PostScript_Handler.hpp"
16 
17 using namespace std;
18 
19 // =================================================================================================
20 /// \file PostScript_Handler.cpp
21 /// \brief File format handler for PostScript and EPS files.
22 ///
23 /// This header ...
24 ///
25 // =================================================================================================
26 
27 static const char * kPSFileTag    = "%!PS-Adobe-";
28 static const size_t kPSFileTagLen = strlen ( kPSFileTag );
29 
30 // =================================================================================================
31 // PostScript_MetaHandlerCTor
32 // ==========================
33 
PostScript_MetaHandlerCTor(XMPFiles * parent)34 XMPFileHandler * PostScript_MetaHandlerCTor ( XMPFiles * parent )
35 {
36 	XMPFileHandler * newHandler = new PostScript_MetaHandler ( parent );
37 
38 	return newHandler;
39 
40 }	// PostScript_MetaHandlerCTor
41 
42 // =================================================================================================
43 // PostScript_CheckFormat
44 // ======================
45 
PostScript_CheckFormat(XMP_FileFormat format,XMP_StringPtr filePath,LFA_FileRef fileRef,XMPFiles * parent)46 bool PostScript_CheckFormat ( XMP_FileFormat format,
47 	                          XMP_StringPtr  filePath,
48 	                          LFA_FileRef    fileRef,
49 	                          XMPFiles *     parent )
50 {
51 	IgnoreParam(filePath); IgnoreParam(parent);
52 	XMP_Assert ( (format == kXMP_EPSFile) || (format == kXMP_PostScriptFile) );
53 
54 	IOBuffer ioBuf;
55 
56 	XMP_Int64 psOffset;
57 	size_t    psLength;
58 	long      temp1, temp2;
59 
60 	// Check for the binary EPSF preview header.
61 
62 	LFA_Seek ( fileRef, 0, SEEK_SET );
63 	if ( ! CheckFileSpace ( fileRef, &ioBuf, 4 ) ) return false;
64 	temp1 = GetUns32BE ( ioBuf.ptr );
65 
66 	if ( temp1 == (long)0xC5D0D3C6 ) {
67 
68 		if ( ! CheckFileSpace ( fileRef, &ioBuf, 30 ) ) return false;
69 
70 		psOffset = GetUns32LE ( ioBuf.ptr+4 );	// PostScript offset.
71 		psLength = GetUns32LE ( ioBuf.ptr+8 );	// PostScript length.
72 
73 		bool ok;
74 		LFA_Seek ( fileRef, psOffset, SEEK_SET, &ok );
75 		if ( ! ok ) return false;	// Don't throw for a failure.
76 
77 		ioBuf.ptr = ioBuf.limit;	// Make sure RefillBuffer does a simple read.
78 		RefillBuffer ( fileRef, &ioBuf );
79 		if ( (ioBuf.len < kIOBufferSize) && (ioBuf.len < psLength) ) return false;	// Not enough PostScript.
80 
81 	}
82 
83 	// Check the start of the PostScript DSC header comment.
84 
85 	if ( ! CheckFileSpace ( fileRef, &ioBuf, (kPSFileTagLen + 3 + 1) ) ) return false;
86 	if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSFileTag), kPSFileTagLen ) ) return false;
87 	ioBuf.ptr += kPSFileTagLen;
88 
89 	// Check the PostScript DSC major version number.
90 
91 	temp1 = LONG_MIN;	// Will safely overflow if there are digits, remain negative if there aren't.
92 	while ( (ioBuf.ptr < ioBuf.limit) && ('0' <= *ioBuf.ptr) && (*ioBuf.ptr <= '9') ) {
93 		temp1 = (temp1 * 10) + (*ioBuf.ptr - '0');
94 		if ( temp1 < 0 ) return false;	// Overflow.
95 		ioBuf.ptr += 1;
96 	}
97 	// if ( temp1 < 0 ) break;	*** Covered by 3.0 check.
98 	if ( temp1 < 3 ) return false;	// The version must be at least 3.0.
99 
100 	if ( ! CheckFileSpace ( fileRef, &ioBuf, 3 ) ) return false;
101 	if ( *ioBuf.ptr != '.' ) return false;	// No minor number.
102 	ioBuf.ptr += 1;
103 
104 	// Check the PostScript DSC minor version number.
105 
106 	temp2 = LONG_MIN;	// Will safely overflow if there are digits, remain negative if there aren't.
107 	while ( (ioBuf.ptr < ioBuf.limit) && ('0' <= *ioBuf.ptr) && (*ioBuf.ptr <= '9') ) {
108 		temp2 = (temp2 * 10) + (*ioBuf.ptr - '0');
109 		if ( temp2 < 0 ) return false;	// Overflow.
110 		ioBuf.ptr += 1;
111 	}
112 	if ( temp2 < 0 ) return false;	// No digits or overflow.
113 	// Note that we don't care about the actual minor version number.
114 
115 	if ( format == kXMP_PostScriptFile ) {
116 
117 		// Almost done for plain PostScript, check for whitespace.
118 
119 		if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
120 		if ( (*ioBuf.ptr != ' ') && (*ioBuf.ptr != kLF) && (*ioBuf.ptr != kCR) ) return false;
121 		ioBuf.ptr += 1;
122 
123 	} else {
124 
125 		// Check for the EPSF keyword on the header comment.
126 
127 		if ( ! CheckFileSpace ( fileRef, &ioBuf, 6+3+1 ) ) return false;
128 		if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr(" EPSF-"), 6 ) ) return false;
129 		ioBuf.ptr += 6;
130 
131 		// Check the EPS major version number.
132 
133 		temp1 = LONG_MIN;	// Will safely overflow if there are digits, remain negative if there aren't.
134 		while ( (ioBuf.ptr < ioBuf.limit) && ('0' <= *ioBuf.ptr) && (*ioBuf.ptr <= '9') ) {
135 			temp1 = (temp1 * 10) + (*ioBuf.ptr - '0');
136 			if ( temp1 < 0 ) return false;	// Overflow.
137 			ioBuf.ptr += 1;
138 		}
139 		// if ( temp1 < 0 ) break;	*** Covered by 3.0 check.
140 		if ( temp1 < 3 ) return false;	// The version must be at least 3.0.
141 
142 		if ( ! CheckFileSpace ( fileRef, &ioBuf, 3 ) ) return false;
143 		if ( *ioBuf.ptr != '.' ) return false;	// No minor number.
144 		ioBuf.ptr += 1;
145 
146 		// Check the EPS minor version number.
147 
148 		temp2 = LONG_MIN;	// Will safely overflow if there are digits, remain negative if there aren't.
149 		while ( (ioBuf.ptr < ioBuf.limit) && ('0' <= *ioBuf.ptr) && (*ioBuf.ptr <= '9') ) {
150 			temp2 = (temp2 * 10) + (*ioBuf.ptr - '0');
151 			if ( temp2 < 0 ) return false;	// Overflow.
152 			ioBuf.ptr += 1;
153 		}
154 		if ( temp2 < 0 ) return false;	// No digits or overflow.
155 		// Note that we don't care about the actual minor version number.
156 
157 		if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return false;
158 		if ( (*ioBuf.ptr != kLF) && (*ioBuf.ptr != kCR) ) return false;
159 		ioBuf.ptr += 1;
160 
161 	}
162 
163 	return true;
164 
165 }	// PostScript_CheckFormat
166 
167 // =================================================================================================
168 // PostScript_MetaHandler::PostScript_MetaHandler
169 // ==============================================
170 
PostScript_MetaHandler(XMPFiles * _parent)171 PostScript_MetaHandler::PostScript_MetaHandler ( XMPFiles * _parent )
172 {
173 	this->parent = _parent;
174 	this->handlerFlags = kPostScript_HandlerFlags;
175 	this->stdCharForm = kXMP_Char8Bit;
176 	this->psHint = kPSHint_NoMarker;
177 
178 }	// PostScript_MetaHandler::PostScript_MetaHandler
179 
180 // =================================================================================================
181 // PostScript_MetaHandler::~PostScript_MetaHandler
182 // ===============================================
183 
~PostScript_MetaHandler()184 PostScript_MetaHandler::~PostScript_MetaHandler()
185 {
186 	// ! Inherit the base cleanup.
187 
188 }	// PostScript_MetaHandler::~PostScript_MetaHandler
189 
190 // =================================================================================================
191 // PostScript_MetaHandler::FindPostScriptHint
192 // ==========================================
193 //
194 // Search for "%ADO_ContainsXMP:" at the beginning of a line, it must be before "%%EndComments". If
195 // the XMP marker is found, look for the MainFirst/MainLast/NoMain options.
196 
197 static const char * kPSContainsXMPString = "%ADO_ContainsXMP:";
198 static const size_t kPSContainsXMPLength = strlen ( kPSContainsXMPString );
199 
200 static const char * kPSEndCommentString  = "%%EndComments";	// ! Assumed shorter than kPSContainsXMPString.
201 static const size_t kPSEndCommentLength  = strlen ( kPSEndCommentString );
202 
FindPostScriptHint()203 int PostScript_MetaHandler::FindPostScriptHint()
204 {
205 	bool     found = false;
206 	IOBuffer ioBuf;
207 	XMP_Uns8 ch;
208 
209 	LFA_FileRef fileRef = this->parent->fileRef;
210 
211 	XMP_AbortProc abortProc  = this->parent->abortProc;
212 	void *        abortArg   = this->parent->abortArg;
213 	const bool    checkAbort = (abortProc != 0);
214 
215 	// Check for the binary EPSF preview header.
216 
217 	LFA_Seek ( fileRef, 0, SEEK_SET );
218 	if ( ! CheckFileSpace ( fileRef, &ioBuf, 4 ) ) return false;
219 	long temp1 = GetUns32BE ( ioBuf.ptr );
220 
221 	if ( temp1 == (long)0xC5D0D3C6 ) {
222 
223 		if ( ! CheckFileSpace ( fileRef, &ioBuf, 30 ) ) return false;
224 
225 		size_t psOffset = GetUns32LE ( ioBuf.ptr+4 );	// PostScript offset.
226 		size_t psLength = GetUns32LE ( ioBuf.ptr+8 );	// PostScript length.
227 
228 		bool ok;
229 		LFA_Seek ( fileRef, psOffset, SEEK_SET, &ok );
230 		if ( ! ok ) return false;	// Don't throw for a failure.
231 
232 		ioBuf.ptr = ioBuf.limit;	// Force the next check to refill the buffer.
233 
234 	}
235 
236 	// Look for the ContainsXMP comment.
237 
238 	while ( true ) {
239 
240 		if ( checkAbort && abortProc(abortArg) ) {
241 			XMP_Throw ( "PostScript_MetaHandler::FindPostScriptHint - User abort", kXMPErr_UserAbort );
242 		}
243 
244 		if ( ! CheckFileSpace ( fileRef, &ioBuf, kPSContainsXMPLength ) ) return kPSHint_NoMarker;
245 
246 		if ( CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSEndCommentString), kPSEndCommentLength ) ) {
247 
248 			// Found "%%EndComments", don't look any further.
249 			return kPSHint_NoMarker;
250 
251 		} else if ( ! CheckBytes ( ioBuf.ptr, Uns8Ptr(kPSContainsXMPString), kPSContainsXMPLength ) ) {
252 
253 			// Not "%%EndComments" or "%ADO_ContainsXMP:", skip past the end of this line.
254 			do {
255 				if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return kPSHint_NoMarker;
256 				ch = *ioBuf.ptr;
257 				++ioBuf.ptr;
258 			} while ( ! IsNewline ( ch ) );
259 
260 		} else {
261 
262 			// Found "%ADO_ContainsXMP:", look for the main packet location option.
263 
264 			ioBuf.ptr += kPSContainsXMPLength;
265 			int xmpHint = kPSHint_NoMain;	// ! From here on, a failure means "no main", not "no marker".
266 			if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return kPSHint_NoMain;
267 			if ( ! IsSpaceOrTab ( *ioBuf.ptr ) ) return kPSHint_NoMain;
268 
269 			while ( true ) {
270 
271 				while ( true ) {	// Skip leading spaces and tabs.
272 					if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return kPSHint_NoMain;
273 					if ( ! IsSpaceOrTab ( *ioBuf.ptr ) ) break;
274 					++ioBuf.ptr;
275 				}
276 				if ( IsNewline ( *ioBuf.ptr ) ) return kPSHint_NoMain;	// Reached the end of the ContainsXMP comment.
277 
278 				if ( ! CheckFileSpace ( fileRef, &ioBuf, 6 ) ) return kPSHint_NoMain;
279 
280 				if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("NoMain"), 6 ) ) {
281 
282 					ioBuf.ptr += 6;
283 					xmpHint = kPSHint_NoMain;
284 					break;
285 
286 				} else if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("MainFi"), 6 ) ) {
287 
288 					ioBuf.ptr += 6;
289 					if ( ! CheckFileSpace ( fileRef, &ioBuf, 3 ) ) return kPSHint_NoMain;
290 					if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("rst"), 3 ) ) {
291 						ioBuf.ptr += 3;
292 						xmpHint = kPSHint_MainFirst;
293 					}
294 					break;
295 
296 				} else if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("MainLa"), 6 ) ) {
297 
298 					ioBuf.ptr += 6;
299 					if ( ! CheckFileSpace ( fileRef, &ioBuf, 2 ) ) return kPSHint_NoMain;
300 					if ( CheckBytes ( ioBuf.ptr, Uns8Ptr("st"), 2 ) ) {
301 						ioBuf.ptr += 2;
302 						xmpHint = kPSHint_MainLast;
303 					}
304 					break;
305 
306 				} else {
307 
308 					while ( true ) {	// Skip until whitespace.
309 						if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return kPSHint_NoMain;
310 						if ( IsWhitespace ( *ioBuf.ptr ) ) break;
311 						++ioBuf.ptr;
312 					}
313 
314 				}
315 
316 			}	// Look for the main packet location option.
317 
318 			// Make sure we found exactly a known option.
319 			if ( ! CheckFileSpace ( fileRef, &ioBuf, 1 ) ) return kPSHint_NoMain;
320 			if ( ! IsWhitespace ( *ioBuf.ptr ) ) return kPSHint_NoMain;
321 			return xmpHint;
322 
323 		}	// Found "%ADO_ContainsXMP:".
324 
325 	}	// Outer marker loop.
326 
327 	return kPSHint_NoMarker;	// Should never reach here.
328 
329 }	// PostScript_MetaHandler::FindPostScriptHint
330 
331 
332 // =================================================================================================
333 // PostScript_MetaHandler::FindFirstPacket
334 // =======================================
335 //
336 // Run the packet scanner until we find a valid packet. The first one is the main. For simplicity,
337 // the state of all snips is checked after each buffer is read. In theory only the last of the
338 // previous snips might change from partial to valid, but then we would have to special case the
339 // first pass when there is no previous set of snips. Since we have to get a full report to look at
340 // the last snip anyway, it costs virtually nothing extra to recheck all of the snips.
341 
FindFirstPacket()342 bool PostScript_MetaHandler::FindFirstPacket()
343 {
344 	int		snipCount;
345 	bool 	found	= false;
346 	size_t	bufPos, bufLen;
347 
348 	LFA_FileRef fileRef = this->parent->fileRef;
349 	XMP_Int64   fileLen = LFA_Measure ( fileRef );
350 	XMP_PacketInfo & packetInfo = this->packetInfo;
351 
352 	XMPScanner scanner ( fileLen );
353 	XMPScanner::SnipInfoVector snips;
354 
355 	enum { kBufferSize = 64*1024 };
356 	XMP_Uns8	buffer [kBufferSize];
357 
358 	XMP_AbortProc abortProc  = this->parent->abortProc;
359 	void *        abortArg   = this->parent->abortArg;
360 	const bool    checkAbort = (abortProc != 0);
361 
362 	bufPos = 0;
363 	bufLen = 0;
364 
365 	LFA_Seek ( fileRef, 0, SEEK_SET );	// Seek back to the beginning of the file.
366 
367 	while ( true ) {
368 
369 		if ( checkAbort && abortProc(abortArg) ) {
370 			XMP_Throw ( "PostScript_MetaHandler::FindFirstPacket - User abort", kXMPErr_UserAbort );
371 		}
372 
373 		bufPos += bufLen;
374 		bufLen = LFA_Read ( fileRef, buffer, kBufferSize );
375 		if ( bufLen == 0 ) return false;	// Must be at EoF, no packets found.
376 
377 		scanner.Scan ( buffer, bufPos, bufLen );
378 		snipCount = scanner.GetSnipCount();
379 		scanner.Report ( snips );
380 
381 		for ( int i = 0; i < snipCount; ++i ) {
382 			if ( snips[i].fState == XMPScanner::eValidPacketSnip ) {
383 				if ( snips[i].fLength > 0x7FFFFFFF ) XMP_Throw ( "PostScript_MetaHandler::FindFirstPacket: Oversize packet", kXMPErr_BadXMP );
384 				packetInfo.offset = snips[i].fOffset;
385 				packetInfo.length = (XMP_Int32)snips[i].fLength;
386 				packetInfo.charForm  = snips[i].fCharForm;
387 				packetInfo.writeable = (snips[i].fAccess == 'w');
388 				return true;
389 			}
390 		}
391 
392 	}
393 
394 	return false;
395 
396 }	// FindFirstPacket
397 
398 
399 // =================================================================================================
400 // PostScript_MetaHandler::FindLastPacket
401 // ======================================
402 //
403 // Run the packet scanner backwards until we find the start of a packet, or a valid packet. If we
404 // found a packet start, resume forward scanning to see if it is a valid packet. For simplicity, all
405 // of the snips are checked on each pass, for much the same reasons as in FindFirstPacket.
406 
407 #if 1
408 
409 // *** Doing this right (as described above) requires out of order scanning support which isn't
410 // *** implemented yet. For now we scan the whole file and pick the last valid packet.
411 
FindLastPacket()412 bool PostScript_MetaHandler::FindLastPacket()
413 {
414 	int		pkt;
415 	size_t	bufPos, bufLen;
416 
417 	LFA_FileRef fileRef = this->parent->fileRef;
418 	XMP_Int64   fileLen = LFA_Measure ( fileRef );
419 	XMP_PacketInfo & packetInfo = this->packetInfo;
420 
421 	// ------------------------------------------------------
422 	// Scan the entire file to find all of the valid packets.
423 
424 	XMPScanner	scanner ( fileLen );
425 
426 	enum { kBufferSize = 64*1024 };
427 	XMP_Uns8	buffer [kBufferSize];
428 
429 	XMP_AbortProc abortProc  = this->parent->abortProc;
430 	void *        abortArg   = this->parent->abortArg;
431 	const bool    checkAbort = (abortProc != 0);
432 
433 	LFA_Seek ( fileRef, 0, SEEK_SET );	// Seek back to the beginning of the file.
434 
435 	for ( bufPos = 0; bufPos < (size_t)fileLen; bufPos += bufLen ) {
436 		if ( checkAbort && abortProc(abortArg) ) {
437 			XMP_Throw ( "PostScript_MetaHandler::FindLastPacket - User abort", kXMPErr_UserAbort );
438 		}
439 		bufLen = LFA_Read ( fileRef, buffer, kBufferSize );
440 		if ( bufLen == 0 ) XMP_Throw ( "PostScript_MetaHandler::FindLastPacket: Read failure", kXMPErr_ExternalFailure );
441 		scanner.Scan ( buffer, bufPos, bufLen );
442 	}
443 
444 	// -------------------------------
445 	// Pick the last the valid packet.
446 
447 	long snipCount = scanner.GetSnipCount();
448 
449 	XMPScanner::SnipInfoVector snips ( snipCount );
450 	scanner.Report ( snips );
451 
452 	for ( pkt = snipCount-1; pkt >= 0; --pkt ) {
453 		if ( snips[pkt].fState == XMPScanner::eValidPacketSnip ) break;
454 	}
455 
456 	if ( pkt >= 0 ) {
457 		if ( snips[pkt].fLength > 0x7FFFFFFF ) XMP_Throw ( "PostScript_MetaHandler::FindLastPacket: Oversize packet", kXMPErr_BadXMP );
458 		packetInfo.offset = snips[pkt].fOffset;
459 		packetInfo.length = (XMP_Int32)snips[pkt].fLength;
460 		packetInfo.charForm  = snips[pkt].fCharForm;
461 		packetInfo.writeable = (snips[pkt].fAccess == 'w');
462 		return true;
463 	}
464 
465 	return false;
466 
467 }	// PostScript_MetaHandler::FindLastPacket
468 
469 #else
470 
FindLastPacket()471 bool PostScript_MetaHandler::FindLastPacket()
472 {
473 	int		  err, snipCount;
474 	bool 	  found	= false;
475 	XMP_Int64 backPos, backLen;
476 	size_t	  ioCount;
477 
478 	LFA_FileRef fileRef = this->parent->fileRef;
479 	XMP_Int64   fileLen = LFA_Measure ( fileRef );
480 	XMP_PacketInfo & packetInfo = this->packetInfo;
481 
482 	XMPScanner scanner ( fileLen );
483 	XMPScanner::SnipInfoVector snips;
484 
485 	enum { kBufferSize = 64*1024 };
486 	XMP_Uns8	buffer [kBufferSize];
487 
488 	XMP_AbortProc abortProc  = this->parent->abortProc;
489 	void *            abortArg   = this->parent->abortArg;
490 	const bool        checkAbort = (abortProc != 0);
491 
492 	backPos = fileLen;
493 	backLen = 0;
494 
495 	while ( true ) {
496 
497 		if ( checkAbort && abortProc(abortArg) ) {
498 			XMP_Throw ( "PostScript_MetaHandler::FindLastPacket - User abort", kXMPErr_UserAbort );
499 		}
500 
501 		backLen = kBufferSize;
502 		if ( backPos < kBufferSize ) backLen = backPos;
503 		if ( backLen == 0 ) return false;	// Must be at BoF, no packets found.
504 
505 		backPos -= backLen;
506 		LFA_Seek ( fileRef, backPos, SEEK_SET );	// Seek back to the start of the next buffer.
507 
508 		#error "ioCount is 32 bits, backLen is 64"
509 		ioCount = LFA_Read ( fileRef, buffer, backLen );
510 		if ( ioCount != backLen ) XMP_Throw ( "PostScript_MetaHandler::FindLastPacket: Read failure", kXMPErr_ExternalFailure );
511 
512 		scanner.Scan ( buffer, backPos, backLen );
513 		snipCount = scanner.GetSnipCount();
514 		scanner.Report ( snips );
515 
516 		for ( int i = snipCount-1; i >= 0; --i ) {
517 
518 			if ( snips[i].fState == XMPScanner::eValidPacketSnip ) {
519 
520 				return VerifyMainPacket ( fileRef, snips[i].fOffset, snips[i].fLength, format, beLenient, mainInfo );
521 
522 			} else if ( snips[i].fState == XMPScanner::ePartialPacketSnip ) {
523 
524 				// This part is a tad tricky. We have a partial packet, so we need to scan
525 				// forward from its ending to see if it is a valid packet. Snips won't recombine,
526 				// the partial snip will change state. Be careful with the I/O to not clobber the
527 				// backward scan positions, so that it can be resumed if necessary.
528 
529 				size_t fwdPos = snips[i].fOffset + snips[i].fLength;
530 				LFA_Seek ( fileRef, fwdPos, SEEK_SET );	// Seek to the end of the partial snip.
531 
532 				while ( (fwdPos < fileLen) && (snips[i].fState == XMPScanner::ePartialPacketSnip) ) {
533 					ioCount = LFA_Read ( fileRef, buffer, kBufferSize );
534 					if ( ioCount == 0 ) XMP_Throw ( "PostScript_MetaHandler::FindLastPacket: Read failure", kXMPErr_ExternalFailure );
535 					scanner.Scan ( buffer, fwdPos, ioCount );
536 					scanner.Report ( snips );
537 					fwdPos += ioCount;
538 				}
539 
540 				if ( snips[i].fState == XMPScanner::eValidPacketSnip ) {
541 					if ( snips[i].fLength > 0x7FFFFFFF ) XMP_Throw ( "PostScript_MetaHandler::FindLastPacket: Oversize packet", kXMPErr_BadXMP );
542 					packetInfo.offset = snips[i].fOffset;
543 					packetInfo.length = (XMP_Int32)snips[i].fLength;
544 					packetInfo.charForm  = snips[i].fCharForm;
545 					packetInfo.writeable = (snips[i].fAccess == 'w');
546 					return true;
547 				}
548 
549 			}
550 
551 		}	//  Backwards snip loop.
552 
553 	}	// Backwards read loop.
554 
555 	return false;	// Should never get here.
556 
557 }	// PostScript_MetaHandler::FindLastPacket
558 
559 #endif
560 
561 // =================================================================================================
562 // PostScript_MetaHandler::CacheFileData
563 // =====================================
564 
CacheFileData()565 void PostScript_MetaHandler::CacheFileData()
566 {
567 	this->containsXMP = false;
568 	this->psHint = FindPostScriptHint();
569 
570 	if ( this->psHint == kPSHint_MainFirst ) {
571 		this->containsXMP = FindFirstPacket();
572 	} else if ( this->psHint == kPSHint_MainLast ) {
573 		this->containsXMP = FindLastPacket();
574 	}
575 
576 	if ( this->containsXMP ) ReadXMPPacket ( this );
577 
578 }	// PostScript_MetaHandler::CacheFileData
579 
580 // =================================================================================================
581