Source code: com/flexstor/flexdbserver/services/asset/FPOExtract.java
1 /*
2 * FPOExtract.java
3 *
4 * Copyright $Date: 2003/08/11 02:22:28 $ FLEXSTOR.net Inc.
5 *
6 * This work is licensed for use and distribution under license terms found at
7 * http://www.flexstor.org/license.html
8 *
9 */
10
11 package com.flexstor.flexdbserver.services.asset;
12
13 import java.io.EOFException;
14 import java.io.IOException;
15 import java.util.Vector;
16
17 import com.flexstor.common.io.xfile.FlexXRandomAccessFile;
18 import com.flexstor.common.util.Diagnostic;
19
20
21 /**
22 * Locate and extract the image from a Xinet EPSF
23 *
24 */
25 public class FPOExtract
26 {
27 public final static String IDENTIFIER="$Id: FPOExtract.java,v 1.4 2003/08/11 02:22:28 aleric Exp $";
28
29
30
31 // Image input parameters
32 protected String sOutputFile = null;
33
34 // Image output parameters
35 protected int nImageWidth = 0;
36 protected int nImageHeight = 0;
37 protected int nImageXResolution = 0;
38 protected int nImageYResolution = 0;
39 protected long nFilesize = 0;
40 protected int nImageLength = 0;
41
42 protected String sImageType = "";
43 protected String sImageSubType = "0";
44
45 protected int nAdditionalItems = 0;
46 protected boolean bError = false;
47
48 /************* TEST MODE ************/
49 protected boolean bTest = false;
50 /************************************/
51
52 /**
53 *
54 *
55 */
56 public FPOExtract()
57 {
58 } // constructor
59
60
61 /**
62 *
63 *
64 */
65 public boolean extractImage(String sParentFile, String sOutputFile, String sImageToProcess)
66 {
67 return extractImage(sParentFile, sOutputFile, sImageToProcess, "");
68 }
69
70
71 /**
72 *
73 *
74 */
75 public boolean extractImage(String sParentFile, String sOutputFile, String sImageToProcess, String sImageName)
76 {
77 return processImages(sParentFile, sOutputFile, sImageName, sImageToProcess, false);
78 }
79
80 /**
81 *
82 *
83 */
84 public boolean getImageInfo(String sParentFile, String sImageToProcess)
85 {
86 return getImageInfo(sParentFile, sImageToProcess, "");
87 }
88
89 /**
90 *
91 *
92 */
93 public boolean getImageInfo(String sParentFile, String sImageToProcess, String sImageName)
94 {
95 return processImages(sParentFile, "", sImageName, sImageToProcess, true);
96 }
97
98 /**
99 *
100 *
101 */
102 public boolean getMapInfo(String sParentFile)
103 {
104 printDiagnosticMessage("Processing file: " + sParentFile);
105 return processImages(sParentFile, "", "", "-1", true);
106 } // getMapInfo
107
108 /**
109 *
110 *
111 */
112 protected boolean processImages(String sParentFile,
113 String sOutputFile,
114 String sName,
115 String sImageToProcess,
116 boolean bInfo)
117 {
118 boolean bResult = true;
119 boolean bReadToEnd = false;
120 this.sOutputFile = sOutputFile;
121
122 // Initialize image parameters
123 sImageType = "";
124 nImageWidth = 0;
125 nImageHeight = 0;
126 nImageXResolution = 0;
127 nImageYResolution = 0;
128 nFilesize = 0;
129
130 // Open the parent file
131 FlexXRandomAccessFile fParentFile = openFile(sParentFile, "r");
132 if (fParentFile == null)
133 {
134 return false;
135 }
136
137 // Get the file map containing pointers to images and headers
138 Vector vMap = getMap(fParentFile);
139 if (vMap == null)
140 {
141 printDiagnosticMessage("Image file not FPO: " + sParentFile);
142 return false;
143 }
144
145 if ((sImageToProcess == "-1") || (bTest == true))
146 {
147 printMap(vMap);
148 if (sImageToProcess == "-1")
149 {
150 return true;
151 }
152 }
153
154 // Get the pointer for the image specified from the file map
155 long nImagePointer = getImagePointer(fParentFile, vMap, sName, sImageToProcess);
156 if (nImagePointer == -1)
157 {
158 closeFile(fParentFile);
159 return false;
160 }
161
162 if (bTest)
163 {
164 printDiagnosticMessage("Image block pointer: " + Long.toHexString(nImagePointer));
165 }
166
167 // Set the filesize
168 nFilesize = (long)nImageLength;
169
170 // Get the image parameters if requested
171 if (bInfo == true)
172 {
173 if (getInfo(fParentFile, nImagePointer) == false)
174 {
175 closeFile(fParentFile);
176 return false;
177 }
178 } // bGetInfo if
179
180 else // Extract and copy the image
181 {
182 // If want to read to the end of the file, set image length to -1
183 // if (bReadToEnd == true)
184 // {
185 // nImageLength = -1;
186 // }
187
188 if (bTest)
189 {
190 // Add the appropriate extension to the filename
191 if (sImageType.equals("") == false)
192 {
193 sOutputFile = sOutputFile + "." + sImageType;
194 }
195
196 printDiagnosticMessage("Output file : " + sOutputFile);
197 printDiagnosticMessage("Image pointer: " + Long.toHexString(nImagePointer));
198 printDiagnosticMessage("Image type : " + sImageType);
199 }
200
201 // Copy the image to the file name specified
202 if (extractAndCopyImage(fParentFile,
203 nImagePointer,
204 nImageLength,
205 sOutputFile,
206 false) == false)
207 {
208 closeFile(fParentFile);
209 return false;
210 }
211 } // bInfo else
212
213 if (bTest == true)
214 {
215 printDiagnosticMessage("Current file pointer: " + Long.toHexString(getCurrentPos(fParentFile)));
216 }
217
218 closeFile(fParentFile);
219 return bResult;
220 } // processImages
221
222
223 /**
224 * Get the location of the image specified by type (FPO, etc) and sequence number (1,2..)
225 * Non-image blocks may be interspersed between the image blocks so must check
226 * the block format to make sure that it is an image block. The image format is
227 * remembered for later info extraction.
228 *
229 */
230 protected long getImagePointer(FlexXRandomAccessFile fParentFile,
231 Vector vMap,
232 String sName,
233 String sImageToProcess)
234 {
235 int nImagePointer = -1;
236 long nCurrentPos = -1;
237
238 // sImageToProcess selects the image within the named group
239 // It can be specified as an int (1,2,3..) or as an image type
240 // The allowed types are <EPS, TIF, EPSORTIF>
241 int nImageToProcess = 0;
242 try
243 {
244 nImageToProcess = Integer.parseInt(sImageToProcess);
245
246 // Appears to be a number (exception not thrown)
247 sImageToProcess = "";
248 }
249
250 catch (NumberFormatException e)
251 {
252 // Not a number, its an image type specification
253 nImageToProcess = 0;
254 }
255
256 // Check the name, in none specified, default to FPO
257 if (sName.equals("") == true)
258 {
259 sName = "FPO";
260 }
261
262 // Is the image map vector valid?
263 if ((vMap == null) || (vMap.size() == 0))
264 {
265 return -1;
266 }
267
268 // Scan the map for the image pointers with the correct name (FPO, PICT, ...)
269 // Select the correct image in this sequence per nImageToProcess or
270 // sImageToProcess, whichever is specified
271 int nImageNumber = 1;
272 for (int i=0; i<vMap.size(); i++)
273 {
274 nImageLength = 0;
275 sImageType = "";
276
277 ImagePointer refMappedPointer = (ImagePointer)vMap.elementAt(i);
278 if (refMappedPointer.getName().trim().equals(sName) == true)
279 {
280 nImagePointer = refMappedPointer.getPointer();
281
282 // Add the offset value to the image pointer
283 nImagePointer += 0x100;
284
285 // Go to the image
286 if (goToFileLocation(fParentFile, (long)nImagePointer) == false)
287 {
288 nCurrentPos = -1;
289 break;
290 }
291
292 // Read the image size (next 4 bytes)
293 nImageLength = readInt(fParentFile);
294
295 // Get and save the current file position (this is the start of the image)
296 nCurrentPos = getCurrentPos(fParentFile);
297 if (nCurrentPos == -1)
298 {
299 nCurrentPos = -1;
300 break;
301 }
302
303 if (bTest)
304 {
305 printDiagnosticMessage("Image pointer: " + Long.toHexString(nCurrentPos));
306 }
307
308 // Is this an image block or other data block (they can be intermixed)
309 // Check for the image identifier, is it a valid image?
310 if (checkFormat(fParentFile) == false)
311 {
312 // An error occurred
313 nCurrentPos = -1;
314 break;
315 }
316
317 // Is the block an image block
318 // If not continue in the loop, getting the next block
319 // for the specified image name (FPO, etc)
320 if (sImageType.equals("") == false)
321 {
322 // It's an image block, so check to see if it is the one
323 // specified by the sequence number or the image type
324 if (nImageToProcess != 0)
325 {
326 // An image sequence number was specified
327 if (nImageNumber == nImageToProcess)
328 {
329 // Correct image found, quit
330 break;
331 }
332
333 else
334 {
335 // The block was an image but not the right one
336 // so increment the image number, and try again
337 nImageNumber++;
338 }
339 } // nImageNumber if
340
341 else
342 {
343 // sImageToProcess denotes a file type
344 if ((sImageToProcess.equals("EPSORTIF")) &&
345 ((sImageType.equals("eps")) || (sImageType.equals("tif"))))
346 {
347 break;
348 }
349
350 if ((sImageToProcess.equals("EPS")) &&
351 (sImageType.equals("eps")))
352 {
353 break;
354 }
355
356 if ((sImageToProcess.equals("TIF")) &&
357 (sImageType.equals("tif")))
358 {
359 break;
360 }
361 } // nImageNumber else
362 } // sImageType if
363
364 else
365 {
366 // Block is not an image
367 if (bTest)
368 {
369 printDiagnosticMessage("Block is not an image: " + Long.toHexString(nCurrentPos));
370 }
371 }
372 } // equals(sName)
373 } // for i
374
375 if (sImageType.equals("") == true)
376 {
377 nCurrentPos = -1;
378 }
379
380 if (nCurrentPos == -1)
381 {
382 printDiagnosticMessage("Image not found in map for params: " + sName + ", " + nImageToProcess);
383 }
384
385 return nCurrentPos;
386 } // getImageToProcess
387
388 /**
389 *
390 *
391 */
392 protected boolean getInfo(FlexXRandomAccessFile fFile, long nBlockStartPos)
393 {
394 boolean bResult = true;
395
396 // Note that the current file position is just after the format-determining header info
397
398 if (sImageType.equals("eps") == true)
399 {
400 if (getEpsInfo(fFile) == false)
401 {
402 return false;
403 }
404 }
405
406 else if (sImageType.equals("gif") == true)
407 {
408 if (getGifInfo(fFile) == false)
409 {
410 return false;
411 }
412 }
413
414 else if (sImageType.equals("tif") == true)
415 {
416 if (getTiffInfo(fFile) == false)
417 {
418 return false;
419 }
420 }
421
422 else if (sImageType.equals("jpg") == true)
423 {
424 if (getJfifInfo(fFile, nBlockStartPos) == false)
425 {
426 return false;
427 }
428 }
429
430 else if ((sImageType.equals("pic") == true) &&
431 (sImageSubType.equals("0") == true))
432 {
433 // Version 2 PICT
434 if (getPictInfo(fFile, nBlockStartPos, 2) == false)
435 {
436 return false;
437 }
438 }
439
440 else if ((sImageType.equals("pic") == true) &&
441 (sImageSubType.equals("1") == true))
442 {
443 // Version 1 PICT
444 if (getPictInfo(fFile, nBlockStartPos, 1) == false)
445 {
446 return false;
447 }
448 }
449
450
451 return bResult;
452 } // genfo
453
454
455 /**
456 *
457 *
458 */
459 protected boolean getEpsInfo(FlexXRandomAccessFile fFile)
460 {
461 boolean bResult = true;
462 String sInfoIdentifier = "%ImageData:";
463
464 // Read 'nLength' bytes from the current file position
465 int nLength = 512;
466 byte anData[] = readBytes(fFile, nLength);
467 if (anData == null)
468 {
469 bResult = false;
470 }
471
472 // Convert the array data to a string, search it for the info identifier string
473 String sFileData = new String(anData);
474
475 int nIndex = sFileData.indexOf(sInfoIdentifier);
476 if (nIndex == -1)
477 {
478 // Info identifier not found
479 bResult = false;
480 }
481
482 else
483 {
484 // Parse the image width and length
485 String sInfoString = sFileData.substring(nIndex + sInfoIdentifier.length());
486
487 if (bTest == true)
488 {
489 printDiagnosticMessage("EPS Info string: " + sInfoString);
490 }
491
492 String sSub = getSubString(sInfoString, 0, 4);
493 if ((sSub != null) && (sSub.length() != 0))
494 {
495 nImageWidth = getInt(sSub);
496 }
497
498 sSub = getSubString(sInfoString, 4, 8);
499 if ((sSub != null) && (sSub.length() != 0))
500 {
501 nImageHeight = getInt(sSub);
502 }
503 } // nIndex else
504
505 return bResult;
506 } // getEpsInfo
507
508
509 /**
510 *
511 *
512 */
513 protected boolean getGifInfo(FlexXRandomAccessFile fFile)
514 {
515 boolean bResult = true;
516
517 // Read 'nLength' bytes from the current file position
518 int nLength = 256;
519 byte anData[] = readBytes(fFile, nLength);
520 if (anData == null)
521 {
522 bResult = false;
523 }
524
525 // Get width
526 int nParam = 0;
527 // nParam = getInt(anData, 1, 2);
528 nParam = getInt(anData[1], anData[0]);
529 nImageWidth = nParam;
530
531 // Get height
532 // nParam = getInt(anData, 3, 2);
533 nParam = getInt(anData[3], anData[2]);
534 nImageHeight = nParam;
535
536 return bResult;
537 } // getGifInfo
538
539 /**
540 *
541 *
542 */
543 protected boolean getPictInfo(FlexXRandomAccessFile fInputFile, long nBlockStartPos, int nPictVersion)
544 {
545 boolean bResult = true;
546
547 // First go back to the start of the image block
548 if (goToFileLocation(fInputFile, nBlockStartPos) == false)
549 {
550 return false;
551 }
552
553 // Get the Image File Header
554 byte anHeader[] = readBytes(fInputFile, 40);
555 if (anHeader == null)
556 {
557 return false;
558 }
559
560 // Get the width and height of the image
561 // Get the bounding box parameters first
562 int nUppperLeftY = getInt(anHeader, 2, 2);
563 int nUppperLeftX = getInt(anHeader, 4, 2);
564 int nLowerRightY = getInt(anHeader, 6, 2);
565 int nLowerRightX = getInt(anHeader, 8, 2);
566
567 nImageWidth = nLowerRightX - nUppperLeftX;
568 nImageHeight = nLowerRightY - nUppperLeftY;
569
570 // Get the resolutions
571 if (nPictVersion == 1)
572 {
573 // PICT version 1 doesn't support header info other than version and bounding rectangle
574 // therefore, no resolution.
575 nImageXResolution = 0;
576 nImageYResolution = 0;
577 }
578
579 else
580 {
581 nImageXResolution = getInt(anHeader, 20, 2);
582 nImageYResolution = getInt(anHeader, 24, 2);
583 }
584
585 return bResult;
586 } // getPictInfo
587
588 /**
589 *
590 *
591 */
592 protected boolean getTiffInfo(FlexXRandomAccessFile fInputFile)
593 {
594 boolean bResult = true;
595
596 // Save the current file position pointer
597 // This is the start of the Tiff image
598 long lCurrentPos = getCurrentPos(fInputFile);
599 if (lCurrentPos == -1)
600 {
601 return false;
602 }
603
604 // Get the Image File Header
605 byte anHeader[] = readBytes(fInputFile, 8);
606 if (anHeader == null)
607 {
608 bResult = false;
609 }
610
611 // Get the pointer to the Image File Directory (IFD)
612 int nIfdPointer = getInt(anHeader, 2, 4);
613 nIfdPointer = nIfdPointer & 0xffffffff;
614
615 // The above pointer is relative to start of image
616 if (goToPointerLocation(fInputFile, nIfdPointer - 10) == false)
617 {
618 return false;
619 }
620
621 // We should be at the start of the Image File Directory
622 // The first two bytes is the number of 12-byte tags it contains
623 // Tags should be in ascending order by tag
624 // This code assumes all relevant tags are contiguous
625 // Read enough data to get the tags of interest
626
627 // Get the number of tags
628 byte anTagCount[] = readBytes(fInputFile, 2);
629 if (anTagCount == null)
630 {
631 return false;
632 }
633
634 int nTagCount = getInt(anTagCount, 0, 2); // Bytes 0-1
635
636 // Read all tags (12 bytes each)
637 byte anIFD[] = readBytes(fInputFile, nTagCount * 12);
638 if (anIFD == null)
639 {
640 return false;
641 }
642
643 // Tag construction: Bytes 0-1 The Tag identifier
644 // Bytes 2-3 The field Type (1-byte, 2-ascii, 3-short(16b), 4-long(32b), 5-rational(64b))
645 // Bytes 4-7 The count of the Type
646 // Bytes 8-11 The value offset or the value itself (msb-justified)
647 //
648 // Note: Technically, we should search for the tags to be more general. Since we only want two common tags
649 // we will get them directly.
650 // We are also not concerned about Endian here.
651 // Also assuming Type 3 values (16 bit) for these tags.
652 //
653 // Get the Image Width Tag (id = 0100h)
654 int nTagIndex = findTag(anIFD, nTagCount, 0x100);
655 if (nTagIndex != -1)
656 {
657 nImageWidth = getInt(anIFD[nTagIndex + 8], anIFD[nTagIndex + 9]);
658 }
659
660 // Get the Image Height Tag (id = 0101h)
661 nTagIndex = findTag(anIFD, nTagCount, 0x101);
662 if (nTagIndex != -1)
663 {
664 nImageHeight = getInt(anIFD[nTagIndex + 8], anIFD[nTagIndex + 9]);
665 }
666
667 // Get the resolution unit tag (id = 0x128h)
668 // 1 = no absolute unit of measurement
669 // 2 = inches
670 // 3 = cm
671 int nResolutionUnit = 2; // default to inches
672 nTagIndex = findTag(anIFD, nTagCount, 0x128);
673 if (nTagIndex != -1)
674 {
675 nResolutionUnit = getInt(anIFD[nTagIndex + 8], anIFD[nTagIndex + 9]);
676 }
677
678 // Get the X resolution tag (id = 0x11ah)
679 nImageXResolution = getTiffResolution(anIFD,
680 fInputFile,
681 lCurrentPos,
682 0x11a,
683 nTagCount,
684 nResolutionUnit);
685
686 // Get the Y resolution tag (id = 0x11bh)
687 nImageYResolution = getTiffResolution(anIFD,
688 fInputFile,
689 lCurrentPos,
690 0x11b,
691 nTagCount,
692 nResolutionUnit);
693
694
695
696
697 return bResult;
698 } // getTiffInfo
699
700
701 /**
702 * Returns the index into the buffer of the specified tag
703 * Return -1 if tag not found
704 */
705 protected int findTag(byte anIFDBuffer[], int nTagCount, int nTag)
706 {
707 // Search thru the tags for the specified tag
708 // Tags immediately follow count
709 int nIndex = 0;
710 for (int i=0; i<nTagCount; i++)
711 {
712 if (getInt(anIFDBuffer, nIndex, 2) == nTag)
713 {
714 // Tag found
715 return nIndex;
716 }
717
718 // Set index to the start of the next tag
719 nIndex += 12;
720 } // for i
721
722 // Tag not found
723 return -1;
724 } // findTag
725
726 /**
727 *
728 *
729 */
730 protected int getTiffResolution(byte anIFD[],
731 FlexXRandomAccessFile fInputFile,
732 long lCurrentPos,
733 int nTag,
734 int nTagCount,
735 int nResolutionUnit)
736 {
737 int nResolution = 0;
738
739 // Get the requested resolution tag
740 int nTagIndex = findTag(anIFD, nTagCount, nTag);
741 if (nTagIndex != -1)
742 {
743 // First get the value offset relative from the beginning of the image
744 int nValueOffset = getInt(anIFD, nTagIndex + 8, 4);
745
746 // Get the actual values (8 bytes)
747 if (goToFileLocation(fInputFile, lCurrentPos + (long)nValueOffset) == true)
748 {
749 byte anValue[] = readBytes(fInputFile, 8);
750
751 // This tag is rational meaning that two 4-byte numbers, the numerator and denominator
752 int nNumerator = getInt(anValue, 0, 4);
753 int nDenominator = getInt(anValue, 4, 4);
754
755 if (nDenominator != 0)
756 {
757 nResolution = nNumerator/nDenominator;
758 }
759
760 if (nResolutionUnit == 3)
761 {
762 // Convert from centimeters to inches
763 nResolution = (nResolution * 254)/100;
764 }
765 } // goToFileLocation if
766 } // nTagIndex if
767
768 return nResolution;
769 } // getTiffResolution
770
771 /**
772 *
773 *
774 */
775 protected boolean getJfifInfo(FlexXRandomAccessFile fInputFile, long nBlockStartPos)
776 {
777 boolean bResult = true;
778
779 // Obtain the image width/height. This is not contained in the JFIF header which
780 // may only have the image resolutions or aspect ratios. Must go to the JPEG header
781 // for width/height.
782
783 // FFD8FFE0xxJFIF identifies the start of a JFIF image
784 //
785 // FFD8 - SOI (2 bytes 0-1)
786 // FFE0 - APPO (2 bytes 2-3)
787 // xx - Byte length - remaining number of bytes in this header(2 bytes 4-5)
788 // JFIFx - Identifier (5 bytes 6-10)
789 // xx - Version (2 bytes 11-12)
790 // x - Units (0=none so x&y densities specify pixel aspect ratios; 1=inches; 2=cm) (1 byte 13)
791 // xx - X density (2 bytes 14-15)
792 // xx - Y density (2 bytes 16-17)
793 // x - X thumbnail pixel count (1 byte 18)
794 // x - Y thumbnail pixel count (1 byte 19)
795 // FFDB - End of this header (2 bytes 20-21)
796 //
797 // Scan the header for a field identified by FFC2 or FFC0
798 // Skip 3 bytes to the start of the 2-byte height and width fields
799
800 // First go back to the start of the image block
801 if (goToFileLocation(fInputFile, nBlockStartPos) == false)
802 {
803 return false;
804 }
805
806 // Get the Image File Header
807 byte anHeader[] = readBytes(fInputFile, 256);
808 if (anHeader == null)
809 {
810 return false;
811 }
812
813 // Get the resolutions from the APPO header if they exist
814 // Determine first if the fields contain resolution (density) or aspect ratio
815 // Ignore if aspect ratio specified
816 if ((anHeader[13] == (byte)0x01) || (anHeader[13] == (byte)0x02))
817 {
818 int nXRes = getInt(anHeader, 14, 2);
819 int nYRes = getInt(anHeader, 16, 2);
820
821 // If it is in cm, convert to inches
822 if (anHeader[13] == (byte)0x02)
823 {
824 nXRes = (nXRes * 254)/100;
825 nYRes = (nYRes * 254)/100;
826 }
827 } // anHeader if
828
829 // Now search for the width and height headers
830 int nIndex = -1;
831 for (int i=0; i<anHeader.length; i++)
832 {
833 // Check for FFC0 or FFC2 headers
834 if ((anHeader[i] == (byte)0xff) &&
835 ((anHeader[i+1] == (byte)0xc2) || (anHeader[i+1] == (byte)0xc0)))
836 {
837 nIndex = i;
838 break;
839 }
840 } // for
841
842 if ((nIndex == -1) || (nIndex + 7 > anHeader.length))
843 {
844 // Field not found
845 return false;
846 }
847
848 // Get the height
849 int nHeight = getInt(anHeader[nIndex + 5], anHeader[nIndex + 6]);
850 nImageHeight = nHeight;
851
852 // Get the width
853 int nWidth = getInt(anHeader[nIndex + 7], anHeader[nIndex + 8]);
854 nImageWidth = nWidth;
855
856 return bResult;
857 } // getJfifInfo
858
859
860 /**
861 *
862 *
863 */
864 protected int getInt(byte bHi, byte bLo)
865 {
866 int nParam = 0;
867
868 nParam = bHi;
869 nParam = nParam << 8;
870 int nTemp = bLo; // Java treats the byte as signed and the sign is carried over
871 nTemp = nTemp & 0xff; // into the int. Thus must be masked.
872 nParam = nParam | nTemp;
873 nParam = nParam & 0xffff;
874
875 return nParam;
876 } // getInt
877
878
879
880 /**
881 *
882 *
883 */
884 protected String getSubString(String sInput, int nStart, int nEnd)
885 {
886 String sResult = null;
887 try
888 {
889 sResult = sInput.substring(nStart, nEnd);
890 }
891
892 catch(StringIndexOutOfBoundsException e)
893 {
894 printDiagnosticMessage("Error parsing info data" + e.toString());
895 }
896
897 return sResult;
898 } // getSubString
899
900 /**
901 *
902 *
903 */
904 protected int getInt(String sNumber)
905 {
906 int nResult = 0;
907
908 try
909 {
910 nResult = Integer.parseInt(sNumber.trim());
911 }
912
913 catch(NumberFormatException e)
914 {
915 printDiagnosticMessage("Error parsing info number" + e.toString());
916 }
917
918 return nResult;
919 } // getInt
920
921 /**
922 *
923 *
924 */
925 protected FlexXRandomAccessFile openFile(String sName, String sMode)
926 {
927 FlexXRandomAccessFile refRAF = null;
928
929 try
930 {
931 refRAF = new FlexXRandomAccessFile(sName, sMode);
932 }
933
934 catch(IOException e)
935 {
936 printDiagnosticMessage("Error opening file " + sName + ": " + e.toString());
937 bError = true;
938 }
939
940 catch(SecurityException e)
941 {
942 printDiagnosticMessage("Error opening file " + sName + ": " + e.toString());
943 bError = true;
944 }
945
946 return refRAF;
947 } // openFile
948
949
950 /**
951 * Reads the 4-byte integer at the current file location
952 *
953 */
954 protected int readInt(FlexXRandomAccessFile fFile)
955 {
956 int nBytesRead = -1;
957
958 try
959 {
960 nBytesRead = fFile.readInt();
961 }
962
963 catch(EOFException e)
964 {
965 printDiagnosticMessage("Error reading integer " + ": " + e.toString());
966 nBytesRead = -1;
967 }
968
969 catch(IOException e)
970 {
971 printDiagnosticMessage("Error reading integer " + ": " + e.toString());
972 nBytesRead = -1;
973 }
974
975 return nBytesRead;
976 } // readInt
977
978
979 /**
980 * Seek to the specified offset from the current file position
981 *
982 */
983 protected boolean goToPointerLocation(FlexXRandomAccessFile fFile, int nOffset)
984 {
985 boolean bResult = true;
986
987 try
988 {
989 fFile.skipBytes(nOffset);
990 }
991
992 catch(EOFException e)
993 {
994 printDiagnosticMessage("Error seeking to pointer " + ": " + e.toString());
995 bResult = false;
996 }
997
998 catch(IOException e)
999 {
1000 printDiagnosticMessage("Error seeking to pointer " + ": " + e.toString());
1001 bResult = false;
1002 }
1003
1004
1005 return bResult;
1006 } // goToPointerLocation
1007
1008 /**
1009 *
1010 *
1011 */
1012 protected boolean checkFormat(FlexXRandomAccessFile fFile)
1013 {
1014 boolean bResult = true;
1015 sImageType = "";
1016
1017 // Table contains: the identifier to search for, the offset into the image
1018 // to search from, the type to give to the image once found, and the subtype
1019 // for a given type.
1020 String[][] saIdentifiers = {{"GIF87a" , "0" , "gif", "0"},
1021 {"GIF89a" , "0" , "gif", "0"},
1022 {"%!PS" , "0" , "eps", "0"},
1023 {"MM" , "0" , "tif", "0"},
1024 {"\\FFD8FFE0", "0" , "jpg", "0"},
1025 {"\\001102FF", "10", "pic", "0"}, // PICT version 2
1026 {"\\1101" , "10", "pic", "1"}}; // PICT version 1
1027
1028 // Save the current file position
1029 long nStartPos = getCurrentPos(fFile);
1030 int nIdLength = 0;
1031
1032 // Read 'n' bytes from the current file position
1033 byte anData[] = readBytes(fFile, 32);
1034 if (anData == null)
1035 {
1036 bResult = false;
1037 }
1038
1039 for (int i=0; i<saIdentifiers.length; i++)
1040 {
1041 String sType = saIdentifiers[i][0];
1042 String sOffset = saIdentifiers[i][1];
1043 int nOffset = Integer.parseInt(sOffset);
1044
1045 if (sType.startsWith("\\") == false)
1046 {
1047 // It's a string
1048 // Convert the file data to a string and compare
1049 // it to the input type string
1050 nIdLength = sType.length() + nOffset;
1051
1052 String sFileData = new String(anData);
1053 if (sFileData.startsWith(sType, nOffset) == true)
1054 {
1055 sImageType = saIdentifiers[i][2];
1056 sImageSubType = saIdentifiers[i][3];
1057 bResult = true;
1058 break;
1059 }
1060 } // sType if
1061
1062 else
1063 {
1064 // It's binary (hex)
1065 String sIdData = saIdentifiers[i][0];
1066 boolean bMatch = true;
1067 int j = 1;
1068 int k = 0;
1069
1070 // Match each byte in file data to hex chars in Id's
1071 while (j+1 < sIdData.length())
1072 {
1073 // Extract chars two at a time and convert hex to binary
1074 String sChars = sIdData.substring(j, j+2);
1075
1076 // Convert hex to binary byte
1077 byte byteHex = Integer.decode("#" + sChars).byteValue();
1078
1079 // Do they match?
1080 if (anData[k + nOffset] != byteHex)
1081 {
1082 // Mismatch so quit
1083 bMatch = false;
1084 break;
1085 }
1086
1087 // Next two characters
1088 j += 2;
1089 k += 1;
1090 } // while
1091
1092 if (bMatch == true)
1093 {
1094 sImageType = saIdentifiers[i][2];
1095 sImageSubType = saIdentifiers[i][3];
1096 nIdLength = k + nOffset;
1097 bResult = true;
1098 break;
1099 }
1100 } // sType else
1101 } // for i
1102
1103 // Reset the current file pos to the index after the ID
1104 goToFileLocation(fFile ,nStartPos + nIdLength);
1105
1106 return bResult;
1107 } // isFormat
1108
1109
1110 /**
1111 *
1112 *
1113 */
1114 protected byte[] readBytes(FlexXRandomAccessFile fFile, int nCount)
1115 {
1116 int nRead = -1;
1117 byte anInputData[] = new byte[nCount];
1118
1119 try
1120 {
1121 nRead = fFile.read(anInputData);
1122 }
1123
1124 catch(IOException e)
1125 {
1126 printDiagnosticMessage("Error reading bytes " + ": " + e.toString());
1127 anInputData = null;
1128 }
1129
1130 if (nRead == -1)
1131 {
1132 // No more data, end of file
1133 anInputData = null;
1134 }
1135
1136 return anInputData;
1137 } // readBytes
1138
1139
1140 /**
1141 *
1142 *
1143 */
1144 protected long getCurrentPos(FlexXRandomAccessFile fFile)
1145 {
1146 try
1147 {
1148 return fFile.getFilePointer();
1149 }
1150
1151 catch(IOException e)
1152 {
1153 printDiagnosticMessage("Error getting file position " + ": " + e.toString());
1154 return -1;
1155 }
1156 } // getCurrentPos
1157
1158
1159 /**
1160 *
1161 *
1162 */
1163 protected boolean extractAndCopyImage(FlexXRandomAccessFile fParent,
1164 long nFilePos,
1165 int nImageLength,
1166 String sThumbnailFile,
1167 boolean bDonotWrite)
1168 {
1169 boolean bResult = true;
1170 FlexXRandomAccessFile fThumbnail = null;
1171
1172 // Create the thumbnail file if not in "DoNotWrite" mode
1173 if (bDonotWrite == false)
1174 {
1175 fThumbnail = openFile(sThumbnailFile, "rw");
1176 if (fThumbnail == null)
1177 {
1178 bResult = false;
1179 }
1180 }
1181
1182 // Seek to the start of the image
1183 if (goToFileLocation(fParent, nFilePos) == false)
1184 {
1185 bResult = false;
1186 }
1187
1188 // Copy the image
1189 if (copyBytes(fParent, fThumbnail, nImageLength) == false)
1190 {
1191 bResult = false;
1192 }
1193
1194 closeFile(fThumbnail);
1195 return bResult;
1196 } // extractAndCopyImage
1197
1198
1199 /**
1200 * Seek to offset from beginning of file
1201 *
1202 */
1203 protected boolean goToFileLocation(FlexXRandomAccessFile fFile, long nFilePos)
1204 {
1205 boolean bResult = true;
1206
1207 try
1208 {
1209 fFile.seek(nFilePos);
1210 }
1211
1212 catch(IOException e)
1213 {
1214 printDiagnosticMessage("Error seeking to offset " + ": " + e.toString());
1215 bResult = false;
1216 }
1217
1218 return bResult;
1219 } // goToFileLocation
1220
1221
1222
1223 /**
1224 *
1225 *
1226 */
1227 protected boolean copyBytes(FlexXRandomAccessFile fFrom,
1228 FlexXRandomAccessFile fTo,
1229 int nCount)
1230 {
1231 boolean bDone = false;
1232 boolean bReadAll = false;
1233 int nBytesToRead = 0;
1234 int nRead = -1;
1235 int nBuffSize = 512;
1236 byte anData[] = new byte[nBuffSize];
1237
1238 // nCount = -1 indicates that the remainder of the file
1239 // should be read
1240 if (nCount == -1)
1241 {
1242 bReadAll = true;
1243 }
1244
1245 // Read/write until nCount bytes, or end-of-file, or error
1246 while (true)
1247 {
1248 // Set the buffer size
1249 if ((bReadAll == false) && (nCount <= nBuffSize))
1250 {
1251 nBytesToRead = nCount;
1252 bDone = true;
1253 }
1254
1255 else
1256 {
1257 nBytesToRead = nBuffSize;
1258 }
1259
1260 // Read a block and write it to the "to" file
1261 try
1262 {
1263 nRead = fFrom.read(anData, 0, nBytesToRead);
1264 if (nRead == -1)
1265 {
1266 // No more data, end of file
1267 return true;
1268 }
1269
1270 // Write to the output file unless in no-write mode
1271 if (fTo != null)
1272 {
1273 fTo.write(anData, 0, nRead);
1274 }
1275
1276 if (bDone == true)
1277 {
1278 // All bytes transfered
1279 return true;
1280 }
1281 } // try
1282
1283 catch(IOException e)
1284 {
1285 printDiagnosticMessage("Error reading/writing bytes " + ": " + e.toString());
1286 return false;
1287 }
1288
1289 // Decrement the count of remaining bytes to transfer
1290 nCount -= nRead;
1291 } // while true
1292 } // copyBytes
1293
1294 /**
1295 *
1296 *
1297 */
1298 protected boolean closeFile(FlexXRandomAccessFile fFile)
1299 {
1300 if (fFile == null)
1301 {
1302 return true;
1303 }
1304
1305 try
1306 {
1307 fFile.close();
1308 }
1309
1310 catch(IOException e)
1311 {
1312 printDiagnosticMessage("Error closing file " + ": " + e.toString());
1313 return false;
1314 }
1315
1316 return true;
1317 } // closeFile
1318
1319
1320 /**
1321 *
1322 *
1323 */
1324 public Vector getMap(FlexXRandomAccessFile fInputFile)
1325 {
1326 Vector vImagePointer = new Vector();
1327 nAdditionalItems = 0;
1328
1329 // Get the first 16 bytes of the file
1330 byte anData[] = readBytes(fInputFile, 16);
1331 if (anData == null)
1332 {
1333 return null;
1334 }
1335
1336 // Get the pointer to the map
1337 int nMapPointer = getInt(anData, 4, 4);
1338 if (nMapPointer == 0)
1339 {
1340 return null;
1341 }
1342
1343 // Get the byte size of the map
1344 int nMapSize = getInt(anData, 14, 2);
1345 if ((nMapSize == 0) || (nMapSize > 1000)) // Maximum size was arbitrarily selected.
1346 { // Concern is that for an illegal file this
1347 return null; // may get huge, causing memory problems.
1348 }
1349
1350 // Go to the map block
1351 if (goToFileLocation(fInputFile, nMapPointer) == false)
1352 {
1353 return null;
1354 }
1355
1356 // Read the full map data block
1357 byte anMapData[] = readBytes(fInputFile, nMapSize);
1358 if (anMapData == null)
1359 {
1360 return null;
1361 }
1362
1363 // Get the count of named pointers
1364 int nNamedCount = getInt(anMapData, 28, 2);
1365 if (nNamedCount == 0)
1366 {
1367 return null;
1368 }
1369
1370 nNamedCount += 1;
1371 int nNamedBaseIndex = 30;
1372
1373 // Get each named pointer and its associated image block pointers
1374 int nItemOffset = nNamedBaseIndex;
1375 for (int i=0; i<nNamedCount; i++)
1376 {
1377 // Get the name (4 bytes)
1378 byte abName[] = new byte[4];
1379 for (int j=0; j<4; j++)
1380 {
1381 abName[j] = (byte)anMapData[nItemOffset + j];
1382 }
1383
1384 // Convert the name to a string
1385 String sName = new String(abName);
1386
1387 // Get the count of additional items (2 bytes)
1388 nAdditionalItems = getInt(anMapData, nItemOffset + 4,2);
1389
1390 // Get the offset to the image block pointer
1391 int nImageBlockPtr = getInt(anMapData, nItemOffset + 6, 2);
1392
1393 // Get the Image pointer
1394 int nImagePointer = getInt(anMapData, nNamedBaseIndex + nImageBlockPtr + 2, 4);
1395
1396 // Get the magic number (the last item doesnt have one)
1397 int nMagicNumber = -1;
1398 if (i < nNamedCount - 1)
1399 {
1400 nMagicNumber = getInt(anMapData, nNamedBaseIndex + nImageBlockPtr + 10, 2);
1401 }
1402
1403 // Put the name and pointer in vector
1404 vImagePointer.addElement(new ImagePointer(sName,
1405 nImagePointer,
1406 0,
1407 nMagicNumber));
1408
1409 // Get any additional pointers associated with the named image pointer
1410 if (nAdditionalItems > 0)
1411 {
1412 int nPtrOffset = 12;
1413 for (int k=0; k<nAdditionalItems; k++)
1414 {
1415 // Get the image pointer and magic number
1416 nImagePointer = getInt(anMapData, nNamedBaseIndex + nImageBlockPtr + nPtrOffset + 2, 4);
1417 nMagicNumber = getInt(anMapData, nNamedBaseIndex + nImageBlockPtr + nPtrOffset + 10, 2);
1418
1419 // Add the previous name (these pointers are not named) and pointer to the vector
1420 vImagePointer.addElement(new ImagePointer(sName,
1421 nImagePointer,
1422 k+1,
1423 nMagicNumber));
1424
1425 nPtrOffset += 12;
1426 }
1427 } // nAdditionalItems if
1428
1429 // Go to next item
1430 nItemOffset += 8;
1431 } // for
1432
1433
1434 return vImagePointer;
1435 } // getMap
1436
1437 /**
1438 *
1439 *
1440 */
1441 public int getInt(byte anData[], int nStartIndex, int nCount)
1442 {
1443 int nInt = 0;
1444 if ((nCount + nStartIndex) > anData.length)
1445 {
1446 return 0;
1447 }
1448
1449 for (int i=nStartIndex; i<nStartIndex + nCount; i++)
1450 {
1451 nInt = nInt << 8;
1452 int nTemp = 0;
1453 nTemp = (nTemp | anData[i]) & 0xff;
1454 nInt = nInt | nTemp;
1455 }
1456
1457 nInt = nInt & 0xffffffff;
1458 return nInt;
1459 } // getInt
1460
1461 /**
1462 *
1463 *
1464 */
1465 protected void printMap(Vector vMap)
1466 {
1467 if ((vMap == null) || (vMap.size() == 0))
1468 {
1469 printDiagnosticMessage("No items found for map");
1470 return;
1471 }
1472
1473 for (int i=0; i<vMap.size(); i++)
1474 {
1475 ImagePointer refImagePointer = (ImagePointer)vMap.elementAt(i);
1476 refImagePointer.printMapItem();
1477 }
1478 } // printMap
1479
1480
1481 /**
1482 *
1483 *
1484 */
1485 public int getWidth()
1486 {
1487 return nImageWidth;
1488 }
1489
1490 /**
1491 *
1492 *
1493 */
1494 public int getHeight()
1495 {
1496 return nImageHeight;
1497 }
1498
1499 /**
1500 *
1501 *
1502 */
1503 public int getXResolution()
1504 {
1505 return nImageXResolution;
1506 }
1507
1508 /**
1509 *
1510 *
1511 */
1512 public int getYResolution()
1513 {
1514 return nImageYResolution;
1515 }
1516
1517 /**
1518 *
1519 *
1520 */
1521 public long getFilesize()
1522 {
1523 return nFilesize;
1524 }
1525
1526 /**
1527 *
1528 *
1529 */
1530 public String getType()
1531 {
1532 return sImageType;
1533 }
1534
1535 /**
1536 *
1537 *
1538 */
1539 protected void printDiagnosticMessage(String sMsg)
1540 {
1541 if (bTest == false)
1542 {
1543 Diagnostic.trace(Diagnostic.APPSERVER_IMPORT, sMsg);
1544 }
1545
1546 else
1547 {
1548 System.out.println(sMsg);
1549 }
1550 }// printDiagnosticMessage
1551
1552 } // FPOExtact
1553
1554
1555
1556