Combine documents from folder - vba

I have a document with several letters separated with section breaks.
What I want to do is to break the document into several ones containing X number of letters (without manually selecting them).
What I have done is to separate it into individual letters with one macro (BreakOnSection), and then combine them with another one (MergeMultiDocsIntoOne) that open a file browser and allows me to select the files I want manually. Below are the macros.
Main Question: If the main document is divided into, let's say, 100 smaller documents, is it possible to modify the second macro, so it selects automatically 10 of them from a folder, merges/combines them creating a new document, and then goes on with another batch of 10, and so on?
First macro:
Sub BreakOnSection()
'Criteria for moving through the document by section.
Application.Browser.Target = wdBrowseSection
'For i = 1 To ((ActiveDocument.Sections.Count) - 1)
For i = 1 To ActiveDocument.Sections.Count
'Copy the whole section
ActiveDocument.Bookmarks("\Section").Range.Copy
'Create a new document to paste text from the clipboard.
Documents.Add
Selection.Paste
'Removes the break that is copied at the end of the section, if any.
Selection.MoveUp Unit:=wdLine, Count:=1, Extend:=wdExtend
Selection.Delete Unit:=wdCharacter, Count:=1
ChangeFileOpenDirectory "C:\Users\MyUser\Desktop\MyFolder"
DocNum = DocNum + 1
ActiveDocument.SaveAs Filename:="letter_" & DocNum & ".docx"
ActiveDocument.Close
'Move the selection to the next section
Application.Browser.Next
Next i
ActiveDocument.Close savechanges:=wdDoNotSaveChanges
'ActiveDocument.Close savechanges:=wdSaveChanges
End Sub
Second macro:
Sub MergeMultiDocsIntoOne()
Dim dlgFile As FileDialog
Dim nTotalFiles As Integer
Dim nEachSelectedFile As Integer
Set dlgFile = Application.FileDialog(msoFileDialogFilePicker)
With dlgFile
.AllowMultiSelect = True
If .Show <> -1 Then
Exit Sub
Else
nTotalFiles = .SelectedItems.Count
End If
End With
For nEachSelectedFile = 1 To nTotalFiles
Selection.InsertFile dlgFile.SelectedItems.Item(nEachSelectedFile)
If nEachSelectedFile < nTotalFiles Then
Selection.InsertBreak Type:=wdPageBreak
Else
If nEachSelectedFile = nTotalFiles Then
Exit Sub
End If
End If
Next nEachSelectedFile
End Sub

