Trying to use VBA to Automate Document Splitting in Word - vba

I am trying to VBA my way into automating a process that my team and myself currently do manually-- taking a Word document and splitting it into multiple documents based on H1 sections (by which I mean, if a doc has 6 H1s, then we wind up with 6 documents).
I have found some code that works well enough, but there are a couple pieces that I can't quite puzzle out.
Getting the footers from my original document to show up in the subdocuments, and
adding a sequential number at the start of each file name.
The former requirement is pretty simple-- my original doc has a footer on it, and I'd like the documents that the code spits out to have the same footer. Right now, the resulting files have blank footers. The latter requirement is that I ultimately would like the new files to have file names with the format "XX - [HeadingText].docx". The code I'm using gets me the heading text just fine, but I can't seem to plug in the sequential numbering.
Here's the code I'm using; any help would be appreciated!
Sub SeparateHeadings()
'
' SeparateHeadings Macro
'
'
Application.ScreenUpdating = False
Dim StrTmplt As String, StrPath As String, StrFlNm As String, Rng As Range, Doc As Document, i As Long
Dim iTemp As Integer
With ActiveDocument
StrTmplt = .AttachedTemplate.FullName
StrPath = .Path & "\"
With .Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = ""
.Style = "Heading 1"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
.Execute
End With
Do While .Find.Found
Set Rng = .Paragraphs(1).Range.Duplicate
With Rng
StrFlNm = Replace(.Text, vbCr, "")
For i = 1 To 255
Select Case i
Case 1 To 31, 33, 34, 37, 42, 44, 46, 47, 58 - 63, 91 - 93, 96, 124, 147, 148
StrFlNm = Replace(StrFlNm, Chr(i), "")
End Select
Next
iTemp = iTemp + 1
Do
If .Paragraphs.Last.Range.End = ActiveDocument.Range.End Then Exit Do
Select Case .Paragraphs.Last.Next.Style
Case "Heading 1"
Exit Do
Case Else
.MoveEnd wdParagraph, 1
End Select
Loop
End With
Set Doc = Documents.Add(Template:=StrTmplt, Visible:=False)
With Doc
.Range.FormattedText = Rng.FormattedText
.SaveAs2 FileName:=StrPath & StrFlNm, Fileformat:=wdFormatXMLDocument, AddToRecentFiles:=False
.Close False
End With
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
End With
Set Doc = Nothing: Set Rng = Nothing
Application.ScreenUpdating = True
End Sub

