سؤال

I'm trying to adapt this example to populate a Word template with data from a database on a server without Office installed (no Interop). I would like to be able to pull the template itself from the database and store the resultant populated document back into the database.

I can upload and download the template file itself and the downloaded template is still readable by Word. But when I try and populate the template with data and download the resultant document, I receive the following error when I try to open the document:

We're sorry. We can't open test.docx because we found a problem with its contents.
Details: No error detail available

The custom XML document, item1.xml seems to be properly formatted and contains the correct data. However there is also an item2.xml that contains the following:

<?xml version="1.0"?>
<b:Sources xmlns="http://schemas.openxmlformats.org/officeDocument/2006/bibliography"
   xmlns:b="http://schemas.openxmlformats.org/officeDocument/2006/bibliography" Version="6" StyleName="APA"
   SelectedStyle="\apasixtheditionofficeonline.xsl"/>

The only suspicious difference in XML I can see is in docProps\app.xml. In the template there is this:

<Template>EquipmentOffering.dotx</Template>
<TotalTime>950</TotalTime>
<Pages>5</Pages>
<Words>2470</Words>
<Characters>14084</Characters>
<Application>Microsoft Office Word</Application>
<DocSecurity>0</DocSecurity>
<Lines>117</Lines>
<Paragraphs>33</Paragraphs>

While in the generated document I have this:

<Template>EquipmentOffering.dotx</Template>
<TotalTime>1297</TotalTime>
<Pages>1</Pages>
<Words>2486</Words>
<Characters>14176</Characters>
<Application>Microsoft Office Word</Application>
<DocSecurity>0</DocSecurity>
<Lines>118</Lines>
<Paragraphs>33</Paragraphs>

(There's obviously more in that file but that's the suspicious part)

Specifically, the <Pages> element worries me.

Here is my implementation of the example code:

Private Const strRelRoot As String = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"

Protected Overrides Sub Execute()
    'Get the parameters and the template to be filled
    Dim params As CreateOfferingParams = Parameters
    Dim serverWorkSpace = Application.Current.CreateDataWorkspace()
    Dim offeringTemps = From sws In serverWorkSpace.myDatabase_dbData.DocTemplates
                        Where sws.TemplateName.Contains("EquipmentOffering")
                        Select sws

    'Open the template and copy it into a local byte array
    Dim myTemplate As DocTemplate = offeringTemps.FirstOrDefault()
    Dim fileBuffer As Byte() = myTemplate.TemplateFile

    'Open the file stream in memory, create a file package, and extract the relationship collection
    Using pkgStream As MemoryStream = New MemoryStream(fileBuffer, True)
        Using pkgFile As Package = Package.Open(pkgStream, FileMode.Open, FileAccess.ReadWrite)
            Dim pkgrcOfficeDocument As PackageRelationshipCollection = pkgFile.GetRelationshipsByType(strRelRoot)

            'Scan each relationship looking for custom XML
            For Each pkgr As PackageRelationship In pkgrcOfficeDocument
                If (pkgr.SourceUri.OriginalString = "/") Then

                    'Add a custom XML part to the package
                    Dim uriData As Uri = New Uri("/customXML/item1.xml", UriKind.Relative)
                    If pkgFile.PartExists(uriData) Then
                        'Delete template "/customXML/item1.xml" part
                        pkgFile.DeletePart(uriData)
                    End If

                    'Load the custom XML data
                    Dim pkgprtData As PackagePart = pkgFile.CreatePart(uriData, "application/xml")
                    Using docStream As Stream = pkgprtData.GetStream()
                        Using writer As XmlWriter = XmlWriter.Create(docStream)

                            'Write the root element and our namespace
                            writer.WriteStartDocument()
                            writer.WriteStartElement("Offering", "http://www.acrison.com")

                            'Write in each data element
                            For Each element In params.OfferingData
                                writer.WriteElementString(element.Key, element.Value)
                            Next

                            writer.WriteEndElement()
                            writer.WriteEndDocument()
                        End Using
                    End Using
                End If
            Next

            'Rewind the stream
            pkgStream.Position = 0

            'Copy the modified package into a new byte array
            Dim generatedOffering As Byte() = New Byte((pkgStream.Length) - 1) {}
            Dim i As Integer = 0
            Do While (i < pkgStream.Length)
                generatedOffering(i) = CType(pkgStream.ReadByte, Byte)
                i = (i + 1)
            Loop

            'Get the appropriate Quote entity so we have somewhere to store the newly generated file
            Dim quoteInfo = From sws In serverWorkSpace.myDatabase_dbData.Quotes
                            Where sws.QuoteID = params.QuoteIDParam
                            Select sws

            Dim myQuote As Quote = quoteInfo.FirstOrDefault()

            'Store the file in the entity
            myQuote.QuoteOffering = generatedOffering

            'Save out the changes
            serverWorkSpace.myDatabase_dbData.SaveChanges()

        End Using
    End Using
