Find and Highlight Text in MS PowerPoint - vba

I used some code from this site to make a macro to do a keyword search on Word docs and highlight the results.
I would like to replicate the effect in PowerPoint.
Here is my code for Word.
Sub HighlightKeywords()
Dim range As range
Dim i As Long
Dim TargetList
TargetList = Array("keyword", "second", "third", "etc") ' array of terms to search for
For i = 0 To UBound(TargetList) ' for the length of the array
Set range = ActiveDocument.range
With range.Find ' find text withing the range "active document"
.Text = TargetList(i) ' that has the words from the array TargetList
.Format = True ' with the same format
.MatchCase = False ' and is case insensitive
.MatchWholeWord = True ' and is not part of a larger word
.MatchAllWordForms = False ' and DO NOT search for all permutations of the word
Do While .Execute(Forward:=True)
range.HighlightColorIndex = wdYellow ' highlight the keywords from the for loop yellow
Loop
End With
Next
End Sub
Here is what I have so far in PowerPoint, it is in no way functional.
Sub HighlightKeywords()
Dim range As range
Dim i As Long
Dim TargetList
TargetList = Array("keyword", "second", "third", "etc") ' array of terms to search for
For Each sld In Application.ActivePresentation.Slides
For Each shp In sld.Shapes
If shp.HasTextFrame Then
Set txtRng = shp.TextFrame.TextRange
For i = 0 To UBound(TargetList) ' for the length of the array
With range.txtRng ' find text withing the range "shape, text frame, text range"
.Text = TargetList(i) ' that has the words from the array TargetList
.Format = True ' with the same format
.MatchCase = False ' and is case insensitive
.MatchWholeWord = True ' and is not part of a larger word
.MatchAllWordForms = False ' and DO NOT search for all permutations of the word
Do While .Execute(Forward:=True)
range.HighlightColorIndex = wdYellow ' highlight the keywords from the for loop yellow
Loop
End With
Next
End Sub
I ended up finding my answer through the MSDN, but it was very close to the answer I selected as correct from what people submitted.
Here is the code I went with:
Sub Keywords()
Dim TargetList
Dim element As Variant
TargetList = Array("First", "Second", "Third", "Etc")
For Each element In TargetList
For Each sld In Application.ActivePresentation.Slides
For Each shp In sld.Shapes
If shp.HasTextFrame Then
Set txtRng = shp.TextFrame.TextRange
Set foundText = txtRng.Find(FindWhat:=element, MatchCase:=False, WholeWords:=True)
Do While Not (foundText Is Nothing)
With foundText
.Font.Bold = True
.Font.Color.RGB = RGB(255, 0, 0)
End With
Loop
End If
Next
Next
Next element
End Sub
Turns out that code worked, but was a performance nightmare. The code I selected as the correct answer below runs much more smoothly. I've adjusted my program to match the answer selected.

AFAIK there is no inbuilt way to highlight the found word with a color. You could go out of the way to create a rectangular shape and place it behind the found text and color it but that is a different ball game altogether.
Here is an example which will search for the text in all slides and then make the found text BOLD, UNDERLINE and ITALICIZED. If you want you can also change the color of the font.
Let's say we have a slide which looks like this
Paste this code in a module and then try it. I have commented the code so that you will not have a problem understanding it.
Option Explicit
Sub HighlightKeywords()
Dim sld As Slide
Dim shp As Shape
Dim txtRng As TextRange, rngFound As TextRange
Dim i As Long, n As Long
Dim TargetList
'~~> Array of terms to search for
TargetList = Array("keyword", "second", "third", "etc")
'~~> Loop through each slide
For Each sld In Application.ActivePresentation.Slides
'~~> Loop through each shape
For Each shp In sld.Shapes
'~~> Check if it has text
If shp.HasTextFrame Then
Set txtRng = shp.TextFrame.TextRange
For i = 0 To UBound(TargetList)
'~~> Find the text
Set rngFound = txtRng.Find(TargetList(i))
'~~~> If found
Do While Not rngFound Is Nothing
'~~> Set the marker so that the next find starts from here
n = rngFound.Start + 1
'~~> Chnage attributes
With rngFound.Font
.Bold = msoTrue
.Underline = msoTrue
.Italic = msoTrue
'~~> Find Next instance
Set rngFound = txtRng.Find(TargetList(i), n)
End With
Loop
Next
End If
Next
Next
End Sub
Final Screenshot

