This Word macro written by Paul Beverley adds a comment to the document and inserts the page and line numbers.
Sub CommentAdd()
' Version 20.02.12
' Add a comment
' Ctrl-Alt-#
attrib1 = "PB: "
attrib2 = "PB: "
postText = ""
keepPaneOpen = False
addPageNum1 = True
addLineNum1 = True
addPageNum2 = True
addLineNum2 = True
highlightTheText = False
textHighlightColour = wdYellow
colourTheText = False
textColour = wdColorBlue
Set rng = Selection.Range
rng.Collapse wdCollapseEnd
rng.MoveEnd , 1
pageNum = rng.Information(wdActiveEndAdjustedPageNumber) ' <<<<<----- This line
lineNum = rng.Information(wdFirstCharacterLineNumber)
If Selection.End <> Selection.Start Then
If Right(Selection, 1) = Chr(32) Then Selection.MoveEnd wdCharacter, -1
Set rng1 = Selection.Range
' Either highlight it ...
myTrack = ActiveDocument.TrackRevisions
ActiveDocument.TrackRevisions = False
If highlightTheText = True Then Selection.Range.HighlightColorIndex _
= textHighlightColour
' And/or change the text colour to red
If colourTheText = True Then Selection.Font.Color = textColour
ActiveDocument.TrackRevisions = myTrack
' Now add the comment
Selection.Comments.Add Range:=Selection.Range
If addPageNum1 = True Then attrib1 = attrib1 & "(p. " & _
pageNum & ") "
If addLineNum1 = True Then attrib1 = attrib1 & "(line " & _
lineNum & ") "
Selection.TypeText Text:=attrib1 & ChrW(8216) & ChrW(8217)
' Move back to between the close and open quotes
Selection.MoveEnd wdCharacter, -1
' 'Paste' in a copy of the selected text
Set rng2 = Selection.Range
rng2.FormattedText = rng1.FormattedText
rng2.Revisions.AcceptAll
rng2.Start = rng2.End - 1
If rng2.Text = Chr(13) Then rng2.Delete
' Move back past the close quote
rng2.Start = rng2.End + 1
If postText > "" Then
rng2.InsertAfter Text:=postText
Else
rng2.InsertAfter Text:=" " & ChrW(8211) & " "
End If
If keepPaneOpen = False Then ActiveWindow.ActivePane.Close
Else
cmntText = attrib2
If addPageNum2 = True Then cmntText = cmntText & _ ' <<<<<----- And this I guess
"(p. " & pageNum & ") "
If addLineNum2 = True Then cmntText = cmntText & _
"(line " & lineNum & ") "
Selection.MoveEnd , 1
Selection.Comments.Add Range:=Selection.Range, Text:=cmntText
End If
End Sub
I have been trying (in Word's VB Editor) to tweak the highlighted line (here followed by '<<<<----- This line') to have the macro get and write the number of the previous header instead of the page number. The idea is that if the user selects a string, the macro looks for the closest header level (h1, h2, h3, etc.) above it and writes its number.
Example:
1 This is header1 > If the selected string happens to be under this header, then write "1"
1.2 This is header2 > If the selected string happens to be under this header, then write "1.2"
1.2.1 This is header3 > If the selected string happens to be under this header, then write "1.2.1"
So far I have tried these two options to replace the bolded line, to no avail (I have renamed "pageNum" to "headerNum"):
' Option 1
headerNum = ActiveDocument.Styles("Heading")
' Option 2
headerNum = Selection.range = Styles("Heading")
What am I doing wrong?
Any help will be highly appreciated!!!
Related
I am trying to create a word document from excel. the document has specific text that doesnt vary with some data being entered from excel sheet which is being entered by means of an array. so whenever this variable data from sheet is entered, the word document adjust the lines as per the length of this variable. I want to keep the non variable part of my text to be sticking to its specific position regardeless of length of varying data being imported from sheets. Iam also struggling with adjusting the sentence length to match with the paper width. can you pls help
Sub ReminderWordDoc(strValue As String)
Dim wdApp As Word.Application
Set wdApp = CreateObject("Word.Application")
With wdApp
.Visible = True
.Activate
.Documents.Add
Dim objVar As Variant
objVar = Split(strValue, "~")
With .Selection
.ParagraphFormat.Alignment = wdAlignParagraphRight
.BoldRun 'Switch Bold on
.Font.Size = 12
.Font.Name = Arial
.Font.Underline = wdUnderlineSingle
.TypeText "IN LIEU OF MSG FORM"
.TypeParagraph 'Enter a new line
.ParagraphFormat.Alignment = wdAlignParagraphLeft
.TypeText "PRIORITY"
.BoldRun 'Switch Bold off
.TypeParagraph
.TypeText "FROM: HQ FORT DTG : 02" & vbCrLf
.TypeText "TO: " + UCase(objVar(1)) + " UNCLAS" & vbCrLf
.TypeText "INFO: " + UCase(objVar(2)) + " " + UCase(objVar(1)) & vbCrLf
.TypeText "--------------------------------------------------------------------------------------------------------------------" & vbCrLf
.TypeText " REMINDER NO 1 (.) COMPLAINT IN R/O " + UCase(objVar(3)) + " " + UCase(objVar(4)) + " " + UCase(objVar(2)) + _
"(.) REF OUR LETTER NO " + UCase(objVar(0)) + _
" DT ___________(___) COMMA _______(____) COMMA _______(____)(.) 'R' OF AS ASKED VIDE OUR LETTER UNDER REF IS STILL AWAITED (.) REQUEST FWD THE SAME BY _______ (___) (.) " & vbCrLf
.TypeText "--------------------------------------------------------------------------------------------------------------------" & vbCrLf
.TypeText "XYZ TELE:27676455 SR MGR" & vbCrLf & vbCrLf & vbCrLf & vbCrLf & vbCrLf
.TypeText "CASE NO: " + UCase(objVar(0)) + " EXEC " & vbCrLf
.TypeText "DATED: " + UCase(Date) + " TOR____H" & vbCrLf
End With
End With
End Sub
Using tables is one way.
Sub ReminderWordDoc(strValue As String)
Dim wdApp As Word.Application
Set wdApp = CreateObject("Word.Application")
Dim wdDoc As Word.Document
With wdApp
.Visible = True
.Activate
End With
Set wdDoc = wdApp.Documents.Add
Dim objVar As Variant
objVar = Split(strValue, "~")
With wdApp.Selection
.ParagraphFormat.Alignment = wdAlignParagraphRight
.BoldRun 'Switch Bold on
.Font.Size = 12
.Font.Name = Arial
.Font.Underline = wdUnderlineSingle
.TypeText "IN LIEU OF MSG FORM"
.TypeParagraph 'Enter a new line
.ParagraphFormat.Alignment = wdAlignParagraphLeft
.TypeText "PRIORITY"
.BoldRun 'Switch Bold off
.TypeParagraph
End With
Dim t1 As Word.Table
Set t1 = wdDoc.Tables.Add(Range:=wdApp.Selection.Range, NumRows:=9, NumColumns:=2, _
DefaultTableBehavior:=wdWord9TableBehavior, _
AutoFitBehavior:=wdAutoFitFixed)
t1.Borders.Enable = False
w = t1.Rows(1).Cells(1).Width * 2
w1 = w * 0.75
w2 = w * 0.25
For x = 1 To 9
With t1.Rows(x)
.Cells(1).Width = w1
.Cells(2).Width = w2
End With
Next x
t1.Rows(1).Cells(1).Range.Text = "FROM: HQ FORT"
t1.Rows(1).Cells(2).Range.Text = "DTG : 02"
t1.Rows(2).Cells(1).Range.Text = "TO: " + UCase(objVar(1))
t1.Rows(2).Cells(2).Range.Text = "UNCLAS"
t1.Rows(3).Cells(1).Range.Text = "INFO: " + UCase(objVar(2))
t1.Rows(3).Cells(2).Range.Text = UCase(objVar(1))
t1.Rows(4).Cells.Merge
t1.Rows(4).Cells(1).Range.Text = String(116, "-")
t1.Rows(5).Cells.Merge
t1.Rows(5).Cells(1).Range.Text = " REMINDER NO 1 (.) COMPLAINT IN R/O " + UCase(objVar(3)) + " " + UCase(objVar(4)) + " " + UCase(objVar(2)) + _
"(.) REF OUR LETTER NO " + UCase(objVar(0)) + _
" DT ___________(___) COMMA _______(____) COMMA _______(____)(.) 'R' OF AS ASKED VIDE OUR LETTER UNDER REF IS STILL AWAITED (.) REQUEST FWD THE SAME BY _______ (___) (.) "
t1.Rows(6).Cells.Merge
t1.Rows(6).Cells(1).Range.Text = String(116, "-")
t1.Rows(7).Cells(1).Split NumRows:=1, NumColumns:=2
t1.Rows(7).Cells(1).Range.Text = "XYZ"
t1.Rows(7).Cells(2).Range.Text = "TELE:27676455"
t1.Rows(7).Cells(3).Range.Text = "SR MGR"
t1.Rows(8).Cells(1).Range.Text = "CASE NO: " + UCase(objVar(0))
t1.Rows(8).Cells(2).Range.Text = "EXEC"
t1.Rows(9).Cells(1).Range.Text = "DATED: " + UCase(Date)
t1.Rows(9).Cells(2).Range.Text = "TOR____H"
End Sub
I'm trying to make the "number of occurrences" either be written in red or in bolded red. Can someone please point me in the right direction. I'm new to coding. This is a word-counter, and when 2+ words are found...it displays the number of words found at the bottom of the word document.
Sub a3()
Dim Word As String
Dim wcount As Integer
Word = InputBox("Search for a word")
If (Word <= "") Then
MsgBox ("Did not enter word")
End If
If (Word > "") Then
wcount = 0
With Selection
.HomeKey Unit:=wdStory
With ActiveDocument.Content.Find
.Text = Word
Do While .Execute
wcount = wcount + 1
Selection.MoveRight
Loop
End With
MsgBox ("The word: '" & Word & "' shows up " & wcount & " times in the document")
End With
End If
If (wcount <= 2) Then
ActiveDocument.Content.InsertAfter Text:=(vbCrLf & "Number occurrences: " & wcount)
Selection.Font.ColorIndex = wdRed
ElseIf (wcount <= 3) Then
ActiveDocument.Content.InsertAfter Text:=(vbCrLf & "Number occurrences: " & wcount)
Selection.Font.ColorIndex = wdRed
Selection.Font.Bold = True
Else
ActiveDocument.Content.InsertAfter Text:=(vbCrLf & "Number occurrences: " & wcount)
Selection.Font.ColorIndex = wdBlack
Selection.Font.Bold = False
End If
End Sub
Working with Word Range objects will help with this. Think of a Range like an invisible selection, except that code can work with multiple Range objects, while there can be only one Selection.
Assign the document's content to a Range, then perform the Find and extension on that. Then the formatting can also be applied to the Range. I've altered (but not tested) the code in the question to demonstrate.
In the last part, where text is written at the end of the document, the Range object is set to the entire document, then collapsed (think of it like pressing the right-arrow key with a selection). Then the new text is assigned to the range and formatting applied. Because the range will contain only the new text, the formatting is applied to that, only.
(Additional notes: I've changed the Word variable name to sWord because "Word" could be misunderstood to mean the Word application. I've also changed the comparison to check whether sWord contains something to Len(sWord) > 0 because the "greater than """ comparison is not guaranteed.)
Sub a3()
Dim sWord As String
Dim wcount As Integer
Dim rng as Word.Range
Set rng = ActiveDocument.Content
sWord = InputBox("Search for a word")
If (sWord <= "") Then
MsgBox ("Did not enter word")
End If
If (Len(sWord) > 0) Then
wcount = 0
With Selection
.HomeKey Unit:=wdStory
With rng.Find
.Text = sWord
Do While .Execute
wcount = wcount + 1
rng.Collapse wdCollapseEnd
Loop
End With
MsgBox ("The word: '" & sWord & "' shows up " & wcount & " times in the document")
End With
End If
Set rng = ActiveDocument.Content
rng.Collapse wdCollapseEnd
If (wcount <= 2) Then
rng.Text = (vbCrLf & "Number occurrences: " & wcount)
rng.Font.ColorIndex = wdRed
ElseIf (wcount <= 3) Then
rng.Text = (vbCrLf & "Number occurrences: " & wcount)
rng.Font.ColorIndex = wdRed
rng.Font.Bold = True
Else
rng.Text = (vbCrLf & "Number occurrences: " & wcount)
rng.Font.ColorIndex = wdBlack
rng.Font.Bold = False
End If
End Sub
There are many ways to do this, some of them are based on a preference for ranges or selections and also the structure of the Find statement. Here is my preference.
Sub a3()
Dim wrd As String
Dim wcount As Integer
Dim rng As Word.Range
wrd = InputBox("Search for a word")
If wrd = vbNullString Then
MsgBox ("Did not enter word")
Exit Sub
End If
Set rng = ActiveDocument.Content
wcount = 0
With rng.Find
.ClearFormatting
.Format = False
.Forward = True
.MatchWholeWord = True
.Text = wrd
.Wrap = wdFindStop
.Execute
Do While .found
wcount = wcount + 1
rng.Collapse Word.WdCollapseDirection.wdCollapseEnd
.Execute
Loop
End With
MsgBox ("The word: " & "" & wrd & "" & " shows up " & wcount & " times in the document")
ActiveDocument.Content.InsertParagraphAfter
Set rng = ActiveDocument.Content
rng.Collapse Word.WdCollapseDirection.wdCollapseEnd
rng.Text = "Number occurrences: " & wcount
If wcount < 3 Then
rng.Font.ColorIndex = wdRed
ElseIf wcount < 4 Then
rng.Font.ColorIndex = wdRed
rng.Font.Bold = True
Else
rng.Font.ColorIndex = wdAuto
rng.Font.Bold = False
End If
End Sub
I am trying to search for occurrences of a particular string in a Word document.
The code should search only after the Table of Contents.
My completed code is below:
Private Sub cmdFindNextAbbr_Click()
Dim myRange As range
'CREATING DICTONARY for Selected Items
If firstClickAbr = True Then
txtNew = ""
abSelIndex = 0
Set abSel = CreateObject("scripting.dictionary")
Set abSelFirstStart = CreateObject("scripting.dictionary")
firstClickAbr = False
iAbbr = 0
For x = 0 To lstAbbreviations.ListCount - 1
If lstAbbreviations.Selected(x) = True Then
If Not abSel.Exists(lstAbbreviations.List(x, 1)) Then
abSel.Add lstAbbreviations.List(x, 0), lstAbbreviations.List(x, 1)
abSelFirstStart.Add lstAbbreviations.List(x, 0), lstAbbreviations.List(x, 5)
End If
End If
Next x
End If
Dim Word, findText As String
Dim chkAbbrLast, fsCountExt, firstOccEnd As Integer
Do While abSelIndex < abSel.count
chkAbbrLast = 0
Set myRange = ActiveDocument.Content
If txtNew <> abSel.keys()(abSelIndex) Then
fnCountAbr = 0
locInteger = abbrTableEnd
End If
firstOccEnd = abSelFirstStart.items()(abSelIndex) + Len(abSel.items()(abSelIndex) & " (" & abSel.keys()(abSelIndex) & ")")
fnCountAbr = fnCountAbr + 1
Word = abSel.keys()(abSelIndex)
'initially search for full text
findText = abSel.items()(abSelIndex)
myRange.Start = locInteger
myRange.Find.ClearFormatting
Do While myRange.Find.Execute( _
findText:=findText, _
MatchCase:=False, _
MatchWholeWord:=True, _
Wrap:=wdFindStop, _
Forward:=True _
)
If Left(myRange.Style, 7) <> "Heading" Then
If abSelFirstStart.items()(abSelIndex) <> myRange.Start Then 'ignore the first occurrence
locInteger = myRange.End
tCount = tCount + 1
'check for full term and abbreviation
fsCountExt = Len(abSel.items()(abSelIndex) & "s (" & abSel.keys()(abSelIndex) & "s)")
myRange.End = myRange.Start + fsCountExt
If InStr(UCase(myRange.Text), UCase(abSel.items()(abSelIndex) & "s (" & abSel.keys()(abSelIndex) & "s)")) > 0 Then
txtNew = abSel.keys()(abSelIndex) & "s"
myRange.Select
Exit Sub
Else
fsCountExt = Len(abSel.items()(abSelIndex) & " (" & abSel.keys()(abSelIndex) & ")")
myRange.End = myRange.Start + fsCountExt
End If
If InStr(UCase(myRange.Text), UCase(abSel.items()(abSelIndex) & " (" & abSel.keys()(abSelIndex) & ")")) > 0 Then
txtNew = abSel.keys()(abSelIndex)
myRange.Select
Exit Sub
End If
'check for full term only
fsCountExt = Len(abSel.items()(abSelIndex) & "s (" & abSel.keys()(abSelIndex) & "s)")
myRange.End = myRange.Start + fsCountExt
If InStr(UCase(myRange.Text), UCase(abSel.items()(abSelIndex) & "s")) > 0 Then
txtNew = abSel.keys()(abSelIndex) & "s"
myRange.Select
Exit Sub
Else
fsCountExt = Len(abSel.items()(abSelIndex))
myRange.End = myRange.Start + fsCountExt
End If
If InStr(UCase(myRange.Text), UCase(abSel.items()(abSelIndex))) > 0 Then
txtNew = abSel.keys()(abSelIndex)
myRange.Select
Exit Sub
End If
End If
End If
chkAbbrLast = chkAbbrLast + 1 ' check to prevent infinite loop
myRange.End = ActiveDocument.Content.End
If chkAbbrLast > 2 Then
Exit Do
End If
Loop
'now search for abbreviation
findText = abSel.keys()(abSelIndex)
chkAbbrLast = 0
myRange.Start = locInteger
myRange.Find.ClearFormatting
Do While myRange.Find.Execute( _
findText:=findText, _
MatchCase:=True, _
MatchWholeWord:=True _
)
If Left(myRange.Style, 7) <> "Heading" And myRange.Start > firstOccEnd Then
If abbIgnoreList.contains(myRange.Start) Then ' skip if match is in ignore list
If abSelIndex = abSel.count - 1 Then
chkAbbrLast = chkAbbrLast + 1 ' check to prevent infinite loop
End If
locInteger = myRange.End
Else
locInteger = myRange.End
tCount = tCount + 1
fsCountExt = Len(abSel.keys()(abSelIndex) & "s")
myRange.End = myRange.Start + fsCountExt
If InStr(UCase(myRange.Text), UCase(abSel.keys()(abSelIndex) & "s")) > 0 Then
txtNew = abSel.keys()(abSelIndex) & "s"
myRange.Select
Exit Sub
Else
fsCountExt = Len(abSel.keys()(abSelIndex))
myRange.End = myRange.Start + fsCountExt
End If
If InStr(UCase(myRange.Text), UCase(abSel.keys()(abSelIndex))) > 0 Then
txtNew = abSel.keys()(abSelIndex)
myRange.Select
Exit Sub
End If
End If
End If
chkAbbrLast = chkAbbrLast + 1 ' check to prevent infinite loop
If chkAbbrLast > 2 Then
Exit Do
End If
myRange.End = ActiveDocument.Content.End
Loop
'loop to next/first item
If abSelIndex <= abSel.count - 1 Then
abSelIndex = abSelIndex + 1
Else
abSelIndex = 0 ' start again at beginning
End If
Loop
MsgBox "No further occurrences found"
End Sub
ToCEnd is 4085.
I am able to find the first result. When I click on a find next button, which calls the same method, I have the below values:
myRange.Start : 18046
myRange.End : 21467
However, after .Find.Execute, I have the below values:
myRange.Start : 18022
myRange.End : 18046
Why does the found text end at the start point I had defined earlier?
The difference between Start and End is the length of my string, 24
EDIT:
I have added the complete code.
What I am doing in the code is finding the text that the user may replace.
The replace is triggered from another button.
In the Find Next button event, I validate a result, store the end of the range to a variable and exit the sub.
On the next click, I am trying to search from the stored location onward.
I updated my code to be like the one at this link, still I have the same behavior.
You apparently want to loop through the found instances. For that you could use code like:
Sub Demo()
Application.ScreenUpdating = False
Dim i As Long
With ActiveDocument.Range
With .Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = InputBox("What is the Text to Find")
.Replacement.Text = ""
.Forward = True
.Wrap = wdFindStop
.Format = False
.MatchCase = False
.MatchWholeWord = False
.MatchWildcards = False
.MatchSoundsLike = False
.MatchAllWordForms = False
.Execute
End With
Do While .Find.Found
i = i + 1
'insert code to do something with whatever's been found here
.Collapse wdCollapseEnd
.Find.Execute
Loop
End With
Application.ScreenUpdating = True
MsgBox i & " instances found."
End Sub
In my document I must add footnotes to every formula i used. For example, I have some text with added Bibliography source
(something like "some very smart text [WrittenBySb]")
and I have formula
(for examle : E = mc2)
And after that formula I must add footnote contains source. I decided to write a macro that add footnote to formula with selected bibliography source:
Sub addFootnoteFromSelection()
Selection.MoveRight Unit:=wdCell 'my formula is in table - first row
'is null. second is actual formula and
'third contains actual formula's number
Selection.MoveRight Unit:=wdCell
Selection.MoveRight Unit:=wdCell
With Selection 'i want footnote mark to be in current
'formula's number
With .FootnoteOptions
.Location = wdBottomOfPage
.NumberingRule = wdRestartContinuous
.StartingNumber = 1
.NumberStyle = wdNoteNumberStyleArabic
End With
.Footnotes.Add Range:=Selection.Range, Reference:=""
End With
Selection.PasteAndFormat (wdFormatOriginalFormatting)
Selection.Style = ActiveDocument.Styles("Numer wzoru")
Selection.ParagraphFormat.Alignment = wdAlignParagraphJustify
End Sub
And that works, but I want to have full source name (like in bibliography field), not a citation mark.
I write a function in vba that returns proper looking and formatted string from Source field:
Function stringFromSource(curField As Source) As String
Set xmlDoc = CreateObject("MSXML2.DOMDocument")
xmlDoc.LoadXML curField.XML
authors = "": title = "": publish = "": city = "": year = "": periodic =
""
'authors
Set surname = xmlDoc.getElementsByTagName("b:Last")
Set name = xmlDoc.getElementsByTagName("b:First")
Dim l As Integer
l = 0
For Each el In surname
If el.Text = "" Then Exit For
authors = authors + (el.Text & " " & name(l).Text & " ")
l = l + 1
Next el
'title
Set titlex = xmlDoc.getElementsByTagName("b:Title")
For Each el In titlex
If el.Text = "" Then Exit For
title = title + (el.Text & " ")
Next el
'publisher
Set pubx = xmlDoc.getElementsByTagName("b:Publisher")
For Each el In pubx
publish = publish + (el.Text & " ")
Next el
'city
Set cityx = xmlDoc.getElementsByTagName("b:City")
If cityx.Length = 0 Then city = city + ("(brak miasta)" & " ")
For Each el In cityx
city = city + (el.Text & " ")
Next el
'year
Set yearx = xmlDoc.getElementsByTagName("b:Year")
If yearx.Length = 0 Then year = year + ("(brak roku wydania)" & " ")
For Each el In yearx
year = year + (el.Text & " ")
Next el
'periodical title
Set periodx = xmlDoc.getElementsByTagName("b:PeriodicalTitle")
For Each el In period
periodic = periodic + (el.Text & " ")
Next el
Dim outputString As String
outputString = author & "- " & title & ", " & publish & periodic & ", " &
year
stringFromSource = outputString
End Function
This function works as expected. But I want to loop through all footnotes and convert it to string using my function:
Sub convertAllFootnotes()
Dim ftn As Footnote
Dim oRng As Range
For Each ftn In ActiveDocument.Footnotes
Set oRng = Selection.Range
oRng.Start = oRng.Start - 1
oRng.End = oRng.End + 1
oRng.Select
oRng.Text = stringFromSource(ftn) 'i don't know how to get source object
'from footnote
Next ftn
End Sub
And that is my problem. I don't know how to get Source object from footnote (that has Source object in it, this is not a static string but copied proper Source field)
I am using regex to find all pattern matches in a Word doc, which I will then manipulate.
The file I'm searching is ~330 pages long and includes copy/pasted emails. My problem is that when I use InStr(startPos, objRange.Text, match.submatches(0)) to find the starting position of each match, the result is actually offset by some amount. For the document in its original state, that offset happened to be 324 characters.
On a hunch, I decided to remove all the hyperlinks in the document to see what that would do. The RemoveHyperlinks sub found and removed 24 hyperlinks, after which the Instr() return value was off by only 20 characters (so that subtracting the magic number matchStart = matchStart - 1 - 20 gives the correct starting position). Obviously I want to avoid all magic numbers, but I cannot figure out where the last 20 characters are coming from.
I tried unlinking all fields, but there weren't any to unlink after the hyperlinks were removed.
Any thoughts on why
matchStart = InStr(startPos, objRange.Text, match.submatches(0))
matchEnd = matchStart + Len(match.submatches(0))
Set subRange0 = objDoc.Range(matchStart, matchEnd)
give me subRange0.Text different from match.submatches(0)? Or where the other hidden characters may be found (to be removed)?
Sub FixHighlightedText()
Dim objDoc As Document
Dim objRange As Range, subRange0 As Range
Dim matchStart As Long, matchEnd As Long, startPos As Long
Dim regex As Object
Dim matches
Set objDoc = ActiveDocument
Set objRange = objDoc.Range(0, objDoc.Content.End)
startPos = 1
Set regex = CreateObject("VBScript.RegExp")
Call RemoveHyperlinks
With regex
.Pattern = "((\([a-zA-Z]*?[-]?Time:.*?\})[a-zA-Z0-9]{0,3})"
.Global = True
End With
If regex.test(objRange.Text) Then
Set matches = regex.Execute(objRange.Text)
Debug.Print "Document has " & matches.Count & " matches"
Debug.Print "Document range is " & objRange.Start & " to " & objRange.End
Debug.Print "FirstIndex = " & matches(0).FirstIndex
For Each match In matches
matchStart = InStr(startPos, objRange.Text, match.submatches(0))
startPos = matchStart + Len(match.submatches(0))
If matchStart > 0 Then
matchStart = matchStart - 1
matchEnd = matchStart + Len(match.submatches(0))
Set subRange0 = objDoc.Range(matchStart, matchEnd)
Debug.Print "Match starts at " & matchStart & " and ends at " & (matchStart + Len(match.submatches(1)))
Debug.Print " match0 text = " & match.submatches(0)
Debug.Print " subrange0 text = " & subRange0.Text
Else
Debug.Print "Match mysteriously not found in text"
End If
Next match
Else
Debug.Print "No regex matches"
End If
End Sub
Sub RemoveHyperlinks()
Dim link, cnt As Long, linkRange As Range, i As Long
cnt = 0
For i = ActiveDocument.Hyperlinks.Count To 1 Step -1
With ActiveDocument.Hyperlinks(i)
.TextToDisplay = .TextToDisplay & " (" & .Address & ")"
Set linkRange = .Range
End With
ActiveDocument.Hyperlinks(i).Delete
With linkRange.Font
.Underline = wdUnderlineNone
.ColorIndex = wdAuto
End With
cnt = cnt + 1
Next i
Debug.Print "Removed " & cnt & " link(s)"
End Sub
Sub RemoveFields()
Dim cnt As Long, i As Long
cnt = 0
For i = ActiveDocument.Fields.Count To 1 Step -1
ActiveDocument.Fields(i).Unlink
cnt = cnt + 1
Next i
Debug.Print "Removed " & cnt & " field(s)"
End Sub
I ended up finding the hint to my answer in the selected answer to this question: vbscript: replace text in activedocument with hyperlink.
Essentially, Instr() does not play well with the WYSIWYG feature of Word, but the Find method will give selections with the proper ranges. No need to remove hyperlinks nor worry about other mysterious, hidden text.
The code would look like:
Sub FixHighlightedText()
Dim objDoc As Document
Dim objRange As Range
Dim startPos As Long
Dim regex As Object
Dim matches
Set objDoc = ActiveDocument
Set objRange = objDoc.Range
startPos = 1
Set regex = CreateObject("VBScript.RegExp")
With regex
.Pattern = "((\([a-zA-Z]*?[-]?Time:.*?\})[a-zA-Z0-9]{0,3})"
.Global = True
End With
If regex.test(objRange.Text) Then
Set matches = regex.Execute(objRange.Text)
Debug.Print "Document has " & matches.Count & " matches"
Debug.Print "Document range is " & objRange.Start & " to " & objRange.End
Debug.Print "FirstIndex = " & matches(0).FirstIndex
For Each match In matches
Set objRange = objDoc.Range(startPos, objDoc.Content.End)
With objRange.Find
.Text = match.submatches(0)
.MatchWholeWord = True
.MatchCase = True
.Wrap = wdFindStop
.Execute
End With
startPos = objRange.End
Debug.Print "Match starts at " & objRange.Start & " and ends at " & objRange.End
Debug.Print " match0 text = " & match.submatches(0)
Debug.Print " subrange text = " & objRange.Text
Next match
Else
Debug.Print "No regex matches"
End If
End Sub