dotnet / Open-XML-SDK

Open XML SDK by Microsoft
https://www.nuget.org/packages/DocumentFormat.OpenXml/
MIT License
4.04k stars 547 forks source link

Embedded documents with images breaks "Save As" on Word for Mac #645

Closed paulresdat closed 4 years ago

paulresdat commented 4 years ago

Is this a:

Description

Using OpenXML, appending a word document with embedded images into another word document using AddAlternativeFormatImportPart(AlternativeFormatImportPartType.WordprocessingML, altChunkId) causes an issue where the user can not "Save" or "Save As" when altering the content in Word for Mac.

Information

Repro

  1. Create a new Console App in Visual Studio on Mac
  2. Add a folder to the top level called "Docs"
  3. Download the Nuget dependency DocumentFormat.OpenXml
  4. Create a new word doc named "test-source.docx"
    1. Put some test text in there, doesn't matter what
    2. Add the document to the solution in "Docs" folder
    3. Change the property to "Copy always"
  5. Create a new word doc named "test-append.docx" (the embedded word doc)
    1. Put in some test text at the top
    2. Copy over a jpg of your choice. The image type I used to reproduce is a jpg. I fetched mine from http://apod.nasa.gov/apod/ , one of their big pics should do it.
    3. Add to the "Docs" folder in the solution
    4. Change property of document to "Copy always"
  6. Copy paste the code below into your Program.cs file:
using System.IO;
using System.Linq;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using Text = DocumentFormat.OpenXml.Wordprocessing.Text;

namespace TestWordAppend
{
    class Program
    {
        static void Main(string[] args)
        {
            var destinationDoc = "Docs/test-destination.docx";
            var sourceDoc = "Docs/test-source.docx";
            var appendedDoc = "Docs/test-append2.docx";

            File.Delete(destinationDoc);
            File.Copy(sourceDoc, destinationDoc);

            using (var doc = WordprocessingDocument.Open(destinationDoc, true))
            {
                var body = doc.MainDocumentPart.Document.Body;

                var p2 = body.InsertAfter(
                    new Paragraph(
                        new ParagraphProperties(
                            new PageBreakBefore()
                    )),
                    body.Elements<Paragraph>().Last()
                );

                var altChunkId = "AltChunkId";
                var chunk = doc.MainDocumentPart.AddAlternativeFormatImportPart(
                    AlternativeFormatImportPartType.WordprocessingML,
                    altChunkId);

                using (var fs = File.Open(appendedDoc, FileMode.Open))
                    chunk.FeedData(fs);

                var altChunk = new AltChunk { Id = altChunkId };
                var run = new Run(new TabChar(), new Text(appendedDoc), new Break());
                run.PrependChild(new RunProperties { RunStyle = new RunStyle { Val = "Bold" } });
                var fileP = p2.InsertAfterSelf(new Paragraph());
                fileP.Append(run);
                run = new Run(new TabChar(), new Text("Uploaded by: "), new TabChar(), new Text("Test User"));
                run.PrependChild(new RunProperties { RunStyle = new RunStyle { Val = "Italic" } });
                fileP.Append(run);

                body.Append(altChunk);
                doc.MainDocumentPart.Document.Save();
            }
        }
    }
}

Observed

When I open the destination document using Word on Mac, I will indeed see the document contents embedded in the destination document, however I can no longer utilize "Save" or "Save As". Upon clicking "save" on a "Save As" when changing the name of the file, you will see the file name revert back to the original document name in the title and when you go to your "Save As" destination, you will not find a new document. This problem does not exist in Word for Windows.

Expected

The document should be able to be altered and saved within the MacOS Word application.

ThomasBarnekow commented 4 years ago

@paulresdat, thank you very much for this excellent issue description, which is a perfect example of how one should do it.

Based on your description, I reproduced the behavior. Microsoft Word for Mac shows the behavior you describe, and Microsoft Word for Windows works as expected. I even made some changes to your sample code to see whether that makes a difference because your sample code has a few issues:

Finally, I streamlined the code a little and cast it as a unit test. Here's the result:

using System;
using System.IO;
using System.Linq;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using Xunit;

namespace CodeSnippets.Tests.OpenXml.Wordprocessing
{
    public class AltChunkImagesTests
    {
        private static AltChunk CreateAltChunkFromWordDocument(
            string sourcePath,
            WordprocessingDocument destWordDocument)
        {
            string altChunkId = "AltChunkId-" + Guid.NewGuid();

            AlternativeFormatImportPart chunk = destWordDocument.MainDocumentPart.AddAlternativeFormatImportPart(
                AlternativeFormatImportPartType.WordprocessingML, altChunkId);

            using FileStream fs = File.Open(sourcePath, FileMode.Open);
            chunk.FeedData(fs);

            return new AltChunk { Id = altChunkId };
        }

        [Fact]
        public void CanAppendDocumentWithImage()
        {
            const string destinationDoc = "test-destination.docx";
            const string sourceDoc = "Resources/test-source.docx";
            const string appendedDoc = "Resources/test-append.docx";

            File.Copy(sourceDoc, destinationDoc, true);

            using WordprocessingDocument destWordDocument = WordprocessingDocument.Open(destinationDoc, true);
            Body body = destWordDocument.MainDocumentPart.Document.Body;

            Paragraph p2 = body.InsertAfter(
                new Paragraph(
                    new ParagraphProperties(
                        new PageBreakBefore()),
                    new Run(
                        new RunProperties(new Bold()),
                        new TabChar(),
                        new Text(appendedDoc)),
                    new Run(
                        new Break()),
                    new Run(
                        new RunProperties(new Italic()),
                        new TabChar(),
                        new Text("Uploaded by:")),
                    new Run(
                        new RunProperties(new Italic()),
                        new TabChar(),
                        new Text("Test User"))),
                body.Elements<Paragraph>().Last());

            p2.InsertAfterSelf(
                CreateAltChunkFromWordDocument(appendedDoc, destWordDocument));
        }
    }
}

Unfortunately, as already mentioned above, Microsoft Word for Mac shows the same behavior even with my "improved" version, which works perfectly well with Microsoft Word for Windows. I've also uploaded test-destination.docx to SharePoint and opened the document in Word Online, which also shows the appended document (test-append.docx) with the image. Word Online is just not able to edit the document, saying that it contains objects that the online version does not support. Instead, it asks you to open the document with the Word application.

So, what do we have now?

Thus, my strong hypothesis is that it is a bug (or limitation in conjunction with a usability issue) of Microsoft Word for Mac rather than an issue with the Open XML SDK.

Would you be able to test this with another application that supports Open XML? Unfortunately, I don't have any other wordprocessing applications that I could use for testing.

paulresdat commented 4 years ago

@ThomasBarnekow - thank you for taking the timeout to test this and for the kind words! 👍

I'm in the process of downloading Open Office so I will follow up with a verdict on a different applicaiton. However as far as our environment at work, we do not use Open Office. We're tied to Microsoft and so a work around to use another application won't suffice as a solution for us.

However I've come up with a code work around that basically copies the internals of one document to another. To me this is hackish but could suffice for the interim. I have a question in the meantime: If this is a bug with Microsoft Word, how can this issue be escalated to the Word team so that it can be addressed for Word on Mac?

twsouthwick commented 4 years ago

Thanks @ThomasBarnekow for your investigation. As this is not an issue with the SDK, i'll close the issue. Please use stack overflow for any Q&A of how to use the SDK. Thanks!