Try:
Sub SplitDocByHeading1()
Application.ScreenUpdating = False
Dim StrTmplt As String, StrPath As String, StrFlNm As String
Dim Rng As Range, i As Long, j As Long, Doc As Document
Const StrNoChr As String = """*./\:?|"
With ActiveDocument
StrTmplt = .FullName
StrPath = .Path & "\"
'Convert auto numbering to static numbering
.ConvertNumbersToText (wdNumberAllNumbers)
With .Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = ""
.Replacement.Text = ""
.Style = wdStyleHeading1
.Format = True
.Forward = True
.Wrap = wdFindStop
.Execute
End With
Do While .Find.Found
Set Rng = .Duplicate: i = i + 1
StrFlNm = Split(Rng.Paragraphs(1).Range.Text, vbCr)(0)
For j = 1 To Len(StrNoChr)
StrFlNm = Replace(StrFlNm, Mid(StrNoChr, j, 1), "_")
Next
StrFlNm = Format(i, "00") & "_" & StrFlNm & ".docx"
Set Rng = Rng.GoTo(What:=wdGoToBookmark, Name:="\HeadingLevel")
Set Doc = Documents.Add(Template:=StrTmplt, Visible:=False)
With Doc
.Range.FormattedText = Rng.FormattedText
.SaveAs2 FileName:=StrPath & StrFlNm, Fileformat:=wdFormatXMLDocument, AddToRecentFiles:=False
.Close False
End With
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
End With
Set Doc = Nothing: Set Rng = Nothing
Application.ScreenUpdating = True
End Sub

Related

How to find table column, then move down and replace the cell's content IF it is "N/A"

I have almost 1,800 Word documents that have about 8 pages with unique data in tables. We were just informed that the data we were given for some of those tables is inaccurate and needs to be changed from "N/A" to "0.0%". As "N/A" is used a lot in the document, I unfortunately cannot just find/replace that text.
Using this thread (Macro to find in Word table for specific string in a cell and move x cell left, check isnumeric then set typography on down x cell in the same column) I was able to adjust the code below to find the column header (On-Time Completion Rate) and move to the adjacent cells to update them. However, since this column is for percentages, the IsNumeric code is changing any data it finds due to the percentage symbol.
Is there a way to do the same but instead of using IsNumeric (since it does not work for percentages) check the value in the cell and if it finds "N/A" change it to "0.0%"? This would then need to be repeated for two more tables, with one table have four rows to look through.
Thank you in advance for any help you can offer!
Screenshot of table
Sub Demo()
Application.ScreenUpdating = False
Dim r As Long, c As Long
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "On-time Completion Rate" 'Column Header'
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchWildcards = True
.Execute
End With
Do While .Find.Found
If .Information(wdWithInTable) = True Then
r = .Cells(1).RowIndex
c = .Cells(1).ColumnIndex
With .Tables(1)
If Not IsNumeric(Split(.Cell(r + 1, c).Range.Text, vbCr)(0)) Then .Cell(r + 1, c).Range.Text = "0.0%"
If Not IsNumeric(Split(.Cell(r + 2, c).Range.Text, vbCr)(0)) Then .Cell(r + 2, c).Range.Text = "0.0%"
End With
End If
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
Application.ScreenUpdating = True
End Sub
Try this:
Sub Demo()
Application.ScreenUpdating = False
Dim r As Long, c As Long
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "On-time Completion Rate" 'Column Header'
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchWildcards = True
.Execute
End With
Do While .Find.Found
If .Information(wdWithInTable) = True Then
r = .Cells(1).RowIndex
c = .Cells(1).ColumnIndex
With .Tables(1)
If Split(.Cell(r + 1, c).Range.Text, vbCr)(0) = "N/A" Then .Cell(r + 1, c).Range.Text = "0.0%"
If Split(.Cell(r + 2, c).Range.Text, vbCr)(0) = "N/A" Then .Cell(r + 2, c).Range.Text = "0.0%"
End With
End If
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
Application.ScreenUpdating = True
End Sub
If all instances of N/A in the tables are to be replaced, the following would be more efficient:
Sub Demo()
Application.ScreenUpdating = False
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "On-time Completion Rate"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchWildcards = True
.Execute
End With
Do While .Find.Found
.Duplicate.Tables(1).Range.Find.Execute FindText:="N/A", ReplaceWith:="0.0%", Wrap:=wdFindStop, Replace:=wdReplaceAll
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
Application.ScreenUpdating = True
End Sub
Extending this to process a whole folder of documents, you could use code like:
Sub UpdateDocuments()
Application.ScreenUpdating = False
Dim strFolder As String, strFile As String, strDocNm As String, wdDoc As Document
strDocNm = ActiveDocument.FullName: strFolder = GetFolder
If strFolder = "" Then Exit Sub
strFile = Dir(strFolder & "\*.docx", vbNormal)
While strFile <> ""
If strFolder & "\" & strFile <> strDocNm Then
Set wdDoc = Documents.Open(FileName:=strFolder & "\" & strFile, AddToRecentFiles:=False, Visible:=False)
With wdDoc
With .Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "On-time Completion Rate"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchWildcards = True
.Execute
End With
Do While .Find.Found
.Duplicate.Tables(1).Range.Find.Execute FindText:="N/A", ReplaceWith:="0.0%", Wrap:=wdFindStop, Replace:=wdReplaceAll
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
.Close SaveChanges:=True
End With
End If
strFile = Dir()
Wend
Set wdDoc = Nothing
Application.ScreenUpdating = True
End Sub
Function GetFolder() As String
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
To extend the code even further to process documents in sub-folders, see: https://www.msofficeforums.com/47785-post14.html
To save the updated documents as PDFs, insert:
.SaveAs FileName:=Split(.FullName, ".doc")(0) & ".pdf", FileFormat:=wdFormatPDF, AddToRecentFiles:=False
before:
.Close SaveChanges:=True

How to prevent word from crashing when using batch find and replace macro?

I am using this code which is a batch find and replace macro. It finds and replaces the words in the document by reading the replacement words from another document (text.docx). This works absolutely fine when there are a handful of changes (i.e. less than 1 page). However, I hope to use this macro on documents that are 10-20 pages. When I use it, the word document just immediately crashes (starts not responding) and has to be forced to quit.
Does anyone have any tips on what can be done to prevent it from crashing? How can I modify the code to batch edit thousands of words? Code is below.
Thanks in advance!
Sub ReplaceFromTableList()
Dim oChanges As Document, oDoc As Document
Dim oTable As Table
Dim oRng As Range
Dim rFindText As Range, rReplacement As Range
Dim i As Long
Dim y As Integer
Dim sFname As String
Dim sAsk As String
sFname = "/Users/user/Desktop/test.docx"
Set oDoc = ActiveDocument
Set oChanges = Documents.Open(FileName:=sFname, Visible:=False)
Set oTable = oChanges.Tables(1)
y = 0
For i = 1 To oTable.Rows.Count
Set oRng = oDoc.Range
Set rFindText = oTable.Cell(i, 1).Range
rFindText.End = rFindText.End - 1
Set rReplacement = oTable.Cell(i, 2).Range
rReplacement.End = rReplacement.End - 1
With oRng.Find
.ClearFormatting
.Replacement.ClearFormatting
Do While .Execute(findText:=rFindText, _
MatchWholeWord:=True, _
MatchWildcards:=False, _
Forward:=True, _
Wrap:=wdFindStop) = True
oRng.Select
oRng.FormattedText = rReplacement.FormattedText
y = y + 1
Loop
End With
Next i
oChanges.Close wdDoNotSaveChanges
MsgBox (y & " errors fixed")
End Sub
Your use of the FormattedText method to reproduce the formatting necessitates a time-consuming loop for each expression. The more the find expression occurs in the target document, the longer the process will take. Your unnecessary use of oRng.Select (which you don't then do anything with) makes it even slower - especially since you don't disable ScreenUpdating. The following macro avoids the need for the FormattedText looping:
Sub BulkFindReplace()
Application.ScreenUpdating = False
Dim ThisDoc As Document, FRDoc As Document, Rng As Range, i As Long, j As Long, StrRep As String, StrCount As String
Set ThisDoc = ActiveDocument
Set FRDoc = Documents.Open("C:\Users\" & Environ("Username") & "\Downloads\FindReplaceTable.docx", _
ReadOnly:=True, AddToRecentFiles:=False, Visible:=False)
With ThisDoc.Range.Find
.ClearFormatting
.Replacement.ClearFormatting
.Format = False
.Forward = True
.Wrap = wdFindContinue
'Process each word from the F/R Table
For i = 1 To FRDoc.Tables(1).Rows.Count
Set Rng = FRDoc.Tables(1).Rows(i).Cells(1).Range
Rng.End = Rng.End - 1
.Text = Rng
StrCount = StrCount & vbCr & Rng.Text & ":" & vbTab & _
(Len(ThisDoc.Range.Text) - Len(Replace(ThisDoc.Range, Rng.Text, ""))) / Len(Rng.Text)
Set Rng = FRDoc.Tables(1).Rows(i).Cells(2).Range
Rng.End = Rng.End - 1
With Rng
If Len(.Text) > 0 Then
.Copy
StrRep = "^c"
Else
StrRep = ""
End If
End With
.Replacement.Text = StrRep
.Execute Replace:=wdReplaceAll
If i Mod 20 = 0 Then DoEvents
Next
End With
FRDoc.Close False
MsgBox "The following strings were replaced:" & StrCount
Set Rng = Nothing: Set FRDoc = Nothing: Set ThisDoc = Nothing
Application.ScreenUpdating = True
End Sub
Try this:
Sub FindReplaceAll()
Dim MyDialog As FileDialog, GetStr(1 To 100) As String
'100 files is the maximum applying this code
On Error Resume Next
Set MyDialog = Application.FileDialog(msoFileDialogFilePicker)
With MyDialog
.Filters.Clear
.AllowMultiSelect = True
i = 1
If .Show = -1 Then
For Each stiSelectedItem In .SelectedItems
GetStr(i) = stiSelectedItem
i = i + 1
Next
i = i - 1
End If
Application.ScreenUpdating = False
For j = 1 To i Step 1
Set Doc = Documents.Open(FileName:=GetStr(j), Visible:=True)
Windows(GetStr(j)).Activate
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = "Marriott International" 'Find What
.Replacement.Text = "Marriott" 'Replace With
.Forward = True
.Wrap = wdFindAsk
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchByte = True
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
Application.Run macroname:="NEWMACROS"
ActiveDocument.Save
ActiveWindow.Close
Next
Application.ScreenUpdating = True
End With
MsgBox "operation end, please view", vbInformation
End Sub
The idea comes from here:
https://www.extendoffice.com/documents/word/1002-word-replace-multiple-files.html

How to split a Word document (docx or rtf) using a heading1 style as the split

I have a large file I have split into many separate rtf files with this slightly modified code I got online. The problem was I didn't want to include the Heading 1 text in the output file. However the Heading 1 data is used to create the filename of each output document.
This is the format of the file I am splitting to new files.
1.1.1 This would be marked Heading1 style
some text in here some text in here some text in here some text in here
1.1.2 This would be marked Heading1 style
some text in here some text in here some text in here some text in here
1.1.3 This would be marked Heading1 style
some text in here some text in here some text in here some text in here
=============================================================================== So what it outputs are files named 1.1.1.rtf, 1.1.2.rtf etc and would just contain the body text, but no heading.
repeats to end
Any guidance will be appreciated.
Sub aSplitOnHeadings()
'
' SplitOnHeadings Macro
'
'
Application.ScreenUpdating = False
Dim StrTmplt As String, StrPath As String, StrFlNm As String, Rng As Range, Doc As Document, i As Long, extension As String
extension = ".rtf" ' Jon added so we can have 1.1.1 for the references
With ActiveDocument
StrTmplt = .AttachedTemplate.FullName
StrPath = .Path & "\"
With .Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = ""
.Style = "Heading 1"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = True
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
.Execute
End With
Do While .Find.Found
Set Rng = .Paragraphs(1).Range.Duplicate
With Rng
StrFlNm = Replace(.Text, vbCr, "")
For i = 1 To 255 'I took out the chr 46 the full stop because it is legal 44 comma
Select Case i
Case 1 To 31, 33, 34, 37, 42, 47, 58 - 63, 91 - 93, 96, 124, 147, 148
StrFlNm = Replace(StrFlNm, Chr(i), "")
End Select
Next
Do
If .Paragraphs.Last.Range.End = ActiveDocument.Range.End Then Exit Do
Select Case .Paragraphs.Last.Next.Style
Case "Heading 1"
Selection.EndKey Unit:=wdLine
Exit Do
Case Else
.MoveEnd wdParagraph, 1
End Select
Loop
End With
Set Doc = Documents.Add(Template:=StrTmplt, Visible:=False)
With Doc
.Range.FormattedText = Rng.FormattedText
.SaveAs2 FileName:=StrPath & StrFlNm & extension, Fileformat:=wdFormatRTF, AddToRecentFiles:=False
.Close False
End With
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
End With
Set Doc = Nothing: Set Rng = Nothing
Application.ScreenUpdating = True
'.SaveAs2 FileName:=StrPath & StrFlNm, FileFormat:=wdFormatRTF, AddToRecentFiles:=False
'.SaveAs2 FileName:=StrPath & StrFlNm, Fileformat:=wdFormatXMLDocument, AddToRecentFiles:=False
End Sub
These two sets macro will work. Each one splits a document on Heading1 style into separate documents with the document named as the Heading1 it was split at, and the Heading1 is not included in the new document. That is just perfect.
Here are the two sets of macro two for output in .rtf and two for docx Also in these macro I removed the . from being an illegal character as I did need the output to be as per the Heading1 exactly. Thanks macropod for taking the time to sort this. I will try to learn more about macros.
Jon.
Sub SplitDocOnHeading1ToRtfWithHeadingInOutput()
'Splits the document on Heading1 style, into new documents, Heading1 is included in the data.
Application.ScreenUpdating = False
Dim Rng As Range, DocSrc As Document, DocTgt As Document
Dim i As Long, StrTxt As String: Const StrNoChr As String = """*/\:?|"
Set DocSrc = ActiveDocument
With DocSrc.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Format = True
.Forward = True
.Text = ""
.Style = wdStyleHeading1
.Replacement.Text = ""
.Wrap = wdFindStop
.Execute
End With
Do While .Find.Found
Set Rng = .Paragraphs(1).Range
Set Rng = Rng.GoTo(What:=wdGoToBookmark, Name:="\HeadingLevel")
Set DocTgt = Documents.Add(DocSrc.AttachedTemplate.FullName)
With DocTgt
Application.ScreenUpdating = False
.Range.FormattedText = Rng.FormattedText
StrTxt = Split(.Paragraphs.First.Range.Text, vbCr)(0)
' Strip out illegal characters
For i = 1 To Len(StrNoChr)
StrTxt = Replace(StrTxt, Mid(StrNoChr, i, 1), "_")
Next
'.Paragraphs.First.Range.Delete 'un comment this line if you don't want to retain headings in the output file
.SaveAs2 FileName:=DocSrc.Path & "\" & StrTxt & ".rtf", Fileformat:=wdFormatRTF, AddToRecentFiles:=False
.Close False
End With
.Start = Rng.End
.Find.Execute
Loop
End With
Set Rng = Nothing: Set DocSrc = Nothing: Set DocTgt = Nothing
Application.ScreenUpdating = True
End Sub
Sub SplitDocOnHeading1ToRtfNoHeadingInOutput()
'Splits the document on Heading1 style, into new documents, Heading1 is NOT included in the data
Application.ScreenUpdating = False
Dim Rng As Range, DocSrc As Document, DocTgt As Document
Dim i As Long, StrTxt As String: Const StrNoChr As String = """*/\:?|"
Set DocSrc = ActiveDocument
With DocSrc.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Format = True
.Forward = True
.Text = ""
.Style = wdStyleHeading1
.Replacement.Text = ""
.Wrap = wdFindStop
.Execute
End With
Do While .Find.Found
Set Rng = .Paragraphs(1).Range
Set Rng = Rng.GoTo(What:=wdGoToBookmark, Name:="\HeadingLevel")
Set DocTgt = Documents.Add(DocSrc.AttachedTemplate.FullName)
With DocTgt
Application.ScreenUpdating = False
.Range.FormattedText = Rng.FormattedText
StrTxt = Split(.Paragraphs.First.Range.Text, vbCr)(0)
' Strip out illegal characters
For i = 1 To Len(StrNoChr)
StrTxt = Replace(StrTxt, Mid(StrNoChr, i, 1), "_")
Next
.Paragraphs.First.Range.Delete 'comment out this line if you want to retain headings in the output file
.SaveAs2 FileName:=DocSrc.Path & "\" & StrTxt & ".rtf", Fileformat:=wdFormatRTF, AddToRecentFiles:=False
.Close False
End With
.Start = Rng.End
.Find.Execute
Loop
End With
Set Rng = Nothing: Set DocSrc = Nothing: Set DocTgt = Nothing
Application.ScreenUpdating = True
End Sub
Sub SplitDocOnHeading1ToDocxWithHeadingInOutput()
'Splits the document on Heading1 style, into new documents, Heading1 is included in the data.
Application.ScreenUpdating = False
Dim Rng As Range, DocSrc As Document, DocTgt As Document
Dim i As Long, StrTxt As String: Const StrNoChr As String = """*/\:?|"
Set DocSrc = ActiveDocument
With DocSrc.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Format = True
.Forward = True
.Text = ""
.Style = wdStyleHeading1
.Replacement.Text = ""
.Wrap = wdFindStop
.Execute
End With
Do While .Find.Found
Set Rng = .Paragraphs(1).Range
Set Rng = Rng.GoTo(What:=wdGoToBookmark, Name:="\HeadingLevel")
Set DocTgt = Documents.Add(DocSrc.AttachedTemplate.FullName)
With DocTgt
Application.ScreenUpdating = False
.Range.FormattedText = Rng.FormattedText
StrTxt = Split(.Paragraphs.First.Range.Text, vbCr)(0)
' Strip out illegal characters
For i = 1 To Len(StrNoChr)
StrTxt = Replace(StrTxt, Mid(StrNoChr, i, 1), "_")
Next
'.Paragraphs.First.Range.Delete 'un comment this line if you don't want to retain headings in the output file
.SaveAs2 FileName:=DocSrc.Path & "\" & StrTxt & ".docx", Fileformat:=wdFormatXMLDocument, AddToRecentFiles:=False
.Close False
End With
.Start = Rng.End
.Find.Execute
Loop
End With
Set Rng = Nothing: Set DocSrc = Nothing: Set DocTgt = Nothing
Application.ScreenUpdating = True
End Sub
Sub SplitDocOnHeading1ToDocxNoHeadingInOutput()
'Splits the document on Heading1 style, into new documents, Heading1 is NOT included in the data
Application.ScreenUpdating = False
Dim Rng As Range, DocSrc As Document, DocTgt As Document
Dim i As Long, StrTxt As String: Const StrNoChr As String = """*/\:?|"
Set DocSrc = ActiveDocument
With DocSrc.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Format = True
.Forward = True
.Text = ""
.Style = wdStyleHeading1
.Replacement.Text = ""
.Wrap = wdFindStop
.Execute
End With
Do While .Find.Found
Set Rng = .Paragraphs(1).Range
Set Rng = Rng.GoTo(What:=wdGoToBookmark, Name:="\HeadingLevel")
Set DocTgt = Documents.Add(DocSrc.AttachedTemplate.FullName)
With DocTgt
Application.ScreenUpdating = False
.Range.FormattedText = Rng.FormattedText
StrTxt = Split(.Paragraphs.First.Range.Text, vbCr)(0)
' Strip out illegal characters
For i = 1 To Len(StrNoChr)
StrTxt = Replace(StrTxt, Mid(StrNoChr, i, 1), "_")
Next
.Paragraphs.First.Range.Delete 'comment out this line if you want to retain headings in the output file
.SaveAs2 FileName:=DocSrc.Path & "\" & StrTxt & ".docx", Fileformat:=wdFormatXMLDocument, AddToRecentFiles:=False
.Close False
End With
.Start = Rng.End
.Find.Execute
Loop
End With
Set Rng = Nothing: Set DocSrc = Nothing: Set DocTgt = Nothing
Application.ScreenUpdating = True
End Sub
Try something based on:
Sub SplitDoc()
Application.ScreenUpdating = False
Dim Rng As Range, DocSrc As Document, DocTgt As Document
Dim i As Long, StrTxt As String: Const StrNoChr As String = """*./\:?|"
Set DocSrc = ActiveDocument
With DocSrc.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Format = True
.Forward = True
.Text = ""
.Style = wdStyleHeading1
.Replacement.Text = ""
.Wrap = wdFindStop
.Execute
End With
Do While .Find.Found
Set Rng = .Paragraphs(1).Range
Set Rng = Rng.GoTo(What:=wdGoToBookmark, Name:="\HeadingLevel")
Set DocTgt = Documents.Add(DocSrc.AttachedTemplate.FullName)
With DocTgt
.Range.FormattedText = Rng.FormattedText
StrTxt = Split(.Paragraphs.First.Range.Text, vbCr)(0)
' Strip out illegal characters
For i = 1 To Len(StrNoChr)
StrTxt = Replace(StrTxt, Mid(StrNoChr, i, 1), "_")
Next
.Paragraphs.First.Range.Delete
.SaveAs2 FileName:=DocSrc.Path & "\" & StrTxt & ".docx", FileFormat:=wdFormatXMLDocument, AddToRecentFiles:=False
.Close False
End With
.Start = Rng.End
.Find.Execute
Loop
End With
Set Rng = Nothing: Set DocSrc = Nothing: Set DocTgt = Nothing
Application.ScreenUpdating = True
End Sub

