none
AlternativeFormatImportPart (AltChunk) and WordprocessingDocument in MemoryStream creates a corrupt file

    Question

  • Hi, I have a project where i need to add plain text as well as the content of small .docx files to a main .docx.
    I have created a test case, and with the use of AlternativeFormatImportParti I can successfully perform the following code to add plain text in my files.

    string chunkId = "D"+Regex.Replace(DateTime.Now.ToString(), @"[^\d]", "");
    
    AlternativeFormatImportPart chunk = _mainDocPart.AddAlternativeFormatImportPart(
    AlternativeFormatImportPartType.TextPlain, chunkId);
    
    using (var memstream = new MemoryStream(Encoding.UTF8.GetBytes(text)))
    {
    	chunk.FeedData(memstream);
    }
    
    AltChunk altChunk = new AltChunk();
    altChunk.Id = chunkId;
    
    OpenXmlElement parent = placeHolder.Parent;
    parent.InsertBefore(altChunk, placeHolder);

    The problem is that in my actual environment I need to load WordprocessingDocument  from a memory stream.
    It seems like when i do this the saved file cannot be opened by Word (Corrupt file)

    I can add paragraphs and other stuff, but not AltChunck when I initiate the WordprocessingDocument as follows

    var byteArrayWithFileFrom360 = ProcessFileHandler.GetFileContent(204735);
    
    var wordDocMemoryStream = new MemoryStream();
    wordDocMemoryStream.Write(byteArrayWithFileFrom360, 0, byteArrayWithFileFrom360.Length);
    
    var myDoc = WordprocessingDocument.Open(wordDocMemoryStream, true);

     
    jeudi 19 avril 2018 11:49