Instead of breaking all the Sections into separate documents before recombining them, you'd do far better to simply split the original document into however multi-Section blocks you need. The following code will split any multi-Section document that you might want to break into equal Section counts:
Sub SplitDocument()
Application.ScreenUpdating = False
Dim i As Long, j As Long, k As Long, StrTxt As String
Dim Rng As Range, Doc As Document, HdFt As HeaderFooter
Const StrNoChr As String = """*./\:?|"
j = InputBox("How many Section breaks are there per output document?", "Split By Sections", 1)
With ActiveDocument
' Process each Section
For i = 1 To .Sections.Count - 1 Step j
With .Sections(i)
'*****
' Get the 1st paragraph
Set Rng = .Range.Paragraphs(1).Range
With Rng
' Contract the range to exclude the final paragraph break
.MoveEnd wdCharacter, -1
StrTxt = .Text
For k = 1 To Len(StrNoChr)
StrTxt = Replace(StrTxt, Mid(StrNoChr, k, 1), "_")
Next
End With
' Construct the destination file path & name
StrTxt = ActiveDocument.Path & "\" & StrTxt
'*****
' Get the whole Section
Set Rng = .Range
With Rng
If j > 1 Then .MoveEnd wdSection, j - 1
'Contract the range to exclude the Section break
.MoveEnd wdCharacter, -1
' Copy the range
.Copy
End With
End With
' Create the output document
Set Doc = Documents.Add(Template:=ActiveDocument.AttachedTemplate.FullName, Visible:=False)
With Doc
' Paste contents into the output document, preserving the formatting
.Range.PasteAndFormat (wdFormatOriginalFormatting)
' Delete trailing paragraph breaks & page breaks at the end
While .Characters.Last.Previous = vbCr Or .Characters.Last.Previous = Chr(12)
.Characters.Last.Previous = vbNullString
Wend
' Replicate the headers & footers
For Each HdFt In Rng.Sections(j).Headers
.Sections(j).Headers(HdFt.Index).Range.FormattedText = HdFt.Range.FormattedText
Next
For Each HdFt In Rng.Sections(j).Footers
.Sections(j).Footers(HdFt.Index).Range.FormattedText = HdFt.Range.FormattedText
Next
' Save & close the output document
.SaveAs FileName:=StrTxt & ".docx", FileFormat:=wdFormatXMLDocument, AddToRecentFiles:=False
' and/or:
.SaveAs FileName:=StrTxt & ".pdf", FileFormat:=wdFormatPDF, AddToRecentFiles:=False
.Close SaveChanges:=False
End With
Next
End With
Set Rng = Nothing: Set Doc = Nothing
Application.ScreenUpdating = True
End Sub
Word document and PDF output are both catered for.
As coded, it is assumed the output filename consists of the first paragraph in each group of Sections. If not, you could use a different range or replace all of the content between the ***** strings with code like:
' Construct the destination file path & name
StrTxt = ActiveDocument.Path & "\" & (i + j - 1) / j

Related

Copy Header and footer along with Section text using VBA

I have a Merged-letters Document I need to split it into individual letters.
Following code is doing exactly But it didn't copy the header and footer of each individual page. How can I make it to copy headers and footers along with first section.
Right now it is using oDoc.Sections.First.Range.Cut line to copy the section.
Code:
Sub Splitter_Updated()
' Based on a Macro created 16-08-98 by Doug Robbins to save each letter created by a
' mailmerge as a separate file.
Dim Letters As Long
Dim Counter As Long
Dim Mask As String
Dim DocName As String
Dim oDoc As Document
Dim oNewDoc As Document
Set oDoc = ActiveDocument
oDoc.Save
Selection.EndKey Unit:=wdStory
Letters = Selection.Information(wdActiveEndSectionNumber)
Selection.HomeKey Unit:=wdStory
Counter = 1
While Counter < Letters
DocName = Format(Date, "ddMMyy") _
& "-" & LTrim$(Str$(Counter)) & ".docx"
Debug.Print oDoc.Sections.Count
Debug.Print oDoc.Sections.First.Headers(wdHeaderFooterFirstPage).Range.Text
oDoc.Sections.First.Range.Cut
Set oNewDoc = Documents.Add
'Documents are based on the Normal template
'To use an alternative template follow the link.
With Selection
.Paste
.EndKey Unit:=wdStory
.MoveLeft Unit:=wdCharacter, Count:=1
.Delete Unit:=wdCharacter, Count:=1
End With
oNewDoc.SaveAs FileName:=oDoc.Path & Application.PathSeparator & DocName, AddToRecentFiles:=False
'FileFormat:=wdFormatDocument,
ActiveWindow.Close
Counter = Counter + 1
Wend
oDoc.Close wdDoNotSaveChanges
End Sub

Append Word docx files while keeping their format in VBA

I am creating a Word Macro that receives two arguments: a list of docx documents and the name of the new file. The goal is that the Macro inserts one document after the other, preserving their respective format, and saves as a new docx document.
Sub Merger(path As String, args () As Variant)
Dim vArg As Variant
Active Document.Select
Selection.ClearFormatting
For Each vArg In args
ActiveDocument.Content.Words.Last.Select
Selection.InsertFile:= _ vArg _,Range:="", _ConfirmConversions:= False, Link:=False, Attachment:= False )
Selection.InsertBreak Type:=wdPageBreak
Next vArg
ActiveDocument.SaveAs2 File Name=path
ActiveDocument.Close
Application.Quit
Note that I call the Macro from an empty docx file.
The problem is that neither the header nor the format of the orginal files are preserved in the new docx document.
The Word format is not modular. Instead, consider creating a Master Document, then filling it with subdocuments. Here's code to create a master document from a folder full of subdocuments:
Sub AssembleMasterDoc()
Dim SubDocFile$, FolderPath$, Template$
Dim Counter&
Dim oFolder As FileDialog
Dim oBookmark As Bookmark
Dim oTOC As TableOfContents
'Create a dynamic array variable, and then declare its initial size
Dim DirectoryListArray() As String
ReDim DirectoryListArray(1000)
Template$ = ActiveDocument.AttachedTemplate.Path & Application.PathSeparator & ActiveDocument.AttachedTemplate.Name
'Loop through all the files in the directory by using Dir$ function
Set oFolder = Application.FileDialog(msoFileDialogFolderPicker)
With oFolder
.AllowMultiSelect = False
If .Show <> 0 Then
FolderPath$ = .SelectedItems(1)
Else
GoTo EndSub
End If
End With
Application.ScreenUpdating = False
SubDocFile$ = Dir$(FolderPath$ & Application.PathSeparator & "*.*")
Do While SubDocFile$ <> ""
DirectoryListArray(Counter) = SubDocFile$
SubDocFile$ = Dir$
Counter& = Counter& + 1
Loop
'Reset the size of the array without losing its values by using Redim Preserve
ReDim Preserve DirectoryListArray(Counter& - 1)
WordBasic.SortArray DirectoryListArray()
ActiveWindow.ActivePane.View.Type = wdOutlineView
ActiveWindow.View = wdMasterView
Selection.EndKey Unit:=wdStory
For x = 0 To (Counter& - 1)
If IsNumeric(Left(DirectoryListArray(x), 1)) Then
FullName$ = FolderPath$ & Application.PathSeparator & DirectoryListArray(x)
Documents.Open FileName:=FullName$, ConfirmConversions:=False
With Documents(FullName$)
.AttachedTemplate = Template$
For Each oBookmark In Documents(FullName$).Bookmarks
oBookmark.Delete
Next oBookmark
.Close SaveChanges:=True
End With
Selection.Range.Subdocuments.AddFromFile Name:=FullName$, ConfirmConversions:=False
End If
Next x
For Each oTOC In ActiveDocument.TablesOfContents
oTOC.Update
Next oTOC
ActiveWindow.ActivePane.View.Type = wdPrintView
Application.ScreenUpdating = True
EndSub:
End Sub
This code is from a previous project, so you may not need all of it, like the update of multiple TOCs.
Don't attempt to maintain and edit Master Documents. The format is prone to corruption. Instead, assemble a master document for printing (or other use), then discard it.

Select some parts of text from one Word document and copy into another Word document

I have a word file with some spaces, for example:
Word File XXXXX
Title: XXXXX
etc
And I have another word file which have that data that is missing:
Word File 20248
Title: Example of word file
etc
My question is, how can I use vba to recognize the data from the first file to be copied into the second file in the spaces I want. Furthermore I'd prefer that you can select the word file you want with a dialog box rather than putting in the code where the file is located as I have different files that can have the location changed.
Thank you so much for your answers. I'm pretty new in vba and I have never used it on word.
By now I have this code to choose the word file from which I want to copy the data:
Sub CopyData()
Dim DC As Document
Dim wD As Document, strD As String, wDNumb As Variant
Dim I As Long
Set wD = ActiveDocument
DSelection:
For I = 1 To Documents.Count
strD = strD & Documents(I).Name & " - " & I & vbCrLf
Next I
wDNumb = InputBox("Please, choose the number of the word file from which you are choosing the data to copy:" & vbCrLf & _
vbCrLf & strD, "Choose the word document from which you are copying the data!", 1)
If wDNumb <= Documents.Count And wDNumb >= 1 Then
GoTo DSelection2
ElseIf wDNumb = "" Then MsgBox "Operation cancelled", vbCritical, "Cancelled"
Exit Sub
ElseIf wDNumb > Documents.Count Or wDNumb < 1 Then MsgBox "Wrong number, input a correct number", vbExclamation, "Wrong number"
Exit Sub
End If
DSelection2:
If IsNumeric(wDNumb) Then
Set DC = Documents(CLng(wDNumb))
Else
MsgBox "Please choose the number on the right of the document chosen!": GoTo DSelection
End If
End Sub
I have the following part of the code to copy some part of the Word to the other using bookmarks:
DC.Activate
Set Rng = DC.Range
With Rng.Find
.ClearFormatting
.Execute FindText:="TITLE:", Forward:=True, _
Format:=False, Wrap:=wdFindStop
Fnd = .Found
End With
If Fnd = True Then
With Rng
.MoveStart wdCharacter, 10
.MoveEnd wdSentence, 1
End With
End If
Rng.Select
Selection.Copy
wD.Activate
Selection.GoTo What:=wdGoToBookmark, Name:="TITLE"
Selection.EndKey Unit:=wdLine, Extend:=wdExtend
Selection.Paste
There are multiple possible ways of approaching this, but your problem description lacks sufficient detail. For example, one could insert:
bookmarks;
content controls;
Section breaks;
tables;
etc.,
into the target document so that content from the source document can be inserted there.
Alternatively, one might use Find/Replace to locate a predefined string that can be replaced with the desired content.
With your updated problem description, you might use:
Dim RngDC As Range, wDRng As Range, BkMkNm As String
BkMkNm "TITLE"
With DC
With .Range.Find
.ClearFormatting
.Execute FindText:=BkMkNm, Forward:=True, Format:=False, Wrap:=wdFindStop
End With
If .Found = True Then
.MoveStart wdCharacter, 10
.MoveEnd wdSentence, 1
Set RngDC = .Duplicate
End If
End With
With wD
Set wDRng = .Bookmarks(BkMkNm).Range
wDRng.FormattedText = RngDC.FormattedText
.Bookmarks.Add BkMkNm, wDRng
End With

VBA Code to change word footer in multiple files based on page number

I have a macro that runs to make a single page doc into a 5 page doc (NCR Duplicates) for all files in a folder.
I am using a set of nested IF fields in my footer, which changes the footer based on page number. The field looks like this
Text here {If{PAGE}="1""Original"{If{PAGE}="2""Copy 1"
{If{PAGE}="3""Copy 2"{If{PAGE}="4""Copy 3"{If{PAGE}="5""Copy 4"}}}}}
Other Text
I am trying to figure out how to add this footer to all the documents in a folder. It doesn't need to use field, if there is a way simply based on page number.
I have bashed my head against the wall, searched like crazy, and now come hat in hand.
The macro to make the duplicate copies is:
Sub Make5CopiesNCR()
vDirectory = BrowseForFolder
vFile = Dir(vDirectory & "\" & "*.*")
Do While vFile <> ""
Documents.Open FileName:=vDirectory & "\" & vFile
MakeCopies
vFile = Dir
Loop
End Sub
End Sub
Private Sub MakeCopies()
Dim i As Integer
Selection.WholeStory
Selection.Copy
For i = 1 To 6
Selection.PasteAndFormat wdFormatOriginalFormatting
Next
With ActiveDocument
.GoTo What:=wdGoToPage, Which:=wdGoToAbsolute, Name:=6 'Page number
.Bookmarks("\Page").Select
With Selection
.Delete
ActiveDocument.Close SaveChanges:=wdSaveChanges, OriginalFormat:=wdWordDocument
End With
End With
End Sub
The problem with using a mailmerge with your field construction is that it gets converted to the result. Try a field coded as:
{={PAGE}-1 \# "'Copy {={PAGE}-1}';;'Original'"}
Now, if you create the required 5 pages in your mailmerge main document, all the outputs will likewise be in multiples of 5 pages, with the correct page numbering.
Even if you use a mailmerge main document with only a single page, the outputs will have the field coding required to produce the correct numbering for however many more pages you want to add to the outputs.
As for replicating this in your existing files, simply create a document with the required footer content, then use a macro like:
Sub ReplicateFooter()
Application.ScreenUpdating = False
Dim DocSrc As Document, DocTgt As Document, Rng As Range
Dim StrPth As String, StrNm As String, StrSrc As String
Set DocSrc = ActiveDocument
Set Rng = DocSrc.Sections.First.Footers(wdHeaderFooterPrimary).Range
StrPth = DocSrc.Path & "\": StrSrc = DocSrc.FullName
StrNm = Dir(StrPth & "*.doc", vbNormal)
While StrNm <> ""
If StrPth & StrNm <> StrSrc Then
Set DocTgt = Documents.Open(FileName:=StrPth & StrNm, AddToRecentFiles:=False, Visible:=False)
With DocTgt
With .Sections.First.Footers(wdHeaderFooterPrimary).Range
.FormattedText = Rng.FormattedText
.Characters.Last.Text = vbNullString
End With
.Close True
End With
End If
StrNm = Dir()
Wend
Set Rng = Nothing: Set DocTgt = Nothing: Set DocSrc = Nothing
Application.ScreenUpdating = True
End Sub

How do I split my one document into many at page breaks in VBA?

I am new to VBA. I got this macro from online and it has worked for me before, but now I am getting a runtime error from it.
The macro is supposed to take a mail merged document I have, and split it into individual documents for each recipient.
The runtime error 5487 is pointing me to the line
" .SaveAs fileName:=StrTxt &...".
I have tried to save it as a different file format, and have gone through the other posts on StackOverflow where others have the same error, but I am still getting the error message.
My code is:
Sub SplitMergedDocument()
' Source: http://msofficeforums.com/mail-merge/21803-mailmerge-tips-tricks.html
Const StrNoChr As String = """*./\:?|"
Dim i As Long, j As Long, k As Long, StrTxt As String
Dim Rng As Range, Doc As Document, HdFt As HeaderFooter
Application.ScreenUpdating = False
j = InputBox("How many Section breaks are there per record?", "Split By Sections ", 1)
With ActiveDocument
For i = 1 To .Sections.Count - 1 Step j ' Process each Section
With .Sections(i)
Set Rng = .Range.Paragraphs(1).Range ' Get 1st paragraph
With Rng
.MoveEnd wdCharacter, -1 'range to exclude final paragraph break
StrTxt = .Text
For k = 1 To Len(StrNoChr)
StrTxt = Replace(StrTxt, Mid(StrNoChr, k, 1), "_")
Next
End With
' Construct destination file path & name
StrTxt = ActiveDocument.Path & Application.PathSeparator & StrTxt
Set Rng = .Range ' Get whole Section
With Rng
If j > 1 Then .MoveEnd wdSection, j - 1
.MoveEnd wdCharacter, -1 'Contract range to exclude Section break
.Copy ' Copy range
End With
End With
' Create output document
Set Doc = Documents.Add(Template:=ActiveDocument.AttachedTemplate.FullName _
, Visible:=False)
With Doc
' Paste contents into output document, preserving formatting
.Range.PasteAndFormat (wdFormatOriginalFormatting)
' Delete trailing paragraph breaks & page breaks at end
While .Characters.Last.Previous = vbCr Or .Characters.Last.Previous = Chr(12)
.Characters.Last.Previous = vbNullString
Wend
For Each HdFt In Rng.Sections(j).Headers ' Replicate headers & footers
.Sections(j).Headers(HdFt.Index).Range.FormattedText = HdFt.Range.FormattedText
Next
For Each HdFt In Rng.Sections(j).Footers
.Sections(j).Footers(HdFt.Index).Range.FormattedText = HdFt.Range.FormattedText
Next
' Save & close output document
.SaveAs FileName:=StrTxt & ".docx", FileFormat:=wdFormatXMLDocument _
, AddToRecentFiles:=False
.Close SaveChanges:=False
End With
Next
End With
Set Rng = Nothing: Set Doc = Nothing
Application.ScreenUpdating = True
End Sub
Thank you!
Without knowing more (such as the value of StrTxt), I can't say for sure why you're getting the error but it's likely either an invalid filename, or the file is locked by another process, or a permissions issue.
Perhaps the procedure below will work better for you. (I'm unclear on the significance of "records" in your code.)
Split document into separate files for each page:
This procedure splits the ActiveDocument into one .DOCX file per "visible page" (calculated page breaks, manual page breaks, section breaks, etc).\
Sub WordDocToPages()
'splits active Word doc by page into separate DOCX files (same folder as active doc)
Dim doc As Document, docPage As Document, rgPage As Range
Dim pgNum As Long, pgCnt As Long, ext As String, fName As String
Set doc = ActiveDocument 'Use current document
Set rgPage = doc.Range 'create range of 1 page
Application.ScreenUpdating = False 'prevent screen updates
pgCnt = doc.Content.Information(wdNumberOfPagesInDocument) 'get page count
Do While pgNum < pgCnt
pgNum = pgNum + 1 'increment page counter
Application.StatusBar = "Saving page " & pgNum & " of " & pgCnt
If pgNum < pgCnt Then
Selection.GoTo wdGoToPage, wdGoToAbsolute, pgNum + 1 'top of next page
rgPage.End = Selection.Start 'end of page=top of next
Else
rgPage.End = doc.Range.End 'end of last page=EOF
End If
rgPage.Copy 'copy page
Set docPage = Documents.Add(Visible:=False) 'create new document
With docPage
With .Range
.Paste 'paste page
.Find.Execute Findtext:="^m", ReplaceWith:="" 'remove manual breaks
.Select
End With
With Selection
.EndKey wdStory 'goto end of doc
.MoveLeft wdCharacter, 1, wdExtend 'remove final CR
If Asc(.Text) = 13 Then .Delete wdCharacter, 1 'remove trailing CR
End With
ext = Mid(doc.FullName, InStrRev(doc.FullName, ".")) 'extract file extension
fName = Replace(doc.FullName, ext, " #" & _
Format(pgNum, String(Len(CStr(pgCnt)), "0")) & ".docx") 'new filename
.SaveAs fName, wdFormatDocumentDefault 'save single-page doc
.Close 'close new document
End With
rgPage.Collapse wdCollapseEnd 'ready for next page
Loop
Application.ScreenUpdating = True 'resume screen updates
Application.StatusBar = "Document was split into " & pgNum & " files."
Set docPage = Nothing: Set rgPage = Nothing: Set doc = Nothing 'cleanup objects
End Sub
This is loosely based on the example at Usefulware Sharing.
The new files are saved to the same folder as ActiveDocument.Path, with the document title appended with a sequential number. Note that existing output files are overwritten, and there is no validation or error handling.