class DICOM::Parent

Super class which contains common code for all parent elements.

Inheritance

Since all parents inherit from this class, these methods are available to instances of the following classes:

Public Instance Methods

[](tag_or_index) click to toggle source

Retrieves the child element matching the specified element tag or item index.

Only immediate children are searched. Grandchildren etc. are not included.

@param [String, Integer] tag_or_index a ruby-dicom tag string or item index @return [Element, Sequence, Item, NilClass] the matched element (or nil, if no match was made) @example Extract the “Pixel Data” data element from the DObject instance

pixel_data_element = dcm["7FE0,0010"]

@example Extract the first Item from a Sequence

first_item = dcm["3006,0020"][0]
# File lib/dicom/parent.rb, line 27
def [](tag_or_index)
  formatted = tag_or_index.is_a?(String) ? tag_or_index.upcase : tag_or_index
  return @tags[formatted]
end
add(element, options={}) click to toggle source

Adds an Element or Sequence instance to self (where self can be either a DObject or an Item).

@note Items can not be added with this method (use add_item instead).

@param [Element, Sequence] element a child element/sequence @param [Hash] options the options used for adding the element/sequence option options [Boolean] :no_follow when true, the method does not update the parent attribute of the child that is added @example Set a new patient's name to the DICOM object

dcm.add(Element.new("0010,0010", "John_Doe"))

@example Add a previously defined element roi_name to the first item of a sequence

dcm["3006,0020"][0].add(roi_name)
# File lib/dicom/parent.rb, line 44
def add(element, options={})
  unless element.is_a?(Item)
    unless self.is_a?(Sequence)
      # Does the element's binary value need to be reencoded?
      reencode = true if element.is_a?(Element) && element.endian != stream.str_endian
      # If we are replacing an existing Element, we need to make sure that this Element's parent value is erased before proceeding.
      self[element.tag].parent = nil if exists?(element.tag)
      # Add the element, and set its parent attribute:
      @tags[element.tag] = element
      element.parent = self unless options[:no_follow]
      # As the element has been moved in place, perform re-encode if indicated:
      element.value = element.value if reencode
    else
      raise "A Sequence is only allowed to have Item elements added to it. Use add_item() instead if the intention is to add an Item."
    end
  else
    raise ArgumentError, "An Item is not allowed as a parameter to the add() method. Use add_item() instead."
  end
end
children() click to toggle source

Retrieves all (immediate) child elementals in an array (sorted by element tag).

@return [Array<Element, Item, Sequence>] the parent's child elementals (an empty array if childless) @example Retrieve all top level elements in a DICOM object

top_level_elements = dcm.children
# File lib/dicom/parent.rb, line 70
def children
  return @tags.sort.transpose[1] || Array.new
end
children?() click to toggle source

Checks if an element actually has any child elementals (elements/items/sequences).

Notice the subtle difference between the children? and is_parent? methods. While they will give the same result in most real use cases, they differ when used on parent elements that do not have any children added yet.

For example, when called on an empty Sequence, the children? method will return false, whereas the is_parent? method still returns true.

@return [Boolean] true if the element has children, and false if not

# File lib/dicom/parent.rb, line 85
def children?
  if @tags.length > 0
    return true
  else
    return false
  end
end
count() click to toggle source

Gives the number of elements connected directly to this parent.

This count does NOT include the number of elements contained in any possible child elements.

@return [Integer] The number of child elements belonging to this parent

# File lib/dicom/parent.rb, line 99
def count
  return @tags.length
end
count_all() click to toggle source

Gives the total number of elements connected to this parent.

This count includes all the elements contained in any possible child elements.

@return [Integer] The total number of child elements connected to this parent

# File lib/dicom/parent.rb, line 109
def count_all
  # Iterate over all elements, and repeat recursively for all elements which themselves contain children.
  total_count = count
  @tags.each_value do |value|
    total_count += value.count_all if value.children?
  end
  return total_count
end
delete(tag_or_index, options={}) click to toggle source

Deletes the specified element from this parent.

@param [String, Integer] tag_or_index a ruby-dicom tag string or item index @param [Hash] options the options used for deleting the element option options [Boolean] :no_follow when true, the method does not update the parent attribute of the child that is deleted @example Delete an Element from a DObject instance

dcm.delete("0008,0090")

@example Delete Item 1 from a Sequence

dcm["3006,0020"].delete(1)
# File lib/dicom/parent.rb, line 128
def delete(tag_or_index, options={})
  check_key(tag_or_index, :delete)
  # We need to delete the specified child element's parent reference in addition to removing it from the tag Hash.
  element = self[tag_or_index]
  if element
    element.parent = nil unless options[:no_follow]
    @tags.delete(tag_or_index)
  end
end
delete_children() click to toggle source

Deletes all child elements from this parent.

# File lib/dicom/parent.rb, line 140
def delete_children
  @tags.each_key do |tag|
    delete(tag)
  end
end
delete_group(group_string) click to toggle source

Deletes all elements of the specified group from this parent.

@param [String] group_string a group string (the first 4 characters of a tag string) @example Delete the File Meta Group of a DICOM object

dcm.delete_group("0002")
# File lib/dicom/parent.rb, line 152
def delete_group(group_string)
  group_elements = group(group_string)
  group_elements.each do |element|
    delete(element.tag)
  end
end
delete_private() click to toggle source

Deletes all private data/sequence elements from this parent.

@example Delete all private elements from a DObject instance

dcm.delete_private

@example Delete only private elements belonging to a specific Sequence

dcm["3006,0020"].delete_private
# File lib/dicom/parent.rb, line 166
def delete_private
  # Iterate all children, and repeat recursively if a child itself has children, to delete all private data elements:
  children.each do |element|
    delete(element.tag) if element.tag.private?
    element.delete_private if element.children?
  end
end
delete_retired() click to toggle source

Deletes all retired data/sequence elements from this parent.

@example Delete all retired elements from a DObject instance

dcm.delete_retired
# File lib/dicom/parent.rb, line 179
def delete_retired
  # Iterate all children, and repeat recursively if a child itself has children, to delete all retired elements:
  children.each do |element|
    dict_element = LIBRARY.element(element.tag)
    delete(element.tag) if dict_element && dict_element.retired?
    element.delete_retired if element.children?
  end
end
each(&block) click to toggle source

Iterates all children of this parent, calling block for each child.

# File lib/dicom/parent.rb, line 190
def each(&block)
  children.each_with_index(&block)
end
each_element(&block) click to toggle source

Iterates the child elements of this parent, calling block for each element.

# File lib/dicom/parent.rb, line 196
def each_element(&block)
  elements.each_with_index(&block) if children?
end
each_item(&block) click to toggle source

