package ua.stu.scplib.attribute; import java.io.*; /** * <p>A class that extends {@link com.pixelmed.dicom.BinaryInputStream BinaryInputStream} by adding * the concept of transfer syntaxes, for a (possible) meta information header and a data set.</p> * * <p>Note this class does not automatically switch from meta information header to data set * transfer syntaxes. That is the responsibility of the caller parsing the individual attributes * (such as by reaching the end of the meta information group length, and then calling * {@link #setReadingDataSet() setReadingDataSet()}.</p> * * @see com.pixelmed.dicom.AttributeList * @see com.pixelmed.dicom.DicomOutputStream * * @author dclunie */ public class DicomInputStream extends BinaryInputStream { /***/ private TransferSyntax TransferSyntaxToReadDataSet; /***/ private TransferSyntax TransferSyntaxToReadMetaHeader; /***/ private TransferSyntax TransferSyntaxInUse; /***/ private boolean readingDataSet; /***/ private long byteOffsetOfStartOfData; /** * @param uid * @param tryMeta * @exception IOException */ private void initializeTransferSyntax(String uid,boolean tryMeta) throws IOException { //System.err.println("initializeTransferSyntax: uid="+uid+" tryMeta="+tryMeta); //System.err.println("initializeTransferSyntax: markSupported()="+markSupported()); TransferSyntaxToReadMetaHeader = null; TransferSyntaxToReadDataSet = null; byte b[] = new byte[8]; // First make use of argument that overrides guesswork at transfer syntax ... if (uid != null) { TransferSyntax ts = new TransferSyntax(uid); if (tryMeta) { TransferSyntaxToReadMetaHeader = ts; // specified UID is transfer syntax to read metaheader } else { TransferSyntaxToReadDataSet = ts; // specified UID is transfer syntax to read dataset (there is no metaheader) } } // else transfer syntax has to be determined by either guesswork or metaheader ... if (tryMeta) { //System.err.println("initializeTransferSyntax: looking for preamble"); // test for metaheader prefix after 128 byte preamble if (markSupported()) mark(140); boolean skipSucceeded = true; try { skipInsistently(128); } catch (IOException e) { skipSucceeded=false; } if (skipSucceeded && read(b,0,4) == 4 && new String(b,0,4).equals("DICM")) { //System.err.println("initializeTransferSyntax: detected DICM"); if (TransferSyntaxToReadMetaHeader == null) { // guess only if not specified as an argument //System.err.println("initializeTransferSyntax: trying to guess TransferSyntaxToReadMetaHeader"); if (markSupported()) { mark(8); if (read(b,0,6) == 6) { // the first 6 bytes of the first attribute tag in the metaheader TransferSyntaxToReadMetaHeader = Character.isUpperCase((char)(b[4])) && Character.isUpperCase((char)(b[5])) ? new TransferSyntax(TransferSyntax.ExplicitVRLittleEndian) // standard : new TransferSyntax(TransferSyntax.ImplicitVRLittleEndian); // old draft (e.g. used internally on GE IOS platform) } else { TransferSyntaxToReadMetaHeader = new TransferSyntax(TransferSyntax.ExplicitVRLittleEndian); } reset(); } else { // can't guess since can't rewind ... insist on standard transfer syntax TransferSyntaxToReadMetaHeader = new TransferSyntax(TransferSyntax.ExplicitVRLittleEndian); //System.err.println("initializeTransferSyntax: can't rewind so assuming TransferSyntaxToReadMetaHeader is ExplicitVRLittleEndian"); } } byteOffsetOfStartOfData=132; } else { //System.err.println("initializeTransferSyntax: no preamble"); // no preamble, so rewind and try using the specified transfer syntax (if any) for the dataset instead if (markSupported()) { reset(); TransferSyntaxToReadDataSet = TransferSyntaxToReadMetaHeader; // may be null anyway if no uid argument specified byteOffsetOfStartOfData=0; } else { throw new IOException("Not a DICOM PS 3.10 file - no DICM after preamble in metaheader, and can't rewind input"); } } } // at this point either we have succeeded or failed at finding a metaheader, or we didn't look // so we either have a detected or specified transfer syntax for the metaheader, or the dataset, or nothing at all if (TransferSyntaxToReadDataSet == null && TransferSyntaxToReadMetaHeader == null) { // was not specified as an argument and there is no metaheader //System.err.println("initializeTransferSyntax: having to try and guess transfer syntax"); boolean bigendian = false; boolean explicitvr = false; if (markSupported()) { mark(10); if (read(b,0,8) == 8) { //System.err.print("initializeTransferSyntax: read beginning of first attribute = "+com.pixelmed.utils.HexDump.dump(b)); // examine probable group number ... assume <= 0x00ff if (b[0] < b[1]) bigendian=true; else if (b[0] == 0 && b[1] == 0) { // blech ... group number is zero // no point in looking at element number // as it will probably be zero too (group length) // try the 32 bit value length of implicit vr if (b[4] < b[7]) bigendian=true; } // else little endian //System.err.println("initializeTransferSyntax: bigendian="+bigendian); if (Character.isUpperCase((char)(b[4])) && Character.isUpperCase((char)(b[5]))) explicitvr=true; //System.err.println("initializeTransferSyntax: b[4]="+(char)(b[4])); //System.err.println("initializeTransferSyntax: b[5]="+(char)(b[5])); //System.err.println("initializeTransferSyntax: explicitvr="+explicitvr); } // go back to start of dataset reset(); } // else can't guess or unrecognized ... assume default ImplicitVRLittleEndian (most common without metaheader due to Mallinckrodt CTN default) if (bigendian) if (explicitvr) TransferSyntaxToReadDataSet = new TransferSyntax(TransferSyntax.ExplicitVRBigEndian); else throw new IOException("Not a DICOM file (masquerades as explicit VR big endian)"); else if (explicitvr) TransferSyntaxToReadDataSet = new TransferSyntax(TransferSyntax.ExplicitVRLittleEndian); else TransferSyntaxToReadDataSet = new TransferSyntax(TransferSyntax.ImplicitVRLittleEndian); } if (TransferSyntaxToReadMetaHeader != null) { setReadingMetaHeader(); } else { setReadingDataSet(); } if (TransferSyntaxInUse == null) throw new IOException("Not a DICOM file (or can't detect Transfer Syntax)"); // leaves us positioned at start of group and element tags (for either metaheader or dataset) //System.err.println("initializeTransferSyntax: TransferSyntaxToReadMetaHeader="+TransferSyntaxToReadMetaHeader); //System.err.println("initializeTransferSyntax: TransferSyntaxToReadDataSet="+TransferSyntaxToReadDataSet); //System.err.println("initializeTransferSyntax: TransferSyntaxInUse="+TransferSyntaxInUse); } //public DicomInputStream(String name) throws IOException { // super(name,true); // initializeTransferSyntax("1.2.840.10008.1.2.1",true); //} /** * <p>Construct a stream to read DICOM data sets from the supplied stream.</p> * * <p>Look for a meta information header; if absent guess at a transfer syntax based on the contents.</p> * * @param i the input stream to read from * @exception IOException */ public DicomInputStream(InputStream i) throws IOException { super(i,true); initializeTransferSyntax(null,true); // allow guessing at transfer syntax, try to find metaheader } /** * <p>Construct a stream to read DICOM data sets from the supplied stream.</p> * * <p>Look for a meta information header; if absent guess at a transfer syntax based on the contents.</p> * * @param file the file to read from * @exception IOException */ public DicomInputStream(File file) throws IOException { super(file,true); initializeTransferSyntax(null,true); // allow guessing at transfer syntax, try to find metaheader } /** * <p>Construct a stream to read DICOM data sets from the supplied stream.</p> * * @param i the input stream to read from * @param transferSyntaxUID use this transfer syntax (may be null) * @param tryMeta if true, try to find a meta information header * @exception IOException */ public DicomInputStream(InputStream i,String transferSyntaxUID,boolean tryMeta) throws IOException { super(i,true); initializeTransferSyntax(transferSyntaxUID,tryMeta); } /** * <p>Construct a stream to read DICOM data sets from the supplied stream.</p> * * @param file the file to read from * @param transferSyntaxUID use this transfer syntax (may be null) * @param tryMeta if true, try to find a meta information header * @exception IOException */ public DicomInputStream(File file,String transferSyntaxUID,boolean tryMeta) throws IOException { super(file,true); initializeTransferSyntax(transferSyntaxUID,tryMeta); } /** * <p>Specify what transfer syntax to use when switching from reading * the meta information header to reading the data set.</p> * * @param ts transfer syntax to use for data set */ public void setTransferSyntaxToReadDataSet(TransferSyntax ts) { TransferSyntaxToReadDataSet=ts; } /** * <p>Switch to the transfer syntax for reading the dataset.</p> */ public void setReadingDataSet() { TransferSyntaxInUse = TransferSyntaxToReadDataSet; setEndian(TransferSyntaxInUse.isBigEndian()); readingDataSet=true; } /** * <p>Are we reading the dataset?</p> * * @return true if reading the dataset, false if reading the meta information header */ public boolean areReadingDataSet() { return readingDataSet; } /** * <p>Switch to the transfer syntax for reading the meta information header.</p> */ public void setReadingMetaHeader() { TransferSyntaxInUse = TransferSyntaxToReadMetaHeader; setEndian(TransferSyntaxInUse.isBigEndian()); readingDataSet=false; } /** * <p>Are we reading the meta information header?</p> * * @return true if reading the meta information header, false if reading the dataset */ public boolean areReadingMetaHeader() { return !readingDataSet; } /** * <p>Do we have a meta information header?</p> * * @return true if there is a meta information header, false if not */ public boolean haveMetaHeader() { return TransferSyntaxToReadMetaHeader != null; } /** * <p>Get the transfer syntax currently in use.</p> * * @return the transfer syntax */ public TransferSyntax getTransferSyntaxInUse() { return TransferSyntaxInUse; } /** * <p>Get the transfer syntax to be used for reading the data set.</p> * * @return the transfer syntax */ public TransferSyntax getTransferSyntaxToReadDataSet() { return TransferSyntaxToReadDataSet; } /** * <p>Get the transfer syntax to be used for reading the meta information header.</p> * * @return the transfer syntax */ public TransferSyntax getTransferSyntaxToReadMetaHeader() { return TransferSyntaxToReadMetaHeader; } /** * <p>Get the byte offset of the start of the dataset or meta information header.</p> * * <p>Will be 0 if no preamble, 132 if a preamble.</p> * * @return the byte offset (from 0 being the start of the stream) */ public long getByteOffsetOfStartOfData() { return byteOffsetOfStartOfData; } }