Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New ImagePart is created as .bin instead of real extension #1305

Closed
Asbjoedt opened this issue Jan 13, 2023 · 13 comments · Fixed by #1380
Closed

New ImagePart is created as .bin instead of real extension #1305

Asbjoedt opened this issue Jan 13, 2023 · 13 comments · Fixed by #1380
Assignees
Milestone

Comments

@Asbjoedt
Copy link
Contributor

Asbjoedt commented Jan 13, 2023

Describe the bug
I have asked the question on Stackoverflow:
https://stackoverflow.com/questions/75100804/embedding-image-as-new-imagepart-creates-a-bin-file-using-open-xml-sdk

I don't expect to get any answer there, and I am also increasingly curious whether, this is an implementation error but an issue in the SDK.

Issue is, I create new embedded image in Excel and it is saved to /xl/media/image.[WhatEverExtensionItIs]
If I create a new ImagePart with the SDK and feed it data like this:

ImagePart new_ImagePart = worksheetPart.AddImagePart(ImagePartType.Tiff);
stream.Position = 0;
new_ImagePart.FeedData(stream);

Then the file will be saved as /xl/media/image.bin.

This behaviour it should not do. It should write the extension as .tiff, since this is the ImagePartType, that I specify. Or secondly it should read the stream and identify it as .tiff through i.e. a magic number.

If you unzip the package and change the image extension to real extension, then the image will open perfectly and I have used a TIFF validator to confirm the image is well-formed.

What do you think of this? Am I doing anything wrong in my implementation, or can we change the SDK to write real extension like Excel does?

Screenshots
If applicable, add screenshots to help explain your problem.

To Reproduce
Steps to reproduce the behavior:

  1. Use the supplied code to stream any TIFF image (or change ImagePartType to any other image type) to a new ImagePart
  2. Unzip the spreadsheet and go to /xl/media/ to find the file as a .bin
  3. Change extension to .tiff and the image will open perfectly

Observed behavior
ImagePart creates a new image with .bin extension

Expected behavior
ImagePart should create a new image with extension based on ImagePartType.

Desktop (please complete the following information):

  • OS: Windows 11
  • Office version: 2212
  • .NET Target: .Net latest version
  • DocumentFormat.OpenXml Version: 2.19
@Asbjoedt
Copy link
Contributor Author

Will deleting the following lines be enough to prevent this behaviour? I am imitating this #1292 fix, that @mikeebowen made. He replaced .xml with .bin. Instead, I delete the same lines.

From: ImagePart.json line 6

"Extension": ".bin",

From: PartConstraintData.json line 1518

"TargetFileExtension": ".bin",

From: ImagePart.cs line 38

string ITargetFeature.Extension => ".bin";

@faerbersteve
Copy link

I'm having the same issue within the Word document when embedding png files.
In compare with the older version 2.0.5022 of DocumentFormat.OpenXml the image is added there as .png.

@Asbjoedt Do you have a working fix for the issue?

@Asbjoedt
Copy link
Contributor Author

Asbjoedt commented Jan 23, 2023

@faerbersteve I think deleting the above lines are maybe only a part of the way.

I found the following methods in the SDK. I don't have a clear overview of how to proceed from here though.

       internal static string GetContentType(ImagePartType imageType)
            => imageType switch
            {
                ImagePartType.Bmp => "image/bmp",
                ImagePartType.Gif => "image/gif",
                ImagePartType.Png => "image/png",
                ImagePartType.Tiff => "image/tiff",

                // ImagePartType.Xbm => "image/xbm",
                ImagePartType.Icon => "image/x-icon",
                ImagePartType.Pcx => "image/x-pcx",

                // ImagePartType.Pcz => "image/x-pcz",
                // ImagePartType.Pict => "image/pict",
                ImagePartType.Jpeg => "image/jpeg",
                ImagePartType.Emf => "image/x-emf",
                ImagePartType.Wmf => "image/x-wmf",
                ImagePartType.Svg => "image/svg+xml",
                _ => throw new ArgumentOutOfRangeException(nameof(imageType)),
            };
        internal static string GetTargetExtension(ImagePartType imageType)
            => imageType switch
            {
                ImagePartType.Bmp => ".bmp",
                ImagePartType.Gif => ".gif",
                ImagePartType.Png => ".png",
                ImagePartType.Tiff => ".tiff",

                // ImagePartType.Xbm => ".xbm",
                ImagePartType.Icon => ".ico",
                ImagePartType.Pcx => ".pcx",

                // ImagePartType.Pcz => ".pcz",
                // ImagePartType.Pict => ".pict",
                ImagePartType.Jpeg => ".jpg",
                ImagePartType.Emf => ".emf",
                ImagePartType.Wmf => ".wmf",
                ImagePartType.Svg => ".svg",
                _ => ".image",
            };
        /// <summary>
        /// Adds a ImagePart to the DrawingsPart
        /// </summary>
        /// <param name="partType">The part type of the ImagePart</param>
        /// <param name="id">The relationship id</param>
        /// <return>The newly added part</return>
        public ImagePart AddImagePart(ImagePartType partType, string id)
        {
            var contentType = ImagePartTypeInfo.GetContentType(partType);
            var partExtension = ImagePartTypeInfo.GetTargetExtension(partType);
            Features.GetRequired<IPartExtensionFeature>().Register(contentType, partExtension);
            return AddImagePart(contentType, id);
        }