Iterates the child items of this parent, calling block for each item.

# File lib/dicom/parent.rb, line 202
def each_item(&block)
  items.each_with_index(&block) if children?
end
each_sequence(&block) click to toggle source

Iterates the child sequences of this parent, calling block for each sequence.

# File lib/dicom/parent.rb, line 208
def each_sequence(&block)
  sequences.each_with_index(&block) if children?
end
each_tag(&block) click to toggle source

Iterates the child tags of this parent, calling block for each tag.

# File lib/dicom/parent.rb, line 214
def each_tag(&block)
  @tags.each_key(&block)
end
elements() click to toggle source

Retrieves all child elements of this parent in an array.

@return [Array<Element>] child elements (or empty array, if childless)

# File lib/dicom/parent.rb, line 222
def elements
  children.select { |child| child.is_a?(Element)}
end
elements?() click to toggle source

A boolean which indicates whether the parent has any child elements.

@return [Boolean] true if any child elements exists, and false if not

# File lib/dicom/parent.rb, line 230
def elements?
  elements.any?
end
encode_children(old_endian) click to toggle source

Re-encodes the binary data strings of all child Element instances. This also includes all the elements contained in any possible child elements.

@note This method is only intended for internal library use, but for technical reasons

(the fact that is called between instances of different classes), can't be made private.

@param [Boolean] old_endian the previous endianness of the elements/DObject instance (used for decoding values from binary)

# File lib/dicom/parent.rb, line 241
def encode_children(old_endian)
   # Cycle through all levels of children recursively:
  children.each do |element|
    if element.children?
      element.encode_children(old_endian)
    elsif element.is_a?(Element)
      encode_child(element, old_endian)
    end
  end
end
exists?(tag_or_index) click to toggle source

Checks whether a specific data element tag is defined for this parent.

@param [String, Integer] tag_or_index a ruby-dicom tag string or item index @return [Boolean] true if the element is found, and false if not @example Do something with an element only if it exists

process_name(dcm["0010,0010"]) if dcm.exists?("0010,0010")
# File lib/dicom/parent.rb, line 259
def exists?(tag_or_index)
  if self[tag_or_index]
    return true
  else
    return false
  end
end
group(group_string) click to toggle source

Returns an array of all child elements that belongs to the specified group.

@param [String] group_string a group string (the first 4 characters of a tag string) @return [Array<Element, Item, Sequence>] the matching child elements (an empty array if no children matches)

# File lib/dicom/parent.rb, line 272
def group(group_string)
  raise ArgumentError, "Expected String, got #{group_string.class}." unless group_string.is_a?(String)
  found = Array.new
  children.each do |child|
    found << child if child.tag.group == group_string.upcase
  end
  return found
end
handle_print(index, max_digits, max_name, max_length, max_generations, visualization, options={}) click to toggle source

Gathers the desired information from the selected data elements and processes this information to make a text output which is nicely formatted.

@note This method is only intended for internal library use, but for technical reasons

(the fact that is called between instances of different classes), can't be made private.
The method is used by the print() method to construct its text output.

@param [Integer] index the index which is given to the first child of this parent @param [Integer] max_digits the maximum number of digits in the index of an element (in reality the number of digits of the last element) @param [Integer] max_name the maximum number of characters in the name of any element to be printed @param [Integer] max_length the maximum number of digits in the length of an element @param [Integer] max_generations the maximum number of generations of children for this parent @param [Integer] visualization an array of string symbols which visualizes the tree structure that the children of this particular parent belongs to (for no visualization, an empty array is passed) @param [Hash] options the options to use when processing the print information @option options [Integer] :value_max if a value max length is specified, the element values which exceeds this are trimmed @return [Array] a text array and an index of the last element

# File lib/dicom/parent.rb, line 298
def handle_print(index, max_digits, max_name, max_length, max_generations, visualization, options={})
  # FIXME: This method is somewhat complex, and some simplification, if possible, wouldn't hurt.
  elements = Array.new
  s = " "
  hook_symbol = "|_"
  last_item_symbol = "  "
  nonlast_item_symbol = "| "
  children.each_with_index do |element, i|
    n_parents = element.parents.length
    # Formatting: Index
    i_s = s*(max_digits-(index).to_s.length)
    # Formatting: Name (and Tag)
    if element.tag == ITEM_TAG
      # Add index numbers to the Item names:
      name = "#{element.name} (\##{i})"
    else
      name = element.name
    end
    n_s = s*(max_name-name.length)
    # Formatting: Tag
    tag = "#{visualization.join}#{element.tag}"
    t_s = s*((max_generations-1)*2+9-tag.length)
    # Formatting: Length
    l_s = s*(max_length-element.length.to_s.length)
    # Formatting Value:
    if element.is_a?(Element)
      value = element.value.to_s
    else
      value = ""
    end
    if options[:value_max]
      value = "#{value[0..(options[:value_max]-3)]}.." if value.length > options[:value_max]
    end
    elements << "#{i_s}#{index} #{tag}#{t_s} #{name}#{n_s} #{element.vr} #{l_s}#{element.length} #{value}"
    index += 1
    # If we have child elements, print those elements recursively:
    if element.children?
      if n_parents > 1
        child_visualization = Array.new
        child_visualization.replace(visualization)
        if element == children.first
          if children.length == 1
            # Last item:
            child_visualization.insert(n_parents-2, last_item_symbol)
          else
            # More items follows:
            child_visualization.insert(n_parents-2, nonlast_item_symbol)
          end
        elsif element == children.last
          # Last item:
          child_visualization[n_parents-2] = last_item_symbol
          child_visualization.insert(-1, hook_symbol)
        else
          # Neither first nor last (more items follows):
          child_visualization.insert(n_parents-2, nonlast_item_symbol)
        end
      elsif n_parents == 1
        child_visualization = Array.new(1, hook_symbol)
      else
        child_visualization = Array.new
      end
      new_elements, index = element.handle_print(index, max_digits, max_name, max_length, max_generations, child_visualization, options)
      elements << new_elements
    end
  end
  return elements.flatten, index
end
inspect() click to toggle source

Gives a string containing a human-readable hash representation of the parent.

@return [String] a hash representation string of the parent

# File lib/dicom/parent.rb, line 370
def inspect
  to_hash.inspect
end
is_parent?() click to toggle source

Checks if an elemental is a parent.

@return [Boolean] true for all parent elementals (Item, Sequence, DObject)

# File lib/dicom/parent.rb, line 378
def is_parent?
  return true
end
items() click to toggle source

Retrieves all child items of this parent in an array.

@return [Array<Item>] child items (or empty array, if childless)

# File lib/dicom/parent.rb, line 386
def items
  children.select { |child| child.is_a?(Item)}
