How to create master / subdocuments

Hi. Our application generates multiple merged letters which need to be filed in separate folders on the shared disk. The user needs to review all the files before printing, and perhaps make changes, so the application should open them all in a single Word window.
I understand MS Office has a master / subdocument mechanism so that a document embedded in the master can be changed, with changes saved back to the subdocument.
Does Aspose support creating master documents, or do you have another way to support this requirement?
Thanks.

Hi
Thanks for your request. Unfortunately subdocuments are not supported yet. This is issue #1128 in our defect database.
So the one way to achieve this is to edit every document in separate window. You can programmatically open MS Word and edit each document. For example you can try using the following code:

// get DOC files from directory
string[] files = Directory.GetFiles(@"Test195", "*.doc");
foreach (string fileName in files)
{
    // Open file in new word window
    System.Diagnostics.Process.Start(fileName);
    // Wait while document is edited
    bool isDocumentOpened = true;
    while (isDocumentOpened)
    {
        System.Threading.Thread.Sleep(1000);
        // get WINWORD process
        System.Diagnostics.Process[] procs = System.Diagnostics.Process.GetProcessesByName("WINWORD");
        isDocumentOpened = false;
        foreach (System.Diagnostics.Process proc in procs)
        {
            // Check name of opened file
            isDocumentOpened = proc.MainWindowTitle.Contains(System.IO.Path.GetFileName(fileName));
            if (isDocumentOpened)
                break;
        }
    }
}

Best regards.

Hi, thanks for your reply. Since our application is running on the web server, the code example would not be helpful.
The only solution I see is to produce a single document with all the subdocuments in it and open it for the user to modify, print, save and close. Our code would then have to split the file up into separate documents to be saved in the appropriate folders. (Perhaps there could be some invisible path spec separating each subdocument within the master.)
Do you have any better suggestions? Thanks.

Hi
Thanks fro your inquiry. You can try using bookmarks. For example see the following methods,

private void MergeDocuments()
{
    string[] documentNames = { @"Test197\in1.doc", @"Test197\in2.doc", @"Test197\in3.doc" };
    // Open master document
    Document master = new Document(@"Test197\master.doc");
    foreach (string docName in documentNames)
    {
        // Generate name of bookmark
        string bookmarkName = "Document_" + docName;
        // Open document
        Document doc = new Document(docName);
        DocumentBuilder builder = new DocumentBuilder(doc);
        // Insert bookmark at the begining of the document
        builder.StartBookmark(bookmarkName);
        builder.EndBookmark(bookmarkName);
        // Merge master document and sub document
        foreach (Section srcSection in doc.Sections)
        {
            Node dstSection = master.ImportNode(srcSection, true, ImportFormatMode.KeepSourceFormatting);
            master.Sections.Add(dstSection);
        }
    }
    // Save master document
    master.Save(@"Test197\out.doc");
}
private void SplitDocuments()
{
    // Open master document
    Document master = new Document(@"Test197\out.doc");
    Document doc = null;
    string path = string.Empty;
    bool createNew = false;
    string bookmarkName = string.Empty;
    // Loop through all section in the document
    foreach (Section srcSection in master.Sections)
    {
        // Check if section contains Bookmark with name Document_
        foreach (Bookmark book in srcSection.Range.Bookmarks)
        {
            if (book.Name.StartsWith("Document_"))
            {
                bookmarkName = book.Name;
                createNew = true;
                break;
            }
        }
        if (createNew)
        {
            // Save previouse document
            if (doc != null)
            {
                doc.Range.Bookmarks["Document_" + path].Remove();
                doc.Save(path);
            }
            // Create new document
            doc = new Document();
            doc.FirstSection.Remove();
            // Parse path
            path = bookmarkName.Replace("Document_", "");
            createNew = false;
        }
        if (doc != null)
        {
            // Append section
            Node dstSection = doc.ImportNode(srcSection, true, ImportFormatMode.KeepSourceFormatting);
            doc.Sections.Add(dstSection);
            if (srcSection.Equals(master.LastSection))
            {
                // Save document if this is last section
                doc.Save(path);
            }
        }
    }
}

