Inserting text while iterating by character - vba

I'm going through a text character by character.
With this Sub.
Believe me it works.
Sub Recorrida2()
prepararVariables
For Each parrf In ActiveDocument.Paragraphs
DoEvents
Dim c As Integer
c = 1
For Each car In parrf.Range.Characters
If cargaAPatron(car) Then
MsgBox "lleno ok"
Selection.TypeText ("ENTER")
lleno = 1
End If
Next 'parrafo
Next
End Sub
My problem is that I want to insert a text when I detect a pattern.
This way it does not work
Selection.TypeText ("ENTER")
Because it inserts the "ENTER" at the beginning of the text, instead of inserting it in the place of detection.
What am I doing wrong?
Thanks in advance!

Word's Character object is also a Range object. So the following should work:
car.InsertParagraphAfter
Generally, working with the Selection object should be avoided when automating Word. There are a very few things for which it's necessary, but writing text to a document is not one of them...
If, instead, the paragraph mark should be inserted a number of characters before the target Range (car), you can extend the start of the Range using the MoveStart method:
car.MoveStart wdCharacter, -n
car.InsertParagraphBefore
Where n is the number of characters you want to go backwards.

Related

Word VBA - format and re-link footnotes and endnotes in hyperlinks

I need to find footnote and endnote marks (in the body, to format them and re-link them) etc, in a macro I have written to clean up documents imported from the web.
My macro works fine, except where part of the find string is hyperlink display text and part is not.
Searching for ([a-z])([0-9]) (to locate endnote marks in the body, as they are not formatted as endnotes) does not find them because [0-9] is hyperlink display text and [a-z] is not.
My question here: how to search for [normal string] adjacent to [hyperlink display text string]?
If found, the next step is how to replace the hyperlink display text (format endnotes superscript, delete the para mark) keeping it in the hyperlink (I can work on that - maybe the subject of another question?)
You could, of course, simply convert your hyperlinks to superscripted numbers then use whatever other code you have to replace the superscripted numbers with endnotes.
Sub Demo()
Dim h As Long
With ActiveDocument
For h = .Hyperlinks.Count To 1 Step -1
With .Hyperlinks(h)
If IsNumeric(.TextToDisplay) = True Then
With .Range
If .Characters.First.Previous Like "[a-z]" Then
.Fields.Unlink
.Font.Reset
.Font.Superscript = True
End If
End With
End If
End With
Next
End With
End Sub
Depending on what you're doing, you could simply insert your endnoting code into the inner If ... End If construct in place of the font manipulations.

How do I extract hidden text after Table of Contents in Microsoft Word using VBA?

I have a requirement where I have to extract two lines of text just after the Table of Contents in a Word Document. The text is hidden and added just for use in VBA.
I did some research and found out that I can extract text from a word Document using the Range object and so I tried hardcoding it and I got the value I wanted by using
MsgBox ActiveDocument.Paragraphs(43).Range.Text
Now this code can work for a specific document but for a new document I don't know where the Table of Contents will end and the exact line number from where I have to extract.
Any help is appreciated and thanks in advance.
This is slightly tricky because field objects don't have a range so you have to seek the range of the Result of the field. Once you have the result range, you can then get the paragraph range in which it sits, and then, the range of the next paragraph and then when you have that, the text of that paragraph. Sounds complicated but in practise is quite simple when you understand how the Word object model works.
Sub Test()
Debug.Print GetTextAfterToc
End Sub
Public Function GetTextAfterToc() As String
Dim myfield As Word.Field
For Each myfield In ActiveDocument.StoryRanges(wdMainTextStory).Fields
If myfield.Type = wdFieldTOC Then
GetTextAfterToc = myfield.Result.Paragraphs(1).Range.Next(unit:=wdParagraph, Count:=1).Text
Exit Function
End If
Next
End Function
I found an answer to my question. I am posting it to help someone else with a similar question. Thanks to u/slang4201 for the answer.
This will select the two lines immediately following a TOC.
Dim myField As Field
For Each myField In ActiveDocument.Fields
If myField.Type = wdFieldTOC Then
myField.Select
Selection.Collapse wdCollapseEnd
Selection.MoveDown unit:=wdLine, Count:=2, Extend:=True
MsgBox (Selection.Range)
Exit For
End If
Next myField
End Sub
However, if the lines you are looking for are hidden, they WILL NOT be selected and found unless you unhide them first.
ActiveDocument.Range.Font.Hidden = False
Hope this helps, and good luck!