end
items?() click to toggle source

A boolean which indicates whether the parent has any child items.

@return [Boolean] true if any child items exists, and false if not

# File lib/dicom/parent.rb, line 394
def items?
  items.any?
end
length=(new_length) click to toggle source

Sets the length of a Sequence or Item.

@note Currently, ruby-dicom does not use sequence/item lengths when writing DICOM files (it sets the length to -1, meaning UNDEFINED). Therefore, in practice, it isn't necessary to use this method, at least as far as writing (valid) DICOM files is concerned.

@param [Integer] new_length the new length to assign to the Sequence/Item

# File lib/dicom/parent.rb, line 406
def length=(new_length)
  unless self.is_a?(DObject)
    @length = new_length
  else
    raise "Length can not be set for a DObject instance."
  end
end
max_lengths() click to toggle source

Finds and returns the maximum character lengths of name and length which occurs for any child element, as well as the maximum number of generations of elements.

@note This method is only intended for internal library use, but for technical reasons

(the fact that is called between instances of different classes), can't be made private.
The method is used by the print() method to achieve a proper format in its output.
# File lib/dicom/parent.rb, line 421
def max_lengths
  max_name = 0
  max_length = 0
  max_generations = 0
  children.each do |element|
    if element.children?
      max_nc, max_lc, max_gc = element.max_lengths
      max_name = max_nc if max_nc > max_name
      max_length = max_lc if max_lc > max_length
      max_generations = max_gc if max_gc > max_generations
    end
    n_length = element.name.length
    l_length = element.length.to_s.length
    generations = element.parents.length
    max_name = n_length if n_length > max_name
    max_length = l_length if l_length > max_length
    max_generations = generations if generations > max_generations
  end
  return max_name, max_length, max_generations
end
method_missing(sym, *args, &block) click to toggle source

Handles missing methods, which in our case is intended to be dynamic method names matching DICOM elements in the dictionary.

When a dynamic method name is matched against a DICOM element, this method:

  • Returns the element if the method name suggests an element retrieval, and the element exists.

  • Returns nil if the method name suggests an element retrieval, but the element doesn't exist.

  • Returns a boolean, if the method name suggests a query (?), based on whether the matched element exists or not.

  • When the method name suggests assignment (=), an element is created with the supplied arguments, or if the argument is nil, the element is deleted.

  • When a dynamic method name is not matched against a DICOM element, and the method is not defined by the parent, a NoMethodError is raised.

@param [Symbol] sym a method name

Calls superclass method
# File lib/dicom/parent.rb, line 455
def method_missing(sym, *args, &block)
  s = sym.to_s
  action = s[-1]
  # Try to match the method against a tag from the dictionary:
  tag = LIBRARY.as_tag(s) || LIBRARY.as_tag(s[0..-2])
  if tag
    if action == '?'
      # Query:
      return self.exists?(tag)
    elsif action == '='
      # Assignment:
      unless args.length==0 || args[0].nil?
        # What kind of element to create?
        if tag == 'FFFE,E000'
          return self.add_item
        elsif LIBRARY.element(tag).vr == 'SQ'
          return self.add(Sequence.new(tag))
        else
          return self.add(Element.new(tag, *args))
        end
      else
        return self.delete(tag)
      end
    else
      # Retrieval:
      return self[tag]
    end
  end
  # Forward to Object#method_missing:
  super
end
parse(bin, syntax, switched=false, explicit=true) click to toggle source

Loads data from an encoded DICOM string and creates items and elements which are linked to this instance.

@param [String] bin an encoded binary string containing DICOM information @param [String] syntax the transfer syntax to use when decoding the DICOM string @param [Boolean] switched indicating whether the transfer syntax 'switch' has occured in the data stream of this object

# File lib/dicom/d_read.rb, line 12
def parse(bin, syntax, switched=false, explicit=true)
  raise ArgumentError, "Invalid argument 'bin'. Expected String, got #{bin.class}." unless bin.is_a?(String)
  raise ArgumentError, "Invalid argument 'syntax'. Expected String, got #{syntax.class}." unless syntax.is_a?(String)
  read(bin, signature=false, :syntax => syntax, :switched => switched, :explicit => explicit)
end
print(options={}) click to toggle source

Prints all child elementals of this particular parent. Information such as tag, parent-child relationship, name, vr, length and value is gathered for each element and processed to produce a nicely formatted output.

@param [Hash] options the options to use for handling the printout option options [Integer] :value_max if a value max length is specified, the element values which exceeds this are trimmed option options [String] :file if a file path is specified, the output is printed to this file instead of being printed to the screen @return [Array<String>] an array of formatted element string lines @example Print a DObject instance to screen

dcm.print

@example Print the DObject to the screen, but specify a 25 character value cutoff to produce better-looking results

dcm.print(:value_max => 25)

@example Print to a text file the elements that belong to a specific Sequence

dcm["3006,0020"].print(:file => "dicom.txt")
representation() click to toggle source

Gives a string which represents this DICOM parent. The DOBject is is represented by its class name, whereas elemental parents (Sequence, Item) is represented by their tags.

@return [String] a representation of the DICOM parent

# File lib/dicom/parent.rb, line 531
def representation
  self.is_a?(DObject) ? 'DObject' : self.tag
end
reset_length() click to toggle source

Resets the length of a Sequence or Item to -1, which is the number used for 'undefined' length.

# File lib/dicom/parent.rb, line 537
def reset_length
  unless self.is_a?(DObject)
    @length = -1
    @bin = ""
  else
    raise "Length can not be set for a DObject instance."
  end
end
respond_to?(method, include_private=false) click to toggle source

Checks if the parent responds to the given method (symbol) (whether the method is defined or not).

@param [Symbol] method a method name who's response is tested @param [Boolean] include_private if true, private methods are included in the search (not used by ruby-dicom) @return [Boolean] true if the parent responds to the given method (method is defined), and false if not

Calls superclass method
# File lib/dicom/parent.rb, line 552
def respond_to?(method, include_private=false)
  # Check the library for a tag corresponding to the given method name symbol:
  return true unless LIBRARY.as_tag(method.to_s).nil?
  # In case of a query (xxx?) or assign (xxx=), remove last character and try again:
  return true unless LIBRARY.as_tag(method.to_s[0..-2]).nil?
  # Forward to Object#respond_to?:
  super
end
sequences() click to toggle source

Retrieves all child sequences of this parent in an array.

@return [Array<Sequence>] child sequences (or empty array, if childless)

# File lib/dicom/parent.rb, line 565
def sequences
  children.select { |child| child.is_a?(Sequence) }
end
sequences?() click to toggle source

