/// <summary> /// Writes the element value to the stream. /// </summary> /// <param name="stream">The stream to write that positioned at the proper location to write.</param> /// <param name="syntax">The tranfer syntax to use if the value is binary.</param> /// <param name="encoding">The specific character set to use if the value is text.</param> /// <param name="options">The options used to encode tags.</param> protected override void WriteValueOnStream(Stream stream, string syntax, SpecificCharacterSet encoding, DataSetOptions options) { EndianBinaryWriter writer = new EndianBinaryWriter(stream, Syntax.GetEndian(syntax)); if (Syntax.CanEncapsulatePixelData(syntax)) { WriteEncapsulatedPixelData(writer); } else { if (vr == "OW") { short[] words = Value as short[]; foreach (short word in words) { writer.Write(word); } } else if (vr == "OB") { byte[] value = Value as byte[]; writer.Write((byte[])value); if (value.Length % 2 != 0) { writer.Write('\0'); } } else { throw new Exception(String.Format("Unexpected vr={0} in WriteValueOnStream", vr)); } } }
/// <summary> /// Writes the element value to the stream. /// </summary> /// <param name="stream">The stream to write that positioned at the proper location to write.</param> /// <param name="syntax">The tranfer syntax to use if the value is binary.</param> /// <param name="encoding">The specific character set to use if the value is text.</param> /// <param name="options">The options used to encode tags.</param> protected override void WriteValueOnStream(Stream stream, string syntax, SpecificCharacterSet encoding, DataSetOptions options) { EndianBinaryWriter writer = new EndianBinaryWriter(stream, Syntax.GetEndian(syntax)); unchecked { foreach (Elements elements in items) { writer.Write((short)0xFFFE); writer.Write((short)0xE000); // each nested element list can have its own SpecificCharacterSet, // and it is valid for all nested elements, unless overridden again further down SpecificCharacterSet scoped = encoding; if (elements.Contains(t.SpecificCharacterSet)) { scoped = new SpecificCharacterSet(elements[t.SpecificCharacterSet].Value); } uint size = 0; // and account for the size of each child element foreach (Element child in elements) { size += child.GetSize(syntax, scoped); } writer.Write(size); foreach (Element child in elements) { child.Write(writer.BaseStream, syntax, scoped, options); } } } }
/// <summary> /// Writes the collection to a stream using the specified transfer syntax. /// </summary> /// <param name="filename">The stream to write to.</param> /// <returns>The number of bytes written.</returns> /// <remarks>See <see cref="Part10Header"></see> for an explanation of the File Meta tags./></remarks> public long Write(Stream stream, string syntax) { if (syntax == Syntax.Unknown) { syntax = Syntax.ImplicitVrLittleEndian; } long results = 0; SpecificCharacterSet encoding = SpecificCharacterSet.Default; if (this.Contains(t.SpecificCharacterSet)) { object text = this[t.SpecificCharacterSet].Value; encoding = new SpecificCharacterSet(text); } foreach (Element element in this) { //element.Dump(); if (!GroupLengths && element.Tag.IsGroupLength) { continue; } results += element.Write(stream, syntax, encoding, options); } return(results); }
/// <summary> /// Just prior to writing, this sets the length of the Group Length tags in the DataSet /// </summary> /// <param name="tags"></param> /// <param name="encoding"></param> private void SetGroupLengths(Elements tags, SpecificCharacterSet encoding) { uint size = 0; Element group = null; foreach (Element element in tags) { if (element.element == 0x0000) { if (group != null) { //Logging.Log("Setting GroupLength {0} to {1}.", group, size); group.Value = size; } group = element; size = 0; } else { size += element.GetSize((group != null && group.element == 0x0000) ? Syntax.ExplicitVrLittleEndian : syntax, encoding); } } if (group != null) { //Logging.Log("Setting GroupLength {0} to {1}.", group, size); group.Value = size; } }
/// <summary> /// This method reads the value from the stream and sets the vm for string types /// </summary> /// <param name="stream">The stream to read the data from.</param> /// <param name="syntax">The transfer syntax used to interpret binary data.</param> /// <param name="encoding">The encoding to use to decode string data.</param> /// <param name="length">The length, in bytes, of the data to read.</param> /// <remarks>The stream is assumed to be positioned at the beginning of the encoded tag value.</remarks> protected override void ReadValueFromStream(Stream stream, string syntax, SpecificCharacterSet encoding, uint length) { this.length = length; value = null; if (length != 0) { EndianBinaryReader reader = new EndianBinaryReader(stream, Syntax.GetEndian(syntax)); //System.Diagnostics.Debug.WriteLine(String.Format("0x{0:X8}:{0}: Reading sequence of length={1}", stream.Position, (length == UInt32.MaxValue) ? "undefined" : length.ToString())); ReadSequence(reader, encoding, reader.BaseStream.Position); //System.Diagnostics.Debug.WriteLine(String.Format("0x{0:X8}:{0}: Finished reading sequence", stream.Position)); } }
/// <summary> /// The size in bytes of the collection if written. /// </summary> /// <param name="syntax"></param> /// <returns></returns> public ulong GetSize(string syntax) { ulong size = 0; SpecificCharacterSet encoding = SpecificCharacterSet.Default; if (Contains(t.SpecificCharacterSet)) { object text = this[t.SpecificCharacterSet].Value; encoding = new SpecificCharacterSet(text); } foreach (KeyValuePair <string, Element> pair in elements) { uint temp = pair.Value.GetSize(syntax, encoding); //System.Diagnostics.Debug.WriteLine(String.Format("\t{0}:{1}:{2}:{3}", pair.Value.Tag.ToString(), pair.Value.VR, temp, pair.Value.ToString())); size += (ulong)temp; } //System.Diagnostics.Debug.WriteLine(String.Format("{0}", size)); return(size); }
/// <summary> /// This method reads the value from the stream and sets the vm for string types /// </summary> /// <param name="stream">The stream to read the data from.</param> /// <param name="syntax">The transfer syntax used to interpret binary data.</param> /// <param name="encoding">The encoding to use to decode string data.</param> /// <param name="length">The length, in bytes, of the data to read.</param> /// <remarks>The stream is assumed to be positioned at the beginning of the encoded tag value.</remarks> protected override void ReadValueFromStream(Stream stream, string syntax, SpecificCharacterSet encoding, uint length) { this.length = length; EndianBinaryReader reader = new EndianBinaryReader(stream, Syntax.GetEndian(syntax)); if (length == 0xFFFFFFFF) { if (!Syntax.CanEncapsulatePixelData(syntax)) { throw new Exception(String.Format("Current syntax {0} does not support Encapsulated Pixel Data.", syntax, this.tag.Name)); } ReadEncapsulatedPixelData(reader); } else { ReadNativePixelData(reader); } }
/// <summary> /// Write the DataSet to a stream. /// </summary> /// <param name="filename">The stream to write to.</param> /// <returns>The number of bytes written.</returns> /// <remarks>See <see cref="Part10Header"></see> for an explanation of the File Meta tags./></remarks> public long Write(Stream stream) { long results = 0; // the transfer syntax as set in the dataset overrides the syntax property. if (Contains(t.TransferSyntaxUID)) { syntax = (string)this[t.TransferSyntaxUID].Value; } if (syntax == Syntax.Unknown) { syntax = Syntax.ImplicitVrLittleEndian; } SpecificCharacterSet encoding = SpecificCharacterSet.Default; if (header) { CheckHeader(); SetGroupLengths(metadata, encoding); BinaryWriter writer = new BinaryWriter(stream); byte[] buffer = new byte[128]; writer.Write(buffer, 0, buffer.Length); writer.Write("DICM".ToCharArray()); results += buffer.Length + 4; // metadata is always written in ExplicitVrLittleEndian results += metadata.Write(stream, Syntax.ExplicitVrLittleEndian); } //Logging.Log(LogLevel.Verbose, "Writing {0}.", Reflection.GetName(typeof(Syntax), syntax)); if (elements.Contains(t.SpecificCharacterSet)) { object text = this[t.SpecificCharacterSet].Value; encoding = new SpecificCharacterSet(text); } SetGroupLengths(elements, encoding); results += elements.Write(stream, syntax); return(results); }
/// <summary> /// The size in bytes of the Element if it were to be written. /// </summary> /// <param name="syntax">The TransferSyntax that will be used to write to the stream.</param> /// <param name="encoding">The encoding with which this element will be written.</param> /// <returns>The size in bytes of the Element.</returns> public override uint GetSize(string syntax, SpecificCharacterSet encoding) { uint size = GetFront(syntax, encoding); foreach (Elements elements in items) { // account for the size of each item tag and item value length size += sizeof(short) + sizeof(short) + sizeof(int); // each nested element list can have its own SpecificCharacterSet, // it is valid for all nested elements, unless overridden again further down SpecificCharacterSet scoped = encoding; if (elements.Contains(t.SpecificCharacterSet)) { scoped = new SpecificCharacterSet(elements[t.SpecificCharacterSet].Value); } // and then account for the size of each child element foreach (Element element in elements) { size += element.GetSize(syntax, scoped); } } return(size); }
private void ReadSequence(EndianBinaryReader reader, SpecificCharacterSet encoding, long start) { Tag lookahead = null; // if we have somethign to read and we are not at the end of the stream if (length != 0 && position != stream.Length) { // look ahead and see if we can find an item delimiter tag lookahead = DataSet.PeekTag(reader, this.syntax); //System.Diagnostics.Debug.WriteLine(String.Format("0x{0:X8}:{0}: Peek {1} in ReadSequence", stream.Position, lookahead.Description)); // as long as we have item tags // TODO if we do not find an item tag that is a problem while (lookahead.Equals(t.Item)) { // position past the tag group and element stream.Position += sizeof(ushort) + sizeof(ushort); // find out the length of the item uint count = reader.ReadUInt32(); //System.Diagnostics.Debug.WriteLine(String.Format("0x{0:X8}:{0}: Reading item of length={1}", stream.Position, (count == UInt32.MaxValue) ? "undefined" : count.ToString())); Elements elements = new Elements(); items.Add(elements); long place = stream.Position; // call scan recursively, passing in the length limit which is the size of the // entire item along with the sequence key name long temp = (count != UInt32.MaxValue) ? (place + count) : -1; place = DataSet.Scan(this, stream, elements, place, temp, UInt16.MaxValue, syntax, encoding); //System.Diagnostics.Debug.WriteLine(String.Format("0x{0:X8}:{0}: Finished reading item", stream.Position)); if (stream.Position < stream.Length) { lookahead = DataSet.PeekTag(reader, this.syntax); //System.Diagnostics.Debug.WriteLine(String.Format("0x{0:X8}:{0}: Peek {1} in ReadSequence", stream.Position, lookahead.Name)); } // the next tag is either another item in the sequence, a sequence delimiter, or the next tag // after the sequence. we only get the next tag if we are not at the end of the file and // we have an undefined sequence length if (length == UInt32.MaxValue) { if (lookahead.Equals(t.SequenceDelimitationItem)) { //System.Diagnostics.Debug.WriteLine(String.Format("0x{0:X8}:{0}: undefined length, found {1}:{2}", stream.Position, lookahead.Description, lookahead.Name)); break; } } else { if (stream.Position >= start + length) { break; } if (!lookahead.Equals(t.Item)) { } } } if (length == UInt32.MaxValue) { if (lookahead.Equals(t.SequenceDelimitationItem)) { //System.Diagnostics.Debug.WriteLine(String.Format("0x{0:X8}:{0}: undefined length, found {1}:{2}", stream.Position, lookahead.Description, lookahead.Name)); // position stream beyond delimitation tag stream.Position += sizeof(ushort) * 2; // no need to check zero int zero = reader.ReadInt32(); } } } }
/// <summary> /// Parses the stream for tags. /// </summary> /// <param name="parent">The parent Element that the tags will belong to, if any.</param> /// <param name="stream">The stream to Read from.</param> /// <param name="elements">The Elements collection to add the Elements to.</param> /// <param name="current">The current postion in the stream.</param> /// <param name="length">The maximum number of bytes to read.</param> /// <param name="stop">The group number ot read up to and including.</param> /// <param name="syntax">The Transfer Syntax of the stream.</param> /// <param name="encoding">The character encoding of the stream.</param> /// <returns>The final position in the stream Read to.</returns> public static long Scan(Element parent, Stream stream, Elements elements, long current, long length, ushort stop, string syntax, SpecificCharacterSet encoding) { //System.Diagnostics.Debug.WriteLine(String.Format("0x{0:X8}:{0}: Scan called parent={1}:{2}. current={3} length={4} stop={5}", // stream.Position, (parent!=null)?parent.Tag.ToString():"root", (parent!=null)?parent.VR:"", current, length, stop)); // we have to get the stream on top of the current position stream.Position = current; EndianBinaryReader reader = new EndianBinaryReader(stream, Syntax.GetEndian(syntax)); // Scan stops when either the stream position exceeds length, or the group exceeds stop, // or we reach the end of a file, or we reach an ItemDelimiter or SequenceDelimiter, or // we can no longer read while (true) { if (!stream.CanRead) { //System.Diagnostics.Debug.WriteLine("stream CanRead==false"); break; } if (length != -1 && (ulong)current >= (ulong)length) { //System.Diagnostics.Debug.WriteLine(String.Format("0x{0:X8}:{0}: current={1} exceeds length={2}", stream.Position, current, length)); break; } // keep track of where we are before reading the next element long previous = current; Tag lookahead = PeekTag(reader, syntax); //System.Diagnostics.Debug.WriteLine(String.Format("0x{0:X8}:{0}: Peek {1} in Scan", stream.Position, lookahead.Name)); // we scan up to and including the group specified by stop // so we check if we have gone beyond the specified group if (lookahead.Group > stop) { //System.Diagnostics.Debug.WriteLine(String.Format("0x{0:X8}:{0}: group={1} exceeds stop={2}", stream.Position, lookahead.Group, stop)); // we need to rewind to before this element current = previous; break; } if (length == -1) { //if (lookahead.Equals(t.ItemDelimitationItem) || lookahead.Equals(t.SequenceDelimitationItem)) if (lookahead.Equals(t.ItemDelimitationItem)) { //System.Diagnostics.Debug.WriteLine(String.Format("0x{0:X8}:{0}: undefined length, found {1}:{2}", stream.Position, lookahead.Description, lookahead.Name)); // position stream beyond delimitation tag stream.Position += sizeof(ushort) * 2; // no need to check zero int zero = reader.ReadInt32(); break; } } Exception exception = null; Element element = Element.Factory(parent, lookahead); try { //System.Diagnostics.Debug.WriteLine(String.Format("reading {0}", lookahead.ToString())); element.Read(stream, syntax, encoding); } catch (Exception ex) { // we are going to delay throwing an exception from reading // until we see if we are going to add it to the collection exception = ex; } current = stream.Position; // if we have the SpecificCharacterSet, create an Encoding for the rest of the DataSet if (element.Tag.Equals(t.SpecificCharacterSet)) { encoding = new SpecificCharacterSet(element.Value); } else if (element.Tag.Equals(t.PixelData) && !Syntax.IsExplicit(syntax)) { // if we have the PixelData, insure that it matches the BitsStored // this can happen with implicit vrs and OW is the default if (elements.Contains(t.BitsStored)) { ushort stored = (ushort)elements[t.BitsStored].Value; Type type = element.Value.GetType().GetElementType(); if (stored == 8 && element.Value is ushort[]) { ushort[] pixels = element.Value as ushort[]; byte[] bytes = new byte[pixels.Length * 2]; Buffer.BlockCopy(pixels, 0, bytes, 0, bytes.Length); element.Value = bytes; } } } else if (element.Tag.Equals(t.ItemDelimitationItem) || element.Tag.Equals(t.SequenceDelimitationItem) || element.Tag.Equals(t.Item)) { // if we have a sequence or item delimiter, stop scanning if (length == -1) { return(current); } } // now that we are about to add the tag, check and see if it parsed correctly if (exception != null) { // we have failed to parse, so we create a tag that contains the rest of the data stream.Position = previous; element = new Element("(BAAD,F00D)", reader.ReadBytes((int)(length - previous))); } string key = Tag.ToString(element); // and insert the tag name/value pair in the hash table, allows duplicates elements[key] = element; //Logging.Log("Adding {0}:{1}", key, element.ToString()); // now we can throw the exception if there is one if (exception != null) { throw exception; } if ((ulong)current >= (ulong)length) { //System.Diagnostics.Debug.WriteLine(String.Format("0x{0:X8}:{0}: current={1} at or exceeds length={2}", stream.Position, current, length)); break; } } return(current); }