I'd like to extend #Siddharth Rout answer which is good and rather recommended (awarder +1 from me). However, there is possibility to 'highlight' a word (range of words) in PP, too. There is one serious disadvantage of setting highlight- it destroys other font settings. Therefore, if one really need to use highlight than we need to return appropriate font settings afterwards.
Here is an example for single word in single text frame:
Sub Highlight_Word()
Dim startSize, startFont, startColor
With ActivePresentation.Slides(1).Shapes(1).TextFrame2.TextRange.Words(8).Font
'read current state
startSize = .Size
startFont = .Name
startColor = .Fill.ForeColor.RGB
'set highlight
.Highlight.RGB = RGB(223, 223, 223) 'light grey
'return standard parameters
.Size = startSize
.Name = startFont
.Fill.ForeColor.RGB = startColor
End With
End Sub
That kind of solution could be placed somewhere inside of #Siddharth solution.

And if you need to preserve the original text formatting completely, you could:
On finding a shape that includes the target text,
Duplicate the shape
Send the duplicate to the original shape's Z-order
Do the highlighting on the duplicate shape
Apply tags to both the duplicate and original to indicate that they need attention later
e.g.
oOriginalShape.Tags.Add "Hilighting", "Original"
oDupeShape.Tags.Add "Hilighting", "Duplicate"
Set the original shape invisible
Then if you need to reverse the highlighting and restore original formatting, you'd simply loop through all shapes; if the shape has a Hilighting tag = "Original", make it visible. If it has Higlighting tag = "Duplicate", delete it.
The hitch here is that if somebody's edited the highlighted shape, the edits will be lost when you revert. Users would have to be taught to revert, edit, then re=highlight.

Related

Copy/paste highlighted text into new different documents