Word VBA - Ignore punctuation while counting/selecting X number of "Words" or "Sentences"

Does anyone know if there is a VBA statement to recognize successive adjacent characters as "words" rather than counting each and every non-alpha character (as a word) along the way?
My research indicates that most people who want to ignore punctuation and/or non-alpha characters are focused on trimming or deselecting the characters. I want to retain these characters, but I also want the non-alpha character ignored while the macro is determining how many "words" to select.
Consider the following examples of "visual" words versus "Word words":
Ms. Smith-Harris (visually 2; Word 5)
9:00 a.m. (visually 2; Word 7)
and/or (visually 1, Word 3)
I have hundreds of macros that I access easily (with mnemonic shortcuts) via a text expander, among them dozens of macros that will select and perform an action (e.g., highlight, bold, uppercase, italicize, delete, etc.) on varying amounts of text to my left or right - something I do quite often in my transcription and editing work.
So, for example, my cursor might be to the right of Ms. Smith-Harris, and perhaps I want to highlight it or make it uppercase or delete it. It's much easier and faster for my eyes to quickly assess that string of characters as 2 "words" (of adjacent text) rather than stop and perform the tedious task of counting all the disruptive non-alpha characters that Word counts as "words" when selecting text, to figure out that it's 5 "words" by Word standards.
Although the text expander itself is capable of performing all the functions I might want to do to the text directly from within the program, it's much faster and more efficient to have it instead invoke a Word macro to perform the function, especially since the macro can intelligently trim unwanted spaces from the selection
But alas, if I run, for example, the Highlight2PreviousWords macro (shown below) while my cursor is to the right of Ms. Smith-Harris, it would only highlight "-Harris."
Here are two examples of my "select X number of words" macros:
Sub h1lw_HighlightPrevious1Word()
Selection.MoveLeft Unit:=wdWord, Count:=1, Extend:=wdExtend
Dim oRng As Word.Range
Set oRng = Selection.Words(1)
Do While oRng.Characters.Last = " "
oRng.MoveEnd wdCharacter, -1
Loop
oRng.Select
Selection.Range.HighlightColorIndex = wdYellow
Selection.Collapse Direction:=wdCollapseEnd
End Sub
Sub h2lw_HighlightPrevious2Words()
'
Selection.MoveLeft Unit:=wdWord, Count:=2, Extend:=wdExtend
Dim oRng As Range
Set oRng = Selection.Range
Do While oRng.Characters.Last = " "
oRng.MoveEnd wdCharacter, -1
Loop
oRng.Select
Selection.Range.HighlightColorIndex = wdYellow
Selection.Collapse Direction:=wdCollapseEnd
End Sub
All of my "select some words and then do something" macros look the same, except for the "do something" part, of course. So all of the "select 1 word to the left and do something" macros look like the first one, and all of the "select 2+ words to the left and do something" look like the second one. I usually don't go beyond 5 words because in the time it takes to visually count beyond that, I could just manually select the text.
So the macros work great for straight-text words, but not so great when other characters are in the mix. I'd love to be able to ignore the non-alpha characters and define all strings of adjacent text as "words."
Does anyone know of a way to accomplish this?
Two tips first:
To get real information about number of words you could use something like Range.ComputeStatistics() method
To select required part of your document you can trace starting and ending characters which defines range in this way ActiveDocument.Range(startingPosition, EndingPosition)
Here is your first subroutine improved. It could be not 100% of what you need but I believe it is a good starting point (tested with Ms. Smith-Harris :) ).
Sub h1lw_HighlightPrevious1Word()
Dim curSection As Range
Dim curEnd As Long
curEnd = Selection.End
Dim newStart As Long
Do
newStart = Selection.Start
Selection.MoveLeft Unit:=WdUnits.wdWord, _
Count:=1
Set curSection = ActiveDocument.Range(Selection.Start, curEnd)
Debug.Print curSection.Text 'test
Loop While curSection.ComputeStatistics(wdStatisticWords) < 2
Set curSection = ActiveDocument.Range(newStart, curEnd)
Debug.Print curSection.Text 'test
'removing ending spaces
Do While curSection.Characters.Last = " "
Set curSection = ActiveDocument.Range(curSection.Start, curSection.End - 1)
Loop
With curSection
.HighlightColorIndex = wdYellow
ActiveDocument.Range(.End, .End).Select
End With
End Sub
Ps. I believe there is a shorter solution but I could not figure it out. I think one could use RegEx here as an alternative.