End Sub

Anyone see anything I'm doing wrong that would result in a mangled .docx file?


As requested in the comments, I've created a simple three control template and tried to generate a file which appears broken. Dropbox links to the template file and generated Word .docx.

A strange thing I noticed though as I did this. Trying to open the .docx in Word 2013 results in the above error. But Dropbox can successfully preview it, though the content controls appear as they would as if no data had been bound to them. I'm not sure what this means.


After reading through the links provided by @Flowerking and trying many of the example solutions out, I came across this SO Answer that uses Open XML 2.0 and which seems to have worked for the OP. Combining what I had, some of what I read at the linked blog, and that answer, I came up with the following:

Protected Overrides Sub Execute()
    'Get the parameters and the template to be filled
    Dim params As CreateOfferingParams = Parameters
    Dim serverWorkSpace = Application.Current.CreateDataWorkspace()
    Dim offeringTemps = From sws In serverWorkSpace.aris_dbData.DocTemplates
                        Where sws.TemplateName.Contains("SimpleTest")
                        Select sws

    'Open the template and copy it into a local byte array
    Dim myTemplate As DocTemplate = offeringTemps.FirstOrDefault()
    Dim fileBuffer As Byte() = myTemplate.TemplateFile

    'Open the file stream in memory and create a document object from it
    'Must be done this way so stream is expandable
    Dim pkgStream As MemoryStream = New MemoryStream
    pkgStream.Write(fileBuffer, 0, fileBuffer.Length)

    Using docTemplate As WordprocessingDocument = WordprocessingDocument.Open(pkgStream, True)
        'Create XML string matching custom XML part
        'Extract the main part of the document and replace any custom XML
        Dim mainPart As MainDocumentPart = docTemplate.MainDocumentPart

        For Each part As CustomXmlPart In mainPart.CustomXmlParts
            Dim docStream As New MemoryStream
            Dim docWriter As XmlWriter = XmlTextWriter.Create(docStream)

            'Write the declaration, root element and our namespace
            docWriter.WriteStartDocument()
            docWriter.WriteStartElement("Offering", "http://www.mycompany.com")

            'Write in each data element
            For Each element In params.OfferingData
                docWriter.WriteElementString(element.Key, element.Value)
            Next

            'Close each of the tags and flush the stream
            docWriter.WriteEndElement()
            docWriter.WriteEndDocument()
            docWriter.Flush()

            'Rewind the stream and feed it to the custom XML part we are working on
            docStream.Seek(0, SeekOrigin.Begin)
            part.FeedData(docStream)

            'Rewind the package stream
            pkgStream.Position = 0

            'Copy the modified package into a new byte array
            Dim generatedOffering As Byte() = New Byte((pkgStream.Length) - 1) {}
            Dim i As Integer = 0
            Do While (i < pkgStream.Length)
                generatedOffering(i) = CType(pkgStream.ReadByte, Byte)
                i = (i + 1)
            Loop

            'Get the appropriate Quote entity so we have somewhere to store the newly generated file
            Dim quoteInfo = From sws In serverWorkSpace.aris_dbData.Quotes
                            Where sws.QuoteID = params.QuoteIDParam
                            Select sws

            Dim myQuote As Quote = quoteInfo.FirstOrDefault()

            'Store the file in the entity
            myQuote.QuoteOffering = generatedOffering

            'Save out the changes
            serverWorkSpace.aris_dbData.SaveChanges()
        Next
    End Using
End Sub

I believe I'm only using proper Open XML 2.5 methodology now but the result is the same and I am unable to open the generated .docx. I have even used the Open XML SDK 2.5 Productivity Tool and the generated .docx validates under Word 2007, 2010, and 2013.

The documents in the Dropbox have been updated to the ones I'm currently working with. Can anyone see anything wrong with them?

هل كانت مفيدة؟

المحلول 2

The main problem turned out to be that my template was a .dotx and I was saving the generated file as a .docx. Changing the extension of the generated file to .dotx I was able to open it and see the content.

I'm not sure if there is a method in the SDK to do a conversion. But simply saving the initial template as a .docx solved the problem.

نصائح أخرى

I think your code is valid for old Open XML supporting Custom XMl parts in Word 2007. But Microsoft lost a patent case and has to drop custom xml from word open xml.

Dropbox is showing the doc properly as it is treating it as word open xml 2007 format. You can simply rename the extension of you document from .docx to .doc to and word can open it without errors but with missing data in content controls.

The best solution is to follow the latest open xml specification and not to use the custom xml parts. Check out this blog with some examples on using content controls.

Hope this helps.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top