A boolean which indicates whether the parent has any child sequences.

@return [Boolean] true if any child sequences exists, and false if not

# File lib/dicom/parent.rb, line 573
def sequences?
  sequences.any?
end
to_hash() click to toggle source

Builds a nested hash containing all children of this parent.

Keys are determined by the key_representation attribute, and data element values are used as values.

  • For private elements, the tag is used for key instead of the key representation, as private tags lacks names.

  • For child-less parents, the key_representation attribute is used as value.

@return [Hash] a nested hash containing key & value pairs of all children

# File lib/dicom/parent.rb, line 585
def to_hash
  as_hash = Hash.new
  unless children?
    if self.is_a?(DObject)
      as_hash = {}
    else
      as_hash[(self.tag.private?) ? self.tag : self.send(DICOM.key_representation)] = nil
    end
  else
    children.each do |child|
      if child.tag.private?
        hash_key = child.tag
      elsif child.is_a?(Item)
        hash_key = "Item #{child.index}"
      else
        hash_key = child.send(DICOM.key_representation)
      end
      if child.is_a?(Element)
        as_hash[hash_key] = child.to_hash[hash_key]
      else
        as_hash[hash_key] = child.to_hash
      end
    end
  end
  return as_hash
end
to_json() click to toggle source

Builds a json string containing a human-readable representation of the parent.

@return [String] a human-readable representation of this parent

# File lib/dicom/parent.rb, line 616
def to_json
  to_hash.to_json
end
to_yaml() click to toggle source

Returns a yaml string containing a human-readable representation of the parent.

@return [String] a human-readable representation of this parent

# File lib/dicom/parent.rb, line 624
def to_yaml
  to_hash.to_yaml
end
value(tag) click to toggle source

Gives the value of a specific Element child of this parent.

  • Only Element instances have values. Parent elements like Sequence and Item have no value themselves.

  • If the specified tag is that of a parent element, an exception is raised.

@param [String] tag a tag string which identifies the child Element @return [String, Integer, Float, NilClass] an element value (or nil, if no element is matched) @example Get the patient's name value

name = dcm.value("0010,0010")

@example Get the Frame of Reference UID from the first item in the Referenced Frame of Reference Sequence

uid = dcm["3006,0010"][0].value("0020,0052")
# File lib/dicom/parent.rb, line 640
def value(tag)
  check_key(tag, :value)
  if exists?(tag)
    if self[tag].is_parent?
      raise ArgumentError, "Illegal parameter '#{tag}'. Parent elements, like the referenced '#{@tags[tag].class}', have no value. Only Element tags are valid."
    else
      return self[tag].value
    end
  else
    return nil
  end
end

Private Instance Methods

add_encoded(string) click to toggle source

Adds a binary string to (the end of) either the instance file or string.

@param [String] string a pre-encoded string

# File lib/dicom/d_write.rb, line 12
def add_encoded(string)
  if @file
    @stream.write(string)
  else
    # Are we writing to a single (big) string, or multiple (smaller) strings?
    unless @segments
      @stream.add_last(string)
    else
      add_with_segmentation(string)
    end
  end
end
add_with_segmentation(string) click to toggle source

Adds an encoded string to the output stream, while keeping track of the accumulated size of the output stream, splitting it up as necessary, and transferring the encoded string fragments to an array.

@param [String] string a pre-encoded string

# File lib/dicom/d_write.rb, line 31
def add_with_segmentation(string)
  # As the encoded DICOM string will be cut in multiple, smaller pieces, we need to monitor the length of our encoded strings:
  if (string.length + @stream.length) > @max_size
    split_and_add(string)
  elsif (30 + @stream.length) > @max_size
    # End the current segment, and start on a new segment for this string.
    @segments << @stream.export
    @stream.add_last(string)
  else
    # We are nowhere near the limit, simply add the string:
    @stream.add_last(string)
  end
end
check_duplicate(tag, elemental) click to toggle source

Checks whether the given tag is a duplicate of an existing tag with this parent.

@param [String] tag the tag of the candidate duplicate elemental @param [String] elemental the duplicate elemental type (e.g. Sequence, Element)

# File lib/dicom/d_read.rb, line 27
def check_duplicate(tag, elemental)
  if @current_parent[tag]
    gp = @current_parent.parent ? "#{@current_parent.parent.representation} => " : ''
    p = @current_parent.representation
    logger.warn("Duplicate #{elemental} (#{tag}) detected at level: #{gp}#{p}")
  end
end
check_encapsulated_image(element) click to toggle source

Toggles the status for enclosed pixel data.

@param [Element, Item, Sequence] element a data element

# File lib/dicom/d_write.rb, line 49
def check_encapsulated_image(element)
  # If DICOM object contains encapsulated pixel data, we need some special handling for its items:
  if element.tag == PIXEL_TAG and element.parent.is_a?(DObject)
    @enc_image = true if element.length <= 0
  end
end
check_header() click to toggle source

Checks for the official DICOM header signature.

@return [Boolean] true if the proper signature is present, false if not, and nil if the string was shorter then the length of the DICOM signature

# File lib/dicom/d_read.rb, line 39
def check_header
  # According to the official DICOM standard, a DICOM file shall contain 128 consequtive (zero) bytes,
  # followed by 4 bytes that spell the string 'DICM'. Apparently, some providers seems to skip this in their DICOM files.
  # Check that the string is long enough to contain a valid header:
  if @str.length < 132
    # This does not seem to be a valid DICOM string and so we return.
    return nil
  else
    @stream.skip(128)
    # Next 4 bytes should spell "DICM":
    identifier = @stream.decode(4, "STR")
    @header_length += 132
    if identifier != "DICM" then
      # Header signature is not valid (we will still try to parse it is a DICOM string though):
      logger.warn("This string does not contain the expected DICOM header. Will try to parse the string anyway (assuming a missing header).")
      # As the string is not conforming to the DICOM standard, it is possible that it does not contain a
      # transfer syntax element, and as such, we attempt to choose the most probable encoding values here:
      @explicit = false
      return false
    else
      # Header signature is valid:
      @signature = true
      return true
    end
  end
end
check_key(tag_or_index, method) click to toggle source

Checks the given key argument and logs a warning if an obviously incorrect key argument is used.

@param [String, Integer] tag_or_index the tag string or item index indentifying a given elemental @param [Symbol] method a representation of the method calling this method

# File lib/dicom/parent.rb, line 663
def check_key(tag_or_index, method)
  if tag_or_index.is_a?(String)
    logger.warn("Parent##{method} called with an invalid tag argument: #{tag_or_index}") unless tag_or_index.tag?
  elsif tag_or_index.is_a?(Integer)
    logger.warn("Parent##{method} called with a negative Integer argument: #{tag_or_index}") if tag_or_index < 0
  else
    logger.warn("Parent##{method} called with an unexpected argument. Expected String or Integer, got: #{tag_or_index.class}")
  end