VBA using excel data to search word document & pasting result into a table

So I want to be able to search a word document (roughly 300 pages) and find certain phrases (one word or two words seperated by a space) (eg: Nationwide/Phrase 2/Phrase 3) which I have in column 'A' of a separate excel document (C:/Test.xlsx). Then this 'phrase' would be coiped and pasted into a table in another word document along with the context (20 characters before & after the 'phrase') along the page/line number it was found. Now someone (and I'm truly thankful) had created the following macro which used an array. Unfortunatley there could be approx 100-200 words that I would be looking for and I can't get it to include them all in the array or use the excel document as the data.
Here is the code so far
Many thanks for looking at this!!!!!
Sub CopyKeywordPlusContext()
'Modified 2-17-2015 GKM
'Makro created on 22.01.2013
Dim oDoc As Document, oDocRecord As Document
Dim strSearch As String, arrSearch
Dim lngCharTrailing As Long, lngCharLeading As Long, lngIndex As Long, lngCount As Long
Dim lngPgNum, lngLineNum As Integer
Dim oRng As Word.Range, oRngSpan As Word.Range
Dim bFound As Boolean
Dim oTbl As Word.Table
strSearch = vbNullString
Dim xl As Object
Set xl = GetObject(, "Excel.Application")
arrSearch = xl.transpose(xl.activesheet.Range("A1:A14").Value)
lngCharLeading = 20
lngCharTrailing = 20
Set oDoc = ActiveDocument
For lngIndex = 1 To UBound(arrSearch)
ResetFRParams
bFound = False
lngCount = 0
Set oRng = oDoc.Range
With oRng.Find
.Text = LCase(arrSearch(lngIndex))
While .Execute
bFound = True
If oDocRecord Is Nothing Then
Set oDocRecord = Documents.Add
Set oTbl = oDocRecord.Tables.Add(oDocRecord.Range, 1, 2)
End If
lngCount = lngCount + 1
If lngCount = 1 Then
oTbl.Rows.Add
With oTbl.Rows.Last.Previous
.Cells.Merge
With .Cells(1).Range
.Text = "Search results for """ & arrSearch(lngIndex) & """ + context in " & """" & oDoc.Name & """"
.Font.Bold = True
End With
End With
End If
Set oRngSpan = oRng.Duplicate
oRngSpan.Select
lngPgNum = Selection.Information(wdActiveEndPageNumber)
lngLineNum = Selection.Information(wdFirstCharacterLineNumber)
With oRngSpan
.MoveStart wdCharacter, -lngCharLeading
.MoveEnd wdCharacter, lngCharTrailing
Do While oRngSpan.Characters.First = vbCr
oRngSpan.MoveStart wdCharacter, -1
Loop
Do While oRngSpan.Characters.Last = vbCr
oRngSpan.MoveEnd wdCharacter, 1
If oRngSpan.End = oDoc.Range.End Then
oRngSpan.End = oRngSpan.End - 1
Exit Do
End If
Loop
End With
oTbl.Rows.Last.Range.Cells(1).Range.Text = Trim(oRngSpan.Text)
oTbl.Rows.Last.Range.Cells(2).Range.Text = "Page: " & lngPgNum & " Line: " & lngLineNum
oTbl.Rows.Add
Wend
End With
If bFound Then
ResetFRParams
With oDocRecord.Range.Find
.Text = LCase(arrSearch(lngIndex))
.Replacement.Text = "^&"
.Replacement.Highlight = True
.Format = True
.Execute Replace:=wdReplaceAll
End With
End If
Next lngIndex
oTbl.Rows.Last.Delete
End Sub
Sub ResetFRParams()
With Selection.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = ""
.Replacement.Text = ""
.Replacement.Highlight = False
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
.Execute
End With
lbl_Exit:
Exit Sub
End Sub
To populate the array with the values in colA of the active sheet in an open instance of Excel (note there can be only one insatance of excel open or it may get the wrong instance):
Replace
arrSearch = Split("Nationwide,Phrase 2,Phrase 3", ",")
with
Dim xl As Object
Set xl = GetObject(, "Excel.Application")
arrSearch = xl.transpose(xl.activesheet.Range("A1:A14").Value)
and
For lngIndex = 0 To UBound(arrSearch)
with
For lngIndex = 1 To UBound(arrSearch)
Answer by the man, the legend Tim Williams!!!! Truly thankful!!!
Sub CopyKeywordPlusContext()
'Modified 2-17-2015 GKM
'Makro created on 22.01.2013
Dim oDoc As Document, oDocRecord As Document
Dim strSearch As String, arrSearch
Dim lngCharTrailing As Long, lngCharLeading As Long, lngIndex As Long, lngCount As Long
Dim lngPgNum, lngLineNum As Integer
Dim oRng As Word.Range, oRngSpan As Word.Range
Dim bFound As Boolean
Dim oTbl As Word.Table
strSearch = vbNullString
Dim xl As Object
Set xl = GetObject(, "Excel.Application")
arrSearch = xl.transpose(xl.activesheet.Range("A1:A14").Value)
lngCharLeading = 20
lngCharTrailing = 20
Set oDoc = ActiveDocument
For lngIndex = 1 To UBound(arrSearch)
ResetFRParams
bFound = False
lngCount = 0
Set oRng = oDoc.Range
With oRng.Find
.Text = LCase(arrSearch(lngIndex))
While .Execute
bFound = True
If oDocRecord Is Nothing Then
Set oDocRecord = Documents.Add
Set oTbl = oDocRecord.Tables.Add(oDocRecord.Range, 1, 2)
End If
lngCount = lngCount + 1
If lngCount = 1 Then
oTbl.Rows.Add
With oTbl.Rows.Last.Previous
.Cells.Merge
With .Cells(1).Range
.Text = "Search results for """ & arrSearch(lngIndex) & """ + context in " & """" & oDoc.Name & """"
.Font.Bold = True
End With
End With
End If
Set oRngSpan = oRng.Duplicate
oRngSpan.Select
lngPgNum = Selection.Information(wdActiveEndPageNumber)
lngLineNum = Selection.Information(wdFirstCharacterLineNumber)
With oRngSpan
.MoveStart wdCharacter, -lngCharLeading
.MoveEnd wdCharacter, lngCharTrailing
Do While oRngSpan.Characters.First = vbCr
oRngSpan.MoveStart wdCharacter, -1
Loop
Do While oRngSpan.Characters.Last = vbCr
oRngSpan.MoveEnd wdCharacter, 1
If oRngSpan.End = oDoc.Range.End Then
oRngSpan.End = oRngSpan.End - 1
Exit Do
End If
Loop
End With
oTbl.Rows.Last.Range.Cells(1).Range.Text = Trim(oRngSpan.Text)
oTbl.Rows.Last.Range.Cells(2).Range.Text = "Page: " & lngPgNum & " Line: " & lngLineNum
oTbl.Rows.Add
Wend
End With
If bFound Then
ResetFRParams
With oDocRecord.Range.Find
.Text = LCase(arrSearch(lngIndex))
.Replacement.Text = "^&"
.Replacement.Highlight = True
.Format = True
.Execute Replace:=wdReplaceAll
End With
End If
Next lngIndex
oTbl.Rows.Last.Delete
End Sub
Sub ResetFRParams()
With Selection.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = ""
.Replacement.Text = ""
.Replacement.Highlight = False
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
.Execute
End With
lbl_Exit:
Exit Sub
End Sub

Searching for words in word, but ignoring tables

I have the fantastic macro below which
Searches for words (listed in an excel file)
Copies each instance
Pastes into a new word document together with it's location from the original document
This has been created and amended by various people and I am truly greatful!!. One thing that I was wondering if possible is:
If in the word document which you're searching there are tables, can you make the macro to ignore tables? or would it be better to say 'If the word is found and is in a table ignore this instance and proceed searching te document again'
The latter would have more unnecessary iterations in my opinion.
I had managed to find the code:
Sub NonTableParagraphs()
Dim rng() As Range
Dim t As Integer
Dim tbl As Table
Dim para As Paragraph
Dim r As Integer
ReDim Preserve rng(t)
Set rng(t) = ActiveDocument.Range
For Each tbl In ActiveDocument.Tables
rng(t).End = tbl.Range.Start
t = t + 1
ReDim Preserve rng(t)
Set rng(t) = ActiveDocument.Range
rng(t).Start = tbl.Range.End
Next tbl
rng(t).End = ActiveDocument.Range.End
For r = 0 To t
For Each para In rng(r).Paragraphs
'do processing
Next para
Next r
End Sub
and had tried to insert NonTableParagraphs in the original macro, so it would run a sub routine, but I couldn't get it to work.
It looks like I should be trying to use ActiveDocument.Tables and somehow stating if ActiveDocument.Tables found, skip the rest of the lines in macro & then return to searching after the table but I can't seem to get it to work.
I'll see if I can search for that
Many thanks!!!
Sub CopyKeywordPlusContext()
'Modified 3-10-2015 TW
'Modified 2-17-2015 GKM
'Makro created on 22.01.2013
Dim oDoc As Document, oDocRecord As Document
Dim strSearch As String, arrSearch
Dim lngCharTrailing As Long, lngCharLeading As Long, lngIndex As Long, lngCount As Long
Dim lngPgNum, lngLineNum As Integer
Dim oRng As Word.Range, oRngSpan As Word.Range
Dim bFound As Boolean
Dim oTbl As Word.Table
strSearch = vbNullString
Dim xl As Object
Set xl = GetObject(, "Excel.Application")
arrSearch = xl.transpose(xl.activesheet.Range("A1:A221").Value)
lngCharLeading = 20
lngCharTrailing = 20
Set oDoc = ActiveDocument
For lngIndex = 1 To UBound(arrSearch)
ResetFRParams
bFound = False
lngCount = 0
Set oRng = oDoc.Range
With oRng.Find
.Text = LCase(arrSearch(lngIndex))
While .Execute
bFound = True
If oDocRecord Is Nothing Then
Set oDocRecord = Documents.Add
Set oTbl = oDocRecord.Tables.Add(oDocRecord.Range, 1, 2)
End If
lngCount = lngCount + 1
If lngCount = 1 Then
oTbl.Rows.Add
With oTbl.Rows.Last.Previous
.Cells.Merge
With .Cells(1).Range
.Text = "Search results for """ & arrSearch(lngIndex) & """ + context in " & """" & oDoc.Name & """"
.Font.Bold = True
End With
End With
End If
Set oRngSpan = oRng.Duplicate
oRngSpan.Select
lngPgNum = Selection.Information(wdActiveEndPageNumber)
lngLineNum = Selection.Information(wdFirstCharacterLineNumber)
With oRngSpan
.MoveStart wdCharacter, -lngCharLeading
.MoveEnd wdCharacter, lngCharTrailing
Do While oRngSpan.Characters.First = vbCr
oRngSpan.MoveStart wdCharacter, -1
Loop
Do While oRngSpan.Characters.Last = vbCr
oRngSpan.MoveEnd wdCharacter, 1
If oRngSpan.End = oDoc.Range.End Then
oRngSpan.End = oRngSpan.End - 1
Exit Do
End If
Loop
End With
oTbl.Rows.Last.Range.Cells(1).Range.Text = Trim(oRngSpan.Text)
oTbl.Rows.Last.Range.Cells(2).Range.Text = "Page: " & lngPgNum & " Line: " & lngLineNum
oTbl.Rows.Add
Wend
End With
If bFound Then
ResetFRParams
With oDocRecord.Range.Find
.Text = LCase(arrSearch(lngIndex))
.Replacement.Text = "^&"
.Replacement.Highlight = True
.Format = True
.Execute Replace:=wdReplaceAll
End With
End If
Next lngIndex
oTbl.Rows.Last.Delete
End Sub
Sub ResetFRParams()
With Selection.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = ""
.Replacement.Text = ""
.Replacement.Highlight = False
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
.Execute
End With
lbl_Exit:
Exit Sub
End Sub
Instead of trying to debug/edit your code look at this and decide for yourself where to insert it.
Sub FindText()
Dim doc As Word.Document, rng As Word.Range
Set doc = Word.ActiveDocument
Set rng = doc.Content
With rng.Find
.ClearFormatting
.Format = False
.Forward = True
.Text = "Now is"
.Wrap = wdFindStop
.Execute
Do While .Found
If rng.Information(Word.WdInformation.wdWithInTable) Then
'do nothing
rng.Collapse Word.WdCollapseDirection.wdCollapseEnd
Else
rng.Text = "Now is not"
rng.Collapse Word.WdCollapseDirection.wdCollapseEnd
End If
.Execute
Loop
End With
End Sub