Toutes les réponses

  • Hello,

    It seems that you want to insert a text to a word document, right? To be honestly, I'm not familiar with AlternativeFormatImportPart. I'm wondering why you need use it. For inserting text, what about adding a paragraph?

    For your code, I failed to reproduce it since I'm not clear what is the placeHolder object. Would you mind share whole code?

    Best Regards,

    Terry


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.


    vendredi 20 avril 2018 03:21
    Modérateur
  • I do not want to use paragraph because the text that is to be inserted are formated, with both colors and different styling. The placeholder object of SdtBlock type.

    I loop trough all ContentControls in a Word file with the given TAG, then try to add text from a Word file.
    To Ensure its not a error in the incomming Word file I have tried with plain text as follows:

    using (var memstream = new MemoryStream(Encoding.UTF8.GetBytes(text)))
    
    Just to try, but that doesnt work either.

    This is how i collect the placeholders.

    List<SdtBlock> placeHolders = _mainDocPart.Document.Descendants<SdtBlock>()
    .Where(s3 => s3.GetFirstChild<SdtProperties>()
    .GetFirstChild<Tag>() != null
    && s3.GetFirstChild<SdtProperties>()
    .GetFirstChild<Tag>().Val.Value.Contains("ContentControlNameInWordFile")).ToList(); foreach (var placeHolder in placeHolders)


    mardi 24 avril 2018 09:15
  • Hello BjørnDev,

    Thanks for the new information. I think I have got your mean. You want to insert text from another file with open xml. Your code works while you open the main document using document path. However, once you open the main document from memory stream, it failed, right?

    I checked the code you shared to open the document from memory. ProcessFileHandler is a custom class and I would not know what you did in the GetFileContent method.

    Have you tried to open the document from memory like this.

                byte[] byteArray = File.ReadAllBytes(@"C:\Users\terryx\Desktop\TestFolder\Test.docx");
                using (MemoryStream wordDocMemoryStream = new MemoryStream())
                {
                    wordDocMemoryStream.Write(byteArray, 0, (int)byteArray.Length);
                    using (WordprocessingDocument myDoc = WordprocessingDocument.Open(wordDocMemoryStream, true))
                    {
                        //do the operation
                    }
                }

    Best Regards,

    Terry


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    mardi 24 avril 2018 10:09
    Modérateur
  • Hi I have written a complete example that shows what i mean. Yes I want to insert text from a word file to another and it works by using filepath, but not when loading WordProcessingDocument from Memory as the following example does.

     [TestMethod]
            public void InsertTextFromAnotherDocX()
            {
                var mainFilePath = @"C:\WordTesting\Main.docx";
                var resultFilePath = @"C:\WordTesting\Result.docx";
                var additionalFilePath = @"C:\WordTesting\Phrase.docx";
                var contentControlTag = "Demo";
    
                byte[] byteArray = File.ReadAllBytes(mainFilePath);
                using (MemoryStream wordDocMemoryStream = new MemoryStream())
                {
                    wordDocMemoryStream.Write(byteArray, 0, (int)byteArray.Length);
                    using (WordprocessingDocument myDoc = WordprocessingDocument.Open(wordDocMemoryStream, true))
                    {
                        // Get content control in the main doc named "Demo"
                        List<SdtBlock> sdtList = myDoc.MainDocumentPart.Document.Descendants<SdtBlock>()
                        .Where(s3 => s3.GetFirstChild<SdtProperties>().GetFirstChild<Tag>() != null
                         && s3.GetFirstChild<SdtProperties>().GetFirstChild<Tag>().Val.Value.Contains(contentControlTag)).ToList();
    
                        // Load additional file
                        using (MemoryStream memStream = new MemoryStream())
                        {
                            // Load additional file to mem                        
                            byte[] byteArrayAdditionalFile = File.ReadAllBytes(additionalFilePath);
                            memStream.Write(byteArrayAdditionalFile, 0, (int)byteArrayAdditionalFile.Length);
    
                            // Insert the 'additional' file content before every content control named 'Demo' in main file (memory)
                            var chunkId = 0;
                            foreach (var placeHolder in sdtList)
                            {
                                AlternativeFormatImportPart chunk = myDoc.MainDocumentPart.AddAlternativeFormatImportPart(
                                AlternativeFormatImportPartType.WordprocessingML, "D" + chunkId);
    
                                chunk.FeedData(memStream);
                                AltChunk altchunk = new AltChunk();
                                altchunk.Id = "D" + chunkId;
    
                                OpenXmlElement parent = placeHolder.Parent;
                                parent.InsertBefore(altchunk, placeHolder);
    
                                chunkId++;
                            }
                        }
    
                        // Save to new file
                        using (Stream fileStream = File.Open(resultFilePath, FileMode.Create))
                        {
                            myDoc.MainDocumentPart.Document.Save();
    
                            var resultFileByteArray = wordDocMemoryStream.ToArray();
    
                            fileStream.Write(resultFileByteArray,0, resultFileByteArray.Length);
                        }
                    }
                }
            }


    • Modifié BjørnDev mardi 24 avril 2018 20:53
    mardi 24 avril 2018 17:46
  • Hello BjørnDev,

    I'm trying to involve some senior engineers into this issue and it will take some time. Your patience will be greatly appreciated.

    Sorry for any inconvenience and have a nice day! 

    Best Regards,

    Terry


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    jeudi 26 avril 2018 02:44
    Modérateur
  • Hi BjørnDev,

    After the investigation, there are two issues for this scenario.

    First issue is that the output is different when we add a AlternativeFormatImportPart via AddAlternativeFormatImportPart function for memory and file stream. Here are the test for your reference:

    Memory stream( Content_types.xml):

    <?xml version="1.0" encoding="UTF-8" standalone="true"?>
    
    <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
    
    <Default ContentType="application/vnd.openxmlformats-package.relationships+xml" Extension="rels"/>
    
    <Default ContentType="application/xml" Extension="xml"/>
    
    <Override ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" PartName="/word/document.xml"/>
    
    <Override ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml" PartName="/word/styles.xml"/>
    
    <Override ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml" PartName="/word/settings.xml"/>
    
    <Override ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml" PartName="/word/webSettings.xml"/>
    
    <Override ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml" PartName="/word/footnotes.xml"/>
    
    <Override ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml" PartName="/word/endnotes.xml"/>
    
    <Override ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml" PartName="/word/fontTable.xml"/>
    
    <Override ContentType="application/vnd.openxmlformats-officedocument.theme+xml" PartName="/word/theme/theme1.xml"/>
    
    <Override ContentType="application/vnd.openxmlformats-package.core-properties+xml" PartName="/docProps/core.xml"/>
    
    <Override ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml" PartName="/docProps/app.xml"/>
    
    <Override ContentType="application/vnd.openxmlformats-officedocument.custom-properties+xml" PartName="/docProps/custom.xml"/>
    
    </Types>

    File steam( Content_Types.XML)

    <?xml version="1.0" encoding="UTF-8"?>
    
    <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
    
    <Default ContentType="application/vnd.openxmlformats-package.relationships+xml" Extension="rels"/>
    
    <Default ContentType="application/xml" Extension="xml"/>
    
    <Default ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" Extension="docx"/>
    
    <Override ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml" PartName="/word/document.xml"/>
    
    <Override ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml" PartName="/word/styles.xml"/>
    
    <Override ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml" PartName="/word/settings.xml"/>
    
    <Override ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml" PartName="/word/webSettings.xml"/>
    
    <Override ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml" PartName="/word/footnotes.xml"/>
    
    <Override ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml" PartName="/word/endnotes.xml"/>
    
    <Override ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml" PartName="/word/fontTable.xml"/>
    
    <Override ContentType="application/vnd.openxmlformats-officedocument.theme+xml" PartName="/word/theme/theme1.xml"/>
    
    <Override ContentType="application/vnd.openxmlformats-package.core-properties+xml" PartName="/docProps/core.xml"/>
    
    <Override ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml" PartName="/docProps/app.xml"/>
    
    <Override ContentType="application/vnd.openxmlformats-officedocument.custom-properties+xml" PartName="/docProps/custom.xml"/>
    
    </Types>


    Here is the test code:

          static void AddAlternativeFormatImportPart4MemoryStream()
            {
               
                byte[] byteArray = File.ReadAllBytes(@"C:\memoryStream.docx");
                using (MemoryStream wordDocMemoryStream = new MemoryStream())
                {
                    wordDocMemoryStream.Write(byteArray, 0, (int)byteArray.Length);
                    using (WordprocessingDocument myDoc = WordprocessingDocument.Open(wordDocMemoryStream, true))
                    {
                        myDoc.MainDocumentPart.AddAlternativeFormatImportPart(AlternativeFormatImportPartType.WordprocessingML, "id1");
                        // Save to new file              
                        using (Stream fileStream = File.Open(@"C:\memoryStream_save.docx", FileMode.Create))
                        {
                            myDoc.MainDocumentPart.Document.Save();
                            var resultFileByteArray = wordDocMemoryStream.ToArray();
                            fileStream.Write(resultFileByteArray, 0, resultFileByteArray.Length);
                        }
                    }
                   
                    
                }
            }
    
            static void AddAlternativeFormatImportPart4FileStream()
            {
                //return;
                using (WordprocessingDocument myDoc =
                 WordprocessingDocument.Open(@"C:\fileStream.docx",true))
                {
                    myDoc.MainDocumentPart.AddAlternativeFormatImportPart(AlternativeFormatImportPartType.WordprocessingML, "id0");
                    myDoc.MainDocumentPart.Document.Save();
                }
            }

    Second issue is that, the position of the stream is at the end when you feed data for the chunk. We should reset it before feed data using that stream or it will generate a empty data file. Here is the code for your reference:

    memStream.Seek(0, SeekOrigin.Begin);

    chunk.FeedData(memStream);

    So the workaround for this issue is that saving the memory stream to file and open that file to modify it using OpenXML. And for this first issue, I also suggest that rise it on the Open-XML-SDK on GitHub.

    Regards & Fei 


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.


    vendredi 27 avril 2018 12:00
    Modérateur
  • Thanks for the work.
    mercredi 23 mai 2018 13:57