Modifying a table cell's .Range.Text removes any attached comments

I've written a small VBA program to remove trailing whitespace from MS Word table cells. It iterates through all the cells of each table and modifies their .Range.Text object using the command
C.Range.Text = myRE.Replace(C.Range.Text, "")
where myRE is a VBScript RegExp 5.5 object and myRE.Pattern = "\s+(?!.*\w)". The entire program can be found here.
The program works fine except for one problem. It removes all the comments from the cells as well. Before:
After (the extra space is gone, but so is the comment):
Looking at the local object tree, I can see that changing C.Range.Text also changes C.Range.Comments - but why?
What can I do to prevent this?
When you work with Range.Text, as is the case whenever RegEx or, indeed, any function that manipulates strings is used, all formatting and other non-text characters are lost when a pure string is written back to the cell.
For example, if a single character in your cell text were formatted as Bold, the bold formatting would be lost. Or if change tracking were in the cell - it would be lost. Any Footnote or Endnote would be lost. Comments fall into this same category.
You need a different approach, one that respects how Word stores non-text information in a document. Here's a suggestion that loops the cells in a table, picks up the Range at the end of the cell, then moves the starting point of the Range back as long as a pre-defined Whitespace character is found. When this criterium is no longer met, the Range is deleted. (Note I don't know why I needed to use Range.Delete twice - the first time had no effect.)
You need to work out what for you is "whitespace". I used a space, a carriage return and a tab character. You can certainly add others to the sWhitespace string.
Sub RemoveWhiteSpaceEndOfCell()
Dim cel As word.Cell
Dim sWhitespace As String
Dim rng As word.Range
Dim lWhiteSpaceChars As Long
'define what constitutes Whitespace.
'Here: a space, a carriage return and a tab
sWhitespace = Chr(32) & Chr(13) & Chr(9)
For Each cel In ActiveDocument.Tables(1).Range.Cells
Set rng = cel.Range
'set the Range to the end of the cell
rng.Collapse wdCollapseEnd
rng.MoveEnd wdCharacter, -1
'move the starting point back as long as whitespace is found
lWhiteSpaceChars = rng.MoveStartWhile(sWhitespace, wdBackward)
'Only if whitespace was found, delete the range
If lWhiteSpaceChars <> 0 Then
'rng.Select 'For debugging purposes
rng.Delete
rng.Delete
End If
Next
End Sub

Getting the previous Word in VBA using selection.previous wdword, 1 bug

I'm trying to write a macro to type the previous word at the cursor.
the problem is when i'm using "selection.previous wdword, 1" to get the previous character, it sometimes get the 2 previous characters and it seems like a bug. when i press "delete" button it works and it is very strange to me.
I'd glad if you help.
my ultimate goal is to create a calendar converter inside word using this code.
here is how i test it:
MsgBox Selection.previous(unit:=wdWord, Count:=1)
it is the same using next :
MsgBox Selection.Next(unit:=wdWord, Count:=1)
instead of next word, sometimes it returns the word after!
For example this is the text: during the flight on 21/3/1389
If the cursor is right after the 1389, msgbox selection.previous(1,1) would show "/"; if the cursor is after a space after 1389 it shows "1389". The problem is, I think, the space. My question is if there is any alternative to read the previous word instead of this command (Selection.previous(unit:=wdWord, Count:=1))
Word is not buggy - it's behaving as designed. Something has to tell Word where words start and end. When the cursor stands to the right of a space it's (quite logically) at the beginning of the next word. So going one word back is going to pick up 1389 instead of /.
You can work around this in your code. I'm sure there's more than one way to do it, but the following works for me in a quick test:
Sub GetPrevWord()
Dim rngSel As word.Range, rngPrev As word.Range
Set rngSel = Selection.Range
Set rngPrev = rngSel.Duplicate
rngPrev.MoveStart wdCharacter, -1
If Left(rngPrev.Text, 1) = " " Then
rngPrev.Collapse wdCollapseStart
End If
rngPrev.Select
MsgBox Selection.Previous(unit:=wdWord, Count:=1)
rngSel.Select
End Sub
What it's doing is using two Ranges: one to hold the original selection, the other to work with (rngPrev). rngPrev is extended backwards by one character and this character is evaluated. If it's a space then rngPrev is collapsed to its starting point. (Think of it like pressing the left arrow key of a selection.) In any case, rngPrev is selected and your MsgBox code is run. Finally, the original range is selected again.