end
encode_child(element, old_endian) click to toggle source

Re-encodes the value of a child Element (but only if the Element encoding is influenced by a shift in endianness).

@param [Element] element the Element who's value will be re-encoded @param [Boolean] old_endian the previous endianness of the element binary (used for decoding the value)

# File lib/dicom/parent.rb, line 679
def encode_child(element, old_endian)
  if element.tag == "7FE0,0010"
    # As encoding settings of the DObject has already been changed, we need to decode the old pixel values with the old encoding:
    stream_old_endian = Stream.new(nil, old_endian)
    pixels = decode_pixels(element.bin, stream_old_endian)
    encode_pixels(pixels, stream)
  else
    # Not all types of tags needs to be reencoded when switching endianness:
    case element.vr
      when "US", "SS", "UL", "SL", "FL", "FD", "OF", "OW", "AT" # Numbers or tag reference
        # Re-encode, as long as it is not a group 0002 element (which must always be little endian):
        unless element.tag.group == "0002"
          stream_old_endian = Stream.new(element.bin, old_endian)
          formatted_value = stream_old_endian.decode(element.length, element.vr)
          element.value = formatted_value # (the value=() method also encodes a new binary for the element)
        end
    end
  end
end
encode_in_segments(max_size, options={}) click to toggle source

Writes DICOM content to a series of size-limited binary strings, which is returned in an array. This is typically used in preparation of transmitting DICOM objects through network connections.

@param [Integer] max_size the maximum segment string length @param [Hash] options the options to use for encoding the DICOM strings @option options [String] :syntax the transfer syntax used for the encoding settings of the post-meta part of the DICOM string @return [Array<String>] the encoded DICOM strings

# File lib/dicom/d_write.rb, line 64
def encode_in_segments(max_size, options={})
  @max_size = max_size
  @transfer_syntax = options[:syntax]
  # Until a DICOM write has completed successfully the status is 'unsuccessful':
  @write_success = false
  # Default explicitness of start of DICOM file:
  @explicit = true
  # Default endianness of start of DICOM files (little endian):
  @str_endian = false
  # When the file switch from group 0002 to a later group we will update encoding values, and this switch will keep track of that:
  @switched = false
  # Items contained under the Pixel Data element needs some special attention to write correctly:
  @enc_image = false
  # Create a Stream instance to handle the encoding of content to a binary string:
  @stream = Stream.new(nil, @str_endian)
  @segments = Array.new
  write_data_elements(children)
  # Extract the remaining string in our stream instance to our array of strings:
  @segments << @stream.export
  # Mark this write session as successful:
  @write_success = true
  return @segments
end
initialize_parent() click to toggle source

Initializes common variables among the parent elements.

# File lib/dicom/parent.rb, line 701
def initialize_parent
  # All child data elements and sequences are stored in a hash where the tag string is used as key:
  @tags = Hash.new
end
open_file(file) click to toggle source

Tests if the path/file is writable, creates any folders if necessary, and opens the file for writing.

@param [String] file a path/file string

# File lib/dicom/d_write.rb, line 92
def open_file(file)
  # Check if file already exists:
  if File.exist?(file)
    # Is it writable?
    if File.writable?(file)
      @file = File.new(file, "wb")
    else
      # Existing file is not writable:
      logger.error("The program does not have permission or resources to create this file: #{file}")
    end
  else
    # File does not exist.
    # Check if this file's path contains a folder that does not exist, and therefore needs to be created:
    folders = file.split(File::SEPARATOR)
    if folders.length > 1
      # Remove last element (which should be the file string):
      folders.pop
      path = folders.join(File::SEPARATOR)
      # Check if this path exists:
      unless File.directory?(path)
        # We need to create (parts of) this path:
        require 'fileutils'
        FileUtils.mkdir_p(path)
      end
    end
    # The path to this non-existing file is verified, and we can proceed to create the file:
    @file = File.new(file, "wb")
  end
end
print_file(elements, file) click to toggle source

Prints an array of formatted element string lines gathered by the print() method to file.

@param [Array<String>] elements an array of formatted element string lines @param [String] file a path/file_name string

print_screen(elements) click to toggle source

Prints an array of formatted element string lines gathered by the print() method to the screen.

@param [Array<String>] elements an array of formatted element string lines

process_data_element() click to toggle source

Handles the process of reading a data element from the DICOM string, and creating an element object from the parsed data.

@return [Boolean] nil if end of string has been reached (in an expected way), false if the element parse failed, and true if an element was parsed successfully

