0
votes

I want to copy the content of a document created by the user to an existing document. The existing document content must be an exact mirror to the document created by the user.

I cannot simply copy the file using System.IO or saving a copy of the document created by the user using SaveAs methods in Word Interop. This is because the existing document is a document that is generated from a webserver and has VBA modules for uploading it back to the server.

The document generated by the webserver (existing document) is a Word 2003 document, but the document created by the user is either a Word 2003 document or Word 2007+.

Having these limitations in mind, I first created the following method:

string tempsave = //location of user created document;
string savelocation = //location of existing document;
Word.Application objWordOpen = new Word.Application();
Document doclocal = objWordOpen.Documents.Open(tempsave);
Document d1 = objWordOpen.Documents.Open(savelocation);
Word.Range oRange = doclocal.Content;
oRange.Copy();
d1.Activate();
d1.UpdateStyles();
d1.ActiveWindow.Selection.WholeStory();
d1.ActiveWindow.Selection.PasteAndFormat(Word.WdRecoveryType.wdFormatOriginalFormatting);

This is generally working. However, the tables are messed up.

Also, if there is a Page Break, the output is different.

The user created document:

enter image description here

The output - existing document:

enter image description here

Also, at the end of the document a paragraph mark is added, as follows:

The user created document:

enter image description here

The output - existing document:

enter image description here

The page format is also messed up, the output having mirror margins set up.

The user created document:

enter image description here

The output - existing document:

enter image description here

I have also tried using Range.Insert() method and setting the range without copying as described here https://stackoverflow.com/a/54500605/10468231, but I am still having these issues.

I have also tried adding the VBA modules to the document, but there are also Document Variables and other custom properties and I don't want to mess with the file being uploaded to the server. How do I handle these issues? Both the documents are based on Normal template.

I am open to another suggestion regarding this topic, but I know that .doc files are not handled as easily as .docx format, this is why I think I am stuck with COM Interop.

Thank you.

UPDATE Based on Macropod code posted by Charles Kenyon, I have managed to copy more of the formatting from the source to target. Still, there is the difference at the page break - the paragraph mark is places on the new page, instead on the same page. Also, the text is slightly larger, even though the Font Size is the same.

            Word.Range oRange;
            oRange = Source.Content;
            Target.Content.FormattedText = oRange.FormattedText;
            LayoutTransfer(Source, Target);

LayoutTransfer method:

