docs: def read_header(file, custom_field_map=None):
      """Read contents of header and parse values from :obj:`file`
  
      :obj:`file` can be a filename indicating where the NRRD header is located or a string iterator object. If a
      filename is specified, then the file will be opened and closed after the header is read from it. If not specifying
      a filename, the :obj:`file` parameter can be any sort of iterator that returns a string each time :meth:`next` is
      called. The two common objects that meet these requirements are file objects and a list of strings. When
      :obj:`file` is a file object, it must be opened with the binary flag ('b') on platforms where that makes a
      difference, such as Windows.
  
      See :ref:`user-guide:Reading NRRD files` for more information on reading NRRD files.
  
      Parameters
      ----------
      file : :class:`str` or string iterator
          Filename, file object or string iterator object to read NRRD header from
      custom_field_map : :class:`dict` (:class:`str`, :class:`str`), optional
          Dictionary used for parsing custom field types where the key is the custom field name and the value is a
          string identifying datatype for the custom field.
  
      Returns
      -------
      header : :class:`dict` (:class:`str`, :obj:`Object`)
          Dictionary containing the header fields and their corresponding parsed value
  
      See Also
      --------
      :meth:`read`, :meth:`read_data`
      """
  
      # If the file is a filename rather than the file handle, then open the file and call this function again with the
      # file handle. Since read function uses a filename, it is easy to think read_header is the same syntax.
      if isinstance(file, str) and file.count('\n') == 0:
          with open(file, 'rb') as fh:
              header = read_header(fh, custom_field_map)
              return header
  
      # Collect number of bytes in the file header (for seeking below)
      header_size = 0
  
      # Get iterator for the file and extract the first line, the magic line
      it = iter(file)
      magic_line = next(it)
  
      # Depending on what type file is, decoding may or may not be necessary. Decode if necessary, otherwise skip.
      need_decode = False
      if hasattr(magic_line, 'decode'):
          need_decode = True
          magic_line = magic_line.decode('ascii', 'ignore')
  
      # Validate the magic line and increment header size by size of the line
      header_size += _validate_magic_line(magic_line)
  
      # Create empty header
      # This is an OrderedDict rather than an ordinary dict because an OrderedDict will keep it's order that key/values
      # are added for when looping back through it. The added benefit of this is that saving the header will save the
      # fields in the same order.
      header = OrderedDict()
  
      # Loop through each line
      for line in it:
          header_size += len(line)
          if need_decode:
              line = line.decode('ascii', 'ignore')
  
          # Trailing whitespace ignored per the NRRD spec
          line = line.rstrip()
  
          # Skip comments starting with # (no leading whitespace is allowed)
          # Or, stop reading the header once a blank line is encountered. This separates header from data.
          if line.startswith('#'):
              continue
          elif line == '':
              break
  
          # Read the field and value from the line, split using regex to search for := or : delimiter
          field, value = re.split(r':=?', line, 1)
  
          # Remove whitespace before and after the field and value
          field, value = field.strip(), value.strip()
  
          # Check if the field has been added already
          if field in header.keys():
              dup_message = "Duplicate header field: '%s'" % str(field)
  
              if not ALLOW_DUPLICATE_FIELD:
                  raise NRRDError(dup_message)
  
              warnings.warn(dup_message)
  
          # Get the datatype of the field based on it's field name and custom field map
          field_type = _get_field_type(field, custom_field_map)
  
          # Parse the field value using the datatype retrieved
          # Place it in the header dictionary
          header[field] = _parse_field_value(value, field_type)
  
      # Reading the file line by line is buffered and so the header is not in the correct position for reading data if
      # the file contains the data in it as well. The solution is to set the file pointer to just behind the header.
      if hasattr(file, 'seek'):
          file.seek(header_size)
  
      return header