# File lib/dicom/d_read.rb, line 71
def process_data_element
  # FIXME: This method has grown a bit messy and isn't very pleasant to read. Cleanup possible?
  # After having been into a possible unknown sequence with undefined length, we may need to reset
  # explicitness from implicit to explicit:
  if !@original_explicit.nil? && @explicitness_reset_parent == @current_parent
    @explicit = @original_explicit
  end
  # STEP 1:
  # Attempt to read data element tag:
  tag = read_tag
  # Return nil if we have (naturally) reached the end of the data string.
  return nil unless tag
  # STEP 2:
  # Access library to retrieve the data element name and VR from the tag we just read:
  # (Note: VR will be overwritten in the next step if the DICOM string contains VR (explicit encoding))
  name, vr = LIBRARY.name_and_vr(tag)
  # STEP 3:
  # Read VR (if it exists) and the length value:
  vr, length = read_vr_length(vr,tag)
  level_vr = vr
  # STEP 4:
  # Reading value of data element.
  # Special handling needed for items in encapsulated image data:
  if @enc_image and tag == ITEM_TAG
    # The first item appearing after the image element is a 'normal' item, the rest hold image data.
    # Note that the first item will contain data if there are multiple images, and so must be read.
    vr = "OW" # how about alternatives like OB?
    # Modify name of item if this is an item that holds pixel data:
    if @current_element.tag != PIXEL_TAG
      name = PIXEL_ITEM_NAME
    end
  end
  # Read the binary string of the element:
  bin = read_bin(length) if length > 0
  # Read the value of the element (if it contains data, and it is not a sequence or ordinary item):
  if length > 0 and vr != "SQ" and tag != ITEM_TAG
    # Read the element's processed value:
    value = read_value(vr, length)
  else
    # Data element has no value (data).
    value = nil
    # Special case: Check if pixel data element is sequenced:
    if tag == PIXEL_TAG
      # Change name and vr of pixel data element if it does not contain data itself:
      name = ENCAPSULATED_PIXEL_NAME
      level_vr = "SQ"
      @enc_image = true
    end
  end
  # Create an Element from the gathered data:
  # if vr is UN ("unknown") and length is -1, treat as a sequence (sec. 6.2.2 of DICOM standard)
  if level_vr == "SQ" or tag == ITEM_TAG or (level_vr == "UN" and length == -1)
    if level_vr == "SQ" or (level_vr == "UN" and length == -1)
      check_duplicate(tag, 'Sequence')
      # If we get an unknown sequence with undefined length, we must switch to implicit for decoding its content:
      if level_vr == "UN" and length == -1
        @original_explicit = @explicit
        @explicit = false
        @explicitness_reset_parent = @current_parent
      end
      unless @current_parent[tag] and !@overwrite
        @current_element = Sequence.new(tag, :length => length, :name => name, :parent => @current_parent, :vr => vr)
      else
        # We have skipped a sequence. This means that any following children
        # of this sequence must be skipped as well. We solve this by creating an 'orphaned'
        # sequence that has a parent defined, but does not add itself to this parent:
        @current_element = Sequence.new(tag, :length => length, :name => name, :vr => vr)
        @current_element.set_parent(@current_parent)
      end
    elsif tag == ITEM_TAG
      # Create an Item:
      if @enc_image
        @current_element = Item.new(:bin => bin, :length => length, :name => name, :parent => @current_parent, :vr => vr)
      else
        @current_element = Item.new(:length => length, :name => name, :parent => @current_parent, :vr => vr)
      end
    end
    # Common operations on the two types of parent elements:
    if length == 0 and @enc_image
      # Set as parent. Exceptions when parent will not be set:
      # Item/Sequence has zero length & Item is a pixel item (which contains pixels, not child elements).
      @current_parent = @current_element
    elsif length != 0
      @current_parent = @current_element unless name == PIXEL_ITEM_NAME
    end
    # If length is specified (no delimitation items), load a new DRead instance to read these child elements
    # and load them into the current sequence. The exception is when we have a pixel data item.
    if length > 0 and not @enc_image
      @current_element.parse(bin, @transfer_syntax, switched=@switched, @explicit)
      @current_parent = @current_parent.parent
      return false unless @read_success
    end
  elsif DELIMITER_TAGS.include?(tag)
    # We do not create an element for the delimiter items.
    # The occurance of such a tag indicates that a sequence or item has ended, and the parent must be changed:
    @current_parent = @current_parent.parent
  else
    check_duplicate(tag, 'Element')
    unless @current_parent[tag] and !@overwrite
      @current_element = Element.new(tag, value, :bin => bin, :name => name, :parent => @current_parent, :vr => vr)
      # Check that the data stream didn't end abruptly:
      raise "The actual length of the value (#{@current_element.bin.length}) does not match its specified length (#{length}) for Data Element #{@current_element.tag}" if length != @current_element.bin.length
    end
  end
  # Return true to indicate success:
  return true
end
read(string, signature=true, options={}) click to toggle source

Builds a DICOM object by parsing an encoded DICOM string.

@param [String] string a binary DICOM string to be parsed @param [Boolean] signature if true (default), the parsing algorithm will look for the DICOM header signature @param [Hash] options the options to use for parsing the DICOM string @option options [Boolean] :overwrite for the rare case of a DICOM file containing duplicate elements, setting this as true instructs the parsing algorithm to overwrite the original element with duplicates @option options [String] :syntax if a syntax string is specified, the parsing algorithm is forced to use this transfer syntax when decoding the string

# File lib/dicom/d_read.rb, line 187
def read(string, signature=true, options={})
  # (Re)Set variables:
  @str = string
  @overwrite = options[:overwrite]
  # Presence of the official DICOM signature:
  @signature = false
  # Default explicitness of start of DICOM string (if undefined it defaults to true):
  @explicit = options[:explicit].nil? ? true : options[:explicit]
  # Default endianness of start of DICOM string is little endian:
  @str_endian = false
  # A switch of endianness may occur after the initial meta group, an this needs to be monitored:
  @switched_endian = false
  # Explicitness of the remaining groups after the initial 0002 group:
  @rest_explicit = false
  # Endianness of the remaining groups after the first group:
  @rest_endian = false
  # When the string switch from group 0002 to a later group we will update encoding values, and this switch will keep track of that:
  @switched = options[:switched] ? options[:switched] : false
  # Keeping track of the data element parent status while parsing the DICOM string:
  @current_parent = self
  # Keeping track of what is the current data element:
  @current_element = self
  # Items contained under the pixel data element may contain data directly, so we need a variable to keep track of this:
  @enc_image = false
  # Assume header size is zero bytes until otherwise is determined:
  @header_length = 0
  # Assume string will be read successfully and toggle it later if we experience otherwise:
  @read_success = true
  # Our encoding instance:
  @stream = Stream.new(@str, @str_endian)
  # If a transfer syntax has been specified as an option for a DICOM object,
  # make sure that it makes it into the object:
  if options[:syntax]
    @transfer_syntax = options[:syntax]
    Element.new("0002,0010", options[:syntax], :parent => self) if self.is_a?(DObject)
  end
  # Check for header information if indicated:
  if signature
    # Read and verify the DICOM header:
    header = check_header
    # If the string is without the expected header, we will attempt
    # to read data elements from the very start of the string:
    if header == false
      @stream.skip(-132)
    elsif header.nil?
      # Not a valid DICOM string, return:
      @read_success = false
      return
    end
  end
  # Run a loop which parses Data Elements, one by one, until the end of the data string is reached:
  data_element = true
  while data_element do
    # Using a rescue clause since processing Data Elements can cause errors when parsing an invalid DICOM string.
    begin
      # Extracting Data element information (nil is returned if end of the string is encountered in a normal way).
      data_element = process_data_element
    rescue Exception => msg
      # The parse algorithm crashed. Set data_element as false to break
      # the loop and toggle the success boolean to indicate failure.
      @read_success = false
      data_element = false
      # Output the raised message as a warning:
      logger.warn(msg.to_s)
      # Ouput the backtrace as debug information:
      logger.debug(msg.backtrace)
      # Explain the failure as an error:
      logger.error("Parsing a Data Element has failed. This is likely caused by an invalid DICOM encoding.")
    end
  end
end
read_bin(length) click to toggle source

Reads the data element's binary value string (varying length).

@param [Integer] length the length of the binary string to be extracted @return [String] the element value

# File lib/dicom/d_read.rb, line 264
def read_bin(length)
  return @stream.extract(length)
end
read_tag() click to toggle source

Reads the data element's tag (the 4 first bytes of a data element).

@return [String, NilClass] the element tag, or nil (if end of string reached)