private void LayoutTransfer(Document source, Document target)
        {
            float sPageHght;
            float sPageWdth;
            float sHeaderDist;
            float sFooterDist;
            float sTMargin;
            float sBMargin;
            float sLMargin;
            float sRMargin;
            float sGutter;
            WdGutterStyle sGutterPos;
            WdPaperSize lPaperSize;
            WdGutterStyleOld lGutterStyle;
            int lMirrorMargins;
            WdVerticalAlignment lVerticalAlignment;
            WdSectionStart lScnStart;
            WdSectionDirection lScnDir;
            int lOddEvenHdFt;
            int lDiffFirstHdFt;
            bool bTwoPagesOnOne;
            bool bBkFldPrnt;
            int bBkFldPrnShts;
            bool bBkFldRevPrnt;
            WdOrientation lOrientation;
            foreach (Word.Section section in source.Sections)
            {
                lPaperSize = section.PageSetup.PaperSize;
                lGutterStyle = section.PageSetup.GutterStyle;
                lOrientation = section.PageSetup.Orientation;
                lMirrorMargins = section.PageSetup.MirrorMargins;
                lScnStart = section.PageSetup.SectionStart;
                lScnDir = section.PageSetup.SectionDirection;
                lOddEvenHdFt = section.PageSetup.OddAndEvenPagesHeaderFooter;
                lDiffFirstHdFt = section.PageSetup.DifferentFirstPageHeaderFooter;
                lVerticalAlignment = section.PageSetup.VerticalAlignment;
                sPageHght = section.PageSetup.PageHeight;
                sPageWdth = section.PageSetup.PageWidth;
                sTMargin = section.PageSetup.TopMargin;
                sBMargin = section.PageSetup.BottomMargin;
                sLMargin = section.PageSetup.LeftMargin;
                sRMargin = section.PageSetup.RightMargin;
                sGutter = section.PageSetup.Gutter;
                sGutterPos = section.PageSetup.GutterPos;
                sHeaderDist = section.PageSetup.HeaderDistance;
                sFooterDist = section.PageSetup.FooterDistance;
                bTwoPagesOnOne = section.PageSetup.TwoPagesOnOne;
                bBkFldPrnt = section.PageSetup.BookFoldPrinting;
                bBkFldPrnShts = section.PageSetup.BookFoldPrintingSheets;
                bBkFldRevPrnt = section.PageSetup.BookFoldRevPrinting;

                var index = section.Index;


                target.Sections[index].PageSetup.PaperSize = lPaperSize;
                target.Sections[index].PageSetup.GutterStyle = lGutterStyle;
                target.Sections[index].PageSetup.Orientation = lOrientation;
                target.Sections[index].PageSetup.MirrorMargins = lMirrorMargins;
                target.Sections[index].PageSetup.SectionStart = lScnStart;
                target.Sections[index].PageSetup.SectionDirection = lScnDir;
                target.Sections[index].PageSetup.OddAndEvenPagesHeaderFooter = lOddEvenHdFt;
                target.Sections[index].PageSetup.DifferentFirstPageHeaderFooter = lDiffFirstHdFt;
                target.Sections[index].PageSetup.VerticalAlignment = lVerticalAlignment;
                target.Sections[index].PageSetup.PageHeight = sPageHght;
                target.Sections[index].PageSetup.PageWidth = sPageWdth;
                target.Sections[index].PageSetup.TopMargin = sTMargin;
                target.Sections[index].PageSetup.BottomMargin = sBMargin;
                target.Sections[index].PageSetup.LeftMargin = sLMargin;
                target.Sections[index].PageSetup.RightMargin = sRMargin;
                target.Sections[index].PageSetup.Gutter = sGutter;
                target.Sections[index].PageSetup.GutterPos = sGutterPos;
                target.Sections[index].PageSetup.HeaderDistance = sHeaderDist;
                target.Sections[index].PageSetup.FooterDistance = sFooterDist;
                target.Sections[index].PageSetup.TwoPagesOnOne = bTwoPagesOnOne;
                target.Sections[index].PageSetup.BookFoldPrinting = bBkFldPrnt;
                target.Sections[index].PageSetup.BookFoldPrintingSheets = bBkFldPrnShts;
                target.Sections[index].PageSetup.BookFoldRevPrinting = bBkFldRevPrnt;
            }
        }

UPDATE 2