I use Word documents which are highlighted in a number of colors. What I need is for the VBA to find a highlighted section and paste that text in a new document, find the next highlighted section in a different color and copy that text in a new (different) document). My code does that for all 15 colors available for highlighting in Word. However, even if the color is not present in the text, it will create a new document for that color. So every time I run my code, I get 15 new documents.
I need the code to ignore a highlight color if that color is not present, while still creating new (and different) documents for the colors that are present in the document.
For example, I might get a document that only has highlighted text in blue and green, so I would need two new documents for that. Or I might get a document that has highlighted text in blue, green, yellow and red, so I would need four new focuments, one for each color.
Any ideas on what I need to change?
Sub ExtractHighlightedTextsInSameColor()
Dim objDoc As Document, objDocAdd As Document
Dim objRange As Range
Dim strFindColor As String
Dim highliteColor As Variant
Dim i As Long
highliteColor = Array(wdYellow, wdBlack, wdBlue, wdBrightGreen, wdDarkBlue, wdDarkRed, wdDarkYellow, wdGreen, wdPink, wdRed, wdTeal, wdTurquoise, wdViolet, wdWhite)
Set objDoc = ActiveDocument
For i = LBound(highliteColor) To UBound(highliteColor)
Set objDocAdd = Documents.Add
Set objRange = objDocAdd.Content
objRange.Collapse wdCollapseEnd
objDoc.Activate
Selection.HomeKey unit:=wdStory
With Selection.Find
.ClearFormatting
.Forward = True
.Format = True
.Highlight = True
.Wrap = wdFindStop
.Execute
Do While .Found
If Selection.Range.HighlightColorIndex = highliteColor(i) Then
' the following copies only the highlighted text
objRange.FormattedText = Selection.Range.FormattedText
'if you want the entire paragraph that contains a highlighted text item then use this
' objRange.FormattedText = Selection.Range.Paragraphs(1).Range.FormattedText
Selection.Collapse wdCollapseEndwdYellow
objRange.InsertParagraphAfter
objRange.Collapse wdCollapseEnd
Else
objRange.Collapse wdCollapseEnd
End If
.Execute
Loop
End With
objRange.Collapse wdCollapseEnd
If i < UBound(highliteColor) Then
'added a conditional check so an extra page break is not inserted at end of document
objRange.InsertBreak Word.WdBreakType.wdPageBreak
End If
Next
End Sub
I think this is easier to manage if you split your code up a bit: first collect all of the highlighted ranges, and then process them.
This should be pretty close.
Sub TestDoc()
Dim col As Collection, rng As Range, dict As Object, hc As String
Dim doc As Document, destRng As Range
Set dict = CreateObject("scripting.dictionary") 'for tracking documents vs highlight colors
Set col = HighlightedRanges(ActiveDocument) 'first get all highlighted ranges
For Each rng In col
hc = CStr(rng.HighlightColorInde 'get the highlight color
If Not dict.Exists(hc) Then 'need a new doc for this one?
dict.Add hc, Documents.Add 'add doc and key to highlight color
End If
Set doc = dict(hc) 'get doc for this color
Set destRng = doc.Content 'copy the content over...
destRng.Collapse Direction:=wdCollapseEnd
destRng.InsertParagraphAfter
destRng.Collapse Direction:=wdCollapseEnd
destRng.FormattedText = rng.FormattedText
Next rng
End Sub
'return a collection of all highlighted ranges in `doc`
Function HighlightedRanges(doc As Document) As Collection
Dim rng As Range, col As New Collection
Set rng = doc.Content
With rng.Find
.ClearFormatting
.Forward = True
.Format = True
.Highlight = True
.Wrap = wdFindStop
Do While .Execute
col.Add doc.Range(rng.Start, rng.End) 'clone the range
Loop
End With
Set HighlightedRanges = col 'return all the found ranges
End Function

Keep Picture Ratio in Word File

I add a picture via VBA in the Left Header Cell of a Word document - works fine with the following code. Now I want to keep the ratio of the Picture but want to change the size and I don't know how to do it:
Sub AutoOpen()
Dim dd1 As Document: Set dd1 = ActiveDocument
Dim rng1 As Range, seC As Section, an(2) As Long
Dim rngO As Range, rngAN As Range
Dim strToPict As String
For Each rngO In dd1.StoryRanges
ActiveDocument.ActiveWindow.ActivePane.View.SeekView = wdSeekCurrentPageHeader
If rngO.StoryType = wdEvenPagesHeaderStory Then
Set rng1 = rngO.Duplicate
For Each seC In rng1.Sections
an(0) = seC.Headers(1).Range.InlineShapes(1).Height
an(1) = seC.Headers(1).Range.InlineShapes(1).Width
Set rngAN = seC.Headers(1).Range.InlineShapes(1).Range.Duplicate
seC.Headers(1).Range.InlineShapes(1).Delete
seC.Headers(1).Range.InlineShapes.AddPicture FileName:=strToPict, _
LinkToFile:=False, SaveWithDocument:=True, Range:=rngAN
With seC.Headers(1).Range.InlineShapes(1)
.Height = 50
.LockAspectRatio = True
End With
Next
Dim i As Long
ActiveDocument.Save
'Footer changing'
For i = 1 To ActiveDocument.Sections.Count
With ActiveDocument.Sections(i)
.Footers(wdHeaderFooterPrimary).Range.Text = ActiveDocument.Name + "Text"
End With
Next
End If
Next
End Sub
EDIT: I post the whole code of the Makro.
«I add a picture via VBA in the Left Header Cell of a Word document». There is no such thing as a 'Left Header Cell' in a Word document. The only headers (and footers) Word has are Primary, First Page and Even Pages.
And, as Timothy said, you "really need to learn to use the tools at you fingertips". Moreover, having found LockAspectRatio, a simple web search - if that was really necessary - would show you how to use it.
In any event, since all it seems you're trying to do is to resize the inlineshape and repeat the primary page header, you could use something along the lines of:
Sub AutoOpen()
Application.ScreenUpdating = False
Dim Rng As Range, iShp As InlineShape, Sctn As Section, StrNm As String
With Dialogs(wdDialogInsertPicture)
.Display
StrNm = .Name
End With
With ActiveDocument
If StrNm <> "" Then
Set Rng = .Sections.First.Headers(wdHeaderFooterPrimary).Range.Tables(1).Cell(1, 1).Range
Set iShp = .InlineShapes.AddPicture(FileName:=StrNm, _
LinkToFile:=False, SaveWithDocument:=True, Range:=Rng)
With iShp
.LockAspectRatio = True
.Height = 50
End With
End If
Set Rng = .Sections.First.Footers(wdHeaderFooterPrimary).Range
.Fields.Add Range:=Rng, Type:=wdFieldEmpty, Text:="FILENAME", PreserveFormatting:=False
Rng.InsertAfter vbTab & "Text"
For Each Sctn In .Sections
Sctn.Headers(wdHeaderFooterPrimary).LinkToPrevious = True
Sctn.Footers(wdHeaderFooterPrimary).LinkToPrevious = True
Next
End With
Application.ScreenUpdating = True
End Sub
You really need to learn to use the tools at you fingertips - IntelliSense, the Object Browser, and online help.
Scrolling through the options that IntelliSense gives you, or looking up InlineShape in the Object Browser, you would find LockAspectRatio. If you weren’t sure whether that was what you needed, pressing F1 would take you to the online help.

How to print row of found string?

I'd like to find several strings within Word document and for each string found, I like to print (debug.print for example) the whole row content where the string is found, not the paragraph.
How can I do this? Thanks
Sub FindStrings
Dim StringsArr (1 to 3)
StringsArr = Array("string1","string2","string3")
For i=1 to 3
With
Selection.Find
.ClearFormatting
.Text = Strings(i)
Debug.Print CurrentRow 'here I need help
End With
Next
End Sub
The term Row in Word is used only in the context of a table. I assume the term you mean is Line, as in a line of text.
The Word object model has no concept of "line" (or "page") due to the dynamic layout algorithm: anything the user does, even changing the printer, could change where a line or a page breaks over. Since these things are dynamic, there's no object.
The only context where "line" can be used is in connection with a Selection. For example, it's possible to extend a Selection to the start and/or end of a line. Incorporating this into the code in the question it would look something like:
Sub FindStrings()
Dim StringsArr As Variant
Dim bFound As Boolean
Dim rng As Word.Range
Set rng = ActiveDocument.content
StringsArr = Array("string1", "string2", "string3")
For i = LBound(StringsArr) To UBound(StringsArr)
With rng.Find
.ClearFormatting
.Text = StringsArr(i)
.Wrap = wdFindStop
bFound = .Execute
'extend the selection to the start and end of the current line
Do While bFound
rng.Select
Selection.MoveStart wdLine, -1
Selection.MoveEnd wdLine, 1
Debug.Print Selection.Text
rng.Collapse wdCollapseEnd
bFound = .Execute
Loop
End With
Set rng = ActiveDocument.content
Next
End Sub
Notes
Since it's easier to control when having to loop numerous times, a Range object is used as the basic search object, rather than Selection. The found Range is only selected for the purpose of getting the entire line as these "Move" methods for lines only work on a Selection.
Before the loop can continue, the Range (or, if we were working with a selection, the selection) needs to be "collapsed" so that the code does not search and find the same instance of the search term, again. (This is also the reason for Wrap = wdFindStop).

Find any occurrence of multiple words and change their color and make bold

I am trying to make my macro bring up a search box that allows me to enter as many words as I want, separated by comma, and then find each word in the list in the document and make them bold and blue. I my code isn't working.
I'm at my wits and and this should have been a simple macro to write in 5 minutes. I am new at this, of course.
Sub BlueWords()
Dim blueword As String
Dim numberofwords As Long
Application.ScreenUpdating = False
' Enter words that need to become bold blue words.
blueword = InputBox("Enter items to be found here,seperated by comma: ", "Items to be found")
numberofwords = UBound(Split(blueword, ","))
' Find each item and replace it with new one respectively.
For numberofwords = 0 To numberofwords
With Selection
.HomeKey Unit:=wdStory
With Selection.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = Split(blueword, ",")(numberofwords)
.blueword.Font.Color.RGB = Split(RGB(255, 0, 0), ",")(numberofwords)
.Format = False
.MatchWholeWord = False
End With
Selection.Find.Execute Replace:=wdReplaceAll
End With
Next numberofwords
Application.ScreenUpdating = True
End Sub
I expect it to work, but I think it all goes off the rails where I'm trying to make the code actually perform the bold and blue part. Of course, it won't run.
The below code works like this
startSearch saves the input from the input box as a string, splits it into an array and loops over the individual words. In each loop, it calls findCells.
findCells uses the .Find function to search the selected range (before you start the macro) for cells that contain the word of the current loop. Then it loops over the found range (making sure not to get into an infinite loop) and calls modifyCell.
modifyCell disables the change event and makes the celltext blue and bold.
startSearch:
Sub startSearch()
Dim inputString As String
Dim inputArray() As String
Dim wordsArray() As Variant
Dim selRange As Range
Application.ScreenUpdating = False
' Enter words that need to become bold blue words.
inputString = InputBox("Enter items to be found here,seperated by comma: ", "Items to be found")
inputArray = Split(inputString, ",")
' Create Array out of input.
ReDim wordsArray(LBound(inputArray) To UBound(inputArray))
Dim index As Long
For index = LBound(inputArray) To UBound(inputArray)
wordsArray(index) = inputArray(index)
Next index
' Determine Selection
Set selRange = Selection
' Loop through array/each word and find them in a range (then modify them).
For Each word In wordsArray
Call findCells(selRange, word)
Next word
Application.ScreenUpdating = True
End Sub
findCells:
Private Sub findCells(searchRange, content)
Dim foundCell As Range
Dim firstFound As String
With searchRange
' Find range of cells that contains relevant word
Set foundCell = .Find(What:=content, _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
' If any cells containing the word were found, then modify them one by one
If Not foundCell Is Nothing Then
' Save first found cell, LOOP over found cells, modify them, go to next cell, until back to the first one
firstFound = foundCell.Address
Do
Call modifyCell(foundCell)
Set foundCell = .FindNext(foundCell)
Loop Until foundCell.Address = firstFound
End If
End With
End Sub
modifyCell:
Private Sub modifyCell(TargetCell As Range)
' disable change event while modifying cells
Application.EnableEvents = False
TargetCell.Font.Color = RGB(0, 0, 255)
TargetCell.Font.Bold = True
Application.EnableEvents = True
End Sub
This line of code .blueword.Font.Color.RGB = Split(RGB(255, 0, 0), ",")(numberofwords) will not work.
RGB() will return a number representing a colour. So the Split
returns an array of 1 (index = 0). As a result, your line of code
will cause an 'index out of bounds' error.
.blueword is not a member of Find
.Font.Color.RGB = RGB(0,0,255) should turn the text blue easily
enough!
There are other issues in the code, and you will probably come across other errors.
Instead of using Split so many times, why not save it to an array variable and just loop through the array - so much cleaner!

Word VBA to copy certain highlighted colors and paste in new document with no formatting lost

I have a word document that is 180 pages and uses all the highlight Colors randomly throughout the document. The document has several different formats on it from italics, bullets and underline and different size fonts.
What I am trying to do is filter through the document select all paragraphs that contain a certain color highlight then paste it in a new document keeping all formatting in place. It then loops through again and selects the next color highlight and pastes it in the same new document with a page break in between or just a new document all together. I have been trying to figure this out for 2 days.
I have tried the formulas from this Word VBA copy highlighted text to new document and preserve formatting and other ones on Stack Overflow but none of them preserve all the formatting or one that I found I could only get it to copy the whole document with formatting and paste in but not the selected highlights.
This one does the trick but it removes all formatting and can't figure out how to place page break in.
Sub ExtractHighlightedTextsInSameColor()
Dim objDoc As Document, objDocAdd As Document
Dim objRange As Range
Dim strFindColor As String
Dim highliteColor As Variant
highliteColor = Array(wdYellow, wdTeal)
Set objDoc = ActiveDocument
Set objDocAdd = Documents.Add
objDoc.Activate
For i = LBound(highliteColor) To UBound(highliteColor)
With Selection
.HomeKey Unit:=wdStory
With Selection.Find
.Highlight = True
Do While .Execute
If Selection.Range.HighlightColorIndex = highliteColor(i) Then
Set objRange = Selection.Range
objDocAdd.Range.InsertAfter objRange & vbCr
Selection.Collapse wdCollapseEnd
End If
Loop
End With
End With
Next
End Sub
'This one only copies all text in document and not just highliteColor asked for
Sub HighlightedColor()
Dim objDoc As Document, objDocAdd As Document
Dim objRange As Range
Dim highliteColor As Variant
highliteColor = Array(wdYellow, wdTeal, wdPink)
Set objDoc = ActiveDocument
Set objDocAdd = Documents.Add
objDoc.Activate
For i = LBound(highliteColor) To UBound(highliteColor)
With Selection
.HomeKey Unit:=wdStory
With Selection.Find
.Highlight = True
Do While .Execute
If Selection.Range.HighlightColorIndex = highliteColor(i) Then
Set objRange = Selection.Range.FormattedText
objRange.Collapse wdCollapseEnd
objDocAdd.Content.FormattedText = objRange
End If
Loop
End With
End With
Next
End Sub
I expect the output to copy all text that are a certain highlight color, paste them into a new document preserving all formatting and then page break it. Go back select the next highlight color and paste in document until all colors are gotten.
I've made adjustments to your code based on what I understand you want to do. In some cases I tried to make it a little more readable, for example I removed one of the With methods.
Look closely at the use of FormattedText and how it is transferred from one range to another. And also look at the end of the routine for how a page break is inserted.
Sub ExtractHighlightedTextsInSameColor()
Dim objDoc As Document, objDocAdd As Document
Dim objRange As Range
Dim strFindColor As String
Dim highliteColor As Variant
Dim i As Long
highliteColor = Array(wdYellow, wdTeal)
Set objDoc = ActiveDocument
Set objDocAdd = Documents.Add
Set objRange = objDocAdd.Content
For i = LBound(highliteColor) To UBound(highliteColor)
objDoc.Activate
Selection.HomeKey unit:=wdStory
objRange.Collapse wdCollapseEnd
With Selection.Find
.ClearFormatting
.Forward = True
.Format = True
.Highlight = True
.Wrap = wdFindStop
.Execute
Do While .found
If Selection.Range.HighlightColorIndex = highliteColor(i) Then
' the following copies only the highlighted text
' objRange.FormattedText = Selection.Range.FormattedText
'if you want the entire paragraph that contains a highlighted text item then use this
objRange.FormattedText = Selection.Range.Paragraphs(1).Range.FormattedText
Selection.Collapse wdCollapseEnd
objRange.InsertParagraphAfter
objRange.Collapse wdCollapseEnd
Else
objRange.Collapse wdCollapseEnd
End If
.Execute
Loop
End With
objRange.Collapse wdCollapseEnd
If i < UBound(highliteColor) Then
'added a conditional check so an extra page break is not inserted at end of document
objRange.InsertBreak Word.WdBreakType.wdPageBreak
End If
Next
End Sub