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?
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.