Actually, the page break not remaining in line with paragraph format is not an issue of copying fidelity, but rather an issue of conversion from .doc to .docx. (https://support.microsoft.com/en-us/help/923183/the-layout-of-a-document-that-contains-a-page-break-may-be-different-i) Maybe someone thought of a method to overcome this.

2
Word files, even with manual page breaks, are not really divided into pages in Word's processes until print time. They will appear different on different computers, and even on the same computer with different printer drivers. Here are some resources on that: wordmvp.com/Mac/PagesInWord.html shaunakelly.com/word/sharing/willmyformatchange.html - Charles Kenyon
Also, if there are headers/footers/watermarks those are in the Section settings which you may not be capturing. Other section settings are the margins (not indents), column settings, and orientation. - Charles Kenyon

2 Answers

1
votes

The following code by Paul Edstein (macropod) may assist you. It will at least give you an idea of the complexities you are facing.

' ============================================================================================================
' KEEP NEXT THREE TOGETHER 
' ============================================================================================================
'
Sub CombineDocuments()
' Paul Edstein
' https://www.msofficeforums.com/word-vba/43339-combine-multiple-word-documents.html
'
' Users occasionally need to combine multiple documents that may of may not have the same page layouts,
'   Style definitions, and so on. Consequently, combining multiple documents is often rather more complex than
'   simply copying & pasting content from one document to another. Problems arise when the documents have
'   different page layouts, headers, footers, page numbering, bookmarks & cross-references,
'   Tables of Contents, Indexes, etc., etc., and especially when those documents have used the same Style
'   names with different definitions.
'
' The following Word macro (for Windows PCs only) handles the more common issues that arise when combining
'   documents; it does not attempt to resolve conflicts with paragraph auto-numbering,
'   document -vs- section page numbering in 'page x of y' numbering schemes, Tables of Contents or Indexing issues.
'   Neither does it attempt to deal with the effects on footnote or endnote numbering & positioning or with the
'   consequences of duplicated bookmarks (only one of which can exist in the merged document) and any corresponding
'   cross-references.
'
' The macro includes a folder browser. Simply select the folder to process and all documents in that folder
'   will be combined into the currently-active document. Word's .doc, .docx, and .docm formats will all be processed,
'   even if different formats exist in the selected folder.
'
    Application.ScreenUpdating = False
    Dim strFolder As String, strFile As String, strTgt As String
    Dim wdDocTgt As Document, wdDocSrc As Document, HdFt As HeaderFooter
    strFolder = GetFolder: If strFolder = "" Then Exit Sub
    Set wdDocTgt = ActiveDocument: strTgt = ActiveDocument.fullname
    strFile = Dir(strFolder & "\*.doc", vbNormal)
    While strFile <> ""
      If strFolder & strFile <> strTgt Then
        Set wdDocSrc = Documents.Open(FileName:=strFolder & "\" & strFile, AddToRecentFiles:=False, Visible:=False)
        With wdDocTgt
          .Characters.Last.InsertBefore vbCr
          .Characters.Last.InsertBreak (wdSectionBreakNextPage)
          With .Sections.Last
            For Each HdFt In .Headers
              With HdFt
                .LinkToPrevious = False
                .range.Text = vbNullString
                .PageNumbers.RestartNumberingAtSection = True
                .PageNumbers.StartingNumber = wdDocSrc.Sections.First.Headers(HdFt.Index).PageNumbers.StartingNumber
              End With
            Next
            For Each HdFt In .Footers
              With HdFt
                .LinkToPrevious = False
                .range.Text = vbNullString
                .PageNumbers.RestartNumberingAtSection = True
                .PageNumbers.StartingNumber = wdDocSrc.Sections.First.Headers(HdFt.Index).PageNumbers.StartingNumber
              End With
            Next
          End With
          Call LayoutTransfer(wdDocTgt, wdDocSrc)
          .range.Characters.Last.FormattedText = wdDocSrc.range.FormattedText
          With .Sections.Last
            For Each HdFt In .Headers
              With HdFt
                .range.FormattedText = wdDocSrc.Sections.Last.Headers(.Index).range.FormattedText
                .range.Characters.Last.Delete
              End With
            Next
            For Each HdFt In .Footers
              With HdFt
                .range.FormattedText = wdDocSrc.Sections.Last.Footers(.Index).range.FormattedText
                .range.Characters.Last.Delete
              End With
            Next
          End With
        End With
        wdDocSrc.Close SaveChanges:=False
      End If
      strFile = Dir()
    Wend
    With wdDocTgt
      ' Save & close the combined document
      .SaveAs FileName:=strFolder & "Forms.docx", FileFormat:=wdFormatXMLDocument, AddToRecentFiles:=False
      ' and/or:
      .SaveAs FileName:=strFolder & "Forms.pdf", FileFormat:=wdFormatPDF, AddToRecentFiles:=False
      .Close SaveChanges:=False
    End With
    Set wdDocSrc = Nothing: Set wdDocTgt = Nothing
    Application.ScreenUpdating = True
End Sub
' ============================================================================================================
Private Function GetFolder() As String
' used by CombineDocument macro by Paul Edstein, keep together in same module
' https://www.msofficeforums.com/word-vba/43339-combine-multiple-word-documents.html

    Dim oFolder As Object
    GetFolder = ""
    Set oFolder = CreateObject("Shell.Application").BrowseForFolder(0, "Choose a folder", 0)
    If (Not oFolder Is Nothing) Then GetFolder = oFolder.Items.Item.Path
    Set oFolder = Nothing
End Function

Sub LayoutTransfer(wdDocTgt As Document, wdDocSrc As Document)
' works with previous Combine Documents macro from Paul Edstein, keep together
' https://www.msofficeforums.com/word-vba/43339-combine-multiple-word-documents.html
'
    Dim sPageHght As Single, sPageWdth As Single
    Dim sHeaderDist As Single, sFooterDist As Single
    Dim sTMargin As Single, sBMargin As Single
    Dim sLMargin As Single, sRMargin As Single
    Dim sGutter As Single, sGutterPos As Single
    Dim lPaperSize As Long, lGutterStyle As Long
    Dim lMirrorMargins As Long, lVerticalAlignment As Long
    Dim lScnStart As Long, lScnDir As Long
    Dim lOddEvenHdFt As Long, lDiffFirstHdFt As Long
    Dim bTwoPagesOnOne As Boolean, bBkFldPrnt As Boolean
    Dim bBkFldPrnShts As Boolean, bBkFldRevPrnt As Boolean
    Dim lOrientation As Long
    With wdDocSrc.Sections.Last.PageSetup
      lPaperSize = .PaperSize
      lGutterStyle = .GutterStyle
      lOrientation = .Orientation
      lMirrorMargins = .MirrorMargins
      lScnStart = .SectionStart
      lScnDir = .SectionDirection
      lOddEvenHdFt = .OddAndEvenPagesHeaderFooter
      lDiffFirstHdFt = .DifferentFirstPageHeaderFooter
      lVerticalAlignment = .VerticalAlignment
      sPageHght = .PageHeight
      sPageWdth = .PageWidth
      sTMargin = .TopMargin
      sBMargin = .BottomMargin
      sLMargin = .LeftMargin
      sRMargin = .RightMargin
      sGutter = .Gutter
      sGutterPos = .GutterPos
      sHeaderDist = .HeaderDistance
      sFooterDist = .FooterDistance
      bTwoPagesOnOne = .TwoPagesOnOne
      bBkFldPrnt = .BookFoldPrinting
      bBkFldPrnShts = .BookFoldPrintingSheets
      bBkFldRevPrnt = .BookFoldRevPrinting
    End With
    With wdDocTgt.Sections.Last.PageSetup
      .GutterStyle = lGutterStyle
      .MirrorMargins = lMirrorMargins
      .SectionStart = lScnStart
      .SectionDirection = lScnDir
      .OddAndEvenPagesHeaderFooter = lOddEvenHdFt
      .DifferentFirstPageHeaderFooter = lDiffFirstHdFt
      .VerticalAlignment = lVerticalAlignment
      .PageHeight = sPageHght
      .PageWidth = sPageWdth
      .TopMargin = sTMargin
      .BottomMargin = sBMargin
      .LeftMargin = sLMargin
      .RightMargin = sRMargin
      .Gutter = sGutter
      .GutterPos = sGutterPos
      .HeaderDistance = sHeaderDist
      .FooterDistance = sFooterDist
      .TwoPagesOnOne = bTwoPagesOnOne
      .BookFoldPrinting = bBkFldPrnt
      .BookFoldPrintingSheets = bBkFldPrnShts
      .BookFoldRevPrinting = bBkFldRevPrnt
      .PaperSize = lPaperSize
      .Orientation = lOrientation
    End With
End Sub
 
' ============================================================================================================
0
votes

I used a Template and copied it several times into a new Word Document after editing it. It worked like this

Word.Range rng = wordDocTarget.Content;
rng.Collapse(Word.WdCollapseDirection.wdCollapseEnd)
rng.FormattedText = wordDocSource.Content.FormattedText

An alternative could also be to insert a whole file to a range / document

rng = wordDoc.Range
rng.Collapse(Word.WdCollapseDirection.wdCollapseEnd)
rng.InsertFile(filepath)