Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/flexstor/flexdbserver/services/asset/iptc/TiffFile.java


1   /*
2    * TiffFile.java
3    *
4    * Copyright $Date: 2003/08/11 02:22:34 $ 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.iptc;
12  
13  import java.io.IOException;
14  import java.util.Hashtable;
15  
16  import com.flexstor.common.io.xfile.FlexXFile;
17  import com.flexstor.common.io.xfile.FlexXRandomAccessFile;
18  
19  
20  /**
21  *
22  *
23  */
24  public class TiffFile 
25  {
26      protected FlexXRandomAccessFile refXRandomFile = null;
27      protected Hashtable htTagData       = null;
28      protected Hashtable htExcludeTagIds = null;   // User can set the Ids of tags which will be excluded
29      protected Hashtable htIncludeTagIds = null;   // User can set the Ids of tags which will be included
30      protected boolean   bIntelFormat    = true;
31      protected long      nSavedPointer   = 0;
32      
33      // ***** DEBUG switch *****
34      protected boolean   bDebug   = false;
35      
36      /**
37      *
38      *
39      */
40      public TiffFile(String sFilename)
41      {
42         
43          htTagData        = new Hashtable();
44          htExcludeTagIds  = new Hashtable();
45          htIncludeTagIds  = new Hashtable();
46          
47          FlexXFile refXFile = new FlexXFile(sFilename);
48          openFile(refXFile);
49      } // constructor 1
50      
51      
52      /**
53      *
54      *
55      */
56      public TiffFile(FlexXFile refXFile)
57      {
58         
59          htTagData        = new Hashtable();
60          htExcludeTagIds  = new Hashtable();
61          htIncludeTagIds  = new Hashtable();
62          
63          openFile(refXFile);
64      } // constructor 2
65      
66      
67      /**
68      *
69      *
70      */
71      public boolean loadTagDirectory()
72      {
73          
74          // Is the file open?
75          if (refXRandomFile == null)
76          {
77              ABNError("File not accessable");
78              return false;
79          }
80          
81          // Read the first 8 bytes of the file. This is the image file header
82          //      bytes 0-1   byte order (0x49, 0x49 = Intel; 0x4d, 0x4d = Motorola)
83          //      bytes 2-3   tiff file type identifier (ignore)
84          //      bytes 4-7   The offset from file start (0) of the first IFD
85          byte[] anHeader = readBytes(8);
86          if (anHeader == null)
87          {
88              ABNError("Could not read image file header");
89              closeFile();
90              return false;
91          }
92          
93          // The first 2 bytes identify the byte order. Get and save it
94          if ((anHeader[0] == 0x49) && (anHeader[1] == 0x49))
95          {
96              bIntelFormat = true;    // It's Intel format
97              ABNDebug("Intel Format");
98          }
99          
100         else if ((anHeader[0] == 0x4d) && (anHeader[1] == 0x4d))
101         {
102             bIntelFormat = false;   // It's Motorola format
103             ABNDebug("Motorola Format");
104         }
105         
106         else
107         {
108             ABNError("Error: Not TIFF file header");
109             closeFile();
110             return false;
111         }
112         
113         // The next two bytes further identify the TIFF file
114         // Ignore these because the value is not always consistent with the spec
115         
116         // The next 4 byte value is the position from the start of the file of the
117         // first IFD which may be ANYWHERE in the file
118         long nIfdOffset = (long)binaryToInt(anHeader, 4, 4);
119         if (nIfdOffset == -1)
120         {
121             ABNError("Error: Bad IFD offset");
122             closeFile();
123             return false;
124         }
125         
126         // Process the first IFD and any that may follow.  This method may be 
127         // recursive if more than one IFD exists.  The end result should be that
128         // all of the data for the tags found will be put into the hashtable, htTagData,
129         // which can be accessed thru the public method getTagData(nTagId)
130         if (processIFD(nIfdOffset) == false)
131         {
132             closeFile();
133             return false;
134         }
135         
136         closeFile();
137         return true;
138     } // loadTagDirectory
139     
140     
141     /**
142     *
143     *
144     */
145     public byte[] getTagData(int nTagID)
146     {
147         return (byte[])htTagData.get(new Integer(nTagID));
148     } // getTagData
149     
150     
151     /**
152     *
153     *
154     */
155     public boolean isIntel()
156     {
157         return bIntelFormat;
158     }
159     
160     
161     /**
162     *
163     *
164     */
165     public FlexXRandomAccessFile getFileReference()
166     {
167         return refXRandomFile;
168     }
169     
170     
171     /**
172     *   The Tad Id exclude table can contain the id of those tags that will NOT be parsed
173     *
174     */
175     public void setExcludeTagIdTable(Hashtable htTable)
176     {
177         htExcludeTagIds = htTable;
178     }
179     
180     
181     /**
182     *
183     *
184     */
185     public Hashtable getExcludeTagIdTable()
186     {
187         return htExcludeTagIds;
188     }
189     
190     
191     /**
192     *
193     *
194     */
195     public boolean addExcludeTagId(int nTagId)
196     {
197         if (htExcludeTagIds == null)
198         {
199             return false;
200         }
201         
202         htExcludeTagIds.put(new Integer(nTagId), new Integer(nTagId));
203         return true;
204     }
205     
206  
207     /**
208     *
209     *
210     */
211     public boolean removeExcludeTagId(int nTagId)
212     {
213         if (htExcludeTagIds == null)
214         {
215             return false;
216         }
217         
218         htExcludeTagIds.remove(new Integer(nTagId));
219         return true;
220     }
221     
222     
223     /**
224     *   The Tad Id include table can contain the id of those tags that will NOT be parsed
225     *
226     */
227     public void setIncludeTagIdTable(Hashtable htTable)
228     {
229         htIncludeTagIds = htTable;
230     }
231     
232     
233     /**
234     *
235     *
236     */
237     public Hashtable getIncludeTagIdTable()
238     {
239         return htIncludeTagIds;
240     }
241     
242     
243     /**
244     *
245     *
246     */
247     public boolean addIncludeTagId(int nTagId)
248     {
249         if (htIncludeTagIds == null)
250         {
251             return false;
252         }
253         
254         htIncludeTagIds.put(new Integer(nTagId), new Integer(nTagId));
255         return true;
256     }
257     
258  
259     /**
260     *
261     *
262     */
263     public boolean removeIncludeTagId(int nTagId)
264     {
265         if (htIncludeTagIds == null)
266         {
267             return false;
268         }
269         
270         htIncludeTagIds.remove(new Integer(nTagId));
271         return true;
272     }
273     
274     /**
275     *
276     *
277     */
278     protected boolean isTagExcluded(int nTagId)
279     {
280         if (htExcludeTagIds == null)
281         {
282             return false;
283         }
284         
285         return htExcludeTagIds.containsKey(new Integer(nTagId));     
286     }
287     
288     
289     /**
290     *
291     *
292     */
293     protected boolean isTagIncluded(int nTagId)
294     {
295         if (htIncludeTagIds == null)
296         {
297             return true;
298         }
299         
300         if (htIncludeTagIds.isEmpty() == true)
301         {
302             // If it is empty then assume all are included
303             return true;
304         }
305         
306         return htIncludeTagIds.containsKey(new Integer(nTagId));     
307     }
308     
309     
310     /**
311     *
312     *
313     */
314     protected boolean processIFD(long nIfdOffset)
315     {
316         boolean bResult = true;
317         
318         // The IFD (Image File Directory) contains 1 or more tags followed by
319         // an optional 0-based offset to the next IFD
320         //  bytes n+0,  n+1      number of tags  (2  bytes)
321         //  bytes n+2 - n+14     first tag       (12 bytes)
322         //  bytes n+15- n+27     second tag      (12 bytes)
323         //  ...
324         //  bytes n+2+number-of-tags*12     Offset to next IFD  (4 bytes)
325         //      Note: Offset = 0000 if no further IFDs
326         
327         // Seek to the IFD
328         if (goToOffset(nIfdOffset) == false)
329         {
330             ABNError("Could not seek to IFD offset: " + nIfdOffset);
331             return false;
332         }
333         
334         // Read the first 2-bytes of the IFD.  This contains the number of
335         // tags in this IFD
336         byte[] anIFDBuffer = readBytes(2);
337         if (anIFDBuffer == null)
338         {
339             ABNError("Could not read tag count");
340             return false;
341         }
342         
343         int nTagCount = binaryToInt(anIFDBuffer, 0, 2);
344         if (nTagCount < 0)
345         {
346             ABNError("Invalid tag count");
347             return false;
348         }
349         
350         // Process the tags for this IFD
351         if (processTags(nTagCount) == false)
352         {
353             ABNError("Process tags error");
354         }
355         
356         // Check for another IFD
357         // Restore the pointer to the end of the just processed IFD
358         if (restorePointer(nSavedPointer) == false)
359         {
360             ABNError("Could not restore file pointer position");
361             return false;
362         }
363         
364         anIFDBuffer = readBytes(4);
365         if (anIFDBuffer == null)
366         {
367             ABNError("Could not read next IFD offset");
368             return false;
369         }
370         
371         int nNextIfdOffset = binaryToInt(anIFDBuffer, 0, 4);
372         if (nNextIfdOffset == -1)
373         {
374             ABNError("Next IFD offset is invalid");
375             return false;
376         }
377         
378         // If the offset is zero, there are no further IFDs
379         if (nNextIfdOffset != 0)
380         {
381             // Recursively process the next IFD
382             bResult = processIFD(nNextIfdOffset);
383         }
384         
385         return bResult;
386     } // processIFD
387     
388     
389     /**
390     *
391     *
392     */
393     protected boolean processTags(int nTagCount)
394     {
395         boolean bResult = true;
396             
397         // Read all of the raw tags for this IFD
398         // There are nTagCount 12-bytes tags in the IFD
399         // Read them from the file
400         byte[] anTags = readBytes(12*nTagCount);
401         if (anTags == null)
402         {
403             ABNError("Could not read tags");
404             return false;
405         }
406         
407         // Save the current file pointer to be restored later after processing this IFD
408         nSavedPointer = savePointer();
409         
410         // Read and parse each tag
411         byte[] anRawTag = new byte[12];
412         for (int i=0; i<12*nTagCount; i+=12)
413         {
414             // Get each 12-byte chunk from the tag buffer
415             System.arraycopy(anTags, i, anRawTag, 0, 12);    
416             
417             // Parse the tag
418             if (parseTag(anRawTag) == false)
419             {
420                 ABNError("Error parsing tag");
421                 bResult = false;
422                 break;
423             }
424         } // for i
425         
426         return bResult;
427     } // processTags
428     
429     
430     
431     /**
432     *
433     *
434     */
435     protected boolean parseTag(byte[] anTagBuffer)
436     {
437         boolean bResult = true;
438 
439         // Each tag consists of the following 12 bytes:
440         //  bytes 0-1   Tag id                  (2 bytes)
441         //  bytes 2-3   Field type              (2 bytes)
442         //  bytes 4-7   Count of type values    (4 bytes)
443         //  bytes 8-11  The value offset of     (4 bytes)   
444         //              the value itself if it
445         //              fits in 4 bytes (offset is from start of file)
446         //
447         
448         // Get the tag id (bytes 0,1)
449         int nTagId = binaryToInt(anTagBuffer, 0, 2);
450         if (nTagId == -1)
451         {
452             ABNError("Could not read tag ID");
453             return false;
454         }
455         
456         // The user can specify a specific tag or group of tags to parse
457         // If the include hash table is not empty (i.e. at least one include tag
458         // was specified, then if this tag is not in that table, skip it.
459         if (isTagIncluded(nTagId) == false)
460         {
461             ABNDebug("Tag " + nTagId  + " is not in exclude set and user specified at least one id for include.");
462             return true;
463         }
464         
465         // The user can specify any tag id as excludable (see public methods)
466         // If this tag is in the exclude hash table, then skip it.
467         if (isTagExcluded(nTagId) == true)
468         {
469             ABNDebug("Tag " + nTagId  + " is excluded");
470             return true;
471         }
472         
473         // Get the field type
474         int nFieldType = binaryToInt(anTagBuffer, 2, 2);
475         if (nFieldType == -1)
476         {
477             ABNError("Could not read tag field type");
478             return false;
479         }
480         
481         if (nFieldType > 8)
482         {
483             // Field type is not in our list, exit but go on to next tag
484             ABNError("Unknown tag field type: " + nFieldType);
485             return true;
486         }
487         
488         
489         // Based on the field type determine the number of bytes per value.
490         // Types:
491         //  1 = BYTE        1 - byte
492         //  2 = ASCII       1 - byte
493         //  3 = SHORT       2 - bytes
494         //  4 = LONG        4 - bytes
495         //  5 = RATIONAL    8 - bytes (2 LONGS, num/den)
496         //  7 = UNDEFINED   1 - byte  (NOTE: in the following table, we are setting the value size to zero
497         //                                   because the sample files have large many bytes at the offset
498         //                                   and, at this point, we aren't interested in it)
499         byte[] anBytesPerValueTable = {0, 1, 1, 2, 4, 8, 0, 0};
500         int nBytesPerValue = anBytesPerValueTable[nFieldType];
501         if (nBytesPerValue == 0)
502         {
503             // Field type is not in our list, exit but go on to next tag
504             ABNError("Unknown tag field type");
505             return true;
506         }
507         
508         // The next 4-bytes contain the number of values of the indicated type
509         int nValueCount = binaryToInt(anTagBuffer, 4, 4);
510         if (nValueCount == -1)
511         {
512             ABNError("Could not read value count");
513             return false;
514         }
515         
516         // The next 4-bytes are the value offset.  If the total value fits in the value offset
517         // (4 bytes or less) then it is placed left-justified in the value offset.  Otherwise,
518         // the value-offset represents an offset in the file to the location that
519         // the value is stored.
520         int nTotalByteCount = nValueCount * nBytesPerValue;
521         ABNDebug("Tag ID: " + nTagId + ",  Byte count: " + nTotalByteCount);
522         byte[] anTagValue = new byte[nTotalByteCount];
523         if (nTotalByteCount <= 4)
524         {
525             // The value offset is the value itself
526             // Copy it to the tag value array
527             System.arraycopy(anTagBuffer, 8, anTagValue, 0, nTotalByteCount);                
528         }
529         
530         else
531         {
532             // The value offset is indeed the offset to the actual data
533             // Get the offset
534             int nValueOffset = binaryToInt(anTagBuffer, 8, 4);
535             if (nValueOffset == -1)
536             {
537                 ABNError("Could not read tag value offset");
538                 return false;
539             }
540             
541             // Go to the file offset and read the value to the tag value array
542             if (goToOffset(nValueOffset) == false)
543             {
544                 ABNError("Could not seek to tag value offset: " + nValueOffset);
545                 return false;
546             }
547             
548             // Read the bytes in the file at the offset specified in the tag
549             anTagValue = readBytes(nTotalByteCount);
550             if (anTagValue == null)
551             {
552                 ABNError("Could not read value at offset " + nValueOffset + " for tag " + nTagId);
553                 return false;
554             }
555             
556         } // nTotalByteCount else
557         
558         // We have all the info from this tag we need so put it in the hashtable
559         // Key = tag id, value = byte array containing the tag value
560         htTagData.put(new Integer(nTagId), anTagValue);
561             
562         return bResult;
563     } // parseTag
564     
565     
566     /**
567     *
568     *
569     */
570     protected int binaryToInt(byte[] anBuffer, int nStartIndex, int nLength)
571     {
572         int nResult = -1;
573         if ((nStartIndex + nLength) > anBuffer.length)
574         {
575             ABNError("Array out of bounds in binaryToInt()");
576             return nResult;
577         }
578         
579         byte[] anTemp = new byte[nLength];
580         if (isIntel() == true)
581         {            
582             // Reverse the byte order for Intel (little-endian)
583             for (int i=0; i<nLength; i++)
584             {
585                 anTemp[i] = anBuffer[nStartIndex + nLength - i - 1];
586             }
587         }
588         
589         else
590         {
591             for (int i=0; i<nLength; i++)
592             {
593                 anTemp[i] = anBuffer[nStartIndex + i];
594             }
595         }
596 
597         nResult = hexBytesToInt(anTemp, 0, nLength);
598         return nResult;
599     } // binaryToInt
600     
601     
602    /** This method converts part of an array of bytes into an integer value.
603     * (For our purposes, we will not exceed four bytes.)
604     * @param abHexBytes The array of bytes.  Some of which we will convert to an int.
605     * @param nStartPos The start of our byte value that will be converted.
606     * @param nLength The number of bytes that will be converted
607     * @return Returns an int value of the specifiec portion of our byte array.
608     * (i.e. {0x1C, 0x2B} would be converted to 7211)
609     */
610    protected int hexBytesToInt (byte abHexBytes[], int nStartPos, int nLength) {
611 
612       // The soon-to-be-computed value of the byte array.
613       int nBytesValue = 0;
614       
615       // The number of bits that need to get shifted is equal to one less than the
616       // number of bytes times eight.  (i.e. 0x01, 0xC2 : the first byte 0x01 needs
617       // to be shifted 8 bits to the left before it is added to the second byte 0xC2.
618       int nBitShifter = ( (nLength - 1) * 8 );
619 
620       // process each of the bytes in turn.
621       for ( int i=0; i < nLength; i++ ) {
622                  //ABNDebug( "byte " + i + " is " + abHexBytes[ nStartPos+i ] + 
623                  //            ", and nBitShifter is " + nBitShifter);
624          // arithmatic is always performed at least at 32-bit precision.  This has the
625          // side effect of un-signing our signed byte before we bitshift.
626          nBytesValue |= (( abHexBytes[ nStartPos+i ] & 0x000000ff ) << nBitShifter );
627          // Each byte we process, we need to adjust the distance the bits are shifted.
628          nBitShifter = nBitShifter - 8;
629       }
630       return nBytesValue;
631    } // hexBytesToInt
632 
633     
634     
635     
636     /**
637     *
638     *
639     */
640     protected void openFile(FlexXFile refXFile)
641     {
642     
643         try
644         {
645             refXRandomFile = new FlexXRandomAccessFile(refXFile, "r");    
646         }
647         
648         catch(IOException ioe)
649         {
650             ABNError("Could not open random access file." + ioe.toString());
651         }
652     } // openFile
653     
654     
655     /**
656     *
657     *
658     */
659     protected void closeFile()
660     {
661         if (refXRandomFile == null)
662         {
663             ABNError("Could not close random access TIFF file.");
664             return;
665         }
666         
667         try
668         {
669             refXRandomFile.close();
670         }
671         
672         catch(IOException ioe)
673         {
674             ABNError("Could not close random access file." + ioe.toString());
675         }    
676     } // closeFile
677     
678     
679     /**
680     *
681     *
682     */
683     protected byte[] readBytes(int nCount)
684     {
685         byte[] anBytes = new byte[nCount];
686         
687         if (refXRandomFile == null)
688         {
689             ABNError("File reference is null pointer - readBytes");
690             return null;
691         }
692         
693         try
694         {
695             int nStatus = refXRandomFile.read(anBytes); 
696             if (nStatus == -1)
697             {
698                 ABNError("Could not read. End of file");
699                 return null;
700             }
701         }
702         
703         catch (IOException ioe)
704         {
705             ABNError("Error reading file: " + ioe.toString());
706             return null;
707         }
708         
709         return anBytes;
710     } // readBytes
711     
712     
713     /**
714     *
715     *
716     */
717     protected boolean goToOffset(long nOffset)
718     {
719         if (refXRandomFile == null)
720         {
721             ABNError("File reference is a null pointer - goToOffset");
722             return false;
723         }
724         
725         try
726         {
727             refXRandomFile.seek(nOffset);    
728         }
729         
730         catch (IOException ioe)
731         {
732             ABNError("File error in goToOffset: " + ioe.toString());
733             return false;
734         }
735         
736         return true;
737     } // goToOffset
738     
739     
740     /**
741     *
742     *
743     */
744     protected boolean restorePointer(long nFilePosition)
745     {        
746         return goToOffset(nFilePosition);
747     } // restorePointer
748 
749     /**
750     *
751     *
752     */
753     protected long savePointer()
754     {
755         long nPos = -1;
756         if (refXRandomFile == null)
757         {
758             return -1;
759         }
760         
761         try
762         {
763             nPos = refXRandomFile.getFilePointer();
764         }
765         
766         catch (IOException ioe)
767         {
768             ABNError("File error getting position: " + ioe.toString());
769             nPos = -1;
770         }
771         
772         return nPos;
773     } // savePointer
774 
775     
776     /**
777     *  Debug code
778     *
779     */
780     protected void printTagData(byte[] anTagData)
781     {
782         if (bDebug == false)
783         {
784             return;
785         }
786         
787         
788         // Print out the contents of the tag data buffer
789         // Use the hard way since printing as a string does'nt work well due
790         // to the various wierd characters that it may contain.
791         // Replace all chars < 0x02 (except for 0x0d) with an asterisk
792         // Note that the replaced characters are important for this application.
793         // They are the tags.
794         String sBuffer = "";
795         for (int i=0; i<anTagData.length; i++)        
796         {
797            byte nChar = anTagData[i]; 
798            if ((nChar < 0x20) && (nChar != 0x0d))
799            {
800                 nChar = 0x2a;
801            }
802            
803            sBuffer += (char)nChar; 
804         } // for i
805 
806         ABNDebug(sBuffer);
807     } // printTagData
808     
809     
810     /**
811     *
812     *
813     */
814     protected void ABNDebug(String sMsg)
815     {
816         if (bDebug)
817         {
818             System.out.println("TiffFile DEBUG: " + sMsg);
819         }
820     }
821 
822 
823     /**
824     *
825     *
826     */
827     protected void ABNError(String sMsg)
828     {
829         System.out.println("TiffFile: " + sMsg);
830     }
831     
832     
833     
834     } // class TiffFile