# File lib/dicom/d_read.rb, line 272
def read_tag
  tag = @stream.decode_tag
  if tag
    # When we shift from group 0002 to another group we need to update our endian/explicitness variables:
    if tag.group != META_GROUP and @switched == false
      switch_syntax_on_read
      # We may need to read our tag again if endian has switched (in which case it has been misread):
      if @switched_endian
        @stream.skip(-4)
        tag = @stream.decode_tag
      end
    end
  end
  return tag
end
read_value(vr, length) click to toggle source

Decodes the data element's value (varying length).

  • Data elements which have multiple numbers as value, will have these numbers joined to a string, separated by the \ character.

  • For some value representations (OW, OB, OF, UN), a value is not processed, and nil is returned.

This means that for data like pixel data, compressed data, unknown data, a value is not available in the data element, and must be processed from the data element's binary variable.

@param [String] vr the value representation of the data element which the value to be decoded belongs to @param [Integer] length the length of the binary string to be extracted @return [String, NilClass] the data element value

# File lib/dicom/d_read.rb, line 300
def read_value(vr, length)
  unless vr == "OW" or vr == "OB" or vr == "OF" or vr == "UN"
    # Since the binary string has already been extracted for this data element, we must first "rewind":
    @stream.skip(-length)
    # Decode data:
    value = @stream.decode(length, vr)
    # If the returned value is an array of multiple values, we will join these values to a string with the separator "\":
    value = value.join("\\") if value.is_a?(Array)
  else
    # No decoded value:
    value = nil
  end
  return value
end
read_vr_length(vr, tag) click to toggle source

Reads the data element's value representation (2 bytes), as well as the data element's length (varying length: 2-6 bytes). The decoding scheme to be applied depends on explicitness, data element type and vr.

@param [String] vr the value representation that was retrieved from the dictionary for the tag of this data element @param [String] tag the tag of this data element @return [Array<String, Integer>] the value representation and length of the element

# File lib/dicom/d_read.rb, line 323
def read_vr_length(vr, tag)
  # Structure will differ, dependent on whether we have explicit or implicit encoding:
  reserved = 0
  bytes = 0
  # *****EXPLICIT*****:
  if @explicit == true
    # Step 1: Read VR, 2 bytes (if it exists - which are all cases except for the item related elements)
    vr = @stream.decode(2, "STR") unless ITEM_TAGS.include?(tag)
    # Step 2: Read length
    # Three possible structures for value length here, dependent on element vr:
    case vr
      when "OB","OW","OF","SQ","UN","UT"
        # 6 bytes total (2 reserved bytes preceeding the 4 byte value length)
        reserved = 2
        bytes = 4
      when ITEM_VR
        # For the item elements: "FFFE,E000", "FFFE,E00D" and "FFFE,E0DD":
        bytes = 4
      else
        # For all the other element vr, value length is 2 bytes:
        bytes = 2
    end
  else
    # *****IMPLICIT*****:
    bytes = 4
  end
  # Handle skips and read out length value:
  @stream.skip(reserved)
  if bytes == 2
    length = @stream.decode(bytes, "US") # (2)
  else
    length = @stream.decode(bytes, "SL") # (4)
  end
  # Check that length is valid (according to the DICOM standard, it must be even):
  raise "Encountered a Data Element (#{tag}) with an invalid (odd) value length." if length.odd? and length > 0
  return vr, length
end
split_and_add(string) click to toggle source

Splits a pre-encoded string in parts and adds it to the segments instance array.

@param [String] string a pre-encoded string

# File lib/dicom/d_write.rb, line 127
def split_and_add(string)
  # Duplicate the string as not to ruin the binary of the data element with our slicing:
  segment = string.dup
  append = segment.slice!(0, @max_size-@stream.length)
  # Clear out the stream along with a small part of the string:
  @segments << @stream.export + append
  if (30 + segment.length) > @max_size
    # The remaining part of the string is bigger than the max limit, fill up more segments:
    # How many full segments will this string fill?
    number = (segment.length/@max_size.to_f).floor
    start_index = 0
    number.times {
      @segments << segment.slice(start_index, @max_size)
      start_index += @max_size
    }
    # The remaining part is added to the stream:
    @stream.add_last(segment.slice(start_index, segment.length - start_index))
  else
    # The rest of the string is small enough that it can be added to the stream:
    @stream.add_last(segment)
  end
end
switch_syntax_on_read() click to toggle source

Changes encoding variables as the parsing proceeds past the initial meta group part (0002,xxxx) of the DICOM string.

# File lib/dicom/d_read.rb, line 364
def switch_syntax_on_read
  # Get the transfer syntax string, unless it has already been provided by keyword:
  @transfer_syntax = (self["0002,0010"] ? self["0002,0010"].value : IMPLICIT_LITTLE_ENDIAN) unless @transfer_syntax
  # Query the library with our particular transfer syntax string:
  ts = LIBRARY.uid(@transfer_syntax)
  logger.warn("Invalid/unknown transfer syntax: #{@transfer_syntax} Will try parsing the string, but errors may occur.") unless ts && ts.transfer_syntax?
  @rest_explicit = ts ? ts.explicit? : true
  @rest_endian = ts ? ts.big_endian? : false
  # Make sure we only run this method once:
  @switched = true
  # Update endian, explicitness and unpack variables:
  @switched_endian = true if @rest_endian != @str_endian
  @str_endian = @rest_endian
  @stream.endian = @rest_endian
  @explicit = @rest_explicit
end
switch_syntax_on_write() click to toggle source

Changes encoding variables as the file writing proceeds past the initial meta group part (0002,xxxx) of the DICOM object.

# File lib/dicom/d_write.rb, line 340
def switch_syntax_on_write
  # Process the transfer syntax string to establish encoding settings:
  ts = LIBRARY.uid(@transfer_syntax)
  logger.warn("Invalid/unknown transfer syntax: #{@transfer_syntax} Will complete encoding the file, but an investigation of the result is recommended.") unless ts && ts.transfer_syntax?
  @rest_explicit = ts ? ts.explicit? : true
  @rest_endian = ts ? ts.big_endian? : false
  # Make sure we only run this method once:
  @switched = true
  # Update explicitness and endianness (pack/unpack variables):
  @explicit = @rest_explicit
  @str_endian = @rest_endian
  @stream.endian = @rest_endian
end
write_data_element(element) click to toggle source

Encodes and writes a single data element.

@param [Element, Item, Sequence] element a data element

# File lib/dicom/d_write.rb, line 154
def write_data_element(element)
  # Step 1: Write tag:
  write_tag(element.tag)
  # Step 2: Write [VR] and value length:
  write_vr_length(element.tag, element.vr, element.length)
  # Step 3: Write value (Insert the already encoded binary string):
  write_value(element.bin)
  check_encapsulated_image(element)
