Delete all misspelled words in Microsoft Word document - vba

I have numerous word documents with misspelt words that I'm hoping to batch delete. I've tried both of the solutions mentioned below, but they all seem to fail for me.
https://answers.microsoft.com/en-us/msoffice/forum/all/how-to-remove-all-misspelled-words-in-ms-word-at/608dbb5d-e719-4b5f-b44e-1b0542b66bd7
Sub DeleteSpellingErrors()
Dim rng As word.Range, i As Integer
If Selection.Range.Start = Selection.Range.End Then
Set rng = ActiveDocument.Content
Else
Set rng = Selection.Range
End If
If rng.SpellingErrors.Count > 0 Then
For i = rng.SpellingErrors.Count To 1 Step -1
rng.SpellingErrors(i).Delete
Next
End If
End Sub
https://answers.microsoft.com/en-us/msoffice/forum/all/remove-all-misspelled-words-in-my-word-document/b686c318-c1fc-4d90-9e56-e922bb556abd
Using these macro codes causes my microsoft word to freeze (I'm using a 10th gen intel i7) indefinitely. Despite having waited for hours, there still hasn't been any progress. It seems to me like these codes only work for shorter documents, but because my word docs have more than 200 pages, it seems to freeze. Does anyone have any other code suggestions? Better yet, does anyone have any suggestions that allow me to batch delete misspelt words across multiple word docs? Currently, I am deleting misspelt words one document at a time. Thanks for any help!

Try if this code snippet is a bit faster:
Sub DeleteSpellingErrors()
Dim cnt As Long
Dim cur As Range
Dim doc As Document
Set doc = ActiveDocument
cnt = doc.Range.SpellingErrors.Count
Set cur = doc.GoTo(What:=wdGoToSpellingError, Which:=wdGoToFirst)
For i = 1 To cnt
cur.Select
cur.Delete
Debug.Print cnt & " " & i
Set cur = doc.GoTo(What:=wdGoToSpellingError, Which:=wdGoToNext)
DoEvents
Next
End Sub
Most probably you will have to re-run the procedure two or three times as I see that SpellingErrors.Count is not exact.
This re-run can be avoided with this other coding:
Sub DeleteSpellingErrors()
Dim cnt, i As Long
Dim cur, Last As Range
Dim doc As Document
Set doc = ActiveDocument
cnt = doc.Range.SpellingErrors.Count
Set cur = doc.GoTo(What:=wdGoToSpellingError, Which:=wdGoToFirst)
Set Last = doc.GoTo(What:=wdGoToSpellingError, Which:=wdGoToLast)
i = 1
Do While cur <> Last
cur.Select
cur.Delete
Debug.Print cnt & " " & i
Set cur = doc.GoTo(What:=wdGoToSpellingError, Which:=wdGoToNext)
DoEvents
i = i + 1
Loop
End Sub
For testing purposes the document consisted of 107 pages with more than 3000 spelling errors and it took few minutes (about 3 or 4) of execution.
This is another version, that needs just one run to delete all the spelling errors, for Graham Mayor's add-in:
Function DeleteSpellingErrors(doc As Document) As Boolean
Dim cnt, i As Long
Dim cur, Last As Range
If doc Is Nothing Then
Set doc = Application.ActiveDocument
End If
Do
cnt = doc.Range.SpellingErrors.Count
If cnt <= 0 Then Exit Do
Set Last = doc.GoTo(What:=wdGoToSpellingError, Which:=wdGoToLast)
Set cur = doc.GoTo(What:=wdGoToSpellingError, Which:=wdGoToFirst)
For i = 1 To cnt
cur.Select
cur.Delete
'Debug.Print cnt & " " & i
Set cur = doc.GoTo(What:=wdGoToSpellingError, Which:=wdGoToNext)
DoEvents
i = i + 1
Next
Loop
DeleteSpellingErrors = True
End Function

Your code runs fine on my PC with a document that has 350 spelling errors.
If you have a 200+ page document it would be better to disable screen updating whilst your macro runs. I would also add a 'doevents' statement to the for loop so that at least the CTRL Break will halt the program. Initially you may also want to debug.print the count of errors to see how the macro is progressing.
Option Explicit
Sub DeleteSpellingErrors()
Dim rng As Word.Range
Dim i As Long
If Selection.Range.Start = Selection.Range.End Then
Set rng = ActiveDocument.StoryRanges(wdMainTextStory)
Else
Set rng = Selection.Range
End If
If rng.SpellingErrors.Count > 0 Then
Application.ScreenUpdating = False
Debug.Print "Total errors = ", rng.SpellingErrors.Count ' for debugging only
For i = rng.SpellingErrors.Count To 1 Step -1
DoEvents
rng.SpellingErrors.Item(i).Delete
Debug.Print i, rng.SpellingErrors.Count ' for debug only. Note Count doesn't change
Next
Application.ScreenUpdating = True
Application.ScreenRefresh
End If
End Sub

Related

Adding a new word to each subsequent cell in Word VBA

I have been working on this code that takes misspelled words from a document and then turns them into a table with all the misspelled words on one column. Then the words are spellchecked and the corrections appear on the other column. My code does everything that I want it to, however only the first word appears on each cell. What am I doing wrong?
Sub SuperSpellCheck()
Dim doc1 As Document
Dim doc2 As Document
Dim tb As Table
Set doc1 = ActiveDocument
Set doc2 = Documents.Add
doc1.Activate
Dim badw As Range
Dim rng As Range
Dim sugg As SpellingSuggestions
Dim sug As Variant
err = doc1.SpellingErrors.Count
For Each badw In doc1.SpellingErrors
doc2.Range.InsertAfter badw & vbCr
Next
doc2.Activate
Set tb = ActiveDocument.Content.ConvertToTable(Separator:=wdSeparateByParagraphs, NumColumns:=1,
NumRows:=ActiveDocument.SpellingErrors.Count, AutoFitBehavior:=wdAutoFitFixed)
With tb
.Style = "Table Grid"
.ApplyStyleHeadingRows = True
.ApplyStyleLastRow = False
.ApplyStyleFirstColumn = True
.ApplyStyleLastColumn = False
.Columns.Add
.PreferredWidthType = wdPreferredWidthPercent
.PreferredWidth = 100
End With
err2 = ActiveDocument.SpellingErrors.Count
i = 1
Set sugg = doc2.Range.GetSpellingSuggestions
For Each rng In doc2.Range.SpellingErrors
With rng
If sugg.Count > 0 Then
Set sug = .GetSpellingSuggestions
tb.Cell(i, 2).Range.InsertAfter sug(1)
End If
End With
Next
End Sub
Not connected to your problem but you need to change these lines
Err = doc1.SpellingErrors.Count
err2 = ActiveDocument.SpellingErrors.Count
To:
Dim errors1 as Long, dim errors2 as Long
errors1 = doc1.SpellingErrors.Count
errors2 = doc2.SpellingErrors.Count
Err is an object in VBA that holds the errors generated by your code. You also haven't declared those variables. Add Option Explicit at the very top of your code module and you will be alerted to any undeclared variables. To turn this on automatically in future go to Tools | Options | Editor and ensure that Require Variable Declaration is checked.
I would change
Dim sugg As SpellingSuggestions
Dim sug As Variant
to
Dim docSugg As SpellingSuggestions
Dim rngSugg As SpellingSuggestions
Dim sug As SpellingSuggestion
This will make it clearer what each of these represents.
SpellingSuggestions is a collection of SpellingSuggestion objects so you can use sug to loop through the collection.
i = 1
Set sugg = doc2.Range.GetSpellingSuggestions
For Each rng In doc2.Range.SpellingErrors
With rng
If sugg.Count > 0 Then
Set sug = .GetSpellingSuggestions
tb.Cell(i, 2).Range.InsertAfter sug(1)
End If
End With
Next
In this block of code you start off by setting the undeclared variable i to a value of 1, but you don't then increase that value. This will result in all your spelling suggestions being inserted in the same cell. Also, when you insert the spelling suggestion you only ever insert the first one as you don't have a means of looping through them. So I would rewrite this as:
i = 1
Set docSugg = doc2.Range.GetSpellingSuggestions
For Each rng In doc2.Range.SpellingErrors
With rng
If docSugg.Count > 0 Then
Set rngSugg = .GetSpellingSuggestions
For Each sug In rngSugg
tb.Cell(i, 2).Range.InsertAfter sug
Next
End If
End With
i = i + 1
Next
EDIT: If you only want the first suggested spelling then use:
i = 1
Set docSugg = doc2.Range.GetSpellingSuggestions
For Each rng In doc2.Range.SpellingErrors
With rng
If docSugg.Count > 0 Then
Set rngSugg = .GetSpellingSuggestions
tb.Cell(i, 2).Range.InsertAfter rngSugg(1)
End If
End With
i = i + 1
Next

Word VBA - How to edit the text in cells in tables in Header

I dont do much word work, but I need to amend the address in the headers of a batch of letters. The address is held in tables in the headers. I also need to retain the formatting.
Below is the code I have ended up with - am I getting close?
Dim doc As Word.Document
Dim hf As Word.HeaderFooter
Dim lr As ListRow
Dim updated As Boolean
Dim tableCount As Integer
Dim t As Integer
Dim c As Cell
Set doc = wd.Documents.Open(Filename:=fi.Path, ReadOnly:=False)
For Each hf In doc.Sections(1).Headers()
tableCount = hf.Range.Tables.Count
For t = 1 To tableCount
For Each c In hf.Range.Tables(t).Range.Cells
If InStr(1, c.Range.Text, AddLOneOld) > 0 Then
updated = True
c.Range.Text = Replace(c.Range.Text, AddLOneOld, AddLOneNew)
End If
If InStr(1, c.Range.Text, AddLTwoOld) > 0 Then
updated = True
c.Range.Text = Replace(c.Range.Text, AddLTwoOld, AddLTwoNew)
End If
If InStr(1, c.Range.Text, AddLThreeOld) > 0 Then
updated = True
c.Range.Text = Replace(c.Range.Text, AddLThreeOld, AddLThreeNew)
End If
Next c
Next t
Next hf
If updated Then
Set lr = filesUpdated.ListRows.Add
lr.Range(1, 1) = fi.Path
doc.Save
End If
doc.Close False
This is the nearest I have got it to go as far as running, but all it does now is produce the error
"Microsoft Excel is waiting for another application to complete an OLE action"
Thanks

How to get the row number of the second (or the last) occurence of a word in Excel?

I am copying some content from a webpage in an excel sheet, and searching for a particular word and then fetching the row number of the record. The copied content is from an HTML page and is pasted in a table format in excel. At times, the word is mentioned twice in the table and I want to get the row number of the record which is in the last. I have code for getting the row number at the first time, but I am not able to figure out the row number of the last record. When I am fetching the row number value, in the variable "row_no", then I need the row number of the record which is displayed in last. How can I get that?
Please find below the code which I have written:
Set ie = New InternetExplorerMedium
ie.Visible = True
ws.Activate
strHTML = URL
ie.navigate strHTML
Do While ie.Busy Or ie.READYSTATE <> 4
DoEvents
Loop
Set oXMLHTTP = CreateObject("MSXML2.ServerXMLHTTP")
Set objCollection = ie.document.getElementsByTagName("input")
Set tables = ie.document.getElementsByTagName("Table")
msg = ie.document.Body.innerHTML
If InStr(msg, "Word1") = 0 Then
Else
For x = 0 To tables.Length
If tables(x).innerText Like "*Word1*" Then
found = True
If x > 0 Then
Set clipboard = New MSForms.DataObject
clipboard.SetText tables(x).outerHTML
clipboard.PutInClipboard
ws.Activate
ws.Range("A1").PasteSpecial
Result = Application.WorksheetFunction.CountIf(Range("E2:E10000"), "End Analysis")
MsgBox Result
If Result> 1 Then
'get the row number
row_no = Worksheets("temp").Range("E2:E10000").Find("End Analysis", lookat:=xlWhole).Row
End If
End If
Next x
End If
Like this
Option Explicit
Public Sub test()
Dim found As Range
With Worksheets("temp").Range("E2:E10000")
Set found = .Find(what:="End Analysis", searchorder:=xlByColumns, searchdirection:=xlPrevious)
If Not found Is Nothing Then Debug.Print found.Row
End With
End Sub
Which looking like yours would be more:
Dim found As Range, row_no As Long
If Result > 1 Then
Set found = Worksheets("temp").Range("E2:E10000").Find(what:="End Analysis", searchorder:=xlByColumns, searchdirection:=xlPrevious)
If Not found Is Nothing Then
row_no = found.Row
Else
MsgBox "Not Found"
End If
End If

How to modify text in Powerpoint via Excel VBA without changing style

I am trying to replace a set of tags in the text of a powerpoint slide from Excel using VBA. I can get the slide text as follows:
Dim txt as String
txt = pptSlide.Shapes(jj).TextFrame.TextRange.Characters.text
I then run through replacing my tags with the requested values. However when I set do
pptSlide.Shapes(jj).TextFrame.TextRange.Characters.text = txt
Problem: All the formatting which the user has set up in the text box is lost.
Background:
The shape object is msoPlaceHolder and contains a range of text styles including bullet points with tags which should be replaced with numbers for instance. The VBA should be unaware of this formatting and need only concern itself with the text replacement.
Can anyone tell me on how to modify the text while keeping the style set up by the user.
Thanks.
Am using Office 2010 if that is helpful.
The solution by Krause is close but the FIND method returns a TextRange object that has to be checked. Here is a complete subroutine that replaces FROM-string with TO-string in an entire presentation, and DOESN'T mess up the formatting!
Sub Replace_in_Shapes_and_Tables(pPPTFile As Presentation, sFromStr As String, sToStr As String)
Dim sld As Slide
Dim shp As Shape
Dim i As Long
Dim j As Long
Dim m As Long
Dim trFoundText As TextRange
On Error GoTo Replace_in_Shapes_and_Tables_Error
For Each sld In pPPTFile.Slides
For Each shp In sld.Shapes
If shp.HasTextFrame Then
If shp.TextFrame.HasText Then ' only perform action on shape if it contains the target string
Set trFoundText = shp.TextFrame.TextRange.Find(sFromStr)
If Not (trFoundText Is Nothing) Then
m = shp.TextFrame.TextRange.Find(sFromStr).Characters.Start
shp.TextFrame.TextRange.Characters(m).InsertBefore (sToStr)
shp.TextFrame.TextRange.Find(sFromStr).Delete
End If
End If
End If
If shp.HasTable Then
For i = 1 To shp.Table.Rows.Count
For j = 1 To shp.Table.Columns.Count
Set trFoundText = shp.Table.Rows.Item(i).Cells(j).Shape.TextFrame.TextRange.Find(sFromStr)
If Not (trFoundText Is Nothing) Then
m = shp.Table.Rows.Item(i).Cells(j).Shape.TextFrame.TextRange.Find(sFromStr).Characters.Start
shp.Table.Rows.Item(i).Cells(j).Shape.TextFrame.TextRange.Characters(m).InsertBefore (sToStr)
shp.Table.Rows.Item(i).Cells(j).Shape.TextFrame.TextRange.Find(sFromStr).Delete
End If
Next j
Next i
End If
Next shp
Next sld
For Each shp In pPPTFile.SlideMaster.Shapes
If shp.HasTextFrame Then
If shp.TextFrame.HasText Then
Set trFoundText = shp.TextFrame.TextRange.Find(sFromStr)
If Not (trFoundText Is Nothing) Then
m = shp.TextFrame.TextRange.Find(sFromStr).Characters.Start
shp.TextFrame.TextRange.Characters(m).InsertBefore (sToStr)
shp.TextFrame.TextRange.Find(sFromStr).Delete
End If
End If
End If
If shp.HasTable Then
For i = 1 To shp.Table.Rows.Count
For j = 1 To shp.Table.Columns.Count
Set trFoundText = shp.Table.Rows.Item(i).Cells(j).Shape.TextFrame.TextRange.Find(sFromStr)
If Not (trFoundText Is Nothing) Then
m = shp.Table.Rows.Item(i).Cells(j).Shape.TextFrame.TextRange.Find(sFromStr).Characters.Start
shp.Table.Rows.Item(i).Cells(j).Shape.TextFrame.TextRange.Characters(m).InsertBefore (sToStr)
shp.Table.Rows.Item(i).Cells(j).Shape.TextFrame.TextRange.Find(sFromStr).Delete
End If
Next j
Next i
End If
Next shp
On Error GoTo 0
Exit Sub
Replace_in_Shapes_and_Tables_Error:
MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure Replace_in_Shapes_and_Tables of Module modA_Code"
Resume
End Sub
While what Steve Rindsberg said is true I think I have come up with a decent workaround. It is by no means pretty but it gets the job done without sacrificing the formatting. It uses Find functions and Error Controlling for any text box that doesn't have the variable you are looking to change out.
i = 1
Set oPs = oPa.ActivePresentation.Slides(oPa.ActivePresentation.Slides.Count)
j = 1
Do Until i > oPa.ActivePresentation.Slides.Count
oPa.ActivePresentation.Slides(i).Select
Do Until j > oPa.ActivePresentation.Slides(i).Shapes.Count
If oPa.ActivePresentation.Slides(i).Shapes(j).HasTextFrame Then
If oPa.ActivePresentation.Slides(i).Shapes(j).TextFrame.HasText Then
On Error GoTo Err1
If oPa.ActivePresentation.Slides(i).Shapes(j).TextFrame.TextRange.Find("[specific search term]") = "[specific search term]" Then
m = oPa.ActivePresentation.Slides(i).Shapes(j).TextFrame.TextRange.Find("[specific search term]").Characters.Start
oPa.ActivePresentation.Slides(i).Shapes(j).TextFrame.TextRange.Characters(m).InsertBefore ([replace term])
oPa.ActivePresentation.Slides(i).Shapes(j).TextFrame.TextRange.Find("[specific search term]").Delete
ExitHere:
End If
End If
End If
j = j + 1
Loop
j = 1
i = i + 1
Loop
Exit Sub
Err1:
Resume ExitHere
End Sub
Hope this helps!
I found the solution using the code below. It edits the notes by replacing "string to replace" with "new string". This example is not iterative and will only replace the first occurrence but it should be fairly easy to make it iterative.
$PowerpointFile = "C:\Users\username\Documents\test.pptx"
$Powerpoint = New-Object -ComObject powerpoint.application
$ppt = $Powerpoint.presentations.open($PowerpointFile, 2, $True, $False)
$ppt.Slides[3].Shapes[2].TextFrame.TextRange.Text
$ppt.Slides[3].NotesPage.Shapes[2].TextFrame.TextRange.Text
foreach($slide in $ppt.slides){
$TextRange = $slide.NotesPage.Shapes[2].TextFrame.TextRange
$find = $TextRange.Find('string to replace').Start
$TextRange.Find('string to replace').Delete()
$TextRange.Characters($find).InsertBefore('new string')
$TextRange.Text
}
$ppt.SaveAs("C:\Users\username\Documents\test2.pptx")
$Powerpoint.Quit()

VBA: error 91 Object variable or with block variable not set

I faced a weird issue of VBA error 91. I saw many other people have this problem because they didn't use keyword "Set" for object, whereas that is not my case.
Following is my code:
Dim eventWS As Worksheet
Set eventWS = Worksheets("Event Sheet")
Dim eventRange As Range
Set eventRange = eventWS.Columns("A:A").Find(240, , xlValues, xlWhole)
If Not eventRange Is Nothing Then
Dim eventFirstAddress As String
eventFirstAddress = eventRange.Address
Do
If eventWS.Range("L" & eventRange.Row).Value = busId Then
If commuter = True Then
Count = Count + Affected(eventWS.Range("Q" & eventRange.Row).Value)
Else
Count = Count + 1
End If
End If
MsgBox("Before call move next: " & eventRange.Row )
Set eventRange = eventWS.Columns("A:A").FindNext(eventRange)
MsgBox("After call move next: " & eventRange.Row )
Loop While Not eventRange Is Nothing And eventRange.Address <> eventFirstAddress
End If
Affected() is a function I can call to do internal processing. And if I removed this "Count = Count + Affected(....)", the code was working fine. If I added it, "Loop While " would throw error 91. If I added a message box to print out the row number before and after moving eventRange, it turned out that "MsgBox("After call move next: " & eventRange.Row)" would throw error 91.
Hence, I'm confuse whether the issue is caused by the internal function or the eventRange now. Hope someone can point my mistakes out. Thank you very much.
Following are the codes of internal function:
Function Affected(markerId As Integer) As Integer
'initialized return value'
AffectedCoummters = 0
'get total financial sheets'
Dim totalFinancial As Integer
totalFinancial = 0
For Each ws In Worksheets
If InStr(ws.Name, "Financial") > 0 Then
totalFinancial = totalFinancial + 1
End If
Next
Dim i As Integer
'run through all financial sheets'
For i = 1 To totalFinancial
'get current financial sheet'
Dim financialWS As Worksheet
Set financialWS = Worksheets("Financial Sheet" & i)
'get total rows of current operation sheet'
Dim rowSize As Long
rowSize = financialWS.Range("A" & financialWS.Rows.Count).End(xlUp).Row
'if reach the maximum number of rows, the value will be 1'
'reInitialize rowSize based on version of Excel'
If rowSize = 1 Then
If Application.Version = "12.0" Then
'MsgBox ("You are using Excel 2007")'
If InStr(ThisWorkbook.Name, ".xlsx") > 0 Then
rowSize = 1048576
Else
'compatible mode'
rowSize = 65536
End If
ElseIf Application.Version = "11.0" Then
'MsgBox ("You are using Excel 2003")'
rowSize = 65536
End If
End If
'filter by marker id first inside current financial sheet'
Dim findMarker As Range
Set findMarker = financialWS.Columns("K:K").Find(markerId, , xlValues, xlWhole)
'if found any given marker id'
If Not findMarker Is Nothing Then
Dim firstAddress As String
firstAddress = findMarker.Address
'check all matched marker id'
Do
AffectedCommuters = AffectedCommuters + financialWS.Range("O" & findMarker.Row).Value
'move to next'
Set findMarker = financialWS.Columns("K:K").FindNext(findMarker)
Loop While Not findMarker Is Nothing And findMarker.Address <> firstAddress
End If
Next i
End Function
Sorry I dont have enough rep to comment so I have to answer here :(
Just want to say that although it is standard procedure to use
Loop While Not eventRange Is Nothing And eventRange.Address <> eventFirstAddress
in this type of procedure, if eventRange is actually Nothing, the line will throw Error 91, because eventRange.address does not exists. What this means is that once you have found something, you can't modify the row in such a way that it will not be found again using .findnext.
After you exit the do...loop, you can modifiy the range to suit...
Perhaps you want to use an array to hold all the rows from your .find...findnext results, and then manipulate them after the Do...loop