Michael Scott
Michael Scott

Reputation: 11

Using LibTiff.Net to inject tags inside TiffTag.IMAGEDESCRIPTION damages file

We are trying to inject a TimestampAnnotation into the StructuredAnnotations section of an OME-Tiff file. I'm getting the annotation in there correctly, but when I try to reload the tiff, all the images are damaged. Is there an easier way to do this?

OME is a list of classes generated from the OME-tiff schema that we obtained (then generated our own) from

https://github.com/boonepeter/OME-Tiff-Csharp

private bool InjectTimeStampIntoSlide()
{
    bool success = false;
    try
    {
        using (Tiff image = Tiff.Open(SlideName, "a"))
        {
            // Image 0 must be the highest res image of the pyramid
            image.SetDirectory(0);

            // Get the Image Header for this image
            TaggedImageHeader header = new TaggedImageHeader(image);
            bool hasSubIfds = header.SubIfd > 0;
            bool hasTiles = image.IsTiled();

            if (hasSubIfds && hasTiles)
            {
                OME omeMetadata;
                var fieldValueArray = image.GetField(TiffTag.IMAGEDESCRIPTION);
                string omeMetadataStr = fieldValueArray[0].ToString();
                
                XmlSerializer serializer = new XmlSerializer(typeof(OME));
                using (TextReader tr = new StringReader(omeMetadataStr))
                {
                    omeMetadata = (OME)serializer.Deserialize(tr);
                }

                // Convert to List
                List<object> lstItems = new List<object>(omeMetadata.Items.ToList());

                // Load StructuredAnnotations
                StructuredAnnotations structuredAnnotations =
                    (StructuredAnnotations)lstItems.FirstOrDefault(
                        i => i.GetType() == typeof(StructuredAnnotations));

                // We need the Index, so we can delete / replace it later
                int structuralAnnotationsIdx =
                    lstItems.FindIndex(i => i.GetType() == typeof(StructuredAnnotations));

                if (structuredAnnotations == null)
                    structuredAnnotations = new StructuredAnnotations();

                // Get and / or create list of Annotations
                List<Annotation> lstAnnotations = new List<Annotation>();
                if (structuredAnnotations.Items?.Length > 0)
                    lstAnnotations = structuredAnnotations.Items.ToList();

                // Add TimestampAnnotation
                lstAnnotations.Add(new TimestampAnnotation()
                    { Description = $"UserName: {Environment.UserName}", Value = DateTime.Now });

                // Delete old Structured Annotations
                if (structuralAnnotationsIdx >= 0)
                    lstItems.RemoveAt(structuralAnnotationsIdx);

                // Add then add new one back
                structuredAnnotations.Items = lstAnnotations.ToArray();
                lstItems.Add((object)structuredAnnotations);

                // Now update Metadata Items
                omeMetadata.Items = lstItems.ToArray();

                // And convert it to XML string
                omeMetadataStr = string.Empty;

                // Using StringWriter makes Encoding utf-16, and we need utf-8.
                // So created the class ExtendedStringWriter, and now using
                // StringBuilder to assist

                StringBuilder sb = new StringBuilder();
                using (ExtendedStringWriter sw = new ExtendedStringWriter(sb, Encoding.UTF8))
                {
                    serializer.Serialize(sw, omeMetadata);
                    omeMetadataStr = sb.ToString();
                }

                image.SetField(TiffTag.IMAGEDESCRIPTION, omeMetadataStr);

                success = image.WriteDirectory();
            }
        }

        success = true;
    }
    catch (Exception ex)
    {
        Log4NetHelper.WriteExceptionToLog(Log, ex, GetType(), MethodBase.GetCurrentMethod().Name);
    }

    return success;
}

Upvotes: 1

Views: 212

Answers (1)

Bobrovsky
Bobrovsky

Reputation: 14236

You are updating an existing directory with WriteDirectory. This causes the data to be written in the file starting at the same location/offset. If the new size of the directory is larger than the previous one, the following data will be corrupted.

The proper way is to use RewriteDirectory instead of WriteDirectory. As stated in the documentation for this method:

The RewriteDirectory operates similarly to WriteDirectory, but can be called with directories previously read or written that already have an established location in the file. It will rewrite the directory, but instead of place it at it's old location (as would) it will place them at the end of the file, correcting the pointer from the preceeding directory or file header to point to it's new location. This is particularly important in cases where the size of the directory and pointed to data has grown, so it won’t fit in the space available at the old location. Note that this will result in the loss of the previously used directory space.

Upvotes: 1

Related Questions