I have a working, however a slow method for searching through a document and search for a specific text: Issue "tab" A and then replace it with text but where A is referred to a customvariable. Has anyone done something similar?
Sub hsdkjgh()
Call replaceIssueNonVariable(ActiveDocument)
End Sub
Public Function SearchInStory(ByVal rngStory As word.Range, ByVal strSearch As String) As Boolean
SearchInStory = False
With rngStory.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = strSearch
.Replacement.Text = strReplace
.Wrap = wdFindContinue
.MatchCase = False
.MatchWildcards = True
.Execute
If .found Then
rngStory.Text = "Issue:" & vbTab & ";;;;;;"
Call SearchInStory2(rngStory, ";;;;;;")
SearchInStory = True
End If
End With
End Function
Public Function SearchInStory2(ByVal rngStory As word.Range, ByVal strSearch As String) As Boolean
SearchInStory2 = False
With rngStory.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = strSearch
.Replacement.Text = strReplace
.Wrap = wdFindContinue
.MatchCase = False
.MatchWildcards = True
.Execute
If .found Then
rngStory.Select
Selection.Fields.Add Range:=Selection.Range, Type:=wdFieldDocProperty, Text:="_DocumentIssue", PreserveFormatting:=True
SearchInStory2 = True
End If
End With
End Function
Sub replaceIssueNonVariable(doc As Document)
Dim temp As Variant
Dim pFindTxtArray(4) As String
Dim pReplaceTxtArray(4) As String
pReplaceTxt = "Issue:^t" ' { DOCPROPERTY _DocumentIssue ^92* MERGEFORMAT }"
ActiveWindow.View.ShowFieldCodes = True
exitRevision = False
For i = 65 To 90
pFindTxt = "Issue:^t^" & i
'Iterate through all story types in the current document
For Each rngStory In doc.StoryRanges
'Iterate through all linked stories
Do
If SearchInStory(rngStory, pFindTxt) Then
'rngStory.Select
'Selection.Fields.Add Range:=Selection.Range, Type:=wdFieldDocProperty, Text:="_DocumentIssue", PreserveFormatting:=True
exitRevision = True
End If
On Error Resume Next
Select Case rngStory.StoryType
Case WdStoryType.wdEvenPagesHeaderStory, _
WdStoryType.wdPrimaryHeaderStory, _
WdStoryType.wdEvenPagesFooterStory, _
WdStoryType.wdPrimaryFooterStory, _
WdStoryType.wdFirstPageHeaderStory, _
WdStoryType.wdFirstPageFooterStory
If rngStory.ShapeRange.Count > 0 Then
For Each oShp In rngStory.ShapeRange
If oShp.TextFrame.HasText Then
If SearchInStory(rngStory, pFindTxt) Then
exitRevision = True
End If
End If
Next
End If
Case Else
'Do Nothing
End Select
On Error GoTo 0
'Get next linked story (if any)
Set rngStory = rngStory.NextStoryRange
Loop Until rngStory Is Nothing
Next
If exitRevision Then
Call CustomProperties.createCustomDocumentProperty(doc, "_DocumentIssue", Chr(i))
Exit For
End If
Next i
' Refresh fields
doc.Fields.update
ActiveWindow.View.ShowFieldCodes = False
Exit Sub
'create the new variable
Call CustomProperties.createCustomDocumentProperty(doc, "_DocumentIssue", Right(pFindTxtArray, 1))
doesNotExist:
MsgBox "CustomVariable " & findText & " does not exist"
Exit Sub
Debug.Print findText & " variable has got this new name: " & replaceText
End Sub
Related
I need a macro that replaces his/her or he/she with a conditional merge field. Thanks to another website, I was able to replace these pronouns with a merge field, but not a conditional merge field without crashing MS Word. Below is the code that I used.
Sub TestAddIf()
Dim doc As Word.Document
Dim mRng As Range
Set doc = ActiveDocument
Set mRng = ActiveDocument.Range
With oRng.Find
Do While .Execute(FindText:="he")
doc.MailMerge.Fields.AddIf mRng, _
MERGEFIELD:="""Client_Sex""", Comparison:=wdMergeIfEqual, CompareTo:="M", _
truetext:="he", _
falsetext:="she"
mRng.Collapse wdCollapseEnd
Loop
End With
End Sub
Try the following macro, which deals with 'he', 'his', 'him', and 'male' throughout the document (delete the ',male' & ',female' terms if you don't want them).
Sub Demo()
Application.ScreenUpdating = False
Dim Rng As Range, RngFld As Range, StrFnd As String, StrRep As String, StrCode As String, i As Long, j As Long
StrM = "he,his,him,male": StrF = "she,her,her,female"
With ActiveDocument
For i = 0 To UBound(Split(StrM, ","))
StrCode = "IFX= ""M"" """ & Split(StrM, ",")(i) & """ """ & Split(StrF, ",")(i) & """"
j = Len(StrCode) + 4
Set Rng = .Range(0, 0)
.Fields.Add Range:=Rng, Type:=wdFieldEmpty, Text:=StrCode, PreserveFormatting:=False
Rng.End = Rng.End + j
.Fields.Add Range:=Rng.Characters(5), Type:=wdFieldEmpty, Text:="MERGEFIELD Client_Sex", PreserveFormatting:=False
Rng.Cut
With .Range.Find
.ClearFormatting
.Replacement.ClearFormatting
.Forward = True
.Wrap = wdFindContinue
.MatchCase = False
.MatchWholeWord = True
.MatchWildcards = False
.Text = Split(StrM, ",")(i)
.Replacement.Text = "^c"
.Execute Replace:=wdReplaceAll
End With
Next
End With
Application.ScreenUpdating = True
End Sub
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
I am trying to go through a word document, and replace existing image path strings with the actual image. When I enter the address hard coded, it works. But if I put the same code in a variable I get an error
Error:
Run-time error '5152':
This is not a valid file name.
Try one or more of the following:
* Check the path to make sure it was typed correctly,
* Select a file from the list of files and folders.
Code:
Sub InsertJPGs()
For Each singleLine In ActiveDocument.Paragraphs
Dim Value As Variant
Dim imageName As String
Options.DefaultFilePath(wdDocumentsPath) = "d:\Downloads\ReportImages\"
originalLineText = singleLine.Range.Text
lineText = singleLine.Range.Text
If InStr(lineText, ".jpg") <> 0 Then
singleLine.Range.Select
rangeText = singleLine.Range.Text
imageName = rangeText
imageName = "D:\Downloads\ReportImages\" & rangeText
'imageName = "D:\Downloads\ReportImages\PictureImportTest_ATTICSkylight#1#_img2.jpg"
Selection.InlineShapes.AddPicture FileName:= _
imageName, LinkToFile:=True, SaveWithDocument:=True
End If
If InStr(lineText, "[[[IMAGE LIST]]]") <> 0 Then
Exit For
End If
Next singleLine
End Sub
Try:
Sub Demo()
Application.ScreenUpdating = False
Dim FlNm As String: Const StrPath As String = "d:\Downloads\ReportImages\"
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = "<[! ^t^l^13]#.[Jj][Pp][Gg]>"
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchWildcards = True
.Execute
End With
Do While .Find.Found
FlNm = .Duplicate.Text
.Text = vbNullString
.InlineShapes.AddPicture StrPath & FlNm, False, True, .Duplicate
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
Application.ScreenUpdating = True
End Sub
If you want to retain the filenames in the document, delete or comment out:
.Text = vbNullString
How can I rename my customVariable in word? I have a Customvaraiable called "Document Number", but I would now like to rename it to _DocumentNumber"
I know I can create a new CustomVariable, delete the old one, but I am then struggling with how to update all the links in the document to the new one.
Edited code:
Sub test()
Dim A As word.Field
Dim FldUpd As String
Dim findText As String
Dim replaceText As String
findText = "Document Number"
replaceText = "_DocumentNumber"
ActiveWindow.View.ShowFieldCodes = False
If ActiveDocument.CustomDocumentProperties(findText).value = "" Then Exit Sub
For Each A In ActiveDocument.Fields
If A.result.Text = ActiveDocument.CustomDocumentProperties(findText).value Then
Call WordProperties.createCustomDocumentProperty(ActiveDocument.name, replaceText, ActiveDocument.CustomDocumentProperties(findText).value)
ActiveWindow.View.ShowFieldCodes = True
A.Select
'DOCPROPERTY "Document number" \* MERGEFORMAT
With Selection.Find
.Text = "DOCPROPERTY*" & findText
.Replacement.Text = "DOCPROPERTY " & replaceText
.Format = True
.MatchCase = False
.MatchWildcards = True
End With
tempBool = Selection.Find.Execute(replace:=wdReplaceAll)
' Refresh fields
ActiveDocument.Fields.update
ActiveWindow.View.ShowFieldCodes = False
If tempBool Then ActiveDocument.CustomDocumentProperties(findText).Delete
End If
Next
End Sub
Edited:
The problem is that the find method does not return true.
You are mixing up two things that must be separate.
First create the new doc property, using ActiveDocument.CustomDocumentProperties.Add
You do this only once, since the doc property exists only once.
Then you replace all existing references to the old doc property to the new one. Something like (excerpts from macro recorder)
' Show field source references (Alt+F9) so you can use Find&Replace
ActiveWindow.View.ShowFieldCodes = True
With Selection.Find
.Text = "DOCPROPERTY Document Number"
.Replacement.Text = "DOCPROPERTY _DocumentNumber"
.Format = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
' Refresh fields
ActiveDocument.Fields.Update
ActiveWindow.View.ShowFieldCodes = False
Then you can delete the old doc property.
Edit: to find and replace in all sections (including header & footer), see
Searching for Text in Header Section of A Word Document
Here is the working code, feel free to improve it:
Sub test()
Dim findText As String
Dim replaceText As String
Dim temp As Variant
findText = "Document Number"
replaceText = "_DocumentNumber"
On Error GoTo doesNotExist
temp = ActiveDocument.CustomDocumentProperties(findText).value
pFindTxt = "DOCPROPERTY*" & findText
pReplaceTxt = "DOCPROPERTY """ & replaceText
ActiveWindow.View.ShowFieldCodes = True
'create the new variable
Call WordProperties.createCustomDocumentProperty(ActiveDocument.name, replaceText, ActiveDocument.CustomDocumentProperties(findText).value)
'Iterate through all story types in the current document
For Each rngStory In ActiveDocument.StoryRanges
'Iterate through all linked stories
Do
SearchAndReplaceInStory rngStory, pFindTxt, pReplaceTxt
On Error Resume Next
Select Case rngStory.StoryType
Case WdStoryType.wdEvenPagesHeaderStory, _
WdStoryType.wdPrimaryHeaderStory, _
WdStoryType.wdEvenPagesFooterStory, _
WdStoryType.wdPrimaryFooterStory, _
WdStoryType.wdFirstPageHeaderStory, _
WdStoryType.wdFirstPageFooterStory
If rngStory.ShapeRange.Count > 0 Then
For Each oShp In rngStory.ShapeRange
If oShp.TextFrame.HasText Then
SearchAndReplaceInStory oShp.TextFrame.TextRange, pFindTxt, pReplaceTxt
End If
Next
End If
Case Else
'Do Nothing
End Select
On Error GoTo 0
'Get next linked story (if any)
Set rngStory = rngStory.NextStoryRange
Loop Until rngStory Is Nothing
Next
' Refresh fields
ActiveDocument.Fields.update
ActiveWindow.View.ShowFieldCodes = False
Exit Sub
doesNotExist:
MsgBox "CustomVariable " & findText & " does not exist"
Exit Sub
End Sub
Public Sub SearchAndReplaceInStory(ByVal rngStory As word.Range, ByVal strSearch As String, ByVal strReplace As String)
With rngStory.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = strSearch
.Replacement.Text = strReplace
.Wrap = wdFindContinue
.MatchCase = False
.MatchWildcards = True
.Execute replace:=wdReplaceAll
End With
End Sub
I need to write a VBA Word macro that will do a find and replace to change all occurrences of text in one font to another font. The code I have (listed below) does this but in ignores all the text in text boxes in the document. How do I either modify this macro to search all text both inside and outside textboxes in the document (headers and footers would be a plus but not absolutely necessary) or do it a different way in a macro. This macro is part of a larger macro that processes tens of thousands of documents so doing anything manually isn't an option.
Selection.Find.ClearFormatting
Selection.Find.Replacement.ClearFormatting
With Selection.Find
.Text = ""
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchCase = True
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
.Font.Name = "PPalotina2007"
.Replacement.Font.Name = "Palotina X"
End With
Selection.Find.Execute Replace:=wdReplaceAll
Found this at http://word.mvps.org/faqs/customization/ReplaceAnywhere.htm I should note this only works on the FIRST of each type of Story... There are better code on the link provided for getting to all story ranges.
Sub FindAndReplaceFirstStoryOfEachType()
Dim rngStory As Range
For Each rngStory In ActiveDocument.StoryRanges
With rngStory.Find
.Text = ""
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindContinue
.Format = True
.MatchCase = True
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
.Font.Name = "PPalotina2007"
.Replacement.Font.Name = "Palotina X"
End With
rngStory.Find.Execute Replace:=wdReplaceAll
Next rngStory
End Sub
Thank you Chrismas007 for the link http://word.mvps.org/faqs/customization/ReplaceAnywhere.htm to the "complete answer" based on that link which I'm posting below for anyone else who needs this. It does a search not only on a text string but also on a particular font which it changes.
Sub FindReplaceAnywhere( _
ByVal pOldFontName As String, _
ByVal pNewFontName As String, _
ByVal pFindTxt As String, _
ByVal pReplaceTxt As String)
Dim rngStory As Word.Range
Dim lngJunk As Long
Dim oShp As Shape
'Fix the skipped blank Header/Footer problem
lngJunk = ActiveDocument.Sections(1).Headers(1).Range.StoryType
'Iterate through all story types in the current document
For Each rngStory In ActiveDocument.StoryRanges
'Iterate through all linked stories
Do
SearchAndReplaceInStory rngStory, pOldFontName, pNewFontName, pFindTxt, pReplaceTxt
On Error Resume Next
Select Case rngStory.StoryType
Case 6, 7, 8, 9, 10, 11
If rngStory.ShapeRange.Count > 0 Then
For Each oShp In rngStory.ShapeRange
If oShp.TextFrame.HasText Then
SearchAndReplaceInStory oShp.TextFrame.TextRange, pOldFontName, pNewFontName, pFindTxt, pReplaceTxt
End If
Next
End If
Case Else
'Do Nothing
End Select
On Error GoTo 0
'Get next linked story (if any)
Set rngStory = rngStory.NextStoryRange
Loop Until rngStory Is Nothing
Next
End Sub
Sub SearchAndReplaceInStory( _
ByVal rngStory As Word.Range, _
ByVal FindFontName As String, _
ByVal ReplaceFontName As String, _
ByVal strSearch As String, _
ByVal strReplace As String)
With rngStory.Find
.ClearFormatting
.Replacement.ClearFormatting
.Forward = True
.Wrap = wdFindContinue
.Font.Name = FindFontName
.Replacement.Font.Name = ReplaceFontName
.Text = strSearch
.Replacement.Text = strReplace
.Execute Replace:=wdReplaceAll
End With
End Sub
Thanks Harry Spier, even though I had to modify your code a little - finally it works great!
Sub FindReplaceAnywhere()
Dim pOldFontName As String
Dim pNewFontName As String
Dim rngStory As Word.Range
Dim lngJunk As Long
Dim oShp As Shape
pOldFontName = "FontDoe" 'replace with the font you want to replace
pNewFontName = "Font Dolores" 'replace with the font you really need to have in your doc
'Fix the skipped blank Header/Footer problem
lngJunk = ActiveDocument.Sections(1).Headers(1).Range.StoryType
'Iterate through all story types in the current document
For Each rngStory In ActiveDocument.StoryRanges
'Iterate through all linked stories
Do
SearchAndReplaceInStory rngStory, pOldFontName, pNewFontName, pFindTxt, pReplaceTxt
On Error Resume Next
Select Case rngStory.StoryType
Case 6, 7, 8, 9, 10, 11
If rngStory.ShapeRange.Count > 0 Then
For Each oShp In rngStory.ShapeRange
If oShp.TextFrame.HasText Then
SearchAndReplaceInStory oShp.TextFrame.TextRange, pOldFontName, pNewFontName, pFindTxt, pReplaceTxt
End If
Next
End If
Case Else
'Do Nothing
End Select
On Error GoTo 0
'Get next linked story (if any)
Set rngStory = rngStory.NextStoryRange
Loop Until rngStory Is Nothing
Next
End Sub
Sub SearchAndReplaceInStory( _
ByVal rngStory As Word.Range, _
ByVal FindFontName As String, _
ByVal ReplaceFontName As String, _
ByVal strSearch As String, _
ByVal strReplace As String)
With rngStory.Find
.ClearFormatting
.Replacement.ClearFormatting
.Forward = True
.Wrap = wdFindContinue
.Font.Name = FindFontName
.Replacement.Font.Name = ReplaceFontName
.Text = strSearch
.Replacement.Text = strReplace
.Execute Replace:=wdReplaceAll
End With
End Sub