Using a template as the basis for a new document

How do I do the following.

  1. Use the sections of a document template to create a completely new document. I want to carry over the formatting and static text while using the replace feature to add the unique content.

  2. Put in page breaks. DocumentBuilder has the page break feature and I can make that work, but I haven’t been able to achieve item 1 above.

We are using your product as a document generator and have developed an api for our customers to design their own templates, in this case invoices. Multiple invoices can be generated at once and need to be created in the same document for printing.

Hi,

Thank you for your question.

  1. Honestly, I can’t understand why you would need to create a new document here. Once you opened a document, you can modify its object model as desired and then save the result to disk. If you don’t want to alter the original model, you can clone a document using Document.Clone and then work with the cloned instance. Please provide the details of your task if you need to learn some concrete howtos.
  2. Same here. Now that you are aware of the DocumentBuilder.InsertBreak method, I don’t fully understand why it does not work for you. Please elaborate this point as well.

The data for the documents I need to generate is in the attached invoicedata.xml. I will put the desired output into a second reply.

I am generating invoices.

There is a header section, then a repeating section called InvoiceSupply, this is at least one, but could be many. With in that section there can be, but are not required a repeating section for SupplyLiquids, SupplyAdjustments, and ResiduePrices. Then after all of those nodes are processed, move on to the next InvoiceSupply record. After the last InvoiceSupply in that InvoiceHeader, insert a page break and procees the next InvoiceHeader.

I use the Range.Replace method to replace specific tags in the document template. For example, to insert the first line in the invoice:

Invoice Number: 0610_1152

The template looks like this:

Invoice Number: <>

We are already doing this on documents that do not have nested repeating sections. After all the replacements are done, the results are returned to the user in the form of a download.

My specific problem is, how do I process nested repeating sections as described above. I can change the xml data if needed, but I will still have the nested sections.

Again, I will put the desired output into a second reply.

Here is the desired output.

The primary repeating section are the Supply Point/Delivery Point rows. I can already make this happen using the clone method with range.replace. What I figure out here is how to insert the page break.

The nested repeating sections are for liquids, adjustments, and price. These may not exist at all, or there can be 1 to many.

Thank you for your help.

Thank you for that comprehensive description. However, I’m still trying to catch why you cannot figure out how to insert a page break. Is it possible also to post or attach the source code used for creating the document and a source document? The point is that if I were able to reproduce the document generation process, it would be much easier to provide help.

I can do the page break using a DocumentBuilder.

Dim doc As Document = New Document()

Dim NewDoc As New Aspose.Words.DocumentBuilder(doc)

NewDoc.InsertBreak(BreakType.PageBreak)

I can’t figure out how to do it using the Document object. This is not actually the biggest problem though, this biggest problem is the nested repeating sections.

There are two Functions in the code. The first:

Public Function NewDoc() As Byte()

generates documents and is in production, it produces one document at a time in seperate documents. If I need 20 documents, the function gets called 20 times. These are contract documents and we store them in our database, they are tied to different contracts.

Whe doing invoices, these are not stored, if 20 invoices are to be created, I need one word document with all twenty invoices in it. I am able to create the InvoiceHeader and put a page break at the bottom of each. A single document is returned. I can get the InvoiceSupply repeating sections done. I am able to use the NewDoc() function to do this.

The function:

Public Function NewDocInvoices() As Byte()

Needs to do all of the same things NewDoc() does, but it also needs to process the nested repeating sections under the InvoiceSupply.

Below is the code from a class module that I am using to process the documents. I have commented out the section in NewDocIncoices that would deal with the nested repeating sections. Again, this works well for one repeating section, it does not for nested sections.

The problem with the code in NewDocInvoices() is that the section formatting is not copied over with text. This is currently what I am stuck on.

The byte stream for the document template comes from the database in the function GetTemplateByteStream()

SubstitutionXML is set using the contents of invoice.xml that I sent earlier.

##############################################################################

Imports System.IO

Imports System.Xml

Imports System.Data.OleDb

Imports Aspose.Words

