Hello all,
As I mentioned in another post, we are investigating whether the Aspose solution can help us with some specific document generation needs. This question is a bit difficult to describe, so I’ll summarize here and upload a Word document with the details so you can see exactly what we are trying to do. Basically, we have a template document that has a table that is bookmarked. The row in the table has several columns, each with text inside that is bookmarked. What we want to do is to create a row in the table for each record we have in a dataset (so for the example, let’s just say we have an array to loop through); each bookmark in the table row should be replaced with some text from the dataset.
I’d appreciate any code or pseudocode in Java/C# or a pointer to some relevant documentation that could help me assess the possibility of doing this.
Document attached with details.
Thanks for your assistance,
John
Hi John,
Thanks for your interest in Aspose.Words. It can supply functionality which you require.
I believe what you’re looking for is mail merge, and more specifically nested mail merge. Please take a look at the following documentation here. Nested mail merge regions will allow to you import data, even from nested data sources into your document, and merging with regions will allow the table to increase with each data entry.
You can also download the corresponding code in the example and have a play with it from here.
Regarding the need to skip any empty data fields, please refer to this property found here
If you have any further queries please don’t hesitate to ask.
Thanks,
Hi Adam,
Thanks for that reply - we had considered mail merge as a possibility, but we’d like to explore doing this programatically. The reason being is that we already have document templates in place (quite a large number of them); they are not set up properly for mail merge, and thus we’d like to try to recreate the current process we use (OLE automation of MS Word) with Aspose. Is there a way we could do what is described in the original question without using mail merge?
Thanks,
John
Hi John,
I understand, it is possible to achieve all of your specifications using bookmarks programmatically as well.
Please refer to the documentation on working with bookmarks here. It also looks like you may need to be building tables dynamically so please have a read here about the DocumentBuilder methods of inserting tables. DocumentBuilder also supplies method to insert text and other elements into an object.
Finally here is a simple example of how to move to a bookmark and build a table at that position.
Document doc = new Document(@"Document.doc");
DocumentBuilder builder = new DocumentBuilder(doc);
// Set table formating
// Set borders
builder.CellFormat.Borders.LineStyle = LineStyle.Single;
builder.CellFormat.Borders.Color = Color.Red;
// Set left indent
builder.RowFormat.LeftIndent = 100;
// etc...
// Move documentBuilder cursor to the bookmark
builder.MoveToBookmark("myBookmark");
// Insert some table
for (int i = 0; i <5; i++)
{
for (int j = 0; j <5; j++)
{
builder.InsertCell();
builder.Write("this is cell");
}
builder.EndRow();
}
builder.EndTable();
// Save output document
doc.Save(@"Document Out.doc");
If you have any further questions feel free to ask.
Thanks,
Thanks again, Andrew. Still a few more questions regarding this one.
I understand how to insert tables using the DocBuilder - this is quite straightforward. Let me start with a simple issue and then perhaps I can use that to expand and build the full solution (I’d be happy to post my final code here, by the way).
1). Let’s assume that I have a template document. In that template is a single table with a single row. The entire row is bookmarked.
2). I need to take that single table row and duplicate it in the document a number of times (let’s say 5 times to be consistent with your previous example).
For now, let’s not worry about the fact that my actual requirement is more complex than this.
So my questions:
a). How can I get a reference to the entire table row that is within the bookmark? I’ve tried something like this:
dbT.moveToBookmark("toplevelrow");
_LOG.info(new Integer(dbT.getCurrentNode().getNodeType()).toString());
(dbT is a document builder) - I end up getting NodeType.RUN as the node type.
What I’d like to do is to get a reference to the table row, and then assuming I have that (perhaps in a Node object), I’d be able to insert it 5x in the document.
Thanks,
John
Hi John,
Thank you for additional information. You can get table row, where bookmark is located using the following code:
// Open document.
Document doc = new Document("in.doc");
DocumentBuilder builder = new DocumentBuilder(doc);
// Move DocumentBuilder cursor to the bookmark.
builder.MoveToBookmark("mybk");
// Get Row, where bookmark is located.
Node row = builder.CurrentParagraph.GetAncestor(NodeType.Row);
// Work with row.
if (row != null)
{
// ...
}
Hope this helps.
Best regards,
Please see the attached document.
Getting closer, Andrey, thanks for bearing with me.
dbT.moveToBookmark("toplevelrow");
Node row = dbT.getCurrentParagraph().getAncestor(NodeType.ROW);
// Where do I move here?, tried table/paragraph, etc to no avail
dbT.insertNode(row);
dbT.insertNode(row);
dbT.insertNode(row);
Code as written throws:
java.lang.IllegalArgumentException: The newChild is an ancestor of this node.
at com.aspose.words.CompositeNode.a(CompositeNode.java:816)
at com.aspose.words.CompositeNode.insertBefore(CompositeNode.java: 363)
at com.aspose.words.DocumentBuilder.insertNode(DocumentBuilder.java: 1697)
at com.myco.docgen.TestDocGen.testTable(TestDocGen.java: 93)
at com.myco.docgen.TestDocGen.main(TestDocGen.java: 25
Obviously, I’m missing something here.
Thanks,
John
Hi
Before inserting the row you should create a clone of this row. Please try to use the code like the following
// Open document.
Document doc = new Document("in.doc");
DocumentBuilder builder = new DocumentBuilder(doc);
// Move DocumentBuilder cursor to the bookmark.
builder.MoveToBookmark("mybk");
// Get Row, where bookmark is located.
Node row = builder.CurrentParagraph.GetAncestor(NodeType.Row);
row.ParentNode.InsertAfter(row.Clone(true), row);
row.ParentNode.InsertAfter(row.Clone(true), row);
row.ParentNode.InsertAfter(row.Clone(true), row);
doc.Save("out.doc");
Best regards,
Andrey,
Brilliant - exactly my use case. Now, let’s add the next layer of complexity. The bookmark actually comprises 2 rows (and furthermore, one of the two rows is also bookmarked), something like this structure:
(start bookmark “parent”)
row / cell / cell
(start bookmark “child”)
row / cell / cell
(end bookmark “child”)
(end bookmark “parent”)
So, parent is a bookmark containing 2 rows;
child is a bookmark containing 1 row
Now, I need to insert both rows that comprise “parent” multiple times. I think I need a CompositeNode to do this? How can I create a CompositeNode containing all of the contents of the “parent” bookmark? From the code you showed, I can then insert multiple copies of the child row.
I am further guessing that this same technique could be used even if table rows aren’t involved, but just general structures? This is probably our most complex use case, and if we can crack this one, the others will be very easy.
Thanks once again,
John
John,
To retrieve content between nodes you can try using ExtractContentBetweenNodes method from this post:
https://forum.aspose.com/t/99344
Bookmark bookmark = srcDoc.Range.Bookmarks["mybk"];
// Get node that contains BookmarkStart and BookmarkEnd
Node startParentNode = bookmark.BookmarkStart.ParentNode;
Node endParentNode = bookmark.BookmarkEnd.ParentNode;
ExtractContentBetweenNodes(startParentNode, endParentNode);
Best regards,
Hi Andrey,
Thanks for that - however, when I try that, I’m getting the “Start and End nodes should be children of main story” exception in the ExtractContentBetweenNodes function. If I comment that check out of the code, it runs, but doesn’t do what I need it to. I’ve attached my template doc file so you can see what I am describing - I’m right now trying to insert multiple copies of the two rows contained in the “toplevelrow” bookmark.
Best,
John
Hi John,
Thanks for your request. It seems there is now way to achieve what you need with your current bookmarks structure. I little bit changed your template. In my case one of bookmarks marks the beginning of a level and the second one the end of a level.
Please see the following code and attached documents:
// Open document.
Document doc = new Document(@"tablesource.docx");
// Create new ArreyList for Rows to copy
ArrayList rowsToCopy = new ArrayList();
// Get firs row with bookmark "first"
Row firstRow = (Row) doc.Range.Bookmarks["first"].BookmarkStart.GetAncestor(NodeType.Row);
Node currentNode = firstRow;
while (currentNode != null && currentNode.Range.Bookmarks["last"] == null)
{
rowsToCopy.Add(currentNode);
currentNode = currentNode.NextSibling;
}
if (currentNode != null)
rowsToCopy.Add(currentNode);
Row refRow = (Row) rowsToCopy[rowsToCopy.Count - 1];
for (int i = 0; i <10; i++)
// Insert all rows from rowsToCopy array after the last Row
foreach(Row r in rowsToCopy)
{
Row newRow = (Row) r.Clone(true);
refRow.ParentNode.InsertAfter(newRow, refRow);
refRow = newRow;
}
doc.Save(@"out.doc");
Best regards,