Hope this helps.
Best regards.

Do I need to create temporary documents using MailMerge.MergeWithRegions(), then join them together? or does Aspose provide options to append them into a single Document object, into separate bookmarks? Thanks.

Hi
Thanks for your inquiry. Please see the first method in the code I provided. This method merge documents together and insert bookmarks to mark end of each document. The second method split created document into particular documents, and save them.
Best regards.

Thank you for the MergeDocuments() code example. Unfortunately, it inserts a Page Break at the top of the master document, and it inserts sub documents after each bookmark, which are all empty. Can you recommend ways to improve on this? Thanks.

Hi
Thanks for your inquiry.

  1. MegeDocuments method doesn’t insert page break at the beginning of master document. This method just appends sub documents to master document. But if your master document is empty, then you will see Section break at the beginning of the result document.
  2. Empty bookmarks between documents is needed by SplitDocuments method. These bookmarks show where the next document starts.

How you would like to improve these methods? If you would like to remove empty page at the beginning of the result document (in case if your master document is empty) then you can check the first section of document. If this section is empty then just remove it.
Best regards.

Reading the first post, my app needs to read all subdocuments content inside a master document. Can I do it with Aspose.Words already? (iow, has issue #1128 been solved?)
Thx a lot!

Hi

Thanks for your request. Unfortunately, Aspose.Words still does not support subdocuments. The issue #1128 is unresolved yet.
Best regards.

Hi
I haven’t managed to find any recent updates on issue #1128 concerning support for subdocuments. I therefore just wanted to know if Aspose has implemented functionality to expand subdocuments contained within a master document.
If not, is there a work-around without refactoring the original documents?
Best wishes,
Andrew

Hi

Thanks for your request. Unfortunately the issue is still unresolved. I cannot suggest you any way to work this problem around at the moment. You will be notified once this feature is supported. Unfortunately, it is difficult to provide you any reliable estimate regarding this now.
Best regards,

Hi Andrey
Thanks for your last reply concerning subdocuments. I was thinking if documents were added as hyperlinks rather than subdocuments would it be possible to insert them into the master document?
For instance if a hyperlink was tested for the string “.doc”, to determine if the link was a document, and then the hyperlink was deleted and the approriate document inserted in its place.
Are there any relevant code examples on how to do this, i.e.

  1. Test a hyperlink for the presence of “.doc”
  2. Delete the hyperlink
  3. Insert the document specified by the hyperlink at the hyperlink position I am able to locate hyperlinks in a document, but unsure how to delete them and insert a document at the same position.

Any help would be greatly appreciated.
Thanks,
Andrew

Hi

Thanks for your request. Could you please attach your input and expected documents here for testing? I will investigate the issue and provide you more information.
Best regards,

Hi Andrey
I have attached two documents - one of which I want to insert into the other at the specified hyperlink.
Andrew

Hi Andrew,

Thank you for additional information. I created a simple code, which demonstrate the technique you can use to achieve this.

const string baseDirectory = @"C:\Temp";
// Open master document.
Document masterDoc = new Document(Path.Combine(baseDirectory, "MasterDocument.doc"));
// Documentbuidler will help us to insert sub-document's content into the master document.
DocumentBuilder builder = new DocumentBuilder(masterDoc);
// Hyperlinks in MS Word documents are fields.
// So in order to find all hyperlinks we should get all FieldStart nodes from the document.
Node[] starts = masterDoc.GetChildNodes(NodeType.FieldStart, true).ToArray();
// Loop through all field starts.
foreach (FieldStart start in starts)
{
    // Check field type. we need to process only Hyperlink fields.
    if (start.FieldType != FieldType.FieldHyperlink)
        continue;
    // If the current FieldStart is start of Hyperlink field,
    // We need to get it's field code.
    // Structure of MS Word field is the following [FieldStart]field code[FieldSepearator]field value[FieldEnd].
    // So to get field code we need to get text between FieldStart and FieldSeparator nodes.
    string fieldCode = string.Empty;
    Node currentNode = start;
    while (currentNode != null && currentNode.NodeType != NodeType.FieldSeparator)
    {
        if (currentNode.NodeType == NodeType.Run)
            fieldCode += ((Run)currentNode).Text;
        // Move to the next node.
        currentNode = currentNode.NextSibling;
    }
    // Once we get field code, we need to parse it in order to get target of the hyperlink.
    // And if target of this hyperlink is another document, we should replace hyperlink with content of this document.
    // We will use regular expression to parse field code.
    Regex regex = new Regex("\\s*HYPERLINK\\s+\"(?[^\"]+)\".*", RegexOptions.IgnoreCase);
    Match match = regex.Match(fieldCode);
    string target = match.Groups["target"].Value;
    // If target ends with .doc we can suppose that this is link to subdocument.
    if (target.EndsWith(".doc"))
    {
        // We suppose that master document and sub document are in the same directory.
        // Open sub-document.
        Document subDoc = new Document(Path.Combine(baseDirectory, target));
        // Move DocumentBuilder cursor to current FieldStart.
        builder.MoveTo(start);
        // Get current paragraph of DocumentBuidler.
        // We will insert subdocument after this paragraph.
        Paragraph insertAfterNode = builder.CurrentParagraph;
        // Insert paragraph break.
        builder.Writeln();
        // Insert sub-document's content into the master document.
        InsertDocument(insertAfterNode, subDoc);
        // Finaly we should remove hyperlink from the master document.
        // Search for FieldEnd node. it is needed to remove field.
        currentNode = start;
        while (currentNode.NodeType != NodeType.FieldEnd)
        {
            currentNode = currentNode.NextPreOrder(masterDoc);
            if (currentNode == null)
                break;
        }
        // Remove all nodes between Fieldstart and FieldEnd
        if (currentNode != null)
        {
            RemoveSequence(start, currentNode);
            start.Remove();
            currentNode.Remove();
        }
    }
}
// Save the final document.
masterDoc.Save(Path.Combine(baseDirectory, "out.doc"));

=============================================================

/// 
/// Remove all nodes between start and end nodes, except start and end nodes
/// 
/// The start node
/// The end node
public void RemoveSequence(Node start, Node end)
{
    Node curNode = start.NextPreOrder(start.Document);
    while (curNode != null && !curNode.Equals(end))
    {
        // Move to next node
        Node nextNode = curNode.NextPreOrder(start.Document);
        // Check whether current contains end node
        if (curNode.IsComposite)
        {
            if (!(curNode as CompositeNode).GetChildNodes(NodeType.Any, true).Contains(end) &&
            !(curNode as CompositeNode).GetChildNodes(NodeType.Any, true).Contains(start))
            {
                nextNode = curNode.NextSibling;
                curNode.Remove();
            }
        }
        else
        {
            curNode.Remove();
        }
        curNode = nextNode;
    }
}

You can find InsertDocument method here:
https://docs.aspose.com/words/net/insert-and-append-documents/
Hope this could help you.
Best regards.

Hi Alexey
Thanks for your help and spending time writing code for this issue.
Best wishes,
Andrew

The issues you have found earlier (filed as WORDSNET-761) have been fixed in this .NET update and this Java update.

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

Are Master Documents still not supported by Aspose words?
I am trying to read a Word Master Document docx and expand it to be processed. When I open it, it only has 99 paragraphs which is a tiny portion of what it would have if expanded.
Would like to be able to process the whole document.
Thanks for any info.

Hi Brad,
The issue mentioned in this thread has already been resolved. Can you please share if you are using a trial version or the licensed version? Please also share your document and code to reproduce the issue.
Best Regards,