Public Class DocumentGenerator

    Public Global\_CompanyID As Integer

Public Global\_ConnectString As String

Public FileID As String

    Public FileName As String

    Public FileType As Integer

    Public SubstitutionXML As String

    Public OutputFormat As Integer = 1

    Private Function GetTemplateByteStream() As MemoryStream

        Dim SQL As String

        Dim dt As DataTable

        Dim dr As DataRow

        Dim docstream As New MemoryStream

        Dim conn As New OleDb.OleDbConnection(Global \ _ConnectString)

        Try

            SQL = "SELECT FileData, FileName, FileType, FileID FROM gmm\_file WHERE SellerID = " & Global \ _CompanyID.ToString & " "

            SQL = SQL & " AND FileID = '" & FileID.ToString & "'"

            SQL = SQL & " UNION ALL "

            SQL = SQL & "SELECT FileDataImage as FileData, FileName, FileType, FileID FROM omm\_file WHERE SellerID = " & Global \ _CompanyID.ToString & " "

            SQL = SQL & " AND FileID = '" & FileID.ToString & "'"

            conn.Open()

            Dim adapter As New OleDb.OleDbDataAdapter(SQL, conn)

            Dim cmdBuilder As New OleDbCommandBuilder(adapter)

            dt = New DataTable("documents")

            adapter.Fill(dt)

            dr = dt.Rows(0)

            Dim srcDocBytes As Byte() = dr("FileData")

            FileName = dr("FileName").ToString

            FileType = dr("FileType")

            docstream.Write(srcDocBytes.Clone, 0, srcDocBytes.Length)

            docstream.Position = 0

            GetTemplateByteStream = docstream

        Catch ex As Exception

            Throw ex

        Finally

            conn.Close()

        End Try

    End Function

    Public Function NewDocInvoices() As Byte()

        Try

            Dim docstream As New MemoryStream

            Dim oStream As New MemoryStream

            Dim objXMLDoc As System.Xml.XmlDocument

            Dim objNodes As System.Xml.XmlNodeList

            Dim objNode As System.Xml.XmlNode

            Dim objChildNodes As System.Xml.XmlNodeList

            Dim objChildNode As System.Xml.XmlNode

            Dim numChildNodes As Integer

            Dim oAttributes As System.Xml.XmlAttributeCollection

            Dim oAttribute As System.Xml.XmlAttribute

            Dim numNodes As Integer

            Dim nX As Integer

            Dim numAttributes As Integer

            Dim aX As Integer

            Dim cX As Integer

            Dim sNodeName As String

            Dim sNodeValue As String

            Dim iPos1 As Integer

            Dim iPos2 As Integer

            Dim strBlank As String

            'get the template

            docstream = GetTemplateByteStream()

            docstream.Position = 0

            Dim sourcedoc As New Aspose.Words.Document(docstream)

            Dim doc As Document = New Document()

            Dim NewDoc As New Aspose.Words.DocumentBuilder(doc)

            'substitutions

            objXMLDoc = New System.Xml.XmlDocument

            Try

                objXMLDoc.LoadXml(SubstitutionXML)

            Catch ex As Exception

                Throw ex

            End Try

            objNodes = objXMLDoc.FirstChild.ChildNodes

            numNodes = objNodes.Count

            For nX = 0 To numNodes - 1

                Dim InvoiceHeaderName As String

                Dim HeaderSection As Section

                HeaderSection = sourcedoc.Sections(0).Clone

                objNode = objNodes(nX)

                oAttributes = objNode.Attributes

                numAttributes = oAttributes.Count

                InvoiceHeaderName = objNode.Name

                For aX = 0 To numAttributes - 1

                    oAttribute = oAttributes(aX)

                    sNodeName = oAttribute.Name

                    sNodeValue = oAttribute.Value

                    HeaderSection.Range.Replace("<<" & sNodeName.ToString.Trim & ">>", sNodeValue.ToString.Trim, False, False)

                Next

                NewDoc.Write(HeaderSection.Range.Text)

                'objChildNodes = objNode.ChildNodes

                'numChildNodes = objChildNodes.Count

                'If numChildNodes > 0 Then

                ' Dim section As Section

                ' Dim clonesection As Section

                ' Dim sSectionName As String

                ' Dim numSections As Integer

                ' Dim sX As Integer

                ' Dim oSection As Section

                ' Dim sTemp As String

                ' For cX = 0 To numChildNodes - 1

                ' objChildNode = objChildNodes(cX)

                ' sSectionName = objChildNode.Name

                ' numSections = sourcedoc.Sections.Count

                ' For sX = 0 To numSections - 1

                ' oSection = sourcedoc.Sections(sX)

                ' sTemp = oSection.Range.Text

                ' If sTemp.Contains("<<" & sSectionName.ToString & "\_") Then

                ' section = sourcedoc.Sections(sX)

                ' clonesection = section.Clone

                ' oAttributes = objChildNode.Attributes

                ' numAttributes = oAttributes.Count

                ' For aX = 0 To numAttributes - 1

                ' oAttribute = oAttributes(aX)

                ' sNodeName = oAttribute.Name

                ' sNodeValue = oAttribute.Value

                ' clonesection.Range.Replace("<>", sNodeValue.ToString.Trim, False, False)

                ' Next

                ' sTemp = clonesection.Range.Text

                ' Do While sTemp.Contains("<>") And sTemp.Contains("\_")

                ' iPos1 = sTemp.IndexOf("<<")

                ' iPos2 = sTemp.IndexOf(">>")

                ' strBlank = sTemp.Substring(iPos1, (iPos2 + 2) - iPos1)

                ' clonesection.Range.Replace(strBlank.ToString.Trim, "", False, False)

                ' sTemp = sTemp.Substring(0, iPos1) & Space((iPos2 + 2) - iPos1) & sTemp.Substring(iPos2 + 2)

                ' Loop

                ' sourcedoc.Sections.Insert(sX, clonesection)

                ' clonesection = Nothing

                ' section = Nothing

                ' Exit For

                ' End If

                ' Next

                ' Next

                ' numSections = sourcedoc.Sections.Count

                ' For sX = numSections - 1 To 0 Step -1

                ' oSection = sourcedoc.Sections(sX)

                ' sTemp = oSection.Range.Text

                ' If sTemp.Contains("<>") And sTemp.Contains("\_") Then

                ' Do While sTemp.Contains("<>") And sTemp.Contains("\_")

                ' iPos1 = sTemp.IndexOf("<<")

                ' iPos2 = sTemp.IndexOf(">>")

                ' strBlank = sTemp.Substring(iPos1, (iPos2 + 2) - iPos1)

                ' sourcedoc.Sections(sX).Range.Replace(strBlank.ToString.Trim, "", False, False)

                ' sTemp = sTemp.Substring(0, iPos1) & Space((iPos2 + 2) - iPos1) & sTemp.Substring(iPos2 + 2)

                ' Loop

                ' 'section = sourcedoc.Sections(sX)

                ' 'sourcedoc.Sections.Remove(section)

                ' End If

                ' Next

                'End If

                If numNodes > 1 And nX < numNodes - 1 Then

                    'add a page break

                    NewDoc.InsertBreak(BreakType.PageBreak)

                End If

            Next

            Select Case OutputFormat

                Case 1

                    Call doc.Save(oStream, SaveFormat.Doc)

                Case 2

                    Call doc.Save(oStream, SaveFormat.AsposePdf)

            End Select

            NewDocInvoices = oStream.ToArray

        Catch ex As Exception

            Throw ex

        End Try

    End Function

    Public Function NewDoc() As Byte()

        Try

            Dim docstream As New MemoryStream

            Dim oStream As New MemoryStream

            Dim objXMLDoc As System.Xml.XmlDocument

            Dim objNodes As System.Xml.XmlNodeList

            Dim objNode As System.Xml.XmlNode

            Dim objChildNodes As System.Xml.XmlNodeList

            Dim objChildNode As System.Xml.XmlNode

            Dim numChildNodes As Integer

            Dim oAttributes As System.Xml.XmlAttributeCollection

            Dim oAttribute As System.Xml.XmlAttribute

            Dim numNodes As Integer

            Dim nX As Integer

            Dim numAttributes As Integer

            Dim aX As Integer

            Dim cX As Integer

            Dim sNodeName As String

            Dim sNodeValue As String

            Dim iPos1 As Integer

            Dim iPos2 As Integer

            Dim strBlank As String

            'get the template

            docstream = GetTemplateByteStream()

            docstream.Position = 0

            Dim doc As New Aspose.Words.Document(docstream)

            'substitutions

            objXMLDoc = New System.Xml.XmlDocument

            Try

                objXMLDoc.LoadXml(SubstitutionXML)

            Catch ex As Exception

                Throw ex

            End Try

            objNodes = objXMLDoc.FirstChild.ChildNodes

            numNodes = objNodes.Count

            For nX = 0 To numNodes - 1

                objNode = objNodes(nX)

                oAttributes = objNode.Attributes

                numAttributes = oAttributes.Count

                For aX = 0 To numAttributes - 1

                    oAttribute = oAttributes(aX)

                    sNodeName = oAttribute.Name

                    sNodeValue = oAttribute.Value

                    doc.Range.Replace("<<" & sNodeName.ToString.Trim & ">>", sNodeValue.ToString.Trim, False, False)

                Next

                objChildNodes = objNode.ChildNodes

                numChildNodes = objChildNodes.Count

                If numChildNodes > 0 Then

                    Dim section As Section

                    Dim clonesection As Section

                    Dim sSectionName As String

                    Dim numSections As Integer

                    Dim sX As Integer

                    Dim oSection As Section

                    Dim sTemp As String

                    For cX = 0 To numChildNodes - 1

                        objChildNode = objChildNodes(cX)

                        sSectionName = objChildNode.Name

                        numSections = doc.Sections.Count

                        For sX = 0 To numSections - 1

                            oSection = doc.Sections(sX)

                            sTemp = oSection.Range.Text

                            If sTemp.Contains("<<" & sSectionName.ToString & "\_") Then

                                section = doc.Sections(sX)

                                clonesection = section.Clone

                                oAttributes = objChildNode.Attributes

                                numAttributes = oAttributes.Count

                                For aX = 0 To numAttributes - 1

                                    oAttribute = oAttributes(aX)

                                    sNodeName = oAttribute.Name

                                    sNodeValue = oAttribute.Value

                                    clonesection.Range.Replace("<<" & sNodeName.ToString.Trim & ">>", sNodeValue.ToString.Trim, False, False)

                                Next

                                sTemp = clonesection.Range.Text

                                Do While sTemp.Contains("<<") And sTemp.Contains(">>") And sTemp.Contains("\_")

                                    iPos1 = sTemp.IndexOf("<<")

                                    iPos2 = sTemp.IndexOf(">>")

                                    strBlank = sTemp.Substring(iPos1, (iPos2 + 2) - iPos1)

                                    clonesection.Range.Replace(strBlank.ToString.Trim, "", False, False)

                                    sTemp = sTemp.Substring(0, iPos1) & Space((iPos2 + 2) - iPos1) & sTemp.Substring(iPos2 + 2)

                                Loop

                                doc.Sections.Insert(sX, clonesection)

                                clonesection = Nothing

                                section = Nothing

                                Exit For

                            End If

                        Next

                    Next

                    numSections = doc.Sections.Count

                    For sX = numSections - 1 To 0 Step -1

                        oSection = doc.Sections(sX)

                        sTemp = oSection.Range.Text

                        If sTemp.Contains("<<") And sTemp.Contains(">>") And sTemp.Contains("\_") Then

                            Do While sTemp.Contains("<<") And sTemp.Contains(">>") And sTemp.Contains("\_")

                                iPos1 = sTemp.IndexOf("<<")

                                iPos2 = sTemp.IndexOf(">>")

                                strBlank = sTemp.Substring(iPos1, (iPos2 + 2) - iPos1)

                                doc.Sections(sX).Range.Replace(strBlank.ToString.Trim, "", False, False)

                                sTemp = sTemp.Substring(0, iPos1) & Space((iPos2 + 2) - iPos1) & sTemp.Substring(iPos2 + 2)

                            Loop

                            'section = doc.Sections(sX)

                            'doc.Sections.Remove(section)

                        End If

                    Next

                End If

            Next

            Select Case OutputFormat

                Case 1

                    Call doc.Save(oStream, SaveFormat.Doc)

                Case 2

                    Call doc.Save(oStream, SaveFormat.AsposePdf)

            End Select

            NewDoc = oStream.ToArray

        Catch ex As Exception

            Throw ex

        End Try

    End Function

    Public Sub New()

        Dim license As Aspose.Words.License = New Aspose.Words.License

        license.SetLicense("Aspose.EMK3.lic")

    End Sub

End Class

Source document. This includes the InvoiceHeader and InvoiceSupply sections. It does not include the nested sections because I haven’t figured it out yet.

Thank you. Give me around a day to find a proper solution for you.

I seem succeeded to reproduce your solution. I had to alter and strip out most of the source XML data and now I see the problem. If you consider I still didn’t catch the idea, let me know.

So you replace the text in sections of the template document and need to build a completely different document containing the modified (actually merged with data) sections retaining the formatting. I saw you use DocumentBuilder to insert the text of the heading sections and inserting a page break.

Of course, using DocumentBuilder.Write and similar methods won’t preserve the original formatting. I think that you should import the modified sections to the new document as described here:

https://docs.aspose.com/words/net/insert-and-append-documents/

Just implement a neat method like the following:

Sub AddSection(ByVal dstDoc As Document, ByVal section As Section)

Dim importedSection As Section = dstDoc.ImportNode(section, True)

importedSection.PageSetup.SectionStart = SectionStart.Continuous

dstDoc.Sections.Add(importedSection)

End Sub

where dstDoc is the destination document and section is the section to copy. I saw you also insert sections at certain positions in section collection, then just modify the method above as appropriate.

Regarding insertion a page break without the use of DocumentBuilder. The exact code depends on the point of insertion, but the idea looks like the following:

Dim para As Paragraph = New Paragraph(dstDoc)

para.AppendChild(New Run(dstDoc, ControlChar.PageBreak))

dstDoc.FirstSection.Body.AppendChild(para)

In this example a page break is inserted at the end of the first section of the document.

If you need any further assistance and/or you still cannot achieve what you want, please let me know.

Some more examples that might be useful:
https://docs.aspose.com/words/net/insert-and-append-documents/

Thank you very much for your help. This has gotten us very close. The document and formatting are coming out just the way I need it to.

The only thing remaining is reference to the nested repeating sections. Is there a way to do this. I have come up with a possible solution that I think will work, but it reduces the flexibility of the user in setting up their templates.

Basically it amounts to navigating the childnodes in the xml, then looping the template to find the appropriate section that needs to be added to the new document.

Thanks again for all your help.

Well I think it is impossible to create nested sections in any other way because sections in a Microsoft Word document are sequential and there is no such term as “nested sections” at all. We have an demo project named Product Catalog that neatly shows how to implement nested regions, please consider it. Although it uses mail merge functionality rather than find and replace (BTW - maybe it is possible to load your XML into a dataset and use mail merge as well?), it might be helpful to understand the idea. It also involves the technique of cloning and importing nested sections one-by-one because it seems to be the only possible approach at the moment. We are planning to add support of nested mail merge regions later.

If you think I did not correctly catch the real problem you experience, please let me know.

The issues you have found earlier (filed as 39) have been fixed in this update.


This message was posted using Notification2Forum from Downloads module by aspose.notifier.
(28)

The issues you have found earlier (filed as ) have been fixed in this update. This message was posted using BugNotificationTool from Downloads module by MuzammilKhan