def save_image(xdata: DataAndMetadata.DataAndMetadata, file):
    """
    Saves the nparray data to the file-like object (or string) file.
    """
    # we need to create a basic DM tree suitable for an image
    # we'll try the minimum: just an data list
    # doesn't work. Do we need a ImageSourceList too?
    # and a DocumentObjectList?

    data = xdata.data
    data_descriptor = xdata.data_descriptor
    dimensional_calibrations = xdata.dimensional_calibrations
    intensity_calibration = xdata.intensity_calibration
    metadata = xdata.metadata
    modified = xdata.timestamp
    timezone = xdata.timezone
    timezone_offset = xdata.timezone_offset
    needs_slice = False
    is_sequence = False

    if len(data.shape) == 3 and data.dtype != numpy.uint8 and data_descriptor.datum_dimension_count == 1:
        data = numpy.moveaxis(data, 2, 0)
        dimensional_calibrations = (dimensional_calibrations[2],) + tuple(dimensional_calibrations[0:2])
    if len(data.shape) == 2 and data.dtype != numpy.uint8 and data_descriptor.datum_dimension_count == 1:
        is_sequence = data_descriptor.is_sequence
        data = numpy.moveaxis(data, 1, 0)
        data = numpy.expand_dims(data, axis=1)
        dimensional_calibrations = (dimensional_calibrations[1], Calibration.Calibration(), dimensional_calibrations[0])
        data_descriptor = DataAndMetadata.DataDescriptor(False, 2, 1)
        needs_slice = True
    data_dict = ndarray_to_imagedatadict(data)
    ret = {}
    ret["ImageList"] = [{"ImageData": data_dict}]
    if dimensional_calibrations and len(dimensional_calibrations) == len(data.shape):
        dimension_list = data_dict.setdefault("Calibrations", dict()).setdefault("Dimension", list())
        for dimensional_calibration in reversed(dimensional_calibrations):
            dimension = dict()
            if dimensional_calibration.scale != 0.0:
                origin = -dimensional_calibration.offset / dimensional_calibration.scale
            else:
                origin = 0.0
            dimension['Origin'] = origin
            dimension['Scale'] = dimensional_calibration.scale
            dimension['Units'] = dimensional_calibration.units
            dimension_list.append(dimension)
    if intensity_calibration:
        if intensity_calibration.scale != 0.0:
            origin = -intensity_calibration.offset / intensity_calibration.scale
        else:
            origin = 0.0
        brightness = data_dict.setdefault("Calibrations", dict()).setdefault("Brightness", dict())
        brightness['Origin'] = origin
        brightness['Scale'] = intensity_calibration.scale
        brightness['Units'] = intensity_calibration.units
    if modified:
        timezone_str = None
        if timezone_str is None and timezone:
            try:
                import pytz
                tz = pytz.timezone(timezone)
                timezone_str = tz.tzname(modified)
            except:
                pass
        if timezone_str is None and timezone_offset:
            timezone_str = timezone_offset
        timezone_str = " " + timezone_str if timezone_str is not None else ""
        date_str = modified.strftime("%x")
        time_str = modified.strftime("%X") + timezone_str
        ret["DataBar"] = {"Acquisition Date": date_str, "Acquisition Time": time_str}
    # I think ImageSource list creates a mapping between ImageSourceIds and Images
    ret["ImageSourceList"] = [{"ClassName": "ImageSource:Simple", "Id": [0], "ImageRef": 0}]
    # I think this lists the sources for the DocumentObjectlist. The source number is not
    # the indxe in the imagelist but is either the index in the ImageSourceList or the Id
    # from that list. We also need to set the annotation type to identify it as an data
    ret["DocumentObjectList"] = [{"ImageSource": 0, "AnnotationType": 20}]
    # finally some display options
    ret["Image Behavior"] = {"ViewDisplayID": 8}
    dm_metadata = copy.deepcopy(metadata)
    if metadata.get("hardware_source", dict()).get("signal_type", "").lower() == "eels":
        if len(data.shape) == 1 or (len(data.shape) == 2 and data.shape[0] == 1):
            dm_metadata.setdefault("Meta Data", dict())["Format"] = "Spectrum"
            dm_metadata.setdefault("Meta Data", dict())["Signal"] = "EELS"
    elif data_descriptor.collection_dimension_count == 2 and data_descriptor.datum_dimension_count == 1:
        dm_metadata.setdefault("Meta Data", dict())["Format"] = "Spectrum image"
        dm_metadata.setdefault("Meta Data", dict())["Signal"] = "EELS"
    elif data_descriptor.datum_dimension_count == 1:
        dm_metadata.setdefault("Meta Data", dict())["Format"] = "Spectrum"
    if (1 if data_descriptor.is_sequence else 0) + data_descriptor.collection_dimension_count == 1 or needs_slice:
        if data_descriptor.is_sequence or is_sequence:
            dm_metadata.setdefault("Meta Data", dict())["IsSequence"] = True
        ret["ImageSourceList"] = [{"ClassName": "ImageSource:Summed", "Do Sum": True, "Id": [0], "ImageRef": 0, "LayerEnd": 0, "LayerStart": 0, "Summed Dimension": len(data.shape) - 1}]
        if needs_slice:
            ret["DocumentObjectList"][0]["AnnotationGroupList"] = [{"AnnotationType": 23, "Name": "SICursor", "Rectangle": (0, 0, 1, 1)}]
            ret["DocumentObjectList"][0]["ImageDisplayType"] = 1  # display as an image
    if modified:
        dm_metadata["Timestamp"] = modified.isoformat()
    if timezone:
        dm_metadata["Timezone"] = timezone
    if timezone_offset:
        dm_metadata["TimezoneOffset"] = timezone_offset
    ret["ImageList"][0]["ImageTags"] = dm_metadata
    ret["InImageMode"] = True
    parse_dm3.parse_dm_header(file, ret)