end
write_data_elements(elements) click to toggle source

Iterates through the data elements, encoding/writing one by one. If an element has children, this method is repeated recursively.

@note Group length data elements are NOT written (they are deprecated/retired in the DICOM standard).

@param [Array<Element, Item, Sequence>] elements an array of data elements (sorted by their tags)

# File lib/dicom/d_write.rb, line 171
def write_data_elements(elements)
  elements.each do |element|
    # If this particular element has children, write these (recursively) before proceeding with elements at the current level:
    if element.is_parent?
      if element.children?
        # Sequence/Item with child elements:
        element.reset_length unless @enc_image
        write_data_element(element)
        write_data_elements(element.children)
        if @enc_image
          # Write a delimiter for the pixel tag, but not for its items:
          write_delimiter(element) if element.tag == PIXEL_TAG
        else
          write_delimiter(element)
        end
      else
        # Parent is childless:
        if element.bin
          write_data_element(element) if element.bin.length > 0
        elsif @include_empty_parents
          # Only write empty/childless parents if specifically indicated:
          write_data_element(element)
          write_delimiter(element)
        end
      end
    else
      # Ordinary Data Element:
      if element.tag.group_length?
        # Among group length elements, only write the meta group element (the others have been retired in the DICOM standard):
        write_data_element(element) if element.tag == "0002,0000"
      else
        write_data_element(element)
      end
    end
  end
end
write_delimiter(element) click to toggle source

Encodes and writes an Item or Sequence delimiter.

@param [Item, Sequence] element a parent element

# File lib/dicom/d_write.rb, line 212
def write_delimiter(element)
  delimiter_tag = (element.tag == ITEM_TAG ? ITEM_DELIMITER : SEQUENCE_DELIMITER)
  write_tag(delimiter_tag)
  write_vr_length(delimiter_tag, ITEM_VR, 0)
end
write_elements(options={}) click to toggle source

Handles the encoding of DICOM information to string as well as writing it to file.

@param [Hash] options the options to use for encoding the DICOM string @option options [String] :file_name the path & name of the DICOM file which is to be written to disk @option options [Boolean] :signature if true, the 128 byte preamble and 'DICM' signature is prepended to the encoded string @option options [String] :syntax the transfer syntax used for the encoding settings of the post-meta part of the DICOM string

# File lib/dicom/d_write.rb, line 225
def write_elements(options={})
  # Check if we are able to create given file:
  open_file(options[:file_name])
  # Go ahead and write if the file was opened successfully:
  if @file
    # Initiate necessary variables:
    @transfer_syntax = options[:syntax]
    # Until a DICOM write has completed successfully the status is 'unsuccessful':
    @write_success = false
    # Default explicitness of start of DICOM file:
    @explicit = true
    # Default endianness of start of DICOM files (little endian):
    @str_endian = false
    # When the file switch from group 0002 to a later group we will update encoding values, and this switch will keep track of that:
    @switched = false
    # Items contained under the Pixel Data element needs some special attention to write correctly:
    @enc_image = false
    # Create a Stream instance to handle the encoding of content to a binary string:
    @stream = Stream.new(nil, @str_endian)
    # Tell the Stream instance which file to write to:
    @stream.set_file(@file)
    # Write the DICOM signature:
    write_signature if options[:signature]
    write_data_elements(children)
    # As file has been written successfully, it can be closed.
    @file.close
    # Mark this write session as successful:
    @write_success = true
  end
end
write_signature() click to toggle source

Writes the DICOM header signature (128 bytes + 'DICM').

# File lib/dicom/d_write.rb, line 258
def write_signature
  # Write the string "DICM" which along with the empty bytes that
  # will be put before it, identifies this as a valid DICOM file:
  identifier = @stream.encode("DICM", "STR")
  # Fill in 128 empty bytes:
  filler = @stream.encode("00"*128, "HEX")
  @stream.write(filler)
  @stream.write(identifier)
end
write_tag(tag) click to toggle source

Encodes and writes a tag (the first part of the data element).

@param [String] tag a data element tag

# File lib/dicom/d_write.rb, line 272
def write_tag(tag)
  # Group 0002 is always little endian, but the rest of the file may be little or big endian.
  # When we shift from group 0002 to another group we need to update our endian/explicitness variables:
  switch_syntax_on_write if tag.group != META_GROUP and @switched == false
  # Write to binary string:
  bin_tag = @stream.encode_tag(tag)
  add_encoded(bin_tag)
end
write_value(bin) click to toggle source

Writes the data element's pre-encoded value.

@param [String] bin the binary string value of this data element

# File lib/dicom/d_write.rb, line 285
def write_value(bin)
  # This is pretty straightforward, just dump the binary data to the file/string:
  add_encoded(bin) if bin
end
write_vr_length(tag, vr, length) click to toggle source

Encodes and writes the value representation (if it is to be written) and length value. The encoding scheme to be applied here depends on explicitness, data element type and vr.

@param [String] tag the tag of this data element @param [String] vr the value representation of this data element @param [Integer] length the data element's length

# File lib/dicom/d_write.rb, line 297
def write_vr_length(tag, vr, length)
  # Encode the length value (cover both scenarios of 2 and 4 bytes):
  length4 = @stream.encode(length, "SL")
  length2 = @stream.encode(length, "US")
  # Structure will differ, dependent on whether we have explicit or implicit encoding:
  # *****EXPLICIT*****:
  if @explicit == true
    # Step 1: Write VR (if it is to be written)
    unless ITEM_TAGS.include?(tag)
      # Write data element VR (2 bytes - since we are not dealing with an item related element):
      add_encoded(@stream.encode(vr, "STR"))
    end
    # Step 2: Write length
    # Three possible structures for value length here, dependent on data element vr:
    case vr
      when "OB","OW","OF","SQ","UN","UT"
        if @enc_image # (4 bytes)
          # Item under an encapsulated Pixel Data (7FE0,0010).
          add_encoded(length4)
        else # (6 bytes total)
          # Two reserved bytes first:
          add_encoded(@stream.encode("00"*2, "HEX"))
          # Value length (4 bytes):
          add_encoded(length4)
        end
      when ITEM_VR # (4 bytes)
        # For the item elements: "FFFE,E000", "FFFE,E00D" and "FFFE,E0DD"
        add_encoded(length4)
      else # (2 bytes)
        # For all the other data element vr, value length is 2 bytes:
        add_encoded(length2)
    end
  else
    # *****IMPLICIT*****:
    # No VR written.
    # Writing value length (4 bytes):
    add_encoded(length4)
  end
end