@Asbjoedt
Copy link
Contributor Author

Hi @twsouthwick, @tomjebo

I have written a fix for this, you can see here: https://github.com/dotnet/Open-XML-SDK/compare/main...Asbjoedt:Open-XML-SDK:ImagePartFix?expand=1

I am not entirely sure the fix works, because I have problems building Open XML SDK to test the new code (approx. 200 namespace references are missing). Maybe you can help me out somehow.

The fix does two things:

  • Remove any .bin references for ImagePart
  • Clean up ImagePart code by removing several lines of outcommented code related to image file formats, that were not implemented when the initial code were made.
  • The code also recommits the code in the recently approved PR for Add two extensions to GetImagePartType #1304 (this is a mistake from me, I will be better at keeping commits properly branched in the future, but the recommit should not be an issue)

@twsouthwick
Copy link
Member

This file is where the .bin is coming from:

"Extension": ".bin",

@Asbjoedt
Copy link
Contributor Author

Asbjoedt commented Apr 4, 2023

Great! thx.
I will verify this when 2.20 is released.

mikeebowen added a commit to mikeebowen/Open-XML-SDK that referenced this issue Apr 4, 2023
* fix boolean logic for file extension when adding OpenXmlPart. Fixes dotnet#1305

* add tests for correct file extension when adding OpenXmlPart
mikeebowen added a commit to mikeebowen/Open-XML-SDK that referenced this issue Apr 5, 2023
mikeebowen added a commit that referenced this issue Apr 5, 2023
* Fix boolean logic for finding file extensions
* add test for correct file extension when adding ImagePart. Fixes #1305
mikeebowen added a commit to mikeebowen/Open-XML-SDK that referenced this issue Apr 5, 2023
tomjebo pushed a commit that referenced this issue Apr 5, 2023
@Asbjoedt
Copy link
Contributor Author

Asbjoedt commented Apr 8, 2023

Confirmed fix.

@Wenfengcheng
Copy link

Wenfengcheng commented Apr 17, 2023

@mikeebowen Not fixed for ImagePart generated by this way in version 2.20:

public static string AppendImagePart(this TypedOpenXmlPart typedOpenXmlPart, byte[] stream, string contentType)
{
    var rId = typedOpenXmlPart.GetNextRelationshipId();

    var imagePart = typedOpenXmlPart.AddNewPart<ImagePart>(contentType, rId);

    using var mStream = new MemoryStream();

    mStream.Write(stream, 0, stream.Length);

    mStream.Position = 0;

    imagePart.FeedData(mStream);

    return rId;
}

@mikeebowen
Copy link
Collaborator

@Wenfengcheng , What is the issue you're seeing? Does it use the wrong extension? Does it throw an error? Something else?

@Wenfengcheng
Copy link

@mikeebowen yeah, the extension is wrong which is .bin file, not the origin image extension.

@psyvision
Copy link

psyvision commented Jul 3, 2023

+1 can confirm the same issue of a .bin in /media/image.bin using v2.20 when saving a word document:

var codes = ImageCodecInfo.GetImageDecoders();
var mimeType = codes.First(c => c.FormatID == image.RawFormat.Guid).MimeType;

var imagePart = mainPart.AddImagePart(mimeType); // image/jpeg

using var stream = new MemoryStream();
image.Save(stream, image.RawFormat);
stream.Position = 0;
imagePart.FeedData(stream);

@twsouthwick
Copy link
Member

@mikeebowen can you take a look to see what's different here?

@twsouthwick
Copy link
Member

We'll track this in the new issue as it looks subtly different

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment